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