Got through an entire game with this Python version.
[super-star-trek.git] / src / sst.py
1 #!/usr/bin/env python
2 """
3 sst.py =-- Super Star Trek in Python
4
5 This code is a Python translation of a C translation of a FORTRAN original.
6 The FORTRANness still shows in many ways, notably the use of a lot of
7 parallel arrays where a more modern language would use structures
8 or objects.  (However, 1-origin array indexing was fixed.)
9
10 Dave Matuszek says:
11
12 SRSCAN, MOVE, PHASERS, CALL, STATUS, IMPULSE, PHOTONS, ABANDON,
13 LRSCAN, WARP, SHIELDS, DESTRUCT, CHART, REST, DOCK, QUIT, and DAMAGE
14 were in the original non-"super" version of UT FORTRAN Star Trek.
15
16 Tholians were not in the original. Dave is dubious about their merits.
17 (They are now controlled by OPTION_THOLIAN and turned off if the game
18 type is "plain".)
19
20 Planets and dilithium crystals were not in the original.  Dave is OK
21 with this idea. (It's now controlled by OPTION_PLANETS and turned 
22 off if the game type is "plain".)
23
24 Dave says the bit about the Galileo getting turned into a
25 McDonald's is "consistant with our original vision".  (This has been
26 left permanently enabled, as it can only happen if OPTION_PLANETS
27 is on.)
28
29 Dave also says the Space Thingy should not be preserved across saved
30 games, so you can't prove to others that you've seen it.  He says it
31 shouldn't fire back, either.  It should do nothing except scream and
32 disappear when hit by photon torpedos.  It's OK that it may move
33 when attacked, but it didn't in the original.  (Whether the Thingy
34 can fire back is now controlled by OPTION_THINGY and turned off if the
35 game type is "plain" or "almy".  The no-save behavior has been restored.)
36
37 The Faerie Queen, black holes, and time warping were in the original.
38
39 Here are Tom Almy's changes:
40
41 In early 1997, I got the bright idea to look for references to
42 "Super Star Trek" on the World Wide Web. There weren't many hits,
43 but there was one that came up with 1979 Fortran sources! This
44 version had a few additional features that mine didn't have,
45 however mine had some feature it didn't have. So I merged its
46 features that I liked. I also took a peek at the DECUS version (a
47 port, less sources, to the PDP-10), and some other variations.
48
49 1, Compared to the original UT version, I've changed the "help" command to
50 "call" and the "terminate" command to "quit" to better match
51 user expectations. The DECUS version apparently made those changes
52 as well as changing "freeze" to "save". However I like "freeze".
53 (Both "freeze" and "save" work in SST2K.)
54
55 2. The experimental deathray originally had only a 5% chance of
56 success, but could be used repeatedly. I guess after a couple
57 years of use, it was less "experimental" because the 1979
58 version had a 70% success rate. However it was prone to breaking
59 after use. I upgraded the deathray, but kept the original set of
60 failure modes (great humor!).  (Now controlled by OPTION_DEATHRAY
61 and turned off if game type is "plain".)
62
63 3. The 1979 version also mentions srscan and lrscan working when
64 docked (using the starbase's scanners), so I made some changes here
65 to do this (and indicating that fact to the player), and then realized
66 the base would have a subspace radio as well -- doing a Chart when docked
67 updates the star chart, and all radio reports will be heard. The Dock
68 command will also give a report if a base is under attack.
69
70 4. Tholian Web from the 1979 version.  (Now controlled by
71 OPTION_THOLIAN and turned off if game type is "plain".)
72
73 5. Enemies can ram the Enterprise. (Now controlled by OPTION_RAMMING
74 and turned off if game type is "plain".)
75
76 6. Regular Klingons and Romulans can move in Expert and Emeritus games. 
77 This code could use improvement. (Now controlled by OPTION_MVBADDY
78 and turned off if game type is "plain".)
79
80 7. The deep-space probe feature from the DECUS version.  (Now controlled
81 by OPTION_PROBE and turned off if game type is "plain").
82
83 8. 'emexit' command from the 1979 version.
84
85 9. Bugfix: Klingon commander movements are no longer reported if long-range 
86 sensors are damaged.
87
88 10. Bugfix: Better base positioning at startup (more spread out).
89 That made sense to add because most people abort games with 
90 bad base placement.
91
92 In June 2002, I fixed two known bugs and a documentation typo.
93 In June 2004 I fixed a number of bugs involving: 1) parsing invalid
94 numbers, 2) manual phasers when SR scan is damaged and commander is
95 present, 3) time warping into the future, 4) hang when moving
96 klingons in crowded quadrants.  (These fixes are in SST2K.)
97
98 Here are Stas Sergeev's changes:
99
100 1. The Space Thingy can be shoved, if you ram it, and can fire back if 
101 fired upon. (Now controlled by OPTION_THINGY and turned off if game 
102 type is "plain" or "almy".)
103
104 2. When you are docked, base covers you with an almost invincible shield. 
105 (A commander can still ram you, or a Romulan can destroy the base,
106 or a SCom can even succeed with direct attack IIRC, but this rarely 
107 happens.)  (Now controlled by OPTION_BASE and turned off if game 
108 type is "plain" or "almy".)
109
110 3. Ramming a black hole is no longer instant death.  There is a
111 chance you might get timewarped instead. (Now controlled by 
112 OPTION_BLKHOLE and turned off if game type is "plain" or "almy".)
113
114 4. The Tholian can be hit with phasers.
115
116 5. SCom can't escape from you if no more enemies remain 
117 (without this, chasing SCom can take an eternity).
118
119 6. Probe target you enter is now the destination quadrant. Before I don't 
120 remember what it was, but it was something I had difficulty using.
121
122 7. Secret password is now autogenerated.
123
124 8. "Plaque" is adjusted for A4 paper :-)
125
126 9. Phasers now tells you how much energy needed, but only if the computer 
127 is alive.
128
129 10. Planets are auto-scanned when you enter the quadrant.
130
131 11. Mining or using crystals in presense of enemy now yields an attack.
132 There are other minor adjustments to what yields an attack
133 and what does not.
134
135 12. "freeze" command reverts to "save", most people will understand this
136 better anyway. (SST2K recognizes both.)
137
138 13. Screen-oriented interface, with sensor scans always up.  (SST2K
139 supports both screen-oriented and TTY modes.)
140
141 Eric Raymond's changes:
142
143 Mainly, I translated this C code out of FORTRAN into C -- created #defines
144 for a lot of magic numbers and refactored the heck out of it.
145
146 1. "sos" and "call" becomes "mayday", "freeze" and "save" are both good.
147
148 2. Status report now indicates when dilithium crystals are on board.
149
150 3. Per Dave Matuszek's remarks, Thingy state is never saved across games.
151
152 4. Added game option selection so you can play a close (but not bug-for-
153 bug identical) approximation of older versions.
154
155 5. Half the quadrants now have inhabited planets, from which one 
156 cannot mine dilithium (there will still be the same additional number
157 of dilithium-bearing planets).  Torpedoing an inhabited world is *bad*.
158 There is BSD-Trek-like logic for Klingons to attack and enslave 
159 inhabited worlds, producing more ships (only is skill is 'good' or 
160 better). (Controlled by OPTION_WORLDS and turned off if game 
161 type is "plain" or "almy".)
162
163 6. User input is now logged so we can do regression testing.
164
165 7. More BSD-Trek features: You can now lose if your entire crew
166 dies in battle.  When abandoning ship in a game with inhabited
167 worlds enabled, they must have one in the quadrant to beam down
168 to; otherwise they die in space and this counts heavily against
169 your score.  Docking at a starbase replenishes your crew.
170
171 8. Still more BSD-Trek: we now have a weighted damage table.
172 Also, the nav subsystem (enabling automatic course
173 setting) can be damaged separately from the main computer (which
174 handles weapons targeting, ETA calculation, and self-destruct).
175
176 After these features were added, I translated this into Python and added
177 more:
178
179 9. A long-range scan is done silently whenever you call CHART; thus
180 the LRSCAN command is no longer needed.  (Controlled by OPTION_AUTOSCAN
181 and turned off if game type is "plain" or "almy".)
182 """
183 import os,sys,math,curses,time,atexit,readline,cPickle,random,getopt,copy
184
185 SSTDOC          = "/usr/share/doc/sst/sst.doc"
186 DOC_NAME        = "sst.doc"
187
188 # Stub to be replaced
189 def _(str): return str
190
191 PHASEFAC        = 2.0
192 GALSIZE         = 8
193 NINHAB          = (GALSIZE * GALSIZE / 2)
194 MAXUNINHAB      = 10
195 PLNETMAX        = (NINHAB + MAXUNINHAB)
196 QUADSIZE        = 10
197 BASEMIN         = 2
198 BASEMAX         = (GALSIZE * GALSIZE / 12)
199 MAXKLGAME       = 127
200 MAXKLQUAD       = 9
201 FULLCREW        = 428   # BSD Trek was 387, that's wrong 
202 FOREVER         = 1e30
203
204 # These functions hide the difference between 0-origin and 1-origin addressing.
205 def VALID_QUADRANT(x, y):       return ((x)>=0 and (x)<GALSIZE and (y)>=0 and (y)<GALSIZE)
206 def VALID_SECTOR(x, y): return ((x)>=0 and (x)<QUADSIZE and (y)>=0 and (y)<QUADSIZE)
207
208 def square(i):          return ((i)*(i))
209 def distance(c1, c2):   return math.sqrt(square(c1.x - c2.x) + square(c1.y - c2.y))
210 def invalidate(w):      w.x = w.y = 0
211 def is_valid(w):        return (w.x != 0 and w.y != 0)
212
213 # How to represent features
214 IHR = 'R',
215 IHK = 'K',
216 IHC = 'C',
217 IHS = 'S',
218 IHSTAR = '*',
219 IHP = 'P',
220 IHW = '@',
221 IHB = 'B',
222 IHBLANK = ' ',
223 IHDOT = '.',
224 IHQUEST = '?',
225 IHE = 'E',
226 IHF = 'F',
227 IHT = 'T',
228 IHWEB = '#',
229 IHMATER0 = '-',
230 IHMATER1 = 'o',
231 IHMATER2 = '0'
232
233 IHEOL = '\n'
234 IHREAL = 0.0
235 IHALPHA = " "
236
237 class coord:
238     def __init__(self, x=None, y=None):
239         self.x = x
240         self.y = y
241     def invalidate(self):
242         self.x = self.y = None
243     def is_valid(self):
244         return self.x != None and self.y != None
245     def __eq__(self, other):
246         return other != None and self.x == other.y and self.x == other.y
247     def __add__(self, other):
248         return coord(self.x+self.x, self.y+self.y)
249     def __sub__(self, other):
250         return coord(self.x-self.x, self.y-self.y)
251     def distance(self, other):
252         return math.sqrt((self.x - other.x)**2 + (self.y - other.y)**2)
253     def sgn(self):
254         s = coord()
255         if self.x == 0:
256             s.x = 0
257         else:
258             s.x = self.x / abs(self.x)
259         if self.y == 0:
260             s.y = 0
261         else:
262             s.y = self.y / abs(self.y)
263         return s
264     def scatter(self):
265         s = coord()
266         s.x = self.x + randrange(-1, 2)
267         s.y = self.y + randrange(-1, 2)
268         return s
269     def __hash__(self):
270         return hash((x, y))
271     def __str__(self):
272         if self.x == None or self.y == None:
273             return "Nowhere"
274         return "%s - %s" % (self.x+1, self.y+1)
275     __repr__ = __str__
276
277 class planet:
278     def __init__(self):
279         self.name = None        # string-valued if inhabited
280         self.w = coord()        # quadrant located
281         self.pclass = None      # could be ""M", "N", "O", or "destroyed"
282         self.crystals = "absent"# could be "mined", "present", "absent"
283         self.known = "unknown"  # could be "unknown", "known", "shuttle_down"
284         self.inhabited = False  # is it inhabites?
285     def __str__(self):
286         return self.name
287
288 class quadrant:
289     def __init__(self):
290         self.stars = None
291         self.planet = None
292         self.starbase = None
293         self.klingons = None
294         self.romulans = None
295         self.supernova = None
296         self.charted = None
297         self.status = None      # Could be "secure", "distressed", "enslaved"
298
299 class page:
300     def __init__(self):
301         self.stars = None
302         self.starbase = None
303         self.klingons = None
304
305 def fill2d(size, fillfun):
306     "Fill an empty list in 2D."
307     lst = []
308     for i in range(size):
309         lst.append([]) 
310         for j in range(size):
311             lst[i].append(fillfun(i, j))
312     return lst
313
314 class snapshot:
315     def __init__(self):
316         self.snap = False       # snapshot taken
317         self.crew = 0           # crew complement
318         self.remkl = 0          # remaining klingons
319         self.remcom = 0         # remaining commanders
320         self.nscrem = 0         # remaining super commanders
321         self.rembase = 0        # remaining bases
322         self.starkl = 0         # destroyed stars
323         self.basekl = 0         # destroyed bases
324         self.nromrem = 0        # Romulans remaining
325         self.nplankl = 0        # destroyed uninhabited planets
326         self.nworldkl = 0       # destroyed inhabited planets
327         self.planets = []       # Planet information
328         self.date = 0.0         # stardate
329         self.remres = 0         # remaining resources
330         self.remtime = 0        # remaining time
331         self.baseq = []         # Base quadrant coordinates
332         for i in range(BASEMAX):
333             self.baseq.append(coord())
334         self.kcmdr = []         # Commander quadrant coordinates
335         for i in range(QUADSIZE):
336             self.kcmdr.append(coord())
337         self.kscmdr = coord()   # Supercommander quadrant coordinates
338         # the galaxy (subscript 0 not used)
339         self.galaxy = fill2d(GALSIZE, lambda i, j: quadrant())
340         # the starchart (subscript 0 not used)
341         self.chart = fill2d(GALSIZE, lambda i, j: page())
342
343 class event:
344     def __init__(self):
345         self.date = None        # A real number
346         self.quadrant = None    # A coord structure
347
348 # game options 
349 OPTION_ALL      = 0xffffffff
350 OPTION_TTY      = 0x00000001    # old interface 
351 OPTION_CURSES   = 0x00000002    # new interface 
352 OPTION_IOMODES  = 0x00000003    # cover both interfaces 
353 OPTION_PLANETS  = 0x00000004    # planets and mining 
354 OPTION_THOLIAN  = 0x00000008    # Tholians and their webs (UT 1979 version)
355 OPTION_THINGY   = 0x00000010    # Space Thingy can shoot back (Stas, 2005)
356 OPTION_PROBE    = 0x00000020    # deep-space probes (DECUS version, 1980)
357 OPTION_SHOWME   = 0x00000040    # bracket Enterprise in chart 
358 OPTION_RAMMING  = 0x00000080    # enemies may ram Enterprise (Almy)
359 OPTION_MVBADDY  = 0x00000100    # more enemies can move (Almy)
360 OPTION_BLKHOLE  = 0x00000200    # black hole may timewarp you (Stas, 2005) 
361 OPTION_BASE     = 0x00000400    # bases have good shields (Stas, 2005)
362 OPTION_WORLDS   = 0x00000800    # logic for inhabited worlds (ESR, 2006)
363 OPTION_AUTOSCAN = 0x00001000    # automatic LRSCAN before CHART (ESR, 2006)
364 OPTION_PLAIN    = 0x01000000    # user chose plain game 
365 OPTION_ALMY     = 0x02000000    # user chose Almy variant 
366
367 # Define devices 
368 DSRSENS = 0
369 DLRSENS = 1
370 DPHASER = 2
371 DPHOTON = 3
372 DLIFSUP = 4
373 DWARPEN = 5
374 DIMPULS = 6
375 DSHIELD = 7
376 DRADIO  = 0
377 DSHUTTL = 9
378 DCOMPTR = 10
379 DNAVSYS = 11
380 DTRANSP = 12
381 DSHCTRL = 13
382 DDRAY   = 14
383 DDSP    = 15
384 NDEVICES= 16    # Number of devices
385
386 SKILL_NONE      = 0
387 SKILL_NOVICE    = 1
388 SKILL_FAIR      = 2
389 SKILL_GOOD      = 3
390 SKILL_EXPERT    = 4
391 SKILL_EMERITUS  = 5
392
393 def damaged(dev):       return (game.damage[dev] != 0.0)
394 def communicating():    return not damaged(DRADIO) or game.condition=="docked"
395
396 # Define future events 
397 FSPY    = 0     # Spy event happens always (no future[] entry)
398                 # can cause SC to tractor beam Enterprise
399 FSNOVA  = 1     # Supernova
400 FTBEAM  = 2     # Commander tractor beams Enterprise
401 FSNAP   = 3     # Snapshot for time warp
402 FBATTAK = 4     # Commander attacks base
403 FCDBAS  = 5     # Commander destroys base
404 FSCMOVE = 6     # Supercommander moves (might attack base)
405 FSCDBAS = 7     # Supercommander destroys base
406 FDSPROB = 8     # Move deep space probe
407 FDISTR  = 9     # Emit distress call from an inhabited world 
408 FENSLV  = 10    # Inhabited word is enslaved */
409 FREPRO  = 11    # Klingons build a ship in an enslaved system
410 NEVENTS = 12
411
412 #
413 # abstract out the event handling -- underlying data structures will change
414 # when we implement stateful events
415
416 def findevent(evtype):  return game.future[evtype]
417
418 class enemy:
419     def __init__(self, type=None, loc=None, power=None):
420         self.type = type
421         self.kloc = coord()
422         if loc:
423             self.move(loc)
424         self.kpower = power     # enemy energy level
425         game.enemies.append(self)
426     def move(self, loc):
427         if self.kloc.x is not None and self.kloc.y is not None:
428             game.quad[self.kloc.x][self.kloc.y] = IHDOT
429         if loc:
430             self.kloc = loc
431             game.quad[self.kloc.x][self.kloc.y] = self.type
432             self.kdist = self.kavgd = distance(game.sector, loc)
433         else:
434             self.kloc = coord() # enemy sector location
435             self.kdist = self.kavgd = None
436             game.enemies.remove(self)
437     def __repr__(self):
438         return "<%s=%f>" % (self.kloc, self.kpower)     # For debugging
439
440 class gamestate:
441     def __init__(self):
442         self.options = None     # Game options
443         self.state = snapshot() # A snapshot structure
444         self.snapsht = snapshot()       # Last snapshot taken for time-travel purposes
445         self.quad = fill2d(QUADSIZE, lambda i, j: IHDOT)        # contents of our quadrant
446         self.damage = [0.0] * NDEVICES  # damage encountered
447         self.future = []                # future events
448         for i in range(NEVENTS):
449             self.future.append(event())
450         self.passwd  = None;            # Self Destruct password
451         self.enemies = []
452         self.quadrant = None    # where we are in the large
453         self.sector = None      # where we are in the small
454         self.tholian = None     # Tholian enemy object
455         self.base = None        # position of base in current quadrant
456         self.battle = None      # base coordinates being attacked
457         self.plnet = None       # location of planet in quadrant
458         self.probec = None      # current probe quadrant
459         self.gamewon = False    # Finished!
460         self.ididit = False     # action taken -- allows enemy to attack
461         self.alive = False      # we are alive (not killed)
462         self.justin = False     # just entered quadrant
463         self.shldup = False     # shields are up
464         self.shldchg = False    # shield is changing (affects efficiency)
465         self.comhere = False    # commander here
466         self.ishere = False     # super-commander in quadrant
467         self.iscate = False     # super commander is here
468         self.ientesc = False    # attempted escape from supercommander
469         self.resting = False    # rest time
470         self.icraft = False     # Kirk in Galileo
471         self.landed = False     # party on planet (true), on ship (false)
472         self.alldone = False    # game is now finished
473         self.neutz = False      # Romulan Neutral Zone
474         self.isarmed = False    # probe is armed
475         self.inorbit = False    # orbiting a planet
476         self.imine = False      # mining
477         self.icrystl = False    # dilithium crystals aboard
478         self.iseenit = False    # seen base attack report
479         self.thawed = False     # thawed game
480         self.condition = None   # "green", "yellow", "red", "docked", "dead"
481         self.iscraft = None     # "onship", "offship", "removed"
482         self.skill = None       # Player skill level
483         self.inkling = 0        # initial number of klingons
484         self.inbase = 0         # initial number of bases
485         self.incom = 0          # initial number of commanders
486         self.inscom = 0         # initial number of commanders
487         self.inrom = 0          # initial number of commanders
488         self.instar = 0         # initial stars
489         self.intorps = 0        # initial/max torpedoes
490         self.torps = 0          # number of torpedoes
491         self.ship = 0           # ship type -- 'E' is Enterprise
492         self.abandoned = 0      # count of crew abandoned in space
493         self.length = 0         # length of game
494         self.klhere = 0         # klingons here
495         self.casual = 0         # causalties
496         self.nhelp = 0          # calls for help
497         self.nkinks = 0         # count of energy-barrier crossings
498         self.iplnet = None      # planet # in quadrant
499         self.inplan = 0         # initial planets
500         self.irhere = 0         # Romulans in quadrant
501         self.isatb = 0          # =1 if super commander is attacking base
502         self.tourn = None       # tournament number
503         self.proben = 0         # number of moves for probe
504         self.nprobes = 0        # number of probes available
505         self.inresor = 0.0      # initial resources
506         self.intime = 0.0       # initial time
507         self.inenrg = 0.0       # initial/max energy
508         self.inshld = 0.0       # initial/max shield
509         self.inlsr = 0.0        # initial life support resources
510         self.indate = 0.0       # initial date
511         self.energy = 0.0       # energy level
512         self.shield = 0.0       # shield level
513         self.warpfac = 0.0      # warp speed
514         self.wfacsq = 0.0       # squared warp factor
515         self.lsupres = 0.0      # life support reserves
516         self.dist = 0.0         # movement distance
517         self.direc = 0.0        # movement direction
518         self.optime = 0.0       # time taken by current operation
519         self.docfac = 0.0       # repair factor when docking (constant?)
520         self.damfac = 0.0       # damage factor
521         self.lastchart = 0.0    # time star chart was last updated
522         self.cryprob = 0.0      # probability that crystal will work
523         self.probex = 0.0       # location of probe
524         self.probey = 0.0       #
525         self.probeinx = 0.0     # probe x,y increment
526         self.probeiny = 0.0     #
527         self.height = 0.0       # height of orbit around planet
528     def recompute(self):
529         # Stas thinks this should be (C expression): 
530         # game.state.remkl + game.state.remcom > 0 ?
531         #       game.state.remres/(game.state.remkl + 4*game.state.remcom) : 99
532         # He says the existing expression is prone to divide-by-zero errors
533         # after killing the last klingon when score is shown -- perhaps also
534         # if the only remaining klingon is SCOM.
535         game.state.remtime = game.state.remres/(game.state.remkl + 4*game.state.remcom)
536 # From enumerated type 'feature'
537 IHR = 'R'
538 IHK = 'K'
539 IHC = 'C'
540 IHS = 'S'
541 IHSTAR = '*'
542 IHP = 'P'
543 IHW = '@'
544 IHB = 'B'
545 IHBLANK = ' '
546 IHDOT = '.'
547 IHQUEST = '?'
548 IHE = 'E'
549 IHF = 'F'
550 IHT = 'T'
551 IHWEB = '#'
552 IHMATER0 = '-'
553 IHMATER1 = 'o'
554 IHMATER2 = '0'
555
556
557 # From enumerated type 'FINTYPE'
558 FWON = 0
559 FDEPLETE = 1
560 FLIFESUP = 2
561 FNRG = 3
562 FBATTLE = 4
563 FNEG3 = 5
564 FNOVA = 6
565 FSNOVAED = 7
566 FABANDN = 8
567 FDILITHIUM = 9
568 FMATERIALIZE = 10
569 FPHASER = 11
570 FLOST = 12
571 FMINING = 13
572 FDPLANET = 14
573 FPNOVA = 15
574 FSSC = 16
575 FSTRACTOR = 17
576 FDRAY = 18
577 FTRIBBLE = 19
578 FHOLE = 20
579 FCREW = 21
580
581 # From enumerated type 'COLORS'
582 DEFAULT = 0
583 BLACK = 1
584 BLUE = 2
585 GREEN = 3
586 CYAN = 4
587 RED = 5
588 MAGENTA = 6
589 BROWN = 7
590 LIGHTGRAY = 8
591 DARKGRAY = 9
592 LIGHTBLUE = 10
593 LIGHTGREEN = 11
594 LIGHTCYAN = 12
595 LIGHTRED = 13
596 LIGHTMAGENTA = 14
597 YELLOW = 15
598 WHITE = 16
599
600 # Log the results of pulling random numbers so we can check determinism.
601
602 import traceback
603
604 def withprob(p):
605     v = random.random()
606     #logfp.write("# withprob(%s) -> %f (%s) at %s\n" % (p, v, v<p, traceback.extract_stack()[-2][1:]))
607     return v < p
608
609 def randrange(*args):
610     v = random.randrange(*args)
611     #logfp.write("# randrange%s -> %s at %s\n" % (args, v, traceback.extract_stack()[-2][1:]))
612     return v
613
614 def randreal(*args):
615     v = random.random()
616     if len(args) == 1:
617         v *= args[0]            # returns from [0, a1)
618     elif len(args) == 2:
619         v = args[0] + v*args[1] # returns from [a1, a2)
620     #logfp.write("# randreal%s -> %s at %s\n" % (args, v, traceback.extract_stack()[-2][1:]))
621     return v
622
623 # Code from ai.c begins here
624
625 def tryexit(look, ienm, loccom, irun):
626     # a bad guy attempts to bug out 
627     iq = coord()
628     iq.x = game.quadrant.x+(look.x+(QUADSIZE-1))/QUADSIZE - 1
629     iq.y = game.quadrant.y+(look.y+(QUADSIZE-1))/QUADSIZE - 1
630     if not VALID_QUADRANT(iq.x,iq.y) or \
631         game.state.galaxy[iq.x][iq.y].supernova or \
632         game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1:
633         return False; # no can do -- neg energy, supernovae, or >MAXKLQUAD-1 Klingons 
634     if ienm == IHR:
635         return False; # Romulans cannot escape! 
636     if not irun:
637         # avoid intruding on another commander's territory 
638         if ienm == IHC:
639             for n in range(game.state.remcom):
640                 if game.state.kcmdr[n] == iq:
641                     return False
642             # refuse to leave if currently attacking starbase 
643             if game.battle == game.quadrant:
644                 return False
645         # don't leave if over 1000 units of energy 
646         if game.enemies[loccom].kpower > 1000.0:
647             return False
648     # print escape message and move out of quadrant.
649     # we know this if either short or long range sensors are working
650     if not damaged(DSRSENS) or not damaged(DLRSENS) or \
651         game.condition == docked:
652         crmena(True, ienm, "sector", game.enemies[loccom].kloc)
653         prout(_(" escapes to Quadrant %s (and regains strength).") % q)
654     # handle local matters related to escape
655     game.enemies[loccom].move(None)
656     game.klhere -= 1
657     if game.condition != docked:
658         newcnd()
659     # Handle global matters related to escape 
660     game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons -= 1
661     game.state.galaxy[iq.x][iq.y].klingons += 1
662     if ienm==IHS:
663         game.ishere = False
664         game.iscate = False
665         game.ientesc = False
666         game.isatb = 0
667         schedule(FSCMOVE, 0.2777)
668         unschedule(FSCDBAS)
669         game.state.kscmdr=iq
670     else:
671         for n in range(game.state.remcom):
672             if game.state.kcmdr[n] == game.quadrant:
673                 game.state.kcmdr[n]=iq
674                 break
675         game.comhere = False
676     return True; # success 
677
678 #
679 # The bad-guy movement algorithm:
680
681 # 1. Enterprise has "force" based on condition of phaser and photon torpedoes.
682 # If both are operating full strength, force is 1000. If both are damaged,
683 # force is -1000. Having shields down subtracts an additional 1000.
684
685 # 2. Enemy has forces equal to the energy of the attacker plus
686 # 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
687 # 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
688
689 # Attacker Initial energy levels (nominal):
690 # Klingon   Romulan   Commander   Super-Commander
691 # Novice    400        700        1200        
692 # Fair      425        750        1250
693 # Good      450        800        1300        1750
694 # Expert    475        850        1350        1875
695 # Emeritus  500        900        1400        2000
696 # VARIANCE   75        200         200         200
697
698 # Enemy vessels only move prior to their attack. In Novice - Good games
699 # only commanders move. In Expert games, all enemy vessels move if there
700 # is a commander present. In Emeritus games all enemy vessels move.
701
702 # 3. If Enterprise is not docked, an aggressive action is taken if enemy
703 # forces are 1000 greater than Enterprise.
704
705 # Agressive action on average cuts the distance between the ship and
706 # the enemy to 1/4 the original.
707
708 # 4.  At lower energy advantage, movement units are proportional to the
709 # advantage with a 650 advantage being to hold ground, 800 to move forward
710 # 1, 950 for two, 150 for back 4, etc. Variance of 100.
711
712 # If docked, is reduced by roughly 1.75*game.skill, generally forcing a
713 # retreat, especially at high skill levels.
714
715 # 5.  Motion is limited to skill level, except for SC hi-tailing it out.
716
717
718 def movebaddy(com, loccom, ienm):
719     # tactical movement for the bad guys 
720     next = coord(); look = coord()
721     irun = False
722     # This should probably be just game.comhere + game.ishere 
723     if game.skill >= SKILL_EXPERT:
724         nbaddys = ((game.comhere*2 + game.ishere*2+game.klhere*1.23+game.irhere*1.5)/2.0)
725     else:
726         nbaddys = game.comhere + game.ishere
727
728     dist1 = game.enemies[loccom].kdist
729     mdist = int(dist1 + 0.5); # Nearest integer distance 
730     # If SC, check with spy to see if should hi-tail it 
731     if ienm==IHS and \
732         (game.enemies[loccom].kpower <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
733         irun = True
734         motion = -QUADSIZE
735     else:
736         # decide whether to advance, retreat, or hold position 
737         forces = game.enemies[loccom].kpower+100.0*len(game.enemies)+400*(nbaddys-1)
738         if not game.shldup:
739             forces += 1000; # Good for enemy if shield is down! 
740         if not damaged(DPHASER) or not damaged(DPHOTON):
741             if damaged(DPHASER): # phasers damaged 
742                 forces += 300.0
743             else:
744                 forces -= 0.2*(game.energy - 2500.0)
745             if damaged(DPHOTON): # photon torpedoes damaged 
746                 forces += 300.0
747             else:
748                 forces -= 50.0*game.torps
749         else:
750             # phasers and photon tubes both out! 
751             forces += 1000.0
752         motion = 0
753         if forces <= 1000.0 and game.condition != "docked": # Typical situation 
754             motion = ((forces + randreal(200))/150.0) - 5.0
755         else:
756             if forces > 1000.0: # Very strong -- move in for kill 
757                 motion = (1.0-square(randreal()))*dist1 + 1.0
758             if game.condition=="docked" and (game.options & OPTION_BASE): # protected by base -- back off ! 
759                 motion -= game.skill*(2.0-square(randreal()))
760         if idebug:
761             proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
762         # don't move if no motion 
763         if motion==0:
764             return
765         # Limit motion according to skill 
766         if abs(motion) > game.skill:
767             if motion < 0:
768                 motion = -game.skill
769             else:
770                 motion = game.skill
771     # calculate preferred number of steps 
772     if motion < 0:
773         nsteps = -motion
774     else:
775         nsteps = motion
776     if motion > 0 and nsteps > mdist:
777         nsteps = mdist; # don't overshoot 
778     if nsteps > QUADSIZE:
779         nsteps = QUADSIZE; # This shouldn't be necessary 
780     if nsteps < 1:
781         nsteps = 1; # This shouldn't be necessary 
782     if idebug:
783         proutn("NSTEPS = %d:" % nsteps)
784     # Compute preferred values of delta X and Y 
785     mx = game.sector.x - com.x
786     my = game.sector.y - com.y
787     if 2.0 * abs(mx) < abs(my):
788         mx = 0
789     if 2.0 * abs(my) < abs(game.sector.x-com.x):
790         my = 0
791     if mx != 0:
792         if mx*motion < 0:
793             mx = -1
794         else:
795             mx = 1
796     if my != 0:
797         if my*motion < 0:
798             my = -1
799         else:
800             my = 1
801     next = com
802     # main move loop 
803     for ll in range(nsteps):
804         if idebug:
805             proutn(" %d" % (ll+1))
806         # Check if preferred position available 
807         look.x = next.x + mx
808         look.y = next.y + my
809         if mx < 0:
810             krawlx = 1
811         else:
812             krawlx = -1
813         if my < 0:
814             krawly = 1
815         else:
816             krawly = -1
817         success = False
818         attempts = 0; # Settle mysterious hang problem 
819         while attempts < 20 and not success:
820             attempts += 1
821             if look.x < 0 or look.x >= QUADSIZE:
822                 if motion < 0 and tryexit(look, ienm, loccom, irun):
823                     return
824                 if krawlx == mx or my == 0:
825                     break
826                 look.x = next.x + krawlx
827                 krawlx = -krawlx
828             elif look.y < 0 or look.y >= QUADSIZE:
829                 if motion < 0 and tryexit(look, ienm, loccom, irun):
830                     return
831                 if krawly == my or mx == 0:
832                     break
833                 look.y = next.y + krawly
834                 krawly = -krawly
835             elif (game.options & OPTION_RAMMING) and game.quad[look.x][look.y] != IHDOT:
836                 # See if we should ram ship 
837                 if game.quad[look.x][look.y] == game.ship and \
838                     (ienm == IHC or ienm == IHS):
839                     ram(True, ienm, com)
840                     return
841                 if krawlx != mx and my != 0:
842                     look.x = next.x + krawlx
843                     krawlx = -krawlx
844                 elif krawly != my and mx != 0:
845                     look.y = next.y + krawly
846                     krawly = -krawly
847                 else:
848                     break; # we have failed 
849             else:
850                 success = True
851         if success:
852             next = look
853             if idebug:
854                 proutn(`next`)
855         else:
856             break; # done early 
857         
858     if idebug:
859         skip(1)
860     # Put commander in place within same quadrant 
861     game.quad[com.x][com.y] = IHDOT
862     game.quad[next.x][next.y] = ienm
863     if next != com:
864         # it moved 
865         game.enemies[loccom].kloc = next
866         game.enemies[loccom].kdist = game.enemies[loccom].kavgd = distance(game.sector, next)
867         if not damaged(DSRSENS) or game.condition == docked:
868             proutn("***")
869             cramen(ienm)
870             proutn(_(" from Sector %s") % com)
871             if game.enemies[loccom].kdist < dist1:
872                 proutn(_(" advances to "))
873             else:
874                 proutn(_(" retreats to "))
875             prout("Sector %s." % next)
876
877 def moveklings():
878     # Klingon tactical movement 
879     if idebug:
880         prout("== MOVCOM")
881     # Figure out which Klingon is the commander (or Supercommander)
882     # and do move
883     if game.comhere:
884         for (i, e) in enumerate(game.enemies):
885             if game.quad[e.kloc.x][e.kloc.y] == IHC:
886                 movebaddy(e.kloc, i, IHC)
887                 break
888     if game.ishere:
889         for (i, e) in enumerate(game.enemies):
890             if game.quad[e.kloc.x][e.kloc.y] == IHS:
891                 movebaddy(e.kloc, i, IHS)
892                 break
893     # If skill level is high, move other Klingons and Romulans too!
894     # Move these last so they can base their actions on what the
895     # commander(s) do.
896     if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
897         for (i, e) in enumerate(game.enemies):
898             if game.quad[e.kloc.x][e.kloc.y] in (IHK, IHR):
899                 movebaddy(e.kloc, i, game.quad[e.kloc.x][e.kloc.y])
900     game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
901
902 def movescom(iq, avoid):
903     # commander movement helper 
904     if iq == game.quadrant or not VALID_QUADRANT(iq.x, iq.y) or \
905         game.state.galaxy[iq.x][iq.y].supernova or \
906         game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1:
907         return 1
908     if avoid:
909         # Avoid quadrants with bases if we want to avoid Enterprise 
910         for i in range(game.state.rembase):
911             if game.state.baseq[i] == iq:
912                 return True
913     if game.justin and not game.iscate:
914         return True
915     # do the move 
916     game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons -= 1
917     game.state.kscmdr = iq
918     game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons += 1
919     if game.ishere:
920         # SC has scooted, Remove him from current quadrant 
921         game.iscate=False
922         game.isatb=0
923         game.ishere = False
924         game.ientesc = False
925         unschedule(FSCDBAS)
926         for e in game.enemies:
927             if game.quad[e.kloc.x][e.kloc.y] == IHS:
928                 break
929         game.enemies[i].move(None)
930         game.klhere -= 1
931         if game.condition!=docked:
932             newcnd()
933         game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
934     # check for a helpful planet 
935     for i in range(game.inplan):
936         if game.state.planets[i].w == game.state.kscmdr and \
937             game.state.planets[i].crystals == "present":
938             # destroy the planet 
939             game.state.planets[i].pclass = "destroyed"
940             game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = None
941             if communicating():
942                 announce()
943                 prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"))
944                 proutn(_("   a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
945                 prout(_("   by the Super-commander.\""))
946             break
947     return False; # looks good! 
948                         
949 def supercommander():
950     # move the Super Commander 
951     iq = coord(); sc = coord(); ibq = coord(); idelta = coord()
952     basetbl = []
953     if idebug:
954         prout("== SUPERCOMMANDER")
955     # Decide on being active or passive 
956     avoid = ((game.incom - game.state.remcom + game.inkling - game.state.remkl)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) or \
957             (game.state.date-game.indate) < 3.0)
958     if not game.iscate and avoid:
959         # compute move away from Enterprise 
960         idelta = game.state.kscmdr-game.quadrant
961         if math.sqrt(idelta.x*idelta.x+idelta.y*idelta.y) > 2.0:
962             # circulate in space 
963             idelta.x = game.state.kscmdr.y-game.quadrant.y
964             idelta.y = game.quadrant.x-game.state.kscmdr.x
965     else:
966         # compute distances to starbases 
967         if game.state.rembase <= 0:
968             # nothing left to do 
969             unschedule(FSCMOVE)
970             return
971         sc = game.state.kscmdr
972         for i in range(game.state.rembase):
973             basetbl.append((i, distance(game.state.baseq[i], sc)))
974         if game.state.rembase > 1:
975             basetbl.sort(lambda x, y: cmp(x[1]. y[1]))
976         # look for nearest base without a commander, no Enterprise, and
977         # without too many Klingons, and not already under attack. 
978         ifindit = iwhichb = 0
979         for i2 in range(game.state.rembase):
980             i = basetbl[i2][0]; # bug in original had it not finding nearest
981             ibq = game.state.baseq[i]
982             if ibq == game.quadrant or ibq == game.battle or \
983                 game.state.galaxy[ibq.x][ibq.y].supernova or \
984                 game.state.galaxy[ibq.x][ibq.y].klingons > MAXKLQUAD-1:
985                 continue
986             # if there is a commander, and no other base is appropriate,
987             #   we will take the one with the commander
988             for j in range(game.state.remcom):
989                 if ibq == game.state.kcmdr[j] and ifindit!= 2:
990                     ifindit = 2
991                     iwhichb = i
992                     break
993             if j > game.state.remcom: # no commander -- use this one 
994                 ifindit = 1
995                 iwhichb = i
996                 break
997         if ifindit==0:
998             return # Nothing suitable -- wait until next time
999         ibq = game.state.baseq[iwhichb]
1000         # decide how to move toward base 
1001         idelta = ibq - game.state.kscmdr
1002     # Maximum movement is 1 quadrant in either or both axes 
1003     idelta = idelta.sgn()
1004     # try moving in both x and y directions
1005     # there was what looked like a bug in the Almy C code here,
1006     # but it might be this translation is just wrong.
1007     iq = game.state.kscmdr + idelta
1008     if movescom(iq, avoid):
1009         # failed -- try some other maneuvers 
1010         if idelta.x==0 or idelta.y==0:
1011             # attempt angle move 
1012             if idelta.x != 0:
1013                 iq.y = game.state.kscmdr.y + 1
1014                 if movescom(iq, avoid):
1015                     iq.y = game.state.kscmdr.y - 1
1016                     movescom(iq, avoid)
1017             else:
1018                 iq.x = game.state.kscmdr.x + 1
1019                 if movescom(iq, avoid):
1020                     iq.x = game.state.kscmdr.x - 1
1021                     movescom(iq, avoid)
1022         else:
1023             # try moving just in x or y 
1024             iq.y = game.state.kscmdr.y
1025             if movescom(iq, avoid):
1026                 iq.y = game.state.kscmdr.y + idelta.y
1027                 iq.x = game.state.kscmdr.x
1028                 movescom(iq, avoid)
1029     # check for a base 
1030     if game.state.rembase == 0:
1031         unschedule(FSCMOVE)
1032     else:
1033         for i in range(game.state.rembase):
1034             ibq = game.state.baseq[i]
1035             if ibq == game.state.kscmdr and game.state.kscmdr == game.battle:
1036                 # attack the base 
1037                 if avoid:
1038                     return # no, don't attack base! 
1039                 game.iseenit = False
1040                 game.isatb = 1
1041                 schedule(FSCDBAS, randreal(1.0, 3.0))
1042                 if is_scheduled(FCDBAS):
1043                     postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date)
1044                 if not communicating():
1045                     return # no warning 
1046                 game.iseenit = True
1047                 announce()
1048                 prout(_("Lt. Uhura-  \"Captain, the starbase in Quadrant %s") \
1049                       % game.state.kscmdr)
1050                 prout(_("   reports that it is under attack from the Klingon Super-commander."))
1051                 proutn(_("   It can survive until stardate %d.\"") \
1052                        % int(scheduled(FSCDBAS)))
1053                 if not game.resting:
1054                     return
1055                 prout(_("Mr. Spock-  \"Captain, shall we cancel the rest period?\""))
1056                 if ja() == False:
1057                     return
1058                 game.resting = False
1059                 game.optime = 0.0; # actually finished 
1060                 return
1061     # Check for intelligence report 
1062     if not idebug and \
1063         (withprob(0.8) or \
1064          (not communicating()) or \
1065          not game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted):
1066         return
1067     announce()
1068     prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"))
1069     proutn(_("   the Super-commander is in Quadrant %s,") % game.state.kscmdr)
1070     return
1071
1072 def movetholian():
1073     # move the Tholian 
1074     if not game.tholian or game.justin:
1075         return
1076     if game.tholian.kloc.x == 0 and game.tholian.kloc.y == 0:
1077         idx = 0; idy = QUADSIZE-1
1078     elif game.tholian.kloc.x == 0 and game.tholian.kloc.y == QUADSIZE-1:
1079         idx = QUADSIZE-1; idy = QUADSIZE-1
1080     elif game.tholian.kloc.x == QUADSIZE-1 and game.tholian.kloc.y == QUADSIZE-1:
1081         idx = QUADSIZE-1; idy = 0
1082     elif game.tholian.kloc.x == QUADSIZE-1 and game.tholian.kloc.y == 0:
1083         idx = 0; idy = 0
1084     else:
1085         # something is wrong! 
1086         game.tholian = None
1087         return
1088     # do nothing if we are blocked 
1089     if game.quad[idx][idy]!= IHDOT and game.quad[idx][idy]!= IHWEB:
1090         return
1091     game.quad[game.tholian.kloc.x][game.tholian.kloc.y] = IHWEB
1092     if game.tholian.kloc.x != idx:
1093         # move in x axis 
1094         im = math.fabs(idx - game.tholian.kloc.x)*1.0/(idx - game.tholian.kloc.x)
1095         while game.tholian.kloc.x != idx:
1096             game.tholian.kloc.x += im
1097             if game.quad[game.tholian.kloc.x][game.tholian.kloc.y]==IHDOT:
1098                 game.quad[game.tholian.kloc.x][game.tholian.kloc.y] = IHWEB
1099     elif game.tholian.kloc.y != idy:
1100         # move in y axis 
1101         im = math.fabs(idy - game.tholian.kloc.y)*1.0/(idy - game.tholian.kloc.y)
1102         while game.tholian.kloc.y != idy:
1103             game.tholian.kloc.y += im
1104             if game.quad[game.tholian.kloc.x][game.tholian.kloc.y]==IHDOT:
1105                 game.quad[game.tholian.kloc.x][game.tholian.kloc.y] = IHWEB
1106     game.quad[game.tholian.kloc.x][game.tholian.kloc.y] = IHT
1107     #game.enemies[-1].kloc = game.tholian       #FIXME
1108     # check to see if all holes plugged 
1109     for i in range(QUADSIZE):
1110         if game.quad[0][i]!=IHWEB and game.quad[0][i]!=IHT:
1111             return
1112         if game.quad[QUADSIZE][i]!=IHWEB and game.quad[QUADSIZE][i]!=IHT:
1113             return
1114         if game.quad[i][0]!=IHWEB and game.quad[i][0]!=IHT:
1115             return
1116         if game.quad[i][QUADSIZE]!=IHWEB and game.quad[i][QUADSIZE]!=IHT:
1117             return
1118     # All plugged up -- Tholian splits 
1119     game.quad[game.tholian.kloc.x][game.tholian.kloc.y]=IHWEB
1120     dropin(IHBLANK)
1121     crmena(True, IHT, "sector", game.tholian)
1122     prout(_(" completes web."))
1123     game.tholian.move(None)
1124     return
1125
1126 # Code from battle.c begins here
1127
1128 def doshield(shraise):
1129     # change shield status 
1130     action = "NONE"
1131     game.ididit = False
1132     if shraise:
1133         action = "SHUP"
1134     else:
1135         key = scan()
1136         if key == IHALPHA:
1137             if isit("transfer"):
1138                 action = "NRG"
1139             else:
1140                 chew()
1141                 if damaged(DSHIELD):
1142                     prout(_("Shields damaged and down."))
1143                     return
1144                 if isit("up"):
1145                     action = "SHUP"
1146                 elif isit("down"):
1147                     action = "SHDN"
1148         if action=="NONE":
1149             proutn(_("Do you wish to change shield energy? "))
1150             if ja() == True:
1151                 proutn(_("Energy to transfer to shields- "))
1152                 action = "NRG"
1153             elif damaged(DSHIELD):
1154                 prout(_("Shields damaged and down."))
1155                 return
1156             elif game.shldup:
1157                 proutn(_("Shields are up. Do you want them down? "))
1158                 if ja() == True:
1159                     action = "SHDN"
1160                 else:
1161                     chew()
1162                     return
1163             else:
1164                 proutn(_("Shields are down. Do you want them up? "))
1165                 if ja() == True:
1166                     action = "SHUP"
1167                 else:
1168                     chew()
1169                     return    
1170     if action == "SHUP": # raise shields 
1171         if game.shldup:
1172             prout(_("Shields already up."))
1173             return
1174         game.shldup = True
1175         game.shldchg = True
1176         if game.condition != "docked":
1177             game.energy -= 50.0
1178         prout(_("Shields raised."))
1179         if game.energy <= 0:
1180             skip(1)
1181             prout(_("Shields raising uses up last of energy."))
1182             finish(FNRG)
1183             return
1184         game.ididit=True
1185         return
1186     elif action == "SHDN":
1187         if not game.shldup:
1188             prout(_("Shields already down."))
1189             return
1190         game.shldup=False
1191         game.shldchg=True
1192         prout(_("Shields lowered."))
1193         game.ididit = True
1194         return
1195     elif action == "NRG":
1196         while scan() != IHREAL:
1197             chew()
1198             proutn(_("Energy to transfer to shields- "))
1199         chew()
1200         if aaitem == 0:
1201             return
1202         if aaitem > game.energy:
1203             prout(_("Insufficient ship energy."))
1204             return
1205         game.ididit = True
1206         if game.shield+aaitem >= game.inshld:
1207             prout(_("Shield energy maximized."))
1208             if game.shield+aaitem > game.inshld:
1209                 prout(_("Excess energy requested returned to ship energy"))
1210             game.energy -= game.inshld-game.shield
1211             game.shield = game.inshld
1212             return
1213         if aaitem < 0.0 and game.energy-aaitem > game.inenrg:
1214             # Prevent shield drain loophole 
1215             skip(1)
1216             prout(_("Engineering to bridge--"))
1217             prout(_("  Scott here. Power circuit problem, Captain."))
1218             prout(_("  I can't drain the shields."))
1219             game.ididit = False
1220             return
1221         if game.shield+aaitem < 0:
1222             prout(_("All shield energy transferred to ship."))
1223             game.energy += game.shield
1224             game.shield = 0.0
1225             return
1226         proutn(_("Scotty- \""))
1227         if aaitem > 0:
1228             prout(_("Transferring energy to shields.\""))
1229         else:
1230             prout(_("Draining energy from shields.\""))
1231         game.shield += aaitem
1232         game.energy -= aaitem
1233         return
1234
1235 def randdevice():
1236     # choose a device to damage, at random. 
1237     #
1238     # Quoth Eric Allman in the code of BSD-Trek:
1239     # "Under certain conditions you can get a critical hit.  This
1240     # sort of hit damages devices.  The probability that a given
1241     # device is damaged depends on the device.  Well protected
1242     # devices (such as the computer, which is in the core of the
1243     # ship and has considerable redundancy) almost never get
1244     # damaged, whereas devices which are exposed (such as the
1245     # warp engines) or which are particularly delicate (such as
1246     # the transporter) have a much higher probability of being
1247     # damaged."
1248     # 
1249     # This is one place where OPTION_PLAIN does not restore the
1250     # original behavior, which was equiprobable damage across
1251     # all devices.  If we wanted that, we'd return randrange(NDEVICES)
1252     # and have done with it.  Also, in the original game, DNAVYS
1253     # and DCOMPTR were the same device. 
1254     # 
1255     # Instead, we use a table of weights similar to the one from BSD Trek.
1256     # BSD doesn't have the shuttle, shield controller, death ray, or probes. 
1257     # We don't have a cloaking device.  The shuttle got the allocation
1258     # for the cloaking device, then we shaved a half-percent off
1259     # everything to have some weight to give DSHCTRL/DDRAY/DDSP.
1260     # 
1261     weights = (
1262         105,    # DSRSENS: short range scanners 10.5% 
1263         105,    # DLRSENS: long range scanners          10.5% 
1264         120,    # DPHASER: phasers                      12.0% 
1265         120,    # DPHOTON: photon torpedoes             12.0% 
1266         25,     # DLIFSUP: life support          2.5% 
1267         65,     # DWARPEN: warp drive                    6.5% 
1268         70,     # DIMPULS: impulse engines               6.5% 
1269         145,    # DSHIELD: deflector shields            14.5% 
1270         30,     # DRADIO:  subspace radio                3.0% 
1271         45,     # DSHUTTL: shuttle                       4.5% 
1272         15,     # DCOMPTR: computer                      1.5% 
1273         20,     # NAVCOMP: navigation system             2.0% 
1274         75,     # DTRANSP: transporter                   7.5% 
1275         20,     # DSHCTRL: high-speed shield controller 2.0% 
1276         10,     # DDRAY: death ray                       1.0% 
1277         30,     # DDSP: deep-space probes                3.0% 
1278     )
1279     idx = randrange(1000)       # weights must sum to 1000 
1280     sum = 0
1281     for (i, w) in enumerate(weights):
1282         sum += w
1283         if idx < sum:
1284             return i
1285     return None;        # we should never get here
1286
1287 def ram(ibumpd, ienm, w):
1288     # make our ship ram something 
1289     prouts(_("***RED ALERT!  RED ALERT!"))
1290     skip(1)
1291     prout(_("***COLLISION IMMINENT."))
1292     skip(2)
1293     proutn("***")
1294     crmshp()
1295     hardness = {IHR:1.5, IHC:2.0, IHS:2.5, IHT:0.5, IHQUEST:4.0}.get(ienm, 1.0)
1296     if ibumpd:
1297         proutn(_(" rammed by "))
1298     else:
1299         proutn(_(" rams "))
1300     crmena(False, ienm, "sector", w)
1301     if ibumpd:
1302         proutn(_(" (original position)"))
1303     skip(1)
1304     deadkl(w, ienm, game.sector)
1305     proutn("***")
1306     crmshp()
1307     prout(_(" heavily damaged."))
1308     icas = randrange(10, 30)
1309     prout(_("***Sickbay reports %d casualties"), icas)
1310     game.casual += icas
1311     game.state.crew -= icas
1312     #
1313     # In the pre-SST2K version, all devices got equiprobably damaged,
1314     # which was silly.  Instead, pick up to half the devices at
1315     # random according to our weighting table,
1316     # 
1317     ncrits = randrange(NDEVICES/2)
1318     for m in range(ncrits):
1319         dev = randdevice()
1320         if game.damage[dev] < 0:
1321             continue
1322         extradm = (10.0*hardness*randreal()+1.0)*game.damfac
1323         # Damage for at least time of travel! 
1324         game.damage[dev] += game.optime + extradm
1325     game.shldup = False
1326     prout(_("***Shields are down."))
1327     if game.state.remkl + game.state.remcom + game.state.nscrem:
1328         announce()
1329         damagereport()
1330     else:
1331         finish(FWON)
1332     return
1333
1334 def torpedo(course, r, incoming, i, n):
1335     # let a photon torpedo fly 
1336     iquad = 0
1337     shoved = False
1338     ac = course + 0.25*r
1339     angle = (15.0-ac)*0.5235988
1340     bullseye = (15.0 - course)*0.5235988
1341     deltax = -math.sin(angle);
1342     deltay = math.cos(angle);
1343     x = incoming.x; y = incoming.y
1344     w = coord(); jw = coord()
1345     w.x = w.y = jw.x = jw.y = 0
1346     bigger = max(math.fabs(deltax), math.fabs(deltay))
1347     deltax /= bigger
1348     deltay /= bigger
1349     if not damaged(DSRSENS) or game.condition=="docked":
1350         setwnd(srscan_window)
1351     else: 
1352         setwnd(message_window)
1353     # Loop to move a single torpedo 
1354     for l in range(1, 15+1):
1355         x += deltax
1356         w.x = int(x + 0.5)
1357         y += deltay
1358         w.y = int(y + 0.5)
1359         if not VALID_SECTOR(w.x, w.y):
1360             break
1361         iquad=game.quad[w.x][w.y]
1362         tracktorpedo(w, l, i, n, iquad)
1363         if iquad==IHDOT:
1364             continue
1365         # hit something 
1366         setwnd(message_window)
1367         if damaged(DSRSENS) and not game.condition=="docked":
1368             skip(1);    # start new line after text track 
1369         if iquad in (IHE, IHF): # Hit our ship 
1370             skip(1)
1371             proutn(_("Torpedo hits "))
1372             crmshp()
1373             prout(".")
1374             hit = 700.0 + randreal(100) - \
1375                 1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
1376             newcnd(); # we're blown out of dock 
1377             # We may be displaced. 
1378             if game.landed or game.condition=="docked":
1379                 return hit # Cheat if on a planet 
1380             ang = angle + 2.5*(randreal()-0.5)
1381             temp = math.fabs(math.sin(ang))
1382             if math.fabs(math.cos(ang)) > temp:
1383                 temp = math.fabs(math.cos(ang))
1384             xx = -math.sin(ang)/temp
1385             yy = math.cos(ang)/temp
1386             jw.x = int(w.x+xx+0.5)
1387             jw.y = int(w.y+yy+0.5)
1388             if not VALID_SECTOR(jw.x, jw.y):
1389                 return hit
1390             if game.quad[jw.x][jw.y]==IHBLANK:
1391                 finish(FHOLE)
1392                 return hit
1393             if game.quad[jw.x][jw.y]!=IHDOT:
1394                 # can't move into object 
1395                 return hit
1396             game.sector = jw
1397             crmshp()
1398             shoved = True
1399         elif iquad in (IHC, IHS): # Hit a commander 
1400             if withprob(0.05):
1401                 crmena(True, iquad, "sector", w)
1402                 prout(_(" uses anti-photon device;"))
1403                 prout(_("   torpedo neutralized."))
1404                 return None
1405         elif iquad in (IHR, IHK): # Hit a regular enemy 
1406             # find the enemy 
1407             for enemy in game.enemies:
1408                 if w == game.enemies[ll].kloc:
1409                     break
1410             kp = math.fabs(e.kpower)
1411             h1 = 700.0 + randrange(100) - \
1412                 1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
1413             h1 = math.fabs(h1)
1414             if kp < h1:
1415                 h1 = kp
1416             if enemy.kpower < 0:
1417                 enemy.kpower -= -h1
1418             else:
1419                 enemy.kpower -= h1
1420             if enemy.kpower == 0:
1421                 deadkl(w, iquad, w)
1422                 return None
1423             crmena(True, iquad, "sector", w)
1424             # If enemy damaged but not destroyed, try to displace 
1425             ang = angle + 2.5*(randreal()-0.5)
1426             temp = math.fabs(math.sin(ang))
1427             if math.fabs(math.cos(ang)) > temp:
1428                 temp = math.fabs(math.cos(ang))
1429             xx = -math.sin(ang)/temp
1430             yy = math.cos(ang)/temp
1431             jw.x=w.x+xx+0.5
1432             jw.y=w.y+yy+0.5
1433             if not VALID_SECTOR(jw.x, jw.y):
1434                 prout(_(" damaged but not destroyed."))
1435                 return
1436             if game.quad[jw.x][jw.y]==IHBLANK:
1437                 prout(_(" buffeted into black hole."))
1438                 deadkl(w, iquad, jw)
1439                 return None
1440             if game.quad[jw.x][jw.y]!=IHDOT:
1441                 # can't move into object 
1442                 prout(_(" damaged but not destroyed."))
1443                 return None
1444             proutn(_(" damaged--"))
1445             enemy.kloc = jw
1446             shoved = True
1447             break
1448         elif iquad == IHB: # Hit a base 
1449             skip(1)
1450             prout(_("***STARBASE DESTROYED.."))
1451             for ll in range(game.state.rembase):
1452                 if game.state.baseq[ll] == game.quadrant:
1453                     game.state.baseq[ll]=game.state.baseq[game.state.rembase]
1454                     break
1455             game.quad[w.x][w.y]=IHDOT
1456             game.state.rembase -= 1
1457             game.base.x=game.base.y=0
1458             game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase -= 1
1459             game.state.chart[game.quadrant.x][game.quadrant.y].starbase -= 1
1460             game.state.basekl += 1
1461             newcnd()
1462             return None
1463         elif iquad == IHP: # Hit a planet 
1464             crmena(True, iquad, "sector", w)
1465             prout(_(" destroyed."))
1466             game.state.nplankl += 1
1467             game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = None
1468             game.iplnet.pclass = "destroyed"
1469             game.iplnet = None
1470             invalidate(game.plnet)
1471             game.quad[w.x][w.y] = IHDOT
1472             if game.landed:
1473                 # captain perishes on planet 
1474                 finish(FDPLANET)
1475             return None
1476         elif iquad == IHW: # Hit an inhabited world -- very bad! 
1477             crmena(True, iquad, "sector", w)
1478             prout(_(" destroyed."))
1479             game.state.nworldkl += 1
1480             game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = None
1481             game.iplnet.pclass = "destroyed"
1482             game.iplnet = None
1483             invalidate(game.plnet)
1484             game.quad[w.x][w.y] = IHDOT
1485             if game.landed:
1486                 # captain perishes on planet 
1487                 finish(FDPLANET)
1488             prout(_("You have just destroyed an inhabited planet."))
1489             prout(_("Celebratory rallies are being held on the Klingon homeworld."))
1490             return None
1491         elif iquad == IHSTAR: # Hit a star 
1492             if withprob(0.9):
1493                 nova(w)
1494             else:
1495                 crmena(True, IHSTAR, "sector", w)
1496                 prout(_(" unaffected by photon blast."))
1497             return None
1498         elif iquad == IHQUEST: # Hit a thingy 
1499             if not (game.options & OPTION_THINGY) or withprob(0.3):
1500                 skip(1)
1501                 prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
1502                 skip(1)
1503                 prouts(_("    HACK!     HACK!    HACK!        *CHOKE!*  "))
1504                 skip(1)
1505                 proutn(_("Mr. Spock-"))
1506                 prouts(_("  \"Fascinating!\""))
1507                 skip(1)
1508                 deadkl(w, iquad, w)
1509             else:
1510                 #
1511                 # Stas Sergeev added the possibility that
1512                 # you can shove the Thingy and piss it off.
1513                 # It then becomes an enemy and may fire at you.
1514                 #
1515                 thing.angry = True
1516                 shoved = True
1517             return None
1518         elif iquad == IHBLANK: # Black hole 
1519             skip(1)
1520             crmena(True, IHBLANK, "sector", w)
1521             prout(_(" swallows torpedo."))
1522             return None
1523         elif iquad == IHWEB: # hit the web 
1524             skip(1)
1525             prout(_("***Torpedo absorbed by Tholian web."))
1526             return None
1527         elif iquad == IHT:  # Hit a Tholian 
1528             h1 = 700.0 + randrange(100) - \
1529                 1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
1530             h1 = math.fabs(h1)
1531             if h1 >= 600:
1532                 game.quad[w.x][w.y] = IHDOT
1533                 deadkl(w, iquad, w)
1534                 game.tholian = None
1535                 return None
1536             skip(1)
1537             crmena(True, IHT, "sector", w)
1538             if withprob(0.05):
1539                 prout(_(" survives photon blast."))
1540                 return None
1541             prout(_(" disappears."))
1542             game.tholian.move(None)
1543             game.quad[w.x][w.y] = IHWEB
1544             dropin(IHBLANK)
1545             return None
1546         else: # Problem!
1547             skip(1)
1548             proutn("Don't know how to handle torpedo collision with ")
1549             crmena(True, iquad, "sector", w)
1550             skip(1)
1551             return None
1552         break
1553     if curwnd!=message_window:
1554         setwnd(message_window)
1555     if shoved:
1556         game.quad[w.x][w.y]=IHDOT
1557         game.quad[jw.x][jw.y]=iquad
1558         prout(_(" displaced by blast to Sector %s ") % jw)
1559         for ll in range(len(game.enemies)):
1560             game.enemies[ll].kdist = game.enemies[ll].kavgd = distance(game.sector,game.enemies[ll].kloc)
1561         game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
1562         return None
1563     skip(1)
1564     prout(_("Torpedo missed."))
1565     return None;
1566
1567 def fry(hit):
1568     # critical-hit resolution 
1569     if hit < (275.0-25.0*game.skill)*randreal(1.0, 1.5):
1570         return
1571     ncrit = int(1.0 + hit/(500.0+randreal(100)))
1572     proutn(_("***CRITICAL HIT--"))
1573     # Select devices and cause damage
1574     cdam = []
1575     for loop1 in range(ncrit):
1576         while True:
1577             j = randdevice()
1578             # Cheat to prevent shuttle damage unless on ship 
1579             if not (game.damage[j]<0.0 or (j==DSHUTTL and game.iscraft != "onship")):
1580                 break
1581         cdam.append(j)
1582         extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
1583         game.damage[j] += extradm
1584     skipcount = 0
1585     for (i, j) in enumerate(cdam):
1586         proutn(device[j])
1587         if skipcount % 3 == 2 and i < len(cdam)-1:
1588             skip()
1589         skipcount += 1
1590         if i < len(cdam)-1:
1591             proutn(_(" and "))
1592     prout(_(" damaged."))
1593     if damaged(DSHIELD) and game.shldup:
1594         prout(_("***Shields knocked down."))
1595         game.shldup=False
1596
1597 def attack(torps_ok):
1598     # bad guy attacks us 
1599     # torps_ok == False forces use of phasers in an attack 
1600     attempt = False; ihurt = False;
1601     hitmax=0.0; hittot=0.0; chgfac=1.0
1602     where = "neither"
1603     # game could be over at this point, check 
1604     if game.alldone:
1605         return
1606     if idebug:
1607         prout("=== ATTACK!")
1608     # Tholian gets to move before attacking 
1609     if game.tholian:
1610         movetholian()
1611     # if you have just entered the RNZ, you'll get a warning 
1612     if game.neutz: # The one chance not to be attacked 
1613         game.neutz = False
1614         return
1615     # commanders get a chance to tac-move towards you 
1616     if (((game.comhere or game.ishere) and not game.justin) or game.skill == SKILL_EMERITUS) and torps_ok:
1617         moveklings()
1618     # if no enemies remain after movement, we're done 
1619     if len(game.enemies)==0 or (len(game.enemies)==1 and thing == game.quadrant and not thing.angry):
1620         return
1621     # set up partial hits if attack happens during shield status change 
1622     pfac = 1.0/game.inshld
1623     if game.shldchg:
1624         chgfac = 0.25 + randreal(0.5)
1625     skip(1)
1626     # message verbosity control 
1627     if game.skill <= SKILL_FAIR:
1628         where = "sector"
1629     for enemy in game.enemies:
1630         if enemy.kpower < 0:
1631             continue;   # too weak to attack 
1632         # compute hit strength and diminish shield power 
1633         r = randreal()
1634         # Increase chance of photon torpedos if docked or enemy energy is low 
1635         if game.condition == "docked":
1636             r *= 0.25
1637         if enemy.kpower < 500:
1638             r *= 0.25; 
1639         if enemy.type==IHT or (enemy.type==IHQUEST and not thing.angry):
1640             continue
1641         # different enemies have different probabilities of throwing a torp 
1642         usephasers = not torps_ok or \
1643             (enemy.type == IHK and r > 0.0005) or \
1644             (enemy.type==IHC and r > 0.015) or \
1645             (enemy.type==IHR and r > 0.3) or \
1646             (enemy.type==IHS and r > 0.07) or \
1647             (enemy.type==IHQUEST and r > 0.05)
1648         if usephasers:      # Enemy uses phasers 
1649             if game.condition == "docked":
1650                 continue; # Don't waste the effort! 
1651             attempt = True; # Attempt to attack 
1652             dustfac = 0.8 + randreal(0.5)
1653             hit = enemy.kpower*math.pow(dustfac,enemy.kavgd)
1654             enemy.kpower *= 0.75
1655         else: # Enemy uses photon torpedo 
1656             course = 1.90985*math.atan2(game.sector.y-enemy.kloc.y, enemy.kloc.x-game.sector.x)
1657             hit = 0
1658             proutn(_("***TORPEDO INCOMING"))
1659             if not damaged(DSRSENS):
1660                 proutn(_(" From "))
1661                 crmena(False, enemy.type, where, enemy.kloc)
1662             attempt = True
1663             prout("  ")
1664             r = (randreal()+randreal())*0.5 - 0.5
1665             r += 0.002*enemy.kpower*r
1666             hit = torpedo(course, r, enemy.kloc, 1, 1)
1667             if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1668                 finish(FWON); # Klingons did themselves in! 
1669             if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova or game.alldone:
1670                 return # Supernova or finished 
1671             if hit == None:
1672                 continue
1673         # incoming phaser or torpedo, shields may dissipate it 
1674         if game.shldup or game.shldchg or game.condition=="docked":
1675             # shields will take hits 
1676             propor = pfac * game.shield
1677             if game.condition =="docked":
1678                 propr *= 2.1
1679             if propor < 0.1:
1680                 propor = 0.1
1681             hitsh = propor*chgfac*hit+1.0
1682             absorb = 0.8*hitsh
1683             if absorb > game.shield:
1684                 absorb = game.shield
1685             game.shield -= absorb
1686             hit -= hitsh
1687             # taking a hit blasts us out of a starbase dock 
1688             if game.condition == "docked":
1689                 dock(False)
1690             # but the shields may take care of it 
1691             if propor > 0.1 and hit < 0.005*game.energy:
1692                 continue
1693         # hit from this opponent got through shields, so take damage 
1694         ihurt = True
1695         proutn(_("%d unit hit") % int(hit))
1696         if (damaged(DSRSENS) and usephasers) or game.skill<=SKILL_FAIR:
1697             proutn(_(" on the "))
1698             crmshp()
1699         if not damaged(DSRSENS) and usephasers:
1700             proutn(_(" from "))
1701             crmena(False, enemy.type, where, enemy.kloc)
1702         skip(1)
1703         # Decide if hit is critical 
1704         if hit > hitmax:
1705             hitmax = hit
1706         hittot += hit
1707         fry(hit)
1708         game.energy -= hit
1709     if game.energy <= 0:
1710         # Returning home upon your shield, not with it... 
1711         finish(FBATTLE)
1712         return
1713     if not attempt and game.condition == "docked":
1714         prout(_("***Enemies decide against attacking your ship."))
1715     percent = 100.0*pfac*game.shield+0.5
1716     if not ihurt:
1717         # Shields fully protect ship 
1718         proutn(_("Enemy attack reduces shield strength to "))
1719     else:
1720         # Print message if starship suffered hit(s) 
1721         skip(1)
1722         proutn(_("Energy left %2d    shields ") % int(game.energy))
1723         if game.shldup:
1724             proutn(_("up "))
1725         elif not damaged(DSHIELD):
1726             proutn(_("down "))
1727         else:
1728             proutn(_("damaged, "))
1729     prout(_("%d%%,   torpedoes left %d") % (percent, game.torps))
1730     # Check if anyone was hurt 
1731     if hitmax >= 200 or hittot >= 500:
1732         icas = randrange(hittot * 0.015)
1733         if icas >= 2:
1734             skip(1)
1735             prout(_("Mc Coy-  \"Sickbay to bridge.  We suffered %d casualties") % icas)
1736             prout(_("   in that last attack.\""))
1737             game.casual += icas
1738             game.state.crew -= icas
1739     # After attack, reset average distance to enemies 
1740     for enemy in game.enemies:
1741         enemy.kavgd = enemy.kdist
1742     game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
1743     return
1744                 
1745 def deadkl(w, type, mv):
1746     # kill a Klingon, Tholian, Romulan, or Thingy 
1747     # Added mv to allow enemy to "move" before dying 
1748     crmena(True, type, "sector", mv)
1749     # Decide what kind of enemy it is and update appropriately 
1750     if type == IHR:
1751         # chalk up a Romulan 
1752         game.state.galaxy[game.quadrant.x][game.quadrant.y].romulans -= 1
1753         game.irhere -= 1
1754         game.state.nromrem -= 1
1755     elif type == IHT:
1756         # Killed a Tholian 
1757         game.tholian = None
1758     elif type == IHQUEST:
1759         # Killed a Thingy
1760         global thing
1761         thing = None
1762     else:
1763         # Some type of a Klingon 
1764         game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons -= 1
1765         game.klhere -= 1
1766         if type == IHC:
1767             game.comhere = False
1768             for i in range(game.state.remcom):
1769                 if game.state.kcmdr[i] == game.quadrant:
1770                     break
1771             game.state.kcmdr[i] = game.state.kcmdr[game.state.remcom]
1772             game.state.kcmdr[game.state.remcom].x = 0
1773             game.state.kcmdr[game.state.remcom].y = 0
1774             game.state.remcom -= 1
1775             unschedule(FTBEAM)
1776             if game.state.remcom != 0:
1777                 schedule(FTBEAM, expran(1.0*game.incom/game.state.remcom))
1778             if is_scheduled(FCDBAS) and game.battle == game.quadrant:
1779                 unschedule(FCDBAS)    
1780         elif type ==  IHK:
1781             game.state.remkl -= 1
1782         elif type ==  IHS:
1783             game.state.nscrem -= 1
1784             game.ishere = False
1785             game.state.kscmdr.x = game.state.kscmdr.y = game.isatb = 0
1786             game.iscate = False
1787             unschedule(FSCMOVE)
1788             unschedule(FSCDBAS)
1789     # For each kind of enemy, finish message to player 
1790     prout(_(" destroyed."))
1791     if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1792         return
1793     game.recompute()
1794     # Remove enemy ship from arrays describing local conditions
1795     for e in game.enemies:
1796         if e.kloc == w:
1797             e.move(None)
1798             break
1799     return
1800
1801 def targetcheck(x, y):
1802     # Return None if target is invalid 
1803     if not VALID_SECTOR(x, y):
1804         huh()
1805         return None
1806     deltx = 0.1*(y - game.sector.y)
1807     delty = 0.1*(x - game.sector.x)
1808     if deltx==0 and delty== 0:
1809         skip(1)
1810         prout(_("Spock-  \"Bridge to sickbay.  Dr. McCoy,"))
1811         prout(_("  I recommend an immediate review of"))
1812         prout(_("  the Captain's psychological profile.\""))
1813         chew()
1814         return None
1815     return 1.90985932*math.atan2(deltx, delty)
1816
1817 def photon():
1818     # launch photon torpedo 
1819     game.ididit = False
1820     if damaged(DPHOTON):
1821         prout(_("Photon tubes damaged."))
1822         chew()
1823         return
1824     if game.torps == 0:
1825         prout(_("No torpedoes left."))
1826         chew()
1827         return
1828     key = scan()
1829     while True:
1830         if key == IHALPHA:
1831             huh()
1832             return
1833         elif key == IHEOL:
1834             prout(_("%d torpedoes left.") % game.torps)
1835             proutn(_("Number of torpedoes to fire- "))
1836             key = scan()
1837         else: # key == IHREAL  {
1838             n = aaitem + 0.5
1839             if n <= 0: # abort command 
1840                 chew()
1841                 return
1842             if n > 3:
1843                 chew()
1844                 prout(_("Maximum of 3 torpedoes per burst."))
1845                 key = IHEOL
1846                 return
1847             if n <= game.torps:
1848                 break
1849             chew()
1850             key = IHEOL
1851     for i in range(1, n+1):
1852         key = scan()
1853         if i==1 and key == IHEOL:
1854             break;      # we will try prompting 
1855         if i==2 and key == IHEOL:
1856             # direct all torpedoes at one target 
1857             while i <= n:
1858                 targ[i][1] = targ[1][1]
1859                 targ[i][2] = targ[1][2]
1860                 course[i] = course[1]
1861                 i += 1
1862             break
1863         if key != IHREAL:
1864             huh()
1865             return
1866         targ[i][1] = aaitem
1867         key = scan()
1868         if key != IHREAL:
1869             huh()
1870             return
1871         targ[i][2] = aaitem
1872         course[i] = targetcheck(targ[i][1], targ[i][2])
1873         if course[i] == None:
1874             return
1875     chew()
1876     if i == 1 and key == IHEOL:
1877         # prompt for each one 
1878         for i in range(1, n+1):
1879             proutn(_("Target sector for torpedo number %d- ") % i)
1880             key = scan()
1881             if key != IHREAL:
1882                 huh()
1883                 return
1884             targ[i][1] = int(aaitem-0.5)
1885             key = scan()
1886             if key != IHREAL:
1887                 huh()
1888                 return
1889             targ[i][2] = int(aaitem-0.5)
1890             chew()
1891             course[i] = targetcheck(targ[i][1], targ[i][2])
1892             if course[i] == None:
1893                 return
1894     game.ididit = True
1895     # Loop for moving <n> torpedoes 
1896     for i in range(n):
1897         if game.condition != "docked":
1898             game.torps -= 1
1899         r = (randreal()+randreal())*0.5 -0.5
1900         if math.fabs(r) >= 0.47:
1901             # misfire! 
1902             r *= randreal(1.2, 2.2)
1903             if n > 0:
1904                 prouts(_("***TORPEDO NUMBER %d MISFIRES") % (i+1))
1905             else:
1906                 prouts(_("***TORPEDO MISFIRES."))
1907             skip(1)
1908             if i < n:
1909                 prout(_("  Remainder of burst aborted."))
1910             if withprob(0.2):
1911                 prout(_("***Photon tubes damaged by misfire."))
1912                 game.damage[DPHOTON] = game.damfac * randreal(1.0, 3.0)
1913             break
1914         if game.shldup or game.condition == "docked":
1915             r *= 1.0 + 0.0001*game.shield
1916         torpedo(course[i], r, game.sector, i, n)
1917         if game.alldone or game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
1918             return
1919     if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1920         finish(FWON);
1921
1922 def overheat(rpow):
1923     # check for phasers overheating 
1924     if rpow > 1500:
1925         checkburn = (rpow-1500.0)*0.00038
1926         if withprob(checkburn):
1927             prout(_("Weapons officer Sulu-  \"Phasers overheated, sir.\""))
1928             game.damage[DPHASER] = game.damfac* randreal(1.0, 2.0) * (1.0+checkburn)
1929
1930 def checkshctrl(rpow):
1931     # check shield control 
1932     skip(1)
1933     if withprob(0.998):
1934         prout(_("Shields lowered."))
1935         return False
1936     # Something bad has happened 
1937     prouts(_("***RED ALERT!  RED ALERT!"))
1938     skip(2)
1939     hit = rpow*game.shield/game.inshld
1940     game.energy -= rpow+hit*0.8
1941     game.shield -= hit*0.2
1942     if game.energy <= 0.0:
1943         prouts(_("Sulu-  \"Captain! Shield malf***********************\""))
1944         skip(1)
1945         stars()
1946         finish(FPHASER)
1947         return True
1948     prouts(_("Sulu-  \"Captain! Shield malfunction! Phaser fire contained!\""))
1949     skip(2)
1950     prout(_("Lt. Uhura-  \"Sir, all decks reporting damage.\""))
1951     icas = randrange(hit*0.012)
1952     skip(1)
1953     fry(0.8*hit)
1954     if icas:
1955         skip(1)
1956         prout(_("McCoy to bridge- \"Severe radiation burns, Jim."))
1957         prout(_("  %d casualties so far.\"") % icas)
1958         game.casual += icas
1959         game.state.crew -= icas
1960     skip(1)
1961     prout(_("Phaser energy dispersed by shields."))
1962     prout(_("Enemy unaffected."))
1963     overheat(rpow)
1964     return True;
1965
1966 def hittem(hits):
1967     # register a phaser hit on Klingons and Romulans
1968     nenhr2 = game.nenhere; kk=0
1969     w = coord()
1970     skip(1)
1971     for (k, wham) in enumerate(hits):
1972         if wham==0:
1973             continue
1974         dustfac = randreal(0.9, 1.0)
1975         hit = wham*math.pow(dustfac,game.enemies[kk].kdist)
1976         kpini = game.enemies[kk].kpower
1977         kp = math.fabs(kpini)
1978         if PHASEFAC*hit < kp:
1979             kp = PHASEFAC*hit
1980         if game.enemies[kk].kpower < 0:
1981             game.enemies[kk].kpower -= -kp
1982         else:
1983             game.enemies[kk].kpower -= kp
1984         kpow = game.enemies[kk].kpower
1985         w = game.enemies[kk].kloc
1986         if hit > 0.005:
1987             if not damaged(DSRSENS):
1988                 boom(w)
1989             proutn(_("%d unit hit on ") % int(hit))
1990         else:
1991             proutn(_("Very small hit on "))
1992         ienm = game.quad[w.x][w.y]
1993         if ienm==IHQUEST:
1994             thing.angry = True
1995         crmena(False, ienm, "sector", w)
1996         skip(1)
1997         if kpow == 0:
1998             deadkl(w, ienm, w)
1999             if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
2000                 finish(FWON);           
2001             if game.alldone:
2002                 return
2003             kk -= 1     # don't do the increment
2004             continue
2005         else: # decide whether or not to emasculate klingon 
2006             if kpow>0 and withprob(0.9) and kpow <= randreal(0.4, 0.8)*kpini:
2007                 prout(_("***Mr. Spock-  \"Captain, the vessel at Sector %s")%w)
2008                 prout(_("   has just lost its firepower.\""))
2009                 game.enemies[kk].kpower = -kpow
2010         kk += 1
2011     return
2012
2013 def phasers():
2014     # fire phasers 
2015     hits = []
2016     kz = 0; k = 1; irec=0 # Cheating inhibitor 
2017     ifast = False; no = False; itarg = True; msgflag = True; rpow=0
2018     automode = "NOTSET"
2019     key=0
2020     skip(1)
2021     # SR sensors and Computer are needed fopr automode 
2022     if damaged(DSRSENS) or damaged(DCOMPTR):
2023         itarg = False
2024     if game.condition == "docked":
2025         prout(_("Phasers can't be fired through base shields."))
2026         chew()
2027         return
2028     if damaged(DPHASER):
2029         prout(_("Phaser control damaged."))
2030         chew()
2031         return
2032     if game.shldup:
2033         if damaged(DSHCTRL):
2034             prout(_("High speed shield control damaged."))
2035             chew()
2036             return
2037         if game.energy <= 200.0:
2038             prout(_("Insufficient energy to activate high-speed shield control."))
2039             chew()
2040             return
2041         prout(_("Weapons Officer Sulu-  \"High-speed shield control enabled, sir.\""))
2042         ifast = True
2043     # Original code so convoluted, I re-did it all
2044     # (That was Tom Almy talking about the C code, I think -- ESR)
2045     while automode=="NOTSET":
2046         key=scan()
2047         if key == IHALPHA:
2048             if isit("manual"):
2049                 if game.nenhere==0:
2050                     prout(_("There is no enemy present to select."))
2051                     chew()
2052                     key = IHEOL
2053                     automode="AUTOMATIC"
2054                 else:
2055                     automode = "MANUAL"
2056                     key = scan()
2057             elif isit("automatic"):
2058                 if (not itarg) and game.nenhere != 0:
2059                     automode = "FORCEMAN"
2060                 else:
2061                     if game.nenhere==0:
2062                         prout(_("Energy will be expended into space."))
2063                     automode = "AUTOMATIC"
2064                     key = scan()
2065             elif isit("no"):
2066                 no = True
2067             else:
2068                 huh()
2069                 return
2070         elif key == IHREAL:
2071             if game.nenhere==0:
2072                 prout(_("Energy will be expended into space."))
2073                 automode = "AUTOMATIC"
2074             elif not itarg:
2075                 automode = "FORCEMAN"
2076             else:
2077                 automode = "AUTOMATIC"
2078         else:
2079             # IHEOL 
2080             if game.nenhere==0:
2081                 prout(_("Energy will be expended into space."))
2082                 automode = "AUTOMATIC"
2083             elif not itarg:
2084                 automode = "FORCEMAN"
2085             else: 
2086                 proutn(_("Manual or automatic? "))
2087                 chew()
2088     avail = game.energy
2089     if ifast:
2090         avail -= 200.0
2091     if automode == "AUTOMATIC":
2092         if key == IHALPHA and isit("no"):
2093             no = True
2094             key = scan()
2095         if key != IHREAL and game.nenhere != 0:
2096             prout(_("Phasers locked on target. Energy available: %.2f")%avail)
2097         irec=0
2098         while True:
2099             chew()
2100             if not kz:
2101                 for i in range(game.nenhere):
2102                     irec += math.fabs(game.enemies[i].kpower)/(PHASEFAC*math.pow(0.90,game.enemies[i].kdist))*randreal(1.01, 1.06) + 1.0
2103             kz=1
2104             proutn(_("%d units required. ") % irec)
2105             chew()
2106             proutn(_("Units to fire= "))
2107             key = scan()
2108             if key!=IHREAL:
2109                 return
2110             rpow = aaitem
2111             if rpow > avail:
2112                 proutn(_("Energy available= %.2f") % avail)
2113                 skip(1)
2114                 key = IHEOL
2115             if not rpow > avail:
2116                 break
2117         if rpow<=0:
2118             # chicken out 
2119             chew()
2120             return
2121         key=scan()
2122         if key == IHALPHA and isit("no"):
2123             no = True
2124         if ifast:
2125             game.energy -= 200; # Go and do it! 
2126             if checkshctrl(rpow):
2127                 return
2128         chew()
2129         game.energy -= rpow
2130         extra = rpow
2131         if game.nenhere:
2132             extra = 0.0
2133             powrem = rpow
2134             for i in range(game.nenhere):
2135                 hits.append(0.0)
2136                 if powrem <= 0:
2137                     continue
2138                 hits[i] = math.fabs(game.enemies[i].kpower)/(PHASEFAC*math.pow(0.90,game.enemies[i].kdist))
2139                 over = randreal(1.01, 1.06) * hits[i]
2140                 temp = powrem
2141                 powrem -= hits[i] + over
2142                 if powrem <= 0 and temp < hits[i]:
2143                     hits[i] = temp
2144                 if powrem <= 0:
2145                     over = 0.0
2146                 extra += over
2147             if powrem > 0.0:
2148                 extra += powrem
2149             hittem(hits)
2150             game.ididit = True
2151         if extra > 0 and not game.alldone:
2152             if game.tholian:
2153                 proutn(_("*** Tholian web absorbs "))
2154                 if game.nenhere>0:
2155                     proutn(_("excess "))
2156                 prout(_("phaser energy."))
2157             else:
2158                 prout(_("%d expended on empty space.") % int(extra))
2159     elif automode == "FORCEMAN":
2160         chew()
2161         key = IHEOL
2162         if damaged(DCOMPTR):
2163             prout(_("Battle computer damaged, manual fire only."))
2164         else:
2165             skip(1)
2166             prouts(_("---WORKING---"))
2167             skip(1)
2168             prout(_("Short-range-sensors-damaged"))
2169             prout(_("Insufficient-data-for-automatic-phaser-fire"))
2170             prout(_("Manual-fire-must-be-used"))
2171             skip(1)
2172     elif automode == "MANUAL":
2173         rpow = 0.0
2174         for k in range(game.nenhere):
2175             aim = game.enemies[k].kloc
2176             ienm = game.quad[aim.x][aim.y]
2177             if msgflag:
2178                 proutn(_("Energy available= %.2f") % (avail-0.006))
2179                 skip(1)
2180                 msgflag = False
2181                 rpow = 0.0
2182             if damaged(DSRSENS) and not (abs(game.sector.x-aim.x) < 2 and abs(game.sector.y-aim.y) < 2) and \
2183                 (ienm == IHC or ienm == IHS):
2184                 cramen(ienm)
2185                 prout(_(" can't be located without short range scan."))
2186                 chew()
2187                 key = IHEOL
2188                 hits[k] = 0; # prevent overflow -- thanks to Alexei Voitenko 
2189                 k += 1
2190                 continue
2191             if key == IHEOL:
2192                 chew()
2193                 if itarg and k > kz:
2194                     irec=(abs(game.enemies[k].kpower)/(PHASEFAC*math.pow(0.9,game.enemies[k].kdist))) * randreal(1.01, 1.06) + 1.0
2195                 kz = k
2196                 proutn("(")
2197                 if not damaged(DCOMPTR):
2198                     proutn("%d" % irec)
2199                 else:
2200                     proutn("??")
2201                 proutn(")  ")
2202                 proutn(_("units to fire at "))
2203                 crmena(False, ienm, "sector", aim)
2204                 proutn("-  ")
2205                 key = scan()
2206             if key == IHALPHA and isit("no"):
2207                 no = True
2208                 key = scan()
2209                 continue
2210             if key == IHALPHA:
2211                 huh()
2212                 return
2213             if key == IHEOL:
2214                 if k==1: # Let me say I'm baffled by this 
2215                     msgflag = True
2216                 continue
2217             if aaitem < 0:
2218                 # abort out 
2219                 chew()
2220                 return
2221             hits[k] = aaitem
2222             rpow += aaitem
2223             # If total requested is too much, inform and start over 
2224             if rpow > avail:
2225                 prout(_("Available energy exceeded -- try again."))
2226                 chew()
2227                 return
2228             key = scan(); # scan for next value 
2229             k += 1
2230         if rpow == 0.0:
2231             # zero energy -- abort 
2232             chew()
2233             return
2234         if key == IHALPHA and isit("no"):
2235             no = True
2236         game.energy -= rpow
2237         chew()
2238         if ifast:
2239             game.energy -= 200.0
2240             if checkshctrl(rpow):
2241                 return
2242         hittem(hits)
2243         game.ididit = True
2244      # Say shield raised or malfunction, if necessary 
2245     if game.alldone:
2246         return
2247     if ifast:
2248         skip(1)
2249         if no == 0:
2250             if withprob(0.99):
2251                 prout(_("Sulu-  \"Sir, the high-speed shield control has malfunctioned . . ."))
2252                 prouts(_("         CLICK   CLICK   POP  . . ."))
2253                 prout(_(" No response, sir!"))
2254                 game.shldup = False
2255             else:
2256                 prout(_("Shields raised."))
2257         else:
2258             game.shldup = False
2259     overheat(rpow);
2260
2261 # Code from events,c begins here.
2262
2263 # This isn't a real event queue a la BSD Trek yet -- you can only have one 
2264 # event of each type active at any given time.  Mostly these means we can 
2265 # only have one FDISTR/FENSLV/FREPRO sequence going at any given time
2266 # BSD Trek, from which we swiped the idea, can have up to 5.
2267
2268 def unschedule(evtype):
2269     # remove an event from the schedule 
2270     game.future[evtype].date = FOREVER
2271     return game.future[evtype]
2272
2273 def is_scheduled(evtype):
2274     # is an event of specified type scheduled 
2275     return game.future[evtype].date != FOREVER
2276
2277 def scheduled(evtype):
2278     # when will this event happen? 
2279     return game.future[evtype].date
2280
2281 def schedule(evtype, offset):
2282     # schedule an event of specified type
2283     game.future[evtype].date = game.state.date + offset
2284     return game.future[evtype]
2285
2286 def postpone(evtype, offset):
2287     # postpone a scheduled event 
2288     game.future[evtype].date += offset
2289
2290 def cancelrest():
2291     # rest period is interrupted by event 
2292     if game.resting:
2293         skip(1)
2294         proutn(_("Mr. Spock-  \"Captain, shall we cancel the rest period?\""))
2295         if ja() == True:
2296             game.resting = False
2297             game.optime = 0.0
2298             return True
2299     return False
2300
2301 def events():
2302     # run through the event queue looking for things to do 
2303     i=0
2304     fintim = game.state.date + game.optime; yank=0
2305     ictbeam = False; istract = False
2306     w = coord(); hold = coord()
2307     ev = event(); ev2 = event()
2308
2309     def tractorbeam(yank):
2310         # tractor beaming cases merge here 
2311         announce()
2312         game.optime = (10.0/(7.5*7.5))*yank # 7.5 is yank rate (warp 7.5) 
2313         skip(1)
2314         proutn("***")
2315         crmshp()
2316         prout(_(" caught in long range tractor beam--"))
2317         # If Kirk & Co. screwing around on planet, handle 
2318         atover(True) # atover(true) is Grab 
2319         if game.alldone:
2320             return
2321         if game.icraft: # Caught in Galileo? 
2322             finish(FSTRACTOR)
2323             return
2324         # Check to see if shuttle is aboard 
2325         if game.iscraft == "offship":
2326             skip(1)
2327             if withprob(0.5):
2328                 prout(_("Galileo, left on the planet surface, is captured"))
2329                 prout(_("by aliens and made into a flying McDonald's."))
2330                 game.damage[DSHUTTL] = -10
2331                 game.iscraft = "removed"
2332             else:
2333                 prout(_("Galileo, left on the planet surface, is well hidden."))
2334         if evcode == FSPY:
2335             game.quadrant = game.state.kscmdr
2336         else:
2337             game.quadrant = game.state.kcmdr[i]
2338         game.sector = randplace(QUADSIZE)
2339         crmshp()
2340         prout(_(" is pulled to Quadrant %s, Sector %s") \
2341                % (game.quadrant, game.sector))
2342         if game.resting:
2343             prout(_("(Remainder of rest/repair period cancelled.)"))
2344             game.resting = False
2345         if not game.shldup:
2346             if not damaged(DSHIELD) and game.shield > 0:
2347                 doshield(shraise=True) # raise shields 
2348                 game.shldchg = False
2349             else:
2350                 prout(_("(Shields not currently useable.)"))
2351         newqad(False)
2352         # Adjust finish time to time of tractor beaming 
2353         fintim = game.state.date+game.optime
2354         attack(False)
2355         if game.state.remcom <= 0:
2356             unschedule(FTBEAM)
2357         else: 
2358             schedule(FTBEAM, game.optime+expran(1.5*game.intime/game.state.remcom))
2359
2360     def destroybase():
2361         # Code merges here for any commander destroying base 
2362         # Not perfect, but will have to do 
2363         # Handle case where base is in same quadrant as starship 
2364         if game.battle == game.quadrant:
2365             game.state.chart[game.battle.x][game.battle.y].starbase = False
2366             game.quad[game.base.x][game.base.y] = IHDOT
2367             game.base.x=game.base.y=0
2368             newcnd()
2369             skip(1)
2370             prout(_("Spock-  \"Captain, I believe the starbase has been destroyed.\""))
2371         elif game.state.rembase != 1 and communicating():
2372             # Get word via subspace radio 
2373             announce()
2374             skip(1)
2375             prout(_("Lt. Uhura-  \"Captain, Starfleet Command reports that"))
2376             proutn(_("   the starbase in Quadrant %s has been destroyed by") % game.battle)
2377             if game.isatb == 2: 
2378                 prout(_("the Klingon Super-Commander"))
2379             else:
2380                 prout(_("a Klingon Commander"))
2381             game.state.chart[game.battle.x][game.battle.y].starbase = False
2382         # Remove Starbase from galaxy 
2383         game.state.galaxy[game.battle.x][game.battle.y].starbase = False
2384         for i in range(1, game.state.rembase+1):
2385             if game.state.baseq[i] == game.battle:
2386                 game.state.baseq[i] = game.state.baseq[game.state.rembase]
2387         game.state.rembase -= 1
2388         if game.isatb == 2:
2389             # reinstate a commander's base attack 
2390             game.battle = hold
2391             game.isatb = 0
2392         else:
2393             invalidate(game.battle)
2394
2395     if idebug:
2396         prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
2397         for i in range(1, NEVENTS):
2398             if   i == FSNOVA:  proutn("=== Supernova       ")
2399             elif i == FTBEAM:  proutn("=== T Beam          ")
2400             elif i == FSNAP:   proutn("=== Snapshot        ")
2401             elif i == FBATTAK: proutn("=== Base Attack     ")
2402             elif i == FCDBAS:  proutn("=== Base Destroy    ")
2403             elif i == FSCMOVE: proutn("=== SC Move         ")
2404             elif i == FSCDBAS: proutn("=== SC Base Destroy ")
2405             elif i == FDSPROB: proutn("=== Probe Move      ")
2406             elif i == FDISTR:  proutn("=== Distress Call   ")
2407             elif i == FENSLV:  proutn("=== Enslavement     ")
2408             elif i == FREPRO:  proutn("=== Klingon Build   ")
2409             if is_scheduled(i):
2410                 prout("%.2f" % (scheduled(i)))
2411             else:
2412                 prout("never")
2413     radio_was_broken = damaged(DRADIO)
2414     hold.x = hold.y = 0
2415     while True:
2416         # Select earliest extraneous event, evcode==0 if no events 
2417         evcode = FSPY
2418         if game.alldone:
2419             return
2420         datemin = fintim
2421         for l in range(1, NEVENTS):
2422             if game.future[l].date < datemin:
2423                 evcode = l
2424                 if idebug:
2425                     prout("== Event %d fires" % evcode)
2426                 datemin = game.future[l].date
2427         xtime = datemin-game.state.date
2428         game.state.date = datemin
2429         # Decrement Federation resources and recompute remaining time 
2430         game.state.remres -= (game.state.remkl+4*game.state.remcom)*xtime
2431         game.recompute()
2432         if game.state.remtime <=0:
2433             finish(FDEPLETE)
2434             return
2435         # Any crew left alive? 
2436         if game.state.crew <=0:
2437             finish(FCREW)
2438             return
2439         # Is life support adequate? 
2440         if damaged(DLIFSUP) and game.condition != "docked":
2441             if game.lsupres < xtime and game.damage[DLIFSUP] > game.lsupres:
2442                 finish(FLIFESUP)
2443                 return
2444             game.lsupres -= xtime
2445             if game.damage[DLIFSUP] <= xtime:
2446                 game.lsupres = game.inlsr
2447         # Fix devices 
2448         repair = xtime
2449         if game.condition == "docked":
2450             repair /= game.docfac
2451         # Don't fix Deathray here 
2452         for l in range(NDEVICES):
2453             if game.damage[l] > 0.0 and l != DDRAY:
2454                 if game.damage[l]-repair > 0.0:
2455                     game.damage[l] -= repair
2456                 else:
2457                     game.damage[l] = 0.0
2458         # If radio repaired, update star chart and attack reports 
2459         if radio_was_broken and not damaged(DRADIO):
2460             prout(_("Lt. Uhura- \"Captain, the sub-space radio is working and"))
2461             prout(_("   surveillance reports are coming in."))
2462             skip(1)
2463             if not game.iseenit:
2464                 attackreport(False)
2465                 game.iseenit = True
2466             rechart()
2467             prout(_("   The star chart is now up to date.\""))
2468             skip(1)
2469         # Cause extraneous event EVCODE to occur 
2470         game.optime -= xtime
2471         if evcode == FSNOVA: # Supernova 
2472             announce()
2473             supernova(False)
2474             schedule(FSNOVA, expran(0.5*game.intime))
2475             if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
2476                 return
2477         elif evcode == FSPY: # Check with spy to see if SC should tractor beam 
2478             if game.state.nscrem == 0 or \
2479                 ictbeam or istract or \
2480                 game.condition=="docked" or game.isatb==1 or game.iscate:
2481                 return
2482             if game.ientesc or \
2483                 (game.energy<2000 and game.torps<4 and game.shield < 1250) or \
2484                 (damaged(DPHASER) and (damaged(DPHOTON) or game.torps<4)) or \
2485                 (damaged(DSHIELD) and \
2486                  (game.energy < 2500 or damaged(DPHASER)) and \
2487                  (game.torps < 5 or damaged(DPHOTON))):
2488                 # Tractor-beam her! 
2489                 istract = ictbeam = True
2490                 tractorbeam(distance(game.state.kscmdr, game.quadrant))
2491             else:
2492                 return
2493         elif evcode == FTBEAM: # Tractor beam 
2494             if game.state.remcom == 0:
2495                 unschedule(FTBEAM)
2496                 continue
2497             i = randrange(game.state.remcom)
2498             yank = distance(game.state.kcmdr[i], game.quadrant)
2499             if istract or game.condition == "docked" or yank == 0:
2500                 # Drats! Have to reschedule 
2501                 schedule(FTBEAM, 
2502                          game.optime + expran(1.5*game.intime/game.state.remcom))
2503                 continue
2504             ictbeam = True
2505             tractorbeam(yank)
2506         elif evcode == FSNAP: # Snapshot of the universe (for time warp) 
2507             game.snapsht = copy.deepcopy(game.state)
2508             game.state.snap = True
2509             schedule(FSNAP, expran(0.5 * game.intime))
2510         elif evcode == FBATTAK: # Commander attacks starbase 
2511             if game.state.remcom==0 or game.state.rembase==0:
2512                 # no can do 
2513                 unschedule(FBATTAK)
2514                 unschedule(FCDBAS)
2515                 continue
2516             i = 0
2517             for j in range(game.state.rembase):
2518                 for k in range(game.state.remcom):
2519                     if game.state.baseq[j] == game.state.kcmdr[k] and \
2520                         not game.state.baseq[j] == game.quadrant and \
2521                         not game.state.baseq[j] == game.state.kscmdr:
2522                         i = 1
2523                 if i == 1:
2524                     continue
2525             if j>game.state.rembase:
2526                 # no match found -- try later 
2527                 schedule(FBATTAK, expran(0.3*game.intime))
2528                 unschedule(FCDBAS)
2529                 continue
2530             # commander + starbase combination found -- launch attack 
2531             game.battle = game.state.baseq[j]
2532             schedule(FCDBAS, randreal(1.0, 4.0))
2533             if game.isatb: # extra time if SC already attacking 
2534                 postpone(FCDBAS, scheduled(FSCDBAS)-game.state.date)
2535             game.future[FBATTAK].date = game.future[FCDBAS].date + expran(0.3*game.intime)
2536             game.iseenit = False
2537             if not communicating():
2538                 continue # No warning :-( 
2539             game.iseenit = True
2540             announce()
2541             skip(1)
2542             prout(_("Lt. Uhura-  \"Captain, the starbase in Quadrant %s") % game.battle)
2543             prout(_("   reports that it is under attack and that it can"))
2544             prout(_("   hold out only until stardate %d.\"") % (int(scheduled(FCDBAS))))
2545             if cancelrest():
2546                 return
2547         elif evcode == FSCDBAS: # Supercommander destroys base 
2548             unschedule(FSCDBAS)
2549             game.isatb = 2
2550             if not game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].starbase: 
2551                 continue # WAS RETURN! 
2552             hold = game.battle
2553             game.battle = game.state.kscmdr
2554             destroybase()
2555         elif evcode == FCDBAS: # Commander succeeds in destroying base 
2556             if evcode==FCDBAS:
2557                 unschedule(FCDBAS)
2558                 # find the lucky pair 
2559                 for i in range(game.state.remcom):
2560                     if game.state.kcmdr[i] == game.battle: 
2561                         break
2562                 if i > game.state.remcom or game.state.rembase == 0 or \
2563                     not game.state.galaxy[game.battle.x][game.battle.y].starbase:
2564                     # No action to take after all 
2565                     invalidate(game.battle)
2566                     continue
2567             destroybase()
2568         elif evcode == FSCMOVE: # Supercommander moves 
2569             schedule(FSCMOVE, 0.2777)
2570             if not game.ientesc and not istract and game.isatb != 1 and \
2571                    (not game.iscate or not game.justin): 
2572                 supercommander()
2573         elif evcode == FDSPROB: # Move deep space probe 
2574             schedule(FDSPROB, 0.01)
2575             game.probex += game.probeinx
2576             game.probey += game.probeiny
2577             i = (int)(game.probex/QUADSIZE +0.05)
2578             j = (int)(game.probey/QUADSIZE + 0.05)
2579             if game.probec.x != i or game.probec.y != j:
2580                 game.probec.x = i
2581                 game.probec.y = j
2582                 if not VALID_QUADRANT(i, j) or \
2583                     game.state.galaxy[game.probec.x][game.probec.y].supernova:
2584                     # Left galaxy or ran into supernova
2585                     if comunicating():
2586                         announce()
2587                         skip(1)
2588                         proutn(_("Lt. Uhura-  \"The deep space probe "))
2589                         if not VALID_QUADRANT(j, i):
2590                             proutn(_("has left the galaxy"))
2591                         else:
2592                             proutn(_("is no longer transmitting"))
2593                         prout(".\"")
2594                     unschedule(FDSPROB)
2595                     continue
2596                 if not communicating():
2597                     announce()
2598                     skip(1)
2599                     proutn(_("Lt. Uhura-  \"The deep space probe is now in Quadrant %s.\"") % game.probec)
2600             pdest = game.state.galaxy[game.probec.x][game.probec.y]
2601             # Update star chart if Radio is working or have access to radio
2602             if communicating():
2603                 chp = game.state.chart[game.probec.x][game.probec.y]
2604                 chp.klingons = pdest.klingons
2605                 chp.starbase = pdest.starbase
2606                 chp.stars = pdest.stars
2607                 pdest.charted = True
2608             game.proben -= 1 # One less to travel
2609             if game.proben == 0 and game.isarmed and pdest.stars:
2610                 # lets blow the sucker! 
2611                 supernova(True, game.probec)
2612                 unschedule(FDSPROB)
2613                 if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova: 
2614                     return
2615         elif evcode == FDISTR: # inhabited system issues distress call 
2616             unschedule(FDISTR)
2617             # try a whole bunch of times to find something suitable 
2618             for i in range(100):
2619                 # need a quadrant which is not the current one,
2620                 # which has some stars which are inhabited and
2621                 # not already under attack, which is not
2622                 # supernova'ed, and which has some Klingons in it
2623                 w = randplace(GALSIZE)
2624                 q = game.state.galaxy[w.x][w.y]
2625                 if not (game.quadrant == w or q.planet == None or \
2626                       not q.planet.inhabited or \
2627                       q.supernova or q.status!="secure" or q.klingons<=0):
2628                     break
2629             else:
2630                 # can't seem to find one; ignore this call 
2631                 if idebug:
2632                     prout("=== Couldn't find location for distress event.")
2633                 continue
2634             # got one!!  Schedule its enslavement 
2635             ev = schedule(FENSLV, expran(game.intime))
2636             ev.quadrant = w
2637             q.status = distressed
2638
2639             # tell the captain about it if we can 
2640             if communicating():
2641                 prout(_("Uhura- Captain, %s in Quadrant %s reports it is under attack") \
2642                         % (q.planet, `w`))
2643                 prout(_("by a Klingon invasion fleet."))
2644                 if cancelrest():
2645                     return
2646         elif evcode == FENSLV:          # starsystem is enslaved 
2647             ev = unschedule(FENSLV)
2648             # see if current distress call still active 
2649             q = game.state.galaxy[ev.quadrant.x][ev.quadrant.y]
2650             if q.klingons <= 0:
2651                 q.status = "secure"
2652                 continue
2653             q.status = "enslaved"
2654
2655             # play stork and schedule the first baby 
2656             ev2 = schedule(FREPRO, expran(2.0 * game.intime))
2657             ev2.quadrant = ev.quadrant
2658
2659             # report the disaster if we can 
2660             if communicating():
2661                 prout(_("Uhura- We've lost contact with starsystem %s") % \
2662                         q.planet)
2663                 prout(_("in Quadrant %s.\n") % ev.quadrant)
2664         elif evcode == FREPRO:          # Klingon reproduces 
2665             # If we ever switch to a real event queue, we'll need to
2666             # explicitly retrieve and restore the x and y.
2667             ev = schedule(FREPRO, expran(1.0 * game.intime))
2668             # see if current distress call still active 
2669             q = game.state.galaxy[ev.quadrant.x][ev.quadrant.y]
2670             if q.klingons <= 0:
2671                 q.status = "secure"
2672                 continue
2673             if game.state.remkl >=MAXKLGAME:
2674                 continue                # full right now 
2675             # reproduce one Klingon 
2676             w = ev.quadrant
2677             if game.klhere >= MAXKLQUAD:
2678                 try:
2679                     # this quadrant not ok, pick an adjacent one 
2680                     for i in range(w.x - 1, w.x + 2):
2681                         for j in range(w.y - 1, w.y + 2):
2682                             if not VALID_QUADRANT(i, j):
2683                                 continue
2684                             q = game.state.galaxy[w.x][w.y]
2685                             # check for this quad ok (not full & no snova) 
2686                             if q.klingons >= MAXKLQUAD or q.supernova:
2687                                 continue
2688                             raise "FOUNDIT"
2689                     else:
2690                         continue        # search for eligible quadrant failed
2691                 except "FOUNDIT":
2692                     w.x = i
2693                     w.y = j
2694             # deliver the child 
2695             game.state.remkl += 1
2696             q.klingons += 1
2697             if game.quadrant == w:
2698                 game.klhere += 1
2699                 game.enemies.append(newkling())
2700             # recompute time left
2701             game.recompute()
2702             # report the disaster if we can 
2703             if communicating():
2704                 if game.quadrant == w:
2705                     prout(_("Spock- sensors indicate the Klingons have"))
2706                     prout(_("launched a warship from %s.") % q.planet)
2707                 else:
2708                     prout(_("Uhura- Starfleet reports increased Klingon activity"))
2709                     if q.planet != None:
2710                         proutn(_("near %s") % q.planet)
2711                     prout(_("in Quadrant %s.") % w)
2712                                 
2713 def wait():
2714     # wait on events 
2715     game.ididit = False
2716     while True:
2717         key = scan()
2718         if key  != IHEOL:
2719             break
2720         proutn(_("How long? "))
2721     chew()
2722     if key != IHREAL:
2723         huh()
2724         return
2725     origTime = delay = aaitem
2726     if delay <= 0.0:
2727         return
2728     if delay >= game.state.remtime or game.nenhere != 0:
2729         proutn(_("Are you sure? "))
2730         if ja() == False:
2731             return
2732     # Alternate resting periods (events) with attacks 
2733     game.resting = True
2734     while True:
2735         if delay <= 0:
2736             game.resting = False
2737         if not game.resting:
2738             prout(_("%d stardates left.") % int(game.state.remtime))
2739             return
2740         temp = game.optime = delay
2741         if game.nenhere:
2742             rtime = randreal(1.0, 2.0)
2743             if rtime < temp:
2744                 temp = rtime
2745             game.optime = temp
2746         if game.optime < delay:
2747             attack(False)
2748         if game.alldone:
2749             return
2750         events()
2751         game.ididit = True
2752         if game.alldone:
2753             return
2754         delay -= temp
2755         # Repair Deathray if long rest at starbase 
2756         if origTime-delay >= 9.99 and game.condition == "docked":
2757             game.damage[DDRAY] = 0.0
2758         # leave if quadrant supernovas
2759         if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
2760             break
2761     game.resting = False
2762     game.optime = 0
2763
2764 # A nova occurs.  It is the result of having a star hit with a
2765 # photon torpedo, or possibly of a probe warhead going off.
2766 # Stars that go nova cause stars which surround them to undergo
2767 # the same probabilistic process.  Klingons next to them are
2768 # destroyed.  And if the starship is next to it, it gets zapped.
2769 # If the zap is too much, it gets destroyed.
2770         
2771 def nova(nov):
2772     # star goes nova 
2773     course = (0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5)
2774     newc = coord(); neighbor = coord(); bump = coord(0, 0)
2775     if withprob(0.05):
2776         # Wow! We've supernova'ed 
2777         supernova(False, nov)
2778         return
2779     # handle initial nova 
2780     game.quad[nov.x][nov.y] = IHDOT
2781     crmena(False, IHSTAR, "sector", nov)
2782     prout(_(" novas."))
2783     game.state.galaxy[game.quadrant.x][game.quadrant.y].stars -= 1
2784     game.state.starkl += 1
2785     # Set up queue to recursively trigger adjacent stars 
2786     hits = [nov]
2787     kount = 0
2788     while hits:
2789         offset = coord()
2790         start = hits.pop()
2791         for offset.x in range(-1, 1+1):
2792             for offset.y in range(-1, 1+1):
2793                 if offset.y==0 and offset.x==0:
2794                     continue
2795                 neighbor = start + offset
2796                 if not VALID_SECTOR(neighbor.y, neighbor.x):
2797                     continue
2798                 iquad = game.quad[neighbor.x][neighbor.y]
2799                 # Empty space ends reaction
2800                 if iquad in (IHDOT, IHQUEST, IHBLANK, IHT, IHWEB):
2801                     pass
2802                 elif iquad == IHSTAR: # Affect another star 
2803                     if withprob(0.05):
2804                         # This star supernovas 
2805                         supernova(False)
2806                         return
2807                     else:
2808                         hits.append(neighbor)
2809                         game.state.galaxy[game.quadrant.x][game.quadrant.y].stars -= 1
2810                         game.state.starkl += 1
2811                         crmena(True, IHSTAR, "sector", neighbor)
2812                         prout(_(" novas."))
2813                         game.quad[neighbor.x][neighbor.y] = IHDOT
2814                         kount += 1
2815                 elif iquad in (IHP, IHW): # Destroy planet 
2816                     game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = None
2817                     if iquad == IHP:
2818                         game.state.nplankl += 1
2819                     else:
2820                         game.state.worldkl += 1
2821                     crmena(True, iquad, "sector", neighbor)
2822                     prout(_(" destroyed."))
2823                     game.iplnet.pclass = "destroyed"
2824                     game.iplnet = None
2825                     invalidate(game.plnet)
2826                     if game.landed:
2827                         finish(FPNOVA)
2828                         return
2829                     game.quad[neighbor.x][neighbor.y] = IHDOT
2830                 elif iquad == IHB: # Destroy base 
2831                     game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase = False
2832                     for i in range(game.state.rembase):
2833                         if game.state.baseq[i] == game.quadrant: 
2834                             break
2835                     game.state.baseq[i] = game.state.baseq[game.state.rembase]
2836                     game.state.rembase -= 1
2837                     invalidate(game.base)
2838                     game.state.basekl += 1
2839                     newcnd()
2840                     crmena(True, IHB, "sector", neighbor)
2841                     prout(_(" destroyed."))
2842                     game.quad[neighbor.x][neighbor.y] = IHDOT
2843                 elif iquad in (IHE, IHF): # Buffet ship 
2844                     prout(_("***Starship buffeted by nova."))
2845                     if game.shldup:
2846                         if game.shield >= 2000.0:
2847                             game.shield -= 2000.0
2848                         else:
2849                             diff = 2000.0 - game.shield
2850                             game.energy -= diff
2851                             game.shield = 0.0
2852                             game.shldup = False
2853                             prout(_("***Shields knocked out."))
2854                             game.damage[DSHIELD] += 0.005*game.damfac*randreal()*diff
2855                     else:
2856                         game.energy -= 2000.0
2857                     if game.energy <= 0:
2858                         finish(FNOVA)
2859                         return
2860                     # add in course nova contributes to kicking starship
2861                     bump += (game.sector-hits[mm]).sgn()
2862                 elif iquad == IHK: # kill klingon 
2863                     deadkl(neighbor, iquad, neighbor)
2864                 elif iquad in (IHC,IHS,IHR): # Damage/destroy big enemies 
2865                     for ll in range(game.nenhere):
2866                         if game.enemies[ll].kloc == neighbor:
2867                             break
2868                     game.enemies[ll].kpower -= 800.0 # If firepower is lost, die 
2869                     if game.enemies[ll].kpower <= 0.0:
2870                         deadkl(neighbor, iquad, neighbor)
2871                         break
2872                     newc = neighbor + neighbor - hits[mm]
2873                     crmena(True, iquad, "sector", neighbor)
2874                     proutn(_(" damaged"))
2875                     if not VALID_SECTOR(newc.x, newc.y):
2876                         # can't leave quadrant 
2877                         skip(1)
2878                         break
2879                     iquad1 = game.quad[newc.x][newc.y]
2880                     if iquad1 == IHBLANK:
2881                         proutn(_(", blasted into "))
2882                         crmena(False, IHBLANK, "sector", newc)
2883                         skip(1)
2884                         deadkl(neighbor, iquad, newc)
2885                         break
2886                     if iquad1 != IHDOT:
2887                         # can't move into something else 
2888                         skip(1)
2889                         break
2890                     proutn(_(", buffeted to Sector %s") % newc)
2891                     game.quad[neighbor.x][neighbor.y] = IHDOT
2892                     game.quad[newc.x][newc.y] = iquad
2893                     game.enemies[ll].move(newc)
2894     # Starship affected by nova -- kick it away. 
2895     game.dist = kount*0.1
2896     game.direc = course[3*(bump.x+1)+bump.y+2]
2897     if game.direc == 0.0:
2898         game.dist = 0.0
2899     if game.dist == 0.0:
2900         return
2901     game.optime = 10.0*game.dist/16.0
2902     skip(1)
2903     prout(_("Force of nova displaces starship."))
2904     imove(novapush=True)
2905     game.optime = 10.0*game.dist/16.0
2906     return
2907         
2908 def supernova(induced, w=None):
2909     # star goes supernova 
2910     num = 0; npdead = 0
2911     nq = coord()
2912     if w != None: 
2913         nq = w
2914     else:
2915         stars = 0
2916         # Scheduled supernova -- select star 
2917         # logic changed here so that we won't favor quadrants in top
2918         # left of universe 
2919         for nq.x in range(GALSIZE):
2920             for nq.y in range(GALSIZE):
2921                 stars += game.state.galaxy[nq.x][nq.y].stars
2922         if stars == 0:
2923             return # nothing to supernova exists 
2924         num = randrange(stars) + 1
2925         for nq.x in range(GALSIZE):
2926             for nq.y in range(GALSIZE):
2927                 num -= game.state.galaxy[nq.x][nq.y].stars
2928                 if num <= 0:
2929                     break
2930             if num <=0:
2931                 break
2932         if idebug:
2933             proutn("=== Super nova here?")
2934             if ja() == True:
2935                 nq = game.quadrant
2936     if not nq == game.quadrant or game.justin:
2937         # it isn't here, or we just entered (treat as enroute) 
2938         if communicating():
2939             skip(1)
2940             prout(_("Message from Starfleet Command       Stardate %.2f") % game.state.date)
2941             prout(_("     Supernova in Quadrant %s; caution advised.") % nq)
2942     else:
2943         ns = coord()
2944         # we are in the quadrant! 
2945         num = randrange(game.state.galaxy[nq.x][nq.y].stars) + 1
2946         for ns.x in range(QUADSIZE):
2947             for ns.y in range(QUADSIZE):
2948                 if game.quad[ns.x][ns.y]==IHSTAR:
2949                     num -= 1
2950                     if num==0:
2951                         break
2952             if num==0:
2953                 break
2954         skip(1)
2955         prouts(_("***RED ALERT!  RED ALERT!"))
2956         skip(1)
2957         prout(_("***Incipient supernova detected at Sector %s") % ns)
2958         if square(ns.x-game.sector.x) + square(ns.y-game.sector.y) <= 2.1:
2959             proutn(_("Emergency override attempts t"))
2960             prouts("***************")
2961             skip(1)
2962             stars()
2963             game.alldone = True
2964
2965     # destroy any Klingons in supernovaed quadrant 
2966     kldead = game.state.galaxy[nq.x][nq.y].klingons
2967     game.state.galaxy[nq.x][nq.y].klingons = 0
2968     if nq == game.state.kscmdr:
2969         # did in the Supercommander! 
2970         game.state.nscrem = game.state.kscmdr.x = game.state.kscmdr.y = game.isatb =  0
2971         game.iscate = False
2972         unschedule(FSCMOVE)
2973         unschedule(FSCDBAS)
2974     if game.state.remcom:
2975         maxloop = game.state.remcom
2976         for l in range(maxloop):
2977             if game.state.kcmdr[l] == nq:
2978                 game.state.kcmdr[l] = game.state.kcmdr[game.state.remcom]
2979                 invalidate(game.state.kcmdr[game.state.remcom])
2980                 game.state.remcom -= 1
2981                 kldead -= 1
2982                 if game.state.remcom==0:
2983                     unschedule(FTBEAM)
2984                 break
2985     game.state.remkl -= kldead
2986     # destroy Romulans and planets in supernovaed quadrant 
2987     nrmdead = game.state.galaxy[nq.x][nq.y].romulans
2988     game.state.galaxy[nq.x][nq.y].romulans = 0
2989     game.state.nromrem -= nrmdead
2990     # Destroy planets 
2991     for loop in range(game.inplan):
2992         if game.state.planets[loop].w == nq:
2993             game.state.planets[loop].pclass = "destroyed"
2994             npdead += 1
2995     # Destroy any base in supernovaed quadrant 
2996     if game.state.rembase:
2997         maxloop = game.state.rembase
2998         for loop in range(maxloop):
2999             if game.state.baseq[loop] == nq:
3000                 game.state.baseq[loop] = game.state.baseq[game.state.rembase]
3001                 invalidate(game.state.baseq[game.state.rembase])
3002                 game.state.rembase -= 1
3003                 break
3004     # If starship caused supernova, tally up destruction 
3005     if induced:
3006         game.state.starkl += game.state.galaxy[nq.x][nq.y].stars
3007         game.state.basekl += game.state.galaxy[nq.x][nq.y].starbase
3008         game.state.nplankl += npdead
3009     # mark supernova in galaxy and in star chart 
3010     if game.quadrant == nq or communicating():
3011         game.state.galaxy[nq.x][nq.y].supernova = True
3012     # If supernova destroys last Klingons give special message 
3013     if (game.state.remkl + game.state.remcom + game.state.nscrem)==0 and not nq == game.quadrant:
3014         skip(2)
3015         if not induced:
3016             prout(_("Lucky you!"))
3017         proutn(_("A supernova in %s has just destroyed the last Klingons.") % nq)
3018         finish(FWON)
3019         return
3020     # if some Klingons remain, continue or die in supernova 
3021     if game.alldone:
3022         finish(FSNOVAED)
3023     return
3024
3025 # Code from finish.c ends here.
3026
3027 def selfdestruct():
3028     # self-destruct maneuver 
3029     # Finish with a BANG! 
3030     chew()
3031     if damaged(DCOMPTR):
3032         prout(_("Computer damaged; cannot execute destruct sequence."))
3033         return
3034     prouts(_("---WORKING---")); skip(1)
3035     prouts(_("SELF-DESTRUCT-SEQUENCE-ACTIVATED")); skip(1)
3036     prouts("   10"); skip(1)
3037     prouts("       9"); skip(1)
3038     prouts("          8"); skip(1)
3039     prouts("             7"); skip(1)
3040     prouts("                6"); skip(1)
3041     skip(1)
3042     prout(_("ENTER-CORRECT-PASSWORD-TO-CONTINUE-"))
3043     skip(1)
3044     prout(_("SELF-DESTRUCT-SEQUENCE-OTHERWISE-"))
3045     skip(1)
3046     prout(_("SELF-DESTRUCT-SEQUENCE-WILL-BE-ABORTED"))
3047     skip(1)
3048     scan()
3049     chew()
3050     if game.passwd != citem:
3051         prouts(_("PASSWORD-REJECTED;"))
3052         skip(1)
3053         prouts(_("CONTINUITY-EFFECTED"))
3054         skip(2)
3055         return
3056     prouts(_("PASSWORD-ACCEPTED")); skip(1)
3057     prouts("                   5"); skip(1)
3058     prouts("                      4"); skip(1)
3059     prouts("                         3"); skip(1)
3060     prouts("                            2"); skip(1)
3061     prouts("                              1"); skip(1)
3062     if withprob(0.15):
3063         prouts(_("GOODBYE-CRUEL-WORLD"))
3064         skip(1)
3065     kaboom()
3066
3067 def kaboom():
3068     stars()
3069     if game.ship==IHE:
3070         prouts("***")
3071     prouts(_("********* Entropy of "))
3072     crmshp()
3073     prouts(_(" maximized *********"))
3074     skip(1)
3075     stars()
3076     skip(1)
3077     if game.nenhere != 0:
3078         whammo = 25.0 * game.energy
3079         l=1
3080         while l <= game.nenhere:
3081             if game.enemies[l].kpower*game.enemies[l].kdist <= whammo: 
3082                 deadkl(game.enemies[l].kloc, game.quad[game.enemies[l].kloc.x][game.enemies[l].kloc.y], game.enemies[l].kloc)
3083             l += 1
3084     finish(FDILITHIUM)
3085                                 
3086 def killrate():
3087     "Compute our rate of kils over time."
3088     elapsed = game.state.date - game.indate
3089     if elapsed == 0:    # Avoid divide-by-zero error if calculated on turn 0
3090         return 0
3091     else:
3092         starting = (game.inkling + game.incom + game.inscom)
3093         remaining = (game.state.remkl + game.state.remcom + game.state.nscrem)
3094         return (starting - remaining)/elapsed
3095
3096 def badpoints():
3097     "Compute demerits."
3098     badpt = 5.0*game.state.starkl + \
3099             game.casual + \
3100             10.0*game.state.nplankl + \
3101             300*game.state.nworldkl + \
3102             45.0*game.nhelp +\
3103             100.0*game.state.basekl +\
3104             3.0*game.abandoned
3105     if game.ship == IHF:
3106         badpt += 100.0
3107     elif game.ship == None:
3108         badpt += 200.0
3109     return badpt
3110
3111 def finish(ifin):
3112     # end the game, with appropriate notfications 
3113     igotit = False
3114     game.alldone = True
3115     skip(3)
3116     prout(_("It is stardate %.1f.") % game.state.date)
3117     skip(1)
3118     if ifin == FWON: # Game has been won
3119         if game.state.nromrem != 0:
3120             prout(_("The remaining %d Romulans surrender to Starfleet Command.") %
3121                   game.state.nromrem)
3122
3123         prout(_("You have smashed the Klingon invasion fleet and saved"))
3124         prout(_("the Federation."))
3125         game.gamewon = True
3126         if game.alive:
3127             badpt = badpoints()
3128             if badpt < 100.0:
3129                 badpt = 0.0     # Close enough!
3130             # killsPerDate >= RateMax
3131             if game.state.date-game.indate < 5.0 or \
3132                 killrate() >= 0.1*game.skill*(game.skill+1.0) + 0.1 + 0.008*badpt:
3133                 skip(1)
3134                 prout(_("In fact, you have done so well that Starfleet Command"))
3135                 if game.skill == SKILL_NOVICE:
3136                     prout(_("promotes you one step in rank from \"Novice\" to \"Fair\"."))
3137                 elif game.skill == SKILL_FAIR:
3138                     prout(_("promotes you one step in rank from \"Fair\" to \"Good\"."))
3139                 elif game.skill == SKILL_GOOD:
3140                     prout(_("promotes you one step in rank from \"Good\" to \"Expert\"."))
3141                 elif game.skill == SKILL_EXPERT:
3142                     prout(_("promotes you to Commodore Emeritus."))
3143                     skip(1)
3144                     prout(_("Now that you think you're really good, try playing"))
3145                     prout(_("the \"Emeritus\" game. It will splatter your ego."))
3146                 elif game.skill == SKILL_EMERITUS:
3147                     skip(1)
3148                     proutn(_("Computer-  "))
3149                     prouts(_("ERROR-ERROR-ERROR-ERROR"))
3150                     skip(2)
3151                     prouts(_("  YOUR-SKILL-HAS-EXCEEDED-THE-CAPACITY-OF-THIS-PROGRAM"))
3152                     skip(1)
3153                     prouts(_("  THIS-PROGRAM-MUST-SURVIVE"))
3154                     skip(1)
3155                     prouts(_("  THIS-PROGRAM-MUST-SURVIVE"))
3156                     skip(1)
3157                     prouts(_("  THIS-PROGRAM-MUST-SURVIVE"))
3158                     skip(1)
3159                     prouts(_("  THIS-PROGRAM-MUST?- MUST ? - SUR? ? -?  VI"))
3160                     skip(2)
3161                     prout(_("Now you can retire and write your own Star Trek game!"))
3162                     skip(1)
3163                 elif game.skill >= SKILL_EXPERT:
3164                     if game.thawed and not idebug:
3165                         prout(_("You cannot get a citation, so..."))
3166                     else:
3167                         proutn(_("Do you want your Commodore Emeritus Citation printed? "))
3168                         chew()
3169                         if ja() == True:
3170                             igotit = True
3171             # Only grant long life if alive (original didn't!)
3172             skip(1)
3173             prout(_("LIVE LONG AND PROSPER."))
3174         score()
3175         if igotit:
3176             plaque()        
3177         return
3178     elif ifin == FDEPLETE: # Federation Resources Depleted
3179         prout(_("Your time has run out and the Federation has been"))
3180         prout(_("conquered.  Your starship is now Klingon property,"))
3181         prout(_("and you are put on trial as a war criminal.  On the"))
3182         proutn(_("basis of your record, you are "))
3183         if (game.state.remkl + game.state.remcom + game.state.nscrem)*3.0 > (game.inkling + game.incom + game.inscom):
3184             prout(_("acquitted."))
3185             skip(1)
3186             prout(_("LIVE LONG AND PROSPER."))
3187         else:
3188             prout(_("found guilty and"))
3189             prout(_("sentenced to death by slow torture."))
3190             game.alive = False
3191         score()
3192         return
3193     elif ifin == FLIFESUP:
3194         prout(_("Your life support reserves have run out, and"))
3195         prout(_("you die of thirst, starvation, and asphyxiation."))
3196         prout(_("Your starship is a derelict in space."))
3197     elif ifin == FNRG:
3198         prout(_("Your energy supply is exhausted."))
3199         skip(1)
3200         prout(_("Your starship is a derelict in space."))
3201     elif ifin == FBATTLE:
3202         proutn(_("The "))
3203         crmshp()
3204         prout(_(" has been destroyed in battle."))
3205         skip(1)
3206         prout(_("Dulce et decorum est pro patria mori."))
3207     elif ifin == FNEG3:
3208         prout(_("You have made three attempts to cross the negative energy"))
3209         prout(_("barrier which surrounds the galaxy."))
3210         skip(1)
3211         prout(_("Your navigation is abominable."))
3212         score()
3213     elif ifin == FNOVA:
3214         prout(_("Your starship has been destroyed by a nova."))
3215         prout(_("That was a great shot."))
3216         skip(1)
3217     elif ifin == FSNOVAED:
3218         proutn(_("The "))
3219         crmshp()
3220         prout(_(" has been fried by a supernova."))
3221         prout(_("...Not even cinders remain..."))
3222     elif ifin == FABANDN:
3223         prout(_("You have been captured by the Klingons. If you still"))
3224         prout(_("had a starbase to be returned to, you would have been"))
3225         prout(_("repatriated and given another chance. Since you have"))
3226         prout(_("no starbases, you will be mercilessly tortured to death."))
3227     elif ifin == FDILITHIUM:
3228         prout(_("Your starship is now an expanding cloud of subatomic particles"))
3229     elif ifin == FMATERIALIZE:
3230         prout(_("Starbase was unable to re-materialize your starship."))
3231         prout(_("Sic transit gloria mundi"))
3232     elif ifin == FPHASER:
3233         proutn(_("The "))
3234         crmshp()
3235         prout(_(" has been cremated by its own phasers."))
3236     elif ifin == FLOST:
3237         prout(_("You and your landing party have been"))
3238         prout(_("converted to energy, disipating through space."))
3239     elif ifin == FMINING:
3240         prout(_("You are left with your landing party on"))
3241         prout(_("a wild jungle planet inhabited by primitive cannibals."))
3242         skip(1)
3243         prout(_("They are very fond of \"Captain Kirk\" soup."))
3244         skip(1)
3245         proutn(_("Without your leadership, the "))
3246         crmshp()
3247         prout(_(" is destroyed."))
3248     elif ifin == FDPLANET:
3249         prout(_("You and your mining party perish."))
3250         skip(1)
3251         prout(_("That was a great shot."))
3252         skip(1)
3253     elif ifin == FSSC:
3254         prout(_("The Galileo is instantly annihilated by the supernova."))
3255         prout(_("You and your mining party are atomized."))
3256         skip(1)
3257         proutn(_("Mr. Spock takes command of the "))
3258         crmshp()
3259         prout(_(" and"))
3260         prout(_("joins the Romulans, reigning terror on the Federation."))
3261     elif ifin == FPNOVA:
3262         prout(_("You and your mining party are atomized."))
3263         skip(1)
3264         proutn(_("Mr. Spock takes command of the "))
3265         crmshp()
3266         prout(_(" and"))
3267         prout(_("joins the Romulans, reigning terror on the Federation."))
3268     elif ifin == FSTRACTOR:
3269         prout(_("The shuttle craft Galileo is also caught,"))
3270         prout(_("and breaks up under the strain."))
3271         skip(1)
3272         prout(_("Your debris is scattered for millions of miles."))
3273         proutn(_("Without your leadership, the "))
3274         crmshp()
3275         prout(_(" is destroyed."))
3276     elif ifin == FDRAY:
3277         prout(_("The mutants attack and kill Spock."))
3278         prout(_("Your ship is captured by Klingons, and"))
3279         prout(_("your crew is put on display in a Klingon zoo."))
3280     elif ifin == FTRIBBLE:
3281         prout(_("Tribbles consume all remaining water,"))
3282         prout(_("food, and oxygen on your ship."))
3283         skip(1)
3284         prout(_("You die of thirst, starvation, and asphyxiation."))
3285         prout(_("Your starship is a derelict in space."))
3286     elif ifin == FHOLE:
3287         prout(_("Your ship is drawn to the center of the black hole."))
3288         prout(_("You are crushed into extremely dense matter."))
3289     elif ifin == FCREW:
3290         prout(_("Your last crew member has died."))
3291     if game.ship == IHF:
3292         game.ship = None
3293     elif game.ship == IHE:
3294         game.ship = IHF
3295     game.alive = False
3296     if (game.state.remkl + game.state.remcom + game.state.nscrem) != 0:
3297         goodies = game.state.remres/game.inresor
3298         baddies = (game.state.remkl + 2.0*game.state.remcom)/(game.inkling+2.0*game.incom)
3299         if goodies/baddies >= randreal(1.0, 1.5):
3300             prout(_("As a result of your actions, a treaty with the Klingon"))
3301             prout(_("Empire has been signed. The terms of the treaty are"))
3302             if goodies/baddies >= randreal(3.0):
3303                 prout(_("favorable to the Federation."))
3304                 skip(1)
3305                 prout(_("Congratulations!"))
3306             else:
3307                 prout(_("highly unfavorable to the Federation."))
3308         else:
3309             prout(_("The Federation will be destroyed."))
3310     else:
3311         prout(_("Since you took the last Klingon with you, you are a"))
3312         prout(_("martyr and a hero. Someday maybe they'll erect a"))
3313         prout(_("statue in your memory. Rest in peace, and try not"))
3314         prout(_("to think about pigeons."))
3315         game.gamewon = True
3316     score()
3317
3318 def score():
3319     # compute player's score 
3320     timused = game.state.date - game.indate
3321     iskill = game.skill
3322     if (timused == 0 or (game.state.remkl + game.state.remcom + game.state.nscrem) != 0) and timused < 5.0:
3323         timused = 5.0
3324     perdate = killrate()
3325     ithperd = 500*perdate + 0.5
3326     iwon = 0
3327     if game.gamewon:
3328         iwon = 100*game.skill
3329     if game.ship == IHE: 
3330         klship = 0
3331     elif game.ship == IHF: 
3332         klship = 1
3333     else:
3334         klship = 2
3335     if not game.gamewon:
3336         game.state.nromrem = 0 # None captured if no win
3337     iscore = 10*(game.inkling - game.state.remkl) \
3338              + 50*(game.incom - game.state.remcom) \
3339              + ithperd + iwon \
3340              + 20*(game.inrom - game.state.nromrem) \
3341              + 200*(game.inscom - game.state.nscrem) \
3342              - game.state.nromrem \
3343              - badpoints()
3344     if not game.alive:
3345         iscore -= 200
3346     skip(2)
3347     prout(_("Your score --"))
3348     if game.inrom - game.state.nromrem:
3349         prout(_("%6d Romulans destroyed                 %5d") %
3350               (game.inrom - game.state.nromrem, 20*(game.inrom - game.state.nromrem)))
3351     if game.state.nromrem:
3352         prout(_("%6d Romulans captured                  %5d") %
3353               (game.state.nromrem, game.state.nromrem))
3354     if game.inkling - game.state.remkl:
3355         prout(_("%6d ordinary Klingons destroyed        %5d") %
3356               (game.inkling - game.state.remkl, 10*(game.inkling - game.state.remkl)))
3357     if game.incom - game.state.remcom:
3358         prout(_("%6d Klingon commanders destroyed       %5d") %
3359               (game.incom - game.state.remcom, 50*(game.incom - game.state.remcom)))
3360     if game.inscom - game.state.nscrem:
3361         prout(_("%6d Super-Commander destroyed          %5d") %
3362               (game.inscom - game.state.nscrem, 200*(game.inscom - game.state.nscrem)))
3363     if ithperd:
3364         prout(_("%6.2f Klingons per stardate              %5d") %
3365               (perdate, ithperd))
3366     if game.state.starkl:
3367         prout(_("%6d stars destroyed by your action     %5d") %
3368               (game.state.starkl, -5*game.state.starkl))
3369     if game.state.nplankl:
3370         prout(_("%6d planets destroyed by your action   %5d") %
3371               (game.state.nplankl, -10*game.state.nplankl))
3372     if (game.options & OPTION_WORLDS) and game.state.nworldkl:
3373         prout(_("%6d inhabited planets destroyed by your action   %5d") %
3374               (game.state.nplankl, -300*game.state.nworldkl))
3375     if game.state.basekl:
3376         prout(_("%6d bases destroyed by your action     %5d") %
3377               (game.state.basekl, -100*game.state.basekl))
3378     if game.nhelp:
3379         prout(_("%6d calls for help from starbase       %5d") %
3380               (game.nhelp, -45*game.nhelp))
3381     if game.casual:
3382         prout(_("%6d casualties incurred                %5d") %
3383               (game.casual, -game.casual))
3384     if game.abandoned:
3385         prout(_("%6d crew abandoned in space            %5d") %
3386               (game.abandoned, -3*game.abandoned))
3387     if klship:
3388         prout(_("%6d ship(s) lost or destroyed          %5d") %
3389               (klship, -100*klship))
3390     if not game.alive:
3391         prout(_("Penalty for getting yourself killed        -200"))
3392     if game.gamewon:
3393         proutn(_("Bonus for winning "))
3394         if game.skill   == SKILL_NOVICE:        proutn(_("Novice game  "))
3395         elif game.skill == SKILL_FAIR:          proutn(_("Fair game    "))
3396         elif game.skill ==  SKILL_GOOD:         proutn(_("Good game    "))
3397         elif game.skill ==  SKILL_EXPERT:       proutn(_("Expert game  "))
3398         elif game.skill ==  SKILL_EMERITUS:     proutn(_("Emeritus game"))
3399         prout("           %5d" % iwon)
3400     skip(1)
3401     prout(_("TOTAL SCORE                               %5d") % iscore)
3402
3403 def plaque():
3404     # emit winner's commemmorative plaque 
3405     skip(2)
3406     while True:
3407         proutn(_("File or device name for your plaque: "))
3408         winner = cgetline()
3409         try:
3410             fp = open(winner, "w")
3411             break
3412         except IOError:
3413             prout(_("Invalid name."))
3414
3415     proutn(_("Enter name to go on plaque (up to 30 characters): "))
3416     winner = cgetline()
3417     # The 38 below must be 64 for 132-column paper 
3418     nskip = 38 - len(winner)/2
3419     fp.write("\n\n\n\n")
3420     # --------DRAW ENTERPRISE PICTURE. 
3421     fp.write("                                       EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n" )
3422     fp.write("                                      EEE                      E  : :                                         :  E\n" )
3423     fp.write("                                    EE   EEE                   E  : :                   NCC-1701              :  E\n")
3424     fp.write("EEEEEEEEEEEEEEEE        EEEEEEEEEEEEEEE  : :                              : E\n")
3425     fp.write(" E                                     EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n")
3426     fp.write("                      EEEEEEEEE               EEEEEEEEEEEEE                 E  E\n")
3427     fp.write("                               EEEEEEE   EEEEE    E          E              E  E\n")
3428     fp.write("                                      EEE           E          E            E  E\n")
3429     fp.write("                                                       E         E          E  E\n")
3430     fp.write("                                                         EEEEEEEEEEEEE      E  E\n")
3431     fp.write("                                                      EEE :           EEEEEEE  EEEEEEEE\n")
3432     fp.write("                                                    :E    :                 EEEE       E\n")
3433     fp.write("                                                   .-E   -:-----                       E\n")
3434     fp.write("                                                    :E    :                            E\n")
3435     fp.write("                                                      EE  :                    EEEEEEEE\n")
3436     fp.write("                                                       EEEEEEEEEEEEEEEEEEEEEEE\n")
3437     fp.write("\n\n\n")
3438     fp.write(_("                                                       U. S. S. ENTERPRISE\n"))
3439     fp.write("\n\n\n\n")
3440     fp.write(_("                                  For demonstrating outstanding ability as a starship captain\n"))
3441     fp.write("\n")
3442     fp.write(_("                                                Starfleet Command bestows to you\n"))
3443     fp.write("\n")
3444     fp.write("%*s%s\n\n" % (nskip, "", winner))
3445     fp.write(_("                                                           the rank of\n\n"))
3446     fp.write(_("                                                       \"Commodore Emeritus\"\n\n"))
3447     fp.write("                                                          ")
3448     if game.skill ==  SKILL_EXPERT:
3449         fp.write(_(" Expert level\n\n"))
3450     elif game.skill == SKILL_EMERITUS:
3451         fp.write(_("Emeritus level\n\n"))
3452     else:
3453         fp.write(_(" Cheat level\n\n"))
3454     timestring = ctime()
3455     fp.write(_("                                                 This day of %.6s %.4s, %.8s\n\n") %
3456                     (timestring+4, timestring+20, timestring+11))
3457     fp.write(_("                                                        Your score:  %d\n\n") % iscore)
3458     fp.write(_("                                                    Klingons per stardate:  %.2f\n") % perdate)
3459     fp.close()
3460
3461 # Code from io.c begins here
3462
3463 rows = linecount = 0    # for paging 
3464 stdscr = None
3465 replayfp = None
3466 fullscreen_window = None
3467 srscan_window     = None
3468 report_window     = None
3469 status_window     = None
3470 lrscan_window     = None
3471 message_window    = None
3472 prompt_window     = None
3473 curwnd = None
3474
3475 def outro():
3476     "wrap up, either normally or due to signal"
3477     if game.options & OPTION_CURSES:
3478         #clear()
3479         #curs_set(1)
3480         #refresh()
3481         #resetterm()
3482         #echo()
3483         curses.endwin()
3484         sys.stdout.write('\n')
3485     if logfp:
3486         logfp.close()
3487
3488 def iostart():
3489     global stdscr, rows
3490     #setlocale(LC_ALL, "")
3491     #bindtextdomain(PACKAGE, LOCALEDIR)
3492     #textdomain(PACKAGE)
3493     if atexit.register(outro):
3494         sys.stderr.write("Unable to register outro(), exiting...\n")
3495         raise SysExit,1
3496     if not (game.options & OPTION_CURSES):
3497         ln_env = os.getenv("LINES")
3498         if ln_env:
3499             rows = ln_env
3500         else:
3501             rows = 25
3502     else:
3503         stdscr = curses.initscr()
3504         stdscr.keypad(True)
3505         #saveterm()
3506         curses.nonl()
3507         curses.cbreak()
3508         curses.start_color()
3509         curses.init_pair(curses.COLOR_BLACK, curses.COLOR_BLACK, curses.COLOR_BLACK)
3510         curses.init_pair(curses.COLOR_GREEN, curses.COLOR_GREEN, curses.COLOR_BLACK)
3511         curses.init_pair(curses.COLOR_RED, curses.COLOR_RED, curses.COLOR_BLACK)
3512         curses.init_pair(curses.COLOR_CYAN, curses.COLOR_CYAN, curses.COLOR_BLACK)
3513         curses.init_pair(curses.COLOR_WHITE, curses.COLOR_WHITE, curses.COLOR_BLACK)
3514         curses.init_pair(curses.COLOR_MAGENTA, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
3515         curses.init_pair(curses.COLOR_BLUE, curses.COLOR_BLUE, curses.COLOR_BLACK)
3516         curses.init_pair(curses.COLOR_YELLOW, curses.COLOR_YELLOW, curses.COLOR_BLACK)
3517         #noecho()
3518         global fullscreen_window, srscan_window, report_window, status_window
3519         global lrscan_window, message_window, prompt_window
3520         fullscreen_window = stdscr
3521         srscan_window     = curses.newwin(12, 25, 0,       0)
3522         report_window     = curses.newwin(11, 0,  1,       25)
3523         status_window     = curses.newwin(10, 0,  1,       39)
3524         lrscan_window     = curses.newwin(5,  0,  0,       64) 
3525         message_window    = curses.newwin(0,  0,  12,      0)
3526         prompt_window     = curses.newwin(1,  0,  rows-2,  0) 
3527         message_window.scrollok(True)
3528         setwnd(fullscreen_window)
3529         textcolor(DEFAULT)
3530
3531 def waitfor():
3532     "wait for user action -- OK to do nothing if on a TTY"
3533     if game.options & OPTION_CURSES:
3534         stsdcr.getch()
3535
3536 def announce():
3537     skip(1)
3538     if game.skill > SKILL_FAIR:
3539         prouts(_("[ANOUNCEMENT ARRIVING...]"))
3540     else:
3541         prouts(_("[IMPORTANT ANNOUNCEMENT ARRIVING -- PRESS ENTER TO CONTINUE]"))
3542     skip(1)
3543
3544 def pause_game():
3545     if game.skill > SKILL_FAIR:
3546         prompt = _("[CONTINUE?]")
3547     else:
3548         prompt = _("[PRESS ENTER TO CONTINUE]")
3549
3550     if game.options & OPTION_CURSES:
3551         drawmaps(0)
3552         setwnd(prompt_window)
3553         prompt_window.wclear()
3554         prompt_window.addstr(prompt)
3555         prompt_window.getstr()
3556         prompt_window.clear()
3557         prompt_window.refresh()
3558         setwnd(message_window)
3559     else:
3560         global linecount
3561         sys.stdout.write('\n')
3562         proutn(prompt)
3563         raw_input()
3564         for j in range(rows):
3565             sys.stdout.write('\n')
3566         linecount = 0
3567
3568 def skip(i):
3569     "Skip i lines.  Pause game if this would cause a scrolling event."
3570     for dummy in range(i):
3571         if game.options & OPTION_CURSES:
3572             (y, x) = curwnd.getyx()
3573             (my, mx) = curwnd.getmaxyx()
3574             if curwnd == message_window and y >= my - 3:
3575                 pause_game()
3576                 clrscr()
3577             else:
3578                 proutn("\n")
3579         else:
3580             global linecount
3581             linecount += 1
3582             if rows and linecount >= rows:
3583                 pause_game()
3584             else:
3585                 sys.stdout.write('\n')
3586
3587 def proutn(line):
3588     "Utter a line with no following line feed."
3589     if game.options & OPTION_CURSES:
3590         curwnd.addstr(line)
3591         curwnd.refresh()
3592     else:
3593         sys.stdout.write(line)
3594         sys.stdout.flush()
3595
3596 def prout(line):
3597     proutn(line)
3598     skip(1)
3599
3600 def prouts(line):
3601     "print slowly!" 
3602     for c in line:
3603         if not replayfp or replayfp.closed:     # Don't slow down replays
3604             time.sleep(0.03)
3605         proutn(c)
3606         if game.options & OPTION_CURSES:
3607             wrefresh(curwnd)
3608         else:
3609             sys.stdout.flush()
3610     if not replayfp or replayfp.closed:
3611         time.sleep(0.03)
3612
3613 def cgetline():
3614     "Get a line of input."
3615     if game.options & OPTION_CURSES:
3616         line = curwnd.getstr() + "\n"
3617         curwnd.refresh()
3618     else:
3619         if replayfp and not replayfp.closed:
3620             while True:
3621                 line = replayfp.readline()
3622                 proutn(line)
3623                 if line == '':
3624                     prout("*** Replay finished")
3625                     replayfp.close()
3626                     break
3627                 elif line[0] != "#":
3628                     break
3629         else:
3630             line = raw_input()
3631     if logfp:
3632         logfp.write("$" + line + "\n")
3633     return line
3634
3635 def setwnd(wnd):
3636     "Change windows -- OK for this to be a no-op in tty mode."
3637     global curwnd
3638     if game.options & OPTION_CURSES:
3639         curwnd = wnd
3640         curses.curs_set(wnd == fullscreen_window or wnd == message_window or wnd == prompt_window)
3641
3642 def clreol():
3643     "Clear to end of line -- can be a no-op in tty mode" 
3644     if game.options & OPTION_CURSES:
3645         wclrtoeol(curwnd)
3646         wrefresh(curwnd)
3647
3648 def clrscr():
3649     "Clear screen -- can be a no-op in tty mode."
3650     global linecount
3651     if game.options & OPTION_CURSES:
3652        curwnd.clear()
3653        curwnd.move(0, 0)
3654        curwnd.refresh()
3655     linecount = 0
3656
3657 def textcolor(color):
3658     "Set the current text color"
3659     if game.options & OPTION_CURSES:
3660         if color == DEFAULT: 
3661             curwnd.attrset(0)
3662         elif color == BLACK: 
3663             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_BLACK))
3664         elif color == BLUE: 
3665             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_BLUE))
3666         elif color == GREEN: 
3667             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_GREEN))
3668         elif color == CYAN: 
3669             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_CYAN))
3670         elif color == RED: 
3671             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_RED))
3672         elif color == MAGENTA: 
3673             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_MAGENTA))
3674         elif color == BROWN: 
3675             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_YELLOW))
3676         elif color == LIGHTGRAY: 
3677             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_WHITE))
3678         elif color == DARKGRAY: 
3679             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_BLACK) | curses.A_BOLD)
3680         elif color == LIGHTBLUE: 
3681             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_BLUE) | curses.A_BOLD)
3682         elif color == LIGHTGREEN: 
3683             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_GREEN) | curses.A_BOLD)
3684         elif color == LIGHTCYAN: 
3685             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_CYAN) | curses.A_BOLD)
3686         elif color == LIGHTRED: 
3687             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_RED) | curses.A_BOLD)
3688         elif color == LIGHTMAGENTA: 
3689             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_MAGENTA) | curses.A_BOLD)
3690         elif color == YELLOW: 
3691             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_YELLOW) | curses.A_BOLD)
3692         elif color == WHITE:
3693             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_WHITE) | curses.A_BOLD)
3694
3695 def highvideo():
3696     "Set highlight video, if this is reasonable."
3697     if game.options & OPTION_CURSES:
3698         curwnd.attron(curses.A_REVERSE)
3699  
3700 #
3701 # Things past this point have policy implications.
3702
3703
3704 def drawmaps(mode):
3705     "Hook to be called after moving to redraw maps."
3706     if game.options & OPTION_CURSES:
3707         if mode == 1:
3708             sensor()
3709         setwnd(srscan_window)
3710         curwnd.move(0, 0)
3711         srscan()
3712         if mode != 2:
3713             setwnd(status_window)
3714             status_window.clear()
3715             status_window.move(0, 0)
3716             setwnd(report_window)
3717             report_window.clear()
3718             report_window.move(0, 0)
3719             status()
3720             setwnd(lrscan_window)
3721             lrscan_window.clear()
3722             lrscan_window.move(0, 0)
3723             lrscan(silent=False)
3724
3725 def put_srscan_sym(w, sym):
3726     "Emit symbol for short-range scan."
3727     srscan_window.move(w.x+1, w.y*2+2)
3728     srscan_window.addch(sym)
3729     srscan_window.refresh()
3730
3731 def boom(w):
3732     "Enemy fall down, go boom."  
3733     if game.options & OPTION_CURSES:
3734         drawmaps(2)
3735         setwnd(srscan_window)
3736         srscan_window.attron(curses.A_REVERSE)
3737         put_srscan_sym(w, game.quad[w.x][w.y])
3738         #sound(500)
3739         #time.sleep(1.0)
3740         #nosound()
3741         srscan_window.attroff(curses.A_REVERSE)
3742         put_srscan_sym(w, game.quad[w.x][w.y])
3743         curses.delay_output(500)
3744         setwnd(message_window) 
3745
3746 def warble():
3747     "Sound and visual effects for teleportation."
3748     if game.options & OPTION_CURSES:
3749         drawmaps(2)
3750         setwnd(message_window)
3751         #sound(50)
3752     prouts("     . . . . .     ")
3753     if game.options & OPTION_CURSES:
3754         #curses.delay_output(1000)
3755         #nosound()
3756         pass
3757
3758 def tracktorpedo(w, l, i, n, iquad):
3759     "Torpedo-track animation." 
3760     if not game.options & OPTION_CURSES:
3761         if l == 1:
3762             if n != 1:
3763                 skip(1)
3764                 proutn(_("Track for torpedo number %d-  ") % i)
3765             else:
3766                 skip(1)
3767                 proutn(_("Torpedo track- "))
3768         elif l==4 or l==9: 
3769             skip(1)
3770         proutn("%d - %d   " % (w.x, w.y))
3771     else:
3772         if not damaged(DSRSENS) or game.condition=="docked":
3773             if i != 1 and l == 1:
3774                 drawmaps(2)
3775                 time.sleep(0.4)
3776             if (iquad==IHDOT) or (iquad==IHBLANK):
3777                 put_srscan_sym(w, '+')
3778                 #sound(l*10)
3779                 #time.sleep(0.1)
3780                 #nosound()
3781                 put_srscan_sym(w, iquad)
3782             else:
3783                 curwnd.attron(curses.A_REVERSE)
3784                 put_srscan_sym(w, iquad)
3785                 #sound(500)
3786                 #time.sleep(1.0)
3787                 #nosound()
3788                 curwnd.attroff(curses.A_REVERSE)
3789                 put_srscan_sym(w, iquad)
3790         else:
3791             proutn("%d - %d   " % (w.x, w.y))
3792
3793 def makechart():
3794     "Display the current galaxy chart."
3795     if game.options & OPTION_CURSES:
3796         setwnd(message_window)
3797         message_window.clear()
3798     chart()
3799     if game.options & OPTION_TTY:
3800         skip(1)
3801
3802 NSYM    = 14
3803
3804 def prstat(txt, data):
3805     proutn(txt)
3806     if game.options & OPTION_CURSES:
3807         skip(1)
3808         setwnd(status_window)
3809     else:
3810         proutn(" " * (NSYM - len(txt)))
3811     proutn(data)
3812     skip(1)
3813     if game.options & OPTION_CURSES:
3814         setwnd(report_window)
3815
3816 # Code from moving.c begins here
3817
3818 def imove(novapush):
3819     # movement execution for warp, impulse, supernova, and tractor-beam events 
3820     w = coord(); final = coord()
3821     trbeam = False
3822
3823     def no_quad_change():
3824         # No quadrant change -- compute new average enemy distances 
3825         game.quad[game.sector.x][game.sector.y] = game.ship
3826         if game.nenhere:
3827             for m in range(game.nenhere):
3828                 finald = distance(w, game.enemies[m].kloc)
3829                 game.enemies[m].kavgd = 0.5 * (finald+game.enemies[m].kdist)
3830                 game.enemies[m].kdist = finald
3831             game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
3832             if not game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
3833                 attack(False)
3834             for m in range(game.nenhere):
3835                 game.enemies[m].kavgd = game.enemies[m].kdist
3836         newcnd()
3837         drawmaps(0)
3838         setwnd(message_window)
3839     w.x = w.y = 0
3840     if game.inorbit:
3841         prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
3842         game.inorbit = False
3843     angle = ((15.0 - game.direc) * 0.5235988)
3844     deltax = -math.sin(angle)
3845     deltay = math.cos(angle)
3846     if math.fabs(deltax) > math.fabs(deltay):
3847         bigger = math.fabs(deltax)
3848     else:
3849         bigger = math.fabs(deltay)
3850     deltay /= bigger
3851     deltax /= bigger
3852     # If tractor beam is to occur, don't move full distance 
3853     if game.state.date+game.optime >= scheduled(FTBEAM):
3854         trbeam = True
3855         game.condition = "red"
3856         game.dist = game.dist*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
3857         game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
3858     # Move within the quadrant 
3859     game.quad[game.sector.x][game.sector.y] = IHDOT
3860     x = game.sector.x
3861     y = game.sector.y
3862     n = int(10.0*game.dist*bigger+0.5)
3863     if n > 0:
3864         for m in range(1, n+1):
3865             x += deltax
3866             y += deltay
3867             w.x = int(round(x))
3868             w.y = int(round(y))
3869             if not VALID_SECTOR(w.x, w.y):
3870                 # Leaving quadrant -- allow final enemy attack 
3871                 # Don't do it if being pushed by Nova 
3872                 if game.nenhere != 0 and not novapush:
3873                     newcnd()
3874                     for m in range(game.nenhere):
3875                         finald = distance(w, game.enemies[m].kloc)
3876                         game.enemies[m].kavgd = 0.5 * (finald + game.enemies[m].kdist)
3877                     #
3878                     # Stas Sergeev added the condition
3879                     # that attacks only happen if Klingons
3880                     # are present and your skill is good.
3881                     # 
3882                     if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
3883                         attack(False)
3884                     if game.alldone:
3885                         return
3886                 # compute final position -- new quadrant and sector 
3887                 x = (QUADSIZE*game.quadrant.x)+game.sector.x
3888                 y = (QUADSIZE*game.quadrant.y)+game.sector.y
3889                 w.x = int(round(x+10.0*game.dist*bigger*deltax))
3890                 w.y = int(round(y+10.0*game.dist*bigger*deltay))
3891                 # check for edge of galaxy 
3892                 kinks = 0
3893                 while True:
3894                     kink = False
3895                     if w.x < 0:
3896                         w.x = -w.x
3897                         kink = True
3898                     if w.y < 0:
3899                         w.y = -w.y
3900                         kink = True
3901                     if w.x >= GALSIZE*QUADSIZE:
3902                         w.x = (GALSIZE*QUADSIZE*2) - w.x
3903                         kink = True
3904                     if w.y >= GALSIZE*QUADSIZE:
3905                         w.y = (GALSIZE*QUADSIZE*2) - w.y
3906                         kink = True
3907                     if kink:
3908                         kinks += 1
3909                     else:
3910                         break
3911                 if kinks:
3912                     game.nkinks += 1
3913                     if game.nkinks == 3:
3914                         # Three strikes -- you're out! 
3915                         finish(FNEG3)
3916                         return
3917                     skip(1)
3918                     prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
3919                     prout(_("AT THE EDGE OF THE GALAXY.  THE THIRD TIME YOU TRY THIS,"))
3920                     prout(_("YOU WILL BE DESTROYED."))
3921                 # Compute final position in new quadrant 
3922                 if trbeam: # Don't bother if we are to be beamed 
3923                     return
3924                 game.quadrant.x = w.x/QUADSIZE
3925                 game.quadrant.y = w.y/QUADSIZE
3926                 game.sector.x = w.x - (QUADSIZE*game.quadrant.x)
3927                 game.sector.y = w.y - (QUADSIZE*game.quadrant.y)
3928                 skip(1)
3929                 prout(_("Entering Quadrant %s.") % game.quadrant)
3930                 game.quad[game.sector.x][game.sector.y] = game.ship
3931                 newqad(False)
3932                 if game.skill>SKILL_NOVICE:
3933                     attack(False)  
3934                 return
3935             iquad = game.quad[w.x][w.y]
3936             if iquad != IHDOT:
3937                 # object encountered in flight path 
3938                 stopegy = 50.0*game.dist/game.optime
3939                 game.dist = distance(game.sector, w) / (QUADSIZE * 1.0)
3940                 if iquad in (IHT, IHK, IHC, IHS, IHR, IHQUEST):
3941                     game.sector = w
3942                     ram(False, iquad, game.sector)
3943                     final = game.sector
3944                 elif iquad == IHBLANK:
3945                     skip(1)
3946                     prouts(_("***RED ALERT!  RED ALERT!"))
3947                     skip(1)
3948                     proutn("***")
3949                     crmshp()
3950                     proutn(_(" pulled into black hole at Sector %s") % w)
3951                     #
3952                     # Getting pulled into a black hole was certain
3953                     # death in Almy's original.  Stas Sergeev added a
3954                     # possibility that you'll get timewarped instead.
3955                     # 
3956                     n=0
3957                     for m in range(NDEVICES):
3958                         if game.damage[m]>0: 
3959                             n += 1
3960                     probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
3961                     if (game.options & OPTION_BLKHOLE) and withprob(1-probf): 
3962                         timwrp()
3963                     else: 
3964                         finish(FHOLE)
3965                     return
3966                 else:
3967                     # something else 
3968                     skip(1)
3969                     crmshp()
3970                     if iquad == IHWEB:
3971                         proutn(_(" encounters Tholian web at %s;") % w)
3972                     else:
3973                         proutn(_(" blocked by object at %s;") % w)
3974                     proutn(_("Emergency stop required "))
3975                     prout(_("%2d units of energy.") % int(stopegy))
3976                     game.energy -= stopegy
3977                     final.x = x-deltax+0.5
3978                     final.y = y-deltay+0.5
3979                     game.sector = final
3980                     if game.energy <= 0:
3981                         finish(FNRG)
3982                         return
3983                 # We're here!
3984                 no_quad_change()
3985                 return
3986         game.dist = distance(game.sector, w) / (QUADSIZE * 1.0)
3987         game.sector = w
3988     final = game.sector
3989     no_quad_change()
3990     return
3991
3992 def dock(verbose):
3993     # dock our ship at a starbase 
3994     chew()
3995     if game.condition == "docked" and verbose:
3996         prout(_("Already docked."))
3997         return
3998     if game.inorbit:
3999         prout(_("You must first leave standard orbit."))
4000         return
4001     if not is_valid(game.base) or abs(game.sector.x-game.base.x) > 1 or abs(game.sector.y-game.base.y) > 1:
4002         crmshp()
4003         prout(_(" not adjacent to base."))
4004         return
4005     game.condition = "docked"
4006     if "verbose":
4007         prout(_("Docked."))
4008     game.ididit = True
4009     if game.energy < game.inenrg:
4010         game.energy = game.inenrg
4011     game.shield = game.inshld
4012     game.torps = game.intorps
4013     game.lsupres = game.inlsr
4014     game.state.crew = FULLCREW
4015     if not damaged(DRADIO) and \
4016         ((is_scheduled(FCDBAS) or game.isatb == 1) and not game.iseenit):
4017         # get attack report from base 
4018         prout(_("Lt. Uhura- \"Captain, an important message from the starbase:\""))
4019         attackreport(False)
4020         game.iseenit = True
4021  
4022 # This program originally required input in terms of a (clock)
4023 # direction and distance. Somewhere in history, it was changed to
4024 # cartesian coordinates. So we need to convert.  Probably
4025 # "manual" input should still be done this way -- it's a real
4026 # pain if the computer isn't working! Manual mode is still confusing
4027 # because it involves giving x and y motions, yet the coordinates
4028 # are always displayed y - x, where +y is downward!
4029
4030 def getcourse(isprobe, akey):
4031     # get course and distance
4032     key = 0
4033     dquad = copy.copy(game.quadrant)
4034     navmode = "unspecified"
4035     itemp = "curt"
4036     dsect = coord()
4037     iprompt = False
4038     if game.landed and not isprobe:
4039         prout(_("Dummy! You can't leave standard orbit until you"))
4040         proutn(_("are back aboard the ship."))
4041         chew()
4042         return False
4043     while navmode == "unspecified":
4044         if damaged(DNAVSYS):
4045             if isprobe:
4046                 prout(_("Computer damaged; manual navigation only"))
4047             else:
4048                 prout(_("Computer damaged; manual movement only"))
4049             chew()
4050             navmode = "manual"
4051             key = IHEOL
4052             break
4053         if isprobe and akey != -1:
4054             # For probe launch, use pre-scanned value first time 
4055             key = akey
4056             akey = -1
4057         else: 
4058             key = scan()
4059         if key == IHEOL:
4060             proutn(_("Manual or automatic- "))
4061             iprompt = True
4062             chew()
4063         elif key == IHALPHA:
4064             if isit("manual"):
4065                 navmode = "manual"
4066                 key = scan()
4067                 break
4068             elif isit("automatic"):
4069                 navmode = "automatic"
4070                 key = scan()
4071                 break
4072             else:
4073                 huh()
4074                 chew()
4075                 return False
4076         else: # numeric 
4077             if isprobe:
4078                 prout(_("(Manual navigation assumed.)"))
4079             else:
4080                 prout(_("(Manual movement assumed.)"))
4081             navmode = "manual"
4082             break
4083     if navmode == "automatic":
4084         while key == IHEOL:
4085             if isprobe:
4086                 proutn(_("Target quadrant or quadrant&sector- "))
4087             else:
4088                 proutn(_("Destination sector or quadrant&sector- "))
4089             chew()
4090             iprompt = True
4091             key = scan()
4092         if key != IHREAL:
4093             huh()
4094             return False
4095         xi = int(round(aaitem))-1
4096         key = scan()
4097         if key != IHREAL:
4098             huh()
4099             return False
4100         xj = int(round(aaitem))-1
4101         key = scan()
4102         if key == IHREAL:
4103             # both quadrant and sector specified 
4104             xk = int(round(aaitem))-1
4105             key = scan()
4106             if key != IHREAL:
4107                 huh()
4108                 return False
4109             xl = int(round(aaitem))-1
4110             dquad.x = xi
4111             dquad.y = xj
4112             dsect.y = xk
4113             dsect.x = xl
4114         else:
4115             # only one pair of numbers was specified
4116             if isprobe:
4117                 # only quadrant specified -- go to center of dest quad 
4118                 dquad.x = xi
4119                 dquad.y = xj
4120                 dsect.y = dsect.x = 4   # preserves 1-origin behavior
4121             else:
4122                 # only sector specified
4123                 dsect.y = xi
4124                 dsect.x = xj
4125             itemp = "normal"
4126         if not VALID_QUADRANT(dquad.y,dquad.x) or not VALID_SECTOR(dsect.x,dsect.y):
4127             huh()
4128             return False
4129         skip(1)
4130         if not isprobe:
4131             if itemp > "curt":
4132                 if iprompt:
4133                     prout(_("Helmsman Sulu- \"Course locked in for Sector %s.\"") % dsect)
4134             else:
4135                 prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
4136         # the actual deltas get computed here
4137         deltax = dquad.y-game.quadrant.y + 0.1*(dsect.x-game.sector.y)
4138         deltay = game.quadrant.x-dquad.x + 0.1*(game.sector.x-dsect.y)
4139     else: # manual 
4140         while key == IHEOL:
4141             proutn(_("X and Y displacements- "))
4142             chew()
4143             iprompt = True
4144             key = scan()
4145         itemp = "verbose"
4146         if key != IHREAL:
4147             huh()
4148             return False
4149         deltax = aaitem
4150         key = scan()
4151         if key != IHREAL:
4152             huh()
4153             return False
4154         deltay = aaitem
4155     # Check for zero movement 
4156     if deltax == 0 and deltay == 0:
4157         chew()
4158         return False
4159     if itemp == "verbose" and not isprobe:
4160         skip(1)
4161         prout(_("Helmsman Sulu- \"Aye, Sir.\""))
4162     # Course actually laid in.
4163     game.dist = math.sqrt(deltax*deltax + deltay*deltay)
4164     game.direc = math.atan2(deltax, deltay)*1.90985932
4165     if game.direc < 0.0:
4166         game.direc += 12.0
4167     chew()
4168     return True
4169
4170 def impulse():
4171     # move under impulse power 
4172     game.ididit = False
4173     if damaged(DIMPULS):
4174         chew()
4175         skip(1)
4176         prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
4177         return
4178     if game.energy > 30.0:
4179         if not getcourse(isprobe=False, akey=0):
4180             return
4181         power = 20.0 + 100.0*game.dist
4182     else:
4183         power = 30.0
4184     if power >= game.energy:
4185         # Insufficient power for trip 
4186         skip(1)
4187         prout(_("First Officer Spock- \"Captain, the impulse engines"))
4188         prout(_("require 20.0 units to engage, plus 100.0 units per"))
4189         if game.energy > 30:
4190             proutn(_("quadrant.  We can go, therefore, a maximum of %d") %
4191                      int(0.01 * (game.energy-20.0)-0.05))
4192             prout(_(" quadrants.\""))
4193         else:
4194             prout(_("quadrant.  They are, therefore, useless.\""))
4195         chew()
4196         return
4197     # Make sure enough time is left for the trip 
4198     game.optime = game.dist/0.095
4199     if game.optime >= game.state.remtime:
4200         prout(_("First Officer Spock- \"Captain, our speed under impulse"))
4201         prout(_("power is only 0.95 sectors per stardate. Are you sure"))
4202         proutn(_("we dare spend the time?\" "))
4203         if ja() == False:
4204             return
4205     # Activate impulse engines and pay the cost 
4206     imove(novapush=False)
4207     game.ididit = True
4208     if game.alldone:
4209         return
4210     power = 20.0 + 100.0*game.dist
4211     game.energy -= power
4212     game.optime = game.dist/0.095
4213     if game.energy <= 0:
4214         finish(FNRG)
4215     return
4216
4217 def warp(timewarp):
4218     # move under warp drive 
4219     blooey = False; twarp = False
4220     if not timewarp: # Not WARPX entry 
4221         game.ididit = False
4222         if game.damage[DWARPEN] > 10.0:
4223             chew()
4224             skip(1)
4225             prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
4226             return
4227         if damaged(DWARPEN) and game.warpfac > 4.0:
4228             chew()
4229             skip(1)
4230             prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
4231             prout(_("  is repaired, I can only give you warp 4.\""))
4232             return
4233         # Read in course and distance 
4234         if not getcourse(isprobe=False, akey=0):
4235             return
4236         # Make sure starship has enough energy for the trip 
4237         power = (game.dist+0.05)*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
4238         if power >= game.energy:
4239             # Insufficient power for trip 
4240             game.ididit = False
4241             skip(1)
4242             prout(_("Engineering to bridge--"))
4243             if not game.shldup or 0.5*power > game.energy:
4244                 iwarp = math.pow((game.energy/(game.dist+0.05)), 0.333333333)
4245                 if iwarp <= 0:
4246                     prout(_("We can't do it, Captain. We don't have enough energy."))
4247                 else:
4248                     proutn(_("We don't have enough energy, but we could do it at warp %d") % iwarp)
4249                     if game.shldup:
4250                         prout(",")
4251                         prout(_("if you'll lower the shields."))
4252                     else:
4253                         prout(".")
4254             else:
4255                 prout(_("We haven't the energy to go that far with the shields up."))
4256             return
4257                                                 
4258         # Make sure enough time is left for the trip 
4259         game.optime = 10.0*game.dist/game.wfacsq
4260         if game.optime >= 0.8*game.state.remtime:
4261             skip(1)
4262             prout(_("First Officer Spock- \"Captain, I compute that such"))
4263             proutn(_("  a trip would require approximately %2.0f") %
4264                    (100.0*game.optime/game.state.remtime))
4265             prout(_(" percent of our"))
4266             proutn(_("  remaining time.  Are you sure this is wise?\" "))
4267             if ja() == False:
4268                 game.ididit = False
4269                 game.optime=0 
4270                 return
4271     # Entry WARPX 
4272     if game.warpfac > 6.0:
4273         # Decide if engine damage will occur
4274         # ESR: Seems wrong. Probability of damage goes *down* with distance? 
4275         prob = game.dist*square(6.0-game.warpfac)/66.666666666
4276         if prob > randreal():
4277             blooey = True
4278             game.dist = randreal(game.dist)
4279         # Decide if time warp will occur 
4280         if 0.5*game.dist*math.pow(7.0,game.warpfac-10.0) > randreal():
4281             twarp = True
4282         if idebug and game.warpfac==10 and not twarp:
4283             blooey = False
4284             proutn("=== Force time warp? ")
4285             if ja() == True:
4286                 twarp = True
4287         if blooey or twarp:
4288             # If time warp or engine damage, check path 
4289             # If it is obstructed, don't do warp or damage 
4290             angle = ((15.0-game.direc)*0.5235998)
4291             deltax = -math.sin(angle)
4292             deltay = math.cos(angle)
4293             if math.fabs(deltax) > math.fabs(deltay):
4294                 bigger = math.fabs(deltax)
4295             else:
4296                 bigger = math.fabs(deltay)
4297                         
4298             deltax /= bigger
4299             deltay /= bigger
4300             n = 10.0 * game.dist * bigger +0.5
4301             x = game.sector.x
4302             y = game.sector.y
4303             for l in range(1, n+1):
4304                 x += deltax
4305                 ix = x + 0.5
4306                 y += deltay
4307                 iy = y +0.5
4308                 if not VALID_SECTOR(ix, iy):
4309                     break
4310                 if game.quad[ix][iy] != IHDOT:
4311                     blooey = False
4312                     twarp = False
4313     # Activate Warp Engines and pay the cost 
4314     imove(novapush=False)
4315     if game.alldone:
4316         return
4317     game.energy -= game.dist*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
4318     if game.energy <= 0:
4319         finish(FNRG)
4320     game.optime = 10.0*game.dist/game.wfacsq
4321     if twarp:
4322         timwrp()
4323     if blooey:
4324         game.damage[DWARPEN] = game.damfac * randreal(1.0, 4.0)
4325         skip(1)
4326         prout(_("Engineering to bridge--"))
4327         prout(_("  Scott here.  The warp engines are damaged."))
4328         prout(_("  We'll have to reduce speed to warp 4."))
4329     game.ididit = True
4330     return
4331
4332 def setwarp():
4333     # change the warp factor    
4334     while True:
4335         key=scan()
4336         if key != IHEOL:
4337             break
4338         chew()
4339         proutn(_("Warp factor- "))
4340     chew()
4341     if key != IHREAL:
4342         huh()
4343         return
4344     if game.damage[DWARPEN] > 10.0:
4345         prout(_("Warp engines inoperative."))
4346         return
4347     if damaged(DWARPEN) and aaitem > 4.0:
4348         prout(_("Engineer Scott- \"I'm doing my best, Captain,"))
4349         prout(_("  but right now we can only go warp 4.\""))
4350         return
4351     if aaitem > 10.0:
4352         prout(_("Helmsman Sulu- \"Our top speed is warp 10, Captain.\""))
4353         return
4354     if aaitem < 1.0:
4355         prout(_("Helmsman Sulu- \"We can't go below warp 1, Captain.\""))
4356         return
4357     oldfac = game.warpfac
4358     game.warpfac = aaitem
4359     game.wfacsq=game.warpfac*game.warpfac
4360     if game.warpfac <= oldfac or game.warpfac <= 6.0:
4361         prout(_("Helmsman Sulu- \"Warp factor %d, Captain.\"") %
4362                int(game.warpfac))
4363         return
4364     if game.warpfac < 8.00:
4365         prout(_("Engineer Scott- \"Aye, but our maximum safe speed is warp 6.\""))
4366         return
4367     if game.warpfac == 10.0:
4368         prout(_("Engineer Scott- \"Aye, Captain, we'll try it.\""))
4369         return
4370     prout(_("Engineer Scott- \"Aye, Captain, but our engines may not take it.\""))
4371     return
4372
4373 def atover(igrab):
4374     # cope with being tossed out of quadrant by supernova or yanked by beam 
4375     chew()
4376     # is captain on planet? 
4377     if game.landed:
4378         if damaged(DTRANSP):
4379             finish(FPNOVA)
4380             return
4381         prout(_("Scotty rushes to the transporter controls."))
4382         if game.shldup:
4383             prout(_("But with the shields up it's hopeless."))
4384             finish(FPNOVA)
4385         prouts(_("His desperate attempt to rescue you . . ."))
4386         if withprob(0.5):
4387             prout(_("fails."))
4388             finish(FPNOVA)
4389             return
4390         prout(_("SUCCEEDS!"))
4391         if game.imine:
4392             game.imine = False
4393             proutn(_("The crystals mined were "))
4394             if withprob(0.25):
4395                 prout(_("lost."))
4396             else:
4397                 prout(_("saved."))
4398                 game.icrystl = True
4399     if igrab:
4400         return
4401     # Check to see if captain in shuttle craft 
4402     if game.icraft:
4403         finish(FSTRACTOR)
4404     if game.alldone:
4405         return
4406     # Inform captain of attempt to reach safety 
4407     skip(1)
4408     while True:
4409         if game.justin:
4410             prouts(_("***RED ALERT!  RED ALERT!"))
4411             skip(1)
4412             proutn(_("The "))
4413             crmshp()
4414             prout(_(" has stopped in a quadrant containing"))
4415             prouts(_("   a supernova."))
4416             skip(2)
4417         proutn(_("***Emergency automatic override attempts to hurl "))
4418         crmshp()
4419         skip(1)
4420         prout(_("safely out of quadrant."))
4421         if not damaged(DRADIO):
4422             game.state.galaxy[game.quadrant.x][game.quadrant.y].charted = True
4423         # Try to use warp engines 
4424         if damaged(DWARPEN):
4425             skip(1)
4426             prout(_("Warp engines damaged."))
4427             finish(FSNOVAED)
4428             return
4429         game.warpfac = randreal(6.0, 8.0)
4430         game.wfacsq = game.warpfac * game.warpfac
4431         prout(_("Warp factor set to %d") % int(game.warpfac))
4432         power = 0.75*game.energy
4433         game.dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1))
4434         distreq = randreal(math.sqrt(2))
4435         if distreq < game.dist:
4436             game.dist = distreq
4437         game.optime = 10.0*game.dist/game.wfacsq
4438         game.direc = randreal(12)       # How dumb! 
4439         game.justin = False
4440         game.inorbit = False
4441         warp(True)
4442         if not game.justin:
4443             # This is bad news, we didn't leave quadrant. 
4444             if game.alldone:
4445                 return
4446             skip(1)
4447             prout(_("Insufficient energy to leave quadrant."))
4448             finish(FSNOVAED)
4449             return
4450         # Repeat if another snova
4451         if not game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
4452             break
4453     if (game.state.remkl + game.state.remcom + game.state.nscrem)==0: 
4454         finish(FWON) # Snova killed remaining enemy. 
4455
4456 def timwrp():
4457     # let's do the time warp again 
4458     prout(_("***TIME WARP ENTERED."))
4459     if game.state.snap and withprob(0.5):
4460         # Go back in time 
4461         prout(_("You are traveling backwards in time %d stardates.") %
4462               int(game.state.date-game.snapsht.date))
4463         game.state = game.snapsht
4464         game.state.snap = False
4465         if game.state.remcom:
4466             schedule(FTBEAM, expran(game.intime/game.state.remcom))
4467             schedule(FBATTAK, expran(0.3*game.intime))
4468         schedule(FSNOVA, expran(0.5*game.intime))
4469         # next snapshot will be sooner 
4470         schedule(FSNAP, expran(0.25*game.state.remtime))
4471                                 
4472         if game.state.nscrem:
4473             schedule(FSCMOVE, 0.2777)       
4474         game.isatb = 0
4475         unschedule(FCDBAS)
4476         unschedule(FSCDBAS)
4477         invalidate(game.battle)
4478
4479         # Make sure Galileo is consistant -- Snapshot may have been taken
4480         # when on planet, which would give us two Galileos! 
4481         gotit = False
4482         for l in range(game.inplan):
4483             if game.state.planets[l].known == "shuttle_down":
4484                 gotit = True
4485                 if game.iscraft == "onship" and game.ship==IHE:
4486                     prout(_("Chekov-  \"Security reports the Galileo has disappeared, Sir!"))
4487                     game.iscraft = "offship"
4488         # Likewise, if in the original time the Galileo was abandoned, but
4489         # was on ship earlier, it would have vanished -- let's restore it.
4490         if game.iscraft == "offship" and not gotit and game.damage[DSHUTTL] >= 0.0:
4491             prout(_("Checkov-  \"Security reports the Galileo has reappeared in the dock!\""))
4492             game.iscraft = "onship"
4493         # 
4494 #        * There used to be code to do the actual reconstrction here,
4495 #        * but the starchart is now part of the snapshotted galaxy state.
4496 #        
4497         prout(_("Spock has reconstructed a correct star chart from memory"))
4498     else:
4499         # Go forward in time 
4500         game.optime = -0.5*game.intime*math.log(randreal())
4501         prout(_("You are traveling forward in time %d stardates.") % int(game.optime))
4502         # cheat to make sure no tractor beams occur during time warp 
4503         postpone(FTBEAM, game.optime)
4504         game.damage[DRADIO] += game.optime
4505     newqad(False)
4506     events()    # Stas Sergeev added this -- do pending events 
4507
4508 def probe():
4509     # launch deep-space probe 
4510     # New code to launch a deep space probe 
4511     if game.nprobes == 0:
4512         chew()
4513         skip(1)
4514         if game.ship == IHE: 
4515             prout(_("Engineer Scott- \"We have no more deep space probes, Sir.\""))
4516         else:
4517             prout(_("Ye Faerie Queene has no deep space probes."))
4518         return
4519     if damaged(DDSP):
4520         chew()
4521         skip(1)
4522         prout(_("Engineer Scott- \"The probe launcher is damaged, Sir.\""))
4523         return
4524     if is_scheduled(FDSPROB):
4525         chew()
4526         skip(1)
4527         if damaged(DRADIO) and game.condition != "docked":
4528             prout(_("Spock-  \"Records show the previous probe has not yet"))
4529             prout(_("   reached its destination.\""))
4530         else:
4531             prout(_("Uhura- \"The previous probe is still reporting data, Sir.\""))
4532         return
4533     key = scan()
4534     if key == IHEOL:
4535         # slow mode, so let Kirk know how many probes there are left
4536         if game.nprobes == 1:
4537             prout(_("1 probe left."))
4538         else:
4539             prout(_("%d probes left") % game.nprobes)
4540         proutn(_("Are you sure you want to fire a probe? "))
4541         if ja() == False:
4542             return
4543     game.isarmed = False
4544     if key == IHALPHA and citem == "armed":
4545         game.isarmed = True
4546         key = scan()
4547     elif key == IHEOL:
4548         proutn(_("Arm NOVAMAX warhead? "))
4549         game.isarmed = ja()
4550     if not getcourse(isprobe=True, akey=key):
4551         return
4552     game.nprobes -= 1
4553     angle = ((15.0 - game.direc) * 0.5235988)
4554     game.probeinx = -math.sin(angle)
4555     game.probeiny = math.cos(angle)
4556     if math.fabs(game.probeinx) > math.fabs(game.probeiny):
4557         bigger = math.fabs(game.probeinx)
4558     else:
4559         bigger = math.fabs(game.probeiny)
4560     game.probeiny /= bigger
4561     game.probeinx /= bigger
4562     game.proben = 10.0*game.dist*bigger +0.5
4563     game.probex = game.quadrant.x*QUADSIZE + game.sector.x - 1  # We will use better packing than original
4564     game.probey = game.quadrant.y*QUADSIZE + game.sector.y - 1
4565     game.probec = game.quadrant
4566     schedule(FDSPROB, 0.01) # Time to move one sector
4567     prout(_("Ensign Chekov-  \"The deep space probe is launched, Captain.\""))
4568     game.ididit = True
4569     return
4570
4571 # Here's how the mayday code works:
4572
4573 # First, the closest starbase is selected.  If there is a a starbase
4574 # in your own quadrant, you are in good shape.  This distance takes
4575 # quadrant distances into account only.
4576 #
4577 # A magic number is computed based on the distance which acts as the
4578 # probability that you will be rematerialized.  You get three tries.
4579 #
4580 # When it is determined that you should be able to be rematerialized
4581 # (i.e., when the probability thing mentioned above comes up
4582 # positive), you are put into that quadrant (anywhere).  Then, we try
4583 # to see if there is a spot adjacent to the star- base.  If not, you
4584 # can't be rematerialized!!!  Otherwise, it drops you there.  It only
4585 # tries five times to find a spot to drop you.  After that, it's your
4586 # problem.
4587
4588 def mayday():
4589     # yell for help from nearest starbase 
4590     # There's more than one way to move in this game! 
4591     line = 0
4592     chew()
4593     # Test for conditions which prevent calling for help 
4594     if game.condition == "docked":
4595         prout(_("Lt. Uhura-  \"But Captain, we're already docked.\""))
4596         return
4597     if damaged(DRADIO):
4598         prout(_("Subspace radio damaged."))
4599         return
4600     if game.state.rembase==0:
4601         prout(_("Lt. Uhura-  \"Captain, I'm not getting any response from Starbase.\""))
4602         return
4603     if game.landed:
4604         proutn(_("You must be aboard the "))
4605         crmshp()
4606         prout(".")
4607         return
4608     # OK -- call for help from nearest starbase 
4609     game.nhelp += 1
4610     if game.base.x!=0:
4611         # There's one in this quadrant 
4612         ddist = distance(game.base, game.sector)
4613     else:
4614         ddist = FOREVER
4615         for m in range(game.state.rembase):
4616             xdist = QUADSIZE * distance(game.state.baseq[m], game.quadrant)
4617             if xdist < ddist:
4618                 ddist = xdist
4619                 line = m
4620         # Since starbase not in quadrant, set up new quadrant 
4621         game.quadrant = game.state.baseq[line]
4622         newqad(True)
4623     # dematerialize starship 
4624     game.quad[game.sector.x][game.sector.y]=IHDOT
4625     proutn(_("Starbase in Quadrant %s responds--") % game.quadrant)
4626     crmshp()
4627     prout(_(" dematerializes."))
4628     game.sector.x=0
4629     for m in range(1, 5+1):
4630         w = game.base.scatter() 
4631         if VALID_SECTOR(ix,iy) and game.quad[ix][iy]==IHDOT:
4632             # found one -- finish up 
4633             game.sector = w
4634             break
4635     if not is_valid(game.sector):
4636         prout(_("You have been lost in space..."))
4637         finish(FMATERIALIZE)
4638         return
4639     # Give starbase three chances to rematerialize starship 
4640     probf = math.pow((1.0 - math.pow(0.98,ddist)), 0.33333333)
4641     for m in range(1, 3+1):
4642         if m == 1: proutn(_("1st"))
4643         elif m == 2: proutn(_("2nd"))
4644         elif m == 3: proutn(_("3rd"))
4645         proutn(_(" attempt to re-materialize "))
4646         crmshp()
4647         game.quad[ix][iy]=(IHMATER0,IHMATER1,IHMATER2)[m-1]
4648         textcolor(RED)
4649         warble()
4650         if randreal() > probf:
4651             break
4652         prout(_("fails."))
4653         curses.delay_output(500)
4654         textcolor(DEFAULT)
4655     if m > 3:
4656         game.quad[ix][iy]=IHQUEST
4657         game.alive = False
4658         drawmaps(1)
4659         setwnd(message_window)
4660         finish(FMATERIALIZE)
4661         return
4662     game.quad[ix][iy]=game.ship
4663     textcolor(GREEN)
4664     prout(_("succeeds."))
4665     textcolor(DEFAULT)
4666     dock(False)
4667     skip(1)
4668     prout(_("Lt. Uhura-  \"Captain, we made it!\""))
4669
4670 # Abandon Ship (the BSD-Trek description)
4671
4672 # The ship is abandoned.  If your current ship is the Faire
4673 # Queene, or if your shuttlecraft is dead, you're out of
4674 # luck.  You need the shuttlecraft in order for the captain
4675 # (that's you!!) to escape.
4676
4677 # Your crew can beam to an inhabited starsystem in the
4678 # quadrant, if there is one and if the transporter is working.
4679 # If there is no inhabited starsystem, or if the transporter
4680 # is out, they are left to die in outer space.
4681
4682 # If there are no starbases left, you are captured by the
4683 # Klingons, who torture you mercilessly.  However, if there
4684 # is at least one starbase, you are returned to the
4685 # Federation in a prisoner of war exchange.  Of course, this
4686 # can't happen unless you have taken some prisoners.
4687
4688 def abandon():
4689     # abandon ship 
4690     chew()
4691     if game.condition=="docked":
4692         if game.ship!=IHE:
4693             prout(_("You cannot abandon Ye Faerie Queene."))
4694             return
4695     else:
4696         # Must take shuttle craft to exit 
4697         if game.damage[DSHUTTL]==-1:
4698             prout(_("Ye Faerie Queene has no shuttle craft."))
4699             return
4700         if game.damage[DSHUTTL]<0:
4701             prout(_("Shuttle craft now serving Big Macs."))
4702             return
4703         if game.damage[DSHUTTL]>0:
4704             prout(_("Shuttle craft damaged."))
4705             return
4706         if game.landed:
4707             prout(_("You must be aboard the ship."))
4708             return
4709         if game.iscraft != "onship":
4710             prout(_("Shuttle craft not currently available."))
4711             return
4712         # Print abandon ship messages 
4713         skip(1)
4714         prouts(_("***ABANDON SHIP!  ABANDON SHIP!"))
4715         skip(1)
4716         prouts(_("***ALL HANDS ABANDON SHIP!"))
4717         skip(2)
4718         prout(_("Captain and crew escape in shuttle craft."))
4719         if game.state.rembase==0:
4720             # Oops! no place to go... 
4721             finish(FABANDN)
4722             return
4723         q = game.state.galaxy[game.quadrant.x][game.quadrant.y]
4724         # Dispose of crew 
4725         if not (game.options & OPTION_WORLDS) and not damaged(DTRANSP):
4726             prout(_("Remainder of ship's complement beam down"))
4727             prout(_("to nearest habitable planet."))
4728         elif q.planet != None and not damaged(DTRANSP):
4729             prout(_("Remainder of ship's complement beam down to %s.") %
4730                     q.planet)
4731         else:
4732             prout(_("Entire crew of %d left to die in outer space.") %
4733                     game.state.crew)
4734             game.casual += game.state.crew
4735             game.abandoned += game.state.crew
4736
4737         # If at least one base left, give 'em the Faerie Queene 
4738         skip(1)
4739         game.icrystl = False # crystals are lost 
4740         game.nprobes = 0 # No probes 
4741         prout(_("You are captured by Klingons and released to"))
4742         prout(_("the Federation in a prisoner-of-war exchange."))
4743         nb = randrange(game.state.rembase)
4744         # Set up quadrant and position FQ adjacient to base 
4745         if not game.quadrant == game.state.baseq[nb]:
4746             game.quadrant = game.state.baseq[nb]
4747             game.sector.x = game.sector.y = 5
4748             newqad(True)
4749         while True:
4750             # position next to base by trial and error 
4751             game.quad[game.sector.x][game.sector.y] = IHDOT
4752             for l in range(QUADSIZE):
4753                 game.sector = game.base.scatter()
4754                 if VALID_SECTOR(game.sector.x, game.sector.y) and \
4755                        game.quad[game.sector.x][game.sector.y] == IHDOT:
4756                     break
4757             if l < QUADSIZE+1:
4758                 break # found a spot 
4759             game.sector.x=QUADSIZE/2
4760             game.sector.y=QUADSIZE/2
4761             newqad(True)
4762     # Get new commission 
4763     game.quad[game.sector.x][game.sector.y] = game.ship = IHF
4764     game.state.crew = FULLCREW
4765     prout(_("Starfleet puts you in command of another ship,"))
4766     prout(_("the Faerie Queene, which is antiquated but,"))
4767     prout(_("still useable."))
4768     if game.icrystl:
4769         prout(_("The dilithium crystals have been moved."))
4770     game.imine = False
4771     game.iscraft = "offship" # Galileo disappears 
4772     # Resupply ship 
4773     game.condition="docked"
4774     for l in range(NDEVICES): 
4775         game.damage[l] = 0.0
4776     game.damage[DSHUTTL] = -1
4777     game.energy = game.inenrg = 3000.0
4778     game.shield = game.inshld = 1250.0
4779     game.torps = game.intorps = 6
4780     game.lsupres=game.inlsr=3.0
4781     game.shldup=False
4782     game.warpfac=5.0
4783     game.wfacsq=25.0
4784     return
4785
4786 # Code from planets.c begins here.
4787
4788 def consumeTime():
4789     # abort a lengthy operation if an event interrupts it 
4790     game.ididit = True
4791     events()
4792     if game.alldone or game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova or game.justin: 
4793         return True
4794     return False
4795
4796 def survey():
4797     # report on (uninhabited) planets in the galaxy 
4798     iknow = False
4799     skip(1)
4800     chew()
4801     prout(_("Spock-  \"Planet report follows, Captain.\""))
4802     skip(1)
4803     for i in range(game.inplan):
4804         if game.state.planets[i].pclass == "destroyed":
4805             continue
4806         if (game.state.planets[i].known != "unknown" \
4807             and not game.state.planets[i].inhabited) \
4808             or idebug:
4809             iknow = True
4810             if idebug and game.state.planets[i].known=="unknown":
4811                 proutn("(Unknown) ")
4812             proutn(_("Quadrant %s") % game.state.planets[i].w)
4813             proutn(_("   class "))
4814             proutn(game.state.planets[i].pclass)
4815             proutn("   ")
4816             if game.state.planets[i].crystals != present:
4817                 proutn(_("no "))
4818             prout(_("dilithium crystals present."))
4819             if game.state.planets[i].known=="shuttle_down": 
4820                 prout(_("    Shuttle Craft Galileo on surface."))
4821     if not iknow:
4822         prout(_("No information available."))
4823
4824 def orbit():
4825     # enter standard orbit 
4826     skip(1)
4827     chew()
4828     if game.inorbit:
4829         prout(_("Already in standard orbit."))
4830         return
4831     if damaged(DWARPEN) and damaged(DIMPULS):
4832         prout(_("Both warp and impulse engines damaged."))
4833         return
4834     if not is_valid(game.plnet) or abs(game.sector.x-game.plnet.x) > 1 or abs(game.sector.y-game.plnet.y) > 1:
4835         crmshp()
4836         prout(_(" not adjacent to planet."))
4837         skip(1)
4838         return
4839     game.optime = randreal(0.02, 0.05)
4840     prout(_("Helmsman Sulu-  \"Entering standard orbit, Sir.\""))
4841     newcnd()
4842     if consumeTime():
4843         return
4844     game.height = randreal(1400, 8600)
4845     prout(_("Sulu-  \"Entered orbit at altitude %.2f kilometers.\"") % game.height)
4846     game.inorbit = True
4847     game.ididit = True
4848
4849 def sensor():
4850     # examine planets in this quadrant 
4851     if damaged(DSRSENS):
4852         if game.options & OPTION_TTY:
4853             prout(_("Short range sensors damaged."))
4854         return
4855     if game.iplnet == None:
4856         if game.options & OPTION_TTY:
4857             prout(_("Spock- \"No planet in this quadrant, Captain.\""))
4858         return
4859     if game.iplnet.known == "unknown":
4860         prout(_("Spock-  \"Sensor scan for Quadrant %s-") % game.quadrant)
4861         skip(1)
4862         prout(_("         Planet at Sector %s is of class %s.") %
4863               (game.plnet, game.iplnet.pclass))
4864         if game.iplnet.known=="shuttle_down": 
4865             prout(_("         Sensors show Galileo still on surface."))
4866         proutn(_("         Readings indicate"))
4867         if game.iplnet.crystals != "present":
4868             proutn(_(" no"))
4869         prout(_(" dilithium crystals present.\""))
4870         if game.iplnet.known == "unknown":
4871             game.iplnet.known = "known"
4872     elif game.iplnet.inhabited:
4873         prout(_("Spock-  \"The inhabited planet %s ") % game.iplnet.name)
4874         prout(_("        is located at Sector %s, Captain.\"") % game.plnet)
4875
4876 def beam():
4877     # use the transporter 
4878     nrgneed = 0
4879     chew()
4880     skip(1)
4881     if damaged(DTRANSP):
4882         prout(_("Transporter damaged."))
4883         if not damaged(DSHUTTL) and (game.iplnet.known=="shuttle_down" or game.iscraft == "onship"):
4884             skip(1)
4885             proutn(_("Spock-  \"May I suggest the shuttle craft, Sir?\" "))
4886             if ja() == True:
4887                 shuttle()
4888         return
4889     if not game.inorbit:
4890         crmshp()
4891         prout(_(" not in standard orbit."))
4892         return
4893     if game.shldup:
4894         prout(_("Impossible to transport through shields."))
4895         return
4896     if game.iplnet.known=="unknown":
4897         prout(_("Spock-  \"Captain, we have no information on this planet"))
4898         prout(_("  and Starfleet Regulations clearly state that in this situation"))
4899         prout(_("  you may not go down.\""))
4900         return
4901     if not game.landed and game.iplnet.crystals=="absent":
4902         prout(_("Spock-  \"Captain, I fail to see the logic in"))
4903         prout(_("  exploring a planet with no dilithium crystals."))
4904         proutn(_("  Are you sure this is wise?\" "))
4905         if ja() == False:
4906             chew()
4907             return
4908     if not (game.options & OPTION_PLAIN):
4909         nrgneed = 50 * game.skill + game.height / 100.0
4910         if nrgneed > game.energy:
4911             prout(_("Engineering to bridge--"))
4912             prout(_("  Captain, we don't have enough energy for transportation."))
4913             return
4914         if not game.landed and nrgneed * 2 > game.energy:
4915             prout(_("Engineering to bridge--"))
4916             prout(_("  Captain, we have enough energy only to transport you down to"))
4917             prout(_("  the planet, but there wouldn't be an energy for the trip back."))
4918             if game.iplnet.known == "shuttle_down":
4919                 prout(_("  Although the Galileo shuttle craft may still be on a surface."))
4920             proutn(_("  Are you sure this is wise?\" "))
4921             if ja() == False:
4922                 chew()
4923                 return
4924     if game.landed:
4925         # Coming from planet 
4926         if game.iplnet.known=="shuttle_down":
4927             proutn(_("Spock-  \"Wouldn't you rather take the Galileo?\" "))
4928             if ja() == True:
4929                 chew()
4930                 return
4931             prout(_("Your crew hides the Galileo to prevent capture by aliens."))
4932         prout(_("Landing party assembled, ready to beam up."))
4933         skip(1)
4934         prout(_("Kirk whips out communicator..."))
4935         prouts(_("BEEP  BEEP  BEEP"))
4936         skip(2)
4937         prout(_("\"Kirk to enterprise-  Lock on coordinates...energize.\""))
4938     else:
4939         # Going to planet 
4940         prout(_("Scotty-  \"Transporter room ready, Sir.\""))
4941         skip(1)
4942         prout(_("Kirk and landing party prepare to beam down to planet surface."))
4943         skip(1)
4944         prout(_("Kirk-  \"Energize.\""))
4945     game.ididit = True
4946     skip(1)
4947     prouts("WWHOOOIIIIIRRRRREEEE.E.E.  .  .  .  .   .    .")
4948     skip(2)
4949     if withprob(0.98):
4950         prouts("BOOOIIIOOOIIOOOOIIIOIING . . .")
4951         skip(2)
4952         prout(_("Scotty-  \"Oh my God!  I've lost them.\""))
4953         finish(FLOST)
4954         return
4955     prouts(".    .   .  .  .  .  .E.E.EEEERRRRRIIIIIOOOHWW")
4956     game.landed = not game.landed
4957     game.energy -= nrgneed
4958     skip(2)
4959     prout(_("Transport complete."))
4960     if game.landed and game.iplnet.known=="shuttle_down":
4961         prout(_("The shuttle craft Galileo is here!"))
4962     if not game.landed and game.imine:
4963         game.icrystl = True
4964         game.cryprob = 0.05
4965     game.imine = False
4966     return
4967
4968 def mine():
4969     # strip-mine a world for dilithium 
4970     skip(1)
4971     chew()
4972     if not game.landed:
4973         prout(_("Mining party not on planet."))
4974         return
4975     if game.iplnet.crystals == "mined":
4976         prout(_("This planet has already been strip-mined for dilithium."))
4977         return
4978     elif game.iplnet.crystals == "absent":
4979         prout(_("No dilithium crystals on this planet."))
4980         return
4981     if game.imine:
4982         prout(_("You've already mined enough crystals for this trip."))
4983         return
4984     if game.icrystl and game.cryprob == 0.05:
4985         proutn(_("With all those fresh crystals aboard the "))
4986         crmshp()
4987         skip(1)
4988         prout(_("there's no reason to mine more at this time."))
4989         return
4990     game.optime = randreal(0.1, 0.3)*(ord(game.iplnet.pclass)-ord("L"))
4991     if consumeTime():
4992         return
4993     prout(_("Mining operation complete."))
4994     game.iplnet.crystals = "mined"
4995     game.imine = game.ididit = True
4996
4997 def usecrystals():
4998     # use dilithium crystals 
4999     game.ididit = False
5000     skip(1)
5001     chew()
5002     if not game.icrystl:
5003         prout(_("No dilithium crystals available."))
5004         return
5005     if game.energy >= 1000:
5006         prout(_("Spock-  \"Captain, Starfleet Regulations prohibit such an operation"))
5007         prout(_("  except when Condition Yellow exists."))
5008         return
5009     prout(_("Spock- \"Captain, I must warn you that loading"))
5010     prout(_("  raw dilithium crystals into the ship's power"))
5011     prout(_("  system may risk a severe explosion."))
5012     proutn(_("  Are you sure this is wise?\" "))
5013     if ja() == False:
5014         chew()
5015         return
5016     skip(1)
5017     prout(_("Engineering Officer Scott-  \"(GULP) Aye Sir."))
5018     prout(_("  Mr. Spock and I will try it.\""))
5019     skip(1)
5020     prout(_("Spock-  \"Crystals in place, Sir."))
5021     prout(_("  Ready to activate circuit.\""))
5022     skip(1)
5023     prouts(_("Scotty-  \"Keep your fingers crossed, Sir!\""))
5024     skip(1)
5025     if with(game.cryprob):
5026         prouts(_("  \"Activating now! - - No good!  It's***"))
5027         skip(2)
5028         prouts(_("***RED ALERT!  RED A*L********************************"))
5029         skip(1)
5030         stars()
5031         prouts(_("******************   KA-BOOM!!!!   *******************"))
5032         skip(1)
5033         kaboom()
5034         return
5035     game.energy += randreal(5000.0, 5500.0)
5036     prouts(_("  \"Activating now! - - "))
5037     prout(_("The instruments"))
5038     prout(_("   are going crazy, but I think it's"))
5039     prout(_("   going to work!!  Congratulations, Sir!\""))
5040     game.cryprob *= 2.0
5041     game.ididit = True
5042
5043 def shuttle():
5044     # use shuttlecraft for planetary jaunt 
5045     chew()
5046     skip(1)
5047     if damaged(DSHUTTL):
5048         if game.damage[DSHUTTL] == -1.0:
5049             if game.inorbit and game.iplnet.known == "shuttle_down":
5050                 prout(_("Ye Faerie Queene has no shuttle craft bay to dock it at."))
5051             else:
5052                 prout(_("Ye Faerie Queene had no shuttle craft."))
5053         elif game.damage[DSHUTTL] > 0:
5054             prout(_("The Galileo is damaged."))
5055         else: # game.damage[DSHUTTL] < 0  
5056             prout(_("Shuttle craft is now serving Big Macs."))
5057         return
5058     if not game.inorbit:
5059         crmshp()
5060         prout(_(" not in standard orbit."))
5061         return
5062     if (game.iplnet.known != "shuttle_down") and game.iscraft != "onship":
5063         prout(_("Shuttle craft not currently available."))
5064         return
5065     if not game.landed and game.iplnet.known=="shuttle_down":
5066         prout(_("You will have to beam down to retrieve the shuttle craft."))
5067         return
5068     if game.shldup or game.condition == "docked":
5069         prout(_("Shuttle craft cannot pass through shields."))
5070         return
5071     if game.iplnet.known=="unknown":
5072         prout(_("Spock-  \"Captain, we have no information on this planet"))
5073         prout(_("  and Starfleet Regulations clearly state that in this situation"))
5074         prout(_("  you may not fly down.\""))
5075         return
5076     game.optime = 3.0e-5*game.height
5077     if game.optime >= 0.8*game.state.remtime:
5078         prout(_("First Officer Spock-  \"Captain, I compute that such"))
5079         proutn(_("  a maneuver would require approximately %2d%% of our") % \
5080                int(100*game.optime/game.state.remtime))
5081         prout(_("remaining time."))
5082         proutn(_("Are you sure this is wise?\" "))
5083         if ja() == False:
5084             game.optime = 0.0
5085             return
5086     if game.landed:
5087         # Kirk on planet 
5088         if game.iscraft == "onship":
5089             # Galileo on ship! 
5090             if not damaged(DTRANSP):
5091                 proutn(_("Spock-  \"Would you rather use the transporter?\" "))
5092                 if ja() == True:
5093                     beam()
5094                     return
5095                 proutn(_("Shuttle crew"))
5096             else:
5097                 proutn(_("Rescue party"))
5098             prout(_(" boards Galileo and swoops toward planet surface."))
5099             game.iscraft = "offship"
5100             skip(1)
5101             if consumeTime():
5102                 return
5103             game.iplnet.known="shuttle_down"
5104             prout(_("Trip complete."))
5105             return
5106         else:
5107             # Ready to go back to ship 
5108             prout(_("You and your mining party board the"))
5109             prout(_("shuttle craft for the trip back to the Enterprise."))
5110             skip(1)
5111             prouts(_("The short hop begins . . ."))
5112             skip(1)
5113             game.iplnet.known="known"
5114             game.icraft = True
5115             skip(1)
5116             game.landed = False
5117             if consumeTime():
5118                 return
5119             game.iscraft = "onship"
5120             game.icraft = False
5121             if game.imine:
5122                 game.icrystl = True
5123                 game.cryprob = 0.05
5124             game.imine = False
5125             prout(_("Trip complete."))
5126             return
5127     else:
5128         # Kirk on ship 
5129         # and so is Galileo 
5130         prout(_("Mining party assembles in the hangar deck,"))
5131         prout(_("ready to board the shuttle craft \"Galileo\"."))
5132         skip(1)
5133         prouts(_("The hangar doors open; the trip begins."))
5134         skip(1)
5135         game.icraft = True
5136         game.iscraft = "offship"
5137         if consumeTime():
5138             return
5139         game.iplnet.known = "shuttle_down"
5140         game.landed = True
5141         game.icraft = False
5142         prout(_("Trip complete."))
5143         return
5144
5145 def deathray():
5146     # use the big zapper 
5147     game.ididit = False
5148     skip(1)
5149     chew()
5150     if game.ship != IHE:
5151         prout(_("Ye Faerie Queene has no death ray."))
5152         return
5153     if game.nenhere==0:
5154         prout(_("Sulu-  \"But Sir, there are no enemies in this quadrant.\""))
5155         return
5156     if damaged(DDRAY):
5157         prout(_("Death Ray is damaged."))
5158         return
5159     prout(_("Spock-  \"Captain, the 'Experimental Death Ray'"))
5160     prout(_("  is highly unpredictible.  Considering the alternatives,"))
5161     proutn(_("  are you sure this is wise?\" "))
5162     if ja() == False:
5163         return
5164     prout(_("Spock-  \"Acknowledged.\""))
5165     skip(1)
5166     game.ididit = True
5167     prouts(_("WHOOEE ... WHOOEE ... WHOOEE ... WHOOEE"))
5168     skip(1)
5169     prout(_("Crew scrambles in emergency preparation."))
5170     prout(_("Spock and Scotty ready the death ray and"))
5171     prout(_("prepare to channel all ship's power to the device."))
5172     skip(1)
5173     prout(_("Spock-  \"Preparations complete, sir.\""))
5174     prout(_("Kirk-  \"Engage!\""))
5175     skip(1)
5176     prouts(_("WHIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"))
5177     skip(1)
5178     dprob = 0.30
5179     if game.options & OPTION_PLAIN:
5180         dprob = 0.5
5181     r = randreal()
5182     if r > dprob:
5183         prouts(_("Sulu- \"Captain!  It's working!\""))
5184         skip(2)
5185         while game.nenhere > 0:
5186             deadkl(game.enemies[1].kloc, game.quad[game.enemies[1].kloc.x][game.enemies[1].kloc.y],game.enemies[1].kloc)
5187         prout(_("Ensign Chekov-  \"Congratulations, Captain!\""))
5188         if (game.state.remkl + game.state.remcom + game.state.nscrem) == 0:
5189             finish(FWON)    
5190         if (game.options & OPTION_PLAIN) == 0:
5191             prout(_("Spock-  \"Captain, I believe the `Experimental Death Ray'"))
5192             if withprob(0.05):
5193                 prout(_("   is still operational.\""))
5194             else:
5195                 prout(_("   has been rendered nonfunctional.\""))
5196                 game.damage[DDRAY] = 39.95
5197         return
5198     r = randreal()      # Pick failure method 
5199     if r <= 0.30:
5200         prouts(_("Sulu- \"Captain!  It's working!\""))
5201         skip(1)
5202         prouts(_("***RED ALERT!  RED ALERT!"))
5203         skip(1)
5204         prout(_("***MATTER-ANTIMATTER IMPLOSION IMMINENT!"))
5205         skip(1)
5206         prouts(_("***RED ALERT!  RED A*L********************************"))
5207         skip(1)
5208         stars()
5209         prouts(_("******************   KA-BOOM!!!!   *******************"))
5210         skip(1)
5211         kaboom()
5212         return
5213     if r <= 0.55:
5214         prouts(_("Sulu- \"Captain!  Yagabandaghangrapl, brachriigringlanbla!\""))
5215         skip(1)
5216         prout(_("Lt. Uhura-  \"Graaeek!  Graaeek!\""))
5217         skip(1)
5218         prout(_("Spock-  \"Fascinating!  . . . All humans aboard"))
5219         prout(_("  have apparently been transformed into strange mutations."))
5220         prout(_("  Vulcans do not seem to be affected."))
5221         skip(1)
5222         prout(_("Kirk-  \"Raauch!  Raauch!\""))
5223         finish(FDRAY)
5224         return
5225     if r <= 0.75:
5226         intj
5227         prouts(_("Sulu- \"Captain!  It's   --WHAT?!?!\""))
5228         skip(2)
5229         proutn(_("Spock-  \"I believe the word is"))
5230         prouts(_(" *ASTONISHING*"))
5231         prout(_(" Mr. Sulu."))
5232         for i in range(QUADSIZE):
5233             for j in range(QUADSIZE):
5234                 if game.quad[i][j] == IHDOT:
5235                     game.quad[i][j] = IHQUEST
5236         prout(_("  Captain, our quadrant is now infested with"))
5237         prouts(_(" - - - - - -  *THINGS*."))
5238         skip(1)
5239         prout(_("  I have no logical explanation.\""))
5240         return
5241     prouts(_("Sulu- \"Captain!  The Death Ray is creating tribbles!\""))
5242     skip(1)
5243     prout(_("Scotty-  \"There are so many tribbles down here"))
5244     prout(_("  in Engineering, we can't move for 'em, Captain.\""))
5245     finish(FTRIBBLE)
5246     return
5247
5248 # Code from reports.c begins here
5249
5250 def attackreport(curt):
5251     # report status of bases under attack 
5252     if not curt:
5253         if is_scheduled(FCDBAS):
5254             prout(_("Starbase in Quadrant %s is currently under Commander attack.") % game.battle)
5255             prout(_("It can hold out until Stardate %d.") % int(scheduled(FCDBAS)))
5256         elif game.isatb == 1:
5257             prout(_("Starbase in Quadrant %s is under Super-commander attack.") % game.state.kscmdr)
5258             prout(_("It can hold out until Stardate %d.") % int(scheduled(FSCDBAS)))
5259         else:
5260             prout(_("No Starbase is currently under attack."))
5261     else:
5262         if is_scheduled(FCDBAS):
5263             proutn(_("Base in %s attacked by C. Alive until %.1f") % (game.battle, scheduled(FCDBAS)))
5264         if game.isatb:
5265             proutn(_("Base in %s attacked by S. Alive until %.1f") % (game.state.kscmdr, scheduled(FSCDBAS)))
5266         clreol()
5267
5268 def report():
5269     # report on general game status 
5270     chew()
5271     s1 = "" and game.thawed and _("thawed ")
5272     s2 = {1:"short", 2:"medium", 4:"long"}[game.length]
5273     s3 = (None, _("novice"). _("fair"),
5274           _("good"), _("expert"), _("emeritus"))[game.skill]
5275     prout(_("You %s a %s%s %s game.") % ((_("were playing"), _("are playing"))[game.alldone], s1, s2, s3))
5276     if game.skill>SKILL_GOOD and game.thawed and not game.alldone:
5277         prout(_("No plaque is allowed."))
5278     if game.tourn:
5279         prout(_("This is tournament game %d.") % game.tourn)
5280     prout(_("Your secret password is \"%s\"") % game.passwd)
5281     proutn(_("%d of %d Klingons have been killed") % (((game.inkling + game.incom + game.inscom) - (game.state.remkl + game.state.remcom + game.state.nscrem)), 
5282            (game.inkling + game.incom + game.inscom)))
5283     if game.incom - game.state.remcom:
5284         prout(_(", including %d Commander%s.") % (game.incom - game.state.remcom, (_("s"), "")[(game.incom - game.state.remcom)==1]))
5285     elif game.inkling - game.state.remkl + (game.inscom - game.state.nscrem) > 0:
5286         prout(_(", but no Commanders."))
5287     else:
5288         prout(".")
5289     if game.skill > SKILL_FAIR:
5290         prout(_("The Super Commander has %sbeen destroyed.") % ("", _("not "))[game.state.nscrem])
5291     if game.state.rembase != game.inbase:
5292         proutn(_("There "))
5293         if game.inbase-game.state.rembase==1:
5294             proutn(_("has been 1 base"))
5295         else:
5296             proutn(_("have been %d bases") % (game.inbase-game.state.rembase))
5297         prout(_(" destroyed, %d remaining.") % game.state.rembase)
5298     else:
5299         prout(_("There are %d bases.") % game.inbase)
5300     if communicating() or game.iseenit:
5301         # Don't report this if not seen and
5302         # either the radio is dead or not at base!
5303         attackreport(False)
5304         game.iseenit = True
5305     if game.casual: 
5306         prout(_("%d casualt%s suffered so far.") % (game.casual, ("y", "ies")[game.casual!=1]))
5307     if game.nhelp:
5308         prout(_("There were %d call%s for help.") % (game.nhelp,  ("" , _("s"))[game.nhelp!=1]))
5309     if game.ship == IHE:
5310         proutn(_("You have "))
5311         if game.nprobes:
5312             proutn("%d" % (game.nprobes))
5313         else:
5314             proutn(_("no"))
5315         proutn(_(" deep space probe"))
5316         if game.nprobes!=1:
5317             proutn(_("s"))
5318         prout(".")
5319     if communicating() and is_scheduled(FDSPROB):
5320         if game.isarmed: 
5321             proutn(_("An armed deep space probe is in "))
5322         else:
5323             proutn(_("A deep space probe is in "))
5324         prout("Quadrant %s." % game.probec)
5325     if game.icrystl:
5326         if game.cryprob <= .05:
5327             prout(_("Dilithium crystals aboard ship... not yet used."))
5328         else:
5329             i=0
5330             ai = 0.05
5331             while game.cryprob > ai:
5332                 ai *= 2.0
5333                 i += 1
5334             prout(_("Dilithium crystals have been used %d time%s.") % \
5335                   (i, (_("s"), "")[i==1]))
5336     skip(1)
5337         
5338 def lrscan(silent):
5339     # long-range sensor scan 
5340     if damaged(DLRSENS):
5341         # Now allow base's sensors if docked 
5342         if game.condition != "docked":
5343             if not silent:
5344                 prout(_("LONG-RANGE SENSORS DAMAGED."))
5345             return
5346         if not silent:
5347             prout(_("Starbase's long-range scan"))
5348     elif not silent:
5349         prout(_("Long-range scan"))
5350     for x in range(game.quadrant.x-1, game.quadrant.x+2):
5351         if not silent:
5352             proutn(" ")
5353         for y in range(game.quadrant.y-1, game.quadrant.y+2):
5354             if not VALID_QUADRANT(x, y):
5355                 if not silent:
5356                     proutn("  -1")
5357             else:
5358                 if not damaged(DRADIO):
5359                     game.state.galaxy[x][y].charted = True
5360                 game.state.chart[x][y].klingons = game.state.galaxy[x][y].klingons
5361                 game.state.chart[x][y].starbase = game.state.galaxy[x][y].starbase
5362                 game.state.chart[x][y].stars = game.state.galaxy[x][y].stars
5363                 if not silent and game.state.galaxy[x][y].supernova: 
5364                     proutn(" ***")
5365                 elif not silent:
5366                     proutn(" %3d" % (game.state.chart[x][y].klingons*100 + game.state.chart[x][y].starbase * 10 + game.state.chart[x][y].stars))
5367         prout(" ")
5368
5369 def damagereport():
5370     # damage report 
5371     jdam = False
5372     chew()
5373
5374     for i in range(NDEVICES):
5375         if damaged(i):
5376             if not jdam:
5377                 prout(_("\tDEVICE\t\t\t-REPAIR TIMES-"))
5378                 prout(_("\t\t\tIN FLIGHT\t\tDOCKED"))
5379                 jdam = True
5380             prout("  %-26s\t%8.2f\t\t%8.2f" % (device[i],
5381                                                game.damage[i]+0.05,
5382                                                game.docfac*game.damage[i]+0.005))
5383     if not jdam:
5384         prout(_("All devices functional."))
5385
5386 def rechart():
5387     # update the chart in the Enterprise's computer from galaxy data 
5388     game.lastchart = game.state.date
5389     for i in range(GALSIZE):
5390         for j in range(GALSIZE):
5391             if game.state.galaxy[i][j].charted:
5392                 game.state.chart[i][j].klingons = game.state.galaxy[i][j].klingons
5393                 game.state.chart[i][j].starbase = game.state.galaxy[i][j].starbase
5394                 game.state.chart[i][j].stars = game.state.galaxy[i][j].stars
5395
5396 def chart():
5397     # display the star chart  
5398     chew()
5399     if (game.options & OPTION_AUTOSCAN):
5400         lrscan(silent=True)
5401     if not damaged(DRADIO):
5402         rechart()
5403     if game.lastchart < game.state.date and game.condition == "docked":
5404         prout(_("Spock-  \"I revised the Star Chart from the starbase's records.\""))
5405         rechart()
5406
5407     prout(_("       STAR CHART FOR THE KNOWN GALAXY"))
5408     if game.state.date > game.lastchart:
5409         prout(_("(Last surveillance update %d stardates ago).") % ((int)(game.state.date-game.lastchart)))
5410     prout("      1    2    3    4    5    6    7    8")
5411     for i in range(GALSIZE):
5412         proutn("%d |" % (i+1))
5413         for j in range(GALSIZE):
5414             if (game.options & OPTION_SHOWME) and i == game.quadrant.x and j == game.quadrant.y:
5415                 proutn("<")
5416             else:
5417                 proutn(" ")
5418             if game.state.galaxy[i][j].supernova:
5419                 show = "***"
5420             elif not game.state.galaxy[i][j].charted and game.state.galaxy[i][j].starbase:
5421                 show = ".1."
5422             elif game.state.galaxy[i][j].charted:
5423                 show = "%3d" % (game.state.chart[i][j].klingons*100 + game.state.chart[i][j].starbase * 10 + game.state.chart[i][j].stars)
5424             else:
5425                 show = "..."
5426             proutn(show)
5427             if (game.options & OPTION_SHOWME) and i == game.quadrant.x and j == game.quadrant.y:
5428                 proutn(">")
5429             else:
5430                 proutn(" ")
5431         proutn("  |")
5432         if i<GALSIZE:
5433             skip(1)
5434
5435 def sectscan(goodScan, i, j):
5436     # light up an individual dot in a sector 
5437     if goodScan or (abs(i-game.sector.x)<= 1 and abs(j-game.sector.y) <= 1):
5438         if (game.quad[i][j]==IHMATER0) or (game.quad[i][j]==IHMATER1) or (game.quad[i][j]==IHMATER2) or (game.quad[i][j]==IHE) or (game.quad[i][j]==IHF):
5439             if game.condition   == "red": textcolor(RED)
5440             elif game.condition == "green": textcolor(GREEN)
5441             elif game.condition == "yellow": textcolor(YELLOW)
5442             elif game.condition == "docked": textcolor(CYAN)
5443             elif game.condition == "dead": textcolor(BROWN)
5444             if game.quad[i][j] != game.ship: 
5445                 highvideo()
5446         proutn("%c " % game.quad[i][j])
5447         textcolor(DEFAULT)
5448     else:
5449         proutn("- ")
5450
5451 def status(req=0):
5452     # print status report lines 
5453
5454     if not req or req == 1:
5455         prstat(_("Stardate"), _("%.1f, Time Left %.2f") \
5456                % (game.state.date, game.state.remtime))
5457     if not req or req == 2:
5458         if game.condition != "docked":
5459             newcnd()
5460         dam = 0
5461         for t in range(NDEVICES):
5462             if game.damage[t]>0: 
5463                 dam += 1
5464         prstat(_("Condition"), _("%s, %i DAMAGES") % (game.condition.upper(), dam))
5465     if not req or req == 3:
5466         prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
5467     if not req or req == 4:
5468         if damaged(DLIFSUP):
5469             if game.condition == "docked":
5470                 s = _("DAMAGED, Base provides")
5471             else:
5472                 s = _("DAMAGED, reserves=%4.2f") % game.lsupres
5473         else:
5474             s = _("ACTIVE")
5475         prstat(_("Life Support"), s)
5476     if not req or req == 5:
5477         prstat(_("Warp Factor"), "%.1f" % game.warpfac)
5478     if not req or req == 6:
5479         extra = ""
5480         if game.icrystl and (game.options & OPTION_SHOWME):
5481             extra = _(" (have crystals)")
5482         prstat(_("Energy"), "%.2f%s" % (game.energy, extra))
5483     if not req or req == 7:
5484         prstat(_("Torpedoes"), "%d" % (game.torps))
5485     if not req or req == 8:
5486         if damaged(DSHIELD):
5487             s = _("DAMAGED,")
5488         elif game.shldup:
5489             s = _("UP,")
5490         else:
5491             s = _("DOWN,")
5492         data = _(" %d%% %.1f units") \
5493                % (int((100.0*game.shield)/game.inshld + 0.5), game.shield)
5494         prstat(_("Shields"), s+data)
5495     if not req or req == 9:
5496         prstat(_("Klingons Left"), "%d" \
5497                % (game.state.remkl + game.state.remcom + game.state.nscrem))
5498     if not req or req == 10:
5499         if game.options & OPTION_WORLDS:
5500             plnet = game.state.galaxy[game.quadrant.x][game.quadrant.y].planet
5501             if plnet and plnet.inhabited:
5502                 prstat(_("Major system"), plnet.name)
5503             else:
5504                 prout(_("Sector is uninhabited"))
5505     elif not req or req == 11:
5506         attackreport(not req)
5507
5508 def request():
5509     requests = ("da","co","po","ls","wa","en","to","sh","kl","sy", "ti")
5510     while scan() == IHEOL:
5511         proutn(_("Information desired? "))
5512     chew()
5513     if citem in requests:
5514         status(requests.index(citem))
5515     else:
5516         prout(_("UNRECOGNIZED REQUEST. Legal requests are:"))
5517         prout(("  date, condition, position, lsupport, warpfactor,"))
5518         prout(("  energy, torpedoes, shields, klingons, system, time."))
5519                 
5520 def srscan():
5521     # short-range scan 
5522     goodScan=True
5523     if damaged(DSRSENS):
5524         # Allow base's sensors if docked 
5525         if game.condition != "docked":
5526             prout(_("   S.R. SENSORS DAMAGED!"))
5527             goodScan=False
5528         else:
5529             prout(_("  [Using Base's sensors]"))
5530     else:
5531         prout(_("     Short-range scan"))
5532     if goodScan and not damaged(DRADIO): 
5533         game.state.chart[game.quadrant.x][game.quadrant.y].klingons = game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons
5534         game.state.chart[game.quadrant.x][game.quadrant.y].starbase = game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase
5535         game.state.chart[game.quadrant.x][game.quadrant.y].stars = game.state.galaxy[game.quadrant.x][game.quadrant.y].stars
5536         game.state.galaxy[game.quadrant.x][game.quadrant.y].charted = True
5537     prout("    1 2 3 4 5 6 7 8 9 10")
5538     if game.condition != "docked":
5539         newcnd()
5540     for i in range(QUADSIZE):
5541         proutn("%2d  " % (i+1))
5542         for j in range(QUADSIZE):
5543             sectscan(goodScan, i, j)
5544         skip(1)
5545                         
5546                         
5547 def eta():
5548     # use computer to get estimated time of arrival for a warp jump 
5549     w1 = coord(); w2 = coord()
5550     prompt = False
5551     if damaged(DCOMPTR):
5552         prout(_("COMPUTER DAMAGED, USE A POCKET CALCULATOR."))
5553         skip(1)
5554         return
5555     if scan() != IHREAL:
5556         prompt = True
5557         chew()
5558         proutn(_("Destination quadrant and/or sector? "))
5559         if scan()!=IHREAL:
5560             huh()
5561             return
5562     w1.y = int(aaitem-0.5)
5563     if scan() != IHREAL:
5564         huh()
5565         return
5566     w1.x = int(aaitem-0.5)
5567     if scan() == IHREAL:
5568         w2.y = int(aaitem-0.5)
5569         if scan() != IHREAL:
5570             huh()
5571             return
5572         w2.x = int(aaitem-0.5)
5573     else:
5574         if game.quadrant.y>w1.x:
5575             w2.x = 0
5576         else:
5577             w2.x=QUADSIZE-1
5578         if game.quadrant.x>w1.y:
5579             w2.y = 0
5580         else:
5581             w2.y=QUADSIZE-1
5582     if not VALID_QUADRANT(w1.x, w1.y) or not VALID_SECTOR(w2.x, w2.y):
5583         huh()
5584         return
5585     game.dist = math.sqrt(square(w1.y-game.quadrant.y+0.1*(w2.y-game.sector.y))+
5586                 square(w1.x-game.quadrant.x+0.1*(w2.x-game.sector.x)))
5587     wfl = False
5588     if prompt:
5589         prout(_("Answer \"no\" if you don't know the value:"))
5590     while True:
5591         chew()
5592         proutn(_("Time or arrival date? "))
5593         if scan()==IHREAL:
5594             ttime = aaitem
5595             if ttime > game.state.date:
5596                 ttime -= game.state.date # Actually a star date
5597             twarp=(math.floor(math.sqrt((10.0*game.dist)/ttime)*10.0)+1.0)/10.0
5598             if ttime <= 1e-10 or twarp > 10:
5599                 prout(_("We'll never make it, sir."))
5600                 chew()
5601                 return
5602             if twarp < 1.0:
5603                 twarp = 1.0
5604             break
5605         chew()
5606         proutn(_("Warp factor? "))
5607         if scan()== IHREAL:
5608             wfl = True
5609             twarp = aaitem
5610             if twarp<1.0 or twarp > 10.0:
5611                 huh()
5612                 return
5613             break
5614         prout(_("Captain, certainly you can give me one of these."))
5615     while True:
5616         chew()
5617         ttime = (10.0*game.dist)/square(twarp)
5618         tpower = game.dist*twarp*twarp*twarp*(game.shldup+1)
5619         if tpower >= game.energy:
5620             prout(_("Insufficient energy, sir."))
5621             if not game.shldup or tpower > game.energy*2.0:
5622                 if not wfl:
5623                     return
5624                 proutn(_("New warp factor to try? "))
5625                 if scan() == IHREAL:
5626                     wfl = True
5627                     twarp = aaitem
5628                     if twarp<1.0 or twarp > 10.0:
5629                         huh()
5630                         return
5631                     continue
5632                 else:
5633                     chew()
5634                     skip(1)
5635                     return
5636             prout(_("But if you lower your shields,"))
5637             proutn(_("remaining"))
5638             tpower /= 2
5639         else:
5640             proutn(_("Remaining"))
5641         prout(_(" energy will be %.2f.") % (game.energy-tpower))
5642         if wfl:
5643             prout(_("And we will arrive at stardate %.2f.") % (game.state.date+ttime))
5644         elif twarp==1.0:
5645             prout(_("Any warp speed is adequate."))
5646         else:
5647             prout(_("Minimum warp needed is %.2f,") % (twarp))
5648             prout(_("and we will arrive at stardate %.2f.") % (game.state.date+ttime))
5649         if game.state.remtime < ttime:
5650             prout(_("Unfortunately, the Federation will be destroyed by then."))
5651         if twarp > 6.0:
5652             prout(_("You'll be taking risks at that speed, Captain"))
5653         if (game.isatb==1 and game.state.kscmdr == w1 and \
5654              scheduled(FSCDBAS)< ttime+game.state.date) or \
5655             (scheduled(FCDBAS)<ttime+game.state.date and game.battle == w1):
5656             prout(_("The starbase there will be destroyed by then."))
5657         proutn(_("New warp factor to try? "))
5658         if scan() == IHREAL:
5659             wfl = True
5660             twarp = aaitem
5661             if twarp<1.0 or twarp > 10.0:
5662                 huh()
5663                 return
5664         else:
5665             chew()
5666             skip(1)
5667             return
5668                         
5669
5670 # Code from setup.c begins here
5671
5672 def prelim():
5673     # issue a historically correct banner 
5674     skip(2)
5675     prout(_("-SUPER- STAR TREK"))
5676     skip(1)
5677 # From the FORTRAN original
5678 #    prout(_("Latest update-21 Sept 78"))
5679 #    skip(1)
5680
5681 def freeze(boss):
5682     # save game 
5683     if boss:
5684         citem = "emsave.trk"
5685     else:
5686         key = scan()
5687         if key == IHEOL:
5688             proutn(_("File name: "))
5689             key = scan()
5690         if key != IHALPHA:
5691             huh()
5692             return
5693         chew()
5694         if '.' not in citem:
5695             citem += ".trk"
5696     try:
5697         fp = open(citem, "wb")
5698     except IOError:
5699         prout(_("Can't freeze game as file %s") % citem)
5700         return
5701     cPickle.dump(game, fp)
5702     fp.close()
5703
5704 def thaw():
5705     # retrieve saved game 
5706     game.passwd[0] = '\0'
5707     key = scan()
5708     if key == IHEOL:
5709         proutn(_("File name: "))
5710         key = scan()
5711     if key != IHALPHA:
5712         huh()
5713         return True
5714     chew()
5715     if '.' not in citem:
5716         citem += ".trk"
5717     try:
5718         fp = open(citem, "rb")
5719     except IOError:
5720         prout(_("Can't thaw game in %s") % citem)
5721         return
5722     game = cPickle.load(fp)
5723     fp.close()
5724     return False
5725
5726 # I used <http://www.memory-alpha.org> to find planets
5727 # with references in ST:TOS.  Eath and the Alpha Centauri
5728 # Colony have been omitted.
5729
5730 # Some planets marked Class G and P here will be displayed as class M
5731 # because of the way planets are generated. This is a known bug.
5732 systnames = (
5733     # Federation Worlds 
5734     _("Andoria (Fesoan)"),      # several episodes 
5735     _("Tellar Prime (Miracht)"),        # TOS: "Journey to Babel" 
5736     _("Vulcan (T'Khasi)"),      # many episodes 
5737     _("Medusa"),                # TOS: "Is There in Truth No Beauty?" 
5738     _("Argelius II (Nelphia)"),# TOS: "Wolf in the Fold" ("IV" in BSD) 
5739     _("Ardana"),                # TOS: "The Cloud Minders" 
5740     _("Catulla (Cendo-Prae)"),  # TOS: "The Way to Eden" 
5741     _("Gideon"),                # TOS: "The Mark of Gideon" 
5742     _("Aldebaran III"), # TOS: "The Deadly Years" 
5743     _("Alpha Majoris I"),       # TOS: "Wolf in the Fold" 
5744     _("Altair IV"),             # TOS: "Amok Time 
5745     _("Ariannus"),              # TOS: "Let That Be Your Last Battlefield" 
5746     _("Benecia"),               # TOS: "The Conscience of the King" 
5747     _("Beta Niobe I (Sarpeidon)"),      # TOS: "All Our Yesterdays" 
5748     _("Alpha Carinae II"),      # TOS: "The Ultimate Computer" 
5749     _("Capella IV (Kohath)"),   # TOS: "Friday's Child" (Class G) 
5750     _("Daran V"),               # TOS: "For the World is Hollow and I Have Touched the Sky" 
5751     _("Deneb II"),              # TOS: "Wolf in the Fold" ("IV" in BSD) 
5752     _("Eminiar VII"),           # TOS: "A Taste of Armageddon" 
5753     _("Gamma Canaris IV"),      # TOS: "Metamorphosis" 
5754     _("Gamma Tranguli VI (Vaalel)"),    # TOS: "The Apple" 
5755     _("Ingraham B"),            # TOS: "Operation: Annihilate" 
5756     _("Janus IV"),              # TOS: "The Devil in the Dark" 
5757     _("Makus III"),             # TOS: "The Galileo Seven" 
5758     _("Marcos XII"),            # TOS: "And the Children Shall Lead", 
5759     _("Omega IV"),              # TOS: "The Omega Glory" 
5760     _("Regulus V"),             # TOS: "Amok Time 
5761     _("Deneva"),                # TOS: "Operation -- Annihilate!" 
5762     # Worlds from BSD Trek 
5763     _("Rigel II"),              # TOS: "Shore Leave" ("III" in BSD) 
5764     _("Beta III"),              # TOS: "The Return of the Archons" 
5765     _("Triacus"),               # TOS: "And the Children Shall Lead", 
5766     _("Exo III"),               # TOS: "What Are Little Girls Made Of?" (Class P) 
5767 #       # Others 
5768 #    _("Hansen's Planet"),      # TOS: "The Galileo Seven" 
5769 #    _("Taurus IV"),            # TOS: "The Galileo Seven" (class G) 
5770 #    _("Antos IV (Doraphane)"), # TOS: "Whom Gods Destroy", "Who Mourns for Adonais?" 
5771 #    _("Izar"),                 # TOS: "Whom Gods Destroy" 
5772 #    _("Tiburon"),              # TOS: "The Way to Eden" 
5773 #    _("Merak II"),             # TOS: "The Cloud Minders" 
5774 #    _("Coridan (Desotriana)"), # TOS: "Journey to Babel" 
5775 #    _("Iotia"),                # TOS: "A Piece of the Action" 
5776 )
5777
5778 device = (
5779         _("S. R. Sensors"), \
5780         _("L. R. Sensors"), \
5781         _("Phasers"), \
5782         _("Photon Tubes"), \
5783         _("Life Support"), \
5784         _("Warp Engines"), \
5785         _("Impulse Engines"), \
5786         _("Shields"), \
5787         _("Subspace Radio"), \
5788         _("Shuttle Craft"), \
5789         _("Computer"), \
5790         _("Navigation System"), \
5791         _("Transporter"), \
5792         _("Shield Control"), \
5793         _("Death Ray"), \
5794         _("D. S. Probe"), \
5795 )
5796
5797 def setup(needprompt):
5798     # prepare to play, set up cosmos 
5799     w = coord()
5800     #  Decide how many of everything
5801     if choose(needprompt):
5802         return # frozen game
5803     # Prepare the Enterprise
5804     game.alldone = game.gamewon = False
5805     game.ship = IHE
5806     game.state.crew = FULLCREW
5807     game.energy = game.inenrg = 5000.0
5808     game.shield = game.inshld = 2500.0
5809     game.shldchg = False
5810     game.shldup = False
5811     game.inlsr = 4.0
5812     game.lsupres = 4.0
5813     game.quadrant = randplace(GALSIZE)
5814     game.sector = randplace(QUADSIZE)
5815     game.torps = game.intorps = 10
5816     game.nprobes = randrange(2, 5)
5817     game.warpfac = 5.0
5818     game.wfacsq = game.warpfac * game.warpfac
5819     for i in range(NDEVICES): 
5820         game.damage[i] = 0.0
5821     # Set up assorted game parameters
5822     game.battle = coord()
5823     game.state.date = game.indate = 100.0 * randreal(20, 51)
5824     game.nkinks = game.nhelp = game.casual = game.abandoned = 0
5825     game.iscate = game.resting = game.imine = game.icrystl = game.icraft = False
5826     game.isatb = game.state.nplankl = 0
5827     game.state.starkl = game.state.basekl = 0
5828     game.iscraft = "onship"
5829     game.landed = False
5830     game.alive = True
5831     game.docfac = 0.25
5832     for i in range(GALSIZE):
5833         for j in range(GALSIZE):
5834             quad = game.state.galaxy[i][j]
5835             quad.charted = 0
5836             quad.planet = None
5837             quad.romulans = 0
5838             quad.klingons = 0
5839             quad.starbase = False
5840             quad.supernova = False
5841             quad.status = "secure"
5842     # Initialize times for extraneous events
5843     schedule(FSNOVA, expran(0.5 * game.intime))
5844     schedule(FTBEAM, expran(1.5 * (game.intime / game.state.remcom)))
5845     schedule(FSNAP, randreal(1.0, 2.0)) # Force an early snapshot
5846     schedule(FBATTAK, expran(0.3*game.intime))
5847     unschedule(FCDBAS)
5848     if game.state.nscrem:
5849         schedule(FSCMOVE, 0.2777)
5850     else:
5851         unschedule(FSCMOVE)
5852     unschedule(FSCDBAS)
5853     unschedule(FDSPROB)
5854     if (game.options & OPTION_WORLDS) and game.skill >= SKILL_GOOD:
5855         schedule(FDISTR, expran(1.0 + game.intime))
5856     else:
5857         unschedule(FDISTR)
5858     unschedule(FENSLV)
5859     unschedule(FREPRO)
5860     # Starchart is functional but we've never seen it
5861     game.lastchart = FOREVER
5862     # Put stars in the galaxy
5863     game.instar = 0
5864     for i in range(GALSIZE):
5865         for j in range(GALSIZE):
5866             k = randrange(1, QUADSIZE**2/10+1)
5867             game.instar += k
5868             game.state.galaxy[i][j].stars = k
5869     # Locate star bases in galaxy
5870     for i in range(game.inbase):
5871         while True:
5872             while True:
5873                 w = randplace(GALSIZE)
5874                 if not game.state.galaxy[w.x][w.y].starbase:
5875                     break
5876             contflag = False
5877             # C version: for (j = i-1; j > 0; j--)
5878             # so it did them in the opposite order.
5879             for j in range(1, i):
5880                 # Improved placement algorithm to spread out bases
5881                 distq = w.distance(game.state.baseq[j])
5882                 if distq < 6.0*(BASEMAX+1-game.inbase) and withprob(0.75):
5883                     contflag = True
5884                     if idebug:
5885                         prout("=== Abandoning base #%d at %s" % (i, w))
5886                     break
5887                 elif distq < 6.0 * (BASEMAX+1-game.inbase):
5888                     if idebug:
5889                         prout("=== Saving base #%d, close to #%d" % (i, j))
5890             if not contflag:
5891                 break
5892         game.state.baseq[i] = w
5893         game.state.galaxy[w.x][w.y].starbase = True
5894         game.state.chart[w.x][w.y].starbase = True
5895     # Position ordinary Klingon Battle Cruisers
5896     krem = game.inkling
5897     klumper = 0.25*game.skill*(9.0-game.length)+1.0
5898     if klumper > MAXKLQUAD: 
5899         klumper = MAXKLQUAD
5900     while True:
5901         r = randreal()
5902         klump = (1.0 - r*r)*klumper
5903         if klump > krem:
5904             klump = krem
5905         krem -= klump
5906         while True:
5907             w = randplace(GALSIZE)
5908             if not game.state.galaxy[w.x][w.y].supernova and \
5909                game.state.galaxy[w.x][w.y].klingons + klump <= MAXKLQUAD:
5910                 break
5911         game.state.galaxy[w.x][w.y].klingons += int(klump)
5912         if krem <= 0:
5913             break
5914     # Position Klingon Commander Ships
5915     for i in range(1, game.incom+1):
5916         while True:
5917             w = randplace(GALSIZE)
5918             if (game.state.galaxy[w.x][w.y].klingons or withprob(0.25)) and \
5919                    not game.state.galaxy[w.x][w.y].supernova and \
5920                    game.state.galaxy[w.x][w.y].klingons <= MAXKLQUAD-1 and \
5921                    not w in game.state.kcmdr[:i]:
5922                 break
5923         game.state.galaxy[w.x][w.y].klingons += 1
5924         game.state.kcmdr[i] = w
5925     # Locate planets in galaxy
5926     for i in range(game.inplan):
5927         while True:
5928             w = randplace(GALSIZE) 
5929             if game.state.galaxy[w.x][w.y].planet == None:
5930                 break
5931         new = planet()
5932         new.w = w
5933         new.crystals = "absent"
5934         if (game.options & OPTION_WORLDS) and i < NINHAB:
5935             new.pclass = "M"    # All inhabited planets are class M
5936             new.crystals = "absent"
5937             new.known = "known"
5938             new.name = systnames[i]
5939             new.inhabited = True
5940         else:
5941             new.pclass = ("M", "N", "O")[randrange(0, 3)]
5942             if withprob(0.33):
5943                 new.crystals = "present"
5944             new.known = "unknown"
5945             new.inhabited = False
5946         game.state.galaxy[w.x][w.y].planet = new
5947         game.state.planets.append(new)
5948     # Locate Romulans
5949     for i in range(game.state.nromrem):
5950         w = randplace(GALSIZE)
5951         game.state.galaxy[w.x][w.y].romulans += 1
5952     # Locate the Super Commander
5953     if game.state.nscrem > 0:
5954         while True:
5955             w = randplace(GALSIZE)
5956             if not game.state.galaxy[w.x][w.y].supernova and game.state.galaxy[w.x][w.y].klingons <= MAXKLQUAD:
5957                 break
5958         game.state.kscmdr = w
5959         game.state.galaxy[w.x][w.y].klingons += 1
5960     # Place thing (in tournament game, we don't want one!)
5961     global thing
5962     if game.tourn is None:
5963         thing = randplace(GALSIZE)
5964     skip(2)
5965     game.state.snap = False
5966     if game.skill == SKILL_NOVICE:
5967         prout(_("It is stardate %d. The Federation is being attacked by") % int(game.state.date))
5968         prout(_("a deadly Klingon invasion force. As captain of the United"))
5969         prout(_("Starship U.S.S. Enterprise, it is your mission to seek out"))
5970         prout(_("and destroy this invasion force of %d battle cruisers.") % ((game.inkling + game.incom + game.inscom)))
5971         prout(_("You have an initial allotment of %d stardates to complete") % int(game.intime))
5972         prout(_("your mission.  As you proceed you may be given more time."))
5973         skip(1)
5974         prout(_("You will have %d supporting starbases.") % (game.inbase))
5975         proutn(_("Starbase locations-  "))
5976     else:
5977         prout(_("Stardate %d.") % int(game.state.date))
5978         skip(1)
5979         prout(_("%d Klingons.") % (game.inkling + game.incom + game.inscom))
5980         prout(_("An unknown number of Romulans."))
5981         if game.state.nscrem:
5982             prout(_("And one (GULP) Super-Commander."))
5983         prout(_("%d stardates.") % int(game.intime))
5984         proutn(_("%d starbases in ") % game.inbase)
5985     for i in range(game.inbase):
5986         proutn(`game.state.baseq[i]`)
5987         proutn("  ")
5988     skip(2)
5989     proutn(_("The Enterprise is currently in Quadrant %s") % game.quadrant)
5990     proutn(_(" Sector %s") % game.sector)
5991     skip(2)
5992     prout(_("Good Luck!"))
5993     if game.state.nscrem:
5994         prout(_("  YOU'LL NEED IT."))
5995     waitfor()
5996     newqad(False)
5997     if game.nenhere - (thing == game.quadrant) - (game.tholian != None):
5998         game.shldup = True
5999     if game.neutz:      # bad luck to start in a Romulan Neutral Zone
6000         attack(False)
6001
6002 def choose(needprompt):
6003     # choose your game type
6004     global thing
6005     while True:
6006         game.tourn = 0
6007         game.thawed = False
6008         game.skill = SKILL_NONE
6009         game.length = 0
6010         if needprompt: # Can start with command line options 
6011             proutn(_("Would you like a regular, tournament, or saved game? "))
6012         scan()
6013         if len(citem)==0: # Try again
6014             continue
6015         if isit("tournament"):
6016             while scan() == IHEOL:
6017                 proutn(_("Type in tournament number-"))
6018             if aaitem == 0:
6019                 chew()
6020                 continue # We don't want a blank entry
6021             game.tourn = int(round(aaitem))
6022             random.seed(aaitem)
6023             if logfp:
6024                 logfp.write("# random.seed(%d)\n" % aaitem)
6025             break
6026         if isit("saved") or isit("frozen"):
6027             if thaw():
6028                 continue
6029             chew()
6030             if game.passwd == None:
6031                 continue
6032             if not game.alldone:
6033                 game.thawed = True # No plaque if not finished
6034             report()
6035             waitfor()
6036             return True
6037         if isit("regular"):
6038             break
6039         proutn(_("What is \"%s\"?"), citem)
6040         chew()
6041     while game.length==0 or game.skill==SKILL_NONE:
6042         if scan() == IHALPHA:
6043             if isit("short"):
6044                 game.length = 1
6045             elif isit("medium"):
6046                 game.length = 2
6047             elif isit("long"):
6048                 game.length = 4
6049             elif isit("novice"):
6050                 game.skill = SKILL_NOVICE
6051             elif isit("fair"):
6052                 game.skill = SKILL_FAIR
6053             elif isit("good"):
6054                 game.skill = SKILL_GOOD
6055             elif isit("expert"):
6056                 game.skill = SKILL_EXPERT
6057             elif isit("emeritus"):
6058                 game.skill = SKILL_EMERITUS
6059             else:
6060                 proutn(_("What is \""))
6061                 proutn(citem)
6062                 prout("\"?")
6063         else:
6064             chew()
6065             if game.length==0:
6066                 proutn(_("Would you like a Short, Medium, or Long game? "))
6067             elif game.skill == SKILL_NONE:
6068                 proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
6069     # Choose game options -- added by ESR for SST2K
6070     if scan() != IHALPHA:
6071         chew()
6072         proutn(_("Choose your game style (or just press enter): "))
6073         scan()
6074     if isit("plain"):
6075         # Approximates the UT FORTRAN version.
6076         game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
6077         game.options |= OPTION_PLAIN
6078     elif isit("almy"):
6079         # Approximates Tom Almy's version.
6080         game.options &=~ (OPTION_THINGY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
6081         game.options |= OPTION_ALMY
6082     elif isit("fancy"):
6083         pass
6084     elif len(citem):
6085         proutn(_("What is \"%s\"?") % citem)
6086     setpassword()
6087     if game.passwd == "debug":
6088         idebug = True
6089         fputs("=== Debug mode enabled\n", sys.stdout)
6090
6091     # Use parameters to generate initial values of things
6092     game.damfac = 0.5 * game.skill
6093     game.state.rembase = randrange(BASEMIN, BASEMAX+1)
6094     game.inbase = game.state.rembase
6095     game.inplan = 0
6096     if game.options & OPTION_PLANETS:
6097         game.inplan += randrange(MAXUNINHAB/2, MAXUNINHAB+1)
6098     if game.options & OPTION_WORLDS:
6099         game.inplan += int(NINHAB)
6100     game.state.nromrem = game.inrom = randrange(2 *game.skill)
6101     game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR)
6102     game.state.remtime = 7.0 * game.length
6103     game.intime = game.state.remtime
6104     game.state.remkl = game.inkling = 2.0*game.intime*((game.skill+1 - 2*randreal())*game.skill*0.1+.15)
6105     game.incom = int(game.skill + 0.0625*game.inkling*randreal())
6106     game.state.remcom = min(10, game.incom)
6107     game.incom = game.state.remcom
6108     game.state.remres = (game.inkling+4*game.incom)*game.intime
6109     game.inresor = game.state.remres
6110     if game.inkling > 50:
6111         game.state.rembase += 1
6112         game.inbase = game.state.rembase
6113     return False
6114
6115 def dropin(iquad=None):
6116     # drop a feature on a random dot in the current quadrant 
6117     w = coord()
6118     while True:
6119         w = randplace(QUADSIZE)
6120         if game.quad[w.x][w.y] == IHDOT:
6121             break
6122     if iquad is not None:
6123         game.quad[w.x][w.y] = iquad
6124     return w
6125
6126 def newcnd():
6127     # update our alert status 
6128     game.condition = "green"
6129     if game.energy < 1000.0:
6130         game.condition = "yellow"
6131     if game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons or game.state.galaxy[game.quadrant.x][game.quadrant.y].romulans:
6132         game.condition = "red"
6133     if not game.alive:
6134         game.condition="dead"
6135
6136 def newkling():
6137     # drop new Klingon into current quadrant
6138     return enemy(IHK, loc=dropin(), power=randreal(300,450)+25.0*game.skill)
6139
6140 def newqad(shutup):
6141     # set up a new state of quadrant, for when we enter or re-enter it 
6142     w = coord()
6143     game.justin = True
6144     game.klhere = 0
6145     game.comhere = False
6146     game.ishere = False
6147     game.irhere = 0
6148     game.iplnet = 0
6149     game.nenhere = 0
6150     game.neutz = False
6151     game.inorbit = False
6152     game.landed = False
6153     game.ientesc = False
6154     game.iseenit = False
6155     if game.iscate:
6156         # Attempt to escape Super-commander, so tbeam back!
6157         game.iscate = False
6158         game.ientesc = True
6159     q = game.state.galaxy[game.quadrant.x][game.quadrant.y]
6160     # cope with supernova
6161     if q.supernova:
6162         return
6163     game.klhere = q.klingons
6164     game.irhere = q.romulans
6165     game.nenhere = game.klhere + game.irhere
6166     # Position Starship
6167     game.quad[game.sector.x][game.sector.y] = game.ship
6168     game.enemies = []
6169     if q.klingons:
6170         # Position ordinary Klingons
6171         for i in range(game.klhere):
6172             newkling()
6173         # If we need a commander, promote a Klingon
6174         for i in range(game.state.remcom):
6175             if game.state.kcmdr[i] == game.quadrant:
6176                 e = game.enemies[game.klhere-1]
6177                 game.quad[e.kloc.x][e.kloc.y] = IHC
6178                 e.kpower = randreal(950,1350) + 50.0*game.skill
6179                 game.comhere = True
6180                 break   
6181         # If we need a super-commander, promote a Klingon
6182         if game.quadrant == game.state.kscmdr:
6183             e = game.enemies[0]
6184             game.quad[e.kloc.x][e.kloc.y] = IHS
6185             e.kpower = randreal(1175.0,  1575.0) + 125.0*game.skill
6186             game.iscate = (game.state.remkl > 1)
6187             game.ishere = True
6188     # Put in Romulans if needed
6189     for i in range(game.klhere, game.nenhere):
6190         enemy(IHR, loc=dropin(), power=randreal(400.0,850.0)+50.0*game.skill)
6191     # If quadrant needs a starbase, put it in
6192     if q.starbase:
6193         game.base = dropin(IHB)
6194     # If quadrant needs a planet, put it in
6195     if q.planet:
6196         game.iplnet = q.planet
6197         if not q.planet.inhabited:
6198             game.plnet = dropin(IHP)
6199         else:
6200             game.plnet = dropin(IHW)
6201     # Check for condition
6202     newcnd()
6203     # Check for RNZ
6204     if game.irhere > 0 and game.klhere == 0:
6205         game.neutz = True
6206         if not damaged(DRADIO):
6207             skip(1)
6208             prout(_("LT. Uhura- \"Captain, an urgent message."))
6209             prout(_("  I'll put it on audio.\"  CLICK"))
6210             skip(1)
6211             prout(_("INTRUDER! YOU HAVE VIOLATED THE ROMULAN NEUTRAL ZONE."))
6212             prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
6213     if shutup==0:
6214         # Put in THING if needed
6215         if thing == game.quadrant:
6216             enemy(symbol=IHQUEST, loc=dropin(),
6217                       power=randreal(6000,6500.0)+250.0*game.skill)
6218             if not damaged(DSRSENS):
6219                 skip(1)
6220                 prout(_("Mr. Spock- \"Captain, this is most unusual."))
6221                 prout(_("    Please examine your short-range scan.\""))
6222     # Decide if quadrant needs a Tholian; lighten up if skill is low 
6223     if game.options & OPTION_THOLIAN:
6224         if (game.skill < SKILL_GOOD and withprob(0.02)) or \
6225             (game.skill == SKILL_GOOD and withprob(0.05)) or \
6226             (game.skill > SKILL_GOOD and withprob(0.08)):
6227             w = coord()
6228             while True:
6229                 w.x = withprob(0.5) * (QUADSIZE-1)
6230                 w.y = withprob(0.5) * (QUADSIZE-1)
6231                 if game.quad[w.x][w.y] == IHDOT:
6232                     break
6233             game.tholian = enemy(symbol=IHT, loc=w,
6234                                  power=randrange(100, 500) + 25.0*game.skill)
6235             # Reserve unoccupied corners 
6236             if game.quad[0][0]==IHDOT:
6237                 game.quad[0][0] = 'X'
6238             if game.quad[0][QUADSIZE-1]==IHDOT:
6239                 game.quad[0][QUADSIZE-1] = 'X'
6240             if game.quad[QUADSIZE-1][0]==IHDOT:
6241                 game.quad[QUADSIZE-1][0] = 'X'
6242             if game.quad[QUADSIZE-1][QUADSIZE-1]==IHDOT:
6243                 game.quad[QUADSIZE-1][QUADSIZE-1] = 'X'
6244     game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
6245     # And finally the stars
6246     for i in range(q.stars):
6247         dropin(IHSTAR)
6248     # Put in a few black holes
6249     for i in range(1, 3+1):
6250         if withprob(0.5): 
6251             dropin(IHBLANK)
6252     # Take out X's in corners if Tholian present
6253     if game.tholian:
6254         if game.quad[0][0]=='X':
6255             game.quad[0][0] = IHDOT
6256         if game.quad[0][QUADSIZE-1]=='X':
6257             game.quad[0][QUADSIZE-1] = IHDOT
6258         if game.quad[QUADSIZE-1][0]=='X':
6259             game.quad[QUADSIZE-1][0] = IHDOT
6260         if game.quad[QUADSIZE-1][QUADSIZE-1]=='X':
6261             game.quad[QUADSIZE-1][QUADSIZE-1] = IHDOT
6262
6263 def setpassword():
6264     # set the self-destruct password 
6265     if game.options & OPTION_PLAIN:
6266         while True:
6267             chew()
6268             proutn(_("Please type in a secret password- "))
6269             scan()
6270             game.passwd = citem
6271             if game.passwd != None:
6272                 break
6273     else:
6274         game.passwd = ""
6275         for i in range(3):
6276             game.passwd += chr(ord('a')+randrange(26))
6277
6278 # Code from sst.c begins here
6279
6280 commands = {
6281     "SRSCAN":           OPTION_TTY,
6282     "STATUS":           OPTION_TTY,
6283     "REQUEST":          OPTION_TTY,
6284     "LRSCAN":           OPTION_TTY,
6285     "PHASERS":          0,
6286     "TORPEDO":          0,
6287     "PHOTONS":          0,
6288     "MOVE":             0,
6289     "SHIELDS":          0,
6290     "DOCK":             0,
6291     "DAMAGES":          0,
6292     "CHART":            0,
6293     "IMPULSE":          0,
6294     "REST":             0,
6295     "WARP":             0,
6296     "SCORE":            0,
6297     "SENSORS":          OPTION_PLANETS,
6298     "ORBIT":            OPTION_PLANETS,
6299     "TRANSPORT":        OPTION_PLANETS,
6300     "MINE":             OPTION_PLANETS,
6301     "CRYSTALS":         OPTION_PLANETS,
6302     "SHUTTLE":          OPTION_PLANETS,
6303     "PLANETS":          OPTION_PLANETS,
6304     "REPORT":           0,
6305     "COMPUTER":         0,
6306     "COMMANDS":         0,
6307     "EMEXIT":           0,
6308     "PROBE":            OPTION_PROBE,
6309     "SAVE":             0,
6310     "FREEZE":           0,      # Synonym for SAVE
6311     "ABANDON":          0,
6312     "DESTRUCT":         0,
6313     "DEATHRAY":         0,
6314     "DEBUG":            0,
6315     "MAYDAY":           0,
6316     "SOS":              0,      # Synonym for MAYDAY
6317     "CALL":             0,      # Synonym for MAYDAY
6318     "QUIT":             0,
6319     "HELP":             0,
6320     "SEED":             0,
6321     "VISUAL":           0,
6322 }
6323
6324 def ACCEPT(cmd):        return (not commands[cmd] or (commands[cmd] & game.options))
6325
6326 def listCommands():
6327     # generate a list of legal commands 
6328     k = 0
6329     proutn(_("LEGAL COMMANDS ARE:"))
6330     for key in commands:
6331         if ACCEPT(key):
6332             if k % 5 == 0:
6333                 skip(1)
6334             proutn("%-12s " % key) 
6335             k += 1
6336     skip(1)
6337
6338 def helpme():
6339     # browse on-line help 
6340     # Give help on commands 
6341     key = scan()
6342     while True:
6343         if key == IHEOL:
6344             setwnd(prompt_window)
6345             proutn(_("Help on what command? "))
6346             key = scan()
6347         setwnd(message_window)
6348         if key == IHEOL:
6349             return
6350         if citem in commands or citem == "ABBREV":
6351             break
6352         skip(1)
6353         listCommands()
6354         key = IHEOL
6355         chew()
6356         skip(1)
6357     cmd = citem.upper()
6358     try:
6359         fp = open(SSTDOC, "r")
6360     except IOError:
6361         try:
6362             fp = open(DOC_NAME, "r")
6363         except IOError:
6364             prout(_("Spock-  \"Captain, that information is missing from the"))
6365             proutn(_("   computer. You need to find "))
6366             proutn(DOC_NAME)
6367             prout(_(" and put it in the"))
6368             proutn(_("   current directory or to "))
6369             proutn(SSTDOC)
6370             prout(".\"")
6371             #
6372             # This used to continue: "You need to find SST.DOC and put 
6373             # it in the current directory."
6374             # 
6375             return
6376     while True:
6377         linebuf = fp.readline()
6378         if linebuf == '':
6379             prout(_("Spock- \"Captain, there is no information on that command.\""))
6380             fp.close()
6381             return
6382         if linebuf[0] == '%' and linebuf[1] == '%' and linebuf[2] == ' ':
6383             linebuf = linebuf[3:].strip()
6384             if cmd == linebuf:
6385                 break
6386     skip(1)
6387     prout(_("Spock- \"Captain, I've found the following information:\""))
6388     skip(1)
6389     while linebuf in fp:
6390         if "******" in linebuf:
6391             break
6392         proutn(linebuf)
6393     fp.close()
6394
6395 def makemoves():
6396     # command-interpretation loop 
6397     v = 0
6398     clrscr()
6399     setwnd(message_window)
6400     while True:         # command loop 
6401         drawmaps(1)
6402         while True:     # get a command 
6403             hitme = False
6404             game.justin = False
6405             game.optime = 0.0
6406             chew()
6407             setwnd(prompt_window)
6408             clrscr()
6409             proutn("COMMAND> ")
6410             if scan() == IHEOL:
6411                 if game.options & OPTION_CURSES:
6412                     makechart()
6413                 continue
6414             game.ididit = False
6415             clrscr()
6416             setwnd(message_window)
6417             clrscr()
6418             candidates = filter(lambda x: x.startswith(citem.upper()),
6419                                 commands)
6420             if len(candidates) == 1:
6421                 cmd = candidates[0]
6422                 break
6423             elif candidates and not (game.options & OPTION_PLAIN):
6424                 prout("Commands with that prefix: " + " ".join(candidates))
6425             else:
6426                 listCommands()
6427                 continue
6428         if cmd == "SRSCAN":             # srscan
6429             srscan()
6430         elif cmd == "STATUS":           # status
6431             status()
6432         elif cmd == "REQUEST":          # status request 
6433             request()
6434         elif cmd == "LRSCAN":           # long range scan
6435             lrscan(silent=False)
6436         elif cmd == "PHASERS":          # phasers
6437             phasers()
6438             if game.ididit:
6439                 hitme = True
6440         elif cmd == "TORPEDO":          # photon torpedos
6441             photon()
6442             if game.ididit:
6443                 hitme = True
6444         elif cmd == "MOVE":             # move under warp
6445             warp(False)
6446         elif cmd == "SHIELDS":          # shields
6447             doshield(shraise=False)
6448             if game.ididit:
6449                 hitme = True
6450                 game.shldchg = False
6451         elif cmd == "DOCK":             # dock at starbase
6452             dock(True)
6453             if game.ididit:
6454                 attack(False)           
6455         elif cmd == "DAMAGES":          # damage reports
6456             damagereport()
6457         elif cmd == "CHART":            # chart
6458             makechart()
6459         elif cmd == "IMPULSE":          # impulse
6460             impulse()
6461         elif cmd == "REST":             # rest
6462             os.wait()
6463             if game.ididit:
6464                 hitme = True
6465         elif cmd == "WARP":             # warp
6466             setwarp()
6467         elif cmd == "SCORE":            # score
6468             score()
6469         elif cmd == "SENSORS":          # sensors
6470             sensor()
6471         elif cmd == "ORBIT":            # orbit
6472             orbit()
6473             if game.ididit:
6474                 hitme = True
6475         elif cmd == "TRANSPORT":                # transport "beam"
6476             beam()
6477         elif cmd == "MINE":             # mine
6478             mine()
6479             if game.ididit:
6480                 hitme = True
6481         elif cmd == "CRYSTALS":         # crystals
6482             usecrystals()
6483             if game.ididit:
6484                 hitme = True
6485         elif cmd == "SHUTTLE":          # shuttle
6486             shuttle()
6487             if game.ididit:
6488                 hitme = True
6489         elif cmd == "PLANETS":          # Planet list
6490             survey()
6491         elif cmd == "REPORT":           # Game Report 
6492             report()
6493         elif cmd == "COMPUTER":         # use COMPUTER!
6494             eta()
6495         elif cmd == "COMMANDS":
6496             listCommands()
6497         elif cmd == "EMEXIT":           # Emergency exit
6498             clrscr()                    # Hide screen
6499             freeze(True)                # forced save
6500             raise SysExit,1                     # And quick exit
6501         elif cmd == "PROBE":
6502             probe()                     # Launch probe
6503             if game.ididit:
6504                 hitme = True
6505         elif cmd == "ABANDON":          # Abandon Ship
6506             abandon()
6507         elif cmd == "DESTRUCT":         # Self Destruct
6508             selfdestruct()
6509         elif cmd == "SAVE":             # Save Game
6510             freeze(False)
6511             clrscr()
6512             if game.skill > SKILL_GOOD:
6513                 prout(_("WARNING--Saved games produce no plaques!"))
6514         elif cmd == "DEATHRAY":         # Try a desparation measure
6515             deathray()
6516             if game.ididit:
6517                 hitme = True
6518         elif cmd == "DEBUGCMD":         # What do we want for debug???
6519             debugme()
6520         elif cmd == "MAYDAY":           # Call for help
6521             mayday()
6522             if game.ididit:
6523                 hitme = True
6524         elif cmd == "QUIT":
6525             game.alldone = True         # quit the game
6526         elif cmd == "HELP":
6527             helpme()                    # get help
6528         while True:
6529             if game.alldone:
6530                 break           # Game has ended
6531             if game.optime != 0.0:
6532                 events()
6533                 if game.alldone:
6534                     break       # Events did us in
6535             if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
6536                 atover(False)
6537                 continue
6538             if hitme and not game.justin:
6539                 attack(True)
6540                 if game.alldone:
6541                     break
6542                 if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
6543                     atover(False)
6544                     hitme = True
6545                     continue
6546             break
6547         if game.alldone:
6548             break
6549     if idebug:
6550         prout("=== Ending")
6551
6552 def cramen(cmd):
6553     # return an enemy 
6554     if   cmd == IHR: s = _("Romulan")
6555     elif cmd == IHK: s = _("Klingon")
6556     elif cmd == IHC: s = _("Commander")
6557     elif cmd == IHS: s = _("Super-commander")
6558     elif cmd == IHSTAR: s = _("Star")
6559     elif cmd == IHP: s = _("Planet")
6560     elif cmd == IHB: s = _("Starbase")
6561     elif cmd == IHBLANK: s = _("Black hole")
6562     elif cmd == IHT: s = _("Tholian")
6563     elif cmd == IHWEB: s = _("Tholian web")
6564     elif cmd == IHQUEST: s = _("Stranger")
6565     elif cmd == IHW: s = _("Inhabited World")
6566     else: s = "Unknown??"
6567     proutn(s)
6568
6569 def crmena(stars, enemy, loctype, w):
6570     # print an enemy and his location 
6571     if stars:
6572         proutn("***")
6573     cramen(enemy)
6574     proutn(_(" at "))
6575     buf = ""
6576     if loctype == "quadrant":
6577         buf = _("Quadrant ")
6578     elif loctype == "sector":
6579         buf = _("Sector ")
6580     proutn(buf + `w`)
6581
6582 def crmshp():
6583     # print our ship name 
6584     if game.ship == IHE:
6585         s = _("Enterprise")
6586     elif game.ship == IHF:
6587         s = _("Faerie Queene")
6588     else:
6589         s = "Ship???"
6590     proutn(s)
6591
6592 def stars():
6593     # print a line of stars 
6594     prouts("******************************************************")
6595     skip(1)
6596
6597 def expran(avrage):
6598     return -avrage*math.log(1e-7 + randreal())
6599
6600 def randplace(size):
6601     # choose a random location  
6602     w = coord()
6603     w.x = randrange(size) 
6604     w.y = randrange(size)
6605     return w
6606
6607 def chew():
6608     # Demand input for next scan
6609     global inqueue
6610     inqueue = None
6611
6612 def chew2():
6613     # return IHEOL next time 
6614     global inqueue
6615     inqueue = []
6616
6617 def scan():
6618     # Get a token from the user
6619     global inqueue, line, citem, aaitem
6620     aaitem = 0.0
6621     citem = ''
6622
6623     # Read a line if nothing here
6624     if inqueue == None:
6625         line = cgetline()
6626         if curwnd==prompt_window:
6627             clrscr()
6628             setwnd(message_window)
6629             clrscr()
6630         # Skip leading white space
6631         line = line.lstrip()
6632         if line:
6633             inqueue = line.split()
6634         else:
6635             inqueue = []
6636             return IHEOL
6637     elif not inqueue:
6638         return IHEOL
6639     # From here on in it's all looking at the queue
6640     citem = inqueue.pop(0)
6641     if citem == IHEOL:
6642         return IHEOL
6643     try:
6644         aaitem = float(citem)
6645         return IHREAL
6646     except ValueError:
6647         pass
6648     # Treat as alpha
6649     citem = citem.lower()
6650     return IHALPHA
6651
6652 def ja():
6653     # yes-or-no confirmation 
6654     chew()
6655     while True:
6656         scan()
6657         chew()
6658         if citem == 'y':
6659             return True
6660         if citem == 'n':
6661             return False
6662         proutn(_("Please answer with \"y\" or \"n\": "))
6663
6664 def huh():
6665     # complain about unparseable input 
6666     chew()
6667     skip(1)
6668     prout(_("Beg your pardon, Captain?"))
6669
6670 def isit(s):
6671     # compares s to citem and returns true if it matches to the length of s
6672     return s.startswith(citem)
6673
6674 def debugme():
6675     # access to the internals for debugging 
6676     proutn("Reset levels? ")
6677     if ja() == True:
6678         if game.energy < game.inenrg:
6679             game.energy = game.inenrg
6680         game.shield = game.inshld
6681         game.torps = game.intorps
6682         game.lsupres = game.inlsr
6683     proutn("Reset damage? ")
6684     if ja() == True:
6685         for i in range(NDEVICES): 
6686             if game.damage[i] > 0.0: 
6687                 game.damage[i] = 0.0
6688     proutn("Toggle debug flag? ")
6689     if ja() == True:
6690         idebug = not idebug
6691         if idebug:
6692             prout("Debug output ON")        
6693         else:
6694             prout("Debug output OFF")
6695     proutn("Cause selective damage? ")
6696     if ja() == True:
6697         for i in range(NDEVICES):
6698             proutn("Kill ")
6699             proutn(device[i])
6700             proutn("? ")
6701             chew()
6702             key = scan()
6703             if key == IHALPHA and isit("y"):
6704                 game.damage[i] = 10.0
6705     proutn("Examine/change events? ")
6706     if ja() == True:
6707         ev = event()
6708         w = coord()
6709         legends = {
6710             FSNOVA:  "Supernova       ",
6711             FTBEAM:  "T Beam          ",
6712             FSNAP:   "Snapshot        ",
6713             FBATTAK: "Base Attack     ",
6714             FCDBAS:  "Base Destroy    ",
6715             FSCMOVE: "SC Move         ",
6716             FSCDBAS: "SC Base Destroy ",
6717             FDSPROB: "Probe Move      ",
6718             FDISTR:  "Distress Call   ",
6719             FENSLV:  "Enslavement     ",
6720             FREPRO:  "Klingon Build   ",
6721         }
6722         for i in range(1, NEVENTS):
6723             proutn(legends[i])
6724             if is_scheduled(i):
6725                 proutn("%.2f" % (scheduled(i)-game.state.date))
6726                 if i == FENSLV or i == FREPRO:
6727                     ev = findevent(i)
6728                     proutn(" in %s" % ev.quadrant)
6729             else:
6730                 proutn("never")
6731             proutn("? ")
6732             chew()
6733             key = scan()
6734             if key == 'n':
6735                 unschedule(i)
6736                 chew()
6737             elif key == IHREAL:
6738                 ev = schedule(i, aaitem)
6739                 if i == FENSLV or i == FREPRO:
6740                     chew()
6741                     proutn("In quadrant- ")
6742                     key = scan()
6743                     # IHEOL says to leave coordinates as they are 
6744                     if key != IHEOL:
6745                         if key != IHREAL:
6746                             prout("Event %d canceled, no x coordinate." % (i))
6747                             unschedule(i)
6748                             continue
6749                         w.x = int(round(aaitem))
6750                         key = scan()
6751                         if key != IHREAL:
6752                             prout("Event %d canceled, no y coordinate." % (i))
6753                             unschedule(i)
6754                             continue
6755                         w.y = int(round(aaitem))
6756                         ev.quadrant = w
6757         chew()
6758     proutn("Induce supernova here? ")
6759     if ja() == True:
6760         game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova = True
6761         atover(True)
6762
6763 if __name__ == '__main__':
6764     try:
6765         global line, thing, game, idebug
6766         game = citem = aaitem = inqueue = None
6767         line = ''
6768         thing = coord()
6769         thing.angry = False
6770         game = gamestate()
6771         idebug = 0
6772         game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY)
6773         # Disable curses mode until the game logic is working.
6774         #    if os.getenv("TERM"):
6775         #       game.options |= OPTION_CURSES | OPTION_SHOWME
6776         #    else:
6777         game.options |= OPTION_TTY
6778         seed = int(time.time())
6779         (options, arguments) = getopt.getopt(sys.argv[1:], "r:tx")
6780         for (switch, val) in options:
6781             if switch == '-r':
6782                 try:
6783                     replayfp = open(val, "r")
6784                 except IOError:
6785                     sys.stderr.write("sst: can't open replay file %s\n" % val)
6786                     raise SystemExit, 1
6787                 try:
6788                     line = replayfp.readline().strip()
6789                     (leader, key, seed) = line.split()
6790                     seed = eval(seed)
6791                     sys.stderr.write("sst2k: seed set to %s\n" % seed)
6792                     line = replayfp.readline().strip()
6793                     arguments += line.split()[2:]
6794                 except ValueError:
6795                     sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
6796                     raise SystemExit(1)
6797                 game.options |= OPTION_TTY
6798                 game.options &=~ OPTION_CURSES
6799             elif switch == '-t':
6800                 game.options |= OPTION_TTY
6801                 game.options &=~ OPTION_CURSES
6802             elif switch == '-x':
6803                 idebug = True
6804             else:
6805                 sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n")
6806                 raise SystemExit, 1
6807         # where to save the input in case of bugs
6808         try:
6809             logfp = open("/usr/tmp/sst-input.log", "w")
6810         except IOError:
6811             sys.stderr.write("sst: warning, can't open logfile\n")
6812         if logfp:
6813             logfp.write("# seed %s\n" % seed)
6814             logfp.write("# options %s\n" % " ".join(arguments))
6815         random.seed(seed)
6816         iostart()
6817         if arguments:
6818             inqueue = arguments
6819         else:
6820             inqueue = None
6821         while True: # Play a game 
6822             setwnd(fullscreen_window)
6823             clrscr()
6824             prelim()
6825             setup(needprompt=not inqueue)
6826             if game.alldone:
6827                 score()
6828                 game.alldone = False
6829             else:
6830                 makemoves()
6831             skip(1)
6832             stars()
6833             skip(1)
6834             if game.tourn and game.alldone:
6835                 proutn(_("Do you want your score recorded?"))
6836                 if ja() == True:
6837                     chew2()
6838                     freeze(False)
6839             chew()
6840             proutn(_("Do you want to play again? "))
6841             if not ja():
6842                 break
6843         skip(1)
6844         prout(_("May the Great Bird of the Galaxy roost upon your home planet."))
6845         raise SystemExit, 0
6846     except KeyboardInterrupt:
6847         print""
6848         pass