director.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  1. #include "director.h"
  2. #include <boost/format.hpp>
  3. #include <cctype>
  4. #include "boxes.h"
  5. #include "galaxy.h"
  6. #include "logging.h"
  7. #include "scripts.h"
  8. #include "utils.h"
  9. Director::Director() {
  10. BUGZ_LOG(warning) << "Director::Director()";
  11. // active = false;
  12. game = 0; // not in a game
  13. galaxy.reset();
  14. // do everything proxy_deactivate does ...
  15. // proxy_deactivate();
  16. active = false;
  17. // reset everything back to good state
  18. talk_direct = true;
  19. show_client = true;
  20. count = 0;
  21. /*
  22. Setup StringFunc for SL_parser:
  23. Construct these once, rather then every single time we need them.
  24. */
  25. SF_cimline = [this](const std::string &s) { this->SL_cimline(s); };
  26. SF_sectorline = [this](const std::string &s) { this->SL_sectorline(s); };
  27. SF_portline = [this](const std::string &s) { this->SL_portline(s); };
  28. SF_warpline = [this](const std::string &s) { this->SL_warpline(s); };
  29. SF_infoline = [this](const std::string &s) { this->SL_infoline(s); };
  30. SF_densityline = [this](const std::string &s) { this->SL_densityline(s); };
  31. SF_computer_portline = [this](const std::string &s) {
  32. this->SL_computer_portline(s);
  33. };
  34. build_menu();
  35. }
  36. Director::~Director() { BUGZ_LOG(warning) << "Director::~Director()"; }
  37. void Director::client_input(const std::string &input) {
  38. // If we're already active, don't try to activate.
  39. if (chain) {
  40. chain->client_input(input);
  41. return;
  42. }
  43. if (active) {
  44. if (input == "Q" || input == "q") proxy_deactivate();
  45. return;
  46. } else if (input == "\x1b" || input == "~") {
  47. std::string &prompt = current_prompt;
  48. BUGZ_LOG(trace) << "ACTIVATE prompt shows: [" << prompt << "]";
  49. if (prompt == "Selection (? for menu): ") {
  50. to_client(
  51. "\n\rThere's not much we can do here. Activate in-game at a "
  52. "Command prompt.\n\r");
  53. to_client(current_raw_prompt);
  54. return;
  55. }
  56. // easter-eggs:
  57. if (prompt == "Enter your choice: ") {
  58. to_client(
  59. "\n\r\x1b[1;36mI'd choose \x1b[1;37m`T`\x1b[1;36m, but "
  60. "that's how I was coded.\n\r");
  61. to_client(current_raw_prompt);
  62. return;
  63. }
  64. // easter-egg
  65. if (prompt == "[Pause]") {
  66. to_client(" \x1b[1;36mPAWS\x1b[0m\n\r");
  67. to_client(current_raw_prompt);
  68. return;
  69. }
  70. if (at_planet_prompt(prompt)) {
  71. // future: If activated at planet menu, activate the planet upgrade
  72. // script!
  73. to_client("\n\r\x1b[0mFUTURE: Activate the planet upgrade script.\n\r");
  74. to_client(current_raw_prompt);
  75. return;
  76. }
  77. //
  78. // The command prompt that we're looking for:
  79. //
  80. // "Command [TL=00:00:00]:[242] (?=Help)? : "
  81. // the time, and the sector number vary...
  82. /*
  83. if (startswith(prompt, "Command [")) {
  84. // if (prompt.substr(0, 9) == "Command [") {
  85. // int len = prompt.length();
  86. if (endswith(prompt, "] (?=Help)? : ")) {
  87. // if (prompt.substr(len - 14) == "] (?=Help)? : ") {
  88. proxy_activate();
  89. return;
  90. }
  91. }
  92. }
  93. */
  94. if (at_command_prompt(prompt)) {
  95. proxy_activate();
  96. return;
  97. }
  98. }
  99. // Ok...
  100. if (talk_direct) to_server(input);
  101. /*
  102. if (emit_client_input)
  103. emit_client_input(line);
  104. */
  105. }
  106. void Director::server_line(const std::string &line,
  107. const std::string &raw_line) {
  108. // check for if we entered game/left game
  109. if (line.find("TradeWars Game Server ") != std::string::npos) {
  110. to_client("\rTradeWars Proxy v2++ READY (~ or ESC to activate)\n\r");
  111. /*
  112. There's a delay here when I save the game data.
  113. I've moved it futher down. Hide it at a prompt, so it isn't so
  114. noticeable.
  115. */
  116. // reset "active game" -- we're at the TWGS main menu
  117. }
  118. if (line.find("Selection (? for menu): ") != std::string::npos) {
  119. char ch = line[line.length() - 1];
  120. if (ch >= 'A' && ch < 'Q') {
  121. if ((game) && (game != ch)) {
  122. galaxy.save();
  123. }
  124. game = ch;
  125. BUGZ_LOG(warning) << "GAME " << game << " activated!";
  126. // TODO: Load game data
  127. galaxy.game = game;
  128. galaxy.username = username;
  129. galaxy.load();
  130. // YAML loaded, set sensible default config values (if missing).
  131. if (!galaxy.config["display_lines"]) {
  132. galaxy.config["display_lines"] = 20;
  133. }
  134. galaxy.meta["help"]["display_lines"] =
  135. "Number of report lines to display";
  136. if (!galaxy.config["burnt_percent"]) {
  137. galaxy.config["burnt_percent"] = 40;
  138. }
  139. galaxy.meta["help"]["burnt_percent"] = "Ignore ports below this percent";
  140. }
  141. // not needed (handled by above Game Server check).
  142. if (ch == 'Q') {
  143. if (game) {
  144. // TODO: Save galaxy data
  145. galaxy.save();
  146. }
  147. game = 0;
  148. galaxy.reset();
  149. }
  150. }
  151. if (game) {
  152. // in-game parsing here.
  153. /*
  154. ____ _ _
  155. / ___| ___ _ ____ _____ _ __ | | (_)_ __ ___
  156. \___ \ / _ \ '__\ \ / / _ \ '__| | | | | '_ \ / _ \
  157. ___) | __/ | \ V / __/ | | |___| | | | | __/
  158. |____/ \___|_| \_/ \___|_| |_____|_|_| |_|\___|
  159. ____ _
  160. | _ \ __ _ _ __ ___(_)_ __ __ _
  161. | |_) / _` | '__/ __| | '_ \ / _` |
  162. | __/ (_| | | \__ \ | | | | (_| |
  163. |_| \__,_|_| |___/_|_| |_|\__, |
  164. |___/
  165. This is where all of the server lines are gleaned for all the
  166. information that we can get out of them.
  167. // When activating the computer
  168. SP: [Command [TL=00:00:00]:[926] (?=Help)? : ]
  169. Sector: 926
  170. CI: [c]
  171. SL: [Command [TL=00:00:00]:[926] (?=Help)? : C]
  172. SL: [<Computer>]
  173. SL: []
  174. SL: [<Computer activated>]
  175. SL: []
  176. SP: [Computer command [TL=00:00:00]:[926] (?=Help)? ]
  177. // deactivating the computer
  178. SL: [Computer command [TL=00:00:00]:[926] (?=Help)? Q]
  179. SL: []
  180. SL: [<Computer deactivated>]
  181. SL: []
  182. */
  183. if (line == "<Port>") {
  184. SL_parser = SF_portline;
  185. }
  186. /*
  187. if (startswith(line, " Items Status Trading % of max OnBoard"))
  188. SL_parser = SF_portline;
  189. */
  190. if (in(line, "==>")) SL_parser = SF_densityline;
  191. if (startswith(line, "Sector : ")) SL_parser = SF_sectorline;
  192. if (line == ": ") SL_parser = SF_cimline;
  193. if (line == "<Info>") SL_parser = SF_infoline;
  194. if (startswith(line, "What sector is the port in? [")) {
  195. // Computer Port Report
  196. // SL: [What sector is the port in? [611] 4]
  197. size_t pos = line.rfind(' ');
  198. computer_port_sector = stoi(line.substr(pos));
  199. SL_parser = SF_computer_portline;
  200. }
  201. }
  202. if (SL_parser) {
  203. SL_parser(line);
  204. }
  205. /*
  206. if (emit_server_line) {
  207. emit_server_line(line);
  208. }
  209. */
  210. if (chain) {
  211. chain->server_line(line, raw_line);
  212. }
  213. }
  214. void Director::server_prompt(const std::string &prompt,
  215. const std::string &raw_prompt) {
  216. current_prompt = prompt;
  217. current_raw_prompt = raw_prompt;
  218. if (game) {
  219. if (prompt == "Selection (? for menu): ") {
  220. galaxy.save();
  221. game = 0;
  222. galaxy.reset();
  223. }
  224. // in-game parsing here.
  225. if (startswith(prompt, "Command [") && endswith(prompt, "] (?=Help)? : ")) {
  226. std::string sector_text;
  227. size_t before, after;
  228. before = prompt.find("]:[") + 3;
  229. after = prompt.find("] (?=Help)");
  230. sector_text = prompt.substr(before, after - before);
  231. current_sector = stoi(sector_text);
  232. BUGZ_LOG(fatal) << "Sector: " << sector_text;
  233. }
  234. }
  235. /*
  236. if (emit_server_prompt)
  237. emit_server_prompt(prompt);
  238. */
  239. if (chain) chain->server_prompt(prompt);
  240. }
  241. void Director::build_menu(void) {
  242. main_menu = std::make_shared<MenuDispatch>(*this);
  243. MenuDispatch *md = static_cast<MenuDispatch *>(&(*main_menu));
  244. md->menu_box_color = "\x1b[1;33;44m";
  245. md->menu_text_color = "\x1b[1;37;44m";
  246. md->menu_title = "Proxy Menu";
  247. md->menu_options_color = "\x1b[1;36;40m";
  248. md->menu_prompt =
  249. "\x1b[0;31;40m\xdb\xb2\xb1\xb0 \x1b[31;40mRED "
  250. "\x1b[32;40mGREEN\x1b[30;42m\xdb\xb2\xb1\xb0 \x1b[0m : ";
  251. md->lazy = true;
  252. md->menu = {{"C", "Configure"},
  253. {"D", "Display Report"},
  254. {"E", "Export Data/Save"},
  255. {"I", "Information"},
  256. {"P", "Port CIM"},
  257. {"W", "Warp CIM"},
  258. {"T", "Trading Report (same as D)"},
  259. {"S", "Scripts"},
  260. {"X", "eXit"}};
  261. md->setNotify([this]() { this->menu_choice(); });
  262. cim = std::make_shared<CIMDispatch>(*this);
  263. cim->setNotify([this]() { this->cim_done(); });
  264. }
  265. void Director::proxy_activate(void) {
  266. active = true; // yes, set keep-alive timer.
  267. to_server(" "); // start keep-alive timer.
  268. // set other values we need
  269. talk_direct = false;
  270. show_client = false;
  271. /*
  272. Wait a minute .. this might be confusing.
  273. Shouldn't I send them the current prompt?
  274. Just in case we abort in the middle of something?!?
  275. */
  276. old_prompt = current_prompt;
  277. old_raw_prompt = current_raw_prompt;
  278. to_client("\x1b[0m\n\r");
  279. /*
  280. ╔══════════════════════════════╗
  281. ║ TradeWars Proxy Active ║
  282. ╚══════════════════════════════╝
  283. -=>
  284. */
  285. Boxes box(30, 1, true);
  286. box.boxcolor = "\x1b[1;33;44m";
  287. box.textcolor = "\x1b[1;33;44m";
  288. to_client(box.top());
  289. std::string output = " TradeWars Proxy \x1b[5mActive\x1b[0;1;33;44m ";
  290. to_client(box.row(output));
  291. to_client(box.bottom());
  292. chain = main_menu;
  293. main_menu->activate();
  294. /*
  295. // Using InputDispatch -- and see have_input
  296. std::shared_ptr<Dispatch> readline = std::make_shared<InputDispatch>(*this);
  297. chain = readline;
  298. InputDispatch *id = static_cast<InputDispatch *>(&(*readline));
  299. id->prompt = "\x1b[0m\x1b[1;33;44m-=>\x1b[0m \x1b[1;37;44m";
  300. id->max_length = 15;
  301. id->setNotify([this]() { this->have_input(); });
  302. readline->activate();
  303. */
  304. }
  305. void Director::menu_choice(void) {
  306. MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));
  307. if (md) {
  308. if (md->input.empty()) {
  309. to_client("Menu aborted.\n\r");
  310. proxy_deactivate();
  311. return;
  312. } else {
  313. switch (md->input[0]) {
  314. case 'C': // configure
  315. config_edit();
  316. return;
  317. break;
  318. case 'D':
  319. case 'T': // display trading report
  320. {
  321. auto pptv = galaxy.find_best_trades();
  322. std::string output;
  323. galaxy.sort_port_pair_type(pptv);
  324. int max_display = 20;
  325. if (galaxy.config["display_lines"])
  326. max_display = galaxy.config["display_lines"].as<int>();
  327. else
  328. galaxy.config["display_lines"] = max_display;
  329. if ((max_display <= 0) || (max_display > 255)) {
  330. max_display = 255;
  331. galaxy.config["display_lines"] = 255;
  332. }
  333. const int per_line = 5;
  334. int count = 0;
  335. int line = 0;
  336. for (auto const &ppt : pptv) {
  337. output = str(boost::format("%1$5d:%2$-5d(%3$d) ") % ppt.s1 %
  338. ppt.s2 % ppt.type);
  339. to_client(output);
  340. ++count;
  341. if (count == per_line) {
  342. count = 0;
  343. to_client("\n\r");
  344. ++line;
  345. }
  346. if (line == max_display) break;
  347. }
  348. if (count != 0) to_client("\n\r");
  349. // We got < 5 lines, and max_display is > 5. Offer suggestion:
  350. if ((line < 5) && (max_display > 5)) {
  351. // suggestion:
  352. to_client(
  353. "HINT: For more lines, try reducing the burnt_percent?\n\r");
  354. }
  355. } break;
  356. case 'E': // Export Data/Save
  357. to_client("Saving...");
  358. galaxy.save();
  359. to_client("\rSaved....\n\r");
  360. break;
  361. case 'I': // Information
  362. information();
  363. break;
  364. case 'P': // Port CIM
  365. // Since we're adding/updating, we don't lose our
  366. // type 0 ports. Type 9 stays at 9.
  367. chain = cim;
  368. to_server("^RQ");
  369. {
  370. std::string text = str(boost::format("Port CIM Report (%1%)\n\r") %
  371. galaxy.ports.size());
  372. // to_client("Port CIM Report\n\r");
  373. to_client(text);
  374. }
  375. chain->activate();
  376. return;
  377. break;
  378. case 'W': // Warp CIM
  379. chain = cim;
  380. to_server("^IQ");
  381. {
  382. std::string text = str(boost::format("Warp CIM Report (%1%)\n\r") %
  383. galaxy.warps.size());
  384. // to_client("Warp CIM Report\n\r");
  385. to_client(text);
  386. }
  387. chain->activate();
  388. return;
  389. break;
  390. // case 'T': // Trading Report
  391. // break;
  392. case 'S': // Scripts
  393. {
  394. init_scripts_menu();
  395. chain = scripts_menu;
  396. chain->activate();
  397. return;
  398. } break;
  399. case 'X': // Exit
  400. proxy_deactivate();
  401. return;
  402. }
  403. /*
  404. std::string text = str(
  405. boost::format("Back from Menu [%1%] was selected.\n\r") %
  406. md->input);
  407. to_client(text);
  408. */
  409. md->activate();
  410. }
  411. }
  412. }
  413. MenuDispatch *Director::init_scripts_menu(void) {
  414. MenuDispatch *md;
  415. if (scripts_menu) {
  416. md = dynamic_cast<MenuDispatch *>(&(*scripts_menu));
  417. return md;
  418. } else {
  419. scripts_menu = std::make_shared<MenuDispatch>(*this);
  420. md = static_cast<MenuDispatch *>(&(*scripts_menu));
  421. md->menu_box_color = "\x1b[0;32;40m";
  422. md->menu_text_color = "\x1b[1;32;40m";
  423. md->menu_title = "Scripts Menu";
  424. md->menu_options_color = "\x1b[1;32;40m";
  425. md->lazy = false;
  426. md->menu_prompt = " SCRIPT : ";
  427. md->menu = {{"!", "Terror"}, {"T", "Trade"}, {"U", "Upgrade Planet Pants"}};
  428. md->setNotify([this]() { this->scripts_done(); });
  429. return md;
  430. }
  431. }
  432. void Director::scripts_done(void) {
  433. // Was script selected? If so, run it!
  434. // otherwise, back to the menu we go...
  435. MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*scripts_menu));
  436. if (md) {
  437. if (md->input.empty()) {
  438. to_client("Scripts aborted.\n\r");
  439. scripts_menu.reset();
  440. proxy_deactivate();
  441. return;
  442. } else {
  443. switch (md->input[0]) {
  444. case 'T': // Trade
  445. {
  446. script = std::make_shared<ScriptTrader>(*this);
  447. ScriptTrader *ts = static_cast<ScriptTrader *>(&((*script)));
  448. ts->setNotify([this]() { this->proxy_deactivate(); });
  449. // Set parameters
  450. auto found = galaxy.find_trades(current_sector, false);
  451. if (found.empty()) {
  452. to_client(
  453. "No Trades found. Port burnt (CONFIG: lower burnt_percent?) "
  454. "or no ports around.\n\r");
  455. proxy_deactivate();
  456. return;
  457. }
  458. // sort first?
  459. galaxy.sort_port_pair_type(found);
  460. BUGZ_LOG(fatal) << "Found " << found.size() << " possible trade(s).";
  461. BUGZ_LOG(fatal) << found[0].s1 << "," << found[0].s2 << " : "
  462. << found[0].type;
  463. ts->port[0] = found[0].s1;
  464. ts->port[1] = found[0].s2;
  465. ts->type = found[0].type;
  466. chain = script;
  467. chain->activate();
  468. return;
  469. } break;
  470. case '!': {
  471. auto best = galaxy.find_closest(current_sector);
  472. if (best.type != 0) {
  473. std::string text =
  474. str(boost::format("Best/Closest: %1% with %2% & %3%\n\r") %
  475. best.type % best.s1 % best.s2);
  476. to_client(text);
  477. } else {
  478. to_client("I don't see any best trades.\n\r");
  479. }
  480. } break;
  481. }
  482. }
  483. }
  484. proxy_activate();
  485. // And to end scripts, we do .. what exactly?
  486. // DEBUG: Ok, why does everything work OK if I reset the scripts_menu
  487. // here?? probably do want to destroy scripts here. ;)
  488. // scripts_menu.reset();
  489. // proxy_deactivate();
  490. }
  491. /**
  492. * @brief Setup Config Input
  493. *
  494. * @return DispatchInput*
  495. */
  496. InputDispatch *Director::init_config_input(void) {
  497. InputDispatch *id;
  498. if (config_input) {
  499. // Yes, it has been setup before.
  500. id = dynamic_cast<InputDispatch *>(&(*config_input));
  501. id->prompt = "Config => ";
  502. id->max_length = 3;
  503. config_item.clear();
  504. return id;
  505. } else {
  506. // set it up
  507. config_input = std::make_shared<InputDispatch>(*this);
  508. id = static_cast<InputDispatch *>(&(*config_input));
  509. id->prompt = "Config => ";
  510. id->max_length = 3;
  511. id->setNotify([this]() { this->config_have_input(); });
  512. config_item.clear();
  513. return id;
  514. }
  515. }
  516. void Director::config_edit(void) {
  517. // display current config
  518. std::string menu_box_color = "\x1b[1;33;44m";
  519. std::string menu_text_color = "\x1b[1;37;44m";
  520. auto abox = Boxes::alert(" Configuration: ", menu_box_color,
  521. menu_text_color, 20, 1, true);
  522. for (auto line : abox) {
  523. to_client(line);
  524. }
  525. // to_client("Configuration:\n\r");
  526. int item = 1;
  527. for (auto const &cfg : galaxy.config) {
  528. std::string output = str(boost::format("%1$2d %2$20s: %3$s\n\r") % item %
  529. cfg.first % cfg.second);
  530. to_client(output);
  531. ++item;
  532. }
  533. to_client("Enter number to edit, blank to exit.\n\r");
  534. // setup call to config_input:
  535. InputDispatch *id = init_config_input();
  536. chain = config_input;
  537. id->activate();
  538. // to return to the menu:
  539. // MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));
  540. // md->activate();
  541. }
  542. void Director::config_have_input(void) {
  543. InputDispatch *id = dynamic_cast<InputDispatch *>(&(*config_input));
  544. if (config_item.empty()) {
  545. // This is a config menu selection
  546. if (id->input.empty()) {
  547. // We're done here. Return to menu.
  548. chain = main_menu;
  549. MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));
  550. md->activate();
  551. // destroy the input? yes.
  552. config_input.reset();
  553. return;
  554. } else {
  555. int item;
  556. try {
  557. item = stoi(id->input);
  558. } catch (const std::invalid_argument &e) {
  559. BUGZ_LOG(fatal) << e.what();
  560. item = 0;
  561. } catch (const std::out_of_range &e) {
  562. BUGZ_LOG(fatal) << e.what();
  563. item = 0;
  564. }
  565. if ((item < 1) || (item > (int)galaxy.config.size())) {
  566. // selection out of range - redisplay config menu
  567. config_edit();
  568. return;
  569. } else {
  570. int pos = 1;
  571. const YAML::Node &config = galaxy.config;
  572. for (auto const &c : config) {
  573. if (pos == item) {
  574. // got it!
  575. config_item = c.first.as<std::string>();
  576. std::string output =
  577. str(boost::format("%1% : %2%\n\r") % config_item %
  578. galaxy.meta["help"][config_item]);
  579. to_client(output);
  580. id->max_length = 30;
  581. id->prompt = "Change to => ";
  582. id->activate();
  583. return;
  584. };
  585. ++pos;
  586. }
  587. to_client("What? I didn't find that item?\n\r");
  588. config_edit();
  589. return;
  590. }
  591. }
  592. } else {
  593. // This is a config item edit
  594. if (id->input.empty()) {
  595. to_client("No change.\n\r");
  596. config_edit();
  597. return;
  598. } else {
  599. BUGZ_LOG(fatal) << "Config EDIT: " << config_item << " = " << id->input;
  600. galaxy.config[config_item] = id->input;
  601. config_edit();
  602. return;
  603. }
  604. }
  605. }
  606. void Director::have_input(void) {
  607. ++count;
  608. InputDispatch *id = dynamic_cast<InputDispatch *>(&(*chain));
  609. if (id) {
  610. std::string output =
  611. str(boost::format("Your Input (%2%): [%1%]\n\r") % id->input % count);
  612. to_client("\x1b[0m");
  613. to_client(output);
  614. } else {
  615. proxy_deactivate();
  616. return;
  617. }
  618. if (count > 3) {
  619. proxy_deactivate();
  620. } else {
  621. chain->activate();
  622. }
  623. }
  624. void Director::cim_done(void) {
  625. BUGZ_LOG(info) << "CIM done";
  626. chain = main_menu;
  627. main_menu->activate();
  628. }
  629. void Director::information(void) {
  630. std::string output;
  631. to_client("I currently know the following:\n\r");
  632. output = str(
  633. boost::format("Ports: %1%, Sectors: %2%, Config: %3%, Meta: %4%\n\r") %
  634. galaxy.ports.size() % galaxy.warps.size() % galaxy.config.size() %
  635. galaxy.meta.size());
  636. to_client(output);
  637. }
  638. void Director::proxy_deactivate(void) {
  639. active = false;
  640. // reset everything back to good state
  641. talk_direct = true;
  642. show_client = true;
  643. chain.reset();
  644. to_client("\n\r");
  645. to_client(current_raw_prompt);
  646. }
  647. /*
  648. Server Line Parsing Routines
  649. */
  650. void Director::SL_cimline(const std::string &line) {
  651. if (line == ": ENDINTERROG") {
  652. SL_parser = nullptr;
  653. return;
  654. }
  655. if (line == ": ") {
  656. // do I need to do anything special here for this?
  657. // Maybe -- We would save special ports that don't show up
  658. // (StarDock/Special) before. We don't know (at this point) if this is
  659. // warps or ports.
  660. return;
  661. }
  662. if (line.empty()) {
  663. SL_parser = nullptr;
  664. return;
  665. }
  666. // parse cimline
  667. // size_t pos = line.find('%');
  668. // std::string work = line;
  669. // if (pos == line.npos) {
  670. if (in(line, "%")) {
  671. // portcim
  672. struct port p = parse_portcim(line);
  673. if (p.sector == 0)
  674. BUGZ_LOG(fatal) << "portcim: FAIL [" << line << "]";
  675. else
  676. BUGZ_LOG(trace) << "portcim: " << p;
  677. galaxy.add_port(p);
  678. } else {
  679. // warpcim
  680. // BUGZ_LOG(fatal) << "warpcim: [" << line << "]";
  681. auto warps = split(line);
  682. sector_warps sw;
  683. for (auto const &w : warps) {
  684. if (sw.sector == 0) {
  685. sw.sector = stoi(w);
  686. } else {
  687. sw.add(stoi(w));
  688. }
  689. }
  690. BUGZ_LOG(trace) << "warpcim: " << sw;
  691. galaxy.add_warp(sw);
  692. }
  693. }
  694. void Director::SL_thiefline(const std::string &line) {
  695. size_t pos = line.find("Suddenly you're Busted!");
  696. bool busted = pos != line.npos;
  697. if (busted) {
  698. BUGZ_LOG(fatal) << "set bust";
  699. SL_parser = nullptr;
  700. } else {
  701. pos = line.find("(You realize the guards saw you last time!)");
  702. if (pos != line.npos) SL_parser = nullptr;
  703. }
  704. // Are those the two ways to exit from this state?
  705. }
  706. void Director::SL_sectorline(const std::string &line) {
  707. BUGZ_LOG(fatal) << "sectorline: [" << line << "]";
  708. if (line.empty()) {
  709. SL_parser = nullptr;
  710. } else {
  711. /*
  712. sectorline: [Sector : 926 in The Federation.]
  713. sectorline: [Beacon : FedSpace, FedLaw Enforced]
  714. sectorline: [Ports : Stargate Alpha I, Class 9 (Special) (StarDock)]
  715. sectorline: [Traders : Civilian phil, w/ 30 ftrs,]
  716. sectorline: [ in Star Stomper (Sverdlov Merchant Cruiser)]
  717. sectorline: [Warps to Sector(s) : 70 - 441 - 575 - 600 - 629 - 711]
  718. sectorline: [Warps to Sector(s) : 70 - (475) - 569]
  719. What can we get from Traders : line? Can we get if they are hostile
  720. to us? We need to respond to "is powering up weapons" ... We can
  721. react faster then a person can!
  722. "phil is powering up weapons systems!"
  723. Also the auto-attack ones Ferrengi -- we need to auto-respond Retreat.
  724. */
  725. if (in(line, "Sector :")) {
  726. current_sector = stoi(line.substr(10));
  727. BUGZ_LOG(warning) << "SECTOR: " << current_sector;
  728. }
  729. if (in(line, "Ports :")) {
  730. std::string port_class;
  731. size_t pos = line.find(", Class ");
  732. if (pos != std::string::npos) {
  733. pos += 8;
  734. int class_ = stoi(line.substr(pos));
  735. BUGZ_LOG(fatal) << "PORT: " << class_;
  736. galaxy.add_port(current_sector, class_);
  737. }
  738. }
  739. if (in(line, "Warps to Sector(s) :")) {
  740. std::string temp = line.substr(20);
  741. replace(temp, " - ", " ");
  742. // unexplored sectors ()
  743. // Should I track these?
  744. replace(temp, "(", "");
  745. replace(temp, ")", "");
  746. sector_warps sw;
  747. auto warps = split(temp);
  748. sw.sector = current_sector;
  749. for (auto const &w : warps) {
  750. sw.add(stoi(w));
  751. }
  752. BUGZ_LOG(fatal) << "WARPS: " << sw;
  753. galaxy.add_warp(sw);
  754. }
  755. }
  756. }
  757. void Director::SL_densityline(const std::string &line) {
  758. BUGZ_LOG(fatal) << "densityline: [" << line << "]";
  759. if (line.empty()) {
  760. SL_parser = nullptr;
  761. return;
  762. }
  763. // Ensure this really is a density scan and not something else
  764. if (!in(line, "Sector") || !in(line, "Warps") || !in(line, "NavHaz") ||
  765. !in(line, "Anom")) {
  766. BUGZ_LOG(fatal) << "densityline: Invalid line.";
  767. SL_parser = nullptr;
  768. return;
  769. }
  770. if (not galaxy.meta["density"]) {
  771. galaxy.meta["density"] = YAML::Node();
  772. }
  773. /*
  774. 0 1 2 3 4 5 6 7 8 9 10 11 12
  775. "Sector 55 ==> 0 Warps : 4 NavHaz : 0% Anom : No"
  776. "Sector ( 223) ==> 0 Warps : 3 NavHaz : 0% Anom : No"
  777. // Cleaned up line
  778. 0 1 2 3 4 5 6 7 8 9
  779. "Sector 55 ==> 0 Warps 4 NavHaz 0 Anom No"
  780. "Sector 223 ==> 0 Warps 3 NavHaz 0 Anom No"
  781. */
  782. std::string work = line;
  783. replace(work, ":", "");
  784. bool known = !in(work, "(");
  785. replace(work, "(", "");
  786. replace(work, ")", "");
  787. replace(work, "%", "");
  788. std::vector<std::string> dense = split(work);
  789. // Parse our data
  790. int sector = std::stoi(dense.at(1));
  791. int density = std::stoi(dense.at(3));
  792. int warps = std::stoi(dense.at(5));
  793. int navhaz = std::stoi(dense.at(7));
  794. bool anom = in(dense.at(9), "Yes");
  795. // Commit data
  796. BUGZ_LOG(warning) << "densityline: {sector=" << sector
  797. << " density=" << density << " warps=" << warps
  798. << " navhaz=" << navhaz << " anom=" << anom
  799. << " known=" << known << "}";
  800. if (galaxy.meta["density"][sector]) {
  801. galaxy.meta["density"][sector] = YAML::Node();
  802. }
  803. galaxy.meta["density"][sector]["density"] = density;
  804. galaxy.meta["density"][sector]["warps"] = warps;
  805. galaxy.meta["density"][sector]["navhaz"] = navhaz;
  806. galaxy.meta["density"][sector]["anom"] = anom;
  807. galaxy.meta["density"][sector]["known"] = known;
  808. // Add a check to see if density is greater than 500
  809. // Add datetime stamp
  810. }
  811. void Director::SL_portline(const std::string &line) {
  812. /*
  813. We take blank lines because we start at <Port>.
  814. Otherwise, we trigger on computer port requests.
  815. if (line.empty()) {
  816. SL_parser = nullptr;
  817. return;
  818. }
  819. */
  820. /*
  821. SL: [ Items Status Trading % of max OnBoard]
  822. SL: [ ----- ------ ------- -------- -------]
  823. SL: [Fuel Ore Buying 3000 100% 0]
  824. SL: [Organics Buying 3000 100% 0]
  825. SL: [Equipment Buying 3000 100% 0]
  826. SL: []
  827. SL: [Commerce report for: 03:51:56 PM Mon Oct 24, 2033 You can buy:]
  828. SL: [A Cargo holds : 650 credits / next hold 0]
  829. SL: [B Fighters : 233 credits per fighter 75]
  830. SL: [C Shield Points : 116 credits per point 100]
  831. SL: []
  832. */
  833. // BUGZ_LOG(info) << "portline : " << line;
  834. if (in(line, "%")) {
  835. // size_t pos = line.find('%');
  836. // if (pos != line.npos) {
  837. // Ok, this is a valid portline
  838. std::string work = line;
  839. replace(work, "Fuel Ore", "Fuel");
  840. auto parts = split(work);
  841. if (parts[0] == "Items") return;
  842. char c = tolower(parts[0][0]);
  843. int pos;
  844. char foe[4] = "foe";
  845. for (pos = 0; pos < 3; ++pos) {
  846. if (c == foe[pos]) break;
  847. }
  848. int amount = stoi(parts[2]);
  849. int percent = stoi(parts[3]);
  850. // update port
  851. auto port = galaxy.ports.find(current_sector);
  852. if (port != galaxy.ports.end()) {
  853. port->second.amount[pos] = amount;
  854. port->second.percent[pos] = percent;
  855. }
  856. /*
  857. BUGZ_LOG(fatal) << "portline split:";
  858. for (auto const p : parts) {
  859. BUGZ_LOG(fatal) << p;
  860. }
  861. */
  862. // Here's the end:
  863. if (parts[0] == "Equipment") SL_parser = nullptr;
  864. // BUGZ_LOG(fatal) << "portline split : [" << parts << "]";
  865. }
  866. }
  867. void Director::SL_warpline(const std::string &line) {
  868. if (line.empty()) {
  869. SL_parser = nullptr;
  870. return;
  871. }
  872. // process warp line
  873. BUGZ_LOG(fatal) << "warpline: [" << line << "]";
  874. }
  875. void Director::SL_infoline(const std::string &line) {
  876. static int state;
  877. if (line == "<Info>") {
  878. state = 0;
  879. galaxy.meta["info"] = YAML::Node();
  880. }
  881. if (line.empty()) {
  882. ++state;
  883. if (state == 2) {
  884. SL_parser = nullptr;
  885. // clear out the existing ship data
  886. galaxy.meta["ship"] = YAML::Node();
  887. // process the parsed information in meta["info"]
  888. if (galaxy.meta["info"]["Total Holds"]) {
  889. std::string work = galaxy.meta["info"]["Total Holds"].as<std::string>();
  890. replace(work, "Fuel Ore", "Fuel");
  891. auto parts = split(work, " - ");
  892. int total_holds = stoi(parts[0]);
  893. BUGZ_LOG(fatal) << "total holds: " << total_holds;
  894. auto contents = split(parts[1]);
  895. for (auto const &hold : contents) {
  896. auto hold_amount = split(hold, "=");
  897. BUGZ_LOG(fatal) << hold_amount[0] << " with " << hold_amount[1];
  898. std::string key = hold_amount[0];
  899. str_tolower(key);
  900. // equipment = e
  901. // organics = o
  902. // fuel = f
  903. // colonists = c
  904. // empty = empty
  905. if (key != "empty") {
  906. key = key[0];
  907. }
  908. galaxy.meta["ship"]["holds"][key] = stoi(hold_amount[1]);
  909. }
  910. galaxy.meta["ship"]["holds"]["total"] = total_holds;
  911. }
  912. if (galaxy.meta["info"]["Turns to Warp"]) {
  913. int warp_turns = galaxy.meta["info"]["Turns to Warp"].as<int>();
  914. BUGZ_LOG(fatal) << "Turns to Warp: " << warp_turns;
  915. galaxy.meta["ship"]["warp_turns"] = warp_turns;
  916. }
  917. if (galaxy.meta["info"]["LongRange Scan"]) {
  918. std::string scanner_text =
  919. galaxy.meta["info"]["LongRange Scan"].as<std::string>();
  920. char scanner = scanner_text[0];
  921. BUGZ_LOG(fatal) << "Scanner: " << scanner;
  922. galaxy.meta["ship"]["scanner"] = scanner;
  923. }
  924. // turns isn't ship specific
  925. if (galaxy.meta["info"]["Turns left"]) {
  926. // OR this could be "Unlimited" !!!
  927. std::string text = galaxy.meta["info"]["Turns left"].as<std::string>();
  928. if (text == "Unlimited") {
  929. galaxy.meta["turns"] = -1;
  930. } else {
  931. int turns =
  932. stoi(text); // galaxy.meta["info"]["Turns left"].as<int>();
  933. BUGZ_LOG(fatal) << "Turns left: " << turns;
  934. galaxy.meta["turns"] = turns;
  935. }
  936. }
  937. if (galaxy.meta["info"]["Current Sector"]) {
  938. int sector = galaxy.meta["info"]["Current Sector"].as<int>();
  939. BUGZ_LOG(fatal) << "Sector: " << sector;
  940. // it should already be sector ...
  941. current_sector = sector;
  942. }
  943. if (galaxy.meta["info"]["Credits"]) {
  944. std::string credit_text =
  945. galaxy.meta["info"]["Credits"].as<std::string>();
  946. replace(credit_text, ",", "");
  947. int credits = stoi(credit_text);
  948. galaxy.meta["credits"] = credits;
  949. BUGZ_LOG(fatal) << "Credits: " << credits;
  950. }
  951. }
  952. return;
  953. }
  954. // info to parse:
  955. size_t pos = line.find(" : ");
  956. if ((!endswith(line, " : ")) && (pos != line.npos)) {
  957. std::string key = line.substr(0, pos);
  958. // Ferrengi ships don't have date built
  959. std::string value = line.substr(pos + 3);
  960. trim(key);
  961. trim(value);
  962. galaxy.meta["info"][key] = value;
  963. BUGZ_LOG(fatal) << "Info: " << key << " : " << value;
  964. }
  965. }
  966. void Director::SL_computer_portline(const std::string &line) {
  967. if (startswith(line, "What sector is the port in?"))
  968. computer_port_done = false;
  969. if (line == "I have no information about a port in that sector.") {
  970. computer_port_sector = 0;
  971. SL_parser = nullptr;
  972. return;
  973. }
  974. if (!computer_port_done) {
  975. if (in(line, "Fuel Ore")) computer_port_done = true;
  976. if (in(line, "Cargo holds")) {
  977. // If I want to scan the class type 0 ports:
  978. // computer_port_done = true;
  979. // otherwise:
  980. SL_parser = nullptr;
  981. return;
  982. }
  983. }
  984. if (computer_port_done) {
  985. if (line.empty()) {
  986. SL_parser = nullptr;
  987. return;
  988. }
  989. // scan for items of interest
  990. // SL: [Fuel Ore Buying 810 100% 0]
  991. // SL: [Organics Buying 856 57% 0]
  992. // SL: [Equipment Selling 922 44% 0]
  993. std::string work = line;
  994. replace(work, "Fuel Ore", "Fuel");
  995. replace(work, "%", "");
  996. auto parts = split(work);
  997. char c = tolower(parts[0][0]);
  998. int pos;
  999. char foe[4] = "foe";
  1000. for (pos = 0; pos < 3; ++pos) {
  1001. if (c == foe[pos]) break;
  1002. }
  1003. int amount = stoi(parts[2]);
  1004. int percent = stoi(parts[3]);
  1005. // update port
  1006. auto port = galaxy.ports.find(computer_port_sector);
  1007. if (port != galaxy.ports.end()) {
  1008. BUGZ_LOG(info) << "COM PORT " << computer_port_sector << " " << c << " "
  1009. << amount << " " << percent;
  1010. port->second.amount[pos] = amount;
  1011. port->second.percent[pos] = percent;
  1012. }
  1013. }
  1014. }