diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index 3d0e86e..a6d13b8 100644 Binary files a/bin/OpenLara.exe and b/bin/OpenLara.exe differ diff --git a/src/camera.h b/src/camera.h index ac12b13..a62663f 100644 --- a/src/camera.h +++ b/src/camera.h @@ -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; diff --git a/src/controller.h b/src/controller.h index b998ed3..2dc7e0e 100644 --- a/src/controller.h +++ b/src/controller.h @@ -52,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]) diff --git a/src/format.h b/src/format.h index 8f769ba..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 @@ -766,6 +771,7 @@ namespace TR { struct { Model *muzzleFlash; + Model *puzzleSet; } extra; Level(const char *name, bool demo) { @@ -899,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 : ; } } @@ -1032,6 +1039,9 @@ namespace TR { info.trigger = Trigger::ACTIVATE; info.trigCmdCount = 0; + if (s.floor == -127) + return; + Room::Sector *sBelow = &s; while (sBelow->roomBelow != 0xFF) sBelow = &getSector(sBelow->roomBelow, x, z, dx, dz); info.floor = 256 * sBelow->floor; @@ -1072,7 +1082,7 @@ namespace TR { 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 ? 256 : 0); + info.ceiling = ey + (e.type == Entity::TRAP_FLOOR ? 0 : 256); break; } diff --git a/src/lara.h b/src/lara.h index 59f248b..f3468eb 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,9 @@ 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(); @@ -264,12 +266,12 @@ struct Lara : Character { 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); @@ -376,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() @@ -713,7 +724,8 @@ struct Lara : Character { } else animation.overrideMask &= ~(BODY_ARM_R | BODY_ARM_L); - lookAt(target); + + lookAt(viewTarget); if (wpnCurrent == Weapon::SHOTGUN) aimShotgun(); @@ -725,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); @@ -941,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 (false) { //!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 @@ -964,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; @@ -1370,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; } } } diff --git a/src/level.h b/src/level.h index b65eb4b..3f086e0 100644 --- a/src/level.h +++ b/src/level.h @@ -385,7 +385,7 @@ struct Level { } void renderEntity(const TR::Entity &entity) { - // if (entity.room != lara->getRoomIndex()) return; + //if (entity.room != lara->getRoomIndex()) return; if (entity.type == TR::Entity::NONE) return; ASSERT(entity.controller); @@ -495,7 +495,7 @@ struct Level { // Debug::Level::portals(level); // Debug::Level::meshes(level); Debug::Level::entities(level); - // Debug::Level::info(level, lara->getEntity(), lara->animation); + 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 71ffb42..7fec58d 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -51,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); @@ -179,8 +179,45 @@ struct Block : Controller { struct Door : Trigger { + int8 *floor[2], orig[2]; - Door(TR::Level *level, int entity) : Trigger(level, entity, true) {} + 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 { @@ -235,7 +272,7 @@ struct TrapFloor : Trigger { e.room = info.roomBelow; if (pos.y > info.floor) { - pos.y = info.floor; + pos.y = (float)info.floor; animation.setState(STATE_DOWN); } updateEntity();