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