director.cpp 33 KB

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