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