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