Local navigation now works in Python.
[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,copy
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(novapush=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         time.sleep(0.03)
3599         proutn(c)
3600         if game.options & OPTION_CURSES:
3601             wrefresh(curwnd)
3602         else:
3603             sys.stdout.flush()
3604     time.sleep(0.03)
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         #time.sleep(1.0)
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                 time.sleep(0.4)
3761             if (iquad==IHDOT) or (iquad==IHBLANK):
3762                 put_srscan_sym(w, '+')
3763                 #sound(l*10)
3764                 #time.sleep(0.1)
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                 #time.sleep(1.0)
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 average 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 = int(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 = int(x + 0.5)
3858             w.y = int(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 = int(x+10.0*game.dist*bigger*deltax+0.5)
3880                 w.y = int(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 getcourse(isprobe, akey):
4022     # get course and distance
4023     key = 0
4024     dquad = copy.copy(game.quadrant)
4025     navmode = "unspecified"
4026     itemp = "curt"
4027     dsect = coord()
4028     iprompt = False
4029     if game.landed and not isprobe:
4030         prout(_("Dummy! You can't leave standard orbit until you"))
4031         proutn(_("are back aboard the ship."))
4032         chew()
4033         return False
4034     while navmode == "unspecified":
4035         if damaged(DNAVSYS):
4036             if isprobe:
4037                 prout(_("Computer damaged; manual navigation only"))
4038             else:
4039                 prout(_("Computer damaged; manual movement only"))
4040             chew()
4041             navmode = "manual"
4042             key = IHEOL
4043             break
4044         if isprobe and akey != -1:
4045             # For probe launch, use pre-scanned value first time 
4046             key = akey
4047             akey = -1
4048         else: 
4049             key = scan()
4050         if key == IHEOL:
4051             proutn(_("Manual or automatic- "))
4052             iprompt = True
4053             chew()
4054         elif key == IHALPHA:
4055             if isit("manual"):
4056                 navmode = "manual"
4057                 key = scan()
4058                 break
4059             elif isit("automatic"):
4060                 navmode = "automatic"
4061                 key = scan()
4062                 break
4063             else:
4064                 huh()
4065                 chew()
4066                 return False
4067         else: # numeric 
4068             if isprobe:
4069                 prout(_("(Manual navigation assumed.)"))
4070             else:
4071                 prout(_("(Manual movement assumed.)"))
4072             navmode = "manual"
4073             break
4074     if navmode == "automatic":
4075         while key == IHEOL:
4076             if isprobe:
4077                 proutn(_("Target quadrant or quadrant&sector- "))
4078             else:
4079                 proutn(_("Destination sector or quadrant&sector- "))
4080             chew()
4081             iprompt = True
4082             key = scan()
4083         if key != IHREAL:
4084             huh()
4085             return False
4086         xi = int(round(aaitem))-1
4087         key = scan()
4088         if key != IHREAL:
4089             huh()
4090             return False
4091         xj = int(round(aaitem))-1
4092         key = scan()
4093         if key == IHREAL:
4094             # both quadrant and sector specified 
4095             xk = int(round(aaitem))-1
4096             key = scan()
4097             if key != IHREAL:
4098                 huh()
4099                 return False
4100             xl = int(round(aaitem))-1
4101             dquad.x = xi
4102             dquad.y = xj
4103             dsect.y = xk
4104             dsect.x = xl
4105         else:
4106             # only one pair of numbers was specified
4107             if isprobe:
4108                 # only quadrant specified -- go to center of dest quad 
4109                 dquad.x = xi
4110                 dquad.y = xj
4111                 dsect.y = dsect.x = 4   # preserves 1-origin behavior
4112             else:
4113                 # only sector specified
4114                 dsect.y = xi
4115                 dsect.x = xj
4116             itemp = "normal"
4117         if not VALID_QUADRANT(dquad.y,dquad.x) or not VALID_SECTOR(dsect.x,dsect.y):
4118             huh()
4119             return False
4120         skip(1)
4121         if not isprobe:
4122             if itemp > "curt":
4123                 if iprompt:
4124                     prout(_("Helmsman Sulu- \"Course locked in for Sector %s.\"") % dsect)
4125             else:
4126                 prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
4127         # the actual deltas get computed here
4128         deltax = dquad.y-game.quadrant.y + 0.1*(dsect.x-game.sector.y)
4129         deltay = game.quadrant.x-dquad.x + 0.1*(game.sector.x-dsect.y)
4130     else: # manual 
4131         while key == IHEOL:
4132             proutn(_("X and Y displacements- "))
4133             chew()
4134             iprompt = True
4135             key = scan()
4136         itemp = "verbose"
4137         if key != IHREAL:
4138             huh()
4139             return False
4140         deltax = aaitem
4141         key = scan()
4142         if key != IHREAL:
4143             huh()
4144             return False
4145         deltay = aaitem
4146     # Check for zero movement 
4147     if deltax == 0 and deltay == 0:
4148         chew()
4149         return False
4150     if itemp == "verbose" and not isprobe:
4151         skip(1)
4152         prout(_("Helmsman Sulu- \"Aye, Sir.\""))
4153     # Course actually laid in.
4154     game.dist = math.sqrt(deltax*deltax + deltay*deltay)
4155     game.direc = math.atan2(deltax, deltay)*1.90985932
4156     if game.direc < 0.0:
4157         game.direc += 12.0
4158     chew()
4159     return True
4160
4161 def impulse():
4162     # move under impulse power 
4163     game.ididit = False
4164     if damaged(DIMPULS):
4165         chew()
4166         skip(1)
4167         prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
4168         return
4169     if game.energy > 30.0:
4170         if not getcourse(isprobe=False, akey=0):
4171             return
4172         power = 20.0 + 100.0*game.dist
4173     else:
4174         power = 30.0
4175     if power >= game.energy:
4176         # Insufficient power for trip 
4177         skip(1)
4178         prout(_("First Officer Spock- \"Captain, the impulse engines"))
4179         prout(_("require 20.0 units to engage, plus 100.0 units per"))
4180         if game.energy > 30:
4181             proutn(_("quadrant.  We can go, therefore, a maximum of %d") %
4182                      int(0.01 * (game.energy-20.0)-0.05))
4183             prout(_(" quadrants.\""))
4184         else:
4185             prout(_("quadrant.  They are, therefore, useless.\""))
4186         chew()
4187         return
4188     # Make sure enough time is left for the trip 
4189     game.optime = game.dist/0.095
4190     if game.optime >= game.state.remtime:
4191         prout(_("First Officer Spock- \"Captain, our speed under impulse"))
4192         prout(_("power is only 0.95 sectors per stardate. Are you sure"))
4193         proutn(_("we dare spend the time?\" "))
4194         if ja() == False:
4195             return
4196     # Activate impulse engines and pay the cost 
4197     imove(novapush=False)
4198     game.ididit = True
4199     if game.alldone:
4200         return
4201     power = 20.0 + 100.0*game.dist
4202     game.energy -= power
4203     game.optime = game.dist/0.095
4204     if game.energy <= 0:
4205         finish(FNRG)
4206     return
4207
4208 def warp(timewarp):
4209     # move under warp drive 
4210     blooey = False; twarp = False
4211     if not timewarp: # Not WARPX entry 
4212         game.ididit = False
4213         if game.damage[DWARPEN] > 10.0:
4214             chew()
4215             skip(1)
4216             prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
4217             return
4218         if damaged(DWARPEN) and game.warpfac > 4.0:
4219             chew()
4220             skip(1)
4221             prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
4222             prout(_("  is repaired, I can only give you warp 4.\""))
4223             return
4224         # Read in course and distance 
4225         if not getcourse(isprobe=False, akey=0):
4226             return
4227         # Make sure starship has enough energy for the trip 
4228         power = (game.dist+0.05)*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
4229         if power >= game.energy:
4230             # Insufficient power for trip 
4231             game.ididit = False
4232             skip(1)
4233             prout(_("Engineering to bridge--"))
4234             if not game.shldup or 0.5*power > game.energy:
4235                 iwarp = math.pow((game.energy/(game.dist+0.05)), 0.333333333)
4236                 if iwarp <= 0:
4237                     prout(_("We can't do it, Captain. We don't have enough energy."))
4238                 else:
4239                     proutn(_("We don't have enough energy, but we could do it at warp %d") % iwarp)
4240                     if game.shldup:
4241                         prout(",")
4242                         prout(_("if you'll lower the shields."))
4243                     else:
4244                         prout(".")
4245             else:
4246                 prout(_("We haven't the energy to go that far with the shields up."))
4247             return
4248                                                 
4249         # Make sure enough time is left for the trip 
4250         game.optime = 10.0*game.dist/game.wfacsq
4251         if game.optime >= 0.8*game.state.remtime:
4252             skip(1)
4253             prout(_("First Officer Spock- \"Captain, I compute that such"))
4254             proutn(_("  a trip would require approximately %2.0f") %
4255                    (100.0*game.optime/game.state.remtime))
4256             prout(_(" percent of our"))
4257             proutn(_("  remaining time.  Are you sure this is wise?\" "))
4258             if ja() == False:
4259                 game.ididit = False
4260                 game.optime=0 
4261                 return
4262     # Entry WARPX 
4263     if game.warpfac > 6.0:
4264         # Decide if engine damage will occur 
4265         prob = game.dist*(6.0-game.warpfac)*(6.0-game.warpfac)/66.666666666
4266         if prob > random.random():
4267             blooey = True
4268             game.dist = random.random()*game.dist
4269         # Decide if time warp will occur 
4270         if 0.5*game.dist*math.pow(7.0,game.warpfac-10.0) > random.random():
4271             twarp = True
4272         if idebug and game.warpfac==10 and not twarp:
4273             blooey = False
4274             proutn("=== Force time warp? ")
4275             if ja() == True:
4276                 twarp = True
4277         if blooey or twarp:
4278             # If time warp or engine damage, check path 
4279             # If it is obstructed, don't do warp or damage 
4280             angle = ((15.0-game.direc)*0.5235998)
4281             deltax = -math.sin(angle)
4282             deltay = math.cos(angle)
4283             if math.fabs(deltax) > math.fabs(deltay):
4284                 bigger = math.fabs(deltax)
4285             else:
4286                 bigger = math.fabs(deltay)
4287                         
4288             deltax /= bigger
4289             deltay /= bigger
4290             n = 10.0 * game.dist * bigger +0.5
4291             x = game.sector.x
4292             y = game.sector.y
4293             for l in range(1, n+1):
4294                 x += deltax
4295                 ix = x + 0.5
4296                 y += deltay
4297                 iy = y +0.5
4298                 if not VALID_SECTOR(ix, iy):
4299                     break
4300                 if game.quad[ix][iy] != IHDOT:
4301                     blooey = False
4302                     twarp = False
4303     # Activate Warp Engines and pay the cost 
4304     imove(novapush=False)
4305     if game.alldone:
4306         return
4307     game.energy -= game.dist*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
4308     if game.energy <= 0:
4309         finish(FNRG)
4310     game.optime = 10.0*game.dist/game.wfacsq
4311     if twarp:
4312         timwrp()
4313     if blooey:
4314         game.damage[DWARPEN] = game.damfac*(3.0*random.random()+1.0)
4315         skip(1)
4316         prout(_("Engineering to bridge--"))
4317         prout(_("  Scott here.  The warp engines are damaged."))
4318         prout(_("  We'll have to reduce speed to warp 4."))
4319     game.ididit = True
4320     return
4321
4322 def setwarp():
4323     # change the warp factor    
4324     while True:
4325         key=scan()
4326         if key != IHEOL:
4327             break
4328         chew()
4329         proutn(_("Warp factor- "))
4330     chew()
4331     if key != IHREAL:
4332         huh()
4333         return
4334     if game.damage[DWARPEN] > 10.0:
4335         prout(_("Warp engines inoperative."))
4336         return
4337     if damaged(DWARPEN) and aaitem > 4.0:
4338         prout(_("Engineer Scott- \"I'm doing my best, Captain,"))
4339         prout(_("  but right now we can only go warp 4.\""))
4340         return
4341     if aaitem > 10.0:
4342         prout(_("Helmsman Sulu- \"Our top speed is warp 10, Captain.\""))
4343         return
4344     if aaitem < 1.0:
4345         prout(_("Helmsman Sulu- \"We can't go below warp 1, Captain.\""))
4346         return
4347     oldfac = game.warpfac
4348     game.warpfac = aaitem
4349     game.wfacsq=game.warpfac*game.warpfac
4350     if game.warpfac <= oldfac or game.warpfac <= 6.0:
4351         prout(_("Helmsman Sulu- \"Warp factor %d, Captain.\"") %
4352                int(game.warpfac))
4353         return
4354     if game.warpfac < 8.00:
4355         prout(_("Engineer Scott- \"Aye, but our maximum safe speed is warp 6.\""))
4356         return
4357     if game.warpfac == 10.0:
4358         prout(_("Engineer Scott- \"Aye, Captain, we'll try it.\""))
4359         return
4360     prout(_("Engineer Scott- \"Aye, Captain, but our engines may not take it.\""))
4361     return
4362
4363 def atover(igrab):
4364     # cope with being tossed out of quadrant by supernova or yanked by beam 
4365     chew()
4366     # is captain on planet? 
4367     if game.landed:
4368         if damaged(DTRANSP):
4369             finish(FPNOVA)
4370             return
4371         prout(_("Scotty rushes to the transporter controls."))
4372         if game.shldup:
4373             prout(_("But with the shields up it's hopeless."))
4374             finish(FPNOVA)
4375         prouts(_("His desperate attempt to rescue you . . ."))
4376         if random.random() <= 0.5:
4377             prout(_("fails."))
4378             finish(FPNOVA)
4379             return
4380         prout(_("SUCCEEDS!"))
4381         if game.imine:
4382             game.imine = False
4383             proutn(_("The crystals mined were "))
4384             if random.random() <= 0.25:
4385                 prout(_("lost."))
4386             else:
4387                 prout(_("saved."))
4388                 game.icrystl = True
4389     if igrab:
4390         return
4391     # Check to see if captain in shuttle craft 
4392     if game.icraft:
4393         finish(FSTRACTOR)
4394     if game.alldone:
4395         return
4396     # Inform captain of attempt to reach safety 
4397     skip(1)
4398     while True:
4399         if game.justin:
4400             prouts(_("***RED ALERT!  RED ALERT!"))
4401             skip(1)
4402             proutn(_("The "))
4403             crmshp()
4404             prout(_(" has stopped in a quadrant containing"))
4405             prouts(_("   a supernova."))
4406             skip(2)
4407         proutn(_("***Emergency automatic override attempts to hurl "))
4408         crmshp()
4409         skip(1)
4410         prout(_("safely out of quadrant."))
4411         if not damaged(DRADIO):
4412             game.state.galaxy[game.quadrant.x][game.quadrant.y].charted = True
4413         # Try to use warp engines 
4414         if damaged(DWARPEN):
4415             skip(1)
4416             prout(_("Warp engines damaged."))
4417             finish(FSNOVAED)
4418             return
4419         game.warpfac = 6.0+2.0*random.random()
4420         game.wfacsq = game.warpfac * game.warpfac
4421         prout(_("Warp factor set to %d") % int(game.warpfac))
4422         power = 0.75*game.energy
4423         game.dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1))
4424         distreq = 1.4142+random.random()
4425         if distreq < game.dist:
4426             game.dist = distreq
4427         game.optime = 10.0*game.dist/game.wfacsq
4428         game.direc = 12.0*random.random()       # How dumb! 
4429         game.justin = False
4430         game.inorbit = False
4431         warp(True)
4432         if not game.justin:
4433             # This is bad news, we didn't leave quadrant. 
4434             if game.alldone:
4435                 return
4436             skip(1)
4437             prout(_("Insufficient energy to leave quadrant."))
4438             finish(FSNOVAED)
4439             return
4440         # Repeat if another snova
4441         if not game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
4442             break
4443     if (game.state.remkl + game.state.remcom + game.state.nscrem)==0: 
4444         finish(FWON) # Snova killed remaining enemy. 
4445
4446 def timwrp():
4447     # let's do the time warp again 
4448     prout(_("***TIME WARP ENTERED."))
4449     if game.state.snap and random.random() < 0.5:
4450         # Go back in time 
4451         prout(_("You are traveling backwards in time %d stardates.") %
4452               int(game.state.date-game.snapsht.date))
4453         game.state = game.snapsht
4454         game.state.snap = False
4455         if game.state.remcom:
4456             schedule(FTBEAM, expran(game.intime/game.state.remcom))
4457             schedule(FBATTAK, expran(0.3*game.intime))
4458         schedule(FSNOVA, expran(0.5*game.intime))
4459         # next snapshot will be sooner 
4460         schedule(FSNAP, expran(0.25*game.state.remtime))
4461                                 
4462         if game.state.nscrem:
4463             schedule(FSCMOVE, 0.2777)       
4464         game.isatb = 0
4465         unschedule(FCDBAS)
4466         unschedule(FSCDBAS)
4467         invalidate(game.battle)
4468
4469         # Make sure Galileo is consistant -- Snapshot may have been taken
4470         # when on planet, which would give us two Galileos! 
4471         gotit = False
4472         for l in range(game.inplan):
4473             if game.state.planets[l].known == "shuttle_down":
4474                 gotit = True
4475                 if game.iscraft == "onship" and game.ship==IHE:
4476                     prout(_("Chekov-  \"Security reports the Galileo has disappeared, Sir!"))
4477                     game.iscraft = "offship"
4478         # Likewise, if in the original time the Galileo was abandoned, but
4479         # was on ship earlier, it would have vanished -- let's restore it.
4480         if game.iscraft == "offship" and not gotit and game.damage[DSHUTTL] >= 0.0:
4481             prout(_("Checkov-  \"Security reports the Galileo has reappeared in the dock!\""))
4482             game.iscraft = "onship"
4483         # 
4484 #        * There used to be code to do the actual reconstrction here,
4485 #        * but the starchart is now part of the snapshotted galaxy state.
4486 #        
4487         prout(_("Spock has reconstructed a correct star chart from memory"))
4488     else:
4489         # Go forward in time 
4490         game.optime = -0.5*game.intime*math.log(random.random())
4491         prout(_("You are traveling forward in time %d stardates.") % int(game.optime))
4492         # cheat to make sure no tractor beams occur during time warp 
4493         postpone(FTBEAM, game.optime)
4494         game.damage[DRADIO] += game.optime
4495     newqad(False)
4496     events()    # Stas Sergeev added this -- do pending events 
4497
4498 def probe():
4499     # launch deep-space probe 
4500     # New code to launch a deep space probe 
4501     if game.nprobes == 0:
4502         chew()
4503         skip(1)
4504         if game.ship == IHE: 
4505             prout(_("Engineer Scott- \"We have no more deep space probes, Sir.\""))
4506         else:
4507             prout(_("Ye Faerie Queene has no deep space probes."))
4508         return
4509     if damaged(DDSP):
4510         chew()
4511         skip(1)
4512         prout(_("Engineer Scott- \"The probe launcher is damaged, Sir.\""))
4513         return
4514     if is_scheduled(FDSPROB):
4515         chew()
4516         skip(1)
4517         if damaged(DRADIO) and game.condition != "docked":
4518             prout(_("Spock-  \"Records show the previous probe has not yet"))
4519             prout(_("   reached its destination.\""))
4520         else:
4521             prout(_("Uhura- \"The previous probe is still reporting data, Sir.\""))
4522         return
4523     key = scan()
4524     if key == IHEOL:
4525         # slow mode, so let Kirk know how many probes there are left
4526         if game.nprobes == 1:
4527             prout(_("1 probe left."))
4528         else:
4529             prout(_("%d probes left") % game.nprobes)
4530         proutn(_("Are you sure you want to fire a probe? "))
4531         if ja() == False:
4532             return
4533     game.isarmed = False
4534     if key == IHALPHA and citem == "armed":
4535         game.isarmed = True
4536         key = scan()
4537     elif key == IHEOL:
4538         proutn(_("Arm NOVAMAX warhead? "))
4539         game.isarmed = ja()
4540     if not getcourse(isprobe=True, akey=key):
4541         return
4542     game.nprobes -= 1
4543     angle = ((15.0 - game.direc) * 0.5235988)
4544     game.probeinx = -math.sin(angle)
4545     game.probeiny = math.cos(angle)
4546     if math.fabs(game.probeinx) > math.fabs(game.probeiny):
4547         bigger = math.fabs(game.probeinx)
4548     else:
4549         bigger = math.fabs(game.probeiny)
4550     game.probeiny /= bigger
4551     game.probeinx /= bigger
4552     game.proben = 10.0*game.dist*bigger +0.5
4553     game.probex = game.quadrant.x*QUADSIZE + game.sector.x - 1  # We will use better packing than original
4554     game.probey = game.quadrant.y*QUADSIZE + game.sector.y - 1
4555     game.probec = game.quadrant
4556     schedule(FDSPROB, 0.01) # Time to move one sector
4557     prout(_("Ensign Chekov-  \"The deep space probe is launched, Captain.\""))
4558     game.ididit = True
4559     return
4560
4561 # Here's how the mayday code works:
4562
4563 # First, the closest starbase is selected.  If there is a a starbase
4564 # in your own quadrant, you are in good shape.  This distance takes
4565 # quadrant distances into account only.
4566 #
4567 # A magic number is computed based on the distance which acts as the
4568 # probability that you will be rematerialized.  You get three tries.
4569 #
4570 # When it is determined that you should be able to be rematerialized
4571 # (i.e., when the probability thing mentioned above comes up
4572 # positive), you are put into that quadrant (anywhere).  Then, we try
4573 # to see if there is a spot adjacent to the star- base.  If not, you
4574 # can't be rematerialized!!!  Otherwise, it drops you there.  It only
4575 # tries five times to find a spot to drop you.  After that, it's your
4576 # problem.
4577
4578 def mayday():
4579     # yell for help from nearest starbase 
4580     # There's more than one way to move in this game! 
4581     line = 0
4582     chew()
4583     # Test for conditions which prevent calling for help 
4584     if game.condition == "docked":
4585         prout(_("Lt. Uhura-  \"But Captain, we're already docked.\""))
4586         return
4587     if damaged(DRADIO):
4588         prout(_("Subspace radio damaged."))
4589         return
4590     if game.state.rembase==0:
4591         prout(_("Lt. Uhura-  \"Captain, I'm not getting any response from Starbase.\""))
4592         return
4593     if game.landed:
4594         proutn(_("You must be aboard the "))
4595         crmshp()
4596         prout(".")
4597         return
4598     # OK -- call for help from nearest starbase 
4599     game.nhelp += 1
4600     if game.base.x!=0:
4601         # There's one in this quadrant 
4602         ddist = distance(game.base, game.sector)
4603     else:
4604         ddist = FOREVER
4605         for m in range(game.state.rembase):
4606             xdist = QUADSIZE * distance(game.state.baseq[m], game.quadrant)
4607             if xdist < ddist:
4608                 ddist = xdist
4609                 line = m
4610         # Since starbase not in quadrant, set up new quadrant 
4611         game.quadrant = game.state.baseq[line]
4612         newqad(True)
4613     # dematerialize starship 
4614     game.quad[game.sector.x][game.sector.y]=IHDOT
4615     proutn(_("Starbase in Quadrant %s responds--") % game.quadrant)
4616     crmshp()
4617     prout(_(" dematerializes."))
4618     game.sector.x=0
4619     for m in range(1, 5+1):
4620         ix = game.base.x+3.0*random.random()-1
4621         iy = game.base.y+3.0*random.random()-1
4622         if VALID_SECTOR(ix,iy) and game.quad[ix][iy]==IHDOT:
4623             # found one -- finish up 
4624             game.sector.x=ix
4625             game.sector.y=iy
4626             break
4627     if not is_valid(game.sector):
4628         prout(_("You have been lost in space..."))
4629         finish(FMATERIALIZE)
4630         return
4631     # Give starbase three chances to rematerialize starship 
4632     probf = math.pow((1.0 - math.pow(0.98,ddist)), 0.33333333)
4633     for m in range(1, 3+1):
4634         if m == 1: proutn(_("1st"))
4635         elif m == 2: proutn(_("2nd"))
4636         elif m == 3: proutn(_("3rd"))
4637         proutn(_(" attempt to re-materialize "))
4638         crmshp()
4639         game.quad[ix][iy]=(IHMATER0,IHMATER1,IHMATER2)[m-1]
4640         textcolor(RED)
4641         warble()
4642         if random.random() > probf:
4643             break
4644         prout(_("fails."))
4645         curses.delay_output(500)
4646         textcolor(DEFAULT)
4647     if m > 3:
4648         game.quad[ix][iy]=IHQUEST
4649         game.alive = False
4650         drawmaps(1)
4651         setwnd(message_window)
4652         finish(FMATERIALIZE)
4653         return
4654     game.quad[ix][iy]=game.ship
4655     textcolor(GREEN)
4656     prout(_("succeeds."))
4657     textcolor(DEFAULT)
4658     dock(False)
4659     skip(1)
4660     prout(_("Lt. Uhura-  \"Captain, we made it!\""))
4661
4662 # Abandon Ship (the BSD-Trek description)
4663
4664 # The ship is abandoned.  If your current ship is the Faire
4665 # Queene, or if your shuttlecraft is dead, you're out of
4666 # luck.  You need the shuttlecraft in order for the captain
4667 # (that's you!!) to escape.
4668
4669 # Your crew can beam to an inhabited starsystem in the
4670 # quadrant, if there is one and if the transporter is working.
4671 # If there is no inhabited starsystem, or if the transporter
4672 # is out, they are left to die in outer space.
4673
4674 # If there are no starbases left, you are captured by the
4675 # Klingons, who torture you mercilessly.  However, if there
4676 # is at least one starbase, you are returned to the
4677 # Federation in a prisoner of war exchange.  Of course, this
4678 # can't happen unless you have taken some prisoners.
4679
4680 def abandon():
4681     # abandon ship 
4682     chew()
4683     if game.condition=="docked":
4684         if game.ship!=IHE:
4685             prout(_("You cannot abandon Ye Faerie Queene."))
4686             return
4687     else:
4688         # Must take shuttle craft to exit 
4689         if game.damage[DSHUTTL]==-1:
4690             prout(_("Ye Faerie Queene has no shuttle craft."))
4691             return
4692         if game.damage[DSHUTTL]<0:
4693             prout(_("Shuttle craft now serving Big Macs."))
4694             return
4695         if game.damage[DSHUTTL]>0:
4696             prout(_("Shuttle craft damaged."))
4697             return
4698         if game.landed:
4699             prout(_("You must be aboard the ship."))
4700             return
4701         if game.iscraft != "onship":
4702             prout(_("Shuttle craft not currently available."))
4703             return
4704         # Print abandon ship messages 
4705         skip(1)
4706         prouts(_("***ABANDON SHIP!  ABANDON SHIP!"))
4707         skip(1)
4708         prouts(_("***ALL HANDS ABANDON SHIP!"))
4709         skip(2)
4710         prout(_("Captain and crew escape in shuttle craft."))
4711         if game.state.rembase==0:
4712             # Oops! no place to go... 
4713             finish(FABANDN)
4714             return
4715         q = game.state.galaxy[game.quadrant.x][game.quadrant.y]
4716         # Dispose of crew 
4717         if not (game.options & OPTION_WORLDS) and not damaged(DTRANSP):
4718             prout(_("Remainder of ship's complement beam down"))
4719             prout(_("to nearest habitable planet."))
4720         elif q.planet != None and not damaged(DTRANSP):
4721             prout(_("Remainder of ship's complement beam down to %s.") %
4722                     q.planet)
4723         else:
4724             prout(_("Entire crew of %d left to die in outer space.") %
4725                     game.state.crew)
4726             game.casual += game.state.crew
4727             game.abandoned += game.state.crew
4728
4729         # If at least one base left, give 'em the Faerie Queene 
4730         skip(1)
4731         game.icrystl = False # crystals are lost 
4732         game.nprobes = 0 # No probes 
4733         prout(_("You are captured by Klingons and released to"))
4734         prout(_("the Federation in a prisoner-of-war exchange."))
4735         nb = random.random()*game.state.rembase+1
4736         # Set up quadrant and position FQ adjacient to base 
4737         if not game.quadrant == game.state.baseq[nb]:
4738             game.quadrant = game.state.baseq[nb]
4739             game.sector.x = game.sector.y = 5
4740             newqad(True)
4741         while True:
4742             # position next to base by trial and error 
4743             game.quad[game.sector.x][game.sector.y] = IHDOT
4744             for l in range(QUADSIZE):
4745                 game.sector.x = 3.0*random.random() - 1.0 + game.base.x
4746                 game.sector.y = 3.0*random.random() - 1.0 + game.base.y
4747                 if VALID_SECTOR(game.sector.x, game.sector.y) and \
4748                        game.quad[game.sector.x][game.sector.y] == IHDOT:
4749                     break
4750             if l < QUADSIZE+1:
4751                 break # found a spot 
4752             game.sector.x=QUADSIZE/2
4753             game.sector.y=QUADSIZE/2
4754             newqad(True)
4755     # Get new commission 
4756     game.quad[game.sector.x][game.sector.y] = game.ship = IHF
4757     game.state.crew = FULLCREW
4758     prout(_("Starfleet puts you in command of another ship,"))
4759     prout(_("the Faerie Queene, which is antiquated but,"))
4760     prout(_("still useable."))
4761     if game.icrystl:
4762         prout(_("The dilithium crystals have been moved."))
4763     game.imine = False
4764     game.iscraft = "offship" # Galileo disappears 
4765     # Resupply ship 
4766     game.condition="docked"
4767     for l in range(NDEVICES): 
4768         game.damage[l] = 0.0
4769     game.damage[DSHUTTL] = -1
4770     game.energy = game.inenrg = 3000.0
4771     game.shield = game.inshld = 1250.0
4772     game.torps = game.intorps = 6
4773     game.lsupres=game.inlsr=3.0
4774     game.shldup=False
4775     game.warpfac=5.0
4776     game.wfacsq=25.0
4777     return
4778
4779 # Code from planets.c begins here.
4780
4781 def consumeTime():
4782     # abort a lengthy operation if an event interrupts it 
4783     game.ididit = True
4784     events()
4785     if game.alldone or game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova or game.justin: 
4786         return True
4787     return False
4788
4789 def survey():
4790     # report on (uninhabited) planets in the galaxy 
4791     iknow = False
4792     skip(1)
4793     chew()
4794     prout(_("Spock-  \"Planet report follows, Captain.\""))
4795     skip(1)
4796     for i in range(game.inplan):
4797         if game.state.planets[i].pclass == "destroyed":
4798             continue
4799         if (game.state.planets[i].known != "unknown" \
4800             and not game.state.planets[i].inhabited) \
4801             or idebug:
4802             iknow = True
4803             if idebug and game.state.planets[i].known=="unknown":
4804                 proutn("(Unknown) ")
4805             proutn(_("Quadrant %s") % game.state.planets[i].w)
4806             proutn(_("   class "))
4807             proutn(game.state.planets[i].pclass)
4808             proutn("   ")
4809             if game.state.planets[i].crystals != present:
4810                 proutn(_("no "))
4811             prout(_("dilithium crystals present."))
4812             if game.state.planets[i].known=="shuttle_down": 
4813                 prout(_("    Shuttle Craft Galileo on surface."))
4814     if not iknow:
4815         prout(_("No information available."))
4816
4817 def orbit():
4818     # enter standard orbit 
4819     skip(1)
4820     chew()
4821     if game.inorbit:
4822         prout(_("Already in standard orbit."))
4823         return
4824     if damaged(DWARPEN) and damaged(DIMPULS):
4825         prout(_("Both warp and impulse engines damaged."))
4826         return
4827     if not is_valid(game.plnet) or abs(game.sector.x-game.plnet.x) > 1 or abs(game.sector.y-game.plnet.y) > 1:
4828         crmshp()
4829         prout(_(" not adjacent to planet."))
4830         skip(1)
4831         return
4832     game.optime = 0.02+0.03*random.random()
4833     prout(_("Helmsman Sulu-  \"Entering standard orbit, Sir.\""))
4834     newcnd()
4835     if consumeTime():
4836         return
4837     game.height = (1400.0+7200.0*random.random())
4838     prout(_("Sulu-  \"Entered orbit at altitude %.2f kilometers.\"") % game.height)
4839     game.inorbit = True
4840     game.ididit = True
4841
4842 def sensor():
4843     # examine planets in this quadrant 
4844     if damaged(DSRSENS):
4845         if game.options & OPTION_TTY:
4846             prout(_("Short range sensors damaged."))
4847         return
4848     if game.iplnet == None:
4849         if game.options & OPTION_TTY:
4850             prout(_("Spock- \"No planet in this quadrant, Captain.\""))
4851         return
4852     if game.iplnet.known == "unknown":
4853         prout(_("Spock-  \"Sensor scan for Quadrant %s-") % game.quadrant)
4854         skip(1)
4855         prout(_("         Planet at Sector %s is of class %s.") %
4856               (game.plnet, game.iplnet.pclass))
4857         if game.iplnet.known=="shuttle_down": 
4858             prout(_("         Sensors show Galileo still on surface."))
4859         proutn(_("         Readings indicate"))
4860         if game.iplnet.crystals != "present":
4861             proutn(_(" no"))
4862         prout(_(" dilithium crystals present.\""))
4863         if game.iplnet.known == "unknown":
4864             game.iplnet.known = "known"
4865     elif game.iplnet.inhabited:
4866         prout(_("Spock-  \"The inhabited planet %s ") % game.iplnet.name)
4867         prout(_("        is located at Sector %s, Captain.\"") % game.plnet)
4868
4869 def beam():
4870     # use the transporter 
4871     nrgneed = 0
4872     chew()
4873     skip(1)
4874     if damaged(DTRANSP):
4875         prout(_("Transporter damaged."))
4876         if not damaged(DSHUTTL) and (game.iplnet.known=="shuttle_down" or game.iscraft == "onship"):
4877             skip(1)
4878             proutn(_("Spock-  \"May I suggest the shuttle craft, Sir?\" "))
4879             if ja() == True:
4880                 shuttle()
4881         return
4882     if not game.inorbit:
4883         crmshp()
4884         prout(_(" not in standard orbit."))
4885         return
4886     if game.shldup:
4887         prout(_("Impossible to transport through shields."))
4888         return
4889     if game.iplnet.known=="unknown":
4890         prout(_("Spock-  \"Captain, we have no information on this planet"))
4891         prout(_("  and Starfleet Regulations clearly state that in this situation"))
4892         prout(_("  you may not go down.\""))
4893         return
4894     if not game.landed and game.iplnet.crystals=="absent":
4895         prout(_("Spock-  \"Captain, I fail to see the logic in"))
4896         prout(_("  exploring a planet with no dilithium crystals."))
4897         proutn(_("  Are you sure this is wise?\" "))
4898         if ja() == False:
4899             chew()
4900             return
4901     if not (game.options & OPTION_PLAIN):
4902         nrgneed = 50 * game.skill + game.height / 100.0
4903         if nrgneed > game.energy:
4904             prout(_("Engineering to bridge--"))
4905             prout(_("  Captain, we don't have enough energy for transportation."))
4906             return
4907         if not game.landed and nrgneed * 2 > game.energy:
4908             prout(_("Engineering to bridge--"))
4909             prout(_("  Captain, we have enough energy only to transport you down to"))
4910             prout(_("  the planet, but there wouldn't be an energy for the trip back."))
4911             if game.iplnet.known == "shuttle_down":
4912                 prout(_("  Although the Galileo shuttle craft may still be on a surface."))
4913             proutn(_("  Are you sure this is wise?\" "))
4914             if ja() == False:
4915                 chew()
4916                 return
4917     if game.landed:
4918         # Coming from planet 
4919         if game.iplnet.known=="shuttle_down":
4920             proutn(_("Spock-  \"Wouldn't you rather take the Galileo?\" "))
4921             if ja() == True:
4922                 chew()
4923                 return
4924             prout(_("Your crew hides the Galileo to prevent capture by aliens."))
4925         prout(_("Landing party assembled, ready to beam up."))
4926         skip(1)
4927         prout(_("Kirk whips out communicator..."))
4928         prouts(_("BEEP  BEEP  BEEP"))
4929         skip(2)
4930         prout(_("\"Kirk to enterprise-  Lock on coordinates...energize.\""))
4931     else:
4932         # Going to planet 
4933         prout(_("Scotty-  \"Transporter room ready, Sir.\""))
4934         skip(1)
4935         prout(_("Kirk and landing party prepare to beam down to planet surface."))
4936         skip(1)
4937         prout(_("Kirk-  \"Energize.\""))
4938     game.ididit = True
4939     skip(1)
4940     prouts("WWHOOOIIIIIRRRRREEEE.E.E.  .  .  .  .   .    .")
4941     skip(2)
4942     if random.random() > 0.98:
4943         prouts("BOOOIIIOOOIIOOOOIIIOIING . . .")
4944         skip(2)
4945         prout(_("Scotty-  \"Oh my God!  I've lost them.\""))
4946         finish(FLOST)
4947         return
4948     prouts(".    .   .  .  .  .  .E.E.EEEERRRRRIIIIIOOOHWW")
4949     game.landed = not game.landed
4950     game.energy -= nrgneed
4951     skip(2)
4952     prout(_("Transport complete."))
4953     if game.landed and game.iplnet.known=="shuttle_down":
4954         prout(_("The shuttle craft Galileo is here!"))
4955     if not game.landed and game.imine:
4956         game.icrystl = True
4957         game.cryprob = 0.05
4958     game.imine = False
4959     return
4960
4961 def mine():
4962     # strip-mine a world for dilithium 
4963     skip(1)
4964     chew()
4965     if not game.landed:
4966         prout(_("Mining party not on planet."))
4967         return
4968     if game.iplnet.crystals == "mined":
4969         prout(_("This planet has already been strip-mined for dilithium."))
4970         return
4971     elif game.iplnet.crystals == "absent":
4972         prout(_("No dilithium crystals on this planet."))
4973         return
4974     if game.imine:
4975         prout(_("You've already mined enough crystals for this trip."))
4976         return
4977     if game.icrystl and game.cryprob == 0.05:
4978         proutn(_("With all those fresh crystals aboard the "))
4979         crmshp()
4980         skip(1)
4981         prout(_("there's no reason to mine more at this time."))
4982         return
4983     game.optime = (0.1+0.2*random.random())*game.iplnet.pclass
4984     if consumeTime():
4985         return
4986     prout(_("Mining operation complete."))
4987     game.iplnet.crystals = "mined"
4988     game.imine = game.ididit = True
4989
4990 def usecrystals():
4991     # use dilithium crystals 
4992     game.ididit = False
4993     skip(1)
4994     chew()
4995     if not game.icrystl:
4996         prout(_("No dilithium crystals available."))
4997         return
4998     if game.energy >= 1000:
4999         prout(_("Spock-  \"Captain, Starfleet Regulations prohibit such an operation"))
5000         prout(_("  except when Condition Yellow exists."))
5001         return
5002     prout(_("Spock- \"Captain, I must warn you that loading"))
5003     prout(_("  raw dilithium crystals into the ship's power"))
5004     prout(_("  system may risk a severe explosion."))
5005     proutn(_("  Are you sure this is wise?\" "))
5006     if ja() == False:
5007         chew()
5008         return
5009     skip(1)
5010     prout(_("Engineering Officer Scott-  \"(GULP) Aye Sir."))
5011     prout(_("  Mr. Spock and I will try it.\""))
5012     skip(1)
5013     prout(_("Spock-  \"Crystals in place, Sir."))
5014     prout(_("  Ready to activate circuit.\""))
5015     skip(1)
5016     prouts(_("Scotty-  \"Keep your fingers crossed, Sir!\""))
5017     skip(1)
5018     if random.random() <= game.cryprob:
5019         prouts(_("  \"Activating now! - - No good!  It's***"))
5020         skip(2)
5021         prouts(_("***RED ALERT!  RED A*L********************************"))
5022         skip(1)
5023         stars()
5024         prouts(_("******************   KA-BOOM!!!!   *******************"))
5025         skip(1)
5026         kaboom()
5027         return
5028     game.energy += 5000.0*(1.0 + 0.9*random.random())
5029     prouts(_("  \"Activating now! - - "))
5030     prout(_("The instruments"))
5031     prout(_("   are going crazy, but I think it's"))
5032     prout(_("   going to work!!  Congratulations, Sir!\""))
5033     game.cryprob *= 2.0
5034     game.ididit = True
5035
5036 def shuttle():
5037     # use shuttlecraft for planetary jaunt 
5038     chew()
5039     skip(1)
5040     if damaged(DSHUTTL):
5041         if game.damage[DSHUTTL] == -1.0:
5042             if game.inorbit and game.iplnet.known == "shuttle_down":
5043                 prout(_("Ye Faerie Queene has no shuttle craft bay to dock it at."))
5044             else:
5045                 prout(_("Ye Faerie Queene had no shuttle craft."))
5046         elif game.damage[DSHUTTL] > 0:
5047             prout(_("The Galileo is damaged."))
5048         else: # game.damage[DSHUTTL] < 0  
5049             prout(_("Shuttle craft is now serving Big Macs."))
5050         return
5051     if not game.inorbit:
5052         crmshp()
5053         prout(_(" not in standard orbit."))
5054         return
5055     if (game.iplnet.known != "shuttle_down") and game.iscraft != "onship":
5056         prout(_("Shuttle craft not currently available."))
5057         return
5058     if not game.landed and game.iplnet.known=="shuttle_down":
5059         prout(_("You will have to beam down to retrieve the shuttle craft."))
5060         return
5061     if game.shldup or game.condition == "docked":
5062         prout(_("Shuttle craft cannot pass through shields."))
5063         return
5064     if game.iplnet.known=="unknown":
5065         prout(_("Spock-  \"Captain, we have no information on this planet"))
5066         prout(_("  and Starfleet Regulations clearly state that in this situation"))
5067         prout(_("  you may not fly down.\""))
5068         return
5069     game.optime = 3.0e-5*game.height
5070     if game.optime >= 0.8*game.state.remtime:
5071         prout(_("First Officer Spock-  \"Captain, I compute that such"))
5072         proutn(_("  a maneuver would require approximately %2d%% of our") % \
5073                int(100*game.optime/game.state.remtime))
5074         prout(_("remaining time."))
5075         proutn(_("Are you sure this is wise?\" "))
5076         if ja() == False:
5077             game.optime = 0.0
5078             return
5079     if game.landed:
5080         # Kirk on planet 
5081         if game.iscraft == "onship":
5082             # Galileo on ship! 
5083             if not damaged(DTRANSP):
5084                 proutn(_("Spock-  \"Would you rather use the transporter?\" "))
5085                 if ja() == True:
5086                     beam()
5087                     return
5088                 proutn(_("Shuttle crew"))
5089             else:
5090                 proutn(_("Rescue party"))
5091             prout(_(" boards Galileo and swoops toward planet surface."))
5092             game.iscraft = "offship"
5093             skip(1)
5094             if consumeTime():
5095                 return
5096             game.iplnet.known="shuttle_down"
5097             prout(_("Trip complete."))
5098             return
5099         else:
5100             # Ready to go back to ship 
5101             prout(_("You and your mining party board the"))
5102             prout(_("shuttle craft for the trip back to the Enterprise."))
5103             skip(1)
5104             prouts(_("The short hop begins . . ."))
5105             skip(1)
5106             game.iplnet.known="known"
5107             game.icraft = True
5108             skip(1)
5109             game.landed = False
5110             if consumeTime():
5111                 return
5112             game.iscraft = "onship"
5113             game.icraft = False
5114             if game.imine:
5115                 game.icrystl = True
5116                 game.cryprob = 0.05
5117             game.imine = False
5118             prout(_("Trip complete."))
5119             return
5120     else:
5121         # Kirk on ship 
5122         # and so is Galileo 
5123         prout(_("Mining party assembles in the hangar deck,"))
5124         prout(_("ready to board the shuttle craft \"Galileo\"."))
5125         skip(1)
5126         prouts(_("The hangar doors open; the trip begins."))
5127         skip(1)
5128         game.icraft = True
5129         game.iscraft = "offship"
5130         if consumeTime():
5131             return
5132         game.iplnet.known = "shuttle_down"
5133         game.landed = True
5134         game.icraft = False
5135         prout(_("Trip complete."))
5136         return
5137
5138 def deathray():
5139     # use the big zapper 
5140     r = random.random()
5141         
5142     game.ididit = False
5143     skip(1)
5144     chew()
5145     if game.ship != IHE:
5146         prout(_("Ye Faerie Queene has no death ray."))
5147         return
5148     if game.nenhere==0:
5149         prout(_("Sulu-  \"But Sir, there are no enemies in this quadrant.\""))
5150         return
5151     if damaged(DDRAY):
5152         prout(_("Death Ray is damaged."))
5153         return
5154     prout(_("Spock-  \"Captain, the 'Experimental Death Ray'"))
5155     prout(_("  is highly unpredictible.  Considering the alternatives,"))
5156     proutn(_("  are you sure this is wise?\" "))
5157     if ja() == False:
5158         return
5159     prout(_("Spock-  \"Acknowledged.\""))
5160     skip(1)
5161     game.ididit = True
5162     prouts(_("WHOOEE ... WHOOEE ... WHOOEE ... WHOOEE"))
5163     skip(1)
5164     prout(_("Crew scrambles in emergency preparation."))
5165     prout(_("Spock and Scotty ready the death ray and"))
5166     prout(_("prepare to channel all ship's power to the device."))
5167     skip(1)
5168     prout(_("Spock-  \"Preparations complete, sir.\""))
5169     prout(_("Kirk-  \"Engage!\""))
5170     skip(1)
5171     prouts(_("WHIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"))
5172     skip(1)
5173     dprob = .30
5174     if game.options & OPTION_PLAIN:
5175         dprob = .5
5176     if r > dprob:
5177         prouts(_("Sulu- \"Captain!  It's working!\""))
5178         skip(2)
5179         while game.nenhere > 0:
5180             deadkl(game.ks[1], game.quad[game.ks[1].x][game.ks[1].y],game.ks[1])
5181         prout(_("Ensign Chekov-  \"Congratulations, Captain!\""))
5182         if (game.state.remkl + game.state.remcom + game.state.nscrem) == 0:
5183             finish(FWON)    
5184         if (game.options & OPTION_PLAIN) == 0:
5185             prout(_("Spock-  \"Captain, I believe the `Experimental Death Ray'"))
5186             if random.random() <= 0.05:
5187                 prout(_("   is still operational.\""))
5188             else:
5189                 prout(_("   has been rendered nonfunctional.\""))
5190                 game.damage[DDRAY] = 39.95
5191         return
5192     r = random.random() # Pick failure method 
5193     if r <= .30:
5194         prouts(_("Sulu- \"Captain!  It's working!\""))
5195         skip(1)
5196         prouts(_("***RED ALERT!  RED ALERT!"))
5197         skip(1)
5198         prout(_("***MATTER-ANTIMATTER IMPLOSION IMMINENT!"))
5199         skip(1)
5200         prouts(_("***RED ALERT!  RED A*L********************************"))
5201         skip(1)
5202         stars()
5203         prouts(_("******************   KA-BOOM!!!!   *******************"))
5204         skip(1)
5205         kaboom()
5206         return
5207     if r <= .55:
5208         prouts(_("Sulu- \"Captain!  Yagabandaghangrapl, brachriigringlanbla!\""))
5209         skip(1)
5210         prout(_("Lt. Uhura-  \"Graaeek!  Graaeek!\""))
5211         skip(1)
5212         prout(_("Spock-  \"Fascinating!  . . . All humans aboard"))
5213         prout(_("  have apparently been transformed into strange mutations."))
5214         prout(_("  Vulcans do not seem to be affected."))
5215         skip(1)
5216         prout(_("Kirk-  \"Raauch!  Raauch!\""))
5217         finish(FDRAY)
5218         return
5219     if r <= 0.75:
5220         intj
5221         prouts(_("Sulu- \"Captain!  It's   --WHAT?!?!\""))
5222         skip(2)
5223         proutn(_("Spock-  \"I believe the word is"))
5224         prouts(_(" *ASTONISHING*"))
5225         prout(_(" Mr. Sulu."))
5226         for i in range(QUADSIZE):
5227             for j in range(QUADSIZE):
5228                 if game.quad[i][j] == IHDOT:
5229                     game.quad[i][j] = IHQUEST
5230         prout(_("  Captain, our quadrant is now infested with"))
5231         prouts(_(" - - - - - -  *THINGS*."))
5232         skip(1)
5233         prout(_("  I have no logical explanation.\""))
5234         return
5235     prouts(_("Sulu- \"Captain!  The Death Ray is creating tribbles!\""))
5236     skip(1)
5237     prout(_("Scotty-  \"There are so many tribbles down here"))
5238     prout(_("  in Engineering, we can't move for 'em, Captain.\""))
5239     finish(FTRIBBLE)
5240     return
5241
5242 # Code from reports.c begins here
5243
5244 def attackreport(curt):
5245     # report status of bases under attack 
5246     if not curt:
5247         if is_scheduled(FCDBAS):
5248             prout(_("Starbase in Quadrant %s is currently under Commander attack.") % game.battle)
5249             prout(_("It can hold out until Stardate %d.") % int(scheduled(FCDBAS)))
5250         elif game.isatb == 1:
5251             prout(_("Starbase in Quadrant %s is under Super-commander attack.") % game.state.kscmdr)
5252             prout(_("It can hold out until Stardate %d.") % int(scheduled(FSCDBAS)))
5253         else:
5254             prout(_("No Starbase is currently under attack."))
5255     else:
5256         if is_scheduled(FCDBAS):
5257             proutn(_("Base in %s attacked by C. Alive until %.1f") % (game.battle, scheduled(FCDBAS)))
5258         if game.isatb:
5259             proutn(_("Base in %s attacked by S. Alive until %.1f") % (game.state.kscmdr, scheduled(FSCDBAS)))
5260         clreol()
5261
5262 def report():
5263     # report on general game status 
5264     chew()
5265     s1 = "" and game.thawed and _("thawed ")
5266     s2 = {1:"short", 2:"medium", 4:"long"}[game.length]
5267     s3 = (None, _("novice"). _("fair"),
5268           _("good"), _("expert"), _("emeritus"))[game.skill]
5269     prout(_("You %s a %s%s %s game.") % ((_("were playing"), _("are playing"))[game.alldone], s1, s2, s3))
5270     if game.skill>SKILL_GOOD and game.thawed and not game.alldone:
5271         prout(_("No plaque is allowed."))
5272     if game.tourn:
5273         prout(_("This is tournament game %d.") % game.tourn)
5274     prout(_("Your secret password is \"%s\"") % game.passwd)
5275     proutn(_("%d of %d Klingons have been killed") % (((game.inkling + game.incom + game.inscom) - (game.state.remkl + game.state.remcom + game.state.nscrem)), 
5276            (game.inkling + game.incom + game.inscom)))
5277     if game.incom - game.state.remcom:
5278         prout(_(", including %d Commander%s.") % (game.incom - game.state.remcom, (_("s"), "")[(game.incom - game.state.remcom)==1]))
5279     elif game.inkling - game.state.remkl + (game.inscom - game.state.nscrem) > 0:
5280         prout(_(", but no Commanders."))
5281     else:
5282         prout(".")
5283     if game.skill > SKILL_FAIR:
5284         prout(_("The Super Commander has %sbeen destroyed.") % ("", _("not "))[game.state.nscrem])
5285     if game.state.rembase != game.inbase:
5286         proutn(_("There "))
5287         if game.inbase-game.state.rembase==1:
5288             proutn(_("has been 1 base"))
5289         else:
5290             proutn(_("have been %d bases") % (game.inbase-game.state.rembase))
5291         prout(_(" destroyed, %d remaining.") % game.state.rembase)
5292     else:
5293         prout(_("There are %d bases.") % game.inbase)
5294     if communicating() or game.iseenit:
5295         # Don't report this if not seen and
5296         # either the radio is dead or not at base!
5297         attackreport(False)
5298         game.iseenit = True
5299     if game.casual: 
5300         prout(_("%d casualt%s suffered so far.") % (game.casual, ("y", "ies")[game.casual!=1]))
5301     if game.nhelp:
5302         prout(_("There were %d call%s for help.") % (game.nhelp,  ("" , _("s"))[game.nhelp!=1]))
5303     if game.ship == IHE:
5304         proutn(_("You have "))
5305         if game.nprobes:
5306             proutn("%d" % (game.nprobes))
5307         else:
5308             proutn(_("no"))
5309         proutn(_(" deep space probe"))
5310         if game.nprobes!=1:
5311             proutn(_("s"))
5312         prout(".")
5313     if communicating() and is_scheduled(FDSPROB):
5314         if game.isarmed: 
5315             proutn(_("An armed deep space probe is in "))
5316         else:
5317             proutn(_("A deep space probe is in "))
5318         prout("Quadrant %s." % game.probec)
5319     if game.icrystl:
5320         if game.cryprob <= .05:
5321             prout(_("Dilithium crystals aboard ship... not yet used."))
5322         else:
5323             i=0
5324             ai = 0.05
5325             while game.cryprob > ai:
5326                 ai *= 2.0
5327                 i += 1
5328             prout(_("Dilithium crystals have been used %d time%s.") % \
5329                   (i, (_("s"), "")[i==1]))
5330     skip(1)
5331         
5332 def lrscan():
5333     # long-range sensor scan 
5334     if damaged(DLRSENS):
5335         # Now allow base's sensors if docked 
5336         if game.condition != "docked":
5337             prout(_("LONG-RANGE SENSORS DAMAGED."))
5338             return
5339         prout(_("Starbase's long-range scan"))
5340     else:
5341         prout(_("Long-range scan"))
5342     for x in range(game.quadrant.x-1, game.quadrant.x+2):
5343         proutn(" ")
5344         for y in range(game.quadrant.y-1, game.quadrant.y+2):
5345             if not VALID_QUADRANT(x, y):
5346                 proutn("  -1")
5347             else:
5348                 if not damaged(DRADIO):
5349                     game.state.galaxy[x][y].charted = True
5350                 game.state.chart[x][y].klingons = game.state.galaxy[x][y].klingons
5351                 game.state.chart[x][y].starbase = game.state.galaxy[x][y].starbase
5352                 game.state.chart[x][y].stars = game.state.galaxy[x][y].stars
5353                 if game.state.galaxy[x][y].supernova: 
5354                     proutn(" ***")
5355                 else:
5356                     proutn(" %3d" % (game.state.chart[x][y].klingons*100 + game.state.chart[x][y].starbase * 10 + game.state.chart[x][y].stars))
5357         prout(" ")
5358
5359 def damagereport():
5360     # damage report 
5361     jdam = False
5362     chew()
5363
5364     for i in range(NDEVICES):
5365         if damaged(i):
5366             if not jdam:
5367                 prout(_("\tDEVICE\t\t\t-REPAIR TIMES-"))
5368                 prout(_("\t\t\tIN FLIGHT\t\tDOCKED"))
5369                 jdam = True
5370             prout("  %-26s\t%8.2f\t\t%8.2f" % (device[i],
5371                                                game.damage[i]+0.05,
5372                                                game.docfac*game.damage[i]+0.005))
5373     if not jdam:
5374         prout(_("All devices functional."))
5375
5376 def rechart():
5377     # update the chart in the Enterprise's computer from galaxy data 
5378     game.lastchart = game.state.date
5379     for i in range(GALSIZE):
5380         for j in range(GALSIZE):
5381             if game.state.galaxy[i][j].charted:
5382                 game.state.chart[i][j].klingons = game.state.galaxy[i][j].klingons
5383                 game.state.chart[i][j].starbase = game.state.galaxy[i][j].starbase
5384                 game.state.chart[i][j].stars = game.state.galaxy[i][j].stars
5385
5386 def chart():
5387     # display the star chart  
5388     chew()
5389     if not damaged(DRADIO):
5390         rechart()
5391     if game.lastchart < game.state.date and game.condition == "docked":
5392         prout(_("Spock-  \"I revised the Star Chart from the starbase's records.\""))
5393         rechart()
5394
5395     prout(_("       STAR CHART FOR THE KNOWN GALAXY"))
5396     if game.state.date > game.lastchart:
5397         prout(_("(Last surveillance update %d stardates ago).") % ((int)(game.state.date-game.lastchart)))
5398     prout("      1    2    3    4    5    6    7    8")
5399     for i in range(GALSIZE):
5400         proutn("%d |" % (i+1))
5401         for j in range(GALSIZE):
5402             if (game.options & OPTION_SHOWME) and i == game.quadrant.x and j == game.quadrant.y:
5403                 proutn("<")
5404             else:
5405                 proutn(" ")
5406             if game.state.galaxy[i][j].supernova:
5407                 show = "***"
5408             elif not game.state.galaxy[i][j].charted and game.state.galaxy[i][j].starbase:
5409                 show = ".1."
5410             elif game.state.galaxy[i][j].charted:
5411                 show = "%3d" % (game.state.chart[i][j].klingons*100 + game.state.chart[i][j].starbase * 10 + game.state.chart[i][j].stars)
5412             else:
5413                 show = "..."
5414             proutn(show)
5415             if (game.options & OPTION_SHOWME) and i == game.quadrant.x and j == game.quadrant.y:
5416                 proutn(">")
5417             else:
5418                 proutn(" ")
5419         proutn("  |")
5420         if i<GALSIZE:
5421             skip(1)
5422
5423 def sectscan(goodScan, i, j):
5424     # light up an individual dot in a sector 
5425     if goodScan or (abs(i-game.sector.x)<= 1 and abs(j-game.sector.y) <= 1):
5426         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):
5427             if game.condition   == "red": textcolor(RED)
5428             elif game.condition == "green": textcolor(GREEN)
5429             elif game.condition == "yellow": textcolor(YELLOW)
5430             elif game.condition == "docked": textcolor(CYAN)
5431             elif game.condition == "dead": textcolor(BROWN)
5432             if game.quad[i][j] != game.ship: 
5433                 highvideo()
5434         proutn("%c " % game.quad[i][j])
5435         textcolor(DEFAULT)
5436     else:
5437         proutn("- ")
5438
5439 def status(req=0):
5440     # print status report lines 
5441
5442     if not req or req == 1:
5443         prstat(_("Stardate"), _("%.1f, Time Left %.2f") \
5444                % (game.state.date, game.state.remtime))
5445     if not req or req == 2:
5446         if game.condition != "docked":
5447             newcnd()
5448         dam = 0
5449         for t in range(NDEVICES):
5450             if game.damage[t]>0: 
5451                 dam += 1
5452         prstat(_("Condition"), _("%s, %i DAMAGES") % (game.condition.upper(), dam))
5453     if not req or req == 3:
5454         prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
5455     if not req or req == 4:
5456         if damaged(DLIFSUP):
5457             if game.condition == "docked":
5458                 s = _("DAMAGED, Base provides")
5459             else:
5460                 s = _("DAMAGED, reserves=%4.2f") % game.lsupres
5461         else:
5462             s = _("ACTIVE")
5463         prstat(_("Life Support"), s)
5464     if not req or req == 5:
5465         prstat(_("Warp Factor"), "%.1f" % game.warpfac)
5466     if not req or req == 6:
5467         extra = ""
5468         if game.icrystl and (game.options & OPTION_SHOWME):
5469             extra = _(" (have crystals)")
5470         prstat(_("Energy"), "%.2f%s" % (game.energy, extra))
5471     if not req or req == 7:
5472         prstat(_("Torpedoes"), "%d" % (game.torps))
5473     if not req or req == 8:
5474         if damaged(DSHIELD):
5475             s = _("DAMAGED,")
5476         elif game.shldup:
5477             s = _("UP,")
5478         else:
5479             s = _("DOWN,")
5480         data = _(" %d%% %.1f units") \
5481                % (int((100.0*game.shield)/game.inshld + 0.5), game.shield)
5482         prstat(_("Shields"), s+data)
5483     if not req or req == 9:
5484         prstat(_("Klingons Left"), "%d" \
5485                % (game.state.remkl + game.state.remcom + game.state.nscrem))
5486     if not req or req == 10:
5487         if game.options & OPTION_WORLDS:
5488             plnet = game.state.galaxy[game.quadrant.x][game.quadrant.y].planet
5489             if plnet and plnet.inhabited:
5490                 prstat(_("Major system"), plnet.name)
5491             else:
5492                 prout(_("Sector is uninhabited"))
5493     elif not req or req == 11:
5494         attackreport(not req)
5495
5496 def request():
5497     requests = ("da","co","po","ls","wa","en","to","sh","kl","sy", "ti")
5498     while scan() == IHEOL:
5499         proutn(_("Information desired? "))
5500     chew()
5501     if citem in requests:
5502         status(requests.index(citem))
5503     else:
5504         prout(_("UNRECOGNIZED REQUEST. Legal requests are:"))
5505         prout(("  date, condition, position, lsupport, warpfactor,"))
5506         prout(("  energy, torpedoes, shields, klingons, system, time."))
5507                 
5508 def srscan():
5509     # short-range scan 
5510     goodScan=True
5511     if damaged(DSRSENS):
5512         # Allow base's sensors if docked 
5513         if game.condition != "docked":
5514             prout(_("   S.R. SENSORS DAMAGED!"))
5515             goodScan=False
5516         else:
5517             prout(_("  [Using Base's sensors]"))
5518     else:
5519         prout(_("     Short-range scan"))
5520     if goodScan and not damaged(DRADIO): 
5521         game.state.chart[game.quadrant.x][game.quadrant.y].klingons = game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons
5522         game.state.chart[game.quadrant.x][game.quadrant.y].starbase = game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase
5523         game.state.chart[game.quadrant.x][game.quadrant.y].stars = game.state.galaxy[game.quadrant.x][game.quadrant.y].stars
5524         game.state.galaxy[game.quadrant.x][game.quadrant.y].charted = True
5525     prout("    1 2 3 4 5 6 7 8 9 10")
5526     if game.condition != "docked":
5527         newcnd()
5528     for i in range(QUADSIZE):
5529         proutn("%2d  " % (i+1))
5530         for j in range(QUADSIZE):
5531             sectscan(goodScan, i, j)
5532         skip(1)
5533                         
5534                         
5535 def eta():
5536     # use computer to get estimated time of arrival for a warp jump 
5537     w1 = coord(); w2 = coord()
5538     prompt = False
5539     if damaged(DCOMPTR):
5540         prout(_("COMPUTER DAMAGED, USE A POCKET CALCULATOR."))
5541         skip(1)
5542         return
5543     if scan() != IHREAL:
5544         prompt = True
5545         chew()
5546         proutn(_("Destination quadrant and/or sector? "))
5547         if scan()!=IHREAL:
5548             huh()
5549             return
5550     w1.y = int(aaitem-0.5)
5551     if scan() != IHREAL:
5552         huh()
5553         return
5554     w1.x = int(aaitem-0.5)
5555     if scan() == IHREAL:
5556         w2.y = int(aaitem-0.5)
5557         if scan() != IHREAL:
5558             huh()
5559             return
5560         w2.x = int(aaitem-0.5)
5561     else:
5562         if game.quadrant.y>w1.x:
5563             w2.x = 0
5564         else:
5565             w2.x=QUADSIZE-1
5566         if game.quadrant.x>w1.y:
5567             w2.y = 0
5568         else:
5569             w2.y=QUADSIZE-1
5570     if not VALID_QUADRANT(w1.x, w1.y) or not VALID_SECTOR(w2.x, w2.y):
5571         huh()
5572         return
5573     game.dist = math.sqrt(square(w1.y-game.quadrant.y+0.1*(w2.y-game.sector.y))+
5574                 square(w1.x-game.quadrant.x+0.1*(w2.x-game.sector.x)))
5575     wfl = False
5576     if prompt:
5577         prout(_("Answer \"no\" if you don't know the value:"))
5578     while True:
5579         chew()
5580         proutn(_("Time or arrival date? "))
5581         if scan()==IHREAL:
5582             ttime = aaitem
5583             if ttime > game.state.date:
5584                 ttime -= game.state.date # Actually a star date
5585             twarp=(math.floor(math.sqrt((10.0*game.dist)/ttime)*10.0)+1.0)/10.0
5586             if ttime <= 1e-10 or twarp > 10:
5587                 prout(_("We'll never make it, sir."))
5588                 chew()
5589                 return
5590             if twarp < 1.0:
5591                 twarp = 1.0
5592             break
5593         chew()
5594         proutn(_("Warp factor? "))
5595         if scan()== IHREAL:
5596             wfl = True
5597             twarp = aaitem
5598             if twarp<1.0 or twarp > 10.0:
5599                 huh()
5600                 return
5601             break
5602         prout(_("Captain, certainly you can give me one of these."))
5603     while True:
5604         chew()
5605         ttime = (10.0*game.dist)/square(twarp)
5606         tpower = game.dist*twarp*twarp*twarp*(game.shldup+1)
5607         if tpower >= game.energy:
5608             prout(_("Insufficient energy, sir."))
5609             if not game.shldup or tpower > game.energy*2.0:
5610                 if not wfl:
5611                     return
5612                 proutn(_("New warp factor to try? "))
5613                 if scan() == IHREAL:
5614                     wfl = True
5615                     twarp = aaitem
5616                     if twarp<1.0 or twarp > 10.0:
5617                         huh()
5618                         return
5619                     continue
5620                 else:
5621                     chew()
5622                     skip(1)
5623                     return
5624             prout(_("But if you lower your shields,"))
5625             proutn(_("remaining"))
5626             tpower /= 2
5627         else:
5628             proutn(_("Remaining"))
5629         prout(_(" energy will be %.2f.") % (game.energy-tpower))
5630         if wfl:
5631             prout(_("And we will arrive at stardate %.2f.") % (game.state.date+ttime))
5632         elif twarp==1.0:
5633             prout(_("Any warp speed is adequate."))
5634         else:
5635             prout(_("Minimum warp needed is %.2f,") % (twarp))
5636             prout(_("and we will arrive at stardate %.2f.") % (game.state.date+ttime))
5637         if game.state.remtime < ttime:
5638             prout(_("Unfortunately, the Federation will be destroyed by then."))
5639         if twarp > 6.0:
5640             prout(_("You'll be taking risks at that speed, Captain"))
5641         if (game.isatb==1 and game.state.kscmdr == w1 and \
5642              scheduled(FSCDBAS)< ttime+game.state.date) or \
5643             (scheduled(FCDBAS)<ttime+game.state.date and game.battle == w1):
5644             prout(_("The starbase there will be destroyed by then."))
5645         proutn(_("New warp factor to try? "))
5646         if scan() == IHREAL:
5647             wfl = True
5648             twarp = aaitem
5649             if twarp<1.0 or twarp > 10.0:
5650                 huh()
5651                 return
5652         else:
5653             chew()
5654             skip(1)
5655             return
5656                         
5657
5658 #ifdef BSD_BUG_FOR_BUG
5659 # A visual scan is made in a particular direction of three sectors
5660 # in the general direction specified.  This takes time, and
5661 # Klingons can attack you, so it should be done only when sensors
5662 # are out.  Code swiped from BSD-Trek.  Not presently used, as we
5663 # automatically display all adjacent sectors on the short-range
5664 # scan even when short-range sensors are out.
5665
5666 # This struct[] has the delta x, delta y for particular directions
5667
5668 visdelta = (
5669     (-1,-1),
5670     (-1, 0),
5671     (-1, 1),
5672     (0,  1),
5673     (1,  1),
5674     (1,  0),
5675     (1, -1),
5676     (0, -1),
5677     (-1,-1),
5678     (-1, 0),
5679     (-1, 1),
5680 )
5681
5682 def visual():
5683     v = coord()
5684     if scan() != IHREAL:
5685         chew()
5686         proutn(_("Direction? "))
5687         if scan()!=IHREAL:
5688             huh()
5689             return
5690     if aaitem < 0.0 or aaitem > 360.0:
5691         return
5692     co = (aaitem + 22) / 45
5693     v = visdelta[co]
5694     ix = game.sector.x + v.x
5695     iy = game.sector.y + v.y
5696     if ix < 0 or ix >= QUADSIZE or iy < 0 or iy >= QUADSIZE:
5697         co = '?'
5698     else:
5699         co = game.quad[ix][iy]
5700     printf("%d,%d %c " % (ix+1, iy+1, co))
5701     v += 1
5702     ix = game.sector.x + v.x
5703     iy = game.sector.y + v.y
5704     if ix < 0 or ix >= QUADSIZE or iy < 0 or iy >= QUADSIZE:
5705         co = '?'
5706     else:
5707         co = game.quad[ix][iy]
5708     printf("%c " % (co))
5709     v += 1
5710     ix = game.sector.x + v.x
5711     iy = game.sector.y + v.y
5712     if ix < 0 or ix >= QUADSIZE or iy < 0 or iy >= QUADSIZE:
5713         co = '?'
5714     else:
5715         co = game.quad[ix][iy]
5716     prout("%c %d,%d\n" % (co, ix+1, iy+1))
5717     game.optime = 0.5
5718     game.ididit = True
5719 #endif
5720
5721 # Code from setup.c begins here
5722
5723 def prelim():
5724     # issue a historically correct banner 
5725     skip(2)
5726     prout(_("-SUPER- STAR TREK"))
5727     skip(1)
5728 #ifdef __HISTORICAL__
5729 #    prout(_("Latest update-21 Sept 78"))
5730 #    skip(1)
5731 #endif __HISTORICAL__ 
5732
5733 def freeze(boss):
5734     # save game 
5735     if boss:
5736         citem = "emsave.trk"
5737     else:
5738         key = scan()
5739         if key == IHEOL:
5740             proutn(_("File name: "))
5741             key = scan()
5742         if key != IHALPHA:
5743             huh()
5744             return
5745         chew()
5746         if '.' not in citem:
5747             citem += ".trk"
5748     try:
5749         fp = open(citem, "wb")
5750     except IOError:
5751         prout(_("Can't freeze game as file %s") % citem)
5752         return
5753     cPickle.dump(game, fp)
5754     fp.close()
5755
5756 def thaw():
5757     # retrieve saved game 
5758     game.passwd[0] = '\0'
5759     key = scan()
5760     if key == IHEOL:
5761         proutn(_("File name: "))
5762         key = scan()
5763     if key != IHALPHA:
5764         huh()
5765         return True
5766     chew()
5767     if '.' not in citem:
5768         citem += ".trk"
5769     try:
5770         fp = open(citem, "rb")
5771     except IOError:
5772         prout(_("Can't thaw game in %s") % citem)
5773         return
5774     game = cPickle.load(fp)
5775     fp.close()
5776     return False
5777
5778 # I used <http://www.memory-alpha.org> to find planets
5779 # with references in ST:TOS.  Eath and the Alpha Centauri
5780 # Colony have been omitted.
5781
5782 # Some planets marked Class G and P here will be displayed as class M
5783 # because of the way planets are generated. This is a known bug.
5784 systnames = (
5785     # Federation Worlds 
5786     _("Andoria (Fesoan)"),      # several episodes 
5787     _("Tellar Prime (Miracht)"),        # TOS: "Journey to Babel" 
5788     _("Vulcan (T'Khasi)"),      # many episodes 
5789     _("Medusa"),                # TOS: "Is There in Truth No Beauty?" 
5790     _("Argelius II (Nelphia)"),# TOS: "Wolf in the Fold" ("IV" in BSD) 
5791     _("Ardana"),                # TOS: "The Cloud Minders" 
5792     _("Catulla (Cendo-Prae)"),  # TOS: "The Way to Eden" 
5793     _("Gideon"),                # TOS: "The Mark of Gideon" 
5794     _("Aldebaran III"), # TOS: "The Deadly Years" 
5795     _("Alpha Majoris I"),       # TOS: "Wolf in the Fold" 
5796     _("Altair IV"),             # TOS: "Amok Time 
5797     _("Ariannus"),              # TOS: "Let That Be Your Last Battlefield" 
5798     _("Benecia"),               # TOS: "The Conscience of the King" 
5799     _("Beta Niobe I (Sarpeidon)"),      # TOS: "All Our Yesterdays" 
5800     _("Alpha Carinae II"),      # TOS: "The Ultimate Computer" 
5801     _("Capella IV (Kohath)"),   # TOS: "Friday's Child" (Class G) 
5802     _("Daran V"),               # TOS: "For the World is Hollow and I Have Touched the Sky" 
5803     _("Deneb II"),              # TOS: "Wolf in the Fold" ("IV" in BSD) 
5804     _("Eminiar VII"),           # TOS: "A Taste of Armageddon" 
5805     _("Gamma Canaris IV"),      # TOS: "Metamorphosis" 
5806     _("Gamma Tranguli VI (Vaalel)"),    # TOS: "The Apple" 
5807     _("Ingraham B"),            # TOS: "Operation: Annihilate" 
5808     _("Janus IV"),              # TOS: "The Devil in the Dark" 
5809     _("Makus III"),             # TOS: "The Galileo Seven" 
5810     _("Marcos XII"),            # TOS: "And the Children Shall Lead", 
5811     _("Omega IV"),              # TOS: "The Omega Glory" 
5812     _("Regulus V"),             # TOS: "Amok Time 
5813     _("Deneva"),                # TOS: "Operation -- Annihilate!" 
5814     # Worlds from BSD Trek 
5815     _("Rigel II"),              # TOS: "Shore Leave" ("III" in BSD) 
5816     _("Beta III"),              # TOS: "The Return of the Archons" 
5817     _("Triacus"),               # TOS: "And the Children Shall Lead", 
5818     _("Exo III"),               # TOS: "What Are Little Girls Made Of?" (Class P) 
5819 #       # Others 
5820 #    _("Hansen's Planet"),      # TOS: "The Galileo Seven" 
5821 #    _("Taurus IV"),            # TOS: "The Galileo Seven" (class G) 
5822 #    _("Antos IV (Doraphane)"), # TOS: "Whom Gods Destroy", "Who Mourns for Adonais?" 
5823 #    _("Izar"),                 # TOS: "Whom Gods Destroy" 
5824 #    _("Tiburon"),              # TOS: "The Way to Eden" 
5825 #    _("Merak II"),             # TOS: "The Cloud Minders" 
5826 #    _("Coridan (Desotriana)"), # TOS: "Journey to Babel" 
5827 #    _("Iotia"),                # TOS: "A Piece of the Action" 
5828 )
5829
5830 device = (
5831         _("S. R. Sensors"), \
5832         _("L. R. Sensors"), \
5833         _("Phasers"), \
5834         _("Photon Tubes"), \
5835         _("Life Support"), \
5836         _("Warp Engines"), \
5837         _("Impulse Engines"), \
5838         _("Shields"), \
5839         _("Subspace Radio"), \
5840         _("Shuttle Craft"), \
5841         _("Computer"), \
5842         _("Navigation System"), \
5843         _("Transporter"), \
5844         _("Shield Control"), \
5845         _("Death Ray"), \
5846         _("D. S. Probe"), \
5847 )
5848
5849 def setup(needprompt):
5850     # prepare to play, set up cosmos 
5851     w = coord()
5852     #  Decide how many of everything
5853     if choose(needprompt):
5854         return # frozen game
5855     # Prepare the Enterprise
5856     game.alldone = game.gamewon = False
5857     game.ship = IHE
5858     game.state.crew = FULLCREW
5859     game.energy = game.inenrg = 5000.0
5860     game.shield = game.inshld = 2500.0
5861     game.shldchg = False
5862     game.shldup = False
5863     game.inlsr = 4.0
5864     game.lsupres = 4.0
5865     game.quadrant = randplace(GALSIZE)
5866     game.sector = randplace(QUADSIZE)
5867     game.torps = game.intorps = 10
5868     game.nprobes = int(3.0*random.random() + 2.0)       # Give them 2-4 of these
5869     game.warpfac = 5.0
5870     game.wfacsq = game.warpfac * game.warpfac
5871     for i in range(NDEVICES): 
5872         game.damage[i] = 0.0
5873     # Set up assorted game parameters
5874     game.battle = coord()
5875     game.state.date = game.indate = 100.0*int(31.0*random.random()+20.0)
5876     game.nkinks = game.nhelp = game.casual = game.abandoned = 0
5877     game.iscate = game.resting = game.imine = game.icrystl = game.icraft = False
5878     game.isatb = game.state.nplankl = 0
5879     game.state.starkl = game.state.basekl = 0
5880     game.iscraft = "onship"
5881     game.landed = False
5882     game.alive = True
5883     game.docfac = 0.25
5884     for i in range(GALSIZE):
5885         for j in range(GALSIZE):
5886             quad = game.state.galaxy[i][j]
5887             quad.charted = 0
5888             quad.planet = None
5889             quad.romulans = 0
5890             quad.klingons = 0
5891             quad.starbase = False
5892             quad.supernova = False
5893             quad.status = "secure"
5894     # Initialize times for extraneous events
5895     schedule(FSNOVA, expran(0.5 * game.intime))
5896     schedule(FTBEAM, expran(1.5 * (game.intime / game.state.remcom)))
5897     schedule(FSNAP, 1.0 + random.random()) # Force an early snapshot
5898     schedule(FBATTAK, expran(0.3*game.intime))
5899     unschedule(FCDBAS)
5900     if game.state.nscrem:
5901         schedule(FSCMOVE, 0.2777)
5902     else:
5903         unschedule(FSCMOVE)
5904     unschedule(FSCDBAS)
5905     unschedule(FDSPROB)
5906     if (game.options & OPTION_WORLDS) and game.skill >= SKILL_GOOD:
5907         schedule(FDISTR, expran(1.0 + game.intime))
5908     else:
5909         unschedule(FDISTR)
5910     unschedule(FENSLV)
5911     unschedule(FREPRO)
5912     # Starchart is functional but we've never seen it
5913     game.lastchart = FOREVER
5914     # Put stars in the galaxy
5915     game.instar = 0
5916     for i in range(GALSIZE):
5917         for j in range(GALSIZE):
5918             k = int(random.random()*9.0 + 1.0)
5919             game.instar += k
5920             game.state.galaxy[i][j].stars = k
5921     # Locate star bases in galaxy
5922     for i in range(game.inbase):
5923         while True:
5924             while True:
5925                 w = randplace(GALSIZE)
5926                 if not game.state.galaxy[w.x][w.y].starbase:
5927                     break
5928             contflag = False
5929             # C version: for (j = i-1; j > 0; j--)
5930             # so it did them in the opposite order.
5931             for j in range(1, i):
5932                 # Improved placement algorithm to spread out bases
5933                 distq = w.distance(game.state.baseq[j])
5934                 if distq < 6.0*(BASEMAX+1-game.inbase) and random.random() < 0.75:
5935                     contflag = True
5936                     if idebug:
5937                         prout("=== Abandoning base #%d at %s" % (i, w))
5938                     break
5939                 elif distq < 6.0 * (BASEMAX+1-game.inbase):
5940                     if idebug:
5941                         prout("=== Saving base #%d, close to #%d" % (i, j))
5942             if not contflag:
5943                 break
5944         game.state.baseq[i] = w
5945         game.state.galaxy[w.x][w.y].starbase = True
5946         game.state.chart[w.x][w.y].starbase = True
5947     # Position ordinary Klingon Battle Cruisers
5948     krem = game.inkling
5949     klumper = 0.25*game.skill*(9.0-game.length)+1.0
5950     if klumper > MAXKLQUAD: 
5951         klumper = MAXKLQUAD
5952     while True:
5953         r = random.random()
5954         klump = (1.0 - r*r)*klumper
5955         if klump > krem:
5956             klump = krem
5957         krem -= klump
5958         while True:
5959             w = randplace(GALSIZE)
5960             if not game.state.galaxy[w.x][w.y].supernova and \
5961                game.state.galaxy[w.x][w.y].klingons + klump <= MAXKLQUAD:
5962                 break
5963         game.state.galaxy[w.x][w.y].klingons += int(klump)
5964         if krem <= 0:
5965             break
5966     # Position Klingon Commander Ships
5967     for i in range(1, game.incom+1):
5968         while True:
5969             w = randplace(GALSIZE)
5970             if (game.state.galaxy[w.x][w.y].klingons or random.random()>=0.75) and \
5971                    not game.state.galaxy[w.x][w.y].supernova and \
5972                    game.state.galaxy[w.x][w.y].klingons <= MAXKLQUAD-1 and \
5973                    not w in game.state.kcmdr[:i]:
5974                 break
5975         game.state.galaxy[w.x][w.y].klingons += 1
5976         game.state.kcmdr[i] = w
5977     # Locate planets in galaxy
5978     for i in range(game.inplan):
5979         while True:
5980             w = randplace(GALSIZE) 
5981             if game.state.galaxy[w.x][w.y].planet == None:
5982                 break
5983         new = planet()
5984         new.w = w
5985         new.crystals = "absent"
5986         if (game.options & OPTION_WORLDS) and i < NINHAB:
5987             new.pclass = "M"    # All inhabited planets are class M
5988             new.crystals = "absent"
5989             new.known = "known"
5990             new.name = systnames[i]
5991             new.inhabited = True
5992         else:
5993             new.pclass = ("M", "N", "O")[random.randint(0, 2)]
5994             if random.random()*1.5:             # 1 in 3 chance of crystals
5995                 new.crystals = "present"
5996             new.known = "unknown"
5997             new.inhabited = False
5998         game.state.galaxy[w.x][w.y].planet = new
5999         game.state.planets.append(new)
6000     # Locate Romulans
6001     for i in range(game.state.nromrem):
6002         w = randplace(GALSIZE)
6003         game.state.galaxy[w.x][w.y].romulans += 1
6004     # Locate the Super Commander
6005     if game.state.nscrem > 0:
6006         while True:
6007             w = randplace(GALSIZE)
6008             if not game.state.galaxy[w.x][w.y].supernova and game.state.galaxy[w.x][w.y].klingons <= MAXKLQUAD:
6009                 break
6010         game.state.kscmdr = w
6011         game.state.galaxy[w.x][w.y].klingons += 1
6012     # Place thing (in tournament game, thingx == -1, don't want one!)
6013     global thing
6014     if thing == None:
6015         thing = randplace(GALSIZE)
6016     skip(2)
6017     game.state.snap = False
6018     if game.skill == SKILL_NOVICE:
6019         prout(_("It is stardate %d. The Federation is being attacked by") % int(game.state.date))
6020         prout(_("a deadly Klingon invasion force. As captain of the United"))
6021         prout(_("Starship U.S.S. Enterprise, it is your mission to seek out"))
6022         prout(_("and destroy this invasion force of %d battle cruisers.") % ((game.inkling + game.incom + game.inscom)))
6023         prout(_("You have an initial allotment of %d stardates to complete") % int(game.intime))
6024         prout(_("your mission.  As you proceed you may be given more time."))
6025         skip(1)
6026         prout(_("You will have %d supporting starbases.") % (game.inbase))
6027         proutn(_("Starbase locations-  "))
6028     else:
6029         prout(_("Stardate %d.") % int(game.state.date))
6030         skip(1)
6031         prout(_("%d Klingons.") % (game.inkling + game.incom + game.inscom))
6032         prout(_("An unknown number of Romulans."))
6033         if game.state.nscrem:
6034             prout(_("And one (GULP) Super-Commander."))
6035         prout(_("%d stardates.") % int(game.intime))
6036         proutn(_("%d starbases in ") % game.inbase)
6037     for i in range(game.inbase):
6038         proutn(`game.state.baseq[i]`)
6039         proutn("  ")
6040     skip(2)
6041     proutn(_("The Enterprise is currently in Quadrant %s") % game.quadrant)
6042     proutn(_(" Sector %s") % game.sector)
6043     skip(2)
6044     prout(_("Good Luck!"))
6045     if game.state.nscrem:
6046         prout(_("  YOU'LL NEED IT."))
6047     waitfor()
6048     newqad(False)
6049     if game.nenhere - (thing == game.quadrant) - game.ithere:
6050         game.shldup = True
6051     if game.neutz:      # bad luck to start in a Romulan Neutral Zone
6052         attack(False)
6053
6054 def choose(needprompt):
6055     # choose your game type
6056     global thing
6057     while True:
6058         game.tourn = 0
6059         game.thawed = False
6060         game.skill = SKILL_NONE
6061         game.length = 0
6062         if needprompt: # Can start with command line options 
6063             proutn(_("Would you like a regular, tournament, or saved game? "))
6064         scan()
6065         if len(citem)==0: # Try again
6066             continue
6067         if isit("tournament"):
6068             while scan() == IHEOL:
6069                 proutn(_("Type in tournament number-"))
6070             if aaitem == 0:
6071                 chew()
6072                 continue # We don't want a blank entry
6073             game.tourn = int(round(aaitem))
6074             random.seed(aaitem)
6075             break
6076         if isit("saved") or isit("frozen"):
6077             if thaw():
6078                 continue
6079             chew()
6080             if game.passwd == None:
6081                 continue
6082             if not game.alldone:
6083                 game.thawed = True # No plaque if not finished
6084             report()
6085             waitfor()
6086             return True
6087         if isit("regular"):
6088             break
6089         proutn(_("What is \"%s\"?"), citem)
6090         chew()
6091     while game.length==0 or game.skill==SKILL_NONE:
6092         if scan() == IHALPHA:
6093             if isit("short"):
6094                 game.length = 1
6095             elif isit("medium"):
6096                 game.length = 2
6097             elif isit("long"):
6098                 game.length = 4
6099             elif isit("novice"):
6100                 game.skill = SKILL_NOVICE
6101             elif isit("fair"):
6102                 game.skill = SKILL_FAIR
6103             elif isit("good"):
6104                 game.skill = SKILL_GOOD
6105             elif isit("expert"):
6106                 game.skill = SKILL_EXPERT
6107             elif isit("emeritus"):
6108                 game.skill = SKILL_EMERITUS
6109             else:
6110                 proutn(_("What is \""))
6111                 proutn(citem)
6112                 prout("\"?")
6113         else:
6114             chew()
6115             if game.length==0:
6116                 proutn(_("Would you like a Short, Medium, or Long game? "))
6117             elif game.skill == SKILL_NONE:
6118                 proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
6119     # Choose game options -- added by ESR for SST2K
6120     if scan() != IHALPHA:
6121         chew()
6122         proutn(_("Choose your game style (or just press enter): "))
6123         scan()
6124     if isit("plain"):
6125         # Approximates the UT FORTRAN version.
6126         game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
6127         game.options |= OPTION_PLAIN
6128     elif isit("almy"):
6129         # Approximates Tom Almy's version.
6130         game.options &=~ (OPTION_THINGY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
6131         game.options |= OPTION_ALMY
6132     elif isit("fancy"):
6133         pass
6134     elif len(citem):
6135         proutn(_("What is \"%s\"?") % citem)
6136     setpassword()
6137     if game.passwd == "debug":
6138         idebug = True
6139         fputs("=== Debug mode enabled\n", sys.stdout)
6140
6141     # Use parameters to generate initial values of things
6142     game.damfac = 0.5 * game.skill
6143     game.state.rembase = random.randint(BASEMIN, BASEMAX)
6144     game.inbase = game.state.rembase
6145     game.inplan = 0
6146     if game.options & OPTION_PLANETS:
6147         game.inplan += int((MAXUNINHAB/2) + (MAXUNINHAB/2+1)*random.random())
6148     if game.options & OPTION_WORLDS:
6149         game.inplan += int(NINHAB)
6150     game.state.nromrem = game.inrom = int((2.0+random.random())*game.skill)
6151     game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR)
6152     game.state.remtime = 7.0 * game.length
6153     game.intime = game.state.remtime
6154     game.state.remkl = game.inkling = 2.0*game.intime*((game.skill+1 - 2*random.random())*game.skill*0.1+.15)
6155     game.incom = int(game.skill + 0.0625*game.inkling*random.random())
6156     game.state.remcom = min(10, game.incom)
6157     game.incom = game.state.remcom
6158     game.state.remres = (game.inkling+4*game.incom)*game.intime
6159     game.inresor = game.state.remres
6160     if game.inkling > 50:
6161         game.state.rembase += 1
6162         game.inbase = game.state.rembase
6163     return False
6164
6165 def dropin(iquad):
6166     # drop a feature on a random dot in the current quadrant 
6167     w = coord()
6168     while True:
6169         w = randplace(QUADSIZE)
6170         if game.quad[w.x][w.y] == IHDOT:
6171             break
6172     game.quad[w.x][w.y] = iquad
6173     return w
6174
6175 def newcnd():
6176     # update our alert status 
6177     game.condition = "green"
6178     if game.energy < 1000.0:
6179         game.condition = "yellow"
6180     if game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons or game.state.galaxy[game.quadrant.x][game.quadrant.y].romulans:
6181         game.condition = "red"
6182     if not game.alive:
6183         game.condition="dead"
6184
6185 def newkling(i):
6186     # drop new Klingon into current quadrant 
6187     pi = dropin(IHK)
6188     game.ks[i] = pi
6189     game.kdist[i] = game.kavgd[i] = distance(game.sector, pi)
6190     game.kpower[i] = random.random()*150.0 +300.0 +25.0*game.skill
6191     return pi
6192
6193 def newqad(shutup):
6194     # set up a new state of quadrant, for when we enter or re-enter it 
6195     w = coord()
6196     game.justin = True
6197     game.klhere = 0
6198     game.comhere = False
6199     game.ishere = False
6200     game.irhere = 0
6201     game.iplnet = 0
6202     game.nenhere = 0
6203     game.neutz = False
6204     game.inorbit = False
6205     game.landed = False
6206     game.ientesc = False
6207     game.ithere = False
6208     global iqengry
6209     iqengry = False
6210     game.iseenit = False
6211     if game.iscate:
6212         # Attempt to escape Super-commander, so tbeam back!
6213         game.iscate = False
6214         game.ientesc = True
6215     q = game.state.galaxy[game.quadrant.x][game.quadrant.y]
6216     # cope with supernova
6217     if q.supernova:
6218         return
6219     game.klhere = q.klingons
6220     game.irhere = q.romulans
6221     game.nenhere = game.klhere + game.irhere
6222     # Position Starship
6223     game.quad[game.sector.x][game.sector.y] = game.ship
6224     if q.klingons:
6225         w.x = w.y = 0   # quiet a gcc warning 
6226         # Position ordinary Klingons
6227         for i in range(game.klhere):
6228             w = newkling(i)
6229         # If we need a commander, promote a Klingon
6230         for i in range(game.state.remcom):
6231             if game.state.kcmdr[i] == game.quadrant:
6232                 break
6233                         
6234         if i <= game.state.remcom:
6235             game.quad[w.x][w.y] = IHC
6236             game.kpower[game.klhere] = 950.0+400.0*random.random()+50.0*game.skill
6237             game.comhere = True
6238         # If we need a super-commander, promote a Klingon
6239         if same(game.quadrant, game.state.kscmdr):
6240             game.quad[game.ks[0].x][game.ks[0].y] = IHS
6241             game.kpower[1] = 1175.0 + 400.0*random.random() + 125.0*game.skill
6242             game.iscate = (game.state.remkl > 1)
6243             game.ishere = True
6244     # Put in Romulans if needed
6245     for i in range(game.klhere, game.nenhere):
6246         w = dropin(IHR)
6247         game.ks[i] = w
6248         game.kdist[i] = game.kavgd[i] = distance(game.sector, w)
6249         game.kpower[i] = random.random()*400.0 + 450.0 + 50.0*game.skill
6250     # If quadrant needs a starbase, put it in
6251     if q.starbase:
6252         game.base = dropin(IHB)
6253     # If quadrant needs a planet, put it in
6254     if q.planet:
6255         game.iplnet = q.planet
6256         if not q.planet.inhabited:
6257             game.plnet = dropin(IHP)
6258         else:
6259             game.plnet = dropin(IHW)
6260     # Check for condition
6261     newcnd()
6262     # And finally the stars
6263     for i in range(q.stars): 
6264         dropin(IHSTAR)
6265
6266     # Check for RNZ
6267     if game.irhere > 0 and game.klhere == 0:
6268         game.neutz = True
6269         if not damaged(DRADIO):
6270             skip(1)
6271             prout(_("LT. Uhura- \"Captain, an urgent message."))
6272             prout(_("  I'll put it on audio.\"  CLICK"))
6273             skip(1)
6274             prout(_("INTRUDER! YOU HAVE VIOLATED THE ROMULAN NEUTRAL ZONE."))
6275             prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
6276     if shutup==0:
6277         # Put in THING if needed
6278         global thing
6279         if thing == game.quadrant:
6280             w = dropin(IHQUEST)
6281             thing = randplace(GALSIZE)
6282             game.nenhere += 1
6283             game.ks[game.nenhere] = w
6284             game.kdist[game.nenhere] = game.kavgd[game.nenhere] = \
6285                 distance(game.sector, w)
6286             game.kpower[game.nenhere] = random.random()*6000.0 +500.0 +250.0*game.skill
6287             if not damaged(DSRSENS):
6288                 skip(1)
6289                 prout(_("Mr. Spock- \"Captain, this is most unusual."))
6290                 prout(_("    Please examine your short-range scan.\""))
6291     # Decide if quadrant needs a Tholian; lighten up if skill is low 
6292     if game.options & OPTION_THOLIAN:
6293         if (game.skill < SKILL_GOOD and random.random() <= 0.02) or \
6294             (game.skill == SKILL_GOOD and random.random() <= 0.05) or \
6295             (game.skill > SKILL_GOOD and random.random() <= 0.08):
6296             while True:
6297                 game.tholian.x = random.choice((0, QUADSIZE-1))
6298                 game.tholian.y = random.choice((0, QUADSIZE-1))
6299                 if game.quad[game.tholian.x][game.tholian.y] == IHDOT:
6300                     break
6301             game.quad[game.tholian.x][game.tholian.y] = IHT
6302             game.ithere = True
6303             game.nenhere += 1
6304             game.ks[game.nenhere] = game.tholian
6305             game.kdist[game.nenhere] = game.kavgd[game.nenhere] = \
6306                 distance(game.sector, game.tholian)
6307             game.kpower[game.nenhere] = random.random()*400.0 +100.0 +25.0*game.skill
6308             # Reserve unoccupied corners 
6309             if game.quad[0][0]==IHDOT:
6310                 game.quad[0][0] = 'X'
6311             if game.quad[0][QUADSIZE-1]==IHDOT:
6312                 game.quad[0][QUADSIZE-1] = 'X'
6313             if game.quad[QUADSIZE-1][0]==IHDOT:
6314                 game.quad[QUADSIZE-1][0] = 'X'
6315             if game.quad[QUADSIZE-1][QUADSIZE-1]==IHDOT:
6316                 game.quad[QUADSIZE-1][QUADSIZE-1] = 'X'
6317     sortklings()
6318     # Put in a few black holes
6319     for i in range(1, 3+1):
6320         if random.random() > 0.5: 
6321             dropin(IHBLANK)
6322     # Take out X's in corners if Tholian present
6323     if game.ithere:
6324         if game.quad[0][0]=='X':
6325             game.quad[0][0] = IHDOT
6326         if game.quad[0][QUADSIZE-1]=='X':
6327             game.quad[0][QUADSIZE-1] = IHDOT
6328         if game.quad[QUADSIZE-1][0]=='X':
6329             game.quad[QUADSIZE-1][0] = IHDOT
6330         if game.quad[QUADSIZE-1][QUADSIZE-1]=='X':
6331             game.quad[QUADSIZE-1][QUADSIZE-1] = IHDOT
6332
6333 def sortklings():
6334     # sort Klingons by distance from us 
6335     # The author liked bubble sort. So we will use it. :-(
6336     if game.nenhere-(thing==game.quadrant)-game.ithere < 2:
6337         return
6338     while True:
6339         sw = False
6340         for j in range(game.nenhere):
6341             if game.kdist[j] > game.kdist[j+1]:
6342                 sw = True
6343                 t = game.kdist[j]
6344                 game.kdist[j] = game.kdist[j+1]
6345                 game.kdist[j+1] = t
6346                 t = game.kavgd[j]
6347                 game.kavgd[j] = game.kavgd[j+1]
6348                 game.kavgd[j+1] = t
6349                 k = game.ks[j].x
6350                 game.ks[j].x = game.ks[j+1].x
6351                 game.ks[j+1].x = k
6352                 k = game.ks[j].y
6353                 game.ks[j].y = game.ks[j+1].y
6354                 game.ks[j+1].y = k
6355                 t = game.kpower[j]
6356                 game.kpower[j] = game.kpower[j+1]
6357                 game.kpower[j+1] = t
6358         if not sw:
6359             break
6360
6361 def setpassword():
6362     # set the self-destruct password 
6363     if game.options & OPTION_PLAIN:
6364         while True:
6365             chew()
6366             proutn(_("Please type in a secret password- "))
6367             scan()
6368             game.passwd = citem
6369             if game.passwd != None:
6370                 break
6371     else:
6372         game.passwd = ""
6373         for i in range(3):
6374             game.passwd += chr(97+int(random.random()*25))
6375
6376 # Code from sst.c begins here
6377
6378 commands = {
6379     "SRSCAN":           OPTION_TTY,
6380     "STATUS":           OPTION_TTY,
6381     "REQUEST":          OPTION_TTY,
6382     "LRSCAN":           OPTION_TTY,
6383     "PHASERS":          0,
6384     "TORPEDO":          0,
6385     "PHOTONS":          0,
6386     "MOVE":             0,
6387     "SHIELDS":          0,
6388     "DOCK":             0,
6389     "DAMAGES":          0,
6390     "CHART":            0,
6391     "IMPULSE":          0,
6392     "REST":             0,
6393     "WARP":             0,
6394     "SCORE":            0,
6395     "SENSORS":          OPTION_PLANETS,
6396     "ORBIT":            OPTION_PLANETS,
6397     "TRANSPORT":        OPTION_PLANETS,
6398     "MINE":             OPTION_PLANETS,
6399     "CRYSTALS":         OPTION_PLANETS,
6400     "SHUTTLE":          OPTION_PLANETS,
6401     "PLANETS":          OPTION_PLANETS,
6402     "REPORT":           0,
6403     "COMPUTER":         0,
6404     "COMMANDS":         0,
6405     "EMEXIT":           0,
6406     "PROBE":            OPTION_PROBE,
6407     "SAVE":             0,
6408     "FREEZE":           0,      # Synonym for SAVE
6409     "ABANDON":          0,
6410     "DESTRUCT":         0,
6411     "DEATHRAY":         0,
6412     "DEBUG":            0,
6413     "MAYDAY":           0,
6414     "SOS":              0,      # Synonym for MAYDAY
6415     "CALL":             0,      # Synonym for MAYDAY
6416     "QUIT":             0,
6417     "HELP":             0,
6418     "SEED":             0,
6419     "VISUAL":           0,
6420 }
6421
6422 def ACCEPT(cmd):        return (not commands[cmd] or (commands[cmd] & game.options))
6423
6424 def listCommands():
6425     # generate a list of legal commands 
6426     k = 0
6427     proutn(_("LEGAL COMMANDS ARE:"))
6428     for key in commands:
6429         if ACCEPT(key):
6430             if k % 5 == 0:
6431                 skip(1)
6432             proutn("%-12s " % key) 
6433             k += 1
6434     skip(1)
6435
6436 def helpme():
6437     # browse on-line help 
6438     # Give help on commands 
6439     key = scan()
6440     while True:
6441         if key == IHEOL:
6442             setwnd(prompt_window)
6443             proutn(_("Help on what command? "))
6444             key = scan()
6445         setwnd(message_window)
6446         if key == IHEOL:
6447             return
6448         if citem in commands or citem == "ABBREV":
6449             break
6450         skip(1)
6451         listCommands()
6452         key = IHEOL
6453         chew()
6454         skip(1)
6455     cmd = citem.upper()
6456     try:
6457         fp = open(SSTDOC, "r")
6458     except IOError:
6459         try:
6460             fp = open(DOC_NAME, "r")
6461         except IOError:
6462             prout(_("Spock-  \"Captain, that information is missing from the"))
6463             proutn(_("   computer. You need to find "))
6464             proutn(DOC_NAME)
6465             prout(_(" and put it in the"))
6466             proutn(_("   current directory or to "))
6467             proutn(SSTDOC)
6468             prout(".\"")
6469             #
6470             # This used to continue: "You need to find SST.DOC and put 
6471             # it in the current directory."
6472             # 
6473             return
6474     while True:
6475         linebuf = fp.readline()
6476         if linebuf == '':
6477             prout(_("Spock- \"Captain, there is no information on that command.\""))
6478             fp.close()
6479             return
6480         if linebuf[0] == '%' and linebuf[1] == '%' and linebuf[2] == ' ':
6481             linebuf = linebuf[3:].strip()
6482             if cmd == linebuf:
6483                 break
6484     skip(1)
6485     prout(_("Spock- \"Captain, I've found the following information:\""))
6486     skip(1)
6487     while linebuf in fp:
6488         if "******" in linebuf:
6489             break
6490         proutn(linebuf)
6491     fp.close()
6492
6493 def makemoves():
6494     # command-interpretation loop 
6495     v = 0
6496     clrscr()
6497     setwnd(message_window)
6498     while True:         # command loop 
6499         drawmaps(1)
6500         while True:     # get a command 
6501             hitme = False
6502             game.justin = False
6503             game.optime = 0.0
6504             chew()
6505             setwnd(prompt_window)
6506             clrscr()
6507             proutn("COMMAND> ")
6508             if scan() == IHEOL:
6509                 if game.options & OPTION_CURSES:
6510                     makechart()
6511                 continue
6512             game.ididit = False
6513             clrscr()
6514             setwnd(message_window)
6515             clrscr()
6516             candidates = filter(lambda x: x.startswith(citem.upper()),
6517                                 commands)
6518             if len(candidates) == 1:
6519                 cmd = candidates[0]
6520                 break
6521             elif candidates and not (game.options & OPTION_PLAIN):
6522                 prout("Commands with that prefix: " + " ".join(candidates))
6523             else:
6524                 listCommands()
6525                 continue
6526         if cmd == "SRSCAN":             # srscan
6527             srscan()
6528         elif cmd == "STATUS":           # status
6529             status()
6530         elif cmd == "REQUEST":          # status request 
6531             request()
6532         elif cmd == "LRSCAN":           # long range scan
6533             lrscan()
6534         elif cmd == "PHASERS":          # phasers
6535             phasers()
6536             if game.ididit:
6537                 hitme = True
6538         elif cmd == "TORPEDO":          # photon torpedos
6539             photon()
6540             if game.ididit:
6541                 hitme = True
6542         elif cmd == "MOVE":             # move under warp
6543             warp(False)
6544         elif cmd == "SHIELDS":          # shields
6545             doshield(False)
6546             if game.ididit:
6547                 hitme = True
6548                 game.shldchg = False
6549         elif cmd == "DOCK":             # dock at starbase
6550             dock(True)
6551             if game.ididit:
6552                 attack(False)           
6553         elif cmd == "DAMAGES":          # damage reports
6554             damagereport()
6555         elif cmd == "CHART":            # chart
6556             makechart()
6557         elif cmd == "IMPULSE":          # impulse
6558             impulse()
6559         elif cmd == "REST":             # rest
6560             os.wait()
6561             if game.ididit:
6562                 hitme = True
6563         elif cmd == "WARP":             # warp
6564             setwarp()
6565         elif cmd == "SCORE":            # score
6566             score()
6567         elif cmd == "SENSORS":          # sensors
6568             sensor()
6569         elif cmd == "ORBIT":            # orbit
6570             orbit()
6571             if game.ididit:
6572                 hitme = True
6573         elif cmd == "TRANSPORT":                # transport "beam"
6574             beam()
6575         elif cmd == "MINE":             # mine
6576             mine()
6577             if game.ididit:
6578                 hitme = True
6579         elif cmd == "CRYSTALS":         # crystals
6580             usecrystals()
6581             if game.ididit:
6582                 hitme = True
6583         elif cmd == "SHUTTLE":          # shuttle
6584             shuttle()
6585             if game.ididit:
6586                 hitme = True
6587         elif cmd == "PLANETS":          # Planet list
6588             survey()
6589         elif cmd == "REPORT":           # Game Report 
6590             report()
6591         elif cmd == "COMPUTER":         # use COMPUTER!
6592             eta()
6593         elif cmd == "COMMANDS":
6594             listCommands()
6595         elif cmd == "EMEXIT":           # Emergency exit
6596             clrscr()                    # Hide screen
6597             freeze(True)                # forced save
6598             os.exit(1)                  # And quick exit
6599         elif cmd == "PROBE":
6600             probe()                     # Launch probe
6601             if game.ididit:
6602                 hitme = True
6603         elif cmd == "ABANDON":          # Abandon Ship
6604             abandon()
6605         elif cmd == "DESTRUCT":         # Self Destruct
6606             selfdestruct()
6607         elif cmd == "SAVE":             # Save Game
6608             freeze(False)
6609             clrscr()
6610             if game.skill > SKILL_GOOD:
6611                 prout(_("WARNING--Saved games produce no plaques!"))
6612         elif cmd == "DEATHRAY":         # Try a desparation measure
6613             deathray()
6614             if game.ididit:
6615                 hitme = True
6616         elif cmd == "DEBUGCMD":         # What do we want for debug???
6617             debugme()
6618         elif cmd == "MAYDAY":           # Call for help
6619             mayday()
6620             if game.ididit:
6621                 hitme = True
6622         elif cmd == "QUIT":
6623             game.alldone = True         # quit the game
6624         elif cmd == "HELP":
6625             helpme()                    # get help
6626         elif cmd == "SEED":             # set random-number seed
6627             key = scan()
6628             if key == IHREAL:
6629                 seed = int(round(aaitem))
6630 #ifdef BSD_BUG_FOR_BUG
6631 #       elif cmd == "VISUAL":
6632 #           visual()                    # perform visual scan
6633 #endif
6634         while True:
6635             if game.alldone:
6636                 break           # Game has ended
6637             if game.optime != 0.0:
6638                 events()
6639                 if game.alldone:
6640                     break       # Events did us in
6641             if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
6642                 atover(False)
6643                 continue
6644             if hitme and not game.justin:
6645                 attack(True)
6646                 if game.alldone:
6647                     break
6648                 if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
6649                     atover(False)
6650                     hitme = True
6651                     continue
6652             break
6653         if game.alldone:
6654             break
6655     if idebug:
6656         prout("=== Ending")
6657
6658 def cramen(cmd):
6659     # return an enemy 
6660     if   cmd == IHR: s = _("Romulan")
6661     elif cmd == IHK: s = _("Klingon")
6662     elif cmd == IHC: s = _("Commander")
6663     elif cmd == IHS: s = _("Super-commander")
6664     elif cmd == IHSTAR: s = _("Star")
6665     elif cmd == IHP: s = _("Planet")
6666     elif cmd == IHB: s = _("Starbase")
6667     elif cmd == IHBLANK: s = _("Black hole")
6668     elif cmd == IHT: s = _("Tholian")
6669     elif cmd == IHWEB: s = _("Tholian web")
6670     elif cmd == IHQUEST: s = _("Stranger")
6671     elif cmd == IHW: s = _("Inhabited World")
6672     else: s = "Unknown??"
6673     proutn(s)
6674
6675 def crmena(stars, enemy, loctype, w):
6676     # print an enemy and his location 
6677     if stars:
6678         proutn("***")
6679     cramen(enemy)
6680     proutn(_(" at "))
6681     if loctype == "quadrant":
6682         buf = _("Quadrant ")
6683     elif loctype == "sector":
6684         buf = _("Sector ")
6685     proutn(buf + `w`)
6686
6687 def crmshp():
6688     # print our ship name 
6689     if game.ship == IHE:
6690         s = _("Enterprise")
6691     elif game.ship == IHF:
6692         s = _("Faerie Queene")
6693     else:
6694         s = "Ship???"
6695     proutn(s)
6696
6697 def stars():
6698     # print a line of stars 
6699     prouts("******************************************************")
6700     skip(1)
6701
6702 def expran(avrage):
6703     return -avrage*math.log(1e-7 + random.random())
6704
6705 def randplace(size):
6706     # choose a random location  
6707     w = coord()
6708     w.x = random.randint(0, size-1) 
6709     w.y = random.randint(0, size-1)
6710     return w
6711
6712 def chew():
6713     # Demand input for next scan
6714     global inqueue
6715     inqueue = None
6716
6717 def chew2():
6718     # return IHEOL next time 
6719     global inqueue
6720     inqueue = []
6721
6722 def scan():
6723     # Get a token from the user
6724     global inqueue, line, citem, aaitem
6725     aaitem = 0.0
6726     citem = ''
6727
6728     # Read a line if nothing here
6729     if inqueue == None:
6730         line = cgetline()
6731         if curwnd==prompt_window:
6732             clrscr()
6733             setwnd(message_window)
6734             clrscr()
6735         # Skip leading white space
6736         line = line.lstrip()
6737         if line:
6738             inqueue = line.split()
6739         else:
6740             inqueue = []
6741             return IHEOL
6742     elif not inqueue:
6743         return IHEOL
6744     # From here on in it's all looking at the queue
6745     citem = inqueue.pop(0)
6746     if citem == IHEOL:
6747         return IHEOL
6748     try:
6749         aaitem = float(citem)
6750         return IHREAL
6751     except ValueError:
6752         pass
6753     # Treat as alpha
6754     citem = citem.lower()
6755     return IHALPHA
6756
6757 def ja():
6758     # yes-or-no confirmation 
6759     chew()
6760     while True:
6761         scan()
6762         chew()
6763         if citem == 'y':
6764             return True
6765         if citem == 'n':
6766             return False
6767         proutn(_("Please answer with \"y\" or \"n\": "))
6768
6769 def huh():
6770     # complain about unparseable input 
6771     chew()
6772     skip(1)
6773     prout(_("Beg your pardon, Captain?"))
6774
6775 def isit(s):
6776     # compares s to citem and returns true if it matches to the length of s
6777     return s.startswith(citem)
6778
6779 def debugme():
6780     # access to the internals for debugging 
6781     proutn("Reset levels? ")
6782     if ja() == True:
6783         if game.energy < game.inenrg:
6784             game.energy = game.inenrg
6785         game.shield = game.inshld
6786         game.torps = game.intorps
6787         game.lsupres = game.inlsr
6788     proutn("Reset damage? ")
6789     if ja() == True:
6790         for i in range(NDEVICES): 
6791             if game.damage[i] > 0.0: 
6792                 game.damage[i] = 0.0
6793     proutn("Toggle debug flag? ")
6794     if ja() == True:
6795         idebug = not idebug
6796         if idebug:
6797             prout("Debug output ON")        
6798         else:
6799             prout("Debug output OFF")
6800     proutn("Cause selective damage? ")
6801     if ja() == True:
6802         for i in range(NDEVICES):
6803             proutn("Kill ")
6804             proutn(device[i])
6805             proutn("? ")
6806             chew()
6807             key = scan()
6808             if key == IHALPHA and isit("y"):
6809                 game.damage[i] = 10.0
6810     proutn("Examine/change events? ")
6811     if ja() == True:
6812         ev = event()
6813         w = coord()
6814         legends = {
6815             FSNOVA:  "Supernova       ",
6816             FTBEAM:  "T Beam          ",
6817             FSNAP:   "Snapshot        ",
6818             FBATTAK: "Base Attack     ",
6819             FCDBAS:  "Base Destroy    ",
6820             FSCMOVE: "SC Move         ",
6821             FSCDBAS: "SC Base Destroy ",
6822             FDSPROB: "Probe Move      ",
6823             FDISTR:  "Distress Call   ",
6824             FENSLV:  "Enslavement     ",
6825             FREPRO:  "Klingon Build   ",
6826         }
6827         for i in range(1, NEVENTS):
6828             proutn(legends[i])
6829             if is_scheduled(i):
6830                 proutn("%.2f" % (scheduled(i)-game.state.date))
6831                 if i == FENSLV or i == FREPRO:
6832                     ev = findevent(i)
6833                     proutn(" in %s" % ev.quadrant)
6834             else:
6835                 proutn("never")
6836             proutn("? ")
6837             chew()
6838             key = scan()
6839             if key == 'n':
6840                 unschedule(i)
6841                 chew()
6842             elif key == IHREAL:
6843                 ev = schedule(i, aaitem)
6844                 if i == FENSLV or i == FREPRO:
6845                     chew()
6846                     proutn("In quadrant- ")
6847                     key = scan()
6848                     # IHEOL says to leave coordinates as they are 
6849                     if key != IHEOL:
6850                         if key != IHREAL:
6851                             prout("Event %d canceled, no x coordinate." % (i))
6852                             unschedule(i)
6853                             continue
6854                         w.x = int(round(aaitem))
6855                         key = scan()
6856                         if key != IHREAL:
6857                             prout("Event %d canceled, no y coordinate." % (i))
6858                             unschedule(i)
6859                             continue
6860                         w.y = int(round(aaitem))
6861                         ev.quadrant = w
6862         chew()
6863     proutn("Induce supernova here? ")
6864     if ja() == True:
6865         game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova = True
6866         atover(True)
6867
6868 if __name__ == '__main__':
6869     global line, thing, game, idebug, iqengry
6870     game = citem = aaitem = inqueue = None
6871     line = ''
6872     thing = coord()
6873     iqengry = False
6874     game = gamestate()
6875     idebug = 0
6876     game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_SHOWME | OPTION_PLAIN | OPTION_ALMY)
6877     # Disable curses mode until the game logic is working.
6878     #    if os.getenv("TERM"):
6879     #   game.options |= OPTION_CURSES | OPTION_SHOWME
6880     #    else:
6881     game.options |= OPTION_TTY
6882     seed = time.time()
6883     (options, arguments) = getopt.getopt(sys.argv[1:], "r:tx")
6884     for (switch, val) in options:
6885         if switch == '-r':
6886             try:
6887                 replayfp = open(optarg, "r")
6888             except IOError:
6889                 sys.stderr.write("sst: can't open replay file %s\n" % optarg)
6890                 os.exit(1)
6891             line = replayfp.readline().strip()
6892             try:
6893                 (key, seed) = line.split()
6894                 seed = int(seed)
6895             except ValueError:
6896                 sys.stderr.write("sst: replay file %s is ill-formed\n"%optarg)
6897                 os.exit(1)
6898             game.options |= OPTION_TTY
6899             game.options &=~ OPTION_CURSES
6900         elif switch == '-t':
6901             game.options |= OPTION_TTY
6902             game.options &=~ OPTION_CURSES
6903         elif switch == '-x':
6904             idebug = True
6905         else:
6906             sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n")
6907             os.exit(0)
6908     # where to save the input in case of bugs
6909     try:
6910         logfp = open("/usr/tmp/sst-input.log", "w")
6911     except IOError:
6912         sys.stderr.write("sst: warning, can't open logfile\n")
6913     if logfp:
6914         #setlinebuf(logfp)
6915         logfp.write("seed %d\n" % (seed))
6916     random.seed(seed)
6917     iostart()
6918     if arguments:
6919         inqueue = arguments
6920     else:
6921         inqueue = None
6922     while True: # Play a game 
6923         setwnd(fullscreen_window)
6924         clrscr()
6925         prelim()
6926         setup(needprompt=not inqueue)
6927         if game.alldone:
6928             score()
6929             game.alldone = False
6930         else:
6931             makemoves()
6932         skip(1)
6933         stars()
6934         skip(1)
6935         if game.tourn and game.alldone:
6936             proutn(_("Do you want your score recorded?"))
6937             if ja() == True:
6938                 chew2()
6939                 freeze(False)
6940         proutn(_("Do you want to play again? "))
6941         if not ja():
6942             break
6943     skip(1)
6944     prout(_("May the Great Bird of the Galaxy roost upon your home planet."))
6945     raise SysExit, 0