main.cpp 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  1. #include "door.h"
  2. #include "space.h"
  3. #include <chrono> // chrono::system_clock
  4. #include <ctime> // localtime
  5. #include <iomanip> // put_time
  6. #include <iostream>
  7. #include <random>
  8. #include <string>
  9. #include "db.h"
  10. #include "deck.h"
  11. #include "version.h"
  12. #include <algorithm> // transform
  13. void string_toupper(std::string &str) {
  14. std::transform(str.begin(), str.end(), str.begin(), ::toupper);
  15. }
  16. bool replace(std::string &str, const std::string &from, const std::string &to) {
  17. size_t start_pos = str.find(from);
  18. if (start_pos == std::string::npos)
  19. return false;
  20. str.replace(start_pos, from.length(), to);
  21. return true;
  22. }
  23. bool replace(std::string &str, const char *from, const char *to) {
  24. size_t start_pos = str.find(from);
  25. if (start_pos == std::string::npos)
  26. return false;
  27. str.replace(start_pos, strlen(from), to);
  28. return true;
  29. }
  30. door::ANSIColor from_string(std::string colorCode);
  31. std::function<std::ofstream &(void)> get_logger;
  32. unsigned long score = 0;
  33. int hand = 1;
  34. int total_hands = 3;
  35. int card_number = 29;
  36. int current_streak = 0;
  37. int best_streak = 0;
  38. std::chrono::_V2::system_clock::time_point play_day;
  39. /*
  40. Cards:
  41. 4 layers deep.
  42. https://en.wikipedia.org/wiki/Code_page_437
  43. using: \xb0, 0xb1, 0xb2, 0xdb
  44. OR: \u2591, \u2592, \u2593, \u2588
  45. Like so:
  46. ##### #####
  47. ##### #####
  48. ##### #####
  49. Cards: (Black on White, or Red on White)
  50. 8D### TH###
  51. ##D## ##H##
  52. ###D8 ###HT
  53. D, H = Red, Clubs, Spades = Black.
  54. ^ Where D = Diamonds, H = Hearts
  55. ♥, ♦, ♣, ♠
  56. \x03, \x04, \x05, \x06
  57. \u2665, \u2666, \u2663, \u2660
  58. Card layout. Actual cards are 3 lines thick.
  59. ░░░░░ ░░░░░ ░░░░░
  60. ░░░░░ ░░░░░ ░░░░░
  61. ▒▒▒▒▒░▒▒▒▒▒ #####░##### #####░#####
  62. ▒▒▒▒▒ ▒▒▒▒▒ ##### ##### ##### #####
  63. ▓▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▓ #####=#####=##### #####=#####=#####
  64. ▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓ ##### ##### ##### ##### ##### #####
  65. █████▓█████▓█████▓#####=#####=#####=#####=#####=#####=#####
  66. █████ █████ █████ ##### ##### ##### ##### ##### ##### #####
  67. █████ █████ █████ ##### ##### ##### ##### ##### ##### #####
  68. #####
  69. Player Information ##### Time in: xx Time out: xx
  70. Name: ##### Playing Day: November 3rd
  71. Hand Score : Current Streak: N
  72. Todays Score : XX Cards Remaining Longest Streak: NN
  73. Monthly Score: Playing Hand X of X Most Won: xxx Lost: xxx
  74. [4] Lf [6] Rt [Space] Play Card [Enter] Draw [D]one [H]elp [R]edraw
  75. ►, ◄, ▲, ▼
  76. \x10, \x11, \x1e, \x1f
  77. \u25ba, \u25c4, \u25b2, \u25bc
  78. The Name is <- 6 to the left.
  79. # is back of card. = is upper card showing between.
  80. There's no fancy anything here. Cards overlap the last
  81. line of the previous line/card.
  82. */
  83. // The idea is that this would be defined elsewhere (maybe)
  84. int user_score = 0;
  85. // NOTE: When run as a door, we always detect CP437 (because that's what Enigma
  86. // is defaulting to)
  87. void adjust_score(int by) { user_score += by; }
  88. door::Panel make_timeout(int mx, int my) {
  89. door::ANSIColor yellowred =
  90. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::RED, door::ATTR::BOLD);
  91. std::string line_text("Sorry, you've been inactive for too long.");
  92. int msgWidth = line_text.length() + (2 * 3); // + padding * 2
  93. door::Panel timeout((mx - (msgWidth)) / 2, my / 2 + 4, msgWidth);
  94. // place.setTitle(std::make_unique<door::Line>(title), 1);
  95. timeout.setStyle(door::BorderStyle::DOUBLE);
  96. timeout.setColor(yellowred);
  97. door::Line base(line_text);
  98. base.setColor(yellowred);
  99. std::string pad1(3, ' ');
  100. /*
  101. std::string pad1(3, '\xb0');
  102. if (door::unicode) {
  103. std::string unicode;
  104. door::cp437toUnicode(pad1.c_str(), unicode);
  105. pad1 = unicode;
  106. }
  107. */
  108. base.setPadding(pad1, yellowred);
  109. // base.setColor(door::ANSIColor(door::COLOR::GREEN, door::COLOR::BLACK));
  110. std::unique_ptr<door::Line> stuff = std::make_unique<door::Line>(base);
  111. timeout.addLine(std::make_unique<door::Line>(base));
  112. return timeout;
  113. }
  114. door::Panel make_notime(int mx, int my) {
  115. door::ANSIColor yellowred =
  116. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::RED, door::ATTR::BOLD);
  117. std::string line_text("Sorry, you've used up all your time for today.");
  118. int msgWidth = line_text.length() + (2 * 3); // + padding * 2
  119. door::Panel timeout((mx - (msgWidth)) / 2, my / 2 + 4, msgWidth);
  120. // place.setTitle(std::make_unique<door::Line>(title), 1);
  121. timeout.setStyle(door::BorderStyle::DOUBLE);
  122. timeout.setColor(yellowred);
  123. door::Line base(line_text);
  124. base.setColor(yellowred);
  125. std::string pad1(3, ' ');
  126. /*
  127. std::string pad1(3, '\xb0');
  128. if (door::unicode) {
  129. std::string unicode;
  130. door::cp437toUnicode(pad1.c_str(), unicode);
  131. pad1 = unicode;
  132. }
  133. */
  134. base.setPadding(pad1, yellowred);
  135. // base.setColor(door::ANSIColor(door::COLOR::GREEN, door::COLOR::BLACK));
  136. std::unique_ptr<door::Line> stuff = std::make_unique<door::Line>(base);
  137. timeout.addLine(std::make_unique<door::Line>(base));
  138. return timeout;
  139. }
  140. door::Menu make_main_menu(void) {
  141. door::Menu m(5, 5, 25);
  142. door::Line mtitle(SPACEACE " Main Menu");
  143. door::ANSIColor border_color(door::COLOR::CYAN, door::COLOR::BLUE);
  144. door::ANSIColor title_color(door::COLOR::CYAN, door::COLOR::BLUE,
  145. door::ATTR::BOLD);
  146. m.setColor(border_color);
  147. mtitle.setColor(title_color);
  148. mtitle.setPadding(" ", title_color);
  149. m.setTitle(std::make_unique<door::Line>(mtitle), 1);
  150. // m.setColorizer(true,
  151. m.setRender(true, door::Menu::makeRender(
  152. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  153. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD),
  154. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  155. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD)));
  156. // m.setColorizer(false,
  157. m.setRender(false, door::Menu::makeRender(
  158. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  159. door::ATTR::BOLD),
  160. door::ANSIColor(door::COLOR::WHITE, door::COLOR::BLUE,
  161. door::ATTR::BOLD),
  162. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  163. door::ATTR::BOLD),
  164. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLUE,
  165. door::ATTR::BOLD)));
  166. m.addSelection('P', "Play Cards");
  167. m.addSelection('S', "View Scores");
  168. m.addSelection('C', "Configure");
  169. m.addSelection('H', "Help");
  170. m.addSelection('A', "About this game");
  171. m.addSelection('Q', "Quit");
  172. return m;
  173. }
  174. door::renderFunction statusValue(door::ANSIColor status,
  175. door::ANSIColor value) {
  176. door::renderFunction rf = [status,
  177. value](const std::string &txt) -> door::Render {
  178. door::Render r(txt);
  179. door::ColorOutput co;
  180. co.pos = 0;
  181. co.len = 0;
  182. co.c = status;
  183. size_t pos = txt.find(':');
  184. if (pos == std::string::npos) {
  185. // failed to find - use entire string as status color.
  186. co.len = txt.length();
  187. r.outputs.push_back(co);
  188. } else {
  189. pos++; // Have : in status color
  190. co.len = pos;
  191. r.outputs.push_back(co);
  192. co.reset();
  193. co.pos = pos;
  194. co.c = value;
  195. co.len = txt.length() - pos;
  196. r.outputs.push_back(co);
  197. }
  198. return r;
  199. };
  200. return rf;
  201. }
  202. door::renderFunction rStatus = [](const std::string &txt) -> door::Render {
  203. door::Render r(txt);
  204. door::ColorOutput co;
  205. // default colors STATUS: value
  206. door::ANSIColor status(door::COLOR::BLUE, door::ATTR::BOLD);
  207. door::ANSIColor value(door::COLOR::YELLOW, door::ATTR::BOLD);
  208. co.pos = 0;
  209. co.len = 0;
  210. co.c = status;
  211. size_t pos = txt.find(':');
  212. if (pos == std::string::npos) {
  213. // failed to find - use entire string as status color.
  214. co.len = txt.length();
  215. r.outputs.push_back(co);
  216. } else {
  217. pos++; // Have : in status color
  218. co.len = pos;
  219. r.outputs.push_back(co);
  220. co.reset();
  221. co.pos = pos;
  222. co.c = value;
  223. co.len = txt.length() - pos;
  224. r.outputs.push_back(co);
  225. }
  226. return r;
  227. };
  228. std::string return_current_time_and_date() {
  229. auto now = std::chrono::system_clock::now();
  230. auto in_time_t = std::chrono::system_clock::to_time_t(now);
  231. std::stringstream ss;
  232. // ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X");
  233. ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %r");
  234. return ss.str();
  235. }
  236. int press_a_key(door::Door &door) {
  237. door << door::reset << "Press a key to continue...";
  238. int r = door.sleep_key(door.inactivity);
  239. door << door::nl;
  240. return r;
  241. }
  242. door::Menu make_config_menu(void) {
  243. door::Menu m(5, 5, 31);
  244. door::Line mtitle(SPACEACE " Configuration Menu");
  245. door::ANSIColor border_color(door::COLOR::CYAN, door::COLOR::BLUE);
  246. door::ANSIColor title_color(door::COLOR::CYAN, door::COLOR::BLUE,
  247. door::ATTR::BOLD);
  248. m.setColor(border_color);
  249. mtitle.setColor(title_color);
  250. mtitle.setPadding(" ", title_color);
  251. m.setTitle(std::make_unique<door::Line>(mtitle), 1);
  252. // m.setColorizer(true,
  253. m.setRender(true, door::Menu::makeRender(
  254. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  255. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD),
  256. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  257. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD)));
  258. // m.setColorizer(false,
  259. m.setRender(false, door::Menu::makeRender(
  260. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  261. door::ATTR::BOLD),
  262. door::ANSIColor(door::COLOR::WHITE, door::COLOR::BLUE,
  263. door::ATTR::BOLD),
  264. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  265. door::ATTR::BOLD),
  266. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLUE,
  267. door::ATTR::BOLD)));
  268. m.addSelection('D', "Deck Colors");
  269. m.addSelection('Q', "Quit");
  270. return m;
  271. }
  272. vector<std::string> deck_colors = {std::string("All"), std::string("Blue"),
  273. std::string("Cyan"), std::string("Green"),
  274. std::string("Magenta"), std::string("Red")};
  275. /**
  276. * @brief menu render that sets the text color based on the color found in the
  277. * text itself.
  278. *
  279. * @param c1
  280. * @param c2
  281. * @param c3
  282. * @return door::renderFunction
  283. */
  284. door::renderFunction makeColorRender(door::ANSIColor c1, door::ANSIColor c2,
  285. door::ANSIColor c3) {
  286. door::renderFunction render = [c1, c2,
  287. c3](const std::string &txt) -> door::Render {
  288. door::Render r(txt);
  289. bool option = true;
  290. door::ColorOutput co;
  291. // I need this mutable
  292. door::ANSIColor textColor = c3;
  293. // Color update:
  294. {
  295. std::string found;
  296. for (auto &dc : deck_colors) {
  297. if (txt.find(dc) != string::npos) {
  298. found = dc;
  299. break;
  300. }
  301. }
  302. if (!found.empty()) {
  303. if (found == "All") {
  304. // handle this some other way.
  305. textColor.setFg(door::COLOR::WHITE);
  306. } else {
  307. door::ANSIColor c = from_string(found);
  308. textColor.setFg(c.getFg());
  309. }
  310. }
  311. }
  312. co.pos = 0;
  313. co.len = 0;
  314. co.c = c1;
  315. // d << blue;
  316. int tpos = 0;
  317. for (char const &c : txt) {
  318. if (option) {
  319. if (c == '[' or c == ']') {
  320. if (co.c != c1)
  321. if (co.len != 0) {
  322. r.outputs.push_back(co);
  323. co.reset();
  324. co.pos = tpos;
  325. }
  326. co.c = c1;
  327. if (c == ']')
  328. option = false;
  329. } else {
  330. if (co.c != c2)
  331. if (co.len != 0) {
  332. r.outputs.push_back(co);
  333. co.reset();
  334. co.pos = tpos;
  335. }
  336. co.c = c2;
  337. }
  338. } else {
  339. if (co.c != textColor)
  340. if (co.len != 0) {
  341. r.outputs.push_back(co);
  342. co.reset();
  343. co.pos = tpos;
  344. }
  345. co.c = textColor;
  346. }
  347. co.len++;
  348. tpos++;
  349. }
  350. if (co.len != 0) {
  351. r.outputs.push_back(co);
  352. }
  353. return r;
  354. };
  355. return render;
  356. }
  357. door::Menu make_deck_menu(void) {
  358. door::Menu m(5, 5, 31);
  359. door::Line mtitle(SPACEACE " Deck Menu");
  360. door::ANSIColor border_color(door::COLOR::CYAN, door::COLOR::BLUE);
  361. door::ANSIColor title_color(door::COLOR::CYAN, door::COLOR::BLUE,
  362. door::ATTR::BOLD);
  363. m.setColor(border_color);
  364. mtitle.setColor(title_color);
  365. mtitle.setPadding(" ", title_color);
  366. m.setTitle(std::make_unique<door::Line>(mtitle), 1);
  367. /*
  368. // m.setColorizer(true,
  369. m.setRender(true, door::Menu::makeRender(
  370. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  371. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD),
  372. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  373. door::ANSIColor(door::COLOR::BLUE,
  374. door::ATTR::BOLD)));
  375. // m.setColorizer(false,
  376. m.setRender(false, door::Menu::makeRender(
  377. door::ANSIColor(door::COLOR::YELLOW,
  378. door::COLOR::BLUE, door::ATTR::BOLD), door::ANSIColor(door::COLOR::WHITE,
  379. door::COLOR::BLUE, door::ATTR::BOLD), door::ANSIColor(door::COLOR::YELLOW,
  380. door::COLOR::BLUE, door::ATTR::BOLD), door::ANSIColor(door::COLOR::CYAN,
  381. door::COLOR::BLUE, door::ATTR::BOLD)));
  382. */
  383. m.setRender(true, makeColorRender(
  384. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  385. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD),
  386. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD)));
  387. m.setRender(false, makeColorRender(
  388. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  389. door::ATTR::BOLD),
  390. door::ANSIColor(door::COLOR::WHITE, door::COLOR::BLUE,
  391. door::ATTR::BOLD),
  392. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  393. door::ATTR::BOLD)));
  394. for (auto iter = deck_colors.begin(); iter != deck_colors.end(); ++iter) {
  395. char c = (*iter)[0];
  396. m.addSelection(c, (*iter).c_str());
  397. }
  398. /*
  399. m.addSelection('A', "All");
  400. m.addSelection('B', "Blue");
  401. m.addSelection('C', "Cyan");
  402. m.addSelection('G', "Green");
  403. m.addSelection('M', "Magenta");
  404. m.addSelection('R', "Red");
  405. */
  406. return m;
  407. }
  408. bool iequals(const string &a, const string &b) {
  409. unsigned int sz = a.size();
  410. if (b.size() != sz)
  411. return false;
  412. for (unsigned int i = 0; i < sz; ++i)
  413. if (tolower(a[i]) != tolower(b[i]))
  414. return false;
  415. return true;
  416. }
  417. // convert a string to an option
  418. // an option to the string to store
  419. door::ANSIColor from_string(std::string colorCode) {
  420. std::map<std::string, door::ANSIColor> codeMap = {
  421. {std::string("BLUE"), door::ANSIColor(door::COLOR::BLUE)},
  422. {std::string("RED"), door::ANSIColor(door::COLOR::RED)},
  423. {std::string("CYAN"), door::ANSIColor(door::COLOR::CYAN)},
  424. {std::string("GREEN"), door::ANSIColor(door::COLOR::GREEN)},
  425. {std::string("MAGENTA"), door::ANSIColor(door::COLOR::MAGENTA)}};
  426. std::string code = colorCode;
  427. string_toupper(code);
  428. auto iter = codeMap.find(code);
  429. if (iter != codeMap.end()) {
  430. return iter->second;
  431. }
  432. // And if it doesn't match, and isn't ALL ... ?
  433. // if (code.compare("ALL") == 0) {
  434. std::random_device dev;
  435. std::mt19937_64 rng(dev());
  436. std::uniform_int_distribution<size_t> idDist(0, codeMap.size() - 1);
  437. iter = codeMap.begin();
  438. std::advance(iter, idDist(rng));
  439. return iter->second;
  440. // }
  441. }
  442. // This does not seem to be working. I keep getting zero.
  443. int opt_from_string(std::string colorCode) {
  444. for (std::size_t pos = 0; pos != deck_colors.size(); ++pos) {
  445. // if (caseInsensitiveStringCompare(colorCode, deck_colors[pos]) == 0) {
  446. if (iequals(colorCode, deck_colors[pos])) {
  447. return pos;
  448. }
  449. }
  450. return 0;
  451. }
  452. std::string from_color_option(int opt) { return deck_colors[opt]; }
  453. int configure(door::Door &door, DBData &db) {
  454. auto menu = make_config_menu();
  455. int r = 0;
  456. while (r >= 0) {
  457. r = menu.choose(door);
  458. if (r > 0) {
  459. door << door::reset << door::cls;
  460. char c = menu.which(r - 1);
  461. if (c == 'D') {
  462. // Ok, deck colors
  463. // get default
  464. std::string key("DeckColor");
  465. std::string currentDefault = db.getSetting(key, std::string("ALL"));
  466. int currentOpt = opt_from_string(currentDefault);
  467. door << door::reset << door::cls;
  468. auto deck = make_deck_menu();
  469. deck.defaultSelection(currentOpt);
  470. int newOpt = deck.choose(door);
  471. door << door::reset << door::cls;
  472. if (newOpt >= 0) {
  473. newOpt--;
  474. std::string newColor = from_color_option(newOpt);
  475. if (newOpt != currentOpt) {
  476. door.log() << key << " was " << currentDefault << ", " << currentOpt
  477. << ". Now " << newColor << ", " << newOpt << std::endl;
  478. db.setSetting(key, newColor);
  479. }
  480. }
  481. }
  482. if (c == 'Q') {
  483. return r;
  484. }
  485. }
  486. }
  487. return r;
  488. }
  489. door::Panel make_panel1(door::Door &door) {
  490. const int W = 25;
  491. door::Panel p(W);
  492. p.setStyle(door::BorderStyle::NONE);
  493. door::ANSIColor statusColor(door::COLOR::WHITE, door::COLOR::BLUE,
  494. door::ATTR::BOLD);
  495. door::ANSIColor valueColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  496. door::ATTR::BOLD);
  497. door::renderFunction svRender = renderStatusValue(statusColor, valueColor);
  498. // or use renderStatus as defined above.
  499. // We'll stick with these for now.
  500. {
  501. std::string userString = "Name: ";
  502. userString += door.username;
  503. door::Line username(userString, W);
  504. username.setRender(svRender);
  505. p.addLine(std::make_unique<door::Line>(username));
  506. }
  507. {
  508. door::updateFunction scoreUpdate = [](void) -> std::string {
  509. std::string text = "Score: ";
  510. text.append(std::to_string(score));
  511. return text;
  512. };
  513. std::string scoreString = scoreUpdate();
  514. door::Line score(scoreString, W);
  515. score.setRender(svRender);
  516. score.setUpdater(scoreUpdate);
  517. p.addLine(std::make_unique<door::Line>(score));
  518. }
  519. {
  520. door::updateFunction timeUpdate = [&door](void) -> std::string {
  521. std::stringstream ss;
  522. std::string text;
  523. ss << "Time used: " << setw(3) << door.time_used << " / " << setw(3)
  524. << door.time_left;
  525. text = ss.str();
  526. return text;
  527. };
  528. std::string timeString = timeUpdate();
  529. door::Line time(timeString, W);
  530. time.setRender(svRender);
  531. time.setUpdater(timeUpdate);
  532. p.addLine(std::make_unique<door::Line>(time));
  533. }
  534. {
  535. door::updateFunction handUpdate = [](void) -> std::string {
  536. std::string text = "Playing Hand ";
  537. text.append(std::to_string(hand));
  538. text.append(" of ");
  539. text.append(std::to_string(total_hands));
  540. return text;
  541. };
  542. std::string handString = handUpdate();
  543. door::Line hands(handString, W);
  544. hands.setRender(svRender);
  545. hands.setUpdater(handUpdate);
  546. p.addLine(std::make_unique<door::Line>(hands));
  547. }
  548. return p;
  549. }
  550. door::Panel make_panel2(void) {
  551. const int W = 20;
  552. door::Panel p(W);
  553. p.setStyle(door::BorderStyle::NONE);
  554. door::ANSIColor statusColor(door::COLOR::WHITE, door::COLOR::BLUE,
  555. door::ATTR::BOLD);
  556. door::ANSIColor valueColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  557. door::ATTR::BOLD);
  558. door::renderFunction svRender = renderStatusValue(statusColor, valueColor);
  559. {
  560. std::string text = "Playing: ";
  561. auto in_time_t = std::chrono::system_clock::to_time_t(play_day);
  562. std::stringstream ss;
  563. ss << std::put_time(std::localtime(&in_time_t), "%B %d");
  564. text.append(ss.str());
  565. }
  566. {
  567. door::updateFunction currentUpdate = [](void) -> std::string {
  568. std::string text = "Current Streak: ";
  569. text.append(std::to_string(current_streak));
  570. return text;
  571. };
  572. std::string currentString = currentUpdate();
  573. door::Line current(currentString, W);
  574. current.setRender(svRender);
  575. current.setUpdater(currentUpdate);
  576. p.addLine(std::make_unique<door::Line>(current));
  577. }
  578. {
  579. door::updateFunction currentUpdate = [](void) -> std::string {
  580. std::string text = "Longest Streak: ";
  581. text.append(std::to_string(best_streak));
  582. return text;
  583. };
  584. std::string currentString = currentUpdate();
  585. door::Line current(currentString, W);
  586. current.setRender(svRender);
  587. current.setUpdater(currentUpdate);
  588. p.addLine(std::make_unique<door::Line>(current));
  589. }
  590. return p;
  591. }
  592. door::renderFunction commandLineRender(door::ANSIColor bracket,
  593. door::ANSIColor inner,
  594. door::ANSIColor outer) {
  595. door::renderFunction rf = [bracket, inner,
  596. outer](const std::string &txt) -> door::Render {
  597. door::Render r(txt);
  598. door::ColorOutput co;
  599. co.pos = 0;
  600. co.len = 0;
  601. co.c = outer;
  602. bool inOuter = true;
  603. int tpos = 0;
  604. for (char const &c : txt) {
  605. if (inOuter) {
  606. // we're in the outer text
  607. if (co.c != outer) {
  608. if (co.len != 0) {
  609. r.outputs.push_back(co);
  610. co.reset();
  611. co.pos = tpos;
  612. }
  613. co.c = outer;
  614. }
  615. // check for [
  616. if (c == '[') {
  617. if (co.len != 0) {
  618. r.outputs.push_back(co);
  619. co.reset();
  620. co.pos = tpos;
  621. }
  622. inOuter = false;
  623. co.c = bracket;
  624. }
  625. } else {
  626. // We're not in the outer.
  627. if (co.c != inner) {
  628. if (co.len != 0) {
  629. r.outputs.push_back(co);
  630. co.reset();
  631. co.pos = tpos;
  632. }
  633. co.c = inner;
  634. }
  635. if (c == ']') {
  636. if (co.len != 0) {
  637. r.outputs.push_back(co);
  638. co.reset();
  639. co.pos = tpos;
  640. }
  641. inOuter = true;
  642. co.c = bracket;
  643. }
  644. }
  645. co.len++;
  646. tpos++;
  647. }
  648. if (co.len != 0)
  649. r.outputs.push_back(co);
  650. return r;
  651. };
  652. return rf;
  653. }
  654. door::Panel make_commandline(void) {
  655. const int W = 76;
  656. door::Panel p(W);
  657. p.setStyle(door::BorderStyle::NONE);
  658. std::string commands;
  659. if (door::unicode) {
  660. commands = "[4/\u25c4] Left [6/\u25ba] Right [Space] Play Card [Enter] "
  661. "Draw [Q]uit "
  662. "[R]edraw [H]elp";
  663. } else {
  664. commands =
  665. "[4/\x11] Left [6/\x10] Right [Space] Play Card [Enter] Draw [Q]uit "
  666. "[R]edraw [H]elp";
  667. }
  668. door::ANSIColor bracketColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  669. door::ATTR::BOLD);
  670. door::ANSIColor innerColor(door::COLOR::CYAN, door::COLOR::BLUE,
  671. door::ATTR::BOLD);
  672. door::ANSIColor outerColor(door::COLOR::GREEN, door::COLOR::BLUE,
  673. door::ATTR::BOLD);
  674. door::renderFunction cmdRender =
  675. commandLineRender(bracketColor, innerColor, outerColor);
  676. door::Line cmd(commands, W);
  677. cmd.setRender(cmdRender);
  678. p.addLine(std::make_unique<door::Line>(cmd));
  679. return p;
  680. }
  681. int play_cards(door::Door &door, DBData &db, std::mt19937 &rng) {
  682. int mx = door.width;
  683. int my = door.height;
  684. // cards color --
  685. // configured by the player.
  686. door::ANSIColor deck_color;
  687. // std::string key("DeckColor");
  688. const char *key = "DeckColor";
  689. std::string currentDefault = db.getSetting(key, "ALL");
  690. door.log() << key << " shows as " << currentDefault << std::endl;
  691. deck_color = from_string(currentDefault);
  692. int height = 3;
  693. Deck d(deck_color, height);
  694. door::Panel *c;
  695. door << door::reset << door::cls;
  696. // This displays the cards in the upper left corner.
  697. // We want them center, and down some.
  698. const int space = 3;
  699. // int cards_dealt_width = 59; int cards_dealt_height = 9;
  700. int game_width;
  701. int game_height = 13; // 9;
  702. {
  703. int cx, cy, level;
  704. cardgo(27, space, height, cx, cy, level);
  705. game_width = cx + 5; // card width
  706. }
  707. int off_x = (mx - game_width) / 2;
  708. int off_y = (my - game_height) / 2;
  709. // The idea is to see the cards with <<Something Unique to the card game>>,
  710. // Year, Month, Day, and game (like 1 of 3).
  711. // This will make the games the same/fair for everyone.
  712. std::string tripeaks(" " SPACEACE " - Tri-Peaks Solitaire v" SPACEACE_VERSION
  713. " ");
  714. int tp_off_x = (mx - tripeaks.size()) / 2;
  715. door::Panel spaceAceTriPeaks(tp_off_x, off_y, tripeaks.size());
  716. spaceAceTriPeaks.setStyle(door::BorderStyle::SINGLE);
  717. spaceAceTriPeaks.setColor(
  718. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLACK));
  719. spaceAceTriPeaks.addLine(
  720. std::make_unique<door::Line>(tripeaks, tripeaks.size()));
  721. door << spaceAceTriPeaks;
  722. off_y += 3;
  723. // figure out what date we're playing / what game we're playing
  724. std::seed_seq s1{2021, 2, 27, 1};
  725. cards deck1 = card_shuffle(s1, 1);
  726. cards state = card_states();
  727. // I tried setting the cursor before the delay and before displaying the
  728. // card. It is very hard to see / just about useless. Not worth the effort.
  729. for (int x = 0; x < 28; x++) {
  730. int cx, cy, level;
  731. cardgo(x, space, height, cx, cy, level);
  732. // This is hardly visible.
  733. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  734. std::this_thread::sleep_for(std::chrono::milliseconds(75));
  735. c = d.back(level);
  736. c->set(cx + off_x, cy + off_y);
  737. door << *c;
  738. }
  739. for (int x = 18; x < 28; x++) {
  740. int cx, cy, level;
  741. state.at(x) = 1;
  742. cardgo(x, space, height, cx, cy, level);
  743. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  744. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  745. c = d.card(deck1.at(x));
  746. c->set(cx + off_x, cy + off_y);
  747. door << *c;
  748. }
  749. off_y += 11;
  750. door::Panel p1 = make_panel1(door);
  751. door::Panel p2 = make_panel2();
  752. door::Panel pc = make_commandline();
  753. p1.set(off_x + 3, off_y);
  754. p2.set(off_x + 55, off_y);
  755. pc.set(off_x + 3, off_y + 5);
  756. door << p1 << p2 << pc;
  757. door << door::reset;
  758. door << door::nl;
  759. int r = door.sleep_key(door.inactivity);
  760. return r;
  761. }
  762. door::Panel make_about(void) {
  763. door::Panel about(60);
  764. about.setStyle(door::BorderStyle::DOUBLE_SINGLE);
  765. about.setColor(door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  766. door::ATTR::BOLD));
  767. about.addLine(std::make_unique<door::Line>("About This Door", 60));
  768. about.addLine(std::make_unique<door::Line>(
  769. "---------------------------------", 60,
  770. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLUE, door::ATTR::BOLD)));
  771. /*
  772. 123456789012345678901234567890123456789012345678901234567890
  773. This door was written by Bugz.
  774. It is written in c++, only supports Linux, and replaces
  775. opendoors.
  776. It's written in c++, and replaces the outdated opendoors
  777. library.
  778. */
  779. about.addLine(
  780. std::make_unique<door::Line>(SPACEACE " v" SPACEACE_VERSION, 60));
  781. std::string copyright = SPACEACE_COPYRIGHT;
  782. if (door::unicode) {
  783. /*
  784. std::string textcp = "(C)";
  785. std::string utf8cp = "\u00a9";
  786. replace(copyright, textcp, utf8cp);*/
  787. replace(copyright, "(C)", "\u00a9");
  788. }
  789. about.addLine(std::make_unique<door::Line>(copyright, 60));
  790. about.addLine(std::make_unique<door::Line>("", 60));
  791. about.addLine(
  792. std::make_unique<door::Line>("This door was written by Bugz.", 60));
  793. about.addLine(std::make_unique<door::Line>("", 60));
  794. about.addLine(std::make_unique<door::Line>(
  795. "It is written in c++, only support Linux, and replaces", 60));
  796. about.addLine(std::make_unique<door::Line>("opendoors.", 60));
  797. /*
  798. door::updateFunction updater = [](void) -> std::string {
  799. std::string text = "Currently: ";
  800. text.append(return_current_time_and_date());
  801. return text;
  802. };
  803. std::string current = updater();
  804. door::Line active(current, 60);
  805. active.setUpdater(updater);
  806. active.setRender(renderStatusValue(
  807. door::ANSIColor(door::COLOR::WHITE, door::COLOR::BLUE,
  808. door::ATTR::BOLD), door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  809. door::ATTR::BOLD)));
  810. about.addLine(std::make_unique<door::Line>(active));
  811. */
  812. /*
  813. about.addLine(std::make_unique<door::Line>(
  814. "Status: blue", 60,
  815. statusValue(door::ANSIColor(door::COLOR::GREEN, door::ATTR::BOLD),
  816. door::ANSIColor(door::COLOR::MAGENTA,
  817. door::ATTR::BLINK)))); about.addLine(std::make_unique<door::Line>("Name:
  818. BUGZ", 60, rStatus)); about.addLine(std::make_unique<door::Line>( "Size:
  819. 10240", 60, statusValue(door::ANSIColor(door::COLOR::GREEN,
  820. door::COLOR::BLUE, door::ATTR::BOLD), door::ANSIColor(door::COLOR::YELLOW,
  821. door::COLOR::BLUE, door::ATTR::BOLD, door::ATTR::BLINK))));
  822. about.addLine(std::make_unique<door::Line>("Bugz is here.", 60, rStatus));
  823. */
  824. return about;
  825. }
  826. void display_starfield(door::Door &door, std::mt19937 &rng) {
  827. door << door::reset << door::cls;
  828. int mx = door.width;
  829. int my = door.height;
  830. // display starfield
  831. const char *stars[2];
  832. stars[0] = ".";
  833. if (door::unicode) {
  834. stars[1] = "\u2219"; // "\u00b7";
  835. } else {
  836. stars[1] = "\xf9"; // "\xfa";
  837. };
  838. {
  839. // Make uniform random distribution between 1 and MAX screen size X/Y
  840. std::uniform_int_distribution<int> uni_x(1, mx);
  841. std::uniform_int_distribution<int> uni_y(1, my);
  842. door::ANSIColor white(door::COLOR::WHITE);
  843. door::ANSIColor dark(door::COLOR::BLACK, door::ATTR::BRIGHT);
  844. // 10 is too many, 100 is too few. 40 looks ok.
  845. int MAX_STARS = ((mx * my) / 40);
  846. // door.log() << "Generating starmap using " << mx << "," << my << " : "
  847. // << MAX_STARS << " stars." << std::endl;
  848. for (int x = 0; x < MAX_STARS; x++) {
  849. door::Goto star_at(uni_x(rng), uni_y(rng));
  850. door << star_at;
  851. if (x % 5 < 2)
  852. door << dark;
  853. else
  854. door << white;
  855. if (x % 2 == 0)
  856. door << stars[0];
  857. else
  858. door << stars[1];
  859. }
  860. }
  861. }
  862. void display_space_ace(door::Door &door) {
  863. int mx = door.width;
  864. int my = door.height;
  865. // space_ace is 72 chars wide, 6 high
  866. int sa_x = (mx - 72) / 2;
  867. int sa_y = (my - 6) / 2;
  868. // output the SpaceAce logo -- centered!
  869. for (const auto s : space) {
  870. door::Goto sa_at(sa_x, sa_y);
  871. door << sa_at;
  872. if (door::unicode) {
  873. std::string unicode;
  874. door::cp437toUnicode(s, unicode);
  875. door << unicode; // << door::nl;
  876. } else
  877. door << s; // << door::nl;
  878. sa_y++;
  879. }
  880. // pause 5 seconds so they can enjoy our awesome logo -- if they want.
  881. door.sleep_key(5);
  882. }
  883. void display_starfield_space_ace(door::Door &door, std::mt19937 &rng) {
  884. // mx = door.width;
  885. // my = door.height;
  886. display_starfield(door, rng);
  887. display_space_ace(door);
  888. door << door::reset;
  889. }
  890. int main(int argc, char *argv[]) {
  891. door::Door door("space-ace", argc, argv);
  892. // store the door log so we can easily access it.
  893. get_logger = [&door]() -> ofstream & { return door.log(); };
  894. DBData spacedb;
  895. spacedb.setUser(door.username);
  896. // retrieve lastcall
  897. time_t last_call = std::stol(spacedb.getSetting("LastCall", "0"));
  898. // store now as lastcall
  899. time_t now =
  900. std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
  901. spacedb.setSetting("LastCall", std::to_string(now));
  902. // Have they used this door before?
  903. if (last_call != 0) {
  904. door << "Welcome Back!" << door::nl;
  905. auto nowClock = std::chrono::system_clock::from_time_t(now);
  906. auto lastClock = std::chrono::system_clock::from_time_t(last_call);
  907. auto delta = nowClock - lastClock;
  908. // int days = chrono::duration_cast<chrono::days>(delta).count(); // c++ 20
  909. int hours = chrono::duration_cast<chrono::hours>(delta).count();
  910. int days = hours / 24;
  911. int minutes = chrono::duration_cast<chrono::minutes>(delta).count();
  912. if (days > 1) {
  913. door << "It's been " << days << " days since you last played."
  914. << door::nl;
  915. } else {
  916. if (hours > 1) {
  917. door << "It's been " << hours << " hours since you last played."
  918. << door::nl;
  919. } else {
  920. door << "It's been " << minutes << " minutes since you last played."
  921. << door::nl;
  922. }
  923. }
  924. press_a_key(door);
  925. }
  926. /*
  927. // example: saving the door.log() for global use.
  928. std::function<std::ofstream &(void)> get_logger;
  929. get_logger = [&door]() -> ofstream & { return door.log(); };
  930. if (get_logger) {
  931. get_logger() << "MEOW" << std::endl;
  932. get_logger() << "hey! It works!" << std::endl;
  933. }
  934. get_logger = nullptr; // before door destruction
  935. */
  936. // https://stackoverflow.com/questions/5008804/generating-random-integer-from-a-range
  937. std::random_device rd; // only used once to initialise (seed) engine
  938. std::mt19937 rng(rd());
  939. // random-number engine used (Mersenne-Twister in this case)
  940. // std::uniform_int_distribution<int> uni(min, max); // guaranteed unbiased
  941. int mx, my; // Max screen width/height
  942. if (door.width == 0) {
  943. // screen detect failed, use sensible defaults
  944. door.width = mx = 80;
  945. door.height = my = 23;
  946. } else {
  947. mx = door.width;
  948. my = door.height;
  949. }
  950. // We assume here that the width and height aren't something crazy like 10x15.
  951. // :P (or 24x923!)
  952. display_starfield_space_ace(door, rng);
  953. // for testing inactivity timeout
  954. // door.inactivity = 10;
  955. door::Panel timeout = make_timeout(mx, my);
  956. door::Menu m = make_main_menu();
  957. door::Panel about = make_about(); // 8 lines
  958. // center the about box
  959. about.set((mx - 60) / 2, (my - 9) / 2);
  960. int r = 0;
  961. while ((r >= 0) and (r != 6)) {
  962. // starfield + menu ?
  963. display_starfield(door, rng);
  964. r = m.choose(door);
  965. // need to reset the colors. (whoops!)
  966. door << door::reset << door::cls; // door::nl;
  967. // OK! The screen is blank at this point!
  968. switch (r) {
  969. case 1: // play game
  970. r = play_cards(door, spacedb, rng);
  971. break;
  972. case 2: // view scores
  973. door << "Show scores goes here!" << door::nl;
  974. r = press_a_key(door);
  975. break;
  976. case 3: // configure
  977. r = configure(door, spacedb);
  978. // r = press_a_key(door);
  979. break;
  980. case 4: // help
  981. door << "Help! Need some help here..." << door::nl;
  982. r = press_a_key(door);
  983. break;
  984. case 5: // about
  985. display_starfield(door, rng);
  986. door << about << door::nl;
  987. r = press_a_key(door);
  988. break;
  989. case 6: // quit
  990. break;
  991. }
  992. }
  993. if (r < 0) {
  994. TIMEOUT:
  995. if (r == -1) {
  996. door.log() << "TIMEOUT" << std::endl;
  997. door << timeout << door::reset << door::nl << door::nl;
  998. } else {
  999. if (r == -3) {
  1000. door.log() << "OUTTA TIME" << std::endl;
  1001. door::Panel notime = make_notime(mx, my);
  1002. door << notime << door::reset << door::nl;
  1003. }
  1004. }
  1005. return 0;
  1006. }
  1007. door << door::nl;
  1008. /*
  1009. // magic time!
  1010. door << door::reset << door::nl << "Press another key...";
  1011. int x;
  1012. for (x = 0; x < 60; ++x) {
  1013. r = door.sleep_key(1);
  1014. if (r == -1) {
  1015. // ok! Expected timeout!
  1016. // PROBLEM: regular "local" terminal loses current attributes
  1017. // when cursor is save / restored.
  1018. door << door::SaveCursor;
  1019. if (about.update(door)) {
  1020. // ok I need to "fix" the cursor position.
  1021. // it has moved.
  1022. }
  1023. door << door::RestoreCursor << door::reset;
  1024. } else {
  1025. if (r < 0)
  1026. goto TIMEOUT;
  1027. if (r >= 0)
  1028. break;
  1029. }
  1030. }
  1031. if (x == 60)
  1032. goto TIMEOUT;
  1033. */
  1034. #ifdef NNY
  1035. // configured by the player.
  1036. door::ANSIColor deck_color;
  1037. // RED, BLUE, GREEN, MAGENTA, CYAN
  1038. std::uniform_int_distribution<int> rand_color(0, 4);
  1039. switch (rand_color(rng)) {
  1040. case 0:
  1041. deck_color = door::ANSIColor(door::COLOR::RED);
  1042. break;
  1043. case 1:
  1044. deck_color = door::ANSIColor(door::COLOR::BLUE);
  1045. break;
  1046. case 2:
  1047. deck_color = door::ANSIColor(door::COLOR::GREEN);
  1048. break;
  1049. case 3:
  1050. deck_color = door::ANSIColor(door::COLOR::MAGENTA);
  1051. break;
  1052. case 4:
  1053. deck_color = door::ANSIColor(door::COLOR::CYAN);
  1054. break;
  1055. default:
  1056. deck_color = door::ANSIColor(door::COLOR::BLUE, door::ATTR::BLINK);
  1057. break;
  1058. }
  1059. int height = 3;
  1060. Deck d(deck_color, height);
  1061. door::Panel *c;
  1062. door << door::reset << door::cls;
  1063. // This displays the cards in the upper left corner.
  1064. // We want them center, and down some.
  1065. int space = 3;
  1066. // int cards_dealt_width = 59; int cards_dealt_height = 9;
  1067. int game_width;
  1068. {
  1069. int cx, cy, level;
  1070. cardgo(27, space, height, cx, cy, level);
  1071. game_width = cx + 5; // card width
  1072. }
  1073. int off_x = (mx - game_width) / 2;
  1074. int off_y = (my - 9) / 2;
  1075. // The idea is to see the cards with <<Something Unique to the card game>>,
  1076. // Year, Month, Day, and game (like 1 of 3).
  1077. // This will make the games the same/fair for everyone.
  1078. std::seed_seq s1{2021, 2, 27, 1};
  1079. cards deck1 = card_shuffle(s1, 1);
  1080. cards state = card_states();
  1081. // I tried setting the cursor before the delay and before displaying the
  1082. // card. It is very hard to see / just about useless. Not worth the effort.
  1083. for (int x = 0; x < 28; x++) {
  1084. int cx, cy, level;
  1085. cardgo(x, space, height, cx, cy, level);
  1086. // This is hardly visible.
  1087. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  1088. std::this_thread::sleep_for(std::chrono::milliseconds(75));
  1089. c = d.back(level);
  1090. c->set(cx + off_x, cy + off_y);
  1091. door << *c;
  1092. }
  1093. /*
  1094. std::this_thread::sleep_for(
  1095. std::chrono::seconds(1)); // 3 secs seemed too long!
  1096. */
  1097. for (int x = 18; x < 28; x++) {
  1098. int cx, cy, level;
  1099. // usleep(1000 * 20);
  1100. state.at(x) = 1;
  1101. cardgo(x, space, height, cx, cy, level);
  1102. // door << door::Goto(cx + off_x - 1, cy + off_y + 1);
  1103. std::this_thread::sleep_for(std::chrono::milliseconds(200));
  1104. c = d.card(deck1.at(x));
  1105. c->set(cx + off_x, cy + off_y);
  1106. door << *c;
  1107. }
  1108. door << door::reset;
  1109. door << door::nl << door::nl;
  1110. r = door.sleep_key(door.inactivity);
  1111. if (r < 0)
  1112. goto TIMEOUT;
  1113. #endif
  1114. /*
  1115. door::Panel *p = d.back(2);
  1116. p->set(10, 10);
  1117. door << *p;
  1118. door::Panel *d8 = d.card(8);
  1119. d8->set(20, 8);
  1120. door << *d8;
  1121. r = door.sleep_key(door.inactivity);
  1122. if (r < 0)
  1123. goto TIMEOUT;
  1124. */
  1125. // door << door::reset << door::cls;
  1126. display_starfield(door, rng);
  1127. door << m << door::reset << door::nl;
  1128. // Normal DOOR exit goes here...
  1129. door << door::nl << "Returning you to the BBS, please wait..." << door::nl;
  1130. get_logger = nullptr;
  1131. return 0;
  1132. }