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