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