123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218 |
- #include "dispatchers.h"
- #include <boost/format.hpp>
- #include <cctype>
- #include "boxes.h"
- #include "logging.h"
- #include "utils.h"
- Dispatch::Dispatch(Director &d) : director{d} { aborted = false; };
- Dispatch::~Dispatch(){};
- void Dispatch::to_server(const std::string &send) { director.to_server(send); }
- void Dispatch::to_client(const std::string &send) { director.to_client(send); }
- const std::string &Dispatch::get_prompt(void) {
- return director.current_prompt;
- }
- void Dispatch::setNotify(notifyFunc nf) { notify_ = nf; }
- void Dispatch::notify(void) {
- if (director.post) {
- director.post(notify_);
- }
- }
- void Dispatch::chain_client_input(const std::string &input) {
- if (director.chain) {
- director.chain->chain_client_input(input);
- } else {
- client_input(input);
- }
- }
- void Dispatch::chain_server_line(const std::string &line,
- const std::string &raw_line) {
- if (director.chain) {
- director.chain->chain_server_line(line, raw_line);
- } else {
- server_line(line, raw_line);
- }
- }
- void Dispatch::chain_server_prompt(const std::string &prompt) {
- if (director.chain) {
- director.chain->chain_server_prompt(prompt);
- } else {
- server_prompt(prompt);
- }
- }
- void Dispatch::server_line(const std::string &line,
- const std::string &raw_line) {}
- void Dispatch::server_prompt(const std::string &prompt) {}
- void Dispatch::client_input(const std::string &input) {
- aborted = true;
- deactivate();
- }
- InputDispatch::InputDispatch(Director &d) : Dispatch(d) {
- BUGZ_LOG(warning) << "InputDispatch()";
- numeric = false;
- }
- InputDispatch::~InputDispatch() { BUGZ_LOG(warning) << "~InputDispatch()"; }
- void InputDispatch::activate(void) {
- BUGZ_LOG(warning) << "InputDispatch::activate() " << max_length;
- input.clear();
- to_client(prompt);
- }
- void InputDispatch::deactivate(void) { notify(); }
- void InputDispatch::server_line(const std::string &line,
- const std::string &raw_line) {
- if (line.empty()) return;
- std::string temp = repr(raw_line);
- BUGZ_LOG(fatal) << "Input:SL(" << temp << ")";
- if (startswith(line, "Command [TL=")) {
- return;
- }
- /*
- temp = raw_line;
- clean_string(temp);
- BUGZ_LOG(fatal) << "InputDispatch::server_line(" << temp << ")";
- */
- temp = prompt;
- ansi_clean(temp);
- size_t total = temp.length() + input.length();
- to_client("\x1b[0m"); // reset colors
- while (total > 0) {
- to_client("\b \b");
- --total;
- }
- // Lines line "\[[1A\[[1;36mphil \[[0;32mwarps into the sector.\[[0m"
- temp = raw_line;
- replace(temp, "\x1b[1A", "");
- // replace(temp, "\x1[2J", "");
- to_client(temp);
- to_client("\n\r");
- // Doesn't matter if it is one or two calls.
- temp = prompt;
- temp.append(input);
- to_client(temp);
- // to_client(prompt);
- // to_client(input);
- }
- // void InputDispatch::server_prompt(const std::string &prompt) {}
- void InputDispatch::client_input(const std::string &cinput) {
- // BUGZ_LOG(info) << "InputDispatch::client_input(" << cinput << ")";
- for (const char ch : cinput) {
- if (isprint(ch)) {
- // Ok!
- if (numeric) {
- // numbers only
- if (!isdigit(ch)) continue;
- }
- if (input.length() < max_length) {
- to_client(std::string(1, ch));
- input += ch;
- }
- } else if ((ch == '\b') || (ch == 0x7f)) {
- // Backspace or rubout
- if (input.length() > 0) {
- to_client("\b \b");
- input.erase(input.size() - 1);
- }
- } else if (ch == '\r') {
- // Ok, we're done!
- BUGZ_LOG(info) << "InputDispatch done: " << input;
- to_client("\x1b[0m\n\r");
- deactivate();
- } else if (ch == '\x1b') {
- aborted = true;
- to_client("\x1b[0m\n\r");
- deactivate();
- }
- }
- }
- /**
- * Menu Dispatch
- *
- * Two types of menus:
- * lazy: display the menu name and show prompt. ? shows menu.
- * non-lazy: displays menu + prompts.
- *
- *
- */
- MenuDispatch::MenuDispatch(Director &d) : Dispatch(d) {
- BUGZ_LOG(warning) << "MenuDispatch()";
- }
- MenuDispatch::~MenuDispatch() { BUGZ_LOG(warning) << "~MenuDispatch()"; }
- void MenuDispatch::activate(void) {
- calculate_widths();
- input.clear();
- BUGZ_LOG(warning) << "MenuDispatch::activate() " << max_width << ", "
- << max_option_width;
- if (lazy)
- menubox();
- else
- help();
- to_client(menu_prompt);
- }
- void MenuDispatch::deactivate(void) { notify(); }
- void MenuDispatch::help(void) {
- size_t max = max_width;
- Boxes mbox(max, 1, true);
- mbox.boxcolor = menu_box_color;
- if (lazy) {
- // just the menu
- mbox.textcolor = menu_options_color;
- to_client(mbox.top());
- for (auto const &menu_item : menu) {
- std::string text = " ";
- text.append(menu_item.first);
- text.append(" - ");
- text.append(menu_item.second);
- while (text.length() < max) text.append(1, ' ');
- to_client(mbox.row(text));
- }
- to_client(mbox.bottom());
- } else {
- // full menu
- mbox.textcolor = menu_text_color;
- to_client(mbox.top());
- std::string title = centered(max, menu_title);
- BUGZ_LOG(debug) << "help max=" << max << " [" << title << "]";
- to_client(mbox.row(title));
- to_client(mbox.middle());
- mbox.textcolor = menu_options_color;
- for (auto const &menu_item : menu) {
- std::string text = " ";
- text.append(menu_item.first);
- text.append(" - ");
- text.append(menu_item.second);
- while (text.length() < max) text.append(1, ' ');
- to_client(mbox.row(text));
- }
- to_client(mbox.bottom());
- }
- }
- std::string MenuDispatch::centered(int length, const std::string &s) {
- std::string text = s;
- size_t leftovers = length - text.length();
- int count = leftovers / 2;
- if (count > 0) {
- text.insert(0, count, ' ');
- text.append(count, ' ');
- }
- if (leftovers % 1 == 1) text.append(1, ' ');
- return text;
- }
- void MenuDispatch::menubox(void) {
- // just the menu box
- std::string title = centered(max_width, menu_title);
- /*
- int leftovers = max - menu_title.length();
- while (leftovers > 2) {
- title.insert(0, 1, " ");
- title.append(1, " ");
- leftovers -= 2;
- };
- if (leftovers == 1)
- title.append(1, " ");
- */
- BUGZ_LOG(debug) << "menubox max=" << max_width << " [" << title << "]";
- auto abox =
- Boxes::alert(title, menu_box_color, menu_text_color, max_width, 1, true);
- for (auto line : abox) {
- to_client(line);
- }
- }
- void MenuDispatch::calculate_widths(void) {
- max_width = menu_title.length() + 2;
- max_option_width = 0;
- for (auto key : menu) {
- size_t menu_line_length =
- 1 + key.first.length() + 3 + key.second.length() + 1;
- if (menu_line_length > max_width) max_width = menu_line_length;
- if (key.first.length() > max_option_width)
- max_option_width = key.first.length();
- }
- instant = max_option_width == 1;
- }
- void MenuDispatch::server_line(const std::string &line,
- const std::string &raw_line) {
- // TODO:
- // Clear prompt, display raw server line, restore prompt.
- if (line.empty()) return;
- std::string temp = repr(raw_line);
- BUGZ_LOG(fatal) << "Input:SL(" << temp << ")";
- if (startswith(line, "Command [TL=")) {
- return;
- }
- /*
- temp = raw_line;
- clean_string(temp);
- BUGZ_LOG(fatal) << "InputDispatch::server_line(" << temp << ")";
- */
- temp = menu_prompt;
- ansi_clean(temp);
- size_t total = temp.length() + input.length();
- to_client("\x1b[0m"); // reset colors
- while (total > 0) {
- to_client("\b \b");
- --total;
- }
- // Lines line "\[[1A\[[1;36mphil \[[0;32mwarps into the sector.\[[0m"
- temp = raw_line;
- replace(temp, "\x1b[1A", "");
- // replace(temp, "\x1[2J", "");
- to_client(temp);
- to_client("\n\r");
- // Doesn't matter if it is one or two calls.
- temp = menu_prompt;
- temp.append(input);
- to_client(temp);
- // to_client(prompt);
- // to_client(input);
- }
- void MenuDispatch::client_input(const std::string &cinput) {
- for (auto const ch : cinput) {
- // not likely that we'd have more then one,
- // but deal with it correctly.
- if (ch == '\r') {
- // enter
- if (instant) return;
- for (auto const &mnu : menu) {
- if (mnu.first == input) {
- to_client("\x1b[0m\n\r");
- deactivate();
- return;
- }
- }
- // input wasn't found ?
- while (input.length() > 0) {
- to_client("\b \b");
- input.erase(input.length() - 1);
- }
- return; // don't continue ...
- }
- if (ch == '\x1b') {
- // [ESC] - erase the input string
- while (input.length() > 0) {
- to_client("\b \b");
- input.erase(input.length() - 1);
- }
- aborted = true;
- // Exit - allow escape from menu
- deactivate();
- return;
- }
- if (ch == '\b') {
- if (input.length() > 0) {
- to_client("\b \b");
- input.erase(input.length() - 1);
- }
- }
- if (ch == '?') {
- to_client(cinput); // display what they entered.
- to_client("\x1b[0m\n\r");
- help();
- to_client(menu_prompt);
- return;
- }
- if (isprint(ch)) {
- char c = ch;
- if (!case_sensitive) c = toupper(ch);
- // ok, it's a printable character
- if (input.length() < max_option_width) {
- // ok, there's room.
- to_client(std::string(1, c));
- input.append(1, c);
- }
- if (instant) {
- for (auto const &mnu : menu) {
- if (mnu.first == input) {
- to_client("\x1b[0m\n\r");
- deactivate();
- return;
- }
- }
- // Erase last character, wasn't an option.
- to_client("\b \b");
- input.erase(input.length() - 1);
- }
- }
- }
- }
- CIMDispatch::CIMDispatch(Director &d) : Dispatch(d) {}
- void CIMDispatch::activate(void) { count = 0; }
- void CIMDispatch::deactivate(void) { notify(); }
- void CIMDispatch::server_line(const std::string &line,
- const std::string &raw_line) {
- if (!((line.empty() || startswith(line, ": ") ||
- startswith(line, "Command [")))) {
- count++;
- if (count % 100 == 0) {
- std::string message = str(boost::format("\r%1%") % count);
- to_client(message);
- }
- }
- if (line == ": ENDINTERROG") {
- std::string message = str(boost::format("\r%1%\n\r") % count);
- to_client(message);
- deactivate();
- }
- }
- MoveDispatch::MoveDispatch(Director &d) : Dispatch(d) {
- BUGZ_LOG(warning) << "MoveDispatch()";
- }
- MoveDispatch::~MoveDispatch() {
- BUGZ_LOG(warning) << "~MoveDispatch()";
- }
- // sector_type move_to;
- void MoveDispatch::activate(void) {
- starting = director.current_sector;
- BUGZ_LOG(fatal) << "MoveDispatch::activate()";
- BUGZ_LOG(warning) << "Moving from " << starting << " to " << move_to;
- // Start with density scan
- to_server("SD");
- state = 1;
- warp_lane.clear();
- warp_pos = 0;
- use_express = false;
- // build final string to match against
- at_destination = "Auto Warping to sector ";
- at_destination.append(std::to_string(move_to));
- }
- void MoveDispatch::deactivate(void) { notify(); }
- // optional here
- void MoveDispatch::server_line(const std::string &line,
- const std::string &raw_line) {
- if (state == 1) {
- if (endswith(line, "Relative Density Scan")) {
- state = 2;
- }
- if (line == "You don't have a long range scanner.") {
- // Ok, we'll have to express move our way there then.
- use_express = true;
- // state = 2;
- std::string command = str(boost::format("M%1%\r") % move_to);
- to_server(command);
- state = 3;
- }
- }
- // SL: [###### DANGER! You have marked sector 740 to be avoided!]
- if ((state != 2) && (state != 5)) {
- // hide the density scan part
- std::string temp = raw_line;
- if (line == "<Auto Pilot Engaging>") {
- // trim out the stray cursor home
- replace(temp, "\x1b[H", "");
- }
- // Replace progress bar with something else.
- if (startswith(temp, "\x1b[1;33m\xb3"))
- temp = "\x1b[1A\x1b[1;33m** SLOW MOVE **";
- /*
- if (in(temp, "\xb3")) {
- std::string debug = repr(temp);
- BUGZ_LOG(fatal) << "MOVE LINE: " << debug;
- }
- */
- // Don't output raw_lines that move the cursor up 3.
- if (!in(temp, "\x1b[3A")) {
- temp.append("\n\r");
- to_client(temp);
- }
- }
- if (state == 3) {
- if (line == "You are already in that sector!") {
- success = true;
- deactivate();
- return;
- }
- if (line == "That Warp Lane is not adjacent.") {
- // ok! Parse out the path that we need to take...
- }
- // [611 > 612 > 577 > 543 > 162 > 947 > 185 > 720 > 894 > 3 > 1]
- // multiple lines possible here? Yes.
- // [344 > 23328 > 2981 > 10465 > 14016 > 8979 > 1916 > 18734 > 5477 > 131 >
- // 27464 >] watch for <Move> it contains >
- // if ((line != "<Move>") && in(line, " > ")) {
- if ((line != "<Move>") && (in(line, " > ") || !warp_lane.empty())) {
- bool more = false;
- std::string work = line;
- if (endswith(work, " >")) {
- more = true;
- work = work.substr(0, work.length() - 2);
- }
- replace(work, " > ", " ");
- replace(work, "(", "");
- replace(work, ")", "");
- auto warps = split(work);
- for (auto const &w : warps) {
- BUGZ_LOG(fatal) << "lane: " << w;
- warp_lane.push_back(stoi(w));
- }
- if (!more) state = 4;
- }
- }
- if (state == 4) {
- if (line == at_destination) {
- // [Auto Warping to sector 1]
- state = 6;
- }
- }
- }
- /*
- bool MoveDispatch::density_clear(density d) { // int sector, int density) {
- /*
- http://wiki.classictw.com/index.php?title=Gypsy%27s_Big_Dummy%27s_Guide_to_TradeWars_Text#Trader_Information
- Density Readings:
- 0 = Empty Sector or Ferrengi Dreadanought
- 1 = Marker Beacon
- 2 = Limpet Type 2 Tracking Mine
- 5 = Fighter (per Fighter)
- 10 = Armid Type 1 Mine
- 21 = Navigation Hazard (Per 1 Percent)
- 21 = Destroyed Ship (Due to 1 Percent Nav-Haz)
- 38 = Unmanned Ship
- 40 = Manned Ship, Alien or Ferrengi Assault Trader
- 50 = Destroyed Starport (After 25 Percent Nav-Haz Clears)
- 100 = Starport or Ferrengi Battle Cruiser
- 210 = Destroyed Planet (Due to 10 Percent Nav-Haz)
- 462 = Federation Starship under Admiral Nelson
- 489 = Federation Starship under Captain Zyrain
- 500 = Planet
- 512 = Federation Starship under Admiral Clausewitz
- 575 = Destroyed Port (Before 25% Nav-Haz Clears)
- */ /*
- if (d.sector == 0) return false;
- switch (d.density) {
- case 0:
- case 1:
- case 100:
- case 101:
- return true;
- }
- // special case for sector 1:
- if ((d.sector == 1) && (d.density == 601)) return true;
- return false;
- } */
- void MoveDispatch::server_prompt(const std::string &prompt) {
- BUGZ_LOG(fatal) << "server_prompt: " << prompt;
- // SL: [###### DANGER! You have marked sector 740 to be avoided!]
- // SP: [Do you really want to warp there? (Y/N) ]
- if (state == 2) {
- if (at_command_prompt(prompt)) {
- // Ok, density is done
- // BUG: If the sector is adjacent, this doesn't check density!
- density d = director.galaxy.dscan.find(move_to);
- if (d.sector == move_to) {
- // Yes! we found the sector in the scan!
- if (!density_clear(d.sector, d.density, d.navhaz)) {
- BUGZ_LOG(fatal) << "Failed density check on single move.";
- success = false;
- deactivate();
- return;
- }
- }
- std::string command = str(boost::format("M%1%\r") % move_to);
- to_server(command);
- state = 3;
- }
- } else if (state == 3) {
- if (at_command_prompt(prompt)) {
- // this happens when the sector is adject to current_sector.
- BUGZ_LOG(fatal) << "Are we there yet?";
- success = true;
- deactivate();
- return;
- }
- } else if (state == 4) {
- if (prompt == "Engage the Autopilot? (Y/N/Single step/Express) [Y] ") {
- if (use_express) {
- BUGZ_LOG(fatal) << "Using Express";
- to_server("E");
- } else {
- int to_check = warp_lane[warp_pos + 1];
- // check density scan
- density d = director.galaxy.dscan.find(to_check);
- /*
- int density =
- director.galaxy.meta["density"][to_check]["density"].as<int>();
- */
- if (density_clear(d.sector, d.density, d.navhaz)) { // to_check, density)) {
- to_server("S");
- ++warp_pos;
- } else {
- to_server("N");
- BUGZ_LOG(fatal) << "density_clear(" << to_check << ") : false";
- success = false;
- deactivate();
- }
- }
- }
- if (prompt == "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ? ") {
- state = 5;
- to_server("SD");
- }
- } else if (state == 5) {
- // finished scan
- if (prompt == "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ? ") {
- int to_check = warp_lane[warp_pos + 1];
- density d = director.galaxy.dscan.find(to_check);
- /*
- int density =
- director.galaxy.meta["density"][to_check]["density"].as<int>();
- */
- if (density_clear(d.sector, d.density, d.navhaz)) {
- to_server("N");
- ++warp_pos;
- state = 4;
- } else {
- to_server("Y");
- BUGZ_LOG(fatal) << "Stopped by density: " << to_check;
- success = 0;
- deactivate();
- }
- }
- } else if (state == 6) {
- if (at_command_prompt(prompt)) {
- // We're done!
- success = 1;
- deactivate();
- }
- }
- }
- void MoveDispatch::client_input(const std::string &input) {
- // This exits us out quickly -- should I stop at a better spot?
- // As in answer "Y" to Stop in this sector?
- success = 0;
- aborted = true;
- deactivate();
- }
- TraderDispatch::TraderDispatch(Director &d) : Dispatch(d) {
- BUGZ_LOG(fatal) << "TraderDispatch()";
- state = 0;
- success = false;
- director.galaxy.meta["help"]["stop_percent"] =
- "ScriptTrader stop trading if below this percent.";
- director.galaxy.meta["help"]["trade_end_empty"] =
- "ScriptTrader end trading with empty holds? Y/N";
- };
- TraderDispatch::~TraderDispatch() { BUGZ_LOG(fatal) << "~TraderDispatch()"; }
- void TraderDispatch::activate(void) {
- // ok, lookup port1 port2
- BUGZ_LOG(fatal) << "TraderDispatch::activate " << port[0] << " & " << port[1];
- auto port_info = director.galaxy.ports.find(port[0]);
- int port0_type = port_info->second.type;
- port_buysell[0] = get_buysell(port0_type);
- // Special case - we just want to buy resources
- if (port[1] != 0) {
- port_info = director.galaxy.ports.find(port[1]);
- int port1_type = port_info->second.type;
- port_buysell[1] = get_buysell(port1_type);
- BUGZ_LOG(fatal) << port0_type << " and " << port1_type;
- } else {
- BUGZ_LOG(fatal) << "Just buy from " << port[0];
- }
- /*
- auto ttr = director.galaxy.trade_type_info(port0_type, port1_type);
- trades = ttr.trades;
- */
- /*
- if (trades.foe[0] && trades.foe[1] && trades.foe[2]) {
- // it has all three -- use the last 2.
- trades.foe[0] = false;
- }
- */
- // Ok, what do we do first here?
- // I - Info
- state = 1;
- percent = 5.0;
- to_server("I");
- if (director.galaxy.config["stop_percent"]) {
- stop_percent = director.galaxy.config["stop_percent"].as<int>();
- } else {
- stop_percent = 25;
- director.galaxy.config["stop_percent"] = stop_percent;
- }
- if (director.galaxy.config["trade_end_empty"]) {
- std::string tee =
- director.galaxy.config["trade_end_empty"].as<std::string>();
- if ((toupper(tee[0]) == 'Y') || (toupper(tee[0]) == 'T')) {
- trade_end_empty = true;
- } else {
- trade_end_empty = false;
- // director.galaxy.config["trade_end_empty"] = "N";
- }
- } else {
- trade_end_empty = true;
- director.galaxy.config["trade_end_empty"] = "Y";
- }
- }
- void TraderDispatch::deactivate(void) { notify(); }
- void TraderDispatch::server_line(const std::string &line,
- const std::string &raw_line) {
- // FUTURE: powering up weapons check
- // Show what's going on...
- if (state > 1) {
- std::string temp = raw_line;
- // replace progress bar with something else
- if (startswith(temp, "\x1b[1;33m\xb3"))
- temp = "\x1b[1A\x1b[1;33m** SLOW MOVE **";
- // don't output lines that move cursor up 3.
- // if (!in(temp, "\x1b[3A")) {
- temp.append("\n\r");
- to_client(temp);
- // }
- }
- if (line == "Docking...") {
- last_offer = 0;
- final_offer = 0;
- initial_offer = 0;
- try_again = false;
- }
- static std::set<std::string> success_lines = {
- "If only more honest traders would port here, we'll take them though.",
- "You will put me out of business, I'll take your offer.",
- "FINE, we'll take them, just leave!",
- "Agreed, and a pleasure doing business with you!",
- "You are a rogue! We'll take them anyway.",
- "You insult my intelligence, but we'll buy them anyway.",
- "Very well, we'll take that offer.",
- "You drive a hard bargain, but we'll take them.",
- "Done, we'll take the lot.",
- "I hate haggling, they're all yours.",
- "You are robbing me, but we'll buy them anyway.",
- "SOLD! Come back anytime!",
- "Cheapskate. Here, take them and leave me alone.",
- "Very well, we'll buy them.",
- "You are a shrewd trader, they're all yours.",
- "I could have twice that much in the Androcan Empire, but they're yours.",
- "Oh well, maybe I can sell these to some other fool, we'll take them.",
- "I PAID more than that! But we'll sell them to you anyway.",
- "(Sigh) Very well, pay up and take them away.",
- "Agreed! We'll purchase them!"};
- if (success_lines.find(line) != success_lines.end()) {
- BUGZ_LOG(fatal) << "Success " << buying << " " << initial_offer << " : "
- << last_offer;
- // calculate % ?
- success = true;
- BUGZ_LOG(fatal) << "% " << (float)initial_offer / (float)last_offer * 100.0;
- BUGZ_LOG(fatal) << "meta trade setting: " << percent << " for "
- << active_port << " " << product;
- director.galaxy.meta["trade"][active_port][product] = percent;
- // subtract total holds value from this port's amount
- auto port = director.galaxy.ports.find(active_port);
- if (port != director.galaxy.ports.end()) {
- // We've found the port!
- // product is the index
- port->second.amount[product] -=
- director.galaxy.meta["ship"]["holds"]["total"].as<int>();
- BUGZ_LOG(fatal) << "Port " << active_port << "," << product
- << " amount is now " << port->second.amount[product];
- }
- }
- // <P-Probe estimates your offer was
- if (startswith(line, "Agreed, ")) {
- last_offer = 0;
- final_offer = 0;
- if (director.galaxy.meta["trade"][active_port][product]) {
- percent = director.galaxy.meta["trade"][active_port][product].as<float>();
- percent += 1.0;
- BUGZ_LOG(fatal) << "Percent for " << active_port << " now " << percent;
- } else {
- BUGZ_LOG(fatal) << "using default for " << active_port;
- percent = 5.0; // check meta for past trades information
- }
- }
- if (startswith(line, "We'll buy them for ")) {
- // I need the initial offer!
- std::string offer = line.substr(19);
- replace(offer, ",", "");
- initial_offer = stoi(offer);
- BUGZ_LOG(fatal) << "Buying, initial: " << initial_offer;
- buying = true; // Port is buying, we are selling.
- }
- if (startswith(line, "We'll sell them for ")) {
- // I need the initial offer!
- std::string offer = line.substr(20);
- replace(offer, ",", "");
- initial_offer = stoi(offer);
- BUGZ_LOG(fatal) << "Selling, initial: " << initial_offer;
- buying = false; // Port is selling, we are buying.
- }
- // SL: [Our final offer is 1,263 credits.]
- if (startswith(line, "Our final offer is ")) {
- // Well snap!
- std::string offer = line.substr(19);
- replace(offer, ",", "");
- final_offer = stoi(offer);
- BUGZ_LOG(fatal) << "Final offer: " << final_offer;
- }
- if (line == "We're not interested.") {
- // well rats.
- BUGZ_LOG(fatal) << "We're not interested => meta trade setting: " << percent
- << " for " << active_port << " " << product;
- director.galaxy.meta["trade"][active_port][product] = percent;
- try_again = true;
- }
- // SL: [You have 16,767 credits and 0 empty cargo holds.]
- // trade accepted. if not 0 empty cargo holds -- we failed!
- // SL: [<P-Probe estimates your offer was 91.83% of best price>]
- // SL: [You have 4,046 credits and 0 empty cargo holds.]
- // this shows up at the initial docking of the port.
- if (startswith(line, "You have ")) {
- if (initial_offer != 0) {
- // Ok, the offer was possibly accepted.
- int success;
- if (buying)
- success = director.galaxy.meta["ship"]["holds"]["total"].as<int>();
- else
- success = 0;
- std::string text = std::to_string(success);
- text.append(" empty cargo holds.");
- if (endswith(line, text)) {
- BUGZ_LOG(fatal) << "Trade SUCCESS!";
- // record this action somewhere in meta.
- } else {
- BUGZ_LOG(fatal) << "Trade FAIL";
- }
- }
- }
- }
- void TraderDispatch::server_prompt(const std::string &prompt) {
- // FUTURE: check for "Surrender/Attack"
- if (at_command_prompt(prompt)) {
- if (state == 1) {
- // Ok, decision time!
- if (director.galaxy.meta["ship"]["holds"]["c"]) {
- // holds contain colonists
- success = false;
- aborted = true;
- to_client("ScriptTrader FAIL: holds contain colonists.");
- deactivate();
- return;
- }
- if (director.galaxy.meta["ship"]["holds"]["total"]) {
- int total = director.galaxy.meta["ship"]["holds"]["total"].as<int>();
- int empty = 0;
- if (director.galaxy.meta["ship"]["holds"]["empty"]) {
- empty = director.galaxy.meta["ship"]["holds"]["empty"].as<int>();
- }
- if (total != empty) {
- BUGZ_LOG(fatal) << "FAIL: " << total << " total holds, " << empty
- << " holds empty.";
- to_client("ScriptTrader FAIL: holds are not empty.");
- success = false;
- aborted = true;
- deactivate();
- return;
- }
- }
- // Which port to trade with first? examine trades
- BUGZ_LOG(fatal) << "trades: " << trades;
- BUGZ_LOG(fatal) << "port0:" << text_from_buysell(port_buysell[0]);
- if (port[1] != 0)
- BUGZ_LOG(fatal) << "port1:" << text_from_buysell(port_buysell[1]);
- // Ok, I might still need this (so I know what port to start with)
- // which is selling?
- // must set active port!
- bool all_holds_empty = false;
- active_port = 0;
- // check the ship and holds here. (MAYBE)
- int holds = director.galaxy.meta["ship"]["holds"]["total"].as<int>();
- if (director.galaxy.meta["ship"]["holds"]["empty"]) {
- if (holds == director.galaxy.meta["ship"]["holds"]["empty"].as<int>())
- all_holds_empty = true;
- }
- if (port[1] == 0) {
- active_port = port[0];
- } else {
- if (!all_holds_empty) {
- for (int x = 0; x < 3; ++x) {
- if (director.galaxy.meta["ship"]["holds"][foe[x]]) {
- if (!port_buysell[0].foe[x]) {
- active_port = port[0];
- break;
- }
- if (!port_buysell[1].foe[x]) {
- active_port = port[1];
- }
- }
- }
- if (active_port == 0) {
- success = false;
- to_client(
- "I don't see any ports that are buying what we have in our "
- "holds.\n\r");
- deactivate();
- return;
- };
- } else {
- // all holds empty, find selling port
- for (int x = 0; x < 3; ++x) {
- if (trades.foe[x]) {
- // TRUE = BUY, so FALSE = sell
- if (!port_buysell[0].foe[x]) {
- active_port = port[0];
- break;
- }
- if (!port_buysell[1].foe[x]) {
- active_port = port[1];
- break;
- }
- }
- }
- }
- }
- state = 2;
- if (director.current_sector == active_port) {
- // begin state 3
- state = 3;
- to_client("Trading...\n\r");
- to_server("PT");
- return;
- } else {
- // initiate move
- std::string move = std::to_string(active_port);
- to_client("Moving...\n\r");
- move.append("\r");
- to_server(move);
- return;
- }
- }
- if (state == 2) {
- if (director.current_sector == active_port) {
- // We're here
- state = 3;
- to_client("Trading...\n\r");
- to_server("PT");
- return;
- } else {
- // we failed to move to where we wanted to go?!
- BUGZ_LOG(fatal) << "Expecting: " << active_port << " but got "
- << director.current_sector;
- deactivate();
- return;
- }
- }
- }
- if (state == 3) {
- if (startswith(prompt, "How many holds of ") && endswith(prompt, "]? ")) {
- char selling = tolower(prompt[18]);
- for (int x = 0; x < 3; ++x) {
- if (foe[x] == selling) product = x;
- }
- if (in(prompt, " to sell ")) {
- // always sell everything
- to_server("\r");
- return;
- }
- if (in(prompt, " to buy ")) {
- bool buy_ok = true;
- std::string max =
- str(boost::format("[%1%]") %
- director.galaxy.meta["ship"]["holds"]["total"].as<int>());
- if (!in(prompt, max)) {
- buy_ok = false;
- }
- if (trade_end_empty) {
- // Ok, we want to end with empty holds...
- int other_port;
- if (active_port == port[0])
- other_port = port[1];
- else
- other_port = port[0];
- BUGZ_LOG(fatal) << "Is " << other_port << " burnt? (trade_end_empty)";
- // Is target port burnt?
- auto pos = director.galaxy.ports.find(other_port);
- bool burnt = false;
- if (pos != director.galaxy.ports.end()) {
- // We'll find the port. Really.
- if (!pos->second.unknown()) {
- // port isn't unknown, so check to see if it's burnt
- for (int x = 0; x < 3; ++x) {
- if (trades.foe[x]) {
- BUGZ_LOG(fatal)
- << other_port << " " << x << " is in trades...";
- BUGZ_LOG(fatal)
- << "amount[" << x << "] = " << pos->second.amount[x];
- if (pos->second.percent[x] < stop_percent) burnt = true;
- if (director.galaxy.meta["ship"]["holds"]["total"]) {
- BUGZ_LOG(fatal)
- << pos->second.amount[x] << " : "
- << director.galaxy.meta["ship"]["holds"]["total"]
- .as<int>();
- if (pos->second.amount[x] <
- director.galaxy.meta["ship"]["holds"]["total"]
- .as<int>()) {
- BUGZ_LOG(fatal) << "Other port " << other_port
- << " is burnt " << x << " burnt = true";
- burnt = true;
- }
- }
- }
- }
- }
- }
- if (burnt) {
- BUGZ_LOG(fatal) << "Port burnt, buy_ok = false";
- buy_ok = false;
- }
- }
- // Ok, what are they selling?
- // char selling = tolower(prompt[18]);
- BUGZ_LOG(fatal) << "Selling: " << selling;
- if (!buy_ok) {
- // no!
- to_server("0\r");
- } else
- for (int x = 0; x < 3; ++x) {
- // if (foe[x] == selling) {
- // We found the item ... is it something that we're trading?
- if (foe[x] == selling) {
- if (trades.foe[x]) {
- // Yes!
- to_server("\r");
- product = x;
- } else {
- // No!
- to_server("0\r");
- }
- }
- }
- // }
- }
- }
- if (startswith(prompt, "Your offer [") && endswith(prompt, " ? ")) {
- // Ok, things get weird here. We also need to look for final offer.
- if (last_offer != 0) percent -= 1.0;
- if (buying)
- last_offer = (int)(initial_offer * (100 + percent) / 100.0);
- else
- last_offer = (int)(initial_offer * (100 - percent) / 100.0);
- BUGZ_LOG(fatal) << "Offer: " << buying << " offer " << last_offer << " % "
- << percent;
- std::string text = std::to_string(last_offer);
- text.append("\r");
- to_server(text);
- }
- if (at_command_prompt(prompt)) {
- // we're done trading...
- // do we carry on, or stop?
- // 1.) CHECK TURNS // need turn tracking
- // 2.) PORTS BURNT?
- if (try_again) {
- state = 3;
- to_client("Trading... Take 2!\n\r");
- to_server("PT");
- return;
- }
- if (active_port == port[0]) {
- if (port[0] == 0) {
- deactivate();
- return;
- }
- active_port = port[1];
- } else
- active_port = port[0];
- // Is target port burnt?
- auto pos = director.galaxy.ports.find(active_port);
- bool burnt = false;
- if (pos != director.galaxy.ports.end()) {
- // We'll find the port. Really.
- if (!pos->second.unknown()) {
- // port isn't unknown, check to see if burnt
- for (int x = 0; x < 3; ++x) {
- if (trades.foe[x]) {
- BUGZ_LOG(fatal) << x << " % " << (int)pos->second.percent[x]
- << " " << stop_percent;
- if (pos->second.percent[x] < stop_percent) burnt = true;
- if (director.galaxy.meta["ship"]["holds"]["total"])
- if (pos->second.amount[x] <
- director.galaxy.meta["ship"]["holds"]["total"].as<int>())
- burnt = true;
- }
- }
- }
- }
- if (burnt) {
- to_client("Ports burnt.\n\r");
- deactivate();
- return;
- }
- std::string move = std::to_string(active_port);
- to_client("Moving...\n\r");
- move.append("\r");
- to_server(move);
- state = 2;
- }
- }
- }
- void TraderDispatch::client_input(const std::string &cinput) {
- aborted = true;
- deactivate();
- };
- /*
- * CoreDispatch: This is an example class that does dispatch.
- * Copy this and make changes from there...
- */
- CoreDispatch::CoreDispatch(Director &d) : Dispatch(d) {
- BUGZ_LOG(warning) << "CoreDispatch()";
- }
- void CoreDispatch::activate(void) {
- // save things, set things
- }
- void CoreDispatch::deactivate(void) {
- // restore things
- notify();
- }
- void CoreDispatch::server_line(const std::string &line,
- const std::string &raw_line) {}
- void CoreDispatch::server_prompt(const std::string &prompt) {}
- void CoreDispatch::client_input(const std::string &input) {
- BUGZ_LOG(warning) << "Got: " << input << " prompt=" << get_prompt();
- aborted = true;
- deactivate();
- }
|