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