| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169 | #include "director.h"#include <boost/format.hpp>#include <cctype>#include "boxes.h"#include "galaxy.h"#include "logging.h"#include "scripts.h"#include "utils.h"Director::Director() {  BUGZ_LOG(warning) << "Director::Director()";  // active = false;  game = 0; // not in a game  galaxy.reset();  // do everything proxy_deactivate does ...  // proxy_deactivate();  active = false;  // reset everything back to good state  talk_direct = true;  show_client = true;  count = 0;  /*  Setup StringFunc for SL_parser:  Construct these once, rather then every single time we need them.  */  SF_cimline = [this](const std::string &s) { this->SL_cimline(s); };  SF_sectorline = [this](const std::string &s) { this->SL_sectorline(s); };  SF_portline = [this](const std::string &s) { this->SL_portline(s); };  SF_warpline = [this](const std::string &s) { this->SL_warpline(s); };  SF_infoline = [this](const std::string &s) { this->SL_infoline(s); };  SF_densityline = [this](const std::string &s) { this->SL_densityline(s); };  SF_computer_portline = [this](const std::string &s) {    this->SL_computer_portline(s);  };  build_menu();}Director::~Director() { BUGZ_LOG(warning) << "Director::~Director()"; }void Director::client_input(const std::string &input) {  // If we're already active, don't try to activate.  if (chain) {    chain->client_input(input);    return;  }  if (active) {    if (input == "Q" || input == "q")      proxy_deactivate();    return;  } else if (input == "\x1b" || input == "~") {    std::string &prompt = current_prompt;    BUGZ_LOG(trace) << "ACTIVATE prompt shows: [" << prompt << "]";    if (prompt == "Selection (? for menu): ") {      to_client("\n\rThere's not much we can do here.  Activate in-game at a "                "Command prompt.\n\r");      to_client(current_raw_prompt);      return;    }    // easter-eggs:    if (prompt == "Enter your choice: ") {      to_client("\n\r\x1b[1;36mI'd choose \x1b[1;37m`T`\x1b[1;36m, but "                "that's how I was coded.\n\r");      to_client(current_raw_prompt);      return;    }    // easter-egg    if (prompt == "[Pause]") {      to_client(" \x1b[1;36mPAWS\x1b[0m\n\r");      to_client(current_raw_prompt);      return;    }    if (at_planet_prompt(prompt)) {      // future:  If activated at planet menu, activate the planet upgrade      // script!      to_client("\n\r\x1b[0mFUTURE:  Activate the planet upgrade script.\n\r");      to_client(current_raw_prompt);      return;    }    //    // The command prompt that we're looking for:    //    // "Command [TL=00:00:00]:[242] (?=Help)? : "    // the time, and the sector number vary...    /*        if (startswith(prompt, "Command [")) {          // if (prompt.substr(0, 9) == "Command [") {          //  int len = prompt.length();          if (endswith(prompt, "] (?=Help)? : ")) {            // if (prompt.substr(len - 14) == "] (?=Help)? : ") {            proxy_activate();            return;          }        }      }    */    if (at_command_prompt(prompt)) {      proxy_activate();      return;    }  }  // Ok...  if (talk_direct)    to_server(input);  /*  if (emit_client_input)    emit_client_input(line);  */}void Director::server_line(const std::string &line,                           const std::string &raw_line) {  // check for if we entered game/left game  if (line.find("TradeWars Game Server   ") != std::string::npos) {    to_client("\rTradeWars Proxy v2++ READY (~ or ESC to activate)\n\r");    /*    There's a delay here when I save the game data.    I've moved it futher down.  Hide it at a prompt, so it isn't so    noticeable.     */    // reset "active game" -- we're at the TWGS main menu  }  if (line.find("Selection (? for menu): ") != std::string::npos) {    char ch = line[line.length() - 1];    if (ch >= 'A' && ch < 'Q') {      if ((game) && (game != ch)) {        galaxy.save();      }      game = ch;      BUGZ_LOG(warning) << "GAME " << game << " activated!";      // TODO:  Load game data      galaxy.game = game;      galaxy.username = username;      galaxy.load();      // YAML loaded, set sensible default config values (if missing).      if (!galaxy.config["display_lines"]) {        galaxy.config["display_lines"] = 20;      }      galaxy.meta["help"]["display_lines"] =          "Number of report lines to display";      if (!galaxy.config["burnt_percent"]) {        galaxy.config["burnt_percent"] = 40;      }      galaxy.meta["help"]["burnt_percent"] = "Ignore ports below this percent";    }    // not needed (handled by above Game Server check).    if (ch == 'Q') {      if (game) {        // TODO:  Save galaxy data        galaxy.save();      }      game = 0;      galaxy.reset();    }  }  if (game) {    // in-game parsing here.    /*      ____                             _     _     / ___|  ___ _ ____   _____ _ __  | |   (_)_ __   ___     \___ \ / _ \ '__\ \ / / _ \ '__| | |   | | '_ \ / _ \      ___) |  __/ |   \ V /  __/ |    | |___| | | | |  __/     |____/ \___|_|    \_/ \___|_|    |_____|_|_| |_|\___|      ____                _     |  _ \ __ _ _ __ ___(_)_ __   __ _     | |_) / _` | '__/ __| | '_ \ / _` |     |  __/ (_| | |  \__ \ | | | | (_| |     |_|   \__,_|_|  |___/_|_| |_|\__, |                                  |___/   This is where all of the server lines are gleaned for all the   information that we can get out of them.    // When activating the computer    SP: [Command [TL=00:00:00]:[926] (?=Help)? : ]    Sector: 926    CI: [c]    SL: [Command [TL=00:00:00]:[926] (?=Help)? : C]    SL: [<Computer>]    SL: []    SL: [<Computer activated>]    SL: []    SP: [Computer command [TL=00:00:00]:[926] (?=Help)? ]    // deactivating the computer    SL: [Computer command [TL=00:00:00]:[926] (?=Help)? Q]    SL: []    SL: [<Computer deactivated>]    SL: []    */    if (line == "<Port>") {      SL_parser = SF_portline;    }    /*    if (startswith(line, " Items     Status  Trading % of max OnBoard"))      SL_parser = SF_portline;    */    if (endswith(line, "Relative Density Scan")) {      galaxy.dscan.reset(current_sector);      SL_parser = SF_densityline;    }    if (startswith(line, "Sector  : ")) SL_parser = SF_sectorline;    if (line == ": ") SL_parser = SF_cimline;    if (line == "<Info>") SL_parser = SF_infoline;    if (startswith(line, "What sector is the port in? [")) {      // Computer Port Report      // SL: [What sector is the port in? [611] 4]      size_t pos = line.rfind(' ');      computer_port_sector = stoi(line.substr(pos));      SL_parser = SF_computer_portline;    }  }  if (SL_parser) {    SL_parser(line);  }  /*  if (emit_server_line) {    emit_server_line(line);  }   */  if (chain) {    chain->server_line(line, raw_line);  }}void Director::server_prompt(const std::string &prompt,                             const std::string &raw_prompt) {  current_prompt = prompt;  current_raw_prompt = raw_prompt;  if (game) {    if (prompt == "Selection (? for menu): ") {      galaxy.save();      game = 0;      galaxy.reset();    }    // in-game parsing here.    if (startswith(prompt, "Command [") && endswith(prompt, "] (?=Help)? : ")) {      std::string sector_text;      size_t before, after;      before = prompt.find("]:[") + 3;      after = prompt.find("] (?=Help)");      sector_text = prompt.substr(before, after - before);      current_sector = stoi(sector_text);      BUGZ_LOG(fatal) << "Sector: " << sector_text;    }  }  /*  if (emit_server_prompt)    emit_server_prompt(prompt);   */  if (chain)    chain->server_prompt(prompt);}void Director::build_menu(void) {  main_menu = std::make_shared<MenuDispatch>(*this);  MenuDispatch *md = static_cast<MenuDispatch *>(&(*main_menu));  md->menu_box_color = "\x1b[1;33;44m";  md->menu_text_color = "\x1b[1;37;44m";  md->menu_title = "Proxy Menu";  md->menu_options_color = "\x1b[1;36;40m";  md->menu_prompt = "\x1b[0;31;40m\xdb\xb2\xb1\xb0 \x1b[31;40mRED "                    "\x1b[32;40mGREEN\x1b[30;42m\xdb\xb2\xb1\xb0 \x1b[0m : ";  md->lazy = true;  md->menu = {{"C", "Configure"},              {"D", "Display Report"},              {"E", "Export Data/Save"},              {"I", "Information"},              {"P", "Port CIM"},              {"W", "Warp CIM"},              {"T", "Trading Report (same as D)"},              {"S", "Scripts"},              {"X", "eXit"}};  md->setNotify([this]() { this->menu_choice(); });  cim = std::make_shared<CIMDispatch>(*this);  cim->setNotify([this]() { this->cim_done(); });}void Director::proxy_activate(void) {  active = true;  // yes, set keep-alive timer.  to_server(" "); // start keep-alive timer.  // set other values we need  talk_direct = false;  show_client = false;  /*  Wait a minute .. this might be confusing.  Shouldn't I send them the current prompt?  Just in case we abort in the middle of something?!?  */  old_prompt = current_prompt;  old_raw_prompt = current_raw_prompt;  to_client("\x1b[0m\n\r");  /*  ╔══════════════════════════════╗  ║    TradeWars Proxy Active    ║  ╚══════════════════════════════╝     -=>  */  Boxes box(30, 1, true);  box.boxcolor = "\x1b[1;33;44m";  box.textcolor = "\x1b[1;33;44m";  to_client(box.top());  std::string output = "    TradeWars Proxy \x1b[5mActive\x1b[0;1;33;44m    ";  to_client(box.row(output));  to_client(box.bottom());  chain = main_menu;  main_menu->activate();  /*    // Using InputDispatch  -- and see have_input    std::shared_ptr<Dispatch> readline = std::make_shared<InputDispatch>(*this);    chain = readline;    InputDispatch *id = static_cast<InputDispatch *>(&(*readline));    id->prompt = "\x1b[0m\x1b[1;33;44m-=>\x1b[0m \x1b[1;37;44m";    id->max_length = 15;    id->setNotify([this]() { this->have_input(); });    readline->activate();  */}void Director::menu_choice(void) {  MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));  if (md) {    if (md->input.empty()) {      to_client("Menu aborted.\n\r");      proxy_deactivate();      return;    } else {      switch (md->input[0]) {      case 'C': // configure        config_edit();        return;        break;      case 'D':      case 'T': // display trading report      {        auto pptv = galaxy.find_best_trades();        std::string output;        galaxy.sort_port_pair_type(pptv);        int max_display = 20;        if (galaxy.config["display_lines"])          max_display = galaxy.config["display_lines"].as<int>();        else          galaxy.config["display_lines"] = max_display;        if ((max_display <= 0) || (max_display > 255)) {          max_display = 255;          galaxy.config["display_lines"] = 255;        }        const int per_line = 5;        int count = 0;        int line = 0;        for (auto const &ppt : pptv) {          output = str(boost::format("%1$5d:%2$-5d(%3$d) ") % ppt.s1 % ppt.s2 %                       ppt.type);          to_client(output);          ++count;          if (count == per_line) {            count = 0;            to_client("\n\r");            ++line;          }          if (line == max_display)            break;        }        if (count != 0)          to_client("\n\r");        // We got < 5 lines, and max_display is > 5.  Offer suggestion:        if ((line < 5) && (max_display > 5)) {          // suggestion:          to_client(              "HINT: For more lines, try reducing the burnt_percent?\n\r");        }      } break;      case 'E': // Export Data/Save        to_client("Saving...");        galaxy.save();        to_client("\rSaved....\n\r");        break;      case 'I': // Information        information();        break;      case 'P': // Port CIM        // Since we're adding/updating, we don't lose our        // type 0 ports.  Type 9 stays at 9.        chain = cim;        to_server("^RQ");        {          std::string text = str(boost::format("Port CIM Report (%1%)\n\r") %                                 galaxy.ports.size());          // to_client("Port CIM Report\n\r");          to_client(text);        }        chain->activate();        return;        break;      case 'W': // Warp CIM        chain = cim;        to_server("^IQ");        {          std::string text = str(boost::format("Warp CIM Report (%1%)\n\r") %                                 galaxy.warps.size());          // to_client("Warp CIM Report\n\r");          to_client(text);        }        chain->activate();        return;        break;      // case 'T':  // Trading Report      //  break;      case 'S': // Scripts      {        init_scripts_menu();        chain = scripts_menu;        chain->activate();        return;      } break;      case 'X': // Exit        proxy_deactivate();        return;      }      /*      std::string text = str(          boost::format("Back from Menu [%1%] was selected.\n\r") %      md->input);      to_client(text);      */      md->activate();    }  }}MenuDispatch *Director::init_scripts_menu(void) {  MenuDispatch *md;  if (scripts_menu) {    md = dynamic_cast<MenuDispatch *>(&(*scripts_menu));    return md;  } else {    scripts_menu = std::make_shared<MenuDispatch>(*this);    md = static_cast<MenuDispatch *>(&(*scripts_menu));    md->menu_box_color = "\x1b[0;32;40m";    md->menu_text_color = "\x1b[1;32;40m";    md->menu_title = "Scripts Menu";    md->menu_options_color = "\x1b[1;32;40m";    md->lazy = false;    md->menu_prompt = " SCRIPT : ";    md->menu = {{"!", "Terror"},                {"T", "Trade"},                {"S", "Safe Move"},                {"C", "Closest Trade"},                {"U", "Upgrade Planet Pants"}};    md->setNotify([this]() { this->scripts_done(); });    return md;  }}void Director::scripts_done(void) {  // Was script selected?  If so, run it!  // otherwise, back to the menu we go...  MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*scripts_menu));  if (md) {    if (md->input.empty()) {      to_client("Scripts aborted.\n\r");      scripts_menu.reset();      proxy_deactivate();      return;    } else {      switch (md->input[0]) {        case 'T':  // Trade        {          script = std::make_shared<TraderDispatch>(*this);          TraderDispatch *ts = static_cast<TraderDispatch *>(&((*script)));          ts->setNotify([this]() { this->proxy_deactivate(); });          // Locate best trades          auto found = galaxy.find_trades(current_sector, false);          if (found.empty()) {            to_client(                "No Trades found.  Port burnt (CONFIG: lower burnt_percent?) "                "or no ports around.\n\r");            proxy_deactivate();            return;          }          // sort first?          galaxy.sort_port_pair_type(found);          BUGZ_LOG(fatal) << "Found " << found.size() << " possible trade(s).";          BUGZ_LOG(fatal) << "Best: " << found[0].s1 << "," << found[0].s2                          << " : " << found[0].type;          // Set parameters          ts->port[0] = found[0].s1;          ts->port[1] = found[0].s2;          ts->type = found[0].type;          ts->trades = found[0].trades;          chain = script;          chain->activate();          return;        } break;        case 'S': {          script = std::make_shared<MoveDispatch>(*this);          MoveDispatch *md = static_cast<MoveDispatch *>(&((*script)));          md->setNotify([this]() { this->proxy_deactivate(); });          md->move_to = 1;          chain = script;          chain->activate();          return;        } break;        case '!': {          script = std::make_shared<ScriptTerror>(*this);          ScriptTerror *st = static_cast<ScriptTerror *>(&(*script));          st->setNotify([this]() {            script.reset();            this->proxy_deactivate();          });          chain = script;          chain->activate();          return;        } break;        // }        case 'C': {          auto best = galaxy.find_closest(current_sector);          if (best.type != 0) {            std::string text =                str(boost::format("Best/Closest: %1% with %2% & %3%\n\r") %                    best.type % best.s1 % best.s2);            to_client(text);          } else {            to_client("I don't see any best trades.\n\r");          }        } break;      }    }  }  proxy_activate();  // And to end scripts, we do .. what exactly?  // DEBUG:  Ok, why does everything work OK if I reset the scripts_menu  // here?? probably do want to destroy scripts here.  ;)  // scripts_menu.reset();  // proxy_deactivate();}/** * @brief Setup Config Input * * @return DispatchInput* */InputDispatch *Director::init_config_input(void) {  InputDispatch *id;  if (config_input) {    // Yes, it has been setup before.    id = dynamic_cast<InputDispatch *>(&(*config_input));    id->prompt = "Config => ";    id->max_length = 3;    config_item.clear();    return id;  } else {    // set it up    config_input = std::make_shared<InputDispatch>(*this);    id = static_cast<InputDispatch *>(&(*config_input));    id->prompt = "Config => ";    id->max_length = 3;    id->setNotify([this]() { this->config_have_input(); });    config_item.clear();    return id;  }}void Director::config_edit(void) {  // display current config  std::string menu_box_color = "\x1b[1;33;44m";  std::string menu_text_color = "\x1b[1;37;44m";  auto abox = Boxes::alert("   Configuration:   ", menu_box_color,                           menu_text_color, 20, 1, true);  for (auto line : abox) {    to_client(line);  }  // to_client("Configuration:\n\r");  int item = 1;  for (auto const &cfg : galaxy.config) {    std::string output = str(boost::format("%1$2d %2$20s: %3$s\n\r") % item %                             cfg.first % cfg.second);    to_client(output);    ++item;  }  to_client("Enter number to edit, blank to exit.\n\r");  // setup call to config_input:  InputDispatch *id = init_config_input();  chain = config_input;  id->activate();  // to return to the menu:  // MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));  // md->activate();}void Director::config_have_input(void) {  InputDispatch *id = dynamic_cast<InputDispatch *>(&(*config_input));  if (config_item.empty()) {    // This is a config menu selection    if (id->input.empty()) {      // We're done here.  Return to menu.      chain = main_menu;      MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));      md->activate();      // destroy the input?  yes.      config_input.reset();      return;    } else {      int item;      try {        item = stoi(id->input);      } catch (const std::invalid_argument &e) {        BUGZ_LOG(fatal) << e.what();        item = 0;      } catch (const std::out_of_range &e) {        BUGZ_LOG(fatal) << e.what();        item = 0;      }      if ((item < 1) || (item > (int)galaxy.config.size())) {        // selection out of range - redisplay config menu        config_edit();        return;      } else {        int pos = 1;        const YAML::Node &config = galaxy.config;        for (auto const &c : config) {          if (pos == item) {            // got it!            config_item = c.first.as<std::string>();            std::string output =                str(boost::format("%1% : %2%\n\r") % config_item %                    galaxy.meta["help"][config_item]);            to_client(output);            id->max_length = 30;            id->prompt = "Change to => ";            id->activate();            return;          };          ++pos;        }        to_client("What?  I didn't find that item?\n\r");        config_edit();        return;      }    }  } else {    // This is a config item edit    if (id->input.empty()) {      to_client("No change.\n\r");      config_edit();      return;    } else {      BUGZ_LOG(fatal) << "Config EDIT: " << config_item << " = " << id->input;      galaxy.config[config_item] = id->input;      config_edit();      return;    }  }}void Director::have_input(void) {  ++count;  InputDispatch *id = dynamic_cast<InputDispatch *>(&(*chain));  if (id) {    std::string output =        str(boost::format("Your Input (%2%): [%1%]\n\r") % id->input % count);    to_client("\x1b[0m");    to_client(output);  } else {    proxy_deactivate();    return;  }  if (count > 3) {    proxy_deactivate();  } else {    chain->activate();  }}void Director::cim_done(void) {  BUGZ_LOG(info) << "CIM done";  chain = main_menu;  main_menu->activate();}void Director::information(void) {  std::string output;  to_client("I currently know the following:\n\r");  output = str(      boost::format("Ports: %1%, Sectors: %2%, Config: %3%, Meta: %4%\n\r") %      galaxy.ports.size() % galaxy.warps.size() % galaxy.config.size() %      galaxy.meta.size());  to_client(output);}void Director::proxy_deactivate(void) {  active = false;  // reset everything back to good state  talk_direct = true;  show_client = true;  chain.reset();  to_client("\n\r");  to_client(current_raw_prompt);}/*Server Line Parsing Routines */void Director::SL_cimline(const std::string &line) {  if (line == ": ENDINTERROG") {    SL_parser = nullptr;    return;  }  if (line == ": ") {    // do I need to do anything special here for this?    // Maybe -- We would save special ports that don't show up    // (StarDock/Special) before. We don't know (at this point) if this is    // warps or ports.    return;  }  if (line.empty()) {    SL_parser = nullptr;    return;  }  // parse cimline  // size_t pos = line.find('%');  // std::string work = line;  // if (pos == line.npos) {  if (in(line, "%")) {    // portcim    struct port p = parse_portcim(line);    if (p.sector == 0)      BUGZ_LOG(fatal) << "portcim:  FAIL [" << line << "]";    else      BUGZ_LOG(trace) << "portcim: " << p;    galaxy.add_port(p);  } else {    // warpcim    // BUGZ_LOG(fatal) << "warpcim: [" << line << "]";    auto warps = split(line);    sector_warps sw;    for (auto const &w : warps) {      if (sw.sector == 0) {        sw.sector = stoi(w);      } else {        sw.add(stoi(w));      }    }    BUGZ_LOG(trace) << "warpcim: " << sw;    galaxy.add_warp(sw);  }}void Director::SL_thiefline(const std::string &line) {  size_t pos = line.find("Suddenly you're Busted!");  bool busted = pos != line.npos;  if (busted) {    BUGZ_LOG(fatal) << "set bust";    SL_parser = nullptr;  } else {    pos = line.find("(You realize the guards saw you last time!)");    if (pos != line.npos)      SL_parser = nullptr;  }  // Are those the two ways to exit from this state?}void Director::SL_sectorline(const std::string &line) {  BUGZ_LOG(fatal) << "sectorline: [" << line << "]";  if (line.empty()) {    SL_parser = nullptr;  } else {    /*    sectorline: [Sector  : 926 in The Federation.]    sectorline: [Beacon  : FedSpace, FedLaw Enforced]    sectorline: [Ports   : Stargate Alpha I, Class 9 (Special) (StarDock)]    sectorline: [Traders : Civilian phil, w/ 30 ftrs,]    sectorline: [           in Star Stomper (Sverdlov Merchant Cruiser)]    sectorline: [Warps to Sector(s) :  70 - 441 - 575 - 600 - 629 - 711]    sectorline: [Warps to Sector(s) :  70 - (475) - 569]    What can we get from Traders : line?  Can we get if they are hostile    to us?  We need to respond to "is powering up weapons" ... We can    react faster then a person can!    "phil is powering up weapons systems!"    Also the auto-attack ones Ferrengi -- we need to auto-respond Retreat.    */    if (in(line, "Sector  :")) {      current_sector = stoi(line.substr(10));      BUGZ_LOG(warning) << "SECTOR: " << current_sector;    }    if (in(line, "Ports   :")) {      std::string port_class;      size_t pos = line.find(", Class ");      if (pos != std::string::npos) {        pos += 8;        int class_ = stoi(line.substr(pos));        BUGZ_LOG(fatal) << "PORT: " << class_;        galaxy.add_port(current_sector, class_);      }    }    if (in(line, "Warps to Sector(s) :")) {      std::string temp = line.substr(20);      replace(temp, " - ", " ");      // unexplored sectors ()      // Should I track these?      replace(temp, "(", "");      replace(temp, ")", "");      sector_warps sw;      auto warps = split(temp);      sw.sector = current_sector;      // what if there is only one warp?      for (auto const &w : warps) {        sw.add(stoi(w));      }      BUGZ_LOG(fatal) << "WARPS: " << sw;      galaxy.add_warp(sw);    }  }}void Director::SL_densityline(const std::string &line) {  BUGZ_LOG(fatal) << "densityline: [" << line << "]";  if (line.empty()) {    SL_parser = nullptr;    return;  }  /*  // Ensure this really is a density scan and not something else  if (!in(line, "Sector") || !in(line, "Warps") || !in(line, "NavHaz") ||      !in(line, "Anom")) {    BUGZ_LOG(fatal) << "densityline: Invalid line.";    SL_parser = nullptr;    return;  }  if (not galaxy.meta["density"]) {    galaxy.meta["density"] = YAML::Node();  }  */  /*   0         1   2                3  4     5 6    7      8     9     10   11 12  "Sector    55  ==>              0  Warps : 4    NavHaz :     0%    Anom : No"  "Sector ( 223) ==>              0  Warps : 3    NavHaz :     0%    Anom : No"  */  if (in(line, "==>")) {    std::string work = line;    replace(work, ":", "");    bool known = !in(work, "(");    replace(work, "(", "");    replace(work, ")", "");    replace(work, "%", "");    auto dense = split(work);    // Parse our data    sector_type sector = std::stoi(dense.at(1));    uint16_t density = std::stoi(dense.at(3));    uint16_t warps = std::stoi(dense.at(5));    uint16_t navhaz = std::stoi(dense.at(7));    bool anom = in(dense.at(9), "Yes");    struct density d = {sector, density, warps, navhaz, anom, known};    galaxy.dscan.add_scan(d);    // Commit data    BUGZ_LOG(warning) << "densityline: {sector=" << sector                      << " density=" << density << " warps=" << warps                      << " navhaz=" << navhaz << " anom=" << anom                      << " known=" << known << "}";    /*    if (galaxy.meta["density"][sector]) {      galaxy.meta["density"][sector] = YAML::Node();    }    */    galaxy.meta["density"][sector]["density"] = density;    galaxy.meta["density"][sector]["warps"] = warps;    galaxy.meta["density"][sector]["navhaz"] = navhaz;    galaxy.meta["density"][sector]["anom"] = anom;    galaxy.meta["density"][sector]["known"] = known;    // Add a check to see if density is greater than 500    // Add datetime stamp  }}void Director::SL_portline(const std::string &line) {  /*  We take blank lines because we start at <Port>.  Otherwise, we trigger on computer port requests.  if (line.empty()) {    SL_parser = nullptr;    return;  }  */  /*  SL: [ Items     Status  Trading % of max OnBoard]  SL: [ -----     ------  ------- -------- -------]  SL: [Fuel Ore   Buying    3000    100%       0]  SL: [Organics   Buying    3000    100%       0]  SL: [Equipment  Buying    3000    100%       0]  SL: []  SL: [Commerce report for: 03:51:56 PM Mon Oct 24, 2033     You can buy:]  SL: [A  Cargo holds     :    650 credits / next hold                0]  SL: [B  Fighters        :    233 credits per fighter               75]  SL: [C  Shield Points   :    116 credits per point                100]  SL: []   */  // BUGZ_LOG(info) << "portline : " << line;  if (in(line, "%")) {    // size_t pos = line.find('%');    // if (pos != line.npos) {    // Ok, this is a valid portline    std::string work = line;    replace(work, "Fuel Ore", "Fuel");    auto parts = split(work);    if (parts[0] == "Items")      return;    char c = tolower(parts[0][0]);    int pos;    char foe[4] = "foe";    for (pos = 0; pos < 3; ++pos) {      if (c == foe[pos])        break;    }    int amount = stoi(parts[2]);    int percent = stoi(parts[3]);    // update port    auto port = galaxy.ports.find(current_sector);    if (port != galaxy.ports.end()) {      port->second.amount[pos] = amount;      port->second.percent[pos] = percent;    }    /*    BUGZ_LOG(fatal) << "portline split:";    for (auto const p : parts) {      BUGZ_LOG(fatal) << p;    }    */    // Here's the end:    if (parts[0] == "Equipment")      SL_parser = nullptr;    // BUGZ_LOG(fatal) << "portline split : [" << parts << "]";  }}void Director::SL_warpline(const std::string &line) {  if (line.empty()) {    SL_parser = nullptr;    return;  }  // process warp line  BUGZ_LOG(fatal) << "warpline: [" << line << "]";}void Director::SL_infoline(const std::string &line) {  static int state;  if (line == "<Info>") {    state = 0;    galaxy.meta["info"] = YAML::Node();  }  if (line.empty()) {    ++state;    if (state == 2) {      SL_parser = nullptr;      // clear out the existing ship data      galaxy.meta["ship"] = YAML::Node();      // process the parsed information in meta["info"]      if (galaxy.meta["info"]["Total Holds"]) {        std::string work = galaxy.meta["info"]["Total Holds"].as<std::string>();        replace(work, "Fuel Ore", "Fuel");        auto parts = split(work, " - ");        int total_holds = stoi(parts[0]);        BUGZ_LOG(fatal) << "total holds: " << total_holds;        auto contents = split(parts[1]);        for (auto const &hold : contents) {          auto hold_amount = split(hold, "=");          BUGZ_LOG(fatal) << hold_amount[0] << " with " << hold_amount[1];          std::string key = hold_amount[0];          str_tolower(key);          // equipment = e          // organics = o          // fuel = f          // colonists = c          // empty = empty          if (key != "empty") {            key = key[0];          }          galaxy.meta["ship"]["holds"][key] = stoi(hold_amount[1]);        }        galaxy.meta["ship"]["holds"]["total"] = total_holds;      }      if (galaxy.meta["info"]["Turns to Warp"]) {        int warp_turns = galaxy.meta["info"]["Turns to Warp"].as<int>();        BUGZ_LOG(fatal) << "Turns to Warp: " << warp_turns;        galaxy.meta["ship"]["warp_turns"] = warp_turns;      }      if (galaxy.meta["info"]["LongRange Scan"]) {        std::string scanner_text =            galaxy.meta["info"]["LongRange Scan"].as<std::string>();        char scanner = scanner_text[0];        BUGZ_LOG(fatal) << "Scanner: " << scanner;        galaxy.meta["ship"]["scanner"] = scanner;      }      // turns isn't ship specific      if (galaxy.meta["info"]["Turns left"]) {        // OR this could be "Unlimited" !!!        std::string text = galaxy.meta["info"]["Turns left"].as<std::string>();        if (text == "Unlimited") {          galaxy.meta["turns"] = -1;        } else {          int turns =              stoi(text); // galaxy.meta["info"]["Turns left"].as<int>();          BUGZ_LOG(fatal) << "Turns left: " << turns;          galaxy.meta["turns"] = turns;        }      }      if (galaxy.meta["info"]["Current Sector"]) {        int sector = galaxy.meta["info"]["Current Sector"].as<int>();        BUGZ_LOG(fatal) << "Sector: " << sector;        // it should already be sector ...        current_sector = sector;      }      if (galaxy.meta["info"]["Credits"]) {        std::string credit_text =            galaxy.meta["info"]["Credits"].as<std::string>();        replace(credit_text, ",", "");        int credits = stoi(credit_text);        galaxy.meta["credits"] = credits;        BUGZ_LOG(fatal) << "Credits: " << credits;      }    }    return;  }  // info to parse:  size_t pos = line.find(" : ");  if ((!endswith(line, " : ")) && (pos != line.npos)) {    std::string key = line.substr(0, pos);    // Ferrengi ships don't have date built    std::string value = line.substr(pos + 3);    trim(key);    trim(value);    galaxy.meta["info"][key] = value;    BUGZ_LOG(fatal) << "Info: " << key << " : " << value;  }}void Director::SL_computer_portline(const std::string &line) {  if (startswith(line, "What sector is the port in?"))    computer_port_done = false;  if (line == "I have no information about a port in that sector.") {    computer_port_sector = 0;    SL_parser = nullptr;    return;  }  if (!computer_port_done) {    if (in(line, "Fuel Ore")) computer_port_done = true;    if (in(line, "Cargo holds")) {      // If I want to scan the class type 0 ports:      // computer_port_done = true;      // otherwise:      SL_parser = nullptr;      return;    }  }  if (computer_port_done) {    if (line.empty()) {      SL_parser = nullptr;      return;    }    // scan for items of interest    // SL: [Fuel Ore   Buying     810    100%       0]    // SL: [Organics   Buying     856     57%       0]    // SL: [Equipment  Selling    922     44%       0]    std::string work = line;    replace(work, "Fuel Ore", "Fuel");    replace(work, "%", "");    auto parts = split(work);    char c = tolower(parts[0][0]);    int pos;    char foe[4] = "foe";    for (pos = 0; pos < 3; ++pos) {      if (c == foe[pos]) break;    }    int amount = stoi(parts[2]);    int percent = stoi(parts[3]);    // update port    auto port = galaxy.ports.find(computer_port_sector);    if (port != galaxy.ports.end()) {      BUGZ_LOG(info) << "COM PORT " << computer_port_sector << " " << c << " "                     << amount << " " << percent;      port->second.amount[pos] = amount;      port->second.percent[pos] = percent;    }  }}
 |