main.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. #include "door.h"
  2. #include "space.h"
  3. #include <iostream>
  4. #include <random>
  5. #include <string>
  6. #include "deck.h"
  7. /*
  8. Cards:
  9. 4 layers deep.
  10. https://en.wikipedia.org/wiki/Code_page_437
  11. using: \xb0, 0xb1, 0xb2, 0xdb
  12. OR: \u2591, \u2592, \u2593, \u2588
  13. Like so:
  14. ##### #####
  15. ##### #####
  16. ##### #####
  17. Cards: (Black on White, or Red on White)
  18. 8D### TH###
  19. ##D## ##H##
  20. ###D8 ###HT
  21. D, H = Red, Clubs, Spades = Black.
  22. ^ Where D = Diamonds, H = Hearts
  23. ♥, ♦, ♣, ♠
  24. \x03, \x04, \x05, \x06
  25. \u2665, \u2666, \u2663, \u2660
  26. Card layout. Actual cards are 3 lines thick.
  27. ░░░░░ ░░░░░ ░░░░░
  28. ░░░░░ ░░░░░ ░░░░░
  29. ▒▒▒▒▒░▒▒▒▒▒ #####░##### #####░#####
  30. ▒▒▒▒▒ ▒▒▒▒▒ ##### ##### ##### #####
  31. ▓▓▓▓▓▒▓▓▓▓▓▒▓▓▓▓▓ #####=#####=##### #####=#####=#####
  32. ▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓ ##### ##### ##### ##### ##### #####
  33. █████▓█████▓█████▓#####=#####=#####=#####=#####=#####=#####
  34. █████ █████ █████ ##### ##### ##### ##### ##### ##### #####
  35. █████ █████ █████ ##### ##### ##### ##### ##### ##### #####
  36. #####
  37. Player Information ##### Time in: xx Time out: xx
  38. Name: ##### Playing Day: November 3rd
  39. Hand Score : Current Streak: N
  40. Todays Score : XX Cards Remaining Longest Streak: NN
  41. Monthly Score: Playing Hand X of X Most Won: xxx Lost: xxx
  42. [4] Lf [6] Rt [Space] Play Card [Enter] Draw [D]one [H]elp [R]edraw
  43. ►, ◄, ▲, ▼
  44. \x10, \x11, \x1e, \x1f
  45. \u25ba, \u25c4, \u25b2, \u25bc
  46. The Name is <- 6 to the left.
  47. # is back of card. = is upper card showing between.
  48. There's no fancy anything here. Cards overlap the last
  49. line of the previous line/card.
  50. */
  51. // The idea is that this would be defined elsewhere (maybe)
  52. int user_score = 0;
  53. // NOTE: When run as a door, we always detect CP437 (because that's what Enigma
  54. // is defaulting to)
  55. void adjust_score(int by) { user_score += by; }
  56. door::Panel make_timeout(int mx, int my) {
  57. door::ANSIColor yellowred =
  58. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::RED, door::ATTR::BOLD);
  59. std::string line_text("Sorry, you've been inactive for too long.");
  60. int msgWidth = line_text.length() + (2 * 3); // + padding * 2
  61. door::Panel timeout((mx - (msgWidth)) / 2, my / 2 + 4, msgWidth);
  62. // place.setTitle(std::make_unique<door::Line>(title), 1);
  63. timeout.setStyle(door::BorderStyle::DOUBLE);
  64. timeout.setColor(yellowred);
  65. door::Line base(line_text);
  66. base.setColor(yellowred);
  67. std::string pad1(3, ' ');
  68. /*
  69. std::string pad1(3, '\xb0');
  70. if (door::unicode) {
  71. std::string unicode;
  72. door::cp437toUnicode(pad1.c_str(), unicode);
  73. pad1 = unicode;
  74. }
  75. */
  76. base.setPadding(pad1, yellowred);
  77. // base.setColor(door::ANSIColor(door::COLOR::GREEN, door::COLOR::BLACK));
  78. std::unique_ptr<door::Line> stuff = std::make_unique<door::Line>(base);
  79. timeout.addLine(std::make_unique<door::Line>(base));
  80. return timeout;
  81. }
  82. door::Panel make_notime(int mx, int my) {
  83. door::ANSIColor yellowred =
  84. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::RED, door::ATTR::BOLD);
  85. std::string line_text("Sorry, you've used up all your time for today.");
  86. int msgWidth = line_text.length() + (2 * 3); // + padding * 2
  87. door::Panel timeout((mx - (msgWidth)) / 2, my / 2 + 4, msgWidth);
  88. // place.setTitle(std::make_unique<door::Line>(title), 1);
  89. timeout.setStyle(door::BorderStyle::DOUBLE);
  90. timeout.setColor(yellowred);
  91. door::Line base(line_text);
  92. base.setColor(yellowred);
  93. std::string pad1(3, ' ');
  94. /*
  95. std::string pad1(3, '\xb0');
  96. if (door::unicode) {
  97. std::string unicode;
  98. door::cp437toUnicode(pad1.c_str(), unicode);
  99. pad1 = unicode;
  100. }
  101. */
  102. base.setPadding(pad1, yellowred);
  103. // base.setColor(door::ANSIColor(door::COLOR::GREEN, door::COLOR::BLACK));
  104. std::unique_ptr<door::Line> stuff = std::make_unique<door::Line>(base);
  105. timeout.addLine(std::make_unique<door::Line>(base));
  106. return timeout;
  107. }
  108. door::Menu make_main_menu(void) {
  109. door::Menu m(5, 5, 25);
  110. door::Line mtitle("Space-Ace Main Menu");
  111. door::ANSIColor border_color(door::COLOR::CYAN, door::COLOR::BLUE);
  112. door::ANSIColor title_color(door::COLOR::CYAN, door::COLOR::BLUE,
  113. door::ATTR::BOLD);
  114. m.setColor(border_color);
  115. mtitle.setColor(title_color);
  116. mtitle.setPadding(" ", title_color);
  117. m.setTitle(std::make_unique<door::Line>(mtitle), 1);
  118. // m.setColorizer(true,
  119. m.setRender(true, door::Menu::makeRender(
  120. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  121. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD),
  122. door::ANSIColor(door::COLOR::CYAN, door::ATTR::BOLD),
  123. door::ANSIColor(door::COLOR::BLUE, door::ATTR::BOLD)));
  124. // m.setColorizer(false,
  125. m.setRender(false, door::Menu::makeRender(
  126. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  127. door::ATTR::BOLD),
  128. door::ANSIColor(door::COLOR::WHITE, door::COLOR::BLUE,
  129. door::ATTR::BOLD),
  130. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  131. door::ATTR::BOLD),
  132. door::ANSIColor(door::COLOR::CYAN, door::COLOR::BLUE,
  133. door::ATTR::BOLD)));
  134. m.addSelection('1', "Play Cards");
  135. m.addSelection('2', "View Scores");
  136. m.addSelection('3', "Drop to DOS");
  137. m.addSelection('4', "Chat with BUGZ");
  138. m.addSelection('H', "Help");
  139. m.addSelection('A', "About this game");
  140. m.addSelection('Q', "Quit");
  141. return m;
  142. }
  143. door::renderFunction statusValue(door::ANSIColor status,
  144. door::ANSIColor value) {
  145. door::renderFunction rf = [status,
  146. value](const std::string &txt) -> door::Render {
  147. door::Render r(txt);
  148. door::ColorOutput co;
  149. co.pos = 0;
  150. co.len = 0;
  151. co.c = status;
  152. size_t pos = txt.find(':');
  153. if (pos == std::string::npos) {
  154. // failed to find - use entire string as status color.
  155. co.len = txt.length();
  156. r.outputs.push_back(co);
  157. } else {
  158. pos++; // Have : in status color
  159. co.len = pos;
  160. r.outputs.push_back(co);
  161. co.reset();
  162. co.pos = pos;
  163. co.c = value;
  164. co.len = txt.length() - pos;
  165. r.outputs.push_back(co);
  166. }
  167. return r;
  168. };
  169. return rf;
  170. }
  171. door::renderFunction rStatus = [](const std::string &txt) -> door::Render {
  172. door::Render r(txt);
  173. door::ColorOutput co;
  174. // default colors STATUS: value
  175. door::ANSIColor status(door::COLOR::BLUE, door::ATTR::BOLD);
  176. door::ANSIColor value(door::COLOR::YELLOW, door::ATTR::BOLD);
  177. co.pos = 0;
  178. co.len = 0;
  179. co.c = status;
  180. size_t pos = txt.find(':');
  181. if (pos == std::string::npos) {
  182. // failed to find - use entire string as status color.
  183. co.len = txt.length();
  184. r.outputs.push_back(co);
  185. } else {
  186. pos++; // Have : in status color
  187. co.len = pos;
  188. r.outputs.push_back(co);
  189. co.reset();
  190. co.pos = pos;
  191. co.c = value;
  192. co.len = txt.length() - pos;
  193. r.outputs.push_back(co);
  194. }
  195. return r;
  196. };
  197. door::Panel make_about(void) {
  198. door::Panel about(2, 2, 60);
  199. about.setStyle(door::BorderStyle::DOUBLE_SINGLE);
  200. about.setColor(door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  201. door::ATTR::BOLD));
  202. about.addLine(std::make_unique<door::Line>("About This Door", 60));
  203. /*
  204. door::Line magic("---------------------------------", 60);
  205. magic.setColor(door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLACK,
  206. door::ATTR::BOLD));
  207. */
  208. about.addLine(std::make_unique<door::Line>(
  209. "---------------------------------", 60,
  210. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLACK,
  211. door::ATTR::BOLD)));
  212. /*
  213. 123456789012345678901234567890123456789012345678901234567890
  214. This door was written by Bugz.
  215. It is written in c++, only supports Linux, and replaces
  216. opendoors.
  217. It's written in c++, and replaces the outdated opendoors
  218. library.
  219. */
  220. about.addLine(
  221. std::make_unique<door::Line>("This door was written by Bugz.", 60));
  222. about.addLine(std::make_unique<door::Line>("", 60));
  223. about.addLine(std::make_unique<door::Line>(
  224. "It is written in c++, only support Linux, and replaces", 60));
  225. about.addLine(std::make_unique<door::Line>("opendoors.", 60));
  226. about.addLine(std::make_unique<door::Line>(
  227. "Status: blue", 60,
  228. statusValue(door::ANSIColor(door::COLOR::GREEN, door::ATTR::BOLD),
  229. door::ANSIColor(door::COLOR::MAGENTA, door::ATTR::BLINK))));
  230. about.addLine(std::make_unique<door::Line>("Name: BUGZ", 60, rStatus));
  231. about.addLine(std::make_unique<door::Line>(
  232. "Size: 10240", 60,
  233. statusValue(door::ANSIColor(door::COLOR::GREEN, door::COLOR::BLUE,
  234. door::ATTR::BOLD),
  235. door::ANSIColor(door::COLOR::YELLOW, door::COLOR::BLUE,
  236. door::ATTR::BOLD, door::ATTR::BLINK))));
  237. about.addLine(std::make_unique<door::Line>("Bugz is here.", 60, rStatus));
  238. return about;
  239. }
  240. void display_starfield(int mx, int my, door::Door &door, std::mt19937 &rng) {
  241. door << door::reset << door::cls;
  242. // display starfield
  243. const char *stars[2];
  244. stars[0] = ".";
  245. if (door::unicode) {
  246. stars[1] = "\u2219"; // "\u00b7";
  247. } else {
  248. stars[1] = "\xf9"; // "\xfa";
  249. };
  250. {
  251. // Make uniform random distribution between 1 and MAX screen size X/Y
  252. std::uniform_int_distribution<int> uni_x(1, mx);
  253. std::uniform_int_distribution<int> uni_y(1, my);
  254. door::ANSIColor white(door::COLOR::WHITE);
  255. door::ANSIColor dark(door::COLOR::BLACK, door::ATTR::BRIGHT);
  256. for (int x = 0; x < (mx * my / 100); x++) {
  257. door::Goto star_at(uni_x(rng), uni_y(rng));
  258. door << star_at;
  259. if (x % 5 < 2)
  260. door << dark;
  261. else
  262. door << white;
  263. if (x % 2 == 0)
  264. door << stars[0];
  265. else
  266. door << stars[1];
  267. }
  268. }
  269. }
  270. void display_space_ace(int mx, int my, door::Door &door) {
  271. // space_ace is 72 chars wide, 6 high
  272. int sa_x = (mx - 72) / 2;
  273. int sa_y = (my - 6) / 2;
  274. // output the SpaceAce logo -- centered!
  275. for (const auto s : space) {
  276. door::Goto sa_at(sa_x, sa_y);
  277. door << sa_at;
  278. if (door::unicode) {
  279. std::string unicode;
  280. door::cp437toUnicode(s, unicode);
  281. door << unicode; // << door::nl;
  282. } else
  283. door << s; // << door::nl;
  284. sa_y++;
  285. }
  286. // pause 5 seconds so they can enjoy our awesome logo
  287. door.sleep_key(5);
  288. }
  289. void display_starfield_space_ace(int mx, int my, door::Door &door,
  290. std::mt19937 &rng) {
  291. display_starfield(mx, my, door, rng);
  292. display_space_ace(mx, my, door);
  293. door << door::reset;
  294. }
  295. int main(int argc, char *argv[]) {
  296. /*
  297. google::InitGoogleLogging(argv[0]);
  298. LOG(INFO) << "Welcome!";
  299. */
  300. door::Door door("space-ace", argc, argv);
  301. // door << door::reset << door::cls << door::nl;
  302. door::ANSIColor ac(door::COLOR::YELLOW, door::ATTR::BOLD);
  303. door::ANSIColor mb(door::COLOR::MAGENTA, door::ATTR::BLINK);
  304. door << mb << "Does this work?" << door::reset << door::nl;
  305. // https://stackoverflow.com/questions/5008804/generating-random-integer-from-a-range
  306. std::random_device rd; // only used once to initialise (seed) engine
  307. std::mt19937 rng(
  308. rd()); // random-number engine used (Mersenne-Twister in this case)
  309. // std::uniform_int_distribution<int> uni(min, max); // guaranteed unbiased
  310. int mx, my; // Max screen width/height
  311. if (door.width == 0) {
  312. // screen detect failed, use sensible defaults
  313. mx = 80;
  314. my = 23;
  315. } else {
  316. mx = door.width;
  317. my = door.height;
  318. }
  319. // We assume here that the width and height are something crazy like 10x15. :P
  320. display_starfield_space_ace(mx, my, door, rng);
  321. // for testing inactivity timeout
  322. // door.inactivity = 10;
  323. door::Panel timeout = make_timeout(mx, my);
  324. door::Menu m = make_main_menu();
  325. int r = m.choose(door);
  326. // need to reset the colors. (whoops!)
  327. door << door::reset << door::nl;
  328. if (r < 0) {
  329. TIMEOUT:
  330. if (r == -1) {
  331. door.log("TIMEOUT");
  332. door << timeout << door::reset << door::nl << door::nl;
  333. } else {
  334. if (r == -3) {
  335. door.log("OUTTA TIME");
  336. door::Panel notime = make_notime(mx, my);
  337. door << notime << door::reset << door::nl;
  338. }
  339. }
  340. return 0;
  341. }
  342. /*
  343. display_starfield(mx, my, door, rng);
  344. // WARNING: After starfield, cursor position is random!
  345. door << door::Goto(1, 9); // door::nl << door::nl; // door::cls;
  346. door << "Your name: "
  347. << door::ANSIColor(door::COLOR::WHITE, door::COLOR::GREEN,
  348. door::ATTR::BOLD);
  349. std::string istring;
  350. istring = door.input_string(25);
  351. door << door::reset << door::nl << "You typed in [" << istring << "]"
  352. << door::nl;
  353. door << "Hello, " << door.username << ", you chose option " << r << "!"
  354. << door::nl;
  355. door << "Press a key...";
  356. r = door.sleep_key(door.inactivity);
  357. if (r < 0)
  358. goto TIMEOUT;
  359. */
  360. door << door::nl;
  361. door::Panel about = make_about();
  362. about.set((mx - 60) / 2, (my - 5) / 2);
  363. door << about;
  364. door << door::reset << door::nl << "Press another key...";
  365. r = door.sleep_key(door.inactivity);
  366. if (r < 0)
  367. goto TIMEOUT;
  368. door << door::nl;
  369. door::ANSIColor deck_color;
  370. // RED, BLUE, GREEN, MAGENTA, CYAN
  371. std::uniform_int_distribution<int> rand_color(0, 4);
  372. switch (rand_color(rng)) {
  373. case 0:
  374. deck_color = door::ANSIColor(door::COLOR::RED);
  375. break;
  376. case 1:
  377. deck_color = door::ANSIColor(door::COLOR::BLUE);
  378. break;
  379. case 2:
  380. deck_color = door::ANSIColor(door::COLOR::GREEN);
  381. break;
  382. case 3:
  383. deck_color = door::ANSIColor(door::COLOR::MAGENTA);
  384. break;
  385. case 4:
  386. deck_color = door::ANSIColor(door::COLOR::CYAN);
  387. break;
  388. default:
  389. deck_color = door::ANSIColor(door::COLOR::BLUE, door::ATTR::BLINK);
  390. break;
  391. }
  392. Deck d(deck_color);
  393. door::Panel *c;
  394. door << door::reset << door::cls;
  395. // This displays the cards in the upper left corner.
  396. // We want them center, and down some.
  397. // int cards_delt_width = 59; int cards_delt_height = 9;
  398. int off_x = (mx - 59) / 2;
  399. int off_y = (my - 9) / 2;
  400. for (int x = 0; x < 28; x++) {
  401. int cx, cy, level;
  402. cardgo(x, cx, cy, level);
  403. c = d.back(level);
  404. c->set(cx + off_x, cy + off_y);
  405. door << *c;
  406. }
  407. door << door::nl << door::nl;
  408. r = door.sleep_key(door.inactivity);
  409. if (r < 0)
  410. goto TIMEOUT;
  411. /*
  412. door::Panel *p = d.back(2);
  413. p->set(10, 10);
  414. door << *p;
  415. door::Panel *d8 = d.card(8);
  416. d8->set(20, 8);
  417. door << *d8;
  418. r = door.sleep_key(door.inactivity);
  419. if (r < 0)
  420. goto TIMEOUT;
  421. */
  422. // door << door::reset << door::cls;
  423. display_starfield(mx, my, door, rng);
  424. door << m << door::reset << door::nl << "This is what the menu looked liked!"
  425. << door::nl;
  426. // Normal DOOR exit goes here...
  427. door << door::nl << "Returning you to the BBS, please wait..." << door::nl;
  428. return 0;
  429. }