diff --git a/TombEngine/Game/Lara/lara.cpp b/TombEngine/Game/Lara/lara.cpp index 650b03ad2..000f91464 100644 --- a/TombEngine/Game/Lara/lara.cpp +++ b/TombEngine/Game/Lara/lara.cpp @@ -40,7 +40,6 @@ using namespace TEN::Effects::Lara; using namespace TEN::Control::Volumes; using namespace TEN::Input; - using std::function; using TEN::Renderer::g_Renderer; @@ -448,7 +447,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll) item->Animation.AnimNumber == LA_STAND_IDLE && item->Animation.ActiveState == LS_IDLE && item->Animation.TargetState == LS_IDLE && - !item->Animation.Airborne) + !item->Animation.IsAirborne) { lara->Control.HandStatus = HandStatus::Free; } @@ -492,7 +491,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll) if (isWater) { item->Pose.Position.y += CLICK(0.5f) - 28; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; lara->Control.WaterStatus = WaterStatus::Underwater; lara->Air = LARA_AIR_MAX; @@ -529,15 +528,15 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll) { lara->Control.WaterStatus = WaterStatus::Wade; - // Make splash ONLY within this particular threshold before swim depth while airborne (WadeSplash() above interferes otherwise). + // Make splash ONLY within this particular threshold before swim depth while is_airborne (WadeSplash() above interferes otherwise). if (waterDepth > (SWIM_DEPTH - CLICK(1)) && - item->Animation.Airborne && !isSwamp) + item->Animation.IsAirborne && !isSwamp) { item->Animation.TargetState = LS_IDLE; Splash(item); } // Lara is grounded; don't splash again. - else if (!item->Animation.Airborne) + else if (!item->Animation.IsAirborne) item->Animation.TargetState = LS_IDLE; else if (isSwamp) { @@ -566,7 +565,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll) SetAnimation(item, LA_FALL_START); ResetLaraLean(item); ResetLaraFlex(item); - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.Velocity = item->Animation.VerticalVelocity / 4; item->Animation.VerticalVelocity = 0; lara->Control.WaterStatus = WaterStatus::Dry; @@ -606,7 +605,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll) if (heightFromWater <= WADE_DEPTH) { SetAnimation(item, LA_FALL_START); - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.Velocity = item->Animation.VerticalVelocity / 4; lara->Control.WaterStatus = WaterStatus::Dry; } @@ -634,7 +633,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll) ResetLaraLean(item); ResetLaraFlex(item); item->Pose.Position.y += 1 - heightFromWater; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; lara->Control.WaterStatus = WaterStatus::TreadWater; diff --git a/TombEngine/Game/Lara/lara_basic.cpp b/TombEngine/Game/Lara/lara_basic.cpp index f25abc6f9..737ee31d0 100644 --- a/TombEngine/Game/Lara/lara_basic.cpp +++ b/TombEngine/Game/Lara/lara_basic.cpp @@ -179,7 +179,7 @@ void lara_col_walk_forward(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); lara->Control.MoveAngle = item->Pose.Orientation.y; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; coll->Setup.LowerFloorBound = STEPUP_HEIGHT; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; @@ -310,7 +310,7 @@ void lara_col_run_forward(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); lara->Control.MoveAngle = item->Pose.Orientation.y; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; @@ -688,7 +688,7 @@ void lara_col_idle(ItemInfo* item, CollisionInfo* coll) bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item); - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; lara->Control.MoveAngle = (item->Animation.Velocity >= 0) ? item->Pose.Orientation.y : (item->Pose.Orientation.y + ANGLE(180.0f)); coll->Setup.LowerFloorBound = isSwamp ? NO_LOWER_BOUND : STEPUP_HEIGHT; @@ -793,7 +793,7 @@ void lara_col_run_back(ItemInfo* item, CollisionInfo* coll) lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f); item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; coll->Setup.BlockFloorSlopeDown = true; coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; @@ -1383,7 +1383,7 @@ void lara_col_death(ItemInfo* item, CollisionInfo* coll) { auto* lara = GetLaraInfo(item); - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; lara->Control.MoveAngle = item->Pose.Orientation.y; coll->Setup.LowerFloorBound = STEPUP_HEIGHT; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; @@ -1508,7 +1508,7 @@ void lara_col_walk_back(ItemInfo* item, CollisionInfo* coll) bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item); lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f); - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; coll->Setup.LowerFloorBound = (lara->Control.WaterStatus == WaterStatus::Wade) ? NO_LOWER_BOUND : STEPUP_HEIGHT; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; @@ -1842,7 +1842,7 @@ void lara_col_step_right(ItemInfo* item, CollisionInfo* coll) bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item); lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(90.0f); - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; coll->Setup.LowerFloorBound = (lara->Control.WaterStatus == WaterStatus::Wade) ? NO_LOWER_BOUND : CLICK(0.8f); coll->Setup.UpperFloorBound = -CLICK(0.8f); @@ -1931,7 +1931,7 @@ void lara_col_step_left(ItemInfo* item, CollisionInfo* coll) bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item); lara->Control.MoveAngle = item->Pose.Orientation.y - ANGLE(90.0f); - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; coll->Setup.LowerFloorBound = (lara->Control.WaterStatus == WaterStatus::Wade) ? NO_LOWER_BOUND : CLICK(0.8f); coll->Setup.UpperFloorBound = -CLICK(0.8f); @@ -2003,7 +2003,7 @@ void lara_col_roll_back(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f); - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; @@ -2072,7 +2072,7 @@ void lara_col_roll_forward(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); lara->Control.MoveAngle = item->Pose.Orientation.y; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; diff --git a/TombEngine/Game/Lara/lara_cheat.cpp b/TombEngine/Game/Lara/lara_cheat.cpp index 594210b10..b6c1f0ca8 100644 --- a/TombEngine/Game/Lara/lara_cheat.cpp +++ b/TombEngine/Game/Lara/lara_cheat.cpp @@ -69,7 +69,7 @@ void LaraCheatyBits(ItemInfo* item) { SetAnimation(item, LA_DOZY); item->Animation.VerticalVelocity = 30; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Pose.Orientation.x = ANGLE(30.0f); item->HitPoints = LARA_HEALTH_MAX; diff --git a/TombEngine/Game/Lara/lara_climb.cpp b/TombEngine/Game/Lara/lara_climb.cpp index f5cf7944b..83ccf6b00 100644 --- a/TombEngine/Game/Lara/lara_climb.cpp +++ b/TombEngine/Game/Lara/lara_climb.cpp @@ -982,7 +982,7 @@ bool LaraCheckForLetGo(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; if (TrInput & IN_ACTION && item->HitPoints > 0 || item->Animation.AnimNumber == LA_ONWATER_TO_LADDER) // Can't let go on this anim return false; @@ -993,7 +993,7 @@ bool LaraCheckForLetGo(ItemInfo* item, CollisionInfo* coll) item->Animation.Velocity = 2; item->Animation.VerticalVelocity = 1; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; lara->Control.HandStatus = HandStatus::Free; return true; } diff --git a/TombEngine/Game/Lara/lara_collide.cpp b/TombEngine/Game/Lara/lara_collide.cpp index c4303d3fd..55a8196f0 100644 --- a/TombEngine/Game/Lara/lara_collide.cpp +++ b/TombEngine/Game/Lara/lara_collide.cpp @@ -158,7 +158,7 @@ bool LaraDeflectEdgeCrawl(ItemInfo* item, CollisionInfo* coll) ShiftItem(item, coll); item->Animation.Velocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; return true; } @@ -189,7 +189,7 @@ bool LaraDeflectEdgeMonkey(ItemInfo* item, CollisionInfo* coll) item->Animation.TargetState = LS_MONKEY_IDLE; item->Animation.Velocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; return true; } @@ -385,7 +385,7 @@ void LaraResetGravityStatus(ItemInfo* item, CollisionInfo* coll) if (coll->Middle.Floor <= STEPUP_HEIGHT) { item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; } } @@ -686,7 +686,7 @@ bool TestLaraHitCeiling(CollisionInfo* coll) void SetLaraHitCeiling(ItemInfo* item, CollisionInfo* coll) { - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.Velocity = 0; item->Animation.VerticalVelocity = 0; item->Pose.Position = coll->Setup.OldPosition; diff --git a/TombEngine/Game/Lara/lara_crawl.cpp b/TombEngine/Game/Lara/lara_crawl.cpp index 497ae3f36..44a53214d 100644 --- a/TombEngine/Game/Lara/lara_crawl.cpp +++ b/TombEngine/Game/Lara/lara_crawl.cpp @@ -109,7 +109,7 @@ void lara_col_crouch_idle(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; lara->Control.KeepLow = TestLaraKeepLow(item, coll); lara->Control.IsLow = true; lara->Control.MoveAngle = item->Pose.Orientation.y; @@ -172,7 +172,7 @@ void lara_col_crouch_roll(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; lara->Control.KeepLow = TestLaraKeepLow(item, coll); lara->Control.IsLow = true; lara->Control.MoveAngle = item->Pose.Orientation.y; @@ -185,7 +185,7 @@ void lara_col_crouch_roll(ItemInfo* item, CollisionInfo* coll) GetCollisionInfo(coll, item); // TODO: With sufficient speed, Lara can still roll off ledges. This is particularly a problem in the uncommon scenario where - // she becomes Airborne within a crawlspace; collision handling will push her back very rapidly and potentially cause a softlock. @Sezz 2021.11.02 + // she becomes IsAirborne within a crawlspace; collision handling will push her back very rapidly and potentially cause a softlock. @Sezz 2021.11.02 if (LaraDeflectEdgeCrawl(item, coll)) item->Pose.Position = coll->Setup.OldPosition; @@ -472,7 +472,7 @@ void lara_col_crawl_idle(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; lara->Control.KeepLow = TestLaraKeepLow(item, coll); lara->Control.IsLow = true; lara->Control.MoveAngle = item->Pose.Orientation.y; @@ -561,7 +561,7 @@ void lara_col_crawl_forward(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; lara->Control.KeepLow = TestLaraKeepLow(item, coll); lara->Control.IsLow = true; lara->Control.MoveAngle = item->Pose.Orientation.y; @@ -649,7 +649,7 @@ void lara_col_crawl_back(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; lara->Control.KeepLow = TestLaraKeepLow(item, coll); lara->Control.IsLow = true; lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f); @@ -859,7 +859,7 @@ void lara_col_crawl_to_hang(ItemInfo* item, CollisionInfo* coll) GetCollisionInfo(coll, item); lara->Control.HandStatus = HandStatus::Busy; item->Pose.Position.y += coll->Front.Floor - GetBoundsAccurate(item)->Y1 - 20; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.Velocity = 2; item->Animation.VerticalVelocity = 1; } diff --git a/TombEngine/Game/Lara/lara_flare.cpp b/TombEngine/Game/Lara/lara_flare.cpp index 4dd249911..a94567383 100644 --- a/TombEngine/Game/Lara/lara_flare.cpp +++ b/TombEngine/Game/Lara/lara_flare.cpp @@ -378,7 +378,7 @@ void DoFlareInHand(ItemInfo* laraItem, int flareLife) if (lara->Flare.Life >= FLARE_LIFE_MAX) { // Prevent Lara from intercepting reach/jump states with flare throws. - if (laraItem->Animation.Airborne || + if (laraItem->Animation.IsAirborne || laraItem->Animation.TargetState == LS_JUMP_PREPARE || laraItem->Animation.TargetState == LS_JUMP_FORWARD) return; diff --git a/TombEngine/Game/Lara/lara_hang.cpp b/TombEngine/Game/Lara/lara_hang.cpp index 28ab45a46..dfa868c79 100644 --- a/TombEngine/Game/Lara/lara_hang.cpp +++ b/TombEngine/Game/Lara/lara_hang.cpp @@ -49,7 +49,7 @@ void lara_col_hang(ItemInfo* item, CollisionInfo* coll) { auto* lara = GetLaraInfo(item); - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; if (item->Animation.AnimNumber == LA_REACH_TO_HANG || diff --git a/TombEngine/Game/Lara/lara_helpers.cpp b/TombEngine/Game/Lara/lara_helpers.cpp index 3097bc421..2be40d8a1 100644 --- a/TombEngine/Game/Lara/lara_helpers.cpp +++ b/TombEngine/Game/Lara/lara_helpers.cpp @@ -19,7 +19,7 @@ #include "Objects/TR3/Vehicles/big_gun.h" #include "Objects/TR3/Vehicles/kayak.h" #include "Objects/TR3/Vehicles/minecart.h" -#include "Objects/TR3/Vehicles/quad.h" +#include "Objects/TR3/Vehicles/quad_bike.h" #include "Objects/TR3/Vehicles/upv.h" #include "Objects/TR4/Vehicles/jeep.h" #include "Objects/TR4/Vehicles/motorbike.h" @@ -346,6 +346,7 @@ short ModulateLaraTurnRate(short turnRate, short accelRate, short minTurnRate, s return newTurnRate * sign; } +// TODO: Make these two functions methods of LaraInfo someday. @Sezz 2022.06.26 void ModulateLaraTurnRateX(ItemInfo* item, short accelRate, short minTurnRate, short maxTurnRate) { auto* lara = GetLaraInfo(item); @@ -358,7 +359,7 @@ void ModulateLaraTurnRateY(ItemInfo* item, short accelRate, short minTurnRate, s auto* lara = GetLaraInfo(item); float axisCoeff = AxisMap[InputAxis::MoveHorizontal]; - if (item->Animation.Airborne) + if (item->Animation.IsAirborne) { int sign = std::copysign(1, axisCoeff); axisCoeff = std::min(1.2f, abs(axisCoeff)) * sign; @@ -627,7 +628,7 @@ void SetLaraVault(ItemInfo* item, CollisionInfo* coll, VaultTestResult vaultResu void SetLaraLand(ItemInfo* item, CollisionInfo* coll) { - //item->Airborne = false; // TODO: Removing this avoids an unusual landing bug Core had worked around in an obscure way. I hope to find a proper solution. @Sezz 2022.02.18 + //item->IsAirborne = false; // TODO: Removing this avoids an unusual landing bug Core had worked around in an obscure way. I hope to find a proper solution. @Sezz 2022.02.18 item->Animation.Velocity = 0; item->Animation.VerticalVelocity = 0; @@ -637,14 +638,14 @@ void SetLaraLand(ItemInfo* item, CollisionInfo* coll) void SetLaraFallAnimation(ItemInfo* item) { SetAnimation(item, LA_FALL_START); - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.VerticalVelocity = 0; } void SetLaraFallBackAnimation(ItemInfo* item) { SetAnimation(item, LA_FALL_BACK); - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.VerticalVelocity = 0; } @@ -662,7 +663,7 @@ void SetLaraMonkeyRelease(ItemInfo* item) { auto* lara = GetLaraInfo(item); - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.Velocity = 2; item->Animation.VerticalVelocity = 1; lara->Control.HandStatus = HandStatus::Free; @@ -760,7 +761,7 @@ void SetLaraCornerAnimation(ItemInfo* item, CollisionInfo* coll, bool flip) if (item->HitPoints <= 0) { SetAnimation(item, LA_FALL_START); - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.Velocity = 2; item->Animation.VerticalVelocity = 1; item->Pose.Position.y += CLICK(1); @@ -865,14 +866,14 @@ void ResetLaraFlex(ItemInfo* item, float rate) lara->ExtraTorsoRot.z = 0; } -void RumbleLaraHealthCondition(ItemInfo* lara) +void RumbleLaraHealthCondition(ItemInfo* item) { - auto* info = GetLaraInfo(lara); + auto* lara = GetLaraInfo(item); - if (lara->HitPoints > LARA_HEALTH_CRITICAL && !info->PoisonPotency) + if (item->HitPoints > LARA_HEALTH_CRITICAL && !lara->PoisonPotency) return; - bool pulse = (GlobalCounter & 0x1F) % 0x1F == 0; - if (pulse) + bool doPulse = (GlobalCounter & 0x0F) % 0x0F == 1; + if (doPulse) Rumble(0.2f, 0.1f); } diff --git a/TombEngine/Game/Lara/lara_helpers.h b/TombEngine/Game/Lara/lara_helpers.h index e5c70a8d4..04838075d 100644 --- a/TombEngine/Game/Lara/lara_helpers.h +++ b/TombEngine/Game/Lara/lara_helpers.h @@ -51,4 +51,4 @@ void SetLaraSwimDiveAnimation(ItemInfo* item); void ResetLaraLean(ItemInfo* item, float rate = 1.0f, bool resetRoll = true, bool resetPitch = true); void ResetLaraFlex(ItemInfo* item, float rate = 1.0f); -void RumbleLaraHealthCondition(ItemInfo* lara); +void RumbleLaraHealthCondition(ItemInfo* item); diff --git a/TombEngine/Game/Lara/lara_jump.cpp b/TombEngine/Game/Lara/lara_jump.cpp index ab61352f2..c354e1ab8 100644 --- a/TombEngine/Game/Lara/lara_jump.cpp +++ b/TombEngine/Game/Lara/lara_jump.cpp @@ -161,7 +161,7 @@ void lara_col_freefall(ItemInfo* item, CollisionInfo* coll) { auto* lara = GetLaraInfo(item); - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; coll->Setup.LowerCeilingBound = BAD_JUMP_CEILING; @@ -224,7 +224,7 @@ void lara_col_reach(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); if (lara->Control.Rope.Ptr == -1) - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; lara->Control.MoveAngle = item->Pose.Orientation.y; diff --git a/TombEngine/Game/Lara/lara_monkey.cpp b/TombEngine/Game/Lara/lara_monkey.cpp index d899da33b..365abc338 100644 --- a/TombEngine/Game/Lara/lara_monkey.cpp +++ b/TombEngine/Game/Lara/lara_monkey.cpp @@ -126,7 +126,7 @@ void lara_col_monkey_idle(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); lara->Control.MoveAngle = item->Pose.Orientation.y; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.UpperFloorBound = 0; coll->Setup.LowerCeilingBound = CLICK(1.25f); diff --git a/TombEngine/Game/Lara/lara_objects.cpp b/TombEngine/Game/Lara/lara_objects.cpp index 1cfcdc035..a82fe7516 100644 --- a/TombEngine/Game/Lara/lara_objects.cpp +++ b/TombEngine/Game/Lara/lara_objects.cpp @@ -277,7 +277,7 @@ void lara_as_horizontal_bar_leap(ItemInfo* item, CollisionInfo* coll) auto* lara = GetLaraInfo(item); auto* barItem = &g_Level.Items[lara->InteractedItem]; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase) { @@ -1062,7 +1062,7 @@ void lara_as_zip_line(ItemInfo* item, CollisionInfo* coll) item->Animation.Velocity = 100; item->Animation.VerticalVelocity = 40; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; lara->Control.MoveAngle = item->Pose.Orientation.y; } } diff --git a/TombEngine/Game/Lara/lara_overhang.cpp b/TombEngine/Game/Lara/lara_overhang.cpp index 141697302..87176edf2 100644 --- a/TombEngine/Game/Lara/lara_overhang.cpp +++ b/TombEngine/Game/Lara/lara_overhang.cpp @@ -457,7 +457,7 @@ void lara_as_slopeclimb(ItemInfo* item, CollisionInfo* coll) void lara_as_slopefall(ItemInfo* item, CollisionInfo* coll) { - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; if (GlobalCounter % 2) item->Pose.Orientation.x--; @@ -492,7 +492,7 @@ void lara_col_slopehang(ItemInfo* item, CollisionInfo* coll) if (!(TrInput & IN_ACTION)) { SetAnimation(item, LA_OVERHANG_HANG_DROP); - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; return; } diff --git a/TombEngine/Game/Lara/lara_slide.cpp b/TombEngine/Game/Lara/lara_slide.cpp index 75eba4efd..1caf86167 100644 --- a/TombEngine/Game/Lara/lara_slide.cpp +++ b/TombEngine/Game/Lara/lara_slide.cpp @@ -92,7 +92,7 @@ void lara_col_slide_forward(ItemInfo* item, CollisionInfo* coll) { auto* lara = GetLaraInfo(item); - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; lara->Control.MoveAngle = item->Pose.Orientation.y; coll->Setup.Height = LARA_HEIGHT_CRAWL; // HACK: Behaves better with clamps. coll->Setup.LowerFloorBound = NO_LOWER_BOUND; @@ -197,7 +197,7 @@ void lara_col_slide_back(ItemInfo* item, CollisionInfo* coll) { auto* lara = GetLaraInfo(item); - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f); coll->Setup.Height = LARA_HEIGHT_CRAWL; // HACK: Behaves better with clamps. coll->Setup.LowerFloorBound = NO_LOWER_BOUND; diff --git a/TombEngine/Game/Lara/lara_struct.h b/TombEngine/Game/Lara/lara_struct.h index 0c8a7191e..fe0151fc6 100644 --- a/TombEngine/Game/Lara/lara_struct.h +++ b/TombEngine/Game/Lara/lara_struct.h @@ -844,7 +844,7 @@ enum class WeaponAmmoType Ammo2, Ammo3, - NumAmmos + NumAmmoTypes }; enum class LaraWeaponType @@ -868,28 +868,28 @@ enum class LaraWeaponType enum LaraWeaponTypeCarried { - WTYPE_MISSING = 0x0, - WTYPE_PRESENT = 0x1, - WTYPE_SILENCER = 0x2, - WTYPE_LASERSIGHT = 0x4, - WTYPE_AMMO_1 = 0x8, - WTYPE_AMMO_2 = 0x10, - WTYPE_AMMO_3 = 0x20, - WTYPE_MASK_AMMO = WTYPE_AMMO_1 | WTYPE_AMMO_2 | WTYPE_AMMO_3, + WTYPE_MISSING = 0, + WTYPE_PRESENT = (1 << 0), + WTYPE_SILENCER = (1 << 1), + WTYPE_LASERSIGHT = (1 << 2), + WTYPE_AMMO_1 = (1 << 3), + WTYPE_AMMO_2 = (1 << 4), + WTYPE_AMMO_3 = (1 << 5), + WTYPE_MASK_AMMO = WTYPE_AMMO_1 | WTYPE_AMMO_2 | WTYPE_AMMO_3, }; -enum class HolsterSlot : int +enum class HolsterSlot { - Empty = ID_LARA_HOLSTERS, - Pistols = ID_LARA_HOLSTERS_PISTOLS, - Uzis = ID_LARA_HOLSTERS_UZIS, - Revolver = ID_LARA_HOLSTERS_REVOLVER, - Shotgun = ID_SHOTGUN_ANIM, - HK = ID_HK_ANIM, - Harpoon = ID_HARPOON_ANIM, - Crowssbow = ID_CROSSBOW_ANIM, + Empty = ID_LARA_HOLSTERS, + Pistols = ID_LARA_HOLSTERS_PISTOLS, + Uzis = ID_LARA_HOLSTERS_UZIS, + Revolver = ID_LARA_HOLSTERS_REVOLVER, + Shotgun = ID_SHOTGUN_ANIM, + HK = ID_HK_ANIM, + Harpoon = ID_HARPOON_ANIM, + Crowssbow = ID_CROSSBOW_ANIM, GrenadeLauncher = ID_GRENADE_ANIM, - RocketLauncher = ID_ROCKET_ANIM, + RocketLauncher = ID_ROCKET_ANIM }; // TODO: Unused. @@ -1064,7 +1064,7 @@ struct HolsterInfo struct CarriedWeaponInfo { bool Present; - Ammo Ammo[(int)WeaponAmmoType::NumAmmos]; + Ammo Ammo[(int)WeaponAmmoType::NumAmmoTypes]; WeaponAmmoType SelectedAmmo; // WeaponAmmoType_enum bool HasLasersight; // TODO: Duplicated in LaraInventoryData. bool HasSilencer; // TODO: Duplicated in LaraInventoryData. @@ -1095,6 +1095,7 @@ struct TorchData bool IsLit; }; +// TODO: Someone's abandoned dairy feature. #define MaxDiaryPages 64 #define MaxStringsPerPage 8 @@ -1132,7 +1133,7 @@ struct LaraInventoryData bool HasBinoculars; bool HasCrowbar; bool HasTorch; - bool HasLasersight; // TODO: Duplicated in CarriedWeaponInfo. + bool HasLasersight; // TODO: Duplicated in CarriedWeaponInfo. bool HasSilencer; // TODO: Duplicated in CarriedWeaponInfo. int TotalSmallMedipacks; diff --git a/TombEngine/Game/Lara/lara_tests.cpp b/TombEngine/Game/Lara/lara_tests.cpp index 2af2df1d1..765a94371 100644 --- a/TombEngine/Game/Lara/lara_tests.cpp +++ b/TombEngine/Game/Lara/lara_tests.cpp @@ -177,7 +177,7 @@ bool TestLaraHang(ItemInfo* item, CollisionInfo* coll) { SetAnimation(item, LA_FALL_START); item->Pose.Position.y += CLICK(1); - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.Velocity = 2; item->Animation.VerticalVelocity = 1; lara->Control.HandStatus = HandStatus::Free; @@ -248,7 +248,7 @@ bool TestLaraHang(ItemInfo* item, CollisionInfo* coll) item->Pose.Position.x += coll->Shift.x; item->Pose.Position.y += GetBoundsAccurate(item)->Y2 * 1.8f; item->Pose.Position.z += coll->Shift.z; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.Velocity = 2; item->Animation.VerticalVelocity = 1; lara->Control.HandStatus = HandStatus::Free; @@ -271,7 +271,7 @@ bool TestLaraHangJump(ItemInfo* item, CollisionInfo* coll) ResetLaraFlex(item); item->Animation.Velocity = 0; item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Pose.Position.y += coll->Middle.Ceiling + (LARA_HEIGHT_MONKEY - coll->Setup.Height); lara->Control.HandStatus = HandStatus::Busy; return true; @@ -318,9 +318,9 @@ bool TestLaraHangJump(ItemInfo* item, CollisionInfo* coll) else SnapItemToLedge(item, coll, 0.2f); + item->Animation.IsAirborne = true; item->Animation.Velocity = 2; item->Animation.VerticalVelocity = 1; - item->Animation.Airborne = true; lara->Control.TurnRate = 0; lara->Control.HandStatus = HandStatus::Busy; return true; @@ -338,7 +338,7 @@ bool TestLaraHangJumpUp(ItemInfo* item, CollisionInfo* coll) SetAnimation(item, LA_JUMP_UP_TO_MONKEY); item->Animation.Velocity = 0; item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Pose.Position.y += coll->Middle.Ceiling + (LARA_HEIGHT_MONKEY - coll->Setup.Height); lara->Control.HandStatus = HandStatus::Busy; return true; @@ -374,7 +374,7 @@ bool TestLaraHangJumpUp(ItemInfo* item, CollisionInfo* coll) item->Animation.Velocity = 0; item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; lara->Control.HandStatus = HandStatus::Busy; lara->ExtraTorsoRot = Vector3Shrt(); return true; @@ -908,7 +908,7 @@ bool TestLaraWaterStepOut(ItemInfo* item, CollisionInfo* coll) item->Pose.Orientation.z = 0; item->Animation.Velocity = 0; item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; lara->Control.WaterStatus = WaterStatus::Wade; return true; @@ -990,7 +990,7 @@ bool TestLaraWaterClimbOut(ItemInfo* item, CollisionInfo* coll) item->Pose.Position.y += frontFloor - 5; item->Animation.ActiveState = LS_ONWATER_EXIT; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.Velocity = 0; item->Animation.VerticalVelocity = 0; lara->Control.TurnRate = 0; @@ -1062,7 +1062,7 @@ bool TestLaraLadderClimbOut(ItemInfo* item, CollisionInfo* coll) // NEW function item->Pose.Orientation.z = 0; item->Animation.Velocity = 0; item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; lara->Control.TurnRate = 0; lara->Control.HandStatus = HandStatus::Busy; lara->Control.WaterStatus = WaterStatus::Dry; @@ -1090,7 +1090,7 @@ void TestLaraWaterDepth(ItemInfo* item, CollisionInfo* coll) item->Pose.Position.y = probe.Position.Floor; item->Pose.Orientation.x = 0; item->Pose.Orientation.z = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.Velocity = 0; item->Animation.VerticalVelocity = 0; lara->Control.WaterStatus = WaterStatus::Wade; @@ -1255,7 +1255,8 @@ bool TestLaraLand(ItemInfo* item, CollisionInfo* coll) { int heightFromFloor = GetCollision(item).Position.Floor - item->Pose.Position.y; - if (item->Animation.Airborne && item->Animation.VerticalVelocity >= 0 && + if (item->Animation.IsAirborne && + item->Animation.VerticalVelocity >= 0 && (heightFromFloor <= item->Animation.VerticalVelocity || TestEnvironment(ENV_FLAG_SWAMP, item))) { diff --git a/TombEngine/Game/animation.cpp b/TombEngine/Game/animation.cpp index d17ab8b24..b1264f289 100644 --- a/TombEngine/Game/animation.cpp +++ b/TombEngine/Game/animation.cpp @@ -51,7 +51,7 @@ void AnimateLara(ItemInfo* item) case COMMAND_JUMP_VELOCITY: item->Animation.VerticalVelocity = *(cmd++); item->Animation.Velocity = *(cmd++); - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; if (lara->Control.CalculatedJumpVelocity) { item->Animation.VerticalVelocity = lara->Control.CalculatedJumpVelocity; @@ -150,7 +150,7 @@ void AnimateLara(ItemInfo* item) lateral += anim->Xacceleration * (item->Animation.FrameNumber - anim->frameBase); item->Animation.LateralVelocity = lateral >>= 16; - if (item->Animation.Airborne) + if (item->Animation.IsAirborne) { if (TestEnvironment(ENV_FLAG_SWAMP, item)) { @@ -158,7 +158,7 @@ void AnimateLara(ItemInfo* item) if (abs(item->Animation.Velocity) < 8) { item->Animation.Velocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; } if (item->Animation.VerticalVelocity > 128) item->Animation.VerticalVelocity /= 2; @@ -241,7 +241,7 @@ void AnimateItem(ItemInfo* item) case COMMAND_JUMP_VELOCITY: item->Animation.VerticalVelocity = *(cmd++); item->Animation.Velocity = *(cmd++); - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; break; case COMMAND_DEACTIVATE: @@ -348,7 +348,7 @@ void AnimateItem(ItemInfo* item) int lateral = 0; - if (item->Animation.Airborne) + if (item->Animation.IsAirborne) { item->Animation.VerticalVelocity += (item->Animation.VerticalVelocity >= 128 ? 1 : 6); item->Pose.Position.y += item->Animation.VerticalVelocity; diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp index 596bc5324..a5459d516 100644 --- a/TombEngine/Game/collision/collide_item.cpp +++ b/TombEngine/Game/collision/collide_item.cpp @@ -38,11 +38,11 @@ void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionIn int collidedBits = TestCollision(item, laraItem); if (collidedBits) { - short oldRot = item->Pose.Orientation.y; + short oldYOrient = item->Pose.Orientation.y; item->Pose.Orientation.y = 0; GetSpheres(item, CreatureSpheres, SPHERES_SPACE_WORLD, Matrix::Identity); - item->Pose.Orientation.y = oldRot; + item->Pose.Orientation.y = oldYOrient; int deadlyBits = *((int*)&item->ItemFlags[0]); auto* sphere = &CreatureSpheres[0]; @@ -821,7 +821,8 @@ bool ItemPushStatic(ItemInfo* item, MESH_INFO* mesh, CollisionInfo* coll) // pre if (coll->CollisionType == CT_NONE) { coll->Setup.OldPosition = item->Pose.Position; - UpdateItemRoom(item, -10); + if (item->IsLara()) + UpdateItemRoom(item, -10); } else { @@ -830,10 +831,11 @@ bool ItemPushStatic(ItemInfo* item, MESH_INFO* mesh, CollisionInfo* coll) // pre } // If Lara is in the process of aligning to an object, cancel it. - if (item == LaraItem && Lara.Control.IsMoving && Lara.Control.Count.PositionAdjust > (LARA_POSITION_ADJUST_MAX_TIME / 6)) + if (item->IsLara() && Lara.Control.IsMoving && Lara.Control.Count.PositionAdjust > (LARA_POSITION_ADJUST_MAX_TIME / 6)) { - Lara.Control.IsMoving = false; - Lara.Control.HandStatus = HandStatus::Free; + auto* lara = GetLaraInfo(item); + lara->Control.IsMoving = false; + lara->Control.HandStatus = HandStatus::Free; } return true; @@ -1672,64 +1674,6 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, ItemNewRoom(itemNumber, collResult.RoomNumber); } -void DoVehicleCollision(ItemInfo* vehicle, int radius) -{ - CollisionInfo coll = {}; - coll.Setup.Radius = radius * 0.8f; // HACK: Most vehicles use radius larger than needed. - coll.Setup.UpperCeilingBound = MAX_HEIGHT; // HACK: this needs to be set to prevent GCI result interference. - coll.Setup.OldPosition = vehicle->Pose.Position; - coll.Setup.EnableObjectPush = true; - - DoObjectCollision(vehicle, &coll); -} - -int DoVehicleWaterMovement(ItemInfo* vehicle, ItemInfo* lara, int currentVelocity, int radius, short* angle) -{ - if (TestEnvironment(ENV_FLAG_WATER, vehicle) || - TestEnvironment(ENV_FLAG_SWAMP, vehicle)) - { - auto waterDepth = (float)GetWaterDepth(vehicle); - auto waterHeight = vehicle->Pose.Position.y - GetWaterHeight(vehicle); - - // HACK: Sometimes quadbike test position may end up under non-portal ceiling block. - // GetWaterDepth returns DEEP_WATER constant in that case, which is too large for our needs. - if (waterDepth == DEEP_WATER) - waterDepth = VEHICLE_MAX_WATER_HEIGHT; - - if (waterDepth <= VEHICLE_MAX_WATER_HEIGHT) - { - bool isWater = TestEnvironment(ENV_FLAG_WATER, vehicle); - - if (currentVelocity != 0) - { - auto coeff = isWater ? VEHICLE_WATER_VEL_COEFFICIENT : VEHICLE_SWAMP_VEL_COEFFICIENT; - currentVelocity -= std::copysign(currentVelocity * ((waterDepth / VEHICLE_MAX_WATER_HEIGHT) / coeff), currentVelocity); - - if (TEN::Math::Random::GenerateInt(0, 32) > 28) - SoundEffect(SFX_TR4_LARA_WADE, &PHD_3DPOS(vehicle->Pose.Position), SoundEnvironment::Land, isWater ? 0.8f : 0.7f); - - if (isWater) - TEN::Effects::TriggerSpeedboatFoam(vehicle, Vector3(0, -waterDepth / 2.0f, -radius)); - } - - if (*angle != 0) - { - auto coeff = isWater ? VEHICLE_WATER_TURN_COEFFICIENT : VEHICLE_SWAMP_TURN_COEFFICIENT; - *angle -= *angle * ((waterDepth / VEHICLE_MAX_WATER_HEIGHT) / coeff); - } - } - else - { - if (waterDepth > VEHICLE_MAX_WATER_HEIGHT && waterHeight > VEHICLE_MAX_WATER_HEIGHT) - ExplodeVehicle(lara, vehicle); - else if (TEN::Math::Random::GenerateInt(0, 32) > 25) - Splash(vehicle); - } - } - - return currentVelocity; -} - void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll) { laraItem->HitStatus = false; diff --git a/TombEngine/Game/collision/collide_item.h b/TombEngine/Game/collision/collide_item.h index 07e1eb6d9..0fa697ebb 100644 --- a/TombEngine/Game/collision/collide_item.h +++ b/TombEngine/Game/collision/collide_item.h @@ -11,12 +11,6 @@ constexpr auto MAX_COLLIDED_OBJECTS = 1024; constexpr auto ITEM_RADIUS_YMAX = SECTOR(3); constexpr auto VEHICLE_COLLISION_TERMINAL_VELOCITY = 30; -constexpr auto VEHICLE_SINK_SPEED = 15; -constexpr auto VEHICLE_MAX_WATER_HEIGHT = CLICK(2.5f); -constexpr auto VEHICLE_WATER_VEL_COEFFICIENT = 16.0f; -constexpr auto VEHICLE_WATER_TURN_COEFFICIENT = 10.0f; -constexpr auto VEHICLE_SWAMP_VEL_COEFFICIENT = 8.0f; -constexpr auto VEHICLE_SWAMP_TURN_COEFFICIENT = 6.0f; extern BOUNDING_BOX GlobalCollisionBounds; extern ItemInfo* CollidedItems[MAX_COLLIDED_OBJECTS]; @@ -62,5 +56,3 @@ void TrapCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, int zv); void DoObjectCollision(ItemInfo* item, CollisionInfo* coll); -void DoVehicleCollision(ItemInfo* vehicle, int radius); -int DoVehicleWaterMovement(ItemInfo* vehicle, ItemInfo* lara, int currentVelocity, int radius, short* angle); diff --git a/TombEngine/Game/collision/collide_room.cpp b/TombEngine/Game/collision/collide_room.cpp index b9683d9cc..3e96e1094 100644 --- a/TombEngine/Game/collision/collide_room.cpp +++ b/TombEngine/Game/collision/collide_room.cpp @@ -123,7 +123,7 @@ CollisionResult GetCollision(ItemInfo* item) } // Overload used to probe point/room collision parameters from a given item's position. -CollisionResult GetCollision(ItemInfo* item, short angle, float forward, float vertical, float lateral) +CollisionResult GetCollision(ItemInfo* item, short angle, float forward, float up, float right) { short tempRoomNumber = item->RoomNumber; @@ -133,18 +133,18 @@ CollisionResult GetCollision(ItemInfo* item, short angle, float forward, float v item->Location : ROOM_VECTOR{ GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &tempRoomNumber)->Room, item->Pose.Position.y }; - auto point = TranslateVector(item->Pose.Position, angle, forward, vertical, lateral); + auto point = TranslateVector(item->Pose.Position, angle, forward, up, right); int adjacentRoomNumber = GetRoom(location, item->Pose.Position.x, point.y, item->Pose.Position.z).roomNumber; return GetCollision(point.x, point.y, point.z, adjacentRoomNumber); } // Overload used to probe point/room collision parameters from a given position. -CollisionResult GetCollision(Vector3Int pos, int roomNumber, short angle, float forward, float vertical, float lateral) +CollisionResult GetCollision(Vector3Int pos, int roomNumber, short angle, float forward, float up, float right) { short tempRoomNumber = roomNumber; auto location = ROOM_VECTOR{ GetFloor(pos.x, pos.y, pos.z, &tempRoomNumber)->Room, pos.y }; - auto point = TranslateVector(pos, angle, forward, vertical, lateral); + auto point = TranslateVector(pos, angle, forward, up, right); int adjacentRoomNumber = GetRoom(location, pos.x, point.y, pos.z).roomNumber; return GetCollision(point.x, point.y, point.z, adjacentRoomNumber); } @@ -173,7 +173,7 @@ CollisionResult GetCollision(FloorInfo* floor, int x, int y, int z) CollisionResult result = {}; // Record coordinates. - result.Coordinates = Vector3(x, y, z); + result.Coordinates = Vector3Int(x, y, z); // Return provided block into result as itself. result.Block = floor; diff --git a/TombEngine/Game/collision/collide_room.h b/TombEngine/Game/collision/collide_room.h index 62e91e933..f7de8ffee 100644 --- a/TombEngine/Game/collision/collide_room.h +++ b/TombEngine/Game/collision/collide_room.h @@ -54,7 +54,7 @@ struct CollisionPosition struct CollisionResult { - Vector3 Coordinates; + Vector3Int Coordinates; int RoomNumber; FloorInfo* Block; @@ -123,8 +123,8 @@ struct CollisionInfo [[nodiscard]] bool TestItemRoomCollisionAABB(ItemInfo* item); CollisionResult GetCollision(ItemInfo* item); -CollisionResult GetCollision(ItemInfo* item, short angle, float forward, float vertical = 0.0f, float lateral = 0.0f); -CollisionResult GetCollision(Vector3Int pos, int roomNumber, short angle, float forward, float vertical = 0.0f, float lateral = 0.0f); +CollisionResult GetCollision(ItemInfo* item, short angle, float forward, float up = 0.0f, float right = 0.0f); +CollisionResult GetCollision(Vector3Int pos, int roomNumber, short angle, float forward, float up = 0.0f, float right = 0.0f); CollisionResult GetCollision(int x, int y, int z, short roomNumber); CollisionResult GetCollision(FloorInfo* floor, int x, int y, int z); @@ -162,3 +162,4 @@ bool TestEnvironment(RoomEnvFlags environmentType, ItemInfo* item); bool TestEnvironment(RoomEnvFlags environmentType, int roomNumber); bool TestEnvironment(RoomEnvFlags environmentType, ROOM_INFO* room); bool TestEnvironmentFlags(RoomEnvFlags environmentType, int flags); + diff --git a/TombEngine/Game/collision/sphere.h b/TombEngine/Game/collision/sphere.h index 72c787ece..429a60530 100644 --- a/TombEngine/Game/collision/sphere.h +++ b/TombEngine/Game/collision/sphere.h @@ -17,5 +17,5 @@ struct SPHERE extern SPHERE LaraSpheres[MAX_SPHERES]; extern SPHERE CreatureSpheres[MAX_SPHERES]; -int TestCollision(ItemInfo* item, ItemInfo* l); +int TestCollision(ItemInfo* item, ItemInfo* laraItem); int GetSpheres(ItemInfo* item, SPHERE* ptr, int worldSpace, Matrix local); diff --git a/TombEngine/Game/control/box.cpp b/TombEngine/Game/control/box.cpp index 2e8f1e97d..7a7410bd2 100644 --- a/TombEngine/Game/control/box.cpp +++ b/TombEngine/Game/control/box.cpp @@ -262,7 +262,7 @@ void CreatureKill(ItemInfo* item, int killAnim, int killState, int laraKillState LaraItem->Animation.TargetState = laraKillState; LaraItem->Pose = item->Pose; - LaraItem->Animation.Airborne = false; + LaraItem->Animation.IsAirborne = false; LaraItem->Animation.Velocity = 0; LaraItem->Animation.VerticalVelocity = 0; diff --git a/TombEngine/Game/itemdata/itemdata.h b/TombEngine/Game/itemdata/itemdata.h index be1a51c05..225afe80e 100644 --- a/TombEngine/Game/itemdata/itemdata.h +++ b/TombEngine/Game/itemdata/itemdata.h @@ -6,12 +6,12 @@ #include "Game/itemdata/creature_info.h" #include "Game/itemdata/door_data.h" #include "Game/Lara/lara_struct.h" -#include "Objects/TR2/Vehicles/boat_info.h" +#include "Objects/TR2/Vehicles/speedboat_info.h" #include "Objects/TR2/Vehicles/skidoo_info.h" #include "Objects/TR3/Vehicles/minecart_info.h" #include "Objects/TR3/Vehicles/big_gun_info.h" #include "Objects/TR3/Vehicles/kayak_info.h" -#include "Objects/TR3/Vehicles/quad_info.h" +#include "Objects/TR3/Vehicles/quad_bike_info.h" #include "Objects/TR3/Vehicles/rubber_boat_info.h" #include "Objects/TR3/Vehicles/upv_info.h" #include "Objects/TR4/Vehicles/jeep_info.h" @@ -46,23 +46,23 @@ class ITEM_DATA double, long double, std::array, + GameVector, + DOOR_DATA, + PushableInfo, ItemInfo*, + LaraInfo*, CreatureInfo, + WraithInfo, LaserHeadInfo, - QuadInfo, + QuadBikeInfo, BigGunInfo, MotorbikeInfo, JeepInfo, - LaraInfo*, KayakInfo, - DOOR_DATA, SkidooInfo, UPVInfo, - SpeedBoatInfo, - GameVector, - WraithInfo, + SpeedboatInfo, RubberBoatInfo, - PushableInfo, MinecartInfo > data; public: diff --git a/TombEngine/Game/items.cpp b/TombEngine/Game/items.cpp index e5c99d12b..d2747f6f8 100644 --- a/TombEngine/Game/items.cpp +++ b/TombEngine/Game/items.cpp @@ -462,7 +462,7 @@ void InitialiseItem(short itemNumber) item->Active = false; item->Status = ITEM_NOT_ACTIVE; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->HitStatus = false; item->Collidable = true; item->LookedAt = false; diff --git a/TombEngine/Game/items.h b/TombEngine/Game/items.h index 99274258d..95354b3f0 100644 --- a/TombEngine/Game/items.h +++ b/TombEngine/Game/items.h @@ -22,31 +22,31 @@ constexpr auto NUM_ITEMS = 1024; enum AIObjectType { - NO_AI = 0x0000, - GUARD = 0x0001, - AMBUSH = 0x0002, - PATROL1 = 0x0004, - MODIFY = 0x0008, - FOLLOW = 0x0010, - PATROL2 = 0x0020, + NO_AI = 0, + GUARD = (1 << 0), + AMBUSH = (1 << 1), + PATROL1 = (1 << 2), + MODIFY = (1 << 3), + FOLLOW = (1 << 4), + PATROL2 = (1 << 5), ALL_AIOBJ = (GUARD | AMBUSH | PATROL1 | MODIFY | FOLLOW | PATROL2) }; enum ItemStatus { - ITEM_NOT_ACTIVE = 0, - ITEM_ACTIVE = 1, + ITEM_NOT_ACTIVE = 0, + ITEM_ACTIVE = 1, ITEM_DEACTIVATED = 2, - ITEM_INVISIBLE = 3 + ITEM_INVISIBLE = 3 }; enum ItemFlags { - IFLAG_CLEAR_BODY = (1 << 7), // 0x0080 - IFLAG_INVISIBLE = (1 << 8), // 0x0100 - IFLAG_REVERSE = (1 << 14), // 0x4000 - IFLAG_KILLED = (1 << 15), // 0x8000 - IFLAG_ACTIVATION_MASK = 0x3E00 // bits 9-13 + IFLAG_CLEAR_BODY = (1 << 7), // 0x0080 + IFLAG_INVISIBLE = (1 << 8), // 0x0100 + IFLAG_REVERSE = (1 << 14), // 0x4000 + IFLAG_KILLED = (1 << 15), // 0x8000 + IFLAG_ACTIVATION_MASK = 0x3E00 // bits 9-13 }; constexpr unsigned int ALL_JOINT_BITS = UINT_MAX; @@ -67,7 +67,7 @@ struct EntityAnimationData int TargetState; int RequiredState; // TODO: Phase out this weird feature. - bool Airborne; + bool IsAirborne; int Velocity; int VerticalVelocity; int LateralVelocity; @@ -85,8 +85,10 @@ struct ItemInfo ITEM_DATA Data; EntityAnimationData Animation; - PHD_3DPOS Pose; PHD_3DPOS StartPose; + PHD_3DPOS Pose; + ROOM_VECTOR Location; + short RoomNumber; int Floor; int HitPoints; @@ -95,8 +97,6 @@ struct ItemInfo bool Collidable; bool InDrawRoom; - ROOM_VECTOR Location; - short RoomNumber; int BoxNumber; int Timer; short Shade; diff --git a/TombEngine/Game/savegame.cpp b/TombEngine/Game/savegame.cpp index b95fa54dc..1abcd4546 100644 --- a/TombEngine/Game/savegame.cpp +++ b/TombEngine/Game/savegame.cpp @@ -339,7 +339,7 @@ bool SaveGame::Save(int slot) CarriedWeaponInfo* info = &Lara.Weapons[i]; std::vector> ammos; - for (int j = 0; j < (int)WeaponAmmoType::NumAmmos; j++) + for (int j = 0; j < (int)WeaponAmmoType::NumAmmoTypes; j++) { Save::AmmoInfoBuilder ammo{ fbb }; ammo.add_count(info->Ammo[j].getCount()); @@ -466,9 +466,9 @@ bool SaveGame::Save(int slot) creatureBuilder.add_ai_target_number(creature->AITargetNumber); creatureOffset = creatureBuilder.Finish(); } - else if (itemToSerialize.Data.is()) + else if (itemToSerialize.Data.is()) { - auto quad = (QuadInfo*)itemToSerialize.Data; + auto quad = (QuadBikeInfo*)itemToSerialize.Data; Save::QuadBikeBuilder quadBuilder{ fbb }; @@ -496,13 +496,13 @@ bool SaveGame::Save(int slot) Save::UPVBuilder upvBuilder{ fbb }; - upvBuilder.add_fan_rot(upv->FanRot); + upvBuilder.add_fan_rot(upv->TurbineRotation); upvBuilder.add_flags(upv->Flags); upvBuilder.add_harpoon_left(upv->HarpoonLeft); upvBuilder.add_harpoon_timer(upv->HarpoonTimer); - upvBuilder.add_rot(upv->Rot); + upvBuilder.add_rot(upv->TurnRate.y); upvBuilder.add_velocity(upv->Velocity); - upvBuilder.add_x_rot(upv->XRot); + upvBuilder.add_x_rot(upv->TurnRate.x); upvOffset = upvBuilder.Finish(); } else if (itemToSerialize.Data.is()) @@ -534,15 +534,15 @@ bool SaveGame::Save(int slot) kayakBuilder.add_flags(kayak->Flags); kayakBuilder.add_forward(kayak->Forward); kayakBuilder.add_front_vertical_velocity(kayak->FrontVerticalVelocity); - kayakBuilder.add_left_right_count(kayak->LeftRightCount); + kayakBuilder.add_left_right_count(kayak->LeftRightPaddleCount); kayakBuilder.add_left_vertical_velocity(kayak->LeftVerticalVelocity); kayakBuilder.add_old_pos(&Save::Position( - kayak->OldPos.Position.x, - kayak->OldPos.Position.y, - kayak->OldPos.Position.z, - kayak->OldPos.Orientation.x, - kayak->OldPos.Orientation.y, - kayak->OldPos.Orientation.z)); + kayak->OldPose.Position.x, + kayak->OldPose.Position.y, + kayak->OldPose.Position.z, + kayak->OldPose.Orientation.x, + kayak->OldPose.Orientation.y, + kayak->OldPose.Orientation.z)); kayakBuilder.add_right_vertical_velocity(kayak->RightVerticalVelocity); kayakBuilder.add_true_water(kayak->TrueWater); kayakBuilder.add_turn(kayak->Turn); @@ -601,7 +601,7 @@ bool SaveGame::Save(int slot) serializedItem.add_triggered((itemToSerialize.Flags & (TRIGGERED | CODE_BITS | ONESHOT)) != 0); serializedItem.add_active(itemToSerialize.Active); serializedItem.add_status(itemToSerialize.Status); - serializedItem.add_airborne(itemToSerialize.Animation.Airborne); + serializedItem.add_is_airborne(itemToSerialize.Animation.IsAirborne); serializedItem.add_hit_stauts(itemToSerialize.HitStatus); serializedItem.add_ai_bits(itemToSerialize.AIBits); serializedItem.add_collidable(itemToSerialize.Collidable); @@ -614,7 +614,7 @@ bool SaveGame::Save(int slot) serializedItem.add_data_type(Save::ItemData::Creature); serializedItem.add_data(creatureOffset.Union()); } - else if (itemToSerialize.Data.is()) + else if (itemToSerialize.Data.is()) { serializedItem.add_data_type(Save::ItemData::QuadBike); serializedItem.add_data(quadOffset.Union()); @@ -1284,7 +1284,7 @@ bool SaveGame::Load(int slot) item->HitStatus = savedItem->hit_stauts(); item->Status = savedItem->status(); item->AIBits = savedItem->ai_bits(); - item->Animation.Airborne = savedItem->airborne(); + item->Animation.IsAirborne = savedItem->is_airborne(); item->Collidable = savedItem->collidable(); item->LookedAt = savedItem->looked_at(); @@ -1346,75 +1346,75 @@ bool SaveGame::Load(int slot) creature->Tosspad = savedCreature->tosspad(); SetBaddyTarget(i, savedCreature->ai_target_number()); } - else if (item->Data.is()) + else if (item->Data.is()) { - auto quad = (QuadInfo*)item->Data; - auto savedQuad = (Save::QuadBike*)savedItem->data(); + auto* quadBike = (QuadBikeInfo*)item->Data; + auto* savedQuad = (Save::QuadBike*)savedItem->data(); - quad->CanStartDrift = savedQuad->can_start_drift(); - quad->DriftStarting = savedQuad->drift_starting(); - quad->EngineRevs = savedQuad->engine_revs(); - quad->ExtraRotation = savedQuad->extra_rotation(); - quad->Flags = savedQuad->flags(); - quad->FrontRot = savedQuad->front_rot(); - quad->LeftVerticalVelocity = savedQuad->left_vertical_velocity(); - quad->MomentumAngle = savedQuad->momentum_angle(); - quad->NoDismount = savedQuad->no_dismount(); - quad->Pitch = savedQuad->pitch(); - quad->RearRot = savedQuad->rear_rot(); - quad->Revs = savedQuad->revs(); - quad->RightVerticalVelocity = savedQuad->right_vertical_velocity(); - quad->SmokeStart = savedQuad->smoke_start(); - quad->TurnRate = savedQuad->turn_rate(); - quad->Velocity = savedQuad->velocity(); + quadBike->CanStartDrift = savedQuad->can_start_drift(); + quadBike->DriftStarting = savedQuad->drift_starting(); + quadBike->EngineRevs = savedQuad->engine_revs(); + quadBike->ExtraRotation = savedQuad->extra_rotation(); + quadBike->Flags = savedQuad->flags(); + quadBike->FrontRot = savedQuad->front_rot(); + quadBike->LeftVerticalVelocity = savedQuad->left_vertical_velocity(); + quadBike->MomentumAngle = savedQuad->momentum_angle(); + quadBike->NoDismount = savedQuad->no_dismount(); + quadBike->Pitch = savedQuad->pitch(); + quadBike->RearRot = savedQuad->rear_rot(); + quadBike->Revs = savedQuad->revs(); + quadBike->RightVerticalVelocity = savedQuad->right_vertical_velocity(); + quadBike->SmokeStart = savedQuad->smoke_start(); + quadBike->TurnRate = savedQuad->turn_rate(); + quadBike->Velocity = savedQuad->velocity(); } else if (item->Data.is()) { - auto upv = (UPVInfo*)item->Data; - auto savedUpv = (Save::UPV*)savedItem->data(); + auto* upv = (UPVInfo*)item->Data; + auto* savedUpv = (Save::UPV*)savedItem->data(); - upv->FanRot = savedUpv->fan_rot(); + upv->TurbineRotation = savedUpv->fan_rot(); upv->Flags = savedUpv->flags(); upv->HarpoonLeft = savedUpv->harpoon_left(); upv->HarpoonTimer = savedUpv->harpoon_timer(); - upv->Rot = savedUpv->rot(); + upv->TurnRate.y = savedUpv->rot(); upv->Velocity = savedUpv->velocity(); - upv->XRot = savedUpv->x_rot(); + upv->TurnRate.x = savedUpv->x_rot(); } else if (item->Data.is()) { - auto mine = (MinecartInfo*)item->Data; - auto savedMine = (Save::Minecart*)savedItem->data(); + auto* minecart = (MinecartInfo*)item->Data; + auto* savedMine = (Save::Minecart*)savedItem->data(); - mine->Flags = savedMine->flags(); - mine->FloorHeightFront = savedMine->floor_height_front(); - mine->FloorHeightMiddle = savedMine->floor_height_middle(); - mine->Gradient = savedMine->gradient(); - mine->StopDelay = savedMine->stop_delay(); - mine->TurnLen = savedMine->turn_len(); - mine->TurnRot = savedMine->turn_rot(); - mine->TurnX = savedMine->turn_x(); - mine->TurnZ = savedMine->turn_z(); - mine->Velocity = savedMine->velocity(); - mine->VerticalVelocity = savedMine->vertical_velocity(); + minecart->Flags = savedMine->flags(); + minecart->FloorHeightFront = savedMine->floor_height_front(); + minecart->FloorHeightMiddle = savedMine->floor_height_middle(); + minecart->Gradient = savedMine->gradient(); + minecart->StopDelay = savedMine->stop_delay(); + minecart->TurnLen = savedMine->turn_len(); + minecart->TurnRot = savedMine->turn_rot(); + minecart->TurnX = savedMine->turn_x(); + minecart->TurnZ = savedMine->turn_z(); + minecart->Velocity = savedMine->velocity(); + minecart->VerticalVelocity = savedMine->vertical_velocity(); } else if (item->Data.is()) { - auto kayak = (KayakInfo*)item->Data; - auto savedKayak = (Save::Kayak*)savedItem->data(); + auto* kayak = (KayakInfo*)item->Data; + auto* savedKayak = (Save::Kayak*)savedItem->data(); kayak->CurrentStartWake = savedKayak->flags(); kayak->Flags = savedKayak->flags(); kayak->Forward = savedKayak->forward(); kayak->FrontVerticalVelocity = savedKayak->front_vertical_velocity(); - kayak->LeftRightCount = savedKayak->left_right_count(); + kayak->LeftRightPaddleCount = savedKayak->left_right_count(); kayak->LeftVerticalVelocity = savedKayak->left_vertical_velocity(); - kayak->OldPos.Position.x = savedKayak->old_pos()->x_pos(); - kayak->OldPos.Position.y = savedKayak->old_pos()->y_pos(); - kayak->OldPos.Position.z = savedKayak->old_pos()->z_pos(); - kayak->OldPos.Orientation.x = savedKayak->old_pos()->x_rot(); - kayak->OldPos.Orientation.y = savedKayak->old_pos()->y_rot(); - kayak->OldPos.Orientation.z = savedKayak->old_pos()->z_rot(); + kayak->OldPose.Position.x = savedKayak->old_pos()->x_pos(); + kayak->OldPose.Position.y = savedKayak->old_pos()->y_pos(); + kayak->OldPose.Position.z = savedKayak->old_pos()->z_pos(); + kayak->OldPose.Orientation.x = savedKayak->old_pos()->x_rot(); + kayak->OldPose.Orientation.y = savedKayak->old_pos()->y_rot(); + kayak->OldPose.Orientation.z = savedKayak->old_pos()->z_rot(); kayak->RightVerticalVelocity = savedKayak->right_vertical_velocity(); kayak->TrueWater = savedKayak->true_water(); kayak->Turn = savedKayak->turn(); @@ -1425,21 +1425,21 @@ bool SaveGame::Load(int slot) } else if (savedItem->data_type() == Save::ItemData::Short) { - auto data = savedItem->data(); - auto savedData = (Save::Short*)data; + auto* data = savedItem->data(); + auto* savedData = (Save::Short*)data; item->Data = savedData->scalar(); } else if (savedItem->data_type() == Save::ItemData::Int) { - auto data = savedItem->data(); - auto savedData = (Save::Int*)data; + auto* data = savedItem->data(); + auto* savedData = (Save::Int*)data; item->Data = savedData->scalar(); } } for (int i = 0; i < s->particles()->size(); i++) { - auto particleInfo = s->particles()->Get(i); + auto* particleInfo = s->particles()->Get(i); auto* particle = &Particles[i]; particle->x = particleInfo->x(); @@ -1483,7 +1483,7 @@ bool SaveGame::Load(int slot) for (int i = 0; i < s->bats()->size(); i++) { - auto batInfo = s->bats()->Get(i); + auto* batInfo = s->bats()->Get(i); auto* bat = &Bats[i]; bat->On = batInfo->on(); @@ -1515,7 +1515,7 @@ bool SaveGame::Load(int slot) for (int i = 0; i < s->spiders()->size(); i++) { - auto spiderInfo = s->spiders()->Get(i); + auto* spiderInfo = s->spiders()->Get(i); auto* spider = &Spiders[i]; spider->On = spiderInfo->on(); diff --git a/TombEngine/Objects/Effects/flame_emitters.cpp b/TombEngine/Objects/Effects/flame_emitters.cpp index 4971d4b17..b108dd9a9 100644 --- a/TombEngine/Objects/Effects/flame_emitters.cpp +++ b/TombEngine/Objects/Effects/flame_emitters.cpp @@ -657,7 +657,7 @@ namespace TEN::Entities::Effects !(TrInput & IN_ACTION) || laraItem->Animation.ActiveState != LS_IDLE || laraItem->Animation.AnimNumber != LA_STAND_IDLE || - laraItem->Animation.Airborne) + laraItem->Animation.IsAirborne) { if (item->ObjectNumber == ID_BURNING_ROOTS) ObjectCollision(itemNumber, laraItem, coll); diff --git a/TombEngine/Objects/Generic/Doors/double_doors.cpp b/TombEngine/Objects/Generic/Doors/double_doors.cpp index 82359f76f..66f4ba373 100644 --- a/TombEngine/Objects/Generic/Doors/double_doors.cpp +++ b/TombEngine/Objects/Generic/Doors/double_doors.cpp @@ -44,7 +44,7 @@ namespace TEN::Entities::Doors laraItem->Animation.ActiveState == LS_IDLE && laraItem->Animation.AnimNumber == LA_STAND_IDLE && !laraItem->HitStatus && - !(doorItem->Status && doorItem->Animation.Airborne) && + !(doorItem->Status && doorItem->Animation.IsAirborne) && laraInfo->Control.HandStatus == HandStatus::Free || laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber) { diff --git a/TombEngine/Objects/Generic/Doors/generic_doors.cpp b/TombEngine/Objects/Generic/Doors/generic_doors.cpp index e2ec20dd4..b0b3a9308 100644 --- a/TombEngine/Objects/Generic/Doors/generic_doors.cpp +++ b/TombEngine/Objects/Generic/Doors/generic_doors.cpp @@ -170,7 +170,7 @@ namespace TEN::Entities::Doors auto* doorItem = &g_Level.Items[itemNumber]; if (doorItem->TriggerFlags == 2 && - doorItem->Status == ITEM_NOT_ACTIVE && !doorItem->Animation.Airborne && // CHECK + doorItem->Status == ITEM_NOT_ACTIVE && !doorItem->Animation.IsAirborne && // CHECK ((TrInput & IN_ACTION || g_Gui.GetInventoryItemChosen() == ID_CROWBAR_ITEM) && laraItem->Animation.ActiveState == LS_IDLE && laraItem->Animation.AnimNumber == LA_STAND_IDLE && diff --git a/TombEngine/Objects/Generic/Doors/underwater_door.cpp b/TombEngine/Objects/Generic/Doors/underwater_door.cpp index cc9dab160..7e824e602 100644 --- a/TombEngine/Objects/Generic/Doors/underwater_door.cpp +++ b/TombEngine/Objects/Generic/Doors/underwater_door.cpp @@ -43,7 +43,7 @@ namespace TEN::Entities::Doors if (TrInput & IN_ACTION && laraItem->Animation.ActiveState == LS_UNDERWATER_IDLE && laraInfo->Control.WaterStatus == WaterStatus::Underwater && - !(doorItem->Status && doorItem->Animation.Airborne) && + !(doorItem->Status && doorItem->Animation.IsAirborne) && laraInfo->Control.HandStatus == HandStatus::Free || laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber) { diff --git a/TombEngine/Objects/Generic/Object/burning_torch.cpp b/TombEngine/Objects/Generic/Object/burning_torch.cpp index ca6e63c70..cd2f42baa 100644 --- a/TombEngine/Objects/Generic/Object/burning_torch.cpp +++ b/TombEngine/Objects/Generic/Object/burning_torch.cpp @@ -83,7 +83,7 @@ namespace TEN::Entities::Generic Lara.Torch.State = TorchState::Dropping; } else if (TrInput & IN_DRAW && - !LaraItem->Animation.Airborne && + !LaraItem->Animation.IsAirborne && !LaraItem->Animation.VerticalVelocity && LaraItem->Animation.ActiveState != LS_JUMP_PREPARE && LaraItem->Animation.ActiveState != LS_JUMP_UP && @@ -104,7 +104,7 @@ namespace TEN::Entities::Generic } else if (Lara.Torch.State == TorchState::Throwing) { - if (Lara.LeftArm.FrameNumber < 12 && LaraItem->Animation.Airborne) + if (Lara.LeftArm.FrameNumber < 12 && LaraItem->Animation.IsAirborne) { Lara.LeftArm.Locked = false; Lara.LeftArm.FrameNumber = 0; @@ -304,7 +304,7 @@ namespace TEN::Entities::Generic if (!(TrInput & IN_ACTION) || laraItem->Animation.ActiveState != LS_IDLE || laraItem->Animation.AnimNumber != LA_STAND_IDLE || - laraItem->Animation.Airborne || + laraItem->Animation.IsAirborne || laraInfo->Control.Weapon.GunType != LaraWeaponType::Torch || laraInfo->Control.HandStatus != HandStatus::WeaponReady || laraInfo->LeftArm.Locked || diff --git a/TombEngine/Objects/Generic/Object/generic_trapdoor.cpp b/TombEngine/Objects/Generic/Object/generic_trapdoor.cpp index 48d0aaacb..48910d0d6 100644 --- a/TombEngine/Objects/Generic/Object/generic_trapdoor.cpp +++ b/TombEngine/Objects/Generic/Object/generic_trapdoor.cpp @@ -70,7 +70,7 @@ void CeilingTrapDoorCollision(short itemNumber, ItemInfo* laraItem, CollisionInf if (TrInput & IN_ACTION && laraItem->Animation.ActiveState == LS_JUMP_UP && - laraItem->Animation.Airborne && + laraItem->Animation.IsAirborne && laraInfo->Control.HandStatus == HandStatus::Free && trapDoorItem->Status != ITEM_ACTIVE && itemIsAbove && @@ -82,7 +82,7 @@ void CeilingTrapDoorCollision(short itemNumber, ItemInfo* laraItem, CollisionInf ResetLaraFlex(laraItem); laraItem->Animation.VerticalVelocity = 0; - laraItem->Animation.Airborne = false; + laraItem->Animation.IsAirborne = false; laraItem->Animation.AnimNumber = LA_TRAPDOOR_CEILING_OPEN; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.ActiveState = LS_FREEFALL_BIS; diff --git a/TombEngine/Objects/Generic/Object/objects.cpp b/TombEngine/Objects/Generic/Object/objects.cpp index 37e034dee..f48537c35 100644 --- a/TombEngine/Objects/Generic/Object/objects.cpp +++ b/TombEngine/Objects/Generic/Object/objects.cpp @@ -219,7 +219,7 @@ void HorizontalBarCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* laraItem->Animation.AnimNumber = LA_SWINGBAR_GRAB; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.VerticalVelocity = false; - laraItem->Animation.Airborne = false; + laraItem->Animation.IsAirborne = false; ResetLaraFlex(barItem); diff --git a/TombEngine/Objects/Generic/Object/polerope.cpp b/TombEngine/Objects/Generic/Object/polerope.cpp index 1525c631e..2218f4061 100644 --- a/TombEngine/Objects/Generic/Object/polerope.cpp +++ b/TombEngine/Objects/Generic/Object/polerope.cpp @@ -74,7 +74,7 @@ namespace TEN::Entities::Generic } else if (TrInput & IN_ACTION && isLara && laraInfo->Control.HandStatus == HandStatus::Free && - laraItem->Animation.Airborne && + laraItem->Animation.IsAirborne && laraItem->Animation.VerticalVelocity > (int)laraInfo->Control.HandStatus && // ????? laraItem->Animation.ActiveState == LS_REACH || laraItem->Animation.ActiveState == LS_JUMP_UP) { @@ -103,7 +103,7 @@ namespace TEN::Entities::Generic laraItem->Animation.ActiveState = LS_POLE_IDLE; laraItem->Animation.VerticalVelocity = 0; - laraItem->Animation.Airborne = false; + laraItem->Animation.IsAirborne = false; laraInfo->Control.HandStatus = HandStatus::Busy; poleItem->Pose.Orientation.y = rot; } diff --git a/TombEngine/Objects/Generic/Object/rope.cpp b/TombEngine/Objects/Generic/Object/rope.cpp index 15bff262e..b74d4e555 100644 --- a/TombEngine/Objects/Generic/Object/rope.cpp +++ b/TombEngine/Objects/Generic/Object/rope.cpp @@ -181,7 +181,7 @@ namespace TEN::Entities::Generic if (TrInput & IN_ACTION && laraInfo->Control.HandStatus == HandStatus::Free && (laraItem->Animation.ActiveState == LS_REACH || laraItem->Animation.ActiveState == LS_JUMP_UP) && - laraItem->Animation.Airborne && + laraItem->Animation.IsAirborne && laraItem->Animation.VerticalVelocity > 0&& rope->active) { @@ -211,7 +211,7 @@ namespace TEN::Entities::Generic laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.VerticalVelocity = 0; - laraItem->Animation.Airborne = false; + laraItem->Animation.IsAirborne = false; laraInfo->Control.HandStatus = HandStatus::Busy; laraInfo->Control.Rope.Ptr = ropeItem->TriggerFlags; @@ -644,7 +644,7 @@ namespace TEN::Entities::Generic } item->Pose.Orientation.x = 0; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; Lara.Control.HandStatus = HandStatus::Free; @@ -671,7 +671,7 @@ namespace TEN::Entities::Generic SetAnimation(item, stumble ? LA_JUMP_WALL_SMASH_START : LA_FALL_START); item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; auto* lara = GetLaraInfo(item); lara->Control.HandStatus = HandStatus::Free; diff --git a/TombEngine/Objects/Generic/Switches/cog_switch.cpp b/TombEngine/Objects/Generic/Switches/cog_switch.cpp index b8f8f49b4..6187bccc9 100644 --- a/TombEngine/Objects/Generic/Switches/cog_switch.cpp +++ b/TombEngine/Objects/Generic/Switches/cog_switch.cpp @@ -67,12 +67,12 @@ namespace TEN::Entities::Switches if (switchItem->Status == ITEM_NOT_ACTIVE) { - if (!(switchItem->Flags & ONESHOT) && + if (!(switchItem->Flags & IFLAG_INVISIBLE) && (TrInput & IN_ACTION && laraItem->Animation.ActiveState == LS_IDLE && laraItem->Animation.AnimNumber == LA_STAND_IDLE && lara->Control.HandStatus == HandStatus::Free && - !switchItem->Animation.Airborne || + !switchItem->Animation.IsAirborne || lara->Control.IsMoving && lara->InteractedItem == itemNum)) { diff --git a/TombEngine/Objects/Generic/Switches/generic_switch.cpp b/TombEngine/Objects/Generic/Switches/generic_switch.cpp index 10ea4ea36..171d135cf 100644 --- a/TombEngine/Objects/Generic/Switches/generic_switch.cpp +++ b/TombEngine/Objects/Generic/Switches/generic_switch.cpp @@ -31,7 +31,7 @@ namespace TEN::Entities::Switches switchItem->Flags |= 0x3E00; - if (!TriggerActive(switchItem) && !(switchItem->Flags & ONESHOT)) + if (!TriggerActive(switchItem) && !(switchItem->Flags & IFLAG_INVISIBLE)) { if (switchItem->ObjectNumber == ID_JUMP_SWITCH) { diff --git a/TombEngine/Objects/Generic/Switches/jump_switch.cpp b/TombEngine/Objects/Generic/Switches/jump_switch.cpp index 2a10081d5..4586db36b 100644 --- a/TombEngine/Objects/Generic/Switches/jump_switch.cpp +++ b/TombEngine/Objects/Generic/Switches/jump_switch.cpp @@ -32,7 +32,7 @@ namespace TEN::Entities::Switches if (TrInput & IN_ACTION && (laraItem->Animation.ActiveState == LS_REACH || laraItem->Animation.ActiveState == LS_JUMP_UP) && - (laraItem->Status || laraItem->Animation.Airborne) && + (laraItem->Status || laraItem->Animation.IsAirborne) && laraItem->Animation.VerticalVelocity > 0 && laraInfo->Control.HandStatus == HandStatus::Free && !switchItem->Animation.ActiveState) @@ -45,7 +45,7 @@ namespace TEN::Entities::Switches laraItem->Animation.AnimNumber = LA_JUMPSWITCH_PULL; laraItem->Animation.VerticalVelocity = 0; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - laraItem->Animation.Airborne = false; + laraItem->Animation.IsAirborne = false; laraInfo->Control.HandStatus = HandStatus::Busy; switchItem->Animation.TargetState = SWITCH_ON; switchItem->Status = ITEM_ACTIVE; diff --git a/TombEngine/Objects/Generic/Switches/pulley_switch.cpp b/TombEngine/Objects/Generic/Switches/pulley_switch.cpp index 8ead73484..7a96835be 100644 --- a/TombEngine/Objects/Generic/Switches/pulley_switch.cpp +++ b/TombEngine/Objects/Generic/Switches/pulley_switch.cpp @@ -49,7 +49,7 @@ namespace TEN::Entities::Switches if (TrInput & IN_ACTION && laraItem->Animation.ActiveState == LS_IDLE && laraItem->Animation.AnimNumber == LA_STAND_IDLE && - laraItem->Animation.Airborne == false && + laraItem->Animation.IsAirborne == false && laraInfo->Control.HandStatus == HandStatus::Free || laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber) { diff --git a/TombEngine/Objects/Generic/Switches/turn_switch.cpp b/TombEngine/Objects/Generic/Switches/turn_switch.cpp index 0211be32a..b007c86f2 100644 --- a/TombEngine/Objects/Generic/Switches/turn_switch.cpp +++ b/TombEngine/Objects/Generic/Switches/turn_switch.cpp @@ -58,7 +58,7 @@ namespace TEN::Entities::Switches if (TrInput & IN_ACTION && laraItem->Animation.ActiveState == LS_IDLE && laraItem->Animation.AnimNumber == LA_STAND_IDLE && - laraItem->Animation.Airborne == false && + laraItem->Animation.IsAirborne == false && laraInfo->Control.HandStatus == HandStatus::Free && switchItem->Animation.ActiveState == TURN_SWITCH_STOP || laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber) diff --git a/TombEngine/Objects/TR1/Entity/tr1_ape.cpp b/TombEngine/Objects/TR1/Entity/tr1_ape.cpp index 3b76af415..e9c015f13 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_ape.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_ape.cpp @@ -30,7 +30,7 @@ namespace TEN::Entities::TR1 constexpr auto SHIFT = 75; - #define APE_RUN_TURN_ANGLE ANGLE(5.0f) + #define APE_RUN_TURN_RATE_MAX ANGLE(5.0f) #define APE_DISPLAY_ANGLE ANGLE(45.0f) enum ApeState @@ -238,7 +238,7 @@ namespace TEN::Entities::TR1 break; case APE_STATE_RUN_FORWARD: - creatureInfo->MaxTurn = APE_RUN_TURN_ANGLE; + creatureInfo->MaxTurn = APE_RUN_TURN_RATE_MAX; if (creatureInfo->Flags == 0 && AI.angle > -APE_DISPLAY_ANGLE && diff --git a/TombEngine/Objects/TR1/Entity/tr1_bear.cpp b/TombEngine/Objects/TR1/Entity/tr1_bear.cpp index 12de3b8f3..6f351ce63 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_bear.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_bear.cpp @@ -32,8 +32,8 @@ namespace TEN::Entities::TR1 constexpr auto BEAR_REAR_CHANCE = 0x300; constexpr auto BEAR_DROP_CHANCE = 0x600; - #define BEAR_WALK_TURN_ANGLE ANGLE(2.0f) - #define BEAR_RUN_TURN_ANGLE ANGLE(5.0f) + #define BEAR_WALK_TURN_RATE_MAX ANGLE(2.0f) + #define BEAR_RUN_TURN_RATE_MAX ANGLE(5.0f) enum BearState { @@ -163,7 +163,7 @@ namespace TEN::Entities::TR1 break; case BEAR_STATE_STROLL: - creature->MaxTurn = BEAR_WALK_TURN_ANGLE; + creature->MaxTurn = BEAR_WALK_TURN_RATE_MAX; if (laraDead && item->TestBits(JointBitType::Touch, BearAttackJoints) && AI.ahead) item->Animation.TargetState = BEAR_STATE_IDLE; @@ -183,7 +183,7 @@ namespace TEN::Entities::TR1 break; case BEAR_STATE_RUN_FORWARD: - creature->MaxTurn = BEAR_RUN_TURN_ANGLE; + creature->MaxTurn = BEAR_RUN_TURN_RATE_MAX; if (item->TestBits(JointBitType::Touch, BearAttackJoints)) { diff --git a/TombEngine/Objects/TR1/Entity/tr1_doppelganger.cpp b/TombEngine/Objects/TR1/Entity/tr1_doppelganger.cpp index 91a258ec8..8a0f5a2be 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_doppelganger.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_doppelganger.cpp @@ -92,12 +92,12 @@ namespace TEN::Entities::TR1 // Compare floor heights. if (item->Floor >= laraFloorHeight + SECTOR(1) + 1 && // Add 1 to avoid bacon Lara dying when exiting water. - !LaraItem->Animation.Airborne) + !LaraItem->Animation.IsAirborne) { SetAnimation(item, LA_JUMP_WALL_SMASH_START); item->Animation.Velocity = 0; item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Data = -1; item->Pose.Position.y += 50; } @@ -115,7 +115,7 @@ namespace TEN::Entities::TR1 TestTriggers(item, true); item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.TargetState = LS_DEATH; item->Animation.RequiredState = LS_DEATH; } diff --git a/TombEngine/Objects/TR1/Entity/tr1_giant_mutant.cpp b/TombEngine/Objects/TR1/Entity/tr1_giant_mutant.cpp index 35917617a..95a4951bb 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_giant_mutant.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_giant_mutant.cpp @@ -95,7 +95,7 @@ namespace TEN::Entities::TR1 { case MUTANT_STATE_SET: item->Animation.TargetState = MUTANT_STATE_FALL; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; break; case MUTANT_STATE_IDLE: @@ -204,7 +204,7 @@ namespace TEN::Entities::TR1 LaraItem->Pose.Position.z = item->Pose.Position.z; LaraItem->Pose.Orientation.y = item->Pose.Orientation.y; LaraItem->Pose.Orientation.x = LaraItem->Pose.Orientation.z = 0; - LaraItem->Animation.Airborne = false; + LaraItem->Animation.IsAirborne = false; LaraItem->HitPoints = -1; Lara.Air = -1; Lara.Control.HandStatus = HandStatus::Busy; @@ -229,7 +229,7 @@ namespace TEN::Entities::TR1 if (item->Pose.Position.y > item->Floor) { item->Animation.TargetState = MUTANT_STATE_IDLE; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Pose.Position.y = item->Floor; Camera.bounce = 500; } diff --git a/TombEngine/Objects/TR1/Entity/tr1_natla.cpp b/TombEngine/Objects/TR1/Entity/tr1_natla.cpp index 2372add16..6f692f7be 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_natla.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_natla.cpp @@ -88,13 +88,13 @@ namespace TEN::Entities::TR1 case NATLA_STATE_FALL: if (item->Pose.Position.y < item->Floor) { - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.Velocity = 0; } else { item->Animation.TargetState = NATLA_STATE_SEMI_DEATH; - item->Animation.Airborne = 0; + item->Animation.IsAirborne = 0; item->Pose.Position.y = item->Floor; timer = 0; } diff --git a/TombEngine/Objects/TR2/Entity/tr2_dragon.cpp b/TombEngine/Objects/TR2/Entity/tr2_dragon.cpp index aec14aaa1..c4d82cfc9 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_dragon.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_dragon.cpp @@ -180,7 +180,7 @@ void DragonCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) if ((anim == DRAGON_ANIM_DEAD || (anim == DRAGON_ANIM_DEAD + 1 && frame <= DRAGON_ALMOST_LIVE)) && TrInput & IN_ACTION && item->ObjectNumber == ID_DRAGON_BACK && - !laraItem->Animation.Airborne && + !laraItem->Animation.IsAirborne && shift <= DRAGON_MID && shift > (DRAGON_CLOSE - 350) && sideShift > -350 && @@ -194,7 +194,7 @@ void DragonCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) laraItem->Animation.TargetState = 7; laraItem->Pose = item->Pose; - laraItem->Animation.Airborne = false; + laraItem->Animation.IsAirborne = false; laraItem->Animation.Velocity = 0; laraItem->Animation.VerticalVelocity = 0; diff --git a/TombEngine/Objects/TR2/Entity/tr2_eagle_or_crow.cpp b/TombEngine/Objects/TR2/Entity/tr2_eagle_or_crow.cpp index bfcda3c49..edee4b8b6 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_eagle_or_crow.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_eagle_or_crow.cpp @@ -64,7 +64,7 @@ void EagleControl(short itemNumber) { item->Pose.Position.y = item->Floor; item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.TargetState = 5; } @@ -83,7 +83,7 @@ void EagleControl(short itemNumber) item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = 4; item->Animation.Velocity = 0; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; break; } item->Pose.Orientation.x = 0; diff --git a/TombEngine/Objects/TR2/Entity/tr2_skidman.cpp b/TombEngine/Objects/TR2/Entity/tr2_skidman.cpp index 5ea743dd1..406fbbc18 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_skidman.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_skidman.cpp @@ -240,13 +240,13 @@ void SkidooManControl(short riderItemNumber) riderItem->Animation.FrameNumber = item->Animation.FrameNumber + (g_Level.Anims[riderItem->Animation.AnimNumber].frameBase - g_Level.Anims[item->Animation.AnimNumber].frameBase); } else if (riderItem->Status == ITEM_DEACTIVATED && - item->Animation.Velocity == 0 && - item->Animation.VerticalVelocity == 0) + item->Animation.Velocity == 0 && + item->Animation.VerticalVelocity == 0) { RemoveActiveItem(riderItemNumber); riderItem->Collidable = false; riderItem->HitPoints = NOT_TARGETABLE; - riderItem->Flags |= ONESHOT; + riderItem->Flags |= IFLAG_INVISIBLE; DisableEntityAI(itemNumber); item->ObjectNumber = ID_SNOWMOBILE; diff --git a/TombEngine/Objects/TR2/Entity/tr2_sword_guardian.cpp b/TombEngine/Objects/TR2/Entity/tr2_sword_guardian.cpp index 046663561..0d9d8c78c 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_sword_guardian.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_sword_guardian.cpp @@ -61,7 +61,7 @@ void SwordGuardianControl(short itemNumber) DisableEntityAI(itemNumber); KillItem(itemNumber); //item->status = ITEM_DESACTIVATED; - //item->flags |= ONESHOT; + //item->flags |= IFLAG_INVISIBLE; item->Animation.ActiveState = 12; } diff --git a/TombEngine/Objects/TR2/Trap/tr2_springboard.cpp b/TombEngine/Objects/TR2/Trap/tr2_springboard.cpp index eb4da986c..25e4b1fb9 100644 --- a/TombEngine/Objects/TR2/Trap/tr2_springboard.cpp +++ b/TombEngine/Objects/TR2/Trap/tr2_springboard.cpp @@ -24,7 +24,7 @@ void SpringBoardControl(short itemNumber) LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase; LaraItem->Animation.ActiveState = LS_JUMP_FORWARD; LaraItem->Animation.TargetState = LS_JUMP_FORWARD; - LaraItem->Animation.Airborne = true; + LaraItem->Animation.IsAirborne = true; LaraItem->Animation.VerticalVelocity = -240; item->Animation.TargetState = 1; diff --git a/TombEngine/Objects/TR2/Vehicles/boat.cpp b/TombEngine/Objects/TR2/Vehicles/boat.cpp deleted file mode 100644 index be14aef58..000000000 --- a/TombEngine/Objects/TR2/Vehicles/boat.cpp +++ /dev/null @@ -1,995 +0,0 @@ -#include "framework.h" -#include "Objects/TR2/Vehicles/boat.h" - -#include "Game/animation.h" -#include "Game/camera.h" -#include "Game/collision/collide_item.h" -#include "Game/collision/sphere.h" -#include "Game/effects/effects.h" -#include "Game/items.h" -#include "Game/Lara/lara.h" -#include "Game/Lara/lara_helpers.h" -#include "Game/effects/simple_particle.h" -#include "Objects/TR2/Vehicles/boat_info.h" -#include "Sound/sound.h" -#include "Specific/input.h" -#include "Specific/level.h" -#include "Specific/setup.h" - -using namespace TEN::Input; - -namespace TEN::Entities::Vehicles -{ - #define BOAT_UNDO_TURN ANGLE(0.25f) - #define BOAT_TURN (ANGLE(0.25f) / 2) - #define BOAT_MAX_TURN ANGLE(4.0f) - #define BOAT_MAX_VELOCITY 110 - #define BOAT_SLOW_SPEED (BOAT_MAX_VELOCITY / 3) - #define BOAT_FAST_SPEED (BOAT_MAX_VELOCITY + 75) - #define BOAT_MIN_SPEED 20 - #define BOAT_ACCELERATION 5 - #define BOAT_BRAKE 5 - #define BOAT_SLOWDOWN 1 - #define BOAT_REVERSE -2 // -5 - #define BOAT_MAX_BACK -20 - #define BOAT_MAX_KICK -80 - #define BOAT_SLIP 10 - #define BOAT_SIDE_SLIP 30 - #define BOAT_FRONT 750 - #define BOAT_BACK -700 - #define BOAT_SIDE 300 - #define BOAT_RADIUS 500 - #define BOAT_SNOW 500 - #define BOAT_MAX_HEIGHT CLICK(1) - #define DISMOUNT_DISTANCE SECTOR(1) - #define BOAT_SOUND_CEILING SECTOR(5) - #define BOAT_TIP (BOAT_FRONT + 250) - - #define SBOAT_IN_ACCELERATE IN_FORWARD - #define SBOAT_IN_REVERSE IN_BACK - #define SBOAT_IN_SPEED (IN_ACTION | IN_SPRINT) - #define SBOAT_IN_SLOW IN_WALK - #define SBOAT_IN_DISMOUNT (IN_JUMP | IN_ROLL) - #define SBOAT_IN_LEFT (IN_LEFT | IN_LSTEP) - #define SBOAT_IN_RIGHT (IN_RIGHT | IN_RSTEP) - - enum SpeedBoatState - { - SBOAT_STATE_MOUNT = 0, - SBOAT_STATE_IDLE = 1, - SBOAT_STATE_MOVING = 2, - SBOAT_STATE_DISMOUNT_RIGHT = 3, - SBOAT_STATE_DISMOUNT_LEFT = 4, - SBOAT_STATE_HIT = 5, - SBOAT_STATE_FALL = 6, - SBOAT_STATE_TURN_RIGHT = 7, - SBOAT_STATE_DEATH = 8, - SBOAT_STATE_TURN_LEFT = 9 - }; - - enum SpeedBoatAnim - { - SBOAT_ANIM_MOUNT_LEFT = 0, - SBOAT_ANIM_IDLE = 1, // ? - SBOAT_ANIM_FORWARD = 2, // ? - - SBOAT_ANIM_DISMOUNT_LEFT = 5, - SBOAT_ANIM_MOUNT_JUMP = 6, - SBOAT_ANIM_DISMOUNT_RIGHT = 7, - SBOAT_ANIM_MOUNT_RIGHT = 8, - - SBOAT_ANIM_HIT_LEFT = 11, - SBOAT_ANIM_HIT_RIGHT = 12, - SBOAT_ANIM_HIT_FRONT = 13, - SBOAT_ANIM_HIT_BACK = 14, - SBOAT_ANIM_LEAP_START = 15, - SBOAT_ANIM_LEAP = 16, - SBOAT_ANIM_LEAP_END = 17, - SBOAT_ANIM_DEATH = 18 - }; - - void InitialiseSpeedBoat(short itemNumber) - { - auto* sBoatItem = &g_Level.Items[itemNumber]; - sBoatItem->Data = SpeedBoatInfo(); - auto* sBoat = (SpeedBoatInfo*)sBoatItem->Data; - - sBoat->TurnRate = 0; - sBoat->LeanAngle = 0; - sBoat->ExtraRotation = 0; - sBoat->LeftVerticalVelocity = 0; - sBoat->RightVerticalVelocity = 0; - sBoat->Water = 0; - sBoat->Pitch = 0; - } - - BoatMountType GetSpeedBoatMountType(ItemInfo* laraItem, ItemInfo* sBoatItem, CollisionInfo* coll) - { - auto* lara = GetLaraInfo(laraItem); - - BoatMountType mountType = BoatMountType::None; - - if (lara->Control.HandStatus != HandStatus::Free) - return mountType; - - if (!TestBoundsCollide(sBoatItem, laraItem, coll->Setup.Radius)) - return mountType; - - if (!TestCollision(sBoatItem, laraItem)) - return mountType; - - int distance = (laraItem->Pose.Position.z - sBoatItem->Pose.Position.z) * phd_cos(-sBoatItem->Pose.Orientation.y) - (laraItem->Pose.Position.x - sBoatItem->Pose.Position.x) * phd_sin(-sBoatItem->Pose.Orientation.y); - if (distance > 200) - return mountType; - - short deltaAngle = sBoatItem->Pose.Orientation.y - laraItem->Pose.Orientation.y; - if (lara->Control.WaterStatus == WaterStatus::TreadWater || lara->Control.WaterStatus == WaterStatus::Wade) - { - if (!(TrInput & IN_ACTION) || laraItem->Animation.Airborne || sBoatItem->Animation.Velocity) - return mountType; - - if (deltaAngle > ANGLE(45.0f) && deltaAngle < ANGLE(135.0f)) - mountType = BoatMountType::WaterRight; - else if (deltaAngle > -ANGLE(135.0f) && deltaAngle < -ANGLE(45.0f)) - mountType = BoatMountType::WaterLeft; - } - else if (lara->Control.WaterStatus == WaterStatus::Dry) - { - if (laraItem->Animation.VerticalVelocity > 0) - { - if (deltaAngle > -ANGLE(135.0f) && deltaAngle < ANGLE(135.0f) && - laraItem->Pose.Position.y > sBoatItem->Pose.Position.y) - { - mountType = BoatMountType::Jump; - } - } - else if (laraItem->Animation.VerticalVelocity == 0) - { - if (deltaAngle > -ANGLE(135.0f) && deltaAngle < ANGLE(135.0f)) - { - if (laraItem->Pose.Position.x == sBoatItem->Pose.Position.x && - laraItem->Pose.Position.y == sBoatItem->Pose.Position.y && - laraItem->Pose.Position.z == sBoatItem->Pose.Position.z) - { - mountType = BoatMountType::StartPosition; - } - else - mountType = BoatMountType::Jump; - } - } - } - - return mountType; - } - - bool TestSpeedBoatDismount(ItemInfo* sBoatItem, int direction) - { - short angle; - if (direction < 0) - angle = sBoatItem->Pose.Orientation.y - ANGLE(90.0f); - else - angle = sBoatItem->Pose.Orientation.y + ANGLE(90.0f); - - int x = sBoatItem->Pose.Position.x + DISMOUNT_DISTANCE * phd_sin(angle); - int y = sBoatItem->Pose.Position.y; - int z = sBoatItem->Pose.Position.z + DISMOUNT_DISTANCE * phd_cos(angle); - auto probe = GetCollision(x, y, z, sBoatItem->RoomNumber); - - if ((probe.Position.Floor - sBoatItem->Pose.Position.y) < -CLICK(2)) - return false; - - if (probe.Position.FloorSlope || - probe.Position.Floor == NO_HEIGHT) - { - return false; - } - - if ((probe.Position.Floor - probe.Position.Ceiling) < LARA_HEIGHT || - (probe.Position.Ceiling - sBoatItem->Pose.Position.y) > -LARA_HEIGHT) - { - return false; - } - - return true; - } - - void DoSpeedBoatDismount(ItemInfo* laraItem, ItemInfo* sBoatItem) - { - auto* lara = GetLaraInfo(laraItem); - - if ((laraItem->Animation.ActiveState == SBOAT_STATE_DISMOUNT_LEFT || - laraItem->Animation.ActiveState == SBOAT_STATE_DISMOUNT_RIGHT) && - TestLastFrame(laraItem, laraItem->Animation.AnimNumber)) - { - if (laraItem->Animation.ActiveState == SBOAT_STATE_DISMOUNT_LEFT) - laraItem->Pose.Orientation.y -= ANGLE(90.0f); - else if (laraItem->Animation.ActiveState == SBOAT_STATE_DISMOUNT_RIGHT) - laraItem->Pose.Orientation.y += ANGLE(90.0f); - - SetAnimation(laraItem, LA_JUMP_FORWARD); - laraItem->Animation.Velocity = 40; - laraItem->Animation.VerticalVelocity = -50; - laraItem->Animation.Airborne = true; - laraItem->Pose.Orientation.x = 0; - laraItem->Pose.Orientation.z = 0; - lara->Vehicle = NO_ITEM; - - int x = laraItem->Pose.Position.x + 360 * phd_sin(laraItem->Pose.Orientation.y); - int y = laraItem->Pose.Position.y - 90; - int z = laraItem->Pose.Position.z + 360 * phd_cos(laraItem->Pose.Orientation.y); - auto probe = GetCollision(x, y, z, laraItem->RoomNumber); - - if (probe.Position.Floor >= (y - CLICK(1))) - { - laraItem->Pose.Position.x = x; - laraItem->Pose.Position.z = z; - - if (probe.RoomNumber != laraItem->RoomNumber) - ItemNewRoom(lara->ItemNumber, probe.RoomNumber); - } - laraItem->Pose.Position.y = y; - - sBoatItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT].animIndex; - sBoatItem->Animation.FrameNumber = g_Level.Anims[sBoatItem->Animation.AnimNumber].frameBase; - } - } - - int SpeedBoatTestWaterHeight(ItemInfo* sBoatItem, int zOffset, int xOffset, Vector3Int* pos) - { - float s = phd_sin(sBoatItem->Pose.Orientation.y); - float c = phd_cos(sBoatItem->Pose.Orientation.y); - - pos->x = sBoatItem->Pose.Position.x + zOffset * s + xOffset * c; - pos->y = sBoatItem->Pose.Position.y - zOffset * phd_sin(sBoatItem->Pose.Orientation.x) + xOffset * phd_sin(sBoatItem->Pose.Orientation.z); - pos->z = sBoatItem->Pose.Position.z + zOffset * c - xOffset * s; - - auto probe = GetCollision(pos->x, pos->y, pos->z, sBoatItem->RoomNumber); - auto height = GetWaterHeight(pos->x, pos->y, pos->z, probe.RoomNumber); - - if (height == NO_HEIGHT) - { - height = probe.Position.Floor; - if (height == NO_HEIGHT) - return height; - } - - return (height - 5); - } - - void SpeedBoatDoBoatShift(ItemInfo* sBoatItem, int itemNumber) - { - short itemNumber2 = g_Level.Rooms[sBoatItem->RoomNumber].itemNumber; - while (itemNumber2 != NO_ITEM) - { - auto* item = &g_Level.Items[itemNumber2]; - - if (item->ObjectNumber == ID_SPEEDBOAT && itemNumber2 != itemNumber && Lara.Vehicle != itemNumber2) - { - int x = item->Pose.Position.x - sBoatItem->Pose.Position.x; - int z = item->Pose.Position.z - sBoatItem->Pose.Position.z; - - int distance = pow(x, 2) + pow(z, 2); - int radius = pow(BOAT_RADIUS * 2, 2); - if (distance < radius) - { - sBoatItem->Pose.Position.x = item->Pose.Position.x - x * radius / distance; - sBoatItem->Pose.Position.z = item->Pose.Position.z - z * radius / distance; - } - - return; - } - - // TODO: mine and gondola - - itemNumber2 = item->NextItem; - } - } - - short SpeedBoatDoShift(ItemInfo* sBoatItem, Vector3Int* pos, Vector3Int* old) - { - int x = pos->x / SECTOR(1); - int z = pos->z / SECTOR(1); - - int xOld = old->x / SECTOR(1); - int zOld = old->z / SECTOR(1); - - int shiftX = pos->x & (SECTOR(1) - 1); - int shiftZ = pos->z & (SECTOR(1) - 1); - - if (x == xOld) - { - if (z == zOld) - { - sBoatItem->Pose.Position.z += (old->z - pos->z); - sBoatItem->Pose.Position.x += (old->x - pos->x); - } - else if (z > zOld) - { - sBoatItem->Pose.Position.z -= shiftZ + 1; - return (pos->x - sBoatItem->Pose.Position.x); - } - else - { - sBoatItem->Pose.Position.z += SECTOR(1) - shiftZ; - return (sBoatItem->Pose.Position.x - pos->x); - } - } - else if (z == zOld) - { - if (x > xOld) - { - sBoatItem->Pose.Position.x -= shiftX + 1; - return (sBoatItem->Pose.Position.z - pos->z); - } - else - { - sBoatItem->Pose.Position.x += SECTOR(1) - shiftX; - return (pos->z - sBoatItem->Pose.Position.z); - } - } - else - { - x = 0; - z = 0; - - auto probe = GetCollision(old->x, pos->y, pos->z, sBoatItem->RoomNumber); - if (probe.Position.Floor < (old->y - CLICK(1))) - { - if (pos->z > old->z) - z = -shiftZ - 1; - else - z = SECTOR(1) - shiftZ; - } - - probe = GetCollision(pos->x, pos->y, old->z, sBoatItem->RoomNumber); - if (probe.Position.Floor < (old->y - CLICK(1))) - { - if (pos->x > old->x) - x = -shiftX - 1; - else - x = SECTOR(1) - shiftX; - } - - if (x && z) - { - sBoatItem->Pose.Position.z += z; - sBoatItem->Pose.Position.x += x; - } - else if (z) - { - sBoatItem->Pose.Position.z += z; - if (z > 0) - return (sBoatItem->Pose.Position.x - pos->x); - else - return (pos->x - sBoatItem->Pose.Position.x); - } - else if (x) - { - sBoatItem->Pose.Position.x += x; - if (x > 0) - return (pos->z - sBoatItem->Pose.Position.z); - else - return (sBoatItem->Pose.Position.z - pos->z); - } - else - { - sBoatItem->Pose.Position.z += (old->z - pos->z); - sBoatItem->Pose.Position.x += (old->x - pos->x); - } - } - - return 0; - } - - int GetSpeedBoatHitAnim(ItemInfo* sBoatItem, Vector3Int* moved) - { - moved->x = sBoatItem->Pose.Position.x - moved->x; - moved->z = sBoatItem->Pose.Position.z - moved->z; - - if (moved->x || moved->z) - { - float s = phd_sin(sBoatItem->Pose.Orientation.y); - float c = phd_cos(sBoatItem->Pose.Orientation.y); - - int front = moved->z * c + moved->x * s; - int side = -moved->z * s + moved->x * c; - - if (abs(front) > abs(side)) - { - if (front > 0) - return SBOAT_ANIM_HIT_BACK; - else - return SBOAT_ANIM_HIT_FRONT; - } - else - { - if (side > 0) - return SBOAT_ANIM_HIT_LEFT; - else - return SBOAT_ANIM_HIT_RIGHT; - } - } - - return 0; - } - - int DoSpeedBoatDynamics(int height, int verticalVelocity, int* y) - { - if (height > *y) - { - *y += verticalVelocity; - if (*y > height) - { - *y = height; - verticalVelocity = 0; - } - else - verticalVelocity += GRAVITY; - } - else - { - verticalVelocity += ((height - *y - verticalVelocity) / 8); - if (verticalVelocity < BOAT_MAX_BACK) - verticalVelocity = BOAT_MAX_BACK; - - if (*y > height) - *y = height; - } - - return verticalVelocity; - } - - int SpeedBoatDynamics(ItemInfo* laraItem, short itemNumber) - { - auto* lara = GetLaraInfo(laraItem); - auto* sBoatItem = &g_Level.Items[itemNumber]; - auto* sBoat = (SpeedBoatInfo*)sBoatItem->Data; - - sBoatItem->Pose.Orientation.z -= sBoat->LeanAngle; - - Vector3Int old, frontLeftOld, frontRightOld, backLeftOld, backRightOld, frontOld; - int heightFrontLeftOld = SpeedBoatTestWaterHeight(sBoatItem, BOAT_FRONT, -BOAT_SIDE, &frontLeftOld); - int heightFrontRightOld = SpeedBoatTestWaterHeight(sBoatItem, BOAT_FRONT, BOAT_SIDE, &frontRightOld); - int heightBackLeftOld = SpeedBoatTestWaterHeight(sBoatItem, -BOAT_FRONT, -BOAT_SIDE, &backLeftOld); - int heightBackRightOld = SpeedBoatTestWaterHeight(sBoatItem, -BOAT_FRONT, BOAT_SIDE, &backRightOld); - int heightFrontOld = SpeedBoatTestWaterHeight(sBoatItem, BOAT_TIP, 0, &frontOld); - - old.x = sBoatItem->Pose.Position.x; - old.y = sBoatItem->Pose.Position.y; - old.z = sBoatItem->Pose.Position.z; - - if (backLeftOld.y > heightBackLeftOld) - backLeftOld.y = heightBackLeftOld; - if (backRightOld.y > heightBackRightOld) - backRightOld.y = heightBackRightOld; - if (frontLeftOld.y > heightFrontLeftOld) - frontLeftOld.y = heightFrontLeftOld; - if (frontRightOld.y > heightFrontRightOld) - frontRightOld.y = heightFrontRightOld; - if (frontOld.y > heightFrontOld) - frontOld.y = heightFrontOld; - - sBoatItem->Pose.Orientation.y += sBoat->TurnRate + sBoat->ExtraRotation; - sBoat->LeanAngle = sBoat->TurnRate * 6; - - sBoatItem->Pose.Position.x += sBoatItem->Animation.Velocity * phd_sin(sBoatItem->Pose.Orientation.y); - sBoatItem->Pose.Position.z += sBoatItem->Animation.Velocity * phd_cos(sBoatItem->Pose.Orientation.y); - - int slip = BOAT_SIDE_SLIP * phd_sin(sBoatItem->Pose.Orientation.z); - if (!slip && sBoatItem->Pose.Orientation.z) - slip = (sBoatItem->Pose.Orientation.z > 0) ? 1 : -1; - sBoatItem->Pose.Position.x += slip * phd_sin(sBoatItem->Pose.Orientation.y); - sBoatItem->Pose.Position.z -= slip * phd_cos(sBoatItem->Pose.Orientation.y); - - slip = BOAT_SLIP * phd_sin(sBoatItem->Pose.Orientation.x); - if (!slip && sBoatItem->Pose.Orientation.x) - slip = (sBoatItem->Pose.Orientation.x > 0) ? 1 : -1; - sBoatItem->Pose.Position.x -= slip * phd_sin(sBoatItem->Pose.Orientation.y); - sBoatItem->Pose.Position.z -= slip * phd_cos(sBoatItem->Pose.Orientation.y); - - auto moved = Vector3Int(sBoatItem->Pose.Position.x, 0, sBoatItem->Pose.Position.z); - - SpeedBoatDoBoatShift(sBoatItem, itemNumber); - - Vector3Int fl, fr, br, bl, f; - short rotation = 0; - auto heightBackLeft = SpeedBoatTestWaterHeight(sBoatItem, -BOAT_FRONT, -BOAT_SIDE, &bl); - if (heightBackLeft < (backLeftOld.y - CLICK(0.5f))) - rotation = SpeedBoatDoShift(sBoatItem, &bl, &backLeftOld); - - auto heightBackRight = SpeedBoatTestWaterHeight(sBoatItem, -BOAT_FRONT, BOAT_SIDE, &br); - if (heightBackRight < (backRightOld.y - CLICK(0.5f))) - rotation += SpeedBoatDoShift(sBoatItem, &br, &backRightOld); - - auto heightFrontLeft = SpeedBoatTestWaterHeight(sBoatItem, BOAT_FRONT, -BOAT_SIDE, &fl); - if (heightFrontLeft < (frontLeftOld.y - CLICK(0.5f))) - rotation += SpeedBoatDoShift(sBoatItem, &fl, &frontLeftOld); - - auto heightFrontRight = SpeedBoatTestWaterHeight(sBoatItem, BOAT_FRONT, BOAT_SIDE, &fr); - if (heightFrontRight < (frontRightOld.y - CLICK(0.5f))) - rotation += SpeedBoatDoShift(sBoatItem, &fr, &frontRightOld); - - int heightFront = 0; - if (!slip) - { - heightFront = SpeedBoatTestWaterHeight(sBoatItem, BOAT_TIP, 0, &f); - if (heightFront < (frontOld.y - CLICK(0.5f))) - SpeedBoatDoShift(sBoatItem, &f, &frontOld); - } - - auto probe = GetCollision(sBoatItem); - auto height = GetWaterHeight(sBoatItem->Pose.Position.x, sBoatItem->Pose.Position.y - 5, sBoatItem->Pose.Position.z, probe.RoomNumber); - - if (height == NO_HEIGHT) - height = GetFloorHeight(probe.Block, sBoatItem->Pose.Position.x, sBoatItem->Pose.Position.y - 5, sBoatItem->Pose.Position.z); - - if (height < (sBoatItem->Pose.Position.y - CLICK(0.5f))) - SpeedBoatDoShift(sBoatItem, (Vector3Int*)&sBoatItem->Pose, &old); - - sBoat->ExtraRotation = rotation; - - DoVehicleCollision(sBoatItem, BOAT_RADIUS); - - auto collide = GetSpeedBoatHitAnim(sBoatItem, &moved); - - int newVelocity = 0; - if (slip || collide) - { - newVelocity = (sBoatItem->Pose.Position.z - old.z) * phd_cos(sBoatItem->Pose.Orientation.y) + (sBoatItem->Pose.Position.x - old.x) * phd_sin(sBoatItem->Pose.Orientation.y); - - if (lara->Vehicle == itemNumber && sBoatItem->Animation.Velocity > BOAT_MAX_VELOCITY + BOAT_ACCELERATION && newVelocity < sBoatItem->Animation.Velocity - 10) - { - DoDamage(laraItem, sBoatItem->Animation.Velocity); - SoundEffect(SFX_TR4_LARA_INJURY, &laraItem->Pose); - newVelocity /= 2; - sBoatItem->Animation.Velocity /= 2; - } - - if (slip) - { - if (sBoatItem->Animation.Velocity <= BOAT_MAX_VELOCITY + 10) - sBoatItem->Animation.Velocity = newVelocity; - } - else - { - if (sBoatItem->Animation.Velocity > 0 && newVelocity < sBoatItem->Animation.Velocity) - sBoatItem->Animation.Velocity = newVelocity; - else if (sBoatItem->Animation.Velocity < 0 && newVelocity > sBoatItem->Animation.Velocity) - sBoatItem->Animation.Velocity = newVelocity; - } - - if (sBoatItem->Animation.Velocity < BOAT_MAX_BACK) - sBoatItem->Animation.Velocity = BOAT_MAX_BACK; - } - - return collide; - } - - bool SpeedBoatUserControl(ItemInfo* laraItem, ItemInfo* sBoatItem) - { - auto* sBoat = (SpeedBoatInfo*)sBoatItem->Data; - - bool noTurn = true; - int maxVelocity; - - if (sBoatItem->Pose.Position.y >= sBoat->Water - CLICK(0.5f) && sBoat->Water != NO_HEIGHT) - { - if (!(TrInput & SBOAT_IN_DISMOUNT) && !(TrInput & IN_LOOK) || - sBoatItem->Animation.Velocity) - { - if (TrInput & SBOAT_IN_LEFT && !(TrInput & SBOAT_IN_REVERSE) || - TrInput & SBOAT_IN_RIGHT && TrInput & SBOAT_IN_REVERSE) - { - if (sBoat->TurnRate > 0) - sBoat->TurnRate -= BOAT_UNDO_TURN; - else - { - sBoat->TurnRate -= BOAT_TURN; - if (sBoat->TurnRate < -BOAT_MAX_TURN) - sBoat->TurnRate = -BOAT_MAX_TURN; - } - - noTurn = false; - } - else if (TrInput & SBOAT_IN_RIGHT && !(TrInput & SBOAT_IN_REVERSE) || - TrInput & SBOAT_IN_LEFT && TrInput & SBOAT_IN_REVERSE) - { - if (sBoat->TurnRate < 0) - sBoat->TurnRate += BOAT_UNDO_TURN; - else - { - sBoat->TurnRate += BOAT_TURN; - if (sBoat->TurnRate > BOAT_MAX_TURN) - sBoat->TurnRate = BOAT_MAX_TURN; - } - - noTurn = false; - } - - if (TrInput & SBOAT_IN_REVERSE) - { - if (sBoatItem->Animation.Velocity > 0) - sBoatItem->Animation.Velocity -= BOAT_BRAKE; - else if (sBoatItem->Animation.Velocity > BOAT_MAX_BACK) - sBoatItem->Animation.Velocity += BOAT_REVERSE; - } - else if (TrInput & SBOAT_IN_ACCELERATE) - { - if (TrInput & SBOAT_IN_SPEED) - maxVelocity = BOAT_FAST_SPEED; - else - maxVelocity = (TrInput & SBOAT_IN_SLOW) ? BOAT_SLOW_SPEED : BOAT_MAX_VELOCITY; - - if (sBoatItem->Animation.Velocity < maxVelocity) - sBoatItem->Animation.Velocity += (BOAT_ACCELERATION / 2) + (BOAT_ACCELERATION * (sBoatItem->Animation.Velocity / (maxVelocity * 2))); - else if (sBoatItem->Animation.Velocity > maxVelocity + BOAT_SLOWDOWN) - sBoatItem->Animation.Velocity -= BOAT_SLOWDOWN; - } - else if (TrInput & (SBOAT_IN_LEFT | SBOAT_IN_RIGHT) && - sBoatItem->Animation.Velocity >= 0 && - sBoatItem->Animation.Velocity < BOAT_MIN_SPEED) - { - if (!(TrInput & SBOAT_IN_DISMOUNT) && - sBoatItem->Animation.Velocity == 0) - sBoatItem->Animation.Velocity = BOAT_MIN_SPEED; - } - else if (sBoatItem->Animation.Velocity > BOAT_SLOWDOWN) - sBoatItem->Animation.Velocity -= BOAT_SLOWDOWN; - else - sBoatItem->Animation.Velocity = 0; - } - else - { - if (TrInput & (SBOAT_IN_LEFT | SBOAT_IN_RIGHT) && - sBoatItem->Animation.Velocity >= 0 && - sBoatItem->Animation.Velocity < BOAT_MIN_SPEED) - { - if (sBoatItem->Animation.Velocity == 0 && !(TrInput & SBOAT_IN_DISMOUNT)) - sBoatItem->Animation.Velocity = BOAT_MIN_SPEED; - } - else if (sBoatItem->Animation.Velocity > BOAT_SLOWDOWN) - sBoatItem->Animation.Velocity -= BOAT_SLOWDOWN; - else - sBoatItem->Animation.Velocity = 0; - - if (TrInput & IN_LOOK && sBoatItem->Animation.Velocity == 0) - LookUpDown(laraItem); - } - } - - return noTurn; - } - - void SpeedBoatAnimation(ItemInfo* laraItem, ItemInfo* sBoatItem, int collide) - { - auto* sBoat = (SpeedBoatInfo*)sBoatItem->Data; - - if (laraItem->HitPoints <= 0) - { - if (laraItem->Animation.ActiveState != SBOAT_STATE_DEATH) - { - laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + SBOAT_ANIM_DEATH; - laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - laraItem->Animation.ActiveState = laraItem->Animation.TargetState = SBOAT_STATE_DEATH; - } - } - else if (sBoatItem->Pose.Position.y < sBoat->Water - CLICK(0.5f) && sBoatItem->Animation.VerticalVelocity > 0) - { - if (laraItem->Animation.ActiveState != SBOAT_STATE_FALL) - { - laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + SBOAT_ANIM_LEAP_START; - laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - laraItem->Animation.ActiveState = laraItem->Animation.TargetState = SBOAT_STATE_FALL; - } - } - else if (collide) - { - if (laraItem->Animation.ActiveState != SBOAT_STATE_HIT) - { - laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + collide; - laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - laraItem->Animation.ActiveState = laraItem->Animation.TargetState = SBOAT_STATE_HIT; - } - } - else - { - switch (laraItem->Animation.ActiveState) - { - case SBOAT_STATE_IDLE: - if (TrInput & SBOAT_IN_DISMOUNT) - { - if (sBoatItem->Animation.Velocity == 0) - { - if (TrInput & SBOAT_IN_RIGHT && TestSpeedBoatDismount(sBoatItem, sBoatItem->Pose.Orientation.y + ANGLE(90.0f))) - laraItem->Animation.TargetState = SBOAT_STATE_DISMOUNT_RIGHT; - else if (TrInput & SBOAT_IN_LEFT && TestSpeedBoatDismount(sBoatItem, sBoatItem->Pose.Orientation.y - ANGLE(90.0f))) - laraItem->Animation.TargetState = SBOAT_STATE_DISMOUNT_LEFT; - } - } - - if (sBoatItem->Animation.Velocity > 0) - laraItem->Animation.TargetState = SBOAT_STATE_MOVING; - - break; - - case SBOAT_STATE_MOVING: - if (TrInput & SBOAT_IN_DISMOUNT) - { - if (TrInput & SBOAT_IN_RIGHT) - laraItem->Animation.TargetState = SBOAT_STATE_DISMOUNT_RIGHT; - else if (TrInput & SBOAT_IN_RIGHT) - laraItem->Animation.TargetState = SBOAT_STATE_DISMOUNT_LEFT; - } - else if (sBoatItem->Animation.Velocity <= 0) - laraItem->Animation.TargetState = SBOAT_STATE_IDLE; - - break; - - case SBOAT_STATE_FALL: - laraItem->Animation.TargetState = SBOAT_STATE_MOVING; - break; - - //case BOAT_TURNR: - if (sBoatItem->Animation.Velocity <= 0) - laraItem->Animation.TargetState = SBOAT_STATE_IDLE; - else if (!(TrInput & SBOAT_IN_RIGHT)) - laraItem->Animation.TargetState = SBOAT_STATE_MOVING; - - break; - - case SBOAT_STATE_TURN_LEFT: - if (sBoatItem->Animation.Velocity <= 0) - laraItem->Animation.TargetState = SBOAT_STATE_IDLE; - else if (!(TrInput & SBOAT_IN_LEFT)) - laraItem->Animation.TargetState = SBOAT_STATE_MOVING; - - break; - } - } - } - - void SpeedBoatSplash(ItemInfo* item, long verticalVelocity, long water) - { - //OLD SPLASH - /* - splash_setup.x = item->pos.x_pos; - splash_setup.y = water; - splash_setup.z = item->pos.z_pos; - splash_setup.InnerXZoff = 16 << 2; - splash_setup.InnerXZsize = 12 << 2; - splash_setup.InnerYsize = -96 << 2; - splash_setup.InnerXZvel = 0xa0; - splash_setup.InnerYvel = -fallspeed << 7; - splash_setup.InnerGravity = 0x80; - splash_setup.InnerFriction = 7; - splash_setup.MiddleXZoff = 24 << 2; - splash_setup.MiddleXZsize = 24 << 2; - splash_setup.MiddleYsize = -64 << 2; - splash_setup.MiddleXZvel = 0xe0; - splash_setup.MiddleYvel = -fallspeed << 6; - splash_setup.MiddleGravity = 0x48; - splash_setup.MiddleFriction = 8; - splash_setup.OuterXZoff = 32 << 2; - splash_setup.OuterXZsize = 32 << 2; - splash_setup.OuterXZvel = 0x110; - splash_setup.OuterFriction = 9; - SetupSplash(&splash_setup); - SplashCount = 16; - */ - } - - void SpeedBoatCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) - { - auto* lara = GetLaraInfo(laraItem); - - if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) - return; - - auto* sBoatItem = &g_Level.Items[itemNumber]; - - switch (GetSpeedBoatMountType(laraItem, sBoatItem, coll)) - { - case BoatMountType::None: - coll->Setup.EnableObjectPush = true; - ObjectCollision(itemNumber, laraItem, coll); - return; - - case BoatMountType::WaterLeft: - laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + SBOAT_ANIM_MOUNT_LEFT; - break; - - case BoatMountType::WaterRight: - laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + SBOAT_ANIM_MOUNT_RIGHT; - break; - - case BoatMountType::Jump: - laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + SBOAT_ANIM_MOUNT_JUMP; - break; - - case BoatMountType::StartPosition: - laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + SBOAT_ANIM_IDLE; - break; - } - - laraItem->Pose.Position.x = sBoatItem->Pose.Position.x; - laraItem->Pose.Position.y = sBoatItem->Pose.Position.y - 5; - laraItem->Pose.Position.z = sBoatItem->Pose.Position.z; - laraItem->Pose.Orientation.x = 0; - laraItem->Pose.Orientation.y = sBoatItem->Pose.Orientation.y; - laraItem->Pose.Orientation.z = 0; - laraItem->Animation.Velocity = 0; - laraItem->Animation.VerticalVelocity = 0; - laraItem->Animation.Airborne = false; - laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - laraItem->Animation.ActiveState = SBOAT_STATE_MOUNT; - laraItem->Animation.TargetState = SBOAT_STATE_MOUNT; - lara->Control.WaterStatus = WaterStatus::Dry; - - if (laraItem->RoomNumber != sBoatItem->RoomNumber) - ItemNewRoom(lara->ItemNumber, sBoatItem->RoomNumber); - - AnimateItem(laraItem); - - if (g_Level.Items[itemNumber].Status != ITEM_ACTIVE) - { - AddActiveItem(itemNumber); - g_Level.Items[itemNumber].Status = ITEM_ACTIVE; - } - - lara->Vehicle = itemNumber; - } - - void SpeedBoatControl(short itemNumber) - { - auto* laraItem = LaraItem; - auto* lara = GetLaraInfo(laraItem); - auto* sBoatItem = &g_Level.Items[itemNumber]; - auto* sBoat = (SpeedBoatInfo*)sBoatItem->Data; - - int collide = SpeedBoatDynamics(laraItem, itemNumber); - - Vector3Int frontLeft, frontRight; - int heightFrontLeft = SpeedBoatTestWaterHeight(sBoatItem, BOAT_FRONT, -BOAT_SIDE, &frontLeft); - int heightFrontRight = SpeedBoatTestWaterHeight(sBoatItem, BOAT_FRONT, BOAT_SIDE, &frontRight); - - auto probe = GetCollision(sBoatItem); - - if (lara->Vehicle == itemNumber) - { - TestTriggers(sBoatItem, true); - TestTriggers(sBoatItem, false); - } - - auto water = GetWaterHeight(sBoatItem->Pose.Position.x, sBoatItem->Pose.Position.y, sBoatItem->Pose.Position.z, probe.RoomNumber); - sBoat->Water = water; - - bool noTurn = true; - bool drive = false; - bool idle = !sBoatItem->Animation.Velocity; - - if (lara->Vehicle == itemNumber && laraItem->HitPoints > 0) - { - switch (laraItem->Animation.ActiveState) - { - case SBOAT_STATE_MOUNT: - case SBOAT_STATE_DISMOUNT_RIGHT: - case SBOAT_STATE_DISMOUNT_LEFT: - break; - - default: - drive = true; - noTurn = SpeedBoatUserControl(laraItem, sBoatItem); - break; - } - } - else - { - if (sBoatItem->Animation.Velocity > BOAT_SLOWDOWN) - sBoatItem->Animation.Velocity -= BOAT_SLOWDOWN; - else - sBoatItem->Animation.Velocity = 0; - } - - if (noTurn) - { - if (sBoat->TurnRate < -BOAT_UNDO_TURN) - sBoat->TurnRate += BOAT_UNDO_TURN; - else if (sBoat->TurnRate > BOAT_UNDO_TURN) - sBoat->TurnRate -= BOAT_UNDO_TURN; - else - sBoat->TurnRate = 0; - } - - sBoatItem->Floor = probe.Position.Floor - 5; - if (sBoat->Water == NO_HEIGHT) - sBoat->Water = probe.Position.Floor; - else - sBoat->Water -= 5; - - sBoat->LeftVerticalVelocity = DoSpeedBoatDynamics(heightFrontLeft, sBoat->LeftVerticalVelocity, (int*)&frontLeft.y); - sBoat->RightVerticalVelocity = DoSpeedBoatDynamics(heightFrontRight, sBoat->RightVerticalVelocity, (int*)&frontRight.y); - sBoatItem->Animation.VerticalVelocity = DoSpeedBoatDynamics(sBoat->Water, sBoatItem->Animation.VerticalVelocity, (int*)&sBoatItem->Pose.Position.y); - - auto ofs = sBoatItem->Animation.VerticalVelocity; - if (ofs - sBoatItem->Animation.VerticalVelocity > 32 && sBoatItem->Animation.VerticalVelocity == 0 && water != NO_HEIGHT) - SpeedBoatSplash(sBoatItem, ofs - sBoatItem->Animation.VerticalVelocity, water); - - probe.Position.Floor = (frontLeft.y + frontRight.y); - if (probe.Position.Floor < 0) - probe.Position.Floor = -(abs(probe.Position.Floor) / 2); - else - probe.Position.Floor /= 2; - - short xRot = phd_atan(BOAT_FRONT, sBoatItem->Pose.Position.y - probe.Position.Floor); - short zRot = phd_atan(BOAT_SIDE, probe.Position.Floor - frontLeft.y); - - sBoatItem->Pose.Orientation.x += ((xRot - sBoatItem->Pose.Orientation.x) / 2); - sBoatItem->Pose.Orientation.z += ((zRot - sBoatItem->Pose.Orientation.z) / 2); - - if (!xRot && abs(sBoatItem->Pose.Orientation.x) < 4) - sBoatItem->Pose.Orientation.x = 0; - if (!zRot && abs(sBoatItem->Pose.Orientation.z) < 4) - sBoatItem->Pose.Orientation.z = 0; - - if (lara->Vehicle == itemNumber) - { - SpeedBoatAnimation(laraItem, sBoatItem, collide); - - if (probe.RoomNumber != sBoatItem->RoomNumber) - { - ItemNewRoom(lara->Vehicle, probe.RoomNumber); - ItemNewRoom(lara->ItemNumber, probe.RoomNumber); - } - - laraItem->Pose.Position.x = sBoatItem->Pose.Position.x; - laraItem->Pose.Position.y = sBoatItem->Pose.Position.y; - laraItem->Pose.Position.z = sBoatItem->Pose.Position.z; - laraItem->Pose.Orientation.x = sBoatItem->Pose.Orientation.x; - laraItem->Pose.Orientation.y = sBoatItem->Pose.Orientation.y; - laraItem->Pose.Orientation.z = sBoatItem->Pose.Orientation.z; - sBoatItem->Pose.Orientation.z += sBoat->LeanAngle; - - AnimateItem(laraItem); - - if (laraItem->HitPoints > 0) - { - sBoatItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT].animIndex + (laraItem->Animation.AnimNumber - Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex); - sBoatItem->Animation.FrameNumber = g_Level.Anims[sBoatItem->Animation.AnimNumber].frameBase + (laraItem->Animation.FrameNumber - g_Level.Anims[laraItem->Animation.AnimNumber].frameBase); - } - - Camera.targetElevation = -ANGLE(20.0f); - Camera.targetDistance = SECTOR(2); - - auto pitch = sBoatItem->Animation.Velocity; - sBoat->Pitch += (pitch - sBoat->Pitch) / 4; - - if (drive) - { - bool accelerating = idle && abs(sBoatItem->Animation.Velocity) > 4; - bool moving = (abs(sBoatItem->Animation.Velocity) > 8 || sBoat->TurnRate); - int fx = accelerating ? SFX_TR2_VEHICLE_SPEEDBOAT_ACCELERATE : (moving ? SFX_TR2_VEHICLE_SPEEDBOAT_MOVING : SFX_TR2_VEHICLE_SPEEDBOAT_IDLE); - float pitch = idle ? 1.0f : 1.0f + sBoat->Pitch / (float)BOAT_MAX_VELOCITY / 4.0f; - SoundEffect(fx, &sBoatItem->Pose, SoundEnvironment::Land, pitch); - } - } - else - { - if (probe.RoomNumber != sBoatItem->RoomNumber) - ItemNewRoom(itemNumber, probe.RoomNumber); - - sBoatItem->Pose.Orientation.z += sBoat->LeanAngle; - } - - if (sBoatItem->Animation.Velocity && (water - 5) == sBoatItem->Pose.Position.y) - { - auto room = probe.Block->RoomBelow(sBoatItem->Pose.Position.x, sBoatItem->Pose.Position.z).value_or(NO_ROOM); - if (room != NO_ROOM && (TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, room) || TestEnvironment(RoomEnvFlags::ENV_FLAG_SWAMP, room))) - TEN::Effects::TriggerSpeedboatFoam(sBoatItem, Vector3(0, 0, BOAT_BACK)); - } - - if (lara->Vehicle != itemNumber) - return; - - DoSpeedBoatDismount(laraItem, sBoatItem); - } -} diff --git a/TombEngine/Objects/TR2/Vehicles/boat.h b/TombEngine/Objects/TR2/Vehicles/boat.h deleted file mode 100644 index b260a0941..000000000 --- a/TombEngine/Objects/TR2/Vehicles/boat.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include "Game/collision/collide_room.h" -#include "Game/items.h" - -namespace TEN::Entities::Vehicles -{ - enum class BoatMountType - { - None = 0, - WaterRight = 1, - WaterLeft = 2, - Jump = 3, - StartPosition = 4 - }; - - void InitialiseSpeedBoat(short itemNumber); - BoatMountType GetSpeedBoatMountType(ItemInfo* laraItem, ItemInfo* sBoatItem, CollisionInfo* coll); - bool TestSpeedBoatDismount(ItemInfo* sBoatItem, int direction); - void DoSpeedBoatDismount(ItemInfo* laraItem, ItemInfo* sBoatItem); - int SpeedBoatTestWaterHeight(ItemInfo* sBoatItem, int zOffset, int xOffset, Vector3Int* pos); - - void SpeedBoatDoBoatShift(ItemInfo* sBoatItem, int itemNumber); - short SpeedBoatDoShift(ItemInfo* sBoatItem, Vector3Int* pos, Vector3Int* old); - - int GetSpeedBoatHitAnim(ItemInfo* sBoatItem, Vector3Int* moved); - int DoSpeedBoatDynamics(int height, int verticalVelocity, int* y); - int SpeedBoatDynamics(ItemInfo* laraItem, short itemNumber); - bool SpeedBoatUserControl(ItemInfo* laraItem, ItemInfo* sBoatItem); - void SpeedBoatAnimation(ItemInfo* laraItem, ItemInfo* sBoatItem, int collide); - void SpeedBoatSplash(ItemInfo* item, long verticalVelocity, long water); - void SpeedBoatCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); - void SpeedBoatControl(short itemNumber); -} diff --git a/TombEngine/Objects/TR2/Vehicles/boat_info.h b/TombEngine/Objects/TR2/Vehicles/boat_info.h deleted file mode 100644 index 0275ce1c9..000000000 --- a/TombEngine/Objects/TR2/Vehicles/boat_info.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -namespace TEN::Entities::Vehicles -{ - struct SpeedBoatInfo - { - int TurnRate; - short LeanAngle; - short ExtraRotation; - - int LeftVerticalVelocity; - int RightVerticalVelocity; - - int Water; - int Pitch; - }; -} diff --git a/TombEngine/Objects/TR2/Vehicles/skidoo.cpp b/TombEngine/Objects/TR2/Vehicles/skidoo.cpp index 257a730a3..07a7261f1 100644 --- a/TombEngine/Objects/TR2/Vehicles/skidoo.cpp +++ b/TombEngine/Objects/TR2/Vehicles/skidoo.cpp @@ -23,53 +23,46 @@ using namespace TEN::Input; using namespace TEN::Math::Random; +using std::vector; namespace TEN::Entities::Vehicles { - #define DAMAGE_START 140 - #define DAMAGE_LENGTH 14 + constexpr auto SKIDOO_RADIUS = 500; + constexpr auto SKIDOO_FRONT = 550; + constexpr auto SKIDOO_SIDE = 260; + constexpr auto SKIDOO_SLIP = 100; + constexpr auto SKIDOO_SLIP_SIDE = 50; + constexpr auto SKIDOO_SNOW = 500; // Unused. + constexpr auto SKIDOO_MOUNT_DISTANCE = CLICK(2); + constexpr auto SKIDOO_DISMOUNT_DISTANCE = 295; - #define SKIDOO_DISMOUNT_DISTANCE 295 + constexpr auto SKIDOO_VELOCITY_ACCEL = 10; + constexpr auto SKIDOO_VELOCITY_DECEL = 2; + constexpr auto SKIDOO_VELOCITY_BRAKE_DECEL = 5; + constexpr auto SKIDOO_REVERSE_VELOCITY_ACCEL = 5; + constexpr auto SKIDOO_KICK_MAX = -80; - #define SKIDOO_UNDO_TURN ANGLE(2.0f) - #define SKIDOO_TURN (ANGLE(0.5f) + SKIDOO_UNDO_TURN) - #define SKIDOO_MAX_TURN ANGLE(6.0f) - #define SKIDOO_MOMENTUM_TURN ANGLE(3.0f) - #define SKIDOO_MAX_MOMENTUM_TURN ANGLE(150.0f) + constexpr auto SKIDOO_SLOW_VELOCITY_MAX = 50; + constexpr auto SKIDOO_NORMAL_VELOCITY_MAX = 100; + constexpr auto SKIDOO_FAST_VELOCITY_MAX = 150; + constexpr auto SKIDOO_TURN_VELOCITY_MAX = 15; + constexpr auto SKIDOO_REVERSE_VELOCITY_MAX = 30; - #define SKIDOO_FAST_VELOCITY 150 - #define SKIDOO_MAX_VELOCITY 100 - #define SKIDOO_SLOW_VELOCITY 50 - #define SKIDOO_MIN_VELOCITY 15 + constexpr auto SKIDOO_STEP_HEIGHT_MAX = CLICK(1); // Unused. + constexpr auto SKIDOO_MIN_BOUNCE = (SKIDOO_NORMAL_VELOCITY_MAX / 2) / 256; - #define SKIDOO_ACCELERATION 10 - #define SKIDOO_BRAKE 5 - #define SKIDOO_SLOWDOWN 2 - #define SKIDOO_REVERSE -5 - #define SKIDOO_MAX_BACK -30 - #define SKIDOO_MAX_KICK -80 + constexpr auto SKIDOO_DAMAGE_START = 140; + constexpr auto SKIDOO_DAMAGE_LENGTH = 14; - #define SKIDOO_SLIP 100 - #define SKIDOO_SLIP_SIDE 50 - #define SKIDOO_FRONT 550 - #define SKIDOO_SIDE 260 - #define SKIDOO_RADIUS 500 - #define SKIDOO_SNOW 500 - - #define SKIDOO_MAX_HEIGHT CLICK(1) - #define SKIDOO_MIN_BOUNCE ((SKIDOO_MAX_VELOCITY / 2) / 256) - - #define SKIDOO_IN_ACCELERATE IN_FORWARD - #define SKIDOO_IN_BRAKE IN_BACK - #define SKIDOO_IN_SLOW IN_WALK - #define SKIDOO_IN_FIRE IN_ACTION - #define SKIDOO_IN_DISMOUNT (IN_JUMP | IN_ROLL) - #define SKIDOO_IN_LEFT IN_LEFT - #define SKIDOO_IN_RIGHT IN_RIGHT + #define SKIDOO_TURN_RATE_ACCEL ANGLE(2.5f) + #define SKIDOO_TURN_RATE_DECEL ANGLE(2.0f) + #define SKIDOO_TURN_RATE_MAX ANGLE(6.0f) + #define SKIDOO_MOMENTUM_TURN_RATE_ACCEL ANGLE(3.0f) + #define SKIDOO_MOMENTUM_TURN_RATE_MAX ANGLE(150.0f) enum SkidooState { - SKIDOO_STATE_SIT = 0, + SKIDOO_STATE_DRIVE = 0, SKIDOO_STATE_MOUNT = 1, SKIDOO_STATE_LEFT = 2, SKIDOO_STATE_RIGHT = 3, @@ -111,62 +104,86 @@ namespace TEN::Entities::Vehicles SKIDOO_ANIM_FALL_DEATH = 22 }; + const vector SkidooMountTypes = + { + VehicleMountType::LevelStart, + VehicleMountType::Left, + VehicleMountType::Right + }; + + SkidooInfo* GetSkidooInfo(ItemInfo* skidooItem) + { + return (SkidooInfo*)skidooItem->Data; + } + void InitialiseSkidoo(short itemNumber) { auto* skidooItem = &g_Level.Items[itemNumber]; skidooItem->Data = SkidooInfo(); - auto* skidoo = (SkidooInfo*)skidooItem->Data; - - skidoo->TurnRate = 0; - skidoo->MomentumAngle = skidooItem->Pose.Orientation.y; - skidoo->ExtraRotation = 0; - skidoo->LeftVerticalVelocity = 0; - skidoo->RightVerticalVelocity = 0; - skidoo->Pitch = 0; - skidoo->FlashTimer = 0; - - if (skidooItem->ObjectNumber == ID_SNOWMOBILE_GUN) - skidoo->Armed = true; - else - skidoo->Armed = false; + auto* skidoo = GetSkidooInfo(skidooItem); if (skidooItem->Status != ITEM_ACTIVE) { AddActiveItem(itemNumber); skidooItem->Status = ITEM_ACTIVE; } + + if (skidooItem->ObjectNumber == ID_SNOWMOBILE_GUN) + skidoo->Armed = true; + + skidoo->MomentumAngle = skidooItem->Pose.Orientation.y; } - int GetSkidooMountType(ItemInfo* laraItem, ItemInfo* skidooItem, CollisionInfo* coll) + void SkidooPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) { - auto* lara = (LaraInfo*&)laraItem->Data; + auto* skidooItem = &g_Level.Items[itemNumber]; + auto* lara = GetLaraInfo(laraItem); - int mountType = 0; + if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) + return; - if (!(TrInput & IN_ACTION) || - lara->Control.HandStatus != HandStatus::Free || - laraItem->Animation.Airborne) - { - return mountType = 0; - } - - short deltaAngle = skidooItem->Pose.Orientation.y - laraItem->Pose.Orientation.y; - if (deltaAngle > ANGLE(45.0f) && deltaAngle < ANGLE(135.0f)) - mountType = 1; - else if (deltaAngle > -ANGLE(135.0f) && deltaAngle < -ANGLE(45.0f)) - mountType = 2; + auto mountType = GetVehicleMountType(skidooItem, laraItem, coll, SkidooMountTypes, SKIDOO_MOUNT_DISTANCE); + if (mountType == VehicleMountType::None) + ObjectCollision(itemNumber, laraItem, coll); else - mountType = 0; - - auto probe = GetCollision(skidooItem); - if (probe.Position.Floor < -32000 || - !TestBoundsCollide(skidooItem, laraItem, coll->Setup.Radius) || - !TestCollision(skidooItem, laraItem)) { - mountType = 0; + lara->Vehicle = itemNumber; + DoSkidooMount(skidooItem, laraItem, mountType); } + } - return mountType; + void DoSkidooMount(ItemInfo* skidooItem, ItemInfo* laraItem, VehicleMountType mountType) + { + auto* lara = GetLaraInfo(laraItem); + + switch (mountType) + { + case VehicleMountType::LevelStart: + laraItem->Animation.AnimNumber = Objects[ID_SNOWMOBILE_LARA_ANIMS].animIndex + SKIDOO_ANIM_IDLE; + laraItem->Animation.ActiveState = SKIDOO_STATE_IDLE; + laraItem->Animation.TargetState = SKIDOO_STATE_IDLE; + break; + + case VehicleMountType::Left: + laraItem->Animation.AnimNumber = Objects[ID_SNOWMOBILE_LARA_ANIMS].animIndex + SKIDOO_ANIM_MOUNT_LEFT; + laraItem->Animation.ActiveState = SKIDOO_STATE_MOUNT; + laraItem->Animation.TargetState = SKIDOO_STATE_MOUNT; + break; + + default: + case VehicleMountType::Right: + laraItem->Animation.AnimNumber = Objects[ID_SNOWMOBILE_LARA_ANIMS].animIndex + SKIDOO_ANIM_MOUNT_RIGHT; + laraItem->Animation.ActiveState = SKIDOO_STATE_MOUNT; + laraItem->Animation.TargetState = SKIDOO_STATE_MOUNT; + break; + } + laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; + + DoVehicleFlareDiscard(laraItem); + laraItem->Pose.Position = skidooItem->Pose.Position; + laraItem->Pose.Orientation = Vector3Shrt(0, skidooItem->Pose.Orientation.y, 0); + lara->Control.HandStatus = HandStatus::Busy; + skidooItem->Collidable = true; } bool TestSkidooDismountOK(ItemInfo* skidooItem, int direction) @@ -177,10 +194,7 @@ namespace TEN::Entities::Vehicles else angle = skidooItem->Pose.Orientation.y - ANGLE(90.0f); - int x = skidooItem->Pose.Position.x - SKIDOO_DISMOUNT_DISTANCE * phd_sin(angle); - int y = skidooItem->Pose.Position.y; - int z = skidooItem->Pose.Position.z - SKIDOO_DISMOUNT_DISTANCE * phd_cos(angle); - auto probe = GetCollision(x, y, z, skidooItem->RoomNumber); + auto probe = GetCollision(skidooItem, angle, -SKIDOO_DISMOUNT_DISTANCE); if ((probe.Position.FloorSlope || probe.Position.Floor == NO_HEIGHT) || abs(probe.Position.Floor - skidooItem->Pose.Position.y) > CLICK(2) || @@ -193,9 +207,9 @@ namespace TEN::Entities::Vehicles return true; } - bool TestSkidooDismount(ItemInfo* laraItem, ItemInfo* skidooItem) + bool TestSkidooDismount(ItemInfo* skidooItem, ItemInfo* laraItem) { - auto* lara = (LaraInfo*&)laraItem->Data; + auto* lara = GetLaraInfo(laraItem); if (lara->Vehicle != NO_ITEM) { @@ -208,12 +222,11 @@ namespace TEN::Entities::Vehicles laraItem->Pose.Orientation.y -= ANGLE(90.0f); SetAnimation(laraItem, LA_STAND_IDLE); - laraItem->Pose.Position.x -= SKIDOO_DISMOUNT_DISTANCE * phd_sin(laraItem->Pose.Orientation.y); - laraItem->Pose.Position.z -= SKIDOO_DISMOUNT_DISTANCE * phd_cos(laraItem->Pose.Orientation.y); + TranslateItem(laraItem, laraItem->Pose.Orientation.y, -SKIDOO_DISMOUNT_DISTANCE); laraItem->Pose.Orientation.x = 0; laraItem->Pose.Orientation.z = 0; - lara->Vehicle = NO_ITEM; lara->Control.HandStatus = HandStatus::Free; + lara->Vehicle = NO_ITEM; } else if (laraItem->Animation.ActiveState == SKIDOO_STATE_JUMP_OFF && (skidooItem->Pose.Position.y == skidooItem->Floor || TestLastFrame(laraItem))) @@ -224,7 +237,7 @@ namespace TEN::Entities::Vehicles { laraItem->Animation.TargetState = LS_DEATH; laraItem->Animation.Velocity = 0; - laraItem->Animation.VerticalVelocity = DAMAGE_START + DAMAGE_LENGTH; + laraItem->Animation.VerticalVelocity = SKIDOO_DAMAGE_START + SKIDOO_DAMAGE_LENGTH; ExplodeVehicle(laraItem, skidooItem); } else @@ -238,11 +251,11 @@ namespace TEN::Entities::Vehicles laraItem->Pose.Orientation.x = 0; laraItem->Pose.Orientation.z = 0; - laraItem->Animation.Airborne = true; - lara->Control.HandStatus = HandStatus::Free; + laraItem->Animation.IsAirborne = true; lara->Control.MoveAngle = skidooItem->Pose.Orientation.y; - skidooItem->Flags |= ONESHOT; + lara->Control.HandStatus = HandStatus::Free; skidooItem->Collidable = false; + skidooItem->Flags |= IFLAG_INVISIBLE; return false; } @@ -253,128 +266,16 @@ namespace TEN::Entities::Vehicles return true; } - int GetSkidooCollisionAnim(ItemInfo* skidooItem, Vector3Int* moved) - { - moved->x = skidooItem->Pose.Position.x - moved->x; - moved->z = skidooItem->Pose.Position.z - moved->z; - - if (moved->x || moved->z) - { - float s = phd_sin(skidooItem->Pose.Orientation.y); - float c = phd_cos(skidooItem->Pose.Orientation.y); - - int side = -moved->z * s + moved->x * c; - int front = moved->z * c + moved->x * s; - - if (abs(front) > abs(side)) - { - if (front > 0) - return SKIDOO_ANIM_HIT_BACK; - else - return SKIDOO_ANIM_HIT_FRONT; - } - else - { - if (side > 0) - return SKIDOO_ANIM_HIT_LEFT; - else - return SKIDOO_ANIM_HIT_RIGHT; - } - } - - return 0; - } - - void SkidooCollision(short itemNum, ItemInfo* laraItem, CollisionInfo* coll) - { - auto* lara = (LaraInfo*&)laraItem->Data; - auto* skidooItem = &g_Level.Items[itemNum]; - - if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) - return; - - int mountType = GetSkidooMountType(laraItem, skidooItem, coll); - if (mountType == 0) - { - ObjectCollision(itemNum, laraItem, coll); - return; - } - - lara->Vehicle = itemNum; - - if (lara->Control.Weapon.GunType == LaraWeaponType::Flare) - { - CreateFlare(laraItem, ID_FLARE_ITEM, false); - UndrawFlareMeshes(laraItem); - lara->Flare.ControlLeft = 0; - lara->Control.Weapon.RequestGunType = LaraWeaponType::None; - lara->Control.HandStatus = HandStatus::Free; - } - - if (mountType == 1) - laraItem->Animation.AnimNumber = Objects[ID_SNOWMOBILE_LARA_ANIMS].animIndex + SKIDOO_ANIM_MOUNT_RIGHT; - else if (mountType == 2) - laraItem->Animation.AnimNumber = Objects[ID_SNOWMOBILE_LARA_ANIMS].animIndex + SKIDOO_ANIM_MOUNT_LEFT; - - laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - laraItem->Animation.ActiveState = SKIDOO_STATE_MOUNT; - laraItem->Pose.Position.x = skidooItem->Pose.Position.x; - laraItem->Pose.Position.y = skidooItem->Pose.Position.y; - laraItem->Pose.Position.z = skidooItem->Pose.Position.z; - laraItem->Pose.Orientation.y = skidooItem->Pose.Orientation.y; - lara->Control.HandStatus = HandStatus::Busy; - skidooItem->Collidable = true; - } - - void SkidooGuns(ItemInfo* laraItem, ItemInfo* skidooItem) - { - auto* lara = (LaraInfo*&)laraItem->Data; - auto* skidoo = (SkidooInfo*)skidooItem->Data; - auto* weapon = &Weapons[(int)LaraWeaponType::Snowmobile]; - - LaraGetNewTarget(laraItem, weapon); - AimWeapon(laraItem, weapon, &lara->RightArm); - - if (TrInput & SKIDOO_IN_FIRE && !skidooItem->ItemFlags[0]) - { - auto angles = Vector3Shrt( - lara->RightArm.Orientation.x, - lara->RightArm.Orientation.y + laraItem->Pose.Orientation.y, - 0 - ); - - if ((int)FireWeapon(LaraWeaponType::Pistol, lara->TargetEntity, laraItem, angles) + - (int)FireWeapon(LaraWeaponType::Pistol, lara->TargetEntity, laraItem, angles)) - { - skidoo->FlashTimer = 2; - SoundEffect(weapon->SampleNum, &laraItem->Pose); - skidooItem->ItemFlags[0] = 4; - } - } - - if (skidooItem->ItemFlags[0]) - skidooItem->ItemFlags[0]--; - } - - void DoSnowEffect(ItemInfo* skidooItem) - { - auto material = GetCollision(skidooItem).BottomBlock->Material; - if (material != FLOOR_MATERIAL::Ice && material != FLOOR_MATERIAL::Snow) - return; - - TEN::Effects::TriggerSnowmobileSnow(skidooItem); - } - bool SkidooControl(ItemInfo* laraItem, CollisionInfo* coll) { - auto* lara = (LaraInfo*&)laraItem->Data; + auto* lara = GetLaraInfo(laraItem); auto* skidooItem = &g_Level.Items[lara->Vehicle]; - auto* skidoo = (SkidooInfo*)skidooItem->Data; + auto* skidoo = GetSkidooInfo(skidooItem); Vector3Int frontLeft, frontRight; - auto collide = SkidooDynamics(laraItem, skidooItem); - auto heightFrontLeft = TestSkidooHeight(skidooItem, SKIDOO_FRONT, -SKIDOO_SIDE, &frontLeft); - auto heightFrontRight = TestSkidooHeight(skidooItem, SKIDOO_FRONT, SKIDOO_SIDE, &frontRight); + auto collide = SkidooDynamics(skidooItem, laraItem); + auto heightFrontLeft = GetVehicleHeight(skidooItem, SKIDOO_FRONT, -SKIDOO_SIDE, true, &frontLeft); + auto heightFrontRight = GetVehicleHeight(skidooItem, SKIDOO_FRONT, SKIDOO_SIDE, true, &frontRight); auto probe = GetCollision(skidooItem); @@ -398,7 +299,7 @@ namespace TEN::Entities::Vehicles int height = probe.Position.Floor; int pitch = 0; - if (skidooItem->Flags & ONESHOT) + if (skidooItem->Flags & IFLAG_INVISIBLE) { drive = 0; collide = 0; @@ -417,8 +318,7 @@ namespace TEN::Entities::Vehicles break; default: - drive = SkidooUserControl(laraItem, skidooItem, height, &pitch); - + drive = SkidooUserControl(skidooItem, laraItem, height, &pitch); break; } } @@ -429,7 +329,7 @@ namespace TEN::Entities::Vehicles skidoo->TrackMesh = ((skidoo->TrackMesh & 3) == 1) ? 2 : 1; skidoo->Pitch += (pitch - skidoo->Pitch) / 4; - auto pitch = std::clamp(0.5f + (float)abs(skidoo->Pitch) / (float)SKIDOO_MAX_VELOCITY, 0.6f, 1.4f); + auto pitch = std::clamp(0.5f + (float)abs(skidoo->Pitch) / (float)SKIDOO_NORMAL_VELOCITY_MAX, 0.6f, 1.4f); SoundEffect(skidoo->Pitch ? SFX_TR2_VEHICLE_SNOWMOBILE_MOVING : SFX_TR2_VEHICLE_SNOWMOBILE_ACCELERATE, &skidooItem->Pose, SoundEnvironment::Land, pitch); } else @@ -450,10 +350,10 @@ namespace TEN::Entities::Vehicles short xRot = phd_atan(SKIDOO_FRONT, skidooItem->Pose.Position.y - height); short zRot = phd_atan(SKIDOO_SIDE, height - frontLeft.y); - skidooItem->Pose.Orientation.x += ((xRot - skidooItem->Pose.Orientation.x) / 2); - skidooItem->Pose.Orientation.z += ((zRot - skidooItem->Pose.Orientation.z) / 2); + skidooItem->Pose.Orientation.x += (xRot - skidooItem->Pose.Orientation.x) / 2; + skidooItem->Pose.Orientation.z += (zRot - skidooItem->Pose.Orientation.z) / 2; - if (skidooItem->Flags & ONESHOT) + if (skidooItem->Flags & IFLAG_INVISIBLE) { if (probe.RoomNumber != skidooItem->RoomNumber) { @@ -466,10 +366,10 @@ namespace TEN::Entities::Vehicles if (skidooItem->Pose.Position.y == skidooItem->Floor) ExplodeVehicle(laraItem, skidooItem); - return 0; + return false; } - SkidooAnimation(laraItem, skidooItem, collide, dead); + SkidooAnimation(skidooItem, laraItem, collide, dead); if (probe.RoomNumber != skidooItem->RoomNumber) { @@ -479,9 +379,7 @@ namespace TEN::Entities::Vehicles if (laraItem->Animation.ActiveState != SKIDOO_STATE_FALLOFF) { - laraItem->Pose.Position.x = skidooItem->Pose.Position.x; - laraItem->Pose.Position.y = skidooItem->Pose.Position.y; - laraItem->Pose.Position.z = skidooItem->Pose.Position.z; + laraItem->Pose.Position = skidooItem->Pose.Position; laraItem->Pose.Orientation.y = skidooItem->Pose.Orientation.y; if (drive >= 0) @@ -498,7 +396,7 @@ namespace TEN::Entities::Vehicles AnimateItem(laraItem); if (!dead && drive >= 0 && banditSkidoo) - SkidooGuns(laraItem, skidooItem); + SkidooGuns(skidooItem, laraItem); if (!dead) { @@ -519,15 +417,15 @@ namespace TEN::Entities::Vehicles DoSnowEffect(skidooItem); } - return TestSkidooDismount(laraItem, skidooItem); + return TestSkidooDismount(skidooItem, laraItem); } - bool SkidooUserControl(ItemInfo* laraItem, ItemInfo* skidooItem, int height, int* pitch) + bool SkidooUserControl(ItemInfo* skidooItem, ItemInfo* laraItem, int height, int* pitch) { - auto* skidoo = (SkidooInfo*)skidooItem->Data; + auto* skidoo = GetSkidooInfo(skidooItem); - bool drive = false; int maxVelocity = 0; + bool drive = false; if (skidooItem->Pose.Position.y >= (height - CLICK(1))) { @@ -536,66 +434,55 @@ namespace TEN::Entities::Vehicles if (TrInput & IN_LOOK && skidooItem->Animation.Velocity == 0) LookUpDown(laraItem); - if ((TrInput & SKIDOO_IN_LEFT && !(TrInput & SKIDOO_IN_BRAKE)) || - (TrInput & SKIDOO_IN_RIGHT && TrInput & SKIDOO_IN_BRAKE)) - { - skidoo->TurnRate -= SKIDOO_TURN; - if (skidoo->TurnRate < -SKIDOO_MAX_TURN) - skidoo->TurnRate = -SKIDOO_MAX_TURN; - } + if (TrInput & (VEHICLE_IN_LEFT | VEHICLE_IN_RIGHT)) + ModulateVehicleTurnRateY(&skidoo->TurnRate, SKIDOO_TURN_RATE_ACCEL, -SKIDOO_TURN_RATE_MAX, SKIDOO_TURN_RATE_MAX); - if ((TrInput & SKIDOO_IN_RIGHT && !(TrInput & SKIDOO_IN_BRAKE)) || - (TrInput & SKIDOO_IN_LEFT && TrInput & SKIDOO_IN_BRAKE)) - { - skidoo->TurnRate += SKIDOO_TURN; - if (skidoo->TurnRate > SKIDOO_MAX_TURN) - skidoo->TurnRate = SKIDOO_MAX_TURN; - } - - if (TrInput & SKIDOO_IN_BRAKE) + if (TrInput & VEHICLE_IN_REVERSE) { if (skidooItem->Animation.Velocity > 0) - skidooItem->Animation.Velocity -= SKIDOO_BRAKE; + skidooItem->Animation.Velocity -= SKIDOO_VELOCITY_BRAKE_DECEL; else { - if (skidooItem->Animation.Velocity > SKIDOO_MAX_BACK) - skidooItem->Animation.Velocity += SKIDOO_REVERSE; + if (skidooItem->Animation.Velocity > -SKIDOO_REVERSE_VELOCITY_MAX) + skidooItem->Animation.Velocity -= SKIDOO_REVERSE_VELOCITY_ACCEL; drive = true; } } - else if (TrInput & SKIDOO_IN_ACCELERATE) + else if (TrInput & VEHICLE_IN_ACCELERATE) { - if (TrInput & SKIDOO_IN_FIRE && !skidoo->Armed) - maxVelocity = SKIDOO_FAST_VELOCITY; - else if (TrInput & SKIDOO_IN_SLOW) - maxVelocity = SKIDOO_SLOW_VELOCITY; + if (TrInput & VEHICLE_IN_SPEED) + maxVelocity = SKIDOO_FAST_VELOCITY_MAX; + else if (TrInput & VEHICLE_IN_SLOW) + maxVelocity = SKIDOO_SLOW_VELOCITY_MAX; else - maxVelocity = SKIDOO_MAX_VELOCITY; + maxVelocity = SKIDOO_NORMAL_VELOCITY_MAX; if (skidooItem->Animation.Velocity < maxVelocity) - skidooItem->Animation.Velocity += (SKIDOO_ACCELERATION / 2) + (SKIDOO_ACCELERATION * (skidooItem->Animation.Velocity / (2 * maxVelocity))); - else if (skidooItem->Animation.Velocity > (maxVelocity + SKIDOO_SLOWDOWN)) - skidooItem->Animation.Velocity -= SKIDOO_SLOWDOWN; + skidooItem->Animation.Velocity += (SKIDOO_VELOCITY_ACCEL / 2) + (SKIDOO_VELOCITY_ACCEL * (skidooItem->Animation.Velocity / (2 * maxVelocity))); + else if (skidooItem->Animation.Velocity > (maxVelocity + SKIDOO_VELOCITY_DECEL)) + skidooItem->Animation.Velocity -= SKIDOO_VELOCITY_DECEL; + drive = true; } - else if (TrInput & (SKIDOO_IN_LEFT | SKIDOO_IN_RIGHT) && + else if (TrInput & (VEHICLE_IN_LEFT | VEHICLE_IN_RIGHT) && skidooItem->Animation.Velocity >= 0 && - skidooItem->Animation.Velocity < SKIDOO_MIN_VELOCITY) + skidooItem->Animation.Velocity < SKIDOO_TURN_VELOCITY_MAX) { - skidooItem->Animation.Velocity = SKIDOO_MIN_VELOCITY; + skidooItem->Animation.Velocity = SKIDOO_TURN_VELOCITY_MAX; drive = true; } - else if (skidooItem->Animation.Velocity > SKIDOO_SLOWDOWN) + else if (skidooItem->Animation.Velocity > SKIDOO_VELOCITY_DECEL) { - skidooItem->Animation.Velocity -= SKIDOO_SLOWDOWN; + skidooItem->Animation.Velocity -= SKIDOO_VELOCITY_DECEL; + if ((GetRandomControl() & 0x7f) < skidooItem->Animation.Velocity) drive = true; } else skidooItem->Animation.Velocity = 0; } - else if (TrInput & (SKIDOO_IN_ACCELERATE | SKIDOO_IN_BRAKE)) + else if (TrInput & (VEHICLE_IN_ACCELERATE | VEHICLE_IN_REVERSE)) { *pitch = skidoo->Pitch + 50; drive = true; @@ -604,13 +491,13 @@ namespace TEN::Entities::Vehicles return drive; } - void SkidooAnimation(ItemInfo* laraItem, ItemInfo* skidooItem, int collide, bool dead) + void SkidooAnimation(ItemInfo* skidooItem, ItemInfo* laraItem, int collide, bool dead) { - auto* skidoo = (SkidooInfo*)skidooItem->Data; + auto* skidoo = GetSkidooInfo(skidooItem); if (laraItem->Animation.ActiveState != SKIDOO_STATE_FALL && - skidooItem->Pose.Position.y != skidooItem->Floor && skidooItem->Animation.VerticalVelocity > 0 && + skidooItem->Pose.Position.y != skidooItem->Floor && !dead) { laraItem->Animation.AnimNumber = Objects[ID_SNOWMOBILE_LARA_ANIMS].animIndex + SKIDOO_ANIM_LEAP_START; @@ -647,52 +534,88 @@ namespace TEN::Entities::Vehicles laraItem->Animation.TargetState = SKIDOO_STATE_IDLE; - if (TrInput & SKIDOO_IN_DISMOUNT) + if (TrInput & VEHICLE_IN_DISMOUNT) { - if (TrInput & SKIDOO_IN_RIGHT && + if (TrInput & VEHICLE_IN_RIGHT && TestSkidooDismountOK(skidooItem, SKIDOO_STATE_DISMOUNT_RIGHT)) { laraItem->Animation.TargetState = SKIDOO_STATE_DISMOUNT_RIGHT; skidooItem->Animation.Velocity = 0; } - else if (TrInput & SKIDOO_IN_LEFT && + else if (TrInput & VEHICLE_IN_LEFT && TestSkidooDismountOK(skidooItem, SKIDOO_STATE_DISMOUNT_LEFT)) { laraItem->Animation.TargetState = SKIDOO_STATE_DISMOUNT_LEFT; skidooItem->Animation.Velocity = 0; } } - else if (TrInput & SKIDOO_IN_LEFT) - laraItem->Animation.TargetState = SKIDOO_STATE_LEFT; - else if (TrInput & SKIDOO_IN_RIGHT) - laraItem->Animation.TargetState = SKIDOO_STATE_RIGHT; - else if (TrInput & (SKIDOO_IN_ACCELERATE | SKIDOO_IN_BRAKE)) - laraItem->Animation.TargetState = SKIDOO_STATE_SIT; + else if (TrInput & VEHICLE_IN_LEFT) + { + if (skidooItem->Animation.Velocity >= 0) + laraItem->Animation.TargetState = SKIDOO_STATE_LEFT; + else + laraItem->Animation.TargetState = SKIDOO_STATE_RIGHT; + } + else if (TrInput & VEHICLE_IN_RIGHT) + { + if (skidooItem->Animation.Velocity >= 0) + laraItem->Animation.TargetState = SKIDOO_STATE_RIGHT; + else + laraItem->Animation.TargetState = SKIDOO_STATE_LEFT; + } + else if (TrInput & (VEHICLE_IN_ACCELERATE | VEHICLE_IN_REVERSE)) + laraItem->Animation.TargetState = SKIDOO_STATE_DRIVE; break; - case SKIDOO_STATE_SIT: + case SKIDOO_STATE_DRIVE: if (skidooItem->Animation.Velocity == 0) laraItem->Animation.TargetState = SKIDOO_STATE_IDLE; if (dead) laraItem->Animation.TargetState = SKIDOO_STATE_FALLOFF; - else if (TrInput & SKIDOO_IN_LEFT) - laraItem->Animation.TargetState = SKIDOO_STATE_LEFT; - else if (TrInput & SKIDOO_IN_RIGHT) - laraItem->Animation.TargetState = SKIDOO_STATE_RIGHT; + else if (TrInput & VEHICLE_IN_LEFT) + { + if (skidooItem->Animation.Velocity >= 0) + laraItem->Animation.TargetState = SKIDOO_STATE_LEFT; + else + laraItem->Animation.TargetState = SKIDOO_STATE_RIGHT; + } + else if (TrInput & VEHICLE_IN_RIGHT) + { + if (skidooItem->Animation.Velocity >= 0) + laraItem->Animation.TargetState = SKIDOO_STATE_RIGHT; + else + laraItem->Animation.TargetState = SKIDOO_STATE_LEFT; + } break; case SKIDOO_STATE_LEFT: - if (!(TrInput & SKIDOO_IN_LEFT)) - laraItem->Animation.TargetState = SKIDOO_STATE_SIT; + if (skidooItem->Animation.Velocity >= 0) + { + if (!(TrInput & VEHICLE_IN_LEFT)) + laraItem->Animation.TargetState = SKIDOO_STATE_DRIVE; + } + else + { + if (!(TrInput & VEHICLE_IN_RIGHT)) + laraItem->Animation.TargetState = SKIDOO_STATE_DRIVE; + } break; case SKIDOO_STATE_RIGHT: - if (!(TrInput & SKIDOO_IN_RIGHT)) - laraItem->Animation.TargetState = SKIDOO_STATE_SIT; + if (skidooItem->Animation.Velocity >= 0) + { + if (!(TrInput & VEHICLE_IN_RIGHT)) + laraItem->Animation.TargetState = SKIDOO_STATE_DRIVE; + } + else + { + if (!(TrInput & VEHICLE_IN_LEFT)) + laraItem->Animation.TargetState = SKIDOO_STATE_DRIVE; + } break; @@ -701,10 +624,10 @@ namespace TEN::Entities::Vehicles skidoo->LeftVerticalVelocity <= 0 || skidoo->RightVerticalVelocity <= 0) { - laraItem->Animation.TargetState = SKIDOO_STATE_SIT; + laraItem->Animation.TargetState = SKIDOO_STATE_DRIVE; SoundEffect(SFX_TR2_VEHICLE_IMPACT3, &skidooItem->Pose); } - else if (skidooItem->Animation.VerticalVelocity > (DAMAGE_START + DAMAGE_LENGTH)) + else if (skidooItem->Animation.VerticalVelocity > (SKIDOO_DAMAGE_START + SKIDOO_DAMAGE_LENGTH)) laraItem->Animation.TargetState = SKIDOO_STATE_JUMP_OFF; break; @@ -712,8 +635,80 @@ namespace TEN::Entities::Vehicles } } + int GetSkidooCollisionAnim(ItemInfo* skidooItem, Vector3Int* moved) + { + moved->x = skidooItem->Pose.Position.x - moved->x; + moved->z = skidooItem->Pose.Position.z - moved->z; + + if (moved->x || moved->z) + { + float sinY = phd_sin(skidooItem->Pose.Orientation.y); + float cosY = phd_cos(skidooItem->Pose.Orientation.y); + + int front = (moved->z * cosY) + (moved->x * sinY); + int side = (moved->z * -sinY) + (moved->x * cosY); + + if (abs(front) > abs(side)) + { + if (front > 0) + return SKIDOO_ANIM_HIT_BACK; + else + return SKIDOO_ANIM_HIT_FRONT; + } + else + { + if (side > 0) + return SKIDOO_ANIM_HIT_LEFT; + else + return SKIDOO_ANIM_HIT_RIGHT; + } + } + + return 0; + } + + void SkidooGuns(ItemInfo* skidooItem, ItemInfo* laraItem) + { + auto* skidoo = GetSkidooInfo(skidooItem); + auto* lara = GetLaraInfo(laraItem); + auto* weapon = &Weapons[(int)LaraWeaponType::Snowmobile]; + + LaraGetNewTarget(laraItem, weapon); + AimWeapon(laraItem, weapon, &lara->RightArm); + + if (TrInput & VEHICLE_IN_FIRE && !skidooItem->ItemFlags[0]) + { + auto angles = Vector3Shrt( + lara->RightArm.Orientation.x, + lara->RightArm.Orientation.y + laraItem->Pose.Orientation.y, + 0 + ); + + if ((int)FireWeapon(LaraWeaponType::Pistol, lara->TargetEntity, laraItem, angles) + + (int)FireWeapon(LaraWeaponType::Pistol, lara->TargetEntity, laraItem, angles)) + { + skidoo->FlashTimer = 2; + SoundEffect(weapon->SampleNum, &laraItem->Pose); + skidooItem->ItemFlags[0] = 4; + } + } + + if (skidooItem->ItemFlags[0]) + skidooItem->ItemFlags[0]--; + } + + void DoSnowEffect(ItemInfo* skidooItem) + { + auto material = GetCollision(skidooItem).BottomBlock->Material; + if (material != FLOOR_MATERIAL::Ice && material != FLOOR_MATERIAL::Snow) + return; + + TEN::Effects::TriggerSnowmobileSnow(skidooItem); + } + int DoSkidooDynamics(int height, int verticalVelocity, int* y) { + // Grounded. if (height > *y) { *y += verticalVelocity; @@ -725,11 +720,12 @@ namespace TEN::Entities::Vehicles else verticalVelocity += GRAVITY; } + // Airborne. else { int kick = (height - *y) * 4; - if (kick < SKIDOO_MAX_KICK) - kick = SKIDOO_MAX_KICK; + if (kick < SKIDOO_KICK_MAX) + kick = SKIDOO_KICK_MAX; verticalVelocity += (kick - verticalVelocity) / 8; @@ -740,26 +736,6 @@ namespace TEN::Entities::Vehicles return verticalVelocity; } - int TestSkidooHeight(ItemInfo* skidooItem, int zOffset, int xOffset, Vector3Int* pos) - { - pos->y = skidooItem->Pose.Position.y - zOffset * phd_sin(skidooItem->Pose.Orientation.x) + xOffset * phd_sin(skidooItem->Pose.Orientation.z); - - float s = phd_sin(skidooItem->Pose.Orientation.y); - float c = phd_cos(skidooItem->Pose.Orientation.y); - - pos->x = skidooItem->Pose.Position.x + zOffset * s + xOffset * c; - pos->z = skidooItem->Pose.Position.z + zOffset * c - xOffset * s; - - auto probe = GetCollision(pos->x, pos->y, pos->z, skidooItem->RoomNumber); - if (probe.Position.Ceiling > pos->y || - probe.Position.Ceiling == NO_HEIGHT) - { - return NO_HEIGHT; - } - - return probe.Position.Floor; - } - short DoSkidooShift(ItemInfo* skidooItem, Vector3Int* pos, Vector3Int* old) { int x = pos->x / SECTOR(1); @@ -860,62 +836,50 @@ namespace TEN::Entities::Vehicles return 0; } - int SkidooDynamics(ItemInfo* laraItem, ItemInfo* skidooItem) + int SkidooDynamics(ItemInfo* skidooItem, ItemInfo* laraItem) { - auto* skidoo = (SkidooInfo*)skidooItem->Data; + auto* skidoo = GetSkidooInfo(skidooItem); Vector3Int frontLeftOld, frontRightOld, backLeftOld, backRightOld; - auto heightFrontLeftOld = TestSkidooHeight(skidooItem, SKIDOO_FRONT, -SKIDOO_SIDE, &frontLeftOld); - auto heightFrontRightOld = TestSkidooHeight(skidooItem, SKIDOO_FRONT, SKIDOO_SIDE, &frontRightOld); - auto heightBackLeftOld = TestSkidooHeight(skidooItem, -SKIDOO_FRONT, -SKIDOO_SIDE, &backLeftOld); - auto heightBackRightOld = TestSkidooHeight(skidooItem, -SKIDOO_FRONT, SKIDOO_SIDE, &backRightOld); + auto heightFrontLeftOld = GetVehicleHeight(skidooItem, SKIDOO_FRONT, -SKIDOO_SIDE, true, &frontLeftOld); + auto heightFrontRightOld = GetVehicleHeight(skidooItem, SKIDOO_FRONT, SKIDOO_SIDE, true, &frontRightOld); + auto heightBackLeftOld = GetVehicleHeight(skidooItem, -SKIDOO_FRONT, -SKIDOO_SIDE, true, &backLeftOld); + auto heightBackRightOld = GetVehicleHeight(skidooItem, -SKIDOO_FRONT, SKIDOO_SIDE, true, &backRightOld); - Vector3Int old; - old.x = skidooItem->Pose.Position.x; - old.y = skidooItem->Pose.Position.y; - old.z = skidooItem->Pose.Position.z; - - if (backLeftOld.y > heightBackLeftOld) - backLeftOld.y = heightBackLeftOld; - if (backRightOld.y > heightBackRightOld) - backRightOld.y = heightBackRightOld; - if (frontLeftOld.y > heightFrontLeftOld) - frontLeftOld.y = heightFrontLeftOld; - if (frontRightOld.y > heightFrontRightOld) - frontRightOld.y = heightFrontRightOld; + auto oldPos = skidooItem->Pose.Position; short rotation; if (skidooItem->Pose.Position.y > (skidooItem->Floor - CLICK(1))) { - if (skidoo->TurnRate < -SKIDOO_UNDO_TURN) - skidoo->TurnRate += SKIDOO_UNDO_TURN; - else if (skidoo->TurnRate > SKIDOO_UNDO_TURN) - skidoo->TurnRate -= SKIDOO_UNDO_TURN; + if (skidoo->TurnRate < -SKIDOO_TURN_RATE_DECEL) + skidoo->TurnRate += SKIDOO_TURN_RATE_DECEL; + else if (skidoo->TurnRate > SKIDOO_TURN_RATE_DECEL) + skidoo->TurnRate -= SKIDOO_TURN_RATE_DECEL; else skidoo->TurnRate = 0; skidooItem->Pose.Orientation.y += skidoo->TurnRate + skidoo->ExtraRotation; rotation = skidooItem->Pose.Orientation.y - skidoo->MomentumAngle; - if (rotation < -SKIDOO_MOMENTUM_TURN) + if (rotation < -SKIDOO_MOMENTUM_TURN_RATE_ACCEL) { - if (rotation < -SKIDOO_MAX_MOMENTUM_TURN) + if (rotation < -SKIDOO_MOMENTUM_TURN_RATE_MAX) { - rotation = -SKIDOO_MAX_MOMENTUM_TURN; + rotation = -SKIDOO_MOMENTUM_TURN_RATE_MAX; skidoo->MomentumAngle = skidooItem->Pose.Orientation.y - rotation; } else - skidoo->MomentumAngle -= SKIDOO_MOMENTUM_TURN; + skidoo->MomentumAngle -= SKIDOO_MOMENTUM_TURN_RATE_ACCEL; } - else if (rotation > SKIDOO_MOMENTUM_TURN) + else if (rotation > SKIDOO_MOMENTUM_TURN_RATE_ACCEL) { - if (rotation > SKIDOO_MAX_MOMENTUM_TURN) + if (rotation > SKIDOO_MOMENTUM_TURN_RATE_MAX) { - rotation = SKIDOO_MAX_MOMENTUM_TURN; + rotation = SKIDOO_MOMENTUM_TURN_RATE_MAX; skidoo->MomentumAngle = skidooItem->Pose.Orientation.y - rotation; } else - skidoo->MomentumAngle += SKIDOO_MOMENTUM_TURN; + skidoo->MomentumAngle += SKIDOO_MOMENTUM_TURN_RATE_ACCEL; } else skidoo->MomentumAngle = skidooItem->Pose.Orientation.y; @@ -923,59 +887,58 @@ namespace TEN::Entities::Vehicles else skidooItem->Pose.Orientation.y += skidoo->TurnRate + skidoo->ExtraRotation; - skidooItem->Pose.Position.z += skidooItem->Animation.Velocity * phd_cos(skidoo->MomentumAngle); - skidooItem->Pose.Position.x += skidooItem->Animation.Velocity * phd_sin(skidoo->MomentumAngle); + TranslateItem(skidooItem, skidoo->MomentumAngle, skidooItem->Animation.Velocity); int slip = SKIDOO_SLIP * phd_sin(skidooItem->Pose.Orientation.x); if (abs(slip) > (SKIDOO_SLIP / 2)) { - skidooItem->Pose.Position.z -= slip * phd_cos(skidooItem->Pose.Orientation.y); skidooItem->Pose.Position.x -= slip * phd_sin(skidooItem->Pose.Orientation.y); + skidooItem->Pose.Position.z -= slip * phd_cos(skidooItem->Pose.Orientation.y); } slip = SKIDOO_SLIP_SIDE * phd_sin(skidooItem->Pose.Orientation.z); if (abs(slip) > (SKIDOO_SLIP_SIDE / 2)) { - skidooItem->Pose.Position.z -= slip * phd_sin(skidooItem->Pose.Orientation.y); skidooItem->Pose.Position.x += slip * phd_cos(skidooItem->Pose.Orientation.y); + skidooItem->Pose.Position.z -= slip * phd_sin(skidooItem->Pose.Orientation.y); } Vector3Int moved; moved.x = skidooItem->Pose.Position.x; moved.z = skidooItem->Pose.Position.z; - if (!(skidooItem->Flags & ONESHOT)) + if (!(skidooItem->Flags & IFLAG_INVISIBLE)) DoVehicleCollision(skidooItem, SKIDOO_RADIUS); Vector3Int frontLeft, frontRight, backRight, backLeft; rotation = 0; - auto heightBackLeft = TestSkidooHeight(skidooItem, -SKIDOO_FRONT, -SKIDOO_SIDE, &backLeft); + auto heightBackLeft = GetVehicleHeight(skidooItem, -SKIDOO_FRONT, -SKIDOO_SIDE, false, &backLeft); if (heightBackLeft < (backLeftOld.y - CLICK(1))) rotation = DoSkidooShift(skidooItem, &backLeft, &backLeftOld); - auto heightBackRight = TestSkidooHeight(skidooItem, -SKIDOO_FRONT, SKIDOO_SIDE, &backRight); + auto heightBackRight = GetVehicleHeight(skidooItem, -SKIDOO_FRONT, SKIDOO_SIDE, false, &backRight); if (heightBackRight < (backRightOld.y - CLICK(1))) rotation += DoSkidooShift(skidooItem, &backRight, &backRightOld); - auto heightFrontLeft = TestSkidooHeight(skidooItem, SKIDOO_FRONT, -SKIDOO_SIDE, &frontLeft); + auto heightFrontLeft = GetVehicleHeight(skidooItem, SKIDOO_FRONT, -SKIDOO_SIDE, false, &frontLeft); if (heightFrontLeft < (frontLeftOld.y - CLICK(1))) rotation += DoSkidooShift(skidooItem, &frontLeft, &frontLeftOld); - auto heightFrontRight = TestSkidooHeight(skidooItem, SKIDOO_FRONT, SKIDOO_SIDE, &frontRight); + auto heightFrontRight = GetVehicleHeight(skidooItem, SKIDOO_FRONT, SKIDOO_SIDE, false, &frontRight); if (heightFrontRight < (frontRightOld.y - CLICK(1))) rotation += DoSkidooShift(skidooItem, &frontRight, &frontRightOld); auto probe = GetCollision(skidooItem); if (probe.Position.Floor < (skidooItem->Pose.Position.y - CLICK(1))) - DoSkidooShift(skidooItem, (Vector3Int*)&skidooItem->Pose, &old); + DoSkidooShift(skidooItem, (Vector3Int*)&skidooItem->Pose, &oldPos); skidoo->ExtraRotation = rotation; auto collide = GetSkidooCollisionAnim(skidooItem, &moved); if (collide) { - int newVelocity = (skidooItem->Pose.Position.z - old.z) * phd_cos(skidoo->MomentumAngle) + (skidooItem->Pose.Position.x - old.x) * phd_sin(skidoo->MomentumAngle); - if (skidooItem->Animation.Velocity > (SKIDOO_MAX_VELOCITY + SKIDOO_ACCELERATION) && + int newVelocity = (skidooItem->Pose.Position.z - oldPos.z) * phd_cos(skidoo->MomentumAngle) + (skidooItem->Pose.Position.x - oldPos.x) * phd_sin(skidoo->MomentumAngle); + if (skidooItem->Animation.Velocity > (SKIDOO_NORMAL_VELOCITY_MAX + SKIDOO_VELOCITY_ACCEL) && newVelocity < (skidooItem->Animation.Velocity - 10)) { DoDamage(laraItem, (skidooItem->Animation.Velocity - newVelocity) / 2); @@ -986,15 +949,10 @@ namespace TEN::Entities::Vehicles else if (skidooItem->Animation.Velocity < 0 && newVelocity > skidooItem->Animation.Velocity) skidooItem->Animation.Velocity = (newVelocity > 0) ? 0 : newVelocity; - if (skidooItem->Animation.Velocity < SKIDOO_MAX_BACK) - skidooItem->Animation.Velocity = SKIDOO_MAX_BACK; + if (skidooItem->Animation.Velocity < SKIDOO_REVERSE_VELOCITY_MAX) + skidooItem->Animation.Velocity = SKIDOO_REVERSE_VELOCITY_MAX; } return collide; } - - void DrawSkidoo(ItemInfo* skidooItem) - { - - } } diff --git a/TombEngine/Objects/TR2/Vehicles/skidoo.h b/TombEngine/Objects/TR2/Vehicles/skidoo.h index 44382e141..5a00fe4d8 100644 --- a/TombEngine/Objects/TR2/Vehicles/skidoo.h +++ b/TombEngine/Objects/TR2/Vehicles/skidoo.h @@ -1,29 +1,28 @@ #pragma once #include "Game/collision/collide_room.h" #include "Game/items.h" +#include "Objects/Utils/VehicleHelpers.h" namespace TEN::Entities::Vehicles { + SkidooInfo* GetSkidooInfo(ItemInfo* skidooItem); void InitialiseSkidoo(short itemNumber); - int GetSkidooMountType(ItemInfo* laraItem, ItemInfo* skidooItem, CollisionInfo* coll); + void SkidooPlayerCollision(short skidooItemNumber, ItemInfo* laraItem, CollisionInfo* coll); + void DoSkidooMount(ItemInfo* skidooItem, ItemInfo* laraItem, VehicleMountType mountType); bool TestSkidooDismountOK(ItemInfo* skidooItem, int direction); - bool TestSkidooDismount(ItemInfo* laraItem, ItemInfo* skidooItem); - - int GetSkidooCollisionAnim(ItemInfo* skidooItem, Vector3Int* moved); - void SkidooCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); - - void SkidooGuns(ItemInfo* laraItem, ItemInfo* skidooItem); - void DoSnowEffect(ItemInfo* skidooItem); + bool TestSkidooDismount(ItemInfo* skidooItem, ItemInfo* laraItem); bool SkidooControl(ItemInfo* laraItem, CollisionInfo* coll); - bool SkidooUserControl(ItemInfo* laraItem, ItemInfo* skidooItem, int height, int* pitch); - void SkidooAnimation(ItemInfo* laraItem, ItemInfo* skidooItem, int collide, bool dead); + bool SkidooUserControl(ItemInfo* skidooItem, ItemInfo* laraItem, int height, int* pitch); + void SkidooAnimation(ItemInfo* skidooItem, ItemInfo* laraItem, int collide, bool dead); - int SkidooDynamics(ItemInfo* laraItem, ItemInfo* skidooItem); - int TestSkidooHeight(ItemInfo* skidooItem, int zOffset, int xOffset, Vector3Int* pos); + int GetSkidooCollisionAnim(ItemInfo* skidooItem, Vector3Int* moved); + + void SkidooGuns(ItemInfo* skidooItem, ItemInfo* laraItem); + void DoSnowEffect(ItemInfo* skidooItem); + + int SkidooDynamics(ItemInfo* skidooItem, ItemInfo* laraItem); short DoSkidooShift(ItemInfo* skidooItem, Vector3Int* pos, Vector3Int* old); int DoSkidooDynamics(int height, int verticalVelocity, int* y); - - void DrawSkidoo(ItemInfo* skidooItem); } diff --git a/TombEngine/Objects/TR2/Vehicles/skidoo_info.h b/TombEngine/Objects/TR2/Vehicles/skidoo_info.h index b596102b8..efc5ab738 100644 --- a/TombEngine/Objects/TR2/Vehicles/skidoo_info.h +++ b/TombEngine/Objects/TR2/Vehicles/skidoo_info.h @@ -4,16 +4,16 @@ namespace TEN::Entities::Vehicles { struct SkidooInfo { - short TurnRate; - short MomentumAngle; - short ExtraRotation; + int LeftVerticalVelocity = 0; + int RightVerticalVelocity = 0; - int LeftVerticalVelocity; - int RightVerticalVelocity; + short TurnRate = 0; + short MomentumAngle = 0; + short ExtraRotation = 0; - int Pitch; - int FlashTimer; - short TrackMesh; - bool Armed; + int Pitch = 0; + int FlashTimer = 0; + short TrackMesh = 0; + bool Armed = false; }; } diff --git a/TombEngine/Objects/TR2/Vehicles/speedboat.cpp b/TombEngine/Objects/TR2/Vehicles/speedboat.cpp new file mode 100644 index 000000000..9621cf898 --- /dev/null +++ b/TombEngine/Objects/TR2/Vehicles/speedboat.cpp @@ -0,0 +1,913 @@ +#include "framework.h" +#include "Objects/TR2/Vehicles/speedboat.h" + +#include "Game/animation.h" +#include "Game/camera.h" +#include "Game/collision/collide_item.h" +#include "Game/collision/sphere.h" +#include "Game/effects/effects.h" +#include "Game/items.h" +#include "Game/Lara/lara.h" +#include "Game/Lara/lara_helpers.h" +#include "Game/effects/simple_particle.h" +#include "Objects/TR2/Vehicles/speedboat_info.h" +#include "Objects/Utils/VehicleHelpers.h" +#include "Sound/sound.h" +#include "Specific/input.h" +#include "Specific/level.h" +#include "Specific/setup.h" + +using std::vector; +using namespace TEN::Input; + +namespace TEN::Entities::Vehicles +{ + const vector SpeedboatMountTypes = + { + VehicleMountType::LevelStart, + VehicleMountType::Left, + VehicleMountType::Right, + VehicleMountType::Jump + }; + + constexpr auto SPEEDBOAT_RADIUS = 500; + constexpr auto SPEEDBOAT_FRONT = 750; + constexpr auto SPEEDBOAT_BACK = -700; + constexpr auto SPEEDBOAT_SIDE = 300; + constexpr auto SPEEDBOAT_SLIP = 10; + constexpr auto SPEEDBOAT_SLIP_SIDE = 30; + constexpr auto SPEEDBOAT_MOUNT_DISTANCE = CLICK(2.25f); + constexpr auto SPEEDBOAT_DISMOUNT_DISTANCE = SECTOR(1); + + constexpr auto SPEEDBOAT_VELOCITY_ACCEL = 5; + constexpr auto SPEEDBOAT_VELOCITY_DECEL = 1; + constexpr auto SPEEDBOAT_VELOCITY_BRAKE_DECEL = 5; + constexpr auto SPEEDBOAT_REVERSE_VELOCITY_DECEL = 5; + + constexpr auto SPEEDBOAT_VELOCITY_MIN = 20; + constexpr auto SPEEDBOAT_SLOW_VELOCITY_MAX = 37; + constexpr auto SPEEDBOAT_NORMAL_VELOCITY_MAX = 110; + constexpr auto SPEEDBOAT_FAST_VELOCITY_MAX = 185; + constexpr auto SPEEDBOAT_REVERSE_VELOCITY_MAX = 20; + + constexpr auto SPEEDBOAT_STEP_HEIGHT_MAX = CLICK(1); // Unused. + constexpr auto SPEEDBOAT_SOUND_CEILING = SECTOR(5); // Unused. + constexpr auto SPEEDBOAT_TIP = SPEEDBOAT_FRONT + 250; + + #define SPEEDBOAT_TURN_RATE_ACCEL (ANGLE(0.25f) / 2) + #define SPEEDBOAT_TURN_RATE_DECEL ANGLE(0.25f) + #define SPEEDBOAT_TURN_RATE_MAX ANGLE(4.0f) + + enum SpeedboatState + { + SPEEDBOAT_STATE_MOUNT = 0, + SPEEDBOAT_STATE_IDLE = 1, + SPEEDBOAT_STATE_MOVING = 2, + SPEEDBOAT_STATE_DISMOUNT_RIGHT = 3, + SPEEDBOAT_STATE_DISMOUNT_LEFT = 4, + SPEEDBOAT_STATE_HIT = 5, + SPEEDBOAT_STATE_FALL = 6, + SPEEDBOAT_STATE_TURN_RIGHT = 7, + SPEEDBOAT_STATE_DEATH = 8, + SPEEDBOAT_STATE_TURN_LEFT = 9 + }; + + enum SpeedboatAnim + { + SPEEDBOAT_ANIM_MOUNT_LEFT = 0, + SPEEDBOAT_ANIM_IDLE = 1, // ? + SPEEDBOAT_ANIM_FORWARD = 2, // ? + + SPEEDBOAT_ANIM_DISMOUNT_LEFT = 5, + SPEEDBOAT_ANIM_MOUNT_JUMP = 6, + SPEEDBOAT_ANIM_DISMOUNT_RIGHT = 7, + SPEEDBOAT_ANIM_MOUNT_RIGHT = 8, + + SPEEDBOAT_ANIM_HIT_LEFT = 11, + SPEEDBOAT_ANIM_HIT_RIGHT = 12, + SPEEDBOAT_ANIM_HIT_FRONT = 13, + SPEEDBOAT_ANIM_HIT_BACK = 14, + SPEEDBOAT_ANIM_LEAP_START = 15, + SPEEDBOAT_ANIM_LEAP = 16, + SPEEDBOAT_ANIM_LEAP_END = 17, + SPEEDBOAT_ANIM_DEATH = 18 + }; + + SpeedboatInfo* GetSpeedboatInfo(ItemInfo* speedboatItem) + { + return (SpeedboatInfo*)speedboatItem->Data; + } + + void InitialiseSpeedboat(short itemNumber) + { + auto* speedboatItem = &g_Level.Items[itemNumber]; + speedboatItem->Data = SpeedboatInfo(); + auto* speedboat = GetSpeedboatInfo(speedboatItem); + } + + void SpeedboatPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) + { + auto* speedboatItem = &g_Level.Items[itemNumber]; + auto* lara = GetLaraInfo(laraItem); + + if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) + return; + + auto mountType = GetVehicleMountType(speedboatItem, laraItem, coll, SpeedboatMountTypes, SPEEDBOAT_MOUNT_DISTANCE, LARA_HEIGHT); + if (mountType == VehicleMountType::None) + { + coll->Setup.EnableObjectPush = true; + ObjectCollision(itemNumber, laraItem, coll); + } + else + { + lara->Vehicle = itemNumber; + DoSpeedboatMount(speedboatItem, laraItem,mountType); + + if (g_Level.Items[itemNumber].Status != ITEM_ACTIVE) + { + AddActiveItem(itemNumber); + g_Level.Items[itemNumber].Status = ITEM_ACTIVE; + } + } + } + + void DoSpeedboatMount(ItemInfo* speedboatItem, ItemInfo* laraItem, VehicleMountType mountType) + { + auto* lara = GetLaraInfo(laraItem); + + switch (mountType) + { + case VehicleMountType::LevelStart: + laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + SPEEDBOAT_ANIM_IDLE; + laraItem->Animation.ActiveState = SPEEDBOAT_STATE_MOUNT; + laraItem->Animation.TargetState = SPEEDBOAT_STATE_MOUNT; + break; + + case VehicleMountType::Left: + laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + SPEEDBOAT_ANIM_MOUNT_LEFT; + laraItem->Animation.ActiveState = SPEEDBOAT_STATE_MOUNT; + laraItem->Animation.TargetState = SPEEDBOAT_STATE_MOUNT; + break; + + case VehicleMountType::Right: + laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + SPEEDBOAT_ANIM_MOUNT_RIGHT; + laraItem->Animation.ActiveState = SPEEDBOAT_STATE_MOUNT; + laraItem->Animation.TargetState = SPEEDBOAT_STATE_MOUNT; + break; + + default: + case VehicleMountType::Jump: + laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + SPEEDBOAT_ANIM_MOUNT_JUMP; + laraItem->Animation.ActiveState = SPEEDBOAT_STATE_MOUNT; + laraItem->Animation.TargetState = SPEEDBOAT_STATE_MOUNT; + break; + } + laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; + + if (laraItem->RoomNumber != speedboatItem->RoomNumber) + ItemNewRoom(lara->ItemNumber, speedboatItem->RoomNumber); + + laraItem->Pose.Position = speedboatItem->Pose.Position; + laraItem->Pose.Position.y -= 5; + laraItem->Pose.Orientation = Vector3Shrt(0, speedboatItem->Pose.Orientation.y, 0); + laraItem->Animation.IsAirborne = false; + laraItem->Animation.Velocity = 0; + laraItem->Animation.VerticalVelocity = 0; + lara->Control.WaterStatus = WaterStatus::Dry; + + AnimateItem(laraItem); + } + + bool TestSpeedboatDismount(ItemInfo* speedboatItem, int direction) + { + short angle; + if (direction < 0) + angle = speedboatItem->Pose.Orientation.y - ANGLE(90.0f); + else + angle = speedboatItem->Pose.Orientation.y + ANGLE(90.0f); + + int x = speedboatItem->Pose.Position.x + SPEEDBOAT_DISMOUNT_DISTANCE * phd_sin(angle); + int y = speedboatItem->Pose.Position.y; + int z = speedboatItem->Pose.Position.z + SPEEDBOAT_DISMOUNT_DISTANCE * phd_cos(angle); + auto probe = GetCollision(x, y, z, speedboatItem->RoomNumber); + + if ((probe.Position.Floor - speedboatItem->Pose.Position.y) < -CLICK(2)) + return false; + + if (probe.Position.FloorSlope || + probe.Position.Floor == NO_HEIGHT) + { + return false; + } + + if ((probe.Position.Floor - probe.Position.Ceiling) < LARA_HEIGHT || + (probe.Position.Ceiling - speedboatItem->Pose.Position.y) > -LARA_HEIGHT) + { + return false; + } + + return true; + } + + void DoSpeedboatDismount(ItemInfo* speedboatItem, ItemInfo* laraItem) + { + auto* lara = GetLaraInfo(laraItem); + + if ((laraItem->Animation.ActiveState == SPEEDBOAT_STATE_DISMOUNT_LEFT || + laraItem->Animation.ActiveState == SPEEDBOAT_STATE_DISMOUNT_RIGHT) && + TestLastFrame(laraItem, laraItem->Animation.AnimNumber)) + { + if (laraItem->Animation.ActiveState == SPEEDBOAT_STATE_DISMOUNT_LEFT) + laraItem->Pose.Orientation.y -= ANGLE(90.0f); + else if (laraItem->Animation.ActiveState == SPEEDBOAT_STATE_DISMOUNT_RIGHT) + laraItem->Pose.Orientation.y += ANGLE(90.0f); + + SetAnimation(laraItem, LA_JUMP_FORWARD); + laraItem->Animation.IsAirborne = true; + laraItem->Animation.Velocity = 40; + laraItem->Animation.VerticalVelocity = -50; + laraItem->Pose.Orientation.x = 0; + laraItem->Pose.Orientation.z = 0; + lara->Vehicle = NO_ITEM; + + int x = laraItem->Pose.Position.x + 360 * phd_sin(laraItem->Pose.Orientation.y); + int y = laraItem->Pose.Position.y - 90; + int z = laraItem->Pose.Position.z + 360 * phd_cos(laraItem->Pose.Orientation.y); + auto probe = GetCollision(x, y, z, laraItem->RoomNumber); + + if (probe.Position.Floor >= (y - CLICK(1))) + { + laraItem->Pose.Position.x = x; + laraItem->Pose.Position.z = z; + + if (probe.RoomNumber != laraItem->RoomNumber) + ItemNewRoom(lara->ItemNumber, probe.RoomNumber); + } + laraItem->Pose.Position.y = y; + + speedboatItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT].animIndex; + speedboatItem->Animation.FrameNumber = g_Level.Anims[speedboatItem->Animation.AnimNumber].frameBase; + } + } + + void SpeedboatDoBoatShift(ItemInfo* speedboatItem, int itemNumber) + { + short itemNumber2 = g_Level.Rooms[speedboatItem->RoomNumber].itemNumber; + while (itemNumber2 != NO_ITEM) + { + auto* item = &g_Level.Items[itemNumber2]; + + if (item->ObjectNumber == ID_SPEEDBOAT && itemNumber2 != itemNumber && Lara.Vehicle != itemNumber2) + { + int x = item->Pose.Position.x - speedboatItem->Pose.Position.x; + int z = item->Pose.Position.z - speedboatItem->Pose.Position.z; + + int distance = pow(x, 2) + pow(z, 2); + int radius = pow(SPEEDBOAT_RADIUS * 2, 2); + if (distance < radius) + { + speedboatItem->Pose.Position.x = item->Pose.Position.x - x * radius / distance; + speedboatItem->Pose.Position.z = item->Pose.Position.z - z * radius / distance; + } + + return; + } + + // TODO: mine and gondola + + itemNumber2 = item->NextItem; + } + } + + short SpeedboatDoShift(ItemInfo* speedboatItem, Vector3Int* pos, Vector3Int* old) + { + int x = pos->x / SECTOR(1); + int z = pos->z / SECTOR(1); + + int xOld = old->x / SECTOR(1); + int zOld = old->z / SECTOR(1); + + int shiftX = pos->x & (SECTOR(1) - 1); + int shiftZ = pos->z & (SECTOR(1) - 1); + + if (x == xOld) + { + if (z == zOld) + { + speedboatItem->Pose.Position.z += (old->z - pos->z); + speedboatItem->Pose.Position.x += (old->x - pos->x); + } + else if (z > zOld) + { + speedboatItem->Pose.Position.z -= shiftZ + 1; + return (pos->x - speedboatItem->Pose.Position.x); + } + else + { + speedboatItem->Pose.Position.z += SECTOR(1) - shiftZ; + return (speedboatItem->Pose.Position.x - pos->x); + } + } + else if (z == zOld) + { + if (x > xOld) + { + speedboatItem->Pose.Position.x -= shiftX + 1; + return (speedboatItem->Pose.Position.z - pos->z); + } + else + { + speedboatItem->Pose.Position.x += SECTOR(1) - shiftX; + return (pos->z - speedboatItem->Pose.Position.z); + } + } + else + { + x = 0; + z = 0; + + auto probe = GetCollision(old->x, pos->y, pos->z, speedboatItem->RoomNumber); + if (probe.Position.Floor < (old->y - CLICK(1))) + { + if (pos->z > old->z) + z = -shiftZ - 1; + else + z = SECTOR(1) - shiftZ; + } + + probe = GetCollision(pos->x, pos->y, old->z, speedboatItem->RoomNumber); + if (probe.Position.Floor < (old->y - CLICK(1))) + { + if (pos->x > old->x) + x = -shiftX - 1; + else + x = SECTOR(1) - shiftX; + } + + if (x && z) + { + speedboatItem->Pose.Position.z += z; + speedboatItem->Pose.Position.x += x; + } + else if (z) + { + speedboatItem->Pose.Position.z += z; + if (z > 0) + return (speedboatItem->Pose.Position.x - pos->x); + else + return (pos->x - speedboatItem->Pose.Position.x); + } + else if (x) + { + speedboatItem->Pose.Position.x += x; + if (x > 0) + return (pos->z - speedboatItem->Pose.Position.z); + else + return (speedboatItem->Pose.Position.z - pos->z); + } + else + { + speedboatItem->Pose.Position.z += (old->z - pos->z); + speedboatItem->Pose.Position.x += (old->x - pos->x); + } + } + + return 0; + } + + int GetSpeedboatHitAnim(ItemInfo* speedboatItem, Vector3Int* moved) + { + moved->x = speedboatItem->Pose.Position.x - moved->x; + moved->z = speedboatItem->Pose.Position.z - moved->z; + + if (moved->x || moved->z) + { + float sinY = phd_sin(speedboatItem->Pose.Orientation.y); + float cosY = phd_cos(speedboatItem->Pose.Orientation.y); + + int front = (moved->z * cosY) + (moved->x * sinY); + int side = (moved->z * -sinY) + (moved->x * cosY); + + if (abs(front) > abs(side)) + { + if (front > 0) + return SPEEDBOAT_ANIM_HIT_BACK; + else + return SPEEDBOAT_ANIM_HIT_FRONT; + } + else + { + if (side > 0) + return SPEEDBOAT_ANIM_HIT_LEFT; + else + return SPEEDBOAT_ANIM_HIT_RIGHT; + } + } + + return 0; + } + + int DoSpeedboatDynamics(int height, int verticalVelocity, int* y) + { + if (height > *y) + { + *y += verticalVelocity; + if (*y > height) + { + *y = height; + verticalVelocity = 0; + } + else + verticalVelocity += GRAVITY; + } + else + { + verticalVelocity += ((height - *y - verticalVelocity) / 8); + if (verticalVelocity < -SPEEDBOAT_REVERSE_VELOCITY_MAX) + verticalVelocity = -SPEEDBOAT_REVERSE_VELOCITY_MAX; + + if (*y > height) + *y = height; + } + + return verticalVelocity; + } + + int SpeedboatDynamics(short itemNumber, ItemInfo* laraItem) + { + auto* speedboatItem = &g_Level.Items[itemNumber]; + auto* speedboat = GetSpeedboatInfo(speedboatItem); + auto* lara = GetLaraInfo(laraItem); + + speedboatItem->Pose.Orientation.z -= speedboat->LeanAngle; + + Vector3Int old, frontLeftOld, frontRightOld, backLeftOld, backRightOld, frontOld; + int heightFrontLeftOld = GetVehicleWaterHeight(speedboatItem, SPEEDBOAT_FRONT, -SPEEDBOAT_SIDE, true, &frontLeftOld); + int heightFrontRightOld = GetVehicleWaterHeight(speedboatItem, SPEEDBOAT_FRONT, SPEEDBOAT_SIDE, true, &frontRightOld); + int heightBackLeftOld = GetVehicleWaterHeight(speedboatItem, -SPEEDBOAT_FRONT, -SPEEDBOAT_SIDE, true, &backLeftOld); + int heightBackRightOld = GetVehicleWaterHeight(speedboatItem, -SPEEDBOAT_FRONT, SPEEDBOAT_SIDE, true, &backRightOld); + int heightFrontOld = GetVehicleWaterHeight(speedboatItem, SPEEDBOAT_TIP, 0, true, &frontOld); + + old.x = speedboatItem->Pose.Position.x; + old.y = speedboatItem->Pose.Position.y; + old.z = speedboatItem->Pose.Position.z; + + speedboatItem->Pose.Orientation.y += speedboat->TurnRate + speedboat->ExtraRotation; + speedboat->LeanAngle = speedboat->TurnRate * 6; + + speedboatItem->Pose.Position.x += speedboatItem->Animation.Velocity * phd_sin(speedboatItem->Pose.Orientation.y); + speedboatItem->Pose.Position.z += speedboatItem->Animation.Velocity * phd_cos(speedboatItem->Pose.Orientation.y); + + int slip = SPEEDBOAT_SLIP_SIDE * phd_sin(speedboatItem->Pose.Orientation.z); + if (!slip && speedboatItem->Pose.Orientation.z) + slip = (speedboatItem->Pose.Orientation.z > 0) ? 1 : -1; + speedboatItem->Pose.Position.x += slip * phd_sin(speedboatItem->Pose.Orientation.y); + speedboatItem->Pose.Position.z -= slip * phd_cos(speedboatItem->Pose.Orientation.y); + + slip = SPEEDBOAT_SLIP * phd_sin(speedboatItem->Pose.Orientation.x); + if (!slip && speedboatItem->Pose.Orientation.x) + slip = (speedboatItem->Pose.Orientation.x > 0) ? 1 : -1; + speedboatItem->Pose.Position.x -= slip * phd_sin(speedboatItem->Pose.Orientation.y); + speedboatItem->Pose.Position.z -= slip * phd_cos(speedboatItem->Pose.Orientation.y); + + auto moved = Vector3Int(speedboatItem->Pose.Position.x, 0, speedboatItem->Pose.Position.z); + + SpeedboatDoBoatShift(speedboatItem, itemNumber); + + Vector3Int fl, fr, br, bl, f; + short rotation = 0; + auto heightBackLeft = GetVehicleWaterHeight(speedboatItem, -SPEEDBOAT_FRONT, -SPEEDBOAT_SIDE, false, &bl); + if (heightBackLeft < (backLeftOld.y - CLICK(0.5f))) + rotation = SpeedboatDoShift(speedboatItem, &bl, &backLeftOld); + + auto heightBackRight = GetVehicleWaterHeight(speedboatItem, -SPEEDBOAT_FRONT, SPEEDBOAT_SIDE, false, &br); + if (heightBackRight < (backRightOld.y - CLICK(0.5f))) + rotation += SpeedboatDoShift(speedboatItem, &br, &backRightOld); + + auto heightFrontLeft = GetVehicleWaterHeight(speedboatItem, SPEEDBOAT_FRONT, -SPEEDBOAT_SIDE, false, &fl); + if (heightFrontLeft < (frontLeftOld.y - CLICK(0.5f))) + rotation += SpeedboatDoShift(speedboatItem, &fl, &frontLeftOld); + + auto heightFrontRight = GetVehicleWaterHeight(speedboatItem, SPEEDBOAT_FRONT, SPEEDBOAT_SIDE, false, &fr); + if (heightFrontRight < (frontRightOld.y - CLICK(0.5f))) + rotation += SpeedboatDoShift(speedboatItem, &fr, &frontRightOld); + + int heightFront = 0; + if (!slip) + { + heightFront = GetVehicleWaterHeight(speedboatItem, SPEEDBOAT_TIP, 0, false, &f); + if (heightFront < (frontOld.y - CLICK(0.5f))) + SpeedboatDoShift(speedboatItem, &f, &frontOld); + } + + auto probe = GetCollision(speedboatItem); + auto height = GetWaterHeight(speedboatItem->Pose.Position.x, speedboatItem->Pose.Position.y - 5, speedboatItem->Pose.Position.z, probe.RoomNumber); + + if (height == NO_HEIGHT) + height = GetFloorHeight(probe.Block, speedboatItem->Pose.Position.x, speedboatItem->Pose.Position.y - 5, speedboatItem->Pose.Position.z); + + if (height < (speedboatItem->Pose.Position.y - CLICK(0.5f))) + SpeedboatDoShift(speedboatItem, (Vector3Int*)&speedboatItem->Pose, &old); + + speedboat->ExtraRotation = rotation; + + DoVehicleCollision(speedboatItem, SPEEDBOAT_RADIUS); + + auto collide = GetSpeedboatHitAnim(speedboatItem, &moved); + + int newVelocity = 0; + if (slip || collide) + { + newVelocity = (speedboatItem->Pose.Position.z - old.z) * phd_cos(speedboatItem->Pose.Orientation.y) + (speedboatItem->Pose.Position.x - old.x) * phd_sin(speedboatItem->Pose.Orientation.y); + + if (lara->Vehicle == itemNumber && speedboatItem->Animation.Velocity > SPEEDBOAT_NORMAL_VELOCITY_MAX + SPEEDBOAT_VELOCITY_ACCEL && newVelocity < speedboatItem->Animation.Velocity - 10) + { + DoDamage(laraItem, speedboatItem->Animation.Velocity); + SoundEffect(SFX_TR4_LARA_INJURY, &laraItem->Pose); + newVelocity /= 2; + speedboatItem->Animation.Velocity /= 2; + } + + if (slip) + { + if (speedboatItem->Animation.Velocity <= SPEEDBOAT_NORMAL_VELOCITY_MAX + 10) + speedboatItem->Animation.Velocity = newVelocity; + } + else + { + if (speedboatItem->Animation.Velocity > 0 && newVelocity < speedboatItem->Animation.Velocity) + speedboatItem->Animation.Velocity = newVelocity; + else if (speedboatItem->Animation.Velocity < 0 && newVelocity > speedboatItem->Animation.Velocity) + speedboatItem->Animation.Velocity = newVelocity; + } + + if (speedboatItem->Animation.Velocity < -SPEEDBOAT_REVERSE_VELOCITY_MAX) + speedboatItem->Animation.Velocity = -SPEEDBOAT_REVERSE_VELOCITY_MAX; + } + + return collide; + } + + bool SpeedboatUserControl(ItemInfo* speedboatItem, ItemInfo* laraItem) + { + auto* speedboat = GetSpeedboatInfo(speedboatItem); + + bool noTurn = true; + int maxVelocity; + + if (speedboatItem->Pose.Position.y >= speedboat->Water - CLICK(0.5f) && speedboat->Water != NO_HEIGHT) + { + if (!(TrInput & VEHICLE_IN_DISMOUNT) && !(TrInput & IN_LOOK) || + speedboatItem->Animation.Velocity) + { + if (TrInput & VEHICLE_IN_LEFT && !(TrInput & VEHICLE_IN_REVERSE) || + TrInput & VEHICLE_IN_RIGHT && TrInput & VEHICLE_IN_REVERSE) + { + if (speedboat->TurnRate > 0) + speedboat->TurnRate -= SPEEDBOAT_TURN_RATE_DECEL; + else + { + speedboat->TurnRate -= SPEEDBOAT_TURN_RATE_ACCEL; + if (speedboat->TurnRate < -SPEEDBOAT_TURN_RATE_MAX) + speedboat->TurnRate = -SPEEDBOAT_TURN_RATE_MAX; + } + + noTurn = false; + } + else if (TrInput & VEHICLE_IN_RIGHT && !(TrInput & VEHICLE_IN_REVERSE) || + TrInput & VEHICLE_IN_LEFT && TrInput & VEHICLE_IN_REVERSE) + { + if (speedboat->TurnRate < 0) + speedboat->TurnRate += SPEEDBOAT_TURN_RATE_DECEL; + else + { + speedboat->TurnRate += SPEEDBOAT_TURN_RATE_ACCEL; + if (speedboat->TurnRate > SPEEDBOAT_TURN_RATE_MAX) + speedboat->TurnRate = SPEEDBOAT_TURN_RATE_MAX; + } + + noTurn = false; + } + + if (TrInput & VEHICLE_IN_REVERSE) + { + if (speedboatItem->Animation.Velocity > 0) + speedboatItem->Animation.Velocity -= SPEEDBOAT_VELOCITY_BRAKE_DECEL; + else if (speedboatItem->Animation.Velocity > -SPEEDBOAT_REVERSE_VELOCITY_MAX) + speedboatItem->Animation.Velocity -= SPEEDBOAT_REVERSE_VELOCITY_DECEL; + } + else if (TrInput & VEHICLE_IN_ACCELERATE) + { + if (TrInput & VEHICLE_IN_SPEED) + maxVelocity = SPEEDBOAT_FAST_VELOCITY_MAX; + else + maxVelocity = (TrInput & VEHICLE_IN_SLOW) ? SPEEDBOAT_SLOW_VELOCITY_MAX : SPEEDBOAT_NORMAL_VELOCITY_MAX; + + if (speedboatItem->Animation.Velocity < maxVelocity) + speedboatItem->Animation.Velocity += (SPEEDBOAT_VELOCITY_ACCEL / 2) + (SPEEDBOAT_VELOCITY_ACCEL * (speedboatItem->Animation.Velocity / (maxVelocity * 2))); + else if (speedboatItem->Animation.Velocity > (maxVelocity + SPEEDBOAT_VELOCITY_DECEL)) + speedboatItem->Animation.Velocity -= SPEEDBOAT_VELOCITY_DECEL; + } + else if (TrInput & (VEHICLE_IN_LEFT | VEHICLE_IN_RIGHT) && + speedboatItem->Animation.Velocity >= 0 && + speedboatItem->Animation.Velocity < SPEEDBOAT_VELOCITY_MIN) + { + if (!(TrInput & VEHICLE_IN_DISMOUNT) && + speedboatItem->Animation.Velocity == 0) + speedboatItem->Animation.Velocity = SPEEDBOAT_VELOCITY_MIN; + } + else if (speedboatItem->Animation.Velocity > SPEEDBOAT_VELOCITY_DECEL) + speedboatItem->Animation.Velocity -= SPEEDBOAT_VELOCITY_DECEL; + else + speedboatItem->Animation.Velocity = 0; + } + else + { + if (TrInput & (VEHICLE_IN_LEFT | VEHICLE_IN_RIGHT) && + speedboatItem->Animation.Velocity >= 0 && + speedboatItem->Animation.Velocity < SPEEDBOAT_VELOCITY_MIN) + { + if (speedboatItem->Animation.Velocity == 0 && !(TrInput & VEHICLE_IN_DISMOUNT)) + speedboatItem->Animation.Velocity = SPEEDBOAT_VELOCITY_MIN; + } + else if (speedboatItem->Animation.Velocity > SPEEDBOAT_VELOCITY_DECEL) + speedboatItem->Animation.Velocity -= SPEEDBOAT_VELOCITY_DECEL; + else + speedboatItem->Animation.Velocity = 0; + + if (TrInput & IN_LOOK && speedboatItem->Animation.Velocity == 0) + LookUpDown(laraItem); + } + } + + return noTurn; + } + + void SpeedboatAnimation(ItemInfo* speedboatItem, ItemInfo* laraItem, int collide) + { + auto* speedboat = GetSpeedboatInfo(speedboatItem); + + if (laraItem->HitPoints <= 0) + { + if (laraItem->Animation.ActiveState != SPEEDBOAT_STATE_DEATH) + { + laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + SPEEDBOAT_ANIM_DEATH; + laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; + laraItem->Animation.ActiveState = laraItem->Animation.TargetState = SPEEDBOAT_STATE_DEATH; + } + } + else if (speedboatItem->Pose.Position.y < speedboat->Water - CLICK(0.5f) && speedboatItem->Animation.VerticalVelocity > 0) + { + if (laraItem->Animation.ActiveState != SPEEDBOAT_STATE_FALL) + { + laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + SPEEDBOAT_ANIM_LEAP_START; + laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; + laraItem->Animation.ActiveState = laraItem->Animation.TargetState = SPEEDBOAT_STATE_FALL; + } + } + else if (collide) + { + if (laraItem->Animation.ActiveState != SPEEDBOAT_STATE_HIT) + { + laraItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex + collide; + laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; + laraItem->Animation.ActiveState = laraItem->Animation.TargetState = SPEEDBOAT_STATE_HIT; + } + } + else + { + switch (laraItem->Animation.ActiveState) + { + case SPEEDBOAT_STATE_IDLE: + if (TrInput & VEHICLE_IN_DISMOUNT) + { + if (speedboatItem->Animation.Velocity == 0) + { + if (TrInput & VEHICLE_IN_RIGHT && TestSpeedboatDismount(speedboatItem, speedboatItem->Pose.Orientation.y + ANGLE(90.0f))) + laraItem->Animation.TargetState = SPEEDBOAT_STATE_DISMOUNT_RIGHT; + else if (TrInput & VEHICLE_IN_LEFT && TestSpeedboatDismount(speedboatItem, speedboatItem->Pose.Orientation.y - ANGLE(90.0f))) + laraItem->Animation.TargetState = SPEEDBOAT_STATE_DISMOUNT_LEFT; + } + } + + if (speedboatItem->Animation.Velocity > 0) + laraItem->Animation.TargetState = SPEEDBOAT_STATE_MOVING; + + break; + + case SPEEDBOAT_STATE_MOVING: + if (TrInput & VEHICLE_IN_DISMOUNT) + { + if (TrInput & VEHICLE_IN_RIGHT) + laraItem->Animation.TargetState = SPEEDBOAT_STATE_DISMOUNT_RIGHT; + else if (TrInput & VEHICLE_IN_RIGHT) + laraItem->Animation.TargetState = SPEEDBOAT_STATE_DISMOUNT_LEFT; + } + else if (speedboatItem->Animation.Velocity <= 0) + laraItem->Animation.TargetState = SPEEDBOAT_STATE_IDLE; + + break; + + case SPEEDBOAT_STATE_FALL: + laraItem->Animation.TargetState = SPEEDBOAT_STATE_MOVING; + break; + + //case SPEEDBOAT_TURN_RATE_ACCELR: + if (speedboatItem->Animation.Velocity <= 0) + laraItem->Animation.TargetState = SPEEDBOAT_STATE_IDLE; + else if (!(TrInput & VEHICLE_IN_RIGHT)) + laraItem->Animation.TargetState = SPEEDBOAT_STATE_MOVING; + + break; + + case SPEEDBOAT_STATE_TURN_LEFT: + if (speedboatItem->Animation.Velocity <= 0) + laraItem->Animation.TargetState = SPEEDBOAT_STATE_IDLE; + else if (!(TrInput & VEHICLE_IN_LEFT)) + laraItem->Animation.TargetState = SPEEDBOAT_STATE_MOVING; + + break; + } + } + } + + void SpeedboatSplash(ItemInfo* speedboatItem, long verticalVelocity, long water) + { + //OLD SPLASH + /* + splash_setup.x = speedboatItem->pos.x_pos; + splash_setup.y = water; + splash_setup.z = item->pos.z_pos; + splash_setup.InnerXZoff = 16 << 2; + splash_setup.InnerXZsize = 12 << 2; + splash_setup.InnerYsize = -96 << 2; + splash_setup.InnerXZvel = 0xa0; + splash_setup.InnerYvel = -fallspeed << 7; + splash_setup.InnerGravity = 0x80; + splash_setup.InnerFriction = 7; + splash_setup.MiddleXZoff = 24 << 2; + splash_setup.MiddleXZsize = 24 << 2; + splash_setup.MiddleYsize = -64 << 2; + splash_setup.MiddleXZvel = 0xe0; + splash_setup.MiddleYvel = -fallspeed << 6; + splash_setup.MiddleGravity = 0x48; + splash_setup.MiddleFriction = 8; + splash_setup.OuterXZoff = 32 << 2; + splash_setup.OuterXZsize = 32 << 2; + splash_setup.OuterXZvel = 0x110; + splash_setup.OuterFriction = 9; + SetupSplash(&splash_setup); + SplashCount = 16; + */ + } + + void SpeedboatControl(short itemNumber) + { + auto* speedboatItem = &g_Level.Items[itemNumber]; + auto* speedboat = GetSpeedboatInfo(speedboatItem); + auto* laraItem = LaraItem; + auto* lara = GetLaraInfo(laraItem); + + int collide = SpeedboatDynamics(itemNumber, laraItem); + + Vector3Int frontLeft, frontRight; + int heightFrontLeft = GetVehicleWaterHeight(speedboatItem, SPEEDBOAT_FRONT, -SPEEDBOAT_SIDE, true, &frontLeft); + int heightFrontRight = GetVehicleWaterHeight(speedboatItem, SPEEDBOAT_FRONT, SPEEDBOAT_SIDE, true, &frontRight); + + auto probe = GetCollision(speedboatItem); + + if (lara->Vehicle == itemNumber) + { + TestTriggers(speedboatItem, true); + TestTriggers(speedboatItem, false); + } + + auto water = GetWaterHeight(speedboatItem->Pose.Position.x, speedboatItem->Pose.Position.y, speedboatItem->Pose.Position.z, probe.RoomNumber); + speedboat->Water = water; + + bool noTurn = true; + bool drive = false; + bool idle = !speedboatItem->Animation.Velocity; + + if (lara->Vehicle == itemNumber && laraItem->HitPoints > 0) + { + switch (laraItem->Animation.ActiveState) + { + case SPEEDBOAT_STATE_MOUNT: + case SPEEDBOAT_STATE_DISMOUNT_RIGHT: + case SPEEDBOAT_STATE_DISMOUNT_LEFT: + break; + + default: + drive = true; + noTurn = SpeedboatUserControl(speedboatItem, laraItem); + break; + } + } + else + { + if (speedboatItem->Animation.Velocity > SPEEDBOAT_VELOCITY_DECEL) + speedboatItem->Animation.Velocity -= SPEEDBOAT_VELOCITY_DECEL; + else + speedboatItem->Animation.Velocity = 0; + } + + if (noTurn) + { + if (speedboat->TurnRate < -SPEEDBOAT_TURN_RATE_DECEL) + speedboat->TurnRate += SPEEDBOAT_TURN_RATE_DECEL; + else if (speedboat->TurnRate > SPEEDBOAT_TURN_RATE_DECEL) + speedboat->TurnRate -= SPEEDBOAT_TURN_RATE_DECEL; + else + speedboat->TurnRate = 0; + } + + speedboatItem->Floor = probe.Position.Floor - 5; + if (speedboat->Water == NO_HEIGHT) + speedboat->Water = probe.Position.Floor; + else + speedboat->Water -= 5; + + speedboat->LeftVerticalVelocity = DoSpeedboatDynamics(heightFrontLeft, speedboat->LeftVerticalVelocity, (int*)&frontLeft.y); + speedboat->RightVerticalVelocity = DoSpeedboatDynamics(heightFrontRight, speedboat->RightVerticalVelocity, (int*)&frontRight.y); + speedboatItem->Animation.VerticalVelocity = DoSpeedboatDynamics(speedboat->Water, speedboatItem->Animation.VerticalVelocity, (int*)&speedboatItem->Pose.Position.y); + + auto ofs = speedboatItem->Animation.VerticalVelocity; + if (ofs - speedboatItem->Animation.VerticalVelocity > 32 && speedboatItem->Animation.VerticalVelocity == 0 && water != NO_HEIGHT) + SpeedboatSplash(speedboatItem, ofs - speedboatItem->Animation.VerticalVelocity, water); + + probe.Position.Floor = (frontLeft.y + frontRight.y); + if (probe.Position.Floor < 0) + probe.Position.Floor = -(abs(probe.Position.Floor) / 2); + else + probe.Position.Floor /= 2; + + short xRot = phd_atan(SPEEDBOAT_FRONT, speedboatItem->Pose.Position.y - probe.Position.Floor); + short zRot = phd_atan(SPEEDBOAT_SIDE, probe.Position.Floor - frontLeft.y); + + speedboatItem->Pose.Orientation.x += ((xRot - speedboatItem->Pose.Orientation.x) / 2); + speedboatItem->Pose.Orientation.z += ((zRot - speedboatItem->Pose.Orientation.z) / 2); + + if (!xRot && abs(speedboatItem->Pose.Orientation.x) < 4) + speedboatItem->Pose.Orientation.x = 0; + if (!zRot && abs(speedboatItem->Pose.Orientation.z) < 4) + speedboatItem->Pose.Orientation.z = 0; + + if (lara->Vehicle == itemNumber) + { + SpeedboatAnimation(speedboatItem, laraItem, collide); + + if (probe.RoomNumber != speedboatItem->RoomNumber) + { + ItemNewRoom(lara->Vehicle, probe.RoomNumber); + ItemNewRoom(lara->ItemNumber, probe.RoomNumber); + } + + laraItem->Pose = speedboatItem->Pose; + speedboatItem->Pose.Orientation.z += speedboat->LeanAngle; + + AnimateItem(laraItem); + + if (laraItem->HitPoints > 0) + { + speedboatItem->Animation.AnimNumber = Objects[ID_SPEEDBOAT].animIndex + (laraItem->Animation.AnimNumber - Objects[ID_SPEEDBOAT_LARA_ANIMS].animIndex); + speedboatItem->Animation.FrameNumber = g_Level.Anims[speedboatItem->Animation.AnimNumber].frameBase + (laraItem->Animation.FrameNumber - g_Level.Anims[laraItem->Animation.AnimNumber].frameBase); + } + + Camera.targetElevation = -ANGLE(20.0f); + Camera.targetDistance = SECTOR(2); + + auto pitch = speedboatItem->Animation.Velocity; + speedboat->Pitch += (pitch - speedboat->Pitch) / 4; + + if (drive) + { + bool accelerating = idle && abs(speedboatItem->Animation.Velocity) > 4; + bool moving = (abs(speedboatItem->Animation.Velocity) > 8 || speedboat->TurnRate); + int fx = accelerating ? SFX_TR2_VEHICLE_SPEEDBOAT_ACCELERATE : (moving ? SFX_TR2_VEHICLE_SPEEDBOAT_MOVING : SFX_TR2_VEHICLE_SPEEDBOAT_IDLE); + float pitch = idle ? 1.0f : 1.0f + speedboat->Pitch / (float)SPEEDBOAT_NORMAL_VELOCITY_MAX / 4.0f; + SoundEffect(fx, &speedboatItem->Pose, SoundEnvironment::Land, pitch); + } + } + else + { + if (probe.RoomNumber != speedboatItem->RoomNumber) + ItemNewRoom(itemNumber, probe.RoomNumber); + + speedboatItem->Pose.Orientation.z += speedboat->LeanAngle; + } + + if (speedboatItem->Animation.Velocity && (water - 5) == speedboatItem->Pose.Position.y) + { + auto room = probe.Block->RoomBelow(speedboatItem->Pose.Position.x, speedboatItem->Pose.Position.z).value_or(NO_ROOM); + if (room != NO_ROOM && (TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, room) || TestEnvironment(RoomEnvFlags::ENV_FLAG_SWAMP, room))) + TEN::Effects::TriggerSpeedboatFoam(speedboatItem, Vector3(0.0f, 0.0f, SPEEDBOAT_BACK)); + } + + if (lara->Vehicle != itemNumber) + return; + + DoSpeedboatDismount(speedboatItem, laraItem); + } +} diff --git a/TombEngine/Objects/TR2/Vehicles/speedboat.h b/TombEngine/Objects/TR2/Vehicles/speedboat.h new file mode 100644 index 000000000..ec8294bd7 --- /dev/null +++ b/TombEngine/Objects/TR2/Vehicles/speedboat.h @@ -0,0 +1,26 @@ +#pragma once +#include "Objects/Utils/VehicleHelpers.h" + +struct CollisionInfo; +struct ItemInfo; + +namespace TEN::Entities::Vehicles +{ + void InitialiseSpeedboat(short itemNumber); + + void SpeedboatPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + void DoSpeedboatMount(ItemInfo* speedboatItem, ItemInfo* laraItem, VehicleMountType mountType); + bool TestSpeedboatDismount(ItemInfo* speedboatItem, int direction); + void DoSpeedboatDismount(ItemInfo* speedboatItem, ItemInfo* laraItem); + + void SpeedboatDoBoatShift(ItemInfo* speedboatItem, int itemNumber); + short SpeedboatDoShift(ItemInfo* speedboatItem, Vector3Int* pos, Vector3Int* old); + + int GetSpeedboatHitAnim(ItemInfo* speedboatItem, Vector3Int* moved); + int DoSpeedboatDynamics(int height, int verticalVelocity, int* y); + int SpeedboatDynamics(short itemNumber, ItemInfo* laraItem); + bool SpeedboatUserControl(ItemInfo* speedboatItem, ItemInfo* laraItem); + void SpeedboatAnimation(ItemInfo* speedboatItem, ItemInfo* laraItem, int collide); + void SpeedboatSplash(ItemInfo* speedboatItem, long verticalVelocity, long water); + void SpeedboatControl(short itemNumber); +} diff --git a/TombEngine/Objects/TR2/Vehicles/speedboat_info.h b/TombEngine/Objects/TR2/Vehicles/speedboat_info.h new file mode 100644 index 000000000..913377fc8 --- /dev/null +++ b/TombEngine/Objects/TR2/Vehicles/speedboat_info.h @@ -0,0 +1,17 @@ +#pragma once + +namespace TEN::Entities::Vehicles +{ + struct SpeedboatInfo + { + int LeftVerticalVelocity = 0; + int RightVerticalVelocity = 0; + + short TurnRate = 0; + short LeanAngle = 0; + short ExtraRotation = 0; + + int Pitch = 0; + int Water = 0; + }; +} diff --git a/TombEngine/Objects/TR2/tr2_objects.cpp b/TombEngine/Objects/TR2/tr2_objects.cpp index dd952faac..ee370c27c 100644 --- a/TombEngine/Objects/TR2/tr2_objects.cpp +++ b/TombEngine/Objects/TR2/tr2_objects.cpp @@ -26,7 +26,7 @@ #include "Objects/TR2/Trap/tr2_springboard.h" #include "Objects/TR2/Trap/tr2_killerstatue.h" /// vehicles -#include "Objects/TR2/Vehicles/boat.h" +#include "Objects/TR2/Vehicles/speedboat.h" #include "Objects/TR2/Vehicles/skidoo.h" /// necessary import #include "Game/control/box.h" @@ -662,9 +662,9 @@ static void StartVehicles(ObjectInfo* obj) obj = &Objects[ID_SPEEDBOAT]; if (obj->loaded) { - obj->initialise = InitialiseSpeedBoat; - obj->collision = SpeedBoatCollision; - obj->control = SpeedBoatControl; + obj->initialise = InitialiseSpeedboat; + obj->collision = SpeedboatPlayerCollision; + obj->control = SpeedboatControl; obj->saveAnim = true; obj->saveFlags = true; obj->savePosition = true; @@ -676,7 +676,7 @@ static void StartVehicles(ObjectInfo* obj) if (obj->loaded) { obj->initialise = InitialiseSkidoo; - obj->collision = SkidooCollision; + obj->collision = SkidooPlayerCollision; //obj->drawRoutine = DrawSkidoo; // TODO: create a new render for the skidoo. (with track animated) obj->saveAnim = true; obj->saveFlags = true; diff --git a/TombEngine/Objects/TR3/Entity/tr3_shiva.cpp b/TombEngine/Objects/TR3/Entity/tr3_shiva.cpp index 643cc796e..f397fad2a 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_shiva.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_shiva.cpp @@ -467,7 +467,7 @@ namespace TEN::Entities::TR3 LaraItem->Pose.Position = item->Pose.Position; LaraItem->Pose.Orientation = Vector3Shrt(0, item->Pose.Orientation.y, 0); - LaraItem->Animation.Airborne = false; + LaraItem->Animation.IsAirborne = false; LaraItem->Animation.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + LARA_ANIM_SHIVA_DEATH; LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase; diff --git a/TombEngine/Objects/TR3/Entity/tr3_sophia.cpp b/TombEngine/Objects/TR3/Entity/tr3_sophia.cpp index 9c430ab94..f28c82d3e 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_sophia.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_sophia.cpp @@ -101,7 +101,7 @@ namespace TEN::Entities::TR3 KillItem(itemNumber); DisableEntityAI(itemNumber); - item->Flags |= ONESHOT; + item->Flags |= IFLAG_INVISIBLE; } void ControlLaserBolts(short itemNumber) diff --git a/TombEngine/Objects/TR3/Entity/tr3_tony.cpp b/TombEngine/Objects/TR3/Entity/tr3_tony.cpp index 9b79a1d3d..6089fc3ac 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_tony.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_tony.cpp @@ -500,7 +500,7 @@ namespace TEN::Entities::TR3 KillItem(itemNumber); DisableEntityAI(itemNumber); - item->Flags |= ONESHOT; + item->Flags |= IFLAG_INVISIBLE; } static bool TonyIsDying() diff --git a/TombEngine/Objects/TR3/Entity/tr3_trex.cpp b/TombEngine/Objects/TR3/Entity/tr3_trex.cpp index 0aa452765..6c4fa0a14 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_trex.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_trex.cpp @@ -57,7 +57,7 @@ namespace TEN::Entities::TR3 laraItem->Pose.Position = tRexItem->Pose.Position; laraItem->Pose.Orientation = Vector3Shrt(0, tRexItem->Pose.Orientation.y, 0); - laraItem->Animation.Airborne = false; + laraItem->Animation.IsAirborne = false; laraItem->Animation.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + LARA_ANIM_TREX_DEATH; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; diff --git a/TombEngine/Objects/TR3/Trap/train.cpp b/TombEngine/Objects/TR3/Trap/train.cpp index e2e18ac86..76d0dfcea 100644 --- a/TombEngine/Objects/TR3/Trap/train.cpp +++ b/TombEngine/Objects/TR3/Trap/train.cpp @@ -114,7 +114,7 @@ void TrainCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) laraItem->Pose.Orientation.y = trainItem->Pose.Orientation.y; laraItem->Animation.Velocity = 0; laraItem->Animation.VerticalVelocity = 0; - laraItem->Animation.Airborne = false; + laraItem->Animation.IsAirborne = false; DoDamage(laraItem, INT_MAX); diff --git a/TombEngine/Objects/TR3/Vehicles/big_gun.cpp b/TombEngine/Objects/TR3/Vehicles/big_gun.cpp index 3d49367f9..9150f4681 100644 --- a/TombEngine/Objects/TR3/Vehicles/big_gun.cpp +++ b/TombEngine/Objects/TR3/Vehicles/big_gun.cpp @@ -13,36 +13,41 @@ #include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_struct.h" #include "Objects/TR3/Vehicles/big_gun_info.h" +#include "Objects/Utils/VehicleHelpers.h" #include "Sound/sound.h" #include "Specific/level.h" #include "Specific/input.h" #include "Specific/setup.h" +using std::vector; using namespace TEN::Input; namespace TEN::Entities::Vehicles { - #define RECOIL_TIME 26 - #define RECOIL_Z 25 + const vector BigGunMountTypes = + { + VehicleMountType::LevelStart, + VehicleMountType::Back + }; - #define BGUN_NUM_UP_DOWN_FRAMES 59 - #define BGUN_DISMOUNT_FRAME 30 + constexpr auto BGUN_MOUNT_DISTANCE = CLICK(2); - #define BGUN_TURN_RATE ANGLE(2.0f) - #define BGUN_TURN_MAX ANGLE(16.0f) + constexpr auto BGUN_RECOIL_TIME = 26; + constexpr auto BGUN_RECOIL_Z = 25; - #define BGUN_IN_FIRE IN_ACTION - #define BGUN_IN_DISMOUNT (IN_ROLL | IN_JUMP) - #define BGUN_IN_UP IN_FORWARD - #define BGUN_IN_DOWN IN_BACK - #define BGUN_IN_LEFT IN_LEFT - #define BGUN_IN_RIGHT IN_RIGHT + constexpr auto BGUN_X_ORIENT_NUM_FRAMES = 59; + constexpr auto BGUN_X_ORIENT_MIDDLE_FRAME = 30; + + #define BGUN_TURN_RATE_ACCEL ANGLE(0.5f) + #define BGUN_TURN_RATE_MAX ANGLE(4.0f) + #define BGUN_X_ORIENT_STEP (ANGLE(80.0f) / BGUN_X_ORIENT_NUM_FRAMES) + #define BGUN_X_ORIENT_MAX ANGLE(40.0f) enum BigGunState { BGUN_STATE_MOUNT = 0, BGUN_STATE_DISMOUNT = 1, - BGUN_STATE_UP_DOWN = 2, + BGUN_STATE_ROTATE_VERTICALLY = 2, BGUN_STATE_RECOIL = 3 }; @@ -50,41 +55,41 @@ namespace TEN::Entities::Vehicles { BGUN_ANIM_MOUNT = 0, BGUN_ANIM_DISMOUNT = 1, - BGUN_ANIM_UP_DOWN = 2, + BGUN_ANIM_ROTATE_VERTICALLY = 2, BGUN_ANIM_RECOIL = 3 }; enum BigGunFlags { - BGUN_FLAG_UP_DOWN = 1, - BGUN_FLAG_AUTO_ROT = 2, - BGUN_FLAG_DISMOUNT = 4, - BGUN_FLAG_FIRE = 8 + BGUN_FLAG_UP_DOWN = (1 << 0), + BGUN_FLAG_AUTO_ROT = (1 << 2), + BGUN_FLAG_DISMOUNT = (1 << 3), + BGUN_FLAG_FIRE = (1 << 4) }; + BigGunInfo* GetBigGunInfo(ItemInfo* bigGunItem) + { + return (BigGunInfo*)bigGunItem->Data; + } + void BigGunInitialise(short itemNumber) { auto* bigGunItem = &g_Level.Items[itemNumber]; bigGunItem->Data = BigGunInfo(); - auto* bigGun = (BigGunInfo*)bigGunItem->Data; + auto* bigGun = GetBigGunInfo(bigGunItem); - bigGun->Rotation.x = BGUN_DISMOUNT_FRAME; - bigGun->Rotation.z = 0; - bigGun->StartYRot = bigGunItem->Pose.Orientation.y; - bigGun->GunRotYAdd = 0; - bigGun->FireCount = 0; - bigGun->Flags = 0; - bigGun->BarrelRotating = false; + bigGun->BaseOrientation = bigGunItem->Pose.Orientation; + bigGun->XOrientFrame = BGUN_X_ORIENT_MIDDLE_FRAME; } - static bool BigGunTestMount(ItemInfo* laraItem, ItemInfo* bigGunItem) + static bool BigGunTestMount(ItemInfo* bigGunItem, ItemInfo* laraItem) { // TODO: If Lara global is not used, the game crashes upon level load. Not sure why. @Sezz 2022.01.09 auto* lara = &Lara/* GetLaraInfo(laraItem)*/; if (!(TrInput & IN_ACTION) || lara->Control.HandStatus != HandStatus::Free || - laraItem->Animation.Airborne) + laraItem->Animation.IsAirborne) { return false; } @@ -94,7 +99,7 @@ namespace TEN::Entities::Vehicles int z = laraItem->Pose.Position.z - bigGunItem->Pose.Position.z; int distance = pow(x, 2) + pow(y, 2) + pow(z, 2); - if (distance > 30000) + if (distance > SECTOR(30)) return false; short deltaAngle = abs(laraItem->Pose.Orientation.y - bigGunItem->Pose.Orientation.y); @@ -104,9 +109,9 @@ namespace TEN::Entities::Vehicles return true; } - void BigGunFire(ItemInfo* laraItem, ItemInfo* bigGunItem) + void BigGunFire(ItemInfo* bigGunItem, ItemInfo* laraItem) { - auto* bigGun = (BigGunInfo*)bigGunItem->Data; + auto* bigGun = GetBigGunInfo(bigGunItem); short itemNumber = CreateItem(); auto* projectileItem = &g_Level.Items[itemNumber]; @@ -123,10 +128,12 @@ namespace TEN::Entities::Vehicles InitialiseItem(itemNumber); - projectileItem->Pose.Orientation.x = -((bigGun->Rotation.x - 32) * ANGLE(1.0f)); - projectileItem->Pose.Orientation.y = bigGunItem->Pose.Orientation.y; - projectileItem->Pose.Orientation.z = 0; projectileItem->Animation.Velocity = 16; + projectileItem->Pose.Orientation = Vector3Shrt( + -((bigGun->XOrientFrame - 32) * ANGLE(1.0f)), + bigGunItem->Pose.Orientation.y, + 0 + ); projectileItem->ItemFlags[0] = BGUN_FLAG_UP_DOWN; AddActiveItem(itemNumber); @@ -141,121 +148,141 @@ namespace TEN::Entities::Vehicles } } - void BigGunCollision(short itemNum, ItemInfo* laraItem, CollisionInfo* coll) + void BigGunCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) { + auto* bigGunItem = &g_Level.Items[itemNumber]; + auto* bigGun = GetBigGunInfo(bigGunItem); auto* lara = GetLaraInfo(laraItem); - auto* bigGunItem = &g_Level.Items[itemNum]; - auto* bigGun = (BigGunInfo*)bigGunItem->Data; if (laraItem->HitPoints <= 0 || lara->Vehicle != NO_ITEM) return; - if (BigGunTestMount(bigGunItem, laraItem)) + if (BigGunTestMount(laraItem, bigGunItem)) { - lara->Vehicle = itemNum; - - if (lara->Control.Weapon.GunType == LaraWeaponType::Flare) - { - CreateFlare(laraItem, ID_FLARE_ITEM, false); - UndrawFlareMeshes(laraItem); - - lara->Flare.ControlLeft = false; - lara->Control.Weapon.RequestGunType = LaraWeaponType::None; - lara->Control.Weapon.GunType = LaraWeaponType::None; - } + lara->Vehicle = itemNumber; + DoVehicleFlareDiscard(laraItem); laraItem->Animation.AnimNumber = Objects[ID_BIGGUN_ANIMS].animIndex + BGUN_ANIM_MOUNT; laraItem->Animation.FrameNumber = g_Level.Anims[Objects[ID_BIGGUN_ANIMS].animIndex + BGUN_ANIM_MOUNT].frameBase; - laraItem->Animation.TargetState = BGUN_STATE_MOUNT; laraItem->Animation.ActiveState = BGUN_STATE_MOUNT; + laraItem->Animation.TargetState = BGUN_STATE_MOUNT; + laraItem->Animation.IsAirborne = false; laraItem->Pose = bigGunItem->Pose; - laraItem->Animation.Airborne = false; lara->Control.HandStatus = HandStatus::Busy; bigGunItem->HitPoints = 1; + bigGun->XOrientFrame = BGUN_X_ORIENT_MIDDLE_FRAME; bigGun->Flags = 0; - bigGun->Rotation.x = BGUN_DISMOUNT_FRAME; - } else - ObjectCollision(itemNum, laraItem, coll); + ObjectCollision(itemNumber, laraItem, coll); } bool BigGunControl(ItemInfo* laraItem, CollisionInfo* coll) { auto* lara = GetLaraInfo(laraItem); auto* bigGunItem = &g_Level.Items[lara->Vehicle]; - auto* bigGun = (BigGunInfo*)bigGunItem->Data; + auto* bigGun = GetBigGunInfo(bigGunItem); if (bigGun->Flags & BGUN_FLAG_UP_DOWN) { - if (bigGun->BarrelRotating) - bigGun->BarrelZRotation--; + if (bigGun->IsBarrelRotating) + bigGun->BarrelRotation--; - if (!bigGun->BarrelZRotation) - bigGun->BarrelRotating = false; + if (!bigGun->BarrelRotation) + bigGun->IsBarrelRotating = false; - if (TrInput & BGUN_IN_DISMOUNT || laraItem->HitPoints <= 0) + if (TrInput & VEHICLE_IN_DISMOUNT || laraItem->HitPoints <= 0) bigGun->Flags = BGUN_FLAG_AUTO_ROT; else { - if (TrInput & BGUN_IN_FIRE && bigGun->FireCount == 0) + if (TrInput & VEHICLE_IN_FIRE && !bigGun->FireCount) { - BigGunFire(laraItem, bigGunItem); - bigGun->FireCount = RECOIL_TIME; - bigGun->BarrelZRotation = RECOIL_Z; - bigGun->BarrelRotating = true; + BigGunFire(bigGunItem, laraItem); + bigGun->FireCount = BGUN_RECOIL_TIME; + bigGun->BarrelRotation = BGUN_RECOIL_Z; + bigGun->IsBarrelRotating = true; } - if (TrInput & BGUN_IN_LEFT) + if (TrInput & VEHICLE_IN_UP) { - if (bigGun->GunRotYAdd > 0) - bigGun->GunRotYAdd /= 2; + if (bigGun->TurnRate.x < 0) + bigGun->TurnRate.x /= 2; - bigGun->GunRotYAdd -= BGUN_TURN_RATE; - if (bigGun->GunRotYAdd < -BGUN_TURN_MAX) - bigGun->GunRotYAdd = -BGUN_TURN_MAX; + bigGun->TurnRate.x += BGUN_TURN_RATE_ACCEL; + if (bigGun->TurnRate.x > (BGUN_TURN_RATE_MAX / 2)) + bigGun->TurnRate.x = (BGUN_TURN_RATE_MAX / 2); } - else if (TrInput & BGUN_IN_RIGHT) + else if (TrInput & VEHICLE_IN_DOWN) { - if (bigGun->GunRotYAdd < 0) - bigGun->GunRotYAdd /= 2; + if (bigGun->TurnRate.x > 0) + bigGun->TurnRate.x /= 2; - bigGun->GunRotYAdd += BGUN_TURN_RATE; - if (bigGun->GunRotYAdd > BGUN_TURN_MAX) - bigGun->GunRotYAdd = BGUN_TURN_MAX; + bigGun->TurnRate.x -= BGUN_TURN_RATE_ACCEL; + if (bigGun->TurnRate.x < (-BGUN_TURN_RATE_MAX / 2)) + bigGun->TurnRate.x = (-BGUN_TURN_RATE_MAX / 2); } else { - bigGun->GunRotYAdd -= bigGun->GunRotYAdd / 4; - if (abs(bigGun->GunRotYAdd) < BGUN_TURN_RATE) - bigGun->GunRotYAdd = 0; + bigGun->TurnRate.x -= bigGun->TurnRate.x / 3; + if (abs(bigGun->TurnRate.x) < BGUN_TURN_RATE_ACCEL) + bigGun->TurnRate.x = 0; } - bigGun->Rotation.z += bigGun->GunRotYAdd / 4; + if (TrInput & VEHICLE_IN_LEFT) + { + if (bigGun->TurnRate.y > 0) + bigGun->TurnRate.y /= 2; - if (TrInput & BGUN_IN_UP && bigGun->Rotation.x < BGUN_NUM_UP_DOWN_FRAMES) - bigGun->Rotation.x++; - else if (TrInput & BGUN_IN_DOWN && bigGun->Rotation.x) - bigGun->Rotation.x--; + bigGun->TurnRate.y -= BGUN_TURN_RATE_ACCEL; + if (bigGun->TurnRate.y < -BGUN_TURN_RATE_MAX) + bigGun->TurnRate.y = -BGUN_TURN_RATE_MAX; + } + else if (TrInput & VEHICLE_IN_RIGHT) + { + if (bigGun->TurnRate.y < 0) + bigGun->TurnRate.y /= 2; + + bigGun->TurnRate.y += BGUN_TURN_RATE_ACCEL; + if (bigGun->TurnRate.y > BGUN_TURN_RATE_MAX) + bigGun->TurnRate.y = BGUN_TURN_RATE_MAX; + } + else + { + bigGun->TurnRate.y -= bigGun->TurnRate.y / 3; + if (abs(bigGun->TurnRate.y) < BGUN_TURN_RATE_ACCEL) + bigGun->TurnRate.y = 0; + } + + bigGun->Rotation.x += bigGun->TurnRate.x; + bigGun->Rotation.y += bigGun->TurnRate.y; + + if (bigGun->Rotation.x > BGUN_X_ORIENT_MAX) + bigGun->Rotation.x = BGUN_X_ORIENT_MAX; + else if (bigGun->Rotation.x < -BGUN_X_ORIENT_MAX) + bigGun->Rotation.x = -BGUN_X_ORIENT_MAX; + + bigGun->XOrientFrame = (int)round((bigGun->Rotation.x + BGUN_X_ORIENT_MAX) / BGUN_X_ORIENT_STEP); } } if (bigGun->Flags & BGUN_FLAG_AUTO_ROT) { - if (bigGun->Rotation.x == BGUN_DISMOUNT_FRAME) + if (bigGun->XOrientFrame == BGUN_X_ORIENT_MIDDLE_FRAME) { laraItem->Animation.AnimNumber = Objects[ID_BIGGUN_ANIMS].animIndex + BGUN_ANIM_DISMOUNT; laraItem->Animation.FrameNumber = g_Level.Anims[Objects[ID_BIGGUN].animIndex + BGUN_ANIM_DISMOUNT].frameBase; laraItem->Animation.ActiveState = BGUN_STATE_DISMOUNT; laraItem->Animation.TargetState = BGUN_STATE_DISMOUNT; - bigGun->GunRotYAdd = 0; - bigGun->BarrelRotating = false; + bigGun->TurnRate.y = 0; + bigGun->IsBarrelRotating = false; bigGun->Flags = BGUN_FLAG_DISMOUNT; } - else if (bigGun->Rotation.x > BGUN_DISMOUNT_FRAME) - bigGun->Rotation.x--; - else if (bigGun->Rotation.x < BGUN_DISMOUNT_FRAME) - bigGun->Rotation.x++; + else if (bigGun->Rotation.x > 0) + bigGun->Rotation.x -= BGUN_X_ORIENT_STEP; + else if (bigGun->Rotation.x < 0) + bigGun->Rotation.x += BGUN_X_ORIENT_STEP; + + bigGun->XOrientFrame = (int)round((bigGun->Rotation.x + BGUN_X_ORIENT_MAX) / BGUN_X_ORIENT_STEP); } switch (laraItem->Animation.ActiveState) @@ -276,27 +303,28 @@ namespace TEN::Entities::Vehicles break; - case BGUN_STATE_UP_DOWN: - laraItem->Animation.AnimNumber = Objects[ID_BIGGUN_ANIMS].animIndex + BGUN_ANIM_UP_DOWN; - laraItem->Animation.FrameNumber = g_Level.Anims[Objects[ID_BIGGUN].animIndex + BGUN_ANIM_UP_DOWN].frameBase + bigGun->Rotation.x; + case BGUN_STATE_ROTATE_VERTICALLY: bigGunItem->Animation.AnimNumber = Objects[ID_BIGGUN].animIndex + (laraItem->Animation.AnimNumber - Objects[ID_BIGGUN_ANIMS].animIndex); bigGunItem->Animation.FrameNumber = g_Level.Anims[bigGunItem->Animation.AnimNumber].frameBase + (laraItem->Animation.FrameNumber - g_Level.Anims[laraItem->Animation.AnimNumber].frameBase); - + if (bigGun->FireCount > 0) bigGun->FireCount--; else bigGun->FireCount = 0; bigGun->Flags = BGUN_FLAG_UP_DOWN; + + laraItem->Animation.AnimNumber = Objects[ID_BIGGUN_ANIMS].animIndex + BGUN_ANIM_ROTATE_VERTICALLY; + laraItem->Animation.FrameNumber = g_Level.Anims[Objects[ID_BIGGUN].animIndex + BGUN_ANIM_ROTATE_VERTICALLY].frameBase + bigGun->XOrientFrame; break; } - Camera.targetElevation = -ANGLE(15.0f); + Camera.targetElevation = -(bigGun->Rotation.x + ANGLE(15.0f)); - bigGunItem->Pose.Orientation.y = bigGun->StartYRot + bigGun->Rotation.z; + bigGunItem->Pose.Orientation.y = bigGun->BaseOrientation.y + bigGun->Rotation.y; laraItem->Pose.Orientation.y = bigGunItem->Pose.Orientation.y; - coll->Setup.EnableSpasm = false; coll->Setup.EnableObjectPush = false; + coll->Setup.EnableSpasm = false; DoObjectCollision(laraItem, coll); diff --git a/TombEngine/Objects/TR3/Vehicles/big_gun.h b/TombEngine/Objects/TR3/Vehicles/big_gun.h index b4641a63b..a8edcf892 100644 --- a/TombEngine/Objects/TR3/Vehicles/big_gun.h +++ b/TombEngine/Objects/TR3/Vehicles/big_gun.h @@ -1,4 +1,5 @@ #pragma once +#include "Objects/Utils/VehicleHelpers.h" struct CollisionInfo; struct ItemInfo; @@ -6,8 +7,8 @@ struct ItemInfo; namespace TEN::Entities::Vehicles { void BigGunInitialise(short itemNumber); - static bool BigGunTestMount(ItemInfo* laraItem, ItemInfo* bigGunItem); - void BigGunFire(ItemInfo* laraItem, ItemInfo* bigGunItem); + static bool BigGunTestMount(ItemInfo* bigGunItem, ItemInfo* laraItem); + void BigGunFire(ItemInfo* bigGunItem, ItemInfo* laraItem); void BigGunCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); bool BigGunControl(ItemInfo* laraItem, CollisionInfo* coll); } diff --git a/TombEngine/Objects/TR3/Vehicles/big_gun_info.h b/TombEngine/Objects/TR3/Vehicles/big_gun_info.h index e9f1624ed..b5f5b1ad6 100644 --- a/TombEngine/Objects/TR3/Vehicles/big_gun_info.h +++ b/TombEngine/Objects/TR3/Vehicles/big_gun_info.h @@ -4,14 +4,15 @@ namespace TEN::Entities::Vehicles { struct BigGunInfo { - Vector3Shrt Rotation; - short BarrelZRotation; - short StartYRot; - long GunRotYAdd; + Vector3Shrt BaseOrientation = Vector3Shrt(); + Vector3Shrt TurnRate = Vector3Shrt(); + Vector3Shrt Rotation = Vector3Shrt(); + short BarrelRotation = 0; + int XOrientFrame = 0; - unsigned int FireCount; - bool BarrelRotating; + unsigned int FireCount = 0; + bool IsBarrelRotating = false; - char Flags; + char Flags = NULL; }; } diff --git a/TombEngine/Objects/TR3/Vehicles/kayak.cpp b/TombEngine/Objects/TR3/Vehicles/kayak.cpp index d75ff9e43..af2721a83 100644 --- a/TombEngine/Objects/TR3/Vehicles/kayak.cpp +++ b/TombEngine/Objects/TR3/Vehicles/kayak.cpp @@ -12,60 +12,85 @@ #include "Game/Lara/lara_flare.h" #include "Game/Lara/lara_helpers.h" #include "Objects/TR3/Vehicles/kayak_info.h" +#include "Objects/Utils/VehicleHelpers.h" #include "Specific/level.h" #include "Specific/input.h" #include "Specific/setup.h" +using std::vector; using namespace TEN::Input; namespace TEN::Entities::Vehicles { - #define KAYAK_COLLIDE CLICK(0.25f) - #define DISMOUNT_DISTANCE CLICK(3) // TODO: Find accurate distance. - #define KAYAK_TO_ENTITY_RADIUS CLICK(1) + struct WAKE_PTS + { + int x[2]; + int y; + int z[2]; + short xvel[2]; + short zvel[2]; + byte life; + byte pad[3]; + }; + static vector KayakLaraLegJoints = { LM_HIPS, LM_LTHIGH, LM_LSHIN, LM_LFOOT, LM_RTHIGH, LM_RSHIN, LM_RFOOT }; + static vector KayakMountTypes = + { + VehicleMountType::LevelStart, + VehicleMountType::Left, + VehicleMountType::Right + }; - #define MAX_VELOCITY 0x380000 - #define KAYAK_FRICTION 0x8000 - #define KAYAK_ROTATE_FRICTION 0x50000 - #define KAYAK_DEFLECT_ROTATION 0x80000 - #define KAYAK_FORWARD_VELOCITY 0x180000 - #define KAYAK_FORWARD_ROTATION 0x800000 - #define KAYAK_LEFT_RIGHT_VELOCITY 0x100000 - #define KAYAK_LEFT_RIGHT_ROTATION 0xc00000 - #define KAYAK_MAX_LEFT_RIGHT 0xc00000 - #define KAYAK_TURN_ROTATION 0x200000 - #define KAYAK_MAX_TURN 0x1000000 - #define KAYAK_TURN_BRAKE 0x8000 - #define KAYAK_HARD_ROTATION 0x1000000 - #define KAYAK_MAX_STAT 0x1000000 + constexpr auto KAYAK_TO_ENTITY_RADIUS = CLICK(1); + constexpr auto KAYAK_COLLIDE = CLICK(0.25f); + constexpr auto KAYAK_MOUNT_DISTANCE = CLICK(1.5f); + constexpr auto KAYAK_DISMOUNT_DISTANCE = CLICK(3); // TODO: Find accurate distance. - #define HIT_BACK 1 - #define HIT_FRONT 2 - #define HIT_LEFT 3 - #define HIT_RIGHT 4 + constexpr int KAYAK_VELOCITY_FORWARD_ACCEL = 24 * VEHICLE_VELOCITY_SCALE; + constexpr int KAYAK_VELOCITY_LR_ACCEL = 16 * VEHICLE_VELOCITY_SCALE; + constexpr int KAYAK_VELOCITY_HOLD_TURN_DECEL = 0.5f * VEHICLE_VELOCITY_SCALE; + constexpr int KAYAK_VELOCITY_FRICTION_DECEL = 0.5f * VEHICLE_VELOCITY_SCALE; + + constexpr int KAYAK_VELOCITY_MAX = 56 * VEHICLE_VELOCITY_SCALE; + + // TODO: Very confusing. + #define KAYAK_TURN_RATE_FRICTION_DECEL ANGLE(0.03f) + #define KAYAK_TURN_RATE_DEFLECT ANGLE(0.05f) + #define KAYAK_TURN_RATE_FORWARD_ACCEL ANGLE(0.7f) + #define KAYAK_TURN_RATE_LR_ACCEL ANGLE(1.0f) + #define KAYAK_TURN_RATE_LR_MAX ANGLE(1.0f) + #define KAYAK_TURN_ROTATION ANGLE(0.18f) + #define KAYAK_TURN_RATE_MAX ANGLE(1.4f) + #define KAYAK_TURN_RATE_HOLD_ACCEL ANGLE(1.4f) + #define KAYAK_TURN_RATE_HOLD_MAX ANGLE(1.4f) + + constexpr auto HIT_BACK = 1; + constexpr auto HIT_FRONT = 2; + constexpr auto HIT_LEFT = 3; + constexpr auto HIT_RIGHT = 4; #define KAYAK_MOUNT_LEFT_FRAME GetFrameNumber(KAYAK_ANIM_MOUNT_RIGHT, 0) #define KAYAK_IDLE_FRAME GetFrameNumber(KAYAK_ANIM_IDLE, 0) #define KAYAK_MOUNT_RIGHT_FRAME GetFrameNumber(KAYAK_ANIM_MOUNT_LEFT, 0) - #define KAYAK_DRAW_SHIFT 32 - #define LARA_LEG_BITS ((1 << LM_HIPS) | (1 << LM_LTHIGH) | (1 << LM_LSHIN) | (1 << LM_LFOOT) | (1 << LM_RTHIGH) | (1 << LM_RSHIN) | (1 << LM_RFOOT)) - #define NUM_WAKE_SPRITES 32 - #define WAKE_SIZE 32 - #define WAKE_VELOCITY 4 - #define KAYAK_X 128 - #define KAYAK_Z 128 - #define KAYAK_MAX_KICK -80 - #define KAYAK_MIN_BOUNCE ((MAX_VELOCITY / 2) / 256) + constexpr auto KAYAK_DRAW_SHIFT = 32; + constexpr auto NUM_WAKE_SPRITES = 32; + constexpr auto WAKE_SIZE = 32; + constexpr auto WAKE_VELOCITY = 4; + constexpr auto KAYAK_X = 128; + constexpr auto KAYAK_Z = 128; + constexpr auto KAYAK_MAX_KICK = -80; + constexpr auto KAYAK_MIN_BOUNCE = (KAYAK_VELOCITY_MAX / 2) / VEHICLE_VELOCITY_SCALE; - #define KAYAK_IN_FORWARD IN_FORWARD - #define KAYAK_IN_BACK IN_BACK - #define KAYAK_IN_LEFT IN_LEFT - #define KAYAK_IN_RIGHT IN_RIGHT - #define KAYAK_IN_HOLD IN_WALK - #define KAYAK_IN_HOLD_LEFT IN_LSTEP - #define KAYAK_IN_HOLD_RIGHT IN_RSTEP - #define KAYAK_IN_DISMOUNT (IN_JUMP | IN_ROLL) + // TODO: Kayak control is fairly unique. Keep this? @Sezz 2022.06.25 + constexpr auto KAYAK_IN_FORWARD = IN_FORWARD; + constexpr auto KAYAK_IN_BACK = IN_BACK; + constexpr auto KAYAK_IN_LEFT = IN_LEFT; + constexpr auto KAYAK_IN_RIGHT = IN_RIGHT; + constexpr auto KAYAK_IN_HOLD = IN_WALK; + constexpr auto KAYAK_IN_HOLD_LEFT = IN_LSTEP; + constexpr auto KAYAK_IN_HOLD_RIGHT = IN_RSTEP; + + WAKE_PTS WakePts[NUM_WAKE_SPRITES][2]; enum KayakState { @@ -76,8 +101,8 @@ namespace TEN::Entities::Vehicles KAYAK_STATE_MOUNT_LEFT = 4, KAYAK_STATE_IDLE_DEATH = 5, KAYAK_STATE_FORWARD = 6, - KAYAK_STATE_CAPSIZE_RECOVER = 7, // Unused. - KAYAK_STATE_CAPSIZE_DEATH = 8, // Unused. + KAYAK_STATE_CAPSIZE_RECOVER = 7, // Unused. + KAYAK_STATE_CAPSIZE_DEATH = 8, // Unused. KAYAK_STATE_DISMOUNT = 9, KAYAK_STATE_HOLD_LEFT = 10, KAYAK_STATE_HOLD_RIGHT = 11, @@ -123,42 +148,18 @@ namespace TEN::Entities::Vehicles KAYAK_ANIM_DISMOUNT_RIGHT = 32 }; - enum class KayakMountType + KayakInfo* GetKayakInfo(ItemInfo* kayakItem) { - None, - Left, - Right - }; - - struct WAKE_PTS - { - int x[2]; - int y; - int z[2]; - short xvel[2]; - short zvel[2]; - byte life; - byte pad[3]; - }; - - WAKE_PTS WakePts[NUM_WAKE_SPRITES][2]; + return (KayakInfo*)kayakItem->Data; + } void InitialiseKayak(short itemNumber) { auto* kayakItem = &g_Level.Items[itemNumber]; kayakItem->Data = KayakInfo(); - auto* kayak = (KayakInfo*)kayakItem->Data; + auto* kayak = GetKayakInfo(kayakItem); - kayak->TurnRate = 0; - kayak->Velocity = 0; - kayak->FrontVerticalVelocity = 0; - kayak->LeftVerticalVelocity = 0; - kayak->RightVerticalVelocity = 0; - kayak->LeftRightCount = 0; - kayak->OldPos = kayakItem->Pose; - kayak->CurrentStartWake = 0; - kayak->WakeShade = 0; - kayak->Flags = 0; + kayak->OldPose = kayakItem->Pose; for (int i = 0; i < NUM_WAKE_SPRITES; i++) { @@ -167,6 +168,72 @@ namespace TEN::Entities::Vehicles } } + void KayakPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) + { + auto* kayakItem = &g_Level.Items[itemNumber]; + auto* kayak = GetKayakInfo(kayakItem); + auto* lara = GetLaraInfo(laraItem); + + if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) + return; + + auto mountType = GetVehicleMountType(kayakItem, laraItem, coll, KayakMountTypes, KAYAK_MOUNT_DISTANCE, LARA_HEIGHT); + if (mountType == VehicleMountType::None) + { + coll->Setup.EnableObjectPush = true; + ObjectCollision(itemNumber, laraItem, coll); + } + else + { + lara->Vehicle = itemNumber; + DoKayakMount(kayakItem, laraItem, mountType); + } + } + + void DoKayakMount(ItemInfo* kayakItem, ItemInfo* laraItem, VehicleMountType mountType) + { + auto* kayak = GetKayakInfo(kayakItem); + auto* lara = GetLaraInfo(laraItem); + + switch (mountType) + { + case VehicleMountType::LevelStart: + laraItem->Animation.AnimNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_IDLE; + laraItem->Animation.ActiveState = KAYAK_STATE_IDLE; + laraItem->Animation.TargetState = KAYAK_STATE_IDLE; + break; + + case VehicleMountType::Left: + laraItem->Animation.AnimNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_MOUNT_LEFT; + laraItem->Animation.ActiveState = KAYAK_STATE_MOUNT_LEFT; + laraItem->Animation.TargetState = KAYAK_STATE_MOUNT_LEFT; + break; + + default: + case VehicleMountType::Right: + laraItem->Animation.AnimNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_MOUNT_RIGHT; + laraItem->Animation.ActiveState = KAYAK_STATE_MOUNT_RIGHT; + laraItem->Animation.TargetState = KAYAK_STATE_MOUNT_RIGHT; + break; + } + laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; + + if (laraItem->RoomNumber != kayakItem->RoomNumber) + ItemNewRoom(lara->ItemNumber, kayakItem->RoomNumber); + + DoVehicleFlareDiscard(laraItem); + laraItem->Pose.Position = kayakItem->Pose.Position; + laraItem->Pose.Orientation = Vector3Shrt(0, kayakItem->Pose.Orientation.y, 0); + laraItem->Animation.IsAirborne = false; + laraItem->Animation.Velocity = 0; + laraItem->Animation.VerticalVelocity = 0; + lara->Control.WaterStatus = WaterStatus::Dry; + kayak->WaterHeight = kayakItem->Pose.Position.y; + kayak->Flags = 0; + + AnimateItem(laraItem); + } + void KayakDraw(ItemInfo* kayakItem) { DrawAnimatingItem(kayakItem); @@ -174,19 +241,19 @@ namespace TEN::Entities::Vehicles void KayakDoWake(ItemInfo* kayakItem, int xOffset, int zOffset, short rotate) { - auto* kayak = (KayakInfo*)kayakItem->Data; + auto* kayak = GetKayakInfo(kayakItem); if (WakePts[kayak->CurrentStartWake][rotate].life) return; - float s = phd_sin(kayakItem->Pose.Orientation.y); - float c = phd_cos(kayakItem->Pose.Orientation.y); + float sinY = phd_sin(kayakItem->Pose.Orientation.y); + float cosY = phd_cos(kayakItem->Pose.Orientation.y); - int x = kayakItem->Pose.Position.x + zOffset * s + xOffset * c; - int z = kayakItem->Pose.Position.z + zOffset * c - xOffset * s; + int x = kayakItem->Pose.Position.x + (zOffset * sinY) + (xOffset * cosY); + int z = kayakItem->Pose.Position.z + (zOffset * cosY) - (xOffset * sinY); - int probedRoomNum = GetCollision(x, kayakItem->Pose.Position.y, z, kayakItem->RoomNumber).RoomNumber; - int waterHeight = GetWaterHeight(x, kayakItem->Pose.Position.y, z, probedRoomNum); + int probedRoomNumber = GetCollision(x, kayakItem->Pose.Position.y, z, kayakItem->RoomNumber).RoomNumber; + int waterHeight = GetWaterHeight(x, kayakItem->Pose.Position.y, z, probedRoomNumber); if (waterHeight != NO_HEIGHT) { @@ -245,14 +312,14 @@ namespace TEN::Entities::Vehicles void KayakDoRipple(ItemInfo* kayakItem, int xOffset, int zOffset) { - float s = phd_sin(kayakItem->Pose.Orientation.y); - float c = phd_cos(kayakItem->Pose.Orientation.y); + float sinY = phd_sin(kayakItem->Pose.Orientation.y); + float cosY = phd_cos(kayakItem->Pose.Orientation.y); - int x = kayakItem->Pose.Position.x + zOffset * s + xOffset * c; - int z = kayakItem->Pose.Position.z + zOffset * c - xOffset * s; + int x = kayakItem->Pose.Position.x + (zOffset * sinY) + (xOffset * cosY); + int z = kayakItem->Pose.Position.z + (zOffset * cosY) - (xOffset * sinY); - int probedRoomNum = GetCollision(x, kayakItem->Pose.Position.y, z, kayakItem->RoomNumber).RoomNumber; - int waterHeight = GetWaterHeight(x, kayakItem->Pose.Position.y, z, probedRoomNum); + int probedRoomNumber = GetCollision(x, kayakItem->Pose.Position.y, z, kayakItem->RoomNumber).RoomNumber; + int waterHeight = GetWaterHeight(x, kayakItem->Pose.Position.y, z, probedRoomNumber); //if (waterHeight != NO_HEIGHT) // SetupRipple(x, kayakItem->Pose.Position.y, z, -2 - (GetRandomControl() & 1), 0, Objects[ID_KAYAK_PADDLE_TRAIL_SPRITE].meshIndex,TO_RAD(kayakItem->Pose.Orientation.y)); @@ -276,46 +343,6 @@ namespace TEN::Entities::Vehicles } } - KayakMountType KayakGetMountType(ItemInfo* laraItem, short itemNumber) - { - auto* lara = GetLaraInfo(laraItem); - auto* kayakItem = &g_Level.Items[itemNumber]; - - if (!(TrInput & IN_ACTION) || - lara->Control.HandStatus != HandStatus::Free || - laraItem->Animation.Airborne) - { - return KayakMountType::None; - } - - int distance = pow(laraItem->Pose.Position.x - kayakItem->Pose.Position.x, 2) + pow(laraItem->Pose.Position.z - kayakItem->Pose.Position.z, 2); - if (distance > pow(360, 2)) - return KayakMountType::None; - - auto probe = GetCollision(kayakItem); - if (probe.Position.Floor > -32000) - { - short angle = phd_atan(kayakItem->Pose.Position.z - laraItem->Pose.Position.z, kayakItem->Pose.Position.x - laraItem->Pose.Position.x); - angle -= kayakItem->Pose.Orientation.y; - - int deltaAngle = laraItem->Pose.Orientation.y - kayakItem->Pose.Orientation.y; - if (angle > -ANGLE(45.0f) && angle < ANGLE(135.0f)) - { - deltaAngle = laraItem->Pose.Orientation.y - kayakItem->Pose.Orientation.y; - if (deltaAngle > ANGLE(45.0f) && deltaAngle < ANGLE(135.0f)) - return KayakMountType::Left; - } - else - { - deltaAngle = laraItem->Pose.Orientation.y - kayakItem->Pose.Orientation.y; - if (deltaAngle > ANGLE(225.0f) && deltaAngle < ANGLE(315.0f)) - return KayakMountType::Right; - } - } - - return KayakMountType::None; - } - int KayakGetCollisionAnim(ItemInfo* kayakItem, int xDiff, int zDiff) { xDiff = kayakItem->Pose.Position.x - xDiff; @@ -378,10 +405,10 @@ namespace TEN::Entities::Vehicles return verticalVelocity; } - void KayakDoCurrent(ItemInfo* laraItem, ItemInfo* kayakItem) + void KayakDoCurrent(ItemInfo* kayakItem, ItemInfo* laraItem) { auto* lara = GetLaraInfo(laraItem); - auto* room = &g_Level.Rooms[kayakItem->RoomNumber]; + auto* room = &g_Level.Rooms[kayakItem->RoomNumber]; // Unused. if (!lara->WaterCurrentActive) { @@ -439,37 +466,10 @@ namespace TEN::Entities::Vehicles lara->WaterCurrentActive = 0; } - int KayakTestHeight(ItemInfo* kayakItem, int x, int z, Vector3Int* pos) - { - Matrix world = - Matrix::CreateFromYawPitchRoll(TO_RAD(kayakItem->Pose.Orientation.y), TO_RAD(kayakItem->Pose.Orientation.x), TO_RAD(kayakItem->Pose.Orientation.z)) * - Matrix::CreateTranslation(kayakItem->Pose.Position.x, kayakItem->Pose.Position.y, kayakItem->Pose.Position.z); - - Vector3 vec = Vector3(x, 0, z); - vec = Vector3::Transform(vec, world); - - pos->x = vec.x; - pos->y = vec.y; - pos->z = vec.z; - - auto probe = GetCollision(pos->x, pos->y, pos->z, kayakItem->RoomNumber); - int probedRoomNum = probe.RoomNumber; - - int height = GetWaterHeight(pos->x, pos->y, pos->z, probedRoomNum); - if (height == NO_HEIGHT) - { - height = probe.Position.Floor; - if (height == NO_HEIGHT) - return height; - } - - return (height - 5); - } - - bool KayakCanGetOut(ItemInfo* kayakItem, int dir) + bool KayakCanGetOut(ItemInfo* kayakItem, int direction) { Vector3Int pos; - int height = KayakTestHeight(kayakItem, (dir < 0) ? -DISMOUNT_DISTANCE : DISMOUNT_DISTANCE, 0, &pos); + int height = GetVehicleWaterHeight(kayakItem, 0, (direction < 0) ? -KAYAK_DISMOUNT_DISTANCE : KAYAK_DISMOUNT_DISTANCE, false, &pos); if ((kayakItem->Pose.Position.y - height) > 0) return false; @@ -580,43 +580,37 @@ namespace TEN::Entities::Vehicles return 0; } - void KayakToBackground(ItemInfo* laraItem, ItemInfo* kayakItem) + void KayakToBackground(ItemInfo* kayakItem, ItemInfo* laraItem) { - auto* kayak = (KayakInfo*)kayakItem->Data; + auto* kayak = GetKayakInfo(kayakItem); - kayak->OldPos = kayakItem->Pose; + kayak->OldPose = kayakItem->Pose; Vector3Int oldPos[9]; int height[8]; - height[0] = KayakTestHeight(kayakItem, 0, 1024, &oldPos[0]); - height[1] = KayakTestHeight(kayakItem, -96, 512, &oldPos[1]); - height[2] = KayakTestHeight(kayakItem, 96, 512, &oldPos[2]); - height[3] = KayakTestHeight(kayakItem, -128, 128, &oldPos[3]); - height[4] = KayakTestHeight(kayakItem, 128, 128, &oldPos[4]); - height[5] = KayakTestHeight(kayakItem, -128, -320, &oldPos[5]); - height[6] = KayakTestHeight(kayakItem, 128, -320, &oldPos[6]); - height[7] = KayakTestHeight(kayakItem, 0, -640, &oldPos[7]); - - for (int i = 0; i < 8; i++) - { - if (oldPos[i].y > height[i]) - oldPos[i].y = height[i]; - } + height[0] = GetVehicleWaterHeight(kayakItem, 1024, 0, true, &oldPos[0]); + height[1] = GetVehicleWaterHeight(kayakItem, 512, -96, true, &oldPos[1]); + height[2] = GetVehicleWaterHeight(kayakItem, 512, 96, true, &oldPos[2]); + height[3] = GetVehicleWaterHeight(kayakItem, 128, -128, true, &oldPos[3]); + height[4] = GetVehicleWaterHeight(kayakItem, 128, 128, true, &oldPos[4]); + height[5] = GetVehicleWaterHeight(kayakItem, -320, -128, true, &oldPos[5]); + height[6] = GetVehicleWaterHeight(kayakItem, -320, 128, true, &oldPos[6]); + height[7] = GetVehicleWaterHeight(kayakItem, -640, 0, true, &oldPos[7]); oldPos[8].x = kayakItem->Pose.Position.x; oldPos[8].y = kayakItem->Pose.Position.y; oldPos[8].z = kayakItem->Pose.Position.z; Vector3Int frontPos, leftPos, rightPos; - int frontHeight = KayakTestHeight(kayakItem, 0, 1024, &frontPos); - int leftHeight = KayakTestHeight(kayakItem, -KAYAK_X, KAYAK_Z, &leftPos); - int rightHeight = KayakTestHeight(kayakItem, KAYAK_X, KAYAK_Z, &rightPos); + int frontHeight = GetVehicleWaterHeight(kayakItem, 1024, 0, false, &frontPos); + int leftHeight = GetVehicleWaterHeight(kayakItem, KAYAK_Z, -KAYAK_X, false, &leftPos); + int rightHeight = GetVehicleWaterHeight(kayakItem, KAYAK_Z, KAYAK_X, false, &rightPos); - kayakItem->Pose.Orientation.y += kayak->TurnRate / (USHRT_MAX + 1); kayakItem->Pose.Position.x += kayakItem->Animation.Velocity * phd_sin(kayakItem->Pose.Orientation.y); kayakItem->Pose.Position.z += kayakItem->Animation.Velocity * phd_cos(kayakItem->Pose.Orientation.y); + kayakItem->Pose.Orientation.y += kayak->TurnRate; - KayakDoCurrent(laraItem,kayakItem); + KayakDoCurrent(kayakItem, laraItem); kayak->LeftVerticalVelocity = KayakDoDynamics(leftHeight, kayak->LeftVerticalVelocity, &leftPos.y); kayak->RightVerticalVelocity = KayakDoDynamics(rightHeight, kayak->RightVerticalVelocity, &rightPos.y); @@ -637,28 +631,28 @@ namespace TEN::Entities::Vehicles int rot = 0; Vector3Int pos; - if ((height2 = KayakTestHeight(kayakItem, 0, -CLICK(2.5f), &pos)) < (oldPos[7].y - KAYAK_COLLIDE)) + if ((height2 = GetVehicleWaterHeight(kayakItem, -CLICK(2.5f), 0, false, &pos)) < (oldPos[7].y - KAYAK_COLLIDE)) rot = KayakDoShift(kayakItem, &pos, &oldPos[7]); - if ((height2 = KayakTestHeight(kayakItem, CLICK(0.5f), -CLICK(1.25f), &pos)) < (oldPos[6].y - KAYAK_COLLIDE)) + if ((height2 = GetVehicleWaterHeight(kayakItem, -CLICK(1.25f), CLICK(0.5f), false, &pos)) < (oldPos[6].y - KAYAK_COLLIDE)) rot += KayakDoShift(kayakItem, &pos, &oldPos[6]); - if ((height2 = KayakTestHeight(kayakItem, -CLICK(0.5f), -CLICK(1.25f), &pos)) < (oldPos[5].y - KAYAK_COLLIDE)) + if ((height2 = GetVehicleWaterHeight(kayakItem, -CLICK(1.25f), -CLICK(0.5f), false, &pos)) < (oldPos[5].y - KAYAK_COLLIDE)) rot += KayakDoShift(kayakItem, &pos, &oldPos[5]); - if ((height2 = KayakTestHeight(kayakItem, CLICK(0.5f), CLICK(0.5f), &pos)) < (oldPos[4].y - KAYAK_COLLIDE)) + if ((height2 = GetVehicleWaterHeight(kayakItem, CLICK(0.5f), CLICK(0.5f), false, &pos)) < (oldPos[4].y - KAYAK_COLLIDE)) rot += KayakDoShift(kayakItem, &pos, &oldPos[4]); - if ((height2 = KayakTestHeight(kayakItem, -CLICK(0.5f), CLICK(0.5f), &pos)) < (oldPos[3].y - KAYAK_COLLIDE)) + if ((height2 = GetVehicleWaterHeight(kayakItem, CLICK(0.5f), -CLICK(0.5f), false, &pos)) < (oldPos[3].y - KAYAK_COLLIDE)) rot += KayakDoShift(kayakItem, &pos, &oldPos[3]); - if ((height2 = KayakTestHeight(kayakItem, 96, CLICK(2), &pos)) < (oldPos[2].y - KAYAK_COLLIDE)) + if ((height2 = GetVehicleWaterHeight(kayakItem, CLICK(2), 96, false, &pos)) < (oldPos[2].y - KAYAK_COLLIDE)) rot += KayakDoShift(kayakItem, &pos, &oldPos[2]); - if ((height2 = KayakTestHeight(kayakItem, -96, CLICK(2), &pos)) < (oldPos[1].y - KAYAK_COLLIDE)) + if ((height2 = GetVehicleWaterHeight(kayakItem, CLICK(2), -96, false, &pos)) < (oldPos[1].y - KAYAK_COLLIDE)) rot += KayakDoShift(kayakItem, &pos, &oldPos[1]); - if ((height2 = KayakTestHeight(kayakItem, 0, CLICK(4), &pos)) < (oldPos[0].y - KAYAK_COLLIDE)) + if ((height2 = GetVehicleWaterHeight(kayakItem, CLICK(4), 0, false, &pos)) < (oldPos[0].y - KAYAK_COLLIDE)) rot += KayakDoShift(kayakItem, &pos, &oldPos[0]); kayakItem->Pose.Orientation.y += rot; @@ -683,9 +677,9 @@ namespace TEN::Entities::Vehicles if (height2 == NO_HEIGHT) { GameVector kayakPos; - kayakPos.x = kayak->OldPos.Position.x; - kayakPos.y = kayak->OldPos.Position.y; - kayakPos.z = kayak->OldPos.Position.z; + kayakPos.x = kayak->OldPose.Position.x; + kayakPos.y = kayak->OldPose.Position.y; + kayakPos.z = kayak->OldPose.Position.z; kayakPos.roomNumber = kayakItem->RoomNumber; CameraCollisionBounds(&kayakPos, 256, 0); @@ -707,11 +701,11 @@ namespace TEN::Entities::Vehicles int newVelocity; newVelocity = (kayakItem->Pose.Position.z - oldPos[8].z) * phd_cos(kayakItem->Pose.Orientation.y) + (kayakItem->Pose.Position.x - oldPos[8].x) * phd_sin(kayakItem->Pose.Orientation.y); - newVelocity *= 256; + newVelocity *= VEHICLE_VELOCITY_SCALE; if (slip) { - if (kayak->Velocity <= MAX_VELOCITY) + if (kayak->Velocity <= KAYAK_VELOCITY_MAX) kayak->Velocity = newVelocity; } else @@ -723,15 +717,15 @@ namespace TEN::Entities::Vehicles kayak->Velocity = newVelocity; } - if (kayak->Velocity < -MAX_VELOCITY) - kayak->Velocity = -MAX_VELOCITY; + if (kayak->Velocity < -KAYAK_VELOCITY_MAX) + kayak->Velocity = -KAYAK_VELOCITY_MAX; } } - void KayakUserInput(ItemInfo* laraItem, ItemInfo* kayakItem) + void KayakUserInput(ItemInfo* kayakItem, ItemInfo* laraItem) { + auto* kayak = GetKayakInfo(kayakItem); auto* lara = GetLaraInfo(laraItem); - auto* kayak = (KayakInfo*)kayakItem->Data; if (laraItem->HitPoints <= 0 && laraItem->Animation.ActiveState != KAYAK_STATE_IDLE_DEATH) @@ -746,7 +740,7 @@ namespace TEN::Entities::Vehicles switch (laraItem->Animation.ActiveState) { case KAYAK_STATE_IDLE: - if (TrInput & KAYAK_IN_DISMOUNT && + if (TrInput & VEHICLE_IN_DISMOUNT && !lara->WaterCurrentActive && !lara->WaterCurrentPull.x && !lara->WaterCurrentPull.z) { @@ -811,20 +805,20 @@ namespace TEN::Entities::Vehicles if (kayak->Forward) { if (!frame) - kayak->LeftRightCount = 0; + kayak->LeftRightPaddleCount = 0; // TODO: Sort out the bitwise operations. - if (frame == 2 && !(kayak->LeftRightCount & 0x80)) - kayak->LeftRightCount++; + if (frame == 2 && !(kayak->LeftRightPaddleCount & 0x80)) + kayak->LeftRightPaddleCount++; else if (frame > 2) - kayak->LeftRightCount &= ~0x80; + kayak->LeftRightPaddleCount &= ~0x80; if (TrInput & KAYAK_IN_FORWARD) { if (TrInput & KAYAK_IN_LEFT && !(TrInput & KAYAK_IN_HOLD)) { - if ((kayak->LeftRightCount & ~0x80) >= 2) + if ((kayak->LeftRightPaddleCount & ~0x80) >= 2) laraItem->Animation.TargetState = KAYAK_STATE_TURN_RIGHT; } else @@ -840,25 +834,25 @@ namespace TEN::Entities::Vehicles { if (kayak->Forward) { - kayak->TurnRate -= KAYAK_FORWARD_ROTATION; - if (kayak->TurnRate < -KAYAK_MAX_TURN) - kayak->TurnRate = -KAYAK_MAX_TURN; + kayak->TurnRate -= KAYAK_TURN_RATE_FORWARD_ACCEL; + if (kayak->TurnRate < -KAYAK_TURN_RATE_MAX) + kayak->TurnRate = -KAYAK_TURN_RATE_MAX; - kayak->Velocity += KAYAK_FORWARD_VELOCITY; + kayak->Velocity += KAYAK_VELOCITY_FORWARD_ACCEL; } else if (kayak->Turn) { - kayak->TurnRate -= KAYAK_HARD_ROTATION; - if (kayak->TurnRate < -KAYAK_MAX_STAT) - kayak->TurnRate = -KAYAK_MAX_STAT; + kayak->TurnRate -= KAYAK_TURN_RATE_HOLD_ACCEL; + if (kayak->TurnRate < -KAYAK_TURN_RATE_HOLD_MAX) + kayak->TurnRate = -KAYAK_TURN_RATE_HOLD_MAX; } else { - kayak->TurnRate -= KAYAK_LEFT_RIGHT_ROTATION; - if (kayak->TurnRate < -KAYAK_MAX_LEFT_RIGHT) - kayak->TurnRate = -KAYAK_MAX_LEFT_RIGHT; + kayak->TurnRate -= KAYAK_TURN_RATE_LR_ACCEL; + if (kayak->TurnRate < -KAYAK_TURN_RATE_LR_MAX) + kayak->TurnRate = -KAYAK_TURN_RATE_LR_MAX; - kayak->Velocity += KAYAK_LEFT_RIGHT_VELOCITY; + kayak->Velocity += KAYAK_VELOCITY_LR_ACCEL; } } @@ -871,19 +865,19 @@ namespace TEN::Entities::Vehicles if (kayak->Forward) { if (!frame) - kayak->LeftRightCount = 0; + kayak->LeftRightPaddleCount = 0; - if (frame == 2 && !(kayak->LeftRightCount & 0x80)) - kayak->LeftRightCount++; + if (frame == 2 && !(kayak->LeftRightPaddleCount & 0x80)) + kayak->LeftRightPaddleCount++; else if (frame > 2) - kayak->LeftRightCount &= ~0x80; + kayak->LeftRightPaddleCount &= ~0x80; if (TrInput & KAYAK_IN_FORWARD) { if (TrInput & KAYAK_IN_RIGHT && !(TrInput & KAYAK_IN_HOLD)) { - if ((kayak->LeftRightCount & ~0x80) >= 2) + if ((kayak->LeftRightPaddleCount & ~0x80) >= 2) laraItem->Animation.TargetState = KAYAK_STATE_TURN_LEFT; } else @@ -900,25 +894,25 @@ namespace TEN::Entities::Vehicles { if (kayak->Forward) { - kayak->TurnRate += KAYAK_FORWARD_ROTATION; - if (kayak->TurnRate > KAYAK_MAX_TURN) - kayak->TurnRate = KAYAK_MAX_TURN; + kayak->TurnRate += KAYAK_TURN_RATE_FORWARD_ACCEL; + if (kayak->TurnRate > KAYAK_TURN_RATE_MAX) + kayak->TurnRate = KAYAK_TURN_RATE_MAX; - kayak->Velocity += KAYAK_FORWARD_VELOCITY; + kayak->Velocity += KAYAK_VELOCITY_FORWARD_ACCEL; } else if (kayak->Turn) { - kayak->TurnRate += KAYAK_HARD_ROTATION; - if (kayak->TurnRate > KAYAK_MAX_STAT) - kayak->TurnRate = KAYAK_MAX_STAT; + kayak->TurnRate += KAYAK_TURN_RATE_HOLD_ACCEL; + if (kayak->TurnRate > KAYAK_TURN_RATE_HOLD_MAX) + kayak->TurnRate = KAYAK_TURN_RATE_HOLD_MAX; } else { - kayak->TurnRate += KAYAK_LEFT_RIGHT_ROTATION; - if (kayak->TurnRate > KAYAK_MAX_LEFT_RIGHT) - kayak->TurnRate = KAYAK_MAX_LEFT_RIGHT; + kayak->TurnRate += KAYAK_TURN_RATE_LR_ACCEL; + if (kayak->TurnRate > KAYAK_TURN_RATE_LR_MAX) + kayak->TurnRate = KAYAK_TURN_RATE_LR_MAX; - kayak->Velocity += KAYAK_LEFT_RIGHT_VELOCITY; + kayak->Velocity += KAYAK_VELOCITY_LR_ACCEL; } } @@ -935,14 +929,14 @@ namespace TEN::Entities::Vehicles { if (frame == 8) { - kayak->TurnRate += KAYAK_FORWARD_ROTATION; - kayak->Velocity -= KAYAK_FORWARD_VELOCITY; + kayak->TurnRate += KAYAK_TURN_RATE_FORWARD_ACCEL; + kayak->Velocity -= KAYAK_VELOCITY_FORWARD_ACCEL; } if (frame == 31) { - kayak->TurnRate -= KAYAK_FORWARD_ROTATION; - kayak->Velocity -= KAYAK_FORWARD_VELOCITY; + kayak->TurnRate -= KAYAK_TURN_RATE_FORWARD_ACCEL; + kayak->Velocity -= KAYAK_VELOCITY_FORWARD_ACCEL; } if (frame < 15 && frame & 1) @@ -967,10 +961,10 @@ namespace TEN::Entities::Vehicles if (kayak->Velocity >= 0) { kayak->TurnRate -= KAYAK_TURN_ROTATION; - if (kayak->TurnRate < -KAYAK_MAX_TURN) - kayak->TurnRate = -KAYAK_MAX_TURN; + if (kayak->TurnRate < -KAYAK_TURN_RATE_MAX) + kayak->TurnRate = -KAYAK_TURN_RATE_MAX; - kayak->Velocity += -KAYAK_TURN_BRAKE; + kayak->Velocity += -KAYAK_VELOCITY_HOLD_TURN_DECEL; if (kayak->Velocity < 0) kayak->Velocity = 0; } @@ -979,7 +973,7 @@ namespace TEN::Entities::Vehicles { kayak->TurnRate += KAYAK_TURN_ROTATION; - kayak->Velocity += KAYAK_TURN_BRAKE; + kayak->Velocity += KAYAK_VELOCITY_HOLD_TURN_DECEL; if (kayak->Velocity > 0) kayak->Velocity = 0; } @@ -1003,10 +997,10 @@ namespace TEN::Entities::Vehicles if (kayak->Velocity >= 0) { kayak->TurnRate += KAYAK_TURN_ROTATION; - if (kayak->TurnRate > KAYAK_MAX_TURN) - kayak->TurnRate = KAYAK_MAX_TURN; + if (kayak->TurnRate > KAYAK_TURN_RATE_MAX) + kayak->TurnRate = KAYAK_TURN_RATE_MAX; - kayak->Velocity += -KAYAK_TURN_BRAKE; + kayak->Velocity += -KAYAK_VELOCITY_HOLD_TURN_DECEL; if (kayak->Velocity < 0) kayak->Velocity = 0; } @@ -1015,7 +1009,7 @@ namespace TEN::Entities::Vehicles { kayak->TurnRate -= KAYAK_TURN_ROTATION; - kayak->Velocity += KAYAK_TURN_BRAKE; + kayak->Velocity += KAYAK_VELOCITY_HOLD_TURN_DECEL; if (kayak->Velocity > 0) kayak->Velocity = 0; } @@ -1033,7 +1027,7 @@ namespace TEN::Entities::Vehicles { kayak->Flags |= 0x80; lara->MeshPtrs[LM_RHAND] = Objects[ID_KAYAK_LARA_ANIMS].meshIndex + LM_RHAND; - laraItem->MeshBits &= ~LARA_LEG_BITS; + laraItem->ClearBits(JointBitType::Mesh, KayakLaraLegJoints); } break; @@ -1045,7 +1039,7 @@ namespace TEN::Entities::Vehicles { kayak->Flags &= ~0x80; lara->MeshPtrs[LM_RHAND] = Objects[ID_LARA_SKIN].meshIndex + LM_RHAND; - laraItem->MeshBits |= LARA_LEG_BITS; + laraItem->SetBits(JointBitType::Mesh, KayakLaraLegJoints); } laraItem->Animation.TargetState = laraItem->Animation.RequiredState; @@ -1059,18 +1053,16 @@ namespace TEN::Entities::Vehicles GetLaraJointPosition(&vec, LM_HIPS); SetAnimation(laraItem, LA_JUMP_FORWARD); - laraItem->Pose.Position.x = vec.x; - laraItem->Pose.Position.y = vec.y; - laraItem->Pose.Position.z = vec.z; + laraItem->Pose.Position = vec; laraItem->Pose.Orientation.x = 0; laraItem->Pose.Orientation.y = kayakItem->Pose.Orientation.y - ANGLE(90.0f); laraItem->Pose.Orientation.z = 0; laraItem->Animation.Velocity = 40; laraItem->Animation.VerticalVelocity = -50; - laraItem->Animation.Airborne = true; + laraItem->Animation.IsAirborne = true; lara->Control.HandStatus = HandStatus::Free; lara->Vehicle = NO_ITEM; - kayak->LeftRightCount = 0; + kayak->LeftRightPaddleCount = 0; } break; @@ -1083,56 +1075,54 @@ namespace TEN::Entities::Vehicles GetLaraJointPosition(&vec, LM_HIPS); SetAnimation(laraItem, LA_JUMP_FORWARD); - laraItem->Pose.Position.x = vec.x; - laraItem->Pose.Position.y = vec.y; - laraItem->Pose.Position.z = vec.z; + laraItem->Pose.Position = vec; laraItem->Pose.Orientation.x = 0; laraItem->Pose.Orientation.y = kayakItem->Pose.Orientation.y + ANGLE(90.0f); laraItem->Pose.Orientation.z = 0; + laraItem->Animation.IsAirborne = true; laraItem->Animation.Velocity = 40; laraItem->Animation.VerticalVelocity = -50; - laraItem->Animation.Airborne = true; lara->Control.HandStatus = HandStatus::Free; lara->Vehicle = NO_ITEM; - kayak->LeftRightCount = 0; + kayak->LeftRightPaddleCount = 0; } } if (kayak->Velocity > 0) { - kayak->Velocity -= KAYAK_FRICTION; + kayak->Velocity -= KAYAK_VELOCITY_FRICTION_DECEL; if (kayak->Velocity < 0) kayak->Velocity = 0; } else if (kayak->Velocity < 0) { - kayak->Velocity += KAYAK_FRICTION; + kayak->Velocity += KAYAK_VELOCITY_FRICTION_DECEL; if (kayak->Velocity > 0) kayak->Velocity = 0; } - if (kayak->Velocity > MAX_VELOCITY) - kayak->Velocity = MAX_VELOCITY; - else if (kayak->Velocity < -MAX_VELOCITY) - kayak->Velocity = -MAX_VELOCITY; + if (kayak->Velocity > KAYAK_VELOCITY_MAX) + kayak->Velocity = KAYAK_VELOCITY_MAX; + else if (kayak->Velocity < -KAYAK_VELOCITY_MAX) + kayak->Velocity = -KAYAK_VELOCITY_MAX; - kayakItem->Animation.Velocity = (kayak->Velocity / (USHRT_MAX + 1)); - ; + kayakItem->Animation.Velocity = kayak->Velocity / VEHICLE_VELOCITY_SCALE; + if (kayak->TurnRate >= 0) { - kayak->TurnRate -= KAYAK_ROTATE_FRICTION; + kayak->TurnRate -= KAYAK_TURN_RATE_FRICTION_DECEL; if (kayak->TurnRate < 0) kayak->TurnRate = 0; } else if (kayak->TurnRate < 0) { - kayak->TurnRate += KAYAK_ROTATE_FRICTION; + kayak->TurnRate += KAYAK_TURN_RATE_FRICTION_DECEL; if (kayak->TurnRate > 0) kayak->TurnRate = 0; } } - void KayakToItemCollision(ItemInfo* laraItem, ItemInfo* kayakItem) + void KayakToItemCollision(ItemInfo* kayakItem, ItemInfo* laraItem) { short roomsToCheck[128]; short numRoomsToCheck = 0; @@ -1198,87 +1188,32 @@ namespace TEN::Entities::Vehicles laraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase; laraItem->Animation.ActiveState = 12; // TODO laraItem->Animation.TargetState = 12; - laraItem->HitPoints = -1; + laraItem->Animation.IsAirborne = false; laraItem->Animation.Velocity = 0; laraItem->Animation.VerticalVelocity = 0; - laraItem->Animation.Airborne = false; + laraItem->HitPoints = -1; AnimateItem(laraItem); - lara->ExtraAnim = 1; lara->Control.HandStatus = HandStatus::Busy; lara->Control.Weapon.GunType = LaraWeaponType::None; + lara->ExtraAnim = 1; lara->HitDirection = -1; } - void KayakCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) - { - auto* lara = GetLaraInfo(laraItem); - auto* kayakItem = &g_Level.Items[itemNumber]; - auto* kayak = (KayakInfo*)kayakItem->Data; - - if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) - return; - - KayakMountType mountType = KayakGetMountType(laraItem, itemNumber); - if (mountType != KayakMountType::None) - { - lara->Vehicle = itemNumber; - - if (lara->Control.Weapon.GunType == LaraWeaponType::Flare) - { - CreateFlare(laraItem, ID_FLARE_ITEM, 0); - UndrawFlareMeshes(laraItem); - lara->Flare.ControlLeft = 0; - lara->Control.Weapon.RequestGunType = lara->Control.Weapon.GunType = LaraWeaponType::None; - } - - if (mountType == KayakMountType::Right) - laraItem->Animation.AnimNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_MOUNT_RIGHT; - else if (mountType == KayakMountType::Left) - laraItem->Animation.AnimNumber = Objects[ID_KAYAK_LARA_ANIMS].animIndex + KAYAK_ANIM_MOUNT_LEFT; - - laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - laraItem->Animation.ActiveState = laraItem->Animation.TargetState = KAYAK_STATE_MOUNT_LEFT; - laraItem->Pose.Position.x = kayakItem->Pose.Position.x; - laraItem->Pose.Position.y = kayakItem->Pose.Position.y; - laraItem->Pose.Position.z = kayakItem->Pose.Position.z; - laraItem->Pose.Orientation.x = 0; - laraItem->Pose.Orientation.y = kayakItem->Pose.Orientation.y; - laraItem->Pose.Orientation.z = 0; - laraItem->Animation.Velocity = 0; - laraItem->Animation.VerticalVelocity = 0; - laraItem->Animation.Airborne = false; - lara->Control.WaterStatus = WaterStatus::Dry; - - if (laraItem->RoomNumber != kayakItem->RoomNumber) - ItemNewRoom(lara->ItemNumber, kayakItem->RoomNumber); - - AnimateItem(laraItem); - - kayak->WaterHeight = kayakItem->Pose.Position.y; - kayak->Flags = 0; - } - else - { - coll->Setup.EnableObjectPush = true; - ObjectCollision(itemNumber, laraItem, coll); - } - } - bool KayakControl(ItemInfo* laraItem) { auto* lara = GetLaraInfo(laraItem); auto* kayakItem = &g_Level.Items[lara->Vehicle]; - auto* kayak = (KayakInfo*)kayakItem->Data; + auto* kayak = GetKayakInfo(kayakItem); if (TrInput & IN_LOOK) LookUpDown(laraItem); int ofs = kayakItem->Animation.VerticalVelocity; - KayakUserInput(laraItem, kayakItem); - KayakToBackground(laraItem, kayakItem); + KayakUserInput(kayakItem, laraItem); + KayakToBackground(kayakItem, laraItem); TestTriggers(kayakItem, false); auto probe = GetCollision(kayakItem); @@ -1314,9 +1249,7 @@ namespace TEN::Entities::Vehicles ItemNewRoom(lara->ItemNumber, probe.RoomNumber); } - laraItem->Pose.Position.x = kayakItem->Pose.Position.x; - laraItem->Pose.Position.y = kayakItem->Pose.Position.y; - laraItem->Pose.Position.z = kayakItem->Pose.Position.z; + laraItem->Pose.Position = kayakItem->Pose.Position; laraItem->Pose.Orientation.x = kayakItem->Pose.Orientation.x; laraItem->Pose.Orientation.y = kayakItem->Pose.Orientation.y; laraItem->Pose.Orientation.z = kayakItem->Pose.Orientation.z / 2; @@ -1373,7 +1306,7 @@ namespace TEN::Entities::Vehicles } KayakUpdateWakeFX(); - KayakToItemCollision(laraItem, kayakItem); + KayakToItemCollision(kayakItem, laraItem); return (lara->Vehicle != NO_ITEM) ? true : false; } diff --git a/TombEngine/Objects/TR3/Vehicles/kayak.h b/TombEngine/Objects/TR3/Vehicles/kayak.h index da55a7dee..15aed7e1b 100644 --- a/TombEngine/Objects/TR3/Vehicles/kayak.h +++ b/TombEngine/Objects/TR3/Vehicles/kayak.h @@ -1,12 +1,16 @@ #pragma once +#include "Objects/Utils/VehicleHelpers.h" -struct ItemInfo; struct CollisionInfo; -struct Vector3Int; +struct ItemInfo; namespace TEN::Entities::Vehicles { void InitialiseKayak(short itemNumber); + + void KayakPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + void DoKayakMount(ItemInfo* kayakItem, ItemInfo* laraItem, VehicleMountType mountType); + void KayakDraw(ItemInfo* kayakItem); void KayakDoWake(ItemInfo* kayakItem, int xOffset, int zOffset, short rotate); @@ -15,15 +19,13 @@ namespace TEN::Entities::Vehicles int KayakGetCollisionAnim(ItemInfo* kayakItem, int xDiff, int zDiff); int KayakDoDynamics(int height, int verticalVelocity, int* y); - void KayakDoCurrent(ItemInfo* laraItem, ItemInfo* kayakItem); - int KayakTestHeight(ItemInfo* kayakItem, int x, int z, Vector3Int* pos); + void KayakDoCurrent(ItemInfo* kayakItem, ItemInfo* laraItem); bool KayakCanGetOut(ItemInfo* kayakItem, int dir); int KayakDoShift(ItemInfo* kayakItem, Vector3Int* pos, Vector3Int* old); - void KayakToBackground(ItemInfo* laraItem, ItemInfo* kayakItem); - void KayakUserInput(ItemInfo* laraItem, ItemInfo* kayakItem); - void KayakToItemCollision(ItemInfo* laraItem, ItemInfo* kayakItem); + void KayakToBackground(ItemInfo* kayakItem, ItemInfo* laraItem); + void KayakUserInput(ItemInfo* kayakItem, ItemInfo* laraItem); + void KayakToItemCollision(ItemInfo* kayakItem, ItemInfo* laraItem); void KayakLaraRapidsDrown(ItemInfo* laraItem); - void KayakCollision(short itemNum, ItemInfo* laraItem, CollisionInfo* coll); bool KayakControl(ItemInfo* laraItem); } diff --git a/TombEngine/Objects/TR3/Vehicles/kayak_info.h b/TombEngine/Objects/TR3/Vehicles/kayak_info.h index d3a75b4da..afb558ce1 100644 --- a/TombEngine/Objects/TR3/Vehicles/kayak_info.h +++ b/TombEngine/Objects/TR3/Vehicles/kayak_info.h @@ -5,23 +5,23 @@ namespace TEN::Entities::Vehicles { struct KayakInfo { - int TurnRate; + int TurnRate = 0; - int Velocity; - int FrontVerticalVelocity; - int LeftVerticalVelocity; - int RightVerticalVelocity; + int Velocity = 0; + int FrontVerticalVelocity = 0; + int LeftVerticalVelocity = 0; + int RightVerticalVelocity = 0; - unsigned int LeftRightCount; - int WaterHeight; - PHD_3DPOS OldPos; - bool Turn; - bool Forward; - bool TrueWater; + PHD_3DPOS OldPose = PHD_3DPOS(); + unsigned int LeftRightPaddleCount = 0; + int WaterHeight = 0; + bool Turn = false; + bool Forward = false; + bool TrueWater = false; - int CurrentStartWake; - int WakeShade; + int CurrentStartWake = 0; + int WakeShade = 0; - char Flags; + char Flags = NULL; }; } diff --git a/TombEngine/Objects/TR3/Vehicles/minecart.cpp b/TombEngine/Objects/TR3/Vehicles/minecart.cpp index 6379970c4..c5dfee8b5 100644 --- a/TombEngine/Objects/TR3/Vehicles/minecart.cpp +++ b/TombEngine/Objects/TR3/Vehicles/minecart.cpp @@ -12,46 +12,56 @@ #include "Game/Lara/lara_flare.h" #include "Game/Lara/lara_helpers.h" #include "Objects/TR3/Vehicles/minecart_info.h" +#include "Objects/Utils/VehicleHelpers.h" #include "Sound/sound.h" #include "Specific/input.h" #include "Specific/level.h" #include "Specific/setup.h" #include "Specific/prng.h" -using namespace TEN::Input; using namespace TEN::Effects::Spark; +using namespace TEN::Input; using namespace TEN::Math::Random; +using std::vector; namespace TEN::Entities::Vehicles { + const vector MinecartMountTypes = + { + VehicleMountType::LevelStart, + VehicleMountType::Left, + VehicleMountType::Right + }; + constexpr auto MINECART_RADIUS = 100; constexpr auto MINECART_ENTITY_RADIUS = CLICK(1); constexpr auto MINECART_HEIGHT = CLICK(2); constexpr auto MINECART_GRAVITY = SECTOR(1) + 1; + constexpr auto MINECART_STEP_HEIGHT = CLICK(1); + constexpr auto MINECART_MOUNT_DISTANCE = CLICK(2); constexpr auto MINECART_DISMOUNT_DISTANCE = 330; constexpr auto MINECART_NUM_HITS = 25; - constexpr auto MINECART_SPEED_MIN = 10 * 256; // TODO: These two have confusing names. @Sezz - constexpr auto MINECART_VELOCITY_MIN = 32; - constexpr auto MINECART_VERTICAL_VELOCITY_MAX = 63 * 256; - constexpr auto MINECART_JUMP_VELOCITY = 63 * 1024; - constexpr auto MINECART_TURN_DEATH_VELOCITY = 128; - constexpr auto MINECART_TURN_FRICTION_VELOCITY = 70; - constexpr auto MINECART_DECELERATION = 6 * 256; + constexpr auto MINECART_VELOCITY_DECEL = 6 * VEHICLE_VELOCITY_SCALE; - constexpr auto MINECART_FORWARD_GRADIENT = -128; - constexpr auto MINECART_BACK_GRADIENT = 128; + constexpr auto MINECART_SPEED_MIN = 10 * VEHICLE_VELOCITY_SCALE; // TODO: These two have confusing names. @Sezz + constexpr auto MINECART_VELOCITY_MIN = 32; + constexpr auto MINECART_FRICTION_VELOCITY_MIN = 70; + constexpr auto MINECART_STOP_VELOCITY_MAX = 240; + constexpr auto MINECART_VERTICAL_VELOCITY_MAX = 63 * VEHICLE_VELOCITY_SCALE; + + constexpr auto MINECART_JUMP_VERTICAL_VELOCITY = 252 * VEHICLE_VELOCITY_SCALE; + constexpr auto MINECART_TURN_DEATH_VELOCITY = 128; + + constexpr auto MINECART_FORWARD_GRADIENT = -CLICK(0.5f); + constexpr auto MINECART_BACK_GRADIENT = CLICK(0.5f); constexpr auto MINECART_WRENCH_MESH_TOGGLE_FRAME = 20; #define MINECART_TERMINAL_ANGLE ANGLE(22.0f) - #define MINECART_IN_BRAKE (IN_BACK | IN_WALK | IN_JUMP) - #define MINECART_IN_DUCK IN_CROUCH - #define MINECART_IN_SWIPE IN_ACTION - #define MINECART_IN_DISMOUNT (IN_JUMP | IN_ROLL) - #define MINECART_IN_LEFT IN_LEFT - #define MINECART_IN_RIGHT IN_RIGHT + #define MINECART_IN_DUCK IN_CROUCH + #define MINECART_IN_SWIPE (IN_ACTION | IN_DRAW) int Wheels[4] = { 2, 3, 1, 4 }; @@ -144,7 +154,7 @@ namespace TEN::Entities::Vehicles MINECART_FLAG_DEAD = (1 << 7) }; - static MinecartInfo* GetMinecartInfo(ItemInfo* minecartItem) + MinecartInfo* GetMinecartInfo(ItemInfo* minecartItem) { return (MinecartInfo*)minecartItem->Data; } @@ -154,20 +164,75 @@ namespace TEN::Entities::Vehicles auto* minecartItem = &g_Level.Items[itemNumber]; minecartItem->Data = MinecartInfo(); auto* minecart = GetMinecartInfo(minecartItem); + } + void MinecartPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) + { + auto* minecartItem = &g_Level.Items[itemNumber]; + auto* lara = GetLaraInfo(laraItem); + + if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) + return; + + // Don't get into minecart if there are two stop blocks in front. + // This allows creation of minecarts which will get to a point and stop forever. + auto mountType = GetVehicleMountType(minecartItem, laraItem, coll, MinecartMountTypes, MINECART_MOUNT_DISTANCE); + if (mountType == VehicleMountType::None || + GetCollision(minecartItem, minecartItem->Pose.Orientation.y, SECTOR(1)).BottomBlock->Flags.MinecartStop()) + { + ObjectCollision(itemNumber, laraItem, coll); + } + else + { + lara->Vehicle = itemNumber; + DoMinecartMount(minecartItem, laraItem, mountType); + } + } + + void DoMinecartMount(ItemInfo* minecartItem, ItemInfo* laraItem, VehicleMountType mountType) + { + auto* minecart = GetMinecartInfo(minecartItem); + auto* lara = GetLaraInfo(laraItem); + + switch (mountType) + { + case VehicleMountType::LevelStart: + laraItem->Animation.AnimNumber = Objects[ID_MINECART_LARA_ANIMS].animIndex + MINECART_ANIM_IDLE; + laraItem->Animation.ActiveState = MINECART_STATE_IDLE; + laraItem->Animation.TargetState = MINECART_STATE_IDLE; + break; + + case VehicleMountType::Left: + laraItem->Animation.AnimNumber = Objects[ID_MINECART_LARA_ANIMS].animIndex + MINECART_ANIM_MOUNT_LEFT; + laraItem->Animation.ActiveState = MINECART_STATE_MOUNT; + laraItem->Animation.TargetState = MINECART_STATE_MOUNT; + break; + + default: + case VehicleMountType::Right: + laraItem->Animation.AnimNumber = Objects[ID_MINECART_LARA_ANIMS].animIndex + MINECART_ANIM_MOUNT_RIGHT; + laraItem->Animation.ActiveState = MINECART_STATE_MOUNT; + laraItem->Animation.TargetState = MINECART_STATE_MOUNT; + break; + } + laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; + + DoVehicleFlareDiscard(laraItem); + laraItem->Pose = minecartItem->Pose; + lara->Control.HandStatus = HandStatus::Busy; minecart->Velocity = 0; minecart->VerticalVelocity = 0; minecart->Gradient = 0; - minecart->Flags = NULL; + minecart->Flags = 0; } - - static void TriggerWheelSparkles(ItemInfo* item, bool left) + + static void TriggerWheelSparkles(ItemInfo* minecartItem, bool left) { for (int i = 0; i < 2; i++) { - auto pos = Vector3Int{}; - GetJointAbsPosition(item, &pos, Wheels[(left ? 0 : 2) + i]); - TriggerFrictionSpark(&GameVector(pos.x, pos.y, pos.z, item->RoomNumber), item->Pose.Orientation, 512, 10); + auto pos = Vector3Int(); + GetJointAbsPosition(minecartItem, &pos, Wheels[(left ? 0 : 2) + i]); + TriggerFrictionSpark(&GameVector(pos.x, pos.y, pos.z, minecartItem->RoomNumber), minecartItem->Pose.Orientation, 512, 10); if (i) { @@ -179,21 +244,6 @@ namespace TEN::Entities::Vehicles } } - static int TestMinecartHeight(ItemInfo* minecartItem, int xOffset, int zOffset) - { - float sinX = phd_sin(minecartItem->Pose.Orientation.x); - float sinY = phd_sin(minecartItem->Pose.Orientation.y); - float cosY = phd_cos(minecartItem->Pose.Orientation.y); - float sinZ = phd_sin(minecartItem->Pose.Orientation.z); - - auto pos = Vector3Int( - minecartItem->Pose.Position.x + (zOffset * sinY) + (xOffset * cosY), - minecartItem->Pose.Position.y - (zOffset * sinX) + (xOffset * sinZ), - minecartItem->Pose.Position.z + (zOffset * cosY) - (xOffset * sinY) - ); - return GetCollision(pos.x, pos.y, pos.z, minecartItem->RoomNumber).Position.Floor; - } - static short GetMinecartCollision(ItemInfo* minecartItem, short angle, int distance) { auto probe = GetCollision(minecartItem, angle, distance, -LARA_HEIGHT); @@ -204,41 +254,6 @@ namespace TEN::Entities::Vehicles return (short)probe.Position.Floor; } - static bool GetInMinecart(ItemInfo* minecartItem, ItemInfo* laraItem, CollisionInfo* coll) - { - auto* lara = GetLaraInfo(laraItem); - - if (!(TrInput & IN_ACTION) || lara->Control.HandStatus != HandStatus::Free || - laraItem->Animation.Airborne) - { - return false; - } - - if (!TestBoundsCollide(minecartItem, laraItem, coll->Setup.Radius)) - return false; - - if (!TestCollision(minecartItem, laraItem)) - return false; - - int x = laraItem->Pose.Position.x - minecartItem->Pose.Position.x; - int z = laraItem->Pose.Position.z - minecartItem->Pose.Position.z; - - int distance = pow(x, 2) + pow(z, 2); - if (distance > pow(CLICK(2), 2)) - return false; - - if (GetCollision(minecartItem).Position.Floor < -SECTOR(31.25f)) - return false; - - // Don't get into minecart if there's two stop blocks in front. - // Allows to create minecarts which will get to a point and stop forever. - - if (GetCollision(minecartItem, minecartItem->Pose.Orientation.y, SECTOR(1)).BottomBlock->Flags.MinecartStop()) - return false; - - return true; - } - static bool TestMinecartDismount(ItemInfo* laraItem, int direction) { auto* lara = GetLaraInfo(laraItem); @@ -267,7 +282,7 @@ namespace TEN::Entities::Vehicles return true; } - static void MinecartToEntityCollision(ItemInfo* laraItem, ItemInfo* minecartItem) + static void MinecartToEntityCollision(ItemInfo* minecartItem, ItemInfo* laraItem) { auto roomsList = GetRoomList(minecartItem->RoomNumber); @@ -336,10 +351,11 @@ namespace TEN::Entities::Vehicles } } - static void MoveCart(ItemInfo* laraItem, ItemInfo* minecartItem) + static void MoveCart(ItemInfo* minecartItem, ItemInfo* laraItem) { - auto* lara = GetLaraInfo(laraItem); auto* minecart = GetMinecartInfo(minecartItem); + auto* lara = GetLaraInfo(laraItem); + auto flags = GetCollision(minecartItem).BottomBlock->Flags; if (minecart->StopDelay) @@ -348,7 +364,7 @@ namespace TEN::Entities::Vehicles if ((flags.MinecartStop() && !minecart->StopDelay) && ((minecartItem->Pose.Position.x & 0x380) == 512 || (minecartItem->Pose.Position.z & 0x380) == 512)) { - if (minecart->Velocity < 0xf000) + if (minecart->Velocity < MINECART_STOP_VELOCITY_MAX) { minecartItem->Animation.Velocity = 0; minecart->Velocity = 0; @@ -434,7 +450,7 @@ namespace TEN::Entities::Vehicles minecart->Velocity = MINECART_SPEED_MIN; minecart->Velocity -= minecart->Gradient * 4; - minecartItem->Animation.Velocity = minecart->Velocity / 256; + minecartItem->Animation.Velocity = minecart->Velocity / VEHICLE_VELOCITY_SCALE; if (minecartItem->Animation.Velocity < MINECART_VELOCITY_MIN) { @@ -515,7 +531,7 @@ namespace TEN::Entities::Vehicles minecartItem->Pose.Position.x = minecart->TurnX + x * 3584; minecartItem->Pose.Position.z = minecart->TurnZ + z * 3584; - if (minecartItem->Animation.Velocity > MINECART_TURN_FRICTION_VELOCITY) + if (minecartItem->Animation.Velocity > MINECART_FRICTION_VELOCITY_MIN) { SoundEffect(SFX_TR3_VEHICLE_MINECART_BRAKE, &minecartItem->Pose, SoundEnvironment::Always); TriggerWheelSparkles(minecartItem, (minecart->Flags & MINECART_FLAG_TURNING_RIGHT) != 0); @@ -525,12 +541,12 @@ namespace TEN::Entities::Vehicles else TranslateItem(minecartItem, minecartItem->Pose.Orientation.y, minecartItem->Animation.Velocity); - minecart->FloorHeightMiddle = TestMinecartHeight(minecartItem, 0, 0); + minecart->FloorHeightMiddle = GetVehicleHeight(minecartItem, 0, 0, true, &Vector3Int()); if (!minecart->VerticalVelocity) { minecartItem->Pose.Position.y = minecart->FloorHeightMiddle; - minecart->FloorHeightFront = TestMinecartHeight(minecartItem, 0, CLICK(1)); + minecart->FloorHeightFront = GetVehicleHeight(minecartItem, CLICK(1), 0, false, &Vector3Int()); minecart->Gradient = minecart->FloorHeightMiddle - minecart->FloorHeightFront; } else @@ -549,7 +565,7 @@ namespace TEN::Entities::Vehicles if (minecart->VerticalVelocity > MINECART_VERTICAL_VELOCITY_MAX) minecart->VerticalVelocity = MINECART_VERTICAL_VELOCITY_MAX; - minecartItem->Pose.Position.y += minecart->VerticalVelocity / 256; + minecartItem->Pose.Position.y += minecart->VerticalVelocity / VEHICLE_VELOCITY_SCALE; } } @@ -567,8 +583,9 @@ namespace TEN::Entities::Vehicles minecartItem->Pose.Orientation.z -= minecartItem->Pose.Orientation.z / 8; } - static void DoUserInput(ItemInfo* minecartItem, ItemInfo* laraItem, MinecartInfo* minecart) + static void DoUserInput(ItemInfo* minecartItem, ItemInfo* laraItem) { + auto* minecart = GetMinecartInfo(minecartItem); auto* lara = GetLaraInfo(laraItem); short floorHeight; @@ -580,7 +597,7 @@ namespace TEN::Entities::Vehicles laraItem->Animation.TargetState = MINECART_STATE_SWIPE; else if (TrInput & MINECART_IN_DUCK) laraItem->Animation.TargetState = MINECART_STATE_DUCK; - else if (TrInput & MINECART_IN_BRAKE) + else if (TrInput & (VEHICLE_IN_BRAKE | VEHICLE_IN_SLOW)) laraItem->Animation.TargetState = MINECART_STATE_BRAKE; else if (minecart->Velocity == MINECART_VELOCITY_MIN || minecart->Flags & MINECART_FLAG_STOPPED) laraItem->Animation.TargetState = MINECART_STATE_IDLE; @@ -588,9 +605,9 @@ namespace TEN::Entities::Vehicles laraItem->Animation.TargetState = MINECART_STATE_FORWARD; else if (minecart->Gradient > MINECART_BACK_GRADIENT) laraItem->Animation.TargetState = MINECART_STATE_BACK; - else if (TrInput & MINECART_IN_LEFT) + else if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = MINECART_STATE_LEFT; - else if (TrInput & MINECART_IN_RIGHT) + else if (TrInput & VEHICLE_IN_RIGHT) laraItem->Animation.TargetState = MINECART_STATE_RIGHT; break; @@ -600,7 +617,7 @@ namespace TEN::Entities::Vehicles laraItem->Animation.TargetState = MINECART_STATE_SWIPE; else if (TrInput & MINECART_IN_DUCK) laraItem->Animation.TargetState = MINECART_STATE_DUCK; - else if (TrInput & MINECART_IN_BRAKE) + else if (TrInput & (VEHICLE_IN_BRAKE | VEHICLE_IN_SLOW)) laraItem->Animation.TargetState = MINECART_STATE_BRAKE; else if (minecart->Gradient > MINECART_FORWARD_GRADIENT) laraItem->Animation.TargetState = MINECART_STATE_MOVE; @@ -612,7 +629,7 @@ namespace TEN::Entities::Vehicles laraItem->Animation.TargetState = MINECART_STATE_SWIPE; else if (TrInput & MINECART_IN_DUCK) laraItem->Animation.TargetState = MINECART_STATE_DUCK; - else if (TrInput & MINECART_IN_BRAKE) + else if (TrInput & (VEHICLE_IN_BRAKE | VEHICLE_IN_SLOW)) laraItem->Animation.TargetState = MINECART_STATE_BRAKE; else if (minecart->Gradient < MINECART_BACK_GRADIENT) laraItem->Animation.TargetState = MINECART_STATE_MOVE; @@ -624,10 +641,10 @@ namespace TEN::Entities::Vehicles laraItem->Animation.TargetState = MINECART_STATE_SWIPE; else if (TrInput & MINECART_IN_DUCK) laraItem->Animation.TargetState = MINECART_STATE_DUCK; - else if (TrInput & MINECART_IN_BRAKE) + else if (TrInput & (VEHICLE_IN_BRAKE | VEHICLE_IN_SLOW)) laraItem->Animation.TargetState = MINECART_STATE_BRAKE; - if (!(TrInput & MINECART_IN_LEFT)) + if (!(TrInput & VEHICLE_IN_LEFT)) laraItem->Animation.TargetState = MINECART_STATE_MOVE; break; @@ -637,10 +654,10 @@ namespace TEN::Entities::Vehicles laraItem->Animation.TargetState = MINECART_STATE_SWIPE; else if (TrInput & MINECART_IN_DUCK) laraItem->Animation.TargetState = MINECART_STATE_DUCK; - else if (TrInput & MINECART_IN_BRAKE) + else if (TrInput & (VEHICLE_IN_BRAKE | VEHICLE_IN_SLOW)) laraItem->Animation.TargetState = MINECART_STATE_BRAKE; - if (!(TrInput & MINECART_IN_RIGHT)) + if (!(TrInput & VEHICLE_IN_RIGHT)) laraItem->Animation.TargetState = MINECART_STATE_MOVE; break; @@ -653,14 +670,14 @@ namespace TEN::Entities::Vehicles minecart->StopDelay = 64; } - if (TrInput & MINECART_IN_DISMOUNT && minecart->Flags & MINECART_FLAG_STOPPED) + if (TrInput & VEHICLE_IN_DISMOUNT && minecart->Flags & MINECART_FLAG_STOPPED) { - if (TrInput & MINECART_IN_LEFT && TestMinecartDismount(laraItem, -1)) + if (TrInput & VEHICLE_IN_LEFT && TestMinecartDismount(laraItem, -1)) { laraItem->Animation.TargetState = MINECART_STATE_DISMOUNT; minecart->Flags &= ~MINECART_FLAG_DISMOUNT_RIGHT; } - else if (TrInput & MINECART_IN_RIGHT && TestMinecartDismount(laraItem, 1)) + else if (TrInput & VEHICLE_IN_RIGHT && TestMinecartDismount(laraItem, 1)) { laraItem->Animation.TargetState = MINECART_STATE_DISMOUNT; minecart->Flags |= MINECART_FLAG_DISMOUNT_RIGHT; @@ -680,7 +697,7 @@ namespace TEN::Entities::Vehicles case MINECART_STATE_DUCK: if (TrInput & MINECART_IN_SWIPE) laraItem->Animation.TargetState = MINECART_STATE_SWIPE; - else if (TrInput & MINECART_IN_BRAKE) + else if (TrInput & (VEHICLE_IN_BRAKE | VEHICLE_IN_SLOW)) laraItem->Animation.TargetState = MINECART_STATE_BRAKE; else if (!(TrInput & MINECART_IN_DUCK)) laraItem->Animation.TargetState = MINECART_STATE_IDLE; @@ -697,17 +714,17 @@ namespace TEN::Entities::Vehicles laraItem->Animation.TargetState = MINECART_STATE_DUCK; StopSoundEffect(SFX_TR3_VEHICLE_MINECART_BRAKE); } - else if (!(TrInput & MINECART_IN_BRAKE) || minecart->Flags & MINECART_FLAG_STOPPED) + else if (!(TrInput & (VEHICLE_IN_BRAKE | VEHICLE_IN_SLOW)) || minecart->Flags & MINECART_FLAG_STOPPED) { laraItem->Animation.TargetState = MINECART_STATE_MOVE; StopSoundEffect(SFX_TR3_VEHICLE_MINECART_BRAKE); } else { - minecart->Velocity -= MINECART_DECELERATION; + minecart->Velocity -= MINECART_VELOCITY_DECEL; SoundEffect(SFX_TR3_VEHICLE_MINECART_BRAKE, &laraItem->Pose, SoundEnvironment::Always); - if (minecartItem->Animation.Velocity > MINECART_TURN_FRICTION_VELOCITY) + if (minecartItem->Animation.Velocity > MINECART_FRICTION_VELOCITY_MIN) { TriggerWheelSparkles(minecartItem, false); TriggerWheelSparkles(minecartItem, true); @@ -797,8 +814,7 @@ namespace TEN::Entities::Vehicles Camera.targetDistance = SECTOR(2); floorHeight = GetMinecartCollision(minecartItem, minecartItem->Pose.Orientation.y, CLICK(2)); - if (floorHeight > -CLICK(1) && - floorHeight < CLICK(1)) + if (abs(floorHeight) < MINECART_STEP_HEIGHT) { if (Wibble & 7 == 0) SoundEffect(SFX_TR3_VEHICLE_QUADBIKE_FRONT_IMPACT, &minecartItem->Pose, SoundEnvironment::Always); @@ -906,59 +922,12 @@ namespace TEN::Entities::Vehicles } if (floorHeight > CLICK(2.25f) && !minecart->VerticalVelocity) - minecart->VerticalVelocity = MINECART_JUMP_VELOCITY; + minecart->VerticalVelocity = MINECART_JUMP_VERTICAL_VELOCITY; - MinecartToEntityCollision(laraItem, minecartItem); + MinecartToEntityCollision(minecartItem, laraItem); } } - void MinecartCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) - { - auto* lara = GetLaraInfo(laraItem); - - if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) - return; - - auto* minecartItem = &g_Level.Items[itemNumber]; - - bool doMount = GetInMinecart(minecartItem, laraItem, coll); - if (doMount) - { - lara->Vehicle = itemNumber; - - if (lara->Control.Weapon.GunType == LaraWeaponType::Flare) - { - CreateFlare(laraItem, ID_FLARE_ITEM, false); - UndrawFlareMeshes(laraItem); - lara->Flare.ControlLeft = false; - lara->Control.Weapon.RequestGunType = LaraWeaponType::None; - lara->Control.Weapon.GunType = LaraWeaponType::None; - } - - lara->Control.HandStatus = HandStatus::Busy; - - short angle = short(mGetAngle(minecartItem->Pose.Position.x, minecartItem->Pose.Position.z, laraItem->Pose.Position.x, laraItem->Pose.Position.z) - minecartItem->Pose.Orientation.y); - if (angle > -ANGLE(45.0f) && angle < ANGLE(135.0f)) - laraItem->Animation.AnimNumber = Objects[ID_MINECART_LARA_ANIMS].animIndex + MINECART_ANIM_MOUNT_RIGHT; - else - laraItem->Animation.AnimNumber = Objects[ID_MINECART_LARA_ANIMS].animIndex + MINECART_ANIM_MOUNT_LEFT; - - laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - laraItem->Animation.ActiveState = MINECART_STATE_MOUNT; - laraItem->Animation.TargetState = MINECART_STATE_MOUNT; - laraItem->Pose = minecartItem->Pose; - - auto* minecart = GetMinecartInfo(minecartItem); - - minecart->Velocity = 0; - minecart->VerticalVelocity = 0; - minecart->Gradient = 0; - minecart->Flags = 0; - } - else - ObjectCollision(itemNumber, laraItem, coll); - } - bool MinecartControl(ItemInfo* laraItem) { auto* lara = GetLaraInfo(laraItem); @@ -971,10 +940,10 @@ namespace TEN::Entities::Vehicles } auto* minecart = GetMinecartInfo(minecartItem); - DoUserInput(minecartItem, laraItem, minecart); + DoUserInput(minecartItem, laraItem); if (minecart->Flags & MINECART_FLAG_CONTROL) - MoveCart(laraItem, minecartItem); + MoveCart(minecartItem, laraItem); if (lara->Vehicle != NO_ITEM) laraItem->Pose = minecartItem->Pose; diff --git a/TombEngine/Objects/TR3/Vehicles/minecart.h b/TombEngine/Objects/TR3/Vehicles/minecart.h index df66017a8..dddf531dd 100644 --- a/TombEngine/Objects/TR3/Vehicles/minecart.h +++ b/TombEngine/Objects/TR3/Vehicles/minecart.h @@ -1,11 +1,15 @@ #pragma once +#include "Objects/Utils/VehicleHelpers.h" -struct ItemInfo; struct CollisionInfo; +struct ItemInfo; namespace TEN::Entities::Vehicles { void InitialiseMinecart(short itemNumber); - void MinecartCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + + void MinecartPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + void DoMinecartMount(ItemInfo* minecartItem, ItemInfo* laraItem, VehicleMountType mountType); + bool MinecartControl(ItemInfo* laraItem); } diff --git a/TombEngine/Objects/TR3/Vehicles/minecart_info.h b/TombEngine/Objects/TR3/Vehicles/minecart_info.h index 67c744ff3..752c61839 100644 --- a/TombEngine/Objects/TR3/Vehicles/minecart_info.h +++ b/TombEngine/Objects/TR3/Vehicles/minecart_info.h @@ -4,19 +4,20 @@ namespace TEN::Entities::Vehicles { struct MinecartInfo { - short TurnRot; - int TurnX; - int TurnZ; - short TurnLen; + int Velocity = 0; + int VerticalVelocity = 0; - int Velocity; - int VerticalVelocity; - int Gradient; - unsigned int StopDelay; + short TurnRot = 0; + int TurnX = 0; + int TurnZ = 0; + short TurnLen = 0; - int FloorHeightMiddle; - int FloorHeightFront; + int Gradient = 0; + unsigned int StopDelay = 0; - char Flags; + int FloorHeightMiddle = 0; + int FloorHeightFront = 0; + + char Flags = NULL; }; } diff --git a/TombEngine/Objects/TR3/Vehicles/quad.cpp b/TombEngine/Objects/TR3/Vehicles/quad.cpp deleted file mode 100644 index 99fc7ecf5..000000000 --- a/TombEngine/Objects/TR3/Vehicles/quad.cpp +++ /dev/null @@ -1,1369 +0,0 @@ -#include "framework.h" -#include "Objects/TR3/Vehicles/quad.h" - -#include "Game/animation.h" -#include "Game/camera.h" -#include "Game/collision/collide_room.h" -#include "Game/collision/collide_item.h" -#include "Game/effects/effects.h" -#include "Game/effects/tomb4fx.h" -#include "Game/items.h" -#include "Game/Lara/lara.h" -#include "Game/Lara/lara_flare.h" -#include "Game/Lara/lara_helpers.h" -#include "Game/Lara/lara_one_gun.h" -#include "Game/misc.h" -#include "Objects/TR3/Vehicles/quad_info.h" -#include "Sound/sound.h" -#include "Specific/level.h" -#include "Specific/input.h" -#include "Specific/setup.h" -#include "Specific/prng.h" -#include "Game/effects/simple_particle.h" - -using namespace TEN::Input; -using namespace TEN::Math::Random; - -namespace TEN::Entities::Vehicles -{ - #define MAX_VELOCITY 0xA000 - #define MIN_DRIFT_VELOCITY 0x3000 - #define BRAKE 0x0280 - #define REVERSE_ACCELERATION -0x0300 - #define MAX_BACK -0x3000 - #define MAX_REVS 0xa000 - #define TERMINAL_VERTICAL_VELOCITY 240 - #define QUAD_SLIP 100 - #define QUAD_SLIP_SIDE 50 - - #define QUAD_FRONT 550 - #define QUAD_BACK -550 - #define QUAD_SIDE 260 - #define QUAD_RADIUS 500 - #define QUAD_HEIGHT 512 - - // TODO - #define QUAD_HIT_LEFT 11 - #define QUAD_HIT_RIGHT 12 - #define QUAD_HIT_FRONT 13 - #define QUAD_HIT_BACK 14 - - #define DAMAGE_START 140 - #define DAMAGE_LENGTH 14 - - #define DISMOUNT_DISTANCE 385 // Precise root bone offset derived from final frame of animation. - - #define QUAD_UNDO_TURN ANGLE(2.0f) - #define QUAD_TURN_RATE (ANGLE(0.5f) + QUAD_UNDO_TURN) - #define QUAD_TURN_MAX ANGLE(5.0f) - #define QUAD_DRIFT_TURN_RATE (ANGLE(0.75f) + QUAD_UNDO_TURN) - #define QUAD_DRIFT_TURN_MAX ANGLE(8.0f) - - #define MIN_MOMENTUM_TURN ANGLE(3.0f) - #define MAX_MOMENTUM_TURN ANGLE(1.5f) - #define QUAD_MAX_MOM_TURN ANGLE(150.0f) - - #define QUAD_MAX_HEIGHT CLICK(1) - #define QUAD_MIN_BOUNCE ((MAX_VELOCITY / 2) / CLICK(1)) - - // TODO: Common controls for all vehicles + unique settings page to set them. @Sezz 2021.11.14 - #define QUAD_IN_ACCELERATE IN_ACTION - #define QUAD_IN_BRAKE IN_JUMP - #define QUAD_IN_DRIFT (IN_CROUCH | IN_SPRINT) - #define QUAD_IN_DISMOUNT IN_ROLL - #define QUAD_IN_LEFT IN_LEFT - #define QUAD_IN_RIGHT IN_RIGHT - - enum QuadState - { - QUAD_STATE_DRIVE = 1, - QUAD_STATE_TURN_LEFT = 2, - QUAD_STATE_SLOW = 5, - QUAD_STATE_BRAKE = 6, - QUAD_STATE_BIKE_DEATH = 7, - QUAD_STATE_FALL = 8, - QUAD_STATE_MOUNT_RIGHT = 9, - QUAD_STATE_DISMOUNT_RIGHT = 10, - QUAD_STATE_HIT_BACK = 11, - QUAD_STATE_HIT_FRONT = 12, - QUAD_STATE_HIT_LEFT = 13, - QUAD_STATE_HIT_RIGHT = 14, - QUAD_STATE_IDLE = 15, - QUAD_STATE_LAND = 17, - QUAD_STATE_STOP_SLOWLY = 18, - QUAD_STATE_FALL_DEATH = 19, - QUAD_STATE_FALL_OFF = 20, - QUAD_STATE_WHEELIE = 21, // Unused. - QUAD_STATE_TURN_RIGHT = 22, - QUAD_STATE_MOUNT_LEFT = 23, - QUAD_STATE_DISMOUNT_LEFT = 24, - }; - - enum QuadAnim - { - QUAD_ANIM_IDLE_DEATH = 0, - QUAD_ANIM_UNK_1 = 1, - QUAD_ANIM_DRIVE_BACK = 2, - QUAD_ANIM_TURN_LEFT_START = 3, - QUAD_ANIM_TURN_LEFT_CONTINUE = 4, - QUAD_ANIM_TURN_LEFT_END = 5, - QUAD_ANIM_LEAP_START = 6, - QUAD_ANIM_LEAP_CONTINUE = 7, - QUAD_ANIM_LEAP_END = 8, - QUAD_ANIM_MOUNT_RIGHT = 9, - QUAD_ANIM_DISMOUNT_RIGHT = 10, - QUAD_ANIM_HIT_FRONT = 11, - QUAD_ANIM_HIT_BACK = 12, - QUAD_ANIM_HIT_RIGHT = 13, - QUAD_ANIM_HIT_LEFT = 14, - QUAD_ANIM_UNK_2 = 15, - QUAD_ANIM_UNK_3 = 16, - QUAD_ANIM_UNK_4 = 17, - QUAD_ANIM_IDLE = 18, - QUAD_ANIM_FALL_OFF_DEATH = 19, - QUAD_ANIM_TURN_RIGHT_START = 20, - QUAD_ANIM_TURN_RIGHT_CONTINUE = 21, - QUAD_ANIM_TURN_RIGHT_END = 22, - QUAD_ANIM_MOUNT_LEFT = 23, - QUAD_ANIM_DISMOUNT_LEFT = 24, - QUAD_ANIM_LEAP_START2 = 25, - QUAD_ANIM_LEAP_CONTINUE2 = 26, - QUAD_ANIM_LEAP_END2 = 27, - QUAD_ANIM_LEAP_TO_FREEFALL = 28 - }; - - enum QuadEffectPosition - { - EXHAUST_LEFT = 0, - EXHAUST_RIGHT = 1, - FRONT_LEFT_TYRE = 2, - FRONT_RIGHT_TYRE = 3, - BACK_LEFT_TYRE = 4, - BACK_RIGHT_TYRE = 5 - }; - - enum QuadFlags - { - QUAD_FLAG_DEAD = 0x80, - QUAD_FLAG_IS_FALLING = 0x40 - }; - - BITE_INFO quadEffectsPositions[6] = - { - { -56, -32, -380, 0 }, - { 56, -32, -380, 0 }, - { -8, 180, -48, 3 }, - { 8, 180, -48, 4 }, - { 90, 180, -32, 6 }, - { -90, 180, -32, 7 } - }; - - void InitialiseQuadBike(short itemNumber) - { - auto* quadItem = &g_Level.Items[itemNumber]; - quadItem->Data = QuadInfo(); - auto* quad = (QuadInfo*)quadItem->Data; - - quad->TurnRate = 0; - quad->MomentumAngle = quadItem->Pose.Orientation.y; - quad->ExtraRotation = 0; - quad->Velocity = 0; - quad->LeftVerticalVelocity = 0; - quad->RightVerticalVelocity = 0; - quad->Pitch = 0; - quad->Flags = 0; - } - - static int CanQuadbikeGetOff(int direction) - { - auto* item = &g_Level.Items[Lara.Vehicle]; - short angle; - - if (direction < 0) - angle = item->Pose.Orientation.y - ANGLE(90.0f); - else - angle = item->Pose.Orientation.y + ANGLE(90.0f); - - int x = item->Pose.Position.x + CLICK(2) * phd_sin(angle); - int y = item->Pose.Position.y; - int z = item->Pose.Position.z + CLICK(2) * phd_cos(angle); - - auto collResult = GetCollision(x, y, z, item->RoomNumber); - - if (collResult.Position.FloorSlope || - collResult.Position.Floor == NO_HEIGHT) - { - return false; - } - - if (abs(collResult.Position.Floor - item->Pose.Position.y) > CLICK(2)) - return false; - - if ((collResult.Position.Ceiling - item->Pose.Position.y) > -LARA_HEIGHT || - (collResult.Position.Floor - collResult.Position.Ceiling) < LARA_HEIGHT) - { - return false; - } - - return true; - } - - static bool QuadCheckGetOff(ItemInfo* laraItem, ItemInfo* quadItem) - { - auto* lara = GetLaraInfo(laraItem); - auto* quad = (QuadInfo*)quadItem->Data; - - if (lara->Vehicle == NO_ITEM) - return true; - - if ((laraItem->Animation.ActiveState == QUAD_STATE_DISMOUNT_RIGHT || laraItem->Animation.ActiveState == QUAD_STATE_DISMOUNT_LEFT) && - TestLastFrame(laraItem)) - { - if (laraItem->Animation.ActiveState == QUAD_STATE_DISMOUNT_LEFT) - laraItem->Pose.Orientation.y += ANGLE(90.0f); - else - laraItem->Pose.Orientation.y -= ANGLE(90.0f); - - SetAnimation(laraItem, LA_STAND_IDLE); - TranslateItem(laraItem, laraItem->Pose.Orientation.y, -DISMOUNT_DISTANCE); - laraItem->Pose.Orientation.x = 0; - laraItem->Pose.Orientation.z = 0; - lara->Vehicle = NO_ITEM; - lara->Control.HandStatus = HandStatus::Free; - - if (laraItem->Animation.ActiveState == QUAD_STATE_FALL_OFF) - { - auto pos = Vector3Int(); - - SetAnimation(laraItem, LA_FREEFALL); - GetJointAbsPosition(laraItem, &pos, LM_HIPS); - - laraItem->Pose.Position = pos; - laraItem->Animation.Airborne = true; - laraItem->Animation.VerticalVelocity = quadItem->Animation.VerticalVelocity; - laraItem->Pose.Orientation.x = 0; - laraItem->Pose.Orientation.z = 0; - laraItem->HitPoints = 0; - lara->Control.HandStatus = HandStatus::Free; - quadItem->Flags |= ONESHOT; - - return false; - } - else if (laraItem->Animation.ActiveState == QUAD_STATE_FALL_DEATH) - { - laraItem->Animation.TargetState = LS_DEATH; - laraItem->Animation.Velocity = 0; - laraItem->Animation.VerticalVelocity = DAMAGE_START + DAMAGE_LENGTH; - quad->Flags |= QUAD_FLAG_DEAD; - - return false; - } - - return true; - } - else - return true; - } - - static int GetOnQuadBike(ItemInfo* laraItem, ItemInfo* quadItem, CollisionInfo* coll) - { - auto* lara = GetLaraInfo(laraItem); - - if (!(TrInput & IN_ACTION) || - laraItem->Animation.Airborne || - lara->Control.HandStatus != HandStatus::Free || - quadItem->Flags & ONESHOT || - abs(quadItem->Pose.Position.y - laraItem->Pose.Position.y) > CLICK(1)) - { - return false; - } - - auto dist = pow(laraItem->Pose.Position.x - quadItem->Pose.Position.x, 2) + pow(laraItem->Pose.Position.z - quadItem->Pose.Position.z, 2); - if (dist > 170000) - return false; - - auto probe = GetCollision(quadItem); - if (probe.Position.Floor < -32000) - return false; - else - { - short angle = phd_atan(quadItem->Pose.Position.z - laraItem->Pose.Position.z, quadItem->Pose.Position.x - laraItem->Pose.Position.x); - angle -= quadItem->Pose.Orientation.y; - - if ((angle > -ANGLE(45.0f)) && (angle < ANGLE(135.0f))) - { - short tempAngle = laraItem->Pose.Orientation.y - quadItem->Pose.Orientation.y; - if (tempAngle > ANGLE(45.0f) && tempAngle < ANGLE(135.0f)) - return true; - else - return false; - } - else - { - short tempAngle = laraItem->Pose.Orientation.y - quadItem->Pose.Orientation.y; - if (tempAngle > ANGLE(225.0f) && tempAngle < ANGLE(315.0f)) - return true; - else - return false; - } - } - - return true; - } - - static int GetQuadCollisionAnim(ItemInfo* quadItem, Vector3Int* p) - { - p->x = quadItem->Pose.Position.x - p->x; - p->z = quadItem->Pose.Position.z - p->z; - - if (p->x || p->z) - { - float c = phd_cos(quadItem->Pose.Orientation.y); - float s = phd_sin(quadItem->Pose.Orientation.y); - int front = p->z * c + p->x * s; - int side = -p->z * s + p->x * c; - - if (abs(front) > abs(side)) - { - if (front > 0) - return QUAD_HIT_BACK; - else - return QUAD_HIT_FRONT; - } - else - { - if (side > 0) - return QUAD_HIT_LEFT; - else - return QUAD_HIT_RIGHT; - } - } - - return 0; - } - - static int TestQuadHeight(ItemInfo* quadItem, int dz, int dx, Vector3Int* pos) - { - pos->y = quadItem->Pose.Position.y - dz * phd_sin(quadItem->Pose.Orientation.x) + dx * phd_sin(quadItem->Pose.Orientation.z); - - float c = phd_cos(quadItem->Pose.Orientation.y); - float s = phd_sin(quadItem->Pose.Orientation.y); - - pos->z = quadItem->Pose.Position.z + dz * c - dx * s; - pos->x = quadItem->Pose.Position.x + dz * s + dx * c; - - auto probe = GetCollision(pos->x, pos->y, pos->z, quadItem->RoomNumber); - if (probe.Position.Ceiling > pos->y || - probe.Position.Ceiling == NO_HEIGHT) - { - return NO_HEIGHT; - } - - return probe.Position.Floor; - } - - static int DoQuadShift(ItemInfo* quadItem, Vector3Int* pos, Vector3Int* old) - { - CollisionResult probe; - int x = pos->x / SECTOR(1); - int z = pos->z / SECTOR(1); - int oldX = old->x / SECTOR(1); - int oldZ = old->z / SECTOR(1); - int shiftX = pos->x & (SECTOR(1) - 1); - int shiftZ = pos->z & (SECTOR(1) - 1); - - if (x == oldX) - { - if (z == oldZ) - { - quadItem->Pose.Position.z += (old->z - pos->z); - quadItem->Pose.Position.x += (old->x - pos->x); - } - else if (z > oldZ) - { - quadItem->Pose.Position.z -= shiftZ + 1; - return (pos->x - quadItem->Pose.Position.x); - } - else - { - quadItem->Pose.Position.z += SECTOR(1) - shiftZ; - return (quadItem->Pose.Position.x - pos->x); - } - } - else if (z == oldZ) - { - if (x > oldX) - { - quadItem->Pose.Position.x -= shiftX + 1; - return (quadItem->Pose.Position.z - pos->z); - } - else - { - quadItem->Pose.Position.x += SECTOR(1) - shiftX; - return (pos->z - quadItem->Pose.Position.z); - } - } - else - { - x = 0; - z = 0; - - probe = GetCollision(old->x, pos->y, pos->z, quadItem->RoomNumber); - if (probe.Position.Floor < (old->y - CLICK(1))) - { - if (pos->z > old->z) - z = -shiftZ - 1; - else - z = SECTOR(1) - shiftZ; - } - - probe = GetCollision(pos->x, pos->y, old->z, quadItem->RoomNumber); - if (probe.Position.Floor < (old->y - CLICK(1))) - { - if (pos->x > old->x) - x = -shiftX - 1; - else - x = SECTOR(1) - shiftX; - } - - if (x && z) - { - quadItem->Pose.Position.z += z; - quadItem->Pose.Position.x += x; - } - else if (z) - { - quadItem->Pose.Position.z += z; - - if (z > 0) - return (quadItem->Pose.Position.x - pos->x); - else - return (pos->x - quadItem->Pose.Position.x); - } - else if (x) - { - quadItem->Pose.Position.x += x; - - if (x > 0) - return (pos->z - quadItem->Pose.Position.z); - else - return (quadItem->Pose.Position.z - pos->z); - } - else - { - quadItem->Pose.Position.z += (old->z - pos->z); - quadItem->Pose.Position.x += (old->x - pos->x); - } - } - - return 0; - } - - static int DoQuadDynamics(int height, int verticalVelocity, int* y) - { - if (height > *y) - { - *y += verticalVelocity; - if (*y > height - QUAD_MIN_BOUNCE) - { - *y = height; - verticalVelocity = 0; - } - else - verticalVelocity += 6; - } - else - { - int kick = (height - *y) * 4; - if (kick < -80) - kick = -80; - - verticalVelocity += ((kick - verticalVelocity) / 8); - - if (*y > height) - *y = height; - } - - return verticalVelocity; - } - - static int QuadDynamics(ItemInfo* laraItem, ItemInfo* quadItem) - { - auto* lara = GetLaraInfo(laraItem); - auto* quad = (QuadInfo*)quadItem->Data; - - quad->NoDismount = false; - - Vector3Int oldFrontLeft, oldFrontRight, oldBottomLeft, oldBottomRight; - int holdFrontLeft = TestQuadHeight(quadItem, QUAD_FRONT, -QUAD_SIDE, &oldFrontLeft); - int holdFrontRight = TestQuadHeight(quadItem, QUAD_FRONT, QUAD_SIDE, &oldFrontRight); - int holdBottomLeft = TestQuadHeight(quadItem, -QUAD_FRONT, -QUAD_SIDE, &oldBottomLeft); - int holdBottomRight = TestQuadHeight(quadItem, -QUAD_FRONT, QUAD_SIDE, &oldBottomRight); - - Vector3Int mtlOld, mtrOld, mmlOld, mmrOld; - int hmml_old = TestQuadHeight(quadItem, 0, -QUAD_SIDE, &mmlOld); - int hmmr_old = TestQuadHeight(quadItem, 0, QUAD_SIDE, &mmrOld); - int hmtl_old = TestQuadHeight(quadItem, QUAD_FRONT / 2, -QUAD_SIDE, &mtlOld); - int hmtr_old = TestQuadHeight(quadItem, QUAD_FRONT / 2, QUAD_SIDE, &mtrOld); - - Vector3Int moldBottomLeft, moldBottomRight; - int hmoldBottomLeft = TestQuadHeight(quadItem, -QUAD_FRONT / 2, -QUAD_SIDE, &moldBottomLeft); - int hmoldBottomRight = TestQuadHeight(quadItem, -QUAD_FRONT / 2, QUAD_SIDE, &moldBottomRight); - - Vector3Int old; - old.x = quadItem->Pose.Position.x; - old.y = quadItem->Pose.Position.y; - old.z = quadItem->Pose.Position.z; - - if (oldBottomLeft.y > holdBottomLeft) - oldBottomLeft.y = holdBottomLeft; - - if (oldBottomRight.y > holdBottomRight) - oldBottomRight.y = holdBottomRight; - - if (oldFrontLeft.y > holdFrontLeft) - oldFrontLeft.y = holdFrontLeft; - - if (oldFrontRight.y > holdFrontRight) - oldFrontRight.y = holdFrontRight; - - if (moldBottomLeft.y > hmoldBottomLeft) - moldBottomLeft.y = hmoldBottomLeft; - - if (moldBottomRight.y > hmoldBottomRight) - moldBottomRight.y = hmoldBottomRight; - - if (mtlOld.y > hmtl_old) - mtlOld.y = hmtl_old; - - if (mtrOld.y > hmtr_old) - mtrOld.y = hmtr_old; - - if (mmlOld.y > hmml_old) - mmlOld.y = hmml_old; - - if (mmrOld.y > hmmr_old) - mmrOld.y = hmmr_old; - - if (quadItem->Pose.Position.y > (quadItem->Floor - CLICK(1))) - { - if (quad->TurnRate < -QUAD_UNDO_TURN) - quad->TurnRate += QUAD_UNDO_TURN; - else if (quad->TurnRate > QUAD_UNDO_TURN) - quad->TurnRate -= QUAD_UNDO_TURN; - else - quad->TurnRate = 0; - - quadItem->Pose.Orientation.y += quad->TurnRate + quad->ExtraRotation; - - short momentum = MIN_MOMENTUM_TURN - (((((MIN_MOMENTUM_TURN - MAX_MOMENTUM_TURN) * 256) / MAX_VELOCITY) * quad->Velocity) / 256); - if (!(TrInput & QUAD_IN_ACCELERATE) && quad->Velocity > 0) - momentum += momentum / 4; - - short rot = quadItem->Pose.Orientation.y - quad->MomentumAngle; - if (rot < -MAX_MOMENTUM_TURN) - { - if (rot < -QUAD_MAX_MOM_TURN) - { - rot = -QUAD_MAX_MOM_TURN; - quad->MomentumAngle = quadItem->Pose.Orientation.y - rot; - } - else - quad->MomentumAngle -= momentum; - } - else if (rot > MAX_MOMENTUM_TURN) - { - if (rot > QUAD_MAX_MOM_TURN) - { - rot = QUAD_MAX_MOM_TURN; - quad->MomentumAngle = quadItem->Pose.Orientation.y - rot; - } - else - quad->MomentumAngle += momentum; - } - else - quad->MomentumAngle = quadItem->Pose.Orientation.y; - } - else - quadItem->Pose.Orientation.y += quad->TurnRate + quad->ExtraRotation; - - auto probe = GetCollision(quadItem); - int speed = 0; - if (quadItem->Pose.Position.y >= probe.Position.Floor) - speed = quadItem->Animation.Velocity * phd_cos(quadItem->Pose.Orientation.x); - else - speed = quadItem->Animation.Velocity; - - TranslateItem(quadItem, quad->MomentumAngle, speed); - - int slip = QUAD_SLIP * phd_sin(quadItem->Pose.Orientation.x); - if (abs(slip) > QUAD_SLIP / 2) - { - if (slip > 0) - slip -= 10; - else - slip += 10; - quadItem->Pose.Position.z -= slip * phd_cos(quadItem->Pose.Orientation.y); - quadItem->Pose.Position.x -= slip * phd_sin(quadItem->Pose.Orientation.y); - } - - slip = QUAD_SLIP_SIDE * phd_sin(quadItem->Pose.Orientation.z); - if (abs(slip) > QUAD_SLIP_SIDE / 2) - { - quadItem->Pose.Position.z -= slip * phd_sin(quadItem->Pose.Orientation.y); - quadItem->Pose.Position.x += slip * phd_cos(quadItem->Pose.Orientation.y); - } - - Vector3Int moved; - moved.x = quadItem->Pose.Position.x; - moved.z = quadItem->Pose.Position.z; - - if (!(quadItem->Flags & ONESHOT)) - DoVehicleCollision(quadItem, QUAD_RADIUS); - - short rot = 0; - short rotAdd = 0; - - Vector3Int fl; - int heightFrontLeft = TestQuadHeight(quadItem, QUAD_FRONT, -QUAD_SIDE, &fl); - if (heightFrontLeft < (oldFrontLeft.y - CLICK(1))) - rot = DoQuadShift(quadItem, &fl, &oldFrontLeft); - - Vector3Int mtl; - int hmtl = TestQuadHeight(quadItem, QUAD_FRONT / 2, -QUAD_SIDE, &mtl); - if (hmtl < (mtlOld.y - CLICK(1))) - DoQuadShift(quadItem, &mtl, &mtlOld); - - Vector3Int mml; - int hmml = TestQuadHeight(quadItem, 0, -QUAD_SIDE, &mml); - if (hmml < (mmlOld.y - CLICK(1))) - DoQuadShift(quadItem, &mml, &mmlOld); - - Vector3Int mbl; - int hmbl = TestQuadHeight(quadItem, -QUAD_FRONT / 2, -QUAD_SIDE, &mbl); - if (hmbl < (moldBottomLeft.y - CLICK(1))) - DoQuadShift(quadItem, &mbl, &moldBottomLeft); - - Vector3Int bl; - int heightBackLeft = TestQuadHeight(quadItem, -QUAD_FRONT, -QUAD_SIDE, &bl); - if (heightBackLeft < (oldBottomLeft.y - CLICK(1))) - { - rotAdd = DoQuadShift(quadItem, &bl, &oldBottomLeft); - if ((rotAdd > 0 && rot >= 0) || (rotAdd < 0 && rot <= 0)) - rot += rotAdd; - } - - Vector3Int fr; - int heightFrontRight = TestQuadHeight(quadItem, QUAD_FRONT, QUAD_SIDE, &fr); - if (heightFrontRight < (oldFrontRight.y - CLICK(1))) - { - rotAdd = DoQuadShift(quadItem, &fr, &oldFrontRight); - if ((rotAdd > 0 && rot >= 0) || (rotAdd < 0 && rot <= 0)) - rot += rotAdd; - } - - Vector3Int mtr; - int hmtr = TestQuadHeight(quadItem, QUAD_FRONT / 2, QUAD_SIDE, &mtr); - if (hmtr < (mtrOld.y - CLICK(1))) - DoQuadShift(quadItem, &mtr, &mtrOld); - - Vector3Int mmr; - int hmmr = TestQuadHeight(quadItem, 0, QUAD_SIDE, &mmr); - if (hmmr < (mmrOld.y - CLICK(1))) - DoQuadShift(quadItem, &mmr, &mmrOld); - - Vector3Int mbr; - int hmbr = TestQuadHeight(quadItem, -QUAD_FRONT / 2, QUAD_SIDE, &mbr); - if (hmbr < (moldBottomRight.y - CLICK(1))) - DoQuadShift(quadItem, &mbr, &moldBottomRight); - - Vector3Int br; - int heightBackRight = TestQuadHeight(quadItem, -QUAD_FRONT, QUAD_SIDE, &br); - if (heightBackRight < (oldBottomRight.y - CLICK(1))) - { - rotAdd = DoQuadShift(quadItem, &br, &oldBottomRight); - if ((rotAdd > 0 && rot >= 0) || (rotAdd < 0 && rot <= 0)) - rot += rotAdd; - } - - probe = GetCollision(quadItem); - if (probe.Position.Floor < quadItem->Pose.Position.y - CLICK(1)) - DoQuadShift(quadItem, (Vector3Int*)&quadItem->Pose, &old); - - quad->ExtraRotation = rot; - - int collide = GetQuadCollisionAnim(quadItem, &moved); - - int newVelocity = 0; - if (collide) - { - newVelocity = (quadItem->Pose.Position.z - old.z) * phd_cos(quad->MomentumAngle) + (quadItem->Pose.Position.x - old.x) * phd_sin(quad->MomentumAngle); - newVelocity *= 256; - - if (&g_Level.Items[lara->Vehicle] == quadItem && - quad->Velocity == MAX_VELOCITY && - newVelocity < (quad->Velocity - 10)) - { - DoDamage(laraItem, (quad->Velocity - newVelocity) / 128); - } - - if (quad->Velocity > 0 && newVelocity < quad->Velocity) - quad->Velocity = (newVelocity < 0) ? 0 : newVelocity; - - else if (quad->Velocity < 0 && newVelocity > quad->Velocity) - quad->Velocity = (newVelocity > 0) ? 0 : newVelocity; - - if (quad->Velocity < MAX_BACK) - quad->Velocity = MAX_BACK; - } - - return collide; - } - - static void AnimateQuadBike(ItemInfo* laraItem, ItemInfo* quadItem, int collide, bool dead) - { - auto* quad = (QuadInfo*)quadItem->Data; - - if (quadItem->Pose.Position.y != quadItem->Floor && - laraItem->Animation.ActiveState != QUAD_STATE_FALL && - laraItem->Animation.ActiveState != QUAD_STATE_LAND && - laraItem->Animation.ActiveState != QUAD_STATE_FALL_OFF && - !dead) - { - if (quad->Velocity < 0) - laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_LEAP_START; - else - laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_LEAP_START2; - - laraItem->Animation.FrameNumber = GetFrameNumber(laraItem, laraItem->Animation.AnimNumber); - laraItem->Animation.ActiveState = QUAD_STATE_FALL; - laraItem->Animation.TargetState = QUAD_STATE_FALL; - } - else if (collide && - laraItem->Animation.ActiveState != QUAD_STATE_HIT_FRONT && - laraItem->Animation.ActiveState != QUAD_STATE_HIT_BACK && - laraItem->Animation.ActiveState != QUAD_STATE_HIT_LEFT && - laraItem->Animation.ActiveState != QUAD_STATE_HIT_RIGHT && - laraItem->Animation.ActiveState != QUAD_STATE_FALL_OFF && - quad->Velocity > (MAX_VELOCITY / 3) && - !dead) - { - if (collide == QUAD_HIT_FRONT) - { - laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_HIT_BACK; - laraItem->Animation.ActiveState = QUAD_STATE_HIT_FRONT; - laraItem->Animation.TargetState = QUAD_STATE_HIT_FRONT; - } - else if (collide == QUAD_HIT_BACK) - { - laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_HIT_FRONT; - laraItem->Animation.ActiveState = QUAD_STATE_HIT_BACK; - laraItem->Animation.TargetState = QUAD_STATE_HIT_BACK; - } - else if (collide == QUAD_HIT_LEFT) - { - laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_HIT_RIGHT; - laraItem->Animation.ActiveState = QUAD_STATE_HIT_LEFT; - laraItem->Animation.TargetState = QUAD_STATE_HIT_LEFT; - } - else - { - laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_HIT_LEFT; - laraItem->Animation.ActiveState = QUAD_STATE_HIT_RIGHT; - laraItem->Animation.TargetState = QUAD_STATE_HIT_RIGHT; - } - - laraItem->Animation.FrameNumber = GetFrameNumber(laraItem, laraItem->Animation.AnimNumber); - SoundEffect(SFX_TR3_VEHICLE_QUADBIKE_FRONT_IMPACT, &quadItem->Pose); - } - else - { - switch (laraItem->Animation.ActiveState) - { - case QUAD_STATE_IDLE: - if (dead) - laraItem->Animation.TargetState = QUAD_STATE_BIKE_DEATH; - else if (TrInput & QUAD_IN_DISMOUNT && - quad->Velocity == 0 && - !quad->NoDismount) - { - if (TrInput & QUAD_IN_LEFT && CanQuadbikeGetOff(-1)) - laraItem->Animation.TargetState = QUAD_STATE_DISMOUNT_LEFT; - else if (TrInput & QUAD_IN_RIGHT && CanQuadbikeGetOff(1)) - laraItem->Animation.TargetState = QUAD_STATE_DISMOUNT_RIGHT; - } - else if (TrInput & (QUAD_IN_ACCELERATE | QUAD_IN_BRAKE)) - laraItem->Animation.TargetState = QUAD_STATE_DRIVE; - - break; - - case QUAD_STATE_DRIVE: - if (dead) - { - if (quad->Velocity > (MAX_VELOCITY / 2)) - laraItem->Animation.TargetState = QUAD_STATE_FALL_DEATH; - else - laraItem->Animation.TargetState = QUAD_STATE_BIKE_DEATH; - } - else if (!(TrInput & (QUAD_IN_ACCELERATE | QUAD_IN_BRAKE)) && - (quad->Velocity / 256) == 0) - { - laraItem->Animation.TargetState = QUAD_STATE_IDLE; - } - else if (TrInput & QUAD_IN_LEFT && - !quad->DriftStarting) - { - laraItem->Animation.TargetState = QUAD_STATE_TURN_LEFT; - } - else if (TrInput & QUAD_IN_RIGHT && - !quad->DriftStarting) - { - laraItem->Animation.TargetState = QUAD_STATE_TURN_RIGHT; - } - else if (TrInput & QUAD_IN_BRAKE) - { - if (quad->Velocity > (MAX_VELOCITY / 3 * 2)) - laraItem->Animation.TargetState = QUAD_STATE_BRAKE; - else - laraItem->Animation.TargetState = QUAD_STATE_SLOW; - } - - break; - - case QUAD_STATE_BRAKE: - case QUAD_STATE_SLOW: - case QUAD_STATE_STOP_SLOWLY: - if ((quad->Velocity / 256) == 0) - laraItem->Animation.TargetState = QUAD_STATE_IDLE; - else if (TrInput & QUAD_IN_LEFT) - laraItem->Animation.TargetState = QUAD_STATE_TURN_LEFT; - else if (TrInput & QUAD_IN_RIGHT) - laraItem->Animation.TargetState = QUAD_STATE_TURN_RIGHT; - - break; - - case QUAD_STATE_TURN_LEFT: - if ((quad->Velocity / 256) == 0) - laraItem->Animation.TargetState = QUAD_STATE_IDLE; - else if (TrInput & QUAD_IN_RIGHT) - { - laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_TURN_RIGHT_START; - laraItem->Animation.FrameNumber = GetFrameNumber(laraItem, laraItem->Animation.AnimNumber); - laraItem->Animation.ActiveState = QUAD_STATE_TURN_RIGHT; - laraItem->Animation.TargetState = QUAD_STATE_TURN_RIGHT; - } - else if (!(TrInput & QUAD_IN_LEFT)) - laraItem->Animation.TargetState = QUAD_STATE_DRIVE; - - break; - - case QUAD_STATE_TURN_RIGHT: - if ((quad->Velocity / 256) == 0) - laraItem->Animation.TargetState = QUAD_STATE_IDLE; - else if (TrInput & QUAD_IN_LEFT) - { - laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_TURN_LEFT_START; - laraItem->Animation.FrameNumber = GetFrameNumber(laraItem, laraItem->Animation.AnimNumber); - laraItem->Animation.ActiveState = QUAD_STATE_TURN_LEFT; - laraItem->Animation.TargetState = QUAD_STATE_TURN_LEFT; - } - else if (!(TrInput & QUAD_IN_RIGHT)) - laraItem->Animation.TargetState = QUAD_STATE_DRIVE; - - break; - - case QUAD_STATE_FALL: - if (quadItem->Pose.Position.y == quadItem->Floor) - laraItem->Animation.TargetState = QUAD_STATE_LAND; - else if (quadItem->Animation.VerticalVelocity > TERMINAL_VERTICAL_VELOCITY) - quad->Flags |= QUAD_FLAG_IS_FALLING; - - break; - - case QUAD_STATE_FALL_OFF: - break; - - case QUAD_STATE_HIT_FRONT: - case QUAD_STATE_HIT_BACK: - case QUAD_STATE_HIT_LEFT: - case QUAD_STATE_HIT_RIGHT: - if (TrInput & (QUAD_IN_ACCELERATE | QUAD_IN_BRAKE)) - laraItem->Animation.TargetState = QUAD_STATE_DRIVE; - - break; - } - } - } - - static int QuadUserControl(ItemInfo* quadItem, int height, int* pitch) - { - auto* quad = (QuadInfo*)quadItem->Data; - - bool drive = false; // Never changes? - - if (!(TrInput & QUAD_IN_DRIFT) && - !quad->Velocity && !quad->CanStartDrift) - { - quad->CanStartDrift = true; - } - else if (quad->Velocity) - quad->CanStartDrift = false; - - if (!(TrInput & QUAD_IN_DRIFT)) - quad->DriftStarting = false; - - if (!quad->DriftStarting) - { - if (quad->Revs > 0x10) - { - quad->Velocity += (quad->Revs / 16); - quad->Revs -= (quad->Revs / 8); - } - else - quad->Revs = 0; - } - - if (quadItem->Pose.Position.y >= (height - CLICK(1))) - { - if (TrInput & IN_LOOK && !quad->Velocity) - LookUpDown(LaraItem); - - // Driving forward. - if (quad->Velocity > 0) - { - if (TrInput & QUAD_IN_DRIFT && - !quad->DriftStarting && - quad->Velocity > MIN_DRIFT_VELOCITY) - { - if (TrInput & QUAD_IN_LEFT) - { - quad->TurnRate -= QUAD_DRIFT_TURN_RATE; - if (quad->TurnRate < -QUAD_DRIFT_TURN_MAX) - quad->TurnRate = -QUAD_DRIFT_TURN_MAX; - } - else if (TrInput & QUAD_IN_RIGHT) - { - quad->TurnRate += QUAD_DRIFT_TURN_RATE; - if (quad->TurnRate > QUAD_DRIFT_TURN_MAX) - quad->TurnRate = QUAD_DRIFT_TURN_MAX; - } - } - else - { - if (TrInput & QUAD_IN_LEFT) - { - quad->TurnRate -= QUAD_TURN_RATE; - if (quad->TurnRate < -QUAD_TURN_MAX) - quad->TurnRate = -QUAD_TURN_MAX; - } - else if (TrInput & QUAD_IN_RIGHT) - { - quad->TurnRate += QUAD_TURN_RATE; - if (quad->TurnRate > QUAD_TURN_MAX) - quad->TurnRate = QUAD_TURN_MAX; - } - } - } - // Driving back. - else if (quad->Velocity < 0) - { - if (TrInput & QUAD_IN_DRIFT && - !quad->DriftStarting && - quad->Velocity < (-MIN_DRIFT_VELOCITY + 0x800)) - { - if (TrInput & QUAD_IN_LEFT) - { - quad->TurnRate -= QUAD_DRIFT_TURN_RATE; - if (quad->TurnRate < -QUAD_DRIFT_TURN_MAX) - quad->TurnRate = -QUAD_DRIFT_TURN_MAX; - } - else if (TrInput & QUAD_IN_RIGHT) - { - quad->TurnRate += QUAD_DRIFT_TURN_RATE; - if (quad->TurnRate > QUAD_DRIFT_TURN_MAX) - quad->TurnRate = QUAD_DRIFT_TURN_MAX; - } - } - else - { - if (TrInput & QUAD_IN_RIGHT) - { - quad->TurnRate -= QUAD_TURN_RATE; - if (quad->TurnRate < -QUAD_TURN_MAX) - quad->TurnRate = -QUAD_TURN_MAX; - } - else if (TrInput & QUAD_IN_LEFT) - { - quad->TurnRate += QUAD_TURN_RATE; - if (quad->TurnRate > QUAD_TURN_MAX) - quad->TurnRate = QUAD_TURN_MAX; - } - } - } - - // Driving back / braking. - if (TrInput & QUAD_IN_BRAKE) - { - if (TrInput & QUAD_IN_DRIFT && - (quad->CanStartDrift || quad->DriftStarting)) - { - quad->DriftStarting = true; - quad->Revs -= 0x200; - if (quad->Revs < MAX_BACK) - quad->Revs = MAX_BACK; - } - else if (quad->Velocity > 0) - quad->Velocity -= BRAKE; - else - { - if (quad->Velocity > MAX_BACK) - quad->Velocity += REVERSE_ACCELERATION; - } - } - else if (TrInput & QUAD_IN_ACCELERATE) - { - if (TrInput & QUAD_IN_DRIFT && - (quad->CanStartDrift || quad->DriftStarting)) - { - quad->DriftStarting = true; - quad->Revs += 0x200; - if (quad->Revs >= MAX_VELOCITY) - quad->Revs = MAX_VELOCITY; - } - else if (quad->Velocity < MAX_VELOCITY) - { - if (quad->Velocity < 0x4000) - quad->Velocity += (8 + (0x4000 + 0x800 - quad->Velocity) / 8); - else if (quad->Velocity < 0x7000) - quad->Velocity += (4 + (0x7000 + 0x800 - quad->Velocity) / 16); - else if (quad->Velocity < MAX_VELOCITY) - quad->Velocity += (2 + (MAX_VELOCITY - quad->Velocity) / 8); - } - else - quad->Velocity = MAX_VELOCITY; - - quad->Velocity -= abs(quadItem->Pose.Orientation.y - quad->MomentumAngle) / 64; - } - - else if (quad->Velocity > 0x0100) - quad->Velocity -= 0x0100; - else if (quad->Velocity < -0x0100) - quad->Velocity += 0x0100; - else - quad->Velocity = 0; - - if (!(TrInput & (QUAD_IN_ACCELERATE | QUAD_IN_BRAKE)) && - quad->DriftStarting && - quad->Revs) - { - if (quad->Revs > 0x8) - quad->Revs -= quad->Revs / 8; - else - quad->Revs = 0; - } - - quadItem->Animation.Velocity = quad->Velocity / 256; - - if (quad->EngineRevs > 0x7000) - quad->EngineRevs = -0x2000; - - int revs = 0; - if (quad->Velocity < 0) - revs = abs(quad->Velocity / 2); - else if (quad->Velocity < 0x7000) - revs = -0x2000 + (quad->Velocity * (0x6800 - -0x2000)) / 0x7000; - else if (quad->Velocity <= MAX_VELOCITY) - revs = -0x2800 + ((quad->Velocity - 0x7000) * (0x7000 - -0x2800)) / (MAX_VELOCITY - 0x7000); - - revs += abs(quad->Revs); - quad->EngineRevs += (revs - quad->EngineRevs) / 8; - } - else - { - if (quad->EngineRevs < 0xA000) - quad->EngineRevs += (0xA000 - quad->EngineRevs) / 8; - } - - *pitch = quad->EngineRevs; - - return drive; - } - - void QuadBikeCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) - { - auto* lara = GetLaraInfo(laraItem); - auto* quadItem = &g_Level.Items[itemNumber]; - auto* quad = (QuadInfo*)quadItem->Data; - - if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) - return; - - if (GetOnQuadBike(laraItem, &g_Level.Items[itemNumber], coll)) - { - lara->Vehicle = itemNumber; - - if (lara->Control.Weapon.GunType == LaraWeaponType::Flare) - { - CreateFlare(laraItem, ID_FLARE_ITEM, 0); - UndrawFlareMeshes(laraItem); - lara->Flare.ControlLeft = false; - lara->Control.Weapon.RequestGunType = lara->Control.Weapon.GunType = LaraWeaponType::None; - } - - lara->Control.HandStatus = HandStatus::Busy; - - short angle = phd_atan(quadItem->Pose.Position.z - laraItem->Pose.Position.z, quadItem->Pose.Position.x - laraItem->Pose.Position.x); - angle -= quadItem->Pose.Orientation.y; - - if (angle > -ANGLE(45.0f) && angle < ANGLE(135.0f)) - { - laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_MOUNT_LEFT; - laraItem->Animation.ActiveState = laraItem->Animation.TargetState = QUAD_STATE_MOUNT_LEFT; - } - else - { - laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QUAD_ANIM_MOUNT_RIGHT; - laraItem->Animation.ActiveState = laraItem->Animation.TargetState = QUAD_STATE_MOUNT_RIGHT; - } - - laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - laraItem->Pose.Position.x = quadItem->Pose.Position.x; - laraItem->Pose.Position.y = quadItem->Pose.Position.y; - laraItem->Pose.Position.z = quadItem->Pose.Position.z; - laraItem->Pose.Orientation.y = quadItem->Pose.Orientation.y; - ResetLaraFlex(laraItem); - lara->HitDirection = -1; - quadItem->HitPoints = 1; - - AnimateItem(laraItem); - - quad->Revs = 0; - } - else - ObjectCollision(itemNumber, laraItem, coll); - } - - static void TriggerQuadExhaustSmoke(int x, int y, int z, short angle, int speed, int moving) - { - auto* spark = GetFreeParticle(); - - spark->on = true; - spark->sR = 0; - spark->sG = 0; - spark->sB = 0; - - spark->dR = 96; - spark->dG = 96; - spark->dB = 128; - - if (moving) - { - spark->dR = (spark->dR * speed) / 32; - spark->dG = (spark->dG * speed) / 32; - spark->dB = (spark->dB * speed) / 32; - } - - spark->sLife = spark->life = (GetRandomControl() & 3) + 20 - (speed / 4096); - if (spark->sLife < 9) - spark->sLife = spark->life = 9; - - spark->blendMode = BLEND_MODES::BLENDMODE_SCREEN; - spark->colFadeSpeed = 4; - spark->fadeToBlack = 4; - spark->extras = 0; - spark->dynamic = -1; - spark->x = x + ((GetRandomControl() & 15) - 8); - spark->y = y + ((GetRandomControl() & 15) - 8); - spark->z = z + ((GetRandomControl() & 15) - 8); - int zv = speed * phd_cos(angle) / 4; - int xv = speed * phd_sin(angle) / 4; - spark->xVel = xv + ((GetRandomControl() & 255) - 128); - spark->yVel = -(GetRandomControl() & 7) - 8; - spark->zVel = zv + ((GetRandomControl() & 255) - 128); - spark->friction = 4; - - if (GetRandomControl() & 1) - { - spark->flags = SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF; - spark->rotAng = GetRandomControl() & 4095; - if (GetRandomControl() & 1) - spark->rotAdd = -(GetRandomControl() & 7) - 24; - else - spark->rotAdd = (GetRandomControl() & 7) + 24; - } - else - spark->flags = SP_SCALE | SP_DEF | SP_EXPDEF; - - spark->spriteIndex = Objects[ID_DEFAULT_SPRITES].meshIndex; - spark->scalar = 2; - spark->gravity = -(GetRandomControl() & 3) - 4; - spark->maxYvel = -(GetRandomControl() & 7) - 8; - int size = (GetRandomControl() & 7) + 64 + (speed / 128); - spark->dSize = size; - spark->size = spark->sSize = size / 2; - } - - bool QuadBikeControl(ItemInfo* laraItem, CollisionInfo* coll) - { - auto* lara = GetLaraInfo(laraItem); - auto* quadItem = &g_Level.Items[lara->Vehicle]; - auto* quad = (QuadInfo*)quadItem->Data; - - GameVector oldPos; - oldPos.x = quadItem->Pose.Position.x; - oldPos.y = quadItem->Pose.Position.y; - oldPos.z = quadItem->Pose.Position.z; - oldPos.roomNumber = quadItem->RoomNumber; - - bool collide = QuadDynamics(laraItem, quadItem); - - auto probe = GetCollision(quadItem); - - Vector3Int frontLeft, frontRight; - auto floorHeightLeft = TestQuadHeight(quadItem, QUAD_FRONT, -QUAD_SIDE, &frontLeft); - auto floorHeightRight = TestQuadHeight(quadItem, QUAD_FRONT, QUAD_SIDE, &frontRight); - - TestTriggers(quadItem, false); - - bool dead = false; - if (laraItem->HitPoints <= 0) - { - TrInput &= ~(IN_LEFT | IN_RIGHT | IN_BACK | IN_FORWARD); - dead = true; - } - - int drive = -1; - int pitch = 0; - if (quad->Flags) - collide = false; - else - { - switch (laraItem->Animation.ActiveState) - { - case QUAD_STATE_MOUNT_LEFT: - case QUAD_STATE_MOUNT_RIGHT: - case QUAD_STATE_DISMOUNT_LEFT: - case QUAD_STATE_DISMOUNT_RIGHT: - drive = -1; - collide = false; - break; - - default: - drive = QuadUserControl(quadItem, probe.Position.Floor, &pitch); - break; - } - } - - if (quad->Velocity || quad->Revs) - { - quad->Pitch = pitch; - if (quad->Pitch < -0x8000) - quad->Pitch = -0x8000; - else if (quad->Pitch > 0xA000) - quad->Pitch = 0xA000; - - SoundEffect(SFX_TR3_VEHICLE_QUADBIKE_MOVE, &quadItem->Pose, SoundEnvironment::Land, 0.5f + (float)abs(quad->Pitch) / (float)MAX_VELOCITY); - } - else - { - if (drive != -1) - SoundEffect(SFX_TR3_VEHICLE_QUADBIKE_IDLE, &quadItem->Pose); - - quad->Pitch = 0; - } - - quadItem->Floor = probe.Position.Floor; - - short rotAdd = quad->Velocity / 4; - quad->RearRot -= rotAdd; - quad->RearRot -= (quad->Revs / 8); - quad->FrontRot -= rotAdd; - - quad->LeftVerticalVelocity = DoQuadDynamics(floorHeightLeft, quad->LeftVerticalVelocity, (int*)&frontLeft.y); - quad->RightVerticalVelocity = DoQuadDynamics(floorHeightRight, quad->RightVerticalVelocity, (int*)&frontRight.y); - quadItem->Animation.VerticalVelocity = DoQuadDynamics(probe.Position.Floor, quadItem->Animation.VerticalVelocity, (int*)&quadItem->Pose.Position.y); - quad->Velocity = DoVehicleWaterMovement(quadItem, laraItem, quad->Velocity, QUAD_RADIUS, &quad->TurnRate); - - probe.Position.Floor = (frontLeft.y + frontRight.y) / 2; - short xRot = phd_atan(QUAD_FRONT, quadItem->Pose.Position.y - probe.Position.Floor); - short zRot = phd_atan(QUAD_SIDE, probe.Position.Floor - frontLeft.y); - - quadItem->Pose.Orientation.x += ((xRot - quadItem->Pose.Orientation.x) / 2); - quadItem->Pose.Orientation.z += ((zRot - quadItem->Pose.Orientation.z) / 2); - - if (!(quad->Flags & QUAD_FLAG_DEAD)) - { - if (probe.RoomNumber != quadItem->RoomNumber) - { - ItemNewRoom(lara->Vehicle, probe.RoomNumber); - ItemNewRoom(lara->ItemNumber, probe.RoomNumber); - } - - laraItem->Pose = quadItem->Pose; - - AnimateQuadBike(laraItem, quadItem, collide, dead); - AnimateItem(laraItem); - - quadItem->Animation.AnimNumber = Objects[ID_QUAD].animIndex + (laraItem->Animation.AnimNumber - Objects[ID_QUAD_LARA_ANIMS].animIndex); - quadItem->Animation.FrameNumber = g_Level.Anims[quadItem->Animation.AnimNumber].frameBase + (laraItem->Animation.FrameNumber - g_Level.Anims[laraItem->Animation.AnimNumber].frameBase); - - Camera.targetElevation = -ANGLE(30.0f); - - if (quad->Flags & QUAD_FLAG_IS_FALLING) - { - if (quadItem->Pose.Position.y == quadItem->Floor) - { - ExplodeVehicle(laraItem, quadItem); - return false; - } - } - } - - if (laraItem->Animation.ActiveState != QUAD_STATE_MOUNT_RIGHT && - laraItem->Animation.ActiveState != QUAD_STATE_MOUNT_LEFT && - laraItem->Animation.ActiveState != QUAD_STATE_DISMOUNT_RIGHT && - laraItem->Animation.ActiveState != QUAD_STATE_DISMOUNT_LEFT) - { - Vector3Int pos; - int speed = 0; - short angle = 0; - - for (int i = 0; i < 2; i++) - { - pos.x = quadEffectsPositions[i].x; - pos.y = quadEffectsPositions[i].y; - pos.z = quadEffectsPositions[i].z; - GetJointAbsPosition(quadItem, &pos, quadEffectsPositions[i].meshNum); - angle = quadItem->Pose.Orientation.y + ((i == 0) ? 0x9000 : 0x7000); - if (quadItem->Animation.Velocity > 32) - { - if (quadItem->Animation.Velocity < 64) - { - speed = 64 - quadItem->Animation.Velocity; - TriggerQuadExhaustSmoke(pos.x, pos.y, pos.z, angle, speed, 1); - } - } - else - { - if (quad->SmokeStart < 16) - { - speed = ((quad->SmokeStart * 2) + (GetRandomControl() & 7) + (GetRandomControl() & 16)) * 128; - quad->SmokeStart++; - } - else if (quad->DriftStarting) - speed = (abs(quad->Revs) * 2) + ((GetRandomControl() & 7) * 128); - else if ((GetRandomControl() & 3) == 0) - speed = ((GetRandomControl() & 15) + (GetRandomControl() & 16)) * 128; - else - speed = 0; - - TriggerQuadExhaustSmoke(pos.x, pos.y, pos.z, angle, speed, 0); - } - } - } - else - quad->SmokeStart = 0; - - return QuadCheckGetOff(laraItem, quadItem); - } -} diff --git a/TombEngine/Objects/TR3/Vehicles/quad.h b/TombEngine/Objects/TR3/Vehicles/quad.h deleted file mode 100644 index f3b6604ba..000000000 --- a/TombEngine/Objects/TR3/Vehicles/quad.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -struct ItemInfo; -struct CollisionInfo; - -namespace TEN::Entities::Vehicles -{ - void InitialiseQuadBike(short itemNumber); - void QuadBikeCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); - bool QuadBikeControl(ItemInfo* laraItem, CollisionInfo* coll); -} diff --git a/TombEngine/Objects/TR3/Vehicles/quad_bike.cpp b/TombEngine/Objects/TR3/Vehicles/quad_bike.cpp new file mode 100644 index 000000000..950a4e3d9 --- /dev/null +++ b/TombEngine/Objects/TR3/Vehicles/quad_bike.cpp @@ -0,0 +1,1272 @@ +#include "framework.h" +#include "Objects/TR3/Vehicles/quad_bike.h" + +#include "Game/animation.h" +#include "Game/camera.h" +#include "Game/collision/collide_room.h" +#include "Game/collision/collide_item.h" +#include "Game/effects/effects.h" +#include "Game/effects/tomb4fx.h" +#include "Game/items.h" +#include "Game/Lara/lara.h" +#include "Game/Lara/lara_flare.h" +#include "Game/Lara/lara_helpers.h" +#include "Game/Lara/lara_one_gun.h" +#include "Game/misc.h" +#include "Objects/TR3/Vehicles/quad_bike_info.h" +#include "Objects/Utils/VehicleHelpers.h" +#include "Sound/sound.h" +#include "Specific/level.h" +#include "Specific/input.h" +#include "Specific/setup.h" +#include "Specific/prng.h" +#include "Game/effects/simple_particle.h" + +using namespace TEN::Input; +using namespace TEN::Math::Random; + +namespace TEN::Entities::Vehicles +{ + BITE_INFO QuadBikeEffectsPositions[6] = + { + { -56, -32, -380, 0 }, + { 56, -32, -380, 0 }, + { -8, 180, -48, 3 }, + { 8, 180, -48, 4 }, + { 90, 180, -32, 6 }, + { -90, 180, -32, 7 } + }; + const vector QuadBikeMountTypes = + { + VehicleMountType::LevelStart, + VehicleMountType::Left, + VehicleMountType::Right + }; + + constexpr auto DAMAGE_START = 140; + constexpr auto DAMAGE_LENGTH = 14; + + constexpr auto QBIKE_RADIUS = 500; + constexpr auto QBIKE_HEIGHT = 512; + constexpr auto QBIKE_FRONT = 550; + constexpr auto QBIKE_BACK = -550; + constexpr auto QBIKE_SIDE = 260; + constexpr auto QBIKE_SLIP = 100; + constexpr auto QBIKE_SLIP_SIDE = 50; + constexpr auto QBIKE_MOUNT_DISTANCE = CLICK(2); + constexpr auto QBIKE_DISMOUNT_DISTANCE = 385; // Precise offset derived from animation. + + constexpr int MAX_VELOCITY = 160 * VEHICLE_VELOCITY_SCALE; + constexpr int MIN_DRIFT_VELOCITY = 48 * VEHICLE_VELOCITY_SCALE; + constexpr int BRAKE = 2.5f * VEHICLE_VELOCITY_SCALE; + constexpr int REVERSE_ACCELERATION = -3 * VEHICLE_VELOCITY_SCALE; + constexpr int MAX_BACK = -48 * VEHICLE_VELOCITY_SCALE; + constexpr int MAX_REVS = 160 * VEHICLE_VELOCITY_SCALE; + constexpr int TERMINAL_VERTICAL_VELOCITY = 240; + + constexpr auto QBIKE_STEP_HEIGHT_MAX = CLICK(1); // Unused. + constexpr auto QBIKE_MIN_BOUNCE = (MAX_VELOCITY / 2) / CLICK(1); + + // TODO + constexpr auto QBIKE_HIT_LEFT = 11; + constexpr auto QBIKE_HIT_RIGHT = 12; + constexpr auto QBIKE_HIT_FRONT = 13; + constexpr auto QBIKE_HIT_BACK = 14; + + #define QBIKE_TURN_RATE_ACCEL ANGLE(2.5f) + #define QBIKE_TURN_RATE_DECEL ANGLE(2.0f) + #define QBIKE_TURN_RATE_MAX ANGLE(5.0f) + #define QBIKE_DRIFT_TURN_RATE_ACCEL ANGLE(2.75f) + #define QBIKE_DRIFT_TURN_RATE_MAX ANGLE(8.0f) + #define QBIKE_MOMENTUM_TURN_RATE_MIN ANGLE(3.0f) + #define QBIKE_MOMENTUM_TURN_RATE_MAX ANGLE(1.5f) + #define QBIKE_MOMENTUM_TURN_RATE_MAX2 ANGLE(150.0f) // TODO: Resolve this naming clash! + + enum QuadBikeState + { + QBIKE_STATE_DRIVE = 1, + QBIKE_STATE_TURN_LEFT = 2, + QBIKE_STATE_SLOW = 5, + QBIKE_STATE_BRAKE = 6, + QBIKE_STATE_BIKE_DEATH = 7, + QBIKE_STATE_FALL = 8, + QBIKE_STATE_MOUNT_RIGHT = 9, + QBIKE_STATE_DISMOUNT_RIGHT = 10, + QBIKE_STATE_HIT_BACK = 11, + QBIKE_STATE_HIT_FRONT = 12, + QBIKE_STATE_HIT_LEFT = 13, + QBIKE_STATE_HIT_RIGHT = 14, + QBIKE_STATE_IDLE = 15, + QBIKE_STATE_LAND = 17, + QBIKE_STATE_STOP_SLOWLY = 18, + QBIKE_STATE_FALL_DEATH = 19, + QBIKE_STATE_FALL_OFF = 20, + QBIKE_STATE_WHEELIE = 21, // Unused. + QBIKE_STATE_TURN_RIGHT = 22, + QBIKE_STATE_MOUNT_LEFT = 23, + QBIKE_STATE_DISMOUNT_LEFT = 24, + }; + + enum QuadBikeAnim + { + QBIKE_ANIM_IDLE_DEATH = 0, + QBIKE_ANIM_UNK_1 = 1, + QBIKE_ANIM_DRIVE_BACK = 2, + QBIKE_ANIM_TURN_LEFT_START = 3, + QBIKE_ANIM_TURN_LEFT_CONTINUE = 4, + QBIKE_ANIM_TURN_LEFT_END = 5, + QBIKE_ANIM_LEAP_START = 6, + QBIKE_ANIM_LEAP_CONTINUE = 7, + QBIKE_ANIM_LEAP_END = 8, + QBIKE_ANIM_MOUNT_RIGHT = 9, + QBIKE_ANIM_DISMOUNT_RIGHT = 10, + QBIKE_ANIM_HIT_FRONT = 11, + QBIKE_ANIM_HIT_BACK = 12, + QBIKE_ANIM_HIT_RIGHT = 13, + QBIKE_ANIM_HIT_LEFT = 14, + QBIKE_ANIM_UNK_2 = 15, + QBIKE_ANIM_UNK_3 = 16, + QBIKE_ANIM_UNK_4 = 17, + QBIKE_ANIM_IDLE = 18, + QBIKE_ANIM_FALL_OFF_DEATH = 19, + QBIKE_ANIM_TURN_RIGHT_START = 20, + QBIKE_ANIM_TURN_RIGHT_CONTINUE = 21, + QBIKE_ANIM_TURN_RIGHT_END = 22, + QBIKE_ANIM_MOUNT_LEFT = 23, + QBIKE_ANIM_DISMOUNT_LEFT = 24, + QBIKE_ANIM_LEAP_START2 = 25, + QBIKE_ANIM_LEAP_CONTINUE2 = 26, + QBIKE_ANIM_LEAP_END2 = 27, + QBIKE_ANIM_LEAP_TO_FREEFALL = 28 + }; + + enum QuadBikeFlags + { + QBIKE_FLAG_FALLING = (1 << 6), + QBIKE_FLAG_DEAD = (1 << 7) + }; + + enum QuadBikeEffectPosition + { + EXHAUST_LEFT = 0, + EXHAUST_RIGHT = 1, + FRONT_LEFT_TYRE = 2, + FRONT_RIGHT_TYRE = 3, + BACK_LEFT_TYRE = 4, + BACK_RIGHT_TYRE = 5 + }; + + QuadBikeInfo* GetQuadBikeInfo(ItemInfo* quadBikeItem) + { + return (QuadBikeInfo*)quadBikeItem->Data; + } + + void InitialiseQuadBike(short itemNumber) + { + auto* quadBikeItem = &g_Level.Items[itemNumber]; + quadBikeItem->Data = QuadBikeInfo(); + auto* quadBike = GetQuadBikeInfo(quadBikeItem); + + quadBike->MomentumAngle = quadBikeItem->Pose.Orientation.y; + } + + void QuadBikePlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) + { + auto* quadBikeItem = &g_Level.Items[itemNumber]; + auto* quadBike = GetQuadBikeInfo(quadBikeItem); + auto* lara = GetLaraInfo(laraItem); + + if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) + return; + + auto mountType = GetVehicleMountType(quadBikeItem, laraItem, coll, QuadBikeMountTypes, QBIKE_MOUNT_DISTANCE); + if (mountType == VehicleMountType::None) + ObjectCollision(itemNumber, laraItem, coll); + else + { + lara->Vehicle = itemNumber; + DoQuadBikeMount(quadBikeItem, laraItem, mountType); + } + } + + void DoQuadBikeMount(ItemInfo* quadBikeItem, ItemInfo* laraItem, VehicleMountType mountType) + { + auto* quadBike = GetQuadBikeInfo(quadBikeItem); + auto* lara = GetLaraInfo(laraItem); + + switch (mountType) + { + case VehicleMountType::LevelStart: + laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QBIKE_ANIM_IDLE; + laraItem->Animation.ActiveState = QBIKE_STATE_IDLE; + laraItem->Animation.TargetState = QBIKE_STATE_IDLE; + break; + + case VehicleMountType::Left: + laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QBIKE_ANIM_MOUNT_LEFT; + laraItem->Animation.ActiveState = QBIKE_STATE_MOUNT_LEFT; + laraItem->Animation.TargetState = QBIKE_STATE_MOUNT_LEFT; + break; + + default: + case VehicleMountType::Right: + laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QBIKE_ANIM_MOUNT_RIGHT; + laraItem->Animation.ActiveState = QBIKE_STATE_MOUNT_RIGHT; + laraItem->Animation.TargetState = QBIKE_STATE_MOUNT_RIGHT; + break; + } + laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; + + DoVehicleFlareDiscard(laraItem); + ResetLaraFlex(laraItem); + laraItem->Pose.Position = quadBikeItem->Pose.Position; + laraItem->Pose.Orientation = Vector3Shrt(0, quadBikeItem->Pose.Orientation.y, 0); + lara->Control.HandStatus = HandStatus::Busy; + lara->HitDirection = -1; + quadBikeItem->HitPoints = 1; + quadBike->Revs = 0; + + AnimateItem(laraItem); + } + + static int CanQuadbikeGetOff(ItemInfo* laraItem, int direction) + { + auto* lara = GetLaraInfo(laraItem); + auto* quadBikeItem = &g_Level.Items[lara->Vehicle]; + + short angle = quadBikeItem->Pose.Orientation.y; + angle += (direction < 0) ? -ANGLE(90.0f) : ANGLE(90.0f); + + int x = quadBikeItem->Pose.Position.x + CLICK(2) * phd_sin(angle); + int y = quadBikeItem->Pose.Position.y; + int z = quadBikeItem->Pose.Position.z + CLICK(2) * phd_cos(angle); + + auto collResult = GetCollision(x, y, z, quadBikeItem->RoomNumber); + + if (collResult.Position.FloorSlope || + collResult.Position.Floor == NO_HEIGHT) + { + return false; + } + + if (abs(collResult.Position.Floor - quadBikeItem->Pose.Position.y) > CLICK(2)) + return false; + + if ((collResult.Position.Ceiling - quadBikeItem->Pose.Position.y) > -LARA_HEIGHT || + (collResult.Position.Floor - collResult.Position.Ceiling) < LARA_HEIGHT) + { + return false; + } + + return true; + } + + static bool QuadBikeCheckGetOff(ItemInfo* quadBikeItem, ItemInfo* laraItem) + { + auto* quadBike = GetQuadBikeInfo(quadBikeItem); + auto* lara = GetLaraInfo(laraItem); + + if (lara->Vehicle == NO_ITEM) + return true; + + if ((laraItem->Animation.ActiveState == QBIKE_STATE_DISMOUNT_RIGHT || laraItem->Animation.ActiveState == QBIKE_STATE_DISMOUNT_LEFT) && + TestLastFrame(laraItem)) + { + if (laraItem->Animation.ActiveState == QBIKE_STATE_DISMOUNT_LEFT) + laraItem->Pose.Orientation.y += ANGLE(90.0f); + else + laraItem->Pose.Orientation.y -= ANGLE(90.0f); + + SetAnimation(laraItem, LA_STAND_IDLE); + TranslateItem(laraItem, laraItem->Pose.Orientation.y, -QBIKE_DISMOUNT_DISTANCE); + laraItem->Pose.Orientation.x = 0; + laraItem->Pose.Orientation.z = 0; + lara->Vehicle = NO_ITEM; + lara->Control.HandStatus = HandStatus::Free; + + if (laraItem->Animation.ActiveState == QBIKE_STATE_FALL_OFF) + { + auto pos = Vector3Int(); + + SetAnimation(laraItem, LA_FREEFALL); + GetJointAbsPosition(laraItem, &pos, LM_HIPS); + + laraItem->Pose.Position = pos; + laraItem->Animation.IsAirborne = true; + laraItem->Animation.VerticalVelocity = quadBikeItem->Animation.VerticalVelocity; + laraItem->Pose.Orientation.x = 0; + laraItem->Pose.Orientation.z = 0; + laraItem->HitPoints = 0; + lara->Control.HandStatus = HandStatus::Free; + quadBikeItem->Flags |= IFLAG_INVISIBLE; + + return false; + } + else if (laraItem->Animation.ActiveState == QBIKE_STATE_FALL_DEATH) + { + laraItem->Animation.TargetState = LS_DEATH; + laraItem->Animation.Velocity = 0; + laraItem->Animation.VerticalVelocity = DAMAGE_START + DAMAGE_LENGTH; + quadBike->Flags |= QBIKE_FLAG_DEAD; + + return false; + } + + return true; + } + else + return true; + } + + static int GetQuadCollisionAnim(ItemInfo* quadBikeItem, Vector3Int* pos) + { + pos->x = quadBikeItem->Pose.Position.x - pos->x; + pos->z = quadBikeItem->Pose.Position.z - pos->z; + + if (pos->x || pos->z) + { + float c = phd_cos(quadBikeItem->Pose.Orientation.y); + float s = phd_sin(quadBikeItem->Pose.Orientation.y); + int front = pos->z * c + pos->x * s; + int side = -pos->z * s + pos->x * c; + + if (abs(front) > abs(side)) + { + if (front > 0) + return QBIKE_HIT_BACK; + else + return QBIKE_HIT_FRONT; + } + else + { + if (side > 0) + return QBIKE_HIT_LEFT; + else + return QBIKE_HIT_RIGHT; + } + } + + return 0; + } + + static int DoQuadShift(ItemInfo* quadBikeItem, Vector3Int* pos, Vector3Int* old) + { + CollisionResult probe; + int x = pos->x / SECTOR(1); + int z = pos->z / SECTOR(1); + int oldX = old->x / SECTOR(1); + int oldZ = old->z / SECTOR(1); + int shiftX = pos->x & (SECTOR(1) - 1); + int shiftZ = pos->z & (SECTOR(1) - 1); + + if (x == oldX) + { + if (z == oldZ) + { + quadBikeItem->Pose.Position.z += (old->z - pos->z); + quadBikeItem->Pose.Position.x += (old->x - pos->x); + } + else if (z > oldZ) + { + quadBikeItem->Pose.Position.z -= shiftZ + 1; + return (pos->x - quadBikeItem->Pose.Position.x); + } + else + { + quadBikeItem->Pose.Position.z += SECTOR(1) - shiftZ; + return (quadBikeItem->Pose.Position.x - pos->x); + } + } + else if (z == oldZ) + { + if (x > oldX) + { + quadBikeItem->Pose.Position.x -= shiftX + 1; + return (quadBikeItem->Pose.Position.z - pos->z); + } + else + { + quadBikeItem->Pose.Position.x += SECTOR(1) - shiftX; + return (pos->z - quadBikeItem->Pose.Position.z); + } + } + else + { + x = 0; + z = 0; + + probe = GetCollision(old->x, pos->y, pos->z, quadBikeItem->RoomNumber); + if (probe.Position.Floor < (old->y - CLICK(1))) + { + if (pos->z > old->z) + z = -shiftZ - 1; + else + z = SECTOR(1) - shiftZ; + } + + probe = GetCollision(pos->x, pos->y, old->z, quadBikeItem->RoomNumber); + if (probe.Position.Floor < (old->y - CLICK(1))) + { + if (pos->x > old->x) + x = -shiftX - 1; + else + x = SECTOR(1) - shiftX; + } + + if (x && z) + { + quadBikeItem->Pose.Position.z += z; + quadBikeItem->Pose.Position.x += x; + } + else if (z) + { + quadBikeItem->Pose.Position.z += z; + + if (z > 0) + return (quadBikeItem->Pose.Position.x - pos->x); + else + return (pos->x - quadBikeItem->Pose.Position.x); + } + else if (x) + { + quadBikeItem->Pose.Position.x += x; + + if (x > 0) + return (pos->z - quadBikeItem->Pose.Position.z); + else + return (quadBikeItem->Pose.Position.z - pos->z); + } + else + { + quadBikeItem->Pose.Position.z += (old->z - pos->z); + quadBikeItem->Pose.Position.x += (old->x - pos->x); + } + } + + return 0; + } + + static int DoQuadDynamics(int height, int verticalVelocity, int* y) + { + if (height > *y) + { + *y += verticalVelocity; + if (*y > height - QBIKE_MIN_BOUNCE) + { + *y = height; + verticalVelocity = 0; + } + else + verticalVelocity += 6; + } + else + { + int kick = (height - *y) * 4; + if (kick < -80) + kick = -80; + + verticalVelocity += ((kick - verticalVelocity) / 8); + + if (*y > height) + *y = height; + } + + return verticalVelocity; + } + + static int QuadDynamics(ItemInfo* quadBikeItem, ItemInfo* laraItem) + { + auto* quadBike = GetQuadBikeInfo(quadBikeItem); + auto* lara = GetLaraInfo(laraItem); + + quadBike->NoDismount = false; + + Vector3Int oldFrontLeft, oldFrontRight, oldBottomLeft, oldBottomRight; + int holdFrontLeft = GetVehicleHeight(quadBikeItem, QBIKE_FRONT, -QBIKE_SIDE, true, &oldFrontLeft); + int holdFrontRight = GetVehicleHeight(quadBikeItem, QBIKE_FRONT, QBIKE_SIDE, true, &oldFrontRight); + int holdBottomLeft = GetVehicleHeight(quadBikeItem, -QBIKE_FRONT, -QBIKE_SIDE, true, &oldBottomLeft); + int holdBottomRight = GetVehicleHeight(quadBikeItem, -QBIKE_FRONT, QBIKE_SIDE, true, &oldBottomRight); + + Vector3Int mtlOld, mtrOld, mmlOld, mmrOld; + int hmml_old = GetVehicleHeight(quadBikeItem, 0, -QBIKE_SIDE, true, &mmlOld); + int hmmr_old = GetVehicleHeight(quadBikeItem, 0, QBIKE_SIDE, true, &mmrOld); + int hmtl_old = GetVehicleHeight(quadBikeItem, QBIKE_FRONT / 2, -QBIKE_SIDE, true, &mtlOld); + int hmtr_old = GetVehicleHeight(quadBikeItem, QBIKE_FRONT / 2, QBIKE_SIDE, true, &mtrOld); + + Vector3Int moldBottomLeft, moldBottomRight; + int hmoldBottomLeft = GetVehicleHeight(quadBikeItem, -QBIKE_FRONT / 2, -QBIKE_SIDE, true, &moldBottomLeft); + int hmoldBottomRight = GetVehicleHeight(quadBikeItem, -QBIKE_FRONT / 2, QBIKE_SIDE, true, &moldBottomRight); + + Vector3Int old; + old.x = quadBikeItem->Pose.Position.x; + old.y = quadBikeItem->Pose.Position.y; + old.z = quadBikeItem->Pose.Position.z; + + if (quadBikeItem->Pose.Position.y > (quadBikeItem->Floor - CLICK(1))) + { + if (quadBike->TurnRate < -QBIKE_TURN_RATE_DECEL) + quadBike->TurnRate += QBIKE_TURN_RATE_DECEL; + else if (quadBike->TurnRate > QBIKE_TURN_RATE_DECEL) + quadBike->TurnRate -= QBIKE_TURN_RATE_DECEL; + else + quadBike->TurnRate = 0; + + quadBikeItem->Pose.Orientation.y += quadBike->TurnRate + quadBike->ExtraRotation; + + short momentum = QBIKE_MOMENTUM_TURN_RATE_MIN - (((((QBIKE_MOMENTUM_TURN_RATE_MIN - QBIKE_MOMENTUM_TURN_RATE_MAX) * 256) / MAX_VELOCITY) * quadBike->Velocity) / 256); + if (!(TrInput & VEHICLE_IN_ACCELERATE) && quadBike->Velocity > 0) + momentum += momentum / 4; + + short rot = quadBikeItem->Pose.Orientation.y - quadBike->MomentumAngle; + if (rot < -QBIKE_MOMENTUM_TURN_RATE_MAX) + { + if (rot < -QBIKE_MOMENTUM_TURN_RATE_MAX2) + { + rot = -QBIKE_MOMENTUM_TURN_RATE_MAX2; + quadBike->MomentumAngle = quadBikeItem->Pose.Orientation.y - rot; + } + else + quadBike->MomentumAngle -= momentum; + } + else if (rot > QBIKE_MOMENTUM_TURN_RATE_MAX) + { + if (rot > QBIKE_MOMENTUM_TURN_RATE_MAX2) + { + rot = QBIKE_MOMENTUM_TURN_RATE_MAX2; + quadBike->MomentumAngle = quadBikeItem->Pose.Orientation.y - rot; + } + else + quadBike->MomentumAngle += momentum; + } + else + quadBike->MomentumAngle = quadBikeItem->Pose.Orientation.y; + } + else + quadBikeItem->Pose.Orientation.y += quadBike->TurnRate + quadBike->ExtraRotation; + + auto probe = GetCollision(quadBikeItem); + int speed = 0; + if (quadBikeItem->Pose.Position.y >= probe.Position.Floor) + speed = quadBikeItem->Animation.Velocity * phd_cos(quadBikeItem->Pose.Orientation.x); + else + speed = quadBikeItem->Animation.Velocity; + + TranslateItem(quadBikeItem, quadBike->MomentumAngle, speed); + + int slip = QBIKE_SLIP * phd_sin(quadBikeItem->Pose.Orientation.x); + if (abs(slip) > QBIKE_SLIP / 2) + { + if (slip > 0) + slip -= 10; + else + slip += 10; + quadBikeItem->Pose.Position.z -= slip * phd_cos(quadBikeItem->Pose.Orientation.y); + quadBikeItem->Pose.Position.x -= slip * phd_sin(quadBikeItem->Pose.Orientation.y); + } + + slip = QBIKE_SLIP_SIDE * phd_sin(quadBikeItem->Pose.Orientation.z); + if (abs(slip) > QBIKE_SLIP_SIDE / 2) + { + quadBikeItem->Pose.Position.z -= slip * phd_sin(quadBikeItem->Pose.Orientation.y); + quadBikeItem->Pose.Position.x += slip * phd_cos(quadBikeItem->Pose.Orientation.y); + } + + Vector3Int moved; + moved.x = quadBikeItem->Pose.Position.x; + moved.z = quadBikeItem->Pose.Position.z; + + if (!(quadBikeItem->Flags & IFLAG_INVISIBLE)) + DoVehicleCollision(quadBikeItem, QBIKE_RADIUS); + + short rot = 0; + short rotAdd = 0; + + Vector3Int fl; + int heightFrontLeft = GetVehicleHeight(quadBikeItem, QBIKE_FRONT, -QBIKE_SIDE, false, &fl); + if (heightFrontLeft < (oldFrontLeft.y - CLICK(1))) + rot = DoQuadShift(quadBikeItem, &fl, &oldFrontLeft); + + Vector3Int mtl; + int hmtl = GetVehicleHeight(quadBikeItem, QBIKE_FRONT / 2, -QBIKE_SIDE, false, &mtl); + if (hmtl < (mtlOld.y - CLICK(1))) + DoQuadShift(quadBikeItem, &mtl, &mtlOld); + + Vector3Int mml; + int hmml = GetVehicleHeight(quadBikeItem, 0, -QBIKE_SIDE, false, &mml); + if (hmml < (mmlOld.y - CLICK(1))) + DoQuadShift(quadBikeItem, &mml, &mmlOld); + + Vector3Int mbl; + int hmbl = GetVehicleHeight(quadBikeItem, -QBIKE_FRONT / 2, -QBIKE_SIDE, false, &mbl); + if (hmbl < (moldBottomLeft.y - CLICK(1))) + DoQuadShift(quadBikeItem, &mbl, &moldBottomLeft); + + Vector3Int bl; + int heightBackLeft = GetVehicleHeight(quadBikeItem, -QBIKE_FRONT, -QBIKE_SIDE, false, &bl); + if (heightBackLeft < (oldBottomLeft.y - CLICK(1))) + { + rotAdd = DoQuadShift(quadBikeItem, &bl, &oldBottomLeft); + if ((rotAdd > 0 && rot >= 0) || (rotAdd < 0 && rot <= 0)) + rot += rotAdd; + } + + Vector3Int fr; + int heightFrontRight = GetVehicleHeight(quadBikeItem, QBIKE_FRONT, QBIKE_SIDE, false, &fr); + if (heightFrontRight < (oldFrontRight.y - CLICK(1))) + { + rotAdd = DoQuadShift(quadBikeItem, &fr, &oldFrontRight); + if ((rotAdd > 0 && rot >= 0) || (rotAdd < 0 && rot <= 0)) + rot += rotAdd; + } + + Vector3Int mtr; + int hmtr = GetVehicleHeight(quadBikeItem, QBIKE_FRONT / 2, QBIKE_SIDE, false, &mtr); + if (hmtr < (mtrOld.y - CLICK(1))) + DoQuadShift(quadBikeItem, &mtr, &mtrOld); + + Vector3Int mmr; + int hmmr = GetVehicleHeight(quadBikeItem, 0, QBIKE_SIDE, false, &mmr); + if (hmmr < (mmrOld.y - CLICK(1))) + DoQuadShift(quadBikeItem, &mmr, &mmrOld); + + Vector3Int mbr; + int hmbr = GetVehicleHeight(quadBikeItem, -QBIKE_FRONT / 2, QBIKE_SIDE, false, &mbr); + if (hmbr < (moldBottomRight.y - CLICK(1))) + DoQuadShift(quadBikeItem, &mbr, &moldBottomRight); + + Vector3Int br; + int heightBackRight = GetVehicleHeight(quadBikeItem, -QBIKE_FRONT, QBIKE_SIDE, false, &br); + if (heightBackRight < (oldBottomRight.y - CLICK(1))) + { + rotAdd = DoQuadShift(quadBikeItem, &br, &oldBottomRight); + if ((rotAdd > 0 && rot >= 0) || (rotAdd < 0 && rot <= 0)) + rot += rotAdd; + } + + probe = GetCollision(quadBikeItem); + if (probe.Position.Floor < quadBikeItem->Pose.Position.y - CLICK(1)) + DoQuadShift(quadBikeItem, (Vector3Int*)&quadBikeItem->Pose, &old); + + quadBike->ExtraRotation = rot; + + int collide = GetQuadCollisionAnim(quadBikeItem, &moved); + + int newVelocity = 0; + if (collide) + { + newVelocity = (quadBikeItem->Pose.Position.z - old.z) * phd_cos(quadBike->MomentumAngle) + (quadBikeItem->Pose.Position.x - old.x) * phd_sin(quadBike->MomentumAngle); + newVelocity *= VEHICLE_VELOCITY_SCALE; + + if (&g_Level.Items[lara->Vehicle] == quadBikeItem && + quadBike->Velocity == MAX_VELOCITY && + newVelocity < (quadBike->Velocity - 10)) + { + DoDamage(laraItem, (quadBike->Velocity - newVelocity) / 128); + } + + if (quadBike->Velocity > 0 && newVelocity < quadBike->Velocity) + quadBike->Velocity = (newVelocity < 0) ? 0 : newVelocity; + + else if (quadBike->Velocity < 0 && newVelocity > quadBike->Velocity) + quadBike->Velocity = (newVelocity > 0) ? 0 : newVelocity; + + if (quadBike->Velocity < MAX_BACK) + quadBike->Velocity = MAX_BACK; + } + + return collide; + } + + static void AnimateQuadBike(ItemInfo* quadBikeItem, ItemInfo* laraItem, int collide, bool dead) + { + auto* quadBike = GetQuadBikeInfo(quadBikeItem); + + if (quadBikeItem->Pose.Position.y != quadBikeItem->Floor && + laraItem->Animation.ActiveState != QBIKE_STATE_FALL && + laraItem->Animation.ActiveState != QBIKE_STATE_LAND && + laraItem->Animation.ActiveState != QBIKE_STATE_FALL_OFF && + !dead) + { + if (quadBike->Velocity < 0) + laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QBIKE_ANIM_LEAP_START; + else + laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QBIKE_ANIM_LEAP_START2; + + laraItem->Animation.FrameNumber = GetFrameNumber(laraItem, laraItem->Animation.AnimNumber); + laraItem->Animation.ActiveState = QBIKE_STATE_FALL; + laraItem->Animation.TargetState = QBIKE_STATE_FALL; + } + else if (collide && + laraItem->Animation.ActiveState != QBIKE_STATE_HIT_FRONT && + laraItem->Animation.ActiveState != QBIKE_STATE_HIT_BACK && + laraItem->Animation.ActiveState != QBIKE_STATE_HIT_LEFT && + laraItem->Animation.ActiveState != QBIKE_STATE_HIT_RIGHT && + laraItem->Animation.ActiveState != QBIKE_STATE_FALL_OFF && + quadBike->Velocity > (MAX_VELOCITY / 3) && + !dead) + { + if (collide == QBIKE_HIT_FRONT) + { + laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QBIKE_ANIM_HIT_BACK; + laraItem->Animation.ActiveState = QBIKE_STATE_HIT_FRONT; + laraItem->Animation.TargetState = QBIKE_STATE_HIT_FRONT; + } + else if (collide == QBIKE_HIT_BACK) + { + laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QBIKE_ANIM_HIT_FRONT; + laraItem->Animation.ActiveState = QBIKE_STATE_HIT_BACK; + laraItem->Animation.TargetState = QBIKE_STATE_HIT_BACK; + } + else if (collide == QBIKE_HIT_LEFT) + { + laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QBIKE_ANIM_HIT_RIGHT; + laraItem->Animation.ActiveState = QBIKE_STATE_HIT_LEFT; + laraItem->Animation.TargetState = QBIKE_STATE_HIT_LEFT; + } + else + { + laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QBIKE_ANIM_HIT_LEFT; + laraItem->Animation.ActiveState = QBIKE_STATE_HIT_RIGHT; + laraItem->Animation.TargetState = QBIKE_STATE_HIT_RIGHT; + } + + laraItem->Animation.FrameNumber = GetFrameNumber(laraItem, laraItem->Animation.AnimNumber); + SoundEffect(SFX_TR3_VEHICLE_QUADBIKE_FRONT_IMPACT, &quadBikeItem->Pose); + } + else + { + switch (laraItem->Animation.ActiveState) + { + case QBIKE_STATE_IDLE: + if (dead) + laraItem->Animation.TargetState = QBIKE_STATE_BIKE_DEATH; + else if (TrInput & VEHICLE_IN_DISMOUNT && + quadBike->Velocity == 0 && + !quadBike->NoDismount) + { + if (TrInput & VEHICLE_IN_LEFT && CanQuadbikeGetOff(laraItem, -1)) + laraItem->Animation.TargetState = QBIKE_STATE_DISMOUNT_LEFT; + else if (TrInput & VEHICLE_IN_RIGHT && CanQuadbikeGetOff(laraItem, 1)) + laraItem->Animation.TargetState = QBIKE_STATE_DISMOUNT_RIGHT; + } + else if (TrInput & (VEHICLE_IN_ACCELERATE | VEHICLE_IN_REVERSE)) + laraItem->Animation.TargetState = QBIKE_STATE_DRIVE; + + break; + + case QBIKE_STATE_DRIVE: + if (dead) + { + if (quadBike->Velocity > (MAX_VELOCITY / 2)) + laraItem->Animation.TargetState = QBIKE_STATE_FALL_DEATH; + else + laraItem->Animation.TargetState = QBIKE_STATE_BIKE_DEATH; + } + else if (!(TrInput & (VEHICLE_IN_ACCELERATE | VEHICLE_IN_REVERSE)) && + (quadBike->Velocity / VEHICLE_VELOCITY_SCALE) == 0) + { + laraItem->Animation.TargetState = QBIKE_STATE_IDLE; + } + else if (TrInput & VEHICLE_IN_LEFT && + !quadBike->DriftStarting) + { + laraItem->Animation.TargetState = QBIKE_STATE_TURN_LEFT; + } + else if (TrInput & VEHICLE_IN_RIGHT && + !quadBike->DriftStarting) + { + laraItem->Animation.TargetState = QBIKE_STATE_TURN_RIGHT; + } + else if (TrInput & (VEHICLE_IN_REVERSE | VEHICLE_IN_BRAKE)) + { + if (quadBike->Velocity > (MAX_VELOCITY / 3 * 2)) + laraItem->Animation.TargetState = QBIKE_STATE_BRAKE; + else + laraItem->Animation.TargetState = QBIKE_STATE_SLOW; + } + + break; + + case QBIKE_STATE_BRAKE: + case QBIKE_STATE_SLOW: + case QBIKE_STATE_STOP_SLOWLY: + if ((quadBike->Velocity / VEHICLE_VELOCITY_SCALE) == 0) + laraItem->Animation.TargetState = QBIKE_STATE_IDLE; + else if (TrInput & VEHICLE_IN_LEFT) + laraItem->Animation.TargetState = QBIKE_STATE_TURN_LEFT; + else if (TrInput & VEHICLE_IN_RIGHT) + laraItem->Animation.TargetState = QBIKE_STATE_TURN_RIGHT; + + break; + + case QBIKE_STATE_TURN_LEFT: + if ((quadBike->Velocity / VEHICLE_VELOCITY_SCALE) == 0) + laraItem->Animation.TargetState = QBIKE_STATE_IDLE; + else if (TrInput & VEHICLE_IN_RIGHT) + { + laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QBIKE_ANIM_TURN_RIGHT_START; + laraItem->Animation.FrameNumber = GetFrameNumber(laraItem, laraItem->Animation.AnimNumber); + laraItem->Animation.ActiveState = QBIKE_STATE_TURN_RIGHT; + laraItem->Animation.TargetState = QBIKE_STATE_TURN_RIGHT; + } + else if (!(TrInput & VEHICLE_IN_LEFT)) + laraItem->Animation.TargetState = QBIKE_STATE_DRIVE; + + break; + + case QBIKE_STATE_TURN_RIGHT: + if ((quadBike->Velocity / VEHICLE_VELOCITY_SCALE) == 0) + laraItem->Animation.TargetState = QBIKE_STATE_IDLE; + else if (TrInput & VEHICLE_IN_LEFT) + { + laraItem->Animation.AnimNumber = Objects[ID_QUAD_LARA_ANIMS].animIndex + QBIKE_ANIM_TURN_LEFT_START; + laraItem->Animation.FrameNumber = GetFrameNumber(laraItem, laraItem->Animation.AnimNumber); + laraItem->Animation.ActiveState = QBIKE_STATE_TURN_LEFT; + laraItem->Animation.TargetState = QBIKE_STATE_TURN_LEFT; + } + else if (!(TrInput & VEHICLE_IN_RIGHT)) + laraItem->Animation.TargetState = QBIKE_STATE_DRIVE; + + break; + + case QBIKE_STATE_FALL: + if (quadBikeItem->Pose.Position.y == quadBikeItem->Floor) + laraItem->Animation.TargetState = QBIKE_STATE_LAND; + else if (quadBikeItem->Animation.VerticalVelocity > TERMINAL_VERTICAL_VELOCITY) + quadBike->Flags |= QBIKE_FLAG_FALLING; + + break; + + case QBIKE_STATE_FALL_OFF: + break; + + case QBIKE_STATE_HIT_FRONT: + case QBIKE_STATE_HIT_BACK: + case QBIKE_STATE_HIT_LEFT: + case QBIKE_STATE_HIT_RIGHT: + if (TrInput & (VEHICLE_IN_ACCELERATE | VEHICLE_IN_REVERSE)) + laraItem->Animation.TargetState = QBIKE_STATE_DRIVE; + + break; + } + } + } + + static int QuadUserControl(ItemInfo* quadBikeItem, int height, int* pitch) + { + auto* quadBike = GetQuadBikeInfo(quadBikeItem); + + bool drive = false; // Never changes? + + if (!(TrInput & VEHICLE_IN_SPEED) && + !quadBike->Velocity && !quadBike->CanStartDrift) + { + quadBike->CanStartDrift = true; + } + else if (quadBike->Velocity) + quadBike->CanStartDrift = false; + + if (!(TrInput & VEHICLE_IN_SPEED)) + quadBike->DriftStarting = false; + + if (!quadBike->DriftStarting) + { + if (quadBike->Revs > 0x10) + { + quadBike->Velocity += (quadBike->Revs / 16); + quadBike->Revs -= (quadBike->Revs / 8); + } + else + quadBike->Revs = 0; + } + + if (quadBikeItem->Pose.Position.y >= (height - CLICK(1))) + { + if (TrInput & IN_LOOK && !quadBike->Velocity) + LookUpDown(LaraItem); + + // Driving forward. + if (quadBike->Velocity > 0) + { + if (TrInput & VEHICLE_IN_SPEED && + !quadBike->DriftStarting && + quadBike->Velocity > MIN_DRIFT_VELOCITY) + { + if (TrInput & VEHICLE_IN_LEFT) + { + quadBike->TurnRate -= QBIKE_DRIFT_TURN_RATE_ACCEL; + if (quadBike->TurnRate < -QBIKE_DRIFT_TURN_RATE_MAX) + quadBike->TurnRate = -QBIKE_DRIFT_TURN_RATE_MAX; + } + else if (TrInput & VEHICLE_IN_RIGHT) + { + quadBike->TurnRate += QBIKE_DRIFT_TURN_RATE_ACCEL; + if (quadBike->TurnRate > QBIKE_DRIFT_TURN_RATE_MAX) + quadBike->TurnRate = QBIKE_DRIFT_TURN_RATE_MAX; + } + } + else + { + if (TrInput & VEHICLE_IN_LEFT) + { + quadBike->TurnRate -= QBIKE_TURN_RATE_ACCEL; + if (quadBike->TurnRate < -QBIKE_TURN_RATE_MAX) + quadBike->TurnRate = -QBIKE_TURN_RATE_MAX; + } + else if (TrInput & VEHICLE_IN_RIGHT) + { + quadBike->TurnRate += QBIKE_TURN_RATE_ACCEL; + if (quadBike->TurnRate > QBIKE_TURN_RATE_MAX) + quadBike->TurnRate = QBIKE_TURN_RATE_MAX; + } + } + } + // Driving back. + else if (quadBike->Velocity < 0) + { + if (TrInput & VEHICLE_IN_SPEED && + !quadBike->DriftStarting && + quadBike->Velocity < (-MIN_DRIFT_VELOCITY + 0x800)) + { + if (TrInput & VEHICLE_IN_LEFT) + { + quadBike->TurnRate -= QBIKE_DRIFT_TURN_RATE_ACCEL; + if (quadBike->TurnRate < -QBIKE_DRIFT_TURN_RATE_MAX) + quadBike->TurnRate = -QBIKE_DRIFT_TURN_RATE_MAX; + } + else if (TrInput & VEHICLE_IN_RIGHT) + { + quadBike->TurnRate += QBIKE_DRIFT_TURN_RATE_ACCEL; + if (quadBike->TurnRate > QBIKE_DRIFT_TURN_RATE_MAX) + quadBike->TurnRate = QBIKE_DRIFT_TURN_RATE_MAX; + } + } + else + { + if (TrInput & VEHICLE_IN_RIGHT) + { + quadBike->TurnRate -= QBIKE_TURN_RATE_ACCEL; + if (quadBike->TurnRate < -QBIKE_TURN_RATE_MAX) + quadBike->TurnRate = -QBIKE_TURN_RATE_MAX; + } + else if (TrInput & VEHICLE_IN_LEFT) + { + quadBike->TurnRate += QBIKE_TURN_RATE_ACCEL; + if (quadBike->TurnRate > QBIKE_TURN_RATE_MAX) + quadBike->TurnRate = QBIKE_TURN_RATE_MAX; + } + } + } + + // Driving back / braking. + if (TrInput & (VEHICLE_IN_REVERSE | VEHICLE_IN_BRAKE)) + { + if (TrInput & VEHICLE_IN_SPEED && + (quadBike->CanStartDrift || quadBike->DriftStarting)) + { + quadBike->DriftStarting = true; + quadBike->Revs -= 0x200; + if (quadBike->Revs < MAX_BACK) + quadBike->Revs = MAX_BACK; + } + else if (quadBike->Velocity > 0) + quadBike->Velocity -= BRAKE; + else + { + if (quadBike->Velocity > MAX_BACK) + quadBike->Velocity += REVERSE_ACCELERATION; + } + } + else if (TrInput & VEHICLE_IN_ACCELERATE) + { + if (TrInput & VEHICLE_IN_SPEED && + (quadBike->CanStartDrift || quadBike->DriftStarting)) + { + quadBike->DriftStarting = true; + quadBike->Revs += 0x200; + if (quadBike->Revs >= MAX_VELOCITY) + quadBike->Revs = MAX_VELOCITY; + } + else if (quadBike->Velocity < MAX_VELOCITY) + { + if (quadBike->Velocity < 0x4000) + quadBike->Velocity += (8 + (0x4000 + 0x800 - quadBike->Velocity) / 8); + else if (quadBike->Velocity < 0x7000) + quadBike->Velocity += (4 + (0x7000 + 0x800 - quadBike->Velocity) / 16); + else if (quadBike->Velocity < MAX_VELOCITY) + quadBike->Velocity += (2 + (MAX_VELOCITY - quadBike->Velocity) / 8); + } + else + quadBike->Velocity = MAX_VELOCITY; + + quadBike->Velocity -= abs(quadBikeItem->Pose.Orientation.y - quadBike->MomentumAngle) / 64; + } + + else if (quadBike->Velocity > 0x0100) + quadBike->Velocity -= 0x0100; + else if (quadBike->Velocity < -0x0100) + quadBike->Velocity += 0x0100; + else + quadBike->Velocity = 0; + + if (!(TrInput & (VEHICLE_IN_ACCELERATE | VEHICLE_IN_REVERSE)) && + quadBike->DriftStarting && + quadBike->Revs) + { + if (quadBike->Revs > 0x8) + quadBike->Revs -= quadBike->Revs / 8; + else + quadBike->Revs = 0; + } + + quadBikeItem->Animation.Velocity = quadBike->Velocity / VEHICLE_VELOCITY_SCALE; + + if (quadBike->EngineRevs > 0x7000) + quadBike->EngineRevs = -0x2000; + + int revs = 0; + if (quadBike->Velocity < 0) + revs = abs(quadBike->Velocity / 2); + else if (quadBike->Velocity < 0x7000) + revs = -0x2000 + (quadBike->Velocity * (0x6800 - -0x2000)) / 0x7000; + else if (quadBike->Velocity <= MAX_VELOCITY) + revs = -0x2800 + ((quadBike->Velocity - 0x7000) * (0x7000 - -0x2800)) / (MAX_VELOCITY - 0x7000); + + revs += abs(quadBike->Revs); + quadBike->EngineRevs += (revs - quadBike->EngineRevs) / 8; + } + else + { + if (quadBike->EngineRevs < 0xA000) + quadBike->EngineRevs += (0xA000 - quadBike->EngineRevs) / 8; + } + + *pitch = quadBike->EngineRevs; + + return drive; + } + + static void TriggerQuadExhaustSmoke(int x, int y, int z, short angle, int speed, int moving) + { + auto* spark = GetFreeParticle(); + + spark->on = true; + spark->sR = 0; + spark->sG = 0; + spark->sB = 0; + + spark->dR = 96; + spark->dG = 96; + spark->dB = 128; + + if (moving) + { + spark->dR = (spark->dR * speed) / 32; + spark->dG = (spark->dG * speed) / 32; + spark->dB = (spark->dB * speed) / 32; + } + + spark->sLife = spark->life = (GetRandomControl() & 3) + 20 - (speed / 4096); + if (spark->sLife < 9) + spark->sLife = spark->life = 9; + + spark->blendMode = BLEND_MODES::BLENDMODE_SCREEN; + spark->colFadeSpeed = 4; + spark->fadeToBlack = 4; + spark->extras = 0; + spark->dynamic = -1; + spark->x = x + ((GetRandomControl() & 15) - 8); + spark->y = y + ((GetRandomControl() & 15) - 8); + spark->z = z + ((GetRandomControl() & 15) - 8); + int zv = speed * phd_cos(angle) / 4; + int xv = speed * phd_sin(angle) / 4; + spark->xVel = xv + ((GetRandomControl() & 255) - 128); + spark->yVel = -(GetRandomControl() & 7) - 8; + spark->zVel = zv + ((GetRandomControl() & 255) - 128); + spark->friction = 4; + + if (GetRandomControl() & 1) + { + spark->flags = SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF; + spark->rotAng = GetRandomControl() & 4095; + if (GetRandomControl() & 1) + spark->rotAdd = -(GetRandomControl() & 7) - 24; + else + spark->rotAdd = (GetRandomControl() & 7) + 24; + } + else + spark->flags = SP_SCALE | SP_DEF | SP_EXPDEF; + + spark->spriteIndex = Objects[ID_DEFAULT_SPRITES].meshIndex; + spark->scalar = 2; + spark->gravity = -(GetRandomControl() & 3) - 4; + spark->maxYvel = -(GetRandomControl() & 7) - 8; + int size = (GetRandomControl() & 7) + 64 + (speed / 128); + spark->dSize = size; + spark->size = spark->sSize = size / 2; + } + + bool QuadBikeControl(ItemInfo* laraItem, CollisionInfo* coll) + { + auto* lara = GetLaraInfo(laraItem); + auto* quadBikeItem = &g_Level.Items[lara->Vehicle]; + auto* quadBike = GetQuadBikeInfo(quadBikeItem); + + GameVector oldPos; + oldPos.x = quadBikeItem->Pose.Position.x; + oldPos.y = quadBikeItem->Pose.Position.y; + oldPos.z = quadBikeItem->Pose.Position.z; + oldPos.roomNumber = quadBikeItem->RoomNumber; + + bool collide = QuadDynamics(quadBikeItem, laraItem); + + auto probe = GetCollision(quadBikeItem); + + Vector3Int frontLeft, frontRight; + auto floorHeightLeft = GetVehicleHeight(quadBikeItem, QBIKE_FRONT, -QBIKE_SIDE, false, &frontLeft); + auto floorHeightRight = GetVehicleHeight(quadBikeItem, QBIKE_FRONT, QBIKE_SIDE, false, &frontRight); + + TestTriggers(quadBikeItem, false); + + bool dead = false; + if (laraItem->HitPoints <= 0) + { + TrInput &= ~(IN_LEFT | IN_RIGHT | IN_BACK | IN_FORWARD); + dead = true; + } + + int drive = -1; + int pitch = 0; + if (quadBike->Flags) + collide = false; + else + { + switch (laraItem->Animation.ActiveState) + { + case QBIKE_STATE_MOUNT_LEFT: + case QBIKE_STATE_MOUNT_RIGHT: + case QBIKE_STATE_DISMOUNT_LEFT: + case QBIKE_STATE_DISMOUNT_RIGHT: + drive = -1; + collide = false; + break; + + default: + drive = QuadUserControl(quadBikeItem, probe.Position.Floor, &pitch); + break; + } + } + + if (quadBike->Velocity || quadBike->Revs) + { + quadBike->Pitch = pitch; + if (quadBike->Pitch < -0x8000) + quadBike->Pitch = -0x8000; + else if (quadBike->Pitch > 0xA000) + quadBike->Pitch = 0xA000; + + SoundEffect(SFX_TR3_VEHICLE_QUADBIKE_MOVE, &quadBikeItem->Pose, SoundEnvironment::Land, 0.5f + (float)abs(quadBike->Pitch) / (float)MAX_VELOCITY); + } + else + { + if (drive != -1) + SoundEffect(SFX_TR3_VEHICLE_QUADBIKE_IDLE, &quadBikeItem->Pose); + + quadBike->Pitch = 0; + } + + quadBikeItem->Floor = probe.Position.Floor; + + short rotAdd = quadBike->Velocity / 4; + quadBike->RearRot -= rotAdd; + quadBike->RearRot -= (quadBike->Revs / 8); + quadBike->FrontRot -= rotAdd; + + quadBike->LeftVerticalVelocity = DoQuadDynamics(floorHeightLeft, quadBike->LeftVerticalVelocity, (int*)&frontLeft.y); + quadBike->RightVerticalVelocity = DoQuadDynamics(floorHeightRight, quadBike->RightVerticalVelocity, (int*)&frontRight.y); + quadBikeItem->Animation.VerticalVelocity = DoQuadDynamics(probe.Position.Floor, quadBikeItem->Animation.VerticalVelocity, (int*)&quadBikeItem->Pose.Position.y); + quadBike->Velocity = DoVehicleWaterMovement(quadBikeItem, laraItem, quadBike->Velocity, QBIKE_RADIUS, &quadBike->TurnRate); + + probe.Position.Floor = (frontLeft.y + frontRight.y) / 2; + short xRot = phd_atan(QBIKE_FRONT, quadBikeItem->Pose.Position.y - probe.Position.Floor); + short zRot = phd_atan(QBIKE_SIDE, probe.Position.Floor - frontLeft.y); + + quadBikeItem->Pose.Orientation.x += ((xRot - quadBikeItem->Pose.Orientation.x) / 2); + quadBikeItem->Pose.Orientation.z += ((zRot - quadBikeItem->Pose.Orientation.z) / 2); + + if (!(quadBike->Flags & QBIKE_FLAG_DEAD)) + { + if (probe.RoomNumber != quadBikeItem->RoomNumber) + { + ItemNewRoom(lara->Vehicle, probe.RoomNumber); + ItemNewRoom(lara->ItemNumber, probe.RoomNumber); + } + + laraItem->Pose = quadBikeItem->Pose; + + AnimateQuadBike(quadBikeItem, laraItem, collide, dead); + AnimateItem(laraItem); + + quadBikeItem->Animation.AnimNumber = Objects[ID_QUAD].animIndex + (laraItem->Animation.AnimNumber - Objects[ID_QUAD_LARA_ANIMS].animIndex); + quadBikeItem->Animation.FrameNumber = g_Level.Anims[quadBikeItem->Animation.AnimNumber].frameBase + (laraItem->Animation.FrameNumber - g_Level.Anims[laraItem->Animation.AnimNumber].frameBase); + + Camera.targetElevation = -ANGLE(30.0f); + + if (quadBike->Flags & QBIKE_FLAG_FALLING) + { + if (quadBikeItem->Pose.Position.y == quadBikeItem->Floor) + { + ExplodeVehicle(laraItem, quadBikeItem); + return false; + } + } + } + + if (laraItem->Animation.ActiveState != QBIKE_STATE_MOUNT_RIGHT && + laraItem->Animation.ActiveState != QBIKE_STATE_MOUNT_LEFT && + laraItem->Animation.ActiveState != QBIKE_STATE_DISMOUNT_RIGHT && + laraItem->Animation.ActiveState != QBIKE_STATE_DISMOUNT_LEFT) + { + Vector3Int pos; + int speed = 0; + short angle = 0; + + for (int i = 0; i < 2; i++) + { + pos.x = QuadBikeEffectsPositions[i].x; + pos.y = QuadBikeEffectsPositions[i].y; + pos.z = QuadBikeEffectsPositions[i].z; + GetJointAbsPosition(quadBikeItem, &pos, QuadBikeEffectsPositions[i].meshNum); + angle = quadBikeItem->Pose.Orientation.y + ((i == 0) ? 0x9000 : 0x7000); + if (quadBikeItem->Animation.Velocity > 32) + { + if (quadBikeItem->Animation.Velocity < 64) + { + speed = 64 - quadBikeItem->Animation.Velocity; + TriggerQuadExhaustSmoke(pos.x, pos.y, pos.z, angle, speed, 1); + } + } + else + { + if (quadBike->SmokeStart < 16) + { + speed = ((quadBike->SmokeStart * 2) + (GetRandomControl() & 7) + (GetRandomControl() & 16)) * 128; + quadBike->SmokeStart++; + } + else if (quadBike->DriftStarting) + speed = (abs(quadBike->Revs) * 2) + ((GetRandomControl() & 7) * 128); + else if ((GetRandomControl() & 3) == 0) + speed = ((GetRandomControl() & 15) + (GetRandomControl() & 16)) * 128; + else + speed = 0; + + TriggerQuadExhaustSmoke(pos.x, pos.y, pos.z, angle, speed, 0); + } + } + } + else + quadBike->SmokeStart = 0; + + return QuadBikeCheckGetOff(quadBikeItem, laraItem); + } +} diff --git a/TombEngine/Objects/TR3/Vehicles/quad_bike.h b/TombEngine/Objects/TR3/Vehicles/quad_bike.h new file mode 100644 index 000000000..eef954ada --- /dev/null +++ b/TombEngine/Objects/TR3/Vehicles/quad_bike.h @@ -0,0 +1,15 @@ +#pragma once +#include "Objects/Utils/VehicleHelpers.h" + +struct CollisionInfo; +struct ItemInfo; + +namespace TEN::Entities::Vehicles +{ + void InitialiseQuadBike(short itemNumber); + + void QuadBikePlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + void DoQuadBikeMount(ItemInfo* quadBikeItem, ItemInfo* laraItem, VehicleMountType mountType); + + bool QuadBikeControl(ItemInfo* laraItem, CollisionInfo* coll); +} diff --git a/TombEngine/Objects/TR3/Vehicles/quad_bike_info.h b/TombEngine/Objects/TR3/Vehicles/quad_bike_info.h new file mode 100644 index 000000000..8e555c6ae --- /dev/null +++ b/TombEngine/Objects/TR3/Vehicles/quad_bike_info.h @@ -0,0 +1,28 @@ +#pragma once + +namespace TEN::Entities::Vehicles +{ + struct QuadBikeInfo + { + short TurnRate = 0; + short FrontRot = 0; + short RearRot = 0; + short MomentumAngle = 0; + short ExtraRotation = 0; + + int Velocity = 0; + int LeftVerticalVelocity = 0; + int RightVerticalVelocity = 0; + + int Revs = 0; + int EngineRevs = 0; + int Pitch = 0; + + int SmokeStart = 0; + bool CanStartDrift = 0; + bool DriftStarting = 0; + bool NoDismount = 0; + + char Flags = NULL; + }; +} diff --git a/TombEngine/Objects/TR3/Vehicles/quad_info.h b/TombEngine/Objects/TR3/Vehicles/quad_info.h deleted file mode 100644 index b4f31d07d..000000000 --- a/TombEngine/Objects/TR3/Vehicles/quad_info.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -namespace TEN::Entities::Vehicles -{ - struct QuadInfo - { - short TurnRate; - short FrontRot; - short RearRot; - short MomentumAngle; - short ExtraRotation; - - int Velocity; - int LeftVerticalVelocity; - int RightVerticalVelocity; - - int Revs; - int EngineRevs; - int Pitch; - - int SmokeStart; - bool CanStartDrift; - bool DriftStarting; - bool NoDismount; - - char Flags; - }; -} diff --git a/TombEngine/Objects/TR3/Vehicles/rubber_boat.cpp b/TombEngine/Objects/TR3/Vehicles/rubber_boat.cpp index 9559ae119..eb3872f98 100644 --- a/TombEngine/Objects/TR3/Vehicles/rubber_boat.cpp +++ b/TombEngine/Objects/TR3/Vehicles/rubber_boat.cpp @@ -10,44 +10,47 @@ #include "Game/Lara/lara.h" #include "Game/Lara/lara_helpers.h" #include "Objects/TR3/Vehicles/rubber_boat_info.h" +#include "Objects/Utils/VehicleHelpers.h" #include "Sound/sound.h" #include "Specific/input.h" #include "Specific/level.h" #include "Specific/setup.h" #include "Renderer/Renderer11Enums.h" +using std::vector; using namespace TEN::Input; namespace TEN::Entities::Vehicles { - #define RBOAT_SLIP 10 - #define RBOAT_SIDE_SLIP 30 + const vector RubberBoatMountTypes = + { + VehicleMountType::LevelStart, + VehicleMountType::Left, + VehicleMountType::Right, + VehicleMountType::Jump + }; - #define RBOAT_FRONT 750 - #define RBOAT_SIDE 300 - #define RBOAT_RADIUS 500 - #define RBOAT_SNOW 500 + constexpr auto RBOAT_RADIUS = 500; + constexpr auto RBOAT_FRONT = 750; + constexpr auto RBOAT_SIDE = 300; + constexpr auto RBOAT_SLIP = 10; + constexpr auto RBOAT_SIDE_SLIP = 30; + constexpr auto RBOAT_MOUNT_DISTANCE = CLICK(2.25f); - #define RBOAT_MAX_VELOCITY 110 - #define RBOAT_SLOW_VELOCITY (RBOAT_MAX_VELOCITY / 3) - #define RBOAT_FAST_VELOCITY (RBOAT_MAX_VELOCITY + 75) - #define RBOAT_MIN_VELOCITY 20 - #define RBOAT_MAX_BACK -20 - #define RBOAT_MAX_KICK -80 + constexpr auto RBOAT_VELOCITY_ACCEL = 5; + constexpr auto RBOAT_VELOCITY_DECEL = 1; + constexpr auto RBOAT_VELOCITY_BRAKE_DECEL = 5; + constexpr auto RBOAT_REVERSE_VELOCITY_DECEL = 2; - #define RBOAT_ACCELERATION 5 - #define RBOAT_BRAKE 5 - #define RBOAT_SLOW_DOWN 1 - #define RBOAT_UNDO_TURN ANGLE(0.25f) - #define RBOAT_TURN (ANGLE(0.25f) / 2) + constexpr auto RBOAT_VELOCITY_MIN = 20; + constexpr auto RBOAT_SLOW_VELOCITY_MAX = 37; + constexpr auto RBOAT_NORMAL_VELOCITY_MAX = 110; + constexpr auto RBOAT_FAST_VELOCITY_MAX = 185; + constexpr auto RBOAT_REVERSE_VELOCITY_MAX = 20; - #define RBOAT_IN_SPEED (IN_SPRINT | IN_CROUCH) - #define RBOAT_IN_SLOW IN_WALK - #define RBOAT_IN_DISMOUNT IN_ROLL - #define RBOAT_IN_FORWARD IN_ACTION - #define RBOAT_IN_BACK IN_JUMP - #define RBOAT_IN_LEFT IN_LEFT - #define RBOAT_IN_RIGHT IN_RIGHT + #define RBOAT_TURN_RATE_ACCEL (ANGLE(0.25f) / 2) + #define RBOAT_TURN_RATE_DECEL ANGLE(0.25f) + #define RBOAT_TURN_RATE_MAX ANGLE(4.0f) enum RubberBoatState { @@ -90,28 +93,90 @@ namespace TEN::Entities::Vehicles RBOAT_ANIM_TURN_RIGHT_START = 22 }; - enum RubberBoatMountType + RubberBoatInfo* GetRubberBoatInfo(ItemInfo* rBoatItem) { - RBOAT_MOUNT_NONE = 0, - RBOAT_MOUNT_LEFT = 1, - RBOAT_MOUNT_RIGHT = 2, - RBOAT_MOUNT_JUMP = 3, - RBOAT_MOUNT_LEVEL_START = 4 - }; + return (RubberBoatInfo*)rBoatItem->Data; + } void InitialiseRubberBoat(short itemNumber) { auto* rBoatItem = &g_Level.Items[itemNumber]; rBoatItem->Data = RubberBoatInfo(); - auto* rBoat = (RubberBoatInfo*)rBoatItem->Data; + auto* rBoat = GetRubberBoatInfo(rBoatItem); + } - rBoat->TurnRate = 0; - rBoat->LeanAngle = 0; - rBoat->ExtraRotation = 0; - rBoat->LeftVerticalVelocity = 0; - rBoat->RightVerticalVelocity = 0; - rBoat->Water = 0; - rBoat->Pitch = 0; + void RubberBoatPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) + { + auto* rBoatItem = &g_Level.Items[itemNumber]; + auto* lara = GetLaraInfo(laraItem); + + if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) + return; + + auto mountType = GetVehicleMountType(rBoatItem, laraItem, coll, RubberBoatMountTypes, RBOAT_MOUNT_DISTANCE, LARA_HEIGHT); + if (mountType == VehicleMountType::None) + { + coll->Setup.EnableObjectPush = true; + ObjectCollision(itemNumber, laraItem, coll); + } + else + { + lara->Vehicle = itemNumber; + DoRubberBoatMount(rBoatItem, laraItem, mountType); + + if (g_Level.Items[itemNumber].Status != ITEM_ACTIVE) + { + AddActiveItem(itemNumber); + g_Level.Items[itemNumber].Status = ITEM_ACTIVE; + } + } + } + + void DoRubberBoatMount(ItemInfo* rBoatItem, ItemInfo* laraItem, VehicleMountType mountType) + { + auto* lara = GetLaraInfo(laraItem); + + switch (mountType) + { + case VehicleMountType::LevelStart: + laraItem->Animation.AnimNumber = Objects[ID_RUBBER_BOAT_LARA_ANIMS].animIndex + RBOAT_ANIM_IDLE; + laraItem->Animation.ActiveState = RBOAT_STATE_IDLE; + laraItem->Animation.TargetState = RBOAT_STATE_IDLE; + break; + + case VehicleMountType::Left: + laraItem->Animation.AnimNumber = Objects[ID_RUBBER_BOAT_LARA_ANIMS].animIndex + RBOAT_ANIM_MOUNT_LEFT; + laraItem->Animation.ActiveState = RBOAT_STATE_MOUNT; + laraItem->Animation.TargetState = RBOAT_STATE_MOUNT; + break; + + case VehicleMountType::Right: + laraItem->Animation.AnimNumber = Objects[ID_RUBBER_BOAT_LARA_ANIMS].animIndex + RBOAT_ANIM_MOUNT_RIGHT; + laraItem->Animation.ActiveState = RBOAT_STATE_MOUNT; + laraItem->Animation.TargetState = RBOAT_STATE_MOUNT; + break; + + default: + case VehicleMountType::Jump: + laraItem->Animation.AnimNumber = Objects[ID_RUBBER_BOAT_LARA_ANIMS].animIndex + RBOAT_ANIM_MOUNT_JUMP; + laraItem->Animation.ActiveState = RBOAT_STATE_MOUNT; + laraItem->Animation.TargetState = RBOAT_STATE_MOUNT; + break; + } + laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; + + if (laraItem->RoomNumber != rBoatItem->RoomNumber) + ItemNewRoom(lara->ItemNumber, rBoatItem->RoomNumber); + + laraItem->Pose.Position = rBoatItem->Pose.Position; + laraItem->Pose.Position.y -= 5; + laraItem->Pose.Orientation = Vector3Shrt(0, rBoatItem->Pose.Orientation.y, 0); + laraItem->Animation.IsAirborne = false; + laraItem->Animation.Velocity = 0; + laraItem->Animation.VerticalVelocity = 0; + lara->Control.WaterStatus = WaterStatus::Dry; + + AnimateItem(laraItem); } void DrawRubberBoat(ItemInfo* rBoatItem) @@ -126,95 +191,10 @@ namespace TEN::Entities::Vehicles */ } - RubberBoatMountType GetRubberBoatMountType(ItemInfo* laraItem, short itemNumber, CollisionInfo* coll) + static void DoRubberBoatShift(int itemNumber, ItemInfo* laraItem) { - auto* lara = GetLaraInfo(laraItem); - auto* rBoat = &g_Level.Items[itemNumber]; - - RubberBoatMountType mountType = RBOAT_MOUNT_NONE; - - if (!(TrInput & IN_ACTION) || - lara->Control.HandStatus != HandStatus::Free || - laraItem->Animation.Airborne || - rBoat->Animation.Velocity) - { - return mountType; - } - - short x = laraItem->Pose.Position.x - rBoat->Pose.Position.x; - short z = laraItem->Pose.Position.z - rBoat->Pose.Position.z; - - int distance = z * phd_cos(-rBoat->Pose.Orientation.y) - x * phd_sin(-rBoat->Pose.Orientation.y); - if (distance > CLICK(2)) - return mountType; - - short deltaAngle = rBoat->Pose.Orientation.y - laraItem->Pose.Orientation.y; - if (lara->Control.WaterStatus == WaterStatus::TreadWater || lara->Control.WaterStatus == WaterStatus::Wade) - { - if (deltaAngle > ANGLE(45.0f) && deltaAngle < ANGLE(135.0f)) - mountType = RBOAT_MOUNT_LEFT; - if (deltaAngle < -ANGLE(135.0f) && deltaAngle > -ANGLE(45.0f)) - mountType = RBOAT_MOUNT_RIGHT; - } - else if (lara->Control.WaterStatus == WaterStatus::Dry) - { - if (laraItem->Animation.VerticalVelocity > 0) - { - if ((laraItem->Pose.Position.y + CLICK(2)) > rBoat->Pose.Position.y) - mountType = RBOAT_MOUNT_JUMP; - } - else if (laraItem->Animation.VerticalVelocity == 0) - { - if (deltaAngle > -ANGLE(135.0f) && deltaAngle < ANGLE(135.0f)) - { - if (laraItem->Pose.Position == rBoat->Pose.Position) - mountType = RBOAT_MOUNT_LEVEL_START; - else - mountType = RBOAT_MOUNT_JUMP; - } - } - } - - if (!mountType) - return RBOAT_MOUNT_NONE; - - if (!TestBoundsCollide(rBoat, laraItem, coll->Setup.Radius)) - return RBOAT_MOUNT_NONE; - - if (!TestCollision(rBoat, laraItem)) - return RBOAT_MOUNT_NONE; - - return mountType; - } - - int TestWaterHeight(ItemInfo* rBoatItem, int zOffset, int xOffset, Vector3Int* pos) - { - float sinX = phd_sin(rBoatItem->Pose.Orientation.x); - float sinY = phd_sin(rBoatItem->Pose.Orientation.y); - float cosY = phd_cos(rBoatItem->Pose.Orientation.y); - float sinZ = phd_sin(rBoatItem->Pose.Orientation.z); - - pos->x = rBoatItem->Pose.Position.x + (zOffset * sinY) + (xOffset * cosY); - pos->y = rBoatItem->Pose.Position.y - (zOffset * sinX) + (xOffset * sinZ); - pos->z = rBoatItem->Pose.Position.z + (zOffset * cosY) - (xOffset * sinY); - - auto probe = GetCollision(pos->x, pos->y, pos->z, rBoatItem->RoomNumber); - auto height = GetWaterHeight(pos->x, pos->y, pos->z, probe.RoomNumber); - - if (height == NO_HEIGHT) - { - height = probe.Position.Floor; - if (height == NO_HEIGHT) - return height; - } - - return (height - 5); - } - - static void DoRubberBoatShift(ItemInfo* laraItem, int itemNumber) - { - auto* lara = GetLaraInfo(laraItem); auto* boatItem = &g_Level.Items[itemNumber]; + auto* lara = GetLaraInfo(laraItem); int itemNumber2 = g_Level.Rooms[boatItem->RoomNumber].itemNumber; while (itemNumber2 != NO_ITEM) @@ -368,37 +348,26 @@ namespace TEN::Entities::Vehicles return 0; } - static int RubberBoatDynamics(ItemInfo* laraItem, short itemNumber) + static int RubberBoatDynamics(short itemNumber, ItemInfo* laraItem) { - auto* lara = GetLaraInfo(laraItem); auto* rBoatItem = &g_Level.Items[itemNumber]; - auto* rBoat = (RubberBoatInfo*)rBoatItem->Data; + auto* rBoat = GetRubberBoatInfo(rBoatItem); + auto* lara = GetLaraInfo(laraItem); rBoatItem->Pose.Orientation.z -= rBoat->LeanAngle; Vector3Int frontLeftOld, frontRightOld, backLeftOld, backRightOld, frontOld; - int heightFrontLeftOld = TestWaterHeight(rBoatItem, RBOAT_FRONT, -RBOAT_SIDE, &frontLeftOld); - int heightFrontRightOld = TestWaterHeight(rBoatItem, RBOAT_FRONT, RBOAT_SIDE, &frontRightOld); - int heightBackLeftOld = TestWaterHeight(rBoatItem, -RBOAT_FRONT, -RBOAT_SIDE, &backLeftOld); - int heightBackRightOld = TestWaterHeight(rBoatItem, -RBOAT_FRONT, RBOAT_SIDE, &backRightOld); - int heightFrontOld = TestWaterHeight(rBoatItem, 1000, 0, &frontOld); + int heightFrontLeftOld = GetVehicleWaterHeight(rBoatItem, RBOAT_FRONT, -RBOAT_SIDE, true, &frontLeftOld); + int heightFrontRightOld = GetVehicleWaterHeight(rBoatItem, RBOAT_FRONT, RBOAT_SIDE, true, &frontRightOld); + int heightBackLeftOld = GetVehicleWaterHeight(rBoatItem, -RBOAT_FRONT, -RBOAT_SIDE, true, &backLeftOld); + int heightBackRightOld = GetVehicleWaterHeight(rBoatItem, -RBOAT_FRONT, RBOAT_SIDE, true, &backRightOld); + int heightFrontOld = GetVehicleWaterHeight(rBoatItem, 1000, 0, true, &frontOld); Vector3Int old; old.x = rBoatItem->Pose.Position.x; old.y = rBoatItem->Pose.Position.y; old.z = rBoatItem->Pose.Position.z; - if (backLeftOld.y > heightBackLeftOld) - backLeftOld.y = heightBackLeftOld; - if (backRightOld.y > heightBackRightOld) - backRightOld.y = heightBackRightOld; - if (frontLeftOld.y > heightFrontLeftOld) - frontLeftOld.y = heightFrontLeftOld; - if (frontRightOld.y > heightFrontRightOld) - frontRightOld.y = heightFrontRightOld; - if (frontOld.y > heightFrontOld) - frontOld.y = heightFrontOld; - rBoatItem->Pose.Orientation.y += rBoat->TurnRate + rBoat->ExtraRotation; rBoat->LeanAngle = rBoat->TurnRate * 6; @@ -427,30 +396,30 @@ namespace TEN::Entities::Vehicles moved.x = rBoatItem->Pose.Position.x; moved.z = rBoatItem->Pose.Position.z; - DoRubberBoatShift(laraItem, itemNumber); + DoRubberBoatShift(itemNumber, laraItem); Vector3Int frontLeft, frontRight, backRight, backLeft, front; short rotation = 0; - int heightBackLeft = TestWaterHeight(rBoatItem, -RBOAT_FRONT, -RBOAT_SIDE, &backLeft); + int heightBackLeft = GetVehicleWaterHeight(rBoatItem, -RBOAT_FRONT, -RBOAT_SIDE, false, &backLeft); if (heightBackLeft < (backLeftOld.y - CLICK(0.5f))) rotation = DoRubberBoatShift2(rBoatItem, &backLeft, &backLeftOld); - int heightBackRight = TestWaterHeight(rBoatItem, -RBOAT_FRONT, RBOAT_SIDE, &backRight); + int heightBackRight = GetVehicleWaterHeight(rBoatItem, -RBOAT_FRONT, RBOAT_SIDE, false, &backRight); if (heightBackRight < (backRightOld.y - CLICK(0.5f))) rotation += DoRubberBoatShift2(rBoatItem, &backRight, &backRightOld); - int heightFrontLeft = TestWaterHeight(rBoatItem, RBOAT_FRONT, -RBOAT_SIDE, &frontLeft); + int heightFrontLeft = GetVehicleWaterHeight(rBoatItem, RBOAT_FRONT, -RBOAT_SIDE, false, &frontLeft); if (heightFrontLeft < (frontLeftOld.y - CLICK(0.5f))) rotation += DoRubberBoatShift2(rBoatItem, &frontLeft, &frontLeftOld); - int heightFrontRight = TestWaterHeight(rBoatItem, RBOAT_FRONT, RBOAT_SIDE, &frontRight); + int heightFrontRight = GetVehicleWaterHeight(rBoatItem, RBOAT_FRONT, RBOAT_SIDE, false, &frontRight); if (heightFrontRight < (frontRightOld.y - CLICK(0.5f))) rotation += DoRubberBoatShift2(rBoatItem, &frontRight, &frontRightOld); if (!slip) { - int heightFront = TestWaterHeight(rBoatItem, 1000, 0, &front); + int heightFront = GetVehicleWaterHeight(rBoatItem, 1000, 0, false, &front); if (heightFront < (frontOld.y - CLICK(0.5f))) DoRubberBoatShift2(rBoatItem, &front, &frontOld); } @@ -475,7 +444,7 @@ namespace TEN::Entities::Vehicles int newVelocity = (rBoatItem->Pose.Position.z - old.z) * phd_cos(rBoatItem->Pose.Orientation.y) + (rBoatItem->Pose.Position.x - old.x) * phd_sin(rBoatItem->Pose.Orientation.y); if (lara->Vehicle == itemNumber && - rBoatItem->Animation.Velocity > (RBOAT_MAX_VELOCITY + RBOAT_ACCELERATION) && + rBoatItem->Animation.Velocity > (RBOAT_NORMAL_VELOCITY_MAX + RBOAT_VELOCITY_ACCEL) && newVelocity < rBoatItem->Animation.Velocity - 10) { DoDamage(laraItem, rBoatItem->Animation.Velocity); @@ -486,7 +455,7 @@ namespace TEN::Entities::Vehicles if (slip) { - if (rBoatItem->Animation.Velocity <= RBOAT_MAX_VELOCITY + 10) + if (rBoatItem->Animation.Velocity <= RBOAT_NORMAL_VELOCITY_MAX + 10) rBoatItem->Animation.Velocity = newVelocity; } else @@ -497,8 +466,8 @@ namespace TEN::Entities::Vehicles rBoatItem->Animation.Velocity = newVelocity; } - if (rBoatItem->Animation.Velocity < RBOAT_MAX_BACK) - rBoatItem->Animation.Velocity = RBOAT_MAX_BACK; + if (rBoatItem->Animation.Velocity < -RBOAT_REVERSE_VELOCITY_MAX) + rBoatItem->Animation.Velocity = -RBOAT_REVERSE_VELOCITY_MAX; } return collide; @@ -530,90 +499,90 @@ namespace TEN::Entities::Vehicles return verticalVelocity; } - bool RubberBoatUserControl(ItemInfo* laraItem, ItemInfo* rBoatItem) + bool RubberBoatUserControl(ItemInfo* rBoatItem, ItemInfo* laraItem) { - auto* rBoat = (RubberBoatInfo*)rBoatItem->Data; + auto* rBoat = GetRubberBoatInfo(rBoatItem); bool noTurn = true; if (rBoatItem->Pose.Position.y >= (rBoat->Water - 128) && rBoat->Water != NO_HEIGHT) { - if (!(TrInput & RBOAT_IN_DISMOUNT) && !(TrInput & IN_LOOK) || rBoatItem->Animation.Velocity) + if (!(TrInput & VEHICLE_IN_DISMOUNT) && !(TrInput & IN_LOOK) || rBoatItem->Animation.Velocity) { - if ((TrInput & RBOAT_IN_LEFT && !(TrInput & RBOAT_IN_BACK)) || - (TrInput & RBOAT_IN_RIGHT && TrInput & RBOAT_IN_BACK)) + if ((TrInput & VEHICLE_IN_LEFT && !(TrInput & VEHICLE_IN_REVERSE)) || + (TrInput & VEHICLE_IN_RIGHT && TrInput & VEHICLE_IN_REVERSE)) { if (rBoat->TurnRate > 0) - rBoat->TurnRate -= ANGLE(0.25f); + rBoat->TurnRate -= RBOAT_TURN_RATE_DECEL; else { - rBoat->TurnRate -= ANGLE(0.25f) / 2; - if (rBoat->TurnRate < -ANGLE(4.0f)) - rBoat->TurnRate = -ANGLE(4.0f); + rBoat->TurnRate -= RBOAT_TURN_RATE_ACCEL; + if (rBoat->TurnRate < -RBOAT_TURN_RATE_MAX) + rBoat->TurnRate = -RBOAT_TURN_RATE_MAX; } noTurn = false; } - else if ((TrInput & RBOAT_IN_RIGHT && !(TrInput & RBOAT_IN_BACK)) || - (TrInput & RBOAT_IN_LEFT && TrInput & RBOAT_IN_BACK)) + else if ((TrInput & VEHICLE_IN_RIGHT && !(TrInput & VEHICLE_IN_REVERSE)) || + (TrInput & VEHICLE_IN_LEFT && TrInput & VEHICLE_IN_REVERSE)) { if (rBoat->TurnRate < 0) - rBoat->TurnRate += ANGLE(0.25f); + rBoat->TurnRate += RBOAT_TURN_RATE_DECEL; else { - rBoat->TurnRate += ANGLE(0.25f) / 2; - if (rBoat->TurnRate > ANGLE(4.0f)) - rBoat->TurnRate = ANGLE(4.0f); + rBoat->TurnRate += RBOAT_TURN_RATE_ACCEL; + if (rBoat->TurnRate > RBOAT_TURN_RATE_MAX) + rBoat->TurnRate = RBOAT_TURN_RATE_MAX; } noTurn = false; } - if (TrInput & RBOAT_IN_BACK) + if (TrInput & VEHICLE_IN_REVERSE) { if (rBoatItem->Animation.Velocity > 0) - rBoatItem->Animation.Velocity -= 5; - else if (rBoatItem->Animation.Velocity > -20) - rBoatItem->Animation.Velocity += -2; + rBoatItem->Animation.Velocity -= RBOAT_VELOCITY_BRAKE_DECEL; + else if (rBoatItem->Animation.Velocity > -RBOAT_REVERSE_VELOCITY_MAX) + rBoatItem->Animation.Velocity -= RBOAT_REVERSE_VELOCITY_DECEL; } - else if (TrInput & RBOAT_IN_FORWARD) + else if (TrInput & VEHICLE_IN_ACCELERATE) { int maxVelocity; - if (TrInput & RBOAT_IN_SPEED) - maxVelocity = 185; + if (TrInput & VEHICLE_IN_SPEED) + maxVelocity = RBOAT_FAST_VELOCITY_MAX; else - maxVelocity = (TrInput & RBOAT_IN_SLOW) ? 37 : 110; + maxVelocity = (TrInput & VEHICLE_IN_SLOW) ? RBOAT_SLOW_VELOCITY_MAX : RBOAT_NORMAL_VELOCITY_MAX; if (rBoatItem->Animation.Velocity < maxVelocity) - rBoatItem->Animation.Velocity += 3 + (5 * rBoatItem->Animation.Velocity) / (maxVelocity * 2); - else if (rBoatItem->Animation.Velocity > (maxVelocity + 1)) - rBoatItem->Animation.Velocity -= 1; + rBoatItem->Animation.Velocity += (RBOAT_VELOCITY_ACCEL / 2 + 1) + (RBOAT_VELOCITY_ACCEL * rBoatItem->Animation.Velocity) / (maxVelocity * 2); + else if (rBoatItem->Animation.Velocity > (maxVelocity + RBOAT_VELOCITY_DECEL)) + rBoatItem->Animation.Velocity -= RBOAT_VELOCITY_DECEL; } - else if (TrInput & (RBOAT_IN_LEFT | RBOAT_IN_RIGHT) && + else if (TrInput & (VEHICLE_IN_LEFT | VEHICLE_IN_RIGHT) && rBoatItem->Animation.Velocity >= 0 && - rBoatItem->Animation.Velocity < 20) + rBoatItem->Animation.Velocity < RBOAT_VELOCITY_MIN) { - if (!(TrInput & RBOAT_IN_DISMOUNT) && rBoatItem->Animation.Velocity == 0) - rBoatItem->Animation.Velocity = 20; + if (!(TrInput & VEHICLE_IN_DISMOUNT) && rBoatItem->Animation.Velocity == 0) + rBoatItem->Animation.Velocity = RBOAT_VELOCITY_MIN; } - else if (rBoatItem->Animation.Velocity > 1) - rBoatItem->Animation.Velocity -= 1; + else if (rBoatItem->Animation.Velocity > RBOAT_VELOCITY_DECEL) + rBoatItem->Animation.Velocity -= RBOAT_VELOCITY_DECEL; else rBoatItem->Animation.Velocity = 0; } else { - if (TrInput & (RBOAT_IN_LEFT | RBOAT_IN_RIGHT) && + if (TrInput & (VEHICLE_IN_LEFT | VEHICLE_IN_RIGHT) && rBoatItem->Animation.Velocity >= 0 && - rBoatItem->Animation.Velocity < 20) + rBoatItem->Animation.Velocity < RBOAT_VELOCITY_MIN) { - if (!(TrInput & RBOAT_IN_DISMOUNT) && rBoatItem->Animation.Velocity == 0) - rBoatItem->Animation.Velocity = 20; + if (!(TrInput & VEHICLE_IN_DISMOUNT) && rBoatItem->Animation.Velocity == 0) + rBoatItem->Animation.Velocity = RBOAT_VELOCITY_MIN; } - else if (rBoatItem->Animation.Velocity > 1) - rBoatItem->Animation.Velocity -= 1; + else if (rBoatItem->Animation.Velocity > RBOAT_VELOCITY_DECEL) + rBoatItem->Animation.Velocity -= RBOAT_VELOCITY_DECEL; else rBoatItem->Animation.Velocity = 0; @@ -625,59 +594,6 @@ namespace TEN::Entities::Vehicles return noTurn; } - void RubberBoatCollision(short itemNum, ItemInfo* laraItem, CollisionInfo* coll) - { - auto* lara = GetLaraInfo(laraItem); - - if (laraItem->HitPoints <= 0 || lara->Vehicle != NO_ITEM) - return; - - auto* item = &g_Level.Items[itemNum]; - int mountType = GetRubberBoatMountType(laraItem, itemNum, coll); - - if (!mountType) - { - coll->Setup.EnableObjectPush = true; - ObjectCollision(itemNum, laraItem, coll); - return; - } - - lara->Vehicle = itemNum; - - if (mountType == 1) - laraItem->Animation.AnimNumber = Objects[ID_RUBBER_BOAT_LARA_ANIMS].animIndex + RBOAT_ANIM_MOUNT_RIGHT; - else if (mountType == 2) - laraItem->Animation.AnimNumber = Objects[ID_RUBBER_BOAT_LARA_ANIMS].animIndex + RBOAT_ANIM_MOUNT_LEFT; - else if (mountType == 3) - laraItem->Animation.AnimNumber = Objects[ID_RUBBER_BOAT_LARA_ANIMS].animIndex + RBOAT_ANIM_MOUNT_JUMP; - else - laraItem->Animation.AnimNumber = Objects[ID_RUBBER_BOAT_LARA_ANIMS].animIndex + RBOAT_ANIM_IDLE; - - laraItem->Pose.Position.x = item->Pose.Position.x; - laraItem->Pose.Position.y = item->Pose.Position.y - 5; - laraItem->Pose.Position.z = item->Pose.Position.z; - laraItem->Pose.Orientation.x = 0; - laraItem->Pose.Orientation.y = item->Pose.Orientation.y; - laraItem->Pose.Orientation.z = 0; - laraItem->Animation.Airborne = false; - laraItem->Animation.Velocity = 0; - laraItem->Animation.VerticalVelocity = 0; - laraItem->Animation.ActiveState = 0; - laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - lara->Control.WaterStatus = WaterStatus::Dry; - - if (laraItem->RoomNumber != item->RoomNumber) - ItemNewRoom(lara->ItemNumber, item->RoomNumber); - - AnimateItem(laraItem); - - if (g_Level.Items[itemNum].Status != ITEM_ACTIVE) - { - AddActiveItem(itemNum); - g_Level.Items[itemNum].Status = ITEM_ACTIVE; - } - } - static bool TestRubberBoatDismount(ItemInfo* laraItem, int direction) { auto* lara = GetLaraInfo(laraItem); @@ -710,9 +626,9 @@ namespace TEN::Entities::Vehicles return true; } - void RubberBoatAnimation(ItemInfo* laraItem, ItemInfo* rBoatItem, int collide) + void RubberBoatAnimation(ItemInfo* rBoatItem, ItemInfo* laraItem, int collide) { - auto* rBoat = (RubberBoatInfo*)rBoatItem->Data; + auto* rBoat = GetRubberBoatInfo(rBoatItem); if (laraItem->HitPoints <= 0) { @@ -750,7 +666,7 @@ namespace TEN::Entities::Vehicles switch (laraItem->Animation.ActiveState) { case RBOAT_STATE_IDLE: - if (TrInput & RBOAT_IN_DISMOUNT) + if (TrInput & VEHICLE_IN_DISMOUNT) { if (rBoatItem->Animation.Velocity == 0) { @@ -770,9 +686,9 @@ namespace TEN::Entities::Vehicles if (rBoatItem->Animation.Velocity <= 0) laraItem->Animation.TargetState = RBOAT_STATE_IDLE; - if (TrInput & RBOAT_IN_RIGHT) + if (TrInput & VEHICLE_IN_RIGHT) laraItem->Animation.TargetState = RBOAT_STATE_TURN_RIGHT; - else if (TrInput & RBOAT_IN_LEFT) + else if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = RBOAT_STATE_TURN_LEFT; break; @@ -784,7 +700,7 @@ namespace TEN::Entities::Vehicles case RBOAT_STATE_TURN_RIGHT: if (rBoatItem->Animation.Velocity <= 0) laraItem->Animation.TargetState = RBOAT_STATE_IDLE; - else if (!(TrInput & RBOAT_IN_RIGHT)) + else if (!(TrInput & VEHICLE_IN_RIGHT)) laraItem->Animation.TargetState = RBOAT_STATE_MOVING; break; @@ -792,7 +708,7 @@ namespace TEN::Entities::Vehicles case RBOAT_STATE_TURN_LEFT: if (rBoatItem->Animation.Velocity <= 0) laraItem->Animation.TargetState = RBOAT_STATE_IDLE; - else if (!(TrInput & RBOAT_IN_LEFT)) + else if (!(TrInput & VEHICLE_IN_LEFT)) laraItem->Animation.TargetState = RBOAT_STATE_MOVING; break; @@ -863,7 +779,7 @@ namespace TEN::Entities::Vehicles } } - void DoRubberBoatDismount(ItemInfo* laraItem, ItemInfo* rBoatItem) + void DoRubberBoatDismount(ItemInfo* rBoatItem, ItemInfo* laraItem) { auto* lara = GetLaraInfo(laraItem); @@ -881,7 +797,7 @@ namespace TEN::Entities::Vehicles laraItem->Animation.TargetState = LS_JUMP_FORWARD; laraItem->Pose.Orientation.x = 0; laraItem->Pose.Orientation.z = 0; - laraItem->Animation.Airborne = true; + laraItem->Animation.IsAirborne = true; laraItem->Animation.Velocity = 20; laraItem->Animation.VerticalVelocity = -40; lara->Vehicle = NO_ITEM; @@ -908,10 +824,10 @@ namespace TEN::Entities::Vehicles void RubberBoatControl(short itemNumber) { + auto* rBoatItem = &g_Level.Items[itemNumber]; + auto* rBoat = GetRubberBoatInfo(rBoatItem); auto* laraItem = LaraItem; auto* lara = GetLaraInfo(laraItem); - auto* rBoatItem = &g_Level.Items[itemNumber]; - auto* rBoat = (RubberBoatInfo*)rBoatItem->Data; bool noTurn = true; bool drive = false; @@ -919,10 +835,9 @@ namespace TEN::Entities::Vehicles int pitch, height, ofs, nowake; Vector3Int frontLeft, frontRight; - int collide = RubberBoatDynamics(laraItem, itemNumber); - int heightFrontLeft = TestWaterHeight(rBoatItem, RBOAT_FRONT, -RBOAT_SIDE, &frontLeft); - int heightFrontRight = TestWaterHeight(rBoatItem, RBOAT_FRONT, RBOAT_SIDE, &frontRight); - + int collide = RubberBoatDynamics(itemNumber, laraItem); + int heightFrontLeft = GetVehicleWaterHeight(rBoatItem, RBOAT_FRONT, -RBOAT_SIDE, true, &frontLeft); + int heightFrontRight = GetVehicleWaterHeight(rBoatItem, RBOAT_FRONT, RBOAT_SIDE, true, &frontRight); if (lara->Vehicle == itemNumber) { @@ -945,24 +860,24 @@ namespace TEN::Entities::Vehicles default: drive = true; - noTurn = RubberBoatUserControl(laraItem, rBoatItem); + noTurn = RubberBoatUserControl(rBoatItem, laraItem); break; } } else { - if (rBoatItem->Animation.Velocity > RBOAT_SLOW_DOWN) - rBoatItem->Animation.Velocity -= RBOAT_SLOW_DOWN; + if (rBoatItem->Animation.Velocity > RBOAT_VELOCITY_DECEL) + rBoatItem->Animation.Velocity -= RBOAT_VELOCITY_DECEL; else rBoatItem->Animation.Velocity = 0; } if (noTurn) { - if (rBoat->TurnRate < -RBOAT_UNDO_TURN) - rBoat->TurnRate += RBOAT_UNDO_TURN; - else if (rBoat->TurnRate > RBOAT_UNDO_TURN) - rBoat->TurnRate -= RBOAT_UNDO_TURN; + if (rBoat->TurnRate < -RBOAT_TURN_RATE_DECEL) + rBoat->TurnRate += RBOAT_TURN_RATE_DECEL; + else if (rBoat->TurnRate > RBOAT_TURN_RATE_DECEL) + rBoat->TurnRate -= RBOAT_TURN_RATE_DECEL; else rBoat->TurnRate = 0; } @@ -999,7 +914,7 @@ namespace TEN::Entities::Vehicles if (lara->Vehicle == itemNumber) { - RubberBoatAnimation(laraItem, rBoatItem, collide); + RubberBoatAnimation(rBoatItem, laraItem, collide); if (probe.RoomNumber != rBoatItem->RoomNumber) { @@ -1033,14 +948,14 @@ namespace TEN::Entities::Vehicles rBoat->Pitch += ((pitch - rBoat->Pitch) / 4); if (rBoatItem->Animation.Velocity > 8) - SoundEffect(SFX_TR3_VEHICLE_RUBBERBOAT_MOVING, &rBoatItem->Pose, SoundEnvironment::Land, 0.5f + (float)abs(rBoat->Pitch) / (float)RBOAT_MAX_VELOCITY); + SoundEffect(SFX_TR3_VEHICLE_RUBBERBOAT_MOVING, &rBoatItem->Pose, SoundEnvironment::Land, 0.5f + (float)abs(rBoat->Pitch) / (float)RBOAT_NORMAL_VELOCITY_MAX); else if (drive) - SoundEffect(SFX_TR3_VEHICLE_RUBBERBOAT_IDLE, &rBoatItem->Pose, SoundEnvironment::Land, 0.5f + (float)abs(rBoat->Pitch) / (float)RBOAT_MAX_VELOCITY); + SoundEffect(SFX_TR3_VEHICLE_RUBBERBOAT_IDLE, &rBoatItem->Pose, SoundEnvironment::Land, 0.5f + (float)abs(rBoat->Pitch) / (float)RBOAT_NORMAL_VELOCITY_MAX); if (lara->Vehicle != itemNumber) return; - DoRubberBoatDismount(laraItem, rBoatItem); + DoRubberBoatDismount(rBoatItem, laraItem); short probedRoomNumber = GetCollision(rBoatItem->Pose.Position.x, rBoatItem->Pose.Position.y + 128, rBoatItem->Pose.Position.z, rBoatItem->RoomNumber).RoomNumber; height = GetWaterHeight(rBoatItem->Pose.Position.x, rBoatItem->Pose.Position.y + 128, rBoatItem->Pose.Position.z, probedRoomNumber); diff --git a/TombEngine/Objects/TR3/Vehicles/rubber_boat.h b/TombEngine/Objects/TR3/Vehicles/rubber_boat.h index 48a81894d..7ab320c20 100644 --- a/TombEngine/Objects/TR3/Vehicles/rubber_boat.h +++ b/TombEngine/Objects/TR3/Vehicles/rubber_boat.h @@ -1,12 +1,16 @@ #pragma once +#include "Objects/Utils/VehicleHelpers.h" -#include "Game/items.h" -#include "Game/collision/collide_room.h" +struct CollisionInfo; +struct ItemInfo; namespace TEN::Entities::Vehicles { void InitialiseRubberBoat(short itemNumber); - void RubberBoatCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + + void RubberBoatPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + void DoRubberBoatMount(ItemInfo* rBoatItem, ItemInfo* laraItem, VehicleMountType mountType); + void RubberBoatControl(short itemNumber); void DrawRubberBoat(ItemInfo* rBoatItem); } diff --git a/TombEngine/Objects/TR3/Vehicles/rubber_boat_info.h b/TombEngine/Objects/TR3/Vehicles/rubber_boat_info.h index becde1c8b..38bd61a44 100644 --- a/TombEngine/Objects/TR3/Vehicles/rubber_boat_info.h +++ b/TombEngine/Objects/TR3/Vehicles/rubber_boat_info.h @@ -4,15 +4,15 @@ namespace TEN::Entities::Vehicles { struct RubberBoatInfo { - int TurnRate; - short LeanAngle; - short PropellerRotation; - short ExtraRotation; + int TurnRate = 0; + short LeanAngle = 0; + short PropellerRotation = 0; + short ExtraRotation = 0; - int LeftVerticalVelocity; - int RightVerticalVelocity; + int LeftVerticalVelocity = 0; + int RightVerticalVelocity = 0; - int Water; - int Pitch; + int Water = 0; + int Pitch = 0; }; } diff --git a/TombEngine/Objects/TR3/Vehicles/upv.cpp b/TombEngine/Objects/TR3/Vehicles/upv.cpp index 73fd83535..c5a22f695 100644 --- a/TombEngine/Objects/TR3/Vehicles/upv.cpp +++ b/TombEngine/Objects/TR3/Vehicles/upv.cpp @@ -18,12 +18,20 @@ #include "Game/Lara/lara_one_gun.h" #include "Game/savegame.h" #include "Objects/TR3/Vehicles/upv_info.h" +#include "Objects/Utils/VehicleHelpers.h" #include "Sound/sound.h" #include "Specific/level.h" #include "Specific/input.h" #include "Specific/setup.h" using namespace TEN::Input; +using std::vector; + +// TODO: +// Redo water surface dismount. +// Calibrate rotation control to work well on both keyboard and gamepad. +// Improve deflection. +// Try to improve room collision. namespace TEN::Entities::Vehicles { @@ -36,115 +44,187 @@ namespace TEN::Entities::Vehicles { 128, 0, -64, 2 }, { 0, 0, -64, 2 } }; + const vector UPVMountTypes = + { + VehicleMountType::LevelStart, + VehicleMountType::Back + }; - #define UPV_CONTROL 1 - #define UPV_SURFACE 2 - #define UPV_DIVE 4 - #define UPV_DEAD 8 + constexpr auto UPV_RADIUS = 300; + constexpr auto UPV_HEIGHT = 400; + constexpr auto UPV_LENGTH = SECTOR(1); + constexpr auto UPV_WATER_SURFACE_DISTANCE = 210; + constexpr auto UPV_MOUNT_DISTANCE = CLICK(2); + constexpr auto UPV_DISMOUNT_DISTANCE = SECTOR(1); - #define ACCELERATION 0x40000 - #define FRICTION 0x18000 - #define MAX_VELOCITY 0x400000 - #define ROT_ACCELERATION 0x400000 - #define ROT_SLOWACCEL 0x200000 - #define ROT_FRICTION 0x100000 - #define MAX_ROTATION 0x1c00000 - #define UPDOWN_ACCEL (ANGLE(2.0f) * (USHRT_MAX + 1)) - #define UPDOWN_SLOWACCEL (ANGLE(1.0f) * (USHRT_MAX + 1)) - #define UPDOWN_FRICTION (ANGLE(1.0f) * (USHRT_MAX + 1)) - #define MAX_UPDOWN (ANGLE(2.0f) * (USHRT_MAX + 1)) - #define UPDOWN_LIMIT ANGLE(80.0f) - #define UPDOWN_SPEED 10 - #define SURFACE_DIST 210 - #define SURFACE_ANGLE ANGLE(30.0f) - #define DIVE_ANGLE ANGLE(15.0f) - #define DIVE_SPEED ANGLE(5.0f) - #define UPV_DRAW_SHIFT 128 - #define UPV_RADIUS 300 - #define UPV_HEIGHT 400 - #define UPV_LENGTH SECTOR(1) - #define FRONT_TOLERANCE (ANGLE(45.0f) * (USHRT_MAX + 1)) - #define TOP_TOLERANCE (ANGLE(45.0f) * (USHRT_MAX + 1)) - #define WALL_DEFLECT (ANGLE(2.0f) * (USHRT_MAX + 1)) - #define DISMOUNT_DISTANCE SECTOR(1) - #define HARPOON_VELOCITY CLICK(1) - #define HARPOON_RELOAD 15 + constexpr int UPV_VELOCITY_ACCEL = 4 * VEHICLE_VELOCITY_SCALE; + constexpr int UPV_VELOCITY_FRICTION_DECEL = 1.5f * VEHICLE_VELOCITY_SCALE; + constexpr int UPV_VELOCITY_MAX = 64 * VEHICLE_VELOCITY_SCALE; - #define UPV_TURBINE_BONE 3 + constexpr int UPV_HARPOON_RELOAD_TIME = 15; + constexpr int UPV_HARPOON_VELOCITY = CLICK(1); + constexpr int UPV_SHIFT = 128; - #define DEATH_FRAME_1 16 - #define DEATH_FRAME_2 17 - #define DISMOUNT_SURFACE_FRAME 51 - #define MOUNT_SURFACE_SOUND_FRAME 30 - #define MOUNT_SURFACE_CONTROL_FRAME 50 - #define DISMOUNT_UNDERWATER_FRAME 42 - #define MOUNT_UNDERWATER_SOUND_FRAME 30 - #define MOUNT_UNDERWATER_CONTROL_FRAME 42 + // TODO: These should probably be done in the wad. @Sezz 2022.06.24 + constexpr auto UPV_DEATH_FRAME_1 = 16; + constexpr auto UPV_DEATH_FRAME_2 = 17; + constexpr auto UPV_MOUNT_WATER_SURFACE_SOUND_FRAME = 30; + constexpr auto UPV_MOUNT_WATER_SURFACE_CONTROL_FRAME = 50; + constexpr auto UPV_DISMOUNT_WATER_SURFACE_FRAME = 51; + constexpr auto UPV_MOUNT_UNDERWATER_SOUND_FRAME = 30; + constexpr auto UPV_MOUNT_UNDERWATER_CONTROL_FRAME = 42; + constexpr auto UPV_DISMOUNT_UNDERWATER_FRAME = 42; - #define UPV_IN_PROPEL IN_JUMP - #define UPV_IN_UP IN_FORWARD - #define UPV_IN_DOWN IN_BACK - #define UPV_IN_LEFT IN_LEFT - #define UPV_IN_RIGHT IN_RIGHT - #define UPV_IN_FIRE IN_ACTION - #define UPV_IN_DISMOUNT IN_ROLL + #define UPV_X_TURN_RATE_DIVE_ACCEL ANGLE(5.0f) + #define UPV_X_TURN_RATE_ACCEL ANGLE(0.6f) + #define UPV_X_TURN_RATE_FRICTION_DECEL ANGLE(0.3f) + #define UPV_X_TURN_RATE_MAX ANGLE(3.25f) + + #define UPV_Y_TURN_RATE_ACCEL ANGLE(0.6f) + #define UPV_Y_TURN_RATE_FRICTION_DECEL ANGLE(0.3f) + #define UPV_Y_TURN_RATE_MAX ANGLE(3.75f) + + #define UPV_X_ORIENT_WATER_SURFACE_MAX ANGLE(30.0f) + #define UPV_X_ORIENT_DIVE_MAX ANGLE(15.0f) + #define UPV_X_ORIENT_MAX ANGLE(85.0f) + + #define UPV_DEFLECT_ANGLE ANGLE(45.0f) + #define UPV_DEFLCT_TURN_RATE_MAX ANGLE(2.0f) + + #define UPV_LEAN_RATE ANGLE(0.6f) + #define UPV_LEAN_MAX ANGLE(10.0f) enum UPVState { UPV_STATE_DEATH = 0, - UPV_STATE_HIT = 1, + UPV_STATE_COLLIDE = 1, UPV_STATE_DISMOUNT_WATER_SURFACE = 2, - UPV_STATE_UNK1 = 3, + UPV_STATE_UNUSED_1 = 3, // Unused. UPV_STATE_MOVE = 4, UPV_STATE_IDLE = 5, - UPV_STATE_UNK2 = 6, // TODO - UPV_STATE_UNK3 = 7, // TODO + UPV_STATE_UNUSED_2 = 6, // Unused. + UPV_STATE_UNUSED_3 = 7, // Unused. UPV_STATE_MOUNT = 8, UPV_STATE_DISMOUNT_UNDERWATER = 9 }; - // TODO enum UPVAnim { - UPV_ANIM_DEATH_MOVING = 0, - UPV_ANIM_DEATH = 1, - + UPV_ANIM_MOVING_DEATH = 0, + UPV_ANIM_IDLE_DEATH = 1, + UPV_ANIM_COLLIDE_FRONT = 2, + UPV_ANIM_MOVE = 3, + UPV_ANIM_COLLIDE_FRONT_2 = 4, // Unused. UPV_ANIM_IDLE = 5, - - UPV_ANIM_DISMOUNT_SURFACE = 9, + UPV_ANIM_IDLE_TO_MOVE = 6, + UPV_ANIM_MOVE_TO_IDLE = 7, + UPV_ANIM_DISMOUNT_WATER_SURFACE_START = 8, + UPV_ANIM_DISMOUNT_WATER_SURFACE_END = 9, UPV_ANIM_MOUNT_SURFACE_START = 10, UPV_ANIM_MOUNT_SURFACE_END = 11, UPV_ANIM_DISMOUNT_UNDERWATER = 12, UPV_ANIM_MOUNT_UNDERWATER = 13, }; - enum UPVBiteFlags + enum UPVJoint { - UPV_FAN = 0, - UPV_FRONT_LIGHT = 1, - UPV_LEFT_FIN_LEFT = 2, - UPV_LEFT_FIN_RIGHT = 3, - UPV_RIGHT_FIN_RIGHT = 4, - UPV_RIGHT_FIN_LEFT = 5 + UPV_JOINT_LEFT_RUDDER = 1, + UPV_JOINT_RIGHT_RUDDER = 2, + UPV_JOINT_TURBINE = 3 }; + + enum UPVBiteIndex + { + UPV_BITE_TURBINE = 0, + UPV_BITE_FRONT_LIGHT = 1, + UPV_BITE_LEFT_RUDDER_LEFT = 2, // Unused. Perhaps something like a trailing stream effect behind rudders was intended? + UPV_BITE_LEFT_RUDDER_RIGHT = 3, // Unused. + UPV_BITE_RIGHT_RUDDER_RIGHT = 4, // Unused. + UPV_BITE_RIGHT_RUDDER_LEFT = 5 // Unused. + }; + enum UPVFlags + { + UPV_FLAG_CONTROL = (1 << 0), + UPV_FLAG_SURFACE = (1 << 1), + UPV_FLAG_DIVE = (1 << 2), + UPV_FLAG_DEAD = (1 << 3) + }; + + + UPVInfo* GetUPVInfo(ItemInfo* UPVItem) + { + return (UPVInfo*)UPVItem->Data; + } void UPVInitialise(short itemNumber) { auto* UPVItem = &g_Level.Items[itemNumber]; UPVItem->Data = UPVInfo(); - auto* UPV = (UPVInfo*)UPVItem->Data; + auto* UPV = GetUPVInfo(UPVItem); - UPV->Velocity = 0; - UPV->Rot = 0; - UPV->Flags = UPV_SURFACE; - UPV->HarpoonTimer = 0; - UPV->HarpoonLeft = false; + UPV->Flags = UPV_FLAG_SURFACE; } - static void FireUPVHarpoon(ItemInfo* laraItem, ItemInfo* UPVItem) + void UPVPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) + { + auto* UPVItem = &g_Level.Items[itemNumber]; + auto* lara = GetLaraInfo(laraItem); + + if (laraItem->HitPoints <= 0 || lara->Vehicle != NO_ITEM) + return; + + auto mountType = GetVehicleMountType(UPVItem, laraItem, coll, UPVMountTypes, UPV_MOUNT_DISTANCE); + if (mountType == VehicleMountType::None) + { + // HACK: Collision in water behaves differently? @Sezz 2022.06.28 + if (TestBoundsCollide(UPVItem, laraItem, coll->Setup.Radius) && TestCollision(UPVItem, laraItem)) + ItemPushItem(UPVItem, laraItem, coll, false, false); + } + else + { + lara->Vehicle = itemNumber; + DoUPVMount(UPVItem, laraItem, mountType); + } + } + + void DoUPVMount(ItemInfo* UPVItem, ItemInfo* laraItem, VehicleMountType mountType) { auto* lara = GetLaraInfo(laraItem); - auto UPV = (UPVInfo*)UPVItem->Data; + + switch (mountType) + { + case VehicleMountType::LevelStart: + laraItem->Animation.AnimNumber = Objects[ID_UPV_LARA_ANIMS].animIndex + UPV_ANIM_IDLE; + laraItem->Animation.ActiveState = UPV_STATE_IDLE; + laraItem->Animation.TargetState = UPV_STATE_IDLE; + break; + + default: + case VehicleMountType::Back: + if (lara->Control.WaterStatus == WaterStatus::TreadWater) + laraItem->Animation.AnimNumber = Objects[ID_UPV_LARA_ANIMS].animIndex + UPV_ANIM_MOUNT_SURFACE_START; + else + laraItem->Animation.AnimNumber = Objects[ID_UPV_LARA_ANIMS].animIndex + UPV_ANIM_MOUNT_UNDERWATER; + + laraItem->Animation.ActiveState = UPV_STATE_MOUNT; + laraItem->Animation.TargetState = UPV_STATE_MOUNT; + break; + } + laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; + + DoVehicleFlareDiscard(laraItem); + laraItem->Pose = UPVItem->Pose; + lara->Control.HandStatus = HandStatus::Busy; + lara->Control.WaterStatus = WaterStatus::Dry; + UPVItem->HitPoints = 1; + + AnimateItem(laraItem); + } + + static void FireUPVHarpoon(ItemInfo* UPVItem, ItemInfo* laraItem) + { + auto UPV = GetUPVInfo(UPVItem); + auto* lara = GetLaraInfo(laraItem); auto& ammo = GetAmmo(laraItem, LaraWeaponType::HarpoonGun); if (ammo.getCount() == 0 && !ammo.hasInfinite()) @@ -162,7 +242,7 @@ namespace TEN::Entities::Vehicles harpoonItem->RoomNumber = UPVItem->RoomNumber; auto pos = Vector3Int((UPV->HarpoonLeft ? 22 : -22), 24, 230); - GetJointAbsPosition(UPVItem, &pos, UPV_TURBINE_BONE); + GetJointAbsPosition(UPVItem, &pos, UPV_JOINT_TURBINE); harpoonItem->Pose.Position = pos; InitialiseItem(itemNumber); @@ -170,8 +250,8 @@ namespace TEN::Entities::Vehicles harpoonItem->Pose.Orientation = Vector3Shrt(UPVItem->Pose.Orientation.x, UPVItem->Pose.Orientation.y, 0); // TODO: Huh? - harpoonItem->Animation.VerticalVelocity = -HARPOON_VELOCITY * phd_sin(harpoonItem->Pose.Orientation.x); - harpoonItem->Animation.Velocity = HARPOON_VELOCITY * phd_cos(harpoonItem->Pose.Orientation.x); + harpoonItem->Animation.VerticalVelocity = -UPV_HARPOON_VELOCITY * phd_sin(harpoonItem->Pose.Orientation.x); + harpoonItem->Animation.Velocity = UPV_HARPOON_VELOCITY * phd_cos(harpoonItem->Pose.Orientation.x); harpoonItem->HitPoints = HARPOON_TIME; harpoonItem->ItemFlags[0] = 1; @@ -239,33 +319,33 @@ namespace TEN::Entities::Vehicles if (itemNumber == NO_ITEM) return; + auto* UPVItem = &g_Level.Items[itemNumber]; + auto* UPV = GetUPVInfo(UPVItem); auto* laraItem = LaraItem; auto* lara = GetLaraInfo(laraItem); - auto* UPVItem = &g_Level.Items[itemNumber]; - auto* UPV = (UPVInfo*)UPVItem->Data; Vector3Int pos; if (lara->Vehicle == itemNumber) { - if (!UPV->Velocity) - UPV->FanRot += ANGLE(2.0f); - else - UPV->FanRot += UPV->Velocity / 4069; + UPV->TurbineRotation += UPV->Velocity ? (UPV->Velocity / 8) : ANGLE(2.0f); + UPV->LeftRudderRotation = (UPV->TurnRate.x + UPV->TurnRate.y) * 8; + UPV->RightRudderRotation = (UPV->TurnRate.x + -UPV->TurnRate.y) * 8; if (UPV->Velocity) { - pos = Vector3Int(UPVBites[UPV_FAN].x, UPVBites[UPV_FAN].y, UPVBites[UPV_FAN].z); - GetJointAbsPosition(UPVItem, &pos, UPVBites[UPV_FAN].meshNum); + pos = Vector3Int(UPVBites[UPV_BITE_TURBINE].x, UPVBites[UPV_BITE_TURBINE].y, UPVBites[UPV_BITE_TURBINE].z); + GetJointAbsPosition(UPVItem, &pos, UPVBites[UPV_BITE_TURBINE].meshNum); - TriggerUPVMist(pos.x, pos.y + UPV_DRAW_SHIFT, pos.z, abs(UPV->Velocity) / (USHRT_MAX + 1), UPVItem->Pose.Orientation.y + ANGLE(180.0f)); + TriggerUPVMist(pos.x, pos.y + UPV_SHIFT, pos.z, abs(UPV->Velocity) / VEHICLE_VELOCITY_SCALE, UPVItem->Pose.Orientation.y + ANGLE(180.0f)); if ((GetRandomControl() & 1) == 0) { - PHD_3DPOS pos2; - pos2.Position.x = pos.x + (GetRandomControl() & 63) - 32; - pos2.Position.y = pos.y + UPV_DRAW_SHIFT; - pos2.Position.z = pos.z + (GetRandomControl() & 63) - 32; + auto pos2 = PHD_3DPOS( + pos.x + (GetRandomControl() & 63) - 32, + pos.y + UPV_SHIFT, + pos.z + (GetRandomControl() & 63) - 32 + ); short probedRoomNumber = GetCollision(pos2.Position.x, pos2.Position.y, pos2.Position.z, UPVItem->RoomNumber).RoomNumber; CreateBubble((Vector3Int*)&pos2, probedRoomNumber, 4, 8, BUBBLE_FLAG_CLUMP, 0, 0, 0); @@ -276,26 +356,18 @@ namespace TEN::Entities::Vehicles for (int lp = 0; lp < 2; lp++) { int random = 31 - (GetRandomControl() & 3); - pos = Vector3Int(UPVBites[UPV_FRONT_LIGHT].x, UPVBites[UPV_FRONT_LIGHT].y, UPVBites[UPV_FRONT_LIGHT].z << (lp * 6)); - GetJointAbsPosition(UPVItem, &pos, UPVBites[UPV_FRONT_LIGHT].meshNum); + pos = Vector3Int(UPVBites[UPV_BITE_FRONT_LIGHT].x, UPVBites[UPV_BITE_FRONT_LIGHT].y, UPVBites[UPV_BITE_FRONT_LIGHT].z << (lp * 6)); + GetJointAbsPosition(UPVItem, &pos, UPVBites[UPV_BITE_FRONT_LIGHT].meshNum); - GameVector source, target; + GameVector source; if (lp == 1) { - target.x = pos.x; - target.y = pos.y; - target.z = pos.z; - target.roomNumber = UPVItem->RoomNumber; + auto target = GameVector(pos.x, pos.y, pos.z, UPVItem->RoomNumber); LOS(&source, &target); pos = Vector3Int(target.x, target.y, target.z); } else - { - source.x = pos.x; - source.y = pos.y; - source.z = pos.z; - source.roomNumber = UPVItem->RoomNumber; - } + source = GameVector(pos.x, pos.y, pos.z, UPVItem->RoomNumber); TriggerDynamicLight(pos.x, pos.y, pos.z, 16 + (lp << 3), random, random, random); } @@ -304,7 +376,7 @@ namespace TEN::Entities::Vehicles UPV->HarpoonTimer--; } - static bool TestUPVDismount(ItemInfo* laraItem, ItemInfo* UPVItem) + static bool TestUPVDismount(ItemInfo* UPVItem, ItemInfo* laraItem) { auto* lara = GetLaraInfo(laraItem); @@ -312,10 +384,10 @@ namespace TEN::Entities::Vehicles return false; short moveAngle = UPVItem->Pose.Orientation.y + ANGLE(180.0f); - int velocity = DISMOUNT_DISTANCE * phd_cos(UPVItem->Pose.Orientation.x); + int velocity = UPV_DISMOUNT_DISTANCE * phd_cos(UPVItem->Pose.Orientation.x); int x = UPVItem->Pose.Position.x + velocity * phd_sin(moveAngle); int z = UPVItem->Pose.Position.z + velocity * phd_cos(moveAngle); - int y = UPVItem->Pose.Position.y - DISMOUNT_DISTANCE * phd_sin(-UPVItem->Pose.Orientation.x); + int y = UPVItem->Pose.Position.y - UPV_DISMOUNT_DISTANCE * phd_sin(-UPVItem->Pose.Orientation.x); auto probe = GetCollision(x, y, z, UPVItem->RoomNumber); if ((probe.Position.Floor - probe.Position.Ceiling) < CLICK(1) || @@ -330,36 +402,7 @@ namespace TEN::Entities::Vehicles return true; } - static bool TestUPVMount(ItemInfo* laraItem, ItemInfo* UPVItem) - { - auto* lara = GetLaraInfo(laraItem); - - if (!(TrInput & IN_ACTION) || - lara->Control.HandStatus != HandStatus::Free || - laraItem->Animation.Airborne) - { - return false; - } - - int y = abs(laraItem->Pose.Position.y - (UPVItem->Pose.Position.y - CLICK(0.5f))); - if (y > CLICK(1)) - return false; - - int distance = pow(laraItem->Pose.Position.x - UPVItem->Pose.Position.x, 2) + pow(laraItem->Pose.Position.z - UPVItem->Pose.Position.z, 2); - if (distance > pow(CLICK(2), 2)) - return false; - - short deltaAngle = abs(laraItem->Pose.Orientation.y - UPVItem->Pose.Orientation.y); - if (deltaAngle > ANGLE(35.0f) || deltaAngle < -ANGLE(35.0f)) - return false; - - if (GetCollision(UPVItem).Position.Floor < -32000) - return false; - - return true; - } - - static void DoCurrent(ItemInfo* laraItem, ItemInfo* UPVItem) + static void DoCurrent(ItemInfo* UPVItem, ItemInfo* laraItem) { auto* lara = GetLaraInfo(laraItem); @@ -421,10 +464,10 @@ namespace TEN::Entities::Vehicles UPVItem->Pose.Position.z += lara->WaterCurrentPull.z / CLICK(1); } - static void BackgroundCollision(ItemInfo* laraItem, ItemInfo* UPVItem) + static void BackgroundCollision(ItemInfo* UPVItem, ItemInfo* laraItem) { + auto* UPV = GetUPVInfo(UPVItem); auto* lara = GetLaraInfo(laraItem); - auto* UPV = (UPVInfo*)UPVItem->Data; CollisionInfo cinfo, * coll = &cinfo; // ?? coll->Setup.Mode = CollisionProbeMode::Quadrants; @@ -469,15 +512,15 @@ namespace TEN::Entities::Vehicles if (coll->CollisionType == CT_FRONT) { - if (UPV->XRot > FRONT_TOLERANCE) - UPV->XRot += WALL_DEFLECT; - else if (UPV->XRot < -FRONT_TOLERANCE) - UPV->XRot -= WALL_DEFLECT; + if (UPV->TurnRate.x > UPV_DEFLECT_ANGLE) + UPV->TurnRate.x += UPV_DEFLCT_TURN_RATE_MAX; + else if (UPV->TurnRate.x < -UPV_DEFLECT_ANGLE) + UPV->TurnRate.x -= UPV_DEFLCT_TURN_RATE_MAX; else { - if (abs(UPV->Velocity) >= MAX_VELOCITY) + if (abs(UPV->Velocity) >= UPV_VELOCITY_MAX) { - laraItem->Animation.TargetState = UPV_STATE_HIT; + laraItem->Animation.TargetState = UPV_STATE_COLLIDE; UPV->Velocity = -UPV->Velocity / 2; } else @@ -486,8 +529,8 @@ namespace TEN::Entities::Vehicles } else if (coll->CollisionType == CT_TOP) { - if (UPV->XRot >= -TOP_TOLERANCE) - UPV->XRot -= WALL_DEFLECT; + if (UPV->TurnRate.x >= -UPV_DEFLECT_ANGLE) + UPV->TurnRate.x -= UPV_DEFLCT_TURN_RATE_MAX; } else if (coll->CollisionType == CT_TOP_FRONT) UPV->Velocity = 0; @@ -505,16 +548,16 @@ namespace TEN::Entities::Vehicles if (coll->Middle.Floor < 0) { UPVItem->Pose.Position.y += coll->Middle.Floor; - UPV->XRot += WALL_DEFLECT; + UPV->TurnRate.x += UPV_DEFLCT_TURN_RATE_MAX; } } - static void UPVControl(ItemInfo* laraItem, ItemInfo* UPVItem) + static void UPVControl(ItemInfo* UPVItem, ItemInfo* laraItem) { + auto* UPV = GetUPVInfo(UPVItem); auto* lara = GetLaraInfo(laraItem); - auto* UPV = (UPVInfo*)UPVItem->Data; - TestUPVDismount(laraItem, UPVItem); + TestUPVDismount(UPVItem, laraItem); int anim = laraItem->Animation.AnimNumber - Objects[ID_UPV_LARA_ANIMS].animIndex; int frame = laraItem->Animation.FrameNumber - g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; @@ -528,16 +571,16 @@ namespace TEN::Entities::Vehicles break; } - if (TrInput & UPV_IN_LEFT) - UPV->Rot -= ROT_ACCELERATION; - - else if (TrInput & UPV_IN_RIGHT) - UPV->Rot += ROT_ACCELERATION; - - if (UPV->Flags & UPV_SURFACE) + if (TrInput & (VEHICLE_IN_LEFT | VEHICLE_IN_RIGHT)) { - int xa = UPVItem->Pose.Orientation.x - SURFACE_ANGLE; - int ax = SURFACE_ANGLE - UPVItem->Pose.Orientation.x; + ModulateVehicleTurnRateY(&UPV->TurnRate.y, UPV_Y_TURN_RATE_ACCEL, -UPV_Y_TURN_RATE_MAX, UPV_Y_TURN_RATE_MAX); + ModulateVehicleLean(UPVItem, UPV_LEAN_RATE, UPV_LEAN_MAX); + } + + if (UPV->Flags & UPV_FLAG_SURFACE) + { + int xa = UPVItem->Pose.Orientation.x - UPV_X_ORIENT_WATER_SURFACE_MAX; + int ax = UPV_X_ORIENT_WATER_SURFACE_MAX - UPVItem->Pose.Orientation.x; if (xa > 0) { @@ -554,28 +597,25 @@ namespace TEN::Entities::Vehicles UPVItem->Pose.Orientation.x += ANGLE(0.1f); } else - UPVItem->Pose.Orientation.x = SURFACE_ANGLE; + UPVItem->Pose.Orientation.x = UPV_X_ORIENT_WATER_SURFACE_MAX; } else { - if (TrInput & UPV_IN_UP) - UPV->XRot -= UPDOWN_ACCEL; - else if (TrInput & UPV_IN_DOWN) - UPV->XRot += UPDOWN_ACCEL; + if (TrInput & (VEHICLE_IN_UP | VEHICLE_IN_DOWN)) + ModulateVehicleTurnRateX(&UPV->TurnRate.x, UPV_X_TURN_RATE_ACCEL, -UPV_X_TURN_RATE_MAX, UPV_X_TURN_RATE_MAX); } - if (TrInput & UPV_IN_PROPEL) + if (TrInput & VEHICLE_IN_ACCELERATE) { - if (TrInput & UPV_IN_UP && - UPV->Flags & UPV_SURFACE && - UPVItem->Pose.Orientation.x > -DIVE_ANGLE) + if (TrInput & VEHICLE_IN_UP && + UPV->Flags & UPV_FLAG_SURFACE && + UPVItem->Pose.Orientation.x > -UPV_X_ORIENT_DIVE_MAX) { - UPV->Flags |= UPV_DIVE; + UPV->Flags |= UPV_FLAG_DIVE; } - UPV->Velocity += ACCELERATION; + UPV->Velocity += UPV_VELOCITY_ACCEL; } - else laraItem->Animation.TargetState = UPV_STATE_IDLE; @@ -587,16 +627,17 @@ namespace TEN::Entities::Vehicles laraItem->Animation.TargetState = UPV_STATE_DEATH; break; } - - if (TrInput & UPV_IN_LEFT) - UPV->Rot -= ROT_SLOWACCEL; - else if (TrInput & UPV_IN_RIGHT) - UPV->Rot += ROT_SLOWACCEL; - - if (UPV->Flags & UPV_SURFACE) + + if (TrInput & (VEHICLE_IN_LEFT | VEHICLE_IN_RIGHT)) { - int xa = UPVItem->Pose.Orientation.x - SURFACE_ANGLE; - int ax = SURFACE_ANGLE - UPVItem->Pose.Orientation.x; + ModulateVehicleTurnRateY(&UPV->TurnRate.y, UPV_Y_TURN_RATE_ACCEL, -UPV_Y_TURN_RATE_MAX, UPV_Y_TURN_RATE_MAX); + ModulateVehicleLean(UPVItem, UPV_LEAN_RATE, UPV_LEAN_MAX); + } + + if (UPV->Flags & UPV_FLAG_SURFACE) + { + int xa = UPVItem->Pose.Orientation.x - UPV_X_ORIENT_WATER_SURFACE_MAX; + int ax = UPV_X_ORIENT_WATER_SURFACE_MAX - UPVItem->Pose.Orientation.x; if (xa > 0) { if (xa > ANGLE(1.0f)) @@ -612,41 +653,39 @@ namespace TEN::Entities::Vehicles UPVItem->Pose.Orientation.x += ANGLE(0.1f); } else - UPVItem->Pose.Orientation.x = SURFACE_ANGLE; + UPVItem->Pose.Orientation.x = UPV_X_ORIENT_WATER_SURFACE_MAX; } else { - if (TrInput & UPV_IN_UP) - UPV->XRot -= UPDOWN_ACCEL; - else if (TrInput & UPV_IN_DOWN) - UPV->XRot += UPDOWN_ACCEL; + if (TrInput & (VEHICLE_IN_UP | VEHICLE_IN_DOWN)) + ModulateVehicleTurnRateX(&UPV->TurnRate.x, UPV_X_TURN_RATE_ACCEL, -UPV_X_TURN_RATE_MAX, UPV_X_TURN_RATE_MAX); } - if (TrInput & UPV_IN_DISMOUNT && TestUPVDismount(laraItem, UPVItem)) + if (TrInput & VEHICLE_IN_DISMOUNT && TestUPVDismount(UPVItem, laraItem)) { if (UPV->Velocity > 0) - UPV->Velocity -= ACCELERATION; + UPV->Velocity -= UPV_VELOCITY_ACCEL; else { - if (UPV->Flags & UPV_SURFACE) + if (UPV->Flags & UPV_FLAG_SURFACE) laraItem->Animation.TargetState = UPV_STATE_DISMOUNT_WATER_SURFACE; else laraItem->Animation.TargetState = UPV_STATE_DISMOUNT_UNDERWATER; - //sub->Flags &= ~UPV_CONTROL; having this here causes the UPV glitch, moving it directly to the states' code is better + //sub->Flags &= ~UPV_FLAG_CONTROL; having this here causes the UPV glitch, moving it directly to the states' code is better StopSoundEffect(SFX_TR3_VEHICLE_UPV_LOOP); SoundEffect(SFX_TR3_VEHICLE_UPV_STOP, (PHD_3DPOS*)&UPVItem->Pose.Position.x, SoundEnvironment::Always); } } - else if (TrInput & UPV_IN_PROPEL) + else if (TrInput & VEHICLE_IN_ACCELERATE) { - if (TrInput & UPV_IN_UP && - UPVItem->Pose.Orientation.x > -DIVE_ANGLE && - UPV->Flags & UPV_SURFACE) + if (TrInput & VEHICLE_IN_UP && + UPVItem->Pose.Orientation.x > -UPV_X_ORIENT_DIVE_MAX && + UPV->Flags & UPV_FLAG_SURFACE) { - UPV->Flags |= UPV_DIVE; + UPV->Flags |= UPV_FLAG_DIVE; } laraItem->Animation.TargetState = UPV_STATE_MOVE; @@ -660,28 +699,28 @@ namespace TEN::Entities::Vehicles UPVItem->Pose.Position.y += 4; UPVItem->Pose.Orientation.x += ANGLE(1.0f); - if (frame == MOUNT_SURFACE_SOUND_FRAME) + if (frame == UPV_MOUNT_WATER_SURFACE_SOUND_FRAME) SoundEffect(SFX_TR3_VEHICLE_UPV_LOOP, (PHD_3DPOS*)&UPVItem->Pose.Position.x, SoundEnvironment::Always); - if (frame == MOUNT_SURFACE_CONTROL_FRAME) - UPV->Flags |= UPV_CONTROL; + if (frame == UPV_MOUNT_WATER_SURFACE_CONTROL_FRAME) + UPV->Flags |= UPV_FLAG_CONTROL; } else if (anim == UPV_ANIM_MOUNT_UNDERWATER) { - if (frame == MOUNT_UNDERWATER_SOUND_FRAME) + if (frame == UPV_MOUNT_UNDERWATER_SOUND_FRAME) SoundEffect(SFX_TR3_VEHICLE_UPV_LOOP, (PHD_3DPOS*)&UPVItem->Pose.Position.x, SoundEnvironment::Always); - if (frame == MOUNT_UNDERWATER_CONTROL_FRAME) - UPV->Flags |= UPV_CONTROL; + if (frame == UPV_MOUNT_UNDERWATER_CONTROL_FRAME) + UPV->Flags |= UPV_FLAG_CONTROL; } break; case UPV_STATE_DISMOUNT_UNDERWATER: - if (anim == UPV_ANIM_DISMOUNT_UNDERWATER && frame == DISMOUNT_UNDERWATER_FRAME) + if (anim == UPV_ANIM_DISMOUNT_UNDERWATER && frame == UPV_DISMOUNT_UNDERWATER_FRAME) { - UPV->Flags &= ~UPV_CONTROL; + UPV->Flags &= ~UPV_FLAG_CONTROL; Vector3Int vec = { 0, 0, 0 }; GetLaraJointPosition(&vec, LM_HIPS); @@ -705,7 +744,7 @@ namespace TEN::Entities::Vehicles SetAnimation(laraItem, LA_UNDERWATER_IDLE); laraItem->Animation.VerticalVelocity = 0; - laraItem->Animation.Airborne = false; + laraItem->Animation.IsAirborne = false; laraItem->Pose.Orientation.x = laraItem->Pose.Orientation.z = 0; UpdateItemRoom(laraItem, 0); @@ -720,9 +759,9 @@ namespace TEN::Entities::Vehicles break; case UPV_STATE_DISMOUNT_WATER_SURFACE: - if (anim == UPV_ANIM_DISMOUNT_SURFACE && frame == DISMOUNT_SURFACE_FRAME) + if (anim == UPV_ANIM_DISMOUNT_WATER_SURFACE_START && frame == UPV_DISMOUNT_WATER_SURFACE_FRAME) { - UPV->Flags &= ~UPV_CONTROL; + UPV->Flags &= ~UPV_FLAG_CONTROL; int waterDepth, waterHeight, heightFromWater; waterDepth = GetWaterSurface(laraItem); @@ -743,7 +782,7 @@ namespace TEN::Entities::Vehicles SetAnimation(laraItem, LA_ONWATER_IDLE); laraItem->Animation.VerticalVelocity = 0; - laraItem->Animation.Airborne = false; + laraItem->Animation.IsAirborne = false; laraItem->Pose.Orientation.x = 0; laraItem->Pose.Orientation.z = 0; @@ -759,7 +798,7 @@ namespace TEN::Entities::Vehicles } else { - UPV->XRot -= UPDOWN_ACCEL; + UPV->TurnRate.x -= UPV_X_TURN_RATE_ACCEL; if (UPVItem->Pose.Orientation.x < 0) UPVItem->Pose.Orientation.x = 0; } @@ -767,8 +806,8 @@ namespace TEN::Entities::Vehicles break; case UPV_STATE_DEATH: - if ((anim == UPV_ANIM_DEATH || anim == UPV_ANIM_DEATH_MOVING) && - (frame == DEATH_FRAME_1 || frame == DEATH_FRAME_2)) + if ((anim == UPV_ANIM_IDLE_DEATH || anim == UPV_ANIM_MOVING_DEATH) && + (frame == UPV_DEATH_FRAME_1 || frame == UPV_DEATH_FRAME_2)) { auto vec = Vector3Int(); GetLaraJointPosition(&vec, LM_HIPS); @@ -778,136 +817,66 @@ namespace TEN::Entities::Vehicles laraItem->Pose.Orientation.z = 0; SetAnimation(laraItem, LA_UNDERWATER_DEATH, 17); + laraItem->Animation.IsAirborne = false; laraItem->Animation.VerticalVelocity = 0; - laraItem->Animation.Airborne = false; - UPV->Flags |= UPV_DEAD; + UPV->Flags |= UPV_FLAG_DEAD; } UPVItem->Animation.Velocity = 0; break; } - if (UPV->Flags & UPV_DIVE) + if (UPV->Flags & UPV_FLAG_DIVE) { - if (UPVItem->Pose.Orientation.x > -DIVE_ANGLE) - UPVItem->Pose.Orientation.x -= DIVE_SPEED; + if (UPVItem->Pose.Orientation.x > -UPV_X_ORIENT_DIVE_MAX) + UPVItem->Pose.Orientation.x -= UPV_X_TURN_RATE_DIVE_ACCEL; else - UPV->Flags &= ~UPV_DIVE; + UPV->Flags &= ~UPV_FLAG_DIVE; } if (UPV->Velocity > 0) { - UPV->Velocity -= FRICTION; + UPV->Velocity -= UPV_VELOCITY_FRICTION_DECEL; if (UPV->Velocity < 0) UPV->Velocity = 0; } else if (UPV->Velocity < 0) { - UPV->Velocity += FRICTION; + UPV->Velocity += UPV_VELOCITY_FRICTION_DECEL; if (UPV->Velocity > 0) UPV->Velocity = 0; } - if (UPV->Velocity > MAX_VELOCITY) - UPV->Velocity = MAX_VELOCITY; - else if (UPV->Velocity < -MAX_VELOCITY) - UPV->Velocity = -MAX_VELOCITY; + if (UPV->Velocity > UPV_VELOCITY_MAX) + UPV->Velocity = UPV_VELOCITY_MAX; + else if (UPV->Velocity < -UPV_VELOCITY_MAX) + UPV->Velocity = -UPV_VELOCITY_MAX; - if (UPV->Rot > 0) + if (UPV->TurnRate.x > 0) { - UPV->Rot -= ROT_FRICTION; - if (UPV->Rot < 0) - UPV->Rot = 0; + UPV->TurnRate.x -= UPV_X_TURN_RATE_FRICTION_DECEL; + if (UPV->TurnRate.x < 0) + UPV->TurnRate.x = 0; } - else if (UPV->Rot < 0) + else if (UPV->TurnRate.x < 0) { - UPV->Rot += ROT_FRICTION; - if (UPV->Rot > 0) - UPV->Rot = 0; + UPV->TurnRate.x += UPV_X_TURN_RATE_FRICTION_DECEL; + if (UPV->TurnRate.x > 0) + UPV->TurnRate.x = 0; } - if (UPV->XRot > 0) + if (UPV->TurnRate.y > 0) { - UPV->XRot -= UPDOWN_FRICTION; - if (UPV->XRot < 0) - UPV->XRot = 0; + UPV->TurnRate.y -= UPV_Y_TURN_RATE_FRICTION_DECEL; + if (UPV->TurnRate.y < 0) + UPV->TurnRate.y = 0; } - else if (UPV->XRot < 0) + else if (UPV->TurnRate.y < 0) { - UPV->XRot += UPDOWN_FRICTION; - if (UPV->XRot > 0) - UPV->XRot = 0; - } - - if (UPV->Rot > MAX_ROTATION) - UPV->Rot = MAX_ROTATION; - else if (UPV->Rot < -MAX_ROTATION) - UPV->Rot = -MAX_ROTATION; - - if (UPV->XRot > MAX_UPDOWN) - UPV->XRot = MAX_UPDOWN; - else if (UPV->XRot < -MAX_UPDOWN) - UPV->XRot = -MAX_UPDOWN; - } - - void NoGetOnCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) - { - auto* item = &g_Level.Items[itemNumber]; - - if (!TestBoundsCollide(item, laraItem, coll->Setup.Radius)) - return; - if (!TestCollision(item, laraItem)) - return; - - ItemPushItem(item, laraItem, coll, 0, 0); - } - - void UPVCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) - { - auto* lara = GetLaraInfo(laraItem); - auto* UPVItem = &g_Level.Items[itemNumber]; - - if (laraItem->HitPoints <= 0 || lara->Vehicle != NO_ITEM) - return; - - if (TestUPVMount(laraItem, UPVItem)) - { - lara->Vehicle = itemNumber; - lara->Control.WaterStatus = WaterStatus::Dry; - - if (lara->Control.Weapon.GunType == LaraWeaponType::Flare) - { - CreateFlare(laraItem, ID_FLARE_ITEM, 0); - UndrawFlareMeshes(laraItem); - - lara->Flare.ControlLeft = false; - lara->Control.Weapon.RequestGunType = lara->Control.Weapon.GunType = LaraWeaponType::None; - } - - laraItem->Pose = UPVItem->Pose; - lara->Control.HandStatus = HandStatus::Busy; - UPVItem->HitPoints = 1; - - if (laraItem->Animation.ActiveState == LS_ONWATER_IDLE || laraItem->Animation.ActiveState == LS_ONWATER_FORWARD) - { - laraItem->Animation.AnimNumber = Objects[ID_UPV_LARA_ANIMS].animIndex + UPV_ANIM_MOUNT_SURFACE_START; - laraItem->Animation.ActiveState = laraItem->Animation.TargetState = UPV_STATE_MOUNT; - } - else - { - laraItem->Animation.AnimNumber = Objects[ID_UPV_LARA_ANIMS].animIndex + UPV_ANIM_MOUNT_UNDERWATER; - laraItem->Animation.ActiveState = laraItem->Animation.TargetState = UPV_STATE_MOUNT; - } - - laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - AnimateItem(laraItem); - } - else - { - UPVItem->Pose.Position.y += UPV_DRAW_SHIFT; - NoGetOnCollision(itemNumber, laraItem, coll); - UPVItem->Pose.Position.y -= UPV_DRAW_SHIFT; + UPV->TurnRate.y += UPV_Y_TURN_RATE_FRICTION_DECEL; + if (UPV->TurnRate.y > 0) + UPV->TurnRate.y = 0; } } @@ -915,25 +884,25 @@ namespace TEN::Entities::Vehicles { auto* lara = GetLaraInfo(laraItem); auto* UPVItem = &g_Level.Items[lara->Vehicle]; - auto* UPV = (UPVInfo*)UPVItem->Data; + auto* UPV = GetUPVInfo(UPVItem); auto oldPos = UPVItem->Pose; auto probe = GetCollision(UPVItem); - if (!(UPV->Flags & UPV_DEAD)) + if (!(UPV->Flags & UPV_FLAG_DEAD)) { - UPVControl(laraItem, UPVItem); + UPVControl(UPVItem, laraItem); - UPVItem->Animation.Velocity = UPV->Velocity / (USHRT_MAX + 1); + UPVItem->Animation.Velocity = UPV->Velocity / VEHICLE_VELOCITY_SCALE; + UPVItem->Pose.Orientation += UPV->TurnRate; - UPVItem->Pose.Orientation.x += UPV->XRot / (USHRT_MAX + 1); - UPVItem->Pose.Orientation.y += UPV->Rot / (USHRT_MAX + 1); - UPVItem->Pose.Orientation.z = UPV->Rot / (USHRT_MAX + 1); + if (UPVItem->Pose.Orientation.x > UPV_X_ORIENT_MAX) + UPVItem->Pose.Orientation.x = UPV_X_ORIENT_MAX; + else if (UPVItem->Pose.Orientation.x < -UPV_X_ORIENT_MAX) + UPVItem->Pose.Orientation.x = -UPV_X_ORIENT_MAX; - if (UPVItem->Pose.Orientation.x > UPDOWN_LIMIT) - UPVItem->Pose.Orientation.x = UPDOWN_LIMIT; - else if (UPVItem->Pose.Orientation.x < -UPDOWN_LIMIT) - UPVItem->Pose.Orientation.x = -UPDOWN_LIMIT; + if (!(TrInput & IN_LEFT ) && !(TrInput & IN_RIGHT)) + ResetVehicleLean(UPVItem, 12.0f); TranslateItem(UPVItem, UPVItem->Pose.Orientation, UPVItem->Animation.Velocity); } @@ -950,39 +919,39 @@ namespace TEN::Entities::Vehicles UPVItem->Floor = probe.Position.Floor; - if (UPV->Flags & UPV_CONTROL && !(UPV->Flags & UPV_DEAD)) + if (UPV->Flags & UPV_FLAG_CONTROL && !(UPV->Flags & UPV_FLAG_DEAD)) { if (!TestEnvironment(ENV_FLAG_WATER, UPVItem->RoomNumber) && waterHeight != NO_HEIGHT) { - if ((waterHeight - UPVItem->Pose.Position.y) >= -SURFACE_DIST) - UPVItem->Pose.Position.y = waterHeight + SURFACE_DIST; + if ((waterHeight - UPVItem->Pose.Position.y) >= -UPV_WATER_SURFACE_DISTANCE) + UPVItem->Pose.Position.y = waterHeight + UPV_WATER_SURFACE_DISTANCE; - if (!(UPV->Flags & UPV_SURFACE)) + if (!(UPV->Flags & UPV_FLAG_SURFACE)) { SoundEffect(SFX_TR4_LARA_BREATH, &laraItem->Pose, SoundEnvironment::Always); - UPV->Flags &= ~UPV_DIVE; + UPV->Flags &= ~UPV_FLAG_DIVE; } - UPV->Flags |= UPV_SURFACE; + UPV->Flags |= UPV_FLAG_SURFACE; } - else if ((waterHeight - UPVItem->Pose.Position.y) >= -SURFACE_DIST && waterHeight != NO_HEIGHT && + else if ((waterHeight - UPVItem->Pose.Position.y) >= -UPV_WATER_SURFACE_DISTANCE && waterHeight != NO_HEIGHT && (laraItem->Pose.Position.y - probe.Position.Ceiling) >= CLICK(1)) { - UPVItem->Pose.Position.y = waterHeight + SURFACE_DIST; + UPVItem->Pose.Position.y = waterHeight + UPV_WATER_SURFACE_DISTANCE; - if (!(UPV->Flags & UPV_SURFACE)) + if (!(UPV->Flags & UPV_FLAG_SURFACE)) { SoundEffect(SFX_TR4_LARA_BREATH, &laraItem->Pose, SoundEnvironment::Always); - UPV->Flags &= ~UPV_DIVE; + UPV->Flags &= ~UPV_FLAG_DIVE; } - UPV->Flags |= UPV_SURFACE; + UPV->Flags |= UPV_FLAG_SURFACE; } else - UPV->Flags &= ~UPV_SURFACE; + UPV->Flags &= ~UPV_FLAG_SURFACE; - if (!(UPV->Flags & UPV_SURFACE)) + if (!(UPV->Flags & UPV_FLAG_SURFACE)) { if (laraItem->HitPoints > 0) { @@ -1008,21 +977,21 @@ namespace TEN::Entities::Vehicles TestTriggers(UPVItem, false); UPVEffects(lara->Vehicle); - if (!(UPV->Flags & UPV_DEAD) && + if (!(UPV->Flags & UPV_FLAG_DEAD) && lara->Vehicle != NO_ITEM) { - DoCurrent(laraItem, UPVItem); + DoCurrent(UPVItem, laraItem); - if (TrInput & UPV_IN_FIRE && - UPV->Flags & UPV_CONTROL && + if (TrInput & VEHICLE_IN_FIRE && + UPV->Flags & UPV_FLAG_CONTROL && !UPV->HarpoonTimer) { if (laraItem->Animation.ActiveState != UPV_STATE_DISMOUNT_UNDERWATER && laraItem->Animation.ActiveState != UPV_STATE_DISMOUNT_WATER_SURFACE && laraItem->Animation.ActiveState != UPV_STATE_MOUNT) { - FireUPVHarpoon(laraItem, UPVItem); - UPV->HarpoonTimer = HARPOON_RELOAD; + FireUPVHarpoon(UPVItem, laraItem); + UPV->HarpoonTimer = UPV_HARPOON_RELOAD_TIME; } } @@ -1035,37 +1004,37 @@ namespace TEN::Entities::Vehicles laraItem->Pose = UPVItem->Pose; AnimateItem(laraItem); - BackgroundCollision(laraItem, UPVItem); + BackgroundCollision(UPVItem, laraItem); DoVehicleCollision(UPVItem, UPV_RADIUS); - if (UPV->Flags & UPV_CONTROL) + if (UPV->Flags & UPV_FLAG_CONTROL) SoundEffect(SFX_TR3_VEHICLE_UPV_LOOP, (PHD_3DPOS*)&UPVItem->Pose.Position.x, SoundEnvironment::Always, 1.0f + (float)UPVItem->Animation.Velocity / 96.0f); UPVItem->Animation.AnimNumber = Objects[ID_UPV].animIndex + (laraItem->Animation.AnimNumber - Objects[ID_UPV_LARA_ANIMS].animIndex); UPVItem->Animation.FrameNumber = g_Level.Anims[UPVItem->Animation.AnimNumber].frameBase + (laraItem->Animation.FrameNumber - g_Level.Anims[laraItem->Animation.AnimNumber].frameBase); - if (UPV->Flags & UPV_SURFACE) + if (UPV->Flags & UPV_FLAG_SURFACE) Camera.targetElevation = -ANGLE(60.0f); else Camera.targetElevation = 0; return true; } - else if (UPV->Flags & UPV_DEAD) + else if (UPV->Flags & UPV_FLAG_DEAD) { AnimateItem(laraItem); if (probe.RoomNumber != UPVItem->RoomNumber) ItemNewRoom(lara->Vehicle, probe.RoomNumber); - BackgroundCollision(laraItem, UPVItem); + BackgroundCollision(UPVItem, laraItem); - UPV->XRot = 0; + UPV->TurnRate.x = 0; SetAnimation(UPVItem, UPV_ANIM_IDLE); UPVItem->Animation.VerticalVelocity = 0; UPVItem->Animation.Velocity = 0; - UPVItem->Animation.Airborne = true; + UPVItem->Animation.IsAirborne = true; AnimateItem(UPVItem); return true; diff --git a/TombEngine/Objects/TR3/Vehicles/upv.h b/TombEngine/Objects/TR3/Vehicles/upv.h index 484e35281..488f056d9 100644 --- a/TombEngine/Objects/TR3/Vehicles/upv.h +++ b/TombEngine/Objects/TR3/Vehicles/upv.h @@ -1,11 +1,16 @@ #pragma once -#include "Game/items.h" -#include "Game/collision/collide_room.h" +#include "Objects/Utils/VehicleHelpers.h" + +struct CollisionInfo; +struct ItemInfo; namespace TEN::Entities::Vehicles { void UPVInitialise(short itemNumber); - void UPVCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + + void UPVPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + void DoUPVMount(ItemInfo* UPVItem, ItemInfo* laraItem, VehicleMountType mountType); + void UPVEffects(short itemNumber); bool UPVControl(ItemInfo* laraItem, CollisionInfo* coll); } diff --git a/TombEngine/Objects/TR3/Vehicles/upv_info.h b/TombEngine/Objects/TR3/Vehicles/upv_info.h index 822c5064f..85938140d 100644 --- a/TombEngine/Objects/TR3/Vehicles/upv_info.h +++ b/TombEngine/Objects/TR3/Vehicles/upv_info.h @@ -4,13 +4,16 @@ namespace TEN::Entities::Vehicles { struct UPVInfo { - int Velocity; - int Rot; - int XRot; - short FanRot; - unsigned int HarpoonTimer; - bool HarpoonLeft; + Vector3Shrt TurnRate; + short TurbineRotation = 0; + short LeftRudderRotation = 0; + short RightRudderRotation = 0; - char Flags; + int Velocity = 0; + + unsigned int HarpoonTimer = 0; + bool HarpoonLeft = false; + + char Flags = NULL; }; } diff --git a/TombEngine/Objects/TR3/tr3_objects.cpp b/TombEngine/Objects/TR3/tr3_objects.cpp index bcd5d738e..885de658f 100644 --- a/TombEngine/Objects/TR3/tr3_objects.cpp +++ b/TombEngine/Objects/TR3/tr3_objects.cpp @@ -23,7 +23,7 @@ #include "Objects/TR3/Vehicles/big_gun.h" #include "Objects/TR3/Vehicles/kayak.h" #include "Objects/TR3/Vehicles/minecart.h" -#include "Objects/TR3/Vehicles/quad.h" +#include "Objects/TR3/Vehicles/quad_bike.h" #include "Objects/TR3/Vehicles/upv.h" #include "Objects/TR3/Vehicles/rubber_boat.h" /// necessary import @@ -388,7 +388,7 @@ static void StartVehicles(ObjectInfo* obj) if (obj->loaded) { obj->initialise = InitialiseQuadBike; - obj->collision = QuadBikeCollision; + obj->collision = QuadBikePlayerCollision; obj->hitEffect = HIT_RICOCHET; obj->savePosition = true; obj->saveAnim = true; @@ -401,7 +401,7 @@ static void StartVehicles(ObjectInfo* obj) { obj->initialise = InitialiseRubberBoat; obj->control = RubberBoatControl; - obj->collision = RubberBoatCollision; + obj->collision = RubberBoatPlayerCollision; obj->drawRoutine = DrawRubberBoat; obj->hitEffect = HIT_RICOCHET; obj->savePosition = true; @@ -415,7 +415,7 @@ static void StartVehicles(ObjectInfo* obj) if (obj->loaded) { obj->initialise = InitialiseKayak; - obj->collision = KayakCollision; + obj->collision = KayakPlayerCollision; //obj->drawRoutine = KayakDraw; obj->hitEffect = HIT_RICOCHET; obj->saveAnim = true; @@ -429,7 +429,7 @@ static void StartVehicles(ObjectInfo* obj) if (obj->loaded) { obj->initialise = InitialiseMinecart; - obj->collision = MinecartCollision; + obj->collision = MinecartPlayerCollision; obj->hitEffect = HIT_RICOCHET; obj->saveAnim = true; obj->saveFlags = true; @@ -455,7 +455,7 @@ static void StartVehicles(ObjectInfo* obj) { obj->initialise = UPVInitialise; obj->control = UPVEffects; - obj->collision = UPVCollision; + obj->collision = UPVPlayerCollision; // obj->drawRoutine = SubDraw; obj->hitEffect = HIT_RICOCHET; obj->saveAnim = true; diff --git a/TombEngine/Objects/TR4/Entity/tr4_baboon.cpp b/TombEngine/Objects/TR4/Entity/tr4_baboon.cpp index e2f9a1d88..874ba3e56 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_baboon.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_baboon.cpp @@ -354,7 +354,7 @@ namespace TEN::Entities::TR4 } else if (creature->Mood == MoodType::Attack) { - if (!(item->AIBits & FOLLOW) || (!item->Animation.Airborne && AI.distance <= pow(BABOON_ROLL_FORWARD_RANGE, 2))) + if (!(item->AIBits & FOLLOW) || (!item->Animation.IsAirborne && AI.distance <= pow(BABOON_ROLL_FORWARD_RANGE, 2))) { if (AI.bite && AI.distance < pow(BABOON_ATTACK_RANGE, 2)) { @@ -471,7 +471,7 @@ namespace TEN::Entities::TR4 if (AI.ahead && Lara.TargetEntity != item) item->Animation.TargetState = BABOON_STATE_IDLE; } - else if (item->AIBits & FOLLOW && (item->Animation.Airborne || AI.distance > pow(BABOON_FOLLOW_RANGE, 2))) + else if (item->AIBits & FOLLOW && (item->Animation.IsAirborne || AI.distance > pow(BABOON_FOLLOW_RANGE, 2))) item->Animation.TargetState = BABOON_STATE_IDLE; else if (creature->Mood == MoodType::Attack) { diff --git a/TombEngine/Objects/TR4/Entity/tr4_baddy.cpp b/TombEngine/Objects/TR4/Entity/tr4_baddy.cpp index e2b458f96..42e5c872e 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_baddy.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_baddy.cpp @@ -437,31 +437,31 @@ namespace TEN::Entities::TR4 switch (item->Animation.ActiveState) { case BADDY_STATE_DEATH: - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; currentCreature->LOT.IsMonkeying = false; if (item->Pose.Position.y >= item->Floor) { item->Pose.Position.y = item->Floor; item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; } break; case BADDY_STATE_MONKEY_TO_FREEFALL: item->Animation.TargetState = BADDY_STATE_FREEFALL; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; break; case BADDY_STATE_FREEFALL: - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; if (item->Pose.Position.y >= item->Floor) { item->Pose.Position.y = item->Floor; item->Animation.VerticalVelocity = 0; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.TargetState = BADDY_STATE_FREEFALL_LAND_DEATH; } @@ -1034,7 +1034,7 @@ namespace TEN::Entities::TR4 if (item->TouchBits) { SetAnimation(LaraItem, LA_JUMP_UP); - LaraItem->Animation.Airborne = true; + LaraItem->Animation.IsAirborne = true; LaraItem->Animation.VerticalVelocity = 2; LaraItem->Animation.VerticalVelocity = 1; LaraItem->Pose.Position.y += CLICK(0.75f); diff --git a/TombEngine/Objects/TR4/Entity/tr4_bat.cpp b/TombEngine/Objects/TR4/Entity/tr4_bat.cpp index 681fa5011..3f4e5eef0 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_bat.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_bat.cpp @@ -155,7 +155,7 @@ namespace TEN::Entities::TR4 if (item->Pose.Position.y >= item->Floor) { item->Animation.TargetState = BAT_STATE_DEATH; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Pose.Position.y = item->Floor; } else @@ -164,7 +164,7 @@ namespace TEN::Entities::TR4 item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = BAT_STATE_DEATH_FALL; item->Animation.TargetState = BAT_STATE_DEATH_FALL; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.Velocity = 0; } } diff --git a/TombEngine/Objects/TR4/Entity/tr4_big_beetle.cpp b/TombEngine/Objects/TR4/Entity/tr4_big_beetle.cpp index a1ea7c3b1..0d854c2b8 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_big_beetle.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_big_beetle.cpp @@ -97,14 +97,14 @@ namespace TEN::Entities::TR4 item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + BBEETLE_ANIM_DEATH_START; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = BBEETLE_STATE_DEATH_START; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.Velocity = 0; } } else if (item->Pose.Position.y >= item->Floor) { item->Pose.Position.y = item->Floor; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; item->Animation.TargetState = BBEETLE_STATE_DEATH_END; } diff --git a/TombEngine/Objects/TR4/Entity/tr4_enemy_jeep.cpp b/TombEngine/Objects/TR4/Entity/tr4_enemy_jeep.cpp index 226a1ebb7..6da447346 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_enemy_jeep.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_enemy_jeep.cpp @@ -369,11 +369,11 @@ namespace TEN::Entities::TR4 ItemNewRoom(itemNumber, probe.RoomNumber); if (item->Pose.Position.y < item->Floor) - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; else { item->Pose.Position.y = item->Floor; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; } diff --git a/TombEngine/Objects/TR4/Entity/tr4_harpy.cpp b/TombEngine/Objects/TR4/Entity/tr4_harpy.cpp index e8d3e2125..36b7bb853 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_harpy.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_harpy.cpp @@ -290,7 +290,7 @@ namespace TEN::Entities::TR4 item->Animation.AnimNumber = object->animIndex + 5; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = HARPY_STATE_DEATH_START; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; item->Animation.Velocity = 0; } @@ -311,7 +311,7 @@ namespace TEN::Entities::TR4 { item->Pose.Position.y = item->Floor; item->Animation.TargetState = HARPY_STATE_DEATH_END; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; } diff --git a/TombEngine/Objects/TR4/Entity/tr4_sas.cpp b/TombEngine/Objects/TR4/Entity/tr4_sas.cpp index f4eb55697..e6d66f81f 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_sas.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_sas.cpp @@ -650,7 +650,7 @@ namespace TEN::Entities::TR4 auto* item = &g_Level.Items[itemNumber]; if ((!(TrInput & IN_ACTION) || - laraItem->Animation.Airborne || + laraItem->Animation.IsAirborne || laraItem->Animation.ActiveState != LS_IDLE || laraItem->Animation.AnimNumber != LA_STAND_IDLE || Lara.Control.HandStatus != HandStatus::Free || diff --git a/TombEngine/Objects/TR4/Entity/tr4_setha.cpp b/TombEngine/Objects/TR4/Entity/tr4_setha.cpp index d2b77abc5..75956dd55 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_setha.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_setha.cpp @@ -348,7 +348,7 @@ namespace TEN::Entities::TR4 case 14: if (item->Animation.AnimNumber != Objects[item->Animation.AnimNumber].animIndex + 26) { - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; creature->MaxTurn = 0; creature->Target.y = LaraItem->Pose.Position.y; creature->LOT.Fly = 16; @@ -374,7 +374,7 @@ namespace TEN::Entities::TR4 } else { - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; creature->LOT.Fly = 0; if ((item->Pose.Position.y - item->Floor) > 0) diff --git a/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp b/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp index c3e7f18b1..98ab48283 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp @@ -551,7 +551,7 @@ namespace TEN::Entities::TR4 item->Animation.AnimNumber = Objects[ID_SKELETON].animIndex + 44; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = 23; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; creature->MaxTurn = 0; creature->LOT.IsJumping = false; } @@ -685,7 +685,7 @@ namespace TEN::Entities::TR4 item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 44; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = 23; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; creature->MaxTurn = 0; creature->LOT.IsJumping = false; } @@ -740,7 +740,7 @@ namespace TEN::Entities::TR4 item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 47; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = 24; - item->Animation.Airborne = true; + item->Animation.IsAirborne = true; creature->MaxTurn = 0; } diff --git a/TombEngine/Objects/TR4/Object/tr4_element_puzzle.cpp b/TombEngine/Objects/TR4/Object/tr4_element_puzzle.cpp index aab715577..b10a516f3 100644 --- a/TombEngine/Objects/TR4/Object/tr4_element_puzzle.cpp +++ b/TombEngine/Objects/TR4/Object/tr4_element_puzzle.cpp @@ -239,7 +239,7 @@ namespace TEN::Entities::TR4 laraItem->Animation.ActiveState != LS_IDLE || laraItem->Animation.AnimNumber != LA_STAND_IDLE || !laraInfo->Torch.IsLit || - laraItem->Animation.Airborne) + laraItem->Animation.IsAirborne) { if (laraItem->Animation.AnimNumber != LA_TORCH_LIGHT_3 || g_Level.Anims[LA_TORCH_LIGHT_3].frameBase + 16 || diff --git a/TombEngine/Objects/TR4/Trap/tr4_teethspike.cpp b/TombEngine/Objects/TR4/Trap/tr4_teethspike.cpp index c9ce13876..94bc4e169 100644 --- a/TombEngine/Objects/TR4/Trap/tr4_teethspike.cpp +++ b/TombEngine/Objects/TR4/Trap/tr4_teethspike.cpp @@ -129,7 +129,7 @@ namespace TEN::Entities::TR4 int bloodCount = 0; if ((item->ItemFlags[0] > 1024 || - LaraItem->Animation.Airborne) && + LaraItem->Animation.IsAirborne) && (item->TriggerFlags & 7) > 2 && (item->TriggerFlags & 7) < 6) { @@ -183,14 +183,14 @@ namespace TEN::Entities::TR4 TriggerBlood(dx, yBottom - (GetRandomControl() % dy), dz, GetRandomControl() << 1, 1); } - if (LaraItem->HitPoints <= 0) + if (LaraItem->HitPoints <= 0 && Lara.Vehicle == NO_ITEM) { int heightFromFloor = GetCollision(LaraItem).Position.Floor - LaraItem->Pose.Position.y; if (item->Pose.Position.y >= LaraItem->Pose.Position.y && heightFromFloor < CLICK(1)) { SetAnimation(LaraItem, LA_SPIKE_DEATH); - LaraItem->Animation.Airborne = false; + LaraItem->Animation.IsAirborne = false; } } } diff --git a/TombEngine/Objects/TR4/Vehicles/jeep.cpp b/TombEngine/Objects/TR4/Vehicles/jeep.cpp index c40d70be5..cb2c7a8d3 100644 --- a/TombEngine/Objects/TR4/Vehicles/jeep.cpp +++ b/TombEngine/Objects/TR4/Vehicles/jeep.cpp @@ -17,6 +17,7 @@ #include "Specific/level.h" #include "Sound/sound.h" #include "Objects/TR4/Vehicles/jeep_info.h" +#include "Objects/Utils/VehicleHelpers.h" #include "Renderer/Renderer11Enums.h" #include "Specific/prng.h" @@ -25,38 +26,51 @@ using std::vector; namespace TEN::Entities::Vehicles { - //bool QuadHandbrakeStarting; - //bool QuadCanHandbrakeStart; char JeepSmokeStart; bool JeepNoGetOff; - short Unk_0080DE1A; - int Unk_0080DDE8; - short Unk_0080DE24; - const vector JeepMeshJoints = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16 }; + const vector JeepJoints = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 16 }; + const vector JeepBrakeLightJoints = { 15, 16 }; - #define JEEP_DISMOUNT_DISTANCE 512 - #define JEEP_UNDO_TURN 91 - #define JEEP_FRONT 550 - #define JEEP_SIDE 256 - #define JEEP_SLIP 100 - #define JEEP_SLIP_SIDE 128 - #define JEEP_MAX_SPEED 0x8000 - #define JEEP_MAX_BACK 0x4000 + const vector JeepMountTypes = + { + VehicleMountType::LevelStart, + VehicleMountType::Left, + VehicleMountType::Right + }; - #define JEEP_IN_ACCELERATE IN_ACTION - #define JEEP_IN_BRAKE IN_JUMP + constexpr auto JEEP_MOUNT_DISTANCE = CLICK(2); + constexpr auto JEEP_DISMOUNT_DISTANCE = 512; + + constexpr auto JEEP_FRONT = 550; + constexpr auto JEEP_SIDE = 280; + constexpr auto JEEP_SLIP = 100; + constexpr auto JEEP_SLIP_SIDE = 128; + + constexpr auto JEEP_VELOCITY_MAX = 128 * VEHICLE_VELOCITY_SCALE; + constexpr auto JEEP_REVERSE_VELOCITY_MAX = 64 * VEHICLE_VELOCITY_SCALE; + + constexpr auto JEEP_CRASH_VELOCITY = 10922; + + #define JEEP_TURN_RATE_DECEL ANGLE(0.5f) + + // TODO: Simpler toggle. + #define JEEP_IN_TOGGLE_REVERSE IN_SPRINT + #define JEEP_IN_TOGGLE_FORWARD IN_WALK enum JeepState { - JS_STOP = 0, + JS_IDLE = 0, JS_DRIVE_FORWARD = 1, - //2 3 4 5 are the collide with walls states + JS_CRASH_RIGHT = 2, + JS_CRASH_BACK = 3, + JS_CRASH_LEFT = 4, + JS_CRASH_FORWARD = 5, JS_BRAKE = 6,//? JS_FWD_LEFT = 7,//FWD = forwards JS_FWD_RIGHT = 8, - JS_GETIN = 9, - JS_GETOFF = 10, + JS_MOUNT = 9, + JS_DISMOUNT = 10, JS_JUMP = 11, JS_LAND = 12, JS_BACK = 13,//when she's turning back, and when idle back.. hmm @@ -78,7 +92,10 @@ namespace TEN::Entities::Vehicles JA_JUMP = 7, JA_FWD_JUMP_LAND = 8, JA_GETIN_RIGHT = 9, - //10 11 12 13 are the collide with walls anims + JA_CRASH_FORWARD = 10, + JA_CRASH_LEFT = 11, + JA_CRASH_RIGHT = 12, + JA_CRASH_BACK = 13, JA_IDLE = 14, JA_FWD_RIGHT_START = 15, JA_FWD_RIGHT = 16, @@ -118,57 +135,102 @@ namespace TEN::Entities::Vehicles JEEP_FLAG_DEAD = (1 << 7) }; - static JeepInfo* GetJeepInfo(ItemInfo* jeepItem) + JeepInfo* GetJeepInfo(ItemInfo* jeepItem) { return (JeepInfo*)jeepItem->Data; } void InitialiseJeep(short itemNumber) { - ItemInfo* jeepItem = &g_Level.Items[itemNumber]; + auto* jeepItem = &g_Level.Items[itemNumber]; jeepItem->Data = JeepInfo(); auto* jeep = GetJeepInfo(jeepItem); - jeepItem->SetBits(JointBitType::Mesh, JeepMeshJoints); - jeep->velocity = 0; - jeep->revs = 0; - jeep->jeepTurn = 0; - jeep->fallSpeed = 0; - jeep->extraRotation = 0; - jeep->momentumAngle = jeepItem->Pose.Position.y; - jeep->pitch = 0; - jeep->flags = 0; - jeep->unknown2 = 0; - jeep->rot1 = 0; - jeep->rot2 = 0; - jeep->rot3 = 0; - jeep->rot4 = 0; + jeepItem->SetBits(JointBitType::Mesh, JeepJoints); + jeepItem->ClearBits(JointBitType::Mesh, { 17 }); + jeep->MomentumAngle = jeepItem->Pose.Orientation.y; } - static int TestJeepHeight(ItemInfo* jeepItem, int dz, int dx, Vector3Int* pos) + void JeepPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) { - float sinX = phd_sin(jeepItem->Pose.Orientation.x); - float sinY = phd_sin(jeepItem->Pose.Orientation.y); - float cosY = phd_cos(jeepItem->Pose.Orientation.y); - float sinZ = phd_sin(jeepItem->Pose.Orientation.z); + auto* jeepItem = &g_Level.Items[itemNumber]; + auto* jeep = GetJeepInfo(jeepItem); + auto* lara = GetLaraInfo(laraItem); - pos->x = jeepItem->Pose.Position.x + (dz * sinY) + (dx * cosY); - pos->y = jeepItem->Pose.Position.y - (dz * sinX) + (dx * sinZ); - pos->z = jeepItem->Pose.Position.z + (dz * cosY) - (dx * sinY); + if (laraItem->HitPoints <= 0 && lara->Vehicle != NO_ITEM) + return; - auto probe = GetCollision(pos->x, pos->y, pos->z, jeepItem->RoomNumber); + auto mountType = GetVehicleMountType(jeepItem, laraItem, coll, JeepMountTypes, JEEP_MOUNT_DISTANCE); + if (mountType == VehicleMountType::None) + ObjectCollision(itemNumber, laraItem, coll); + else + { + lara->Vehicle = itemNumber; + DoJeepMount(jeepItem, laraItem, mountType); + } + } - if (pos->y < probe.Position.Ceiling || probe.Position.Ceiling == NO_HEIGHT) - return NO_HEIGHT; + void DoJeepMount(ItemInfo* jeepItem, ItemInfo* laraItem, VehicleMountType mountType) + { + auto* jeep = GetJeepInfo(jeepItem); + auto* lara = GetLaraInfo(laraItem); - return probe.Position.Floor; + // HACK: Hardcoded jeep keys check. + /*if (g_Gui.GetInventoryItemChosen() == ID_PUZZLE_ITEM1) + { + g_Gui.SetInventoryItemChosen(NO_ITEM); + return true; + } + else + { + if (g_Gui.IsObjectInInventory(ID_PUZZLE_ITEM1)) + g_Gui.SetEnterInventory(ID_PUZZLE_ITEM1); + + return false; + }*/ + + switch (mountType) + { + case VehicleMountType::LevelStart: + laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_IDLE; + laraItem->Animation.ActiveState = JS_IDLE; + laraItem->Animation.TargetState = JS_IDLE; + break; + + case VehicleMountType::Left: + laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_GETIN_LEFT; + laraItem->Animation.ActiveState = JS_MOUNT; + laraItem->Animation.TargetState = JS_MOUNT; + break; + + default: + case VehicleMountType::Right: + laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_GETIN_RIGHT; + laraItem->Animation.ActiveState = JS_MOUNT; + laraItem->Animation.TargetState = JS_MOUNT; + break; + } + laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; + + DoVehicleFlareDiscard(laraItem); + ResetLaraFlex(laraItem); + laraItem->Pose.Position = jeepItem->Pose.Position; + laraItem->Pose.Orientation.y = jeepItem->Pose.Orientation.y; + lara->Control.HandStatus = HandStatus::Busy; + lara->HitDirection = -1; + jeepItem->HitPoints = 1; + jeepItem->Flags |= TRIGGERED; + jeep->Revs = 0; + jeep->Gear = 0; + + AnimateItem(laraItem); } static int DoJeepShift(ItemInfo* jeepItem, Vector3Int* pos, Vector3Int* old) { int x = pos->x / SECTOR(1); int z = pos->z / SECTOR(1); - int oldX = old->x / SECTOR(1); + int oldX = old->x / SECTOR(1); int oldZ = old->z / SECTOR(1); int shiftX = pos->x & (WALL_SIZE - 1); int shiftZ = pos->z & (WALL_SIZE - 1); @@ -187,11 +249,12 @@ namespace TEN::Entities::Vehicles } else { - jeepItem->Pose.Position.z += WALL_SIZE - shiftZ; + jeepItem->Pose.Position.z += WALL_SIZE - 1 - shiftZ; return (jeepItem->Pose.Position.x - pos->x); } } - else if (z == oldZ) + + if (z == oldZ) { if (x > oldX) { @@ -200,113 +263,112 @@ namespace TEN::Entities::Vehicles } else { - jeepItem->Pose.Position.x += WALL_SIZE - shiftX; + jeepItem->Pose.Position.x += WALL_SIZE - 1 - shiftX; return (pos->z - jeepItem->Pose.Position.z); } } - else + + x = 0; + z = 0; + + short roomNumber = jeepItem->RoomNumber; + FloorInfo* floor = GetFloor(old->x, pos->y, pos->z, &roomNumber); + int height = GetFloorHeight(floor, old->x, pos->y, pos->z); + + if (height < old->y - STEP_SIZE) { - x = 0; - z = 0; - - short roomNumber = jeepItem->RoomNumber; - FloorInfo* floor = GetFloor(old->x, pos->y, pos->z, &roomNumber); - int height = GetFloorHeight(floor, old->x, pos->y, pos->z); - if (height < old->y - STEP_SIZE) - { - if (pos->z > old->z) - z = -shiftZ - 1; - else - z = WALL_SIZE - shiftZ; - } - - roomNumber = jeepItem->RoomNumber; - floor = GetFloor(pos->x, pos->y, old->z, &roomNumber); - height = GetFloorHeight(floor, pos->x, pos->y, old->z); - if (height < old->y - STEP_SIZE) - { - if (pos->x > old->x) - x = -shiftX - 1; - else - x = WALL_SIZE - shiftX; - } - - if (x && z) - { - jeepItem->Pose.Position.z += z; - jeepItem->Pose.Position.x += x; - } - else if (z) - { - jeepItem->Pose.Position.z += z; - if (z > 0) - return (jeepItem->Pose.Position.x - pos->x); - else - return (pos->x - jeepItem->Pose.Position.x); - } - else if (x) - { - jeepItem->Pose.Position.x += x; - if (x > 0) - return (pos->z - jeepItem->Pose.Position.z); - else - return (jeepItem->Pose.Position.z - pos->z); - } + if (pos->z > old->z) + z = -1 - shiftZ; else - { - jeepItem->Pose.Position.z += (old->z - pos->z); - jeepItem->Pose.Position.x += (old->x - pos->x); - } + z = WALL_SIZE + 1 - shiftZ; } + roomNumber = jeepItem->RoomNumber; + floor = GetFloor(pos->x, pos->y, old->z, &roomNumber); + height = GetFloorHeight(floor, pos->x, pos->y, old->z); + + if (height < old->y - STEP_SIZE) + { + if (pos->x > old->x) + x = -1 - shiftX; + else + x = WALL_SIZE + 1 - shiftX; + } + + if (x && z) + { + jeepItem->Pose.Position.z += z; + jeepItem->Pose.Position.x += x; + } + + if (z) + { + jeepItem->Pose.Position.z += z; + if (z > 0) + return (jeepItem->Pose.Position.x - pos->x); + else + return (pos->x - jeepItem->Pose.Position.x); + } + + if (x) + { + jeepItem->Pose.Position.x += x; + if (x > 0) + return (pos->z - jeepItem->Pose.Position.z); + else + return (jeepItem->Pose.Position.z - pos->z); + } + + jeepItem->Pose.Position.z += (old->z - pos->z); + jeepItem->Pose.Position.x += (old->x - pos->x); + return 0; } - static int DoJeepDynamics(ItemInfo* laraItem, int height, int speed, int* y, int flags) + static int DoJeepDynamics(ItemInfo* laraItem, int height, int verticalVelocity, int* yPos, int flags) { - int result = 0; - // Grounded. - if (height <= *y) + if (height <= *yPos) { if (flags) - result = speed; + return verticalVelocity; else { - int temp = height - *y; + int kick = height - *yPos; - if (temp < -80) - temp = -80; + if (kick < -80) + kick = -80; - result = ((temp - speed) / 16) + speed; + verticalVelocity += ((kick - verticalVelocity) / 16); - if (*y > height) - *y = height; + if (*yPos > height) + *yPos = height; } } - // Airborne. + // IsAirborne. else { - *y += speed; - if (*y <= height - 32) + *yPos += verticalVelocity; + + if (*yPos <= height - 32) { if (flags) - result = flags + (flags / 2) + speed; + verticalVelocity += flags + (flags / 2); else - result = speed + GRAVITY; + verticalVelocity += (int)((float)GRAVITY * 1.5f); } else { - *y = height; + *yPos = height; - if (speed > 150) - laraItem->HitPoints += 150 - speed; + if (verticalVelocity > 150) + laraItem->HitPoints += 150 - verticalVelocity; - result = 0; + verticalVelocity = 0; } } - return result; + return verticalVelocity; } static bool JeepCanGetOff(ItemInfo* jeepItem, ItemInfo* laraItem) @@ -399,7 +461,7 @@ namespace TEN::Entities::Vehicles { auto* lara = GetLaraInfo(laraItem); - if (laraItem->Animation.ActiveState == JS_GETOFF) + if (laraItem->Animation.ActiveState == JS_DISMOUNT) { if (laraItem->Animation.FrameNumber == g_Level.Anims[laraItem->Animation.AnimNumber].frameEnd) { @@ -417,105 +479,23 @@ namespace TEN::Entities::Vehicles return true; } - static bool GetOnJeep(int itemNumber, ItemInfo* laraItem) - { - auto* jeepItem = &g_Level.Items[itemNumber]; - auto* lara = GetLaraInfo(laraItem); - - if (!(TrInput & IN_ACTION) && g_Gui.GetInventoryItemChosen() != ID_PUZZLE_ITEM1) - return false; - - if (jeepItem->Flags & 0x100) - return false; - - if (lara->Control.HandStatus != HandStatus::Free) - return false; - - if (laraItem->Animation.ActiveState != LS_IDLE) - return false; - - if (laraItem->Animation.AnimNumber != LA_STAND_IDLE) - return false; - - if (laraItem->Animation.Airborne) - return false; - - if (abs(jeepItem->Pose.Position.y - laraItem->Pose.Position.y) >= STEP_SIZE) - return false; - - if (!TestBoundsCollide(jeepItem, laraItem, 100)) - return false; - - int floorHeight = GetCollision(jeepItem).Position.Floor; - if (floorHeight < -32000) - return false; - - short angle = phd_atan(jeepItem->Pose.Position.z - laraItem->Pose.Position.z, jeepItem->Pose.Position.x - laraItem->Pose.Position.x); - angle -= jeepItem->Pose.Orientation.y; - - if ((angle > -ANGLE(45)) && (angle < ANGLE(135))) - { - int tempAngle = laraItem->Pose.Orientation.y - jeepItem->Pose.Orientation.y; - if (tempAngle > ANGLE(45) && tempAngle < ANGLE(135)) - { - if (g_Gui.GetInventoryItemChosen() == ID_PUZZLE_ITEM1) - { - g_Gui.SetInventoryItemChosen(NO_ITEM); - return true; - } - else - { - if (g_Gui.IsObjectInInventory(ID_PUZZLE_ITEM1)) - g_Gui.SetEnterInventory(ID_PUZZLE_ITEM1); - - return false; - } - } - else - return false; - } - else - { - int tempAngle = laraItem->Pose.Orientation.y - jeepItem->Pose.Orientation.y; - if (tempAngle > ANGLE(225) && tempAngle < ANGLE(315)) - { - if (g_Gui.GetInventoryItemChosen() == ID_PUZZLE_ITEM1) - { - g_Gui.SetInventoryItemChosen(NO_ITEM); - return true; - } - else - { - if (g_Gui.IsObjectInInventory(ID_PUZZLE_ITEM1)) - g_Gui.SetEnterInventory(ID_PUZZLE_ITEM1); - - return false; - } - } - else - return false; - } - - return false; - } - - static int GetJeepCollisionAnim(ItemInfo* jeepItem, Vector3Int* p) + static int GetJeepCollisionAnim(ItemInfo* jeepItem, Vector3Int* pos) { auto* jeep = GetJeepInfo(jeepItem); - if (jeep->unknown2 != 0) + if (jeep->Gear != 0) return 0; - p->x = jeepItem->Pose.Position.x - p->x; - p->z = jeepItem->Pose.Position.z - p->z; + pos->x = jeepItem->Pose.Position.x - pos->x; + pos->z = jeepItem->Pose.Position.z - pos->z; - if (p->x || p->z) + if (pos->x || pos->z) { float sinY = phd_sin(jeepItem->Pose.Orientation.y); float cosY = phd_cos(jeepItem->Pose.Orientation.y); - int front = (p->z * cosY) + (p->x * sinY); - int side = (p->z * -sinY) + (p->x * cosY); + int front = (pos->z * cosY) + (pos->x * sinY); + int side = (pos->z * -sinY) + (pos->x * cosY); if (abs(front) > abs(side)) return ((front > 0) + 13); @@ -533,99 +513,80 @@ namespace TEN::Entities::Vehicles Vector3Int f_old, b_old, mm_old, mt_old, mb_old; - int hf_old = TestJeepHeight(jeepItem, JEEP_FRONT, -JEEP_SIDE, &f_old); - int hb_old = TestJeepHeight(jeepItem, JEEP_FRONT, JEEP_SIDE, &b_old); - int hmm_old = TestJeepHeight(jeepItem, -(JEEP_FRONT + 50), -JEEP_SIDE, &mm_old); - int hmt_old = TestJeepHeight(jeepItem, -(JEEP_FRONT + 50), JEEP_SIDE, &mt_old); - int hmb_old = TestJeepHeight(jeepItem, -(JEEP_FRONT + 50), 0, (Vector3Int*)&mb_old); + int hf_old = GetVehicleHeight(jeepItem, JEEP_FRONT, -JEEP_SIDE, true, &f_old); + int hb_old = GetVehicleHeight(jeepItem, JEEP_FRONT, JEEP_SIDE, true, &b_old); + int hmm_old = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), -JEEP_SIDE, true, &mm_old); + int hmt_old = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), JEEP_SIDE, true, &mt_old); + int hmb_old = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), 0, true, &mb_old); - Vector3Int oldPos; - oldPos.x = jeepItem->Pose.Position.x; - oldPos.y = jeepItem->Pose.Position.y; - oldPos.z = jeepItem->Pose.Position.z; - - if (mm_old.y > hmm_old) - mm_old.y = hmm_old; - if (mt_old.y > hmt_old) - mt_old.y = hmt_old; - if (f_old.y > hf_old) - f_old.y = hf_old; - if (b_old.y > hb_old) - b_old.y = hb_old; - if (mb_old.y > hmb_old) - mb_old.y = hmb_old; + auto oldPos = jeepItem->Pose.Position; short rot = 0; + JeepNoGetOff = 0; - if (oldPos.y <= jeepItem->Floor - 8 ) + if (oldPos.y <= jeepItem->Floor - 8) { - if (jeep->jeepTurn < -JEEP_UNDO_TURN) - jeep->jeepTurn += JEEP_UNDO_TURN; - else if (jeep->jeepTurn > JEEP_UNDO_TURN) - jeep->jeepTurn -= JEEP_UNDO_TURN; + if (jeep->TurnRate < -JEEP_TURN_RATE_DECEL) + jeep->TurnRate += JEEP_TURN_RATE_DECEL; + else if (jeep->TurnRate > JEEP_TURN_RATE_DECEL) + jeep->TurnRate -= JEEP_TURN_RATE_DECEL; else - jeep->jeepTurn = 0; + jeep->TurnRate = 0; - jeepItem->Pose.Orientation.y += jeep->jeepTurn + jeep->extraRotation; - jeep->momentumAngle += ((jeepItem->Pose.Orientation.y - jeep->momentumAngle) / 32); + jeepItem->Pose.Orientation.y += (short)(jeep->TurnRate + jeep->ExtraRotation); + jeep->MomentumAngle += ((short)(jeepItem->Pose.Orientation.y - jeep->MomentumAngle) / 32); } else { - short rot2 = 0; - short momentum = 0; - - if (jeep->jeepTurn < -ANGLE(1)) - jeep->jeepTurn += ANGLE(1); - else if (jeep->jeepTurn > ANGLE(1)) - jeep->jeepTurn -= ANGLE(1); + if (jeep->TurnRate < -ANGLE(1)) + jeep->TurnRate += ANGLE(1); + else if (jeep->TurnRate > ANGLE(1)) + jeep->TurnRate -= ANGLE(1); else - jeep->jeepTurn = 0; + jeep->TurnRate = 0; - jeepItem->Pose.Orientation.y += jeep->jeepTurn + jeep->extraRotation; + jeepItem->Pose.Orientation.y += jeep->TurnRate + jeep->ExtraRotation; - rot = jeepItem->Pose.Orientation.y - jeep->momentumAngle; - momentum = 728 - ((3 * jeep->velocity) / 2048); + rot = jeepItem->Pose.Orientation.y - jeep->MomentumAngle; + short momentum = (short)(728 - ((3 * jeep->Velocity) / 2048)); - if (!(TrInput & IN_ACTION) && jeep->velocity > 0) + if (!(TrInput & IN_ACTION) && jeep->Velocity > 0) momentum -= (momentum / 4); - if (rot >= -273) + if (rot < -273) { - if (rot <= 273) - jeep->momentumAngle = jeepItem->Pose.Orientation.y; - else - { - if (rot > 13650) - { - jeepItem->Pose.Position.y -= 41; - jeepItem->Animation.VerticalVelocity = -6 - (GetRandomControl() & 3); - jeep->jeepTurn = 0; - jeep->velocity -= (jeep->velocity / 8); - } - - if (rot <= 16380) - jeep->momentumAngle += momentum; - else - jeep->momentumAngle = jeepItem->Pose.Orientation.y - 16380; - } - } - else - { - if (rot < -13650) + if (rot < -ANGLE(75)) { jeepItem->Pose.Position.y -= 41; jeepItem->Animation.VerticalVelocity = -6 - (GetRandomControl() & 3); - jeep->jeepTurn = 0; - jeep->velocity -= (jeep->velocity / 8); + jeep->TurnRate = 0; + jeep->Velocity -= (jeep->Velocity / 8); } - if (rot >= -16380) - jeep->momentumAngle -= momentum; + if (rot < -ANGLE(90)) + jeep->MomentumAngle = jeepItem->Pose.Orientation.y + ANGLE(90); else - jeep->momentumAngle = jeepItem->Pose.Orientation.y + 16380; + jeep->MomentumAngle -= momentum; } - } + else if (rot > 273) + { + if (rot > ANGLE(75)) + { + jeepItem->Pose.Position.y -= 41; + jeepItem->Animation.VerticalVelocity = -6 - (GetRandomControl() & 3); + jeep->TurnRate = 0; + jeep->Velocity -= (jeep->Velocity / 8); + } + if (rot > ANGLE(90)) + jeep->MomentumAngle = jeepItem->Pose.Orientation.y - ANGLE(90); + else + jeep->MomentumAngle += momentum; + } + else + jeep->MomentumAngle = jeepItem->Pose.Orientation.y; + } + short roomNumber = jeepItem->RoomNumber; FloorInfo* floor = GetFloor(jeepItem->Pose.Position.x, jeepItem->Pose.Position.y, jeepItem->Pose.Position.z, &roomNumber); int height = GetFloorHeight(floor, jeepItem->Pose.Position.x, jeepItem->Pose.Position.y, jeepItem->Pose.Position.z); @@ -636,27 +597,25 @@ namespace TEN::Entities::Vehicles else speed = jeepItem->Animation.Velocity * phd_cos(jeepItem->Pose.Orientation.x); - jeepItem->Pose.Position.x += speed * phd_sin(jeep->momentumAngle); - jeepItem->Pose.Position.z += speed * phd_cos(jeep->momentumAngle); + jeepItem->Pose.Position.x += speed * phd_sin(jeep->MomentumAngle); + jeepItem->Pose.Position.z += speed * phd_cos(jeep->MomentumAngle); - int slip = 0; if (jeepItem->Pose.Position.y >= height) { - slip = JEEP_SLIP * phd_sin(jeepItem->Pose.Orientation.x); + short slip = JEEP_SLIP * phd_sin(jeepItem->Pose.Orientation.x); if (abs(slip) > 16) { JeepNoGetOff = 1; - if (slip >= 0) - slip = jeep->velocity - (SQUARE(slip - 16) / 2); + + if (slip < 0) + jeep->Velocity += SQUARE(slip + 16) / 2; else - slip = (SQUARE(slip + 16) / 2) + jeep->velocity; - jeep->velocity = slip; + jeep->Velocity -= SQUARE(slip - 16) / 2; } - else - JeepNoGetOff = 0; slip = JEEP_SLIP_SIDE * phd_sin(jeepItem->Pose.Orientation.z); + if (abs(slip) > JEEP_SLIP_SIDE / 4) { JeepNoGetOff = 1; @@ -672,20 +631,18 @@ namespace TEN::Entities::Vehicles jeepItem->Pose.Position.z += (slip - 24) * phd_cos(jeepItem->Pose.Orientation.y - ANGLE(90)); } } - else - JeepNoGetOff = 0; } - if (jeep->velocity > JEEP_MAX_SPEED) - jeep->velocity = JEEP_MAX_SPEED; - else if (jeep->velocity < -JEEP_MAX_BACK) - jeep->velocity = -JEEP_MAX_BACK; + if (jeep->Velocity > JEEP_VELOCITY_MAX) + jeep->Velocity = JEEP_VELOCITY_MAX; + else if (jeep->Velocity < -JEEP_REVERSE_VELOCITY_MAX) + jeep->Velocity = -JEEP_REVERSE_VELOCITY_MAX; Vector3Int movedPos; movedPos.x = jeepItem->Pose.Position.x; movedPos.z = jeepItem->Pose.Position.z; - if (!(jeepItem->Flags & ONESHOT)) + if (!(jeepItem->Flags & IFLAG_INVISIBLE)) DoVehicleCollision(jeepItem, JEEP_FRONT); Vector3Int f, b, mm, mt, mb; @@ -693,11 +650,11 @@ namespace TEN::Entities::Vehicles int rot1 = 0; int rot2 = 0; - int hf = TestJeepHeight(jeepItem, JEEP_FRONT, -JEEP_SIDE, (Vector3Int*)&f); + int hf = GetVehicleHeight(jeepItem, JEEP_FRONT, -JEEP_SIDE, false, &f); if (hf < f_old.y - STEP_SIZE) rot1 = abs(4 * DoJeepShift(jeepItem, &f, &f_old)); - int hmm = TestJeepHeight(jeepItem, -(JEEP_FRONT + 50), -JEEP_SIDE, (Vector3Int*)&mm); + int hmm = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), -JEEP_SIDE, false, &mm); if (hmm < mm_old.y - STEP_SIZE) { if (rot) @@ -706,15 +663,15 @@ namespace TEN::Entities::Vehicles rot1 = -abs(4 * DoJeepShift(jeepItem, &mm, &mm_old)); } - int hb = TestJeepHeight(jeepItem, JEEP_FRONT, JEEP_SIDE, (Vector3Int*)&b); + int hb = GetVehicleHeight(jeepItem, JEEP_FRONT, JEEP_SIDE, false, &b); if (hb < b_old.y - STEP_SIZE) rot2 = -abs(4 * DoJeepShift(jeepItem, &b, &b_old)); - int hmb = TestJeepHeight(jeepItem, -(JEEP_FRONT + 50), 0, (Vector3Int*)&mb); + int hmb = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), 0, false, &mb); if (hmb < mb_old.y - STEP_SIZE) DoJeepShift(jeepItem, &mb, &mb_old); - int hmt = TestJeepHeight(jeepItem, -(JEEP_FRONT + 50), JEEP_SIDE, (Vector3Int*)&mt); + int hmt = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), JEEP_SIDE, false, &mt); if (hmt < mt_old.y - STEP_SIZE) { if (rot2) @@ -731,60 +688,56 @@ namespace TEN::Entities::Vehicles if (GetFloorHeight(floor, jeepItem->Pose.Position.x, jeepItem->Pose.Position.y, jeepItem->Pose.Position.z) < jeepItem->Pose.Position.y - STEP_SIZE) DoJeepShift(jeepItem, (Vector3Int*)&jeepItem->Pose, &oldPos); - if (!jeep->velocity) + if (!jeep->Velocity) rot1 = 0; - jeep->extraRotation = rot1; - - /*jeep->unknown0 += rot1 / 2; + jeep->ExtraRotationDrift = (jeep->ExtraRotationDrift + rot1) / 2; - if (abs(jeep->unknown0) < 2) - jeep->unknown0 = 0; + if (abs(jeep->ExtraRotationDrift) < 2) + jeep->ExtraRotationDrift = 0; - if (abs(jeep->unknown0 - jeep->extraRotation) >= 4) - jeep->extraRotation += ((jeep->unknown0 - jeep->extraRotation) / 4); + if (abs(jeep->ExtraRotationDrift - jeep->ExtraRotation) < 4) + jeep->ExtraRotation = jeep->ExtraRotationDrift; else - jeep->extraRotation = jeep->unknown0; - */ // just incase this code is ever uncommented. - int newspeed = 0; - int collide = GetJeepCollisionAnim(jeepItem, &movedPos); + jeep->ExtraRotation += ((jeep->ExtraRotationDrift - jeep->ExtraRotation) / 4); + + int anim = GetJeepCollisionAnim(jeepItem, &movedPos); - if (collide) + if (anim) { - newspeed = (jeepItem->Pose.Position.z - oldPos.z) * phd_cos(jeep->momentumAngle) + (jeepItem->Pose.Position.x - oldPos.x) * phd_sin(jeep->momentumAngle); + int newspeed = (jeepItem->Pose.Position.z - oldPos.z) * phd_cos(jeep->MomentumAngle) + (jeepItem->Pose.Position.x - oldPos.x) * phd_sin(jeep->MomentumAngle); newspeed *= 256; - if ((&g_Level.Items[lara->Vehicle] == jeepItem) && (jeep->velocity == JEEP_MAX_SPEED) && (newspeed < (JEEP_MAX_SPEED - 10))) + if ((&g_Level.Items[lara->Vehicle] == jeepItem) && (jeep->Velocity == JEEP_VELOCITY_MAX) && (newspeed < (JEEP_VELOCITY_MAX - 10))) { - DoDamage(laraItem, (JEEP_MAX_SPEED - newspeed) / 128); + DoDamage(laraItem, (JEEP_VELOCITY_MAX - newspeed) / 128); } - if (jeep->velocity > 0 && newspeed < jeep->velocity) - jeep->velocity = (newspeed > 0) ? 0 : newspeed; + if (jeep->Velocity > 0 && newspeed < jeep->Velocity) + jeep->Velocity = (newspeed < 0) ? 0 : newspeed; + else if (jeep->Velocity < 0 && newspeed > jeep->Velocity) + jeep->Velocity = (newspeed > 0) ? 0 : newspeed; - else if (jeep->velocity < 0 && newspeed > jeep->velocity) - jeep->velocity = (newspeed < 0) ? 0 : newspeed; - - if (jeep->velocity < JEEP_MAX_BACK) - jeep->velocity = JEEP_MAX_BACK; + if (jeep->Velocity < -JEEP_REVERSE_VELOCITY_MAX) + jeep->Velocity = -JEEP_REVERSE_VELOCITY_MAX; } - return collide; + return anim; } static int JeepUserControl(ItemInfo* jeepItem, ItemInfo* laraItem, int height, int* pitch) { auto* jeep = GetJeepInfo(jeepItem); - if (laraItem->Animation.ActiveState == JS_GETOFF || laraItem->Animation.TargetState == JS_GETOFF) + if (laraItem->Animation.ActiveState == JS_DISMOUNT || laraItem->Animation.TargetState == JS_DISMOUNT) TrInput = 0; - if (jeep->revs <= 16) - jeep->revs = 0; + if (jeep->Revs <= 16) + jeep->Revs = 0; else { - jeep->velocity += (jeep->revs / 16); - jeep->revs -= (jeep->revs / 8); + jeep->Velocity += (jeep->Revs / 16); + jeep->Revs -= (jeep->Revs / 8); } int rot1 = 0; @@ -792,16 +745,16 @@ namespace TEN::Entities::Vehicles if (jeepItem->Pose.Position.y >= height - STEP_SIZE) { - if (!jeep->velocity) + if (!jeep->Velocity) { if (TrInput & IN_LOOK) LookUpDown(laraItem); } - if (abs(jeep->velocity) <= JEEP_MAX_SPEED / 2) + if (abs(jeep->Velocity) <= JEEP_VELOCITY_MAX / 2) { - rot1 = ANGLE(5) * abs(jeep->velocity) / 16384; - rot2 = 60 * abs(jeep->velocity) / 16384 + ANGLE(1); + rot1 = ANGLE(5) * abs(jeep->Velocity) / 16384; + rot2 = 60 * abs(jeep->Velocity) / 16384 + ANGLE(1); } else { @@ -809,112 +762,114 @@ namespace TEN::Entities::Vehicles rot2 = 242; } - if (jeep->velocity < 0) + if (jeep->Velocity < 0) { - if (TrInput & (IN_RIGHT | IN_RSTEP)) + if (TrInput & VEHICLE_IN_RIGHT) { - jeep->jeepTurn -= rot2; - if (jeep->jeepTurn < -rot1) - jeep->jeepTurn = -rot1; + jeep->TurnRate -= rot2; + if (jeep->TurnRate < -rot1) + jeep->TurnRate = -rot1; } - else if (TrInput & (IN_LEFT | IN_LSTEP)) + else if (TrInput & VEHICLE_IN_LEFT) { - jeep->jeepTurn += rot2; - if (jeep->jeepTurn > rot1) - jeep->jeepTurn = rot1; + jeep->TurnRate += rot2; + if (jeep->TurnRate > rot1) + jeep->TurnRate = rot1; } } - else if (jeep->velocity > 0) + else if (jeep->Velocity > 0) { - if (TrInput & (IN_RIGHT | IN_RSTEP)) + if (TrInput & VEHICLE_IN_RIGHT) { - jeep->jeepTurn += rot2; - if (jeep->jeepTurn > rot1) - jeep->jeepTurn = rot1; + jeep->TurnRate += rot2; + if (jeep->TurnRate > rot1) + jeep->TurnRate = rot1; } - else if (TrInput & (IN_LEFT | IN_LSTEP)) + else if (TrInput & VEHICLE_IN_LEFT) { - jeep->jeepTurn -= rot2; - if (jeep->jeepTurn < -rot1) - jeep->jeepTurn = -rot1; + jeep->TurnRate -= rot2; + if (jeep->TurnRate < -rot1) + jeep->TurnRate = -rot1; } } - // Break/reverse - if (TrInput & JEEP_IN_BRAKE) + // Brake/reverse + if (TrInput & VEHICLE_IN_BRAKE) { - if (jeep->velocity < 0) + if (jeep->Velocity < 0) { - jeep->velocity += 768; - if (jeep->velocity > 0) - jeep->velocity = 0; + jeep->Velocity += 3 * VEHICLE_VELOCITY_SCALE; + if (jeep->Velocity > 0) + jeep->Velocity = 0; } - else if (jeep->velocity > 0) + else if (jeep->Velocity > 0) { - jeep->velocity -= 768; - if (jeep->velocity < 0) - jeep->velocity = 0; + jeep->Velocity -= 3 * VEHICLE_VELOCITY_SCALE; + if (jeep->Velocity < 0) + jeep->Velocity = 0; } } // Accelerate - else if (TrInput & JEEP_IN_ACCELERATE) + else if (TrInput & VEHICLE_IN_ACCELERATE) { - if (jeep->unknown2) + if (jeep->Gear) { - if (jeep->unknown2 == 1 && jeep->velocity > -JEEP_MAX_BACK) - jeep->velocity -= (abs(-JEEP_MAX_BACK - jeep->velocity) / 8) - 2; - else if (jeep->unknown2 == 1 && jeep->velocity < -JEEP_MAX_BACK) - jeep->velocity = -JEEP_MAX_BACK; + if (jeep->Gear == 1 && jeep->Velocity > -JEEP_REVERSE_VELOCITY_MAX) + jeep->Velocity -= (abs(-JEEP_REVERSE_VELOCITY_MAX - jeep->Velocity) / 8) - 2; + else if (jeep->Gear == 1 && jeep->Velocity < -JEEP_REVERSE_VELOCITY_MAX) + jeep->Velocity = -JEEP_REVERSE_VELOCITY_MAX; } else { - if (jeep->velocity < JEEP_MAX_SPEED) + if (jeep->Velocity < JEEP_VELOCITY_MAX) { - if (jeep->velocity < 0x4000) - jeep->velocity += 8 + ((0x4000 + 0x800 - jeep->velocity) / 8); - else if (jeep->velocity < 0x7000) - jeep->velocity += 4 + ((0x7000 + 0x800 - jeep->velocity) / 16); - else if (jeep->velocity < JEEP_MAX_SPEED) - jeep->velocity += 2 + ((JEEP_MAX_SPEED - jeep->velocity) / 8); + if (jeep->Velocity < (64 * VEHICLE_VELOCITY_SCALE)) + jeep->Velocity += 8 + (((64 * VEHICLE_VELOCITY_SCALE) + (8 * VEHICLE_VELOCITY_SCALE) - jeep->Velocity) / 8); + else if (jeep->Velocity < (112 * VEHICLE_VELOCITY_SCALE)) + jeep->Velocity += 4 + (((112 * VEHICLE_VELOCITY_SCALE) + (8 * VEHICLE_VELOCITY_SCALE) - jeep->Velocity) / 16); + else if (jeep->Velocity < JEEP_VELOCITY_MAX) + jeep->Velocity += 2 + ((JEEP_VELOCITY_MAX - jeep->Velocity) / 8); } else - jeep->velocity = JEEP_MAX_SPEED; + jeep->Velocity = JEEP_VELOCITY_MAX; } - jeep->velocity -= (abs(jeepItem->Pose.Orientation.y - jeep->momentumAngle) / 64); + jeep->Velocity -= (abs(jeepItem->Pose.Orientation.y - jeep->MomentumAngle) / 64); } - else if (jeep->velocity > 256) - jeep->velocity -= 256; - else if (jeep->velocity < -256) - jeep->velocity += 256; + else if (jeep->Velocity > (1 * VEHICLE_VELOCITY_SCALE)) + jeep->Velocity -= 1 * VEHICLE_VELOCITY_SCALE; + else if (jeep->Velocity < -(1 * VEHICLE_VELOCITY_SCALE)) + jeep->Velocity += 1 * VEHICLE_VELOCITY_SCALE; else - jeep->velocity = 0; + jeep->Velocity = 0; - jeepItem->Animation.Velocity = jeep->velocity / 256; - if (jeep->engineRevs > 0xC000) - jeep->engineRevs = (GetRandomControl() & 0x1FF) + 48896; + jeepItem->Animation.Velocity = jeep->Velocity / VEHICLE_VELOCITY_SCALE; + if (jeep->EngineRevs > 0xC000) + jeep->EngineRevs = (GetRandomControl() & 0x1FF) + 48896; - int revs = jeep->velocity; - if (jeep->velocity < 0) + int revs = jeep->Velocity; + if (jeep->Velocity < 0) revs /= 2; - jeep->engineRevs += ((abs(revs) - jeep->engineRevs) / 8); + jeep->EngineRevs += (abs(revs) - jeep->EngineRevs) / 8; } - if (TrInput & JEEP_IN_BRAKE) + if (TrInput & VEHICLE_IN_BRAKE) { - Vector3Int pos; - pos.x = 0; - pos.y = -144; - pos.z = -1024; + auto pos = Vector3Int(0, -144, -1024); GetJointAbsPosition(jeepItem, &pos, 11); + TriggerDynamicLight(pos.x, pos.y, pos.z, 10, 64, 0, 0); - jeepItem->MeshBits = 163839; + jeepItem->SetBits(JointBitType::Mesh, { 17 }); + jeepItem->ClearBits(JointBitType::Mesh, JeepBrakeLightJoints); } else - jeepItem->MeshBits = 114687; + { + jeepItem->SetBits(JointBitType::Mesh, JeepBrakeLightJoints); + jeepItem->ClearBits(JointBitType::Mesh, { 17 }); + } - *pitch = jeep->engineRevs; + *pitch = jeep->EngineRevs; return 1; } @@ -924,12 +879,12 @@ namespace TEN::Entities::Vehicles auto* jeep = GetJeepInfo(jeepItem); bool dismount; - if (jeepItem->Pose.Position.y != jeepItem->Floor && + if (!dead && + jeepItem->Pose.Position.y != jeepItem->Floor && laraItem->Animation.ActiveState != JS_JUMP && - laraItem->Animation.ActiveState != JS_LAND && - !dead) + laraItem->Animation.ActiveState != JS_LAND) { - if (jeep->unknown2 == 1) + if (jeep->Gear == 1) laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_BACK_JUMP_START; else laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_FWD_JUMP_START; @@ -938,36 +893,35 @@ namespace TEN::Entities::Vehicles laraItem->Animation.TargetState = JS_JUMP; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; } - else if ( (collide) && - (laraItem->Animation.ActiveState != 4) && - (laraItem->Animation.ActiveState != 5) && - (laraItem->Animation.ActiveState != 2) && - (laraItem->Animation.ActiveState != 3) && - (laraItem->Animation.ActiveState != JS_JUMP) && - (0x2AAA < (short)jeep->velocity) && - (!dead) ) + else if (collide && !dead && + laraItem->Animation.ActiveState != 4 && + laraItem->Animation.ActiveState != 5 && + laraItem->Animation.ActiveState != 2 && + laraItem->Animation.ActiveState != 3 && + laraItem->Animation.ActiveState != JS_JUMP && + jeep->Velocity > JEEP_CRASH_VELOCITY) { short state; switch (collide) { case 13: - state = 4; - laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + 11; + state = JS_CRASH_LEFT; + laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_CRASH_LEFT; break; case 14: - state = 5; - laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + 10; + state = JS_CRASH_FORWARD; + laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_CRASH_FORWARD; break; case 11: - state = 2; - laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + 12; + state = JS_CRASH_RIGHT; + laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_CRASH_RIGHT; break; case 12: - state = 3; - laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + 13; + state = JS_CRASH_BACK; + laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_CRASH_BACK; break; } @@ -979,76 +933,77 @@ namespace TEN::Entities::Vehicles { switch (laraItem->Animation.ActiveState) { - case JS_STOP: + case JS_IDLE: if (dead) laraItem->Animation.TargetState = JS_DEATH; else { - dismount = ((TrInput & JEEP_IN_BRAKE) && (TrInput & IN_LEFT)) ? true : false; - if (dismount && - !jeep->velocity && - !JeepNoGetOff) + dismount = ((TrInput & VEHICLE_IN_BRAKE) && (TrInput & VEHICLE_IN_LEFT)) ? true : false; + if (dismount && !jeep->Velocity && !JeepNoGetOff) { if (JeepCanGetOff(jeepItem, laraItem)) - laraItem->Animation.TargetState = JS_GETOFF; + laraItem->Animation.TargetState = JS_DISMOUNT; + break; } - if (DbInput & IN_WALK) + if (DbInput & JEEP_IN_TOGGLE_FORWARD) { - if (jeep->unknown2) - jeep->unknown2--; + if (jeep->Gear) + jeep->Gear--; + break; } - else if (DbInput & IN_SPRINT) + else if (DbInput & JEEP_IN_TOGGLE_REVERSE) { - if (jeep->unknown2 < 1) + if (jeep->Gear < 1) { - jeep->unknown2++; - if (jeep->unknown2 == 1) + jeep->Gear++; + if (jeep->Gear == 1) laraItem->Animation.TargetState = JS_DRIVE_BACK; + break; } } else { - if ((TrInput & JEEP_IN_ACCELERATE) && !(TrInput & JEEP_IN_BRAKE)) + if ((TrInput & VEHICLE_IN_ACCELERATE) && !(TrInput & VEHICLE_IN_BRAKE)) { laraItem->Animation.TargetState = JS_DRIVE_FORWARD; break; } - else if (TrInput & (IN_LEFT | IN_LSTEP)) + else if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = JS_FWD_LEFT; - else if (TrInput & (IN_RIGHT | IN_RSTEP)) + else if (TrInput & VEHICLE_IN_RIGHT) laraItem->Animation.TargetState = JS_FWD_RIGHT; } - /* if (!(DbInput & IN_WALK)) + /* if (!(DbInput & JEEP_IN_TOGGLE_FORWARD)) { - if (!(DbInput & IN_SPRINT)) + if (!(DbInput & JEEP_IN_TOGGLE_REVERSE)) { - if ((TrInput & JEEP_IN_ACCELERATE) && !(TrInput & JEEP_IN_BRAKE)) + if ((TrInput & VEHICLE_IN_ACCELERATE) && !(TrInput & VEHICLE_IN_BRAKE)) { laraItem->TargetState = JS_DRIVE_FORWARD; break; } - else if (TrInput & (IN_LEFT | IN_LSTEP)) + else if (TrInput & VEHICLE_IN_LEFT) laraItem->TargetState = JS_FWD_LEFT; - else if (TrInput & (IN_RIGHT | IN_RSTEP)) + else if (TrInput & VEHICLE_IN_RIGHT) laraItem->TargetState = JS_FWD_RIGHT; } - else if (jeep->unknown2 < 1) + else if (jeep->Gear < 1) { - jeep->unknown2++; - if (jeep->unknown2 == 1) + jeep->Gear++; + if (jeep->Gear == 1) laraItem->TargetState = JS_DRIVE_BACK; } } else { - if (jeep->unknown2) - jeep->unknown2--; + if (jeep->Gear) + jeep->Gear--; }*/ } @@ -1056,29 +1011,29 @@ namespace TEN::Entities::Vehicles case JS_DRIVE_FORWARD: if (dead) - laraItem->Animation.TargetState = JS_STOP; + laraItem->Animation.TargetState = JS_IDLE; else { - if (jeep->velocity & 0xFFFFFF00 || - TrInput & (JEEP_IN_ACCELERATE | JEEP_IN_BRAKE)) + if (jeep->Velocity & 0xFFFFFF00 || + TrInput & (VEHICLE_IN_ACCELERATE | VEHICLE_IN_BRAKE)) { - if (TrInput & JEEP_IN_BRAKE) + if (TrInput & VEHICLE_IN_BRAKE) { - if (jeep->velocity <= 21844) - laraItem->Animation.TargetState = JS_STOP; + if (jeep->Velocity <= 21844) + laraItem->Animation.TargetState = JS_IDLE; else laraItem->Animation.TargetState = JS_BRAKE; } else { - if (TrInput & (IN_LEFT | IN_LSTEP)) + if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = JS_FWD_LEFT; - else if (TrInput & (IN_RIGHT | IN_RSTEP)) + else if (TrInput & VEHICLE_IN_RIGHT) laraItem->Animation.TargetState = JS_FWD_RIGHT; } } else - laraItem->Animation.TargetState = JS_STOP; + laraItem->Animation.TargetState = JS_IDLE; } break; @@ -1088,36 +1043,38 @@ namespace TEN::Entities::Vehicles case 4: case 5: if (dead) - laraItem->Animation.TargetState = JS_STOP; - else if (TrInput & (JEEP_IN_ACCELERATE | JEEP_IN_BRAKE)) + laraItem->Animation.TargetState = JS_IDLE; + else if (TrInput & (VEHICLE_IN_ACCELERATE | VEHICLE_IN_BRAKE)) laraItem->Animation.TargetState = JS_DRIVE_FORWARD; + break; case JS_BRAKE: if (dead) - laraItem->Animation.TargetState = JS_STOP; - else if (jeep->velocity & 0xFFFFFF00) + laraItem->Animation.TargetState = JS_IDLE; + else if (jeep->Velocity & 0xFFFFFF00) { - if (TrInput & (IN_LEFT | IN_LSTEP)) + if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = JS_FWD_LEFT; - else if (TrInput & (IN_RIGHT | IN_RSTEP)) + else if (TrInput & VEHICLE_IN_RIGHT) laraItem->Animation.TargetState = JS_FWD_RIGHT; } else - laraItem->Animation.TargetState = JS_STOP; + laraItem->Animation.TargetState = JS_IDLE; + break; case JS_FWD_LEFT: if (dead) - laraItem->Animation.TargetState = JS_STOP; - else if (!(DbInput & IN_WALK)) + laraItem->Animation.TargetState = JS_IDLE; + else if (!(DbInput & JEEP_IN_TOGGLE_FORWARD)) { - if (DbInput & IN_SPRINT) + if (DbInput & JEEP_IN_TOGGLE_REVERSE) { - if (jeep->unknown2 < 1) + if (jeep->Gear < 1) { - jeep->unknown2++; - if (jeep->unknown2 == 1) + jeep->Gear++; + if (jeep->Gear == 1) { laraItem->Animation.TargetState = JS_BACK_RIGHT; laraItem->Animation.ActiveState = JS_BACK_RIGHT; @@ -1127,30 +1084,30 @@ namespace TEN::Entities::Vehicles } } } - else if (TrInput & (IN_RIGHT | IN_RSTEP)) + else if (TrInput & VEHICLE_IN_RIGHT) laraItem->Animation.TargetState = JS_DRIVE_FORWARD; - else if (TrInput & (IN_LEFT | IN_LSTEP)) + else if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = JS_FWD_LEFT; - else if (jeep->velocity) + else if (jeep->Velocity) laraItem->Animation.TargetState = JS_DRIVE_FORWARD; else - laraItem->Animation.TargetState = JS_STOP; + laraItem->Animation.TargetState = JS_IDLE; } else { - if (jeep->unknown2) - jeep->unknown2--; + if (jeep->Gear) + jeep->Gear--; } if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_FWD_LEFT && - !jeep->velocity) + !jeep->Velocity) { laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_IDLE_RIGHT_START; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase + JA_IDLE; } if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_IDLE_RIGHT_START) { - if (jeep->velocity) + if (jeep->Velocity) { laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_FWD_LEFT; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; @@ -1161,15 +1118,16 @@ namespace TEN::Entities::Vehicles case JS_FWD_RIGHT: if (dead) - laraItem->Animation.TargetState = JS_STOP; - if (!(DbInput & IN_WALK)) + laraItem->Animation.TargetState = JS_IDLE; + + if (!(DbInput & JEEP_IN_TOGGLE_FORWARD)) { - if (DbInput & IN_SPRINT) + if (DbInput & JEEP_IN_TOGGLE_REVERSE) { - if (jeep->unknown2 < 1) + if (jeep->Gear < 1) { - jeep->unknown2++; - if (jeep->unknown2 == 1) + jeep->Gear++; + if (jeep->Gear == 1) { laraItem->Animation.TargetState = JS_BACK_LEFT; laraItem->Animation.ActiveState = JS_BACK_LEFT; @@ -1179,29 +1137,29 @@ namespace TEN::Entities::Vehicles } } } - else if (TrInput & (IN_LEFT | IN_LSTEP)) + else if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = JS_DRIVE_FORWARD; - else if (TrInput & (IN_RIGHT | IN_RSTEP)) + else if (TrInput & VEHICLE_IN_RIGHT) laraItem->Animation.TargetState = JS_FWD_RIGHT; - else if (jeep->velocity) + else if (jeep->Velocity) laraItem->Animation.TargetState = JS_DRIVE_FORWARD; else - laraItem->Animation.TargetState = JS_STOP; + laraItem->Animation.TargetState = JS_IDLE; } else { - if (jeep->unknown2) - jeep->unknown2--; + if (jeep->Gear) + jeep->Gear--; } - if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_FWD_RIGHT && !jeep->velocity) + if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_FWD_RIGHT && !jeep->Velocity) { laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_IDLE_LEFT_START; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase + 14;//hmm } if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_IDLE_LEFT_START) { - if (jeep->velocity) + if (jeep->Velocity) { laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_FWD_RIGHT; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; @@ -1214,17 +1172,18 @@ namespace TEN::Entities::Vehicles if (jeepItem->Pose.Position.y == jeepItem->Floor) laraItem->Animation.TargetState = JS_LAND; else if (jeepItem->Animation.VerticalVelocity > 300) - jeep->flags |= JEEP_FLAG_FALLING; + jeep->Flags |= JEEP_FLAG_FALLING; + break; case JS_BACK: if (dead) laraItem->Animation.TargetState = JS_DRIVE_BACK; - else if (abs(jeep->velocity) & 0xFFFFFF00) + else if (abs(jeep->Velocity) & 0xFFFFFF00) { - if (TrInput & (IN_LEFT | IN_LSTEP)) + if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = JS_BACK_RIGHT; - else if (TrInput & (IN_RIGHT | IN_RSTEP)) + else if (TrInput & VEHICLE_IN_RIGHT) laraItem->Animation.TargetState = JS_BACK_LEFT; } else @@ -1235,24 +1194,24 @@ namespace TEN::Entities::Vehicles case JS_BACK_LEFT: if (dead) laraItem->Animation.TargetState = JS_DRIVE_BACK; - else if (!(DbInput & IN_WALK)) + else if (!(DbInput & JEEP_IN_TOGGLE_FORWARD)) { - if (DbInput & IN_SPRINT) + if (DbInput & JEEP_IN_TOGGLE_REVERSE) { - if (jeep->unknown2 < 1) - jeep->unknown2++; + if (jeep->Gear < 1) + jeep->Gear++; } - else if (TrInput & (IN_RIGHT | IN_RSTEP)) + else if (TrInput & VEHICLE_IN_RIGHT) laraItem->Animation.TargetState = JS_BACK_LEFT; else laraItem->Animation.TargetState = JS_BACK; } else { - if (jeep->unknown2) + if (jeep->Gear) { - jeep->unknown2--; - if (!jeep->unknown2) + jeep->Gear--; + if (!jeep->Gear) { laraItem->Animation.TargetState = JS_FWD_RIGHT; laraItem->Animation.ActiveState = JS_FWD_RIGHT; @@ -1263,14 +1222,14 @@ namespace TEN::Entities::Vehicles } } - if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_BACK_LEFT && !jeep->velocity) + if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_BACK_LEFT && !jeep->Velocity) { laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_IDLE_LEFT_BACK_START; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase + 14; } if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_IDLE_LEFT_BACK_START) { - if (jeep->velocity) + if (jeep->Velocity) { laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_BACK_LEFT; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; @@ -1284,26 +1243,27 @@ namespace TEN::Entities::Vehicles { laraItem->Animation.TargetState = JS_DRIVE_BACK; } - else if (!(DbInput & IN_WALK)) + else if (!(DbInput & JEEP_IN_TOGGLE_FORWARD)) { - if (DbInput & IN_SPRINT) + if (DbInput & JEEP_IN_TOGGLE_REVERSE) { - if (jeep->unknown2 < 1) - jeep->unknown2++; + if (jeep->Gear < 1) + jeep->Gear++; } - else if (TrInput & (IN_LEFT | IN_LSTEP)) + else if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = JS_BACK_RIGHT; else laraItem->Animation.TargetState = JS_BACK; - if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_BACK_RIGHT && !jeep->velocity) + if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_BACK_RIGHT && !jeep->Velocity) { laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_IDLE_RIGHT_BACK_START; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase + 14; } + if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_IDLE_RIGHT_BACK_START) { - if (jeep->velocity) + if (jeep->Velocity) { laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_BACK_RIGHT; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; @@ -1311,21 +1271,23 @@ namespace TEN::Entities::Vehicles } break; } - else if (!jeep->unknown2 || (--jeep->unknown2 != 0)) + else if (!jeep->Gear || (--jeep->Gear != 0)) { - if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_BACK_RIGHT && !jeep->velocity) + if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_BACK_RIGHT && !jeep->Velocity) { laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_IDLE_RIGHT_BACK_START; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase + 14; } + if (laraItem->Animation.AnimNumber == Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_IDLE_RIGHT_BACK_START) { - if (jeep->velocity) + if (jeep->Velocity) { laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_BACK_RIGHT; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; } } + break; } @@ -1333,27 +1295,26 @@ namespace TEN::Entities::Vehicles laraItem->Animation.ActiveState = JS_FWD_LEFT; laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_IDLE_FWD_RIGHT; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - break; case JS_DRIVE_BACK: if (dead) - laraItem->Animation.TargetState = JS_STOP; + laraItem->Animation.TargetState = JS_IDLE; else - // if (jeep->velocity || JeepNoGetOff) + // if (jeep->Velocity || JeepNoGetOff) { - if (!(DbInput & IN_WALK)) + if (!(DbInput & JEEP_IN_TOGGLE_FORWARD)) { - if (DbInput & IN_SPRINT) + if (DbInput & JEEP_IN_TOGGLE_REVERSE) { - if (jeep->unknown2 < 1) - jeep->unknown2++; + if (jeep->Gear < 1) + jeep->Gear++; } - else if (!(TrInput & JEEP_IN_ACCELERATE) || TrInput & JEEP_IN_BRAKE) + else if (!(TrInput & VEHICLE_IN_ACCELERATE) || TrInput & VEHICLE_IN_BRAKE) { - if (TrInput & (IN_LEFT | IN_LSTEP)) + if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = JS_BACK_RIGHT; - else if (TrInput & (IN_LEFT | IN_LSTEP)) + else if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = JS_BACK_LEFT; } else @@ -1361,11 +1322,11 @@ namespace TEN::Entities::Vehicles } else { - if (jeep->unknown2) + if (jeep->Gear) { - jeep->unknown2--; - if (!jeep->unknown2) - laraItem->Animation.TargetState = JS_STOP; + jeep->Gear--; + if (!jeep->Gear) + laraItem->Animation.TargetState = JS_IDLE; } } } @@ -1378,84 +1339,6 @@ namespace TEN::Entities::Vehicles } } - void JeepCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) - { - auto* item = &g_Level.Items[itemNumber]; - auto* lara = GetLaraInfo(laraItem); - - if (laraItem->HitPoints <= 0 && lara->Vehicle != NO_ITEM) - return; - - if (GetOnJeep(itemNumber, laraItem)) - { - lara->Vehicle = itemNumber; - - if (lara->Control.Weapon.GunType == LaraWeaponType::Flare) - { - CreateFlare(laraItem, ID_FLARE_ITEM, 0); - UndrawFlareMeshes(laraItem); - lara->Flare.ControlLeft = 0; - lara->Control.Weapon.RequestGunType = LaraWeaponType::None; - lara->Control.Weapon.GunType = LaraWeaponType::None; - } - - lara->Control.HandStatus = HandStatus::Busy; - - /*v4 = *(_WORD*)(Rooms + 148 * (signed short)v3->roomNumber + 72); - // Enable ENEMY_JEEP - if (v4 != -1) - { - while (1) - { - v5 = Items + 5622 * v4; - if (*(_WORD*)(Items + 5622 * v4 + 12) == 34) - { - break; - } - v4 = *(_WORD*)(v5 + 26); - if (v4 == -1) - { - goto LABEL_11; - } - } - EnableEntityAI(v4, 1); - *(_DWORD*)(v5 + 5610) = *(_DWORD*)(v5 + 5610) & 0xFFFFFFFB | 2; - AddActiveItem(v4); - }*/ - - short ang = phd_atan(item->Pose.Position.z - laraItem->Pose.Position.z, item->Pose.Position.x - laraItem->Pose.Position.x); - ang -= item->Pose.Orientation.y; - - if ((ang > -(ANGLE(45))) && (ang < (ANGLE(135)))) - laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_GETIN_LEFT; - else - laraItem->Animation.AnimNumber = Objects[ID_JEEP_LARA_ANIMS].animIndex + JA_GETIN_RIGHT; - - laraItem->Animation.TargetState = JS_GETIN; - laraItem->Animation.ActiveState = JS_GETIN; - laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - - item->HitPoints = 1; - laraItem->Pose.Position = item->Pose.Position; - laraItem->Pose.Orientation.y = item->Pose.Orientation.y; - - ResetLaraFlex(laraItem); - lara->HitDirection = -1; - - AnimateItem(laraItem); - - int anim = laraItem->Animation.AnimNumber; - - JeepInfo* jeep = (JeepInfo*)item->Data; - jeep->revs = 0; - jeep->unknown2 = 0; - - item->Flags |= TRIGGERED; - } - else - ObjectCollision(itemNumber, laraItem, coll); - } - int JeepControl(ItemInfo* laraItem) { auto* lara = GetLaraInfo(laraItem); @@ -1474,9 +1357,9 @@ namespace TEN::Entities::Vehicles int ceiling = GetCeiling(floor, jeepItem->Pose.Position.x, jeepItem->Pose.Position.y, jeepItem->Pose.Position.z); Vector3Int fl, fr, bc; - int hfl = TestJeepHeight(jeepItem, JEEP_FRONT, -JEEP_SIDE, &fl); - int hfr = TestJeepHeight(jeepItem, JEEP_FRONT, JEEP_SIDE, &fr); - int hbc = TestJeepHeight(jeepItem, -(JEEP_FRONT + 50), 0, &bc); + int hfl = GetVehicleHeight(jeepItem, JEEP_FRONT, -JEEP_SIDE, true, &fl); + int hfr = GetVehicleHeight(jeepItem, JEEP_FRONT, JEEP_SIDE, true, &fr); + int hbc = GetVehicleHeight(jeepItem, -(JEEP_FRONT + 50), 0, true, &bc); roomNumber = jeepItem->RoomNumber; floor = GetFloor(jeepItem->Pose.Position.x, jeepItem->Pose.Position.y, jeepItem->Pose.Position.z, &roomNumber); @@ -1492,9 +1375,9 @@ namespace TEN::Entities::Vehicles } int pitch = 0; - if (jeep->flags) + if (jeep->Flags) collide = 0; - else if (laraItem->Animation.ActiveState == JS_GETIN) + else if (laraItem->Animation.ActiveState == JS_MOUNT) { drive = -1; collide = 0; @@ -1502,68 +1385,64 @@ namespace TEN::Entities::Vehicles else drive = JeepUserControl(jeepItem, laraItem, floorHeight, &pitch); - if (jeep->velocity || jeep->revs) + if (jeep->Velocity || jeep->Revs) { - jeep->pitch = pitch; + jeep->Pitch = pitch; if (pitch >= -32768) { if (pitch > 40960) - jeep->pitch = 40960; + jeep->Pitch = 40960; } else - jeep->pitch = -32768; + jeep->Pitch = -32768; - SoundEffect(SFX_TR4_VEHICLE_JEEP_MOVING, &jeepItem->Pose, SoundEnvironment::Land, 0.5f + jeep->pitch / 65535.0f); + SoundEffect(SFX_TR4_VEHICLE_JEEP_MOVING, &jeepItem->Pose, SoundEnvironment::Land, 0.5f + jeep->Pitch / 65535.0f); } else { if (drive != -1) SoundEffect(SFX_TR4_VEHICLE_JEEP_IDLE, &jeepItem->Pose); - jeep->pitch = 0; + jeep->Pitch = 0; } jeepItem->Floor = floorHeight; - short rotAdd = jeep->velocity / 4; - jeep->rot1 -= rotAdd; - jeep->rot2 -= rotAdd; - jeep->rot3 -= rotAdd; - jeep->rot4 -= rotAdd; + short rotAdd = jeep->Velocity / 4; + jeep->FrontRightWheelRotation -= rotAdd; + jeep->FrontLeftWheelRotation -= rotAdd; + jeep->BackRightWheelRotation -= rotAdd; + jeep->BackLeftWheelRotation -= rotAdd; int oldY = jeepItem->Pose.Position.y; jeepItem->Animation.VerticalVelocity = DoJeepDynamics(laraItem, floorHeight, jeepItem->Animation.VerticalVelocity, &jeepItem->Pose.Position.y, 0); - jeep->velocity = DoVehicleWaterMovement(jeepItem, laraItem, jeep->velocity, JEEP_FRONT, &jeep->jeepTurn); + jeep->Velocity = DoVehicleWaterMovement(jeepItem, laraItem, jeep->Velocity, JEEP_FRONT, &jeep->TurnRate); - floorHeight = (fl.y + fr.y) / 2; short xRot; - short zRot; - if (bc.y >= hbc) + floorHeight = (fl.y + fr.y) / 2; + + if (bc.y < hbc) { - if (floorHeight >= (hfl + hfr) / 2) - xRot = phd_atan(1100, hbc - floorHeight); + if (floorHeight < (hfl + hfr) / 2) + { + xRot = phd_atan(137, oldY - jeepItem->Pose.Position.y); + if (jeep->Velocity < 0) + xRot = -xRot; + } else - xRot = phd_atan(JEEP_FRONT, hbc - jeepItem->Pose.Position.y); + xRot = phd_atan(JEEP_FRONT, jeepItem->Pose.Position.y - floorHeight); } else { - if (floorHeight >= (hfl + hfr) / 2) - xRot = phd_atan(JEEP_FRONT, jeepItem->Pose.Position.y - floorHeight); + if (floorHeight < (hfl + hfr) / 2) + xRot = phd_atan(JEEP_FRONT, hbc - jeepItem->Pose.Position.y); else - { - xRot = -phd_atan(137, oldY - jeepItem->Pose.Position.y); - if (jeep->velocity < 0) - xRot = -xRot; - } + xRot = phd_atan(1100, hbc - floorHeight); } + short zRot = phd_atan(350, floorHeight - fl.y); jeepItem->Pose.Orientation.x += (xRot - jeepItem->Pose.Orientation.x) / 4; - jeepItem->Pose.Orientation.z += (phd_atan(256, floorHeight - fl.y) - jeepItem->Pose.Orientation.z) / 4; - if (jeep->velocity == 0) - { - jeepItem->Pose.Orientation.x = 0; - jeepItem->Pose.Orientation.z = 0; - } + jeepItem->Pose.Orientation.z += (zRot - jeepItem->Pose.Orientation.z) / 4; - if (!(jeep->flags & JEEP_FLAG_DEAD)) + if (!(jeep->Flags & JEEP_FLAG_DEAD)) { if (roomNumber != jeepItem->RoomNumber) { @@ -1573,34 +1452,23 @@ namespace TEN::Entities::Vehicles laraItem->Pose = jeepItem->Pose; - int jeepAnim = Objects[ID_JEEP].animIndex; - int laraAnim = laraItem->Animation.AnimNumber; - int extraAnim = Objects[ID_JEEP_LARA_ANIMS].animIndex; - AnimateJeep(jeepItem, laraItem, collide, dead); AnimateItem(laraItem); jeepItem->Animation.AnimNumber = Objects[ID_JEEP].animIndex + laraItem->Animation.AnimNumber - Objects[ID_JEEP_LARA_ANIMS].animIndex; jeepItem->Animation.FrameNumber = g_Level.Anims[jeepItem->Animation.AnimNumber].frameBase + (laraItem->Animation.FrameNumber - g_Level.Anims[laraItem->Animation.AnimNumber].frameBase); - jeepAnim = Objects[ID_JEEP].animIndex; - laraAnim = laraItem->Animation.AnimNumber; - extraAnim = Objects[ID_JEEP_LARA_ANIMS].animIndex; - Camera.targetElevation = -ANGLE(30.0f); Camera.targetDistance = SECTOR(2); - if (jeep->unknown2) - { - if (jeep->unknown2 == 1) - jeep->fallSpeed += ((32578 - jeep->fallSpeed) / 8); - } + if (jeep->Gear == 1) + jeep->CameraElevation += ((32578 - jeep->CameraElevation) / 8); else - jeep->fallSpeed -= (jeep->fallSpeed / 8); + jeep->CameraElevation -= (jeep->CameraElevation / 8); - Camera.targetAngle = jeep->fallSpeed; + Camera.targetAngle = jeep->CameraElevation; - if (jeep->flags & JEEP_FLAG_FALLING && jeepItem->Pose.Position.y == jeepItem->Floor) + if (jeep->Flags & JEEP_FLAG_FALLING && jeepItem->Pose.Position.y == jeepItem->Floor) { laraItem->MeshBits = 0; ExplodeVehicle(laraItem, jeepItem); @@ -1608,8 +1476,8 @@ namespace TEN::Entities::Vehicles } } - if (laraItem->Animation.ActiveState == JS_GETIN || - laraItem->Animation.ActiveState == JS_GETOFF) + if (laraItem->Animation.ActiveState == JS_MOUNT || + laraItem->Animation.ActiveState == JS_DISMOUNT) { JeepSmokeStart = 0; } diff --git a/TombEngine/Objects/TR4/Vehicles/jeep.h b/TombEngine/Objects/TR4/Vehicles/jeep.h index 6cd689dda..2deb93ea0 100644 --- a/TombEngine/Objects/TR4/Vehicles/jeep.h +++ b/TombEngine/Objects/TR4/Vehicles/jeep.h @@ -1,10 +1,15 @@ #pragma once -#include "Game/items.h" -#include "Game/collision/collide_room.h" +#include "Objects/Utils/VehicleHelpers.h" + +struct CollisionInfo; +struct ItemInfo; namespace TEN::Entities::Vehicles { void InitialiseJeep(short itemNumber); - void JeepCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + + void JeepPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + void DoJeepMount(ItemInfo* jeepItem, ItemInfo* laraItem, VehicleMountType mountType); + int JeepControl(ItemInfo* laraItem); } diff --git a/TombEngine/Objects/TR4/Vehicles/jeep_info.h b/TombEngine/Objects/TR4/Vehicles/jeep_info.h index e69fd1ac5..4aaf5ecb2 100644 --- a/TombEngine/Objects/TR4/Vehicles/jeep_info.h +++ b/TombEngine/Objects/TR4/Vehicles/jeep_info.h @@ -4,26 +4,24 @@ namespace TEN::Entities::Vehicles { struct JeepInfo { - short jeepTurn; - short rot1; - short rot2; - short rot3; - short rot4; - short momentumAngle; - short extraRotation; + int Velocity = 0; + int CameraElevation = 0; - int velocity; - int fallSpeed; + short TurnRate = 0; + short MomentumAngle = 0; + short ExtraRotation = 0; + short ExtraRotationDrift = 0; + short FrontRightWheelRotation = 0; + short FrontLeftWheelRotation = 0; + short BackRightWheelRotation = 0; + short BackLeftWheelRotation = 0; - int revs; - short engineRevs; - int pitch; - short trackMesh; + int Revs = 0; + short EngineRevs = 0; + int Pitch = 0; + short TrackMesh = 0; - short flags; - - short unknown0; - short unknown1; - short unknown2; + short Gear = 0; + short Flags = NULL; }; } diff --git a/TombEngine/Objects/TR4/Vehicles/motorbike.cpp b/TombEngine/Objects/TR4/Vehicles/motorbike.cpp index ced0b9de4..40d7f2848 100644 --- a/TombEngine/Objects/TR4/Vehicles/motorbike.cpp +++ b/TombEngine/Objects/TR4/Vehicles/motorbike.cpp @@ -1,6 +1,7 @@ #include "framework.h" #include "Objects/TR4/Vehicles/motorbike_info.h" #include "Objects/TR4/Vehicles/motorbike.h" +#include "Objects/Utils/VehicleHelpers.h" #include "Game/control/control.h" #include "Game/effects/effects.h" #include "Game/Lara/lara.h" @@ -28,25 +29,31 @@ namespace TEN::Entities::Vehicles { const vector MotorbikeJoints = { 0, 1, 2, 4, 5, 6, 7, 8, 9 }; const vector MotorbikeBrakeLightJoints = { 10 }; + const vector MotorbikeMountTypes = + { + VehicleMountType::LevelStart, + VehicleMountType::Right + }; constexpr auto MOTORBIKE_RADIUS = 500; + constexpr auto MOTORBIKE_MOUNT_DISTANCE = CLICK(2); constexpr auto MOTORBIKE_DISMOUNT_DISTANCE = CLICK(1.5f); constexpr auto MOTORBIKE_FRICTION = 384; constexpr auto MOTORBIKE_FRONT = 500; constexpr auto MOTORBIKE_SIDE = 350; constexpr auto MOTORBIKE_SLIP = 100; - constexpr auto MOTORBIKE_ACCEL_1 = 64 * 256; - constexpr auto MOTORBIKE_ACCEL_2 = 112 * 256; - constexpr auto MOTORBIKE_ACCEL_MAX = 192 * 256; - constexpr auto MOTORBIKE_ACCEL = 128 * 256; - constexpr auto MOTORBIKE_BACKING_VEL = 8 * 256; - constexpr auto MOTORBIKE_BIG_SLOWDOWN = 48 * 256; - constexpr auto MOTORBIKE_SLOWDOWN1 = 1088; // 4.25f * 256; // TODO: Float velocities. @Sezz 2022.06.16 - constexpr auto MOTORBIKE_SLOWDOWN2 = 6 * 256; + constexpr auto MOTORBIKE_ACCEL_1 = 64 * VEHICLE_VELOCITY_SCALE; + constexpr auto MOTORBIKE_ACCEL_2 = 112 * VEHICLE_VELOCITY_SCALE; + constexpr auto MOTORBIKE_ACCEL_MAX = 192 * VEHICLE_VELOCITY_SCALE; + constexpr auto MOTORBIKE_ACCEL = 128 * VEHICLE_VELOCITY_SCALE; + constexpr auto MOTORBIKE_BACKING_VEL = 8 * VEHICLE_VELOCITY_SCALE; + constexpr auto MOTORBIKE_BIG_SLOWDOWN = 48 * VEHICLE_VELOCITY_SCALE; + constexpr auto MOTORBIKE_SLOWDOWN1 = (int)(4.25f * VEHICLE_VELOCITY_SCALE); // TODO: Float velocities. @Sezz 2022.06.16 + constexpr auto MOTORBIKE_SLOWDOWN2 = 6 * VEHICLE_VELOCITY_SCALE; constexpr auto MOTORBIKE_PITCH_SLOWDOWN = 0x8000; - constexpr auto MOTORBIKE_BITCH_MAX = 0xA000; + constexpr auto MOTORBIKE_PITCH_MAX = 0xA000; #define MOTORBIKE_FORWARD_TURN_ANGLE ANGLE(1.5f) #define MOTORBIKE_BACK_TURN_ANGLE ANGLE(0.5f) @@ -55,14 +62,6 @@ namespace TEN::Entities::Vehicles #define MOTORBIKE_MOMENTUM_TURN_ANGLE_MAX ANGLE(1.5f) #define MOTORBIKE_MOMENTUM_TURN_ANGLE_MAX2 ANGLE(150.0f) - #define MOTORBIKE_IN_ACCELERATE IN_ACTION - #define MOTORBIKE_IN_REVERSE IN_BACK - #define MOTORBIKE_IN_SPEED IN_SPRINT - #define MOTORBIKE_IN_BRAKE IN_JUMP - #define MOTORBIKE_IN_DISMOUNT (IN_JUMP | IN_ROLL) - #define MOTORBIKE_IN_LEFT (IN_LEFT | IN_LSTEP) - #define MOTORBIKE_IN_RIGHT (IN_RIGHT | IN_RSTEP) - enum MotorbikeState { MOTORBIKE_STATE_NONE, @@ -126,11 +125,12 @@ namespace TEN::Entities::Vehicles enum MotorbikeFlags { MOTORBIKE_FLAG_BOOST = (1 << 0), + MOTORBIKE_FLAG_NITRO = (1 << 1), MOTORBIKE_FLAG_FALLING = (1 << 6), MOTORBIKE_FLAG_DEATH = (1 << 7) }; - static MotorbikeInfo* GetMotorbikeInfo(ItemInfo* motorbikeItem) + MotorbikeInfo* GetMotorbikeInfo(ItemInfo* motorbikeItem) { return (MotorbikeInfo*)motorbikeItem->Data; } @@ -142,39 +142,72 @@ namespace TEN::Entities::Vehicles auto* motorbike = GetMotorbikeInfo(motorbikeItem); motorbikeItem->SetBits(JointBitType::Mesh, MotorbikeJoints); - motorbike->TurnRate = 0; motorbike->MomentumAngle = motorbikeItem->Pose.Orientation.y; - motorbike->ExtraRotation = 0; - motorbike->WallShiftRotation = 0; - motorbike->Velocity = 0; - motorbike->Revs = 0; - motorbike->EngineRevs = 0; - motorbike->Pitch = 0; - motorbike->LightPower = 0; - motorbike->LeftWheelRotation = 0; - motorbike->RightWheelsRotation = 0; - motorbike->ExhaustStart = 0; - motorbike->DisableDismount = false; - motorbike->Flags = NULL; } - static int TestMotorbikeHeight(ItemInfo* motorbikeItem, int dz, int dx, Vector3Int* pos) + void MotorbikePlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) { - float sinX = phd_sin(motorbikeItem->Pose.Orientation.x); - float sinY = phd_sin(motorbikeItem->Pose.Orientation.y); - float cosY = phd_cos(motorbikeItem->Pose.Orientation.y); - float sinZ = phd_sin(motorbikeItem->Pose.Orientation.z); + auto* motorbikeItem = &g_Level.Items[itemNumber]; + auto* motorbike = GetMotorbikeInfo(motorbikeItem); + auto* lara = GetLaraInfo(laraItem); - pos->x = motorbikeItem->Pose.Position.x + (dz * sinY) + (dx * cosY); - pos->y = motorbikeItem->Pose.Position.y - (dz * sinX) + (dx * sinZ); - pos->z = motorbikeItem->Pose.Position.z + (dz * cosY) - (dx * sinY); + if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) + return; - auto probe = GetCollision(pos->x, pos->y, pos->z, motorbikeItem->RoomNumber); + auto mountType = GetVehicleMountType(motorbikeItem, laraItem, coll, MotorbikeMountTypes, MOTORBIKE_MOUNT_DISTANCE); + if (mountType == VehicleMountType::None) + ObjectCollision(itemNumber, laraItem, coll); + else + { + lara->Vehicle = itemNumber; + DoMotorbikeMount(motorbikeItem, laraItem, mountType); + } + } - if (pos->y < probe.Position.Ceiling || probe.Position.Ceiling == NO_HEIGHT) - return NO_HEIGHT; + void DoMotorbikeMount(ItemInfo* motorbikeItem, ItemInfo* laraItem, VehicleMountType mountType) + { + auto* motorbike = GetMotorbikeInfo(motorbikeItem); + auto* lara = GetLaraInfo(laraItem); - return probe.Position.Floor; + switch (mountType) + { + case VehicleMountType::LevelStart: + laraItem->Animation.AnimNumber = Objects[ID_MOTORBIKE_LARA_ANIMS].animIndex + MOTORBIKE_ANIM_IDLE; + laraItem->Animation.ActiveState = MOTORBIKE_STATE_IDLE; + laraItem->Animation.TargetState = MOTORBIKE_STATE_IDLE; + break; + + default: + case VehicleMountType::Right: + // HACK: Hardcoded Nitro item check. + /*if (g_Gui.GetInventoryItemChosen() == ID_PUZZLE_ITEM1) + { + laraItem->Animation.AnimNumber = Objects[ID_MOTORBIKE_LARA_ANIMS].animIndex + MOTORBIKE_ANIM_UNLOCK; + g_Gui.SetInventoryItemChosen(NO_ITEM); + motorbike->Flags |= MOTORBIKE_FLAG_NITRO; + } + else + laraItem->Animation.AnimNumber = Objects[ID_MOTORBIKE_LARA_ANIMS].animIndex + MOTORBIKE_ANIM_MOUNT;*/ + + laraItem->Animation.AnimNumber = Objects[ID_MOTORBIKE_LARA_ANIMS].animIndex + MOTORBIKE_ANIM_MOUNT; + laraItem->Animation.ActiveState = MOTORBIKE_STATE_MOUNT; + laraItem->Animation.TargetState = MOTORBIKE_STATE_MOUNT; + break; + } + laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; + + DoVehicleFlareDiscard(laraItem); + ResetLaraFlex(laraItem); + laraItem->Pose.Position = motorbikeItem->Pose.Position; + laraItem->Pose.Orientation.y = motorbikeItem->Pose.Orientation.y; + lara->Control.HandStatus = HandStatus::Free; + lara->HitDirection = -1; + motorbikeItem->Collidable = true; + motorbikeItem->HitPoints = 1; + motorbike->Revs = 0; + motorbike->LightPower = 0; + + AnimateItem(laraItem); } static int DoMotorbikeShift(ItemInfo* motorbikeItem, Vector3Int* pos, Vector3Int* old) @@ -273,107 +306,6 @@ namespace TEN::Entities::Vehicles return 0; } - static bool TestMotorbikeMount(short itemNumber, ItemInfo* laraItem) - { - auto* motorbikeItem = &g_Level.Items[itemNumber]; - auto* lara = GetLaraInfo(laraItem); - - if (motorbikeItem->Flags & ONESHOT || lara->Control.HandStatus != HandStatus::Free || laraItem->Animation.Airborne) - return false; - - if ((!(TrInput & IN_ACTION) || abs(motorbikeItem->Pose.Position.y - laraItem->Pose.Position.y) >= CLICK(1)) && - g_Gui.GetInventoryItemChosen() != ID_PUZZLE_ITEM1) // HACK: Hardcoded Nitro item check. - { - return false; - } - - int dx = laraItem->Pose.Position.x - motorbikeItem->Pose.Position.x; - int dz = laraItem->Pose.Position.z - motorbikeItem->Pose.Position.z; - int distance = pow(dx, 2) + pow(dz, 2); - if (distance > SECTOR(166)) - return false; - - int floorHeight = GetCollision(motorbikeItem).Position.Floor; - if (floorHeight < -SECTOR(31.25f)) - return false; - - short angle = phd_atan(motorbikeItem->Pose.Position.z - laraItem->Pose.Position.z, motorbikeItem->Pose.Position.x - laraItem->Pose.Position.x) - motorbikeItem->Pose.Orientation.y; - unsigned short deltaAngle = angle - motorbikeItem->Pose.Orientation.y; - - // Left. - if (angle > -ANGLE(45.0f) && angle < ANGLE(135.0f)) - { - if (deltaAngle > -ANGLE(45.0f) && angle < ANGLE(135.0f)) - return false; - } - // Right. - else - { - if (deltaAngle > ANGLE(225.0f) && deltaAngle < ANGLE(315.0f)) - return false; - } - - return true; - } - - void MotorbikeCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) - { - auto* motorbikeItem = &g_Level.Items[itemNumber]; - auto* motorbike = GetMotorbikeInfo(motorbikeItem); - auto* lara = GetLaraInfo(laraItem); - - if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM) - return; - - if (TestMotorbikeMount(itemNumber, laraItem)) - { - lara->Vehicle = itemNumber; - - if (lara->Control.Weapon.GunType == LaraWeaponType::Flare) - { - CreateFlare(laraItem, ID_FLARE_ITEM, false); - UndrawFlareMeshes(laraItem); - lara->Control.Weapon.GunType = LaraWeaponType::None; - lara->Control.Weapon.RequestGunType = LaraWeaponType::None; - lara->Flare.Life = 0; - lara->Flare.ControlLeft = false; - } - - lara->Control.HandStatus = HandStatus::Free; - - short angle = phd_atan(motorbikeItem->Pose.Position.z - laraItem->Pose.Position.z, motorbikeItem->Pose.Position.x - laraItem->Pose.Position.x) - motorbikeItem->Pose.Orientation.y; - if (angle <= -ANGLE(45.0f) || angle >= ANGLE(135.0f)) - { - // HACK: Hardcoded Nitro item check. - if (g_Gui.GetInventoryItemChosen() == ID_PUZZLE_ITEM1) - { - laraItem->Animation.AnimNumber = Objects[ID_MOTORBIKE_LARA_ANIMS].animIndex + MOTORBIKE_ANIM_UNLOCK; - g_Gui.SetInventoryItemChosen(NO_ITEM); - } - else - laraItem->Animation.AnimNumber = Objects[ID_MOTORBIKE_LARA_ANIMS].animIndex + MOTORBIKE_ANIM_MOUNT; - - laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; - laraItem->Animation.ActiveState = MOTORBIKE_STATE_MOUNT; - laraItem->Animation.TargetState = MOTORBIKE_STATE_MOUNT; - } - - ResetLaraFlex(laraItem); - laraItem->Pose.Position = motorbikeItem->Pose.Position; - laraItem->Pose.Orientation.y = motorbikeItem->Pose.Orientation.y; - lara->HitDirection = -1; - - AnimateItem(laraItem); - - motorbikeItem->Collidable = true; - motorbikeItem->HitPoints = 1; - motorbike->Revs = 0; - motorbike->LightPower = 0; - } - else - ObjectCollision(itemNumber, laraItem, coll); - } - static void DrawMotorbikeLight(ItemInfo* motorbikeItem) { auto* motorbike = GetMotorbikeInfo(motorbikeItem); @@ -612,29 +544,14 @@ namespace TEN::Entities::Vehicles motorbike->DisableDismount = false; Vector3Int backLeftOld, mtb_old, backRightOld, mtf_old, rightLeftOld; - int hfl_old = TestMotorbikeHeight(motorbikeItem, MOTORBIKE_FRONT, -MOTORBIKE_SIDE, &rightLeftOld); - int hmf_old = TestMotorbikeHeight(motorbikeItem, MOTORBIKE_FRONT, CLICK(0.5f), &mtf_old); - int hbl_old = TestMotorbikeHeight(motorbikeItem, -MOTORBIKE_FRONT, -MOTORBIKE_SIDE, &backLeftOld); - int hbr_old = TestMotorbikeHeight(motorbikeItem, -MOTORBIKE_FRONT, CLICK(0.5f), &backRightOld); - int hmtb_old = TestMotorbikeHeight(motorbikeItem, -MOTORBIKE_FRONT, 0, &mtb_old); + int hfl_old = GetVehicleHeight(motorbikeItem, MOTORBIKE_FRONT, -MOTORBIKE_SIDE, true, &rightLeftOld); + int hmf_old = GetVehicleHeight(motorbikeItem, MOTORBIKE_FRONT, CLICK(0.5f), true, &mtf_old); + int hbl_old = GetVehicleHeight(motorbikeItem, -MOTORBIKE_FRONT, -MOTORBIKE_SIDE, true, &backLeftOld); + int hbr_old = GetVehicleHeight(motorbikeItem, -MOTORBIKE_FRONT, CLICK(0.5f), true, &backRightOld); + int hmtb_old = GetVehicleHeight(motorbikeItem, -MOTORBIKE_FRONT, 0, true, &mtb_old); auto oldPos = motorbikeItem->Pose.Position; - if (backLeftOld.y > hbl_old) - backLeftOld.y = hbl_old; - - if (backRightOld.y > hbr_old) - backRightOld.y = hbr_old; - - if (rightLeftOld.y > hfl_old) - rightLeftOld.y = hfl_old; - - if (mtf_old.y > hmf_old) - mtf_old.y = hmf_old; - - if (mtb_old.y > hmtb_old) - mtb_old.y = hmtb_old; - if (motorbikeItem->Pose.Position.y <= (motorbikeItem->Floor - 8)) { if (motorbike->TurnRate < -ANGLE(0.5f)) @@ -663,7 +580,7 @@ namespace TEN::Entities::Vehicles rotation = motorbikeItem->Pose.Orientation.y - motorbike->MomentumAngle; momentum = MOTORBIKE_MOMENTUM_TURN_ANGLE_MIN - ((2 * motorbike->Velocity) / SECTOR(1)); - if (!(TrInput & MOTORBIKE_IN_ACCELERATE) && motorbike->Velocity > 0) + if (!(TrInput & VEHICLE_IN_ACCELERATE) && motorbike->Velocity > 0) momentum += momentum / 2; if (rotation < -MOTORBIKE_MOMENTUM_TURN_ANGLE_MAX) @@ -729,7 +646,7 @@ namespace TEN::Entities::Vehicles } } - if (motorbike->Velocity <= MOTORBIKE_ACCEL || motorbike->Flags & MOTORBIKE_FLAG_BOOST) + if (motorbike->Velocity <= MOTORBIKE_ACCEL || (motorbike->Flags & MOTORBIKE_FLAG_BOOST)) { if (motorbike->Velocity <= MOTORBIKE_ACCEL_MAX) { @@ -745,19 +662,19 @@ namespace TEN::Entities::Vehicles moved.x = motorbikeItem->Pose.Position.x; moved.z = motorbikeItem->Pose.Position.z; - if (!(motorbikeItem->Flags & ONESHOT)) + if (!(motorbikeItem->Flags & IFLAG_INVISIBLE)) DoVehicleCollision(motorbikeItem, MOTORBIKE_RADIUS); int rot1 = 0; int rot2 = 0; - int hfl = TestMotorbikeHeight(motorbikeItem, MOTORBIKE_FRONT, -MOTORBIKE_SIDE, &frontLeft); + int hfl = GetVehicleHeight(motorbikeItem, MOTORBIKE_FRONT, -MOTORBIKE_SIDE, false, &frontLeft); if (hfl < rightLeftOld.y - CLICK(1)) { rot1 = abs(4 * DoMotorbikeShift(motorbikeItem, &frontLeft, &rightLeftOld)); } - int hbl = TestMotorbikeHeight(motorbikeItem, -MOTORBIKE_FRONT, -MOTORBIKE_SIDE, &backLeft); + int hbl = GetVehicleHeight(motorbikeItem, -MOTORBIKE_FRONT, -MOTORBIKE_SIDE, false, &backLeft); if (hbl < backLeftOld.y - CLICK(1)) { if (rot1) @@ -766,15 +683,15 @@ namespace TEN::Entities::Vehicles rot1 -= abs(4 * DoMotorbikeShift(motorbikeItem, &backLeft, &backLeftOld)); } - int hmtf = TestMotorbikeHeight(motorbikeItem, MOTORBIKE_FRONT, CLICK(0.5f), &mtf); + int hmtf = GetVehicleHeight(motorbikeItem, MOTORBIKE_FRONT, CLICK(0.5f), false, &mtf); if (hmtf < mtf_old.y - CLICK(1)) rot2 -= abs(4 * DoMotorbikeShift(motorbikeItem, &backLeft, &backLeftOld)); - int hmtb = TestMotorbikeHeight(motorbikeItem, -MOTORBIKE_FRONT, 0, &mtb); + int hmtb = GetVehicleHeight(motorbikeItem, -MOTORBIKE_FRONT, 0, false, &mtb); if (hmtb < mtb_old.y - CLICK(1)) DoMotorbikeShift(motorbikeItem, &mtb, &mtb_old); - int hbr = TestMotorbikeHeight(motorbikeItem, -MOTORBIKE_FRONT, CLICK(0.5f), &backRight); + int hbr = GetVehicleHeight(motorbikeItem, -MOTORBIKE_FRONT, CLICK(0.5f), false, &backRight); if (hbr < backRightOld.y - CLICK(1)) { if (rot2) @@ -857,7 +774,7 @@ namespace TEN::Entities::Vehicles laraItem->Animation.ActiveState == MOTORBIKE_STATE_NONE_6) { if (isDead || !collide || - motorbike->Velocity <= (42 * 256) || + motorbike->Velocity <= (42 * VEHICLE_VELOCITY_SCALE) || laraItem->Animation.ActiveState == MOTORBIKE_STATE_HITBACK || laraItem->Animation.ActiveState == MOTORBIKE_STATE_HITFRONT || laraItem->Animation.ActiveState == MOTORBIKE_STATE_HITLEFT || @@ -871,14 +788,14 @@ namespace TEN::Entities::Vehicles else { bool dismount = false; - if ((TrInput & MOTORBIKE_IN_RIGHT) && (TrInput & MOTORBIKE_IN_DISMOUNT)) + if ((TrInput & VEHICLE_IN_RIGHT) && (TrInput & VEHICLE_IN_DISMOUNT)) dismount = true; if (!dismount || motorbike->Velocity || motorbike->DisableDismount) { - if (TrInput & MOTORBIKE_IN_ACCELERATE && !(TrInput & MOTORBIKE_IN_BRAKE)) + if (TrInput & VEHICLE_IN_ACCELERATE && !(TrInput & VEHICLE_IN_BRAKE)) laraItem->Animation.TargetState = MOTORBIKE_STATE_MOVING_FRONT; - else if (TrInput & MOTORBIKE_IN_REVERSE) + else if (TrInput & VEHICLE_IN_REVERSE) laraItem->Animation.TargetState = MOTORBIKE_STATE_MOVING_BACK; } else if (dismount && TestMotorbikeDismount(motorbikeItem, laraItem)) @@ -897,20 +814,20 @@ namespace TEN::Entities::Vehicles else laraItem->Animation.TargetState = MOTORBIKE_STATE_NONE_5; } - else if ((motorbike->Velocity / 256) != 0) + else if ((motorbike->Velocity / VEHICLE_VELOCITY_SCALE) != 0) { - if (TrInput & MOTORBIKE_IN_LEFT) + if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = MOTORBIKE_STATE_MOVING_LEFT; - else if (TrInput & MOTORBIKE_IN_RIGHT) + else if (TrInput & VEHICLE_IN_RIGHT) laraItem->Animation.TargetState = MOTORBIKE_STATE_MOVING_RIGHT; - else if (TrInput & MOTORBIKE_IN_BRAKE) + else if (TrInput & VEHICLE_IN_BRAKE) { - if (motorbike->Velocity <= 0x5554) // 85.3f * 256 + if (motorbike->Velocity <= 0x5554) // 85.3f * VEHICLE_VELOCITY_SCALE laraItem->Animation.TargetState = MOTORBIKE_STATE_NONE_3; else laraItem->Animation.TargetState = MOTORBIKE_STATE_STOP; } - else if (TrInput & MOTORBIKE_IN_REVERSE && motorbike->Velocity <= MOTORBIKE_BACKING_VEL) + else if (TrInput & VEHICLE_IN_REVERSE && motorbike->Velocity <= MOTORBIKE_BACKING_VEL) laraItem->Animation.TargetState = MOTORBIKE_STATE_MOVING_BACK; else if (motorbike->Velocity == 0) laraItem->Animation.TargetState = MOTORBIKE_STATE_IDLE; @@ -921,9 +838,9 @@ namespace TEN::Entities::Vehicles break; case MOTORBIKE_STATE_MOVING_LEFT: - if ((motorbike->Velocity / 256) != 0) + if ((motorbike->Velocity / VEHICLE_VELOCITY_SCALE) != 0) { - if (TrInput & MOTORBIKE_IN_RIGHT || !(TrInput & MOTORBIKE_IN_LEFT)) + if (TrInput & VEHICLE_IN_RIGHT || !(TrInput & VEHICLE_IN_LEFT)) laraItem->Animation.TargetState = MOTORBIKE_STATE_MOVING_FRONT; } else @@ -934,7 +851,7 @@ namespace TEN::Entities::Vehicles break; case MOTORBIKE_STATE_MOVING_BACK: - if (TrInput & MOTORBIKE_IN_REVERSE) + if (TrInput & VEHICLE_IN_REVERSE) laraItem->Animation.TargetState = MOTORBIKE_STATE_MOVING_BACK_LOOP; else laraItem->Animation.TargetState = MOTORBIKE_STATE_IDLE; @@ -942,9 +859,9 @@ namespace TEN::Entities::Vehicles break; case MOTORBIKE_STATE_MOVING_RIGHT: - if ((motorbike->Velocity / 256) != 0) + if ((motorbike->Velocity / VEHICLE_VELOCITY_SCALE) != 0) { - if (TrInput & MOTORBIKE_IN_LEFT || !(TrInput & MOTORBIKE_IN_RIGHT)) + if (TrInput & VEHICLE_IN_LEFT || !(TrInput & VEHICLE_IN_RIGHT)) laraItem->Animation.TargetState = MOTORBIKE_STATE_MOVING_FRONT; } else @@ -958,12 +875,12 @@ namespace TEN::Entities::Vehicles case MOTORBIKE_STATE_NONE_3: case MOTORBIKE_STATE_STOP: case MOTORBIKE_STATE_ACCELERATE: - if ((motorbike->Velocity / 256) != 0) + if ((motorbike->Velocity / VEHICLE_VELOCITY_SCALE) != 0) { - if (TrInput & MOTORBIKE_IN_LEFT) + if (TrInput & VEHICLE_IN_LEFT) laraItem->Animation.TargetState = MOTORBIKE_STATE_MOVING_LEFT; - if (TrInput & MOTORBIKE_IN_RIGHT) + if (TrInput & VEHICLE_IN_RIGHT) laraItem->Animation.TargetState = MOTORBIKE_STATE_MOVING_RIGHT; } else @@ -994,7 +911,7 @@ namespace TEN::Entities::Vehicles case MOTORBIKE_STATE_HITBACK: case MOTORBIKE_STATE_HITRIGHT: case MOTORBIKE_STATE_HITLEFT: - if (TrInput & (MOTORBIKE_IN_ACCELERATE | MOTORBIKE_IN_BRAKE)) + if (TrInput & (VEHICLE_IN_ACCELERATE | VEHICLE_IN_BRAKE)) laraItem->Animation.TargetState = MOTORBIKE_STATE_MOVING_FRONT; break; @@ -1068,15 +985,20 @@ namespace TEN::Entities::Vehicles else motorbike->Revs = 0; - if ((TrInput & MOTORBIKE_IN_SPEED) && (TrInput & MOTORBIKE_IN_ACCELERATE) && lara->SprintEnergy) + if ((TrInput & VEHICLE_IN_SPEED) && + (TrInput & VEHICLE_IN_ACCELERATE) && + (motorbike->Flags & MOTORBIKE_FLAG_NITRO)) { - motorbike->Flags |= MOTORBIKE_FLAG_BOOST; - - lara->SprintEnergy -= 2; - if (lara->SprintEnergy > MOTORBIKE_ACCEL)//hmm + if (lara->SprintEnergy > 10) { - motorbike->Flags &= ~MOTORBIKE_FLAG_BOOST; - lara->SprintEnergy = 0; + motorbike->Flags |= MOTORBIKE_FLAG_BOOST; + lara->SprintEnergy -= 2; + + if (lara->SprintEnergy <= 0) + { + motorbike->Flags &= ~MOTORBIKE_FLAG_BOOST; + lara->SprintEnergy = 0; + } } } else @@ -1090,22 +1012,22 @@ namespace TEN::Entities::Vehicles // Moving forward. if (motorbike->Velocity > 0) { - if (TrInput & MOTORBIKE_IN_LEFT) + if (TrInput & VEHICLE_IN_LEFT) { if (motorbike->Velocity > MOTORBIKE_ACCEL_1) motorbike->TurnRate -= MOTORBIKE_FORWARD_TURN_ANGLE; else - motorbike->TurnRate -= MOTORBIKE_FORWARD_TURN_ANGLE * ((float)motorbike->Velocity / 16384.0f); + motorbike->TurnRate -= MOTORBIKE_FORWARD_TURN_ANGLE * ((float)motorbike->Velocity / 8192.0f); if (motorbike->TurnRate < -MOTORBIKE_TURN_ANGLE_MAX) motorbike->TurnRate = -MOTORBIKE_TURN_ANGLE_MAX; } - else if (TrInput & MOTORBIKE_IN_RIGHT) + else if (TrInput & VEHICLE_IN_RIGHT) { if (motorbike->Velocity > MOTORBIKE_ACCEL_1) motorbike->TurnRate += MOTORBIKE_FORWARD_TURN_ANGLE; else - motorbike->TurnRate += MOTORBIKE_FORWARD_TURN_ANGLE * ((float)motorbike->Velocity / 16384.0f); + motorbike->TurnRate += MOTORBIKE_FORWARD_TURN_ANGLE * ((float)motorbike->Velocity / 8192.0f); if (motorbike->TurnRate > MOTORBIKE_TURN_ANGLE_MAX) motorbike->TurnRate = MOTORBIKE_TURN_ANGLE_MAX; @@ -1114,13 +1036,13 @@ namespace TEN::Entities::Vehicles // Moving back. else if (motorbike->Velocity < 0) { - if (TrInput & MOTORBIKE_IN_LEFT) + if (TrInput & VEHICLE_IN_LEFT) { motorbike->TurnRate += MOTORBIKE_BACK_TURN_ANGLE; if (motorbike->TurnRate > MOTORBIKE_TURN_ANGLE_MAX) motorbike->TurnRate = MOTORBIKE_TURN_ANGLE_MAX; } - else if (TrInput & MOTORBIKE_IN_RIGHT) + else if (TrInput & VEHICLE_IN_RIGHT) { motorbike->TurnRate -= MOTORBIKE_BACK_TURN_ANGLE; if (motorbike->TurnRate < -MOTORBIKE_TURN_ANGLE_MAX) @@ -1128,7 +1050,7 @@ namespace TEN::Entities::Vehicles } } - if (TrInput & MOTORBIKE_IN_BRAKE) + if (TrInput & VEHICLE_IN_BRAKE) { auto pos = Vector3Int(0, -144, -1024); GetJointAbsPosition(motorbikeItem, &pos, NULL); @@ -1139,22 +1061,22 @@ namespace TEN::Entities::Vehicles else motorbikeItem->ClearBits(JointBitType::Mesh, MotorbikeBrakeLightJoints); - if (TrInput & MOTORBIKE_IN_BRAKE) + if (TrInput & VEHICLE_IN_BRAKE) { if (motorbike->Velocity < 0) { - motorbike->Velocity += 3 * 256; + motorbike->Velocity += 3 * VEHICLE_VELOCITY_SCALE; if (motorbike->Velocity > 0) motorbike->Velocity = 0; } else { - motorbike->Velocity -= 3 * 256; + motorbike->Velocity -= 3 * VEHICLE_VELOCITY_SCALE; if (motorbike->Velocity < 0) motorbike->Velocity = 0; } } - else if (TrInput & MOTORBIKE_IN_ACCELERATE) + else if (TrInput & VEHICLE_IN_ACCELERATE) { if (motorbike->Velocity < MOTORBIKE_ACCEL_MAX) { @@ -1166,7 +1088,7 @@ namespace TEN::Entities::Vehicles motorbike->Velocity += ((MOTORBIKE_ACCEL_MAX - motorbike->Velocity) / 16) + 2; if (motorbike->Flags & MOTORBIKE_FLAG_BOOST) - motorbike->Velocity += 1 * 256; + motorbike->Velocity += 1 * VEHICLE_VELOCITY_SCALE; } else motorbike->Velocity = MOTORBIKE_ACCEL_MAX; @@ -1202,7 +1124,7 @@ namespace TEN::Entities::Vehicles } } - motorbikeItem->Animation.Velocity = motorbike->Velocity / 256; + motorbikeItem->Animation.Velocity = motorbike->Velocity / VEHICLE_VELOCITY_SCALE; if (motorbike->EngineRevs > MOTORBIKE_ACCEL_MAX) motorbike->EngineRevs = (GetRandomControl() & 0x1FF) + 0xBF00; @@ -1235,7 +1157,7 @@ namespace TEN::Entities::Vehicles laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.ActiveState = MOTORBIKE_STATE_IDLE; laraItem->Animation.TargetState = MOTORBIKE_STATE_IDLE; - laraItem->Animation.Airborne = false; + laraItem->Animation.IsAirborne = false; lara->Control.HandStatus = HandStatus::Busy; lara->HitDirection = -1; @@ -1258,9 +1180,9 @@ namespace TEN::Entities::Vehicles auto oldPos = motorbikeItem->Pose.Position; Vector3Int frontLeft, frontRight, frontMiddle; - int heightFrontLeft = TestMotorbikeHeight(motorbikeItem, MOTORBIKE_FRONT, -MOTORBIKE_SIDE, &frontLeft); - int heightFrontRight = TestMotorbikeHeight(motorbikeItem, MOTORBIKE_FRONT, CLICK(0.5f), &frontRight); - int heightFrontMiddle = TestMotorbikeHeight(motorbikeItem, -MOTORBIKE_FRONT, 0, &frontMiddle); + int heightFrontLeft = GetVehicleHeight(motorbikeItem, MOTORBIKE_FRONT, -MOTORBIKE_SIDE, true, &frontLeft); + int heightFrontRight = GetVehicleHeight(motorbikeItem, MOTORBIKE_FRONT, CLICK(0.5f), true, &frontRight); + int heightFrontMiddle = GetVehicleHeight(motorbikeItem, -MOTORBIKE_FRONT, 0, true, &frontMiddle); auto probe = GetCollision(motorbikeItem); @@ -1278,32 +1200,26 @@ namespace TEN::Entities::Vehicles int pitch = 0; - if (motorbike->Flags) - collide = 0; + DrawMotorbikeLight(motorbikeItem); + if (laraItem->Animation.ActiveState < MOTORBIKE_STATE_MOUNT || + laraItem->Animation.ActiveState > MOTORBIKE_STATE_DISMOUNT) + { + drive = MotorbikeUserControl(motorbikeItem, laraItem, probe.Position.Floor, &pitch); + } else { - DrawMotorbikeLight(motorbikeItem); - if (laraItem->Animation.ActiveState < MOTORBIKE_STATE_MOUNT || - laraItem->Animation.ActiveState > MOTORBIKE_STATE_DISMOUNT) - { - drive = MotorbikeUserControl(motorbikeItem, laraItem, probe.Position.Floor, &pitch); - } - else - { - drive = -1; - collide = 0; - } + drive = -1; + collide = 0; } if (motorbike->Velocity > 0 || motorbike->Revs) { motorbike->Pitch = pitch; - if (motorbike->Pitch < -MOTORBIKE_PITCH_SLOWDOWN) - motorbike->Pitch = -MOTORBIKE_PITCH_SLOWDOWN; - else - if (motorbike->Pitch > MOTORBIKE_BITCH_MAX) - motorbike->Pitch = MOTORBIKE_BITCH_MAX; + if (motorbike->Pitch < -MOTORBIKE_PITCH_SLOWDOWN) + motorbike->Pitch = -MOTORBIKE_PITCH_SLOWDOWN; + else if (motorbike->Pitch > MOTORBIKE_PITCH_MAX) + motorbike->Pitch = MOTORBIKE_PITCH_MAX; SoundEffect(SFX_TR4_VEHICLE_MOTORBIKE_MOVING, &motorbikeItem->Pose, SoundEnvironment::Land, 0.7f + motorbike->Pitch / 24756.0f); } @@ -1362,31 +1278,28 @@ namespace TEN::Entities::Vehicles motorbikeItem->Pose.Orientation.x += (xRot - motorbikeItem->Pose.Orientation.x) / 4; motorbikeItem->Pose.Orientation.z += (zRot - motorbikeItem->Pose.Orientation.z) / 4; - if (motorbike->Flags >= 0) + if (probe.RoomNumber != motorbikeItem->RoomNumber) { - if (probe.RoomNumber != motorbikeItem->RoomNumber) + ItemNewRoom(lara->Vehicle, probe.RoomNumber); + ItemNewRoom(lara->ItemNumber, probe.RoomNumber); + } + + laraItem->Pose = motorbikeItem->Pose; + + AnimateMotorbike(motorbikeItem, laraItem, collide, isDead); + AnimateItem(laraItem); + + motorbikeItem->Animation.AnimNumber = laraItem->Animation.AnimNumber + (Objects[ID_MOTORBIKE].animIndex - Objects[ID_MOTORBIKE_LARA_ANIMS].animIndex); + motorbikeItem->Animation.FrameNumber = laraItem->Animation.FrameNumber + (g_Level.Anims[motorbikeItem->Animation.AnimNumber].frameBase - g_Level.Anims[laraItem->Animation.AnimNumber].frameBase); + + Camera.targetElevation = -ANGLE(30.0f); + + if (motorbike->Flags & MOTORBIKE_FLAG_FALLING) + { + if (motorbikeItem->Pose.Position.y == motorbikeItem->Floor) { - ItemNewRoom(lara->Vehicle, probe.RoomNumber); - ItemNewRoom(lara->ItemNumber, probe.RoomNumber); - } - - laraItem->Pose = motorbikeItem->Pose; - - AnimateMotorbike(motorbikeItem, laraItem, collide, isDead); - AnimateItem(laraItem); - - motorbikeItem->Animation.AnimNumber = laraItem->Animation.AnimNumber + (Objects[ID_MOTORBIKE].animIndex - Objects[ID_MOTORBIKE_LARA_ANIMS].animIndex); - motorbikeItem->Animation.FrameNumber = laraItem->Animation.FrameNumber + (g_Level.Anims[motorbikeItem->Animation.AnimNumber].frameBase - g_Level.Anims[laraItem->Animation.AnimNumber].frameBase); - - Camera.targetElevation = -ANGLE(30.0f); - - if (motorbike->Flags & MOTORBIKE_FLAG_FALLING) - { - if (motorbikeItem->Pose.Position.y == motorbikeItem->Floor) - { - ExplodeVehicle(laraItem, motorbikeItem); - return 0; - } + ExplodeVehicle(laraItem, motorbikeItem); + return 0; } } diff --git a/TombEngine/Objects/TR4/Vehicles/motorbike.h b/TombEngine/Objects/TR4/Vehicles/motorbike.h index 4b781b14b..2537ee70f 100644 --- a/TombEngine/Objects/TR4/Vehicles/motorbike.h +++ b/TombEngine/Objects/TR4/Vehicles/motorbike.h @@ -1,10 +1,15 @@ #pragma once -#include "Game/items.h" -#include "Game/collision/collide_room.h" +#include "Objects/Utils/VehicleHelpers.h" + +struct CollisionInfo; +struct ItemInfo; namespace TEN::Entities::Vehicles { void InitialiseMotorbike(short itemNumber); - void MotorbikeCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + + void MotorbikePlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); + void DoMotorbikeMount(ItemInfo* motorbikeItem, ItemInfo* laraItem, VehicleMountType mountType); + bool MotorbikeControl(ItemInfo* laraItem, CollisionInfo* coll); } diff --git a/TombEngine/Objects/TR4/Vehicles/motorbike_info.h b/TombEngine/Objects/TR4/Vehicles/motorbike_info.h index e5260cb4a..c77fcfbaa 100644 --- a/TombEngine/Objects/TR4/Vehicles/motorbike_info.h +++ b/TombEngine/Objects/TR4/Vehicles/motorbike_info.h @@ -4,24 +4,24 @@ namespace TEN::Entities::Vehicles { struct MotorbikeInfo { - short TurnRate; - short MomentumAngle; - short ExtraRotation; - short WallShiftRotation; + int Velocity = 0; - int Velocity; + short TurnRate = 0; + short MomentumAngle = 0; + short ExtraRotation = 0; + short WallShiftRotation = 0; - int Revs; - int EngineRevs; - int Pitch; + int Revs = 0; + int EngineRevs = 0; + int Pitch = 0; - short LightPower; - short LeftWheelRotation; - short RightWheelsRotation; + short LightPower = 0; + short LeftWheelRotation = 0; + short RightWheelsRotation = 0; - char ExhaustStart; - bool DisableDismount; + char ExhaustStart = 0; + bool DisableDismount = false; - short Flags; + short Flags = NULL; }; } diff --git a/TombEngine/Objects/TR4/tr4_objects.cpp b/TombEngine/Objects/TR4/tr4_objects.cpp index 318622ba7..6c5829d49 100644 --- a/TombEngine/Objects/TR4/tr4_objects.cpp +++ b/TombEngine/Objects/TR4/tr4_objects.cpp @@ -1189,7 +1189,7 @@ static void StartVehicles(ObjectInfo* obj) if (obj->loaded) { obj->initialise = InitialiseJeep; - obj->collision = JeepCollision; + obj->collision = JeepPlayerCollision; obj->hitEffect = HIT_RICOCHET; obj->savePosition = true; obj->saveAnim = true; @@ -1202,7 +1202,7 @@ static void StartVehicles(ObjectInfo* obj) if (obj->loaded) { obj->initialise = InitialiseMotorbike; - obj->collision = MotorbikeCollision; + obj->collision = MotorbikePlayerCollision; //obj->drawRoutine = DrawMotorbike; // for wheel rotation obj->hitEffect = HIT_RICOCHET; obj->savePosition = true; diff --git a/TombEngine/Objects/TR5/Entity/tr5_larson.cpp b/TombEngine/Objects/TR5/Entity/tr5_larson.cpp index b957cb2f6..f85a439fd 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_larson.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_larson.cpp @@ -78,7 +78,7 @@ void LarsonControl(short itemNumber) CreatureInfo* creature = (CreatureInfo*)item->Data; // In Streets of Rome when Larson HP are below 40 he runs way - /*if (item->HitPoints <= TR5_LARSON_MIN_HP && !(item->flags & ONESHOT)) + /*if (item->HitPoints <= TR5_LARSON_MIN_HP && !(item->flags & IFLAG_INVISIBLE)) { item->HitPoints = TR5_LARSON_MIN_HP; creature->flags++; @@ -104,14 +104,14 @@ void LarsonControl(short itemNumber) if (CurrentLevel == 2) { item->ItemFlags[3] = 1; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->HitStatus = false; item->Collidable = false; item->Status = ITEM_DEACTIVATED; } else { - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->HitStatus = false; item->Collidable = false; item->Status = ITEM_ACTIVE; @@ -136,7 +136,7 @@ void LarsonControl(short itemNumber) /*if (creature->flags) { item->HitPoints = 60; - item->Airborne = false; + item->IsAirborne = false; item->hitStatus = false; item->collidable = false; item->status = ITEM_DESACTIVATED; @@ -404,7 +404,7 @@ void LarsonControl(short itemNumber) item->TargetState = STATE_TR5_LARSON_STOP; item->RequiredState = STATE_TR5_LARSON_STOP; creature->reachedGoal = false; - item->Airborne = false; + item->IsAirborne = false; item->hitStatus = false; item->collidable = false; item->status = ITEM_NOT_ACTIVE; diff --git a/TombEngine/Objects/TR5/Object/tr5_pushableblock.cpp b/TombEngine/Objects/TR5/Object/tr5_pushableblock.cpp index 8fb63b247..e0254b38c 100644 --- a/TombEngine/Objects/TR5/Object/tr5_pushableblock.cpp +++ b/TombEngine/Objects/TR5/Object/tr5_pushableblock.cpp @@ -147,7 +147,7 @@ void PushableBlockControl(short itemNumber) } // control block falling - if (item->Animation.Airborne) + if (item->Animation.IsAirborne) { int floorHeight = GetCollision(item->Pose.Position.x, item->Pose.Position.y + 10, item->Pose.Position.z, item->RoomNumber).Position.Floor; @@ -163,7 +163,7 @@ void PushableBlockControl(short itemNumber) } else { - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; int relY = floorHeight - item->Pose.Position.y; item->Pose.Position.y = floorHeight; @@ -259,7 +259,7 @@ void PushableBlockControl(short itemNumber) DoPushPull = 0; LaraItem->Animation.TargetState = LS_IDLE; - item->Animation.Airborne = true; // do fall + item->Animation.IsAirborne = true; // do fall return; } } @@ -391,7 +391,7 @@ void PushableBlockCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* if ((!(TrInput & IN_ACTION) || laraItem->Animation.ActiveState != LS_IDLE || laraItem->Animation.AnimNumber != LA_STAND_IDLE || - laraItem->Animation.Airborne || + laraItem->Animation.IsAirborne || laraInfo->Control.HandStatus != HandStatus::Free || pushableItem->Status == ITEM_INVISIBLE || pushableItem->TriggerFlags < 0) && diff --git a/TombEngine/Objects/TR5/Object/tr5_rollingball.cpp b/TombEngine/Objects/TR5/Object/tr5_rollingball.cpp index b4038c2e1..028f052cb 100644 --- a/TombEngine/Objects/TR5/Object/tr5_rollingball.cpp +++ b/TombEngine/Objects/TR5/Object/tr5_rollingball.cpp @@ -28,7 +28,7 @@ void RollingBallCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* c laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.TargetState = LS_DEATH; laraItem->Animation.ActiveState = LS_DEATH; - laraItem->Animation.Airborne = false; + laraItem->Animation.IsAirborne = false; } else ObjectCollision(itemNumber, laraItem, coll); @@ -289,7 +289,7 @@ void ClassicRollingBallCollision(short itemNum, ItemInfo* lara, CollisionInfo* c return; if (!TestCollision(item, lara)) return; - if (lara->Animation.Airborne) + if (lara->Animation.IsAirborne) { if (coll->Setup.EnableObjectPush) ItemPushItem(item, lara, coll, coll->Setup.EnableSpasm, 1); @@ -362,9 +362,9 @@ void ClassicRollingBallControl(short itemNum) if (item->Pose.Position.y < item->Floor) { - if (!item->Animation.Airborne) + if (!item->Animation.IsAirborne) { - item->Animation.Airborne = 1; + item->Animation.IsAirborne = 1; item->Animation.VerticalVelocity = -10; } } @@ -385,7 +385,7 @@ void ClassicRollingBallControl(short itemNum) if (item->Pose.Position.y >= (int)floor - 256) { - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.VerticalVelocity = 0; item->Pose.Position.y = item->Floor; SoundEffect(SFX_TR4_ROLLING_BALL, &item->Pose); diff --git a/TombEngine/Objects/TR5/Object/tr5_teleporter.cpp b/TombEngine/Objects/TR5/Object/tr5_teleporter.cpp index 2ee5b458d..f922a09f9 100644 --- a/TombEngine/Objects/TR5/Object/tr5_teleporter.cpp +++ b/TombEngine/Objects/TR5/Object/tr5_teleporter.cpp @@ -206,7 +206,7 @@ void ControlTeleporter(short itemNumber) if (LaraItem->RoomNumber != roomNumber) ItemNewRoom(Lara.ItemNumber, roomNumber); - if (item->Flags & ONESHOT) + if (item->Flags & IFLAG_INVISIBLE) { KillItem(itemNumber); } diff --git a/TombEngine/Objects/TR5/Switch/tr5_crowdove_switch.cpp b/TombEngine/Objects/TR5/Switch/tr5_crowdove_switch.cpp index 584f849a2..01f3a9c07 100644 --- a/TombEngine/Objects/TR5/Switch/tr5_crowdove_switch.cpp +++ b/TombEngine/Objects/TR5/Switch/tr5_crowdove_switch.cpp @@ -38,12 +38,12 @@ namespace TEN::Entities::TR5 auto* laraInfo = GetLaraInfo(laraItem); auto* switchItem = &g_Level.Items[itemNumber]; - if (switchItem->Flags & ONESHOT || + if (switchItem->Flags & IFLAG_INVISIBLE || !(switchItem->MeshBits & 4) || (!(TrInput & IN_ACTION) || laraItem->Animation.ActiveState != LS_IDLE || laraItem->Animation.AnimNumber != LA_STAND_IDLE || - laraItem->Animation.Airborne || + laraItem->Animation.IsAirborne || laraInfo->Control.HandStatus != HandStatus::Free) && (!laraInfo->Control.IsMoving || laraInfo->InteractedItem != itemNumber)) { diff --git a/TombEngine/Objects/TR5/Trap/tr5_deathslide.cpp b/TombEngine/Objects/TR5/Trap/tr5_deathslide.cpp index 7c2ad160c..ad2215b7f 100644 --- a/TombEngine/Objects/TR5/Trap/tr5_deathslide.cpp +++ b/TombEngine/Objects/TR5/Trap/tr5_deathslide.cpp @@ -43,7 +43,7 @@ void DeathSlideCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* co if (!(TrInput & IN_ACTION) || laraItem->Animation.ActiveState != LS_IDLE || - laraItem->Animation.Airborne || + laraItem->Animation.IsAirborne || laraInfo->Control.HandStatus != HandStatus::Free) { return; @@ -67,7 +67,7 @@ void DeathSlideCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* co AddActiveItem(itemNumber); zipLineItem->Status = ITEM_ACTIVE; - zipLineItem->Flags |= ONESHOT; + zipLineItem->Flags |= IFLAG_INVISIBLE; } } @@ -77,7 +77,7 @@ void ControlDeathSlide(short itemNumber) if (zipLineItem->Status == ITEM_ACTIVE) { - if (!(zipLineItem->Flags & ONESHOT)) + if (!(zipLineItem->Flags & IFLAG_INVISIBLE)) { auto* old = (GameVector*)zipLineItem->Data; @@ -139,7 +139,7 @@ void ControlDeathSlide(short itemNumber) { LaraItem->Animation.TargetState = LS_JUMP_FORWARD; AnimateLara(LaraItem); - LaraItem->Animation.Airborne = true; + LaraItem->Animation.IsAirborne = true; LaraItem->Animation.Velocity = zipLineItem->Animation.VerticalVelocity; LaraItem->Animation.VerticalVelocity = zipLineItem->Animation.VerticalVelocity / 4; } @@ -148,7 +148,7 @@ void ControlDeathSlide(short itemNumber) SoundEffect(SFX_TR4_VONCROY_KNIFE_SWISH, &zipLineItem->Pose); RemoveActiveItem(itemNumber); zipLineItem->Status = ITEM_NOT_ACTIVE; - zipLineItem->Flags -= ONESHOT; + zipLineItem->Flags -= IFLAG_INVISIBLE; } else { diff --git a/TombEngine/Objects/TR5/Trap/tr5_fallingceiling.cpp b/TombEngine/Objects/TR5/Trap/tr5_fallingceiling.cpp index 049cb9c1f..911d06c04 100644 --- a/TombEngine/Objects/TR5/Trap/tr5_fallingceiling.cpp +++ b/TombEngine/Objects/TR5/Trap/tr5_fallingceiling.cpp @@ -21,7 +21,7 @@ void FallingCeilingControl(short itemNumber) else { item->Animation.TargetState = 1; - item->Animation.Airborne = true;; + item->Animation.IsAirborne = true;; } AnimateItem(item); @@ -42,7 +42,7 @@ void FallingCeilingControl(short itemNumber) if (item->Pose.Position.y >= item->Floor) { item->Pose.Position.y = item->Floor; - item->Animation.Airborne = false; + item->Animation.IsAirborne = false; item->Animation.TargetState = 2; item->Animation.VerticalVelocity = 0; } diff --git a/TombEngine/Objects/Utils/VehicleHelpers.cpp b/TombEngine/Objects/Utils/VehicleHelpers.cpp new file mode 100644 index 000000000..b8d6c0c2e --- /dev/null +++ b/TombEngine/Objects/Utils/VehicleHelpers.cpp @@ -0,0 +1,298 @@ +#include "framework.h" +#include "Objects/Utils/VehicleHelpers.h" + +#include "Game/effects/simple_particle.h" +#include "Game/effects/tomb4fx.h" +#include "Game/collision/collide_item.h" +#include "Game/collision/sphere.h" +#include "Game/Lara/lara_flare.h" +#include "Game/Lara/lara_helpers.h" +#include "Game/Lara/lara_struct.h" +#include "Game/room.h" +#include "Sound/sound.h" +#include "Specific/input.h" +#include "Specific/prng.h" + +using namespace TEN::Input; + +namespace TEN::Entities::Vehicles +{ + VehicleMountType GetVehicleMountType(ItemInfo* vehicleItem, ItemInfo* laraItem, CollisionInfo* coll, vector allowedMountTypes, float maxDistance2D, float maxVerticalDistance) + { + auto* lara = GetLaraInfo(laraItem); + + // Assess vehicle usability. + if (vehicleItem->Flags & IFLAG_INVISIBLE) + return VehicleMountType::None; + + // Assess hand status. + if (lara->Control.HandStatus != HandStatus::Free) + return VehicleMountType::None; + + // Assess vertical distance to vehicle. + if (abs(laraItem->Pose.Position.y - vehicleItem->Pose.Position.y) > maxVerticalDistance) + return VehicleMountType::None; + + // Assess 2D distance to vehicle. + float distance2D = Vector2::Distance( + Vector2(laraItem->Pose.Position.x, laraItem->Pose.Position.z), + Vector2(vehicleItem->Pose.Position.x, vehicleItem->Pose.Position.z)); + if (distance2D > maxDistance2D) + return VehicleMountType::None; + + // Assess object collision. + if (!TestBoundsCollide(vehicleItem, laraItem, coll->Setup.Radius) || !TestCollision(vehicleItem, laraItem)) + return VehicleMountType::None; + + bool hasInputAction = TrInput & IN_ACTION; + + short deltaHeadingAngle = vehicleItem->Pose.Orientation.y - laraItem->Pose.Orientation.y; + short angleBetweenPositions = vehicleItem->Pose.Orientation.y - GetOrientBetweenPoints(laraItem->Pose.Position, vehicleItem->Pose.Position).y; + bool onCorrectSide = abs(deltaHeadingAngle - angleBetweenPositions) < ANGLE(45.0f); + + // Assess mount types allowed for vehicle. + for (auto mountType : allowedMountTypes) + { + switch (mountType) + { + case VehicleMountType::LevelStart: + if (!laraItem->Animation.Velocity && + !laraItem->Animation.VerticalVelocity && + laraItem->Pose.Position == vehicleItem->Pose.Position) + { + break; + } + + continue; + + case VehicleMountType::Front: + if (hasInputAction && + deltaHeadingAngle > ANGLE(135.0f) && deltaHeadingAngle < -ANGLE(135.0f) && + onCorrectSide && + !laraItem->Animation.IsAirborne) + { + break; + } + + continue; + + case VehicleMountType::Back: + if (hasInputAction && + abs(deltaHeadingAngle) < ANGLE(45.0f) && + onCorrectSide && + !laraItem->Animation.IsAirborne) + { + break; + } + + continue; + + case VehicleMountType::Left: + if (hasInputAction && + deltaHeadingAngle > -ANGLE(135.0f) && deltaHeadingAngle < -ANGLE(45.0f) && + onCorrectSide && + !laraItem->Animation.IsAirborne) + { + break; + } + + continue; + + case VehicleMountType::Right: + if (hasInputAction && + deltaHeadingAngle > ANGLE(45.0f) && deltaHeadingAngle < ANGLE(135.0f) && + onCorrectSide && + !laraItem->Animation.IsAirborne) + { + break; + } + + continue; + + case VehicleMountType::Jump: + if (laraItem->Animation.IsAirborne && + laraItem->Animation.VerticalVelocity > 0 && + laraItem->Pose.Position.y < vehicleItem->Pose.Position.y) + { + break; + } + + continue; + + default: + return VehicleMountType::None; + } + + return mountType; + } + + return VehicleMountType::None; + } + + int GetVehicleHeight(ItemInfo* vehicleItem, int forward, int right, bool clamp, Vector3Int* pos) + { + float sinX = phd_sin(vehicleItem->Pose.Orientation.x); + float sinY = phd_sin(vehicleItem->Pose.Orientation.y); + float cosY = phd_cos(vehicleItem->Pose.Orientation.y); + float sinZ = phd_sin(vehicleItem->Pose.Orientation.z); + + pos->x = vehicleItem->Pose.Position.x + (forward * sinY) + (right * cosY); + pos->y = vehicleItem->Pose.Position.y - (forward * sinX) + (right * sinZ); + pos->z = vehicleItem->Pose.Position.z + (forward * cosY) - (right * sinY); + + // Get collision a bit higher to be able to detect bridges. + auto probe = GetCollision(pos->x, pos->y - CLICK(2), pos->z, vehicleItem->RoomNumber); + + if (pos->y < probe.Position.Ceiling || probe.Position.Ceiling == NO_HEIGHT) + return NO_HEIGHT; + + if (pos->y > probe.Position.Floor && clamp) + pos->y = probe.Position.Floor; + + return probe.Position.Floor; + } + + int GetVehicleWaterHeight(ItemInfo* vehicleItem, int forward, int right, bool clamp, Vector3Int* pos) + { + Matrix world = + Matrix::CreateFromYawPitchRoll(TO_RAD(vehicleItem->Pose.Orientation.y), TO_RAD(vehicleItem->Pose.Orientation.x), TO_RAD(vehicleItem->Pose.Orientation.z)) * + Matrix::CreateTranslation(vehicleItem->Pose.Position.x, vehicleItem->Pose.Position.y, vehicleItem->Pose.Position.z); + + Vector3 vec = Vector3(right, 0, forward); + vec = Vector3::Transform(vec, world); + + pos->x = vec.x; + pos->y = vec.y; + pos->z = vec.z; + + auto probe = GetCollision(pos->x, pos->y, pos->z, vehicleItem->RoomNumber); + int probedRoomNumber = probe.RoomNumber; + + int height = GetWaterHeight(pos->x, pos->y, pos->z, probedRoomNumber); + + if (height == NO_HEIGHT) + { + height = probe.Position.Floor; + if (height == NO_HEIGHT) + return height; + } + + height -= 5; + + if (pos->y > height && clamp) + pos->y = height; + + return height; + } + + void DoVehicleCollision(ItemInfo* vehicleItem, int radius) + { + CollisionInfo coll = {}; + coll.Setup.Radius = radius * 0.8f; // HACK: Most vehicles use radius larger than needed. + coll.Setup.UpperCeilingBound = MAX_HEIGHT; // HACK: this needs to be set to prevent GCI result interference. + coll.Setup.OldPosition = vehicleItem->Pose.Position; + coll.Setup.EnableObjectPush = true; + + DoObjectCollision(vehicleItem, &coll); + } + + int DoVehicleWaterMovement(ItemInfo* vehicleItem, ItemInfo* laraItem, int currentVelocity, int radius, short* turnRate) + { + if (TestEnvironment(ENV_FLAG_WATER, vehicleItem) || + TestEnvironment(ENV_FLAG_SWAMP, vehicleItem)) + { + auto waterDepth = (float)GetWaterDepth(vehicleItem); + auto waterHeight = vehicleItem->Pose.Position.y - GetWaterHeight(vehicleItem); + + // HACK: Sometimes quadbike test position may end up under non-portal ceiling block. + // GetWaterDepth returns DEEP_WATER constant in that case, which is too large for our needs. + if (waterDepth == DEEP_WATER) + waterDepth = VEHICLE_WATER_HEIGHT_MAX; + + if (waterDepth <= VEHICLE_WATER_HEIGHT_MAX) + { + bool isWater = TestEnvironment(ENV_FLAG_WATER, vehicleItem); + + if (currentVelocity != 0) + { + auto coeff = isWater ? VEHICLE_WATER_VELOCITY_COEFF : VEHICLE_SWAMP_VELOCITY_COEFF; + currentVelocity -= std::copysign(currentVelocity * ((waterDepth / VEHICLE_WATER_HEIGHT_MAX) / coeff), currentVelocity); + + if (TEN::Math::Random::GenerateInt(0, 32) > 28) + SoundEffect(SFX_TR4_LARA_WADE, &PHD_3DPOS(vehicleItem->Pose.Position), SoundEnvironment::Land, isWater ? 0.8f : 0.7f); + + if (isWater) + TEN::Effects::TriggerSpeedboatFoam(vehicleItem, Vector3(0, -waterDepth / 2.0f, -radius)); + } + + if (*turnRate) + { + auto coeff = isWater ? VEHICLE_WATER_TURN_RATE_COEFF : VEHICLE_SWAMP_TURN_RATE_COEFF; + *turnRate -= *turnRate * ((waterDepth / VEHICLE_WATER_HEIGHT_MAX) / coeff); + } + } + else + { + if (waterDepth > VEHICLE_WATER_HEIGHT_MAX && waterHeight > VEHICLE_WATER_HEIGHT_MAX) + ExplodeVehicle(laraItem, vehicleItem); + else if (TEN::Math::Random::GenerateInt(0, 32) > 25) + Splash(vehicleItem); + } + } + + return currentVelocity; + } + + // TODO: Allow flares on every vehicle and see how that works. Boats already allowed them originally. + void DoVehicleFlareDiscard(ItemInfo* laraItem) + { + auto* lara = GetLaraInfo(laraItem); + + if (lara->Control.Weapon.GunType == LaraWeaponType::Flare) + { + CreateFlare(laraItem, ID_FLARE_ITEM, 0); + UndrawFlareMeshes(laraItem); + lara->Control.Weapon.GunType = LaraWeaponType::None; + lara->Control.Weapon.RequestGunType = LaraWeaponType::None; + lara->Flare.ControlLeft = false; + } + } + + // TODO: This is a copy-pase of the player version, but I may need a different method for vehicles. + short ModulateVehicleTurnRate(short turnRate, short accelRate, short minTurnRate, short maxTurnRate, float axisCoeff) + { + int sign = std::copysign(1, axisCoeff); + short minTurnRateNormalized = minTurnRate * abs(axisCoeff); + short maxTurnRateNormalized = maxTurnRate * abs(axisCoeff); + + short newTurnRate = (turnRate + (accelRate * sign)) * sign; + newTurnRate = std::clamp(newTurnRate, minTurnRateNormalized, maxTurnRateNormalized); + return newTurnRate * sign; + } + + void ModulateVehicleTurnRateX(short* turnRate, short accelRate, short minTurnRate, short maxTurnRate) + { + *turnRate = ModulateVehicleTurnRate(*turnRate, accelRate, minTurnRate, maxTurnRate, -AxisMap[InputAxis::MoveVertical]); + } + + void ModulateVehicleTurnRateY(short* turnRate, short accelRate, short minTurnRate, short maxTurnRate) + { + *turnRate = ModulateVehicleTurnRate(*turnRate, accelRate, minTurnRate, maxTurnRate, AxisMap[InputAxis::MoveHorizontal]); + } + + void ModulateVehicleLean(ItemInfo* vehicleItem, short baseRate, short maxAngle) + { + float axisCoeff = AxisMap[InputAxis::MoveHorizontal]; + int sign = copysign(1, axisCoeff); + short maxAngleNormalized = maxAngle * axisCoeff; + vehicleItem->Pose.Orientation.z += std::min(baseRate, abs(maxAngleNormalized - vehicleItem->Pose.Orientation.z) / 3) * sign; + } + + void ResetVehicleLean(ItemInfo* vehicleItem, float rate) + { + if (abs(vehicleItem->Pose.Orientation.z) > ANGLE(0.1f)) + vehicleItem->Pose.Orientation.z += vehicleItem->Pose.Orientation.z / -rate; + else + vehicleItem->Pose.Orientation.z = 0; + } +} diff --git a/TombEngine/Objects/Utils/VehicleHelpers.h b/TombEngine/Objects/Utils/VehicleHelpers.h new file mode 100644 index 000000000..cf2a555b4 --- /dev/null +++ b/TombEngine/Objects/Utils/VehicleHelpers.h @@ -0,0 +1,53 @@ +#pragma once +#include "Specific/phd_global.h" +#include "Specific/trmath.h" + +struct CollisionInfo; +struct CollisionResult; +struct ItemInfo; + +using std::vector; + +////////// +////////// +// TODO: SAVEGAMES! +////////// +////////// +////////// + +namespace TEN::Entities::Vehicles +{ + constexpr int VEHICLE_VELOCITY_SCALE = 256; // TODO: Deal with this nonsense *immediately* post-beta. @Sezz 2022.06.25 + + constexpr int VEHICLE_SINK_VELOCITY = 15; + constexpr int VEHICLE_WATER_HEIGHT_MAX = CLICK(2.5f); + constexpr float VEHICLE_WATER_VELOCITY_COEFF = 16.0f; + constexpr float VEHICLE_WATER_TURN_RATE_COEFF = 10.0f; + constexpr float VEHICLE_SWAMP_VELOCITY_COEFF = 8.0f; + constexpr float VEHICLE_SWAMP_TURN_RATE_COEFF = 6.0f; + + enum class VehicleMountType + { + None, + LevelStart, + Front, + Back, + Left, + Right, + Jump + }; + + VehicleMountType GetVehicleMountType(ItemInfo* vehicleItem, ItemInfo* laraItem, CollisionInfo* coll, vector allowedMountTypes, float maxDistance2D, float maxVerticalDistance = STEPUP_HEIGHT); + int GetVehicleHeight(ItemInfo* vehicleItem, int forward, int right, bool clamp, Vector3Int* pos); + int GetVehicleWaterHeight(ItemInfo* vehicleItem, int forward, int right, bool clamp, Vector3Int* pos); + + void DoVehicleCollision(ItemInfo* vehicleItem, int radius); + int DoVehicleWaterMovement(ItemInfo* vehicleItem, ItemInfo* laraItem, int currentVelocity, int radius, short* turnRate); + void DoVehicleFlareDiscard(ItemInfo* laraItem); + + short ModulateVehicleTurnRate(short turnRate, short accelRate, short minTurnRate, short maxTurnRate, float axisCoeff); + void ModulateVehicleTurnRateX(short* turnRate, short accelRate, short minTurnRate, short maxTurnRate); + void ModulateVehicleTurnRateY(short* turnRate, short accelRate, short minTurnRate, short maxTurnRate); + void ModulateVehicleLean(ItemInfo* vehicleItem, short baseRate, short maxAngle); + void ResetVehicleLean(ItemInfo* vehicleItem, float rate); +} diff --git a/TombEngine/Renderer/Renderer11DrawMenu.cpp b/TombEngine/Renderer/Renderer11DrawMenu.cpp index f0a5fd00b..a0549a779 100644 --- a/TombEngine/Renderer/Renderer11DrawMenu.cpp +++ b/TombEngine/Renderer/Renderer11DrawMenu.cpp @@ -920,7 +920,7 @@ namespace TEN::Renderer PrintDebugMessage("Lara WaterStatus: %d", Lara.Control.WaterStatus); PrintDebugMessage("Lara IsMoving: %d", Lara.Control.IsMoving); PrintDebugMessage("Lara HandStatus: %d", Lara.Control.HandStatus); - PrintDebugMessage("Lara Airborne: %d", LaraItem->Animation.Airborne); + PrintDebugMessage("Lara IsAirborne: %d", LaraItem->Animation.IsAirborne); PrintDebugMessage("Lara CanClimbLadder: %d", Lara.Control.CanClimbLadder); break; diff --git a/TombEngine/Renderer/Renderer11Helper.cpp b/TombEngine/Renderer/Renderer11Helper.cpp index 801b3d3ae..f81f2f768 100644 --- a/TombEngine/Renderer/Renderer11Helper.cpp +++ b/TombEngine/Renderer/Renderer11Helper.cpp @@ -10,7 +10,7 @@ #include "Game/collision/sphere.h" #include "Flow/ScriptInterfaceFlowHandler.h" #include "Renderer\RenderView\RenderView.h" -#include "Objects/TR3/Vehicles/quad.h" +#include "Objects/TR3/Vehicles/quad_bike.h" #include "Objects/TR3/Vehicles/rubber_boat.h" #include "Objects/TR3/Vehicles/upv.h" #include "Objects/TR3/Vehicles/big_gun.h" @@ -18,7 +18,7 @@ #include "Objects/TR4/Vehicles/motorbike.h" #include #include "Game/itemdata/creature_info.h" -#include "Objects/TR3/Vehicles/quad_info.h" +#include "Objects/TR3/Vehicles/quad_bike_info.h" #include "Objects/TR4/Vehicles/jeep_info.h" #include "Objects/TR4/Vehicles/motorbike_info.h" #include "Objects/TR3/Vehicles/rubber_boat_info.h" @@ -207,92 +207,116 @@ namespace TEN::Renderer currentBone->ExtraRotation = Vector3(0.0f, 0.0f, 0.0f); nativeItem->Data.apply( - [&j, ¤tBone](QuadInfo& quad) - { - if (j == 3 || j == 4) - currentBone->ExtraRotation.x = TO_RAD(quad.RearRot); - else if (j == 6 || j == 7) - currentBone->ExtraRotation.x = TO_RAD(quad.FrontRot); - }, - [&j, ¤tBone](JeepInfo& jeep) - { - switch(j) + [&j, ¤tBone](QuadBikeInfo& quadBike) { - case 9: - currentBone->ExtraRotation.x = TO_RAD(jeep.rot1); - break; - case 10: - currentBone->ExtraRotation.x = TO_RAD(jeep.rot2); - break; - case 12: - currentBone->ExtraRotation.x = TO_RAD(jeep.rot3); - break; - case 13: - currentBone->ExtraRotation.x = TO_RAD(jeep.rot4); - break; - } - }, - [&j, ¤tBone](MotorbikeInfo& bike) - { - switch (j) + if (j == 3 || j == 4) + currentBone->ExtraRotation.x = TO_RAD(quadBike.RearRot); + else if (j == 6 || j == 7) + { + currentBone->ExtraRotation.x = TO_RAD(quadBike.FrontRot); + currentBone->ExtraRotation.y = TO_RAD(quadBike.TurnRate * 2); + } + }, + [&j, ¤tBone](JeepInfo& jeep) { - case 2: - case 4: - currentBone->ExtraRotation.x = TO_RAD(bike.RightWheelsRotation); - break; - case 8: - currentBone->ExtraRotation.x = TO_RAD(bike.LeftWheelRotation); - break; - } - }, - [&j, ¤tBone, &oldRotation](MinecartInfo& cart) - { - switch (j) - { - case 1: - case 2: - case 3: - case 4: - currentBone->ExtraRotation.z = TO_RAD((short)std::clamp(cart.Velocity, 0, (int)ANGLE(25.0f)) + FROM_RAD(oldRotation.z)); - break; - } - }, - [&j, ¤tBone](RubberBoatInfo& boat) - { - if (j == 2) - currentBone->ExtraRotation.z = TO_RAD(boat.PropellerRotation); - }, - [&j, ¤tBone](UPVInfo& upv) - { - if (j == 3) - currentBone->ExtraRotation.z = TO_RAD(upv.FanRot); - }, - [&j, ¤tBone](BigGunInfo& big_gun) - { - if (j == 2) - currentBone->ExtraRotation.z = big_gun.BarrelZRotation; - }, - [&j, ¤tBone, &lastJoint](CreatureInfo& creature) - { - if (currentBone->ExtraRotationFlags & ROT_Y) - { - currentBone->ExtraRotation.y = TO_RAD(creature.JointRotation[lastJoint]); - lastJoint++; - } + switch(j) + { + case 9: + currentBone->ExtraRotation.x = TO_RAD(jeep.FrontRightWheelRotation); + currentBone->ExtraRotation.y = TO_RAD(jeep.TurnRate * 4); + break; - if (currentBone->ExtraRotationFlags & ROT_X) - { - currentBone->ExtraRotation.x = TO_RAD(creature.JointRotation[lastJoint]); - lastJoint++; - } + case 10: + currentBone->ExtraRotation.x = TO_RAD(jeep.FrontLeftWheelRotation); + currentBone->ExtraRotation.y = TO_RAD(jeep.TurnRate * 4); + break; - if (currentBone->ExtraRotationFlags & ROT_Z) - { - currentBone->ExtraRotation.z = TO_RAD(creature.JointRotation[lastJoint]); - lastJoint++; - } - } - ); + case 12: + currentBone->ExtraRotation.x = TO_RAD(jeep.BackRightWheelRotation); + break; + + case 13: + currentBone->ExtraRotation.x = TO_RAD(jeep.BackLeftWheelRotation); + break; + } + }, + [&j, ¤tBone](MotorbikeInfo& bike) + { + switch (j) + { + case 2: + currentBone->ExtraRotation.x = TO_RAD(bike.RightWheelsRotation); + currentBone->ExtraRotation.y = TO_RAD(bike.TurnRate * 8); + break; + + case 4: + currentBone->ExtraRotation.x = TO_RAD(bike.RightWheelsRotation); + break; + + case 8: + currentBone->ExtraRotation.x = TO_RAD(bike.LeftWheelRotation); + break; + } + }, + [&j, ¤tBone, &oldRotation](MinecartInfo& cart) + { + switch (j) + { + case 1: + case 2: + case 3: + case 4: + currentBone->ExtraRotation.z = TO_RAD((short)std::clamp(cart.Velocity, 0, (int)ANGLE(25.0f)) + FROM_RAD(oldRotation.z)); + break; + } + }, + [&j, ¤tBone](RubberBoatInfo& boat) + { + if (j == 2) + currentBone->ExtraRotation.z = TO_RAD(boat.PropellerRotation); + }, + [&j, ¤tBone](UPVInfo& upv) + { + switch (j) + { + case 1: + currentBone->ExtraRotation.x = TO_RAD(upv.LeftRudderRotation); + break; + + case 2: + currentBone->ExtraRotation.x = TO_RAD(upv.RightRudderRotation); + break; + + case 3: + currentBone->ExtraRotation.z = TO_RAD(upv.TurbineRotation); + break; + } + }, + [&j, ¤tBone](BigGunInfo& big_gun) + { + if (j == 2) + currentBone->ExtraRotation.z = big_gun.BarrelRotation; + }, + [&j, ¤tBone, &lastJoint](CreatureInfo& creature) + { + if (currentBone->ExtraRotationFlags & ROT_Y) + { + currentBone->ExtraRotation.y = TO_RAD(creature.JointRotation[lastJoint]); + lastJoint++; + } + + if (currentBone->ExtraRotationFlags & ROT_X) + { + currentBone->ExtraRotation.x = TO_RAD(creature.JointRotation[lastJoint]); + lastJoint++; + } + + if (currentBone->ExtraRotationFlags & ROT_Z) + { + currentBone->ExtraRotation.z = TO_RAD(creature.JointRotation[lastJoint]); + lastJoint++; + } + }); } ANIM_FRAME* framePtr[2]; diff --git a/TombEngine/Renderer/Structures/RendererBone.h b/TombEngine/Renderer/Structures/RendererBone.h index 30c2e634e..3d3f6854d 100644 --- a/TombEngine/Renderer/Structures/RendererBone.h +++ b/TombEngine/Renderer/Structures/RendererBone.h @@ -6,22 +6,22 @@ namespace TEN::Renderer { struct RendererBone { + int Index; + Vector3 GlobalTranslation; Vector3 Translation; Matrix GlobalTransform; Matrix Transform; - Vector3 GlobalTranslation; - std::vector Children; - RendererBone* Parent; - int Index; Vector3 ExtraRotation; + RendererBone* Parent; + std::vector Children; byte ExtraRotationFlags; RendererBone(int index) { Index = index; + Translation = Vector3::Zero; + ExtraRotation = Vector3::Zero; ExtraRotationFlags = 0; - Translation = Vector3(0, 0, 0); - ExtraRotation = Vector3(0, 0, 0); } }; -} \ No newline at end of file +} diff --git a/TombEngine/Specific/input.h b/TombEngine/Specific/input.h index 53ad6d6b9..91ddb0cc9 100644 --- a/TombEngine/Specific/input.h +++ b/TombEngine/Specific/input.h @@ -85,17 +85,30 @@ namespace TEN::Input constexpr int MAX_INPUT_SLOTS = MAX_KEYBOARD_KEYS + MAX_GAMEPAD_KEYS + MAX_GAMEPAD_POV_AXES + MAX_GAMEPAD_AXES * 2; - constexpr int IN_OPTIC_CONTROLS = (IN_FORWARD | IN_BACK | IN_LEFT | IN_RIGHT | IN_ACTION | IN_CROUCH | IN_SPRINT); - constexpr int IN_WAKE = (IN_FORWARD | IN_BACK | IN_LEFT | IN_RIGHT | IN_LSTEP | IN_RSTEP | IN_WALK | IN_JUMP | IN_SPRINT | IN_ROLL | IN_CROUCH | IN_DRAW | IN_FLARE | IN_ACTION); - constexpr int IN_DIRECTION = (IN_FORWARD | IN_BACK | IN_LEFT | IN_RIGHT); + // Temporary input constants for use with vehicles. + constexpr int VEHICLE_IN_UP = IN_FORWARD; + constexpr int VEHICLE_IN_DOWN = IN_BACK; + constexpr int VEHICLE_IN_LEFT = IN_LEFT; + constexpr int VEHICLE_IN_RIGHT = IN_RIGHT; + constexpr int VEHICLE_IN_ACCELERATE = IN_ACTION; + constexpr int VEHICLE_IN_REVERSE = IN_BACK; + constexpr int VEHICLE_IN_SPEED = IN_SPRINT; + constexpr int VEHICLE_IN_SLOW = IN_WALK; + constexpr int VEHICLE_IN_BRAKE = IN_JUMP; + constexpr int VEHICLE_IN_FIRE = IN_DRAW | IN_CROUCH; + constexpr int VEHICLE_IN_DISMOUNT = IN_JUMP | IN_ROLL; + + constexpr int IN_OPTIC_CONTROLS = IN_FORWARD | IN_BACK | IN_LEFT | IN_RIGHT | IN_ACTION | IN_CROUCH | IN_SPRINT; + constexpr int IN_WAKE = IN_FORWARD | IN_BACK | IN_LEFT | IN_RIGHT | IN_LSTEP | IN_RSTEP | IN_WALK | IN_JUMP | IN_SPRINT | IN_ROLL | IN_CROUCH | IN_DRAW | IN_FLARE | IN_ACTION; + constexpr int IN_DIRECTION = IN_FORWARD | IN_BACK | IN_LEFT | IN_RIGHT; extern const char* g_KeyNames[]; - extern int TrInput; - extern int DbInput; - extern int RawInput; + extern int DbInput; // Debounce; is input clicked? + extern int TrInput; // Throttle; is input held? + extern int RawInput; // Throttle for binocular input. - extern std::vector KeyMap; - extern std::vector AxisMap; + extern std::vector KeyMap; + extern std::vector AxisMap; extern short KeyboardLayout[2][KEY_COUNT]; @@ -105,4 +118,4 @@ namespace TEN::Input void DefaultConflict(); void Rumble(float power, float delayInSeconds = 0.3f, RumbleMode mode = RumbleMode::Both); void StopRumble(); -} \ No newline at end of file +} diff --git a/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h b/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h index 759791d2c..2a142dcca 100644 --- a/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h +++ b/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h @@ -407,7 +407,7 @@ struct ItemT : public flatbuffers::NativeTable { bool triggered = false; bool active = false; int32_t status = 0; - bool airborne = false; + bool is_airborne = false; bool hit_stauts = false; bool collidable = false; bool looked_at = false; @@ -452,7 +452,7 @@ struct Item FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_TRIGGERED = 50, VT_ACTIVE = 52, VT_STATUS = 54, - VT_AIRBORNE = 56, + VT_IS_AIRBORNE = 56, VT_HIT_STAUTS = 58, VT_COLLIDABLE = 60, VT_LOOKED_AT = 62, @@ -544,8 +544,8 @@ struct Item FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { int32_t status() const { return GetField(VT_STATUS, 0); } - bool airborne() const { - return GetField(VT_AIRBORNE, 0) != 0; + bool is_airborne() const { + return GetField(VT_IS_AIRBORNE, 0) != 0; } bool hit_stauts() const { return GetField(VT_HIT_STAUTS, 0) != 0; @@ -679,7 +679,7 @@ struct Item FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VerifyField(verifier, VT_TRIGGERED) && VerifyField(verifier, VT_ACTIVE) && VerifyField(verifier, VT_STATUS) && - VerifyField(verifier, VT_AIRBORNE) && + VerifyField(verifier, VT_IS_AIRBORNE) && VerifyField(verifier, VT_HIT_STAUTS) && VerifyField(verifier, VT_COLLIDABLE) && VerifyField(verifier, VT_LOOKED_AT) && @@ -875,8 +875,8 @@ struct ItemBuilder { void add_status(int32_t status) { fbb_.AddElement(Item::VT_STATUS, status, 0); } - void add_airborne(bool airborne) { - fbb_.AddElement(Item::VT_AIRBORNE, static_cast(airborne), 0); + void add_is_airborne(bool is_airborne) { + fbb_.AddElement(Item::VT_IS_AIRBORNE, static_cast(is_airborne), 0); } void add_hit_stauts(bool hit_stauts) { fbb_.AddElement(Item::VT_HIT_STAUTS, static_cast(hit_stauts), 0); @@ -953,7 +953,7 @@ inline flatbuffers::Offset CreateItem( bool triggered = false, bool active = false, int32_t status = 0, - bool airborne = false, + bool is_airborne = false, bool hit_stauts = false, bool collidable = false, bool looked_at = false, @@ -1003,7 +1003,7 @@ inline flatbuffers::Offset CreateItem( builder_.add_looked_at(looked_at); builder_.add_collidable(collidable); builder_.add_hit_stauts(hit_stauts); - builder_.add_airborne(airborne); + builder_.add_is_airborne(is_airborne); builder_.add_active(active); builder_.add_triggered(triggered); return builder_.Finish(); @@ -1042,7 +1042,7 @@ inline flatbuffers::Offset CreateItemDirect( bool triggered = false, bool active = false, int32_t status = 0, - bool airborne = false, + bool is_airborne = false, bool hit_stauts = false, bool collidable = false, bool looked_at = false, @@ -1089,7 +1089,7 @@ inline flatbuffers::Offset CreateItemDirect( triggered, active, status, - airborne, + is_airborne, hit_stauts, collidable, looked_at, @@ -6422,7 +6422,7 @@ inline void Item::UnPackTo(ItemT *_o, const flatbuffers::resolver_function_t *_r { auto _e = triggered(); _o->triggered = _e; } { auto _e = active(); _o->active = _e; } { auto _e = status(); _o->status = _e; } - { auto _e = airborne(); _o->airborne = _e; } + { auto _e = is_airborne(); _o->is_airborne = _e; } { auto _e = hit_stauts(); _o->hit_stauts = _e; } { auto _e = collidable(); _o->collidable = _e; } { auto _e = looked_at(); _o->looked_at = _e; } @@ -6471,7 +6471,7 @@ inline flatbuffers::Offset CreateItem(flatbuffers::FlatBufferBuilder &_fbb auto _triggered = _o->triggered; auto _active = _o->active; auto _status = _o->status; - auto _airborne = _o->airborne; + auto _is_airborne = _o->is_airborne; auto _hit_stauts = _o->hit_stauts; auto _collidable = _o->collidable; auto _looked_at = _o->looked_at; @@ -6512,7 +6512,7 @@ inline flatbuffers::Offset CreateItem(flatbuffers::FlatBufferBuilder &_fbb _triggered, _active, _status, - _airborne, + _is_airborne, _hit_stauts, _collidable, _looked_at, diff --git a/TombEngine/Specific/savegame/schema/ten_savegame.fbs b/TombEngine/Specific/savegame/schema/ten_savegame.fbs index b57c9684b..665059031 100644 --- a/TombEngine/Specific/savegame/schema/ten_savegame.fbs +++ b/TombEngine/Specific/savegame/schema/ten_savegame.fbs @@ -32,7 +32,7 @@ table Item { triggered: bool; active: bool; status: int32; - airborne: bool; + is_airborne: bool; hit_stauts: bool; collidable: bool; looked_at: bool; diff --git a/TombEngine/Specific/setup.h b/TombEngine/Specific/setup.h index 562b948a6..f89486eb5 100644 --- a/TombEngine/Specific/setup.h +++ b/TombEngine/Specific/setup.h @@ -41,7 +41,7 @@ struct ObjectInfo std::function floorBorder; std::function ceilingBorder; std::function drawRoutine; - std::function collision; + std::function collision; ZoneType zoneType; int animIndex; short HitPoints; diff --git a/TombEngine/Specific/trmath.cpp b/TombEngine/Specific/trmath.cpp index be074132c..b28df5903 100644 --- a/TombEngine/Specific/trmath.cpp +++ b/TombEngine/Specific/trmath.cpp @@ -90,6 +90,11 @@ Vector3Shrt GetVectorAngles(int x, int y, int z) ); } +Vector3Shrt GetOrientBetweenPoints(Vector3Int origin, Vector3Int target) +{ + return GetVectorAngles(target.x - origin.x, target.y - origin.y, target.z - origin.z); +} + int phd_Distance(PHD_3DPOS* first, PHD_3DPOS* second) { return (int)round(Vector3::Distance(first->Position.ToVector3(), second->Position.ToVector3())); @@ -272,32 +277,32 @@ Vector3Int* FP_Normalise(Vector3Int* v) return v; } -float Smoothstep(float e0, float e1, float x) +float Smoothstep(float edge0, float edge1, float x) { // Scale, bias and saturate x to 0..1 range - x = std::clamp((x - e0) / (e1 - e0), 0.0f, 1.0f); + x = std::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f); // Evaluate polynomial return x * x * (3 - 2 * x); } -Vector3 TranslateVector(Vector3 vector, short angle, float forward, float vertical, float lateral) +Vector3 TranslateVector(Vector3 vector, short angle, float forward, float up, float right) { - if (forward == 0.0f && vertical == 0.0f && lateral == 0.0f) + if (forward == 0.0f && up == 0.0f && right == 0.0f) return vector; float sinAngle = phd_sin(angle); float cosAngle = phd_cos(angle); - vector.x += (forward * sinAngle) + (lateral * cosAngle); - vector.y += vertical; - vector.z += (forward * cosAngle) + (lateral * -sinAngle); + vector.x += (forward * sinAngle) + (right * cosAngle); + vector.y += up; + vector.z += (forward * cosAngle) - (right * sinAngle); return vector; } -Vector3Int TranslateVector(Vector3Int vector, short angle, float forward, float vertical, float lateral) +Vector3Int TranslateVector(Vector3Int vector, short angle, float forward, float up, float right) { - auto newVector = TranslateVector(vector.ToVector3(), angle, forward, vertical, lateral); + auto newVector = TranslateVector(vector.ToVector3(), angle, forward, up, right); return Vector3Int( (int)round(newVector.x), (int)round(newVector.y), diff --git a/TombEngine/Specific/trmath.h b/TombEngine/Specific/trmath.h index 5383ed4ce..fcb15847e 100644 --- a/TombEngine/Specific/trmath.h +++ b/TombEngine/Specific/trmath.h @@ -43,6 +43,7 @@ const Vector3 getRandomVectorInCone(const Vector3& direction,const float angleDe int mGetAngle(int x1, int y1, int x2, int y2); int phd_atan(int dz, int dx); Vector3Shrt GetVectorAngles(int x, int y, int z); +Vector3Shrt GetOrientBetweenPoints(Vector3Int origin, Vector3Int target); void phd_RotBoundingBoxNoPersp(PHD_3DPOS* pos, BOUNDING_BOX* bounds, BOUNDING_BOX* tbounds); int phd_Distance(PHD_3DPOS* first, PHD_3DPOS* second); @@ -66,10 +67,10 @@ Vector3Int* FP_Normalise(Vector3Int* v); #define MULFP(a,b) (int)((((__int64)a*(__int64)b))>>16) #define DIVFP(a,b) (int)(((a)/(b>>8))<<8) -float Smoothstep(float e0, float e1, float x); +float Smoothstep(float edge0, float edge1, float x); -Vector3 TranslateVector(Vector3 vector, short angle, float forward, float vertical = 0.0f, float lateral = 0.0f); -Vector3Int TranslateVector(Vector3Int vector, short angle, float forward, float vertical = 0.0f, float lateral = 0.0f); +Vector3 TranslateVector(Vector3 vector, short angle, float forward, float up = 0.0f, float right = 0.0f); +Vector3Int TranslateVector(Vector3Int vector, short angle, float forward, float up = 0.0f, float right = 0.0f); Vector3 TranslateVector(Vector3 vector, Vector3Shrt orient, float distance); Vector3Int TranslateVector(Vector3Int vector, Vector3Shrt orient, float distance); Vector3 TranslateVector(Vector3 vector, Vector3 target, float distance); diff --git a/TombEngine/TombEngine.vcxproj b/TombEngine/TombEngine.vcxproj index 4786ba3d2..940139746 100644 --- a/TombEngine/TombEngine.vcxproj +++ b/TombEngine/TombEngine.vcxproj @@ -178,7 +178,7 @@ CALL gen.bat - + @@ -193,6 +193,7 @@ CALL gen.bat + @@ -290,7 +291,7 @@ CALL gen.bat - + @@ -467,13 +468,13 @@ CALL gen.bat - + - + @@ -628,6 +629,7 @@ CALL gen.bat + @@ -787,7 +789,7 @@ CALL gen.bat - + @@ -900,7 +902,7 @@ CALL gen.bat - +