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