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