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