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