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