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