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