diff --git a/src/controller.h b/src/controller.h index c86167e..acce20f 100644 --- a/src/controller.h +++ b/src/controller.h @@ -49,7 +49,7 @@ struct IGame { virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {} virtual void renderCompose(int roomIndex) {} virtual void renderView(int roomIndex, bool water) {} - virtual void setEffect(TR::Effect effect, float param) {} + virtual void setEffect(Controller *controller, TR::Effect effect) {} virtual void checkTrigger(Controller *controller, bool heavy) {} @@ -520,7 +520,7 @@ struct Controller { if (cmd == TR::ANIM_CMD_EFFECT) { switch (fx) { case TR::Effect::ROTATE_180 : angle.y = angle.y + PI; break; - case TR::Effect::FLOOR_SHAKE : game->setEffect(TR::Effect(fx), 0.5f * max(0.0f, 1.0f - (pos - ((ICamera*)level->cameraController)->pos).length2() / (15 * 1024 * 15 * 1024) )); break; + case TR::Effect::FLOOR_SHAKE : game->setEffect(this, TR::Effect(fx)); break; case TR::Effect::FLIP_MAP : level->isFlipped = !level->isFlipped; break; default : cmdEffect(fx); break; } @@ -698,28 +698,35 @@ struct Controller { if (entity.isActor()) // cutscene entities have no blob shadow return; - Box box = animation.getBoundingBox(pos, 0); - vec3 center = box.center(); + Box boxL = getBoundingBoxLocal(); + Box boxA = boxL * getMatrix(); + + vec3 center = boxA.center(); TR::Level::FloorInfo info; level->getFloorInfo(entity.room, int(center.x), int(center.y), int(center.z), info); - const vec3 fpos = vec3(pos.x, info.floor - 16.0f, pos.z); - const vec3 size = box.size(); + const vec3 size = boxL.size() * (1.0f / 1024.0f); - mat4 m = Core::mViewProj; - m.translate(fpos); - m.rotateY(angle.y); - m.translate(vec3(center.x - pos.x, 0.0f, center.z - pos.z)); - m.scale(vec3(size.x, 0.0f, size.z) * (1.0f / 1024.0f)); + vec3 dir = getDir(); + vec3 up = info.getNormal(); + vec3 right = dir.cross(up).normal(); + dir = up.cross(right).normal(); + + mat4 m; + m.identity(); + m.dir = vec4(dir * size.z, 0.0f); + m.up = vec4(up, 0.0f); + m.right = vec4(right * size.x, 0.0f); + m.offset = vec4(center.x, info.floor - 8.0f, center.z, 1.0f); Basis b; b.identity(); game->setShader(Core::pass, Shader::FLASH, false, false); - Core::active.shader->setParam(uViewProj, m); + Core::active.shader->setParam(uViewProj, Core::mViewProj * m); Core::active.shader->setParam(uBasis, b); - float alpha = lerp(0.7f, 0.90f, clamp((fpos.y - box.max.y) / 1024.0f, 0.0f, 1.0f) ); + float alpha = lerp(0.7f, 0.90f, clamp((info.floor - boxA.max.y) / 1024.0f, 0.0f, 1.0f) ); Core::active.shader->setParam(uMaterial, vec4(vec3(0.5f * (1.0f - alpha)), alpha)); Core::active.shader->setParam(uAmbient, vec3(0.0f)); diff --git a/src/format.h b/src/format.h index 473893d..6be8b0a 100644 --- a/src/format.h +++ b/src/format.h @@ -662,7 +662,8 @@ namespace TR { return isEnemy() || isDoor() || (type == DRAWBRIDGE && flags.active != ACTIVE) || - (type == SCION_HOLDER); + (type == SCION_HOLDER) || + ((type == HAMMER_HANDLE || type == HAMMER_BLOCK) && flags.collision); } bool isItem() const { @@ -2266,6 +2267,13 @@ namespace TR { } break; } + case Entity::HAMMER_HANDLE : { + int dirX, dirZ; + e.getAxis(dirX, dirZ); + if (abs(e.x + dirX * 1024 * 3 - x) < 512 && abs(e.z + dirZ * 1024 * 3 - z) < 512) + info.floor -= 1024 * 3; + break; + } case Entity::BRIDGE_0 : case Entity::BRIDGE_1 : case Entity::BRIDGE_2 : { diff --git a/src/lara.h b/src/lara.h index a4fbc19..4210392 100644 --- a/src/lara.h +++ b/src/lara.h @@ -1366,14 +1366,20 @@ struct Lara : Character { } case TR::HIT_BOULDER : { animation.setAnim(ANIM_DEATH_BOULDER); - if (enemy) + + vec3 v(0.0f); + if (enemy && enemy->getEntity().type == TR::Entity::TRAP_BOULDER) { angle = enemy->angle; - TR::Level::FloorInfo info; - level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info); - vec3 d = getDir(); - vec3 v = info.getSlant(d); - angle.x = -acos(d.dot(v)); - v = enemy ? ((TrapBoulder*)enemy)->velocity * 2.0f : vec3(0.0f); + TR::Level::FloorInfo info; + level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info); + vec3 d = getDir(); + v = info.getSlant(d); + float dp = d.dot(v); + if (fabsf(dp) < 0.999) + angle.x = -acosf(dp); + v = ((TrapBoulder*)enemy)->velocity * 2.0f; + } + for (int i = 0; i < 15; i++) addBlood(256.0f, 512.0f, v); break; @@ -1822,7 +1828,7 @@ struct Lara : Character { if (needFlip) { level->isFlipped = !level->isFlipped; - game->setEffect(effect, 0); + game->setEffect(this, effect); } } @@ -2573,7 +2579,7 @@ struct Lara : Character { } // check enemies & doors - for (int i = 0; i < level->entitiesBaseCount; i++) { + for (int i = 0; i < level->entitiesCount; i++) { TR::Entity &e = level->entities[i]; if (!e.isCollider()) continue; @@ -2581,11 +2587,12 @@ struct Lara : Character { Controller *controller = (Controller*)e.controller; if (e.isEnemy()) { - if (e.type != TR::Entity::ENEMY_REX && (!e.flags.active || ((Character*)controller)->health <= 0)) continue; + if (e.type != TR::Entity::ENEMY_REX && (e.flags.active != TR::ACTIVE || ((Character*)controller)->health <= 0)) continue; } else { // fast distance check for object TR::Entity &entity = getEntity(); - if (abs(entity.x - e.x) > 1024 || abs(entity.z - e.z) > 1024 || abs(entity.y - e.y) > 2048) continue; + if (e.type != TR::Entity::HAMMER_HANDLE && e.type != TR::Entity::HAMMER_BLOCK) + if (abs(entity.x - e.x) > 1024 || abs(entity.z - e.z) > 1024 || abs(entity.y - e.y) > 2048) continue; } vec3 dir = pos - vec3(0.0f, 128.0f, 0.0f) - controller->pos; @@ -2594,7 +2601,7 @@ struct Lara : Character { Box box = controller->getBoundingBoxLocal(); box.expand(vec3(LARA_RADIUS, 0.0f, LARA_RADIUS)); - if (!box.contains(p)) + if (!box.contains(p)) // TODO: Box vs Box or check Lara's head point? (check thor hammer handle) continue; if (e.isEnemy()) { // enemy collision diff --git a/src/level.h b/src/level.h index 9065ade..b1e99eb 100644 --- a/src/level.h +++ b/src/level.h @@ -187,14 +187,14 @@ struct Level : IGame { } } - virtual void setEffect(TR::Effect effect, float param) { + virtual void setEffect(Controller *controller, TR::Effect effect) { this->effect = effect; this->effectTimer = 0.0f; this->effectIdx = 0; switch (effect) { case TR::Effect::FLOOR_SHAKE : - camera->shake = param; + camera->shake = 0.5f * max(0.0f, 1.0f - (controller->pos - camera->pos).length2() / (15 * 1024 * 15 * 1024)); return; case TR::Effect::FLOOD : { Sound::Sample *sample = playSound(TR::SND_FLOOD, vec3(), 0); @@ -476,6 +476,7 @@ struct Level : IGame { case TR::Entity::TRAP_CEILING_2 : return new TrapCeiling(this, index); case TR::Entity::TRAP_SLAM : return new TrapSlam(this, index); case TR::Entity::TRAP_SWORD : return new TrapSword(this, index); + case TR::Entity::HAMMER_HANDLE : return new ThorHammer(this, index); case TR::Entity::DOOR_LATCH : return new DoorLatch(this, index); case TR::Entity::SWITCH : case TR::Entity::SWITCH_WATER : return new Switch(this, index); diff --git a/src/trigger.h b/src/trigger.h index 65efdf1..54ecc05 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -283,7 +283,9 @@ struct Block : Controller { STATE_PULL, }; - Block(IGame *game, int entity) : Controller(game, entity) { + float velocity; + + Block(IGame *game, int entity) : Controller(game, entity), velocity(0.0f) { updateFloor(true); } @@ -339,13 +341,34 @@ struct Block : Controller { } virtual void update() { - if (state == STATE_STAND) return; - updateAnimation(true); - if (state == STATE_STAND) { + TR::Entity &e = getEntity(); + TR::Level::FloorInfo info; + level->getFloorInfo(e.room, e.x, e.y, e.z, info); + + if (pos.y < info.floor) { + if (info.roomBelow != TR::NO_ROOM) + e.room = info.roomBelow; + + velocity += Core::deltaTime * GRAVITY; + pos.y += Core::deltaTime * velocity * 30.0f; + if (pos.y >= info.floor) { + velocity = 0.0f; + pos.y = info.floor; + game->setEffect(this, TR::Effect::FLOOR_SHAKE); + game->playSound(TR::SND_BOULDER, pos, Sound::PAN); + deactivate(true); + updateFloor(true); + } updateEntity(); - updateFloor(true); - deactivate(); - game->checkTrigger(this, true); + } else { + if (state == STATE_STAND) return; + updateAnimation(true); + if (state == STATE_STAND) { + updateEntity(); + updateFloor(true); + deactivate(); + game->checkTrigger(this, true); + } } updateLights(); } @@ -762,6 +785,57 @@ struct TrapSword : Controller { }; +struct ThorHammer : Controller { + enum { + STATE_IDLE, + STATE_START, + STATE_FALL, + STATE_DOWN, + }; + + Controller *block; + + ThorHammer(IGame *game, int entity) : Controller(game, entity) { + TR::Entity &e = getEntity(); + int index = level->entityAdd(TR::Entity::HAMMER_BLOCK, getRoomIndex(), e.x, e.y, e.z, e.rotation, -1); + if (index > -1) { + block = new Controller(game, index); + level->entities[index].controller = block; + } + e.flags.collision = block->getEntity().flags.collision = false; + } + + virtual void update() { + Character *lara = (Character*)game->getLara(); + + switch (state) { + case STATE_IDLE : if (isActive()) animation.setState(STATE_START); break; + case STATE_START : animation.setState(isActive() ? STATE_FALL : STATE_IDLE); break; + case STATE_FALL : { + if (animation.frameIndex > 30 && lara->health > 0.0f) { + vec3 d = pos + getDir() * (3.0f * 1024.0f) - lara->pos; + if (fabsf(d.x) < 520.0f && fabsf(d.z) < 520.0f) + lara->hit(1001, this, TR::HIT_BOULDER); + } + break; + } + case STATE_DOWN : { + game->checkTrigger(this, 1); + if (lara->health > 0.0f) + getEntity().flags.collision = block->getEntity().flags.collision = true; + deactivate(true); + break; + } + } + + updateAnimation(true); + block->animation.frameA = animation.frameA; + block->animation.frameB = animation.frameB; + block->animation.delta = animation.delta; + } +}; + + struct TrapLava : Controller { bool done;