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