Open Source Tomb Raider Engine
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

Menu.cpp 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /*!
  2. * \file src/Menu.cpp
  3. * \brief Menu 'overlay'
  4. *
  5. * \author xythobuz
  6. */
  7. #include <cctype>
  8. #include <cstring>
  9. #include "global.h"
  10. #include "Console.h"
  11. #include "OpenRaider.h"
  12. #include "utils/strings.h"
  13. #include "TombRaider.h"
  14. #include "Window.h"
  15. #include "Menu.h"
  16. #if defined(HAVE_DIRENT_H) && defined(HAVE_OPENDIR) && defined(HAVE_READDIR_R) && defined(HAVE_CLOSEDIR) && defined(HAVE_DT_DIR)
  17. #include <dirent.h>
  18. #define USE_DIRENT
  19. #elif defined (_WIN32)
  20. #include <windows.h>
  21. #define USE_FINDFILE
  22. #else
  23. #error No support for recursive folder traversal
  24. #endif
  25. Menu::Menu() {
  26. mVisible = false;
  27. mCursor = 0;
  28. mMin = 0;
  29. mainText.text = bufferString("%s", VERSION);
  30. mainText.color[0] = BLUE[0];
  31. mainText.color[1] = BLUE[1];
  32. mainText.color[2] = BLUE[2];
  33. mainText.color[3] = BLUE[3];
  34. mainText.scale = 1.2f;
  35. mainText.y = 10;
  36. mainText.w = 0;
  37. mainText.h = 0;
  38. mMapListFilled = false;
  39. mFirstPass = false;
  40. }
  41. Menu::~Menu() {
  42. delete [] mainText.text;
  43. mainText.text = NULL;
  44. while (mMapList.size() > 0) {
  45. delete [] mMapList.back();
  46. mMapList.pop_back();
  47. }
  48. }
  49. void Menu::setVisible(bool visible) {
  50. mVisible = visible;
  51. }
  52. bool Menu::isVisible() {
  53. return mVisible;
  54. }
  55. void Menu::loadPakFolderRecursive(const char *dir) {
  56. assert(dir != NULL);
  57. assert(dir[0] != '\0');
  58. #ifdef USE_DIRENT
  59. struct dirent entry;
  60. struct dirent *ep = NULL;
  61. DIR *pakDir;
  62. pakDir = opendir(dir);
  63. if (pakDir != NULL) {
  64. readdir_r(pakDir, &entry, &ep);
  65. while (ep != NULL) {
  66. if (ep->d_type == DT_DIR) {
  67. if ((strcmp(".", ep->d_name) != 0)
  68. && (strcmp("..", ep->d_name) != 0)) {
  69. char *tmp = bufferString("%s%s", dir, ep->d_name);
  70. char *next = fullPath(tmp, '/');
  71. loadPakFolderRecursive(next);
  72. delete next;
  73. delete tmp;
  74. }
  75. } else {
  76. char *fullPathMap = bufferString("%s%s", dir, ep->d_name);
  77. char *lowerPath = bufferString("%s", fullPathMap);
  78. for (char *p = lowerPath; *p; ++p) *p = (char)tolower(*p);
  79. // Check for valid extension
  80. if (stringEndsWith(lowerPath, ".phd")
  81. || stringEndsWith(lowerPath, ".tr2")
  82. || stringEndsWith(lowerPath, ".tr4")
  83. || stringEndsWith(lowerPath, ".trc")) {
  84. int error = TombRaider::checkMime(fullPathMap);
  85. if (error == 0) {
  86. // Just load relative filename
  87. mMapList.push_back(bufferString("%s", (fullPathMap + strlen(getOpenRaider().mPakDir) + 1)));
  88. } else {
  89. getConsole().print("Error: pak file '%s' %s",
  90. fullPathMap, (error == -1) ? "not found" : "invalid");
  91. }
  92. }
  93. delete [] lowerPath;
  94. delete [] fullPathMap;
  95. }
  96. readdir_r(pakDir, &entry, &ep);
  97. }
  98. closedir(pakDir);
  99. } else {
  100. getConsole().print("Could not open PAK dir %s!", dir);
  101. }
  102. #elif defined(USE_FINDFILE)
  103. std::vector<char *> list;
  104. list.push_back(bufferString("%s", dir));
  105. do {
  106. WIN32_FIND_DATA fd;
  107. char *tmp = bufferString("%s\\*", list.at(0));
  108. HANDLE hFind = FindFirstFile(tmp, &fd);
  109. do {
  110. if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  111. list.push_back(bufferString("%s\\%s", list.at(0), fd.cFileName));
  112. } else {
  113. char *fullPathMap = bufferString("%s\\%s", list.at(0), fd.cFileName);
  114. char *lowerPath = bufferString("%s", fullPathMap);
  115. for (char *p = lowerPath; *p; ++p) *p = (char)tolower(*p);
  116. // Check for valid extension
  117. if (stringEndsWith(lowerPath, ".phd")
  118. || stringEndsWith(lowerPath, ".tr2")
  119. || stringEndsWith(lowerPath, ".tr4")
  120. || stringEndsWith(lowerPath, ".trc")) {
  121. int error = TombRaider::checkMime(fullPathMap);
  122. if (error == 0) {
  123. // Just load relative filename
  124. mMapList.push_back(bufferString("%s", (fullPathMap + strlen(getOpenRaider().mPakDir) + 1)));
  125. } else {
  126. getConsole().print("Error: pak file '%s' %s",
  127. fullPathMap, (error == -1) ? "not found" : "invalid");
  128. }
  129. }
  130. delete [] lowerPath;
  131. delete [] fullPathMap;
  132. }
  133. } while (FindNextFile(hFind, &fd) != 0);
  134. FindClose(hFind);
  135. delete [] tmp;
  136. delete [] list.at(0);
  137. list.erase(list.begin());
  138. } while (list.size() > 0);
  139. #endif
  140. }
  141. void Menu::fillMapList() {
  142. char *tmp = fullPath(getOpenRaider().mPakDir, '/');
  143. loadPakFolderRecursive(tmp);
  144. delete [] tmp;
  145. mMapListFilled = true;
  146. }
  147. void Menu::displayMapList() {
  148. // Estimate displayable number of items
  149. int items = (getWindow().getHeight() - 110) / 25;
  150. // Select which part of the list to show
  151. long min, max;
  152. if (((long)mCursor - (items / 2)) > 0)
  153. min = mCursor - (items / 2);
  154. else
  155. min = 0;
  156. if ((mCursor + (items / 2)) < mMapList.size())
  157. max = mCursor + (items / 2);
  158. else
  159. max = mMapList.size();
  160. while ((max - min) < items) {
  161. if (min > 0)
  162. min--;
  163. else if (max < ((int)mMapList.size()))
  164. max++;
  165. else
  166. break;
  167. }
  168. mMin = min;
  169. for (int i = 0; i < (max - min); i++) {
  170. char *map = mMapList[i + min];
  171. if ((i + min) == (int)mCursor)
  172. getFont().drawText(25, 100 + (25 * i), 0.75f, RED, "%s", map);
  173. else
  174. getFont().drawText(25, 100 + (25 * i), 0.75f, BLUE, "%s", map);
  175. }
  176. }
  177. void Menu::display() {
  178. if (mVisible) {
  179. // Draw half-transparent *overlay*
  180. glColor4f(0.0f, 0.0f, 0.0f, 0.75f);
  181. glDisable(GL_TEXTURE_2D);
  182. glRecti(0, 0, getWindow().getWidth(), getWindow().getHeight());
  183. glEnable(GL_TEXTURE_2D);
  184. // Draw heading text, using FontString so we can get the
  185. // width of the drawn text to center it
  186. mainText.x = (getWindow().getWidth() / 2) - (mainText.w / 2);
  187. getFont().writeString(mainText);
  188. if (!mMapListFilled) {
  189. getFont().drawText(25, (getWindow().getHeight() / 2) - 20, 0.75f, BLUE, "Generating map list...");
  190. } else {
  191. if (mMapList.size() == 0) {
  192. getFont().drawText(25, (getWindow().getHeight() / 2) - 20, 0.75f, RED, "No maps found! See README.md");
  193. } else {
  194. // draw *play button* above list
  195. glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  196. glDisable(GL_TEXTURE_2D);
  197. glRecti(25, 25, 100, 75);
  198. glEnable(GL_TEXTURE_2D);
  199. getFont().drawText(40, 35, 0.75f, BLACK, "Play");
  200. displayMapList();
  201. }
  202. }
  203. // Fill map list after first render pass,
  204. // so menu *loading screen* is visible
  205. if (mFirstPass) {
  206. if (!mMapListFilled) {
  207. fillMapList();
  208. }
  209. } else {
  210. mFirstPass = true;
  211. }
  212. }
  213. }
  214. void Menu::play() {
  215. char *tmp = bufferString("load %s", mMapList[mCursor]);
  216. if (getOpenRaider().command(tmp) == 0) {
  217. setVisible(false);
  218. } else {
  219. //! \todo Display something if an error occurs
  220. }
  221. delete [] tmp;
  222. }
  223. void Menu::handleKeyboard(KeyboardButton key, bool pressed) {
  224. if (!pressed)
  225. return;
  226. if (key == upKey) {
  227. if (mCursor > 0)
  228. mCursor--;
  229. else
  230. mCursor = mMapList.size() - 1;
  231. } else if (key == downKey) {
  232. if (mCursor < (mMapList.size() - 1))
  233. mCursor++;
  234. else
  235. mCursor = 0;
  236. } else if (key == rightKey) {
  237. long i = 10;
  238. if (mCursor > (mMapList.size() - 11))
  239. i = mMapList.size() - 1 - mCursor;
  240. while (i-- > 0)
  241. handleKeyboard(downKey, true);
  242. } else if (key == leftKey) {
  243. long i = 10;
  244. if (mCursor < 10)
  245. i = mCursor;
  246. while (i-- > 0)
  247. handleKeyboard(upKey, true);
  248. } else if (key == enterKey) {
  249. play();
  250. }
  251. }
  252. void Menu::handleMouseClick(unsigned int x, unsigned int y, KeyboardButton button, bool released) {
  253. int items = (getWindow().getHeight() - 110) / 25;
  254. if (released || (button != leftmouseKey))
  255. return;
  256. if ((y >= 100) && (y <= (unsigned int)(100 + (25 * items)))) {
  257. y -= 100;
  258. mCursor = mMin + (y / 25);
  259. } else if ((y >= 25) && (y <= 100) && (x >= 25) && (x <= 125)) {
  260. // Play button
  261. play();
  262. }
  263. }