Open Source Tomb Raider Engine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Menu.cpp 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /*!
  2. * \file src/Menu.cpp
  3. * \brief Menu 'overlay'
  4. *
  5. * \author xythobuz
  6. */
  7. #include <assert.h>
  8. #include <dirent.h>
  9. #include <cctype>
  10. #ifdef __APPLE__
  11. #include <OpenGL/gl.h>
  12. #else
  13. #include <GL/gl.h>
  14. #endif
  15. #include "config.h"
  16. #include "global.h"
  17. #include "main.h"
  18. #include "Menu.h"
  19. #include "utils/strings.h"
  20. Menu::Menu() {
  21. mVisible = false;
  22. mCursor = 0;
  23. mMin = 0;
  24. mainText.text = bufferString(VERSION);
  25. mainText.color[0] = 0xFF;
  26. mainText.color[1] = 0xFF;
  27. mainText.color[2] = 0xFF;
  28. mainText.color[3] = 0xFF;
  29. mainText.scale = 1.2f;
  30. mainText.y = 10;
  31. mainText.w = 0;
  32. mainText.h = 0;
  33. mMapListFilled = false;
  34. mFirstPass = false;
  35. }
  36. Menu::~Menu() {
  37. delete [] mainText.text;
  38. while (mMapList.size() > 0) {
  39. delete [] mMapList.back();
  40. mMapList.pop_back();
  41. }
  42. }
  43. void Menu::setVisible(bool visible) {
  44. mVisible = visible;
  45. }
  46. bool Menu::isVisible() {
  47. return mVisible;
  48. }
  49. void Menu::loadPakFolderRecursive(const char *dir) {
  50. struct dirent entry;
  51. struct dirent *ep = NULL;
  52. DIR *pakDir;
  53. assert(dir != NULL);
  54. assert(dir[0] != '\0');
  55. pakDir = opendir(dir);
  56. if (pakDir != NULL) {
  57. readdir_r(pakDir, &entry, &ep);
  58. while (ep != NULL) {
  59. if (ep->d_type == DT_DIR) {
  60. if ((strcmp(".", ep->d_name) != 0)
  61. && (strcmp("..", ep->d_name) != 0)) {
  62. char *tmp = bufferString("%s%s", dir, ep->d_name);
  63. char *next = fullPath(tmp, '/');
  64. loadPakFolderRecursive(next);
  65. delete next;
  66. delete tmp;
  67. }
  68. } else {
  69. char *fullPathMap = bufferString("%s%s", dir, ep->d_name);
  70. char *lowerPath = bufferString("%s", fullPathMap);
  71. for (char *p = lowerPath; *p; ++p) *p = (char)tolower(*p);
  72. // Check for valid extension
  73. if (stringEndsWith(lowerPath, ".phd")
  74. || stringEndsWith(lowerPath, ".tr2")
  75. || stringEndsWith(lowerPath, ".tr4")
  76. || stringEndsWith(lowerPath, ".trc")) {
  77. int error = TombRaider::checkMime(fullPathMap);
  78. if (error == 0) {
  79. // Just load relative filename
  80. mMapList.push_back(bufferString("%s", (fullPathMap + strlen(getOpenRaider().mPakDir) + 1)));
  81. } else {
  82. getConsole().print("Error: pak file '%s' %s",
  83. fullPathMap, (error == -1) ? "not found" : "invalid");
  84. }
  85. }
  86. delete [] lowerPath;
  87. delete [] fullPathMap;
  88. }
  89. readdir_r(pakDir, &entry, &ep);
  90. }
  91. closedir(pakDir);
  92. } else {
  93. getConsole().print("Could not open PAK dir %s!", dir);
  94. }
  95. }
  96. void Menu::fillMapList() {
  97. char *tmp = fullPath(getOpenRaider().mPakDir, '/');
  98. loadPakFolderRecursive(tmp);
  99. delete [] tmp;
  100. mMapListFilled = true;
  101. }
  102. void Menu::displayMapList() {
  103. // Estimate displayable number of items
  104. int items = (getWindow().mHeight - 110) / 25;
  105. // Select which part of the list to show
  106. int min, max;
  107. if (((int)mCursor - (items / 2)) > 0)
  108. min = mCursor - (items / 2);
  109. else
  110. min = 0;
  111. if ((mCursor + (items / 2)) < mMapList.size())
  112. max = mCursor + (items / 2);
  113. else
  114. max = mMapList.size();
  115. while ((max - min) < items) {
  116. if (min > 0)
  117. min--;
  118. else if (max < ((int)mMapList.size()))
  119. max++;
  120. else
  121. break;
  122. }
  123. mMin = min;
  124. for (int i = 0; i < (max - min); i++) {
  125. char *map = mMapList[i + min];
  126. if ((i + min) == (int)mCursor)
  127. getWindow().drawText(25, 100 + (25 * i), 0.75f, RED, "%s", map);
  128. else
  129. getWindow().drawText(25, 100 + (25 * i), 0.75f, OR_BLUE, "%s", map);
  130. }
  131. }
  132. void Menu::display() {
  133. if (mVisible) {
  134. // Draw half-transparent *overlay*
  135. glColor4f(0.0f, 0.0f, 0.0f, 0.75f);
  136. glDisable(GL_TEXTURE_2D);
  137. glRecti(0, 0, getWindow().mWidth, getWindow().mHeight);
  138. glEnable(GL_TEXTURE_2D);
  139. // Draw heading text
  140. mainText.x = (getWindow().mWidth / 2) - (mainText.w / 2);
  141. getWindow().writeString(&mainText);
  142. if (!mMapListFilled) {
  143. getWindow().drawText(25, (getWindow().mHeight / 2) - 20, 0.75f, OR_BLUE, "Generating map list...");
  144. } else {
  145. if (mMapList.size() == 0) {
  146. getWindow().drawText(25, (getWindow().mHeight / 2) - 20, 0.75f, RED, "No maps found! See README.md");
  147. } else {
  148. // draw *play button* above list
  149. glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  150. glDisable(GL_TEXTURE_2D);
  151. glRecti(25, 25, 100, 75);
  152. glEnable(GL_TEXTURE_2D);
  153. getWindow().drawText(40, 35, 0.75f, BLACK, "Play");
  154. displayMapList();
  155. }
  156. }
  157. // Fill map list after first render pass,
  158. // so menu *loading screen* is visible
  159. if (mFirstPass) {
  160. if (!mMapListFilled) {
  161. fillMapList();
  162. }
  163. } else {
  164. mFirstPass = true;
  165. }
  166. }
  167. }
  168. void Menu::handleKeyboard(KeyboardButton key, bool pressed) {
  169. if (!pressed)
  170. return;
  171. if (key == upKey) {
  172. if (mCursor > 0)
  173. mCursor--;
  174. else
  175. mCursor = mMapList.size() - 1;
  176. } else if (key == downKey) {
  177. if (mCursor < (mMapList.size() - 1))
  178. mCursor++;
  179. else
  180. mCursor = 0;
  181. } else if (key == rightKey) {
  182. int i = 10;
  183. if (mCursor > (mMapList.size() - 11))
  184. i = mMapList.size() - 1 - mCursor;
  185. while (i-- > 0)
  186. handleKeyboard(downKey, true);
  187. } else if (key == leftKey) {
  188. int i = 10;
  189. if (mCursor < 10)
  190. i = mCursor;
  191. while (i-- > 0)
  192. handleKeyboard(upKey, true);
  193. } else if (key == enterKey) {
  194. char *tmp = bufferString("load %s", mMapList[mCursor]);
  195. if (getOpenRaider().command(tmp) == 0) {
  196. setVisible(false);
  197. } else {
  198. //! \todo Display something if an error occurs
  199. }
  200. delete [] tmp;
  201. }
  202. }
  203. void Menu::handleMouseClick(unsigned int x, unsigned int y, KeyboardButton button, bool released) {
  204. int items = (getWindow().mHeight - 110) / 25;
  205. if ((!released) || (button != leftmouseKey))
  206. return;
  207. if ((y >= 100) && (y <= (unsigned int)(100 + (25 * items)))) {
  208. y -= 100;
  209. mCursor = mMin + (y / 25);
  210. } else if ((y >= 25) && (y <= 100) && (x >= 25) && (x <= 125)) {
  211. // Play button
  212. mCursor = 0;
  213. }
  214. }