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