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