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