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