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