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