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