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