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