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