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