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