director.cpp 27 KB

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