3 sst.py -- Super Star Trek 2K
5 SST2K is a Python translation of a C translation of a FORTRAN
6 original dating back to 1973. Beautiful Python it is not, but it
7 works. Translation by Eric S. Raymond; original game by David Matuszek
8 and Paul Reynolds, with modifications by Don Smith, Tom Almy,
9 Stas Sergeev, and Eric S. Raymond.
11 See the doc/HACKING file in the distribution for designers notes and advice
12 on how to modify (and how not to modify!) this code.
14 import os, sys, math, curses, time, readline, cPickle, random, copy, gettext, getpass
18 docpath = (".", "../doc", "/usr/share/doc/sst")
20 def _(str): return gettext.gettext(str)
22 GALSIZE = 8 # Galaxy size in quadrants
23 NINHAB = (GALSIZE * GALSIZE / 2) # Number of inhabited worlds
24 MAXUNINHAB = 10 # Maximum uninhabited worlds
25 QUADSIZE = 10 # Quadrant size in sectors
26 BASEMIN = 2 # Minimum starbases
27 BASEMAX = (GALSIZE * GALSIZE / 12) # Maximum starbases
28 MAXKLGAME = 127 # Maximum Klingons per game
29 MAXKLQUAD = 9 # Maximum Klingons per quadrant
30 FULLCREW = 428 # Crew size. BSD Trek was 387, that's wrong
31 FOREVER = 1e30 # Time for the indefinite future
32 MAXBURST = 3 # Max # of torps you can launch in one turn
33 MINCMDR = 10 # Minimum number of Klingon commanders
34 DOCKFAC = 0.25 # Repair faster when docked
35 PHASEFAC = 2.0 # Unclear what this is, it was in the C version
55 class TrekError(Exception):
58 class JumpOut(Exception):
62 def __init__(self, x=None, y=None):
65 def valid_quadrant(self):
66 return self.i>=0 and self.i<GALSIZE and self.j>=0 and self.j<GALSIZE
67 def valid_sector(self):
68 return self.i>=0 and self.i<QUADSIZE and self.j>=0 and self.j<QUADSIZE
70 self.i = self.j = None
72 return self.i != None and self.j != None
73 def __eq__(self, other):
74 return other != None and self.i == other.i and self.j == other.j
75 def __ne__(self, other):
76 return other == None or self.i != other.i or self.j != other.j
77 def __add__(self, other):
78 return coord(self.i+other.i, self.j+other.j)
79 def __sub__(self, other):
80 return coord(self.i-other.i, self.j-other.j)
81 def __mul__(self, other):
82 return coord(self.i*other, self.j*other)
83 def __rmul__(self, other):
84 return coord(self.i*other, self.j*other)
85 def __div__(self, other):
86 return coord(self.i/other, self.j/other)
87 def __mod__(self, other):
88 return coord(self.i % other, self.j % other)
89 def __rdiv__(self, other):
90 return coord(self.i/other, self.j/other)
91 def roundtogrid(self):
92 return coord(int(round(self.i)), int(round(self.j)))
93 def distance(self, other=None):
94 if not other: other = coord(0, 0)
95 return math.sqrt((self.i - other.i)**2 + (self.j - other.j)**2)
97 return 1.90985*math.atan2(self.j, self.i)
103 s.i = self.i / abs(self.i)
107 s.j = self.j / abs(self.j)
110 #print "Location %s -> %s" % (self, (self / QUADSIZE).roundtogrid())
111 return self.roundtogrid() / QUADSIZE
113 return self.roundtogrid() % QUADSIZE
116 s.i = self.i + randrange(-1, 2)
117 s.j = self.j + randrange(-1, 2)
120 if self.i == None or self.j == None:
122 return "%s - %s" % (self.i+1, self.j+1)
127 self.name = None # string-valued if inhabited
128 self.quadrant = coord() # quadrant located
129 self.pclass = None # could be ""M", "N", "O", or "destroyed"
130 self.crystals = "absent"# could be "mined", "present", "absent"
131 self.known = "unknown" # could be "unknown", "known", "shuttle_down"
132 self.inhabited = False # is it inhabites?
140 self.starbase = False
143 self.supernova = False
145 self.status = "secure" # Could be "secure", "distressed", "enslaved"
153 def fill2d(size, fillfun):
154 "Fill an empty list in 2D."
156 for i in range(size):
158 for j in range(size):
159 lst[i].append(fillfun(i, j))
164 self.snap = False # snapshot taken
165 self.crew = 0 # crew complement
166 self.remkl = 0 # remaining klingons
167 self.nscrem = 0 # remaining super commanders
168 self.starkl = 0 # destroyed stars
169 self.basekl = 0 # destroyed bases
170 self.nromrem = 0 # Romulans remaining
171 self.nplankl = 0 # destroyed uninhabited planets
172 self.nworldkl = 0 # destroyed inhabited planets
173 self.planets = [] # Planet information
174 self.date = 0.0 # stardate
175 self.remres = 0 # remaining resources
176 self.remtime = 0 # remaining time
177 self.baseq = [] # Base quadrant coordinates
178 self.kcmdr = [] # Commander quadrant coordinates
179 self.kscmdr = coord() # Supercommander quadrant coordinates
181 self.galaxy = fill2d(GALSIZE, lambda i_unused, j_unused: quadrant())
183 self.chart = fill2d(GALSIZE, lambda i_unused, j_unused: page())
187 self.date = None # A real number
188 self.quadrant = None # A coord structure
191 OPTION_ALL = 0xffffffff
192 OPTION_TTY = 0x00000001 # old interface
193 OPTION_CURSES = 0x00000002 # new interface
194 OPTION_IOMODES = 0x00000003 # cover both interfaces
195 OPTION_PLANETS = 0x00000004 # planets and mining
196 OPTION_THOLIAN = 0x00000008 # Tholians and their webs (UT 1979 version)
197 OPTION_THINGY = 0x00000010 # Space Thingy can shoot back (Stas, 2005)
198 OPTION_PROBE = 0x00000020 # deep-space probes (DECUS version, 1980)
199 OPTION_SHOWME = 0x00000040 # bracket Enterprise in chart
200 OPTION_RAMMING = 0x00000080 # enemies may ram Enterprise (Almy)
201 OPTION_MVBADDY = 0x00000100 # more enemies can move (Almy)
202 OPTION_BLKHOLE = 0x00000200 # black hole may timewarp you (Stas, 2005)
203 OPTION_BASE = 0x00000400 # bases have good shields (Stas, 2005)
204 OPTION_WORLDS = 0x00000800 # logic for inhabited worlds (ESR, 2006)
205 OPTION_AUTOSCAN = 0x00001000 # automatic LRSCAN before CHART (ESR, 2006)
206 OPTION_PLAIN = 0x01000000 # user chose plain game
207 OPTION_ALMY = 0x02000000 # user chose Almy variant
208 OPTION_COLOR = 0x04000000 # enable color display (experimental, ESR, 2010)
227 NDEVICES= 16 # Number of devices
236 def damaged(dev): return (game.damage[dev] != 0.0)
237 def communicating(): return not damaged(DRADIO) or game.condition=="docked"
239 # Define future events
240 FSPY = 0 # Spy event happens always (no future[] entry)
241 # can cause SC to tractor beam Enterprise
242 FSNOVA = 1 # Supernova
243 FTBEAM = 2 # Commander tractor beams Enterprise
244 FSNAP = 3 # Snapshot for time warp
245 FBATTAK = 4 # Commander attacks base
246 FCDBAS = 5 # Commander destroys base
247 FSCMOVE = 6 # Supercommander moves (might attack base)
248 FSCDBAS = 7 # Supercommander destroys base
249 FDSPROB = 8 # Move deep space probe
250 FDISTR = 9 # Emit distress call from an inhabited world
251 FENSLV = 10 # Inhabited word is enslaved */
252 FREPRO = 11 # Klingons build a ship in an enslaved system
255 # Abstract out the event handling -- underlying data structures will change
256 # when we implement stateful events
257 def findevent(evtype): return game.future[evtype]
260 def __init__(self, type=None, loc=None, power=None):
262 self.location = coord()
265 self.power = power # enemy energy level
266 game.enemies.append(self)
268 motion = (loc != self.location)
269 if self.location.i is not None and self.location.j is not None:
272 game.quad[self.location.i][self.location.j] = '#'
274 game.quad[self.location.i][self.location.j] = '.'
276 self.location = copy.copy(loc)
277 game.quad[self.location.i][self.location.j] = self.type
278 self.kdist = self.kavgd = (game.sector - loc).distance()
280 self.location = coord()
281 self.kdist = self.kavgd = None
282 game.enemies.remove(self)
285 return "<%s,%s.%f>" % (self.type, self.location, self.power) # For debugging
289 self.options = None # Game options
290 self.state = snapshot() # A snapshot structure
291 self.snapsht = snapshot() # Last snapshot taken for time-travel purposes
292 self.quad = None # contents of our quadrant
293 self.damage = [0.0] * NDEVICES # damage encountered
294 self.future = [] # future events
295 for i_unused in range(NEVENTS):
296 self.future.append(event())
297 self.passwd = None; # Self Destruct password
299 self.quadrant = None # where we are in the large
300 self.sector = None # where we are in the small
301 self.tholian = None # Tholian enemy object
302 self.base = None # position of base in current quadrant
303 self.battle = None # base coordinates being attacked
304 self.plnet = None # location of planet in quadrant
305 self.gamewon = False # Finished!
306 self.ididit = False # action taken -- allows enemy to attack
307 self.alive = False # we are alive (not killed)
308 self.justin = False # just entered quadrant
309 self.shldup = False # shields are up
310 self.shldchg = False # shield is changing (affects efficiency)
311 self.iscate = False # super commander is here
312 self.ientesc = False # attempted escape from supercommander
313 self.resting = False # rest time
314 self.icraft = False # Kirk in Galileo
315 self.landed = False # party on planet (true), on ship (false)
316 self.alldone = False # game is now finished
317 self.neutz = False # Romulan Neutral Zone
318 self.isarmed = False # probe is armed
319 self.inorbit = False # orbiting a planet
320 self.imine = False # mining
321 self.icrystl = False # dilithium crystals aboard
322 self.iseenit = False # seen base attack report
323 self.thawed = False # thawed game
324 self.condition = None # "green", "yellow", "red", "docked", "dead"
325 self.iscraft = None # "onship", "offship", "removed"
326 self.skill = None # Player skill level
327 self.inkling = 0 # initial number of klingons
328 self.inbase = 0 # initial number of bases
329 self.incom = 0 # initial number of commanders
330 self.inscom = 0 # initial number of commanders
331 self.inrom = 0 # initial number of commanders
332 self.instar = 0 # initial stars
333 self.intorps = 0 # initial/max torpedoes
334 self.torps = 0 # number of torpedoes
335 self.ship = 0 # ship type -- 'E' is Enterprise
336 self.abandoned = 0 # count of crew abandoned in space
337 self.length = 0 # length of game
338 self.klhere = 0 # klingons here
339 self.casual = 0 # causalties
340 self.nhelp = 0 # calls for help
341 self.nkinks = 0 # count of energy-barrier crossings
342 self.iplnet = None # planet # in quadrant
343 self.inplan = 0 # initial planets
344 self.irhere = 0 # Romulans in quadrant
345 self.isatb = 0 # =2 if super commander is attacking base
346 self.tourn = None # tournament number
347 self.nprobes = 0 # number of probes available
348 self.inresor = 0.0 # initial resources
349 self.intime = 0.0 # initial time
350 self.inenrg = 0.0 # initial/max energy
351 self.inshld = 0.0 # initial/max shield
352 self.inlsr = 0.0 # initial life support resources
353 self.indate = 0.0 # initial date
354 self.energy = 0.0 # energy level
355 self.shield = 0.0 # shield level
356 self.warpfac = 0.0 # warp speed
357 self.lsupres = 0.0 # life support reserves
358 self.optime = 0.0 # time taken by current operation
359 self.damfac = 0.0 # damage factor
360 self.lastchart = 0.0 # time star chart was last updated
361 self.cryprob = 0.0 # probability that crystal will work
362 self.probe = None # object holding probe course info
363 self.height = 0.0 # height of orbit around planet
364 self.idebug = False # Debugging instrumentation enabled?
366 # Stas thinks this should be (C expression):
367 # game.state.remkl + len(game.state.kcmdr) > 0 ?
368 # game.state.remres/(game.state.remkl + 4*len(game.state.kcmdr)) : 99
369 # He says the existing expression is prone to divide-by-zero errors
370 # after killing the last klingon when score is shown -- perhaps also
371 # if the only remaining klingon is SCOM.
372 game.state.remtime = game.state.remres/(game.state.remkl + 4*len(game.state.kcmdr))
398 return random.random() < p
400 def randrange(*args):
401 return random.randrange(*args)
406 v *= args[0] # from [0, args[0])
408 v = args[0] + v*(args[1]-args[0]) # from [args[0], args[1])
411 # Code from ai.c begins here
414 "Would this quadrant welcome another Klingon?"
415 return iq.valid_quadrant() and \
416 not game.state.galaxy[iq.i][iq.j].supernova and \
417 game.state.galaxy[iq.i][iq.j].klingons < MAXKLQUAD
419 def tryexit(enemy, look, irun):
420 "A bad guy attempts to bug out."
422 iq.i = game.quadrant.i+(look.i+(QUADSIZE-1))/QUADSIZE - 1
423 iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))/QUADSIZE - 1
424 if not welcoming(iq):
426 if enemy.type == 'R':
427 return False; # Romulans cannot escape!
429 # avoid intruding on another commander's territory
430 if enemy.type == 'C':
431 if iq in game.state.kcmdr:
433 # refuse to leave if currently attacking starbase
434 if game.battle == game.quadrant:
436 # don't leave if over 1000 units of energy
437 if enemy.power > 1000.0:
439 # emit escape message and move out of quadrant.
440 # we know this if either short or long range sensors are working
441 if not damaged(DSRSENS) or not damaged(DLRSENS) or \
442 game.condition == "docked":
443 prout(crmena(True, enemy.type, "sector", enemy.location) + \
444 (_(" escapes to Quadrant %s (and regains strength).") % iq))
445 # handle local matters related to escape
448 if game.condition != "docked":
450 # Handle global matters related to escape
451 game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
452 game.state.galaxy[iq.i][iq.j].klingons += 1
457 schedule(FSCMOVE, 0.2777)
461 for cmdr in game.state.kcmdr:
462 if cmdr == game.quadrant:
463 game.state.kcmdr.append(iq)
465 return True; # success
467 # The bad-guy movement algorithm:
469 # 1. Enterprise has "force" based on condition of phaser and photon torpedoes.
470 # If both are operating full strength, force is 1000. If both are damaged,
471 # force is -1000. Having shields down subtracts an additional 1000.
473 # 2. Enemy has forces equal to the energy of the attacker plus
474 # 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
475 # 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
477 # Attacker Initial energy levels (nominal):
478 # Klingon Romulan Commander Super-Commander
479 # Novice 400 700 1200
481 # Good 450 800 1300 1750
482 # Expert 475 850 1350 1875
483 # Emeritus 500 900 1400 2000
484 # VARIANCE 75 200 200 200
486 # Enemy vessels only move prior to their attack. In Novice - Good games
487 # only commanders move. In Expert games, all enemy vessels move if there
488 # is a commander present. In Emeritus games all enemy vessels move.
490 # 3. If Enterprise is not docked, an aggressive action is taken if enemy
491 # forces are 1000 greater than Enterprise.
493 # Agressive action on average cuts the distance between the ship and
494 # the enemy to 1/4 the original.
496 # 4. At lower energy advantage, movement units are proportional to the
497 # advantage with a 650 advantage being to hold ground, 800 to move forward
498 # 1, 950 for two, 150 for back 4, etc. Variance of 100.
500 # If docked, is reduced by roughly 1.75*game.skill, generally forcing a
501 # retreat, especially at high skill levels.
503 # 5. Motion is limited to skill level, except for SC hi-tailing it out.
505 def movebaddy(enemy):
506 "Tactical movement for the bad guys."
507 goto = coord(); look = coord()
509 # This should probably be just (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
510 if game.skill >= SKILL_EXPERT:
511 nbaddys = (((game.quadrant in game.state.kcmdr)*2 + (game.state.kscmdr==game.quadrant)*2+game.klhere*1.23+game.irhere*1.5)/2.0)
513 nbaddys = (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
515 mdist = int(dist1 + 0.5); # Nearest integer distance
516 # If SC, check with spy to see if should hi-tail it
517 if enemy.type=='S' and \
518 (enemy.power <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
522 # decide whether to advance, retreat, or hold position
523 forces = enemy.power+100.0*len(game.enemies)+400*(nbaddys-1)
525 forces += 1000; # Good for enemy if shield is down!
526 if not damaged(DPHASER) or not damaged(DPHOTON):
527 if damaged(DPHASER): # phasers damaged
530 forces -= 0.2*(game.energy - 2500.0)
531 if damaged(DPHOTON): # photon torpedoes damaged
534 forces -= 50.0*game.torps
536 # phasers and photon tubes both out!
539 if forces <= 1000.0 and game.condition != "docked": # Typical situation
540 motion = ((forces + randreal(200))/150.0) - 5.0
542 if forces > 1000.0: # Very strong -- move in for kill
543 motion = (1.0 - randreal())**2 * dist1 + 1.0
544 if game.condition=="docked" and (game.options & OPTION_BASE): # protected by base -- back off !
545 motion -= game.skill*(2.0-randreal()**2)
547 proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
548 # don't move if no motion
551 # Limit motion according to skill
552 if abs(motion) > game.skill:
557 # calculate preferred number of steps
558 nsteps = abs(int(motion))
559 if motion > 0 and nsteps > mdist:
560 nsteps = mdist; # don't overshoot
561 if nsteps > QUADSIZE:
562 nsteps = QUADSIZE; # This shouldn't be necessary
564 nsteps = 1; # This shouldn't be necessary
566 proutn("NSTEPS = %d:" % nsteps)
567 # Compute preferred values of delta X and Y
568 m = game.sector - enemy.location
569 if 2.0 * abs(m.i) < abs(m.j):
571 if 2.0 * abs(m.j) < abs(game.sector.i-enemy.location.i):
573 m = (motion * m).sgn()
574 goto = enemy.location
576 for ll in range(nsteps):
578 proutn(" %d" % (ll+1))
579 # Check if preferred position available
590 attempts = 0; # Settle mysterious hang problem
591 while attempts < 20 and not success:
593 if look.i < 0 or look.i >= QUADSIZE:
594 if motion < 0 and tryexit(enemy, look, irun):
596 if krawli == m.i or m.j == 0:
598 look.i = goto.i + krawli
600 elif look.j < 0 or look.j >= QUADSIZE:
601 if motion < 0 and tryexit(enemy, look, irun):
603 if krawlj == m.j or m.i == 0:
605 look.j = goto.j + krawlj
607 elif (game.options & OPTION_RAMMING) and game.quad[look.i][look.j] != '.':
608 # See if enemy should ram ship
609 if game.quad[look.i][look.j] == game.ship and \
610 (enemy.type == 'C' or enemy.type == 'S'):
611 collision(rammed=True, enemy=enemy)
613 if krawli != m.i and m.j != 0:
614 look.i = goto.i + krawli
616 elif krawlj != m.j and m.i != 0:
617 look.j = goto.j + krawlj
620 break; # we have failed
632 if not damaged(DSRSENS) or game.condition == "docked":
633 proutn(_("*** %s from Sector %s") % (cramen(enemy.type), enemy.location))
634 if enemy.kdist < dist1:
635 proutn(_(" advances to "))
637 proutn(_(" retreats to "))
638 prout("Sector %s." % goto)
641 "Sequence Klingon tactical movement."
644 # Figure out which Klingon is the commander (or Supercommander)
646 if game.quadrant in game.state.kcmdr:
647 for enemy in game.enemies:
648 if enemy.type == 'C':
650 if game.state.kscmdr==game.quadrant:
651 for enemy in game.enemies:
652 if enemy.type == 'S':
655 # If skill level is high, move other Klingons and Romulans too!
656 # Move these last so they can base their actions on what the
658 if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
659 for enemy in game.enemies:
660 if enemy.type in ('K', 'R'):
662 game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
664 def movescom(iq, avoid):
665 "Commander movement helper."
666 # Avoid quadrants with bases if we want to avoid Enterprise
667 if not welcoming(iq) or (avoid and iq in game.state.baseq):
669 if game.justin and not game.iscate:
672 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons -= 1
673 game.state.kscmdr = iq
674 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons += 1
675 if game.state.kscmdr==game.quadrant:
676 # SC has scooted, remove him from current quadrant
681 for enemy in game.enemies:
682 if enemy.type == 'S':
686 if game.condition != "docked":
688 game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
689 # check for a helpful planet
690 for i in range(game.inplan):
691 if game.state.planets[i].quadrant == game.state.kscmdr and \
692 game.state.planets[i].crystals == "present":
694 game.state.planets[i].pclass = "destroyed"
695 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].planet = None
698 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
699 proutn(_(" a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
700 prout(_(" by the Super-commander.\""))
702 return True; # looks good!
704 def supercommander():
705 "Move the Super Commander."
706 iq = coord(); sc = coord(); ibq = coord(); idelta = coord()
709 prout("== SUPERCOMMANDER")
710 # Decide on being active or passive
711 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 \
712 (game.state.date-game.indate) < 3.0)
713 if not game.iscate and avoid:
714 # compute move away from Enterprise
715 idelta = game.state.kscmdr-game.quadrant
716 if idelta.distance() > 2.0:
718 idelta.i = game.state.kscmdr.j-game.quadrant.j
719 idelta.j = game.quadrant.i-game.state.kscmdr.i
721 # compute distances to starbases
722 if not game.state.baseq:
726 sc = game.state.kscmdr
727 for base in game.state.baseq:
728 basetbl.append((i, (base - sc).distance()))
729 if game.state.baseq > 1:
730 basetbl.sort(lambda x, y: cmp(x[1], y[1]))
731 # look for nearest base without a commander, no Enterprise, and
732 # without too many Klingons, and not already under attack.
733 ifindit = iwhichb = 0
734 for (i2, base) in enumerate(game.state.baseq):
735 i = basetbl[i2][0]; # bug in original had it not finding nearest
736 if base==game.quadrant or base==game.battle or not welcoming(base):
738 # if there is a commander, and no other base is appropriate,
739 # we will take the one with the commander
740 for cmdr in game.state.kcmdr:
741 if base == cmdr and ifindit != 2:
745 else: # no commander -- use this one
750 return # Nothing suitable -- wait until next time
751 ibq = game.state.baseq[iwhichb]
752 # decide how to move toward base
753 idelta = ibq - game.state.kscmdr
754 # Maximum movement is 1 quadrant in either or both axes
755 idelta = idelta.sgn()
756 # try moving in both x and y directions
757 # there was what looked like a bug in the Almy C code here,
758 # but it might be this translation is just wrong.
759 iq = game.state.kscmdr + idelta
760 if not movescom(iq, avoid):
761 # failed -- try some other maneuvers
762 if idelta.i==0 or idelta.j==0:
765 iq.j = game.state.kscmdr.j + 1
766 if not movescom(iq, avoid):
767 iq.j = game.state.kscmdr.j - 1
770 iq.i = game.state.kscmdr.i + 1
771 if not movescom(iq, avoid):
772 iq.i = game.state.kscmdr.i - 1
775 # try moving just in x or y
776 iq.j = game.state.kscmdr.j
777 if not movescom(iq, avoid):
778 iq.j = game.state.kscmdr.j + idelta.j
779 iq.i = game.state.kscmdr.i
782 if len(game.state.baseq) == 0:
785 for ibq in game.state.baseq:
786 if ibq == game.state.kscmdr and game.state.kscmdr == game.battle:
789 return # no, don't attack base!
792 schedule(FSCDBAS, randreal(1.0, 3.0))
793 if is_scheduled(FCDBAS):
794 postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date)
795 if not communicating():
799 prout(_("Lt. Uhura- \"Captain, the starbase in Quadrant %s") \
801 prout(_(" reports that it is under attack from the Klingon Super-commander."))
802 proutn(_(" It can survive until stardate %d.\"") \
803 % int(scheduled(FSCDBAS)))
806 prout(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""))
810 game.optime = 0.0; # actually finished
812 # Check for intelligence report
813 if not game.idebug and \
815 (not communicating()) or \
816 not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].charted):
819 prout(_("Lt. Uhura- \"Captain, Starfleet Intelligence reports"))
820 proutn(_(" the Super-commander is in Quadrant %s,") % game.state.kscmdr)
825 if not game.tholian or game.justin:
828 if game.tholian.location.i == 0 and game.tholian.location.j == 0:
829 tid.i = 0; tid.j = QUADSIZE-1
830 elif game.tholian.location.i == 0 and game.tholian.location.j == QUADSIZE-1:
831 tid.i = QUADSIZE-1; tid.j = QUADSIZE-1
832 elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == QUADSIZE-1:
833 tid.i = QUADSIZE-1; tid.j = 0
834 elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == 0:
837 # something is wrong!
838 game.tholian.move(None)
839 prout("***Internal error: Tholian in a bad spot.")
841 # do nothing if we are blocked
842 if game.quad[tid.i][tid.j] not in ('.', '#'):
844 here = copy.copy(game.tholian.location)
845 delta = (tid - game.tholian.location).sgn()
847 while here.i != tid.i:
849 if game.quad[here.i][here.j]=='.':
850 game.tholian.move(here)
852 while here.j != tid.j:
854 if game.quad[here.i][here.j]=='.':
855 game.tholian.move(here)
856 # check to see if all holes plugged
857 for i in range(QUADSIZE):
858 if game.quad[0][i]!='#' and game.quad[0][i]!='T':
860 if game.quad[QUADSIZE-1][i]!='#' and game.quad[QUADSIZE-1][i]!='T':
862 if game.quad[i][0]!='#' and game.quad[i][0]!='T':
864 if game.quad[i][QUADSIZE-1]!='#' and game.quad[i][QUADSIZE-1]!='T':
866 # All plugged up -- Tholian splits
867 game.quad[game.tholian.location.i][game.tholian.location.j]='#'
869 prout(crmena(True, 'T', "sector", game.tholian) + _(" completes web."))
870 game.tholian.move(None)
873 # Code from battle.c begins here
875 def doshield(shraise):
876 "Change shield status."
884 if scanner.sees("transfer"):
888 prout(_("Shields damaged and down."))
890 if scanner.sees("up"):
892 elif scanner.sees("down"):
895 proutn(_("Do you wish to change shield energy? "))
898 elif damaged(DSHIELD):
899 prout(_("Shields damaged and down."))
902 proutn(_("Shields are up. Do you want them down? "))
909 proutn(_("Shields are down. Do you want them up? "))
915 if action == "SHUP": # raise shields
917 prout(_("Shields already up."))
921 if game.condition != "docked":
923 prout(_("Shields raised."))
926 prout(_("Shields raising uses up last of energy."))
931 elif action == "SHDN":
933 prout(_("Shields already down."))
937 prout(_("Shields lowered."))
940 elif action == "NRG":
941 while scanner.next() != "IHREAL":
943 proutn(_("Energy to transfer to shields- "))
948 if nrg > game.energy:
949 prout(_("Insufficient ship energy."))
952 if game.shield+nrg >= game.inshld:
953 prout(_("Shield energy maximized."))
954 if game.shield+nrg > game.inshld:
955 prout(_("Excess energy requested returned to ship energy"))
956 game.energy -= game.inshld-game.shield
957 game.shield = game.inshld
959 if nrg < 0.0 and game.energy-nrg > game.inenrg:
960 # Prevent shield drain loophole
962 prout(_("Engineering to bridge--"))
963 prout(_(" Scott here. Power circuit problem, Captain."))
964 prout(_(" I can't drain the shields."))
967 if game.shield+nrg < 0:
968 prout(_("All shield energy transferred to ship."))
969 game.energy += game.shield
972 proutn(_("Scotty- \""))
974 prout(_("Transferring energy to shields.\""))
976 prout(_("Draining energy from shields.\""))
982 "Choose a device to damage, at random."
984 105, # DSRSENS: short range scanners 10.5%
985 105, # DLRSENS: long range scanners 10.5%
986 120, # DPHASER: phasers 12.0%
987 120, # DPHOTON: photon torpedoes 12.0%
988 25, # DLIFSUP: life support 2.5%
989 65, # DWARPEN: warp drive 6.5%
990 70, # DIMPULS: impulse engines 6.5%
991 145, # DSHIELD: deflector shields 14.5%
992 30, # DRADIO: subspace radio 3.0%
993 45, # DSHUTTL: shuttle 4.5%
994 15, # DCOMPTR: computer 1.5%
995 20, # NAVCOMP: navigation system 2.0%
996 75, # DTRANSP: transporter 7.5%
997 20, # DSHCTRL: high-speed shield controller 2.0%
998 10, # DDRAY: death ray 1.0%
999 30, # DDSP: deep-space probes 3.0%
1001 assert(sum(weights) == 1000)
1002 idx = randrange(1000)
1004 for (i, w) in enumerate(weights):
1008 return None; # we should never get here
1010 def collision(rammed, enemy):
1011 "Collision handling fot rammong events."
1012 prouts(_("***RED ALERT! RED ALERT!"))
1014 prout(_("***COLLISION IMMINENT."))
1018 hardness = {'R':1.5, 'C':2.0, 'S':2.5, 'T':0.5, '?':4.0}.get(enemy.type, 1.0)
1020 proutn(_(" rammed by "))
1023 proutn(crmena(False, enemy.type, "sector", enemy.location))
1025 proutn(_(" (original position)"))
1027 deadkl(enemy.location, enemy.type, game.sector)
1028 proutn("***" + crmshp() + " heavily damaged.")
1029 icas = randrange(10, 30)
1030 prout(_("***Sickbay reports %d casualties") % icas)
1032 game.state.crew -= icas
1033 # In the pre-SST2K version, all devices got equiprobably damaged,
1034 # which was silly. Instead, pick up to half the devices at
1035 # random according to our weighting table,
1036 ncrits = randrange(NDEVICES/2)
1040 if game.damage[dev] < 0:
1042 extradm = (10.0*hardness*randreal()+1.0)*game.damfac
1043 # Damage for at least time of travel!
1044 game.damage[dev] += game.optime + extradm
1046 prout(_("***Shields are down."))
1047 if game.state.remkl + len(game.state.kcmdr) + game.state.nscrem:
1054 def torpedo(origin, bearing, dispersion, number, nburst):
1055 "Let a photon torpedo fly"
1056 if not damaged(DSRSENS) or game.condition=="docked":
1057 setwnd(srscan_window)
1059 setwnd(message_window)
1060 ac = bearing + 0.25*dispersion # dispersion is a random variable
1061 bullseye = (15.0 - bearing)*0.5235988
1062 track = course(bearing=ac, distance=QUADSIZE, origin=cartesian(origin))
1063 bumpto = coord(0, 0)
1064 # Loop to move a single torpedo
1065 setwnd(message_window)
1066 for step in range(1, QUADSIZE*2):
1067 if not track.next(): break
1069 if not w.valid_sector():
1071 iquad=game.quad[w.i][w.j]
1072 tracktorpedo(origin, w, step, number, nburst, iquad)
1076 if not damaged(DSRSENS) or game.condition == "docked":
1077 skip(1); # start new line after text track
1078 if iquad in ('E', 'F'): # Hit our ship
1080 prout(_("Torpedo hits %s.") % crmshp())
1081 hit = 700.0 + randreal(100) - \
1082 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1083 newcnd(); # we're blown out of dock
1084 if game.landed or game.condition=="docked":
1085 return hit # Cheat if on a planet
1086 # In the C/FORTRAN version, dispersion was 2.5 radians, which
1087 # is 143 degrees, which is almost exactly 4.8 clockface units
1088 displacement = course(track.bearing+randreal(-2.4,2.4), distance=2**0.5)
1090 bumpto = displacement.sector()
1091 if not bumpto.valid_sector():
1093 if game.quad[bumpto.i][bumpto.j]==' ':
1096 if game.quad[bumpto.i][bumpto.j]!='.':
1097 # can't move into object
1099 game.sector = bumpto
1101 game.quad[w.i][w.j]='.'
1102 game.quad[bumpto.i][bumpto.j]=iquad
1103 prout(_(" displaced by blast to Sector %s ") % bumpto)
1104 for enemy in game.enemies:
1105 enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
1106 game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
1108 elif iquad in ('C', 'S', 'R', 'K'): # Hit a regular enemy
1110 if iquad in ('C', 'S') and withprob(0.05):
1111 prout(crmena(True, iquad, "sector", w) + _(" uses anti-photon device;"))
1112 prout(_(" torpedo neutralized."))
1114 for enemy in game.enemies:
1115 if w == enemy.location:
1117 kp = math.fabs(enemy.power)
1118 h1 = 700.0 + randrange(100) - \
1119 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
1127 if enemy.power == 0:
1130 proutn(crmena(True, iquad, "sector", w))
1131 displacement = course(track.bearing+randreal(-2.4,2.4), distance=2**0.5)
1133 bumpto = displacement.sector()
1134 if not bumpto.valid_sector():
1135 prout(_(" damaged but not destroyed."))
1137 if game.quad[bumpto.i][bumpto.j] == ' ':
1138 prout(_(" buffeted into black hole."))
1139 deadkl(w, iquad, bumpto)
1140 if game.quad[bumpto.i][bumpto.j] != '.':
1141 prout(_(" damaged but not destroyed."))
1143 prout(_(" damaged-- displaced by blast to Sector %s ")%bumpto)
1144 enemy.location = bumpto
1145 game.quad[w.i][w.j]='.'
1146 game.quad[bumpto.i][bumpto.j]=iquad
1147 for enemy in game.enemies:
1148 enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
1149 game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
1151 elif iquad == 'B': # Hit a base
1153 prout(_("***STARBASE DESTROYED.."))
1154 game.state.baseq = filter(lambda x: x != game.quadrant, game.state.baseq)
1155 game.quad[w.i][w.j]='.'
1156 game.base.invalidate()
1157 game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase -= 1
1158 game.state.chart[game.quadrant.i][game.quadrant.j].starbase -= 1
1159 game.state.basekl += 1
1162 elif iquad == 'P': # Hit a planet
1163 prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
1164 game.state.nplankl += 1
1165 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
1166 game.iplnet.pclass = "destroyed"
1168 game.plnet.invalidate()
1169 game.quad[w.i][w.j] = '.'
1171 # captain perishes on planet
1174 elif iquad == '@': # Hit an inhabited world -- very bad!
1175 prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
1176 game.state.nworldkl += 1
1177 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
1178 game.iplnet.pclass = "destroyed"
1180 game.plnet.invalidate()
1181 game.quad[w.i][w.j] = '.'
1183 # captain perishes on planet
1185 prout(_("The torpedo destroyed an inhabited planet."))
1187 elif iquad == '*': # Hit a star
1191 prout(crmena(True, '*', "sector", w) + _(" unaffected by photon blast."))
1193 elif iquad == '?': # Hit a thingy
1194 if not (game.options & OPTION_THINGY) or withprob(0.3):
1196 prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
1198 prouts(_(" HACK! HACK! HACK! *CHOKE!* "))
1200 proutn(_("Mr. Spock-"))
1201 prouts(_(" \"Fascinating!\""))
1205 # Stas Sergeev added the possibility that
1206 # you can shove the Thingy and piss it off.
1207 # It then becomes an enemy and may fire at you.
1210 elif iquad == ' ': # Black hole
1212 prout(crmena(True, ' ', "sector", w) + _(" swallows torpedo."))
1214 elif iquad == '#': # hit the web
1216 prout(_("***Torpedo absorbed by Tholian web."))
1218 elif iquad == 'T': # Hit a Tholian
1219 h1 = 700.0 + randrange(100) - \
1220 1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-angle))
1223 game.quad[w.i][w.j] = '.'
1228 proutn(crmena(True, 'T', "sector", w))
1230 prout(_(" survives photon blast."))
1232 prout(_(" disappears."))
1233 game.tholian.move(None)
1234 game.quad[w.i][w.j] = '#'
1239 proutn("Don't know how to handle torpedo collision with ")
1240 proutn(crmena(True, iquad, "sector", w))
1245 prout(_("Torpedo missed."))
1249 "Critical-hit resolution."
1250 if hit < (275.0-25.0*game.skill)*randreal(1.0, 1.5):
1252 ncrit = int(1.0 + hit/(500.0+randreal(100)))
1253 proutn(_("***CRITICAL HIT--"))
1254 # Select devices and cause damage
1260 # Cheat to prevent shuttle damage unless on ship
1261 if not (game.damage[j]<0.0 or (j==DSHUTTL and game.iscraft != "onship")):
1264 extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
1265 game.damage[j] += extradm
1267 for (i, j) in enumerate(cdam):
1269 if skipcount % 3 == 2 and i < len(cdam)-1:
1274 prout(_(" damaged."))
1275 if damaged(DSHIELD) and game.shldup:
1276 prout(_("***Shields knocked down."))
1279 def attack(torps_ok):
1280 # bad guy attacks us
1281 # torps_ok == False forces use of phasers in an attack
1282 # game could be over at this point, check
1285 attempt = False; ihurt = False;
1286 hitmax=0.0; hittot=0.0; chgfac=1.0
1289 prout("=== ATTACK!")
1290 # Tholian gets to move before attacking
1293 # if you have just entered the RNZ, you'll get a warning
1294 if game.neutz: # The one chance not to be attacked
1297 # commanders get a chance to tac-move towards you
1298 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:
1300 # if no enemies remain after movement, we're done
1301 if len(game.enemies)==0 or (len(game.enemies)==1 and thing == game.quadrant and not thing.angry):
1303 # set up partial hits if attack happens during shield status change
1304 pfac = 1.0/game.inshld
1306 chgfac = 0.25 + randreal(0.5)
1308 # message verbosity control
1309 if game.skill <= SKILL_FAIR:
1311 for enemy in game.enemies:
1313 continue; # too weak to attack
1314 # compute hit strength and diminish shield power
1316 # Increase chance of photon torpedos if docked or enemy energy is low
1317 if game.condition == "docked":
1319 if enemy.power < 500:
1321 if enemy.type=='T' or (enemy.type=='?' and not thing.angry):
1323 # different enemies have different probabilities of throwing a torp
1324 usephasers = not torps_ok or \
1325 (enemy.type == 'K' and r > 0.0005) or \
1326 (enemy.type=='C' and r > 0.015) or \
1327 (enemy.type=='R' and r > 0.3) or \
1328 (enemy.type=='S' and r > 0.07) or \
1329 (enemy.type=='?' and r > 0.05)
1330 if usephasers: # Enemy uses phasers
1331 if game.condition == "docked":
1332 continue; # Don't waste the effort!
1333 attempt = True; # Attempt to attack
1334 dustfac = randreal(0.8, 0.85)
1335 hit = enemy.power*math.pow(dustfac,enemy.kavgd)
1337 else: # Enemy uses photon torpedo
1338 # We should be able to make the bearing() method work here
1339 pcourse = 1.90985*math.atan2(game.sector.j-enemy.location.j, enemy.location.i-game.sector.i)
1341 proutn(_("***TORPEDO INCOMING"))
1342 if not damaged(DSRSENS):
1343 proutn(_(" From ") + crmena(False, enemy.type, where, enemy.location))
1346 dispersion = (randreal()+randreal())*0.5 - 0.5
1347 dispersion += 0.002*enemy.power*dispersion
1348 hit = torpedo(enemy.location, pcourse, dispersion, number=1, nburst=1)
1349 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
1350 finish(FWON); # Klingons did themselves in!
1351 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova or game.alldone:
1352 return # Supernova or finished
1355 # incoming phaser or torpedo, shields may dissipate it
1356 if game.shldup or game.shldchg or game.condition=="docked":
1357 # shields will take hits
1358 propor = pfac * game.shield
1359 if game.condition =="docked":
1363 hitsh = propor*chgfac*hit+1.0
1365 if absorb > game.shield:
1366 absorb = game.shield
1367 game.shield -= absorb
1369 # taking a hit blasts us out of a starbase dock
1370 if game.condition == "docked":
1372 # but the shields may take care of it
1373 if propor > 0.1 and hit < 0.005*game.energy:
1375 # hit from this opponent got through shields, so take damage
1377 proutn(_("%d unit hit") % int(hit))
1378 if (damaged(DSRSENS) and usephasers) or game.skill<=SKILL_FAIR:
1379 proutn(_(" on the ") + crmshp())
1380 if not damaged(DSRSENS) and usephasers:
1381 prout(_(" from ") + crmena(False, enemy.type, where, enemy.location))
1383 # Decide if hit is critical
1389 if game.energy <= 0:
1390 # Returning home upon your shield, not with it...
1393 if not attempt and game.condition == "docked":
1394 prout(_("***Enemies decide against attacking your ship."))
1395 percent = 100.0*pfac*game.shield+0.5
1397 # Shields fully protect ship
1398 proutn(_("Enemy attack reduces shield strength to "))
1400 # Emit message if starship suffered hit(s)
1402 proutn(_("Energy left %2d shields ") % int(game.energy))
1405 elif not damaged(DSHIELD):
1408 proutn(_("damaged, "))
1409 prout(_("%d%%, torpedoes left %d") % (percent, game.torps))
1410 # Check if anyone was hurt
1411 if hitmax >= 200 or hittot >= 500:
1412 icas = randrange(int(hittot * 0.015))
1415 prout(_("Mc Coy- \"Sickbay to bridge. We suffered %d casualties") % icas)
1416 prout(_(" in that last attack.\""))
1418 game.state.crew -= icas
1419 # After attack, reset average distance to enemies
1420 for enemy in game.enemies:
1421 enemy.kavgd = enemy.kdist
1422 game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
1425 def deadkl(w, type, mv):
1426 "Kill a Klingon, Tholian, Romulan, or Thingy."
1427 # Added mv to allow enemy to "move" before dying
1428 proutn(crmena(True, type, "sector", mv))
1429 # Decide what kind of enemy it is and update appropriately
1431 # Chalk up a Romulan
1432 game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans -= 1
1434 game.state.nromrem -= 1
1443 # Killed some type of Klingon
1444 game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
1447 game.state.kcmdr.remove(game.quadrant)
1449 if game.state.kcmdr:
1450 schedule(FTBEAM, expran(1.0*game.incom/len(game.state.kcmdr)))
1451 if is_scheduled(FCDBAS) and game.battle == game.quadrant:
1454 game.state.remkl -= 1
1456 game.state.nscrem -= 1
1457 game.state.kscmdr.invalidate()
1462 # For each kind of enemy, finish message to player
1463 prout(_(" destroyed."))
1464 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
1467 # Remove enemy ship from arrays describing local conditions
1468 for e in game.enemies:
1475 "Return None if target is invalid, otherwise return a course angle."
1476 if not w.valid_sector():
1480 # FIXME: C code this was translated from is wacky -- why the sign reversal?
1481 delta.j = (w.j - game.sector.j);
1482 delta.i = (game.sector.i - w.i);
1483 if delta == coord(0, 0):
1485 prout(_("Spock- \"Bridge to sickbay. Dr. McCoy,"))
1486 prout(_(" I recommend an immediate review of"))
1487 prout(_(" the Captain's psychological profile.\""))
1490 return delta.bearing()
1493 "Launch photon torpedo salvo."
1496 if damaged(DPHOTON):
1497 prout(_("Photon tubes damaged."))
1501 prout(_("No torpedoes left."))
1504 # First, get torpedo count
1507 if scanner.token == "IHALPHA":
1510 elif scanner.token == "IHEOL" or not scanner.waiting():
1511 prout(_("%d torpedoes left.") % game.torps)
1513 proutn(_("Number of torpedoes to fire- "))
1514 continue # Go back around to get a number
1515 else: # key == "IHREAL"
1517 if n <= 0: # abort command
1522 prout(_("Maximum of %d torpedoes per burst.") % MAXBURST)
1525 scanner.chew() # User requested more torps than available
1526 continue # Go back around
1527 break # All is good, go to next stage
1531 key = scanner.next()
1532 if i==0 and key == "IHEOL":
1533 break; # no coordinate waiting, we will try prompting
1534 if i==1 and key == "IHEOL":
1535 # direct all torpedoes at one target
1537 target.append(target[0])
1538 tcourse.append(tcourse[0])
1541 scanner.push(scanner.token)
1542 target.append(scanner.getcoord())
1543 if target[-1] == None:
1545 tcourse.append(targetcheck(target[-1]))
1546 if tcourse[-1] == None:
1549 if len(target) == 0:
1550 # prompt for each one
1552 proutn(_("Target sector for torpedo number %d- ") % (i+1))
1554 target.append(scanner.getcoord())
1555 if target[-1] == None:
1557 tcourse.append(targetcheck(target[-1]))
1558 if tcourse[-1] == None:
1561 # Loop for moving <n> torpedoes
1563 if game.condition != "docked":
1565 dispersion = (randreal()+randreal())*0.5 -0.5
1566 if math.fabs(dispersion) >= 0.47:
1568 dispersion *= randreal(1.2, 2.2)
1570 prouts(_("***TORPEDO NUMBER %d MISFIRES") % (i+1))
1572 prouts(_("***TORPEDO MISFIRES."))
1575 prout(_(" Remainder of burst aborted."))
1577 prout(_("***Photon tubes damaged by misfire."))
1578 game.damage[DPHOTON] = game.damfac * randreal(1.0, 3.0)
1580 if game.shldup or game.condition == "docked":
1581 dispersion *= 1.0 + 0.0001*game.shield
1582 torpedo(game.sector, tcourse[i], dispersion, number=i, nburst=n)
1583 if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
1585 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)<=0:
1589 "Check for phasers overheating."
1591 checkburn = (rpow-1500.0)*0.00038
1592 if withprob(checkburn):
1593 prout(_("Weapons officer Sulu- \"Phasers overheated, sir.\""))
1594 game.damage[DPHASER] = game.damfac* randreal(1.0, 2.0) * (1.0+checkburn)
1596 def checkshctrl(rpow):
1597 "Check shield control."
1600 prout(_("Shields lowered."))
1602 # Something bad has happened
1603 prouts(_("***RED ALERT! RED ALERT!"))
1605 hit = rpow*game.shield/game.inshld
1606 game.energy -= rpow+hit*0.8
1607 game.shield -= hit*0.2
1608 if game.energy <= 0.0:
1609 prouts(_("Sulu- \"Captain! Shield malf***********************\""))
1614 prouts(_("Sulu- \"Captain! Shield malfunction! Phaser fire contained!\""))
1616 prout(_("Lt. Uhura- \"Sir, all decks reporting damage.\""))
1617 icas = randrange(int(hit*0.012))
1622 prout(_("McCoy to bridge- \"Severe radiation burns, Jim."))
1623 prout(_(" %d casualties so far.\"") % icas)
1625 game.state.crew -= icas
1627 prout(_("Phaser energy dispersed by shields."))
1628 prout(_("Enemy unaffected."))
1633 "Register a phaser hit on Klingons and Romulans."
1637 for (k, wham) in enumerate(hits):
1640 dustfac = randreal(0.9, 1.0)
1641 hit = wham*math.pow(dustfac,game.enemies[kk].kdist)
1642 kpini = game.enemies[kk].power
1643 kp = math.fabs(kpini)
1644 if PHASEFAC*hit < kp:
1646 if game.enemies[kk].power < 0:
1647 game.enemies[kk].power -= -kp
1649 game.enemies[kk].power -= kp
1650 kpow = game.enemies[kk].power
1651 w = game.enemies[kk].location
1653 if not damaged(DSRSENS):
1655 proutn(_("%d unit hit on ") % int(hit))
1657 proutn(_("Very small hit on "))
1658 ienm = game.quad[w.i][w.j]
1661 proutn(crmena(False, ienm, "sector", w))
1665 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
1669 kk -= 1 # don't do the increment
1671 else: # decide whether or not to emasculate klingon
1672 if kpow>0 and withprob(0.9) and kpow <= randreal(0.4, 0.8)*kpini:
1673 prout(_("***Mr. Spock- \"Captain, the vessel at Sector %s")%w)
1674 prout(_(" has just lost its firepower.\""))
1675 game.enemies[kk].power = -kpow
1680 "Fire phasers at bad guys."
1682 kz = 0; k = 1; irec=0 # Cheating inhibitor
1683 ifast = False; no = False; itarg = True; msgflag = True; rpow=0
1687 # SR sensors and Computer are needed for automode
1688 if damaged(DSRSENS) or damaged(DCOMPTR):
1690 if game.condition == "docked":
1691 prout(_("Phasers can't be fired through base shields."))
1694 if damaged(DPHASER):
1695 prout(_("Phaser control damaged."))
1699 if damaged(DSHCTRL):
1700 prout(_("High speed shield control damaged."))
1703 if game.energy <= 200.0:
1704 prout(_("Insufficient energy to activate high-speed shield control."))
1707 prout(_("Weapons Officer Sulu- \"High-speed shield control enabled, sir.\""))
1709 # Original code so convoluted, I re-did it all
1710 # (That was Tom Almy talking about the C code, I think -- ESR)
1711 while automode=="NOTSET":
1713 if key == "IHALPHA":
1714 if scanner.sees("manual"):
1715 if len(game.enemies)==0:
1716 prout(_("There is no enemy present to select."))
1719 automode="AUTOMATIC"
1722 key = scanner.next()
1723 elif scanner.sees("automatic"):
1724 if (not itarg) and len(game.enemies) != 0:
1725 automode = "FORCEMAN"
1727 if len(game.enemies)==0:
1728 prout(_("Energy will be expended into space."))
1729 automode = "AUTOMATIC"
1730 key = scanner.next()
1731 elif scanner.sees("no"):
1736 elif key == "IHREAL":
1737 if len(game.enemies)==0:
1738 prout(_("Energy will be expended into space."))
1739 automode = "AUTOMATIC"
1741 automode = "FORCEMAN"
1743 automode = "AUTOMATIC"
1746 if len(game.enemies)==0:
1747 prout(_("Energy will be expended into space."))
1748 automode = "AUTOMATIC"
1750 automode = "FORCEMAN"
1752 proutn(_("Manual or automatic? "))
1757 if automode == "AUTOMATIC":
1758 if key == "IHALPHA" and scanner.sees("no"):
1760 key = scanner.next()
1761 if key != "IHREAL" and len(game.enemies) != 0:
1762 prout(_("Phasers locked on target. Energy available: %.2f")%avail)
1767 for i in range(len(game.enemies)):
1768 irec += math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90,game.enemies[i].kdist))*randreal(1.01, 1.06) + 1.0
1770 proutn(_("%d units required. ") % irec)
1772 proutn(_("Units to fire= "))
1773 key = scanner.next()
1778 proutn(_("Energy available= %.2f") % avail)
1781 if not rpow > avail:
1788 if key == "IHALPHA" and scanner.sees("no"):
1791 game.energy -= 200; # Go and do it!
1792 if checkshctrl(rpow):
1797 if len(game.enemies):
1800 for i in range(len(game.enemies)):
1804 hits[i] = math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90,game.enemies[i].kdist))
1805 over = randreal(1.01, 1.06) * hits[i]
1807 powrem -= hits[i] + over
1808 if powrem <= 0 and temp < hits[i]:
1817 if extra > 0 and not game.alldone:
1819 proutn(_("*** Tholian web absorbs "))
1820 if len(game.enemies)>0:
1821 proutn(_("excess "))
1822 prout(_("phaser energy."))
1824 prout(_("%d expended on empty space.") % int(extra))
1825 elif automode == "FORCEMAN":
1828 if damaged(DCOMPTR):
1829 prout(_("Battle computer damaged, manual fire only."))
1832 prouts(_("---WORKING---"))
1834 prout(_("Short-range-sensors-damaged"))
1835 prout(_("Insufficient-data-for-automatic-phaser-fire"))
1836 prout(_("Manual-fire-must-be-used"))
1838 elif automode == "MANUAL":
1840 for k in range(len(game.enemies)):
1841 aim = game.enemies[k].location
1842 ienm = game.quad[aim.i][aim.j]
1844 proutn(_("Energy available= %.2f") % (avail-0.006))
1848 if damaged(DSRSENS) and \
1849 not game.sector.distance(aim)<2**0.5 and ienm in ('C', 'S'):
1850 prout(cramen(ienm) + _(" can't be located without short range scan."))
1853 hits[k] = 0; # prevent overflow -- thanks to Alexei Voitenko
1858 if itarg and k > kz:
1859 irec=(abs(game.enemies[k].power)/(PHASEFAC*math.pow(0.9,game.enemies[k].kdist))) * randreal(1.01, 1.06) + 1.0
1862 if not damaged(DCOMPTR):
1867 proutn(_("units to fire at %s- ") % crmena(False, ienm, "sector", aim))
1868 key = scanner.next()
1869 if key == "IHALPHA" and scanner.sees("no"):
1871 key = scanner.next()
1873 if key == "IHALPHA":
1877 if k==1: # Let me say I'm baffled by this
1880 if scanner.real < 0:
1884 hits[k] = scanner.real
1885 rpow += scanner.real
1886 # If total requested is too much, inform and start over
1888 prout(_("Available energy exceeded -- try again."))
1891 key = scanner.next(); # scan for next value
1894 # zero energy -- abort
1897 if key == "IHALPHA" and scanner.sees("no"):
1902 game.energy -= 200.0
1903 if checkshctrl(rpow):
1907 # Say shield raised or malfunction, if necessary
1914 prout(_("Sulu- \"Sir, the high-speed shield control has malfunctioned . . ."))
1915 prouts(_(" CLICK CLICK POP . . ."))
1916 prout(_(" No response, sir!"))
1919 prout(_("Shields raised."))
1924 # Code from events,c begins here.
1926 # This isn't a real event queue a la BSD Trek yet -- you can only have one
1927 # event of each type active at any given time. Mostly these means we can
1928 # only have one FDISTR/FENSLV/FREPRO sequence going at any given time
1929 # BSD Trek, from which we swiped the idea, can have up to 5.
1931 def unschedule(evtype):
1932 "Remove an event from the schedule."
1933 game.future[evtype].date = FOREVER
1934 return game.future[evtype]
1936 def is_scheduled(evtype):
1937 "Is an event of specified type scheduled."
1938 return game.future[evtype].date != FOREVER
1940 def scheduled(evtype):
1941 "When will this event happen?"
1942 return game.future[evtype].date
1944 def schedule(evtype, offset):
1945 "Schedule an event of specified type."
1946 game.future[evtype].date = game.state.date + offset
1947 return game.future[evtype]
1949 def postpone(evtype, offset):
1950 "Postpone a scheduled event."
1951 game.future[evtype].date += offset
1954 "Rest period is interrupted by event."
1957 proutn(_("Mr. Spock- \"Captain, shall we cancel the rest period?\""))
1959 game.resting = False
1965 "Run through the event queue looking for things to do."
1967 fintim = game.state.date + game.optime; yank=0
1968 ictbeam = False; istract = False
1969 w = coord(); hold = coord()
1970 ev = event(); ev2 = event()
1972 def tractorbeam(yank):
1973 "Tractor-beaming cases merge here."
1975 game.optime = (10.0/(7.5*7.5))*yank # 7.5 is yank rate (warp 7.5)
1977 prout("***" + crmshp() + _(" caught in long range tractor beam--"))
1978 # If Kirk & Co. screwing around on planet, handle
1979 atover(True) # atover(true) is Grab
1982 if game.icraft: # Caught in Galileo?
1985 # Check to see if shuttle is aboard
1986 if game.iscraft == "offship":
1989 prout(_("Galileo, left on the planet surface, is captured"))
1990 prout(_("by aliens and made into a flying McDonald's."))
1991 game.damage[DSHUTTL] = -10
1992 game.iscraft = "removed"
1994 prout(_("Galileo, left on the planet surface, is well hidden."))
1996 game.quadrant = game.state.kscmdr
1998 game.quadrant = game.state.kcmdr[i]
1999 game.sector = randplace(QUADSIZE)
2000 prout(crmshp() + _(" is pulled to Quadrant %s, Sector %s") \
2001 % (game.quadrant, game.sector))
2003 prout(_("(Remainder of rest/repair period cancelled.)"))
2004 game.resting = False
2006 if not damaged(DSHIELD) and game.shield > 0:
2007 doshield(shraise=True) # raise shields
2008 game.shldchg = False
2010 prout(_("(Shields not currently useable.)"))
2012 # Adjust finish time to time of tractor beaming
2013 fintim = game.state.date+game.optime
2014 attack(torps_ok=False)
2015 if not game.state.kcmdr:
2018 schedule(FTBEAM, game.optime+expran(1.5*game.intime/len(game.state.kcmdr)))
2021 "Code merges here for any commander destroying a starbase."
2022 # Not perfect, but will have to do
2023 # Handle case where base is in same quadrant as starship
2024 if game.battle == game.quadrant:
2025 game.state.chart[game.battle.i][game.battle.j].starbase = False
2026 game.quad[game.base.i][game.base.j] = '.'
2027 game.base.invalidate()
2030 prout(_("Spock- \"Captain, I believe the starbase has been destroyed.\""))
2031 elif game.state.baseq and communicating():
2032 # Get word via subspace radio
2035 prout(_("Lt. Uhura- \"Captain, Starfleet Command reports that"))
2036 proutn(_(" the starbase in Quadrant %s has been destroyed by") % game.battle)
2038 prout(_("the Klingon Super-Commander"))
2040 prout(_("a Klingon Commander"))
2041 game.state.chart[game.battle.i][game.battle.j].starbase = False
2042 # Remove Starbase from galaxy
2043 game.state.galaxy[game.battle.i][game.battle.j].starbase = False
2044 game.state.baseq = filter(lambda x: x != game.battle, game.state.baseq)
2046 # reinstate a commander's base attack
2050 game.battle.invalidate()
2052 prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
2053 for i in range(1, NEVENTS):
2054 if i == FSNOVA: proutn("=== Supernova ")
2055 elif i == FTBEAM: proutn("=== T Beam ")
2056 elif i == FSNAP: proutn("=== Snapshot ")
2057 elif i == FBATTAK: proutn("=== Base Attack ")
2058 elif i == FCDBAS: proutn("=== Base Destroy ")
2059 elif i == FSCMOVE: proutn("=== SC Move ")
2060 elif i == FSCDBAS: proutn("=== SC Base Destroy ")
2061 elif i == FDSPROB: proutn("=== Probe Move ")
2062 elif i == FDISTR: proutn("=== Distress Call ")
2063 elif i == FENSLV: proutn("=== Enslavement ")
2064 elif i == FREPRO: proutn("=== Klingon Build ")
2066 prout("%.2f" % (scheduled(i)))
2069 radio_was_broken = damaged(DRADIO)
2072 # Select earliest extraneous event, evcode==0 if no events
2077 for l in range(1, NEVENTS):
2078 if game.future[l].date < datemin:
2081 prout("== Event %d fires" % evcode)
2082 datemin = game.future[l].date
2083 xtime = datemin-game.state.date
2084 game.state.date = datemin
2085 # Decrement Federation resources and recompute remaining time
2086 game.state.remres -= (game.state.remkl+4*len(game.state.kcmdr))*xtime
2088 if game.state.remtime <=0:
2091 # Any crew left alive?
2092 if game.state.crew <=0:
2095 # Is life support adequate?
2096 if damaged(DLIFSUP) and game.condition != "docked":
2097 if game.lsupres < xtime and game.damage[DLIFSUP] > game.lsupres:
2100 game.lsupres -= xtime
2101 if game.damage[DLIFSUP] <= xtime:
2102 game.lsupres = game.inlsr
2105 if game.condition == "docked":
2107 # Don't fix Deathray here
2108 for l in range(NDEVICES):
2109 if game.damage[l] > 0.0 and l != DDRAY:
2110 if game.damage[l]-repair > 0.0:
2111 game.damage[l] -= repair
2113 game.damage[l] = 0.0
2114 # If radio repaired, update star chart and attack reports
2115 if radio_was_broken and not damaged(DRADIO):
2116 prout(_("Lt. Uhura- \"Captain, the sub-space radio is working and"))
2117 prout(_(" surveillance reports are coming in."))
2119 if not game.iseenit:
2123 prout(_(" The star chart is now up to date.\""))
2125 # Cause extraneous event EVCODE to occur
2126 game.optime -= xtime
2127 if evcode == FSNOVA: # Supernova
2130 schedule(FSNOVA, expran(0.5*game.intime))
2131 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
2133 elif evcode == FSPY: # Check with spy to see if SC should tractor beam
2134 if game.state.nscrem == 0 or \
2135 ictbeam or istract or \
2136 game.condition=="docked" or game.isatb==1 or game.iscate:
2138 if game.ientesc or \
2139 (game.energy<2000 and game.torps<4 and game.shield < 1250) or \
2140 (damaged(DPHASER) and (damaged(DPHOTON) or game.torps<4)) or \
2141 (damaged(DSHIELD) and \
2142 (game.energy < 2500 or damaged(DPHASER)) and \
2143 (game.torps < 5 or damaged(DPHOTON))):
2145 istract = ictbeam = True
2146 tractorbeam((game.state.kscmdr-game.quadrant).distance())
2149 elif evcode == FTBEAM: # Tractor beam
2150 if not game.state.kcmdr:
2153 i = randrange(len(game.state.kcmdr))
2154 yank = (game.state.kcmdr[i]-game.quadrant).distance()
2155 if istract or game.condition == "docked" or yank == 0:
2156 # Drats! Have to reschedule
2158 game.optime + expran(1.5*game.intime/len(game.state.kcmdr)))
2162 elif evcode == FSNAP: # Snapshot of the universe (for time warp)
2163 game.snapsht = copy.deepcopy(game.state)
2164 game.state.snap = True
2165 schedule(FSNAP, expran(0.5 * game.intime))
2166 elif evcode == FBATTAK: # Commander attacks starbase
2167 if not game.state.kcmdr or not game.state.baseq:
2173 for ibq in game.state.baseq:
2174 for cmdr in game.state.kcmdr:
2175 if ibq == cmdr and ibq != game.quadrant and ibq != game.state.kscmdr:
2178 # no match found -- try later
2179 schedule(FBATTAK, expran(0.3*game.intime))
2184 # commander + starbase combination found -- launch attack
2186 schedule(FCDBAS, randreal(1.0, 4.0))
2187 if game.isatb: # extra time if SC already attacking
2188 postpone(FCDBAS, scheduled(FSCDBAS)-game.state.date)
2189 game.future[FBATTAK].date = game.future[FCDBAS].date + expran(0.3*game.intime)
2190 game.iseenit = False
2191 if not communicating():
2192 continue # No warning :-(
2196 prout(_("Lt. Uhura- \"Captain, the starbase in Quadrant %s") % game.battle)
2197 prout(_(" reports that it is under attack and that it can"))
2198 prout(_(" hold out only until stardate %d.\"") % (int(scheduled(FCDBAS))))
2201 elif evcode == FSCDBAS: # Supercommander destroys base
2204 if not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].starbase:
2205 continue # WAS RETURN!
2207 game.battle = game.state.kscmdr
2209 elif evcode == FCDBAS: # Commander succeeds in destroying base
2212 if not game.state.baseq() \
2213 or not game.state.galaxy[game.battle.i][game.battle.j].starbase:
2214 game.battle.invalidate()
2216 # find the lucky pair
2217 for cmdr in game.state.kcmdr:
2218 if cmdr == game.battle:
2221 # No action to take after all
2224 elif evcode == FSCMOVE: # Supercommander moves
2225 schedule(FSCMOVE, 0.2777)
2226 if not game.ientesc and not istract and game.isatb != 1 and \
2227 (not game.iscate or not game.justin):
2229 elif evcode == FDSPROB: # Move deep space probe
2230 schedule(FDSPROB, 0.01)
2231 if not game.probe.next():
2232 if not game.probe.quadrant().valid_quadrant() or \
2233 game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j].supernova:
2234 # Left galaxy or ran into supernova
2238 proutn(_("Lt. Uhura- \"The deep space probe "))
2239 if not game.probe.quadrant().valid_quadrant():
2240 prout(_("has left the galaxy.\""))
2242 prout(_("is no longer transmitting.\""))
2248 prout(_("Lt. Uhura- \"The deep space probe is now in Quadrant %s.\"") % game.probe.quadrant())
2249 pdest = game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j]
2251 chp = game.state.chart[game.probe.quadrant().i][game.probe.quadrant().j]
2252 chp.klingons = pdest.klingons
2253 chp.starbase = pdest.starbase
2254 chp.stars = pdest.stars
2255 pdest.charted = True
2256 game.probe.moves -= 1 # One less to travel
2257 if game.probe.arrived() and game.isarmed and pdest.stars:
2258 supernova(game.probe) # fire in the hole!
2260 if game.state.galaxy[game.quadrant().i][game.quadrant().j].supernova:
2262 elif evcode == FDISTR: # inhabited system issues distress call
2264 # try a whole bunch of times to find something suitable
2265 for i in range(100):
2266 # need a quadrant which is not the current one,
2267 # which has some stars which are inhabited and
2268 # not already under attack, which is not
2269 # supernova'ed, and which has some Klingons in it
2270 w = randplace(GALSIZE)
2271 q = game.state.galaxy[w.i][w.j]
2272 if not (game.quadrant == w or q.planet == None or \
2273 not q.planet.inhabited or \
2274 q.supernova or q.status!="secure" or q.klingons<=0):
2277 # can't seem to find one; ignore this call
2279 prout("=== Couldn't find location for distress event.")
2281 # got one!! Schedule its enslavement
2282 ev = schedule(FENSLV, expran(game.intime))
2284 q.status = "distressed"
2285 # tell the captain about it if we can
2287 prout(_("Uhura- Captain, %s in Quadrant %s reports it is under attack") \
2289 prout(_("by a Klingon invasion fleet."))
2292 elif evcode == FENSLV: # starsystem is enslaved
2293 ev = unschedule(FENSLV)
2294 # see if current distress call still active
2295 q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
2299 q.status = "enslaved"
2301 # play stork and schedule the first baby
2302 ev2 = schedule(FREPRO, expran(2.0 * game.intime))
2303 ev2.quadrant = ev.quadrant
2305 # report the disaster if we can
2307 prout(_("Uhura- We've lost contact with starsystem %s") % \
2309 prout(_("in Quadrant %s.\n") % ev.quadrant)
2310 elif evcode == FREPRO: # Klingon reproduces
2311 # If we ever switch to a real event queue, we'll need to
2312 # explicitly retrieve and restore the x and y.
2313 ev = schedule(FREPRO, expran(1.0 * game.intime))
2314 # see if current distress call still active
2315 q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
2319 if game.state.remkl >=MAXKLGAME:
2320 continue # full right now
2321 # reproduce one Klingon
2324 if game.klhere >= MAXKLQUAD:
2326 # this quadrant not ok, pick an adjacent one
2327 for m.i in range(w.i - 1, w.i + 2):
2328 for m.j in range(w.j - 1, w.j + 2):
2329 if not m.valid_quadrant():
2331 q = game.state.galaxy[m.i][m.j]
2332 # check for this quad ok (not full & no snova)
2333 if q.klingons >= MAXKLQUAD or q.supernova:
2337 continue # search for eligible quadrant failed
2341 game.state.remkl += 1
2343 if game.quadrant == w:
2345 game.enemies.append(newkling())
2346 # recompute time left
2349 if game.quadrant == w:
2350 prout(_("Spock- sensors indicate the Klingons have"))
2351 prout(_("launched a warship from %s.") % q.planet)
2353 prout(_("Uhura- Starfleet reports increased Klingon activity"))
2354 if q.planet != None:
2355 proutn(_("near %s ") % q.planet)
2356 prout(_("in Quadrant %s.") % w)
2362 key = scanner.next()
2365 proutn(_("How long? "))
2370 origTime = delay = scanner.real
2373 if delay >= game.state.remtime or len(game.enemies) != 0:
2374 proutn(_("Are you sure? "))
2377 # Alternate resting periods (events) with attacks
2381 game.resting = False
2382 if not game.resting:
2383 prout(_("%d stardates left.") % int(game.state.remtime))
2385 temp = game.optime = delay
2386 if len(game.enemies):
2387 rtime = randreal(1.0, 2.0)
2391 if game.optime < delay:
2392 attack(torps_ok=False)
2400 # Repair Deathray if long rest at starbase
2401 if origTime-delay >= 9.99 and game.condition == "docked":
2402 game.damage[DDRAY] = 0.0
2403 # leave if quadrant supernovas
2404 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
2406 game.resting = False
2411 ncourse = (0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5)
2412 newc = coord(); neighbor = coord(); bump = coord(0, 0)
2414 # Wow! We've supernova'ed
2415 supernova(game.quadrant)
2417 # handle initial nova
2418 game.quad[nov.i][nov.j] = '.'
2419 prout(crmena(False, '*', "sector", nov) + _(" novas."))
2420 game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
2421 game.state.starkl += 1
2422 # Set up queue to recursively trigger adjacent stars
2428 for offset.i in range(-1, 1+1):
2429 for offset.j in range(-1, 1+1):
2430 if offset.j==0 and offset.i==0:
2432 neighbor = start + offset
2433 if not neighbor.valid_sector():
2435 iquad = game.quad[neighbor.i][neighbor.j]
2436 # Empty space ends reaction
2437 if iquad in ('.', '?', ' ', 'T', '#'):
2439 elif iquad == '*': # Affect another star
2441 # This star supernovas
2442 supernova(game.quadrant)
2445 hits.append(neighbor)
2446 game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
2447 game.state.starkl += 1
2448 proutn(crmena(True, '*', "sector", neighbor))
2450 game.quad[neighbor.i][neighbor.j] = '.'
2452 elif iquad in ('P', '@'): # Destroy planet
2453 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
2455 game.state.nplankl += 1
2457 game.state.worldkl += 1
2458 prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
2459 game.iplnet.pclass = "destroyed"
2461 game.plnet.invalidate()
2465 game.quad[neighbor.i][neighbor.j] = '.'
2466 elif iquad == 'B': # Destroy base
2467 game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
2468 game.state.baseq = filter(lambda x: x!= game.quadrant, game.state.baseq)
2469 game.base.invalidate()
2470 game.state.basekl += 1
2472 prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
2473 game.quad[neighbor.i][neighbor.j] = '.'
2474 elif iquad in ('E', 'F'): # Buffet ship
2475 prout(_("***Starship buffeted by nova."))
2477 if game.shield >= 2000.0:
2478 game.shield -= 2000.0
2480 diff = 2000.0 - game.shield
2484 prout(_("***Shields knocked out."))
2485 game.damage[DSHIELD] += 0.005*game.damfac*randreal()*diff
2487 game.energy -= 2000.0
2488 if game.energy <= 0:
2491 # add in course nova contributes to kicking starship
2492 bump += (game.sector-hits[mm]).sgn()
2493 elif iquad == 'K': # kill klingon
2494 deadkl(neighbor, iquad, neighbor)
2495 elif iquad in ('C','S','R'): # Damage/destroy big enemies
2496 for ll in range(len(game.enemies)):
2497 if game.enemies[ll].location == neighbor:
2499 game.enemies[ll].power -= 800.0 # If firepower is lost, die
2500 if game.enemies[ll].power <= 0.0:
2501 deadkl(neighbor, iquad, neighbor)
2503 newc = neighbor + neighbor - hits[mm]
2504 proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged"))
2505 if not newc.valid_sector():
2506 # can't leave quadrant
2509 iquad1 = game.quad[newc.i][newc.j]
2511 proutn(_(", blasted into ") + crmena(False, ' ', "sector", newc))
2513 deadkl(neighbor, iquad, newc)
2516 # can't move into something else
2519 proutn(_(", buffeted to Sector %s") % newc)
2520 game.quad[neighbor.i][neighbor.j] = '.'
2521 game.quad[newc.i][newc.j] = iquad
2522 game.enemies[ll].move(newc)
2523 # Starship affected by nova -- kick it away.
2525 direc = ncourse[3*(bump.i+1)+bump.j+2]
2530 scourse = course(bearing=direc, distance=dist)
2531 game.optime = course.time(warp=4)
2533 prout(_("Force of nova displaces starship."))
2534 imove(scourse, noattack=True)
2535 game.optime = scourse.time(warp=4)
2539 "Star goes supernova."
2544 # Scheduled supernova -- select star at random.
2547 for nq.i in range(GALSIZE):
2548 for nq.j in range(GALSIZE):
2549 stars += game.state.galaxy[nq.i][nq.j].stars
2551 return # nothing to supernova exists
2552 num = randrange(stars) + 1
2553 for nq.i in range(GALSIZE):
2554 for nq.j in range(GALSIZE):
2555 num -= game.state.galaxy[nq.i][nq.j].stars
2561 proutn("=== Super nova here?")
2564 if not nq == game.quadrant or game.justin:
2565 # it isn't here, or we just entered (treat as enroute)
2568 prout(_("Message from Starfleet Command Stardate %.2f") % game.state.date)
2569 prout(_(" Supernova in Quadrant %s; caution advised.") % nq)
2572 # we are in the quadrant!
2573 num = randrange(game.state.galaxy[nq.i][nq.j].stars) + 1
2574 for ns.i in range(QUADSIZE):
2575 for ns.j in range(QUADSIZE):
2576 if game.quad[ns.i][ns.j]=='*':
2583 prouts(_("***RED ALERT! RED ALERT!"))
2585 prout(_("***Incipient supernova detected at Sector %s") % ns)
2586 if (ns.i-game.sector.i)**2 + (ns.j-game.sector.j)**2 <= 2.1:
2587 proutn(_("Emergency override attempts t"))
2588 prouts("***************")
2592 # destroy any Klingons in supernovaed quadrant
2593 kldead = game.state.galaxy[nq.i][nq.j].klingons
2594 game.state.galaxy[nq.i][nq.j].klingons = 0
2595 if nq == game.state.kscmdr:
2596 # did in the Supercommander!
2597 game.state.nscrem = game.state.kscmdr.i = game.state.kscmdr.j = game.isatb = 0
2601 survivors = filter(lambda w: w != nq, game.state.kcmdr)
2602 comkills = len(game.state.kcmdr) - len(survivors)
2603 game.state.kcmdr = survivors
2605 if not game.state.kcmdr:
2607 game.state.remkl -= kldead
2608 # destroy Romulans and planets in supernovaed quadrant
2609 nrmdead = game.state.galaxy[nq.i][nq.j].romulans
2610 game.state.galaxy[nq.i][nq.j].romulans = 0
2611 game.state.nromrem -= nrmdead
2613 for loop in range(game.inplan):
2614 if game.state.planets[loop].quadrant == nq:
2615 game.state.planets[loop].pclass = "destroyed"
2617 # Destroy any base in supernovaed quadrant
2618 game.state.baseq = filter(lambda x: x != nq, game.state.baseq)
2619 # If starship caused supernova, tally up destruction
2621 game.state.starkl += game.state.galaxy[nq.i][nq.j].stars
2622 game.state.basekl += game.state.galaxy[nq.i][nq.j].starbase
2623 game.state.nplankl += npdead
2624 # mark supernova in galaxy and in star chart
2625 if game.quadrant == nq or communicating():
2626 game.state.galaxy[nq.i][nq.j].supernova = True
2627 # If supernova destroys last Klingons give special message
2628 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0 and not nq == game.quadrant:
2631 prout(_("Lucky you!"))
2632 proutn(_("A supernova in %s has just destroyed the last Klingons.") % nq)
2635 # if some Klingons remain, continue or die in supernova
2640 # Code from finish.c ends here.
2643 "Self-destruct maneuver. Finish with a BANG!"
2645 if damaged(DCOMPTR):
2646 prout(_("Computer damaged; cannot execute destruct sequence."))
2648 prouts(_("---WORKING---")); skip(1)
2649 prouts(_("SELF-DESTRUCT-SEQUENCE-ACTIVATED")); skip(1)
2650 prouts(" 10"); skip(1)
2651 prouts(" 9"); skip(1)
2652 prouts(" 8"); skip(1)
2653 prouts(" 7"); skip(1)
2654 prouts(" 6"); skip(1)
2656 prout(_("ENTER-CORRECT-PASSWORD-TO-CONTINUE-"))
2658 prout(_("SELF-DESTRUCT-SEQUENCE-OTHERWISE-"))
2660 prout(_("SELF-DESTRUCT-SEQUENCE-WILL-BE-ABORTED"))
2664 if game.passwd != scanner.token:
2665 prouts(_("PASSWORD-REJECTED;"))
2667 prouts(_("CONTINUITY-EFFECTED"))
2670 prouts(_("PASSWORD-ACCEPTED")); skip(1)
2671 prouts(" 5"); skip(1)
2672 prouts(" 4"); skip(1)
2673 prouts(" 3"); skip(1)
2674 prouts(" 2"); skip(1)
2675 prouts(" 1"); skip(1)
2677 prouts(_("GOODBYE-CRUEL-WORLD"))
2685 prouts(_("********* Entropy of %s maximized *********") % crmshp())
2689 if len(game.enemies) != 0:
2690 whammo = 25.0 * game.energy
2692 while l <= len(game.enemies):
2693 if game.enemies[l].power*game.enemies[l].kdist <= whammo:
2694 deadkl(game.enemies[l].location, game.quad[game.enemies[l].location.i][game.enemies[l].location.j], game.enemies[l].location)
2699 "Compute our rate of kils over time."
2700 elapsed = game.state.date - game.indate
2701 if elapsed == 0: # Avoid divide-by-zero error if calculated on turn 0
2704 starting = (game.inkling + game.incom + game.inscom)
2705 remaining = (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)
2706 return (starting - remaining)/elapsed
2710 badpt = 5.0*game.state.starkl + \
2712 10.0*game.state.nplankl + \
2713 300*game.state.nworldkl + \
2715 100.0*game.state.basekl +\
2717 if game.ship == 'F':
2719 elif game.ship == None:
2724 # end the game, with appropriate notfications
2728 prout(_("It is stardate %.1f.") % game.state.date)
2730 if ifin == FWON: # Game has been won
2731 if game.state.nromrem != 0:
2732 prout(_("The remaining %d Romulans surrender to Starfleet Command.") %
2735 prout(_("You have smashed the Klingon invasion fleet and saved"))
2736 prout(_("the Federation."))
2741 badpt = 0.0 # Close enough!
2742 # killsPerDate >= RateMax
2743 if game.state.date-game.indate < 5.0 or \
2744 killrate() >= 0.1*game.skill*(game.skill+1.0) + 0.1 + 0.008*badpt:
2746 prout(_("In fact, you have done so well that Starfleet Command"))
2747 if game.skill == SKILL_NOVICE:
2748 prout(_("promotes you one step in rank from \"Novice\" to \"Fair\"."))
2749 elif game.skill == SKILL_FAIR:
2750 prout(_("promotes you one step in rank from \"Fair\" to \"Good\"."))
2751 elif game.skill == SKILL_GOOD:
2752 prout(_("promotes you one step in rank from \"Good\" to \"Expert\"."))
2753 elif game.skill == SKILL_EXPERT:
2754 prout(_("promotes you to Commodore Emeritus."))
2756 prout(_("Now that you think you're really good, try playing"))
2757 prout(_("the \"Emeritus\" game. It will splatter your ego."))
2758 elif game.skill == SKILL_EMERITUS:
2760 proutn(_("Computer- "))
2761 prouts(_("ERROR-ERROR-ERROR-ERROR"))
2763 prouts(_(" YOUR-SKILL-HAS-EXCEEDED-THE-CAPACITY-OF-THIS-PROGRAM"))
2765 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
2767 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
2769 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
2771 prouts(_(" THIS-PROGRAM-MUST?- MUST ? - SUR? ? -? VI"))
2773 prout(_("Now you can retire and write your own Star Trek game!"))
2775 elif game.skill >= SKILL_EXPERT:
2776 if game.thawed and not game.idebug:
2777 prout(_("You cannot get a citation, so..."))
2779 proutn(_("Do you want your Commodore Emeritus Citation printed? "))
2783 # Only grant long life if alive (original didn't!)
2785 prout(_("LIVE LONG AND PROSPER."))
2790 elif ifin == FDEPLETE: # Federation Resources Depleted
2791 prout(_("Your time has run out and the Federation has been"))
2792 prout(_("conquered. Your starship is now Klingon property,"))
2793 prout(_("and you are put on trial as a war criminal. On the"))
2794 proutn(_("basis of your record, you are "))
2795 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)*3.0 > (game.inkling + game.incom + game.inscom):
2796 prout(_("acquitted."))
2798 prout(_("LIVE LONG AND PROSPER."))
2800 prout(_("found guilty and"))
2801 prout(_("sentenced to death by slow torture."))
2805 elif ifin == FLIFESUP:
2806 prout(_("Your life support reserves have run out, and"))
2807 prout(_("you die of thirst, starvation, and asphyxiation."))
2808 prout(_("Your starship is a derelict in space."))
2810 prout(_("Your energy supply is exhausted."))
2812 prout(_("Your starship is a derelict in space."))
2813 elif ifin == FBATTLE:
2814 prout(_("The %s has been destroyed in battle.") % crmshp())
2816 prout(_("Dulce et decorum est pro patria mori."))
2818 prout(_("You have made three attempts to cross the negative energy"))
2819 prout(_("barrier which surrounds the galaxy."))
2821 prout(_("Your navigation is abominable."))
2824 prout(_("Your starship has been destroyed by a nova."))
2825 prout(_("That was a great shot."))
2827 elif ifin == FSNOVAED:
2828 prout(_("The %s has been fried by a supernova.") % crmshp())
2829 prout(_("...Not even cinders remain..."))
2830 elif ifin == FABANDN:
2831 prout(_("You have been captured by the Klingons. If you still"))
2832 prout(_("had a starbase to be returned to, you would have been"))
2833 prout(_("repatriated and given another chance. Since you have"))
2834 prout(_("no starbases, you will be mercilessly tortured to death."))
2835 elif ifin == FDILITHIUM:
2836 prout(_("Your starship is now an expanding cloud of subatomic particles"))
2837 elif ifin == FMATERIALIZE:
2838 prout(_("Starbase was unable to re-materialize your starship."))
2839 prout(_("Sic transit gloria mundi"))
2840 elif ifin == FPHASER:
2841 prout(_("The %s has been cremated by its own phasers.") % crmshp())
2843 prout(_("You and your landing party have been"))
2844 prout(_("converted to energy, disipating through space."))
2845 elif ifin == FMINING:
2846 prout(_("You are left with your landing party on"))
2847 prout(_("a wild jungle planet inhabited by primitive cannibals."))
2849 prout(_("They are very fond of \"Captain Kirk\" soup."))
2851 prout(_("Without your leadership, the %s is destroyed.") % crmshp())
2852 elif ifin == FDPLANET:
2853 prout(_("You and your mining party perish."))
2855 prout(_("That was a great shot."))
2858 prout(_("The Galileo is instantly annihilated by the supernova."))
2859 prout(_("You and your mining party are atomized."))
2861 prout(_("Mr. Spock takes command of the %s and") % crmshp())
2862 prout(_("joins the Romulans, wreaking terror on the Federation."))
2863 elif ifin == FPNOVA:
2864 prout(_("You and your mining party are atomized."))
2866 prout(_("Mr. Spock takes command of the %s and") % crmshp())
2867 prout(_("joins the Romulans, wreaking terror on the Federation."))
2868 elif ifin == FSTRACTOR:
2869 prout(_("The shuttle craft Galileo is also caught,"))
2870 prout(_("and breaks up under the strain."))
2872 prout(_("Your debris is scattered for millions of miles."))
2873 prout(_("Without your leadership, the %s is destroyed.") % crmshp())
2875 prout(_("The mutants attack and kill Spock."))
2876 prout(_("Your ship is captured by Klingons, and"))
2877 prout(_("your crew is put on display in a Klingon zoo."))
2878 elif ifin == FTRIBBLE:
2879 prout(_("Tribbles consume all remaining water,"))
2880 prout(_("food, and oxygen on your ship."))
2882 prout(_("You die of thirst, starvation, and asphyxiation."))
2883 prout(_("Your starship is a derelict in space."))
2885 prout(_("Your ship is drawn to the center of the black hole."))
2886 prout(_("You are crushed into extremely dense matter."))
2888 prout(_("Your last crew member has died."))
2889 if game.ship == 'F':
2891 elif game.ship == 'E':
2894 if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0:
2895 goodies = game.state.remres/game.inresor
2896 baddies = (game.state.remkl + 2.0*len(game.state.kcmdr))/(game.inkling+2.0*game.incom)
2897 if goodies/baddies >= randreal(1.0, 1.5):
2898 prout(_("As a result of your actions, a treaty with the Klingon"))
2899 prout(_("Empire has been signed. The terms of the treaty are"))
2900 if goodies/baddies >= randreal(3.0):
2901 prout(_("favorable to the Federation."))
2903 prout(_("Congratulations!"))
2905 prout(_("highly unfavorable to the Federation."))
2907 prout(_("The Federation will be destroyed."))
2909 prout(_("Since you took the last Klingon with you, you are a"))
2910 prout(_("martyr and a hero. Someday maybe they'll erect a"))
2911 prout(_("statue in your memory. Rest in peace, and try not"))
2912 prout(_("to think about pigeons."))
2917 "Compute player's score."
2918 timused = game.state.date - game.indate
2920 if (timused == 0 or (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0) and timused < 5.0:
2922 perdate = killrate()
2923 ithperd = 500*perdate + 0.5
2926 iwon = 100*game.skill
2927 if game.ship == 'E':
2929 elif game.ship == 'F':
2933 iscore = 10*(game.inkling - game.state.remkl) \
2934 + 50*(game.incom - len(game.state.kcmdr)) \
2936 + 20*(game.inrom - game.state.nromrem) \
2937 + 200*(game.inscom - game.state.nscrem) \
2938 - game.state.nromrem \
2943 prout(_("Your score --"))
2944 if game.inrom - game.state.nromrem:
2945 prout(_("%6d Romulans destroyed %5d") %
2946 (game.inrom - game.state.nromrem, 20*(game.inrom - game.state.nromrem)))
2947 if game.state.nromrem and game.gamewon:
2948 prout(_("%6d Romulans captured %5d") %
2949 (game.state.nromrem, game.state.nromrem))
2950 if game.inkling - game.state.remkl:
2951 prout(_("%6d ordinary Klingons destroyed %5d") %
2952 (game.inkling - game.state.remkl, 10*(game.inkling - game.state.remkl)))
2953 if game.incom - len(game.state.kcmdr):
2954 prout(_("%6d Klingon commanders destroyed %5d") %
2955 (game.incom - len(game.state.kcmdr), 50*(game.incom - len(game.state.kcmdr))))
2956 if game.inscom - game.state.nscrem:
2957 prout(_("%6d Super-Commander destroyed %5d") %
2958 (game.inscom - game.state.nscrem, 200*(game.inscom - game.state.nscrem)))
2960 prout(_("%6.2f Klingons per stardate %5d") %
2962 if game.state.starkl:
2963 prout(_("%6d stars destroyed by your action %5d") %
2964 (game.state.starkl, -5*game.state.starkl))
2965 if game.state.nplankl:
2966 prout(_("%6d planets destroyed by your action %5d") %
2967 (game.state.nplankl, -10*game.state.nplankl))
2968 if (game.options & OPTION_WORLDS) and game.state.nworldkl:
2969 prout(_("%6d inhabited planets destroyed by your action %5d") %
2970 (game.state.nworldkl, -300*game.state.nworldkl))
2971 if game.state.basekl:
2972 prout(_("%6d bases destroyed by your action %5d") %
2973 (game.state.basekl, -100*game.state.basekl))
2975 prout(_("%6d calls for help from starbase %5d") %
2976 (game.nhelp, -45*game.nhelp))
2978 prout(_("%6d casualties incurred %5d") %
2979 (game.casual, -game.casual))
2981 prout(_("%6d crew abandoned in space %5d") %
2982 (game.abandoned, -3*game.abandoned))
2984 prout(_("%6d ship(s) lost or destroyed %5d") %
2985 (klship, -100*klship))
2987 prout(_("Penalty for getting yourself killed -200"))
2989 proutn(_("Bonus for winning "))
2990 if game.skill == SKILL_NOVICE: proutn(_("Novice game "))
2991 elif game.skill == SKILL_FAIR: proutn(_("Fair game "))
2992 elif game.skill == SKILL_GOOD: proutn(_("Good game "))
2993 elif game.skill == SKILL_EXPERT: proutn(_("Expert game "))
2994 elif game.skill == SKILL_EMERITUS: proutn(_("Emeritus game"))
2995 prout(" %5d" % iwon)
2997 prout(_("TOTAL SCORE %5d") % iscore)
3000 "Emit winner's commemmorative plaque."
3003 proutn(_("File or device name for your plaque: "))
3006 fp = open(winner, "w")
3009 prout(_("Invalid name."))
3011 proutn(_("Enter name to go on plaque (up to 30 characters): "))
3013 # The 38 below must be 64 for 132-column paper
3014 nskip = 38 - len(winner)/2
3015 fp.write("\n\n\n\n")
3016 # --------DRAW ENTERPRISE PICTURE.
3017 fp.write(" EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n" )
3018 fp.write(" EEE E : : : E\n" )
3019 fp.write(" EE EEE E : : NCC-1701 : E\n")
3020 fp.write("EEEEEEEEEEEEEEEE EEEEEEEEEEEEEEE : : : E\n")
3021 fp.write(" E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n")
3022 fp.write(" EEEEEEEEE EEEEEEEEEEEEE E E\n")
3023 fp.write(" EEEEEEE EEEEE E E E E\n")
3024 fp.write(" EEE E E E E\n")
3025 fp.write(" E E E E\n")
3026 fp.write(" EEEEEEEEEEEEE E E\n")
3027 fp.write(" EEE : EEEEEEE EEEEEEEE\n")
3028 fp.write(" :E : EEEE E\n")
3029 fp.write(" .-E -:----- E\n")
3030 fp.write(" :E : E\n")
3031 fp.write(" EE : EEEEEEEE\n")
3032 fp.write(" EEEEEEEEEEEEEEEEEEEEEEE\n")
3034 fp.write(_(" U. S. S. ENTERPRISE\n"))
3035 fp.write("\n\n\n\n")
3036 fp.write(_(" For demonstrating outstanding ability as a starship captain\n"))
3038 fp.write(_(" Starfleet Command bestows to you\n"))
3040 fp.write("%*s%s\n\n" % (nskip, "", winner))
3041 fp.write(_(" the rank of\n\n"))
3042 fp.write(_(" \"Commodore Emeritus\"\n\n"))
3044 if game.skill == SKILL_EXPERT:
3045 fp.write(_(" Expert level\n\n"))
3046 elif game.skill == SKILL_EMERITUS:
3047 fp.write(_("Emeritus level\n\n"))
3049 fp.write(_(" Cheat level\n\n"))
3050 timestring = time.ctime()
3051 fp.write(_(" This day of %.6s %.4s, %.8s\n\n") %
3052 (timestring+4, timestring+20, timestring+11))
3053 fp.write(_(" Your score: %d\n\n") % iscore)
3054 fp.write(_(" Klingons per stardate: %.2f\n") % perdate)
3057 # Code from io.c begins here
3059 rows = linecount = 0 # for paging
3062 fullscreen_window = None
3063 srscan_window = None
3064 report_window = None
3065 status_window = None
3066 lrscan_window = None
3067 message_window = None
3068 prompt_window = None
3073 "for some recent versions of python2, the following enables UTF8"
3074 "for the older ones we probably need to set C locale, and the python3"
3075 "has no problems at all"
3076 if sys.version_info[0] < 3:
3078 locale.setlocale(locale.LC_ALL, "")
3079 gettext.bindtextdomain("sst", "/usr/local/share/locale")
3080 gettext.textdomain("sst")
3081 if not (game.options & OPTION_CURSES):
3082 ln_env = os.getenv("LINES")
3088 stdscr = curses.initscr()
3092 if game.options & OPTION_COLOR:
3093 curses.start_color();
3094 curses.use_default_colors()
3095 curses.init_pair(curses.COLOR_BLACK, curses.COLOR_BLACK, -1);
3096 curses.init_pair(curses.COLOR_GREEN, curses.COLOR_GREEN, -1);
3097 curses.init_pair(curses.COLOR_RED, curses.COLOR_RED, -1);
3098 curses.init_pair(curses.COLOR_CYAN, curses.COLOR_CYAN, -1);
3099 curses.init_pair(curses.COLOR_WHITE, curses.COLOR_WHITE, -1);
3100 curses.init_pair(curses.COLOR_MAGENTA, curses.COLOR_MAGENTA, -1);
3101 curses.init_pair(curses.COLOR_BLUE, curses.COLOR_BLUE, -1);
3102 curses.init_pair(curses.COLOR_YELLOW, curses.COLOR_YELLOW, -1);
3103 global fullscreen_window, srscan_window, report_window, status_window
3104 global lrscan_window, message_window, prompt_window
3105 (rows, columns) = stdscr.getmaxyx()
3106 fullscreen_window = stdscr
3107 srscan_window = curses.newwin(12, 25, 0, 0)
3108 report_window = curses.newwin(11, 0, 1, 25)
3109 status_window = curses.newwin(10, 0, 1, 39)
3110 lrscan_window = curses.newwin(5, 0, 0, 64)
3111 message_window = curses.newwin(0, 0, 12, 0)
3112 prompt_window = curses.newwin(1, 0, rows-2, 0)
3113 message_window.scrollok(True)
3114 setwnd(fullscreen_window)
3118 if game.options & OPTION_CURSES:
3119 stdscr.keypad(False)
3125 "Wait for user action -- OK to do nothing if on a TTY"
3126 if game.options & OPTION_CURSES:
3131 prouts(_("[ANNOUNCEMENT ARRIVING...]"))
3135 if game.skill > SKILL_FAIR:
3136 prompt = _("[CONTINUE?]")
3138 prompt = _("[PRESS ENTER TO CONTINUE]")
3140 if game.options & OPTION_CURSES:
3142 setwnd(prompt_window)
3143 prompt_window.clear()
3144 prompt_window.addstr(prompt)
3145 prompt_window.getstr()
3146 prompt_window.clear()
3147 prompt_window.refresh()
3148 setwnd(message_window)
3151 sys.stdout.write('\n')
3154 for j_unused in range(rows):
3155 sys.stdout.write('\n')
3159 "Skip i lines. Pause game if this would cause a scrolling event."
3160 for dummy in range(i):
3161 if game.options & OPTION_CURSES:
3162 (y, x) = curwnd.getyx()
3163 (my, mx) = curwnd.getmaxyx()
3164 if curwnd == message_window and y >= my - 2:
3170 except curses.error:
3175 if rows and linecount >= rows:
3178 sys.stdout.write('\n')
3181 "Utter a line with no following line feed."
3182 if game.options & OPTION_CURSES:
3186 sys.stdout.write(line)
3196 if not replayfp or replayfp.closed: # Don't slow down replays
3199 if game.options & OPTION_CURSES:
3203 if not replayfp or replayfp.closed:
3207 "Get a line of input."
3208 if game.options & OPTION_CURSES:
3209 line = curwnd.getstr() + "\n"
3212 if replayfp and not replayfp.closed:
3214 line = replayfp.readline()
3217 prout("*** Replay finished")
3220 elif line[0] != "#":
3223 line = raw_input() + "\n"
3229 "Change windows -- OK for this to be a no-op in tty mode."
3231 if game.options & OPTION_CURSES:
3233 curses.curs_set(wnd == fullscreen_window or wnd == message_window or wnd == prompt_window)
3236 "Clear to end of line -- can be a no-op in tty mode"
3237 if game.options & OPTION_CURSES:
3242 "Clear screen -- can be a no-op in tty mode."
3244 if game.options & OPTION_CURSES:
3250 def textcolor(color=DEFAULT):
3251 if game.options & OPTION_COLOR:
3252 if color == DEFAULT:
3254 elif color == BLACK:
3255 curwnd.attron(curses.color_pair(curses.COLOR_BLACK));
3257 curwnd.attron(curses.color_pair(curses.COLOR_BLUE));
3258 elif color == GREEN:
3259 curwnd.attron(curses.color_pair(curses.COLOR_GREEN));
3261 curwnd.attron(curses.color_pair(curses.COLOR_CYAN));
3263 curwnd.attron(curses.color_pair(curses.COLOR_RED));
3264 elif color == MAGENTA:
3265 curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA));
3266 elif color == BROWN:
3267 curwnd.attron(curses.color_pair(curses.COLOR_YELLOW));
3268 elif color == LIGHTGRAY:
3269 curwnd.attron(curses.color_pair(curses.COLOR_WHITE));
3270 elif color == DARKGRAY:
3271 curwnd.attron(curses.color_pair(curses.COLOR_BLACK) | curses.A_BOLD);
3272 elif color == LIGHTBLUE:
3273 curwnd.attron(curses.color_pair(curses.COLOR_BLUE) | curses.A_BOLD);
3274 elif color == LIGHTGREEN:
3275 curwnd.attron(curses.color_pair(curses.COLOR_GREEN) | curses.A_BOLD);
3276 elif color == LIGHTCYAN:
3277 curwnd.attron(curses.color_pair(curses.COLOR_CYAN) | curses.A_BOLD);
3278 elif color == LIGHTRED:
3279 curwnd.attron(curses.color_pair(curses.COLOR_RED) | curses.A_BOLD);
3280 elif color == LIGHTMAGENTA:
3281 curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA) | curses.A_BOLD);
3282 elif color == YELLOW:
3283 curwnd.attron(curses.color_pair(curses.COLOR_YELLOW) | curses.A_BOLD);
3284 elif color == WHITE:
3285 curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD);
3288 if game.options & OPTION_COLOR:
3289 curwnd.attron(curses.A_REVERSE)
3292 # Things past this point have policy implications.
3296 "Hook to be called after moving to redraw maps."
3297 if game.options & OPTION_CURSES:
3300 setwnd(srscan_window)
3304 setwnd(status_window)
3305 status_window.clear()
3306 status_window.move(0, 0)
3307 setwnd(report_window)
3308 report_window.clear()
3309 report_window.move(0, 0)
3311 setwnd(lrscan_window)
3312 lrscan_window.clear()
3313 lrscan_window.move(0, 0)
3314 lrscan(silent=False)
3316 def put_srscan_sym(w, sym):
3317 "Emit symbol for short-range scan."
3318 srscan_window.move(w.i+1, w.j*2+2)
3319 srscan_window.addch(sym)
3320 srscan_window.refresh()
3323 "Enemy fall down, go boom."
3324 if game.options & OPTION_CURSES:
3326 setwnd(srscan_window)
3327 srscan_window.attron(curses.A_REVERSE)
3328 put_srscan_sym(w, game.quad[w.i][w.j])
3332 srscan_window.attroff(curses.A_REVERSE)
3333 put_srscan_sym(w, game.quad[w.i][w.j])
3334 curses.delay_output(500)
3335 setwnd(message_window)
3338 "Sound and visual effects for teleportation."
3339 if game.options & OPTION_CURSES:
3341 setwnd(message_window)
3343 prouts(" . . . . . ")
3344 if game.options & OPTION_CURSES:
3345 #curses.delay_output(1000)
3349 def tracktorpedo(origin, w, step, i, n, iquad):
3350 "Torpedo-track animation."
3351 if not game.options & OPTION_CURSES:
3355 proutn(_("Track for torpedo number %d- ") % (i+1))
3358 proutn(_("Torpedo track- "))
3359 elif step==4 or step==9:
3363 if not damaged(DSRSENS) or game.condition=="docked":
3364 if i != 0 and step == 1:
3367 if (iquad=='.') or (iquad==' '):
3368 put_srscan_sym(w, '+')
3372 put_srscan_sym(w, iquad)
3374 curwnd.attron(curses.A_REVERSE)
3375 put_srscan_sym(w, iquad)
3379 curwnd.attroff(curses.A_REVERSE)
3380 put_srscan_sym(w, iquad)
3385 "Display the current galaxy chart."
3386 if game.options & OPTION_CURSES:
3387 setwnd(message_window)
3388 message_window.clear()
3390 if game.options & OPTION_TTY:
3395 def prstat(txt, data):
3397 if game.options & OPTION_CURSES:
3399 setwnd(status_window)
3401 proutn(" " * (NSYM - len(txt)))
3404 if game.options & OPTION_CURSES:
3405 setwnd(report_window)
3407 # Code from moving.c begins here
3409 def imove(course=None, noattack=False):
3410 "Movement execution for warp, impulse, supernova, and tractor-beam events."
3413 def newquadrant(noattack):
3414 # Leaving quadrant -- allow final enemy attack
3415 # Don't do it if being pushed by Nova
3416 if len(game.enemies) != 0 and not noattack:
3418 for enemy in game.enemies:
3419 finald = (w - enemy.location).distance()
3420 enemy.kavgd = 0.5 * (finald + enemy.kdist)
3421 # Stas Sergeev added the condition
3422 # that attacks only happen if Klingons
3423 # are present and your skill is good.
3424 if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3425 attack(torps_ok=False)
3428 # check for edge of galaxy
3432 if course.final.i < 0:
3433 course.final.i = -course.final.i
3435 if course.final.j < 0:
3436 course.final.j = -course.final.j
3438 if course.final.i >= GALSIZE*QUADSIZE:
3439 course.final.i = (GALSIZE*QUADSIZE*2) - course.final.i
3441 if course.final.j >= GALSIZE*QUADSIZE:
3442 course.final.j = (GALSIZE*QUADSIZE*2) - course.final.j
3450 if game.nkinks == 3:
3451 # Three strikes -- you're out!
3455 prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
3456 prout(_("AT THE EDGE OF THE GALAXY. THE THIRD TIME YOU TRY THIS,"))
3457 prout(_("YOU WILL BE DESTROYED."))
3458 # Compute final position in new quadrant
3459 if trbeam: # Don't bother if we are to be beamed
3461 game.quadrant = course.final.quadrant()
3462 game.sector = course.final.sector()
3464 prout(_("Entering Quadrant %s.") % game.quadrant)
3465 game.quad[game.sector.i][game.sector.j] = game.ship
3467 if game.skill>SKILL_NOVICE:
3468 attack(torps_ok=False)
3470 def check_collision(h):
3471 iquad = game.quad[h.i][h.j]
3473 # object encountered in flight path
3474 stopegy = 50.0*course.distance/game.optime
3475 if iquad in ('T', 'K', 'C', 'S', 'R', '?'):
3476 for enemy in game.enemies:
3477 if enemy.location == game.sector:
3479 collision(rammed=False, enemy=enemy)
3483 prouts(_("***RED ALERT! RED ALERT!"))
3485 proutn("***" + crmshp())
3486 proutn(_(" pulled into black hole at Sector %s") % h)
3487 # Getting pulled into a black hole was certain
3488 # death in Almy's original. Stas Sergeev added a
3489 # possibility that you'll get timewarped instead.
3491 for m in range(NDEVICES):
3492 if game.damage[m]>0:
3494 probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
3495 if (game.options & OPTION_BLKHOLE) and withprob(1-probf):
3505 prout(_(" encounters Tholian web at %s;") % h)
3507 prout(_(" blocked by object at %s;") % h)
3508 proutn(_("Emergency stop required "))
3509 prout(_("%2d units of energy.") % int(stopegy))
3510 game.energy -= stopegy
3511 if game.energy <= 0:
3518 prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
3519 game.inorbit = False
3520 # If tractor beam is to occur, don't move full distance
3521 if game.state.date+game.optime >= scheduled(FTBEAM):
3523 game.condition = "red"
3524 course.distance = course.distance*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
3525 game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
3527 game.quad[game.sector.i][game.sector.j] = '.'
3528 for m in range(course.moves):
3531 if course.origin.quadrant() != course.location.quadrant():
3532 newquadrant(noattack)
3534 elif check_collision(w):
3535 print "Collision detected"
3539 # We're in destination quadrant -- compute new average enemy distances
3540 game.quad[game.sector.i][game.sector.j] = game.ship
3542 for enemy in game.enemies:
3543 finald = (w-enemy.location).distance()
3544 enemy.kavgd = 0.5 * (finald + enemy.kdist)
3545 enemy.kdist = finald
3546 game.enemies.sort(lambda x, y: cmp(x.kdist, y.kdist))
3547 if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3548 attack(torps_ok=False)
3549 for enemy in game.enemies:
3550 enemy.kavgd = enemy.kdist
3553 setwnd(message_window)
3557 "Dock our ship at a starbase."
3559 if game.condition == "docked" and verbose:
3560 prout(_("Already docked."))
3563 prout(_("You must first leave standard orbit."))
3565 if not game.base.is_valid() or abs(game.sector.i-game.base.i) > 1 or abs(game.sector.j-game.base.j) > 1:
3566 prout(crmshp() + _(" not adjacent to base."))
3568 game.condition = "docked"
3572 if game.energy < game.inenrg:
3573 game.energy = game.inenrg
3574 game.shield = game.inshld
3575 game.torps = game.intorps
3576 game.lsupres = game.inlsr
3577 game.state.crew = FULLCREW
3578 if not damaged(DRADIO) and \
3579 ((is_scheduled(FCDBAS) or game.isatb == 1) and not game.iseenit):
3580 # get attack report from base
3581 prout(_("Lt. Uhura- \"Captain, an important message from the starbase:\""))
3585 def cartesian(loc1=None, loc2=None):
3587 return game.quadrant * QUADSIZE + game.sector
3589 return game.quadrant * QUADSIZE + loc1
3591 return loc1 * QUADSIZE + loc2
3593 def getcourse(isprobe):
3594 "Get a course and distance from the user."
3596 dquad = copy.copy(game.quadrant)
3597 navmode = "unspecified"
3601 if game.landed and not isprobe:
3602 prout(_("Dummy! You can't leave standard orbit until you"))
3603 proutn(_("are back aboard the ship."))
3606 while navmode == "unspecified":
3607 if damaged(DNAVSYS):
3609 prout(_("Computer damaged; manual navigation only"))
3611 prout(_("Computer damaged; manual movement only"))
3616 key = scanner.next()
3618 proutn(_("Manual or automatic- "))
3621 elif key == "IHALPHA":
3622 if scanner.sees("manual"):
3624 key = scanner.next()
3626 elif scanner.sees("automatic"):
3627 navmode = "automatic"
3628 key = scanner.next()
3636 prout(_("(Manual navigation assumed.)"))
3638 prout(_("(Manual movement assumed.)"))
3642 if navmode == "automatic":
3643 while key == "IHEOL":
3645 proutn(_("Target quadrant or quadrant§or- "))
3647 proutn(_("Destination sector or quadrant§or- "))
3650 key = scanner.next()
3654 xi = int(round(scanner.real))-1
3655 key = scanner.next()
3659 xj = int(round(scanner.real))-1
3660 key = scanner.next()
3662 # both quadrant and sector specified
3663 xk = int(round(scanner.real))-1
3664 key = scanner.next()
3668 xl = int(round(scanner.real))-1
3674 # only one pair of numbers was specified
3676 # only quadrant specified -- go to center of dest quad
3679 dsect.j = dsect.i = 4 # preserves 1-origin behavior
3681 # only sector specified
3685 if not dquad.valid_quadrant() or not dsect.valid_sector():
3692 prout(_("Helmsman Sulu- \"Course locked in for Sector %s.\"") % dsect)
3694 prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
3695 # the actual deltas get computed here
3696 delta.j = dquad.j-game.quadrant.j + (dsect.j-game.sector.j)/(QUADSIZE*1.0)
3697 delta.i = game.quadrant.i-dquad.i + (game.sector.i-dsect.i)/(QUADSIZE*1.0)
3699 while key == "IHEOL":
3700 proutn(_("X and Y displacements- "))
3703 key = scanner.next()
3708 delta.j = scanner.real
3709 key = scanner.next()
3713 delta.i = scanner.real
3714 # Check for zero movement
3715 if delta.i == 0 and delta.j == 0:
3718 if itemp == "verbose" and not isprobe:
3720 prout(_("Helmsman Sulu- \"Aye, Sir.\""))
3722 return course(bearing=delta.bearing(), distance=delta.distance())
3725 def __init__(self, bearing, distance, origin=None):
3726 self.distance = distance
3727 self.bearing = bearing
3729 self.origin = cartesian(game.quadrant, game.sector)
3731 self.origin = origin
3732 # The bearing() code we inherited from FORTRAN is actually computing
3733 # clockface directions!
3734 if self.bearing < 0.0:
3735 self.bearing += 12.0
3736 self.angle = ((15.0 - self.bearing) * 0.5235988)
3738 self.origin = cartesian(game.quadrant, game.sector)
3740 self.origin = cartesian(game.quadrant, origin)
3741 self.increment = coord(-math.sin(self.angle), math.cos(self.angle))
3742 bigger = max(abs(self.increment.i), abs(self.increment.j))
3743 self.increment /= bigger
3744 self.moves = int(round(10*self.distance*bigger))
3746 self.final = (self.location + self.moves*self.increment).roundtogrid()
3748 self.location = self.origin
3751 return self.location.roundtogrid() == self.final
3753 "Next step on course."
3755 self.nextlocation = self.location + self.increment
3756 samequad = (self.location.quadrant() == self.nextlocation.quadrant())
3757 self.location = self.nextlocation
3760 return self.location.quadrant()
3762 return self.location.sector()
3763 def power(self, warp):
3764 return self.distance*(warp**3)*(game.shldup+1)
3765 def time(self, warp):
3766 return 10.0*self.distance/warp**2
3769 "Move under impulse power."
3771 if damaged(DIMPULS):
3774 prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
3776 if game.energy > 30.0:
3778 course = getcourse(isprobe=False)
3781 power = 20.0 + 100.0*course.distance
3784 if power >= game.energy:
3785 # Insufficient power for trip
3787 prout(_("First Officer Spock- \"Captain, the impulse engines"))
3788 prout(_("require 20.0 units to engage, plus 100.0 units per"))
3789 if game.energy > 30:
3790 proutn(_("quadrant. We can go, therefore, a maximum of %d") %
3791 int(0.01 * (game.energy-20.0)-0.05))
3792 prout(_(" quadrants.\""))
3794 prout(_("quadrant. They are, therefore, useless.\""))
3797 # Make sure enough time is left for the trip
3798 game.optime = course.dist/0.095
3799 if game.optime >= game.state.remtime:
3800 prout(_("First Officer Spock- \"Captain, our speed under impulse"))
3801 prout(_("power is only 0.95 sectors per stardate. Are you sure"))
3802 proutn(_("we dare spend the time?\" "))
3805 # Activate impulse engines and pay the cost
3806 imove(course, noattack=False)
3810 power = 20.0 + 100.0*course.dist
3811 game.energy -= power
3812 game.optime = course.dist/0.095
3813 if game.energy <= 0:
3817 def warp(course, involuntary):
3818 "ove under warp drive."
3819 blooey = False; twarp = False
3820 if not involuntary: # Not WARPX entry
3822 if game.damage[DWARPEN] > 10.0:
3825 prout(_("Engineer Scott- \"The warp engines are damaged, Sir.\""))
3827 if damaged(DWARPEN) and game.warpfac > 4.0:
3830 prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
3831 prout(_(" is repaired, I can only give you warp 4.\""))
3833 # Read in course and distance
3836 course = getcourse(isprobe=False)
3839 # Make sure starship has enough energy for the trip
3840 # Note: this formula is slightly different from the C version,
3841 # and lets you skate a bit closer to the edge.
3842 if course.power(game.warpfac) >= game.energy:
3843 # Insufficient power for trip
3846 prout(_("Engineering to bridge--"))
3847 if not game.shldup or 0.5*course.power(game.warpfac) > game.energy:
3848 iwarp = (game.energy/(course.dist+0.05)) ** 0.333333333
3850 prout(_("We can't do it, Captain. We don't have enough energy."))
3852 proutn(_("We don't have enough energy, but we could do it at warp %d") % iwarp)
3855 prout(_("if you'll lower the shields."))
3859 prout(_("We haven't the energy to go that far with the shields up."))
3861 # Make sure enough time is left for the trip
3862 game.optime = course.time(game.warpfac)
3863 if game.optime >= 0.8*game.state.remtime:
3865 prout(_("First Officer Spock- \"Captain, I compute that such"))
3866 proutn(_(" a trip would require approximately %2.0f") %
3867 (100.0*game.optime/game.state.remtime))
3868 prout(_(" percent of our"))
3869 proutn(_(" remaining time. Are you sure this is wise?\" "))
3875 if game.warpfac > 6.0:
3876 # Decide if engine damage will occur
3877 # ESR: Seems wrong. Probability of damage goes *down* with distance?
3878 prob = course.distance*(6.0-game.warpfac)**2/66.666666666
3879 if prob > randreal():