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