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