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