X-Git-Url: https://jxself.org/git/?p=super-star-trek.git;a=blobdiff_plain;f=sst.py;h=a4ed80e97f5d5b29f23421e7846f9b4fa0816ee2;hp=3f77f98b179e9125fdb72c448880225aa78d2f08;hb=cd424d0d1d9dcca9adf104d8615896b9432da8b6;hpb=6545dc8096266e9b7f4e765a25ddced052d2c8b8 diff --git a/sst.py b/sst.py index 3f77f98..a4ed80e 100755 --- a/sst.py +++ b/sst.py @@ -12,6 +12,7 @@ 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, 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. @@ -22,7 +23,7 @@ except ImportError: version = "2.1" -docpath = (".", "../doc", "/usr/share/doc/sst") +docpath = (".", "doc/", "/usr/share/doc/sst/") def _(st): return gettext.gettext(st) @@ -42,6 +43,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 @@ -211,22 +215,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,15 +243,16 @@ 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 +DDRAY = 14 +DDSP = 15 +DCLOAK = 16 +NDEVICES = 17 # Number of devices SKILL_NONE = 0 SKILL_NOVICE = 1 @@ -393,6 +400,12 @@ 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 recompute(self): # Stas thinks this should be (C expression): # game.state.remkl + len(game.state.kcmdr) > 0 ? @@ -424,6 +437,7 @@ FDRAY = 18 FTRIBBLE = 19 FHOLE = 20 FCREW = 21 +FCLOAK = 22 def withprob(p): return random.random() < p @@ -489,7 +503,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: # @@ -901,6 +915,85 @@ 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": + 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 damage(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 be 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" @@ -1026,6 +1119,7 @@ def randdevice(): 20, # DSHCTRL: high-speed shield controller 2.0% 10, # DDRAY: death ray 1.0% 30, # DDSP: deep-space probes 3.0% + 0, # DCLOAK: the cloaking device 0.0 ) assert(sum(weights) == 1000) idx = randrange(1000) @@ -1292,7 +1386,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 +1404,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 @@ -1632,7 +1731,9 @@ 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: @@ -1741,9 +1842,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 +2083,67 @@ 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) + 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)) + x *= 2.5; # would originally have been equivalent of 1.4, + # but we want command to work more often, more humanely */ + #prout(_("Prob = %d (%.4f)\n", i, 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 > brigfree: + prout(_("%d Klingons die because there is no room for them in the brig.") % i-brigfree) + i = brigfree + brigfree -= i + prout(_("%d captives taken") % i) + deadkl(weakest.location, weakest.type, game.sector) + if (game.state.remkl + len(game.state.kcmdr) + game.state.nscrem)<=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,6 +2307,11 @@ 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 @@ -2196,7 +2362,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.state.iscloaked or \ ictbeam or istract or \ game.condition == "docked" or game.isatb == 1 or game.iscate: return @@ -2217,7 +2383,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 +2400,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 @@ -2398,8 +2564,8 @@ 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 @@ -2469,7 +2635,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 +2724,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 - hits[-1] + 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 +2763,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 +2777,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 @@ -2775,7 +2945,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 +2954,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 +2967,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() @@ -2903,7 +3077,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,8 +3120,16 @@ 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': @@ -2997,6 +3179,7 @@ def score(): + 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 @@ -3014,6 +3197,9 @@ def score(): 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 +3230,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: @@ -3135,7 +3327,6 @@ def iostart(): # 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") @@ -3233,7 +3424,7 @@ 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() @@ -3243,20 +3434,20 @@ def proutn(line): clrscr() # Uncomment this to debug curses problems if logfp: - logfp.write("#curses: at %s proutn(%s)\n" % ((y, x), repr(line))) - curwnd.addstr(line) + 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 +3461,24 @@ 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" + linein = eval(input()) + "\n" 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." @@ -3499,8 +3690,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() @@ -3515,6 +3706,8 @@ def imove(icourse=None, noattack=False): # check for edge of galaxy kinks = 0 while True: + + kink = False if icourse.final.i < 0: icourse.final.i = -icourse.final.i @@ -3562,9 +3755,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,10 +3801,16 @@ 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): @@ -3652,8 +3853,11 @@ def dock(verbose): 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 + 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,6 +3866,10 @@ def dock(verbose): game.torps = game.intorps game.lsupres = game.inlsr game.state.crew = FULLCREW + 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 not damaged(DRADIO) and \ ((is_scheduled(FCDBAS) or game.isatb == 1) and not game.iseenit): # get attack report from base @@ -3679,7 +3887,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" @@ -3821,16 +4029,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 +4053,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 +4068,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 +4088,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 +4096,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 +4112,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) @@ -4247,11 +4458,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() @@ -4363,12 +4578,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 +4610,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. @@ -4913,6 +5130,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 +5159,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.")) @@ -5043,12 +5272,21 @@ 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: + if game.quad[i][j] in ('E', 'F'): + 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)) + if game.iscloaked: highvideo() proutn("%c " % game.quad[i][j]) textcolor(DEFAULT) @@ -5065,6 +5303,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: @@ -5394,6 +5634,7 @@ device = ( _("Shield Control"), \ _("Death Ray"), \ _("D. S. Probe"), \ + _("Cloaking Device"), \ ) def setup(): @@ -5680,17 +5921,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,7 +5943,7 @@ 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 @@ -5748,7 +5988,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: @@ -5782,7 +6022,7 @@ def newqad(): 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): + 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 +6078,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 @@ -5889,7 +6129,6 @@ commands = [ ("IMPULSE", 0), ("REST", 0), ("WARP", 0), - ("SCORE", 0), ("SENSORS", OPTION_PLANETS), ("ORBIT", OPTION_PLANETS), ("TRANSPORT", OPTION_PLANETS), @@ -5907,12 +6146,15 @@ 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), ("", 0), ] @@ -5984,6 +6226,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 +6251,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": @@ -6029,10 +6278,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 +6308,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 +6357,8 @@ def makemoves(): deathray() if game.ididit: hitme = True + elif cmd == "CAPTURE": + capture() elif cmd == "DEBUGCMD": # What do we want for debug??? debugme() elif cmd == "MAYDAY": # Call for help @@ -6118,6 +6369,8 @@ def makemoves(): game.alldone = True # quit the game elif cmd == "HELP": helpme() # get help + elif cmd == "SCORE": + score() # see current score while True: if game.alldone: break # Game has ended @@ -6142,27 +6395,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 +6455,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 +6497,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,7 +6620,6 @@ def debugme(): atover(True) if __name__ == '__main__': - import getopt, socket try: #global line, thing, game game = None