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