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.

OpenRaider.cpp 42KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170
  1. /*!
  2. * \file src/OpenRaider.cpp
  3. * \brief Main Game Object
  4. *
  5. * \author xythobuz
  6. */
  7. #include <cstdio>
  8. #include <cstring>
  9. #include <assert.h>
  10. #include <dirent.h>
  11. #include "WindowSDL.h"
  12. #include "config.h"
  13. #include "Console.h"
  14. #include "Game.h"
  15. #include "main.h"
  16. #include "math/math.h"
  17. #include "Menu.h"
  18. #include "Sound.h"
  19. #include "TombRaider.h"
  20. #include "utils/strings.h"
  21. #include "utils/time.h"
  22. #include "OpenRaider.h"
  23. OpenRaider::OpenRaider() {
  24. mRunning = false;
  25. mFPS = false;
  26. mBaseDir = NULL;
  27. mPakDir = NULL;
  28. mAudioDir = NULL;
  29. mDataDir = NULL;
  30. mMapListFilled = false;
  31. for (int i = 0; i < ActionEventCount; i++)
  32. keyBindings[i] = unknown;
  33. mCameraRotationDeltaX = OR_DEG_TO_RAD(1.0f);
  34. mCameraRotationDeltaY = OR_DEG_TO_RAD(1.0f);
  35. }
  36. OpenRaider::~OpenRaider() {
  37. if (mBaseDir)
  38. delete mBaseDir;
  39. if (mPakDir)
  40. delete mPakDir;
  41. if (mAudioDir)
  42. delete mAudioDir;
  43. if (mDataDir)
  44. delete mDataDir;
  45. while (mMapList.size() > 0) {
  46. delete [] mMapList.back();
  47. mMapList.pop_back();
  48. }
  49. }
  50. int OpenRaider::loadConfig(const char *config) {
  51. assert(config != NULL);
  52. assert(config[0] != '\0');
  53. char *configFile = fullPath(config, 0);
  54. getConsole().print("Loading config from \"%s\"...", configFile);
  55. FILE *f = fopen(configFile, "r");
  56. if (f == NULL) {
  57. getConsole().print("Could not open file!");
  58. return -1;
  59. }
  60. char buffer[256];
  61. while (fgets(buffer, 256, f) != NULL) {
  62. int error = command(buffer);
  63. if (error != 0) {
  64. getConsole().print("Error Code: %d", error);
  65. }
  66. }
  67. fclose(f);
  68. return 0;
  69. }
  70. int OpenRaider::command(const char *command) {
  71. assert(command != NULL);
  72. assert(command[0] != '\0');
  73. int returnValue = 0;
  74. char *cmd = bufferString("%s", command);
  75. size_t length = strlen(cmd);
  76. // Command ends at '\n' or # when a comment begins
  77. for (size_t i = 0; i < length; i++) {
  78. if (cmd[i] == '\n' || cmd[i] == '#') {
  79. cmd[i] = '\0';
  80. break;
  81. }
  82. }
  83. char *token = strtok(cmd, " \t");
  84. if (token != NULL) {
  85. // token is the command to execute
  86. // get arguments
  87. std::vector<char *> args;
  88. char *next;
  89. while ((next = strtok(NULL, " \t")) != NULL) {
  90. args.push_back(next);
  91. }
  92. // Execute
  93. returnValue = this->command(token, &args);
  94. }
  95. free(cmd);
  96. return returnValue;
  97. }
  98. int OpenRaider::command(const char *command, std::vector<char *> *args) {
  99. assert(command != NULL);
  100. assert(command[0] != '\0');
  101. assert(args != NULL);
  102. if (strcmp(command, "set") == 0) {
  103. if (args->size() != 2) {
  104. getConsole().print("Invalid use of set-command");
  105. return -1;
  106. } else {
  107. return set(args->at(0), args->at(1));
  108. }
  109. } else if (strcmp(command, "bind") == 0) {
  110. if (args->size() != 2) {
  111. getConsole().print("Invalid use of bind-command");
  112. return -2;
  113. } else {
  114. return bind(args->at(0), args->at(1));
  115. }
  116. } else if (strcmp(command, "quit") == 0) {
  117. exit(0);
  118. } else if (strcmp(command, "load") == 0) {
  119. char *tmp = bufferString("%s/%s", mPakDir, args->at(0));
  120. int error = getGame().loadLevel(tmp);
  121. delete [] tmp;
  122. return error;
  123. } else if (strcmp(command, "sshot") == 0) {
  124. char *filename = bufferString("%s/sshots/%s", mBaseDir, VERSION);
  125. bool console = (args->size() > 0) && (strcmp(args->at(0), "console") == 0);
  126. bool menu = (args->size() > 0) && (strcmp(args->at(0), "menu") == 0);
  127. if (!console) {
  128. getConsole().setVisible(false);
  129. if (menu)
  130. getMenu().setVisible(true);
  131. frame();
  132. frame(); // Double buffered
  133. }
  134. getRender().screenShot(filename);
  135. if (!console) {
  136. getConsole().setVisible(true);
  137. if (menu)
  138. getMenu().setVisible(false);
  139. }
  140. getConsole().print("Screenshot stored...");
  141. delete filename;
  142. } else if (strcmp(command, "mode") == 0) {
  143. if (args->size() > 0) {
  144. char *mode = args->at(0);
  145. if (strcmp(mode, "wireframe") == 0) {
  146. if (getGame().mLoaded) {
  147. getRender().setMode(Render::modeWireframe);
  148. getConsole().print("Wireframe mode");
  149. } else {
  150. getConsole().print("Load a level to set this mode!");
  151. return -3;
  152. }
  153. } else if (strcmp(mode, "solid") == 0) {
  154. if (getGame().mLoaded) {
  155. getRender().setMode(Render::modeSolid);
  156. getConsole().print("Solid mode");
  157. } else {
  158. getConsole().print("Load a level to set this mode!");
  159. return -4;
  160. }
  161. } else if (strcmp(mode, "texture") == 0) {
  162. if (getGame().mLoaded) {
  163. getRender().setMode(Render::modeTexture);
  164. getConsole().print("Texture mode");
  165. } else {
  166. getConsole().print("Load a level to set this mode!");
  167. return -5;
  168. }
  169. } else if (strcmp(mode, "vertexlight") == 0) {
  170. if (getGame().mLoaded) {
  171. getRender().setMode(Render::modeVertexLight);
  172. getConsole().print("Vertexlight mode");
  173. } else {
  174. getConsole().print("Load a level to set this mode!");
  175. return -6;
  176. }
  177. } else if (strcmp(mode, "titlescreen") == 0) {
  178. getRender().setMode(Render::modeLoadScreen);
  179. getConsole().print("Titlescreen mode");
  180. } else {
  181. getConsole().print("Invalid use of mode command (%s)!", mode);
  182. return -7;
  183. }
  184. } else {
  185. getConsole().print("Invalid use of mode command!");
  186. return -8;
  187. }
  188. } else if (strcmp(command, "move") == 0) {
  189. if (args->size() > 0) {
  190. if (getGame().mLoaded) {
  191. char *move = args->at(0);
  192. if (strcmp(move, "walk") == 0) {
  193. getGame().mLara->moveType = worldMoveType_walk;
  194. getConsole().print("Lara is walking...");
  195. } else if (strcmp(move, "fly") == 0) {
  196. getGame().mLara->moveType = worldMoveType_fly;
  197. getConsole().print("Lara is flying...");
  198. } else if (strcmp(move, "noclip") == 0) {
  199. getGame().mLara->moveType = worldMoveType_noClipping;
  200. getConsole().print("Lara is noclipping...");
  201. } else {
  202. getConsole().print("Invalid use of move command (%s)!", move);
  203. return -9;
  204. }
  205. } else {
  206. getConsole().print("Load a level to change the movement type!");
  207. return -10;
  208. }
  209. } else {
  210. getConsole().print("Invalid use of move command!");
  211. return -11;
  212. }
  213. } else if (strcmp(command, "sound") == 0) {
  214. if (args->size() > 0) {
  215. getSound().play(atoi(args->at(0)));
  216. } else {
  217. getConsole().print("Invalid use of sound command!");
  218. return -12;
  219. }
  220. } else if (strcmp(command, "animate") == 0) {
  221. if (args->size() > 0) {
  222. char c = args->at(0)[0];
  223. if (c == 'n') {
  224. // Step all skeletal models to their next animation
  225. if (getRender().getFlags() & Render::fAnimateAllModels) {
  226. for (unsigned int i = 0; i < getRender().mModels.size(); i++) {
  227. SkeletalModel *m = getRender().mModels[i];
  228. if (m->getAnimation() < ((int)m->model->animation.size() - 1))
  229. m->setAnimation(m->getAnimation() + 1);
  230. else
  231. if (m->getAnimation() != 0)
  232. m->setAnimation(0);
  233. }
  234. } else {
  235. getConsole().print("Animations need to be enabled!");
  236. }
  237. } else if (c == 'p') {
  238. // Step all skeletal models to their previous animation
  239. if (getRender().getFlags() & Render::fAnimateAllModels) {
  240. for (unsigned int i = 0; i < getRender().mModels.size(); i++) {
  241. SkeletalModel *m = getRender().mModels[i];
  242. if (m->getAnimation() > 0)
  243. m->setAnimation(m->getAnimation() - 1);
  244. else
  245. if (m->model->animation.size() > 0)
  246. m->setAnimation(m->model->animation.size() - 1);
  247. }
  248. } else {
  249. getConsole().print("Animations need to be enabled!");
  250. }
  251. } else {
  252. // Enable or disable animating all skeletal models
  253. bool b;
  254. if (readBool(args->at(0), &b) < 0) {
  255. getConsole().print("Pass BOOL to animate command!");
  256. return -13;
  257. }
  258. if (b)
  259. getRender().setFlags(Render::fAnimateAllModels);
  260. else
  261. getRender().clearFlags(Render::fAnimateAllModels);
  262. getConsole().print(b ? "Animating all models" : "No longer animating all models");
  263. }
  264. } else {
  265. getConsole().print("Invalid use of animate command!");
  266. return -14;
  267. }
  268. } else if (strcmp(command, "light") == 0) {
  269. if (args->size() > 0) {
  270. bool b;
  271. if (readBool(args->at(0), &b) < 0) {
  272. getConsole().print("Pass BOOL to light command!");
  273. return -15;
  274. }
  275. if (b)
  276. getRender().setFlags(Render::fGL_Lights);
  277. else
  278. getRender().clearFlags(Render::fGL_Lights);
  279. getConsole().print("GL-Lights are now %s", b ? "on" : "off");
  280. } else {
  281. getConsole().print("Invalid use of light-command!");
  282. return -16;
  283. }
  284. } else if (strcmp(command, "fog") == 0) {
  285. if (args->size() > 0) {
  286. bool b;
  287. if (readBool(args->at(0), &b) < 0) {
  288. getConsole().print("Pass BOOL to fog command!");
  289. return -17;
  290. }
  291. if (b)
  292. getRender().setFlags(Render::fFog);
  293. else
  294. getRender().clearFlags(Render::fFog);
  295. getConsole().print("Fog is now %s", b ? "on" : "off");
  296. } else {
  297. getConsole().print("Invalid use of fog-command!");
  298. return -18;
  299. }
  300. } else if (strcmp(command, "hop") == 0) {
  301. if (args->size() > 0) {
  302. bool b;
  303. if (readBool(args->at(0), &b) < 0) {
  304. getConsole().print("Pass BOOL to hop command!");
  305. return -19;
  306. }
  307. if (b)
  308. getWorld().setFlag(World::fEnableHopping);
  309. else
  310. getWorld().clearFlag(World::fEnableHopping);
  311. getConsole().print("Room hopping is now %s", b ? "on" : "off");
  312. } else {
  313. getConsole().print("Invalid use of hop-command!");
  314. return -20;
  315. }
  316. } else if (strcmp(command, "viewmodel") == 0) {
  317. if (getGame().mLara) {
  318. SkeletalModel *smdl = static_cast<SkeletalModel *>(getGame().mLara->tmpHook);
  319. skeletal_model_t *mdl = getWorld().getModel(atoi(args->at(0)));
  320. if (smdl)
  321. smdl->setModel(mdl);
  322. }
  323. //m_render.ViewModel(LARA, atoi(cmd));
  324. } else if (strcmp(command, "pos") == 0) {
  325. if (getGame().mLara) {
  326. getConsole().print("Position:");
  327. getConsole().print(" Room %i (0x%X)", getGame().mLara->room, getWorld().getRoomInfo(getGame().mLara->room));
  328. getConsole().print(" %.1fx %.1fy %.1fz", getGame().mLara->pos[0], getGame().mLara->pos[1], getGame().mLara->pos[2]);
  329. getConsole().print(" %.1f Yaw %.1f Pitch", OR_RAD_TO_DEG(getGame().mLara->angles[1]), OR_RAD_TO_DEG(getGame().mLara->angles[2]));
  330. } else {
  331. getConsole().print("Load a level to get Laras position!");
  332. return -21;
  333. }
  334. } else if (strcmp(command, "vmodel") == 0) {
  335. if (args->size() > 0) {
  336. bool b;
  337. if (readBool(args->at(0), &b) < 0) {
  338. getConsole().print("Pass BOOL to vmodel command!");
  339. return -22;
  340. }
  341. if (b)
  342. getRender().setFlags(Render::fViewModel);
  343. else
  344. getRender().clearFlags(Render::fViewModel);
  345. getConsole().print("Viewmodel is now %s", b ? "on" : "off");
  346. } else {
  347. getConsole().print("Invalid use of vmodel-command!");
  348. return -23;
  349. }
  350. } else if (strcmp(command, "ralpha") == 0) {
  351. if (args->size() > 0) {
  352. bool b;
  353. if (readBool(args->at(0), &b) < 0) {
  354. getConsole().print("Pass BOOL to ralpha command!");
  355. return -24;
  356. }
  357. if (b)
  358. getRender().setFlags(Render::fRoomAlpha);
  359. else
  360. getRender().clearFlags(Render::fRoomAlpha);
  361. getConsole().print("Room Alpha is now %s", b ? "on" : "off");
  362. } else {
  363. getConsole().print("Invalid use of ralpha-command!");
  364. return -25;
  365. }
  366. } else if (strcmp(command, "portal") == 0) {
  367. if (args->size() > 0) {
  368. bool b;
  369. if (readBool(args->at(0), &b) < 0) {
  370. getConsole().print("Pass BOOL to portal command!");
  371. return -26;
  372. }
  373. if (b)
  374. getRender().setFlags(Render::fPortals);
  375. else
  376. getRender().clearFlags(Render::fPortals);
  377. getConsole().print("Portals are now %s", b ? "on" : "off");
  378. } else {
  379. getConsole().print("Invalid use of portal-command!");
  380. return -27;
  381. }
  382. } else if (strcmp(command, "vis") == 0) {
  383. if (args->size() > 0) {
  384. bool b;
  385. if (readBool(args->at(0), &b) < 0) {
  386. getConsole().print("Pass BOOL to vis command!");
  387. return -28;
  388. }
  389. if (b)
  390. getRender().setFlags(Render::fUsePortals);
  391. else
  392. getRender().clearFlags(Render::fUsePortals);
  393. getConsole().print("Portals are now %s", b ? "on" : "off");
  394. } else {
  395. getConsole().print("Invalid use of vis-command!");
  396. return -29;
  397. }
  398. } else if (strcmp(command, "upf") == 0) {
  399. if (args->size() > 0) {
  400. bool b;
  401. if (readBool(args->at(0), &b) < 0) {
  402. getConsole().print("Pass BOOL to upf command!");
  403. return -30;
  404. }
  405. if (b)
  406. getRender().setFlags(Render::fUpdateRoomListPerFrame);
  407. else
  408. getRender().clearFlags(Render::fUpdateRoomListPerFrame);
  409. getConsole().print("URLPF is now %s", b ? "on" : "off");
  410. } else {
  411. getConsole().print("Invalid use of upf-command!");
  412. return -31;
  413. }
  414. } else if (strcmp(command, "particle") == 0) {
  415. if (args->size() > 0) {
  416. bool b;
  417. if (readBool(args->at(0), &b) < 0) {
  418. getConsole().print("Pass BOOL to particle command!");
  419. return -32;
  420. }
  421. if (b)
  422. getRender().setFlags(Render::fParticles);
  423. else
  424. getRender().clearFlags(Render::fParticles);
  425. getConsole().print("Particles are now %s", b ? "on" : "off");
  426. } else {
  427. getConsole().print("Invalid use of particle-command!");
  428. return -33;
  429. }
  430. } else if (strcmp(command, "sprite") == 0) {
  431. if (args->size() > 0) {
  432. bool b;
  433. if (readBool(args->at(0), &b) < 0) {
  434. getConsole().print("Pass BOOL to sprite command!");
  435. return -34;
  436. }
  437. if (b)
  438. getRender().setFlags(Render::fSprites);
  439. else
  440. getRender().clearFlags(Render::fSprites);
  441. getConsole().print("Sprites are now %s", b ? "on" : "off");
  442. } else {
  443. getConsole().print("Invalid use of sprite-command!");
  444. return -35;
  445. }
  446. } else if (strcmp(command, "roommodel") == 0) {
  447. if (args->size() > 0) {
  448. bool b;
  449. if (readBool(args->at(0), &b) < 0) {
  450. getConsole().print("Pass BOOL to roommodel command!");
  451. return -36;
  452. }
  453. if (b)
  454. getRender().setFlags(Render::fRoomModels);
  455. else
  456. getRender().clearFlags(Render::fRoomModels);
  457. getConsole().print("Roommodels are now %s", b ? "on" : "off");
  458. } else {
  459. getConsole().print("Invalid use of roommodel-command!");
  460. return -37;
  461. }
  462. } else if (strcmp(command, "entmodel") == 0) {
  463. if (args->size() > 0) {
  464. bool b;
  465. if (readBool(args->at(0), &b) < 0) {
  466. getConsole().print("Pass BOOL to entmodel command!");
  467. return -38;
  468. }
  469. if (b)
  470. getRender().setFlags(Render::fEntityModels);
  471. else
  472. getRender().clearFlags(Render::fEntityModels);
  473. getConsole().print("Entmodels are now %s", b ? "on" : "off");
  474. } else {
  475. getConsole().print("Invalid use of entmodel-command!");
  476. return -39;
  477. }
  478. } else if (strcmp(command, "oneroom") == 0) {
  479. if (args->size() > 0) {
  480. bool b;
  481. if (readBool(args->at(0), &b) < 0) {
  482. getConsole().print("Pass BOOL to oneroom command!");
  483. return -40;
  484. }
  485. if (b)
  486. getRender().setFlags(Render::fOneRoom);
  487. else
  488. getRender().clearFlags(Render::fOneRoom);
  489. getConsole().print("Rendering one room is now %s", b ? "on" : "off");
  490. } else {
  491. getConsole().print("Invalid use of oneroom-command!");
  492. return -41;
  493. }
  494. } else if (strcmp(command, "allrooms") == 0) {
  495. if (args->size() > 0) {
  496. bool b;
  497. if (readBool(args->at(0), &b) < 0) {
  498. getConsole().print("Pass BOOL to allrooms command!");
  499. return -42;
  500. }
  501. if (b)
  502. getRender().setFlags(Render::fAllRooms);
  503. else
  504. getRender().clearFlags(Render::fAllRooms);
  505. getConsole().print("Rendering all rooms is now %s", b ? "on" : "off");
  506. } else {
  507. getConsole().print("Invalid use of allrooms-command!");
  508. return -43;
  509. }
  510. } else if (strcmp(command, "ponytail") == 0) {
  511. if (args->size() > 0) {
  512. bool b;
  513. if (readBool(args->at(0), &b) < 0) {
  514. getConsole().print("Pass BOOL to ponytail command!");
  515. return -44;
  516. }
  517. if (b)
  518. getRender().setFlags(Render::fRenderPonytail);
  519. else
  520. getRender().clearFlags(Render::fRenderPonytail);
  521. getConsole().print("Ponytail is now %s", b ? "on" : "off");
  522. } else {
  523. getConsole().print("Invalid use of ponytail-command!");
  524. return -45;
  525. }
  526. } else if (strcmp(command, "help") == 0) {
  527. if (args->size() == 0) {
  528. getConsole().print("Available commands:");
  529. getConsole().print(" load - load a level");
  530. getConsole().print(" set - set a parameter");
  531. getConsole().print(" bind - bind a keyboard/mouse action");
  532. getConsole().print(" sshot - make a screenshot");
  533. getConsole().print(" move - [walk|fly|noclip]");
  534. getConsole().print(" sound - INT - Test play sound");
  535. getConsole().print(" mode - MODE - Render mode");
  536. getConsole().print(" animate - [BOOL|n|p] - Animate models");
  537. getConsole().print(" light - BOOL - GL Lights");
  538. getConsole().print(" fog - BOOL - GL Fog");
  539. getConsole().print(" hop - BOOL - Room hop");
  540. getConsole().print(" viewmodel - INT - Change Laras model");
  541. getConsole().print(" pos - Print position info");
  542. getConsole().print(" vmodel - BOOL - View Model");
  543. getConsole().print(" ralpha - BOOL - Room Alpha");
  544. getConsole().print(" portal - BOOL");
  545. getConsole().print(" vis - BOOL - Use Portals");
  546. getConsole().print(" upf - BOOL - Update Room List Per Frame");
  547. getConsole().print(" particle - BOOL");
  548. getConsole().print(" sprite - BOOL");
  549. getConsole().print(" roommodel - BOOL");
  550. getConsole().print(" entmodel - BOOL");
  551. getConsole().print(" oneroom - BOOL");
  552. getConsole().print(" allrooms - BOOL");
  553. getConsole().print(" ponytail - BOOL");
  554. getConsole().print(" help - print command help");
  555. getConsole().print(" quit - exit OpenRaider");
  556. getConsole().print("Use help COMMAND to get additional info");
  557. } else if (args->size() == 1) {
  558. return help(args->at(0));
  559. } else {
  560. getConsole().print("Invalid use of help-command");
  561. return -46;
  562. }
  563. } else {
  564. getConsole().print("Unknown command: %s ", command);
  565. return -47;
  566. }
  567. return 0;
  568. }
  569. int OpenRaider::help(const char *cmd) {
  570. assert(cmd != NULL);
  571. assert(cmd[0] != '\0');
  572. if (strcmp(cmd, "set") == 0) {
  573. getConsole().print("set-Command Usage:");
  574. getConsole().print(" set VAR VAL");
  575. getConsole().print("Available Variables:");
  576. getConsole().print(" basedir STRING");
  577. getConsole().print(" pakdir STRING");
  578. getConsole().print(" audiodir STRING");
  579. getConsole().print(" datadir STRING");
  580. getConsole().print(" font STRING");
  581. getConsole().print(" gldriver STRING");
  582. getConsole().print(" size \"INTxINT\"");
  583. getConsole().print(" fullscreen BOOL");
  584. getConsole().print(" audio BOOL");
  585. getConsole().print(" volume BOOL");
  586. getConsole().print(" mouse_x FLOAT");
  587. getConsole().print(" mouse_y FLOAT");
  588. getConsole().print(" fps BOOL");
  589. getConsole().print("Enclose STRINGs with \"\"!");
  590. getConsole().print("size expects a STRING in the specified format");
  591. } else if (strcmp(cmd, "bind") == 0) {
  592. getConsole().print("bind-Command Usage:");
  593. getConsole().print(" bind ACTION KEY");
  594. getConsole().print("Available Actions:");
  595. getConsole().print(" menu");
  596. getConsole().print(" console");
  597. getConsole().print(" forward");
  598. getConsole().print(" backward");
  599. getConsole().print(" left");
  600. getConsole().print(" right");
  601. getConsole().print(" jump");
  602. getConsole().print(" crouch");
  603. getConsole().print(" use");
  604. getConsole().print(" holster");
  605. getConsole().print("Key-Format:");
  606. getConsole().print(" 'a' or '1' for character/number keys");
  607. getConsole().print(" \"leftctrl\" for symbols and special keys");
  608. } else if (strcmp(cmd, "load") == 0) {
  609. getConsole().print("load-Command Usage:");
  610. getConsole().print(" load levelfile.name");
  611. } else if (strcmp(cmd, "game") == 0) {
  612. getConsole().print("Use \"game help\" for more info");
  613. } else if (strcmp(cmd, "sshot") == 0) {
  614. getConsole().print("sshot-Command Usage:");
  615. getConsole().print(" sshot [console|menu]");
  616. getConsole().print("Add console/menu to capture them too");
  617. } else if (strcmp(cmd, "sound") == 0) {
  618. getConsole().print("sound-Command Usage:");
  619. getConsole().print(" sound INT");
  620. getConsole().print("Where INT is a valid sound ID integer");
  621. } else if (strcmp(cmd, "move") == 0) {
  622. getConsole().print("move-Command Usage:");
  623. getConsole().print(" move COMMAND");
  624. getConsole().print("Where COMMAND is one of the following:");
  625. getConsole().print(" walk");
  626. getConsole().print(" fly");
  627. getConsole().print(" noclip");
  628. } else if (strcmp(cmd, "mode") == 0) {
  629. getConsole().print("mode-Command Usage:");
  630. getConsole().print(" mode MODE");
  631. getConsole().print("Where MODE is one of the following:");
  632. getConsole().print(" wireframe");
  633. getConsole().print(" solid");
  634. getConsole().print(" texture");
  635. getConsole().print(" vertexlight");
  636. getConsole().print(" titlescreen");
  637. } else if (strcmp(cmd, "animate") == 0) {
  638. getConsole().print("animate-Command Usage:");
  639. getConsole().print(" animate [n|p|BOOL]");
  640. getConsole().print("Where the commands have the following meaning:");
  641. getConsole().print(" BOOL to (de)activate animating all models");
  642. getConsole().print(" n to step all models to their next animation");
  643. getConsole().print(" p to step all models to their previous animation");
  644. } else {
  645. getConsole().print("No help available for %s", cmd);
  646. return -1;
  647. }
  648. return 0;
  649. }
  650. char *OpenRaider::expandDirectoryNames(const char *s) {
  651. const char *base = "$(basedir)";
  652. const char *pak = "$(pakdir)";
  653. const char *audio = "$(audiodir)";
  654. const char *data = "$(datadir)";
  655. assert(s != NULL);
  656. assert(s[0] != '\0');
  657. if (mBaseDir != NULL) {
  658. if (strstr(s, base) != NULL) {
  659. return stringReplace(s, base, mBaseDir);
  660. }
  661. }
  662. if (mPakDir != NULL) {
  663. if (strstr(s, pak) != NULL) {
  664. return stringReplace(s, pak, mPakDir);
  665. }
  666. }
  667. if (mAudioDir != NULL) {
  668. if (strstr(s, audio) != NULL) {
  669. return stringReplace(s, audio, mAudioDir);
  670. }
  671. }
  672. if (mDataDir != NULL) {
  673. if (strstr(s, data) != NULL) {
  674. return stringReplace(s, data, mDataDir);
  675. }
  676. }
  677. return NULL;
  678. }
  679. #define CHANGE_DIR_WITH_EXPANSION(a) do { \
  680. char *quotes = stringRemoveQuotes(value); \
  681. char *tmp = expandDirectoryNames(quotes); \
  682. if (tmp == NULL) { \
  683. a = fullPath(quotes, 0); \
  684. } else { \
  685. a = fullPath(tmp, 0); \
  686. delete [] tmp; \
  687. } \
  688. delete [] quotes; \
  689. } while(false)
  690. int OpenRaider::set(const char *var, const char *value) {
  691. assert(var != NULL);
  692. assert(var[0] != '\0');
  693. assert(value != NULL);
  694. assert(value[0] != '\0');
  695. if (strcmp(var, "size") == 0) {
  696. // value has format like "\"1024x768\""
  697. unsigned int w = DEFAULT_WIDTH, h = DEFAULT_HEIGHT;
  698. if (sscanf(value, "\"%dx%d\"", &w, &h) != 2) {
  699. getConsole().print("set-size-Error: Invalid value (%s)", value);
  700. return -2;
  701. }
  702. getWindow().setSize(w, h);
  703. } else if (strcmp(var, "fullscreen") == 0) {
  704. bool fullscreen = false;
  705. if (readBool(value, &fullscreen) != 0) {
  706. getConsole().print("set-fullscreen-Error: Invalid value (%s)", value);
  707. return -3;
  708. }
  709. getWindow().setFullscreen(fullscreen);
  710. } else if (strcmp(var, "gldriver") == 0) {
  711. getWindow().setDriver(value);
  712. } else if (strcmp(var, "audio") == 0) {
  713. bool audio = false;
  714. if (readBool(value, &audio) != 0) {
  715. getConsole().print("set-audio-Error: Invalid value (%s)", value);
  716. return -4;
  717. }
  718. getSound().setEnabled(audio);
  719. } else if (strcmp(var, "volume") == 0) {
  720. float vol = 1.0f;
  721. if (sscanf(value, "%f", &vol) != 1) {
  722. getConsole().print("set-volume-Error: Invalid value (%s)", value);
  723. return -5;
  724. }
  725. getSound().setVolume(vol);
  726. } else if (strcmp(var, "mouse_x") == 0) {
  727. float sense = 1.0f;
  728. if (sscanf(value, "%f", &sense) != 1) {
  729. getConsole().print("set-mouse_x-Error: Invalid value (%s)", value);
  730. return -6;
  731. }
  732. mCameraRotationDeltaX = OR_DEG_TO_RAD(sense);
  733. } else if (strcmp(var, "mouse_y") == 0) {
  734. float sense = 1.0f;
  735. if (sscanf(value, "%f", &sense) != 1) {
  736. getConsole().print("set-mouse_y-Error: Invalid value (%s)", value);
  737. return -7;
  738. }
  739. mCameraRotationDeltaY = OR_DEG_TO_RAD(sense);
  740. } else if (strcmp(var, "fps") == 0) {
  741. bool fps = false;
  742. if (readBool(value, &fps) != 0) {
  743. getConsole().print("set-fps-Error: Invalid value (%s)", value);
  744. return -8;
  745. }
  746. mFPS = fps;
  747. } else if (strcmp(var, "basedir") == 0) {
  748. CHANGE_DIR_WITH_EXPANSION(mBaseDir);
  749. } else if (strcmp(var, "pakdir") == 0) {
  750. CHANGE_DIR_WITH_EXPANSION(mPakDir);
  751. } else if (strcmp(var, "audiodir") == 0) {
  752. CHANGE_DIR_WITH_EXPANSION(mAudioDir);
  753. } else if (strcmp(var, "datadir") == 0) {
  754. CHANGE_DIR_WITH_EXPANSION(mDataDir);
  755. } else if (strcmp(var, "font") == 0) {
  756. char *quotes = stringReplace(value, "\"", "");
  757. char *tmp = expandDirectoryNames(quotes);
  758. if (tmp == NULL) {
  759. getWindow().setFont(quotes);
  760. } else {
  761. getWindow().setFont(tmp);
  762. delete [] tmp;
  763. }
  764. delete [] quotes;
  765. } else {
  766. getConsole().print("set-Error: Unknown variable (%s = %s)", var, value);
  767. return -1;
  768. }
  769. return 0;
  770. }
  771. int OpenRaider::bind(const char *action, const char *key) {
  772. assert(action != NULL);
  773. assert(action[0] != '\0');
  774. assert(key != NULL);
  775. assert(key[0] != '\0');
  776. const char *tmp = action;
  777. if (action[0] == '+')
  778. tmp++;
  779. if (strcmp(tmp, "menu") == 0) {
  780. return bind(menuAction, key);
  781. } else if (strcmp(tmp, "console") == 0) {
  782. return bind(consoleAction, key);
  783. } else if (strcmp(tmp, "forward") == 0) {
  784. return bind(forwardAction, key);
  785. } else if (strcmp(tmp, "backward") == 0) {
  786. return bind(backwardAction, key);
  787. } else if (strcmp(tmp, "left") == 0) {
  788. return bind(leftAction, key);
  789. } else if (strcmp(tmp, "right") == 0) {
  790. return bind(rightAction, key);
  791. } else if (strcmp(tmp, "jump") == 0) {
  792. return bind(jumpAction, key);
  793. } else if (strcmp(tmp, "crouch") == 0) {
  794. return bind(crouchAction, key);
  795. } else if (strcmp(tmp, "use") == 0) {
  796. return bind(useAction, key);
  797. } else if (strcmp(tmp, "holster") == 0) {
  798. return bind(holsterAction, key);
  799. } else {
  800. getConsole().print("bind-Error: Unknown action (%s --> %s)", key, action);
  801. return -1;
  802. }
  803. }
  804. int OpenRaider::bind(ActionEvents action, const char *key) {
  805. assert(action < ActionEventCount);
  806. assert(key != NULL);
  807. assert(key[0] != '\0');
  808. size_t length = strlen(key);
  809. if ((key[0] == '\'') && (key[length - 1] == '\'') && (length == 3)) {
  810. // Literal character like w, a, s, d, 0, 1...
  811. char c = key[1];
  812. if (((c >= '0') && (c <= '9'))
  813. || ((c >= 'a') && (c <= 'z'))) {
  814. keyBindings[action] = (KeyboardButton)c;
  815. } else {
  816. getConsole().print("bind-\'\'-Error: Unknown key (%s)", key);
  817. return -1;
  818. }
  819. } else if ((key[0] == '\"') && (key[length - 1] == '\"')) {
  820. // Special characters like tilde, esc, quote...
  821. char *tmp = stringRemoveQuotes(key);
  822. if (strcmp(tmp, "quote") == 0) {
  823. keyBindings[action] = quote;
  824. } else if (strcmp(tmp, "backslash") == 0) {
  825. keyBindings[action] = quote;
  826. } else if (strcmp(tmp, "backspace") == 0) {
  827. keyBindings[action] = backspace;
  828. } else if (strcmp(tmp, "capslock") == 0) {
  829. keyBindings[action] = capslock;
  830. } else if (strcmp(tmp, "comma") == 0) {
  831. keyBindings[action] = comma;
  832. } else if (strcmp(tmp, "del") == 0) {
  833. keyBindings[action] = del;
  834. } else if (strcmp(tmp, "up") == 0) {
  835. keyBindings[action] = up;
  836. } else if (strcmp(tmp, "down") == 0) {
  837. keyBindings[action] = down;
  838. } else if (strcmp(tmp, "left") == 0) {
  839. keyBindings[action] = left;
  840. } else if (strcmp(tmp, "right") == 0) {
  841. keyBindings[action] = right;
  842. } else if (strcmp(tmp, "end") == 0) {
  843. keyBindings[action] = end;
  844. } else if (strcmp(tmp, "equals") == 0) {
  845. keyBindings[action] = equals;
  846. } else if (strcmp(tmp, "escape") == 0) {
  847. keyBindings[action] = escape;
  848. } else if (strcmp(tmp, "f1") == 0) {
  849. keyBindings[action] = f1;
  850. } else if (strcmp(tmp, "f2") == 0) {
  851. keyBindings[action] = f2;
  852. } else if (strcmp(tmp, "f3") == 0) {
  853. keyBindings[action] = f3;
  854. } else if (strcmp(tmp, "f4") == 0) {
  855. keyBindings[action] = f4;
  856. } else if (strcmp(tmp, "f5") == 0) {
  857. keyBindings[action] = f5;
  858. } else if (strcmp(tmp, "f6") == 0) {
  859. keyBindings[action] = f6;
  860. } else if (strcmp(tmp, "f7") == 0) {
  861. keyBindings[action] = f7;
  862. } else if (strcmp(tmp, "f8") == 0) {
  863. keyBindings[action] = f8;
  864. } else if (strcmp(tmp, "f9") == 0) {
  865. keyBindings[action] = f9;
  866. } else if (strcmp(tmp, "f10") == 0) {
  867. keyBindings[action] = f10;
  868. } else if (strcmp(tmp, "f11") == 0) {
  869. keyBindings[action] = f11;
  870. } else if (strcmp(tmp, "f12") == 0) {
  871. keyBindings[action] = f12;
  872. } else if (strcmp(tmp, "backquote") == 0) {
  873. keyBindings[action] = backquote;
  874. } else if (strcmp(tmp, "home") == 0) {
  875. keyBindings[action] = home;
  876. } else if (strcmp(tmp, "insert") == 0) {
  877. keyBindings[action] = insert;
  878. } else if (strcmp(tmp, "leftalt") == 0) {
  879. keyBindings[action] = leftalt;
  880. } else if (strcmp(tmp, "leftctrl") == 0) {
  881. keyBindings[action] = leftctrl;
  882. } else if (strcmp(tmp, "leftbracket") == 0) {
  883. keyBindings[action] = leftbracket;
  884. } else if (strcmp(tmp, "leftgui") == 0) {
  885. keyBindings[action] = leftgui;
  886. } else if (strcmp(tmp, "leftshift") == 0) {
  887. keyBindings[action] = leftshift;
  888. } else if (strcmp(tmp, "minus") == 0) {
  889. keyBindings[action] = minus;
  890. } else if (strcmp(tmp, "numlock") == 0) {
  891. keyBindings[action] = numlock;
  892. } else if (strcmp(tmp, "pagedown") == 0) {
  893. keyBindings[action] = pagedown;
  894. } else if (strcmp(tmp, "pageup") == 0) {
  895. keyBindings[action] = pageup;
  896. } else if (strcmp(tmp, "pause") == 0) {
  897. keyBindings[action] = pause;
  898. } else if (strcmp(tmp, "dot") == 0) {
  899. keyBindings[action] = dot;
  900. } else if (strcmp(tmp, "rightalt") == 0) {
  901. keyBindings[action] = rightalt;
  902. } else if (strcmp(tmp, "rightctrl") == 0) {
  903. keyBindings[action] = rightctrl;
  904. } else if (strcmp(tmp, "enter") == 0) {
  905. keyBindings[action] = enter;
  906. } else if (strcmp(tmp, "rightgui") == 0) {
  907. keyBindings[action] = rightgui;
  908. } else if (strcmp(tmp, "rightbracket") == 0) {
  909. keyBindings[action] = rightbracket;
  910. } else if (strcmp(tmp, "rightshift") == 0) {
  911. keyBindings[action] = rightshift;
  912. } else if (strcmp(tmp, "scrolllock") == 0) {
  913. keyBindings[action] = scrolllock;
  914. } else if (strcmp(tmp, "semicolon") == 0) {
  915. keyBindings[action] = semicolon;
  916. } else if (strcmp(tmp, "slash") == 0) {
  917. keyBindings[action] = slash;
  918. } else if (strcmp(tmp, "space") == 0) {
  919. keyBindings[action] = space;
  920. } else if (strcmp(tmp, "tab") == 0) {
  921. keyBindings[action] = tab;
  922. } else if (strcmp(tmp, "leftmouse") == 0) {
  923. keyBindings[action] = leftmouse;
  924. } else if (strcmp(tmp, "middlemouse") == 0) {
  925. keyBindings[action] = middlemouse;
  926. } else if (strcmp(tmp, "rightmouse") == 0) {
  927. keyBindings[action] = rightmouse;
  928. } else {
  929. getConsole().print("bind-\"\"-Error: Unknown key (%s)", key);
  930. delete [] tmp;
  931. return -2;
  932. }
  933. delete [] tmp;
  934. } else {
  935. getConsole().print("bind-Error: Unknown key (%s)", key);
  936. return -3;
  937. }
  938. return 0;
  939. }
  940. void OpenRaider::loadPakFolderRecursive(const char *dir) {
  941. struct dirent *ep;
  942. DIR *pakDir;
  943. assert(dir != NULL);
  944. assert(dir[0] != '\0');
  945. assert(mRunning == true);
  946. pakDir = opendir(dir);
  947. if (pakDir != NULL) {
  948. while ((ep = readdir(pakDir)) != NULL) {
  949. if (ep->d_type == DT_DIR) {
  950. if ((strcmp(".", ep->d_name) != 0)
  951. && (strcmp("..", ep->d_name) != 0)) {
  952. char *tmp = bufferString("%s%s", dir, ep->d_name);
  953. char *next = fullPath(tmp, '/');
  954. loadPakFolderRecursive(next);
  955. delete next;
  956. delete tmp;
  957. }
  958. } else {
  959. char *fullPathMap = bufferString("%s%s", dir, ep->d_name);
  960. char *lowerPath = bufferString("%s", fullPathMap);
  961. for (char *p = lowerPath; *p; ++p) *p = (char)tolower(*p);
  962. // Check for valid extension
  963. if (stringEndsWith(lowerPath, ".phd")
  964. || stringEndsWith(lowerPath, ".tr2")
  965. || stringEndsWith(lowerPath, ".tr4")
  966. || stringEndsWith(lowerPath, ".trc")) {
  967. int error = TombRaider::checkMime(fullPathMap);
  968. if (error == 0) {
  969. // Just load relative filename
  970. mMapList.push_back(bufferString("%s", (fullPathMap + strlen(mPakDir) + 1)));
  971. } else {
  972. getConsole().print("Error: pak file '%s' %s",
  973. fullPathMap, (error == -1) ? "not found" : "invalid");
  974. }
  975. }
  976. delete [] lowerPath;
  977. delete [] fullPathMap;
  978. }
  979. }
  980. closedir(pakDir);
  981. } else {
  982. getConsole().print("Could not open PAK dir %s!", dir);
  983. }
  984. }
  985. void OpenRaider::fillMapList() {
  986. assert(mRunning == true);
  987. char *tmp = fullPath(mPakDir, '/');
  988. loadPakFolderRecursive(tmp);
  989. delete [] tmp;
  990. mMapListFilled = true;
  991. }
  992. void OpenRaider::run() {
  993. assert(mRunning == false);
  994. mRunning = true;
  995. while (mRunning) {
  996. frame();
  997. }
  998. }
  999. void OpenRaider::frame() {
  1000. assert(mRunning == true);
  1001. static clock_t fpsSum = 0, fpsCount = 0;
  1002. static int fps = 0;
  1003. clock_t startTime = systemTimerGet();
  1004. // Get keyboard and mouse input
  1005. getWindow().eventHandling();
  1006. // Clear screen
  1007. glClearColor(0.00f, 0.00f, 0.00f, 1.0f);
  1008. glClear(GL_COLOR_BUFFER_BIT);
  1009. // Draw game scene
  1010. getRender().display();
  1011. // Draw 2D overlays (console and menu)
  1012. getWindow().glEnter2D();
  1013. getConsole().display();
  1014. getMenu().display();
  1015. // Draw FPS counter
  1016. if (mFPS)
  1017. getWindow().drawText(10, getWindow().mHeight - 20, 0.5f, OR_BLUE, "%dFPS", fps);
  1018. getWindow().glExit2D();
  1019. // Put new frame on screen
  1020. getWindow().swapBuffersGL();
  1021. // Fill map list after first render pass,
  1022. // so menu *loading screen* is visible
  1023. if (!mMapListFilled)
  1024. fillMapList();
  1025. // Calculate FPS display value
  1026. fpsCount++;
  1027. fpsSum += (systemTimerGet() - startTime);
  1028. if (fpsSum >= 500) {
  1029. // Update every 500ms
  1030. fps = (int)((float)fpsCount * (1000.0f / (float)fpsSum));
  1031. fpsCount = fpsSum = 0;
  1032. }
  1033. }
  1034. void OpenRaider::handleKeyboard(KeyboardButton key, bool pressed) {
  1035. assert(key < unknown);
  1036. assert(mRunning == true);
  1037. if ((keyBindings[menuAction] == key) && pressed) {
  1038. getMenu().setVisible(!getMenu().isVisible());
  1039. } else if (!getMenu().isVisible()) {
  1040. if ((keyBindings[consoleAction] == key) && pressed) {
  1041. getConsole().setVisible(!getConsole().isVisible());
  1042. } else if (!getConsole().isVisible()) {
  1043. for (int i = forwardAction; i < ActionEventCount; i++) {
  1044. if (keyBindings[i] == key) {
  1045. getGame().handleAction((ActionEvents)i, pressed);
  1046. }
  1047. }
  1048. } else {
  1049. getConsole().handleKeyboard(key, pressed);
  1050. }
  1051. } else {
  1052. getMenu().handleKeyboard(key, pressed);
  1053. }
  1054. getWindow().setMousegrab(!(getMenu().isVisible() || getConsole().isVisible()));
  1055. }
  1056. void OpenRaider::handleText(char *text, bool notFinished) {
  1057. assert(text != NULL);
  1058. assert(text[0] != '\0');
  1059. assert(mRunning == true);
  1060. if ((getConsole().isVisible()) && (!getMenu().isVisible())) {
  1061. getConsole().handleText(text, notFinished);
  1062. }
  1063. }
  1064. void OpenRaider::handleMouseClick(unsigned int x, unsigned int y, KeyboardButton button, bool released) {
  1065. assert(button < unknown);
  1066. assert(mRunning == true);
  1067. if (getMenu().isVisible()) {
  1068. getMenu().handleMouseClick(x, y, button, released);
  1069. } else if (!getConsole().isVisible()) {
  1070. for (int i = forwardAction; i < ActionEventCount; i++) {
  1071. if (keyBindings[i] == button) {
  1072. getGame().handleAction((ActionEvents)i, !released);
  1073. }
  1074. }
  1075. }
  1076. }
  1077. void OpenRaider::handleMouseMotion(int xrel, int yrel) {
  1078. assert((xrel != 0) || (yrel != 0));
  1079. assert(mRunning == true);
  1080. if ((!getConsole().isVisible()) && (!getMenu().isVisible())) {
  1081. getGame().handleMouseMotion(xrel, yrel);
  1082. }
  1083. }
  1084. void OpenRaider::handleMouseScroll(int xrel, int yrel) {
  1085. assert((xrel != 0) || (yrel != 0));
  1086. assert(mRunning == true);
  1087. if ((getConsole().isVisible()) && (!getMenu().isVisible())) {
  1088. getConsole().handleMouseScroll(xrel, yrel);
  1089. }
  1090. }