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