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