director.cpp 33 KB

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