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