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