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