sst2k.py

#!/usr/bin/env python3
"""
sst.py -- Super Star Trek 2K
 
SST2K is a Python translation of a C translation of a FORTRAN
original dating back to 1973.  Beautiful Python it is not, but it
works.  Translation by Eric S. Raymond; original game by David Matuszek
and Paul Reynolds, with modifications by Don Smith, Tom Almy,
Stas Sergeev, and Eric S. Raymond.
 
See the doc/HACKING file in the distribution for designers notes and advice
on how to modify (and how not to modify!) this code.
"""
import os, sys, math, curses, time, readline, pickle, random, copy, gettext, getpass
 
version = "2.1"
 
docpath  	= (".", "../doc", "/usr/share/doc/sst")
 
def _(st):
    return gettext.gettext(st)
 
GALSIZE 	= 8		# Galaxy size in quadrants
NINHAB  	= (GALSIZE * GALSIZE // 2)	# Number of inhabited worlds
MAXUNINHAB	= 10		# Maximum uninhabited worlds
QUADSIZE	= 10		# Quadrant size in sectors
BASEMIN		= 2				# Minimum starbases
BASEMAX 	= (GALSIZE * GALSIZE // 12)	# Maximum starbases
MAXKLGAME	= 127		# Maximum Klingons per game
MAXKLQUAD	= 9		# Maximum Klingons per quadrant
FULLCREW	= 428		# Crew size. BSD Trek was 387, that's wrong
FOREVER 	= 1e30		# Time for the indefinite future
MAXBURST	= 3		# Max # of torps you can launch in one turn
MINCMDR 	= 10		# Minimum number of Klingon commanders
DOCKFAC		= 0.25		# Repair faster when docked
PHASEFAC	= 2.0		# Unclear what this is, it was in the C version
 
DEFAULT      = -1
BLACK        = 0
BLUE         = 1
GREEN        = 2
CYAN         = 3
RED          = 4
MAGENTA      = 5
BROWN        = 6
LIGHTGRAY    = 7
DARKGRAY     = 8
LIGHTBLUE    = 9
LIGHTGREEN   = 10
LIGHTCYAN    = 11
LIGHTRED     = 12
LIGHTMAGENTA = 13
YELLOW       = 14
WHITE        = 15
 
class TrekError(Exception):
    pass
 
class JumpOut(Exception):
    pass
 
class Coord:
    def __init__(self, x=None, y=None):
        self.i = x
        self.j = y
    def valid_quadrant(self):
        return self.i >= 0 and self.i < GALSIZE and self.j >= 0 and self.j < GALSIZE
    def valid_sector(self):
        return self.i >= 0 and self.i < QUADSIZE and self.j >= 0 and self.j < QUADSIZE
    def invalidate(self):
        self.i = self.j = None
    def is_valid(self):
        return self.i != None and self.j != None
    def __eq__(self, other):
        return other != None and self.i == other.i and self.j == other.j
    def __ne__(self, other):
        return other == None or self.i != other.i or self.j != other.j
    def __add__(self, other):
        return Coord(self.i+other.i, self.j+other.j)
    def __sub__(self, other):
        return Coord(self.i-other.i, self.j-other.j)
    def __mul__(self, other):
        return Coord(self.i*other, self.j*other)
    def __rmul__(self, other):
        return Coord(self.i*other, self.j*other)
    def __div__(self, other):
        return Coord(self.i/other, self.j/other)
    def __mod__(self, other):
        return Coord(self.i % other, self.j % other)
    def __truediv__(self, other):
        return Coord(self.i/other, self.j/other)
    def roundtogrid(self):
        return Coord(int(round(self.i)), int(round(self.j)))
    def distance(self, other=None):
        if not other:
            other = Coord(0, 0)
        return math.sqrt((self.i - other.i)**2 + (self.j - other.j)**2)
    def bearing(self):
        return 1.90985*math.atan2(self.j, self.i)
    def sgn(self):
        s = Coord()
        if self.i == 0:
            s.i = 0
        else:
            s.i = self.i / abs(self.i)
        if self.j == 0:
            s.j = 0
        else:
            s.j = self.j / abs(self.j)
        return s
    def quadrant(self):
        #print "Location %s -> %s" % (self, (self / QUADSIZE).roundtogrid())
        return self.roundtogrid() / QUADSIZE
    def sector(self):
        return self.roundtogrid() % QUADSIZE
    def scatter(self):
        s = Coord()
        s.i = self.i + randrange(-1, 2)
        s.j = self.j + randrange(-1, 2)
        return s
    def __str__(self):
        if self.i == None or self.j == None:
            return "Nowhere"
        return "%s - %s" % (self.i+1, self.j+1)
    __repr__ = __str__
 
class Thingy(Coord):
    "Do not anger the Space Thingy!"
    def __init__(self):
        Coord.__init__(self)
        self.angered = False
    def angry(self):
        self.angered = True
    def at(self, q):
        return (q.i, q.j) == (self.i, self.j)
 
class Planet:
    def __init__(self):
        self.name = None        # string-valued if inhabited
        self.quadrant = Coord()        # quadrant located
        self.pclass = None        # could be ""M", "N", "O", or "destroyed"
        self.crystals = "absent"# could be "mined", "present", "absent"
        self.known = "unknown"        # could be "unknown", "known", "shuttle_down"
        self.inhabited = False        # is it inhabited?
    def __str__(self):
        return self.name
 
class Quadrant:
    def __init__(self):
        self.stars = 0
        self.planet = None
        self.starbase = False
        self.klingons = 0
        self.romulans = 0
        self.supernova = False
        self.charted = False
        self.status = "secure"        # Could be "secure", "distressed", "enslaved"
 
class Page:
    def __init__(self):
        self.stars = None
        self.starbase = False
        self.klingons = None
    def __repr__(self):
        return "<%s,%s,%s>" % (self.klingons, self.starbase, self.stars)
 
def fill2d(size, fillfun):
    "Fill an empty list in 2D."
    lst = []
    for i in range(size):
        lst.append([])
        for j in range(size):
            lst[i].append(fillfun(i, j))
    return lst
 
class Snapshot:
    def __init__(self):
        self.snap = False        # snapshot taken
        self.crew = 0           # crew complement
        self.remkl = 0          # remaining klingons
        self.nscrem = 0                # remaining super commanders
        self.starkl = 0         # destroyed stars
        self.basekl = 0         # destroyed bases
        self.nromrem = 0        # Romulans remaining
        self.nplankl = 0        # destroyed uninhabited planets
        self.nworldkl = 0        # destroyed inhabited planets
        self.planets = []        # Planet information
        self.date = 0.0           # stardate
        self.remres = 0         # remaining resources
        self.remtime = 0        # remaining time
        self.baseq = []         # Base quadrant coordinates
        self.kcmdr = []         # Commander quadrant coordinates
        self.kscmdr = Coord()        # Supercommander quadrant coordinates
        # the galaxy
        self.galaxy = fill2d(GALSIZE, lambda i_unused, j_unused: Quadrant())
        # the starchart
        self.chart = fill2d(GALSIZE, lambda i_unused, j_unused: Page())
 
class Event:
    def __init__(self):
        self.date = None        # A real number
        self.quadrant = None        # A coord structure
 
# game options
OPTION_ALL        = 0xffffffff
OPTION_TTY        = 0x00000001        # old interface
OPTION_CURSES        = 0x00000002        # new interface
OPTION_IOMODES        = 0x00000003        # cover both interfaces
OPTION_PLANETS        = 0x00000004        # planets and mining
OPTION_THOLIAN        = 0x00000008        # Tholians and their webs (UT 1979 version)
OPTION_THINGY        = 0x00000010        # Space Thingy can shoot back (Stas, 2005)
OPTION_PROBE        = 0x00000020        # deep-space probes (DECUS version, 1980)
OPTION_SHOWME        = 0x00000040        # bracket Enterprise in chart
OPTION_RAMMING        = 0x00000080        # enemies may ram Enterprise (Almy)
OPTION_MVBADDY        = 0x00000100        # more enemies can move (Almy)
OPTION_BLKHOLE        = 0x00000200        # black hole may timewarp you (Stas, 2005)
OPTION_BASE        = 0x00000400        # bases have good shields (Stas, 2005)
OPTION_WORLDS        = 0x00000800        # logic for inhabited worlds (ESR, 2006)
OPTION_AUTOSCAN        = 0x00001000        # automatic LRSCAN before CHART (ESR, 2006)
OPTION_PLAIN        = 0x01000000        # user chose plain game
OPTION_ALMY        = 0x02000000        # user chose Almy variant
OPTION_COLOR    = 0x04000000        # enable color display (experimental, ESR, 2010)
 
# Define devices
DSRSENS         = 0
DLRSENS         = 1
DPHASER         = 2
DPHOTON         = 3
DLIFSUP         = 4
DWARPEN         = 5
DIMPULS         = 6
DSHIELD         = 7
DRADIO         = 0
DSHUTTL  = 9
DCOMPTR  = 10
DNAVSYS         = 11
DTRANSP  = 12
DSHCTRL         = 13
DDRAY         = 14
DDSP         = 15
NDEVICES = 16        # Number of devices
 
SKILL_NONE        = 0
SKILL_NOVICE        = 1
SKILL_FAIR        = 2
SKILL_GOOD        = 3
SKILL_EXPERT        = 4
SKILL_EMERITUS        = 5
 
def damaged(dev):
    return (game.damage[dev] != 0.0)
def communicating():
    return not damaged(DRADIO) or game.condition=="docked"
 
# Define future events
FSPY        = 0        # Spy event happens always (no future[] entry)
                # can cause SC to tractor beam Enterprise
FSNOVA  = 1        # Supernova
FTBEAM  = 2        # Commander tractor beams Enterprise
FSNAP   = 3        # Snapshot for time warp
FBATTAK = 4        # Commander attacks base
FCDBAS  = 5        # Commander destroys base
FSCMOVE = 6        # Supercommander moves (might attack base)
FSCDBAS = 7        # Supercommander destroys base
FDSPROB = 8        # Move deep space probe
FDISTR        = 9        # Emit distress call from an inhabited world
FENSLV        = 10        # Inhabited word is enslaved */
FREPRO        = 11        # Klingons build a ship in an enslaved system
NEVENTS        = 12
 
# Abstract out the event handling -- underlying data structures will change
# when we implement stateful events
def findevent(evtype):
    return game.future[evtype]
 
class Enemy:
    def __init__(self, etype=None, loc=None, power=None):
        self.type = etype
        self.location = Coord()
        self.kdist = None
        self.kavgd = None
        if loc:
            self.move(loc)
        self.power = power        # enemy energy level
        game.enemies.append(self)
    def move(self, loc):
        motion = (loc != self.location)
        if self.location.i is not None and self.location.j is not None:
            if motion:
                if self.type == 'T':
                    game.quad[self.location.i][self.location.j] = '#'
                else:
                    game.quad[self.location.i][self.location.j] = '.'
        if loc:
            self.location = copy.copy(loc)
            game.quad[self.location.i][self.location.j] = self.type
            self.kdist = self.kavgd = (game.sector - loc).distance()
        else:
            self.location = Coord()
            self.kdist = self.kavgd = None
            game.enemies.remove(self)
        return motion
    def __repr__(self):
        return "<%s,%s.%f>" % (self.type, self.location, self.power)        # For debugging
 
class Gamestate:
    def __init__(self):
        self.options = None        # Game options
        self.state = Snapshot()        # A snapshot structure
        self.snapsht = Snapshot()        # Last snapshot taken for time-travel purposes
        self.quad = None        # contents of our quadrant
        self.damage = [0.0] * NDEVICES        # damage encountered
        self.future = []        # future events
        i = NEVENTS
        while i > 0:
            i -= 1
            self.future.append(Event())
        self.passwd  = None        # Self Destruct password
        self.enemies = []
        self.quadrant = None        # where we are in the large
        self.sector = None        # where we are in the small
        self.tholian = None        # Tholian enemy object
        self.base = None        # position of base in current quadrant
        self.battle = None        # base coordinates being attacked
        self.plnet = None        # location of planet in quadrant
        self.gamewon = False        # Finished!
        self.ididit = False        # action taken -- allows enemy to attack
        self.alive = False        # we are alive (not killed)
        self.justin = False        # just entered quadrant
        self.shldup = False        # shields are up
        self.shldchg = False        # shield is changing (affects efficiency)
        self.iscate = False        # super commander is here
        self.ientesc = False        # attempted escape from supercommander
        self.resting = False        # rest time
        self.icraft = False        # Kirk in Galileo
        self.landed = False        # party on planet (true), on ship (false)
        self.alldone = False        # game is now finished
        self.neutz = False        # Romulan Neutral Zone
        self.isarmed = False        # probe is armed
        self.inorbit = False        # orbiting a planet
        self.imine = False        # mining
        self.icrystl = False        # dilithium crystals aboard
        self.iseenit = False        # seen base attack report
        self.thawed = False        # thawed game
        self.condition = None        # "green", "yellow", "red", "docked", "dead"
        self.iscraft = None        # "onship", "offship", "removed"
        self.skill = None        # Player skill level
        self.inkling = 0        # initial number of klingons
        self.inbase = 0                # initial number of bases
        self.incom = 0                # initial number of commanders
        self.inscom = 0                # initial number of commanders
        self.inrom = 0                # initial number of commanders
        self.instar = 0                # initial stars
        self.intorps = 0        # initial/max torpedoes
        self.torps = 0                # number of torpedoes
        self.ship = 0                # ship type -- 'E' is Enterprise
        self.abandoned = 0        # count of crew abandoned in space
        self.length = 0                # length of game
        self.klhere = 0                # klingons here
        self.casual = 0                # causalties
        self.nhelp = 0                # calls for help
        self.nkinks = 0                # count of energy-barrier crossings
        self.iplnet = None        # planet # in quadrant
        self.inplan = 0                # initial planets
        self.irhere = 0                # Romulans in quadrant
        self.isatb = 0                # =2 if super commander is attacking base
        self.tourn = None        # tournament number
        self.nprobes = 0        # number of probes available
        self.inresor = 0.0        # initial resources
        self.intime = 0.0        # initial time
        self.inenrg = 0.0        # initial/max energy
        self.inshld = 0.0        # initial/max shield
        self.inlsr = 0.0        # initial life support resources
        self.indate = 0.0        # initial date
        self.energy = 0.0        # energy level
        self.shield = 0.0        # shield level
        self.warpfac = 0.0        # warp speed
        self.lsupres = 0.0        # life support reserves
        self.optime = 0.0        # time taken by current operation
        self.damfac = 0.0        # damage factor
        self.lastchart = 0.0        # time star chart was last updated
        self.cryprob = 0.0        # probability that crystal will work
        self.probe = None        # object holding probe course info
        self.height = 0.0        # height of orbit around planet
        self.score = 0.0        # overall score
        self.perdate = 0.0        # rate of kills
        self.idebug = False        # Debugging instrumentation enabled?
        self.statekscmdr = None # No SuperCommander coordinates yet.
    def recompute(self):
        # Stas thinks this should be (C expression):
        # game.state.remkl + len(game.state.kcmdr) > 0 ?
        #        game.state.remres/(game.state.remkl + 4*len(game.state.kcmdr)) : 99
        # He says the existing expression is prone to divide-by-zero errors
        # after killing the last klingon when score is shown -- perhaps also
        # if the only remaining klingon is SCOM.
        self.state.remtime = self.state.remres/(self.state.remkl + 4*len(self.state.kcmdr))
 
FWON = 0
FDEPLETE = 1
FLIFESUP = 2
FNRG = 3
FBATTLE = 4
FNEG3 = 5
FNOVA = 6
FSNOVAED = 7
FABANDN = 8
FDILITHIUM = 9
FMATERIALIZE = 10
FPHASER = 11
FLOST = 12
FMINING = 13
FDPLANET = 14
FPNOVA = 15
FSSC = 16
FSTRACTOR = 17
FDRAY = 18
FTRIBBLE = 19
FHOLE = 20
FCREW = 21
 
def withprob(p):
    return random.random() < p
 
def randrange(*args):
    return random.randrange(*args)
 
def randreal(*args):
    v = random.random()
    if len(args) == 1:
        v *= args[0]                 # from [0, args[0])
    elif len(args) == 2:
        v = args[0] + v*(args[1]-args[0])        # from [args[0], args[1])
    return v
 
# Code from ai.c begins here
 
def welcoming(iq):
    "Would this quadrant welcome another Klingon?"
    return iq.valid_quadrant() and \
        not game.state.galaxy[iq.i][iq.j].supernova and \
        game.state.galaxy[iq.i][iq.j].klingons < MAXKLQUAD
 
def tryexit(enemy, look, irun):
    "A bad guy attempts to bug out."
    iq = Coord()
    iq.i = game.quadrant.i+(look.i+(QUADSIZE-1))/QUADSIZE - 1
    iq.j = game.quadrant.j+(look.j+(QUADSIZE-1))/QUADSIZE - 1
    if not welcoming(iq):
        return False
    if enemy.type == 'R':
        return False # Romulans cannot escape!
    if not irun:
        # avoid intruding on another commander's territory
        if enemy.type == 'C':
            if iq in game.state.kcmdr:
                return []
            # refuse to leave if currently attacking starbase
            if game.battle == game.quadrant:
                return []
        # don't leave if over 1000 units of energy
        if enemy.power > 1000.0:
            return []
    oldloc = copy.copy(enemy.location)
    # handle local matters related to escape
    enemy.move(None)
    game.klhere -= 1
    if game.condition != "docked":
        newcnd()
    # Handle global matters related to escape
    game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
    game.state.galaxy[iq.i][iq.j].klingons += 1
    if enemy.type == 'S':
        game.iscate = False
        game.ientesc = False
        game.isatb = 0
        schedule(FSCMOVE, 0.2777)
        unschedule(FSCDBAS)
        game.state.kscmdr = iq
    else:
        for cmdr in game.state.kcmdr:
            if cmdr == game.quadrant:
                game.state.kcmdr.append(iq)
                break
    # report move out of quadrant.
    return [(True, enemy, oldloc, ibq)]
 
# The bad-guy movement algorithm:
#
# 1. Enterprise has "force" based on condition of phaser and photon torpedoes.
# If both are operating full strength, force is 1000. If both are damaged,
# force is -1000. Having shields down subtracts an additional 1000.
#
# 2. Enemy has forces equal to the energy of the attacker plus
# 100*(K+R) + 500*(C+S) - 400 for novice through good levels OR
# 346*K + 400*R + 500*(C+S) - 400 for expert and emeritus.
#
# Attacker Initial energy levels (nominal):
# Klingon   Romulan   Commander   Super-Commander
# Novice    400        700        1200
# Fair      425        750        1250
# Good      450        800        1300        1750
# Expert    475        850        1350        1875
# Emeritus  500        900        1400        2000
# VARIANCE   75        200         200         200
#
# Enemy vessels only move prior to their attack. In Novice - Good games
# only commanders move. In Expert games, all enemy vessels move if there
# is a commander present. In Emeritus games all enemy vessels move.
#
# 3. If Enterprise is not docked, an aggressive action is taken if enemy
# forces are 1000 greater than Enterprise.
#
# Agressive action on average cuts the distance between the ship and
# the enemy to 1/4 the original.
#
# 4.  At lower energy advantage, movement units are proportional to the
# advantage with a 650 advantage being to hold ground, 800 to move forward
# 1, 950 for two, 150 for back 4, etc. Variance of 100.
#
# If docked, is reduced by roughly 1.75*game.skill, generally forcing a
# retreat, especially at high skill levels.
#
# 5.  Motion is limited to skill level, except for SC hi-tailing it out.
 
def movebaddy(enemy):
    "Tactical movement for the bad guys."
    goto = Coord()
    look = Coord()
    irun = False
    # This should probably be just (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
    if game.skill >= SKILL_EXPERT:
        nbaddys = (((game.quadrant in game.state.kcmdr)*2 + (game.state.kscmdr==game.quadrant)*2+game.klhere*1.23+game.irhere*1.5)/2.0)
    else:
        nbaddys = (game.quadrant in game.state.kcmdr) + (game.state.kscmdr==game.quadrant)
    old_dist = enemy.kdist
    mdist = int(old_dist + 0.5) # Nearest integer distance
    # If SC, check with spy to see if should hi-tail it
    if enemy.type == 'S' and \
        (enemy.power <= 500.0 or (game.condition=="docked" and not damaged(DPHOTON))):
        irun = True
        motion = -QUADSIZE
    else:
        # decide whether to advance, retreat, or hold position
        forces = enemy.power+100.0*len(game.enemies)+400*(nbaddys-1)
        if not game.shldup:
            forces += 1000 # Good for enemy if shield is down!
        if not damaged(DPHASER) or not damaged(DPHOTON):
            if damaged(DPHASER): # phasers damaged
                forces += 300.0
            else:
                forces -= 0.2*(game.energy - 2500.0)
            if damaged(DPHOTON): # photon torpedoes damaged
                forces += 300.0
            else:
                forces -= 50.0*game.torps
        else:
            # phasers and photon tubes both out!
            forces += 1000.0
        motion = 0
        if forces <= 1000.0 and game.condition != "docked": # Typical situation
            motion = ((forces + randreal(200))/150.0) - 5.0
        else:
            if forces > 1000.0: # Very strong -- move in for kill
                motion = (1.0 - randreal())**2 * old_dist + 1.0
            if game.condition == "docked" and (game.options & OPTION_BASE): # protected by base -- back off !
                motion -= game.skill*(2.0-randreal()**2)
        if game.idebug:
            proutn("=== MOTION = %d, FORCES = %1.2f, " % (motion, forces))
        # don't move if no motion
        if motion == 0:
            return []
        # Limit motion according to skill
        if abs(motion) > game.skill:
            if motion < 0:
                motion = -game.skill
            else:
                motion = game.skill
    # calculate preferred number of steps
    nsteps = abs(int(motion))
    if motion > 0 and nsteps > mdist:
        nsteps = mdist # don't overshoot
    if nsteps > QUADSIZE:
        nsteps = QUADSIZE # This shouldn't be necessary
    if nsteps < 1:
        nsteps = 1 # This shouldn't be necessary
    if game.idebug:
        proutn("NSTEPS = %d:" % nsteps)
    # Compute preferred values of delta X and Y
    m = game.sector - enemy.location
    if 2.0 * abs(m.i) < abs(m.j):
        m.i = 0
    if 2.0 * abs(m.j) < abs(game.sector.i-enemy.location.i):
        m.j = 0
    m = (motion * m).sgn()
    goto = enemy.location
    # main move loop
    for ll in range(nsteps):
        if game.idebug:
            proutn(" %d" % (ll+1))
        # Check if preferred position available
        look = goto + m
        if m.i < 0:
            krawli = 1
        else:
            krawli = -1
        if m.j < 0:
            krawlj = 1
        else:
            krawlj = -1
        success = False
        attempts = 0 # Settle mysterious hang problem
        while attempts < 20 and not success:
            attempts += 1
            if look.i < 0 or look.i >= QUADSIZE:
                if motion < 0:
                    return tryexit(enemy, look, irun)
                if krawli == m.i or m.j == 0:
                    break
                look.i = goto.i + krawli
                krawli = -krawli
            elif look.j < 0 or look.j >= QUADSIZE:
                if motion < 0:
                    return tryexit(enemy, look, irun)
                if krawlj == m.j or m.i == 0:
                    break
                look.j = goto.j + krawlj
                krawlj = -krawlj
            elif (game.options & OPTION_RAMMING) and game.quad[look.i][look.j] != '.':
                # See if enemy should ram ship
                if game.quad[look.i][look.j] == game.ship and \
                    (enemy.type == 'C' or enemy.type == 'S'):
                    collision(rammed=True, enemy=enemy)
                    return []
                if krawli != m.i and m.j != 0:
                    look.i = goto.i + krawli
                    krawli = -krawli
                elif krawlj != m.j and m.i != 0:
                    look.j = goto.j + krawlj
                    krawlj = -krawlj
                else:
                    break # we have failed
            else:
                success = True
        if success:
            goto = look
            if game.idebug:
                proutn(repr(goto))
        else:
            break # done early
    if game.idebug:
        skip(1)
    # Enemy moved, but is still in sector
    return [(False, enemy, old_dist, goto)]
 
def moveklings():
    "Sequence Klingon tactical movement."
    if game.idebug:
        prout("== MOVCOM")
    # Figure out which Klingon is the commander (or Supercommander)
    # and do move
    tacmoves = []
    if game.quadrant in game.state.kcmdr:
        for enemy in game.enemies:
            if enemy.type == 'C':
                tacmoves += movebaddy(enemy)
    if game.state.kscmdr == game.quadrant:
        for enemy in game.enemies:
            if enemy.type == 'S':
                tacmoves += movebaddy(enemy)
                break
    # If skill level is high, move other Klingons and Romulans too!
    # Move these last so they can base their actions on what the
    # commander(s) do.
    if game.skill >= SKILL_EXPERT and (game.options & OPTION_MVBADDY):
        for enemy in game.enemies:
            if enemy.type in ('K', 'R'):
                tacmoves += movebaddy(enemy)
    return tacmoves
 
def movescom(iq, avoid):
    "Commander movement helper."
    # Avoid quadrants with bases if we want to avoid Enterprise
    if not welcoming(iq) or (avoid and iq in game.state.baseq):
        return False
    if game.justin and not game.iscate:
        return False
    # do the move
    game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons -= 1
    game.state.kscmdr = iq
    game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].klingons += 1
    if game.state.kscmdr == game.quadrant:
        # SC has scooted, remove him from current quadrant
        game.iscate = False
        game.isatb = 0
        game.ientesc = False
        unschedule(FSCDBAS)
        for enemy in game.enemies:
            if enemy.type == 'S':
                enemy.move(None)
        game.klhere -= 1
        if game.condition != "docked":
            newcnd()
        sortenemies()
    # check for a helpful planet
    for i in range(game.inplan):
        if game.state.planets[i].quadrant == game.state.kscmdr and \
            game.state.planets[i].crystals == "present":
            # destroy the planet
            game.state.planets[i].pclass = "destroyed"
            game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].planet = None
            if communicating():
                announce()
                prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"))
                proutn(_("   a planet in Quadrant %s has been destroyed") % game.state.kscmdr)
                prout(_("   by the Super-commander.\""))
            break
    return True # looks good!
 
def supercommander():
    "Move the Super Commander."
    iq = Coord()
    sc = Coord()
    ibq = Coord()
    idelta = Coord()
    basetbl = []
    if game.idebug:
        prout("== SUPERCOMMANDER")
    # Decide on being active or passive
    avoid = ((game.incom - len(game.state.kcmdr) + game.inkling - game.state.remkl)/(game.state.date+0.01-game.indate) < 0.1*game.skill*(game.skill+1.0) or \
            (game.state.date-game.indate) < 3.0)
    if not game.iscate and avoid:
        # compute move away from Enterprise
        idelta = game.state.kscmdr-game.quadrant
        if idelta.distance() > 2.0:
            # circulate in space
            idelta.i = game.state.kscmdr.j-game.quadrant.j
            idelta.j = game.quadrant.i-game.state.kscmdr.i
    else:
        # compute distances to starbases
        if not game.state.baseq:
            # nothing left to do
            unschedule(FSCMOVE)
            return
        sc = game.state.kscmdr
        for (i, base) in enumerate(game.state.baseq):
            basetbl.append((i, (base - sc).distance()))
        if game.state.baseq > 1:
            basetbl.sort(key=lambda x: x[1])
        # look for nearest base without a commander, no Enterprise, and
        # without too many Klingons, and not already under attack.
        ifindit = iwhichb = 0
        for (i2, base) in enumerate(game.state.baseq):
            i = basetbl[i2][0]        # bug in original had it not finding nearest
            if base == game.quadrant or base == game.battle or not welcoming(base):
                continue
            # if there is a commander, and no other base is appropriate,
            # we will take the one with the commander
            for cmdr in game.state.kcmdr:
                if base == cmdr and ifindit != 2:
                    ifindit = 2
                    iwhichb = i
                    break
            else:        # no commander -- use this one
                ifindit = 1
                iwhichb = i
                break
        if ifindit == 0:
            return # Nothing suitable -- wait until next time
        ibq = game.state.baseq[iwhichb]
        # decide how to move toward base
        idelta = ibq - game.state.kscmdr
    # Maximum movement is 1 quadrant in either or both axes
    idelta = idelta.sgn()
    # try moving in both x and y directions
    # there was what looked like a bug in the Almy C code here,
    # but it might be this translation is just wrong.
    iq = game.state.kscmdr + idelta
    if not movescom(iq, avoid):
        # failed -- try some other maneuvers
        if idelta.i == 0 or idelta.j == 0:
            # attempt angle move
            if idelta.i != 0:
                iq.j = game.state.kscmdr.j + 1
                if not movescom(iq, avoid):
                    iq.j = game.state.kscmdr.j - 1
                    movescom(iq, avoid)
            elif idelta.j != 0:
                iq.i = game.state.kscmdr.i + 1
                if not movescom(iq, avoid):
                    iq.i = game.state.kscmdr.i - 1
                    movescom(iq, avoid)
        else:
            # try moving just in x or y
            iq.j = game.state.kscmdr.j
            if not movescom(iq, avoid):
                iq.j = game.state.kscmdr.j + idelta.j
                iq.i = game.state.kscmdr.i
                movescom(iq, avoid)
    # check for a base
    if len(game.state.baseq) == 0:
        unschedule(FSCMOVE)
    else:
        for ibq in game.state.baseq:
            if ibq == game.state.kscmdr and game.state.kscmdr == game.battle:
                # attack the base
                if avoid:
                    return # no, don't attack base!
                game.iseenit = False
                game.isatb = 1
                schedule(FSCDBAS, randreal(1.0, 3.0))
                if is_scheduled(FCDBAS):
                    postpone(FSCDBAS, scheduled(FCDBAS)-game.state.date)
                if not communicating():
                    return # no warning
                game.iseenit = True
                announce()
                prout(_("Lt. Uhura-  \"Captain, the starbase in Quadrant %s") \
                      % game.state.kscmdr)
                prout(_("   reports that it is under attack from the Klingon Super-commander."))
                proutn(_("   It can survive until stardate %d.\"") \
                       % int(scheduled(FSCDBAS)))
                if not game.resting:
                    return
                prout(_("Mr. Spock-  \"Captain, shall we cancel the rest period?\""))
                if not ja():
                    return
                game.resting = False
                game.optime = 0.0 # actually finished
                return
    # Check for intelligence report
    if not game.idebug and \
        (withprob(0.8) or \
         (not communicating()) or \
         not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].charted):
        return
    announce()
    prout(_("Lt. Uhura-  \"Captain, Starfleet Intelligence reports"))
    proutn(_("   the Super-commander is in Quadrant %s,") % game.state.kscmdr)
    return
 
def movetholian():
    "Move the Tholian."
    if not game.tholian or game.justin:
        return
    tid = Coord()
    if game.tholian.location.i == 0 and game.tholian.location.j == 0:
        tid.i = 0
        tid.j = QUADSIZE-1
    elif game.tholian.location.i == 0 and game.tholian.location.j == QUADSIZE-1:
        tid.i = QUADSIZE-1
        tid.j = QUADSIZE-1
    elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == QUADSIZE-1:
        tid.i = QUADSIZE-1
        tid.j = 0
    elif game.tholian.location.i == QUADSIZE-1 and game.tholian.location.j == 0:
        tid.i = 0
        tid.j = 0
    else:
        # something is wrong!
        game.tholian.move(None)
        prout("***Internal error: Tholian in a bad spot.")
        return
    # do nothing if we are blocked
    if game.quad[tid.i][tid.j] not in ('.', '#'):
        return
    here = copy.copy(game.tholian.location)
    delta = (tid - game.tholian.location).sgn()
    # move in x axis
    while here.i != tid.i:
        here.i += delta.i
        if game.quad[here.i][here.j] == '.':
            game.tholian.move(here)
    # move in y axis
    while here.j != tid.j:
        here.j += delta.j
        if game.quad[here.i][here.j] == '.':
            game.tholian.move(here)
    # check to see if all holes plugged
    for i in range(QUADSIZE):
        if game.quad[0][i] != '#' and game.quad[0][i] != 'T':
            return
        if game.quad[QUADSIZE-1][i] != '#' and game.quad[QUADSIZE-1][i] != 'T':
            return
        if game.quad[i][0] != '#' and game.quad[i][0] != 'T':
            return
        if game.quad[i][QUADSIZE-1] != '#' and game.quad[i][QUADSIZE-1] != 'T':
            return
    # All plugged up -- Tholian splits
    game.quad[game.tholian.location.i][game.tholian.location.j] = '#'
    dropin(' ')
    prout(crmena(True, 'T', "sector", game.tholian) + _(" completes web."))
    game.tholian.move(None)
    return
 
# Code from battle.c begins here
 
def doshield(shraise):
    "Change shield status."
    action = "NONE"
    game.ididit = False
    if shraise:
        action = "SHUP"
    else:
        key = scanner.nexttok()
        if key == "IHALPHA":
            if scanner.sees("transfer"):
                action = "NRG"
            else:
                if damaged(DSHIELD):
                    prout(_("Shields damaged and down."))
                    return
                if scanner.sees("up"):
                    action = "SHUP"
                elif scanner.sees("down"):
                    action = "SHDN"
        if action == "NONE":
            proutn(_("Do you wish to change shield energy? "))
            if ja():
                action = "NRG"
            elif damaged(DSHIELD):
                prout(_("Shields damaged and down."))
                return
            elif game.shldup:
                proutn(_("Shields are up. Do you want them down? "))
                if ja():
                    action = "SHDN"
                else:
                    scanner.chew()
                    return
            else:
                proutn(_("Shields are down. Do you want them up? "))
                if ja():
                    action = "SHUP"
                else:
                    scanner.chew()
                    return
    if action == "SHUP": # raise shields
        if game.shldup:
            prout(_("Shields already up."))
            return
        game.shldup = True
        game.shldchg = True
        if game.condition != "docked":
            game.energy -= 50.0
        prout(_("Shields raised."))
        if game.energy <= 0:
            skip(1)
            prout(_("Shields raising uses up last of energy."))
            finish(FNRG)
            return
        game.ididit = True
        return
    elif action == "SHDN":
        if not game.shldup:
            prout(_("Shields already down."))
            return
        game.shldup = False
        game.shldchg = True
        prout(_("Shields lowered."))
        game.ididit = True
        return
    elif action == "NRG":
        while scanner.nexttok() != "IHREAL":
            scanner.chew()
            proutn(_("Energy to transfer to shields- "))
        nrg = scanner.real
        scanner.chew()
        if nrg == 0:
            return
        if nrg > game.energy:
            prout(_("Insufficient ship energy."))
            return
        game.ididit = True
        if game.shield+nrg >= game.inshld:
            prout(_("Shield energy maximized."))
            if game.shield+nrg > game.inshld:
                prout(_("Excess energy requested returned to ship energy"))
            game.energy -= game.inshld-game.shield
            game.shield = game.inshld
            return
        if nrg < 0.0 and game.energy-nrg > game.inenrg:
            # Prevent shield drain loophole
            skip(1)
            prout(_("Engineering to bridge--"))
            prout(_("  Scott here. Power circuit problem, Captain."))
            prout(_("  I can't drain the shields."))
            game.ididit = False
            return
        if game.shield+nrg < 0:
            prout(_("All shield energy transferred to ship."))
            game.energy += game.shield
            game.shield = 0.0
            return
        proutn(_("Scotty- \""))
        if nrg > 0:
            prout(_("Transferring energy to shields.\""))
        else:
            prout(_("Draining energy from shields.\""))
        game.shield += nrg
        game.energy -= nrg
        return
 
def randdevice():
    "Choose a device to damage, at random."
    weights = (
        105,        # DSRSENS: short range scanners        10.5%
        105,        # DLRSENS: long range scanners                10.5%
        120,        # DPHASER: phasers                        12.0%
        120,        # DPHOTON: photon torpedoes                12.0%
        25,        # DLIFSUP: life support                         2.5%
        65,        # DWARPEN: warp drive                         6.5%
        70,        # DIMPULS: impulse engines                 6.5%
        145,        # DSHIELD: deflector shields                14.5%
        30,        # DRADIO:  subspace radio                 3.0%
        45,        # DSHUTTL: shuttle                         4.5%
        15,        # DCOMPTR: computer                         1.5%
        20,        # NAVCOMP: navigation system                 2.0%
        75,        # DTRANSP: transporter                         7.5%
        20,        # DSHCTRL: high-speed shield controller  2.0%
        10,        # DDRAY: death ray                         1.0%
        30,        # DDSP: deep-space probes                 3.0%
    )
    assert(sum(weights) == 1000)
    idx = randrange(1000)
    wsum = 0
    for (i, w) in enumerate(weights):
        wsum += w
        if idx < wsum:
            return i
    return None        # we should never get here
 
def collision(rammed, enemy):
    "Collision handling for rammong events."
    prouts(_("***RED ALERT!  RED ALERT!"))
    skip(1)
    prout(_("***COLLISION IMMINENT."))
    skip(2)
    proutn("***")
    proutn(crmshp())
    hardness = {'R':1.5, 'C':2.0, 'S':2.5, 'T':0.5, '?':4.0}.get(enemy.type, 1.0)
    if rammed:
        proutn(_(" rammed by "))
    else:
        proutn(_(" rams "))
    proutn(crmena(False, enemy.type, "sector", enemy.location))
    if rammed:
        proutn(_(" (original position)"))
    skip(1)
    deadkl(enemy.location, enemy.type, game.sector)
    proutn("***" + crmshp() + " heavily damaged.")
    icas = randrange(10, 30)
    prout(_("***Sickbay reports %d casualties") % icas)
    game.casual += icas
    game.state.crew -= icas
    # In the pre-SST2K version, all devices got equiprobably damaged,
    # which was silly.  Instead, pick up to half the devices at
    # random according to our weighting table,
    ncrits = randrange(int(NDEVICES/2))
    while ncrits > 0:
        ncrits -= 1
        dev = randdevice()
        if game.damage[dev] < 0:
            continue
        extradm = (10.0*hardness*randreal()+1.0)*game.damfac
        # Damage for at least time of travel!
        game.damage[dev] += game.optime + extradm
    game.shldup = False
    prout(_("***Shields are down."))
    if game.state.remkl + len(game.state.kcmdr) + game.state.nscrem:
        announce()
        damagereport()
    else:
        finish(FWON)
    return
 
def torpedo(origin, bearing, dispersion, number, nburst):
    "Let a photon torpedo fly"
    if not damaged(DSRSENS) or game.condition == "docked":
        setwnd(srscan_window)
    else:
        setwnd(message_window)
    ac = bearing + 0.25*dispersion        # dispersion is a random variable
    bullseye = (15.0 - bearing)*0.5235988
    track = course(bearing=ac, distance=QUADSIZE, origin=cartesian(origin))
    bumpto = Coord(0, 0)
    # Loop to move a single torpedo
    setwnd(message_window)
    for step in range(1, QUADSIZE*2):
        if not track.nexttok():
            break
        w = track.sector()
        if not w.valid_sector():
            break
        iquad = game.quad[w.i][w.j]
        tracktorpedo(w, step, number, nburst, iquad)
        if iquad == '.':
            continue
        # hit something
        setwnd(message_window)
        if not damaged(DSRSENS) or game.condition == "docked":
            skip(1)        # start new line after text track
        if iquad in ('E', 'F'): # Hit our ship
            skip(1)
            prout(_("Torpedo hits %s.") % crmshp())
            hit = 700.0 + randreal(100) - \
                1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
            newcnd() # we're blown out of dock
            if game.landed or game.condition == "docked":
                return hit # Cheat if on a planet
            # In the C/FORTRAN version, dispersion was 2.5 radians, which
            # is 143 degrees, which is almost exactly 4.8 clockface units
            displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
            displacement.nexttok()
            bumpto = displacement.sector()
            if not bumpto.valid_sector():
                return hit
            if game.quad[bumpto.i][bumpto.j] == ' ':
                finish(FHOLE)
                return hit
            if game.quad[bumpto.i][bumpto.j] != '.':
                # can't move into object
                return hit
            game.sector = bumpto
            proutn(crmshp())
            game.quad[w.i][w.j] = '.'
            game.quad[bumpto.i][bumpto.j] = iquad
            prout(_(" displaced by blast to Sector %s ") % bumpto)
            for enemy in game.enemies:
                enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
            sortenemies()
            return None
        elif iquad in ('C', 'S', 'R', 'K'): # Hit a regular enemy
            # find the enemy
            if iquad in ('C', 'S') and withprob(0.05):
                prout(crmena(True, iquad, "sector", w) + _(" uses anti-photon device;"))
                prout(_("   torpedo neutralized."))
                return None
            for enemy in game.enemies:
                if w == enemy.location:
                    kp = math.fabs(enemy.power)
                    h1 = 700.0 + randrange(100) - \
                        1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
                    h1 = math.fabs(h1)
                    if kp < h1:
                        h1 = kp
                    if enemy.power < 0:
                        enemy.power -= -h1
                    else:
                        enemy.power -= h1
                    if enemy.power == 0:
                        deadkl(w, iquad, w)
                        return None
                    proutn(crmena(True, iquad, "sector", w))
                    displacement = course(track.bearing+randreal(-2.4, 2.4), distance=2**0.5)
                    displacement.nexttok()
                    bumpto = displacement.sector()
                    if not bumpto.valid_sector():
                        prout(_(" damaged but not destroyed."))
                        return
                    if game.quad[bumpto.i][bumpto.j] == ' ':
                        prout(_(" buffeted into black hole."))
                        deadkl(w, iquad, bumpto)
                    if game.quad[bumpto.i][bumpto.j] != '.':
                        prout(_(" damaged but not destroyed."))
                    else:
                        prout(_(" damaged-- displaced by blast to Sector %s ")%bumpto)
                        enemy.location = bumpto
                        game.quad[w.i][w.j] = '.'
                        game.quad[bumpto.i][bumpto.j] = iquad
                        for enemy in game.enemies:
                            enemy.kdist = enemy.kavgd = (game.sector-enemy.location).distance()
                        sortenemies()
                    break
            else:
                prout("Internal error, no enemy where expected!")
                raise SystemExit(1)
            return None
        elif iquad == 'B': # Hit a base
            skip(1)
            prout(_("***STARBASE DESTROYED.."))
            game.state.baseq = [x for x in game.state.baseq if x != game.quadrant]
            game.quad[w.i][w.j] = '.'
            game.base.invalidate()
            game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
            game.state.chart[game.quadrant.i][game.quadrant.j].starbase = False
            game.state.basekl += 1
            newcnd()
            return None
        elif iquad == 'P': # Hit a planet
            prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
            game.state.nplankl += 1
            game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
            game.iplnet.pclass = "destroyed"
            game.iplnet = None
            game.plnet.invalidate()
            game.quad[w.i][w.j] = '.'
            if game.landed:
                # captain perishes on planet
                finish(FDPLANET)
            return None
        elif iquad == '@': # Hit an inhabited world -- very bad!
            prout(crmena(True, iquad, "sector", w) + _(" destroyed."))
            game.state.nworldkl += 1
            game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
            game.iplnet.pclass = "destroyed"
            game.iplnet = None
            game.plnet.invalidate()
            game.quad[w.i][w.j] = '.'
            if game.landed:
                # captain perishes on planet
                finish(FDPLANET)
            prout(_("The torpedo destroyed an inhabited planet."))
            return None
        elif iquad == '*': # Hit a star
            if withprob(0.9):
                nova(w)
            else:
                prout(crmena(True, '*', "sector", w) + _(" unaffected by photon blast."))
            return None
        elif iquad == '?': # Hit a thingy
            if not (game.options & OPTION_THINGY) or withprob(0.3):
                skip(1)
                prouts(_("AAAAIIIIEEEEEEEEAAAAAAAAUUUUUGGGGGHHHHHHHHHHHH!!!"))
                skip(1)
                prouts(_("    HACK!     HACK!    HACK!        *CHOKE!*  "))
                skip(1)
                proutn(_("Mr. Spock-"))
                prouts(_("  \"Fascinating!\""))
                skip(1)
                deadkl(w, iquad, w)
            else:
                # Stas Sergeev added the possibility that
                # you can shove the Thingy and piss it off.
                # It then becomes an enemy and may fire at you.
                thing.angry()
            return None
        elif iquad == ' ': # Black hole
            skip(1)
            prout(crmena(True, ' ', "sector", w) + _(" swallows torpedo."))
            return None
        elif iquad == '#': # hit the web
            skip(1)
            prout(_("***Torpedo absorbed by Tholian web."))
            return None
        elif iquad == 'T':  # Hit a Tholian
            h1 = 700.0 + randrange(100) - \
                1000.0 * (w-origin).distance() * math.fabs(math.sin(bullseye-track.angle))
            h1 = math.fabs(h1)
            if h1 >= 600:
                game.quad[w.i][w.j] = '.'
                deadkl(w, iquad, w)
                game.tholian = None
                return None
            skip(1)
            proutn(crmena(True, 'T', "sector", w))
            if withprob(0.05):
                prout(_(" survives photon blast."))
                return None
            prout(_(" disappears."))
            game.tholian.move(None)
            game.quad[w.i][w.j] = '#'
            dropin(' ')
            return None
        else: # Problem!
            skip(1)
            proutn("Don't know how to handle torpedo collision with ")
            proutn(crmena(True, iquad, "sector", w))
            skip(1)
            return None
        break
    skip(1)
    setwnd(message_window)
    prout(_("Torpedo missed."))
    return None
 
def fry(hit):
    "Critical-hit resolution."
    if hit < (275.0-25.0*game.skill)*randreal(1.0, 1.5):
        return
    ncrit = int(1.0 + hit/(500.0+randreal(100)))
    proutn(_("***CRITICAL HIT--"))
    # Select devices and cause damage
    cdam = []
    while ncrit > 0:
        while True:
            j = randdevice()
            # Cheat to prevent shuttle damage unless on ship
            if not (game.damage[j]<0.0 or (j == DSHUTTL and game.iscraft != "onship")):
                break
        cdam.append(j)
        extradm = (hit*game.damfac)/(ncrit*randreal(75, 100))
        game.damage[j] += extradm
        ncrit -= 1
    skipcount = 0
    for (i, j) in enumerate(cdam):
        proutn(device[j])
        if skipcount % 3 == 2 and i < len(cdam)-1:
            skip(1)
        skipcount += 1
        if i < len(cdam)-1:
            proutn(_(" and "))
    prout(_(" damaged."))
    if damaged(DSHIELD) and game.shldup:
        prout(_("***Shields knocked down."))
        game.shldup = False
 
def attack(torps_ok):
    # bad guy attacks us
    # torps_ok == False forces use of phasers in an attack
    # game could be over at this point, check
    if game.alldone:
        return
    attempt = False
    ihurt = False
    hitmax = 0.0
    hittot = 0.0
    chgfac = 1.0
    where = "neither"
    if game.idebug:
        prout("=== ATTACK!")
    # Tholian gets to move before attacking
    if game.tholian:
        movetholian()
    # if you have just entered the RNZ, you'll get a warning
    if game.neutz: # The one chance not to be attacked
        game.neutz = False
        return
    # commanders get a chance to tac-move towards you
    if (((game.quadrant in game.state.kcmdr or game.state.kscmdr == game.quadrant) and not game.justin) or game.skill == SKILL_EMERITUS) and torps_ok:
        for (bugout, enemy, old, goto) in  moveklings():
            if bugout:
                # we know about this if either short or long range
                # sensors are working
                if damaged(DSRSENS) and damaged(DLRSENS) \
                       and game.condition != "docked":
                    prout(crmena(True, enemy.type, "sector", old) + \
                          (_(" escapes to Quadrant %s (and regains strength).") % goto))
            else: # Enemy still in-sector
                if enemy.move(goto):
                    if not damaged(DSRSENS) or game.condition == "docked":
                        proutn(_("*** %s from Sector %s") % (cramen(enemy.type), enemy.location))
                        if enemy.kdist < old:
                            proutn(_(" advances to "))
                        else:
                            proutn(_(" retreats to "))
                        prout("Sector %s." % goto)
        sortenemies()
    # if no enemies remain after movement, we're done
    if len(game.enemies) == 0 or (len(game.enemies) == 1 and thing.at(game.quadrant) and not thing.angered):
        return
    # set up partial hits if attack happens during shield status change
    pfac = 1.0/game.inshld
    if game.shldchg:
        chgfac = 0.25 + randreal(0.5)
    skip(1)
    # message verbosity control
    if game.skill <= SKILL_FAIR:
        where = "sector"
    for enemy in game.enemies:
        if enemy.power < 0:
            continue        # too weak to attack
        # compute hit strength and diminish shield power
        r = randreal()
        # Increase chance of photon torpedos if docked or enemy energy is low
        if game.condition == "docked":
            r *= 0.25
        if enemy.power < 500:
            r *= 0.25
        if enemy.type == 'T' or (enemy.type == '?' and not thing.angered):
            continue
        # different enemies have different probabilities of throwing a torp
        usephasers = not torps_ok or \
            (enemy.type == 'K' and r > 0.0005) or \
            (enemy.type == 'C' and r > 0.015) or \
            (enemy.type == 'R' and r > 0.3) or \
            (enemy.type == 'S' and r > 0.07) or \
            (enemy.type == '?' and r > 0.05)
        if usephasers:            # Enemy uses phasers
            if game.condition == "docked":
                continue # Don't waste the effort!
            attempt = True # Attempt to attack
            dustfac = randreal(0.8, 0.85)
            hit = enemy.power*math.pow(dustfac, enemy.kavgd)
            enemy.power *= 0.75
        else: # Enemy uses photon torpedo
            # We should be able to make the bearing() method work here
            pcourse = 1.90985*math.atan2(game.sector.j-enemy.location.j, enemy.location.i-game.sector.i)
            hit = 0
            proutn(_("***TORPEDO INCOMING"))
            if not damaged(DSRSENS):
                proutn(_(" From ") + crmena(False, enemy.type, where, enemy.location))
            attempt = True
            prout("  ")
            dispersion = (randreal()+randreal())*0.5 - 0.5
            dispersion += 0.002*enemy.power*dispersion
            hit = torpedo(enemy.location, pcourse, dispersion, number=1, nburst=1)
            if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) == 0:
                finish(FWON) # Klingons did themselves in!
            if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova or game.alldone:
                return # Supernova or finished
            if hit == None:
                continue
        # incoming phaser or torpedo, shields may dissipate it
        if game.shldup or game.shldchg or game.condition == "docked":
            # shields will take hits
            propor = pfac * game.shield
            if game.condition == "docked":
                propor *= 2.1
            if propor < 0.1:
                propor = 0.1
            hitsh = propor*chgfac*hit+1.0
            absorb = 0.8*hitsh
            if absorb > game.shield:
                absorb = game.shield
            game.shield -= absorb
            hit -= hitsh
            # taking a hit blasts us out of a starbase dock
            if game.condition == "docked":
                dock(False)
            # but the shields may take care of it
            if propor > 0.1 and hit < 0.005*game.energy:
                continue
        # hit from this opponent got through shields, so take damage
        ihurt = True
        proutn(_("%d unit hit") % int(hit))
        if (damaged(DSRSENS) and usephasers) or game.skill<=SKILL_FAIR:
            proutn(_(" on the ") + crmshp())
        if not damaged(DSRSENS) and usephasers:
            prout(_(" from ") + crmena(False, enemy.type, where, enemy.location))
        skip(1)
        # Decide if hit is critical
        if hit > hitmax:
            hitmax = hit
        hittot += hit
        fry(hit)
        game.energy -= hit
    if game.energy <= 0:
        # Returning home upon your shield, not with it...
        finish(FBATTLE)
        return
    if not attempt and game.condition == "docked":
        prout(_("***Enemies decide against attacking your ship."))
    percent = 100.0*pfac*game.shield+0.5
    if not ihurt:
        # Shields fully protect ship
        proutn(_("Enemy attack reduces shield strength to "))
    else:
        # Emit message if starship suffered hit(s)
        skip(1)
        proutn(_("Energy left %2d    shields ") % int(game.energy))
        if game.shldup:
            proutn(_("up "))
        elif not damaged(DSHIELD):
            proutn(_("down "))
        else:
            proutn(_("damaged, "))
    prout(_("%d%%,   torpedoes left %d") % (percent, game.torps))
    # Check if anyone was hurt
    if hitmax >= 200 or hittot >= 500:
        icas = randrange(int(hittot * 0.015))
        if icas >= 2:
            skip(1)
            prout(_("Mc Coy-  \"Sickbay to bridge.  We suffered %d casualties") % icas)
            prout(_("   in that last attack.\""))
            game.casual += icas
            game.state.crew -= icas
    # After attack, reset average distance to enemies
    for enemy in game.enemies:
        enemy.kavgd = enemy.kdist
    sortenemies()
    return
 
def deadkl(w, etype, mv):
    "Kill a Klingon, Tholian, Romulan, or Thingy."
    # Added mv to allow enemy to "move" before dying
    proutn(crmena(True, etype, "sector", mv))
    # Decide what kind of enemy it is and update appropriately
    if etype == 'R':
        # Chalk up a Romulan
        game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans -= 1
        game.irhere -= 1
        game.state.nromrem -= 1
    elif etype == 'T':
        # Killed a Tholian
        game.tholian = None
    elif etype == '?':
        # Killed a Thingy
        global thing
        thing = None
    else:
        # Killed some type of Klingon
        game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1
        game.klhere -= 1
        if type == 'C':
            game.state.kcmdr.remove(game.quadrant)
            unschedule(FTBEAM)
            if game.state.kcmdr:
                schedule(FTBEAM, expran(1.0*game.incom/len(game.state.kcmdr)))
            if is_scheduled(FCDBAS) and game.battle == game.quadrant:
                unschedule(FCDBAS)
        elif type ==  'K':
            game.state.remkl -= 1
        elif type ==  'S':
            game.state.nscrem -= 1
            game.state.kscmdr.invalidate()
            game.isatb = 0
            game.iscate = False
            unschedule(FSCMOVE)
            unschedule(FSCDBAS)
    # For each kind of enemy, finish message to player
    prout(_(" destroyed."))
    if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) == 0:
        return
    game.recompute()
    # Remove enemy ship from arrays describing local conditions
    for e in game.enemies:
        if e.location == w:
            e.move(None)
            break
    return
 
def targetcheck(w):
    "Return None if target is invalid, otherwise return a course angle."
    if not w.valid_sector():
        huh()
        return None
    delta = Coord()
    # C code this was translated from is wacky -- why the sign reversal?
    delta.j = (w.j - game.sector.j)
    delta.i = (game.sector.i - w.i)
    if delta == Coord(0, 0):
        skip(1)
        prout(_("Spock-  \"Bridge to sickbay.  Dr. McCoy,"))
        prout(_("  I recommend an immediate review of"))
        prout(_("  the Captain's psychological profile.\""))
        scanner.chew()
        return None
    return delta.bearing()
 
def torps():
    "Launch photon torpedo salvo."
    tcourse = []
    game.ididit = False
    if damaged(DPHOTON):
        prout(_("Photon tubes damaged."))
        scanner.chew()
        return
    if game.torps == 0:
        prout(_("No torpedoes left."))
        scanner.chew()
        return
    # First, get torpedo count
    while True:
        scanner.nexttok()
        if scanner.token == "IHALPHA":
            huh()
            return
        elif scanner.token == "IHEOL" or not scanner.waiting():
            prout(_("%d torpedoes left.") % game.torps)
            scanner.chew()
            proutn(_("Number of torpedoes to fire- "))
            continue        # Go back around to get a number
        else: # key == "IHREAL"
            n = scanner.int()
            if n <= 0: # abort command
                scanner.chew()
                return
            if n > MAXBURST:
                scanner.chew()
                prout(_("Maximum of %d torpedoes per burst.") % MAXBURST)
                return
            if n > game.torps:
                scanner.chew()        # User requested more torps than available
                continue        # Go back around
            break        # All is good, go to next stage
    # Next, get targets
    target = []
    for i in range(n):
        key = scanner.nexttok()
        if i == 0 and key == "IHEOL":
            break        # no coordinate waiting, we will try prompting
        if i == 1 and key == "IHEOL":
            # direct all torpedoes at one target
            while i < n:
                target.append(target[0])
                tcourse.append(tcourse[0])
                i += 1
            break
        scanner.push(scanner.token)
        target.append(scanner.getcoord())
        if target[-1] == None:
            return
        tcourse.append(targetcheck(target[-1]))
        if tcourse[-1] == None:
            return
    scanner.chew()
    if len(target) == 0:
        # prompt for each one
        for i in range(n):
            proutn(_("Target sector for torpedo number %d- ") % (i+1))
            scanner.chew()
            target.append(scanner.getcoord())
            if target[-1] == None:
                return
            tcourse.append(targetcheck(target[-1]))
            if tcourse[-1] == None:
                return
    game.ididit = True
    # Loop for moving <n> torpedoes
    for i in range(n):
        if game.condition != "docked":
            game.torps -= 1
        dispersion = (randreal()+randreal())*0.5 -0.5
        if math.fabs(dispersion) >= 0.47:
            # misfire!
            dispersion *= randreal(1.2, 2.2)
            if n > 0:
                prouts(_("***TORPEDO NUMBER %d MISFIRES") % (i+1))
            else:
                prouts(_("***TORPEDO MISFIRES."))
            skip(1)
            if i < n:
                prout(_("  Remainder of burst aborted."))
            if withprob(0.2):
                prout(_("***Photon tubes damaged by misfire."))
                game.damage[DPHOTON] = game.damfac * randreal(1.0, 3.0)
            break
        if game.shldup or game.condition == "docked":
            dispersion *= 1.0 + 0.0001*game.shield
        torpedo(game.sector, tcourse[i], dispersion, number=i, nburst=n)
        if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
            return
    if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)<=0:
        finish(FWON)
 
def overheat(rpow):
    "Check for phasers overheating."
    if rpow > 1500:
        checkburn = (rpow-1500.0)*0.00038
        if withprob(checkburn):
            prout(_("Weapons officer Sulu-  \"Phasers overheated, sir.\""))
            game.damage[DPHASER] = game.damfac* randreal(1.0, 2.0) * (1.0+checkburn)
 
def checkshctrl(rpow):
    "Check shield control."
    skip(1)
    if withprob(0.998):
        prout(_("Shields lowered."))
        return False
    # Something bad has happened
    prouts(_("***RED ALERT!  RED ALERT!"))
    skip(2)
    hit = rpow*game.shield/game.inshld
    game.energy -= rpow+hit*0.8
    game.shield -= hit*0.2
    if game.energy <= 0.0:
        prouts(_("Sulu-  \"Captain! Shield malf***********************\""))
        skip(1)
        stars()
        finish(FPHASER)
        return True
    prouts(_("Sulu-  \"Captain! Shield malfunction! Phaser fire contained!\""))
    skip(2)
    prout(_("Lt. Uhura-  \"Sir, all decks reporting damage.\""))
    icas = randrange(int(hit*0.012))
    skip(1)
    fry(0.8*hit)
    if icas:
        skip(1)
        prout(_("McCoy to bridge- \"Severe radiation burns, Jim."))
        prout(_("  %d casualties so far.\"") % icas)
        game.casual += icas
        game.state.crew -= icas
    skip(1)
    prout(_("Phaser energy dispersed by shields."))
    prout(_("Enemy unaffected."))
    overheat(rpow)
    return True
 
def hittem(hits):
    "Register a phaser hit on Klingons and Romulans."
    w = Coord()
    skip(1)
    kk = 0
    for wham in hits:
        if wham == 0:
            continue
        dustfac = randreal(0.9, 1.0)
        hit = wham*math.pow(dustfac, game.enemies[kk].kdist)
        kpini = game.enemies[kk].power
        kp = math.fabs(kpini)
        if PHASEFAC*hit < kp:
            kp = PHASEFAC*hit
        if game.enemies[kk].power < 0:
            game.enemies[kk].power -= -kp
        else:
            game.enemies[kk].power -= kp
        kpow = game.enemies[kk].power
        w = game.enemies[kk].location
        if hit > 0.005:
            if not damaged(DSRSENS):
                boom(w)
            proutn(_("%d unit hit on ") % int(hit))
        else:
            proutn(_("Very small hit on "))
        ienm = game.quad[w.i][w.j]
        if ienm == '?':
            thing.angry()
        proutn(crmena(False, ienm, "sector", w))
        skip(1)
        if kpow == 0:
            deadkl(w, ienm, w)
            if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
                finish(FWON)
            if game.alldone:
                return
            kk -= 1        # don't do the increment
            continue
        else: # decide whether or not to emasculate klingon
            if kpow > 0 and withprob(0.9) and kpow <= randreal(0.4, 0.8)*kpini:
                prout(_("***Mr. Spock-  \"Captain, the vessel at Sector %s")%w)
                prout(_("   has just lost its firepower.\""))
                game.enemies[kk].power = -kpow
        kk += 1
    return
 
def phasers():
    "Fire phasers at bad guys."
    hits = []
    kz = 0
    k = 1
    irec = 0 # Cheating inhibitor
    ifast = False
    no = False
    itarg = True
    msgflag = True
    rpow = 0
    automode = "NOTSET"
    key = 0
    skip(1)
    # SR sensors and Computer are needed for automode
    if damaged(DSRSENS) or damaged(DCOMPTR):
        itarg = False
    if game.condition == "docked":
        prout(_("Phasers can't be fired through base shields."))
        scanner.chew()
        return
    if damaged(DPHASER):
        prout(_("Phaser control damaged."))
        scanner.chew()
        return
    if game.shldup:
        if damaged(DSHCTRL):
            prout(_("High speed shield control damaged."))
            scanner.chew()
            return
        if game.energy <= 200.0:
            prout(_("Insufficient energy to activate high-speed shield control."))
            scanner.chew()
            return
        prout(_("Weapons Officer Sulu-  \"High-speed shield control enabled, sir.\""))
        ifast = True
    # Original code so convoluted, I re-did it all
    # (That was Tom Almy talking about the C code, I think -- ESR)
    while automode == "NOTSET":
        key = scanner.nexttok()
        if key == "IHALPHA":
            if scanner.sees("manual"):
                if len(game.enemies)==0:
                    prout(_("There is no enemy present to select."))
                    scanner.chew()
                    key = "IHEOL"
                    automode = "AUTOMATIC"
                else:
                    automode = "MANUAL"
                    key = scanner.nexttok()
            elif scanner.sees("automatic"):
                if (not itarg) and len(game.enemies) != 0:
                    automode = "FORCEMAN"
                else:
                    if len(game.enemies)==0:
                        prout(_("Energy will be expended into space."))
                    automode = "AUTOMATIC"
                    key = scanner.nexttok()
            elif scanner.sees("no"):
                no = True
            else:
                huh()
                return
        elif key == "IHREAL":
            if len(game.enemies)==0:
                prout(_("Energy will be expended into space."))
                automode = "AUTOMATIC"
            elif not itarg:
                automode = "FORCEMAN"
            else:
                automode = "AUTOMATIC"
        else:
            # "IHEOL"
            if len(game.enemies)==0:
                prout(_("Energy will be expended into space."))
                automode = "AUTOMATIC"
            elif not itarg:
                automode = "FORCEMAN"
            else:
                proutn(_("Manual or automatic? "))
                scanner.chew()
    avail = game.energy
    if ifast:
        avail -= 200.0
    if automode == "AUTOMATIC":
        if key == "IHALPHA" and scanner.sees("no"):
            no = True
            key = scanner.nexttok()
        if key != "IHREAL" and len(game.enemies) != 0:
            prout(_("Phasers locked on target. Energy available: %.2f")%avail)
        irec = 0
        while True:
            scanner.chew()
            if not kz:
                for i in range(len(game.enemies)):
                    irec += math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90, game.enemies[i].kdist))*randreal(1.01, 1.06) + 1.0
            kz = 1
            proutn(_("%d units required. ") % irec)
            scanner.chew()
            proutn(_("Units to fire= "))
            key = scanner.nexttok()
            if key != "IHREAL":
                return
            rpow = scanner.real
            if rpow > avail:
                proutn(_("Energy available= %.2f") % avail)
                skip(1)
                key = "IHEOL"
            if not rpow > avail:
                break
        if rpow <= 0:
            # chicken out
            scanner.chew()
            return
        key = scanner.nexttok()
        if key == "IHALPHA" and scanner.sees("no"):
            no = True
        if ifast:
            game.energy -= 200 # Go and do it!
            if checkshctrl(rpow):
                return
        scanner.chew()
        game.energy -= rpow
        extra = rpow
        if len(game.enemies):
            extra = 0.0
            powrem = rpow
            for i in range(len(game.enemies)):
                hits.append(0.0)
                if powrem <= 0:
                    continue
                hits[i] = math.fabs(game.enemies[i].power)/(PHASEFAC*math.pow(0.90, game.enemies[i].kdist))
                over = randreal(1.01, 1.06) * hits[i]
                temp = powrem
                powrem -= hits[i] + over
                if powrem <= 0 and temp < hits[i]:
                    hits[i] = temp
                if powrem <= 0:
                    over = 0.0
                extra += over
            if powrem > 0.0:
                extra += powrem
            hittem(hits)
            game.ididit = True
        if extra > 0 and not game.alldone:
            if game.tholian:
                proutn(_("*** Tholian web absorbs "))
                if len(game.enemies)>0:
                    proutn(_("excess "))
                prout(_("phaser energy."))
            else:
                prout(_("%d expended on empty space.") % int(extra))
    elif automode == "FORCEMAN":
        scanner.chew()
        key = "IHEOL"
        if damaged(DCOMPTR):
            prout(_("Battle computer damaged, manual fire only."))
        else:
            skip(1)
            prouts(_("---WORKING---"))
            skip(1)
            prout(_("Short-range-sensors-damaged"))
            prout(_("Insufficient-data-for-automatic-phaser-fire"))
            prout(_("Manual-fire-must-be-used"))
            skip(1)
    elif automode == "MANUAL":
        rpow = 0.0
        for k in range(len(game.enemies)):
            aim = game.enemies[k].location
            ienm = game.quad[aim.i][aim.j]
            if msgflag:
                proutn(_("Energy available= %.2f") % (avail-0.006))
                skip(1)
                msgflag = False
                rpow = 0.0
            if damaged(DSRSENS) and \
               not game.sector.distance(aim)<2**0.5 and ienm in ('C', 'S'):
                prout(cramen(ienm) + _(" can't be located without short range scan."))
                scanner.chew()
                key = "IHEOL"
                hits[k] = 0 # prevent overflow -- thanks to Alexei Voitenko
                k += 1
                continue
            if key == "IHEOL":
                scanner.chew()
                if itarg and k > kz:
                    irec = (abs(game.enemies[k].power)/(PHASEFAC*math.pow(0.9, game.enemies[k].kdist))) *        randreal(1.01, 1.06) + 1.0
                kz = k
                proutn("(")
                if not damaged(DCOMPTR):
                    proutn("%d" % irec)
                else:
                    proutn("??")
                proutn(")  ")
                proutn(_("units to fire at %s-  ") % crmena(False, ienm, "sector", aim))
                key = scanner.nexttok()
            if key == "IHALPHA" and scanner.sees("no"):
                no = True
                key = scanner.nexttok()
                continue
            if key == "IHALPHA":
                huh()
                return
            if key == "IHEOL":
                if k == 1: # Let me say I'm baffled by this
                    msgflag = True
                continue
            if scanner.real < 0:
                # abort out
                scanner.chew()
                return
            hits[k] = scanner.real
            rpow += scanner.real
            # If total requested is too much, inform and start over
            if rpow > avail:
                prout(_("Available energy exceeded -- try again."))
                scanner.chew()
                return
            key = scanner.nexttok() # scan for next value
            k += 1
        if rpow == 0.0:
            # zero energy -- abort
            scanner.chew()
            return
        if key == "IHALPHA" and scanner.sees("no"):
            no = True
        game.energy -= rpow
        scanner.chew()
        if ifast:
            game.energy -= 200.0
            if checkshctrl(rpow):
                return
        hittem(hits)
        game.ididit = True
     # Say shield raised or malfunction, if necessary
    if game.alldone:
        return
    if ifast:
        skip(1)
        if no == 0:
            if withprob(0.01):
                prout(_("Sulu-  \"Sir, the high-speed shield control has malfunctioned . . ."))
                prouts(_("         CLICK   CLICK   POP  . . ."))
                prout(_(" No response, sir!"))
                game.shldup = False
            else:
                prout(_("Shields raised."))
        else:
            game.shldup = False
    overheat(rpow)
 
# Code from events,c begins here.
 
# This isn't a real event queue a la BSD Trek yet -- you can only have one
# event of each type active at any given time.  Mostly these means we can
# only have one FDISTR/FENSLV/FREPRO sequence going at any given time
# BSD Trek, from which we swiped the idea, can have up to 5.
 
def unschedule(evtype):
    "Remove an event from the schedule."
    game.future[evtype].date = FOREVER
    return game.future[evtype]
 
def is_scheduled(evtype):
    "Is an event of specified type scheduled."
    return game.future[evtype].date != FOREVER
 
def scheduled(evtype):
    "When will this event happen?"
    return game.future[evtype].date
 
def schedule(evtype, offset):
    "Schedule an event of specified type."
    game.future[evtype].date = game.state.date + offset
    return game.future[evtype]
 
def postpone(evtype, offset):
    "Postpone a scheduled event."
    game.future[evtype].date += offset
 
def cancelrest():
    "Rest period is interrupted by event."
    if game.resting:
        skip(1)
        proutn(_("Mr. Spock-  \"Captain, shall we cancel the rest period?\""))
        if ja():
            game.resting = False
            game.optime = 0.0
            return True
    return False
 
def events():
    "Run through the event queue looking for things to do."
    i = 0
    fintim = game.state.date + game.optime
    yank = 0
    ictbeam = False
    istract = False
    w = Coord()
    hold = Coord()
    ev = Event()
    ev2 = Event()
 
    def tractorbeam(yank):
        "Tractor-beaming cases merge here."
        announce()
        game.optime = (10.0/(7.5*7.5))*yank # 7.5 is yank rate (warp 7.5)
        skip(1)
        prout("***" + crmshp() + _(" caught in long range tractor beam--"))
        # If Kirk & Co. screwing around on planet, handle
        atover(True) # atover(true) is Grab
        if game.alldone:
            return
        if game.icraft: # Caught in Galileo?
            finish(FSTRACTOR)
            return
        # Check to see if shuttle is aboard
        if game.iscraft == "offship":
            skip(1)
            if withprob(0.5):
                prout(_("Galileo, left on the planet surface, is captured"))
                prout(_("by aliens and made into a flying McDonald's."))
                game.damage[DSHUTTL] = -10
                game.iscraft = "removed"
            else:
                prout(_("Galileo, left on the planet surface, is well hidden."))
        if evcode == FSPY:
            game.quadrant = game.state.kscmdr
        else:
            game.quadrant = game.state.kcmdr[i]
        game.sector = randplace(QUADSIZE)
        prout(crmshp() + _(" is pulled to Quadrant %s, Sector %s") \
               % (game.quadrant, game.sector))
        if game.resting:
            prout(_("(Remainder of rest/repair period cancelled.)"))
            game.resting = False
        if not game.shldup:
            if not damaged(DSHIELD) and game.shield > 0:
                doshield(shraise=True) # raise shields
                game.shldchg = False
            else:
                prout(_("(Shields not currently useable.)"))
        newqad()
        # Adjust finish time to time of tractor beaming?
        # fintim = game.state.date+game.optime
        attack(torps_ok=False)
        if not game.state.kcmdr:
            unschedule(FTBEAM)
        else:
            schedule(FTBEAM, game.optime+expran(1.5*game.intime/len(game.state.kcmdr)))
 
    def destroybase():
        "Code merges here for any commander destroying a starbase."
        # Not perfect, but will have to do
        # Handle case where base is in same quadrant as starship
        if game.battle == game.quadrant:
            game.state.chart[game.battle.i][game.battle.j].starbase = False
            game.quad[game.base.i][game.base.j] = '.'
            game.base.invalidate()
            newcnd()
            skip(1)
            prout(_("Spock-  \"Captain, I believe the starbase has been destroyed.\""))
        elif game.state.baseq and communicating():
            # Get word via subspace radio
            announce()
            skip(1)
            prout(_("Lt. Uhura-  \"Captain, Starfleet Command reports that"))
            proutn(_("   the starbase in Quadrant %s has been destroyed by") % game.battle)
            if game.isatb == 2:
                prout(_("the Klingon Super-Commander"))
            else:
                prout(_("a Klingon Commander"))
            game.state.chart[game.battle.i][game.battle.j].starbase = False
        # Remove Starbase from galaxy
        game.state.galaxy[game.battle.i][game.battle.j].starbase = False
        game.state.baseq = [x for x in game.state.baseq if x != game.battle]
        if game.isatb == 2:
            # reinstate a commander's base attack
            game.battle = hold
            game.isatb = 0
        else:
            game.battle.invalidate()
    if game.idebug:
        prout("=== EVENTS from %.2f to %.2f:" % (game.state.date, fintim))
        for i in range(1, NEVENTS):
            if   i == FSNOVA:  proutn("=== Supernova       ")
            elif i == FTBEAM:  proutn("=== T Beam          ")
            elif i == FSNAP:   proutn("=== Snapshot        ")
            elif i == FBATTAK: proutn("=== Base Attack     ")
            elif i == FCDBAS:  proutn("=== Base Destroy    ")
            elif i == FSCMOVE: proutn("=== SC Move         ")
            elif i == FSCDBAS: proutn("=== SC Base Destroy ")
            elif i == FDSPROB: proutn("=== Probe Move      ")
            elif i == FDISTR:  proutn("=== Distress Call   ")
            elif i == FENSLV:  proutn("=== Enslavement     ")
            elif i == FREPRO:  proutn("=== Klingon Build   ")
            if is_scheduled(i):
                prout("%.2f" % (scheduled(i)))
            else:
                prout("never")
    radio_was_broken = damaged(DRADIO)
    hold.i = hold.j = 0
    while True:
        # Select earliest extraneous event, evcode==0 if no events
        evcode = FSPY
        if game.alldone:
            return
        datemin = fintim
        for l in range(1, NEVENTS):
            if game.future[l].date < datemin:
                evcode = l
                if game.idebug:
                    prout("== Event %d fires" % evcode)
                datemin = game.future[l].date
        xtime = datemin-game.state.date
        game.state.date = datemin
        # Decrement Federation resources and recompute remaining time
        game.state.remres -= (game.state.remkl+4*len(game.state.kcmdr))*xtime
        game.recompute()
        if game.state.remtime <= 0:
            finish(FDEPLETE)
            return
        # Any crew left alive?
        if game.state.crew <= 0:
            finish(FCREW)
            return
        # Is life support adequate?
        if damaged(DLIFSUP) and game.condition != "docked":
            if game.lsupres < xtime and game.damage[DLIFSUP] > game.lsupres:
                finish(FLIFESUP)
                return
            game.lsupres -= xtime
            if game.damage[DLIFSUP] <= xtime:
                game.lsupres = game.inlsr
        # Fix devices
        repair = xtime
        if game.condition == "docked":
            repair /= DOCKFAC
        # Don't fix Deathray here
        for l in range(NDEVICES):
            if game.damage[l] > 0.0 and l != DDRAY:
                if game.damage[l]-repair > 0.0:
                    game.damage[l] -= repair
                else:
                    game.damage[l] = 0.0
        # If radio repaired, update star chart and attack reports
        if radio_was_broken and not damaged(DRADIO):
            prout(_("Lt. Uhura- \"Captain, the sub-space radio is working and"))
            prout(_("   surveillance reports are coming in."))
            skip(1)
            if not game.iseenit:
                attackreport(False)
                game.iseenit = True
            rechart()
            prout(_("   The star chart is now up to date.\""))
            skip(1)
        # Cause extraneous event EVCODE to occur
        game.optime -= xtime
        if evcode == FSNOVA: # Supernova
            announce()
            supernova(None)
            schedule(FSNOVA, expran(0.5*game.intime))
            if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
                return
        elif evcode == FSPY: # Check with spy to see if SC should tractor beam
            if game.state.nscrem == 0 or \
                ictbeam or istract or \
                game.condition == "docked" or game.isatb == 1 or game.iscate:
                return
            if game.ientesc or \
                (game.energy<2000 and game.torps<4 and game.shield < 1250) or \
                (damaged(DPHASER) and (damaged(DPHOTON) or game.torps<4)) or \
                (damaged(DSHIELD) and \
                 (game.energy < 2500 or damaged(DPHASER)) and \
                 (game.torps < 5 or damaged(DPHOTON))):
                # Tractor-beam her!
                istract = ictbeam = True
                tractorbeam((game.state.kscmdr-game.quadrant).distance())
            else:
                return
        elif evcode == FTBEAM: # Tractor beam
            if not game.state.kcmdr:
                unschedule(FTBEAM)
                continue
            i = randrange(len(game.state.kcmdr))
            yank = (game.state.kcmdr[i]-game.quadrant).distance()
            if istract or game.condition == "docked" or yank == 0:
                # Drats! Have to reschedule
                schedule(FTBEAM,
                         game.optime + expran(1.5*game.intime/len(game.state.kcmdr)))
                continue
            ictbeam = True
            tractorbeam(yank)
        elif evcode == FSNAP: # Snapshot of the universe (for time warp)
            game.snapsht = copy.deepcopy(game.state)
            game.state.snap = True
            schedule(FSNAP, expran(0.5 * game.intime))
        elif evcode == FBATTAK: # Commander attacks starbase
            if not game.state.kcmdr or not game.state.baseq:
                # no can do
                unschedule(FBATTAK)
                unschedule(FCDBAS)
                continue
            try:
                for ibq in game.state.baseq:
                    for cmdr in game.state.kcmdr:
                        if ibq == cmdr and ibq != game.quadrant and ibq != game.state.kscmdr:
                            raise JumpOut
                else:
                    # no match found -- try later
                    schedule(FBATTAK, expran(0.3*game.intime))
                    unschedule(FCDBAS)
                    continue
            except JumpOut:
                pass
            # commander + starbase combination found -- launch attack
            game.battle = ibq
            schedule(FCDBAS, randreal(1.0, 4.0))
            if game.isatb: # extra time if SC already attacking
                postpone(FCDBAS, scheduled(FSCDBAS)-game.state.date)
            game.future[FBATTAK].date = game.future[FCDBAS].date + expran(0.3*game.intime)
            game.iseenit = False
            if not communicating():
                continue # No warning :-(
            game.iseenit = True
            announce()
            skip(1)
            prout(_("Lt. Uhura-  \"Captain, the starbase in Quadrant %s") % game.battle)
            prout(_("   reports that it is under attack and that it can"))
            prout(_("   hold out only until stardate %d.\"") % (int(scheduled(FCDBAS))))
            if cancelrest():
                return
        elif evcode == FSCDBAS: # Supercommander destroys base
            unschedule(FSCDBAS)
            game.isatb = 2
            if not game.state.galaxy[game.state.kscmdr.i][game.state.kscmdr.j].starbase:
                continue # WAS RETURN!
            hold = game.battle
            game.battle = game.state.kscmdr
            destroybase()
        elif evcode == FCDBAS: # Commander succeeds in destroying base
            if evcode == FCDBAS:
                unschedule(FCDBAS)
                if not game.state.baseq() \
                       or not game.state.galaxy[game.battle.i][game.battle.j].starbase:
                    game.battle.invalidate()
                    continue
                # find the lucky pair
                for cmdr in game.state.kcmdr:
                    if cmdr == game.battle:
                        break
                else:
                    # No action to take after all
                    continue
            destroybase()
        elif evcode == FSCMOVE: # Supercommander moves
            schedule(FSCMOVE, 0.2777)
            if not game.ientesc and not istract and game.isatb != 1 and \
                   (not game.iscate or not game.justin):
                supercommander()
        elif evcode == FDSPROB: # Move deep space probe
            schedule(FDSPROB, 0.01)
            if not game.probe.nexttok():
                if not game.probe.quadrant().valid_quadrant() or \
                    game.state.galaxy[game.probe.quadrant().i][game.probe.quadrant().j].supernova:
                    # Left galaxy or ran into supernova
                    if communicating():
                        announce()
                        skip(1)
                        proutn(_("Lt. Uhura-  \"The deep space probe "))
                        if not game.probe.quadrant().valid_quadrant():
                            prout(_("has left the galaxy.\""))
                        else:
                            prout(_("is no longer transmitting.\""))
                    unschedule(FDSPROB)
                    continue
                if communicating():
                    #announce()
                    skip(1)
                    prout(_("Lt. Uhura-  \"The deep space probe is now in Quadrant %s.\"") % game.probe.quadrant())
            pquad = game.probe.quadrant()
            pdest = game.state.galaxy[pquad.i][pquad.j]
            if communicating():
                game.state.chart[pquad.i][pquad.j].klingons = pdest.klingons
                game.state.chart[pquad.i][pquad.j].starbase = pdest.starbase
                game.state.chart[pquad.i][pquad.j].stars = pdest.stars
                pdest.charted = True
            game.probe.moves -= 1 # One less to travel
            if game.probe.arrived() and game.isarmed and pdest.stars:
                supernova(game.probe.quadrant())     # fire in the hole!
                unschedule(FDSPROB)
                if game.state.galaxy[pquad.i][pquad.j].supernova:
                    return
        elif evcode == FDISTR: # inhabited system issues distress call
            unschedule(FDISTR)
            # try a whole bunch of times to find something suitable
            for i in range(100):
                # need a quadrant which is not the current one,
                # which has some stars which are inhabited and
                # not already under attack, which is not
                # supernova'ed, and which has some Klingons in it
                w = randplace(GALSIZE)
                q = game.state.galaxy[w.i][w.j]
                if not (game.quadrant == w or q.planet == None or \
                      not q.planet.inhabited or \
                      q.supernova or q.status!="secure" or q.klingons<=0):
                    break
            else:
                # can't seem to find one; ignore this call
                if game.idebug:
                    prout("=== Couldn't find location for distress event.")
                continue
            # got one!!  Schedule its enslavement
            ev = schedule(FENSLV, expran(game.intime))
            ev.quadrant = w
            q.status = "distressed"
            # tell the captain about it if we can
            if communicating():
                prout(_("Uhura- Captain, %s in Quadrant %s reports it is under attack") \
                        % (q.planet, repr(w)))
                prout(_("by a Klingon invasion fleet."))
                if cancelrest():
                    return
        elif evcode == FENSLV:                # starsystem is enslaved
            ev = unschedule(FENSLV)
            # see if current distress call still active
            q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
            if q.klingons <= 0:
                q.status = "secure"
                continue
            q.status = "enslaved"
 
            # play stork and schedule the first baby
            ev2 = schedule(FREPRO, expran(2.0 * game.intime))
            ev2.quadrant = ev.quadrant
 
            # report the disaster if we can
            if communicating():
                prout(_("Uhura- We've lost contact with starsystem %s") % \
                        q.planet)
                prout(_("in Quadrant %s.\n") % ev.quadrant)
        elif evcode == FREPRO:                # Klingon reproduces
            # If we ever switch to a real event queue, we'll need to
            # explicitly retrieve and restore the x and y.
            ev = schedule(FREPRO, expran(1.0 * game.intime))
            # see if current distress call still active
            q = game.state.galaxy[ev.quadrant.i][ev.quadrant.j]
            if q.klingons <= 0:
                q.status = "secure"
                continue
            if game.state.remkl >= MAXKLGAME:
                continue                # full right now
            # reproduce one Klingon
            w = ev.quadrant
            m = Coord()
            if game.klhere >= MAXKLQUAD:
                try:
                    # this quadrant not ok, pick an adjacent one
                    for m.i in range(w.i - 1, w.i + 2):
                        for m.j in range(w.j - 1, w.j + 2):
                            if not m.valid_quadrant():
                                continue
                            q = game.state.galaxy[m.i][m.j]
                            # check for this quad ok (not full & no snova)
                            if q.klingons >= MAXKLQUAD or q.supernova:
                                continue
                            raise JumpOut
                    else:
                        continue        # search for eligible quadrant failed
                except JumpOut:
                    w = m
            # deliver the child
            game.state.remkl += 1
            q.klingons += 1
            if game.quadrant == w:
                game.klhere += 1
                game.enemies.append(newkling())
            # recompute time left
            game.recompute()
            if communicating():
                if game.quadrant == w:
                    prout(_("Spock- sensors indicate the Klingons have"))
                    prout(_("launched a warship from %s.") % q.planet)
                else:
                    prout(_("Uhura- Starfleet reports increased Klingon activity"))
                    if q.planet != None:
                        proutn(_("near %s ") % q.planet)
                    prout(_("in Quadrant %s.") % w)
 
def wait():
    "Wait on events."
    game.ididit = False
    while True:
        key = scanner.nexttok()
        if key  != "IHEOL":
            break
        proutn(_("How long? "))
    scanner.chew()
    if key != "IHREAL":
        huh()
        return
    origTime = delay = scanner.real
    if delay <= 0.0:
        return
    if delay >= game.state.remtime or len(game.enemies) != 0:
        proutn(_("Are you sure? "))
        if not ja():
            return
    # Alternate resting periods (events) with attacks
    game.resting = True
    while True:
        if delay <= 0:
            game.resting = False
        if not game.resting:
            prout(_("%d stardates left.") % int(game.state.remtime))
            return
        temp = game.optime = delay
        if len(game.enemies):
            rtime = randreal(1.0, 2.0)
            if rtime < temp:
                temp = rtime
            game.optime = temp
        if game.optime < delay:
            attack(torps_ok=False)
        if game.alldone:
            return
        events()
        game.ididit = True
        if game.alldone:
            return
        delay -= temp
        # Repair Deathray if long rest at starbase
        if origTime-delay >= 9.99 and game.condition == "docked":
            game.damage[DDRAY] = 0.0
        # leave if quadrant supernovas
        if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
            break
    game.resting = False
    game.optime = 0
 
def nova(nov):
    "Star goes nova."
    ncourse = (0.0, 10.5, 12.0, 1.5, 9.0, 0.0, 3.0, 7.5, 6.0, 4.5)
    newc = Coord(); neighbor = Coord(); bump = Coord(0, 0)
    if withprob(0.05):
        # Wow! We've supernova'ed
        supernova(game.quadrant)
        return
    # handle initial nova
    game.quad[nov.i][nov.j] = '.'
    prout(crmena(False, '*', "sector", nov) + _(" novas."))
    game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
    game.state.starkl += 1
    # Set up queue to recursively trigger adjacent stars
    hits = [nov]
    kount = 0
    while hits:
        offset = Coord()
        start = hits.pop()
        for offset.i in range(-1, 1+1):
            for offset.j in range(-1, 1+1):
                if offset.j == 0 and offset.i == 0:
                    continue
                neighbor = start + offset
                if not neighbor.valid_sector():
                    continue
                iquad = game.quad[neighbor.i][neighbor.j]
                # Empty space ends reaction
                if iquad in ('.', '?', ' ', 'T', '#'):
                    pass
                elif iquad == '*': # Affect another star
                    if withprob(0.05):
                        # This star supernovas
                        supernova(game.quadrant)
                        return
                    else:
                        hits.append(neighbor)
                        game.state.galaxy[game.quadrant.i][game.quadrant.j].stars -= 1
                        game.state.starkl += 1
                        proutn(crmena(True, '*', "sector", neighbor))
                        prout(_(" novas."))
                        game.quad[neighbor.i][neighbor.j] = '.'
                        kount += 1
                elif iquad in ('P', '@'): # Destroy planet
                    game.state.galaxy[game.quadrant.i][game.quadrant.j].planet = None
                    if iquad == 'P':
                        game.state.nplankl += 1
                    else:
                        game.state.nworldkl += 1
                    prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
                    game.iplnet.pclass = "destroyed"
                    game.iplnet = None
                    game.plnet.invalidate()
                    if game.landed:
                        finish(FPNOVA)
                        return
                    game.quad[neighbor.i][neighbor.j] = '.'
                elif iquad == 'B': # Destroy base
                    game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase = False
                    game.state.baseq = [x for x in game.state.baseq if x!= game.quadrant]
                    game.base.invalidate()
                    game.state.basekl += 1
                    newcnd()
                    prout(crmena(True, 'B', "sector", neighbor) + _(" destroyed."))
                    game.quad[neighbor.i][neighbor.j] = '.'
                elif iquad in ('E', 'F'): # Buffet ship
                    prout(_("***Starship buffeted by nova."))
                    if game.shldup:
                        if game.shield >= 2000.0:
                            game.shield -= 2000.0
                        else:
                            diff = 2000.0 - game.shield
                            game.energy -= diff
                            game.shield = 0.0
                            game.shldup = False
                            prout(_("***Shields knocked out."))
                            game.damage[DSHIELD] += 0.005*game.damfac*randreal()*diff
                    else:
                        game.energy -= 2000.0
                    if game.energy <= 0:
                        finish(FNOVA)
                        return
                    # add in course nova contributes to kicking starship
                    bump += (game.sector-hits[-1]).sgn()
                elif iquad == 'K': # kill klingon
                    deadkl(neighbor, iquad, neighbor)
                elif iquad in ('C','S','R'): # Damage/destroy big enemies
                    for ll in range(len(game.enemies)):
                        if game.enemies[ll].location == neighbor:
                            break
                    game.enemies[ll].power -= 800.0 # If firepower is lost, die
                    if game.enemies[ll].power <= 0.0:
                        deadkl(neighbor, iquad, neighbor)
                        break
                    newc = neighbor + neighbor - hits[-1]
                    proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged"))
                    if not newc.valid_sector():
                        # can't leave quadrant
                        skip(1)
                        break
                    iquad1 = game.quad[newc.i][newc.j]
                    if iquad1 == ' ':
                        proutn(_(", blasted into ") + crmena(False, ' ', "sector", newc))
                        skip(1)
                        deadkl(neighbor, iquad, newc)
                        break
                    if iquad1 != '.':
                        # can't move into something else
                        skip(1)
                        break
                    proutn(_(", buffeted to Sector %s") % newc)
                    game.quad[neighbor.i][neighbor.j] = '.'
                    game.quad[newc.i][newc.j] = iquad
                    game.enemies[ll].move(newc)
    # Starship affected by nova -- kick it away.
    dist = kount*0.1
    direc = ncourse[3*(bump.i+1)+bump.j+2]
    if direc == 0.0:
        dist = 0.0
    if dist == 0.0:
        return
    scourse = course(bearing=direc, distance=dist)
    game.optime = scourse.time(warp=4)
    skip(1)
    prout(_("Force of nova displaces starship."))
    imove(scourse, noattack=True)
    game.optime = scourse.time(warp=4)
    return
 
def supernova(w):
    "Star goes supernova."
    num = 0; npdead = 0
    if w != None:
        nq = copy.copy(w)
    else:
        # Scheduled supernova -- select star at random.
        stars = 0
        nq = Coord()
        for nq.i in range(GALSIZE):
            for nq.j in range(GALSIZE):
                stars += game.state.galaxy[nq.i][nq.j].stars
        if stars == 0:
            return # nothing to supernova exists
        num = randrange(stars) + 1
        for nq.i in range(GALSIZE):
            for nq.j in range(GALSIZE):
                num -= game.state.galaxy[nq.i][nq.j].stars
                if num <= 0:
                    break
            if num <=0:
                break
        if game.idebug:
            proutn("=== Super nova here?")
            if ja():
                nq = game.quadrant
    if not nq == game.quadrant or game.justin:
        # it isn't here, or we just entered (treat as enroute)
        if communicating():
            skip(1)
            prout(_("Message from Starfleet Command       Stardate %.2f") % game.state.date)
            prout(_("     Supernova in Quadrant %s; caution advised.") % nq)
    else:
        ns = Coord()
        # we are in the quadrant!
        num = randrange(game.state.galaxy[nq.i][nq.j].stars) + 1
        for ns.i in range(QUADSIZE):
            for ns.j in range(QUADSIZE):
                if game.quad[ns.i][ns.j]=='*':
                    num -= 1
                    if num==0:
                        break
            if num==0:
                break
        skip(1)
        prouts(_("***RED ALERT!  RED ALERT!"))
        skip(1)
        prout(_("***Incipient supernova detected at Sector %s") % ns)
        if (ns.i-game.sector.i)**2 + (ns.j-game.sector.j)**2 <= 2.1:
            proutn(_("Emergency override attempts t"))
            prouts("***************")
            skip(1)
            stars()
            game.alldone = True
    # destroy any Klingons in supernovaed quadrant
    kldead = game.state.galaxy[nq.i][nq.j].klingons
    game.state.galaxy[nq.i][nq.j].klingons = 0
    if nq == game.state.kscmdr:
        # did in the Supercommander!
        game.state.nscrem = game.state.kscmdr.i = game.state.kscmdr.j = game.isatb =  0
        game.iscate = False
        unschedule(FSCMOVE)
        unschedule(FSCDBAS)
    survivors = filter(lambda w: w != nq, game.state.kcmdr)
    comkills = len(game.state.kcmdr) - len(survivors)
    game.state.kcmdr = survivors
    kldead -= comkills
    if not game.state.kcmdr:
        unschedule(FTBEAM)
    game.state.remkl -= kldead
    # destroy Romulans and planets in supernovaed quadrant
    nrmdead = game.state.galaxy[nq.i][nq.j].romulans
    game.state.galaxy[nq.i][nq.j].romulans = 0
    game.state.nromrem -= nrmdead
    # Destroy planets
    for loop in range(game.inplan):
        if game.state.planets[loop].quadrant == nq:
            game.state.planets[loop].pclass = "destroyed"
            npdead += 1
    # Destroy any base in supernovaed quadrant
    game.state.baseq = [x for x in game.state.baseq if x != nq]
    # If starship caused supernova, tally up destruction
    if w != None:
        game.state.starkl += game.state.galaxy[nq.i][nq.j].stars
        game.state.basekl += game.state.galaxy[nq.i][nq.j].starbase
        game.state.nplankl += npdead
    # mark supernova in galaxy and in star chart
    if game.quadrant == nq or communicating():
        game.state.galaxy[nq.i][nq.j].supernova = True
    # If supernova destroys last Klingons give special message
    if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0 and not nq == game.quadrant:
        skip(2)
        if w == None:
            prout(_("Lucky you!"))
        proutn(_("A supernova in %s has just destroyed the last Klingons.") % nq)
        finish(FWON)
        return
    # if some Klingons remain, continue or die in supernova
    if game.alldone:
        finish(FSNOVAED)
    return
 
# Code from finish.c ends here.
 
def selfdestruct():
    "Self-destruct maneuver. Finish with a BANG!"
    scanner.chew()
    if damaged(DCOMPTR):
        prout(_("Computer damaged; cannot execute destruct sequence."))
        return
    prouts(_("---WORKING---")); skip(1)
    prouts(_("SELF-DESTRUCT-SEQUENCE-ACTIVATED")); skip(1)
    prouts("   10"); skip(1)
    prouts("       9"); skip(1)
    prouts("          8"); skip(1)
    prouts("             7"); skip(1)
    prouts("                6"); skip(1)
    skip(1)
    prout(_("ENTER-CORRECT-PASSWORD-TO-CONTINUE-"))
    skip(1)
    prout(_("SELF-DESTRUCT-SEQUENCE-OTHERWISE-"))
    skip(1)
    prout(_("SELF-DESTRUCT-SEQUENCE-WILL-BE-ABORTED"))
    skip(1)
    scanner.nexttok()
    if game.passwd != scanner.token:
        prouts(_("PASSWORD-REJECTED;"))
        skip(1)
        prouts(_("CONTINUITY-EFFECTED"))
        skip(2)
        return
    prouts(_("PASSWORD-ACCEPTED")); skip(1)
    prouts("                   5"); skip(1)
    prouts("                      4"); skip(1)
    prouts("                         3"); skip(1)
    prouts("                            2"); skip(1)
    prouts("                              1"); skip(1)
    if withprob(0.15):
        prouts(_("GOODBYE-CRUEL-WORLD"))
        skip(1)
    kaboom()
 
def kaboom():
    stars()
    if game.ship=='E':
        prouts("***")
    prouts(_("********* Entropy of %s maximized *********") % crmshp())
    skip(1)
    stars()
    skip(1)
    if len(game.enemies) != 0:
        whammo = 25.0 * game.energy
        for l in range(len(game.enemies)):
            if game.enemies[l].power*game.enemies[l].kdist <= whammo:
                deadkl(game.enemies[l].location, game.quad[game.enemies[l].location.i][game.enemies[l].location.j], game.enemies[l].location)
    finish(FDILITHIUM)
 
def killrate():
    "Compute our rate of kils over time."
    elapsed = game.state.date - game.indate
    if elapsed == 0:        # Avoid divide-by-zero error if calculated on turn 0
        return 0
    else:
        starting = (game.inkling + game.incom + game.inscom)
        remaining = (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)
        return (starting - remaining)/elapsed
 
def badpoints():
    "Compute demerits."
    badpt = 5.0*game.state.starkl + \
            game.casual + \
            10.0*game.state.nplankl + \
            300*game.state.nworldkl + \
            45.0*game.nhelp +\
            100.0*game.state.basekl +\
            3.0*game.abandoned
    if game.ship == 'F':
        badpt += 100.0
    elif game.ship == None:
        badpt += 200.0
    return badpt
 
def finish(ifin):
    # end the game, with appropriate notfications
    igotit = False
    game.alldone = True
    skip(3)
    prout(_("It is stardate %.1f.") % game.state.date)
    skip(1)
    if ifin == FWON: # Game has been won
        if game.state.nromrem != 0:
            prout(_("The remaining %d Romulans surrender to Starfleet Command.") %
                  game.state.nromrem)
 
        prout(_("You have smashed the Klingon invasion fleet and saved"))
        prout(_("the Federation."))
        game.gamewon = True
        if game.alive:
            badpt = badpoints()
            if badpt < 100.0:
                badpt = 0.0        # Close enough!
            # killsPerDate >= RateMax
            if game.state.date-game.indate < 5.0 or \
                killrate() >= 0.1*game.skill*(game.skill+1.0) + 0.1 + 0.008*badpt:
                skip(1)
                prout(_("In fact, you have done so well that Starfleet Command"))
                if game.skill == SKILL_NOVICE:
                    prout(_("promotes you one step in rank from \"Novice\" to \"Fair\"."))
                elif game.skill == SKILL_FAIR:
                    prout(_("promotes you one step in rank from \"Fair\" to \"Good\"."))
                elif game.skill == SKILL_GOOD:
                    prout(_("promotes you one step in rank from \"Good\" to \"Expert\"."))
                elif game.skill == SKILL_EXPERT:
                    prout(_("promotes you to Commodore Emeritus."))
                    skip(1)
                    prout(_("Now that you think you're really good, try playing"))
                    prout(_("the \"Emeritus\" game. It will splatter your ego."))
                elif game.skill == SKILL_EMERITUS:
                    skip(1)
                    proutn(_("Computer-  "))
                    prouts(_("ERROR-ERROR-ERROR-ERROR"))
                    skip(2)
                    prouts(_("  YOUR-SKILL-HAS-EXCEEDED-THE-CAPACITY-OF-THIS-PROGRAM"))
                    skip(1)
                    prouts(_("  THIS-PROGRAM-MUST-SURVIVE"))
                    skip(1)
                    prouts(_("  THIS-PROGRAM-MUST-SURVIVE"))
                    skip(1)
                    prouts(_("  THIS-PROGRAM-MUST-SURVIVE"))
                    skip(1)
                    prouts(_("  THIS-PROGRAM-MUST?- MUST ? - SUR? ? -?  VI"))
                    skip(2)
                    prout(_("Now you can retire and write your own Star Trek game!"))
                    skip(1)
                elif game.skill >= SKILL_EXPERT:
                    if game.thawed and not game.idebug:
                        prout(_("You cannot get a citation, so..."))
                    else:
                        proutn(_("Do you want your Commodore Emeritus Citation printed? "))
                        scanner.chew()
                        if ja():
                            igotit = True
            # Only grant long life if alive (original didn't!)
            skip(1)
            prout(_("LIVE LONG AND PROSPER."))
        score()
        if igotit:
            plaque()
        return
    elif ifin == FDEPLETE: # Federation Resources Depleted
        prout(_("Your time has run out and the Federation has been"))
        prout(_("conquered.  Your starship is now Klingon property,"))
        prout(_("and you are put on trial as a war criminal.  On the"))
        proutn(_("basis of your record, you are "))
        if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)*3.0 > (game.inkling + game.incom + game.inscom):
            prout(_("acquitted."))
            skip(1)
            prout(_("LIVE LONG AND PROSPER."))
        else:
            prout(_("found guilty and"))
            prout(_("sentenced to death by slow torture."))
            game.alive = False
        score()
        return
    elif ifin == FLIFESUP:
        prout(_("Your life support reserves have run out, and"))
        prout(_("you die of thirst, starvation, and asphyxiation."))
        prout(_("Your starship is a derelict in space."))
    elif ifin == FNRG:
        prout(_("Your energy supply is exhausted."))
        skip(1)
        prout(_("Your starship is a derelict in space."))
    elif ifin == FBATTLE:
        prout(_("The %s has been destroyed in battle.") % crmshp())
        skip(1)
        prout(_("Dulce et decorum est pro patria mori."))
    elif ifin == FNEG3:
        prout(_("You have made three attempts to cross the negative energy"))
        prout(_("barrier which surrounds the galaxy."))
        skip(1)
        prout(_("Your navigation is abominable."))
        score()
    elif ifin == FNOVA:
        prout(_("Your starship has been destroyed by a nova."))
        prout(_("That was a great shot."))
        skip(1)
    elif ifin == FSNOVAED:
        prout(_("The %s has been fried by a supernova.") % crmshp())
        prout(_("...Not even cinders remain..."))
    elif ifin == FABANDN:
        prout(_("You have been captured by the Klingons. If you still"))
        prout(_("had a starbase to be returned to, you would have been"))
        prout(_("repatriated and given another chance. Since you have"))
        prout(_("no starbases, you will be mercilessly tortured to death."))
    elif ifin == FDILITHIUM:
        prout(_("Your starship is now an expanding cloud of subatomic particles"))
    elif ifin == FMATERIALIZE:
        prout(_("Starbase was unable to re-materialize your starship."))
        prout(_("Sic transit gloria mundi"))
    elif ifin == FPHASER:
        prout(_("The %s has been cremated by its own phasers.") % crmshp())
    elif ifin == FLOST:
        prout(_("You and your landing party have been"))
        prout(_("converted to energy, disipating through space."))
    elif ifin == FMINING:
        prout(_("You are left with your landing party on"))
        prout(_("a wild jungle planet inhabited by primitive cannibals."))
        skip(1)
        prout(_("They are very fond of \"Captain Kirk\" soup."))
        skip(1)
        prout(_("Without your leadership, the %s is destroyed.") % crmshp())
    elif ifin == FDPLANET:
        prout(_("You and your mining party perish."))
        skip(1)
        prout(_("That was a great shot."))
        skip(1)
    elif ifin == FSSC:
        prout(_("The Galileo is instantly annihilated by the supernova."))
        prout(_("You and your mining party are atomized."))
        skip(1)
        prout(_("Mr. Spock takes command of the %s and") % crmshp())
        prout(_("joins the Romulans, wreaking terror on the Federation."))
    elif ifin == FPNOVA:
        prout(_("You and your mining party are atomized."))
        skip(1)
        prout(_("Mr. Spock takes command of the %s and") % crmshp())
        prout(_("joins the Romulans, wreaking terror on the Federation."))
    elif ifin == FSTRACTOR:
        prout(_("The shuttle craft Galileo is also caught,"))
        prout(_("and breaks up under the strain."))
        skip(1)
        prout(_("Your debris is scattered for millions of miles."))
        prout(_("Without your leadership, the %s is destroyed.") % crmshp())
    elif ifin == FDRAY:
        prout(_("The mutants attack and kill Spock."))
        prout(_("Your ship is captured by Klingons, and"))
        prout(_("your crew is put on display in a Klingon zoo."))
    elif ifin == FTRIBBLE:
        prout(_("Tribbles consume all remaining water,"))
        prout(_("food, and oxygen on your ship."))
        skip(1)
        prout(_("You die of thirst, starvation, and asphyxiation."))
        prout(_("Your starship is a derelict in space."))
    elif ifin == FHOLE:
        prout(_("Your ship is drawn to the center of the black hole."))
        prout(_("You are crushed into extremely dense matter."))
    elif ifin == FCREW:
        prout(_("Your last crew member has died."))
    if game.ship == 'F':
        game.ship = None
    elif game.ship == 'E':
        game.ship = 'F'
    game.alive = False
    if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0:
        goodies = game.state.remres/game.inresor
        baddies = (game.state.remkl + 2.0*len(game.state.kcmdr))/(game.inkling+2.0*game.incom)
        if goodies/baddies >= randreal(1.0, 1.5):
            prout(_("As a result of your actions, a treaty with the Klingon"))
            prout(_("Empire has been signed. The terms of the treaty are"))
            if goodies/baddies >= randreal(3.0):
                prout(_("favorable to the Federation."))
                skip(1)
                prout(_("Congratulations!"))
            else:
                prout(_("highly unfavorable to the Federation."))
        else:
            prout(_("The Federation will be destroyed."))
    else:
        prout(_("Since you took the last Klingon with you, you are a"))
        prout(_("martyr and a hero. Someday maybe they'll erect a"))
        prout(_("statue in your memory. Rest in peace, and try not"))
        prout(_("to think about pigeons."))
        game.gamewon = True
    score()
 
def score():
    "Compute player's score."
    timused = game.state.date - game.indate
    if (timused == 0 or (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) != 0) and timused < 5.0:
        timused = 5.0
    game.perdate = killrate()
    ithperd = 500*game.perdate + 0.5
    iwon = 0
    if game.gamewon:
        iwon = 100*game.skill
    if game.ship == 'E':
        klship = 0
    elif game.ship == 'F':
        klship = 1
    else:
        klship = 2
    game.score = 10*(game.inkling - game.state.remkl) \
             + 50*(game.incom - len(game.state.kcmdr)) \
             + ithperd + iwon \
             + 20*(game.inrom - game.state.nromrem) \
             + 200*(game.inscom - game.state.nscrem) \
                 - game.state.nromrem \
             - badpoints()
    if not game.alive:
        game.score -= 200
    skip(2)
    prout(_("Your score --"))
    if game.inrom - game.state.nromrem:
        prout(_("%6d Romulans destroyed                 %5d") %
              (game.inrom - game.state.nromrem, 20*(game.inrom - game.state.nromrem)))
    if game.state.nromrem and game.gamewon:
        prout(_("%6d Romulans captured                  %5d") %
              (game.state.nromrem, game.state.nromrem))
    if game.inkling - game.state.remkl:
        prout(_("%6d ordinary Klingons destroyed        %5d") %
              (game.inkling - game.state.remkl, 10*(game.inkling - game.state.remkl)))
    if game.incom - len(game.state.kcmdr):
        prout(_("%6d Klingon commanders destroyed       %5d") %
              (game.incom - len(game.state.kcmdr), 50*(game.incom - len(game.state.kcmdr))))
    if game.inscom - game.state.nscrem:
        prout(_("%6d Super-Commander destroyed          %5d") %
              (game.inscom - game.state.nscrem, 200*(game.inscom - game.state.nscrem)))
    if ithperd:
        prout(_("%6.2f Klingons per stardate              %5d") %
              (game.perdate, ithperd))
    if game.state.starkl:
        prout(_("%6d stars destroyed by your action     %5d") %
              (game.state.starkl, -5*game.state.starkl))
    if game.state.nplankl:
        prout(_("%6d planets destroyed by your action   %5d") %
              (game.state.nplankl, -10*game.state.nplankl))
    if (game.options & OPTION_WORLDS) and game.state.nworldkl:
        prout(_("%6d inhabited planets destroyed by your action   %5d") %
              (game.state.nworldkl, -300*game.state.nworldkl))
    if game.state.basekl:
        prout(_("%6d bases destroyed by your action     %5d") %
              (game.state.basekl, -100*game.state.basekl))
    if game.nhelp:
        prout(_("%6d calls for help from starbase       %5d") %
              (game.nhelp, -45*game.nhelp))
    if game.casual:
        prout(_("%6d casualties incurred                %5d") %
              (game.casual, -game.casual))
    if game.abandoned:
        prout(_("%6d crew abandoned in space            %5d") %
              (game.abandoned, -3*game.abandoned))
    if klship:
        prout(_("%6d ship(s) lost or destroyed          %5d") %
              (klship, -100*klship))
    if not game.alive:
        prout(_("Penalty for getting yourself killed        -200"))
    if game.gamewon:
        proutn(_("Bonus for winning "))
        if game.skill   == SKILL_NOVICE:        proutn(_("Novice game  "))
        elif game.skill == SKILL_FAIR:          proutn(_("Fair game    "))
        elif game.skill ==  SKILL_GOOD:         proutn(_("Good game    "))
        elif game.skill ==  SKILL_EXPERT:        proutn(_("Expert game  "))
        elif game.skill ==  SKILL_EMERITUS:        proutn(_("Emeritus game"))
        prout("           %5d" % iwon)
    skip(1)
    prout(_("TOTAL SCORE                               %5d") % game.score)
 
def plaque():
    "Emit winner's commemmorative plaque."
    skip(2)
    while True:
        proutn(_("File or device name for your plaque: "))
        winner = cgetline()
        try:
            fp = open(winner, "w")
            break
        except IOError:
            prout(_("Invalid name."))
 
    proutn(_("Enter name to go on plaque (up to 30 characters): "))
    winner = cgetline()
    # The 38 below must be 64 for 132-column paper
    nskip = 38 - len(winner)/2
    fp.write("\n\n\n\n")
    # --------DRAW ENTERPRISE PICTURE.
    fp.write("                                       EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n" )
    fp.write("                                      EEE                      E  : :                                         :  E\n" )
    fp.write("                                    EE   EEE                   E  : :                   NCC-1701              :  E\n")
    fp.write("EEEEEEEEEEEEEEEE        EEEEEEEEEEEEEEE  : :                              : E\n")
    fp.write(" E                                     EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n")
    fp.write("                      EEEEEEEEE               EEEEEEEEEEEEE                 E  E\n")
    fp.write("                               EEEEEEE   EEEEE    E          E              E  E\n")
    fp.write("                                      EEE           E          E            E  E\n")
    fp.write("                                                       E         E          E  E\n")
    fp.write("                                                         EEEEEEEEEEEEE      E  E\n")
    fp.write("                                                      EEE :           EEEEEEE  EEEEEEEE\n")
    fp.write("                                                    :E    :                 EEEE       E\n")
    fp.write("                                                   .-E   -:-----                       E\n")
    fp.write("                                                    :E    :                            E\n")
    fp.write("                                                      EE  :                    EEEEEEEE\n")
    fp.write("                                                       EEEEEEEEEEEEEEEEEEEEEEE\n")
    fp.write("\n\n\n")
    fp.write(_("                                                       U. S. S. ENTERPRISE\n"))
    fp.write("\n\n\n\n")
    fp.write(_("                                  For demonstrating outstanding ability as a starship captain\n"))
    fp.write("\n")
    fp.write(_("                                                Starfleet Command bestows to you\n"))
    fp.write("\n")
    fp.write("%*s%s\n\n" % (nskip, "", winner))
    fp.write(_("                                                           the rank of\n\n"))
    fp.write(_("                                                       \"Commodore Emeritus\"\n\n"))
    fp.write("                                                          ")
    if game.skill ==  SKILL_EXPERT:
        fp.write(_(" Expert level\n\n"))
    elif game.skill == SKILL_EMERITUS:
        fp.write(_("Emeritus level\n\n"))
    else:
        fp.write(_(" Cheat level\n\n"))
    timestring = time.ctime()
    fp.write(_("                                                 This day of %.6s %.4s, %.8s\n\n") %
                    (timestring+4, timestring+20, timestring+11))
    fp.write(_("                                                        Your score:  %d\n\n") % game.score)
    fp.write(_("                                                    Klingons per stardate:  %.2f\n") % game.perdate)
    fp.close()
 
# Code from io.c begins here
 
rows = linecount = 0        # for paging
stdscr = None
replayfp = None
fullscreen_window = None
srscan_window     = None
report_window     = None
status_window     = None
lrscan_window     = None
message_window    = None
prompt_window     = None
curwnd = None
 
def iostart():
    global stdscr, rows
    "for some recent versions of python2, the following enables UTF8"
    "for the older ones we probably need to set C locale, and the python3"
    "has no problems at all"
    if sys.version_info[0] < 3:
        import locale
        locale.setlocale(locale.LC_ALL, "")
    gettext.bindtextdomain("sst", "/usr/local/share/locale")
    gettext.textdomain("sst")
    if not (game.options & OPTION_CURSES):
        ln_env = os.getenv("LINES")
        if ln_env:
            rows = ln_env
        else:
            rows = 25
    else:
        stdscr = curses.initscr()
        stdscr.keypad(True)
        curses.nonl()
        curses.cbreak()
        if game.options & OPTION_COLOR:
            curses.start_color()
            curses.use_default_colors()
            curses.init_pair(curses.COLOR_BLACK,   curses.COLOR_BLACK, -1)
            curses.init_pair(curses.COLOR_GREEN,   curses.COLOR_GREEN, -1)
            curses.init_pair(curses.COLOR_RED,     curses.COLOR_RED, -1)
            curses.init_pair(curses.COLOR_CYAN,    curses.COLOR_CYAN, -1)
            curses.init_pair(curses.COLOR_WHITE,   curses.COLOR_WHITE, -1)
            curses.init_pair(curses.COLOR_MAGENTA, curses.COLOR_MAGENTA, -1)
            curses.init_pair(curses.COLOR_BLUE,    curses.COLOR_BLUE, -1)
            curses.init_pair(curses.COLOR_YELLOW,  curses.COLOR_YELLOW, -1)
        global fullscreen_window, srscan_window, report_window, status_window
        global lrscan_window, message_window, prompt_window
        (rows, columns)   = stdscr.getmaxyx()
        fullscreen_window = stdscr
        srscan_window     = curses.newwin(12, 25, 0,       0)
        report_window     = curses.newwin(11, 0,  1,       25)
        status_window     = curses.newwin(10, 0,  1,       39)
        lrscan_window     = curses.newwin(5,  0,  0,       64)
        message_window    = curses.newwin(0,  0,  12,      0)
        prompt_window     = curses.newwin(1,  0,  rows-2,  0)
        message_window.scrollok(True)
        setwnd(fullscreen_window)
 
def ioend():
    "Wrap up I/O."
    if game.options & OPTION_CURSES:
        stdscr.keypad(False)
        curses.echo()
        curses.nocbreak()
        curses.endwin()
 
def waitfor():
    "Wait for user action -- OK to do nothing if on a TTY"
    if game.options & OPTION_CURSES:
        stdscr.getch()
 
def announce():
    skip(1)
    prouts(_("[ANNOUNCEMENT ARRIVING...]"))
    skip(1)
 
def pause_game():
    if game.skill > SKILL_FAIR:
        prompt = _("[CONTINUE?]")
    else:
        prompt = _("[PRESS ENTER TO CONTINUE]")
 
    if game.options & OPTION_CURSES:
        drawmaps(0)
        setwnd(prompt_window)
        prompt_window.clear()
        prompt_window.addstr(prompt)
        prompt_window.getstr()
        prompt_window.clear()
        prompt_window.refresh()
        setwnd(message_window)
    else:
        global linecount
        sys.stdout.write('\n')
        proutn(prompt)
        if not replayfp:
            input()
        sys.stdout.write('\n' * rows)
        linecount = 0
 
def skip(i):
    "Skip i lines.  Pause game if this would cause a scrolling event."
    for dummy in range(i):
        if game.options & OPTION_CURSES:
            (y, x) = curwnd.getyx()
            try:
                curwnd.move(y+1, 0)
            except curses.error:
                pass
        else:
            global linecount
            linecount += 1
            if rows and linecount >= rows:
                pause_game()
            else:
                sys.stdout.write('\n')
 
def proutn(line):
    "Utter a line with no following line feed."
    if game.options & OPTION_CURSES:
        (y, x) = curwnd.getyx()
        (my, mx) = curwnd.getmaxyx()
        if curwnd == message_window and y >= my - 2:
            pause_game()
            clrscr()
        # Uncomment this to debug curses problems
        if logfp:
            logfp.write("#curses: at %s proutn(%s)\n" % ((y, x), repr(line)))
        curwnd.addstr(line)
        curwnd.refresh()
    else:
        sys.stdout.write(line)
        sys.stdout.flush()
 
def prout(line):
    proutn(line)
    skip(1)
 
def prouts(line):
    "Emit slowly!"
    for c in line:
        if not replayfp or replayfp.closed:        # Don't slow down replays
            time.sleep(0.03)
        proutn(c)
        if game.options & OPTION_CURSES:
            curwnd.refresh()
        else:
            sys.stdout.flush()
    if not replayfp or replayfp.closed:
        time.sleep(0.03)
 
def cgetline():
    "Get a line of input."
    if game.options & OPTION_CURSES:
        line = curwnd.getstr().decode('utf-8') + "\n"
        curwnd.refresh()
    else:
        if replayfp and not replayfp.closed:
            while True:
                line = replayfp.readline()
                proutn(line)
                if line == '':
                    prout("*** Replay finished")
                    replayfp.close()
                    break
                elif line[0] != "#":
                    break
        else:
            line = eval(input()) + "\n"
    if logfp:
        logfp.write(line)
    return line
 
def setwnd(wnd):
    "Change windows -- OK for this to be a no-op in tty mode."
    global curwnd
    if game.options & OPTION_CURSES:
        # Uncomment this to debug curses problems
        if logfp:
            if wnd == fullscreen_window:
                legend = "fullscreen"
            elif wnd == srscan_window:
                legend = "srscan"
            elif wnd == report_window:
                legend = "report"
            elif wnd == status_window:
                legend = "status"
            elif wnd == lrscan_window:
                legend = "lrscan"
            elif wnd == message_window:
                legend = "message"
            elif wnd == prompt_window:
                legend = "prompt"
            else:
                legend = "unknown"
            logfp.write("#curses: setwnd(%s)\n" % legend)
        curwnd = wnd
        # Some curses implementations get confused when you try this.
        try:
            curses.curs_set(wnd in (fullscreen_window, message_window, prompt_window))
        except curses.error:
            pass
 
def clreol():
    "Clear to end of line -- can be a no-op in tty mode"
    if game.options & OPTION_CURSES:
        curwnd.clrtoeol()
        curwnd.refresh()
 
def clrscr():
    "Clear screen -- can be a no-op in tty mode."
    global linecount
    if game.options & OPTION_CURSES:
        curwnd.clear()
        curwnd.move(0, 0)
        curwnd.refresh()
    linecount = 0
 
def textcolor(color=DEFAULT):
    if game.options & OPTION_COLOR:
        if color == DEFAULT:
            curwnd.attrset(0)
        elif color ==  BLACK:
            curwnd.attron(curses.color_pair(curses.COLOR_BLACK))
        elif color ==  BLUE:
            curwnd.attron(curses.color_pair(curses.COLOR_BLUE))
        elif color ==  GREEN:
            curwnd.attron(curses.color_pair(curses.COLOR_GREEN))
        elif color ==  CYAN:
            curwnd.attron(curses.color_pair(curses.COLOR_CYAN))
        elif color ==  RED:
            curwnd.attron(curses.color_pair(curses.COLOR_RED))
        elif color ==  MAGENTA:
            curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA))
        elif color ==  BROWN:
            curwnd.attron(curses.color_pair(curses.COLOR_YELLOW))
        elif color ==  LIGHTGRAY:
            curwnd.attron(curses.color_pair(curses.COLOR_WHITE))
        elif color ==  DARKGRAY:
            curwnd.attron(curses.color_pair(curses.COLOR_BLACK) | curses.A_BOLD)
        elif color ==  LIGHTBLUE:
            curwnd.attron(curses.color_pair(curses.COLOR_BLUE) | curses.A_BOLD)
        elif color ==  LIGHTGREEN:
            curwnd.attron(curses.color_pair(curses.COLOR_GREEN) | curses.A_BOLD)
        elif color ==  LIGHTCYAN:
            curwnd.attron(curses.color_pair(curses.COLOR_CYAN) | curses.A_BOLD)
        elif color ==  LIGHTRED:
            curwnd.attron(curses.color_pair(curses.COLOR_RED) | curses.A_BOLD)
        elif color ==  LIGHTMAGENTA:
            curwnd.attron(curses.color_pair(curses.COLOR_MAGENTA) | curses.A_BOLD)
        elif color ==  YELLOW:
            curwnd.attron(curses.color_pair(curses.COLOR_YELLOW) | curses.A_BOLD)
        elif color ==  WHITE:
            curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD)
 
def highvideo():
    if game.options & OPTION_COLOR:
        curwnd.attron(curses.A_REVERSE)
 
#
# Things past this point have policy implications.
#
 
def drawmaps(mode):
    "Hook to be called after moving to redraw maps."
    if game.options & OPTION_CURSES:
        if mode == 1:
            sensor()
        setwnd(srscan_window)
        curwnd.move(0, 0)
        srscan()
        if mode != 2:
            setwnd(status_window)
            status_window.clear()
            status_window.move(0, 0)
            setwnd(report_window)
            report_window.clear()
            report_window.move(0, 0)
            status()
            setwnd(lrscan_window)
            lrscan_window.clear()
            lrscan_window.move(0, 0)
            lrscan(silent=False)
 
def put_srscan_sym(w, sym):
    "Emit symbol for short-range scan."
    srscan_window.move(w.i+1, w.j*2+2)
    srscan_window.addch(sym)
    srscan_window.refresh()
 
def boom(w):
    "Enemy fall down, go boom."
    if game.options & OPTION_CURSES:
        drawmaps(2)
        setwnd(srscan_window)
        srscan_window.attron(curses.A_REVERSE)
        put_srscan_sym(w, game.quad[w.i][w.j])
        #sound(500)
        #time.sleep(1.0)
        #nosound()
        srscan_window.attroff(curses.A_REVERSE)
        put_srscan_sym(w, game.quad[w.i][w.j])
        curses.delay_output(500)
        setwnd(message_window)
 
def warble():
    "Sound and visual effects for teleportation."
    if game.options & OPTION_CURSES:
        drawmaps(2)
        setwnd(message_window)
        #sound(50)
    prouts("     . . . . .     ")
    if game.options & OPTION_CURSES:
        #curses.delay_output(1000)
        #nosound()
        pass
 
def tracktorpedo(w, step, i, n, iquad):
    "Torpedo-track animation."
    if not game.options & OPTION_CURSES:
        if step == 1:
            if n != 1:
                skip(1)
                proutn(_("Track for torpedo number %d-  ") % (i+1))
            else:
                skip(1)
                proutn(_("Torpedo track- "))
        elif step==4 or step==9:
            skip(1)
        proutn("%s   " % w)
    else:
        if not damaged(DSRSENS) or game.condition=="docked":
            if i != 0 and step == 1:
                drawmaps(2)
                time.sleep(0.4)
            if (iquad=='.') or (iquad==' '):
                put_srscan_sym(w, '+')
                #sound(step*10)
                #time.sleep(0.1)
                #nosound()
                put_srscan_sym(w, iquad)
            else:
                curwnd.attron(curses.A_REVERSE)
                put_srscan_sym(w, iquad)
                #sound(500)
                #time.sleep(1.0)
                #nosound()
                curwnd.attroff(curses.A_REVERSE)
                put_srscan_sym(w, iquad)
        else:
            proutn("%s   " % w)
 
def makechart():
    "Display the current galaxy chart."
    if game.options & OPTION_CURSES:
        setwnd(message_window)
        message_window.clear()
    chart()
    if game.options & OPTION_TTY:
        skip(1)
 
NSYM        = 14
 
def prstat(txt, data):
    proutn(txt)
    if game.options & OPTION_CURSES:
        skip(1)
        setwnd(status_window)
    else:
        proutn(" " * (NSYM - len(txt)))
    proutn(data)
    skip(1)
    if game.options & OPTION_CURSES:
        setwnd(report_window)
 
# Code from moving.c begins here
 
def imove(icourse=None, noattack=False):
    "Movement execution for warp, impulse, supernova, and tractor-beam events."
    w = Coord()
 
    def newquadrant(noattack):
        # Leaving quadrant -- allow final enemy attack
        # Don't do it if being pushed by Nova
        if len(game.enemies) != 0 and not noattack:
            newcnd()
            for enemy in game.enemies:
                finald = (w - enemy.location).distance()
                enemy.kavgd = 0.5 * (finald + enemy.kdist)
            # Stas Sergeev added the condition
            # that attacks only happen if Klingons
            # are present and your skill is good.
            if game.skill > SKILL_GOOD and game.klhere > 0 and not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
                attack(torps_ok=False)
            if game.alldone:
                return
        # check for edge of galaxy
        kinks = 0
        while True:
            kink = False
            if icourse.final.i < 0:
                icourse.final.i = -icourse.final.i
                kink = True
            if icourse.final.j < 0:
                icourse.final.j = -icourse.final.j
                kink = True
            if icourse.final.i >= GALSIZE*QUADSIZE:
                icourse.final.i = (GALSIZE*QUADSIZE*2) - icourse.final.i
                kink = True
            if icourse.final.j >= GALSIZE*QUADSIZE:
                icourse.final.j = (GALSIZE*QUADSIZE*2) - icourse.final.j
                kink = True
            if kink:
                kinks += 1
            else:
                break
        if kinks:
            game.nkinks += 1
            if game.nkinks == 3:
                # Three strikes -- you're out!
                finish(FNEG3)
                return
            skip(1)
            prout(_("YOU HAVE ATTEMPTED TO CROSS THE NEGATIVE ENERGY BARRIER"))
            prout(_("AT THE EDGE OF THE GALAXY.  THE THIRD TIME YOU TRY THIS,"))
            prout(_("YOU WILL BE DESTROYED."))
        # Compute final position in new quadrant
        if trbeam: # Don't bother if we are to be beamed
            return
        game.quadrant = icourse.final.quadrant()
        game.sector = icourse.final.sector()
        skip(1)
        prout(_("Entering Quadrant %s.") % game.quadrant)
        game.quad[game.sector.i][game.sector.j] = game.ship
        newqad()
        if game.skill>SKILL_NOVICE:
            attack(torps_ok=False)
 
    def check_collision(h):
        iquad = game.quad[h.i][h.j]
        if iquad != '.':
            # object encountered in flight path
            stopegy = 50.0*icourse.distance/game.optime
            if iquad in ('T', 'K', 'C', 'S', 'R', '?'):
                for enemy in game.enemies:
                    if enemy.location == game.sector:
                        break
                collision(rammed=False, enemy=enemy)
                return True
            elif iquad == ' ':
                skip(1)
                prouts(_("***RED ALERT!  RED ALERT!"))
                skip(1)
                proutn("***" + crmshp())
                proutn(_(" pulled into black hole at Sector %s") % h)
                # Getting pulled into a black hole was certain
                # death in Almy's original.  Stas Sergeev added a
                # possibility that you'll get timewarped instead.
                n=0
                for m in range(NDEVICES):
                    if game.damage[m]>0:
                        n += 1
                probf=math.pow(1.4,(game.energy+game.shield)/5000.0-1.0)*math.pow(1.3,1.0/(n+1)-1.0)
                if (game.options & OPTION_BLKHOLE) and withprob(1-probf):
                    timwrp()
                else:
                    finish(FHOLE)
                return True
            else:
                # something else
                skip(1)
                proutn(crmshp())
                if iquad == '#':
                    prout(_(" encounters Tholian web at %s;") % h)
                else:
                    prout(_(" blocked by object at %s;") % h)
                proutn(_("Emergency stop required "))
                prout(_("%2d units of energy.") % int(stopegy))
                game.energy -= stopegy
                if game.energy <= 0:
                    finish(FNRG)
                return True
        return False
 
    trbeam = False
    if game.inorbit:
        prout(_("Helmsman Sulu- \"Leaving standard orbit.\""))
        game.inorbit = False
    # If tractor beam is to occur, don't move full distance
    if game.state.date+game.optime >= scheduled(FTBEAM):
        trbeam = True
        game.condition = "red"
        icourse.distance = icourse.distance*(scheduled(FTBEAM)-game.state.date)/game.optime + 0.1
        game.optime = scheduled(FTBEAM) - game.state.date + 1e-5
    # Move out
    game.quad[game.sector.i][game.sector.j] = '.'
    for m in range(icourse.moves):
        icourse.nexttok()
        w = icourse.sector()
        if icourse.origin.quadrant() != icourse.location.quadrant():
            newquadrant(noattack)
            break
        elif check_collision(w):
            print("Collision detected")
            break
        else:
            game.sector = w
    # We're in destination quadrant -- compute new average enemy distances
    game.quad[game.sector.i][game.sector.j] = game.ship
    if game.enemies:
        for enemy in game.enemies:
            finald = (w-enemy.location).distance()
            enemy.kavgd = 0.5 * (finald + enemy.kdist)
            enemy.kdist = finald
        sortenemies()
        if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
            attack(torps_ok=False)
        for enemy in game.enemies:
            enemy.kavgd = enemy.kdist
    newcnd()
    drawmaps(0)
    setwnd(message_window)
    return
 
def dock(verbose):
    "Dock our ship at a starbase."
    scanner.chew()
    if game.condition == "docked" and verbose:
        prout(_("Already docked."))
        return
    if game.inorbit:
        prout(_("You must first leave standard orbit."))
        return
    if not game.base.is_valid() or abs(game.sector.i-game.base.i) > 1 or abs(game.sector.j-game.base.j) > 1:
        prout(crmshp() + _(" not adjacent to base."))
        return
    game.condition = "docked"
    if "verbose":
        prout(_("Docked."))
    game.ididit = True
    if game.energy < game.inenrg:
        game.energy = game.inenrg
    game.shield = game.inshld
    game.torps = game.intorps
    game.lsupres = game.inlsr
    game.state.crew = FULLCREW
    if not damaged(DRADIO) and \
        ((is_scheduled(FCDBAS) or game.isatb == 1) and not game.iseenit):
        # get attack report from base
        prout(_("Lt. Uhura- \"Captain, an important message from the starbase:\""))
        attackreport(False)
        game.iseenit = True
 
def cartesian(loc1=None, loc2=None):
    if loc1 is None:
        return game.quadrant * QUADSIZE + game.sector
    elif loc2 is None:
        return game.quadrant * QUADSIZE + loc1
    else:
        return loc1 * QUADSIZE + loc2
 
def getcourse(isprobe):
    "Get a course and distance from the user."
    key = 0
    dquad = copy.copy(game.quadrant)
    navmode = "unspecified"
    itemp = "curt"
    dsect = Coord()
    iprompt = False
    if game.landed and not isprobe:
        prout(_("Dummy! You can't leave standard orbit until you"))
        proutn(_("are back aboard the ship."))
        scanner.chew()
        raise TrekError
    while navmode == "unspecified":
        if damaged(DNAVSYS):
            if isprobe:
                prout(_("Computer damaged; manual navigation only"))
            else:
                prout(_("Computer damaged; manual movement only"))
            scanner.chew()
            navmode = "manual"
            key = "IHEOL"
            break
        key = scanner.nexttok()
        if key == "IHEOL":
            proutn(_("Manual or automatic- "))
            iprompt = True
            scanner.chew()
        elif key == "IHALPHA":
            if scanner.sees("manual"):
                navmode = "manual"
                key = scanner.nexttok()
                break
            elif scanner.sees("automatic"):
                navmode = "automatic"
                key = scanner.nexttok()
                break
            else:
                huh()
                scanner.chew()
                raise TrekError
        else: # numeric
            if isprobe:
                prout(_("(Manual navigation assumed.)"))
            else:
                prout(_("(Manual movement assumed.)"))
            navmode = "manual"
            break
    delta = Coord()
    if navmode == "automatic":
        while key == "IHEOL":
            if isprobe:
                proutn(_("Target quadrant or quadrant&sector- "))
            else:
                proutn(_("Destination sector or quadrant&sector- "))
            scanner.chew()
            iprompt = True
            key = scanner.nexttok()
        if key != "IHREAL":
            huh()
            raise TrekError
        xi = int(round(scanner.real))-1
        key = scanner.nexttok()
        if key != "IHREAL":
            huh()
            raise TrekError
        xj = int(round(scanner.real))-1
        key = scanner.nexttok()
        if key == "IHREAL":
            # both quadrant and sector specified
            xk = int(round(scanner.real))-1
            key = scanner.nexttok()
            if key != "IHREAL":
                huh()
                raise TrekError
            xl = int(round(scanner.real))-1
            dquad.i = xi
            dquad.j = xj
            dsect.i = xk
            dsect.j = xl
        else:
            # only one pair of numbers was specified
            if isprobe:
                # only quadrant specified -- go to center of dest quad
                dquad.i = xi
                dquad.j = xj
                dsect.j = dsect.i = 4        # preserves 1-origin behavior
            else:
                # only sector specified
                dsect.i = xi
                dsect.j = xj
            itemp = "normal"
        if not dquad.valid_quadrant() or not dsect.valid_sector():
            huh()
            raise TrekError
        skip(1)
        if not isprobe:
            if itemp > "curt":
                if iprompt:
                    prout(_("Helmsman Sulu- \"Course locked in for Sector %s.\"") % dsect)
            else:
                prout(_("Ensign Chekov- \"Course laid in, Captain.\""))
        # the actual deltas get computed here
        delta.j = dquad.j-game.quadrant.j + (dsect.j-game.sector.j)/(QUADSIZE*1.0)
        delta.i = game.quadrant.i-dquad.i + (game.sector.i-dsect.i)/(QUADSIZE*1.0)
    else: # manual
        while key == "IHEOL":
            proutn(_("X and Y displacements- "))
            scanner.chew()
            iprompt = True
            key = scanner.nexttok()
        itemp = "verbose"
        if key != "IHREAL":
            huh()
            raise TrekError
        delta.j = scanner.real
        key = scanner.nexttok()
        if key != "IHREAL":
            huh()
            raise TrekError
        delta.i = scanner.real
    # Check for zero movement
    if delta.i == 0 and delta.j == 0:
        scanner.chew()
        raise TrekError
    if itemp == "verbose" and not isprobe:
        skip(1)
        prout(_("Helmsman Sulu- \"Aye, Sir.\""))
    scanner.chew()
    return course(bearing=delta.bearing(), distance=delta.distance())
 
class course:
    def __init__(self, bearing, distance, origin=None):
        self.distance = distance
        self.bearing = bearing
        if origin is None:
            self.origin = cartesian(game.quadrant, game.sector)
        else:
            self.origin = origin
        # The bearing() code we inherited from FORTRAN is actually computing
        # clockface directions!
        if self.bearing < 0.0:
            self.bearing += 12.0
        self.angle = ((15.0 - self.bearing) * 0.5235988)
        if origin is None:
            self.origin = cartesian(game.quadrant, game.sector)
        else:
            self.origin = cartesian(game.quadrant, origin)
        self.increment = Coord(-math.sin(self.angle), math.cos(self.angle))
        bigger = max(abs(self.increment.i), abs(self.increment.j))
        self.increment /= bigger
        self.moves = int(round(10*self.distance*bigger))
        self.reset()
        self.final = (self.location + self.moves*self.increment).roundtogrid()
    def reset(self):
        self.location = self.origin
        self.step = 0
    def arrived(self):
        return self.location.roundtogrid() == self.final
    def nexttok(self):
        "Next step on course."
        self.step += 1
        self.nextlocation = self.location + self.increment
        samequad = (self.location.quadrant() == self.nextlocation.quadrant())
        self.location = self.nextlocation
        return samequad
    def quadrant(self):
        return self.location.quadrant()
    def sector(self):
        return self.location.sector()
    def power(self, warp):
        return self.distance*(warp**3)*(game.shldup+1)
    def time(self, warp):
        return 10.0*self.distance/warp**2
 
def impulse():
    "Move under impulse power."
    game.ididit = False
    if damaged(DIMPULS):
        scanner.chew()
        skip(1)
        prout(_("Engineer Scott- \"The impulse engines are damaged, Sir.\""))
        return
    if game.energy > 30.0:
        try:
            course = getcourse(isprobe=False)
        except TrekError:
            return
        power = 20.0 + 100.0*course.distance
    else:
        power = 30.0
    if power >= game.energy:
        # Insufficient power for trip
        skip(1)
        prout(_("First Officer Spock- \"Captain, the impulse engines"))
        prout(_("require 20.0 units to engage, plus 100.0 units per"))
        if game.energy > 30:
            proutn(_("quadrant.  We can go, therefore, a maximum of %d") %
                     int(0.01 * (game.energy-20.0)-0.05))
            prout(_(" quadrants.\""))
        else:
            prout(_("quadrant.  They are, therefore, useless.\""))
        scanner.chew()
        return
    # Make sure enough time is left for the trip
    game.optime = course.distance/0.095
    if game.optime >= game.state.remtime:
        prout(_("First Officer Spock- \"Captain, our speed under impulse"))
        prout(_("power is only 0.95 sectors per stardate. Are you sure"))
        proutn(_("we dare spend the time?\" "))
        if not ja():
            return
    # Activate impulse engines and pay the cost
    imove(course, noattack=False)
    game.ididit = True
    if game.alldone:
        return
    power = 20.0 + 100.0*course.distance
    game.energy -= power
    game.optime = course.distance/0.095
    if game.energy <= 0:
        finish(FNRG)
    return
 
def warp(wcourse, involuntary):
    "ove under warp drive."
    blooey = False; twarp = False
    if not involuntary: # Not WARPX entry
        game.ididit = False
        if game.damage[DWARPEN] > 10.0:
            scanner.chew()
            skip(1)
            prout(_("Engineer Scott- \"The warp engines are damaged, Sir.\""))
            return
        if damaged(DWARPEN) and game.warpfac > 4.0:
            scanner.chew()
            skip(1)
            prout(_("Engineer Scott- \"Sorry, Captain. Until this damage"))
            prout(_("  is repaired, I can only give you warp 4.\""))
            return
               # Read in course and distance
        if wcourse==None:
            try:
                wcourse = getcourse(isprobe=False)
            except TrekError:
                return
        # Make sure starship has enough energy for the trip
        # Note: this formula is slightly different from the C version,
        # and lets you skate a bit closer to the edge.
        if wcourse.power(game.warpfac) >= game.energy:
            # Insufficient power for trip
            game.ididit = False
            skip(1)
            prout(_("Engineering to bridge--"))
            if not game.shldup or 0.5*wcourse.power(game.warpfac) > game.energy:
                iwarp = (game.energy/(wcourse.distance+0.05)) ** 0.333333333
                if iwarp <= 0:
                    prout(_("We can't do it, Captain. We don't have enough energy."))
                else:
                    proutn(_("We don't have enough energy, but we could do it at warp %d") % iwarp)
                    if game.shldup:
                        prout(",")
                        prout(_("if you'll lower the shields."))
                    else:
                        prout(".")
            else:
                prout(_("We haven't the energy to go that far with the shields up."))
            return
        # Make sure enough time is left for the trip
        game.optime = wcourse.time(game.warpfac)
        if game.optime >= 0.8*game.state.remtime:
            skip(1)
            prout(_("First Officer Spock- \"Captain, I compute that such"))
            proutn(_("  a trip would require approximately %2.0f") %
                   (100.0*game.optime/game.state.remtime))
            prout(_(" percent of our"))
            proutn(_("  remaining time.  Are you sure this is wise?\" "))
            if not ja():
                game.ididit = False
                game.optime=0
                return
    # Entry WARPX
    if game.warpfac > 6.0:
        # Decide if engine damage will occur
        # ESR: Seems wrong. Probability of damage goes *down* with distance?
        prob = wcourse.distance*(6.0-game.warpfac)**2/66.666666666
        if prob > randreal():
            blooey = True
            wcourse.distance = randreal(wcourse.distance)
        # Decide if time warp will occur
        if 0.5*wcourse.distance*math.pow(7.0,game.warpfac-10.0) > randreal():
            twarp = True
        if game.idebug and game.warpfac==10 and not twarp:
            blooey = False
            proutn("=== Force time warp? ")
            if ja():
                twarp = True
        if blooey or twarp:
            # If time warp or engine damage, check path
            # If it is obstructed, don't do warp or damage
            look = wcourse.moves
            while look > 0:
                look -= 1
                wcourse.nexttok()
                w = wcourse.sector()
                if not w.valid_sector():
                    break
                if game.quad[w.i][w.j] != '.':
                    blooey = False
                    twarp = False
            wcourse.reset()
    # Activate Warp Engines and pay the cost
    imove(wcourse, noattack=False)
    if game.alldone:
        return
    game.energy -= wcourse.power(game.warpfac)
    if game.energy <= 0:
        finish(FNRG)
    game.optime = wcourse.time(game.warpfac)
    if twarp:
        timwrp()
    if blooey:
        game.damage[DWARPEN] = game.damfac * randreal(1.0, 4.0)
        skip(1)
        prout(_("Engineering to bridge--"))
        prout(_("  Scott here.  The warp engines are damaged."))
        prout(_("  We'll have to reduce speed to warp 4."))
    game.ididit = True
    return
 
def setwarp():
    "Change the warp factor."
    while True:
        key=scanner.nexttok()
        if key != "IHEOL":
            break
        scanner.chew()
        proutn(_("Warp factor- "))
    if key != "IHREAL":
        huh()
        return
    if game.damage[DWARPEN] > 10.0:
        prout(_("Warp engines inoperative."))
        return
    if damaged(DWARPEN) and scanner.real > 4.0:
        prout(_("Engineer Scott- \"I'm doing my best, Captain,"))
        prout(_("  but right now we can only go warp 4.\""))
        return
    if scanner.real > 10.0:
        prout(_("Helmsman Sulu- \"Our top speed is warp 10, Captain.\""))
        return
    if scanner.real < 1.0:
        prout(_("Helmsman Sulu- \"We can't go below warp 1, Captain.\""))
        return
    oldfac = game.warpfac
    game.warpfac = scanner.real
    if game.warpfac <= oldfac or game.warpfac <= 6.0:
        prout(_("Helmsman Sulu- \"Warp factor %d, Captain.\"") %
               int(game.warpfac))
        return
    if game.warpfac < 8.00:
        prout(_("Engineer Scott- \"Aye, but our maximum safe speed is warp 6.\""))
        return
    if game.warpfac == 10.0:
        prout(_("Engineer Scott- \"Aye, Captain, we'll try it.\""))
        return
    prout(_("Engineer Scott- \"Aye, Captain, but our engines may not take it.\""))
    return
 
def atover(igrab):
    "Cope with being tossed out of quadrant by supernova or yanked by beam."
    scanner.chew()
    # is captain on planet?
    if game.landed:
        if damaged(DTRANSP):
            finish(FPNOVA)
            return
        prout(_("Scotty rushes to the transporter controls."))
        if game.shldup:
            prout(_("But with the shields up it's hopeless."))
            finish(FPNOVA)
        prouts(_("His desperate attempt to rescue you . . ."))
        if withprob(0.5):
            prout(_("fails."))
            finish(FPNOVA)
            return
        prout(_("SUCCEEDS!"))
        if game.imine:
            game.imine = False
            proutn(_("The crystals mined were "))
            if withprob(0.25):
                prout(_("lost."))
            else:
                prout(_("saved."))
                game.icrystl = True
    if igrab:
        return
    # Check to see if captain in shuttle craft
    if game.icraft:
        finish(FSTRACTOR)
    if game.alldone:
        return
    # Inform captain of attempt to reach safety
    skip(1)
    while True:
        if game.justin:
            prouts(_("***RED ALERT!  RED ALERT!"))
            skip(1)
            proutn(_("The %s has stopped in a quadrant containing") % crmshp())
            prouts(_("   a supernova."))
            skip(2)
        prout(_("***Emergency automatic override attempts to hurl ")+crmshp())
        prout(_("safely out of quadrant."))
        if not damaged(DRADIO):
            game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
        # Try to use warp engines
        if damaged(DWARPEN):
            skip(1)
            prout(_("Warp engines damaged."))
            finish(FSNOVAED)
            return
        game.warpfac = randreal(6.0, 8.0)
        prout(_("Warp factor set to %d") % int(game.warpfac))
        power = 0.75*game.energy
        dist = power/(game.warpfac*game.warpfac*game.warpfac*(game.shldup+1))
        dist = max(dist, randreal(math.sqrt(2)))
        bugout = course(bearing=randreal(12), distance=dist)        # How dumb!
        game.optime = bugout.time(game.warpfac)
        game.justin = False
        game.inorbit = False
        warp(bugout, involuntary=True)
        if not game.justin:
            # This is bad news, we didn't leave quadrant.
            if game.alldone:
                return
            skip(1)
            prout(_("Insufficient energy to leave quadrant."))
            finish(FSNOVAED)
            return
        # Repeat if another snova
        if not game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
            break
    if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0:
        finish(FWON) # Snova killed remaining enemy.
 
def timwrp():
    "Let's do the time warp again."
    prout(_("***TIME WARP ENTERED."))
    if game.state.snap and withprob(0.5):
        # Go back in time
        prout(_("You are traveling backwards in time %d stardates.") %
              int(game.state.date-game.snapsht.date))
        game.state = game.snapsht
        game.state.snap = False
        if len(game.state.kcmdr):
            schedule(FTBEAM, expran(game.intime/len(game.state.kcmdr)))
            schedule(FBATTAK, expran(0.3*game.intime))
        schedule(FSNOVA, expran(0.5*game.intime))
        # next snapshot will be sooner
        schedule(FSNAP, expran(0.25*game.state.remtime))
 
        if game.state.nscrem:
            schedule(FSCMOVE, 0.2777)
        game.isatb = 0
        unschedule(FCDBAS)
        unschedule(FSCDBAS)
        game.battle.invalidate()
        # Make sure Galileo is consistant -- Snapshot may have been taken
        # when on planet, which would give us two Galileos!
        gotit = False
        for l in range(game.inplan):
            if game.state.planets[l].known == "shuttle_down":
                gotit = True
                if game.iscraft == "onship" and game.ship=='E':
                    prout(_("Chekov-  \"Security reports the Galileo has disappeared, Sir!"))
                    game.iscraft = "offship"
        # Likewise, if in the original time the Galileo was abandoned, but
        # was on ship earlier, it would have vanished -- let's restore it.
        if game.iscraft == "offship" and not gotit and game.damage[DSHUTTL] >= 0.0:
            prout(_("Chekov-  \"Security reports the Galileo has reappeared in the dock!\""))
            game.iscraft = "onship"
        # There used to be code to do the actual reconstrction here,
        # but the starchart is now part of the snapshotted galaxy state.
        prout(_("Spock has reconstructed a correct star chart from memory"))
    else:
        # Go forward in time
        game.optime = expran(0.5*game.intime)
        prout(_("You are traveling forward in time %d stardates.") % int(game.optime))
        # cheat to make sure no tractor beams occur during time warp
        postpone(FTBEAM, game.optime)
        game.damage[DRADIO] += game.optime
    newqad()
    events()        # Stas Sergeev added this -- do pending events
 
def probe():
    "Launch deep-space probe."
    # New code to launch a deep space probe
    if game.nprobes == 0:
        scanner.chew()
        skip(1)
        if game.ship == 'E':
            prout(_("Engineer Scott- \"We have no more deep space probes, Sir.\""))
        else:
            prout(_("Ye Faerie Queene has no deep space probes."))
        return
    if damaged(DDSP):
        scanner.chew()
        skip(1)
        prout(_("Engineer Scott- \"The probe launcher is damaged, Sir.\""))
        return
    if is_scheduled(FDSPROB):
        scanner.chew()
        skip(1)
        if damaged(DRADIO) and game.condition != "docked":
            prout(_("Spock-  \"Records show the previous probe has not yet"))
            prout(_("   reached its destination.\""))
        else:
            prout(_("Uhura- \"The previous probe is still reporting data, Sir.\""))
        return
    key = scanner.nexttok()
    if key == "IHEOL":
        if game.nprobes == 1:
            prout(_("1 probe left."))
        else:
            prout(_("%d probes left") % game.nprobes)
        proutn(_("Are you sure you want to fire a probe? "))
        if not ja():
            return
    game.isarmed = False
    if key == "IHALPHA" and scanner.token == "armed":
        game.isarmed = True
        key = scanner.nexttok()
    elif key == "IHEOL":
        proutn(_("Arm NOVAMAX warhead? "))
        game.isarmed = ja()
    elif key == "IHREAL":                # first element of course
        scanner.push(scanner.token)
    try:
        game.probe = getcourse(isprobe=True)
    except TrekError:
        return
    game.nprobes -= 1
    schedule(FDSPROB, 0.01) # Time to move one sector
    prout(_("Ensign Chekov-  \"The deep space probe is launched, Captain.\""))
    game.ididit = True
    return
 
def mayday():
    "Yell for help from nearest starbase."
    # There's more than one way to move in this game!
    scanner.chew()
    # Test for conditions which prevent calling for help
    if game.condition == "docked":
        prout(_("Lt. Uhura-  \"But Captain, we're already docked.\""))
        return
    if damaged(DRADIO):
        prout(_("Subspace radio damaged."))
        return
    if not game.state.baseq:
        prout(_("Lt. Uhura-  \"Captain, I'm not getting any response from Starbase.\""))
        return
    if game.landed:
        prout(_("You must be aboard the %s.") % crmshp())
        return
    # OK -- call for help from nearest starbase
    game.nhelp += 1
    if game.base.i!=0:
        # There's one in this quadrant
        ddist = (game.base - game.sector).distance()
    else:
        ddist = FOREVER
        for ibq in game.state.baseq:
            xdist = QUADSIZE * (ibq - game.quadrant).distance()
            if xdist < ddist:
                ddist = xdist
        # Since starbase not in quadrant, set up new quadrant
        game.quadrant = ibq
        newqad()
    # dematerialize starship
    game.quad[game.sector.i][game.sector.j]='.'
    proutn(_("Starbase in Quadrant %s responds--%s dematerializes") \
           % (game.quadrant, crmshp()))
    game.sector.invalidate()
    for m in range(1, 5+1):
        w = game.base.scatter()
        if w.valid_sector() and game.quad[w.i][w.j]=='.':
            # found one -- finish up
            game.sector = w
            break
    if not game.sector.is_valid():
        prout(_("You have been lost in space..."))
        finish(FMATERIALIZE)
        return
    # Give starbase three chances to rematerialize starship
    probf = math.pow((1.0 - math.pow(0.98,ddist)), 0.33333333)
    for m in range(1, 3+1):
        if m == 1: proutn(_("1st"))
        elif m == 2: proutn(_("2nd"))
        elif m == 3: proutn(_("3rd"))
        proutn(_(" attempt to re-materialize ") + crmshp())
        game.quad[game.sector.i][game.sector.j]=('-','o','O')[m-1]
        textcolor(RED)
        warble()
        if randreal() > probf:
            break
        prout(_("fails."))
        textcolor(DEFAULT)
        curses.delay_output(500)
    if m > 3:
        game.quad[game.sector.i][game.sector.j]='?'
        game.alive = False
        drawmaps(1)
        setwnd(message_window)
        finish(FMATERIALIZE)
        return
    game.quad[game.sector.i][game.sector.j]=game.ship
    textcolor(GREEN)
    prout(_("succeeds."))
    textcolor(DEFAULT)
    dock(False)
    skip(1)
    prout(_("Lt. Uhura-  \"Captain, we made it!\""))
 
def abandon():
    "Abandon ship."
    scanner.chew()
    if game.condition=="docked":
        if game.ship!='E':
            prout(_("You cannot abandon Ye Faerie Queene."))
            return
    else:
        # Must take shuttle craft to exit
        if game.damage[DSHUTTL]==-1:
            prout(_("Ye Faerie Queene has no shuttle craft."))
            return
        if game.damage[DSHUTTL]<0:
            prout(_("Shuttle craft now serving Big Macs."))
            return
        if game.damage[DSHUTTL]>0:
            prout(_("Shuttle craft damaged."))
            return
        if game.landed:
            prout(_("You must be aboard the ship."))
            return
        if game.iscraft != "onship":
            prout(_("Shuttle craft not currently available."))
            return
        # Emit abandon ship messages
        skip(1)
        prouts(_("***ABANDON SHIP!  ABANDON SHIP!"))
        skip(1)
        prouts(_("***ALL HANDS ABANDON SHIP!"))
        skip(2)
        prout(_("Captain and crew escape in shuttle craft."))
        if not game.state.baseq:
            # Oops! no place to go...
            finish(FABANDN)
            return
        q = game.state.galaxy[game.quadrant.i][game.quadrant.j]
        # Dispose of crew
        if not (game.options & OPTION_WORLDS) and not damaged(DTRANSP):
            prout(_("Remainder of ship's complement beam down"))
            prout(_("to nearest habitable planet."))
        elif q.planet != None and not damaged(DTRANSP):
            prout(_("Remainder of ship's complement beam down to %s.") %
                    q.planet)
        else:
            prout(_("Entire crew of %d left to die in outer space.") %
                    game.state.crew)
            game.casual += game.state.crew
            game.abandoned += game.state.crew
        # If at least one base left, give 'em the Faerie Queene
        skip(1)
        game.icrystl = False # crystals are lost
        game.nprobes = 0 # No probes
        prout(_("You are captured by Klingons and released to"))
        prout(_("the Federation in a prisoner-of-war exchange."))
        nb = randrange(len(game.state.baseq))
        # Set up quadrant and position FQ adjacient to base
        if not game.quadrant == game.state.baseq[nb]:
            game.quadrant = game.state.baseq[nb]
            game.sector.i = game.sector.j = 5
            newqad()
        while True:
            # position next to base by trial and error
            game.quad[game.sector.i][game.sector.j] = '.'
            for l in range(QUADSIZE):
                game.sector = game.base.scatter()
                if game.sector.valid_sector() and \
                       game.quad[game.sector.i][game.sector.j] == '.':
                    break
            if l < QUADSIZE+1:
                break # found a spot
            game.sector.i=QUADSIZE/2
            game.sector.j=QUADSIZE/2
            newqad()
    # Get new commission
    game.quad[game.sector.i][game.sector.j] = game.ship = 'F'
    game.state.crew = FULLCREW
    prout(_("Starfleet puts you in command of another ship,"))
    prout(_("the Faerie Queene, which is antiquated but,"))
    prout(_("still useable."))
    if game.icrystl:
        prout(_("The dilithium crystals have been moved."))
    game.imine = False
    game.iscraft = "offship" # Galileo disappears
    # Resupply ship
    game.condition="docked"
    for l in range(NDEVICES):
        game.damage[l] = 0.0
    game.damage[DSHUTTL] = -1
    game.energy = game.inenrg = 3000.0
    game.shield = game.inshld = 1250.0
    game.torps = game.intorps = 6
    game.lsupres=game.inlsr=3.0
    game.shldup=False
    game.warpfac=5.0
    return
 
# Code from planets.c begins here.
 
def consumeTime():
    "Abort a lengthy operation if an event interrupts it."
    game.ididit = True
    events()
    if game.alldone or game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova or game.justin:
        return True
    return False
 
def survey():
    "Report on (uninhabited) planets in the galaxy."
    iknow = False
    skip(1)
    scanner.chew()
    prout(_("Spock-  \"Planet report follows, Captain.\""))
    skip(1)
    for i in range(game.inplan):
        if game.state.planets[i].pclass == "destroyed":
            continue
        if (game.state.planets[i].known != "unknown" \
            and not game.state.planets[i].inhabited) \
            or game.idebug:
            iknow = True
            if game.idebug and game.state.planets[i].known=="unknown":
                proutn("(Unknown) ")
            proutn(_("Quadrant %s") % game.state.planets[i].quadrant)
            proutn(_("   class "))
            proutn(game.state.planets[i].pclass)
            proutn("   ")
            if game.state.planets[i].crystals != "present":
                proutn(_("no "))
            prout(_("dilithium crystals present."))
            if game.state.planets[i].known=="shuttle_down":
                prout(_("    Shuttle Craft Galileo on surface."))
    if not iknow:
        prout(_("No information available."))
 
def orbit():
    "Enter standard orbit."
    skip(1)
    scanner.chew()
    if game.inorbit:
        prout(_("Already in standard orbit."))
        return
    if damaged(DWARPEN) and damaged(DIMPULS):
        prout(_("Both warp and impulse engines damaged."))
        return
    if not game.plnet.is_valid():
        prout("There is no planet in this sector.")
        return
    if abs(game.sector.i-game.plnet.i)>1 or abs(game.sector.j-game.plnet.j)>1:
        prout(crmshp() + _(" not adjacent to planet."))
        skip(1)
        return
    game.optime = randreal(0.02, 0.05)
    prout(_("Helmsman Sulu-  \"Entering standard orbit, Sir.\""))
    newcnd()
    if consumeTime():
        return
    game.height = randreal(1400, 8600)
    prout(_("Sulu-  \"Entered orbit at altitude %.2f kilometers.\"") % game.height)
    game.inorbit = True
    game.ididit = True
 
def sensor():
    "Examine planets in this quadrant."
    if damaged(DSRSENS):
        if game.options & OPTION_TTY:
            prout(_("Short range sensors damaged."))
        return
    if game.iplnet == None:
        if game.options & OPTION_TTY:
            prout(_("Spock- \"No planet in this quadrant, Captain.\""))
        return
    if game.iplnet.known == "unknown":
        prout(_("Spock-  \"Sensor scan for Quadrant %s-") % game.quadrant)
        skip(1)
        prout(_("         Planet at Sector %s is of class %s.") %
              (game.plnet, game.iplnet.pclass))
        if game.iplnet.known=="shuttle_down":
            prout(_("         Sensors show Galileo still on surface."))
        proutn(_("         Readings indicate"))
        if game.iplnet.crystals != "present":
            proutn(_(" no"))
        prout(_(" dilithium crystals present.\""))
        if game.iplnet.known == "unknown":
            game.iplnet.known = "known"
    elif game.iplnet.inhabited:
        prout(_("Spock-  \"The inhabited planet %s ") % game.iplnet.name)
        prout(_("        is located at Sector %s, Captain.\"") % game.plnet)
 
def beam():
    "Use the transporter."
    nrgneed = 0
    scanner.chew()
    skip(1)
    if damaged(DTRANSP):
        prout(_("Transporter damaged."))
        if not damaged(DSHUTTL) and (game.iplnet.known=="shuttle_down" or game.iscraft == "onship"):
            skip(1)
            proutn(_("Spock-  \"May I suggest the shuttle craft, Sir?\" "))
            if ja():
                shuttle()
        return
    if not game.inorbit:
        prout(crmshp() + _(" not in standard orbit."))
        return
    if game.shldup:
        prout(_("Impossible to transport through shields."))
        return
    if game.iplnet.known=="unknown":
        prout(_("Spock-  \"Captain, we have no information on this planet"))
        prout(_("  and Starfleet Regulations clearly state that in this situation"))
        prout(_("  you may not go down.\""))
        return
    if not game.landed and game.iplnet.crystals=="absent":
        prout(_("Spock-  \"Captain, I fail to see the logic in"))
        prout(_("  exploring a planet with no dilithium crystals."))
        proutn(_("  Are you sure this is wise?\" "))
        if not ja():
            scanner.chew()
            return
    if not (game.options & OPTION_PLAIN):
        nrgneed = 50 * game.skill + game.height / 100.0
        if nrgneed > game.energy:
            prout(_("Engineering to bridge--"))
            prout(_("  Captain, we don't have enough energy for transportation."))
            return
        if not game.landed and nrgneed * 2 > game.energy:
            prout(_("Engineering to bridge--"))
            prout(_("  Captain, we have enough energy only to transport you down to"))
            prout(_("  the planet, but there wouldn't be an energy for the trip back."))
            if game.iplnet.known == "shuttle_down":
                prout(_("  Although the Galileo shuttle craft may still be on a surface."))
            proutn(_("  Are you sure this is wise?\" "))
            if not ja():
                scanner.chew()
                return
    if game.landed:
        # Coming from planet
        if game.iplnet.known=="shuttle_down":
            proutn(_("Spock-  \"Wouldn't you rather take the Galileo?\" "))
            if ja():
                scanner.chew()
                return
            prout(_("Your crew hides the Galileo to prevent capture by aliens."))
        prout(_("Landing party assembled, ready to beam up."))
        skip(1)
        prout(_("Kirk whips out communicator..."))
        prouts(_("BEEP  BEEP  BEEP"))
        skip(2)
        prout(_("\"Kirk to enterprise-  Lock on coordinates...energize.\""))
    else:
        # Going to planet
        prout(_("Scotty-  \"Transporter room ready, Sir.\""))
        skip(1)
        prout(_("Kirk and landing party prepare to beam down to planet surface."))
        skip(1)
        prout(_("Kirk-  \"Energize.\""))
    game.ididit = True
    skip(1)
    prouts("WWHOOOIIIIIRRRRREEEE.E.E.  .  .  .  .   .    .")
    skip(2)
    if not withprob(0.98):
        prouts("BOOOIIIOOOIIOOOOIIIOIING . . .")
        skip(2)
        prout(_("Scotty-  \"Oh my God!  I've lost them.\""))
        finish(FLOST)
        return
    prouts(".    .   .  .  .  .  .E.E.EEEERRRRRIIIIIOOOHWW")
    game.landed = not game.landed
    game.energy -= nrgneed
    skip(2)
    prout(_("Transport complete."))
    if game.landed and game.iplnet.known=="shuttle_down":
        prout(_("The shuttle craft Galileo is here!"))
    if not game.landed and game.imine:
        game.icrystl = True
        game.cryprob = 0.05
    game.imine = False
    return
 
def mine():
    "Strip-mine a world for dilithium."
    skip(1)
    scanner.chew()
    if not game.landed:
        prout(_("Mining party not on planet."))
        return
    if game.iplnet.crystals == "mined":
        prout(_("This planet has already been strip-mined for dilithium."))
        return
    elif game.iplnet.crystals == "absent":
        prout(_("No dilithium crystals on this planet."))
        return
    if game.imine:
        prout(_("You've already mined enough crystals for this trip."))
        return
    if game.icrystl and game.cryprob == 0.05:
        prout(_("With all those fresh crystals aboard the ") + crmshp())
        prout(_("there's no reason to mine more at this time."))
        return
    game.optime = randreal(0.1, 0.3)*(ord(game.iplnet.pclass)-ord("L"))
    if consumeTime():
        return
    prout(_("Mining operation complete."))
    game.iplnet.crystals = "mined"
    game.imine = game.ididit = True
 
def usecrystals():
    "Use dilithium crystals."
    game.ididit = False
    skip(1)
    scanner.chew()
    if not game.icrystl:
        prout(_("No dilithium crystals available."))
        return
    if game.energy >= 1000:
        prout(_("Spock-  \"Captain, Starfleet Regulations prohibit such an operation"))
        prout(_("  except when Condition Yellow exists."))
        return
    prout(_("Spock- \"Captain, I must warn you that loading"))
    prout(_("  raw dilithium crystals into the ship's power"))
    prout(_("  system may risk a severe explosion."))
    proutn(_("  Are you sure this is wise?\" "))
    if not ja():
        scanner.chew()
        return
    skip(1)
    prout(_("Engineering Officer Scott-  \"(GULP) Aye Sir."))
    prout(_("  Mr. Spock and I will try it.\""))
    skip(1)
    prout(_("Spock-  \"Crystals in place, Sir."))
    prout(_("  Ready to activate circuit.\""))
    skip(1)
    prouts(_("Scotty-  \"Keep your fingers crossed, Sir!\""))
    skip(1)
    if withprob(game.cryprob):
        prouts(_("  \"Activating now! - - No good!  It's***"))
        skip(2)
        prouts(_("***RED ALERT!  RED A*L********************************"))
        skip(1)
        stars()
        prouts(_("******************   KA-BOOM!!!!   *******************"))
        skip(1)
        kaboom()
        return
    game.energy += randreal(5000.0, 5500.0)
    prouts(_("  \"Activating now! - - "))
    prout(_("The instruments"))
    prout(_("   are going crazy, but I think it's"))
    prout(_("   going to work!!  Congratulations, Sir!\""))
    game.cryprob *= 2.0
    game.ididit = True
 
def shuttle():
    "Use shuttlecraft for planetary jaunt."
    scanner.chew()
    skip(1)
    if damaged(DSHUTTL):
        if game.damage[DSHUTTL] == -1.0:
            if game.inorbit and game.iplnet.known == "shuttle_down":
                prout(_("Ye Faerie Queene has no shuttle craft bay to dock it at."))
            else:
                prout(_("Ye Faerie Queene had no shuttle craft."))
        elif game.damage[DSHUTTL] > 0:
            prout(_("The Galileo is damaged."))
        else: # game.damage[DSHUTTL] < 0
            prout(_("Shuttle craft is now serving Big Macs."))
        return
    if not game.inorbit:
        prout(crmshp() + _(" not in standard orbit."))
        return
    if (game.iplnet.known != "shuttle_down") and game.iscraft != "onship":
        prout(_("Shuttle craft not currently available."))
        return
    if not game.landed and game.iplnet.known=="shuttle_down":
        prout(_("You will have to beam down to retrieve the shuttle craft."))
        return
    if game.shldup or game.condition == "docked":
        prout(_("Shuttle craft cannot pass through shields."))
        return
    if game.iplnet.known=="unknown":
        prout(_("Spock-  \"Captain, we have no information on this planet"))
        prout(_("  and Starfleet Regulations clearly state that in this situation"))
        prout(_("  you may not fly down.\""))
        return
    game.optime = 3.0e-5*game.height
    if game.optime >= 0.8*game.state.remtime:
        prout(_("First Officer Spock-  \"Captain, I compute that such"))
        proutn(_("  a maneuver would require approximately %2d%% of our") % \
               int(100*game.optime/game.state.remtime))
        prout(_("remaining time."))
        proutn(_("Are you sure this is wise?\" "))
        if not ja():
            game.optime = 0.0
            return
    if game.landed:
        # Kirk on planet
        if game.iscraft == "onship":
            # Galileo on ship!
            if not damaged(DTRANSP):
                proutn(_("Spock-  \"Would you rather use the transporter?\" "))
                if ja():
                    beam()
                    return
                proutn(_("Shuttle crew"))
            else:
                proutn(_("Rescue party"))
            prout(_(" boards Galileo and swoops toward planet surface."))
            game.iscraft = "offship"
            skip(1)
            if consumeTime():
                return
            game.iplnet.known="shuttle_down"
            prout(_("Trip complete."))
            return
        else:
            # Ready to go back to ship
            prout(_("You and your mining party board the"))
            prout(_("shuttle craft for the trip back to the Enterprise."))
            skip(1)
            prouts(_("The short hop begins . . ."))
            skip(1)
            game.iplnet.known="known"
            game.icraft = True
            skip(1)
            game.landed = False
            if consumeTime():
                return
            game.iscraft = "onship"
            game.icraft = False
            if game.imine:
                game.icrystl = True
                game.cryprob = 0.05
            game.imine = False
            prout(_("Trip complete."))
            return
    else:
        # Kirk on ship and so is Galileo
        prout(_("Mining party assembles in the hangar deck,"))
        prout(_("ready to board the shuttle craft \"Galileo\"."))
        skip(1)
        prouts(_("The hangar doors open; the trip begins."))
        skip(1)
        game.icraft = True
        game.iscraft = "offship"
        if consumeTime():
            return
        game.iplnet.known = "shuttle_down"
        game.landed = True
        game.icraft = False
        prout(_("Trip complete."))
        return
 
def deathray():
    "Use the big zapper."
    game.ididit = False
    skip(1)
    scanner.chew()
    if game.ship != 'E':
        prout(_("Ye Faerie Queene has no death ray."))
        return
    if len(game.enemies)==0:
        prout(_("Sulu-  \"But Sir, there are no enemies in this quadrant.\""))
        return
    if damaged(DDRAY):
        prout(_("Death Ray is damaged."))
        return
    prout(_("Spock-  \"Captain, the 'Experimental Death Ray'"))
    prout(_("  is highly unpredictible.  Considering the alternatives,"))
    proutn(_("  are you sure this is wise?\" "))
    if not ja():
        return
    prout(_("Spock-  \"Acknowledged.\""))
    skip(1)
    game.ididit = True
    prouts(_("WHOOEE ... WHOOEE ... WHOOEE ... WHOOEE"))
    skip(1)
    prout(_("Crew scrambles in emergency preparation."))
    prout(_("Spock and Scotty ready the death ray and"))
    prout(_("prepare to channel all ship's power to the device."))
    skip(1)
    prout(_("Spock-  \"Preparations complete, sir.\""))
    prout(_("Kirk-  \"Engage!\""))
    skip(1)
    prouts(_("WHIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"))
    skip(1)
    dprob = 0.30
    if game.options & OPTION_PLAIN:
        dprob = 0.5
    r = randreal()
    if r > dprob:
        prouts(_("Sulu- \"Captain!  It's working!\""))
        skip(2)
        while len(game.enemies) > 0:
            deadkl(game.enemies[1].location, game.quad[game.enemies[1].location.i][game.enemies[1].location.j],game.enemies[1].location)
        prout(_("Ensign Chekov-  \"Congratulations, Captain!\""))
        if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) == 0:
            finish(FWON)
        if (game.options & OPTION_PLAIN) == 0:
            prout(_("Spock-  \"Captain, I believe the `Experimental Death Ray'"))
            if withprob(0.05):
                prout(_("   is still operational.\""))
            else:
                prout(_("   has been rendered nonfunctional.\""))
                game.damage[DDRAY] = 39.95
        return
    r = randreal()        # Pick failure method
    if r <= 0.30:
        prouts(_("Sulu- \"Captain!  It's working!\""))
        skip(1)
        prouts(_("***RED ALERT!  RED ALERT!"))
        skip(1)
        prout(_("***MATTER-ANTIMATTER IMPLOSION IMMINENT!"))
        skip(1)
        prouts(_("***RED ALERT!  RED A*L********************************"))
        skip(1)
        stars()
        prouts(_("******************   KA-BOOM!!!!   *******************"))
        skip(1)
        kaboom()
        return
    if r <= 0.55:
        prouts(_("Sulu- \"Captain!  Yagabandaghangrapl, brachriigringlanbla!\""))
        skip(1)
        prout(_("Lt. Uhura-  \"Graaeek!  Graaeek!\""))
        skip(1)
        prout(_("Spock-  \"Fascinating!  . . . All humans aboard"))
        prout(_("  have apparently been transformed into strange mutations."))
        prout(_("  Vulcans do not seem to be affected."))
        skip(1)
        prout(_("Kirk-  \"Raauch!  Raauch!\""))
        finish(FDRAY)
        return
    if r <= 0.75:
        prouts(_("Sulu- \"Captain!  It's   --WHAT?!?!\""))
        skip(2)
        proutn(_("Spock-  \"I believe the word is"))
        prouts(_(" *ASTONISHING*"))
        prout(_(" Mr. Sulu."))
        for i in range(QUADSIZE):
            for j in range(QUADSIZE):
                if game.quad[i][j] == '.':
                    game.quad[i][j] = '?'
        prout(_("  Captain, our quadrant is now infested with"))
        prouts(_(" - - - - - -  *THINGS*."))
        skip(1)
        prout(_("  I have no logical explanation.\""))
        return
    prouts(_("Sulu- \"Captain!  The Death Ray is creating tribbles!\""))
    skip(1)
    prout(_("Scotty-  \"There are so many tribbles down here"))
    prout(_("  in Engineering, we can't move for 'em, Captain.\""))
    finish(FTRIBBLE)
    return
 
# Code from reports.c begins here
 
def attackreport(curt):
    "eport status of bases under attack."
    if not curt:
        if is_scheduled(FCDBAS):
            prout(_("Starbase in Quadrant %s is currently under Commander attack.") % game.battle)
            prout(_("It can hold out until Stardate %d.") % int(scheduled(FCDBAS)))
        elif game.isatb == 1:
            prout(_("Starbase in Quadrant %s is under Super-commander attack.") % game.state.kscmdr)
            prout(_("It can hold out until Stardate %d.") % int(scheduled(FSCDBAS)))
        else:
            prout(_("No Starbase is currently under attack."))
    else:
        if is_scheduled(FCDBAS):
            proutn(_("Base in %s attacked by C. Alive until %.1f") % (game.battle, scheduled(FCDBAS)))
        if game.isatb:
            proutn(_("Base in %s attacked by S. Alive until %.1f") % (game.state.kscmdr, scheduled(FSCDBAS)))
        clreol()
 
def report():
    # report on general game status
    scanner.chew()
    s1 = (game.thawed and _("thawed ")) or ""
    s2 = {1:"short", 2:"medium", 4:"long"}[game.length]
    s3 = (None, _("novice"), _("fair"),
          _("good"), _("expert"), _("emeritus"))[game.skill]
    prout(_("You %s a %s%s %s game.") % ((_("were playing"), _("are playing"))[game.alldone], s1, s2, s3))
    if game.skill>SKILL_GOOD and game.thawed and not game.alldone:
        prout(_("No plaque is allowed."))
    if game.tourn:
        prout(_("This is tournament game %d.") % game.tourn)
    prout(_("Your secret password is \"%s\"") % game.passwd)
    proutn(_("%d of %d Klingons have been killed") % (((game.inkling + game.incom + game.inscom) - (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)),
           (game.inkling + game.incom + game.inscom)))
    if game.incom - len(game.state.kcmdr):
        prout(_(", including %d Commander%s.") % (game.incom - len(game.state.kcmdr), (_("s"), "")[(game.incom - len(game.state.kcmdr))==1]))
    elif game.inkling - game.state.remkl + (game.inscom - game.state.nscrem) > 0:
        prout(_(", but no Commanders."))
    else:
        prout(".")
    if game.skill > SKILL_FAIR:
        prout(_("The Super Commander has %sbeen destroyed.") % ("", _("not "))[game.state.nscrem])
    if len(game.state.baseq) != game.inbase:
        proutn(_("There "))
        if game.inbase-len(game.state.baseq)==1:
            proutn(_("has been 1 base"))
        else:
            proutn(_("have been %d bases") % (game.inbase-len(game.state.baseq)))
        prout(_(" destroyed, %d remaining.") % len(game.state.baseq))
    else:
        prout(_("There are %d bases.") % game.inbase)
    if communicating() or game.iseenit:
        # Don't report this if not seen and
        # either the radio is dead or not at base!
        attackreport(False)
        game.iseenit = True
    if game.casual:
        prout(_("%d casualt%s suffered so far.") % (game.casual, ("y", "ies")[game.casual!=1]))
    if game.nhelp:
        prout(_("There were %d call%s for help.") % (game.nhelp,  ("" , _("s"))[game.nhelp!=1]))
    if game.ship == 'E':
        proutn(_("You have "))
        if game.nprobes:
            proutn("%d" % (game.nprobes))
        else:
            proutn(_("no"))
        proutn(_(" deep space probe"))
        if game.nprobes!=1:
            proutn(_("s"))
        prout(".")
    if communicating() and is_scheduled(FDSPROB):
        if game.isarmed:
            proutn(_("An armed deep space probe is in "))
        else:
            proutn(_("A deep space probe is in "))
        prout("Quadrant %s." % game.probec)
    if game.icrystl:
        if game.cryprob <= .05:
            prout(_("Dilithium crystals aboard ship... not yet used."))
        else:
            i=0
            ai = 0.05
            while game.cryprob > ai:
                ai *= 2.0
                i += 1
            prout(_("Dilithium crystals have been used %d time%s.") % \
                  (i, (_("s"), "")[i==1]))
    skip(1)
 
def lrscan(silent):
    "Long-range sensor scan."
    if damaged(DLRSENS):
        # Now allow base's sensors if docked
        if game.condition != "docked":
            if not silent:
                prout(_("LONG-RANGE SENSORS DAMAGED."))
            return
        if not silent:
            prout(_("Starbase's long-range scan"))
    elif not silent:
        prout(_("Long-range scan"))
    for x in range(game.quadrant.i-1, game.quadrant.i+2):
        if not silent:
            proutn(" ")
        for y in range(game.quadrant.j-1, game.quadrant.j+2):
            if not Coord(x, y).valid_quadrant():
                if not silent:
                    proutn("  -1")
            else:
                if not damaged(DRADIO):
                    game.state.galaxy[x][y].charted = True
                game.state.chart[x][y].klingons = game.state.galaxy[x][y].klingons
                game.state.chart[x][y].starbase = game.state.galaxy[x][y].starbase
                game.state.chart[x][y].stars = game.state.galaxy[x][y].stars
                if not silent and game.state.galaxy[x][y].supernova:
                    proutn(" ***")
                elif not silent:
                    proutn(" %3d" % (game.state.chart[x][y].klingons*100 + game.state.chart[x][y].starbase * 10 + game.state.chart[x][y].stars))
        if not silent:
            prout(" ")
 
def damagereport():
    "Damage report."
    jdam = False
    scanner.chew()
    for i in range(NDEVICES):
        if damaged(i):
            if not jdam:
                prout(_("\tDEVICE\t\t\t-REPAIR TIMES-"))
                prout(_("\t\t\tIN FLIGHT\t\tDOCKED"))
                jdam = True
            prout("  %-26s\t%8.2f\t\t%8.2f" % (device[i],
                                               game.damage[i]+0.05,
                                               DOCKFAC*game.damage[i]+0.005))
    if not jdam:
        prout(_("All devices functional."))
 
def rechart():
    "Update the chart in the Enterprise's computer from galaxy data."
    game.lastchart = game.state.date
    for i in range(GALSIZE):
        for j in range(GALSIZE):
            if game.state.galaxy[i][j].charted:
                game.state.chart[i][j].klingons = game.state.galaxy[i][j].klingons
                game.state.chart[i][j].starbase = game.state.galaxy[i][j].starbase
                game.state.chart[i][j].stars = game.state.galaxy[i][j].stars
 
def chart():
    "Display the star chart."
    scanner.chew()
    if (game.options & OPTION_AUTOSCAN):
        lrscan(silent=True)
    if not damaged(DRADIO):
        rechart()
    if game.lastchart < game.state.date and game.condition == "docked":
        prout(_("Spock-  \"I revised the Star Chart from the starbase's records.\""))
        rechart()
    prout(_("       STAR CHART FOR THE KNOWN GALAXY"))
    if game.state.date > game.lastchart:
        prout(_("(Last surveillance update %d stardates ago).") % ((int)(game.state.date-game.lastchart)))
    prout("      1    2    3    4    5    6    7    8")
    for i in range(GALSIZE):
        proutn("%d |" % (i+1))
        for j in range(GALSIZE):
            if (game.options & OPTION_SHOWME) and i == game.quadrant.i and j == game.quadrant.j:
                proutn("<")
            else:
                proutn(" ")
            if game.state.galaxy[i][j].supernova:
                show = "***"
            elif not game.state.galaxy[i][j].charted and game.state.galaxy[i][j].starbase:
                show = ".1."
            elif game.state.galaxy[i][j].charted:
                show = "%3d" % (game.state.chart[i][j].klingons*100 + game.state.chart[i][j].starbase * 10 + game.state.chart[i][j].stars)
            else:
                show = "..."
            proutn(show)
            if (game.options & OPTION_SHOWME) and i == game.quadrant.i and j == game.quadrant.j:
                proutn(">")
            else:
                proutn(" ")
        proutn("  |")
        if i<GALSIZE:
            skip(1)
 
def sectscan(goodScan, i, j):
    "Light up an individual dot in a sector."
    if goodScan or (abs(i-game.sector.i)<= 1 and abs(j-game.sector.j) <= 1):
        textcolor({"green":GREEN,
                   "yellow":YELLOW,
                   "red":RED,
                   "docked":CYAN,
                   "dead":BROWN}[game.condition])
        if game.quad[i][j] != game.ship:
            highvideo()
        proutn("%c " % game.quad[i][j])
        textcolor(DEFAULT)
    else:
        proutn("- ")
 
def status(req=0):
    "Emit status report lines"
    if not req or req == 1:
        prstat(_("Stardate"), _("%.1f, Time Left %.2f") \
               % (game.state.date, game.state.remtime))
    if not req or req == 2:
        if game.condition != "docked":
            newcnd()
        prstat(_("Condition"), _("%s, %i DAMAGES") % \
               (game.condition.upper(), sum([x > 0 for x in game.damage])))
    if not req or req == 3:
        prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector))
    if not req or req == 4:
        if damaged(DLIFSUP):
            if game.condition == "docked":
                s = _("DAMAGED, Base provides")
            else:
                s = _("DAMAGED, reserves=%4.2f") % game.lsupres
        else:
            s = _("ACTIVE")
        prstat(_("Life Support"), s)
    if not req or req == 5:
        prstat(_("Warp Factor"), "%.1f" % game.warpfac)
    if not req or req == 6:
        extra = ""
        if game.icrystl and (game.options & OPTION_SHOWME):
            extra = _(" (have crystals)")
        prstat(_("Energy"), "%.2f%s" % (game.energy, extra))
    if not req or req == 7:
        prstat(_("Torpedoes"), "%d" % (game.torps))
    if not req or req == 8:
        if damaged(DSHIELD):
            s = _("DAMAGED,")
        elif game.shldup:
            s = _("UP,")
        else:
            s = _("DOWN,")
        data = _(" %d%% %.1f units") \
               % (int((100.0*game.shield)/game.inshld + 0.5), game.shield)
        prstat(_("Shields"), s+data)
    if not req or req == 9:
        prstat(_("Klingons Left"), "%d" \
               % (game.state.remkl+len(game.state.kcmdr)+game.state.nscrem))
    if not req or req == 10:
        if game.options & OPTION_WORLDS:
            plnet = game.state.galaxy[game.quadrant.i][game.quadrant.j].planet
            if plnet and plnet.inhabited:
                prstat(_("Major system"), plnet.name)
            else:
                prout(_("Sector is uninhabited"))
    elif not req or req == 11:
        attackreport(not req)
 
def request():
    "Request specified status data, a historical relic from slow TTYs."
    requests = ("da","co","po","ls","wa","en","to","sh","kl","sy", "ti")
    while scanner.nexttok() == "IHEOL":
        proutn(_("Information desired? "))
    scanner.chew()
    if scanner.token in requests:
        status(requests.index(scanner.token))
    else:
        prout(_("UNRECOGNIZED REQUEST. Legal requests are:"))
        prout(("  date, condition, position, lsupport, warpfactor,"))
        prout(("  energy, torpedoes, shields, klingons, system, time."))
 
def srscan():
    "Short-range scan."
    goodScan=True
    if damaged(DSRSENS):
        # Allow base's sensors if docked
        if game.condition != "docked":
            prout(_("   S.R. SENSORS DAMAGED!"))
            goodScan=False
        else:
            prout(_("  [Using Base's sensors]"))
    else:
        prout(_("     Short-range scan"))
    if goodScan and not damaged(DRADIO):
        game.state.chart[game.quadrant.i][game.quadrant.j].klingons = game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons
        game.state.chart[game.quadrant.i][game.quadrant.j].starbase = game.state.galaxy[game.quadrant.i][game.quadrant.j].starbase
        game.state.chart[game.quadrant.i][game.quadrant.j].stars = game.state.galaxy[game.quadrant.i][game.quadrant.j].stars
        game.state.galaxy[game.quadrant.i][game.quadrant.j].charted = True
    prout("    1 2 3 4 5 6 7 8 9 10")
    if game.condition != "docked":
        newcnd()
    for i in range(QUADSIZE):
        proutn("%2d  " % (i+1))
        for j in range(QUADSIZE):
            sectscan(goodScan, i, j)
        skip(1)
 
def eta():
    "Use computer to get estimated time of arrival for a warp jump."
    w1 = Coord(); w2 = Coord()
    prompt = False
    if damaged(DCOMPTR):
        prout(_("COMPUTER DAMAGED, USE A POCKET CALCULATOR."))
        skip(1)
        return
    if scanner.nexttok() != "IHREAL":
        prompt = True
        scanner.chew()
        proutn(_("Destination quadrant and/or sector? "))
        if scanner.nexttok()!="IHREAL":
            huh()
            return
    w1.j = int(scanner.real-0.5)
    if scanner.nexttok() != "IHREAL":
        huh()
        return
    w1.i = int(scanner.real-0.5)
    if scanner.nexttok() == "IHREAL":
        w2.j = int(scanner.real-0.5)
        if scanner.nexttok() != "IHREAL":
            huh()
            return
        w2.i = int(scanner.real-0.5)
    else:
        if game.quadrant.j>w1.i:
            w2.i = 0
        else:
            w2.i=QUADSIZE-1
        if game.quadrant.i>w1.j:
            w2.j = 0
        else:
            w2.j=QUADSIZE-1
    if not w1.valid_quadrant() or not w2.valid_sector():
        huh()
        return
    dist = math.sqrt((w1.j-game.quadrant.j+(w2.j-game.sector.j)/(QUADSIZE*1.0))**2+
                (w1.i-game.quadrant.i+(w2.i-game.sector.i)/(QUADSIZE*1.0))**2)
    wfl = False
    if prompt:
        prout(_("Answer \"no\" if you don't know the value:"))
    while True:
        scanner.chew()
        proutn(_("Time or arrival date? "))
        if scanner.nexttok()=="IHREAL":
            ttime = scanner.real
            if ttime > game.state.date:
                ttime -= game.state.date # Actually a star date
            twarp=(math.floor(math.sqrt((10.0*dist)/ttime)*10.0)+1.0)/10.0
            if ttime <= 1e-10 or twarp > 10:
                prout(_("We'll never make it, sir."))
                scanner.chew()
                return
            if twarp < 1.0:
                twarp = 1.0
            break
        scanner.chew()
        proutn(_("Warp factor? "))
        if scanner.nexttok()== "IHREAL":
            wfl = True
            twarp = scanner.real
            if twarp<1.0 or twarp > 10.0:
                huh()
                return
            break
        prout(_("Captain, certainly you can give me one of these."))
    while True:
        scanner.chew()
        ttime = (10.0*dist)/twarp**2
        tpower = dist*twarp*twarp*twarp*(game.shldup+1)
        if tpower >= game.energy:
            prout(_("Insufficient energy, sir."))
            if not game.shldup or tpower > game.energy*2.0:
                if not wfl:
                    return
                proutn(_("New warp factor to try? "))
                if scanner.nexttok() == "IHREAL":
                    wfl = True
                    twarp = scanner.real
                    if twarp<1.0 or twarp > 10.0:
                        huh()
                        return
                    continue
                else:
                    scanner.chew()
                    skip(1)
                    return
            prout(_("But if you lower your shields,"))
            proutn(_("remaining"))
            tpower /= 2
        else:
            proutn(_("Remaining"))
        prout(_(" energy will be %.2f.") % (game.energy-tpower))
        if wfl:
            prout(_("And we will arrive at stardate %.2f.") % (game.state.date+ttime))
        elif twarp==1.0:
            prout(_("Any warp speed is adequate."))
        else:
            prout(_("Minimum warp needed is %.2f,") % (twarp))
            prout(_("and we will arrive at stardate %.2f.") % (game.state.date+ttime))
        if game.state.remtime < ttime:
            prout(_("Unfortunately, the Federation will be destroyed by then."))
        if twarp > 6.0:
            prout(_("You'll be taking risks at that speed, Captain"))
        if (game.isatb==1 and game.state.kscmdr == w1 and \
             scheduled(FSCDBAS)< ttime+game.state.date) or \
            (scheduled(FCDBAS)<ttime+game.state.date and game.battle == w1):
            prout(_("The starbase there will be destroyed by then."))
        proutn(_("New warp factor to try? "))
        if scanner.nexttok() == "IHREAL":
            wfl = True
            twarp = scanner.real
            if twarp<1.0 or twarp > 10.0:
                huh()
                return
        else:
            scanner.chew()
            skip(1)
            return
 
# Code from setup.c begins here
 
def prelim():
    "Issue a historically correct banner."
    skip(2)
    prout(_("-SUPER- STAR TREK"))
    skip(1)
# From the FORTRAN original
#    prout(_("Latest update-21 Sept 78"))
#    skip(1)
 
def freeze(boss):
    "Save game."
    if boss:
        scanner.push("emsave.trk")
    key = scanner.nexttok()
    if key == "IHEOL":
        proutn(_("File name: "))
        key = scanner.nexttok()
    if key != "IHALPHA":
        huh()
        return
    if '.' not in scanner.token:
        scanner.token += ".trk"
    try:
        fp = open(scanner.token, "wb")
    except IOError:
        prout(_("Can't freeze game as file %s") % scanner.token)
        return
    pickle.dump(game, fp)
    fp.close()
    scanner.chew()
 
def thaw():
    "Retrieve saved game."
    global game
    game.passwd = None
    key = scanner.nexttok()
    if key == "IHEOL":
        proutn(_("File name: "))
        key = scanner.nexttok()
    if key != "IHALPHA":
        huh()
        return True
    if '.' not in scanner.token:
        scanner.token += ".trk"
    try:
        fp = open(scanner.token, "rb")
    except IOError:
        prout(_("Can't thaw game in %s") % scanner.token)
        return
    game = pickle.load(fp)
    fp.close()
    scanner.chew()
    return False
 
# I used <http://www.memory-alpha.org> to find planets
# with references in ST:TOS.  Earth and the Alpha Centauri
# Colony have been omitted.
#
# Some planets marked Class G and P here will be displayed as class M
# because of the way planets are generated. This is a known bug.
systnames = (
    # Federation Worlds
    _("Andoria (Fesoan)"),        # several episodes
    _("Tellar Prime (Miracht)"),        # TOS: "Journey to Babel"
    _("Vulcan (T'Khasi)"),        # many episodes
    _("Medusa"),                # TOS: "Is There in Truth No Beauty?"
    _("Argelius II (Nelphia)"),        # TOS: "Wolf in the Fold" ("IV" in BSD)
    _("Ardana"),                # TOS: "The Cloud Minders"
    _("Catulla (Cendo-Prae)"),        # TOS: "The Way to Eden"
    _("Gideon"),                # TOS: "The Mark of Gideon"
    _("Aldebaran III"),                # TOS: "The Deadly Years"
    _("Alpha Majoris I"),        # TOS: "Wolf in the Fold"
    _("Altair IV"),                # TOS: "Amok Time
    _("Ariannus"),                # TOS: "Let That Be Your Last Battlefield"
    _("Benecia"),                # TOS: "The Conscience of the King"
    _("Beta Niobe I (Sarpeidon)"),        # TOS: "All Our Yesterdays"
    _("Alpha Carinae II"),        # TOS: "The Ultimate Computer"
    _("Capella IV (Kohath)"),        # TOS: "Friday's Child" (Class G)
    _("Daran V"),                # TOS: "For the World is Hollow and I Have Touched the Sky"
    _("Deneb II"),                # TOS: "Wolf in the Fold" ("IV" in BSD)
    _("Eminiar VII"),                # TOS: "A Taste of Armageddon"
    _("Gamma Canaris IV"),        # TOS: "Metamorphosis"
    _("Gamma Tranguli VI (Vaalel)"),        # TOS: "The Apple"
    _("Ingraham B"),                # TOS: "Operation: Annihilate"
    _("Janus IV"),                # TOS: "The Devil in the Dark"
    _("Makus III"),                # TOS: "The Galileo Seven"
    _("Marcos XII"),                # TOS: "And the Children Shall Lead",
    _("Omega IV"),                # TOS: "The Omega Glory"
    _("Regulus V"),                # TOS: "Amok Time
    _("Deneva"),                # TOS: "Operation -- Annihilate!"
    # Worlds from BSD Trek
    _("Rigel II"),                # TOS: "Shore Leave" ("III" in BSD)
    _("Beta III"),                # TOS: "The Return of the Archons"
    _("Triacus"),                # TOS: "And the Children Shall Lead",
    _("Exo III"),                # TOS: "What Are Little Girls Made Of?" (Class P)
#        # Others
#    _("Hansen's Planet"),        # TOS: "The Galileo Seven"
#    _("Taurus IV"),                # TOS: "The Galileo Seven" (class G)
#    _("Antos IV (Doraphane)"),        # TOS: "Whom Gods Destroy", "Who Mourns for Adonais?"
#    _("Izar"),                        # TOS: "Whom Gods Destroy"
#    _("Tiburon"),                # TOS: "The Way to Eden"
#    _("Merak II"),                # TOS: "The Cloud Minders"
#    _("Coridan (Desotriana)"),        # TOS: "Journey to Babel"
#    _("Iotia"),                # TOS: "A Piece of the Action"
)
 
device = (
        _("S. R. Sensors"), \
        _("L. R. Sensors"), \
        _("Phasers"), \
        _("Photon Tubes"), \
        _("Life Support"), \
        _("Warp Engines"), \
        _("Impulse Engines"), \
        _("Shields"), \
        _("Subspace Radio"), \
        _("Shuttle Craft"), \
        _("Computer"), \
        _("Navigation System"), \
        _("Transporter"), \
        _("Shield Control"), \
        _("Death Ray"), \
        _("D. S. Probe"), \
)
 
def setup():
    "Prepare to play, set up cosmos."
    w = Coord()
    #  Decide how many of everything
    if choose():
        return # frozen game
    # Prepare the Enterprise
    game.alldone = game.gamewon = game.shldchg = game.shldup = False
    game.ship = 'E'
    game.state.crew = FULLCREW
    game.energy = game.inenrg = 5000.0
    game.shield = game.inshld = 2500.0
    game.inlsr = 4.0
    game.lsupres = 4.0
    game.quadrant = randplace(GALSIZE)
    game.sector = randplace(QUADSIZE)
    game.torps = game.intorps = 10
    game.nprobes = randrange(2, 5)
    game.warpfac = 5.0
    for i in range(NDEVICES):
        game.damage[i] = 0.0
    # Set up assorted game parameters
    game.battle = Coord()
    game.state.date = game.indate = 100.0 * randreal(20, 51)
    game.nkinks = game.nhelp = game.casual = game.abandoned = 0
    game.iscate = game.resting = game.imine = game.icrystl = game.icraft = False
    game.isatb = game.state.nplankl = 0
    game.state.starkl = game.state.basekl = game.state.nworldkl = 0
    game.iscraft = "onship"
    game.landed = False
    game.alive = True
 
    # the galaxy
    game.state.galaxy = fill2d(GALSIZE, lambda i_unused, j_unused: Quadrant())
    # the starchart
    game.state.chart = fill2d(GALSIZE, lambda i_unused, j_unused: Page())
 
    game.state.planets = []      # Planet information
    game.state.baseq = []      # Base quadrant coordinates
    game.state.kcmdr = []      # Commander quadrant coordinates
    game.statekscmdr = Coord() # Supercommander quadrant coordinates
 
    # Starchart is functional but we've never seen it
    game.lastchart = FOREVER
    # Put stars in the galaxy
    game.instar = 0
    for i in range(GALSIZE):
        for j in range(GALSIZE):
            # Can't have more stars per quadrant than fit in one decimal digit,
            # if we do the chart representation will break.
            k = randrange(1, min(10, QUADSIZE**2/10))
            game.instar += k
            game.state.galaxy[i][j].stars = k
    # Locate star bases in galaxy
    if game.idebug:
        prout("=== Allocating %d bases" % game.inbase)
    for i in range(game.inbase):
        while True:
            while True:
                w = randplace(GALSIZE)
                if not game.state.galaxy[w.i][w.j].starbase:
                    break
            contflag = False
            # C version: for (j = i-1; j > 0; j--)
            # so it did them in the opposite order.
            for j in range(1, i):
                # Improved placement algorithm to spread out bases
                distq = (w - game.state.baseq[j]).distance()
                if distq < 6.0*(BASEMAX+1-game.inbase) and withprob(0.75):
                    contflag = True
                    if game.idebug:
                        prout("=== Abandoning base #%d at %s" % (i, w))
                    break
                elif distq < 6.0 * (BASEMAX+1-game.inbase):
                    if game.idebug:
                        prout("=== Saving base #%d, close to #%d" % (i, j))
            if not contflag:
                break
        if game.idebug:
            prout("=== Placing base #%d in quadrant %s" % (i, w))
        game.state.baseq.append(w)
        game.state.galaxy[w.i][w.j].starbase = game.state.chart[w.i][w.j].starbase = True
    # Position ordinary Klingon Battle Cruisers
    krem = game.inkling
    klumper = 0.25*game.skill*(9.0-game.length)+1.0
    if klumper > MAXKLQUAD:
        klumper = MAXKLQUAD
    while True:
        r = randreal()
        klump = (1.0 - r*r)*klumper
        if klump > krem:
            klump = krem
        krem -= klump
        while True:
            w = randplace(GALSIZE)
            if not game.state.galaxy[w.i][w.j].supernova and \
               game.state.galaxy[w.i][w.j].klingons + klump <= MAXKLQUAD:
                break
        game.state.galaxy[w.i][w.j].klingons += int(klump)
        if krem <= 0:
            break
    # Position Klingon Commander Ships
    for i in range(game.incom):
        while True:
            w = randplace(GALSIZE)
            if not welcoming(w) or w in game.state.kcmdr:
                continue
            if (game.state.galaxy[w.i][w.j].klingons or withprob(0.25)):
                break
        game.state.galaxy[w.i][w.j].klingons += 1
        game.state.kcmdr.append(w)
    # Locate planets in galaxy
    for i in range(game.inplan):
        while True:
            w = randplace(GALSIZE)
            if game.state.galaxy[w.i][w.j].planet == None:
                break
        new = Planet()
        new.quadrant = w
        new.crystals = "absent"
        if (game.options & OPTION_WORLDS) and i < NINHAB:
            new.pclass = "M"        # All inhabited planets are class M
            new.crystals = "absent"
            new.known = "known"
            new.name = systnames[i]
            new.inhabited = True
        else:
            new.pclass = ("M", "N", "O")[randrange(0, 3)]
            if withprob(0.33):
                new.crystals = "present"
            new.known = "unknown"
            new.inhabited = False
        game.state.galaxy[w.i][w.j].planet = new
        game.state.planets.append(new)
    # Locate Romulans
    for i in range(game.state.nromrem):
        w = randplace(GALSIZE)
        game.state.galaxy[w.i][w.j].romulans += 1
    # Place the Super-Commander if needed
    if game.state.nscrem > 0:
        while True:
            w = randplace(GALSIZE)
            if welcoming(w):
                break
        game.state.kscmdr = w
        game.state.galaxy[w.i][w.j].klingons += 1
    # Initialize times for extraneous events
    schedule(FSNOVA, expran(0.5 * game.intime))
    schedule(FTBEAM, expran(1.5 * (game.intime / len(game.state.kcmdr))))
    schedule(FSNAP, randreal(1.0, 2.0)) # Force an early snapshot
    schedule(FBATTAK, expran(0.3*game.intime))
    unschedule(FCDBAS)
    if game.state.nscrem:
        schedule(FSCMOVE, 0.2777)
    else:
        unschedule(FSCMOVE)
    unschedule(FSCDBAS)
    unschedule(FDSPROB)
    if (game.options & OPTION_WORLDS) and game.skill >= SKILL_GOOD:
        schedule(FDISTR, expran(1.0 + game.intime))
    else:
        unschedule(FDISTR)
    unschedule(FENSLV)
    unschedule(FREPRO)
    # Place thing (in tournament game, we don't want one!)
    # New in SST2K: never place the Thing near a starbase.
    # This makes sense and avoids a special case in the old code.
    global thing
    if game.tourn is None:
        while True:
            thing = randplace(GALSIZE)
            if thing not in game.state.baseq:
                break
    skip(2)
    game.state.snap = False
    if game.skill == SKILL_NOVICE:
        prout(_("It is stardate %d. The Federation is being attacked by") % int(game.state.date))
        prout(_("a deadly Klingon invasion force. As captain of the United"))
        prout(_("Starship U.S.S. Enterprise, it is your mission to seek out"))
        prout(_("and destroy this invasion force of %d battle cruisers.") % ((game.inkling + game.incom + game.inscom)))
        prout(_("You have an initial allotment of %d stardates to complete") % int(game.intime))
        prout(_("your mission.  As you proceed you may be given more time."))
        skip(1)
        prout(_("You will have %d supporting starbases.") % (game.inbase))
        proutn(_("Starbase locations-  "))
    else:
        prout(_("Stardate %d.") % int(game.state.date))
        skip(1)
        prout(_("%d Klingons.") % (game.inkling + game.incom + game.inscom))
        prout(_("An unknown number of Romulans."))
        if game.state.nscrem:
            prout(_("And one (GULP) Super-Commander."))
        prout(_("%d stardates.") % int(game.intime))
        proutn(_("%d starbases in ") % game.inbase)
    for i in range(game.inbase):
        proutn(repr(game.state.baseq[i]))
        proutn("  ")
    skip(2)
    proutn(_("The Enterprise is currently in Quadrant %s") % game.quadrant)
    proutn(_(" Sector %s") % game.sector)
    skip(2)
    prout(_("Good Luck!"))
    if game.state.nscrem:
        prout(_("  YOU'LL NEED IT."))
    waitfor()
    clrscr()
    setwnd(message_window)
    newqad()
    if len(game.enemies) - (thing == game.quadrant) - (game.tholian != None):
        game.shldup = True
    if game.neutz:        # bad luck to start in a Romulan Neutral Zone
        attack(torps_ok=False)
 
def choose():
    "Choose your game type."
    while True:
        game.tourn = game.length = 0
        game.thawed = False
        game.skill = SKILL_NONE
        scanner.chew()
#        if not scanner.inqueue: # Can start with command line options
        proutn(_("Would you like a regular, tournament, or saved game? "))
        scanner.nexttok()
        if scanner.sees("tournament"):
            while scanner.nexttok() == "IHEOL":
                proutn(_("Type in tournament number-"))
            if scanner.real == 0:
                scanner.chew()
                continue # We don't want a blank entry
            game.tourn = int(round(scanner.real))
            random.seed(scanner.real)
            if logfp:
                logfp.write("# random.seed(%d)\n" % scanner.real)
            break
        if scanner.sees("saved") or scanner.sees("frozen"):
            if thaw():
                continue
            scanner.chew()
            if game.passwd == None:
                continue
            if not game.alldone:
                game.thawed = True # No plaque if not finished
            report()
            waitfor()
            return True
        if scanner.sees("regular"):
            break
        proutn(_("What is \"%s\"? ") % scanner.token)
        scanner.chew()
    while game.length==0 or game.skill==SKILL_NONE:
        if scanner.nexttok() == "IHALPHA":
            if scanner.sees("short"):
                game.length = 1
            elif scanner.sees("medium"):
                game.length = 2
            elif scanner.sees("long"):
                game.length = 4
            elif scanner.sees("novice"):
                game.skill = SKILL_NOVICE
            elif scanner.sees("fair"):
                game.skill = SKILL_FAIR
            elif scanner.sees("good"):
                game.skill = SKILL_GOOD
            elif scanner.sees("expert"):
                game.skill = SKILL_EXPERT
            elif scanner.sees("emeritus"):
                game.skill = SKILL_EMERITUS
            else:
                proutn(_("What is \""))
                proutn(scanner.token)
                prout("\"?")
        else:
            scanner.chew()
            if game.length==0:
                proutn(_("Would you like a Short, Medium, or Long game? "))
            elif game.skill == SKILL_NONE:
                proutn(_("Are you a Novice, Fair, Good, Expert, or Emeritus player? "))
    # Choose game options -- added by ESR for SST2K
    if scanner.nexttok() != "IHALPHA":
        scanner.chew()
        proutn(_("Choose your game style (plain, almy, fancy or just press enter): "))
        scanner.nexttok()
    if scanner.sees("plain"):
        # Approximates the UT FORTRAN version.
        game.options &=~ (OPTION_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
        game.options |= OPTION_PLAIN
    elif scanner.sees("almy"):
        # Approximates Tom Almy's version.
        game.options &=~ (OPTION_THINGY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS)
        game.options |= OPTION_ALMY
    elif scanner.sees("fancy") or scanner.sees("\n"):
        pass
    elif len(scanner.token):
        proutn(_("What is \"%s\"?") % scanner.token)
    game.options &=~ OPTION_COLOR
    setpassword()
    if game.passwd == "debug":
        game.idebug = True
        prout("=== Debug mode enabled.")
    # Use parameters to generate initial values of things
    game.damfac = 0.5 * game.skill
    game.inbase = randrange(BASEMIN, BASEMAX+1)
    game.inplan = 0
    if game.options & OPTION_PLANETS:
        game.inplan += randrange(int(MAXUNINHAB/2), int(MAXUNINHAB+1))
    if game.options & OPTION_WORLDS:
        game.inplan += int(NINHAB)
    game.state.nromrem = game.inrom = randrange(2 *game.skill)
    game.state.nscrem = game.inscom = (game.skill > SKILL_FAIR)
    game.state.remtime = 7.0 * game.length
    game.intime = game.state.remtime
    game.state.remkl = game.inkling = 2.0*game.intime*((game.skill+1 - 2*randreal())*game.skill*0.1+.15)
    game.incom = min(MINCMDR, int(game.skill + 0.0625*game.inkling*randreal()))
    game.state.remres = (game.inkling+4*game.incom)*game.intime
    game.inresor = game.state.remres
    if game.inkling > 50:
        game.state.inbase += 1
    return False
 
def dropin(iquad=None):
    "Drop a feature on a random dot in the current quadrant."
    while True:
        w = randplace(QUADSIZE)
        if game.quad[w.i][w.j] == '.':
            break
    if iquad is not None:
        game.quad[w.i][w.j] = iquad
    return w
 
def newcnd():
    "Update our alert status."
    game.condition = "green"
    if game.energy < 1000.0:
        game.condition = "yellow"
    if game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons or game.state.galaxy[game.quadrant.i][game.quadrant.j].romulans:
        game.condition = "red"
    if not game.alive:
        game.condition="dead"
 
def newkling():
    "Drop new Klingon into current quadrant."
    return Enemy('K', loc=dropin(), power=randreal(300,450)+25.0*game.skill)
 
def sortenemies():
    "Sort enemies by distance so 'nearest' is meaningful."
    game.enemies.sort(key=lambda x: x.kdist)
 
def newqad():
    "Set up a new state of quadrant, for when we enter or re-enter it."
    game.justin = True
    game.iplnet = None
    game.neutz = game.inorbit = game.landed = False
    game.ientesc = game.iseenit = False
    # Create a blank quadrant
    game.quad = fill2d(QUADSIZE, lambda i, j: '.')
    if game.iscate:
        # Attempt to escape Super-commander, so tbeam back!
        game.iscate = False
        game.ientesc = True
    q = game.state.galaxy[game.quadrant.i][game.quadrant.j]
    # cope with supernova
    if q.supernova:
        return
    game.klhere = q.klingons
    game.irhere = q.romulans
    # Position Starship
    game.quad[game.sector.i][game.sector.j] = game.ship
    game.enemies = []
    if q.klingons:
        # Position ordinary Klingons
        for i in range(game.klhere):
            newkling()
        # If we need a commander, promote a Klingon
        for cmdr in game.state.kcmdr:
            if cmdr == game.quadrant:
                e = game.enemies[game.klhere-1]
                game.quad[e.location.i][e.location.j] = 'C'
                e.power = randreal(950,1350) + 50.0*game.skill
                break
        # If we need a super-commander, promote a Klingon
        if game.quadrant == game.state.kscmdr:
            e = game.enemies[0]
            game.quad[e.location.i][e.location.j] = 'S'
            e.power = randreal(1175.0,  1575.0) + 125.0*game.skill
            game.iscate = (game.state.remkl > 1)
    # Put in Romulans if needed
    for i in range(q.romulans):
        Enemy('R', loc=dropin(), power=randreal(400.0,850.0)+50.0*game.skill)
    # If quadrant needs a starbase, put it in
    if q.starbase:
        game.base = dropin('B')
    # If quadrant needs a planet, put it in
    if q.planet:
        game.iplnet = q.planet
        if not q.planet.inhabited:
            game.plnet = dropin('P')
        else:
            game.plnet = dropin('@')
    # Check for condition
    newcnd()
    # Check for RNZ
    if game.irhere > 0 and game.klhere == 0:
        game.neutz = True
        if not damaged(DRADIO):
            skip(1)
            prout(_("LT. Uhura- \"Captain, an urgent message."))
            prout(_("  I'll put it on audio.\"  CLICK"))
            skip(1)
            prout(_("INTRUDER! YOU HAVE VIOLATED THE ROMULAN NEUTRAL ZONE."))
            prout(_("LEAVE AT ONCE, OR YOU WILL BE DESTROYED!"))
    # Put in THING if needed
    if thing == game.quadrant:
        Enemy(etype='?', loc=dropin(),
                  power=randreal(6000,6500.0)+250.0*game.skill)
        if not damaged(DSRSENS):
            skip(1)
            prout(_("Mr. Spock- \"Captain, this is most unusual."))
            prout(_("    Please examine your short-range scan.\""))
    # Decide if quadrant needs a Tholian; lighten up if skill is low
    if game.options & OPTION_THOLIAN:
        if (game.skill < SKILL_GOOD and withprob(0.02)) or \
            (game.skill == SKILL_GOOD and withprob(0.05)) or \
            (game.skill > SKILL_GOOD and withprob(0.08)):
            w = Coord()
            while True:
                w.i = withprob(0.5) * (QUADSIZE-1)
                w.j = withprob(0.5) * (QUADSIZE-1)
                if game.quad[w.i][w.j] == '.':
                    break
            game.tholian = Enemy(etype='T', loc=w,
                                 power=randrange(100, 500) + 25.0*game.skill)
            # Reserve unoccupied corners
            if game.quad[0][0]=='.':
                game.quad[0][0] = 'X'
            if game.quad[0][QUADSIZE-1]=='.':
                game.quad[0][QUADSIZE-1] = 'X'
            if game.quad[QUADSIZE-1][0]=='.':
                game.quad[QUADSIZE-1][0] = 'X'
            if game.quad[QUADSIZE-1][QUADSIZE-1]=='.':
                game.quad[QUADSIZE-1][QUADSIZE-1] = 'X'
    sortenemies()
    # And finally the stars
    for i in range(q.stars):
        dropin('*')
    # Put in a few black holes
    for i in range(1, 3+1):
        if withprob(0.5):
            dropin(' ')
    # Take out X's in corners if Tholian present
    if game.tholian:
        if game.quad[0][0]=='X':
            game.quad[0][0] = '.'
        if game.quad[0][QUADSIZE-1]=='X':
            game.quad[0][QUADSIZE-1] = '.'
        if game.quad[QUADSIZE-1][0]=='X':
            game.quad[QUADSIZE-1][0] = '.'
        if game.quad[QUADSIZE-1][QUADSIZE-1]=='X':
            game.quad[QUADSIZE-1][QUADSIZE-1] = '.'
 
def setpassword():
    "Set the self-destruct password."
    if game.options & OPTION_PLAIN:
        while True:
            scanner.chew()
            proutn(_("Please type in a secret password- "))
            scanner.nexttok()
            game.passwd = scanner.token
            if game.passwd != None:
                break
    else:
        game.passwd = ""
        game.passwd += chr(ord('a')+randrange(26))
        game.passwd += chr(ord('a')+randrange(26))
        game.passwd += chr(ord('a')+randrange(26))
 
# Code from sst.c begins here
 
commands = [
    ("SRSCAN",           OPTION_TTY),
    ("STATUS",           OPTION_TTY),
    ("REQUEST",          OPTION_TTY),
    ("LRSCAN",           OPTION_TTY),
    ("PHASERS",          0),
    ("TORPEDO",          0),
    ("PHOTONS",          0),
    ("MOVE",             0),
    ("SHIELDS",           0),
    ("DOCK",             0),
    ("DAMAGES",           0),
    ("CHART",            0),
    ("IMPULSE",          0),
    ("REST",             0),
    ("WARP",             0),
    ("SCORE",            0),
    ("SENSORS",          OPTION_PLANETS),
    ("ORBIT",                OPTION_PLANETS),
    ("TRANSPORT",        OPTION_PLANETS),
    ("MINE",                OPTION_PLANETS),
    ("CRYSTALS",          OPTION_PLANETS),
    ("SHUTTLE",          OPTION_PLANETS),
    ("PLANETS",          OPTION_PLANETS),
    ("REPORT",           0),
    ("COMPUTER",         0),
    ("COMMANDS",         0),
    ("EMEXIT",                0),
    ("PROBE",                OPTION_PROBE),
    ("SAVE",                0),
    ("FREEZE",                0),        # Synonym for SAVE
    ("ABANDON",          0),
    ("DESTRUCT",         0),
    ("DEATHRAY",         0),
    ("DEBUG",            0),
    ("MAYDAY",                0),
    ("SOS",                0),        # Synonym for MAYDAY
    ("CALL",                0),        # Synonym for MAYDAY
    ("QUIT",                0),
    ("HELP",                0),
    ("",                0),
]
 
def listCommands():
    "Generate a list of legal commands."
    prout(_("LEGAL COMMANDS ARE:"))
    emitted = 0
    for (key, opt) in commands:
        if not opt or (opt & game.options):
            proutn("%-12s " % key)
            emitted += 1
            if emitted % 5 == 4:
                skip(1)
    skip(1)
 
def helpme():
    "Browse on-line help."
    key = scanner.nexttok()
    while True:
        if key == "IHEOL":
            setwnd(prompt_window)
            proutn(_("Help on what command? "))
            key = scanner.nexttok()
        setwnd(message_window)
        if key == "IHEOL":
            return
        cmds = [x[0] for x in commands]
        if scanner.token.upper() in cmds or scanner.token.upper() == "ABBREV":
            break
        skip(1)
        listCommands()
        key = "IHEOL"
        scanner.chew()
        skip(1)
    cmd = scanner.token.upper()
    for directory in docpath:
        try:
            fp = open(os.path.join(directory, "sst.doc"), "r")
            break
        except IOError:
            pass
    else:
        prout(_("Spock-  \"Captain, that information is missing from the"))
        prout(_("   computer. You need to find sst.doc and put it somewhere"))
        proutn(_("   in these directories: %s") % ":".join(docpath))
        prout(".\"")
        # This used to continue: "You need to find SST.DOC and put
        # it in the current directory."
        return
    while True:
        linebuf = fp.readline()
        if linebuf == '':
            prout(_("Spock- \"Captain, there is no information on that command.\""))
            fp.close()
            return
        if linebuf[0] == '%' and linebuf[1] == '%' and linebuf[2] == ' ':
            linebuf = linebuf[3:].strip()
            if cmd.upper() == linebuf:
                break
    skip(1)
    prout(_("Spock- \"Captain, I've found the following information:\""))
    skip(1)
    while True:
        linebuf = fp.readline()
        if "******" in linebuf:
            break
        proutn(linebuf)
    fp.close()
 
def makemoves():
    "Command-interpretation loop."
    while True:         # command loop
        drawmaps(1)
        while True:        # get a command
            hitme = False
            game.optime = game.justin = False
            scanner.chew()
            setwnd(prompt_window)
            clrscr()
            proutn("COMMAND> ")
            if scanner.nexttok() == "IHEOL":
                if game.options & OPTION_CURSES:
                    makechart()
                continue
            elif scanner.token == "":
                continue
            game.ididit = False
            clrscr()
            setwnd(message_window)
            clrscr()
            abandon_passed = False
            for (cmd, opt) in commands:
                # commands after ABANDON cannot be abbreviated
                if cmd == "ABANDON":
                    abandon_passed = True
                if cmd == scanner.token.upper() or (not abandon_passed \
                        and cmd.startswith(scanner.token.upper())):
                    break
            if cmd == "":
                listCommands()
                continue
            else:
                break
        if cmd == "SRSCAN":                # srscan
            srscan()
        elif cmd == "STATUS":                # status
            status()
        elif cmd == "REQUEST":                # status request
            request()
        elif cmd == "LRSCAN":                # long range scan
            lrscan(silent=False)
        elif cmd == "PHASERS":                # phasers
            phasers()
            if game.ididit:
                hitme = True
        elif cmd in ("TORPEDO", "PHOTONS"):        # photon torpedos
            torps()
            if game.ididit:
                hitme = True
        elif cmd == "MOVE":                # move under warp
            warp(wcourse=None, involuntary=False)
        elif cmd == "SHIELDS":                # shields
            doshield(shraise=False)
            if game.ididit:
                hitme = True
                game.shldchg = False
        elif cmd == "DOCK":                # dock at starbase
            dock(True)
            if game.ididit:
                attack(torps_ok=False)
        elif cmd == "DAMAGES":                # damage reports
            damagereport()
        elif cmd == "CHART":                # chart
            makechart()
        elif cmd == "IMPULSE":                # impulse
            impulse()
        elif cmd == "REST":                # rest
            wait()
            if game.ididit:
                hitme = True
        elif cmd == "WARP":                # warp
            setwarp()
        elif cmd == "SCORE":                # score
            score()
        elif cmd == "SENSORS":                # sensors
            sensor()
        elif cmd == "ORBIT":                # orbit
            orbit()
            if game.ididit:
                hitme = True
        elif cmd == "TRANSPORT":                # transport "beam"
            beam()
        elif cmd == "MINE":                # mine
            mine()
            if game.ididit:
                hitme = True
        elif cmd == "CRYSTALS":                # crystals
            usecrystals()
            if game.ididit:
                hitme = True
        elif cmd == "SHUTTLE":                # shuttle
            shuttle()
            if game.ididit:
                hitme = True
        elif cmd == "PLANETS":                # Planet list
            survey()
        elif cmd == "REPORT":                # Game Report
            report()
        elif cmd == "COMPUTER":                # use COMPUTER!
            eta()
        elif cmd == "COMMANDS":
            listCommands()
        elif cmd == "EMEXIT":                # Emergency exit
            clrscr()                        # Hide screen
            freeze(True)                # forced save
            raise SystemExit(1)                # And quick exit
        elif cmd == "PROBE":
            probe()                        # Launch probe
            if game.ididit:
                hitme = True
        elif cmd == "ABANDON":                # Abandon Ship
            abandon()
        elif cmd == "DESTRUCT":                # Self Destruct
            selfdestruct()
        elif cmd == "SAVE":                # Save Game
            freeze(False)
            clrscr()
            if game.skill > SKILL_GOOD:
                prout(_("WARNING--Saved games produce no plaques!"))
        elif cmd == "DEATHRAY":                # Try a desparation measure
            deathray()
            if game.ididit:
                hitme = True
        elif cmd == "DEBUGCMD":                # What do we want for debug???
            debugme()
        elif cmd == "MAYDAY":                # Call for help
            mayday()
            if game.ididit:
                hitme = True
        elif cmd == "QUIT":
            game.alldone = True                # quit the game
        elif cmd == "HELP":
            helpme()                        # get help
        while True:
            if game.alldone:
                break                # Game has ended
            if game.optime != 0.0:
                events()
                if game.alldone:
                    break        # Events did us in
            if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
                atover(False)
                continue
            if hitme and not game.justin:
                attack(torps_ok=True)
                if game.alldone:
                    break
                if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova:
                    atover(False)
                    hitme = True
                    continue
            break
        if game.alldone:
            break
    if game.idebug:
        prout("=== Ending")
 
def cramen(type):
    "Emit the name of an enemy or feature."
    if   type == 'R': s = _("Romulan")
    elif type == 'K': s = _("Klingon")
    elif type == 'C': s = _("Commander")
    elif type == 'S': s = _("Super-commander")
    elif type == '*': s = _("Star")
    elif type == 'P': s = _("Planet")
    elif type == 'B': s = _("Starbase")
    elif type == ' ': s = _("Black hole")
    elif type == 'T': s = _("Tholian")
    elif type == '#': s = _("Tholian web")
    elif type == '?': s = _("Stranger")
    elif type == '@': s = _("Inhabited World")
    else: s = "Unknown??"
    return s
 
def crmena(stars, enemy, loctype, w):
    "Emit the name of an enemy and his location."
    buf = ""
    if stars:
        buf += "***"
    buf += cramen(enemy) + _(" at ")
    if loctype == "quadrant":
        buf += _("Quadrant ")
    elif loctype == "sector":
        buf += _("Sector ")
    return buf + repr(w)
 
def crmshp():
    "Emit our ship name."
    return{'E':_("Enterprise"),'F':_("Faerie Queene")}.get(game.ship,"Ship???")
 
def stars():
    "Emit a line of stars"
    prouts("******************************************************")
    skip(1)
 
def expran(avrage):
    return -avrage*math.log(1e-7 + randreal())
 
def randplace(size):
    "Choose a random location."
    w = Coord()
    w.i = randrange(size)
    w.j = randrange(size)
    return w
 
class sstscanner:
    def __init__(self):
        self.type = None
        self.token = None
        self.real = 0.0
        self.inqueue = []
    def nexttok(self):
        # Get a token from the user
        self.real = 0.0
        self.token = ''
        # Fill the token quue if nothing here
        while not self.inqueue:
            line = cgetline()
            if curwnd==prompt_window:
                clrscr()
                setwnd(message_window)
                clrscr()
            if line == '':
                return None
            if not line:
                continue
            else:
                self.inqueue = line.lstrip().split() + ["\n"]
        # From here on in it's all looking at the queue
        self.token = self.inqueue.pop(0)
        if self.token == "\n":
            self.type = "IHEOL"
            return "IHEOL"
        try:
            self.real = float(self.token)
            self.type = "IHREAL"
            return "IHREAL"
        except ValueError:
            pass
        # Treat as alpha
        self.token = self.token.lower()
        self.type = "IHALPHA"
        self.real = None
        return "IHALPHA"
    def append(self, tok):
        self.inqueue.append(tok)
    def push(self, tok):
        self.inqueue.insert(0, tok)
    def waiting(self):
        return self.inqueue
    def chew(self):
        # Demand input for next scan
        self.inqueue = []
        self.real = self.token = None
    def sees(self, s):
        # compares s to item and returns true if it matches to the length of s
        return s.startswith(self.token)
    def int(self):
        # Round token value to nearest integer
        return int(round(scanner.real))
    def getcoord(self):
        s = Coord()
        scanner.nexttok()
        if scanner.type != "IHREAL":
            huh()
            return None
        s.i = scanner.int()-1
        scanner.nexttok()
        if scanner.type != "IHREAL":
            huh()
            return None
        s.j = scanner.int()-1
        return s
    def __repr__(self):
        return "<sstcanner: token=%s, type=%s, queue=%s>" % (scanner.token, scanner.type, scanner.inqueue)
 
def ja():
    "Yes-or-no confirmation."
    scanner.chew()
    while True:
        scanner.nexttok()
        if scanner.token == 'y':
            return True
        if scanner.token == 'n':
            return False
        scanner.chew()
        proutn(_("Please answer with \"y\" or \"n\": "))
 
def huh():
    "Complain about unparseable input."
    scanner.chew()
    skip(1)
    prout(_("Beg your pardon, Captain?"))
 
def debugme():
    "Access to the internals for debugging."
    proutn("Reset levels? ")
    if ja():
        if game.energy < game.inenrg:
            game.energy = game.inenrg
        game.shield = game.inshld
        game.torps = game.intorps
        game.lsupres = game.inlsr
    proutn("Reset damage? ")
    if ja():
        for i in range(NDEVICES):
            if game.damage[i] > 0.0:
                game.damage[i] = 0.0
    proutn("Toggle debug flag? ")
    if ja():
        game.idebug = not game.idebug
        if game.idebug:
            prout("Debug output ON")
        else:
            prout("Debug output OFF")
    proutn("Cause selective damage? ")
    if ja():
        for i in range(NDEVICES):
            proutn("Kill %s?" % device[i])
            scanner.chew()
            key = scanner.nexttok()
            if key == "IHALPHA" and scanner.sees("y"):
                game.damage[i] = 10.0
    proutn("Examine/change events? ")
    if ja():
        ev = Event()
        w = Coord()
        legends = {
            FSNOVA:  "Supernova       ",
            FTBEAM:  "T Beam          ",
            FSNAP:   "Snapshot        ",
            FBATTAK: "Base Attack     ",
            FCDBAS:  "Base Destroy    ",
            FSCMOVE: "SC Move         ",
            FSCDBAS: "SC Base Destroy ",
            FDSPROB: "Probe Move      ",
            FDISTR:  "Distress Call   ",
            FENSLV:  "Enslavement     ",
            FREPRO:  "Klingon Build   ",
        }
        for i in range(1, NEVENTS):
            proutn(legends[i])
            if is_scheduled(i):
                proutn("%.2f" % (scheduled(i)-game.state.date))
                if i == FENSLV or i == FREPRO:
                    ev = findevent(i)
                    proutn(" in %s" % ev.quadrant)
            else:
                proutn("never")
            proutn("? ")
            scanner.chew()
            key = scanner.nexttok()
            if key == 'n':
                unschedule(i)
                scanner.chew()
            elif key == "IHREAL":
                ev = schedule(i, scanner.real)
                if i == FENSLV or i == FREPRO:
                    scanner.chew()
                    proutn("In quadrant- ")
                    key = scanner.nexttok()
                    # "IHEOL" says to leave coordinates as they are
                    if key != "IHEOL":
                        if key != "IHREAL":
                            prout("Event %d canceled, no x coordinate." % (i))
                            unschedule(i)
                            continue
                        w.i = int(round(scanner.real))
                        key = scanner.nexttok()
                        if key != "IHREAL":
                            prout("Event %d canceled, no y coordinate." % (i))
                            unschedule(i)
                            continue
                        w.j = int(round(scanner.real))
                        ev.quadrant = w
        scanner.chew()
    proutn("Induce supernova here? ")
    if ja():
        game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova = True
        atover(True)
 
if __name__ == '__main__':
    import getopt, socket
    try:
        global line, thing, game
        game = None
        thing = Thingy()
        game = Gamestate()
        game.options = OPTION_ALL &~ (OPTION_IOMODES | OPTION_PLAIN | OPTION_ALMY)
        if os.getenv("TERM"):
            game.options |= OPTION_CURSES
        else:
            game.options |= OPTION_TTY
        seed = int(time.time())
        (options, arguments) = getopt.getopt(sys.argv[1:], "r:s:txV")
        replay = False
        for (switch, val) in options:
            if switch == '-r':
                try:
                    replayfp = open(val, "r")
                except IOError:
                    sys.stderr.write("sst: can't open replay file %s\n" % val)
                    raise SystemExit(1)
                try:
                    line = replayfp.readline().strip()
                    (leader, __, seed_) = line.split()
                    seed = eval(seed_)
                    sys.stderr.write("sst2k: seed set to %s\n" % seed)
                    line = replayfp.readline().strip()
                    arguments += line.split()[2:]
                    replay = True
                except ValueError:
                    sys.stderr.write("sst: replay file %s is ill-formed\n"% val)
                    raise SystemExit(1)
                game.options |= OPTION_TTY
                game.options &=~ OPTION_CURSES
            elif switch == '-s':
                seed = int(val)
            elif switch == '-t':
                game.options |= OPTION_TTY
                game.options &=~ OPTION_CURSES
            elif switch == '-x':
                game.idebug = True
            elif switch == '-V':
                print("SST2K", version)
                raise SystemExit(0)
            else:
                sys.stderr.write("usage: sst [-t] [-x] [startcommand...].\n")
                raise SystemExit(1)
        # where to save the input in case of bugs
        if "TMPDIR" in os.environ:
            tmpdir = os.environ['TMPDIR']
        else:
            tmpdir = "/tmp"
        try:
            logfp = open(os.path.join(tmpdir, "sst-input.log"), "w")
        except IOError:
            sys.stderr.write("sst: warning, can't open logfile\n")
            sys.exit(1)
        if logfp:
            logfp.write("# seed %s\n" % seed)
            logfp.write("# options %s\n" % " ".join(arguments))
            logfp.write("# SST2K version %s\n" % version)
            logfp.write("# recorded by %s@%s on %s\n" % \
                    (getpass.getuser(),socket.gethostname(),time.ctime()))
        random.seed(seed)
        scanner = sstscanner()
        for arg in arguments:
            scanner.append(arg)
        try:
            iostart()
            while True: # Play a game
                setwnd(fullscreen_window)
                clrscr()
                prelim()
                setup()
                if game.alldone:
                    score()
                    game.alldone = False
                else:
                    makemoves()
                if replay:
                    break
                skip(1)
                stars()
                skip(1)
                if game.tourn and game.alldone:
                    proutn(_("Do you want your score recorded?"))
                    if ja():
                        scanner.chew()
                        scanner.push("\n")
                        freeze(False)
                scanner.chew()
                proutn(_("Do you want to play again? "))
                if not ja():
                    break
            skip(1)
            prout(_("May the Great Bird of the Galaxy roost upon your home planet."))
        finally:
            ioend()
        raise SystemExit(0)
    except KeyboardInterrupt:
        if logfp:
            logfp.close()
        print("")
 
# End.
  • sst2k.py.txt
  • Última modificación: 2024/08/01 19:48
  • por peron