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