diff --git a/bin/OpenLara.exe b/bin/OpenLara.exe index 8f8991c..3fb7f56 100644 Binary files a/bin/OpenLara.exe and b/bin/OpenLara.exe differ diff --git a/src/camera.h b/src/camera.h index 6141712..32ea547 100644 --- a/src/camera.h +++ b/src/camera.h @@ -225,7 +225,8 @@ struct Camera : Controller { pos = pos.lerp(destPos, min(1.0f, Core::deltaTime * 2.0f)); - FloorInfo info = getFloorInfo((int)pos.x, (int)pos.z); + TR::Level::FloorInfo info; + level->getFloorInfo(room, (int)pos.x, (int)pos.z, info); if (info.roomNext != 255) room = info.roomNext; diff --git a/src/controller.h b/src/controller.h index 44fcd27..ab9ee1e 100644 --- a/src/controller.h +++ b/src/controller.h @@ -26,7 +26,7 @@ struct Controller { WALK = 1 << 6, ACTION = 1 << 7, WEAPON = 1 << 8, - DEATH = 1 << 11 }; + DEATH = 1 << 9 }; float animTime; int animIndex; @@ -41,12 +41,20 @@ struct Controller { float turnTime; - Controller(TR::Level *level, int entity) : level(level), entity(entity), velocity(0.0f), animTime(0.0f), animPrevFrame(0), health(100), turnTime(0.0f) { + struct Action { + TR::Action action; + int value; + + Action(TR::Action action, int value) : action(action), value(value) {} + } nextAction; + + Controller(TR::Level *level, int entity) : level(level), entity(entity), velocity(0.0f), animTime(0.0f), animPrevFrame(0), health(100), turnTime(0.0f), nextAction(TR::Action::NONE, 0) { TR::Entity &e = getEntity(); pos = vec3((float)e.x, (float)e.y, (float)e.z); angle = vec3(0.0f, e.rotation / 16384.0f * PI * 0.5f, 0.0f); stand = STAND_GROUND; animIndex = getModel().animation; + state = level->anims[animIndex].state; } void updateEntity() { @@ -90,29 +98,6 @@ struct Controller { return getEntity().room; } - TR::Room::Sector& getSector(int roomIndex, int x, int z, int &dx, int &dz) const { - ASSERT(roomIndex >= 0 && roomIndex < level->roomsCount); - TR::Room &room = level->rooms[roomIndex]; - - int sx = x - room.info.x; - int sz = z - room.info.z; - - sx = clamp(sx, 0, (room.xSectors - 1) << 10); - sz = clamp(sz, 0, (room.zSectors - 1) << 10); - - dx = sx & 1023; // mod 1024 - dz = sz & 1023; - sx >>= 10; // div 1024 - sz >>= 10; - - return room.sectors[sx * room.zSectors + sz]; - } - - TR::Room::Sector& getSector(int &dx, int &dz) const { - TR::Entity &entity = getEntity(); - return getSector(entity.room, entity.x, entity.z, dx, dz); - } - int setAnimation(int index, int frame = -1) { animIndex = index; TR::Animation &anim = level->anims[animIndex]; @@ -150,7 +135,7 @@ struct Controller { int getOverlap(int fromX, int fromY, int fromZ, int toX, int toZ, int &delta) const { int dx, dz; - TR::Room::Sector &s = getSector(getEntity().room, fromX, fromZ, dx, dz); + TR::Room::Sector &s = level->getSector(getEntity().room, fromX, fromZ, dx, dz); if (s.boxIndex == 0xFFFF) return NO_OVERLAP; @@ -179,108 +164,9 @@ struct Controller { return floor; } - struct FloorInfo { - int floor, ceiling; - int roomNext, roomBelow, roomAbove; - int floorIndex; - }; - - FloorInfo getFloorInfo(int x, int z) { - return getFloorInfo(getRoomIndex(), x, z); - } - - FloorInfo getFloorInfo(int roomIndex, int x, int z) { - int dx, dz; - TR::Room::Sector &s = getSector(roomIndex, x, z, dx, dz); - - FloorInfo info; - info.floor = 256 * (int)s.floor; - info.ceiling = 256 * (int)s.ceiling; - info.roomNext = 255; - info.roomBelow = s.roomBelow; - info.roomAbove = s.roomAbove; - info.floorIndex = s.floorIndex; - - if (!s.floorIndex) return info; - - TR::FloorData *fd = &level->floors[s.floorIndex]; - TR::FloorData::Command cmd; - - do { - cmd = (*fd++).cmd; - - switch (cmd.func) { - - case TR::FD_PORTAL : - info.roomNext = (*fd++).data; - break; - - case TR::FD_FLOOR : // floor & ceiling - case TR::FD_CEILING : { - TR::FloorData::Slant slant = (*fd++).slant; - int sx = (int)slant.x; - int sz = (int)slant.z; - if (cmd.func == TR::FD_FLOOR) { - info.floor -= sx * (sx > 0 ? (dx - 1024) : dx) >> 2; - info.floor -= sz * (sz > 0 ? (dz - 1024) : dz) >> 2; - } else { - info.ceiling -= sx * (sx < 0 ? (dx - 1024) : dx) >> 2; - info.ceiling += sz * (sz > 0 ? (dz - 1024) : dz) >> 2; - } - break; - } - - case TR::FD_TRIGGER : { - TR::FloorData::TriggerInfo info = (*fd++).triggerInfo; - TR::FloorData::TriggerCommand trigCmd; - do { - trigCmd = (*fd++).triggerCmd; // trigger action - switch (trigCmd.func) { - case 0 : - /* - if (getEntity().id == 0 && cmd.sub == 2) { - TR::Entity &e = level->entities[trigCmd.args]; - for (int i = 0; i < level->modelsCount; i++) - if (e.id == level->models[i].id) { - TR::Animation *anim = &level->anims[level->models[i].animation]; - for (int j = 0; j < anim->scCount; j++) - LOG("state: %d\n", anim->state); - break; - } - }*/ - break; // activate item - case 1 : break; // switch to camera - case 2 : break; // camera delay - case 3 : break; // flip map - case 4 : break; // flip on - case 5 : break; // flip off - case 6 : break; // look at item - case 7 : break; // end level - case 8 : break; // play soundtrack - case 9 : break; // special hadrdcode trigger - case 10 : break; // secret found - case 11 : break; // clear bodies - case 12 : break; // flyby camera sequence - case 13 : break; // play cutscene - } - // .. - } while (!trigCmd.end); - break; - } - - case TR::FD_KILL : - health = 0; - break; - - default : LOG("unknown func: %d\n", cmd.func); - } - - } while (!cmd.end); - - return info; - } - void playSound(int id) const { + // LOG("play sound %d\n", id); + int16 a = level->soundsMap[id]; TR::SoundInfo &b = level->soundsInfo[a]; if (b.chance == 0 || (rand() & 0x7fff) <= b.chance) { @@ -314,7 +200,8 @@ struct Controller { void collide() { TR::Entity &entity = getEntity(); - FloorInfo info = getFloorInfo(entity.x, entity.z); + TR::Level::FloorInfo info; + level->getFloorInfo(entity.room, entity.x, entity.z, info); if (info.roomNext != 0xFF) entity.room = info.roomNext; @@ -345,6 +232,16 @@ struct Controller { } } + void activateNext() { // activate next entity (for triggers) + if (nextAction.action == TR::Action::NONE) return; + + Controller *controller = (Controller*)level->entities[nextAction.value].controller; + nextAction.action = TR::Action::NONE; + if (controller) + controller->activate(); + } + + virtual void activate() {} virtual void updateVelocity() {} virtual void move() {} virtual Stand getStand() { return STAND_AIR; } @@ -381,93 +278,97 @@ struct Controller { } virtual void updateBegin() { - animTime += Core::deltaTime; mask = getInputMask(); state = getState(stand = getStand()); } virtual void updateEnd() { - int frameIndex = int(animTime * 30.0f); - TR::Animation *anim = &level->anims[animIndex]; - bool endFrame = frameIndex > anim->frameEnd - anim->frameStart; - - // apply animation commands - int16 *ptr = &level->commands[anim->animCommand]; - - for (int i = 0; i < anim->acCount; i++) { - int cmd = *ptr++; - switch (cmd) { - case TR::ANIM_CMD_MOVE : { // cmd position - int16 sx = *ptr++; - int16 sy = *ptr++; - int16 sz = *ptr++; - if (endFrame) { - pos = pos + vec3(sx, sy, sz).rotateY(angle.y); - updateEntity(); - LOG("move: %d %d %d\n", (int)sx, (int)sy, (int)sz); - } - break; - } - case TR::ANIM_CMD_SPEED : { // cmd jump speed - int16 sy = *ptr++; - int16 sz = *ptr++; - if (endFrame) { - LOG("jump: %d %d\n", (int)sy, (int)sz); - velocity.x = sinf(angleExt) * sz; - velocity.y = sy; - velocity.z = cosf(angleExt) * sz; - stand = STAND_AIR; - } - break; - } - case TR::ANIM_CMD_EMPTY : // empty hands - break; - case TR::ANIM_CMD_KILL : // kill - break; - case TR::ANIM_CMD_SOUND : { // play sound - int frame = (*ptr++); - int id = (*ptr++) & 0x3FFF; - int idx = frame - anim->frameStart; - - if (idx > animPrevFrame && idx <= frameIndex) { - playSound(id); - // LOG("play sound %d\n", getEntity().id); - } - break; - } - case TR::ANIM_CMD_SPECIAL : // special commands - if (frameIndex != animPrevFrame && frameIndex + anim->frameStart == ptr[0]) { - switch (ptr[1]) { - case TR::ANIM_CMD_SPECIAL_FLIP : angle.y = angle.y + PI; break; - case TR::ANIM_CMD_SPECIAL_BUBBLE : /* playSound(TR::SND_BUBBLE); */ break; - case TR::ANIM_CMD_SPECIAL_CTRL : LOG("water out ?\n"); break; - default : LOG("unknown special cmd %d\n", (int)ptr[1]); - } - } - ptr += 2; - break; - default : - LOG("unknown animation command %d\n", cmd); - } - } - - if (endFrame) // if animation is end - switch to next - setAnimation(anim->nextAnimation, anim->nextFrame); - else - animPrevFrame = frameIndex; - - updateVelocity(); move(); collide(); - updateEntity(); } virtual void updateState() {} + + virtual void updateAnimation(bool commands) { + int frameIndex = int((animTime += Core::deltaTime) * 30.0f); + TR::Animation *anim = &level->anims[animIndex]; + bool endFrame = frameIndex > anim->frameEnd - anim->frameStart; + + // apply animation commands + if (commands) { + int16 *ptr = &level->commands[anim->animCommand]; + + for (int i = 0; i < anim->acCount; i++) { + int cmd = *ptr++; + switch (cmd) { + case TR::ANIM_CMD_MOVE : { // cmd position + int16 sx = *ptr++; + int16 sy = *ptr++; + int16 sz = *ptr++; + if (endFrame) { + pos = pos + vec3(sx, sy, sz).rotateY(angle.y); + updateEntity(); + LOG("move: %d %d %d\n", (int)sx, (int)sy, (int)sz); + } + break; + } + case TR::ANIM_CMD_SPEED : { // cmd jump speed + int16 sy = *ptr++; + int16 sz = *ptr++; + if (endFrame) { + LOG("jump: %d %d\n", (int)sy, (int)sz); + velocity.x = sinf(angleExt) * sz; + velocity.y = sy; + velocity.z = cosf(angleExt) * sz; + stand = STAND_AIR; + } + break; + } + case TR::ANIM_CMD_EMPTY : // empty hands + break; + case TR::ANIM_CMD_KILL : // kill + break; + case TR::ANIM_CMD_SOUND : { // play sound + int frame = (*ptr++); + int id = (*ptr++) & 0x3FFF; + int idx = frame - anim->frameStart; + + if (idx > animPrevFrame && idx <= frameIndex) { + if (getEntity().id != ENTITY_ENEMY_BAT) // temporary mute the bat + playSound(id); + } + break; + } + case TR::ANIM_CMD_SPECIAL : // special commands + if (frameIndex != animPrevFrame && frameIndex + anim->frameStart == ptr[0]) { + switch (ptr[1]) { + case TR::ANIM_CMD_SPECIAL_FLIP : angle.y = angle.y + PI; break; + case TR::ANIM_CMD_SPECIAL_BUBBLE : /* playSound(TR::SND_BUBBLE); */ break; + case TR::ANIM_CMD_SPECIAL_CTRL : LOG("water out ?\n"); break; + default : LOG("unknown special cmd %d\n", (int)ptr[1]); + } + } + ptr += 2; + break; + default : + LOG("unknown animation command %d\n", cmd); + } + } + } + + if (endFrame) { // if animation is end - switch to next + setAnimation(anim->nextAnimation, anim->nextFrame); + activateNext(); + } else + animPrevFrame = frameIndex; + } virtual void update() { updateBegin(); updateState(); + updateAnimation(true); + updateVelocity(); updateEnd(); } }; diff --git a/src/debug.h b/src/debug.h index 4d98db8..337a758 100644 --- a/src/debug.h +++ b/src/debug.h @@ -340,7 +340,7 @@ namespace Debug { bool current = (int)p.x == x && (int)p.z == z; debugFloor(level, f, c, s.floorIndex, s.boxIndex, current); - + /* if (current && s.boxIndex != 0xFFFF && level.boxes[s.boxIndex].overlap != 0xFFFF) { glDisable(GL_DEPTH_TEST); glColor4f(0.0f, 1.0f, 0.0f, 0.25f); @@ -349,6 +349,7 @@ namespace Debug { debugOverlaps(level, s.boxIndex); glEnable(GL_DEPTH_TEST); } + */ } } @@ -394,6 +395,16 @@ namespace Debug { Core::setBlending(bmAlpha); } + void entities(const TR::Level &level) { + for (int i = 0; i < level.entitiesCount; i++) { + TR::Entity &e = level.entities[i]; + + char buf[255]; + sprintf(buf, "%d", (int)e.id); + Debug::Draw::text(vec3(e.x, e.y, e.z), vec4(0.8, 0.0, 0.0, 1), buf); + } + } + void lights(const TR::Level &level) { // int roomIndex = level.entities[lara->entity].room; // int lightIndex = getLightIndex(lara->pos, roomIndex); @@ -471,7 +482,7 @@ namespace Debug { int fSize = sizeof(TR::AnimFrame) + m.mCount * sizeof(uint16) * 2; TR::Animation *anim = controller ? &level.anims[controller->animIndex] : &level.anims[m.animation]; - TR::AnimFrame *frame = (TR::AnimFrame*)&level.frameData[anim->frameOffset + (controller ? int((controller->animTime * 30.0f / anim->frameRate)) * fSize : 0) >> 1]; + TR::AnimFrame *frame = (TR::AnimFrame*)&level.frameData[(anim->frameOffset + (controller ? int((controller->animTime * 30.0f / anim->frameRate)) * fSize : 0) >> 1)]; //mat4 m; //m.identity(); @@ -528,6 +539,18 @@ namespace Debug { } } + void info(const TR::Level &level, const TR::Entity &entity) { + char buf[255]; + sprintf(buf, "DIP = %d, TRI = %d", Core::stats.dips, Core::stats.tris); + Debug::Draw::text(vec2(16, 16), vec4(1.0f), buf); + sprintf(buf, "pos = (%d, %d, %d), room = %d", entity.x, entity.y, entity.z, entity.room); + Debug::Draw::text(vec2(16, 32), vec4(1.0f), buf); + + TR::Level::FloorInfo info; + level.getFloorInfo(entity.room, entity.x, 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, 48), vec4(1.0f), buf); + } } } diff --git a/src/format.h b/src/format.h index 5ad186a..ec415ad 100644 --- a/src/format.h +++ b/src/format.h @@ -39,14 +39,46 @@ namespace TR { SND_BUBBLE = 37, }; + enum { + TRIGGER_ACTIVATE = 0, + TRIGGER_PAD = 1, + TRIGGER_SWITCH = 2, + TRIGGER_KEY = 3, + TRIGGER_PICKUP = 4, + TRIGGER_HEAVY = 5, + TRIGGER_ANTIPAD = 6, + TRIGGER_COMBAT = 7, + TRIGGER_DUMMY = 8, + TRIGGER_ANTI = 9 + }; + + enum Action { + NONE = -1, // no action + ACTIVATE = 0, // activate item + CAMERA_SWITCH = 1, // switch to camera + CAMERA_DELAY = 2, // camera delay + FLIP_MAP = 3, // flip map + FLIP_ON = 4, // flip on + FLIP_OFF = 5, // flip off + LOOK_AT = 6, // look at item + END = 7, // end level + SOUNDTRACK = 8, // play soundtrack + HARDCODE = 9, // special hadrdcode trigger + SECRET = 10, // secret found + CLEAR_BODIES = 11, // clear bodies + CAMERA_FLYBY = 12, // flyby camera sequence + CUTSCENE = 13, // play cutscene + }; + + #define DATA_PORTAL 0x01 #define DATA_FLOOR 0x02 #define DATA_CEILING 0x03 - #define ENTITY_FLAG_CLEAR 0x0080 #define ENTITY_FLAG_VISIBLE 0x0100 - #define ENTITY_FLAG_MASK 0x3E00 + #define ENTITY_FLAG_ACTIVE 0x3E00 + #define ENTITY_LARA 0 #define ENTITY_LARA_CUT 77 @@ -71,25 +103,41 @@ namespace TR { #define ENTITY_ENEMY_MUMMY 24 #define ENTITY_ENEMY_LARSON 27 - #define ENTITY_CRYSTAL 83 + #define ENTITY_BLADE 36 - #define ENTITY_MEDIKIT_SMALL 93 - #define ENTITY_MEDIKIT_BIG 94 + #define ENTITY_CRYSTAL 83 - #define ENTITY_VIEW_TARGET 169 + #define ENTITY_MEDIKIT_SMALL 93 + #define ENTITY_MEDIKIT_BIG 94 - #define ENTITY_TRAP_FLOOR 35 - #define ENTITY_TRAP_SPIKES 37 - #define ENTITY_TRAP_STONE 38 - #define ENTITY_TRAP_DART 40 + #define ENTITY_TRAP_FLOOR 35 + #define ENTITY_TRAP_SPIKES 37 + #define ENTITY_TRAP_STONE 38 + #define ENTITY_TRAP_DART 40 - #define ENTITY_SWITCH 55 + #define ENTITY_SWITCH 55 + #define ENTITY_SWITCH_WATER 56 - #define ENTITY_GUN_SHOTGUN 85 + #define ENTITY_DOOR_1 57 + #define ENTITY_DOOR_2 58 + #define ENTITY_DOOR_3 59 + #define ENTITY_DOOR_4 60 + #define ENTITY_DOOR_BIG_1 61 + #define ENTITY_DOOR_BIG_2 62 + #define ENTITY_DOOR_5 63 + #define ENTITY_DOOR_6 64 + #define ENTITY_DOOR_FLOOR_1 65 + #define ENTITY_DOOR_FLOOR_2 66 - #define ENTITY_AMMO_UZI 91 - #define ENTITY_AMMO_SHOTGUN 89 - #define ENTITY_AMMO_MAGNUM 90 + #define ENTITY_GUN_SHOTGUN 85 + + #define ENTITY_AMMO_UZI 91 + #define ENTITY_AMMO_SHOTGUN 89 + #define ENTITY_AMMO_MAGNUM 90 + + #define ENTITY_HOLE_PUZZLE 118 + #define ENTITY_HOLE_KEY 137 + #define ENTITY_VIEW_TARGET 169 #pragma pack(push, 1) @@ -306,8 +354,8 @@ namespace TR { }; struct AnimFrame { - MinMax box; - TR::Vertex pos; // Starting offset for this model + MinMax box; + Vertex pos; // Starting offset for this model int16 aCount; uint16 angles[0]; // angle frames in YXZ order @@ -579,7 +627,7 @@ namespace TR { // lights r.lights = new Room::Light[stream.read(r.lightsCount)]; for (int i = 0; i < r.lightsCount; i++) { - TR::Room::Light &light = r.lights[i]; + Room::Light &light = r.lights[i]; stream.read(light.x); stream.read(light.y); stream.read(light.z); @@ -711,13 +759,127 @@ namespace TR { delete[] soundOffsets; } - TR::StaticMesh* getMeshByID(int id) const { // TODO: map this + // common methods + + StaticMesh* getMeshByID(int id) const { // TODO: map this for (int i = 0; i < staticMeshesCount; i++) if (staticMeshes[i].id == id) return &staticMeshes[i]; return NULL; } - }; + + struct FloorInfo { + int floor, ceiling; + int roomNext, roomBelow, roomAbove; + int floorIndex; + bool kill; + int trigger; + FloorData::TriggerInfo trigInfo; + FloorData::TriggerCommand trigCmd[16]; + int trigCmdCount; + }; + + Room::Sector& getSector(int roomIndex, int x, int z, int &dx, int &dz) const { + ASSERT(roomIndex >= 0 && roomIndex < roomsCount); + Room &room = rooms[roomIndex]; + + int sx = x - room.info.x; + int sz = z - room.info.z; + + sx = clamp(sx, 0, (room.xSectors - 1) * 1024); + sz = clamp(sz, 0, (room.zSectors - 1) * 1024); + + dx = sx % 1024; + dz = sz % 1024; + sx /= 1024; + sz /= 1024; + + return room.sectors[sx * room.zSectors + sz]; + } + + void getFloorInfo(int roomIndex, int x, 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.roomNext = 255; + info.roomBelow = s.roomBelow; + info.roomAbove = s.roomAbove; + info.floorIndex = s.floorIndex; + info.kill = false; + info.trigger = -1; + info.trigCmdCount = 0; + + if (!s.floorIndex) return; + + FloorData *fd = &floors[s.floorIndex]; + FloorData::Command cmd; + + do { + cmd = (*fd++).cmd; + + switch (cmd.func) { + + case FD_PORTAL : + info.roomNext = (*fd++).data; + break; + + case FD_FLOOR : // floor & ceiling + case FD_CEILING : { + FloorData::Slant slant = (*fd++).slant; + int sx = (int)slant.x; + int sz = (int)slant.z; + if (cmd.func == FD_FLOOR) { + info.floor -= sx * (sx > 0 ? (dx - 1024) : dx) >> 2; + info.floor -= sz * (sz > 0 ? (dz - 1024) : dz) >> 2; + } else { + info.ceiling -= sx * (sx < 0 ? (dx - 1024) : dx) >> 2; + info.ceiling += sz * (sz > 0 ? (dz - 1024) : dz) >> 2; + } + break; + } + + case FD_TRIGGER : { + info.trigger = cmd.sub; + info.trigCmdCount = 0; + info.trigInfo = (*fd++).triggerInfo; + FloorData::TriggerCommand trigCmd; + do { + trigCmd = (*fd++).triggerCmd; // trigger action + info.trigCmd[info.trigCmdCount++] = trigCmd; + switch (trigCmd.func) { + case 0 : break; // activate item + case 1 : break; // switch to camera + case 2 : break; // camera delay + case 3 : break; // flip map + case 4 : break; // flip on + case 5 : break; // flip off + case 6 : break; // look at item + case 7 : break; // end level + case 8 : break; // play soundtrack + case 9 : break; // special hadrdcode trigger + case 10 : break; // secret found + case 11 : break; // clear bodies + case 12 : break; // flyby camera sequence + case 13 : break; // play cutscene + } + // .. + } while (!trigCmd.end); + break; + } + + case FD_KILL : + info.kill = true; + break; + + default : LOG("unknown func: %d\n", cmd.func); + } + + } while (!cmd.end); + } + + }; // struct Level } #endif \ No newline at end of file diff --git a/src/lara.h b/src/lara.h index d999853..f87403f 100644 --- a/src/lara.h +++ b/src/lara.h @@ -99,7 +99,7 @@ struct Lara : Controller { // level 2 (pool) pos = vec3(70067, -256, 29104); angle = vec3(0.0f, -0.68f, 0.0f); - getEntity().room = 15; + getEntity().room = 15; */ /* // level 2 (wolf) @@ -129,8 +129,9 @@ struct Lara : Controller { bool waterOut(int &outState) { vec3 dst = pos + getDir() * 32.0f; - FloorInfo infoCur = getFloorInfo((int)pos.x, (int)pos.z), - infoDst = getFloorInfo(infoCur.roomAbove, (int)dst.x, (int)dst.z); + 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); if (infoDst.roomBelow == 0xFF && pos.y - infoDst.floor <= 256) { // possibility check if (!setState(STATE_STOP)) { // can't set water out state @@ -151,6 +152,77 @@ struct Lara : Controller { return false; } + void performTrigger() { + TR::Entity &e = getEntity(); + TR::Level::FloorInfo info; + level->getFloorInfo(e.room, e.x, e.z, info); + + if (!info.trigCmdCount) return; // has no trigger + bool isActive = (level->entities[info.trigCmd[0].args].flags & ENTITY_FLAG_ACTIVE); + if (info.trigInfo.once == 1 && (level->entities[info.trigCmd[0].args].flags & ENTITY_FLAG_ACTIVE)) return; // once trigger is already activated + + int actionState = 0; + switch (info.trigger) { + case TR::TRIGGER_ACTIVATE : + if (isActive) return; + actionState = state; + break; + case TR::TRIGGER_PAD : + if (stand != STAND_GROUND || isActive) return; + actionState = state; + break; + case TR::TRIGGER_SWITCH : + if (mask & ACTION) { + actionState = isActive ? STATE_SWITCH_OFF : STATE_SWITCH_ON; + } else + return; + break; + case TR::TRIGGER_KEY : + if (isActive) return; + if (mask & ACTION) + // if (entity.id == ENTITY_PUZZLE_1) + // actionState = STATE_USE_PUZZLE; + // else + actionState = STATE_USE_KEY; + else + return; + break; + case TR::TRIGGER_PICKUP : + if ((mask & ACTION) && stand == STAND_GROUND) + actionState = STATE_PICK_UP; + else + return; + break; + default : + return; + } + + // try to activate Lara state + if (!setState(actionState)) return; + + // build trigger activation chain + for (int i = 0; i < info.trigCmdCount; i++) { + if (info.trigCmd[i].func != TR::Action::ACTIVATE) continue; // TODO: other trigger types + Controller *controller = (Controller*)level->entities[info.trigCmd[i].args].controller; + if (!controller) { + LOG("! next activation entity %d has no controller\n", level->entities[info.trigCmd[i].args].id); + playSound(2); + return; + } else + controller->nextAction = (i < info.trigCmdCount - 1) ? Action(TR::Action::ACTIVATE, info.trigCmd[i + 1].args) : Action(TR::Action::NONE, 0); + } + + if (info.trigCmd[0].func != TR::Action::ACTIVATE) return; // see above TODO + + // activate first entity in chain + Controller *controller = (Controller*)level->entities[info.trigCmd[0].args].controller; + if (info.trigger == TR::TRIGGER_KEY) { + nextAction = controller->nextAction; + controller->nextAction.action = TR::Action::NONE; + } else + controller->activate(); + } + virtual Stand getStand() { if (stand == STAND_ONWATER && state != STATE_DIVE && state != STATE_STOP) return stand; @@ -161,7 +233,8 @@ struct Lara : Controller { int extra = isMovingState(state) ? 256 : 0; TR::Entity &e = getEntity(); - FloorInfo info = getFloorInfo(e.x, e.z); + TR::Level::FloorInfo info; + level->getFloorInfo(e.room, e.x, e.z, info); if (info.roomBelow == 0xFF && e.y + extra >= info.floor) return STAND_GROUND; @@ -235,7 +308,7 @@ struct Lara : Controller { if (mask & FORTH) return STATE_RUN; if (mask & BACK) return STATE_FAST_BACK; if (mask & LEFT) return turnTime < FAST_TURN_TIME ? STATE_TURN_LEFT : STATE_FAST_TURN; - if (mask & RIGHT) return turnTime < FAST_TURN_TIME ? STATE_TURN_RIGHT : STATE_FAST_TURN; + if (mask & RIGHT) return turnTime < FAST_TURN_TIME ? STATE_TURN_RIGHT : STATE_FAST_TURN; return STATE_STOP; } @@ -249,6 +322,7 @@ struct Lara : Controller { } if (mask & JUMP) return STATE_SWIM; + return (state == STATE_SWIM || velocity.y > GLIDE_SPEED) ? STATE_GLIDE : STATE_TREAD; } @@ -306,6 +380,8 @@ struct Lara : Controller { } virtual void updateState() { + performTrigger(); + TR::Animation *anim = &level->anims[animIndex]; int fCount = anim->frameEnd - anim->frameStart; @@ -340,7 +416,6 @@ struct Lara : Controller { } else lState = false; #endif - // calculate turn tilt if (state == STATE_RUN && (mask & (LEFT | RIGHT))) { if (mask & LEFT) angle.z -= Core::deltaTime * TURN_TILT; @@ -401,7 +476,7 @@ struct Lara : Controller { case STATE_SURF_RIGHT : angleExt += PI * 0.5f; break; - } + } } virtual void updateVelocity() { @@ -467,7 +542,8 @@ struct Lara : Controller { vec3 p = pos; pos = pos + offset; - FloorInfo info = getFloorInfo((int)pos.x, (int)pos.z); + TR::Level::FloorInfo info; + level->getFloorInfo(getEntity().room, (int)pos.x, (int)pos.z, info); int delta; int d = getOverlap((int)p.x, (int)p.y, (int)p.z, (int)pos.x, (int)pos.z, delta); @@ -518,7 +594,7 @@ struct Lara : Controller { case STAND_ONWATER : break; } - } else + } else updateEntity(); } }; diff --git a/src/level.h b/src/level.h index 578cc2c..68236ad 100644 --- a/src/level.h +++ b/src/level.h @@ -7,6 +7,7 @@ #include "lara.h" #include "enemy.h" #include "camera.h" +#include "trigger.h" #ifdef _DEBUG #include "debug.h" @@ -72,6 +73,25 @@ struct Level { case ENTITY_ENEMY_LARSON : entity.controller = new Enemy(&level, i); break; + case ENTITY_DOOR_1 : + case ENTITY_DOOR_2 : + case ENTITY_DOOR_3 : + case ENTITY_DOOR_4 : + case ENTITY_DOOR_5 : + case ENTITY_DOOR_6 : + case ENTITY_DOOR_BIG_1 : + case ENTITY_DOOR_BIG_2 : + case ENTITY_DOOR_FLOOR_1 : + case ENTITY_DOOR_FLOOR_2 : + case ENTITY_BLADE : + entity.controller = new Trigger(&level, i, true); + break; + case ENTITY_SWITCH : + case ENTITY_SWITCH_WATER : + case ENTITY_HOLE_PUZZLE : + case ENTITY_HOLE_KEY : + entity.controller = new Trigger(&level, i, false); + break; } } @@ -360,7 +380,7 @@ struct Level { if (fIndexB == 0) { nextAnim = &level.anims[anim->nextAnimation]; - fIndexB = (anim->nextFrame - nextAnim->frameStart) / anim->frameRate; + fIndexB = (anim->nextFrame - nextAnim->frameStart) / nextAnim->frameRate; } else nextAnim = anim; @@ -553,10 +573,12 @@ struct Level { #ifdef _DEBUG Debug::begin(); - // Debug::Level::rooms(level, lara->pos, lara->getEntity().room); + Debug::Level::rooms(level, lara->pos, lara->getEntity().room); // Debug::Level::lights(level); // Debug::Level::portals(level); - Debug::Level::meshes(level); + // Debug::Level::meshes(level); + Debug::Level::entities(level); + Debug::Level::info(level, lara->getEntity()); Debug::end(); #endif } diff --git a/src/utils.h b/src/utils.h index 58edb44..f5e8adc 100644 --- a/src/utils.h +++ b/src/utils.h @@ -19,8 +19,8 @@ #else #define ASSERT(expr) -// #define LOG(...) ((void)0) - #define LOG(...) printf(__VA_ARGS__) + #define LOG(...) ((void)0) +// #define LOG(...) printf(__VA_ARGS__) #endif diff --git a/src/win/OpenLara.vcxproj b/src/win/OpenLara.vcxproj index 0e72818..22d45ab 100644 --- a/src/win/OpenLara.vcxproj +++ b/src/win/OpenLara.vcxproj @@ -109,6 +109,7 @@ +