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 # SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
15 # SPDX-License-Identifier: BSD-2-Clause
17 # pylint: disable=line-too-long,superfluous-parens,too-many-lines,invalid-name,missing-function-docstring,missing-class-docstring,multiple-statements,too-many-branches,too-many-statements,too-many-locals,too-many-nested-blocks,too-many-return-statements,too-many-instance-attributes,global-statement,no-else-break,no-else-return,no-else-continue,too-few-public-methods,too-many-boolean-expressions,consider-using-f-string,consider-using-enumerate,consider-using-with,unspecified-encoding,consider-using-generator
19 # pylint: disable=multiple-imports
20 import os, sys, math, curses, time, pickle, copy, gettext, getpass
24 # This import only works on Unixes. The intention is to enable
25 # Ctrl-P, Ctrl-N, and friends in Cmd.
27 # pylint: disable=unused-import
29 except ImportError: # pragma: no cover
34 docpath = (".", "doc/", "/usr/share/doc/sst/")
38 return gettext.gettext(st)
41 # Rolling our own LCG because Python changed its incompatibly in 3.2.
42 # Thus, we needed to have our own to be 2/3 polyglot; it will be
43 # helpful when and if we ever forward-port to another language.
47 # LCG PRNG parameters tested against
48 # Knuth vol. 2. by the authors of ADVENT
57 randomizer.LCG_A * game.lcg_x + randomizer.LCG_C
59 return old_x / randomizer.LCG_M
63 v = randomizer.random()
65 # logfp.write("#withprob(%.2f) -> %s\n" % (p, v < p))
70 v = randomizer.random()
74 v = args[0] + int(v * (args[1] - args[0]))
76 # logfp.write("#integer%s -> %s\n" % (args, v))
81 v = randomizer.random()
83 v *= args[0] # from [0, args[0])
85 v = args[0] + v * (args[1] - args[0]) # from [args[0], args[1])
87 # logfp.write("#real%s -> %f\n" % (args, v))
93 # logfp.write("#seed(%d)\n" % n)
94 game.lcg_x = n % randomizer.LCG_M
105 GALSIZE = 8 # Galaxy size in quadrants
106 NINHAB = GALSIZE * GALSIZE // 2 # Number of inhabited worlds
107 MAXUNINHAB = 10 # Maximum uninhabited worlds
108 QUADSIZE = 10 # Quadrant size in sectors
109 BASEMIN = 2 # Minimum starbases
110 BASEMAX = GALSIZE * GALSIZE // 12 # Maximum starbases
111 MAXKLGAME = 127 # Maximum Klingons per game
112 MAXKLQUAD = 9 # Maximum Klingons per quadrant
113 FULLCREW = 428 # Crew size. BSD Trek was 387, that's wrong
114 FOREVER = 1e30 # Time for the indefinite future
115 MAXBURST = 3 # Max # of torps you can launch in one turn
116 MINCMDR = 10 # Minimum number of Klingon commanders
117 DOCKFAC = 0.25 # Repair faster when docked
118 PHASEFAC = 2.0 # Phaser attenuation factor
120 ALGERON = 2311 # Date of the Treaty of Algeron
142 class TrekError(Exception):
146 class JumpOut(Exception):
151 return chr(ord("a") + n - 1)
155 def __init__(self, x=None, y=None):
159 def valid_quadrant(self):
162 and (self.j is not None)
164 and (self.i < GALSIZE)
166 and (self.j < GALSIZE)
169 def valid_sector(self):
172 and (self.j is not None)
174 and (self.i < QUADSIZE)
176 and (self.j < QUADSIZE)
179 def invalidate(self):
180 self.i = self.j = None
182 def __eq__(self, other):
183 return other is not None and self.i == other.i and self.j == other.j
185 def __ne__(self, other):
186 return other is None or self.i != other.i or self.j != other.j
188 def __add__(self, other):
189 return Coord(self.i + other.i, self.j + other.j)
191 def __sub__(self, other):
192 return Coord(self.i - other.i, self.j - other.j)
194 def __mul__(self, other):
195 return Coord(self.i * other, self.j * other)
197 def __rmul__(self, other):
198 return Coord(self.i * other, self.j * other)
200 def __div__(self, other): # pragma: no cover
201 return Coord(self.i / other, self.j / other)
203 def __truediv__(self, other): # pragma: no cover
204 return Coord(self.i / other, self.j / other)
206 def __floordiv__(self, other): # pragma: no cover
207 return Coord(self.i // other, self.j // other)
209 def __mod__(self, other):
210 return Coord(self.i % other, self.j % other)
212 def __rtruediv__(self, other): # pragma: no cover
213 return Coord(self.i / other, self.j / other)
215 def __rfloordiv__(self, other): # pragma: no cover
216 return Coord(self.i // other, self.j // other)
218 def roundtogrid(self):
219 return Coord(int(round(self.i)), int(round(self.j)))
221 def distance(self, other=None):
224 return math.sqrt((self.i - other.i) ** 2 + (self.j - other.j) ** 2)
227 return 1.90985 * math.atan2(self.j, self.i)
246 # print "Location %s -> %s" % (self, (self / QUADSIZE).roundtogrid())
247 return self.roundtogrid() // QUADSIZE
250 return self.roundtogrid() % QUADSIZE
253 if self.i is None or self.j is None:
254 return "Nowhere" # pragma: no cover
255 if game.options & OPTION_ALPHAMERIC:
256 return letterize(self.i + 1) + str(self.j + 1)
257 return "%s - %s" % (self.i + 1, self.j + 1)
263 "Do not anger the Space Thingy!"
266 self.location = Coord()
269 return (q.i, q.j) == (self.location.i, self.location.j)
274 self.name = None # string-valued if inhabited
275 self.quadrant = Coord() # quadrant located
276 self.pclass = None # could be ""M", "N", "O", or "destroyed"
277 self.crystals = "absent" # could be "mined", "present", "absent"
278 self.known = "unknown" # could be "unknown", "known", "shuttle_down"
279 self.inhabited = False # is it inhabited?
289 self.starbase = False
292 self.supernova = False
294 self.status = "secure" # Could be "secure", "distressed", "enslaved"
297 return "<Quadrant: %(klingons)d>" % self.__dict__ # pragma: no cover
305 self.starbase = False
309 return "<%s,%s,%s>" % (
316 def fill2d(size, fillfun):
317 "Fill an empty list in 2D."
319 for i in range(size):
321 for j in range(size):
322 lst[i].append(fillfun(i, j))
328 self.snap = False # snapshot taken
329 self.crew = 0 # crew complement
330 self.nscrem = 0 # remaining super commanders
331 self.starkl = 0 # destroyed stars
332 self.basekl = 0 # destroyed bases
333 self.nromrem = 0 # Romulans remaining
334 self.nplankl = 0 # destroyed uninhabited planets
335 self.nworldkl = 0 # destroyed inhabited planets
336 self.planets = [] # Planet information
337 self.date = 0.0 # stardate
338 self.remres = 0 # remaining resources
339 self.remtime = 0 # remaining time
340 self.baseq = [] # Base quadrant coordinates
341 self.kcmdr = [] # Commander quadrant coordinates
342 self.kscmdr = Coord() # Supercommander quadrant coordinates
344 self.galaxy = fill2d(GALSIZE, lambda i_unused, j_unused: Quadrant())
346 self.chart = fill2d(GALSIZE, lambda i_unused, j_unused: Page())
349 for i in range(GALSIZE):
350 for j in range(GALSIZE):
351 yield (i, j, self.galaxy[i][j])
356 self.date = None # A real number
357 self.quadrant = None # A coord structure
361 OPTION_ALL = 0xFFFFFFFF
362 OPTION_TTY = 0x00000001 # old interface
363 OPTION_CURSES = 0x00000002 # new interface
364 OPTION_IOMODES = 0x00000003 # cover both interfaces
365 OPTION_PLANETS = 0x00000004 # planets and mining (> 1974)
366 OPTION_THOLIAN = 0x00000008 # Tholians and their webs (UT 1979 version)
367 OPTION_SUPERCMDR = 0x00000010 # Supercommanders (UT 1979 version)
368 OPTION_PROBE = 0x00000020 # deep-space probes (DECUS version, 1980)
369 OPTION_MVBADDY = 0x00000040 # more enemies can move (Almy, 1979?)
370 OPTION_RAMMING = 0x00000080 # enemies may ram Enterprise (Almy, 1979?)
371 OPTION_ALMY = 0x00000100 # Almy's death ray upgrade (1997?)
372 OPTION_AUTOPASS = 0x00000200 # Autogenerate password (Almy, 1997?)
373 OPTION_BASE = 0x00000400 # bases have good shields (Stas, 2005)
374 OPTION_BLKHOLE = 0x00000800 # black hole may timewarp you (Stas, 2005)
375 OPTION_SHOWME = 0x00001000 # bracket Enterprise in chart (ESR, 2005)
376 OPTION_WORLDS = 0x00002000 # logic for inhabited worlds (ESR, 2006)
377 OPTION_AUTOSCAN = 0x00004000 # automatic LRSCAN before CHART (ESR, 2006)
378 OPTION_COLOR = 0x00008000 # enable color display (ESR, 2010)
379 OPTION_CAPTURE = 0x00010000 # Enable BSD-Trek capture (Almy, 2013).
380 OPTION_CLOAK = 0x10020000 # Enable BSD-Trek capture (Almy, 2013).
381 OPTION_DOTFILL = 0x01040000 # fix dotfill glitch in chart (ESR, 2019)
382 OPTION_ALPHAMERIC = 0x00080000 # Alpha Y coordinates (ESR, 2023)
385 "ALL": (OPTION_ALL, 0),
386 "TTY": (OPTION_TTY, 0),
387 "IOMODES": (OPTION_IOMODES, 0),
388 "PLANETS": (OPTION_PLANETS, 1974),
389 "THOLIAN": (OPTION_THOLIAN, 1979),
390 "SUPERCMDR": (OPTION_SUPERCMDR, 1979),
391 "PROBE": (OPTION_PROBE, 1980),
392 "MVBADDY": (OPTION_MVBADDY, 1981), # year bumped to make it distinct
393 "RAMMING": (OPTION_RAMMING, 1982), # year bumped to make it distinct
394 "ALMY": (OPTION_ALMY, 1997),
395 "AUTOPASS": (OPTION_AUTOPASS, 1998), # year bumped to make it distinct
396 "BASE": (OPTION_BASE, 2004), # year bumped to make it distinct
397 "BLKHOLE": (OPTION_BLKHOLE, 2004), # year bumped to make it distinct
398 "SHOWME": (OPTION_SHOWME, 2005),
399 "WORLDS": (OPTION_WORLDS, 2006),
400 "AUTOSCAN": (OPTION_AUTOSCAN, 2007), # year bumped to make it distinct
401 "COLOR": (OPTION_COLOR, 2010),
402 "CAPTURE": (OPTION_CAPTURE, 2013),
403 "CLOAK": (OPTION_CLOAK, 2014), # year bumped to make it distinct
404 "DOTFILL": (OPTION_DOTFILL, 2019),
405 "ALPHAMERIC": (OPTION_ALPHAMERIC, 2023),
426 NDEVICES = 17 # Number of devices
437 return game.damage[dev] != 0.0
441 return not damaged(DRADIO) or game.condition == "docked"
444 # Define future events
445 FSPY = 0 # Spy event happens always (no future[] entry)
446 # can cause SC to tractor beam Enterprise
447 FSNOVA = 1 # Supernova
448 FTBEAM = 2 # Commander tractor beams Enterprise
449 FSNAP = 3 # Snapshot for time warp
450 FBATTAK = 4 # Commander attacks base
451 FCDBAS = 5 # Commander destroys base
452 FSCMOVE = 6 # Supercommander moves (might attack base)
453 FSCDBAS = 7 # Supercommander destroys base
454 FDSPROB = 8 # Move deep space probe
455 FDISTR = 9 # Emit distress call from an inhabited world
456 FENSLV = 10 # Inhabited word is enslaved
457 FREPRO = 11 # Klingons build a ship in an enslaved system
460 # Abstract out the event handling -- underlying data structures will change
461 # when we implement stateful events
462 def findevent(evtype): # pragma: no cover
463 return game.future[evtype]
467 def __init__(self, etype=None, loc=None, power=None):
469 self.location = Coord()
474 self.power = power # enemy energy level
475 game.enemies.append(self)
478 motion = loc != self.location
479 if self.location.i is not None and self.location.j is not None:
482 game.quad[self.location.i][self.location.j] = "#"
484 game.quad[self.location.i][self.location.j] = "."
486 self.location = copy.copy(loc)
487 game.quad[self.location.i][self.location.j] = self.type
488 self.kdist = self.kavgd = (game.sector - loc).distance()
490 self.location = Coord()
491 self.kdist = self.kavgd = None
492 # Guard prevents failure on Tholian or thingy
493 if self in game.enemies:
494 game.enemies.remove(self)
498 return "<%s,%s,%f>" % (self.type, self.location, self.power) # pragma: no cover
503 self.options = None # Game options
504 self.state = Snapshot() # A snapshot structure
505 self.snapsht = Snapshot() # Last snapshot taken for time-travel purposes
506 self.quad = None # contents of our quadrant
507 self.damage = [0.0] * NDEVICES # damage encountered
508 self.future = [] # future events
512 self.future.append(Event())
513 self.passwd = None # Self Destruct password
515 self.quadrant = None # where we are in the large
516 self.sector = None # where we are in the small
517 self.tholian = None # Tholian enemy object
518 self.base = None # position of base in current quadrant
519 self.battle = None # base coordinates being attacked
520 self.plnet = None # location of planet in quadrant
521 self.gamewon = False # Finished!
522 self.ididit = False # action taken -- allows enemy to attack
523 self.alive = False # we are alive (not killed)
524 self.justin = False # just entered quadrant
525 self.shldup = False # shields are up
526 self.shldchg = False # shield is changing (affects efficiency)
527 self.iscate = False # super commander is here
528 self.ientesc = False # attempted escape from supercommander
529 self.resting = False # rest time
530 self.icraft = False # Kirk in Galileo
531 self.landed = False # party on planet (true), on ship (false)
532 self.alldone = False # game is now finished
533 self.neutz = False # Romulan Neutral Zone
534 self.isarmed = False # probe is armed
535 self.inorbit = False # orbiting a planet
536 self.imine = False # mining
537 self.icrystl = False # dilithium crystals aboard
538 self.iseenit = False # seen base attack report
539 self.thawed = False # thawed game
540 self.condition = None # "green", "yellow", "red", "docked", "dead"
541 self.iscraft = None # "onship", "offship", "removed"
542 self.skill = SKILL_NONE # Player skill level
543 self.inkling = 0 # initial number of klingons
544 self.inbase = 0 # initial number of bases
545 self.incom = 0 # initial number of commanders
546 self.inscom = 0 # initial number of commanders
547 self.inrom = 0 # initial number of commanders
548 self.instar = 0 # initial stars
549 self.intorps = 0 # initial/max torpedoes
550 self.torps = 0 # number of torpedoes
551 self.ship = 0 # ship type -- 'E' is Enterprise
552 self.abandoned = 0 # count of crew abandoned in space
553 self.length = 0 # length of game
554 self.klhere = 0 # klingons here
555 self.casual = 0 # causalties
556 self.nhelp = 0 # calls for help
557 self.nkinks = 0 # count of energy-barrier crossings
558 self.iplnet = None # planet # in quadrant
559 self.inplan = 0 # initial planets
560 self.irhere = 0 # Romulans in quadrant
561 self.isatb = 0 # =2 if super commander is attacking base
562 self.tourn = None # tournament number
563 self.nprobes = 0 # number of probes available
564 self.inresor = 0.0 # initial resources
565 self.intime = 0.0 # initial time
566 self.inenrg = 0.0 # initial/max energy
567 self.inshld = 0.0 # initial/max shield
568 self.inlsr = 0.0 # initial life support resources
569 self.indate = 0.0 # initial date
570 self.energy = 0.0 # energy level
571 self.shield = 0.0 # shield level
572 self.warpfac = 0.0 # warp speed
573 self.lsupres = 0.0 # life support reserves
574 self.optime = 0.0 # time taken by current operation
575 self.damfac = 0.0 # damage factor
576 self.lastchart = 0.0 # time star chart was last updated
577 self.cryprob = 0.0 # probability that crystal will work
578 self.probe = None # object holding probe course info
579 self.height = 0.0 # height of orbit around planet
580 self.score = 0.0 # overall score
581 self.perdate = 0.0 # rate of kills
582 self.idebug = False # Debugging instrumentation enabled?
583 self.cdebug = False # Debugging instrumentation for curses enabled?
584 self.statekscmdr = None # No SuperCommander coordinates yet.
585 self.brigcapacity = 400 # Enterprise brig capacity
586 self.brigfree = 400 # How many klingons can we put in the brig?
587 self.kcaptured = 0 # Total Klingons captured, for scoring.
588 self.iscloaked = False # Cloaking device on?
589 self.ncviol = 0 # Algreon treaty violations
590 self.isviolreported = False # We have been warned
591 self.lcg_x = 0 # LCG generator value
594 return sum([q.klingons for (_i, _j, q) in list(self.state.traverse())])
597 # Stas thinks this should be (C expression):
598 # game.remkl() + len(game.state.kcmdr) > 0 ?
599 # game.state.remres/(game.remkl() + 4*len(game.state.kcmdr)) : 99
600 # He says the existing expression is prone to divide-by-zero errors
601 # after killing the last klingon when score is shown -- perhaps also
602 # if the only remaining klingon is SCOM.
603 self.state.remtime = self.state.remres / (
604 self.remkl() + 4 * len(self.state.kcmdr)
608 "Are there Klingons remaining?"
636 # Code from ai.c begins here
640 "Would this quadrant welcome another Klingon?"
643 and not game.state.galaxy[iq.i][iq.j].supernova
644 and game.state.galaxy[iq.i][iq.j].klingons < MAXKLQUAD
648 def tryexit(enemy, look, irun):
649 "A bad guy attempts to bug out."
651 iq.i = game.quadrant.i + (look.i + (QUADSIZE - 1)) // QUADSIZE - 1
652 iq.j = game.quadrant.j + (look.j + (QUADSIZE - 1)) // QUADSIZE - 1
653 if not welcoming(iq):
655 if enemy.type == "R":
656 return [] # Romulans cannot escape!
658 # avoid intruding on another commander's territory
659 if enemy.type == "C":
660 if iq in game.state.kcmdr:
662 # refuse to leave if currently attacking starbase
663 if game.battle == game.quadrant:
665 # don't leave if over 1000 units of energy
666 if enemy.power > 1000.0:
668 oldloc = copy.copy(enemy.location)
669 # handle local matters related to escape
672 if game.condition != "docked":
674 # Handle global matters related to escape
675 game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
676 game.state.galaxy[iq.i][iq.j].klingons += 1
677 if enemy.type == "S":
681 schedule(FSCMOVE, 0.2777)
683 game.state.kscmdr = iq
685 for cmdr in game.state.kcmdr:
686 if cmdr == game.quadrant:
687 game.state.kcmdr.append(iq)
689 # report move out of quadrant.
690 return [(True, enemy, oldloc, iq)]
693 # The bad-guy movement algorithm:
695 # 1. Enterprise has "force" based on condition of phaser and photon torpedoes.
696 # If both are operating full strength, force is 1000. If both are damaged,
697 # force is -1000. Having shields down subtracts an additional 1000.
699 # 2. Enemy has forces equal to the energy of the attacker plus
700 # 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
701 # 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
703 # Attacker Initial energy levels (nominal):
704 # Klingon Romulan Commander Super-Commander
705 # Novice 400 700 1200
707 # Good 450 800 1300 1750
708 # Expert 475 850 1350 1875
709 # Emeritus 500 900 1400 2000
710 # VARIANCE 75 200 200 200
712 # Enemy vessels only move prior to their attack. In Novice - Good games
713 # only commanders move. In Expert games, all enemy vessels move if there
714 # is a commander present. In Emeritus games all enemy vessels move.
716 # 3. If Enterprise is not docked, an aggressive action is taken if enemy
717 # forces are 1000 greater than Enterprise.
719 # Agressive action on average cuts the distance between the ship and
720 # the enemy to 1/4 the original.
722 # 4. At lower energy advantage, movement units are proportional to the
723 # advantage with a 650 advantage being to hold ground, 800 to move forward
724 # 1, 950 for two, 150 for back 4, etc. Variance of 100.
726 # If docked, is reduced by roughly 1.75*game.skill, generally forcing a
727 # retreat, especially at high skill levels.
729 # 5. Motion is limited to skill level, except for SC hi-tailing it out.
732 def movebaddy(enemy):
733 "Tactical movement for the bad guys."
737 # This should probably be just (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
738 if game.skill >= SKILL_EXPERT:
741 (game.quadrant in game.state.kcmdr) * 2
742 + (game.state.kscmdr == game.quadrant) * 2
749 nbaddys = (game.quadrant in game.state.kcmdr) + (
750 game.state.kscmdr == game.quadrant
752 old_dist = enemy.kdist
753 mdist = int(old_dist + 0.5) # Nearest integer distance
754 # If SC, check with spy to see if should hi-tail it
755 if enemy.type == "S" and (
756 enemy.power <= 500.0 or (game.condition == "docked" and not damaged(DPHOTON))
761 # decide whether to advance, retreat, or hold position
762 forces = enemy.power + 100.0 * len(game.enemies) + 400 * (nbaddys - 1)
764 forces += 1000 # Good for enemy if shield is down!
765 if not damaged(DPHASER) or not damaged(DPHOTON):
766 if damaged(DPHASER): # phasers damaged
769 forces -= 0.2 * (game.energy - 2500.0)
770 if damaged(DPHOTON): # photon torpedoes damaged
773 forces -= 50.0 * game.torps
775 # phasers and photon tubes both out!
778 if forces <= 1000.0 and game.condition != "docked": # Typical situation
779 motion = ((forces + rnd.real(200)) / 150.0) - 5.0
781 if forces > 1000.0: # Very strong -- move in for kill
782 motion = (1.0 - rnd.real()) ** 2 * old_dist + 1.0
783 if game.condition == "docked" and (
784 game.options & OPTION_BASE
785 ): # protected by base -- back off !
786 motion -= game.skill * (2.0 - rnd.real() ** 2)
789 "=== MOTION = %d, FORCES = %1.2f, " % (motion, forces)
791 # don't move if no motion
794 # Limit motion according to skill
795 if abs(motion) > game.skill:
800 # calculate preferred number of steps
801 nsteps = abs(int(motion))
802 if motion > 0 and nsteps > mdist:
803 nsteps = mdist # don't overshoot
804 nsteps = min(nsteps, QUADSIZE) # This shouldn't be necessary
805 nsteps = max(nsteps, 1) # This shouldn't be necessary
807 proutn("NSTEPS = %d:" % nsteps) # pragma: no cover
808 # Compute preferred values of delta X and Y
809 m = game.sector - enemy.location
810 if 2.0 * abs(m.i) < abs(m.j):
812 if 2.0 * abs(m.j) < abs(game.sector.i - enemy.location.i):
814 m = (motion * m).sgn()
815 goto = enemy.location
817 for ll in range(nsteps):
819 proutn(" %d" % (ll + 1)) # pragma: no cover
820 # Check if preferred position available
831 attempts = 0 # Settle mysterious hang problem
832 while attempts < 20 and not success:
834 if look.i < 0 or look.i >= QUADSIZE:
836 return tryexit(enemy, look, irun)
837 if krawli == m.i or m.j == 0:
839 look.i = goto.i + krawli
841 elif look.j < 0 or look.j >= QUADSIZE:
843 return tryexit(enemy, look, irun)
844 if krawlj == m.j or m.i == 0:
846 look.j = goto.j + krawlj
848 elif (game.options & OPTION_RAMMING) and game.quad[look.i][look.j] != ".":
849 # See if enemy should ram ship
850 if game.quad[look.i][look.j] == game.ship and enemy.type in ("C", "S"):
851 collision(rammed=True, enemy=enemy)
853 if krawli != m.i and m.j != 0:
854 look.i = goto.i + krawli
856 elif krawlj != m.j and m.i != 0:
857 look.j = goto.j + krawlj
860 break # we have failed
866 proutn(repr(goto)) # pragma: no cover
870 skip(1) # pragma: no cover
871 # Enemy moved, but is still in sector
872 return [(False, enemy, old_dist, goto)]
876 "Sequence Klingon tactical movement."
878 prout("== MOVCOM") # pragma: no cover
879 # Figure out which Klingon is the commander (or Supercommander)
882 if game.quadrant in game.state.kcmdr:
883 for enemy in game.enemies:
884 if enemy.type == "C":
885 tacmoves += movebaddy(enemy)
886 if game.state.kscmdr == game.quadrant:
887 for enemy in game.enemies:
888 if enemy.type == "S":
889 tacmoves += movebaddy(enemy)
891 # If skill level is high, move other Klingons and Romulans too!
892 # Move these last so they can base their actions on what the
894 if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
895 for enemy in game.enemies:
896 if enemy.type in ("K", "R"):
897 tacmoves += movebaddy(enemy)
901 def movescom(iq, avoid):
902 "Supercommander movement helper."
903 # Avoid quadrants with bases if we want to avoid Enterprise
904 if not welcoming(iq) or (avoid and iq in game.state.baseq):
906 if game.justin and not game.iscate:
909 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons -= 1
910 game.state.kscmdr = iq
911 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons += 1
912 if game.state.kscmdr == game.quadrant:
913 # SC has scooted, remove him from current quadrant
918 for enemy in game.enemies:
919 if enemy.type == "S":
922 if game.condition != "docked":
925 # check for a helpful planet
926 for i in range(game.inplan):
928 game.state.planets[i].quadrant == game.state.kscmdr
929 and game.state.planets[i].crystals == "present"
932 game.state.planets[i].pclass = "destroyed"
933 game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].planet = None
936 prout(_('Lt. Uhura- "Captain, Starfleet Intelligence reports'))
938 _(" a planet in Quadrant %s has been destroyed")
941 prout(_(' by the Super-commander."'))
943 return True # looks good!
946 def supercommander():
947 "Move the Super Commander."
954 prout("== SUPERCOMMANDER") # pragma: no cover
955 # Decide on being active or passive
956 avoid = (game.incom - len(game.state.kcmdr) + game.inkling - game.remkl()) / (
957 game.state.date + 0.01 - game.indate
958 ) < 0.1 * game.skill * (game.skill + 1.0) or (game.state.date - game.indate) < 3.0
959 if not game.iscate and avoid:
960 # compute move away from Enterprise
961 idelta = game.state.kscmdr - game.quadrant
962 if idelta.distance() > 2.0:
964 idelta.i = game.state.kscmdr.j - game.quadrant.j
965 idelta.j = game.quadrant.i - game.state.kscmdr.i
967 # compute distances to starbases
968 if not game.state.baseq:
972 sc = game.state.kscmdr
973 for (i, base) in enumerate(game.state.baseq):
974 basetbl.append((i, (base - sc).distance()))
975 if len(game.state.baseq) > 1:
976 basetbl.sort(key=lambda x: x[1])
977 # look for nearest base without a commander, no Enterprise, and
978 # without too many Klingons, and not already under attack.
979 ifindit = iwhichb = 0
980 for (i2, base) in enumerate(game.state.baseq):
981 i = basetbl[i2][0] # bug in original had it not finding nearest
982 if base == game.quadrant or base == game.battle or not welcoming(base):
984 # if there is a commander, and no other base is appropriate,
985 # we will take the one with the commander
986 for cmdr in game.state.kcmdr:
987 if base == cmdr and ifindit != 2:
991 else: # no commander -- use this one
996 return # Nothing suitable -- wait until next time
997 ibq = game.state.baseq[iwhichb]
998 # decide how to move toward base
999 idelta = ibq - game.state.kscmdr
1000 # Maximum movement is 1 quadrant in either or both axes
1001 idelta = idelta.sgn()
1002 # try moving in both x and y directions
1003 # there was what looked like a bug in the Almy C code here,
1004 # but it might be this translation is just wrong.
1005 iq = game.state.kscmdr + idelta
1006 if not movescom(iq, avoid):
1007 # failed -- try some other maneuvers
1008 if idelta.i == 0 or idelta.j == 0:
1009 # attempt angle move
1011 iq.j = game.state.kscmdr.j + 1
1012 if not movescom(iq, avoid):
1013 iq.j = game.state.kscmdr.j - 1
1016 iq.i = game.state.kscmdr.i + 1
1017 if not movescom(iq, avoid):
1018 iq.i = game.state.kscmdr.i - 1
1021 # try moving just in x or y
1022 iq.j = game.state.kscmdr.j
1023 if not movescom(iq, avoid):
1024 iq.j = game.state.kscmdr.j + idelta.j
1025 iq.i = game.state.kscmdr.i
1028 if len(game.state.baseq) == 0:
1031 for ibq in game.state.baseq:
1032 if ibq == game.state.kscmdr and game.state.kscmdr == game.battle:
1035 return # no, don't attack base!
1036 game.iseenit = False
1038 schedule(FSCDBAS, rnd.real(1.0, 3.0))
1039 if is_scheduled(FCDBAS):
1040 postpone(FSCDBAS, scheduled(FCDBAS) - game.state.date)
1041 if not communicating():
1046 _('Lt. Uhura- "Captain, the starbase in Quadrant %s')
1051 " reports that it is under attack from the Klingon Super-commander."
1055 _(' It can survive until stardate %d."') % int(scheduled(FSCDBAS))
1057 if not game.resting:
1059 prout(_('Mr. Spock- "Captain, shall we cancel the rest period?"'))
1062 game.resting = False
1063 game.optime = 0.0 # actually finished
1065 # Check for intelligence report
1066 if not game.idebug and (
1068 or (not communicating())
1069 or not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].charted
1073 prout(_('Lt. Uhura- "Captain, Starfleet Intelligence reports'))
1074 prout(_(" the Super-commander is in Quadrant %s.") % game.state.kscmdr)
1080 if not game.tholian or game.justin:
1083 if game.tholian.location.i == 0 and game.tholian.location.j == 0:
1085 tid.j = QUADSIZE - 1
1086 elif game.tholian.location.i == 0 and game.tholian.location.j == QUADSIZE - 1:
1087 tid.i = QUADSIZE - 1
1088 tid.j = QUADSIZE - 1
1090 game.tholian.location.i == QUADSIZE - 1
1091 and game.tholian.location.j == QUADSIZE - 1
1093 tid.i = QUADSIZE - 1
1095 elif game.tholian.location.i == QUADSIZE - 1 and game.tholian.location.j == 0:
1098 else: # pragma: no cover
1099 # something is wrong!
1100 game.tholian.move(None)
1101 prout("***Internal error: Tholian in a bad spot.")
1103 # do nothing if we are blocked
1104 if game.quad[tid.i][tid.j] not in (".", "#"):
1106 here = copy.copy(game.tholian.location)
1107 delta = (tid - game.tholian.location).sgn()
1109 while here.i != tid.i:
1111 if game.quad[here.i][here.j] == ".":
1112 game.tholian.move(here)
1114 while here.j != tid.j:
1116 if game.quad[here.i][here.j] == ".":
1117 game.tholian.move(here)
1118 # check to see if all holes plugged
1119 for i in range(QUADSIZE):
1120 if game.quad[0][i] != "#" and game.quad[0][i] != "T":
1122 if game.quad[QUADSIZE - 1][i] != "#" and game.quad[QUADSIZE - 1][i] != "T":
1124 if game.quad[i][0] != "#" and game.quad[i][0] != "T":
1126 if game.quad[i][QUADSIZE - 1] != "#" and game.quad[i][QUADSIZE - 1] != "T":
1128 # All plugged up -- Tholian splits
1129 game.quad[game.tholian.location.i][game.tholian.location.j] = "#"
1131 prout(crmena(True, "T", "sector", game.tholian) + _(" completes web."))
1132 game.tholian.move(None)
1136 # Code from battle.c begins here
1140 "Change cloaking-device status."
1141 if game.ship == "F":
1142 prout(_("Ye Faerie Queene hath no cloaking device."))
1145 key = scanner.nexttok()
1152 if key == "IHALPHA":
1153 if scanner.sees("on"):
1155 prout(_("The cloaking device has already been switched on."))
1158 elif scanner.sees("off"):
1159 if not game.iscloaked:
1160 prout(_("The cloaking device has already been switched off."))
1167 if not game.iscloaked:
1168 proutn(_("Switch cloaking device on? "))
1173 proutn(_("Switch cloaking device off? "))
1180 if action == "CLOFF":
1181 if game.irhere and game.state.date >= ALGERON and not game.isviolreported:
1184 'Spock- "Captain, the Treaty of Algeron is in effect.\n Are you sure this is wise?"'
1189 prout('Engineer Scott- "Aye, Sir."')
1190 game.iscloaked = False
1191 if game.irhere and game.state.date >= ALGERON and not game.isviolreported:
1193 _("The Romulan ship discovers you are breaking the Treaty of Algeron!")
1196 game.isviolreported = True
1198 # if (neutz and game.state.date >= ALGERON) finish(FCLOAK);
1201 if action == "CLON":
1203 prout(_('Engineer Scott- "The cloaking device is damaged, Sir."'))
1206 if game.condition == "docked":
1207 prout(_("You cannot cloak while docked."))
1209 if game.state.date >= ALGERON and not game.isviolreported:
1210 prout(_('Spock- "Captain, using the cloaking device is a violation'))
1211 prout(_(" of the Treaty of Algeron. Considering the alternatives,"))
1212 proutn(_(" are you sure this is wise? "))
1215 prout(_('Engineer Scott- "Cloaking device has engaging, Sir..."'))
1217 prout(_('Engineer Scott- "Cloaking device has engaged, Sir."'))
1218 game.iscloaked = True
1220 if game.irhere and game.state.date >= ALGERON and not game.isviolreported:
1222 _("The Romulan ship discovers you are breaking the Treaty of Algeron!")
1225 game.isviolreported = True
1228 def doshield(shraise):
1229 "Change shield status."
1235 key = scanner.nexttok()
1236 if key == "IHALPHA":
1237 if scanner.sees("transfer"):
1240 if damaged(DSHIELD):
1241 prout(_("Shields damaged and down."))
1243 if scanner.sees("up"):
1245 elif scanner.sees("down"):
1247 if action == "NONE":
1248 proutn(_("Do you wish to change shield energy? "))
1251 elif damaged(DSHIELD):
1252 prout(_("Shields damaged and down."))
1255 proutn(_("Shields are up. Do you want them down? "))
1262 proutn(_("Shields are down. Do you want them up? "))
1268 if action == "SHUP": # raise shields
1270 prout(_("Shields already up."))
1274 if game.condition != "docked":
1276 prout(_("Shields raised."))
1277 if game.energy <= 0:
1279 prout(_("Shields raising uses up last of energy."))
1284 elif action == "SHDN":
1286 prout(_("Shields already down."))
1290 prout(_("Shields lowered."))
1293 elif action == "NRG":
1294 while scanner.nexttok() != "IHREAL":
1296 proutn(_("Energy to transfer to shields- "))
1301 if nrg > game.energy:
1302 prout(_("Insufficient ship energy."))
1305 if game.shield + nrg >= game.inshld:
1306 prout(_("Shield energy maximized."))
1307 if game.shield + nrg > game.inshld:
1308 prout(_("Excess energy requested returned to ship energy"))
1309 game.energy -= game.inshld - game.shield
1310 game.shield = game.inshld
1312 if nrg < 0.0 and game.energy - nrg > game.inenrg:
1313 # Prevent shield drain loophole
1315 prout(_("Engineering to bridge--"))
1316 prout(_(" Scott here. Power circuit problem, Captain."))
1317 prout(_(" I can't drain the shields."))
1320 if game.shield + nrg < 0:
1321 prout(_("All shield energy transferred to ship."))
1322 game.energy += game.shield
1325 proutn(_('Scotty- "'))
1327 prout(_('Transferring energy to shields."'))
1329 prout(_('Draining energy from shields."'))
1336 "Choose a device to damage, at random."
1338 105, # DSRSENS: short range scanners 10.5%
1339 105, # DLRSENS: long range scanners 10.5%
1340 120, # DPHASER: phasers 12.0%
1341 120, # DPHOTON: photon torpedoes 12.0%
1342 25, # DLIFSUP: life support 2.5%
1343 65, # DWARPEN: warp drive 6.5%
1344 70, # DIMPULS: impulse engines 6.5%
1345 135, # DSHIELD: deflector shields 13.5%
1346 30, # DRADIO: subspace radio 3.0%
1347 45, # DSHUTTL: shuttle 4.5%
1348 15, # DCOMPTR: computer 1.5%
1349 20, # NAVCOMP: navigation system 2.0%
1350 75, # DTRANSP: transporter 7.5%
1351 20, # DSHCTRL: high-speed shield controller 2.0%
1352 10, # DDRAY: death ray 1.0%
1353 30, # DDSP: deep-space probes 3.0%
1354 10, # DCLOAK: the cloaking device 1.0
1356 assert sum(weights) == 1000
1357 idx = rnd.integer(1000)
1359 for (i, w) in enumerate(weights):
1363 return None # pragma: no cover
1366 def collision(rammed, enemy):
1367 "Collision handling for ramming events."
1368 prouts(_("***RED ALERT! RED ALERT!"))
1370 prout(_("***COLLISION IMMINENT."))
1374 hardness = {"R": 1.5, "C": 2.0, "S": 2.5, "T": 0.5, "?": 4.0}.get(enemy.type, 1.0)
1376 proutn(_(" rammed by "))
1379 proutn(crmena(False, enemy.type, "sector", enemy.location))
1381 proutn(_(" (original position)"))
1383 deadkl(enemy.location, enemy.type, game.sector)
1384 prout("***" + crmshp() + " heavily damaged.")
1385 icas = rnd.integer(10, 30)
1386 prout(_("***Sickbay reports %d casualties") % icas)
1388 game.state.crew -= icas
1389 # In the pre-SST2K versions, all devices got equiprobably damaged,
1390 # which was silly. Instead, pick up to half the devices at
1391 # random according to our weighting table,
1392 ncrits = rnd.integer(NDEVICES // 2)
1396 if game.damage[dev] < 0:
1398 extradm = (10.0 * hardness * rnd.real() + 1.0) * game.damfac
1399 # Damage for at least time of travel!
1400 game.damage[dev] += game.optime + extradm
1402 prout(_("***Shields are down."))
1410 def torpedo(origin, bearing, dispersion, number, nburst):
1411 "Let a photon torpedo fly"
1412 if not damaged(DSRSENS) or game.condition == "docked":
1413 setwnd(srscan_window)
1415 setwnd(message_window)
1416 ac = bearing + 0.25 * dispersion # dispersion is a random variable
1417 bullseye = (15.0 - bearing) * 0.5235988
1418 track = course(bearing=ac, distance=QUADSIZE, origin=cartesian(origin))
1419 bumpto = Coord(0, 0)
1420 # Loop to move a single torpedo
1421 setwnd(message_window)
1422 for step in range(1, QUADSIZE * 2):
1423 if not track.nextstep():
1426 if not w.valid_sector():
1428 iquad = game.quad[w.i][w.j]
1429 tracktorpedo(w, step, number, nburst, iquad)
1433 setwnd(message_window)
1434 if not damaged(DSRSENS) or game.condition == "docked":
1435 skip(1) # start new line after text track
1436 if iquad in ("E", "F"): # Hit our ship
1438 prout(_("Torpedo hits %s.") % crmshp())
1443 * (w - origin).distance()
1444 * math.fabs(math.sin(bullseye - track.angle))
1446 newcnd() # we're blown out of dock
1447 if game.landed or game.condition == "docked":
1448 return hit # Cheat if on a planet
1449 # In the C/FORTRAN version, dispersion was 2.5 radians, which
1450 # is 143 degrees, which is almost exactly 4.8 clockface units
1451 displacement = course(
1452 track.bearing + rnd.real(-2.4, 2.4), distance=2 ** 0.5
1454 displacement.nextstep()
1455 bumpto = displacement.sector()
1456 if not bumpto.valid_sector():
1458 if game.quad[bumpto.i][bumpto.j] == " ":
1461 if game.quad[bumpto.i][bumpto.j] != ".":
1462 # can't move into object
1464 game.sector = bumpto
1466 game.quad[w.i][w.j] = "."
1467 game.quad[bumpto.i][bumpto.j] = iquad
1468 prout(_(" displaced by blast to Sector %s ") % bumpto)
1469 for enemy in game.enemies:
1470 enemy.kdist = enemy.kavgd = (game.sector - enemy.location).distance()
1473 elif iquad in ("C", "S", "R", "K"): # Hit a regular enemy
1475 if iquad in ("C", "S") and rnd.withprob(0.05):
1476 prout(crmena(True, iquad, "sector", w) + _(" uses anti-photon device;"))
1477 prout(_(" torpedo neutralized."))
1479 for enemy in game.enemies:
1480 if w == enemy.location:
1481 kp = math.fabs(enemy.power)
1486 * (w - origin).distance()
1487 * math.fabs(math.sin(bullseye - track.angle))
1496 if enemy.power == 0:
1499 proutn(crmena(True, iquad, "sector", w))
1500 displacement = course(
1501 track.bearing + rnd.real(-2.4, 2.4), distance=2 ** 0.5, origin=w
1503 displacement.nextstep()
1504 bumpto = displacement.sector()
1505 if game.quad[bumpto.i][bumpto.j] == " ":
1506 prout(_(" buffeted into black hole."))
1507 deadkl(w, iquad, bumpto)
1509 if not bumpto.valid_sector():
1510 prout(_(" damaged but not destroyed."))
1512 if game.quad[bumpto.i][bumpto.j] != ".":
1513 prout(_(" damaged but not destroyed."))
1515 prout(_(" damaged-- displaced by blast to Sector %s ") % bumpto)
1516 enemy.location = bumpto
1517 game.quad[w.i][w.j] = "."
1518 game.quad[bumpto.i][bumpto.j] = iquad
1519 for tenemy in game.enemies:
1520 tenemy.kdist = tenemy.kavgd = (
1521 game.sector - tenemy.location
1525 else: # pragma: no cover
1526 prout("Internal error, no enemy where expected!")
1529 elif iquad == "B": # Hit a base
1531 prout(_("***STARBASE DESTROYED.."))
1532 game.state.baseq = [x for x in game.state.baseq if x != game.quadrant]
1533 game.quad[w.i][w.j] = "."
1534 game.base.invalidate()
1535 game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
1536 game.state.chart[game.quadrant.i][game.quadrant.j].starbase = False
1537 game.state.basekl += 1
1540 elif iquad == "P": # Hit a planet
1541 prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
1542 game.state.nplankl += 1
1543 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
1544 game.iplnet.pclass = "destroyed"
1546 game.plnet.invalidate()
1547 game.quad[w.i][w.j] = "."
1549 # captain perishes on planet
1552 elif iquad == "@": # Hit an inhabited world -- very bad!
1553 prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
1554 game.state.nworldkl += 1
1555 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
1556 game.iplnet.pclass = "destroyed"
1558 game.plnet.invalidate()
1559 game.quad[w.i][w.j] = "."
1561 # captain perishes on planet
1563 prout(_("The torpedo destroyed an inhabited planet."))
1565 elif iquad == "*": # Hit a star
1566 if rnd.withprob(0.9):
1570 crmena(True, "*", "sector", w) + _(" unaffected by photon blast.")
1573 elif iquad == "?": # Hit a thingy
1575 prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
1577 prouts(_(" HACK! HACK! HACK! *CHOKE!* "))
1579 proutn(_("Mr. Spock-"))
1580 prouts(_(' "Fascinating!"'))
1584 elif iquad == " ": # Black hole
1586 prout(crmena(True, " ", "sector", w) + _(" swallows torpedo."))
1588 elif iquad == "#": # hit the web
1590 prout(_("***Torpedo absorbed by Tholian web."))
1592 elif iquad == "T": # Hit a Tholian
1597 * (w - origin).distance()
1598 * math.fabs(math.sin(bullseye - track.angle))
1602 game.quad[w.i][w.j] = "."
1607 proutn(crmena(True, "T", "sector", w))
1608 if rnd.withprob(0.05):
1609 prout(_(" survives photon blast."))
1611 prout(_(" disappears."))
1612 game.tholian.move(None)
1613 game.quad[w.i][w.j] = "#"
1618 proutn("Don't know how to handle torpedo collision with ")
1619 proutn(crmena(True, iquad, "sector", w))
1624 setwnd(message_window)
1625 prout(_("Torpedo missed."))
1630 "Critical-hit resolution."
1631 if hit < (275.0 - 25.0 * game.skill) * rnd.real(1.0, 1.5):
1633 ncrit = int(1.0 + hit / (500.0 + rnd.real(100)))
1634 proutn(_("***CRITICAL HIT--"))
1635 # Select devices and cause damage
1640 # Cheat to prevent shuttle damage unless on ship
1642 game.damage[j] < 0.0
1643 or (j == DSHUTTL and game.iscraft != "onship")
1644 or (j == DCLOAK and game.ship != "E" or j == DDRAY)
1648 extradm = (hit * game.damfac) / (ncrit * rnd.real(75, 100))
1649 game.damage[j] += extradm
1652 for (i, j) in enumerate(cdam):
1654 if skipcount % 3 == 2 and i < len(cdam) - 1:
1657 if i < len(cdam) - 1:
1659 prout(_(" damaged."))
1660 if damaged(DSHIELD) and game.shldup:
1661 prout(_("***Shields knocked down."))
1663 if damaged(DCLOAK) and game.iscloaked:
1664 prout(_("***Cloaking device rendered inoperative."))
1665 game.iscloaked = False
1668 def attack(torps_ok):
1669 # bad guy attacks us
1670 # torps_ok == False forces use of phasers in an attack
1673 # game could be over at this point, check
1683 prout("=== ATTACK!") # pragma: no cover
1684 # Tholian gets to move before attacking
1687 # if you have just entered the RNZ, you'll get a warning
1688 if game.neutz: # The one chance not to be attacked
1691 # commanders get a chance to tac-move towards you
1694 (game.quadrant in game.state.kcmdr or game.state.kscmdr == game.quadrant)
1697 or game.skill == SKILL_EMERITUS
1699 for (bugout, enemy, old, goto) in moveklings():
1701 # we know about this if either short or long range
1702 # sensors are working
1703 if damaged(DSRSENS) and damaged(DLRSENS) and game.condition != "docked":
1705 crmena(True, enemy.type, "sector", old)
1706 + (_(" escapes to Quadrant %s (and regains strength).") % goto)
1708 else: # Enemy still in-sector
1709 if enemy.move(goto):
1710 if not damaged(DSRSENS) or game.condition == "docked":
1712 _("*** %s from Sector %s")
1713 % (cramen(enemy.type), enemy.location)
1715 if enemy.kdist < old:
1716 proutn(_(" advances to "))
1718 proutn(_(" retreats to "))
1719 prout("Sector %s." % goto)
1721 # if no enemies remain after movement, we're done
1722 if len(game.enemies) == 0 or (len(game.enemies) == 1 and thing.at(game.quadrant)):
1724 # set up partial hits if attack happens during shield status change
1725 pfac = 1.0 / game.inshld
1727 chgfac = 0.25 + rnd.real(0.5)
1729 # message verbosity control
1730 if game.skill <= SKILL_FAIR:
1732 for enemy in game.enemies:
1734 continue # too weak to attack
1735 # compute hit strength and diminish shield power
1737 # Increase chance of photon torpedos if docked or enemy energy is low
1738 if game.condition == "docked":
1740 if enemy.power < 500:
1742 if enemy.type in ("T", "?"):
1744 # different enemies have different probabilities of throwing a torp
1747 or (enemy.type == "K" and r > 0.0005)
1748 or (enemy.type == "C" and r > 0.015)
1749 or (enemy.type == "R" and r > 0.3)
1750 or (enemy.type == "S" and r > 0.07)
1751 or (enemy.type == "?" and r > 0.05)
1753 if usephasers: # Enemy uses phasers
1754 if game.condition == "docked":
1755 continue # Don't waste the effort!
1756 attempt = True # Attempt to attack
1757 dustfac = rnd.real(0.8, 0.85)
1758 hit = enemy.power * math.pow(dustfac, enemy.kavgd)
1760 else: # Enemy uses photon torpedo
1761 # We should be able to make the bearing() method work here
1762 pcourse = 1.90985 * math.atan2(
1763 game.sector.j - enemy.location.j, enemy.location.i - game.sector.i
1766 proutn(_("***TORPEDO INCOMING"))
1767 if not damaged(DSRSENS):
1768 proutn(_(" From ") + crmena(False, enemy.type, where, enemy.location))
1771 dispersion = (rnd.real() + rnd.real()) * 0.5 - 0.5
1772 dispersion += 0.002 * enemy.power * dispersion
1773 hit = torpedo(enemy.location, pcourse, dispersion, number=1, nburst=1)
1774 if game.unwon() == 0:
1775 finish(FWON) # Klingons did themselves in!
1777 game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova
1780 return # Supernova or finished
1783 # incoming phaser or torpedo, shields may dissipate it
1784 if game.shldup or game.shldchg or game.condition == "docked":
1785 # shields will take hits
1786 propor = pfac * game.shield
1787 if game.condition == "docked":
1789 propor = max(propor, 0.1)
1790 hitsh = propor * chgfac * hit + 1.0
1791 absorb = 0.8 * hitsh
1792 if absorb > game.shield:
1793 absorb = game.shield
1794 game.shield -= absorb
1796 # taking a hit blasts us out of a starbase dock
1797 if game.condition == "docked":
1799 # but the shields may take care of it
1800 if propor > 0.1 and hit < 0.005 * game.energy:
1802 # hit from this opponent got through shields, so take damage
1804 proutn(_("%d unit hit") % int(hit))
1805 if (damaged(DSRSENS) and usephasers) or game.skill <= SKILL_FAIR:
1806 proutn(_(" on the ") + crmshp())
1807 if not damaged(DSRSENS) and usephasers:
1808 prout(_(" from ") + crmena(False, enemy.type, where, enemy.location))
1810 # Decide if hit is critical
1816 if game.energy <= 0:
1817 # Returning home upon your shield, not with it...
1820 if not attempt and game.condition == "docked":
1821 prout(_("***Enemies decide against attacking your ship."))
1822 percent = 100.0 * pfac * game.shield + 0.5
1824 # Shields fully protect ship
1825 proutn(_("Enemy attack reduces shield strength to "))
1827 # Emit message if starship suffered hit(s)
1829 proutn(_("Energy left %2d shields ") % int(game.energy))
1832 elif not damaged(DSHIELD):
1835 proutn(_("damaged, "))
1836 prout(_("%d%%, torpedoes left %d") % (percent, game.torps))
1837 # Check if anyone was hurt
1838 if hitmax >= 200 or hittot >= 500:
1839 icas = rnd.integer(int(hittot * 0.015))
1842 prout(_('Mc Coy- "Sickbay to bridge. We suffered %d casualties') % icas)
1843 prout(_(' in that last attack."'))
1845 game.state.crew -= icas
1846 # After attack, reset average distance to enemies
1847 for enemy in game.enemies:
1848 enemy.kavgd = enemy.kdist
1853 def deadkl(w, etype, mv):
1854 "Kill a Klingon, Tholian, Romulan, or Thingy."
1855 # Added mv to allow enemy to "move" before dying
1856 proutn(crmena(True, etype, "sector", mv))
1857 # Decide what kind of enemy it is and update appropriately
1859 # Chalk up a Romulan
1860 game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans -= 1
1862 game.state.nromrem -= 1
1871 # Killed some type of Klingon
1872 game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
1875 game.state.kcmdr.remove(game.quadrant)
1877 if game.state.kcmdr:
1878 schedule(FTBEAM, expran(1.0 * game.incom / len(game.state.kcmdr)))
1879 if is_scheduled(FCDBAS) and game.battle == game.quadrant:
1884 game.state.nscrem -= 1
1885 game.state.kscmdr.invalidate()
1890 # For each kind of enemy, finish message to player
1891 prout(_(" destroyed."))
1892 if game.unwon() == 0:
1895 # Remove enemy ship from arrays describing local conditions
1896 for e in game.enemies:
1904 "Return None if target is invalid, otherwise return a course angle."
1905 if not w.valid_sector():
1909 # C code this was translated from is wacky -- why the sign reversal?
1910 delta.j = w.j - game.sector.j
1911 delta.i = game.sector.i - w.i
1912 if delta == Coord(0, 0):
1914 prout(_('Spock- "Bridge to sickbay. Dr. McCoy,'))
1915 prout(_(" I recommend an immediate review of"))
1916 prout(_(" the Captain's psychological profile.\""))
1919 return delta.bearing()
1923 "Launch photon torpedo salvo."
1926 if damaged(DPHOTON):
1927 prout(_("Photon tubes damaged."))
1931 prout(_("No torpedoes left."))
1934 # First, get torpedo count
1937 if scanner.token == "IHALPHA":
1940 elif scanner.token == "IHEOL" or not scanner.waiting():
1941 prout(_("%d torpedoes left.") % game.torps)
1943 proutn(_("Number of torpedoes to fire- "))
1944 continue # Go back around to get a number
1945 else: # key == "IHREAL"
1951 if n <= 0: # abort command
1956 prout(_("Maximum of %d torpedoes per burst.") % MAXBURST)
1959 scanner.chew() # User requested more torps than available
1960 continue # Go back around
1961 break # All is good, go to next stage
1965 key = scanner.nexttok()
1966 if i == 0 and key == "IHEOL":
1967 break # no coordinate waiting, we will try prompting
1968 if i == 1 and key == "IHEOL":
1969 # direct all torpedoes at one target
1971 target.append(target[0])
1972 tcourse.append(tcourse[0])
1975 scanner.push(scanner.token)
1976 target.append(scanner.getcoord())
1977 if target[-1] is None:
1979 tcourse.append(targetcheck(target[-1]))
1980 if tcourse[-1] is None:
1983 if len(target) == 0:
1984 # prompt for each one
1986 proutn(_("Target sector for torpedo number %d- ") % (i + 1))
1988 target.append(scanner.getcoord())
1989 if target[-1] is None:
1991 tcourse.append(targetcheck(target[-1]))
1992 if tcourse[-1] is None:
1995 # Loop for moving <n> torpedoes
1997 if game.condition != "docked":
1999 dispersion = (rnd.real() + rnd.real()) * 0.5 - 0.5
2000 if math.fabs(dispersion) >= 0.47:
2002 dispersion *= rnd.real(1.2, 2.2)
2004 prouts(_("***TORPEDO NUMBER %d MISFIRES") % (i + 1))
2006 prouts(_("***TORPEDO MISFIRES."))
2009 prout(_(" Remainder of burst aborted."))
2010 if rnd.withprob(0.2):
2011 prout(_("***Photon tubes damaged by misfire."))
2012 game.damage[DPHOTON] = game.damfac * rnd.real(1.0, 3.0)
2016 elif game.shldup or game.condition == "docked":
2017 dispersion *= 1.0 + 0.0001 * game.shield
2018 torpedo(game.sector, tcourse[i], dispersion, number=i, nburst=n)
2021 or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova
2024 if game.unwon() <= 0:
2029 "Check for phasers overheating."
2031 checkburn = (rpow - 1500.0) * 0.00038
2032 if rnd.withprob(checkburn):
2033 prout(_('Weapons officer Sulu- "Phasers overheated, sir."'))
2034 game.damage[DPHASER] = game.damfac * rnd.real(1.0, 2.0) * (1.0 + checkburn)
2037 def checkshctrl(rpow):
2038 "Check shield control."
2040 if rnd.withprob(0.998):
2041 prout(_("Shields lowered."))
2043 # Something bad has happened
2044 prouts(_("***RED ALERT! RED ALERT!"))
2046 hit = rpow * game.shield / game.inshld
2047 game.energy -= rpow + hit * 0.8
2048 game.shield -= hit * 0.2
2049 if game.energy <= 0.0:
2050 prouts(_('Sulu- "Captain! Shield malf***********************"'))
2055 prouts(_('Sulu- "Captain! Shield malfunction! Phaser fire contained!"'))
2057 prout(_('Lt. Uhura- "Sir, all decks reporting damage."'))
2058 icas = rnd.integer(int(hit * 0.012))
2063 prout(_('McCoy to bridge- "Severe radiation burns, Jim.'))
2064 prout(_(' %d casualties so far."') % icas)
2066 game.state.crew -= icas
2068 prout(_("Phaser energy dispersed by shields."))
2069 prout(_("Enemy unaffected."))
2075 "Register a phaser hit on Klingons and Romulans."
2082 dustfac = rnd.real(0.9, 1.0)
2083 hit = wham * math.pow(dustfac, game.enemies[kk].kdist)
2084 kpini = game.enemies[kk].power
2085 kp = math.fabs(kpini)
2086 if PHASEFAC * hit < kp:
2088 if game.enemies[kk].power < 0:
2089 game.enemies[kk].power -= -kp
2091 game.enemies[kk].power -= kp
2092 kpow = game.enemies[kk].power
2093 w = game.enemies[kk].location
2095 if not damaged(DSRSENS):
2097 proutn(_("%d unit hit on ") % int(hit))
2099 proutn(_("Very small hit on "))
2100 ienm = game.quad[w.i][w.j]
2101 proutn(crmena(False, ienm, "sector", w))
2105 if game.unwon() == 0:
2110 else: # decide whether or not to emasculate klingon
2111 # pylint: disable=chained-comparison
2112 if kpow > 0 and rnd.withprob(0.9) and kpow <= rnd.real(0.4, 0.8) * kpini:
2113 prout(_('***Mr. Spock- "Captain, the vessel at Sector %s') % w)
2114 prout(_(' has just lost its firepower."'))
2115 game.enemies[kk].power = -kpow
2121 "Fire phasers at bad guys."
2125 irec = 0 # Cheating inhibitor
2134 # SR sensors and Computer are needed for automode
2135 if damaged(DSRSENS) or damaged(DCOMPTR):
2137 if game.condition == "docked":
2138 prout(_("Phasers can't be fired through base shields."))
2141 if damaged(DPHASER):
2142 prout(_("Phaser control damaged."))
2146 if damaged(DSHCTRL):
2147 prout(_("High speed shield control damaged."))
2150 if game.energy <= 200.0:
2151 prout(_("Insufficient energy to activate high-speed shield control."))
2154 prout(_('Weapons Officer Sulu- "High-speed shield control enabled, sir."'))
2156 # Original code so convoluted, I re-did it all
2157 # (That was Tom Almy talking about the C code, I think -- ESR)
2158 while automode == "NOTSET":
2159 key = scanner.nexttok()
2160 if key == "IHALPHA":
2161 if scanner.sees("manual"):
2162 if len(game.enemies) == 0:
2163 prout(_("There is no enemy present to select."))
2166 automode = "AUTOMATIC"
2169 key = scanner.nexttok()
2170 elif scanner.sees("automatic"):
2171 if (not itarg) and len(game.enemies) != 0:
2172 automode = "FORCEMAN"
2174 if len(game.enemies) == 0:
2175 prout(_("Energy will be expended into space."))
2176 automode = "AUTOMATIC"
2177 key = scanner.nexttok()
2178 elif scanner.sees("no"):
2183 elif key == "IHREAL":
2184 if len(game.enemies) == 0:
2185 prout(_("Energy will be expended into space."))
2186 automode = "AUTOMATIC"
2188 automode = "FORCEMAN"
2190 automode = "AUTOMATIC"
2193 if len(game.enemies) == 0:
2194 prout(_("Energy will be expended into space."))
2195 automode = "AUTOMATIC"
2197 automode = "FORCEMAN"
2199 proutn(_("Manual or automatic? "))
2204 if automode == "AUTOMATIC":
2205 if key == "IHALPHA" and scanner.sees("no"):
2207 key = scanner.nexttok()
2208 if key != "IHREAL" and len(game.enemies) != 0:
2209 prout(_("Phasers locked on target. Energy available: %.2f") % avail)
2214 for i in range(len(game.enemies)):
2216 math.fabs(game.enemies[i].power)
2217 / (PHASEFAC * math.pow(0.90, game.enemies[i].kdist))
2218 * rnd.real(1.01, 1.06)
2222 proutn(_("%d units required. ") % irec)
2224 proutn(_("Units to fire= "))
2225 key = scanner.nexttok()
2230 proutn(_("Energy available= %.2f") % avail)
2233 if not rpow > avail:
2239 key = scanner.nexttok()
2240 if key == "IHALPHA" and scanner.sees("no"):
2243 game.energy -= 200 # Go and do it!
2244 if checkshctrl(rpow):
2249 if len(game.enemies):
2252 for i in range(len(game.enemies)):
2256 hits[i] = math.fabs(game.enemies[i].power) / (
2257 PHASEFAC * math.pow(0.90, game.enemies[i].kdist)
2259 over = rnd.real(1.01, 1.06) * hits[i]
2261 powrem -= hits[i] + over
2262 if powrem <= 0 and temp < hits[i]:
2271 if extra > 0 and not game.alldone:
2273 proutn(_("*** Tholian web absorbs "))
2274 if len(game.enemies) > 0:
2275 proutn(_("excess "))
2276 prout(_("phaser energy."))
2278 prout(_("%d expended on empty space.") % int(extra))
2279 elif automode == "FORCEMAN":
2282 if damaged(DCOMPTR):
2283 prout(_("Battle computer damaged, manual fire only."))
2286 prouts(_("---WORKING---"))
2288 prout(_("Short-range-sensors-damaged"))
2289 prout(_("Insufficient-data-for-automatic-phaser-fire"))
2290 prout(_("Manual-fire-must-be-used"))
2292 elif automode == "MANUAL":
2294 for k in range(len(game.enemies)):
2295 aim = game.enemies[k].location
2296 ienm = game.quad[aim.i][aim.j]
2298 proutn(_("Energy available= %.2f") % (avail - 0.006))
2304 and not game.sector.distance(aim) < 2 ** 0.5
2305 and ienm in ("C", "S")
2307 prout(cramen(ienm) + _(" can't be located without short range scan."))
2310 hits[k] = 0 # prevent overflow -- thanks to Alexei Voitenko
2314 if itarg and k > kz:
2316 abs(game.enemies[k].power)
2317 / (PHASEFAC * math.pow(0.9, game.enemies[k].kdist))
2318 ) * rnd.real(1.01, 1.06) + 1.0
2321 if not damaged(DCOMPTR):
2326 proutn(_("units to fire at %s- ") % crmena(False, ienm, "sector", aim))
2327 key = scanner.nexttok()
2328 if key == "IHALPHA" and scanner.sees("no"):
2330 key = scanner.nexttok()
2332 if key == "IHALPHA":
2336 if k == 1: # Let me say I'm baffled by this
2339 if scanner.real < 0:
2343 hits.append(scanner.real)
2344 rpow += scanner.real
2345 # If total requested is too much, inform and start over
2347 prout(_("Available energy exceeded -- try again."))
2350 key = scanner.nexttok() # scan for next value
2352 # zero energy -- abort
2355 if key == "IHALPHA" and scanner.sees("no"):
2360 game.energy -= 200.0
2361 if checkshctrl(rpow):
2365 # Say shield raised or malfunction, if necessary
2371 if rnd.withprob(0.01):
2374 'Sulu- "Sir, the high-speed shield control has malfunctioned . . .'
2377 prouts(_(" CLICK CLICK POP . . ."))
2378 prout(_(" No response, sir!"))
2381 prout(_("Shields raised."))
2388 game.ididit = False # Nothing if we fail
2391 # Make sure there is room in the brig
2392 if game.brigfree == 0:
2393 prout(_("Security reports the brig is already full."))
2397 prout(_('Uhura- "We have no subspace radio communication, sir."'))
2400 if damaged(DTRANSP):
2401 prout(_('Scotty- "Transporter damaged, sir."'))
2404 # find out if there are any at all
2406 prout(_('Uhura- "Getting no response, sir."'))
2409 # if there is more than one Klingon, find out which one
2410 # Cruddy, just takes one at random. Should ask the captain.
2411 # Nah, just select the weakest one since it is most likely to
2412 # surrender (Tom Almy mod)
2413 klingons = [e for e in game.enemies if e.type == "K"]
2414 weakest = sorted(klingons, key=lambda e: e.power)[0]
2415 game.optime = 0.05 # This action will take some time
2416 game.ididit = True # So any others can strike back
2418 # check out that Klingon
2419 # The algorithm isn't that great and could use some more
2420 # intelligent design
2421 # x = 300 + 25*skill;
2422 x = game.energy / (weakest.power * len(klingons))
2423 # prout(_("Stats: energy = %s, kpower = %s, klingons = %s")
2424 # % (game.energy, weakest.power, len(klingons)))
2425 x *= 2.5 # would originally have been equivalent of 1.4,
2426 # but we want command to work more often, more humanely
2427 # prout(_("Prob = %.4f" % x))
2428 # x = 100; // For testing, of course!
2429 if x < rnd.real(100):
2430 # guess what, he surrendered!!!
2431 prout(_("Klingon captain at %s surrenders.") % weakest.location)
2435 _("%d Klingons commit suicide rather than be taken captive.")
2438 if i > game.brigfree:
2440 _("%d Klingons die because there is no room for them in the brig.")
2441 % (i - game.brigfree)
2445 prout(_("%d captives taken") % i)
2446 deadkl(weakest.location, weakest.type, game.sector)
2447 if game.unwon() <= 0:
2451 # big surprise, he refuses to surrender
2452 prout(_("Fat chance, captain!"))
2455 # Code from events.c begins here.
2457 # This isn't a real event queue a la BSD Trek yet -- you can only have one
2458 # event of each type active at any given time. Mostly these means we can
2459 # only have one FDISTR/FENSLV/FREPRO sequence going at any given time
2460 # BSD Trek, from which we swiped the idea, can have up to 5.
2463 def unschedule(evtype):
2464 "Remove an event from the schedule."
2465 game.future[evtype].date = FOREVER
2466 return game.future[evtype]
2469 def is_scheduled(evtype):
2470 "Is an event of specified type scheduled."
2471 return game.future[evtype].date != FOREVER
2474 def scheduled(evtype):
2475 "When will this event happen?"
2476 return game.future[evtype].date
2479 def schedule(evtype, offset):
2480 "Schedule an event of specified type."
2481 game.future[evtype].date = game.state.date + offset
2482 return game.future[evtype]
2485 def postpone(evtype, offset):
2486 "Postpone a scheduled event."
2487 game.future[evtype].date += offset
2491 "Rest period is interrupted by event."
2494 proutn(_('Mr. Spock- "Captain, shall we cancel the rest period?"'))
2496 game.resting = False
2503 "Run through the event queue looking for things to do."
2505 fintim = game.state.date + game.optime
2514 def tractorbeam(yank):
2515 "Tractor-beaming cases merge here."
2517 game.optime = (10.0 / (7.5 * 7.5)) * yank # 7.5 is yank rate (warp 7.5)
2519 prout("***" + crmshp() + _(" caught in long range tractor beam--"))
2520 # If Kirk & Co. screwing around on planet, handle
2521 atover(True) # atover(true) is Grab
2524 if game.icraft: # Caught in Galileo?
2527 # Check to see if shuttle is aboard
2528 if game.iscraft == "offship":
2530 if rnd.withprob(0.5):
2531 prout(_("Galileo, left on the planet surface, is captured"))
2532 prout(_("by aliens and made into a flying McDonald's."))
2533 game.damage[DSHUTTL] = -10
2534 game.iscraft = "removed"
2536 prout(_("Galileo, left on the planet surface, is well hidden."))
2538 game.quadrant = game.state.kscmdr
2540 game.quadrant = game.state.kcmdr[i]
2541 game.sector = randplace(QUADSIZE)
2544 + _(" is pulled to Quadrant %s, Sector %s") % (game.quadrant, game.sector)
2547 prout(_("(Remainder of rest/repair period cancelled.)"))
2548 game.resting = False
2550 if not damaged(DSHIELD) and game.shield > 0:
2551 doshield(shraise=True) # raise shields
2552 game.shldchg = False
2554 prout(_("(Shields not currently useable.)"))
2556 # Adjust finish time to time of tractor beaming?
2557 # fintim = game.state.date+game.optime
2558 attack(torps_ok=False)
2559 if not game.state.kcmdr:
2563 FTBEAM, game.optime + expran(1.5 * game.intime / len(game.state.kcmdr))
2567 "Code merges here for any commander destroying a starbase."
2568 # Not perfect, but will have to do
2569 # Handle case where base is in same quadrant as starship
2570 if game.battle == game.quadrant:
2571 game.state.chart[game.battle.i][game.battle.j].starbase = False
2572 game.quad[game.base.i][game.base.j] = "."
2573 game.base.invalidate()
2576 prout(_('Spock- "Captain, I believe the starbase has been destroyed."'))
2577 elif game.state.baseq and communicating():
2578 # Get word via subspace radio
2581 prout(_('Lt. Uhura- "Captain, Starfleet Command reports that'))
2583 _(" the starbase in Quadrant %s has been destroyed by") % game.battle
2586 prout(_("the Klingon Super-Commander"))
2588 prout(_("a Klingon Commander"))
2589 game.state.chart[game.battle.i][game.battle.j].starbase = False
2590 # Remove Starbase from galaxy
2591 game.state.galaxy[game.battle.i][game.battle.j].starbase = False
2592 game.state.baseq = [x for x in game.state.baseq if x != game.battle]
2594 # reinstate a commander's base attack
2598 game.battle.invalidate()
2600 if game.idebug: # pragma: no cover
2601 prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
2602 for i in range(1, NEVENTS):
2604 proutn("=== Supernova ")
2606 proutn("=== T Beam ")
2608 proutn("=== Snapshot ")
2610 proutn("=== Base Attack ")
2612 proutn("=== Base Destroy ")
2614 proutn("=== SC Move ")
2616 proutn("=== SC Base Destroy ")
2618 proutn("=== Probe Move ")
2620 proutn("=== Distress Call ")
2622 proutn("=== Enslavement ")
2624 proutn("=== Klingon Build ")
2626 prout("%.2f" % (scheduled(i)))
2629 radio_was_broken = damaged(DRADIO)
2632 # Select earliest extraneous event, evcode==0 if no events
2637 for l in range(1, NEVENTS):
2638 if game.future[l].date < datemin:
2641 prout("== Event %d fires" % evcode) # pragma: no cover
2642 datemin = game.future[l].date
2643 xtime = datemin - game.state.date
2645 game.energy -= xtime * 500.0
2646 if game.energy <= 0:
2649 game.state.date = datemin
2650 # Decrement Federation resources and recompute remaining time
2651 game.state.remres -= (game.remkl() + 4 * len(game.state.kcmdr)) * xtime
2653 if game.state.remtime <= 0:
2656 # Any crew left alive?
2657 if game.state.crew <= 0:
2660 # Is life support adequate?
2661 if damaged(DLIFSUP) and game.condition != "docked":
2662 if game.lsupres < xtime and game.damage[DLIFSUP] > game.lsupres:
2665 game.lsupres -= xtime
2666 if game.damage[DLIFSUP] <= xtime:
2667 game.lsupres = game.inlsr
2670 if game.condition == "docked":
2672 # Don't fix Deathray here
2673 for l in range(NDEVICES):
2674 if game.damage[l] > 0.0 and l != DDRAY:
2675 if game.damage[l] - repair > 0.0:
2676 game.damage[l] -= repair
2678 game.damage[l] = 0.0
2679 # If radio repaired, update star chart and attack reports
2680 if radio_was_broken and not damaged(DRADIO):
2681 prout(_('Lt. Uhura- "Captain, the sub-space radio is working and'))
2682 prout(_(" surveillance reports are coming in."))
2684 if not game.iseenit:
2688 prout(_(' The star chart is now up to date."'))
2690 # Cause extraneous event EVCODE to occur
2691 game.optime -= xtime
2692 if evcode == FSNOVA: # Supernova
2695 schedule(FSNOVA, expran(0.5 * game.intime))
2696 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
2698 elif evcode == FSPY: # Check with spy to see if SC should tractor beam
2700 game.state.nscrem == 0
2704 or game.condition == "docked"
2711 or (game.energy < 2000 and game.torps < 4 and game.shield < 1250)
2712 or (damaged(DPHASER) and (damaged(DPHOTON) or game.torps < 4))
2715 and (game.energy < 2500 or damaged(DPHASER))
2716 and (game.torps < 5 or damaged(DPHOTON))
2720 istract = ictbeam = True
2721 tractorbeam((game.state.kscmdr - game.quadrant).distance())
2724 elif evcode == FTBEAM: # Tractor beam
2725 if not game.state.kcmdr:
2728 i = rnd.integer(len(game.state.kcmdr))
2729 yank = (game.state.kcmdr[i] - game.quadrant).distance()
2730 if istract or game.condition == "docked" or game.iscloaked or yank == 0:
2731 # Drats! Have to reschedule
2734 game.optime + expran(1.5 * game.intime / len(game.state.kcmdr)),
2739 elif evcode == FSNAP: # Snapshot of the universe (for time warp)
2740 game.snapsht = copy.deepcopy(game.state)
2741 game.state.snap = True
2742 schedule(FSNAP, expran(0.5 * game.intime))
2743 elif evcode == FBATTAK: # Commander attacks starbase
2744 if not game.state.kcmdr or not game.state.baseq:
2749 ibq = None # Force battle location to persist past loop
2751 for ibq in game.state.baseq:
2752 for cmdr in game.state.kcmdr:
2755 and ibq != game.quadrant
2756 and ibq != game.state.kscmdr
2759 # no match found -- try later
2760 schedule(FBATTAK, expran(0.3 * game.intime))
2765 # commander + starbase combination found -- launch attack
2767 schedule(FCDBAS, rnd.real(1.0, 4.0))
2768 if game.isatb: # extra time if SC already attacking
2769 postpone(FCDBAS, scheduled(FSCDBAS) - game.state.date)
2770 game.future[FBATTAK].date = game.future[FCDBAS].date + expran(
2773 game.iseenit = False
2774 if not communicating():
2775 continue # No warning :-(
2779 prout(_('Lt. Uhura- "Captain, the starbase in Quadrant %s') % game.battle)
2780 prout(_(" reports that it is under attack and that it can"))
2781 prout(_(' hold out only until stardate %d."') % (int(scheduled(FCDBAS))))
2784 elif evcode == FSCDBAS: # Supercommander destroys base
2787 if not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].starbase:
2788 continue # WAS RETURN!
2790 game.battle = game.state.kscmdr
2792 elif evcode == FCDBAS: # Commander succeeds in destroying base
2793 if evcode == FCDBAS:
2796 not game.state.baseq
2797 or not game.state.galaxy[game.battle.i][game.battle.j].starbase
2799 game.battle.invalidate()
2801 # find the lucky pair
2802 for cmdr in game.state.kcmdr:
2803 if cmdr == game.battle:
2806 # No action to take after all
2809 elif evcode == FSCMOVE: # Supercommander moves
2810 schedule(FSCMOVE, 0.2777)
2815 and (not game.iscate or not game.justin)
2818 elif evcode == FDSPROB: # Move deep space probe
2819 schedule(FDSPROB, 0.01)
2820 if not game.probe.nextstep():
2822 not game.probe.quadrant().valid_quadrant()
2823 or game.state.galaxy[game.probe.quadrant().i][
2824 game.probe.quadrant().j
2827 # Left galaxy or ran into supernova
2831 proutn(_('Lt. Uhura- "The deep space probe '))
2832 if not game.probe.quadrant().valid_quadrant():
2833 prout(_('has left the galaxy."'))
2835 prout(_('is no longer transmitting."'))
2842 _('Lt. Uhura- "The deep space probe is now in Quadrant %s."')
2843 % game.probe.quadrant()
2845 pquad = game.probe.quadrant()
2846 pdest = game.state.galaxy[pquad.i][pquad.j]
2848 game.state.chart[pquad.i][pquad.j].klingons = pdest.klingons
2849 game.state.chart[pquad.i][pquad.j].starbase = pdest.starbase
2850 game.state.chart[pquad.i][pquad.j].stars = pdest.stars
2851 pdest.charted = True
2852 game.probe.moves -= 1 # One less to travel
2853 if game.probe.arrived() and game.isarmed and pdest.stars:
2854 supernova(game.probe.quadrant()) # fire in the hole!
2856 if game.state.galaxy[pquad.i][pquad.j].supernova:
2858 elif evcode == FDISTR: # inhabited system issues distress call
2860 # try a whole bunch of times to find something suitable
2861 for i in range(100):
2862 # need a quadrant which is not the current one,
2863 # which has some stars which are inhabited and
2864 # not already under attack, which is not
2865 # supernova'ed, and which has some Klingons in it
2866 w = randplace(GALSIZE)
2867 q = game.state.galaxy[w.i][w.j]
2871 or not q.planet.inhabited
2873 or q.status != "secure"
2878 # can't seem to find one; ignore this call
2881 "=== Couldn't find location for distress event."
2882 ) # pragma: no cover
2884 # got one!! Schedule its enslavement
2885 ev = schedule(FENSLV, expran(game.intime))
2887 q.status = "distressed"
2888 # tell the captain about it if we can
2891 _("Uhura- Captain, %s in Quadrant %s reports it is under attack")
2892 % (q.planet, repr(w))
2894 prout(_("by a Klingon invasion fleet."))
2897 elif evcode == FENSLV: # starsystem is enslaved
2898 ev = unschedule(FENSLV)
2899 # see if current distress call still active
2900 q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
2904 q.status = "enslaved"
2906 # play stork and schedule the first baby
2907 ev2 = schedule(FREPRO, expran(2.0 * game.intime))
2908 ev2.quadrant = ev.quadrant
2910 # report the disaster if we can
2912 prout(_("Uhura- We've lost contact with starsystem %s") % q.planet)
2913 prout(_("in Quadrant %s.\n") % ev.quadrant)
2914 elif evcode == FREPRO: # Klingon reproduces
2915 # If we ever switch to a real event queue, we'll need to
2916 # explicitly retrieve and restore the x and y.
2917 ev = schedule(FREPRO, expran(1.0 * game.intime))
2918 # see if current distress call still active
2919 q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
2923 if game.remkl() >= MAXKLGAME:
2924 continue # full right now
2925 # reproduce one Klingon
2928 if game.klhere >= MAXKLQUAD:
2930 # this quadrant not ok, pick an adjacent one
2931 for m.i in range(w.i - 1, w.i + 2):
2932 for m.j in range(w.j - 1, w.j + 2):
2933 if not m.valid_quadrant():
2935 q = game.state.galaxy[m.i][m.j]
2936 # check for this quad ok (not full & no snova)
2937 if q.klingons >= MAXKLQUAD or q.supernova:
2940 # search for eligible quadrant failed
2946 if game.quadrant == w:
2948 newkling() # also adds it to game.enemies
2949 # recompute time left
2952 if game.quadrant == w:
2953 prout(_("Spock- sensors indicate the Klingons have"))
2954 prout(_("launched a warship from %s.") % q.planet)
2956 prout(_("Uhura- Starfleet reports increased Klingon activity"))
2957 if q.planet is not None:
2958 proutn(_("near %s ") % q.planet)
2959 prout(_("in Quadrant %s.") % w)
2966 key = scanner.nexttok()
2969 proutn(_("How long? "))
2974 origTime = delay = scanner.real
2977 if delay >= game.state.remtime or len(game.enemies) != 0:
2978 proutn(_("Are you sure? "))
2981 # Alternate resting periods (events) with attacks
2985 game.resting = False
2986 if not game.resting:
2987 prout(_("%d stardates left.") % int(game.state.remtime))
2989 temp = game.optime = delay
2990 if len(game.enemies):
2991 rtime = rnd.real(1.0, 2.0)
2995 if game.optime < delay:
2996 attack(torps_ok=False)
3004 # Repair Deathray if long rest at starbase
3005 if origTime - delay >= 9.99 and game.condition == "docked":
3006 game.damage[DDRAY] = 0.0
3007 # leave if quadrant supernovas
3008 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
3010 game.resting = False
3016 ncourse = (0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5)
3020 if rnd.withprob(0.05):
3021 # Wow! We've supernova'ed
3022 supernova(game.quadrant)
3024 # handle initial nova
3025 game.quad[nov.i][nov.j] = "."
3026 prout(crmena(False, "*", "sector", nov) + _(" novas."))
3027 game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
3028 game.state.starkl += 1
3029 # Set up queue to recursively trigger adjacent stars
3035 for offset.i in range(-1, 1 + 1):
3036 for offset.j in range(-1, 1 + 1):
3037 if offset.j == 0 and offset.i == 0:
3039 neighbor = start + offset
3040 if not neighbor.valid_sector():
3042 iquad = game.quad[neighbor.i][neighbor.j]
3043 # Empty space ends reaction
3044 if iquad in (".", "?", " ", "T", "#"):
3046 elif iquad == "*": # Affect another star
3047 if rnd.withprob(0.05):
3048 # This star supernovas
3049 supernova(game.quadrant)
3052 hits.append(neighbor)
3053 game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
3054 game.state.starkl += 1
3055 proutn(crmena(True, "*", "sector", neighbor))
3057 game.quad[neighbor.i][neighbor.j] = "."
3059 elif iquad in ("P", "@"): # Destroy planet
3060 game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
3062 game.state.nplankl += 1
3064 game.state.nworldkl += 1
3065 prout(crmena(True, "B", "sector", neighbor) + _(" destroyed."))
3066 game.iplnet.pclass = "destroyed"
3068 game.plnet.invalidate()
3072 game.quad[neighbor.i][neighbor.j] = "."
3073 elif iquad == "B": # Destroy base
3074 game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
3075 game.state.baseq = [
3076 x for x in game.state.baseq if x != game.quadrant
3078 game.base.invalidate()
3079 game.state.basekl += 1
3081 prout(crmena(True, "B", "sector", neighbor) + _(" destroyed."))
3082 game.quad[neighbor.i][neighbor.j] = "."
3083 elif iquad in ("E", "F"): # Buffet ship
3084 prout(_("***Starship buffeted by nova."))
3086 if game.shield >= 2000.0:
3087 game.shield -= 2000.0
3089 diff = 2000.0 - game.shield
3093 prout(_("***Shields knocked out."))
3094 game.damage[DSHIELD] += (
3095 0.005 * game.damfac * rnd.real() * diff
3098 game.energy -= 2000.0
3099 if game.energy <= 0:
3102 # add in course nova contributes to kicking starship
3104 bump += (game.sector - hits[-1]).sgn()
3105 elif iquad == "K": # kill klingon
3106 deadkl(neighbor, iquad, neighbor)
3107 elif iquad in ("C", "S", "R"): # Damage/destroy big enemies
3109 for ll in range(len(game.enemies)):
3110 if game.enemies[ll].location == neighbor:
3111 target = game.enemies[ll]
3113 if target is not None:
3114 target.power -= 800.0 # If firepower is lost, die
3115 if target.power <= 0.0:
3116 deadkl(neighbor, iquad, neighbor)
3117 continue # neighbor loop
3118 # Else enemy gets flung by the blast wave
3119 newc = neighbor + neighbor - start
3120 proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged"))
3121 if not newc.valid_sector():
3122 # can't leave quadrant
3125 iquad1 = game.quad[newc.i][newc.j]
3128 _(", blasted into ")
3129 + crmena(False, " ", "sector", newc)
3132 deadkl(neighbor, iquad, newc)
3135 # can't move into something else
3138 proutn(_(", buffeted to Sector %s") % newc)
3139 game.quad[neighbor.i][neighbor.j] = "."
3140 game.quad[newc.i][newc.j] = iquad
3142 # Starship affected by nova -- kick it away.
3144 direc = ncourse[3 * (bump.i + 1) + bump.j + 2]
3149 scourse = course(bearing=direc, distance=dist)
3150 game.optime = scourse.time(w=4)
3152 prout(_("Force of nova displaces starship."))
3153 imove(scourse, noattack=True)
3154 game.optime = scourse.time(w=4)
3159 "Star goes supernova."
3165 # Scheduled supernova -- select star at random.
3168 for nq.i in range(GALSIZE):
3169 for nq.j in range(GALSIZE):
3170 nstars += game.state.galaxy[nq.i][nq.j].stars
3172 return # nothing to supernova exists
3173 num = rnd.integer(nstars) + 1
3174 for nq.i in range(GALSIZE):
3175 for nq.j in range(GALSIZE):
3176 num -= game.state.galaxy[nq.i][nq.j].stars
3181 if game.idebug: # pragma: no cover
3182 proutn("=== Super nova here?")
3185 if nq != game.quadrant or game.justin:
3186 # it isn't here, or we just entered (treat as enroute)
3190 _("Message from Starfleet Command Stardate %.2f")
3193 prout(_(" Supernova in Quadrant %s; caution advised.") % nq)
3196 # we are in the quadrant!
3197 num = rnd.integer(game.state.galaxy[nq.i][nq.j].stars) + 1
3198 for ns.i in range(QUADSIZE):
3199 for ns.j in range(QUADSIZE):
3200 if game.quad[ns.i][ns.j] == "*":
3207 prouts(_("***RED ALERT! RED ALERT!"))
3209 prout(_("***Incipient supernova detected at Sector %s") % ns)
3210 if (ns.i - game.sector.i) ** 2 + (ns.j - game.sector.j) ** 2 <= 2.1:
3211 proutn(_("Emergency override attempts t"))
3212 prouts("***************")
3216 # destroy any Klingons in supernovaed quadrant
3217 game.state.galaxy[nq.i][nq.j].klingons = 0
3218 if nq == game.state.kscmdr:
3219 # did in the Supercommander!
3220 game.state.nscrem = game.state.kscmdr.i = game.state.kscmdr.j = game.isatb = 0
3224 # Changing this to [w for w in game.state.kcmdr if w != nq]
3225 # causes regression-test failure
3226 survivors = list(filter(lambda w: w != nq, game.state.kcmdr))
3227 # comkills = len(game.state.kcmdr) - len(survivors)
3228 game.state.kcmdr = survivors
3229 if not game.state.kcmdr:
3231 # destroy Romulans and planets in supernovaed quadrant
3232 nrmdead = game.state.galaxy[nq.i][nq.j].romulans
3233 game.state.galaxy[nq.i][nq.j].romulans = 0
3234 game.state.nromrem -= nrmdead
3236 for loop in range(game.inplan):
3237 if game.state.planets[loop].quadrant == nq:
3238 game.state.planets[loop].pclass = "destroyed"
3240 # Destroy any base in supernovaed quadrant
3241 game.state.baseq = [x for x in game.state.baseq if x != nq]
3242 # If starship caused supernova, tally up destruction
3244 game.state.starkl += game.state.galaxy[nq.i][nq.j].stars
3245 game.state.basekl += game.state.galaxy[nq.i][nq.j].starbase
3246 game.state.nplankl += npdead
3247 # mark supernova in galaxy and in star chart
3248 if game.quadrant == nq or communicating():
3249 game.state.galaxy[nq.i][nq.j].supernova = True
3250 # If supernova destroys last Klingons give special message
3251 if game.unwon() == 0 and nq != game.quadrant:
3254 prout(_("Lucky you!"))
3255 proutn(_("A supernova in %s has just destroyed the last Klingons.") % nq)
3258 # if some Klingons remain, continue or die in supernova
3264 # Code from finish.c ends here.
3268 "Self-destruct maneuver. Finish with a BANG!"
3270 if damaged(DCOMPTR):
3271 prout(_("Computer damaged; cannot execute destruct sequence."))
3273 prouts(_("---WORKING---"))
3275 prouts(_("SELF-DESTRUCT-SEQUENCE-ACTIVATED"))
3288 prout(_("ENTER-CORRECT-PASSWORD-TO-CONTINUE-"))
3290 prout(_("SELF-DESTRUCT-SEQUENCE-OTHERWISE-"))
3292 prout(_("SELF-DESTRUCT-SEQUENCE-WILL-BE-ABORTED"))
3295 if game.passwd != scanner.token:
3296 prouts(_("PASSWORD-REJECTED;"))
3298 prouts(_("CONTINUITY-EFFECTED"))
3301 prouts(_("PASSWORD-ACCEPTED"))
3313 if rnd.withprob(0.15):
3314 prouts(_("GOODBYE-CRUEL-WORLD"))
3321 if game.ship == "E":
3323 prouts(_("********* Entropy of %s maximized *********") % crmshp())
3327 if len(game.enemies) != 0:
3328 whammo = 25.0 * game.energy
3329 for e in game.enemies[::-1]:
3330 if e.power * e.kdist <= whammo:
3331 deadkl(e.location, game.quad[e.location.i][e.location.j], e.location)
3336 "Compute our rate of kils over time."
3337 elapsed = game.state.date - game.indate
3338 if elapsed == 0: # Avoid divide-by-zero error if calculated on turn 0
3341 starting = game.inkling + game.incom + game.inscom
3342 remaining = game.unwon()
3343 return (starting - remaining) / elapsed
3349 5.0 * game.state.starkl
3351 + 10.0 * game.state.nplankl
3352 + 300 * game.state.nworldkl
3354 + 100.0 * game.state.basekl
3355 + 3.0 * game.abandoned
3358 if game.ship == "F":
3360 elif game.ship is None:
3366 # end the game, with appropriate notifications
3370 prout(_("It is stardate %.1f.") % game.state.date)
3372 if ifin == FWON: # Game has been won
3373 if game.state.nromrem != 0:
3375 _("The remaining %d Romulans surrender to Starfleet Command.")
3376 % game.state.nromrem
3379 prout(_("You have smashed the Klingon invasion fleet and saved"))
3380 prout(_("the Federation."))
3381 if game.alive and game.brigcapacity - game.brigfree > 0:
3382 game.kcaptured += game.brigcapacity - game.brigfree
3384 _("The %d captured Klingons are transferred to Star Fleet Command.")
3385 % (game.brigcapacity - game.brigfree)
3391 badpt = 0.0 # Close enough!
3392 # killsPerDate >= RateMax
3394 game.state.date - game.indate < 5.0
3396 >= 0.1 * game.skill * (game.skill + 1.0) + 0.1 + 0.008 * badpt
3399 prout(_("In fact, you have done so well that Starfleet Command"))
3400 if game.skill == SKILL_NOVICE:
3401 prout(_('promotes you one step in rank from "Novice" to "Fair".'))
3402 elif game.skill == SKILL_FAIR:
3403 prout(_('promotes you one step in rank from "Fair" to "Good".'))
3404 elif game.skill == SKILL_GOOD:
3405 prout(_('promotes you one step in rank from "Good" to "Expert".'))
3406 elif game.skill == SKILL_EXPERT:
3407 prout(_("promotes you to Commodore Emeritus."))
3409 prout(_("Now that you think you're really good, try playing"))
3410 prout(_('the "Emeritus" game. It will splatter your ego.'))
3411 elif game.skill == SKILL_EMERITUS:
3413 proutn(_("Computer- "))
3414 prouts(_("ERROR-ERROR-ERROR-ERROR"))
3416 prouts(_(" YOUR-SKILL-HAS-EXCEEDED-THE-CAPACITY-OF-THIS-PROGRAM"))
3418 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
3420 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
3422 prouts(_(" THIS-PROGRAM-MUST-SURVIVE"))
3424 prouts(_(" THIS-PROGRAM-MUST?- MUST ? - SUR? ? -? VI"))
3426 prout(_("Now you can retire and write your own Star Trek game!"))
3428 elif game.skill >= SKILL_EXPERT:
3429 if game.thawed and not game.idebug:
3430 prout(_("You cannot get a citation, so..."))
3433 _("Do you want your Commodore Emeritus Citation printed? ")
3438 # Only grant long life if alive (original didn't!)
3440 prout(_("LIVE LONG AND PROSPER."))
3445 elif ifin == FDEPLETE: # Federation Resources Depleted
3446 prout(_("Your time has run out and the Federation has been"))
3447 prout(_("conquered. Your starship is now Klingon property,"))
3448 prout(_("and you are put on trial as a war criminal. On the"))
3449 proutn(_("basis of your record, you are "))
3450 if game.unwon() * 3.0 > (game.inkling + game.incom + game.inscom):
3451 prout(_("acquitted."))
3453 prout(_("LIVE LONG AND PROSPER."))
3455 prout(_("found guilty and"))
3456 prout(_("sentenced to death by slow torture."))
3460 elif ifin == FLIFESUP:
3461 prout(_("Your life support reserves have run out, and"))
3462 prout(_("you die of thirst, starvation, and asphyxiation."))
3463 prout(_("Your starship is a derelict in space."))
3465 prout(_("Your energy supply is exhausted."))
3467 prout(_("Your starship is a derelict in space."))
3468 elif ifin == FBATTLE:
3469 prout(_("The %s has been destroyed in battle.") % crmshp())
3471 prout(_("Dulce et decorum est pro patria mori."))
3473 prout(_("You have made three attempts to cross the negative energy"))
3474 prout(_("barrier which surrounds the galaxy."))
3476 prout(_("Your navigation is abominable."))
3479 prout(_("Your starship has been destroyed by a nova."))
3480 prout(_("That was a great shot."))
3482 elif ifin == FSNOVAED:
3483 prout(_("The %s has been fried by a supernova.") % crmshp())
3484 prout(_("...Not even cinders remain..."))
3485 elif ifin == FABANDN:
3486 prout(_("You have been captured by the Klingons. If you still"))
3487 prout(_("had a starbase to be returned to, you would have been"))
3488 prout(_("repatriated and given another chance. Since you have"))
3489 prout(_("no starbases, you will be mercilessly tortured to death."))
3490 elif ifin == FDILITHIUM:
3491 prout(_("Your starship is now an expanding cloud of subatomic particles"))
3492 elif ifin == FMATERIALIZE:
3493 prout(_("Starbase was unable to re-materialize your starship."))
3494 prout(_("Sic transit gloria mundi."))
3495 elif ifin == FPHASER:
3496 prout(_("The %s has been cremated by its own phasers.") % crmshp())
3498 prout(_("You and your landing party have been"))
3499 prout(_("converted to energy, dissipating through space."))
3500 elif ifin == FMINING:
3501 # This does not seem to be reachable from any code path.
3502 prout(_("You are left with your landing party on"))
3503 prout(_("a wild jungle planet inhabited by primitive cannibals."))
3505 prout(_('They are very fond of "Captain Kirk" soup.'))
3507 prout(_("Without your leadership, the %s is destroyed.") % crmshp())
3508 elif ifin == FDPLANET:
3509 prout(_("You and your mining party perish."))
3511 prout(_("That was a great shot."))
3514 # This does not seem to be reachable from any code path.
3515 prout(_("The Galileo is instantly annihilated by the supernova."))
3516 prout(_("You and your mining party are atomized."))
3518 prout(_("Mr. Spock takes command of the %s and") % crmshp())
3519 prout(_("joins the Romulans, wreaking terror on the Federation."))
3520 elif ifin == FPNOVA:
3521 prout(_("You and your mining party are atomized."))
3523 prout(_("Mr. Spock takes command of the %s and") % crmshp())
3524 prout(_("joins the Romulans, wreaking terror on the Federation."))
3525 elif ifin == FSTRACTOR:
3526 prout(_("The shuttle craft Galileo is also caught,"))
3527 prout(_("and breaks up under the strain."))
3529 prout(_("Your debris is scattered for millions of miles."))
3530 prout(_("Without your leadership, the %s is destroyed.") % crmshp())
3532 prout(_("The mutants attack and kill Spock."))
3533 prout(_("Your ship is captured by Klingons, and"))
3534 prout(_("your crew is put on display in a Klingon zoo."))
3535 elif ifin == FTRIBBLE:
3536 prout(_("Tribbles consume all remaining water,"))
3537 prout(_("food, and oxygen on your ship."))
3539 prout(_("You die of thirst, starvation, and asphyxiation."))
3540 prout(_("Your starship is a derelict in space."))
3542 prout(_("Your ship is drawn to the center of the black hole."))
3543 prout(_("You are crushed into extremely dense matter."))
3544 elif ifin == FCLOAK:
3546 prout(_("You have violated the Treaty of Algeron."))
3547 prout(_("The Romulan Empire can never trust you again."))
3549 prout(_("Your last crew member has died."))
3550 if ifin != FWON and ifin != FCLOAK and game.iscloaked:
3552 _("Your ship was cloaked so your subspace radio did not receive anything.")
3554 prout(_("You may have missed some warning messages."))
3556 if game.ship == "F":
3558 elif game.ship == "E":
3561 if game.unwon() != 0:
3562 goodies = game.state.remres / game.inresor
3563 baddies = (game.remkl() + 2.0 * len(game.state.kcmdr)) / (
3564 game.inkling + 2.0 * game.incom
3566 if goodies / baddies >= rnd.real(1.0, 1.5):
3567 prout(_("As a result of your actions, a treaty with the Klingon"))
3568 prout(_("Empire has been signed. The terms of the treaty are"))
3569 if goodies / baddies >= rnd.real(3.0):
3570 prout(_("favorable to the Federation."))
3572 prout(_("Congratulations!"))
3574 prout(_("highly unfavorable to the Federation."))
3576 prout(_("The Federation will be destroyed."))
3578 prout(_("Since you took the last Klingon with you, you are a"))
3579 prout(_("martyr and a hero. Someday maybe they'll erect a"))
3580 prout(_("statue in your memory. Rest in peace, and try not"))
3581 prout(_("to think about pigeons."))
3584 scanner.chew() # Clean up leftovers
3588 "Compute player's score."
3589 timused = game.state.date - game.indate
3590 if (timused == 0 or game.unwon() != 0) and timused < 5.0:
3592 game.perdate = killrate()
3593 ithperd = 500 * game.perdate + 0.5
3596 iwon = 100 * game.skill
3597 if game.ship == "E":
3599 elif game.ship == "F":
3604 game.inkling - game.remkl() + len(game.state.kcmdr) + game.state.nscrem
3607 10 * (dead_ordinaries)
3608 + 50 * (game.incom - len(game.state.kcmdr))
3611 + 20 * (game.inrom - game.state.nromrem)
3612 + 200 * (game.inscom - game.state.nscrem)
3613 - game.state.nromrem
3614 + 3 * game.kcaptured
3620 prout(_("Your score --"))
3621 if game.inrom - game.state.nromrem:
3623 _("%6d Romulans destroyed %5d")
3624 % (game.inrom - game.state.nromrem, 20 * (game.inrom - game.state.nromrem))
3626 if game.state.nromrem and game.gamewon:
3628 _("%6d Romulans captured %5d")
3629 % (game.state.nromrem, game.state.nromrem)
3633 _("%6d ordinary Klingons destroyed %5d")
3634 % (dead_ordinaries, 10 * dead_ordinaries)
3636 if game.incom - len(game.state.kcmdr):
3638 _("%6d Klingon commanders destroyed %5d")
3640 game.incom - len(game.state.kcmdr),
3641 50 * (game.incom - len(game.state.kcmdr)),
3646 _("%d Klingons captured %5d")
3647 % (game.kcaptured, 3 * game.kcaptured)
3649 if game.inscom - game.state.nscrem:
3651 _("%6d Super-Commander destroyed %5d")
3652 % (game.inscom - game.state.nscrem, 200 * (game.inscom - game.state.nscrem))
3656 _("%6.2f Klingons per stardate %5d") % (game.perdate, ithperd)
3658 if game.state.starkl:
3660 _("%6d stars destroyed by your action %5d")
3661 % (game.state.starkl, -5 * game.state.starkl)
3663 if game.state.nplankl:
3665 _("%6d planets destroyed by your action %5d")
3666 % (game.state.nplankl, -10 * game.state.nplankl)
3668 if (game.options & OPTION_WORLDS) and game.state.nworldkl:
3670 _("%6d inhabited planets destroyed by your action %5d")
3671 % (game.state.nworldkl, -300 * game.state.nworldkl)
3673 if game.state.basekl:
3675 _("%6d bases destroyed by your action %5d")
3676 % (game.state.basekl, -100 * game.state.basekl)
3680 _("%6d calls for help from starbase %5d")
3681 % (game.nhelp, -45 * game.nhelp)
3685 _("%6d casualties incurred %5d")
3686 % (game.casual, -game.casual)
3690 _("%6d crew abandoned in space %5d")
3691 % (game.abandoned, -3 * game.abandoned)
3694 prout(_("%6d ship(s) lost or destroyed %5d") % (klship, -100 * klship))
3696 if game.ncviol == 1:
3697 prout(_("1 Treaty of Algeron violation -100"))
3700 _("%6d Treaty of Algeron violations %5d\n")
3701 % (game.ncviol, -100 * game.ncviol)
3704 prout(_("Penalty for getting yourself killed -200"))
3706 proutn(_("Bonus for winning "))
3707 if game.skill == SKILL_NOVICE:
3708 proutn(_("Novice game "))
3709 elif game.skill == SKILL_FAIR:
3710 proutn(_("Fair game "))
3711 elif game.skill == SKILL_GOOD:
3712 proutn(_("Good game "))
3713 elif game.skill == SKILL_EXPERT:
3714 proutn(_("Expert game "))
3715 elif game.skill == SKILL_EMERITUS:
3716 proutn(_("Emeritus game"))
3717 prout(" %5d" % iwon)
3719 prout(_("TOTAL SCORE %5d") % game.score)
3723 "Emit winner's commemmorative plaque."
3726 proutn(_("File or device name for your plaque: "))
3729 fp = open(winner, "w")
3732 prout(_("Invalid name."))
3734 proutn(_("Enter name to go on plaque (up to 30 characters): "))
3736 # The 38 below must be 64 for 132-column paper
3737 nskip = 38 - len(winner) / 2
3738 # This is where the ASCII art picture was emitted.
3739 # It got garbled somewhere in the chain of transmission to the Almy version.
3740 # We should restore it if we can find old enough FORTRAN sources.
3744 " U. S. S. ENTERPRISE\n"
3747 fp.write("\n\n\n\n")
3750 " For demonstrating outstanding ability as a starship captain\n"
3756 " Starfleet Command bestows to you\n"
3760 fp.write("%*s%s\n\n" % (nskip, "", winner))
3762 _(" the rank of\n\n")
3766 ' "Commodore Emeritus"\n\n'
3770 if game.skill == SKILL_EXPERT:
3771 fp.write(_(" Expert level\n\n"))
3772 elif game.skill == SKILL_EMERITUS:
3773 fp.write(_("Emeritus level\n\n"))
3775 fp.write(_(" Cheat level\n\n"))
3776 timestring = time.ctime()
3779 " This day of %.6s %.4s, %.8s\n\n"
3781 % (timestring + 4, timestring + 20, timestring + 11)
3784 _(" Your score: %d\n\n")
3789 " Klingons per stardate: %.2f\n"
3796 # Code from io.c begins here
3798 rows = linecount = 0 # for paging
3801 fullscreen_window = None
3802 srscan_window = None # Short range scan
3803 report_window = None # Report legends for status window
3804 status_window = None # The status window itself
3805 lrscan_window = None # Long range scan
3806 message_window = None # Main window for scrolling text
3807 prompt_window = None # Prompt window at bottom of display
3813 gettext.bindtextdomain("sst", "/usr/local/share/locale")
3814 gettext.textdomain("sst")
3815 if not (game.options & OPTION_CURSES):
3816 ln_env = os.getenv("LINES")
3821 else: # pragma: no cover
3822 stdscr = curses.initscr()
3826 if game.options & OPTION_COLOR:
3827 curses.start_color()
3828 curses.use_default_colors()
3829 curses.init_pair(curses.COLOR_BLACK, curses.COLOR_BLACK, -1)
3830 curses.init_pair(curses.COLOR_GREEN, curses.COLOR_GREEN, -1)
3831 curses.init_pair(curses.COLOR_RED, curses.COLOR_RED, -1)
3832 curses.init_pair(curses.COLOR_CYAN, curses.COLOR_CYAN, -1)
3833 curses.init_pair(curses.COLOR_WHITE, curses.COLOR_WHITE, -1)
3834 curses.init_pair(curses.COLOR_MAGENTA, curses.COLOR_MAGENTA, -1)
3835 curses.init_pair(curses.COLOR_BLUE, curses.COLOR_BLUE, -1)
3836 curses.init_pair(curses.COLOR_YELLOW, curses.COLOR_YELLOW, -1)
3837 global fullscreen_window, srscan_window, report_window, status_window
3838 global lrscan_window, message_window, prompt_window
3839 (rows, _columns) = stdscr.getmaxyx()
3840 fullscreen_window = stdscr
3841 srscan_window = curses.newwin(12, 25, 0, 0)
3842 report_window = curses.newwin(11, 0, 1, 25)
3843 status_window = curses.newwin(10, 0, 1, 39)
3844 lrscan_window = curses.newwin(5, 0, 0, 64)
3845 message_window = curses.newwin(0, 0, 12, 0)
3846 prompt_window = curses.newwin(1, 0, rows - 2, 0)
3847 message_window.scrollok(True)
3848 setwnd(fullscreen_window)
3853 if game.options & OPTION_CURSES: # pragma: no cover
3854 stdscr.keypad(False)
3861 "Wait for user action -- OK to do nothing if on a TTY"
3862 if game.options & OPTION_CURSES: # pragma: no cover
3868 prouts(_("[ANNOUNCEMENT ARRIVING...]"))
3873 if game.skill > SKILL_FAIR:
3874 prompt = _("[CONTINUE?]")
3876 prompt = _("[PRESS ENTER TO CONTINUE]")
3878 if game.options & OPTION_CURSES: # pragma: no cover
3880 setwnd(prompt_window)
3881 prompt_window.clear()
3882 prompt_window.addstr(prompt)
3883 prompt_window.getstr()
3884 prompt_window.clear()
3885 prompt_window.refresh()
3886 setwnd(message_window)
3889 sys.stdout.write("\n")
3893 sys.stdout.write("\n" * rows)
3898 "Skip i lines. Pause game if this would cause a scrolling event."
3899 for _dummy in range(i):
3900 if game.options & OPTION_CURSES: # pragma: no cover
3901 (y, _x) = curwnd.getyx()
3903 curwnd.move(y + 1, 0)
3904 except curses.error:
3909 if rows and linecount >= rows:
3912 sys.stdout.write("\n")
3915 def proutn(proutntline):
3916 "Utter a line with no following line feed."
3917 if game.options & OPTION_CURSES: # pragma: no cover
3918 (y, x) = curwnd.getyx()
3919 (my, _mx) = curwnd.getmaxyx()
3920 if curwnd == message_window and y >= my - 2:
3923 if logfp and game.cdebug:
3924 logfp.write("#curses: at %s proutn(%s)\n" % ((y, x), repr(proutntline)))
3925 curwnd.addstr(proutntline)
3928 sys.stdout.write(proutntline)
3932 def prout(proutline):
3937 def prouts(proutsline):
3939 for c in proutsline:
3940 if not replayfp or replayfp.closed: # Don't slow down replays
3943 if game.options & OPTION_CURSES: # pragma: no cover
3947 if not replayfp or replayfp.closed:
3952 "Get a line of input."
3953 if game.options & OPTION_CURSES: # pragma: no cover
3954 linein = codecs.decode(curwnd.getstr()) + "\n"
3957 if replayfp and not replayfp.closed:
3959 linein = replayfp.readline()
3961 if linein == "": # pragma: no cover
3962 prout("*** Replay finished")
3965 elif linein[0] != "#":
3969 linein = input() + "\n"
3979 "Change windows -- OK for this to be a no-op in tty mode."
3981 if game.options & OPTION_CURSES: # pragma: no cover
3982 if game.cdebug and logfp:
3983 if wnd == fullscreen_window:
3984 legend = "fullscreen"
3985 elif wnd == srscan_window:
3987 elif wnd == report_window:
3989 elif wnd == status_window:
3991 elif wnd == lrscan_window:
3993 elif wnd == message_window:
3995 elif wnd == prompt_window:
3999 logfp.write("#curses: setwnd(%s)\n" % legend)
4001 # Some curses implementations get confused when you try this.
4003 curses.curs_set(wnd in (fullscreen_window, message_window, prompt_window))
4004 except curses.error:
4009 "Clear to end of line -- can be a no-op in tty mode"
4010 if game.options & OPTION_CURSES: # pragma: no cover
4016 "Clear screen -- can be a no-op in tty mode."
4018 if game.options & OPTION_CURSES: # pragma: no cover
4025 def textcolor(color=DEFAULT):
4026 if (game.options & OPTION_COLOR) and (
4027 game.options & OPTION_CURSES
4028 ): # pragma: no cover
4029 if color == DEFAULT:
4031 elif color == BLACK:
4032 curwnd.attron(curses.color_pair(curses.COLOR_BLACK))
4034 curwnd.attron(curses.color_pair(curses.COLOR_BLUE))
4035 elif color == GREEN:
4036 curwnd.attron(curses.color_pair(curses.COLOR_GREEN))
4038 curwnd.attron(curses.color_pair(curses.COLOR_CYAN))
4040 curwnd.attron(curses.color_pair(curses.COLOR_RED))
4041 elif color == MAGENTA:
4042 curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA))
4043 elif color == BROWN:
4044 curwnd.attron(curses.color_pair(curses.COLOR_YELLOW))
4045 elif color == LIGHTGRAY:
4046 curwnd.attron(curses.color_pair(curses.COLOR_WHITE))
4047 elif color == DARKGRAY:
4048 curwnd.attron(curses.color_pair(curses.COLOR_BLACK) | curses.A_BOLD)
4049 elif color == LIGHTBLUE:
4050 curwnd.attron(curses.color_pair(curses.COLOR_BLUE) | curses.A_BOLD)
4051 elif color == LIGHTGREEN:
4052 curwnd.attron(curses.color_pair(curses.COLOR_GREEN) | curses.A_BOLD)
4053 elif color == LIGHTCYAN:
4054 curwnd.attron(curses.color_pair(curses.COLOR_CYAN) | curses.A_BOLD)
4055 elif color == LIGHTRED:
4056 curwnd.attron(curses.color_pair(curses.COLOR_RED) | curses.A_BOLD)
4057 elif color == LIGHTMAGENTA:
4058 curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA) | curses.A_BOLD)
4059 elif color == YELLOW:
4060 curwnd.attron(curses.color_pair(curses.COLOR_YELLOW) | curses.A_BOLD)
4061 elif color == WHITE:
4062 curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD)
4066 if (game.options & OPTION_COLOR) and (
4067 game.options & OPTION_CURSES
4068 ): # pragma: no cover
4069 curwnd.attron(curses.A_REVERSE)
4073 # Things past this point have policy implications.
4078 "Hook to be called after moving to redraw maps."
4079 if game.options & OPTION_CURSES: # pragma: no cover
4082 setwnd(srscan_window)
4086 setwnd(status_window)
4087 status_window.clear()
4088 status_window.move(0, 0)
4089 setwnd(report_window)
4090 report_window.clear()
4091 report_window.move(0, 0)
4093 setwnd(lrscan_window)
4094 lrscan_window.clear()
4095 lrscan_window.move(0, 0)
4096 lrscan(silent=False)
4099 def put_srscan_sym(w, sym): # pragma: no cover
4100 "Emit symbol for short-range scan."
4101 srscan_window.move(w.i + 1, w.j * 2 + 2)
4102 srscan_window.addch(sym)
4103 srscan_window.refresh()
4107 "Enemy fall down, go boom."
4108 if game.options & OPTION_CURSES: # pragma: no cover
4110 setwnd(srscan_window)
4111 srscan_window.attron(curses.A_REVERSE)
4112 put_srscan_sym(w, game.quad[w.i][w.j])
4116 srscan_window.attroff(curses.A_REVERSE)
4117 put_srscan_sym(w, game.quad[w.i][w.j])
4118 curses.delay_output(500)
4119 setwnd(message_window)
4123 "Sound and visual effects for teleportation."
4124 if game.options & OPTION_CURSES: # pragma: no cover
4126 setwnd(message_window)
4128 prouts(" . . . . . ")
4129 if game.options & OPTION_CURSES: # pragma: no cover
4130 # curses.delay_output(1000)
4135 def tracktorpedo(w, step, i, n, iquad):
4136 "Torpedo-track animation."
4137 if not game.options & OPTION_CURSES:
4141 proutn(_("Track for torpedo number %d- ") % (i + 1))
4144 proutn(_("Torpedo track- "))
4145 elif step in {4, 9}:
4148 else: # pragma: no cover
4149 if not damaged(DSRSENS) or game.condition == "docked":
4150 if i != 0 and step == 1:
4153 if iquad in {".", " "}:
4154 put_srscan_sym(w, "+")
4158 put_srscan_sym(w, iquad)
4160 curwnd.attron(curses.A_REVERSE)
4161 put_srscan_sym(w, iquad)
4165 curwnd.attroff(curses.A_REVERSE)
4166 put_srscan_sym(w, iquad)
4172 "Display the current galaxy chart."
4173 if game.options & OPTION_CURSES: # pragma: no cover
4174 setwnd(message_window)
4175 message_window.clear()
4177 if game.options & OPTION_TTY:
4184 def prstat(txt, data):
4186 if game.options & OPTION_CURSES: # pragma: no cover
4188 setwnd(status_window)
4190 proutn(" " * (NSYM - len(txt)))
4193 if game.options & OPTION_CURSES: # pragma: no cover
4194 setwnd(report_window)
4197 # Code from moving.c begins here
4200 def imove(icourse=None, noattack=False):
4201 "Movement execution for warp, impulse, supernova, and tractor-beam events."
4204 def newquadrant(noattack):
4205 # Leaving quadrant -- allow final enemy attack
4206 # Don't set up attack if being pushed by nova or cloaked
4207 if len(game.enemies) != 0 and not noattack and not game.iscloaked:
4209 for enemy in game.enemies:
4210 finald = (w - enemy.location).distance()
4211 enemy.kavgd = 0.5 * (finald + enemy.kdist)
4212 # Stas Sergeev added the condition
4213 # that attacks only happen if Klingons
4214 # are present and your skill is good.
4216 game.skill > SKILL_GOOD
4218 and not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova
4220 attack(torps_ok=False)
4223 # check for edge of galaxy
4227 if icourse.final.i < 0:
4228 icourse.final.i = -icourse.final.i
4230 if icourse.final.j < 0:
4231 icourse.final.j = -icourse.final.j
4233 if icourse.final.i >= GALSIZE * QUADSIZE:
4234 icourse.final.i = (GALSIZE * QUADSIZE * 2) - icourse.final.i
4236 if icourse.final.j >= GALSIZE * QUADSIZE:
4237 icourse.final.j = (GALSIZE * QUADSIZE * 2) - icourse.final.j
4245 if game.nkinks == 3:
4246 # Three strikes -- you're out!
4250 prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
4251 prout(_("AT THE EDGE OF THE GALAXY. THE THIRD TIME YOU TRY THIS,"))
4252 prout(_("YOU WILL BE DESTROYED."))
4253 # Compute final position in new quadrant
4254 if trbeam: # Don't bother if we are to be beamed
4256 game.quadrant = icourse.final.quadrant()
4257 game.sector = icourse.final.sector()
4259 prout(_("Entering Quadrant %s.") % game.quadrant)
4260 game.quad[game.sector.i][game.sector.j] = game.ship
4262 if game.skill > SKILL_NOVICE:
4263 attack(torps_ok=False)
4265 def check_collision(h):
4266 iquad = game.quad[h.i][h.j]
4268 # object encountered in flight path
4269 stopegy = 50.0 * icourse.distance / game.optime
4270 if iquad in ("T", "K", "C", "S", "R", "?"):
4271 for enemy in game.enemies:
4272 if enemy.location == h:
4273 collision(rammed=False, enemy=enemy)
4275 # This should not happen
4276 prout(_("Which way did he go?")) # pragma: no cover
4277 return False # pragma: no cover
4280 prouts(_("***RED ALERT! RED ALERT!"))
4282 proutn("***" + crmshp())
4283 proutn(_(" pulled into black hole at Sector %s") % h)
4284 # Getting pulled into a black hole was certain
4285 # death in Almy's original. Stas Sergeev added a
4286 # possibility that you'll get timewarped instead.
4288 for m in range(NDEVICES):
4289 if game.damage[m] > 0:
4292 1.4, (game.energy + game.shield) / 5000.0 - 1.0
4293 ) * math.pow(1.3, 1.0 / (n + 1) - 1.0)
4294 if (game.options & OPTION_BLKHOLE) and rnd.withprob(1 - probf):
4304 prout(_(" encounters Tholian web at %s;") % h)
4306 prout(_(" blocked by object at %s;") % h)
4307 proutn(_("Emergency stop required "))
4308 prout(_("%2d units of energy.") % int(stopegy))
4309 game.energy -= stopegy
4310 if game.energy <= 0:
4317 prout(_('Helmsman Sulu- "Leaving standard orbit."'))
4318 game.inorbit = False
4319 # If tractor beam is to occur, don't move full distance
4320 if game.state.date + game.optime >= scheduled(FTBEAM):
4322 # We can't be tractor beamed if cloaked,
4323 # so move the event into the future
4325 FTBEAM, game.optime + expran(1.5 * game.intime / len(game.state.kcmdr))
4329 game.condition = "red"
4330 icourse.distance = (
4331 icourse.distance * (scheduled(FTBEAM) - game.state.date) / game.optime
4334 game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
4336 game.quad[game.sector.i][game.sector.j] = "."
4337 for _m in range(icourse.moves):
4339 w = icourse.sector()
4340 if icourse.origin.quadrant() != icourse.location.quadrant():
4341 newquadrant(noattack)
4343 elif check_collision(w):
4344 prout(_("Collision detected"))
4348 # We're in destination quadrant -- compute new average enemy distances
4349 game.quad[game.sector.i][game.sector.j] = game.ship
4351 for enemy in game.enemies:
4352 finald = (w - enemy.location).distance()
4353 enemy.kavgd = 0.5 * (finald + enemy.kdist)
4354 enemy.kdist = finald
4356 if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
4357 attack(torps_ok=False)
4358 for enemy in game.enemies:
4359 enemy.kavgd = enemy.kdist
4362 setwnd(message_window)
4366 "Dock our ship at a starbase."
4368 if game.condition == "docked" and verbose:
4369 prout(_("Already docked."))
4372 prout(_("You must first leave standard orbit."))
4374 if game.base is None or not game.base.valid_sector():
4375 prout(_("No starbase available for docking in this quadrant."))
4377 if (abs(game.sector.i - game.base.i) > 1) or (abs(game.sector.j - game.base.j) > 1):
4378 prout(crmshp() + _(" not adjacent to base."))
4381 prout(_("You cannot dock while cloaked."))
4383 game.condition = "docked"
4387 if game.energy < game.inenrg:
4388 game.energy = game.inenrg
4389 game.shield = game.inshld
4390 game.torps = game.intorps
4391 game.lsupres = game.inlsr
4392 game.state.crew = FULLCREW
4393 if game.brigcapacity - game.brigfree > 0:
4395 _("%d captured Klingons transferred to base")
4396 % (game.brigcapacity - game.brigfree)
4398 game.kcaptured += game.brigcapacity - game.brigfree
4399 game.brigfree = game.brigcapacity
4400 if communicating() and (
4401 (is_scheduled(FCDBAS) or game.isatb == 1) and not game.iseenit
4403 # get attack report from base
4404 prout(_('Lt. Uhura- "Captain, an important message from the starbase:"'))
4409 def cartesian(loc1=None, loc2=None):
4411 return game.quadrant * QUADSIZE + game.sector
4413 return game.quadrant * QUADSIZE + loc1
4415 return loc1 * QUADSIZE + loc2
4418 def getcourse(isprobe):
4419 "Get a course and distance from the user."
4421 dquad = copy.copy(game.quadrant)
4422 navmode = "unspecified"
4426 if game.landed and not isprobe:
4427 prout(_("Dummy! You can't leave standard orbit until you"))
4428 proutn(_("are back aboard the ship."))
4431 while navmode == "unspecified":
4432 if damaged(DNAVSYS):
4434 prout(_("Computer damaged; manual navigation only"))
4436 prout(_("Computer damaged; manual movement only"))
4441 key = scanner.nexttok()
4443 proutn(_("Manual or automatic- "))
4446 elif key == "IHALPHA":
4447 if scanner.sees("manual"):
4449 key = scanner.nexttok()
4451 elif scanner.sees("automatic"):
4452 navmode = "automatic"
4453 key = scanner.nexttok()
4461 prout(_("(Manual navigation assumed.)"))
4463 prout(_("(Manual movement assumed.)"))
4467 if navmode == "automatic":
4468 while key == "IHEOL":
4470 proutn(_("Target quadrant or quadrant§or- "))
4472 proutn(_("Destination sector or quadrant§or- "))
4475 key = scanner.nexttok()
4476 scanner.push(scanner.token) # Something IHREAL or IHALPHA awaits us
4477 first = scanner.getcoord()
4481 if scanner.type == "IHEOL":
4484 scanner.push(scanner.token)
4485 second = scanner.getcoord()
4489 if second is not None:
4495 # only one pair of numbers was specified
4497 # only quadrant specified -- go to center of dest quad
4500 dsect.j = dsect.i = (QUADSIZE / 2) - 1 # preserves 1-origin behavior
4502 # only sector specified
4506 if not dquad.valid_quadrant() or not dsect.valid_sector():
4513 prout(_('Helmsman Sulu- "Course locked in for Sector %s."') % dsect)
4515 prout(_('Ensign Chekov- "Course laid in, Captain."'))
4516 # the actual deltas get computed here
4518 dquad.j - game.quadrant.j + (dsect.j - game.sector.j) / (QUADSIZE * 1.0)
4521 game.quadrant.i - dquad.i + (game.sector.i - dsect.i) / (QUADSIZE * 1.0)
4524 while key == "IHEOL":
4525 proutn(_("X and Y displacements- "))
4528 key = scanner.nexttok()
4531 delta.j = scanner.real
4535 key = scanner.nexttok()
4537 delta.i = scanner.real
4538 elif key == "IHEOL":
4544 # Check for zero movement
4545 if delta.i == 0 and delta.j == 0:
4548 if itemp == "verbose" and not isprobe:
4550 prout(_('Helmsman Sulu- "Aye, Sir."'))
4552 return course(bearing=delta.bearing(), distance=delta.distance())
4556 def __init__(self, bearing, distance, origin=None):
4557 self.distance = distance
4558 self.bearing = bearing
4560 self.origin = cartesian(game.quadrant, game.sector)
4562 self.origin = origin
4563 # The bearing() code we inherited from FORTRAN is actually computing
4564 # clockface directions!
4565 if self.bearing < 0.0:
4566 self.bearing += 12.0
4567 self.angle = (15.0 - self.bearing) * 0.5235988
4568 self.increment = Coord(-math.sin(self.angle), math.cos(self.angle))
4569 bigger = max(abs(self.increment.i), abs(self.increment.j))
4570 self.increment /= bigger
4571 self.moves = int(round(10 * self.distance * bigger))
4573 self.final = (self.location + self.moves * self.increment).roundtogrid()
4574 self.location = self.origin
4575 self.nextlocation = None
4578 self.location = self.origin
4582 return self.location.roundtogrid() == self.final
4585 "Next step on course."
4587 self.nextlocation = self.location + self.increment
4588 samequad = self.location.quadrant() == self.nextlocation.quadrant()
4589 self.location = self.nextlocation
4593 return self.location.quadrant()
4596 return self.location.sector()
4599 return self.distance * (w ** 3) * (game.shldup + 1)
4602 return 10.0 * self.distance / w ** 2
4606 "Move under impulse power."
4608 if damaged(DIMPULS):
4611 prout(_('Engineer Scott- "The impulse engines are damaged, Sir."'))
4613 if game.energy > 30.0:
4615 icourse = getcourse(isprobe=False)
4618 power = 20.0 + 100.0 * icourse.distance
4621 if power >= game.energy:
4622 # Insufficient power for trip
4624 prout(_('First Officer Spock- "Captain, the impulse engines'))
4625 prout(_("require 20.0 units to engage, plus 100.0 units per"))
4626 if game.energy > 30:
4628 _("quadrant. We can go, therefore, a maximum of %d")
4629 % int(0.01 * (game.energy - 20.0) - 0.05)
4631 prout(_(' quadrants."'))
4633 prout(_('quadrant. They are, therefore, useless."'))
4636 # Make sure enough time is left for the trip
4637 game.optime = icourse.distance / 0.095
4638 if game.optime >= game.state.remtime:
4639 prout(_('First Officer Spock- "Captain, our speed under impulse'))
4640 prout(_("power is only 0.95 sectors per stardate. Are you sure"))
4641 proutn(_('we dare spend the time?" '))
4644 # Activate impulse engines and pay the cost
4645 imove(icourse, noattack=False)
4649 power = 20.0 + 100.0 * icourse.distance
4650 game.energy -= power
4651 game.optime = icourse.distance / 0.095
4652 if game.energy <= 0:
4657 def warp(wcourse, involuntary):
4658 "Move under warp drive."
4661 if not involuntary: # Not WARPX entry
4668 'Engineer Scott- "The warp engines can not be used while cloaked, Sir."'
4672 if game.damage[DWARPEN] > 10.0:
4675 prout(_('Engineer Scott- "The warp engines are damaged, Sir."'))
4677 if damaged(DWARPEN) and game.warpfac > 4.0:
4680 prout(_('Engineer Scott- "Sorry, Captain. Until this damage'))
4681 prout(_(' is repaired, I can only give you warp 4."'))
4683 # Read in course and distance
4686 wcourse = getcourse(isprobe=False)
4689 # Make sure starship has enough energy for the trip
4690 # Note: this formula is slightly different from the C version,
4691 # and lets you skate a bit closer to the edge.
4692 if wcourse.power(game.warpfac) >= game.energy:
4693 # Insufficient power for trip
4696 prout(_("Engineering to bridge--"))
4697 if not game.shldup or 0.5 * wcourse.power(game.warpfac) > game.energy:
4698 iwarp = (game.energy / (wcourse.distance + 0.05)) ** 0.333333333
4700 prout(_("We can't do it, Captain. We don't have enough energy."))
4703 _("We don't have enough energy, but we could do it at warp %d")
4708 prout(_("if you'll lower the shields."))
4712 prout(_("We haven't the energy to go that far with the shields up."))
4714 # Make sure enough time is left for the trip
4715 game.optime = wcourse.time(game.warpfac)
4716 if game.optime >= 0.8 * game.state.remtime:
4718 prout(_('First Officer Spock- "Captain, I compute that such'))
4720 _(" a trip would require approximately %2.0f")
4721 % (100.0 * game.optime / game.state.remtime)
4723 prout(_(" percent of our"))
4724 proutn(_(' remaining time. Are you sure this is wise?" '))
4730 if game.warpfac > 6.0:
4731 # Decide if engine damage will occur
4732 # ESR: Seems wrong. Probability of damage goes *down* with distance?
4733 prob = wcourse.distance * (6.0 - game.warpfac) ** 2 / 66.666666666
4734 if prob > rnd.real():
4736 wcourse.distance = rnd.real(wcourse.distance)
4737 # Decide if time warp will occur
4738 if 0.5 * wcourse.distance * math.pow(7.0, game.warpfac - 10.0) > rnd.real():
4740 if game.idebug and game.warpfac == 10 and not twarp: # pragma: no cover
4742 proutn("=== Force time warp? ")
4746 # If time warp or engine damage, check path
4747 # If it is obstructed, don't do warp or damage
4748 look = wcourse.moves
4752 w = wcourse.sector()
4753 if not w.valid_sector():
4755 if game.quad[w.i][w.j] != ".":
4759 # Activate Warp Engines and pay the cost
4760 imove(wcourse, noattack=False)
4763 game.energy -= wcourse.power(game.warpfac)
4764 if game.energy <= 0:
4766 game.optime = wcourse.time(game.warpfac)
4770 game.damage[DWARPEN] = game.damfac * rnd.real(1.0, 4.0)
4772 prout(_("Engineering to bridge--"))
4773 prout(_(" Scott here. The warp engines are damaged."))
4774 prout(_(" We'll have to reduce speed to warp 4."))
4780 "Change the warp factor."
4782 key = scanner.nexttok()
4786 proutn(_("Warp factor- "))
4790 if game.damage[DWARPEN] > 10.0:
4791 prout(_("Warp engines inoperative."))
4793 if damaged(DWARPEN) and scanner.real > 4.0:
4794 prout(_("Engineer Scott- \"I'm doing my best, Captain,"))
4795 prout(_(' but right now we can only go warp 4."'))
4797 if scanner.real > 10.0:
4798 prout(_('Helmsman Sulu- "Our top speed is warp 10, Captain."'))
4800 if scanner.real < 1.0:
4801 prout(_('Helmsman Sulu- "We can\'t go below warp 1, Captain."'))
4803 oldfac = game.warpfac
4804 game.warpfac = scanner.real
4805 if game.warpfac <= oldfac or game.warpfac <= 6.0:
4806 prout(_('Helmsman Sulu- "Warp factor %d, Captain."') % int(game.warpfac))
4808 if game.warpfac < 8.00:
4809 prout(_('Engineer Scott- "Aye, but our maximum safe speed is warp 6."'))
4811 if game.warpfac == 10.0:
4812 prout(_('Engineer Scott- "Aye, Captain, we\'ll try it."'))
4814 prout(_('Engineer Scott- "Aye, Captain, but our engines may not take it."'))
4819 "Cope with being tossed out of quadrant by supernova or yanked by beam."
4821 # is captain on planet?
4823 if damaged(DTRANSP):
4826 prout(_("Scotty rushes to the transporter controls."))
4828 prout(_("But with the shields up it's hopeless."))
4830 prouts(_("His desperate attempt to rescue you . . ."))
4831 if rnd.withprob(0.5):
4835 prout(_("SUCCEEDS!"))
4838 proutn(_("The crystals mined were "))
4839 if rnd.withprob(0.25):
4846 # Check to see if captain in shuttle craft
4851 # Inform captain of attempt to reach safety
4855 prouts(_("***RED ALERT! RED ALERT!"))
4857 proutn(_("The %s has stopped in a quadrant containing") % crmshp())
4858 prouts(_(" a supernova."))
4860 prout(_("***Emergency automatic override attempts to hurl ") + crmshp())
4861 prout(_("safely out of quadrant."))
4862 if not damaged(DRADIO):
4863 game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
4864 # Try to use warp engines
4865 if damaged(DWARPEN):
4867 prout(_("Warp engines damaged."))
4870 game.warpfac = rnd.real(6.0, 8.0)
4871 prout(_("Warp factor set to %d") % int(game.warpfac))
4872 power = 0.75 * game.energy
4873 dist = power / (game.warpfac * game.warpfac * game.warpfac * (game.shldup + 1))
4874 dist = max(dist, rnd.real(math.sqrt(2)))
4875 bugout = course(bearing=rnd.real(12), distance=dist) # How dumb!
4876 game.optime = bugout.time(game.warpfac)
4878 game.inorbit = False
4879 warp(bugout, involuntary=True)
4881 # This is bad news, we didn't leave quadrant.
4885 prout(_("Insufficient energy to leave quadrant."))
4888 # Repeat if another snova
4889 if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
4891 if game.unwon() == 0:
4892 finish(FWON) # Snova killed remaining enemy.
4896 "Let's do the time warp again."
4897 prout(_("***TIME WARP ENTERED."))
4898 if game.state.snap and rnd.withprob(0.5):
4901 _("You are traveling backwards in time %d stardates.")
4902 % int(game.state.date - game.snapsht.date)
4904 game.state = game.snapsht
4905 game.state.snap = False
4906 if len(game.state.kcmdr):
4907 schedule(FTBEAM, expran(game.intime / len(game.state.kcmdr)))
4908 schedule(FBATTAK, expran(0.3 * game.intime))
4909 schedule(FSNOVA, expran(0.5 * game.intime))
4910 # next snapshot will be sooner
4911 schedule(FSNAP, expran(0.25 * game.state.remtime))
4913 if game.state.nscrem:
4914 schedule(FSCMOVE, 0.2777)
4918 game.battle.invalidate()
4919 # Make sure Galileo is consistant -- Snapshot may have been taken
4920 # when on planet, which would give us two Galileos!
4922 for l in range(game.inplan):
4923 if game.state.planets[l].known == "shuttle_down":
4925 if game.iscraft == "onship" and game.ship == "E":
4928 'Chekov- "Security reports the Galileo has disappeared, Sir!'
4931 game.iscraft = "offship"
4932 # Likewise, if in the original time the Galileo was abandoned, but
4933 # was on ship earlier, it would have vanished -- let's restore it.
4934 if game.iscraft == "offship" and not gotit and game.damage[DSHUTTL] >= 0.0:
4936 _('Chekov- "Security reports the Galileo has reappeared in the dock!"')
4938 game.iscraft = "onship"
4939 # There used to be code to do the actual reconstrction here,
4940 # but the starchart is now part of the snapshotted galaxy state.
4941 prout(_("Spock has reconstructed a correct star chart from memory"))
4943 # Go forward in time
4944 game.optime = expran(0.5 * game.intime)
4945 prout(_("You are traveling forward in time %d stardates.") % int(game.optime))
4946 # cheat to make sure no tractor beams occur during time warp
4947 postpone(FTBEAM, game.optime)
4948 game.damage[DRADIO] += game.optime
4950 events() # Stas Sergeev added this -- do pending events
4954 "Launch deep-space probe."
4955 # New code to launch a deep space probe
4956 if game.nprobes == 0:
4959 if game.ship == "E":
4960 prout(_('Engineer Scott- "We have no more deep space probes, Sir."'))
4962 prout(_("Ye Faerie Queene has no deep space probes."))
4967 prout(_('Engineer Scott- "The probe launcher is damaged, Sir."'))
4969 if is_scheduled(FDSPROB):
4972 if damaged(DRADIO) and game.condition != "docked":
4973 prout(_('Spock- "Records show the previous probe has not yet'))
4974 prout(_(' reached its destination."'))
4976 prout(_('Uhura- "The previous probe is still reporting data, Sir."'))
4978 key = scanner.nexttok()
4980 if game.nprobes == 1:
4981 prout(_("1 probe left."))
4983 prout(_("%d probes left") % game.nprobes)
4984 proutn(_("Are you sure you want to fire a probe? "))
4987 game.isarmed = False
4988 if key == "IHALPHA" and scanner.token == "armed":
4990 key = scanner.nexttok()
4991 elif key == "IHEOL":
4992 proutn(_("Arm NOVAMAX warhead? "))
4994 elif key == "IHREAL": # first element of course
4995 scanner.push(scanner.token)
4997 game.probe = getcourse(isprobe=True)
5001 schedule(FDSPROB, 0.01) # Time to move one sector
5002 prout(_('Ensign Chekov- "The deep space probe is launched, Captain."'))
5008 "Yell for help from nearest starbase."
5009 # There's more than one way to move in this game!
5011 # Test for conditions which prevent calling for help
5012 if game.condition == "docked":
5013 prout(_('Lt. Uhura- "But Captain, we\'re already docked."'))
5016 prout(_("Subspace radio damaged."))
5018 if not game.state.baseq:
5019 prout(_('Lt. Uhura- "Captain, I\'m not getting any response from Starbase."'))
5022 prout(_("You must be aboard the %s.") % crmshp())
5024 # OK -- call for help from nearest starbase
5026 if game.base.i != 0:
5027 # There's one in this quadrant
5028 ddist = (game.base - game.sector).distance()
5030 ibq = None # Force base-quadrant game to persist past loop
5032 for ibq in game.state.baseq:
5033 xdist = QUADSIZE * (ibq - game.quadrant).distance()
5037 prout(_("No starbases remain. You are alone in a hostile galaxy."))
5039 # Since starbase not in quadrant, set up new quadrant
5042 # dematerialize starship
5043 game.quad[game.sector.i][game.sector.j] = "."
5045 _("Starbase in Quadrant %s responds--%s dematerializes")
5046 % (game.quadrant, crmshp())
5048 game.sector.invalidate()
5049 for m in range(1, 5 + 1):
5050 w = game.base.scatter()
5051 if w.valid_sector() and game.quad[w.i][w.j] == ".":
5052 # found one -- finish up
5055 if game.sector is None:
5056 prout(_("You have been lost in space..."))
5057 finish(FMATERIALIZE)
5059 # Give starbase three chances to rematerialize starship
5060 probf = math.pow((1.0 - math.pow(0.98, ddist)), 0.33333333)
5061 for m in range(1, 3 + 1):
5068 proutn(_(" attempt to re-materialize ") + crmshp())
5069 game.quad[game.sector.i][game.sector.j] = ("-", "o", "O")[m - 1]
5072 if rnd.real() > probf:
5076 if game.options & OPTION_CURSES: # pragma: no cover
5077 curses.delay_output(500)
5079 game.quad[game.sector.i][game.sector.j] = "?"
5082 setwnd(message_window)
5083 finish(FMATERIALIZE)
5085 game.quad[game.sector.i][game.sector.j] = game.ship
5087 prout(_("succeeds."))
5091 prout(_('Lt. Uhura- "Captain, we made it!"'))
5097 if game.condition == "docked":
5098 if game.ship != "E":
5099 prout(_("You cannot abandon Ye Faerie Queene."))
5102 # Must take shuttle craft to exit
5103 if game.damage[DSHUTTL] == -1:
5104 prout(_("Ye Faerie Queene has no shuttle craft."))
5106 if game.damage[DSHUTTL] < 0:
5107 prout(_("Shuttle craft now serving Big Macs."))
5109 if game.damage[DSHUTTL] > 0:
5110 prout(_("Shuttle craft damaged."))
5113 prout(_("You must be aboard the ship."))
5115 if game.iscraft != "onship":
5116 prout(_("Shuttle craft not currently available."))
5118 # Emit abandon ship messages
5120 prouts(_("***ABANDON SHIP! ABANDON SHIP!"))
5122 prouts(_("***ALL HANDS ABANDON SHIP!"))
5124 prout(_("Captain and crew escape in shuttle craft."))
5125 if not game.state.baseq:
5126 # Oops! no place to go...
5129 q = game.state.galaxy[game.quadrant.i][game.quadrant.j]
5131 if not (game.options & OPTION_WORLDS) and not damaged(DTRANSP):
5132 prout(_("Remainder of ship's complement beam down"))
5133 prout(_("to nearest habitable planet."))
5134 elif q.planet is not None and not damaged(DTRANSP):
5135 prout(_("Remainder of ship's complement beam down to %s.") % q.planet)
5137 prout(_("Entire crew of %d left to die in outer space.") % game.state.crew)
5138 game.casual += game.state.crew
5139 game.abandoned += game.state.crew
5140 # If at least one base left, give 'em the Faerie Queene
5142 game.icrystl = False # crystals are lost
5143 game.nprobes = 0 # No probes
5144 prout(_("You are captured by Klingons and released to"))
5145 prout(_("the Federation in a prisoner-of-war exchange."))
5146 nb = rnd.integer(len(game.state.baseq))
5147 # Set up quadrant and position FQ adjacient to base
5148 if not game.quadrant == game.state.baseq[nb]:
5149 game.quadrant = game.state.baseq[nb]
5150 game.sector.i = game.sector.j = 5
5153 # position next to base by trial and error
5154 game.quad[game.sector.i][game.sector.j] = "."
5156 for l in range(QUADSIZE):
5157 game.sector = game.base.scatter()
5159 game.sector.valid_sector()
5160 and game.quad[game.sector.i][game.sector.j] == "."
5164 break # found a spot
5165 game.sector.i = QUADSIZE / 2
5166 game.sector.j = QUADSIZE / 2
5168 # Get new commission
5169 game.quad[game.sector.i][game.sector.j] = game.ship = "F"
5170 game.state.crew = FULLCREW
5171 prout(_("Starfleet puts you in command of another ship,"))
5172 prout(_("the Faerie Queene, which is antiquated but,"))
5173 prout(_("still useable."))
5175 prout(_("The dilithium crystals have been moved."))
5177 game.iscraft = "offship" # Galileo disappears
5179 game.condition = "docked"
5180 for l in range(NDEVICES):
5181 game.damage[l] = 0.0
5182 game.damage[DSHUTTL] = -1
5183 game.energy = game.inenrg = 3000.0
5184 game.shield = game.inshld = 1250.0
5185 game.torps = game.intorps = 6
5186 game.lsupres = game.inlsr = 3.0
5189 game.brigfree = game.brigcapacity = 300
5193 # Code from planets.c begins here.
5197 "Abort a lengthy operation if an event interrupts it."
5202 or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova
5210 "Report on (uninhabited) planets in the galaxy."
5214 prout(_('Spock- "Planet report follows, Captain."'))
5216 for i in range(game.inplan):
5217 if game.state.planets[i].pclass == "destroyed":
5220 game.state.planets[i].known != "unknown"
5221 and not game.state.planets[i].inhabited
5224 if game.idebug and game.state.planets[i].known == "unknown":
5225 proutn("(Unknown) ")
5226 proutn(_("Quadrant %s") % game.state.planets[i].quadrant)
5227 proutn(_(" class "))
5228 proutn(game.state.planets[i].pclass)
5230 if game.state.planets[i].crystals != "present":
5232 prout(_("dilithium crystals present."))
5233 if game.state.planets[i].known == "shuttle_down":
5234 prout(_(" Shuttle Craft Galileo on surface."))
5236 prout(_("No information available."))
5240 "Enter standard orbit."
5244 prout(_("Already in standard orbit."))
5246 if damaged(DWARPEN) and damaged(DIMPULS):
5247 prout(_("Both warp and impulse engines damaged."))
5249 if game.plnet is None:
5250 prout("There is no planet in this sector.")
5252 if abs(game.sector.i - game.plnet.i) > 1 or abs(game.sector.j - game.plnet.j) > 1:
5253 prout(crmshp() + _(" not adjacent to planet."))
5256 game.optime = rnd.real(0.02, 0.05)
5257 prout(_('Helmsman Sulu- "Entering standard orbit, Sir."'))
5261 game.height = rnd.real(1400, 8600)
5262 prout(_('Sulu- "Entered orbit at altitude %.2f kilometers."') % game.height)
5268 "Examine planets in this quadrant."
5269 if damaged(DSRSENS):
5270 if game.options & OPTION_TTY:
5271 prout(_("Short range sensors damaged."))
5273 if game.iplnet is None:
5274 if game.options & OPTION_TTY:
5275 prout(_('Spock- "No planet in this quadrant, Captain."'))
5277 if game.iplnet.known == "unknown":
5278 prout(_('Spock- "Sensor scan for Quadrant %s-') % game.quadrant)
5281 _(" Planet at Sector %s is of class %s.")
5282 % (game.plnet, game.iplnet.pclass)
5284 if game.iplnet.known == "shuttle_down":
5285 prout(_(" Sensors show Galileo still on surface."))
5286 proutn(_(" Readings indicate"))
5287 if game.iplnet.crystals != "present":
5289 prout(_(' dilithium crystals present."'))
5290 if game.iplnet.known == "unknown":
5291 game.iplnet.known = "known"
5292 elif game.iplnet.inhabited:
5293 prout(_('Spock- "The inhabited planet %s ') % game.iplnet.name)
5294 prout(_(' is located at Sector %s, Captain."') % game.plnet)
5298 "Use the transporter."
5302 if damaged(DTRANSP):
5303 prout(_("Transporter damaged."))
5304 if not damaged(DSHUTTL) and (
5305 game.iplnet.known == "shuttle_down" or game.iscraft == "onship"
5308 proutn(_('Spock- "May I suggest the shuttle craft, Sir?" '))
5312 if not game.inorbit:
5313 prout(crmshp() + _(" not in standard orbit."))
5316 prout(_("Impossible to transport through shields."))
5318 if game.iplnet.known == "unknown":
5319 prout(_('Spock- "Captain, we have no information on this planet'))
5320 prout(_(" and Starfleet Regulations clearly state that in this situation"))
5321 prout(_(' you may not go down."'))
5323 if not game.landed and game.iplnet.crystals == "absent":
5324 prout(_('Spock- "Captain, I fail to see the logic in'))
5325 prout(_(" exploring a planet with no dilithium crystals."))
5326 proutn(_(' Are you sure this is wise?" '))
5330 if game.options & OPTION_ALMY:
5331 nrgneed = 50 * game.skill + game.height / 100.0
5332 if nrgneed > game.energy:
5333 prout(_("Engineering to bridge--"))
5334 prout(_(" Captain, we don't have enough energy for transportation."))
5336 if not game.landed and nrgneed * 2 > game.energy:
5337 prout(_("Engineering to bridge--"))
5338 prout(_(" Captain, we have enough energy only to transport you down to"))
5339 prout(_(" the planet, but there wouldn't be an energy for the trip back."))
5340 if game.iplnet.known == "shuttle_down":
5342 _(" Although the Galileo shuttle craft may still be on a surface.")
5344 proutn(_(' Are you sure this is wise?" '))
5349 # Coming from planet
5350 if game.iplnet.known == "shuttle_down":
5351 proutn(_('Spock- "Wouldn\'t you rather take the Galileo?" '))
5355 prout(_("Your crew hides the Galileo to prevent capture by aliens."))
5356 prout(_("Landing party assembled, ready to beam up."))
5358 prout(_("Kirk whips out communicator..."))
5359 prouts(_("BEEP BEEP BEEP"))
5361 prout(_('"Kirk to enterprise- Lock on coordinates...energize."'))
5364 prout(_('Scotty- "Transporter room ready, Sir."'))
5366 prout(_("Kirk and landing party prepare to beam down to planet surface."))
5368 prout(_('Kirk- "Energize."'))
5371 prouts("WWHOOOIIIIIRRRRREEEE.E.E. . . . . . .")
5373 if not rnd.withprob(0.98):
5374 prouts("BOOOIIIOOOIIOOOOIIIOIING . . .")
5376 prout(_('Scotty- "Oh my God! I\'ve lost them."'))
5379 prouts(". . . . . . .E.E.EEEERRRRRIIIIIOOOHWW")
5380 game.landed = not game.landed
5381 game.energy -= nrgneed
5383 prout(_("Transport complete."))
5384 if game.landed and game.iplnet.known == "shuttle_down":
5385 prout(_("The shuttle craft Galileo is here!"))
5386 if not game.landed and game.imine:
5394 "Strip-mine a world for dilithium."
5398 prout(_("Mining party not on planet."))
5400 if game.iplnet.crystals == "mined":
5401 prout(_("This planet has already been strip-mined for dilithium."))
5403 elif game.iplnet.crystals == "absent":
5404 prout(_("No dilithium crystals on this planet."))
5407 prout(_("You've already mined enough crystals for this trip."))
5409 if game.icrystl and game.cryprob == 0.05:
5410 prout(_("With all those fresh crystals aboard the ") + crmshp())
5411 prout(_("there's no reason to mine more at this time."))
5413 game.optime = rnd.real(0.1, 0.3) * (ord(game.iplnet.pclass) - ord("L"))
5416 prout(_("Mining operation complete."))
5417 game.iplnet.crystals = "mined"
5418 game.imine = game.ididit = True
5422 "Use dilithium crystals."
5426 if not game.icrystl:
5427 prout(_("No dilithium crystals available."))
5429 if game.energy >= 1000:
5430 prout(_('Spock- "Captain, Starfleet Regulations prohibit such an operation'))
5431 prout(_(" except when Condition Yellow exists."))
5433 prout(_('Spock- "Captain, I must warn you that loading'))
5434 prout(_(" raw dilithium crystals into the ship's power"))
5435 prout(_(" system may risk a severe explosion."))
5436 proutn(_(' Are you sure this is wise?" '))
5441 prout(_('Engineering Officer Scott- "(GULP) Aye Sir.'))
5442 prout(_(' Mr. Spock and I will try it."'))
5444 prout(_('Spock- "Crystals in place, Sir.'))
5445 prout(_(' Ready to activate circuit."'))
5447 prouts(_('Scotty- "Keep your fingers crossed, Sir!"'))
5449 if rnd.withprob(game.cryprob):
5450 prouts(_(" \"Activating now! - - No good! It's***"))
5452 prouts(_("***RED ALERT! RED A*L********************************"))
5455 prouts(_("****************** KA-BOOM!!!! *******************"))
5459 game.energy += rnd.real(5000.0, 5500.0)
5460 prouts(_(' "Activating now! - - '))
5461 prout(_("The instruments"))
5462 prout(_(" are going crazy, but I think it's"))
5463 prout(_(' going to work!! Congratulations, Sir!"'))
5469 "Use shuttlecraft for planetary jaunt."
5472 if damaged(DSHUTTL):
5473 if game.damage[DSHUTTL] == -1.0:
5474 if game.inorbit and game.iplnet.known == "shuttle_down":
5475 prout(_("Ye Faerie Queene has no shuttle craft bay to dock it at."))
5477 prout(_("Ye Faerie Queene had no shuttle craft."))
5478 elif game.damage[DSHUTTL] > 0:
5479 prout(_("The Galileo is damaged."))
5480 else: # game.damage[DSHUTTL] < 0
5481 prout(_("Shuttle craft is now serving Big Macs."))
5483 if not game.inorbit:
5484 prout(crmshp() + _(" not in standard orbit."))
5486 if (game.iplnet.known != "shuttle_down") and game.iscraft != "onship":
5487 prout(_("Shuttle craft not currently available."))
5489 if not game.landed and game.iplnet.known == "shuttle_down":
5490 prout(_("You will have to beam down to retrieve the shuttle craft."))
5492 if game.shldup or game.condition == "docked":
5493 prout(_("Shuttle craft cannot pass through shields."))
5495 if game.iplnet.known == "unknown":
5496 prout(_('Spock- "Captain, we have no information on this planet'))
5497 prout(_(" and Starfleet Regulations clearly state that in this situation"))
5498 prout(_(' you may not fly down."'))
5500 game.optime = 3.0e-5 * game.height
5501 if game.optime >= 0.8 * game.state.remtime:
5502 prout(_('First Officer Spock- "Captain, I compute that such'))
5504 _(" a maneuver would require approximately %2d%% of our")
5505 % int(100 * game.optime / game.state.remtime)
5507 prout(_("remaining time."))
5508 proutn(_('Are you sure this is wise?" '))
5514 if game.iscraft == "onship":
5516 if not damaged(DTRANSP):
5517 proutn(_('Spock- "Would you rather use the transporter?" '))
5521 proutn(_("Shuttle crew"))
5523 proutn(_("Rescue party"))
5524 prout(_(" boards Galileo and swoops toward planet surface."))
5525 game.iscraft = "offship"
5529 game.iplnet.known = "shuttle_down"
5530 prout(_("Trip complete."))
5533 # Ready to go back to ship
5534 prout(_("You and your mining party board the"))
5535 prout(_("shuttle craft for the trip back to the Enterprise."))
5537 prouts(_("The short hop begins . . ."))
5539 game.iplnet.known = "known"
5545 game.iscraft = "onship"
5551 prout(_("Trip complete."))
5554 # Kirk on ship and so is Galileo
5555 prout(_("Mining party assembles in the hangar deck,"))
5556 prout(_('ready to board the shuttle craft "Galileo".'))
5558 prouts(_("The hangar doors open; the trip begins."))
5561 game.iscraft = "offship"
5564 game.iplnet.known = "shuttle_down"
5567 prout(_("Trip complete."))
5572 "Use the big zapper."
5576 if game.ship != "E":
5577 prout(_("Ye Faerie Queene has no death ray."))
5579 if len(game.enemies) == 0:
5580 prout(_('Sulu- "But Sir, there are no enemies in this quadrant."'))
5583 prout(_("Death Ray is damaged."))
5585 prout(_("Spock- \"Captain, the 'Experimental Death Ray'"))
5586 prout(_(" is highly unpredictible. Considering the alternatives,"))
5587 proutn(_(' are you sure this is wise?" '))
5590 prout(_('Spock- "Acknowledged."'))
5593 prouts(_("WHOOEE ... WHOOEE ... WHOOEE ... WHOOEE"))
5595 prout(_("Crew scrambles in emergency preparation."))
5596 prout(_("Spock and Scotty ready the death ray and"))
5597 prout(_("prepare to channel all ship's power to the device."))
5599 prout(_('Spock- "Preparations complete, sir."'))
5600 prout(_('Kirk- "Engage!"'))
5602 prouts(_("WHIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"))
5605 # Ugh. This test (For Tom Almy's death-ray upgrade) was inverted for a long time.
5606 # Furthermore, somebody (ESR or Stas?) changed Tom Almy's 0.7 upgraded chance of
5608 if game.options & OPTION_ALMY:
5612 prouts(_('Sulu- "Captain! It\'s working!"'))
5614 while len(game.enemies) > 0:
5616 game.enemies[-1].location,
5617 game.quad[game.enemies[-1].location.i][game.enemies[-1].location.j],
5618 game.enemies[-1].location,
5620 prout(_('Ensign Chekov- "Congratulations, Captain!"'))
5621 if game.unwon() == 0:
5623 if game.options & OPTION_ALMY:
5624 prout(_("Spock- \"Captain, I believe the `Experimental Death Ray'"))
5625 if rnd.withprob(0.05):
5626 prout(_(' is still operational."'))
5628 prout(_(' has been rendered nonfunctional."'))
5629 game.damage[DDRAY] = 39.95
5631 r = rnd.real() # Pick failure method
5633 prouts(_('Sulu- "Captain! It\'s working!"'))
5635 prouts(_("***RED ALERT! RED ALERT!"))
5637 prout(_("***MATTER-ANTIMATTER IMPLOSION IMMINENT!"))
5639 prouts(_("***RED ALERT! RED A*L********************************"))
5642 prouts(_("****************** KA-BOOM!!!! *******************"))
5647 prouts(_('Sulu- "Captain! Yagabandaghangrapl, brachriigringlanbla!"'))
5649 prout(_('Lt. Uhura- "Graaeek! Graaeek!"'))
5651 prout(_('Spock- "Fascinating! . . . All humans aboard'))
5652 prout(_(" have apparently been transformed into strange mutations."))
5653 prout(_(" Vulcans do not seem to be affected."))
5655 prout(_('Kirk- "Raauch! Raauch!"'))
5659 prouts(_('Sulu- "Captain! It\'s --WHAT?!?!"'))
5661 proutn(_('Spock- "I believe the word is'))
5662 prouts(_(" *ASTONISHING*"))
5663 prout(_(" Mr. Sulu."))
5664 for i in range(QUADSIZE):
5665 for j in range(QUADSIZE):
5666 if game.quad[i][j] == ".":
5667 game.quad[i][j] = "?"
5668 prout(_(" Captain, our quadrant is now infested with"))
5669 prouts(_(" - - - - - - *THINGS*."))
5671 prout(_(' I have no logical explanation."'))
5673 prouts(_('Sulu- "Captain! The Death Ray is creating tribbles!"'))
5675 prout(_('Scotty- "There are so many tribbles down here'))
5676 prout(_(" in Engineering, we can't move for 'em, Captain.\""))
5681 # Code from reports.c begins here
5684 def attackreport(curt):
5685 "eport status of bases under attack."
5687 if is_scheduled(FCDBAS):
5689 _("Starbase in Quadrant %s is currently under Commander attack.")
5692 prout(_("It can hold out until Stardate %d.") % int(scheduled(FCDBAS)))
5693 elif game.isatb == 1:
5695 _("Starbase in Quadrant %s is under Super-commander attack.")
5698 prout(_("It can hold out until Stardate %d.") % int(scheduled(FSCDBAS)))
5700 prout(_("No Starbase is currently under attack."))
5702 if is_scheduled(FCDBAS):
5704 _("Base in %s attacked by C. Alive until %.1f")
5705 % (game.battle, scheduled(FCDBAS))
5709 _("Base in %s attacked by S. Alive until %.1f")
5710 % (game.state.kscmdr, scheduled(FSCDBAS))
5716 # report on general game status
5718 # pylint: disable=consider-using-ternary
5719 s1 = (game.thawed and _("thawed ")) or ""
5720 s2 = {1: "short", 2: "medium", 4: "long"}[game.length]
5721 s3 = (None, _("novice"), _("fair"), _("good"), _("expert"), _("emeritus"))[
5725 _("You %s a %s%s %s game.")
5726 % ((_("were playing"), _("are playing"))[game.alldone], s1, s2, s3)
5728 if game.skill > SKILL_GOOD and game.thawed and not game.alldone:
5729 prout(_("No plaque is allowed."))
5731 prout(_("This is tournament game %d.") % game.tourn)
5732 prout(_('Your secret password is "%s"') % game.passwd)
5734 _("%d of %d Klingons have been killed")
5736 ((game.inkling + game.incom + game.inscom) - game.unwon()),
5737 (game.inkling + game.incom + game.inscom),
5740 if game.incom - len(game.state.kcmdr):
5742 _(", including %d Commander%s.")
5744 game.incom - len(game.state.kcmdr),
5745 (_("s"), "")[(game.incom - len(game.state.kcmdr)) == 1],
5748 elif game.inkling - game.remkl() + (game.inscom - game.state.nscrem) > 0:
5749 prout(_(", but no Commanders."))
5752 if game.skill > SKILL_FAIR:
5754 _("The Super Commander has %sbeen destroyed.")
5755 % ("", _("not "))[game.state.nscrem]
5757 if len(game.state.baseq) != game.inbase:
5759 if game.inbase - len(game.state.baseq) == 1:
5760 proutn(_("has been 1 base"))
5762 proutn(_("have been %d bases") % (game.inbase - len(game.state.baseq)))
5763 prout(_(" destroyed, %d remaining.") % len(game.state.baseq))
5765 prout(_("There are %d bases.") % game.inbase)
5766 if communicating() or game.iseenit:
5767 # Don't report this if not seen and
5768 # either the radio is dead or not at base!
5773 _("%d casualt%s suffered so far.")
5774 % (game.casual, ("y", "ies")[game.casual != 1])
5776 if game.brigcapacity != game.brigfree:
5777 embriggened = game.brigcapacity - game.brigfree
5778 if embriggened == 1:
5779 prout(_("1 Klingon in brig"))
5781 prout(_("%d Klingons in brig.") % embriggened)
5782 if game.kcaptured == 0:
5784 elif game.kcaptured == 1:
5785 prout(_("1 captured Klingon turned in to Starfleet."))
5787 prout(_("%d captured Klingons turned in to Star Fleet.") % game.kcaptured)
5790 _("There were %d call%s for help.")
5791 % (game.nhelp, ("", _("s"))[game.nhelp != 1])
5793 if game.ship == "E":
5794 proutn(_("You have "))
5796 proutn("%d" % (game.nprobes))
5799 proutn(_(" deep space probe"))
5800 if game.nprobes != 1:
5803 if communicating() and is_scheduled(FDSPROB):
5805 proutn(_("An armed deep space probe is in "))
5807 proutn(_("A deep space probe is in "))
5808 prout("Quadrant %s." % game.probe.quadrant())
5810 if game.cryprob <= 0.05:
5811 prout(_("Dilithium crystals aboard ship... not yet used."))
5815 while game.cryprob > ai:
5819 _("Dilithium crystals have been used %d time%s.")
5820 % (i, (_("s"), "")[i == 1])
5826 "Long-range sensor scan."
5827 if damaged(DLRSENS):
5828 # Now allow base's sensors if docked
5829 if game.condition != "docked":
5831 prout(_("LONG-RANGE SENSORS DAMAGED."))
5834 prout(_("Starbase's long-range scan"))
5836 prout(_("Long-range scan"))
5837 for x in range(game.quadrant.i - 1, game.quadrant.i + 2):
5840 for y in range(game.quadrant.j - 1, game.quadrant.j + 2):
5841 if not Coord(x, y).valid_quadrant():
5845 if not damaged(DRADIO):
5846 game.state.galaxy[x][y].charted = True
5847 game.state.chart[x][y].klingons = game.state.galaxy[x][y].klingons
5848 game.state.chart[x][y].starbase = game.state.galaxy[x][y].starbase
5849 game.state.chart[x][y].stars = game.state.galaxy[x][y].stars
5850 if not silent and game.state.galaxy[x][y].supernova:
5854 game.state.chart[x][y].klingons * 100
5855 + game.state.chart[x][y].starbase * 10
5856 + game.state.chart[x][y].stars
5858 proutn(((3 - len(cn)) * ".") + cn)
5867 for i in range(NDEVICES):
5870 prout(_("DEVICE REPAIR TIMES"))
5878 " %-26s\t%8.2f\t\t%8.2f"
5879 % (device[i], game.damage[i] + 0.05, DOCKFAC * game.damage[i] + 0.005)
5882 prout(_("All devices functional."))
5886 "Update the chart in the Enterprise's computer from galaxy data."
5887 game.lastchart = game.state.date
5888 for i in range(GALSIZE):
5889 for j in range(GALSIZE):
5890 if game.state.galaxy[i][j].charted:
5891 game.state.chart[i][j].klingons = game.state.galaxy[i][j].klingons
5892 game.state.chart[i][j].starbase = game.state.galaxy[i][j].starbase
5893 game.state.chart[i][j].stars = game.state.galaxy[i][j].stars
5897 "Display the star chart."
5899 if game.options & OPTION_AUTOSCAN:
5903 if game.lastchart < game.state.date and game.condition == "docked":
5904 prout(_('Spock- "I revised the Star Chart from the starbase\'s records."'))
5906 prout(_(" STAR CHART FOR THE KNOWN GALAXY"))
5907 if game.state.date > game.lastchart:
5909 _("(Last surveillance update %d stardates ago).")
5910 % ((int)(game.state.date - game.lastchart))
5912 prout(" 1 2 3 4 5 6 7 8")
5913 for i in range(GALSIZE):
5914 if game.options & OPTION_ALPHAMERIC:
5915 proutn("%c |" % letterize(i + 1))
5917 proutn("%d |" % (i + 1))
5918 for j in range(GALSIZE):
5920 (game.options & OPTION_SHOWME)
5921 and i == game.quadrant.i
5922 and j == game.quadrant.j
5927 if game.state.galaxy[i][j].supernova:
5930 not game.state.galaxy[i][j].charted and game.state.galaxy[i][j].starbase
5933 elif game.state.galaxy[i][j].charted:
5935 game.state.chart[i][j].klingons * 100
5936 + game.state.chart[i][j].starbase * 10
5937 + game.state.chart[i][j].stars
5939 if game.options & OPTION_DOTFILL:
5940 show = show.replace(" ", ".")
5945 (game.options & OPTION_SHOWME)
5946 and i == game.quadrant.i
5947 and j == game.quadrant.j
5957 def sectscan(goodScan, i, j):
5958 "Light up an individual dot in a sector."
5959 if goodScan or (abs(i - game.sector.i) <= 1 and abs(j - game.sector.j) <= 1):
5960 if game.quad[i][j] in ("E", "F"):
5962 highvideo() # pragma: no cover
5983 }.get(game.quad[i][j], DEFAULT)
5985 proutn("%c " % game.quad[i][j])
5992 "Emit status report lines"
5993 if not req or req == 1:
5996 _("%.1f, Time Left %.2f") % (game.state.date, game.state.remtime),
5998 if not req or req == 2:
5999 if game.condition != "docked":
6004 % (game.condition.upper(), sum([x > 0 for x in game.damage])),
6007 prout(_(", CLOAKED"))
6008 if not req or req == 3:
6009 prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
6010 if not req or req == 4:
6011 if damaged(DLIFSUP):
6012 if game.condition == "docked":
6013 s = _("DAMAGED, Base provides")
6015 s = _("DAMAGED, reserves=%4.2f") % game.lsupres
6018 prstat(_("Life Support"), s)
6019 if not req or req == 5:
6020 prstat(_("Warp Factor"), "%.1f" % game.warpfac)
6021 if not req or req == 6:
6023 if game.icrystl and (game.options & OPTION_SHOWME):
6024 extra = _(" (have crystals)")
6025 prstat(_("Energy"), "%.2f%s" % (game.energy, extra))
6026 if not req or req == 7:
6027 prstat(_("Torpedoes"), "%d" % (game.torps))
6028 if not req or req == 8:
6029 if damaged(DSHIELD):
6035 data = _(" %d%% %.1f units") % (
6036 int((100.0 * game.shield) / game.inshld + 0.5),
6039 prstat(_("Shields"), s + data)
6040 if not req or req == 9:
6041 prstat(_("Klingons Left"), "%d" % game.unwon())
6042 if not req or req == 10:
6043 if game.options & OPTION_WORLDS:
6044 plnet = game.state.galaxy[game.quadrant.i][game.quadrant.j].planet
6045 if plnet and plnet.inhabited:
6046 prstat(_("Major system"), plnet.name)
6048 prout(_("Sector is uninhabited"))
6049 elif not req or req == 11:
6050 attackreport(not req)
6054 "Request specified status data, a historical relic from slow TTYs."
6055 requests = ("da", "co", "po", "ls", "wa", "en", "to", "sh", "kl", "sy", "ti")
6056 while scanner.nexttok() == "IHEOL":
6057 proutn(_("Information desired? "))
6059 if scanner.token in requests:
6060 status(requests.index(scanner.token))
6062 prout(_("UNRECOGNIZED REQUEST. Legal requests are:"))
6063 prout((" date, condition, position, lsupport, warpfactor,"))
6064 prout((" energy, torpedoes, shields, klingons, system, time."))
6070 if damaged(DSRSENS):
6071 # Allow base's sensors if docked
6072 if game.condition != "docked":
6073 prout(_(" S.R. SENSORS DAMAGED!"))
6076 prout(_(" [Using Base's sensors]"))
6078 prout(_(" Short-range scan"))
6079 if goodScan and communicating():
6080 game.state.chart[game.quadrant.i][game.quadrant.j].klingons = game.state.galaxy[
6082 ][game.quadrant.j].klingons
6083 game.state.chart[game.quadrant.i][game.quadrant.j].starbase = game.state.galaxy[
6085 ][game.quadrant.j].starbase
6086 game.state.chart[game.quadrant.i][game.quadrant.j].stars = game.state.galaxy[
6088 ][game.quadrant.j].stars
6089 game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
6090 prout(" 1 2 3 4 5 6 7 8 9 10")
6091 if game.condition != "docked":
6093 for i in range(QUADSIZE):
6094 if game.options & OPTION_ALPHAMERIC:
6095 proutn("%c " % letterize(i + 1))
6097 proutn("%2d " % (i + 1))
6098 for j in range(QUADSIZE):
6099 sectscan(goodScan, i, j)
6104 "Use computer to get estimated time of arrival for a warp jump."
6108 if damaged(DCOMPTR):
6109 prout(_("COMPUTER DAMAGED, USE A POCKET CALCULATOR."))
6112 if scanner.nexttok() != "IHREAL":
6115 proutn(_("Destination quadrant and/or sector? "))
6116 if scanner.nexttok() != "IHREAL":
6119 w1.j = int(scanner.real - 0.5)
6120 if scanner.nexttok() != "IHREAL":
6123 w1.i = int(scanner.real - 0.5)
6124 if scanner.nexttok() == "IHREAL":
6125 w2.j = int(scanner.real - 0.5)
6126 if scanner.nexttok() != "IHREAL":
6129 w2.i = int(scanner.real - 0.5)
6131 if game.quadrant.j > w1.i:
6135 if game.quadrant.i > w1.j:
6139 if not w1.valid_quadrant() or not w2.valid_sector():
6143 (w1.j - game.quadrant.j + (w2.j - game.sector.j) / (QUADSIZE * 1.0)) ** 2
6144 + (w1.i - game.quadrant.i + (w2.i - game.sector.i) / (QUADSIZE * 1.0)) ** 2
6148 prout(_('Answer "no" if you don\'t know the value:'))
6151 proutn(_("Time or arrival date? "))
6152 if scanner.nexttok() == "IHREAL":
6153 ttime = scanner.real
6154 if ttime > game.state.date:
6155 ttime -= game.state.date # Actually a star date
6156 twarp = (math.floor(math.sqrt((10.0 * dist) / ttime) * 10.0) + 1.0) / 10.0
6157 if ttime <= 1e-10 or twarp > 10:
6158 prout(_("We'll never make it, sir."))
6161 twarp = max(twarp, 1.0)
6164 proutn(_("Warp factor? "))
6165 if scanner.nexttok() == "IHREAL":
6167 twarp = scanner.real
6168 if twarp < 1.0 or twarp > 10.0:
6172 prout(_("Captain, certainly you can give me one of these."))
6175 ttime = (10.0 * dist) / twarp ** 2
6176 tpower = dist * twarp * twarp * twarp * (game.shldup + 1)
6177 if tpower >= game.energy:
6178 prout(_("Insufficient energy, sir."))
6179 if not game.shldup or tpower > game.energy * 2.0:
6182 proutn(_("New warp factor to try? "))
6183 if scanner.nexttok() == "IHREAL":
6185 twarp = scanner.real
6186 if twarp < 1.0 or twarp > 10.0:
6194 prout(_("But if you lower your shields,"))
6195 proutn(_("remaining"))
6198 proutn(_("Remaining"))
6199 prout(_(" energy will be %.2f.") % (game.energy - tpower))
6201 prout(_("And we will arrive at stardate %.2f.") % (game.state.date + ttime))
6203 prout(_("Any warp speed is adequate."))
6205 prout(_("Minimum warp needed is %.2f,") % (twarp))
6206 prout(_("and we will arrive at stardate %.2f.") % (game.state.date + ttime))
6207 if game.state.remtime < ttime:
6208 prout(_("Unfortunately, the Federation will be destroyed by then."))
6210 prout(_("You'll be taking risks at that speed, Captain"))
6213 and game.state.kscmdr == w1
6214 and scheduled(FSCDBAS) < ttime + game.state.date
6215 ) or (scheduled(FCDBAS) < ttime + game.state.date and game.battle == w1):
6216 prout(_("The starbase there will be destroyed by then."))
6217 proutn(_("New warp factor to try? "))
6218 if scanner.nexttok() == "IHREAL":
6220 twarp = scanner.real
6221 if twarp < 1.0 or twarp > 10.0:
6230 # This is new in SST2K.
6234 mode = scanner.nexttok()
6237 for k, v in option_names.items():
6238 if (v[0] & game.options) and k != "ALL":
6241 prout(str(" ".join(active)))
6242 elif scanner.token in {"set", "clear"}:
6243 mode = scanner.token
6247 if scanner.type == "IHEOL":
6249 if scanner.token.upper() in option_names:
6250 changemask |= option_names[scanner.token.upper()][0]
6252 prout(_("No such option as ") + scanner.token)
6254 if (not (game.options & OPTION_CURSES)) and (
6255 changemask & OPTION_CURSES
6256 ): # pragma: no cover
6258 game.options |= changemask
6259 elif mode == "clear":
6260 if (game.options & OPTION_CURSES) and (
6261 not (changemask & OPTION_CURSES)
6262 ): # pragma: no cover
6264 game.options &= ~changemask
6265 prout(_("Acknowledged, Captain."))
6272 # Code from setup.c begins here
6276 "Issue a historically correct banner."
6278 prout(_("-SUPER- STAR TREK"))
6282 # From the FORTRAN original
6283 # prout(_("Latest update-21 Sept 78"))
6290 scanner.push("emsave.trk")
6291 key = scanner.nexttok()
6293 proutn(_("File name: "))
6294 key = scanner.nexttok()
6295 if key != "IHALPHA":
6298 if "." not in scanner.token:
6299 scanner.token += ".trk"
6301 fp = open(scanner.token, "wb")
6303 prout(_("Can't freeze game as file %s") % scanner.token)
6305 pickle.dump(game, fp)
6311 "Retrieve saved game."
6314 key = scanner.nexttok()
6316 proutn(_("File name: "))
6317 key = scanner.nexttok()
6318 if key != "IHALPHA":
6321 if "." not in scanner.token:
6322 scanner.token += ".trk"
6324 fp = open(scanner.token, "rb")
6326 prout(_("Can't thaw game in %s") % scanner.token)
6328 game = pickle.load(fp)
6334 # I used <http://www.memory-alpha.org> to find planets
6335 # with references in ST:TOS. Earth and the Alpha Centauri
6336 # Colony have been omitted.
6338 # Some planets marked Class G and P here will be displayed as class M
6339 # because of the way planets are generated. This is a known bug.
6342 _("Andoria (Fesoan)"), # several episodes
6343 _("Tellar Prime (Miracht)"), # TOS: "Journey to Babel"
6344 _("Vulcan (T'Khasi)"), # many episodes
6345 _("Medusa"), # TOS: "Is There in Truth No Beauty?"
6346 _("Argelius II (Nelphia)"), # TOS: "Wolf in the Fold" ("IV" in BSD)
6347 _("Ardana"), # TOS: "The Cloud Minders"
6348 _("Catulla (Cendo-Prae)"), # TOS: "The Way to Eden"
6349 _("Gideon"), # TOS: "The Mark of Gideon"
6350 _("Aldebaran III"), # TOS: "The Deadly Years"
6351 _("Alpha Majoris I"), # TOS: "Wolf in the Fold"
6352 _("Altair IV"), # TOS: "Amok Time
6353 _("Ariannus"), # TOS: "Let That Be Your Last Battlefield"
6354 _("Benecia"), # TOS: "The Conscience of the King"
6355 _("Beta Niobe I (Sarpeidon)"), # TOS: "All Our Yesterdays"
6356 _("Alpha Carinae II"), # TOS: "The Ultimate Computer"
6357 _("Capella IV (Kohath)"), # TOS: "Friday's Child" (Class G)
6358 _("Daran V"), # TOS: "For the World is Hollow and I Have Touched the Sky"
6359 _("Deneb II"), # TOS: "Wolf in the Fold" ("IV" in BSD)
6360 _("Eminiar VII"), # TOS: "A Taste of Armageddon"
6361 _("Gamma Canaris IV"), # TOS: "Metamorphosis"
6362 _("Gamma Tranguli VI (Vaalel)"), # TOS: "The Apple"
6363 _("Ingraham B"), # TOS: "Operation: Annihilate"
6364 _("Janus IV"), # TOS: "The Devil in the Dark"
6365 _("Makus III"), # TOS: "The Galileo Seven"
6366 _("Marcos XII"), # TOS: "And the Children Shall Lead",
6367 _("Omega IV"), # TOS: "The Omega Glory"
6368 _("Regulus V"), # TOS: "Amok Time
6369 _("Deneva"), # TOS: "Operation -- Annihilate!"
6370 # Worlds from BSD Trek
6371 _("Rigel II"), # TOS: "Shore Leave" ("III" in BSD)
6372 _("Beta III"), # TOS: "The Return of the Archons"
6373 _("Triacus"), # TOS: "And the Children Shall Lead",
6374 _("Exo III"), # TOS: "What Are Little Girls Made Of?" (Class P)
6376 # _("Hansen's Planet"), # TOS: "The Galileo Seven"
6377 # _("Taurus IV"), # TOS: "The Galileo Seven" (class G)
6378 # _("Antos IV (Doraphane)"), # TOS: "Whom Gods Destroy", "Who Mourns for Adonais?"
6379 # _("Izar"), # TOS: "Whom Gods Destroy"
6380 # _("Tiburon"), # TOS: "The Way to Eden"
6381 # _("Merak II"), # TOS: "The Cloud Minders"
6382 # _("Coridan (Desotriana)"), # TOS: "Journey to Babel"
6383 # _("Iotia"), # TOS: "A Piece of the Action"
6393 _("Impulse Engines"),
6395 _("Subspace Radio"),
6398 _("Navigation System"),
6400 _("Shield Control"),
6403 _("Cloaking Device"),
6408 "Prepare to play, set up cosmos."
6410 # Decide how many of everything
6412 return # frozen game
6413 # Prepare the Enterprise
6414 game.alldone = game.gamewon = game.shldchg = game.shldup = False
6416 game.state.crew = FULLCREW
6417 game.energy = game.inenrg = 5000.0
6418 game.shield = game.inshld = 2500.0
6421 game.quadrant = randplace(GALSIZE)
6422 game.sector = randplace(QUADSIZE)
6423 game.torps = game.intorps = 10
6424 game.nprobes = rnd.integer(2, 5)
6426 for i in range(NDEVICES):
6427 game.damage[i] = 0.0
6428 # Set up assorted game parameters
6429 game.battle = Coord()
6430 game.state.date = game.indate = 100.0 * rnd.real(20, 51)
6431 game.nkinks = game.nhelp = game.casual = game.abandoned = 0
6432 game.iscate = game.resting = game.imine = game.icrystl = game.icraft = False
6433 game.isatb = game.state.nplankl = 0
6434 game.state.starkl = game.state.basekl = game.state.nworldkl = 0
6435 game.iscraft = "onship"
6440 game.state.galaxy = fill2d(GALSIZE, lambda i_unused, j_unused: Quadrant())
6442 game.state.chart = fill2d(GALSIZE, lambda i_unused, j_unused: Page())
6444 game.state.planets = [] # Planet information
6445 game.state.baseq = [] # Base quadrant coordinates
6446 game.state.kcmdr = [] # Commander quadrant coordinates
6447 game.statekscmdr = Coord() # Supercommander quadrant coordinates
6449 # Starchart is functional but we've never seen it
6450 game.lastchart = FOREVER
6451 # Put stars in the galaxy
6453 for i in range(GALSIZE):
6454 for j in range(GALSIZE):
6455 # Can't have more stars per quadrant than fit in one decimal digit,
6456 # if we do the chart representation will break.
6457 k = rnd.integer(1, min(10, QUADSIZE ** 2 / 10))
6459 game.state.galaxy[i][j].stars = k
6460 # Locate star bases in galaxy
6462 prout("=== Allocating %d bases" % game.inbase) # pragma: no cover
6463 for i in range(game.inbase):
6466 w = randplace(GALSIZE)
6467 if not game.state.galaxy[w.i][w.j].starbase:
6470 # C version: for (j = i-1; j > 0; j--)
6471 # so it did them in the opposite order.
6472 for j in range(1, i):
6473 # Improved placement algorithm to spread out bases
6474 distq = (w - game.state.baseq[j]).distance()
6475 if distq < 6.0 * (BASEMAX + 1 - game.inbase) and rnd.withprob(0.75):
6479 "=== Abandoning base #%d at %s" % (i, w)
6480 ) # pragma: no cover
6482 elif distq < 6.0 * (BASEMAX + 1 - game.inbase):
6485 "=== Saving base #%d, close to #%d" % (i, j)
6486 ) # pragma: no cover
6490 prout("=== Placing base #%d in quadrant %s" % (i, w)) # pragma: no cover
6491 game.state.baseq.append(w)
6492 game.state.galaxy[w.i][w.j].starbase = game.state.chart[w.i][
6495 # Position ordinary Klingon Battle Cruisers
6497 klumper = 0.25 * game.skill * (9.0 - game.length) + 1.0
6498 klumper = min(klumper, MAXKLQUAD)
6501 klump = int((1.0 - r * r) * klumper)
6502 klump = min(klump, krem)
6505 w = randplace(GALSIZE)
6507 not game.state.galaxy[w.i][w.j].supernova
6508 and game.state.galaxy[w.i][w.j].klingons + klump <= MAXKLQUAD
6511 game.state.galaxy[w.i][w.j].klingons += klump
6514 # Position Klingon Commander Ships
6515 for i in range(game.incom):
6517 w = randplace(GALSIZE)
6518 if not welcoming(w) or w in game.state.kcmdr:
6520 if game.state.galaxy[w.i][w.j].klingons or rnd.withprob(0.25):
6522 game.state.galaxy[w.i][w.j].klingons += 1
6523 game.state.kcmdr.append(w)
6524 # Locate planets in galaxy
6525 for i in range(game.inplan):
6527 w = randplace(GALSIZE)
6528 if game.state.galaxy[w.i][w.j].planet is None:
6532 new.crystals = "absent"
6533 if (game.options & OPTION_WORLDS) and i < NINHAB:
6534 new.pclass = "M" # All inhabited planets are class M
6535 new.crystals = "absent"
6537 new.name = systnames[i]
6538 new.inhabited = True
6540 new.pclass = ("M", "N", "O")[rnd.integer(0, 3)]
6541 if rnd.withprob(0.33):
6542 new.crystals = "present"
6543 new.known = "unknown"
6544 new.inhabited = False
6545 game.state.galaxy[w.i][w.j].planet = new
6546 game.state.planets.append(new)
6548 for i in range(game.state.nromrem):
6549 w = randplace(GALSIZE)
6550 game.state.galaxy[w.i][w.j].romulans += 1
6551 # Place the Super-Commander if needed
6552 if game.state.nscrem > 0:
6554 w = randplace(GALSIZE)
6557 game.state.kscmdr = w
6558 game.state.galaxy[w.i][w.j].klingons += 1
6559 # Initialize times for extraneous events
6560 schedule(FSNOVA, expran(0.5 * game.intime))
6561 schedule(FTBEAM, expran(1.5 * (game.intime / len(game.state.kcmdr))))
6562 schedule(FSNAP, rnd.real(1.0, 2.0)) # Force an early snapshot
6563 schedule(FBATTAK, expran(0.3 * game.intime))
6565 if game.state.nscrem:
6566 schedule(FSCMOVE, 0.2777)
6571 if (game.options & OPTION_WORLDS) and game.skill >= SKILL_GOOD:
6572 schedule(FDISTR, expran(1.0 + game.intime))
6577 # Place thing (in tournament game, we don't want one!)
6578 # New in SST2K: never place the Thing near a starbase.
6579 # This makes sense and avoids a special case in the old code.
6580 if game.tourn is None:
6581 # Avoid distrubing the RNG chain. This code
6582 # was a late fix and we don't want to mess up
6583 # all the regression tests.
6584 state = randomizer.getrngstate()
6586 thing.location = randplace(GALSIZE)
6587 # Put it somewhere a starbase is not
6588 if thing.location not in game.state.baseq:
6590 randomizer.setrngstate(state)
6592 game.state.snap = False
6593 if game.skill == SKILL_NOVICE:
6595 _("It is stardate %d. The Federation is being attacked by")
6596 % int(game.state.date)
6598 prout(_("a deadly Klingon invasion force. As captain of the United"))
6599 prout(_("Starship U.S.S. Enterprise, it is your mission to seek out"))
6601 _("and destroy this invasion force of %d battle cruisers.")
6602 % ((game.inkling + game.incom + game.inscom))
6605 _("You have an initial allotment of %d stardates to complete")
6608 prout(_("your mission. As you proceed you may be given more time."))
6610 prout(_("You will have %d supporting starbases.") % (game.inbase))
6611 proutn(_("Starbase locations- "))
6613 prout(_("Stardate %d.") % int(game.state.date))
6615 prout(_("%d Klingons.") % (game.inkling + game.incom + game.inscom))
6616 prout(_("An unknown number of Romulans."))
6617 if game.state.nscrem:
6618 prout(_("And one (GULP) Super-Commander."))
6619 prout(_("%d stardates.") % int(game.intime))
6620 proutn(_("%d starbases in ") % game.inbase)
6621 for i in range(game.inbase):
6622 proutn(repr(game.state.baseq[i]))
6625 proutn(_("The Enterprise is currently in Quadrant %s") % game.quadrant)
6626 proutn(_(" Sector %s") % game.sector)
6628 prout(_("Good Luck!"))
6629 if game.state.nscrem:
6630 prout(_(" YOU'LL NEED IT."))
6633 setwnd(message_window)
6637 - (thing.location == game.quadrant)
6638 - (game.tholian is not None)
6641 if game.neutz: # bad luck to start in a Romulan Neutral Zone
6642 attack(torps_ok=False)
6646 "Choose your game type."
6650 game.skill = SKILL_NONE
6654 gametype is None or game.length == 0 or game.skill == SKILL_NONE or wayback == 0
6656 eol_is_fancy = False
6658 not scanner.inqueue or scanner.token == "IHEOL"
6659 ): # Can start with command line options
6660 if gametype is None:
6661 proutn(_("Would you like a regular, tournament, or saved game? "))
6662 elif game.length == 0:
6663 proutn(_("Would you like a Short, Medium, or Long game? "))
6664 elif game.skill == SKILL_NONE:
6665 proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
6667 proutn(_("Wayback setting (press enter for current year): "))
6671 prout("-- Token: %s=%s" % (scanner.type, repr(scanner.token)))
6672 if scanner.token == "":
6673 raise SystemExit(0) # Early end of replay
6674 if scanner.token.startswith("r"): # regular
6675 gametype = "regular"
6676 elif scanner.token.startswith("t"):
6677 gametype = "tournament"
6678 proutn(_("Type in tournament number-"))
6680 while scanner.nexttok() == "IHEOL":
6681 if scanner.real == 0:
6683 continue # We don't want a blank entry
6684 game.tourn = int(round(scanner.real))
6685 rnd.seed(scanner.real)
6687 logfp.write("# rnd.seed(%d)\n" % scanner.real)
6688 elif scanner.token.startswith("sa") or scanner.token.startswith(
6690 ): # saved or frozen
6695 if game.passwd is None:
6697 if not game.alldone:
6698 game.thawed = True # No plaque if not finished
6702 elif scanner.token.startswith("s"): # short
6704 elif scanner.token.startswith("m"): # medium
6706 elif scanner.token.startswith("l"): # long
6708 elif scanner.token.startswith("n"): # novice
6709 game.skill = SKILL_NOVICE
6710 elif (game.skill is None) and scanner.token.startswith("f"): # fair
6711 game.skill = SKILL_FAIR
6712 elif scanner.token.startswith("g"): # good
6713 game.skill = SKILL_GOOD
6714 elif scanner.token.startswith("e"): # expert
6715 game.skill = SKILL_EXPERT
6716 elif scanner.token.startswith("em"): # emeritus
6717 game.skill = SKILL_EMERITUS
6718 elif scanner.type == "IHREAL":
6719 wayback = scanner.int()
6720 elif eol_is_fancy and scanner.token.startswith("\n"):
6721 wayback = time.localtime().tm_year
6722 elif scanner.token.startswith("\n"):
6724 elif scanner.token.startswith("idebug"):
6727 # Unrecognized token
6728 prout(_("Can't interpret %s") % repr(scanner.token))
6729 for (__, (option, year)) in option_names.items():
6731 game.options &= ~option
6733 if game.passwd == "debug": # pragma: no cover
6735 prout("=== Debug mode enabled.")
6738 "--- Setup: type=%s length=%s skill=%s wayback=%s"
6739 % (gametype, game.length, game.skill, wayback)
6741 # Use parameters to generate initial values of things
6742 game.damfac = 0.5 * game.skill
6743 game.inbase = rnd.integer(BASEMIN, BASEMAX + 1)
6745 if game.options & OPTION_PLANETS:
6746 game.inplan += rnd.integer(MAXUNINHAB / 2, MAXUNINHAB + 1)
6747 if game.options & OPTION_WORLDS:
6748 game.inplan += int(NINHAB)
6749 game.state.nromrem = game.inrom = rnd.integer(2 * game.skill)
6750 game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR) and (
6751 (game.options & OPTION_SUPERCMDR) != 0
6753 game.state.remtime = 7.0 * game.length
6754 game.intime = game.state.remtime
6758 * ((game.skill + 1 - 2 * rnd.real()) * game.skill * 0.1 + 0.15)
6760 game.incom = min(MINCMDR, int(game.skill + 0.0625 * game.inkling * rnd.real()))
6761 game.state.remres = (game.inkling + 4 * game.incom) * game.intime
6762 game.inresor = game.state.remres
6763 if game.inkling > 50:
6768 def dropin(iquad=None):
6769 "Drop a feature on a random dot in the current quadrant."
6771 w = randplace(QUADSIZE)
6772 if game.quad[w.i][w.j] == ".":
6774 if iquad is not None:
6775 game.quad[w.i][w.j] = iquad
6780 "Update our alert status."
6781 game.condition = "green"
6782 if game.energy < 1000.0:
6783 game.condition = "yellow"
6785 game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons
6786 or game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans
6788 game.condition = "red"
6790 game.condition = "dead"
6794 "Drop new Klingon into current quadrant."
6795 return Enemy("K", loc=dropin(), power=rnd.real(300, 450) + 25.0 * game.skill)
6799 "Sort enemies by distance so 'nearest' is meaningful."
6800 game.enemies.sort(key=lambda x: x.kdist)
6804 "Set up a new state of quadrant, for when we enter or re-enter it."
6807 game.neutz = game.inorbit = game.landed = False
6808 game.ientesc = game.iseenit = game.isviolreported = False
6810 # Create a blank quadrant
6811 game.quad = fill2d(QUADSIZE, lambda i, j: ".")
6813 # Attempt to escape Super-commander, so tbeam back!
6816 q = game.state.galaxy[game.quadrant.i][game.quadrant.j]
6817 # cope with supernova
6820 game.klhere = q.klingons
6821 game.irhere = q.romulans
6823 game.quad[game.sector.i][game.sector.j] = game.ship
6826 # Position ordinary Klingons
6827 for _i in range(game.klhere):
6829 # If we need a commander, promote a Klingon
6830 for cmdr in game.state.kcmdr:
6831 if cmdr == game.quadrant:
6832 e = game.enemies[game.klhere - 1]
6833 game.quad[e.location.i][e.location.j] = "C"
6834 e.power = rnd.real(950, 1350) + 50.0 * game.skill
6836 # If we need a super-commander, promote a Klingon
6837 if game.quadrant == game.state.kscmdr:
6839 game.quad[e.location.i][e.location.j] = "S"
6840 e.power = rnd.real(1175.0, 1575.0) + 125.0 * game.skill
6841 game.iscate = game.remkl() > 1
6842 # Put in Romulans if needed
6843 for _i in range(q.romulans):
6844 Enemy("R", loc=dropin(), power=rnd.real(400.0, 850.0) + 50.0 * game.skill)
6845 # If quadrant needs a starbase, put it in
6847 game.base = dropin("B")
6848 # If quadrant needs a planet, put it in
6850 game.iplnet = q.planet
6851 if not q.planet.inhabited:
6852 game.plnet = dropin("P")
6854 game.plnet = dropin("@")
6855 # Check for condition
6858 if game.irhere > 0 and game.klhere == 0:
6860 if not damaged(DRADIO):
6862 prout(_('LT. Uhura- "Captain, an urgent message.'))
6863 prout(_(" I'll put it on audio.\" CLICK"))
6865 prout(_("INTRUDER! YOU HAVE VIOLATED THE ROMULAN NEUTRAL ZONE."))
6866 prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
6867 # Put in THING if needed
6868 if thing.location == game.quadrant:
6870 etype="?", loc=dropin(), power=rnd.real(6000, 6500.0) + 250.0 * game.skill
6872 if not damaged(DSRSENS):
6874 prout(_('Mr. Spock- "Captain, this is most unusual.'))
6875 prout(_(' Please examine your short-range scan."'))
6876 # Decide if quadrant needs a Tholian; lighten up if skill is low
6877 if game.options & OPTION_THOLIAN:
6879 (game.skill < SKILL_GOOD and rnd.withprob(0.02))
6880 or (game.skill == SKILL_GOOD and rnd.withprob(0.05))
6881 or (game.skill > SKILL_GOOD and rnd.withprob(0.08))
6885 w.i = rnd.withprob(0.5) * (QUADSIZE - 1)
6886 w.j = rnd.withprob(0.5) * (QUADSIZE - 1)
6887 if game.quad[w.i][w.j] == ".":
6889 game.tholian = Enemy(
6890 etype="T", loc=w, power=rnd.integer(100, 500) + 25.0 * game.skill
6892 # Reserve unoccupied corners
6893 if game.quad[0][0] == ".":
6894 game.quad[0][0] = "X"
6895 if game.quad[0][QUADSIZE - 1] == ".":
6896 game.quad[0][QUADSIZE - 1] = "X"
6897 if game.quad[QUADSIZE - 1][0] == ".":
6898 game.quad[QUADSIZE - 1][0] = "X"
6899 if game.quad[QUADSIZE - 1][QUADSIZE - 1] == ".":
6900 game.quad[QUADSIZE - 1][QUADSIZE - 1] = "X"
6902 # And finally the stars
6903 for _i in range(q.stars):
6905 # Put in a few black holes
6906 for _i in range(1, 3 + 1):
6907 if rnd.withprob(0.5):
6909 # Take out X's in corners if Tholian present
6911 if game.quad[0][0] == "X":
6912 game.quad[0][0] = "."
6913 if game.quad[0][QUADSIZE - 1] == "X":
6914 game.quad[0][QUADSIZE - 1] = "."
6915 if game.quad[QUADSIZE - 1][0] == "X":
6916 game.quad[QUADSIZE - 1][0] = "."
6917 if game.quad[QUADSIZE - 1][QUADSIZE - 1] == "X":
6918 game.quad[QUADSIZE - 1][QUADSIZE - 1] = "."
6919 # This should guarantee that replay games don't lose info about the chart
6920 if (game.options & OPTION_AUTOSCAN) or replayfp:
6925 "Set the self-destruct password."
6926 if game.options & OPTION_AUTOPASS:
6928 game.passwd += chr(ord("a") + rnd.integer(26))
6929 game.passwd += chr(ord("a") + rnd.integer(26))
6930 game.passwd += chr(ord("a") + rnd.integer(26))
6934 proutn(_("Please type in a secret password- "))
6936 game.passwd = scanner.token
6937 # game.passwd = getpass.getpass("Please type in a secret password- ")
6938 if game.passwd is not None:
6942 # Code from sst.c begins here
6945 ("SRSCAN", OPTION_TTY),
6946 ("STATUS", OPTION_TTY),
6947 ("REQUEST", OPTION_TTY),
6948 ("LRSCAN", OPTION_TTY),
6960 ("SENSORS", OPTION_PLANETS),
6961 ("ORBIT", OPTION_PLANETS),
6962 ("TRANSPORT", OPTION_PLANETS),
6963 ("MINE", OPTION_PLANETS),
6964 ("CRYSTALS", OPTION_PLANETS),
6965 ("SHUTTLE", OPTION_PLANETS),
6966 ("PLANETS", OPTION_PLANETS),
6971 ("PROBE", OPTION_PROBE),
6973 ("FREEZE", 0), # Synonym for SAVE
6976 # No abbreviations accepted after this point
6979 ("CAPTURE", OPTION_CAPTURE),
6980 ("CLOAK", OPTION_CLOAK),
6983 ("SOS", 0), # Synonym for MAYDAY
6984 ("CALL", 0), # Synonym for MAYDAY
6993 def listCommands(): # pragma: no cover
6994 "Generate a list of legal commands."
6995 # Coverage-disabled because testing for this is fragile
6996 # in the presence of changes in the command set.
6997 prout(_("LEGAL COMMANDS ARE:"))
6999 for (key, opt) in commands:
7000 if not opt or (opt & game.options):
7001 proutn("%-12s " % key)
7003 if emitted % 5 == 4:
7009 "Browse on-line help."
7010 key = scanner.nexttok()
7013 setwnd(prompt_window)
7014 proutn(_("Help on what command? "))
7015 key = scanner.nexttok()
7016 setwnd(message_window)
7019 cmds = [x[0] for x in commands]
7020 if scanner.token.upper() in cmds or scanner.token.upper() == "ABBREV":
7027 cmd = scanner.token.upper()
7028 for directory in docpath:
7030 fp = open(os.path.join(directory, "sst.doc"), "r")
7035 prout(_('Spock- "Captain, that information is missing from the'))
7036 prout(_(" computer. You need to find sst.doc and put it somewhere"))
7037 proutn(_(" in these directories: %s") % ":".join(docpath))
7039 # This used to continue: "You need to find SST.DOC and put
7040 # it in the current directory."
7043 linebuf = fp.readline()
7045 prout(_('Spock- "Captain, there is no information on that command."'))
7048 if linebuf[0] == "%" and linebuf[1] == "%" and linebuf[2] == " ":
7049 linebuf = linebuf[3:].strip()
7050 if cmd.upper() == linebuf:
7053 prout(_('Spock- "Captain, I\'ve found the following information:"'))
7056 linebuf = fp.readline()
7057 if "******" in linebuf:
7064 "Command-interpretation loop."
7069 and game.state.date >= ALGERON
7070 and not game.isviolreported
7074 _("The Romulan ship discovers you are breaking the Treaty of Algeron!")
7077 game.isviolreported = True
7079 while True: # command loop
7081 while True: # get a command
7083 game.optime = game.justin = False
7085 setwnd(prompt_window)
7088 if scanner.nexttok() == "IHEOL":
7089 if game.options & OPTION_CURSES: # pragma: no cover
7092 elif scanner.token == "":
7096 setwnd(message_window)
7098 abandon_passed = False
7099 cmd = "" # Force cmd to persist after loop
7100 opt = 0 # Force opt to persist after loop
7101 for (cmd, opt) in commands:
7102 # commands after ABANDON cannot be abbreviated
7103 if cmd == "ABANDON":
7104 abandon_passed = True
7105 if cmd == scanner.token.upper() or (
7106 not abandon_passed and cmd.startswith(scanner.token.upper())
7109 if cmd == "": # pragma: no cover
7112 elif opt and not (opt & game.options):
7116 if game.options & OPTION_CURSES: # pragma: no cover
7117 prout("COMMAND> %s" % cmd)
7118 if cmd == "SRSCAN": # srscan
7120 elif cmd == "STATUS": # status
7122 elif cmd == "REQUEST": # status request
7124 elif cmd == "LRSCAN": # long range scan
7125 lrscan(silent=False)
7126 elif cmd == "PHASERS": # phasers
7131 elif cmd in ("TORPEDO", "PHOTONS"): # photon torpedos
7136 elif cmd == "MOVE": # move under warp
7137 warp(wcourse=None, involuntary=False)
7138 elif cmd == "SHIELDS": # shields
7139 doshield(shraise=False)
7142 game.shldchg = False
7143 elif cmd == "DOCK": # dock at starbase
7146 attack(torps_ok=False)
7147 elif cmd == "DAMAGES": # damage reports
7149 elif cmd == "CHART": # chart
7151 elif cmd == "IMPULSE": # impulse
7153 elif cmd == "REST": # rest
7157 elif cmd == "WARP": # warp
7159 elif cmd == "SENSORS": # sensors
7161 elif cmd == "ORBIT": # orbit
7165 elif cmd == "TRANSPORT": # transport "beam"
7167 elif cmd == "MINE": # mine
7171 elif cmd == "CRYSTALS": # crystals
7175 elif cmd == "SHUTTLE": # shuttle
7179 elif cmd == "PLANETS": # Planet list
7181 elif cmd == "REPORT": # Game Report
7183 elif cmd == "COMPUTER": # use COMPUTER!
7185 elif cmd == "COMMANDS":
7187 elif cmd == "EMEXIT": # Emergency exit
7188 clrscr() # Hide screen
7189 freeze(True) # forced save
7190 raise SystemExit(1) # And quick exit
7191 elif cmd == "PROBE":
7192 probe() # Launch probe
7195 elif cmd == "ABANDON": # Abandon Ship
7197 elif cmd == "DESTRUCT": # Self Destruct
7199 elif cmd == "SAVE": # Save Game
7202 if game.skill > SKILL_GOOD:
7203 prout(_("WARNING--Saved games produce no plaques!"))
7204 elif cmd == "DEATHRAY": # Try a desparation measure
7208 elif cmd == "CAPTURE":
7210 elif cmd == "CLOAK":
7212 elif cmd == "DEBUGCMD": # What do we want for debug???
7214 elif cmd == "MAYDAY": # Call for help
7219 game.alldone = True # quit the game
7222 elif cmd == "SCORE":
7223 score() # see current score
7224 elif cmd == "CURSES": # pragma: no cover
7225 game.options |= OPTION_CURSES | OPTION_COLOR
7227 elif cmd == "OPTIONS":
7231 break # Game has ended
7232 if game.optime != 0.0:
7235 break # Events did us in
7236 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
7239 if hitme and not game.justin:
7240 attack(torps_ok=True)
7243 if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
7251 prout("=== Ending") # pragma: no cover
7255 "Emit the name of an enemy or feature."
7263 s = _("Super-commander")
7275 s = _("Tholian web")
7279 s = _("Inhabited World")
7281 s = "Unknown??" # pragma: no cover
7285 def crmena(loud, enemy, loctype, w):
7286 "Emit the name of an enemy and his location."
7290 buf += cramen(enemy) + _(" at ")
7291 if loctype == "quadrant":
7292 buf += _("Quadrant ")
7293 elif loctype == "sector":
7295 return buf + repr(w)
7299 "Emit our ship name."
7300 return {"E": _("Enterprise"), "F": _("Faerie Queene")}.get(game.ship, "Ship???")
7304 "Emit a line of stars"
7305 prouts("******************************************************")
7310 return -avrage * math.log(1e-7 + rnd.real())
7313 def randplace(size):
7314 "Choose a random location."
7316 w.i = rnd.integer(size)
7317 w.j = rnd.integer(size)
7329 # Get a token from the user
7332 # Fill the token queue if nothing here
7333 while not self.inqueue:
7335 if curwnd == prompt_window:
7337 setwnd(message_window)
7344 self.inqueue = sline.lstrip().split() + ["\n"]
7345 # From here on in it's all looking at the queue
7346 self.token = self.inqueue.pop(0)
7347 if self.token == "\n":
7351 self.real = float(self.token)
7352 self.type = "IHREAL"
7357 self.token = self.token.lower()
7358 self.type = "IHALPHA"
7362 def append(self, tok):
7363 self.inqueue.append(tok)
7365 def push(self, tok):
7366 self.inqueue.insert(0, tok)
7372 # Demand input for next scan
7374 self.real = self.token = None
7377 # compares s to item and returns true if it matches to the length of s
7378 return s.startswith(self.token)
7381 # Round token value to nearest integer
7382 return int(round(self.real))
7387 if game.options & OPTION_ALPHAMERIC:
7390 (self.type == "IHALPHA")
7391 and (self.token[0] in "abcdefghij")
7392 and (self.token[1] in "0123456789")
7394 s.i = ord(self.token[0]) - ord("a")
7395 s.j = int(self.token[1:]) - 1
7397 except (TypeError, IndexError):
7400 if self.type != "IHREAL":
7403 s.i = self.int() - 1
7405 if self.type != "IHREAL":
7408 s.j = self.int() - 1
7411 def __repr__(self): # pragma: no cover
7412 return "<sstcanner: token=%s, type=%s, queue=%s>" % (
7420 "Yes-or-no confirmation."
7424 if scanner.token == "y":
7426 if scanner.token == "n":
7429 proutn(_('Please answer with "y" or "n": '))
7433 "Complain about unparseable input."
7436 prout(_("Beg your pardon, Captain?"))
7439 def debugme(): # pragma: no cover
7440 "Access to the internals for debugging."
7441 proutn("Reset levels? ")
7443 if game.energy < game.inenrg:
7444 game.energy = game.inenrg
7445 game.shield = game.inshld
7446 game.torps = game.intorps
7447 game.lsupres = game.inlsr
7448 proutn("Reset damage? ")
7450 for i in range(NDEVICES):
7451 if game.damage[i] > 0.0:
7452 game.damage[i] = 0.0
7453 proutn("Toggle debug flag? ")
7455 game.idebug = not game.idebug
7456 if game.idebug: # pragma: no cover
7457 prout("Debug output ON")
7459 prout("Debug output OFF")
7460 proutn("Cause selective damage? ")
7462 for i in range(NDEVICES):
7463 proutn("Kill %s?" % device[i])
7465 key = scanner.nexttok()
7466 if key == "IHALPHA" and scanner.sees("y"):
7467 game.damage[i] = 10.0
7468 proutn("Examine/change events? ")
7473 FSNOVA: "Supernova ",
7476 FBATTAK: "Base Attack ",
7477 FCDBAS: "Base Destroy ",
7478 FSCMOVE: "SC Move ",
7479 FSCDBAS: "SC Base Destroy ",
7480 FDSPROB: "Probe Move ",
7481 FDISTR: "Distress Call ",
7482 FENSLV: "Enslavement ",
7483 FREPRO: "Klingon Build ",
7485 for i in range(1, NEVENTS):
7488 proutn("%.2f" % (scheduled(i) - game.state.date))
7489 if i in {FENSLV, FREPRO}:
7491 proutn(" in %s" % ev.quadrant)
7496 key = scanner.nexttok()
7500 elif key == "IHREAL":
7501 ev = schedule(i, scanner.real)
7502 if i in {FENSLV, FREPRO}:
7504 proutn("In quadrant- ")
7505 key = scanner.nexttok()
7506 # "IHEOL" says to leave coordinates as they are
7509 prout("Event %d canceled, no x coordinate." % (i))
7512 w.i = int(round(scanner.real))
7513 key = scanner.nexttok()
7515 prout("Event %d canceled, no y coordinate." % (i))
7518 w.j = int(round(scanner.real))
7521 proutn("Induce supernova here? ")
7523 game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova = True
7527 if __name__ == "__main__":
7529 # global line, thing, game
7535 game.options = OPTION_ALL & ~OPTION_IOMODES
7536 if os.getenv("TERM"):
7537 game.options |= OPTION_CURSES # pragma: no cover
7539 game.options |= OPTION_TTY
7540 seed = int(time.time())
7542 (options, arguments) = getopt.getopt(sys.argv[1:], "cr:s:txV")
7543 for (switch, val) in options:
7545 # pylint: disable=raise-missing-from
7547 replayfp = open(val, "r")
7549 sys.stderr.write("sst: can't open replay file %s\n" % val)
7551 # pylint: disable=raise-missing-from
7554 line = replayfp.readline().strip()
7558 if line.startswith("# seed"):
7559 (__, __, seed) = line.split()
7560 # pylint: disable=eval-used
7562 elif line.startswith("# arguments"):
7563 arguments += line.split()[2:]
7564 except ValueError: # pragma: no cover
7565 sys.stderr.write("sst: replay file %s is ill-formed\n" % val)
7567 game.options |= OPTION_TTY
7568 game.options &= ~OPTION_CURSES
7569 elif switch == "-s": # pragma: no cover
7571 elif switch == "-t": # pragma: no cover
7572 game.options |= OPTION_TTY
7573 game.options &= ~OPTION_CURSES
7574 elif switch == "-x": # pragma: no cover
7576 elif switch == "-c": # Enable curses debugging - undocumented
7578 elif switch == "-V": # pragma: no cover
7579 print("SST2K", version)
7581 else: # pragma: no cover
7582 sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n")
7584 except getopt.GetoptError as err:
7586 raise SystemExit(1) from err
7587 # where to save the input in case of bugs
7588 if "TMPDIR" in os.environ: # pragma: no cover
7589 tmpdir = os.environ["TMPDIR"]
7593 logfp = open(os.path.join(tmpdir, "sst-input.log"), "w")
7594 except IOError: # pragma: no cover
7595 sys.stderr.write("sst: warning, can't open logfile\n")
7598 logfp.write("# seed %s\n" % seed)
7599 logfp.write("# arguments %s\n" % " ".join(arguments))
7600 logfp.write("# SST2K version %s\n" % version)
7602 "# recorded by %s@%s on %s\n"
7603 % (getpass.getuser(), socket.getfqdn(), time.ctime())
7607 scanner = sstscanner()
7608 for arg in arguments:
7612 while True: # Play a game
7613 setwnd(fullscreen_window)
7619 game.alldone = False
7625 if game.options & OPTION_TTY:
7628 if game.tourn and game.alldone:
7629 proutn(_("Do you want your score recorded?"))
7635 if game.options & OPTION_TTY:
7636 proutn(_("Do you want to play again? "))
7642 prout(_("May the Great Bird of the Galaxy roost upon your home planet."))
7646 except KeyboardInterrupt: # pragma: no cover