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