Assign some probability to claaking device getting damaged.
[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             game.enemies.remove(self)
323         return motion
324     def __repr__(self):
325         return "<%s,%s.%f>" % (self.type, self.location, self.power)        # For debugging
326
327 class Gamestate:
328     def __init__(self):
329         self.options = None        # Game options
330         self.state = Snapshot()        # A snapshot structure
331         self.snapsht = Snapshot()        # Last snapshot taken for time-travel purposes
332         self.quad = None        # contents of our quadrant
333         self.damage = [0.0] * NDEVICES        # damage encountered
334         self.future = []        # future events
335         i = NEVENTS
336         while i > 0:
337             i -= 1
338             self.future.append(Event())
339         self.passwd  = None        # Self Destruct password
340         self.enemies = []
341         self.quadrant = None        # where we are in the large
342         self.sector = None        # where we are in the small
343         self.tholian = None        # Tholian enemy object
344         self.base = None        # position of base in current quadrant
345         self.battle = None        # base coordinates being attacked
346         self.plnet = None        # location of planet in quadrant
347         self.gamewon = False        # Finished!
348         self.ididit = False        # action taken -- allows enemy to attack
349         self.alive = False        # we are alive (not killed)
350         self.justin = False        # just entered quadrant
351         self.shldup = False        # shields are up
352         self.shldchg = False        # shield is changing (affects efficiency)
353         self.iscate = False        # super commander is here
354         self.ientesc = False        # attempted escape from supercommander
355         self.resting = False        # rest time
356         self.icraft = False        # Kirk in Galileo
357         self.landed = False        # party on planet (true), on ship (false)
358         self.alldone = False        # game is now finished
359         self.neutz = False        # Romulan Neutral Zone
360         self.isarmed = False        # probe is armed
361         self.inorbit = False        # orbiting a planet
362         self.imine = False        # mining
363         self.icrystl = False        # dilithium crystals aboard
364         self.iseenit = False        # seen base attack report
365         self.thawed = False        # thawed game
366         self.condition = None        # "green", "yellow", "red", "docked", "dead"
367         self.iscraft = None        # "onship", "offship", "removed"
368         self.skill = None        # Player skill level
369         self.inkling = 0        # initial number of klingons
370         self.inbase = 0                # initial number of bases
371         self.incom = 0                # initial number of commanders
372         self.inscom = 0                # initial number of commanders
373         self.inrom = 0                # initial number of commanders
374         self.instar = 0                # initial stars
375         self.intorps = 0        # initial/max torpedoes
376         self.torps = 0                # number of torpedoes
377         self.ship = 0                # ship type -- 'E' is Enterprise
378         self.abandoned = 0        # count of crew abandoned in space
379         self.length = 0                # length of game
380         self.klhere = 0                # klingons here
381         self.casual = 0                # causalties
382         self.nhelp = 0                # calls for help
383         self.nkinks = 0                # count of energy-barrier crossings
384         self.iplnet = None        # planet # in quadrant
385         self.inplan = 0                # initial planets
386         self.irhere = 0                # Romulans in quadrant
387         self.isatb = 0                # =2 if super commander is attacking base
388         self.tourn = None        # tournament number
389         self.nprobes = 0        # number of probes available
390         self.inresor = 0.0        # initial resources
391         self.intime = 0.0        # initial time
392         self.inenrg = 0.0        # initial/max energy
393         self.inshld = 0.0        # initial/max shield
394         self.inlsr = 0.0        # initial life support resources
395         self.indate = 0.0        # initial date
396         self.energy = 0.0        # energy level
397         self.shield = 0.0        # shield level
398         self.warpfac = 0.0        # warp speed
399         self.lsupres = 0.0        # life support reserves
400         self.optime = 0.0        # time taken by current operation
401         self.damfac = 0.0        # damage factor
402         self.lastchart = 0.0        # time star chart was last updated
403         self.cryprob = 0.0        # probability that crystal will work
404         self.probe = None        # object holding probe course info
405         self.height = 0.0        # height of orbit around planet
406         self.score = 0.0        # overall score
407         self.perdate = 0.0        # rate of kills
408         self.idebug = False        # Debugging instrumentation enabled?
409         self.statekscmdr = None # No SuperCommander coordinates yet.
410         self.brigcapacity = 400     # Enterprise brig capacity
411         self.brigfree = 400       # How many klingons can we put in the brig?
412         self.kcaptured = 0      # Total Klingons captured, for scoring.
413         self.iscloaked = False  # Cloaking device on?
414         self.ncviol = 0         # Algreon treaty violations
415         self.isviolreported = False # We have been warned
416     def recompute(self):
417         # Stas thinks this should be (C expression):
418         # game.state.remkl + len(game.state.kcmdr) > 0 ?
419         #        game.state.remres/(game.state.remkl + 4*len(game.state.kcmdr)) : 99
420         # He says the existing expression is prone to divide-by-zero errors
421         # after killing the last klingon when score is shown -- perhaps also
422         # if the only remaining klingon is SCOM.
423         self.state.remtime = self.state.remres/(self.state.remkl + 4*len(self.state.kcmdr))
424     def unwon(self):
425         "Are there Klingons remaining?"
426         return self.state.remkl + len(self.state.kcmdr) + self.state.nscrem
427
428 FWON = 0
429 FDEPLETE = 1
430 FLIFESUP = 2
431 FNRG = 3
432 FBATTLE = 4
433 FNEG3 = 5
434 FNOVA = 6
435 FSNOVAED = 7
436 FABANDN = 8
437 FDILITHIUM = 9
438 FMATERIALIZE = 10
439 FPHASER = 11
440 FLOST = 12
441 FMINING = 13
442 FDPLANET = 14
443 FPNOVA = 15
444 FSSC = 16
445 FSTRACTOR = 17
446 FDRAY = 18
447 FTRIBBLE = 19
448 FHOLE = 20
449 FCREW = 21
450 FCLOAK = 22
451
452 def withprob(p):
453     return random.random() < p
454
455 def randrange(*args):
456     return random.randrange(*args)
457
458 def randreal(*args):
459     v = random.random()
460     if len(args) == 1:
461         v *= args[0]                 # from [0, args[0])
462     elif len(args) == 2:
463         v = args[0] + v*(args[1]-args[0])        # from [args[0], args[1])
464     return v
465
466 # Code from ai.c begins here
467
468 def welcoming(iq):
469     "Would this quadrant welcome another Klingon?"
470     return iq.valid_quadrant() and \
471         not game.state.galaxy[iq.i][iq.j].supernova and \
472         game.state.galaxy[iq.i][iq.j].klingons < MAXKLQUAD
473
474 def tryexit(enemy, look, irun):
475     "A bad guy attempts to bug out."
476     iq = Coord()
477     iq.i = game.quadrant.i+(look.i+(QUADSIZE-1))/QUADSIZE - 1
478     iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))/QUADSIZE - 1
479     if not welcoming(iq):
480         return False
481     if enemy.type == 'R':
482         return False # Romulans cannot escape!
483     if not irun:
484         # avoid intruding on another commander's territory
485         if enemy.type == 'C':
486             if iq in game.state.kcmdr:
487                 return []
488             # refuse to leave if currently attacking starbase
489             if game.battle == game.quadrant:
490                 return []
491         # don't leave if over 1000 units of energy
492         if enemy.power > 1000.0:
493             return []
494     oldloc = copy.copy(enemy.location)
495     # handle local matters related to escape
496     enemy.move(None)
497     game.klhere -= 1
498     if game.condition != "docked":
499         newcnd()
500     # Handle global matters related to escape
501     game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
502     game.state.galaxy[iq.i][iq.j].klingons += 1
503     if enemy.type == 'S':
504         game.iscate = False
505         game.ientesc = False
506         game.isatb = 0
507         schedule(FSCMOVE, 0.2777)
508         unschedule(FSCDBAS)
509         game.state.kscmdr = iq
510     else:
511         for cmdr in game.state.kcmdr:
512             if cmdr == game.quadrant:
513                 game.state.kcmdr.append(iq)
514                 break
515     # report move out of quadrant.
516     return [(True, enemy, oldloc, iq)]
517
518 # The bad-guy movement algorithm:
519 #
520 # 1. Enterprise has "force" based on condition of phaser and photon torpedoes.
521 # If both are operating full strength, force is 1000. If both are damaged,
522 # force is -1000. Having shields down subtracts an additional 1000.
523 #
524 # 2. Enemy has forces equal to the energy of the attacker plus
525 # 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
526 # 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
527 #
528 # Attacker Initial energy levels (nominal):
529 # Klingon   Romulan   Commander   Super-Commander
530 # Novice    400        700        1200
531 # Fair      425        750        1250
532 # Good      450        800        1300        1750
533 # Expert    475        850        1350        1875
534 # Emeritus  500        900        1400        2000
535 # VARIANCE   75        200         200         200
536 #
537 # Enemy vessels only move prior to their attack. In Novice - Good games
538 # only commanders move. In Expert games, all enemy vessels move if there
539 # is a commander present. In Emeritus games all enemy vessels move.
540 #
541 # 3. If Enterprise is not docked, an aggressive action is taken if enemy
542 # forces are 1000 greater than Enterprise.
543 #
544 # Agressive action on average cuts the distance between the ship and
545 # the enemy to 1/4 the original.
546 #
547 # 4.  At lower energy advantage, movement units are proportional to the
548 # advantage with a 650 advantage being to hold ground, 800 to move forward
549 # 1, 950 for two, 150 for back 4, etc. Variance of 100.
550 #
551 # If docked, is reduced by roughly 1.75*game.skill, generally forcing a
552 # retreat, especially at high skill levels.
553 #
554 # 5.  Motion is limited to skill level, except for SC hi-tailing it out.
555
556 def movebaddy(enemy):
557     "Tactical movement for the bad guys."
558     goto = Coord()
559     look = Coord()
560     irun = False
561     # This should probably be just (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
562     if game.skill >= SKILL_EXPERT:
563         nbaddys = (((game.quadrant in game.state.kcmdr)*2 + (game.state.kscmdr==game.quadrant)*2+game.klhere*1.23+game.irhere*1.5)/2.0)
564     else:
565         nbaddys = (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
566     old_dist = enemy.kdist
567     mdist = int(old_dist + 0.5) # Nearest integer distance
568     # If SC, check with spy to see if should hi-tail it
569     if enemy.type == 'S' and \
570         (enemy.power <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
571         irun = True
572         motion = -QUADSIZE
573     else:
574         # decide whether to advance, retreat, or hold position
575         forces = enemy.power+100.0*len(game.enemies)+400*(nbaddys-1)
576         if not game.shldup:
577             forces += 1000 # Good for enemy if shield is down!
578         if not damaged(DPHASER) or not damaged(DPHOTON):
579             if damaged(DPHASER): # phasers damaged
580                 forces += 300.0
581             else:
582                 forces -= 0.2*(game.energy - 2500.0)
583             if damaged(DPHOTON): # photon torpedoes damaged
584                 forces += 300.0
585             else:
586                 forces -= 50.0*game.torps
587         else:
588             # phasers and photon tubes both out!
589             forces += 1000.0
590         motion = 0
591         if forces <= 1000.0 and game.condition != "docked": # Typical situation
592             motion = ((forces + randreal(200))/150.0) - 5.0
593         else:
594             if forces > 1000.0: # Very strong -- move in for kill
595                 motion = (1.0 - randreal())**2 * old_dist + 1.0
596             if game.condition == "docked" and (game.options & OPTION_BASE): # protected by base -- back off !
597                 motion -= game.skill*(2.0-randreal()**2)
598         if game.idebug:
599             proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
600         # don't move if no motion
601         if motion == 0:
602             return []
603         # Limit motion according to skill
604         if abs(motion) > game.skill:
605             if motion < 0:
606                 motion = -game.skill
607             else:
608                 motion = game.skill
609     # calculate preferred number of steps
610     nsteps = abs(int(motion))
611     if motion > 0 and nsteps > mdist:
612         nsteps = mdist # don't overshoot
613     if nsteps > QUADSIZE:
614         nsteps = QUADSIZE # This shouldn't be necessary
615     if nsteps < 1:
616         nsteps = 1 # This shouldn't be necessary
617     if game.idebug:
618         proutn("NSTEPS = %d:" % nsteps)
619     # Compute preferred values of delta X and Y
620     m = game.sector - enemy.location
621     if 2.0 * abs(m.i) < abs(m.j):
622         m.i = 0
623     if 2.0 * abs(m.j) < abs(game.sector.i-enemy.location.i):
624         m.j = 0
625     m = (motion * m).sgn()
626     goto = enemy.location
627     # main move loop
628     for ll in range(nsteps):
629         if game.idebug:
630             proutn(" %d" % (ll+1))
631         # Check if preferred position available
632         look = goto + m
633         if m.i < 0:
634             krawli = 1
635         else:
636             krawli = -1
637         if m.j < 0:
638             krawlj = 1
639         else:
640             krawlj = -1
641         success = False
642         attempts = 0 # Settle mysterious hang problem
643         while attempts < 20 and not success:
644             attempts += 1
645             if look.i < 0 or look.i >= QUADSIZE:
646                 if motion < 0:
647                     return tryexit(enemy, look, irun)
648                 if krawli == m.i or m.j == 0:
649                     break
650                 look.i = goto.i + krawli
651                 krawli = -krawli
652             elif look.j < 0 or look.j >= QUADSIZE:
653                 if motion < 0:
654                     return tryexit(enemy, look, irun)
655                 if krawlj == m.j or m.i == 0:
656                     break
657                 look.j = goto.j + krawlj
658                 krawlj = -krawlj
659             elif (game.options & OPTION_RAMMING) and game.quad[look.i][look.j] != '.':
660                 # See if enemy should ram ship
661                 if game.quad[look.i][look.j] == game.ship and \
662                     (enemy.type == 'C' or enemy.type == 'S'):
663                     collision(rammed=True, enemy=enemy)
664                     return []
665                 if krawli != m.i and m.j != 0:
666                     look.i = goto.i + krawli
667                     krawli = -krawli
668                 elif krawlj != m.j and m.i != 0:
669                     look.j = goto.j + krawlj
670                     krawlj = -krawlj
671                 else:
672                     break # we have failed
673             else:
674                 success = True
675         if success:
676             goto = look
677             if game.idebug:
678                 proutn(repr(goto))
679         else:
680             break # done early
681     if game.idebug:
682         skip(1)
683     # Enemy moved, but is still in sector
684     return [(False, enemy, old_dist, goto)]
685
686 def moveklings():
687     "Sequence Klingon tactical movement."
688     if game.idebug:
689         prout("== MOVCOM")
690     # Figure out which Klingon is the commander (or Supercommander)
691     # and do move
692     tacmoves = []
693     if game.quadrant in game.state.kcmdr:
694         for enemy in game.enemies:
695             if enemy.type == 'C':
696                 tacmoves += movebaddy(enemy)
697     if game.state.kscmdr == game.quadrant:
698         for enemy in game.enemies:
699             if enemy.type == 'S':
700                 tacmoves += movebaddy(enemy)
701                 break
702     # If skill level is high, move other Klingons and Romulans too!
703     # Move these last so they can base their actions on what the
704     # commander(s) do.
705     if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
706         for enemy in game.enemies:
707             if enemy.type in ('K', 'R'):
708                 tacmoves += movebaddy(enemy)
709     return tacmoves
710
711 def movescom(iq, avoid):
712     "Supercommander movement helper."
713     # Avoid quadrants with bases if we want to avoid Enterprise
714     if not welcoming(iq) or (avoid and iq in game.state.baseq):
715         return False
716     if game.justin and not game.iscate:
717         return False
718     # do the move
719     game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons -= 1
720     game.state.kscmdr = iq
721     game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons += 1
722     if game.state.kscmdr == game.quadrant:
723         # SC has scooted, remove him from current quadrant
724         game.iscate = False
725         game.isatb = 0
726         game.ientesc = False
727         unschedule(FSCDBAS)
728         for enemy in game.enemies:
729             if enemy.type == 'S':
730                 enemy.move(None)
731         game.klhere -= 1
732         if game.condition != "docked":
733             newcnd()
734         sortenemies()
735     # check for a helpful planet
736     for i in range(game.inplan):
737         if game.state.planets[i].quadrant == game.state.kscmdr and \
738             game.state.planets[i].crystals == "present":
739             # destroy the planet
740             game.state.planets[i].pclass = "destroyed"
741             game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].planet = None
742             if communicating():
743                 announce()
744                 prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"))
745                 proutn(_("   a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
746                 prout(_("   by the Super-commander.\""))
747             break
748     return True # looks good!
749
750 def supercommander():
751     "Move the Super Commander."
752     iq = Coord()
753     sc = Coord()
754     ibq = Coord()
755     idelta = Coord()
756     basetbl = []
757     if game.idebug:
758         prout("== SUPERCOMMANDER")
759     # Decide on being active or passive
760     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 \
761             (game.state.date-game.indate) < 3.0)
762     if not game.iscate and avoid:
763         # compute move away from Enterprise
764         idelta = game.state.kscmdr-game.quadrant
765         if idelta.distance() > 2.0:
766             # circulate in space
767             idelta.i = game.state.kscmdr.j-game.quadrant.j
768             idelta.j = game.quadrant.i-game.state.kscmdr.i
769     else:
770         # compute distances to starbases
771         if not game.state.baseq:
772             # nothing left to do
773             unschedule(FSCMOVE)
774             return
775         sc = game.state.kscmdr
776         for (i, base) in enumerate(game.state.baseq):
777             basetbl.append((i, (base - sc).distance()))
778         if game.state.baseq > 1:
779             basetbl.sort(key=lambda x: x[1])
780         # look for nearest base without a commander, no Enterprise, and
781         # without too many Klingons, and not already under attack.
782         ifindit = iwhichb = 0
783         for (i2, base) in enumerate(game.state.baseq):
784             i = basetbl[i2][0]        # bug in original had it not finding nearest
785             if base == game.quadrant or base == game.battle or not welcoming(base):
786                 continue
787             # if there is a commander, and no other base is appropriate,
788             # we will take the one with the commander
789             for cmdr in game.state.kcmdr:
790                 if base == cmdr and ifindit != 2:
791                     ifindit = 2
792                     iwhichb = i
793                     break
794             else:        # no commander -- use this one
795                 ifindit = 1
796                 iwhichb = i
797                 break
798         if ifindit == 0:
799             return # Nothing suitable -- wait until next time
800         ibq = game.state.baseq[iwhichb]
801         # decide how to move toward base
802         idelta = ibq - game.state.kscmdr
803     # Maximum movement is 1 quadrant in either or both axes
804     idelta = idelta.sgn()
805     # try moving in both x and y directions
806     # there was what looked like a bug in the Almy C code here,
807     # but it might be this translation is just wrong.
808     iq = game.state.kscmdr + idelta
809     if not movescom(iq, avoid):
810         # failed -- try some other maneuvers
811         if idelta.i == 0 or idelta.j == 0:
812             # attempt angle move
813             if idelta.i != 0:
814                 iq.j = game.state.kscmdr.j + 1
815                 if not movescom(iq, avoid):
816                     iq.j = game.state.kscmdr.j - 1
817                     movescom(iq, avoid)
818             elif idelta.j != 0:
819                 iq.i = game.state.kscmdr.i + 1
820                 if not movescom(iq, avoid):
821                     iq.i = game.state.kscmdr.i - 1
822                     movescom(iq, avoid)
823         else:
824             # try moving just in x or y
825             iq.j = game.state.kscmdr.j
826             if not movescom(iq, avoid):
827                 iq.j = game.state.kscmdr.j + idelta.j
828                 iq.i = game.state.kscmdr.i
829                 movescom(iq, avoid)
830     # check for a base
831     if len(game.state.baseq) == 0:
832         unschedule(FSCMOVE)
833     else:
834         for ibq in game.state.baseq:
835             if ibq == game.state.kscmdr and game.state.kscmdr == game.battle:
836                 # attack the base
837                 if avoid:
838                     return # no, don't attack base!
839                 game.iseenit = False
840                 game.isatb = 1
841                 schedule(FSCDBAS, randreal(1.0, 3.0))
842                 if is_scheduled(FCDBAS):
843                     postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date)
844                 if not communicating():
845                     return # no warning
846                 game.iseenit = True
847                 announce()
848                 prout(_("Lt. Uhura-  \"Captain, the starbase in Quadrant %s") \
849                       % game.state.kscmdr)
850                 prout(_("   reports that it is under attack from the Klingon Super-commander."))
851                 proutn(_("   It can survive until stardate %d.\"") \
852                        % int(scheduled(FSCDBAS)))
853                 if not game.resting:
854                     return
855                 prout(_("Mr. Spock-  \"Captain, shall we cancel the rest period?\""))
856                 if not ja():
857                     return
858                 game.resting = False
859                 game.optime = 0.0 # actually finished
860                 return
861     # Check for intelligence report
862     if not game.idebug and \
863         (withprob(0.8) or \
864          (not communicating()) or \
865          not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].charted):
866         return
867     announce()
868     prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"))
869     proutn(_("   the Super-commander is in Quadrant %s,") % game.state.kscmdr)
870     return
871
872 def movetholian():
873     "Move the Tholian."
874     if not game.tholian or game.justin:
875         return
876     tid = Coord()
877     if game.tholian.location.i == 0 and game.tholian.location.j == 0:
878         tid.i = 0
879         tid.j = QUADSIZE-1
880     elif game.tholian.location.i == 0 and game.tholian.location.j == QUADSIZE-1:
881         tid.i = QUADSIZE-1
882         tid.j = QUADSIZE-1
883     elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == QUADSIZE-1:
884         tid.i = QUADSIZE-1
885         tid.j = 0
886     elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == 0:
887         tid.i = 0
888         tid.j = 0
889     else:
890         # something is wrong!
891         game.tholian.move(None)
892         prout("***Internal error: Tholian in a bad spot.")
893         return
894     # do nothing if we are blocked
895     if game.quad[tid.i][tid.j] not in ('.', '#'):
896         return
897     here = copy.copy(game.tholian.location)
898     delta = (tid - game.tholian.location).sgn()
899     # move in x axis
900     while here.i != tid.i:
901         here.i += delta.i
902         if game.quad[here.i][here.j] == '.':
903             game.tholian.move(here)
904     # move in y axis
905     while here.j != tid.j:
906         here.j += delta.j
907         if game.quad[here.i][here.j] == '.':
908             game.tholian.move(here)
909     # check to see if all holes plugged
910     for i in range(QUADSIZE):
911         if game.quad[0][i] != '#' and game.quad[0][i] != 'T':
912             return
913         if game.quad[QUADSIZE-1][i] != '#' and game.quad[QUADSIZE-1][i] != 'T':
914             return
915         if game.quad[i][0] != '#' and game.quad[i][0] != 'T':
916             return
917         if game.quad[i][QUADSIZE-1] != '#' and game.quad[i][QUADSIZE-1] != 'T':
918             return
919     # All plugged up -- Tholian splits
920     game.quad[game.tholian.location.i][game.tholian.location.j] = '#'
921     dropin(' ')
922     prout(crmena(True, 'T', "sector", game.tholian) + _(" completes web."))
923     game.tholian.move(None)
924     return
925
926 # Code from battle.c begins here
927
928 def cloak():
929     "Change cloaking-device status."
930     if game.ship == 'F':
931         prout(_("Ye Faerie Queene hath no cloaking device."));
932         return
933
934     key = scanner.nexttok()
935
936     if key == "IHREAL":
937         huh()
938         return
939
940     action = None
941     if key == "IHALPHA":
942         if scanner.sees("on"):
943             if game.iscloaked:
944                 prout(_("The cloaking device has already been switched on."))
945                 return
946             action = "CLON"
947         elif scanner.sees("off"):
948             if not game.iscloaked:
949                 prout(_("The cloaking device has already been switched off."))
950                 return
951             action = "CLOFF"
952         else:
953             huh()
954             return
955     else:
956         if not game.iscloaked:
957             proutn(_("Switch cloaking device on? "))
958             if not ja():
959                 return
960             action = "CLON"
961         else:
962             proutn(_("Switch cloaking device off? "))
963             if not ja():
964                 return
965             action = "CLOFF"
966     if action == None:
967         return;
968
969     if action == "CLOFF":
970         if game.irhere and game.state.date >= ALGERON and not game.isviolreported:
971             prout(_("Spock- \"Captain, the Treaty of Algeron is in effect.\n   Are you sure this is wise?\""))
972             if not ja():
973                 return;
974         prout("Engineer Scott- \"Aye, Sir.\"");
975         game.iscloaked = False;
976         if game.irhere and game.state.date >= ALGERON and not game.isviolreported:
977             prout(_("The Romulan ship discovers you are breaking the Treaty of Algeron!"))
978             game.ncviol += 1
979             game.isviolreported = True
980
981             #if (neutz and game.state.date >= ALGERON) finish(FCLOAK);
982             return;
983
984     if action == "CLON":
985         if damaged(DCLOAK):
986             prout(_("Engineer Scott- \"The cloaking device is damaged, Sir.\""))
987             return;
988
989         if game.condition == "docked":
990             prout(_("You cannot cloak while docked."))
991
992         if game.state.date >= ALGERON and not game.isviolreported:
993             prout(_("Spock- \"Captain, using the cloaking device is a violation"))
994             prout(_("  of the Treaty of Algeron. Considering the alternatives,"))
995             proutn(_("  are you sure this is wise? "))
996             if not ja():
997                 return
998         prout(_("Engineer Scott- \"Cloaking device has engaging, Sir...\""))
999         attack(True)
1000         prout(_("Engineer Scott- \"Cloaking device has engaged, Sir.\""))
1001         game.iscloaked = True
1002
1003         if game.irhere and game.state.date >= ALGERON and not game.isviolreported:
1004             prout(_("The Romulan ship discovers you are breaking the Treaty of Algeron!"))
1005             game.ncviol += 1
1006             game.isviolreported = True
1007
1008 def doshield(shraise):
1009     "Change shield status."
1010     action = "NONE"
1011     game.ididit = False
1012     if shraise:
1013         action = "SHUP"
1014     else:
1015         key = scanner.nexttok()
1016         if key == "IHALPHA":
1017             if scanner.sees("transfer"):
1018                 action = "NRG"
1019             else:
1020                 if damaged(DSHIELD):
1021                     prout(_("Shields damaged and down."))
1022                     return
1023                 if scanner.sees("up"):
1024                     action = "SHUP"
1025                 elif scanner.sees("down"):
1026                     action = "SHDN"
1027         if action == "NONE":
1028             proutn(_("Do you wish to change shield energy? "))
1029             if ja():
1030                 action = "NRG"
1031             elif damaged(DSHIELD):
1032                 prout(_("Shields damaged and down."))
1033                 return
1034             elif game.shldup:
1035                 proutn(_("Shields are up. Do you want them down? "))
1036                 if ja():
1037                     action = "SHDN"
1038                 else:
1039                     scanner.chew()
1040                     return
1041             else:
1042                 proutn(_("Shields are down. Do you want them up? "))
1043                 if ja():
1044                     action = "SHUP"
1045                 else:
1046                     scanner.chew()
1047                     return
1048     if action == "SHUP": # raise shields
1049         if game.shldup:
1050             prout(_("Shields already up."))
1051             return
1052         game.shldup = True
1053         game.shldchg = True
1054         if game.condition != "docked":
1055             game.energy -= 50.0
1056         prout(_("Shields raised."))
1057         if game.energy <= 0:
1058             skip(1)
1059             prout(_("Shields raising uses up last of energy."))
1060             finish(FNRG)
1061             return
1062         game.ididit = True
1063         return
1064     elif action == "SHDN":
1065         if not game.shldup:
1066             prout(_("Shields already down."))
1067             return
1068         game.shldup = False
1069         game.shldchg = True
1070         prout(_("Shields lowered."))
1071         game.ididit = True
1072         return
1073     elif action == "NRG":
1074         while scanner.nexttok() != "IHREAL":
1075             scanner.chew()
1076             proutn(_("Energy to transfer to shields- "))
1077         nrg = scanner.real
1078         scanner.chew()
1079         if nrg == 0:
1080             return
1081         if nrg > game.energy:
1082             prout(_("Insufficient ship energy."))
1083             return
1084         game.ididit = True
1085         if game.shield+nrg >= game.inshld:
1086             prout(_("Shield energy maximized."))
1087             if game.shield+nrg > game.inshld:
1088                 prout(_("Excess energy requested returned to ship energy"))
1089             game.energy -= game.inshld-game.shield
1090             game.shield = game.inshld
1091             return
1092         if nrg < 0.0 and game.energy-nrg > game.inenrg:
1093             # Prevent shield drain loophole
1094             skip(1)
1095             prout(_("Engineering to bridge--"))
1096             prout(_("  Scott here. Power circuit problem, Captain."))
1097             prout(_("  I can't drain the shields."))
1098             game.ididit = False
1099             return
1100         if game.shield+nrg < 0:
1101             prout(_("All shield energy transferred to ship."))
1102             game.energy += game.shield
1103             game.shield = 0.0
1104             return
1105         proutn(_("Scotty- \""))
1106         if nrg > 0:
1107             prout(_("Transferring energy to shields.\""))
1108         else:
1109             prout(_("Draining energy from shields.\""))
1110         game.shield += nrg
1111         game.energy -= nrg
1112         return
1113
1114 def randdevice():
1115     "Choose a device to damage, at random."
1116     weights = (
1117         105,       # DSRSENS: short range scanners         10.5%
1118         105,       # DLRSENS: long range scanners          10.5%
1119         120,       # DPHASER: phasers                      12.0%
1120         120,       # DPHOTON: photon torpedoes             12.0%
1121         25,        # DLIFSUP: life support                  2.5%
1122         65,        # DWARPEN: warp drive                    6.5%
1123         70,        # DIMPULS: impulse engines               6.5%
1124         135,       # DSHIELD: deflector shields            13.5%
1125         30,        # DRADIO:  subspace radio                3.0%
1126         45,        # DSHUTTL: shuttle                       4.5%
1127         15,        # DCOMPTR: computer                      1.5%
1128         20,        # NAVCOMP: navigation system             2.0%
1129         75,        # DTRANSP: transporter                   7.5%
1130         20,        # DSHCTRL: high-speed shield controller  2.0%
1131         10,        # DDRAY: death ray                       1.0%
1132         30,        # DDSP: deep-space probes                3.0%
1133         10,        # DCLOAK: the cloaking device            1.0
1134     )
1135     assert(sum(weights) == 1000)
1136     idx = randrange(1000)
1137     wsum = 0
1138     for (i, w) in enumerate(weights):
1139         wsum += w
1140         if idx < wsum:
1141             return i
1142     return None        # we should never get here
1143
1144 def collision(rammed, enemy):
1145     "Collision handling for rammong events."
1146     prouts(_("***RED ALERT!  RED ALERT!"))
1147     skip(1)
1148     prout(_("***COLLISION IMMINENT."))
1149     skip(2)
1150     proutn("***")
1151     proutn(crmshp())
1152     hardness = {'R':1.5, 'C':2.0, 'S':2.5, 'T':0.5, '?':4.0}.get(enemy.type, 1.0)
1153     if rammed:
1154         proutn(_(" rammed by "))
1155     else:
1156         proutn(_(" rams "))
1157     proutn(crmena(False, enemy.type, "sector", enemy.location))
1158     if rammed:
1159         proutn(_(" (original position)"))
1160     skip(1)
1161     deadkl(enemy.location, enemy.type, game.sector)
1162     proutn("***" + crmshp() + " heavily damaged.")
1163     icas = randrange(10, 30)
1164     prout(_("***Sickbay reports %d casualties") % icas)
1165     game.casual += icas
1166     game.state.crew -= icas
1167     # In the pre-SST2K version, all devices got equiprobably damaged,
1168     # which was silly.  Instead, pick up to half the devices at
1169     # random according to our weighting table,
1170     ncrits = randrange(NDEVICES/2)
1171     while ncrits > 0:
1172         ncrits -= 1
1173         dev = randdevice()
1174         if game.damage[dev] < 0:
1175             continue
1176         extradm = (10.0*hardness*randreal()+1.0)*game.damfac
1177         # Damage for at least time of travel!
1178         game.damage[dev] += game.optime + extradm
1179     game.shldup = False
1180     prout(_("***Shields are down."))
1181     if game.state.remkl + len(game.state.kcmdr) + game.state.nscrem:
1182         announce()
1183         damagereport()
1184     else:
1185         finish(FWON)
1186     return
1187
1188 def torpedo(origin, bearing, dispersion, number, nburst):
1189     "Let a photon torpedo fly"
1190     if not damaged(DSRSENS) or game.condition == "docked":
1191         setwnd(srscan_window)
1192     else:
1193         setwnd(message_window)
1194     ac = bearing + 0.25*dispersion        # dispersion is a random variable
1195     bullseye = (15.0 - bearing)*0.5235988
1196     track = course(bearing=ac, distance=QUADSIZE, origin=cartesian(origin))
1197     bumpto = Coord(0, 0)
1198     # Loop to move a single torpedo
1199     setwnd(message_window)
1200     for step in range(1, QUADSIZE*2):
1201         if not track.nexttok():
1202             break
1203         w = track.sector()
1204         if not w.valid_sector():
1205             break
1206         iquad = game.quad[w.i][w.j]
1207         tracktorpedo(w, step, number, nburst, iquad)
1208         if iquad == '.':
1209             continue
1210         # hit something
1211         setwnd(message_window)
1212         if not damaged(DSRSENS) or game.condition == "docked":
1213             skip(1)        # start new line after text track
1214         if iquad in ('E', 'F'): # Hit our ship
1215             skip(1)
1216             prout(_("Torpedo hits %s.") % crmshp())
1217             hit = 700.0 + randreal(100) - \
1218                 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1219             newcnd() # we're blown out of dock
1220             if game.landed or game.condition == "docked":
1221                 return hit # Cheat if on a planet
1222             # In the C/FORTRAN version, dispersion was 2.5 radians, which
1223             # is 143 degrees, which is almost exactly 4.8 clockface units
1224             displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
1225             displacement.nexttok()
1226             bumpto = displacement.sector()
1227             if not bumpto.valid_sector():
1228                 return hit
1229             if game.quad[bumpto.i][bumpto.j] == ' ':
1230                 finish(FHOLE)
1231                 return hit
1232             if game.quad[bumpto.i][bumpto.j] != '.':
1233                 # can't move into object
1234                 return hit
1235             game.sector = bumpto
1236             proutn(crmshp())
1237             game.quad[w.i][w.j] = '.'
1238             game.quad[bumpto.i][bumpto.j] = iquad
1239             prout(_(" displaced by blast to Sector %s ") % bumpto)
1240             for enemy in game.enemies:
1241                 enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
1242             sortenemies()
1243             return None
1244         elif iquad in ('C', 'S', 'R', 'K'): # Hit a regular enemy
1245             # find the enemy
1246             if iquad in ('C', 'S') and withprob(0.05):
1247                 prout(crmena(True, iquad, "sector", w) + _(" uses anti-photon device;"))
1248                 prout(_("   torpedo neutralized."))
1249                 return None
1250             for enemy in game.enemies:
1251                 if w == enemy.location:
1252                     kp = math.fabs(enemy.power)
1253                     h1 = 700.0 + randrange(100) - \
1254                         1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1255                     h1 = math.fabs(h1)
1256                     if kp < h1:
1257                         h1 = kp
1258                     if enemy.power < 0:
1259                         enemy.power -= -h1
1260                     else:
1261                         enemy.power -= h1
1262                     if enemy.power == 0:
1263                         deadkl(w, iquad, w)
1264                         return None
1265                     proutn(crmena(True, iquad, "sector", w))
1266                     displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
1267                     displacement.nexttok()
1268                     bumpto = displacement.sector()
1269                     if not bumpto.valid_sector():
1270                         prout(_(" damaged but not destroyed."))
1271                         return
1272                     if game.quad[bumpto.i][bumpto.j] == ' ':
1273                         prout(_(" buffeted into black hole."))
1274                         deadkl(w, iquad, bumpto)
1275                     if game.quad[bumpto.i][bumpto.j] != '.':
1276                         prout(_(" damaged but not destroyed."))
1277                     else:
1278                         prout(_(" damaged-- displaced by blast to Sector %s ")%bumpto)
1279                         enemy.location = bumpto
1280                         game.quad[w.i][w.j] = '.'
1281                         game.quad[bumpto.i][bumpto.j] = iquad
1282                         for enemy in game.enemies:
1283                             enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
1284                         sortenemies()
1285                     break
1286             else:
1287                 prout("Internal error, no enemy where expected!")
1288                 raise SystemExit(1)
1289             return None
1290         elif iquad == 'B': # Hit a base
1291             skip(1)
1292             prout(_("***STARBASE DESTROYED.."))
1293             game.state.baseq = [x for x in game.state.baseq if x != game.quadrant]
1294             game.quad[w.i][w.j] = '.'
1295             game.base.invalidate()
1296             game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
1297             game.state.chart[game.quadrant.i][game.quadrant.j].starbase = False
1298             game.state.basekl += 1
1299             newcnd()
1300             return None
1301         elif iquad == 'P': # Hit a planet
1302             prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
1303             game.state.nplankl += 1
1304             game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
1305             game.iplnet.pclass = "destroyed"
1306             game.iplnet = None
1307             game.plnet.invalidate()
1308             game.quad[w.i][w.j] = '.'
1309             if game.landed:
1310                 # captain perishes on planet
1311                 finish(FDPLANET)
1312             return None
1313         elif iquad == '@': # Hit an inhabited world -- very bad!
1314             prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
1315             game.state.nworldkl += 1
1316             game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
1317             game.iplnet.pclass = "destroyed"
1318             game.iplnet = None
1319             game.plnet.invalidate()
1320             game.quad[w.i][w.j] = '.'
1321             if game.landed:
1322                 # captain perishes on planet
1323                 finish(FDPLANET)
1324             prout(_("The torpedo destroyed an inhabited planet."))
1325             return None
1326         elif iquad == '*': # Hit a star
1327             if withprob(0.9):
1328                 nova(w)
1329             else:
1330                 prout(crmena(True, '*', "sector", w) + _(" unaffected by photon blast."))
1331             return None
1332         elif iquad == '?': # Hit a thingy
1333             if not (game.options & OPTION_THINGY) or withprob(0.3):
1334                 skip(1)
1335                 prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
1336                 skip(1)
1337                 prouts(_("    HACK!     HACK!    HACK!        *CHOKE!*  "))
1338                 skip(1)
1339                 proutn(_("Mr. Spock-"))
1340                 prouts(_("  \"Fascinating!\""))
1341                 skip(1)
1342                 deadkl(w, iquad, w)
1343             else:
1344                 # Stas Sergeev added the possibility that
1345                 # you can shove the Thingy and piss it off.
1346                 # It then becomes an enemy and may fire at you.
1347                 thing.angry()
1348             return None
1349         elif iquad == ' ': # Black hole
1350             skip(1)
1351             prout(crmena(True, ' ', "sector", w) + _(" swallows torpedo."))
1352             return None
1353         elif iquad == '#': # hit the web
1354             skip(1)
1355             prout(_("***Torpedo absorbed by Tholian web."))
1356             return None
1357         elif iquad == 'T':  # Hit a Tholian
1358             h1 = 700.0 + randrange(100) - \
1359                 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1360             h1 = math.fabs(h1)
1361             if h1 >= 600:
1362                 game.quad[w.i][w.j] = '.'
1363                 deadkl(w, iquad, w)
1364                 game.tholian = None
1365                 return None
1366             skip(1)
1367             proutn(crmena(True, 'T', "sector", w))
1368             if withprob(0.05):
1369                 prout(_(" survives photon blast."))
1370                 return None
1371             prout(_(" disappears."))
1372             game.tholian.move(None)
1373             game.quad[w.i][w.j] = '#'
1374             dropin(' ')
1375             return None
1376         else: # Problem!
1377             skip(1)
1378             proutn("Don't know how to handle torpedo collision with ")
1379             proutn(crmena(True, iquad, "sector", w))
1380             skip(1)
1381             return None
1382         break
1383     skip(1)
1384     setwnd(message_window)
1385     prout(_("Torpedo missed."))
1386     return None
1387
1388 def fry(hit):
1389     "Critical-hit resolution."
1390     if hit < (275.0-25.0*game.skill)*randreal(1.0, 1.5):
1391         return
1392     ncrit = int(1.0 + hit/(500.0+randreal(100)))
1393     proutn(_("***CRITICAL HIT--"))
1394     # Select devices and cause damage
1395     cdam = []
1396     while ncrit > 0:
1397         while True:
1398             j = randdevice()
1399             # Cheat to prevent shuttle damage unless on ship
1400             if not (game.damage[j]<0.0 or (j == DSHUTTL and game.iscraft != "onship") or (j == DCLOAK and game.ship != 'E' or j == DDRAY)):
1401                 break
1402         cdam.append(j)
1403         extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
1404         game.damage[j] += extradm
1405         ncrit -= 1
1406     skipcount = 0
1407     for (i, j) in enumerate(cdam):
1408         proutn(device[j])
1409         if skipcount % 3 == 2 and i < len(cdam)-1:
1410             skip(1)
1411         skipcount += 1
1412         if i < len(cdam)-1:
1413             proutn(_(" and "))
1414     prout(_(" damaged."))
1415     if damaged(DSHIELD) and game.shldup:
1416         prout(_("***Shields knocked down."))
1417         game.shldup = False
1418     if damaged(DCLOAK) and game.iscloaked:
1419         prout(_("***Cloaking device rendered inoperative."))
1420         game.iscloaked = False
1421
1422 def attack(torps_ok):
1423     # bad guy attacks us
1424     # torps_ok == False forces use of phasers in an attack
1425     if game.iscloaked:
1426         return
1427     # game could be over at this point, check
1428     if game.alldone:
1429         return
1430     attempt = False
1431     ihurt = False
1432     hitmax = 0.0
1433     hittot = 0.0
1434     chgfac = 1.0
1435     where = "neither"
1436     if game.idebug:
1437         prout("=== ATTACK!")
1438     # Tholian gets to move before attacking
1439     if game.tholian:
1440         movetholian()
1441     # if you have just entered the RNZ, you'll get a warning
1442     if game.neutz: # The one chance not to be attacked
1443         game.neutz = False
1444         return
1445     # commanders get a chance to tac-move towards you
1446     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:
1447         for (bugout, enemy, old, goto) in  moveklings():
1448             if bugout:
1449                 # we know about this if either short or long range
1450                 # sensors are working
1451                 if damaged(DSRSENS) and damaged(DLRSENS) \
1452                        and game.condition != "docked":
1453                     prout(crmena(True, enemy.type, "sector", old) + \
1454                           (_(" escapes to Quadrant %s (and regains strength).") % goto))
1455             else: # Enemy still in-sector
1456                 if enemy.move(goto):
1457                     if not damaged(DSRSENS) or game.condition == "docked":
1458                         proutn(_("*** %s from Sector %s") % (cramen(enemy.type), enemy.location))
1459                         if enemy.kdist < old:
1460                             proutn(_(" advances to "))
1461                         else:
1462                             proutn(_(" retreats to "))
1463                         prout("Sector %s." % goto)
1464         sortenemies()
1465     # if no enemies remain after movement, we're done
1466     if len(game.enemies) == 0 or (len(game.enemies) == 1 and thing.at(game.quadrant) and not thing.angered):
1467         return
1468     # set up partial hits if attack happens during shield status change
1469     pfac = 1.0/game.inshld
1470     if game.shldchg:
1471         chgfac = 0.25 + randreal(0.5)
1472     skip(1)
1473     # message verbosity control
1474     if game.skill <= SKILL_FAIR:
1475         where = "sector"
1476     for enemy in game.enemies:
1477         if enemy.power < 0:
1478             continue        # too weak to attack
1479         # compute hit strength and diminish shield power
1480         r = randreal()
1481         # Increase chance of photon torpedos if docked or enemy energy is low
1482         if game.condition == "docked":
1483             r *= 0.25
1484         if enemy.power < 500:
1485             r *= 0.25
1486         if enemy.type == 'T' or (enemy.type == '?' and not thing.angered):
1487             continue
1488         # different enemies have different probabilities of throwing a torp
1489         usephasers = not torps_ok or \
1490             (enemy.type == 'K' and r > 0.0005) or \
1491             (enemy.type == 'C' and r > 0.015) or \
1492             (enemy.type == 'R' and r > 0.3) or \
1493             (enemy.type == 'S' and r > 0.07) or \
1494             (enemy.type == '?' and r > 0.05)
1495         if usephasers:            # Enemy uses phasers
1496             if game.condition == "docked":
1497                 continue # Don't waste the effort!
1498             attempt = True # Attempt to attack
1499             dustfac = randreal(0.8, 0.85)
1500             hit = enemy.power*math.pow(dustfac, enemy.kavgd)
1501             enemy.power *= 0.75
1502         else: # Enemy uses photon torpedo
1503             # We should be able to make the bearing() method work here
1504             pcourse = 1.90985*math.atan2(game.sector.j-enemy.location.j, enemy.location.i-game.sector.i)
1505             hit = 0
1506             proutn(_("***TORPEDO INCOMING"))
1507             if not damaged(DSRSENS):
1508                 proutn(_(" From ") + crmena(False, enemy.type, where, enemy.location))
1509             attempt = True
1510             prout("  ")
1511             dispersion = (randreal()+randreal())*0.5 - 0.5
1512             dispersion += 0.002*enemy.power*dispersion
1513             hit = torpedo(enemy.location, pcourse, dispersion, number=1, nburst=1)
1514             if game.unwon() == 0:
1515                 finish(FWON) # Klingons did themselves in!
1516             if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova or game.alldone:
1517                 return # Supernova or finished
1518             if hit is None:
1519                 continue
1520         # incoming phaser or torpedo, shields may dissipate it
1521         if game.shldup or game.shldchg or game.condition == "docked":
1522             # shields will take hits
1523             propor = pfac * game.shield
1524             if game.condition == "docked":
1525                 propor *= 2.1
1526             if propor < 0.1:
1527                 propor = 0.1
1528             hitsh = propor*chgfac*hit+1.0
1529             absorb = 0.8*hitsh
1530             if absorb > game.shield:
1531                 absorb = game.shield
1532             game.shield -= absorb
1533             hit -= hitsh
1534             # taking a hit blasts us out of a starbase dock
1535             if game.condition == "docked":
1536                 dock(False)
1537             # but the shields may take care of it
1538             if propor > 0.1 and hit < 0.005*game.energy:
1539                 continue
1540         # hit from this opponent got through shields, so take damage
1541         ihurt = True
1542         proutn(_("%d unit hit") % int(hit))
1543         if (damaged(DSRSENS) and usephasers) or game.skill<=SKILL_FAIR:
1544             proutn(_(" on the ") + crmshp())
1545         if not damaged(DSRSENS) and usephasers:
1546             prout(_(" from ") + crmena(False, enemy.type, where, enemy.location))
1547         skip(1)
1548         # Decide if hit is critical
1549         if hit > hitmax:
1550             hitmax = hit
1551         hittot += hit
1552         fry(hit)
1553         game.energy -= hit
1554     if game.energy <= 0:
1555         # Returning home upon your shield, not with it...
1556         finish(FBATTLE)
1557         return
1558     if not attempt and game.condition == "docked":
1559         prout(_("***Enemies decide against attacking your ship."))
1560     percent = 100.0*pfac*game.shield+0.5
1561     if not ihurt:
1562         # Shields fully protect ship
1563         proutn(_("Enemy attack reduces shield strength to "))
1564     else:
1565         # Emit message if starship suffered hit(s)
1566         skip(1)
1567         proutn(_("Energy left %2d    shields ") % int(game.energy))
1568         if game.shldup:
1569             proutn(_("up "))
1570         elif not damaged(DSHIELD):
1571             proutn(_("down "))
1572         else:
1573             proutn(_("damaged, "))
1574     prout(_("%d%%,   torpedoes left %d") % (percent, game.torps))
1575     # Check if anyone was hurt
1576     if hitmax >= 200 or hittot >= 500:
1577         icas = randrange(int(hittot * 0.015))
1578         if icas >= 2:
1579             skip(1)
1580             prout(_("Mc Coy-  \"Sickbay to bridge.  We suffered %d casualties") % icas)
1581             prout(_("   in that last attack.\""))
1582             game.casual += icas
1583             game.state.crew -= icas
1584     # After attack, reset average distance to enemies
1585     for enemy in game.enemies:
1586         enemy.kavgd = enemy.kdist
1587     sortenemies()
1588     return
1589
1590 def deadkl(w, etype, mv):
1591     "Kill a Klingon, Tholian, Romulan, or Thingy."
1592     # Added mv to allow enemy to "move" before dying
1593     proutn(crmena(True, etype, "sector", mv))
1594     # Decide what kind of enemy it is and update appropriately
1595     if etype == 'R':
1596         # Chalk up a Romulan
1597         game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans -= 1
1598         game.irhere -= 1
1599         game.state.nromrem -= 1
1600     elif etype == 'T':
1601         # Killed a Tholian
1602         game.tholian = None
1603     elif etype == '?':
1604         # Killed a Thingy
1605         global thing
1606         thing = None
1607     else:
1608         # Killed some type of Klingon
1609         game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
1610         game.klhere -= 1
1611         if etype == 'C':
1612             game.state.kcmdr.remove(game.quadrant)
1613             unschedule(FTBEAM)
1614             if game.state.kcmdr:
1615                 schedule(FTBEAM, expran(1.0*game.incom/len(game.state.kcmdr)))
1616             if is_scheduled(FCDBAS) and game.battle == game.quadrant:
1617                 unschedule(FCDBAS)
1618         elif etype ==  'K':
1619             game.state.remkl -= 1
1620         elif etype ==  'S':
1621             game.state.nscrem -= 1
1622             game.state.kscmdr.invalidate()
1623             game.isatb = 0
1624             game.iscate = False
1625             unschedule(FSCMOVE)
1626             unschedule(FSCDBAS)
1627     # For each kind of enemy, finish message to player
1628     prout(_(" destroyed."))
1629     if game.unwon() == 0:
1630         return
1631     game.recompute()
1632     # Remove enemy ship from arrays describing local conditions
1633     for e in game.enemies:
1634         if e.location == w:
1635             e.move(None)
1636             break
1637     return
1638
1639 def targetcheck(w):
1640     "Return None if target is invalid, otherwise return a course angle."
1641     if not w.valid_sector():
1642         huh()
1643         return None
1644     delta = Coord()
1645     # C code this was translated from is wacky -- why the sign reversal?
1646     delta.j = (w.j - game.sector.j)
1647     delta.i = (game.sector.i - w.i)
1648     if delta == Coord(0, 0):
1649         skip(1)
1650         prout(_("Spock-  \"Bridge to sickbay.  Dr. McCoy,"))
1651         prout(_("  I recommend an immediate review of"))
1652         prout(_("  the Captain's psychological profile.\""))
1653         scanner.chew()
1654         return None
1655     return delta.bearing()
1656
1657 def torps():
1658     "Launch photon torpedo salvo."
1659     tcourse = []
1660     game.ididit = False
1661     if damaged(DPHOTON):
1662         prout(_("Photon tubes damaged."))
1663         scanner.chew()
1664         return
1665     if game.torps == 0:
1666         prout(_("No torpedoes left."))
1667         scanner.chew()
1668         return
1669     # First, get torpedo count
1670     while True:
1671         scanner.nexttok()
1672         if scanner.token == "IHALPHA":
1673             huh()
1674             return
1675         elif scanner.token == "IHEOL" or not scanner.waiting():
1676             prout(_("%d torpedoes left.") % game.torps)
1677             scanner.chew()
1678             proutn(_("Number of torpedoes to fire- "))
1679             continue        # Go back around to get a number
1680         else: # key == "IHREAL"
1681             n = scanner.int()
1682             if n <= 0: # abort command
1683                 scanner.chew()
1684                 return
1685             if n > MAXBURST:
1686                 scanner.chew()
1687                 prout(_("Maximum of %d torpedoes per burst.") % MAXBURST)
1688                 return
1689             if n > game.torps:
1690                 scanner.chew()        # User requested more torps than available
1691                 continue        # Go back around
1692             break        # All is good, go to next stage
1693     # Next, get targets
1694     target = []
1695     for i in range(n):
1696         key = scanner.nexttok()
1697         if i == 0 and key == "IHEOL":
1698             break        # no coordinate waiting, we will try prompting
1699         if i == 1 and key == "IHEOL":
1700             # direct all torpedoes at one target
1701             while i < n:
1702                 target.append(target[0])
1703                 tcourse.append(tcourse[0])
1704                 i += 1
1705             break
1706         scanner.push(scanner.token)
1707         target.append(scanner.getcoord())
1708         if target[-1] is None:
1709             return
1710         tcourse.append(targetcheck(target[-1]))
1711         if tcourse[-1] is None:
1712             return
1713     scanner.chew()
1714     if len(target) == 0:
1715         # prompt for each one
1716         for i in range(n):
1717             proutn(_("Target sector for torpedo number %d- ") % (i+1))
1718             scanner.chew()
1719             target.append(scanner.getcoord())
1720             if target[-1] is None:
1721                 return
1722             tcourse.append(targetcheck(target[-1]))
1723             if tcourse[-1] is None:
1724                 return
1725     game.ididit = True
1726     # Loop for moving <n> torpedoes
1727     for i in range(n):
1728         if game.condition != "docked":
1729             game.torps -= 1
1730         dispersion = (randreal()+randreal())*0.5 -0.5
1731         if math.fabs(dispersion) >= 0.47:
1732             # misfire!
1733             dispersion *= randreal(1.2, 2.2)
1734             if n > 0:
1735                 prouts(_("***TORPEDO NUMBER %d MISFIRES") % (i+1))
1736             else:
1737                 prouts(_("***TORPEDO MISFIRES."))
1738             skip(1)
1739             if i < n:
1740                 prout(_("  Remainder of burst aborted."))
1741             if withprob(0.2):
1742                 prout(_("***Photon tubes damaged by misfire."))
1743                 game.damage[DPHOTON] = game.damfac * randreal(1.0, 3.0)
1744             break
1745         if game.iscloaked:
1746             dispersion *= 1.2
1747         elif game.shldup or game.condition == "docked":
1748             dispersion *= 1.0 + 0.0001*game.shield
1749         torpedo(game.sector, tcourse[i], dispersion, number=i, nburst=n)
1750         if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
1751             return
1752     if game.unwon()<=0:
1753         finish(FWON)
1754
1755 def overheat(rpow):
1756     "Check for phasers overheating."
1757     if rpow > 1500:
1758         checkburn = (rpow-1500.0)*0.00038
1759         if withprob(checkburn):
1760             prout(_("Weapons officer Sulu-  \"Phasers overheated, sir.\""))
1761             game.damage[DPHASER] = game.damfac* randreal(1.0, 2.0) * (1.0+checkburn)
1762
1763 def checkshctrl(rpow):
1764     "Check shield control."
1765     skip(1)
1766     if withprob(0.998):
1767         prout(_("Shields lowered."))
1768         return False
1769     # Something bad has happened
1770     prouts(_("***RED ALERT!  RED ALERT!"))
1771     skip(2)
1772     hit = rpow*game.shield/game.inshld
1773     game.energy -= rpow+hit*0.8
1774     game.shield -= hit*0.2
1775     if game.energy <= 0.0:
1776         prouts(_("Sulu-  \"Captain! Shield malf***********************\""))
1777         skip(1)
1778         stars()
1779         finish(FPHASER)
1780         return True
1781     prouts(_("Sulu-  \"Captain! Shield malfunction! Phaser fire contained!\""))
1782     skip(2)
1783     prout(_("Lt. Uhura-  \"Sir, all decks reporting damage.\""))
1784     icas = randrange(int(hit*0.012))
1785     skip(1)
1786     fry(0.8*hit)
1787     if icas:
1788         skip(1)
1789         prout(_("McCoy to bridge- \"Severe radiation burns, Jim."))
1790         prout(_("  %d casualties so far.\"") % icas)
1791         game.casual += icas
1792         game.state.crew -= icas
1793     skip(1)
1794     prout(_("Phaser energy dispersed by shields."))
1795     prout(_("Enemy unaffected."))
1796     overheat(rpow)
1797     return True
1798
1799 def hittem(hits):
1800     "Register a phaser hit on Klingons and Romulans."
1801     w = Coord()
1802     skip(1)
1803     kk = 0
1804     for wham in hits:
1805         if wham == 0:
1806             continue
1807         dustfac = randreal(0.9, 1.0)
1808         hit = wham*math.pow(dustfac, game.enemies[kk].kdist)
1809         kpini = game.enemies[kk].power
1810         kp = math.fabs(kpini)
1811         if PHASEFAC*hit < kp:
1812             kp = PHASEFAC*hit
1813         if game.enemies[kk].power < 0:
1814             game.enemies[kk].power -= -kp
1815         else:
1816             game.enemies[kk].power -= kp
1817         kpow = game.enemies[kk].power
1818         w = game.enemies[kk].location
1819         if hit > 0.005:
1820             if not damaged(DSRSENS):
1821                 boom(w)
1822             proutn(_("%d unit hit on ") % int(hit))
1823         else:
1824             proutn(_("Very small hit on "))
1825         ienm = game.quad[w.i][w.j]
1826         if ienm == '?':
1827             thing.angry()
1828         proutn(crmena(False, ienm, "sector", w))
1829         skip(1)
1830         if kpow == 0:
1831             deadkl(w, ienm, w)
1832             if game.unwon()==0:
1833                 finish(FWON)
1834             if game.alldone:
1835                 return
1836             continue
1837         else: # decide whether or not to emasculate klingon
1838             if kpow > 0 and withprob(0.9) and kpow <= randreal(0.4, 0.8)*kpini:
1839                 prout(_("***Mr. Spock-  \"Captain, the vessel at Sector %s")%w)
1840                 prout(_("   has just lost its firepower.\""))
1841                 game.enemies[kk].power = -kpow
1842         kk += 1
1843     return
1844
1845 def phasers():
1846     "Fire phasers at bad guys."
1847     hits = []
1848     kz = 0
1849     k = 1
1850     irec = 0 # Cheating inhibitor
1851     ifast = False
1852     no = False
1853     itarg = True
1854     msgflag = True
1855     rpow = 0.0
1856     automode = "NOTSET"
1857     key = ""
1858     skip(1)
1859     # SR sensors and Computer are needed for automode
1860     if damaged(DSRSENS) or damaged(DCOMPTR):
1861         itarg = False
1862     if game.condition == "docked":
1863         prout(_("Phasers can't be fired through base shields."))
1864         scanner.chew()
1865         return
1866     if damaged(DPHASER):
1867         prout(_("Phaser control damaged."))
1868         scanner.chew()
1869         return
1870     if game.shldup:
1871         if damaged(DSHCTRL):
1872             prout(_("High speed shield control damaged."))
1873             scanner.chew()
1874             return
1875         if game.energy <= 200.0:
1876             prout(_("Insufficient energy to activate high-speed shield control."))
1877             scanner.chew()
1878             return
1879         prout(_("Weapons Officer Sulu-  \"High-speed shield control enabled, sir.\""))
1880         ifast = True
1881     # Original code so convoluted, I re-did it all
1882     # (That was Tom Almy talking about the C code, I think -- ESR)
1883     while automode == "NOTSET":
1884         key = scanner.nexttok()
1885         if key == "IHALPHA":
1886             if scanner.sees("manual"):
1887                 if len(game.enemies)==0:
1888                     prout(_("There is no enemy present to select."))
1889                     scanner.chew()
1890                     key = "IHEOL"
1891                     automode = "AUTOMATIC"
1892                 else:
1893                     automode = "MANUAL"
1894                     key = scanner.nexttok()
1895             elif scanner.sees("automatic"):
1896                 if (not itarg) and len(game.enemies) != 0:
1897                     automode = "FORCEMAN"
1898                 else:
1899                     if len(game.enemies)==0:
1900                         prout(_("Energy will be expended into space."))
1901                     automode = "AUTOMATIC"
1902                     key = scanner.nexttok()
1903             elif scanner.sees("no"):
1904                 no = True
1905             else:
1906                 huh()
1907                 return
1908         elif key == "IHREAL":
1909             if len(game.enemies)==0:
1910                 prout(_("Energy will be expended into space."))
1911                 automode = "AUTOMATIC"
1912             elif not itarg:
1913                 automode = "FORCEMAN"
1914             else:
1915                 automode = "AUTOMATIC"
1916         else:
1917             # "IHEOL"
1918             if len(game.enemies)==0:
1919                 prout(_("Energy will be expended into space."))
1920                 automode = "AUTOMATIC"
1921             elif not itarg:
1922                 automode = "FORCEMAN"
1923             else:
1924                 proutn(_("Manual or automatic? "))
1925                 scanner.chew()
1926     avail = game.energy
1927     if ifast:
1928         avail -= 200.0
1929     if automode == "AUTOMATIC":
1930         if key == "IHALPHA" and scanner.sees("no"):
1931             no = True
1932             key = scanner.nexttok()
1933         if key != "IHREAL" and len(game.enemies) != 0:
1934             prout(_("Phasers locked on target. Energy available: %.2f")%avail)
1935         irec = 0
1936         while True:
1937             scanner.chew()
1938             if not kz:
1939                 for i in range(len(game.enemies)):
1940                     irec += math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90, game.enemies[i].kdist))*randreal(1.01, 1.06) + 1.0
1941             kz = 1
1942             proutn(_("%d units required. ") % irec)
1943             scanner.chew()
1944             proutn(_("Units to fire= "))
1945             key = scanner.nexttok()
1946             if key != "IHREAL":
1947                 return
1948             rpow = scanner.real
1949             if rpow > avail:
1950                 proutn(_("Energy available= %.2f") % avail)
1951                 skip(1)
1952                 key = "IHEOL"
1953             if not rpow > avail:
1954                 break
1955         if rpow <= 0:
1956             # chicken out
1957             scanner.chew()
1958             return
1959         key = scanner.nexttok()
1960         if key == "IHALPHA" and scanner.sees("no"):
1961             no = True
1962         if ifast:
1963             game.energy -= 200 # Go and do it!
1964             if checkshctrl(rpow):
1965                 return
1966         scanner.chew()
1967         game.energy -= rpow
1968         extra = rpow
1969         if len(game.enemies):
1970             extra = 0.0
1971             powrem = rpow
1972             for i in range(len(game.enemies)):
1973                 hits.append(0.0)
1974                 if powrem <= 0:
1975                     continue
1976                 hits[i] = math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90, game.enemies[i].kdist))
1977                 over = randreal(1.01, 1.06) * hits[i]
1978                 temp = powrem
1979                 powrem -= hits[i] + over
1980                 if powrem <= 0 and temp < hits[i]:
1981                     hits[i] = temp
1982                 if powrem <= 0:
1983                     over = 0.0
1984                 extra += over
1985             if powrem > 0.0:
1986                 extra += powrem
1987             hittem(hits)
1988             game.ididit = True
1989         if extra > 0 and not game.alldone:
1990             if game.tholian:
1991                 proutn(_("*** Tholian web absorbs "))
1992                 if len(game.enemies)>0:
1993                     proutn(_("excess "))
1994                 prout(_("phaser energy."))
1995             else:
1996                 prout(_("%d expended on empty space.") % int(extra))
1997     elif automode == "FORCEMAN":
1998         scanner.chew()
1999         key = "IHEOL"
2000         if damaged(DCOMPTR):
2001             prout(_("Battle computer damaged, manual fire only."))
2002         else:
2003             skip(1)
2004             prouts(_("---WORKING---"))
2005             skip(1)
2006             prout(_("Short-range-sensors-damaged"))
2007             prout(_("Insufficient-data-for-automatic-phaser-fire"))
2008             prout(_("Manual-fire-must-be-used"))
2009             skip(1)
2010     elif automode == "MANUAL":
2011         rpow = 0.0
2012         for k in range(len(game.enemies)):
2013             aim = game.enemies[k].location
2014             ienm = game.quad[aim.i][aim.j]
2015             if msgflag:
2016                 proutn(_("Energy available= %.2f") % (avail-0.006))
2017                 skip(1)
2018                 msgflag = False
2019                 rpow = 0.0
2020             if damaged(DSRSENS) and \
2021                not game.sector.distance(aim)<2**0.5 and ienm in ('C', 'S'):
2022                 prout(cramen(ienm) + _(" can't be located without short range scan."))
2023                 scanner.chew()
2024                 key = "IHEOL"
2025                 hits[k] = 0 # prevent overflow -- thanks to Alexei Voitenko
2026                 k += 1
2027                 continue
2028             if key == "IHEOL":
2029                 scanner.chew()
2030                 if itarg and k > kz:
2031                     irec = (abs(game.enemies[k].power)/(PHASEFAC*math.pow(0.9, game.enemies[k].kdist))) *        randreal(1.01, 1.06) + 1.0
2032                 kz = k
2033                 proutn("(")
2034                 if not damaged(DCOMPTR):
2035                     proutn("%d" % irec)
2036                 else:
2037                     proutn("??")
2038                 proutn(")  ")
2039                 proutn(_("units to fire at %s-  ") % crmena(False, ienm, "sector", aim))
2040                 key = scanner.nexttok()
2041             if key == "IHALPHA" and scanner.sees("no"):
2042                 no = True
2043                 key = scanner.nexttok()
2044                 continue
2045             if key == "IHALPHA":
2046                 huh()
2047                 return
2048             if key == "IHEOL":
2049                 if k == 1: # Let me say I'm baffled by this
2050                     msgflag = True
2051                 continue
2052             if scanner.real < 0:
2053                 # abort out
2054                 scanner.chew()
2055                 return
2056             hits[k] = scanner.real
2057             rpow += scanner.real
2058             # If total requested is too much, inform and start over
2059             if rpow > avail:
2060                 prout(_("Available energy exceeded -- try again."))
2061                 scanner.chew()
2062                 return
2063             key = scanner.nexttok() # scan for next value
2064             k += 1
2065         if rpow == 0.0:
2066             # zero energy -- abort
2067             scanner.chew()
2068             return
2069         if key == "IHALPHA" and scanner.sees("no"):
2070             no = True
2071         game.energy -= rpow
2072         scanner.chew()
2073         if ifast:
2074             game.energy -= 200.0
2075             if checkshctrl(rpow):
2076                 return
2077         hittem(hits)
2078         game.ididit = True
2079      # Say shield raised or malfunction, if necessary
2080     if game.alldone:
2081         return
2082     if ifast:
2083         skip(1)
2084         if no == 0:
2085             if withprob(0.01):
2086                 prout(_("Sulu-  \"Sir, the high-speed shield control has malfunctioned . . ."))
2087                 prouts(_("         CLICK   CLICK   POP  . . ."))
2088                 prout(_(" No response, sir!"))
2089                 game.shldup = False
2090             else:
2091                 prout(_("Shields raised."))
2092         else:
2093             game.shldup = False
2094     overheat(rpow)
2095
2096
2097 def capture():
2098     game.ididit = False # Nothing if we fail
2099     game.optime = 0.0;
2100
2101     # Make sure there is room in the brig */
2102     if game.brigfree == 0:
2103         prout(_("Security reports the brig is already full."))
2104         return;
2105
2106     if damaged(DRADIO):
2107         prout(_("Uhura- \"We have no subspace radio communication, sir.\""))
2108         return
2109
2110     if damaged(DTRANSP):
2111         prout(_("Scotty- \"Transporter damaged, sir.\""))
2112         return
2113
2114     # find out if there are any at all
2115     if game.klhere < 1:
2116         prout(_("Uhura- \"Getting no response, sir.\""))
2117         return
2118
2119     # if there is more than one Klingon, find out which one */
2120     #   Cruddy, just takes one at random.  Should ask the captain.
2121     #   Nah, just select the weakest one since it is most likely to
2122     #   surrender (Tom Almy mod)
2123     klingons = [e for e in game.enemies if e.type == 'K']
2124     weakest = sorted(klingons, key=lambda e: e.power)[0]
2125     game.optime = 0.05          # This action will take some time
2126     game.ididit = True #  So any others can strike back
2127
2128     # check out that Klingon
2129     # The algorithm isn't that great and could use some more
2130     # intelligent design
2131     # x = 300 + 25*skill;
2132     x = game.energy / (weakest.power * len(klingons))
2133     #prout(_("Stats: energy = %s, kpower = %s, klingons = %s")
2134     #      % (game.energy, weakest.power, len(klingons))) 
2135     x *= 2.5  # would originally have been equivalent of 1.4,
2136                # but we want command to work more often, more humanely */
2137     #prout(_("Prob = %.4f" % x))
2138     #   x = 100; // For testing, of course!
2139     if x < randreal(100):
2140         # guess what, he surrendered!!! */
2141         prout(_("Klingon captain at %s surrenders.") % weakest.location)
2142         i = randreal(200)
2143         if i > 0:
2144             prout(_("%d Klingons commit suicide rather than be taken captive.") % (200 - i))
2145         if i > game.brigfree:
2146             prout(_("%d Klingons die because there is no room for them in the brig.") % (i-brigfree))
2147             i = game.brigfree
2148         game.brigfree -= i
2149         prout(_("%d captives taken") % i)
2150         deadkl(weakest.location, weakest.type, game.sector)
2151         if game.unwon()<=0:
2152             finish(FWON)
2153         return
2154
2155         # big surprise, he refuses to surrender */
2156     prout(_("Fat chance, captain!"))
2157
2158 # Code from events.c begins here.
2159
2160 # This isn't a real event queue a la BSD Trek yet -- you can only have one
2161 # event of each type active at any given time.  Mostly these means we can
2162 # only have one FDISTR/FENSLV/FREPRO sequence going at any given time
2163 # BSD Trek, from which we swiped the idea, can have up to 5.
2164
2165 def unschedule(evtype):
2166     "Remove an event from the schedule."
2167     game.future[evtype].date = FOREVER
2168     return game.future[evtype]
2169
2170 def is_scheduled(evtype):
2171     "Is an event of specified type scheduled."
2172     return game.future[evtype].date != FOREVER
2173
2174 def scheduled(evtype):
2175     "When will this event happen?"
2176     return game.future[evtype].date
2177
2178 def schedule(evtype, offset):
2179     "Schedule an event of specified type."
2180     game.future[evtype].date = game.state.date + offset
2181     return game.future[evtype]
2182
2183 def postpone(evtype, offset):
2184     "Postpone a scheduled event."
2185     game.future[evtype].date += offset
2186
2187 def cancelrest():
2188     "Rest period is interrupted by event."
2189     if game.resting:
2190         skip(1)
2191         proutn(_("Mr. Spock-  \"Captain, shall we cancel the rest period?\""))
2192         if ja():
2193             game.resting = False
2194             game.optime = 0.0
2195             return True
2196     return False
2197
2198 def events():
2199     "Run through the event queue looking for things to do."
2200     i = 0
2201     fintim = game.state.date + game.optime
2202     yank = 0
2203     ictbeam = False
2204     istract = False
2205     w = Coord()
2206     hold = Coord()
2207     ev = Event()
2208     ev2 = Event()
2209
2210     def tractorbeam(yank):
2211         "Tractor-beaming cases merge here."
2212         announce()
2213         game.optime = (10.0/(7.5*7.5))*yank # 7.5 is yank rate (warp 7.5)
2214         skip(1)
2215         prout("***" + crmshp() + _(" caught in long range tractor beam--"))
2216         # If Kirk & Co. screwing around on planet, handle
2217         atover(True) # atover(true) is Grab
2218         if game.alldone:
2219             return
2220         if game.icraft: # Caught in Galileo?
2221             finish(FSTRACTOR)
2222             return
2223         # Check to see if shuttle is aboard
2224         if game.iscraft == "offship":
2225             skip(1)
2226             if withprob(0.5):
2227                 prout(_("Galileo, left on the planet surface, is captured"))
2228                 prout(_("by aliens and made into a flying McDonald's."))
2229                 game.damage[DSHUTTL] = -10
2230                 game.iscraft = "removed"
2231             else:
2232                 prout(_("Galileo, left on the planet surface, is well hidden."))
2233         if evcode == FSPY:
2234             game.quadrant = game.state.kscmdr
2235         else:
2236             game.quadrant = game.state.kcmdr[i]
2237         game.sector = randplace(QUADSIZE)
2238         prout(crmshp() + _(" is pulled to Quadrant %s, Sector %s") \
2239                % (game.quadrant, game.sector))
2240         if game.resting:
2241             prout(_("(Remainder of rest/repair period cancelled.)"))
2242             game.resting = False
2243         if not game.shldup:
2244             if not damaged(DSHIELD) and game.shield > 0:
2245                 doshield(shraise=True) # raise shields
2246                 game.shldchg = False
2247             else:
2248                 prout(_("(Shields not currently useable.)"))
2249         newqad()
2250         # Adjust finish time to time of tractor beaming?
2251         # fintim = game.state.date+game.optime
2252         attack(torps_ok=False)
2253         if not game.state.kcmdr:
2254             unschedule(FTBEAM)
2255         else:
2256             schedule(FTBEAM, game.optime+expran(1.5*game.intime/len(game.state.kcmdr)))
2257
2258     def destroybase():
2259         "Code merges here for any commander destroying a starbase."
2260         # Not perfect, but will have to do
2261         # Handle case where base is in same quadrant as starship
2262         if game.battle == game.quadrant:
2263             game.state.chart[game.battle.i][game.battle.j].starbase = False
2264             game.quad[game.base.i][game.base.j] = '.'
2265             game.base.invalidate()
2266             newcnd()
2267             skip(1)
2268             prout(_("Spock-  \"Captain, I believe the starbase has been destroyed.\""))
2269         elif game.state.baseq and communicating():
2270             # Get word via subspace radio
2271             announce()
2272             skip(1)
2273             prout(_("Lt. Uhura-  \"Captain, Starfleet Command reports that"))
2274             proutn(_("   the starbase in Quadrant %s has been destroyed by") % game.battle)
2275             if game.isatb == 2:
2276                 prout(_("the Klingon Super-Commander"))
2277             else:
2278                 prout(_("a Klingon Commander"))
2279             game.state.chart[game.battle.i][game.battle.j].starbase = False
2280         # Remove Starbase from galaxy
2281         game.state.galaxy[game.battle.i][game.battle.j].starbase = False
2282         game.state.baseq = [x for x in game.state.baseq if x != game.battle]
2283         if game.isatb == 2:
2284             # reinstate a commander's base attack
2285             game.battle = hold
2286             game.isatb = 0
2287         else:
2288             game.battle.invalidate()
2289     if game.idebug:
2290         prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
2291         for i in range(1, NEVENTS):
2292             if   i == FSNOVA:  proutn("=== Supernova       ")
2293             elif i == FTBEAM:  proutn("=== T Beam          ")
2294             elif i == FSNAP:   proutn("=== Snapshot        ")
2295             elif i == FBATTAK: proutn("=== Base Attack     ")
2296             elif i == FCDBAS:  proutn("=== Base Destroy    ")
2297             elif i == FSCMOVE: proutn("=== SC Move         ")
2298             elif i == FSCDBAS: proutn("=== SC Base Destroy ")
2299             elif i == FDSPROB: proutn("=== Probe Move      ")
2300             elif i == FDISTR:  proutn("=== Distress Call   ")
2301             elif i == FENSLV:  proutn("=== Enslavement     ")
2302             elif i == FREPRO:  proutn("=== Klingon Build   ")
2303             if is_scheduled(i):
2304                 prout("%.2f" % (scheduled(i)))
2305             else:
2306                 prout("never")
2307     radio_was_broken = damaged(DRADIO)
2308     hold.i = hold.j = 0
2309     while True:
2310         # Select earliest extraneous event, evcode==0 if no events
2311         evcode = FSPY
2312         if game.alldone:
2313             return
2314         datemin = fintim
2315         for l in range(1, NEVENTS):
2316             if game.future[l].date < datemin:
2317                 evcode = l
2318                 if game.idebug:
2319                     prout("== Event %d fires" % evcode)
2320                 datemin = game.future[l].date
2321         xtime = datemin-game.state.date
2322         if game.iscloaked:
2323             game.energy -= xtime*500.0
2324             if game.energy <= 0:
2325                 finish(FNRG)
2326                 return
2327         game.state.date = datemin
2328         # Decrement Federation resources and recompute remaining time
2329         game.state.remres -= (game.state.remkl+4*len(game.state.kcmdr))*xtime
2330         game.recompute()
2331         if game.state.remtime <= 0:
2332             finish(FDEPLETE)
2333             return
2334         # Any crew left alive?
2335         if game.state.crew <= 0:
2336             finish(FCREW)
2337             return
2338         # Is life support adequate?
2339         if damaged(DLIFSUP) and game.condition != "docked":
2340             if game.lsupres < xtime and game.damage[DLIFSUP] > game.lsupres:
2341                 finish(FLIFESUP)
2342                 return
2343             game.lsupres -= xtime
2344             if game.damage[DLIFSUP] <= xtime:
2345                 game.lsupres = game.inlsr
2346         # Fix devices
2347         repair = xtime
2348         if game.condition == "docked":
2349             repair /= DOCKFAC
2350         # Don't fix Deathray here
2351         for l in range(NDEVICES):
2352             if game.damage[l] > 0.0 and l != DDRAY:
2353                 if game.damage[l]-repair > 0.0:
2354                     game.damage[l] -= repair
2355                 else:
2356                     game.damage[l] = 0.0
2357         # If radio repaired, update star chart and attack reports
2358         if radio_was_broken and not damaged(DRADIO):
2359             prout(_("Lt. Uhura- \"Captain, the sub-space radio is working and"))
2360             prout(_("   surveillance reports are coming in."))
2361             skip(1)
2362             if not game.iseenit:
2363                 attackreport(False)
2364                 game.iseenit = True
2365             rechart()
2366             prout(_("   The star chart is now up to date.\""))
2367             skip(1)
2368         # Cause extraneous event EVCODE to occur
2369         game.optime -= xtime
2370         if evcode == FSNOVA: # Supernova
2371             announce()
2372             supernova(None)
2373             schedule(FSNOVA, expran(0.5*game.intime))
2374             if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
2375                 return
2376         elif evcode == FSPY: # Check with spy to see if SC should tractor beam
2377             if game.state.nscrem == 0 or game.iscloaked or \
2378                 ictbeam or istract or \
2379                 game.condition == "docked" or game.isatb == 1 or game.iscate:
2380                 return
2381             if game.ientesc or \
2382                 (game.energy<2000 and game.torps<4 and game.shield < 1250) or \
2383                 (damaged(DPHASER) and (damaged(DPHOTON) or game.torps<4)) or \
2384                 (damaged(DSHIELD) and \
2385                  (game.energy < 2500 or damaged(DPHASER)) and \
2386                  (game.torps < 5 or damaged(DPHOTON))):
2387                 # Tractor-beam her!
2388                 istract = ictbeam = True
2389                 tractorbeam((game.state.kscmdr-game.quadrant).distance())
2390             else:
2391                 return
2392         elif evcode == FTBEAM: # Tractor beam
2393             if not game.state.kcmdr:
2394                 unschedule(FTBEAM)
2395                 continue
2396             i = randrange(len(game.state.kcmdr))
2397             yank = (game.state.kcmdr[i]-game.quadrant).distance()
2398             if istract or game.condition == "docked" or game.iscloaked or yank == 0:
2399                 # Drats! Have to reschedule
2400                 schedule(FTBEAM,
2401                          game.optime + expran(1.5*game.intime/len(game.state.kcmdr)))
2402                 continue
2403             ictbeam = True
2404             tractorbeam(yank)
2405         elif evcode == FSNAP: # Snapshot of the universe (for time warp)
2406             game.snapsht = copy.deepcopy(game.state)
2407             game.state.snap = True
2408             schedule(FSNAP, expran(0.5 * game.intime))
2409         elif evcode == FBATTAK: # Commander attacks starbase
2410             if not game.state.kcmdr or not game.state.baseq:
2411                 # no can do
2412                 unschedule(FBATTAK)
2413                 unschedule(FCDBAS)
2414                 continue
2415             ibq = None  # Force battle location to persist past loop
2416             try:
2417                 for ibq in game.state.baseq:
2418                     for cmdr in game.state.kcmdr:
2419                         if ibq == cmdr and ibq != game.quadrant and ibq != game.state.kscmdr:
2420                             raise JumpOut
2421                 # no match found -- try later
2422                 schedule(FBATTAK, expran(0.3*game.intime))
2423                 unschedule(FCDBAS)
2424                 continue
2425             except JumpOut:
2426                 pass
2427             # commander + starbase combination found -- launch attack
2428             game.battle = ibq
2429             schedule(FCDBAS, randreal(1.0, 4.0))
2430             if game.isatb: # extra time if SC already attacking
2431                 postpone(FCDBAS, scheduled(FSCDBAS)-game.state.date)
2432             game.future[FBATTAK].date = game.future[FCDBAS].date + expran(0.3*game.intime)
2433             game.iseenit = False
2434             if not communicating():
2435                 continue # No warning :-(
2436             game.iseenit = True
2437             announce()
2438             skip(1)
2439             prout(_("Lt. Uhura-  \"Captain, the starbase in Quadrant %s") % game.battle)
2440             prout(_("   reports that it is under attack and that it can"))
2441             prout(_("   hold out only until stardate %d.\"") % (int(scheduled(FCDBAS))))
2442             if cancelrest():
2443                 return
2444         elif evcode == FSCDBAS: # Supercommander destroys base
2445             unschedule(FSCDBAS)
2446             game.isatb = 2
2447             if not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].starbase:
2448                 continue # WAS RETURN!
2449             hold = game.battle
2450             game.battle = game.state.kscmdr
2451             destroybase()
2452         elif evcode == FCDBAS: # Commander succeeds in destroying base
2453             if evcode == FCDBAS:
2454                 unschedule(FCDBAS)
2455                 if not game.state.baseq() \
2456                        or not game.state.galaxy[game.battle.i][game.battle.j].starbase:
2457                     game.battle.invalidate()
2458                     continue
2459                 # find the lucky pair
2460                 for cmdr in game.state.kcmdr:
2461                     if cmdr == game.battle:
2462                         break
2463                 else:
2464                     # No action to take after all
2465                     continue
2466             destroybase()
2467         elif evcode == FSCMOVE: # Supercommander moves
2468             schedule(FSCMOVE, 0.2777)
2469             if not game.ientesc and not istract and game.isatb != 1 and \
2470                    (not game.iscate or not game.justin):
2471                 supercommander()
2472         elif evcode == FDSPROB: # Move deep space probe
2473             schedule(FDSPROB, 0.01)
2474             if not game.probe.nexttok():
2475                 if not game.probe.quadrant().valid_quadrant() or \
2476                     game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j].supernova:
2477                     # Left galaxy or ran into supernova
2478                     if communicating():
2479                         announce()
2480                         skip(1)
2481                         proutn(_("Lt. Uhura-  \"The deep space probe "))
2482                         if not game.probe.quadrant().valid_quadrant():
2483                             prout(_("has left the galaxy.\""))
2484                         else:
2485                             prout(_("is no longer transmitting.\""))
2486                     unschedule(FDSPROB)
2487                     continue
2488                 if communicating():
2489                     #announce()
2490                     skip(1)
2491                     prout(_("Lt. Uhura-  \"The deep space probe is now in Quadrant %s.\"") % game.probe.quadrant())
2492             pquad = game.probe.quadrant()
2493             pdest = game.state.galaxy[pquad.i][pquad.j]
2494             if communicating():
2495                 game.state.chart[pquad.i][pquad.j].klingons = pdest.klingons
2496                 game.state.chart[pquad.i][pquad.j].starbase = pdest.starbase
2497                 game.state.chart[pquad.i][pquad.j].stars = pdest.stars
2498                 pdest.charted = True
2499             game.probe.moves -= 1 # One less to travel
2500             if game.probe.arrived() and game.isarmed and pdest.stars:
2501                 supernova(game.probe)                # fire in the hole!
2502                 unschedule(FDSPROB)
2503                 if game.state.galaxy[pquad.i][pquad.j].supernova:
2504                     return
2505         elif evcode == FDISTR: # inhabited system issues distress call
2506             unschedule(FDISTR)
2507             # try a whole bunch of times to find something suitable
2508             for i in range(100):
2509                 # need a quadrant which is not the current one,
2510                 # which has some stars which are inhabited and
2511                 # not already under attack, which is not
2512                 # supernova'ed, and which has some Klingons in it
2513                 w = randplace(GALSIZE)
2514                 q = game.state.galaxy[w.i][w.j]
2515                 if not (game.quadrant == w or q.planet is None or \
2516                       not q.planet.inhabited or \
2517                       q.supernova or q.status!="secure" or q.klingons<=0):
2518                     break
2519             else:
2520                 # can't seem to find one; ignore this call
2521                 if game.idebug:
2522                     prout("=== Couldn't find location for distress event.")
2523                 continue
2524             # got one!!  Schedule its enslavement
2525             ev = schedule(FENSLV, expran(game.intime))
2526             ev.quadrant = w
2527             q.status = "distressed"
2528             # tell the captain about it if we can
2529             if communicating():
2530                 prout(_("Uhura- Captain, %s in Quadrant %s reports it is under attack") \
2531                         % (q.planet, repr(w)))
2532                 prout(_("by a Klingon invasion fleet."))
2533                 if cancelrest():
2534                     return
2535         elif evcode == FENSLV:                # starsystem is enslaved
2536             ev = unschedule(FENSLV)
2537             # see if current distress call still active
2538             q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
2539             if q.klingons <= 0:
2540                 q.status = "secure"
2541                 continue
2542             q.status = "enslaved"
2543
2544             # play stork and schedule the first baby
2545             ev2 = schedule(FREPRO, expran(2.0 * game.intime))
2546             ev2.quadrant = ev.quadrant
2547
2548             # report the disaster if we can
2549             if communicating():
2550                 prout(_("Uhura- We've lost contact with starsystem %s") % \
2551                         q.planet)
2552                 prout(_("in Quadrant %s.\n") % ev.quadrant)
2553         elif evcode == FREPRO:                # Klingon reproduces
2554             # If we ever switch to a real event queue, we'll need to
2555             # explicitly retrieve and restore the x and y.
2556             ev = schedule(FREPRO, expran(1.0 * game.intime))
2557             # see if current distress call still active
2558             q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
2559             if q.klingons <= 0:
2560                 q.status = "secure"
2561                 continue
2562             if game.state.remkl >= MAXKLGAME:
2563                 continue                # full right now
2564             # reproduce one Klingon
2565             w = ev.quadrant
2566             m = Coord()
2567             if game.klhere >= MAXKLQUAD:
2568                 try:
2569                     # this quadrant not ok, pick an adjacent one
2570                     for m.i in range(w.i - 1, w.i + 2):
2571                         for m.j in range(w.j - 1, w.j + 2):
2572                             if not m.valid_quadrant():
2573                                 continue
2574                             q = game.state.galaxy[m.i][m.j]
2575                             # check for this quad ok (not full & no snova)
2576                             if q.klingons >= MAXKLQUAD or q.supernova:
2577                                 continue
2578                             raise JumpOut
2579                     # search for eligible quadrant failed
2580                     continue
2581                 except JumpOut:
2582                     w = m
2583             # deliver the child
2584             game.state.remkl += 1
2585             q.klingons += 1
2586             if game.quadrant == w:
2587                 game.klhere += 1
2588                 game.enemies.append(newkling())
2589             # recompute time left
2590             game.recompute()
2591             if communicating():
2592                 if game.quadrant == w:
2593                     prout(_("Spock- sensors indicate the Klingons have"))
2594                     prout(_("launched a warship from %s.") % q.planet)
2595                 else:
2596                     prout(_("Uhura- Starfleet reports increased Klingon activity"))
2597                     if q.planet != None:
2598                         proutn(_("near %s ") % q.planet)
2599                     prout(_("in Quadrant %s.") % w)
2600
2601 def wait():
2602     "Wait on events."
2603     game.ididit = False
2604     while True:
2605         key = scanner.nexttok()
2606         if key  != "IHEOL":
2607             break
2608         proutn(_("How long? "))
2609     scanner.chew()
2610     if key != "IHREAL":
2611         huh()
2612         return
2613     origTime = delay = scanner.real
2614     if delay <= 0.0:
2615         return
2616     if delay >= game.state.remtime or len(game.enemies) != 0:
2617         proutn(_("Are you sure? "))
2618         if not ja():
2619             return
2620     # Alternate resting periods (events) with attacks
2621     game.resting = True
2622     while True:
2623         if delay <= 0:
2624             game.resting = False
2625         if not game.resting:
2626             prout(_("%d stardates left.") % int(game.state.remtime))
2627             return
2628         temp = game.optime = delay
2629         if len(game.enemies):
2630             rtime = randreal(1.0, 2.0)
2631             if rtime < temp:
2632                 temp = rtime
2633             game.optime = temp
2634         if game.optime < delay:
2635             attack(torps_ok=False)
2636         if game.alldone:
2637             return
2638         events()
2639         game.ididit = True
2640         if game.alldone:
2641             return
2642         delay -= temp
2643         # Repair Deathray if long rest at starbase
2644         if origTime-delay >= 9.99 and game.condition == "docked":
2645             game.damage[DDRAY] = 0.0
2646         # leave if quadrant supernovas
2647         if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
2648             break
2649     game.resting = False
2650     game.optime = 0.0
2651
2652 def nova(nov):
2653     "Star goes nova."
2654     ncourse = (0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5)
2655     newc = Coord(); neighbor = Coord(); bump = Coord(0, 0)
2656     if withprob(0.05):
2657         # Wow! We've supernova'ed
2658         supernova(game.quadrant)
2659         return
2660     # handle initial nova
2661     game.quad[nov.i][nov.j] = '.'
2662     prout(crmena(False, '*', "sector", nov) + _(" novas."))
2663     game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
2664     game.state.starkl += 1
2665     # Set up queue to recursively trigger adjacent stars
2666     hits = [nov]
2667     kount = 0
2668     while hits:
2669         offset = Coord()
2670         start = hits.pop()
2671         for offset.i in range(-1, 1+1):
2672             for offset.j in range(-1, 1+1):
2673                 if offset.j == 0 and offset.i == 0:
2674                     continue
2675                 neighbor = start + offset
2676                 if not neighbor.valid_sector():
2677                     continue
2678                 iquad = game.quad[neighbor.i][neighbor.j]
2679                 # Empty space ends reaction
2680                 if iquad in ('.', '?', ' ', 'T', '#'):
2681                     pass
2682                 elif iquad == '*': # Affect another star
2683                     if withprob(0.05):
2684                         # This star supernovas
2685                         supernova(game.quadrant)
2686                         return
2687                     else:
2688                         hits.append(neighbor)
2689                         game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
2690                         game.state.starkl += 1
2691                         proutn(crmena(True, '*', "sector", neighbor))
2692                         prout(_(" novas."))
2693                         game.quad[neighbor.i][neighbor.j] = '.'
2694                         kount += 1
2695                 elif iquad in ('P', '@'): # Destroy planet
2696                     game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
2697                     if iquad == 'P':
2698                         game.state.nplankl += 1
2699                     else:
2700                         game.state.nworldkl += 1
2701                     prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
2702                     game.iplnet.pclass = "destroyed"
2703                     game.iplnet = None
2704                     game.plnet.invalidate()
2705                     if game.landed:
2706                         finish(FPNOVA)
2707                         return
2708                     game.quad[neighbor.i][neighbor.j] = '.'
2709                 elif iquad == 'B': # Destroy base
2710                     game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
2711                     game.state.baseq = [x for x in game.state.baseq if x!= game.quadrant]
2712                     game.base.invalidate()
2713                     game.state.basekl += 1
2714                     newcnd()
2715                     prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
2716                     game.quad[neighbor.i][neighbor.j] = '.'
2717                 elif iquad in ('E', 'F'): # Buffet ship
2718                     prout(_("***Starship buffeted by nova."))
2719                     if game.shldup:
2720                         if game.shield >= 2000.0:
2721                             game.shield -= 2000.0
2722                         else:
2723                             diff = 2000.0 - game.shield
2724                             game.energy -= diff
2725                             game.shield = 0.0
2726                             game.shldup = False
2727                             prout(_("***Shields knocked out."))
2728                             game.damage[DSHIELD] += 0.005*game.damfac*randreal()*diff
2729                     else:
2730                         game.energy -= 2000.0
2731                     if game.energy <= 0:
2732                         finish(FNOVA)
2733                         return
2734                     # add in course nova contributes to kicking starship
2735                     bump += (game.sector-hits[-1]).sgn()
2736                 elif iquad == 'K': # kill klingon
2737                     deadkl(neighbor, iquad, neighbor)
2738                 elif iquad in ('C','S','R'): # Damage/destroy big enemies
2739                     target = None
2740                     for ll in range(len(game.enemies)):
2741                         if game.enemies[ll].location == neighbor:
2742                             target = game.enemies[ll]
2743                             break
2744                     if target is not None:
2745                         target.power -= 800.0 # If firepower is lost, die
2746                         if target.power <= 0.0:
2747                             deadkl(neighbor, iquad, neighbor)
2748                             continue    # neighbor loop
2749                         # Else enemy gets flung by the blast wave
2750                         newc = neighbor + neighbor - hits[-1]
2751                         proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged"))
2752                         if not newc.valid_sector():
2753                             # can't leave quadrant
2754                             skip(1)
2755                             continue
2756                         iquad1 = game.quad[newc.i][newc.j]
2757                         if iquad1 == ' ':
2758                             proutn(_(", blasted into ") + crmena(False, ' ', "sector", newc))
2759                             skip(1)
2760                             deadkl(neighbor, iquad, newc)
2761                             continue
2762                         if iquad1 != '.':
2763                             # can't move into something else
2764                             skip(1)
2765                             continue
2766                         proutn(_(", buffeted to Sector %s") % newc)
2767                         game.quad[neighbor.i][neighbor.j] = '.'
2768                         game.quad[newc.i][newc.j] = iquad
2769                         target.move(newc)
2770     # Starship affected by nova -- kick it away.
2771     dist = kount*0.1
2772     direc = ncourse[3*(bump.i+1)+bump.j+2]
2773     if direc == 0.0:
2774         dist = 0.0
2775     if dist == 0.0:
2776         return
2777     scourse = course(bearing=direc, distance=dist)
2778     game.optime = scourse.time(w=4)
2779     skip(1)
2780     prout(_("Force of nova displaces starship."))
2781     imove(scourse, noattack=True)
2782     game.optime = scourse.time(w=4)
2783     return
2784
2785 def supernova(w):
2786     "Star goes supernova."
2787     num = 0; npdead = 0
2788     if w != None:
2789         nq = copy.copy(w)
2790     else:
2791         # Scheduled supernova -- select star at random.
2792         nstars = 0
2793         nq = Coord()
2794         for nq.i in range(GALSIZE):
2795             for nq.j in range(GALSIZE):
2796                 nstars += game.state.galaxy[nq.i][nq.j].stars
2797         if stars == 0:
2798             return # nothing to supernova exists
2799         num = randrange(nstars) + 1
2800         for nq.i in range(GALSIZE):
2801             for nq.j in range(GALSIZE):
2802                 num -= game.state.galaxy[nq.i][nq.j].stars
2803                 if num <= 0:
2804                     break
2805             if num <=0:
2806                 break
2807         if game.idebug:
2808             proutn("=== Super nova here?")
2809             if ja():
2810                 nq = game.quadrant
2811     if not nq == game.quadrant or game.justin:
2812         # it isn't here, or we just entered (treat as enroute)
2813         if communicating():
2814             skip(1)
2815             prout(_("Message from Starfleet Command       Stardate %.2f") % game.state.date)
2816             prout(_("     Supernova in Quadrant %s; caution advised.") % nq)
2817     else:
2818         ns = Coord()
2819         # we are in the quadrant!
2820         num = randrange(game.state.galaxy[nq.i][nq.j].stars) + 1
2821         for ns.i in range(QUADSIZE):
2822             for ns.j in range(QUADSIZE):
2823                 if game.quad[ns.i][ns.j]=='*':
2824                     num -= 1
2825                     if num==0:
2826                         break
2827             if num==0:
2828                 break
2829         skip(1)
2830         prouts(_("***RED ALERT!  RED ALERT!"))
2831         skip(1)
2832         prout(_("***Incipient supernova detected at Sector %s") % ns)
2833         if (ns.i-game.sector.i)**2 + (ns.j-game.sector.j)**2 <= 2.1:
2834             proutn(_("Emergency override attempts t"))
2835             prouts("***************")
2836             skip(1)
2837             stars()
2838             game.alldone = True
2839     # destroy any Klingons in supernovaed quadrant
2840     kldead = game.state.galaxy[nq.i][nq.j].klingons
2841     game.state.galaxy[nq.i][nq.j].klingons = 0
2842     if nq == game.state.kscmdr:
2843         # did in the Supercommander!
2844         game.state.nscrem = game.state.kscmdr.i = game.state.kscmdr.j = game.isatb =  0
2845         game.iscate = False
2846         unschedule(FSCMOVE)
2847         unschedule(FSCDBAS)
2848     survivors = filter(lambda w: w != nq, game.state.kcmdr)
2849     comkills = len(game.state.kcmdr) - len(survivors)
2850     game.state.kcmdr = survivors
2851     kldead -= comkills
2852     if not game.state.kcmdr:
2853         unschedule(FTBEAM)
2854     game.state.remkl -= kldead
2855     # destroy Romulans and planets in supernovaed quadrant
2856     nrmdead = game.state.galaxy[nq.i][nq.j].romulans
2857     game.state.galaxy[nq.i][nq.j].romulans = 0
2858     game.state.nromrem -= nrmdead
2859     # Destroy planets
2860     for loop in range(game.inplan):
2861         if game.state.planets[loop].quadrant == nq:
2862             game.state.planets[loop].pclass = "destroyed"
2863             npdead += 1
2864     # Destroy any base in supernovaed quadrant
2865     game.state.baseq = [x for x in game.state.baseq if x != nq]
2866     # If starship caused supernova, tally up destruction
2867     if w != None:
2868         game.state.starkl += game.state.galaxy[nq.i][nq.j].stars
2869         game.state.basekl += game.state.galaxy[nq.i][nq.j].starbase
2870         game.state.nplankl += npdead
2871     # mark supernova in galaxy and in star chart
2872     if game.quadrant == nq or communicating():
2873         game.state.galaxy[nq.i][nq.j].supernova = True
2874     # If supernova destroys last Klingons give special message
2875     if game.unwon()==0 and not nq == game.quadrant:
2876         skip(2)
2877         if w is None:
2878             prout(_("Lucky you!"))
2879         proutn(_("A supernova in %s has just destroyed the last Klingons.") % nq)
2880         finish(FWON)
2881         return
2882     # if some Klingons remain, continue or die in supernova
2883     if game.alldone:
2884         finish(FSNOVAED)
2885     return
2886
2887 # Code from finish.c ends here.
2888
2889 def selfdestruct():
2890     "Self-destruct maneuver. Finish with a BANG!"
2891     scanner.chew()
2892     if damaged(DCOMPTR):
2893         prout(_("Computer damaged; cannot execute destruct sequence."))
2894         return
2895     prouts(_("---WORKING---")); skip(1)
2896     prouts(_("SELF-DESTRUCT-SEQUENCE-ACTIVATED")); skip(1)
2897     prouts("   10"); skip(1)
2898     prouts("       9"); skip(1)
2899     prouts("          8"); skip(1)
2900     prouts("             7"); skip(1)
2901     prouts("                6"); skip(1)
2902     skip(1)
2903     prout(_("ENTER-CORRECT-PASSWORD-TO-CONTINUE-"))
2904     skip(1)
2905     prout(_("SELF-DESTRUCT-SEQUENCE-OTHERWISE-"))
2906     skip(1)
2907     prout(_("SELF-DESTRUCT-SEQUENCE-WILL-BE-ABORTED"))
2908     skip(1)
2909     scanner.nexttok()
2910     if game.passwd != scanner.token:
2911         prouts(_("PASSWORD-REJECTED;"))
2912         skip(1)
2913         prouts(_("CONTINUITY-EFFECTED"))
2914         skip(2)
2915         return
2916     prouts(_("PASSWORD-ACCEPTED")); skip(1)
2917     prouts("                   5"); skip(1)
2918     prouts("                      4"); skip(1)
2919     prouts("                         3"); skip(1)
2920     prouts("                            2"); skip(1)
2921     prouts("                              1"); skip(1)
2922     if withprob(0.15):
2923         prouts(_("GOODBYE-CRUEL-WORLD"))
2924         skip(1)
2925     kaboom()
2926
2927 def kaboom():
2928     stars()
2929     if game.ship=='E':
2930         prouts("***")
2931     prouts(_("********* Entropy of %s maximized *********") % crmshp())
2932     skip(1)
2933     stars()
2934     skip(1)
2935     if len(game.enemies) != 0:
2936         whammo = 25.0 * game.energy
2937         for l in range(len(game.enemies)):
2938             if game.enemies[l].power*game.enemies[l].kdist <= whammo:
2939                 deadkl(game.enemies[l].location, game.quad[game.enemies[l].location.i][game.enemies[l].location.j], game.enemies[l].location)
2940     finish(FDILITHIUM)
2941
2942 def killrate():
2943     "Compute our rate of kils over time."
2944     elapsed = game.state.date - game.indate
2945     if elapsed == 0:        # Avoid divide-by-zero error if calculated on turn 0
2946         return 0
2947     else:
2948         starting = (game.inkling + game.incom + game.inscom)
2949         remaining = game.unwon()
2950         return (starting - remaining)/elapsed
2951
2952 def badpoints():
2953     "Compute demerits."
2954     badpt = 5.0*game.state.starkl + \
2955             game.casual + \
2956             10.0*game.state.nplankl + \
2957             300*game.state.nworldkl + \
2958             45.0*game.nhelp +\
2959             100.0*game.state.basekl +\
2960             3.0*game.abandoned +\
2961             100*game.ncviol
2962     if game.ship == 'F':
2963         badpt += 100.0
2964     elif game.ship is None:
2965         badpt += 200.0
2966     return badpt
2967
2968 def finish(ifin):
2969     # end the game, with appropriate notifications
2970     igotit = False
2971     game.alldone = True
2972     skip(3)
2973     prout(_("It is stardate %.1f.") % game.state.date)
2974     skip(1)
2975     if ifin == FWON: # Game has been won
2976         if game.state.nromrem != 0:
2977             prout(_("The remaining %d Romulans surrender to Starfleet Command.") %
2978                   game.state.nromrem)
2979
2980         prout(_("You have smashed the Klingon invasion fleet and saved"))
2981         prout(_("the Federation."))
2982         if game.alive and game.brigcapacity-game.brigfree > 0:
2983             game.kcaptured += game.brigcapacity-game.brigfree
2984             prout(_("The %d captured Klingons are transferred to Star Fleet Command.") % (game.brigcapacity-game.brigfree))
2985         game.gamewon = True
2986         if game.alive:
2987             badpt = badpoints()
2988             if badpt < 100.0:
2989                 badpt = 0.0        # Close enough!
2990             # killsPerDate >= RateMax
2991             if game.state.date-game.indate < 5.0 or \
2992                 killrate() >= 0.1*game.skill*(game.skill+1.0) + 0.1 + 0.008*badpt:
2993                 skip(1)
2994                 prout(_("In fact, you have done so well that Starfleet Command"))
2995                 if game.skill == SKILL_NOVICE:
2996                     prout(_("promotes you one step in rank from \"Novice\" to \"Fair\"."))
2997                 elif game.skill == SKILL_FAIR:
2998                     prout(_("promotes you one step in rank from \"Fair\" to \"Good\"."))
2999                 elif game.skill == SKILL_GOOD:
3000                     prout(_("promotes you one step in rank from \"Good\" to \"Expert\"."))
3001                 elif game.skill == SKILL_EXPERT:
3002                     prout(_("promotes you to Commodore Emeritus."))
3003                     skip(1)
3004                     prout(_("Now that you think you're really good, try playing"))
3005                     prout(_("the \"Emeritus\" game. It will splatter your ego."))
3006                 elif game.skill == SKILL_EMERITUS:
3007                     skip(1)
3008                     proutn(_("Computer-  "))
3009                     prouts(_("ERROR-ERROR-ERROR-ERROR"))
3010                     skip(2)
3011                     prouts(_("  YOUR-SKILL-HAS-EXCEEDED-THE-CAPACITY-OF-THIS-PROGRAM"))
3012                     skip(1)
3013                     prouts(_("  THIS-PROGRAM-MUST-SURVIVE"))
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?- MUST ? - SUR? ? -?  VI"))
3020                     skip(2)
3021                     prout(_("Now you can retire and write your own Star Trek game!"))
3022                     skip(1)
3023                 elif game.skill >= SKILL_EXPERT:
3024                     if game.thawed and not game.idebug:
3025                         prout(_("You cannot get a citation, so..."))
3026                     else:
3027                         proutn(_("Do you want your Commodore Emeritus Citation printed? "))
3028                         scanner.chew()
3029                         if ja():
3030                             igotit = True
3031             # Only grant long life if alive (original didn't!)
3032             skip(1)
3033             prout(_("LIVE LONG AND PROSPER."))
3034         score()
3035         if igotit:
3036             plaque()
3037         return
3038     elif ifin == FDEPLETE: # Federation Resources Depleted
3039         prout(_("Your time has run out and the Federation has been"))
3040         prout(_("conquered.  Your starship is now Klingon property,"))
3041         prout(_("and you are put on trial as a war criminal.  On the"))
3042         proutn(_("basis of your record, you are "))
3043         if game.unwon()*3.0 > (game.inkling + game.incom + game.inscom):
3044             prout(_("acquitted."))
3045             skip(1)
3046             prout(_("LIVE LONG AND PROSPER."))
3047         else:
3048             prout(_("found guilty and"))
3049             prout(_("sentenced to death by slow torture."))
3050             game.alive = False
3051         score()
3052         return
3053     elif ifin == FLIFESUP:
3054         prout(_("Your life support reserves have run out, and"))
3055         prout(_("you die of thirst, starvation, and asphyxiation."))
3056         prout(_("Your starship is a derelict in space."))
3057     elif ifin == FNRG:
3058         prout(_("Your energy supply is exhausted."))
3059         skip(1)
3060         prout(_("Your starship is a derelict in space."))
3061     elif ifin == FBATTLE:
3062         prout(_("The %s has been destroyed in battle.") % crmshp())
3063         skip(1)
3064         prout(_("Dulce et decorum est pro patria mori."))
3065     elif ifin == FNEG3:
3066         prout(_("You have made three attempts to cross the negative energy"))
3067         prout(_("barrier which surrounds the galaxy."))
3068         skip(1)
3069         prout(_("Your navigation is abominable."))
3070         score()
3071     elif ifin == FNOVA:
3072         prout(_("Your starship has been destroyed by a nova."))
3073         prout(_("That was a great shot."))
3074         skip(1)
3075     elif ifin == FSNOVAED:
3076         prout(_("The %s has been fried by a supernova.") % crmshp())
3077         prout(_("...Not even cinders remain..."))
3078     elif ifin == FABANDN:
3079         prout(_("You have been captured by the Klingons. If you still"))
3080         prout(_("had a starbase to be returned to, you would have been"))
3081         prout(_("repatriated and given another chance. Since you have"))
3082         prout(_("no starbases, you will be mercilessly tortured to death."))
3083     elif ifin == FDILITHIUM:
3084         prout(_("Your starship is now an expanding cloud of subatomic particles"))
3085     elif ifin == FMATERIALIZE:
3086         prout(_("Starbase was unable to re-materialize your starship."))
3087         prout(_("Sic transit gloria mundi"))
3088     elif ifin == FPHASER:
3089         prout(_("The %s has been cremated by its own phasers.") % crmshp())
3090     elif ifin == FLOST:
3091         prout(_("You and your landing party have been"))
3092         prout(_("converted to energy, dissipating through space."))
3093     elif ifin == FMINING:
3094         prout(_("You are left with your landing party on"))
3095         prout(_("a wild jungle planet inhabited by primitive cannibals."))
3096         skip(1)
3097         prout(_("They are very fond of \"Captain Kirk\" soup."))
3098         skip(1)
3099         prout(_("Without your leadership, the %s is destroyed.") % crmshp())
3100     elif ifin == FDPLANET:
3101         prout(_("You and your mining party perish."))
3102         skip(1)
3103         prout(_("That was a great shot."))
3104         skip(1)
3105     elif ifin == FSSC:
3106         prout(_("The Galileo is instantly annihilated by the supernova."))
3107         prout(_("You and your mining party are atomized."))
3108         skip(1)
3109         prout(_("Mr. Spock takes command of the %s and") % crmshp())
3110         prout(_("joins the Romulans, wreaking terror on the Federation."))
3111     elif ifin == FPNOVA:
3112         prout(_("You and your mining party are atomized."))
3113         skip(1)
3114         prout(_("Mr. Spock takes command of the %s and") % crmshp())
3115         prout(_("joins the Romulans, wreaking terror on the Federation."))
3116     elif ifin == FSTRACTOR:
3117         prout(_("The shuttle craft Galileo is also caught,"))
3118         prout(_("and breaks up under the strain."))
3119         skip(1)
3120         prout(_("Your debris is scattered for millions of miles."))
3121         prout(_("Without your leadership, the %s is destroyed.") % crmshp())
3122     elif ifin == FDRAY:
3123         prout(_("The mutants attack and kill Spock."))
3124         prout(_("Your ship is captured by Klingons, and"))
3125         prout(_("your crew is put on display in a Klingon zoo."))
3126     elif ifin == FTRIBBLE:
3127         prout(_("Tribbles consume all remaining water,"))
3128         prout(_("food, and oxygen on your ship."))
3129         skip(1)
3130         prout(_("You die of thirst, starvation, and asphyxiation."))
3131         prout(_("Your starship is a derelict in space."))
3132     elif ifin == FHOLE:
3133         prout(_("Your ship is drawn to the center of the black hole."))
3134         prout(_("You are crushed into extremely dense matter."))
3135     elif ifin == FCLOAK:
3136         game.ncviol += 1
3137         prout(_("You have violated the Treaty of Algeron."))
3138         prout(_("The Romulan Empire can never trust you again."))
3139     elif ifin == FCREW:
3140         prout(_("Your last crew member has died."))
3141     if ifin != FWON and ifin != FCLOAK and game.iscloaked:
3142         prout(_("Your ship was cloaked so your subspace radio did not receive anything."))
3143         prout(_("You may have missed some warning messages."))
3144         skip(1)
3145     if game.ship == 'F':
3146         game.ship = None
3147     elif game.ship == 'E':
3148         game.ship = 'F'
3149     game.alive = False
3150     if game.unwon() != 0:
3151         goodies = game.state.remres/game.inresor
3152         baddies = (game.state.remkl + 2.0*len(game.state.kcmdr))/(game.inkling+2.0*game.incom)
3153         if goodies/baddies >= randreal(1.0, 1.5):
3154             prout(_("As a result of your actions, a treaty with the Klingon"))
3155             prout(_("Empire has been signed. The terms of the treaty are"))
3156             if goodies/baddies >= randreal(3.0):
3157                 prout(_("favorable to the Federation."))
3158                 skip(1)
3159                 prout(_("Congratulations!"))
3160             else:
3161                 prout(_("highly unfavorable to the Federation."))
3162         else:
3163             prout(_("The Federation will be destroyed."))
3164     else:
3165         prout(_("Since you took the last Klingon with you, you are a"))
3166         prout(_("martyr and a hero. Someday maybe they'll erect a"))
3167         prout(_("statue in your memory. Rest in peace, and try not"))
3168         prout(_("to think about pigeons."))
3169         game.gamewon = True
3170     score()
3171     scanner.chew()      # Clean up leftovers
3172
3173 def score():
3174     "Compute player's score."
3175     timused = game.state.date - game.indate
3176     if (timused == 0 or game.unwon() != 0) and timused < 5.0:
3177         timused = 5.0
3178     game.perdate = killrate()
3179     ithperd = 500*game.perdate + 0.5
3180     iwon = 0
3181     if game.gamewon:
3182         iwon = 100*game.skill
3183     if game.ship == 'E':
3184         klship = 0
3185     elif game.ship == 'F':
3186         klship = 1
3187     else:
3188         klship = 2
3189     game.score = 10*(game.inkling - game.state.remkl) \
3190              + 50*(game.incom - len(game.state.kcmdr)) \
3191              + ithperd + iwon \
3192              + 20*(game.inrom - game.state.nromrem) \
3193              + 200*(game.inscom - game.state.nscrem) \
3194                  - game.state.nromrem \
3195              + 3 * game.kcaptured \
3196              - badpoints()
3197     if not game.alive:
3198         game.score -= 200
3199     skip(2)
3200     prout(_("Your score --"))
3201     if game.inrom - game.state.nromrem:
3202         prout(_("%6d Romulans destroyed                 %5d") %
3203               (game.inrom - game.state.nromrem, 20*(game.inrom - game.state.nromrem)))
3204     if game.state.nromrem and game.gamewon:
3205         prout(_("%6d Romulans captured                  %5d") %
3206               (game.state.nromrem, game.state.nromrem))
3207     if game.inkling - game.state.remkl:
3208         prout(_("%6d ordinary Klingons destroyed        %5d") %
3209               (game.inkling - game.state.remkl, 10*(game.inkling - game.state.remkl)))
3210     if game.incom - len(game.state.kcmdr):
3211         prout(_("%6d Klingon commanders destroyed       %5d") %
3212               (game.incom - len(game.state.kcmdr), 50*(game.incom - len(game.state.kcmdr))))
3213     if game.kcaptured:
3214         prout(_("%d Klingons captured                   %5d") %
3215               (game.kcaptured, 3 * game.kcaptured))
3216     if game.inscom - game.state.nscrem:
3217         prout(_("%6d Super-Commander destroyed          %5d") %
3218               (game.inscom - game.state.nscrem, 200*(game.inscom - game.state.nscrem)))
3219     if ithperd:
3220         prout(_("%6.2f Klingons per stardate              %5d") %
3221               (game.perdate, ithperd))
3222     if game.state.starkl:
3223         prout(_("%6d stars destroyed by your action     %5d") %
3224               (game.state.starkl, -5*game.state.starkl))
3225     if game.state.nplankl:
3226         prout(_("%6d planets destroyed by your action   %5d") %
3227               (game.state.nplankl, -10*game.state.nplankl))
3228     if (game.options & OPTION_WORLDS) and game.state.nworldkl:
3229         prout(_("%6d inhabited planets destroyed by your action   %5d") %
3230               (game.state.nworldkl, -300*game.state.nworldkl))
3231     if game.state.basekl:
3232         prout(_("%6d bases destroyed by your action     %5d") %
3233               (game.state.basekl, -100*game.state.basekl))
3234     if game.nhelp:
3235         prout(_("%6d calls for help from starbase       %5d") %
3236               (game.nhelp, -45*game.nhelp))
3237     if game.casual:
3238         prout(_("%6d casualties incurred                %5d") %
3239               (game.casual, -game.casual))
3240     if game.abandoned:
3241         prout(_("%6d crew abandoned in space            %5d") %
3242               (game.abandoned, -3*game.abandoned))
3243     if klship:
3244         prout(_("%6d ship(s) lost or destroyed          %5d") %
3245               (klship, -100*klship))
3246     if game.ncviol > 0:
3247         if ncviol == 1:
3248             prout(_("1 Treaty of Algeron violation          -100"))
3249         else:
3250             prout(_("%6d Treaty of Algeron violations       %5d\n") %
3251                   (ncviol, -100*ncviol))
3252     if not game.alive:
3253         prout(_("Penalty for getting yourself killed        -200"))
3254     if game.gamewon:
3255         proutn(_("Bonus for winning "))
3256         if game.skill   == SKILL_NOVICE:        proutn(_("Novice game  "))
3257         elif game.skill == SKILL_FAIR:          proutn(_("Fair game    "))
3258         elif game.skill ==  SKILL_GOOD:         proutn(_("Good game    "))
3259         elif game.skill ==  SKILL_EXPERT:        proutn(_("Expert game  "))
3260         elif game.skill ==  SKILL_EMERITUS:        proutn(_("Emeritus game"))
3261         prout("           %5d" % iwon)
3262     skip(1)
3263     prout(_("TOTAL SCORE                               %5d") % game.score)
3264
3265 def plaque():
3266     "Emit winner's commemmorative plaque."
3267     skip(2)
3268     while True:
3269         proutn(_("File or device name for your plaque: "))
3270         winner = cgetline()
3271         try:
3272             fp = open(winner, "w")
3273             break
3274         except IOError:
3275             prout(_("Invalid name."))
3276
3277     proutn(_("Enter name to go on plaque (up to 30 characters): "))
3278     winner = cgetline()
3279     # The 38 below must be 64 for 132-column paper
3280     nskip = 38 - len(winner)/2
3281     # This is where the ASCII art picture was emitted.
3282     # It got garbled somewhere in the chain of transmission to the Almy version.
3283     # We should restore it if we can find old enough FORTRAN sources.
3284     fp.write("\n\n\n")
3285     fp.write(_("                                                       U. S. S. ENTERPRISE\n"))
3286     fp.write("\n\n\n\n")
3287     fp.write(_("                                  For demonstrating outstanding ability as a starship captain\n"))
3288     fp.write("\n")
3289     fp.write(_("                                                Starfleet Command bestows to you\n"))
3290     fp.write("\n")
3291     fp.write("%*s%s\n\n" % (nskip, "", winner))
3292     fp.write(_("                                                           the rank of\n\n"))
3293     fp.write(_("                                                       \"Commodore Emeritus\"\n\n"))
3294     fp.write("                                                          ")
3295     if game.skill ==  SKILL_EXPERT:
3296         fp.write(_(" Expert level\n\n"))
3297     elif game.skill == SKILL_EMERITUS:
3298         fp.write(_("Emeritus level\n\n"))
3299     else:
3300         fp.write(_(" Cheat level\n\n"))
3301     timestring = time.ctime()
3302     fp.write(_("                                                 This day of %.6s %.4s, %.8s\n\n") %
3303              (timestring+4, timestring+20, timestring+11))
3304     fp.write(_("                                                        Your score:  %d\n\n") % game.score)
3305     fp.write(_("                                                    Klingons per stardate:  %.2f\n") % game.perdate)
3306     fp.close()
3307
3308 # Code from io.c begins here
3309
3310 rows = linecount = 0        # for paging
3311 stdscr = None
3312 replayfp = None
3313 fullscreen_window = None
3314 srscan_window     = None   # Short range scan
3315 report_window     = None   # Report legends for status window
3316 status_window     = None   # The status window itself
3317 lrscan_window     = None   # Long range scan
3318 message_window    = None   # Main window for scrolling text
3319 prompt_window     = None   # Prompt window at bottom of display
3320 curwnd = None
3321
3322 def iostart():
3323     global stdscr, rows
3324     # for some recent versions of python2, the following enables UTF8
3325     # for the older ones we probably need to set C locale, and python3
3326     # has no problems at all
3327     if sys.version_info[0] < 3:
3328         locale.setlocale(locale.LC_ALL, "")
3329     gettext.bindtextdomain("sst", "/usr/local/share/locale")
3330     gettext.textdomain("sst")
3331     if not (game.options & OPTION_CURSES):
3332         ln_env = os.getenv("LINES")
3333         if ln_env:
3334             rows = ln_env
3335         else:
3336             rows = 25
3337     else:
3338         stdscr = curses.initscr()
3339         stdscr.keypad(True)
3340         curses.nonl()
3341         curses.cbreak()
3342         if game.options & OPTION_COLOR:
3343             curses.start_color()
3344             curses.use_default_colors()
3345             curses.init_pair(curses.COLOR_BLACK,   curses.COLOR_BLACK, -1)
3346             curses.init_pair(curses.COLOR_GREEN,   curses.COLOR_GREEN, -1)
3347             curses.init_pair(curses.COLOR_RED,     curses.COLOR_RED, -1)
3348             curses.init_pair(curses.COLOR_CYAN,    curses.COLOR_CYAN, -1)
3349             curses.init_pair(curses.COLOR_WHITE,   curses.COLOR_WHITE, -1)
3350             curses.init_pair(curses.COLOR_MAGENTA, curses.COLOR_MAGENTA, -1)
3351             curses.init_pair(curses.COLOR_BLUE,    curses.COLOR_BLUE, -1)
3352             curses.init_pair(curses.COLOR_YELLOW,  curses.COLOR_YELLOW, -1)
3353         global fullscreen_window, srscan_window, report_window, status_window
3354         global lrscan_window, message_window, prompt_window
3355         (rows, _columns)   = stdscr.getmaxyx()
3356         fullscreen_window = stdscr
3357         srscan_window     = curses.newwin(12, 25, 0,       0)
3358         report_window     = curses.newwin(11, 0,  1,       25)
3359         status_window     = curses.newwin(10, 0,  1,       39)
3360         lrscan_window     = curses.newwin(5,  0,  0,       64)
3361         message_window    = curses.newwin(0,  0,  12,      0)
3362         prompt_window     = curses.newwin(1,  0,  rows-2,  0)
3363         message_window.scrollok(True)
3364         setwnd(fullscreen_window)
3365
3366 def ioend():
3367     "Wrap up I/O."
3368     if game.options & OPTION_CURSES:
3369         stdscr.keypad(False)
3370         curses.echo()
3371         curses.nocbreak()
3372         curses.endwin()
3373
3374 def waitfor():
3375     "Wait for user action -- OK to do nothing if on a TTY"
3376     if game.options & OPTION_CURSES:
3377         stdscr.getch()
3378
3379 def announce():
3380     skip(1)
3381     prouts(_("[ANNOUNCEMENT ARRIVING...]"))
3382     skip(1)
3383
3384 def pause_game():
3385     if game.skill > SKILL_FAIR:
3386         prompt = _("[CONTINUE?]")
3387     else:
3388         prompt = _("[PRESS ENTER TO CONTINUE]")
3389
3390     if game.options & OPTION_CURSES:
3391         drawmaps(0)
3392         setwnd(prompt_window)
3393         prompt_window.clear()
3394         prompt_window.addstr(prompt)
3395         prompt_window.getstr()
3396         prompt_window.clear()
3397         prompt_window.refresh()
3398         setwnd(message_window)
3399     else:
3400         global linecount
3401         sys.stdout.write('\n')
3402         proutn(prompt)
3403         if not replayfp:
3404             my_input()
3405         sys.stdout.write('\n' * rows)
3406         linecount = 0
3407
3408 def skip(i):
3409     "Skip i lines.  Pause game if this would cause a scrolling event."
3410     for _dummy in range(i):
3411         if game.options & OPTION_CURSES:
3412             (y, _x) = curwnd.getyx()
3413             try:
3414                 curwnd.move(y+1, 0)
3415             except curses.error:
3416                 pass
3417         else:
3418             global linecount
3419             linecount += 1
3420             if rows and linecount >= rows:
3421                 pause_game()
3422             else:
3423                 sys.stdout.write('\n')
3424
3425 def proutn(proutntline):
3426     "Utter a line with no following line feed."
3427     if game.options & OPTION_CURSES:
3428         (y, x) = curwnd.getyx()
3429         (my, _mx) = curwnd.getmaxyx()
3430         if curwnd == message_window and y >= my - 2:
3431             pause_game()
3432             clrscr()
3433         # Uncomment this to debug curses problems
3434         if logfp:
3435             logfp.write("#curses: at %s proutn(%s)\n" % ((y, x), repr(proutntline)))
3436         curwnd.addstr(proutntline)
3437         curwnd.refresh()
3438     else:
3439         sys.stdout.write(proutntline)
3440         sys.stdout.flush()
3441
3442 def prout(proutline):
3443     proutn(proutline)
3444     skip(1)
3445
3446 def prouts(proutsline):
3447     "Emit slowly!"
3448     for c in proutsline:
3449         if not replayfp or replayfp.closed:        # Don't slow down replays
3450             time.sleep(0.03)
3451         proutn(c)
3452         if game.options & OPTION_CURSES:
3453             curwnd.refresh()
3454         else:
3455             sys.stdout.flush()
3456     if not replayfp or replayfp.closed:
3457         time.sleep(0.03)
3458
3459 def cgetline():
3460     "Get a line of input."
3461     if game.options & OPTION_CURSES:
3462         linein = curwnd.getstr() + "\n"
3463         curwnd.refresh()
3464     else:
3465         if replayfp and not replayfp.closed:
3466             while True:
3467                 linein = replayfp.readline()
3468                 proutn(linein)
3469                 if linein == '':
3470                     prout("*** Replay finished")
3471                     replayfp.close()
3472                     break
3473                 elif linein[0] != "#":
3474                     break
3475         else:
3476             try:
3477                 linein = my_input() + "\n"
3478             except EOFError:
3479                 prout("")
3480                 sys.exit(0)
3481     if logfp:
3482         logfp.write(linein)
3483     return linein
3484
3485 def setwnd(wnd):
3486     "Change windows -- OK for this to be a no-op in tty mode."
3487     global curwnd
3488     if game.options & OPTION_CURSES:
3489         # Uncomment this to debug curses problems
3490         if logfp:
3491             if wnd == fullscreen_window:
3492                 legend = "fullscreen"
3493             elif wnd == srscan_window:
3494                 legend = "srscan"
3495             elif wnd == report_window:
3496                 legend = "report"
3497             elif wnd == status_window:
3498                 legend = "status"
3499             elif wnd == lrscan_window:
3500                 legend = "lrscan"
3501             elif wnd == message_window:
3502                 legend = "message"
3503             elif wnd == prompt_window:
3504                 legend = "prompt"
3505             else:
3506                 legend = "unknown"
3507             logfp.write("#curses: setwnd(%s)\n" % legend)
3508         curwnd = wnd
3509         # Some curses implementations get confused when you try this.
3510         try:
3511             curses.curs_set(wnd in (fullscreen_window, message_window, prompt_window))
3512         except curses.error:
3513             pass
3514
3515 def clreol():
3516     "Clear to end of line -- can be a no-op in tty mode"
3517     if game.options & OPTION_CURSES:
3518         curwnd.clrtoeol()
3519         curwnd.refresh()
3520
3521 def clrscr():
3522     "Clear screen -- can be a no-op in tty mode."
3523     global linecount
3524     if game.options & OPTION_CURSES:
3525         curwnd.clear()
3526         curwnd.move(0, 0)
3527         curwnd.refresh()
3528     linecount = 0
3529
3530 def textcolor(color=DEFAULT):
3531     if (game.options & OPTION_COLOR) and (game.options & OPTION_CURSES):
3532         if color == DEFAULT:
3533             curwnd.attrset(0)
3534         elif color ==  BLACK:
3535             curwnd.attron(curses.color_pair(curses.COLOR_BLACK))
3536         elif color ==  BLUE:
3537             curwnd.attron(curses.color_pair(curses.COLOR_BLUE))
3538         elif color ==  GREEN:
3539             curwnd.attron(curses.color_pair(curses.COLOR_GREEN))
3540         elif color ==  CYAN:
3541             curwnd.attron(curses.color_pair(curses.COLOR_CYAN))
3542         elif color ==  RED:
3543             curwnd.attron(curses.color_pair(curses.COLOR_RED))
3544         elif color ==  MAGENTA:
3545             curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA))
3546         elif color ==  BROWN:
3547             curwnd.attron(curses.color_pair(curses.COLOR_YELLOW))
3548         elif color ==  LIGHTGRAY:
3549             curwnd.attron(curses.color_pair(curses.COLOR_WHITE))
3550         elif color ==  DARKGRAY:
3551             curwnd.attron(curses.color_pair(curses.COLOR_BLACK) | curses.A_BOLD)
3552         elif color ==  LIGHTBLUE:
3553             curwnd.attron(curses.color_pair(curses.COLOR_BLUE) | curses.A_BOLD)
3554         elif color ==  LIGHTGREEN:
3555             curwnd.attron(curses.color_pair(curses.COLOR_GREEN) | curses.A_BOLD)
3556         elif color ==  LIGHTCYAN:
3557             curwnd.attron(curses.color_pair(curses.COLOR_CYAN) | curses.A_BOLD)
3558         elif color ==  LIGHTRED:
3559             curwnd.attron(curses.color_pair(curses.COLOR_RED) | curses.A_BOLD)
3560         elif color ==  LIGHTMAGENTA:
3561             curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA) | curses.A_BOLD)
3562         elif color ==  YELLOW:
3563             curwnd.attron(curses.color_pair(curses.COLOR_YELLOW) | curses.A_BOLD)
3564         elif color ==  WHITE:
3565             curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD)
3566
3567 def highvideo():
3568     if (game.options & OPTION_COLOR) and (game.options & OPTION_CURSES):
3569         curwnd.attron(curses.A_REVERSE)
3570
3571 #
3572 # Things past this point have policy implications.
3573 #
3574
3575 def drawmaps(mode):
3576     "Hook to be called after moving to redraw maps."
3577     if game.options & OPTION_CURSES:
3578         if mode == 1:
3579             sensor()
3580         setwnd(srscan_window)
3581         curwnd.move(0, 0)
3582         srscan()
3583         if mode != 2:
3584             setwnd(status_window)
3585             status_window.clear()
3586             status_window.move(0, 0)
3587             setwnd(report_window)
3588             report_window.clear()
3589             report_window.move(0, 0)
3590             status()
3591             setwnd(lrscan_window)
3592             lrscan_window.clear()
3593             lrscan_window.move(0, 0)
3594             lrscan(silent=False)
3595
3596 def put_srscan_sym(w, sym):
3597     "Emit symbol for short-range scan."
3598     srscan_window.move(w.i+1, w.j*2+2)
3599     srscan_window.addch(sym)
3600     srscan_window.refresh()
3601
3602 def boom(w):
3603     "Enemy fall down, go boom."
3604     if game.options & OPTION_CURSES:
3605         drawmaps(0)
3606         setwnd(srscan_window)
3607         srscan_window.attron(curses.A_REVERSE)
3608         put_srscan_sym(w, game.quad[w.i][w.j])
3609         #sound(500)
3610         #time.sleep(1.0)
3611         #nosound()
3612         srscan_window.attroff(curses.A_REVERSE)
3613         put_srscan_sym(w, game.quad[w.i][w.j])
3614         curses.delay_output(500)
3615         setwnd(message_window)
3616
3617 def warble():
3618     "Sound and visual effects for teleportation."
3619     if game.options & OPTION_CURSES:
3620         drawmaps(2)
3621         setwnd(message_window)
3622         #sound(50)
3623     prouts("     . . . . .     ")
3624     if game.options & OPTION_CURSES:
3625         #curses.delay_output(1000)
3626         #nosound()
3627         pass
3628
3629 def tracktorpedo(w, step, i, n, iquad):
3630     "Torpedo-track animation."
3631     if not game.options & OPTION_CURSES:
3632         if step == 1:
3633             if n != 1:
3634                 skip(1)
3635                 proutn(_("Track for torpedo number %d-  ") % (i+1))
3636             else:
3637                 skip(1)
3638                 proutn(_("Torpedo track- "))
3639         elif step==4 or step==9:
3640             skip(1)
3641         proutn("%s   " % w)
3642     else:
3643         if not damaged(DSRSENS) or game.condition=="docked":
3644             if i != 0 and step == 1:
3645                 drawmaps(2)
3646                 time.sleep(0.4)
3647             if (iquad=='.') or (iquad==' '):
3648                 put_srscan_sym(w, '+')
3649                 #sound(step*10)
3650                 #time.sleep(0.1)
3651                 #nosound()
3652                 put_srscan_sym(w, iquad)
3653             else:
3654                 curwnd.attron(curses.A_REVERSE)
3655                 put_srscan_sym(w, iquad)
3656                 #sound(500)
3657                 #time.sleep(1.0)
3658                 #nosound()
3659                 curwnd.attroff(curses.A_REVERSE)
3660                 put_srscan_sym(w, iquad)
3661         else:
3662             proutn("%s   " % w)
3663
3664 def makechart():
3665     "Display the current galaxy chart."
3666     if game.options & OPTION_CURSES:
3667         setwnd(message_window)
3668         message_window.clear()
3669     chart()
3670     if game.options & OPTION_TTY:
3671         skip(1)
3672
3673 NSYM        = 14
3674
3675 def prstat(txt, data):
3676     proutn(txt)
3677     if game.options & OPTION_CURSES:
3678         skip(1)
3679         setwnd(status_window)
3680     else:
3681         proutn(" " * (NSYM - len(txt)))
3682     proutn(data)
3683     skip(1)
3684     if game.options & OPTION_CURSES:
3685         setwnd(report_window)
3686
3687 # Code from moving.c begins here
3688
3689 def imove(icourse=None, noattack=False):
3690     "Movement execution for warp, impulse, supernova, and tractor-beam events."
3691     w = Coord()
3692
3693     def newquadrant(noattack):
3694         # Leaving quadrant -- allow final enemy attack
3695         # Don't set up attack if being pushed by nova or cloaked
3696         if len(game.enemies) != 0 and not noattack and not game.iscloaked:
3697             newcnd()
3698             for enemy in game.enemies:
3699                 finald = (w - enemy.location).distance()
3700                 enemy.kavgd = 0.5 * (finald + enemy.kdist)
3701             # Stas Sergeev added the condition
3702             # that attacks only happen if Klingons
3703             # are present and your skill is good.
3704             if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3705                 attack(torps_ok=False)
3706             if game.alldone:
3707                 return
3708         # check for edge of galaxy
3709         kinks = 0
3710         while True:
3711
3712
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                     proutn(" %3d" % (game.state.chart[x][y].klingons*100 + game.state.chart[x][y].starbase * 10 + game.state.chart[x][y].stars))
5212         if not silent:
5213             prout(" ")
5214
5215 def damagereport():
5216     "Damage report."
5217     jdam = False
5218     scanner.chew()
5219     for i in range(NDEVICES):
5220         if damaged(i):
5221             if not jdam:
5222                 prout(_("\tDEVICE\t\t\t-REPAIR TIMES-"))
5223                 prout(_("\t\t\tIN FLIGHT\t\tDOCKED"))
5224                 jdam = True
5225             prout("  %-26s\t%8.2f\t\t%8.2f" % (device[i],
5226                                                game.damage[i]+0.05,
5227                                                DOCKFAC*game.damage[i]+0.005))
5228     if not jdam:
5229         prout(_("All devices functional."))
5230
5231 def rechart():
5232     "Update the chart in the Enterprise's computer from galaxy data."
5233     game.lastchart = game.state.date
5234     for i in range(GALSIZE):
5235         for j in range(GALSIZE):
5236             if game.state.galaxy[i][j].charted:
5237                 game.state.chart[i][j].klingons = game.state.galaxy[i][j].klingons
5238                 game.state.chart[i][j].starbase = game.state.galaxy[i][j].starbase
5239                 game.state.chart[i][j].stars = game.state.galaxy[i][j].stars
5240
5241 def chart():
5242     "Display the star chart."
5243     scanner.chew()
5244     if (game.options & OPTION_AUTOSCAN):
5245         lrscan(silent=True)
5246     if communicating():
5247         rechart()
5248     if game.lastchart < game.state.date and game.condition == "docked":
5249         prout(_("Spock-  \"I revised the Star Chart from the starbase's records.\""))
5250         rechart()
5251     prout(_("       STAR CHART FOR THE KNOWN GALAXY"))
5252     if game.state.date > game.lastchart:
5253         prout(_("(Last surveillance update %d stardates ago).") % ((int)(game.state.date-game.lastchart)))
5254     prout("      1    2    3    4    5    6    7    8")
5255     for i in range(GALSIZE):
5256         proutn("%d |" % (i+1))
5257         for j in range(GALSIZE):
5258             if (game.options & OPTION_SHOWME) and i == game.quadrant.i and j == game.quadrant.j:
5259                 proutn("<")
5260             else:
5261                 proutn(" ")
5262             if game.state.galaxy[i][j].supernova:
5263                 show = "***"
5264             elif not game.state.galaxy[i][j].charted and game.state.galaxy[i][j].starbase:
5265                 show = ".1."
5266             elif game.state.galaxy[i][j].charted:
5267                 show = "%3d" % (game.state.chart[i][j].klingons*100 + game.state.chart[i][j].starbase * 10 + game.state.chart[i][j].stars)
5268             else:
5269                 show = "..."
5270             proutn(show)
5271             if (game.options & OPTION_SHOWME) and i == game.quadrant.i and j == game.quadrant.j:
5272                 proutn(">")
5273             else:
5274                 proutn(" ")
5275         proutn("  |")
5276         if i<GALSIZE:
5277             skip(1)
5278
5279 def sectscan(goodScan, i, j):
5280     "Light up an individual dot in a sector."
5281     if goodScan or (abs(i-game.sector.i)<= 1 and abs(j-game.sector.j) <= 1):
5282         if game.quad[i][j] in ('E', 'F'):
5283             if game.iscloaked:
5284                 highvideo()
5285             textcolor({"green":GREEN,
5286                        "yellow":YELLOW,
5287                        "red":RED,
5288                        "docked":CYAN,
5289                        "dead":BROWN}[game.condition])
5290         else:
5291             textcolor({'?':LIGHTMAGENTA,
5292                        'K':LIGHTRED,
5293                        'S':LIGHTRED,
5294                        'C':LIGHTRED,
5295                        'R':LIGHTRED,
5296                        'T':LIGHTRED,
5297                        }.get(game.quad[i][j], DEFAULT))
5298         proutn("%c " % game.quad[i][j])
5299         textcolor(DEFAULT)
5300     else:
5301         proutn("- ")
5302
5303 def status(req=0):
5304     "Emit status report lines"
5305     if not req or req == 1:
5306         prstat(_("Stardate"), _("%.1f, Time Left %.2f") \
5307                % (game.state.date, game.state.remtime))
5308     if not req or req == 2:
5309         if game.condition != "docked":
5310             newcnd()
5311         prstat(_("Condition"), _("%s, %i DAMAGES") % \
5312                (game.condition.upper(), sum([x > 0 for x in game.damage])))
5313         if game.iscloaked:
5314             prout(_(", CLOAKED"))
5315     if not req or req == 3:
5316         prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
5317     if not req or req == 4:
5318         if damaged(DLIFSUP):
5319             if game.condition == "docked":
5320                 s = _("DAMAGED, Base provides")
5321             else:
5322                 s = _("DAMAGED, reserves=%4.2f") % game.lsupres
5323         else:
5324             s = _("ACTIVE")
5325         prstat(_("Life Support"), s)
5326     if not req or req == 5:
5327         prstat(_("Warp Factor"), "%.1f" % game.warpfac)
5328     if not req or req == 6:
5329         extra = ""
5330         if game.icrystl and (game.options & OPTION_SHOWME):
5331             extra = _(" (have crystals)")
5332         prstat(_("Energy"), "%.2f%s" % (game.energy, extra))
5333     if not req or req == 7:
5334         prstat(_("Torpedoes"), "%d" % (game.torps))
5335     if not req or req == 8:
5336         if damaged(DSHIELD):
5337             s = _("DAMAGED,")
5338         elif game.shldup:
5339             s = _("UP,")
5340         else:
5341             s = _("DOWN,")
5342         data = _(" %d%% %.1f units") \
5343                % (int((100.0*game.shield)/game.inshld + 0.5), game.shield)
5344         prstat(_("Shields"), s+data)
5345     if not req or req == 9:
5346         prstat(_("Klingons Left"), "%d" % game.unwon())
5347     if not req or req == 10:
5348         if game.options & OPTION_WORLDS:
5349             plnet = game.state.galaxy[game.quadrant.i][game.quadrant.j].planet
5350             if plnet and plnet.inhabited:
5351                 prstat(_("Major system"), plnet.name)
5352             else:
5353                 prout(_("Sector is uninhabited"))
5354     elif not req or req == 11:
5355         attackreport(not req)
5356
5357 def request():
5358     "Request specified status data, a historical relic from slow TTYs."
5359     requests = ("da","co","po","ls","wa","en","to","sh","kl","sy", "ti")
5360     while scanner.nexttok() == "IHEOL":
5361         proutn(_("Information desired? "))
5362     scanner.chew()
5363     if scanner.token in requests:
5364         status(requests.index(scanner.token))
5365     else:
5366         prout(_("UNRECOGNIZED REQUEST. Legal requests are:"))
5367         prout(("  date, condition, position, lsupport, warpfactor,"))
5368         prout(("  energy, torpedoes, shields, klingons, system, time."))
5369
5370 def srscan():
5371     "Short-range scan."
5372     goodScan=True
5373     if damaged(DSRSENS):
5374         # Allow base's sensors if docked
5375         if game.condition != "docked":
5376             prout(_("   S.R. SENSORS DAMAGED!"))
5377             goodScan=False
5378         else:
5379             prout(_("  [Using Base's sensors]"))
5380     else:
5381         prout(_("     Short-range scan"))
5382     if goodScan and communicating():
5383         game.state.chart[game.quadrant.i][game.quadrant.j].klingons = game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons
5384         game.state.chart[game.quadrant.i][game.quadrant.j].starbase = game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase
5385         game.state.chart[game.quadrant.i][game.quadrant.j].stars = game.state.galaxy[game.quadrant.i][game.quadrant.j].stars
5386         game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
5387     prout("    1 2 3 4 5 6 7 8 9 10")
5388     if game.condition != "docked":
5389         newcnd()
5390     for i in range(QUADSIZE):
5391         proutn("%2d  " % (i+1))
5392         for j in range(QUADSIZE):
5393             sectscan(goodScan, i, j)
5394         skip(1)
5395
5396 def eta():
5397     "Use computer to get estimated time of arrival for a warp jump."
5398     w1 = Coord(); w2 = Coord()
5399     prompt = False
5400     if damaged(DCOMPTR):
5401         prout(_("COMPUTER DAMAGED, USE A POCKET CALCULATOR."))
5402         skip(1)
5403         return
5404     if scanner.nexttok() != "IHREAL":
5405         prompt = True
5406         scanner.chew()
5407         proutn(_("Destination quadrant and/or sector? "))
5408         if scanner.nexttok()!="IHREAL":
5409             huh()
5410             return
5411     w1.j = int(scanner.real-0.5)
5412     if scanner.nexttok() != "IHREAL":
5413         huh()
5414         return
5415     w1.i = int(scanner.real-0.5)
5416     if scanner.nexttok() == "IHREAL":
5417         w2.j = int(scanner.real-0.5)
5418         if scanner.nexttok() != "IHREAL":
5419             huh()
5420             return
5421         w2.i = int(scanner.real-0.5)
5422     else:
5423         if game.quadrant.j>w1.i:
5424             w2.i = 0
5425         else:
5426             w2.i=QUADSIZE-1
5427         if game.quadrant.i>w1.j:
5428             w2.j = 0
5429         else:
5430             w2.j=QUADSIZE-1
5431     if not w1.valid_quadrant() or not w2.valid_sector():
5432         huh()
5433         return
5434     dist = math.sqrt((w1.j-game.quadrant.j+(w2.j-game.sector.j)/(QUADSIZE*1.0))**2+
5435                      (w1.i-game.quadrant.i+(w2.i-game.sector.i)/(QUADSIZE*1.0))**2)
5436     wfl = False
5437     if prompt:
5438         prout(_("Answer \"no\" if you don't know the value:"))
5439     while True:
5440         scanner.chew()
5441         proutn(_("Time or arrival date? "))
5442         if scanner.nexttok()=="IHREAL":
5443             ttime = scanner.real
5444             if ttime > game.state.date:
5445                 ttime -= game.state.date # Actually a star date
5446             twarp=(math.floor(math.sqrt((10.0*dist)/ttime)*10.0)+1.0)/10.0
5447             if ttime <= 1e-10 or twarp > 10:
5448                 prout(_("We'll never make it, sir."))
5449                 scanner.chew()
5450                 return
5451             if twarp < 1.0:
5452                 twarp = 1.0
5453             break
5454         scanner.chew()
5455         proutn(_("Warp factor? "))
5456         if scanner.nexttok()== "IHREAL":
5457             wfl = True
5458             twarp = scanner.real
5459             if twarp<1.0 or twarp > 10.0:
5460                 huh()
5461                 return
5462             break
5463         prout(_("Captain, certainly you can give me one of these."))
5464     while True:
5465         scanner.chew()
5466         ttime = (10.0*dist)/twarp**2
5467         tpower = dist*twarp*twarp*twarp*(game.shldup+1)
5468         if tpower >= game.energy:
5469             prout(_("Insufficient energy, sir."))
5470             if not game.shldup or tpower > game.energy*2.0:
5471                 if not wfl:
5472                     return
5473                 proutn(_("New warp factor to try? "))
5474                 if scanner.nexttok() == "IHREAL":
5475                     wfl = True
5476                     twarp = scanner.real
5477                     if twarp<1.0 or twarp > 10.0:
5478                         huh()
5479                         return
5480                     continue
5481                 else:
5482                     scanner.chew()
5483                     skip(1)
5484                     return
5485             prout(_("But if you lower your shields,"))
5486             proutn(_("remaining"))
5487             tpower /= 2
5488         else:
5489             proutn(_("Remaining"))
5490         prout(_(" energy will be %.2f.") % (game.energy-tpower))
5491         if wfl:
5492             prout(_("And we will arrive at stardate %.2f.") % (game.state.date+ttime))
5493         elif twarp==1.0:
5494             prout(_("Any warp speed is adequate."))
5495         else:
5496             prout(_("Minimum warp needed is %.2f,") % (twarp))
5497             prout(_("and we will arrive at stardate %.2f.") % (game.state.date+ttime))
5498         if game.state.remtime < ttime:
5499             prout(_("Unfortunately, the Federation will be destroyed by then."))
5500         if twarp > 6.0:
5501             prout(_("You'll be taking risks at that speed, Captain"))
5502         if (game.isatb==1 and game.state.kscmdr == w1 and \
5503              scheduled(FSCDBAS)< ttime+game.state.date) or \
5504             (scheduled(FCDBAS)<ttime+game.state.date and game.battle == w1):
5505             prout(_("The starbase there will be destroyed by then."))
5506         proutn(_("New warp factor to try? "))
5507         if scanner.nexttok() == "IHREAL":
5508             wfl = True
5509             twarp = scanner.real
5510             if twarp<1.0 or twarp > 10.0:
5511                 huh()
5512                 return
5513         else:
5514             scanner.chew()
5515             skip(1)
5516             return
5517
5518 # Code from setup.c begins here
5519
5520 def prelim():
5521     "Issue a historically correct banner."
5522     skip(2)
5523     prout(_("-SUPER- STAR TREK"))
5524     skip(1)
5525 # From the FORTRAN original
5526 #    prout(_("Latest update-21 Sept 78"))
5527 #    skip(1)
5528
5529 def freeze(boss):
5530     "Save game."
5531     if boss:
5532         scanner.push("emsave.trk")
5533     key = scanner.nexttok()
5534     if key == "IHEOL":
5535         proutn(_("File name: "))
5536         key = scanner.nexttok()
5537     if key != "IHALPHA":
5538         huh()
5539         return
5540     if '.' not in scanner.token:
5541         scanner.token += ".trk"
5542     try:
5543         fp = open(scanner.token, "wb")
5544     except IOError:
5545         prout(_("Can't freeze game as file %s") % scanner.token)
5546         return
5547     pickle.dump(game, fp)
5548     fp.close()
5549     scanner.chew()
5550
5551 def thaw():
5552     "Retrieve saved game."
5553     global game
5554     game.passwd = None
5555     key = scanner.nexttok()
5556     if key == "IHEOL":
5557         proutn(_("File name: "))
5558         key = scanner.nexttok()
5559     if key != "IHALPHA":
5560         huh()
5561         return True
5562     if '.' not in scanner.token:
5563         scanner.token += ".trk"
5564     try:
5565         fp = open(scanner.token, "rb")
5566     except IOError:
5567         prout(_("Can't thaw game in %s") % scanner.token)
5568         return
5569     game = pickle.load(fp)
5570     fp.close()
5571     scanner.chew()
5572     return False
5573
5574 # I used <http://www.memory-alpha.org> to find planets
5575 # with references in ST:TOS.  Earth and the Alpha Centauri
5576 # Colony have been omitted.
5577 #
5578 # Some planets marked Class G and P here will be displayed as class M
5579 # because of the way planets are generated. This is a known bug.
5580 systnames = (
5581     # Federation Worlds
5582     _("Andoria (Fesoan)"),        # several episodes
5583     _("Tellar Prime (Miracht)"),        # TOS: "Journey to Babel"
5584     _("Vulcan (T'Khasi)"),        # many episodes
5585     _("Medusa"),                # TOS: "Is There in Truth No Beauty?"
5586     _("Argelius II (Nelphia)"),        # TOS: "Wolf in the Fold" ("IV" in BSD)
5587     _("Ardana"),                # TOS: "The Cloud Minders"
5588     _("Catulla (Cendo-Prae)"),        # TOS: "The Way to Eden"
5589     _("Gideon"),                # TOS: "The Mark of Gideon"
5590     _("Aldebaran III"),                # TOS: "The Deadly Years"
5591     _("Alpha Majoris I"),        # TOS: "Wolf in the Fold"
5592     _("Altair IV"),                # TOS: "Amok Time
5593     _("Ariannus"),                # TOS: "Let That Be Your Last Battlefield"
5594     _("Benecia"),                # TOS: "The Conscience of the King"
5595     _("Beta Niobe I (Sarpeidon)"),        # TOS: "All Our Yesterdays"
5596     _("Alpha Carinae II"),        # TOS: "The Ultimate Computer"
5597     _("Capella IV (Kohath)"),        # TOS: "Friday's Child" (Class G)
5598     _("Daran V"),                # TOS: "For the World is Hollow and I Have Touched the Sky"
5599     _("Deneb II"),                # TOS: "Wolf in the Fold" ("IV" in BSD)
5600     _("Eminiar VII"),                # TOS: "A Taste of Armageddon"
5601     _("Gamma Canaris IV"),        # TOS: "Metamorphosis"
5602     _("Gamma Tranguli VI (Vaalel)"),        # TOS: "The Apple"
5603     _("Ingraham B"),                # TOS: "Operation: Annihilate"
5604     _("Janus IV"),                # TOS: "The Devil in the Dark"
5605     _("Makus III"),                # TOS: "The Galileo Seven"
5606     _("Marcos XII"),                # TOS: "And the Children Shall Lead",
5607     _("Omega IV"),                # TOS: "The Omega Glory"
5608     _("Regulus V"),                # TOS: "Amok Time
5609     _("Deneva"),                # TOS: "Operation -- Annihilate!"
5610     # Worlds from BSD Trek
5611     _("Rigel II"),                # TOS: "Shore Leave" ("III" in BSD)
5612     _("Beta III"),                # TOS: "The Return of the Archons"
5613     _("Triacus"),                # TOS: "And the Children Shall Lead",
5614     _("Exo III"),                # TOS: "What Are Little Girls Made Of?" (Class P)
5615     #        # Others
5616     #    _("Hansen's Planet"),        # TOS: "The Galileo Seven"
5617     #    _("Taurus IV"),                # TOS: "The Galileo Seven" (class G)
5618     #    _("Antos IV (Doraphane)"),        # TOS: "Whom Gods Destroy", "Who Mourns for Adonais?"
5619     #    _("Izar"),                        # TOS: "Whom Gods Destroy"
5620     #    _("Tiburon"),                # TOS: "The Way to Eden"
5621     #    _("Merak II"),                # TOS: "The Cloud Minders"
5622     #    _("Coridan (Desotriana)"),        # TOS: "Journey to Babel"
5623     #    _("Iotia"),                # TOS: "A Piece of the Action"
5624 )
5625
5626 device = (
5627     _("S. R. Sensors"), \
5628     _("L. R. Sensors"), \
5629     _("Phasers"), \
5630     _("Photon Tubes"), \
5631     _("Life Support"), \
5632     _("Warp Engines"), \
5633     _("Impulse Engines"), \
5634     _("Shields"), \
5635     _("Subspace Radio"), \
5636     _("Shuttle Craft"), \
5637     _("Computer"), \
5638     _("Navigation System"), \
5639     _("Transporter"), \
5640     _("Shield Control"), \
5641     _("Death Ray"), \
5642     _("D. S. Probe"), \
5643     _("Cloaking Device"), \
5644 )
5645
5646 def setup():
5647     "Prepare to play, set up cosmos."
5648     w = Coord()
5649     #  Decide how many of everything
5650     if choose():
5651         return # frozen game
5652     # Prepare the Enterprise
5653     game.alldone = game.gamewon = game.shldchg = game.shldup = False
5654     game.ship = 'E'
5655     game.state.crew = FULLCREW
5656     game.energy = game.inenrg = 5000.0
5657     game.shield = game.inshld = 2500.0
5658     game.inlsr = 4.0
5659     game.lsupres = 4.0
5660     game.quadrant = randplace(GALSIZE)
5661     game.sector = randplace(QUADSIZE)
5662     game.torps = game.intorps = 10
5663     game.nprobes = randrange(2, 5)
5664     game.warpfac = 5.0
5665     for i in range(NDEVICES):
5666         game.damage[i] = 0.0
5667     # Set up assorted game parameters
5668     game.battle = Coord()
5669     game.state.date = game.indate = 100.0 * randreal(20, 51)
5670     game.nkinks = game.nhelp = game.casual = game.abandoned = 0
5671     game.iscate = game.resting = game.imine = game.icrystl = game.icraft = False
5672     game.isatb = game.state.nplankl = 0
5673     game.state.starkl = game.state.basekl = game.state.nworldkl = 0
5674     game.iscraft = "onship"
5675     game.landed = False
5676     game.alive = True
5677
5678     # the galaxy
5679     game.state.galaxy = fill2d(GALSIZE, lambda i_unused, j_unused: Quadrant())
5680     # the starchart
5681     game.state.chart = fill2d(GALSIZE, lambda i_unused, j_unused: Page())
5682
5683     game.state.planets = []      # Planet information
5684     game.state.baseq = []      # Base quadrant coordinates
5685     game.state.kcmdr = []      # Commander quadrant coordinates
5686     game.statekscmdr = Coord() # Supercommander quadrant coordinates
5687
5688     # Starchart is functional but we've never seen it
5689     game.lastchart = FOREVER
5690     # Put stars in the galaxy
5691     game.instar = 0
5692     for i in range(GALSIZE):
5693         for j in range(GALSIZE):
5694             # Can't have more stars per quadrant than fit in one decimal digit,
5695             # if we do the chart representation will break.
5696             k = randrange(1, min(10, QUADSIZE**2/10))
5697             game.instar += k
5698             game.state.galaxy[i][j].stars = k
5699     # Locate star bases in galaxy
5700     if game.idebug:
5701         prout("=== Allocating %d bases" % game.inbase)
5702     for i in range(game.inbase):
5703         while True:
5704             while True:
5705                 w = randplace(GALSIZE)
5706                 if not game.state.galaxy[w.i][w.j].starbase:
5707                     break
5708             contflag = False
5709             # C version: for (j = i-1; j > 0; j--)
5710             # so it did them in the opposite order.
5711             for j in range(1, i):
5712                 # Improved placement algorithm to spread out bases
5713                 distq = (w - game.state.baseq[j]).distance()
5714                 if distq < 6.0*(BASEMAX+1-game.inbase) and withprob(0.75):
5715                     contflag = True
5716                     if game.idebug:
5717                         prout("=== Abandoning base #%d at %s" % (i, w))
5718                     break
5719                 elif distq < 6.0 * (BASEMAX+1-game.inbase):
5720                     if game.idebug:
5721                         prout("=== Saving base #%d, close to #%d" % (i, j))
5722             if not contflag:
5723                 break
5724         if game.idebug:
5725             prout("=== Placing base #%d in quadrant %s" % (i, w))
5726         game.state.baseq.append(w)
5727         game.state.galaxy[w.i][w.j].starbase = game.state.chart[w.i][w.j].starbase = True
5728     # Position ordinary Klingon Battle Cruisers
5729     krem = game.inkling
5730     klumper = 0.25*game.skill*(9.0-game.length)+1.0
5731     if klumper > MAXKLQUAD:
5732         klumper = MAXKLQUAD
5733     while True:
5734         r = randreal()
5735         klump = (1.0 - r*r)*klumper
5736         if klump > krem:
5737             klump = krem
5738         krem -= klump
5739         while True:
5740             w = randplace(GALSIZE)
5741             if not game.state.galaxy[w.i][w.j].supernova and \
5742                game.state.galaxy[w.i][w.j].klingons + klump <= MAXKLQUAD:
5743                 break
5744         game.state.galaxy[w.i][w.j].klingons += int(klump)
5745         if krem <= 0:
5746             break
5747     # Position Klingon Commander Ships
5748     for i in range(game.incom):
5749         while True:
5750             w = randplace(GALSIZE)
5751             if not welcoming(w) or w in game.state.kcmdr:
5752                 continue
5753             if (game.state.galaxy[w.i][w.j].klingons or withprob(0.25)):
5754                 break
5755         game.state.galaxy[w.i][w.j].klingons += 1
5756         game.state.kcmdr.append(w)
5757     # Locate planets in galaxy
5758     for i in range(game.inplan):
5759         while True:
5760             w = randplace(GALSIZE)
5761             if game.state.galaxy[w.i][w.j].planet is None:
5762                 break
5763         new = Planet()
5764         new.quadrant = w
5765         new.crystals = "absent"
5766         if (game.options & OPTION_WORLDS) and i < NINHAB:
5767             new.pclass = "M"        # All inhabited planets are class M
5768             new.crystals = "absent"
5769             new.known = "known"
5770             new.name = systnames[i]
5771             new.inhabited = True
5772         else:
5773             new.pclass = ("M", "N", "O")[randrange(0, 3)]
5774             if withprob(0.33):
5775                 new.crystals = "present"
5776             new.known = "unknown"
5777             new.inhabited = False
5778         game.state.galaxy[w.i][w.j].planet = new
5779         game.state.planets.append(new)
5780     # Locate Romulans
5781     for i in range(game.state.nromrem):
5782         w = randplace(GALSIZE)
5783         game.state.galaxy[w.i][w.j].romulans += 1
5784     # Place the Super-Commander if needed
5785     if game.state.nscrem > 0:
5786         while True:
5787             w = randplace(GALSIZE)
5788             if welcoming(w):
5789                 break
5790         game.state.kscmdr = w
5791         game.state.galaxy[w.i][w.j].klingons += 1
5792     # Initialize times for extraneous events
5793     schedule(FSNOVA, expran(0.5 * game.intime))
5794     schedule(FTBEAM, expran(1.5 * (game.intime / len(game.state.kcmdr))))
5795     schedule(FSNAP, randreal(1.0, 2.0)) # Force an early snapshot
5796     schedule(FBATTAK, expran(0.3*game.intime))
5797     unschedule(FCDBAS)
5798     if game.state.nscrem:
5799         schedule(FSCMOVE, 0.2777)
5800     else:
5801         unschedule(FSCMOVE)
5802     unschedule(FSCDBAS)
5803     unschedule(FDSPROB)
5804     if (game.options & OPTION_WORLDS) and game.skill >= SKILL_GOOD:
5805         schedule(FDISTR, expran(1.0 + game.intime))
5806     else:
5807         unschedule(FDISTR)
5808     unschedule(FENSLV)
5809     unschedule(FREPRO)
5810     # Place thing (in tournament game, we don't want one!)
5811     # New in SST2K: never place the Thing near a starbase.
5812     # This makes sense and avoids a special case in the old code.
5813     global thing
5814     if game.tourn is None:
5815         while True:
5816             thing = randplace(GALSIZE)
5817             if thing not in game.state.baseq:
5818                 break
5819     skip(2)
5820     game.state.snap = False
5821     if game.skill == SKILL_NOVICE:
5822         prout(_("It is stardate %d. The Federation is being attacked by") % int(game.state.date))
5823         prout(_("a deadly Klingon invasion force. As captain of the United"))
5824         prout(_("Starship U.S.S. Enterprise, it is your mission to seek out"))
5825         prout(_("and destroy this invasion force of %d battle cruisers.") % ((game.inkling + game.incom + game.inscom)))
5826         prout(_("You have an initial allotment of %d stardates to complete") % int(game.intime))
5827         prout(_("your mission.  As you proceed you may be given more time."))
5828         skip(1)
5829         prout(_("You will have %d supporting starbases.") % (game.inbase))
5830         proutn(_("Starbase locations-  "))
5831     else:
5832         prout(_("Stardate %d.") % int(game.state.date))
5833         skip(1)
5834         prout(_("%d Klingons.") % (game.inkling + game.incom + game.inscom))
5835         prout(_("An unknown number of Romulans."))
5836         if game.state.nscrem:
5837             prout(_("And one (GULP) Super-Commander."))
5838         prout(_("%d stardates.") % int(game.intime))
5839         proutn(_("%d starbases in ") % game.inbase)
5840     for i in range(game.inbase):
5841         proutn(repr(game.state.baseq[i]))
5842         proutn("  ")
5843     skip(2)
5844     proutn(_("The Enterprise is currently in Quadrant %s") % game.quadrant)
5845     proutn(_(" Sector %s") % game.sector)
5846     skip(2)
5847     prout(_("Good Luck!"))
5848     if game.state.nscrem:
5849         prout(_("  YOU'LL NEED IT."))
5850     waitfor()
5851     clrscr()
5852     setwnd(message_window)
5853     newqad()
5854     if len(game.enemies) - (thing == game.quadrant) - (game.tholian != None):
5855         game.shldup = True
5856     if game.neutz:        # bad luck to start in a Romulan Neutral Zone
5857         attack(torps_ok=False)
5858
5859 def choose():
5860     "Choose your game type."
5861     while True:
5862         game.tourn = game.length = 0
5863         game.thawed = False
5864         game.skill = SKILL_NONE
5865         # Do not chew here, we want to use command-line tokens
5866         if not scanner.inqueue: # Can start with command line options
5867             proutn(_("Would you like a regular, tournament, or saved game? "))
5868         scanner.nexttok()
5869         if scanner.sees("tournament"):
5870             while scanner.nexttok() == "IHEOL":
5871                 proutn(_("Type in tournament number-"))
5872             if scanner.real == 0:
5873                 scanner.chew()
5874                 continue # We don't want a blank entry
5875             game.tourn = int(round(scanner.real))
5876             random.seed(scanner.real)
5877             if logfp:
5878                 logfp.write("# random.seed(%d)\n" % scanner.real)
5879             break
5880         if scanner.sees("saved") or scanner.sees("frozen"):
5881             if thaw():
5882                 continue
5883             scanner.chew()
5884             if game.passwd is None:
5885                 continue
5886             if not game.alldone:
5887                 game.thawed = True # No plaque if not finished
5888             report()
5889             waitfor()
5890             return True
5891         if scanner.sees("regular"):
5892             break
5893         proutn(_("What is \"%s\"? ") % scanner.token)
5894         scanner.chew()
5895     while game.length==0 or game.skill==SKILL_NONE:
5896         if scanner.nexttok() == "IHALPHA":
5897             if scanner.sees("short"):
5898                 game.length = 1
5899             elif scanner.sees("medium"):
5900                 game.length = 2
5901             elif scanner.sees("long"):
5902                 game.length = 4
5903             elif scanner.sees("novice"):
5904                 game.skill = SKILL_NOVICE
5905             elif scanner.sees("fair"):
5906                 game.skill = SKILL_FAIR
5907             elif scanner.sees("good"):
5908                 game.skill = SKILL_GOOD
5909             elif scanner.sees("expert"):
5910                 game.skill = SKILL_EXPERT
5911             elif scanner.sees("emeritus"):
5912                 game.skill = SKILL_EMERITUS
5913             else:
5914                 proutn(_("What is \""))
5915                 proutn(scanner.token)
5916                 prout("\"?")
5917         else:
5918             scanner.chew()
5919             if game.length==0:
5920                 proutn(_("Would you like a Short, Medium, or Long game? "))
5921             elif game.skill == SKILL_NONE:
5922                 proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
5923     # Choose game options -- added by ESR for SST2K
5924     if scanner.nexttok() != "IHALPHA":
5925         scanner.chew()
5926         proutn(_("Choose your game style (plain, almy, fancy or just press enter): "))
5927         scanner.nexttok()
5928     if scanner.sees("plain"):
5929         # Approximates the UT FORTRAN version.
5930         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)
5931         game.options |= OPTION_PLAIN
5932     elif scanner.sees("almy"):
5933         # Approximates Tom Almy's version.
5934         game.options &=~ (OPTION_THINGY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR)
5935         game.options |= OPTION_ALMY
5936     elif scanner.sees("fancy") or scanner.sees("\n"):
5937         pass
5938     elif len(scanner.token):
5939         proutn(_("What is \"%s\"?") % scanner.token)
5940     setpassword()
5941     if game.passwd == "debug":
5942         game.idebug = True
5943         prout("=== Debug mode enabled.")
5944     # Use parameters to generate initial values of things
5945     game.damfac = 0.5 * game.skill
5946     game.inbase = randrange(BASEMIN, BASEMAX+1)
5947     game.inplan = 0
5948     if game.options & OPTION_PLANETS:
5949         game.inplan += randrange(MAXUNINHAB/2, MAXUNINHAB+1)
5950     if game.options & OPTION_WORLDS:
5951         game.inplan += int(NINHAB)
5952     game.state.nromrem = game.inrom = randrange(2 * game.skill)
5953     game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR)
5954     game.state.remtime = 7.0 * game.length
5955     game.intime = game.state.remtime
5956     game.state.remkl = game.inkling = 2.0*game.intime*((game.skill+1 - 2*randreal())*game.skill*0.1+.15)
5957     game.incom = min(MINCMDR, int(game.skill + 0.0625*game.inkling*randreal()))
5958     game.state.remres = (game.inkling+4*game.incom)*game.intime
5959     game.inresor = game.state.remres
5960     if game.inkling > 50:
5961         game.inbase += 1
5962     return False
5963
5964 def dropin(iquad=None):
5965     "Drop a feature on a random dot in the current quadrant."
5966     while True:
5967         w = randplace(QUADSIZE)
5968         if game.quad[w.i][w.j] == '.':
5969             break
5970     if iquad is not None:
5971         game.quad[w.i][w.j] = iquad
5972     return w
5973
5974 def newcnd():
5975     "Update our alert status."
5976     game.condition = "green"
5977     if game.energy < 1000.0:
5978         game.condition = "yellow"
5979     if game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons or game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans:
5980         game.condition = "red"
5981     if not game.alive:
5982         game.condition="dead"
5983
5984 def newkling():
5985     "Drop new Klingon into current quadrant."
5986     return Enemy('K', loc=dropin(), power=randreal(300,450)+25.0*game.skill)
5987
5988 def sortenemies():
5989     "Sort enemies by distance so 'nearest' is meaningful."
5990     game.enemies.sort(key=lambda x: x.kdist)
5991
5992 def newqad():
5993     "Set up a new state of quadrant, for when we enter or re-enter it."
5994     game.justin = True
5995     game.iplnet = None
5996     game.neutz = game.inorbit = game.landed = False
5997     game.ientesc = game.iseenit = game.isviolreported = False
5998     # Create a blank quadrant
5999     game.quad = fill2d(QUADSIZE, lambda i, j: '.')
6000     if game.iscate:
6001         # Attempt to escape Super-commander, so tbeam back!
6002         game.iscate = False
6003         game.ientesc = True
6004     q = game.state.galaxy[game.quadrant.i][game.quadrant.j]
6005     # cope with supernova
6006     if q.supernova:
6007         return
6008     game.klhere = q.klingons
6009     game.irhere = q.romulans
6010     # Position Starship
6011     game.quad[game.sector.i][game.sector.j] = game.ship
6012     game.enemies = []
6013     if q.klingons:
6014         # Position ordinary Klingons
6015         for _i in range(game.klhere):
6016             newkling()
6017         # If we need a commander, promote a Klingon
6018         for cmdr in game.state.kcmdr:
6019             if cmdr == game.quadrant:
6020                 e = game.enemies[game.klhere-1]
6021                 game.quad[e.location.i][e.location.j] = 'C'
6022                 e.power = randreal(950,1350) + 50.0*game.skill
6023                 break
6024         # If we need a super-commander, promote a Klingon
6025         if game.quadrant == game.state.kscmdr:
6026             e = game.enemies[0]
6027             game.quad[e.location.i][e.location.j] = 'S'
6028             e.power = randreal(1175.0,  1575.0) + 125.0*game.skill
6029             game.iscate = (game.state.remkl > 1)
6030     # Put in Romulans if needed
6031     for _i in range(q.romulans):
6032         Enemy('R', loc=dropin(), power=randreal(400.0,850.0)+50.0*game.skill)
6033     # If quadrant needs a starbase, put it in
6034     if q.starbase:
6035         game.base = dropin('B')
6036     # If quadrant needs a planet, put it in
6037     if q.planet:
6038         game.iplnet = q.planet
6039         if not q.planet.inhabited:
6040             game.plnet = dropin('P')
6041         else:
6042             game.plnet = dropin('@')
6043     # Check for condition
6044     newcnd()
6045     # Check for RNZ
6046     if game.irhere > 0 and game.klhere == 0:
6047         game.neutz = True
6048         if not damaged(DRADIO):
6049             skip(1)
6050             prout(_("LT. Uhura- \"Captain, an urgent message."))
6051             prout(_("  I'll put it on audio.\"  CLICK"))
6052             skip(1)
6053             prout(_("INTRUDER! YOU HAVE VIOLATED THE ROMULAN NEUTRAL ZONE."))
6054             prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
6055     # Put in THING if needed
6056     if thing == game.quadrant:
6057         Enemy(etype='?', loc=dropin(),
6058               power=randreal(6000,6500.0)+250.0*game.skill)
6059         if not damaged(DSRSENS):
6060             skip(1)
6061             prout(_("Mr. Spock- \"Captain, this is most unusual."))
6062             prout(_("    Please examine your short-range scan.\""))
6063     # Decide if quadrant needs a Tholian; lighten up if skill is low
6064     if game.options & OPTION_THOLIAN:
6065         if (game.skill < SKILL_GOOD and withprob(0.02)) or \
6066             (game.skill == SKILL_GOOD and withprob(0.05)) or \
6067             (game.skill > SKILL_GOOD and withprob(0.08)):
6068             w = Coord()
6069             while True:
6070                 w.i = withprob(0.5) * (QUADSIZE-1)
6071                 w.j = withprob(0.5) * (QUADSIZE-1)
6072                 if game.quad[w.i][w.j] == '.':
6073                     break
6074             game.tholian = Enemy(etype='T', loc=w,
6075                                  power=randrange(100, 500) + 25.0*game.skill)
6076             # Reserve unoccupied corners
6077             if game.quad[0][0]=='.':
6078                 game.quad[0][0] = 'X'
6079             if game.quad[0][QUADSIZE-1]=='.':
6080                 game.quad[0][QUADSIZE-1] = 'X'
6081             if game.quad[QUADSIZE-1][0]=='.':
6082                 game.quad[QUADSIZE-1][0] = 'X'
6083             if game.quad[QUADSIZE-1][QUADSIZE-1]=='.':
6084                 game.quad[QUADSIZE-1][QUADSIZE-1] = 'X'
6085     sortenemies()
6086     # And finally the stars
6087     for _i in range(q.stars):
6088         dropin('*')
6089     # Put in a few black holes
6090     for _i in range(1, 3+1):
6091         if withprob(0.5):
6092             dropin(' ')
6093     # Take out X's in corners if Tholian present
6094     if game.tholian:
6095         if game.quad[0][0]=='X':
6096             game.quad[0][0] = '.'
6097         if game.quad[0][QUADSIZE-1]=='X':
6098             game.quad[0][QUADSIZE-1] = '.'
6099         if game.quad[QUADSIZE-1][0]=='X':
6100             game.quad[QUADSIZE-1][0] = '.'
6101         if game.quad[QUADSIZE-1][QUADSIZE-1]=='X':
6102             game.quad[QUADSIZE-1][QUADSIZE-1] = '.'
6103
6104 def setpassword():
6105     "Set the self-destruct password."
6106     if game.options & OPTION_PLAIN:
6107         while True:
6108             scanner.chew()
6109             proutn(_("Please type in a secret password- "))
6110             scanner.nexttok()
6111             game.passwd = scanner.token
6112             if game.passwd != None:
6113                 break
6114     else:
6115         game.passwd = ""
6116         game.passwd += chr(ord('a')+randrange(26))
6117         game.passwd += chr(ord('a')+randrange(26))
6118         game.passwd += chr(ord('a')+randrange(26))
6119
6120 # Code from sst.c begins here
6121
6122 commands = [
6123     ("SRSCAN",           OPTION_TTY),
6124     ("STATUS",           OPTION_TTY),
6125     ("REQUEST",          OPTION_TTY),
6126     ("LRSCAN",           OPTION_TTY),
6127     ("PHASERS",          0),
6128     ("TORPEDO",          0),
6129     ("PHOTONS",          0),
6130     ("MOVE",             0),
6131     ("SHIELDS",          0),
6132     ("DOCK",             0),
6133     ("DAMAGES",          0),
6134     ("CHART",            0),
6135     ("IMPULSE",          0),
6136     ("REST",             0),
6137     ("WARP",             0),
6138     ("SENSORS",          OPTION_PLANETS),
6139     ("ORBIT",            OPTION_PLANETS),
6140     ("TRANSPORT",        OPTION_PLANETS),
6141     ("MINE",             OPTION_PLANETS),
6142     ("CRYSTALS",         OPTION_PLANETS),
6143     ("SHUTTLE",          OPTION_PLANETS),
6144     ("PLANETS",          OPTION_PLANETS),
6145     ("REPORT",           0),
6146     ("COMPUTER",         0),
6147     ("COMMANDS",         0),
6148     ("EMEXIT",           0),
6149     ("PROBE",            OPTION_PROBE),
6150     ("SAVE",             0),
6151     ("FREEZE",           0),        # Synonym for SAVE
6152     ("ABANDON",          0),
6153     ("DESTRUCT",         0),
6154     ("DEATHRAY",         0),
6155     ("CAPTURE",          OPTION_CAPTURE),
6156     ("CLOAK",            OPTION_CLOAK),
6157     ("DEBUG",            0),
6158     ("MAYDAY",           0),
6159     ("SOS",              0),        # Synonym for MAYDAY
6160     ("CALL",             0),        # Synonym for MAYDAY
6161     ("QUIT",             0),
6162     ("HELP",             0),
6163     ("SCORE",            0),
6164     ("",                 0),
6165 ]
6166
6167 def listCommands():
6168     "Generate a list of legal commands."
6169     prout(_("LEGAL COMMANDS ARE:"))
6170     emitted = 0
6171     for (key, opt) in commands:
6172         if not opt or (opt & game.options):
6173             proutn("%-12s " % key)
6174             emitted += 1
6175             if emitted % 5 == 4:
6176                 skip(1)
6177     skip(1)
6178
6179 def helpme():
6180     "Browse on-line help."
6181     key = scanner.nexttok()
6182     while True:
6183         if key == "IHEOL":
6184             setwnd(prompt_window)
6185             proutn(_("Help on what command? "))
6186             key = scanner.nexttok()
6187         setwnd(message_window)
6188         if key == "IHEOL":
6189             return
6190         cmds = [x[0] for x in commands]
6191         if scanner.token.upper() in cmds or scanner.token.upper() == "ABBREV":
6192             break
6193         skip(1)
6194         listCommands()
6195         key = "IHEOL"
6196         scanner.chew()
6197         skip(1)
6198     cmd = scanner.token.upper()
6199     for directory in docpath:
6200         try:
6201             fp = open(os.path.join(directory, "sst.doc"), "r")
6202             break
6203         except IOError:
6204             pass
6205     else:
6206         prout(_("Spock-  \"Captain, that information is missing from the"))
6207         prout(_("   computer. You need to find sst.doc and put it somewhere"))
6208         proutn(_("   in these directories: %s") % ":".join(docpath))
6209         prout(".\"")
6210         # This used to continue: "You need to find SST.DOC and put
6211         # it in the current directory."
6212         return
6213     while True:
6214         linebuf = fp.readline()
6215         if linebuf == '':
6216             prout(_("Spock- \"Captain, there is no information on that command.\""))
6217             fp.close()
6218             return
6219         if linebuf[0] == '%' and linebuf[1] == '%' and linebuf[2] == ' ':
6220             linebuf = linebuf[3:].strip()
6221             if cmd.upper() == linebuf:
6222                 break
6223     skip(1)
6224     prout(_("Spock- \"Captain, I've found the following information:\""))
6225     skip(1)
6226     while True:
6227         linebuf = fp.readline()
6228         if "******" in linebuf:
6229             break
6230         proutn(linebuf)
6231     fp.close()
6232
6233 def makemoves():
6234     "Command-interpretation loop."
6235     def checkviol():
6236         if game.irhere and game.state.date >= ALGERON and not game.isviolreported and game.iscloaked:
6237             prout(_("The Romulan ship discovers you are breaking the Treaty of Algeron!"))
6238             game.ncviol += 1
6239             game.isviolreported = True
6240     while True:         # command loop
6241         drawmaps(1)
6242         while True:        # get a command
6243             hitme = False
6244             game.optime = game.justin = False
6245             scanner.chew()
6246             setwnd(prompt_window)
6247             clrscr()
6248             proutn("COMMAND> ")
6249             if scanner.nexttok() == "IHEOL":
6250                 if game.options & OPTION_CURSES:
6251                     makechart()
6252                 continue
6253             elif scanner.token == "":
6254                 continue
6255             game.ididit = False
6256             clrscr()
6257             setwnd(message_window)
6258             clrscr()
6259             abandon_passed = False
6260             cmd = ""    # Force cmd to persist after loop
6261             opt = 0     # Force opt to persist after loop
6262             for (cmd, opt) in commands:
6263                 # commands after ABANDON cannot be abbreviated
6264                 if cmd == "ABANDON":
6265                     abandon_passed = True
6266                 if cmd == scanner.token.upper() or (not abandon_passed \
6267                         and cmd.startswith(scanner.token.upper())):
6268                     break
6269             if cmd == "":
6270                 listCommands()
6271                 continue
6272             elif opt and not (opt & game.options):
6273                 huh()
6274             else:
6275                 break
6276         if game.options & OPTION_CURSES:
6277             prout("COMMAND> %s" % cmd)
6278         if cmd == "SRSCAN":                # srscan
6279             srscan()
6280         elif cmd == "STATUS":                # status
6281             status()
6282         elif cmd == "REQUEST":                # status request
6283             request()
6284         elif cmd == "LRSCAN":                # long range scan
6285             lrscan(silent=False)
6286         elif cmd == "PHASERS":                # phasers
6287             phasers()
6288             if game.ididit:
6289                 checkviol()
6290                 hitme = True
6291         elif cmd in ("TORPEDO", "PHOTONS"):        # photon torpedos
6292             torps()
6293             if game.ididit:
6294                 checkviol()
6295                 hitme = True
6296         elif cmd == "MOVE":                # move under warp
6297             warp(wcourse=None, involuntary=False)
6298         elif cmd == "SHIELDS":                # shields
6299             doshield(shraise=False)
6300             if game.ididit:
6301                 hitme = True
6302                 game.shldchg = False
6303         elif cmd == "DOCK":                # dock at starbase
6304             dock(True)
6305             if game.ididit:
6306                 attack(torps_ok=False)
6307         elif cmd == "DAMAGES":                # damage reports
6308             damagereport()
6309         elif cmd == "CHART":                # chart
6310             makechart()
6311         elif cmd == "IMPULSE":                # impulse
6312             impulse()
6313         elif cmd == "REST":                # rest
6314             wait()
6315             if game.ididit:
6316                 hitme = True
6317         elif cmd == "WARP":                # warp
6318             setwarp()
6319         elif cmd == "SENSORS":                # sensors
6320             sensor()
6321         elif cmd == "ORBIT":                # orbit
6322             orbit()
6323             if game.ididit:
6324                 hitme = True
6325         elif cmd == "TRANSPORT":                # transport "beam"
6326             beam()
6327         elif cmd == "MINE":                # mine
6328             mine()
6329             if game.ididit:
6330                 hitme = True
6331         elif cmd == "CRYSTALS":                # crystals
6332             usecrystals()
6333             if game.ididit:
6334                 hitme = True
6335         elif cmd == "SHUTTLE":                # shuttle
6336             shuttle()
6337             if game.ididit:
6338                 hitme = True
6339         elif cmd == "PLANETS":                # Planet list
6340             survey()
6341         elif cmd == "REPORT":                # Game Report
6342             report()
6343         elif cmd == "COMPUTER":                # use COMPUTER!
6344             eta()
6345         elif cmd == "COMMANDS":
6346             listCommands()
6347         elif cmd == "EMEXIT":                # Emergency exit
6348             clrscr()                        # Hide screen
6349             freeze(True)                # forced save
6350             raise SystemExit(1)                # And quick exit
6351         elif cmd == "PROBE":
6352             probe()                        # Launch probe
6353             if game.ididit:
6354                 hitme = True
6355         elif cmd == "ABANDON":                # Abandon Ship
6356             abandon()
6357         elif cmd == "DESTRUCT":                # Self Destruct
6358             selfdestruct()
6359         elif cmd == "SAVE":                # Save Game
6360             freeze(False)
6361             clrscr()
6362             if game.skill > SKILL_GOOD:
6363                 prout(_("WARNING--Saved games produce no plaques!"))
6364         elif cmd == "DEATHRAY":                # Try a desparation measure
6365             deathray()
6366             if game.ididit:
6367                 hitme = True
6368         elif cmd == "CAPTURE":
6369             capture()
6370         elif cmd == "CLOAK":
6371             cloak()
6372         elif cmd == "DEBUGCMD":                # What do we want for debug???
6373             debugme()
6374         elif cmd == "MAYDAY":                # Call for help
6375             mayday()
6376             if game.ididit:
6377                 hitme = True
6378         elif cmd == "QUIT":
6379             game.alldone = True                # quit the game
6380         elif cmd == "HELP":
6381             helpme()                        # get help
6382         elif cmd == "SCORE":
6383             score()                         # see current score
6384         while True:
6385             if game.alldone:
6386                 break                # Game has ended
6387             if game.optime != 0.0:
6388                 events()
6389                 if game.alldone:
6390                     break        # Events did us in
6391             if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
6392                 atover(False)
6393                 continue
6394             if hitme and not game.justin:
6395                 attack(torps_ok=True)
6396                 if game.alldone:
6397                     break
6398                 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
6399                     atover(False)
6400                     hitme = True
6401                     continue
6402             break
6403         if game.alldone:
6404             break
6405     if game.idebug:
6406         prout("=== Ending")
6407
6408 def cramen(ch):
6409     "Emit the name of an enemy or feature."
6410     if   ch == 'R': s = _("Romulan")
6411     elif ch == 'K': s = _("Klingon")
6412     elif ch == 'C': s = _("Commander")
6413     elif ch == 'S': s = _("Super-commander")
6414     elif ch == '*': s = _("Star")
6415     elif ch == 'P': s = _("Planet")
6416     elif ch == 'B': s = _("Starbase")
6417     elif ch == ' ': s = _("Black hole")
6418     elif ch == 'T': s = _("Tholian")
6419     elif ch == '#': s = _("Tholian web")
6420     elif ch == '?': s = _("Stranger")
6421     elif ch == '@': s = _("Inhabited World")
6422     else: s = "Unknown??"
6423     return s
6424
6425 def crmena(loud, enemy, loctype, w):
6426     "Emit the name of an enemy and his location."
6427     buf = ""
6428     if loud:
6429         buf += "***"
6430     buf += cramen(enemy) + _(" at ")
6431     if loctype == "quadrant":
6432         buf += _("Quadrant ")
6433     elif loctype == "sector":
6434         buf += _("Sector ")
6435     return buf + repr(w)
6436
6437 def crmshp():
6438     "Emit our ship name."
6439     return{'E':_("Enterprise"),'F':_("Faerie Queene")}.get(game.ship,"Ship???")
6440
6441 def stars():
6442     "Emit a line of stars"
6443     prouts("******************************************************")
6444     skip(1)
6445
6446 def expran(avrage):
6447     return -avrage*math.log(1e-7 + randreal())
6448
6449 def randplace(size):
6450     "Choose a random location."
6451     w = Coord()
6452     w.i = randrange(size)
6453     w.j = randrange(size)
6454     return w
6455
6456 class sstscanner:
6457     def __init__(self):
6458         self.type = None
6459         self.token = None
6460         self.real = 0.0
6461         self.inqueue = []
6462     def nexttok(self):
6463         # Get a token from the user
6464         self.real = 0.0
6465         self.token = ''
6466         # Fill the token quue if nothing here
6467         while not self.inqueue:
6468             sline = cgetline()
6469             if curwnd==prompt_window:
6470                 clrscr()
6471                 setwnd(message_window)
6472                 clrscr()
6473             if sline == '':
6474                 return None
6475             if not sline:
6476                 continue
6477             else:
6478                 self.inqueue = sline.lstrip().split() + ["\n"]
6479         # From here on in it's all looking at the queue
6480         self.token = self.inqueue.pop(0)
6481         if self.token == "\n":
6482             self.type = "IHEOL"
6483             return "IHEOL"
6484         try:
6485             self.real = float(self.token)
6486             self.type = "IHREAL"
6487             return "IHREAL"
6488         except ValueError:
6489             pass
6490         # Treat as alpha
6491         self.token = self.token.lower()
6492         self.type = "IHALPHA"
6493         self.real = None
6494         return "IHALPHA"
6495     def append(self, tok):
6496         self.inqueue.append(tok)
6497     def push(self, tok):
6498         self.inqueue.insert(0, tok)
6499     def waiting(self):
6500         return self.inqueue
6501     def chew(self):
6502         # Demand input for next scan
6503         self.inqueue = []
6504         self.real = self.token = None
6505     def sees(self, s):
6506         # compares s to item and returns true if it matches to the length of s
6507         return s.startswith(self.token)
6508     def int(self):
6509         # Round token value to nearest integer
6510         return int(round(self.real))
6511     def getcoord(self):
6512         s = Coord()
6513         self.nexttok()
6514         if self.type != "IHREAL":
6515             huh()
6516             return None
6517         s.i = self.int()-1
6518         self.nexttok()
6519         if self.type != "IHREAL":
6520             huh()
6521             return None
6522         s.j = self.int()-1
6523         return s
6524     def __repr__(self):
6525         return "<sstcanner: token=%s, type=%s, queue=%s>" % (self.token, self.type, self.inqueue)
6526
6527 def ja():
6528     "Yes-or-no confirmation."
6529     scanner.chew()
6530     while True:
6531         scanner.nexttok()
6532         if scanner.token == 'y':
6533             return True
6534         if scanner.token == 'n':
6535             return False
6536         scanner.chew()
6537         proutn(_("Please answer with \"y\" or \"n\": "))
6538
6539 def huh():
6540     "Complain about unparseable input."
6541     scanner.chew()
6542     skip(1)
6543     prout(_("Beg your pardon, Captain?"))
6544
6545 def debugme():
6546     "Access to the internals for debugging."
6547     proutn("Reset levels? ")
6548     if ja():
6549         if game.energy < game.inenrg:
6550             game.energy = game.inenrg
6551         game.shield = game.inshld
6552         game.torps = game.intorps
6553         game.lsupres = game.inlsr
6554     proutn("Reset damage? ")
6555     if ja():
6556         for i in range(NDEVICES):
6557             if game.damage[i] > 0.0:
6558                 game.damage[i] = 0.0
6559     proutn("Toggle debug flag? ")
6560     if ja():
6561         game.idebug = not game.idebug
6562         if game.idebug:
6563             prout("Debug output ON")
6564         else:
6565             prout("Debug output OFF")
6566     proutn("Cause selective damage? ")
6567     if ja():
6568         for i in range(NDEVICES):
6569             proutn("Kill %s?" % device[i])
6570             scanner.chew()
6571             key = scanner.nexttok()
6572             if key == "IHALPHA" and scanner.sees("y"):
6573                 game.damage[i] = 10.0
6574     proutn("Examine/change events? ")
6575     if ja():
6576         ev = Event()
6577         w = Coord()
6578         legends = {
6579             FSNOVA:  "Supernova       ",
6580             FTBEAM:  "T Beam          ",
6581             FSNAP:   "Snapshot        ",
6582             FBATTAK: "Base Attack     ",
6583             FCDBAS:  "Base Destroy    ",
6584             FSCMOVE: "SC Move         ",
6585             FSCDBAS: "SC Base Destroy ",
6586             FDSPROB: "Probe Move      ",
6587             FDISTR:  "Distress Call   ",
6588             FENSLV:  "Enslavement     ",
6589             FREPRO:  "Klingon Build   ",
6590         }
6591         for i in range(1, NEVENTS):
6592             proutn(legends[i])
6593             if is_scheduled(i):
6594                 proutn("%.2f" % (scheduled(i)-game.state.date))
6595                 if i == FENSLV or i == FREPRO:
6596                     ev = findevent(i)
6597                     proutn(" in %s" % ev.quadrant)
6598             else:
6599                 proutn("never")
6600             proutn("? ")
6601             scanner.chew()
6602             key = scanner.nexttok()
6603             if key == 'n':
6604                 unschedule(i)
6605                 scanner.chew()
6606             elif key == "IHREAL":
6607                 ev = schedule(i, scanner.real)
6608                 if i == FENSLV or i == FREPRO:
6609                     scanner.chew()
6610                     proutn("In quadrant- ")
6611                     key = scanner.nexttok()
6612                     # "IHEOL" says to leave coordinates as they are
6613                     if key != "IHEOL":
6614                         if key != "IHREAL":
6615                             prout("Event %d canceled, no x coordinate." % (i))
6616                             unschedule(i)
6617                             continue
6618                         w.i = int(round(scanner.real))
6619                         key = scanner.nexttok()
6620                         if key != "IHREAL":
6621                             prout("Event %d canceled, no y coordinate." % (i))
6622                             unschedule(i)
6623                             continue
6624                         w.j = int(round(scanner.real))
6625                         ev.quadrant = w
6626         scanner.chew()
6627     proutn("Induce supernova here? ")
6628     if ja():
6629         game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova = True
6630         atover(True)
6631
6632 if __name__ == '__main__':
6633     try:
6634         #global line, thing, game
6635         game = None
6636         thing = Thingy()
6637         game = Gamestate()
6638         game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY)
6639         if os.getenv("TERM"):
6640             game.options |= OPTION_CURSES
6641         else:
6642             game.options |= OPTION_TTY
6643         seed = int(time.time())
6644         (options, arguments) = getopt.getopt(sys.argv[1:], "r:s:txV")
6645         replay = False
6646         for (switch, val) in options:
6647             if switch == '-r':
6648                 try:
6649                     replayfp = open(val, "r")
6650                 except IOError:
6651                     sys.stderr.write("sst: can't open replay file %s\n" % val)
6652                     raise SystemExit(1)
6653                 try:
6654                     line = replayfp.readline().strip()
6655                     (leader, __, seed) = line.split()
6656                     seed = eval(seed)
6657                     sys.stderr.write("sst2k: seed set to %s\n" % seed)
6658                     line = replayfp.readline().strip()
6659                     arguments += line.split()[2:]
6660                     replay = True
6661                 except ValueError:
6662                     sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
6663                     raise SystemExit(1)
6664                 game.options |= OPTION_TTY
6665                 game.options &=~ OPTION_CURSES
6666             elif switch == '-s':
6667                 seed = int(val)
6668             elif switch == '-t':
6669                 game.options |= OPTION_TTY
6670                 game.options &=~ OPTION_CURSES
6671             elif switch == '-x':
6672                 game.idebug = True
6673             elif switch == '-V':
6674                 print("SST2K", version)
6675                 raise SystemExit(0)
6676             else:
6677                 sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n")
6678                 raise SystemExit(1)
6679         # where to save the input in case of bugs
6680         if "TMPDIR" in os.environ:
6681             tmpdir = os.environ['TMPDIR']
6682         else:
6683             tmpdir = "/tmp"
6684         try:
6685             logfp = open(os.path.join(tmpdir, "sst-input.log"), "w")
6686         except IOError:
6687             sys.stderr.write("sst: warning, can't open logfile\n")
6688             sys.exit(1)
6689         if logfp:
6690             logfp.write("# seed %s\n" % seed)
6691             logfp.write("# options %s\n" % " ".join(arguments))
6692             logfp.write("# SST2K version %s\n" % version)
6693             logfp.write("# recorded by %s@%s on %s\n" % \
6694                     (getpass.getuser(),socket.gethostname(),time.ctime()))
6695         random.seed(seed)
6696         scanner = sstscanner()
6697         for arg in arguments:
6698             scanner.append(arg)
6699         try:
6700             iostart()
6701             while True: # Play a game
6702                 setwnd(fullscreen_window)
6703                 clrscr()
6704                 prelim()
6705                 setup()
6706                 if game.alldone:
6707                     score()
6708                     game.alldone = False
6709                 else:
6710                     makemoves()
6711                 if replay:
6712                     break
6713                 skip(1)
6714                 stars()
6715                 skip(1)
6716                 if game.tourn and game.alldone:
6717                     proutn(_("Do you want your score recorded?"))
6718                     if ja():
6719                         scanner.chew()
6720                         scanner.push("\n")
6721                         freeze(False)
6722                 scanner.chew()
6723                 proutn(_("Do you want to play again? "))
6724                 if not ja():
6725                     break
6726             skip(1)
6727             prout(_("May the Great Bird of the Galaxy roost upon your home planet."))
6728         finally:
6729             ioend()
6730         raise SystemExit(0)
6731     except KeyboardInterrupt:
6732         if logfp:
6733             logfp.close()
6734         print("")
6735
6736 # End.