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