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