X-Git-Url: https://jxself.org/git/?p=super-star-trek.git;a=blobdiff_plain;f=sst.py;h=b6ac039828e5d872e3309f71aceaf78d239d6cbe;hp=cf32126f814a658cd22307d48f188e35636b2809;hb=b9262a12fb1dc8dc7d4772fd72c88e79e582d1c6;hpb=526bf76486558f099344cd60818b41a50f727283 diff --git a/sst.py b/sst.py index cf32126..b6ac039 100755 --- a/sst.py +++ b/sst.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 """ sst.py -- Super Star Trek 2K @@ -11,7 +11,8 @@ 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 +import os, sys, math, curses, time, pickle, random, copy, gettext, getpass +import getopt, socket, locale # This import only works on Unixes. The intention is to enable # Ctrl-P, Ctrl-N, and friends in Cmd. @@ -20,9 +21,15 @@ try: except ImportError: pass -version = "2.1" +# Prevent lossage under Python 3 +try: + my_input = raw_input +except NameError: + my_input = input + +version = "2.3" -docpath = (".", "../doc", "/usr/share/doc/sst") +docpath = (".", "doc/", "/usr/share/doc/sst/") def _(st): return gettext.gettext(st) @@ -42,6 +49,9 @@ 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 +ALGERON = 2311 # Date of the Treaty of Algeron + + DEFAULT = -1 BLACK = 0 BLUE = 1 @@ -76,8 +86,6 @@ class Coord: 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): @@ -162,6 +170,9 @@ class Quadrant: self.supernova = False self.charted = False self.status = "secure" # Could be "secure", "distressed", "enslaved" + def __str__(self): + return "" % self.__dict__ + __repr__ = __str__ class Page: def __init__(self): @@ -182,10 +193,9 @@ def fill2d(size, fillfun): class Snapshot: def __init__(self): - self.snap = False # snapshot taken + self.snap = False # snapshot taken self.crew = 0 # crew complement - self.remkl = 0 # remaining klingons - self.nscrem = 0 # remaining super commanders + self.nscrem = 0 # remaining super commanders self.starkl = 0 # destroyed stars self.basekl = 0 # destroyed bases self.nromrem = 0 # Romulans remaining @@ -202,6 +212,10 @@ class Snapshot: self.galaxy = fill2d(GALSIZE, lambda i_unused, j_unused: Quadrant()) # the starchart self.chart = fill2d(GALSIZE, lambda i_unused, j_unused: Page()) + def traverse(self): + for i in range(GALSIZE): + for j in range(GALSIZE): + yield (i, j, self.galaxy[i][j]) class Event: def __init__(self): @@ -211,22 +225,24 @@ class Event: # 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) +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_CAPTURE = 0x00002000 # Enable BSD-Trek capture (Almy, 2013). +OPTION_CLOAK = 0x80004000 # Enable BSD-Trek capture (Almy, 2013). +OPTION_PLAIN = 0x01000000 # user chose plain game +OPTION_ALMY = 0x02000000 # user chose Almy variant +OPTION_COLOR = 0x04000000 # enable color display (ESR, 2010) # Define devices DSRSENS = 0 @@ -237,22 +253,23 @@ DLIFSUP = 4 DWARPEN = 5 DIMPULS = 6 DSHIELD = 7 -DRADIO = 0 -DSHUTTL = 9 -DCOMPTR = 10 +DRADIO = 8 +DSHUTTL = 9 +DCOMPTR = 10 DNAVSYS = 11 -DTRANSP = 12 +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 +DDRAY = 14 +DDSP = 15 +DCLOAK = 16 +NDEVICES = 17 # 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) @@ -260,8 +277,8 @@ 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 +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 @@ -270,10 +287,10 @@ 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 +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 @@ -305,7 +322,9 @@ class Enemy: else: self.location = Coord() self.kdist = self.kavgd = None - game.enemies.remove(self) + # Guard prevents failure on Tholian or thingy + if self in game.enemies: + game.enemies.remove(self) return motion def __repr__(self): return "<%s,%s.%f>" % (self.type, self.location, self.power) # For debugging @@ -393,14 +412,25 @@ class Gamestate: self.perdate = 0.0 # rate of kills self.idebug = False # Debugging instrumentation enabled? self.statekscmdr = None # No SuperCommander coordinates yet. + self.brigcapacity = 400 # Enterprise brig capacity + self.brigfree = 400 # How many klingons can we put in the brig? + self.kcaptured = 0 # Total Klingons captured, for scoring. + self.iscloaked = False # Cloaking device on? + self.ncviol = 0 # Algreon treaty violations + self.isviolreported = False # We have been warned + def remkl(self): + return sum([q.klingons for (_i, _j, q) in list(self.state.traverse())]) 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 + # game.remkl() + len(game.state.kcmdr) > 0 ? + # game.state.remres/(game.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)) + self.state.remtime = self.state.remres/(self.remkl() + 4*len(self.state.kcmdr)) + def unwon(self): + "Are there Klingons remaining?" + return self.remkl() FWON = 0 FDEPLETE = 1 @@ -424,6 +454,7 @@ FDRAY = 18 FTRIBBLE = 19 FHOLE = 20 FCREW = 21 +FCLOAK = 22 def withprob(p): return random.random() < p @@ -489,7 +520,7 @@ def tryexit(enemy, look, irun): game.state.kcmdr.append(iq) break # report move out of quadrant. - return [(True, enemy, oldloc, ibq)] + return [(True, enemy, oldloc, iq)] # The bad-guy movement algorithm: # @@ -685,7 +716,7 @@ def moveklings(): return tacmoves def movescom(iq, avoid): - "Commander movement helper." + "Supercommander 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 @@ -733,7 +764,7 @@ def supercommander(): 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 \ + avoid = ((game.incom - len(game.state.kcmdr) + game.inkling - game.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 @@ -901,6 +932,86 @@ def movetholian(): # Code from battle.c begins here +def cloak(): + "Change cloaking-device status." + if game.ship == 'F': + prout(_("Ye Faerie Queene hath no cloaking device.")); + return + + key = scanner.nexttok() + + if key == "IHREAL": + huh() + return + + action = None + if key == "IHALPHA": + if scanner.sees("on"): + if game.iscloaked: + prout(_("The cloaking device has already been switched on.")) + return + action = "CLON" + elif scanner.sees("off"): + if not game.iscloaked: + prout(_("The cloaking device has already been switched off.")) + return + action = "CLOFF" + else: + huh() + return + else: + if not game.iscloaked: + proutn(_("Switch cloaking device on? ")) + if not ja(): + return + action = "CLON" + else: + proutn(_("Switch cloaking device off? ")) + if not ja(): + return + action = "CLOFF" + if action == None: + return; + + if action == "CLOFF": + if game.irhere and game.state.date >= ALGERON and not game.isviolreported: + prout(_("Spock- \"Captain, the Treaty of Algeron is in effect.\n Are you sure this is wise?\"")) + if not ja(): + return; + prout("Engineer Scott- \"Aye, Sir.\""); + game.iscloaked = False; + if game.irhere and game.state.date >= ALGERON and not game.isviolreported: + prout(_("The Romulan ship discovers you are breaking the Treaty of Algeron!")) + game.ncviol += 1 + game.isviolreported = True + + #if (neutz and game.state.date >= ALGERON) finish(FCLOAK); + return; + + if action == "CLON": + if damaged(DCLOAK): + prout(_("Engineer Scott- \"The cloaking device is damaged, Sir.\"")) + return; + + if game.condition == "docked": + prout(_("You cannot cloak while docked.")) + + if game.state.date >= ALGERON and not game.isviolreported: + prout(_("Spock- \"Captain, using the cloaking device is a violation")) + prout(_(" of the Treaty of Algeron. Considering the alternatives,")) + proutn(_(" are you sure this is wise? ")) + if not ja(): + return + prout(_("Engineer Scott- \"Cloaking device has engaging, Sir...\"")) + attack(True) + prout(_("Engineer Scott- \"Cloaking device has engaged, Sir.\"")) + game.iscloaked = True + + if game.irhere and game.state.date >= ALGERON and not game.isviolreported: + prout(_("The Romulan ship discovers you are breaking the Treaty of Algeron!")) + game.ncviol += 1 + game.isviolreported = True + def doshield(shraise): "Change shield status." action = "NONE" @@ -1010,22 +1121,23 @@ def doshield(shraise): 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% + 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% + 135, # DSHIELD: deflector shields 13.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% + 10, # DDRAY: death ray 1.0% + 30, # DDSP: deep-space probes 3.0% + 10, # DCLOAK: the cloaking device 1.0 ) assert(sum(weights) == 1000) idx = randrange(1000) @@ -1073,7 +1185,7 @@ def collision(rammed, enemy): game.damage[dev] += game.optime + extradm game.shldup = False prout(_("***Shields are down.")) - if game.state.remkl + len(game.state.kcmdr) + game.state.nscrem: + if game.unwon(): announce() damagereport() else: @@ -1292,7 +1404,7 @@ def fry(hit): 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")): + if not (game.damage[j]<0.0 or (j == DSHUTTL and game.iscraft != "onship") or (j == DCLOAK and game.ship != 'E' or j == DDRAY)): break cdam.append(j) extradm = (hit*game.damfac)/(ncrit*randreal(75, 100)) @@ -1310,10 +1422,15 @@ def fry(hit): if damaged(DSHIELD) and game.shldup: prout(_("***Shields knocked down.")) game.shldup = False + if damaged(DCLOAK) and game.iscloaked: + prout(_("***Cloaking device rendered inoperative.")) + game.iscloaked = False def attack(torps_ok): # bad guy attacks us # torps_ok == False forces use of phasers in an attack + if game.iscloaked: + return # game could be over at this point, check if game.alldone: return @@ -1401,7 +1518,7 @@ def attack(torps_ok): 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: + if game.unwon() == 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 @@ -1498,16 +1615,16 @@ def deadkl(w, etype, mv): # Killed some type of Klingon game.state.galaxy[game.quadrant.i][game.quadrant.j].klingons -= 1 game.klhere -= 1 - if type == 'C': + if etype == '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': + elif etype == 'K': + pass + elif etype == 'S': game.state.nscrem -= 1 game.state.kscmdr.invalidate() game.isatb = 0 @@ -1516,7 +1633,7 @@ def deadkl(w, etype, mv): 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: + if game.unwon() == 0: return game.recompute() # Remove enemy ship from arrays describing local conditions @@ -1632,12 +1749,14 @@ def torps(): prout(_("***Photon tubes damaged by misfire.")) game.damage[DPHOTON] = game.damfac * randreal(1.0, 3.0) break - if game.shldup or game.condition == "docked": + if game.iscloaked: + dispersion *= 1.2 + elif 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: + if game.unwon()<=0: finish(FWON) def overheat(rpow): @@ -1717,11 +1836,10 @@ def hittem(hits): skip(1) if kpow == 0: deadkl(w, ienm, w) - if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)==0: + if game.unwon()==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: @@ -1741,9 +1859,9 @@ def phasers(): no = False itarg = True msgflag = True - rpow = 0 + rpow = 0.0 automode = "NOTSET" - key = 0 + key = "" skip(1) # SR sensors and Computer are needed for automode if damaged(DSRSENS) or damaged(DCOMPTR): @@ -1982,7 +2100,69 @@ def phasers(): game.shldup = False overheat(rpow) -# Code from events,c begins here. + +def capture(): + game.ididit = False # Nothing if we fail + game.optime = 0.0; + + # Make sure there is room in the brig */ + if game.brigfree == 0: + prout(_("Security reports the brig is already full.")) + return; + + if damaged(DRADIO): + prout(_("Uhura- \"We have no subspace radio communication, sir.\"")) + return + + if damaged(DTRANSP): + prout(_("Scotty- \"Transporter damaged, sir.\"")) + return + + # find out if there are any at all + if game.klhere < 1: + prout(_("Uhura- \"Getting no response, sir.\"")) + return + + # if there is more than one Klingon, find out which one */ + # Cruddy, just takes one at random. Should ask the captain. + # Nah, just select the weakest one since it is most likely to + # surrender (Tom Almy mod) + klingons = [e for e in game.enemies if e.type == 'K'] + weakest = sorted(klingons, key=lambda e: e.power)[0] + game.optime = 0.05 # This action will take some time + game.ididit = True # So any others can strike back + + # check out that Klingon + # The algorithm isn't that great and could use some more + # intelligent design + # x = 300 + 25*skill; + x = game.energy / (weakest.power * len(klingons)) + #prout(_("Stats: energy = %s, kpower = %s, klingons = %s") + # % (game.energy, weakest.power, len(klingons))) + x *= 2.5 # would originally have been equivalent of 1.4, + # but we want command to work more often, more humanely */ + #prout(_("Prob = %.4f" % x)) + # x = 100; // For testing, of course! + if x < randreal(100): + # guess what, he surrendered!!! */ + prout(_("Klingon captain at %s surrenders.") % weakest.location) + i = randreal(200) + if i > 0: + prout(_("%d Klingons commit suicide rather than be taken captive.") % (200 - i)) + if i > game.brigfree: + prout(_("%d Klingons die because there is no room for them in the brig.") % (i-brigfree)) + i = game.brigfree + game.brigfree -= i + prout(_("%d captives taken") % i) + deadkl(weakest.location, weakest.type, game.sector) + if game.unwon()<=0: + finish(FWON) + return + + # big surprise, he refuses to surrender */ + prout(_("Fat chance, captain!")) + +# 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 @@ -2146,9 +2326,14 @@ def events(): prout("== Event %d fires" % evcode) datemin = game.future[l].date xtime = datemin-game.state.date + if game.iscloaked: + game.energy -= xtime*500.0 + if game.energy <= 0: + finish(FNRG) + return game.state.date = datemin # Decrement Federation resources and recompute remaining time - game.state.remres -= (game.state.remkl+4*len(game.state.kcmdr))*xtime + game.state.remres -= (game.remkl()+4*len(game.state.kcmdr))*xtime game.recompute() if game.state.remtime <= 0: finish(FDEPLETE) @@ -2196,7 +2381,7 @@ def events(): 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 \ + if game.state.nscrem == 0 or game.iscloaked or \ ictbeam or istract or \ game.condition == "docked" or game.isatb == 1 or game.iscate: return @@ -2217,7 +2402,7 @@ def events(): continue i = randrange(len(game.state.kcmdr)) yank = (game.state.kcmdr[i]-game.quadrant).distance() - if istract or game.condition == "docked" or yank == 0: + if istract or game.condition == "docked" or game.iscloaked or yank == 0: # Drats! Have to reschedule schedule(FTBEAM, game.optime + expran(1.5*game.intime/len(game.state.kcmdr))) @@ -2234,16 +2419,16 @@ def events(): unschedule(FBATTAK) unschedule(FCDBAS) continue + ibq = None # Force battle location to persist past loop 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 + # no match found -- try later + schedule(FBATTAK, expran(0.3*game.intime)) + unschedule(FCDBAS) + continue except JumpOut: pass # commander + starbase combination found -- launch attack @@ -2381,7 +2566,7 @@ def events(): if q.klingons <= 0: q.status = "secure" continue - if game.state.remkl >= MAXKLGAME: + if game.remkl() >= MAXKLGAME: continue # full right now # reproduce one Klingon w = ev.quadrant @@ -2398,16 +2583,15 @@ def events(): if q.klingons >= MAXKLQUAD or q.supernova: continue raise JumpOut - else: - continue # search for eligible quadrant failed + # search for eligible quadrant failed + continue 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()) + newkling() # also adds it to game.enemies # recompute time left game.recompute() if communicating(): @@ -2428,7 +2612,7 @@ def wait(): if key != "IHEOL": break proutn(_("How long? ")) - scanner.chew() + scanner.chew() if key != "IHREAL": huh() return @@ -2469,7 +2653,7 @@ def wait(): if game.state.galaxy[game.quadrant.i][game.quadrant.j].supernova: break game.resting = False - game.optime = 0 + game.optime = 0.0 def nova(nov): "Star goes nova." @@ -2558,33 +2742,37 @@ def nova(nov): elif iquad == 'K': # kill klingon deadkl(neighbor, iquad, neighbor) elif iquad in ('C','S','R'): # Damage/destroy big enemies + target = None for ll in range(len(game.enemies)): if game.enemies[ll].location == neighbor: + target = game.enemies[ll] 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) + if target is not None: + target.power -= 800.0 # If firepower is lost, die + if target.power <= 0.0: + deadkl(neighbor, iquad, neighbor) + continue # neighbor loop + # Else enemy gets flung by the blast wave + newc = neighbor + neighbor - start + proutn(crmena(True, iquad, "sector", neighbor) + _(" damaged")) + if not newc.valid_sector(): + # can't leave quadrant + skip(1) + continue + iquad1 = game.quad[newc.i][newc.j] + if iquad1 == ' ': + proutn(_(", blasted into ") + crmena(False, ' ', "sector", newc)) + skip(1) + deadkl(neighbor, iquad, newc) + continue + if iquad1 != '.': + # can't move into something else + skip(1) + continue + proutn(_(", buffeted to Sector %s") % newc) + game.quad[neighbor.i][neighbor.j] = '.' + game.quad[newc.i][newc.j] = iquad + target.move(newc) # Starship affected by nova -- kick it away. dist = kount*0.1 direc = ncourse[3*(bump.i+1)+bump.j+2] @@ -2593,11 +2781,11 @@ def nova(nov): if dist == 0.0: return scourse = course(bearing=direc, distance=dist) - game.optime = scourse.time(warp=4) + game.optime = scourse.time(w=4) skip(1) prout(_("Force of nova displaces starship.")) imove(scourse, noattack=True) - game.optime = scourse.time(warp=4) + game.optime = scourse.time(w=4) return def supernova(w): @@ -2607,14 +2795,14 @@ def supernova(w): nq = copy.copy(w) else: # Scheduled supernova -- select star at random. - stars = 0 + nstars = 0 nq = Coord() for nq.i in range(GALSIZE): for nq.j in range(GALSIZE): - stars += game.state.galaxy[nq.i][nq.j].stars + nstars += game.state.galaxy[nq.i][nq.j].stars if stars == 0: return # nothing to supernova exists - num = randrange(stars) + 1 + num = randrange(nstars) + 1 for nq.i in range(GALSIZE): for nq.j in range(GALSIZE): num -= game.state.galaxy[nq.i][nq.j].stars @@ -2655,7 +2843,6 @@ def supernova(w): 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! @@ -2666,10 +2853,8 @@ def supernova(w): 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 @@ -2690,7 +2875,7 @@ def supernova(w): 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: + if game.unwon()==0 and not nq == game.quadrant: skip(2) if w is None: prout(_("Lucky you!")) @@ -2752,9 +2937,9 @@ def kaboom(): 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) + for e in game.enemies[::-1]: + if e.power*e.kdist <= whammo: + deadkl(e.location, game.quad[e.location.i][e.location.j], e.location) finish(FDILITHIUM) def killrate(): @@ -2764,7 +2949,7 @@ def killrate(): return 0 else: starting = (game.inkling + game.incom + game.inscom) - remaining = (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem) + remaining = game.unwon() return (starting - remaining)/elapsed def badpoints(): @@ -2775,7 +2960,8 @@ def badpoints(): 300*game.state.nworldkl + \ 45.0*game.nhelp +\ 100.0*game.state.basekl +\ - 3.0*game.abandoned + 3.0*game.abandoned +\ + 100*game.ncviol if game.ship == 'F': badpt += 100.0 elif game.ship is None: @@ -2783,7 +2969,7 @@ def badpoints(): return badpt def finish(ifin): - # end the game, with appropriate notfications + # end the game, with appropriate notifications igotit = False game.alldone = True skip(3) @@ -2796,6 +2982,9 @@ def finish(ifin): prout(_("You have smashed the Klingon invasion fleet and saved")) prout(_("the Federation.")) + if game.alive and game.brigcapacity-game.brigfree > 0: + game.kcaptured += game.brigcapacity-game.brigfree + prout(_("The %d captured Klingons are transferred to Star Fleet Command.") % (game.brigcapacity-game.brigfree)) game.gamewon = True if game.alive: badpt = badpoints() @@ -2854,7 +3043,7 @@ def finish(ifin): 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): + if game.unwon()*3.0 > (game.inkling + game.incom + game.inscom): prout(_("acquitted.")) skip(1) prout(_("LIVE LONG AND PROSPER.")) @@ -2903,7 +3092,7 @@ def finish(ifin): 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.")) + prout(_("converted to energy, dissipating through space.")) elif ifin == FMINING: prout(_("You are left with your landing party on")) prout(_("a wild jungle planet inhabited by primitive cannibals.")) @@ -2946,16 +3135,24 @@ def finish(ifin): 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 == FCLOAK: + game.ncviol += 1 + prout(_("You have violated the Treaty of Algeron.")) + prout(_("The Romulan Empire can never trust you again.")) elif ifin == FCREW: prout(_("Your last crew member has died.")) + if ifin != FWON and ifin != FCLOAK and game.iscloaked: + prout(_("Your ship was cloaked so your subspace radio did not receive anything.")) + prout(_("You may have missed some warning messages.")) + skip(1) 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: + if game.unwon() != 0: goodies = game.state.remres/game.inresor - baddies = (game.state.remkl + 2.0*len(game.state.kcmdr))/(game.inkling+2.0*game.incom) + baddies = (game.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")) @@ -2974,11 +3171,12 @@ def finish(ifin): prout(_("to think about pigeons.")) game.gamewon = True score() + scanner.chew() # Clean up leftovers 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: + if (timused == 0 or game.unwon() != 0) and timused < 5.0: timused = 5.0 game.perdate = killrate() ithperd = 500*game.perdate + 0.5 @@ -2991,12 +3189,14 @@ def score(): klship = 1 else: klship = 2 - game.score = 10*(game.inkling - game.state.remkl) \ + dead_ordinaries= game.inkling - game.remkl() + len(game.state.kcmdr) + game.state.nscrem + game.score = 10*(dead_ordinaries)\ + 50*(game.incom - len(game.state.kcmdr)) \ + ithperd + iwon \ + 20*(game.inrom - game.state.nromrem) \ + 200*(game.inscom - game.state.nscrem) \ - game.state.nromrem \ + + 3 * game.kcaptured \ - badpoints() if not game.alive: game.score -= 200 @@ -3008,12 +3208,15 @@ def score(): if game.state.nromrem and game.gamewon: prout(_("%6d Romulans captured %5d") % (game.state.nromrem, game.state.nromrem)) - if game.inkling - game.state.remkl: + if dead_ordinaries: prout(_("%6d ordinary Klingons destroyed %5d") % - (game.inkling - game.state.remkl, 10*(game.inkling - game.state.remkl))) + (dead_ordinaries, 10*dead_ordinaries)) 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.kcaptured: + prout(_("%d Klingons captured %5d") % + (game.kcaptured, 3 * game.kcaptured)) if game.inscom - game.state.nscrem: prout(_("%6d Super-Commander destroyed %5d") % (game.inscom - game.state.nscrem, 200*(game.inscom - game.state.nscrem))) @@ -3044,6 +3247,12 @@ def score(): if klship: prout(_("%6d ship(s) lost or destroyed %5d") % (klship, -100*klship)) + if game.ncviol > 0: + if ncviol == 1: + prout(_("1 Treaty of Algeron violation -100")) + else: + prout(_("%6d Treaty of Algeron violations %5d\n") % + (ncviol, -100*ncviol)) if not game.alive: prout(_("Penalty for getting yourself killed -200")) if game.gamewon: @@ -3073,24 +3282,9 @@ def plaque(): 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") + # This is where the ASCII art picture was emitted. + # It got garbled somewhere in the chain of transmission to the Almy version. + # We should restore it if we can find old enough FORTRAN sources. fp.write("\n\n\n") fp.write(_(" U. S. S. ENTERPRISE\n")) fp.write("\n\n\n\n") @@ -3131,11 +3325,10 @@ 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" + # for some recent versions of python2, the following enables UTF8 + # for the older ones we probably need to set C locale, and 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") @@ -3163,7 +3356,7 @@ def iostart(): 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() + (rows, _columns) = stdscr.getmaxyx() fullscreen_window = stdscr srscan_window = curses.newwin(12, 25, 0, 0) report_window = curses.newwin(11, 0, 1, 25) @@ -3212,15 +3405,15 @@ def pause_game(): sys.stdout.write('\n') proutn(prompt) if not replayfp: - input() + my_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): + for _dummy in range(i): if game.options & OPTION_CURSES: - (y, x) = curwnd.getyx() + (y, _x) = curwnd.getyx() try: curwnd.move(y+1, 0) except curses.error: @@ -3233,30 +3426,30 @@ def skip(i): else: sys.stdout.write('\n') -def proutn(line): +def proutn(proutntline): "Utter a line with no following line feed." if game.options & OPTION_CURSES: (y, x) = curwnd.getyx() - (my, mx) = curwnd.getmaxyx() + (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) + #if logfp: + # logfp.write("#curses: at %s proutn(%s)\n" % ((y, x), repr(proutntline))) + curwnd.addstr(proutntline) curwnd.refresh() else: - sys.stdout.write(line) + sys.stdout.write(proutntline) sys.stdout.flush() -def prout(line): - proutn(line) +def prout(proutline): + proutn(proutline) skip(1) -def prouts(line): +def prouts(proutsline): "Emit slowly!" - for c in line: + for c in proutsline: if not replayfp or replayfp.closed: # Don't slow down replays time.sleep(0.03) proutn(c) @@ -3270,24 +3463,28 @@ def prouts(line): def cgetline(): "Get a line of input." if game.options & OPTION_CURSES: - line = curwnd.getstr() + "\n" + linein = curwnd.getstr() + "\n" curwnd.refresh() else: if replayfp and not replayfp.closed: while True: - line = replayfp.readline() - proutn(line) - if line == '': + linein = replayfp.readline() + proutn(linein) + if linein == '': prout("*** Replay finished") replayfp.close() break - elif line[0] != "#": + elif linein[0] != "#": break else: - line = eval(input()) + "\n" + try: + linein = my_input() + "\n" + except EOFError: + prout("") + sys.exit(0) if logfp: - logfp.write(line) - return line + logfp.write(linein) + return linein def setwnd(wnd): "Change windows -- OK for this to be a no-op in tty mode." @@ -3311,7 +3508,7 @@ def setwnd(wnd): legend = "prompt" else: legend = "unknown" - logfp.write("#curses: setwnd(%s)\n" % legend) + #logfp.write("#curses: setwnd(%s)\n" % legend) curwnd = wnd # Some curses implementations get confused when you try this. try: @@ -3335,7 +3532,7 @@ def clrscr(): linecount = 0 def textcolor(color=DEFAULT): - if game.options & OPTION_COLOR: + if (game.options & OPTION_COLOR) and (game.options & OPTION_CURSES): if color == DEFAULT: curwnd.attrset(0) elif color == BLACK: @@ -3372,7 +3569,7 @@ def textcolor(color=DEFAULT): curwnd.attron(curses.color_pair(curses.COLOR_WHITE) | curses.A_BOLD) def highvideo(): - if game.options & OPTION_COLOR: + if (game.options & OPTION_COLOR) and (game.options & OPTION_CURSES): curwnd.attron(curses.A_REVERSE) # @@ -3409,7 +3606,7 @@ def put_srscan_sym(w, sym): def boom(w): "Enemy fall down, go boom." if game.options & OPTION_CURSES: - drawmaps(2) + drawmaps(0) setwnd(srscan_window) srscan_window.attron(curses.A_REVERSE) put_srscan_sym(w, game.quad[w.i][w.j]) @@ -3499,8 +3696,8 @@ def imove(icourse=None, noattack=False): 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: + # Don't set up attack if being pushed by nova or cloaked + if len(game.enemies) != 0 and not noattack and not game.iscloaked: newcnd() for enemy in game.enemies: finald = (w - enemy.location).distance() @@ -3562,9 +3759,11 @@ def imove(icourse=None, noattack=False): 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 + collision(rammed=False, enemy=enemy) + return True + # This should not happen + prout(_("Which way did he go?")) + return False elif iquad == ' ': skip(1) prouts(_("***RED ALERT! RED ALERT!")) @@ -3606,13 +3805,19 @@ def imove(icourse=None, noattack=False): 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 + if game.iscloaked: + # We can't be tractor beamed if cloaked, + # so move the event into the future + postpone(FTBEAM, game.optime + expran(1.5*game.intime/len(game.kcmdr))) + pass + else: + 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): + for _m in range(icourse.moves): icourse.nexttok() w = icourse.sector() if icourse.origin.quadrant() != icourse.location.quadrant(): @@ -3649,11 +3854,14 @@ def dock(verbose): 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: + if game.base is None 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 + if game.iscloaked: + prout(_("You cannot dock while cloaked.")) + return game.condition = "docked" - if "verbose": + if verbose: prout(_("Docked.")) game.ididit = True if game.energy < game.inenrg: @@ -3662,7 +3870,11 @@ def dock(verbose): game.torps = game.intorps game.lsupres = game.inlsr game.state.crew = FULLCREW - if not damaged(DRADIO) and \ + if game.brigcapacity-game.brigfree > 0: + prout(_("%d captured Klingons transferred to base") % (game.brigcapacity-game.brigfree)) + game.kcaptured += game.brigcapacity-game.brigfree + game.brigfree = game.brigcapacity + if communicating() 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:\"")) @@ -3679,7 +3891,7 @@ def cartesian(loc1=None, loc2=None): def getcourse(isprobe): "Get a course and distance from the user." - key = 0 + key = "" dquad = copy.copy(game.quadrant) navmode = "unspecified" itemp = "curt" @@ -3789,15 +4001,20 @@ def getcourse(isprobe): iprompt = True key = scanner.nexttok() itemp = "verbose" - if key != "IHREAL": + if key == "IHREAL": + delta.j = scanner.real + else: huh() raise TrekError - delta.j = scanner.real key = scanner.nexttok() - if key != "IHREAL": + if key == "IHREAL": + delta.i = scanner.real + elif key == "IHEOL": + delta.i = 0 + scanner.push("\n") + else: huh() raise TrekError - delta.i = scanner.real # Check for zero movement if delta.i == 0 and delta.j == 0: scanner.chew() @@ -3821,16 +4038,14 @@ class course: 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() + self.location = self.origin + self.nextlocation = None def reset(self): self.location = self.origin self.step = 0 @@ -3847,10 +4062,10 @@ class course: 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 power(self, w): + return self.distance*(w**3)*(game.shldup+1) + def time(self, w): + return 10.0*self.distance/w**2 def impulse(): "Move under impulse power." @@ -3862,10 +4077,10 @@ def impulse(): return if game.energy > 30.0: try: - course = getcourse(isprobe=False) + icourse = getcourse(isprobe=False) except TrekError: return - power = 20.0 + 100.0*course.distance + power = 20.0 + 100.0*icourse.distance else: power = 30.0 if power >= game.energy: @@ -3882,7 +4097,7 @@ def impulse(): scanner.chew() return # Make sure enough time is left for the trip - game.optime = course.distance/0.095 + game.optime = icourse.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")) @@ -3890,13 +4105,13 @@ def impulse(): if not ja(): return # Activate impulse engines and pay the cost - imove(course, noattack=False) + imove(icourse, noattack=False) game.ididit = True if game.alldone: return - power = 20.0 + 100.0*course.distance + power = 20.0 + 100.0*icourse.distance game.energy -= power - game.optime = course.distance/0.095 + game.optime = icourse.distance/0.095 if game.energy <= 0: finish(FNRG) return @@ -3906,6 +4121,11 @@ def warp(wcourse, involuntary): blooey = False; twarp = False if not involuntary: # Not WARPX entry game.ididit = False + if game.iscloaked: + scanner.chew() + skip(1) + prout(_("Engineer Scott- \"The warp engines can not be used while cloaked, Sir.\"")) + return if game.damage[DWARPEN] > 10.0: scanner.chew() skip(1) @@ -4119,7 +4339,7 @@ def atover(igrab): # 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: + if game.unwon()==0: finish(FWON) # Snova killed remaining enemy. def timwrp(): @@ -4247,11 +4467,15 @@ def mayday(): # There's one in this quadrant ddist = (game.base - game.sector).distance() else: + ibq = None # Force base-quadrant game to persist past loop ddist = FOREVER for ibq in game.state.baseq: xdist = QUADSIZE * (ibq - game.quadrant).distance() if xdist < ddist: ddist = xdist + if ibq is None: + prout(_("No starbases remain. You are alone in a hostile galaxy.")) + return # Since starbase not in quadrant, set up new quadrant game.quadrant = ibq newqad() @@ -4266,7 +4490,7 @@ def mayday(): # found one -- finish up game.sector = w break - if not game.sector.is_valid(): + if game.sector is None: prout(_("You have been lost in space...")) finish(FMATERIALIZE) return @@ -4363,12 +4587,13 @@ def abandon(): while True: # position next to base by trial and error game.quad[game.sector.i][game.sector.j] = '.' + l = QUADSIZE 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: + if l < QUADSIZE: break # found a spot game.sector.i=QUADSIZE/2 game.sector.j=QUADSIZE/2 @@ -4394,6 +4619,7 @@ def abandon(): game.lsupres=game.inlsr=3.0 game.shldup=False game.warpfac=5.0 + game.brigfree = game.brigcapacity = 300 return # Code from planets.c begins here. @@ -4444,7 +4670,7 @@ def orbit(): if damaged(DWARPEN) and damaged(DIMPULS): prout(_("Both warp and impulse engines damaged.")) return - if not game.plnet.is_valid(): + if game.plnet is None: 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: @@ -4793,9 +5019,9 @@ def deathray(): 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) + 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: + if game.unwon() == 0: finish(FWON) if (game.options & OPTION_PLAIN) == 0: prout(_("Spock- \"Captain, I believe the `Experimental Death Ray'")) @@ -4887,11 +5113,11 @@ def report(): 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)), + proutn(_("%d of %d Klingons have been killed") % (((game.inkling + game.incom + game.inscom) - game.unwon()), (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: + elif game.inkling - game.remkl() + (game.inscom - game.state.nscrem) > 0: prout(_(", but no Commanders.")) else: prout(".") @@ -4913,6 +5139,18 @@ def report(): game.iseenit = True if game.casual: prout(_("%d casualt%s suffered so far.") % (game.casual, ("y", "ies")[game.casual!=1])) + if game.brigcapacity != game.brigfree: + embriggened = brigcapacity-brigfree + if embriggened == 1: + prout(_("1 Klingon in brig")) + else: + prout(_("%d Klingons in brig.") % embriggened) + if game.kcaptured == 0: + pass + elif game.kcaptured == 1: + prout(_("1 captured Klingon turned in to Starfleet.")) + else: + prout(_("%d captured Klingons turned in to Star Fleet.") % game.kcaptured) if game.nhelp: prout(_("There were %d call%s for help.") % (game.nhelp, ("" , _("s"))[game.nhelp!=1])) if game.ship == 'E': @@ -4930,7 +5168,7 @@ def report(): proutn(_("An armed deep space probe is in ")) else: proutn(_("A deep space probe is in ")) - prout("Quadrant %s." % game.probec) + prout("Quadrant %s." % game.probe.quadrant()) if game.icrystl: if game.cryprob <= .05: prout(_("Dilithium crystals aboard ship... not yet used.")) @@ -4972,7 +5210,8 @@ def lrscan(silent): 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)) + cn = " %3d" % (game.state.chart[x][y].klingons*100 + game.state.chart[x][y].starbase * 10 + game.state.chart[x][y].stars) + proutn(((3 - len(cn)) * '.') + cn) if not silent: prout(" ") @@ -5007,7 +5246,7 @@ def chart(): scanner.chew() if (game.options & OPTION_AUTOSCAN): lrscan(silent=True) - if not damaged(DRADIO): + if communicating(): rechart() if game.lastchart < game.state.date and game.condition == "docked": prout(_("Spock- \"I revised the Star Chart from the starbase's records.\"")) @@ -5043,13 +5282,22 @@ def chart(): 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() + if game.quad[i][j] in ('E', 'F'): + if game.iscloaked: + highvideo() + textcolor({"green":GREEN, + "yellow":YELLOW, + "red":RED, + "docked":CYAN, + "dead":BROWN}[game.condition]) + else: + textcolor({'?':LIGHTMAGENTA, + 'K':LIGHTRED, + 'S':LIGHTRED, + 'C':LIGHTRED, + 'R':LIGHTRED, + 'T':LIGHTRED, + }.get(game.quad[i][j], DEFAULT)) proutn("%c " % game.quad[i][j]) textcolor(DEFAULT) else: @@ -5065,6 +5313,8 @@ def status(req=0): newcnd() prstat(_("Condition"), _("%s, %i DAMAGES") % \ (game.condition.upper(), sum([x > 0 for x in game.damage]))) + if game.iscloaked: + prout(_(", CLOAKED")) if not req or req == 3: prstat(_("Position"), "%s , %s" % (game.quadrant, game.sector)) if not req or req == 4: @@ -5096,8 +5346,7 @@ def status(req=0): % (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)) + prstat(_("Klingons Left"), "%d" % game.unwon()) if not req or req == 10: if game.options & OPTION_WORLDS: plnet = game.state.galaxy[game.quadrant.i][game.quadrant.j].planet @@ -5133,7 +5382,7 @@ def srscan(): prout(_(" [Using Base's sensors]")) else: prout(_(" Short-range scan")) - if goodScan and not damaged(DRADIO): + if goodScan and communicating(): 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 @@ -5394,6 +5643,7 @@ device = ( _("Shield Control"), \ _("Death Ray"), \ _("D. S. Probe"), \ + _("Cloaking Device"), \ ) def setup(): @@ -5485,7 +5735,7 @@ def setup(): klumper = MAXKLQUAD while True: r = randreal() - klump = (1.0 - r*r)*klumper + klump = int((1.0 - r*r)*klumper) if klump > krem: klump = krem krem -= klump @@ -5494,7 +5744,7 @@ def setup(): 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) + game.state.galaxy[w.i][w.j].klingons += klump if krem <= 0: break # Position Klingon Commander Ships @@ -5615,9 +5865,9 @@ def choose(): 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? ")) + # Do not chew here, we want to use command-line tokens + 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": @@ -5680,17 +5930,16 @@ def choose(): 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_THOLIAN | OPTION_PLANETS | OPTION_THINGY | OPTION_PROBE | OPTION_RAMMING | OPTION_MVBADDY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR | OPTION_CAPTURE | OPTION_CLOAK) 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_THINGY | OPTION_BLKHOLE | OPTION_BASE | OPTION_WORLDS | OPTION_COLOR) 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 @@ -5703,11 +5952,11 @@ def choose(): game.inplan += randrange(MAXUNINHAB/2, MAXUNINHAB+1) if game.options & OPTION_WORLDS: game.inplan += int(NINHAB) - game.state.nromrem = game.inrom = randrange(2 *game.skill) + 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.inkling = int(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 @@ -5748,7 +5997,7 @@ def newqad(): game.justin = True game.iplnet = None game.neutz = game.inorbit = game.landed = False - game.ientesc = game.iseenit = False + game.ientesc = game.iseenit = game.isviolreported = False # Create a blank quadrant game.quad = fill2d(QUADSIZE, lambda i, j: '.') if game.iscate: @@ -5766,7 +6015,7 @@ def newqad(): game.enemies = [] if q.klingons: # Position ordinary Klingons - for i in range(game.klhere): + for _i in range(game.klhere): newkling() # If we need a commander, promote a Klingon for cmdr in game.state.kcmdr: @@ -5780,9 +6029,9 @@ def newqad(): 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) + game.iscate = (game.remkl() > 1) # Put in Romulans if needed - for i in range(q.romulans): + 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: @@ -5838,10 +6087,10 @@ def newqad(): game.quad[QUADSIZE-1][QUADSIZE-1] = 'X' sortenemies() # And finally the stars - for i in range(q.stars): + for _i in range(q.stars): dropin('*') # Put in a few black holes - for i in range(1, 3+1): + for _i in range(1, 3+1): if withprob(0.5): dropin(' ') # Take out X's in corners if Tholian present @@ -5854,6 +6103,9 @@ def newqad(): game.quad[QUADSIZE-1][0] = '.' if game.quad[QUADSIZE-1][QUADSIZE-1]=='X': game.quad[QUADSIZE-1][QUADSIZE-1] = '.' + # This should guarantee that replay games don't lose info about the chart + if (game.options & OPTION_AUTOSCAN) or replayfp: + lrscan(silent=True) def setpassword(): "Set the self-destruct password." @@ -5889,7 +6141,6 @@ commands = [ ("IMPULSE", 0), ("REST", 0), ("WARP", 0), - ("SCORE", 0), ("SENSORS", OPTION_PLANETS), ("ORBIT", OPTION_PLANETS), ("TRANSPORT", OPTION_PLANETS), @@ -5907,12 +6158,16 @@ commands = [ ("ABANDON", 0), ("DESTRUCT", 0), ("DEATHRAY", 0), + ("CAPTURE", OPTION_CAPTURE), + ("CLOAK", OPTION_CLOAK), ("DEBUG", 0), ("MAYDAY", 0), ("SOS", 0), # Synonym for MAYDAY ("CALL", 0), # Synonym for MAYDAY ("QUIT", 0), ("HELP", 0), + ("SCORE", 0), + ("CURSES", 0), ("", 0), ] @@ -5984,6 +6239,11 @@ def helpme(): def makemoves(): "Command-interpretation loop." + def checkviol(): + if game.irhere and game.state.date >= ALGERON and not game.isviolreported and game.iscloaked: + prout(_("The Romulan ship discovers you are breaking the Treaty of Algeron!")) + game.ncviol += 1 + game.isviolreported = True while True: # command loop drawmaps(1) while True: # get a command @@ -6004,6 +6264,8 @@ def makemoves(): setwnd(message_window) clrscr() abandon_passed = False + cmd = "" # Force cmd to persist after loop + opt = 0 # Force opt to persist after loop for (cmd, opt) in commands: # commands after ABANDON cannot be abbreviated if cmd == "ABANDON": @@ -6018,6 +6280,8 @@ def makemoves(): huh() else: break + if game.options & OPTION_CURSES: + prout("COMMAND> %s" % cmd) if cmd == "SRSCAN": # srscan srscan() elif cmd == "STATUS": # status @@ -6029,10 +6293,12 @@ def makemoves(): elif cmd == "PHASERS": # phasers phasers() if game.ididit: + checkviol() hitme = True elif cmd in ("TORPEDO", "PHOTONS"): # photon torpedos torps() if game.ididit: + checkviol() hitme = True elif cmd == "MOVE": # move under warp warp(wcourse=None, involuntary=False) @@ -6057,8 +6323,6 @@ def makemoves(): hitme = True elif cmd == "WARP": # warp setwarp() - elif cmd == "SCORE": # score - score() elif cmd == "SENSORS": # sensors sensor() elif cmd == "ORBIT": # orbit @@ -6108,6 +6372,10 @@ def makemoves(): deathray() if game.ididit: hitme = True + elif cmd == "CAPTURE": + capture() + elif cmd == "CLOAK": + cloak() elif cmd == "DEBUGCMD": # What do we want for debug??? debugme() elif cmd == "MAYDAY": # Call for help @@ -6118,6 +6386,11 @@ def makemoves(): game.alldone = True # quit the game elif cmd == "HELP": helpme() # get help + elif cmd == "SCORE": + score() # see current score + elif cmd == "CURSES": + game.options |= (OPTION_CURSES | OPTION_COLOR) + iostart() while True: if game.alldone: break # Game has ended @@ -6142,27 +6415,27 @@ def makemoves(): if game.idebug: prout("=== Ending") -def cramen(type): +def cramen(ch): "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") + if ch == 'R': s = _("Romulan") + elif ch == 'K': s = _("Klingon") + elif ch == 'C': s = _("Commander") + elif ch == 'S': s = _("Super-commander") + elif ch == '*': s = _("Star") + elif ch == 'P': s = _("Planet") + elif ch == 'B': s = _("Starbase") + elif ch == ' ': s = _("Black hole") + elif ch == 'T': s = _("Tholian") + elif ch == '#': s = _("Tholian web") + elif ch == '?': s = _("Stranger") + elif ch == '@': s = _("Inhabited World") else: s = "Unknown??" return s -def crmena(stars, enemy, loctype, w): +def crmena(loud, enemy, loctype, w): "Emit the name of an enemy and his location." buf = "" - if stars: + if loud: buf += "***" buf += cramen(enemy) + _(" at ") if loctype == "quadrant": @@ -6202,17 +6475,17 @@ class sstscanner: self.token = '' # Fill the token quue if nothing here while not self.inqueue: - line = cgetline() + sline = cgetline() if curwnd==prompt_window: clrscr() setwnd(message_window) clrscr() - if line == '': + if sline == '': return None - if not line: + if not sline: continue else: - self.inqueue = line.lstrip().split() + ["\n"] + self.inqueue = sline.lstrip().split() + ["\n"] # From here on in it's all looking at the queue self.token = self.inqueue.pop(0) if self.token == "\n": @@ -6244,22 +6517,22 @@ class sstscanner: return s.startswith(self.token) def int(self): # Round token value to nearest integer - return int(round(scanner.real)) + return int(round(self.real)) def getcoord(self): s = Coord() - scanner.nexttok() - if scanner.type != "IHREAL": + self.nexttok() + if self.type != "IHREAL": huh() return None - s.i = scanner.int()-1 - scanner.nexttok() - if scanner.type != "IHREAL": + s.i = self.int()-1 + self.nexttok() + if self.type != "IHREAL": huh() return None - s.j = scanner.int()-1 + s.j = self.int()-1 return s def __repr__(self): - return "" % (scanner.token, scanner.type, scanner.inqueue) + return "" % (self.token, self.type, self.inqueue) def ja(): "Yes-or-no confirmation." @@ -6367,9 +6640,8 @@ def debugme(): atover(True) if __name__ == '__main__': - import getopt, socket try: - global line, thing, game + #global line, thing, game game = None thing = Thingy() game = Gamestate()