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