from twisted.internet import reactor from twisted.internet import task from twisted.internet import defer from colorama import Fore, Back, Style # from twisted.python import log from twisted.internet.task import coiterate from twisted.internet.defer import gatherResults from itertools import cycle import pendulum from pprint import pformat from galaxy import GameData, PORT_CLASSES, CLASSES_PORT from boxes import Boxes import logging from time import localtime BLINK = "\x1b[5m" log = logging.getLogger(__name__) class SpinningCursor(object): """ Spinner class, that handles every so many clicks s = SpinningCursor(5) # every 5 for x in range(10): if s.click(): print(s.cycle()) """ def __init__(self, every=10): self.itercycle = cycle(["/", "-", "\\", "|"]) self.count = 0 self.every = every def reset(self): self.itercycle = cycle(["/", "-", "\\", "|"]) self.count = 0 def click(self): self.count += 1 return self.count % self.every == 0 def cycle(self): return next(self.itercycle) def again(self, count: int): return self.count % count == 0 def merge(color_string): """ Given a string of colorama ANSI, merge them if you can. """ return color_string.replace("m\x1b[", ";") class PlayerInput(object): """ Player Input Example: from flexible import PlayerInput ask = PlayerInput(self.game) # abort_blank means, if the input field is blank, abort. Use error_back. d = ask.prompt("What is your quest?", 40, name="quest", abort_blank=True) # Display the user's input / but not needed. d.addCallback(ask.output) d.addCallback( lambda ignore: ask.prompt( "What is your favorite color?", 10, name="color" ) ) d.addCallback(ask.output) d.addCallback( lambda ignore: ask.prompt( "What is your least favorite number?", 12, name="number", digits=True, ) ) d.addCallback(ask.output) def show_values(show): log.debug(show) self.queue_game.put(pformat(show).replace("\n", "\n\r") + self.nl) d.addCallback(lambda ignore: show_values(ask.keep)) d.addCallback(self.welcome_back) # On error, just return back d.addErrback(self.welcome_back) """ def __init__(self, game): # I think game gives us access to everything we need self.game = game self.observer = self.game.observer self.save = None self.deferred = None self.queue_game = game.queue_game self.keep = {} # default colors # prompt self.c = merge(Style.BRIGHT + Fore.WHITE + Back.BLUE) # input self.cp = merge(Style.BRIGHT + Fore.YELLOW + Back.BLUE) # useful constants self.r = Style.RESET_ALL self.nl = "\n\r" self.bsb = "\b \b" self.keepalive = None def color(self, c): self.c = c def colorp(self, cp): self.cp = cp def alive(self): log.debug("PlayerInput.alive()") self.game.queue_player.put(" ") def prompt(self, user_prompt, limit, **kw): """ Generate prompt for user input. Note: This returns deferred. prompt = text displayed. limit = # of characters allowed. default = (text to default to) keywords: abort_blank : Abort if they give us blank text. name : Stores the input in self.keep dict. digits : Only allow 0-9 to be entered. """ log.debug("PlayerInput({0}, {1}, {2}".format(user_prompt, limit, kw)) self.limit = limit self.input = "" self.kw = kw assert self.save is None assert self.keepalive is None # Note: This clears out the server "keep alive" self.save = self.observer.save() self.observer.connect("player", self.get_input) self.keepalive = task.LoopingCall(self.alive) self.keepalive.start(30) # We need to "hide" the game output. # Otherwise it WITH mess up the user input display. self.to_player = self.game.to_player self.game.to_player = False # Display prompt # self.queue_game.put(self.r + self.nl + self.c + user_prompt + " " + self.cp) self.queue_game.put(self.r + self.c + user_prompt + self.r + " " + self.cp) # Set "Background of prompt" self.queue_game.put(" " * limit + "\b" * limit) assert self.deferred is None d = defer.Deferred() self.deferred = d log.debug("Return deferred ...") return d def get_input(self, chunk): """ Data from player (in bytes) """ chunk = chunk.decode("latin-1", "ignore") for ch in chunk: if ch == "\b": if len(self.input) > 0: self.queue_game.put(self.bsb) self.input = self.input[0:-1] else: self.queue_game.put("\a") elif ch == "\r": self.queue_game.put(self.r + self.nl) log.debug("Restore observer dispatch {0}".format(self.save)) assert not self.save is None self.observer.load(self.save) self.save = None log.debug("Disable keepalive") self.keepalive.stop() self.keepalive = None line = self.input self.input = "" assert not self.deferred is None self.game.to_player = self.to_player # If they gave us the keyword name, save the value as that name if "name" in self.kw: self.keep[self.kw["name"]] = line if "abort_blank" in self.kw and self.kw["abort_blank"]: # Abort on blank input if line.strip() == "": # Yes, input is blank, abort. log.info("errback, abort_blank") reactor.callLater( 0, self.deferred.errback, Exception("abort_blank") ) self.deferred = None return # Ok, use deferred.callback, or reactor.callLater? # self.deferred.callback(line) reactor.callLater(0, self.deferred.callback, line) self.deferred = None return elif ch.isprintable(): # Printable, but is it acceptable? if "digits" in self.kw: if not ch.isdigit(): self.queue_game.put("\a") continue if len(self.input) + 1 <= self.limit: self.input += ch self.queue_game.put(ch) else: self.queue_game.put("\a") def output(self, line): """ A default display of what they just input. """ log.debug("PlayerInput.output({0})".format(line)) self.game.queue_game.put(self.r + "[{0}]".format(line) + self.nl) return line import re # The CIMWarpReport -- is only needed if the json file gets damaged in some way. # Like when the universe gets bigbanged. :P # or needs to be reset. The warps should automatically update themselves now. class CIMWarpReport(object): def __init__(self, game): self.game = game self.queue_game = game.queue_game self.queue_player = game.queue_player self.observer = game.observer # Yes, at this point we would activate self.prompt = game.buffer self.save = self.observer.save() self.days = ("Mon", "Tues", "Wed", "Thurs", "Fri", "Sat", "Sun") # I actually don't want the player input, but I'll grab it anyway. self.observer.connect("player", self.player) self.observer.connect("prompt", self.game_prompt) self.observer.connect("game-line", self.game_line) # If we want it, it's here. self.defer = None self.to_player = self.game.to_player # Hide what's happening from the player self.game.to_player = False self.queue_player.put("^") # Activate CIM self.state = 1 # self.warpdata = {} tm = localtime() log.debug("Today is {0}".format(self.days[tm[6]])) self.queue_game.put("Loading ") # cycle eats last char. self.warpcycle = SpinningCursor() def game_prompt(self, prompt): if prompt == ": ": if self.state == 1: # Ok, then we're ready to request the port report self.warpcycle.reset() self.queue_player.put("I") self.state = 2 elif self.state == 2: self.queue_player.put("Q") self.state = 3 if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): if self.state == 3: # Ok, time to exit # exit from this... self.game.to_player = self.to_player self.observer.load(self.save) self.save = None # self.game.warpdata = self.warpdata self.queue_game.put("\b \b\r\n") if not self.defer is None: self.defer.callback(self.game.gamedata.warps) self.defer = None def game_line(self, line): if line == "" or line == ": ": return if line == ": ENDINTERROG": return if line.startswith("Command [TL="): return # This should be the CIM Report Data -- parse it if self.warpcycle: if self.warpcycle.click(): self.queue_game.put("\b") if self.warpcycle.again(1000): self.queue_game.put(".") self.queue_game.put(self.warpcycle.cycle()) work = line.strip() parts = re.split(r"(?<=\d)\s", work) parts = [int(x) for x in parts] sector = parts.pop(0) # tuples are nicer on memory, and the warpdata map isn't going to be changing. # self.warpdata[sector] = tuple(parts) self.game.gamedata.warp_to(sector, *parts) def __del__(self): log.debug("CIMWarpReport {0} RIP".format(self)) def whenDone(self): self.defer = defer.Deferred() # Call this to chain something after we exit. return self.defer def player(self, chunk): """ Data from player (in bytes). """ chunk = chunk.decode("latin-1", "ignore") key = chunk.upper() log.warn("CIMWarpReport.player({0}) : I AM stopping...".format(key)) # Stop the keepalive if we are activating something else # or leaving... # self.keepalive.stop() self.queue_game.put("\b \b\r\n") if not self.defer is None: # We have something, so: self.game.to_player = self.to_player self.observer.load(self.save) self.save = None self.defer.errback(Exception("User Abort")) self.defer = None else: # Still "exit" out. self.game.to_player = self.to_player self.observer.load(self.save) # the CIMPortReport will still be needed. # We can't get fresh report data (that changes) any other way. class CIMPortReport(object): """ Parse data from CIM Port Report Example: from flexible import CIMPortReport report = CIMPortReport(self.game) d = report.whenDone() d.addCallback(self.port_report) d.addErrback(self.welcome_back) def port_report(self, portdata): self.portdata = portdata self.queue_game.put("Loaded {0} records.".format(len(portdata)) + self.nl) self.welcome_back() def welcome_back(self,*_): ... restore keep alive timers, etc. """ def __init__(self, game): self.game = game self.queue_game = game.queue_game self.queue_player = game.queue_player self.observer = game.observer # Yes, at this point we would activate self.prompt = game.buffer self.save = self.observer.save() self.days = ("Mon", "Tues", "Wed", "Thurs", "Fri", "Sat", "Sun") # I actually don't want the player input, but I'll grab it anyway. self.observer.connect("player", self.player) self.observer.connect("prompt", self.game_prompt) self.observer.connect("game-line", self.game_line) # If we want it, it's here. self.defer = None self.to_player = self.game.to_player log.debug("to_player (stored) {0}".format(self.to_player)) # Hide what's happening from the player self.game.to_player = False self.queue_player.put("^") # Activate CIM self.state = 1 # self.portdata = {} tm = localtime() log.debug("Today is {0}".format(self.days[tm[6]])) self.queue_game.put("Loading ") # cycle eats last char. self.portcycle = SpinningCursor() def game_prompt(self, prompt): if prompt == ": ": if self.state == 1: # Ok, then we're ready to request the port report self.portcycle.reset() self.queue_player.put("R") self.state = 2 elif self.state == 2: self.queue_player.put("Q") self.state = 3 if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): if self.state == 3: # Ok, time to exit # exit from this... self.game.to_player = self.to_player self.observer.load(self.save) self.save = None # self.game.portdata = self.portdata self.queue_game.put("\b \b\r\n") if not self.defer is None: self.defer.callback(self.game.gamedata.ports) self.defer = None def game_line(self, line: str): if line == "" or line == ": ": return if line == ": ENDINTERROG": return # This should be the CIM Report Data -- parse it if self.portcycle: if self.portcycle.click(): self.queue_game.put("\b") if self.portcycle.again(1000): self.queue_game.put(".") self.queue_game.put(self.portcycle.cycle()) work = line.replace("%", "") parts = re.split(r"(?<=\d)\s", work) if len(parts) == 8: port = int(parts[0].strip()) data = dict() def portBS(info): if info[0] == "-": bs = "B" else: bs = "S" return (bs, int(info[1:].strip())) data["fuel"] = dict() data["fuel"]["sale"], data["fuel"]["units"] = portBS(parts[1]) data["fuel"]["pct"] = int(parts[2].strip()) data["org"] = dict() data["org"]["sale"], data["org"]["units"] = portBS(parts[3]) data["org"]["pct"] = int(parts[4].strip()) data["equ"] = dict() data["equ"]["sale"], data["equ"]["units"] = portBS(parts[5]) data["equ"]["pct"] = int(parts[6].strip()) # Store what this port is buying/selling data["port"] = ( data["fuel"]["sale"] + data["org"]["sale"] + data["equ"]["sale"] ) # Convert BBS/SBB to Class number 1-8 data["class"] = CLASSES_PORT[data["port"]] self.game.gamedata.set_port(port, data) # self.portdata[port] = data else: log.error("CIMPortReport: {0} ???".format(line)) def __del__(self): log.debug("CIMPortReport {0} RIP".format(self)) def whenDone(self): self.defer = defer.Deferred() # Call this to chain something after we exit. return self.defer def player(self, chunk): """ Data from player (in bytes). """ chunk = chunk.decode("latin-1", "ignore") key = chunk.upper() log.warn("CIMPortReport.player({0}) : I AM stopping...".format(key)) # Stop the keepalive if we are activating something else # or leaving... # self.keepalive.stop() self.queue_game.put("\b \b\r\n") if not self.defer is None: # We have something, so: self.game.to_player = self.to_player self.observer.load(self.save) self.save = None self.defer.errback(Exception("User Abort")) self.defer = None else: # Still "exit" out. self.game.to_player = self.to_player self.observer.load(self.save) class ScriptPort(object): """ Performs the Port script. This is close to the original. We don't ask for the port to trade with -- because that information is available to us after "D" (display). We look at the adjacent sectors, and see if we know any ports. If the ports are burnt (< 20%), we remove them from the list. We sort the best trades first. If there's just one, we use it. Otherwise we ask them to choose. We have options Trade_UseFirst, which uses the first one, if there is more then one. Option Trade_Turns, will use this as default turns, without asking. state = 1 "D" Get current sector, store possible warps. state = 2 Done with display sector. Look for possible trades. If possible, or only one, state to 3. state = 3 Command prompt, state = 4 Prompt for port to trade with (If not Trade_UserFirst). Port found/given to trade with, state = 5, trade() trade() "PT" state = 5 "-----" in line, state = 6 state = 6 "We are buying", "\r" (sell all!), state = 7 "We are selling", state = 8 state = 7 "Haggle sell" "We are buying", "\r" (sell all!), state = 7 "We are selling", state = 8 state = 8 "Haggle buy" Done, if times_left > 0 then move and state = 10 """ def __init__(self, game): self.game = game self.queue_game = game.queue_game self.queue_player = game.queue_player self.observer = game.observer self.r = Style.RESET_ALL self.nl = "\n\r" self.this_sector = None # Starting sector self.sector1 = None # Current Sector self.sector2 = None # Next Sector Stop self.percent = self.game.gamedata.get_config("Trade_Percent", "5") self.stop = self.game.gamedata.get_config("Trade_Stop", "10") try: self.stop = int(self.stop) except ValueError: self.stop = 10 # Validate what we got from the config. # Not an int: make it 5, and update. # > 50, make it 5 and update. # < 0, make it 0 and update. update_config = False try: self.percent = int(self.percent) except ValueError: self.percent = 5 update_config = True if self.percent > 50: self.percent = 5 update_config = True if self.percent < 0: self.percent = 0 update_config = True if update_config: self.game.gamedata.set_config("Trade_Percent", self.percent) self.credits = 0 self.last_credits = None self.times_left = 0 # Activate self.prompt = game.buffer self.save = self.observer.save() self.observer.connect("player", self.player) self.observer.connect("prompt", self.game_prompt) self.observer.connect("game-line", self.game_line) self.defer = None self.send2player(self.r + "Script based on: Port Pair Trading v2.00" + self.nl) self.possible_sectors = None self.state = 1 self.queue_player.put("D") # Original, send 'D' to display current sector. # We could get the sector number from the self.prompt string -- HOWEVER: # IF! We send 'D', we can also get the sectors around -- we might not even need to # prompt for sector to trade with (we could possibly figure it out ourselves). # [Command [TL=00:00:00]:[967] (?=Help)? : D] # [] # [] # [Sector : 967 in uncharted space.] # [Planets : (M) Into the Darkness] # [Warps to Sector(s) : 397 - (562) - (639)] # [] def whenDone(self): self.defer = defer.Deferred() # Call this to chain something after we exit. return self.defer def deactivate(self, andExit=True): self.state = 0 log.debug("ScriptPort.deactivate ({0})".format(self.times_left)) self.queue_game.put(self.nl + Boxes.alert("Trading Script deactivating...")) assert not self.save is None self.observer.load(self.save) self.save = None if self.defer: if andExit: self.defer.callback({"exit": True}) else: self.defer.callback("done") self.defer = None def player(self, chunk: bytes): # If we receive anything -- ABORT! self.deactivate() def send2game(self, txt): log.debug("ScriptPort.send2game({0})".format(txt)) self.queue_player.put(txt) def send2player(self, txt): log.debug("ScriptPort.send2player({0})".format(txt)) self.queue_game.put(txt) def game_prompt(self, prompt: str): log.debug("{0} : {1}".format(self.state, prompt)) if self.state == 3: # log.("game_prompt: ", prompt) if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): self.state = 4 log.debug("Ok, state 4") use_first = ( self.game.gamedata.get_config("Trade_UseFirst", "N").upper()[0] == "Y" ) if self.sector2 is None and use_first: # Use the first one by default self.sector2 = self.possible[0] log.info("default to {0}".format(self.sector2)) if self.sector2 is None: # Ok, we need to prompt for this. self.queue_game.put( self.r + self.nl + "Which sector to trade with? {0}".format( GameData.port_show_part( self.this_sector, self.game.gamedata.ports[self.this_sector], ) ) + self.nl ) for i, p in enumerate(self.possible): self.queue_game.put( " " + Fore.CYAN + str(i + 1) + " : " + GameData.port_show_part(p, self.game.gamedata.ports[p]) + self.nl ) pi = PlayerInput(self.game) def got_need1(*_): log.debug("Ok, I have: {0}".format(pi.keep)) if pi.keep["count"].strip() == "": self.deactivate() return self.times_left = int(pi.keep["count"]) if pi.keep["choice"].strip() == "": self.deactivate() return c = int(pi.keep["choice"]) - 1 if c < 0 or c >= len(self.possible): self.deactivate() return self.sector2 = self.possible[int(pi.keep["choice"]) - 1] # self.queue_game.put(pformat(pi.keep).replace("\n", "\n\r")) self.state = 5 self.trade() d = pi.prompt("Choose -=>", 5, name="choice", digits=True) d.addCallback( lambda ignore: pi.prompt( "Times to execute script:", 5, name="count", digits=True ) ) d.addCallback(got_need1) else: # We already have our target port, so... self.queue_game.put( self.r + self.nl + "Trading from {0} ({1}) to default {2} ({3}).".format( self.this_sector, self.game.gamedata.ports[self.this_sector]["port"], self.sector2, self.game.gamedata.ports[self.sector2]["port"], ) + self.nl ) self.queue_game.put( self.r + self.nl + "Trading {0}".format( self.game.gamedata.port_trade_show( self.this_sector, self.sector2, 0 ) ) ) pi = PlayerInput(self.game) def got_need2(*_): if pi.keep["count"].strip() == "": self.deactivate() return self.times_left = int(pi.keep["count"]) log.debug("Ok, I have: {0}".format(pi.keep)) # self.queue_game.put(pformat(pi.keep).replace("\n", "\n\r")) self.state = 5 self.trade() self.queue_game.put(self.r + self.nl) default_turns = self.game.gamedata.get_config("Trade_Turns", "0") if default_turns == "0": # No default given, ask. d = pi.prompt("Times to execute script", 5, name="count") d.addCallback(got_need2) else: try: self.times_left = int(default_turns) except ValueError: self.times_left = 30 self.state = 5 self.trade() elif self.state == 6: if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): if self.end_trans: self.deactivate() return if self.fixable: # self.queue_game.put("Ok! Let's fix this by going to the other sector..." + self.nl) self.queue_game.put( self.nl + Boxes.alert( "Ok, FINE. We'll trade with the other port.", base="green", style=0, ) ) log.debug("Fixing...") # Swap this and other sector self.this_sector, self.other_sector = ( self.other_sector, self.this_sector, ) self.queue_player.put("{0}\r".format(self.sector2)) self.state = 5 self.trade() elif self.state == 7: if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # Done if self.end_trans: self.deactivate() return # Swap this and other sector self.this_sector, self.other_sector = ( self.other_sector, self.this_sector, ) if self.this_sector == self.sector2: self.times_left -= 1 if self.times_left <= 0: # Ok, exit out self.deactivate() return if self.last_credits is None: self.last_credits = self.credits else: if self.credits <= self.last_credits: log.warn("We don't seem to be making any money here...") self.queue_game.put( self.r + self.nl + "We don't seem to be making any money here. I'm stopping!" + self.nl ) self.deactivate() return self.queue_player.put("{0}\r".format(self.this_sector)) self.state = 10 elif re.match(r"Your offer \[\d+\] \?", prompt): log.info("Your offer? [{0}]".format(self.fix_offer)) if self.fix_offer: # Make real offer / WHAT?@?! work = prompt.replace(",", "") parts = re.split(r"\s+", work) amount = parts[2] # Ok, we have the amount, now to figure pct... if self.sell_percent > 100: self.sell_percent -= 1 else: self.sell_percent += 1 price = amount * self.sell_percent // 100 log.debug( "start: {0} % {1} price {2}".format( amount, self.sell_percent, price ) ) if self.sell_percent > 100: self.sell_percent -= 1 else: self.sell_percent += 1 self.queue_player.put("{0}\r".format(price)) # elif re.match(r"How many holds of .+ do you want to sell \[\d+\]\?", prompt): # log.info("Sell everything we can...") # this seems to screw up the sync of everything. # self.queue_player.put("\r") elif self.state == 8: # What are we trading # How many holds of Equipment do you want to buy [75]? if re.match(r"How many holds of .+ do you want to buy \[\d+\]\?", prompt): parts = prompt.split() trade_type = parts[4] log.info("Buy {0} [{1} ~ {2}]".format(trade_type, self.tpc, self.opc)) if trade_type == "Fuel": if (self.tpc in (5, 7)) and (self.opc in (2, 3, 4, 8)): # Can buy equipment - fuel ore is worthless. self.queue_player.put("0\r") return if (self.tpc in (4, 7)) and (self.opc in (1, 3, 5, 8)): # Can buy organics - fuel ore is worthless. self.queue_player.put("0\r") return if (self.tpc in (4, 7, 3, 5)) and (self.opc in (3, 4, 5, 7)): # No point in buying fuel ore if it can't be sold. self.queue_player.put("0\r") return elif trade_type == "Organics": if (self.tpc in (6, 7)) and (self.opc in (2, 3, 4, 8)): # Can buy equipment - organics is worthless. self.queue_player.put("0\r") return if (self.tpc in (2, 4, 6, 7)) and (self.opc in (2, 4, 6, 7)): # No point in buying organics if it can't be sold. self.queue_player.put("0\r") return elif trade_type == "Equipment": if self.opc in (1, 5, 6, 7): # No point in buying equipment if it can't be sold. self.queue_player.put("0\r") return self.queue_player.put("\r") elif re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # Done if self.end_trans: self.deactivate() return # Swap this and other sector self.this_sector, self.other_sector = ( self.other_sector, self.this_sector, ) if self.this_sector == self.sector2: self.times_left -= 1 if self.times_left <= 0: # Ok, exit out self.deactivate() return if self.last_credits is None: self.last_credits = self.credits else: if self.credits <= self.last_credits: log.warn("We don't seem to be making any money here...") self.queue_game.put( self.r + self.nl + "We don't seem to be making any money here. I'm stopping!" + self.nl ) self.deactivate() return self.queue_player.put("{0}\r".format(self.this_sector)) self.state = 10 elif self.state == 10: # DANGER! You have marked sector X to be avoided! if prompt.startswith("Do you really want to warp there? (Y/N) "): self.queue_player.put("Y") elif self.state == 99: # This is a good place to deactivate at. if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): if hasattr(self, "message"): if self.message is not None: self.queue_game.put(self.message) self.message = None self.deactivate() def trade(self, *_): # state 5 log.debug("trade!") self.queue_player.put("pt") # Port Trade self.end_trans = False self.fixable = False self.this_port = self.game.gamedata.ports[self.this_sector] # I think other_sector will alway be correct, but leaving this # for now. FUTURE: TODO: REMOVE if self.this_sector == self.sector1: self.other_sector = self.sector2 else: self.other_sector = self.sector1 self.other_port = self.game.gamedata.ports[self.other_sector] # Ok, perform some calculations self.tpc = self.this_port["class"] self.opc = self.other_port["class"] self.fixable = 0 self.fix_offer = 0 # [ Items Status Trading % of max OnBoard] # [ ----- ------ ------- -------- -------] # [Fuel Ore Selling 2573 93% 0] # [Organics Buying 2960 100% 0] # [Equipment Buying 1958 86% 0] # [] # [] # [You have 1,000 credits and 20 empty cargo holds.] # [] # [We are selling up to 2573. You have 0 in your holds.] # [How many holds of Fuel Ore do you want to buy [20]? 0] def game_line(self, line: str): if line.startswith("You have ") and "credits and" in line: parts = line.replace(",", "").split() credits = int(parts[2]) log.debug("Credits: {0}".format(credits)) self.credits = credits if self.state == 1: # First exploration if line.startswith("Sector :"): # We have starting sector information parts = re.split(r"\s+", line) self.this_sector = int(parts[2]) # These will be the ones swapped around as we trade back and forth. self.sector1 = self.this_sector elif line.startswith("Warps to Sector(s) : "): # Warps to Sector(s) : 397 - (562) - (639) _, _, warps = line.partition(":") warps = warps.replace("-", "").replace("(", "").replace(")", "").strip() log.debug("Warps: [{0}]".format(warps)) self.warps = [int(x) for x in re.split(r"\s+", warps)] log.debug("Warps: [{0}]".format(self.warps)) self.state = 2 elif self.state == 2: if line == "": # Ok, we're done self.state = 3 # Check to see if we have information on any possible ports # if hasattr(self.game, 'portdata'): if True: if not self.this_sector in self.game.gamedata.ports: self.state = 0 log.debug( "Current sector {0} not in portdata.".format( self.this_sector ) ) self.queue_game.put( self.r + self.nl + "I can't find the current sector in the portdata." + self.nl ) self.deactivate() return else: # Ok, we are in the portdata pd = self.game.gamedata.ports[self.this_sector] if GameData.port_burnt(pd): log.debug( "Current sector {0} port is burnt (<= 20%).".format( self.this_sector ) ) self.queue_game.put( self.r + self.nl + "Current sector port is burnt out. <= 20%." + self.nl ) self.deactivate() return possible = [x for x in self.warps if x in self.game.gamedata.ports] log.debug("Possible: {0}".format(possible)) # BUG: Sometimes links to another sector, don't link back! # This causes the game to plot a course / autopilot. # if hasattr(self.game, 'warpdata'): if True: # Great! verify that those warps link back to us! possible = [ x for x in possible if x in self.game.gamedata.warps and self.this_sector in self.game.gamedata.warps[x] ] if len(possible) == 0: self.state = 0 self.queue_game.put( self.r + self.nl + "I don't see any ports in [{0}].".format(self.warps) + self.nl ) self.deactivate() return possible = [ x for x in possible if not GameData.port_burnt(self.game.gamedata.ports[x]) ] log.debug("Possible: {0}".format(possible)) if len(possible) == 0: self.state = 0 self.queue_game.put( self.r + self.nl + "I don't see any unburnt ports in [{0}].".format( self.warps ) + self.nl ) self.deactivate() return possible = [ x for x in possible if GameData.port_trading( self.game.gamedata.ports[self.this_sector]["port"], self.game.gamedata.ports[x]["port"], ) ] # sort by best, then by % start_port = self.game.gamedata.ports[self.this_sector] if "port" in start_port: start_port_port = start_port["port"] start_with = start_port_port[1:] if start_with in ("BS", "SB"): # Ok, good trades may be possible best = GameData.flip(start_with) log.info("Sorting the best ({0}) to the top.".format(best)) log.info("{0}".format(possible)) dec = [ [ self.game.gamedata.ports[p].get("port", "---")[1:] == best, p, ] for p in possible ] dec = sorted(dec, reverse=True) possible = [x[1] for x in dec] log.info("{0}".format(possible)) self.possible = possible if len(possible) == 0: self.state = 0 self.queue_game.put( self.r + self.nl + "I don't see any possible port trades in [{0}].".format( self.warps ) + self.nl ) self.deactivate() return elif len(possible) == 1: # Ok! there's only one! self.sector2 = possible[0] # Display possible ports: # spos = [ str(x) for x in possible] # self.queue_game.put(self.r + self.nl + self.nl.join(spos) + self.nl) # At state 3, we only get a prompt. return else: self.state = 0 log.warn("We don't have any portdata!") self.queue_game.put( self.r + self.nl + "I have no portdata. Please run CIM Port Report." + self.nl ) self.deactivate() return elif self.state == 5: if "-----" in line: self.state = 6 elif self.state == 6: if "We are buying up to" in line: log.info("buying up to -- so sell all") # Sell self.state = 7 self.queue_player.put("\r") self.sell_percent = 100 + self.percent if "We are selling up to" in line: log.info("selling up to -- state 8 / set percent") # Buy self.state = 8 self.sell_percent = 100 - self.percent if ( line.startswith("Fuel Ore") or line.startswith("Organics") or line.startswith("Equipment") ): work = line.replace("Fuel Ore", "Fuel").replace("%", "") parts = re.split(r"\s+", work) # log.debug(parts) # Equipment, Selling xxx x% xxx if parts[-1] != "0" and parts[2] != "0" and parts[1] != "Buying": log.warn( "We have a problem -- they aren't buying what we have in stock!" ) stuff = line[0] # F O or E. if self.game.gamedata.port_buying(self.other_sector, stuff): log.info("fixable") self.fixable = True if int(parts[3]) < self.stop: log.info("Port is burnt! % < {0} (end_trans)".format(self.stop)) self.end_trans = True if "You don't have anything they want" in line: log.warn("Don't have anything they want.") # Neither! DRAT! if not self.fixable: self.state = 99 return if "We're not interested." in line: log.warn("Try, try again. :(") self.state = 5 self.trade() elif self.state == 7: # Haggle Sell if "We'll buy them for" in line or "Our final offer" in line: if "Our final offer" in line: self.sell_percent -= 1 parts = line.replace(",", "").split() start_price = int(parts[4]) price = start_price * self.sell_percent // 100 log.debug( "start: {0} % {1} price {2}".format( start_price, self.sell_percent, price ) ) self.sell_percent -= 1 self.queue_player.put("{0}\r".format(price)) if "We are selling up to" in line: log.info("selling up to / state 8 / set percent") # Buy self.state = 8 self.sell_percent = 100 - self.percent if "We are buying up to" in line: log.info("buying up to -- so sell all") # Sell self.state = 7 self.queue_player.put("\r") self.sell_percent = 100 + self.percent if "We're not interested." in line: log.info("Not interested. Try, try again. :(") self.state = 5 self.trade() if "WHAT?!@!? you must be crazy!" in line: log.warn("fix offer") self.fix_offer = 1 if "So, you think I'm as stupid as you look?" in line: log.warn("fix offer") self.fix_offer = 1 if "Quit playing around, you're wasting my time!" in line: log.warn("fix offer") self.fix_offer = 1 elif self.state == 8: # Haggle Buy if "We'll sell them for" in line or "Our final offer" in line: if "Our final offer" in line: self.sell_percent += 1 parts = line.replace(",", "").split() start_price = int(parts[4]) price = start_price * self.sell_percent // 100 log.debug( "start: {0} % {1} price {2}".format( start_price, self.sell_percent, price ) ) self.sell_percent += 1 self.queue_player.put("{0}\r".format(price)) if "We're not interested." in line: log.info("Not interested. Try, try again. :(") self.state = 5 self.trade() elif self.state == 10: if "Sector : " in line: # Trade self.state = 5 reactor.callLater(0, self.trade, 0) # self.trade() # elif self.state == 3: # log.debug("At state 3 [{0}]".format(line)) # self.queue_game.put("At state 3.") # self.deactivate() # return class ScriptExplore(object): """ Exploration Script WARNINGS: We assume the player has lots o turns, or unlimited turns! """ def __init__(self, game): self.game = game self.queue_game = game.queue_game self.queue_player = game.queue_player self.observer = game.observer self.r = Style.RESET_ALL self.c = merge(Style.BRIGHT + Fore.YELLOW) self.nl = "\n\r" # Our Stuff, Not our pants! self.dense = [] # We did a density, store that info. self.clear = [] # Warps that we know are clear. self.highsector = 0 # Selected Sector to move to next! self.highwarp = 0 # Selected Sector's Warp Count! self.stacksector = ( [] ) # Set of sectors that we have not picked but are unexplored... even though we did a holo! self.oneMoveSector = False self.times = 0 self.maxtimes = 0 # Activate self.prompt = game.buffer self.save = self.observer.save() self.observer.connect("player", self.player) self.observer.connect("prompt", self.game_prompt) self.observer.connect("game-line", self.game_line) self.prefer_ports = ( self.game.gamedata.get_config("Explorer_PrefPorts", "N").upper()[0] == "Y" ) self.defer = None self.send2player(Boxes.alert("Explorer", base="green")) # How many times we going to go today? ask = PlayerInput(self.game) def settimes(*_): times = ask.keep["times"].strip() log.debug("settimes got '{0}'".format(times)) if times == "": self.deactivate() else: times = int(times) self.times = times self.maxtimes = times self.send2game("D") log.debug("times: {0} maxtimes: {0}".format(self.times)) self.state = 1 d = ask.prompt( "How many sectors would you like to explorer?", 5, name="times", digits=True ) # d.addCallback(ask.output) # d.addCallback(lambda ignore: self.settimes(ask.keep)) d.addCallback(settimes) def whenDone(self): self.defer = defer.Deferred() # Call this to chain something after we exit. return self.defer def deactivate(self, andExit=False): self.state = 0 log.debug("ScriptExplore.deactivate()") assert not self.save is None self.observer.load(self.save) self.save = None if self.defer: if andExit: self.defer.callback({"exit": True}) else: self.defer.callback("done") self.defer = None def player(self, chunk: bytes): # If we receive anything -- ABORT! self.deactivate(True) def send2game(self, txt): log.debug("ScriptExplore.send2game({0})".format(txt)) self.queue_player.put(txt) def send2player(self, txt): log.debug("ScriptExplore.send2player({0})".format(txt)) self.queue_game.put(txt) def resetStuff(self): self.dense = [] self.clear = [] self.highwarp = 0 self.highsector = 0 log.debug("ScriptExplore.resetStuff()") def dead_end(self): """ We've reached a dead end. Either pop a new location to travel to, or give it up. """ self.send2player(self.nl + Boxes.alert("** DEAD END **", base="blue")) if self.stacksector: # Ok, there's somewhere to go ... self.highsector = self.stacksector.pop() # travel state self.state = 10 self.send2game("{0}\r".format(self.highsector)) else: self.send2player( self.nl + Boxes.alert( "I've run out of places to look ({0}/{1}).".format( self.maxtimes - self.times, self.maxtimes ) ) ) self.deactivate(True) def game_over(self, msg): self.send2player( self.nl + Boxes.alert( "STOP: {0} ({1}/{2}).".format( msg, self.maxtimes - self.times, self.maxtimes ) ) ) self.deactivate() def game_prompt(self, prompt: str): log.debug("{0} : {1}".format(self.state, prompt)) if self.state == 2: if "Select (H)olo Scan or (D)ensity Scan or (Q)uit" in prompt: self.send2game("D") # self.state += 1 elif self.state == 5: log.debug("dense is {0} sectors big".format(len(self.dense))) self.state += 1 self.send2game("SH") elif self.state == 10: if prompt.startswith("Do you want to engage the TransWarp drive? "): self.send2game("N") elif self.state == 12: # Looking for "Engage the Autopilot?" if prompt.startswith("Engage the Autopilot? (Y/N/Single step/Express) [Y]"): self.send2game("S") self.travel_path.pop(0) if prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N]" ): self.send2game("SD") self.state += 1 # Arriving sector :1691 Autopilot disengaging. if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): log.info("We made it to where we wanted to go!") # can't init state 1, because we're at a prompt, so... self.send2game("S") self.state = 2 return elif self.state == 15: if prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N]" ): self.send2game("N") self.travel_path.pop(0) self.state = 12 if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): log.info("We made it to where we wanted to go!") # can't init state 1, because we're at a prompt, so... self.send2game("S") self.state = 2 return elif self.state == 20: if prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N]" ): # Stop in this sector / Yes! self.send2game("Y") self.state = 1 # this should re-trigger a scan def game_line(self, line: str): log.debug("{0} | {1}".format(self.state, line)) # if "Mine Control" in line: # If we don't have a Holo-Scanner and we attempted to do a Holo-scan, abort # self.deactivate() if line.startswith("You don't have enough turns left."): self.send2player(self.nl + Boxes.alert("You're out of turns!")) self.deactivate(True) return if self.state == 1: if line.startswith("You have ") and "turns left." in line: # Ok, you're in trouble! self.send2player(self.nl + Boxes.alert("You're running low on turns!")) self.deactivate(True) return self.send2game("S") self.state += 1 elif self.state == 2: if "Relative Density Scan" in line: self.state = 3 elif "You don't have a long range scanner." in line: log.warn("FATAL: No Long Range Scanner Installed!") self.send2player(Boxes.alert("You need a Long Range Scanner!")) self.deactivate(True) return # elif "Long Range Scan" in line: # self.state += 1 elif self.state == 3: # Get the Density Data! if line.startswith("Sector"): new_sector = "(" in line work = ( line.replace(":", "") .replace(")", "") .replace("(", "") .replace("%", "") .replace("==>", "") ) work = re.split(r"\s+", work) log.debug(work) # 'Sector', '8192', '0', 'Warps', '4', 'NavHaz', '0', 'Anom', 'No' # 'Sector', '(', '8192)', '0', 'Warps', '4', 'NavHaz', '0', 'Anom', 'No' # New Sector? if new_sector: # Switch Anom into bool state # if(work[8] == 'No'): # temp = False # else: # temp = True # self.dense.append( {'sector': int(work[1]), 'density': int(work[2]), 'warps': int(work[4]), 'navhaz': int(work[6]), 'anom': temp} ) self.dense.append( { "sector": int(work[1]), "density": int(work[2]), "warps": int(work[4]), "navhaz": int(work[6]), "anom": work[8] == "Yes", } ) log.debug(self.dense) # {'sector': 8192, 'density': 0, 'warps': 4, 'navhaz': 0, 'anom': False} elif line == "": self.state += 1 # yeah, this would be better in the above line... # leaving it for now. # Which is why I broke the elif chain. ... if self.state == 4: # Begin Processing our data we got from density scan and find highest warp count in what sectors # Remove sectors with one warp log.debug("state 4: {0}".format(self.dense)) # Do we have a new place to go? (That is also worth going to) if not self.dense: # Dense contains no new sectors, abort log.info("No New Sectors Found!") self.dead_end() return # Is the sector safe to go into? # self.clear = [ x['sector'] for x in self.dense if not x['anom'] and not x['navhaz'] and x['density'] in (0,1,100,101) and x['warps'] > 1 ] self.clear = [ x for x in self.dense if not x["anom"] and not x["navhaz"] and x["density"] in (0, 1, 100, 101) ] if self.clear: # We have sector(s) we can move to! log.debug("Clear Sectors: {0}".format(len(self.clear))) # This was state 5 but why can't we reduce number of states? ( Yeah let's kick California and New York out of the US, oh wrong states :P ) # Sort to find greatest warp count if self.prefer_ports: _, self.highwarp, self.highsector = max( (x["density"], x["warps"], x["sector"]) for x in self.clear ) else: self.highwarp, self.highsector = max( (x["warps"], x["sector"]) for x in self.clear ) log.info( "Sector: {0:5d} Warps: {1}".format(self.highsector, self.highwarp) ) self.state += 1 else: log.warn("No (safe) sectors to move to!") # Let's try this?! # self.dead_end() # NO! self.game_over("No SAFE moves.") # Another NOP state. This also could be merged into above. # break the elif chain. if self.state == 5: # Add the dense scan of unknown sectors onto the stack of sectors, only save the ones we think are clear... for now. for c in self.clear: sector = c["sector"] if sector != self.highsector: if sector not in self.stacksector: self.stacksector.append(sector) # Or simply not add it in the first place ... # Remove the sector we are just about to go to, we use discard so if the sector does not exist we don't throw a error! # self.stacksector.discard(self.highsector) # Ok, we need to decide to stop exploring -- before we # issue the sector move! :P # # Warning! Yes we can and will eat all the turns! :P if self.times == 0: self.send2player( Boxes.alert("Completed {0}".format(self.maxtimes), base="green") ) log.info("Completed {0}".format(self.maxtimes)) self.deactivate() return self.times -= 1 # Ok we know the sector we want to go to now let's move it! self.send2game("m{0}\r".format(self.highsector)) if self.highsector in self.stacksector: log.info("Removing {0} from stacksector list.".format(self.highsector)) self.stacksector.remove(self.highsector) # Reset Variables for fresh data self.resetStuff() self.state = 1 elif self.state == 10: if line.startswith("You are already in that sector!"): log.info("Already here. (Whoops!)") self.state = 1 return if line.startswith("Sector : {0}".format(self.highsector)): log.info("We're here!") # Ok, we're already there! no autopilot needed! self.state = 1 return # Warping self.go_on = True if line.startswith("The shortest path ("): # Ok, we've got a path. self.state += 1 self.travel_path = [] elif self.state == 11: if line == "": # The end of the (possibly) multiline warp. self.state += 1 self.travel_path.pop(0) # First sector is one we're in. self.stophere = False self.go_on = True else: self.travel_path.extend( line.replace("(", "").replace(")", "").split(" > ") ) log.debug("Travel path: {0}".format(self.travel_path)) elif self.state == 12: # Arriving sector :1691 Autopilot disengaging. if "Autopilot disengaging." in line: log.info("We made it to where we wanted to go!") self.state = 1 return elif self.state == 13: if "Relative Density Scan" in line: self.state += 1 elif self.state == 14: if line == "": log.debug("PATH: {0}".format(self.travel_path)) # end of the scan, decision time if self.stophere: log.info("STOPHERE") # Ok, let's stop here! # Re-save the sector we were trying to get to. (we didn't make it there) if self.highsector not in self.stacksector: self.stacksector.append(self.highsector) self.state = 20 else: if self.go_on: log.info("GO ON") # Ok, carry on! self.state = 15 else: log.warn("Our way is blocked...") if self.highsector not in self.stacksector: self.stacksector.append(self.highsector) self.state = 20 else: if line.strip("-") != "": work = ( line.replace(" :", "") .replace("%", "") .replace(")", "") .replace("==>", "") ) # Does this contain something new? unseen? stophere = "(" in work work = work.replace("(", "") # Sector XXXX DENS Warps N NavHaz P Anom YN parts = re.split(r"\s+", work) # Don't bother stopping if there's only one warp # YES! Stop, even if there is just one warp! # if stophere and parts[4] == '1': # stophere = False if stophere: self.stophere = True next_stop = self.travel_path[0] log.debug( "next_stop {0} from {1}".format(next_stop, self.travel_path) ) log.debug("parts: {0}".format(parts)) if parts[1] == next_stop: log.info("next_stop {0} found...".format(next_stop)) # Ok, this is our next stop. Is it safe to travel to? if parts[2] not in ("100", "0", "1", "101"): # Ok, it's not safe to go on. self.go_on = False # Check the rest navhav and anom ... class ScriptSpace(object): """ Space Exploration script. Send "SD", verify paths are clear. Find nearest unknown. Sector + CR. Save "shortest path from to" information. At 'Engage the Autopilot', Send "S" (Single Step) At '[Stop in this sector', Send "SD", verify path is clear. Send "SH" (glean sector/port information along the way.) Send "N" (Next)! Send "SD" / Verify clear. Send "SH" Repeat for Next closest. """ def __init__(self, game): self.game = game self.queue_game = game.queue_game self.queue_player = game.queue_player self.observer = game.observer self.r = Style.RESET_ALL self.nl = "\n\r" self.this_sector = None # Starting sector self.target_sector = None # Sector going to self.path = [] self.times_left = 0 # How many times to look for target self.density = dict() # Results of density scan. (Just the numbers) # Activate self.prompt = game.buffer self.save = self.observer.save() self.observer.connect("player", self.player) self.observer.connect("prompt", self.game_prompt) self.observer.connect("game-line", self.game_line) self.defer = None self.queue_game.put(self.nl + "Bugz (like space), is big." + self.r + self.nl) self.state = 1 self.queue_player.put("SD") # Get current density scan + also get the current sector. # [Command [TL=00:00:00]:[XXXX] (?=Help)? : D] def whenDone(self): self.defer = defer.Deferred() # Call this to chain something after we exit. return self.defer def deactivate(self, andExit=False): self.state = 0 log.debug("ScriptPort.deactivate ({0})".format(self.times_left)) assert not self.save is None self.observer.load(self.save) self.save = None if self.defer: if andExit: self.defer.callback({"exit": True}) else: self.defer.callback("done") self.defer = None def player(self, chunk: bytes): # If we receive anything -- ABORT! self.deactivate(True) def unknown_search(self, starting_sector): seen = set() possible = set() possible.add(int(starting_sector)) done = False while not done: next_possible = set() for s in possible: p = self.game.gamedata.get_warps(s) if p is not None: for pos in p: if pos not in seen: next_possible.add(pos) else: log.debug("unknown found: {0}".format(s)) self.unknown = s done = True break seen.add(s) if self.unknown is None: log.debug("possible: {0}".format(next_possible)) possible = next_possible yield def find_unknown(self, starting_sector): log.debug("find_unknown( {0})".format(starting_sector)) d = defer.Deferred() # Process things self.unknown = None c = coiterate(self.unknown_search(starting_sector)) c.addCallback(lambda unknown: d.callback(self.unknown)) return d def show_unknown(self, sector): if sector is None: self.deactivate() return log.debug("Travel to {0}...".format(sector)) self.queue_player.put("{0}\r".format(sector)) def game_prompt(self, prompt: str): log.debug("{0} : {1}".format(self.state, prompt)) if self.state == 3: if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # this_sector code isn't working -- so! Get sector from prompt self.state = 4 _, _, sector = prompt.partition("]:[") sector, _, _ = sector.partition("]") self.this_sector = int(sector) # Ok, we're done with Density Scan, and we're back at the command prompt log.debug("Go find the nearest unknown...") d = self.find_unknown(sector) d.addCallback(self.show_unknown) elif self.state == 6: # Engage the autopilot? if prompt.startswith("Engage the Autopilot? (Y/N/Single step/Express) [Y]"): self.state = 7 sector = self.path.pop(0) if sector in self.density: if self.density[sector] in (0, 100): # Ok, looks safe! self.queue_player.put("S") self.this_sector = sector else: log.warn( "FATAL: Density for {0} is {1}".format( sector, self.density[sector] ) ) self.deactivate(True) return else: log.error( "{0} not in density scan? (how's that possible?)".format(sector) ) self.deactivate(True) return elif self.state == 7: # Ok, we're in a new sector (single stepping through space) # update density scan if prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ?" ): self.queue_player.put("SD") self.state = 8 elif self.state == 10: # Because we're here if prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ?" ): self.queue_player.put("SH") self.state = 11 elif self.state == 11: if prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ?" ): # Ok, is the density scan clear? sector = self.path.pop(0) if sector in self.density: if self.density[sector] in (0, 100): # Ok, looks safe self.queue_player.put("N") self.state = 7 self.this_sector = sector else: log.warn( "FATAL: Density for {0} is {1}".format( sector, self.density[sector] ) ) self.deactivate() return else: log.error( "{0} not in density scane? (how's that possible...)".format( sector ) ) self.deactivate() return def next_unknown(self, sector): log.info("Unknown is : {0}".format(sector)) self.deactivate() def game_line(self, line: str): log.debug("line {0} : {1}".format(self.state, line)) if line.startswith("Sector : "): work = line.strip() parts = re.split(r"\s+", work) self.this_sector = int(parts[2]) log.debug("game_line sector {0}".format(self.this_sector)) elif line.startswith("Command [TL=]"): # Ok, get the current sector from this _, _, sector = line.partition("]:[") sector, _, _ = sector.partition("]") self.this_sector = int(sector) log.debug("current sector: {0}".format(self.this_sector)) elif line.startswith("Warps to Sector(s) :"): # Warps to Sector(s) : 5468 _, _, work = line.partition(":") work = work.strip().replace("(", "").replace(")", "").replace(" - ", " ") parts = [int(x) for x in work.split(" ")] self.path = list(parts) if self.state in (1, 8): if "Relative Density Scan" in line: # Start Density Scan self.state += 1 self.density = {} elif self.state in (2, 9): if line == "": # End of Density Scan self.state += 1 log.debug("Density: {0}".format(self.density)) # self.deactivate() elif line.startswith("Sector"): # Parse Density Scan values work = ( line.replace("(", "") .replace(")", "") .replace(":", "") .replace("%", "") .replace(",", "") ) parts = re.split(r"\s+", work) log.debug("Sector {0}".format(parts)) sector = int(parts[1]) self.density[sector] = int(parts[3]) if parts[7] != "0": log.warn("NavHaz {0} : {1}".format(parts[7], work)) # navhaz already alters density # self.density[sector] += 99 if parts[9] != "No": log.warn("Anom {0} : {1}".format(parts[9], work)) # anom already alters density self.density[sector] += 990 elif self.state == 4: # Looking for shortest path message / warp info # Or possibly, "We're here!" if line.startswith("Sector :") and str(self.unknown) in line: # Ok, I'd guess that we're already there! # Try it again! self.queue_player.put("SD") self.state = 1 if line.startswith("The shortest path"): self.state = 5 elif self.state == 5: # This is the warps line # Can this be multiple lines? if line == "": self.state = 6 else: work = line.replace("(", "").replace(")", "").replace(">", "").strip() self.path = [int(x) for x in work.split()] log.debug("Path: {0}".format(self.path)) # Verify current = self.path.pop(0) if current != self.this_sector: log.warn("Failed: {0} != {1}".format(current, self.this_sector)) self.deactivate() return elif self.state == 7: if self.unknown == self.this_sector: # We have arrived! log.info("We're here!") self.deactivate() class ScriptTerror(object): """ Terror script. This uses the Port Trading script. Basically, we look for the next best port trading pair. Move to it, fire off the Port Trading script. Repeat until our loop is done, or there's no more pairs. Terror_Use: 'F' First available/Trade report ordering. 'C' Closest trade pair state=1 entered sector number to move to. state=2 (route "The shortest path") Fire ScriptPort. Callback journey_on. """ def __init__(self, game, proxy, count): self.game = game self.queue_game = game.queue_game self.queue_player = game.queue_player self.proxy = proxy self.count = count self.observer = game.observer self.r = Style.RESET_ALL self.nl = "\n\r" self.target_sector = None # Activate self.prompt = game.buffer self.save = self.observer.save() self.observer.connect("player", self.player) self.observer.connect("prompt", self.game_prompt) self.observer.connect("game-line", self.game_line) self.state = 0 self.defer = None # Verify that they have configured auto-port trading. # Otherwise, this doesn't work! usefirst = self.game.gamedata.get_config("Trade_UseFirst") if usefirst is None: usefirst = "?" if usefirst.upper()[0] != "Y": self.queue_game.put( Boxes.alert("Sorry! You need to config Trade_UseFirst=Y", base="red") ) self.deactivate() return turns = self.game.gamedata.get_config("Trade_Turns") if turns is None: turns = "0" try: t = int(turns) except ValueError: self.queue_game.put( Boxes.alert("Sorry! You need to config Trade_Turns", base="red") ) self.deactivate() return if t < 5: self.queue_game.put( Boxes.alert("Sorry! You need to config Trade_Turns >", base="red") ) self.deactivate() return self.usewhich = self.game.gamedata.get_config("Terror_Use", "F").upper()[0] if self.usewhich == "F": c = coiterate(self.find_next_good_trade_pair()) c.addCallback(lambda unknown: self.scary()) return if self.usewhich == "C": # Where are we? # Command [TL=00:00:00]:[10202] (?=Help)? : prompt = self.game.getPrompt() _, _, sector = prompt.partition("]:[") sector, _, _ = sector.partition("]") self.sector = int(sector) log.info("find_nearest_tradepairs({0})".format(self.sector)) c = coiterate(self.game.gamedata.find_nearest_tradepairs(self.sector, self)) c.addCallback(lambda unknown: self.scary()) return self.queue_game.put(Boxes.alert("What? What?", base="red")) self.deactivate() def scary(self): if self.target_sector is None: self.queue_game.put( Boxes.alert("Sorry! I don't see any ports to trade with.", base="red") ) self.deactivate() else: self.state = 1 self.queue_player.put("{0}\r".format(self.target_sector)) # This will have to be our best guess. :P self.sector = self.target_sector def find_next_good_trade_pair(self): """ Find the next GOOD trade pair sector. This is sequential (just like Trade report) """ # This should be using the setting in the config, not a hard coded value! # show_limit (galaxy.port_trade_show) is pct >= show_limit. show_limit = self.game.gamedata.get_config("Display_Percent", "90") try: show_limit = int(show_limit) except ValueError: show_limit = 90 if show_limit < 15: show_limit = 15 if show_limit > 90: show_limit = 90 # Look for "GOOD" trades for sector in sorted(self.game.gamedata.ports.keys()): pd = self.game.gamedata.ports[sector] if not GameData.port_burnt(pd): # This happens when you trade with a StarDock if "class" not in pd: continue pc = pd["class"] # Ok, let's look into it. if not sector in self.game.gamedata.warps: continue # Busted port if not sector in self.game.gamedata.busts: continue warps = self.game.gamedata.warps[sector] for w in warps: # We can get there, and get back. if ( w in self.game.gamedata.warps and sector in self.game.gamedata.warps[w] ): # Ok, we can get there -- and get back! if ( w > sector and w in self.game.gamedata.ports and not GameData.port_burnt(self.game.gamedata.ports[w]) ): wd = self.game.gamedata.ports[w] if "class" not in wd: continue wc = wd["class"] if pc in (1, 5) and wc in (2, 4): data = self.game.gamedata.port_trade_show( sector, w, show_limit ) if data: self.target_sector = sector return sector elif pc in (2, 4) and wc in (1, 5): data = self.game.gamedata.port_trade_show( sector, w, show_limit ) if data: self.target_sector = sector return sector yield # Look for OK trades for sector in sorted(self.game.gamedata.ports.keys()): pd = self.game.gamedata.ports[sector] if not GameData.port_burnt(pd): if "class" not in pd: continue pc = pd["class"] # Ok, let's look into it. if not sector in self.game.gamedata.warps: continue # Busted port if not sector in self.game.gamedata.busts: continue warps = self.game.gamedata.warps[sector] for w in warps: # We can get there, and get back. if ( w in self.game.gamedata.warps and sector in self.game.gamedata.warps[w] ): # Ok, we can get there -- and get back! if ( w > sector and w in self.game.gamedata.ports and not GameData.port_burnt(self.game.gamedata.ports[w]) ): wd = self.game.gamedata.ports[w] if "class" not in wd: continue wc = wd["class"] if GameData.port_trading( pd["port"], self.game.gamedata.ports[w]["port"] ): data = self.game.gamedata.port_trade_show( sector, w, show_limit ) if data: self.target_sector = sector return sector yield self.target_sector = None def whenDone(self): self.defer = defer.Deferred() # Call this to chain something after we exit. return self.defer def deactivate(self, andExit=False): self.state = 0 log.debug("ScriptTerror.deactivate") assert not self.save is None self.observer.load(self.save) self.save = None if self.defer: if andExit: self.defer.callback({"exit": True}) else: self.defer.callback("done") self.defer = None def player(self, chunk: bytes): # If we receive anything -- ABORT! self.deactivate(True) def journey_on(self, *_): log.info("journey_on( {0})".format(self.count)) if self.count > 0: self.count -= 1 if self.usewhich == "F": c = coiterate(self.find_next_good_trade_pair()) c.addCallback(lambda unknown: self.scary()) return if self.usewhich == "C": log.info("find_nearest_tradepairs({0})".format(self.sector)) c = coiterate( self.game.gamedata.find_nearest_tradepairs(self.sector, self) ) c.addCallback(lambda unknown: self.scary()) return self.deactivate() # c = coiterate(self.find_next_good_trade_pair()) # c.addCallback(lambda unknown: self.scary()) # self.target_sector = self.proxy.find_next_good_trade_pair() # Sector going to # self.state = 1 # self.queue_player.put("{0}\r".format(self.target_sector)) else: self.deactivate() def game_prompt(self, prompt: str): log.debug("{0} : {1}".format(self.state, prompt)) if self.state == 1: if prompt.startswith("Do you want to engage the TransWarp drive? "): self.queue_player.put("N") elif self.state == 2: if prompt.startswith("Do you want to engage the TransWarp drive? "): self.queue_player.put("N") elif prompt.startswith( "Engage the Autopilot? (Y/N/Single step/Express) [Y]" ): self.queue_player.put("E") elif prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ?" ): self.queue_player.put("N") elif re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # We should be where we wanted to. ports = ScriptPort(self.game) d = ports.whenDone() d.addCallback(self.journey_on) d.addErrback(self.journey_on) def game_line(self, line: str): log.debug("line {0} : {1}".format(self.state, line)) if self.state == 1: if line.startswith("The shortest path ("): self.state = 2 elif line.startswith("Warping to Sector "): self.state = 2 elif line.startswith("You are already in that sector!"): # Whoops. ports = ScriptPort(self.game) d = ports.whenDone() d.addCallback(self.journey_on) d.addErrback(self.journey_on) class PlanetUpScript(object): """ Planet Upgrade Script state=1 Pulling TLQ (Team/Corp List Planets) Pulling CYQ (Computer, Your planets) state=2 'Personal Planet Scan' or 'Corporate Planet Scan' parse. Display list of planets, and prompt user for planet number to upgrade. state=3 Moving to planet state=4 Landing on planet, parse planet contents. select 'C' state=5 Parse requirements for next upgrade state=6 move to next needed item. (Colonists, F, O, E) If completed, 'L' and state=4 Otherwise move, fetch=ITEM, and state=7 state=7 travel to where we need something. Once there, L (land) for Colonist, otherwise PT (Port trade) state=8 Return to planet. state=9 At planet, or in route. Land. Transfer Colonists/Cargo. state=6 """ def __init__(self, game): self.game = game self.queue_game = game.queue_game self.queue_player = game.queue_player self.observer = game.observer # Yes, at this point we would activate self.prompt = game.buffer self.save = self.observer.save() self.nl = "\n\r" self.cargo_index = {"F": 0, "O": 1, "E": 2} self.index_cargo = ("F", "O", "E") # I actually don't want the player input, but I'll grab it anyway. self.observer.connect("player", self.player) self.observer.connect("prompt", self.game_prompt) self.observer.connect("game-line", self.game_line) # If we want it, it's here. self.defer = None self.to_player = self.game.to_player self.planets = {} self.citadel = False # Hide what's happening from the player self.game.to_player = False # self.queue_player.put("CYQ") # Computer -> Your Planets -> Quit self.queue_player.put("TLQ") # Team/Corp -> List Corp Planets -> Quit self.corp = True self.state = 1 # self.warpdata = {} self.queue_game.put(Boxes.alert("Let me see what I can see here...")) def display_planets(self, justLocal=True): tc = merge(Style.BRIGHT + Fore.YELLOW + Back.BLUE) c1 = merge(Style.BRIGHT + Fore.WHITE + Back.BLUE) c2 = merge(Style.BRIGHT + Fore.CYAN + Back.BLUE) box = Boxes(44, color=tc) self.queue_game.put(box.top()) # Are we just displaying local only? Then say so. if justLocal: planet_label = "LOCAL Planet Name" else: planet_label = "Planet Name" self.queue_game.put( box.row( tc + "{0:3} {1:6} {2:20} {3} ".format( " # ", "Sector", planet_label, "Citadel LVL" ) ) ) self.queue_game.put(box.middle()) def planet_output(number, sector, name, citadel): row = "{0}{1:^3} {2:6} {3}{4:29} {0}{5:2} ".format( c1, number, sector, c2, name, citadel ) self.queue_game.put(box.row(row)) for s in sorted(self.planets.keys()): if (justLocal == False) or ( self.current_sector == self.planets[s]["sector"] ): planet_output( s, self.planets[s]["sector"], self.planets[s]["name"], self.planets[s]["citadel"], ) self.queue_game.put(box.bottom()) def game_prompt(self, prompt): log.info("prompt {0} : {1}".format(self.state, prompt)) if self.state == 1 and re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # Ok, you're not on a team. :P self.queue_player.put("CYQ") if self.state == 2 and re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): if self.corp: self.corp = False self.state = 1 self.queue_player.put("CYQ") return self.game.to_player = True # For now we output the information, and exit # self.queue_game.put("{0}\r\n".format(self.planets)) # self.queue_game.put(pformat(self.planets).replace("\n", self.nl) + self.nl) if len(self.planets) == 0: # Ok, this is easy. self.queue_game.put( self.nl + Boxes.alert( "You don't have any planets? You poor, poor dear.", base="red" ) ) self.deactivate(True) return # I need this to know if I can just land, or need to move to the planet. # Get current sector from the prompt # Command [TL=00:00:00]:[10202] (?=Help)? : _, _, part = prompt.partition("]:[") sector, _, _ = part.partition("]") self.current_sector = int(sector) # A better default is to ask which planet to upgrade. self.display_planets(True) ask = PlayerInput(self.game) d = ask.prompt("Choose a planet (L to List)", 3, name="planet") d.addCallback(self.planet_chosen) elif self.state == 3: if prompt.startswith("Do you want to engage the TransWarp drive? "): self.queue_player.put("N") elif prompt.startswith( "Engage the Autopilot? (Y/N/Single step/Express) [Y]" ): self.queue_player.put("E") elif prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ?" ): self.queue_player.put("N") elif re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # We're here! self.state = 4 self.queue_player.put("L") elif self.state == 4: # If you have a planet scanner, or if there's more then one planet. if prompt.startswith("Land on which planet ?"): self.queue_player.put("{0}\r".format(self.planet_number)) if prompt.startswith("Planet command (?=help) [D] "): # self.queue_game.put(self.nl + "{0} : {1}".format( self.colonists, self.cargo) + self.nl) self.state = 5 self.queue_player.put("C") elif self.state == 5: if re.match(r"Do you wish to construct .+\?", prompt): # 'Do you wish to construct a Combat Control Computer?' # Have we met all the needs? If so, do it. ;) # If not, xfer the cargo on the ship to the planet and get moving! ready = True for i in self.index_cargo: # ['F', 'O', 'E']: if self.need[i] > self.cargo[i]: ready = False log.info("Need: {0}".format(i)) # break if self.need["C"] > self.colonists: log.info("Need: people") ready = False # self.queue_game.put(self.nl + "{0}".format(self.need)) if ready: self.queue_game.put(self.nl + Boxes.alert("Party Planet Pants On!")) self.queue_player.put("YQ") if self.citadel: # Need extra Quit to get out of citadel, then out of planet. self.queue_player.put("Q") self.deactivate(True) return if "construct one" in prompt: # No, but start moving things around to build one. self.queue_player.put("N") else: # No, and quit the Citadel menu. self.queue_player.put("NQ") # Xfer cargo, and get ready to travel... elif prompt.startswith("Citadel command (?=help)"): self.queue_player.put("U") self.citadel = True if prompt.startswith("Planet command (?=help) [D] "): # self.queue_game.put(pformat(self.ship_cargo).replace("\n", self.nl) + self.nl) # self.queue_game.put(pformat(self.cargo).replace("\n", self.nl) + self.nl) for idx, c in enumerate(self.index_cargo): # ('F', 'O', 'E')): if self.ship_cargo[c] > 0: # Transfer Cargo, (No display), Leave [1,2, or 3], \r = All of it. self.queue_player.put("TNL{0}\r".format(idx + 1)) self.cargo[c] += self.ship_cargo[c] self.ship_cargo[c] = 0 return break self.queue_player.put("Q") self.state = 6 # self.queue_game.put(pformat(self.ship_cargo).replace("\n", self.nl) + self.nl) # self.queue_game.put(pformat(self.cargo).replace("\n", self.nl) + self.nl) # self.deactivate() elif self.state == 6: if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # Ok, what do we need and where do we get it? if self.need["C"] > self.colonists: # NavPoint, T, Express self.fetch = "C" self.queue_player.put("NTE") self.state = 7 self.send_land = False else: for i in ("F", "O", "E"): if self.need[i] > self.cargo[i]: self.fetch = i # TODO: Make this a config setting. place = self.game.gamedata.find_nearest_selling( self.planet_sector, i, 400 ) if place == 0: self.queue_game.put( self.nl + Boxes.alert("Find Nearest Failed!") ) self.deactivate(True) return self.queue_player.put("{0}\r".format(place)) # self.queue_player.put("{0}\rE".format(place)) self.state = 7 return # Ok, upgrade time! self.state = 4 self.queue_player.put("L") # self.queue_game.put("No, not yet!" + self.nl) # self.deactivate() # for i in ['F', 'O', 'E']: # if self.need[i] > self.cargo[i]: # ready = False # self.queue_game.put( "Need: {0}".format(i) + self.nl) elif self.state == 7: if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): if self.fetch == "C": # Colonist # [How many groups of Colonists do you want to take ([125] empty holds) ? ] if self.send_land == False: self.send_land = True self.queue_player.put("L") # "LT\r") elif self.fetch in self.index_cargo: # ('F', 'O', 'E'): # Port, Trade self.queue_player.put("pt") elif re.match(r"How many holds of .+ do you want to sell", prompt): # This shouldn't occur... self.queue_game.put("OH NOSE!" + self.nl) self.deactivate(True) return elif prompt.startswith("Do you want to engage the TransWarp drive? "): self.queue_player.put("N") elif prompt.startswith( "Engage the Autopilot? (Y/N/Single step/Express) [Y]" ): self.queue_player.put("E") elif prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ?" ): self.queue_player.put("N") elif re.match( r"How many holds of .+ do you want to buy \[\d+\]\? ", prompt ): if prompt.startswith("How many holds of " + self.fetch): _, _, holds = prompt.partition("[") holds, _, _ = holds.partition("]") self.fetch_amount = int(holds) log.info("Buying {0} of {1}".format(holds, self.fetch)) # BTTZ. if int(holds) == 0: # FAIL-WHALE self.queue_player.put("\r") self.queue_game.put( self.nl + Boxes.alert( "FAILURE: Just bought 0 holds! Out of money!?!", base="red", ) ) self.deactivate(True) return self.queue_player.put("\r\r") self.state = 8 else: # We don't want to buy this one. Skip it! self.queue_player.put("0\r") elif prompt.startswith("Land on which planet ?"): if self.fetch == "C": self.queue_player.put("1\r") elif prompt.startswith( "Do you wish to (L)eave or (T)ake Colonists? [T] (Q to leave)" ): if self.fetch == "C": self.queue_player.put("T\r") self.state = 8 elif self.state == 8: if re.match(r"How many holds of .+ do you want to buy \[\d+\]\? ", prompt): # No, no, we're done buying! self.queue_player.put("0\r") if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # Ok, return to the planet... _, _, part = prompt.partition("]:[") sector, _, _ = part.partition("]") if sector != self.planet_sector: log.info("{0} is {1}".format(sector, self.planet_sector)) self.queue_player.put("{0}\rE".format(self.planet_sector)) else: log.info("{0} is not {1}".format(sector, self.planet_sector)) self.queue_player.put("\r") self.state = 9 elif self.state == 9: log.info("prompt9 {0} : {1}".format(self.state, prompt)) if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # land self.queue_player.put("L") elif prompt.startswith("Do you want to engage the TransWarp drive? "): self.queue_player.put("N") elif prompt.startswith( "Engage the Autopilot? (Y/N/Single step/Express) [Y]" ): self.queue_player.put("E") elif prompt.startswith("Engage Express mode? (Y/N) [N] "): self.queue_player.put("Y") elif prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ?" ): self.queue_player.put("N") elif prompt.startswith("Land on which planet ?"): self.queue_player.put("{0}\r".format(self.planet_number)) elif prompt.startswith("Planet command (?=help) [D]"): if self.fetch == "C": # Colonist / No display Planet / Leave self.queue_player.put("SNL") elif self.fetch in self.index_cargo: # ('F', 'O', 'E'): # Cargo, No display / Leave self.queue_player.put("TNL") elif prompt.startswith("(1)Ore, (2)Org or (3)Equipment ?"): self.queue_player.put( "{0}\r\rQ".format(self.cargo_index[self.fetch] + 1) ) self.cargo[self.fetch] += self.fetch_amount self.state = 6 elif prompt.startswith("(1)Ore, (2)Org or (3)Equipment Production?"): log.info("place_people: {0}".format(self.place_people)) safe_places = [ k for k in self.place_people.keys() if self.place_people[k] ] if len(safe_places) == 0: # Ok, (GREAT) Class "U" Vaporous/Gaseus >:( safe_places = ["F", "O", "E"] # TO FIX: Use self.place_people to decide. # Ok, I'd choose, but for right now. log.info("Safe {0} index {1}".format(safe_places, self.place_start)) put_people = safe_places[self.place_start] log.info("Use: {0}".format(put_people)) self.place_start += 1 if self.place_start >= len(safe_places): self.place_start = 0 self.queue_player.put( "{0}\r\rQ".format(self.cargo_index[put_people] + 1) ) self.colonists += self.fetch_amount self.state = 6 def planet_chosen(self, choice: str): if choice.strip() == "": self.deactivate() elif choice.strip().upper()[0] == "L": self.display_planets(False) ask = PlayerInput(self.game) d = ask.prompt("Choose a planet (L to List)", 3, name="planet") d.addCallback(self.planet_chosen) return else: try: self.planet_number = int(choice) except ValueError: self.deactivate() return if self.planet_number in self.planets: # Ok, this'll work self.planet_sector = self.planets[self.planet_number]["sector"] self.planet_name = self.planets[self.planet_number]["name"] if self.current_sector == self.planet_sector: # We're here. Land self.state = 4 self.queue_player.put("L") else: # Get moving! self.state = 3 self.queue_player.put("{0}\r".format(self.planet_sector)) else: self.deactivate() def game_line(self, line): log.info("line {0} : {1}".format(self.state, line)) if self.state == 1: if "Personal Planet Scan" in line or "Corporate Planet Scan" in line: self.state = 2 elif self.state == 2: # Ok, we're in the planet scan part of this # 10202 #3 Home of Bugz Class M, Earth Type No Citadel] if "#" in line: # Ok, we have a planet detail line. # There's an extra T when the planet is maxed out. if line[7] == "T": line = line[:7] + " " + line[8:] details, _, citadel = line.partition("Class") # log.info(details) # Is that what we are after? # log.info(citadel) sector, planet_number, name = re.split(r"\s+", details.strip(), 2) sector = int(sector) number = int(planet_number.replace("#", "")) if "Level" in citadel: cit = int(citadel[-1]) # Grab last item else: # Oops, there looks like there is no citadel here. cit = 0 self.last_seen = number self.planets[number] = {"sector": sector, "name": name, "citadel": cit} log.info( "Planet # {0} in {1} called {2} with a lvl {3} citadel".format( number, sector, name, cit ) ) # detail, _, _ = line.partition('Class') # detail = detail.strip() # Sector #X Name of planet # sector, number, name = re.split(r'\s+', detail, 2) # sector = int(sector) # number = int(number[1:]) # self.last_seen = number # self.planets[number] = {"sector": sector, "name": name} # log.info("Planet # {0} in {1} called {2}".format( number, sector, name)) if "---" in line: number = self.last_seen # Ok, take the last_seen number, and use it for this line # [ Sector Planet Name Ore Org Equ Ore Org Equ Fighters Citadel] # [ Shields Population -=Productions=- -=-=-=-=-On Hands-=-=-=-=- Credits] # [ 10202 #3 Home of Bugz Class M, Earth Type No Citadel] # [ --- (1M) 144 49 26 145 75 12 10 0] details = re.split(r"\s+", line.strip()) # OK: Likely, I'm not going to use these numbers AT ALL. self.planets[number]["population"] = ( details[1].replace("(", "").replace(")", "") ) # Ok, there's going to have to be some sort of modifier (K, M, etc.) # to these numbers. Beware! self.planets[number]["ore"] = details[5] self.planets[number]["org"] = details[6] self.planets[number]["equ"] = details[7] elif self.state == 4: # Combat Control Computer under construction, 4 day(s) till complete. if "under construction, " in line and "day(s) till complete" in line: # Ok, already building. self.queue_game.put(self.nl + Boxes.alert("NO, NOT YET!") + self.nl) self.queue_player.put("Q") # quit Planet menu. self.deactivate(True) return # [ Item Colonists Colonists Daily Planet Ship Planet ] # [Fuel Ore 0 1 0 0 0 1,000,000] # [Organics 0 N/A 0 0 0 10,000] # [Equipment 0 500 0 125 125 100,000] # [Fighters N/A N/A 0 0 400 1,000,000] items = ["Fuel Ore", "Organics", "Equipment"] for i in items: if line.startswith(i): cargo = line[0].upper() work = line.replace(",", "") if i == "Fuel Ore": work = work.replace(i, "Fuel") self.colonists = 0 self.cargo = {} self.need = {} self.ship_cargo = {} self.place_people = {} parts = re.split(r"\s+", work) log.info("parts: {0}".format(parts)) c = int(parts[1]) planet_has = int(parts[4]) self.colonists += c self.cargo[cargo] = planet_has self.ship_cargo[cargo] = int(parts[5]) # Boolean, can we place people here? If N/A, then don't! self.place_people[cargo] = parts[2] != "N/A" self.place_start = 0 elif self.state == 5: # [Planet command (?=help) [D] C] # [Be patient, your Citadel is not yet finished.] if line.startswith("Be patient, your Citadel is not yet finished."): # Ah HA! self.queue_game.put(self.nl + Boxes.alert("NO, NOT YET!") + self.nl) self.queue_player.put("Q") # quit Planet menu. self.deactivate(True) elif line.startswith("This Citadel cannot be upgraded further"): self.queue_game.put(self.nl + Boxes.alert("NO MORE!") + self.nl) self.queue_player.put("QQ") # quit Citadel, quit Planet menu. self.deactivate(True) else: items = ["Colonists", "Fuel Ore", "Organics", "Equipment"] work = line.replace(",", "").replace(" units of", "").strip() # 800,000 Colonists to support the construction, # 500 units of Fuel Ore, # 300 units of Organics, # 600 units of Equipment and for i in items: if i in line: count = int(work.split()[0]) k = i[0].upper() # keep colonists in same units. if k == "C": count //= 1000 self.need[k] = count elif self.state == 8: if re.match( r"How many groups of Colonists do you want to take \(\[\d+\] empty holds\) \?", line, ): # Ok, how many holds? _, _, holds = line.partition("[") holds, _, _ = holds.partition("]") self.fetch_amount = int(holds) if line.startswith("One turn deducted, "): parts = line.split() turns = int(parts[3]) if turns < 200: self.queue_game.put(self.nl + Boxes.alert("LOW TURNS.") + self.nl) self.deactivate(True) elif self.state == 9: if re.match(r"You have \d turns left.", line): parts = line.split() turns = int(parts[2]) log.debug("Turns: {0}".format(turns)) def __del__(self): log.debug("PlanetUpScript {0} RIP".format(self)) def whenDone(self): self.defer = defer.Deferred() # Call this to chain something after we exit. return self.defer def deactivate(self, andExit=False): self.state = 0 if not self.defer is None: # We have something, so: self.game.to_player = self.to_player self.observer.load(self.save) self.save = None if andExit: self.defer.callback({"exit": True}) else: self.defer.callback("done") self.defer = None else: # Still "exit" out. self.game.to_player = self.to_player self.observer.load(self.save) def player(self, chunk): """ Data from player (in bytes). """ chunk = chunk.decode("latin-1", "ignore") key = chunk.upper() log.warn( "PlanetUpScript.player({0}) : I AM stopping...(user input)".format(key) ) if not self.defer is None: # We have something, so: self.game.to_player = self.to_player self.observer.load(self.save) self.save = None self.defer.errback(Exception("User Abort")) self.defer = None else: # Still "exit" out. self.game.to_player = self.to_player self.observer.load(self.save) class ColoScript2(object): """ ColoScript 2.0 Goal: * Allow a player to move people from anywhere to anywhere. (Acheived!) States: 1 = Computer talks with us giving corp and personal planet info 2 = Grab's data and asks series of questions, TO, FROM, LOOPS 3 = Move to FROM 4 = Identify Population Categories / Is it terra? 5 = Load People, then move to planet TO 6 = Init Land on TO 7 = Decide to loop (Jump back to 3), Unload People """ def __init__(self, game): self.game = game self.queue_game = game.queue_game self.queue_player = game.queue_player self.observer = game.observer self.r = Style.RESET_ALL self.c = merge(Style.BRIGHT + Fore.YELLOW) self.nl = "\n\r" # My "stuff" in my pants! self.state = 1 self.corp = True self.planets = {} self.loops = 0 self.maxloops = 0 self.last_seen = 0 self.valid2From = True # Sector Numbers of Planets TO and FROM self.TO = 0 self.FROM = 0 # Planet Numbers of Planets TO and FROM self.to_number = 0 self.from_number = 0 # Activate self.prompt = game.buffer self.save = self.observer.save() self.observer.connect("player", self.player) self.observer.connect("prompt", self.game_prompt) self.observer.connect("game-line", self.game_line) self.defer = None self.to_player = self.game.to_player self.game.to_player = False self.send2game("TLQ") def whenDone(self): self.defer = defer.Deferred() return self.defer def deactivate(self, andExit=False): self.state = 0 log.debug("ColoScript2.deactivate()") self.game.to_player = True assert not self.save is None self.observer.load(self.save) self.save = None if self.defer: if andExit: self.defer.callback({"exit": True}) else: self.defer.callback("done") self.defer = None def player(self, chunk: bytes): self.deactivate(True) def send2game(self, txt): # Removed debug info since I use this to also display Boxes.alert self.queue_player.put(txt) def send2player(self, txt): # Removed debug since it really doesn't help with anything self.queue_game.put(txt) def to_chosen(self, choice: str): if choice.strip() == "": self.deactivate(True) else: self.to_number = int(choice) if self.to_number == 1: # Oooh Terra! self.TO = 1 self.planet_name = "Terra" ask1 = PlayerInput(self.game) d1 = ask1.prompt("From: ", 3, name="from", digits=True) d1.addCallback(self.from_chosen) elif self.to_number in self.planets: # Ok, this'll work self.TO = self.planets[self.to_number]["sector"] self.planet_name = self.planets[self.to_number]["name"] # Are we really getting this? Yup # log.debug("TO Planet Number: {0} Sector: {1}".format(self.to_number, self.TO)) ask1 = PlayerInput(self.game) d1 = ask1.prompt("From: ", 3, name="from", digits=True) d1.addCallback(self.from_chosen) else: self.deactivate(True) def from_chosen(self, choice: str): if choice.strip() == "": self.deactivate(True) else: self.from_number = int(choice) if self.from_number == self.to_number: # Prevent the user from asking to move folks to the same planet they would be getting # them from. See Recursion! self.valid2From = False if self.from_number in self.planets: # Ok, this'll work self.FROM = self.planets[self.from_number]["sector"] self.planet_name = self.planets[self.from_number]["name"] # Are we really getting this? Yup Yup # log.debug("FROM Planet Number: {0} Sector: {1}".format(self.from_number, self.FROM)) ask1 = PlayerInput(self.game) d1 = ask1.prompt("How many times ", 3, name="rolls", digits=True) d1.addCallback(self.loop_chosen) elif self.from_number == 1: # Yup handle if the user picks to pull from Terra self.FROM = 1 ask1 = PlayerInput(self.game) d1 = ask1.prompt("How many times ", 3, name="rolls", digits=True) d1.addCallback(self.loop_chosen) else: self.deactivate(True) def loop_chosen(self, choice: str): if choice.strip() == "": self.deactivate(True) else: self.loops = abs(int(choice)) if self.loops == 0: self.loops = 1 self.maxloops = self.loops # ask2 = PlayerInput(self.game) # d2 = ask2.prompt("From? ", 5, name="from", digits=True) # d2.addCallback(self.from_chosen) if self.valid2From: self.state = 3 self.game.to_player = False self.send2game("I") else: self.game.to_player = True self.send2player( self.nl + Boxes.alert( "Completed ({0}) Wow, it's like nothing happened.".format( self.maxloops ) ) ) self.deactivate(True) def game_prompt(self, prompt: str): log.debug("P {0} | {1}".format(self.state, prompt)) if self.state == 1 and re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # Ok, you're not on a team. :P self.queue_player.put("CYQ") if self.state == 2 and re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): if self.corp: self.corp = False self.state = 1 self.queue_player.put("CYQ") return self.game.to_player = True # For now we output the information, and exit # self.queue_game.put("{0}\r\n".format(self.planets)) # self.queue_game.put(pformat(self.planets).replace("\n", self.nl) + self.nl) if len(self.planets) == 0: # Ok, this is easy. self.queue_game.put( self.nl + Boxes.alert( "You don't have any planets? You poor, poor dear.", base="red" ) ) self.deactivate(True) return # I need this to know if I can just land, or need to move to the planet. # Get current sector from the prompt # Command [TL=00:00:00]:[10202] (?=Help)? : _, _, part = prompt.partition("]:[") sector, _, _ = part.partition("]") self.current_sector = int(sector) # A better default is to ask which planet to upgrade. tc = merge(Style.BRIGHT + Fore.YELLOW + Back.BLUE) c1 = merge(Style.BRIGHT + Fore.WHITE + Back.BLUE) c2 = merge(Style.BRIGHT + Fore.CYAN + Back.BLUE) box = Boxes(44, color=tc) self.queue_game.put(box.top()) self.queue_game.put( box.row( tc + "{0:3} {1:6} {2:20} {3} ".format( " # ", "Sector", "Planet Name", "Citadel LVL" ) ) ) self.queue_game.put(box.middle()) def planet_output(number, sector, name, citadel): row = "{0}{1:^3} {2:6} {3}{4:29} {0}{5:2} ".format( c1, number, sector, c2, name, citadel ) self.queue_game.put(box.row(row)) # Manually add in Terra so a player knows about it, # Since we now support Terra being TO or FROM. terra = row = "{0}{1:^3} {2:6} {3}{4:29} {0}{5:2} ".format( c1, 1, 1, c2, "Terra", " " ) self.send2player(box.row(terra)) for s in sorted(self.planets.keys()): planet_output( s, self.planets[s]["sector"], self.planets[s]["name"], self.planets[s]["citadel"], ) self.queue_game.put(box.bottom()) ask = PlayerInput(self.game) d = ask.prompt("To: ", 3, name="planet", digits=True) d.addCallback(self.to_chosen) elif self.state == 3: # Initalize moving to sector 1, Terra if prompt.startswith("Do you want to engage the TransWarp drive? "): self.queue_player.put("N") elif prompt.startswith( "Engage the Autopilot? (Y/N/Single step/Express) [Y]" ): self.queue_player.put("E") elif prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ?" ): self.queue_player.put("N") elif re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): self.state = 4 self.game.to_player = True self.send2game("{0}\r".format(self.FROM)) # Move to planet FROM elif self.state == 4: # Are we there yet? (NNY) if prompt.startswith("Do you want to engage the TransWarp drive? "): self.queue_player.put("N") elif prompt.startswith( "Engage the Autopilot? (Y/N/Single step/Express) [Y]" ): self.queue_player.put("E") elif prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ?" ): self.queue_player.put("N") elif re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): self.state = 5 self.send2game("L") elif self.state == 5: if self.FROM == 1: # Is our FROM planet Terra? if prompt.startswith("Land on which planet ?"): self.send2game("1\r\r\r{0}\r".format(self.TO)) self.state = 6 # Planetary Scanner Detected, Move to sector with planet elif prompt.startswith( "Do you wish to (L)eave or (T)ake Colonists? [T] (Q to leave)" ): self.send2game("\r\r{0}\r".format(self.TO)) self.state = 6 # No Planetary Scanner Detected, Move to sector with planet elif prompt.startswith( "Engage the Autopilot? (Y/N/Single step/Express) [Y]" ): self.send2game("E") self.state = 4 # We are not at terra, go back else: if prompt.startswith("Land on which planet ?"): self.send2game( "{0}\rs\rt1\rq{1}\r".format(self.from_number, self.TO) ) self.state = 6 # Planetary Scanner Detected, Move to sector with planet elif prompt.startswith("Planet command (?=help) [D] "): self.send2game("s\rt1\rq{0}\r".format(self.TO)) self.state = 6 elif prompt.startswith( "Engage the Autopilot? (Y/N/Single step/Express) [Y]" ): self.send2game("E") self.state = 4 # We are not at terra, go back elif self.state == 6: if prompt.startswith("Do you want to engage the TransWarp drive? "): self.queue_player.put("N") elif prompt.startswith( "Engage the Autopilot? (Y/N/Single step/Express) [Y]" ): self.queue_player.put("E") elif prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ?" ): self.queue_player.put("N") elif re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): self.state = 6 self.send2game("L") if self.TO != 1: if prompt.startswith("Land on which planet ?"): self.state = 7 self.send2game("{0}\r".format(self.to_number)) # Planetary Scanner Detected selecting planet number elif prompt.startswith("Planet command (?=help) [D] "): self.state = 7 self.send2game("D") # No Planetary Scaner skip on else: if prompt.startswith("Land on which planet ?"): self.state = 7 self.send2game("1\r") # Planetary Scanner Detected selecting planet number elif prompt.startswith("Do you wish to (L)eave or (T)ake Colonists?"): self.state = 7 self.send2game("L") # No Planetary Scaner skip on elif self.state == 7: if prompt.startswith("Engage the Autopilot? (Y/N/Single step/Express) [Y]"): self.state = 6 self.send2game("E") # Missed moving jump back to state 6 if self.TO != 1: if prompt.startswith("Land on which planet ?"): self.queue_player.put("{0}\r".format(self.to_number)) # Planetary Scanner Detected selecting planet number if prompt.startswith("Planet command (?=help) [D] "): # Unload people and process the loop self.loops -= 1 if self.loops: self.state = 3 self.send2game("S\r\r1\rQ") # Jump to state 3 we are not done else: self.send2game("S\r\r1\rQ") self.send2player( "\r" + Boxes.alert("Completed ({0})".format(self.maxloops)) ) self.deactivate(True) # Ok we are done else: if prompt.startswith("Land on which planet ?"): self.queue_player.put("1\r") # Planetary Scanner Detected selecting planet number if prompt.startswith("Do you wish to (L)eave or (T)ake Colonists?"): # Unload people and process the loop self.loops -= 1 if self.loops: self.state = 3 self.send2game("L\r") # Jump to state 3 we are not done else: self.send2game("L\r") self.send2player( "\r" + Boxes.alert("Completed ({0})".format(self.maxloops)) ) self.deactivate(True) # Ok we are done if prompt.startswith( "How many groups of Colonists do you want to leave" ): self.loops -= 1 if self.loops: self.state = 3 self.send2game("\r") # Jump to state 3 we are not done else: self.send2game("\r") self.send2player( "\r" + Boxes.alert("Completed ({0})".format(self.maxloops)) ) self.deactivate(True) # Ok we are done def game_line(self, line: str): log.debug("L {0} | {1}".format(self.state, line)) # IF at any state we see turns left lets grab it if "turns left" in line: work = line[19:].replace(" turns left.", "").strip() self.turns = work log.debug("TURNS LEFT: {0}".format(self.turns)) if int(self.turns) < 200: self.send2player( "\r" + Boxes.alert("Low Turns! ({0})".format(self.turns)) ) self.deactivate(True) # IF at any state we see how many holds avalible let's get that if "Total Holds" in line: work = line[16:].replace("-", "").replace("=", " ").split() self.total_holds = int(work[0]) count = 0 seen_empty = False for w in work: if w != "Empty": count += 1 elif w == "Empty": count += 1 self.holds = int(work[count]) log.debug("EMPTY HOLDS = {0}".format(self.holds)) self.holds_percent = int((self.holds / self.total_holds) * 100.0) log.debug("HOLDS PERCENT = {0}%".format(self.holds_percent)) seen_empty = True if self.holds < self.total_holds: self.send2player( "\r" + Boxes.alert( "You need {0} holds empty! ({1} Empty)".format( self.total_holds, self.holds ) ) ) self.deactivate(True) if ( seen_empty != True ): # Just incase we didn't see Empty... meaning we have 0 avalible holds self.send2player( "\r" + Boxes.alert( "You need {0} holds empty! (0 Empty)".format(self.total_holds) ) ) self.deactivate(True) if "There aren't that many on the planet!" in line: self.send2player("\r" + Boxes.alert("We're missing people!")) self.deactivate(True) if "There isn't room on the planet for that many!" in line: self.send2player("\r" + Boxes.alert("We have to many people!")) self.deactivate(True) if "There aren't that many on Terra!" in line: self.send2player("\r" + Boxes.alert("No people on Terra to get!")) self.deactivate(True) # Now back to our scheduled program if self.state == 1: if "Personal Planet Scan" in line or "Corporate Planet Scan" in line: self.state = 2 elif self.state == 2: # Ok, we're in the planet scan part of this # 10202 #3 Home of Bugz Class M, Earth Type No Citadel] if "#" in line: # Ok, we have a planet detail line. # There's an extra T when the planet is maxed out. # [ 986 #114 Trantor Class M, Earth Type No Citadel] # [ 23 #17 gift Class O, Oceanic Level 1] if line[7] == "T": line = line[:7] + " " + line[8:] details, _, citadel = line.partition("Class") # log.info(details) # Is that what we are after? # log.info(citadel) sector, planet_number, name = re.split(r"\s+", details.strip(), 2) sector = int(sector) number = int(planet_number.replace("#", "")) if "Level" in citadel: cit = int(citadel[-1]) # Grab last item else: # Oops, there looks like there is no citadel here. cit = 0 self.last_seen = number self.planets[number] = {"sector": sector, "name": name, "citadel": cit} log.info( "Planet # {0} in {1} called {2} with a lvl {3} citadel".format( number, sector, name, cit ) ) if "---" in line: number = self.last_seen # Ok, take the last_seen number, and use it for this line # [ Sector Planet Name Ore Org Equ Ore Org Equ Fighters Citadel] # [ Shields Population -=Productions=- -=-=-=-=-On Hands-=-=-=-=- Credits] # [ 10202 #3 Home of Bugz Class M, Earth Type No Citadel] # [ --- (1M) 144 49 26 145 75 12 10 0] details = re.split(r"\s+", line.strip()) log.debug(details) # What are we getting? # OK: Likely, I'm not going to use these numbers AT ALL. self.planets[number]["population"] = ( details[1].replace("(", "").replace(")", "") ) # Ok, there's going to have to be some sort of modifier (K, M, etc.) # to these numbers. Beware! self.planets[number]["ore"] = details[5] self.planets[number]["org"] = details[6] self.planets[number]["equ"] = details[7] # self.planets[number]['cit'] = details[9] # This is if we wanted cit money... but that's not it class MaxFighterMake(object): """ Max Fighter Make State of the Union: 1 = Grab planets in current sector 2 = Ask what planet to utilize, land 3 = Grab Snapshot of planet (We will need to check population lvls too) 4 = try cat 1 to cat 2 (Or perhaps this defaults to false if cat 2 is N/A) 5 = if bad then try cat 1 to cat 3 (Or perhaps this defaults to false if cat 3 is N/A) 6 = if bad then try cat 2 to cat 3 (Or perhaps this defaults to false if cat 3 is N/A) 7 = loop back to state 3 until all result in bad (Yay best acheived) """ def __init__(self, game): self.game = game self.queue_game = game.queue_game self.queue_player = game.queue_player self.observer = game.observer self.r = Style.RESET_ALL self.c = merge(Style.BRIGHT + Fore.YELLOW) self.nl = "\n\r" # My "stuff" in my pants! self.state = 1 self.corp = True self.target_sector = 0 # Sector Number of target self.target_number = 0 # Planet Number of target self.last_seen = {"Ore": False, "Org": False, "Equ": False, "Fig": False} self.max_product = 0 self.trying = "Ore2Org" self.trying_NEXT = False self.movement = 1000 self.planets_list = [] # Will be a list of dicts, containing planet information self.cap = False # Capture line for planet # Activate self.prompt = game.buffer self.save = self.observer.save() self.observer.connect("player", self.player) self.observer.connect("prompt", self.game_prompt) self.observer.connect("game-line", self.game_line) self.ore = {} self.org = {} self.equ = {} self.fig = {} self.defer = None self.to_player = self.game.to_player self.game.to_player = False self.send2game("D") def whenDone(self): self.defer = defer.Deferred() return self.defer def deactivate(self, andExit=False): self.state = 0 log.debug("MaxFighterMake.deactivate()") self.game.to_player = True assert not self.save is None self.observer.load(self.save) self.save = None if self.defer: if andExit: self.defer.callback({"exit": True}) else: self.defer.callback("done") self.defer = None def player(self, chunk: bytes): self.deactivate(True) def send2game(self, txt): # Removed debug info since I use this to also display Boxes.alert self.queue_player.put(txt) def send2player(self, txt): # Removed debug since it really doesn't help with anything self.queue_game.put(txt) def target_chosen(self, choice: str): if choice.strip() == "": self.deactivate(True) else: self.target_number = int(choice) if self.target_number > self.total: self.deactivate(True) else: self.state = 3 self.send2game("L") def check_resource(self, resource): # Given a valid string that can be converted to int if resource == "N/A": resource = -1 else: resource = int(resource.replace(",", "")) return resource def game_prompt(self, prompt: str): log.debug("P {0} | {1}".format(self.state, prompt)) if self.state == 2: self.game.to_player = True if len(self.planets_list) == 0: self.send2player(self.nl + Boxes.alert("No planets in this sector!")) self.deactivate(True) tc = merge(Style.BRIGHT + Fore.YELLOW + Back.BLUE) c1 = merge(Style.BRIGHT + Fore.WHITE + Back.BLUE) c2 = merge(Style.BRIGHT + Fore.CYAN + Back.BLUE) box = Boxes(44, color=tc) self.queue_game.put(box.top()) self.queue_game.put( box.row( tc + "{0:3} {1:20} {2:18} ".format(" # ", "Planet Name", "Planet Type") ) ) self.queue_game.put(box.middle()) def planet_output(num, planet_name, planet_type): row = "{0}{1:3} {2}{3:29} {4:9} ".format( c1, num, c2, planet_name, planet_type ) self.send2player(box.row(row)) self.total = 0 for p in self.planets_list: planet_output(self.total, p["name"], p["type"]) self.total += 1 ask = PlayerInput(self.game) d = ask.prompt("Choose a planet", 3, name="planet", digits=True) d = d.addCallback(self.target_chosen) elif self.state == 4: if prompt.startswith("Planet command (?=help) [D]"): self.send2game("?") self.state = 6 elif self.state == 6: if prompt.startswith("Planet command (?=help) [D]"): self.send2game("P") self.state = 7 elif self.state == 7: if prompt.startswith("Display planet?"): self.send2game("N") self.state = 8 elif self.state == 8: if prompt.startswith("(1)Ore, (2)Org or (3)Equipment?"): if self.trying == "Ore2Org" or self.trying == "Ore2Equ": self.send2game("1") elif self.trying == "Org2Equ" or self.trying == "Org2Ore": self.send2game("2") elif self.trying == "Equ2Org" or self.trying == "Equ2Ore": self.send2game("3") self.state = 9 elif self.state == 9: if prompt.startswith("How many groups of Colonists do you want to move?"): self.send2game("{0}\r".format(self.movement)) self.state = 10 elif self.state == 10: if prompt.startswith("(1)Ore, (2)Org or (3)Equipment?"): if self.trying == "Org2Ore" or self.trying == "Equ2Ore": self.send2game("1") elif self.trying == "Ore2Org" or self.trying == "Equ2Org": self.send2game("2") elif self.trying == "Ore2Equ" or self.trying == "Org2Equ": self.send2game("3") self.state = 11 elif self.state == 11: if prompt.startswith("Planet command (?=help) [D]"): self.send2game("D") self.trying_NEXT = False self.last_seen["Ore"] = False self.last_seen["Org"] = False self.last_seen["Equ"] = False self.last_seen["Fig"] = False self.state = 12 elif self.state == 12: if prompt.startswith("Planet command (?=help) [D]"): self.send2game("?") self.state = 6 # if(self.trying == 'Ore2Org'): # self.send2game('P\r1{0}2D'.format(self.movement)) # self.state = 4 # # Back to looking at the planet # elif(self.trying == 'Ore2Equ'): # self.send2game('P\r1{0}3D'.format(self.movement)) # self.state = 4 # # Back to looking at the planet # elif(self.trying == 'Org2Equ'): # self.send2game('P\r2{0}3D'.format(self.movement)) # self.state = 4 # elif(self.trying == 'reverse'): # if(self.trying_OLD == 'Ore2Org'): # self.send2game('P\r2{0}1D'.format(self.movement)) # elif(self.trying_OLD == 'Ore2Equ'): # self.send2game('P\r3{0}1D'.format(self.movement)) # elif(self.trying_OLD == 'Org2Equ'): # self.send2game('P\r3{0}2D'.format(self.movement)) # self.state = 4 # self.trying = self.trying_NEXT def game_line(self, line: str): log.debug("L {0} | {1}".format(self.state, line)) # IF at any state we see turns left lets grab it if "turns left" in line: work = line[19:].replace(" turns left.", "").strip() self.turns = work log.debug("TURNS LEFT: {0}".format(self.turns)) if int(self.turns) < 200: self.send2player( "\r" + Boxes.alert("Low Turns! ({0})".format(self.turns)) ) self.deactivate(True) # IF at any state we see how many holds avalible let's get that if "Total Holds" in line: work = line[16:].replace("-", "").replace("=", " ").split() self.total_holds = int(work[0]) count = 0 for w in work: if w != "Empty": count += 1 elif w == "Empty": count += 1 self.holds = int(work[count]) log.debug("EMPTY HOLDS = {0}".format(self.holds)) if self.holds < self.total_holds: self.send2player( "\r" + Boxes.alert( "You need {0} holds empty! ({1} Empty)".format( self.total_holds, self.holds ) ) ) self.deactivate(True) if "There aren't that many on the planet!" in line: self.send2player("\r" + Boxes.alert("We're missing people!")) self.deactivate(True) if "There isn't room on the planet for that many!" in line: self.send2player("\r" + Boxes.alert("We have to many people!")) self.deactivate(True) # Now back to our scheduled program if self.state == 1: if line.startswith("Planets"): self.cap = True work = re.split(r"\s+", line) # log.debug("'{0}'".format(work)) final = {"name": work[-1], "type": work[-2]} self.planets_list.append(final) log.debug("Planet {0} type {1}".format(final["name"], final["type"])) elif line.startswith("Ships"): # Stop 1, so we don't capture everything self.cap = False elif line.startswith("Fighters"): # Stop 2, so we don't capture everything self.cap = False elif self.cap == True: work = re.split(r"\s+", line) # log.debug("'{0}'".format(work)) final = {"name": work[-1], "type": work[-2]} self.planets_list.append(final) log.debug("Planet {0} type {1}".format(final["name"], final["type"])) elif self.cap == False and len(self.planets_list) != 0: self.state = 2 elif self.state == 3: if self.planets_list[self.target_number]["name"] in line: work = re.split(r"\s+", line) log.debug("'{0}'".format(work)) pnumber = work[2].replace(">", "") self.planets_list[self.target_number][ "pnumber" ] = pnumber # Add this to our dict of the planet log.debug( "Pnumber {0} is {1}".format( pnumber, self.planets_list[self.target_number]["name"] ) ) self.send2game("{0}\r".format(pnumber)) self.state = 4 elif self.state == 4: # Item Colonists Colonists Daily Planet Ship Planet # (1000s) 2 Build 1 Product Amount Amount Maximum # ------- --------- --------- --------- --------- --------- --------- # Fuel Ore 707 2 353 10,376 0 200,000 # Organics 651 5 130 4,304 0 200,000 # Equipment 1,641 20 82 2,618 0 100,000 # Fighters N/A 63 47 1,463 400 1,000,000 if line.startswith("Fuel Ore"): work = re.split(r"\s+", line) # log.debug("Ore - Pop: {0}, 2make1: {1} dailyproduct: {2} pamount: {3} saamount: {4} pmax: {5}".format(work[2], work[3], work[4], work[5], work[6], work[7])) self.ore = { "pop": self.check_resource(work[2]), "make": self.check_resource(work[3]), "daily": self.check_resource(work[4]), "amount": self.check_resource(work[5]), "ship": self.check_resource(work[6]), "pmax": self.check_resource(work[7]), } self.last_seen["Ore"] = True elif line.startswith("Organics"): work = re.split(r"\s+", line) # log.debug("Org - Pop: {0}, 2make1: {1} dailyproduct: {2} pamount: {3} saamount: {4} pmax: {5}".format(work[1], work[2], work[3], work[4], work[5], work[6])) self.org = { "pop": self.check_resource(work[1]), "make": self.check_resource(work[2]), "daily": self.check_resource(work[3]), "amount": self.check_resource(work[4]), "ship": self.check_resource(work[5]), "pmax": self.check_resource(work[6]), } self.last_seen["Org"] = True elif line.startswith("Equipment"): work = re.split(r"\s+", line) # log.debug("Equ - Pop: {0}, 2make1: {1} dailyproduct: {2} pamount: {3} saamount: {4} pmax: {5}".format(work[1], work[2], work[3], work[4], work[5], work[6])) self.equ = { "pop": self.check_resource(work[1]), "make": self.check_resource(work[2]), "daily": self.check_resource(work[3]), "amount": self.check_resource(work[4]), "ship": self.check_resource(work[5]), "pmax": self.check_resource(work[6]), } self.last_seen["Equ"] = True elif line.startswith("Fighters"): work = re.split(r"\s+", line) # log.debug("Fig - Pop: {0}, 2make1: {1} dailyproduct: {2} pamount: {3} saamount: {4} pmax: {5}".format(work[1], work[2], work[3], work[4], work[5], work[6])) self.fig = { "pop": self.check_resource(work[1]), "make": self.check_resource(work[2]), "daily": self.check_resource(work[3]), "amount": self.check_resource(work[4]), "ship": self.check_resource(work[5]), "pmax": self.check_resource(work[6]), } self.last_seen["Fig"] = True else: if ( self.last_seen["Ore"] == True and self.last_seen["Org"] == True and self.last_seen["Equ"] == True and self.last_seen["Fig"] == True ): if self.fig["daily"] > self.max_product: self.max_product = self.fig["daily"] self.planets_list[self.target_number]["Ore"] = self.ore self.planets_list[self.target_number]["Org"] = self.org self.planets_list[self.target_number]["Equ"] = self.equ self.planets_list[self.target_number]["Fig"] = self.fig log.debug("Max Production is {0}!".format(self.max_product)) self.state == 6 elif self.state == 12: if line.startswith("Fuel Ore"): work = re.split(r"\s+", line) # log.debug("Ore - Pop: {0}, 2make1: {1} dailyproduct: {2} pamount: {3} saamount: {4} pmax: {5}".format(work[2], work[3], work[4], work[5], work[6], work[7])) self.ore = { "pop": self.check_resource(work[2]), "make": self.check_resource(work[3]), "daily": self.check_resource(work[4]), "amount": self.check_resource(work[5]), "ship": self.check_resource(work[6]), "pmax": self.check_resource(work[7]), } self.last_seen["Ore"] = True elif line.startswith("Organics"): work = re.split(r"\s+", line) # log.debug("Org - Pop: {0}, 2make1: {1} dailyproduct: {2} pamount: {3} saamount: {4} pmax: {5}".format(work[1], work[2], work[3], work[4], work[5], work[6])) self.org = { "pop": self.check_resource(work[1]), "make": self.check_resource(work[2]), "daily": self.check_resource(work[3]), "amount": self.check_resource(work[4]), "ship": self.check_resource(work[5]), "pmax": self.check_resource(work[6]), } self.last_seen["Org"] = True elif line.startswith("Equipment"): work = re.split(r"\s+", line) # log.debug("Equ - Pop: {0}, 2make1: {1} dailyproduct: {2} pamount: {3} saamount: {4} pmax: {5}".format(work[1], work[2], work[3], work[4], work[5], work[6])) self.equ = { "pop": self.check_resource(work[1]), "make": self.check_resource(work[2]), "daily": self.check_resource(work[3]), "amount": self.check_resource(work[4]), "ship": self.check_resource(work[5]), "pmax": self.check_resource(work[6]), } self.last_seen["Equ"] = True elif line.startswith("Fighters"): work = re.split(r"\s+", line) # log.debug("Fig - Pop: {0}, 2make1: {1} dailyproduct: {2} pamount: {3} saamount: {4} pmax: {5}".format(work[1], work[2], work[3], work[4], work[5], work[6])) self.fig = { "pop": self.check_resource(work[1]), "make": self.check_resource(work[2]), "daily": self.check_resource(work[3]), "amount": self.check_resource(work[4]), "ship": self.check_resource(work[5]), "pmax": self.check_resource(work[6]), } self.last_seen["Fig"] = True else: if ( self.last_seen["Ore"] == True and self.last_seen["Org"] == True and self.last_seen["Equ"] == True and self.last_seen["Fig"] == True ): if self.fig["daily"] > self.max_product: self.max_product = self.fig["daily"] self.planets_list[self.target_number]["Ore"] = self.ore self.planets_list[self.target_number]["Org"] = self.org self.planets_list[self.target_number]["Equ"] = self.equ self.planets_list[self.target_number]["Fig"] = self.fig log.debug("Max Production is now {0}!".format(self.max_product)) if self.fig["daily"] < self.max_product: # Oh nose, our change actually made things worse... let's fix that and try a different way if self.trying == "Ore2Org": self.trying = "Org2Ore" elif self.trying == "Ore2Equ": self.trying = "Equ2Ore" elif self.trying == "Org2Equ": self.trying = "Equ2Org" self.state = 6 if ( self.ore["pop"] < 1 or self.org["pop"] < 1 or self.equ["pop"] < 1 ): self.send2game("Q") self.send2player( self.nl + Boxes.alert( "Peak Fighter Production: {0}".format(self.fig["daily"]) ) ) self.deactivate(True) class evilTrade(object): """ A little bit on playing evil, Doing SSM, a bust will cause some holds and any cargo to be removed! You must move to different ports to reduce the chance of a bust, Bust's are reset after so many days... so might want to handle that. SSM rewards you -10 Alignment and +5 Experiece if you are sucessful in robbing back your cargo you just sold. States: 0 = Pre-State, Asks the user how many times to run 1 = Gather user info 2 = Verify Cargo is full of Equ, else jump to state 3.5 3 = Find nearest "Evil Pair" of ports, move to first, jump to 4 3.5 = User does not have Equ, lets buy it, then move on (SSM: Sell-Steal-Move) 4 = Port and Sell Equ 5 = Port and Steal Equ 6 = Move to 2nd "Evil Pair" 7 = Port and Sell Equ 8 = Port and Steal Equ 9 = Move to 1st "Evil Pair" 10 = Decide to loop, if so we jump back to state 4, else exit """ def __init__(self, game): self.game = game self.queue_game = game.queue_game self.queue_player = game.queue_player self.observer = game.observer self.r = Style.RESET_ALL self.c = merge(Style.BRIGHT + Fore.YELLOW) self.nl = "\n\r" self.state = 0 # Pre-state self.holds = 0 # Total holds empty, self.holds == self.cargo["Empty"] self.cargo = { "Ore": 0, "Org": 0, "Equ": 0, "Colo": 0, "Empty": 0, } # What is in our holds? self.maxHolds = 0 # Total holds self.maxLoops = 0 # So when we display done show the total loops asked to do self.loops = 0 # Current number of loops left to do self.completeINFO = False # Collected all INFO needed self.exp = 0 # Experience Points the player has self.alignment = 0 # Alignment the player has self.credits = 0 # Credits player has self.turns = 0 # Turns the player has self.sector = 0 # Current sector self.sectors = False # Do we need to update sector1 and 2? self.sector1 = None self.sector2 = None self.target_sector = 0 # Next port to trade with self.name = "" # Player name to include into stats for logs self.prompt = game.buffer self.save = self.observer.save() self.observer.connect("player", self.player) self.observer.connect("prompt", self.game_prompt) self.observer.connect("game-line", self.game_line) self.defer = None self.to_player = self.game.to_player self.game.to_player = False self.send2game("D") def whenDone(self): self.defer = defer.Deferred() return self.defer def deactivate(self, andExit=False): self.state = 0 log.debug("EvilTrade.deactivate()") self.game.to_player = True assert not self.save is None self.observer.load(self.save) self.save = None if self.defer: if andExit: self.defer.callback({"exit": True}) else: self.defer.callback("done") self.defer = None def player(self, chunk: bytes): self.deactivate(True) def send2game(self, txt): self.queue_player.put(txt) def send2player(self, txt): self.queue_game.put(txt) def loops_chosen(self, choice: str): if choice.strip() == "": self.deactivate(True) else: self.loops = int(choice) self.maxLoops = self.loops self.state = 1 self.send2game("I") def game_prompt(self, prompt: str): log.debug("P {0} | {1}".format(self.state, prompt)) if self.state == 0: if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): ask = PlayerInput(self.game) d = ask.prompt("How many times: ", 3, name="times", digits=True) d.addCallback(self.loops_chosen) elif self.state == 1: if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): if self.completeINFO: # We have all our info, lets display it into logs log.debug("{0}'s Stats...".format(self.name)) log.debug( "Experience: {0} Alignment: {1} Turns: {2} Credits: {3} Total Holds: {4} Avaible Holds: {5} Current Sector: {6}".format( self.exp, self.alignment, self.turns, self.credits, self.maxHolds, self.holds, self.sector, ) ) log.debug( "Ore: {0} Org: {1} Equ: {2} Colo: {3} Empty: {4}".format( self.cargo["Ore"], self.cargo["Org"], self.cargo["Equ"], self.cargo["Colo"], self.cargo["Empty"], ) ) self.state = 2 self.send2game("D") self.game.to_player = True elif self.state == 2: if self.cargo["Ore"] != 0: # Nada, we want Equ self.send2player( self.nl + Boxes.alert("Holds must contain Equ not Ore!") ) self.deactivate(True) elif self.cargo["Org"] != 0: # Oops, wrong, Give use Equ self.send2player( self.nl + Boxes.alert("Holds must contain Equ not Org!") ) self.deactivate(True) elif ( self.cargo["Colo"] != 0 ): # We don't want people, let the user handle this self.send2player( self.nl + Boxes.alert("Holds must contain Equ not Colonist!") ) self.deactivate(True) elif self.cargo["Equ"] != 0: # We do have Equ, but do we need more? if self.cargo["Equ"] != self.maxHolds: self.send2player( self.nl + Boxes.alert( "You need {0} more Equ!".format(self.cargo["Empty"]) ) ) # Ok nope, we need more, let's find the nearest and "Mush!" place = self.game.gamedata.find_nearest_selling( self.sector, "E", 400 ) if place == 0: self.queue_game.put( self.nl + Boxes.alert("Find Nearest Failed!") ) self.deactivate(True) else: self.send2game("{0}\rEPT".format(place)) # Code here to handle getting Equ self.state = 3.5 else: # All OK, now to find_nearest_evilpairs self.state = 3 self.game.to_player = False self.send2game("D") self.game.to_player = True else: # Ok so we have Empty holds, lets fill it with Equ place = self.game.gamedata.find_nearest_selling(self.sector, "E", 400) if place == 0: self.queue_game.put(self.nl + Boxes.alert("Find Nearest Failed!")) self.deactivate(True) else: self.send2game("{0}\rEPT".format(place)) # Code here to handle getting Equ self.state = 3.5 elif self.state == 3.5: # Ok, we are getting Equ into holds then onto 3 if prompt.startswith("How many holds of Fuel Ore do you want to buy "): self.send2game("0\r") elif prompt.startswith("How many holds of Organics do you want to buy "): self.send2game("0\r") elif prompt.startswith("How many holds of Equipment do you want to buy "): self.send2game("\r\r") # Oops, we are expecting the user to have enough money to buy it! self.state = 3 elif re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): self.send2game("D") self.state = 3 elif self.state == 3: if prompt.startswith("Your offer"): self.send2game("0\r") self.deactivate(True) else: # Now to go to evil trade pair \o/ # Ok so if we go to line 2117 we can see how to use find_nearest_tradepairs() # Which will give us a starting point for what needs to be done for us trading log.info("find_nearest_evilpairs({0})".format(self.sector)) c = coiterate( self.game.gamedata.find_nearest_evilpairs(self.sector, self) ) c.addCallback(lambda unknown: self.evil()) self.state = 4 elif self.state == 4: if prompt.startswith("Do you want to engage the TransWarp drive? "): self.queue_player.put("N") elif prompt.startswith( "Engage the Autopilot? (Y/N/Single step/Express) [Y]" ): self.queue_player.put("E") elif prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ?" ): self.queue_player.put("N") elif re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # We should be where we wanted to. self.send2game("PT\r\r") self.state = 5 elif self.state == 5: if prompt.startswith("How many holds of Fuel Ore do you want to buy"): self.send2game("0\r") elif prompt.startswith("How many holds of Organics do you want to buy"): self.send2game("0\r") elif re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): self.send2game("PR\rS3\r") self.state = 6 elif self.state == 7: if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): c = coiterate(self.find_next_evilpair()) c.addCallback(lambda unknown: self.evil()) self.state = 8 elif self.state == 8: if prompt.startswith("Do you want to engage the TransWarp drive? "): self.queue_player.put("N") elif prompt.startswith( "Engage the Autopilot? (Y/N/Single step/Express) [Y]" ): self.queue_player.put("E") elif prompt.startswith( "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ?" ): self.queue_player.put("N") elif re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # We should be where we wanted to. self.send2game("PT\r\r") self.state = 9 elif self.state == 9: if prompt.startswith("How many holds of Fuel Ore do you want to buy"): self.send2game("0\r") elif prompt.startswith("How many holds of Organics do you want to buy"): self.send2game("0\r") elif re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): self.send2game("PR\rS3\r") self.state = 10 def evil(self): if self.target_sector is None: self.send2player( self.nl + Boxes.alert("I don't see any ports to trade with.") ) self.deactivate(True) else: self.send2game("{0}\r".format(self.target_sector)) if self.sectors == False: self.sector1 = self.sector self.sector2 = self.target_sector self.sectors = True self.sector = self.target_sector def find_next_evilpair(self): """ Find the next GOOD trade pair sector. This is sequential (just like Trade report) """ # This should be using the setting in the config, not a hard coded value! # show_limit (galaxy.port_trade_show) is pct >= show_limit. show_limit = self.game.gamedata.get_config("Display_Percent", "90") try: show_limit = int(show_limit) except ValueError: show_limit = 90 if show_limit < 15: show_limit = 15 if show_limit > 90: show_limit = 90 # Look for "GOOD" trades for sector in sorted(self.game.gamedata.ports.keys()): pd = self.game.gamedata.ports[sector] if not GameData.port_burnt(pd): # This happens when you trade with a StarDock if "class" not in pd: continue pc = pd["class"] # Ok, let's look into it. if not sector in self.game.gamedata.warps: continue # Busted port if not sector in self.game.gamedata.busts: continue warps = self.game.gamedata.warps[sector] for w in warps: # We can get there, and get back. if ( w in self.game.gamedata.warps and sector in self.game.gamedata.warps[w] ): # Ok, we can get there -- and get back! if ( w > sector and w in self.game.gamedata.ports and not GameData.port_burnt(self.game.gamedata.ports[w]) ): wd = self.game.gamedata.ports[w] if "class" not in wd: continue wc = wd["class"] if pc in (2, 3, 4, 8) and wc in (2, 3, 4, 8): data = self.game.gamedata.port_trade_show( sector, w, show_limit ) if data: self.target_sector = sector return sector yield # Look for OK trades for sector in sorted(self.game.gamedata.ports.keys()): pd = self.game.gamedata.ports[sector] if not GameData.port_burnt(pd): if "class" not in pd: continue pc = pd["class"] # Ok, let's look into it. if not sector in self.game.gamedata.warps: continue # Busted port if not sector in self.game.gamedata.busts: continue warps = self.game.gamedata.warps[sector] for w in warps: # We can get there, and get back. if ( w in self.game.gamedata.warps and sector in self.game.gamedata.warps[w] ): # Ok, we can get there -- and get back! if ( w > sector and w in self.game.gamedata.ports and not GameData.port_burnt(self.game.gamedata.ports[w]) ): wd = self.game.gamedata.ports[w] if "class" not in wd: continue wc = wd["class"] if GameData.port_trading( pd["port"], self.game.gamedata.ports[w]["port"] ): data = self.game.gamedata.port_trade_show( sector, w, show_limit ) if data: self.target_sector = sector return sector yield self.target_sector = None def game_line(self, line: str): log.debug("L {0} | {1}".format(self.state, line)) if self.state == 1: if line.startswith("Trader Name"): work = line.replace("Trader Name :", "").split() self.name = work[-1] elif line.startswith("Current Sector"): work = line.replace("Current Sector : ", "") self.sector = int(work) elif line.startswith("Rank and Exp"): work = line.replace("Rank and Exp :", "").split() # Rank and Exp : 110 points, Alignment=-116 Crass self.exp = work[0] self.alignment = work[2].replace("Alignment=", "") elif line.startswith("Turns left"): work = line.replace("Turns left : ", "") self.turns = int(work) elif line.startswith("Total Holds"): work = ( line[16:] .replace("-", "") .replace("=", " ") .replace("Fuel Ore", "Fuel") .split() ) self.maxHolds = int(work[0]) # Total Holds : 53 - Empty=53 # Total Holds : 53 - Fuel Ore=1 Organics=2 Equipment=3 Colonists=4 Empty=43 # log.debug(work) # '53', 'Fuel', '1', 'Organics', '2', 'Equipment', '3', 'Colonists', '4', 'Empty', '43' count = 0 for w in work: if w == "Fuel": try: self.cargo["Ore"] = int(work[count + 1]) except ValueError: # The next value is not a number! self.cargo["Ore"] = 0 elif w == "Organics": try: self.cargo["Org"] = int(work[count + 1]) except ValueError: # The next value is not a number! self.cargo["Org"] = 0 elif w == "Equipment": try: self.cargo["Equ"] = int(work[count + 1]) except ValueError: # The next value is not a number! self.cargo["Equ"] = 0 elif w == "Colonists": try: self.cargo["Colo"] = int(work[count + 1]) except ValueError: # The next value is not a number! self.cargo["Colo"] = 0 elif w == "Empty": try: self.cargo["Empty"] = int(work[count + 1]) self.holds = self.cargo["Empty"] except ValueError: # The next value is not a number! self.cargo["Empty"] = 0 count += 1 elif line.startswith("Credits"): work = line.replace("Credits : ", "").replace(",", "") self.credits = int(work) self.completeINFO = True if self.state == 6: if "Suddenly you're Busted!" in line: # Oops! We got busted, let's stop. self.deactivate(True) elif "Success!" in line: # Ok we passed that... now to move on. self.state = 7 if self.state == 10: if "Suddenly you're Busted!" in line: # Oops! We got busted, let's stop. self.deactivate(True) elif "Success!" in line: # Ok we passed that... now to move on. self.loops -= 1 if self.loops >= 0: self.send2player( self.nl + Boxes.alert("Completed {0}".format(self.maxLoops)) ) self.deactivate(True) else: c = coiterate(self.find_next_evilpair()) c.addCallback(lambda unknown: self.evil()) self.state = 4 class bustViewer(object): """ Display all busts Perhaps also add it so we can manually reset/clear busts? Or maybe... We make galaxy's maint_busts() configurable so the check in days is in the config States: 0 = Perform convertion of self.game.gamedata.busts into a more understandable format. 1 = Display/List all busts perferably just their sector numbers and when (what date) did that occur. Optionals: 1. Give numbering to state 0 so you can select a specific bust to be perhaps removed and/or add a remove all option. (Dangerous, might not want to allow someone to manually reset all that) 2. Add math that calculates the age in days for that particular bust. (Then add this to state 1 Display/List) (Ok well this one should be really easy make a string/tuple storing month/day/year for both now and the bust then,) (compare them one by one against eachother if 0 we know it's not that category) <- Should always be days since we are clearing them out automatically """ def __init__(self, game): self.game = game self.queue_game = game.queue_game self.queue_player = game.queue_player self.observer = game.observer self.r = Style.RESET_ALL self.c = merge(Style.BRIGHT + Fore.YELLOW) self.nl = "\n\r" # Stuff self.busted = {} # Make custom bust dict based on self.game.gamedata.busts dict self.age = {} self.state = 0 # Display/List self.prompt = game.buffer self.save = self.observer.save() self.observer.connect("player", self.player) self.observer.connect("prompt", self.game_prompt) self.observer.connect("game-line", self.game_line) self.defer = None self.to_player = self.game.to_player self.game.to_player = False self.send2game("D") # Since we need to send at least 1 character def whenDone(self): self.defer = defer.Deferred() return self.defer def deactivate(self, andExit=False): self.state = 0 log.debug("bustViewer.deactivate()") self.game.to_player = True assert not self.save is None self.observer.load(self.save) self.save = None if self.defer: if andExit: self.defer.callback({"exit": True}) else: self.defer.callback("done") self.defer = None def player(self, chunk: bytes): self.deactivate(True) def send2game(self, txt): self.queue_player.put(txt) def send2player(self, txt): self.queue_game.put(txt) def calcAge(self, dt): rightNow = pendulum.now() result = rightNow.diff(dt).in_days() log.debug("calcAge('{0}') got {1} days".format(dt.to_datetime_string(), result)) return result def game_prompt(self, prompt: str): log.debug("P {0} | {1}".format(self.state, prompt)) if self.state == 0: # Convert from YYYY-MM-DD to MM-DD-YYYY for s in self.game.gamedata.busts: d = self.game.gamedata.busts[s] # Keep as string d1 = pendulum.parse(d) # Convert to dateTime obj log.debug("{0} on {1}".format(s, d)) self.busted[s] = "{0:02d}-{1:02d}-{2:04d}".format( d1.month, d1.day, d1.year ) self.age[s] = self.calcAge(d1) self.state += 1 self.send2game("D") elif self.state == 1: # Display it nicely to the user, then exit tc = merge(Style.BRIGHT + Fore.YELLOW + Back.BLUE) c1 = merge(Style.BRIGHT + Fore.WHITE + Back.BLUE) c2 = merge(Style.BRIGHT + Fore.CYAN + Back.BLUE) if self.busted: box = Boxes(42, color=tc) self.queue_game.put(box.top()) self.queue_game.put( box.row( tc + " {0:10} {1:<20} {2:8} ".format( "Sector", "Date Busted On", "Days old" ) ) ) self.queue_game.put(box.middle()) for s in self.busted: if self.age[s]: self.queue_game.put( box.row( c1 + " {0:<10} {1:<20} {2:8} ".format( s, self.busted[s], self.age[s] ) ) ) else: self.queue_game.put( box.row( c1 + " {0:<10} {1:<20} ".format(s, self.busted[s]) ) ) self.queue_game.put(box.bottom()) else: self.send2player( self.nl + Boxes.alert( "You have no busts to view, perhaps you just aren't into trouble." ) ) self.deactivate(True) def game_line(self, line: str): log.debug("L {0} | {1}".format(self.state, line)) class firstActivate(object): """ Upon first time activation of the proxy what happens States: 0 = Send V and gather information on when busts were cleared 1 = Calculate date from pendulum.now().subtract(days=#) 2 = If pendulum.now() is greater than our date to clear then self.game.gamedata.reset_busts() Well drat... while in a game you can't access when busts were cleared. :( """ def __init__(self, game): self.game = game self.queue_game = game.queue_game self.queue_player = game.queue_player self.observer = game.observer self.r = Style.RESET_ALL self.c = merge(Style.BRIGHT + Fore.YELLOW) self.nl = "\n\r" # Stuffz self.state = 0 # V and gather info self.prompt = game.buffer self.save = self.observer.save() self.observer.connect("player", self.player) self.observer.connect("prompt", self.game_prompt) self.observer.connect("game-line", self.game_line) self.defer = None self.to_player = self.game.to_player self.game.to_player = False self.send2game("V") # Since we need to send at least 1 character def whenDone(self): self.defer = defer.Deferred() return self.defer def deactivate(self, andExit=False): self.state = 0 log.debug("firstActivate.deactivate()") self.game.to_player = True assert not self.save is None self.observer.load(self.save) self.save = None if self.defer: if andExit: self.defer.callback({"exit": True}) else: self.defer.callback("done") self.defer = None def player(self, chunk: bytes): self.deactivate(True) def send2game(self, txt): self.queue_player.put(txt) def send2player(self, txt): self.queue_game.put(txt) def game_prompt(self, prompt: str): log.debug("P {0} | {1}".format(self.state, prompt)) if self.state == 0: # Enable sloppy-shit mode. This shows random parts # of the command line because you don't wait for # the entire commandline to be displayed. # There's a darn good reason I worked on the regex to # match the prompt... if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): # if prompt.startswith("Command"): self.deactivate() def game_line(self, line: str): log.debug("L {0} | {1}".format(self.state, line)) class ProxyMenu(object): """ Display ProxyMenu Example: from flexible import ProxyMenu if re.match(r"Command \[TL=.* \(\?=Help\)\? :", prompt): menu = ProxyMenu(self.game) """ def __init__(self, game): self.nl = "\n\r" self.c = merge(Style.BRIGHT + Fore.YELLOW + Back.BLUE) self.r = Style.RESET_ALL self.c1 = merge(Style.BRIGHT + Fore.WHITE + Back.BLUE) self.c2 = merge(Style.NORMAL + Fore.CYAN + Back.BLUE) # self.portdata = None self.game = game self.queue_game = game.queue_game self.observer = game.observer self.game.gamedata.get_config("Macro", "D^1N^D") # Am I using self or game? (I think I want game, not self.) # if hasattr(self.game, "portdata"): # self.portdata = self.game.portdata # else: # self.portdata = {} # if hasattr(self.game, 'warpdata'): # self.warpdata = self.game.warpdata # else: # self.warpdata = {} if hasattr(self.game, "trade_report"): self.trade_report = self.game.trade_report else: self.trade_report = [] # Yes, at this point we would activate self.prompt = game.buffer self.save = self.observer.save() self.observer.connect("player", self.player) # If we want it, it's here. self.defer = None self.game.to_player = True # Is it our first time activated? if so do a little extra stuff. if self.game.gamedata.activated == False: self.game.gamedata.activated = True # Run V to get date of bust clear from game # Decide to clear or not # Clear or continue on log.debug("First time activation dectected!") first = firstActivate(self.game) d = first.whenDone() d.addCallback(self.pre_back) d.addErrback(self.pre_back) else: # well that was all skipped because we already did that. self.pre_back() def __del__(self): log.debug("ProxyMenu {0} RIP".format(self)) # When we exit, always make sure player echo is on. self.game.to_player = True def pre_back(self, stuffz=None): self.keepalive = task.LoopingCall(self.awake) self.keepalive.start(30) self.menu() def whenDone(self): self.defer = defer.Deferred() # Call this to chain something after we exit. return self.defer def menu(self): box = Boxes(30, color=self.c) self.queue_game.put(box.top()) text = self.c + "{0:^30}".format("TradeWars Proxy Active") text = text.replace("Active", BLINK + "Active" + Style.RESET_ALL + self.c) self.queue_game.put(box.row(text)) self.queue_game.put(box.bottom()) self.queue_game.put(" " + self.c + "-=>" + self.r + " ") def fullmenu(self): box = Boxes(30, color=self.c) self.queue_game.put(box.top()) def menu_item(ch: str, desc: str): row = self.c1 + " {0} {1}- {2}{3:25}".format(ch, self.c2, self.c1, desc) self.queue_game.put(box.row(row)) # self.queue_game.put( # " " + self.c1 + ch + self.c2 + " - " + self.c1 + desc + self.nl # ) menu_item("C", "Configuration ({0})".format(len(self.game.gamedata.config))) menu_item("D", "Display Report again") menu_item("E", "Export Data (Save)") # menu_item("Q", "Quest") menu_item("M", "Macro") menu_item("P", "Port CIM Report ({0})".format(len(self.game.gamedata.ports))) menu_item("W", "Warp CIM Report ({0})".format(len(self.game.gamedata.warps))) menu_item("R", "Restock Report") menu_item("T", "Trading Report") menu_item("B", "Display Busts ({0})".format(len(self.game.gamedata.busts))) menu_item("S", "Scripts") menu_item("X", "eXit") bottom = box.bottom() self.queue_game.put(bottom[:-2]) def awake(self): log.info("ProxyMenu.awake()") self.game.queue_player.put(" ") def port_report(self, portdata: dict): # Check to see if the old data is close to the new data. # If so, don't toss out the special! matches = 0 for k, v in self.old_ports.items(): if k in self.game.gamedata.ports: # Ok, key exists. Is the class the same if self.game.gamedata.ports[k]["class"] == v["class"]: matches += 1 log.info("Got {0} matches old ports to new.".format(matches)) if matches > 12: self.queue_game.put( "Restoring (SPECIAL) class ports ({0}).".format(len(self.specials)) + self.nl ) for p in self.specials.keys(): self.game.gamedata.ports[int(p)] = self.specials[p] # self.portdata = portdata # self.game.portdata = portdata self.queue_game.put("Loaded {0} ports.".format(len(self.game.ports)) + self.nl) self.welcome_back() def warp_report(self, warpdata: dict): # self.warpdata = warpdata # self.game.warpdata = warpdata self.queue_game.put("Loaded {0} sectors.".format(len(warpdata)) + self.nl) self.welcome_back() def make_trade_report(self): log.debug("make_trade_report()") ok_trades = [] best_trades = [] show_best = self.game.gamedata.get_config("Display_Best", "Y").upper()[0] == "Y" show_ok = self.game.gamedata.get_config("Display_Ok", "N").upper()[0] == "Y" show_limit = self.game.gamedata.get_config("Display_Percent", "90") update_config = False try: show_limit = int(show_limit) except ValueError: show_limit = 90 update_config = True if show_limit < 0: show_limit = 0 update_config = True elif show_limit > 100: show_limit = 100 update_config = True if update_config: self.game.gamedata.set_config("Display_Percent", show_limit) # for sector, pd in self.game.gamedata.ports.items(): for sector in sorted(self.game.gamedata.ports.keys()): pd = self.game.gamedata.ports[sector] if not GameData.port_burnt(pd): # This happens if you trade with a StarDock. (It doesn't get the class set.) if "class" not in pd: continue pc = pd["class"] # Ok, let's look into it. if not sector in self.game.gamedata.warps: continue warps = self.game.gamedata.warps[sector] for w in warps: # Verify that we have that warp's info, and that the sector is in it. # (We can get back from it) if ( w in self.game.gamedata.warps and sector in self.game.gamedata.warps[w] ): # Ok, we can get there -- and get back! if ( w > sector and w in self.game.gamedata.ports and not GameData.port_burnt(self.game.gamedata.ports[w]) ): # it is > and has a port. wd = self.game.gamedata.ports[w] if "class" not in wd: continue wc = wd["class"] # 1: "BBS", # 2: "BSB", # 3: "SBB", # 4: "SSB", # 5: "SBS", # 6: "BSS", # 7: "SSS", # 8: "BBB", if pc in (1, 5) and wc in (2, 4): data = self.game.gamedata.port_trade_show( sector, w, show_limit ) if data: best_trades.append(data) # best_trades.append( "{0:5} -=- {1:5}".format(sector, w)) elif pc in (2, 4) and wc in (1, 5): data = self.game.gamedata.port_trade_show( sector, w, show_limit ) if data: best_trades.append(data) # best_trades.append( "{0:5} -=- {1:5}".format(sector, w)) elif GameData.port_trading( pd["port"], self.game.gamedata.ports[w]["port"] ): # ok_trades.append( "{0:5} -=- {1:5}".format(sector,w)) data = self.game.gamedata.port_trade_show( sector, w, show_limit ) if data: ok_trades.append(data) yield if show_best: self.trade_report.append("Best Trades: (org/equ)") self.trade_report.extend(best_trades) if show_ok: self.trade_report.append("Ok Trades:") self.trade_report.extend(ok_trades) if not show_best and not show_ok: self.queue_game.put( Boxes.alert( "You probably want to choose something to display in configuration!", base="red", ) ) # self.queue_game.put("BEST TRADES:" + self.nl + self.nl.join(best_trades) + self.nl) # self.queue_game.put("OK TRADES:" + self.nl + self.nl.join(ok_trades) + self.nl) def get_display_maxlines(self): show_maxlines = self.game.gamedata.get_config("Display_Maxlines", "0") try: show_maxlines = int(show_maxlines) except ValueError: show_maxlines = 0 if show_maxlines <= 0: show_maxlines = None return show_maxlines def show_trade_report(self, *_): show_maxlines = self.get_display_maxlines() self.game.trade_report = self.trade_report for t in self.trade_report[:show_maxlines]: self.queue_game.put(t + self.nl) self.queue_game.put(self.nl + Boxes.alert("Proxy done.", base="green")) self.observer.load(self.save) self.save = None self.keepalive = None self.prompt = None # self.welcome_back() def player(self, chunk: bytes): """ Data from player (in bytes). """ chunk = chunk.decode("latin-1", "ignore") key = chunk.upper() log.debug("ProxyMenu.player({0})".format(key)) # Stop the keepalive if we are activating something else # or leaving... self.keepalive.stop() if key == "T": self.queue_game.put(self.c + key + self.r + self.nl) # Trade Report # do we have enough information to do this? # if not hasattr(self.game, 'portdata') and not hasattr(self.game, 'warpdata'): # self.queue_game.put("Missing portdata and warpdata." + self.nl) # elif not hasattr(self.game, 'portdata'): # self.queue_game.put("Missing portdata." + self.nl) # elif not hasattr(self.game, 'warpdata'): # self.queue_game.put("Missing warpdata." + self.nl) # else: if True: # Yes, so let's start! self.trade_report = [] d = coiterate(self.make_trade_report()) d.addCallback(self.show_trade_report) return elif key == "P": self.queue_game.put(self.c + key + self.r + self.nl) # Save specials, save 10 ports self.specials = self.game.gamedata.special_ports() # Save 20 ports. https://stackoverflow.com/questions/7971618/python-return-first-n-keyvalue-pairs-from-dict#7971655 self.old_ports = { k: self.game.gamedata.ports[k] for k in list(self.game.gamedata.ports)[:20] } self.game.gamedata.reset_ports() # Activate CIM Port Report report = CIMPortReport(self.game) d = report.whenDone() d.addCallback(self.port_report) d.addErrback(self.welcome_back) return elif key == "W": self.queue_game.put(self.c + key + self.r + self.nl) self.game.gamedata.reset_warps() # Activate CIM Warp Report report = CIMWarpReport(self.game) d = report.whenDone() d.addCallback(self.warp_report) d.addErrback(self.welcome_back) return elif key == "M": self.queue_game.put(self.c + key + self.r + self.nl) self.activate_macro(1) if False: ask = PlayerInput(self.game) d = ask.prompt( "How many times?", 10, name="times", abort_blank=True, digits=True ) d.addCallback(self.activate_macro) d.addErrback(self.welcome_back) return elif key == "B": self.queue_game.put(self.c + key + self.r + self.nl) # Display Busts busting = bustViewer(self.game) d = busting.whenDone() d.addCallback(self.welcome_back) d.addErrback(self.welcome_back) return elif key == "S": self.queue_game.put(self.c + key + self.r + self.nl) # Scripts self.activate_scripts_menu() return elif key == "R": self.queue_game.put(self.c + key + self.r + self.nl) s = self.game.gamedata.special_ports() box = Boxes(14, color=self.c) self.queue_game.put(box.top()) self.queue_game.put(box.row(self.c1 + " Sector Class ")) for sector, data in s.items(): self.queue_game.put( box.row( "{0} {1:5}{2} {3:^5} ".format( self.c1, sector, self.c2, data["class"] ) ) ) self.queue_game.put(box.bottom()) elif key == "D": self.queue_game.put(self.c + key + self.r + self.nl) # (Re) Display Trade Report show_maxlines = self.get_display_maxlines() if self.trade_report: for t in self.trade_report[:show_maxlines]: self.queue_game.put(t + self.nl) self.queue_game.put(self.nl + Boxes.alert("Proxy done.", base="green")) self.observer.load(self.save) self.save = None self.keepalive = None self.prompt = None return else: self.queue_game.put("Missing trade_report." + self.nl) elif key == "E": self.queue_game.put(self.c + key + self.r + self.nl) self.queue_game.put(Boxes.alert("Saving...")) then_do = coiterate(self.game.gamedata.save()) then_do.addCallback(self.welcome_back) return elif key == "C": self.queue_game.put(self.c + key + self.r + self.nl) self.activate_config_menu() return # self.queue_game.put(pformat(self.portdata).replace("\n", "\n\r") + self.nl) # self.queue_game.put(pformat(self.warpdata).replace("\n", "\n\r") + self.nl) # elif key == "Q": # self.queue_game.put(self.c + key + self.r + self.nl) # # # This is an example of chaining PlayerInput prompt calls. # # ask = PlayerInput(self.game) # d = ask.prompt("What is your quest?", 40, name="quest", abort_blank=True) # # # Display the user's input # d.addCallback(ask.output) # # d.addCallback( # lambda ignore: ask.prompt( # "What is your favorite color?", 10, name="color" # ) # ) # d.addCallback(ask.output) # # d.addCallback( # lambda ignore: ask.prompt( # "What is the meaning of the squirrel?", # 12, # name="squirrel", # digits=True, # ) # ) # d.addCallback(ask.output) # # def show_values(show): # log.debug(show) # self.queue_game.put(pformat(show).replace("\n", "\n\r") + self.nl) # # d.addCallback(lambda ignore: show_values(ask.keep)) # d.addCallback(self.welcome_back) # # # On error, just return back # # This doesn't seem to be getting called. # # d.addErrback(lambda ignore: self.welcome_back) # d.addErrback(self.welcome_back) # return elif key == "X": self.queue_game.put(self.c + key + self.r + self.nl) self.queue_game.put(Boxes.alert("Proxy done.", base="green")) self.observer.load(self.save) self.save = None # It isn't running (NOW), so don't try to stop it. # self.keepalive.stop() self.keepalive = None # Ok, this is a HORRIBLE idea, because the prompt might be # outdated. # self.queue_game.put(self.prompt) self.prompt = None # Send '\r' to re-display the prompt # instead of displaying the original one. self.game.queue_player.put("d") # Were we asked to do something when we were done here? if self.defer: reactor.CallLater(0, self.defer.callback) # self.defer.callback() self.defer = None return else: self.fullmenu() self.keepalive.start(30, True) self.menu() def activate_config_menu(self): self.observer.disconnect("player", self.player) self.observer.connect("player", self.config_player) self.config_menu() def deactivate_config_menu(self, *data): log.warn("deactivate_config_menu ({0})".format(data)) self.observer.disconnect("player", self.config_player) self.observer.connect("player", self.player) self.welcome_back() def activate_macro(self, *data): log.warn("macro: ({0})".format(data)) macro = self.game.gamedata.get_config("Macro", "D") # Macro processing would go in here ... macro = macro.replace("^", "\r") log.warn("macro: [{0}]".format(repr(macro))) self.game.queue_player.put(macro) log.warn("Restore ...") self.observer.load(self.save) self.save = None self.keepalive = None if self.defer: reactor.CallLater(0, self.defer.callback) self.defer = None # self.welcome_back() def activate_scripts_menu(self): self.observer.disconnect("player", self.player) self.observer.connect("player", self.scripts_player) self.scripts_menu() def option_entry(self, entry): if len(entry) > 0: # Ok, they gave us something self.game.gamedata.set_config(self.option_select, entry.strip()) else: self.queue_game.put("Edit aborted." + self.nl) self.config_menu() def option_input(self, option): if len(option) > 0: option = int(option) if option in self.config_opt: # Ok, it's a valid option! self.option_select = self.config_opt[option] ask = PlayerInput(self.game) if self.option_select == "Macro": d = ask.prompt("Change {0} to?".format(self.option_select), 48) else: d = ask.prompt("Change {0} to?".format(self.option_select), 18) d.addCallback(self.option_entry) # d.addErrback(self.config_menu) else: self.queue_game.put("Unknown option, sorry." + self.nl) self.config_menu() else: # Aborted self.config_menu() def config_player(self, chunk: bytes): """ Data from player (in bytes). """ chunk = chunk.decode("latin-1", "ignore") key = chunk.upper() if key == "C": self.queue_game.put(self.c + key + self.r + self.nl) self.game.gamedata.config = {} elif key == "E": self.queue_game.put(self.c + key + self.r + self.nl) ask = PlayerInput(self.game) d = ask.prompt( "Which to edit?", 4, name="option", abort_blank=True, digits=True ) d.addCallback(self.option_input) d.addErrback(self.config_menu) return elif key in ("1", "2", "3", "4", "5", "6", "7", "8", "9"): self.queue_game.put(self.c + key + self.r + self.nl) option = int(key) if option in self.config_opt: # Ok, it's a valid option! self.option_select = self.config_opt[option] ask = PlayerInput(self.game) if self.option_select == "Macro": d = ask.prompt("Change {0} to?".format(self.option_select), 48) else: d = ask.prompt("Change {0} to?".format(self.option_select), 18) d.addCallback(self.option_entry) # d.addErrback(self.config_menu) return else: self.queue_game.put("Unknown option, sorry." + self.nl) elif key == "X": self.queue_game.put(self.c + key + self.r + self.nl) self.deactivate_config_menu() return else: self.queue_game.put(self.c + "?" + self.r + self.nl) self.config_menu() def config_menu(self, *_): titlecolor = merge(Style.BRIGHT + Fore.CYAN + Back.BLUE) tc = merge(Style.BRIGHT + Fore.YELLOW + Back.BLUE) c1 = merge(Style.BRIGHT + Fore.WHITE + Back.BLUE) c2 = merge(Style.BRIGHT + Fore.CYAN + Back.BLUE) # box = Boxes(44, color=titlecolor) box = Boxes(44, color=tc) self.queue_game.put(box.top()) # self.queue_game.put(box.row(titlecolor + "{0:^44}".format("Configuration"))) self.queue_game.put(box.row(tc + "{0:^44}".format("Configuration"))) self.queue_game.put(box.middle()) def config_option(index, key, value): # Really, python? Because # builtins.ValueError: Precision not allowed in integer format specifier if type(value) == int: value = str(value) row = "{0}{1:2} {2:19}{3}{4:<20.20}".format(c1, index, key, c2, value) self.queue_game.put(box.row(row)) def menu_item(ch, desc): row = "{0} {1} {2}-{3} {4:39}".format(c1, ch, c2, c1, desc) # self.queue_game.put( # " " + c1 + ch + c2 + " - " + c1 + desc + self.nl # ) self.queue_game.put(box.row(row)) index = 1 self.config_opt = {} for k in sorted(self.game.gamedata.config.keys()): # for k, v in self.game.gamedata.config.items(): v = self.game.gamedata.config[k] self.config_opt[index] = k config_option(index, k, v) index += 1 self.queue_game.put(box.middle()) menu_item("C", "Clear Config") menu_item("E", "Edit Item") menu_item("X", "eXit") self.queue_game.put(box.bottom()) self.queue_game.put(" " + tc + "-=>" + self.r + " ") def deactivate_scripts_menu(self, *data): log.warn("deactivate_scripts_menu ({0})".format(data)) self.observer.disconnect("player", self.scripts_player) self.observer.connect("player", self.player) # Did they request exit? if len(data) > 0 and type(data[0]) == dict: info = data[0] if "exit" in info and info["exit"]: log.warn("exit proxy...") # Exit Proxy Code self.queue_game.put( self.nl + Boxes.alert("Proxy done.", base="green", style=3) ) self.observer.load(self.save) self.save = None # It isn't running (NOW), so don't try to stop it. # self.keepalive.stop() self.keepalive = None # Ok, this is a HORRIBLE idea, because the prompt might be # outdated. # self.queue_game.put(self.prompt) self.prompt = None # I'm not sure where we are, we might not be at a prompt. # let's check! if re.match(r"Command \[TL=.* \(\?=Help\)\? :", self.game.getPrompt()): # Send '\r' to re-display the prompt # instead of displaying the original one. self.game.queue_player.put("d") # Were we asked to do something when we were done here? if self.defer: reactor.CallLater(0, self.defer.callback) # self.defer.callback() self.defer = None return log.warn("calling welcome_back") self.welcome_back() def scripts_menu(self, *_): c1 = merge(Style.BRIGHT + Fore.CYAN) c2 = merge(Style.NORMAL + Fore.CYAN) box = Boxes(40, color=c1) self.queue_game.put(box.top()) self.queue_game.put(box.row(c1 + "{0:^40}".format("Scripts"))) self.queue_game.put(box.middle()) def menu_item(ch, desc): row = " {0}{1} {2}-{3} {4:35}".format(c1, ch, c2, c1, desc) # self.queue_game.put( # " " + c1 + ch + c2 + " - " + c1 + desc + self.nl # ) self.queue_game.put(box.row(row)) menu_item("1", "Ports (Trades between two sectors)") menu_item("!", "Terrorize Ports/Trades") menu_item("2", "Explore (Strange new sectors)") menu_item("3", "Space... the broken script...") menu_item("4", "Upgrade Planet") # menu_item("5", "Colonize Planet") menu_item("5", "Colonize Planet v2.0") menu_item("%", "Maxumize Fighter Production") menu_item("6", "Trade Evil (Does SSM)") menu_item("X", "eXit") self.queue_game.put(box.bottom()) self.queue_game.put(" " + c1 + "-=>" + self.r + " ") def terror(self, *_): log.debug("terror {0}".format(_)) loops = _[0] if loops.strip() == "": self.deactivate_scripts_menu() else: # Ok, we have something here, I think... terror = ScriptTerror(self.game, self, int(loops)) d = terror.whenDone() d.addCallback(self.deactivate_scripts_menu) d.addErrback(self.deactivate_scripts_menu) def scripts_player(self, chunk: bytes): """ Data from player (in bytes). """ chunk = chunk.decode("latin-1", "ignore") key = chunk.upper() if key == "1": self.queue_game.put(self.c + key + self.r + self.nl) # Activate this magical event here ports = ScriptPort(self.game) d = ports.whenDone() # d.addCallback(self.scripts_menu) # d.addErrback(self.scripts_menu) d.addCallback(self.deactivate_scripts_menu) d.addErrback(self.deactivate_scripts_menu) return elif key == "!": self.queue_game.put(self.c + key + self.r + self.nl) ask = PlayerInput(self.game) # This is TERROR, so do something! ask.color(merge(Style.BRIGHT + Fore.WHITE + Back.RED)) ask.colorp(merge(Style.BRIGHT + Fore.YELLOW + Back.RED)) d = ask.prompt( "How many loops of terror?", 4, name="loops", digits=True, abort_blank=True, ) d.addCallback(self.terror, ask) d.addErrback(self.deactivate_scripts_menu) return elif key == "2": self.queue_game.put(self.c + key + self.r + self.nl) explore = ScriptExplore(self.game) d = explore.whenDone() d.addCallback(self.deactivate_scripts_menu) d.addErrback(self.deactivate_scripts_menu) return elif key == "3": self.queue_game.put(self.c + key + self.r + self.nl) space = ScriptSpace(self.game) d = space.whenDone() d.addCallback(self.deactivate_scripts_menu) d.addErrback(self.deactivate_scripts_menu) return elif key == "4": self.queue_game.put(self.c + key + self.r + self.nl) upgrade = PlanetUpScript(self.game) d = upgrade.whenDone() d.addCallback(self.deactivate_scripts_menu) d.addErrback(self.deactivate_scripts_menu) return elif key == "5": self.queue_game.put(self.c + key + self.r + self.nl) # colo = ColoScript(self.game) colo = ColoScript2(self.game) d = colo.whenDone() d.addCallback(self.deactivate_scripts_menu) d.addErrback(self.deactivate_scripts_menu) return elif key == "%": self.queue_game.put(self.c + key + self.r + self.nl) maxfigs = MaxFighterMake(self.game) d = maxfigs.whenDone() d.addCallback(self.deactivate_scripts_menu) d.addErrback(self.deactivate_scripts_menu) return elif key == "6": self.queue_game.put(self.c + key + self.r + self.nl) evil = evilTrade(self.game) d = evil.whenDone() d.addCallback(self.deactivate_scripts_menu) d.addErrback(self.deactivate_scripts_menu) return elif key == "X": self.queue_game.put(self.c + key + self.r + self.nl) self.deactivate_scripts_menu() return else: self.queue_game.put(self.c + "?" + self.r + self.nl) self.scripts_menu() def welcome_back(self, *_): log.debug("welcome_back") self.keepalive.start(30, True) self.menu()