#include "framework.h" #include "lara.h" #include "lara_basic.h" #include "lara_tests.h" #include "lara_collide.h" #include "lara_slide.h" #include "lara_monkey.h" #include "lara_helpers.h" #include "input.h" #include "level.h" #include "setup.h" #include "health.h" #include "Sound/sound.h" #include "animation.h" #include "pickup.h" #include "collide.h" #include "items.h" #include "camera.h" // ------------------------------ // BASIC MOVEMENT & MISCELLANEOUS // Control & Collision Functions // ------------------------------ // -------------- // MISCELLANEOUS: // -------------- void lara_void_func(ITEM_INFO* item, COLL_INFO* coll) { return; } void lara_default_col(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->moveAngle = item->pos.yRot; coll->Setup.BadHeightDown = STEPUP_HEIGHT; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = 0; coll->Setup.SlopesArePits = true; coll->Setup.SlopesAreWalls = true; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); LaraResetGravityStatus(item, coll); } void lara_as_special(ITEM_INFO* item, COLL_INFO* coll) { Camera.flags = CF_FOLLOW_CENTER; Camera.targetAngle = ANGLE(170.0f); Camera.targetElevation = -ANGLE(25.0f); } void lara_as_null(ITEM_INFO* item, COLL_INFO* coll) { coll->Setup.EnableObjectPush = false; coll->Setup.EnableSpaz = false; } void lara_as_controlled(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = false; coll->Setup.EnableObjectPush = false; coll->Setup.EnableSpaz = false; if (item->frameNumber == TestLastFrame(item, item->animNumber) - 1) { info->gunStatus = LG_NO_ARMS; if (UseForcedFixedCamera) UseForcedFixedCamera = 0; } } void lara_as_controlledl(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = false; coll->Setup.EnableObjectPush = false; coll->Setup.EnableSpaz = false; } // --------------- // BASIC MOVEMENT: // --------------- // State: LS_WALK_FORWARD (0) // Collision: lara_col_walk() void lara_as_walk(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; if (item->hitPoints <= 0) { item->goalAnimState = LS_DEATH; return; } // TODO: Implement item alignment properly. @Sezz 2021.11.01 if (info->isMoving) return; // TODO: If stopping and holding WALK without FORWARD, Lara can't turn. @Sezz 2021.10.09 if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_SLOW_TURN) info->turnRate = -LARA_SLOW_TURN; DoLaraLean(item, coll, -LARA_LEAN_MAX / 3, 10); } else if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_SLOW_TURN) info->turnRate = LARA_SLOW_TURN; DoLaraLean(item, coll, LARA_LEAN_MAX / 3, 10); } if (TrInput & IN_FORWARD) { if (info->waterStatus == LW_WADE) item->goalAnimState = LS_WADE_FORWARD; else if (TrInput & IN_WALK) [[likely]] item->goalAnimState = LS_WALK_FORWARD; else item->goalAnimState = LS_RUN_FORWARD; return; } item->goalAnimState = LS_STOP; } // State: LA_WALK_FORWARD (0) // Control: lara_as_walk_forward() void lara_col_walk(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->moveAngle = item->pos.yRot; item->gravityStatus = false; item->fallspeed = 0; coll->Setup.BadHeightDown = STEPUP_HEIGHT; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = 0; coll->Setup.SlopesAreWalls = true; coll->Setup.SlopesArePits = true; coll->Setup.DeathFlagIsPit = true; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); if (TestLaraHitCeiling(coll)) { SetLaraHitCeiling(item, coll); return; } if (LaraDeflectEdge(item, coll)) { item->goalAnimState = LS_SPLAT; if (GetChange(item, &g_Level.Anims[item->animNumber])) return; LaraCollideStop(item, coll); } if (TestLaraFall(coll)) { SetLaraFallState(item); return; } if (TestLaraSlide(item, coll)) return; if (TestLaraVault(item, coll)) return; if (TestLaraStep(coll) && coll->CollisionType != CT_FRONT) { DoLaraStep(item, coll); return; } } // State: LS_RUN_FORWARD (1) // Collision: lara_col_run() void lara_as_run(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; if (item->hitPoints <= 0) { item->goalAnimState = LS_DEATH; return; } if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_FAST_TURN) info->turnRate = -LARA_FAST_TURN; DoLaraLean(item, coll, -LARA_LEAN_MAX, 7); } else if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_FAST_TURN) info->turnRate = LARA_FAST_TURN; DoLaraLean(item, coll, LARA_LEAN_MAX, 7); } static bool allowJump = false; if (item->animNumber == LA_STAND_TO_RUN) allowJump = false; else if (item->animNumber == LA_RUN) { if (item->frameNumber == 4) allowJump = true; } else allowJump = true; // TODO: Do something about wade checks. @Sezz 2021.10.17 // Pseudo action queue which makes JUMP input take complete precedence. // Creates a committal lock to perform a forward jump when JUMP is pressed and released while allowJump isn't true yet. static bool commitToJump = false; if ((TrInput & IN_JUMP || commitToJump) && !item->gravityStatus && info->waterStatus != LW_WADE) { commitToJump = TrInput & IN_FORWARD; if (allowJump) { commitToJump = false; item->goalAnimState = LS_JUMP_FORWARD; } return; } if (TrInput & IN_SPRINT && info->sprintTimer && info->waterStatus != LW_WADE) { item->goalAnimState = LS_SPRINT; return; } if (TrInput & IN_ROLL && info->waterStatus != LW_WADE) { item->goalAnimState = LS_ROLL_FORWARD; return; } if (TrInput & IN_DUCK && (info->gunStatus == LG_NO_ARMS || !IsStandingWeapon(info->gunType)) && info->waterStatus != LW_WADE) { item->goalAnimState = LS_CROUCH_IDLE; return; } if (TrInput & IN_FORWARD) { if (info->waterStatus == LW_WADE) item->goalAnimState = LS_WADE_FORWARD; else if (TrInput & IN_WALK) item->goalAnimState = LS_WALK_FORWARD; else [[likely]] item->goalAnimState = LS_RUN_FORWARD; return; } item->goalAnimState = LS_STOP; } // State: LS_RUN_FORWARD (1) // Control: lara_as_run() void lara_col_run(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->moveAngle = item->pos.yRot; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = 0; coll->Setup.SlopesAreWalls = true; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); LaraResetGravityStatus(item, coll); if (TestLaraHitCeiling(coll)) { SetLaraHitCeiling(item, coll); return; } if (LaraDeflectEdge(item, coll)) { item->pos.zRot = 0; if (coll->HitTallObject || TestLaraWall(item, 256, 0, -640) != SPLAT_COLL::NONE) { item->goalAnimState = LS_SPLAT; if (GetChange(item, &g_Level.Anims[item->animNumber])) { item->currentAnimState = LS_SPLAT; return; } } LaraCollideStop(item, coll); } if (TestLaraFall(coll)) { SetLaraFallState(item); return; } if (TestLaraSlide(item, coll)) return; if (TestLaraVault(item, coll)) return; if (TestLaraStep(coll) && coll->CollisionType != CT_FRONT) { DoLaraStep(item, coll); return; } // LEGACY step //if (coll->Front.Floor == NO_HEIGHT || coll->Front.Floor < -STEPUP_HEIGHT || coll->Front.Floor >= -STEP_SIZE / 2) //{ // coll->Middle.Floor = 0; //} //else //{ // item->goalAnimState = LS_STEP_UP; // GetChange(item, &g_Level.Anims[item->animNumber]); //} //if (coll->Middle.Floor < 50) //{ // if (coll->Middle.Floor != NO_HEIGHT) // item->pos.yPos += coll->Middle.Floor; //} //else //{ // item->goalAnimState = LS_STEP_DOWN; // for theoretical running stepdown anims, not in default anims // if (GetChange(item, &g_Level.Anims[item->animNumber])) // item->pos.yPos += coll->Middle.Floor; // move Lara to middle.Floor // else // item->pos.yPos += 50; // do the default aligment //} } // State: LS_STOP (2) // Collision: lara_col_stop() void lara_as_stop(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = ((TestLaraSwamp(item) && info->waterStatus == LW_WADE) || item->animNumber == LA_SWANDIVE_ROLL) ? false : true; // TODO: Hardcoding. @Sezz 2021.09.28 if (item->animNumber != LA_SPRINT_TO_STAND_RIGHT && item->animNumber != LA_SPRINT_TO_STAND_LEFT) StopSoundEffect(SFX_TR4_LARA_SLIPPING); if (item->hitPoints <= 0) { item->goalAnimState = LS_DEATH; return; } // TODO: Hardcoding. @Sezz 2021.09.28 // Handles waterskin and clockwork beetle. if (UseSpecialItem(item)) return; if (TrInput & IN_LOOK && info->look) LookUpDown(); if (TrInput & IN_LEFT && !(TrInput & IN_JUMP)) // JUMP locks y rotation. { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_SLOW_TURN) info->turnRate = -LARA_SLOW_TURN; } else if (TrInput & IN_RIGHT && !(TrInput & IN_JUMP)) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_SLOW_TURN) info->turnRate = LARA_SLOW_TURN; } // TODO: Refine this handling. Create LS_WADE_IDLE state? Might complicate things. @Sezz 2021.09.28 if (info->waterStatus == LW_WADE) { if (TestLaraSwamp(item)) LaraSwampStop(item, coll); else [[likely]] LaraWadeStop(item, coll); return; } if (TrInput & IN_JUMP && coll->Middle.Ceiling < -LARA_HEADROOM * 0.7f) { item->goalAnimState = LS_JUMP_PREPARE; return; } if (TrInput & IN_ROLL) { item->goalAnimState = LS_ROLL_FORWARD; return; } if (TrInput & IN_DUCK && (info->gunStatus == LG_NO_ARMS || !IsStandingWeapon(info->gunType))) { item->goalAnimState = LS_CROUCH_IDLE; return; } if (TrInput & IN_FORWARD) { if (TrInput & IN_WALK) { if (TestLaraWalkForward(item, coll)) { item->goalAnimState = LS_WALK_FORWARD; return; } } else if (TrInput & IN_SPRINT && TestLaraRunForward(item, coll)) { item->goalAnimState = LS_SPRINT; return; } else if (TestLaraRunForward(item, coll)) [[likely]] { item->goalAnimState = LS_RUN_FORWARD; return; } } // TODO: Create new LS_WADE_BACK state? Its function would make a direct call to lara_as_back(). @Sezz 2021.06.27 else if (TrInput & IN_BACK) { if (TrInput & IN_WALK) { if (TestLaraWalkBack(item, coll)) { item->goalAnimState = LS_WALK_BACK; return; } } else if (TestLaraHopBack(item, coll)) [[likely]] { item->goalAnimState = LS_HOP_BACK; return; } } if (TrInput & IN_LSTEP && TestLaraStepLeft(item, coll)) { item->goalAnimState = LS_STEP_LEFT; return; } else if (TrInput & IN_RSTEP && TestLaraStepRight(item, coll)) { item->goalAnimState = LS_STEP_RIGHT; return; } if (TrInput & IN_LEFT) { if (TrInput & IN_SPRINT || info->turnRate <= -LARA_SLOW_TURN || (info->gunStatus == LG_READY && info->gunType != WEAPON_TORCH) || (info->gunStatus == LG_DRAW_GUNS && info->gunType != WEAPON_FLARE)) // Why are these weapons??? @Sezz 2021.10.10 { item->goalAnimState = LS_TURN_LEFT_FAST; } else [[likely]] item->goalAnimState = LS_TURN_LEFT_SLOW; return; } else if (TrInput & IN_RIGHT) { if (TrInput & IN_SPRINT || info->turnRate >= LARA_SLOW_TURN || (info->gunStatus == LG_READY && info->gunType != WEAPON_TORCH) || (info->gunStatus == LG_DRAW_GUNS && info->gunType != WEAPON_FLARE)) { item->goalAnimState = LS_TURN_RIGHT_FAST; } else [[likely]] item->goalAnimState = LS_TURN_RIGHT_SLOW; return; } // TODO: LUA. // TODO: Without animation blending, the AFK pose animation's // movement lock will be rather obnoxious. // TODO: Adding some idle breathing would be nice. @Sezz 2021.10.31 info->NewAnims.Pose = false; if (info->poseCount == LARA_POSE_TIME && TestLaraPose(item, coll) && info->NewAnims.Pose) { item->goalAnimState = LS_POSE; return; } item->goalAnimState = LS_STOP; } // TODO: Future-proof for raising water. // TODO: See if making these into true states would be beneficial. @Sezz 2021.10.13 // Pseudo-state for idling in wade-height water. void LaraWadeStop(ITEM_INFO* item, COLL_INFO* coll) { if (TrInput & IN_JUMP) { item->goalAnimState = LS_JUMP_PREPARE; return; } if (TrInput & IN_FORWARD && coll->CollisionType != CT_FRONT && coll->CollisionType != CT_TOP_FRONT) { item->goalAnimState = LS_WADE_FORWARD; return; } if (TrInput & IN_BACK && TestLaraWalkBack(item, coll)) { item->goalAnimState = LS_WALK_BACK; return; } if (TrInput & IN_LEFT) { item->goalAnimState = LS_TURN_LEFT_SLOW; return; } else if (TrInput & IN_RIGHT) { item->goalAnimState = LS_TURN_RIGHT_SLOW; return; } if (TrInput & IN_LSTEP && TestLaraStepLeft(item, coll)) { item->goalAnimState = LS_STEP_LEFT; return; } else if (TrInput & IN_RSTEP && TestLaraStepRight(item, coll)) { item->goalAnimState = LS_STEP_RIGHT; return; } item->goalAnimState = LS_STOP; } // Pseudo-state for idling in swamps. void LaraSwampStop(ITEM_INFO* item, COLL_INFO* coll) { if (TrInput & IN_FORWARD && coll->CollisionType != CT_FRONT && coll->CollisionType != CT_TOP_FRONT) { item->goalAnimState = LS_WADE_FORWARD; return; } if (TrInput & IN_BACK && TestLaraWalkBackSwamp(item, coll)) { item->goalAnimState = LS_WALK_BACK; return; } if (TrInput & IN_LEFT) { item->goalAnimState = LS_TURN_LEFT_SLOW; return; } else if (TrInput & IN_RIGHT) { item->goalAnimState = LS_TURN_RIGHT_SLOW; return; } if (TrInput & IN_LSTEP && TestLaraStepLeftSwamp(item, coll)) { item->goalAnimState = LS_STEP_LEFT; return; } else if (TrInput & IN_RSTEP && TestLaraStepRightSwamp(item, coll)) { item->goalAnimState = LS_STEP_RIGHT; return; } item->goalAnimState = LS_STOP; } // State: LS_STOP (2) // Control: lara_as_stop() void lara_col_stop(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->moveAngle = item->pos.yRot; coll->Setup.BadHeightDown = TestLaraSwamp(item) ? NO_BAD_POS : STEPUP_HEIGHT; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = 0; item->gravityStatus = false; item->fallspeed = 0; coll->Setup.SlopesArePits = true; coll->Setup.SlopesAreWalls = true; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); if (TestLaraHitCeiling(coll)) { SetLaraHitCeiling(item, coll); return; } if (TestLaraFall(coll)) { SetLaraFallState(item); return; } if (TestLaraSlide(item, coll)) return; ShiftItem(item, coll); // TODO: Vaulting from this state. LaraSnapToHeight(item, coll); } void lara_as_forwardjump(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 3*/ /*collision: */ if (item->goalAnimState == LS_SWANDIVE_START || item->goalAnimState == LS_REACH) item->goalAnimState = LS_JUMP_FORWARD; if (item->goalAnimState != LS_DEATH && item->goalAnimState != LS_STOP && item->goalAnimState != LS_RUN_FORWARD) { if (info->gunStatus == LG_NO_ARMS && TrInput & IN_ACTION) item->goalAnimState = LS_REACH; if (TrInput & IN_BACK || TrInput & IN_ROLL) item->goalAnimState = LS_JUMP_ROLL_180; if (info->gunStatus == LG_NO_ARMS && TrInput & IN_WALK) item->goalAnimState = LS_SWANDIVE_START; if (item->fallspeed > LARA_FREEFALL_SPEED) item->goalAnimState = LS_FREEFALL; } if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_JUMP_TURN) info->turnRate = -LARA_JUMP_TURN; } else if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_JUMP_TURN) info->turnRate = LARA_JUMP_TURN; } } void lara_col_forwardjump(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 3*/ /*state code: lara_as_forwardjump*/ if (item->speed < 0) info->moveAngle = item->pos.yRot + ANGLE(180); else info->moveAngle = item->pos.yRot; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = BAD_JUMP_CEILING; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); LaraDeflectEdgeJump(item, coll); if (item->speed < 0) info->moveAngle = item->pos.yRot; if (item->fallspeed > 0 && (coll->Middle.Floor <= 0 || TestLaraSwamp(item))) { if (LaraLandedBad(item, coll)) { item->goalAnimState = LS_DEATH; } else { if (info->waterStatus == LW_WADE) { item->goalAnimState = LS_STOP; } else { if (TrInput & IN_FORWARD && !(TrInput & IN_STEPSHIFT)) item->goalAnimState = LS_RUN_FORWARD; else item->goalAnimState = LS_STOP; } } item->gravityStatus = false; item->fallspeed = 0; item->speed = 0; LaraSnapToHeight(item, coll); } } // State: LS_POSE (4) // Control: lara_col_pose() void lara_as_pose(ITEM_INFO* item, COLL_INFO* coll) { if (item->hitPoints <= 0) { item->goalAnimState = LS_DEATH; return; } if (TrInput & IN_LOOK) LookUpDown(); if (TestLaraPose(item, coll)) { if (TrInput & IN_ROLL) { item->goalAnimState = LS_ROLL_FORWARD; return; } if (TrInput & IN_WAKE) { item->goalAnimState = LS_STOP; return; } item->goalAnimState = LS_POSE; return; } item->goalAnimState = LS_STOP; } // State: LS_POSE (4) // Control: lara_as_pose() void lara_col_pose(ITEM_INFO* item, COLL_INFO* coll) { lara_col_stop(item, coll); } // State: LS_HOP_BACK (5) // Collision: lara_col_fastback() void lara_as_fastback(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_MED_TURN) info->turnRate = -LARA_MED_TURN; DoLaraLean(item, coll, -LARA_LEAN_MAX / 2, 10); } else if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_MED_TURN) info->turnRate = LARA_MED_TURN; DoLaraLean(item, coll, LARA_LEAN_MAX / 2, 10); } if (TrInput & IN_ROLL) { item->goalAnimState = LS_ROLL_FORWARD; return; } item->goalAnimState = LS_STOP; } // State: LS_HOP_BACK (5) // Control: lara_as_fastback() void lara_col_fastback(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->moveAngle = item->pos.yRot + ANGLE(180.0f); item->fallspeed = 0; item->gravityStatus = false; coll->Setup.SlopesAreWalls = false; coll->Setup.SlopesArePits = true; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = 0; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); if (TestLaraHitCeiling(coll)) { SetLaraHitCeiling(item, coll); return; } if (LaraDeflectEdge(item, coll)) LaraCollideStop(item, coll); if (coll->Middle.Floor > STEPUP_HEIGHT / 2) { SetLaraFallBackState(item); return; } if (TestLaraSlide(item, coll)) return; if (TestLaraStep(coll)) { DoLaraStep(item, coll); return; } } // State: LS_TURN_RIGHT_SLOW (6) // Collision: lara_col_turn_r() void lara_as_turn_r(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = (TestLaraSwamp(item) && info->waterStatus == LW_WADE) ? false : true; if (item->hitPoints <= 0) { item->goalAnimState = LS_DEATH; return; } // TODO: This can't be anywhere below the run dispatch because a test to prevent forward movement without embedding currently can't exist. @Sezz 2021.11.12 info->turnRate += LARA_TURN_RATE; if (info->waterStatus == LW_WADE) { if (TestLaraSwamp(item)) LaraSwampTurnRight(item, coll); else [[likely]] LaraWadeTurnRight(item, coll); return; } if (TrInput & IN_JUMP && coll->Middle.Ceiling < -LARA_HEADROOM * 0.7f) { item->goalAnimState = LS_JUMP_PREPARE; return; } if (TrInput & IN_ROLL) { item->goalAnimState = LS_ROLL_FORWARD; return; } if ((TrInput & IN_DUCK/* || TestLaraKeepCrouched(coll)*/) && (info->gunStatus == LG_NO_ARMS || !IsStandingWeapon(info->gunType))) { item->goalAnimState = LS_CROUCH_IDLE; return; } if (TrInput & IN_FORWARD) { if (TrInput & IN_WALK) { if (TestLaraWalkForward(item, coll)) { item->goalAnimState = LS_WALK_FORWARD; return; } } else if (TrInput & IN_SPRINT && TestLaraRunForward(item, coll)) { item->goalAnimState = LS_SPRINT; return; } else if (TestLaraRunForward(item, coll)) [[likely]] { item->goalAnimState = LS_RUN_FORWARD; return; } } else if (TrInput & IN_BACK) { if (TrInput & IN_WALK) { if (TestLaraWalkBack(item, coll)) { item->goalAnimState = LS_WALK_BACK; return; } } else if (TestLaraHopBack(item, coll)) [[likely]] { item->goalAnimState = LS_HOP_BACK; return; } } if (TrInput & IN_LSTEP && TestLaraStepLeft(item, coll)) { item->goalAnimState = LS_STEP_LEFT; return; } else if (TrInput & IN_RSTEP && TestLaraStepRight(item, coll)) { item->goalAnimState = LS_STEP_RIGHT; return; } // TODO: Lara can get locked in the turn right/left animation when, holding forward against a wall, // the player presses and holds the button to turn the opposite way. @Sezz 2021.10.16 if (TrInput & IN_RIGHT) { if (info->turnRate < 0) info->turnRate = 0; else if (info->turnRate > LARA_FAST_TURN) info->turnRate = LARA_FAST_TURN; if (TrInput & IN_WALK) // TODO: This hasn't worked since TR1. { if (info->turnRate > LARA_SLOW_TURN) info->turnRate = LARA_SLOW_TURN; item->goalAnimState = LS_TURN_RIGHT_SLOW; } else if (info->turnRate > LARA_SLOW_TURN) item->goalAnimState = LS_TURN_RIGHT_FAST; else [[likely]] item->goalAnimState = LS_TURN_RIGHT_SLOW; return; } item->goalAnimState = LS_STOP; } // Pseudo-state for turning right in wade-height water. void LaraWadeTurnRight(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; if (TrInput & IN_JUMP) { item->goalAnimState = LS_JUMP_PREPARE; return; } if (TrInput & IN_FORWARD && coll->CollisionType != CT_FRONT && coll->CollisionType != CT_TOP_FRONT) { item->goalAnimState = LS_WADE_FORWARD; return; } else if (TrInput & IN_BACK && TestLaraWalkBack(item, coll)) { item->goalAnimState = LS_WALK_BACK; return; } if (TrInput & IN_LSTEP && TestLaraStepLeft(item, coll)) { item->goalAnimState = LS_STEP_LEFT; return; } else if (TrInput & IN_RSTEP && TestLaraStepRight(item, coll)) { item->goalAnimState = LS_STEP_RIGHT; return; } if (TrInput & IN_RIGHT) { if (info->turnRate > LARA_MED_TURN) info->turnRate = LARA_MED_TURN; item->goalAnimState = LS_TURN_RIGHT_SLOW; return; } item->goalAnimState = LS_STOP; } // Pseudo-state for turning right in swamps. void LaraSwampTurnRight(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; if (TrInput & IN_FORWARD && coll->CollisionType != CT_FRONT && coll->CollisionType != CT_TOP_FRONT) { item->goalAnimState = LS_WADE_FORWARD; return; } else if (TrInput & IN_BACK && TestLaraWalkBackSwamp(item, coll)) { item->goalAnimState = LS_WALK_BACK; return; } if (TrInput & IN_LSTEP && TestLaraStepLeftSwamp(item, coll)) { item->goalAnimState = LS_STEP_LEFT; return; } else if (TrInput & IN_RSTEP && TestLaraStepRightSwamp(item, coll)) { item->goalAnimState = LS_STEP_RIGHT; return; } if (TrInput & IN_RIGHT) { if (info->turnRate > LARA_SLOW_TURN / 2) info->turnRate = LARA_SLOW_TURN / 2; item->goalAnimState = LS_TURN_RIGHT_SLOW; return; } item->goalAnimState = LS_STOP; } // State: LS_TURN_RIGHT_SLOW (6) // Control: lara_as_turn_r() void lara_col_turn_r(ITEM_INFO* item, COLL_INFO* coll) { lara_col_stop(item, coll); } // State: LS_TURN_LEFT_SLOW (7) // Collision: lara_col_turn_l() void lara_as_turn_l(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = (TestLaraSwamp(item) && info->waterStatus == LW_WADE) ? false : true; if (item->hitPoints <= 0) { item->goalAnimState = LS_DEATH; return; } info->turnRate -= LARA_TURN_RATE; if (info->waterStatus == LW_WADE) { if (TestLaraSwamp(item)) LaraSwampTurnLeft(item, coll); else [[likely]] LaraWadeTurnLeft(item, coll); return; } if (TrInput & IN_JUMP && coll->Middle.Ceiling < -LARA_HEADROOM * 0.7f) { item->goalAnimState = LS_JUMP_PREPARE; return; } if (TrInput & IN_ROLL) { item->goalAnimState = LS_ROLL_FORWARD; return; } if ((TrInput & IN_DUCK) && (info->gunStatus == LG_NO_ARMS || !IsStandingWeapon(info->gunType))) { item->goalAnimState = LS_CROUCH_IDLE; return; } if (TrInput & IN_FORWARD) { if (TrInput & IN_WALK) { if (TestLaraWalkForward(item, coll)) { item->goalAnimState = LS_WALK_FORWARD; return; } } else if (TrInput & IN_SPRINT && TestLaraRunForward(item, coll)) { item->goalAnimState = LS_SPRINT; return; } else if (TestLaraRunForward(item, coll)) [[likely]] { item->goalAnimState = LS_RUN_FORWARD; return; } } else if (TrInput & IN_BACK) { if (TrInput & IN_WALK) { if (TestLaraWalkBack(item, coll)) { item->goalAnimState = LS_WALK_BACK; return; } } else if (TestLaraHopBack(item, coll)) [[likely]] { item->goalAnimState = LS_HOP_BACK; return; } } if (TrInput & IN_LSTEP && TestLaraStepLeft(item, coll)) { item->goalAnimState = LS_STEP_LEFT; return; } // TODO: Lara steps left. Why?? @Sezz 2021.10.08 else if (TrInput & IN_RSTEP && TestLaraStepRight(item, coll)) { item->goalAnimState = LS_STEP_RIGHT; return; } if (TrInput & IN_LEFT) { if (info->turnRate > 0) info->turnRate = 0; else if (info->turnRate < -LARA_FAST_TURN) info->turnRate = -LARA_FAST_TURN; if (TrInput & IN_WALK) { if (info->turnRate < -LARA_SLOW_TURN) info->turnRate = -LARA_SLOW_TURN; item->goalAnimState = LS_TURN_LEFT_SLOW; } else if (info->turnRate < -LARA_MED_TURN) item->goalAnimState = LS_TURN_LEFT_FAST; else [[likely]] item->goalAnimState = LS_TURN_RIGHT_SLOW; return; } item->goalAnimState = LS_STOP; } // Pseudo-state for turning left in wade-height water. void LaraWadeTurnLeft(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; if (TrInput & IN_JUMP) { item->goalAnimState = LS_JUMP_PREPARE; return; } if (TrInput & IN_FORWARD && coll->CollisionType != CT_FRONT && coll->CollisionType != CT_TOP_FRONT) { item->goalAnimState = LS_WADE_FORWARD; return; } else if (TrInput & IN_BACK && TestLaraWalkBack(item, coll)) { item->goalAnimState = LS_WALK_BACK; return; } if (TrInput & IN_LSTEP && TestLaraStepLeft(item, coll)) { item->goalAnimState = LS_STEP_LEFT; return; } else if (TrInput & IN_RSTEP && TestLaraStepRight(item, coll)) { item->goalAnimState = LS_STEP_RIGHT; return; } if (TrInput & IN_LEFT) { if (info->turnRate < -LARA_MED_TURN) info->turnRate = -LARA_MED_TURN; item->goalAnimState = LS_TURN_LEFT_SLOW; return; } item->goalAnimState = LS_STOP; } // Pseudo-state for turning left in swamps. void LaraSwampTurnLeft(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; if (TrInput & IN_FORWARD && coll->CollisionType != CT_FRONT && coll->CollisionType != CT_TOP_FRONT) { item->goalAnimState = LS_WADE_FORWARD; return; } else if (TrInput & IN_BACK && TestLaraWalkBack(item, coll)) { item->goalAnimState = LS_WALK_BACK; return; } if (TrInput & IN_LSTEP && TestLaraStepLeft(item, coll)) { item->goalAnimState = LS_STEP_LEFT; return; } else if (TrInput & IN_RSTEP && TestLaraStepRight(item, coll)) { item->goalAnimState = LS_STEP_RIGHT; return; } if (TrInput & IN_LEFT) { if (info->turnRate < -LARA_SLOW_TURN / 2) info->turnRate = -LARA_SLOW_TURN / 2; item->goalAnimState = LS_TURN_LEFT_SLOW; return; } item->goalAnimState = LS_STOP; } // State: LS_TURN_LEFT_SLOW (7) // Control: lara_as_turn_l() void lara_col_turn_l(ITEM_INFO* item, COLL_INFO* coll) { lara_col_turn_r(item, coll); } void lara_as_death(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 8*/ /*collision: lara_col_death*/ info->look = false; coll->Setup.EnableObjectPush = false; coll->Setup.EnableSpaz = false; if (BinocularRange) { BinocularRange = 0; LaserSight = false; AlterFOV(ANGLE(80.0f)); LaraItem->meshBits = -1; info->busy = false; } } void lara_col_death(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 8*/ /*state code: lara_as_death*/ StopSoundEffect(SFX_TR4_LARA_FALL); info->moveAngle = item->pos.yRot; coll->Setup.BadHeightDown = STEPUP_HEIGHT; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = 0; coll->Setup.Radius = LARA_RAD_DEATH; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); ShiftItem(item, coll); item->hitPoints = -1; info->air = -1; if (coll->Middle.Floor != NO_HEIGHT) item->pos.yPos += coll->Middle.Floor; } void lara_as_fastfall(ITEM_INFO* item, COLL_INFO* coll) { /*state 9*/ /*collision: lara_col_fastfall*/ item->speed = (item->speed * 95) / 100; if (item->fallspeed == 154) SoundEffect(SFX_TR4_LARA_FALL, &item->pos, 0); } void lara_col_fastfall(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 9*/ /*state code: lara_as_fastfall*/ item->gravityStatus = true; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = BAD_JUMP_CEILING; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); LaraSlideEdgeJump(item, coll); if (coll->Middle.Floor <= 0 || TestLaraSwamp(item)) { if (LaraLandedBad(item, coll)) item->goalAnimState = LS_DEATH; else SetAnimation(item, LA_FREEFALL_LAND); StopSoundEffect(SFX_TR4_LARA_FALL); item->fallspeed = 0; item->gravityStatus = false; LaraSnapToHeight(item, coll); } } void lara_as_reach(ITEM_INFO* item, COLL_INFO* coll) { /*state 11*/ /*collision: lara_col_reach*/ Camera.targetAngle = ANGLE(85.0f); if (item->fallspeed > LARA_FREEFALL_SPEED) item->goalAnimState = LS_FREEFALL; } void lara_col_reach(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 11*/ /*state code: lara_as_reach*/ if (info->ropePtr == -1) item->gravityStatus = true; info->moveAngle = item->pos.yRot; coll->Setup.Height = LARA_HEIGHT_STRETCH; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = 0; coll->Setup.BadCeilingHeight = BAD_JUMP_CEILING; coll->Setup.ForwardAngle = info->moveAngle; coll->Setup.Radius = coll->Setup.Radius * 1.2f; coll->Setup.Mode = COLL_PROBE_MODE::FREE_FORWARD; GetCollisionInfo(coll, item); if (TestLaraHangJump(item, coll)) return; LaraSlideEdgeJump(item, coll); coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); ShiftItem(item, coll); if (item->fallspeed > 0 && coll->Middle.Floor <= 0) { if (LaraLandedBad(item, coll)) { item->goalAnimState = LS_DEATH; } else { item->goalAnimState = LS_STOP; item->fallspeed = 0; item->gravityStatus = false; if (coll->Middle.Floor != NO_HEIGHT) item->pos.yPos += coll->Middle.Floor; } } } void lara_as_splat(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = false; } void lara_col_splat(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->moveAngle = item->pos.yRot; coll->Setup.SlopesAreWalls = true; coll->Setup.SlopesArePits = true; coll->Setup.BadHeightDown = STEPUP_HEIGHT; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = 0; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); ShiftItem(item, coll); if (coll->Middle.Floor >= -256 && coll->Middle.Floor <= 256) item->pos.yPos += coll->Middle.Floor; } void lara_col_land(ITEM_INFO* item, COLL_INFO* coll) { /*state 14*/ /*state code: lara_void_func*/ lara_col_stop(item, coll); } // State: LS_JUMP_PREPARE (15) // Collision: lara_col_compress() void lara_as_compress(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; // TODO: dispatch /*if (item->hitPoints <= 0) { item->goalAnimState = LS_DEATH; return; }*/ if (info->waterStatus == LW_WADE) { item->goalAnimState = LS_JUMP_UP; return; } if (TrInput & IN_LEFT && TrInput & (IN_FORWARD | IN_BACK)) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_SLOW_TURN) info->turnRate = -LARA_SLOW_TURN; } else if (TrInput & IN_RIGHT && TrInput & (IN_FORWARD | IN_BACK)) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_SLOW_TURN) info->turnRate = LARA_SLOW_TURN; } if (TrInput & IN_FORWARD && TestLaraStandingJump(item, coll, item->pos.yRot)) { info->moveAngle = item->pos.yRot; item->goalAnimState = LS_JUMP_FORWARD; return; } else if (TrInput & IN_BACK && TestLaraStandingJump(item, coll, item->pos.yRot + ANGLE(180.0f))) { info->moveAngle = item->pos.yRot + ANGLE(180.0f); item->goalAnimState = LS_JUMP_BACK; return; } if (TrInput & IN_LEFT && TestLaraStandingJump(item, coll, item->pos.yRot - ANGLE(90.0f))) { info->moveAngle = item->pos.yRot - ANGLE(90.0f); item->goalAnimState = LS_JUMP_LEFT; return; } else if (TrInput & IN_RIGHT && TestLaraStandingJump(item, coll, item->pos.yRot + ANGLE(90.0f))) { info->moveAngle = item->pos.yRot + ANGLE(90.0f); item->goalAnimState = LS_JUMP_RIGHT; return; } item->goalAnimState = LS_JUMP_UP; // TODO: What is this? @Sezz 2021.11.13 if (item->fallspeed > LARA_FREEFALL_SPEED) item->goalAnimState = LS_FREEFALL; } // State: LS_JUMP_PREPARE (15) // Collision: lara_as_compress() void lara_col_compress(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; item->fallspeed = 0; item->gravityStatus = false; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = NO_HEIGHT; coll->Setup.BadCeilingHeight = 0; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); if (TestLaraFall(coll)) { SetLaraFallState(item); return; } if (TestLaraSlide(item, coll)) return; // TODO: Better handling. if (coll->Middle.Ceiling > -100) { SetAnimation(item, LA_STAND_SOLID); item->speed = 0; item->fallspeed = 0; item->gravityStatus = false; item->pos.xPos = coll->Setup.OldPosition.x; item->pos.yPos = coll->Setup.OldPosition.y; item->pos.zPos = coll->Setup.OldPosition.z; } if (coll->Middle.Floor > -STEP_SIZE && coll->Middle.Floor < STEP_SIZE) item->pos.yPos += coll->Middle.Floor; } // State: LS_WALK_BACK (16) // Collision: lara_col_back() void lara_as_back(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = (TestLaraSwamp(item) && info->waterStatus == LW_WADE) ? false : true; if (item->hitPoints <= 0) { item->goalAnimState = LS_STOP; // TODO dispatch return; } if (info->isMoving) return; if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_SLOW_TURN) info->turnRate = -LARA_SLOW_TURN; DoLaraLean(item, coll, -LARA_LEAN_MAX / 3, 10); } else if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_SLOW_TURN) info->turnRate = LARA_SLOW_TURN; DoLaraLean(item, coll, LARA_LEAN_MAX / 3, 10); } if (TrInput & IN_BACK && (TrInput & IN_WALK || info->waterStatus == LW_WADE)) { item->goalAnimState = LS_WALK_BACK; return; } item->goalAnimState = LS_STOP; } // State: LS_WALK_BACK (16) // Control: lara_as_back() void lara_col_back(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->moveAngle = item->pos.yRot + ANGLE(180); item->gravityStatus = false; item->fallspeed = 0; coll->Setup.BadHeightDown = info->waterStatus == LW_WADE ? NO_BAD_POS : STEPUP_HEIGHT; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = 0; coll->Setup.SlopesArePits = true; coll->Setup.SlopesAreWalls = true; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); if (TestLaraHitCeiling(coll)) { SetLaraHitCeiling(item, coll); return; } if (LaraDeflectEdge(item, coll)) LaraCollideStop(item, coll); if (TestLaraFall(coll)) { SetLaraFallState(item); return; } if (TestLaraSlide(item, coll)) return; if (TestLaraStep(coll)) { DoLaraStep(item, coll); return; } } // State: LS_TURN_RIGHT_FAST (20) // Collision: lara_col_turn_right_fast() void lara_as_turn_right_fast(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; if (item->hitPoints <= 0) { item->goalAnimState = LS_DEATH; return; } // TODO: Wade handling. @Sezz 2021.10.13 if (TrInput & IN_JUMP && coll->Middle.Ceiling < -LARA_HEADROOM * 0.7f) { item->goalAnimState = LS_JUMP_PREPARE; return; } if (TrInput & IN_ROLL && info->waterStatus != LW_WADE) { item->goalAnimState = LS_ROLL_FORWARD; return; } if ((TrInput & IN_DUCK/* || TestLaraKeepCrouched(coll)*/) && (info->gunStatus == LG_NO_ARMS || !IsStandingWeapon(info->gunType)) && info->waterStatus != LW_WADE) { item->goalAnimState = LS_CROUCH_IDLE; return; } if (TrInput & IN_FORWARD) { if (info->waterStatus == LW_WADE) // Should not be possible, but here for security. { if (TestLaraRunForward(item, coll)) { item->goalAnimState = LS_WADE_FORWARD; return; } } else if (TrInput & IN_WALK) { if (TestLaraWalkForward(item, coll)) { item->goalAnimState = LS_WALK_FORWARD; return; } } else if (TrInput & IN_SPRINT && TestLaraRunForward(item, coll)) { item->goalAnimState = LS_SPRINT; return; } else if (TestLaraRunForward(item, coll)) [[likely]] { item->goalAnimState = LS_RUN_FORWARD; return; } } else if (TrInput & IN_BACK) { if (TrInput & IN_WALK) { if (TestLaraWalkBack(item, coll)) { item->goalAnimState = LS_WALK_BACK; return; } } else if (TestLaraHopBack(item, coll)) [[likely]] { item->goalAnimState = LS_HOP_BACK; return; } } if (TrInput & IN_LSTEP && TestLaraStepLeft(item, coll)) { item->goalAnimState = LS_STEP_LEFT; return; } else if (TrInput & IN_RSTEP && TestLaraStepRight(item, coll)) { item->goalAnimState = LS_STEP_RIGHT; return; } // TODO: Hold WALK to slow down again. if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate < LARA_MED_TURN) info->turnRate = LARA_MED_TURN; else if (info->turnRate > LARA_FAST_TURN) info->turnRate = LARA_FAST_TURN; item->goalAnimState = LS_TURN_RIGHT_FAST; return; } item->goalAnimState = LS_STOP; } // State: LS_TURN_RIGHT_FAST (20) // Control: lara_as_turn_right_fast() void lara_col_turn_right_fast(ITEM_INFO* item, COLL_INFO* coll) { lara_col_stop(item, coll); } // State: LS_TURN_LEFT_FAST (152) // Collision: lara_col_turn_left_fast() void lara_as_turn_left_fast(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; if (item->hitPoints <= 0) { item->goalAnimState = LS_DEATH; return; } if (TrInput & IN_JUMP && coll->Middle.Ceiling < -LARA_HEADROOM * 0.7f) { item->goalAnimState = LS_JUMP_PREPARE; return; } if (TrInput & IN_ROLL && info->waterStatus != LW_WADE) { item->goalAnimState = LS_ROLL_FORWARD; return; } if ((TrInput & IN_DUCK/* || TestLaraKeepCrouched(coll)*/) && (info->gunStatus == LG_NO_ARMS || !IsStandingWeapon(info->gunType)) && info->waterStatus != LW_WADE) { item->goalAnimState = LS_CROUCH_IDLE; return; } if (TrInput & IN_FORWARD) { if (info->waterStatus == LW_WADE) // Should not be possible, but here for security. { if (TestLaraRunForward(item, coll)) { item->goalAnimState = LS_WADE_FORWARD; return; } } else if (TrInput & IN_WALK) { if (TestLaraWalkForward(item, coll)) { item->goalAnimState = LS_WALK_FORWARD; return; } } else if (TrInput & IN_SPRINT && TestLaraRunForward(item, coll)) { item->goalAnimState = LS_SPRINT; return; } else if (TestLaraRunForward(item, coll)) [[likely]] { item->goalAnimState = LS_RUN_FORWARD; return; } } else if (TrInput & IN_BACK) { if (TrInput & IN_WALK) { if (TestLaraWalkBack(item, coll)) { item->goalAnimState = LS_WALK_BACK; return; } } else if (TestLaraHopBack(item, coll)) [[likely]] { item->goalAnimState = LS_HOP_BACK; return; } } if (TrInput & IN_LSTEP && TestLaraStepLeft(item, coll)) { item->goalAnimState = LS_STEP_LEFT; return; } // TODO: Lara steps left. Why?? @Sezz 2021.10.08 else if (TrInput & IN_RSTEP && TestLaraStepRight(item, coll)) { item->goalAnimState = LS_STEP_RIGHT; return; } if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate > -LARA_MED_TURN) info->turnRate = -LARA_MED_TURN; else if (info->turnRate < -LARA_FAST_TURN) info->turnRate = -LARA_FAST_TURN; item->goalAnimState = LS_TURN_LEFT_FAST; return; } item->goalAnimState = LS_STOP; } // State: LS_TURN_LEFT_FAST (152) // Control: lara_as_turn_left_fast() void lara_col_turn_left_fast(ITEM_INFO* item, COLL_INFO* coll) { lara_col_stop(item, coll); } // State: LS_SIDESTEP_RIGHT (21) // Collision: lara_col_stepright() void lara_as_stepright(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = false; if (item->hitPoints <= 0) { item->goalAnimState = LS_STOP; return; } if (info->isMoving) return; if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_SLOW_TURN) info->turnRate = -LARA_SLOW_TURN; } else if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_SLOW_TURN) info->turnRate = LARA_SLOW_TURN; } if (TrInput & IN_RSTEP) { item->goalAnimState = LS_STEP_RIGHT; return; } item->goalAnimState = LS_STOP; } // State: LS_STEP_RIGHT (21) // Control: lara_as_stepright() void lara_col_stepright(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->moveAngle = item->pos.yRot + ANGLE(90.0f); item->gravityStatus = false; item->fallspeed = 0; coll->Setup.BadHeightDown = (info->waterStatus == LW_WADE) ? NO_BAD_POS : STEP_SIZE / 2; coll->Setup.BadHeightUp = -STEP_SIZE / 2; coll->Setup.BadCeilingHeight = 0; coll->Setup.SlopesArePits = true; coll->Setup.SlopesAreWalls = true; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); if (TestLaraHitCeiling(coll)) { SetLaraHitCeiling(item, coll); return; } if (LaraDeflectEdge(item, coll)) LaraCollideStop(item, coll); if (TestLaraFall(coll)) { SetLaraFallState(item); return; } if (TestLaraSlide(item, coll)) return; if (TestLaraStep(coll) || TestLaraSwamp(item)) { DoLaraStep(item, coll); return; } } // State: LS_SIDESTEP_LEFT (22) // Collision: lara_col_stepleft() void lara_as_stepleft(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = false; if (item->hitPoints <= 0) { item->goalAnimState = LS_STOP; return; } if (info->isMoving) return; if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_SLOW_TURN) info->turnRate = -LARA_SLOW_TURN; } else if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_SLOW_TURN) info->turnRate = LARA_SLOW_TURN; } if (TrInput & IN_LSTEP) { item->goalAnimState = LS_STEP_LEFT; return; } item->goalAnimState = LS_STOP; } // State: LS_STEP_LEFT (22) // Control: lara_as_stepleft() void lara_col_stepleft(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->moveAngle = item->pos.yRot - ANGLE(90.0f); item->gravityStatus = false; item->fallspeed = 0; coll->Setup.BadHeightDown = (info->waterStatus == LW_WADE) ? NO_BAD_POS : STEP_SIZE / 2; coll->Setup.BadHeightUp = -STEP_SIZE / 2; coll->Setup.BadCeilingHeight = 0; coll->Setup.SlopesArePits = true; coll->Setup.SlopesAreWalls = true; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); if (TestLaraHitCeiling(coll)) { SetLaraHitCeiling(item, coll); return; } if (LaraDeflectEdge(item, coll)) LaraCollideStop(item, coll); if (TestLaraFall(coll)) { SetLaraFallState(item); return; } if (TestLaraSlide(item, coll)) return; if (TestLaraStep(coll) || TestLaraSwamp(item)) { DoLaraStep(item, coll); return; } } // State: LS_ROLL_BACK(23) // Collision: lara_col_roll2() void lara_as_roll2(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = false; if (TrInput & IN_ROLL) { item->goalAnimState = LS_ROLL_BACK; return; } item->goalAnimState = LS_STOP; } // State: LS_ROLL_BACK (23) // Control: lara_as_roll2() void lara_col_roll2(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; Camera.laraNode = 0; info->moveAngle = item->pos.yRot + ANGLE(180); item->gravityStatus = false; item->fallspeed = 0; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = 0; coll->Setup.SlopesAreWalls = true; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); if (TestLaraHitCeiling(coll)) { SetLaraHitCeiling(item, coll); return; } if (TestLaraSlide(item, coll)) return; if (coll->Middle.Floor > STEPUP_HEIGHT / 2) // Was 200. { SetLaraFallBackState(item); return; } ShiftItem(item, coll); if (TestLaraStep(coll)) { DoLaraStep(item, coll); return; } } void lara_as_backjump(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 25*/ /*collision: lara_col_backjump*/ info->look = false; Camera.targetAngle = ANGLE(135.0f); if (item->fallspeed <= LARA_FREEFALL_SPEED) { if (item->goalAnimState == LS_RUN_FORWARD) { item->goalAnimState = LS_STOP; } else if ((TrInput & IN_FORWARD || TrInput & IN_ROLL) && item->goalAnimState != LS_STOP) { item->goalAnimState = LS_JUMP_ROLL_180; } } else { item->goalAnimState = LS_FREEFALL; } } void lara_col_backjump(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 25*/ /*state code: lara_as_backjump*/ info->moveAngle = item->pos.yRot + ANGLE(180); lara_col_jumper(item, coll); } void lara_as_rightjump(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 26*/ /*collision: lara_col_rightjump*/ info->look = false; if (item->fallspeed > LARA_FREEFALL_SPEED) item->goalAnimState = LS_FREEFALL; else if (TrInput & IN_LEFT && item->goalAnimState != LS_STOP) item->goalAnimState = LS_JUMP_ROLL_180; } void lara_col_rightjump(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 26*/ /*state code: lara_as_rightjump*/ info->moveAngle = item->pos.yRot + ANGLE(90); lara_col_jumper(item, coll); } void lara_as_leftjump(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 27*/ /*collision: lara_col_leftjump*/ info->look = false; if (item->fallspeed > LARA_FREEFALL_SPEED) item->goalAnimState = LS_FREEFALL; else if (TrInput & IN_RIGHT && item->goalAnimState != LS_STOP) item->goalAnimState = LS_JUMP_ROLL_180; } void lara_col_leftjump(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 27*/ /*state code: lara_as_leftjump*/ info->moveAngle = item->pos.yRot - ANGLE(90); lara_col_jumper(item, coll); } void lara_col_jumper(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*states 25, 26, 27*/ /*state code: none, but is called in lara_col_backjump, lara_col_rightjump and lara_col_leftjump*/ coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = BAD_JUMP_CEILING; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); LaraDeflectEdgeJump(item, coll); if (item->fallspeed > 0 && coll->Middle.Floor <= 0) { if (LaraLandedBad(item, coll)) item->goalAnimState = LS_DEATH; else item->goalAnimState = LS_STOP; item->fallspeed = 0; item->gravityStatus = 0; if (coll->Middle.Floor != NO_HEIGHT) item->pos.yPos += coll->Middle.Floor; } } void lara_as_upjump(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = false; /*state 28*/ /*collision: lara_col_upjump*/ if (item->fallspeed > LARA_FREEFALL_SPEED) { item->goalAnimState = LS_FREEFALL; } } void lara_col_upjump(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 28*/ /*state code: lara_as_upjump*/ if (item->hitPoints <= 0) { item->goalAnimState = LS_STOP; return; } info->moveAngle = item->pos.yRot; coll->Setup.Height = LARA_HEIGHT_STRETCH; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = BAD_JUMP_CEILING; coll->Setup.ForwardAngle = item->speed < 0 ? info->moveAngle + ANGLE(180.0f) : info->moveAngle; coll->Setup.Mode = COLL_PROBE_MODE::FREE_FORWARD; GetCollisionInfo(coll, item); if (TestLaraHangJumpUp(item, coll)) return; if (coll->CollisionType == CT_CLAMP || coll->CollisionType == CT_TOP || coll->CollisionType == CT_TOP_FRONT) item->fallspeed = 1; ShiftItem(item, coll); if (coll->CollisionType == CT_NONE) { if (item->fallspeed < -70) { if (TrInput & IN_FORWARD && item->speed < 5) { item->speed++; } else if (TrInput & IN_BACK && item->speed > -5) { item->speed -= 2; } } } else { item->speed = item->speed <= 0 ? -2 : 2; } if (item->fallspeed > 0 && coll->Middle.Floor <= 0) { item->goalAnimState = LaraLandedBad(item, coll) ? LS_DEATH : LS_STOP; item->gravityStatus = false; item->fallspeed = 0; if (coll->Middle.Floor != NO_HEIGHT) item->pos.yPos += coll->Middle.Floor; } } void lara_as_fallback(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 29*/ /*collision: lara_col_fallback*/ if (item->fallspeed > LARA_FREEFALL_SPEED) item->goalAnimState = LS_FREEFALL; if (TrInput & IN_ACTION) if (info->gunStatus == LG_NO_ARMS) item->goalAnimState = LS_REACH; } void lara_col_fallback(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 29*/ /*state code: lara_as_fallback*/ info->moveAngle = item->pos.yRot + ANGLE(180); coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = BAD_JUMP_CEILING; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); LaraDeflectEdgeJump(item, coll); if (item->fallspeed > 0 && (coll->Middle.Floor <= 0 || TestLaraSwamp(item))) { if (LaraLandedBad(item, coll)) item->goalAnimState = LS_DEATH; else item->goalAnimState = LS_STOP; LaraResetGravityStatus(item, coll); LaraSnapToHeight(item, coll); } } // State: LS_ROLL_FORWARD (45) // Collision: lara_col_roll() void lara_as_roll(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = false; // TODO: Interpolation instead. info->NewAnims.SwandiveRollRun = true; if (TrInput & IN_FORWARD && item->animNumber == LA_SWANDIVE_ROLL && info->NewAnims.SwandiveRollRun) { item->goalAnimState = LS_RUN_FORWARD; return; } if (TrInput & IN_ROLL) { item->goalAnimState = LS_ROLL_FORWARD; return; } item->goalAnimState = LS_STOP; } // State: LS_ROLL_FORWARD (45) // Control: lara_as_roll() void lara_col_roll(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->moveAngle = item->pos.yRot; item->gravityStatus = false; item->fallspeed = 0; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = 0; coll->Setup.SlopesArePits = false; coll->Setup.SlopesAreWalls = true; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); if (TestLaraHitCeiling(coll)) { SetLaraHitCeiling(item, coll); return; } if (TestLaraFall(coll)) { SetLaraFallState(item); return; } if (TestLaraSlide(item, coll)) return; ShiftItem(item, coll); if (TestLaraStep(coll)) { DoLaraStep(item, coll); return; } } void lara_as_swandive(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 52*/ /*collision: lara_col_swandive*/ info->look = false; coll->Setup.EnableObjectPush = true; coll->Setup.EnableSpaz = false; if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_JUMP_TURN) info->turnRate = -LARA_JUMP_TURN; DoLaraLean(item, coll, -LARA_LEAN_MAX, 16); } else if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_JUMP_TURN) info->turnRate = LARA_JUMP_TURN; DoLaraLean(item, coll, LARA_LEAN_MAX, 16); } if (item->fallspeed > LARA_FREEFALL_SPEED && item->goalAnimState != LS_DIVE) item->goalAnimState = LS_SWANDIVE_END; } void lara_col_swandive(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 52*/ /*state code: lara_as_swandive*/ info->moveAngle = item->pos.yRot; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = BAD_JUMP_CEILING; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); LaraDeflectEdgeJump(item, coll); if (coll->Middle.Floor <= 0 && item->fallspeed > 0) { item->goalAnimState = LS_STOP; item->fallspeed = 0; item->gravityStatus = 0; if (coll->Middle.Floor != NO_HEIGHT) item->pos.yPos += coll->Middle.Floor; } } void lara_as_fastdive(ITEM_INFO* item, COLL_INFO* coll) { /*state 53*/ /*collision: lara_col_fastdive*/ if (TrInput & IN_ROLL && item->goalAnimState == LS_SWANDIVE_END) item->goalAnimState = LS_JUMP_ROLL_180; coll->Setup.EnableObjectPush = true; coll->Setup.EnableSpaz = false; item->speed = (item->speed * 95) / 100; } void lara_col_fastdive(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 53*/ /*state code: lara_as_fastdive*/ info->moveAngle = item->pos.yRot; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = BAD_JUMP_CEILING; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); LaraDeflectEdgeJump(item, coll); if (coll->Middle.Floor <= 0 && item->fallspeed > 0) { if (item->fallspeed <= 133) item->goalAnimState = LS_STOP; else item->goalAnimState = LS_DEATH; item->fallspeed = 0; item->gravityStatus = 0; if (coll->Middle.Floor != NO_HEIGHT) item->pos.yPos += coll->Middle.Floor; } } void lara_as_gymnast(ITEM_INFO* item, COLL_INFO* coll) { /*state 54*/ /*collision: lara_default_col*/ coll->Setup.EnableObjectPush = false; coll->Setup.EnableSpaz = false; } // State: LS_WADE_FORWARD (65) // Collision: lara_col_wade() void lara_as_wade(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->look = (TestLaraSwamp(item) && info->waterStatus == LW_WADE) ? false : true; Camera.targetElevation = -ANGLE(22.0f); if (item->hitPoints <= 0) { item->goalAnimState = LS_STOP; return; } if (TestLaraSwamp(item)) { LaraWadeSwamp(item, coll); return; } if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_FAST_TURN) info->turnRate = -LARA_FAST_TURN; DoLaraLean(item, coll, -LARA_LEAN_MAX, 10); } else if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_FAST_TURN) info->turnRate = LARA_FAST_TURN; DoLaraLean(item, coll, LARA_LEAN_MAX, 10); } if (TrInput & IN_FORWARD) { if (info->waterStatus == LW_ABOVE_WATER) item->goalAnimState = LS_RUN_FORWARD; else [[likely]] item->goalAnimState = LS_WADE_FORWARD; return; } item->goalAnimState = LS_STOP; } // Pseudo-state for wading in a swamp. void LaraWadeSwamp(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_SLOW_TURN / 2) info->turnRate = -LARA_SLOW_TURN / 2; DoLaraLean(item, coll, -LARA_LEAN_MAX / 5 * 3, 12); } else if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_SLOW_TURN / 2) info->turnRate = LARA_SLOW_TURN / 2; DoLaraLean(item, coll, LARA_LEAN_MAX / 5 * 3, 12); } if (TrInput & IN_FORWARD) { item->goalAnimState = LS_WADE_FORWARD; return; } item->goalAnimState = LS_STOP; } // State: LS_WADE_FORWARD (65) // Control: lara_as_wade() void lara_col_wade(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->moveAngle = item->pos.yRot; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = 0; coll->Setup.SlopesAreWalls = true; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); if (TestLaraHitCeiling(coll)) { SetLaraHitCeiling(item, coll); return; } if (LaraDeflectEdge(item, coll)) { item->pos.zRot = 0; if (coll->Front.Floor < -STEPUP_HEIGHT && !coll->Front.Slope && !TestLaraSwamp(item)) { item->goalAnimState = LS_SPLAT; if (GetChange(item, &g_Level.Anims[item->animNumber])) return; } LaraCollideStop(item, coll); } if (TestLaraVault(item, coll)) return; if (TestLaraStep(coll) || TestLaraSwamp(item)) { DoLaraStep(item, coll); return; } // LEGACY step /*if (coll->Middle.Floor >= -STEPUP_HEIGHT && coll->Middle.Floor < -STEP_SIZE / 2 && !TestLaraSwamp(item)) { item->goalAnimState = LS_STEP_UP; GetChange(item, &g_Level.Anims[item->animNumber]); } if (coll->Middle.Floor >= 50 && !TestLaraSwamp(item)) { item->pos.yPos += 50; return; } LaraSnapToHeight(item, coll);*/ } // State: LS_SPRINT (73) // Collision: lara_col_dash() void lara_as_dash(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->sprintTimer--; if (item->hitPoints <= 0) { item->goalAnimState = LS_DEATH; return; } if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_SLOW_TURN) info->turnRate = -LARA_SLOW_TURN; DoLaraLean(item, coll, -LARA_LEAN_MAX, 9); } else if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_SLOW_TURN) info->turnRate = LARA_SLOW_TURN; DoLaraLean(item, coll, LARA_LEAN_MAX, 9); } if (TrInput & IN_JUMP) { item->goalAnimState = LS_SPRINT_ROLL; return; } if ((TrInput & IN_DUCK/* || TestLaraKeepCrouched(coll)*/) && (info->gunStatus == LG_NO_ARMS || !IsStandingWeapon(info->gunType))) { item->goalAnimState = LS_CROUCH_IDLE; return; } // TODO: Supposedly there is a bug wherein sprinting into the boundary between shallow and deep water // under some condition allows Lara to run around in the water room. Investigate. @Sezz 2021.09.29 if (TrInput & IN_FORWARD) { if (info->waterStatus == LW_WADE) item->goalAnimState = LS_RUN_FORWARD; // TODO: Dispatch to wade forward state. @Sezz 2021.09.29 else if (TrInput & IN_WALK) item->goalAnimState = LS_WALK_FORWARD; else if (TrInput & IN_SPRINT && info->sprintTimer > 0) [[likely]] item->goalAnimState = LS_SPRINT; else item->goalAnimState = LS_RUN_FORWARD; return; } item->goalAnimState = LS_STOP; } // State: LS_SPRINT (73) // Control: lara_as_dash() void lara_col_dash(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; info->moveAngle = item->pos.yRot; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = 0; coll->Setup.SlopesAreWalls = true; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); if (TestLaraHitCeiling(coll)) { SetLaraHitCeiling(item, coll); return; } if (TestLaraVault(item, coll)) return; if (LaraDeflectEdge(item, coll)) { item->pos.zRot = 0; if (coll->HitTallObject || TestLaraWall(item, 256, 0, -640) != SPLAT_COLL::NONE) { item->goalAnimState = LS_SPLAT; if (GetChange(item, &g_Level.Anims[item->animNumber])) { item->currentAnimState = LS_SPLAT; return; } } LaraCollideStop(item, coll); } if (TestLaraFall(coll)) { SetLaraFallState(item); return; } if (TestLaraSlide(item, coll)) return; if (TestLaraStep(coll)) { DoLaraStep(item, coll); return; } // LEGACY step //if (coll->Middle.Floor >= -STEPUP_HEIGHT && coll->Middle.Floor < -STEP_SIZE / 2) //{ // item->goalAnimState = LS_STEP_UP; // GetChange(item, &g_Level.Anims[item->animNumber]); //} // //if (coll->Middle.Floor < 50) //{ // if (coll->Middle.Floor != NO_HEIGHT) // item->pos.yPos += coll->Middle.Floor; //} //else //{ // item->goalAnimState = LS_STEP_DOWN; // for theoretical sprint stepdown anims, not in default anims // if (GetChange(item, &g_Level.Anims[item->animNumber])) // item->pos.yPos += coll->Middle.Floor; // move Lara to middle.Floor // else // item->pos.yPos += 50; // do the default aligment //} } // State: LS_SPRINT_ROLL (74) // Collision: lara_col_dashdive() void lara_as_dashdive(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; if (TrInput & IN_LEFT) { info->turnRate -= LARA_TURN_RATE; if (info->turnRate < -LARA_SLOW_TURN) info->turnRate = -LARA_SLOW_TURN; DoLaraLean(item, coll, -(LARA_LEAN_MAX * 3) / 5, 10); } else if (TrInput & IN_RIGHT) { info->turnRate += LARA_TURN_RATE; if (info->turnRate > LARA_SLOW_TURN) info->turnRate = LARA_SLOW_TURN; DoLaraLean(item, coll, (LARA_LEAN_MAX * 3) / 5, 10); } // TODO: What? if (item->goalAnimState != LS_DEATH && item->goalAnimState != LS_STOP && item->goalAnimState != LS_RUN_FORWARD && item->fallspeed > LARA_FREEFALL_SPEED) { item->goalAnimState = LS_FREEFALL; } } void lara_col_dashdive(ITEM_INFO* item, COLL_INFO* coll) { LaraInfo*& info = item->data; /*state 74*/ /*state code: lara_as_dashdive*/ if (item->speed < 0) info->moveAngle = item->pos.yRot + ANGLE(180); else info->moveAngle = item->pos.yRot; coll->Setup.BadHeightDown = NO_BAD_POS; coll->Setup.BadHeightUp = -STEPUP_HEIGHT; coll->Setup.BadCeilingHeight = BAD_JUMP_CEILING; coll->Setup.SlopesAreWalls = true; coll->Setup.ForwardAngle = info->moveAngle; GetCollisionInfo(coll, item); LaraDeflectEdgeJump(item, coll); if (!LaraFallen(item, coll)) { if (item->speed < 0) info->moveAngle = item->pos.yRot; if (coll->Middle.Floor <= 0 && item->fallspeed > 0) { if (LaraLandedBad(item, coll)) { item->goalAnimState = LS_DEATH; } else if (info->waterStatus == LW_WADE || !(TrInput & IN_FORWARD) || TrInput & IN_WALK) { item->goalAnimState = LS_STOP; } else { item->goalAnimState = LS_RUN_FORWARD; } item->gravityStatus = false; item->fallspeed = 0; item->pos.yPos += coll->Middle.Floor; item->speed = 0; AnimateLara(item); } ShiftItem(item, coll); if (coll->Middle.Floor != NO_HEIGHT) item->pos.yPos += coll->Middle.Floor; } }