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