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

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