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