PageRenderTime 46ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/src/gui.cpp

https://github.com/Compendium/TROG
C++ | 1220 lines | 1025 code | 168 blank | 27 comment | 181 complexity | a508544a7e2c99c0fdd07fedd9daa721 MD5 | raw file
  1. #include "gui.hpp"
  2. #include "character.hpp"
  3. #include <unistd.h>
  4. #include <ncurses.h>
  5. #include <sstream>
  6. #include <cstring>
  7. #include <iostream>
  8. #define NOISE_CHARACTER '#'
  9. Game *GUI::g;
  10. Point GUI::cam;
  11. int GUI::NUM_COLOURS;
  12. std::string GUI::status_line;
  13. std::vector<std::string> GUI::messages;
  14. const char *GUI::SplashStr = "\
  15. \n\
  16. \n\
  17. \n\
  18. \n\
  19. \n\
  20. .ddhddddmdddhhdddddm/ .... -oydNMMMNNmd: \
  21. :No:...`/MMM:.````.ysodNNdyyyhddy/ ./sddhyyydmmy+. sMNs:.``.-omM: \
  22. + -MMm`` ` `+MMy`` `oNh+. .yMNo-``` `:hMMh: :NMh.`` .m. \
  23. `mNM. :MMs` yMMs .mMN:`` /NMM+ -NMm.` s` \
  24. `hdM. /MMs` sMM+ hMMo` +MMM: sMMs` \
  25. .MMN. /MMy` :mNo MMM+` NMMs`mMM+` \
  26. `MMN. /MMdo++odMm- MMMd` mMMs`NMM+` `:::/++ \
  27. .MMN` /MMy-...oddh- oMMM+ -MMN-`mMMs` .-dMMm \
  28. /MMM. /MMy` .yMN+ +NMNo -mMd-` /MMm. :MMy \
  29. -MMM. sMMh` `oNMh: .omMmo-....:+hmh+.` hMMh` -MMs \
  30. +MMM` :+ssso+: .sNMh/- `-/ossyyyso/.`` `sMMy. -MMs \
  31. hMMM/ `/+ss+. ``` -hNNy+:-`.dMy \
  32. .:oyyyhh/. ./oyyyys+:. \
  33. \n\
  34. \n\
  35. \n\
  36. \n\
  37. \n\
  38. ";
  39. const char *GUI::TombStr = "\
  40. ______________________________________________________\n\
  41. / /|\n\
  42. / / |\n\
  43. / / |\n\
  44. /_____________________________________________________/ |\n\
  45. | | |\n\
  46. | | |\n\
  47. | | |\n\
  48. | | |\n\
  49. | | |\n\
  50. | | |\n\
  51. | | |\n\
  52. | | |\n\
  53. | | |\n\
  54. | | |\n\
  55. | | |\n\
  56. | | |\n\
  57. | | |\n\
  58. | | |\n\
  59. | | /\n\
  60. | | /\n\
  61. | |/\n\
  62. \\//__________\\//_________\\//_______\\//_____\\\\/________\\//\n\
  63. ";
  64. void
  65. GUI::Init(void) {
  66. initscr();
  67. cbreak();
  68. keypad(stdscr, TRUE);
  69. noecho();
  70. curs_set(0);
  71. start_color();
  72. if (COLS < 80 || LINES < 24) {
  73. endwin();
  74. std::cout << "ERROR: This game requires a terminal size of at least 80x24\n";
  75. exit(1);
  76. }
  77. for (int i = 0; i < NUM_COLOURS; ++i)
  78. init_pair(i, i, COLOR_BLACK);
  79. }
  80. void
  81. GUI::End(void) {
  82. endwin();
  83. }
  84. void
  85. GUI::AttachTo(Game *g) {
  86. GUI::g = g;
  87. }
  88. void
  89. GUI::RedrawStatus(void) {
  90. mvprintw(LINES -1, 0, GUI::status_line.c_str());
  91. for (int i = GUI::status_line.length(); i <= COLS; ++i)
  92. addch(' ');
  93. }
  94. void
  95. GUI::SetStatus(std::string str) {
  96. GUI::status_line = str;
  97. GUI::RedrawStatus();
  98. }
  99. void
  100. GUI::AddMessage(std::string str) {
  101. messages.push_back(str);
  102. }
  103. void
  104. GUI::ProcessMessages(Game *g) {
  105. std::string s;
  106. if (messages.size() == 0) {
  107. GUI::CharacterStatusLine(g->character);
  108. return;
  109. }
  110. for (std::vector<std::string>::iterator it = messages.begin();
  111. it != messages.end();) {
  112. s = *it;
  113. SetStatus(s);
  114. if (++it != messages.end()) {
  115. mvprintw(LINES - 1, s.length(), " <More>");
  116. getch();
  117. }
  118. }
  119. messages.clear();
  120. }
  121. Character*
  122. GUI::CharacterCreation(void) {
  123. Character::Race race;
  124. Character::Class cclass;
  125. Character *c;
  126. std::string name;
  127. GUI::ShowSplash();
  128. race = GUI::SelectRace();
  129. GUI::ShowSplash();
  130. cclass = GUI::SelectClass();
  131. GUI::ShowSplash();
  132. name = GUI::GetString("Please enter a name:");
  133. c = new Character(name, race, cclass);
  134. if (c->name == "") {
  135. GUI::Alert("You can't have a nameless character. A name will be generated for you!");
  136. c->GiveRandomName();
  137. }
  138. return c;
  139. }
  140. Character::Race
  141. GUI::SelectRace(void) {
  142. WINDOW *w;
  143. Rect pos;
  144. int ret;
  145. int c;
  146. static char const * const title = ":: CHOOSE YOUR RACE ::";
  147. static char const * const underline = "======================";
  148. static char const * const left_ptr = ">>";
  149. static char const * const right_ptr = "<<";
  150. static char const * const blank = " ";
  151. std::string s;
  152. //Determine how wide our window needs to be
  153. c = strlen(title) + 4;
  154. for (int i = 0; i < Character::LAST_RACE; ++i) {
  155. s = Character::RaceAsString((Character::Race) i );
  156. c = MAX((unsigned int)c, s.length() + strlen(left_ptr) + strlen(right_ptr) + 6);
  157. }
  158. pos.w = c;
  159. pos.h = Character::LAST_RACE*2 + 4;
  160. pos.x = (COLS - pos.w) / 2;
  161. pos.y = (LINES - pos.h) / 2;
  162. w = newwin(pos.h, pos.w, pos.y, pos.x);
  163. if (!w) {
  164. std::cerr << "Error making window for race selection!";
  165. exit(1);
  166. }
  167. box(w, 0, 0);
  168. mvwprintw(w, 0, (pos.w - strlen(title)) / 2, title);
  169. mvwprintw(w, 1, (pos.w - strlen(title)) / 2, underline);
  170. for (int i = 0; i < Character::LAST_RACE; ++i) {
  171. s = Character::RaceAsString((Character::Race) i );
  172. mvwprintw(w, 3 + i*2, 8, s.c_str());
  173. }
  174. wrefresh(w);
  175. //Menu driver
  176. ret = 0;
  177. c = 0;
  178. do {
  179. //Blank old position
  180. mvwprintw(w, 3 + ret*2, 2, blank);
  181. mvwprintw(w, 3 + ret*2, pos.w - strlen(right_ptr) - 2, blank);
  182. if (c == KEY_UP)
  183. ret = (ret == 0) ? Character::LAST_RACE - 1 : ret - 1;
  184. else if (c == KEY_DOWN)
  185. ret = (ret == Character::LAST_RACE - 1) ? 0 : ret + 1;
  186. //Redraw pointers
  187. mvwprintw(w, 3 + ret*2, 2, left_ptr);
  188. mvwprintw(w, 3 + ret*2, pos.w - strlen(right_ptr) - 2, right_ptr);
  189. wrefresh(w);
  190. } while ((c = getch()) != '\n');
  191. //Cleanup
  192. delwin(w);
  193. return (Character::Race) ret;
  194. }
  195. Character::Class
  196. GUI::SelectClass(void) {
  197. WINDOW *w;
  198. Rect pos;
  199. int ret;
  200. int c;
  201. static char const * const title = ":: CHOOSE YOUR CLASS ::";
  202. static char const * const underline = "======================";
  203. static char const * const left_ptr = ">>";
  204. static char const * const right_ptr = "<<";
  205. static char const * const blank = " ";
  206. std::string s;
  207. //Determine how wide our window needs to be
  208. c = strlen(title) + 4;
  209. for (int i = 0; i < Character::LAST_CLASS; ++i) {
  210. s = Character::ClassAsString((Character::Class) i );
  211. c = MAX((unsigned int)c, s.length() + strlen(left_ptr) + strlen(right_ptr) + 6);
  212. }
  213. pos.w = c;
  214. pos.h = Character::LAST_CLASS*2 + 4;
  215. pos.x = (COLS - pos.w) / 2;
  216. pos.y = (LINES - pos.h) / 2;
  217. w = newwin(pos.h, pos.w, pos.y, pos.x);
  218. if (!w) {
  219. std::cerr << "Error making window for class selection!";
  220. exit(1);
  221. }
  222. box(w, 0, 0);
  223. mvwprintw(w, 0, (pos.w - strlen(title)) / 2, title);
  224. mvwprintw(w, 1, (pos.w - strlen(title)) / 2, underline);
  225. for (int i = 0; i < Character::LAST_CLASS; ++i) {
  226. s = Character::ClassAsString((Character::Class) i );
  227. mvwprintw(w, 3 + i*2, 8, s.c_str());
  228. }
  229. wrefresh(w);
  230. //Menu driver
  231. ret = 0;
  232. c = 0;
  233. do {
  234. //Blank old position
  235. mvwprintw(w, 3 + ret*2, 2, blank);
  236. mvwprintw(w, 3 + ret*2, pos.w - strlen(right_ptr) - 2, blank);
  237. if (c == KEY_UP)
  238. ret = (ret == 0) ? Character::LAST_CLASS - 1 : ret - 1;
  239. else if (c == KEY_DOWN)
  240. ret = (ret == Character::LAST_CLASS - 1) ? 0 : ret + 1;
  241. //Redraw pointers
  242. mvwprintw(w, 3 + ret*2, 2, left_ptr);
  243. mvwprintw(w, 3 + ret*2, pos.w - strlen(right_ptr) - 2, right_ptr);
  244. wrefresh(w);
  245. } while ((c = getch()) != '\n');
  246. //Cleanup
  247. delwin(w);
  248. return (Character::Class) ret;
  249. }
  250. std::string
  251. GUI::GetString(std::string prompt) {
  252. static const int IN_SZ = 30;
  253. WINDOW *w;
  254. char in[IN_SZ];
  255. std::stringstream ss;
  256. std::string s;
  257. echo();
  258. curs_set(1);
  259. refresh();
  260. w = GUI::NewCentredWindow(IN_SZ + prompt.length() + 6, 3);
  261. if (!w) {
  262. std::cerr << "Error making window for string selection!";
  263. return "";
  264. }
  265. box(w, 0, 0);
  266. mvwprintw(w, 1, 2, prompt.c_str());
  267. waddch(w, ' ');
  268. wrefresh(w);
  269. refresh();
  270. wgetnstr(w, in, 30);
  271. noecho();
  272. curs_set(0);
  273. delwin(w);
  274. ss << in;
  275. s = ss.str();
  276. return s;
  277. }
  278. void
  279. GUI::ShowSplash(void) {
  280. GUI::ShowSplash(COL_BLUE);
  281. }
  282. void
  283. GUI::ShowSplash(Colour col) {
  284. WINDOW *w;
  285. refresh();
  286. w = GUI::NewCentredWindow(80, 24);
  287. wattron(w, COLOR_PAIR(col));
  288. mvwprintw(w, 0, 0, SplashStr);
  289. wattroff(w, COLOR_PAIR(col));
  290. wrefresh(w);
  291. refresh();
  292. delwin(w);
  293. }
  294. void
  295. GUI::StartScreen(void) {
  296. static const std::string request = "Press any key to begin...";
  297. GUI::ShowSplash(COL_RED);
  298. mvprintw(LINES/2 + 10, ((COLS - request.length()) / 2), request.c_str());
  299. getch();
  300. }
  301. void
  302. GUI::ScreenNoise(void) {
  303. for (int e = 0; e < LINES; ++e) {
  304. for (int i = 0; i < COLS; ++i) {
  305. mvaddch(e, i, '#'); //Magic number here is bad but efficient
  306. }
  307. }
  308. }
  309. WINDOW*
  310. GUI::NewCentredWindow(int w, int h) {
  311. return newwin(h, w, (LINES - h) / 2, (COLS - w) / 2);
  312. }
  313. void
  314. GUI::Alert(std::string str) {
  315. WINDOW *w;
  316. int c;
  317. Rect pos;
  318. static std::string const press_msg = "Press spacebar to continue.";
  319. pos.w = MAX(str.length(), press_msg.length()) + 4;
  320. pos.w = MIN(pos.w, 80);
  321. pos.h = 5;
  322. pos.x = (COLS - pos.w) / 2;
  323. pos.y = (LINES - pos.h) / 2;
  324. w = newwin(pos.h, pos.w, pos.y, pos.x);
  325. if (!w) {
  326. std::cerr << "Error making window for an alert!";
  327. return;
  328. }
  329. box(w, 0, 0);
  330. wrefresh(w);
  331. refresh();
  332. pos.x = (pos.w - str.length()) / 2;
  333. mvwprintw(w, 2, pos.x, str.c_str());
  334. pos.x = (pos.w - press_msg.length()) / 2;
  335. mvwprintw(w, 4, pos.x, press_msg.c_str());
  336. wrefresh(w);
  337. refresh();
  338. while((c = wgetch(w)) != ' ');
  339. delwin(w);
  340. }
  341. void
  342. GUI::Alert2(char const * const * const msg) {
  343. WINDOW *w;
  344. int c;
  345. Rect pos;
  346. static std::string const press_msg = "Press spacebar to continue.";
  347. int maxlen;
  348. int lines;
  349. maxlen = press_msg.length();
  350. for (lines = 0; msg[lines]; ++lines) {
  351. if ((int)strlen(msg[lines]) > maxlen)
  352. maxlen = strlen(msg[lines]);
  353. }
  354. pos.w = MIN(maxlen + 4, 80);
  355. pos.h = 5 + lines - 1;
  356. pos.x = (COLS - pos.w) / 2;
  357. pos.y = (LINES - pos.h) / 2;
  358. w = newwin(pos.h, pos.w, pos.y, pos.x);
  359. if (!w) {
  360. std::cerr << "Error making window for an alert!";
  361. return;
  362. }
  363. box(w, 0, 0);
  364. wrefresh(w);
  365. refresh();
  366. for (int i = 0; i < lines; ++i) {
  367. pos.x = (pos.w - strlen(msg[i])) / 2;
  368. mvwprintw(w, 2+i, pos.x, msg[i]);
  369. }
  370. pos.x = (pos.w - press_msg.length()) / 2;
  371. mvwprintw(w, pos.h - 1, pos.x, press_msg.c_str());
  372. wrefresh(w);
  373. refresh();
  374. while((c = wgetch(w)) != ' ');
  375. delwin(w);
  376. }
  377. bool
  378. GUI::BinaryChoice(std::string str, char a, char b) {
  379. WINDOW *w;
  380. Rect pos;
  381. char c;
  382. str.append(1, ' ');
  383. str.append(1, a);
  384. str.append(1, '/');
  385. str.append(1, b);
  386. pos.w = str.length() + 4;
  387. pos.h = 3;
  388. pos.x = (COLS - pos.w) / 2;
  389. pos.y = (LINES - pos.h) / 2;
  390. w = newwin(pos.h, pos.w, pos.y, pos.x);
  391. if (!w) {
  392. std::cerr << "Error making window for a binary choice! (Defaulted to true)";
  393. return true;
  394. }
  395. box(w, 0, 0);
  396. pos.x = (pos.w - str.length()) / 2;
  397. mvwprintw(w, 1, pos.x, str.c_str());
  398. wrefresh(w);
  399. while(1) {
  400. c = getch();
  401. if (c == a || c == b)
  402. break;
  403. }
  404. delwin(w);
  405. if (c == a)
  406. return true;
  407. return false;
  408. }
  409. void
  410. GUI::FancyClear(void) {
  411. wmove(stdscr, 0, 0);
  412. for (int i = 0; i < LINES - 1; ++i) {
  413. for (int e = 0; e < COLS; ++e) {
  414. waddch(stdscr, ' ');
  415. }
  416. }
  417. refresh();
  418. }
  419. void
  420. GUI::CharacterRename(Character *c) {
  421. std::string newname;
  422. newname = GUI::GetString("Enter the new character name: ");
  423. if (newname == "") {
  424. GUI::Alert("You can't have an empty name. Using the old name.");
  425. return;
  426. }
  427. c->name = newname;
  428. }
  429. void
  430. GUI::ShowCharacterScreen(Character *c) {
  431. WINDOW *w;
  432. Rect pos;
  433. std::stringstream ss;
  434. std::string s;
  435. Point p;
  436. pos.w = 80;
  437. pos.h = 24;
  438. pos.x = (COLS - pos.w) / 2;
  439. pos.y = (LINES - pos.h) / 2;
  440. w = newwin(pos.h, pos.w, pos.y, pos.x);
  441. if (!w) {
  442. std::cerr << "Error making window for character screen!";
  443. return;
  444. }
  445. box(w, 0, 0);
  446. //Make our box shape
  447. //Horizontal lines
  448. wmove(w, 3, 1);
  449. for (int i = 0; i < pos.w - 2; ++i) {
  450. if ((i - 1) % 3 == 0)
  451. waddch(w, '|');
  452. else
  453. waddch(w, '=');
  454. }
  455. wmove(w, 0, 1);
  456. for (int i = 0; i < pos.w - 2; ++i) {
  457. if ((i - 1) % 3 == 0)
  458. waddch(w, '|');
  459. else
  460. waddch(w, '=');
  461. }
  462. wmove(w, pos.h - 1, 1);
  463. for (int i = 0; i < pos.w - 2; ++i) {
  464. if ((i - 1) % 3 == 0)
  465. waddch(w, '|');
  466. else
  467. waddch(w, '=');
  468. }
  469. //Actual output begins
  470. mvwprintw(w, 1, (pos.w - c->name.length()) / 2, c->name.c_str());
  471. s = c->GetRaceString() + " " + c->GetClassString() + " ";
  472. mvwprintw(w, 2, (pos.w - s.length()) / 2, s.c_str());
  473. p.x = 3;
  474. p.y = 5;
  475. ss.str("");
  476. ss << "HP :: " << c->curHP << "/" << c->baseHP;
  477. s = ss.str();
  478. mvwprintw(w, p.y, p.x, s.c_str());
  479. ss.str("");
  480. ss << "MP :: " << c->curMP << "/" << c->baseMP;
  481. s = ss.str();
  482. mvwprintw(w, ++p.y, p.x, s.c_str());
  483. ss.str("");
  484. ss << "STR :: " << c->curSTR;
  485. if (c->curSTR != c->baseSTR)
  486. ss << "\t(Base :: " << c->baseSTR << ")";
  487. s = ss.str();
  488. mvwprintw(w, p.y += 2, p.x, s.c_str());
  489. ss.str("");
  490. ss << "TOU :: " << c->curTOU;
  491. if (c->curTOU != c->baseTOU)
  492. ss << "\t(Base :: " << c->baseTOU << ")";
  493. s = ss.str();
  494. mvwprintw(w, ++p.y, p.x, s.c_str());
  495. ss.str("");
  496. ss << "ATT :: " << c->curATT;
  497. if (c->curATT != c->baseATT)
  498. ss << "\t(Base :: " << c->baseATT << ")";
  499. s = ss.str();
  500. mvwprintw(w, ++p.y, p.x, s.c_str());
  501. ss.str("");
  502. ss << "DEF :: " << c->curDEF;
  503. if (c->curDEF != c->baseDEF)
  504. ss << "\t(Base :: " << c->baseDEF << ")";
  505. s = ss.str();
  506. mvwprintw(w, ++p.y, p.x, s.c_str());
  507. ss.str("");
  508. ss << "MAG :: " << c->curMAG;
  509. if (c->curMAG != c->baseMAG)
  510. ss << "\t(Base :: " << c->baseMAG << ")";
  511. s = ss.str();
  512. mvwprintw(w, ++p.y, p.x, s.c_str());
  513. ss.str("");
  514. if (!w) {
  515. std::cerr << "Error making window for inventory screen!";
  516. return;
  517. }
  518. ss << "WIL :: " << c->curWIL;
  519. if (c->curWIL != c->baseWIL)
  520. ss << "\t(Base :: " << c->baseWIL << ")";
  521. s = ss.str();
  522. mvwprintw(w, ++p.y, p.x, s.c_str());
  523. mvwprintw(w, p.y += 2, p.x -1, "=|==|==|==|==|==|==|==|==|==|==|==|=");
  524. ss.str("");
  525. ss << "SIGHT RANGE :: " << c->curSIGHT;
  526. if (c->curSIGHT != c->baseSIGHT)
  527. ss << "\t(Base :: " << c->baseSIGHT << ")";
  528. s = ss.str();
  529. mvwprintw(w, p.y += 2, p.x, s.c_str());
  530. ss.str("");
  531. ss << "MV COST :: " << c->curMV;
  532. if (c->curMV != c->baseMV)
  533. ss << "\t(Base :: " << c->baseMV << ")";
  534. s = ss.str();
  535. mvwprintw(w, ++p.y, p.x, s.c_str());
  536. //Second row
  537. p.x = 40;
  538. p.y = 5;
  539. ss.str("");
  540. ss << "LEVEL :: " << c->Level;
  541. s = ss.str();
  542. mvwprintw(w, p.y, p.x, s.c_str());
  543. ss.str("");
  544. ss << "XP :: " << c->getXP() << "/" << c->next_level;
  545. s = ss.str();
  546. mvwprintw(w, ++p.y, p.x, s.c_str());
  547. mvwprintw(w, p.y + 2, p.x - 1, "=|==|==|==|==|==|==|==|==|==|==|==|==|=");
  548. //Second row. Equipment section.
  549. p.y = 10;
  550. ss.str("");
  551. ss << "HEAD :: " << c->getEquipmentNameWithQuality(HEAD);
  552. s = ss.str();
  553. mvwprintw(w, p.y, p.x, s.c_str());
  554. ss.str("");
  555. ss << "BODY :: " << c->getEquipmentNameWithQuality(BODY);
  556. s = ss.str();
  557. mvwprintw(w, ++p.y, p.x, s.c_str());
  558. ss.str("");
  559. ss << "WEAPON :: " << c->getEquipmentNameWithQuality(WEAPON);
  560. s = ss.str();
  561. mvwprintw(w, ++p.y, p.x, s.c_str());
  562. ss.str("");
  563. ss << "SHIELD :: " << c->getEquipmentNameWithQuality(SHIELD);
  564. s = ss.str();
  565. mvwprintw(w, ++p.y, p.x, s.c_str());
  566. ss.str("");
  567. ss << "GLOVES :: " << c->getEquipmentNameWithQuality(GLOVES);
  568. s = ss.str();
  569. mvwprintw(w, ++p.y, p.x, s.c_str());
  570. ss.str("");
  571. ss << "BOOTS :: " << c->getEquipmentNameWithQuality(BOOTS);
  572. s = ss.str();
  573. mvwprintw(w, ++p.y, p.x, s.c_str());
  574. ss.str("");
  575. ss << "RING1 :: " << c->getEquipmentNameWithQuality(RING1);
  576. s = ss.str();
  577. mvwprintw(w, ++p.y, p.x, s.c_str());
  578. ss.str("");
  579. ss << "RING2 :: " << c->getEquipmentNameWithQuality(RING2);
  580. s = ss.str();
  581. mvwprintw(w, ++p.y, p.x, s.c_str());
  582. ss.str("");
  583. ss << "NECK :: " << c->getEquipmentNameWithQuality(NECK);
  584. s = ss.str();
  585. mvwprintw(w, ++p.y, p.x, s.c_str());
  586. //Output all done
  587. wrefresh(w);
  588. delwin(w);
  589. }
  590. void
  591. GUI::ShowInventoryScreen(Character *c) {
  592. WINDOW *w;
  593. Rect pos;
  594. std::stringstream ss;
  595. std::string s;
  596. Item* item;
  597. int i;
  598. pos.w = 80;
  599. pos.h = 24;
  600. pos.x = (COLS - pos.w) / 2;
  601. pos.y = (LINES - pos.h) / 2;
  602. w = newwin(pos.h, pos.w, pos.y, pos.x);
  603. box(w, 0, 0);
  604. //Make our box shape
  605. //Horizontal lines
  606. wmove(w, 0, 1);
  607. for (int i = 0; i < pos.w - 2; ++i) {
  608. if ((i - 1) % 3 == 0)
  609. waddch(w, '|');
  610. else
  611. waddch(w, '=');
  612. }
  613. wmove(w, 2, 1);
  614. for (int i = 0; i < pos.w - 2; ++i) {
  615. if ((i - 1) % 3 == 0)
  616. waddch(w, '|');
  617. else
  618. waddch(w, '=');
  619. }
  620. wmove(w, pos.h - 1, 1);
  621. for (int i = 0; i < pos.w - 2; ++i) {
  622. if ((i - 1) % 3 == 0)
  623. waddch(w, '|');
  624. else
  625. waddch(w, '=');
  626. }
  627. //header
  628. mvwprintw(w, 1, 2, "INVENTORY");
  629. ss << "Wealth :: " << c->getGold() << "gp";
  630. s = ss.str();
  631. mvwprintw(w, 1, pos.w - 2 - s.length(), s.c_str());
  632. i = 0;
  633. for (std::list<Item*>::iterator it = c->Inventory.begin();
  634. it != c->Inventory.end(); ++it, ++i) {
  635. item = &**it;
  636. mvwprintw(w, i + 3, 8, item->GetName().c_str());
  637. }
  638. wrefresh(w);
  639. delwin(w);
  640. }
  641. void
  642. GUI::CharacterStatusLine(Character *c) {
  643. std::stringstream ss;
  644. ss << "HP:" << c->curHP << "/" << c->baseHP;
  645. if (c->baseMP > 0)
  646. ss << " MP:" << c->curMP << "/" << c->baseMP;
  647. ss << " GP:" << c->getGold();
  648. ss << " XP:" << c->getXP() << "/" << c->next_level;
  649. GUI::SetStatus(ss.str());
  650. }
  651. void
  652. GUI::ShowInfoScreen(Game *g) {
  653. WINDOW *w;
  654. Rect pos;
  655. std::stringstream ss;
  656. std::string s;
  657. pos.w = 80;
  658. pos.h = 24;
  659. pos.x = (COLS - pos.w) / 2;
  660. pos.y = (LINES - pos.h) / 2;
  661. w = newwin(pos.h, pos.w, pos.y, pos.x);
  662. box(w, 0, 0);
  663. //Make our box shape
  664. //Horizontal lines
  665. wmove(w, 0, 1);
  666. for (int i = 0; i < pos.w - 2; ++i) {
  667. if ((i - 1) % 3 == 0)
  668. waddch(w, '|');
  669. else
  670. waddch(w, '=');
  671. }
  672. wmove(w, pos.h - 1, 1);
  673. for (int i = 0; i < pos.w - 2; ++i) {
  674. if ((i - 1) % 3 == 0)
  675. waddch(w, '|');
  676. else
  677. waddch(w, '=');
  678. }
  679. //header
  680. mvwprintw(w, 1, 2, "MAP INFO");
  681. //List of attributes
  682. ss << "Current Depth :: " << g->cur_level->depth;
  683. mvwprintw(w, 3, 3, ss.str().c_str());
  684. ss.str("");
  685. ss << "Deepest Visited :: " << g->character->deepest_visited;
  686. mvwprintw(w, 4, 3, ss.str().c_str());
  687. wrefresh(w);
  688. delwin(w);
  689. }
  690. void
  691. GUI::QuitDialogue(Game *g) {
  692. GUI::FancyClear();
  693. GUI::ShowSplash();
  694. g->running = !GUI::BinaryChoice("Are you sure you want to quit?", 'y', 'n');
  695. if (g->running)
  696. GUI::DoRedraw();
  697. }
  698. void
  699. GUI::DrawAsOverlay(Point p, char c, int col) {
  700. if (!GUI::isOnScreen(p))
  701. return;
  702. attron(COLOR_PAIR(col));
  703. mvaddch(p.y - GUI::cam.y, p.x - GUI::cam.x, c);
  704. attroff(COLOR_PAIR(col));
  705. }
  706. bool
  707. GUI::isOnScreen(Point p) {
  708. if (p.x - GUI::cam.x < 0)
  709. return false;
  710. if (p.y - GUI::cam.y < 0)
  711. return false;
  712. if (p.x - GUI::cam.x >= COLS)
  713. return false;
  714. if (p.y - GUI::cam.y >= LINES - 1) // -1 gives space for the status bar
  715. return false;
  716. return true;
  717. }
  718. void
  719. GUI::MoveCamera(Direction::Type d) {
  720. int step_x;
  721. int step_y;
  722. step_x = MAX(COLS / 3, 1);
  723. step_y = MAX(LINES / 3, 1);
  724. switch (d) {
  725. case Direction::NORTH:
  726. GUI::cam.y -= step_y;
  727. break;
  728. case Direction::SOUTH:
  729. GUI::cam.y += step_y;
  730. break;
  731. case Direction::EAST:
  732. GUI::cam.x += step_x;
  733. break;
  734. case Direction::WEST:
  735. GUI::cam.x -= step_x;
  736. break;
  737. default:
  738. break;
  739. }
  740. GUI::DoRedraw();
  741. }
  742. void
  743. GUI::DoRedraw(void) {
  744. if (g->game_mode == GameMode::MAP_WALK) {
  745. GUI::DrawLevel(g->cur_level);
  746. GUI::RedrawStatus();
  747. }
  748. else if (g->game_mode == GameMode::MAP_LOOK){
  749. GUI::SetStatus(g->cur_level->DescriptionOfTile(g->target, g));
  750. GUI::DrawLevel(g->cur_level);
  751. GUI::RedrawStatus();
  752. g->DrawLookTarget();
  753. }
  754. else if (g->game_mode == GameMode::INFO_SCREEN)
  755. GUI::ShowInfoScreen(g);
  756. else if (g->game_mode == GameMode::CHARACTER_SCREEN)
  757. GUI::ShowCharacterScreen(g->character);
  758. else if (g->game_mode == GameMode::INVENTORY_SCREEN)
  759. GUI::ShowInventoryScreen(g->character);
  760. else if (g->game_mode == GameMode::POTION_SELECT)
  761. g->PotionSelectMenu->Show();
  762. else if (g->game_mode == GameMode::READING_SELECT)
  763. g->BookSelectMenu->Show();
  764. else if (g->game_mode == GameMode::GEAR_SELECT)
  765. g->EquipSelectMenu->Show();
  766. else if (g->game_mode == GameMode::SPELL_SELECT)
  767. g->SpellSelectMenu->Show();
  768. }
  769. void
  770. GUI::DrawLevel(Level *l) {
  771. int e2, i2;
  772. Item *item;
  773. Tile *t;
  774. char c;
  775. Colour colour;
  776. move(0,0);
  777. //Floor tiles
  778. for (int e = 0; e < LINES - 1; ++e) { //Save space for the status line
  779. for (int i = 0; i < COLS; ++i) {
  780. i2 = i + GUI::cam.x;
  781. e2 = e + GUI::cam.y;
  782. t = &l->tiles[i2][e2];
  783. if (i2 >= MAP_W || i2 < 0 || e2 >= MAP_H || e2 < 0)
  784. c = ' ';
  785. else if (!t->isVisible)
  786. c = ' ';
  787. else
  788. c = t->getTileType();
  789. if (c == CLOSED_DOOR_CHAR || c == OPEN_DOOR_CHAR) {
  790. attron(COLOR_PAIR(COL_RED));
  791. addch(c);
  792. attroff(COLOR_PAIR(COL_RED));
  793. }
  794. else if (c == FLOOR_CHAR) {
  795. if (GUI::NUM_COLOURS < 16)
  796. colour = COL_WHITE;
  797. else
  798. colour = COL_GREY;
  799. attron(COLOR_PAIR(colour));
  800. addch(c);
  801. attroff(COLOR_PAIR(colour));
  802. }
  803. else
  804. addch(c);
  805. }
  806. }
  807. for (std::list<Item*>::iterator it = l->items.begin();
  808. it != l->items.end(); it++) {
  809. item = &**it;
  810. l->ConditionallyShowObject(item->pos, item->symbol, item->colour);
  811. }
  812. //Special objects
  813. l->ConditionallyShowObject(l->stairs_up, '<', COL_CYAN);
  814. l->ConditionallyShowObject(l->stairs_down, '>', COL_CYAN);
  815. //Enemies
  816. for (std::list<Enemy>::iterator it = l->enemies.begin();
  817. it != l->enemies.end(); it++) {
  818. l->ConditionallyShowObject(it->pos, it->getSymbol(), it->getColour());
  819. }
  820. //Character
  821. l->ConditionallyShowObject(GUI::g->character->pos, GUI::g->character->symbol,
  822. GUI::g->character->colour);
  823. refresh();
  824. }
  825. void
  826. GUI::DrawObjectRelative(Point p, char c) {
  827. mvaddch(p.y - GUI::cam.y, p.x - GUI::cam.x, c);
  828. }
  829. void
  830. GUI::CentreCam(Point p) {
  831. GUI::cam.x = p.x - COLS / 2;
  832. GUI::cam.y = p.y - LINES / 2;
  833. }
  834. void
  835. GUI::InfoScreen(Item *i) {
  836. if (!i)
  837. return;
  838. switch (i->type) {
  839. case Item::POTION:
  840. GUI::InfoScreen((Potion*) i);
  841. return;
  842. case Item::STAT_TOME:
  843. GUI::InfoScreen((StatTome*) i);
  844. return;
  845. case Item::EQUIPPABLE:
  846. GUI::InfoScreen((Equippable*) i);
  847. return;
  848. case Item::LAST_TYPE:
  849. case Item::GENERIC:
  850. case Item::TREASURE_T:
  851. GUI::Alert("Asked to describe something but I don't know what it is!");
  852. return;
  853. }
  854. }
  855. void
  856. GUI::InfoScreen(Equippable *e) {
  857. std::stringstream ss;
  858. int nummods;
  859. char **fullmsg;
  860. int i;
  861. if (!e)
  862. return;
  863. nummods = 0;
  864. if (e->modSTR)
  865. nummods++;
  866. if (e->modTOU)
  867. nummods++;
  868. if (e->modATT)
  869. nummods++;
  870. if (e->modDEF)
  871. nummods++;
  872. if (e->modMAG)
  873. nummods++;
  874. if (e->modWIL)
  875. nummods++;
  876. if (e->modSIGHT)
  877. nummods++;
  878. if (e->modMV)
  879. nummods++;
  880. fullmsg = new char* [nummods + 3];
  881. fullmsg[0] = (char*) e->GetLongDescription();
  882. fullmsg[1] = (char*) "";
  883. i = 2;
  884. if (e->modSTR) {
  885. ss.str("");
  886. ss << "STR :: " << e->modSTR;
  887. fullmsg[i] = strdup(ss.str().c_str());
  888. i++;
  889. }
  890. if (e->modTOU) {
  891. ss.str("");
  892. ss << "TOU :: " << e->modTOU;
  893. fullmsg[i] = strdup(ss.str().c_str());
  894. i++;
  895. }
  896. if (e->modATT) {
  897. ss.str("");
  898. ss << "ATT :: " << e->modATT;
  899. fullmsg[i] = strdup(ss.str().c_str());
  900. i++;
  901. }
  902. if (e->modDEF) {
  903. ss.str("");
  904. ss << "DEF :: " << e->modDEF;
  905. fullmsg[i] = strdup(ss.str().c_str());
  906. i++;
  907. }
  908. if (e->modMAG) {
  909. ss.str("");
  910. ss << "MAG :: " << e->modMAG;
  911. fullmsg[i] = strdup(ss.str().c_str());
  912. i++;
  913. }
  914. if (e->modWIL) {
  915. ss.str("");
  916. ss << "WIL :: " << e->modWIL;
  917. fullmsg[i] = strdup(ss.str().c_str());
  918. i++;
  919. }
  920. if (e->modSIGHT) {
  921. ss.str("");
  922. ss << "SIGHT :: " << e->modSIGHT;
  923. fullmsg[i] = strdup(ss.str().c_str());
  924. i++;
  925. }
  926. if (e->modMV) {
  927. ss.str("");
  928. ss << "MV :: " << e->modMV;
  929. fullmsg[i] = strdup(ss.str().c_str());
  930. i++;
  931. }
  932. fullmsg[nummods + 2] = NULL;
  933. GUI::Alert2(fullmsg);
  934. for (int i = 0; i < nummods; ++i)
  935. free(fullmsg[i+2]);
  936. delete fullmsg;
  937. }
  938. void
  939. GUI::InfoScreen(Potion *p) {
  940. if (!p)
  941. return;
  942. GUI::Alert(p->GetLongDescription());
  943. }
  944. void
  945. GUI::InfoScreen(Spell *sp) {
  946. if (!sp)
  947. return;
  948. GUI::Alert(sp->description);
  949. }
  950. void
  951. GUI::InfoScreen(StatTome *st) {
  952. if (!st)
  953. return;
  954. GUI::Alert(st->GetLongDescription());
  955. }
  956. void
  957. GUI::HardRedraw(void) {
  958. erase();
  959. clear();
  960. endwin();
  961. refresh();
  962. GUI::DoRedraw();
  963. }
  964. void
  965. GUI::ShowTombstone(Game *g) {
  966. std::stringstream ss;
  967. std::string s;
  968. Character *c;
  969. WINDOW *w;
  970. c = g->character;
  971. w = GUI::NewCentredWindow(80, 24);
  972. wprintw(w, GUI::TombStr);
  973. s = "R . I . P .";
  974. mvwprintw(w, 6, (80 - s.length()) / 2, s.c_str());
  975. ss << c->name << ", " << c->GetRaceString() << " ";
  976. ss << c->GetClassString();
  977. s = ss.str();
  978. mvwprintw(w, 8, (80 - s.length()) / 2, s.c_str());
  979. ss.str("");
  980. ss << "XP: " << c->getXP() << "\tLevel: " << c->Level;
  981. s = ss.str();
  982. mvwprintw(w, 9, (80 - s.length()) / 2, s.c_str());
  983. ss.str("");
  984. ss << "Monsters killed: " << c->monsters_killed;
  985. s = ss.str();
  986. mvwprintw(w, 11, (80 - s.length()) / 2, s.c_str());
  987. ss.str("");
  988. ss << "Deepest travelled: " << c->deepest_visited;
  989. s = ss.str();
  990. mvwprintw(w, 12, (80 - s.length()) / 2, s.c_str());
  991. ss.str("");
  992. ss << "Gold collected: " << c->total_gold_collected;
  993. s = ss.str();
  994. mvwprintw(w, 13, (80 - s.length()) / 2, s.c_str());
  995. ss.str("");
  996. ss << "Potions drunk: " << c->potions_drunk;
  997. s = ss.str();
  998. mvwprintw(w, 14, (80 - s.length()) / 2, s.c_str());
  999. ss.str("");
  1000. ss << "Books read: " << c->books_read;
  1001. s = ss.str();
  1002. mvwprintw(w, 15, (80 - s.length()) / 2, s.c_str());
  1003. if (c->toughest_defeated) {
  1004. s = "Toughest monster defeated:";
  1005. mvwprintw(w, 17, (80 - s.length()) / 2, s.c_str());
  1006. s = c->toughest_defeated->name;
  1007. mvwprintw(w, 18, (80 - s.length()) / 2, s.c_str());
  1008. }
  1009. GUI::FancyClear();
  1010. GUI::SetStatus("");
  1011. wrefresh(w);
  1012. refresh();
  1013. delwin(w);
  1014. }
  1015. Direction::Type
  1016. GUI::GetDirection(void) {
  1017. int c;
  1018. GUI::DrawLevel(g->cur_level);
  1019. GUI::SetStatus("Cast in which direction?");
  1020. while (1) {
  1021. c = getch();
  1022. switch (c) {
  1023. case '7':
  1024. case 'q':
  1025. return Direction::NW;
  1026. case '8':
  1027. case 'w':
  1028. return Direction::NORTH;
  1029. case '9':
  1030. case 'e':
  1031. return Direction::NE;
  1032. case '4':
  1033. case 'a':
  1034. return Direction::WEST;
  1035. case '6':
  1036. case 'd':
  1037. return Direction::EAST;
  1038. case '1':
  1039. case 'z':
  1040. return Direction::SW;
  1041. case '2':
  1042. case 'x':
  1043. return Direction::SOUTH;
  1044. case '3':
  1045. case 'c':
  1046. return Direction::SE;
  1047. default:
  1048. break;
  1049. }
  1050. }
  1051. return Direction::LAST_DIRECTION;
  1052. }