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