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