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.

Game.cpp 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. /*!
  2. * \file src/Game.cpp
  3. * \brief Game abstraction
  4. *
  5. * \author xythobuz
  6. */
  7. #include <algorithm>
  8. #include <map>
  9. #include <cstdlib>
  10. #include "global.h"
  11. #include "Console.h"
  12. #include "Game.h"
  13. #include "OpenRaider.h"
  14. #include "Sound.h"
  15. #include "utils/strings.h"
  16. #include "games/TombRaider1.h"
  17. #ifdef EXPERIMENTAL
  18. std::vector<unsigned int> gColorTextureHACK;
  19. #endif
  20. #ifdef MULTITEXTURE
  21. std::map<int, int> gMapTex2Bump;
  22. #endif
  23. Game::Game() {
  24. mLoaded = false;
  25. mName = NULL;
  26. mLara = -1;
  27. mTextureStart = 0;
  28. mTextureOffset = 0;
  29. }
  30. Game::~Game() {
  31. destroy();
  32. }
  33. unsigned int Game::getTextureStart() {
  34. return mTextureStart;
  35. }
  36. unsigned int Game::getTextureOffset() {
  37. return mTextureOffset;
  38. }
  39. int Game::initialize() {
  40. // Enable Renderer
  41. mTextureStart = getRender().initTextures(getOpenRaider().mDataDir);
  42. getRender().setMode(Render::modeLoadScreen);
  43. return 0;
  44. }
  45. void Game::destroy() {
  46. delete [] mName;
  47. mName = NULL;
  48. mLoaded = false;
  49. mLara = -1;
  50. getRender().setMode(Render::modeDisabled);
  51. getWorld().destroy();
  52. getRender().ClearWorld();
  53. getSound().clear(); // Remove all previously loaded sounds
  54. }
  55. bool Game::isLoaded() {
  56. return mLoaded;
  57. }
  58. int Game::loadLevel(const char *level) {
  59. if (mLoaded)
  60. destroy();
  61. mName = bufferString("%s", level);
  62. getConsole().print("Loading %s", mName);
  63. int error = mTombRaider.Load(mName);
  64. if (error != 0)
  65. return error;
  66. // If required, load the external sound effect file MAIN.SFX into TombRaider
  67. if ((mTombRaider.getEngine() == TR_VERSION_2) || (mTombRaider.getEngine() == TR_VERSION_3)) {
  68. char *tmp = bufferString("%sMAIN.SFX", level); // Ensure theres enough space
  69. size_t length = strlen(tmp);
  70. size_t dir = 0;
  71. for (int i = length - 1; i >= 0; i--) {
  72. if ((tmp[i] == '/') || (tmp[i] == '\\')) {
  73. dir = i + 1; // Find where the filename (bla.tr2) starts
  74. break;
  75. }
  76. }
  77. strcpy(tmp + dir, "MAIN.SFX"); // overwrite the name itself with MAIN.SFX
  78. tmp[dir + 8] = '\0';
  79. error = mTombRaider.loadSFX(tmp);
  80. if (error != 0)
  81. getConsole().print("Could not load %s", tmp);
  82. delete [] tmp;
  83. }
  84. // Process data
  85. processTextures();
  86. processRooms();
  87. processModels();
  88. processSprites();
  89. processMoveables();
  90. processPakSounds();
  91. mTombRaider.reset();
  92. if (mLara == -1) {
  93. getConsole().print("Can't find Lara entity in level pak!");
  94. destroy();
  95. return -1;
  96. } else {
  97. mLoaded = true;
  98. getRender().setMode(Render::modeVertexLight);
  99. return 0;
  100. }
  101. }
  102. void Game::handleAction(ActionEvents action, bool isFinished) {
  103. if (mLoaded && (!isFinished)) {
  104. if (action == forwardAction) {
  105. getLara().move('f');
  106. } else if (action == backwardAction) {
  107. getLara().move('b');
  108. } else if (action == leftAction) {
  109. getLara().move('l');
  110. } else if (action == rightAction) {
  111. getLara().move('r');
  112. } else if (action == jumpAction) {
  113. } else if (action == crouchAction) {
  114. } else if (action == useAction) {
  115. } else if (action == holsterAction) {
  116. } else if (action == walkAction) {
  117. }
  118. }
  119. }
  120. void Game::handleMouseMotion(int xrel, int yrel) {
  121. if (mLoaded) {
  122. // Move Camera on X Axis
  123. if (xrel > 0)
  124. while (xrel-- > 0)
  125. getCamera().command(CAMERA_ROTATE_RIGHT);
  126. else if (xrel < 0)
  127. while (xrel++ < 0)
  128. getCamera().command(CAMERA_ROTATE_LEFT);
  129. // Move Camera on Y Axis
  130. if (yrel > 0)
  131. while (yrel-- > 0)
  132. getCamera().command(CAMERA_ROTATE_UP);
  133. else if (yrel < 0)
  134. while (yrel++ < 0)
  135. getCamera().command(CAMERA_ROTATE_DOWN);
  136. // Fix Laras rotation
  137. getLara().setAngle(getCamera().getRadianYaw());
  138. }
  139. }
  140. Entity &Game::getLara() {
  141. assert(mLara >= 0);
  142. assert(mLara < (int)getWorld().sizeEntity());
  143. return getWorld().getEntity(mLara);
  144. }
  145. void Game::processSprites() {
  146. for (int i = 0; i < (mTombRaider.NumItems() - 1); i++) {
  147. if ((mTombRaider.Engine() == TR_VERSION_1) && (mTombRaider.Item()[i].intensity1 == -1))
  148. continue;
  149. for (int j = 0; j < mTombRaider.NumSpriteSequences(); j++) {
  150. if (mTombRaider.SpriteSequence()[j].object_id == mTombRaider.Item()[i].object_id)
  151. getWorld().addSprite(*new SpriteSequence(mTombRaider, i, j));
  152. }
  153. }
  154. getConsole().print("Found %d sprites.", mTombRaider.NumSpriteSequences());
  155. }
  156. void Game::processRooms() {
  157. for (int index = 0; index < mTombRaider.NumRooms(); index++)
  158. getWorld().addRoom(*new Room(mTombRaider, index));
  159. getConsole().print("Found %d rooms.", mTombRaider.NumRooms());
  160. }
  161. void Game::processPakSounds()
  162. {
  163. unsigned char *riff;
  164. unsigned int riffSz;
  165. //tr2_sound_source_t *sound;
  166. //tr2_sound_details_t *detail;
  167. //float pos[3];
  168. unsigned int i;
  169. int id;
  170. /* detail
  171. short sample;
  172. short volume;
  173. short sound_range;
  174. short flags; // bits 8-15: priority?, 2-7: number of sound samples
  175. // in this group, bits 0-1: channel number
  176. */
  177. for (i = 0; i < mTombRaider.getSoundSamplesCount(); ++i)
  178. {
  179. mTombRaider.getSoundSample(i, &riffSz, &riff);
  180. getSound().addWave(riff, riffSz, &id, Sound::SoundFlagsNone);
  181. //if (((i + 1) == TR_SOUND_F_PISTOL) && (id > 0))
  182. //{
  183. //m_testSFX = id;
  184. //}
  185. delete [] riff;
  186. // sound[i].sound_id; // internal sound index
  187. // sound[i].flags; // 0x40, 0x80, or 0xc0
  188. //pos[0] = sound[i].x;
  189. //pos[1] = sound[i].y;
  190. //pos[2] = sound[i].z;
  191. //getSound().SourceAt(id, pos);
  192. }
  193. getConsole().print("Found %u sound samples.", mTombRaider.getSoundSamplesCount());
  194. }
  195. void Game::processTextures()
  196. {
  197. unsigned char *image;
  198. unsigned char *bumpmap;
  199. int i;
  200. //if ( mTombRaider.getNumBumpMaps())
  201. // gBumpMapStart = mTombRaider.NumTextures();
  202. for (i = 0; i < mTombRaider.NumTextures(); ++i)
  203. {
  204. mTombRaider.Texture(i, &image, &bumpmap);
  205. // Overwrite any previous level textures on load
  206. getRender().loadTexture(image, 256, 256, (mTextureStart - 1) + i);
  207. #ifdef MULTITEXTURE
  208. gMapTex2Bump[(mTextureStart - 1) + i] = -1;
  209. #endif
  210. if (bumpmap)
  211. {
  212. #ifdef MULTITEXTURE
  213. gMapTex2Bump[(mTextureStart - 1) + i] = (mTextureStart - 1) + i +
  214. mTombRaider.NumTextures();
  215. #endif
  216. getRender().loadTexture(bumpmap, 256, 256, (mTextureStart - 1) + i +
  217. mTombRaider.NumTextures());
  218. }
  219. if (image)
  220. delete [] image;
  221. if (bumpmap)
  222. delete [] bumpmap;
  223. }
  224. mTextureOffset = (mTextureStart - 1) + mTombRaider.NumTextures();
  225. getConsole().print("Found %d textures.", mTombRaider.NumTextures());
  226. }
  227. void Game::processMoveables()
  228. {
  229. unsigned int statCount = 0;
  230. tr2_moveable_t *moveable = mTombRaider.Moveable();
  231. tr2_item_t *item = mTombRaider.Item();
  232. tr2_sprite_sequence_t *sprite_sequence = mTombRaider.SpriteSequence();
  233. for (int i = 0; i < mTombRaider.NumItems(); ++i)
  234. {
  235. int j;
  236. int object_id = item[i].object_id;
  237. // It may not be a moveable, test for sprite
  238. if (!(mTombRaider.Engine() == TR_VERSION_1 && item[i].intensity1 == -1)) {
  239. for (j = 0; j < (int)mTombRaider.NumSpriteSequences(); ++j) {
  240. if (sprite_sequence[j].object_id == object_id)
  241. break;
  242. }
  243. // It's not a moveable, skip sprite
  244. if (j < (int)mTombRaider.NumSpriteSequences())
  245. continue;
  246. }
  247. for (j = 0; j < (int)mTombRaider.NumMoveables(); ++j) {
  248. if ((int)moveable[j].object_id == object_id)
  249. break;
  250. }
  251. // It's not a moveable or even a sprite? Skip unknown
  252. if (j == (int)mTombRaider.NumMoveables())
  253. continue;
  254. processMoveable(j, i, object_id);
  255. statCount++;
  256. }
  257. /*
  258. // Get models that aren't items
  259. for (int i = 0; i < mTombRaider.NumMoveables(); ++i)
  260. {
  261. switch ((int)moveable[i].object_id)
  262. {
  263. case 30:
  264. case 2: // Which tr needs this as model again?
  265. processMoveable(i, i, (int)moveable[i].object_id);
  266. break;
  267. default:
  268. switch (mTombRaider.Engine())
  269. {
  270. case TR_VERSION_1:
  271. switch ((int)moveable[i].object_id)
  272. {
  273. case TombRaider1::LaraMutant:
  274. processMoveable(i, i, (int)moveable[i].object_id);
  275. break;
  276. }
  277. break;
  278. case TR_VERSION_4:
  279. switch ((int)moveable[i].object_id)
  280. {
  281. case TR4_PISTOLS_ANIM:
  282. case TR4_UZI_ANIM:
  283. case TR4_SHOTGUN_ANIM:
  284. case TR4_CROSSBOW_ANIM:
  285. case TR4_GRENADE_GUN_ANIM:
  286. case TR4_SIXSHOOTER_ANIM:
  287. processMoveable(i, i, (int)moveable[i].object_id);
  288. break;
  289. }
  290. break;
  291. case TR_VERSION_2:
  292. case TR_VERSION_3:
  293. case TR_VERSION_5:
  294. case TR_VERSION_UNKNOWN:
  295. break;
  296. }
  297. }
  298. }
  299. */
  300. getConsole().print("Found %d moveables.", mTombRaider.NumMoveables() + statCount);
  301. }
  302. // index moveable, i item, sometimes both moveable
  303. // object_id of item or moveable
  304. void Game::processMoveable(int index, int i, int object_id) {
  305. // Check if the SkeletalModel is already cached
  306. bool cached = false;
  307. unsigned int model;
  308. for (model = 0; model < getWorld().sizeSkeletalModel(); model++) {
  309. if (getWorld().getSkeletalModel(model).getId() == object_id) {
  310. cached = true;
  311. break;
  312. }
  313. }
  314. // Create a new SkeletalModel, if needed
  315. if (!cached)
  316. getWorld().addSkeletalModel(*new SkeletalModel(mTombRaider, index, object_id));
  317. // Create a new Entity, using the cached or the new SkeletalModel
  318. Entity &entity = *new Entity(mTombRaider, index, i, model);
  319. getWorld().addEntity(entity);
  320. // Store reference to Lara
  321. if (entity.getObjectId() == 0)
  322. mLara = getWorld().sizeEntity() - 1;
  323. // Store reference to the SkyMesh
  324. if (i == mTombRaider.getSkyModelId())
  325. getRender().setSkyMesh(i, //moveable[i].starting_mesh,
  326. (mTombRaider.Engine() == TR_VERSION_2));
  327. }
  328. bool compareFaceTextureId(const void *voidA, const void *voidB)
  329. {
  330. texture_tri_t *a = (texture_tri_t *)voidA, *b = (texture_tri_t *)voidB;
  331. if (!a || !b)
  332. return false; // error really
  333. return (a->texture < b->texture);
  334. }
  335. #ifdef EXPERIMENTAL
  336. void Game::setupTextureColor(texture_tri_t *r_tri, float *colorf)
  337. {
  338. unsigned char color[4];
  339. unsigned int colorI;
  340. color[0] = (unsigned char)(colorf[0]*255.0f);
  341. color[1] = (unsigned char)(colorf[1]*255.0f);
  342. color[2] = (unsigned char)(colorf[2]*255.0f);
  343. color[3] = (unsigned char)(colorf[3]*255.0f);
  344. ((unsigned char *)(&colorI))[3] = color[0];
  345. ((unsigned char *)(&colorI))[2] = color[1];
  346. ((unsigned char *)(&colorI))[1] = color[2];
  347. ((unsigned char *)(&colorI))[0] = color[3];
  348. bool found = false;
  349. unsigned int foundIndex = 0;
  350. for (foundIndex = 0; foundIndex < gColorTextureHACK.size(); foundIndex++) {
  351. if (gColorTextureHACK[foundIndex] == colorI) {
  352. found = true;
  353. break;
  354. }
  355. }
  356. if (!found)
  357. {
  358. gColorTextureHACK.push_back(colorI);
  359. r_tri->texture = mTextureOffset + gColorTextureHACK.size();
  360. getRender().loadTexture(Texture::generateColorTexture(color, 32, 32),
  361. 32, 32, r_tri->texture);
  362. }
  363. else
  364. {
  365. //getConsole().print("Color already loaded %i -> 0x%08x",
  366. // gColorTextureHACK.getCurrentIndex(),
  367. // gColorTextureHACK.current());
  368. r_tri->texture = mTextureOffset + foundIndex;
  369. }
  370. //r_tri->texture = white; // White texture
  371. }
  372. #endif
  373. void Game::processModels()
  374. {
  375. for (int index = 0; index < mTombRaider.getMeshCount(); index++) {
  376. int i, j, count, texture;
  377. int vertexIndices[6];
  378. float st[12];
  379. float color[4];
  380. unsigned short transparency;
  381. texture_tri_t *r_tri;
  382. // Assert common sense
  383. if (index < 0 || !mTombRaider.isMeshValid(index))
  384. {
  385. //! \fixme allow sparse lists with matching ids instead?
  386. getWorld().addMesh(NULL); // Filler, to make meshes array ids align
  387. return;
  388. }
  389. #ifndef EXPERIMENTAL
  390. // WHITE texture id
  391. int white = 0;
  392. #endif
  393. model_mesh_t *mesh = new model_mesh_t;
  394. // Mongoose 2002.08.30, Testing support for 'shootable' models ( traceable )
  395. mTombRaider.getMeshCollisionInfo(index, mesh->center, &mesh->radius);
  396. //! \fixme Arrays don't work either =)
  397. // Mesh geometery, colors, etc
  398. mTombRaider.getMeshVertexArrays(index,
  399. &mesh->vertexCount, &mesh->vertices,
  400. &mesh->normalCount, &mesh->normals,
  401. &mesh->colorCount, &mesh->colors);
  402. // Textured Triangles
  403. count = mTombRaider.getMeshTexturedTriangleCount(index);
  404. mesh->texturedTriangles.reserve(count); // little faster
  405. for (i = 0; i < count; ++i)
  406. {
  407. r_tri = new texture_tri_t;
  408. mTombRaider.getMeshTexturedTriangle(index, i,
  409. r_tri->index,
  410. r_tri->st,
  411. &r_tri->texture,
  412. &r_tri->transparency);
  413. r_tri->texture += mTextureStart;
  414. // Add to face vector
  415. mesh->texturedTriangles.push_back(r_tri);
  416. }
  417. // Coloured Triangles
  418. count = mTombRaider.getMeshColoredTriangleCount(index);
  419. mesh->coloredTriangles.reserve(count); // little faster
  420. for (i = 0; i < count; i++)
  421. {
  422. r_tri = new texture_tri_t;
  423. mTombRaider.getMeshColoredTriangle(index, i,
  424. r_tri->index,
  425. color);
  426. r_tri->st[0] = color[0];
  427. r_tri->st[1] = color[1];
  428. r_tri->st[2] = color[2];
  429. r_tri->st[3] = color[3];
  430. r_tri->st[4] = 1.0;
  431. r_tri->st[5] = 1.0;
  432. #ifdef EXPERIMENTAL
  433. setupTextureColor(r_tri, color);
  434. #else
  435. r_tri->texture = white; // White texture
  436. #endif
  437. r_tri->transparency = 0;
  438. // Add to face vector
  439. mesh->coloredTriangles.push_back(r_tri);
  440. }
  441. // Textured Rectangles
  442. count = mTombRaider.getMeshTexturedRectangleCount(index);
  443. mesh->texturedRectangles.reserve(count*2); // little faster
  444. for (i = 0; i < count; ++i)
  445. {
  446. mTombRaider.getMeshTexturedRectangle(index, i,
  447. vertexIndices,
  448. st,
  449. &texture,
  450. &transparency);
  451. r_tri = new texture_tri_t;
  452. for (j = 0; j < 3; ++j)
  453. r_tri->index[j] = vertexIndices[j];
  454. for (j = 0; j < 6; ++j)
  455. r_tri->st[j] = st[j];
  456. r_tri->texture = texture + mTextureStart;
  457. r_tri->transparency = transparency;
  458. // Add to face vector
  459. mesh->texturedRectangles.push_back(r_tri);
  460. r_tri = new texture_tri_t;
  461. for (j = 3; j < 6; ++j)
  462. r_tri->index[j-3] = vertexIndices[j];
  463. for (j = 6; j < 12; ++j)
  464. r_tri->st[j-6] = st[j];
  465. r_tri->texture = texture + mTextureStart;
  466. r_tri->transparency = transparency;
  467. // Add to face vector
  468. mesh->texturedRectangles.push_back(r_tri);
  469. }
  470. // Coloured Rectangles
  471. count = mTombRaider.getMeshColoredRectangleCount(index);
  472. mesh->coloredRectangles.reserve(count*2); // little faster
  473. for (i = 0; i < count; ++i)
  474. {
  475. mTombRaider.getMeshColoredRectangle(index, i,
  476. vertexIndices,
  477. color);
  478. r_tri = new texture_tri_t;
  479. for (j = 0; j < 3; ++j)
  480. r_tri->index[j] = vertexIndices[j];
  481. //for (j = 0; j < 6; ++j)
  482. // r_tri->st[j] = st[j];
  483. r_tri->st[0] = color[0];
  484. r_tri->st[1] = color[1];
  485. r_tri->st[2] = color[2];
  486. r_tri->st[3] = color[3];
  487. r_tri->st[4] = 1.0;
  488. r_tri->st[5] = 1.0;
  489. #ifdef EXPERIMENTAL
  490. //for (j = 6; j < 12; ++j)
  491. // r_tri->st[j-6] = st[j];
  492. setupTextureColor(r_tri, color);
  493. #else
  494. r_tri->texture = white; // White texture
  495. #endif
  496. r_tri->transparency = 0;
  497. // Add to face vector
  498. mesh->coloredRectangles.push_back(r_tri);
  499. r_tri = new texture_tri_t;
  500. for (j = 3; j < 6; ++j)
  501. r_tri->index[j-3] = vertexIndices[j];
  502. //for (j = 6; j < 12; ++j)
  503. // r_tri->st[j-6] = st[j];
  504. r_tri->st[0] = color[0];
  505. r_tri->st[1] = color[1];
  506. r_tri->st[2] = color[2];
  507. r_tri->st[3] = color[3];
  508. r_tri->st[4] = 1.0;
  509. r_tri->st[5] = 1.0;
  510. #ifdef EXPERIMENTAL
  511. setupTextureColor(r_tri, color);
  512. #else
  513. r_tri->texture = white; // White texture
  514. #endif
  515. r_tri->transparency = 0;
  516. // Add to face vector
  517. mesh->coloredRectangles.push_back(r_tri);
  518. }
  519. // Sort faces by texture
  520. std::sort(mesh->texturedTriangles.begin(), mesh->texturedTriangles.end(), compareFaceTextureId);
  521. std::sort(mesh->coloredTriangles.begin(), mesh->coloredTriangles.end(), compareFaceTextureId);
  522. std::sort(mesh->texturedRectangles.begin(), mesh->texturedRectangles.end(), compareFaceTextureId);
  523. std::sort(mesh->coloredRectangles.begin(), mesh->coloredRectangles.end(), compareFaceTextureId);
  524. getWorld().addMesh(mesh);
  525. }
  526. getConsole().print("Found %d meshes.", mTombRaider.getMeshCount());
  527. }