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