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