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