380c1a5a67b1c2d2b2333323f786bd308fef53c1
[super-star-trek.git] / src / sst.py
1 """
2 sst.py =-- Super Star Trek in Python
3
4 This code is a Python translation of a C translation of a FORTRAN original.
5 The FORTRANness still shows in many ways, notably the use of 1-origin index
6 an a lot of parallel arrays where a more modern language would use structures
7 or objects.
8 """
9 import os, sys, math, curses, time, atexit, readline
10
11 SSTDOC = "/usr/share/doc/sst/sst.doc"
12
13 # Stub to be replaced
14 def _(str): return str
15
16 PHASEFAC        = 2.0
17 GALSIZE         = 8
18 NINHAB          = (GALSIZE * GALSIZE / 2)
19 MAXUNINHAB      = 10
20 PLNETMAX        = (NINHAB + MAXUNINHAB)
21 QUADSIZE        = 10
22 BASEMAX         = (GALSIZE * GALSIZE / 12)
23 MAXKLGAME       = 127
24 MAXKLQUAD       = 9
25 FULLCREW        = 428   # BSD Trek was 387, that's wrong 
26 FOREVER         = 1e30
27
28 # These functions hide the difference between 0-origin and 1-origin addressing.
29 def VALID_QUADRANT(x, y):       return ((x)>=1 and (x)<=GALSIZE and (y)>=1 and (y)<=GALSIZE)
30 def VALID_SECTOR(x, y): return ((x)>=1 and (x)<=QUADSIZE and (y)>=1 and (y)<=QUADSIZE)
31
32 def square(i):          return ((i)*(i))
33 def distance(c1, c2):   return math.sqrt(square(c1.x - c2.x) + square(c1.y - c2.y))
34 def invalidate(w):      w.x = w.y = 0
35 def is_valid(w):        return (w.x != 0 and w.y != 0)
36
37 class coord:
38     def __init(self, x=None, y=None):
39         self.x = x
40         self.y = y
41     def invalidate(self):
42         self.x = self.y = None
43     def is_valid(self):
44         return self.x != None and self.y != None
45     def __eq__(self, other):
46         return self.x == other.y and self.x == other.y
47     def __add__(self, other):
48         return coord(self.x+self.x, self.y+self.y)
49     def __sub__(self, other):
50         return coord(self.x-self.x, self.y-self.y)
51     def distance(self, other):
52         return math.sqrt((self.x - other.x)**2 + (self.y - other.y)**2)
53     def sgn(self):
54         return coord(self.x / abs(x), self.y / abs(y));
55     def __hash__(self):
56         return hash((x, y))
57     def __str__(self):
58         return "%d - %d" % (self.x, self.y)
59
60 class planet:
61     def __init(self):
62         self.name = None        # string-valued if inhabited
63         self.w = coord()        # quadrant located
64         self.pclass = None      # could be ""M", "N", "O", or "destroyed"
65         self.crystals = None    # could be "mined", "present", "absent"
66         self.known = None       # could be "unknown", "known", "shuttle_down"
67
68 # How to represent features
69 IHR = 'R',
70 IHK = 'K',
71 IHC = 'C',
72 IHS = 'S',
73 IHSTAR = '*',
74 IHP = 'P',
75 IHW = '@',
76 IHB = 'B',
77 IHBLANK = ' ',
78 IHDOT = '.',
79 IHQUEST = '?',
80 IHE = 'E',
81 IHF = 'F',
82 IHT = 'T',
83 IHWEB = '#',
84 IHMATER0 = '-',
85 IHMATER1 = 'o',
86 IHMATER2 = '0'
87
88 NOPLANET = None
89 class quadrant:
90     def __init(self):
91         self.stars = None
92         self.planet = None
93         self.starbase = None
94         self.klingons = None
95         self.romulans = None
96         self.supernova = None
97         self.charted = None
98         self.status = None      # Could be "secure", "distressed", "enslaved"
99
100 class page:
101     def __init(self):
102         self.stars = None
103         self.starbase = None
104         self.klingons = None
105
106 class snapshot:
107     def __init(self):
108         self.snap = False       # snapshot taken
109         self.crew = None        # crew complement
110         self.remkl = None       # remaining klingons
111         self.remcom = None      # remaining commanders
112         self.nscrem = None      # remaining super commanders
113         self.rembase = None     # remaining bases
114         self.starkl = None      # destroyed stars
115         self.basekl = None      # destroyed bases
116         self.nromrem = None     # Romulans remaining
117         self.nplankl = None     # destroyed uninhabited planets
118         self.nworldkl = None    # destroyed inhabited planets
119         self.planets = []       # Planet information
120         for i in range(PLNETMAX):
121             self.planets.append(planet())
122         self.date = None        # stardate
123         self.remres = None      # remaining resources
124         self.remtime = None     # remaining time
125         self.baseq = []         # Base quadrant coordinates
126         for i in range(BASEMAX+1):
127             self.baseq.append(coord())
128         self.kcmdr = []         # Commander quadrant coordinates
129         for i in range(QUADSIZE+1):
130             self.kcmdr.append(coord())
131         self.kscmdr = coord()   # Supercommander quadrant coordinates
132         self.galaxy = []        # The Galaxy (subscript 0 not used)
133         for i in range(GALSIZE+1):
134             self.chart.append([])
135             for j in range(GALSIZE+1):
136                 self.galaxy[i].append(quadrant())
137         self.chart = []         # the starchart (subscript 0 not used)
138         for i in range(GALSIZE+1):
139             self.chart.append([])
140             for j in range(GALSIZE+1):
141                 self.chart[i].append(page())
142
143 class event:
144     def __init__(self):
145         self.date = None        # A real number
146         self.quadrant = None    # A coord structure
147
148 # game options 
149 OPTION_ALL      = 0xffffffff
150 OPTION_TTY      = 0x00000001    # old interface 
151 OPTION_CURSES   = 0x00000002    # new interface 
152 OPTION_IOMODES  = 0x00000003    # cover both interfaces 
153 OPTION_PLANETS  = 0x00000004    # planets and mining 
154 OPTION_THOLIAN  = 0x00000008    # Tholians and their webs 
155 OPTION_THINGY   = 0x00000010    # Space Thingy can shoot back 
156 OPTION_PROBE    = 0x00000020    # deep-space probes 
157 OPTION_SHOWME   = 0x00000040    # bracket Enterprise in chart 
158 OPTION_RAMMING  = 0x00000080    # enemies may ram Enterprise 
159 OPTION_MVBADDY  = 0x00000100    # more enemies can move 
160 OPTION_BLKHOLE  = 0x00000200    # black hole may timewarp you 
161 OPTION_BASE     = 0x00000400    # bases have good shields 
162 OPTION_WORLDS   = 0x00000800    # logic for inhabited worlds 
163 OPTION_PLAIN    = 0x01000000    # user chose plain game 
164 OPTION_ALMY     = 0x02000000    # user chose Almy variant 
165
166 # Define devices 
167 DSRSENS = 0
168 DLRSENS = 1
169 DPHASER = 2
170 DPHOTON = 3
171 DLIFSUP = 4
172 DWARPEN = 5
173 DIMPULS = 6
174 DSHIELD = 7
175 DRADIO  = 0
176 DSHUTTL = 9
177 DCOMPTR = 10
178 DNAVSYS = 11
179 DTRANSP = 12
180 DSHCTRL = 13
181 DDRAY   = 14
182 DDSP    = 15
183 NDEVICES= 16    # Number of devices
184
185 SKILL_NONE      = 0
186 SKILL_NOVICE    = 1
187 SKILL_FAIR      = 2
188 SKILL_GOOD      = 3
189 SKILL_EXPERT    = 4
190 SKILL_EMERITUS  = 5
191
192 def damaged(dev):       return (game.damage[dev] != 0.0)
193
194 # Define future events 
195 FSPY    = 0     # Spy event happens always (no future[] entry)
196                 # can cause SC to tractor beam Enterprise
197 FSNOVA  = 1     # Supernova
198 FTBEAM  = 2     # Commander tractor beams Enterprise
199 FSNAP   = 3     # Snapshot for time warp
200 FBATTAK = 4     # Commander attacks base
201 FCDBAS  = 5     # Commander destroys base
202 FSCMOVE = 6     # Supercommander moves (might attack base)
203 FSCDBAS = 7     # Supercommander destroys base
204 FDSPROB = 8     # Move deep space probe
205 FDISTR  = 9     # Emit distress call from an inhabited world 
206 FENSLV  = 10    # Inhabited word is enslaved */
207 FREPRO  = 11    # Klingons build a ship in an enslaved system
208 NEVENTS = 12
209
210 #
211 # abstract out the event handling -- underlying data structures will change
212 # when we implement stateful events
213
214 def findevent(evtype):  return game.future[evtype]
215
216 class gamestate:
217     def __init__(self):
218         self.options = None     # Game options
219         self.state = None       # A snapshot structure
220         self.snapsht = None     # Last snapshot taken for time-travel purposes
221         self.quad = [[IHDOT * (QUADSIZE+1)] * (QUADSIZE+1)]     # contents of our quadrant
222         self.kpower = [[0 * (QUADSIZE+1)] * (QUADSIZE+1)]       # enemy energy levels
223         self.kdist = [[0 * (QUADSIZE+1)] * (QUADSIZE+1)]        # enemy distances
224         self.kavgd = [[0 * (QUADSIZE+1)] * (QUADSIZE+1)]        # average distances
225         self.damage = [0] * NDEVICES    # damage encountered
226         self.future = [0.0] * NEVENTS   # future events
227         for i in range(NEVENTS):
228             self.future.append(event())
229         self.passwd  = None;            # Self Destruct password
230         self.ks = [[None * (QUADSIZE+1)] * (QUADSIZE+1)]        # enemy sector locations
231         self.quadrant = None    # where we are in the large
232         self.sector = None      # where we are in the small
233         self.tholian = None     # coordinates of Tholian
234         self.base = None        # position of base in current quadrant
235         self.battle = None      # base coordinates being attacked
236         self.plnet = None       # location of planet in quadrant
237         self.probec = None      # current probe quadrant
238         self.gamewon = False    # Finished!
239         self.ididit = False     # action taken -- allows enemy to attack
240         self.alive = False      # we are alive (not killed)
241         self.justin = False     # just entered quadrant
242         self.shldup = False     # shields are up
243         self.shldchg = False    # shield is changing (affects efficiency)
244         self.comhere = False    # commander here
245         self.ishere = False     # super-commander in quadrant
246         self.iscate = False     # super commander is here
247         self.ientesc = False    # attempted escape from supercommander
248         self.ithere = False     # Tholian is here 
249         self.resting = False    # rest time
250         self.icraft = False     # Kirk in Galileo
251         self.landed = False     # party on planet (true), on ship (false)
252         self.alldone = False    # game is now finished
253         self.neutz = False      # Romulan Neutral Zone
254         self.isarmed = False    # probe is armed
255         self.inorbit = False    # orbiting a planet
256         self.imine = False      # mining
257         self.icrystl = False    # dilithium crystals aboard
258         self.iseenit = False    # seen base attack report
259         self.thawed = False     # thawed game
260         self.condition = None   # "green", "yellow", "red", "docked", "dead"
261         self.iscraft = None     # "onship", "offship", "removed"
262         self.skill = None       # Player skill level
263         self.inkling = 0        # initial number of klingons
264         self.inbase = 0         # initial number of bases
265         self.incom = 0          # initial number of commanders
266         self.inscom = 0         # initial number of commanders
267         self.inrom = 0          # initial number of commanders
268         self.instar = 0         # initial stars
269         self.intorps = 0        # initial/max torpedoes
270         self.torps = 0          # number of torpedoes
271         self.ship = 0           # ship type -- 'E' is Enterprise
272         self.abandoned = 0      # count of crew abandoned in space
273         self.length = 0         # length of game
274         self.klhere = 0         # klingons here
275         self.casual = 0         # causalties
276         self.nhelp = 0          # calls for help
277         self.nkinks = 0         # count of energy-barrier crossings
278         self.iplnet = 0         # planet # in quadrant
279         self.inplan = 0         # initial planets
280         self.nenhere = 0        # number of enemies in quadrant
281         self.irhere = 0         # Romulans in quadrant
282         self.isatb = 0          # =1 if super commander is attacking base
283         self.tourn = 0          # tournament number
284         self.proben = 0         # number of moves for probe
285         self.nprobes = 0        # number of probes available
286         self.inresor = 0.0      # initial resources
287         self.intime = 0.0       # initial time
288         self.inenrg = 0.0       # initial/max energy
289         self.inshld = 0.0       # initial/max shield
290         self.inlsr = 0.0        # initial life support resources
291         self.indate = 0.0       # initial date
292         self.energy = 0.0       # energy level
293         self.shield = 0.0       # shield level
294         self.warpfac = 0.0      # warp speed
295         self.wfacsq = 0.0       # squared warp factor
296         self.lsupres = 0.0      # life support reserves
297         self.dist = 0.0         # movement distance
298         self.direc = 0.0        # movement direction
299         self.optime = 0.0       # time taken by current operation
300         self.docfac = 0.0       # repair factor when docking (constant?)
301         self.damfac = 0.0       # damage factor
302         self.lastchart = 0.0    # time star chart was last updated
303         self.cryprob = 0.0      # probability that crystal will work
304         self.probex = 0.0       # location of probe
305         self.probey = 0.0       #
306         self.probeinx = 0.0     # probe x,y increment
307         self.probeiny = 0.0     #
308         self.height = 0.0       # height of orbit around planet
309     def recompute(self):
310         # Stas thinks this should be (C expression): 
311         # game.state.remkl + game.state.remcom > 0 ?
312         #       game.state.remres/(game.state.remkl + 4*game.state.remcom) : 99
313         # He says the existing expression is prone to divide-by-zero errors
314         # after killing the last klingon when score is shown -- perhaps also
315         # if the only remaining klingon is SCOM.
316         game.state.remtime = game.state.remres/(game.state.remkl + 4*game.state.remcom)
317 # From enumerated type 'feature'
318 IHR = 'R'
319 IHK = 'K'
320 IHC = 'C'
321 IHS = 'S'
322 IHSTAR = '*'
323 IHP = 'P'
324 IHW = '@'
325 IHB = 'B'
326 IHBLANK = ' '
327 IHDOT = '.'
328 IHQUEST = '?'
329 IHE = 'E'
330 IHF = 'F'
331 IHT = 'T'
332 IHWEB = '#'
333 IHMATER0 = '-'
334 IHMATER1 = 'o'
335 IHMATER2 = '0'
336
337
338 # From enumerated type 'FINTYPE'
339 FWON = 0
340 FDEPLETE = 1
341 FLIFESUP = 2
342 FNRG = 3
343 FBATTLE = 4
344 FNEG3 = 5
345 FNOVA = 6
346 FSNOVAED = 7
347 FABANDN = 8
348 FDILITHIUM = 9
349 FMATERIALIZE = 10
350 FPHASER = 11
351 FLOST = 12
352 FMINING = 13
353 FDPLANET = 14
354 FPNOVA = 15
355 FSSC = 16
356 FSTRACTOR = 17
357 FDRAY = 18
358 FTRIBBLE = 19
359 FHOLE = 20
360 FCREW = 21
361
362 # From enumerated type 'COLORS'
363 DEFAULT = 0
364 BLACK = 1
365 BLUE = 2
366 GREEN = 3
367 CYAN = 4
368 RED = 5
369 MAGENTA = 6
370 BROWN = 7
371 LIGHTGRAY = 8
372 DARKGRAY = 9
373 LIGHTBLUE = 10
374 LIGHTGREEN = 11
375 LIGHTCYAN = 12
376 LIGHTRED = 13
377 LIGHTMAGENTA = 14
378 YELLOW = 15
379 WHITE = 16
380
381 # Code from ai.c begins here
382
383 def tryexit(look, ienm, loccom, irun):
384     # a bad guy attempts to bug out 
385     iq = coord()
386     iq.x = game.quadrant.x+(look.x+(QUADSIZE-1))/QUADSIZE - 1
387     iq.y = game.quadrant.y+(look.y+(QUADSIZE-1))/QUADSIZE - 1
388     if not VALID_QUADRANT(iq.x,iq.y) or \
389         game.state.galaxy[iq.x][iq.y].supernova or \
390         game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1:
391         return False; # no can do -- neg energy, supernovae, or >MAXKLQUAD-1 Klingons 
392     if ienm == IHR:
393         return False; # Romulans cannot escape! 
394     if not irun:
395         # avoid intruding on another commander's territory 
396         if ienm == IHC:
397             for n in range(1, game.state.remcom+1):
398                 if same(game.state.kcmdr[n],iq):
399                     return False
400             # refuse to leave if currently attacking starbase 
401             if same(game.battle, game.quadrant):
402                 return False
403         # don't leave if over 1000 units of energy 
404         if game.kpower[loccom] > 1000.0:
405             return False
406     # print escape message and move out of quadrant.
407     # we know this if either short or long range sensors are working
408     if not damaged(DSRSENS) or not damaged(DLRSENS) or \
409         game.condition == docked:
410         crmena(True, ienm, "sector", game.ks[loccom])
411         prout(_(" escapes to Quadrant %s (and regains strength).") % q)
412     # handle local matters related to escape 
413     game.quad[game.ks[loccom].x][game.ks[loccom].y] = IHDOT
414     game.ks[loccom] = game.ks[game.nenhere]
415     game.kavgd[loccom] = game.kavgd[game.nenhere]
416     game.kpower[loccom] = game.kpower[game.nenhere]
417     game.kdist[loccom] = game.kdist[game.nenhere]
418     game.klhere -= 1
419     game.nenhere -= 1
420     if game.condition != docked:
421         newcnd()
422     # Handle global matters related to escape 
423     game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons -= 1
424     game.state.galaxy[iq.x][iq.y].klingons += 1
425     if ienm==IHS:
426         game.ishere = False
427         game.iscate = False
428         game.ientesc = False
429         game.isatb = 0
430         schedule(FSCMOVE, 0.2777)
431         unschedule(FSCDBAS)
432         game.state.kscmdr=iq
433     else:
434         for n in range(1, game.state.remcom+1):
435             if same(game.state.kcmdr[n], game.quadrant):
436                 game.state.kcmdr[n]=iq
437                 break
438         game.comhere = False
439     return True; # success 
440
441 #
442 # The bad-guy movement algorithm:
443
444 # 1. Enterprise has "force" based on condition of phaser and photon torpedoes.
445 # If both are operating full strength, force is 1000. If both are damaged,
446 # force is -1000. Having shields down subtracts an additional 1000.
447
448 # 2. Enemy has forces equal to the energy of the attacker plus
449 # 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
450 # 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
451
452 # Attacker Initial energy levels (nominal):
453 # Klingon   Romulan   Commander   Super-Commander
454 # Novice    400        700        1200        
455 # Fair      425        750        1250
456 # Good      450        800        1300        1750
457 # Expert    475        850        1350        1875
458 # Emeritus  500        900        1400        2000
459 # VARIANCE   75        200         200         200
460
461 # Enemy vessels only move prior to their attack. In Novice - Good games
462 # only commanders move. In Expert games, all enemy vessels move if there
463 # is a commander present. In Emeritus games all enemy vessels move.
464
465 # 3. If Enterprise is not docked, an aggressive action is taken if enemy
466 # forces are 1000 greater than Enterprise.
467
468 # Agressive action on average cuts the distance between the ship and
469 # the enemy to 1/4 the original.
470
471 # 4.  At lower energy advantage, movement units are proportional to the
472 # advantage with a 650 advantage being to hold ground, 800 to move forward
473 # 1, 950 for two, 150 for back 4, etc. Variance of 100.
474
475 # If docked, is reduced by roughly 1.75*game.skill, generally forcing a
476 # retreat, especially at high skill levels.
477
478 # 5.  Motion is limited to skill level, except for SC hi-tailing it out.
479
480
481 def movebaddy(com, loccom, ienm):
482     # tactical movement for the bad guys 
483     next = coord(); look = coord()
484     irun = False
485     # This should probably be just game.comhere + game.ishere 
486     if game.skill >= SKILL_EXPERT:
487         nbaddys = ((game.comhere*2 + game.ishere*2+game.klhere*1.23+game.irhere*1.5)/2.0)
488     else:
489         nbaddys = game.comhere + game.ishere
490
491     dist1 = game.kdist[loccom]
492     mdist = int(dist1 + 0.5); # Nearest integer distance 
493
494     # If SC, check with spy to see if should hi-tail it 
495     if ienm==IHS and \
496         (game.kpower[loccom] <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
497         irun = True
498         motion = -QUADSIZE
499     else:
500         # decide whether to advance, retreat, or hold position 
501         forces = game.kpower[loccom]+100.0*game.nenhere+400*(nbaddys-1)
502         if not game.shldup:
503             forces += 1000; # Good for enemy if shield is down! 
504         if not damaged(DPHASER) or not damaged(DPHOTON):
505             if damaged(DPHASER): # phasers damaged 
506                 forces += 300.0
507             else:
508                 forces -= 0.2*(game.energy - 2500.0)
509             if damaged(DPHOTON): # photon torpedoes damaged 
510                 forces += 300.0
511             else:
512                 forces -= 50.0*game.torps
513         else:
514             # phasers and photon tubes both out! 
515             forces += 1000.0
516         motion = 0
517         if forces <= 1000.0 and game.condition != "docked": # Typical situation 
518             motion = ((forces+200.0*Rand())/150.0) - 5.0
519         else:
520             if forces > 1000.0: # Very strong -- move in for kill 
521                 motion = (1.0-square(Rand()))*dist1 + 1.0
522             if game.condition=="docked" and (game.options & OPTION_BASE): # protected by base -- back off ! 
523                 motion -= game.skill*(2.0-square(Rand()))
524         if idebug:
525             proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
526         # don't move if no motion 
527         if motion==0:
528             return
529         # Limit motion according to skill 
530         if abs(motion) > game.skill:
531             if motion < 0:
532                 motion = -game.skill
533             else:
534                 motion = game.skill
535     # calculate preferred number of steps 
536     if motion < 0:
537         msteps = -motion
538     else:
539         msteps = motion
540     if motion > 0 and nsteps > mdist:
541         nsteps = mdist; # don't overshoot 
542     if nsteps > QUADSIZE:
543         nsteps = QUADSIZE; # This shouldn't be necessary 
544     if nsteps < 1:
545         nsteps = 1; # This shouldn't be necessary 
546     if idebug:
547         proutn("NSTEPS = %d:" % nsteps)
548     # Compute preferred values of delta X and Y 
549     mx = game.sector.x - com.x
550     my = game.sector.y - com.y
551     if 2.0 * abs(mx) < abs(my):
552         mx = 0
553     if 2.0 * abs(my) < abs(game.sector.x-com.x):
554         my = 0
555     if mx != 0:
556         if mx*motion < 0:
557             mx = -1
558         else:
559             mx = 1
560     if my != 0:
561         if my*motion < 0:
562             my = -1
563         else:
564             my = 1
565     next = com
566     # main move loop 
567     for ll in range(nsteps):
568         if idebug:
569             proutn(" %d" % (ll+1))
570         # Check if preferred position available 
571         look.x = next.x + mx
572         look.y = next.y + my
573         if mx < 0:
574             krawlx = 1
575         else:
576             krawlx = -1
577         if my < 0:
578             krawly = 1
579         else:
580             krawly = -1
581         success = False
582         attempts = 0; # Settle mysterious hang problem 
583         while attempts < 20 and not success:
584             attempts += 1
585             if look.x < 1 or look.x > QUADSIZE:
586                 if motion < 0 and tryexit(look, ienm, loccom, irun):
587                     return
588                 if krawlx == mx or my == 0:
589                     break
590                 look.x = next.x + krawlx
591                 krawlx = -krawlx
592             elif look.y < 1 or look.y > QUADSIZE:
593                 if motion < 0 and tryexit(look, ienm, loccom, irun):
594                     return
595                 if krawly == my or mx == 0:
596                     break
597                 look.y = next.y + krawly
598                 krawly = -krawly
599             elif (game.options & OPTION_RAMMING) and game.quad[look.x][look.y] != IHDOT:
600                 # See if we should ram ship 
601                 if game.quad[look.x][look.y] == game.ship and \
602                     (ienm == IHC or ienm == IHS):
603                     ram(True, ienm, com)
604                     return
605                 if krawlx != mx and my != 0:
606                     look.x = next.x + krawlx
607                     krawlx = -krawlx
608                 elif krawly != my and mx != 0:
609                     look.y = next.y + krawly
610                     krawly = -krawly
611                 else:
612                     break; # we have failed 
613             else:
614                 success = True
615         if success:
616             next = look
617             if idebug:
618                 proutn(`next`)
619         else:
620             break; # done early 
621         
622     if idebug:
623         skip(1)
624     # Put commander in place within same quadrant 
625     game.quad[com.x][com.y] = IHDOT
626     game.quad[next.x][next.y] = ienm
627     if not same(next, com):
628         # it moved 
629         game.ks[loccom] = next
630         game.kdist[loccom] = game.kavgd[loccom] = distance(game.sector, next)
631         if not damaged(DSRSENS) or game.condition == docked:
632             proutn("***")
633             cramen(ienm)
634             proutn(_(" from Sector %s") % com)
635             if game.kdist[loccom] < dist1:
636                 proutn(_(" advances to "))
637             else:
638                 proutn(_(" retreats to "))
639             prout("Sector %s." % next)
640
641 def moveklings():
642     # Klingon tactical movement 
643     if idebug:
644         prout("== MOVCOM")
645     # Figure out which Klingon is the commander (or Supercommander)
646     # and do move
647     if game.comhere:
648         for i in range(1, game.nenhere+1):
649             w = game.ks[i]
650             if game.quad[w.x][w.y] == IHC:
651                 movebaddy(w, i, IHC)
652                 break
653     if game.ishere:
654         for i in range(1, game.nenhere+1):
655             w = game.ks[i]
656             if game.quad[w.x][w.y] == IHS:
657                 movebaddy(w, i, IHS)
658                 break
659     # If skill level is high, move other Klingons and Romulans too!
660     # Move these last so they can base their actions on what the
661     # commander(s) do.
662     if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
663         for i in range(1, game.nenhere+1):
664             w = game.ks[i]
665             if game.quad[w.x][w.y] == IHK or game.quad[w.x][w.y] == IHR:
666                 movebaddy(w, i, game.quad[w.x][w.y])
667     sortklings();
668
669 def movescom(iq, avoid):
670     # commander movement helper 
671     if same(iq, game.quadrant) or not VALID_QUADRANT(iq.x, iq.y) or \
672         game.state.galaxy[iq.x][iq.y].supernova or \
673         game.state.galaxy[iq.x][iq.y].klingons > MAXKLQUAD-1:
674         return 1
675     if avoid:
676         # Avoid quadrants with bases if we want to avoid Enterprise 
677         for i in range(1, game.state.rembase+1):
678             if same(game.state.baseq[i], iq):
679                 return True
680     if game.justin and not game.iscate:
681         return True
682     # do the move 
683     game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons -= 1
684     game.state.kscmdr = iq
685     game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].klingons += 1
686     if game.ishere:
687         # SC has scooted, Remove him from current quadrant 
688         game.iscate=False
689         game.isatb=0
690         game.ishere = False
691         game.ientesc = False
692         unschedule(FSCDBAS)
693         for i in range(1, game.nenhere+1):
694             if game.quad[game.ks[i].x][game.ks[i].y] == IHS:
695                 break
696         game.quad[game.ks[i].x][game.ks[i].y] = IHDOT
697         game.ks[i] = game.ks[game.nenhere]
698         game.kdist[i] = game.kdist[game.nenhere]
699         game.kavgd[i] = game.kavgd[game.nenhere]
700         game.kpower[i] = game.kpower[game.nenhere]
701         game.klhere -= 1
702         game.nenhere -= 1
703         if game.condition!=docked:
704             newcnd()
705         sortklings()
706     # check for a helpful planet 
707     for i in range(game.inplan):
708         if same(game.state.planets[i].w, game.state.kscmdr) and \
709             game.state.planets[i].crystals == present:
710             # destroy the planet 
711             game.state.planets[i].pclass = destroyed
712             game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].planet = NOPLANET
713             if not damaged(DRADIO) or game.condition == docked:
714                 announce()
715                 prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"))
716                 proutn(_("   a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
717                 prout(_("   by the Super-commander.\""))
718             break
719     return False; # looks good! 
720                         
721 def supercommander():
722     # move the Super Commander 
723     iq = coord(); sc = coord(); ibq = coord(); idelta = coord()
724     basetbl = []
725     if idebug:
726         prout("== SUPERCOMMANDER")
727     # Decide on being active or passive 
728     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 \
729             (game.state.date-game.indate) < 3.0)
730     if not game.iscate and avoid:
731         # compute move away from Enterprise 
732         idelta = game.state.kscmdr-game.quadrant
733         if math.sqrt(idelta.x*idelta.x+idelta.y*idelta.y) > 2.0:
734             # circulate in space 
735             idelta.x = game.state.kscmdr.y-game.quadrant.y
736             idelta.y = game.quadrant.x-game.state.kscmdr.x
737     else:
738         # compute distances to starbases 
739         if game.state.rembase <= 0:
740             # nothing left to do 
741             unschedule(FSCMOVE)
742             return
743         sc = game.state.kscmdr
744         for i in range(1, game.state.rembase+1):
745             basetbl.append((i, distance(game.state.baseq[i], sc)))
746         if game.state.rembase > 1:
747             basetbl.sort(lambda x, y: cmp(x[1]. y[1]))
748         # look for nearest base without a commander, no Enterprise, and
749         # without too many Klingons, and not already under attack. 
750         ifindit = iwhichb = 0
751         for i2 in range(1, game.state.rembase+1):
752             i = basetbl[i2][0]; # bug in original had it not finding nearest
753             ibq = game.state.baseq[i]
754             if same(ibq, game.quadrant) or same(ibq, game.battle) or \
755                 game.state.galaxy[ibq.x][ibq.y].supernova or \
756                 game.state.galaxy[ibq.x][ibq.y].klingons > MAXKLQUAD-1:
757                 continue
758             # if there is a commander, and no other base is appropriate,
759             #   we will take the one with the commander
760             for j in range(1, game.state.remcom+1):
761                 if same(ibq, game.state.kcmdr[j]) and ifindit!= 2:
762                     ifindit = 2
763                     iwhichb = i
764                     break
765             if j > game.state.remcom: # no commander -- use this one 
766                 ifindit = 1
767                 iwhichb = i
768                 break
769         if ifindit==0:
770             return; # Nothing suitable -- wait until next time
771         ibq = game.state.baseq[iwhichb]
772         # decide how to move toward base 
773         idelta = ibq - game.state.kscmdr
774     # Maximum movement is 1 quadrant in either or both axes 
775     idelta = idelta.sgn()
776     # try moving in both x and y directions
777     # there was what looked like a bug in the Almy C code here,
778     # but it might be this translation is just wrong.
779     iq = game.state.kscmdr + idelta
780     if movescom(iq, avoid):
781         # failed -- try some other maneuvers 
782         if idelta.x==0 or idelta.y==0:
783             # attempt angle move 
784             if idelta.x != 0:
785                 iq.y = game.state.kscmdr.y + 1
786                 if movescom(iq, avoid):
787                     iq.y = game.state.kscmdr.y - 1
788                     movescom(iq, avoid)
789             else:
790                 iq.x = game.state.kscmdr.x + 1
791                 if movescom(iq, avoid):
792                     iq.x = game.state.kscmdr.x - 1
793                     movescom(iq, avoid)
794         else:
795             # try moving just in x or y 
796             iq.y = game.state.kscmdr.y
797             if movescom(iq, avoid):
798                 iq.y = game.state.kscmdr.y + idelta.y
799                 iq.x = game.state.kscmdr.x
800                 movescom(iq, avoid)
801     # check for a base 
802     if game.state.rembase == 0:
803         unschedule(FSCMOVE)
804     else:
805         for i in range(1, game.state.rembase+1):
806             ibq = game.state.baseq[i]
807             if same(ibq, game.state.kscmdr) and same(game.state.kscmdr, game.battle):
808                 # attack the base 
809                 if avoid:
810                     return; # no, don't attack base! 
811                 game.iseenit = False
812                 game.isatb = 1
813                 schedule(FSCDBAS, 1.0 +2.0*Rand())
814                 if is_scheduled(FCDBAS):
815                     postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date)
816                 if damaged(DRADIO) and game.condition != docked:
817                     return; # no warning 
818                 game.iseenit = True
819                 announce()
820                 prout(_("Lt. Uhura-  \"Captain, the starbase in Quadrant %s") \
821                       % game.state.kscmdr)
822                 prout(_("   reports that it is under attack from the Klingon Super-commander."))
823                 proutn(_("   It can survive until stardate %d.\"") \
824                        % int(scheduled(FSCDBAS)))
825                 if not game.resting:
826                     return
827                 prout(_("Mr. Spock-  \"Captain, shall we cancel the rest period?\""))
828                 if ja() == False:
829                     return
830                 game.resting = False
831                 game.optime = 0.0; # actually finished 
832                 return
833     # Check for intelligence report 
834     if not idebug and \
835         (Rand() > 0.2 or \
836          (damaged(DRADIO) and game.condition != docked) or \
837          not game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].charted):
838         return
839     announce()
840     prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"))
841     proutn(_("   the Super-commander is in Quadrant %s,") % game.state.kscmdr)
842     return;
843
844 def movetholian():
845     # move the Tholian 
846     if not game.ithere or game.justin:
847         return
848
849     if game.tholian.x == 1 and game.tholian.y == 1:
850         idx = 1; idy = QUADSIZE
851     elif game.tholian.x == 1 and game.tholian.y == QUADSIZE:
852         idx = QUADSIZE; idy = QUADSIZE
853     elif game.tholian.x == QUADSIZE and game.tholian.y == QUADSIZE:
854         idx = QUADSIZE; idy = 1
855     elif game.tholian.x == QUADSIZE and game.tholian.y == 1:
856         idx = 1; idy = 1
857     else:
858         # something is wrong! 
859         game.ithere = False
860         return
861
862     # do nothing if we are blocked 
863     if game.quad[idx][idy]!= IHDOT and game.quad[idx][idy]!= IHWEB:
864         return
865     game.quad[game.tholian.x][game.tholian.y] = IHWEB
866
867     if game.tholian.x != idx:
868         # move in x axis 
869         im = math.fabs(idx - game.tholian.x)*1.0/(idx - game.tholian.x)
870         while game.tholian.x != idx:
871             game.tholian.x += im
872             if game.quad[game.tholian.x][game.tholian.y]==IHDOT:
873                 game.quad[game.tholian.x][game.tholian.y] = IHWEB
874     elif game.tholian.y != idy:
875         # move in y axis 
876         im = math.fabs(idy - game.tholian.y)*1.0/(idy - game.tholian.y)
877         while game.tholian.y != idy:
878             game.tholian.y += im
879             if game.quad[game.tholian.x][game.tholian.y]==IHDOT:
880                 game.quad[game.tholian.x][game.tholian.y] = IHWEB
881     game.quad[game.tholian.x][game.tholian.y] = IHT
882     game.ks[game.nenhere] = game.tholian
883
884     # check to see if all holes plugged 
885     for i in range(1, QUADSIZE+1):
886         if game.quad[1][i]!=IHWEB and game.quad[1][i]!=IHT:
887             return
888         if game.quad[QUADSIZE][i]!=IHWEB and game.quad[QUADSIZE][i]!=IHT:
889             return
890         if game.quad[i][1]!=IHWEB and game.quad[i][1]!=IHT:
891             return
892         if game.quad[i][QUADSIZE]!=IHWEB and game.quad[i][QUADSIZE]!=IHT:
893             return
894     # All plugged up -- Tholian splits 
895     game.quad[game.tholian.x][game.tholian.y]=IHWEB
896     dropin(IHBLANK)
897     crmena(True, IHT, "sector", game.tholian)
898     prout(_(" completes web."))
899     game.ithere = False
900     game.nenhere -= 1
901     return
902
903 # Code from battle.c begins here
904
905 def doshield(shraise):
906     # change shield status 
907     action = "NONE"
908     game.ididit = False
909     if shraise:
910         action = "SHUP"
911     else:
912         key = scan()
913         if key == IHALPHA:
914             if isit("transfer"):
915                 action = "NRG"
916             else:
917                 chew()
918                 if damaged(DSHIELD):
919                     prout(_("Shields damaged and down."))
920                     return
921                 if isit("up"):
922                     action = "SHUP"
923                 elif isit("down"):
924                     action = "SHDN"
925         if action=="NONE":
926             proutn(_("Do you wish to change shield energy? "))
927             if ja() == True:
928                 proutn(_("Energy to transfer to shields- "))
929                 action = "NRG"
930             elif damaged(DSHIELD):
931                 prout(_("Shields damaged and down."))
932                 return
933             elif game.shldup:
934                 proutn(_("Shields are up. Do you want them down? "))
935                 if ja() == True:
936                     action = "SHDN"
937                 else:
938                     chew()
939                     return
940             else:
941                 proutn(_("Shields are down. Do you want them up? "))
942                 if ja() == True:
943                     action = "SHUP"
944                 else:
945                     chew()
946                     return    
947     if action == "SHUP": # raise shields 
948         if game.shldup:
949             prout(_("Shields already up."))
950             return
951         game.shldup = True
952         game.shldchg = True
953         if game.condition != "docked":
954             game.energy -= 50.0
955         prout(_("Shields raised."))
956         if game.energy <= 0:
957             skip(1)
958             prout(_("Shields raising uses up last of energy."))
959             finish(FNRG)
960             return
961         game.ididit=True
962         return
963     elif action == "SHDN":
964         if not game.shldup:
965             prout(_("Shields already down."))
966             return
967         game.shldup=False
968         game.shldchg=True
969         prout(_("Shields lowered."))
970         game.ididit = True
971         return
972     elif action == "NRG":
973         while scan() != IHREAL:
974             chew()
975             proutn(_("Energy to transfer to shields- "))
976         chew()
977         if aaitem==0:
978             return
979         if aaitem > game.energy:
980             prout(_("Insufficient ship energy."))
981             return
982         game.ididit = True
983         if game.shield+aaitem >= game.inshld:
984             prout(_("Shield energy maximized."))
985             if game.shield+aaitem > game.inshld:
986                 prout(_("Excess energy requested returned to ship energy"))
987             game.energy -= game.inshld-game.shield
988             game.shield = game.inshld
989             return
990         if aaitem < 0.0 and game.energy-aaitem > game.inenrg:
991             # Prevent shield drain loophole 
992             skip(1)
993             prout(_("Engineering to bridge--"))
994             prout(_("  Scott here. Power circuit problem, Captain."))
995             prout(_("  I can't drain the shields."))
996             game.ididit = False
997             return
998         if game.shield+aaitem < 0:
999             prout(_("All shield energy transferred to ship."))
1000             game.energy += game.shield
1001             game.shield = 0.0
1002             return
1003         proutn(_("Scotty- \""))
1004         if aaitem > 0:
1005             prout(_("Transferring energy to shields.\""))
1006         else:
1007             prout(_("Draining energy from shields.\""))
1008         game.shield += aaitem
1009         game.energy -= aaitem
1010         return
1011
1012 def randdevice():
1013     # choose a device to damage, at random. 
1014     #
1015     # Quoth Eric Allman in the code of BSD-Trek:
1016     # "Under certain conditions you can get a critical hit.  This
1017     # sort of hit damages devices.  The probability that a given
1018     # device is damaged depends on the device.  Well protected
1019     # devices (such as the computer, which is in the core of the
1020     # ship and has considerable redundancy) almost never get
1021     # damaged, whereas devices which are exposed (such as the
1022     # warp engines) or which are particularly delicate (such as
1023     # the transporter) have a much higher probability of being
1024     # damaged."
1025     # 
1026     # This is one place where OPTION_PLAIN does not restore the
1027     # original behavior, which was equiprobable damage across
1028     # all devices.  If we wanted that, we'd return NDEVICES*Rand()
1029     # and have done with it.  Also, in the original game, DNAVYS
1030     # and DCOMPTR were the same device. 
1031     # 
1032     # Instead, we use a table of weights similar to the one from BSD Trek.
1033     # BSD doesn't have the shuttle, shield controller, death ray, or probes. 
1034     # We don't have a cloaking device.  The shuttle got the allocation
1035     # for the cloaking device, then we shaved a half-percent off
1036     # everything to have some weight to give DSHCTRL/DDRAY/DDSP.
1037     # 
1038     weights = (
1039         105,    # DSRSENS: short range scanners 10.5% 
1040         105,    # DLRSENS: long range scanners          10.5% 
1041         120,    # DPHASER: phasers                      12.0% 
1042         120,    # DPHOTON: photon torpedoes             12.0% 
1043         25,     # DLIFSUP: life support          2.5% 
1044         65,     # DWARPEN: warp drive                    6.5% 
1045         70,     # DIMPULS: impulse engines               6.5% 
1046         145,    # DSHIELD: deflector shields            14.5% 
1047         30,     # DRADIO:  subspace radio                3.0% 
1048         45,     # DSHUTTL: shuttle                       4.5% 
1049         15,     # DCOMPTR: computer                      1.5% 
1050         20,     # NAVCOMP: navigation system             2.0% 
1051         75,     # DTRANSP: transporter                   7.5% 
1052         20,     # DSHCTRL: high-speed shield controller 2.0% 
1053         10,     # DDRAY: death ray                       1.0% 
1054         30,     # DDSP: deep-space probes                3.0% 
1055     )
1056     idx = Rand() * 1000.0       # weights must sum to 1000 
1057     sum = 0
1058     for (i, w) in enumerate(weights):
1059         sum += w
1060         if idx < sum:
1061             return i
1062     return None;        # we should never get here
1063
1064 def ram(ibumpd, ienm, w):
1065     # make our ship ram something 
1066     prouts(_("***RED ALERT!  RED ALERT!"))
1067     skip(1)
1068     prout(_("***COLLISION IMMINENT."))
1069     skip(2)
1070     proutn("***")
1071     crmshp()
1072     hardness = {IHR:1.5, IHC:2.0, IHS:2.5, IHT:0.5, IHQUEST:4.0}.get(ienm, 1.0)
1073     if ibumpd:
1074         proutn(_(" rammed by "))
1075     else:
1076         proutn(_(" rams "))
1077     crmena(False, ienm, sector, w)
1078     if ibumpd:
1079         proutn(_(" (original position)"))
1080     skip(1)
1081     deadkl(w, ienm, game.sector)
1082     proutn("***")
1083     crmshp()
1084     prout(_(" heavily damaged."))
1085     icas = 10.0+20.0*Rand()
1086     prout(_("***Sickbay reports %d casualties"), icas)
1087     game.casual += icas
1088     game.state.crew -= icas
1089     #
1090     # In the pre-SST2K version, all devices got equiprobably damaged,
1091     # which was silly.  Instead, pick up to half the devices at
1092     # random according to our weighting table,
1093     # 
1094     ncrits = Rand() * (NDEVICES/2)
1095     for m in range(ncrits):
1096         dev = randdevice()
1097         if game.damage[dev] < 0:
1098             continue
1099         extradm = (10.0*hardness*Rand()+1.0)*game.damfac
1100         # Damage for at least time of travel! 
1101         game.damage[dev] += game.optime + extradm
1102     game.shldup = False
1103     prout(_("***Shields are down."))
1104     if game.state.remkl + game.state.remcom + game.state.nscrem:
1105         announce()
1106         damagereport()
1107     else:
1108         finish(FWON)
1109     return;
1110
1111 def torpedo(course, r, incoming, i, n):
1112     # let a photon torpedo fly 
1113     iquad = 0
1114     shoved = False
1115     ac = course + 0.25*r
1116     angle = (15.0-ac)*0.5235988
1117     bullseye = (15.0 - course)*0.5235988
1118     deltax = -math.sin(angle);
1119     deltay = math.cos(angle);
1120     x = incoming.x; y = incoming.y
1121     w = coord(); jw = coord()
1122     w.x = w.y = jw.x = jw.y = 0
1123     bigger = max(math.fabs(deltax), math.fabs(deltay))
1124     deltax /= bigger
1125     deltay /= bigger
1126     if not damaged(DSRSENS) or game.condition=="docked":
1127         setwnd(srscan_window)
1128     else: 
1129         setwnd(message_window)
1130     # Loop to move a single torpedo 
1131     for l in range(1, 15+1):
1132         x += deltax
1133         w.x = x + 0.5
1134         y += deltay
1135         w.y = y + 0.5
1136         if not VALID_SECTOR(w.x, w.y):
1137             break
1138         iquad=game.quad[w.x][w.y]
1139         tracktorpedo(w, l, i, n, iquad)
1140         if iquad==IHDOT:
1141             continue
1142         # hit something 
1143         setwnd(message_window)
1144         if damaged(DSRSENS) and not game.condition=="docked":
1145             skip(1);    # start new line after text track 
1146         if iquad in (IHE, IHF): # Hit our ship 
1147             skip(1)
1148             proutn(_("Torpedo hits "))
1149             crmshp()
1150             prout(".")
1151             hit = 700.0 + 100.0*Rand() - \
1152                 1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
1153             newcnd(); # we're blown out of dock 
1154             # We may be displaced. 
1155             if game.landed or game.condition=="docked":
1156                 return hit # Cheat if on a planet 
1157             ang = angle + 2.5*(Rand()-0.5)
1158             temp = math.fabs(math.sin(ang))
1159             if math.fabs(math.cos(ang)) > temp:
1160                 temp = math.fabs(math.cos(ang))
1161             xx = -math.sin(ang)/temp
1162             yy = math.cos(ang)/temp
1163             jw.x=w.x+xx+0.5
1164             jw.y=w.y+yy+0.5
1165             if not VALID_SECTOR(jw.x, jw.y):
1166                 return hit
1167             if game.quad[jw.x][jw.y]==IHBLANK:
1168                 finish(FHOLE)
1169                 return hit
1170             if game.quad[jw.x][jw.y]!=IHDOT:
1171                 # can't move into object 
1172                 return hit
1173             game.sector = jw
1174             crmshp()
1175             shoved = True
1176         elif iquad in (IHC, IHS): # Hit a commander 
1177             if Rand() <= 0.05:
1178                 crmena(True, iquad, sector, w)
1179                 prout(_(" uses anti-photon device;"))
1180                 prout(_("   torpedo neutralized."))
1181                 return None
1182         elif iquad in (IHR, IHK): # Hit a regular enemy 
1183             # find the enemy 
1184             for ll in range(1, game.nenhere+1):
1185                 if same(w, game.ks[ll]):
1186                     break
1187             kp = math.fabs(game.kpower[ll])
1188             h1 = 700.0 + 100.0*Rand() - \
1189                 1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
1190             h1 = math.fabs(h1)
1191             if kp < h1:
1192                 h1 = kp
1193             if game.kpower[ll] < 0:
1194                 game.kpower[ll] -= -h1
1195             else:
1196                 game.kpower[ll] -= h1
1197             if game.kpower[ll] == 0:
1198                 deadkl(w, iquad, w)
1199                 return None
1200             crmena(True, iquad, "sector", w)
1201             # If enemy damaged but not destroyed, try to displace 
1202             ang = angle + 2.5*(Rand()-0.5)
1203             temp = math.fabs(math.sin(ang))
1204             if math.fabs(math.cos(ang)) > temp:
1205                 temp = math.fabs(math.cos(ang))
1206             xx = -math.sin(ang)/temp
1207             yy = math.cos(ang)/temp
1208             jw.x=w.x+xx+0.5
1209             jw.y=w.y+yy+0.5
1210             if not VALID_SECTOR(jw.x, jw.y):
1211                 prout(_(" damaged but not destroyed."))
1212                 return
1213             if game.quad[jw.x][jw.y]==IHBLANK:
1214                 prout(_(" buffeted into black hole."))
1215                 deadkl(w, iquad, jw)
1216                 return None
1217             if game.quad[jw.x][jw.y]!=IHDOT:
1218                 # can't move into object 
1219                 prout(_(" damaged but not destroyed."))
1220                 return None
1221             proutn(_(" damaged--"))
1222             game.ks[ll] = jw
1223             shoved = True
1224             break
1225         elif iquad == IHB: # Hit a base 
1226             skip(1)
1227             prout(_("***STARBASE DESTROYED.."))
1228             for ll in range(1, game.state.rembase+1):
1229                 if same(game.state.baseq[ll], game.quadrant):
1230                     game.state.baseq[ll]=game.state.baseq[game.state.rembase]
1231                     break
1232             game.quad[w.x][w.y]=IHDOT
1233             game.state.rembase -= 1
1234             game.base.x=game.base.y=0
1235             game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase -= 1
1236             game.state.chart[game.quadrant.x][game.quadrant.y].starbase -= 1
1237             game.state.basekl += 1
1238             newcnd()
1239             return None
1240         elif iquad == IHP: # Hit a planet 
1241             crmena(True, iquad, sector, w)
1242             prout(_(" destroyed."))
1243             game.state.nplankl += 1
1244             game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NOPLANET
1245             game.state.planets[game.iplnet].pclass = destroyed
1246             game.iplnet = 0
1247             invalidate(game.plnet)
1248             game.quad[w.x][w.y] = IHDOT
1249             if game.landed:
1250                 # captain perishes on planet 
1251                 finish(FDPLANET)
1252             return None
1253         elif iquad == IHW: # Hit an inhabited world -- very bad! 
1254             crmena(True, iquad, sector, w)
1255             prout(_(" destroyed."))
1256             game.state.nworldkl += 1
1257             game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NOPLANET
1258             game.state.planets[game.iplnet].pclass = destroyed
1259             game.iplnet = 0
1260             invalidate(game.plnet)
1261             game.quad[w.x][w.y] = IHDOT
1262             if game.landed:
1263                 # captain perishes on planet 
1264                 finish(FDPLANET)
1265             prout(_("You have just destroyed an inhabited planet."))
1266             prout(_("Celebratory rallies are being held on the Klingon homeworld."))
1267             return None
1268         elif iquad == IHSTAR: # Hit a star 
1269             if Rand() > 0.10:
1270                 nova(w)
1271                 return None
1272             crmena(True, IHSTAR, sector, w)
1273             prout(_(" unaffected by photon blast."))
1274             return None
1275         elif iquad == IHQUEST: # Hit a thingy 
1276             if not (game.options & OPTION_THINGY) or Rand()>0.7:
1277                 skip(1)
1278                 prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
1279                 skip(1)
1280                 prouts(_("    HACK!     HACK!    HACK!        *CHOKE!*  "))
1281                 skip(1)
1282                 proutn(_("Mr. Spock-"))
1283                 prouts(_("  \"Fascinating!\""))
1284                 skip(1)
1285                 deadkl(w, iquad, w)
1286             else:
1287                 #
1288                 # Stas Sergeev added the possibility that
1289                 # you can shove the Thingy and piss it off.
1290                 # It then becomes an enemy and may fire at you.
1291                 # 
1292                 iqengry = True
1293                 shoved = True
1294             return None
1295         elif iquad == IHBLANK: # Black hole 
1296             skip(1)
1297             crmena(True, IHBLANK, sector, w)
1298             prout(_(" swallows torpedo."))
1299             return None
1300         elif iquad == IHWEB: # hit the web 
1301             skip(1)
1302             prout(_("***Torpedo absorbed by Tholian web."))
1303             return None
1304         elif iquad == IHT:  # Hit a Tholian 
1305             h1 = 700.0 + 100.0*Rand() - \
1306                 1000.0 * distance(w, incoming) * math.fabs(math.sin(bullseye-angle))
1307             h1 = math.fabs(h1)
1308             if h1 >= 600:
1309                 game.quad[w.x][w.y] = IHDOT
1310                 game.ithere = False
1311                 deadkl(w, iquad, w)
1312                 return None
1313             skip(1)
1314             crmena(True, IHT, sector, w)
1315             if Rand() > 0.05:
1316                 prout(_(" survives photon blast."))
1317                 return None
1318             prout(_(" disappears."))
1319             game.quad[w.x][w.y] = IHWEB
1320             game.ithere = False
1321             game.nenhere -= 1
1322             dropin(IHBLANK)
1323             return None
1324         else: # Problem!
1325             skip(1)
1326             proutn("Don't know how to handle torpedo collision with ")
1327             crmena(True, iquad, sector, w)
1328             skip(1)
1329             return None
1330         break
1331     if curwnd!=message_window:
1332         setwnd(message_window)
1333     if shoved:
1334         game.quad[w.x][w.y]=IHDOT
1335         game.quad[jw.x][jw.y]=iquad
1336         prout(_(" displaced by blast to Sector %s ") % jw)
1337         for ll in range(1, game.nenhere+1):
1338             game.kdist[ll] = game.kavgd[ll] = distance(game.sector,game.ks[ll])
1339         sortklings()
1340         return None
1341     skip(1)
1342     prout(_("Torpedo missed."))
1343     return None;
1344
1345 def fry(hit):
1346     # critical-hit resolution 
1347     ktr=1
1348     # a critical hit occured 
1349     if hit < (275.0-25.0*game.skill)*(1.0+0.5*Rand()):
1350         return
1351
1352     ncrit = 1.0 + hit/(500.0+100.0*Rand())
1353     proutn(_("***CRITICAL HIT--"))
1354     # Select devices and cause damage
1355     cdam = []
1356     for loop1 in range(ncrit):
1357         while True:
1358             j = randdevice()
1359             # Cheat to prevent shuttle damage unless on ship 
1360             if not (game.damage[j]<0.0 or (j==DSHUTTL and game.iscraft != "onship")):
1361                 break
1362         cdam.append(j)
1363         extradm = (hit*game.damfac)/(ncrit*(75.0+25.0*Rand()))
1364         game.damage[j] += extradm
1365         if loop1 > 0:
1366             for loop2 in range(loop1):
1367                 if j == cdam[loop2]:
1368                     break
1369             if loop2 < loop1:
1370                 continue
1371             ktr += 1
1372             if ktr==3:
1373                 skip(1)
1374             proutn(_(" and "))
1375         proutn(device[j])
1376     prout(_(" damaged."))
1377     if damaged(DSHIELD) and game.shldup:
1378         prout(_("***Shields knocked down."))
1379         game.shldup=False
1380
1381 def attack(torps_ok):
1382     # bad guy attacks us 
1383     # torps_ok == false forces use of phasers in an attack 
1384     atackd = False; attempt = False; ihurt = False;
1385     hitmax=0.0; hittot=0.0; chgfac=1.0
1386     jay = coord()
1387     where = "neither"
1388
1389     # game could be over at this point, check 
1390     if game.alldone:
1391         return
1392
1393     if idebug:
1394         prout("=== ATTACK!")
1395
1396     # Tholian gewts to move before attacking 
1397     if game.ithere:
1398         movetholian()
1399
1400     # if you have just entered the RNZ, you'll get a warning 
1401     if game.neutz: # The one chance not to be attacked 
1402         game.neutz = False
1403         return
1404
1405     # commanders get a chance to tac-move towards you 
1406     if (((game.comhere or game.ishere) and not game.justin) or game.skill == SKILL_EMERITUS) and torps_ok:
1407         moveklings()
1408
1409     # if no enemies remain after movement, we're done 
1410     if game.nenhere==0 or (game.nenhere==1 and iqhere and not iqengry):
1411         return
1412
1413     # set up partial hits if attack happens during shield status change 
1414     pfac = 1.0/game.inshld
1415     if game.shldchg:
1416         chgfac = 0.25+0.5*Rand()
1417
1418     skip(1)
1419
1420     # message verbosity control 
1421     if game.skill <= SKILL_FAIR:
1422         where = "sector"
1423
1424     for loop in range(1, game.nenhere+1):
1425         if game.kpower[loop] < 0:
1426             continue;   # too weak to attack 
1427         # compute hit strength and diminish shield power 
1428         r = Rand()
1429         # Increase chance of photon torpedos if docked or enemy energy low 
1430         if game.condition == "docked":
1431             r *= 0.25
1432         if game.kpower[loop] < 500:
1433             r *= 0.25; 
1434         jay = game.ks[loop]
1435         iquad = game.quad[jay.x][jay.y]
1436         if iquad==IHT or (iquad==IHQUEST and not iqengry):
1437             continue
1438         # different enemies have different probabilities of throwing a torp 
1439         usephasers = not torps_ok or \
1440             (iquad == IHK and r > 0.0005) or \
1441             (iquad==IHC and r > 0.015) or \
1442             (iquad==IHR and r > 0.3) or \
1443             (iquad==IHS and r > 0.07) or \
1444             (iquad==IHQUEST and r > 0.05)
1445         if usephasers:      # Enemy uses phasers 
1446             if game.condition == "docked":
1447                 continue; # Don't waste the effort! 
1448             attempt = True; # Attempt to attack 
1449             dustfac = 0.8+0.05*Rand()
1450             hit = game.kpower[loop]*math.pow(dustfac,game.kavgd[loop])
1451             game.kpower[loop] *= 0.75
1452         else: # Enemy uses photon torpedo 
1453             course = 1.90985*math.atan2(game.sector.y-jay.y, jay.x-game.sector.x)
1454             hit = 0
1455             proutn(_("***TORPEDO INCOMING"))
1456             if not damaged(DSRSENS):
1457                 proutn(_(" From "))
1458                 crmena(False, iquad, where, jay)
1459             attempt = True
1460             prout("  ")
1461             r = (Rand()+Rand())*0.5 -0.5
1462             r += 0.002*game.kpower[loop]*r
1463             hit = torpedo(course, r, jay, 1, 1)
1464             if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1465                 finish(FWON); # Klingons did themselves in! 
1466             if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova or game.alldone:
1467                 return; # Supernova or finished 
1468             if hit == None:
1469                 continue
1470         # incoming phaser or torpedo, shields may dissipate it 
1471         if game.shldup or game.shldchg or game.condition=="docked":
1472             # shields will take hits 
1473             propor = pfac * game.shield
1474             if game.condition =="docked":
1475                 propr *= 2.1
1476             if propor < 0.1:
1477                 propor = 0.1
1478             hitsh = propor*chgfac*hit+1.0
1479             absorb = 0.8*hitsh
1480             if absorb > game.shield:
1481                 absorb = game.shield
1482             game.shield -= absorb
1483             hit -= hitsh
1484             # taking a hit blasts us out of a starbase dock 
1485             if game.condition == "docked":
1486                 dock(False)
1487             # but the shields may take care of it 
1488             if propor > 0.1 and hit < 0.005*game.energy:
1489                 continue
1490         # hit from this opponent got through shields, so take damage 
1491         ihurt = True
1492         proutn(_("%d unit hit") % int(hit))
1493         if (damaged(DSRSENS) and usephasers) or game.skill<=SKILL_FAIR:
1494             proutn(_(" on the "))
1495             crmshp()
1496         if not damaged(DSRSENS) and usephasers:
1497             proutn(_(" from "))
1498             crmena(False, iquad, where, jay)
1499         skip(1)
1500         # Decide if hit is critical 
1501         if hit > hitmax:
1502             hitmax = hit
1503         hittot += hit
1504         fry(hit)
1505         game.energy -= hit
1506     if game.energy <= 0:
1507         # Returning home upon your shield, not with it... 
1508         finish(FBATTLE)
1509         return
1510     if not attempt and game.condition == "docked":
1511         prout(_("***Enemies decide against attacking your ship."))
1512     if not atackd:
1513         return
1514     percent = 100.0*pfac*game.shield+0.5
1515     if not ihurt:
1516         # Shields fully protect ship 
1517         proutn(_("Enemy attack reduces shield strength to "))
1518     else:
1519         # Print message if starship suffered hit(s) 
1520         skip(1)
1521         proutn(_("Energy left %2d    shields ") % int(game.energy))
1522         if game.shldup:
1523             proutn(_("up "))
1524         elif not damaged(DSHIELD):
1525             proutn(_("down "))
1526         else:
1527             proutn(_("damaged, "))
1528     prout(_("%d%%,   torpedoes left %d") % (percent, game.torps))
1529     # Check if anyone was hurt 
1530     if hitmax >= 200 or hittot >= 500:
1531         icas= hittot*Rand()*0.015
1532         if icas >= 2:
1533             skip(1)
1534             prout(_("Mc Coy-  \"Sickbay to bridge.  We suffered %d casualties") % icas)
1535             prout(_("   in that last attack.\""))
1536             game.casual += icas
1537             game.state.crew -= icas
1538     # After attack, reset average distance to enemies 
1539     for loop in range(1, game.nenhere+1):
1540         game.kavgd[loop] = game.kdist[loop]
1541     sortklings()
1542     return;
1543                 
1544 def deadkl(w, type, mv):
1545     # kill a Klingon, Tholian, Romulan, or Thingy 
1546     # Added mv to allow enemy to "move" before dying 
1547
1548     crmena(True, type, sector, mv)
1549     # Decide what kind of enemy it is and update appropriately 
1550     if type == IHR:
1551         # chalk up a Romulan 
1552         game.state.galaxy[game.quadrant.x][game.quadrant.y].romulans -= 1
1553         game.irhere -= 1
1554         game.state.nromrem -= 1
1555     elif type == IHT:
1556         # Killed a Tholian 
1557         game.ithere = False
1558     elif type == IHQUEST:
1559         # Killed a Thingy 
1560         iqhere = iqengry = False
1561         invalidate(thing)
1562     else:
1563         # Some type of a Klingon 
1564         game.state.galaxy[game.quadrant.x][game.quadrant.y].klingons -= 1
1565         game.klhere -= 1
1566         if type == IHC:
1567             game.comhere = False
1568             for i in range(1, game.state.remcom+1):
1569                 if same(game.state.kcmdr[i], game.quadrant):
1570                     break
1571             game.state.kcmdr[i] = game.state.kcmdr[game.state.remcom]
1572             game.state.kcmdr[game.state.remcom].x = 0
1573             game.state.kcmdr[game.state.remcom].y = 0
1574             game.state.remcom -= 1
1575             unschedule(FTBEAM)
1576             if game.state.remcom != 0:
1577                 schedule(FTBEAM, expran(1.0*game.incom/game.state.remcom))
1578         elif type ==  IHK:
1579             game.state.remkl -= 1
1580         elif type ==  IHS:
1581             game.state.nscrem -= 1
1582             game.ishere = False
1583             game.state.kscmdr.x = game.state.kscmdr.y = game.isatb = 0
1584             game.iscate = False
1585             unschedule(FSCMOVE)
1586             unschedule(FSCDBAS)
1587         else:
1588             prout("*** Internal error, deadkl() called on %s\n" % type)
1589
1590     # For each kind of enemy, finish message to player 
1591     prout(_(" destroyed."))
1592     game.quad[w.x][w.y] = IHDOT
1593     if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1594         return
1595     game.recompute()
1596     # Remove enemy ship from arrays describing local conditions 
1597     if is_scheduled(FCDBAS) and same(game.battle, game.quadrant) and type==IHC:
1598         unschedule(FCDBAS)
1599     for i in range(1, game.nenhere+1):
1600         if same(game.ks[i], w):
1601             break
1602     game.nenhere -= 1
1603     if i <= game.nenhere:
1604         for j in range(i, game.nenhere+1):
1605             game.ks[j] = game.ks[j+1]
1606             game.kpower[j] = game.kpower[j+1]
1607             game.kavgd[j] = game.kdist[j] = game.kdist[j+1]
1608     game.ks[game.nenhere+1].x = 0
1609     game.ks[game.nenhere+1].x = 0
1610     game.kdist[game.nenhere+1] = 0
1611     game.kavgd[game.nenhere+1] = 0
1612     game.kpower[game.nenhere+1] = 0
1613     return;
1614
1615 def targetcheck(x, y):
1616     # Return None if target is invalid 
1617     if not VALID_SECTOR(x, y):
1618         huh()
1619         return None
1620     deltx = 0.1*(y - game.sector.y)
1621     delty = 0.1*(x - game.sector.x)
1622     if deltx==0 and delty== 0:
1623         skip(1)
1624         prout(_("Spock-  \"Bridge to sickbay.  Dr. McCoy,"))
1625         prout(_("  I recommend an immediate review of"))
1626         prout(_("  the Captain's psychological profile.\""))
1627         chew()
1628         return None
1629     return 1.90985932*math.atan2(deltx, delty)
1630
1631 def photon():
1632     # launch photon torpedo 
1633     game.ididit = False
1634     if damaged(DPHOTON):
1635         prout(_("Photon tubes damaged."))
1636         chew()
1637         return
1638     if game.torps == 0:
1639         prout(_("No torpedoes left."))
1640         chew()
1641         return
1642     key = scan()
1643     while True:
1644         if key == IHALPHA:
1645             huh()
1646             return
1647         elif key == IHEOL:
1648             prout(_("%d torpedoes left.") % game.torps)
1649             proutn(_("Number of torpedoes to fire- "))
1650             key = scan()
1651         else: # key == IHREAL  {
1652             n = aaitem + 0.5
1653             if n <= 0: # abort command 
1654                 chew()
1655                 return
1656             if n > 3:
1657                 chew()
1658                 prout(_("Maximum of 3 torpedoes per burst."))
1659                 key = IHEOL
1660                 return
1661             if n <= game.torps:
1662                 break
1663             chew()
1664             key = IHEOL
1665     for i in range(1, n+1):
1666         key = scan()
1667         if i==1 and key == IHEOL:
1668             break;      # we will try prompting 
1669         if i==2 and key == IHEOL:
1670             # direct all torpedoes at one target 
1671             while i <= n:
1672                 targ[i][1] = targ[1][1]
1673                 targ[i][2] = targ[1][2]
1674                 course[i] = course[1]
1675                 i += 1
1676             break
1677         if key != IHREAL:
1678             huh()
1679             return
1680         targ[i][1] = aaitem
1681         key = scan()
1682         if key != IHREAL:
1683             huh()
1684             return
1685         targ[i][2] = aaitem
1686         course[i] = targetcheck(targ[i][1], targ[i][2])
1687         if course[i] == None:
1688             return
1689     chew()
1690     if i == 1 and key == IHEOL:
1691         # prompt for each one 
1692         for i in range(1, n+1):
1693             proutn(_("Target sector for torpedo number %d- ") % i)
1694             key = scan()
1695             if key != IHREAL:
1696                 huh()
1697                 return
1698             targ[i][1] = aaitem
1699             key = scan()
1700             if key != IHREAL:
1701                 huh()
1702                 return
1703             targ[i][2] = aaitem
1704             chew()
1705             course[i] = targetcheck(targ[i][1], targ[i][2])
1706             if course[i] == None:
1707                 return
1708     game.ididit = True
1709     # Loop for moving <n> torpedoes 
1710     for i in range(1, n+1):
1711         if game.condition != "docked":
1712             game.torps -= 1
1713         r = (Rand()+Rand())*0.5 -0.5
1714         if math.fabs(r) >= 0.47:
1715             # misfire! 
1716             r = (Rand()+1.2) * r
1717             if n>1:
1718                 prouts(_("***TORPEDO NUMBER %d MISFIRES") % i)
1719             else:
1720                 prouts(_("***TORPEDO MISFIRES."))
1721             skip(1)
1722             if i < n:
1723                 prout(_("  Remainder of burst aborted."))
1724             if Rand() <= 0.2:
1725                 prout(_("***Photon tubes damaged by misfire."))
1726                 game.damage[DPHOTON] = game.damfac*(1.0+2.0*Rand())
1727             break
1728         if game.shldup or game.condition == "docked":
1729             r *= 1.0 + 0.0001*game.shield
1730         torpedo(course[i], r, game.sector, i, n)
1731         if game.alldone or game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
1732             return
1733     if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1734         finish(FWON);
1735
1736 def overheat(rpow):
1737     # check for phasers overheating 
1738     if rpow > 1500:
1739         chekbrn = (rpow-1500.)*0.00038
1740         if Rand() <= chekbrn:
1741             prout(_("Weapons officer Sulu-  \"Phasers overheated, sir.\""))
1742             game.damage[DPHASER] = game.damfac*(1.0 + Rand()) * (1.0+chekbrn)
1743
1744 def checkshctrl(rpow):
1745     # check shield control 
1746         
1747     skip(1)
1748     if Rand() < 0.998:
1749         prout(_("Shields lowered."))
1750         return False
1751     # Something bad has happened 
1752     prouts(_("***RED ALERT!  RED ALERT!"))
1753     skip(2)
1754     hit = rpow*game.shield/game.inshld
1755     game.energy -= rpow+hit*0.8
1756     game.shield -= hit*0.2
1757     if game.energy <= 0.0:
1758         prouts(_("Sulu-  \"Captain! Shield malf***********************\""))
1759         skip(1)
1760         stars()
1761         finish(FPHASER)
1762         return True
1763     prouts(_("Sulu-  \"Captain! Shield malfunction! Phaser fire contained!\""))
1764     skip(2)
1765     prout(_("Lt. Uhura-  \"Sir, all decks reporting damage.\""))
1766     icas = hit*Rand()*0.012
1767     skip(1)
1768     fry(0.8*hit)
1769     if icas:
1770         skip(1)
1771         prout(_("McCoy to bridge- \"Severe radiation burns, Jim."))
1772         prout(_("  %d casualties so far.\"") % icas)
1773         game.casual += icas
1774         game.state.crew -= icas
1775     skip(1)
1776     prout(_("Phaser energy dispersed by shields."))
1777     prout(_("Enemy unaffected."))
1778     overheat(rpow)
1779     return True;
1780
1781 def hittem(doublehits):
1782     # register a phaser hit on Klingons and Romulans 
1783     nenhr2=game.nenhere; kk=1
1784     w = coord()
1785     skip(1)
1786     for k in range(1, nenhr2+1):
1787         wham = hits[k]
1788         if wham==0:
1789             continue
1790         dustfac = 0.9 + 0.01*Rand()
1791         hit = wham*math.pow(dustfac,game.kdist[kk])
1792         kpini = game.kpower[kk]
1793         kp = math.fabs(kpini)
1794         if PHASEFAC*hit < kp:
1795             kp = PHASEFAC*hit
1796         if game.kpower[kk] < 0:
1797             game.kpower[kk] -= -kp
1798         else:
1799             game.kpower[kk] -= kp
1800         kpow = game.kpower[kk]
1801         w = game.ks[kk]
1802         if hit > 0.005:
1803             if not damaged(DSRSENS):
1804                 boom(w)
1805             proutn(_("%d unit hit on ") % int(hit))
1806         else:
1807             proutn(_("Very small hit on "))
1808         ienm = game.quad[w.x][w.y]
1809         if ienm==IHQUEST:
1810             iqengry = True
1811         crmena(False, ienm, "sector", w)
1812         skip(1)
1813         if kpow == 0:
1814             deadkl(w, ienm, w)
1815             if (game.state.remkl + game.state.remcom + game.state.nscrem)==0:
1816                 finish(FWON);           
1817             if game.alldone:
1818                 return
1819             kk -= 1; # don't do the increment 
1820         else: # decide whether or not to emasculate klingon 
1821             if kpow > 0 and Rand() >= 0.9 and \
1822                 kpow <= ((0.4 + 0.4*Rand())*kpini):
1823                 prout(_("***Mr. Spock-  \"Captain, the vessel at Sector %s"), w)
1824                 prout(_("   has just lost its firepower.\""))
1825                 game.kpower[kk] = -kpow
1826         kk += 1
1827     return;
1828
1829 def phasers():
1830     # fire phasers 
1831     hits = []; rpow=0
1832     kz = 0; k = 1; irec=0 # Cheating inhibitor 
1833     ifast = False; no = False; itarg = True; msgflag = True
1834     automode = "NOTSET"
1835     key=0
1836
1837     skip(1)
1838     # SR sensors and Computer are needed fopr automode 
1839     if damaged(DSRSENS) or damaged(DCOMPTR):
1840         itarg = False
1841     if game.condition == "docked":
1842         prout(_("Phasers can't be fired through base shields."))
1843         chew()
1844         return
1845     if damaged(DPHASER):
1846         prout(_("Phaser control damaged."))
1847         chew()
1848         return
1849     if game.shldup:
1850         if damaged(DSHCTRL):
1851             prout(_("High speed shield control damaged."))
1852             chew()
1853             return
1854         if game.energy <= 200.0:
1855             prout(_("Insufficient energy to activate high-speed shield control."))
1856             chew()
1857             return
1858         prout(_("Weapons Officer Sulu-  \"High-speed shield control enabled, sir.\""))
1859         ifast = True
1860                 
1861     # Original code so convoluted, I re-did it all 
1862     while automode=="NOTSET":
1863         key=scan()
1864         if key == IHALPHA:
1865             if isit("manual"):
1866                 if game.nenhere==0:
1867                     prout(_("There is no enemy present to select."))
1868                     chew()
1869                     key = IHEOL
1870                     automode="AUTOMATIC"
1871                 else:
1872                     automode = "MANUAL"
1873                     key = scan()
1874             elif isit("automatic"):
1875                 if (not itarg) and game.nenhere != 0:
1876                     automode = "FORCEMAN"
1877                 else:
1878                     if game.nenhere==0:
1879                         prout(_("Energy will be expended into space."))
1880                     automode = "AUTOMATIC"
1881                     key = scan()
1882             elif isit("no"):
1883                 no = True
1884             else:
1885                 huh()
1886                 return
1887         elif key == IHREAL:
1888             if game.nenhere==0:
1889                 prout(_("Energy will be expended into space."))
1890                 automode = "AUTOMATIC"
1891             elif not itarg:
1892                 automode = "FORCEMAN"
1893             else:
1894                 automode = "AUTOMATIC"
1895         else:
1896             # IHEOL 
1897             if game.nenhere==0:
1898                 prout(_("Energy will be expended into space."))
1899                 automode = "AUTOMATIC"
1900             elif not itarg:
1901                 automode = "FORCEMAN"
1902             else: 
1903                 proutn(_("Manual or automatic? "))                      
1904     avail = game.energy
1905     if ifast:
1906         avail -= 200.0
1907     if automode == "AUTOMATIC":
1908         if key == IHALPHA and isit("no"):
1909             no = True
1910             key = scan()
1911         if key != IHREAL and game.nenhere != 0:
1912             prout(_("Phasers locked on target. Energy available: %.2f")%avail)
1913         irec=0
1914         while True:
1915             chew()
1916             if not kz:
1917                 for i in range(1, game.nenhere+1):
1918                     irec += math.fabs(game.kpower[i])/(PHASEFAC*math.pow(0.90,game.kdist[i]))*(1.01+0.05*Rand()) + 1.0
1919             kz=1
1920             proutn(_("%d units required. ") % irec)
1921             chew()
1922             proutn(_("Units to fire= "))
1923             key = scan()
1924             if key!=IHREAL:
1925                 return
1926             rpow = aaitem
1927             if rpow > avail:
1928                 proutn(_("Energy available= %.2f") % avail)
1929                 skip(1)
1930                 key = IHEOL
1931             if not rpow > avail:
1932                 break
1933         if rpow<=0:
1934             # chicken out 
1935             chew()
1936             return
1937         key=scan()
1938         if key == IHALPHA and isit("no"):
1939             no = True
1940         if ifast:
1941             game.energy -= 200; # Go and do it! 
1942             if checkshctrl(rpow):
1943                 return
1944         chew()
1945         game.energy -= rpow
1946         extra = rpow
1947         if game.nenhere:
1948             extra = 0.0
1949             powrem = rpow
1950             for i in range(1, game.nenhere+1):
1951                 hits[i] = 0.0
1952                 if powrem <= 0:
1953                     continue
1954                 hits[i] = math.fabs(game.kpower[i])/(PHASEFAC*math.pow(0.90,game.kdist[i]))
1955                 over = (0.01 + 0.05*Rand())*hits[i]
1956                 temp = powrem
1957                 powrem -= hits[i] + over
1958                 if powrem <= 0 and temp < hits[i]:
1959                     hits[i] = temp
1960                 if powrem <= 0:
1961                     over = 0.0
1962                 extra += over
1963             if powrem > 0.0:
1964                 extra += powrem
1965             hittem(hits)
1966             game.ididit = True
1967         if extra > 0 and not game.alldone:
1968             if game.ithere:
1969                 proutn(_("*** Tholian web absorbs "))
1970                 if game.nenhere>0:
1971                     proutn(_("excess "))
1972                 prout(_("phaser energy."))
1973             else:
1974                 prout(_("%d expended on empty space.") % int(extra))
1975     elif automode == "FORCEMAN":
1976         chew()
1977         key = IHEOL
1978         if damaged(DCOMPTR):
1979             prout(_("Battle computer damaged, manual fire only."))
1980         else:
1981             skip(1)
1982             prouts(_("---WORKING---"))
1983             skip(1)
1984             prout(_("Short-range-sensors-damaged"))
1985             prout(_("Insufficient-data-for-automatic-phaser-fire"))
1986             prout(_("Manual-fire-must-be-used"))
1987             skip(1)
1988     elif automode == "MANUAL":
1989         rpow = 0.0
1990         for k in range(1, game.nenhere+1):
1991             aim = game.ks[k]
1992             ienm = game.quad[aim.x][aim.y]
1993             if msgflag:
1994                 proutn(_("Energy available= %.2f") % (avail-0.006))
1995                 skip(1)
1996                 msgflag = False
1997                 rpow = 0.0
1998             if damaged(DSRSENS) and not (abs(game.sector.x-aim.x) < 2 and abs(game.sector.y-aim.y) < 2) and \
1999                 (ienm == IHC or ienm == IHS):
2000                 cramen(ienm)
2001                 prout(_(" can't be located without short range scan."))
2002                 chew()
2003                 key = IHEOL
2004                 hits[k] = 0; # prevent overflow -- thanks to Alexei Voitenko 
2005                 k += 1
2006                 continue
2007             if key == IHEOL:
2008                 chew()
2009                 if itarg and k > kz:
2010                     irec=(abs(game.kpower[k])/(PHASEFAC*math.pow(0.9,game.kdist[k]))) * (1.01+0.05*Rand()) + 1.0
2011                 kz = k
2012                 proutn("(")
2013                 if not damaged(DCOMPTR):
2014                     proutn("%d" % irec)
2015                 else:
2016                     proutn("??")
2017                 proutn(")  ")
2018                 proutn(_("units to fire at "))
2019                 crmena(False, ienm, sector, aim)
2020                 proutn("-  ")
2021                 key = scan()
2022             if key == IHALPHA and isit("no"):
2023                 no = True
2024                 key = scan()
2025                 continue
2026             if key == IHALPHA:
2027                 huh()
2028                 return
2029             if key == IHEOL:
2030                 if k==1: # Let me say I'm baffled by this 
2031                     msgflag = True
2032                 continue
2033             if aaitem < 0:
2034                 # abort out 
2035                 chew()
2036                 return
2037             hits[k] = aaitem
2038             rpow += aaitem
2039             # If total requested is too much, inform and start over 
2040             if rpow > avail:
2041                 prout(_("Available energy exceeded -- try again."))
2042                 chew()
2043                 return
2044             key = scan(); # scan for next value 
2045             k += 1
2046         if rpow == 0.0:
2047             # zero energy -- abort 
2048             chew()
2049             return
2050         if key == IHALPHA and isit("no"):
2051             no = True
2052         game.energy -= rpow
2053         chew()
2054         if ifast:
2055             game.energy -= 200.0
2056             if checkshctrl(rpow):
2057                 return
2058         hittem(hits)
2059         game.ididit = True
2060      # Say shield raised or malfunction, if necessary 
2061     if game.alldone:
2062         return
2063     if ifast:
2064         skip(1)
2065         if no == 0:
2066             if Rand() >= 0.99:
2067                 prout(_("Sulu-  \"Sir, the high-speed shield control has malfunctioned . . ."))
2068                 prouts(_("         CLICK   CLICK   POP  . . ."))
2069                 prout(_(" No response, sir!"))
2070                 game.shldup = False
2071             else:
2072                 prout(_("Shields raised."))
2073         else:
2074             game.shldup = False
2075     overheat(rpow);
2076
2077 # Code from events,c begins here.
2078
2079 # This isn't a real event queue a la BSD Trek yet -- you can only have one 
2080 # event of each type active at any given time.  Mostly these means we can 
2081 # only have one FDISTR/FENSLV/FREPRO sequence going at any given time
2082 # BSD Trek, from which we swiped the idea, can have up to 5.
2083
2084 import math
2085
2086 def unschedule(evtype):
2087     # remove an event from the schedule 
2088     game.future[evtype].date = FOREVER
2089     return game.future[evtype]
2090
2091 def is_scheduled(evtype):
2092     # is an event of specified type scheduled 
2093     return game.future[evtype].date != FOREVER
2094
2095 def scheduled(evtype):
2096     # when will this event happen? 
2097     return game.future[evtype].date
2098
2099 def schedule(evtype, offset):
2100     # schedule an event of specified type 
2101     game.future[evtype].date = game.state.date + offset
2102     return game.future[evtype]
2103
2104 def postpone(evtype, offset):
2105     # postpone a scheduled event 
2106     game.future[evtype].date += offset
2107
2108 def cancelrest():
2109     # rest period is interrupted by event 
2110     if game.resting:
2111         skip(1)
2112         proutn(_("Mr. Spock-  \"Captain, shall we cancel the rest period?\""))
2113         if ja() == True:
2114             game.resting = False
2115             game.optime = 0.0
2116             return True
2117
2118     return False
2119
2120 def events():
2121     # run through the event queue looking for things to do 
2122     i=0
2123     fintim = game.state.date + game.optime; yank=0
2124     ictbeam = False; istract = False
2125     w = coord(); hold = coord()
2126     ev = event(); ev2 = event()
2127
2128     def tractorbeam():
2129         # tractor beaming cases merge here 
2130         yank = math.sqrt(yank)
2131         announce()
2132         game.optime = (10.0/(7.5*7.5))*yank # 7.5 is yank rate (warp 7.5) 
2133         skip(1)
2134         proutn("***")
2135         crmshp()
2136         prout(_(" caught in long range tractor beam--"))
2137         # If Kirk & Co. screwing around on planet, handle 
2138         atover(True) # atover(true) is Grab 
2139         if game.alldone:
2140             return
2141         if game.icraft: # Caught in Galileo? 
2142             finish(FSTRACTOR)
2143             return
2144         # Check to see if shuttle is aboard 
2145         if game.iscraft == "offship":
2146             skip(1)
2147             if Rand() > 0.5:
2148                 prout(_("Galileo, left on the planet surface, is captured"))
2149                 prout(_("by aliens and made into a flying McDonald's."))
2150                 game.damage[DSHUTTL] = -10
2151                 game.iscraft = "removed"
2152             else:
2153                 prout(_("Galileo, left on the planet surface, is well hidden."))
2154         if evcode==0:
2155             game.quadrant = game.state.kscmdr
2156         else:
2157             game.quadrant = game.state.kcmdr[i]
2158         game.sector = randplace(QUADSIZE)
2159         crmshp()
2160         proutn(_(" is pulled to "))
2161         proutn(cramlc(quadrant, game.quadrant))
2162         proutn(", ")
2163         prout(cramlc(sector, game.sector))
2164         if game.resting:
2165             prout(_("(Remainder of rest/repair period cancelled.)"))
2166             game.resting = False
2167         if not game.shldup:
2168             if not damaged(DSHIELD) and game.shield > 0:
2169                 doshield(True) # raise shields 
2170                 game.shldchg=False
2171             else:
2172                 prout(_("(Shields not currently useable.)"))
2173         newqad(False)
2174         # Adjust finish time to time of tractor beaming 
2175         fintim = game.state.date+game.optime
2176         attack(False)
2177         if game.state.remcom <= 0:
2178             unschedule(FTBEAM)
2179         else: 
2180             schedule(FTBEAM, game.optime+expran(1.5*game.intime/game.state.remcom))
2181
2182     def destroybase():
2183         # Code merges here for any commander destroying base 
2184         # Not perfect, but will have to do 
2185         # Handle case where base is in same quadrant as starship 
2186         if same(game.battle, game.quadrant):
2187             game.state.chart[game.battle.x][game.battle.y].starbase = False
2188             game.quad[game.base.x][game.base.y] = IHDOT
2189             game.base.x=game.base.y=0
2190             newcnd()
2191             skip(1)
2192             prout(_("Spock-  \"Captain, I believe the starbase has been destroyed.\""))
2193         elif game.state.rembase != 1 and \
2194                  (not damaged(DRADIO) or game.condition == "docked"):
2195             # Get word via subspace radio 
2196             announce()
2197             skip(1)
2198             prout(_("Lt. Uhura-  \"Captain, Starfleet Command reports that"))
2199             proutn(_("   the starbase in "))
2200             proutn(cramlc(quadrant, game.battle))
2201             prout(_(" has been destroyed by"))
2202             if game.isatb == 2: 
2203                 prout(_("the Klingon Super-Commander"))
2204             else:
2205                 prout(_("a Klingon Commander"))
2206             game.state.chart[game.battle.x][game.battle.y].starbase = False
2207         # Remove Starbase from galaxy 
2208         game.state.galaxy[game.battle.x][game.battle.y].starbase = False
2209         for i in range(1, game.state.rembase+1):
2210             if same(game.state.baseq[i], game.battle):
2211                 game.state.baseq[i] = game.state.baseq[game.state.rembase]
2212         game.state.rembase -= 1
2213         if game.isatb == 2:
2214             # reinstate a commander's base attack 
2215             game.battle = hold
2216             game.isatb = 0
2217         else:
2218             invalidate(game.battle)
2219
2220     if idebug:
2221         prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
2222         for i in range(1, NEVENTS):
2223             if   i == FSNOVA:  proutn("=== Supernova       ")
2224             elif i == FTBEAM:  proutn("=== T Beam          ")
2225             elif i == FSNAP:   proutn("=== Snapshot        ")
2226             elif i == FBATTAK: proutn("=== Base Attack     ")
2227             elif i == FCDBAS:  proutn("=== Base Destroy    ")
2228             elif i == FSCMOVE: proutn("=== SC Move         ")
2229             elif i == FSCDBAS: proutn("=== SC Base Destroy ")
2230             elif i == FDSPROB: proutn("=== Probe Move      ")
2231             elif i == FDISTR:  proutn("=== Distress Call   ")
2232             elif i == FENSLV:  proutn("=== Enslavement     ")
2233             elif i == FREPRO:  proutn("=== Klingon Build   ")
2234             if is_scheduled(i):
2235                 prout("%.2f" % (scheduled(i)))
2236             else:
2237                 prout("never")
2238     radio_was_broken = damaged(DRADIO)
2239     hold.x = hold.y = 0
2240     while True:
2241         # Select earliest extraneous event, evcode==0 if no events 
2242         evcode = FSPY
2243         if game.alldone:
2244             return
2245         datemin = fintim
2246         for l in range(1, NEVENTS):
2247             if game.future[l].date < datemin:
2248                 evcode = l
2249                 if idebug:
2250                     prout("== Event %d fires" % evcode)
2251                 datemin = game.future[l].date
2252         xtime = datemin-game.state.date
2253         game.state.date = datemin
2254         # Decrement Federation resources and recompute remaining time 
2255         game.state.remres -= (game.state.remkl+4*game.state.remcom)*xtime
2256         game.recompute()
2257         if game.state.remtime <=0:
2258             finish(FDEPLETE)
2259             return
2260         # Any crew left alive? 
2261         if game.state.crew <=0:
2262             finish(FCREW)
2263             return
2264         # Is life support adequate? 
2265         if damaged(DLIFSUP) and game.condition != "docked":
2266             if game.lsupres < xtime and game.damage[DLIFSUP] > game.lsupres:
2267                 finish(FLIFESUP)
2268                 return
2269             game.lsupres -= xtime
2270             if game.damage[DLIFSUP] <= xtime:
2271                 game.lsupres = game.inlsr
2272         # Fix devices 
2273         repair = xtime
2274         if game.condition == "docked":
2275             repair /= game.docfac
2276         # Don't fix Deathray here 
2277         for l in range(0, NDEVICES):
2278             if game.damage[l] > 0.0 and l != DDRAY:
2279                 if game.damage[l]-repair > 0.0:
2280                     game.damage[l] -= repair
2281                 else:
2282                     game.damage[l] = 0.0
2283         # If radio repaired, update star chart and attack reports 
2284         if radio_was_broken and not damaged(DRADIO):
2285             prout(_("Lt. Uhura- \"Captain, the sub-space radio is working and"))
2286             prout(_("   surveillance reports are coming in."))
2287             skip(1)
2288             if not game.iseenit:
2289                 attackreport(False)
2290                 game.iseenit = True
2291             rechart()
2292             prout(_("   The star chart is now up to date.\""))
2293             skip(1)
2294         # Cause extraneous event EVCODE to occur 
2295         game.optime -= xtime
2296         if evcode == FSNOVA: # Supernova 
2297             announce()
2298             supernova(False)
2299             schedule(FSNOVA, expran(0.5*game.intime))
2300             if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
2301                 return
2302         elif evcode == FSPY: # Check with spy to see if SC should tractor beam 
2303             if game.state.nscrem == 0 or \
2304                 ictbeam or istract or \
2305                 game.condition=="docked" or game.isatb==1 or game.iscate:
2306                 return
2307             if game.ientesc or \
2308                 (game.energy<2000 and game.torps<4 and game.shield < 1250) or \
2309                 (damaged(DPHASER) and (damaged(DPHOTON) or game.torps<4)) or \
2310                 (damaged(DSHIELD) and \
2311                  (game.energy < 2500 or damaged(DPHASER)) and \
2312                  (game.torps < 5 or damaged(DPHOTON))):
2313                 # Tractor-beam her! 
2314                 istract = True
2315                 yank = distance(game.state.kscmdr, game.quadrant)
2316                 ictbeam = True
2317                 tractorbeam()
2318             else:
2319                 return
2320         elif evcode == FTBEAM: # Tractor beam 
2321             if game.state.remcom == 0:
2322                 unschedule(FTBEAM)
2323                 continue
2324             i = Rand()*game.state.remcom+1.0
2325             yank = square(game.state.kcmdr[i].x-game.quadrant.x) + square(game.state.kcmdr[i].y-game.quadrant.y)
2326             if istract or game.condition == "docked" or yank == 0:
2327                 # Drats! Have to reschedule 
2328                 schedule(FTBEAM, 
2329                          game.optime + expran(1.5*game.intime/game.state.remcom))
2330                 continue
2331             ictbeam = True
2332             tractorbeam()
2333         elif evcode == FSNAP: # Snapshot of the universe (for time warp) 
2334             game.snapsht = game.state
2335             game.state.snap = True
2336             schedule(FSNAP, expran(0.5 * game.intime))
2337         elif evcode == FBATTAK: # Commander attacks starbase 
2338             if game.state.remcom==0 or game.state.rembase==0:
2339                 # no can do 
2340                 unschedule(FBATTAK)
2341                 unschedule(FCDBAS)
2342                 continue
2343             i = 0
2344             for j in range(1, game.state.rembase+1):
2345                 for k in range(1, game.state.remcom+1):
2346                     if same(game.state.baseq[j], game.state.kcmdr[k]) and \
2347                         not same(game.state.baseq[j], game.quadrant) and \
2348                         not same(game.state.baseq[j], game.state.kscmdr):
2349                         i = 1
2350                 if i == 1:
2351                     continue
2352             if j>game.state.rembase:
2353                 # no match found -- try later 
2354                 schedule(FBATTAK, expran(0.3*game.intime))
2355                 unschedule(FCDBAS)
2356                 continue
2357             # commander + starbase combination found -- launch attack 
2358             game.battle = game.state.baseq[j]
2359             schedule(FCDBAS, 1.0+3.0*Rand())
2360             if game.isatb: # extra time if SC already attacking 
2361                 postpone(FCDBAS, scheduled(FSCDBAS)-game.state.date)
2362             game.future[FBATTAK].date = game.future[FCDBAS].date + expran(0.3*game.intime)
2363             game.iseenit = False
2364             if damaged(DRADIO) and game.condition != "docked": 
2365                 continue # No warning :-( 
2366             game.iseenit = True
2367             announce()
2368             skip(1)
2369             proutn(_("Lt. Uhura-  \"Captain, the starbase in Quadrant %s") % game.battle)
2370             prout(_("   reports that it is under attack and that it can"))
2371             proutn(_("   hold out only until stardate %d") % (int(scheduled(FCDBAS))))
2372             prout(".\"")
2373             if cancelrest():
2374                 return
2375         elif evcode == FSCDBAS: # Supercommander destroys base 
2376             unschedule(FSCDBAS)
2377             game.isatb = 2
2378             if not game.state.galaxy[game.state.kscmdr.x][game.state.kscmdr.y].starbase: 
2379                 continue # WAS RETURN! 
2380             hold = game.battle
2381             game.battle = game.state.kscmdr
2382             destroybase()
2383         elif evcode == FCDBAS: # Commander succeeds in destroying base 
2384             if evcode==FCDBAS:
2385                 unschedule(FCDBAS)
2386                 # find the lucky pair 
2387                 for i in range(1, game.state.remcom+1):
2388                     if same(game.state.kcmdr[i], game.battle): 
2389                         break
2390                 if i > game.state.remcom or game.state.rembase == 0 or \
2391                     not game.state.galaxy[game.battle.x][game.battle.y].starbase:
2392                     # No action to take after all 
2393                     invalidate(game.battle)
2394                     continue
2395             destroybase()
2396         elif evcode == FSCMOVE: # Supercommander moves 
2397             schedule(FSCMOVE, 0.2777)
2398             if not game.ientesc and not istract and game.isatb != 1 and \
2399                    (not game.iscate or not game.justin): 
2400                 supercommander()
2401         elif evcode == FDSPROB: # Move deep space probe 
2402             schedule(FDSPROB, 0.01)
2403             game.probex += game.probeinx
2404             game.probey += game.probeiny
2405             i = (int)(game.probex/QUADSIZE +0.05)
2406             j = (int)(game.probey/QUADSIZE + 0.05)
2407             if game.probec.x != i or game.probec.y != j:
2408                 game.probec.x = i
2409                 game.probec.y = j
2410                 if not VALID_QUADRANT(i, j) or \
2411                     game.state.galaxy[game.probec.x][game.probec.y].supernova:
2412                     # Left galaxy or ran into supernova
2413                     if not damaged(DRADIO) or game.condition == "docked":
2414                         announce()
2415                         skip(1)
2416                         proutn(_("Lt. Uhura-  \"The deep space probe "))
2417                         if not VALID_QUADRANT(j, i):
2418                             proutn(_("has left the galaxy"))
2419                         else:
2420                             proutn(_("is no longer transmitting"))
2421                         prout(".\"")
2422                     unschedule(FDSPROB)
2423                     continue
2424                 if not damaged(DRADIO) or game.condition == "docked":
2425                     announce()
2426                     skip(1)
2427                     proutn(_("Lt. Uhura-  \"The deep space probe is now in "))
2428                     proutn(cramlc(quadrant, game.probec))
2429                     prout(".\"")
2430             pdest = game.state.galaxy[game.probec.x][game.probec.y]
2431             # Update star chart if Radio is working or have access to radio
2432             if not damaged(DRADIO) or game.condition == "docked":
2433                 chp = game.state.chart[game.probec.x][game.probec.y]
2434                 chp.klingons = pdest.klingons
2435                 chp.starbase = pdest.starbase
2436                 chp.stars = pdest.stars
2437                 pdest.charted = True
2438             game.proben -= 1 # One less to travel
2439             if game.proben == 0 and game.isarmed and pdest.stars:
2440                 # lets blow the sucker! 
2441                 supernova(True, game.probec)
2442                 unschedule(FDSPROB)
2443                 if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova: 
2444                     return
2445         elif evcode == FDISTR: # inhabited system issues distress call 
2446             unschedule(FDISTR)
2447             # try a whole bunch of times to find something suitable 
2448             for i in range(100):
2449                 # need a quadrant which is not the current one,
2450                 # which has some stars which are inhabited and
2451                 # not already under attack, which is not
2452                 # supernova'ed, and which has some Klingons in it
2453                 w = randplace(GALSIZE)
2454                 q = game.state.galaxy[w.x][w.y]
2455                 if not (same(game.quadrant, w) or q.planet == NOPLANET or \
2456                       game.state.planets[q.planet].inhabited == UNINHABITED or \
2457                       q.supernova or q.status!=secure or q.klingons<=0):
2458                     break
2459             else:
2460                 # can't seem to find one; ignore this call 
2461                 if idebug:
2462                     prout("=== Couldn't find location for distress event.")
2463                 continue
2464             # got one!!  Schedule its enslavement 
2465             ev = schedule(FENSLV, expran(game.intime))
2466             ev.quadrant = w
2467             q.status = distressed
2468
2469             # tell the captain about it if we can 
2470             if not damaged(DRADIO) or game.condition == "docked":
2471                 prout(_("Uhura- Captain, %s in Quadrant %s reports it is under attack") \
2472                         % (q.planet, `w`))
2473                 prout(_("by a Klingon invasion fleet."))
2474                 if cancelrest():
2475                     return
2476         elif evcode == FENSLV:          # starsystem is enslaved 
2477             ev = unschedule(FENSLV)
2478             # see if current distress call still active 
2479             q = game.state.galaxy[ev.quadrant.x][ev.quadrant.y]
2480             if q.klingons <= 0:
2481                 q.status = "secure"
2482                 continue
2483             q.status = "enslaved"
2484
2485             # play stork and schedule the first baby 
2486             ev2 = schedule(FREPRO, expran(2.0 * game.intime))
2487             ev2.quadrant = ev.quadrant
2488
2489             # report the disaster if we can 
2490             if not damaged(DRADIO) or game.condition == "docked":
2491                 prout(_("Uhura- We've lost contact with starsystem %s") % \
2492                         q.planet)
2493                 prout(_("in Quadrant %s.\n") % ev.quadrant)
2494         elif evcode == FREPRO:          # Klingon reproduces 
2495             # If we ever switch to a real event queue, we'll need to
2496             # explicitly retrieve and restore the x and y.
2497             ev = schedule(FREPRO, expran(1.0 * game.intime))
2498             # see if current distress call still active 
2499             q = game.state.galaxy[ev.quadrant.x][ev.quadrant.y]
2500             if q.klingons <= 0:
2501                 q.status = "secure"
2502                 continue
2503             if game.state.remkl >=MAXKLGAME:
2504                 continue                # full right now 
2505             # reproduce one Klingon 
2506             w = ev.quadrant
2507             if game.klhere >= MAXKLQUAD:
2508                 try:
2509                     # this quadrant not ok, pick an adjacent one 
2510                     for i in range(w.x - 1, w.x + 2):
2511                         for j in range(w.y - 1, w.y + 2):
2512                             if not VALID_QUADRANT(i, j):
2513                                 continue
2514                             q = game.state.galaxy[w.x][w.y]
2515                             # check for this quad ok (not full & no snova) 
2516                             if q.klingons >= MAXKLQUAD or q.supernova:
2517                                 continue
2518                             raise "FOUNDIT"
2519                     else:
2520                         continue        # search for eligible quadrant failed
2521                 except "FOUNDIT":
2522                     w.x = i
2523                     w.y = j
2524             # deliver the child 
2525             game.state.remkl += 1
2526             q.klingons += 1
2527             if same(game.quadrant, w):
2528                 newkling(++game.klhere)
2529
2530             # recompute time left
2531             game.recompute()
2532             # report the disaster if we can 
2533             if not damaged(DRADIO) or game.condition == "docked":
2534                 if same(game.quadrant, w):
2535                     prout(_("Spock- sensors indicate the Klingons have"))
2536                     prout(_("launched a warship from %s.") % q.planet)
2537                 else:
2538                     prout(_("Uhura- Starfleet reports increased Klingon activity"))
2539                     if q.planet != NOPLANET:
2540                         proutn(_("near %s") % q.planet)
2541                     prout(_("in Quadrant %s.") % w)
2542                                 
2543 def wait():
2544     # wait on events 
2545     game.ididit = False
2546     while True:
2547         key = scan()
2548         if key  != IHEOL:
2549             break
2550         proutn(_("How long? "))
2551     chew()
2552     if key != IHREAL:
2553         huh()
2554         return
2555     origTime = delay = aaitem
2556     if delay <= 0.0:
2557         return
2558     if delay >= game.state.remtime or game.nenhere != 0:
2559         proutn(_("Are you sure? "))
2560         if ja() == False:
2561             return
2562
2563     # Alternate resting periods (events) with attacks 
2564
2565     game.resting = True
2566     while True:
2567         if delay <= 0:
2568             game.resting = False
2569         if not game.resting:
2570             prout(_("%d stardates left.") % int(game.state.remtime))
2571             return
2572         temp = game.optime = delay
2573         if game.nenhere:
2574             rtime = 1.0 + Rand()
2575             if rtime < temp:
2576                 temp = rtime
2577             game.optime = temp
2578         if game.optime < delay:
2579             attack(False)
2580         if game.alldone:
2581             return
2582         events()
2583         game.ididit = True
2584         if game.alldone:
2585             return
2586         delay -= temp
2587         # Repair Deathray if long rest at starbase 
2588         if origTime-delay >= 9.99 and game.condition == "docked":
2589             game.damage[DDRAY] = 0.0
2590         # leave if quadrant supernovas
2591         if game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
2592             break
2593     game.resting = False
2594     game.optime = 0
2595
2596 # A nova occurs.  It is the result of having a star hit with a
2597 # photon torpedo, or possibly of a probe warhead going off.
2598 # Stars that go nova cause stars which surround them to undergo
2599 # the same probabilistic process.  Klingons next to them are
2600 # destroyed.  And if the starship is next to it, it gets zapped.
2601 # If the zap is too much, it gets destroyed.
2602         
2603 def nova(nov):
2604     # star goes nova 
2605     course = (0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5)
2606     newc = coord(); scratch = coord()
2607
2608     if Rand() < 0.05:
2609         # Wow! We've supernova'ed 
2610         supernova(False, nov)
2611         return
2612
2613     # handle initial nova 
2614     game.quad[nov.x][nov.y] = IHDOT
2615     crmena(False, IHSTAR, sector, nov)
2616     prout(_(" novas."))
2617     game.state.galaxy[game.quadrant.x][game.quadrant.y].stars -= 1
2618     game.state.starkl += 1
2619         
2620     # Set up stack to recursively trigger adjacent stars 
2621     bot = top = top2 = 1
2622     kount = 0
2623     icx = icy = 0
2624     hits[1][1] = nov.x
2625     hits[1][2] = nov.y
2626     while True:
2627         for mm in range(bot, top+1): 
2628             for nn in range(1, 3+1):  # nn,j represents coordinates around current 
2629                 for j in range(1, 3+1):
2630                     if j==2 and nn== 2:
2631                         continue
2632                     scratch.x = hits[mm][1]+nn-2
2633                     scratch.y = hits[mm][2]+j-2
2634                     if not VALID_SECTOR(scratch.y, scratch.x):
2635                         continue
2636                     iquad = game.quad[scratch.x][scratch.y]
2637                     # Empty space ends reaction
2638                     if iquad in (IHDOT, IHQUEST, IHBLANK, IHT, IHWEB):
2639                         break
2640                     elif iquad == IHSTAR: # Affect another star 
2641                         if Rand() < 0.05:
2642                             # This star supernovas 
2643                             scratch = supernova(False)
2644                             return
2645                         top2 += 1
2646                         hits[top2][1]=scratch.x
2647                         hits[top2][2]=scratch.y
2648                         game.state.galaxy[game.quadrant.x][game.quadrant.y].stars -= 1
2649                         game.state.starkl += 1
2650                         crmena(True, IHSTAR, sector, scratch)
2651                         prout(_(" novas."))
2652                         game.quad[scratch.x][scratch.y] = IHDOT
2653                     elif iquad == IHP: # Destroy planet 
2654                         game.state.galaxy[game.quadrant.x][game.quadrant.y].planet = NOPLANET
2655                         game.state.nplankl += 1
2656                         crmena(True, IHP, sector, scratch)
2657                         prout(_(" destroyed."))
2658                         game.state.planets[game.iplnet].pclass = destroyed
2659                         game.iplnet = 0
2660                         invalidate(game.plnet)
2661                         if game.landed:
2662                             finish(FPNOVA)
2663                             return
2664                         game.quad[scratch.x][scratch.y] = IHDOT
2665                     elif iquad == IHB: # Destroy base 
2666                         game.state.galaxy[game.quadrant.x][game.quadrant.y].starbase = False
2667                         for i in range(1, game.state.rembase+1):
2668                             if same(game.state.baseq[i], game.quadrant): 
2669                                 break
2670                         game.state.baseq[i] = game.state.baseq[game.state.rembase]
2671                         game.state.rembase -= 1
2672                         invalidate(game.base)
2673                         game.state.basekl += 1
2674                         newcnd()
2675                         crmena(True, IHB, sector, scratch)
2676                         prout(_(" destroyed."))
2677                         game.quad[scratch.x][scratch.y] = IHDOT
2678                     elif iquad in (IHE, IHF): # Buffet ship 
2679                         prout(_("***Starship buffeted by nova."))
2680                         if game.shldup:
2681                             if game.shield >= 2000.0:
2682                                 game.shield -= 2000.0
2683                             else:
2684                                 diff = 2000.0 - game.shield
2685                                 game.energy -= diff
2686                                 game.shield = 0.0
2687                                 game.shldup = False
2688                                 prout(_("***Shields knocked out."))
2689                                 game.damage[DSHIELD] += 0.005*game.damfac*Rand()*diff
2690                         else:
2691                             game.energy -= 2000.0
2692                         if game.energy <= 0:
2693                             finish(FNOVA)
2694                             return
2695                         # add in course nova contributes to kicking starship
2696                         icx += game.sector.x-hits[mm][1]
2697                         icy += game.sector.y-hits[mm][2]
2698                         kount += 1
2699                     elif iquad == IHK: # kill klingon 
2700                         deadkl(scratch,iquad, scratch)
2701                     elif iquad in (IHC,IHS,IHR): # Damage/destroy big enemies 
2702                         for ll in range(1, game.nenhere+1):
2703                             if same(game.ks[ll], scratch):
2704                                 break
2705                         game.kpower[ll] -= 800.0 # If firepower is lost, die 
2706                         if game.kpower[ll] <= 0.0:
2707                             deadkl(scratch, iquad, scratch)
2708                             break
2709                         newc.x = scratch.x + scratch.x - hits[mm][1]
2710                         newc.y = scratch.y + scratch.y - hits[mm][2]
2711                         crmena(True, iquad, sector, scratch)
2712                         proutn(_(" damaged"))
2713                         if not VALID_SECTOR(newc.x, newc.y):
2714                             # can't leave quadrant 
2715                             skip(1)
2716                             break
2717                         iquad1 = game.quad[newc.x][newc.y]
2718                         if iquad1 == IHBLANK:
2719                             proutn(_(", blasted into "))
2720                             crmena(False, IHBLANK, sector, newc)
2721                             skip(1)
2722                             deadkl(scratch, iquad, newc)
2723                             break
2724                         if iquad1 != IHDOT:
2725                             # can't move into something else 
2726                             skip(1)
2727                             break
2728                         proutn(_(", buffeted to "))
2729                         proutn(cramlc(sector, newc))
2730                         game.quad[scratch.x][scratch.y] = IHDOT
2731                         game.quad[newc.x][newc.y] = iquad
2732                         game.ks[ll] = newc
2733                         game.kdist[ll] = game.kavgd[ll] = distance(game.sector, newc)
2734                         skip(1)
2735         if top == top2: 
2736             break
2737         bot = top + 1
2738         top = top2
2739     if kount==0: 
2740         return
2741
2742     # Starship affected by nova -- kick it away. 
2743     game.dist = kount*0.1
2744     icx = sgn(icx)
2745     icy = sgn(icy)
2746     game.direc = course[3*(icx+1)+icy+2]
2747     if game.direc == 0.0:
2748         game.dist = 0.0
2749     if game.dist == 0.0:
2750         return
2751     game.optime = 10.0*game.dist/16.0
2752     skip(1)
2753     prout(_("Force of nova displaces starship."))
2754     imove(True)
2755     game.optime = 10.0*game.dist/16.0
2756     return
2757         
2758 def supernova(induced, w=None):
2759     # star goes supernova 
2760     num = 0; npdead = 0
2761     nq = coord()
2762
2763     if w != None: 
2764         nq = w
2765     else:
2766         stars = 0
2767         # Scheduled supernova -- select star 
2768         # logic changed here so that we won't favor quadrants in top
2769         # left of universe 
2770         for nq.x in range(1, GALSIZE+1):
2771             for nq.y in range(1, GALSIZE+1):
2772                 stars += game.state.galaxy[nq.x][nq.y].stars
2773         if stars == 0:
2774             return # nothing to supernova exists 
2775         num = Rand()*stars + 1
2776         for nq.x in range(1, GALSIZE+1):
2777             for nq.y in range(1, GALSIZE+1):
2778                 num -= game.state.galaxy[nq.x][nq.y].stars
2779                 if num <= 0:
2780                     break
2781             if num <=0:
2782                 break
2783         if idebug:
2784             proutn("=== Super nova here?")
2785             if ja() == True:
2786                 nq = game.quadrant
2787
2788     if not same(nq, game.quadrant) or game.justin:
2789         # it isn't here, or we just entered (treat as enroute) 
2790         if not damaged(DRADIO) or game.condition == "docked":
2791             skip(1)
2792             prout(_("Message from Starfleet Command       Stardate %.2f") % game.state.date)
2793             prout(_("     Supernova in Quadrant %s; caution advised.") % nq)
2794     else:
2795         ns = coord()
2796         # we are in the quadrant! 
2797         num = Rand()* game.state.galaxy[nq.x][nq.y].stars + 1
2798         for ns.x in range(1, QUADSIZE+1):
2799             for ns.y in range(1, QUADSIZE+1):
2800                 if game.quad[ns.x][ns.y]==IHSTAR:
2801                     num -= 1
2802                     if num==0:
2803                         break
2804             if num==0:
2805                 break
2806
2807         skip(1)
2808         prouts(_("***RED ALERT!  RED ALERT!"))
2809         skip(1)
2810         prout(_("***Incipient supernova detected at Sector %s") % ns)
2811         if square(ns.x-game.sector.x) + square(ns.y-game.sector.y) <= 2.1:
2812             proutn(_("Emergency override attempts t"))
2813             prouts("***************")
2814             skip(1)
2815             stars()
2816             game.alldone = True
2817
2818     # destroy any Klingons in supernovaed quadrant 
2819     kldead = game.state.galaxy[nq.x][nq.y].klingons
2820     game.state.galaxy[nq.x][nq.y].klingons = 0
2821     if same(nq, game.state.kscmdr):
2822         # did in the Supercommander! 
2823         game.state.nscrem = game.state.kscmdr.x = game.state.kscmdr.y = game.isatb =  0
2824         game.iscate = False
2825         unschedule(FSCMOVE)
2826         unschedule(FSCDBAS)
2827     if game.state.remcom:
2828         maxloop = game.state.remcom
2829         for l in range(1, maxloop+1):
2830             if same(game.state.kcmdr[l], nq):
2831                 game.state.kcmdr[l] = game.state.kcmdr[game.state.remcom]
2832                 invalidate(game.state.kcmdr[game.state.remcom])
2833                 game.state.remcom -= 1
2834                 kldead -= 1
2835                 if game.state.remcom==0:
2836                     unschedule(FTBEAM)
2837                 break
2838     game.state.remkl -= kldead
2839     # destroy Romulans and planets in supernovaed quadrant 
2840     nrmdead = game.state.galaxy[nq.x][nq.y].romulans
2841     game.state.galaxy[nq.x][nq.y].romulans = 0
2842     game.state.nromrem -= nrmdead
2843     # Destroy planets 
2844     for loop in range(game.inplan):
2845         if same(game.state.planets[loop].w, nq):
2846             game.state.planets[loop].pclass = destroyed
2847             npdead += 1
2848     # Destroy any base in supernovaed quadrant 
2849     if game.state.rembase:
2850         maxloop = game.state.rembase
2851         for loop in range(1, maxloop+1):
2852             if same(game.state.baseq[loop], nq):
2853                 game.state.baseq[loop] = game.state.baseq[game.state.rembase]
2854                 invalidate(game.state.baseq[game.state.rembase])
2855                 game.state.rembase -= 1
2856                 break
2857     # If starship caused supernova, tally up destruction 
2858     if induced:
2859         game.state.starkl += game.state.galaxy[nq.x][nq.y].stars
2860         game.state.basekl += game.state.galaxy[nq.x][nq.y].starbase
2861         game.state.nplankl += npdead
2862     # mark supernova in galaxy and in star chart 
2863     if same(game.quadrant, nq) or not damaged(DRADIO) or game.condition == "docked":
2864         game.state.galaxy[nq.x][nq.y].supernova = True
2865     # If supernova destroys last Klingons give special message 
2866     if (game.state.remkl + game.state.remcom + game.state.nscrem)==0 and not same(nq, game.quadrant):
2867         skip(2)
2868         if not induced:
2869             prout(_("Lucky you!"))
2870         proutn(_("A supernova in %s has just destroyed the last Klingons.") % nq)
2871         finish(FWON)
2872         return
2873     # if some Klingons remain, continue or die in supernova 
2874     if game.alldone:
2875         finish(FSNOVAED)
2876     return
2877
2878 # Code from finish.c ends here.
2879
2880 def selfdestruct():
2881     # self-destruct maneuver 
2882     # Finish with a BANG! 
2883     chew()
2884     if damaged(DCOMPTR):
2885         prout(_("Computer damaged; cannot execute destruct sequence."))
2886         return
2887     prouts(_("---WORKING---")); skip(1)
2888     prouts(_("SELF-DESTRUCT-SEQUENCE-ACTIVATED")); skip(1)
2889     prouts("   10"); skip(1)
2890     prouts("       9"); skip(1)
2891     prouts("          8"); skip(1)
2892     prouts("             7"); skip(1)
2893     prouts("                6"); skip(1)
2894     skip(1)
2895     prout(_("ENTER-CORRECT-PASSWORD-TO-CONTINUE-"))
2896     skip(1)
2897     prout(_("SELF-DESTRUCT-SEQUENCE-OTHERWISE-"))
2898     skip(1)
2899     prout(_("SELF-DESTRUCT-SEQUENCE-WILL-BE-ABORTED"))
2900     skip(1)
2901     scan()
2902     chew()
2903     if game.passwd != citem:
2904         prouts(_("PASSWORD-REJECTED;"))
2905         skip(1)
2906         prouts(_("CONTINUITY-EFFECTED"))
2907         skip(2)
2908         return
2909     prouts(_("PASSWORD-ACCEPTED")); skip(1)
2910     prouts("                   5"); skip(1)
2911     prouts("                      4"); skip(1)
2912     prouts("                         3"); skip(1)
2913     prouts("                            2"); skip(1)
2914     prouts("                              1"); skip(1)
2915     if Rand() < 0.15:
2916         prouts(_("GOODBYE-CRUEL-WORLD"))
2917         skip(1)
2918     kaboom()
2919
2920 def kaboom():
2921     stars()
2922     if game.ship==IHE:
2923         prouts("***")
2924     prouts(_("********* Entropy of "))
2925     crmshp()
2926     prouts(_(" maximized *********"))
2927     skip(1)
2928     stars()
2929     skip(1)
2930     if game.nenhere != 0:
2931         whammo = 25.0 * game.energy
2932         l=1
2933         while l <= game.nenhere:
2934             if game.kpower[l]*game.kdist[l] <= whammo: 
2935                 deadkl(game.ks[l], game.quad[game.ks[l].x][game.ks[l].y], game.ks[l])
2936             l += 1
2937     finish(FDILITHIUM)
2938                                 
2939 def killrate():
2940     "Compute our rate of kils over time."
2941     return ((game.inkling + game.incom + game.inscom) - (game.state.remkl + game.state.remcom + game.state.nscrem))/(game.state.date-game.indate)
2942
2943 def badpoints():
2944     "Compute demerits."
2945     badpt = 5.0*game.state.starkl + \
2946             game.casual + \
2947             10.0*game.state.nplankl + \
2948             300*game.state.nworldkl + \
2949             45.0*game.nhelp +\
2950             100.0*game.state.basekl +\
2951             3.0*game.abandoned
2952     if game.ship == IHF:
2953         badpt += 100.0
2954     elif game.ship == None:
2955         badpt += 200.0
2956     return badpt
2957
2958
2959 def finish(ifin):
2960     # end the game, with appropriate notfications 
2961     igotit = False
2962     game.alldone = True
2963     skip(3)
2964     prout(_("It is stardate %.1f.") % game.state.date)
2965     skip(1)
2966     if ifin == FWON: # Game has been won
2967         if game.state.nromrem != 0:
2968             prout(_("The remaining %d Romulans surrender to Starfleet Command.") %
2969                   game.state.nromrem)
2970
2971         prout(_("You have smashed the Klingon invasion fleet and saved"))
2972         prout(_("the Federation."))
2973         game.gamewon = True
2974         if game.alive:
2975             badpt = badpoints()
2976             if badpt < 100.0:
2977                 badpt = 0.0     # Close enough!
2978             # killsPerDate >= RateMax
2979             if game.state.date-game.indate < 5.0 or \
2980                 killrate() >= 0.1*game.skill*(game.skill+1.0) + 0.1 + 0.008*badpt:
2981                 skip(1)
2982                 prout(_("In fact, you have done so well that Starfleet Command"))
2983                 if game.skill == SKILL_NOVICE:
2984                     prout(_("promotes you one step in rank from \"Novice\" to \"Fair\"."))
2985                 elif game.skill == SKILL_FAIR:
2986                     prout(_("promotes you one step in rank from \"Fair\" to \"Good\"."))
2987                 elif game.skill == SKILL_GOOD:
2988                     prout(_("promotes you one step in rank from \"Good\" to \"Expert\"."))
2989                 elif game.skill == SKILL_EXPERT:
2990                     prout(_("promotes you to Commodore Emeritus."))
2991                     skip(1)
2992                     prout(_("Now that you think you're really good, try playing"))
2993                     prout(_("the \"Emeritus\" game. It will splatter your ego."))
2994                 elif game.skill == SKILL_EMERITUS:
2995                     skip(1)
2996                     proutn(_("Computer-  "))
2997                     prouts(_("ERROR-ERROR-ERROR-ERROR"))
2998                     skip(2)
2999                     prouts(_("  YOUR-SKILL-HAS-EXCEEDED-THE-CAPACITY-OF-THIS-PROGRAM"))
3000                     skip(1)
3001                     prouts(_("  THIS-PROGRAM-MUST-SURVIVE"))
3002                     skip(1)
3003                     prouts(_("  THIS-PROGRAM-MUST-SURVIVE"))
3004                     skip(1)
3005                     prouts(_("  THIS-PROGRAM-MUST-SURVIVE"))
3006                     skip(1)
3007                     prouts(_("  THIS-PROGRAM-MUST?- MUST ? - SUR? ? -?  VI"))
3008                     skip(2)
3009                     prout(_("Now you can retire and write your own Star Trek game!"))
3010                     skip(1)
3011                 elif game.skill >= SKILL_EXPERT:
3012                     if game.thawed and not idebug:
3013                         prout(_("You cannot get a citation, so..."))
3014                     else:
3015                         proutn(_("Do you want your Commodore Emeritus Citation printed? "))
3016                         chew()
3017                         if ja() == True:
3018                             igotit = True
3019             # Only grant long life if alive (original didn't!)
3020             skip(1)
3021             prout(_("LIVE LONG AND PROSPER."))
3022         score()
3023         if igotit:
3024             plaque()        
3025         return
3026     elif ifin == FDEPLETE: # Federation Resources Depleted
3027         prout(_("Your time has run out and the Federation has been"))
3028         prout(_("conquered.  Your starship is now Klingon property,"))
3029         prout(_("and you are put on trial as a war criminal.  On the"))
3030         proutn(_("basis of your record, you are "))
3031         if (game.state.remkl + game.state.remcom + game.state.nscrem)*3.0 > (game.inkling + game.incom + game.inscom):
3032             prout(_("acquitted."))
3033             skip(1)
3034             prout(_("LIVE LONG AND PROSPER."))
3035         else:
3036             prout(_("found guilty and"))
3037             prout(_("sentenced to death by slow torture."))
3038             game.alive = False
3039         score()
3040         return
3041     elif ifin == FLIFESUP:
3042         prout(_("Your life support reserves have run out, and"))
3043         prout(_("you die of thirst, starvation, and asphyxiation."))
3044         prout(_("Your starship is a derelict in space."))
3045     elif ifin == FNRG:
3046         prout(_("Your energy supply is exhausted."))
3047         skip(1)
3048         prout(_("Your starship is a derelict in space."))
3049     elif ifin == FBATTLE:
3050         proutn(_("The "))
3051         crmshp()
3052         prout(_("has been destroyed in battle."))
3053         skip(1)
3054         prout(_("Dulce et decorum est pro patria mori."))
3055     elif ifin == FNEG3:
3056         prout(_("You have made three attempts to cross the negative energy"))
3057         prout(_("barrier which surrounds the galaxy."))
3058         skip(1)
3059         prout(_("Your navigation is abominable."))
3060         score()
3061     elif ifin == FNOVA:
3062         prout(_("Your starship has been destroyed by a nova."))
3063         prout(_("That was a great shot."))
3064         skip(1)
3065     elif ifin == FSNOVAED:
3066         proutn(_("The "))
3067         crmshp()
3068         prout(_(" has been fried by a supernova."))
3069         prout(_("...Not even cinders remain..."))
3070     elif ifin == FABANDN:
3071         prout(_("You have been captured by the Klingons. If you still"))
3072         prout(_("had a starbase to be returned to, you would have been"))
3073         prout(_("repatriated and given another chance. Since you have"))
3074         prout(_("no starbases, you will be mercilessly tortured to death."))
3075     elif ifin == FDILITHIUM:
3076         prout(_("Your starship is now an expanding cloud of subatomic particles"))
3077     elif ifin == FMATERIALIZE:
3078         prout(_("Starbase was unable to re-materialize your starship."))
3079         prout(_("Sic transit gloria mundi"))
3080     elif ifin == FPHASER:
3081         proutn(_("The "))
3082         crmshp()
3083         prout(_(" has been cremated by its own phasers."))
3084     elif ifin == FLOST:
3085         prout(_("You and your landing party have been"))
3086         prout(_("converted to energy, disipating through space."))
3087     elif ifin == FMINING:
3088         prout(_("You are left with your landing party on"))
3089         prout(_("a wild jungle planet inhabited by primitive cannibals."))
3090         skip(1)
3091         prout(_("They are very fond of \"Captain Kirk\" soup."))
3092         skip(1)
3093         proutn(_("Without your leadership, the "))
3094         crmshp()
3095         prout(_(" is destroyed."))
3096     elif ifin == FDPLANET:
3097         prout(_("You and your mining party perish."))
3098         skip(1)
3099         prout(_("That was a great shot."))
3100         skip(1)
3101     elif ifin == FSSC:
3102         prout(_("The Galileo is instantly annihilated by the supernova."))
3103         prout(_("You and your mining party are atomized."))
3104         skip(1)
3105         proutn(_("Mr. Spock takes command of the "))
3106         crmshp()
3107         prout(_(" and"))
3108         prout(_("joins the Romulans, reigning terror on the Federation."))
3109     elif ifin == FPNOVA:
3110         prout(_("You and your mining party are atomized."))
3111         skip(1)
3112         proutn(_("Mr. Spock takes command of the "))
3113         crmshp()
3114         prout(_(" and"))
3115         prout(_("joins the Romulans, reigning terror on the Federation."))
3116     elif ifin == FSTRACTOR:
3117         prout(_("The shuttle craft Galileo is also caught,"))
3118         prout(_("and breaks up under the strain."))
3119         skip(1)
3120         prout(_("Your debris is scattered for millions of miles."))
3121         proutn(_("Without your leadership, the "))
3122         crmshp()
3123         prout(_(" is destroyed."))
3124     elif ifin == FDRAY:
3125         prout(_("The mutants attack and kill Spock."))
3126         prout(_("Your ship is captured by Klingons, and"))
3127         prout(_("your crew is put on display in a Klingon zoo."))
3128     elif ifin == FTRIBBLE:
3129         prout(_("Tribbles consume all remaining water,"))
3130         prout(_("food, and oxygen on your ship."))
3131         skip(1)
3132         prout(_("You die of thirst, starvation, and asphyxiation."))
3133         prout(_("Your starship is a derelict in space."))
3134     elif ifin == FHOLE:
3135         prout(_("Your ship is drawn to the center of the black hole."))
3136         prout(_("You are crushed into extremely dense matter."))
3137     elif ifin == FCREW:
3138         prout(_("Your last crew member has died."))
3139     if game.ship == IHF:
3140         game.ship = None
3141     elif game.ship == IHE:
3142         game.ship = IHF
3143     game.alive = False
3144     if (game.state.remkl + game.state.remcom + game.state.nscrem) != 0:
3145         goodies = game.state.remres/game.inresor
3146         baddies = (game.state.remkl + 2.0*game.state.remcom)/(game.inkling+2.0*game.incom)
3147         if goodies/baddies >= 1.0+0.5*Rand():
3148             prout(_("As a result of your actions, a treaty with the Klingon"))
3149             prout(_("Empire has been signed. The terms of the treaty are"))
3150             if goodies/baddies >= 3.0+Rand():
3151                 prout(_("favorable to the Federation."))
3152                 skip(1)
3153                 prout(_("Congratulations!"))
3154             else:
3155                 prout(_("highly unfavorable to the Federation."))
3156         else:
3157             prout(_("The Federation will be destroyed."))
3158     else:
3159         prout(_("Since you took the last Klingon with you, you are a"))
3160         prout(_("martyr and a hero. Someday maybe they'll erect a"))
3161         prout(_("statue in your memory. Rest in peace, and try not"))
3162         prout(_("to think about pigeons."))
3163         game.gamewon = True
3164     score()
3165
3166 def score():
3167     # compute player's score 
3168     timused = game.state.date - game.indate
3169
3170     iskill = game.skill
3171     if (timused == 0 or (game.state.remkl + game.state.remcom + game.state.nscrem) != 0) and timused < 5.0:
3172         timused = 5.0
3173     perdate = killrate()
3174     ithperd = 500*perdate + 0.5
3175     iwon = 0
3176     if game.gamewon:
3177         iwon = 100*game.skill
3178     if game.ship == IHE: 
3179         klship = 0
3180     elif game.ship == IHF: 
3181         klship = 1
3182     else:
3183         klship = 2
3184     if not game.gamewon:
3185         game.state.nromrem = 0 # None captured if no win
3186     iscore = 10*(game.inkling - game.state.remkl) \
3187              + 50*(game.incom - game.state.remcom) \
3188              + ithperd + iwon \
3189              + 20*(game.inrom - game.state.nromrem) \
3190              + 200*(game.inscom - game.state.nscrem) \
3191              - game.state.nromrem \
3192              - badpoints()
3193     if not game.alive:
3194         iscore -= 200
3195     skip(2)
3196     prout(_("Your score --"))
3197     if game.inrom - game.state.nromrem:
3198         prout(_("%6d Romulans destroyed                 %5d") %
3199               (game.inrom - game.state.nromrem, 20*(game.inrom - game.state.nromrem)))
3200     if game.state.nromrem:
3201         prout(_("%6d Romulans captured                  %5d") %
3202               (game.state.nromrem, game.state.nromrem))
3203     if game.inkling - game.state.remkl:
3204         prout(_("%6d ordinary Klingons destroyed        %5d") %
3205               (game.inkling - game.state.remkl, 10*(game.inkling - game.state.remkl)))
3206     if game.incom - game.state.remcom:
3207         prout(_("%6d Klingon commanders destroyed       %5d") %
3208               (game.incom - game.state.remcom, 50*(game.incom - game.state.remcom)))
3209     if game.inscom - game.state.nscrem:
3210         prout(_("%6d Super-Commander destroyed          %5d") %
3211               (game.inscom - game.state.nscrem, 200*(game.inscom - game.state.nscrem)))
3212     if ithperd:
3213         prout(_("%6.2f Klingons per stardate              %5d") %
3214               (perdate, ithperd))
3215     if game.state.starkl:
3216         prout(_("%6d stars destroyed by your action     %5d") %
3217               (game.state.starkl, -5*game.state.starkl))
3218     if game.state.nplankl:
3219         prout(_("%6d planets destroyed by your action   %5d") %
3220               (game.state.nplankl, -10*game.state.nplankl))
3221     if (game.options & OPTION_WORLDS) and game.state.nworldkl:
3222         prout(_("%6d inhabited planets destroyed by your action   %5d") %
3223               (game.state.nplankl, -300*game.state.nworldkl))
3224     if game.state.basekl:
3225         prout(_("%6d bases destroyed by your action     %5d") %
3226               (game.state.basekl, -100*game.state.basekl))
3227     if game.nhelp:
3228         prout(_("%6d calls for help from starbase       %5d") %
3229               (game.nhelp, -45*game.nhelp))
3230     if game.casual:
3231         prout(_("%6d casualties incurred                %5d") %
3232               (game.casual, -game.casual))
3233     if game.abandoned:
3234         prout(_("%6d crew abandoned in space            %5d") %
3235               (game.abandoned, -3*game.abandoned))
3236     if klship:
3237         prout(_("%6d ship(s) lost or destroyed          %5d") %
3238               (klship, -100*klship))
3239     if not game.alive:
3240         prout(_("Penalty for getting yourself killed        -200"))
3241     if game.gamewon:
3242         proutn(_("Bonus for winning "))
3243         if game.skill   == SKILL_NOVICE:        proutn(_("Novice game  "))
3244         elif game.skill == SKILL_FAIR:          proutn(_("Fair game    "))
3245         elif game.skill ==  SKILL_GOOD:         proutn(_("Good game    "))
3246         elif game.skill ==  SKILL_EXPERT:       proutn(_("Expert game  "))
3247         elif game.skill ==  SKILL_EMERITUS:     proutn(_("Emeritus game"))
3248         prout("           %5d" % iwon)
3249     skip(1)
3250     prout(_("TOTAL SCORE                               %5d") % iscore)
3251
3252 def plaque():
3253     # emit winner's commemmorative plaque 
3254     skip(2)
3255     while True:
3256         proutn(_("File or device name for your plaque: "))
3257         cgetline(winner, sizeof(winner))
3258         try:
3259             fp = open(winner, "w")
3260             break
3261         except IOError:
3262             prout(_("Invalid name."))
3263
3264     proutn(_("Enter name to go on plaque (up to 30 characters): "))
3265     cgetline(winner, sizeof(winner))
3266     # The 38 below must be 64 for 132-column paper 
3267     nskip = 38 - len(winner)/2
3268
3269     fp.write("\n\n\n\n")
3270     # --------DRAW ENTERPRISE PICTURE. 
3271     fp.write("                                       EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n" )
3272     fp.write("                                      EEE                      E  : :                                         :  E\n" )
3273     fp.write("                                    EE   EEE                   E  : :                   NCC-1701              :  E\n")
3274     fp.write("EEEEEEEEEEEEEEEE        EEEEEEEEEEEEEEE  : :                              : E\n")
3275     fp.write(" E                                     EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n")
3276     fp.write("                      EEEEEEEEE               EEEEEEEEEEEEE                 E  E\n")
3277     fp.write("                               EEEEEEE   EEEEE    E          E              E  E\n")
3278     fp.write("                                      EEE           E          E            E  E\n")
3279     fp.write("                                                       E         E          E  E\n")
3280     fp.write("                                                         EEEEEEEEEEEEE      E  E\n")
3281     fp.write("                                                      EEE :           EEEEEEE  EEEEEEEE\n")
3282     fp.write("                                                    :E    :                 EEEE       E\n")
3283     fp.write("                                                   .-E   -:-----                       E\n")
3284     fp.write("                                                    :E    :                            E\n")
3285     fp.write("                                                      EE  :                    EEEEEEEE\n")
3286     fp.write("                                                       EEEEEEEEEEEEEEEEEEEEEEE\n")
3287     fp.write("\n\n\n")
3288     fp.write(_("                                                       U. S. S. ENTERPRISE\n"))
3289     fp.write("\n\n\n\n")
3290     fp.write(_("                                  For demonstrating outstanding ability as a starship captain\n"))
3291     fp.write("\n")
3292     fp.write(_("                                                Starfleet Command bestows to you\n"))
3293     fp.write("\n")
3294     fp.write("%*s%s\n\n" % (nskip, "", winner))
3295     fp.write(_("                                                           the rank of\n\n"))
3296     fp.write(_("                                                       \"Commodore Emeritus\"\n\n"))
3297     fp.write("                                                          ")
3298     if game.skill ==  SKILL_EXPERT:
3299         fp.write(_(" Expert level\n\n"))
3300     elif game.skill == SKILL_EMERITUS:
3301         fp.write(_("Emeritus level\n\n"))
3302     else:
3303         fp.write(_(" Cheat level\n\n"))
3304     timestring = ctime()
3305     fp.write(_("                                                 This day of %.6s %.4s, %.8s\n\n") %
3306                     (timestring+4, timestring+20, timestring+11))
3307     fp.write(_("                                                        Your score:  %d\n\n") % iscore)
3308     fp.write(_("                                                    Klingons per stardate:  %.2f\n") % perdate)
3309     fp.close()
3310
3311 # Code from io.c begins here
3312
3313 rows = linecount = 0    # for paging 
3314 stdscr = None
3315 fullscreen_window = None
3316 srscan_window     = None
3317 report_window     = None
3318 status_window     = None
3319 lrscan_window     = None
3320 message_window    = None
3321 prompt_window     = None
3322
3323 def outro():
3324     "wrap up, either normally or due to signal"
3325     if game.options & OPTION_CURSES:
3326         #clear()
3327         #curs_set(1)
3328         #refresh()
3329         #resetterm()
3330         #echo()
3331         curses.endwin()
3332         stdout.write('\n')
3333     if logfp:
3334         logfp.close()
3335
3336 def iostart():
3337     global stdscr
3338     #setlocale(LC_ALL, "")
3339     #bindtextdomain(PACKAGE, LOCALEDIR)
3340     #textdomain(PACKAGE)
3341     if atexit.register(outro):
3342         sys.stderr.write("Unable to register outro(), exiting...\n")
3343         os.exit(1)
3344     if not (game.options & OPTION_CURSES):
3345         ln_env = os.getenv("LINES")
3346         if ln_env:
3347             rows = ln_env
3348         else:
3349             rows = 25
3350     else:
3351         stdscr = curses.initscr()
3352         stdscr.keypad(True)
3353         #saveterm()
3354         curses.nonl()
3355         curses.cbreak()
3356         curses.start_color()
3357         curses.init_pair(curses.COLOR_BLACK, curses.COLOR_BLACK, curses.COLOR_BLACK)
3358         curses.init_pair(curses.COLOR_GREEN, curses.COLOR_GREEN, curses.COLOR_BLACK)
3359         curses.init_pair(curses.COLOR_RED, curses.COLOR_RED, curses.COLOR_BLACK)
3360         curses.init_pair(curses.COLOR_CYAN, curses.COLOR_CYAN, curses.COLOR_BLACK)
3361         curses.init_pair(curses.COLOR_WHITE, curses.COLOR_WHITE, curses.COLOR_BLACK)
3362         curses.init_pair(curses.COLOR_MAGENTA, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
3363         curses.init_pair(curses.COLOR_BLUE, curses.COLOR_BLUE, curses.COLOR_BLACK)
3364         curses.init_pair(curses.COLOR_YELLOW, curses.COLOR_YELLOW, curses.COLOR_BLACK)
3365         #noecho()
3366         global fullscreen_window, srscan_window, report_window, status_window
3367         global lrscan_window, message_window, prompt_window
3368         fullscreen_window = stdscr
3369         srscan_window     = curses.newwin(12, 25, 0,       0)
3370         report_window     = curses.newwin(11, 0,  1,       25)
3371         status_window     = curses.newwin(10, 0,  1,       39)
3372         lrscan_window     = curses.newwin(5,  0,  0,       64) 
3373         message_window    = curses.newwin(0,  0,  12,      0)
3374         prompt_window     = curses.newwin(1,  0,  rows-2,  0) 
3375         message_window.scrollok(True)
3376         setwnd(fullscreen_window)
3377         textcolor(DEFAULT)
3378
3379
3380 def waitfor():
3381     "wait for user action -- OK to do nothing if on a TTY"
3382     if game.options & OPTION_CURSES:
3383         stsdcr.getch()
3384
3385 def announce():
3386     skip(1)
3387     if game.skill > SKILL_FAIR:
3388         prouts(_("[ANOUNCEMENT ARRIVING...]"))
3389     else:
3390         prouts(_("[IMPORTANT ANNOUNCEMENT ARRIVING -- PRESS ENTER TO CONTINUE]"))
3391     skip(1)
3392
3393 def pause_game():
3394     if game.skill > SKILL_FAIR:
3395         prompt = _("[CONTINUE?]")
3396     else:
3397         prompt = _("[PRESS ENTER TO CONTINUE]")
3398
3399     if game.options & OPTION_CURSES:
3400         drawmaps(0)
3401         setwnd(prompt_window)
3402         prompt_window.wclear()
3403         prompt_window.addstr(prompt)
3404         prompt_window.getstr()
3405         prompt_window.clear()
3406         prompt_window.refresh()
3407         setwnd(message_window)
3408     else:
3409         global linecount
3410         stdout.write('\n')
3411         proutn(prompt)
3412         raw_input()
3413         for j in range(0, rows):
3414             stdout.write('\n')
3415         linecount = 0
3416
3417 def skip(i):
3418     "Skip i lines.  Pause game if this would cause a scrolling event."
3419     while dummy in range(i):
3420         if game.options & OPTION_CURSES:
3421             (y, x) = curwnd.getyx()
3422             (my, mx) = curwnd.getmaxyx()
3423             if curwnd == message_window and y >= my - 3:
3424                 pause_game()
3425                 clrscr()
3426             else:
3427                 proutn("\n")
3428         else:
3429             global linecount
3430             linecount += 1
3431             if linecount >= rows:
3432                 pause_game()
3433             else:
3434                 stdout.write('\n')
3435
3436 def proutn(line):
3437     "Utter a line with no following line feed."
3438     if game.options & OPTION_CURSES:
3439         curwnd.addstr(line)
3440         curwnd.refresh()
3441     else:
3442         stdout.write(line)
3443
3444 def prout(line):
3445     proutn(line)
3446     skip(1)
3447
3448 def prouts(line):
3449     "print slowly!" 
3450     for c in line:
3451         curses.delay_output(30)
3452         proutn(c)
3453         if game.options & OPTION_CURSES:
3454             wrefresh(curwnd)
3455         else:
3456             sys.stdout.flush()
3457     curses.delay_output(300)
3458
3459 def cgetline(line, max):
3460     "Get a line of input."
3461     if game.options & OPTION_CURSES:
3462         line = curwnd.getstr() + "\n"
3463         curwnd.refresh()
3464     else:
3465         if replayfp and not replayfp.closed:
3466             line = replayfp.readline()
3467         else:
3468             sys.stdin.readline()
3469     if logfp:
3470         logfp.write(line)
3471
3472 def setwnd(wnd):
3473     "Change windows -- OK for this to be a no-op in tty mode." 
3474     if game.options & OPTION_CURSES:
3475         curwnd = wnd
3476         curses.curs_set(wnd == fullscreen_window or wnd == message_window or wnd == prompt_window)
3477
3478 def clreol():
3479     "Clear to end of line -- can be a no-op in tty mode" 
3480     if game.options & OPTION_CURSES:
3481         wclrtoeol(curwnd)
3482         wrefresh(curwnd)
3483
3484 def clrscr():
3485     "Clear screen -- can be a no-op in tty mode."
3486     global linecount
3487     if game.options & OPTION_CURSES:
3488        curwnd.clear()
3489        curwnd.move(0, 0)
3490        curwnd.refresh()
3491     linecount = 0
3492
3493 def textcolor(color):
3494     "Set the current text color"
3495     if game.options & OPTION_CURSES:
3496         if color == DEFAULT: 
3497             curwnd.attrset(0)
3498         elif color == BLACK: 
3499             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_BLACK))
3500         elif color == BLUE: 
3501             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_BLUE))
3502         elif color == GREEN: 
3503             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_GREEN))
3504         elif color == CYAN: 
3505             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_CYAN))
3506         elif color == RED: 
3507             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_RED))
3508         elif color == MAGENTA: 
3509             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_MAGENTA))
3510         elif color == BROWN: 
3511             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_YELLOW))
3512         elif color == LIGHTGRAY: 
3513             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_WHITE))
3514         elif color == DARKGRAY: 
3515             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_BLACK) | curses.A_BOLD)
3516         elif color == LIGHTBLUE: 
3517             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_BLUE) | curses.A_BOLD)
3518         elif color == LIGHTGREEN: 
3519             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_GREEN) | curses.A_BOLD)
3520         elif color == LIGHTCYAN: 
3521             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_CYAN) | curses.A_BOLD)
3522         elif color == LIGHTRED: 
3523             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_RED) | curses.A_BOLD)
3524         elif color == LIGHTMAGENTA: 
3525             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_MAGENTA) | curses.A_BOLD)
3526         elif color == YELLOW: 
3527             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_YELLOW) | curses.A_BOLD)
3528         elif color == WHITE:
3529             curwnd.attron(curses.COLOR_PAIR(curses.COLOR_WHITE) | curses.A_BOLD)
3530
3531 def highvideo():
3532     "Set highlight video, if this is reasonable."
3533     if game.options & OPTION_CURSES:
3534         curwnd.attron(curses.A_REVERSE)
3535  
3536 def commandhook(cmd, before):
3537     pass
3538
3539 #
3540 # Things past this point have policy implications.
3541
3542
3543 def drawmaps(mode):
3544     "Hook to be called after moving to redraw maps."
3545     if game.options & OPTION_CURSES:
3546         if mode == 1:
3547             sensor()
3548         setwnd(srscan_window)
3549         curwnd.move(0, 0)
3550         srscan()
3551         if mode != 2:
3552             setwnd(status_window)
3553             status_window.clear()
3554             status_window.move(0, 0)
3555             setwnd(report_window)
3556             report_window.clear()
3557             report_window.move(0, 0)
3558             status(0)
3559             setwnd(lrscan_window)
3560             lrscan_window.clear()
3561             lrscan_window.move(0, 0)
3562             lrscan()
3563
3564 def put_srscan_sym(w, sym):
3565     "Emit symbol for short-range scan."
3566     srscan_window.move(w.x+1, w.y*2+2)
3567     srscan_window.addch(sym)
3568     srscan_window.refresh()
3569
3570 def boom(w):
3571     "Enemy fall down, go boom."  
3572     if game.options & OPTION_CURSES:
3573         drawmaps(2)
3574         setwnd(srscan_window)
3575         srscan_window.attron(curses.A_REVERSE)
3576         put_srscan_sym(w, game.quad[w.x][w.y])
3577         #sound(500)
3578         #delay(1000)
3579         #nosound()
3580         srscan_window.attroff(curses.A_REVERSE)
3581         put_srscan_sym(w, game.quad[w.x][w.y])
3582         curses.delay_output(500)
3583         setwnd(message_window) 
3584
3585 def warble():
3586     "Sound and visual effects for teleportation."
3587     if game.options & OPTION_CURSES:
3588         drawmaps(2)
3589         setwnd(message_window)
3590         #sound(50)
3591     prouts("     . . . . .     ")
3592     if game.options & OPTION_CURSES:
3593         #curses.delay_output(1000)
3594         #nosound()
3595         pass
3596
3597 def tracktorpedo(w, l, i, n, iquad):
3598     "Torpedo-track animation." 
3599     if not game.options & OPTION_CURSES:
3600         if l == 1:
3601             if n != 1:
3602                 skip(1)
3603                 proutn(_("Track for torpedo number %d-  ") % i)
3604             else:
3605                 skip(1)
3606                 proutn(_("Torpedo track- "))
3607         elif l==4 or l==9: 
3608             skip(1)
3609         proutn("%d - %d   " % (w.x, w.y))
3610     else:
3611         if not damaged(DSRSENS) or game.condition=="docked":
3612             if i != 1 and l == 1:
3613                 drawmaps(2)
3614                 curses.delay_output(400)
3615             if (iquad==IHDOT) or (iquad==IHBLANK):
3616                 put_srscan_sym(w, '+')
3617                 #sound(l*10)
3618                 #curses.delay_output(100)
3619                 #nosound()
3620                 put_srscan_sym(w, iquad)
3621             else:
3622                 curwnd.attron(curses.A_REVERSE)
3623                 put_srscan_sym(w, iquad)
3624                 #sound(500)
3625                 #curses.delay_output(1000)
3626                 #nosound()
3627                 curwnd.attroff(curses.A_REVERSE)
3628                 put_srscan_sym(w, iquad)
3629         else:
3630             proutn("%d - %d   " % (w.x, w.y))
3631
3632 def makechart():
3633     "Display the current galaxy chart."
3634     if game.options & OPTION_CURSES:
3635         setwnd(message_window)
3636         message_window.clear()
3637     chart()
3638     if game.options & OPTION_TTY:
3639         skip(1)
3640
3641 NSYM    = 14
3642
3643 def prstat(txt, data):
3644     proutn(txt)
3645     if game.options & OPTION_CURSES:
3646         skip(1)
3647         setwnd(status_window)
3648     else:
3649         proutn(" " * NSYM - len(tx))
3650     vproutn(data)
3651     skip(1)
3652     if game.options & OPTION_CURSES:
3653         setwnd(report_window)
3654
3655 # Code from moving.c begins here
3656
3657 def imove(novapush):
3658     # movement execution for warp, impulse, supernova, and tractor-beam events 
3659     w = coord(); final = coord()
3660     trbeam = False
3661
3662     def no_quad_change():
3663         # No quadrant change -- compute new avg enemy distances 
3664         game.quad[game.sector.x][game.sector.y] = game.ship
3665         if game.nenhere:
3666             for m in range(1, game.nenhere+1):
3667                 finald = distance(w, game.ks[m])
3668                 game.kavgd[m] = 0.5 * (finald+game.kdist[m])
3669                 game.kdist[m] = finald
3670             sortklings()
3671             if not game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
3672                 attack(False)
3673             for m in range(1, game.nenhere+1):
3674                 game.kavgd[m] = game.kdist[m]
3675         newcnd()
3676         drawmaps(0)
3677         setwnd(message_window)
3678
3679     w.x = w.y = 0
3680     if game.inorbit:
3681         prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
3682         game.inorbit = False
3683
3684     angle = ((15.0 - game.direc) * 0.5235988)
3685     deltax = -math.sin(angle)
3686     deltay = math.cos(angle)
3687     if math.fabs(deltax) > math.fabs(deltay):
3688         bigger = math.fabs(deltax)
3689     else:
3690         bigger = math.fabs(deltay)
3691                 
3692     deltay /= bigger
3693     deltax /= bigger
3694
3695     # If tractor beam is to occur, don't move full distance 
3696     if game.state.date+game.optime >= scheduled(FTBEAM):
3697         trbeam = True
3698         game.condition = "red"
3699         game.dist = game.dist*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
3700         game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
3701     # Move within the quadrant 
3702     game.quad[game.sector.x][game.sector.y] = IHDOT
3703     x = game.sector.x
3704     y = game.sector.y
3705     n = 10.0*game.dist*bigger+0.5
3706
3707     if n > 0:
3708         for m in range(1, n+1):
3709             x += deltax
3710             y += deltay
3711             w.x = x + 0.5
3712             w.y = y + 0.5
3713             if not VALID_SECTOR(w.x, w.y):
3714                 # Leaving quadrant -- allow final enemy attack 
3715                 # Don't do it if being pushed by Nova 
3716                 if game.nenhere != 0 and not novapush:
3717                     newcnd()
3718                     for m in range(1, game.nenhere+1):
3719                         finald = distance(w, game.ks[m])
3720                         game.kavgd[m] = 0.5 * (finald + game.kdist[m])
3721                     #
3722                     # Stas Sergeev added the condition
3723                     # that attacks only happen if Klingons
3724                     # are present and your skill is good.
3725                     # 
3726                     if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
3727                         attack(False)
3728                     if game.alldone:
3729                         return
3730                 # compute final position -- new quadrant and sector 
3731                 x = QUADSIZE*(game.quadrant.x-1)+game.sector.x
3732                 y = QUADSIZE*(game.quadrant.y-1)+game.sector.y
3733                 w.x = x+10.0*game.dist*bigger*deltax+0.5
3734                 w.y = y+10.0*game.dist*bigger*deltay+0.5
3735                 # check for edge of galaxy 
3736                 kinks = 0
3737                 while True:
3738                     kink = 0
3739                     if w.x <= 0:
3740                         w.x = -w.x + 1
3741                         kink = 1
3742                     if w.y <= 0:
3743                         w.y = -w.y + 1
3744                         kink = 1
3745                     if w.x > GALSIZE*QUADSIZE:
3746                         w.x = (GALSIZE*QUADSIZE*2)+1 - w.x
3747                         kink = 1
3748                     if w.y > GALSIZE*QUADSIZE:
3749                         w.y = (GALSIZE*QUADSIZE*2)+1 - w.y
3750                         kink = 1
3751                     if kink:
3752                         kinks = 1
3753                 if not kink:
3754                     break
3755
3756                 if kinks:
3757                     game.nkinks += 1
3758                     if game.nkinks == 3:
3759                         # Three strikes -- you're out! 
3760                         finish(FNEG3)
3761                         return
3762                     skip(1)
3763                     prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
3764                     prout(_("AT THE EDGE OF THE GALAXY.  THE THIRD TIME YOU TRY THIS,"))
3765                     prout(_("YOU WILL BE DESTROYED."))
3766                 # Compute final position in new quadrant 
3767                 if trbeam: # Don't bother if we are to be beamed 
3768                     return
3769                 game.quadrant.x = (w.x+(QUADSIZE-1))/QUADSIZE
3770                 game.quadrant.y = (w.y+(QUADSIZE-1))/QUADSIZE
3771                 game.sector.x = w.x - QUADSIZE*(game.quadrant.x-1)
3772                 game.sector.y = w.y - QUADSIZE*(game.quadrant.y-1)
3773                 skip(1)
3774                 prout(_("Entering Quadrant %s.") % game.quadrant)
3775                 game.quad[game.sector.x][game.sector.y] = game.ship
3776                 newqad(False)
3777                 if game.skill>SKILL_NOVICE:
3778                     attack(False)  
3779                 return
3780             iquad = game.quad[w.x][w.y]
3781             if iquad != IHDOT:
3782                 # object encountered in flight path 
3783                 stopegy = 50.0*game.dist/game.optime
3784                 game.dist = distance(game.sector, w) / (QUADSIZE * 1.0)
3785                 if iquad in (IHT. IHK, OHC, IHS, IHR, IHQUEST):
3786                     game.sector = w
3787                     ram(False, iquad, game.sector)
3788                     final = game.sector
3789                 elif iquad == IHBLANK:
3790                     skip(1)
3791                     prouts(_("***RED ALERT!  RED ALERT!"))
3792                     skip(1)
3793                     proutn("***")
3794                     crmshp()
3795                     proutn(_(" pulled into black hole at Sector %s") % w)
3796                     #
3797                     # Getting pulled into a black hole was certain
3798                     # death in Almy's original.  Stas Sergeev added a
3799                     # possibility that you'll get timewarped instead.
3800                     # 
3801                     n=0
3802                     for m in range(0, NDEVICES):
3803                         if game.damage[m]>0: 
3804                             n += 1
3805                     probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
3806                     if (game.options & OPTION_BLKHOLE) and Rand()>probf: 
3807                         timwrp()
3808                     else: 
3809                         finish(FHOLE)
3810                     return
3811                 else:
3812                     # something else 
3813                     skip(1)
3814                     crmshp()
3815                     if iquad == IHWEB:
3816                         proutn(_(" encounters Tholian web at %s;") % w)
3817                     else:
3818                         proutn(_(" blocked by object at %s;") % w)
3819                     proutn(_("Emergency stop required "))
3820                     prout(_("%2d units of energy.") % int(stopegy))
3821                     game.energy -= stopegy
3822                     final.x = x-deltax+0.5
3823                     final.y = y-deltay+0.5
3824                     game.sector = final
3825                     if game.energy <= 0:
3826                         finish(FNRG)
3827                         return
3828                 # We're here!
3829                 no_quad_change()
3830                 return
3831         game.dist = distance(game.sector, w) / (QUADSIZE * 1.0)
3832         game.sector = w
3833     final = game.sector
3834     no_quad_change()
3835     return
3836
3837 def dock(verbose):
3838     # dock our ship at a starbase 
3839     chew()
3840     if game.condition == "docked" and verbose:
3841         prout(_("Already docked."))
3842         return
3843     if game.inorbit:
3844         prout(_("You must first leave standard orbit."))
3845         return
3846     if not is_valid(game.base) or abs(game.sector.x-game.base.x) > 1 or abs(game.sector.y-game.base.y) > 1:
3847         crmshp()
3848         prout(_(" not adjacent to base."))
3849         return
3850     game.condition = "docked"
3851     if "verbose":
3852         prout(_("Docked."))
3853     game.ididit = True
3854     if game.energy < game.inenrg:
3855         game.energy = game.inenrg
3856     game.shield = game.inshld
3857     game.torps = game.intorps
3858     game.lsupres = game.inlsr
3859     game.state.crew = FULLCREW
3860     if not damaged(DRADIO) and \
3861         ((is_scheduled(FCDBAS) or game.isatb == 1) and not game.iseenit):
3862         # get attack report from base 
3863         prout(_("Lt. Uhura- \"Captain, an important message from the starbase:\""))
3864         attackreport(False)
3865         game.iseenit = True
3866
3867
3868 # This program originally required input in terms of a (clock)
3869 # direction and distance. Somewhere in history, it was changed to
3870 # cartesian coordinates. So we need to convert.  Probably
3871 # "manual" input should still be done this way -- it's a real
3872 # pain if the computer isn't working! Manual mode is still confusing
3873 # because it involves giving x and y motions, yet the coordinates
3874 # are always displayed y - x, where +y is downward!
3875
3876
3877 def getcd(isprobe, akey):
3878     # get course and distance 
3879     irowq=game.quadrant.x; icolq=game.quadrant.y; key=0
3880     navmode = "unspecified"
3881     itemp = "curt"
3882     incr = coord()
3883     iprompt = False
3884
3885     # Get course direction and distance. If user types bad values, return
3886     # with DIREC = -1.0.
3887     game.direc = -1.0
3888         
3889     if game.landed and not isprobe:
3890         prout(_("Dummy! You can't leave standard orbit until you"))
3891         proutn(_("are back aboard the ship."))
3892         chew()
3893         return
3894     while navmode == "unspecified":
3895         if damaged(DNAVSYS):
3896             if isprobe:
3897                 prout(_("Computer damaged; manual navigation only"))
3898             else:
3899                 prout(_("Computer damaged; manual movement only"))
3900             chew()
3901             navmode = "manual"
3902             key = IHEOL
3903             break
3904         if isprobe and akey != -1:
3905             # For probe launch, use pre-scanned value first time 
3906             key = akey
3907             akey = -1
3908         else: 
3909             key = scan()
3910
3911         if key == IHEOL:
3912             proutn(_("Manual or automatic- "))
3913             iprompt = True
3914             chew()
3915         elif key == IHALPHA:
3916             if isit("manual"):
3917                 navmode = "manual"
3918                 key = scan()
3919                 break
3920             elif isit("automatic"):
3921                 navmode = "automatic"
3922                 key = scan()
3923                 break
3924             else:
3925                 huh()
3926                 chew()
3927                 return
3928         else: # numeric 
3929             if isprobe:
3930                 prout(_("(Manual navigation assumed.)"))
3931             else:
3932                 prout(_("(Manual movement assumed.)"))
3933             navmode = "manual"
3934             break
3935
3936     if navmode == "automatic":
3937         while key == IHEOL:
3938             if isprobe:
3939                 proutn(_("Target quadrant or quadrant&sector- "))
3940             else:
3941                 proutn(_("Destination sector or quadrant&sector- "))
3942             chew()
3943             iprompt = True
3944             key = scan()
3945
3946         if key != IHREAL:
3947             huh()
3948             return
3949         xi = aaitem
3950         key = scan()
3951         if key != IHREAL:
3952             huh()
3953             return
3954         xj = aaitem
3955         key = scan()
3956         if key == IHREAL:
3957             # both quadrant and sector specified 
3958             xk = aaitem
3959             key = scan()
3960             if key != IHREAL:
3961                 huh()
3962                 return
3963             xl = aaitem
3964
3965             irowq = xi + 0.5
3966             icolq = xj + 0.5
3967             incr.y = xk + 0.5
3968             incr.x = xl + 0.5
3969         else:
3970             if isprobe:
3971                 # only quadrant specified -- go to center of dest quad 
3972                 irowq = xi + 0.5
3973                 icolq = xj + 0.5
3974                 incr.y = incr.x = 5
3975             else:
3976                 incr.y = xi + 0.5
3977                 incr.x = xj + 0.5
3978             itemp = "normal"
3979         if not VALID_QUADRANT(icolq,irowq) or not VALID_SECTOR(incr.x,incr.y):
3980             huh()
3981             return
3982         skip(1)
3983         if not isprobe:
3984             if itemp > "curt":
3985                 if iprompt:
3986                     prout(_("Helmsman Sulu- \"Course locked in for Sector %s.\"") % incr)
3987             else:
3988                 prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
3989         deltax = icolq - game.quadrant.y + 0.1*(incr.x-game.sector.y)
3990         deltay = game.quadrant.x - irowq + 0.1*(game.sector.x-incr.y)
3991     else: # manual 
3992         while key == IHEOL:
3993             proutn(_("X and Y displacements- "))
3994             chew()
3995             iprompt = True
3996             key = scan()
3997         itemp = "verbose"
3998         if key != IHREAL:
3999             huh()
4000             return
4001         deltax = aaitem
4002         key = scan()
4003         if key != IHREAL:
4004             huh()
4005             return
4006         deltay = aaitem
4007     # Check for zero movement 
4008     if deltax == 0 and deltay == 0:
4009         chew()
4010         return
4011     if itemp == "verbose" and not isprobe:
4012         skip(1)
4013         prout(_("Helmsman Sulu- \"Aye, Sir.\""))
4014     game.dist = math.sqrt(deltax*deltax + deltay*deltay)
4015     game.direc = math.atan2(deltax, deltay)*1.90985932
4016     if game.direc < 0.0:
4017         game.direc += 12.0
4018     chew()
4019     return
4020
4021 def impulse():
4022     # move under impulse power 
4023     game.ididit = False
4024     if damaged(DIMPULS):
4025         chew()
4026         skip(1)
4027         prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
4028         return
4029     if game.energy > 30.0:
4030         getcd(False, 0)
4031         if game.direc == -1.0:
4032             return
4033         power = 20.0 + 100.0*game.dist
4034     else:
4035         power = 30.0
4036
4037     if power >= game.energy:
4038         # Insufficient power for trip 
4039         skip(1)
4040         prout(_("First Officer Spock- \"Captain, the impulse engines"))
4041         prout(_("require 20.0 units to engage, plus 100.0 units per"))
4042         if game.energy > 30:
4043             proutn(_("quadrant.  We can go, therefore, a maximum of %d") %
4044                      int(0.01 * (game.energy-20.0)-0.05))
4045             prout(_(" quadrants.\""))
4046         else:
4047             prout(_("quadrant.  They are, therefore, useless.\""))
4048         chew()
4049         return
4050     # Make sure enough time is left for the trip 
4051     game.optime = game.dist/0.095
4052     if game.optime >= game.state.remtime:
4053         prout(_("First Officer Spock- \"Captain, our speed under impulse"))
4054         prout(_("power is only 0.95 sectors per stardate. Are you sure"))
4055         proutn(_("we dare spend the time?\" "))
4056         if ja() == False:
4057             return
4058     # Activate impulse engines and pay the cost 
4059     imove(False)
4060     game.ididit = True
4061     if game.alldone:
4062         return
4063     power = 20.0 + 100.0*game.dist
4064     game.energy -= power
4065     game.optime = game.dist/0.095
4066     if game.energy <= 0:
4067         finish(FNRG)
4068     return
4069
4070 def warp(timewarp):
4071     # move under warp drive 
4072     blooey = False; twarp = False
4073     if not timewarp: # Not WARPX entry 
4074         game.ididit = False
4075         if game.damage[DWARPEN] > 10.0:
4076             chew()
4077             skip(1)
4078             prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
4079             return
4080         if damaged(DWARPEN) and game.warpfac > 4.0:
4081             chew()
4082             skip(1)
4083             prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
4084             prout(_("  is repaired, I can only give you warp 4.\""))
4085             return
4086                         
4087         # Read in course and distance 
4088         getcd(False, 0)
4089         if game.direc == -1.0:
4090             return
4091
4092         # Make sure starship has enough energy for the trip 
4093         power = (game.dist+0.05)*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
4094         if power >= game.energy:
4095             # Insufficient power for trip 
4096             game.ididit = False
4097             skip(1)
4098             prout(_("Engineering to bridge--"))
4099             if not game.shldup or 0.5*power > game.energy:
4100                 iwarp = math.pow((game.energy/(game.dist+0.05)), 0.333333333)
4101                 if iwarp <= 0:
4102                     prout(_("We can't do it, Captain. We don't have enough energy."))
4103                 else:
4104                     proutn(_("We don't have enough energy, but we could do it at warp %d") % iwarp)
4105                     if game.shldup:
4106                         prout(",")
4107                         prout(_("if you'll lower the shields."))
4108                     else:
4109                         prout(".")
4110             else:
4111                 prout(_("We haven't the energy to go that far with the shields up."))
4112             return
4113                                                 
4114         # Make sure enough time is left for the trip 
4115         game.optime = 10.0*game.dist/game.wfacsq
4116         if game.optime >= 0.8*game.state.remtime:
4117             skip(1)
4118             prout(_("First Officer Spock- \"Captain, I compute that such"))
4119             proutn(_("  a trip would require approximately %2.0f") %
4120                    (100.0*game.optime/game.state.remtime))
4121             prout(_(" percent of our"))
4122             proutn(_("  remaining time.  Are you sure this is wise?\" "))
4123             if ja() == False:
4124                 game.ididit = False
4125                 game.optime=0 
4126                 return
4127     # Entry WARPX 
4128     if game.warpfac > 6.0:
4129         # Decide if engine damage will occur 
4130         prob = game.dist*(6.0-game.warpfac)*(6.0-game.warpfac)/66.666666666
4131         if prob > Rand():
4132             blooey = True
4133             game.dist = Rand()*game.dist
4134         # Decide if time warp will occur 
4135         if 0.5*game.dist*math.pow(7.0,game.warpfac-10.0) > Rand():
4136             twarp = True
4137         if idebug and game.warpfac==10 and not twarp:
4138             blooey = False
4139             proutn("=== Force time warp? ")
4140             if ja() == True:
4141                 twarp = True
4142         if blooey or twarp:
4143             # If time warp or engine damage, check path 
4144             # If it is obstructed, don't do warp or damage 
4145             angle = ((15.0-game.direc)*0.5235998)
4146             deltax = -math.sin(angle)
4147             deltay = math.cos(angle)
4148             if math.fabs(deltax) > math.fabs(deltay):
4149                 bigger = math.fabs(deltax)
4150             else:
4151                 bigger = math.fabs(deltay)
4152                         
4153             deltax /= bigger
4154             deltay /= bigger
4155             n = 10.0 * game.dist * bigger +0.5
4156             x = game.sector.x
4157             y = game.sector.y
4158             for l in range(1, n+1):
4159                 x += deltax
4160                 ix = x + 0.5
4161                 y += deltay
4162                 iy = y +0.5
4163                 if not VALID_SECTOR(ix, iy):
4164                     break
4165                 if game.quad[ix][iy] != IHDOT:
4166                     blooey = False
4167                     twarp = False
4168                                 
4169
4170     # Activate Warp Engines and pay the cost 
4171     imove(False)
4172     if game.alldone:
4173         return
4174     game.energy -= game.dist*game.warpfac*game.warpfac*game.warpfac*(game.shldup+1)
4175     if game.energy <= 0:
4176         finish(FNRG)
4177     game.optime = 10.0*game.dist/game.wfacsq
4178     if twarp:
4179         timwrp()
4180     if blooey:
4181         game.damage[DWARPEN] = game.damfac*(3.0*Rand()+1.0)
4182         skip(1)
4183         prout(_("Engineering to bridge--"))
4184         prout(_("  Scott here.  The warp engines are damaged."))
4185         prout(_("  We'll have to reduce speed to warp 4."))
4186     game.ididit = True
4187     return
4188
4189 def setwarp():
4190     # change the warp factor    
4191     while True:
4192         key=scan()
4193         if key != IHEOL:
4194             break
4195         chew()
4196         proutn(_("Warp factor- "))
4197     chew()
4198     if key != IHREAL:
4199         huh()
4200         return
4201     if game.damage[DWARPEN] > 10.0:
4202         prout(_("Warp engines inoperative."))
4203         return
4204     if damaged(DWARPEN) and aaitem > 4.0:
4205         prout(_("Engineer Scott- \"I'm doing my best, Captain,"))
4206         prout(_("  but right now we can only go warp 4.\""))
4207         return
4208     if aaitem > 10.0:
4209         prout(_("Helmsman Sulu- \"Our top speed is warp 10, Captain.\""))
4210         return
4211     if aaitem < 1.0:
4212         prout(_("Helmsman Sulu- \"We can't go below warp 1, Captain.\""))
4213         return
4214     oldfac = game.warpfac
4215     game.warpfac = aaitem
4216     game.wfacsq=game.warpfac*game.warpfac
4217     if game.warpfac <= oldfac or game.warpfac <= 6.0:
4218         prout(_("Helmsman Sulu- \"Warp factor %d, Captain.\"") %
4219                int(game.warpfac))
4220         return
4221     if game.warpfac < 8.00:
4222         prout(_("Engineer Scott- \"Aye, but our maximum safe speed is warp 6.\""))
4223         return
4224     if game.warpfac == 10.0:
4225         prout(_("Engineer Scott- \"Aye, Captain, we'll try it.\""))
4226         return
4227     prout(_("Engineer Scott- \"Aye, Captain, but our engines may not take it.\""))
4228     return
4229
4230 def atover(igrab):
4231     # cope with being tossed out of quadrant by supernova or yanked by beam 
4232
4233     chew()
4234     # is captain on planet? 
4235     if game.landed:
4236         if damaged(DTRANSP):
4237             finish(FPNOVA)
4238             return
4239         prout(_("Scotty rushes to the transporter controls."))
4240         if game.shldup:
4241             prout(_("But with the shields up it's hopeless."))
4242             finish(FPNOVA)
4243         prouts(_("His desperate attempt to rescue you . . ."))
4244         if Rand() <= 0.5:
4245             prout(_("fails."))
4246             finish(FPNOVA)
4247             return
4248         prout(_("SUCCEEDS!"))
4249         if game.imine:
4250             game.imine = False
4251             proutn(_("The crystals mined were "))
4252             if Rand() <= 0.25:
4253                 prout(_("lost."))
4254             else:
4255                 prout(_("saved."))
4256                 game.icrystl = True
4257     if igrab:
4258         return
4259
4260     # Check to see if captain in shuttle craft 
4261     if game.icraft:
4262         finish(FSTRACTOR)
4263     if game.alldone:
4264         return
4265
4266     # Inform captain of attempt to reach safety 
4267     skip(1)
4268     while True:
4269         if game.justin:
4270             prouts(_("***RED ALERT!  RED ALERT!"))
4271             skip(1)
4272             proutn(_("The "))
4273             crmshp()
4274             prout(_(" has stopped in a quadrant containing"))
4275             prouts(_("   a supernova."))
4276             skip(2)
4277         proutn(_("***Emergency automatic override attempts to hurl "))
4278         crmshp()
4279         skip(1)
4280         prout(_("safely out of quadrant."))
4281         if not damaged(DRADIO):
4282             game.state.galaxy[game.quadrant.x][game.quadrant.y].charted = True
4283         # Try to use warp engines 
4284         if damaged(DWARPEN):
4285             skip(1)
4286             prout(_("Warp engines damaged."))
4287             finish(FSNOVAED)
4288             return
4289         game.warpfac = 6.0+2.0*Rand()
4290         game.wfacsq = game.warpfac * game.warpfac
4291         prout(_("Warp factor set to %d") % int(game.warpfac))
4292         power = 0.75*game.energy
4293         game.dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1))
4294         distreq = 1.4142+Rand()
4295         if distreq < game.dist:
4296             game.dist = distreq
4297         game.optime = 10.0*game.dist/game.wfacsq
4298         game.direc = 12.0*Rand()        # How dumb! 
4299         game.justin = False
4300         game.inorbit = False
4301         warp(True)
4302         if not game.justin:
4303             # This is bad news, we didn't leave quadrant. 
4304             if game.alldone:
4305                 return
4306             skip(1)
4307             prout(_("Insufficient energy to leave quadrant."))
4308             finish(FSNOVAED)
4309             return
4310         # Repeat if another snova
4311         if not game.state.galaxy[game.quadrant.x][game.quadrant.y].supernova:
4312             break
4313     if (game.state.remkl + game.state.remcom + game.state.nscrem)==0: 
4314         finish(FWON) # Snova killed remaining enemy. 
4315
4316 def timwrp():
4317     # let's do the time warp again 
4318     prout(_("***TIME WARP ENTERED."))
4319     if game.state.snap and Rand() < 0.5:
4320         # Go back in time 
4321         prout(_("You are traveling backwards in time %d stardates.") %
4322               int(game.state.date-game.snapsht.date))
4323         game.state = game.snapsht
4324         game.state.snap = False
4325         if game.state.remcom:
4326             schedule(FTBEAM, expran(game.intime/game.state.remcom))
4327             schedule(FBATTAK, expran(0.3*game.intime))
4328         schedule(FSNOVA, expran(0.5*game.intime))
4329         # next snapshot will be sooner 
4330         schedule(FSNAP, expran(0.25*game.state.remtime))
4331                                 
4332         if game.state.nscrem:
4333             schedule(FSCMOVE, 0.2777)       
4334         game.isatb = 0
4335         unschedule(FCDBAS)
4336         unschedule(FSCDBAS)
4337         invalidate(game.battle)
4338
4339         # Make sure Galileo is consistant -- Snapshot may have been taken
4340         # when on planet, which would give us two Galileos! 
4341         gotit = False
4342         for l in range(game.inplan):
4343             if game.state.planets[l].known == "shuttle_down":
4344                 gotit = True
4345                 if game.iscraft == "onship" and game.ship==IHE:
4346                     prout(_("Chekov-  \"Security reports the Galileo has disappeared, Sir!"))
4347                     game.iscraft = "offship"
4348         # Likewise, if in the original time the Galileo was abandoned, but
4349         # was on ship earlier, it would have vanished -- let's restore it.
4350         if game.iscraft == "offship" and not gotit and game.damage[DSHUTTL] >= 0.0:
4351             prout(_("Checkov-  \"Security reports the Galileo has reappeared in the dock!\""))
4352             game.iscraft = "onship"
4353         # 
4354 #        * There used to be code to do the actual reconstrction here,
4355 #        * but the starchart is now part of the snapshotted galaxy state.
4356 #        
4357         prout(_("Spock has reconstructed a correct star chart from memory"))
4358     else:
4359         # Go forward in time 
4360         game.optime = -0.5*game.intime*math.log(Rand())
4361         prout(_("You are traveling forward in time %d stardates.") % int(game.optime))
4362         # cheat to make sure no tractor beams occur during time warp 
4363         postpone(FTBEAM, game.optime)
4364         game.damage[DRADIO] += game.optime
4365     newqad(False)
4366     events()    # Stas Sergeev added this -- do pending events 
4367
4368 def probe():
4369     # launch deep-space probe 
4370     # New code to launch a deep space probe 
4371     if game.nprobes == 0:
4372         chew()
4373         skip(1)
4374         if game.ship == IHE: 
4375             prout(_("Engineer Scott- \"We have no more deep space probes, Sir.\""))
4376         else:
4377             prout(_("Ye Faerie Queene has no deep space probes."))
4378         return
4379     if damaged(DDSP):
4380         chew()
4381         skip(1)
4382         prout(_("Engineer Scott- \"The probe launcher is damaged, Sir.\""))
4383         return
4384     if is_scheduled(FDSPROB):
4385         chew()
4386         skip(1)
4387         if damaged(DRADIO) and game.condition != "docked":
4388             prout(_("Spock-  \"Records show the previous probe has not yet"))
4389             prout(_("   reached its destination.\""))
4390         else:
4391             prout(_("Uhura- \"The previous probe is still reporting data, Sir.\""))
4392         return
4393     key = scan()
4394
4395     if key == IHEOL:
4396         # slow mode, so let Kirk know how many probes there are left
4397         if game.nprobes == 1:
4398             prout(_("1 probe left."))
4399         else:
4400             prout(_("%d probes left") % game.nprobes)
4401         proutn(_("Are you sure you want to fire a probe? "))
4402         if ja() == False:
4403             return
4404
4405     game.isarmed = False
4406     if key == IHALPHA and citem == "armed":
4407         game.isarmed = True
4408         key = scan()
4409     elif key == IHEOL:
4410         proutn(_("Arm NOVAMAX warhead? "))
4411         game.isarmed = ja()
4412     getcd(True, key)
4413     if game.direc == -1.0:
4414         return
4415     game.nprobes -= 1
4416     angle = ((15.0 - game.direc) * 0.5235988)
4417     game.probeinx = -math.sin(angle)
4418     game.probeiny = math.cos(angle)
4419     if math.fabs(game.probeinx) > math.fabs(game.probeiny):
4420         bigger = math.fabs(game.probeinx)
4421     else:
4422         bigger = math.fabs(game.probeiny)
4423                 
4424     game.probeiny /= bigger
4425     game.probeinx /= bigger
4426     game.proben = 10.0*game.dist*bigger +0.5
4427     game.probex = game.quadrant.x*QUADSIZE + game.sector.x - 1  # We will use better packing than original
4428     game.probey = game.quadrant.y*QUADSIZE + game.sector.y - 1
4429     game.probec = game.quadrant
4430     schedule(FDSPROB, 0.01) # Time to move one sector
4431     prout(_("Ensign Chekov-  \"The deep space probe is launched, Captain.\""))
4432     game.ididit = True
4433     return
4434
4435 # Here's how the mayday code works:
4436
4437 # First, the closest starbase is selected.  If there is a a starbase
4438 # in your own quadrant, you are in good shape.  This distance takes
4439 # quadrant distances into account only.
4440 #
4441 # A magic number is computed based on the distance which acts as the
4442 # probability that you will be rematerialized.  You get three tries.
4443 #
4444 # When it is determined that you should be able to be rematerialized
4445 # (i.e., when the probability thing mentioned above comes up
4446 # positive), you are put into that quadrant (anywhere).  Then, we try
4447 # to see if there is a spot adjacent to the star- base.  If not, you
4448 # can't be rematerialized!!!  Otherwise, it drops you there.  It only
4449 # tries five times to find a spot to drop you.  After that, it's your
4450 # problem.
4451
4452 def mayday():
4453     # yell for help from nearest starbase 
4454     # There's more than one way to move in this game! 
4455     line = 0
4456
4457     chew()
4458     # Test for conditions which prevent calling for help 
4459     if game.condition == "docked":
4460         prout(_("Lt. Uhura-  \"But Captain, we're already docked.\""))
4461         return
4462     if damaged(DRADIO):
4463         prout(_("Subspace radio damaged."))
4464         return
4465     if game.state.rembase==0:
4466         prout(_("Lt. Uhura-  \"Captain, I'm not getting any response from Starbase.\""))
4467         return
4468     if game.landed:
4469         proutn(_("You must be aboard the "))
4470         crmshp()
4471         prout(".")
4472         return
4473     # OK -- call for help from nearest starbase 
4474     game.nhelp += 1
4475     if game.base.x!=0:
4476         # There's one in this quadrant 
4477         ddist = distance(game.base, game.sector)
4478     else:
4479         ddist = FOREVER
4480         for m in range(1, game.state.rembase+1):
4481             xdist = QUADSIZE * distance(game.state.baseq[m], game.quadrant)
4482             if xdist < ddist:
4483                 ddist = xdist
4484                 line = m
4485         # Since starbase not in quadrant, set up new quadrant 
4486         game.quadrant = game.state.baseq[line]
4487         newqad(True)
4488     # dematerialize starship 
4489     game.quad[game.sector.x][game.sector.y]=IHDOT
4490     proutn(_("Starbase in Quadrant %s responds--") % game.quadrant)
4491     crmshp()
4492     prout(_(" dematerializes."))
4493     game.sector.x=0
4494     for m in range(1, 5+1):
4495         ix = game.base.x+3.0*Rand()-1
4496         iy = game.base.y+3.0*Rand()-1
4497         if VALID_SECTOR(ix,iy) and game.quad[ix][iy]==IHDOT:
4498             # found one -- finish up 
4499             game.sector.x=ix
4500             game.sector.y=iy
4501             break
4502     if not is_valid(game.sector):
4503         prout(_("You have been lost in space..."))
4504         finish(FMATERIALIZE)
4505         return
4506     # Give starbase three chances to rematerialize starship 
4507     probf = math.pow((1.0 - math.pow(0.98,ddist)), 0.33333333)
4508     for m in range(1, 3+1):
4509         if m == 1: proutn(_("1st"))
4510         elif m == 2: proutn(_("2nd"))
4511         elif m == 3: proutn(_("3rd"))
4512         proutn(_(" attempt to re-materialize "))
4513         crmshp()
4514         game.quad[ix][iy]=(IHMATER0,IHMATER1,IHMATER2)[m-1]
4515         textcolor(RED)
4516         warble()
4517         if Rand() > probf:
4518             break
4519         prout(_("fails."))
4520         curses.delay_output(500)
4521         textcolor(DEFAULT)
4522     if m > 3:
4523         game.quad[ix][iy]=IHQUEST
4524         game.alive = False
4525         drawmaps(1)
4526         setwnd(message_window)
4527         finish(FMATERIALIZE)
4528         return
4529     game.quad[ix][iy]=game.ship
4530     textcolor(GREEN)
4531     prout(_("succeeds."))
4532     textcolor(DEFAULT)
4533     dock(False)
4534     skip(1)
4535     prout(_("Lt. Uhura-  \"Captain, we made it!\""))
4536
4537 # Abandon Ship (the BSD-Trek description)
4538
4539 # The ship is abandoned.  If your current ship is the Faire
4540 # Queene, or if your shuttlecraft is dead, you're out of
4541 # luck.  You need the shuttlecraft in order for the captain
4542 # (that's you!!) to escape.
4543
4544 # Your crew can beam to an inhabited starsystem in the
4545 # quadrant, if there is one and if the transporter is working.
4546 # If there is no inhabited starsystem, or if the transporter
4547 # is out, they are left to die in outer space.
4548
4549 # If there are no starbases left, you are captured by the
4550 # Klingons, who torture you mercilessly.  However, if there
4551 # is at least one starbase, you are returned to the
4552 # Federation in a prisoner of war exchange.  Of course, this
4553 # can't happen unless you have taken some prisoners.
4554
4555 def abandon():
4556     # abandon ship 
4557     chew()
4558     if game.condition=="docked":
4559         if game.ship!=IHE:
4560             prout(_("You cannot abandon Ye Faerie Queene."))
4561             return
4562     else:
4563         # Must take shuttle craft to exit 
4564         if game.damage[DSHUTTL]==-1:
4565             prout(_("Ye Faerie Queene has no shuttle craft."))
4566             return
4567         if game.damage[DSHUTTL]<0:
4568             prout(_("Shuttle craft now serving Big Macs."))
4569             return
4570         if game.damage[DSHUTTL]>0:
4571             prout(_("Shuttle craft damaged."))
4572             return
4573         if game.landed:
4574             prout(_("You must be aboard the ship."))
4575             return
4576         if game.iscraft != "onship":
4577             prout(_("Shuttle craft not currently available."))
4578             return
4579         # Print abandon ship messages 
4580         skip(1)
4581         prouts(_("***ABANDON SHIP!  ABANDON SHIP!"))
4582         skip(1)
4583         prouts(_("***ALL HANDS ABANDON SHIP!"))
4584         skip(2)
4585         prout(_("Captain and crew escape in shuttle craft."))
4586         if game.state.rembase==0:
4587             # Oops! no place to go... 
4588             finish(FABANDN)
4589             return
4590         q = game.state.galaxy[game.quadrant.x][game.quadrant.y]
4591         # Dispose of crew 
4592         if not (game.options & OPTION_WORLDS) and not damaged(DTRANSP):
4593             prout(_("Remainder of ship's complement beam down"))
4594             prout(_("to nearest habitable planet."))
4595         elif q.planet != NOPLANET and not damaged(DTRANSP):
4596             prout(_("Remainder of ship's complement beam down to %s.") %
4597                     q.planet)
4598         else:
4599             prout(_("Entire crew of %d left to die in outer space.") %
4600                     game.state.crew)
4601             game.casual += game.state.crew
4602             game.abandoned += game.state.crew
4603
4604         # If at least one base left, give 'em the Faerie Queene 
4605         skip(1)
4606         game.icrystl = False # crystals are lost 
4607         game.nprobes = 0 # No probes 
4608         prout(_("You are captured by Klingons and released to"))
4609         prout(_("the Federation in a prisoner-of-war exchange."))
4610         nb = Rand()*game.state.rembase+1
4611         # Set up quadrant and position FQ adjacient to base 
4612         if not same(game.quadrant, game.state.baseq[nb]):
4613             game.quadrant = game.state.baseq[nb]
4614             game.sector.x = game.sector.y = 5
4615             newqad(True)
4616         while True:
4617             # position next to base by trial and error 
4618             game.quad[game.sector.x][game.sector.y] = IHDOT
4619             for l in range(1, QUADSIZE+1):
4620                 game.sector.x = 3.0*Rand() - 1.0 + game.base.x
4621                 game.sector.y = 3.0*Rand() - 1.0 + game.base.y
4622                 if VALID_SECTOR(game.sector.x, game.sector.y) and \
4623                        game.quad[game.sector.x][game.sector.y] == IHDOT:
4624                     break
4625             if l < QUADSIZE+1:
4626                 break # found a spot 
4627             game.sector.x=QUADSIZE/2
4628             game.sector.y=QUADSIZE/2
4629             newqad(True)
4630     # Get new commission 
4631     game.quad[game.sector.x][game.sector.y] = game.ship = IHF
4632     game.state.crew = FULLCREW
4633     prout(_("Starfleet puts you in command of another ship,"))
4634     prout(_("the Faerie Queene, which is antiquated but,"))
4635     prout(_("still useable."))
4636     if game.icrystl:
4637         prout(_("The dilithium crystals have been moved."))
4638     game.imine = False
4639     game.iscraft = "offship" # Galileo disappears 
4640     # Resupply ship 
4641     game.condition="docked"
4642     for l in range(0, NDEVICES): 
4643         game.damage[l] = 0.0
4644     game.damage[DSHUTTL] = -1
4645     game.energy = game.inenrg = 3000.0
4646     game.shield = game.inshld = 1250.0
4647     game.torps = game.intorps = 6
4648     game.lsupres=game.inlsr=3.0
4649     game.shldup=False
4650     game.warpfac=5.0
4651     game.wfacsq=25.0
4652     return