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