Make some constants into strings for better debugging messages.
[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.wfacsq
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.wfacsq
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     game.wfacsq=game.warpfac*game.warpfac
4193     if game.warpfac <= oldfac or game.warpfac <= 6.0:
4194         prout(_("Helmsman Sulu- \"Warp factor %d, Captain.\"") %
4195                int(game.warpfac))
4196         return
4197     if game.warpfac < 8.00:
4198         prout(_("Engineer Scott- \"Aye, but our maximum safe speed is warp 6.\""))
4199         return
4200     if game.warpfac == 10.0:
4201         prout(_("Engineer Scott- \"Aye, Captain, we'll try it.\""))
4202         return
4203     prout(_("Engineer Scott- \"Aye, Captain, but our engines may not take it.\""))
4204     return
4205
4206 def atover(igrab):
4207     "Cope with being tossed out of quadrant by supernova or yanked by beam."
4208     scanner.chew()
4209     # is captain on planet? 
4210     if game.landed:
4211         if damaged(DTRANSP):
4212             finish(FPNOVA)
4213             return
4214         prout(_("Scotty rushes to the transporter controls."))
4215         if game.shldup:
4216             prout(_("But with the shields up it's hopeless."))
4217             finish(FPNOVA)
4218         prouts(_("His desperate attempt to rescue you . . ."))
4219         if withprob(0.5):
4220             prout(_("fails."))
4221             finish(FPNOVA)
4222             return
4223         prout(_("SUCCEEDS!"))
4224         if game.imine:
4225             game.imine = False
4226             proutn(_("The crystals mined were "))
4227             if withprob(0.25):
4228                 prout(_("lost."))
4229             else:
4230                 prout(_("saved."))
4231                 game.icrystl = True
4232     if igrab:
4233         return
4234     # Check to see if captain in shuttle craft 
4235     if game.icraft:
4236         finish(FSTRACTOR)
4237     if game.alldone:
4238         return
4239     # Inform captain of attempt to reach safety 
4240     skip(1)
4241     while True:
4242         if game.justin:
4243             prouts(_("***RED ALERT!  RED ALERT!"))
4244             skip(1)
4245             proutn(_("The %s has stopped in a quadrant containing") % crmshp())
4246             prouts(_("   a supernova."))
4247             skip(2)
4248         prout(_("***Emergency automatic override attempts to hurl ")+crmshp())
4249         skip(1)
4250         prout(_("safely out of quadrant."))
4251         if not damaged(DRADIO):
4252             game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
4253         # Try to use warp engines 
4254         if damaged(DWARPEN):
4255             skip(1)
4256             prout(_("Warp engines damaged."))
4257             finish(FSNOVAED)
4258             return
4259         game.warpfac = randreal(6.0, 8.0)
4260         game.wfacsq = game.warpfac * game.warpfac
4261         prout(_("Warp factor set to %d") % int(game.warpfac))
4262         power = 0.75*game.energy
4263         game.dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1))
4264         distreq = randreal(math.sqrt(2))
4265         if distreq < game.dist:
4266             game.dist = distreq
4267         game.optime = 10.0*game.dist/game.wfacsq
4268         game.direc = randreal(12)       # How dumb! 
4269         game.justin = False
4270         game.inorbit = False
4271         warp(True)
4272         if not game.justin:
4273             # This is bad news, we didn't leave quadrant. 
4274             if game.alldone:
4275                 return
4276             skip(1)
4277             prout(_("Insufficient energy to leave quadrant."))
4278             finish(FSNOVAED)
4279             return
4280         # Repeat if another snova
4281         if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
4282             break
4283     if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0: 
4284         finish(FWON) # Snova killed remaining enemy. 
4285
4286 def timwrp():
4287     "Let's do the time warp again."
4288     prout(_("***TIME WARP ENTERED."))
4289     if game.state.snap and withprob(0.5):
4290         # Go back in time 
4291         prout(_("You are traveling backwards in time %d stardates.") %
4292               int(game.state.date-game.snapsht.date))
4293         game.state = game.snapsht
4294         game.state.snap = False
4295         if len(game.state.kcmdr):
4296             schedule(FTBEAM, expran(game.intime/len(game.state.kcmdr)))
4297             schedule(FBATTAK, expran(0.3*game.intime))
4298         schedule(FSNOVA, expran(0.5*game.intime))
4299         # next snapshot will be sooner 
4300         schedule(FSNAP, expran(0.25*game.state.remtime))
4301                                 
4302         if game.state.nscrem:
4303             schedule(FSCMOVE, 0.2777)       
4304         game.isatb = 0
4305         unschedule(FCDBAS)
4306         unschedule(FSCDBAS)
4307         game.battle.invalidate()
4308
4309         # Make sure Galileo is consistant -- Snapshot may have been taken
4310         # when on planet, which would give us two Galileos! 
4311         gotit = False
4312         for l in range(game.inplan):
4313             if game.state.planets[l].known == "shuttle_down":
4314                 gotit = True
4315                 if game.iscraft == "onship" and game.ship==IHE:
4316                     prout(_("Chekov-  \"Security reports the Galileo has disappeared, Sir!"))
4317                     game.iscraft = "offship"
4318         # Likewise, if in the original time the Galileo was abandoned, but
4319         # was on ship earlier, it would have vanished -- let's restore it.
4320         if game.iscraft == "offship" and not gotit and game.damage[DSHUTTL] >= 0.0:
4321             prout(_("Checkov-  \"Security reports the Galileo has reappeared in the dock!\""))
4322             game.iscraft = "onship"
4323         # There used to be code to do the actual reconstrction here,
4324         # but the starchart is now part of the snapshotted galaxy state.
4325         prout(_("Spock has reconstructed a correct star chart from memory"))
4326     else:
4327         # Go forward in time 
4328         game.optime = -0.5*game.intime*math.log(randreal())
4329         prout(_("You are traveling forward in time %d stardates.") % int(game.optime))
4330         # cheat to make sure no tractor beams occur during time warp 
4331         postpone(FTBEAM, game.optime)
4332         game.damage[DRADIO] += game.optime
4333     newqad()
4334     events()    # Stas Sergeev added this -- do pending events 
4335
4336 def probe():
4337     "Launch deep-space probe." 
4338     # New code to launch a deep space probe 
4339     if game.nprobes == 0:
4340         scanner.chew()
4341         skip(1)
4342         if game.ship == IHE: 
4343             prout(_("Engineer Scott- \"We have no more deep space probes, Sir.\""))
4344         else:
4345             prout(_("Ye Faerie Queene has no deep space probes."))
4346         return
4347     if damaged(DDSP):
4348         scanner.chew()
4349         skip(1)
4350         prout(_("Engineer Scott- \"The probe launcher is damaged, Sir.\""))
4351         return
4352     if is_scheduled(FDSPROB):
4353         scanner.chew()
4354         skip(1)
4355         if damaged(DRADIO) and game.condition != "docked":
4356             prout(_("Spock-  \"Records show the previous probe has not yet"))
4357             prout(_("   reached its destination.\""))
4358         else:
4359             prout(_("Uhura- \"The previous probe is still reporting data, Sir.\""))
4360         return
4361     key = scanner.next()
4362     if key == "IHEOL":
4363         # slow mode, so let Kirk know how many probes there are left
4364         if game.nprobes == 1:
4365             prout(_("1 probe left."))
4366         else:
4367             prout(_("%d probes left") % game.nprobes)
4368         proutn(_("Are you sure you want to fire a probe? "))
4369         if ja() == False:
4370             return
4371     game.isarmed = False
4372     if key == "IHALPHA" and scanner.token == "armed":
4373         game.isarmed = True
4374         key = scanner.next()
4375     elif key == "IHEOL":
4376         proutn(_("Arm NOVAMAX warhead? "))
4377         game.isarmed = ja()
4378     if not getcourse(isprobe=True, akey=key):
4379         return
4380     game.nprobes -= 1
4381     angle = ((15.0 - game.direc) * 0.5235988)
4382     game.probeinx = -math.sin(angle)
4383     game.probeiny = math.cos(angle)
4384     if math.fabs(game.probeinx) > math.fabs(game.probeiny):
4385         bigger = math.fabs(game.probeinx)
4386     else:
4387         bigger = math.fabs(game.probeiny)
4388     game.probeiny /= bigger
4389     game.probeinx /= bigger
4390     game.proben = 10.0*game.dist*bigger +0.5
4391     game.probex = game.quadrant.i*QUADSIZE + game.sector.i - 1  # We will use better packing than original
4392     game.probey = game.quadrant.j*QUADSIZE + game.sector.j - 1
4393     game.probec = game.quadrant
4394     schedule(FDSPROB, 0.01) # Time to move one sector
4395     prout(_("Ensign Chekov-  \"The deep space probe is launched, Captain.\""))
4396     game.ididit = True
4397     return
4398
4399 # Here's how the mayday code works:
4400
4401 # First, the closest starbase is selected.  If there is a a starbase
4402 # in your own quadrant, you are in good shape.  This distance takes
4403 # quadrant distances into account only.
4404 #
4405 # A magic number is computed based on the distance which acts as the
4406 # probability that you will be rematerialized.  You get three tries.
4407 #
4408 # When it is determined that you should be able to be rematerialized
4409 # (i.e., when the probability thing mentioned above comes up
4410 # positive), you are put into that quadrant (anywhere).  Then, we try
4411 # to see if there is a spot adjacent to the star- base.  If not, you
4412 # can't be rematerialized!!!  Otherwise, it drops you there.  It only
4413 # tries five times to find a spot to drop you.  After that, it's your
4414 # problem.
4415
4416 def mayday():
4417     "Yell for help from nearest starbase."
4418     # There's more than one way to move in this game! 
4419     scanner.chew()
4420     # Test for conditions which prevent calling for help 
4421     if game.condition == "docked":
4422         prout(_("Lt. Uhura-  \"But Captain, we're already docked.\""))
4423         return
4424     if damaged(DRADIO):
4425         prout(_("Subspace radio damaged."))
4426         return
4427     if not game.state.baseq:
4428         prout(_("Lt. Uhura-  \"Captain, I'm not getting any response from Starbase.\""))
4429         return
4430     if game.landed:
4431         prout(_("You must be aboard the %s.") % crmshp())
4432         return
4433     # OK -- call for help from nearest starbase 
4434     game.nhelp += 1
4435     if game.base.i!=0:
4436         # There's one in this quadrant 
4437         ddist = (game.base - game.sector).distance()
4438     else:
4439         ddist = FOREVER
4440         for ibq in game.state.baseq:
4441             xdist = QUADSIZE * (ibq - game.quadrant).distance()
4442             if xdist < ddist:
4443                 ddist = xdist
4444         # Since starbase not in quadrant, set up new quadrant 
4445         game.quadrant = ibq
4446         newqad()
4447     # dematerialize starship 
4448     game.quad[game.sector.i][game.sector.j]=IHDOT
4449     proutn(_("Starbase in Quadrant %s responds--%s dematerializes") \
4450            % (game.quadrant, crmshp()))
4451     game.sector.invalidate()
4452     for m in range(1, 5+1):
4453         w = game.base.scatter() 
4454         if VALID_SECTOR(w.i,w.j) and game.quad[w.i][w.j]==IHDOT:
4455             # found one -- finish up 
4456             game.sector = w
4457             break
4458     if not game.sector.is_valid():
4459         prout(_("You have been lost in space..."))
4460         finish(FMATERIALIZE)
4461         return
4462     # Give starbase three chances to rematerialize starship 
4463     probf = math.pow((1.0 - math.pow(0.98,ddist)), 0.33333333)
4464     for m in range(1, 3+1):
4465         if m == 1: proutn(_("1st"))
4466         elif m == 2: proutn(_("2nd"))
4467         elif m == 3: proutn(_("3rd"))
4468         proutn(_(" attempt to re-materialize ") + crmshp())
4469         game.quad[ix][iy]=(IHMATER0,IHMATER1,IHMATER2)[m-1]
4470         textcolor("red")
4471         warble()
4472         if randreal() > probf:
4473             break
4474         prout(_("fails."))
4475         curses.delay_output(500)
4476         textcolor(None)
4477     if m > 3:
4478         game.quad[ix][iy]=IHQUEST
4479         game.alive = False
4480         drawmaps(1)
4481         setwnd(message_window)
4482         finish(FMATERIALIZE)
4483         return
4484     game.quad[ix][iy]=game.ship
4485     textcolor("green")
4486     prout(_("succeeds."))
4487     textcolor(None)
4488     dock(False)
4489     skip(1)
4490     prout(_("Lt. Uhura-  \"Captain, we made it!\""))
4491
4492 # Abandon Ship (the BSD-Trek description)
4493
4494 # The ship is abandoned.  If your current ship is the Faire
4495 # Queene, or if your shuttlecraft is dead, you're out of
4496 # luck.  You need the shuttlecraft in order for the captain
4497 # (that's you!!) to escape.
4498
4499 # Your crew can beam to an inhabited starsystem in the
4500 # quadrant, if there is one and if the transporter is working.
4501 # If there is no inhabited starsystem, or if the transporter
4502 # is out, they are left to die in outer space.
4503
4504 # If there are no starbases left, you are captured by the
4505 # Klingons, who torture you mercilessly.  However, if there
4506 # is at least one starbase, you are returned to the
4507 # Federation in a prisoner of war exchange.  Of course, this
4508 # can't happen unless you have taken some prisoners.
4509
4510 def abandon():
4511     "Abandon ship."
4512     scanner.chew()
4513     if game.condition=="docked":
4514         if game.ship!=IHE:
4515             prout(_("You cannot abandon Ye Faerie Queene."))
4516             return
4517     else:
4518         # Must take shuttle craft to exit 
4519         if game.damage[DSHUTTL]==-1:
4520             prout(_("Ye Faerie Queene has no shuttle craft."))
4521             return
4522         if game.damage[DSHUTTL]<0:
4523             prout(_("Shuttle craft now serving Big Macs."))
4524             return
4525         if game.damage[DSHUTTL]>0:
4526             prout(_("Shuttle craft damaged."))
4527             return
4528         if game.landed:
4529             prout(_("You must be aboard the ship."))
4530             return
4531         if game.iscraft != "onship":
4532             prout(_("Shuttle craft not currently available."))
4533             return
4534         # Emit abandon ship messages 
4535         skip(1)
4536         prouts(_("***ABANDON SHIP!  ABANDON SHIP!"))
4537         skip(1)
4538         prouts(_("***ALL HANDS ABANDON SHIP!"))
4539         skip(2)
4540         prout(_("Captain and crew escape in shuttle craft."))
4541         if not game.state.baseq:
4542             # Oops! no place to go... 
4543             finish(FABANDN)
4544             return
4545         q = game.state.galaxy[game.quadrant.i][game.quadrant.j]
4546         # Dispose of crew 
4547         if not (game.options & OPTION_WORLDS) and not damaged(DTRANSP):
4548             prout(_("Remainder of ship's complement beam down"))
4549             prout(_("to nearest habitable planet."))
4550         elif q.planet != None and not damaged(DTRANSP):
4551             prout(_("Remainder of ship's complement beam down to %s.") %
4552                     q.planet)
4553         else:
4554             prout(_("Entire crew of %d left to die in outer space.") %
4555                     game.state.crew)
4556             game.casual += game.state.crew
4557             game.abandoned += game.state.crew
4558         # If at least one base left, give 'em the Faerie Queene 
4559         skip(1)
4560         game.icrystl = False # crystals are lost 
4561         game.nprobes = 0 # No probes 
4562         prout(_("You are captured by Klingons and released to"))
4563         prout(_("the Federation in a prisoner-of-war exchange."))
4564         nb = randrange(len(game.state.baseq))
4565         # Set up quadrant and position FQ adjacient to base 
4566         if not game.quadrant == game.state.baseq[nb]:
4567             game.quadrant = game.state.baseq[nb]
4568             game.sector.i = game.sector.j = 5
4569             newqad()
4570         while True:
4571             # position next to base by trial and error 
4572             game.quad[game.sector.i][game.sector.j] = IHDOT
4573             for l in range(QUADSIZE):
4574                 game.sector = game.base.scatter()
4575                 if VALID_SECTOR(game.sector.i, game.sector.j) and \
4576                        game.quad[game.sector.i][game.sector.j] == IHDOT:
4577                     break
4578             if l < QUADSIZE+1:
4579                 break # found a spot 
4580             game.sector.i=QUADSIZE/2
4581             game.sector.j=QUADSIZE/2
4582             newqad()
4583     # Get new commission 
4584     game.quad[game.sector.i][game.sector.j] = game.ship = IHF
4585     game.state.crew = FULLCREW
4586     prout(_("Starfleet puts you in command of another ship,"))
4587     prout(_("the Faerie Queene, which is antiquated but,"))
4588     prout(_("still useable."))
4589     if game.icrystl:
4590         prout(_("The dilithium crystals have been moved."))
4591     game.imine = False
4592     game.iscraft = "offship" # Galileo disappears 
4593     # Resupply ship 
4594     game.condition="docked"
4595     for l in range(NDEVICES): 
4596         game.damage[l] = 0.0
4597     game.damage[DSHUTTL] = -1
4598     game.energy = game.inenrg = 3000.0
4599     game.shield = game.inshld = 1250.0
4600     game.torps = game.intorps = 6
4601     game.lsupres=game.inlsr=3.0
4602     game.shldup=False
4603     game.warpfac=5.0
4604     game.wfacsq=25.0
4605     return
4606
4607 # Code from planets.c begins here.
4608
4609 def consumeTime():
4610     "Abort a lengthy operation if an event interrupts it." 
4611     game.ididit = True
4612     events()
4613     if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova or game.justin: 
4614         return True
4615     return False
4616
4617 def survey():
4618     "Report on (uninhabited) planets in the galaxy."
4619     iknow = False
4620     skip(1)
4621     scanner.chew()
4622     prout(_("Spock-  \"Planet report follows, Captain.\""))
4623     skip(1)
4624     for i in range(game.inplan):
4625         if game.state.planets[i].pclass == "destroyed":
4626             continue
4627         if (game.state.planets[i].known != "unknown" \
4628             and not game.state.planets[i].inhabited) \
4629             or idebug:
4630             iknow = True
4631             if idebug and game.state.planets[i].known=="unknown":
4632                 proutn("(Unknown) ")
4633             proutn(_("Quadrant %s") % game.state.planets[i].quadrant)
4634             proutn(_("   class "))
4635             proutn(game.state.planets[i].pclass)
4636             proutn("   ")
4637             if game.state.planets[i].crystals != present:
4638                 proutn(_("no "))
4639             prout(_("dilithium crystals present."))
4640             if game.state.planets[i].known=="shuttle_down": 
4641                 prout(_("    Shuttle Craft Galileo on surface."))
4642     if not iknow:
4643         prout(_("No information available."))
4644
4645 def orbit():
4646     "Enter standard orbit." 
4647     skip(1)
4648     scanner.chew()
4649     if game.inorbit:
4650         prout(_("Already in standard orbit."))
4651         return
4652     if damaged(DWARPEN) and damaged(DIMPULS):
4653         prout(_("Both warp and impulse engines damaged."))
4654         return
4655     if not game.plnet.is_valid():
4656         prout("There is no planet in this sector.")
4657         return
4658     if abs(game.sector.i-game.plnet.i)>1 or abs(game.sector.j-game.plnet.j)>1:
4659         prout(crmshp() + _(" not adjacent to planet."))
4660         skip(1)
4661         return
4662     game.optime = randreal(0.02, 0.05)
4663     prout(_("Helmsman Sulu-  \"Entering standard orbit, Sir.\""))
4664     newcnd()
4665     if consumeTime():
4666         return
4667     game.height = randreal(1400, 8600)
4668     prout(_("Sulu-  \"Entered orbit at altitude %.2f kilometers.\"") % game.height)
4669     game.inorbit = True
4670     game.ididit = True
4671
4672 def sensor():
4673     "Examine planets in this quadrant."
4674     if damaged(DSRSENS):
4675         if game.options & OPTION_TTY:
4676             prout(_("Short range sensors damaged."))
4677         return
4678     if game.iplnet == None:
4679         if game.options & OPTION_TTY:
4680             prout(_("Spock- \"No planet in this quadrant, Captain.\""))
4681         return
4682     if game.iplnet.known == "unknown":
4683         prout(_("Spock-  \"Sensor scan for Quadrant %s-") % game.quadrant)
4684         skip(1)
4685         prout(_("         Planet at Sector %s is of class %s.") %
4686               (game.plnet, game.iplnet.pclass))
4687         if game.iplnet.known=="shuttle_down": 
4688             prout(_("         Sensors show Galileo still on surface."))
4689         proutn(_("         Readings indicate"))
4690         if game.iplnet.crystals != "present":
4691             proutn(_(" no"))
4692         prout(_(" dilithium crystals present.\""))
4693         if game.iplnet.known == "unknown":
4694             game.iplnet.known = "known"
4695     elif game.iplnet.inhabited:
4696         prout(_("Spock-  \"The inhabited planet %s ") % game.iplnet.name)
4697         prout(_("        is located at Sector %s, Captain.\"") % game.plnet)
4698
4699 def beam():
4700     "Use the transporter."
4701     nrgneed = 0
4702     scanner.chew()
4703     skip(1)
4704     if damaged(DTRANSP):
4705         prout(_("Transporter damaged."))
4706         if not damaged(DSHUTTL) and (game.iplnet.known=="shuttle_down" or game.iscraft == "onship"):
4707             skip(1)
4708             proutn(_("Spock-  \"May I suggest the shuttle craft, Sir?\" "))
4709             if ja() == True:
4710                 shuttle()
4711         return
4712     if not game.inorbit:
4713         prout(crmshp() + _(" not in standard orbit."))
4714         return
4715     if game.shldup:
4716         prout(_("Impossible to transport through shields."))
4717         return
4718     if game.iplnet.known=="unknown":
4719         prout(_("Spock-  \"Captain, we have no information on this planet"))
4720         prout(_("  and Starfleet Regulations clearly state that in this situation"))
4721         prout(_("  you may not go down.\""))
4722         return
4723     if not game.landed and game.iplnet.crystals=="absent":
4724         prout(_("Spock-  \"Captain, I fail to see the logic in"))
4725         prout(_("  exploring a planet with no dilithium crystals."))
4726         proutn(_("  Are you sure this is wise?\" "))
4727         if ja() == False:
4728             scanner.chew()
4729             return
4730     if not (game.options & OPTION_PLAIN):
4731         nrgneed = 50 * game.skill + game.height / 100.0
4732         if nrgneed > game.energy:
4733             prout(_("Engineering to bridge--"))
4734             prout(_("  Captain, we don't have enough energy for transportation."))
4735             return
4736         if not game.landed and nrgneed * 2 > game.energy:
4737             prout(_("Engineering to bridge--"))
4738             prout(_("  Captain, we have enough energy only to transport you down to"))
4739             prout(_("  the planet, but there wouldn't be an energy for the trip back."))
4740             if game.iplnet.known == "shuttle_down":
4741                 prout(_("  Although the Galileo shuttle craft may still be on a surface."))
4742             proutn(_("  Are you sure this is wise?\" "))
4743             if ja() == False:
4744                 scanner.chew()
4745                 return
4746     if game.landed:
4747         # Coming from planet 
4748         if game.iplnet.known=="shuttle_down":
4749             proutn(_("Spock-  \"Wouldn't you rather take the Galileo?\" "))
4750             if ja() == True:
4751                 scanner.chew()
4752                 return
4753             prout(_("Your crew hides the Galileo to prevent capture by aliens."))
4754         prout(_("Landing party assembled, ready to beam up."))
4755         skip(1)
4756         prout(_("Kirk whips out communicator..."))
4757         prouts(_("BEEP  BEEP  BEEP"))
4758         skip(2)
4759         prout(_("\"Kirk to enterprise-  Lock on coordinates...energize.\""))
4760     else:
4761         # Going to planet 
4762         prout(_("Scotty-  \"Transporter room ready, Sir.\""))
4763         skip(1)
4764         prout(_("Kirk and landing party prepare to beam down to planet surface."))
4765         skip(1)
4766         prout(_("Kirk-  \"Energize.\""))
4767     game.ididit = True
4768     skip(1)
4769     prouts("WWHOOOIIIIIRRRRREEEE.E.E.  .  .  .  .   .    .")
4770     skip(2)
4771     if withprob(0.98):
4772         prouts("BOOOIIIOOOIIOOOOIIIOIING . . .")
4773         skip(2)
4774         prout(_("Scotty-  \"Oh my God!  I've lost them.\""))
4775         finish(FLOST)
4776         return
4777     prouts(".    .   .  .  .  .  .E.E.EEEERRRRRIIIIIOOOHWW")
4778     game.landed = not game.landed
4779     game.energy -= nrgneed
4780     skip(2)
4781     prout(_("Transport complete."))
4782     if game.landed and game.iplnet.known=="shuttle_down":
4783         prout(_("The shuttle craft Galileo is here!"))
4784     if not game.landed and game.imine:
4785         game.icrystl = True
4786         game.cryprob = 0.05
4787     game.imine = False
4788     return
4789
4790 def mine():
4791     "Strip-mine a world for dilithium."
4792     skip(1)
4793     scanner.chew()
4794     if not game.landed:
4795         prout(_("Mining party not on planet."))
4796         return
4797     if game.iplnet.crystals == "mined":
4798         prout(_("This planet has already been strip-mined for dilithium."))
4799         return
4800     elif game.iplnet.crystals == "absent":
4801         prout(_("No dilithium crystals on this planet."))
4802         return
4803     if game.imine:
4804         prout(_("You've already mined enough crystals for this trip."))
4805         return
4806     if game.icrystl and game.cryprob == 0.05:
4807         prout(_("With all those fresh crystals aboard the ") + crmshp())
4808         prout(_("there's no reason to mine more at this time."))
4809         return
4810     game.optime = randreal(0.1, 0.3)*(ord(game.iplnet.pclass)-ord("L"))
4811     if consumeTime():
4812         return
4813     prout(_("Mining operation complete."))
4814     game.iplnet.crystals = "mined"
4815     game.imine = game.ididit = True
4816
4817 def usecrystals():
4818     "Use dilithium crystals."
4819     game.ididit = False
4820     skip(1)
4821     scanner.chew()
4822     if not game.icrystl:
4823         prout(_("No dilithium crystals available."))
4824         return
4825     if game.energy >= 1000:
4826         prout(_("Spock-  \"Captain, Starfleet Regulations prohibit such an operation"))
4827         prout(_("  except when Condition Yellow exists."))
4828         return
4829     prout(_("Spock- \"Captain, I must warn you that loading"))
4830     prout(_("  raw dilithium crystals into the ship's power"))
4831     prout(_("  system may risk a severe explosion."))
4832     proutn(_("  Are you sure this is wise?\" "))
4833     if ja() == False:
4834         scanner.chew()
4835         return
4836     skip(1)
4837     prout(_("Engineering Officer Scott-  \"(GULP) Aye Sir."))
4838     prout(_("  Mr. Spock and I will try it.\""))
4839     skip(1)
4840     prout(_("Spock-  \"Crystals in place, Sir."))
4841     prout(_("  Ready to activate circuit.\""))
4842     skip(1)
4843     prouts(_("Scotty-  \"Keep your fingers crossed, Sir!\""))
4844     skip(1)
4845     if with(game.cryprob):
4846         prouts(_("  \"Activating now! - - No good!  It's***"))
4847         skip(2)
4848         prouts(_("***RED ALERT!  RED A*L********************************"))
4849         skip(1)
4850         stars()
4851         prouts(_("******************   KA-BOOM!!!!   *******************"))
4852         skip(1)
4853         kaboom()
4854         return
4855     game.energy += randreal(5000.0, 5500.0)
4856     prouts(_("  \"Activating now! - - "))
4857     prout(_("The instruments"))
4858     prout(_("   are going crazy, but I think it's"))
4859     prout(_("   going to work!!  Congratulations, Sir!\""))
4860     game.cryprob *= 2.0
4861     game.ididit = True
4862
4863 def shuttle():
4864     "Use shuttlecraft for planetary jaunt."
4865     scanner.chew()
4866     skip(1)
4867     if damaged(DSHUTTL):
4868         if game.damage[DSHUTTL] == -1.0:
4869             if game.inorbit and game.iplnet.known == "shuttle_down":
4870                 prout(_("Ye Faerie Queene has no shuttle craft bay to dock it at."))
4871             else:
4872                 prout(_("Ye Faerie Queene had no shuttle craft."))
4873         elif game.damage[DSHUTTL] > 0:
4874             prout(_("The Galileo is damaged."))
4875         else: # game.damage[DSHUTTL] < 0  
4876             prout(_("Shuttle craft is now serving Big Macs."))
4877         return
4878     if not game.inorbit:
4879         prout(crmshp() + _(" not in standard orbit."))
4880         return
4881     if (game.iplnet.known != "shuttle_down") and game.iscraft != "onship":
4882         prout(_("Shuttle craft not currently available."))
4883         return
4884     if not game.landed and game.iplnet.known=="shuttle_down":
4885         prout(_("You will have to beam down to retrieve the shuttle craft."))
4886         return
4887     if game.shldup or game.condition == "docked":
4888         prout(_("Shuttle craft cannot pass through shields."))
4889         return
4890     if game.iplnet.known=="unknown":
4891         prout(_("Spock-  \"Captain, we have no information on this planet"))
4892         prout(_("  and Starfleet Regulations clearly state that in this situation"))
4893         prout(_("  you may not fly down.\""))
4894         return
4895     game.optime = 3.0e-5*game.height
4896     if game.optime >= 0.8*game.state.remtime:
4897         prout(_("First Officer Spock-  \"Captain, I compute that such"))
4898         proutn(_("  a maneuver would require approximately %2d%% of our") % \
4899                int(100*game.optime/game.state.remtime))
4900         prout(_("remaining time."))
4901         proutn(_("Are you sure this is wise?\" "))
4902         if ja() == False:
4903             game.optime = 0.0
4904             return
4905     if game.landed:
4906         # Kirk on planet 
4907         if game.iscraft == "onship":
4908             # Galileo on ship! 
4909             if not damaged(DTRANSP):
4910                 proutn(_("Spock-  \"Would you rather use the transporter?\" "))
4911                 if ja() == True:
4912                     beam()
4913                     return
4914                 proutn(_("Shuttle crew"))
4915             else:
4916                 proutn(_("Rescue party"))
4917             prout(_(" boards Galileo and swoops toward planet surface."))
4918             game.iscraft = "offship"
4919             skip(1)
4920             if consumeTime():
4921                 return
4922             game.iplnet.known="shuttle_down"
4923             prout(_("Trip complete."))
4924             return
4925         else:
4926             # Ready to go back to ship 
4927             prout(_("You and your mining party board the"))
4928             prout(_("shuttle craft for the trip back to the Enterprise."))
4929             skip(1)
4930             prouts(_("The short hop begins . . ."))
4931             skip(1)
4932             game.iplnet.known="known"
4933             game.icraft = True
4934             skip(1)
4935             game.landed = False
4936             if consumeTime():
4937                 return
4938             game.iscraft = "onship"
4939             game.icraft = False
4940             if game.imine:
4941                 game.icrystl = True
4942                 game.cryprob = 0.05
4943             game.imine = False
4944             prout(_("Trip complete."))
4945             return
4946     else:
4947         # Kirk on ship and so is Galileo 
4948         prout(_("Mining party assembles in the hangar deck,"))
4949         prout(_("ready to board the shuttle craft \"Galileo\"."))
4950         skip(1)
4951         prouts(_("The hangar doors open; the trip begins."))
4952         skip(1)
4953         game.icraft = True
4954         game.iscraft = "offship"
4955         if consumeTime():
4956             return
4957         game.iplnet.known = "shuttle_down"
4958         game.landed = True
4959         game.icraft = False
4960         prout(_("Trip complete."))
4961         return
4962
4963 def deathray():
4964     "Use the big zapper."
4965     game.ididit = False
4966     skip(1)
4967     scanner.chew()
4968     if game.ship != IHE:
4969         prout(_("Ye Faerie Queene has no death ray."))
4970         return
4971     if len(game.enemies)==0:
4972         prout(_("Sulu-  \"But Sir, there are no enemies in this quadrant.\""))
4973         return
4974     if damaged(DDRAY):
4975         prout(_("Death Ray is damaged."))
4976         return
4977     prout(_("Spock-  \"Captain, the 'Experimental Death Ray'"))
4978     prout(_("  is highly unpredictible.  Considering the alternatives,"))
4979     proutn(_("  are you sure this is wise?\" "))
4980     if ja() == False:
4981         return
4982     prout(_("Spock-  \"Acknowledged.\""))
4983     skip(1)
4984     game.ididit = True
4985     prouts(_("WHOOEE ... WHOOEE ... WHOOEE ... WHOOEE"))
4986     skip(1)
4987     prout(_("Crew scrambles in emergency preparation."))
4988     prout(_("Spock and Scotty ready the death ray and"))
4989     prout(_("prepare to channel all ship's power to the device."))
4990     skip(1)
4991     prout(_("Spock-  \"Preparations complete, sir.\""))
4992     prout(_("Kirk-  \"Engage!\""))
4993     skip(1)
4994     prouts(_("WHIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"))
4995     skip(1)
4996     dprob = 0.30
4997     if game.options & OPTION_PLAIN:
4998         dprob = 0.5
4999     r = randreal()
5000     if r > dprob:
5001         prouts(_("Sulu- \"Captain!  It's working!\""))
5002         skip(2)
5003         while len(game.enemies) > 0:
5004             deadkl(game.enemies[1].kloc, game.quad[game.enemies[1].kloc.i][game.enemies[1].kloc.j],game.enemies[1].kloc)
5005         prout(_("Ensign Chekov-  \"Congratulations, Captain!\""))
5006         if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) == 0:
5007             finish(FWON)    
5008         if (game.options & OPTION_PLAIN) == 0:
5009             prout(_("Spock-  \"Captain, I believe the `Experimental Death Ray'"))
5010             if withprob(0.05):
5011                 prout(_("   is still operational.\""))
5012             else:
5013                 prout(_("   has been rendered nonfunctional.\""))
5014                 game.damage[DDRAY] = 39.95
5015         return
5016     r = randreal()      # Pick failure method 
5017     if r <= 0.30:
5018         prouts(_("Sulu- \"Captain!  It's working!\""))
5019         skip(1)
5020         prouts(_("***RED ALERT!  RED ALERT!"))
5021         skip(1)
5022         prout(_("***MATTER-ANTIMATTER IMPLOSION IMMINENT!"))
5023         skip(1)
5024         prouts(_("***RED ALERT!  RED A*L********************************"))
5025         skip(1)
5026         stars()
5027         prouts(_("******************   KA-BOOM!!!!   *******************"))
5028         skip(1)
5029         kaboom()
5030         return
5031     if r <= 0.55:
5032         prouts(_("Sulu- \"Captain!  Yagabandaghangrapl, brachriigringlanbla!\""))
5033         skip(1)
5034         prout(_("Lt. Uhura-  \"Graaeek!  Graaeek!\""))
5035         skip(1)
5036         prout(_("Spock-  \"Fascinating!  . . . All humans aboard"))
5037         prout(_("  have apparently been transformed into strange mutations."))
5038         prout(_("  Vulcans do not seem to be affected."))
5039         skip(1)
5040         prout(_("Kirk-  \"Raauch!  Raauch!\""))
5041         finish(FDRAY)
5042         return
5043     if r <= 0.75:
5044         intj
5045         prouts(_("Sulu- \"Captain!  It's   --WHAT?!?!\""))
5046         skip(2)
5047         proutn(_("Spock-  \"I believe the word is"))
5048         prouts(_(" *ASTONISHING*"))
5049         prout(_(" Mr. Sulu."))
5050         for i in range(QUADSIZE):
5051             for j in range(QUADSIZE):
5052                 if game.quad[i][j] == IHDOT:
5053                     game.quad[i][j] = IHQUEST
5054         prout(_("  Captain, our quadrant is now infested with"))
5055         prouts(_(" - - - - - -  *THINGS*."))
5056         skip(1)
5057         prout(_("  I have no logical explanation.\""))
5058         return
5059     prouts(_("Sulu- \"Captain!  The Death Ray is creating tribbles!\""))
5060     skip(1)
5061     prout(_("Scotty-  \"There are so many tribbles down here"))
5062     prout(_("  in Engineering, we can't move for 'em, Captain.\""))
5063     finish(FTRIBBLE)
5064     return
5065
5066 # Code from reports.c begins here
5067
5068 def attackreport(curt):
5069     "eport status of bases under attack."
5070     if not curt:
5071         if is_scheduled(FCDBAS):
5072             prout(_("Starbase in Quadrant %s is currently under Commander attack.") % game.battle)
5073             prout(_("It can hold out until Stardate %d.") % int(scheduled(FCDBAS)))
5074         elif game.isatb == 1:
5075             prout(_("Starbase in Quadrant %s is under Super-commander attack.") % game.state.kscmdr)
5076             prout(_("It can hold out until Stardate %d.") % int(scheduled(FSCDBAS)))
5077         else:
5078             prout(_("No Starbase is currently under attack."))
5079     else:
5080         if is_scheduled(FCDBAS):
5081             proutn(_("Base in %s attacked by C. Alive until %.1f") % (game.battle, scheduled(FCDBAS)))
5082         if game.isatb:
5083             proutn(_("Base in %s attacked by S. Alive until %.1f") % (game.state.kscmdr, scheduled(FSCDBAS)))
5084         clreol()
5085
5086 def report():
5087     # report on general game status 
5088     scanner.chew()
5089     s1 = "" and game.thawed and _("thawed ")
5090     s2 = {1:"short", 2:"medium", 4:"long"}[game.length]
5091     s3 = (None, _("novice"). _("fair"),
5092           _("good"), _("expert"), _("emeritus"))[game.skill]
5093     prout(_("You %s a %s%s %s game.") % ((_("were playing"), _("are playing"))[game.alldone], s1, s2, s3))
5094     if game.skill>SKILL_GOOD and game.thawed and not game.alldone:
5095         prout(_("No plaque is allowed."))
5096     if game.tourn:
5097         prout(_("This is tournament game %d.") % game.tourn)
5098     prout(_("Your secret password is \"%s\"") % game.passwd)
5099     proutn(_("%d of %d Klingons have been killed") % (((game.inkling + game.incom + game.inscom) - (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)), 
5100            (game.inkling + game.incom + game.inscom)))
5101     if game.incom - len(game.state.kcmdr):
5102         prout(_(", including %d Commander%s.") % (game.incom - len(game.state.kcmdr), (_("s"), "")[(game.incom - len(game.state.kcmdr))==1]))
5103     elif game.inkling - game.state.remkl + (game.inscom - game.state.nscrem) > 0:
5104         prout(_(", but no Commanders."))
5105     else:
5106         prout(".")
5107     if game.skill > SKILL_FAIR:
5108         prout(_("The Super Commander has %sbeen destroyed.") % ("", _("not "))[game.state.nscrem])
5109     if len(game.state.baseq) != game.inbase:
5110         proutn(_("There "))
5111         if game.inbase-len(game.state.baseq)==1:
5112             proutn(_("has been 1 base"))
5113         else:
5114             proutn(_("have been %d bases") % (game.inbase-len(game.state.baseq)))
5115         prout(_(" destroyed, %d remaining.") % len(game.state.baseq))
5116     else:
5117         prout(_("There are %d bases.") % game.inbase)
5118     if communicating() or game.iseenit:
5119         # Don't report this if not seen and
5120         # either the radio is dead or not at base!
5121         attackreport(False)
5122         game.iseenit = True
5123     if game.casual: 
5124         prout(_("%d casualt%s suffered so far.") % (game.casual, ("y", "ies")[game.casual!=1]))
5125     if game.nhelp:
5126         prout(_("There were %d call%s for help.") % (game.nhelp,  ("" , _("s"))[game.nhelp!=1]))
5127     if game.ship == IHE:
5128         proutn(_("You have "))
5129         if game.nprobes:
5130             proutn("%d" % (game.nprobes))
5131         else:
5132             proutn(_("no"))
5133         proutn(_(" deep space probe"))
5134         if game.nprobes!=1:
5135             proutn(_("s"))
5136         prout(".")
5137     if communicating() and is_scheduled(FDSPROB):
5138         if game.isarmed: 
5139             proutn(_("An armed deep space probe is in "))
5140         else:
5141             proutn(_("A deep space probe is in "))
5142         prout("Quadrant %s." % game.probec)
5143     if game.icrystl:
5144         if game.cryprob <= .05:
5145             prout(_("Dilithium crystals aboard ship... not yet used."))
5146         else:
5147             i=0
5148             ai = 0.05
5149             while game.cryprob > ai:
5150                 ai *= 2.0
5151                 i += 1
5152             prout(_("Dilithium crystals have been used %d time%s.") % \
5153                   (i, (_("s"), "")[i==1]))
5154     skip(1)
5155         
5156 def lrscan(silent):
5157     "Long-range sensor scan."
5158     if damaged(DLRSENS):
5159         # Now allow base's sensors if docked 
5160         if game.condition != "docked":
5161             if not silent:
5162                 prout(_("LONG-RANGE SENSORS DAMAGED."))
5163             return
5164         if not silent:
5165             prout(_("Starbase's long-range scan"))
5166     elif not silent:
5167         prout(_("Long-range scan"))
5168     for x in range(game.quadrant.i-1, game.quadrant.i+2):
5169         if not silent:
5170             proutn(" ")
5171         for y in range(game.quadrant.j-1, game.quadrant.j+2):
5172             if not VALID_QUADRANT(x, y):
5173                 if not silent:
5174                     proutn("  -1")
5175             else:
5176                 if not damaged(DRADIO):
5177                     game.state.galaxy[x][y].charted = True
5178                 game.state.chart[x][y].klingons = game.state.galaxy[x][y].klingons
5179                 game.state.chart[x][y].starbase = game.state.galaxy[x][y].starbase
5180                 game.state.chart[x][y].stars = game.state.galaxy[x][y].stars
5181                 if not silent and game.state.galaxy[x][y].supernova: 
5182                     proutn(" ***")
5183                 elif not silent:
5184                     proutn(" %3d" % (game.state.chart[x][y].klingons*100 + game.state.chart[x][y].starbase * 10 + game.state.chart[x][y].stars))
5185         prout(" ")
5186
5187 def damagereport():
5188     "Damage report."
5189     jdam = False
5190     scanner.chew()
5191
5192     for i in range(NDEVICES):
5193         if damaged(i):
5194             if not jdam:
5195                 prout(_("\tDEVICE\t\t\t-REPAIR TIMES-"))
5196                 prout(_("\t\t\tIN FLIGHT\t\tDOCKED"))
5197                 jdam = True
5198             prout("  %-26s\t%8.2f\t\t%8.2f" % (device[i],
5199                                                game.damage[i]+0.05,
5200                                                game.docfac*game.damage[i]+0.005))
5201     if not jdam:
5202         prout(_("All devices functional."))
5203
5204 def rechart():
5205     "Update the chart in the Enterprise's computer from galaxy data."
5206     game.lastchart = game.state.date
5207     for i in range(GALSIZE):
5208         for j in range(GALSIZE):
5209             if game.state.galaxy[i][j].charted:
5210                 game.state.chart[i][j].klingons = game.state.galaxy[i][j].klingons
5211                 game.state.chart[i][j].starbase = game.state.galaxy[i][j].starbase
5212                 game.state.chart[i][j].stars = game.state.galaxy[i][j].stars
5213
5214 def chart():
5215     "Display the star chart."
5216     scanner.chew()
5217     if (game.options & OPTION_AUTOSCAN):
5218         lrscan(silent=True)
5219     if not damaged(DRADIO):
5220         rechart()
5221     if game.lastchart < game.state.date and game.condition == "docked":
5222         prout(_("Spock-  \"I revised the Star Chart from the starbase's records.\""))
5223         rechart()
5224     prout(_("       STAR CHART FOR THE KNOWN GALAXY"))
5225     if game.state.date > game.lastchart:
5226         prout(_("(Last surveillance update %d stardates ago).") % ((int)(game.state.date-game.lastchart)))
5227     prout("      1    2    3    4    5    6    7    8")
5228     for i in range(GALSIZE):
5229         proutn("%d |" % (i+1))
5230         for j in range(GALSIZE):
5231             if (game.options & OPTION_SHOWME) and i == game.quadrant.i and j == game.quadrant.j:
5232                 proutn("<")
5233             else:
5234                 proutn(" ")
5235             if game.state.galaxy[i][j].supernova:
5236                 show = "***"
5237             elif not game.state.galaxy[i][j].charted and game.state.galaxy[i][j].starbase:
5238                 show = ".1."
5239             elif game.state.galaxy[i][j].charted:
5240                 show = "%3d" % (game.state.chart[i][j].klingons*100 + game.state.chart[i][j].starbase * 10 + game.state.chart[i][j].stars)
5241             else:
5242                 show = "..."
5243             proutn(show)
5244             if (game.options & OPTION_SHOWME) and i == game.quadrant.i and j == game.quadrant.j:
5245                 proutn(">")
5246             else:
5247                 proutn(" ")
5248         proutn("  |")
5249         if i<GALSIZE:
5250             skip(1)
5251
5252 def sectscan(goodScan, i, j):
5253     "Light up an individual dot in a sector."
5254     if goodScan or (abs(i-game.sector.i)<= 1 and abs(j-game.sector.j) <= 1):
5255         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):
5256             if game.condition   == "red": textcolor("red")
5257             elif game.condition == "green": textcolor("green")
5258             elif game.condition == "yellow": textcolor("yellow")
5259             elif game.condition == "docked": textcolor("cyan")
5260             elif game.condition == "dead": textcolor("brown")
5261             if game.quad[i][j] != game.ship: 
5262                 highvideo()
5263         proutn("%c " % game.quad[i][j])
5264         textcolor(None)
5265     else:
5266         proutn("- ")
5267
5268 def status(req=0):
5269     "Emit status report lines"
5270     if not req or req == 1:
5271         prstat(_("Stardate"), _("%.1f, Time Left %.2f") \
5272                % (game.state.date, game.state.remtime))
5273     if not req or req == 2:
5274         if game.condition != "docked":
5275             newcnd()
5276         dam = 0
5277         for t in range(NDEVICES):
5278             if game.damage[t]>0: 
5279                 dam += 1
5280         prstat(_("Condition"), _("%s, %i DAMAGES") % (game.condition.upper(), dam))
5281     if not req or req == 3:
5282         prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
5283     if not req or req == 4:
5284         if damaged(DLIFSUP):
5285             if game.condition == "docked":
5286                 s = _("DAMAGED, Base provides")
5287             else:
5288                 s = _("DAMAGED, reserves=%4.2f") % game.lsupres
5289         else:
5290             s = _("ACTIVE")
5291         prstat(_("Life Support"), s)
5292     if not req or req == 5:
5293         prstat(_("Warp Factor"), "%.1f" % game.warpfac)
5294     if not req or req == 6:
5295         extra = ""
5296         if game.icrystl and (game.options & OPTION_SHOWME):
5297             extra = _(" (have crystals)")
5298         prstat(_("Energy"), "%.2f%s" % (game.energy, extra))
5299     if not req or req == 7:
5300         prstat(_("Torpedoes"), "%d" % (game.torps))
5301     if not req or req == 8:
5302         if damaged(DSHIELD):
5303             s = _("DAMAGED,")
5304         elif game.shldup:
5305             s = _("UP,")
5306         else:
5307             s = _("DOWN,")
5308         data = _(" %d%% %.1f units") \
5309                % (int((100.0*game.shield)/game.inshld + 0.5), game.shield)
5310         prstat(_("Shields"), s+data)
5311     if not req or req == 9:
5312         prstat(_("Klingons Left"), "%d" \
5313                % (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem))
5314     if not req or req == 10:
5315         if game.options & OPTION_WORLDS:
5316             plnet = game.state.galaxy[game.quadrant.i][game.quadrant.j].planet
5317             if plnet and plnet.inhabited:
5318                 prstat(_("Major system"), plnet.name)
5319             else:
5320                 prout(_("Sector is uninhabited"))
5321     elif not req or req == 11:
5322         attackreport(not req)
5323
5324 def request():
5325     "Request specified status data, a historical relic from slow TTYs."
5326     requests = ("da","co","po","ls","wa","en","to","sh","kl","sy", "ti")
5327     while scanner.next() == "IHEOL":
5328         proutn(_("Information desired? "))
5329     scanner.chew()
5330     if scanner.token in requests:
5331         status(requests.index(scanner.token))
5332     else:
5333         prout(_("UNRECOGNIZED REQUEST. Legal requests are:"))
5334         prout(("  date, condition, position, lsupport, warpfactor,"))
5335         prout(("  energy, torpedoes, shields, klingons, system, time."))
5336                 
5337 def srscan():
5338     "Short-range scan." 
5339     goodScan=True
5340     if damaged(DSRSENS):
5341         # Allow base's sensors if docked 
5342         if game.condition != "docked":
5343             prout(_("   S.R. SENSORS DAMAGED!"))
5344             goodScan=False
5345         else:
5346             prout(_("  [Using Base's sensors]"))
5347     else:
5348         prout(_("     Short-range scan"))
5349     if goodScan and not damaged(DRADIO): 
5350         game.state.chart[game.quadrant.i][game.quadrant.j].klingons = game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons
5351         game.state.chart[game.quadrant.i][game.quadrant.j].starbase = game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase
5352         game.state.chart[game.quadrant.i][game.quadrant.j].stars = game.state.galaxy[game.quadrant.i][game.quadrant.j].stars
5353         game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
5354     prout("    1 2 3 4 5 6 7 8 9 10")
5355     if game.condition != "docked":
5356         newcnd()
5357     for i in range(QUADSIZE):
5358         proutn("%2d  " % (i+1))
5359         for j in range(QUADSIZE):
5360             sectscan(goodScan, i, j)
5361         skip(1)
5362                         
5363 def eta():
5364     "Use computer to get estimated time of arrival for a warp jump."
5365     w1 = coord(); w2 = coord()
5366     prompt = False
5367     if damaged(DCOMPTR):
5368         prout(_("COMPUTER DAMAGED, USE A POCKET CALCULATOR."))
5369         skip(1)
5370         return
5371     if scanner.next() != "IHREAL":
5372         prompt = True
5373         scanner.chew()
5374         proutn(_("Destination quadrant and/or sector? "))
5375         if scanner.next()!="IHREAL":
5376             huh()
5377             return
5378     w1.j = int(scanner.real-0.5)
5379     if scanner.next() != "IHREAL":
5380         huh()
5381         return
5382     w1.i = int(scanner.real-0.5)
5383     if scanner.next() == "IHREAL":
5384         w2.j = int(scanner.real-0.5)
5385         if scanner.next() != "IHREAL":
5386             huh()
5387             return
5388         w2.i = int(scanner.real-0.5)
5389     else:
5390         if game.quadrant.j>w1.i:
5391             w2.i = 0
5392         else:
5393             w2.i=QUADSIZE-1
5394         if game.quadrant.i>w1.j:
5395             w2.j = 0
5396         else:
5397             w2.j=QUADSIZE-1
5398     if not VALID_QUADRANT(w1.i, w1.j) or not VALID_SECTOR(w2.i, w2.j):
5399         huh()
5400         return
5401     game.dist = math.sqrt((w1.j-game.quadrant.j+0.1*(w2.j-game.sector.j))**2+
5402                 (w1.i-game.quadrant.i+0.1*(w2.i-game.sector.i))**2)
5403     wfl = False
5404     if prompt:
5405         prout(_("Answer \"no\" if you don't know the value:"))
5406     while True:
5407         scanner.chew()
5408         proutn(_("Time or arrival date? "))
5409         if scanner.next()=="IHREAL":
5410             ttime = scanner.real
5411             if ttime > game.state.date:
5412                 ttime -= game.state.date # Actually a star date
5413             twarp=(math.floor(math.sqrt((10.0*game.dist)/ttime)*10.0)+1.0)/10.0
5414             if ttime <= 1e-10 or twarp > 10:
5415                 prout(_("We'll never make it, sir."))
5416                 scanner.chew()
5417                 return
5418             if twarp < 1.0:
5419                 twarp = 1.0
5420             break
5421         scanner.chew()
5422         proutn(_("Warp factor? "))
5423         if scanner.next()== "IHREAL":
5424             wfl = True
5425             twarp = scanner.real
5426             if twarp<1.0 or twarp > 10.0:
5427                 huh()
5428                 return
5429             break
5430         prout(_("Captain, certainly you can give me one of these."))
5431     while True:
5432         scanner.chew()
5433         ttime = (10.0*game.dist)/twarp**2
5434         tpower = game.dist*twarp*twarp*twarp*(game.shldup+1)
5435         if tpower >= game.energy:
5436             prout(_("Insufficient energy, sir."))
5437             if not game.shldup or tpower > game.energy*2.0:
5438                 if not wfl:
5439                     return
5440                 proutn(_("New warp factor to try? "))
5441                 if scanner.next() == "IHREAL":
5442                     wfl = True
5443                     twarp = scanner.real
5444                     if twarp<1.0 or twarp > 10.0:
5445                         huh()
5446                         return
5447                     continue
5448                 else:
5449                     scanner.chew()
5450                     skip(1)
5451                     return
5452             prout(_("But if you lower your shields,"))
5453             proutn(_("remaining"))
5454             tpower /= 2
5455         else:
5456             proutn(_("Remaining"))
5457         prout(_(" energy will be %.2f.") % (game.energy-tpower))
5458         if wfl:
5459             prout(_("And we will arrive at stardate %.2f.") % (game.state.date+ttime))
5460         elif twarp==1.0:
5461             prout(_("Any warp speed is adequate."))
5462         else:
5463             prout(_("Minimum warp needed is %.2f,") % (twarp))
5464             prout(_("and we will arrive at stardate %.2f.") % (game.state.date+ttime))
5465         if game.state.remtime < ttime:
5466             prout(_("Unfortunately, the Federation will be destroyed by then."))
5467         if twarp > 6.0:
5468             prout(_("You'll be taking risks at that speed, Captain"))
5469         if (game.isatb==1 and game.state.kscmdr == w1 and \
5470              scheduled(FSCDBAS)< ttime+game.state.date) or \
5471             (scheduled(FCDBAS)<ttime+game.state.date and game.battle == w1):
5472             prout(_("The starbase there will be destroyed by then."))
5473         proutn(_("New warp factor to try? "))
5474         if scanner.next() == "IHREAL":
5475             wfl = True
5476             twarp = scanner.real
5477             if twarp<1.0 or twarp > 10.0:
5478                 huh()
5479                 return
5480         else:
5481             scanner.chew()
5482             skip(1)
5483             return
5484
5485 # Code from setup.c begins here
5486
5487 def prelim():
5488     "Issue a historically correct banner."
5489     skip(2)
5490     prout(_("-SUPER- STAR TREK"))
5491     skip(1)
5492 # From the FORTRAN original
5493 #    prout(_("Latest update-21 Sept 78"))
5494 #    skip(1)
5495
5496 def freeze(boss):
5497     "Save game."
5498     if boss:
5499         scanner.token = "emsave.trk"
5500     else:
5501         key = scanner.next()
5502         if key == "IHEOL":
5503             proutn(_("File name: "))
5504             key = scanner.next()
5505         if key != "IHALPHA":
5506             huh()
5507             return
5508         scanner.chew()
5509         if '.' not in scanner.token:
5510             scanner.token += ".trk"
5511     try:
5512         fp = open(scanner.token, "wb")
5513     except IOError:
5514         prout(_("Can't freeze game as file %s") % scanner.token)
5515         return
5516     cPickle.dump(game, fp)
5517     fp.close()
5518
5519 def thaw():
5520     "Retrieve saved game." 
5521     game.passwd[0] = '\0'
5522     key = scanner.next()
5523     if key == "IHEOL":
5524         proutn(_("File name: "))
5525         key = scanner.next()
5526     if key != "IHALPHA":
5527         huh()
5528         return True
5529     scanner.chew()
5530     if '.' not in scanner.token:
5531         scanner.token += ".trk"
5532     try:
5533         fp = open(scanner.token, "rb")
5534     except IOError:
5535         prout(_("Can't thaw game in %s") % scanner.token)
5536         return
5537     game = cPickle.load(fp)
5538     fp.close()
5539     return False
5540
5541 # I used <http://www.memory-alpha.org> to find planets
5542 # with references in ST:TOS.  Eath and the Alpha Centauri
5543 # Colony have been omitted.
5544
5545 # Some planets marked Class G and P here will be displayed as class M
5546 # because of the way planets are generated. This is a known bug.
5547 systnames = (
5548     # Federation Worlds 
5549     _("Andoria (Fesoan)"),      # several episodes 
5550     _("Tellar Prime (Miracht)"),        # TOS: "Journey to Babel" 
5551     _("Vulcan (T'Khasi)"),      # many episodes 
5552     _("Medusa"),                # TOS: "Is There in Truth No Beauty?" 
5553     _("Argelius II (Nelphia)"),# TOS: "Wolf in the Fold" ("IV" in BSD) 
5554     _("Ardana"),                # TOS: "The Cloud Minders" 
5555     _("Catulla (Cendo-Prae)"),  # TOS: "The Way to Eden" 
5556     _("Gideon"),                # TOS: "The Mark of Gideon" 
5557     _("Aldebaran III"), # TOS: "The Deadly Years" 
5558     _("Alpha Majoris I"),       # TOS: "Wolf in the Fold" 
5559     _("Altair IV"),             # TOS: "Amok Time 
5560     _("Ariannus"),              # TOS: "Let That Be Your Last Battlefield" 
5561     _("Benecia"),               # TOS: "The Conscience of the King" 
5562     _("Beta Niobe I (Sarpeidon)"),      # TOS: "All Our Yesterdays" 
5563     _("Alpha Carinae II"),      # TOS: "The Ultimate Computer" 
5564     _("Capella IV (Kohath)"),   # TOS: "Friday's Child" (Class G) 
5565     _("Daran V"),               # TOS: "For the World is Hollow and I Have Touched the Sky" 
5566     _("Deneb II"),              # TOS: "Wolf in the Fold" ("IV" in BSD) 
5567     _("Eminiar VII"),           # TOS: "A Taste of Armageddon" 
5568     _("Gamma Canaris IV"),      # TOS: "Metamorphosis" 
5569     _("Gamma Tranguli VI (Vaalel)"),    # TOS: "The Apple" 
5570     _("Ingraham B"),            # TOS: "Operation: Annihilate" 
5571     _("Janus IV"),              # TOS: "The Devil in the Dark" 
5572     _("Makus III"),             # TOS: "The Galileo Seven" 
5573     _("Marcos XII"),            # TOS: "And the Children Shall Lead", 
5574     _("Omega IV"),              # TOS: "The Omega Glory" 
5575     _("Regulus V"),             # TOS: "Amok Time 
5576     _("Deneva"),                # TOS: "Operation -- Annihilate!" 
5577     # Worlds from BSD Trek 
5578     _("Rigel II"),              # TOS: "Shore Leave" ("III" in BSD) 
5579     _("Beta III"),              # TOS: "The Return of the Archons" 
5580     _("Triacus"),               # TOS: "And the Children Shall Lead", 
5581     _("Exo III"),               # TOS: "What Are Little Girls Made Of?" (Class P) 
5582 #       # Others 
5583 #    _("Hansen's Planet"),      # TOS: "The Galileo Seven" 
5584 #    _("Taurus IV"),            # TOS: "The Galileo Seven" (class G) 
5585 #    _("Antos IV (Doraphane)"), # TOS: "Whom Gods Destroy", "Who Mourns for Adonais?" 
5586 #    _("Izar"),                 # TOS: "Whom Gods Destroy" 
5587 #    _("Tiburon"),              # TOS: "The Way to Eden" 
5588 #    _("Merak II"),             # TOS: "The Cloud Minders" 
5589 #    _("Coridan (Desotriana)"), # TOS: "Journey to Babel" 
5590 #    _("Iotia"),                # TOS: "A Piece of the Action" 
5591 )
5592
5593 device = (
5594         _("S. R. Sensors"), \
5595         _("L. R. Sensors"), \
5596         _("Phasers"), \
5597         _("Photon Tubes"), \
5598         _("Life Support"), \
5599         _("Warp Engines"), \
5600         _("Impulse Engines"), \
5601         _("Shields"), \
5602         _("Subspace Radio"), \
5603         _("Shuttle Craft"), \
5604         _("Computer"), \
5605         _("Navigation System"), \
5606         _("Transporter"), \
5607         _("Shield Control"), \
5608         _("Death Ray"), \
5609         _("D. S. Probe"), \
5610 )
5611
5612 def setup():
5613     "Prepare to play, set up cosmos."
5614     w = coord()
5615     #  Decide how many of everything
5616     if choose():
5617         return # frozen game
5618     # Prepare the Enterprise
5619     game.alldone = game.gamewon = False
5620     game.ship = IHE
5621     game.state.crew = FULLCREW
5622     game.energy = game.inenrg = 5000.0
5623     game.shield = game.inshld = 2500.0
5624     game.shldchg = False
5625     game.shldup = False
5626     game.inlsr = 4.0
5627     game.lsupres = 4.0
5628     game.quadrant = randplace(GALSIZE)
5629     game.sector = randplace(QUADSIZE)
5630     game.torps = game.intorps = 10
5631     game.nprobes = randrange(2, 5)
5632     game.warpfac = 5.0
5633     game.wfacsq = game.warpfac * game.warpfac
5634     for i in range(NDEVICES): 
5635         game.damage[i] = 0.0
5636     # Set up assorted game parameters
5637     game.battle = coord()
5638     game.state.date = game.indate = 100.0 * randreal(20, 51)
5639     game.nkinks = game.nhelp = game.casual = game.abandoned = 0
5640     game.iscate = game.resting = game.imine = game.icrystl = game.icraft = False
5641     game.isatb = game.state.nplankl = 0
5642     game.state.starkl = game.state.basekl = 0
5643     game.iscraft = "onship"
5644     game.landed = False
5645     game.alive = True
5646     game.docfac = 0.25
5647     # Starchart is functional but we've never seen it
5648     game.lastchart = FOREVER
5649     # Put stars in the galaxy
5650     game.instar = 0
5651     for i in range(GALSIZE):
5652         for j in range(GALSIZE):
5653             k = randrange(1, QUADSIZE**2/10+1)
5654             game.instar += k
5655             game.state.galaxy[i][j].stars = k
5656     # Locate star bases in galaxy
5657     for i in range(game.inbase):
5658         while True:
5659             while True:
5660                 w = randplace(GALSIZE)
5661                 if not game.state.galaxy[w.i][w.j].starbase:
5662                     break
5663             contflag = False
5664             # C version: for (j = i-1; j > 0; j--)
5665             # so it did them in the opposite order.
5666             for j in range(1, i):
5667                 # Improved placement algorithm to spread out bases
5668                 distq = (w - game.state.baseq[j]).distance()
5669                 if distq < 6.0*(BASEMAX+1-game.inbase) and withprob(0.75):
5670                     contflag = True
5671                     if idebug:
5672                         prout("=== Abandoning base #%d at %s" % (i, w))
5673                     break
5674                 elif distq < 6.0 * (BASEMAX+1-game.inbase):
5675                     if idebug:
5676                         prout("=== Saving base #%d, close to #%d" % (i, j))
5677             if not contflag:
5678                 break
5679         game.state.baseq.append(w)
5680         game.state.galaxy[w.i][w.j].starbase = game.state.chart[w.i][w.j].starbase = True
5681     # Position ordinary Klingon Battle Cruisers
5682     krem = game.inkling
5683     klumper = 0.25*game.skill*(9.0-game.length)+1.0
5684     if klumper > MAXKLQUAD: 
5685         klumper = MAXKLQUAD
5686     while True:
5687         r = randreal()
5688         klump = (1.0 - r*r)*klumper
5689         if klump > krem:
5690             klump = krem
5691         krem -= klump
5692         while True:
5693             w = randplace(GALSIZE)
5694             if not game.state.galaxy[w.i][w.j].supernova and \
5695                game.state.galaxy[w.i][w.j].klingons + klump <= MAXKLQUAD:
5696                 break
5697         game.state.galaxy[w.i][w.j].klingons += int(klump)
5698         if krem <= 0:
5699             break
5700     # Position Klingon Commander Ships
5701     for i in range(game.incom):
5702         while True:
5703             w = randplace(GALSIZE)
5704             if not welcoming(w) or w in game.state.kcmdr:
5705                 continue
5706             if (game.state.galaxy[w.i][w.j].klingons or withprob(0.25)):
5707                 break
5708         game.state.galaxy[w.i][w.j].klingons += 1
5709         game.state.kcmdr.append(w)
5710     # Locate planets in galaxy
5711     for i in range(game.inplan):
5712         while True:
5713             w = randplace(GALSIZE) 
5714             if game.state.galaxy[w.i][w.j].planet == None:
5715                 break
5716         new = planet()
5717         new.quadrant = w
5718         new.crystals = "absent"
5719         if (game.options & OPTION_WORLDS) and i < NINHAB:
5720             new.pclass = "M"    # All inhabited planets are class M
5721             new.crystals = "absent"
5722             new.known = "known"
5723             new.name = systnames[i]
5724             new.inhabited = True
5725         else:
5726             new.pclass = ("M", "N", "O")[randrange(0, 3)]
5727             if withprob(0.33):
5728                 new.crystals = "present"
5729             new.known = "unknown"
5730             new.inhabited = False
5731         game.state.galaxy[w.i][w.j].planet = new
5732         game.state.planets.append(new)
5733     # Locate Romulans
5734     for i in range(game.state.nromrem):
5735         w = randplace(GALSIZE)
5736         game.state.galaxy[w.i][w.j].romulans += 1
5737     # Place the Super-Commander if needed
5738     if game.state.nscrem > 0:
5739         while True:
5740             w = randplace(GALSIZE)
5741             if welcoming(w):
5742                 break
5743         game.state.kscmdr = w
5744         game.state.galaxy[w.i][w.j].klingons += 1
5745     # Initialize times for extraneous events
5746     schedule(FSNOVA, expran(0.5 * game.intime))
5747     schedule(FTBEAM, expran(1.5 * (game.intime / len(game.state.kcmdr))))
5748     schedule(FSNAP, randreal(1.0, 2.0)) # Force an early snapshot
5749     schedule(FBATTAK, expran(0.3*game.intime))
5750     unschedule(FCDBAS)
5751     if game.state.nscrem:
5752         schedule(FSCMOVE, 0.2777)
5753     else:
5754         unschedule(FSCMOVE)
5755     unschedule(FSCDBAS)
5756     unschedule(FDSPROB)
5757     if (game.options & OPTION_WORLDS) and game.skill >= SKILL_GOOD:
5758         schedule(FDISTR, expran(1.0 + game.intime))
5759     else:
5760         unschedule(FDISTR)
5761     unschedule(FENSLV)
5762     unschedule(FREPRO)
5763     # Place thing (in tournament game, we don't want one!)
5764     # New in SST2K: never place the Thing near a starbase.
5765     # This makes sense and avoids a special case in the old code.
5766     global thing
5767     if game.tourn is None:
5768         while True:
5769             thing = randplace(GALSIZE)
5770             if thing not in game.state.baseq:
5771                 break
5772     skip(2)
5773     game.state.snap = False
5774     if game.skill == SKILL_NOVICE:
5775         prout(_("It is stardate %d. The Federation is being attacked by") % int(game.state.date))
5776         prout(_("a deadly Klingon invasion force. As captain of the United"))
5777         prout(_("Starship U.S.S. Enterprise, it is your mission to seek out"))
5778         prout(_("and destroy this invasion force of %d battle cruisers.") % ((game.inkling + game.incom + game.inscom)))
5779         prout(_("You have an initial allotment of %d stardates to complete") % int(game.intime))
5780         prout(_("your mission.  As you proceed you may be given more time."))
5781         skip(1)
5782         prout(_("You will have %d supporting starbases.") % (game.inbase))
5783         proutn(_("Starbase locations-  "))
5784     else:
5785         prout(_("Stardate %d.") % int(game.state.date))
5786         skip(1)
5787         prout(_("%d Klingons.") % (game.inkling + game.incom + game.inscom))
5788         prout(_("An unknown number of Romulans."))
5789         if game.state.nscrem:
5790             prout(_("And one (GULP) Super-Commander."))
5791         prout(_("%d stardates.") % int(game.intime))
5792         proutn(_("%d starbases in ") % game.inbase)
5793     for i in range(game.inbase):
5794         proutn(`game.state.baseq[i]`)
5795         proutn("  ")
5796     skip(2)
5797     proutn(_("The Enterprise is currently in Quadrant %s") % game.quadrant)
5798     proutn(_(" Sector %s") % game.sector)
5799     skip(2)
5800     prout(_("Good Luck!"))
5801     if game.state.nscrem:
5802         prout(_("  YOU'LL NEED IT."))
5803     waitfor()
5804     newqad()
5805     if len(game.enemies) - (thing == game.quadrant) - (game.tholian != None):
5806         game.shldup = True
5807     if game.neutz:      # bad luck to start in a Romulan Neutral Zone
5808         attack(torps_ok=False)
5809
5810 def choose():
5811     "Choose your game type."
5812     global thing
5813     while True:
5814         game.tourn = 0
5815         game.thawed = False
5816         game.skill = SKILL_NONE
5817         game.length = 0
5818         if not scanner.inqueue: # Can start with command line options 
5819             proutn(_("Would you like a regular, tournament, or saved game? "))
5820         scanner.next()
5821         if len(scanner.token)==0: # Try again
5822             continue
5823         if scanner.sees("tournament"):
5824             while scanner.next() == "IHEOL":
5825                 proutn(_("Type in tournament number-"))
5826             if scanner.real == 0:
5827                 scanner.chew()
5828                 continue # We don't want a blank entry
5829             game.tourn = int(round(scanner.real))
5830             random.seed(scanner.real)
5831             if logfp:
5832                 logfp.write("# random.seed(%d)\n" % scanner.real)
5833             break
5834         if scanner.sees("saved") or scanner.sees("frozen"):
5835             if thaw():
5836                 continue
5837             scanner.chew()
5838             if game.passwd == None:
5839                 continue
5840             if not game.alldone:
5841                 game.thawed = True # No plaque if not finished
5842             report()
5843             waitfor()
5844             return True
5845         if scanner.sees("regular"):
5846             break
5847         proutn(_("What is \"%s\"?") % scanner.token)
5848         scanner.chew()
5849     while game.length==0 or game.skill==SKILL_NONE:
5850         if scanner.next() == "IHALPHA":
5851             if scanner.sees("short"):
5852                 game.length = 1
5853             elif scanner.sees("medium"):
5854                 game.length = 2
5855             elif scanner.sees("long"):
5856                 game.length = 4
5857             elif scanner.sees("novice"):
5858                 game.skill = SKILL_NOVICE
5859             elif scanner.sees("fair"):
5860                 game.skill = SKILL_FAIR
5861             elif scanner.sees("good"):
5862                 game.skill = SKILL_GOOD
5863             elif scanner.sees("expert"):
5864                 game.skill = SKILL_EXPERT
5865             elif scanner.sees("emeritus"):
5866                 game.skill = SKILL_EMERITUS
5867             else:
5868                 proutn(_("What is \""))
5869                 proutn(scanner.token)
5870                 prout("\"?")
5871         else:
5872             scanner.chew()
5873             if game.length==0:
5874                 proutn(_("Would you like a Short, Medium, or Long game? "))
5875             elif game.skill == SKILL_NONE:
5876                 proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
5877     # Choose game options -- added by ESR for SST2K
5878     if scanner.next() != "IHALPHA":
5879         scanner.chew()
5880         proutn(_("Choose your game style (or just press enter): "))
5881         scanner.next()
5882     if scanner.sees("plain"):
5883         # Approximates the UT FORTRAN version.
5884         game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
5885         game.options |= OPTION_PLAIN
5886     elif scanner.sees("almy"):
5887         # Approximates Tom Almy's version.
5888         game.options &=~ (OPTION_THINGY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
5889         game.options |= OPTION_ALMY
5890     elif scanner.sees("fancy"):
5891         pass
5892     elif len(scanner.token):
5893         proutn(_("What is \"%s\"?") % scanner.token)
5894     setpassword()
5895     if game.passwd == "debug":
5896         idebug = True
5897         prout("=== Debug mode enabled.")
5898     # Use parameters to generate initial values of things
5899     game.damfac = 0.5 * game.skill
5900     game.inbase = randrange(BASEMIN, BASEMAX+1)
5901     game.inplan = 0
5902     if game.options & OPTION_PLANETS:
5903         game.inplan += randrange(MAXUNINHAB/2, MAXUNINHAB+1)
5904     if game.options & OPTION_WORLDS:
5905         game.inplan += int(NINHAB)
5906     game.state.nromrem = game.inrom = randrange(2 *game.skill)
5907     game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR)
5908     game.state.remtime = 7.0 * game.length
5909     game.intime = game.state.remtime
5910     game.state.remkl = game.inkling = 2.0*game.intime*((game.skill+1 - 2*randreal())*game.skill*0.1+.15)
5911     game.incom = min(MINCMDR, int(game.skill + 0.0625*game.inkling*randreal()))
5912     game.state.remres = (game.inkling+4*game.incom)*game.intime
5913     game.inresor = game.state.remres
5914     if game.inkling > 50:
5915         game.state.inbase += 1
5916     return False
5917
5918 def dropin(iquad=None):
5919     "Drop a feature on a random dot in the current quadrant."
5920     while True:
5921         w = randplace(QUADSIZE)
5922         if game.quad[w.i][w.j] == IHDOT:
5923             break
5924     if iquad is not None:
5925         game.quad[w.i][w.j] = iquad
5926     return w
5927
5928 def newcnd():
5929     "Update our alert status."
5930     game.condition = "green"
5931     if game.energy < 1000.0:
5932         game.condition = "yellow"
5933     if game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons or game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans:
5934         game.condition = "red"
5935     if not game.alive:
5936         game.condition="dead"
5937
5938 def newkling():
5939     "Drop new Klingon into current quadrant."
5940     return enemy(IHK, loc=dropin(), power=randreal(300,450)+25.0*game.skill)
5941
5942 def newqad():
5943     "Set up a new state of quadrant, for when we enter or re-enter it."
5944     game.justin = True
5945     game.iplnet = None
5946     game.neutz = game.inorbit = game.landed = False
5947     game.ientesc = game.iseenit = False
5948     # Create a blank quadrant
5949     game.quad = fill2d(QUADSIZE, lambda i, j: IHDOT)
5950     if game.iscate:
5951         # Attempt to escape Super-commander, so tbeam back!
5952         game.iscate = False
5953         game.ientesc = True
5954     q = game.state.galaxy[game.quadrant.i][game.quadrant.j]
5955     # cope with supernova
5956     if q.supernova:
5957         return
5958     game.klhere = q.klingons
5959     game.irhere = q.romulans
5960     # Position Starship
5961     game.quad[game.sector.i][game.sector.j] = game.ship
5962     game.enemies = []
5963     if q.klingons:
5964         # Position ordinary Klingons
5965         for i in range(game.klhere):
5966             newkling()
5967         # If we need a commander, promote a Klingon
5968         for cmdr in game.state.kcmdr:
5969             if cmdr == game.quadrant:
5970                 e = game.enemies[game.klhere-1]
5971                 game.quad[e.kloc.i][e.kloc.j] = IHC
5972                 e.kpower = randreal(950,1350) + 50.0*game.skill
5973                 break   
5974         # If we need a super-commander, promote a Klingon
5975         if game.quadrant == game.state.kscmdr:
5976             e = game.enemies[0]
5977             game.quad[e.kloc.i][e.kloc.j] = IHS
5978             e.kpower = randreal(1175.0,  1575.0) + 125.0*game.skill
5979             game.iscate = (game.state.remkl > 1)
5980     # Put in Romulans if needed
5981     for i in range(q.romulans):
5982         enemy(IHR, loc=dropin(), power=randreal(400.0,850.0)+50.0*game.skill)
5983     # If quadrant needs a starbase, put it in
5984     if q.starbase:
5985         game.base = dropin(IHB)
5986     # If quadrant needs a planet, put it in
5987     if q.planet:
5988         game.iplnet = q.planet
5989         if not q.planet.inhabited:
5990             game.plnet = dropin(IHP)
5991         else:
5992             game.plnet = dropin(IHW)
5993     # Check for condition
5994     newcnd()
5995     # Check for RNZ
5996     if game.irhere > 0 and game.klhere == 0:
5997         game.neutz = True
5998         if not damaged(DRADIO):
5999             skip(1)
6000             prout(_("LT. Uhura- \"Captain, an urgent message."))
6001             prout(_("  I'll put it on audio.\"  CLICK"))
6002             skip(1)
6003             prout(_("INTRUDER! YOU HAVE VIOLATED THE ROMULAN NEUTRAL ZONE."))
6004             prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
6005     # Put in THING if needed
6006     if thing == game.quadrant:
6007         enemy(type=IHQUEST, loc=dropin(),
6008                   power=randreal(6000,6500.0)+250.0*game.skill)
6009         if not damaged(DSRSENS):
6010             skip(1)
6011             prout(_("Mr. Spock- \"Captain, this is most unusual."))
6012             prout(_("    Please examine your short-range scan.\""))
6013     # Decide if quadrant needs a Tholian; lighten up if skill is low 
6014     if game.options & OPTION_THOLIAN:
6015         if (game.skill < SKILL_GOOD and withprob(0.02)) or \
6016             (game.skill == SKILL_GOOD and withprob(0.05)) or \
6017             (game.skill > SKILL_GOOD and withprob(0.08)):
6018             w = coord()
6019             while True:
6020                 w.i = withprob(0.5) * (QUADSIZE-1)
6021                 w.j = withprob(0.5) * (QUADSIZE-1)
6022                 if game.quad[w.i][w.j] == IHDOT:
6023                     break
6024             game.tholian = enemy(type=IHT, loc=w,
6025                                  power=randrange(100, 500) + 25.0*game.skill)
6026             # Reserve unoccupied corners 
6027             if game.quad[0][0]==IHDOT:
6028                 game.quad[0][0] = 'X'
6029             if game.quad[0][QUADSIZE-1]==IHDOT:
6030                 game.quad[0][QUADSIZE-1] = 'X'
6031             if game.quad[QUADSIZE-1][0]==IHDOT:
6032                 game.quad[QUADSIZE-1][0] = 'X'
6033             if game.quad[QUADSIZE-1][QUADSIZE-1]==IHDOT:
6034                 game.quad[QUADSIZE-1][QUADSIZE-1] = 'X'
6035     game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
6036     # And finally the stars
6037     for i in range(q.stars):
6038         dropin(IHSTAR)
6039     # Put in a few black holes
6040     for i in range(1, 3+1):
6041         if withprob(0.5): 
6042             dropin(IHBLANK)
6043     # Take out X's in corners if Tholian present
6044     if game.tholian:
6045         if game.quad[0][0]=='X':
6046             game.quad[0][0] = IHDOT
6047         if game.quad[0][QUADSIZE-1]=='X':
6048             game.quad[0][QUADSIZE-1] = IHDOT
6049         if game.quad[QUADSIZE-1][0]=='X':
6050             game.quad[QUADSIZE-1][0] = IHDOT
6051         if game.quad[QUADSIZE-1][QUADSIZE-1]=='X':
6052             game.quad[QUADSIZE-1][QUADSIZE-1] = IHDOT
6053
6054 def setpassword():
6055     "Set the self-destruct password."
6056     if game.options & OPTION_PLAIN:
6057         while True:
6058             scanner.chew()
6059             proutn(_("Please type in a secret password- "))
6060             scanner.next()
6061             game.passwd = scanner.token
6062             if game.passwd != None:
6063                 break
6064     else:
6065         game.passwd = ""
6066         for i in range(8):
6067             game.passwd += chr(ord('a')+randrange(26))
6068
6069 # Code from sst.c begins here
6070
6071 commands = {
6072     "SRSCAN":           OPTION_TTY,
6073     "STATUS":           OPTION_TTY,
6074     "REQUEST":          OPTION_TTY,
6075     "LRSCAN":           OPTION_TTY,
6076     "PHASERS":          0,
6077     "TORPEDO":          0,
6078     "PHOTONS":          0,
6079     "MOVE":             0,
6080     "SHIELDS":          0,
6081     "DOCK":             0,
6082     "DAMAGES":          0,
6083     "CHART":            0,
6084     "IMPULSE":          0,
6085     "REST":             0,
6086     "WARP":             0,
6087     "SCORE":            0,
6088     "SENSORS":          OPTION_PLANETS,
6089     "ORBIT":            OPTION_PLANETS,
6090     "TRANSPORT":        OPTION_PLANETS,
6091     "MINE":             OPTION_PLANETS,
6092     "CRYSTALS":         OPTION_PLANETS,
6093     "SHUTTLE":          OPTION_PLANETS,
6094     "PLANETS":          OPTION_PLANETS,
6095     "REPORT":           0,
6096     "COMPUTER":         0,
6097     "COMMANDS":         0,
6098     "EMEXIT":           0,
6099     "PROBE":            OPTION_PROBE,
6100     "SAVE":             0,
6101     "FREEZE":           0,      # Synonym for SAVE
6102     "ABANDON":          0,
6103     "DESTRUCT":         0,
6104     "DEATHRAY":         0,
6105     "DEBUG":            0,
6106     "MAYDAY":           0,
6107     "SOS":              0,      # Synonym for MAYDAY
6108     "CALL":             0,      # Synonym for MAYDAY
6109     "QUIT":             0,
6110     "HELP":             0,
6111 }
6112
6113 def listCommands():
6114     "Generate a list of legal commands."
6115     proutn(_("LEGAL COMMANDS ARE:"))
6116     for (k, key) in enumerate(commands):
6117         if not commands[key] or (commands[key] & game.options):
6118             if k % 5 == 0:
6119                 skip(1)
6120             proutn("%-12s " % key) 
6121     skip(1)
6122
6123 def helpme():
6124     "Browse on-line help."
6125     key = scanner.next()
6126     while True:
6127         if key == "IHEOL":
6128             setwnd(prompt_window)
6129             proutn(_("Help on what command? "))
6130             key = scanner.next()
6131         setwnd(message_window)
6132         if key == "IHEOL":
6133             return
6134         if scanner.token in commands or scanner.token == "ABBREV":
6135             break
6136         skip(1)
6137         listCommands()
6138         key = "IHEOL"
6139         scanner.chew()
6140         skip(1)
6141     cmd = scanner.token.upper()
6142     try:
6143         fp = open(SSTDOC, "r")
6144     except IOError:
6145         try:
6146             fp = open(DOC_NAME, "r")
6147         except IOError:
6148             prout(_("Spock-  \"Captain, that information is missing from the"))
6149             proutn(_("   computer. You need to find "))
6150             proutn(DOC_NAME)
6151             prout(_(" and put it in the"))
6152             proutn(_("   current directory or to "))
6153             proutn(SSTDOC)
6154             prout(".\"")
6155             #
6156             # This used to continue: "You need to find SST.DOC and put 
6157             # it in the current directory."
6158             # 
6159             return
6160     while True:
6161         linebuf = fp.readline()
6162         if linebuf == '':
6163             prout(_("Spock- \"Captain, there is no information on that command.\""))
6164             fp.close()
6165             return
6166         if linebuf[0] == '%' and linebuf[1] == '%' and linebuf[2] == ' ':
6167             linebuf = linebuf[3:].strip()
6168             if cmd == linebuf:
6169                 break
6170     skip(1)
6171     prout(_("Spock- \"Captain, I've found the following information:\""))
6172     skip(1)
6173     while linebuf in fp:
6174         if "******" in linebuf:
6175             break
6176         proutn(linebuf)
6177     fp.close()
6178
6179 def makemoves():
6180     "Command-interpretation loop."
6181     clrscr()
6182     setwnd(message_window)
6183     while True:         # command loop 
6184         drawmaps(1)
6185         while True:     # get a command 
6186             hitme = False
6187             game.justin = False
6188             game.optime = 0.0
6189             scanner.chew()
6190             setwnd(prompt_window)
6191             clrscr()
6192             proutn("COMMAND> ")
6193             if scanner.next() == "IHEOL":
6194                 if game.options & OPTION_CURSES:
6195                     makechart()
6196                 continue
6197             elif scanner.token == "":
6198                 continue
6199             game.ididit = False
6200             clrscr()
6201             setwnd(message_window)
6202             clrscr()
6203             candidates = filter(lambda x: x.startswith(scanner.token.upper()),
6204                                 commands)
6205             if len(candidates) == 1:
6206                 cmd = candidates[0]
6207                 break
6208             elif candidates and not (game.options & OPTION_PLAIN):
6209                 prout("Commands with prefix '%s': %s" % (scanner.token, " ".join(candidates)))
6210             else:
6211                 listCommands()
6212                 continue
6213         if cmd == "SRSCAN":             # srscan
6214             srscan()
6215         elif cmd == "STATUS":           # status
6216             status()
6217         elif cmd == "REQUEST":          # status request 
6218             request()
6219         elif cmd == "LRSCAN":           # long range scan
6220             lrscan(silent=False)
6221         elif cmd == "PHASERS":          # phasers
6222             phasers()
6223             if game.ididit:
6224                 hitme = True
6225         elif cmd == "TORPEDO":          # photon torpedos
6226             photon()
6227             if game.ididit:
6228                 hitme = True
6229         elif cmd == "MOVE":             # move under warp
6230             warp(False)
6231         elif cmd == "SHIELDS":          # shields
6232             doshield(shraise=False)
6233             if game.ididit:
6234                 hitme = True
6235                 game.shldchg = False
6236         elif cmd == "DOCK":             # dock at starbase
6237             dock(True)
6238             if game.ididit:
6239                 attack(torps_ok=False)          
6240         elif cmd == "DAMAGES":          # damage reports
6241             damagereport()
6242         elif cmd == "CHART":            # chart
6243             makechart()
6244         elif cmd == "IMPULSE":          # impulse
6245             impulse()
6246         elif cmd == "REST":             # rest
6247             wait()
6248             if game.ididit:
6249                 hitme = True
6250         elif cmd == "WARP":             # warp
6251             setwarp()
6252         elif cmd == "SCORE":            # score
6253             score()
6254         elif cmd == "SENSORS":          # sensors
6255             sensor()
6256         elif cmd == "ORBIT":            # orbit
6257             orbit()
6258             if game.ididit:
6259                 hitme = True
6260         elif cmd == "TRANSPORT":                # transport "beam"
6261             beam()
6262         elif cmd == "MINE":             # mine
6263             mine()
6264             if game.ididit:
6265                 hitme = True
6266         elif cmd == "CRYSTALS":         # crystals
6267             usecrystals()
6268             if game.ididit:
6269                 hitme = True
6270         elif cmd == "SHUTTLE":          # shuttle
6271             shuttle()
6272             if game.ididit:
6273                 hitme = True
6274         elif cmd == "PLANETS":          # Planet list
6275             survey()
6276         elif cmd == "REPORT":           # Game Report 
6277             report()
6278         elif cmd == "COMPUTER":         # use COMPUTER!
6279             eta()
6280         elif cmd == "COMMANDS":
6281             listCommands()
6282         elif cmd == "EMEXIT":           # Emergency exit
6283             clrscr()                    # Hide screen
6284             freeze(True)                # forced save
6285             raise SysExit,1                     # And quick exit
6286         elif cmd == "PROBE":
6287             probe()                     # Launch probe
6288             if game.ididit:
6289                 hitme = True
6290         elif cmd == "ABANDON":          # Abandon Ship
6291             abandon()
6292         elif cmd == "DESTRUCT":         # Self Destruct
6293             selfdestruct()
6294         elif cmd == "SAVE":             # Save Game
6295             freeze(False)
6296             clrscr()
6297             if game.skill > SKILL_GOOD:
6298                 prout(_("WARNING--Saved games produce no plaques!"))
6299         elif cmd == "DEATHRAY":         # Try a desparation measure
6300             deathray()
6301             if game.ididit:
6302                 hitme = True
6303         elif cmd == "DEBUGCMD":         # What do we want for debug???
6304             debugme()
6305         elif cmd == "MAYDAY":           # Call for help
6306             mayday()
6307             if game.ididit:
6308                 hitme = True
6309         elif cmd == "QUIT":
6310             game.alldone = True         # quit the game
6311         elif cmd == "HELP":
6312             helpme()                    # get help
6313         while True:
6314             if game.alldone:
6315                 break           # Game has ended
6316             if game.optime != 0.0:
6317                 events()
6318                 if game.alldone:
6319                     break       # Events did us in
6320             if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
6321                 atover(False)
6322                 continue
6323             if hitme and not game.justin:
6324                 attack(torps_ok=True)
6325                 if game.alldone:
6326                     break
6327                 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
6328                     atover(False)
6329                     hitme = True
6330                     continue
6331             break
6332         if game.alldone:
6333             break
6334     if idebug:
6335         prout("=== Ending")
6336
6337 def cramen(type):
6338     "Emit the name of an enemy or feature." 
6339     if   type == IHR: s = _("Romulan")
6340     elif type == IHK: s = _("Klingon")
6341     elif type == IHC: s = _("Commander")
6342     elif type == IHS: s = _("Super-commander")
6343     elif type == IHSTAR: s = _("Star")
6344     elif type == IHP: s = _("Planet")
6345     elif type == IHB: s = _("Starbase")
6346     elif type == IHBLANK: s = _("Black hole")
6347     elif type == IHT: s = _("Tholian")
6348     elif type == IHWEB: s = _("Tholian web")
6349     elif type == IHQUEST: s = _("Stranger")
6350     elif type == IHW: s = _("Inhabited World")
6351     else: s = "Unknown??"
6352     return s
6353
6354 def crmena(stars, enemy, loctype, w):
6355     "Emit the name of an enemy and his location."
6356     buf = ""
6357     if stars:
6358         buf += "***"
6359     buf += cramen(enemy) + _(" at ")
6360     if loctype == "quadrant":
6361         buf += _("Quadrant ")
6362     elif loctype == "sector":
6363         buf += _("Sector ")
6364     return buf + `w`
6365
6366 def crmshp():
6367     "Emit our ship name." 
6368     return{IHE:_("Enterprise"),IHF:_("Faerie Queene")}.get(game.ship,"Ship???")
6369
6370 def stars():
6371     "Emit a line of stars" 
6372     prouts("******************************************************")
6373     skip(1)
6374
6375 def expran(avrage):
6376     return -avrage*math.log(1e-7 + randreal())
6377
6378 def randplace(size):
6379     "Choose a random location."
6380     w = coord()
6381     w.i = randrange(size) 
6382     w.j = randrange(size)
6383     return w
6384
6385 class sstscanner:
6386     def __init__(self):
6387         self.type = None
6388         self.token = None
6389         self.real = 0.0
6390         self.inqueue = []
6391     def next(self):
6392         # Get a token from the user
6393         self.real = 0.0
6394         self.token = ''
6395         # Fill the token quue if nothing here
6396         while not self.inqueue:
6397             line = cgetline()
6398             if curwnd==prompt_window:
6399                 clrscr()
6400                 setwnd(message_window)
6401                 clrscr()
6402             if line == '':
6403                 return None
6404             # Skip leading white space
6405             line = line.lstrip()
6406             if not line:
6407                 continue
6408             else:
6409                 self.inqueue = line.lstrip().split() + ["IHEOL"] 
6410         # From here on in it's all looking at the queue
6411         self.token = self.inqueue.pop(0)
6412         if self.token == "IHEOL":
6413             self.type = "IHEOL"
6414             return "IHEOL"
6415         try:
6416             self.real = float(self.token)
6417             self.type = "IHREAL"
6418             return "IHREAL"
6419         except ValueError:
6420             pass
6421         # Treat as alpha
6422         self.token = self.token.lower()
6423         self.type = "IHALPHA"
6424         self.real = None
6425         return "IHALPHA"
6426     def push(self, tok):
6427         self.inqueue.append(tok)
6428     def waiting(self):
6429         return self.inqueue
6430     def chew(self):
6431         # Demand input for next scan
6432         self.inqueue = []
6433         self.real = self.token = None
6434     def chew2(self):
6435         # return "IHEOL" next time 
6436         self.inqueue = ["IHEOL"]
6437         self.real = self.token = None
6438     def sees(self, s):
6439         # compares s to item and returns true if it matches to the length of s
6440         return s.startswith(self.token)
6441     def int(self):
6442         # Round token value to nearest integer
6443         return int(round(scanner.real))
6444     def getcoord(self):
6445         s = coord()
6446         scanner.next()
6447         if scanner.type != "IHREAL":
6448             huh()
6449             return None
6450         s.i = scanner.int()-1
6451         scanner.next()
6452         if scanner.type != "IHREAL":
6453             huh()
6454             return None
6455         s.j = scanner.int()-1
6456         return s
6457     def __repr__(str):
6458         return "<sstcanner: token=%s, type=%s, queue=%s>" % (scanner.token, scanner.type, scanner.inqueue)
6459
6460 def ja():
6461     "Yes-or-no confirmation."
6462     scanner.chew()
6463     while True:
6464         scanner.next()
6465         if scanner.token == 'y':
6466             return True
6467         if scanner.token == 'n':
6468             return False
6469         scanner.chew()
6470         proutn(_("Please answer with \"y\" or \"n\": "))
6471
6472 def huh():
6473     "Complain about unparseable input."
6474     scanner.chew()
6475     skip(1)
6476     prout(_("Beg your pardon, Captain?"))
6477
6478 def debugme():
6479     "Access to the internals for debugging."
6480     proutn("Reset levels? ")
6481     if ja() == True:
6482         if game.energy < game.inenrg:
6483             game.energy = game.inenrg
6484         game.shield = game.inshld
6485         game.torps = game.intorps
6486         game.lsupres = game.inlsr
6487     proutn("Reset damage? ")
6488     if ja() == True:
6489         for i in range(NDEVICES): 
6490             if game.damage[i] > 0.0: 
6491                 game.damage[i] = 0.0
6492     proutn("Toggle debug flag? ")
6493     if ja() == True:
6494         idebug = not idebug
6495         if idebug:
6496             prout("Debug output ON")        
6497         else:
6498             prout("Debug output OFF")
6499     proutn("Cause selective damage? ")
6500     if ja() == True:
6501         for i in range(NDEVICES):
6502             proutn("Kill %s?" % device[i])
6503             scanner.chew()
6504             key = scanner.next()
6505             if key == "IHALPHA" and scanner.sees("y"):
6506                 game.damage[i] = 10.0
6507     proutn("Examine/change events? ")
6508     if ja() == True:
6509         ev = event()
6510         w = coord()
6511         legends = {
6512             FSNOVA:  "Supernova       ",
6513             FTBEAM:  "T Beam          ",
6514             FSNAP:   "Snapshot        ",
6515             FBATTAK: "Base Attack     ",
6516             FCDBAS:  "Base Destroy    ",
6517             FSCMOVE: "SC Move         ",
6518             FSCDBAS: "SC Base Destroy ",
6519             FDSPROB: "Probe Move      ",
6520             FDISTR:  "Distress Call   ",
6521             FENSLV:  "Enslavement     ",
6522             FREPRO:  "Klingon Build   ",
6523         }
6524         for i in range(1, NEVENTS):
6525             proutn(legends[i])
6526             if is_scheduled(i):
6527                 proutn("%.2f" % (scheduled(i)-game.state.date))
6528                 if i == FENSLV or i == FREPRO:
6529                     ev = findevent(i)
6530                     proutn(" in %s" % ev.quadrant)
6531             else:
6532                 proutn("never")
6533             proutn("? ")
6534             scanner.chew()
6535             key = scanner.next()
6536             if key == 'n':
6537                 unschedule(i)
6538                 scanner.chew()
6539             elif key == "IHREAL":
6540                 ev = schedule(i, scanner.real)
6541                 if i == FENSLV or i == FREPRO:
6542                     scanner.chew()
6543                     proutn("In quadrant- ")
6544                     key = scanner.next()
6545                     # "IHEOL" says to leave coordinates as they are 
6546                     if key != "IHEOL":
6547                         if key != "IHREAL":
6548                             prout("Event %d canceled, no x coordinate." % (i))
6549                             unschedule(i)
6550                             continue
6551                         w.i = int(round(scanner.real))
6552                         key = scanner.next()
6553                         if key != "IHREAL":
6554                             prout("Event %d canceled, no y coordinate." % (i))
6555                             unschedule(i)
6556                             continue
6557                         w.j = int(round(scanner.real))
6558                         ev.quadrant = w
6559         scanner.chew()
6560     proutn("Induce supernova here? ")
6561     if ja() == True:
6562         game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova = True
6563         atover(True)
6564
6565 if __name__ == '__main__':
6566     try:
6567         global line, thing, game, idebug
6568         game = None
6569         thing = coord()
6570         thing.angry = False
6571         game = gamestate()
6572         idebug = 0
6573         game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY)
6574         # Disable curses mode until the game logic is working.
6575         #    if os.getenv("TERM"):
6576         #       game.options |= OPTION_CURSES | OPTION_SHOWME
6577         #    else:
6578         game.options |= OPTION_TTY
6579         seed = int(time.time())
6580         (options, arguments) = getopt.getopt(sys.argv[1:], "r:s:tx")
6581         for (switch, val) in options:
6582             if switch == '-r':
6583                 try:
6584                     replayfp = open(val, "r")
6585                 except IOError:
6586                     sys.stderr.write("sst: can't open replay file %s\n" % val)
6587                     raise SystemExit, 1
6588                 try:
6589                     line = replayfp.readline().strip()
6590                     (leader, key, seed) = line.split()
6591                     seed = eval(seed)
6592                     sys.stderr.write("sst2k: seed set to %s\n" % seed)
6593                     line = replayfp.readline().strip()
6594                     arguments += line.split()[2:]
6595                 except ValueError:
6596                     sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
6597                     raise SystemExit(1)
6598                 game.options |= OPTION_TTY
6599                 game.options &=~ OPTION_CURSES
6600             elif switch == '-s':
6601                 seed = int(val)
6602             elif switch == '-t':
6603                 game.options |= OPTION_TTY
6604                 game.options &=~ OPTION_CURSES
6605             elif switch == '-x':
6606                 idebug = True
6607             else:
6608                 sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n")
6609                 raise SystemExit, 1
6610         # where to save the input in case of bugs
6611         try:
6612             logfp = open("/usr/tmp/sst-input.log", "w")
6613         except IOError:
6614             sys.stderr.write("sst: warning, can't open logfile\n")
6615         if logfp:
6616             logfp.write("# seed %s\n" % seed)
6617             logfp.write("# options %s\n" % " ".join(arguments))
6618         random.seed(seed)
6619         scanner = sstscanner()
6620         map(scanner.push, arguments)
6621         try:
6622             iostart()
6623             while True: # Play a game 
6624                 setwnd(fullscreen_window)
6625                 clrscr()
6626                 prelim()
6627                 setup()
6628                 if game.alldone:
6629                     score()
6630                     game.alldone = False
6631                 else:
6632                     makemoves()
6633                 skip(1)
6634                 stars()
6635                 skip(1)
6636                 if game.tourn and game.alldone:
6637                     proutn(_("Do you want your score recorded?"))
6638                     if ja() == True:
6639                         scanner.chew2()
6640                         freeze(False)
6641                 scanner.chew()
6642                 proutn(_("Do you want to play again? "))
6643                 if not ja():
6644                     break
6645             skip(1)
6646             prout(_("May the Great Bird of the Galaxy roost upon your home planet."))
6647         finally:
6648             ioend()
6649         raise SystemExit, 0
6650     except KeyboardInterrupt:
6651         print""
6652         pass