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