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