864dfa1b78586e179b3bd614af62375ad2d3250e
[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 iostart():
3327     global stdscr, rows
3328     if not (game.options & OPTION_CURSES):
3329         ln_env = os.getenv("LINES")
3330         if ln_env:
3331             rows = ln_env
3332         else:
3333             rows = 25
3334     else:
3335         stdscr = curses.initscr()
3336         stdscr.keypad(True)
3337         curses.nonl()
3338         curses.cbreak()
3339         global fullscreen_window, srscan_window, report_window, status_window
3340         global lrscan_window, message_window, prompt_window
3341         (rows, columns)   = stdscr.getmaxyx()
3342         fullscreen_window = stdscr
3343         srscan_window     = curses.newwin(12, 25, 0,       0)
3344         report_window     = curses.newwin(11, 0,  1,       25)
3345         status_window     = curses.newwin(10, 0,  1,       39)
3346         lrscan_window     = curses.newwin(5,  0,  0,       64) 
3347         message_window    = curses.newwin(0,  0,  12,      0)
3348         prompt_window     = curses.newwin(1,  0,  rows-2,  0) 
3349         message_window.scrollok(True)
3350         setwnd(fullscreen_window)
3351
3352 def ioend():
3353     "Wrap up I/O.  Presently a stub."
3354     stdscr.keypad(False)
3355     curses.echo()
3356     curses.nocbreak()
3357     curses.endwin()
3358
3359 def waitfor():
3360     "Wait for user action -- OK to do nothing if on a TTY"
3361     if game.options & OPTION_CURSES:
3362         stdscr.getch()
3363
3364 def announce():
3365     skip(1)
3366     prouts(_("[ANOUNCEMENT ARRIVING...]"))
3367     skip(1)
3368
3369 def pause_game():
3370     if game.skill > SKILL_FAIR:
3371         prompt = _("[CONTINUE?]")
3372     else:
3373         prompt = _("[PRESS ENTER TO CONTINUE]")
3374
3375     if game.options & OPTION_CURSES:
3376         drawmaps(0)
3377         setwnd(prompt_window)
3378         prompt_window.wclear()
3379         prompt_window.addstr(prompt)
3380         prompt_window.getstr()
3381         prompt_window.clear()
3382         prompt_window.refresh()
3383         setwnd(message_window)
3384     else:
3385         global linecount
3386         sys.stdout.write('\n')
3387         proutn(prompt)
3388         raw_input()
3389         for j in range(rows):
3390             sys.stdout.write('\n')
3391         linecount = 0
3392
3393 def skip(i):
3394     "Skip i lines.  Pause game if this would cause a scrolling event."
3395     for dummy in range(i):
3396         if game.options & OPTION_CURSES:
3397             (y, x) = curwnd.getyx()
3398             (my, mx) = curwnd.getmaxyx()
3399             if curwnd == message_window and y >= my - 3:
3400                 pause_game()
3401                 clrscr()
3402             else:
3403                 proutn("\n")
3404         else:
3405             global linecount
3406             linecount += 1
3407             if rows and linecount >= rows:
3408                 pause_game()
3409             else:
3410                 sys.stdout.write('\n')
3411
3412 def proutn(line):
3413     "Utter a line with no following line feed."
3414     if game.options & OPTION_CURSES:
3415         curwnd.addstr(line)
3416         curwnd.refresh()
3417     else:
3418         sys.stdout.write(line)
3419         sys.stdout.flush()
3420
3421 def prout(line):
3422     proutn(line)
3423     skip(1)
3424
3425 def prouts(line):
3426     "Emit slowly!" 
3427     for c in line:
3428         if not replayfp or replayfp.closed:     # Don't slow down replays
3429             time.sleep(0.03)
3430         proutn(c)
3431         if game.options & OPTION_CURSES:
3432             wrefresh(curwnd)
3433         else:
3434             sys.stdout.flush()
3435     if not replayfp or replayfp.closed:
3436         time.sleep(0.03)
3437
3438 def cgetline():
3439     "Get a line of input."
3440     if game.options & OPTION_CURSES:
3441         line = curwnd.getstr() + "\n"
3442         curwnd.refresh()
3443     else:
3444         if replayfp and not replayfp.closed:
3445             while True:
3446                 line = replayfp.readline()
3447                 proutn(line)
3448                 if line == '':
3449                     prout("*** Replay finished")
3450                     replayfp.close()
3451                     break
3452                 elif line[0] != "#":
3453                     break
3454         else:
3455             line = raw_input() + "\n"
3456     if logfp:
3457         logfp.write(line)
3458     return line
3459
3460 def setwnd(wnd):
3461     "Change windows -- OK for this to be a no-op in tty mode."
3462     global curwnd
3463     if game.options & OPTION_CURSES:
3464         curwnd = wnd
3465         curses.curs_set(wnd == fullscreen_window or wnd == message_window or wnd == prompt_window)
3466
3467 def clreol():
3468     "Clear to end of line -- can be a no-op in tty mode" 
3469     if game.options & OPTION_CURSES:
3470         wclrtoeol(curwnd)
3471         wrefresh(curwnd)
3472
3473 def clrscr():
3474     "Clear screen -- can be a no-op in tty mode."
3475     global linecount
3476     if game.options & OPTION_CURSES:
3477        curwnd.clear()
3478        curwnd.move(0, 0)
3479        curwnd.refresh()
3480     linecount = 0
3481     
3482 def highvideo():
3483     "Set highlight video, if this is reasonable."
3484     if game.options & OPTION_CURSES:
3485         curwnd.attron(curses.A_REVERSE)
3486  
3487 #
3488 # Things past this point have policy implications.
3489
3490
3491 def drawmaps(mode):
3492     "Hook to be called after moving to redraw maps."
3493     if game.options & OPTION_CURSES:
3494         if mode == 1:
3495             sensor()
3496         setwnd(srscan_window)
3497         curwnd.move(0, 0)
3498         srscan()
3499         if mode != 2:
3500             setwnd(status_window)
3501             status_window.clear()
3502             status_window.move(0, 0)
3503             setwnd(report_window)
3504             report_window.clear()
3505             report_window.move(0, 0)
3506             status()
3507             setwnd(lrscan_window)
3508             lrscan_window.clear()
3509             lrscan_window.move(0, 0)
3510             lrscan(silent=False)
3511
3512 def put_srscan_sym(w, sym):
3513     "Emit symbol for short-range scan."
3514     srscan_window.move(w.i+1, w.j*2+2)
3515     srscan_window.addch(sym)
3516     srscan_window.refresh()
3517
3518 def boom(w):
3519     "Enemy fall down, go boom."  
3520     if game.options & OPTION_CURSES:
3521         drawmaps(2)
3522         setwnd(srscan_window)
3523         srscan_window.attron(curses.A_REVERSE)
3524         put_srscan_sym(w, game.quad[w.i][w.j])
3525         #sound(500)
3526         #time.sleep(1.0)
3527         #nosound()
3528         srscan_window.attroff(curses.A_REVERSE)
3529         put_srscan_sym(w, game.quad[w.i][w.j])
3530         curses.delay_output(500)
3531         setwnd(message_window) 
3532
3533 def warble():
3534     "Sound and visual effects for teleportation."
3535     if game.options & OPTION_CURSES:
3536         drawmaps(2)
3537         setwnd(message_window)
3538         #sound(50)
3539     prouts("     . . . . .     ")
3540     if game.options & OPTION_CURSES:
3541         #curses.delay_output(1000)
3542         #nosound()
3543         pass
3544
3545 def tracktorpedo(origin, w, step, i, n, iquad):
3546     "Torpedo-track animation." 
3547     if not game.options & OPTION_CURSES:
3548         if step == 1:
3549             if n != 1:
3550                 skip(1)
3551                 proutn(_("Track for %s torpedo number %d-  ") % (game.quad[origin.i][origin.j],i+1))
3552             else:
3553                 skip(1)
3554                 proutn(_("Torpedo track- "))
3555         elif step==4 or step==9: 
3556             skip(1)
3557         proutn("%s   " % w)
3558     else:
3559         if not damaged(DSRSENS) or game.condition=="docked":
3560             if i != 0 and step == 1:
3561                 drawmaps(2)
3562                 time.sleep(0.4)
3563             if (iquad==IHDOT) or (iquad==IHBLANK):
3564                 put_srscan_sym(w, '+')
3565                 #sound(step*10)
3566                 #time.sleep(0.1)
3567                 #nosound()
3568                 put_srscan_sym(w, iquad)
3569             else:
3570                 curwnd.attron(curses.A_REVERSE)
3571                 put_srscan_sym(w, iquad)
3572                 #sound(500)
3573                 #time.sleep(1.0)
3574                 #nosound()
3575                 curwnd.attroff(curses.A_REVERSE)
3576                 put_srscan_sym(w, iquad)
3577         else:
3578             proutn("%s   " % w)
3579
3580 def makechart():
3581     "Display the current galaxy chart."
3582     if game.options & OPTION_CURSES:
3583         setwnd(message_window)
3584         message_window.clear()
3585     chart()
3586     if game.options & OPTION_TTY:
3587         skip(1)
3588
3589 NSYM    = 14
3590
3591 def prstat(txt, data):
3592     proutn(txt)
3593     if game.options & OPTION_CURSES:
3594         skip(1)
3595         setwnd(status_window)
3596     else:
3597         proutn(" " * (NSYM - len(txt)))
3598     proutn(data)
3599     skip(1)
3600     if game.options & OPTION_CURSES:
3601         setwnd(report_window)
3602
3603 # Code from moving.c begins here
3604
3605 def imove(novapush):
3606     "Movement execution for warp, impulse, supernova, and tractor-beam events."
3607     w = coord(); final = coord()
3608     trbeam = False
3609
3610     def no_quad_change():
3611         # No quadrant change -- compute new average enemy distances 
3612         game.quad[game.sector.i][game.sector.j] = game.ship
3613         if game.enemies:
3614             for enemy in game.enemies:
3615                 finald = (w-enemy.kloc).distance()
3616                 enemy.kavgd = 0.5 * (finald + enemy.kdist)
3617                 enemy.kdist = finald
3618             game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
3619             if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3620                 attack(torps_ok=False)
3621             for enemy in game.enemies:
3622                 enemy.kavgd = enemy.kdist
3623         newcnd()
3624         drawmaps(0)
3625         setwnd(message_window)
3626     w.i = w.j = 0
3627     if game.inorbit:
3628         prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
3629         game.inorbit = False
3630     angle = ((15.0 - game.direc) * 0.5235988)
3631     deltax = -math.sin(angle)
3632     deltay = math.cos(angle)
3633     if math.fabs(deltax) > math.fabs(deltay):
3634         bigger = math.fabs(deltax)
3635     else:
3636         bigger = math.fabs(deltay)
3637     deltay /= bigger
3638     deltax /= bigger
3639     # If tractor beam is to occur, don't move full distance 
3640     if game.state.date+game.optime >= scheduled(FTBEAM):
3641         trbeam = True
3642         game.condition = "red"
3643         game.dist = game.dist*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
3644         game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
3645     # Move within the quadrant 
3646     game.quad[game.sector.i][game.sector.j] = IHDOT
3647     x = game.sector.i
3648     y = game.sector.j
3649     n = int(10.0*game.dist*bigger+0.5)
3650     if n > 0:
3651         for m in range(1, n+1):
3652             x += deltax
3653             y += deltay
3654             w.i = int(round(x))
3655             w.j = int(round(y))
3656             if not VALID_SECTOR(w.i, w.j):
3657                 # Leaving quadrant -- allow final enemy attack 
3658                 # Don't do it if being pushed by Nova 
3659                 if len(game.enemies) != 0 and not novapush:
3660                     newcnd()
3661                     for enemy in game.enemies:
3662                         finald = (w - enemy.kloc).distance()
3663                         enemy.kavgd = 0.5 * (finald + enemy.kdist)
3664                     #
3665                     # Stas Sergeev added the condition
3666                     # that attacks only happen if Klingons
3667                     # are present and your skill is good.
3668                     # 
3669                     if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3670                         attack(torps_ok=False)
3671                     if game.alldone:
3672                         return
3673                 # compute final position -- new quadrant and sector 
3674                 x = (QUADSIZE*game.quadrant.i)+game.sector.i
3675                 y = (QUADSIZE*game.quadrant.j)+game.sector.j
3676                 w.i = int(round(x+10.0*game.dist*bigger*deltax))
3677                 w.j = int(round(y+10.0*game.dist*bigger*deltay))
3678                 # check for edge of galaxy 
3679                 kinks = 0
3680                 while True:
3681                     kink = False
3682                     if w.i < 0:
3683                         w.i = -w.i
3684                         kink = True
3685                     if w.j < 0:
3686                         w.j = -w.j
3687                         kink = True
3688                     if w.i >= GALSIZE*QUADSIZE:
3689                         w.i = (GALSIZE*QUADSIZE*2) - w.i
3690                         kink = True
3691                     if w.j >= GALSIZE*QUADSIZE:
3692                         w.j = (GALSIZE*QUADSIZE*2) - w.j
3693                         kink = True
3694                     if kink:
3695                         kinks += 1
3696                     else:
3697                         break
3698                 if kinks:
3699                     game.nkinks += 1
3700                     if game.nkinks == 3:
3701                         # Three strikes -- you're out! 
3702                         finish(FNEG3)
3703                         return
3704                     skip(1)
3705                     prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
3706                     prout(_("AT THE EDGE OF THE GALAXY.  THE THIRD TIME YOU TRY THIS,"))
3707                     prout(_("YOU WILL BE DESTROYED."))
3708                 # Compute final position in new quadrant 
3709                 if trbeam: # Don't bother if we are to be beamed 
3710                     return
3711                 game.quadrant.i = w.i/QUADSIZE
3712                 game.quadrant.j = w.j/QUADSIZE
3713                 game.sector.i = w.i - (QUADSIZE*game.quadrant.i)
3714                 game.sector.j = w.j - (QUADSIZE*game.quadrant.j)
3715                 skip(1)
3716                 prout(_("Entering Quadrant %s.") % game.quadrant)
3717                 game.quad[game.sector.i][game.sector.j] = game.ship
3718                 newqad()
3719                 if game.skill>SKILL_NOVICE:
3720                     attack(torps_ok=False)  
3721                 return
3722             iquad = game.quad[w.i][w.j]
3723             if iquad != IHDOT:
3724                 # object encountered in flight path 
3725                 stopegy = 50.0*game.dist/game.optime
3726                 game.dist = (game.sector - w).distance() / (QUADSIZE * 1.0)
3727                 if iquad in (IHT, IHK, IHC, IHS, IHR, IHQUEST):
3728                     game.sector = w
3729                     for enemy in game.enemies:
3730                         if enemy.kloc == game.sector:
3731                             break
3732                     collision(rammed=False, enemy=enemy)
3733                     final = game.sector
3734                 elif iquad == IHBLANK:
3735                     skip(1)
3736                     prouts(_("***RED ALERT!  RED ALERT!"))
3737                     skip(1)
3738                     proutn("***" + crmshp())
3739                     proutn(_(" pulled into black hole at Sector %s") % w)
3740                     #
3741                     # Getting pulled into a black hole was certain
3742                     # death in Almy's original.  Stas Sergeev added a
3743                     # possibility that you'll get timewarped instead.
3744                     # 
3745                     n=0
3746                     for m in range(NDEVICES):
3747                         if game.damage[m]>0: 
3748                             n += 1
3749                     probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
3750                     if (game.options & OPTION_BLKHOLE) and withprob(1-probf): 
3751                         timwrp()
3752                     else: 
3753                         finish(FHOLE)
3754                     return
3755                 else:
3756                     # something else 
3757                     skip(1)
3758                     proutn(crmshp())
3759                     if iquad == IHWEB:
3760                         prout(_(" encounters Tholian web at %s;") % w)
3761                     else:
3762                         prout(_(" blocked by object at %s;") % w)
3763                     proutn(_("Emergency stop required "))
3764                     prout(_("%2d units of energy.") % int(stopegy))
3765                     game.energy -= stopegy
3766                     final.i = int(round(deltax))
3767                     final.j = int(round(deltay))
3768                     game.sector = final
3769                     if game.energy <= 0:
3770                         finish(FNRG)
3771                         return
3772                 # We're here!
3773                 no_quad_change()
3774                 return
3775         game.dist = (game.sector - w).distance() / (QUADSIZE * 1.0)
3776         game.sector = w
3777     final = game.sector
3778     no_quad_change()
3779     return
3780
3781 def dock(verbose):
3782     "Dock our ship at a starbase."
3783     scanner.chew()
3784     if game.condition == "docked" and verbose:
3785         prout(_("Already docked."))
3786         return
3787     if game.inorbit:
3788         prout(_("You must first leave standard orbit."))
3789         return
3790     if not game.base.is_valid() or abs(game.sector.i-game.base.i) > 1 or abs(game.sector.j-game.base.j) > 1:
3791         prout(crmshp() + _(" not adjacent to base."))
3792         return
3793     game.condition = "docked"
3794     if "verbose":
3795         prout(_("Docked."))
3796     game.ididit = True
3797     if game.energy < game.inenrg:
3798         game.energy = game.inenrg
3799     game.shield = game.inshld
3800     game.torps = game.intorps
3801     game.lsupres = game.inlsr
3802     game.state.crew = FULLCREW
3803     if not damaged(DRADIO) and \
3804         ((is_scheduled(FCDBAS) or game.isatb == 1) and not game.iseenit):
3805         # get attack report from base 
3806         prout(_("Lt. Uhura- \"Captain, an important message from the starbase:\""))
3807         attackreport(False)
3808         game.iseenit = True
3809  
3810 # This program originally required input in terms of a (clock)
3811 # direction and distance. Somewhere in history, it was changed to
3812 # cartesian coordinates. So we need to convert.  Probably
3813 # "manual" input should still be done this way -- it's a real
3814 # pain if the computer isn't working! Manual mode is still confusing
3815 # because it involves giving x and y motions, yet the coordinates
3816 # are always displayed y - x, where +y is downward!
3817
3818 def getcourse(isprobe, akey):
3819     "Get a course and distance from the user."
3820     key = 0
3821     dquad = copy.copy(game.quadrant)
3822     navmode = "unspecified"
3823     itemp = "curt"
3824     dsect = coord()
3825     iprompt = False
3826     if game.landed and not isprobe:
3827         prout(_("Dummy! You can't leave standard orbit until you"))
3828         proutn(_("are back aboard the ship."))
3829         scanner.chew()
3830         return False
3831     while navmode == "unspecified":
3832         if damaged(DNAVSYS):
3833             if isprobe:
3834                 prout(_("Computer damaged; manual navigation only"))
3835             else:
3836                 prout(_("Computer damaged; manual movement only"))
3837             scanner.chew()
3838             navmode = "manual"
3839             key = "IHEOL"
3840             break
3841         if isprobe and akey != -1:
3842             # For probe launch, use pre-scanned value first time 
3843             key = akey
3844             akey = -1
3845         else: 
3846             key = scanner.next()
3847         if key == "IHEOL":
3848             proutn(_("Manual or automatic- "))
3849             iprompt = True
3850             scanner.chew()
3851         elif key == "IHALPHA":
3852             if scanner.sees("manual"):
3853                 navmode = "manual"
3854                 key = scanner.next()
3855                 break
3856             elif scanner.sees("automatic"):
3857                 navmode = "automatic"
3858                 key = scanner.next()
3859                 break
3860             else:
3861                 huh()
3862                 scanner.chew()
3863                 return False
3864         else: # numeric 
3865             if isprobe:
3866                 prout(_("(Manual navigation assumed.)"))
3867             else:
3868                 prout(_("(Manual movement assumed.)"))
3869             navmode = "manual"
3870             break
3871     if navmode == "automatic":
3872         while key == "IHEOL":
3873             if isprobe:
3874                 proutn(_("Target quadrant or quadrant&sector- "))
3875             else:
3876                 proutn(_("Destination sector or quadrant&sector- "))
3877             scanner.chew()
3878             iprompt = True
3879             key = scanner.next()
3880         if key != "IHREAL":
3881             huh()
3882             return False
3883         xi = int(round(scanner.real))-1
3884         key = scanner.next()
3885         if key != "IHREAL":
3886             huh()
3887             return False
3888         xj = int(round(scanner.real))-1
3889         key = scanner.next()
3890         if key == "IHREAL":
3891             # both quadrant and sector specified 
3892             xk = int(round(scanner.real))-1
3893             key = scanner.next()
3894             if key != "IHREAL":
3895                 huh()
3896                 return False
3897             xl = int(round(scanner.real))-1
3898             dquad.i = xi
3899             dquad.j = xj
3900             dsect.i = xk
3901             dsect.j = xl
3902         else:
3903             # only one pair of numbers was specified
3904             if isprobe:
3905                 # only quadrant specified -- go to center of dest quad 
3906                 dquad.i = xi
3907                 dquad.j = xj
3908                 dsect.j = dsect.i = 4   # preserves 1-origin behavior
3909             else:
3910                 # only sector specified
3911                 dsect.i = xi
3912                 dsect.j = xj
3913             itemp = "normal"
3914         if not VALID_QUADRANT(dquad.i,dquad.j) or not VALID_SECTOR(dsect.i,dsect.j):
3915             huh()
3916             return False
3917         skip(1)
3918         if not isprobe:
3919             if itemp > "curt":
3920                 if iprompt:
3921                     prout(_("Helmsman Sulu- \"Course locked in for Sector %s.\"") % dsect)
3922             else:
3923                 prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
3924         # the actual deltas get computed here
3925         delta = coord()
3926         delta.i = dquad.j-game.quadrant.j + 0.1*(dsect.j-game.sector.j)
3927         delta.j = game.quadrant.i-dquad.i + 0.1*(game.sector.i-dsect.i)
3928     else: # manual 
3929         while key == "IHEOL":
3930             proutn(_("X and Y displacements- "))
3931             scanner.chew()
3932             iprompt = True
3933             key = scanner.next()
3934         itemp = "verbose"
3935         if key != "IHREAL":
3936             huh()
3937             return False
3938         delta.j = scanner.real
3939         key = scanner.next()
3940         if key != "IHREAL":
3941             huh()
3942             return False
3943         delta.i = scanner.real
3944     # Check for zero movement 
3945     if delta.i == 0 and delta.j == 0:
3946         scanner.chew()
3947         return False
3948     if itemp == "verbose" and not isprobe:
3949         skip(1)
3950         prout(_("Helmsman Sulu- \"Aye, Sir.\""))
3951     # Course actually laid in.
3952     game.dist = delta.distance()
3953     game.direc = delta.bearing()
3954     if game.direc < 0.0:
3955         game.direc += 12.0
3956     scanner.chew()
3957     return True
3958
3959 def impulse():
3960     "Move under impulse power."
3961     game.ididit = False
3962     if damaged(DIMPULS):
3963         scanner.chew()
3964         skip(1)
3965         prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
3966         return
3967     if game.energy > 30.0:
3968         if not getcourse(isprobe=False, akey=0):
3969             return
3970         power = 20.0 + 100.0*game.dist
3971     else:
3972         power = 30.0
3973     if power >= game.energy:
3974         # Insufficient power for trip 
3975         skip(1)
3976         prout(_("First Officer Spock- \"Captain, the impulse engines"))
3977         prout(_("require 20.0 units to engage, plus 100.0 units per"))
3978         if game.energy > 30:
3979             proutn(_("quadrant.  We can go, therefore, a maximum of %d") %
3980                      int(0.01 * (game.energy-20.0)-0.05))
3981             prout(_(" quadrants.\""))
3982         else:
3983             prout(_("quadrant.  They are, therefore, useless.\""))
3984         scanner.chew()
3985         return
3986     # Make sure enough time is left for the trip 
3987     game.optime = game.dist/0.095
3988     if game.optime >= game.state.remtime:
3989         prout(_("First Officer Spock- \"Captain, our speed under impulse"))
3990         prout(_("power is only 0.95 sectors per stardate. Are you sure"))
3991         proutn(_("we dare spend the time?\" "))
3992         if ja() == False:
3993             return
3994     # Activate impulse engines and pay the cost 
3995     imove(novapush=False)
3996     game.ididit = True
3997     if game.alldone:
3998         return
3999     power = 20.0 + 100.0*game.dist
4000     game.energy -= power
4001     game.optime = game.dist/0.095
4002     if game.energy <= 0:
4003         finish(FNRG)
4004     return
4005
4006 def warp(timewarp):
4007     "ove under warp drive."
4008     blooey = False; twarp = False
4009     if not timewarp: # Not WARPX entry 
4010         game.ididit = False
4011         if game.damage[DWARPEN] > 10.0:
4012             scanner.chew()
4013             skip(1)
4014             prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
4015             return
4016         if damaged(DWARPEN) and game.warpfac > 4.0:
4017             scanner.chew()
4018             skip(1)
4019             prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
4020             prout(_("  is repaired, I can only give you warp 4.\""))
4021             return
4022         # Read in course and distance 
4023         if not getcourse(isprobe=False, akey=0):
4024             return
4025         # Make sure starship has enough energy for the trip 
4026         power = (game.dist+0.05)*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
4027         if power >= game.energy:
4028             # Insufficient power for trip 
4029             game.ididit = False
4030             skip(1)
4031             prout(_("Engineering to bridge--"))
4032             if not game.shldup or 0.5*power > game.energy:
4033                 iwarp = math.pow((game.energy/(game.dist+0.05)), 0.333333333)
4034                 if iwarp <= 0:
4035                     prout(_("We can't do it, Captain. We don't have enough energy."))
4036                 else:
4037                     proutn(_("We don't have enough energy, but we could do it at warp %d") % iwarp)
4038                     if game.shldup:
4039                         prout(",")
4040                         prout(_("if you'll lower the shields."))
4041                     else:
4042                         prout(".")
4043             else:
4044                 prout(_("We haven't the energy to go that far with the shields up."))
4045             return
4046                                                 
4047         # Make sure enough time is left for the trip 
4048         game.optime = 10.0*game.dist/game.warpfac**2
4049         if game.optime >= 0.8*game.state.remtime:
4050             skip(1)
4051             prout(_("First Officer Spock- \"Captain, I compute that such"))
4052             proutn(_("  a trip would require approximately %2.0f") %
4053                    (100.0*game.optime/game.state.remtime))
4054             prout(_(" percent of our"))
4055             proutn(_("  remaining time.  Are you sure this is wise?\" "))
4056             if ja() == False:
4057                 game.ididit = False
4058                 game.optime=0 
4059                 return
4060     # Entry WARPX 
4061     if game.warpfac > 6.0:
4062         # Decide if engine damage will occur
4063         # ESR: Seems wrong. Probability of damage goes *down* with distance? 
4064         prob = game.dist*(6.0-game.warpfac)**2/66.666666666
4065         if prob > randreal():
4066             blooey = True
4067             game.dist = randreal(game.dist)
4068         # Decide if time warp will occur 
4069         if 0.5*game.dist*math.pow(7.0,game.warpfac-10.0) > randreal():
4070             twarp = True
4071         if idebug and game.warpfac==10 and not twarp:
4072             blooey = False
4073             proutn("=== Force time warp? ")
4074             if ja() == True:
4075                 twarp = True
4076         if blooey or twarp:
4077             # If time warp or engine damage, check path 
4078             # If it is obstructed, don't do warp or damage 
4079             angle = ((15.0-game.direc)*0.5235998)
4080             deltax = -math.sin(angle)
4081             deltay = math.cos(angle)
4082             if math.fabs(deltax) > math.fabs(deltay):
4083                 bigger = math.fabs(deltax)
4084             else:
4085                 bigger = math.fabs(deltay)
4086             deltax /= bigger
4087             deltay /= bigger
4088             n = 10.0 * game.dist * bigger +0.5
4089             x = game.sector.i
4090             y = game.sector.j
4091             for l in range(1, n+1):
4092                 x += deltax
4093                 ix = x + 0.5
4094                 y += deltay
4095                 iy = y +0.5
4096                 if not VALID_SECTOR(ix, iy):
4097                     break
4098                 if game.quad[ix][iy] != IHDOT:
4099                     blooey = False
4100                     twarp = False
4101     # Activate Warp Engines and pay the cost 
4102     imove(novapush=False)
4103     if game.alldone:
4104         return
4105     game.energy -= game.dist*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
4106     if game.energy <= 0:
4107         finish(FNRG)
4108     game.optime = 10.0*game.dist/game.warpfac**2
4109     if twarp:
4110         timwrp()
4111     if blooey:
4112         game.damage[DWARPEN] = game.damfac * randreal(1.0, 4.0)
4113         skip(1)
4114         prout(_("Engineering to bridge--"))
4115         prout(_("  Scott here.  The warp engines are damaged."))
4116         prout(_("  We'll have to reduce speed to warp 4."))
4117     game.ididit = True
4118     return
4119
4120 def setwarp():
4121     "Change the warp factor."
4122     while True:
4123         key=scanner.next()
4124         if key != "IHEOL":
4125             break
4126         scanner.chew()
4127         proutn(_("Warp factor- "))
4128     scanner.chew()
4129     if key != "IHREAL":
4130         huh()
4131         return
4132     if game.damage[DWARPEN] > 10.0:
4133         prout(_("Warp engines inoperative."))
4134         return
4135     if damaged(DWARPEN) and scanner.real > 4.0:
4136         prout(_("Engineer Scott- \"I'm doing my best, Captain,"))
4137         prout(_("  but right now we can only go warp 4.\""))
4138         return
4139     if scanner.real > 10.0:
4140         prout(_("Helmsman Sulu- \"Our top speed is warp 10, Captain.\""))
4141         return
4142     if scanner.real < 1.0:
4143         prout(_("Helmsman Sulu- \"We can't go below warp 1, Captain.\""))
4144         return
4145     oldfac = game.warpfac
4146     game.warpfac = scanner.real
4147     if game.warpfac <= oldfac or game.warpfac <= 6.0:
4148         prout(_("Helmsman Sulu- \"Warp factor %d, Captain.\"") %
4149                int(game.warpfac))
4150         return
4151     if game.warpfac < 8.00:
4152         prout(_("Engineer Scott- \"Aye, but our maximum safe speed is warp 6.\""))
4153         return
4154     if game.warpfac == 10.0:
4155         prout(_("Engineer Scott- \"Aye, Captain, we'll try it.\""))
4156         return
4157     prout(_("Engineer Scott- \"Aye, Captain, but our engines may not take it.\""))
4158     return
4159
4160 def atover(igrab):
4161     "Cope with being tossed out of quadrant by supernova or yanked by beam."
4162     scanner.chew()
4163     # is captain on planet? 
4164     if game.landed:
4165         if damaged(DTRANSP):
4166             finish(FPNOVA)
4167             return
4168         prout(_("Scotty rushes to the transporter controls."))
4169         if game.shldup:
4170             prout(_("But with the shields up it's hopeless."))
4171             finish(FPNOVA)
4172         prouts(_("His desperate attempt to rescue you . . ."))
4173         if withprob(0.5):
4174             prout(_("fails."))
4175             finish(FPNOVA)
4176             return
4177         prout(_("SUCCEEDS!"))
4178         if game.imine:
4179             game.imine = False
4180             proutn(_("The crystals mined were "))
4181             if withprob(0.25):
4182                 prout(_("lost."))
4183             else:
4184                 prout(_("saved."))
4185                 game.icrystl = True
4186     if igrab:
4187         return
4188     # Check to see if captain in shuttle craft 
4189     if game.icraft:
4190         finish(FSTRACTOR)
4191     if game.alldone:
4192         return
4193     # Inform captain of attempt to reach safety 
4194     skip(1)
4195     while True:
4196         if game.justin:
4197             prouts(_("***RED ALERT!  RED ALERT!"))
4198             skip(1)
4199             proutn(_("The %s has stopped in a quadrant containing") % crmshp())
4200             prouts(_("   a supernova."))
4201             skip(2)
4202         prout(_("***Emergency automatic override attempts to hurl ")+crmshp())
4203         skip(1)
4204         prout(_("safely out of quadrant."))
4205         if not damaged(DRADIO):
4206             game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
4207         # Try to use warp engines 
4208         if damaged(DWARPEN):
4209             skip(1)
4210             prout(_("Warp engines damaged."))
4211             finish(FSNOVAED)
4212             return
4213         game.warpfac = randreal(6.0, 8.0)
4214         prout(_("Warp factor set to %d") % int(game.warpfac))
4215         power = 0.75*game.energy
4216         game.dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1))
4217         distreq = randreal(math.sqrt(2))
4218         if distreq < game.dist:
4219             game.dist = distreq
4220         game.optime = 10.0*game.dist/game.warpfac**2
4221         game.direc = randreal(12)       # How dumb! 
4222         game.justin = False
4223         game.inorbit = False
4224         warp(True)
4225         if not game.justin:
4226             # This is bad news, we didn't leave quadrant. 
4227             if game.alldone:
4228                 return
4229             skip(1)
4230             prout(_("Insufficient energy to leave quadrant."))
4231             finish(FSNOVAED)
4232             return
4233         # Repeat if another snova
4234         if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
4235             break
4236     if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0: 
4237         finish(FWON) # Snova killed remaining enemy. 
4238
4239 def timwrp():
4240     "Let's do the time warp again."
4241     prout(_("***TIME WARP ENTERED."))
4242     if game.state.snap and withprob(0.5):
4243         # Go back in time 
4244         prout(_("You are traveling backwards in time %d stardates.") %
4245               int(game.state.date-game.snapsht.date))
4246         game.state = game.snapsht
4247         game.state.snap = False
4248         if len(game.state.kcmdr):
4249             schedule(FTBEAM, expran(game.intime/len(game.state.kcmdr)))
4250             schedule(FBATTAK, expran(0.3*game.intime))
4251         schedule(FSNOVA, expran(0.5*game.intime))
4252         # next snapshot will be sooner 
4253         schedule(FSNAP, expran(0.25*game.state.remtime))
4254                                 
4255         if game.state.nscrem:
4256             schedule(FSCMOVE, 0.2777)       
4257         game.isatb = 0
4258         unschedule(FCDBAS)
4259         unschedule(FSCDBAS)
4260         game.battle.invalidate()
4261
4262         # Make sure Galileo is consistant -- Snapshot may have been taken
4263         # when on planet, which would give us two Galileos! 
4264         gotit = False
4265         for l in range(game.inplan):
4266             if game.state.planets[l].known == "shuttle_down":
4267                 gotit = True
4268                 if game.iscraft == "onship" and game.ship==IHE:
4269                     prout(_("Chekov-  \"Security reports the Galileo has disappeared, Sir!"))
4270                     game.iscraft = "offship"
4271         # Likewise, if in the original time the Galileo was abandoned, but
4272         # was on ship earlier, it would have vanished -- let's restore it.
4273         if game.iscraft == "offship" and not gotit and game.damage[DSHUTTL] >= 0.0:
4274             prout(_("Checkov-  \"Security reports the Galileo has reappeared in the dock!\""))
4275             game.iscraft = "onship"
4276         # There used to be code to do the actual reconstrction here,
4277         # but the starchart is now part of the snapshotted galaxy state.
4278         prout(_("Spock has reconstructed a correct star chart from memory"))
4279     else:
4280         # Go forward in time 
4281         game.optime = -0.5*game.intime*math.log(randreal())
4282         prout(_("You are traveling forward in time %d stardates.") % int(game.optime))
4283         # cheat to make sure no tractor beams occur during time warp 
4284         postpone(FTBEAM, game.optime)
4285         game.damage[DRADIO] += game.optime
4286     newqad()
4287     events()    # Stas Sergeev added this -- do pending events 
4288
4289 def probe():
4290     "Launch deep-space probe." 
4291     # New code to launch a deep space probe 
4292     if game.nprobes == 0:
4293         scanner.chew()
4294         skip(1)
4295         if game.ship == IHE: 
4296             prout(_("Engineer Scott- \"We have no more deep space probes, Sir.\""))
4297         else:
4298             prout(_("Ye Faerie Queene has no deep space probes."))
4299         return
4300     if damaged(DDSP):
4301         scanner.chew()
4302         skip(1)
4303         prout(_("Engineer Scott- \"The probe launcher is damaged, Sir.\""))
4304         return
4305     if is_scheduled(FDSPROB):
4306         scanner.chew()
4307         skip(1)
4308         if damaged(DRADIO) and game.condition != "docked":
4309             prout(_("Spock-  \"Records show the previous probe has not yet"))
4310             prout(_("   reached its destination.\""))
4311         else:
4312             prout(_("Uhura- \"The previous probe is still reporting data, Sir.\""))
4313         return
4314     key = scanner.next()
4315     if key == "IHEOL":
4316         # slow mode, so let Kirk know how many probes there are left
4317         if game.nprobes == 1:
4318             prout(_("1 probe left."))
4319         else:
4320             prout(_("%d probes left") % game.nprobes)
4321         proutn(_("Are you sure you want to fire a probe? "))
4322         if ja() == False:
4323             return
4324     game.isarmed = False
4325     if key == "IHALPHA" and scanner.token == "armed":
4326         game.isarmed = True
4327         key = scanner.next()
4328     elif key == "IHEOL":
4329         proutn(_("Arm NOVAMAX warhead? "))
4330         game.isarmed = ja()
4331     if not getcourse(isprobe=True, akey=key):
4332         return
4333     game.nprobes -= 1
4334     angle = ((15.0 - game.direc) * 0.5235988)
4335     game.probeinx = -math.sin(angle)
4336     game.probeiny = math.cos(angle)
4337     if math.fabs(game.probeinx) > math.fabs(game.probeiny):
4338         bigger = math.fabs(game.probeinx)
4339     else:
4340         bigger = math.fabs(game.probeiny)
4341     game.probeiny /= bigger
4342     game.probeinx /= bigger
4343     game.proben = 10.0*game.dist*bigger +0.5
4344     game.probex = game.quadrant.i*QUADSIZE + game.sector.i - 1  # We will use better packing than original
4345     game.probey = game.quadrant.j*QUADSIZE + game.sector.j - 1
4346     game.probec = game.quadrant
4347     schedule(FDSPROB, 0.01) # Time to move one sector
4348     prout(_("Ensign Chekov-  \"The deep space probe is launched, Captain.\""))
4349     game.ididit = True
4350     return
4351
4352 # Here's how the mayday code works:
4353
4354 # First, the closest starbase is selected.  If there is a a starbase
4355 # in your own quadrant, you are in good shape.  This distance takes
4356 # quadrant distances into account only.
4357 #
4358 # A magic number is computed based on the distance which acts as the
4359 # probability that you will be rematerialized.  You get three tries.
4360 #
4361 # When it is determined that you should be able to be rematerialized
4362 # (i.e., when the probability thing mentioned above comes up
4363 # positive), you are put into that quadrant (anywhere).  Then, we try
4364 # to see if there is a spot adjacent to the star- base.  If not, you
4365 # can't be rematerialized!!!  Otherwise, it drops you there.  It only
4366 # tries five times to find a spot to drop you.  After that, it's your
4367 # problem.
4368
4369 def mayday():
4370     "Yell for help from nearest starbase."
4371     # There's more than one way to move in this game! 
4372     scanner.chew()
4373     # Test for conditions which prevent calling for help 
4374     if game.condition == "docked":
4375         prout(_("Lt. Uhura-  \"But Captain, we're already docked.\""))
4376         return
4377     if damaged(DRADIO):
4378         prout(_("Subspace radio damaged."))
4379         return
4380     if not game.state.baseq:
4381         prout(_("Lt. Uhura-  \"Captain, I'm not getting any response from Starbase.\""))
4382         return
4383     if game.landed:
4384         prout(_("You must be aboard the %s.") % crmshp())
4385         return
4386     # OK -- call for help from nearest starbase 
4387     game.nhelp += 1
4388     if game.base.i!=0:
4389         # There's one in this quadrant 
4390         ddist = (game.base - game.sector).distance()
4391     else:
4392         ddist = FOREVER
4393         for ibq in game.state.baseq:
4394             xdist = QUADSIZE * (ibq - game.quadrant).distance()
4395             if xdist < ddist:
4396                 ddist = xdist
4397         # Since starbase not in quadrant, set up new quadrant 
4398         game.quadrant = ibq
4399         newqad()
4400     # dematerialize starship 
4401     game.quad[game.sector.i][game.sector.j]=IHDOT
4402     proutn(_("Starbase in Quadrant %s responds--%s dematerializes") \
4403            % (game.quadrant, crmshp()))
4404     game.sector.invalidate()
4405     for m in range(1, 5+1):
4406         w = game.base.scatter() 
4407         if VALID_SECTOR(w.i,w.j) and game.quad[w.i][w.j]==IHDOT:
4408             # found one -- finish up 
4409             game.sector = w
4410             break
4411     if not game.sector.is_valid():
4412         prout(_("You have been lost in space..."))
4413         finish(FMATERIALIZE)
4414         return
4415     # Give starbase three chances to rematerialize starship 
4416     probf = math.pow((1.0 - math.pow(0.98,ddist)), 0.33333333)
4417     for m in range(1, 3+1):
4418         if m == 1: proutn(_("1st"))
4419         elif m == 2: proutn(_("2nd"))
4420         elif m == 3: proutn(_("3rd"))
4421         proutn(_(" attempt to re-materialize ") + crmshp())
4422         game.quad[ix][iy]=(IHMATER0,IHMATER1,IHMATER2)[m-1]
4423         #textcolor("red")
4424         warble()
4425         if randreal() > probf:
4426             break
4427         prout(_("fails."))
4428         curses.delay_output(500)
4429         #textcolor(None)
4430     if m > 3:
4431         game.quad[ix][iy]=IHQUEST
4432         game.alive = False
4433         drawmaps(1)
4434         setwnd(message_window)
4435         finish(FMATERIALIZE)
4436         return
4437     game.quad[ix][iy]=game.ship
4438     #textcolor("green")
4439     prout(_("succeeds."))
4440     #textcolor(None)
4441     dock(False)
4442     skip(1)
4443     prout(_("Lt. Uhura-  \"Captain, we made it!\""))
4444
4445 # Abandon Ship (the BSD-Trek description)
4446
4447 # The ship is abandoned.  If your current ship is the Faire
4448 # Queene, or if your shuttlecraft is dead, you're out of
4449 # luck.  You need the shuttlecraft in order for the captain
4450 # (that's you!!) to escape.
4451
4452 # Your crew can beam to an inhabited starsystem in the
4453 # quadrant, if there is one and if the transporter is working.
4454 # If there is no inhabited starsystem, or if the transporter
4455 # is out, they are left to die in outer space.
4456
4457 # If there are no starbases left, you are captured by the
4458 # Klingons, who torture you mercilessly.  However, if there
4459 # is at least one starbase, you are returned to the
4460 # Federation in a prisoner of war exchange.  Of course, this
4461 # can't happen unless you have taken some prisoners.
4462
4463 def abandon():
4464     "Abandon ship."
4465     scanner.chew()
4466     if game.condition=="docked":
4467         if game.ship!=IHE:
4468             prout(_("You cannot abandon Ye Faerie Queene."))
4469             return
4470     else:
4471         # Must take shuttle craft to exit 
4472         if game.damage[DSHUTTL]==-1:
4473             prout(_("Ye Faerie Queene has no shuttle craft."))
4474             return
4475         if game.damage[DSHUTTL]<0:
4476             prout(_("Shuttle craft now serving Big Macs."))
4477             return
4478         if game.damage[DSHUTTL]>0:
4479             prout(_("Shuttle craft damaged."))
4480             return
4481         if game.landed:
4482             prout(_("You must be aboard the ship."))
4483             return
4484         if game.iscraft != "onship":
4485             prout(_("Shuttle craft not currently available."))
4486             return
4487         # Emit abandon ship messages 
4488         skip(1)
4489         prouts(_("***ABANDON SHIP!  ABANDON SHIP!"))
4490         skip(1)
4491         prouts(_("***ALL HANDS ABANDON SHIP!"))
4492         skip(2)
4493         prout(_("Captain and crew escape in shuttle craft."))
4494         if not game.state.baseq:
4495             # Oops! no place to go... 
4496             finish(FABANDN)
4497             return
4498         q = game.state.galaxy[game.quadrant.i][game.quadrant.j]
4499         # Dispose of crew 
4500         if not (game.options & OPTION_WORLDS) and not damaged(DTRANSP):
4501             prout(_("Remainder of ship's complement beam down"))
4502             prout(_("to nearest habitable planet."))
4503         elif q.planet != None and not damaged(DTRANSP):
4504             prout(_("Remainder of ship's complement beam down to %s.") %
4505                     q.planet)
4506         else:
4507             prout(_("Entire crew of %d left to die in outer space.") %
4508                     game.state.crew)
4509             game.casual += game.state.crew
4510             game.abandoned += game.state.crew
4511         # If at least one base left, give 'em the Faerie Queene 
4512         skip(1)
4513         game.icrystl = False # crystals are lost 
4514         game.nprobes = 0 # No probes 
4515         prout(_("You are captured by Klingons and released to"))
4516         prout(_("the Federation in a prisoner-of-war exchange."))
4517         nb = randrange(len(game.state.baseq))
4518         # Set up quadrant and position FQ adjacient to base 
4519         if not game.quadrant == game.state.baseq[nb]:
4520             game.quadrant = game.state.baseq[nb]
4521             game.sector.i = game.sector.j = 5
4522             newqad()
4523         while True:
4524             # position next to base by trial and error 
4525             game.quad[game.sector.i][game.sector.j] = IHDOT
4526             for l in range(QUADSIZE):
4527                 game.sector = game.base.scatter()
4528                 if VALID_SECTOR(game.sector.i, game.sector.j) and \
4529                        game.quad[game.sector.i][game.sector.j] == IHDOT:
4530                     break
4531             if l < QUADSIZE+1:
4532                 break # found a spot 
4533             game.sector.i=QUADSIZE/2
4534             game.sector.j=QUADSIZE/2
4535             newqad()
4536     # Get new commission 
4537     game.quad[game.sector.i][game.sector.j] = game.ship = IHF
4538     game.state.crew = FULLCREW
4539     prout(_("Starfleet puts you in command of another ship,"))
4540     prout(_("the Faerie Queene, which is antiquated but,"))
4541     prout(_("still useable."))
4542     if game.icrystl:
4543         prout(_("The dilithium crystals have been moved."))
4544     game.imine = False
4545     game.iscraft = "offship" # Galileo disappears 
4546     # Resupply ship 
4547     game.condition="docked"
4548     for l in range(NDEVICES): 
4549         game.damage[l] = 0.0
4550     game.damage[DSHUTTL] = -1
4551     game.energy = game.inenrg = 3000.0
4552     game.shield = game.inshld = 1250.0
4553     game.torps = game.intorps = 6
4554     game.lsupres=game.inlsr=3.0
4555     game.shldup=False
4556     game.warpfac=5.0
4557     return
4558
4559 # Code from planets.c begins here.
4560
4561 def consumeTime():
4562     "Abort a lengthy operation if an event interrupts it." 
4563     game.ididit = True
4564     events()
4565     if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova or game.justin: 
4566         return True
4567     return False
4568
4569 def survey():
4570     "Report on (uninhabited) planets in the galaxy."
4571     iknow = False
4572     skip(1)
4573     scanner.chew()
4574     prout(_("Spock-  \"Planet report follows, Captain.\""))
4575     skip(1)
4576     for i in range(game.inplan):
4577         if game.state.planets[i].pclass == "destroyed":
4578             continue
4579         if (game.state.planets[i].known != "unknown" \
4580             and not game.state.planets[i].inhabited) \
4581             or idebug:
4582             iknow = True
4583             if idebug and game.state.planets[i].known=="unknown":
4584                 proutn("(Unknown) ")
4585             proutn(_("Quadrant %s") % game.state.planets[i].quadrant)
4586             proutn(_("   class "))
4587             proutn(game.state.planets[i].pclass)
4588             proutn("   ")
4589             if game.state.planets[i].crystals != present:
4590                 proutn(_("no "))
4591             prout(_("dilithium crystals present."))
4592             if game.state.planets[i].known=="shuttle_down": 
4593                 prout(_("    Shuttle Craft Galileo on surface."))
4594     if not iknow:
4595         prout(_("No information available."))
4596
4597 def orbit():
4598     "Enter standard orbit." 
4599     skip(1)
4600     scanner.chew()
4601     if game.inorbit:
4602         prout(_("Already in standard orbit."))
4603         return
4604     if damaged(DWARPEN) and damaged(DIMPULS):
4605         prout(_("Both warp and impulse engines damaged."))
4606         return
4607     if not game.plnet.is_valid():
4608         prout("There is no planet in this sector.")
4609         return
4610     if abs(game.sector.i-game.plnet.i)>1 or abs(game.sector.j-game.plnet.j)>1:
4611         prout(crmshp() + _(" not adjacent to planet."))
4612         skip(1)
4613         return
4614     game.optime = randreal(0.02, 0.05)
4615     prout(_("Helmsman Sulu-  \"Entering standard orbit, Sir.\""))
4616     newcnd()
4617     if consumeTime():
4618         return
4619     game.height = randreal(1400, 8600)
4620     prout(_("Sulu-  \"Entered orbit at altitude %.2f kilometers.\"") % game.height)
4621     game.inorbit = True
4622     game.ididit = True
4623
4624 def sensor():
4625     "Examine planets in this quadrant."
4626     if damaged(DSRSENS):
4627         if game.options & OPTION_TTY:
4628             prout(_("Short range sensors damaged."))
4629         return
4630     if game.iplnet == None:
4631         if game.options & OPTION_TTY:
4632             prout(_("Spock- \"No planet in this quadrant, Captain.\""))
4633         return
4634     if game.iplnet.known == "unknown":
4635         prout(_("Spock-  \"Sensor scan for Quadrant %s-") % game.quadrant)
4636         skip(1)
4637         prout(_("         Planet at Sector %s is of class %s.") %
4638               (game.plnet, game.iplnet.pclass))
4639         if game.iplnet.known=="shuttle_down": 
4640             prout(_("         Sensors show Galileo still on surface."))
4641         proutn(_("         Readings indicate"))
4642         if game.iplnet.crystals != "present":
4643             proutn(_(" no"))
4644         prout(_(" dilithium crystals present.\""))
4645         if game.iplnet.known == "unknown":
4646             game.iplnet.known = "known"
4647     elif game.iplnet.inhabited:
4648         prout(_("Spock-  \"The inhabited planet %s ") % game.iplnet.name)
4649         prout(_("        is located at Sector %s, Captain.\"") % game.plnet)
4650
4651 def beam():
4652     "Use the transporter."
4653     nrgneed = 0
4654     scanner.chew()
4655     skip(1)
4656     if damaged(DTRANSP):
4657         prout(_("Transporter damaged."))
4658         if not damaged(DSHUTTL) and (game.iplnet.known=="shuttle_down" or game.iscraft == "onship"):
4659             skip(1)
4660             proutn(_("Spock-  \"May I suggest the shuttle craft, Sir?\" "))
4661             if ja() == True:
4662                 shuttle()
4663         return
4664     if not game.inorbit:
4665         prout(crmshp() + _(" not in standard orbit."))
4666         return
4667     if game.shldup:
4668         prout(_("Impossible to transport through shields."))
4669         return
4670     if game.iplnet.known=="unknown":
4671         prout(_("Spock-  \"Captain, we have no information on this planet"))
4672         prout(_("  and Starfleet Regulations clearly state that in this situation"))
4673         prout(_("  you may not go down.\""))
4674         return
4675     if not game.landed and game.iplnet.crystals=="absent":
4676         prout(_("Spock-  \"Captain, I fail to see the logic in"))
4677         prout(_("  exploring a planet with no dilithium crystals."))
4678         proutn(_("  Are you sure this is wise?\" "))
4679         if ja() == False:
4680             scanner.chew()
4681             return
4682     if not (game.options & OPTION_PLAIN):
4683         nrgneed = 50 * game.skill + game.height / 100.0
4684         if nrgneed > game.energy:
4685             prout(_("Engineering to bridge--"))
4686             prout(_("  Captain, we don't have enough energy for transportation."))
4687             return
4688         if not game.landed and nrgneed * 2 > game.energy:
4689             prout(_("Engineering to bridge--"))
4690             prout(_("  Captain, we have enough energy only to transport you down to"))
4691             prout(_("  the planet, but there wouldn't be an energy for the trip back."))
4692             if game.iplnet.known == "shuttle_down":
4693                 prout(_("  Although the Galileo shuttle craft may still be on a surface."))
4694             proutn(_("  Are you sure this is wise?\" "))
4695             if ja() == False:
4696                 scanner.chew()
4697                 return
4698     if game.landed:
4699         # Coming from planet 
4700         if game.iplnet.known=="shuttle_down":
4701             proutn(_("Spock-  \"Wouldn't you rather take the Galileo?\" "))
4702             if ja() == True:
4703                 scanner.chew()
4704                 return
4705             prout(_("Your crew hides the Galileo to prevent capture by aliens."))
4706         prout(_("Landing party assembled, ready to beam up."))
4707         skip(1)
4708         prout(_("Kirk whips out communicator..."))
4709         prouts(_("BEEP  BEEP  BEEP"))
4710         skip(2)
4711         prout(_("\"Kirk to enterprise-  Lock on coordinates...energize.\""))
4712     else:
4713         # Going to planet 
4714         prout(_("Scotty-  \"Transporter room ready, Sir.\""))
4715         skip(1)
4716         prout(_("Kirk and landing party prepare to beam down to planet surface."))
4717         skip(1)
4718         prout(_("Kirk-  \"Energize.\""))
4719     game.ididit = True
4720     skip(1)
4721     prouts("WWHOOOIIIIIRRRRREEEE.E.E.  .  .  .  .   .    .")
4722     skip(2)
4723     if withprob(0.98):
4724         prouts("BOOOIIIOOOIIOOOOIIIOIING . . .")
4725         skip(2)
4726         prout(_("Scotty-  \"Oh my God!  I've lost them.\""))
4727         finish(FLOST)
4728         return
4729     prouts(".    .   .  .  .  .  .E.E.EEEERRRRRIIIIIOOOHWW")
4730     game.landed = not game.landed
4731     game.energy -= nrgneed
4732     skip(2)
4733     prout(_("Transport complete."))
4734     if game.landed and game.iplnet.known=="shuttle_down":
4735         prout(_("The shuttle craft Galileo is here!"))
4736     if not game.landed and game.imine:
4737         game.icrystl = True
4738         game.cryprob = 0.05
4739     game.imine = False
4740     return
4741
4742 def mine():
4743     "Strip-mine a world for dilithium."
4744     skip(1)
4745     scanner.chew()
4746     if not game.landed:
4747         prout(_("Mining party not on planet."))
4748         return
4749     if game.iplnet.crystals == "mined":
4750         prout(_("This planet has already been strip-mined for dilithium."))
4751         return
4752     elif game.iplnet.crystals == "absent":
4753         prout(_("No dilithium crystals on this planet."))
4754         return
4755     if game.imine:
4756         prout(_("You've already mined enough crystals for this trip."))
4757         return
4758     if game.icrystl and game.cryprob == 0.05:
4759         prout(_("With all those fresh crystals aboard the ") + crmshp())
4760         prout(_("there's no reason to mine more at this time."))
4761         return
4762     game.optime = randreal(0.1, 0.3)*(ord(game.iplnet.pclass)-ord("L"))
4763     if consumeTime():
4764         return
4765     prout(_("Mining operation complete."))
4766     game.iplnet.crystals = "mined"
4767     game.imine = game.ididit = True
4768
4769 def usecrystals():
4770     "Use dilithium crystals."
4771     game.ididit = False
4772     skip(1)
4773     scanner.chew()
4774     if not game.icrystl:
4775         prout(_("No dilithium crystals available."))
4776         return
4777     if game.energy >= 1000:
4778         prout(_("Spock-  \"Captain, Starfleet Regulations prohibit such an operation"))
4779         prout(_("  except when Condition Yellow exists."))
4780         return
4781     prout(_("Spock- \"Captain, I must warn you that loading"))
4782     prout(_("  raw dilithium crystals into the ship's power"))
4783     prout(_("  system may risk a severe explosion."))
4784     proutn(_("  Are you sure this is wise?\" "))
4785     if ja() == False:
4786         scanner.chew()
4787         return
4788     skip(1)
4789     prout(_("Engineering Officer Scott-  \"(GULP) Aye Sir."))
4790     prout(_("  Mr. Spock and I will try it.\""))
4791     skip(1)
4792     prout(_("Spock-  \"Crystals in place, Sir."))
4793     prout(_("  Ready to activate circuit.\""))
4794     skip(1)
4795     prouts(_("Scotty-  \"Keep your fingers crossed, Sir!\""))
4796     skip(1)
4797     if with(game.cryprob):
4798         prouts(_("  \"Activating now! - - No good!  It's***"))
4799         skip(2)
4800         prouts(_("***RED ALERT!  RED A*L********************************"))
4801         skip(1)
4802         stars()
4803         prouts(_("******************   KA-BOOM!!!!   *******************"))
4804         skip(1)
4805         kaboom()
4806         return
4807     game.energy += randreal(5000.0, 5500.0)
4808     prouts(_("  \"Activating now! - - "))
4809     prout(_("The instruments"))
4810     prout(_("   are going crazy, but I think it's"))
4811     prout(_("   going to work!!  Congratulations, Sir!\""))
4812     game.cryprob *= 2.0
4813     game.ididit = True
4814
4815 def shuttle():
4816     "Use shuttlecraft for planetary jaunt."
4817     scanner.chew()
4818     skip(1)
4819     if damaged(DSHUTTL):
4820         if game.damage[DSHUTTL] == -1.0:
4821             if game.inorbit and game.iplnet.known == "shuttle_down":
4822                 prout(_("Ye Faerie Queene has no shuttle craft bay to dock it at."))
4823             else:
4824                 prout(_("Ye Faerie Queene had no shuttle craft."))
4825         elif game.damage[DSHUTTL] > 0:
4826             prout(_("The Galileo is damaged."))
4827         else: # game.damage[DSHUTTL] < 0  
4828             prout(_("Shuttle craft is now serving Big Macs."))
4829         return
4830     if not game.inorbit:
4831         prout(crmshp() + _(" not in standard orbit."))
4832         return
4833     if (game.iplnet.known != "shuttle_down") and game.iscraft != "onship":
4834         prout(_("Shuttle craft not currently available."))
4835         return
4836     if not game.landed and game.iplnet.known=="shuttle_down":
4837         prout(_("You will have to beam down to retrieve the shuttle craft."))
4838         return
4839     if game.shldup or game.condition == "docked":
4840         prout(_("Shuttle craft cannot pass through shields."))
4841         return
4842     if game.iplnet.known=="unknown":
4843         prout(_("Spock-  \"Captain, we have no information on this planet"))
4844         prout(_("  and Starfleet Regulations clearly state that in this situation"))
4845         prout(_("  you may not fly down.\""))
4846         return
4847     game.optime = 3.0e-5*game.height
4848     if game.optime >= 0.8*game.state.remtime:
4849         prout(_("First Officer Spock-  \"Captain, I compute that such"))
4850         proutn(_("  a maneuver would require approximately %2d%% of our") % \
4851                int(100*game.optime/game.state.remtime))
4852         prout(_("remaining time."))
4853         proutn(_("Are you sure this is wise?\" "))
4854         if ja() == False:
4855             game.optime = 0.0
4856             return
4857     if game.landed:
4858         # Kirk on planet 
4859         if game.iscraft == "onship":
4860             # Galileo on ship! 
4861             if not damaged(DTRANSP):
4862                 proutn(_("Spock-  \"Would you rather use the transporter?\" "))
4863                 if ja() == True:
4864                     beam()
4865                     return
4866                 proutn(_("Shuttle crew"))
4867             else:
4868                 proutn(_("Rescue party"))
4869             prout(_(" boards Galileo and swoops toward planet surface."))
4870             game.iscraft = "offship"
4871             skip(1)
4872             if consumeTime():
4873                 return
4874             game.iplnet.known="shuttle_down"
4875             prout(_("Trip complete."))
4876             return
4877         else:
4878             # Ready to go back to ship 
4879             prout(_("You and your mining party board the"))
4880             prout(_("shuttle craft for the trip back to the Enterprise."))
4881             skip(1)
4882             prouts(_("The short hop begins . . ."))
4883             skip(1)
4884             game.iplnet.known="known"
4885             game.icraft = True
4886             skip(1)
4887             game.landed = False
4888             if consumeTime():
4889                 return
4890             game.iscraft = "onship"
4891             game.icraft = False
4892             if game.imine:
4893                 game.icrystl = True
4894                 game.cryprob = 0.05
4895             game.imine = False
4896             prout(_("Trip complete."))
4897             return
4898     else:
4899         # Kirk on ship and so is Galileo 
4900         prout(_("Mining party assembles in the hangar deck,"))
4901         prout(_("ready to board the shuttle craft \"Galileo\"."))
4902         skip(1)
4903         prouts(_("The hangar doors open; the trip begins."))
4904         skip(1)
4905         game.icraft = True
4906         game.iscraft = "offship"
4907         if consumeTime():
4908             return
4909         game.iplnet.known = "shuttle_down"
4910         game.landed = True
4911         game.icraft = False
4912         prout(_("Trip complete."))
4913         return
4914
4915 def deathray():
4916     "Use the big zapper."
4917     game.ididit = False
4918     skip(1)
4919     scanner.chew()
4920     if game.ship != IHE:
4921         prout(_("Ye Faerie Queene has no death ray."))
4922         return
4923     if len(game.enemies)==0:
4924         prout(_("Sulu-  \"But Sir, there are no enemies in this quadrant.\""))
4925         return
4926     if damaged(DDRAY):
4927         prout(_("Death Ray is damaged."))
4928         return
4929     prout(_("Spock-  \"Captain, the 'Experimental Death Ray'"))
4930     prout(_("  is highly unpredictible.  Considering the alternatives,"))
4931     proutn(_("  are you sure this is wise?\" "))
4932     if ja() == False:
4933         return
4934     prout(_("Spock-  \"Acknowledged.\""))
4935     skip(1)
4936     game.ididit = True
4937     prouts(_("WHOOEE ... WHOOEE ... WHOOEE ... WHOOEE"))
4938     skip(1)
4939     prout(_("Crew scrambles in emergency preparation."))
4940     prout(_("Spock and Scotty ready the death ray and"))
4941     prout(_("prepare to channel all ship's power to the device."))
4942     skip(1)
4943     prout(_("Spock-  \"Preparations complete, sir.\""))
4944     prout(_("Kirk-  \"Engage!\""))
4945     skip(1)
4946     prouts(_("WHIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"))
4947     skip(1)
4948     dprob = 0.30
4949     if game.options & OPTION_PLAIN:
4950         dprob = 0.5
4951     r = randreal()
4952     if r > dprob:
4953         prouts(_("Sulu- \"Captain!  It's working!\""))
4954         skip(2)
4955         while len(game.enemies) > 0:
4956             deadkl(game.enemies[1].kloc, game.quad[game.enemies[1].kloc.i][game.enemies[1].kloc.j],game.enemies[1].kloc)
4957         prout(_("Ensign Chekov-  \"Congratulations, Captain!\""))
4958         if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) == 0:
4959             finish(FWON)    
4960         if (game.options & OPTION_PLAIN) == 0:
4961             prout(_("Spock-  \"Captain, I believe the `Experimental Death Ray'"))
4962             if withprob(0.05):
4963                 prout(_("   is still operational.\""))
4964             else:
4965                 prout(_("   has been rendered nonfunctional.\""))
4966                 game.damage[DDRAY] = 39.95
4967         return
4968     r = randreal()      # Pick failure method 
4969     if r <= 0.30:
4970         prouts(_("Sulu- \"Captain!  It's working!\""))
4971         skip(1)
4972         prouts(_("***RED ALERT!  RED ALERT!"))
4973         skip(1)
4974         prout(_("***MATTER-ANTIMATTER IMPLOSION IMMINENT!"))
4975         skip(1)
4976         prouts(_("***RED ALERT!  RED A*L********************************"))
4977         skip(1)
4978         stars()
4979         prouts(_("******************   KA-BOOM!!!!   *******************"))
4980         skip(1)
4981         kaboom()
4982         return
4983     if r <= 0.55:
4984         prouts(_("Sulu- \"Captain!  Yagabandaghangrapl, brachriigringlanbla!\""))
4985         skip(1)
4986         prout(_("Lt. Uhura-  \"Graaeek!  Graaeek!\""))
4987         skip(1)
4988         prout(_("Spock-  \"Fascinating!  . . . All humans aboard"))
4989         prout(_("  have apparently been transformed into strange mutations."))
4990         prout(_("  Vulcans do not seem to be affected."))
4991         skip(1)
4992         prout(_("Kirk-  \"Raauch!  Raauch!\""))
4993         finish(FDRAY)
4994         return
4995     if r <= 0.75:
4996         intj
4997         prouts(_("Sulu- \"Captain!  It's   --WHAT?!?!\""))
4998         skip(2)
4999         proutn(_("Spock-  \"I believe the word is"))
5000         prouts(_(" *ASTONISHING*"))
5001         prout(_(" Mr. Sulu."))
5002         for i in range(QUADSIZE):
5003             for j in range(QUADSIZE):
5004                 if game.quad[i][j] == IHDOT:
5005                     game.quad[i][j] = IHQUEST
5006         prout(_("  Captain, our quadrant is now infested with"))
5007         prouts(_(" - - - - - -  *THINGS*."))
5008         skip(1)
5009         prout(_("  I have no logical explanation.\""))
5010         return
5011     prouts(_("Sulu- \"Captain!  The Death Ray is creating tribbles!\""))
5012     skip(1)
5013     prout(_("Scotty-  \"There are so many tribbles down here"))
5014     prout(_("  in Engineering, we can't move for 'em, Captain.\""))
5015     finish(FTRIBBLE)
5016     return
5017
5018 # Code from reports.c begins here
5019
5020 def attackreport(curt):
5021     "eport status of bases under attack."
5022     if not curt:
5023         if is_scheduled(FCDBAS):
5024             prout(_("Starbase in Quadrant %s is currently under Commander attack.") % game.battle)
5025             prout(_("It can hold out until Stardate %d.") % int(scheduled(FCDBAS)))
5026         elif game.isatb == 1:
5027             prout(_("Starbase in Quadrant %s is under Super-commander attack.") % game.state.kscmdr)
5028             prout(_("It can hold out until Stardate %d.") % int(scheduled(FSCDBAS)))
5029         else:
5030             prout(_("No Starbase is currently under attack."))
5031     else:
5032         if is_scheduled(FCDBAS):
5033             proutn(_("Base in %s attacked by C. Alive until %.1f") % (game.battle, scheduled(FCDBAS)))
5034         if game.isatb:
5035             proutn(_("Base in %s attacked by S. Alive until %.1f") % (game.state.kscmdr, scheduled(FSCDBAS)))
5036         clreol()
5037
5038 def report():
5039     # report on general game status 
5040     scanner.chew()
5041     s1 = "" and game.thawed and _("thawed ")
5042     s2 = {1:"short", 2:"medium", 4:"long"}[game.length]
5043     s3 = (None, _("novice"). _("fair"),
5044           _("good"), _("expert"), _("emeritus"))[game.skill]
5045     prout(_("You %s a %s%s %s game.") % ((_("were playing"), _("are playing"))[game.alldone], s1, s2, s3))
5046     if game.skill>SKILL_GOOD and game.thawed and not game.alldone:
5047         prout(_("No plaque is allowed."))
5048     if game.tourn:
5049         prout(_("This is tournament game %d.") % game.tourn)
5050     prout(_("Your secret password is \"%s\"") % game.passwd)
5051     proutn(_("%d of %d Klingons have been killed") % (((game.inkling + game.incom + game.inscom) - (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)), 
5052            (game.inkling + game.incom + game.inscom)))
5053     if game.incom - len(game.state.kcmdr):
5054         prout(_(", including %d Commander%s.") % (game.incom - len(game.state.kcmdr), (_("s"), "")[(game.incom - len(game.state.kcmdr))==1]))
5055     elif game.inkling - game.state.remkl + (game.inscom - game.state.nscrem) > 0:
5056         prout(_(", but no Commanders."))
5057     else:
5058         prout(".")
5059     if game.skill > SKILL_FAIR:
5060         prout(_("The Super Commander has %sbeen destroyed.") % ("", _("not "))[game.state.nscrem])
5061     if len(game.state.baseq) != game.inbase:
5062         proutn(_("There "))
5063         if game.inbase-len(game.state.baseq)==1:
5064             proutn(_("has been 1 base"))
5065         else:
5066             proutn(_("have been %d bases") % (game.inbase-len(game.state.baseq)))
5067         prout(_(" destroyed, %d remaining.") % len(game.state.baseq))
5068     else:
5069         prout(_("There are %d bases.") % game.inbase)
5070     if communicating() or game.iseenit:
5071         # Don't report this if not seen and
5072         # either the radio is dead or not at base!
5073         attackreport(False)
5074         game.iseenit = True
5075     if game.casual: 
5076         prout(_("%d casualt%s suffered so far.") % (game.casual, ("y", "ies")[game.casual!=1]))
5077     if game.nhelp:
5078         prout(_("There were %d call%s for help.") % (game.nhelp,  ("" , _("s"))[game.nhelp!=1]))
5079     if game.ship == IHE:
5080         proutn(_("You have "))
5081         if game.nprobes:
5082             proutn("%d" % (game.nprobes))
5083         else:
5084             proutn(_("no"))
5085         proutn(_(" deep space probe"))
5086         if game.nprobes!=1:
5087             proutn(_("s"))
5088         prout(".")
5089     if communicating() and is_scheduled(FDSPROB):
5090         if game.isarmed: 
5091             proutn(_("An armed deep space probe is in "))
5092         else:
5093             proutn(_("A deep space probe is in "))
5094         prout("Quadrant %s." % game.probec)
5095     if game.icrystl:
5096         if game.cryprob <= .05:
5097             prout(_("Dilithium crystals aboard ship... not yet used."))
5098         else:
5099             i=0
5100             ai = 0.05
5101             while game.cryprob > ai:
5102                 ai *= 2.0
5103                 i += 1
5104             prout(_("Dilithium crystals have been used %d time%s.") % \
5105                   (i, (_("s"), "")[i==1]))
5106     skip(1)
5107         
5108 def lrscan(silent):
5109     "Long-range sensor scan."
5110     if damaged(DLRSENS):
5111         # Now allow base's sensors if docked 
5112         if game.condition != "docked":
5113             if not silent:
5114                 prout(_("LONG-RANGE SENSORS DAMAGED."))
5115             return
5116         if not silent:
5117             prout(_("Starbase's long-range scan"))
5118     elif not silent:
5119         prout(_("Long-range scan"))
5120     for x in range(game.quadrant.i-1, game.quadrant.i+2):
5121         if not silent:
5122             proutn(" ")
5123         for y in range(game.quadrant.j-1, game.quadrant.j+2):
5124             if not VALID_QUADRANT(x, y):
5125                 if not silent:
5126                     proutn("  -1")
5127             else:
5128                 if not damaged(DRADIO):
5129                     game.state.galaxy[x][y].charted = True
5130                 game.state.chart[x][y].klingons = game.state.galaxy[x][y].klingons
5131                 game.state.chart[x][y].starbase = game.state.galaxy[x][y].starbase
5132                 game.state.chart[x][y].stars = game.state.galaxy[x][y].stars
5133                 if not silent and game.state.galaxy[x][y].supernova: 
5134                     proutn(" ***")
5135                 elif not silent:
5136                     proutn(" %3d" % (game.state.chart[x][y].klingons*100 + game.state.chart[x][y].starbase * 10 + game.state.chart[x][y].stars))
5137         prout(" ")
5138
5139 def damagereport():
5140     "Damage report."
5141     jdam = False
5142     scanner.chew()
5143
5144     for i in range(NDEVICES):
5145         if damaged(i):
5146             if not jdam:
5147                 prout(_("\tDEVICE\t\t\t-REPAIR TIMES-"))
5148                 prout(_("\t\t\tIN FLIGHT\t\tDOCKED"))
5149                 jdam = True
5150             prout("  %-26s\t%8.2f\t\t%8.2f" % (device[i],
5151                                                game.damage[i]+0.05,
5152                                                game.docfac*game.damage[i]+0.005))
5153     if not jdam:
5154         prout(_("All devices functional."))
5155
5156 def rechart():
5157     "Update the chart in the Enterprise's computer from galaxy data."
5158     game.lastchart = game.state.date
5159     for i in range(GALSIZE):
5160         for j in range(GALSIZE):
5161             if game.state.galaxy[i][j].charted:
5162                 game.state.chart[i][j].klingons = game.state.galaxy[i][j].klingons
5163                 game.state.chart[i][j].starbase = game.state.galaxy[i][j].starbase
5164                 game.state.chart[i][j].stars = game.state.galaxy[i][j].stars
5165
5166 def chart():
5167     "Display the star chart."
5168     scanner.chew()
5169     if (game.options & OPTION_AUTOSCAN):
5170         lrscan(silent=True)
5171     if not damaged(DRADIO):
5172         rechart()
5173     if game.lastchart < game.state.date and game.condition == "docked":
5174         prout(_("Spock-  \"I revised the Star Chart from the starbase's records.\""))
5175         rechart()
5176     prout(_("       STAR CHART FOR THE KNOWN GALAXY"))
5177     if game.state.date > game.lastchart:
5178         prout(_("(Last surveillance update %d stardates ago).") % ((int)(game.state.date-game.lastchart)))
5179     prout("      1    2    3    4    5    6    7    8")
5180     for i in range(GALSIZE):
5181         proutn("%d |" % (i+1))
5182         for j in range(GALSIZE):
5183             if (game.options & OPTION_SHOWME) and i == game.quadrant.i and j == game.quadrant.j:
5184                 proutn("<")
5185             else:
5186                 proutn(" ")
5187             if game.state.galaxy[i][j].supernova:
5188                 show = "***"
5189             elif not game.state.galaxy[i][j].charted and game.state.galaxy[i][j].starbase:
5190                 show = ".1."
5191             elif game.state.galaxy[i][j].charted:
5192                 show = "%3d" % (game.state.chart[i][j].klingons*100 + game.state.chart[i][j].starbase * 10 + game.state.chart[i][j].stars)
5193             else:
5194                 show = "..."
5195             proutn(show)
5196             if (game.options & OPTION_SHOWME) and i == game.quadrant.i and j == game.quadrant.j:
5197                 proutn(">")
5198             else:
5199                 proutn(" ")
5200         proutn("  |")
5201         if i<GALSIZE:
5202             skip(1)
5203
5204 def sectscan(goodScan, i, j):
5205     "Light up an individual dot in a sector."
5206     if goodScan or (abs(i-game.sector.i)<= 1 and abs(j-game.sector.j) <= 1):
5207         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):
5208             #if game.condition   == "red": textcolor("red")
5209             #elif game.condition == "green": textcolor("green")
5210             #elif game.condition == "yellow": textcolor("yellow")
5211             #elif game.condition == "docked": textcolor("cyan")
5212             #elif game.condition == "dead": textcolor("brown")
5213             if game.quad[i][j] != game.ship: 
5214                 highvideo()
5215         proutn("%c " % game.quad[i][j])
5216         #textcolor(None)
5217     else:
5218         proutn("- ")
5219
5220 def status(req=0):
5221     "Emit status report lines"
5222     if not req or req == 1:
5223         prstat(_("Stardate"), _("%.1f, Time Left %.2f") \
5224                % (game.state.date, game.state.remtime))
5225     if not req or req == 2:
5226         if game.condition != "docked":
5227             newcnd()
5228         dam = 0
5229         for t in range(NDEVICES):
5230             if game.damage[t]>0: 
5231                 dam += 1
5232         prstat(_("Condition"), _("%s, %i DAMAGES") % (game.condition.upper(), dam))
5233     if not req or req == 3:
5234         prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
5235     if not req or req == 4:
5236         if damaged(DLIFSUP):
5237             if game.condition == "docked":
5238                 s = _("DAMAGED, Base provides")
5239             else:
5240                 s = _("DAMAGED, reserves=%4.2f") % game.lsupres
5241         else:
5242             s = _("ACTIVE")
5243         prstat(_("Life Support"), s)
5244     if not req or req == 5:
5245         prstat(_("Warp Factor"), "%.1f" % game.warpfac)
5246     if not req or req == 6:
5247         extra = ""
5248         if game.icrystl and (game.options & OPTION_SHOWME):
5249             extra = _(" (have crystals)")
5250         prstat(_("Energy"), "%.2f%s" % (game.energy, extra))
5251     if not req or req == 7:
5252         prstat(_("Torpedoes"), "%d" % (game.torps))
5253     if not req or req == 8:
5254         if damaged(DSHIELD):
5255             s = _("DAMAGED,")
5256         elif game.shldup:
5257             s = _("UP,")
5258         else:
5259             s = _("DOWN,")
5260         data = _(" %d%% %.1f units") \
5261                % (int((100.0*game.shield)/game.inshld + 0.5), game.shield)
5262         prstat(_("Shields"), s+data)
5263     if not req or req == 9:
5264         prstat(_("Klingons Left"), "%d" \
5265                % (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem))
5266     if not req or req == 10:
5267         if game.options & OPTION_WORLDS:
5268             plnet = game.state.galaxy[game.quadrant.i][game.quadrant.j].planet
5269             if plnet and plnet.inhabited:
5270                 prstat(_("Major system"), plnet.name)
5271             else:
5272                 prout(_("Sector is uninhabited"))
5273     elif not req or req == 11:
5274         attackreport(not req)
5275
5276 def request():
5277     "Request specified status data, a historical relic from slow TTYs."
5278     requests = ("da","co","po","ls","wa","en","to","sh","kl","sy", "ti")
5279     while scanner.next() == "IHEOL":
5280         proutn(_("Information desired? "))
5281     scanner.chew()
5282     if scanner.token in requests:
5283         status(requests.index(scanner.token))
5284     else:
5285         prout(_("UNRECOGNIZED REQUEST. Legal requests are:"))
5286         prout(("  date, condition, position, lsupport, warpfactor,"))
5287         prout(("  energy, torpedoes, shields, klingons, system, time."))
5288                 
5289 def srscan():
5290     "Short-range scan." 
5291     goodScan=True
5292     if damaged(DSRSENS):
5293         # Allow base's sensors if docked 
5294         if game.condition != "docked":
5295             prout(_("   S.R. SENSORS DAMAGED!"))
5296             goodScan=False
5297         else:
5298             prout(_("  [Using Base's sensors]"))
5299     else:
5300         prout(_("     Short-range scan"))
5301     if goodScan and not damaged(DRADIO): 
5302         game.state.chart[game.quadrant.i][game.quadrant.j].klingons = game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons
5303         game.state.chart[game.quadrant.i][game.quadrant.j].starbase = game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase
5304         game.state.chart[game.quadrant.i][game.quadrant.j].stars = game.state.galaxy[game.quadrant.i][game.quadrant.j].stars
5305         game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
5306     prout("    1 2 3 4 5 6 7 8 9 10")
5307     if game.condition != "docked":
5308         newcnd()
5309     for i in range(QUADSIZE):
5310         proutn("%2d  " % (i+1))
5311         for j in range(QUADSIZE):
5312             sectscan(goodScan, i, j)
5313         skip(1)
5314                         
5315 def eta():
5316     "Use computer to get estimated time of arrival for a warp jump."
5317     w1 = coord(); w2 = coord()
5318     prompt = False
5319     if damaged(DCOMPTR):
5320         prout(_("COMPUTER DAMAGED, USE A POCKET CALCULATOR."))
5321         skip(1)
5322         return
5323     if scanner.next() != "IHREAL":
5324         prompt = True
5325         scanner.chew()
5326         proutn(_("Destination quadrant and/or sector? "))
5327         if scanner.next()!="IHREAL":
5328             huh()
5329             return
5330     w1.j = int(scanner.real-0.5)
5331     if scanner.next() != "IHREAL":
5332         huh()
5333         return
5334     w1.i = int(scanner.real-0.5)
5335     if scanner.next() == "IHREAL":
5336         w2.j = int(scanner.real-0.5)
5337         if scanner.next() != "IHREAL":
5338             huh()
5339             return
5340         w2.i = int(scanner.real-0.5)
5341     else:
5342         if game.quadrant.j>w1.i:
5343             w2.i = 0
5344         else:
5345             w2.i=QUADSIZE-1
5346         if game.quadrant.i>w1.j:
5347             w2.j = 0
5348         else:
5349             w2.j=QUADSIZE-1
5350     if not VALID_QUADRANT(w1.i, w1.j) or not VALID_SECTOR(w2.i, w2.j):
5351         huh()
5352         return
5353     game.dist = math.sqrt((w1.j-game.quadrant.j+0.1*(w2.j-game.sector.j))**2+
5354                 (w1.i-game.quadrant.i+0.1*(w2.i-game.sector.i))**2)
5355     wfl = False
5356     if prompt:
5357         prout(_("Answer \"no\" if you don't know the value:"))
5358     while True:
5359         scanner.chew()
5360         proutn(_("Time or arrival date? "))
5361         if scanner.next()=="IHREAL":
5362             ttime = scanner.real
5363             if ttime > game.state.date:
5364                 ttime -= game.state.date # Actually a star date
5365             twarp=(math.floor(math.sqrt((10.0*game.dist)/ttime)*10.0)+1.0)/10.0
5366             if ttime <= 1e-10 or twarp > 10:
5367                 prout(_("We'll never make it, sir."))
5368                 scanner.chew()
5369                 return
5370             if twarp < 1.0:
5371                 twarp = 1.0
5372             break
5373         scanner.chew()
5374         proutn(_("Warp factor? "))
5375         if scanner.next()== "IHREAL":
5376             wfl = True
5377             twarp = scanner.real
5378             if twarp<1.0 or twarp > 10.0:
5379                 huh()
5380                 return
5381             break
5382         prout(_("Captain, certainly you can give me one of these."))
5383     while True:
5384         scanner.chew()
5385         ttime = (10.0*game.dist)/twarp**2
5386         tpower = game.dist*twarp*twarp*twarp*(game.shldup+1)
5387         if tpower >= game.energy:
5388             prout(_("Insufficient energy, sir."))
5389             if not game.shldup or tpower > game.energy*2.0:
5390                 if not wfl:
5391                     return
5392                 proutn(_("New warp factor to try? "))
5393                 if scanner.next() == "IHREAL":
5394                     wfl = True
5395                     twarp = scanner.real
5396                     if twarp<1.0 or twarp > 10.0:
5397                         huh()
5398                         return
5399                     continue
5400                 else:
5401                     scanner.chew()
5402                     skip(1)
5403                     return
5404             prout(_("But if you lower your shields,"))
5405             proutn(_("remaining"))
5406             tpower /= 2
5407         else:
5408             proutn(_("Remaining"))
5409         prout(_(" energy will be %.2f.") % (game.energy-tpower))
5410         if wfl:
5411             prout(_("And we will arrive at stardate %.2f.") % (game.state.date+ttime))
5412         elif twarp==1.0:
5413             prout(_("Any warp speed is adequate."))
5414         else:
5415             prout(_("Minimum warp needed is %.2f,") % (twarp))
5416             prout(_("and we will arrive at stardate %.2f.") % (game.state.date+ttime))
5417         if game.state.remtime < ttime:
5418             prout(_("Unfortunately, the Federation will be destroyed by then."))
5419         if twarp > 6.0:
5420             prout(_("You'll be taking risks at that speed, Captain"))
5421         if (game.isatb==1 and game.state.kscmdr == w1 and \
5422              scheduled(FSCDBAS)< ttime+game.state.date) or \
5423             (scheduled(FCDBAS)<ttime+game.state.date and game.battle == w1):
5424             prout(_("The starbase there will be destroyed by then."))
5425         proutn(_("New warp factor to try? "))
5426         if scanner.next() == "IHREAL":
5427             wfl = True
5428             twarp = scanner.real
5429             if twarp<1.0 or twarp > 10.0:
5430                 huh()
5431                 return
5432         else:
5433             scanner.chew()
5434             skip(1)
5435             return
5436
5437 # Code from setup.c begins here
5438
5439 def prelim():
5440     "Issue a historically correct banner."
5441     skip(2)
5442     prout(_("-SUPER- STAR TREK"))
5443     skip(1)
5444 # From the FORTRAN original
5445 #    prout(_("Latest update-21 Sept 78"))
5446 #    skip(1)
5447
5448 def freeze(boss):
5449     "Save game."
5450     if boss:
5451         scanner.token = "emsave.trk"
5452     else:
5453         key = scanner.next()
5454         if key == "IHEOL":
5455             proutn(_("File name: "))
5456             key = scanner.next()
5457         if key != "IHALPHA":
5458             huh()
5459             return
5460         scanner.chew()
5461         if '.' not in scanner.token:
5462             scanner.token += ".trk"
5463     try:
5464         fp = open(scanner.token, "wb")
5465     except IOError:
5466         prout(_("Can't freeze game as file %s") % scanner.token)
5467         return
5468     cPickle.dump(game, fp)
5469     fp.close()
5470
5471 def thaw():
5472     "Retrieve saved game." 
5473     game.passwd[0] = '\0'
5474     key = scanner.next()
5475     if key == "IHEOL":
5476         proutn(_("File name: "))
5477         key = scanner.next()
5478     if key != "IHALPHA":
5479         huh()
5480         return True
5481     scanner.chew()
5482     if '.' not in scanner.token:
5483         scanner.token += ".trk"
5484     try:
5485         fp = open(scanner.token, "rb")
5486     except IOError:
5487         prout(_("Can't thaw game in %s") % scanner.token)
5488         return
5489     game = cPickle.load(fp)
5490     fp.close()
5491     return False
5492
5493 # I used <http://www.memory-alpha.org> to find planets
5494 # with references in ST:TOS.  Eath and the Alpha Centauri
5495 # Colony have been omitted.
5496
5497 # Some planets marked Class G and P here will be displayed as class M
5498 # because of the way planets are generated. This is a known bug.
5499 systnames = (
5500     # Federation Worlds 
5501     _("Andoria (Fesoan)"),      # several episodes 
5502     _("Tellar Prime (Miracht)"),        # TOS: "Journey to Babel" 
5503     _("Vulcan (T'Khasi)"),      # many episodes 
5504     _("Medusa"),                # TOS: "Is There in Truth No Beauty?" 
5505     _("Argelius II (Nelphia)"),# TOS: "Wolf in the Fold" ("IV" in BSD) 
5506     _("Ardana"),                # TOS: "The Cloud Minders" 
5507     _("Catulla (Cendo-Prae)"),  # TOS: "The Way to Eden" 
5508     _("Gideon"),                # TOS: "The Mark of Gideon" 
5509     _("Aldebaran III"), # TOS: "The Deadly Years" 
5510     _("Alpha Majoris I"),       # TOS: "Wolf in the Fold" 
5511     _("Altair IV"),             # TOS: "Amok Time 
5512     _("Ariannus"),              # TOS: "Let That Be Your Last Battlefield" 
5513     _("Benecia"),               # TOS: "The Conscience of the King" 
5514     _("Beta Niobe I (Sarpeidon)"),      # TOS: "All Our Yesterdays" 
5515     _("Alpha Carinae II"),      # TOS: "The Ultimate Computer" 
5516     _("Capella IV (Kohath)"),   # TOS: "Friday's Child" (Class G) 
5517     _("Daran V"),               # TOS: "For the World is Hollow and I Have Touched the Sky" 
5518     _("Deneb II"),              # TOS: "Wolf in the Fold" ("IV" in BSD) 
5519     _("Eminiar VII"),           # TOS: "A Taste of Armageddon" 
5520     _("Gamma Canaris IV"),      # TOS: "Metamorphosis" 
5521     _("Gamma Tranguli VI (Vaalel)"),    # TOS: "The Apple" 
5522     _("Ingraham B"),            # TOS: "Operation: Annihilate" 
5523     _("Janus IV"),              # TOS: "The Devil in the Dark" 
5524     _("Makus III"),             # TOS: "The Galileo Seven" 
5525     _("Marcos XII"),            # TOS: "And the Children Shall Lead", 
5526     _("Omega IV"),              # TOS: "The Omega Glory" 
5527     _("Regulus V"),             # TOS: "Amok Time 
5528     _("Deneva"),                # TOS: "Operation -- Annihilate!" 
5529     # Worlds from BSD Trek 
5530     _("Rigel II"),              # TOS: "Shore Leave" ("III" in BSD) 
5531     _("Beta III"),              # TOS: "The Return of the Archons" 
5532     _("Triacus"),               # TOS: "And the Children Shall Lead", 
5533     _("Exo III"),               # TOS: "What Are Little Girls Made Of?" (Class P) 
5534 #       # Others 
5535 #    _("Hansen's Planet"),      # TOS: "The Galileo Seven" 
5536 #    _("Taurus IV"),            # TOS: "The Galileo Seven" (class G) 
5537 #    _("Antos IV (Doraphane)"), # TOS: "Whom Gods Destroy", "Who Mourns for Adonais?" 
5538 #    _("Izar"),                 # TOS: "Whom Gods Destroy" 
5539 #    _("Tiburon"),              # TOS: "The Way to Eden" 
5540 #    _("Merak II"),             # TOS: "The Cloud Minders" 
5541 #    _("Coridan (Desotriana)"), # TOS: "Journey to Babel" 
5542 #    _("Iotia"),                # TOS: "A Piece of the Action" 
5543 )
5544
5545 device = (
5546         _("S. R. Sensors"), \
5547         _("L. R. Sensors"), \
5548         _("Phasers"), \
5549         _("Photon Tubes"), \
5550         _("Life Support"), \
5551         _("Warp Engines"), \
5552         _("Impulse Engines"), \
5553         _("Shields"), \
5554         _("Subspace Radio"), \
5555         _("Shuttle Craft"), \
5556         _("Computer"), \
5557         _("Navigation System"), \
5558         _("Transporter"), \
5559         _("Shield Control"), \
5560         _("Death Ray"), \
5561         _("D. S. Probe"), \
5562 )
5563
5564 def setup():
5565     "Prepare to play, set up cosmos."
5566     w = coord()
5567     #  Decide how many of everything
5568     if choose():
5569         return # frozen game
5570     # Prepare the Enterprise
5571     game.alldone = game.gamewon = game.shldchg = game.shldup = False
5572     game.ship = IHE
5573     game.state.crew = FULLCREW
5574     game.energy = game.inenrg = 5000.0
5575     game.shield = game.inshld = 2500.0
5576     game.inlsr = 4.0
5577     game.lsupres = 4.0
5578     game.quadrant = randplace(GALSIZE)
5579     game.sector = randplace(QUADSIZE)
5580     game.torps = game.intorps = 10
5581     game.nprobes = randrange(2, 5)
5582     game.warpfac = 5.0
5583     for i in range(NDEVICES): 
5584         game.damage[i] = 0.0
5585     # Set up assorted game parameters
5586     game.battle = coord()
5587     game.state.date = game.indate = 100.0 * randreal(20, 51)
5588     game.nkinks = game.nhelp = game.casual = game.abandoned = 0
5589     game.iscate = game.resting = game.imine = game.icrystl = game.icraft = False
5590     game.isatb = game.state.nplankl = 0
5591     game.state.starkl = game.state.basekl = 0
5592     game.iscraft = "onship"
5593     game.landed = False
5594     game.alive = True
5595     game.docfac = 0.25
5596     # Starchart is functional but we've never seen it
5597     game.lastchart = FOREVER
5598     # Put stars in the galaxy
5599     game.instar = 0
5600     for i in range(GALSIZE):
5601         for j in range(GALSIZE):
5602             k = randrange(1, QUADSIZE**2/10+1)
5603             game.instar += k
5604             game.state.galaxy[i][j].stars = k
5605     # Locate star bases in galaxy
5606     for i in range(game.inbase):
5607         while True:
5608             while True:
5609                 w = randplace(GALSIZE)
5610                 if not game.state.galaxy[w.i][w.j].starbase:
5611                     break
5612             contflag = False
5613             # C version: for (j = i-1; j > 0; j--)
5614             # so it did them in the opposite order.
5615             for j in range(1, i):
5616                 # Improved placement algorithm to spread out bases
5617                 distq = (w - game.state.baseq[j]).distance()
5618                 if distq < 6.0*(BASEMAX+1-game.inbase) and withprob(0.75):
5619                     contflag = True
5620                     if idebug:
5621                         prout("=== Abandoning base #%d at %s" % (i, w))
5622                     break
5623                 elif distq < 6.0 * (BASEMAX+1-game.inbase):
5624                     if idebug:
5625                         prout("=== Saving base #%d, close to #%d" % (i, j))
5626             if not contflag:
5627                 break
5628         game.state.baseq.append(w)
5629         game.state.galaxy[w.i][w.j].starbase = game.state.chart[w.i][w.j].starbase = True
5630     # Position ordinary Klingon Battle Cruisers
5631     krem = game.inkling
5632     klumper = 0.25*game.skill*(9.0-game.length)+1.0
5633     if klumper > MAXKLQUAD: 
5634         klumper = MAXKLQUAD
5635     while True:
5636         r = randreal()
5637         klump = (1.0 - r*r)*klumper
5638         if klump > krem:
5639             klump = krem
5640         krem -= klump
5641         while True:
5642             w = randplace(GALSIZE)
5643             if not game.state.galaxy[w.i][w.j].supernova and \
5644                game.state.galaxy[w.i][w.j].klingons + klump <= MAXKLQUAD:
5645                 break
5646         game.state.galaxy[w.i][w.j].klingons += int(klump)
5647         if krem <= 0:
5648             break
5649     # Position Klingon Commander Ships
5650     for i in range(game.incom):
5651         while True:
5652             w = randplace(GALSIZE)
5653             if not welcoming(w) or w in game.state.kcmdr:
5654                 continue
5655             if (game.state.galaxy[w.i][w.j].klingons or withprob(0.25)):
5656                 break
5657         game.state.galaxy[w.i][w.j].klingons += 1
5658         game.state.kcmdr.append(w)
5659     # Locate planets in galaxy
5660     for i in range(game.inplan):
5661         while True:
5662             w = randplace(GALSIZE) 
5663             if game.state.galaxy[w.i][w.j].planet == None:
5664                 break
5665         new = planet()
5666         new.quadrant = w
5667         new.crystals = "absent"
5668         if (game.options & OPTION_WORLDS) and i < NINHAB:
5669             new.pclass = "M"    # All inhabited planets are class M
5670             new.crystals = "absent"
5671             new.known = "known"
5672             new.name = systnames[i]
5673             new.inhabited = True
5674         else:
5675             new.pclass = ("M", "N", "O")[randrange(0, 3)]
5676             if withprob(0.33):
5677                 new.crystals = "present"
5678             new.known = "unknown"
5679             new.inhabited = False
5680         game.state.galaxy[w.i][w.j].planet = new
5681         game.state.planets.append(new)
5682     # Locate Romulans
5683     for i in range(game.state.nromrem):
5684         w = randplace(GALSIZE)
5685         game.state.galaxy[w.i][w.j].romulans += 1
5686     # Place the Super-Commander if needed
5687     if game.state.nscrem > 0:
5688         while True:
5689             w = randplace(GALSIZE)
5690             if welcoming(w):
5691                 break
5692         game.state.kscmdr = w
5693         game.state.galaxy[w.i][w.j].klingons += 1
5694     # Initialize times for extraneous events
5695     schedule(FSNOVA, expran(0.5 * game.intime))
5696     schedule(FTBEAM, expran(1.5 * (game.intime / len(game.state.kcmdr))))
5697     schedule(FSNAP, randreal(1.0, 2.0)) # Force an early snapshot
5698     schedule(FBATTAK, expran(0.3*game.intime))
5699     unschedule(FCDBAS)
5700     if game.state.nscrem:
5701         schedule(FSCMOVE, 0.2777)
5702     else:
5703         unschedule(FSCMOVE)
5704     unschedule(FSCDBAS)
5705     unschedule(FDSPROB)
5706     if (game.options & OPTION_WORLDS) and game.skill >= SKILL_GOOD:
5707         schedule(FDISTR, expran(1.0 + game.intime))
5708     else:
5709         unschedule(FDISTR)
5710     unschedule(FENSLV)
5711     unschedule(FREPRO)
5712     # Place thing (in tournament game, we don't want one!)
5713     # New in SST2K: never place the Thing near a starbase.
5714     # This makes sense and avoids a special case in the old code.
5715     global thing
5716     if game.tourn is None:
5717         while True:
5718             thing = randplace(GALSIZE)
5719             if thing not in game.state.baseq:
5720                 break
5721     skip(2)
5722     game.state.snap = False
5723     if game.skill == SKILL_NOVICE:
5724         prout(_("It is stardate %d. The Federation is being attacked by") % int(game.state.date))
5725         prout(_("a deadly Klingon invasion force. As captain of the United"))
5726         prout(_("Starship U.S.S. Enterprise, it is your mission to seek out"))
5727         prout(_("and destroy this invasion force of %d battle cruisers.") % ((game.inkling + game.incom + game.inscom)))
5728         prout(_("You have an initial allotment of %d stardates to complete") % int(game.intime))
5729         prout(_("your mission.  As you proceed you may be given more time."))
5730         skip(1)
5731         prout(_("You will have %d supporting starbases.") % (game.inbase))
5732         proutn(_("Starbase locations-  "))
5733     else:
5734         prout(_("Stardate %d.") % int(game.state.date))
5735         skip(1)
5736         prout(_("%d Klingons.") % (game.inkling + game.incom + game.inscom))
5737         prout(_("An unknown number of Romulans."))
5738         if game.state.nscrem:
5739             prout(_("And one (GULP) Super-Commander."))
5740         prout(_("%d stardates.") % int(game.intime))
5741         proutn(_("%d starbases in ") % game.inbase)
5742     for i in range(game.inbase):
5743         proutn(`game.state.baseq[i]`)
5744         proutn("  ")
5745     skip(2)
5746     proutn(_("The Enterprise is currently in Quadrant %s") % game.quadrant)
5747     proutn(_(" Sector %s") % game.sector)
5748     skip(2)
5749     prout(_("Good Luck!"))
5750     if game.state.nscrem:
5751         prout(_("  YOU'LL NEED IT."))
5752     waitfor()
5753     newqad()
5754     if len(game.enemies) - (thing == game.quadrant) - (game.tholian != None):
5755         game.shldup = True
5756     if game.neutz:      # bad luck to start in a Romulan Neutral Zone
5757         attack(torps_ok=False)
5758
5759 def choose():
5760     "Choose your game type."
5761     global thing
5762     while True:
5763         game.tourn = 0
5764         game.thawed = False
5765         game.skill = SKILL_NONE
5766         game.length = 0
5767         if not scanner.inqueue: # Can start with command line options 
5768             proutn(_("Would you like a regular, tournament, or saved game? "))
5769         scanner.next()
5770         if len(scanner.token)==0: # Try again
5771             continue
5772         if scanner.sees("tournament"):
5773             while scanner.next() == "IHEOL":
5774                 proutn(_("Type in tournament number-"))
5775             if scanner.real == 0:
5776                 scanner.chew()
5777                 continue # We don't want a blank entry
5778             game.tourn = int(round(scanner.real))
5779             random.seed(scanner.real)
5780             if logfp:
5781                 logfp.write("# random.seed(%d)\n" % scanner.real)
5782             break
5783         if scanner.sees("saved") or scanner.sees("frozen"):
5784             if thaw():
5785                 continue
5786             scanner.chew()
5787             if game.passwd == None:
5788                 continue
5789             if not game.alldone:
5790                 game.thawed = True # No plaque if not finished
5791             report()
5792             waitfor()
5793             return True
5794         if scanner.sees("regular"):
5795             break
5796         proutn(_("What is \"%s\"?") % scanner.token)
5797         scanner.chew()
5798     while game.length==0 or game.skill==SKILL_NONE:
5799         if scanner.next() == "IHALPHA":
5800             if scanner.sees("short"):
5801                 game.length = 1
5802             elif scanner.sees("medium"):
5803                 game.length = 2
5804             elif scanner.sees("long"):
5805                 game.length = 4
5806             elif scanner.sees("novice"):
5807                 game.skill = SKILL_NOVICE
5808             elif scanner.sees("fair"):
5809                 game.skill = SKILL_FAIR
5810             elif scanner.sees("good"):
5811                 game.skill = SKILL_GOOD
5812             elif scanner.sees("expert"):
5813                 game.skill = SKILL_EXPERT
5814             elif scanner.sees("emeritus"):
5815                 game.skill = SKILL_EMERITUS
5816             else:
5817                 proutn(_("What is \""))
5818                 proutn(scanner.token)
5819                 prout("\"?")
5820         else:
5821             scanner.chew()
5822             if game.length==0:
5823                 proutn(_("Would you like a Short, Medium, or Long game? "))
5824             elif game.skill == SKILL_NONE:
5825                 proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
5826     # Choose game options -- added by ESR for SST2K
5827     if scanner.next() != "IHALPHA":
5828         scanner.chew()
5829         proutn(_("Choose your game style (or just press enter): "))
5830         scanner.next()
5831     if scanner.sees("plain"):
5832         # Approximates the UT FORTRAN version.
5833         game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
5834         game.options |= OPTION_PLAIN
5835     elif scanner.sees("almy"):
5836         # Approximates Tom Almy's version.
5837         game.options &=~ (OPTION_THINGY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
5838         game.options |= OPTION_ALMY
5839     elif scanner.sees("fancy"):
5840         pass
5841     elif len(scanner.token):
5842         proutn(_("What is \"%s\"?") % scanner.token)
5843     setpassword()
5844     if game.passwd == "debug":
5845         idebug = True
5846         prout("=== Debug mode enabled.")
5847     # Use parameters to generate initial values of things
5848     game.damfac = 0.5 * game.skill
5849     game.inbase = randrange(BASEMIN, BASEMAX+1)
5850     game.inplan = 0
5851     if game.options & OPTION_PLANETS:
5852         game.inplan += randrange(MAXUNINHAB/2, MAXUNINHAB+1)
5853     if game.options & OPTION_WORLDS:
5854         game.inplan += int(NINHAB)
5855     game.state.nromrem = game.inrom = randrange(2 *game.skill)
5856     game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR)
5857     game.state.remtime = 7.0 * game.length
5858     game.intime = game.state.remtime
5859     game.state.remkl = game.inkling = 2.0*game.intime*((game.skill+1 - 2*randreal())*game.skill*0.1+.15)
5860     game.incom = min(MINCMDR, int(game.skill + 0.0625*game.inkling*randreal()))
5861     game.state.remres = (game.inkling+4*game.incom)*game.intime
5862     game.inresor = game.state.remres
5863     if game.inkling > 50:
5864         game.state.inbase += 1
5865     return False
5866
5867 def dropin(iquad=None):
5868     "Drop a feature on a random dot in the current quadrant."
5869     while True:
5870         w = randplace(QUADSIZE)
5871         if game.quad[w.i][w.j] == IHDOT:
5872             break
5873     if iquad is not None:
5874         game.quad[w.i][w.j] = iquad
5875     return w
5876
5877 def newcnd():
5878     "Update our alert status."
5879     game.condition = "green"
5880     if game.energy < 1000.0:
5881         game.condition = "yellow"
5882     if game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons or game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans:
5883         game.condition = "red"
5884     if not game.alive:
5885         game.condition="dead"
5886
5887 def newkling():
5888     "Drop new Klingon into current quadrant."
5889     return enemy(IHK, loc=dropin(), power=randreal(300,450)+25.0*game.skill)
5890
5891 def newqad():
5892     "Set up a new state of quadrant, for when we enter or re-enter it."
5893     game.justin = True
5894     game.iplnet = None
5895     game.neutz = game.inorbit = game.landed = False
5896     game.ientesc = game.iseenit = False
5897     # Create a blank quadrant
5898     game.quad = fill2d(QUADSIZE, lambda i, j: IHDOT)
5899     if game.iscate:
5900         # Attempt to escape Super-commander, so tbeam back!
5901         game.iscate = False
5902         game.ientesc = True
5903     q = game.state.galaxy[game.quadrant.i][game.quadrant.j]
5904     # cope with supernova
5905     if q.supernova:
5906         return
5907     game.klhere = q.klingons
5908     game.irhere = q.romulans
5909     # Position Starship
5910     game.quad[game.sector.i][game.sector.j] = game.ship
5911     game.enemies = []
5912     if q.klingons:
5913         # Position ordinary Klingons
5914         for i in range(game.klhere):
5915             newkling()
5916         # If we need a commander, promote a Klingon
5917         for cmdr in game.state.kcmdr:
5918             if cmdr == game.quadrant:
5919                 e = game.enemies[game.klhere-1]
5920                 game.quad[e.kloc.i][e.kloc.j] = IHC
5921                 e.kpower = randreal(950,1350) + 50.0*game.skill
5922                 break   
5923         # If we need a super-commander, promote a Klingon
5924         if game.quadrant == game.state.kscmdr:
5925             e = game.enemies[0]
5926             game.quad[e.kloc.i][e.kloc.j] = IHS
5927             e.kpower = randreal(1175.0,  1575.0) + 125.0*game.skill
5928             game.iscate = (game.state.remkl > 1)
5929     # Put in Romulans if needed
5930     for i in range(q.romulans):
5931         enemy(IHR, loc=dropin(), power=randreal(400.0,850.0)+50.0*game.skill)
5932     # If quadrant needs a starbase, put it in
5933     if q.starbase:
5934         game.base = dropin(IHB)
5935     # If quadrant needs a planet, put it in
5936     if q.planet:
5937         game.iplnet = q.planet
5938         if not q.planet.inhabited:
5939             game.plnet = dropin(IHP)
5940         else:
5941             game.plnet = dropin(IHW)
5942     # Check for condition
5943     newcnd()
5944     # Check for RNZ
5945     if game.irhere > 0 and game.klhere == 0:
5946         game.neutz = True
5947         if not damaged(DRADIO):
5948             skip(1)
5949             prout(_("LT. Uhura- \"Captain, an urgent message."))
5950             prout(_("  I'll put it on audio.\"  CLICK"))
5951             skip(1)
5952             prout(_("INTRUDER! YOU HAVE VIOLATED THE ROMULAN NEUTRAL ZONE."))
5953             prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
5954     # Put in THING if needed
5955     if thing == game.quadrant:
5956         enemy(type=IHQUEST, loc=dropin(),
5957                   power=randreal(6000,6500.0)+250.0*game.skill)
5958         if not damaged(DSRSENS):
5959             skip(1)
5960             prout(_("Mr. Spock- \"Captain, this is most unusual."))
5961             prout(_("    Please examine your short-range scan.\""))
5962     # Decide if quadrant needs a Tholian; lighten up if skill is low 
5963     if game.options & OPTION_THOLIAN:
5964         if (game.skill < SKILL_GOOD and withprob(0.02)) or \
5965             (game.skill == SKILL_GOOD and withprob(0.05)) or \
5966             (game.skill > SKILL_GOOD and withprob(0.08)):
5967             w = coord()
5968             while True:
5969                 w.i = withprob(0.5) * (QUADSIZE-1)
5970                 w.j = withprob(0.5) * (QUADSIZE-1)
5971                 if game.quad[w.i][w.j] == IHDOT:
5972                     break
5973             game.tholian = enemy(type=IHT, loc=w,
5974                                  power=randrange(100, 500) + 25.0*game.skill)
5975             # Reserve unoccupied corners 
5976             if game.quad[0][0]==IHDOT:
5977                 game.quad[0][0] = 'X'
5978             if game.quad[0][QUADSIZE-1]==IHDOT:
5979                 game.quad[0][QUADSIZE-1] = 'X'
5980             if game.quad[QUADSIZE-1][0]==IHDOT:
5981                 game.quad[QUADSIZE-1][0] = 'X'
5982             if game.quad[QUADSIZE-1][QUADSIZE-1]==IHDOT:
5983                 game.quad[QUADSIZE-1][QUADSIZE-1] = 'X'
5984     game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
5985     # And finally the stars
5986     for i in range(q.stars):
5987         dropin(IHSTAR)
5988     # Put in a few black holes
5989     for i in range(1, 3+1):
5990         if withprob(0.5): 
5991             dropin(IHBLANK)
5992     # Take out X's in corners if Tholian present
5993     if game.tholian:
5994         if game.quad[0][0]=='X':
5995             game.quad[0][0] = IHDOT
5996         if game.quad[0][QUADSIZE-1]=='X':
5997             game.quad[0][QUADSIZE-1] = IHDOT
5998         if game.quad[QUADSIZE-1][0]=='X':
5999             game.quad[QUADSIZE-1][0] = IHDOT
6000         if game.quad[QUADSIZE-1][QUADSIZE-1]=='X':
6001             game.quad[QUADSIZE-1][QUADSIZE-1] = IHDOT
6002
6003 def setpassword():
6004     "Set the self-destruct password."
6005     if game.options & OPTION_PLAIN:
6006         while True:
6007             scanner.chew()
6008             proutn(_("Please type in a secret password- "))
6009             scanner.next()
6010             game.passwd = scanner.token
6011             if game.passwd != None:
6012                 break
6013     else:
6014         game.passwd = ""
6015         for i in range(8):
6016             game.passwd += chr(ord('a')+randrange(26))
6017
6018 # Code from sst.c begins here
6019
6020 commands = {
6021     "SRSCAN":           OPTION_TTY,
6022     "STATUS":           OPTION_TTY,
6023     "REQUEST":          OPTION_TTY,
6024     "LRSCAN":           OPTION_TTY,
6025     "PHASERS":          0,
6026     "TORPEDO":          0,
6027     "PHOTONS":          0,
6028     "MOVE":             0,
6029     "SHIELDS":          0,
6030     "DOCK":             0,
6031     "DAMAGES":          0,
6032     "CHART":            0,
6033     "IMPULSE":          0,
6034     "REST":             0,
6035     "WARP":             0,
6036     "SCORE":            0,
6037     "SENSORS":          OPTION_PLANETS,
6038     "ORBIT":            OPTION_PLANETS,
6039     "TRANSPORT":        OPTION_PLANETS,
6040     "MINE":             OPTION_PLANETS,
6041     "CRYSTALS":         OPTION_PLANETS,
6042     "SHUTTLE":          OPTION_PLANETS,
6043     "PLANETS":          OPTION_PLANETS,
6044     "REPORT":           0,
6045     "COMPUTER":         0,
6046     "COMMANDS":         0,
6047     "EMEXIT":           0,
6048     "PROBE":            OPTION_PROBE,
6049     "SAVE":             0,
6050     "FREEZE":           0,      # Synonym for SAVE
6051     "ABANDON":          0,
6052     "DESTRUCT":         0,
6053     "DEATHRAY":         0,
6054     "DEBUG":            0,
6055     "MAYDAY":           0,
6056     "SOS":              0,      # Synonym for MAYDAY
6057     "CALL":             0,      # Synonym for MAYDAY
6058     "QUIT":             0,
6059     "HELP":             0,
6060 }
6061
6062 def listCommands():
6063     "Generate a list of legal commands."
6064     proutn(_("LEGAL COMMANDS ARE:"))
6065     for (k, key) in enumerate(commands):
6066         if not commands[key] or (commands[key] & game.options):
6067             if k % 5 == 0:
6068                 skip(1)
6069             proutn("%-12s " % key) 
6070     skip(1)
6071
6072 def helpme():
6073     "Browse on-line help."
6074     key = scanner.next()
6075     while True:
6076         if key == "IHEOL":
6077             setwnd(prompt_window)
6078             proutn(_("Help on what command? "))
6079             key = scanner.next()
6080         setwnd(message_window)
6081         if key == "IHEOL":
6082             return
6083         if scanner.token in commands or scanner.token == "ABBREV":
6084             break
6085         skip(1)
6086         listCommands()
6087         key = "IHEOL"
6088         scanner.chew()
6089         skip(1)
6090     cmd = scanner.token.upper()
6091     try:
6092         fp = open(SSTDOC, "r")
6093     except IOError:
6094         try:
6095             fp = open(DOC_NAME, "r")
6096         except IOError:
6097             prout(_("Spock-  \"Captain, that information is missing from the"))
6098             proutn(_("   computer. You need to find "))
6099             proutn(DOC_NAME)
6100             prout(_(" and put it in the"))
6101             proutn(_("   current directory or to "))
6102             proutn(SSTDOC)
6103             prout(".\"")
6104             #
6105             # This used to continue: "You need to find SST.DOC and put 
6106             # it in the current directory."
6107             # 
6108             return
6109     while True:
6110         linebuf = fp.readline()
6111         if linebuf == '':
6112             prout(_("Spock- \"Captain, there is no information on that command.\""))
6113             fp.close()
6114             return
6115         if linebuf[0] == '%' and linebuf[1] == '%' and linebuf[2] == ' ':
6116             linebuf = linebuf[3:].strip()
6117             if cmd == linebuf:
6118                 break
6119     skip(1)
6120     prout(_("Spock- \"Captain, I've found the following information:\""))
6121     skip(1)
6122     while linebuf in fp:
6123         if "******" in linebuf:
6124             break
6125         proutn(linebuf)
6126     fp.close()
6127
6128 def makemoves():
6129     "Command-interpretation loop."
6130     clrscr()
6131     setwnd(message_window)
6132     while True:         # command loop 
6133         drawmaps(1)
6134         while True:     # get a command 
6135             hitme = False
6136             game.justin = False
6137             game.optime = 0.0
6138             scanner.chew()
6139             setwnd(prompt_window)
6140             clrscr()
6141             proutn("COMMAND> ")
6142             if scanner.next() == "IHEOL":
6143                 if game.options & OPTION_CURSES:
6144                     makechart()
6145                 continue
6146             elif scanner.token == "":
6147                 continue
6148             game.ididit = False
6149             clrscr()
6150             setwnd(message_window)
6151             clrscr()
6152             candidates = filter(lambda x: x.startswith(scanner.token.upper()),
6153                                 commands)
6154             if len(candidates) == 1:
6155                 cmd = candidates[0]
6156                 break
6157             elif candidates and not (game.options & OPTION_PLAIN):
6158                 prout("Commands with prefix '%s': %s" % (scanner.token, " ".join(candidates)))
6159             else:
6160                 listCommands()
6161                 continue
6162         if cmd == "SRSCAN":             # srscan
6163             srscan()
6164         elif cmd == "STATUS":           # status
6165             status()
6166         elif cmd == "REQUEST":          # status request 
6167             request()
6168         elif cmd == "LRSCAN":           # long range scan
6169             lrscan(silent=False)
6170         elif cmd == "PHASERS":          # phasers
6171             phasers()
6172             if game.ididit:
6173                 hitme = True
6174         elif cmd == "TORPEDO":          # photon torpedos
6175             photon()
6176             if game.ididit:
6177                 hitme = True
6178         elif cmd == "MOVE":             # move under warp
6179             warp(False)
6180         elif cmd == "SHIELDS":          # shields
6181             doshield(shraise=False)
6182             if game.ididit:
6183                 hitme = True
6184                 game.shldchg = False
6185         elif cmd == "DOCK":             # dock at starbase
6186             dock(True)
6187             if game.ididit:
6188                 attack(torps_ok=False)          
6189         elif cmd == "DAMAGES":          # damage reports
6190             damagereport()
6191         elif cmd == "CHART":            # chart
6192             makechart()
6193         elif cmd == "IMPULSE":          # impulse
6194             impulse()
6195         elif cmd == "REST":             # rest
6196             wait()
6197             if game.ididit:
6198                 hitme = True
6199         elif cmd == "WARP":             # warp
6200             setwarp()
6201         elif cmd == "SCORE":            # score
6202             score()
6203         elif cmd == "SENSORS":          # sensors
6204             sensor()
6205         elif cmd == "ORBIT":            # orbit
6206             orbit()
6207             if game.ididit:
6208                 hitme = True
6209         elif cmd == "TRANSPORT":                # transport "beam"
6210             beam()
6211         elif cmd == "MINE":             # mine
6212             mine()
6213             if game.ididit:
6214                 hitme = True
6215         elif cmd == "CRYSTALS":         # crystals
6216             usecrystals()
6217             if game.ididit:
6218                 hitme = True
6219         elif cmd == "SHUTTLE":          # shuttle
6220             shuttle()
6221             if game.ididit:
6222                 hitme = True
6223         elif cmd == "PLANETS":          # Planet list
6224             survey()
6225         elif cmd == "REPORT":           # Game Report 
6226             report()
6227         elif cmd == "COMPUTER":         # use COMPUTER!
6228             eta()
6229         elif cmd == "COMMANDS":
6230             listCommands()
6231         elif cmd == "EMEXIT":           # Emergency exit
6232             clrscr()                    # Hide screen
6233             freeze(True)                # forced save
6234             raise SysExit,1                     # And quick exit
6235         elif cmd == "PROBE":
6236             probe()                     # Launch probe
6237             if game.ididit:
6238                 hitme = True
6239         elif cmd == "ABANDON":          # Abandon Ship
6240             abandon()
6241         elif cmd == "DESTRUCT":         # Self Destruct
6242             selfdestruct()
6243         elif cmd == "SAVE":             # Save Game
6244             freeze(False)
6245             clrscr()
6246             if game.skill > SKILL_GOOD:
6247                 prout(_("WARNING--Saved games produce no plaques!"))
6248         elif cmd == "DEATHRAY":         # Try a desparation measure
6249             deathray()
6250             if game.ididit:
6251                 hitme = True
6252         elif cmd == "DEBUGCMD":         # What do we want for debug???
6253             debugme()
6254         elif cmd == "MAYDAY":           # Call for help
6255             mayday()
6256             if game.ididit:
6257                 hitme = True
6258         elif cmd == "QUIT":
6259             game.alldone = True         # quit the game
6260         elif cmd == "HELP":
6261             helpme()                    # get help
6262         while True:
6263             if game.alldone:
6264                 break           # Game has ended
6265             if game.optime != 0.0:
6266                 events()
6267                 if game.alldone:
6268                     break       # Events did us in
6269             if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
6270                 atover(False)
6271                 continue
6272             if hitme and not game.justin:
6273                 attack(torps_ok=True)
6274                 if game.alldone:
6275                     break
6276                 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
6277                     atover(False)
6278                     hitme = True
6279                     continue
6280             break
6281         if game.alldone:
6282             break
6283     if idebug:
6284         prout("=== Ending")
6285
6286 def cramen(type):
6287     "Emit the name of an enemy or feature." 
6288     if   type == IHR: s = _("Romulan")
6289     elif type == IHK: s = _("Klingon")
6290     elif type == IHC: s = _("Commander")
6291     elif type == IHS: s = _("Super-commander")
6292     elif type == IHSTAR: s = _("Star")
6293     elif type == IHP: s = _("Planet")
6294     elif type == IHB: s = _("Starbase")
6295     elif type == IHBLANK: s = _("Black hole")
6296     elif type == IHT: s = _("Tholian")
6297     elif type == IHWEB: s = _("Tholian web")
6298     elif type == IHQUEST: s = _("Stranger")
6299     elif type == IHW: s = _("Inhabited World")
6300     else: s = "Unknown??"
6301     return s
6302
6303 def crmena(stars, enemy, loctype, w):
6304     "Emit the name of an enemy and his location."
6305     buf = ""
6306     if stars:
6307         buf += "***"
6308     buf += cramen(enemy) + _(" at ")
6309     if loctype == "quadrant":
6310         buf += _("Quadrant ")
6311     elif loctype == "sector":
6312         buf += _("Sector ")
6313     return buf + `w`
6314
6315 def crmshp():
6316     "Emit our ship name." 
6317     return{IHE:_("Enterprise"),IHF:_("Faerie Queene")}.get(game.ship,"Ship???")
6318
6319 def stars():
6320     "Emit a line of stars" 
6321     prouts("******************************************************")
6322     skip(1)
6323
6324 def expran(avrage):
6325     return -avrage*math.log(1e-7 + randreal())
6326
6327 def randplace(size):
6328     "Choose a random location."
6329     w = coord()
6330     w.i = randrange(size) 
6331     w.j = randrange(size)
6332     return w
6333
6334 class sstscanner:
6335     def __init__(self):
6336         self.type = None
6337         self.token = None
6338         self.real = 0.0
6339         self.inqueue = []
6340     def next(self):
6341         # Get a token from the user
6342         self.real = 0.0
6343         self.token = ''
6344         # Fill the token quue if nothing here
6345         while not self.inqueue:
6346             line = cgetline()
6347             if curwnd==prompt_window:
6348                 clrscr()
6349                 setwnd(message_window)
6350                 clrscr()
6351             if line == '':
6352                 return None
6353             if not line:
6354                 continue
6355             else:
6356                 self.inqueue = line.lstrip().split() + ["IHEOL"] 
6357         # From here on in it's all looking at the queue
6358         self.token = self.inqueue.pop(0)
6359         if self.token == "IHEOL":
6360             self.type = "IHEOL"
6361             return "IHEOL"
6362         try:
6363             self.real = float(self.token)
6364             self.type = "IHREAL"
6365             return "IHREAL"
6366         except ValueError:
6367             pass
6368         # Treat as alpha
6369         self.token = self.token.lower()
6370         self.type = "IHALPHA"
6371         self.real = None
6372         return "IHALPHA"
6373     def append(self, tok):
6374         self.inqueue.append(tok)
6375     def push(self, tok):
6376         self.inqueue.insert(0, tok)
6377     def waiting(self):
6378         return self.inqueue
6379     def chew(self):
6380         # Demand input for next scan
6381         self.inqueue = []
6382         self.real = self.token = None
6383     def chew2(self):
6384         # return "IHEOL" next time 
6385         self.inqueue = ["IHEOL"]
6386         self.real = self.token = None
6387     def sees(self, s):
6388         # compares s to item and returns true if it matches to the length of s
6389         return s.startswith(self.token)
6390     def int(self):
6391         # Round token value to nearest integer
6392         return int(round(scanner.real))
6393     def getcoord(self):
6394         s = coord()
6395         scanner.next()
6396         if scanner.type != "IHREAL":
6397             huh()
6398             return None
6399         s.i = scanner.int()-1
6400         scanner.next()
6401         if scanner.type != "IHREAL":
6402             huh()
6403             return None
6404         s.j = scanner.int()-1
6405         return s
6406     def __repr__(str):
6407         return "<sstcanner: token=%s, type=%s, queue=%s>" % (scanner.token, scanner.type, scanner.inqueue)
6408
6409 def ja():
6410     "Yes-or-no confirmation."
6411     scanner.chew()
6412     while True:
6413         scanner.next()
6414         if scanner.token == 'y':
6415             return True
6416         if scanner.token == 'n':
6417             return False
6418         scanner.chew()
6419         proutn(_("Please answer with \"y\" or \"n\": "))
6420
6421 def huh():
6422     "Complain about unparseable input."
6423     scanner.chew()
6424     skip(1)
6425     prout(_("Beg your pardon, Captain?"))
6426
6427 def debugme():
6428     "Access to the internals for debugging."
6429     proutn("Reset levels? ")
6430     if ja() == True:
6431         if game.energy < game.inenrg:
6432             game.energy = game.inenrg
6433         game.shield = game.inshld
6434         game.torps = game.intorps
6435         game.lsupres = game.inlsr
6436     proutn("Reset damage? ")
6437     if ja() == True:
6438         for i in range(NDEVICES): 
6439             if game.damage[i] > 0.0: 
6440                 game.damage[i] = 0.0
6441     proutn("Toggle debug flag? ")
6442     if ja() == True:
6443         idebug = not idebug
6444         if idebug:
6445             prout("Debug output ON")        
6446         else:
6447             prout("Debug output OFF")
6448     proutn("Cause selective damage? ")
6449     if ja() == True:
6450         for i in range(NDEVICES):
6451             proutn("Kill %s?" % device[i])
6452             scanner.chew()
6453             key = scanner.next()
6454             if key == "IHALPHA" and scanner.sees("y"):
6455                 game.damage[i] = 10.0
6456     proutn("Examine/change events? ")
6457     if ja() == True:
6458         ev = event()
6459         w = coord()
6460         legends = {
6461             FSNOVA:  "Supernova       ",
6462             FTBEAM:  "T Beam          ",
6463             FSNAP:   "Snapshot        ",
6464             FBATTAK: "Base Attack     ",
6465             FCDBAS:  "Base Destroy    ",
6466             FSCMOVE: "SC Move         ",
6467             FSCDBAS: "SC Base Destroy ",
6468             FDSPROB: "Probe Move      ",
6469             FDISTR:  "Distress Call   ",
6470             FENSLV:  "Enslavement     ",
6471             FREPRO:  "Klingon Build   ",
6472         }
6473         for i in range(1, NEVENTS):
6474             proutn(legends[i])
6475             if is_scheduled(i):
6476                 proutn("%.2f" % (scheduled(i)-game.state.date))
6477                 if i == FENSLV or i == FREPRO:
6478                     ev = findevent(i)
6479                     proutn(" in %s" % ev.quadrant)
6480             else:
6481                 proutn("never")
6482             proutn("? ")
6483             scanner.chew()
6484             key = scanner.next()
6485             if key == 'n':
6486                 unschedule(i)
6487                 scanner.chew()
6488             elif key == "IHREAL":
6489                 ev = schedule(i, scanner.real)
6490                 if i == FENSLV or i == FREPRO:
6491                     scanner.chew()
6492                     proutn("In quadrant- ")
6493                     key = scanner.next()
6494                     # "IHEOL" says to leave coordinates as they are 
6495                     if key != "IHEOL":
6496                         if key != "IHREAL":
6497                             prout("Event %d canceled, no x coordinate." % (i))
6498                             unschedule(i)
6499                             continue
6500                         w.i = int(round(scanner.real))
6501                         key = scanner.next()
6502                         if key != "IHREAL":
6503                             prout("Event %d canceled, no y coordinate." % (i))
6504                             unschedule(i)
6505                             continue
6506                         w.j = int(round(scanner.real))
6507                         ev.quadrant = w
6508         scanner.chew()
6509     proutn("Induce supernova here? ")
6510     if ja() == True:
6511         game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova = True
6512         atover(True)
6513
6514 if __name__ == '__main__':
6515     try:
6516         global line, thing, game, idebug
6517         game = None
6518         thing = coord()
6519         thing.angry = False
6520         game = gamestate()
6521         idebug = 0
6522         game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY)
6523         if os.getenv("TERM"):
6524             game.options |= OPTION_CURSES
6525         else:
6526             game.options |= OPTION_TTY
6527         seed = int(time.time())
6528         (options, arguments) = getopt.getopt(sys.argv[1:], "r:s:tx")
6529         for (switch, val) in options:
6530             if switch == '-r':
6531                 try:
6532                     replayfp = open(val, "r")
6533                 except IOError:
6534                     sys.stderr.write("sst: can't open replay file %s\n" % val)
6535                     raise SystemExit, 1
6536                 try:
6537                     line = replayfp.readline().strip()
6538                     (leader, key, seed) = line.split()
6539                     seed = eval(seed)
6540                     sys.stderr.write("sst2k: seed set to %s\n" % seed)
6541                     line = replayfp.readline().strip()
6542                     arguments += line.split()[2:]
6543                 except ValueError:
6544                     sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
6545                     raise SystemExit(1)
6546                 game.options |= OPTION_TTY
6547                 game.options &=~ OPTION_CURSES
6548             elif switch == '-s':
6549                 seed = int(val)
6550             elif switch == '-t':
6551                 game.options |= OPTION_TTY
6552                 game.options &=~ OPTION_CURSES
6553             elif switch == '-x':
6554                 idebug = True
6555             else:
6556                 sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n")
6557                 raise SystemExit, 1
6558         # where to save the input in case of bugs
6559         try:
6560             logfp = open("/usr/tmp/sst-input.log", "w")
6561         except IOError:
6562             sys.stderr.write("sst: warning, can't open logfile\n")
6563         if logfp:
6564             logfp.write("# seed %s\n" % seed)
6565             logfp.write("# options %s\n" % " ".join(arguments))
6566         random.seed(seed)
6567         scanner = sstscanner()
6568         map(scanner.append, arguments)
6569         try:
6570             iostart()
6571             while True: # Play a game 
6572                 setwnd(fullscreen_window)
6573                 clrscr()
6574                 prelim()
6575                 setup()
6576                 if game.alldone:
6577                     score()
6578                     game.alldone = False
6579                 else:
6580                     makemoves()
6581                 skip(1)
6582                 stars()
6583                 skip(1)
6584                 if game.tourn and game.alldone:
6585                     proutn(_("Do you want your score recorded?"))
6586                     if ja() == True:
6587                         scanner.chew2()
6588                         freeze(False)
6589                 scanner.chew()
6590                 proutn(_("Do you want to play again? "))
6591                 if not ja():
6592                     break
6593             skip(1)
6594             prout(_("May the Great Bird of the Galaxy roost upon your home planet."))
6595         finally:
6596             ioend()
6597         raise SystemExit, 0
6598     except KeyboardInterrupt:
6599         if logfp:
6600             logfp.close()
6601         print ""
6602         pass