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