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