this enabled UTF8 for some recent versions of python2.
[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     "for some recent versions of python2, the following enables UTF8"
3067     "for the older ones we probably need to set C locale, and the python3"
3068     "has no problems at all"
3069     if sys.version_info.major < 3:
3070         import locale
3071         locale.setlocale(locale.LC_ALL, "")
3072     gettext.bindtextdomain("sst", "/usr/local/share/locale")
3073     gettext.textdomain("sst")
3074     if not (game.options & OPTION_CURSES):
3075         ln_env = os.getenv("LINES")
3076         if ln_env:
3077             rows = ln_env
3078         else:
3079             rows = 25
3080     else:
3081         stdscr = curses.initscr()
3082         stdscr.keypad(True)
3083         curses.nonl()
3084         curses.cbreak()
3085         if game.options & OPTION_COLOR:
3086             curses.start_color();
3087             curses.use_default_colors()
3088             curses.init_pair(curses.COLOR_BLACK,   curses.COLOR_BLACK, -1);
3089             curses.init_pair(curses.COLOR_GREEN,   curses.COLOR_GREEN, -1);
3090             curses.init_pair(curses.COLOR_RED,     curses.COLOR_RED, -1);
3091             curses.init_pair(curses.COLOR_CYAN,    curses.COLOR_CYAN, -1);
3092             curses.init_pair(curses.COLOR_WHITE,   curses.COLOR_WHITE, -1);
3093             curses.init_pair(curses.COLOR_MAGENTA, curses.COLOR_MAGENTA, -1);
3094             curses.init_pair(curses.COLOR_BLUE,    curses.COLOR_BLUE, -1);
3095             curses.init_pair(curses.COLOR_YELLOW,  curses.COLOR_YELLOW, -1);
3096         global fullscreen_window, srscan_window, report_window, status_window
3097         global lrscan_window, message_window, prompt_window
3098         (rows, columns)   = stdscr.getmaxyx()
3099         fullscreen_window = stdscr
3100         srscan_window     = curses.newwin(12, 25, 0,       0)
3101         report_window     = curses.newwin(11, 0,  1,       25)
3102         status_window     = curses.newwin(10, 0,  1,       39)
3103         lrscan_window     = curses.newwin(5,  0,  0,       64) 
3104         message_window    = curses.newwin(0,  0,  12,      0)
3105         prompt_window     = curses.newwin(1,  0,  rows-2,  0) 
3106         message_window.scrollok(True)
3107         setwnd(fullscreen_window)
3108
3109 def ioend():
3110     "Wrap up I/O."
3111     if game.options & OPTION_CURSES:
3112         stdscr.keypad(False)
3113         curses.echo()
3114         curses.nocbreak()
3115         curses.endwin()
3116
3117 def waitfor():
3118     "Wait for user action -- OK to do nothing if on a TTY"
3119     if game.options & OPTION_CURSES:
3120         stdscr.getch()
3121
3122 def announce():
3123     skip(1)
3124     prouts(_("[ANNOUNCEMENT ARRIVING...]"))
3125     skip(1)
3126
3127 def pause_game():
3128     if game.skill > SKILL_FAIR:
3129         prompt = _("[CONTINUE?]")
3130     else:
3131         prompt = _("[PRESS ENTER TO CONTINUE]")
3132
3133     if game.options & OPTION_CURSES:
3134         drawmaps(0)
3135         setwnd(prompt_window)
3136         prompt_window.clear()
3137         prompt_window.addstr(prompt)
3138         prompt_window.getstr()
3139         prompt_window.clear()
3140         prompt_window.refresh()
3141         setwnd(message_window)
3142     else:
3143         global linecount
3144         sys.stdout.write('\n')
3145         proutn(prompt)
3146         raw_input()
3147         for j in range(rows):
3148             sys.stdout.write('\n')
3149         linecount = 0
3150
3151 def skip(i):
3152     "Skip i lines.  Pause game if this would cause a scrolling event."
3153     for dummy in range(i):
3154         if game.options & OPTION_CURSES:
3155             (y, x) = curwnd.getyx()
3156             (my, mx) = curwnd.getmaxyx()
3157             if curwnd == message_window and y >= my - 3:
3158                 pause_game()
3159                 clrscr()
3160             else:
3161                 try:
3162                     curwnd.move(y+1, 0)
3163                 except curses.error:
3164                     pass
3165         else:
3166             global linecount
3167             linecount += 1
3168             if rows and linecount >= rows:
3169                 pause_game()
3170             else:
3171                 sys.stdout.write('\n')
3172
3173 def proutn(line):
3174     "Utter a line with no following line feed."
3175     if game.options & OPTION_CURSES:
3176         curwnd.addstr(line)
3177         curwnd.refresh()
3178     else:
3179         sys.stdout.write(line)
3180         sys.stdout.flush()
3181
3182 def prout(line):
3183     proutn(line)
3184     skip(1)
3185
3186 def prouts(line):
3187     "Emit slowly!" 
3188     for c in line:
3189         if not replayfp or replayfp.closed:     # Don't slow down replays
3190             time.sleep(0.03)
3191         proutn(c)
3192         if game.options & OPTION_CURSES:
3193             curwnd.refresh()
3194         else:
3195             sys.stdout.flush()
3196     if not replayfp or replayfp.closed:
3197         time.sleep(0.03)
3198
3199 def cgetline():
3200     "Get a line of input."
3201     if game.options & OPTION_CURSES:
3202         line = curwnd.getstr() + "\n"
3203         curwnd.refresh()
3204     else:
3205         if replayfp and not replayfp.closed:
3206             while True:
3207                 line = replayfp.readline()
3208                 proutn(line)
3209                 if line == '':
3210                     prout("*** Replay finished")
3211                     replayfp.close()
3212                     break
3213                 elif line[0] != "#":
3214                     break
3215         else:
3216             line = raw_input() + "\n"
3217     if logfp:
3218         logfp.write(line)
3219     return line
3220
3221 def setwnd(wnd):
3222     "Change windows -- OK for this to be a no-op in tty mode."
3223     global curwnd
3224     if game.options & OPTION_CURSES:
3225         curwnd = wnd
3226         curses.curs_set(wnd == fullscreen_window or wnd == message_window or wnd == prompt_window)
3227
3228 def clreol():
3229     "Clear to end of line -- can be a no-op in tty mode" 
3230     if game.options & OPTION_CURSES:
3231         curwnd.clrtoeol()
3232         curwnd.refresh()
3233
3234 def clrscr():
3235     "Clear screen -- can be a no-op in tty mode."
3236     global linecount
3237     if game.options & OPTION_CURSES:
3238        curwnd.clear()
3239        curwnd.move(0, 0)
3240        curwnd.refresh()
3241     linecount = 0
3242
3243 def textcolor(color=DEFAULT):
3244     if game.options & OPTION_COLOR:
3245         if color == DEFAULT: 
3246             curwnd.attrset(0);
3247         elif color ==  BLACK: 
3248             curwnd.attron(curses.color_pair(curses.COLOR_BLACK));
3249         elif color ==  BLUE: 
3250             curwnd.attron(curses.color_pair(curses.COLOR_BLUE));
3251         elif color ==  GREEN: 
3252             curwnd.attron(curses.color_pair(curses.COLOR_GREEN));
3253         elif color ==  CYAN: 
3254             curwnd.attron(curses.color_pair(curses.COLOR_CYAN));
3255         elif color ==  RED: 
3256             curwnd.attron(curses.color_pair(curses.COLOR_RED));
3257         elif color ==  MAGENTA: 
3258             curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA));
3259         elif color ==  BROWN: 
3260             curwnd.attron(curses.color_pair(curses.COLOR_YELLOW));
3261         elif color ==  LIGHTGRAY: 
3262             curwnd.attron(curses.color_pair(curses.COLOR_WHITE));
3263         elif color ==  DARKGRAY: 
3264             curwnd.attron(curses.color_pair(curses.COLOR_BLACK) | curses.A_BOLD);
3265         elif color ==  LIGHTBLUE: 
3266             curwnd.attron(curses.color_pair(curses.COLOR_BLUE) | curses.A_BOLD);
3267         elif color ==  LIGHTGREEN: 
3268             curwnd.attron(curses.color_pair(curses.COLOR_GREEN) | curses.A_BOLD);
3269         elif color ==  LIGHTCYAN: 
3270             curwnd.attron(curses.color_pair(curses.COLOR_CYAN) | curses.A_BOLD);
3271         elif color ==  LIGHTRED: 
3272             curwnd.attron(curses.color_pair(curses.COLOR_RED) | curses.A_BOLD);
3273         elif color ==  LIGHTMAGENTA: 
3274             curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA) | curses.A_BOLD);
3275         elif color ==  YELLOW: 
3276             curwnd.attron(curses.color_pair(curses.COLOR_YELLOW) | curses.A_BOLD);
3277         elif color ==  WHITE:
3278             curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD);
3279
3280 def highvideo():
3281     if game.options & OPTION_COLOR:
3282         curwnd.attron(curses.A_REVERSE)
3283
3284 #
3285 # Things past this point have policy implications.
3286
3287
3288 def drawmaps(mode):
3289     "Hook to be called after moving to redraw maps."
3290     if game.options & OPTION_CURSES:
3291         if mode == 1:
3292             sensor()
3293         setwnd(srscan_window)
3294         curwnd.move(0, 0)
3295         srscan()
3296         if mode != 2:
3297             setwnd(status_window)
3298             status_window.clear()
3299             status_window.move(0, 0)
3300             setwnd(report_window)
3301             report_window.clear()
3302             report_window.move(0, 0)
3303             status()
3304             setwnd(lrscan_window)
3305             lrscan_window.clear()
3306             lrscan_window.move(0, 0)
3307             lrscan(silent=False)
3308
3309 def put_srscan_sym(w, sym):
3310     "Emit symbol for short-range scan."
3311     srscan_window.move(w.i+1, w.j*2+2)
3312     srscan_window.addch(sym)
3313     srscan_window.refresh()
3314
3315 def boom(w):
3316     "Enemy fall down, go boom."  
3317     if game.options & OPTION_CURSES:
3318         drawmaps(2)
3319         setwnd(srscan_window)
3320         srscan_window.attron(curses.A_REVERSE)
3321         put_srscan_sym(w, game.quad[w.i][w.j])
3322         #sound(500)
3323         #time.sleep(1.0)
3324         #nosound()
3325         srscan_window.attroff(curses.A_REVERSE)
3326         put_srscan_sym(w, game.quad[w.i][w.j])
3327         curses.delay_output(500)
3328         setwnd(message_window) 
3329
3330 def warble():
3331     "Sound and visual effects for teleportation."
3332     if game.options & OPTION_CURSES:
3333         drawmaps(2)
3334         setwnd(message_window)
3335         #sound(50)
3336     prouts("     . . . . .     ")
3337     if game.options & OPTION_CURSES:
3338         #curses.delay_output(1000)
3339         #nosound()
3340         pass
3341
3342 def tracktorpedo(origin, w, step, i, n, iquad):
3343     "Torpedo-track animation." 
3344     if not game.options & OPTION_CURSES:
3345         if step == 1:
3346             if n != 1:
3347                 skip(1)
3348                 proutn(_("Track for torpedo number %d-  ") % (i+1))
3349             else:
3350                 skip(1)
3351                 proutn(_("Torpedo track- "))
3352         elif step==4 or step==9: 
3353             skip(1)
3354         proutn("%s   " % w)
3355     else:
3356         if not damaged(DSRSENS) or game.condition=="docked":
3357             if i != 0 and step == 1:
3358                 drawmaps(2)
3359                 time.sleep(0.4)
3360             if (iquad=='.') or (iquad==' '):
3361                 put_srscan_sym(w, '+')
3362                 #sound(step*10)
3363                 #time.sleep(0.1)
3364                 #nosound()
3365                 put_srscan_sym(w, iquad)
3366             else:
3367                 curwnd.attron(curses.A_REVERSE)
3368                 put_srscan_sym(w, iquad)
3369                 #sound(500)
3370                 #time.sleep(1.0)
3371                 #nosound()
3372                 curwnd.attroff(curses.A_REVERSE)
3373                 put_srscan_sym(w, iquad)
3374         else:
3375             proutn("%s   " % w)
3376
3377 def makechart():
3378     "Display the current galaxy chart."
3379     if game.options & OPTION_CURSES:
3380         setwnd(message_window)
3381         message_window.clear()
3382     chart()
3383     if game.options & OPTION_TTY:
3384         skip(1)
3385
3386 NSYM    = 14
3387
3388 def prstat(txt, data):
3389     proutn(txt)
3390     if game.options & OPTION_CURSES:
3391         skip(1)
3392         setwnd(status_window)
3393     else:
3394         proutn(" " * (NSYM - len(txt)))
3395     proutn(data)
3396     skip(1)
3397     if game.options & OPTION_CURSES:
3398         setwnd(report_window)
3399
3400 # Code from moving.c begins here
3401
3402 def imove(course=None, noattack=False):
3403     "Movement execution for warp, impulse, supernova, and tractor-beam events."
3404     w = coord()
3405
3406     def newquadrant(noattack):
3407         # Leaving quadrant -- allow final enemy attack 
3408         # Don't do it if being pushed by Nova 
3409         if len(game.enemies) != 0 and not noattack:
3410             newcnd()
3411             for enemy in game.enemies:
3412                 finald = (w - enemy.location).distance()
3413                 enemy.kavgd = 0.5 * (finald + enemy.kdist)
3414             # Stas Sergeev added the condition
3415             # that attacks only happen if Klingons
3416             # are present and your skill is good.
3417             if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3418                 attack(torps_ok=False)
3419             if game.alldone:
3420                 return
3421         # check for edge of galaxy 
3422         kinks = 0
3423         while True:
3424             kink = False
3425             if course.final.i < 0:
3426                 course.final.i = -course.final.i
3427                 kink = True
3428             if course.final.j < 0:
3429                 course.final.j = -course.final.j
3430                 kink = True
3431             if course.final.i >= GALSIZE*QUADSIZE:
3432                 course.final.i = (GALSIZE*QUADSIZE*2) - course.final.i
3433                 kink = True
3434             if course.final.j >= GALSIZE*QUADSIZE:
3435                 course.final.j = (GALSIZE*QUADSIZE*2) - course.final.j
3436                 kink = True
3437             if kink:
3438                 kinks += 1
3439             else:
3440                 break
3441         if kinks:
3442             game.nkinks += 1
3443             if game.nkinks == 3:
3444                 # Three strikes -- you're out! 
3445                 finish(FNEG3)
3446                 return
3447             skip(1)
3448             prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
3449             prout(_("AT THE EDGE OF THE GALAXY.  THE THIRD TIME YOU TRY THIS,"))
3450             prout(_("YOU WILL BE DESTROYED."))
3451         # Compute final position in new quadrant 
3452         if trbeam: # Don't bother if we are to be beamed 
3453             return
3454         game.quadrant = course.final.quadrant()
3455         game.sector = course.final.sector()
3456         skip(1)
3457         prout(_("Entering Quadrant %s.") % game.quadrant)
3458         game.quad[game.sector.i][game.sector.j] = game.ship
3459         newqad()
3460         if game.skill>SKILL_NOVICE:
3461             attack(torps_ok=False)  
3462
3463     def check_collision(h):
3464         iquad = game.quad[h.i][h.j]
3465         if iquad != '.':
3466             # object encountered in flight path 
3467             stopegy = 50.0*course.distance/game.optime
3468             if iquad in ('T', 'K', 'C', 'S', 'R', '?'):
3469                 for enemy in game.enemies:
3470                     if enemy.location == game.sector:
3471                         break
3472                 collision(rammed=False, enemy=enemy)
3473                 return True
3474             elif iquad == ' ':
3475                 skip(1)
3476                 prouts(_("***RED ALERT!  RED ALERT!"))
3477                 skip(1)
3478                 proutn("***" + crmshp())
3479                 proutn(_(" pulled into black hole at Sector %s") % h)
3480                 # Getting pulled into a black hole was certain
3481                 # death in Almy's original.  Stas Sergeev added a
3482                 # possibility that you'll get timewarped instead.
3483                 n=0
3484                 for m in range(NDEVICES):
3485                     if game.damage[m]>0: 
3486                         n += 1
3487                 probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
3488                 if (game.options & OPTION_BLKHOLE) and withprob(1-probf): 
3489                     timwrp()
3490                 else: 
3491                     finish(FHOLE)
3492                 return True
3493             else:
3494                 # something else 
3495                 skip(1)
3496                 proutn(crmshp())
3497                 if iquad == '#':
3498                     prout(_(" encounters Tholian web at %s;") % h)
3499                 else:
3500                     prout(_(" blocked by object at %s;") % h)
3501                 proutn(_("Emergency stop required "))
3502                 prout(_("%2d units of energy.") % int(stopegy))
3503                 game.energy -= stopegy
3504                 if game.energy <= 0:
3505                     finish(FNRG)
3506                 return True
3507         return False
3508
3509     trbeam = False
3510     if game.inorbit:
3511         prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
3512         game.inorbit = False
3513     # If tractor beam is to occur, don't move full distance 
3514     if game.state.date+game.optime >= scheduled(FTBEAM):
3515         trbeam = True
3516         game.condition = "red"
3517         course.distance = course.distance*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
3518         game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
3519     # Move out
3520     game.quad[game.sector.i][game.sector.j] = '.'
3521     for m in range(course.moves):
3522         course.next()
3523         w = course.sector()
3524         if course.origin.quadrant() != course.location.quadrant():
3525             newquadrant(noattack)
3526             break
3527         elif check_collision(w):
3528             print "Collision detected"
3529             break
3530         else:
3531             game.sector = w
3532     # We're in destination quadrant -- compute new average enemy distances
3533     game.quad[game.sector.i][game.sector.j] = game.ship
3534     if game.enemies:
3535         for enemy in game.enemies:
3536             finald = (w-enemy.location).distance()
3537             enemy.kavgd = 0.5 * (finald + enemy.kdist)
3538             enemy.kdist = finald
3539         game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
3540         if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3541             attack(torps_ok=False)
3542         for enemy in game.enemies:
3543             enemy.kavgd = enemy.kdist
3544     newcnd()
3545     drawmaps(0)
3546     setwnd(message_window)
3547     return
3548
3549 def dock(verbose):
3550     "Dock our ship at a starbase."
3551     scanner.chew()
3552     if game.condition == "docked" and verbose:
3553         prout(_("Already docked."))
3554         return
3555     if game.inorbit:
3556         prout(_("You must first leave standard orbit."))
3557         return
3558     if not game.base.is_valid() or abs(game.sector.i-game.base.i) > 1 or abs(game.sector.j-game.base.j) > 1:
3559         prout(crmshp() + _(" not adjacent to base."))
3560         return
3561     game.condition = "docked"
3562     if "verbose":
3563         prout(_("Docked."))
3564     game.ididit = True
3565     if game.energy < game.inenrg:
3566         game.energy = game.inenrg
3567     game.shield = game.inshld
3568     game.torps = game.intorps
3569     game.lsupres = game.inlsr
3570     game.state.crew = FULLCREW
3571     if not damaged(DRADIO) and \
3572         ((is_scheduled(FCDBAS) or game.isatb == 1) and not game.iseenit):
3573         # get attack report from base 
3574         prout(_("Lt. Uhura- \"Captain, an important message from the starbase:\""))
3575         attackreport(False)
3576         game.iseenit = True
3577
3578 def cartesian(loc1=None, loc2=None):
3579     if loc1 is None:
3580         return game.quadrant * QUADSIZE + game.sector
3581     elif loc2 is None:
3582         return game.quadrant * QUADSIZE + loc1
3583     else:
3584         return loc1 * QUADSIZE + loc2
3585
3586 def getcourse(isprobe):
3587     "Get a course and distance from the user."
3588     key = 0
3589     dquad = copy.copy(game.quadrant)
3590     navmode = "unspecified"
3591     itemp = "curt"
3592     dsect = coord()
3593     iprompt = False
3594     if game.landed and not isprobe:
3595         prout(_("Dummy! You can't leave standard orbit until you"))
3596         proutn(_("are back aboard the ship."))
3597         scanner.chew()
3598         raise TrekError
3599     while navmode == "unspecified":
3600         if damaged(DNAVSYS):
3601             if isprobe:
3602                 prout(_("Computer damaged; manual navigation only"))
3603             else:
3604                 prout(_("Computer damaged; manual movement only"))
3605             scanner.chew()
3606             navmode = "manual"
3607             key = "IHEOL"
3608             break
3609         key = scanner.next()
3610         if key == "IHEOL":
3611             proutn(_("Manual or automatic- "))
3612             iprompt = True
3613             scanner.chew()
3614         elif key == "IHALPHA":
3615             if scanner.sees("manual"):
3616                 navmode = "manual"
3617                 key = scanner.next()
3618                 break
3619             elif scanner.sees("automatic"):
3620                 navmode = "automatic"
3621                 key = scanner.next()
3622                 break
3623             else:
3624                 huh()
3625                 scanner.chew()
3626                 raise TrekError
3627         else: # numeric 
3628             if isprobe:
3629                 prout(_("(Manual navigation assumed.)"))
3630             else:
3631                 prout(_("(Manual movement assumed.)"))
3632             navmode = "manual"
3633             break
3634     delta = coord()
3635     if navmode == "automatic":
3636         while key == "IHEOL":
3637             if isprobe:
3638                 proutn(_("Target quadrant or quadrant&sector- "))
3639             else:
3640                 proutn(_("Destination sector or quadrant&sector- "))
3641             scanner.chew()
3642             iprompt = True
3643             key = scanner.next()
3644         if key != "IHREAL":
3645             huh()
3646             raise TrekError
3647         xi = int(round(scanner.real))-1
3648         key = scanner.next()
3649         if key != "IHREAL":
3650             huh()
3651             raise TrekError
3652         xj = int(round(scanner.real))-1
3653         key = scanner.next()
3654         if key == "IHREAL":
3655             # both quadrant and sector specified 
3656             xk = int(round(scanner.real))-1
3657             key = scanner.next()
3658             if key != "IHREAL":
3659                 huh()
3660                 raise TrekError
3661             xl = int(round(scanner.real))-1
3662             dquad.i = xi
3663             dquad.j = xj
3664             dsect.i = xk
3665             dsect.j = xl
3666         else:
3667             # only one pair of numbers was specified
3668             if isprobe:
3669                 # only quadrant specified -- go to center of dest quad 
3670                 dquad.i = xi
3671                 dquad.j = xj
3672                 dsect.j = dsect.i = 4   # preserves 1-origin behavior
3673             else:
3674                 # only sector specified
3675                 dsect.i = xi
3676                 dsect.j = xj
3677             itemp = "normal"
3678         if not dquad.valid_quadrant() or not dsect.valid_sector():
3679             huh()
3680             raise TrekError
3681         skip(1)
3682         if not isprobe:
3683             if itemp > "curt":
3684                 if iprompt:
3685                     prout(_("Helmsman Sulu- \"Course locked in for Sector %s.\"") % dsect)
3686             else:
3687                 prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
3688         # the actual deltas get computed here
3689         delta.j = dquad.j-game.quadrant.j + (dsect.j-game.sector.j)/(QUADSIZE*1.0)
3690         delta.i = game.quadrant.i-dquad.i + (game.sector.i-dsect.i)/(QUADSIZE*1.0)
3691     else: # manual 
3692         while key == "IHEOL":
3693             proutn(_("X and Y displacements- "))
3694             scanner.chew()
3695             iprompt = True
3696             key = scanner.next()
3697         itemp = "verbose"
3698         if key != "IHREAL":
3699             huh()
3700             raise TrekError
3701         delta.j = scanner.real
3702         key = scanner.next()
3703         if key != "IHREAL":
3704             huh()
3705             raise TrekError
3706         delta.i = scanner.real
3707     # Check for zero movement 
3708     if delta.i == 0 and delta.j == 0:
3709         scanner.chew()
3710         raise TrekError
3711     if itemp == "verbose" and not isprobe:
3712         skip(1)
3713         prout(_("Helmsman Sulu- \"Aye, Sir.\""))
3714     scanner.chew()
3715     return course(bearing=delta.bearing(), distance=delta.distance())
3716
3717 class course:
3718     def __init__(self, bearing, distance, origin=None): 
3719         self.distance = distance
3720         self.bearing = bearing
3721         if origin is None:
3722             self.origin = cartesian(game.quadrant, game.sector)
3723         else:
3724             self.origin = origin
3725         # The bearing() code we inherited from FORTRAN is actually computing
3726         # clockface directions!
3727         if self.bearing < 0.0:
3728             self.bearing += 12.0
3729         self.angle = ((15.0 - self.bearing) * 0.5235988)
3730         if origin is None:
3731             self.origin = cartesian(game.quadrant, game.sector)
3732         else:
3733             self.origin = cartesian(game.quadrant, origin)
3734         self.increment = coord(-math.sin(self.angle), math.cos(self.angle))
3735         bigger = max(abs(self.increment.i), abs(self.increment.j))
3736         self.increment /= bigger
3737         self.moves = int(round(10*self.distance*bigger))
3738         self.reset()
3739         self.final = (self.location + self.moves*self.increment).roundtogrid()
3740     def reset(self):
3741         self.location = self.origin
3742         self.step = 0
3743     def arrived(self):
3744         return self.location.roundtogrid() == self.final
3745     def next(self):
3746         "Next step on course."
3747         self.step += 1
3748         self.nextlocation = self.location + self.increment
3749         samequad = (self.location.quadrant() == self.nextlocation.quadrant())
3750         self.location = self.nextlocation
3751         return samequad
3752     def quadrant(self):
3753         return self.location.quadrant()
3754     def sector(self):
3755         return self.location.sector()
3756     def power(self, warp):
3757         return self.distance*(warp**3)*(game.shldup+1)
3758     def time(self, warp):
3759         return 10.0*self.distance/warp**2
3760
3761 def impulse():
3762     "Move under impulse power."
3763     game.ididit = False
3764     if damaged(DIMPULS):
3765         scanner.chew()
3766         skip(1)
3767         prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
3768         return
3769     if game.energy > 30.0:
3770         try:
3771             course = getcourse(isprobe=False)
3772         except TrekError:
3773             return
3774         power = 20.0 + 100.0*course.distance
3775     else:
3776         power = 30.0
3777     if power >= game.energy:
3778         # Insufficient power for trip 
3779         skip(1)
3780         prout(_("First Officer Spock- \"Captain, the impulse engines"))
3781         prout(_("require 20.0 units to engage, plus 100.0 units per"))
3782         if game.energy > 30:
3783             proutn(_("quadrant.  We can go, therefore, a maximum of %d") %
3784                      int(0.01 * (game.energy-20.0)-0.05))
3785             prout(_(" quadrants.\""))
3786         else:
3787             prout(_("quadrant.  They are, therefore, useless.\""))
3788         scanner.chew()
3789         return
3790     # Make sure enough time is left for the trip 
3791     game.optime = course.dist/0.095
3792     if game.optime >= game.state.remtime:
3793         prout(_("First Officer Spock- \"Captain, our speed under impulse"))
3794         prout(_("power is only 0.95 sectors per stardate. Are you sure"))
3795         proutn(_("we dare spend the time?\" "))
3796         if ja() == False:
3797             return
3798     # Activate impulse engines and pay the cost 
3799     imove(course, noattack=False)
3800     game.ididit = True
3801     if game.alldone:
3802         return
3803     power = 20.0 + 100.0*course.dist
3804     game.energy -= power
3805     game.optime = course.dist/0.095
3806     if game.energy <= 0:
3807         finish(FNRG)
3808     return
3809
3810 def warp(course, involuntary):
3811     "ove under warp drive."
3812     blooey = False; twarp = False
3813     if not involuntary: # Not WARPX entry 
3814         game.ididit = False
3815         if game.damage[DWARPEN] > 10.0:
3816             scanner.chew()
3817             skip(1)
3818             prout(_("Engineer Scott- \"The warp engines are damaged, Sir.\""))
3819             return
3820         if damaged(DWARPEN) and game.warpfac > 4.0:
3821             scanner.chew()
3822             skip(1)
3823             prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
3824             prout(_("  is repaired, I can only give you warp 4.\""))
3825             return
3826         # Read in course and distance
3827         if course==None:
3828             try:
3829                 course = getcourse(isprobe=False)
3830             except TrekError:
3831                 return
3832         # Make sure starship has enough energy for the trip
3833         # Note: this formula is slightly different from the C version,
3834         # and lets you skate a bit closer to the edge.
3835         if course.power(game.warpfac) >= game.energy:
3836             # Insufficient power for trip 
3837             game.ididit = False
3838             skip(1)
3839             prout(_("Engineering to bridge--"))
3840             if not game.shldup or 0.5*power > game.energy:
3841                 iwarp = (game.energy/(course.dist+0.05)) ** 0.333333333
3842                 if iwarp <= 0:
3843                     prout(_("We can't do it, Captain. We don't have enough energy."))
3844                 else:
3845                     proutn(_("We don't have enough energy, but we could do it at warp %d") % iwarp)
3846                     if game.shldup:
3847                         prout(",")
3848                         prout(_("if you'll lower the shields."))
3849                     else:
3850                         prout(".")
3851             else:
3852                 prout(_("We haven't the energy to go that far with the shields up."))
3853             return                              
3854         # Make sure enough time is left for the trip 
3855         game.optime = course.time(game.warpfac)
3856         if game.optime >= 0.8*game.state.remtime:
3857             skip(1)
3858             prout(_("First Officer Spock- \"Captain, I compute that such"))
3859             proutn(_("  a trip would require approximately %2.0f") %
3860                    (100.0*game.optime/game.state.remtime))
3861             prout(_(" percent of our"))
3862             proutn(_("  remaining time.  Are you sure this is wise?\" "))
3863             if ja() == False:
3864                 game.ididit = False
3865                 game.optime=0 
3866                 return
3867     # Entry WARPX 
3868     if game.warpfac > 6.0:
3869         # Decide if engine damage will occur
3870         # ESR: Seems wrong. Probability of damage goes *down* with distance? 
3871         prob = course.distance*(6.0-game.warpfac)**2/66.666666666
3872         if prob > randreal():
3873             blooey = True
3874             course.distance = randreal(course.distance)
3875         # Decide if time warp will occur 
3876         if 0.5*course.distance*math.pow(7.0,game.warpfac-10.0) > randreal():
3877             twarp = True
3878         if idebug and game.warpfac==10 and not twarp:
3879             blooey = False
3880             proutn("=== Force time warp? ")
3881             if ja() == True:
3882                 twarp = True
3883         if blooey or twarp:
3884             # If time warp or engine damage, check path 
3885             # If it is obstructed, don't do warp or damage 
3886             for m in range(course.moves):
3887                 course.next()
3888                 w = course.sector()
3889                 if not w.valid_sector():
3890                     break
3891                 if game.quad[w.i][w.j] != '.':
3892                     blooey = False
3893                     twarp = False
3894             course.reset()
3895     # Activate Warp Engines and pay the cost 
3896     imove(course, noattack=False)
3897     if game.alldone:
3898         return
3899     game.energy -= course.power(game.warpfac)
3900     if game.energy <= 0:
3901         finish(FNRG)
3902     game.optime = course.time(game.warpfac)
3903     if twarp:
3904         timwrp()
3905     if blooey:
3906         game.damage[DWARPEN] = game.damfac * randreal(1.0, 4.0)
3907         skip(1)
3908         prout(_("Engineering to bridge--"))
3909         prout(_("  Scott here.  The warp engines are damaged."))
3910         prout(_("  We'll have to reduce speed to warp 4."))
3911     game.ididit = True
3912     return
3913
3914 def setwarp():
3915     "Change the warp factor."
3916     while True:
3917         key=scanner.next()
3918         if key != "IHEOL":
3919             break
3920         scanner.chew()
3921         proutn(_("Warp factor- "))
3922     scanner.chew()
3923     if key != "IHREAL":
3924         huh()
3925         return
3926     if game.damage[DWARPEN] > 10.0:
3927         prout(_("Warp engines inoperative."))
3928         return
3929     if damaged(DWARPEN) and scanner.real > 4.0:
3930         prout(_("Engineer Scott- \"I'm doing my best, Captain,"))
3931         prout(_("  but right now we can only go warp 4.\""))
3932         return
3933     if scanner.real > 10.0:
3934         prout(_("Helmsman Sulu- \"Our top speed is warp 10, Captain.\""))
3935         return
3936     if scanner.real < 1.0:
3937         prout(_("Helmsman Sulu- \"We can't go below warp 1, Captain.\""))
3938         return
3939     oldfac = game.warpfac
3940     game.warpfac = scanner.real
3941     if game.warpfac <= oldfac or game.warpfac <= 6.0:
3942         prout(_("Helmsman Sulu- \"Warp factor %d, Captain.\"") %
3943                int(game.warpfac))
3944         return
3945     if game.warpfac < 8.00:
3946         prout(_("Engineer Scott- \"Aye, but our maximum safe speed is warp 6.\""))
3947         return
3948     if game.warpfac == 10.0:
3949         prout(_("Engineer Scott- \"Aye, Captain, we'll try it.\""))
3950         return
3951     prout(_("Engineer Scott- \"Aye, Captain, but our engines may not take it.\""))
3952     return
3953
3954 def atover(igrab):
3955     "Cope with being tossed out of quadrant by supernova or yanked by beam."
3956     scanner.chew()
3957     # is captain on planet? 
3958     if game.landed:
3959         if damaged(DTRANSP):
3960             finish(FPNOVA)
3961             return
3962         prout(_("Scotty rushes to the transporter controls."))
3963         if game.shldup:
3964             prout(_("But with the shields up it's hopeless."))
3965             finish(FPNOVA)
3966         prouts(_("His desperate attempt to rescue you . . ."))
3967         if withprob(0.5):
3968             prout(_("fails."))
3969             finish(FPNOVA)
3970             return
3971         prout(_("SUCCEEDS!"))
3972         if game.imine:
3973             game.imine = False
3974             proutn(_("The crystals mined were "))
3975             if withprob(0.25):
3976                 prout(_("lost."))
3977             else:
3978                 prout(_("saved."))
3979                 game.icrystl = True
3980     if igrab:
3981         return
3982     # Check to see if captain in shuttle craft 
3983     if game.icraft:
3984         finish(FSTRACTOR)
3985     if game.alldone:
3986         return
3987     # Inform captain of attempt to reach safety 
3988     skip(1)
3989     while True:
3990         if game.justin:
3991             prouts(_("***RED ALERT!  RED ALERT!"))
3992             skip(1)
3993             proutn(_("The %s has stopped in a quadrant containing") % crmshp())
3994             prouts(_("   a supernova."))
3995             skip(2)
3996         prout(_("***Emergency automatic override attempts to hurl ")+crmshp())
3997         prout(_("safely out of quadrant."))
3998         if not damaged(DRADIO):
3999             game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
4000         # Try to use warp engines 
4001         if damaged(DWARPEN):
4002             skip(1)
4003             prout(_("Warp engines damaged."))
4004             finish(FSNOVAED)
4005             return
4006         game.warpfac = randreal(6.0, 8.0)
4007         prout(_("Warp factor set to %d") % int(game.warpfac))
4008         power = 0.75*game.energy
4009         dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1))
4010         dist = max(dist, randreal(math.sqrt(2)))
4011         bugout = course(bearing=randreal(12), distance=dist)    # How dumb!
4012         game.optime = bugout.time(game.warpfac)
4013         game.justin = False
4014         game.inorbit = False
4015         warp(bugout, involuntary=True)
4016         if not game.justin:
4017             # This is bad news, we didn't leave quadrant. 
4018             if game.alldone:
4019                 return
4020             skip(1)
4021             prout(_("Insufficient energy to leave quadrant."))
4022             finish(FSNOVAED)
4023             return
4024         # Repeat if another snova
4025         if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
4026             break
4027     if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0: 
4028         finish(FWON) # Snova killed remaining enemy. 
4029
4030 def timwrp():
4031     "Let's do the time warp again."
4032     prout(_("***TIME WARP ENTERED."))
4033     if game.state.snap and withprob(0.5):
4034         # Go back in time 
4035         prout(_("You are traveling backwards in time %d stardates.") %
4036               int(game.state.date-game.snapsht.date))
4037         game.state = game.snapsht
4038         game.state.snap = False
4039         if len(game.state.kcmdr):
4040             schedule(FTBEAM, expran(game.intime/len(game.state.kcmdr)))
4041             schedule(FBATTAK, expran(0.3*game.intime))
4042         schedule(FSNOVA, expran(0.5*game.intime))
4043         # next snapshot will be sooner 
4044         schedule(FSNAP, expran(0.25*game.state.remtime))
4045                                 
4046         if game.state.nscrem:
4047             schedule(FSCMOVE, 0.2777)       
4048         game.isatb = 0
4049         unschedule(FCDBAS)
4050         unschedule(FSCDBAS)
4051         game.battle.invalidate()
4052         # Make sure Galileo is consistant -- Snapshot may have been taken
4053         # when on planet, which would give us two Galileos! 
4054         gotit = False
4055         for l in range(game.inplan):
4056             if game.state.planets[l].known == "shuttle_down":
4057                 gotit = True
4058                 if game.iscraft == "onship" and game.ship=='E':
4059                     prout(_("Chekov-  \"Security reports the Galileo has disappeared, Sir!"))
4060                     game.iscraft = "offship"
4061         # Likewise, if in the original time the Galileo was abandoned, but
4062         # was on ship earlier, it would have vanished -- let's restore it.
4063         if game.iscraft == "offship" and not gotit and game.damage[DSHUTTL] >= 0.0:
4064             prout(_("Chekov-  \"Security reports the Galileo has reappeared in the dock!\""))
4065             game.iscraft = "onship"
4066         # There used to be code to do the actual reconstrction here,
4067         # but the starchart is now part of the snapshotted galaxy state.
4068         prout(_("Spock has reconstructed a correct star chart from memory"))
4069     else:
4070         # Go forward in time 
4071         game.optime = expran(0.5*game.intime)
4072         prout(_("You are traveling forward in time %d stardates.") % int(game.optime))
4073         # cheat to make sure no tractor beams occur during time warp 
4074         postpone(FTBEAM, game.optime)
4075         game.damage[DRADIO] += game.optime
4076     newqad()
4077     events()    # Stas Sergeev added this -- do pending events 
4078
4079 def probe():
4080     "Launch deep-space probe." 
4081     # New code to launch a deep space probe 
4082     if game.nprobes == 0:
4083         scanner.chew()
4084         skip(1)
4085         if game.ship == 'E': 
4086             prout(_("Engineer Scott- \"We have no more deep space probes, Sir.\""))
4087         else:
4088             prout(_("Ye Faerie Queene has no deep space probes."))
4089         return
4090     if damaged(DDSP):
4091         scanner.chew()
4092         skip(1)
4093         prout(_("Engineer Scott- \"The probe launcher is damaged, Sir.\""))
4094         return
4095     if is_scheduled(FDSPROB):
4096         scanner.chew()
4097         skip(1)
4098         if damaged(DRADIO) and game.condition != "docked":
4099             prout(_("Spock-  \"Records show the previous probe has not yet"))
4100             prout(_("   reached its destination.\""))
4101         else:
4102             prout(_("Uhura- \"The previous probe is still reporting data, Sir.\""))
4103         return
4104     key = scanner.next()
4105     if key == "IHEOL":
4106         if game.nprobes == 1:
4107             prout(_("1 probe left."))
4108         else:
4109             prout(_("%d probes left") % game.nprobes)
4110         proutn(_("Are you sure you want to fire a probe? "))
4111         if ja() == False:
4112             return
4113     game.isarmed = False
4114     if key == "IHALPHA" and scanner.token == "armed":
4115         game.isarmed = True
4116         key = scanner.next()
4117     elif key == "IHEOL":
4118         proutn(_("Arm NOVAMAX warhead? "))
4119         game.isarmed = ja()
4120     elif key == "IHREAL":               # first element of course
4121         scanner.push(scanner.token)
4122     try:
4123         game.probe = getcourse(isprobe=True)
4124     except TrekError:
4125         return
4126     game.nprobes -= 1
4127     schedule(FDSPROB, 0.01) # Time to move one sector
4128     prout(_("Ensign Chekov-  \"The deep space probe is launched, Captain.\""))
4129     game.ididit = True
4130     return
4131
4132 def mayday():
4133     "Yell for help from nearest starbase."
4134     # There's more than one way to move in this game! 
4135     scanner.chew()
4136     # Test for conditions which prevent calling for help 
4137     if game.condition == "docked":
4138         prout(_("Lt. Uhura-  \"But Captain, we're already docked.\""))
4139         return
4140     if damaged(DRADIO):
4141         prout(_("Subspace radio damaged."))
4142         return
4143     if not game.state.baseq:
4144         prout(_("Lt. Uhura-  \"Captain, I'm not getting any response from Starbase.\""))
4145         return
4146     if game.landed:
4147         prout(_("You must be aboard the %s.") % crmshp())
4148         return
4149     # OK -- call for help from nearest starbase 
4150     game.nhelp += 1
4151     if game.base.i!=0:
4152         # There's one in this quadrant 
4153         ddist = (game.base - game.sector).distance()
4154     else:
4155         ddist = FOREVER
4156         for ibq in game.state.baseq:
4157             xdist = QUADSIZE * (ibq - game.quadrant).distance()
4158             if xdist < ddist:
4159                 ddist = xdist
4160         # Since starbase not in quadrant, set up new quadrant 
4161         game.quadrant = ibq
4162         newqad()
4163     # dematerialize starship 
4164     game.quad[game.sector.i][game.sector.j]='.'
4165     proutn(_("Starbase in Quadrant %s responds--%s dematerializes") \
4166            % (game.quadrant, crmshp()))
4167     game.sector.invalidate()
4168     for m in range(1, 5+1):
4169         w = game.base.scatter() 
4170         if w.valid_sector() and game.quad[w.i][w.j]=='.':
4171             # found one -- finish up 
4172             game.sector = w
4173             break
4174     if not game.sector.is_valid():
4175         prout(_("You have been lost in space..."))
4176         finish(FMATERIALIZE)
4177         return
4178     # Give starbase three chances to rematerialize starship 
4179     probf = math.pow((1.0 - math.pow(0.98,ddist)), 0.33333333)
4180     for m in range(1, 3+1):
4181         if m == 1: proutn(_("1st"))
4182         elif m == 2: proutn(_("2nd"))
4183         elif m == 3: proutn(_("3rd"))
4184         proutn(_(" attempt to re-materialize ") + crmshp())
4185         game.quad[ix][iy]=('-','o','O')[m-1]
4186         textcolor(RED)
4187         warble()
4188         if randreal() > probf:
4189             break
4190         prout(_("fails."))
4191         textcolor(DEFAULT)
4192         curses.delay_output(500)
4193     if m > 3:
4194         game.quad[ix][iy]='?'
4195         game.alive = False
4196         drawmaps(1)
4197         setwnd(message_window)
4198         finish(FMATERIALIZE)
4199         return
4200     game.quad[ix][iy]=game.ship
4201     textcolor(GREEN);
4202     prout(_("succeeds."))
4203     textcolor(DEFAULT);
4204     dock(False)
4205     skip(1)
4206     prout(_("Lt. Uhura-  \"Captain, we made it!\""))
4207
4208 def abandon():
4209     "Abandon ship."
4210     scanner.chew()
4211     if game.condition=="docked":
4212         if game.ship!='E':
4213             prout(_("You cannot abandon Ye Faerie Queene."))
4214             return
4215     else:
4216         # Must take shuttle craft to exit 
4217         if game.damage[DSHUTTL]==-1:
4218             prout(_("Ye Faerie Queene has no shuttle craft."))
4219             return
4220         if game.damage[DSHUTTL]<0:
4221             prout(_("Shuttle craft now serving Big Macs."))
4222             return
4223         if game.damage[DSHUTTL]>0:
4224             prout(_("Shuttle craft damaged."))
4225             return
4226         if game.landed:
4227             prout(_("You must be aboard the ship."))
4228             return
4229         if game.iscraft != "onship":
4230             prout(_("Shuttle craft not currently available."))
4231             return
4232         # Emit abandon ship messages 
4233         skip(1)
4234         prouts(_("***ABANDON SHIP!  ABANDON SHIP!"))
4235         skip(1)
4236         prouts(_("***ALL HANDS ABANDON SHIP!"))
4237         skip(2)
4238         prout(_("Captain and crew escape in shuttle craft."))
4239         if not game.state.baseq:
4240             # Oops! no place to go... 
4241             finish(FABANDN)
4242             return
4243         q = game.state.galaxy[game.quadrant.i][game.quadrant.j]
4244         # Dispose of crew 
4245         if not (game.options & OPTION_WORLDS) and not damaged(DTRANSP):
4246             prout(_("Remainder of ship's complement beam down"))
4247             prout(_("to nearest habitable planet."))
4248         elif q.planet != None and not damaged(DTRANSP):
4249             prout(_("Remainder of ship's complement beam down to %s.") %
4250                     q.planet)
4251         else:
4252             prout(_("Entire crew of %d left to die in outer space.") %
4253                     game.state.crew)
4254             game.casual += game.state.crew
4255             game.abandoned += game.state.crew
4256         # If at least one base left, give 'em the Faerie Queene 
4257         skip(1)
4258         game.icrystl = False # crystals are lost 
4259         game.nprobes = 0 # No probes 
4260         prout(_("You are captured by Klingons and released to"))
4261         prout(_("the Federation in a prisoner-of-war exchange."))
4262         nb = randrange(len(game.state.baseq))
4263         # Set up quadrant and position FQ adjacient to base 
4264         if not game.quadrant == game.state.baseq[nb]:
4265             game.quadrant = game.state.baseq[nb]
4266             game.sector.i = game.sector.j = 5
4267             newqad()
4268         while True:
4269             # position next to base by trial and error 
4270             game.quad[game.sector.i][game.sector.j] = '.'
4271             for l in range(QUADSIZE):
4272                 game.sector = game.base.scatter()
4273                 if game.sector.valid_sector() and \
4274                        game.quad[game.sector.i][game.sector.j] == '.':
4275                     break
4276             if l < QUADSIZE+1:
4277                 break # found a spot 
4278             game.sector.i=QUADSIZE/2
4279             game.sector.j=QUADSIZE/2
4280             newqad()
4281     # Get new commission 
4282     game.quad[game.sector.i][game.sector.j] = game.ship = 'F'
4283     game.state.crew = FULLCREW
4284     prout(_("Starfleet puts you in command of another ship,"))
4285     prout(_("the Faerie Queene, which is antiquated but,"))
4286     prout(_("still useable."))
4287     if game.icrystl:
4288         prout(_("The dilithium crystals have been moved."))
4289     game.imine = False
4290     game.iscraft = "offship" # Galileo disappears 
4291     # Resupply ship 
4292     game.condition="docked"
4293     for l in range(NDEVICES): 
4294         game.damage[l] = 0.0
4295     game.damage[DSHUTTL] = -1
4296     game.energy = game.inenrg = 3000.0
4297     game.shield = game.inshld = 1250.0
4298     game.torps = game.intorps = 6
4299     game.lsupres=game.inlsr=3.0
4300     game.shldup=False
4301     game.warpfac=5.0
4302     return
4303
4304 # Code from planets.c begins here.
4305
4306 def consumeTime():
4307     "Abort a lengthy operation if an event interrupts it." 
4308     game.ididit = True
4309     events()
4310     if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova or game.justin: 
4311         return True
4312     return False
4313
4314 def survey():
4315     "Report on (uninhabited) planets in the galaxy."
4316     iknow = False
4317     skip(1)
4318     scanner.chew()
4319     prout(_("Spock-  \"Planet report follows, Captain.\""))
4320     skip(1)
4321     for i in range(game.inplan):
4322         if game.state.planets[i].pclass == "destroyed":
4323             continue
4324         if (game.state.planets[i].known != "unknown" \
4325             and not game.state.planets[i].inhabited) \
4326             or idebug:
4327             iknow = True
4328             if idebug and game.state.planets[i].known=="unknown":
4329                 proutn("(Unknown) ")
4330             proutn(_("Quadrant %s") % game.state.planets[i].quadrant)
4331             proutn(_("   class "))
4332             proutn(game.state.planets[i].pclass)
4333             proutn("   ")
4334             if game.state.planets[i].crystals != present:
4335                 proutn(_("no "))
4336             prout(_("dilithium crystals present."))
4337             if game.state.planets[i].known=="shuttle_down": 
4338                 prout(_("    Shuttle Craft Galileo on surface."))
4339     if not iknow:
4340         prout(_("No information available."))
4341
4342 def orbit():
4343     "Enter standard orbit." 
4344     skip(1)
4345     scanner.chew()
4346     if game.inorbit:
4347         prout(_("Already in standard orbit."))
4348         return
4349     if damaged(DWARPEN) and damaged(DIMPULS):
4350         prout(_("Both warp and impulse engines damaged."))
4351         return
4352     if not game.plnet.is_valid():
4353         prout("There is no planet in this sector.")
4354         return
4355     if abs(game.sector.i-game.plnet.i)>1 or abs(game.sector.j-game.plnet.j)>1:
4356         prout(crmshp() + _(" not adjacent to planet."))
4357         skip(1)
4358         return
4359     game.optime = randreal(0.02, 0.05)
4360     prout(_("Helmsman Sulu-  \"Entering standard orbit, Sir.\""))
4361     newcnd()
4362     if consumeTime():
4363         return
4364     game.height = randreal(1400, 8600)
4365     prout(_("Sulu-  \"Entered orbit at altitude %.2f kilometers.\"") % game.height)
4366     game.inorbit = True
4367     game.ididit = True
4368
4369 def sensor():
4370     "Examine planets in this quadrant."
4371     if damaged(DSRSENS):
4372         if game.options & OPTION_TTY:
4373             prout(_("Short range sensors damaged."))
4374         return
4375     if game.iplnet == None:
4376         if game.options & OPTION_TTY:
4377             prout(_("Spock- \"No planet in this quadrant, Captain.\""))
4378         return
4379     if game.iplnet.known == "unknown":
4380         prout(_("Spock-  \"Sensor scan for Quadrant %s-") % game.quadrant)
4381         skip(1)
4382         prout(_("         Planet at Sector %s is of class %s.") %
4383               (game.plnet, game.iplnet.pclass))
4384         if game.iplnet.known=="shuttle_down": 
4385             prout(_("         Sensors show Galileo still on surface."))
4386         proutn(_("         Readings indicate"))
4387         if game.iplnet.crystals != "present":
4388             proutn(_(" no"))
4389         prout(_(" dilithium crystals present.\""))
4390         if game.iplnet.known == "unknown":
4391             game.iplnet.known = "known"
4392     elif game.iplnet.inhabited:
4393         prout(_("Spock-  \"The inhabited planet %s ") % game.iplnet.name)
4394         prout(_("        is located at Sector %s, Captain.\"") % game.plnet)
4395
4396 def beam():
4397     "Use the transporter."
4398     nrgneed = 0
4399     scanner.chew()
4400     skip(1)
4401     if damaged(DTRANSP):
4402         prout(_("Transporter damaged."))
4403         if not damaged(DSHUTTL) and (game.iplnet.known=="shuttle_down" or game.iscraft == "onship"):
4404             skip(1)
4405             proutn(_("Spock-  \"May I suggest the shuttle craft, Sir?\" "))
4406             if ja() == True:
4407                 shuttle()
4408         return
4409     if not game.inorbit:
4410         prout(crmshp() + _(" not in standard orbit."))
4411         return
4412     if game.shldup:
4413         prout(_("Impossible to transport through shields."))
4414         return
4415     if game.iplnet.known=="unknown":
4416         prout(_("Spock-  \"Captain, we have no information on this planet"))
4417         prout(_("  and Starfleet Regulations clearly state that in this situation"))
4418         prout(_("  you may not go down.\""))
4419         return
4420     if not game.landed and game.iplnet.crystals=="absent":
4421         prout(_("Spock-  \"Captain, I fail to see the logic in"))
4422         prout(_("  exploring a planet with no dilithium crystals."))
4423         proutn(_("  Are you sure this is wise?\" "))
4424         if ja() == False:
4425             scanner.chew()
4426             return
4427     if not (game.options & OPTION_PLAIN):
4428         nrgneed = 50 * game.skill + game.height / 100.0
4429         if nrgneed > game.energy:
4430             prout(_("Engineering to bridge--"))
4431             prout(_("  Captain, we don't have enough energy for transportation."))
4432             return
4433         if not game.landed and nrgneed * 2 > game.energy:
4434             prout(_("Engineering to bridge--"))
4435             prout(_("  Captain, we have enough energy only to transport you down to"))
4436             prout(_("  the planet, but there wouldn't be an energy for the trip back."))
4437             if game.iplnet.known == "shuttle_down":
4438                 prout(_("  Although the Galileo shuttle craft may still be on a surface."))
4439             proutn(_("  Are you sure this is wise?\" "))
4440             if ja() == False:
4441                 scanner.chew()
4442                 return
4443     if game.landed:
4444         # Coming from planet 
4445         if game.iplnet.known=="shuttle_down":
4446             proutn(_("Spock-  \"Wouldn't you rather take the Galileo?\" "))
4447             if ja() == True:
4448                 scanner.chew()
4449                 return
4450             prout(_("Your crew hides the Galileo to prevent capture by aliens."))
4451         prout(_("Landing party assembled, ready to beam up."))
4452         skip(1)
4453         prout(_("Kirk whips out communicator..."))
4454         prouts(_("BEEP  BEEP  BEEP"))
4455         skip(2)
4456         prout(_("\"Kirk to enterprise-  Lock on coordinates...energize.\""))
4457     else:
4458         # Going to planet 
4459         prout(_("Scotty-  \"Transporter room ready, Sir.\""))
4460         skip(1)
4461         prout(_("Kirk and landing party prepare to beam down to planet surface."))
4462         skip(1)
4463         prout(_("Kirk-  \"Energize.\""))
4464     game.ididit = True
4465     skip(1)
4466     prouts("WWHOOOIIIIIRRRRREEEE.E.E.  .  .  .  .   .    .")
4467     skip(2)
4468     if withprob(0.98):
4469         prouts("BOOOIIIOOOIIOOOOIIIOIING . . .")
4470         skip(2)
4471         prout(_("Scotty-  \"Oh my God!  I've lost them.\""))
4472         finish(FLOST)
4473         return
4474     prouts(".    .   .  .  .  .  .E.E.EEEERRRRRIIIIIOOOHWW")
4475     game.landed = not game.landed
4476     game.energy -= nrgneed
4477     skip(2)
4478     prout(_("Transport complete."))
4479     if game.landed and game.iplnet.known=="shuttle_down":
4480         prout(_("The shuttle craft Galileo is here!"))
4481     if not game.landed and game.imine:
4482         game.icrystl = True
4483         game.cryprob = 0.05
4484     game.imine = False
4485     return
4486
4487 def mine():
4488     "Strip-mine a world for dilithium."
4489     skip(1)
4490     scanner.chew()
4491     if not game.landed:
4492         prout(_("Mining party not on planet."))
4493         return
4494     if game.iplnet.crystals == "mined":
4495         prout(_("This planet has already been strip-mined for dilithium."))
4496         return
4497     elif game.iplnet.crystals == "absent":
4498         prout(_("No dilithium crystals on this planet."))
4499         return
4500     if game.imine:
4501         prout(_("You've already mined enough crystals for this trip."))
4502         return
4503     if game.icrystl and game.cryprob == 0.05:
4504         prout(_("With all those fresh crystals aboard the ") + crmshp())
4505         prout(_("there's no reason to mine more at this time."))
4506         return
4507     game.optime = randreal(0.1, 0.3)*(ord(game.iplnet.pclass)-ord("L"))
4508     if consumeTime():
4509         return
4510     prout(_("Mining operation complete."))
4511     game.iplnet.crystals = "mined"
4512     game.imine = game.ididit = True
4513
4514 def usecrystals():
4515     "Use dilithium crystals."
4516     game.ididit = False
4517     skip(1)
4518     scanner.chew()
4519     if not game.icrystl:
4520         prout(_("No dilithium crystals available."))
4521         return
4522     if game.energy >= 1000:
4523         prout(_("Spock-  \"Captain, Starfleet Regulations prohibit such an operation"))
4524         prout(_("  except when Condition Yellow exists."))
4525         return
4526     prout(_("Spock- \"Captain, I must warn you that loading"))
4527     prout(_("  raw dilithium crystals into the ship's power"))
4528     prout(_("  system may risk a severe explosion."))
4529     proutn(_("  Are you sure this is wise?\" "))
4530     if ja() == False:
4531         scanner.chew()
4532         return
4533     skip(1)
4534     prout(_("Engineering Officer Scott-  \"(GULP) Aye Sir."))
4535     prout(_("  Mr. Spock and I will try it.\""))
4536     skip(1)
4537     prout(_("Spock-  \"Crystals in place, Sir."))
4538     prout(_("  Ready to activate circuit.\""))
4539     skip(1)
4540     prouts(_("Scotty-  \"Keep your fingers crossed, Sir!\""))
4541     skip(1)
4542     if withprob(game.cryprob):
4543         prouts(_("  \"Activating now! - - No good!  It's***"))
4544         skip(2)
4545         prouts(_("***RED ALERT!  RED A*L********************************"))
4546         skip(1)
4547         stars()
4548         prouts(_("******************   KA-BOOM!!!!   *******************"))
4549         skip(1)
4550         kaboom()
4551         return
4552     game.energy += randreal(5000.0, 5500.0)
4553     prouts(_("  \"Activating now! - - "))
4554     prout(_("The instruments"))
4555     prout(_("   are going crazy, but I think it's"))
4556     prout(_("   going to work!!  Congratulations, Sir!\""))
4557     game.cryprob *= 2.0
4558     game.ididit = True
4559
4560 def shuttle():
4561     "Use shuttlecraft for planetary jaunt."
4562     scanner.chew()
4563     skip(1)
4564     if damaged(DSHUTTL):
4565         if game.damage[DSHUTTL] == -1.0:
4566             if game.inorbit and game.iplnet.known == "shuttle_down":
4567                 prout(_("Ye Faerie Queene has no shuttle craft bay to dock it at."))
4568             else:
4569                 prout(_("Ye Faerie Queene had no shuttle craft."))
4570         elif game.damage[DSHUTTL] > 0:
4571             prout(_("The Galileo is damaged."))
4572         else: # game.damage[DSHUTTL] < 0  
4573             prout(_("Shuttle craft is now serving Big Macs."))
4574         return
4575     if not game.inorbit:
4576         prout(crmshp() + _(" not in standard orbit."))
4577         return
4578     if (game.iplnet.known != "shuttle_down") and game.iscraft != "onship":
4579         prout(_("Shuttle craft not currently available."))
4580         return
4581     if not game.landed and game.iplnet.known=="shuttle_down":
4582         prout(_("You will have to beam down to retrieve the shuttle craft."))
4583         return
4584     if game.shldup or game.condition == "docked":
4585         prout(_("Shuttle craft cannot pass through shields."))
4586         return
4587     if game.iplnet.known=="unknown":
4588         prout(_("Spock-  \"Captain, we have no information on this planet"))
4589         prout(_("  and Starfleet Regulations clearly state that in this situation"))
4590         prout(_("  you may not fly down.\""))
4591         return
4592     game.optime = 3.0e-5*game.height
4593     if game.optime >= 0.8*game.state.remtime:
4594         prout(_("First Officer Spock-  \"Captain, I compute that such"))
4595         proutn(_("  a maneuver would require approximately %2d%% of our") % \
4596                int(100*game.optime/game.state.remtime))
4597         prout(_("remaining time."))
4598         proutn(_("Are you sure this is wise?\" "))
4599         if ja() == False:
4600             game.optime = 0.0
4601             return
4602     if game.landed:
4603         # Kirk on planet 
4604         if game.iscraft == "onship":
4605             # Galileo on ship! 
4606             if not damaged(DTRANSP):
4607                 proutn(_("Spock-  \"Would you rather use the transporter?\" "))
4608                 if ja() == True:
4609                     beam()
4610                     return
4611                 proutn(_("Shuttle crew"))
4612             else:
4613                 proutn(_("Rescue party"))
4614             prout(_(" boards Galileo and swoops toward planet surface."))
4615             game.iscraft = "offship"
4616             skip(1)
4617             if consumeTime():
4618                 return
4619             game.iplnet.known="shuttle_down"
4620             prout(_("Trip complete."))
4621             return
4622         else:
4623             # Ready to go back to ship 
4624             prout(_("You and your mining party board the"))
4625             prout(_("shuttle craft for the trip back to the Enterprise."))
4626             skip(1)
4627             prouts(_("The short hop begins . . ."))
4628             skip(1)
4629             game.iplnet.known="known"
4630             game.icraft = True
4631             skip(1)
4632             game.landed = False
4633             if consumeTime():
4634                 return
4635             game.iscraft = "onship"
4636             game.icraft = False
4637             if game.imine:
4638                 game.icrystl = True
4639                 game.cryprob = 0.05
4640             game.imine = False
4641             prout(_("Trip complete."))
4642             return
4643     else:
4644         # Kirk on ship and so is Galileo 
4645         prout(_("Mining party assembles in the hangar deck,"))
4646         prout(_("ready to board the shuttle craft \"Galileo\"."))
4647         skip(1)
4648         prouts(_("The hangar doors open; the trip begins."))
4649         skip(1)
4650         game.icraft = True
4651         game.iscraft = "offship"
4652         if consumeTime():
4653             return
4654         game.iplnet.known = "shuttle_down"
4655         game.landed = True
4656         game.icraft = False
4657         prout(_("Trip complete."))
4658         return
4659
4660 def deathray():
4661     "Use the big zapper."
4662     game.ididit = False
4663     skip(1)
4664     scanner.chew()
4665     if game.ship != 'E':
4666         prout(_("Ye Faerie Queene has no death ray."))
4667         return
4668     if len(game.enemies)==0:
4669         prout(_("Sulu-  \"But Sir, there are no enemies in this quadrant.\""))
4670         return
4671     if damaged(DDRAY):
4672         prout(_("Death Ray is damaged."))
4673         return
4674     prout(_("Spock-  \"Captain, the 'Experimental Death Ray'"))
4675     prout(_("  is highly unpredictible.  Considering the alternatives,"))
4676     proutn(_("  are you sure this is wise?\" "))
4677     if ja() == False:
4678         return
4679     prout(_("Spock-  \"Acknowledged.\""))
4680     skip(1)
4681     game.ididit = True
4682     prouts(_("WHOOEE ... WHOOEE ... WHOOEE ... WHOOEE"))
4683     skip(1)
4684     prout(_("Crew scrambles in emergency preparation."))
4685     prout(_("Spock and Scotty ready the death ray and"))
4686     prout(_("prepare to channel all ship's power to the device."))
4687     skip(1)
4688     prout(_("Spock-  \"Preparations complete, sir.\""))
4689     prout(_("Kirk-  \"Engage!\""))
4690     skip(1)
4691     prouts(_("WHIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"))
4692     skip(1)
4693     dprob = 0.30
4694     if game.options & OPTION_PLAIN:
4695         dprob = 0.5
4696     r = randreal()
4697     if r > dprob:
4698         prouts(_("Sulu- \"Captain!  It's working!\""))
4699         skip(2)
4700         while len(game.enemies) > 0:
4701             deadkl(game.enemies[1].location, game.quad[game.enemies[1].location.i][game.enemies[1].location.j],game.enemies[1].location)
4702         prout(_("Ensign Chekov-  \"Congratulations, Captain!\""))
4703         if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) == 0:
4704             finish(FWON)    
4705         if (game.options & OPTION_PLAIN) == 0:
4706             prout(_("Spock-  \"Captain, I believe the `Experimental Death Ray'"))
4707             if withprob(0.05):
4708                 prout(_("   is still operational.\""))
4709             else:
4710                 prout(_("   has been rendered nonfunctional.\""))
4711                 game.damage[DDRAY] = 39.95
4712         return
4713     r = randreal()      # Pick failure method 
4714     if r <= 0.30:
4715         prouts(_("Sulu- \"Captain!  It's working!\""))
4716         skip(1)
4717         prouts(_("***RED ALERT!  RED ALERT!"))
4718         skip(1)
4719         prout(_("***MATTER-ANTIMATTER IMPLOSION IMMINENT!"))
4720         skip(1)
4721         prouts(_("***RED ALERT!  RED A*L********************************"))
4722         skip(1)
4723         stars()
4724         prouts(_("******************   KA-BOOM!!!!   *******************"))
4725         skip(1)
4726         kaboom()
4727         return
4728     if r <= 0.55:
4729         prouts(_("Sulu- \"Captain!  Yagabandaghangrapl, brachriigringlanbla!\""))
4730         skip(1)
4731         prout(_("Lt. Uhura-  \"Graaeek!  Graaeek!\""))
4732         skip(1)
4733         prout(_("Spock-  \"Fascinating!  . . . All humans aboard"))
4734         prout(_("  have apparently been transformed into strange mutations."))
4735         prout(_("  Vulcans do not seem to be affected."))
4736         skip(1)
4737         prout(_("Kirk-  \"Raauch!  Raauch!\""))
4738         finish(FDRAY)
4739         return
4740     if r <= 0.75:
4741         intj
4742         prouts(_("Sulu- \"Captain!  It's   --WHAT?!?!\""))
4743         skip(2)
4744         proutn(_("Spock-  \"I believe the word is"))
4745         prouts(_(" *ASTONISHING*"))
4746         prout(_(" Mr. Sulu."))
4747         for i in range(QUADSIZE):
4748             for j in range(QUADSIZE):
4749                 if game.quad[i][j] == '.':
4750                     game.quad[i][j] = '?'
4751         prout(_("  Captain, our quadrant is now infested with"))
4752         prouts(_(" - - - - - -  *THINGS*."))
4753         skip(1)
4754         prout(_("  I have no logical explanation.\""))
4755         return
4756     prouts(_("Sulu- \"Captain!  The Death Ray is creating tribbles!\""))
4757     skip(1)
4758     prout(_("Scotty-  \"There are so many tribbles down here"))
4759     prout(_("  in Engineering, we can't move for 'em, Captain.\""))
4760     finish(FTRIBBLE)
4761     return
4762
4763 # Code from reports.c begins here
4764
4765 def attackreport(curt):
4766     "eport status of bases under attack."
4767     if not curt:
4768         if is_scheduled(FCDBAS):
4769             prout(_("Starbase in Quadrant %s is currently under Commander attack.") % game.battle)
4770             prout(_("It can hold out until Stardate %d.") % int(scheduled(FCDBAS)))
4771         elif game.isatb == 1:
4772             prout(_("Starbase in Quadrant %s is under Super-commander attack.") % game.state.kscmdr)
4773             prout(_("It can hold out until Stardate %d.") % int(scheduled(FSCDBAS)))
4774         else:
4775             prout(_("No Starbase is currently under attack."))
4776     else:
4777         if is_scheduled(FCDBAS):
4778             proutn(_("Base in %s attacked by C. Alive until %.1f") % (game.battle, scheduled(FCDBAS)))
4779         if game.isatb:
4780             proutn(_("Base in %s attacked by S. Alive until %.1f") % (game.state.kscmdr, scheduled(FSCDBAS)))
4781         clreol()
4782
4783 def report():
4784     # report on general game status 
4785     scanner.chew()
4786     s1 = "" and game.thawed and _("thawed ")
4787     s2 = {1:"short", 2:"medium", 4:"long"}[game.length]
4788     s3 = (None, _("novice"). _("fair"),
4789           _("good"), _("expert"), _("emeritus"))[game.skill]
4790     prout(_("You %s a %s%s %s game.") % ((_("were playing"), _("are playing"))[game.alldone], s1, s2, s3))
4791     if game.skill>SKILL_GOOD and game.thawed and not game.alldone:
4792         prout(_("No plaque is allowed."))
4793     if game.tourn:
4794         prout(_("This is tournament game %d.") % game.tourn)
4795     prout(_("Your secret password is \"%s\"") % game.passwd)
4796     proutn(_("%d of %d Klingons have been killed") % (((game.inkling + game.incom + game.inscom) - (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)), 
4797            (game.inkling + game.incom + game.inscom)))
4798     if game.incom - len(game.state.kcmdr):
4799         prout(_(", including %d Commander%s.") % (game.incom - len(game.state.kcmdr), (_("s"), "")[(game.incom - len(game.state.kcmdr))==1]))
4800     elif game.inkling - game.state.remkl + (game.inscom - game.state.nscrem) > 0:
4801         prout(_(", but no Commanders."))
4802     else:
4803         prout(".")
4804     if game.skill > SKILL_FAIR:
4805         prout(_("The Super Commander has %sbeen destroyed.") % ("", _("not "))[game.state.nscrem])
4806     if len(game.state.baseq) != game.inbase:
4807         proutn(_("There "))
4808         if game.inbase-len(game.state.baseq)==1:
4809             proutn(_("has been 1 base"))
4810         else:
4811             proutn(_("have been %d bases") % (game.inbase-len(game.state.baseq)))
4812         prout(_(" destroyed, %d remaining.") % len(game.state.baseq))
4813     else:
4814         prout(_("There are %d bases.") % game.inbase)
4815     if communicating() or game.iseenit:
4816         # Don't report this if not seen and
4817         # either the radio is dead or not at base!
4818         attackreport(False)
4819         game.iseenit = True
4820     if game.casual: 
4821         prout(_("%d casualt%s suffered so far.") % (game.casual, ("y", "ies")[game.casual!=1]))
4822     if game.nhelp:
4823         prout(_("There were %d call%s for help.") % (game.nhelp,  ("" , _("s"))[game.nhelp!=1]))
4824     if game.ship == 'E':
4825         proutn(_("You have "))
4826         if game.nprobes:
4827             proutn("%d" % (game.nprobes))
4828         else:
4829             proutn(_("no"))
4830         proutn(_(" deep space probe"))
4831         if game.nprobes!=1:
4832             proutn(_("s"))
4833         prout(".")
4834     if communicating() and is_scheduled(FDSPROB):
4835         if game.isarmed: 
4836             proutn(_("An armed deep space probe is in "))
4837         else:
4838             proutn(_("A deep space probe is in "))
4839         prout("Quadrant %s." % game.probec)
4840     if game.icrystl:
4841         if game.cryprob <= .05:
4842             prout(_("Dilithium crystals aboard ship... not yet used."))
4843         else:
4844             i=0
4845             ai = 0.05
4846             while game.cryprob > ai:
4847                 ai *= 2.0
4848                 i += 1
4849             prout(_("Dilithium crystals have been used %d time%s.") % \
4850                   (i, (_("s"), "")[i==1]))
4851     skip(1)
4852         
4853 def lrscan(silent):
4854     "Long-range sensor scan."
4855     if damaged(DLRSENS):
4856         # Now allow base's sensors if docked 
4857         if game.condition != "docked":
4858             if not silent:
4859                 prout(_("LONG-RANGE SENSORS DAMAGED."))
4860             return
4861         if not silent:
4862             prout(_("Starbase's long-range scan"))
4863     elif not silent:
4864         prout(_("Long-range scan"))
4865     for x in range(game.quadrant.i-1, game.quadrant.i+2):
4866         if not silent:
4867             proutn(" ")
4868         for y in range(game.quadrant.j-1, game.quadrant.j+2):
4869             if not coord(x, y).valid_quadrant():
4870                 if not silent:
4871                     proutn("  -1")
4872             else:
4873                 if not damaged(DRADIO):
4874                     game.state.galaxy[x][y].charted = True
4875                 game.state.chart[x][y].klingons = game.state.galaxy[x][y].klingons
4876                 game.state.chart[x][y].starbase = game.state.galaxy[x][y].starbase
4877                 game.state.chart[x][y].stars = game.state.galaxy[x][y].stars
4878                 if not silent and game.state.galaxy[x][y].supernova: 
4879                     proutn(" ***")
4880                 elif not silent:
4881                     proutn(" %3d" % (game.state.chart[x][y].klingons*100 + game.state.chart[x][y].starbase * 10 + game.state.chart[x][y].stars))
4882         prout(" ")
4883
4884 def damagereport():
4885     "Damage report."
4886     jdam = False
4887     scanner.chew()
4888     for i in range(NDEVICES):
4889         if damaged(i):
4890             if not jdam:
4891                 prout(_("\tDEVICE\t\t\t-REPAIR TIMES-"))
4892                 prout(_("\t\t\tIN FLIGHT\t\tDOCKED"))
4893                 jdam = True
4894             prout("  %-26s\t%8.2f\t\t%8.2f" % (device[i],
4895                                                game.damage[i]+0.05,
4896                                                DOCKFAC*game.damage[i]+0.005))
4897     if not jdam:
4898         prout(_("All devices functional."))
4899
4900 def rechart():
4901     "Update the chart in the Enterprise's computer from galaxy data."
4902     game.lastchart = game.state.date
4903     for i in range(GALSIZE):
4904         for j in range(GALSIZE):
4905             if game.state.galaxy[i][j].charted:
4906                 game.state.chart[i][j].klingons = game.state.galaxy[i][j].klingons
4907                 game.state.chart[i][j].starbase = game.state.galaxy[i][j].starbase
4908                 game.state.chart[i][j].stars = game.state.galaxy[i][j].stars
4909
4910 def chart():
4911     "Display the star chart."
4912     scanner.chew()
4913     if (game.options & OPTION_AUTOSCAN):
4914         lrscan(silent=True)
4915     if not damaged(DRADIO):
4916         rechart()
4917     if game.lastchart < game.state.date and game.condition == "docked":
4918         prout(_("Spock-  \"I revised the Star Chart from the starbase's records.\""))
4919         rechart()
4920     prout(_("       STAR CHART FOR THE KNOWN GALAXY"))
4921     if game.state.date > game.lastchart:
4922         prout(_("(Last surveillance update %d stardates ago).") % ((int)(game.state.date-game.lastchart)))
4923     prout("      1    2    3    4    5    6    7    8")
4924     for i in range(GALSIZE):
4925         proutn("%d |" % (i+1))
4926         for j in range(GALSIZE):
4927             if (game.options & OPTION_SHOWME) and i == game.quadrant.i and j == game.quadrant.j:
4928                 proutn("<")
4929             else:
4930                 proutn(" ")
4931             if game.state.galaxy[i][j].supernova:
4932                 show = "***"
4933             elif not game.state.galaxy[i][j].charted and game.state.galaxy[i][j].starbase:
4934                 show = ".1."
4935             elif game.state.galaxy[i][j].charted:
4936                 show = "%3d" % (game.state.chart[i][j].klingons*100 + game.state.chart[i][j].starbase * 10 + game.state.chart[i][j].stars)
4937             else:
4938                 show = "..."
4939             proutn(show)
4940             if (game.options & OPTION_SHOWME) and i == game.quadrant.i and j == game.quadrant.j:
4941                 proutn(">")
4942             else:
4943                 proutn(" ")
4944         proutn("  |")
4945         if i<GALSIZE:
4946             skip(1)
4947
4948 def sectscan(goodScan, i, j):
4949     "Light up an individual dot in a sector."
4950     if goodScan or (abs(i-game.sector.i)<= 1 and abs(j-game.sector.j) <= 1):
4951         textcolor({"green":GREEN,
4952                    "yellow":YELLOW,
4953                    "red":RED,
4954                    "docked":CYAN,
4955                    "dead":BROWN}[game.condition]) 
4956         if game.quad[i][j] != game.ship: 
4957             highvideo();
4958         proutn("%c " % game.quad[i][j])
4959         textcolor(DEFAULT)
4960     else:
4961         proutn("- ")
4962
4963 def status(req=0):
4964     "Emit status report lines"
4965     if not req or req == 1:
4966         prstat(_("Stardate"), _("%.1f, Time Left %.2f") \
4967                % (game.state.date, game.state.remtime))
4968     if not req or req == 2:
4969         if game.condition != "docked":
4970             newcnd()
4971         prstat(_("Condition"), _("%s, %i DAMAGES") % \
4972                (game.condition.upper(), sum(map(lambda x: x > 0, game.damage))))
4973     if not req or req == 3:
4974         prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
4975     if not req or req == 4:
4976         if damaged(DLIFSUP):
4977             if game.condition == "docked":
4978                 s = _("DAMAGED, Base provides")
4979             else:
4980                 s = _("DAMAGED, reserves=%4.2f") % game.lsupres
4981         else:
4982             s = _("ACTIVE")
4983         prstat(_("Life Support"), s)
4984     if not req or req == 5:
4985         prstat(_("Warp Factor"), "%.1f" % game.warpfac)
4986     if not req or req == 6:
4987         extra = ""
4988         if game.icrystl and (game.options & OPTION_SHOWME):
4989             extra = _(" (have crystals)")
4990         prstat(_("Energy"), "%.2f%s" % (game.energy, extra))
4991     if not req or req == 7:
4992         prstat(_("Torpedoes"), "%d" % (game.torps))
4993     if not req or req == 8:
4994         if damaged(DSHIELD):
4995             s = _("DAMAGED,")
4996         elif game.shldup:
4997             s = _("UP,")
4998         else:
4999             s = _("DOWN,")
5000         data = _(" %d%% %.1f units") \
5001                % (int((100.0*game.shield)/game.inshld + 0.5), game.shield)
5002         prstat(_("Shields"), s+data)
5003     if not req or req == 9:
5004         prstat(_("Klingons Left"), "%d" \
5005                % (game.state.remkl+len(game.state.kcmdr)+game.state.nscrem))
5006     if not req or req == 10:
5007         if game.options & OPTION_WORLDS:
5008             plnet = game.state.galaxy[game.quadrant.i][game.quadrant.j].planet
5009             if plnet and plnet.inhabited:
5010                 prstat(_("Major system"), plnet.name)
5011             else:
5012                 prout(_("Sector is uninhabited"))
5013     elif not req or req == 11:
5014         attackreport(not req)
5015
5016 def request():
5017     "Request specified status data, a historical relic from slow TTYs."
5018     requests = ("da","co","po","ls","wa","en","to","sh","kl","sy", "ti")
5019     while scanner.next() == "IHEOL":
5020         proutn(_("Information desired? "))
5021     scanner.chew()
5022     if scanner.token in requests:
5023         status(requests.index(scanner.token))
5024     else:
5025         prout(_("UNRECOGNIZED REQUEST. Legal requests are:"))
5026         prout(("  date, condition, position, lsupport, warpfactor,"))
5027         prout(("  energy, torpedoes, shields, klingons, system, time."))
5028                 
5029 def srscan():
5030     "Short-range scan." 
5031     goodScan=True
5032     if damaged(DSRSENS):
5033         # Allow base's sensors if docked 
5034         if game.condition != "docked":
5035             prout(_("   S.R. SENSORS DAMAGED!"))
5036             goodScan=False
5037         else:
5038             prout(_("  [Using Base's sensors]"))
5039     else:
5040         prout(_("     Short-range scan"))
5041     if goodScan and not damaged(DRADIO): 
5042         game.state.chart[game.quadrant.i][game.quadrant.j].klingons = game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons
5043         game.state.chart[game.quadrant.i][game.quadrant.j].starbase = game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase
5044         game.state.chart[game.quadrant.i][game.quadrant.j].stars = game.state.galaxy[game.quadrant.i][game.quadrant.j].stars
5045         game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
5046     prout("    1 2 3 4 5 6 7 8 9 10")
5047     if game.condition != "docked":
5048         newcnd()
5049     for i in range(QUADSIZE):
5050         proutn("%2d  " % (i+1))
5051         for j in range(QUADSIZE):
5052             sectscan(goodScan, i, j)
5053         skip(1)
5054                 
5055 def eta():
5056     "Use computer to get estimated time of arrival for a warp jump."
5057     w1 = coord(); w2 = coord()
5058     prompt = False
5059     if damaged(DCOMPTR):
5060         prout(_("COMPUTER DAMAGED, USE A POCKET CALCULATOR."))
5061         skip(1)
5062         return
5063     if scanner.next() != "IHREAL":
5064         prompt = True
5065         scanner.chew()
5066         proutn(_("Destination quadrant and/or sector? "))
5067         if scanner.next()!="IHREAL":
5068             huh()
5069             return
5070     w1.j = int(scanner.real-0.5)
5071     if scanner.next() != "IHREAL":
5072         huh()
5073         return
5074     w1.i = int(scanner.real-0.5)
5075     if scanner.next() == "IHREAL":
5076         w2.j = int(scanner.real-0.5)
5077         if scanner.next() != "IHREAL":
5078             huh()
5079             return
5080         w2.i = int(scanner.real-0.5)
5081     else:
5082         if game.quadrant.j>w1.i:
5083             w2.i = 0
5084         else:
5085             w2.i=QUADSIZE-1
5086         if game.quadrant.i>w1.j:
5087             w2.j = 0
5088         else:
5089             w2.j=QUADSIZE-1
5090     if not w1.valid_quadrant() or not w2.valid_sector():
5091         huh()
5092         return
5093     dist = math.sqrt((w1.j-game.quadrant.j+(w2.j-game.sector.j)/(QUADSIZE*1.0))**2+
5094                 (w1.i-game.quadrant.i+(w2.i-game.sector.i)/(QUADSIZE*1.0))**2)
5095     wfl = False
5096     if prompt:
5097         prout(_("Answer \"no\" if you don't know the value:"))
5098     while True:
5099         scanner.chew()
5100         proutn(_("Time or arrival date? "))
5101         if scanner.next()=="IHREAL":
5102             ttime = scanner.real
5103             if ttime > game.state.date:
5104                 ttime -= game.state.date # Actually a star date
5105             twarp=(math.floor(math.sqrt((10.0*dist)/ttime)*10.0)+1.0)/10.0
5106             if ttime <= 1e-10 or twarp > 10:
5107                 prout(_("We'll never make it, sir."))
5108                 scanner.chew()
5109                 return
5110             if twarp < 1.0:
5111                 twarp = 1.0
5112             break
5113         scanner.chew()
5114         proutn(_("Warp factor? "))
5115         if scanner.next()== "IHREAL":
5116             wfl = True
5117             twarp = scanner.real
5118             if twarp<1.0 or twarp > 10.0:
5119                 huh()
5120                 return
5121             break
5122         prout(_("Captain, certainly you can give me one of these."))
5123     while True:
5124         scanner.chew()
5125         ttime = (10.0*dist)/twarp**2
5126         tpower = dist*twarp*twarp*twarp*(game.shldup+1)
5127         if tpower >= game.energy:
5128             prout(_("Insufficient energy, sir."))
5129             if not game.shldup or tpower > game.energy*2.0:
5130                 if not wfl:
5131                     return
5132                 proutn(_("New warp factor to try? "))
5133                 if scanner.next() == "IHREAL":
5134                     wfl = True
5135                     twarp = scanner.real
5136                     if twarp<1.0 or twarp > 10.0:
5137                         huh()
5138                         return
5139                     continue
5140                 else:
5141                     scanner.chew()
5142                     skip(1)
5143                     return
5144             prout(_("But if you lower your shields,"))
5145             proutn(_("remaining"))
5146             tpower /= 2
5147         else:
5148             proutn(_("Remaining"))
5149         prout(_(" energy will be %.2f.") % (game.energy-tpower))
5150         if wfl:
5151             prout(_("And we will arrive at stardate %.2f.") % (game.state.date+ttime))
5152         elif twarp==1.0:
5153             prout(_("Any warp speed is adequate."))
5154         else:
5155             prout(_("Minimum warp needed is %.2f,") % (twarp))
5156             prout(_("and we will arrive at stardate %.2f.") % (game.state.date+ttime))
5157         if game.state.remtime < ttime:
5158             prout(_("Unfortunately, the Federation will be destroyed by then."))
5159         if twarp > 6.0:
5160             prout(_("You'll be taking risks at that speed, Captain"))
5161         if (game.isatb==1 and game.state.kscmdr == w1 and \
5162              scheduled(FSCDBAS)< ttime+game.state.date) or \
5163             (scheduled(FCDBAS)<ttime+game.state.date and game.battle == w1):
5164             prout(_("The starbase there will be destroyed by then."))
5165         proutn(_("New warp factor to try? "))
5166         if scanner.next() == "IHREAL":
5167             wfl = True
5168             twarp = scanner.real
5169             if twarp<1.0 or twarp > 10.0:
5170                 huh()
5171                 return
5172         else:
5173             scanner.chew()
5174             skip(1)
5175             return
5176
5177 # Code from setup.c begins here
5178
5179 def prelim():
5180     "Issue a historically correct banner."
5181     skip(2)
5182     prout(_("-SUPER- STAR TREK"))
5183     skip(1)
5184 # From the FORTRAN original
5185 #    prout(_("Latest update-21 Sept 78"))
5186 #    skip(1)
5187
5188 def freeze(boss):
5189     "Save game."
5190     if boss:
5191         scanner.push("emsave.trk")
5192     key = scanner.next()
5193     if key == "IHEOL":
5194         proutn(_("File name: "))
5195         key = scanner.next()
5196     if key != "IHALPHA":
5197         huh()
5198         return
5199     scanner.chew()
5200     if '.' not in scanner.token:
5201         scanner.token += ".trk"
5202     try:
5203         fp = open(scanner.token, "wb")
5204     except IOError:
5205         prout(_("Can't freeze game as file %s") % scanner.token)
5206         return
5207     cPickle.dump(game, fp)
5208     fp.close()
5209
5210 def thaw():
5211     "Retrieve saved game." 
5212     game.passwd[0] = '\0'
5213     key = scanner.next()
5214     if key == "IHEOL":
5215         proutn(_("File name: "))
5216         key = scanner.next()
5217     if key != "IHALPHA":
5218         huh()
5219         return True
5220     scanner.chew()
5221     if '.' not in scanner.token:
5222         scanner.token += ".trk"
5223     try:
5224         fp = open(scanner.token, "rb")
5225     except IOError:
5226         prout(_("Can't thaw game in %s") % scanner.token)
5227         return
5228     game = cPickle.load(fp)
5229     fp.close()
5230     return False
5231
5232 # I used <http://www.memory-alpha.org> to find planets
5233 # with references in ST:TOS.  Eath and the Alpha Centauri
5234 # Colony have been omitted.
5235
5236 # Some planets marked Class G and P here will be displayed as class M
5237 # because of the way planets are generated. This is a known bug.
5238 systnames = (
5239     # Federation Worlds 
5240     _("Andoria (Fesoan)"),      # several episodes 
5241     _("Tellar Prime (Miracht)"),        # TOS: "Journey to Babel" 
5242     _("Vulcan (T'Khasi)"),      # many episodes 
5243     _("Medusa"),                # TOS: "Is There in Truth No Beauty?" 
5244     _("Argelius II (Nelphia)"), # TOS: "Wolf in the Fold" ("IV" in BSD) 
5245     _("Ardana"),                # TOS: "The Cloud Minders" 
5246     _("Catulla (Cendo-Prae)"),  # TOS: "The Way to Eden" 
5247     _("Gideon"),                # TOS: "The Mark of Gideon" 
5248     _("Aldebaran III"),         # TOS: "The Deadly Years" 
5249     _("Alpha Majoris I"),       # TOS: "Wolf in the Fold" 
5250     _("Altair IV"),             # TOS: "Amok Time 
5251     _("Ariannus"),              # TOS: "Let That Be Your Last Battlefield" 
5252     _("Benecia"),               # TOS: "The Conscience of the King" 
5253     _("Beta Niobe I (Sarpeidon)"),      # TOS: "All Our Yesterdays" 
5254     _("Alpha Carinae II"),      # TOS: "The Ultimate Computer" 
5255     _("Capella IV (Kohath)"),   # TOS: "Friday's Child" (Class G) 
5256     _("Daran V"),               # TOS: "For the World is Hollow and I Have Touched the Sky" 
5257     _("Deneb II"),              # TOS: "Wolf in the Fold" ("IV" in BSD) 
5258     _("Eminiar VII"),           # TOS: "A Taste of Armageddon" 
5259     _("Gamma Canaris IV"),      # TOS: "Metamorphosis" 
5260     _("Gamma Tranguli VI (Vaalel)"),    # TOS: "The Apple" 
5261     _("Ingraham B"),            # TOS: "Operation: Annihilate" 
5262     _("Janus IV"),              # TOS: "The Devil in the Dark" 
5263     _("Makus III"),             # TOS: "The Galileo Seven" 
5264     _("Marcos XII"),            # TOS: "And the Children Shall Lead", 
5265     _("Omega IV"),              # TOS: "The Omega Glory" 
5266     _("Regulus V"),             # TOS: "Amok Time 
5267     _("Deneva"),                # TOS: "Operation -- Annihilate!" 
5268     # Worlds from BSD Trek 
5269     _("Rigel II"),              # TOS: "Shore Leave" ("III" in BSD) 
5270     _("Beta III"),              # TOS: "The Return of the Archons" 
5271     _("Triacus"),               # TOS: "And the Children Shall Lead", 
5272     _("Exo III"),               # TOS: "What Are Little Girls Made Of?" (Class P) 
5273 #       # Others 
5274 #    _("Hansen's Planet"),      # TOS: "The Galileo Seven" 
5275 #    _("Taurus IV"),            # TOS: "The Galileo Seven" (class G) 
5276 #    _("Antos IV (Doraphane)"), # TOS: "Whom Gods Destroy", "Who Mourns for Adonais?" 
5277 #    _("Izar"),                 # TOS: "Whom Gods Destroy" 
5278 #    _("Tiburon"),              # TOS: "The Way to Eden" 
5279 #    _("Merak II"),             # TOS: "The Cloud Minders" 
5280 #    _("Coridan (Desotriana)"), # TOS: "Journey to Babel" 
5281 #    _("Iotia"),                # TOS: "A Piece of the Action" 
5282 )
5283
5284 device = (
5285         _("S. R. Sensors"), \
5286         _("L. R. Sensors"), \
5287         _("Phasers"), \
5288         _("Photon Tubes"), \
5289         _("Life Support"), \
5290         _("Warp Engines"), \
5291         _("Impulse Engines"), \
5292         _("Shields"), \
5293         _("Subspace Radio"), \
5294         _("Shuttle Craft"), \
5295         _("Computer"), \
5296         _("Navigation System"), \
5297         _("Transporter"), \
5298         _("Shield Control"), \
5299         _("Death Ray"), \
5300         _("D. S. Probe"), \
5301 )
5302
5303 def setup():
5304     "Prepare to play, set up cosmos."
5305     w = coord()
5306     #  Decide how many of everything
5307     if choose():
5308         return # frozen game
5309     # Prepare the Enterprise
5310     game.alldone = game.gamewon = game.shldchg = game.shldup = False
5311     game.ship = 'E'
5312     game.state.crew = FULLCREW
5313     game.energy = game.inenrg = 5000.0
5314     game.shield = game.inshld = 2500.0
5315     game.inlsr = 4.0
5316     game.lsupres = 4.0
5317     game.quadrant = randplace(GALSIZE)
5318     game.sector = randplace(QUADSIZE)
5319     game.torps = game.intorps = 10
5320     game.nprobes = randrange(2, 5)
5321     game.warpfac = 5.0
5322     for i in range(NDEVICES): 
5323         game.damage[i] = 0.0
5324     # Set up assorted game parameters
5325     game.battle = coord()
5326     game.state.date = game.indate = 100.0 * randreal(20, 51)
5327     game.nkinks = game.nhelp = game.casual = game.abandoned = 0
5328     game.iscate = game.resting = game.imine = game.icrystl = game.icraft = False
5329     game.isatb = game.state.nplankl = 0
5330     game.state.starkl = game.state.basekl = 0
5331     game.iscraft = "onship"
5332     game.landed = False
5333     game.alive = True
5334     # Starchart is functional but we've never seen it
5335     game.lastchart = FOREVER
5336     # Put stars in the galaxy
5337     game.instar = 0
5338     for i in range(GALSIZE):
5339         for j in range(GALSIZE):
5340             k = randrange(1, QUADSIZE**2/10+1)
5341             game.instar += k
5342             game.state.galaxy[i][j].stars = k
5343     # Locate star bases in galaxy
5344     for i in range(game.inbase):
5345         while True:
5346             while True:
5347                 w = randplace(GALSIZE)
5348                 if not game.state.galaxy[w.i][w.j].starbase:
5349                     break
5350             contflag = False
5351             # C version: for (j = i-1; j > 0; j--)
5352             # so it did them in the opposite order.
5353             for j in range(1, i):
5354                 # Improved placement algorithm to spread out bases
5355                 distq = (w - game.state.baseq[j]).distance()
5356                 if distq < 6.0*(BASEMAX+1-game.inbase) and withprob(0.75):
5357                     contflag = True
5358                     if idebug:
5359                         prout("=== Abandoning base #%d at %s" % (i, w))
5360                     break
5361                 elif distq < 6.0 * (BASEMAX+1-game.inbase):
5362                     if idebug:
5363                         prout("=== Saving base #%d, close to #%d" % (i, j))
5364             if not contflag:
5365                 break
5366         game.state.baseq.append(w)
5367         game.state.galaxy[w.i][w.j].starbase = game.state.chart[w.i][w.j].starbase = True
5368     # Position ordinary Klingon Battle Cruisers
5369     krem = game.inkling
5370     klumper = 0.25*game.skill*(9.0-game.length)+1.0
5371     if klumper > MAXKLQUAD: 
5372         klumper = MAXKLQUAD
5373     while True:
5374         r = randreal()
5375         klump = (1.0 - r*r)*klumper
5376         if klump > krem:
5377             klump = krem
5378         krem -= klump
5379         while True:
5380             w = randplace(GALSIZE)
5381             if not game.state.galaxy[w.i][w.j].supernova and \
5382                game.state.galaxy[w.i][w.j].klingons + klump <= MAXKLQUAD:
5383                 break
5384         game.state.galaxy[w.i][w.j].klingons += int(klump)
5385         if krem <= 0:
5386             break
5387     # Position Klingon Commander Ships
5388     for i in range(game.incom):
5389         while True:
5390             w = randplace(GALSIZE)
5391             if not welcoming(w) or w in game.state.kcmdr:
5392                 continue
5393             if (game.state.galaxy[w.i][w.j].klingons or withprob(0.25)):
5394                 break
5395         game.state.galaxy[w.i][w.j].klingons += 1
5396         game.state.kcmdr.append(w)
5397     # Locate planets in galaxy
5398     for i in range(game.inplan):
5399         while True:
5400             w = randplace(GALSIZE) 
5401             if game.state.galaxy[w.i][w.j].planet == None:
5402                 break
5403         new = planet()
5404         new.quadrant = w
5405         new.crystals = "absent"
5406         if (game.options & OPTION_WORLDS) and i < NINHAB:
5407             new.pclass = "M"    # All inhabited planets are class M
5408             new.crystals = "absent"
5409             new.known = "known"
5410             new.name = systnames[i]
5411             new.inhabited = True
5412         else:
5413             new.pclass = ("M", "N", "O")[randrange(0, 3)]
5414             if withprob(0.33):
5415                 new.crystals = "present"
5416             new.known = "unknown"
5417             new.inhabited = False
5418         game.state.galaxy[w.i][w.j].planet = new
5419         game.state.planets.append(new)
5420     # Locate Romulans
5421     for i in range(game.state.nromrem):
5422         w = randplace(GALSIZE)
5423         game.state.galaxy[w.i][w.j].romulans += 1
5424     # Place the Super-Commander if needed
5425     if game.state.nscrem > 0:
5426         while True:
5427             w = randplace(GALSIZE)
5428             if welcoming(w):
5429                 break
5430         game.state.kscmdr = w
5431         game.state.galaxy[w.i][w.j].klingons += 1
5432     # Initialize times for extraneous events
5433     schedule(FSNOVA, expran(0.5 * game.intime))
5434     schedule(FTBEAM, expran(1.5 * (game.intime / len(game.state.kcmdr))))
5435     schedule(FSNAP, randreal(1.0, 2.0)) # Force an early snapshot
5436     schedule(FBATTAK, expran(0.3*game.intime))
5437     unschedule(FCDBAS)
5438     if game.state.nscrem:
5439         schedule(FSCMOVE, 0.2777)
5440     else:
5441         unschedule(FSCMOVE)
5442     unschedule(FSCDBAS)
5443     unschedule(FDSPROB)
5444     if (game.options & OPTION_WORLDS) and game.skill >= SKILL_GOOD:
5445         schedule(FDISTR, expran(1.0 + game.intime))
5446     else:
5447         unschedule(FDISTR)
5448     unschedule(FENSLV)
5449     unschedule(FREPRO)
5450     # Place thing (in tournament game, we don't want one!)
5451     # New in SST2K: never place the Thing near a starbase.
5452     # This makes sense and avoids a special case in the old code.
5453     global thing
5454     if game.tourn is None:
5455         while True:
5456             thing = randplace(GALSIZE)
5457             if thing not in game.state.baseq:
5458                 break
5459     skip(2)
5460     game.state.snap = False
5461     if game.skill == SKILL_NOVICE:
5462         prout(_("It is stardate %d. The Federation is being attacked by") % int(game.state.date))
5463         prout(_("a deadly Klingon invasion force. As captain of the United"))
5464         prout(_("Starship U.S.S. Enterprise, it is your mission to seek out"))
5465         prout(_("and destroy this invasion force of %d battle cruisers.") % ((game.inkling + game.incom + game.inscom)))
5466         prout(_("You have an initial allotment of %d stardates to complete") % int(game.intime))
5467         prout(_("your mission.  As you proceed you may be given more time."))
5468         skip(1)
5469         prout(_("You will have %d supporting starbases.") % (game.inbase))
5470         proutn(_("Starbase locations-  "))
5471     else:
5472         prout(_("Stardate %d.") % int(game.state.date))
5473         skip(1)
5474         prout(_("%d Klingons.") % (game.inkling + game.incom + game.inscom))
5475         prout(_("An unknown number of Romulans."))
5476         if game.state.nscrem:
5477             prout(_("And one (GULP) Super-Commander."))
5478         prout(_("%d stardates.") % int(game.intime))
5479         proutn(_("%d starbases in ") % game.inbase)
5480     for i in range(game.inbase):
5481         proutn(`game.state.baseq[i]`)
5482         proutn("  ")
5483     skip(2)
5484     proutn(_("The Enterprise is currently in Quadrant %s") % game.quadrant)
5485     proutn(_(" Sector %s") % game.sector)
5486     skip(2)
5487     prout(_("Good Luck!"))
5488     if game.state.nscrem:
5489         prout(_("  YOU'LL NEED IT."))
5490     waitfor()
5491     newqad()
5492     if len(game.enemies) - (thing == game.quadrant) - (game.tholian != None):
5493         game.shldup = True
5494     if game.neutz:      # bad luck to start in a Romulan Neutral Zone
5495         attack(torps_ok=False)
5496
5497 def choose():
5498     "Choose your game type."
5499     while True:
5500         game.tourn = game.length = 0
5501         game.thawed = False
5502         game.skill = SKILL_NONE
5503         if not scanner.inqueue: # Can start with command line options 
5504             proutn(_("Would you like a regular, tournament, or saved game? "))
5505         scanner.next()
5506         if scanner.sees("tournament"):
5507             while scanner.next() == "IHEOL":
5508                 proutn(_("Type in tournament number-"))
5509             if scanner.real == 0:
5510                 scanner.chew()
5511                 continue # We don't want a blank entry
5512             game.tourn = int(round(scanner.real))
5513             random.seed(scanner.real)
5514             if logfp:
5515                 logfp.write("# random.seed(%d)\n" % scanner.real)
5516             break
5517         if scanner.sees("saved") or scanner.sees("frozen"):
5518             if thaw():
5519                 continue
5520             scanner.chew()
5521             if game.passwd == None:
5522                 continue
5523             if not game.alldone:
5524                 game.thawed = True # No plaque if not finished
5525             report()
5526             waitfor()
5527             return True
5528         if scanner.sees("regular"):
5529             break
5530         proutn(_("What is \"%s\"?") % scanner.token)
5531         scanner.chew()
5532     while game.length==0 or game.skill==SKILL_NONE:
5533         if scanner.next() == "IHALPHA":
5534             if scanner.sees("short"):
5535                 game.length = 1
5536             elif scanner.sees("medium"):
5537                 game.length = 2
5538             elif scanner.sees("long"):
5539                 game.length = 4
5540             elif scanner.sees("novice"):
5541                 game.skill = SKILL_NOVICE
5542             elif scanner.sees("fair"):
5543                 game.skill = SKILL_FAIR
5544             elif scanner.sees("good"):
5545                 game.skill = SKILL_GOOD
5546             elif scanner.sees("expert"):
5547                 game.skill = SKILL_EXPERT
5548             elif scanner.sees("emeritus"):
5549                 game.skill = SKILL_EMERITUS
5550             else:
5551                 proutn(_("What is \""))
5552                 proutn(scanner.token)
5553                 prout("\"?")
5554         else:
5555             scanner.chew()
5556             if game.length==0:
5557                 proutn(_("Would you like a Short, Medium, or Long game? "))
5558             elif game.skill == SKILL_NONE:
5559                 proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
5560     # Choose game options -- added by ESR for SST2K
5561     if scanner.next() != "IHALPHA":
5562         scanner.chew()
5563         proutn(_("Choose your game style (plain, almy, fancy or just press enter): "))
5564         scanner.next()
5565     if scanner.sees("plain"):
5566         # Approximates the UT FORTRAN version.
5567         game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
5568         game.options |= OPTION_PLAIN
5569     elif scanner.sees("almy"):
5570         # Approximates Tom Almy's version.
5571         game.options &=~ (OPTION_THINGY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
5572         game.options |= OPTION_ALMY
5573     elif scanner.sees("fancy") or scanner.sees("\n"):
5574         pass
5575     elif len(scanner.token):
5576         proutn(_("What is \"%s\"?") % scanner.token)
5577     game.options &=~ OPTION_COLOR
5578     setpassword()
5579     if game.passwd == "debug":
5580         idebug = True
5581         prout("=== Debug mode enabled.")
5582     # Use parameters to generate initial values of things
5583     game.damfac = 0.5 * game.skill
5584     game.inbase = randrange(BASEMIN, BASEMAX+1)
5585     game.inplan = 0
5586     if game.options & OPTION_PLANETS:
5587         game.inplan += randrange(MAXUNINHAB/2, MAXUNINHAB+1)
5588     if game.options & OPTION_WORLDS:
5589         game.inplan += int(NINHAB)
5590     game.state.nromrem = game.inrom = randrange(2 *game.skill)
5591     game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR)
5592     game.state.remtime = 7.0 * game.length
5593     game.intime = game.state.remtime
5594     game.state.remkl = game.inkling = 2.0*game.intime*((game.skill+1 - 2*randreal())*game.skill*0.1+.15)
5595     game.incom = min(MINCMDR, int(game.skill + 0.0625*game.inkling*randreal()))
5596     game.state.remres = (game.inkling+4*game.incom)*game.intime
5597     game.inresor = game.state.remres
5598     if game.inkling > 50:
5599         game.state.inbase += 1
5600     return False
5601
5602 def dropin(iquad=None):
5603     "Drop a feature on a random dot in the current quadrant."
5604     while True:
5605         w = randplace(QUADSIZE)
5606         if game.quad[w.i][w.j] == '.':
5607             break
5608     if iquad is not None:
5609         game.quad[w.i][w.j] = iquad
5610     return w
5611
5612 def newcnd():
5613     "Update our alert status."
5614     game.condition = "green"
5615     if game.energy < 1000.0:
5616         game.condition = "yellow"
5617     if game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons or game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans:
5618         game.condition = "red"
5619     if not game.alive:
5620         game.condition="dead"
5621
5622 def newkling():
5623     "Drop new Klingon into current quadrant."
5624     return enemy('K', loc=dropin(), power=randreal(300,450)+25.0*game.skill)
5625
5626 def newqad():
5627     "Set up a new state of quadrant, for when we enter or re-enter it."
5628     game.justin = True
5629     game.iplnet = None
5630     game.neutz = game.inorbit = game.landed = False
5631     game.ientesc = game.iseenit = False
5632     # Create a blank quadrant
5633     game.quad = fill2d(QUADSIZE, lambda i, j: '.')
5634     if game.iscate:
5635         # Attempt to escape Super-commander, so tbeam back!
5636         game.iscate = False
5637         game.ientesc = True
5638     q = game.state.galaxy[game.quadrant.i][game.quadrant.j]
5639     # cope with supernova
5640     if q.supernova:
5641         return
5642     game.klhere = q.klingons
5643     game.irhere = q.romulans
5644     # Position Starship
5645     game.quad[game.sector.i][game.sector.j] = game.ship
5646     game.enemies = []
5647     if q.klingons:
5648         # Position ordinary Klingons
5649         for i in range(game.klhere):
5650             newkling()
5651         # If we need a commander, promote a Klingon
5652         for cmdr in game.state.kcmdr:
5653             if cmdr == game.quadrant:
5654                 e = game.enemies[game.klhere-1]
5655                 game.quad[e.location.i][e.location.j] = 'C'
5656                 e.power = randreal(950,1350) + 50.0*game.skill
5657                 break   
5658         # If we need a super-commander, promote a Klingon
5659         if game.quadrant == game.state.kscmdr:
5660             e = game.enemies[0]
5661             game.quad[e.location.i][e.location.j] = 'S'
5662             e.power = randreal(1175.0,  1575.0) + 125.0*game.skill
5663             game.iscate = (game.state.remkl > 1)
5664     # Put in Romulans if needed
5665     for i in range(q.romulans):
5666         enemy('R', loc=dropin(), power=randreal(400.0,850.0)+50.0*game.skill)
5667     # If quadrant needs a starbase, put it in
5668     if q.starbase:
5669         game.base = dropin('B')
5670     # If quadrant needs a planet, put it in
5671     if q.planet:
5672         game.iplnet = q.planet
5673         if not q.planet.inhabited:
5674             game.plnet = dropin('P')
5675         else:
5676             game.plnet = dropin('@')
5677     # Check for condition
5678     newcnd()
5679     # Check for RNZ
5680     if game.irhere > 0 and game.klhere == 0:
5681         game.neutz = True
5682         if not damaged(DRADIO):
5683             skip(1)
5684             prout(_("LT. Uhura- \"Captain, an urgent message."))
5685             prout(_("  I'll put it on audio.\"  CLICK"))
5686             skip(1)
5687             prout(_("INTRUDER! YOU HAVE VIOLATED THE ROMULAN NEUTRAL ZONE."))
5688             prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
5689     # Put in THING if needed
5690     if thing == game.quadrant:
5691         enemy(type='?', loc=dropin(),
5692                   power=randreal(6000,6500.0)+250.0*game.skill)
5693         if not damaged(DSRSENS):
5694             skip(1)
5695             prout(_("Mr. Spock- \"Captain, this is most unusual."))
5696             prout(_("    Please examine your short-range scan.\""))
5697     # Decide if quadrant needs a Tholian; lighten up if skill is low 
5698     if game.options & OPTION_THOLIAN:
5699         if (game.skill < SKILL_GOOD and withprob(0.02)) or \
5700             (game.skill == SKILL_GOOD and withprob(0.05)) or \
5701             (game.skill > SKILL_GOOD and withprob(0.08)):
5702             w = coord()
5703             while True:
5704                 w.i = withprob(0.5) * (QUADSIZE-1)
5705                 w.j = withprob(0.5) * (QUADSIZE-1)
5706                 if game.quad[w.i][w.j] == '.':
5707                     break
5708             game.tholian = enemy(type='T', loc=w,
5709                                  power=randrange(100, 500) + 25.0*game.skill)
5710             # Reserve unoccupied corners 
5711             if game.quad[0][0]=='.':
5712                 game.quad[0][0] = 'X'
5713             if game.quad[0][QUADSIZE-1]=='.':
5714                 game.quad[0][QUADSIZE-1] = 'X'
5715             if game.quad[QUADSIZE-1][0]=='.':
5716                 game.quad[QUADSIZE-1][0] = 'X'
5717             if game.quad[QUADSIZE-1][QUADSIZE-1]=='.':
5718                 game.quad[QUADSIZE-1][QUADSIZE-1] = 'X'
5719     game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
5720     # And finally the stars
5721     for i in range(q.stars):
5722         dropin('*')
5723     # Put in a few black holes
5724     for i in range(1, 3+1):
5725         if withprob(0.5): 
5726             dropin(' ')
5727     # Take out X's in corners if Tholian present
5728     if game.tholian:
5729         if game.quad[0][0]=='X':
5730             game.quad[0][0] = '.'
5731         if game.quad[0][QUADSIZE-1]=='X':
5732             game.quad[0][QUADSIZE-1] = '.'
5733         if game.quad[QUADSIZE-1][0]=='X':
5734             game.quad[QUADSIZE-1][0] = '.'
5735         if game.quad[QUADSIZE-1][QUADSIZE-1]=='X':
5736             game.quad[QUADSIZE-1][QUADSIZE-1] = '.'
5737
5738 def setpassword():
5739     "Set the self-destruct password."
5740     if game.options & OPTION_PLAIN:
5741         while True:
5742             scanner.chew()
5743             proutn(_("Please type in a secret password- "))
5744             scanner.next()
5745             game.passwd = scanner.token
5746             if game.passwd != None:
5747                 break
5748     else:
5749         game.passwd = ""
5750         for i in range(8):
5751             game.passwd += chr(ord('a')+randrange(26))
5752
5753 # Code from sst.c begins here
5754
5755 commands = {
5756     "SRSCAN":           OPTION_TTY,
5757     "STATUS":           OPTION_TTY,
5758     "REQUEST":          OPTION_TTY,
5759     "LRSCAN":           OPTION_TTY,
5760     "PHASERS":          0,
5761     "TORPEDO":          0,
5762     "PHOTONS":          0,
5763     "MOVE":             0,
5764     "SHIELDS":          0,
5765     "DOCK":             0,
5766     "DAMAGES":          0,
5767     "CHART":            0,
5768     "IMPULSE":          0,
5769     "REST":             0,
5770     "WARP":             0,
5771     "SCORE":            0,
5772     "SENSORS":          OPTION_PLANETS,
5773     "ORBIT":            OPTION_PLANETS,
5774     "TRANSPORT":        OPTION_PLANETS,
5775     "MINE":             OPTION_PLANETS,
5776     "CRYSTALS":         OPTION_PLANETS,
5777     "SHUTTLE":          OPTION_PLANETS,
5778     "PLANETS":          OPTION_PLANETS,
5779     "REPORT":           0,
5780     "COMPUTER":         0,
5781     "COMMANDS":         0,
5782     "EMEXIT":           0,
5783     "PROBE":            OPTION_PROBE,
5784     "SAVE":             0,
5785     "FREEZE":           0,      # Synonym for SAVE
5786     "ABANDON":          0,
5787     "DESTRUCT":         0,
5788     "DEATHRAY":         0,
5789     "DEBUG":            0,
5790     "MAYDAY":           0,
5791     "SOS":              0,      # Synonym for MAYDAY
5792     "CALL":             0,      # Synonym for MAYDAY
5793     "QUIT":             0,
5794     "HELP":             0,
5795 }
5796
5797 def listCommands():
5798     "Generate a list of legal commands."
5799     prout(_("LEGAL COMMANDS ARE:"))
5800     emitted = 0
5801     for key in commands:
5802         if not commands[key] or (commands[key] & game.options):
5803             proutn("%-12s " % key)
5804             emitted += 1
5805             if emitted % 5 == 4:
5806                 skip(1)
5807     skip(1)
5808
5809 def helpme():
5810     "Browse on-line help."
5811     key = scanner.next()
5812     while True:
5813         if key == "IHEOL":
5814             setwnd(prompt_window)
5815             proutn(_("Help on what command? "))
5816             key = scanner.next()
5817         setwnd(message_window)
5818         if key == "IHEOL":
5819             return
5820         if scanner.token.upper() in commands or scanner.token == "ABBREV":
5821             break
5822         skip(1)
5823         listCommands()
5824         key = "IHEOL"
5825         scanner.chew()
5826         skip(1)
5827     cmd = scanner.token.upper()
5828     for directory in docpath:
5829         try:
5830             fp = open(os.path.join(directory, "sst.doc"), "r")
5831             break
5832         except IOError:
5833             pass
5834     else:
5835         prout(_("Spock-  \"Captain, that information is missing from the"))
5836         prout(_("   computer. You need to find sst.doc and put it somewhere"))
5837         proutn(_("   in these directories: %s") % ":".join(docpath))
5838         prout(".\"")
5839         # This used to continue: "You need to find SST.DOC and put 
5840         # it in the current directory."
5841         return
5842     while True:
5843         linebuf = fp.readline()
5844         if linebuf == '':
5845             prout(_("Spock- \"Captain, there is no information on that command.\""))
5846             fp.close()
5847             return
5848         if linebuf[0] == '%' and linebuf[1] == '%' and linebuf[2] == ' ':
5849             linebuf = linebuf[3:].strip()
5850             if cmd.upper() == linebuf:
5851                 break
5852     skip(1)
5853     prout(_("Spock- \"Captain, I've found the following information:\""))
5854     skip(1)
5855     while True:
5856         linebuf = fp.readline()
5857         if "******" in linebuf:
5858             break
5859         proutn(linebuf)
5860     fp.close()
5861
5862 def makemoves():
5863     "Command-interpretation loop."
5864     clrscr()
5865     setwnd(message_window)
5866     while True:         # command loop 
5867         drawmaps(1)
5868         while True:     # get a command 
5869             hitme = False
5870             game.optime = game.justin = False
5871             scanner.chew()
5872             setwnd(prompt_window)
5873             clrscr()
5874             proutn("COMMAND> ")
5875             if scanner.next() == "IHEOL":
5876                 if game.options & OPTION_CURSES:
5877                     makechart()
5878                 continue
5879             elif scanner.token == "":
5880                 continue
5881             game.ididit = False
5882             clrscr()
5883             setwnd(message_window)
5884             clrscr()
5885             candidates = filter(lambda x: x.startswith(scanner.token.upper()),
5886                                 commands)
5887             if len(candidates) == 1:
5888                 cmd = candidates[0]
5889                 break
5890             elif candidates and not (game.options & OPTION_PLAIN):
5891                 prout("Commands with prefix '%s': %s" % (scanner.token, " ".join(candidates)))
5892             else:
5893                 listCommands()
5894                 continue
5895         if cmd == "SRSCAN":             # srscan
5896             srscan()
5897         elif cmd == "STATUS":           # status
5898             status()
5899         elif cmd == "REQUEST":          # status request 
5900             request()
5901         elif cmd == "LRSCAN":           # long range scan
5902             lrscan(silent=False)
5903         elif cmd == "PHASERS":          # phasers
5904             phasers()
5905             if game.ididit:
5906                 hitme = True
5907         elif cmd in ("TORPEDO", "PHOTONS"):     # photon torpedos
5908             torps()
5909             if game.ididit:
5910                 hitme = True
5911         elif cmd == "MOVE":             # move under warp
5912             warp(course=None, involuntary=False)
5913         elif cmd == "SHIELDS":          # shields
5914             doshield(shraise=False)
5915             if game.ididit:
5916                 hitme = True
5917                 game.shldchg = False
5918         elif cmd == "DOCK":             # dock at starbase
5919             dock(True)
5920             if game.ididit:
5921                 attack(torps_ok=False)          
5922         elif cmd == "DAMAGES":          # damage reports
5923             damagereport()
5924         elif cmd == "CHART":            # chart
5925             makechart()
5926         elif cmd == "IMPULSE":          # impulse
5927             impulse()
5928         elif cmd == "REST":             # rest
5929             wait()
5930             if game.ididit:
5931                 hitme = True
5932         elif cmd == "WARP":             # warp
5933             setwarp()
5934         elif cmd == "SCORE":            # score
5935             score()
5936         elif cmd == "SENSORS":          # sensors
5937             sensor()
5938         elif cmd == "ORBIT":            # orbit
5939             orbit()
5940             if game.ididit:
5941                 hitme = True
5942         elif cmd == "TRANSPORT":                # transport "beam"
5943             beam()
5944         elif cmd == "MINE":             # mine
5945             mine()
5946             if game.ididit:
5947                 hitme = True
5948         elif cmd == "CRYSTALS":         # crystals
5949             usecrystals()
5950             if game.ididit:
5951                 hitme = True
5952         elif cmd == "SHUTTLE":          # shuttle
5953             shuttle()
5954             if game.ididit:
5955                 hitme = True
5956         elif cmd == "PLANETS":          # Planet list
5957             survey()
5958         elif cmd == "REPORT":           # Game Report 
5959             report()
5960         elif cmd == "COMPUTER":         # use COMPUTER!
5961             eta()
5962         elif cmd == "COMMANDS":
5963             listCommands()
5964         elif cmd == "EMEXIT":           # Emergency exit
5965             clrscr()                    # Hide screen
5966             freeze(True)                # forced save
5967             raise SysExit,1                     # And quick exit
5968         elif cmd == "PROBE":
5969             probe()                     # Launch probe
5970             if game.ididit:
5971                 hitme = True
5972         elif cmd == "ABANDON":          # Abandon Ship
5973             abandon()
5974         elif cmd == "DESTRUCT":         # Self Destruct
5975             selfdestruct()
5976         elif cmd == "SAVE":             # Save Game
5977             freeze(False)
5978             clrscr()
5979             if game.skill > SKILL_GOOD:
5980                 prout(_("WARNING--Saved games produce no plaques!"))
5981         elif cmd == "DEATHRAY":         # Try a desparation measure
5982             deathray()
5983             if game.ididit:
5984                 hitme = True
5985         elif cmd == "DEBUGCMD":         # What do we want for debug???
5986             debugme()
5987         elif cmd == "MAYDAY":           # Call for help
5988             mayday()
5989             if game.ididit:
5990                 hitme = True
5991         elif cmd == "QUIT":
5992             game.alldone = True         # quit the game
5993         elif cmd == "HELP":
5994             helpme()                    # get help
5995         while True:
5996             if game.alldone:
5997                 break           # Game has ended
5998             if game.optime != 0.0:
5999                 events()
6000                 if game.alldone:
6001                     break       # Events did us in
6002             if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
6003                 atover(False)
6004                 continue
6005             if hitme and not game.justin:
6006                 attack(torps_ok=True)
6007                 if game.alldone:
6008                     break
6009                 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
6010                     atover(False)
6011                     hitme = True
6012                     continue
6013             break
6014         if game.alldone:
6015             break
6016     if idebug:
6017         prout("=== Ending")
6018
6019 def cramen(type):
6020     "Emit the name of an enemy or feature." 
6021     if   type == 'R': s = _("Romulan")
6022     elif type == 'K': s = _("Klingon")
6023     elif type == 'C': s = _("Commander")
6024     elif type == 'S': s = _("Super-commander")
6025     elif type == '*': s = _("Star")
6026     elif type == 'P': s = _("Planet")
6027     elif type == 'B': s = _("Starbase")
6028     elif type == ' ': s = _("Black hole")
6029     elif type == 'T': s = _("Tholian")
6030     elif type == '#': s = _("Tholian web")
6031     elif type == '?': s = _("Stranger")
6032     elif type == '@': s = _("Inhabited World")
6033     else: s = "Unknown??"
6034     return s
6035
6036 def crmena(stars, enemy, loctype, w):
6037     "Emit the name of an enemy and his location."
6038     buf = ""
6039     if stars:
6040         buf += "***"
6041     buf += cramen(enemy) + _(" at ")
6042     if loctype == "quadrant":
6043         buf += _("Quadrant ")
6044     elif loctype == "sector":
6045         buf += _("Sector ")
6046     return buf + `w`
6047
6048 def crmshp():
6049     "Emit our ship name." 
6050     return{'E':_("Enterprise"),'F':_("Faerie Queene")}.get(game.ship,"Ship???")
6051
6052 def stars():
6053     "Emit a line of stars" 
6054     prouts("******************************************************")
6055     skip(1)
6056
6057 def expran(avrage):
6058     return -avrage*math.log(1e-7 + randreal())
6059
6060 def randplace(size):
6061     "Choose a random location."
6062     w = coord()
6063     w.i = randrange(size) 
6064     w.j = randrange(size)
6065     return w
6066
6067 class sstscanner:
6068     def __init__(self):
6069         self.type = None
6070         self.token = None
6071         self.real = 0.0
6072         self.inqueue = []
6073     def next(self):
6074         # Get a token from the user
6075         self.real = 0.0
6076         self.token = ''
6077         # Fill the token quue if nothing here
6078         while not self.inqueue:
6079             line = cgetline()
6080             if curwnd==prompt_window:
6081                 clrscr()
6082                 setwnd(message_window)
6083                 clrscr()
6084             if line == '':
6085                 return None
6086             if not line:
6087                 continue
6088             else:
6089                 self.inqueue = line.lstrip().split() + ["\n"]
6090         # From here on in it's all looking at the queue
6091         self.token = self.inqueue.pop(0)
6092         if self.token == "\n":
6093             self.type = "IHEOL"
6094             return "IHEOL"
6095         try:
6096             self.real = float(self.token)
6097             self.type = "IHREAL"
6098             return "IHREAL"
6099         except ValueError:
6100             pass
6101         # Treat as alpha
6102         self.token = self.token.lower()
6103         self.type = "IHALPHA"
6104         self.real = None
6105         return "IHALPHA"
6106     def append(self, tok):
6107         self.inqueue.append(tok)
6108     def push(self, tok):
6109         self.inqueue.insert(0, tok)
6110     def waiting(self):
6111         return self.inqueue
6112     def chew(self):
6113         # Demand input for next scan
6114         self.inqueue = []
6115         self.real = self.token = None
6116     def sees(self, s):
6117         # compares s to item and returns true if it matches to the length of s
6118         return s.startswith(self.token)
6119     def int(self):
6120         # Round token value to nearest integer
6121         return int(round(scanner.real))
6122     def getcoord(self):
6123         s = coord()
6124         scanner.next()
6125         if scanner.type != "IHREAL":
6126             huh()
6127             return None
6128         s.i = scanner.int()-1
6129         scanner.next()
6130         if scanner.type != "IHREAL":
6131             huh()
6132             return None
6133         s.j = scanner.int()-1
6134         return s
6135     def __repr__(str):
6136         return "<sstcanner: token=%s, type=%s, queue=%s>" % (scanner.token, scanner.type, scanner.inqueue)
6137
6138 def ja():
6139     "Yes-or-no confirmation."
6140     scanner.chew()
6141     while True:
6142         scanner.next()
6143         if scanner.token == 'y':
6144             return True
6145         if scanner.token == 'n':
6146             return False
6147         scanner.chew()
6148         proutn(_("Please answer with \"y\" or \"n\": "))
6149
6150 def huh():
6151     "Complain about unparseable input."
6152     scanner.chew()
6153     skip(1)
6154     prout(_("Beg your pardon, Captain?"))
6155
6156 def debugme():
6157     "Access to the internals for debugging."
6158     proutn("Reset levels? ")
6159     if ja() == True:
6160         if game.energy < game.inenrg:
6161             game.energy = game.inenrg
6162         game.shield = game.inshld
6163         game.torps = game.intorps
6164         game.lsupres = game.inlsr
6165     proutn("Reset damage? ")
6166     if ja() == True:
6167         for i in range(NDEVICES): 
6168             if game.damage[i] > 0.0: 
6169                 game.damage[i] = 0.0
6170     proutn("Toggle debug flag? ")
6171     if ja() == True:
6172         idebug = not idebug
6173         if idebug:
6174             prout("Debug output ON")        
6175         else:
6176             prout("Debug output OFF")
6177     proutn("Cause selective damage? ")
6178     if ja() == True:
6179         for i in range(NDEVICES):
6180             proutn("Kill %s?" % device[i])
6181             scanner.chew()
6182             key = scanner.next()
6183             if key == "IHALPHA" and scanner.sees("y"):
6184                 game.damage[i] = 10.0
6185     proutn("Examine/change events? ")
6186     if ja() == True:
6187         ev = event()
6188         w = coord()
6189         legends = {
6190             FSNOVA:  "Supernova       ",
6191             FTBEAM:  "T Beam          ",
6192             FSNAP:   "Snapshot        ",
6193             FBATTAK: "Base Attack     ",
6194             FCDBAS:  "Base Destroy    ",
6195             FSCMOVE: "SC Move         ",
6196             FSCDBAS: "SC Base Destroy ",
6197             FDSPROB: "Probe Move      ",
6198             FDISTR:  "Distress Call   ",
6199             FENSLV:  "Enslavement     ",
6200             FREPRO:  "Klingon Build   ",
6201         }
6202         for i in range(1, NEVENTS):
6203             proutn(legends[i])
6204             if is_scheduled(i):
6205                 proutn("%.2f" % (scheduled(i)-game.state.date))
6206                 if i == FENSLV or i == FREPRO:
6207                     ev = findevent(i)
6208                     proutn(" in %s" % ev.quadrant)
6209             else:
6210                 proutn("never")
6211             proutn("? ")
6212             scanner.chew()
6213             key = scanner.next()
6214             if key == 'n':
6215                 unschedule(i)
6216                 scanner.chew()
6217             elif key == "IHREAL":
6218                 ev = schedule(i, scanner.real)
6219                 if i == FENSLV or i == FREPRO:
6220                     scanner.chew()
6221                     proutn("In quadrant- ")
6222                     key = scanner.next()
6223                     # "IHEOL" says to leave coordinates as they are 
6224                     if key != "IHEOL":
6225                         if key != "IHREAL":
6226                             prout("Event %d canceled, no x coordinate." % (i))
6227                             unschedule(i)
6228                             continue
6229                         w.i = int(round(scanner.real))
6230                         key = scanner.next()
6231                         if key != "IHREAL":
6232                             prout("Event %d canceled, no y coordinate." % (i))
6233                             unschedule(i)
6234                             continue
6235                         w.j = int(round(scanner.real))
6236                         ev.quadrant = w
6237         scanner.chew()
6238     proutn("Induce supernova here? ")
6239     if ja() == True:
6240         game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova = True
6241         atover(True)
6242
6243 if __name__ == '__main__':
6244     import getopt, socket
6245     try:
6246         global line, thing, game, idebug
6247         game = None
6248         thing = coord()
6249         thing.angry = False
6250         game = gamestate()
6251         idebug = 0
6252         game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY)
6253         if os.getenv("TERM"):
6254             game.options |= OPTION_CURSES
6255         else:
6256             game.options |= OPTION_TTY
6257         seed = int(time.time())
6258         (options, arguments) = getopt.getopt(sys.argv[1:], "r:s:tx")
6259         for (switch, val) in options:
6260             if switch == '-r':
6261                 try:
6262                     replayfp = open(val, "r")
6263                 except IOError:
6264                     sys.stderr.write("sst: can't open replay file %s\n" % val)
6265                     raise SystemExit, 1
6266                 try:
6267                     line = replayfp.readline().strip()
6268                     (leader, key, seed) = line.split()
6269                     seed = eval(seed)
6270                     sys.stderr.write("sst2k: seed set to %s\n" % seed)
6271                     line = replayfp.readline().strip()
6272                     arguments += line.split()[2:]
6273                 except ValueError:
6274                     sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
6275                     raise SystemExit(1)
6276                 game.options |= OPTION_TTY
6277                 game.options &=~ OPTION_CURSES
6278             elif switch == '-s':
6279                 seed = int(val)
6280             elif switch == '-t':
6281                 game.options |= OPTION_TTY
6282                 game.options &=~ OPTION_CURSES
6283             elif switch == '-x':
6284                 idebug = True
6285             else:
6286                 sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n")
6287                 raise SystemExit, 1
6288         # where to save the input in case of bugs
6289         if "TMPDIR" in os.environ:
6290             tmpdir = os.environ['TMPDIR']
6291         else:
6292             tmpdir = "/tmp"
6293         try:
6294             logfp = open(os.path.join(tmpdir, "sst-input.log"), "w")
6295         except IOError:
6296             sys.stderr.write("sst: warning, can't open logfile\n")
6297             sys.exit(1)
6298         if logfp:
6299             logfp.write("# seed %s\n" % seed)
6300             logfp.write("# options %s\n" % " ".join(arguments))
6301             logfp.write("# recorded by %s@%s on %s\n" % \
6302                     (getpass.getuser(),socket.gethostname(),time.ctime()))
6303         random.seed(seed)
6304         scanner = sstscanner()
6305         map(scanner.append, arguments)
6306         try:
6307             iostart()
6308             while True: # Play a game 
6309                 setwnd(fullscreen_window)
6310                 clrscr()
6311                 prelim()
6312                 setup()
6313                 if game.alldone:
6314                     score()
6315                     game.alldone = False
6316                 else:
6317                     makemoves()
6318                 skip(1)
6319                 stars()
6320                 skip(1)
6321                 if game.tourn and game.alldone:
6322                     proutn(_("Do you want your score recorded?"))
6323                     if ja() == True:
6324                         scanner.chew()
6325                         scanner.push("\n")
6326                         freeze(False)
6327                 scanner.chew()
6328                 proutn(_("Do you want to play again? "))
6329                 if not ja():
6330                     break
6331             skip(1)
6332             prout(_("May the Great Bird of the Galaxy roost upon your home planet."))
6333         finally:
6334             ioend()
6335         raise SystemExit, 0
6336     except KeyboardInterrupt:
6337         if logfp:
6338             logfp.close()
6339         print ""