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