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.9KB

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