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