director.cpp 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588
  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. SF_planetline = [this](const std::string &s) { this->SL_planetline(s); };
  36. build_menu();
  37. }
  38. Director::~Director() { BUGZ_LOG(warning) << "Director::~Director()"; }
  39. void Director::client_input(const std::string &input) {
  40. std::string output = repr(input);
  41. if (chain) {
  42. BUGZ_LOG(trace) << chain->name << " CI: [" << output << "]";
  43. chain->client_input(input);
  44. return;
  45. } else {
  46. BUGZ_LOG(trace) << "CI: [" << output << "]";
  47. }
  48. // If we're already active, don't try to activate.
  49. if (active) {
  50. if (input == "Q" || input == "q") proxy_deactivate();
  51. return;
  52. } else if (input == "\x1b" || input == "~") {
  53. std::string &prompt = current_prompt;
  54. BUGZ_LOG(trace) << "ACTIVATE prompt shows: [" << prompt << "]";
  55. if (prompt == "Selection (? for menu): ") {
  56. to_client(
  57. "\n\rThere's not much we can do here. Activate in-game at a "
  58. "Command prompt.\n\r");
  59. to_client(current_raw_prompt);
  60. return;
  61. }
  62. // easter-eggs:
  63. if (prompt == "Enter your choice: ") {
  64. // ANSIColor c1(COLOR::CYAN, ATTR::BOLD);
  65. // ANSIColor c2(COLOR::WHITE, ATTR::BOLD);
  66. ANSIColor c1("BOLD CYAN");
  67. ANSIColor c2("BOLD WHITE");
  68. to_client(std::string("\n\r") + c1() + "I'd choose " + c2() + "`T`" +
  69. c1() + ", but that's how I was coded.\n\r");
  70. to_client(current_raw_prompt);
  71. return;
  72. }
  73. // easter-egg
  74. if (prompt == "[Pause]") {
  75. ANSIColor c1("BOLD CYAN"); // COLOR::CYAN, ATTR::BOLD);
  76. to_client(std::string(" ") + c1() + "PAWS" + reset() + "\n\r");
  77. to_client(current_raw_prompt);
  78. return;
  79. }
  80. if (at_planet_prompt(prompt)) {
  81. // future: If activated at planet menu, activate the planet upgrade
  82. // script! (Maybe). If I can figure out what planet it is/and where.
  83. to_client("\n\r\x1b[0mFUTURE: Activate the planet upgrade script.\n\r");
  84. to_client(current_raw_prompt);
  85. return;
  86. }
  87. if (at_command_prompt(prompt)) {
  88. proxy_activate();
  89. return;
  90. }
  91. } else if (input == "\x01" || input == "\x02" || input == "\x03") {
  92. // MACROS
  93. std::string macro;
  94. macro.assign(1, input[0] + 'A' - 1);
  95. std::string to_send;
  96. if (galaxy.meta.contains("macros") &&
  97. galaxy.meta["macros"].contains(macro)) {
  98. to_send = galaxy.meta["macros"][macro].get<std::string>();
  99. BUGZ_LOG(fatal) << "Sending Macro " << macro << ": [" << to_send << "]";
  100. replace(to_send, "^", "\r");
  101. to_server(to_send, "MACRO");
  102. return;
  103. }
  104. }
  105. // Ok...
  106. if (talk_direct) to_server(input, "Director::client_input");
  107. }
  108. void Director::server_line(const std::string &line,
  109. const std::string &raw_line) {
  110. // check for if we entered game/left game
  111. SL_history.push_back(line);
  112. if (!chain) BUGZ_LOG(info) << "SL: [" << line << "]";
  113. if (line.find("TradeWars Game Server ") != std::string::npos) {
  114. // Inject our proxy activation message
  115. to_client("\rTradeWars Proxy v2++ READY (~ or ESC to activate)\n\r");
  116. /*
  117. There's a delay here when I save the game data.
  118. I've moved it futher down. Hide it at a prompt, so it isn't so
  119. noticeable.
  120. */
  121. // reset "active game" -- we're at the TWGS main menu
  122. }
  123. if (line == "Trade Wars 2002 Win32 module now loading.") {
  124. // This happens if the game is customized with ANSI/menus/etc.
  125. // We're not detecting the Selection, go find what was selected.
  126. auto find = SL_history.rbegin();
  127. ++find;
  128. while (find != SL_history.rend()) {
  129. if (*find != "") {
  130. std::string last_SL = *find;
  131. char ch = last_SL[last_SL.length() - 1];
  132. if ((game) && (game != ch)) {
  133. galaxy.save();
  134. }
  135. game = ch;
  136. BUGZ_LOG(warning) << "GAME " << game << " activated!";
  137. galaxy.game = game;
  138. galaxy.username = username;
  139. galaxy.load();
  140. // YAML loaded, set sensible default config values (if missing).
  141. if (!galaxy.config.contains("display_lines")) {
  142. galaxy.config["display_lines"] = 20;
  143. }
  144. galaxy.meta["help"]["display_lines"] =
  145. "Number of report lines to display";
  146. if (!galaxy.config.contains("burnt_percent")) {
  147. galaxy.config["burnt_percent"] = 40;
  148. }
  149. galaxy.meta["help"]["burnt_percent"] =
  150. "Don't display ports in report below this percent";
  151. break;
  152. }
  153. ++find;
  154. }
  155. }
  156. if (line.find("Selection (? for menu): ") != std::string::npos) {
  157. char ch = line[line.length() - 1];
  158. if (ch >= 'A' && ch < 'Q') {
  159. if ((game) && (game != ch)) {
  160. galaxy.save();
  161. }
  162. game = ch;
  163. BUGZ_LOG(warning) << "GAME " << game << " activated!";
  164. // TODO: Load game data
  165. galaxy.game = game;
  166. galaxy.username = username;
  167. galaxy.load();
  168. // YAML loaded, set sensible default config values (if missing).
  169. if (!galaxy.config.contains("display_lines")) {
  170. galaxy.config["display_lines"] = 20;
  171. }
  172. galaxy.meta["help"]["display_lines"] =
  173. "Number of report lines to display";
  174. if (!galaxy.config.contains("burnt_percent")) {
  175. galaxy.config["burnt_percent"] = 40;
  176. }
  177. galaxy.meta["help"]["burnt_percent"] =
  178. "Don't display ports in report below this percent";
  179. }
  180. // not needed (handled by above Game Server check).
  181. if (ch == 'Q') {
  182. if (game) {
  183. // TODO: Save galaxy data
  184. galaxy.save();
  185. }
  186. game = 0;
  187. galaxy.reset();
  188. }
  189. }
  190. if (game) {
  191. // in-game parsing here.
  192. /*
  193. ____ _ _
  194. / ___| ___ _ ____ _____ _ __ | | (_)_ __ ___
  195. \___ \ / _ \ '__\ \ / / _ \ '__| | | | | '_ \ / _ \
  196. ___) | __/ | \ V / __/ | | |___| | | | | __/
  197. |____/ \___|_| \_/ \___|_| |_____|_|_| |_|\___|
  198. ____ _
  199. | _ \ __ _ _ __ ___(_)_ __ __ _
  200. | |_) / _` | '__/ __| | '_ \ / _` |
  201. | __/ (_| | | \__ \ | | | | (_| |
  202. |_| \__,_|_| |___/_|_| |_|\__, |
  203. |___/
  204. This is where all of the server lines are gleaned for all the
  205. information that we can get out of them.
  206. // When activating the computer
  207. SP: [Command [TL=00:00:00]:[926] (?=Help)? : ]
  208. Sector: 926
  209. CI: [c]
  210. SL: [Command [TL=00:00:00]:[926] (?=Help)? : C]
  211. SL: [<Computer>]
  212. SL: []
  213. SL: [<Computer activated>]
  214. SL: []
  215. SP: [Computer command [TL=00:00:00]:[926] (?=Help)? ]
  216. // deactivating the computer
  217. SL: [Computer command [TL=00:00:00]:[926] (?=Help)? Q]
  218. SL: []
  219. SL: [<Computer deactivated>]
  220. SL: []
  221. */
  222. if (line == "<Port>") {
  223. SL_parser = SF_portline;
  224. }
  225. if (line ==
  226. " Corporate Planet Scan "
  227. " " ||
  228. line ==
  229. " Personal Planet Scan "
  230. " ") {
  231. SL_parser = SF_planetline;
  232. }
  233. /*
  234. if (startswith(line, " Items Status Trading % of max OnBoard"))
  235. SL_parser = SF_portline;
  236. */
  237. if (endswith(line, "Relative Density Scan")) {
  238. galaxy.dscan.reset(current_sector);
  239. SL_parser = SF_densityline;
  240. }
  241. if (startswith(line, "Sector : ")) SL_parser = SF_sectorline;
  242. if (line == ": ") SL_parser = SF_cimline;
  243. if (line == "<Info>") SL_parser = SF_infoline;
  244. if (startswith(line, "What sector is the port in? [")) {
  245. // Computer Port Report
  246. // SL: [What sector is the port in? [611] 4]
  247. size_t pos = line.rfind(' ');
  248. computer_port_sector = stoi(line.substr(pos));
  249. SL_parser = SF_computer_portline;
  250. }
  251. }
  252. if (SL_parser) {
  253. SL_parser(line);
  254. }
  255. if (chain) {
  256. BUGZ_LOG(info) << chain->name << " SL: [" << line << "]";
  257. chain->server_line(line, raw_line);
  258. }
  259. }
  260. void Director::server_prompt(const std::string &prompt,
  261. const std::string &raw_prompt) {
  262. current_prompt = prompt;
  263. current_raw_prompt = raw_prompt;
  264. if (!chain) {
  265. std::string temp = repr(prompt);
  266. BUGZ_LOG(trace) << "SP: [" << temp << "]";
  267. }
  268. if (game) {
  269. if (prompt == "Selection (? for menu): ") {
  270. galaxy.save();
  271. game = 0;
  272. galaxy.reset();
  273. }
  274. // in-game parsing here.
  275. // if (startswith(prompt, "Command [") && endswith(prompt, "] (?=Help)? :
  276. // ")) {
  277. if (at_command_prompt(prompt)) {
  278. std::string sector_text;
  279. size_t before, after;
  280. before = prompt.find("]:[") + 3;
  281. after = prompt.find("] (?=Help)");
  282. sector_text = prompt.substr(before, after - before);
  283. current_sector = stoi(sector_text);
  284. BUGZ_LOG(info) << "current_sector = " << current_sector;
  285. }
  286. }
  287. /*
  288. if (emit_server_prompt)
  289. emit_server_prompt(prompt);
  290. */
  291. if (chain) {
  292. std::string output = repr(prompt);
  293. BUGZ_LOG(info) << chain->name << " SP: [" << output << "]";
  294. chain->server_prompt(prompt);
  295. }
  296. }
  297. void Director::build_menu(void) {
  298. main_menu = std::make_shared<MenuDispatch>(*this, "MenuMain");
  299. MenuDispatch *md = static_cast<MenuDispatch *>(&(*main_menu));
  300. ANSIColor bcolor(COLOR::YELLOW, COLOR::BLUE, ATTR::BOLD);
  301. ANSIColor tcolor(COLOR::WHITE, COLOR::BLUE, ATTR::BOLD);
  302. ANSIColor mocolor(COLOR::CYAN, ATTR::BOLD);
  303. md->menu_box_color = bcolor(); // "\x1b[1;33;44m";
  304. md->menu_text_color = tcolor(); // "\x1b[1;37;44m";
  305. md->menu_title = "Proxy Menu";
  306. md->menu_options_color = mocolor(); // "\x1b[1;36;40m";
  307. ANSIColor by{1, 33};
  308. ANSIColor cyan{36};
  309. ANSIColor bg{1, 32};
  310. std::string prompt = by() + "M" + cyan() + "ain " + by() + "P" + cyan() +
  311. "roxy " + bg() + "=>" + reset() + " ";
  312. md->menu_prompt = prompt; // "Main Proxy => ";
  313. // "\x1b[0;31;40m\xdb\xb2\xb1\xb0 \x1b[31;40mRED "
  314. // "\x1b[32;40mGREEN\x1b[30;42m\xdb\xb2\xb1\xb0 \x1b[0m : ";
  315. md->lazy = true;
  316. md->menu = {{"C", "Configure"}, {"D", "Display Report"},
  317. {"E", "Export Data/Save"}, {"I", "Information"},
  318. {"P", "Port CIM"}, {"W", "Warp CIM"},
  319. {"M", "Macros"}, {"T", "Trading Report (same as D)"},
  320. {"S", "Scripts"}, {"X", "eXit"}};
  321. md->setNotify([this]() { this->menu_choice(); });
  322. cim = std::make_shared<CIMDispatch>(*this, "CIMDirector");
  323. cim->setNotify([this]() { this->cim_done(); });
  324. }
  325. void Director::proxy_activate(void) {
  326. active = true; // yes, set keep-alive timer.
  327. to_server(" ", "Director::proxy_active()"); // start keep-alive timer.
  328. // set other values we need
  329. talk_direct = false;
  330. show_client = false;
  331. /*
  332. Wait a minute .. this might be confusing.
  333. Shouldn't I send them the current prompt?
  334. Just in case we abort in the middle of something?!?
  335. */
  336. old_prompt = current_prompt;
  337. old_raw_prompt = current_raw_prompt;
  338. to_client("\x1b[0m\n\r");
  339. /*
  340. ╔══════════════════════════════╗
  341. ║ TradeWars Proxy Active ║
  342. ╚══════════════════════════════╝
  343. -=>
  344. */
  345. Boxes box(30, 1, true);
  346. box.boxcolor = "\x1b[1;33;44m";
  347. box.textcolor = "\x1b[1;33;44m";
  348. to_client(box.top());
  349. std::string output = " TradeWars Proxy \x1b[5mActive\x1b[0;1;33;44m ";
  350. to_client(box.row(output));
  351. to_client(box.bottom());
  352. if (galaxy.meta.contains("port_CIM")) {
  353. time_t last_port_cim_report = galaxy.meta["port_CIM"].get<int>();
  354. int seconds_ago = time_t_now() - last_port_cim_report;
  355. int minutes_ago = seconds_ago / 60;
  356. BUGZ_LOG(warning) << "port_CIM was " << minutes_ago << " minutes ago.";
  357. if (minutes_ago >= 120) {
  358. float hours_ago = minutes_ago / 60.0;
  359. std::string message =
  360. str(boost::format(
  361. "Warning: Last Port CIM Refresh was %1$0.2f hours ago.\n\r") %
  362. hours_ago);
  363. to_client(message);
  364. }
  365. } else {
  366. to_client("Warning: No CIM data.\n\r");
  367. BUGZ_LOG(warning) << "no meta port_CIM value seen.";
  368. }
  369. chain = main_menu;
  370. main_menu->activate();
  371. /*
  372. // Using InputDispatch -- and see have_input
  373. std::shared_ptr<Dispatch> readline = std::make_shared<InputDispatch>(*this);
  374. chain = readline;
  375. InputDispatch *id = static_cast<InputDispatch *>(&(*readline));
  376. id->prompt = "\x1b[0m\x1b[1;33;44m-=>\x1b[0m \x1b[1;37;44m";
  377. id->max_length = 15;
  378. id->setNotify([this]() { this->have_input(); });
  379. readline->activate();
  380. */
  381. }
  382. void Director::menu_choice(void) {
  383. MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));
  384. if (md) {
  385. if (md->input.empty()) {
  386. to_client("Menu aborted.\n\r");
  387. proxy_deactivate();
  388. return;
  389. } else {
  390. switch (md->input[0]) {
  391. case 'C': // configure
  392. config_edit();
  393. return;
  394. break;
  395. case 'D':
  396. case 'T': // display trading report
  397. {
  398. auto pptv = galaxy.find_best_trades();
  399. std::string output;
  400. galaxy.sort_port_pair_type(pptv);
  401. int max_display = 20;
  402. if (galaxy.config.contains("display_lines"))
  403. max_display = galaxy.config["display_lines"].get<int>();
  404. else
  405. galaxy.config["display_lines"] = max_display;
  406. if ((max_display <= 0) || (max_display > 255)) {
  407. max_display = 255;
  408. galaxy.config["display_lines"] = 255;
  409. }
  410. const int per_line = 5;
  411. int count = 0;
  412. int line = 0;
  413. std::string display_line;
  414. ANSIColor by{1, 33}; // yellow
  415. ANSIColor cyan{36}; // cyan
  416. ANSIColor bg{1, 32}; // bright green
  417. ANSIColor bb{1, 34}; // bright blue
  418. for (auto const &ppt : pptv) {
  419. output =
  420. str(boost::format("%1%%2$5d%3%:%4%%5$-5d%3%(%6%%7$d%3%) ") %
  421. by() % ppt.s1 % cyan() % bg() % ppt.s2 % bb() % ppt.type);
  422. display_line.append(output);
  423. ++count;
  424. if (count == per_line) {
  425. count = 0;
  426. display_line.append("\n\r");
  427. to_client(display_line);
  428. display_line.clear();
  429. ++line;
  430. }
  431. if (line == max_display) break;
  432. }
  433. if (count != 0) {
  434. display_line.append("\n\r");
  435. to_client(display_line);
  436. display_line.clear();
  437. }
  438. // We got < 5 lines, and max_display is > 5. Offer suggestion:
  439. if ((line < 5) && (max_display > 5)) {
  440. // suggestion:
  441. to_client(
  442. "HINT: For more lines, try reducing the burnt_percent?\n\r");
  443. }
  444. } break;
  445. case 'E': // Export Data/Save
  446. to_client("Saving...");
  447. galaxy.save();
  448. to_client("\rSaved....\n\r");
  449. break;
  450. case 'I': // Information
  451. information();
  452. break;
  453. case 'P': // Port CIM
  454. // Since we're adding/updating, we don't lose our
  455. // type 0 ports. Type 9 stays at 9.
  456. galaxy.meta["port_CIM"] = (int)time_t_now();
  457. chain = cim;
  458. to_server("^RQ", "Director::Port CIM");
  459. {
  460. std::string text = str(boost::format("Port CIM Report (%1%)\n\r") %
  461. galaxy.ports.size());
  462. // to_client("Port CIM Report\n\r");
  463. to_client(text);
  464. }
  465. chain->activate();
  466. return;
  467. break;
  468. case 'M': // Macros
  469. macro_edit();
  470. return;
  471. break;
  472. case 'W': // Warp CIM
  473. chain = cim;
  474. to_server("^IQ", "Director::Warp CIM");
  475. {
  476. std::string text = str(boost::format("Warp CIM Report (%1%)\n\r") %
  477. galaxy.warps.size());
  478. // to_client("Warp CIM Report\n\r");
  479. to_client(text);
  480. }
  481. chain->activate();
  482. return;
  483. break;
  484. // case 'T': // Trading Report
  485. // break;
  486. case 'S': // Scripts
  487. {
  488. init_scripts_menu();
  489. chain = scripts_menu;
  490. chain->activate();
  491. return;
  492. } break;
  493. case 'X': // Exit
  494. proxy_deactivate();
  495. return;
  496. }
  497. /*
  498. std::string text = str(
  499. boost::format("Back from Menu [%1%] was selected.\n\r") %
  500. md->input);
  501. to_client(text);
  502. */
  503. md->activate();
  504. }
  505. }
  506. }
  507. MenuDispatch *Director::init_scripts_menu(void) {
  508. MenuDispatch *md;
  509. if (scripts_menu) {
  510. md = dynamic_cast<MenuDispatch *>(&(*scripts_menu));
  511. return md;
  512. } else {
  513. scripts_menu = std::make_shared<MenuDispatch>(*this, "MenuScripts");
  514. md = static_cast<MenuDispatch *>(&(*scripts_menu));
  515. md->menu_box_color = "\x1b[0;32;40m";
  516. md->menu_text_color = "\x1b[1;32;40m";
  517. md->menu_title = "Scripts Menu";
  518. md->menu_options_color = "\x1b[1;32;40m";
  519. md->lazy = false;
  520. ANSIColor by{1, 33};
  521. ANSIColor cyan{36};
  522. ANSIColor bg{1, 32};
  523. std::string prompt = by() + "S" + cyan() + "cript " + by() + "M" + cyan() +
  524. "enu " + bg() + "=>" + reset() + " ";
  525. md->menu_prompt = prompt; // " SCRIPT : ";
  526. md->menu = {{"!", "Terror"},
  527. {"T", "Trade"},
  528. {"S", "Safe Move"},
  529. {"C", "Closest Trade"},
  530. {"N", "Nearest Unexplored"},
  531. {"V", "Voyager Explorer"},
  532. {"E", "Explorer"},
  533. {"U", "Upgrade Planet Pants"},
  534. {"X", "Exit Scripts"}};
  535. md->setNotify([this]() { this->scripts_done(); });
  536. return md;
  537. }
  538. }
  539. void Director::scripts_done(void) {
  540. // Was script selected? If so, run it!
  541. // otherwise, back to the menu we go...
  542. MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*scripts_menu));
  543. if (md) {
  544. if (md->input.empty()) {
  545. to_client("Scripts aborted.\n\r");
  546. scripts_menu.reset();
  547. proxy_deactivate();
  548. return;
  549. } else {
  550. switch (md->input[0]) {
  551. case 'T': // Trade
  552. {
  553. script = std::make_shared<TraderDispatch>(*this, "TraderDirector");
  554. TraderDispatch *ts = static_cast<TraderDispatch *>(&((*script)));
  555. ts->setNotify([this]() { this->proxy_deactivate(); });
  556. // Locate best trades
  557. auto found = galaxy.find_trades(current_sector, false);
  558. if (found.empty()) {
  559. to_client(
  560. "No Trades found. Port burnt (CONFIG: lower burnt_percent?) "
  561. "or no ports around.\n\r");
  562. proxy_deactivate();
  563. return;
  564. }
  565. // sort first?
  566. galaxy.sort_port_pair_type(found);
  567. BUGZ_LOG(fatal) << "Found " << found.size() << " possible trade(s).";
  568. BUGZ_LOG(fatal) << "Best: " << found[0].s1 << "," << found[0].s2
  569. << " : " << found[0].type;
  570. // Set parameters
  571. ts->port[0] = found[0].s1;
  572. ts->port[1] = found[0].s2;
  573. ts->type = found[0].type;
  574. ts->trades = found[0].trades;
  575. chain = script;
  576. chain->activate();
  577. return;
  578. } break;
  579. case 'S': {
  580. script = std::make_shared<MoveDispatch>(*this, "MoveDirector");
  581. MoveDispatch *md = static_cast<MoveDispatch *>(&((*script)));
  582. md->setNotify([this]() { this->proxy_deactivate(); });
  583. md->move_to = 1;
  584. chain = script;
  585. chain->activate();
  586. return;
  587. } break;
  588. case '!': {
  589. script = std::make_shared<ScriptTerror>(*this, "TerrorDirector");
  590. ScriptTerror *st = static_cast<ScriptTerror *>(&(*script));
  591. st->setNotify([this]() {
  592. script.reset();
  593. this->proxy_deactivate();
  594. });
  595. chain = script;
  596. chain->activate();
  597. return;
  598. } break;
  599. // }
  600. case 'C': {
  601. auto best = galaxy.find_closest(current_sector);
  602. std::string text;
  603. if (best.type != 0) {
  604. text = str(boost::format("Best/Closest: %1% with %2% & %3%\n\r") %
  605. best.type % best.s1 % best.s2);
  606. to_client(text);
  607. } else {
  608. to_client("I don't see any best trades.\n\r");
  609. }
  610. const char *foe[3] = {"Fuel", "Organics", "Equipment"};
  611. for (int x = 0; x < 3; ++x) {
  612. auto s = galaxy.find_nearest_selling(current_sector, x);
  613. text = str(boost::format("Closest Selling %1% is %2%\n\r") %
  614. foe[x] % s);
  615. to_client(text);
  616. }
  617. to_client("Good places to hide:\n\r");
  618. auto sectors = galaxy.find_safe();
  619. text = "";
  620. int c = 0;
  621. for (auto const &s : sectors) {
  622. ++c;
  623. text += str(boost::format("%1$5d ") % s);
  624. if (c % 10 == 0) {
  625. text.append("\n\r");
  626. to_client(text);
  627. text.clear();
  628. c = 0;
  629. }
  630. }
  631. if (c != 0) {
  632. text.append("\n\r");
  633. to_client(text);
  634. }
  635. } break;
  636. case 'N': {
  637. sector_type s = galaxy.find_nearest_unexplored(current_sector);
  638. if (s != 0) {
  639. std::string text = str(boost::format("Sector: %1%\n\r") % s);
  640. to_client(text);
  641. } else {
  642. to_client("I don't see any unexplored.\n\r");
  643. }
  644. } break;
  645. case 'V': {
  646. script = std::make_shared<ScriptVoyager>(*this, "VoyagerDirector");
  647. ScriptVoyager *sv = static_cast<ScriptVoyager *>(&(*script));
  648. sv->setNotify([this]() {
  649. script.reset();
  650. this->proxy_deactivate();
  651. });
  652. chain = script;
  653. chain->activate();
  654. return;
  655. } break;
  656. case 'E': {
  657. script = std::make_shared<ScriptExplore>(*this, "ExploreDirector");
  658. ScriptExplore *se = static_cast<ScriptExplore *>(&(*script));
  659. se->setNotify([this]() {
  660. script.reset();
  661. this->proxy_deactivate();
  662. });
  663. chain = script;
  664. chain->activate();
  665. return;
  666. } break;
  667. case 'U': {
  668. script = std::make_shared<ScriptPlanet>(*this, "PlanetDirector");
  669. ScriptPlanet *sp = static_cast<ScriptPlanet *>(&(*script));
  670. sp->setNotify([this]() {
  671. script.reset();
  672. this->proxy_deactivate();
  673. });
  674. chain = script;
  675. chain->activate();
  676. return;
  677. } break;
  678. case 'Q':
  679. chain = main_menu;
  680. main_menu->activate();
  681. return;
  682. break;
  683. }
  684. }
  685. }
  686. proxy_activate();
  687. // And to end scripts, we do .. what exactly?
  688. // DEBUG: Ok, why does everything work OK if I reset the scripts_menu
  689. // here?? probably do want to destroy scripts here. ;)
  690. // scripts_menu.reset();
  691. // proxy_deactivate();
  692. }
  693. /**
  694. * @brief Setup Config Input
  695. *
  696. * @return DispatchInput*
  697. */
  698. InputDispatch *Director::init_config_input(void) {
  699. InputDispatch *id;
  700. if (config_input) {
  701. // Yes, it has been setup before.
  702. id = dynamic_cast<InputDispatch *>(&(*config_input));
  703. ANSIColor by{1, 33};
  704. ANSIColor cyan{36};
  705. ANSIColor bg{1, 32};
  706. std::string prompt =
  707. by() + "C" + cyan() + "onfig " + bg() + "=>" + reset() + " ";
  708. id->prompt = prompt; // "Config => ";
  709. id->numeric = true;
  710. id->max_length = 3;
  711. config_item.clear();
  712. return id;
  713. } else {
  714. // set it up
  715. config_input = std::make_shared<InputDispatch>(*this, "InputDirector");
  716. id = static_cast<InputDispatch *>(&(*config_input));
  717. ANSIColor by{1, 33};
  718. ANSIColor cyan{36};
  719. ANSIColor bg{1, 32};
  720. std::string prompt =
  721. by() + "C" + cyan() + "onfig " + bg() + "=>" + reset() + " ";
  722. id->prompt = prompt; // "Config => ";
  723. id->numeric = true;
  724. id->max_length = 3;
  725. id->setNotify([this]() { this->config_have_input(); });
  726. config_item.clear();
  727. return id;
  728. }
  729. }
  730. void Director::config_edit(void) {
  731. // display current config
  732. std::string menu_box_color = "\x1b[1;33;44m";
  733. std::string menu_text_color = "\x1b[1;37;44m";
  734. auto abox = Boxes::alert(" Configuration: ", menu_box_color,
  735. menu_text_color, 20, 1, true);
  736. for (auto line : abox) {
  737. to_client(line);
  738. }
  739. // to_client("Configuration:\n\r");
  740. int item = 1;
  741. ANSIColor number(COLOR::CYAN);
  742. ANSIColor key(COLOR::GREEN, ATTR::BOLD);
  743. ANSIColor value(COLOR::BLUE, ATTR::BOLD);
  744. for (auto const &cfg : galaxy.config.items()) {
  745. // for (auto const &cfg : galaxy.config) {
  746. std::string output =
  747. str(boost::format("%1%%2$2d %3%%4$20s: %5%%6$s%7%\n\r") % number() %
  748. item % key() % cfg.key() % value() % cfg.value() % reset());
  749. to_client(output);
  750. ++item;
  751. }
  752. std::string message =
  753. number() + "Enter number to edit, " + key() + "blank to exit.\n\r";
  754. // to_client("Enter number to edit, blank to exit.\n\r");
  755. to_client(message);
  756. // setup call to config_input:
  757. InputDispatch *id = init_config_input();
  758. chain = config_input;
  759. id->activate();
  760. // to return to the menu:
  761. // MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));
  762. // md->activate();
  763. }
  764. void Director::config_have_input(void) {
  765. InputDispatch *id = dynamic_cast<InputDispatch *>(&(*config_input));
  766. if (config_item.empty()) {
  767. // This is a config menu selection
  768. if (id->input.empty()) {
  769. // We're done here. Return to menu.
  770. chain = main_menu;
  771. MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));
  772. md->activate();
  773. // destroy the input? yes.
  774. config_input.reset();
  775. return;
  776. } else {
  777. int item = sstoi(id->input);
  778. if ((item < 1) || (item > (int)galaxy.config.size())) {
  779. // selection out of range - redisplay config menu
  780. to_client("What? I didn't see that item.\n\r");
  781. config_edit();
  782. return;
  783. } else {
  784. int pos = 1;
  785. for (auto const &c : galaxy.config.items()) {
  786. if (pos == item) {
  787. // got it!
  788. ANSIColor key(COLOR::GREEN, ATTR::BOLD);
  789. ANSIColor value(COLOR::BLUE, ATTR::BOLD);
  790. config_item = c.key();
  791. std::string output =
  792. str(boost::format("%1%%2% : %3%%4%\n\r") % key() % config_item %
  793. value() % galaxy.meta["help"][config_item]);
  794. to_client(output);
  795. id->max_length = 30;
  796. id->numeric = false;
  797. ANSIColor by{1, 33};
  798. ANSIColor cyan{36};
  799. ANSIColor bg{1, 32};
  800. std::string prompt =
  801. by() + "C" + cyan() + "hange to " + bg() + "=>" + reset() + " ";
  802. id->prompt = prompt;
  803. id->activate();
  804. return;
  805. };
  806. ++pos;
  807. }
  808. to_client("What? I didn't find that item?\n\r");
  809. config_edit();
  810. return;
  811. }
  812. }
  813. } else {
  814. // This is a config item edit
  815. if (id->input.empty()) {
  816. to_client("No change.\n\r");
  817. config_edit();
  818. return;
  819. } else {
  820. BUGZ_LOG(fatal) << "Config EDIT: " << config_item << " = " << id->input;
  821. galaxy.config[config_item] = id->input;
  822. config_edit();
  823. return;
  824. }
  825. }
  826. }
  827. InputDispatch *Director::init_macro_input(void) {
  828. InputDispatch *id;
  829. if (macro_input) {
  830. // Yes, it has been setup before.
  831. id = dynamic_cast<InputDispatch *>(&(*macro_input));
  832. ANSIColor by{1, 33};
  833. ANSIColor cyan{36};
  834. ANSIColor bg{1, 32};
  835. std::string prompt =
  836. by() + "M" + cyan() + "acro " + bg() + "=>" + reset() + " ";
  837. id->prompt = prompt; // "Config => ";
  838. id->numeric = false;
  839. id->max_length = 1;
  840. config_item.clear();
  841. return id;
  842. } else {
  843. // set it up
  844. macro_input = std::make_shared<InputDispatch>(*this, "InputDirector");
  845. id = static_cast<InputDispatch *>(&(*macro_input));
  846. ANSIColor by{1, 33};
  847. ANSIColor cyan{36};
  848. ANSIColor bg{1, 32};
  849. std::string prompt =
  850. by() + "M" + cyan() + "acro " + bg() + "=>" + reset() + " ";
  851. id->prompt = prompt;
  852. id->numeric = false;
  853. id->max_length = 1;
  854. id->setNotify([this]() { this->macro_have_input(); });
  855. macro_item.clear();
  856. return id;
  857. }
  858. }
  859. void Director::macro_edit(void) {
  860. // display current config
  861. std::string menu_box_color = "\x1b[1;33;44m";
  862. std::string menu_text_color = "\x1b[1;37;44m";
  863. auto abox = Boxes::alert(" Macros: ", menu_box_color, menu_text_color,
  864. 14, 1, true);
  865. for (auto line : abox) {
  866. to_client(line);
  867. }
  868. ANSIColor number(COLOR::CYAN);
  869. ANSIColor key(COLOR::GREEN, ATTR::BOLD);
  870. ANSIColor value(COLOR::BLUE, ATTR::BOLD);
  871. if (!galaxy.meta.contains("macros")) {
  872. galaxy.meta["macros"]["A"] = "";
  873. galaxy.meta["macros"]["B"] = "";
  874. galaxy.meta["macros"]["C"] = "";
  875. galaxy.meta["macros"]["D"] = "";
  876. galaxy.meta["macros"]["E"] = "";
  877. galaxy.meta["macros"]["F"] = "";
  878. }
  879. for (auto const &cfg : galaxy.meta["macros"].items()) {
  880. // for (auto const &cfg : galaxy.config) {
  881. std::string output = str(boost::format("%1%%2%: %3%%4$s%5%\n\r") % key() %
  882. cfg.key() % value() % cfg.value() % reset());
  883. to_client(output);
  884. }
  885. std::string message =
  886. number() + "Enter Letter to edit, " + key() + "blank to exit.\n\r";
  887. to_client(message);
  888. // setup call to config_input:
  889. InputDispatch *id = init_macro_input();
  890. chain = macro_input;
  891. id->input.clear();
  892. macro_item.clear();
  893. id->activate();
  894. }
  895. void Director::macro_have_input(void) {
  896. InputDispatch *id = dynamic_cast<InputDispatch *>(&(*macro_input));
  897. if (macro_item.empty()) {
  898. // Macro selection
  899. if (id->input.empty()) {
  900. chain = main_menu;
  901. MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));
  902. md->activate();
  903. macro_input.reset();
  904. return;
  905. } else {
  906. char c = toupper(id->input[0]);
  907. macro_item.assign(1, c);
  908. if (galaxy.meta["macros"].contains(macro_item)) {
  909. constexpr ANSIColor key("BOLD GREEN"); // COLOR::GREEN, ATTR::BOLD);
  910. constexpr ANSIColor value("BOLD BLUE"); // COLOR::BLUE, ATTR::BOLD);
  911. std::string output =
  912. str(boost::format("%1%%2% : %3%%4%\n\r") % key() % macro_item %
  913. value() % galaxy.meta["macros"][macro_item]);
  914. to_client(output);
  915. id->max_length = 100;
  916. id->numeric = false;
  917. ANSIColor by{1, 33};
  918. ANSIColor cyan{36};
  919. ANSIColor bg{1, 32};
  920. std::string prompt = by() + "M" + cyan() + "acro " + macro_item + bg() +
  921. "=>" + reset() + " ";
  922. id->prompt = prompt;
  923. id->activate();
  924. return;
  925. } else {
  926. to_client("I don't understand.\n\r");
  927. macro_edit();
  928. return;
  929. }
  930. }
  931. } else {
  932. // clear out macro_item when done.
  933. if (id->input.empty()) {
  934. to_client("No change.\n\r");
  935. macro_item.clear();
  936. macro_edit();
  937. return;
  938. } else {
  939. galaxy.meta["macros"][macro_item] = id->input;
  940. macro_item.clear();
  941. macro_edit();
  942. return;
  943. }
  944. }
  945. }
  946. void Director::have_input(void) {
  947. ++count;
  948. InputDispatch *id = dynamic_cast<InputDispatch *>(&(*chain));
  949. if (id) {
  950. std::string output =
  951. str(boost::format("Your Input (%2%): [%1%]\n\r") % id->input % count);
  952. to_client("\x1b[0m");
  953. to_client(output);
  954. } else {
  955. proxy_deactivate();
  956. return;
  957. }
  958. if (count > 3) {
  959. proxy_deactivate();
  960. } else {
  961. chain->activate();
  962. }
  963. }
  964. void Director::cim_done(void) {
  965. BUGZ_LOG(info) << "CIM done";
  966. chain = main_menu;
  967. main_menu->activate();
  968. }
  969. void Director::information(void) {
  970. std::string output;
  971. to_client("I currently know the following:\n\r");
  972. output = str(
  973. boost::format("Ports: %1%, Sectors: %2%, Config: %3%, Meta: %4%\n\r") %
  974. galaxy.ports.size() % galaxy.warps.size() % galaxy.config.size() %
  975. galaxy.meta.size());
  976. to_client(output);
  977. }
  978. void Director::proxy_deactivate(void) {
  979. active = false;
  980. // reset everything back to good state
  981. talk_direct = true;
  982. show_client = true;
  983. chain.reset();
  984. to_client("\n\r");
  985. to_client(current_raw_prompt);
  986. }
  987. /*
  988. Server Line Parsing Routines
  989. */
  990. void Director::SL_cimline(const std::string &line) {
  991. if (line == ": ENDINTERROG") {
  992. SL_parser = nullptr;
  993. return;
  994. }
  995. if (line == ": ") {
  996. // do I need to do anything special here for this?
  997. // Maybe -- We would save special ports that don't show up
  998. // (StarDock/Special) before. We don't know (at this point) if this is
  999. // warps or ports.
  1000. return;
  1001. }
  1002. if (line.empty()) {
  1003. SL_parser = nullptr;
  1004. return;
  1005. }
  1006. // parse cimline
  1007. // size_t pos = line.find('%');
  1008. // std::string work = line;
  1009. // if (pos == line.npos) {
  1010. if (in(line, "%")) {
  1011. // portcim
  1012. struct port p = parse_portcim(line);
  1013. if (p.sector == 0)
  1014. BUGZ_LOG(fatal) << "portcim: FAIL [" << line << "]";
  1015. else
  1016. galaxy.add_port(p);
  1017. // else
  1018. // BUGZ_LOG(trace) << "portcim: " << p;
  1019. } else {
  1020. // warpcim
  1021. // BUGZ_LOG(fatal) << "warpcim: [" << line << "]";
  1022. auto warps = split(line);
  1023. sector_warps sw;
  1024. for (auto const &w : warps) {
  1025. if (sw.sector == 0) {
  1026. sw.sector = stoi(w);
  1027. } else {
  1028. sw.add(stoi(w));
  1029. }
  1030. }
  1031. // BUGZ_LOG(trace) << "warpcim: " << sw;
  1032. galaxy.add_warp(sw);
  1033. }
  1034. }
  1035. void Director::SL_thiefline(const std::string &line) {
  1036. size_t pos = line.find("Suddenly you're Busted!");
  1037. bool busted = pos != line.npos;
  1038. if (busted) {
  1039. BUGZ_LOG(fatal) << "set bust";
  1040. SL_parser = nullptr;
  1041. } else {
  1042. pos = line.find("(You realize the guards saw you last time!)");
  1043. if (pos != line.npos) SL_parser = nullptr;
  1044. }
  1045. // Are those the two ways to exit from this state?
  1046. }
  1047. void Director::SL_sectorline(const std::string &line) {
  1048. // BUGZ_LOG(fatal) << "sectorline: [" << line << "]";
  1049. if (line.empty()) {
  1050. SL_parser = nullptr;
  1051. } else {
  1052. /*
  1053. sectorline: [Sector : 926 in The Federation.]
  1054. sectorline: [Beacon : FedSpace, FedLaw Enforced]
  1055. sectorline: [Ports : Stargate Alpha I, Class 9 (Special) (StarDock)]
  1056. sectorline: [Traders : Civilian phil, w/ 30 ftrs,]
  1057. sectorline: [ in Star Stomper (Sverdlov Merchant Cruiser)]
  1058. sectorline: [Warps to Sector(s) : 70 - 441 - 575 - 600 - 629 - 711]
  1059. sectorline: [Warps to Sector(s) : 70 - (475) - 569]
  1060. What can we get from Traders : line? Can we get if they are hostile
  1061. to us? We need to respond to "is powering up weapons" ... We can
  1062. react faster then a person can!
  1063. "phil is powering up weapons systems!"
  1064. Also the auto-attack ones Ferrengi -- we need to auto-respond Retreat.
  1065. */
  1066. if (in(line, "Sector :")) {
  1067. current_sector = stoi(line.substr(10));
  1068. // BUGZ_LOG(trace) << "SECTOR: " << current_sector;
  1069. }
  1070. if (in(line, "Ports :")) {
  1071. std::string port_class;
  1072. size_t pos = line.find(", Class ");
  1073. if (pos != std::string::npos) {
  1074. pos += 8;
  1075. int class_ = stoi(line.substr(pos));
  1076. // BUGZ_LOG(trace) << "PORT: " << class_;
  1077. galaxy.add_port(current_sector, class_);
  1078. }
  1079. }
  1080. if (in(line, "Warps to Sector(s) :")) {
  1081. std::string temp = line.substr(20);
  1082. replace(temp, " - ", " ");
  1083. // unexplored sectors ()
  1084. // Should I track these?
  1085. replace(temp, "(", "");
  1086. replace(temp, ")", "");
  1087. sector_warps sw;
  1088. auto warps = split(temp);
  1089. sw.sector = current_sector;
  1090. // what if there is only one warp?
  1091. for (auto const &w : warps) {
  1092. sw.add(stoi(w));
  1093. }
  1094. // BUGZ_LOG(trace) << "WARPS: " << sw;
  1095. galaxy.add_warp(sw);
  1096. }
  1097. }
  1098. }
  1099. void Director::SL_densityline(const std::string &line) {
  1100. // BUGZ_LOG(trace) << "densityline: [" << line << "]";
  1101. if (line.empty()) {
  1102. SL_parser = nullptr;
  1103. return;
  1104. }
  1105. /*
  1106. // Ensure this really is a density scan and not something else
  1107. if (!in(line, "Sector") || !in(line, "Warps") || !in(line, "NavHaz") ||
  1108. !in(line, "Anom")) {
  1109. BUGZ_LOG(fatal) << "densityline: Invalid line.";
  1110. SL_parser = nullptr;
  1111. return;
  1112. }
  1113. if (not galaxy.meta["density"]) {
  1114. galaxy.meta["density"] = YAML::Node();
  1115. }
  1116. */
  1117. /*
  1118. 0 1 2 3 4 5 6 7 8 9 10 11 12
  1119. "Sector 55 ==> 0 Warps : 4 NavHaz : 0% Anom : No"
  1120. "Sector ( 223) ==> 0 Warps : 3 NavHaz : 0% Anom : No"
  1121. SL: [Sector ( 1704) ==> 10,350 Warps : 4 NavHaz : 0% Anom :
  1122. No]
  1123. */
  1124. if (in(line, "==>")) {
  1125. std::string work = line;
  1126. replace(work, ":", "");
  1127. bool known = !in(work, "(");
  1128. replace(work, "(", "");
  1129. replace(work, ")", "");
  1130. replace(work, "%", "");
  1131. replace(work, ",", "");
  1132. auto dense = split(work);
  1133. // Parse our data
  1134. sector_type sector = std::stoi(dense.at(1));
  1135. uint16_t density = std::stoi(dense.at(3));
  1136. uint16_t warps = std::stoi(dense.at(5));
  1137. uint16_t navhaz = std::stoi(dense.at(7));
  1138. bool anom = in(dense.at(9), "Yes");
  1139. struct density d = {sector, density, warps, navhaz, anom, known};
  1140. galaxy.dscan.add_scan(d);
  1141. // Commit data
  1142. /*
  1143. BUGZ_LOG(trace) << "densityline: {sector=" << sector
  1144. << " density=" << density << " warps=" << warps
  1145. << " navhaz=" << navhaz << " anom=" << anom
  1146. << " known=" << known << "}";
  1147. */
  1148. /*
  1149. if (galaxy.meta["density"][sector]) {
  1150. galaxy.meta["density"][sector] = YAML::Node();
  1151. }
  1152. */
  1153. // what(): [json.exception.type_error.305] cannot use operator[] with a
  1154. // numeric argument with object numeric argument with object
  1155. /*
  1156. std::string sector_text = std::to_string(sector);
  1157. galaxy.meta["_density"][sector_text]["density"] = density;
  1158. galaxy.meta["_density"][sector_text]["warps"] = warps;
  1159. galaxy.meta["_density"][sector_text]["navhaz"] = navhaz;
  1160. galaxy.meta["_density"][sector_text]["anom"] = anom;
  1161. galaxy.meta["_density"][sector_text]["known"] = known;
  1162. */
  1163. // Add a check to see if density is greater than 500
  1164. // Add datetime stamp
  1165. }
  1166. }
  1167. void Director::SL_portline(const std::string &line) {
  1168. /*
  1169. We take blank lines because we start at <Port>.
  1170. Otherwise, we trigger on computer port requests.
  1171. if (line.empty()) {
  1172. SL_parser = nullptr;
  1173. return;
  1174. }
  1175. */
  1176. /*
  1177. SL: [ Items Status Trading % of max OnBoard]
  1178. SL: [ ----- ------ ------- -------- -------]
  1179. SL: [Fuel Ore Buying 3000 100% 0]
  1180. SL: [Organics Buying 3000 100% 0]
  1181. SL: [Equipment Buying 3000 100% 0]
  1182. SL: []
  1183. SL: [Commerce report for: 03:51:56 PM Mon Oct 24, 2033 You can buy:]
  1184. SL: [A Cargo holds : 650 credits / next hold 0]
  1185. SL: [B Fighters : 233 credits per fighter 75]
  1186. SL: [C Shield Points : 116 credits per point 100]
  1187. SL: []
  1188. */
  1189. // BUGZ_LOG(info) << "portline : " << line;
  1190. if (in(line, "%")) {
  1191. // size_t pos = line.find('%');
  1192. // if (pos != line.npos) {
  1193. // Ok, this is a valid portline
  1194. std::string work = line;
  1195. replace(work, "Fuel Ore", "Fuel");
  1196. auto parts = split(work);
  1197. if (parts[0] == "Items") return;
  1198. char c = tolower(parts[0][0]);
  1199. int pos;
  1200. char foe[4] = "foe";
  1201. for (pos = 0; pos < 3; ++pos) {
  1202. if (c == foe[pos]) break;
  1203. }
  1204. int amount = stoi(parts[2]);
  1205. int percent = stoi(parts[3]);
  1206. // update port
  1207. auto port = galaxy.ports.find(current_sector);
  1208. if (port != galaxy.ports.end()) {
  1209. port->second.amount[pos] = amount;
  1210. port->second.percent[pos] = percent;
  1211. }
  1212. /*
  1213. BUGZ_LOG(fatal) << "portline split:";
  1214. for (auto const p : parts) {
  1215. BUGZ_LOG(fatal) << p;
  1216. }
  1217. */
  1218. // Here's the end:
  1219. if (parts[0] == "Equipment") SL_parser = nullptr;
  1220. // BUGZ_LOG(fatal) << "portline split : [" << parts << "]";
  1221. }
  1222. }
  1223. void Director::SL_warpline(const std::string &line) {
  1224. if (line.empty()) {
  1225. SL_parser = nullptr;
  1226. return;
  1227. }
  1228. // process warp line
  1229. BUGZ_LOG(fatal) << "warpline: [" << line << "]";
  1230. }
  1231. void Director::SL_infoline(const std::string &line) {
  1232. static int state;
  1233. if (line == "<Info>") {
  1234. state = 0;
  1235. galaxy.meta["info"] = json();
  1236. }
  1237. if (line.empty()) {
  1238. ++state;
  1239. if (state == 2) {
  1240. SL_parser = nullptr;
  1241. // clear out the existing ship data
  1242. galaxy.meta["ship"] = json();
  1243. // process the parsed information in meta["info"]
  1244. if (galaxy.meta["info"].contains("Total Holds")) {
  1245. std::string work = galaxy.meta["info"]["Total Holds"];
  1246. replace(work, "Fuel Ore", "Fuel");
  1247. auto parts = split(work, " - ");
  1248. int total_holds = stoi(parts[0]);
  1249. BUGZ_LOG(fatal) << "total holds: " << total_holds;
  1250. auto contents = split(parts[1]);
  1251. for (auto const &hold : contents) {
  1252. auto hold_amount = split(hold, "=");
  1253. BUGZ_LOG(fatal) << hold_amount[0] << " with " << hold_amount[1];
  1254. std::string key = hold_amount[0];
  1255. str_tolower(key);
  1256. // equipment = e
  1257. // organics = o
  1258. // fuel = f
  1259. // colonists = c
  1260. // empty = empty
  1261. if (key != "empty") {
  1262. key = key[0];
  1263. }
  1264. galaxy.meta["ship"]["holds"][key] = stoi(hold_amount[1]);
  1265. }
  1266. galaxy.meta["ship"]["holds"]["total"] = total_holds;
  1267. }
  1268. if (galaxy.meta["info"].contains("Turns to Warp")) {
  1269. // Why is this saying that this is a string?
  1270. int warp_turns = sstoi(json_str(galaxy.meta["info"]["Turns to Warp"]));
  1271. // BUGZ_LOG(trace) << "Turns to Warp: " << warp_turns;
  1272. galaxy.meta["ship"]["warp_turns"] = warp_turns;
  1273. }
  1274. if (galaxy.meta["info"].contains("LongRange Scan")) {
  1275. std::string scanner_text =
  1276. galaxy.meta["info"]["LongRange Scan"].get<std::string>();
  1277. char scanner = scanner_text[0];
  1278. // BUGZ_LOG(trace) << "Scanner: " << scanner;
  1279. galaxy.meta["ship"]["scanner"] = scanner;
  1280. }
  1281. // turns isn't ship specific
  1282. if (galaxy.meta["info"].contains("Turns left")) {
  1283. // OR this could be "Unlimited" !!!
  1284. std::string text = galaxy.meta["info"]["Turns left"];
  1285. if (text == "Unlimited") {
  1286. galaxy.meta["turns"] = -1;
  1287. } else {
  1288. int turns =
  1289. stoi(text); // galaxy.meta["info"]["Turns left"].as<int>();
  1290. // BUGZ_LOG(trace) << "Turns left: " << turns;
  1291. galaxy.meta["turns"] = turns;
  1292. }
  1293. }
  1294. if (galaxy.meta["info"].contains("Current Sector")) {
  1295. int sector = sstoi(json_str(galaxy.meta["info"]["Current Sector"]));
  1296. // BUGZ_LOG(trace) << "Sector: " << sector;
  1297. // it should already be sector ...
  1298. current_sector = sector;
  1299. }
  1300. if (galaxy.meta["info"].contains("Credits")) {
  1301. std::string credit_text = galaxy.meta["info"]["Credits"];
  1302. replace(credit_text, ",", "");
  1303. int credits = stoi(credit_text);
  1304. galaxy.meta["credits"] = credits;
  1305. // BUGZ_LOG(trace) << "Credits: " << credits;
  1306. }
  1307. }
  1308. return;
  1309. }
  1310. // info to parse:
  1311. size_t pos = line.find(" : ");
  1312. if ((!endswith(line, " : ")) && (pos != line.npos)) {
  1313. std::string key = line.substr(0, pos);
  1314. // Ferrengi ships don't have date built
  1315. std::string value = line.substr(pos + 3);
  1316. trim(key);
  1317. trim(value);
  1318. galaxy.meta["info"][key] = value;
  1319. // BUGZ_LOG(trace) << "Info: " << key << " : " << value;
  1320. }
  1321. // [Corp # 1, Galaxy Stomping, Inc.]
  1322. if (startswith(line, "Corp ")) {
  1323. pos = line.find(", ");
  1324. if (pos != line.npos) {
  1325. galaxy.meta["info"]["Corp"] = line.substr(pos + 2);
  1326. }
  1327. }
  1328. }
  1329. void Director::SL_computer_portline(const std::string &line) {
  1330. if (startswith(line, "What sector is the port in?"))
  1331. computer_port_done = false;
  1332. if (line == "I have no information about a port in that sector.") {
  1333. computer_port_sector = 0;
  1334. SL_parser = nullptr;
  1335. return;
  1336. }
  1337. if (!computer_port_done) {
  1338. if (in(line, "Fuel Ore")) computer_port_done = true;
  1339. if (in(line, "Cargo holds")) {
  1340. // If I want to scan the class type 0 ports:
  1341. // computer_port_done = true;
  1342. // otherwise:
  1343. SL_parser = nullptr;
  1344. return;
  1345. }
  1346. }
  1347. if (computer_port_done) {
  1348. if (line.empty()) {
  1349. SL_parser = nullptr;
  1350. return;
  1351. }
  1352. // scan for items of interest
  1353. // SL: [Fuel Ore Buying 810 100% 0]
  1354. // SL: [Organics Buying 856 57% 0]
  1355. // SL: [Equipment Selling 922 44% 0]
  1356. std::string work = line;
  1357. replace(work, "Fuel Ore", "Fuel");
  1358. replace(work, "%", "");
  1359. auto parts = split(work);
  1360. char c = tolower(parts[0][0]);
  1361. int pos;
  1362. char foe[4] = "foe";
  1363. for (pos = 0; pos < 3; ++pos) {
  1364. if (c == foe[pos]) break;
  1365. }
  1366. int amount = stoi(parts[2]);
  1367. int percent = stoi(parts[3]);
  1368. // update port
  1369. auto port = galaxy.ports.find(computer_port_sector);
  1370. if (port != galaxy.ports.end()) {
  1371. BUGZ_LOG(trace) << "COM PORT " << computer_port_sector << " " << c << " "
  1372. << amount << " " << percent;
  1373. port->second.amount[pos] = amount;
  1374. port->second.percent[pos] = percent;
  1375. }
  1376. }
  1377. }
  1378. void Director::SL_planetline(const std::string &line) {
  1379. if (line == "Corporate command [TL=00:00:00]:[344] (?=Help)? Q" ||
  1380. line == "Computer command [TL=00:00:00]:[344] (?=Help)? Q") {
  1381. SL_parser = nullptr;
  1382. return;
  1383. }
  1384. if (in(line, "Class ") &&
  1385. (in(line, "Level ") || (endswith(line, "No Citadel")))) {
  1386. int level;
  1387. char c;
  1388. sector_type sector;
  1389. int number;
  1390. std::string name;
  1391. std::string work = line;
  1392. size_t pos = work.find('#');
  1393. std::string temp = work.substr(0, pos);
  1394. trim(temp);
  1395. sector = sstoi(temp);
  1396. work = work.substr(pos + 1);
  1397. number = sstoi(work);
  1398. pos = work.find(' ');
  1399. work = work.substr(pos + 1);
  1400. trim(work);
  1401. if (endswith(work, "No Citadel")) {
  1402. level = 0;
  1403. } else {
  1404. pos = work.rfind("Level ");
  1405. temp = work.substr(pos + 6);
  1406. level = sstoi(temp);
  1407. }
  1408. pos = work.rfind("Class ");
  1409. temp = work.substr(pos + 6);
  1410. c = temp[0];
  1411. work = work.substr(0, pos);
  1412. trim(work);
  1413. name = work;
  1414. /*
  1415. BUGZ_LOG(trace) << (int)sector << " # " << number << " Class " << c
  1416. << " Level " << level << " name: [" << name << "]";
  1417. */
  1418. planet p{sector, number, level, name, c};
  1419. galaxy.planets[number] = p;
  1420. }
  1421. /*
  1422. SL: [ 344 #4 Enjoy the Silence Class M, Earth Type Level 3] SL:
  1423. [ --- (4M) 1T 64 25 14T 693 277 2T 10M] SL: [
  1424. 344 #5 High There! Class L, Mountainous Level 2]
  1425. SL: [ --- (5M) 2T 0 6 25T 100 112 2T 6M]
  1426. ...
  1427. SL: [ --- (9M) 3T 64 31 39T 793 389 4T ---]
  1428. */
  1429. }