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