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