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