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