diff --git a/bin/OpenLara b/bin/OpenLara index 737c3f8..e4dda8f 100755 Binary files a/bin/OpenLara and b/bin/OpenLara differ diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index 3d0e86e..fc8196d 100644 Binary files a/bin/OpenLara.exe and b/bin/OpenLara.exe differ diff --git a/src/camera.h b/src/camera.h index f8b21ee..a62663f 100644 --- a/src/camera.h +++ b/src/camera.h @@ -21,7 +21,7 @@ struct Camera : Controller { int actTargetEntity, actCamera; Camera(TR::Level *level, Lara *owner) : Controller(level, owner ? owner->entity : 0), owner(owner), frustum(new Frustum()), timer(0.0f), actTargetEntity(-1), actCamera(-1) { - fov = 80.0f; + fov = 65.0f; znear = 128; zfar = 100.0f * 1024.0f; angleAdv = vec3(0.0f); @@ -57,8 +57,10 @@ struct Camera : Controller { virtual void update() { int lookAt = -1; - if (owner->target > -1) lookAt = owner->target; if (actTargetEntity > -1) lookAt = actTargetEntity; + if (owner->target > -1) lookAt = owner->target; + + owner->viewTarget = lookAt; if (timer > 0.0f) { timer -= Core::deltaTime; @@ -130,27 +132,27 @@ struct Camera : Controller { if (actCamera <= -1) { TR::Level::FloorInfo info; - level->getFloorInfo(room, (int)pos.x, (int)pos.z, info); + level->getFloorInfo(room, (int)pos.x, (int)pos.y, (int)pos.z, info); int lastRoom = room; if (info.roomNext != 255) room = info.roomNext; - if (pos.y < info.ceiling) { + if (pos.y < info.roomCeiling) { if (info.roomAbove != 255) room = info.roomAbove; else - if (info.ceiling != 0xffff8100) - pos.y = (float)info.ceiling; + if (info.roomCeiling != 0xffff8100) + pos.y = (float)info.roomCeiling; } - if (pos.y > info.floor) { + if (pos.y > info.roomFloor) { if (info.roomBelow != 255) room = info.roomBelow; else - if (info.floor != 0xffff8100) - pos.y = (float)info.floor; + if (info.roomFloor != 0xffff8100) + pos.y = (float)info.roomFloor; } // play underwater sound when camera goes under water diff --git a/src/character.h b/src/character.h index 819c4e4..aa90279 100644 --- a/src/character.h +++ b/src/character.h @@ -41,19 +41,19 @@ struct Character : Controller { virtual void checkRoom() { TR::Level::FloorInfo info; TR::Entity &e = getEntity(); - level->getFloorInfo(e.room, e.x, e.z, info); + level->getFloorInfo(e.room, e.x, e.y, e.z, info); if (info.roomNext != 0xFF) e.room = info.roomNext; - if (info.roomBelow != 0xFF && e.y > info.floor) - e.room = info.roomBelow; + if (info.roomBelow != 0xFF && e.y > info.roomFloor) + e.room = info.roomBelow; - if (info.roomAbove != 0xFF && e.y <= info.ceiling) { + if (info.roomAbove != 0xFF && e.y <= info.roomCeiling) { if (stand == STAND_UNDERWATER && !level->rooms[info.roomAbove].flags.water) { stand = STAND_ONWATER; velocity.y = 0; - pos.y = float(info.ceiling); + pos.y = float(info.roomCeiling); updateEntity(); } else if (stand != STAND_ONWATER) diff --git a/src/controller.h b/src/controller.h index 1e8a074..2dc7e0e 100644 --- a/src/controller.h +++ b/src/controller.h @@ -24,13 +24,14 @@ struct Controller { int mCount; struct ActionCommand { + int emitter; TR::Action action; int value; float timer; ActionCommand *next; ActionCommand() {} - ActionCommand(TR::Action action, int value, float timer, ActionCommand *next = NULL) : action(action), value(value), timer(timer), next(next) {} + ActionCommand(int emitter, TR::Action action, int value, float timer, ActionCommand *next = NULL) : emitter(emitter), action(action), value(value), timer(timer), next(next) {} } *actionCommand; Controller(TR::Level *level, int entity) : level(level), entity(entity), animation(level, getModel()), state(animation.state), actionCommand(NULL), mCount(0), meshes(NULL) { @@ -51,7 +52,9 @@ struct Controller { meshes[i] = model->mStart + i; } - void meshSwap(TR::Model *model, int mask) { + void meshSwap(TR::Model *model, int mask = 0xFFFFFFFF) { + if (!meshes) initMeshOverrides(); + for (int i = 0; i < model->mCount; i++) { int index = model->mStart + i; if (((1 << i) & mask) && level->meshOffsets[index]) @@ -223,10 +226,10 @@ struct Controller { sz = pz / 1024 * 1024 + 512; if (lr != room || lx != sx || lz != sz) { - level->getFloorInfo(room, sx, sz, info); + level->getFloorInfo(room, sx, py, sz, info); if (info.roomNext != 0xFF) { room = info.roomNext; - level->getFloorInfo(room, sx, sz, info); + level->getFloorInfo(room, sx, py, sz, info); } lr = room; lx = sx; @@ -234,9 +237,9 @@ struct Controller { } if (isCamera) { - if (py > info.floor && info.roomBelow != 0xFF) + if (py > info.roomFloor && info.roomBelow != 0xFF) room = info.roomBelow; - else if (py < info.ceiling && info.roomAbove != 0xFF) + else if (py < info.roomCeiling && info.roomAbove != 0xFF) room = info.roomAbove; else if (py > info.floor || py < info.ceiling) { int minX = px / 1024 * 1024; @@ -248,14 +251,14 @@ struct Controller { dir = (pos - from).normal(); } } else { - if (py > info.floor) { + if (py > info.roomFloor) { if (info.roomBelow != 0xFF) room = info.roomBelow; else break; } - if (py < info.ceiling) { + if (py < info.roomCeiling) { if (info.roomAbove != 0xFF) room = info.roomAbove; else @@ -277,45 +280,7 @@ struct Controller { if (rand() % 10 <= 6) return; playSound(TR::SND_BUBBLE, pos, Sound::Flags::PAN); } - /* - void collide() { - TR::Entity &entity = getEntity(); - TR::Level::FloorInfo info; - level->getFloorInfo(entity.room, entity.x, entity.z, info); - - if (info.roomNext != 0xFF) - entity.room = info.roomNext; - - if (entity.y > info.floor) { - if (info.roomBelow == 0xFF) { - if (entity.y > info.floor) { - entity.y = info.floor; - pos.y = entity.y; - velocity.y = 0.0f; - } - } else - entity.room = info.roomBelow; - } - - int height = getHeight(); - if (entity.y - height < info.ceiling) { - if (info.roomAbove == 0xFF) { - pos.y = entity.y = info.ceiling + height; - if (velocity.y < 0.0f) - velocity.y = GRAVITY; - } else { - if (stand == STAND_UNDERWATER && !level->rooms[info.roomAbove].flags.water) { - stand = STAND_ONWATER; - velocity.y = 0; - pos.y = info.ceiling; - } else - if (stand != STAND_ONWATER && entity.y < info.ceiling) - entity.room = info.roomAbove; - } - } - } - */ void activateNext() { // activate next entity (for triggers) if (!actionCommand || !actionCommand->next) { actionCommand = NULL; @@ -480,7 +445,7 @@ struct Controller { if (TR::castShadow(entity.type)) { TR::Level::FloorInfo info; - level->getFloorInfo(entity.room, entity.x, entity.z, info, true); + level->getFloorInfo(entity.room, entity.x, entity.y, entity.z, info); renderShadow(mesh, vec3(float(entity.x), info.floor - 16.0f, float(entity.z)), box.center(), box.size() * 0.8f, angle.y); } } diff --git a/src/core.h b/src/core.h index 21c3592..30c7897 100644 --- a/src/core.h +++ b/src/core.h @@ -18,6 +18,14 @@ #include #include #include +/* + * In compatibility mode, Mac OS X only supports OpenGL 2 (no VAO), but it does + * support the Apple-specific VAO extension which is older and in all relevant + * parts 100% compatible. So use those functions instead. + */ +#define glBindVertexArray glBindVertexArrayAPPLE +#define glGenVertexArrays glGenVertexArraysAPPLE +#define glDeleteVertexArrays glDeleteVertexArraysAPPLE #elif __EMSCRIPTEN__ #define MOBILE 1 #include diff --git a/src/debug.h b/src/debug.h index 3ebb393..e366d25 100644 --- a/src/debug.h +++ b/src/debug.h @@ -184,85 +184,72 @@ namespace Debug { namespace Level { - void debugFloor(const TR::Level &level, int roomIndex, int x, int z) { + void debugFloor(const TR::Level &level, int roomIndex, int x, int y, int z) { TR::Level::FloorInfo info; - level.getFloorInfo(roomIndex, x, z, info); + level.getFloorInfo(roomIndex, x, y, z, info); - vec3 f = vec3(x, info.floor, z); - vec3 c = vec3(x, info.ceiling, z); - vec3 vf[4] = { f, f + vec3(1024, 0, 0), f + vec3(1024, 0, 1024), f + vec3(0, 0, 1024) }; - vec3 vc[4] = { c, c + vec3(1024, 0, 0), c + vec3(1024, 0, 1024), c + vec3(0, 0, 1024) }; + vec3 rf[4], rc[4], f[4], c[4]; - - int sx = 256 * info.slantX; - int sz = 256 * info.slantZ; + int offsets[4][2] = { { 1, 1 }, { 1023, 1 }, { 1023, 1023 }, { 1, 1023 } }; - if (sx > 0) { - vf[0].y += sx; - vf[3].y += sx; - } else { - vf[1].y -= sx; - vf[2].y -= sx; + for (int i = 0; i < 4; i++) { + level.getFloorInfo(roomIndex, x + offsets[i][0], y, z + offsets[i][1], info); + rf[i] = vec3( x + offsets[i][0], info.roomFloor - 4, z + offsets[i][1] ); + rc[i] = vec3( x + offsets[i][0], info.roomCeiling + 4, z + offsets[i][1] ); + f[i] = vec3( x + offsets[i][0], info.floor - 4, z + offsets[i][1] ); + c[i] = vec3( x + offsets[i][0], info.ceiling + 4, z + offsets[i][1] ); + if (info.roomBelow == 0xFF) rf[i].y = f[i].y; + if (info.roomAbove == 0xFF) rc[i].y = c[i].y; } - if (sz > 0) { - vf[0].y += sz; - vf[1].y += sz; - } else { - vf[3].y -= sz; - vf[2].y -= sz; - } - /* - } else { // ceiling - if (sx < 0) { - p[0].y += sx; - p[3].y += sx; - } else { - p[1].y -= sx; - p[2].y -= sx; - } - - if (sz > 0) { - p[0].y -= sz; - p[1].y -= sz; - } else { - p[3].y += sz; - p[2].y += sz; - } - } - */ - - glBegin(GL_LINE_STRIP); - for (int i = 0; i < 5; i++) - glVertex3fv((GLfloat*)&vf[i % 4]); - glEnd(); - - glColor3f(1, 0, 0); - glBegin(GL_LINE_STRIP); - for (int i = 0; i < 5; i++) - glVertex3fv((GLfloat*)&vc[i % 4]); - glEnd(); - if (info.roomNext != 0xFF) { - glColor4f(0.0f, 0.0f, 1.0f, 0.5f); + glColor4f(0.0f, 0.0f, 1.0f, 0.1f); glBegin(GL_QUADS); - for (int i = 3; i >= 0; i--) - glVertex3fv((GLfloat*)&vf[i]); + glVertex3fv((GLfloat*)&f[3]); + glVertex3fv((GLfloat*)&f[2]); + glVertex3fv((GLfloat*)&f[1]); + glVertex3fv((GLfloat*)&f[0]); + glEnd(); + } else { + + glColor4f(0.0f, 1.0f, 0.0f, 0.1f); + glBegin(GL_QUADS); + glVertex3fv((GLfloat*)&f[3]); + glVertex3fv((GLfloat*)&f[2]); + glVertex3fv((GLfloat*)&f[1]); + glVertex3fv((GLfloat*)&f[0]); + glEnd(); + + if (info.trigCmdCount > 0) + glColor4f(1.0f, 1.0f, 1.0f, 0.5f); + else + glColor4f(0.0f, 1.0f, 0.0f, 0.25f); + glBegin(GL_LINE_STRIP); + for (int i = 0; i < 5; i++) + glVertex3fv((GLfloat*)&rf[i % 4]); glEnd(); } + + glColor4f(1.0f, 0.0f, 0.0f, 0.1f); + glBegin(GL_QUADS); + glVertex3fv((GLfloat*)&c[0]); + glVertex3fv((GLfloat*)&c[1]); + glVertex3fv((GLfloat*)&c[2]); + glVertex3fv((GLfloat*)&c[3]); + glEnd(); - if (info.roomAbove != 0xFF) { - glColor4f(1.0f, 0.0f, 0.0f, 1.0f); - glBegin(GL_QUADS); - for (int i = 3; i >= 0; i--) { - vec3 v = vf[i]; - v.y -= 32.0f; - glVertex3fv((GLfloat*)&v); - } - glEnd(); - } + if (info.trigCmdCount > 0) + glColor4f(1.0f, 1.0f, 1.0f, 0.5f); + else + glColor4f(1.0f, 0.0f, 0.0f, 0.25f); + glBegin(GL_LINE_STRIP); + for (int i = 0; i < 5; i++) + glVertex3fv((GLfloat*)&rc[i % 4]); + glEnd(); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + /* if (boxIndex == 0xFFFF) { glBegin(GL_LINES); @@ -292,37 +279,14 @@ namespace Debug { } while (!(o++)->end); } - void debugSectors(const TR::Level &level, const vec3 &pos, int roomIndex) { + void sectors(const TR::Level &level, int roomIndex, int y) { TR::Room &room = level.rooms[roomIndex]; - vec3 p = (pos - vec3(room.info.x, 0, room.info.z)) * vec3(1.0f / 1024.0f, 1, 1.0f / 1024.0f); - - glDisable(GL_DEPTH_TEST); + // glDisable(GL_DEPTH_TEST); for (int z = 0; z < room.zSectors; z++) - for (int x = 0; x < room.xSectors; x++) { - TR::Room::Sector &s = room.sectors[x * room.zSectors + z]; - float floor = s.floor * 256; - /* - if (s.boxIndex < 0xFFFF) { - auto &b = level.boxes[s.boxIndex]; - // floor = b.floor; - } - */ - vec3 f(x * 1024 + room.info.x, floor - 1, z * 1024 + room.info.z); - vec3 c(x * 1024 + room.info.x, s.ceiling * 256 + 1, z * 1024 + room.info.z); - - bool current = (int)p.x == x && (int)p.z == z; - debugFloor(level, roomIndex, room.info.x + x * 1024, room.info.z + z * 1024); - /* - if (current && s.boxIndex != 0xFFFF && level.boxes[s.boxIndex].overlap != 0xFFFF) { - glColor4f(0.0f, 1.0f, 0.0f, 0.25f); - debugBox(level.boxes[s.boxIndex]); - glColor4f(1.0f, 1.0f, 0.0f, 0.25f); - debugOverlaps(level, s.boxIndex); - } - */ - } - glEnable(GL_DEPTH_TEST); + for (int x = 0; x < room.xSectors; x++) + debugFloor(level, roomIndex, room.info.x + x * 1024, y, room.info.z + z * 1024); + // glEnable(GL_DEPTH_TEST); } void rooms(const TR::Level &level, const vec3 &pos, int roomIndex) { @@ -334,7 +298,7 @@ namespace Debug { if (i == roomIndex) { //if (lara->insideRoom(Core::viewPos, i)) { - debugSectors(level, pos, i); + // sectors(level, i); glColor3f(0, 1, 0); } else glColor3f(1, 1, 1); @@ -526,7 +490,7 @@ namespace Debug { Debug::Draw::text(vec2(16, 48), vec4(1.0f), buf); TR::Level::FloorInfo info; - level.getFloorInfo(entity.room, entity.x, entity.z, info); + level.getFloorInfo(entity.room, entity.x, entity.y, entity.z, info); sprintf(buf, "floor = %d, roomBelow = %d, roomAbove = %d, height = %d", info.floorIndex, info.roomBelow, info.roomAbove, info.floor - info.ceiling); Debug::Draw::text(vec2(16, 64), vec4(1.0f), buf); } diff --git a/src/enemy.h b/src/enemy.h index f5d0a5c..158e473 100644 --- a/src/enemy.h +++ b/src/enemy.h @@ -32,7 +32,7 @@ struct Enemy : Character { vec3 p = pos; pos += velocity * Core::deltaTime * 30.0f; TR::Level::FloorInfo info; - level->getFloorInfo(getRoomIndex(), (int)pos.x, (int)pos.z, info, true); + level->getFloorInfo(getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z, info); if (pos.y - info.floor > 1024) { pos = p; return; diff --git a/src/format.h b/src/format.h index 7b26a53..8f79e81 100644 --- a/src/format.h +++ b/src/format.h @@ -9,6 +9,10 @@ namespace TR { + enum { + FLOOR_BLOCK = -127, + }; + enum { ANIM_CMD_NONE , ANIM_CMD_OFFSET , @@ -387,6 +391,7 @@ namespace TR { PUZZLE_4 = 113, // sprite HOLE_PUZZLE = 118, + HOLE_PUZZLE_SET = 122, PICKUP = 126, // sprite @@ -420,7 +425,7 @@ namespace TR { angle rotation; int16 intensity; union { - struct { uint16 unused:7, clear:1, invisible:1, active:5, unused2:1, rendered:1; }; + struct { uint16 unused:7, clear:1, invisible:1, active:5, collision:1, rendered:1; }; uint16 value; } flags; // not exists in file @@ -736,9 +741,10 @@ namespace TR { }; struct FloorInfo { + int roomFloor, roomCeiling; + int roomNext, roomBelow, roomAbove; int floor, ceiling; int slantX, slantZ; - int roomNext, roomBelow, roomAbove; int floorIndex; int kill; int trigCmdCount; @@ -765,6 +771,7 @@ namespace TR { struct { Model *muzzleFlash; + Model *puzzleSet; } extra; Level(const char *name, bool demo) { @@ -898,7 +905,8 @@ namespace TR { memset(&extra, 0, sizeof(extra)); for (int i = 0; i < modelsCount; i++) switch (models[i].type) { - case Entity::MUZZLE_FLASH : extra.muzzleFlash = &models[i]; break; + case Entity::MUZZLE_FLASH : extra.muzzleFlash = &models[i]; break; + case Entity::HOLE_PUZZLE_SET : extra.puzzleSet = &models[i]; break; default : ; } } @@ -1013,15 +1021,17 @@ namespace TR { return room.sectors[sx * room.zSectors + sz]; } - void getFloorInfo(int roomIndex, int x, int z, FloorInfo &info, bool actual = false, int prevRoom = 0xFF) const { + void getFloorInfo(int roomIndex, int x, int y, int z, FloorInfo &info) const { int dx, dz; Room::Sector &s = getSector(roomIndex, x, z, dx, dz); - info.floor = 256 * (int)s.floor; - info.ceiling = 256 * (int)s.ceiling; + info.roomFloor = 256 * s.floor; + info.roomCeiling = 256 * s.ceiling; + info.floor = info.roomFloor; + info.ceiling = info.roomCeiling; info.slantX = 0; info.slantZ = 0; - info.roomNext = 255; + info.roomNext = 0xFF; info.roomBelow = s.roomBelow; info.roomAbove = s.roomAbove; info.floorIndex = s.floorIndex; @@ -1029,23 +1039,63 @@ namespace TR { info.trigger = Trigger::ACTIVATE; info.trigCmdCount = 0; - if (actual) { - if (info.roomBelow != 0xFF && info.roomBelow != prevRoom) { - FloorInfo tmp; - getFloorInfo(info.roomBelow, x, z, tmp, true, roomIndex); - info.floor = tmp.floor; - } + if (s.floor == -127) + return; - if (info.roomAbove != 0xFF && info.roomAbove != prevRoom) { - FloorInfo tmp; - getFloorInfo(info.roomAbove, x, z, tmp, true, roomIndex); - info.ceiling = tmp.ceiling; + Room::Sector *sBelow = &s; + while (sBelow->roomBelow != 0xFF) sBelow = &getSector(sBelow->roomBelow, x, z, dx, dz); + info.floor = 256 * sBelow->floor; + parseFloorData(info, sBelow->floorIndex, dx, dz); + + if (info.roomNext == 0xFF) { + Room::Sector *sAbove = &s; + while (sAbove->roomAbove != 0xFF) sAbove = &getSector(sAbove->roomAbove, x, z, dx, dz); + if (sAbove != sBelow) { + info.ceiling = 256 * sAbove->ceiling; + parseFloorData(info, sAbove->floorIndex, dx, dz); } + } else { + int tmp = info.roomNext; + getFloorInfo(tmp, x, y, z, info); + info.roomNext = tmp; } - if (!s.floorIndex) return; + // entities collide + if (info.trigCmdCount) { + int sx = x / 1024; + int sz = z / 1024; - FloorData *fd = &floors[s.floorIndex]; + for (int i = 0; i < info.trigCmdCount; i++) { + FloorData::TriggerCommand cmd = info.trigCmd[i]; + if (cmd.action != Action::ACTIVATE) continue; + + Entity &e = entities[cmd.args]; + if (!e.flags.collision) continue; + + if (sx != e.x / 1024 || sz != e.z / 1024) continue; + + switch (e.type) { + case Entity::DOOR_FLOOR_1 : + case Entity::DOOR_FLOOR_2 : + case Entity::TRAP_FLOOR : { + int ey = e.y - (e.type == Entity::TRAP_FLOOR ? 512 : 0); + if (ey >= y - 128 && ey < info.floor) + info.floor = ey; + if (ey < y - 128 && ey > info.ceiling) + info.ceiling = ey + (e.type == Entity::TRAP_FLOOR ? 0 : 256); + break; + } + + default : ; + } + } + } + } + + void parseFloorData(FloorInfo &info, int floorIndex, int dx, int dz) const { + if (!floorIndex) return; + + FloorData *fd = &floors[floorIndex]; FloorData::Command cmd; do { @@ -1083,7 +1133,7 @@ namespace TR { ASSERT(info.trigCmdCount < MAX_TRIGGER_COMMANDS); trigCmd = (*fd++).triggerCmd; // trigger action info.trigCmd[info.trigCmdCount++] = trigCmd; - } while (!trigCmd.end); + } while (!trigCmd.end); break; } @@ -1096,10 +1146,9 @@ namespace TR { } while (!cmd.end); - if (actual && info.roomNext != 0xFF) - getFloorInfo(info.roomNext, x, z, info, actual, prevRoom); } + }; // struct Level bool castShadow(Entity::Type type) { diff --git a/src/game.h b/src/game.h index 09ea3f6..76d8449 100644 --- a/src/game.h +++ b/src/game.h @@ -14,7 +14,7 @@ namespace Game { Core::init(); level = new Level("LEVEL2_DEMO.PHD", true, false); //level = new Level("GYM.PHD", false, true); - //level = new Level("LEVEL4.PHD", false, false); + //level = new Level("LEVEL8A.PHD", false, false); #ifndef __EMSCRIPTEN__ //Sound::play(Sound::openWAD("05_Lara's_Themes.wav"), 1, 1, 0); diff --git a/src/lara.h b/src/lara.h index 0f65402..ab75469 100644 --- a/src/lara.h +++ b/src/lara.h @@ -24,6 +24,7 @@ #define PICKUP_FRAME_GROUND 40 #define PICKUP_FRAME_UNDERWATER 16 +#define PUZZLE_FRAME 80 #define MAX_TRIGGER_ACTIONS 64 @@ -193,8 +194,10 @@ struct Lara : Character { Inventory inventory; int lastPickUp; + int viewTarget; - Lara(TR::Level *level, int entity, bool home) : Character(level, entity, 1000), home(home), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos) { + Lara(TR::Level *level, int entity, bool home) : Character(level, entity, 1000), home(home), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), viewTarget(-1) { + animation.setAnim(ANIM_STAND); getEntity().flags.active = 1; initMeshOverrides(); for (int i = 0; i < 2; i++) { @@ -218,12 +221,12 @@ struct Lara : Character { pos = vec3(43182, 2473, 51556); angle = vec3(0.0f, PI * 0.5f, 0.0f); getEntity().room = 12; - */ + // level 2 (pool) pos = vec3(70067, -256, 29104); angle = vec3(0.0f, -0.68f, 0.0f); getEntity().room = 15; - /* + // level 2 (blade) pos = vec3(27221, -1024, 29205); angle = vec3(0.0f, PI * 0.5f, 0.0f); @@ -258,16 +261,27 @@ struct Lara : Character { pos = vec3(70082, -512, 26935); angle = vec3(0.0f, PI * 0.5f, 0.0f); getEntity().room = 15; - + + // level 2 (trap floor) + pos = vec3(31390, -2048, 33472); + angle = vec3(0.0f, 0.0f, 0.0f); + getEntity().room = 63; + */ + // level 2 (trap door) + pos = vec3(21987, -1024, 29144); + angle = vec3(0.0f, PI * 3.0f * 0.5f, 0.0f); + getEntity().room = 61; + /* // level 3a pos = vec3(41015, 3584, 34494); angle = vec3(0.0f, -PI, 0.0f); getEntity().room = 51; - + // level 1 pos = vec3(20215, 6656, 52942); angle = vec3(0.0f, PI, 0.0f); getEntity().room = 14; + */ updateEntity(); #endif @@ -364,6 +378,15 @@ struct Lara : Character { return wpnCurrent == Weapon::EMPTY || arms[0].anim == Weapon::Anim::NONE; } + bool canLookAt() { + return (stand == STAND_GROUND || stand == STAND_SLIDE) + && state != STATE_REACH + && state != STATE_PUSH_BLOCK + && state != STATE_PULL_BLOCK + && state != STATE_PUSH_PULL_READY + && state != STATE_PICK_UP; + } + bool canDrawWeapon() { return wpnCurrent != Weapon::EMPTY && emptyHands() @@ -701,7 +724,8 @@ struct Lara : Character { } else animation.overrideMask &= ~(BODY_ARM_R | BODY_ARM_L); - lookAt(target); + + lookAt(viewTarget); if (wpnCurrent == Weapon::SHOTGUN) aimShotgun(); @@ -713,15 +737,16 @@ struct Lara : Character { float speed = 8.0f * Core::deltaTime; quat rot; + bool can = canLookAt(); // chest - if ((stand == STAND_GROUND || stand == STAND_SLIDE) && aim(target, 7, vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.9f, PI * 0.9f), rot)) + if (can && aim(target, 7, vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.9f, PI * 0.9f), rot)) rotChest = rotChest.slerp(quat(0, 0, 0, 1).slerp(rot, 0.5f), speed); else rotChest = rotChest.slerp(quat(0, 0, 0, 1), speed); animation.overrides[7] = rotChest * animation.overrides[7]; // head - if (aim(target, 14, vec4(-PI * 0.25f, PI * 0.25f, -PI * 0.5f, PI * 0.5f), rot)) + if (can && aim(target, 14, vec4(-PI * 0.25f, PI * 0.25f, -PI * 0.5f, PI * 0.5f), rot)) rotHead = rotHead.slerp(rot, speed); else rotHead = rotHead.slerp(quat(0, 0, 0, 1), speed); @@ -838,8 +863,8 @@ struct Lara : Character { vec3 dst = pos + getDir() * 32.0f; TR::Level::FloorInfo infoCur, infoDst; - level->getFloorInfo(getEntity().room, (int)pos.x, (int)pos.z, infoCur), - level->getFloorInfo(infoCur.roomAbove, (int)dst.x, (int)dst.z, infoDst); + level->getFloorInfo(getEntity().room, (int)pos.x, (int)pos.y, (int)pos.z, infoCur), + level->getFloorInfo(infoCur.roomAbove, (int)dst.x, (int)dst.y, (int)dst.z, infoDst); int h = int(pos.y - infoDst.floor); @@ -907,7 +932,7 @@ struct Lara : Character { TR::Entity &e = getEntity(); TR::Level::FloorInfo info; - level->getFloorInfo(e.room, e.x, e.z, info); + level->getFloorInfo(e.room, e.x, e.y, e.z, info); if (!info.trigCmdCount) return; // has no trigger bool isActive = level->entities[info.trigCmd[0].args].flags.active != 0; @@ -929,15 +954,21 @@ struct Lara : Character { return; break; case TR::Level::Trigger::KEY : - actionState = STATE_USE_KEY; + if (level->entities[info.trigCmd[0].args].flags.active) + return; + actionState = level->entities[info.trigCmd[0].args].type == TR::Entity::HOLE_KEY ? STATE_USE_KEY : STATE_USE_PUZZLE; if (isActive || !isPressed(ACTION) || state == actionState || !emptyHands()) // TODO: STATE_USE_PUZZLE return; if (!checkAngle(level->entities[info.trigCmd[0].args].rotation)) return; - if (animation.canSetState(actionState) && !useItem(TR::Entity::NONE, level->entities[info.trigCmd[0].args].type)) { - playSound(TR::SND_NO, pos, Sound::PAN); + if (animation.canSetState(actionState)) { + if (!useItem(TR::Entity::NONE, level->entities[info.trigCmd[0].args].type)) { + playSound(TR::SND_NO, pos, Sound::PAN); + return; + } + } else return; - } + lastPickUp = info.trigCmd[0].args; // TODO: it's not pickup, it's key/puzzle hole break; case TR::Level::Trigger::PICKUP : if (!isActive) // check if item is not picked up @@ -952,6 +983,9 @@ struct Lara : Character { if (!animation.setState(actionState)) return; if (info.trigger == TR::Level::Trigger::SWITCH || info.trigger == TR::Level::Trigger::KEY) { + if (info.trigger == TR::Level::Trigger::KEY) + level->entities[info.trigCmd[0].args].flags.active = true; + TR::Entity &p = level->entities[info.trigCmd[0].args]; angle.y = p.rotation; angle.x = 0; @@ -976,10 +1010,10 @@ struct Lara : Character { TR::FloorData::TriggerCommand &cmd = info.trigCmd[i]; switch (cmd.action) { case TR::Action::CAMERA_SWITCH : - *actionItem = ActionCommand(cmd.action, cmd.args, (float)info.trigCmd[++i].delay); // camera switch uses next command for delay timer + *actionItem = ActionCommand(entity, cmd.action, cmd.args, (float)info.trigCmd[++i].delay); // camera switch uses next command for delay timer break; default : - *actionItem = ActionCommand(cmd.action, cmd.args, info.trigInfo.timer); + *actionItem = ActionCommand(entity, cmd.action, cmd.args, info.trigInfo.timer); } actionItem->next = (i < info.trigCmdCount - 1) ? actionItem + 1 : NULL; @@ -1027,7 +1061,7 @@ struct Lara : Character { TR::Entity &e = getEntity(); TR::Level::FloorInfo info; - level->getFloorInfo(e.room, e.x, e.z, info, true); + level->getFloorInfo(e.room, e.x, e.y, e.z, info); if (stand == STAND_SLIDE || (stand == STAND_AIR && velocity.y > 0) || stand == STAND_GROUND) { if (e.y + 8 >= info.floor && (abs(info.slantX) > 2 || abs(info.slantZ) > 2)) { @@ -1077,12 +1111,12 @@ struct Lara : Character { TR::Level::FloorInfo info; info.roomAbove = getRoomIndex(); - level->getFloorInfo(info.roomAbove, (int)pos.x, (int)pos.z, info); + level->getFloorInfo(info.roomAbove, (int)pos.x, (int)pos.y, (int)pos.z, info); if (info.roomAbove == 0xFF) info.roomAbove = getRoomIndex(); do { - level->getFloorInfo(info.roomAbove, (int)p.x, (int)p.z, info, true); + level->getFloorInfo(info.roomAbove, (int)p.x, (int)p.y, (int)p.z, info); } while (info.ceiling > p.y - LARA_HANG_OFFSET && info.roomAbove != 0xFF); if (abs(int(info.floor - (p.y - LARA_HANG_OFFSET))) < 16) { @@ -1145,7 +1179,7 @@ struct Lara : Character { if ((input & (FORTH | ACTION)) == (FORTH | ACTION) && (animation.index == ANIM_STAND || animation.index == ANIM_STAND_NORMAL) && emptyHands()) { // TODO: get rid of animation.index vec3 p = pos + getDir() * 64.0f; TR::Level::FloorInfo info; - level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.z, info, true); + level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.y, (int)p.z, info); int h = (int)pos.y - info.floor; int aIndex = animation.index; @@ -1233,7 +1267,7 @@ struct Lara : Character { if (state != STATE_SLIDE && state != STATE_SLIDE_BACK) { TR::Level::FloorInfo info; - level->getFloorInfo(e.room, e.x, e.z, info); + level->getFloorInfo(e.room, e.x, e.y, e.z, info); int sx = abs(info.slantX), sz = abs(info.slantZ); // get direction @@ -1269,7 +1303,7 @@ struct Lara : Character { // possibility check TR::Level::FloorInfo info; vec3 p = pos + getDir() * 128.0f; - level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.z, info, true); + level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.y, (int)p.z, info); if (info.floor - info.ceiling >= 768) return (input & WALK) ? STATE_HANDSTAND : STATE_HANG_UP; } @@ -1358,14 +1392,23 @@ struct Lara : Character { } virtual void doCustomCommand(int curFrame, int prevFrame) { - if (state == STATE_PICK_UP) { - TR::Entity &item = level->entities[lastPickUp]; - if (!item.flags.invisible) { - int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER; - if (animation.isFrameActive(pickupFrame)) { - item.flags.invisible = true; - inventory.add(item.type, 1); + switch (state) { + case STATE_PICK_UP : { + TR::Entity &item = level->entities[lastPickUp]; + if (!item.flags.invisible) { + int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER; + if (animation.isFrameActive(pickupFrame)) { + item.flags.invisible = true; + inventory.add(item.type, 1); + } } + break; + } + case STATE_USE_PUZZLE : { + TR::Entity &item = level->entities[lastPickUp]; + if (animation.isFrameActive(PUZZLE_FRAME)) + ((Controller*)item.controller)->meshSwap(level->extra.puzzleSet); + break; } } } @@ -1464,11 +1507,11 @@ struct Lara : Character { TR::Level::FloorInfo info; if (stand == STAND_HANG) { vec3 p = pos + getDir() * 128.0f; - level->getFloorInfo(e.room, (int)p.x, (int)p.z, info); + level->getFloorInfo(e.room, (int)p.x, (int)p.y, (int)p.z, info); if (info.roomAbove != 0xFF && info.floor >= e.y - LARA_HANG_OFFSET) - level->getFloorInfo(info.roomAbove, (int)p.x, (int)p.z, info); + level->getFloorInfo(info.roomAbove, (int)p.x, (int)p.y, (int)p.z, info); } else - level->getFloorInfo(e.room, e.x, e.z, info); + level->getFloorInfo(e.room, e.x, e.y, e.z, info); vec3 v(sinf(angleExt), 0.0f, cosf(angleExt)); velocity = info.getSlant(v) * speed; @@ -1509,7 +1552,7 @@ struct Lara : Character { TR::Entity &e = getEntity(); TR::Level::FloorInfo info; - level->getFloorInfo(e.room, (int)pos.x, (int)pos.z, info, true); + level->getFloorInfo(e.room, (int)pos.x, (int)pos.y, (int)pos.z, info); // get frame to get height TR::Animation *anim = animation; @@ -1583,7 +1626,7 @@ struct Lara : Character { canPassGap = f >= 220.0f; // check dist to floor if (canPassGap) { // check end of hang layer vec3 d = pos + getDir() * 128.0f; - level->getFloorInfo(info.roomAbove != 0xFF ? info.roomAbove : getRoomIndex(), (int)d.x, (int)d.z, info, true); + level->getFloorInfo(info.roomAbove != 0xFF ? info.roomAbove : getRoomIndex(), (int)d.x, (int)d.y, (int)d.z, info); canPassGap = fabsf((pos.y - LARA_HANG_OFFSET) - info.floor) < 64.0f; } break; diff --git a/src/level.h b/src/level.h index a813abf..3f086e0 100644 --- a/src/level.h +++ b/src/level.h @@ -83,9 +83,15 @@ struct Level { case TR::Entity::DOOR_6 : case TR::Entity::DOOR_BIG_1 : case TR::Entity::DOOR_BIG_2 : + entity.controller = new Door(&level, i); + break; case TR::Entity::DOOR_FLOOR_1 : case TR::Entity::DOOR_FLOOR_2 : + entity.controller = new DoorFloor(&level, i); + break; case TR::Entity::TRAP_FLOOR : + entity.controller = new TrapFloor(&level, i); + break; case TR::Entity::TRAP_BLADE : case TR::Entity::TRAP_SPIKES : entity.controller = new Trigger(&level, i, true); @@ -238,6 +244,8 @@ struct Level { Shader *sh = setRoomShader(room, 1.0f); + Core::lightColor[0] = vec4(0, 0, 0, 1); + sh->bind(); sh->setParam(uColor, Core::color); sh->setParam(uLightColor, Core::lightColor[0], MAX_LIGHTS); @@ -264,12 +272,11 @@ struct Level { rMesh.flags.rendered = true; // set light parameters - getLight(offset, roomIndex); + //getLight(offset, roomIndex); if (rMesh.intensity >= 0) { - Core::ambient = vec3(intensity(rMesh.intensity) / 255.0f); - Core::ambient = vec3(0.0); - sh->setParam(uAmbient, Core::ambient); + // Core::ambient = vec3(intensity(rMesh.intensity) / 255.0f); + sh->setParam(uAmbient, vec3(0.0f));//Core::ambient); } // render static mesh @@ -378,6 +385,7 @@ struct Level { } void renderEntity(const TR::Entity &entity) { + //if (entity.room != lara->getRoomIndex()) return; if (entity.type == TR::Entity::NONE) return; ASSERT(entity.controller); @@ -483,9 +491,10 @@ struct Level { Debug::begin(); // Debug::Level::rooms(level, lara->pos, lara->getEntity().room); // Debug::Level::lights(level); + Debug::Level::sectors(level, lara->getRoomIndex(), (int)lara->pos.y); // Debug::Level::portals(level); // Debug::Level::meshes(level); - // Debug::Level::entities(level); + Debug::Level::entities(level); Debug::Level::info(level, lara->getEntity(), lara->animation); Debug::end(); #endif diff --git a/src/sound.h b/src/sound.h index 761d67c..c9e26c7 100644 --- a/src/sound.h +++ b/src/sound.h @@ -196,10 +196,9 @@ namespace Sound { OGG(Stream *stream, int channels) : Decoder(stream, channels), size(stream->size), pos(0) { buffer = new char[size]; // TODO: file streaming stream->raw(buffer, size); - int error; alloc.alloc_buffer_length_in_bytes = 256 * 1024; alloc.alloc_buffer = new char[alloc.alloc_buffer_length_in_bytes]; - ogg = stb_vorbis_open_memory((unsigned char*)buffer, size, &error, &alloc); + ogg = stb_vorbis_open_memory((unsigned char*)buffer, size, NULL, &alloc); stb_vorbis_info info = stb_vorbis_get_info(ogg); channels = info.channels; } @@ -220,7 +219,10 @@ namespace Sound { return i; } - // TODO: replay + virtual void replay() { + stb_vorbis_close(ogg); + ogg = stb_vorbis_open_memory((unsigned char*)buffer, size, NULL, &alloc); + } }; #endif @@ -321,8 +323,11 @@ namespace Sound { while (i < count) { int res = decoder->decode(&frames[i], count - i); if (res == 0) { - if (i == 0) isPlaying = false; - break; + if (!(flags & Flags::LOOP)) { + if (i == 0) isPlaying = false; + break; + } else + decoder->replay(); } i += res; } diff --git a/src/trigger.h b/src/trigger.h index 3a59de8..7fec58d 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -12,6 +12,7 @@ struct Trigger : Controller { Trigger(TR::Level *level, int entity, bool immediate) : Controller(level, entity), immediate(immediate), timer(0.0f) { baseState = state; + getEntity().flags.collision = false; } bool inState() { @@ -50,7 +51,7 @@ struct Trigger : Controller { } } - if (!inState()) + if (!inState() && entity.type != TR::Entity::HOLE_KEY && entity.type != TR::Entity::HOLE_PUZZLE) animation.setState(state != baseState ? baseState : (entity.type == TR::Entity::TRAP_BLADE ? 2 : (baseState ^ 1))); updateAnimation(true); @@ -73,7 +74,7 @@ struct Dart : Controller { pos = pos + velocity * (Core::deltaTime * 30.0f); updateEntity(); TR::Level::FloorInfo info; - level->getFloorInfo(getRoomIndex(), (int)pos.x, (int)pos.z, info); + level->getFloorInfo(getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z, info); if (pos.y > info.floor || pos.y < info.ceiling || !insideRoom(pos, getRoomIndex())) { if (!inWall) { TR::Entity &e = getEntity(); @@ -144,7 +145,7 @@ struct Block : Controller { void updateFloor(bool rise) { TR::Entity &e = getEntity(); TR::Level::FloorInfo info; - level->getFloorInfo(e.room, e.x, e.z, info); + level->getFloorInfo(e.room, e.x, e.y, e.z, info); if (info.roomNext != 0xFF) e.room = info.roomNext; int dx, dz; @@ -157,7 +158,7 @@ struct Block : Controller { vec3 dir = getDir() * (push ? 1024.0f : -2048.0f); TR::Entity &e = getEntity(); TR::Level::FloorInfo info; - level->getFloorInfo(e.room, e.x + (int)dir.x, e.z + (int)dir.z, info, true); + level->getFloorInfo(e.room, e.x + (int)dir.x, e.y, e.z + (int)dir.z, info); if ((info.slantX | info.slantZ) || info.floor != e.y) return false; if (!animation.setState(push ? STATE_PUSH : STATE_PULL)) @@ -176,4 +177,107 @@ struct Block : Controller { } }; + +struct Door : Trigger { + int8 *floor[2], orig[2]; + + Door(TR::Level *level, int entity) : Trigger(level, entity, true) { + TR::Entity &e = getEntity(); + TR::Level::FloorInfo info; + vec3 p = pos - getDir() * 1024.0f; + + level->getFloorInfo(e.room, (int)p.x, (int)p.y, (int)p.z, info); + int dx, dz; + TR::Room::Sector *s = &level->getSector(e.room, (int)p.x, (int)p.z, dx, dz); + + orig[0] = *(floor[0] = &s->floor); + + if (info.roomNext != 0xFF) { + s = &level->getSector(info.roomNext, e.x, e.z, dx, dz); + orig[1] = *(floor[1] = &s->floor); + } else + floor[1] = NULL; + + updateBlock(); + } + + void updateBlock() { + int8 v[2]; + if (getEntity().flags.active) { + v[0] = orig[0]; + v[1] = orig[1]; + } else + v[0] = v[1] = TR::FLOOR_BLOCK; + + if (floor[0]) *floor[0] = v[0]; + if (floor[1]) *floor[1] = v[1]; + } + + virtual bool activate(ActionCommand *cmd) { + bool res = Trigger::activate(cmd); + updateBlock(); + return res; + } +}; + +struct DoorFloor : Trigger { + + DoorFloor(TR::Level *level, int entity) : Trigger(level, entity, true) { + getEntity().flags.collision = true; + } + + virtual bool activate(ActionCommand *cmd) { + bool res = Trigger::activate(cmd); + getEntity().flags.collision = !getEntity().flags.active; + return res; + } + +}; + + +struct TrapFloor : Trigger { + + enum { + STATE_STATIC, + STATE_SHAKE, + STATE_FALL, + STATE_DOWN, + }; + float velocity; + + TrapFloor(TR::Level *level, int entity) : Trigger(level, entity, true), velocity(0) { + TR::Entity &e = getEntity(); + e.flags.collision = true; + } + + virtual bool activate(ActionCommand *cmd) { + TR::Entity &e = level->entities[cmd->emitter]; + if (e.type != TR::Entity::LARA) return true; + int ey = (int)pos.y - 512; // real floor object position + return (abs(e.y - ey) <= 8) ? Trigger::activate(cmd) : true; + } + + virtual void update() { + Trigger::update(); + if (state == STATE_FALL) { + TR::Entity &e = getEntity(); + e.flags.collision = false; + velocity += GRAVITY * 30 * Core::deltaTime; + pos.y += velocity * Core::deltaTime; + + TR::Level::FloorInfo info; + level->getFloorInfo(e.room, e.x, (int)pos.y, e.z, info); + + if (pos.y > info.roomFloor && info.roomBelow != 0xFF) + e.room = info.roomBelow; + + if (pos.y > info.floor) { + pos.y = (float)info.floor; + animation.setState(STATE_DOWN); + } + updateEntity(); + } + } +}; + #endif \ No newline at end of file