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