1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345 |
- #include "dispatchers.h"
- #include <boost/format.hpp>
- #include <cctype>
- #include "boxes.h"
- #include "logging.h"
- #include "utils.h"
- Dispatch::Dispatch(Director &d, const char *called) : director{d} {
- aborted = false;
- if (called == nullptr)
- name = "Dispatch";
- else
- name = called;
- };
- Dispatch::~Dispatch(){};
- void Dispatch::deactivate(void) { notify(); }
- void Dispatch::to_server(const std::string &send) {
- director.to_server(send, name);
- }
- 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::activate_chain(std::shared_ptr<Dispatch> new_chain) {
- director.chain = new_chain;
- director.chain->activate();
- }
- 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, const char *called)
- : Dispatch(d, called) {
- BUGZ_LOG(warning) << "InputDispatch()";
- numeric = false;
- if (called == nullptr) name = "Input";
- }
- 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, const char *called)
- : Dispatch(d, called) {
- BUGZ_LOG(warning) << "MenuDispatch()";
- if (called == nullptr) name = "Menu";
- }
- 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, const char *called)
- : Dispatch(d, called) {
- if (called == nullptr) name = "CIM";
- }
- 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, const char *called)
- : Dispatch(d, called) {
- BUGZ_LOG(warning) << "MoveDispatch()";
- use_express = false;
- if (called == nullptr) name = "Move";
- }
- 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;
- if (starting == move_to) {
- success = true;
- deactivate();
- return;
- }
- /*
- 1 = Issue "I"
- 2 = Density Scan
- 3 = Move
- 4 = Autopilot
- 5 = Density Scan
- */
- // Start with density scan -- unless express
- warp_lane.clear();
- warp_pos = 0;
- if (use_express) {
- std::string command = str(boost::format("M%1%\r") % move_to);
- to_server(command);
- state = 3;
- } else {
- to_server("S"); // because we might not have a scanner
- state = 1;
- }
- // build final string to match against
- at_destination = "Auto Warping to sector ";
- at_destination.append(std::to_string(move_to));
- }
- void MoveDispatch::deactivate(void) {
- BUGZ_LOG(fatal) << "MoveDispatch::deactivate() " << success;
- notify();
- }
- // optional here
- void MoveDispatch::server_line(const std::string &line,
- const std::string &raw_line) {
- if (!line.empty()) BUGZ_LOG(fatal) << state << " : " << 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 == "You don't have a long range scanner.") {
- use_express = true;
- }
- 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: " << state << " SP: [" << prompt << "]";
- // SL: [###### DANGER! You have marked sector 740 to be avoided!]
- // SP: [Do you really want to warp there? (Y/N) ]
- if (state == 1) {
- if (prompt == "Select (H)olo Scan or (D)ensity Scan or (Q)uit? [D] ") {
- to_server("D");
- }
- } else 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.";
- why_failed = str(boost::format("Sector %1% has density %2%.") %
- move_to % d.density);
- 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 (prompt == "Clear Avoids? ") {
- to_server("N");
- why_failed = "No path / check avoids.";
- success = false;
- deactivate();
- }
- if (at_command_prompt(prompt)) {
- // this happens when the sector is adjcent to current_sector.
- // Or it happens when the ship is FUBAR.
- // NO!!!!
- if (director.current_sector == move_to) {
- BUGZ_LOG(fatal) << "Are we there yet? / Adjacent sector.";
- success = true;
- deactivate();
- } else {
- // Nope! Wrong!
- }
- return;
- }
- if (prompt == "Engage the Autopilot? (Y/N/Single step/Express) [Y] ") {
- to_server("E");
- 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";
- why_failed = str(boost::format("Sector %1% has density %2%.") %
- to_check % d.density);
- success = false;
- deactivate();
- }
- }
- }
- if (prompt == "Stop in this sector (Y,N,E,I,R,S,D,P,?) (?=Help) [N] ? ") {
- state = 5;
- // BUT? Do we have a scanner?
- if (use_express) {
- to_server("E");
- } else
- to_server("SD");
- return;
- }
- } else if (state == 5) {
- if (prompt == "Engage Express mode? (Y/N) [N] ") {
- to_server("Y");
- return;
- }
- // 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);
- 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;
- why_failed = str(boost::format("Sector %1% has density %2%.") %
- to_check % d.density);
- success = 0;
- deactivate();
- }
- }
- if (at_command_prompt(prompt)) {
- if (director.current_sector == move_to) {
- success = 1;
- } else
- success = 0;
- deactivate();
- return;
- }
- } 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, const char *called)
- : Dispatch(d, called) {
- BUGZ_LOG(fatal) << "TraderDispatch()";
- state = 0;
- success = false;
- if (called == nullptr) name = "Trader";
- 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];
- }
- /*
- 1 = Info
- */
- // Ok, what do we do first here?
- // I - Info
- state = 1;
- percent = 5.0;
- to_server("I");
- if (director.galaxy.config.contains("stop_percent")) {
- stop_percent = director.galaxy.config["stop_percent"].get<int>();
- } else {
- stop_percent = 25;
- director.galaxy.config["stop_percent"] = stop_percent;
- }
- if (director.galaxy.config.contains("trade_end_empty")) {
- std::string tee = director.galaxy.config["trade_end_empty"];
- 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"][std::to_string(active_port)][std::to_string(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"].get<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;
- std::string active_port_text = std::to_string(active_port);
- std::string product_text;
- product_text.assign(product, 1);
- if (director.galaxy.meta["_trade"][active_port_text].contains(
- product_text)) {
- percent = director.galaxy.meta["_trade"][active_port_text][product_text];
- 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;
- std::string active_port_text = std::to_string(active_port);
- director.galaxy.meta["_trade"][active_port_text][std::to_string(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"];
- 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"].contains("c")) {
- // holds contain colonists
- why_failed = "Holds contain colonists.";
- success = false;
- aborted = true;
- to_client("ScriptTrader FAIL: holds contain colonists.");
- deactivate();
- return;
- }
- int total;
- if (director.galaxy.meta["ship"]["holds"].contains("total")) {
- total = json_int(director.galaxy.meta["ship"]["holds"]["total"]);
- int empty = 0;
- if (director.galaxy.meta["ship"]["holds"].contains("empty")) {
- empty = json_int(director.galaxy.meta["ship"]["holds"]["empty"]);
- }
- if (total != empty) {
- BUGZ_LOG(fatal) << "FAIL: " << total << " total holds, " << empty
- << " holds empty.";
- to_client("ScriptTrader FAIL: holds are not empty.");
- why_failed = "Holds are not empty.";
- success = false;
- aborted = true;
- deactivate();
- return;
- }
- }
- // Right now, I stop trading, if we can't fill/buy max holds.
- // We now look to see if you have 75 * total_holds credits.
- int credits = json_int(director.galaxy.meta["credits"]);
- if (credits < (total * 75)) {
- BUGZ_LOG(fatal) << "FAIL: credits < " << total * 75 << " have "
- << credits;
- std::string message =
- str(boost::format("ScriptTrader FAIl: You need at least %1% "
- "credits to trade.\n\r") %
- (total * 75));
- to_client(message);
- why_failed = "Low credits.";
- 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 = json_int(director.galaxy.meta["ship"]["holds"]["total"]);
- if (director.galaxy.meta["ship"]["holds"].contains("empty")) {
- if (holds == json_int(director.galaxy.meta["ship"]["holds"]["empty"]))
- all_holds_empty = true;
- }
- if (port[1] == 0) {
- active_port = port[0];
- } else {
- if (!all_holds_empty) {
- for (int x = 0; x < 3; ++x) {
- std::string foe_text;
- foe_text.assign(1, foe[x]);
- if (director.galaxy.meta["ship"]["holds"].contains(foe_text)) {
- 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) {
- why_failed = "No ports will buy what we have in our holds.";
- 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%]") %
- json_int(director.galaxy.meta["ship"]["holds"]["total"]));
- 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"].contains("total")) {
- BUGZ_LOG(fatal)
- << pos->second.amount[x] << " : "
- << json_int(
- director.galaxy.meta["ship"]["holds"]["total"]);
- if (pos->second.amount[x] <
- json_int(
- director.galaxy.meta["ship"]["holds"]["total"])) {
- 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]) {
- active_port = port[1];
- } else
- active_port = port[0];
- if (active_port == 0) {
- deactivate();
- return;
- }
- // 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"].contains("total"))
- if (pos->second.amount[x] <
- json_int(director.galaxy.meta["ship"]["holds"]["total"]))
- 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, const char *called)
- : Dispatch(d, called) {
- BUGZ_LOG(warning) << "CoreDispatch()";
- if (called == nullptr) name = "Core";
- }
- 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();
- }
|