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