Vehicles cleanup before impacts were broken (#575)

* Enhance big gun rotation; vehicle cleanup

* Determine defaults for vehicle info members; keep camera elevation aligned with line of sight on big gun

* Organise constants

* Fix jeep/bike floor tilts

* Unify vehicle height calcs, fix bridges for vehicles

* Fix water vehicles

* Fix typo

* Introduce unified vehicle mounting mechanism and use it for the skidoo

* Ensure vehicle mounting occurs from correct side; modify skidoo and speedboat control inputs

* Update VehicleHelpers.cpp

* Fix kayak

* Standardise vehicle inputs

* Update vehicle acceleration input

* Make vehicle inputs constants

* Rename Airborne to IsAirborne

* Move generic vehicle functions to VehicleHelpers.cpp

* Also move constants

* Remove inaccurate vehicle mount condition

* Move vehicle constants to header; redo quad bike input replacement (further adjustment required)

* Establish vehicle mount constants for later; convert vehicle macros to constants

* Add condition to level start mount

* Organise and demagic some vehicle constants

* More cleanup and demagicking of vehicle constants

* Demagic and simplify UPV garbage

* Organise UPV angle macros and constants to make sense

* Organise a little more

* Final cleanup of UPV constants

* Add temporary vehicle velocity scale constant for easier removal later; demagic rubber boat velocity and turn rate values

* Cleanup, demagicking

* Cleanup

* Organisation

* Prototype vehicle turn rate modulation with skidoo and UPV

* Tentatively simplify UPV turn rate deceleration

* Fix UPV turning

* Tweak UPV turn rate macros

* Tweak UPV turn rate macros

* Complete vehicle mount function

* Use new vehicle mount function with quad bike

* Use new vehicle mount function with motorbike

* Add oneshot check to GetVehicleMountType()

* Prepare mechanism to allow starting a level already mounted on any vehicle

* Fix typo

* Cleanup and organisation

* Remove use of Lara global

* Use new vehicle mount function with minecart

* Fix spike deaths on vehicles

* Use new vehicle mount function with jeep

* Assess 2D distance for vehicle mounts instead of 3D for better fidelity around steps

* Cleanup

* Use new vehicle mount function with speedboat

* Fix mounts on boats

* Use new vehicle mount function with rubber boat

* Use new vehicle mount function with kayak

* Cleanup

* Use new mount function with UPV

* Fix motorbike nitro and remove useless flag checks

* Fix bike nitro, rotate wheels for vehicles

* Fix ItemPushStatic not to corrupt non-Lara items room number

* Remove unneeded clamps

* Fix typo

* Jeep: comply to tomb4 decompilation order

* Uncomment drift code for jeep, rename unknown2 to Gear

* Fix flag meanings

* Further backport of jeep from tomb4 project

* Use JeepBrakeLightJoints

* Update jeep.cpp

* Fix vehicle static collisions

* Add disabled option to insert nitro into motorbike

* Simplify UPV collision check

* Update comment

* Make vehicle function parameter ordering consistent

* Rotate UPV rudders when turning the vehicle

* Lean UPV when turning

* Simplify condition

* Simplify skidoo turn rate modulation

* Fix UPV turbine rotation

* Fix merge error

* Tentatively invert turning when moving in reverse on skidoo

* Add function to discard flares on vehicles to avoid copy-paste

* Add big reminder comment for savegames

* Fix another "epic" typo by me, convert enum hex values to shifts

* lara_struct.h cleanup

* UPV cleanup

* #include cleanup in vehicle headers

* Also brake quadbike with brake button

Co-authored-by: Sezz <seb.zych@outlook.com>
This commit is contained in:
Lwmte 2022-07-02 21:12:40 +03:00 committed by GitHub
parent deaae5093f
commit aded24fa10
126 changed files with 5292 additions and 5558 deletions

View file

@ -40,7 +40,6 @@
using namespace TEN::Effects::Lara; using namespace TEN::Effects::Lara;
using namespace TEN::Control::Volumes; using namespace TEN::Control::Volumes;
using namespace TEN::Input; using namespace TEN::Input;
using std::function; using std::function;
using TEN::Renderer::g_Renderer; using TEN::Renderer::g_Renderer;
@ -448,7 +447,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
item->Animation.AnimNumber == LA_STAND_IDLE && item->Animation.AnimNumber == LA_STAND_IDLE &&
item->Animation.ActiveState == LS_IDLE && item->Animation.ActiveState == LS_IDLE &&
item->Animation.TargetState == LS_IDLE && item->Animation.TargetState == LS_IDLE &&
!item->Animation.Airborne) !item->Animation.IsAirborne)
{ {
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
} }
@ -492,7 +491,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
if (isWater) if (isWater)
{ {
item->Pose.Position.y += CLICK(0.5f) - 28; item->Pose.Position.y += CLICK(0.5f) - 28;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
lara->Control.WaterStatus = WaterStatus::Underwater; lara->Control.WaterStatus = WaterStatus::Underwater;
lara->Air = LARA_AIR_MAX; lara->Air = LARA_AIR_MAX;
@ -529,15 +528,15 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
{ {
lara->Control.WaterStatus = WaterStatus::Wade; 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)) && if (waterDepth > (SWIM_DEPTH - CLICK(1)) &&
item->Animation.Airborne && !isSwamp) item->Animation.IsAirborne && !isSwamp)
{ {
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
Splash(item); Splash(item);
} }
// Lara is grounded; don't splash again. // Lara is grounded; don't splash again.
else if (!item->Animation.Airborne) else if (!item->Animation.IsAirborne)
item->Animation.TargetState = LS_IDLE; item->Animation.TargetState = LS_IDLE;
else if (isSwamp) else if (isSwamp)
{ {
@ -566,7 +565,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
SetAnimation(item, LA_FALL_START); SetAnimation(item, LA_FALL_START);
ResetLaraLean(item); ResetLaraLean(item);
ResetLaraFlex(item); ResetLaraFlex(item);
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Animation.Velocity = item->Animation.VerticalVelocity / 4; item->Animation.Velocity = item->Animation.VerticalVelocity / 4;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
lara->Control.WaterStatus = WaterStatus::Dry; lara->Control.WaterStatus = WaterStatus::Dry;
@ -606,7 +605,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
if (heightFromWater <= WADE_DEPTH) if (heightFromWater <= WADE_DEPTH)
{ {
SetAnimation(item, LA_FALL_START); SetAnimation(item, LA_FALL_START);
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Animation.Velocity = item->Animation.VerticalVelocity / 4; item->Animation.Velocity = item->Animation.VerticalVelocity / 4;
lara->Control.WaterStatus = WaterStatus::Dry; lara->Control.WaterStatus = WaterStatus::Dry;
} }
@ -634,7 +633,7 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll)
ResetLaraLean(item); ResetLaraLean(item);
ResetLaraFlex(item); ResetLaraFlex(item);
item->Pose.Position.y += 1 - heightFromWater; item->Pose.Position.y += 1 - heightFromWater;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
lara->Control.WaterStatus = WaterStatus::TreadWater; lara->Control.WaterStatus = WaterStatus::TreadWater;

View file

@ -179,7 +179,7 @@ void lara_col_walk_forward(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
coll->Setup.LowerFloorBound = STEPUP_HEIGHT; coll->Setup.LowerFloorBound = STEPUP_HEIGHT;
coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT;
@ -310,7 +310,7 @@ void lara_col_run_forward(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.LowerFloorBound = NO_LOWER_BOUND;
coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT;
@ -688,7 +688,7 @@ void lara_col_idle(ItemInfo* item, CollisionInfo* coll)
bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item); bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item);
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
lara->Control.MoveAngle = (item->Animation.Velocity >= 0) ? item->Pose.Orientation.y : (item->Pose.Orientation.y + ANGLE(180.0f)); 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; 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); lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f);
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
coll->Setup.BlockFloorSlopeDown = true; coll->Setup.BlockFloorSlopeDown = true;
coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.LowerFloorBound = NO_LOWER_BOUND;
coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT;
@ -1383,7 +1383,7 @@ void lara_col_death(ItemInfo* item, CollisionInfo* coll)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
coll->Setup.LowerFloorBound = STEPUP_HEIGHT; coll->Setup.LowerFloorBound = STEPUP_HEIGHT;
coll->Setup.UpperFloorBound = -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); bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item);
lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f); lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f);
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
coll->Setup.LowerFloorBound = (lara->Control.WaterStatus == WaterStatus::Wade) ? NO_LOWER_BOUND : STEPUP_HEIGHT; coll->Setup.LowerFloorBound = (lara->Control.WaterStatus == WaterStatus::Wade) ? NO_LOWER_BOUND : STEPUP_HEIGHT;
coll->Setup.UpperFloorBound = -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); bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item);
lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(90.0f); lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(90.0f);
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
coll->Setup.LowerFloorBound = (lara->Control.WaterStatus == WaterStatus::Wade) ? NO_LOWER_BOUND : CLICK(0.8f); coll->Setup.LowerFloorBound = (lara->Control.WaterStatus == WaterStatus::Wade) ? NO_LOWER_BOUND : CLICK(0.8f);
coll->Setup.UpperFloorBound = -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); bool isSwamp = TestEnvironment(ENV_FLAG_SWAMP, item);
lara->Control.MoveAngle = item->Pose.Orientation.y - ANGLE(90.0f); lara->Control.MoveAngle = item->Pose.Orientation.y - ANGLE(90.0f);
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
coll->Setup.LowerFloorBound = (lara->Control.WaterStatus == WaterStatus::Wade) ? NO_LOWER_BOUND : CLICK(0.8f); coll->Setup.LowerFloorBound = (lara->Control.WaterStatus == WaterStatus::Wade) ? NO_LOWER_BOUND : CLICK(0.8f);
coll->Setup.UpperFloorBound = -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); auto* lara = GetLaraInfo(item);
lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f); lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f);
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.LowerFloorBound = NO_LOWER_BOUND;
coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT;
@ -2072,7 +2072,7 @@ void lara_col_roll_forward(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.LowerFloorBound = NO_LOWER_BOUND;
coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT;

View file

@ -69,7 +69,7 @@ void LaraCheatyBits(ItemInfo* item)
{ {
SetAnimation(item, LA_DOZY); SetAnimation(item, LA_DOZY);
item->Animation.VerticalVelocity = 30; item->Animation.VerticalVelocity = 30;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Pose.Orientation.x = ANGLE(30.0f); item->Pose.Orientation.x = ANGLE(30.0f);
item->HitPoints = LARA_HEALTH_MAX; item->HitPoints = LARA_HEALTH_MAX;

View file

@ -982,7 +982,7 @@ bool LaraCheckForLetGo(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
item->Animation.VerticalVelocity = 0; 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 if (TrInput & IN_ACTION && item->HitPoints > 0 || item->Animation.AnimNumber == LA_ONWATER_TO_LADDER) // Can't let go on this anim
return false; return false;
@ -993,7 +993,7 @@ bool LaraCheckForLetGo(ItemInfo* item, CollisionInfo* coll)
item->Animation.Velocity = 2; item->Animation.Velocity = 2;
item->Animation.VerticalVelocity = 1; item->Animation.VerticalVelocity = 1;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
return true; return true;
} }

View file

@ -158,7 +158,7 @@ bool LaraDeflectEdgeCrawl(ItemInfo* item, CollisionInfo* coll)
ShiftItem(item, coll); ShiftItem(item, coll);
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
return true; return true;
} }
@ -189,7 +189,7 @@ bool LaraDeflectEdgeMonkey(ItemInfo* item, CollisionInfo* coll)
item->Animation.TargetState = LS_MONKEY_IDLE; item->Animation.TargetState = LS_MONKEY_IDLE;
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
return true; return true;
} }
@ -385,7 +385,7 @@ void LaraResetGravityStatus(ItemInfo* item, CollisionInfo* coll)
if (coll->Middle.Floor <= STEPUP_HEIGHT) if (coll->Middle.Floor <= STEPUP_HEIGHT)
{ {
item->Animation.VerticalVelocity = 0; 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) void SetLaraHitCeiling(ItemInfo* item, CollisionInfo* coll)
{ {
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Pose.Position = coll->Setup.OldPosition; item->Pose.Position = coll->Setup.OldPosition;

View file

@ -109,7 +109,7 @@ void lara_col_crouch_idle(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
lara->Control.KeepLow = TestLaraKeepLow(item, coll); lara->Control.KeepLow = TestLaraKeepLow(item, coll);
lara->Control.IsLow = true; lara->Control.IsLow = true;
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
@ -172,7 +172,7 @@ void lara_col_crouch_roll(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
lara->Control.KeepLow = TestLaraKeepLow(item, coll); lara->Control.KeepLow = TestLaraKeepLow(item, coll);
lara->Control.IsLow = true; lara->Control.IsLow = true;
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
@ -185,7 +185,7 @@ void lara_col_crouch_roll(ItemInfo* item, CollisionInfo* coll)
GetCollisionInfo(coll, item); GetCollisionInfo(coll, item);
// TODO: With sufficient speed, Lara can still roll off ledges. This is particularly a problem in the uncommon scenario where // 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)) if (LaraDeflectEdgeCrawl(item, coll))
item->Pose.Position = coll->Setup.OldPosition; item->Pose.Position = coll->Setup.OldPosition;
@ -472,7 +472,7 @@ void lara_col_crawl_idle(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
lara->Control.KeepLow = TestLaraKeepLow(item, coll); lara->Control.KeepLow = TestLaraKeepLow(item, coll);
lara->Control.IsLow = true; lara->Control.IsLow = true;
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
@ -561,7 +561,7 @@ void lara_col_crawl_forward(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
lara->Control.KeepLow = TestLaraKeepLow(item, coll); lara->Control.KeepLow = TestLaraKeepLow(item, coll);
lara->Control.IsLow = true; lara->Control.IsLow = true;
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
@ -649,7 +649,7 @@ void lara_col_crawl_back(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
lara->Control.KeepLow = TestLaraKeepLow(item, coll); lara->Control.KeepLow = TestLaraKeepLow(item, coll);
lara->Control.IsLow = true; lara->Control.IsLow = true;
lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f); 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); GetCollisionInfo(coll, item);
lara->Control.HandStatus = HandStatus::Busy; lara->Control.HandStatus = HandStatus::Busy;
item->Pose.Position.y += coll->Front.Floor - GetBoundsAccurate(item)->Y1 - 20; 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.Velocity = 2;
item->Animation.VerticalVelocity = 1; item->Animation.VerticalVelocity = 1;
} }

View file

@ -378,7 +378,7 @@ void DoFlareInHand(ItemInfo* laraItem, int flareLife)
if (lara->Flare.Life >= FLARE_LIFE_MAX) if (lara->Flare.Life >= FLARE_LIFE_MAX)
{ {
// Prevent Lara from intercepting reach/jump states with flare throws. // 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_PREPARE ||
laraItem->Animation.TargetState == LS_JUMP_FORWARD) laraItem->Animation.TargetState == LS_JUMP_FORWARD)
return; return;

View file

@ -49,7 +49,7 @@ void lara_col_hang(ItemInfo* item, CollisionInfo* coll)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
if (item->Animation.AnimNumber == LA_REACH_TO_HANG || if (item->Animation.AnimNumber == LA_REACH_TO_HANG ||

View file

@ -19,7 +19,7 @@
#include "Objects/TR3/Vehicles/big_gun.h" #include "Objects/TR3/Vehicles/big_gun.h"
#include "Objects/TR3/Vehicles/kayak.h" #include "Objects/TR3/Vehicles/kayak.h"
#include "Objects/TR3/Vehicles/minecart.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/upv.h"
#include "Objects/TR4/Vehicles/jeep.h" #include "Objects/TR4/Vehicles/jeep.h"
#include "Objects/TR4/Vehicles/motorbike.h" #include "Objects/TR4/Vehicles/motorbike.h"
@ -346,6 +346,7 @@ short ModulateLaraTurnRate(short turnRate, short accelRate, short minTurnRate, s
return newTurnRate * sign; 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) void ModulateLaraTurnRateX(ItemInfo* item, short accelRate, short minTurnRate, short maxTurnRate)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
@ -358,7 +359,7 @@ void ModulateLaraTurnRateY(ItemInfo* item, short accelRate, short minTurnRate, s
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
float axisCoeff = AxisMap[InputAxis::MoveHorizontal]; float axisCoeff = AxisMap[InputAxis::MoveHorizontal];
if (item->Animation.Airborne) if (item->Animation.IsAirborne)
{ {
int sign = std::copysign(1, axisCoeff); int sign = std::copysign(1, axisCoeff);
axisCoeff = std::min(1.2f, abs(axisCoeff)) * sign; 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) 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.Velocity = 0;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
@ -637,14 +638,14 @@ void SetLaraLand(ItemInfo* item, CollisionInfo* coll)
void SetLaraFallAnimation(ItemInfo* item) void SetLaraFallAnimation(ItemInfo* item)
{ {
SetAnimation(item, LA_FALL_START); SetAnimation(item, LA_FALL_START);
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
} }
void SetLaraFallBackAnimation(ItemInfo* item) void SetLaraFallBackAnimation(ItemInfo* item)
{ {
SetAnimation(item, LA_FALL_BACK); SetAnimation(item, LA_FALL_BACK);
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
} }
@ -662,7 +663,7 @@ void SetLaraMonkeyRelease(ItemInfo* item)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Animation.Velocity = 2; item->Animation.Velocity = 2;
item->Animation.VerticalVelocity = 1; item->Animation.VerticalVelocity = 1;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
@ -760,7 +761,7 @@ void SetLaraCornerAnimation(ItemInfo* item, CollisionInfo* coll, bool flip)
if (item->HitPoints <= 0) if (item->HitPoints <= 0)
{ {
SetAnimation(item, LA_FALL_START); SetAnimation(item, LA_FALL_START);
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Animation.Velocity = 2; item->Animation.Velocity = 2;
item->Animation.VerticalVelocity = 1; item->Animation.VerticalVelocity = 1;
item->Pose.Position.y += CLICK(1); item->Pose.Position.y += CLICK(1);
@ -865,14 +866,14 @@ void ResetLaraFlex(ItemInfo* item, float rate)
lara->ExtraTorsoRot.z = 0; 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; return;
bool pulse = (GlobalCounter & 0x1F) % 0x1F == 0; bool doPulse = (GlobalCounter & 0x0F) % 0x0F == 1;
if (pulse) if (doPulse)
Rumble(0.2f, 0.1f); Rumble(0.2f, 0.1f);
} }

View file

@ -51,4 +51,4 @@ void SetLaraSwimDiveAnimation(ItemInfo* item);
void ResetLaraLean(ItemInfo* item, float rate = 1.0f, bool resetRoll = true, bool resetPitch = true); void ResetLaraLean(ItemInfo* item, float rate = 1.0f, bool resetRoll = true, bool resetPitch = true);
void ResetLaraFlex(ItemInfo* item, float rate = 1.0f); void ResetLaraFlex(ItemInfo* item, float rate = 1.0f);
void RumbleLaraHealthCondition(ItemInfo* lara); void RumbleLaraHealthCondition(ItemInfo* item);

View file

@ -161,7 +161,7 @@ void lara_col_freefall(ItemInfo* item, CollisionInfo* coll)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.LowerFloorBound = NO_LOWER_BOUND;
coll->Setup.UpperFloorBound = -STEPUP_HEIGHT; coll->Setup.UpperFloorBound = -STEPUP_HEIGHT;
coll->Setup.LowerCeilingBound = BAD_JUMP_CEILING; coll->Setup.LowerCeilingBound = BAD_JUMP_CEILING;
@ -224,7 +224,7 @@ void lara_col_reach(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
if (lara->Control.Rope.Ptr == -1) if (lara->Control.Rope.Ptr == -1)
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;

View file

@ -126,7 +126,7 @@ void lara_col_monkey_idle(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.LowerFloorBound = NO_LOWER_BOUND;
coll->Setup.UpperFloorBound = 0; coll->Setup.UpperFloorBound = 0;
coll->Setup.LowerCeilingBound = CLICK(1.25f); coll->Setup.LowerCeilingBound = CLICK(1.25f);

View file

@ -277,7 +277,7 @@ void lara_as_horizontal_bar_leap(ItemInfo* item, CollisionInfo* coll)
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
auto* barItem = &g_Level.Items[lara->InteractedItem]; 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) 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.Velocity = 100;
item->Animation.VerticalVelocity = 40; item->Animation.VerticalVelocity = 40;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
} }
} }

View file

@ -457,7 +457,7 @@ void lara_as_slopeclimb(ItemInfo* item, CollisionInfo* coll)
void lara_as_slopefall(ItemInfo* item, CollisionInfo* coll) void lara_as_slopefall(ItemInfo* item, CollisionInfo* coll)
{ {
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
if (GlobalCounter % 2) if (GlobalCounter % 2)
item->Pose.Orientation.x--; item->Pose.Orientation.x--;
@ -492,7 +492,7 @@ void lara_col_slopehang(ItemInfo* item, CollisionInfo* coll)
if (!(TrInput & IN_ACTION)) if (!(TrInput & IN_ACTION))
{ {
SetAnimation(item, LA_OVERHANG_HANG_DROP); SetAnimation(item, LA_OVERHANG_HANG_DROP);
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
return; return;
} }

View file

@ -92,7 +92,7 @@ void lara_col_slide_forward(ItemInfo* item, CollisionInfo* coll)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
lara->Control.MoveAngle = item->Pose.Orientation.y; lara->Control.MoveAngle = item->Pose.Orientation.y;
coll->Setup.Height = LARA_HEIGHT_CRAWL; // HACK: Behaves better with clamps. coll->Setup.Height = LARA_HEIGHT_CRAWL; // HACK: Behaves better with clamps.
coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.LowerFloorBound = NO_LOWER_BOUND;
@ -197,7 +197,7 @@ void lara_col_slide_back(ItemInfo* item, CollisionInfo* coll)
{ {
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f); lara->Control.MoveAngle = item->Pose.Orientation.y + ANGLE(180.0f);
coll->Setup.Height = LARA_HEIGHT_CRAWL; // HACK: Behaves better with clamps. coll->Setup.Height = LARA_HEIGHT_CRAWL; // HACK: Behaves better with clamps.
coll->Setup.LowerFloorBound = NO_LOWER_BOUND; coll->Setup.LowerFloorBound = NO_LOWER_BOUND;

View file

@ -844,7 +844,7 @@ enum class WeaponAmmoType
Ammo2, Ammo2,
Ammo3, Ammo3,
NumAmmos NumAmmoTypes
}; };
enum class LaraWeaponType enum class LaraWeaponType
@ -868,28 +868,28 @@ enum class LaraWeaponType
enum LaraWeaponTypeCarried enum LaraWeaponTypeCarried
{ {
WTYPE_MISSING = 0x0, WTYPE_MISSING = 0,
WTYPE_PRESENT = 0x1, WTYPE_PRESENT = (1 << 0),
WTYPE_SILENCER = 0x2, WTYPE_SILENCER = (1 << 1),
WTYPE_LASERSIGHT = 0x4, WTYPE_LASERSIGHT = (1 << 2),
WTYPE_AMMO_1 = 0x8, WTYPE_AMMO_1 = (1 << 3),
WTYPE_AMMO_2 = 0x10, WTYPE_AMMO_2 = (1 << 4),
WTYPE_AMMO_3 = 0x20, WTYPE_AMMO_3 = (1 << 5),
WTYPE_MASK_AMMO = WTYPE_AMMO_1 | WTYPE_AMMO_2 | WTYPE_AMMO_3, WTYPE_MASK_AMMO = WTYPE_AMMO_1 | WTYPE_AMMO_2 | WTYPE_AMMO_3,
}; };
enum class HolsterSlot : int enum class HolsterSlot
{ {
Empty = ID_LARA_HOLSTERS, Empty = ID_LARA_HOLSTERS,
Pistols = ID_LARA_HOLSTERS_PISTOLS, Pistols = ID_LARA_HOLSTERS_PISTOLS,
Uzis = ID_LARA_HOLSTERS_UZIS, Uzis = ID_LARA_HOLSTERS_UZIS,
Revolver = ID_LARA_HOLSTERS_REVOLVER, Revolver = ID_LARA_HOLSTERS_REVOLVER,
Shotgun = ID_SHOTGUN_ANIM, Shotgun = ID_SHOTGUN_ANIM,
HK = ID_HK_ANIM, HK = ID_HK_ANIM,
Harpoon = ID_HARPOON_ANIM, Harpoon = ID_HARPOON_ANIM,
Crowssbow = ID_CROSSBOW_ANIM, Crowssbow = ID_CROSSBOW_ANIM,
GrenadeLauncher = ID_GRENADE_ANIM, GrenadeLauncher = ID_GRENADE_ANIM,
RocketLauncher = ID_ROCKET_ANIM, RocketLauncher = ID_ROCKET_ANIM
}; };
// TODO: Unused. // TODO: Unused.
@ -1064,7 +1064,7 @@ struct HolsterInfo
struct CarriedWeaponInfo struct CarriedWeaponInfo
{ {
bool Present; bool Present;
Ammo Ammo[(int)WeaponAmmoType::NumAmmos]; Ammo Ammo[(int)WeaponAmmoType::NumAmmoTypes];
WeaponAmmoType SelectedAmmo; // WeaponAmmoType_enum WeaponAmmoType SelectedAmmo; // WeaponAmmoType_enum
bool HasLasersight; // TODO: Duplicated in LaraInventoryData. bool HasLasersight; // TODO: Duplicated in LaraInventoryData.
bool HasSilencer; // TODO: Duplicated in LaraInventoryData. bool HasSilencer; // TODO: Duplicated in LaraInventoryData.
@ -1095,6 +1095,7 @@ struct TorchData
bool IsLit; bool IsLit;
}; };
// TODO: Someone's abandoned dairy feature.
#define MaxDiaryPages 64 #define MaxDiaryPages 64
#define MaxStringsPerPage 8 #define MaxStringsPerPage 8
@ -1132,7 +1133,7 @@ struct LaraInventoryData
bool HasBinoculars; bool HasBinoculars;
bool HasCrowbar; bool HasCrowbar;
bool HasTorch; bool HasTorch;
bool HasLasersight; // TODO: Duplicated in CarriedWeaponInfo. bool HasLasersight; // TODO: Duplicated in CarriedWeaponInfo.
bool HasSilencer; // TODO: Duplicated in CarriedWeaponInfo. bool HasSilencer; // TODO: Duplicated in CarriedWeaponInfo.
int TotalSmallMedipacks; int TotalSmallMedipacks;

View file

@ -177,7 +177,7 @@ bool TestLaraHang(ItemInfo* item, CollisionInfo* coll)
{ {
SetAnimation(item, LA_FALL_START); SetAnimation(item, LA_FALL_START);
item->Pose.Position.y += CLICK(1); item->Pose.Position.y += CLICK(1);
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Animation.Velocity = 2; item->Animation.Velocity = 2;
item->Animation.VerticalVelocity = 1; item->Animation.VerticalVelocity = 1;
lara->Control.HandStatus = HandStatus::Free; 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.x += coll->Shift.x;
item->Pose.Position.y += GetBoundsAccurate(item)->Y2 * 1.8f; item->Pose.Position.y += GetBoundsAccurate(item)->Y2 * 1.8f;
item->Pose.Position.z += coll->Shift.z; item->Pose.Position.z += coll->Shift.z;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Animation.Velocity = 2; item->Animation.Velocity = 2;
item->Animation.VerticalVelocity = 1; item->Animation.VerticalVelocity = 1;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
@ -271,7 +271,7 @@ bool TestLaraHangJump(ItemInfo* item, CollisionInfo* coll)
ResetLaraFlex(item); ResetLaraFlex(item);
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.VerticalVelocity = 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); item->Pose.Position.y += coll->Middle.Ceiling + (LARA_HEIGHT_MONKEY - coll->Setup.Height);
lara->Control.HandStatus = HandStatus::Busy; lara->Control.HandStatus = HandStatus::Busy;
return true; return true;
@ -318,9 +318,9 @@ bool TestLaraHangJump(ItemInfo* item, CollisionInfo* coll)
else else
SnapItemToLedge(item, coll, 0.2f); SnapItemToLedge(item, coll, 0.2f);
item->Animation.IsAirborne = true;
item->Animation.Velocity = 2; item->Animation.Velocity = 2;
item->Animation.VerticalVelocity = 1; item->Animation.VerticalVelocity = 1;
item->Animation.Airborne = true;
lara->Control.TurnRate = 0; lara->Control.TurnRate = 0;
lara->Control.HandStatus = HandStatus::Busy; lara->Control.HandStatus = HandStatus::Busy;
return true; return true;
@ -338,7 +338,7 @@ bool TestLaraHangJumpUp(ItemInfo* item, CollisionInfo* coll)
SetAnimation(item, LA_JUMP_UP_TO_MONKEY); SetAnimation(item, LA_JUMP_UP_TO_MONKEY);
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.VerticalVelocity = 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); item->Pose.Position.y += coll->Middle.Ceiling + (LARA_HEIGHT_MONKEY - coll->Setup.Height);
lara->Control.HandStatus = HandStatus::Busy; lara->Control.HandStatus = HandStatus::Busy;
return true; return true;
@ -374,7 +374,7 @@ bool TestLaraHangJumpUp(ItemInfo* item, CollisionInfo* coll)
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
lara->Control.HandStatus = HandStatus::Busy; lara->Control.HandStatus = HandStatus::Busy;
lara->ExtraTorsoRot = Vector3Shrt(); lara->ExtraTorsoRot = Vector3Shrt();
return true; return true;
@ -908,7 +908,7 @@ bool TestLaraWaterStepOut(ItemInfo* item, CollisionInfo* coll)
item->Pose.Orientation.z = 0; item->Pose.Orientation.z = 0;
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
lara->Control.WaterStatus = WaterStatus::Wade; lara->Control.WaterStatus = WaterStatus::Wade;
return true; return true;
@ -990,7 +990,7 @@ bool TestLaraWaterClimbOut(ItemInfo* item, CollisionInfo* coll)
item->Pose.Position.y += frontFloor - 5; item->Pose.Position.y += frontFloor - 5;
item->Animation.ActiveState = LS_ONWATER_EXIT; item->Animation.ActiveState = LS_ONWATER_EXIT;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
lara->Control.TurnRate = 0; lara->Control.TurnRate = 0;
@ -1062,7 +1062,7 @@ bool TestLaraLadderClimbOut(ItemInfo* item, CollisionInfo* coll) // NEW function
item->Pose.Orientation.z = 0; item->Pose.Orientation.z = 0;
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
lara->Control.TurnRate = 0; lara->Control.TurnRate = 0;
lara->Control.HandStatus = HandStatus::Busy; lara->Control.HandStatus = HandStatus::Busy;
lara->Control.WaterStatus = WaterStatus::Dry; lara->Control.WaterStatus = WaterStatus::Dry;
@ -1090,7 +1090,7 @@ void TestLaraWaterDepth(ItemInfo* item, CollisionInfo* coll)
item->Pose.Position.y = probe.Position.Floor; item->Pose.Position.y = probe.Position.Floor;
item->Pose.Orientation.x = 0; item->Pose.Orientation.x = 0;
item->Pose.Orientation.z = 0; item->Pose.Orientation.z = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
lara->Control.WaterStatus = WaterStatus::Wade; 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; 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 || (heightFromFloor <= item->Animation.VerticalVelocity ||
TestEnvironment(ENV_FLAG_SWAMP, item))) TestEnvironment(ENV_FLAG_SWAMP, item)))
{ {

View file

@ -51,7 +51,7 @@ void AnimateLara(ItemInfo* item)
case COMMAND_JUMP_VELOCITY: case COMMAND_JUMP_VELOCITY:
item->Animation.VerticalVelocity = *(cmd++); item->Animation.VerticalVelocity = *(cmd++);
item->Animation.Velocity = *(cmd++); item->Animation.Velocity = *(cmd++);
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
if (lara->Control.CalculatedJumpVelocity) if (lara->Control.CalculatedJumpVelocity)
{ {
item->Animation.VerticalVelocity = 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); lateral += anim->Xacceleration * (item->Animation.FrameNumber - anim->frameBase);
item->Animation.LateralVelocity = lateral >>= 16; item->Animation.LateralVelocity = lateral >>= 16;
if (item->Animation.Airborne) if (item->Animation.IsAirborne)
{ {
if (TestEnvironment(ENV_FLAG_SWAMP, item)) if (TestEnvironment(ENV_FLAG_SWAMP, item))
{ {
@ -158,7 +158,7 @@ void AnimateLara(ItemInfo* item)
if (abs(item->Animation.Velocity) < 8) if (abs(item->Animation.Velocity) < 8)
{ {
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
} }
if (item->Animation.VerticalVelocity > 128) if (item->Animation.VerticalVelocity > 128)
item->Animation.VerticalVelocity /= 2; item->Animation.VerticalVelocity /= 2;
@ -241,7 +241,7 @@ void AnimateItem(ItemInfo* item)
case COMMAND_JUMP_VELOCITY: case COMMAND_JUMP_VELOCITY:
item->Animation.VerticalVelocity = *(cmd++); item->Animation.VerticalVelocity = *(cmd++);
item->Animation.Velocity = *(cmd++); item->Animation.Velocity = *(cmd++);
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
break; break;
case COMMAND_DEACTIVATE: case COMMAND_DEACTIVATE:
@ -348,7 +348,7 @@ void AnimateItem(ItemInfo* item)
int lateral = 0; int lateral = 0;
if (item->Animation.Airborne) if (item->Animation.IsAirborne)
{ {
item->Animation.VerticalVelocity += (item->Animation.VerticalVelocity >= 128 ? 1 : 6); item->Animation.VerticalVelocity += (item->Animation.VerticalVelocity >= 128 ? 1 : 6);
item->Pose.Position.y += item->Animation.VerticalVelocity; item->Pose.Position.y += item->Animation.VerticalVelocity;

View file

@ -38,11 +38,11 @@ void GenericSphereBoxCollision(short itemNumber, ItemInfo* laraItem, CollisionIn
int collidedBits = TestCollision(item, laraItem); int collidedBits = TestCollision(item, laraItem);
if (collidedBits) if (collidedBits)
{ {
short oldRot = item->Pose.Orientation.y; short oldYOrient = item->Pose.Orientation.y;
item->Pose.Orientation.y = 0; item->Pose.Orientation.y = 0;
GetSpheres(item, CreatureSpheres, SPHERES_SPACE_WORLD, Matrix::Identity); GetSpheres(item, CreatureSpheres, SPHERES_SPACE_WORLD, Matrix::Identity);
item->Pose.Orientation.y = oldRot; item->Pose.Orientation.y = oldYOrient;
int deadlyBits = *((int*)&item->ItemFlags[0]); int deadlyBits = *((int*)&item->ItemFlags[0]);
auto* sphere = &CreatureSpheres[0]; auto* sphere = &CreatureSpheres[0];
@ -821,7 +821,8 @@ bool ItemPushStatic(ItemInfo* item, MESH_INFO* mesh, CollisionInfo* coll) // pre
if (coll->CollisionType == CT_NONE) if (coll->CollisionType == CT_NONE)
{ {
coll->Setup.OldPosition = item->Pose.Position; coll->Setup.OldPosition = item->Pose.Position;
UpdateItemRoom(item, -10); if (item->IsLara())
UpdateItemRoom(item, -10);
} }
else 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 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; auto* lara = GetLaraInfo(item);
Lara.Control.HandStatus = HandStatus::Free; lara->Control.IsMoving = false;
lara->Control.HandStatus = HandStatus::Free;
} }
return true; return true;
@ -1672,64 +1674,6 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
ItemNewRoom(itemNumber, collResult.RoomNumber); 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) void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
{ {
laraItem->HitStatus = false; laraItem->HitStatus = false;

View file

@ -11,12 +11,6 @@ constexpr auto MAX_COLLIDED_OBJECTS = 1024;
constexpr auto ITEM_RADIUS_YMAX = SECTOR(3); constexpr auto ITEM_RADIUS_YMAX = SECTOR(3);
constexpr auto VEHICLE_COLLISION_TERMINAL_VELOCITY = 30; 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 BOUNDING_BOX GlobalCollisionBounds;
extern ItemInfo* CollidedItems[MAX_COLLIDED_OBJECTS]; 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 DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, int zv);
void DoObjectCollision(ItemInfo* item, CollisionInfo* coll); void DoObjectCollision(ItemInfo* item, CollisionInfo* coll);
void DoVehicleCollision(ItemInfo* vehicle, int radius);
int DoVehicleWaterMovement(ItemInfo* vehicle, ItemInfo* lara, int currentVelocity, int radius, short* angle);

View file

@ -123,7 +123,7 @@ CollisionResult GetCollision(ItemInfo* item)
} }
// Overload used to probe point/room collision parameters from a given item's position. // 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; short tempRoomNumber = item->RoomNumber;
@ -133,18 +133,18 @@ CollisionResult GetCollision(ItemInfo* item, short angle, float forward, float v
item->Location : item->Location :
ROOM_VECTOR{ GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &tempRoomNumber)->Room, item->Pose.Position.y }; 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; int adjacentRoomNumber = GetRoom(location, item->Pose.Position.x, point.y, item->Pose.Position.z).roomNumber;
return GetCollision(point.x, point.y, point.z, adjacentRoomNumber); return GetCollision(point.x, point.y, point.z, adjacentRoomNumber);
} }
// Overload used to probe point/room collision parameters from a given position. // 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; short tempRoomNumber = roomNumber;
auto location = ROOM_VECTOR{ GetFloor(pos.x, pos.y, pos.z, &tempRoomNumber)->Room, pos.y }; 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; int adjacentRoomNumber = GetRoom(location, pos.x, point.y, pos.z).roomNumber;
return GetCollision(point.x, point.y, point.z, adjacentRoomNumber); 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 = {}; CollisionResult result = {};
// Record coordinates. // Record coordinates.
result.Coordinates = Vector3(x, y, z); result.Coordinates = Vector3Int(x, y, z);
// Return provided block into result as itself. // Return provided block into result as itself.
result.Block = floor; result.Block = floor;

View file

@ -54,7 +54,7 @@ struct CollisionPosition
struct CollisionResult struct CollisionResult
{ {
Vector3 Coordinates; Vector3Int Coordinates;
int RoomNumber; int RoomNumber;
FloorInfo* Block; FloorInfo* Block;
@ -123,8 +123,8 @@ struct CollisionInfo
[[nodiscard]] bool TestItemRoomCollisionAABB(ItemInfo* item); [[nodiscard]] bool TestItemRoomCollisionAABB(ItemInfo* item);
CollisionResult GetCollision(ItemInfo* item); CollisionResult GetCollision(ItemInfo* item);
CollisionResult GetCollision(ItemInfo* item, 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 vertical = 0.0f, float lateral = 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(int x, int y, int z, short roomNumber);
CollisionResult GetCollision(FloorInfo* floor, int x, int y, int z); 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, int roomNumber);
bool TestEnvironment(RoomEnvFlags environmentType, ROOM_INFO* room); bool TestEnvironment(RoomEnvFlags environmentType, ROOM_INFO* room);
bool TestEnvironmentFlags(RoomEnvFlags environmentType, int flags); bool TestEnvironmentFlags(RoomEnvFlags environmentType, int flags);

View file

@ -17,5 +17,5 @@ struct SPHERE
extern SPHERE LaraSpheres[MAX_SPHERES]; extern SPHERE LaraSpheres[MAX_SPHERES];
extern SPHERE CreatureSpheres[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); int GetSpheres(ItemInfo* item, SPHERE* ptr, int worldSpace, Matrix local);

View file

@ -262,7 +262,7 @@ void CreatureKill(ItemInfo* item, int killAnim, int killState, int laraKillState
LaraItem->Animation.TargetState = laraKillState; LaraItem->Animation.TargetState = laraKillState;
LaraItem->Pose = item->Pose; LaraItem->Pose = item->Pose;
LaraItem->Animation.Airborne = false; LaraItem->Animation.IsAirborne = false;
LaraItem->Animation.Velocity = 0; LaraItem->Animation.Velocity = 0;
LaraItem->Animation.VerticalVelocity = 0; LaraItem->Animation.VerticalVelocity = 0;

View file

@ -6,12 +6,12 @@
#include "Game/itemdata/creature_info.h" #include "Game/itemdata/creature_info.h"
#include "Game/itemdata/door_data.h" #include "Game/itemdata/door_data.h"
#include "Game/Lara/lara_struct.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/TR2/Vehicles/skidoo_info.h"
#include "Objects/TR3/Vehicles/minecart_info.h" #include "Objects/TR3/Vehicles/minecart_info.h"
#include "Objects/TR3/Vehicles/big_gun_info.h" #include "Objects/TR3/Vehicles/big_gun_info.h"
#include "Objects/TR3/Vehicles/kayak_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/rubber_boat_info.h"
#include "Objects/TR3/Vehicles/upv_info.h" #include "Objects/TR3/Vehicles/upv_info.h"
#include "Objects/TR4/Vehicles/jeep_info.h" #include "Objects/TR4/Vehicles/jeep_info.h"
@ -46,23 +46,23 @@ class ITEM_DATA
double, double,
long double, long double,
std::array<short, 4>, std::array<short, 4>,
GameVector,
DOOR_DATA,
PushableInfo,
ItemInfo*, ItemInfo*,
LaraInfo*,
CreatureInfo, CreatureInfo,
WraithInfo,
LaserHeadInfo, LaserHeadInfo,
QuadInfo, QuadBikeInfo,
BigGunInfo, BigGunInfo,
MotorbikeInfo, MotorbikeInfo,
JeepInfo, JeepInfo,
LaraInfo*,
KayakInfo, KayakInfo,
DOOR_DATA,
SkidooInfo, SkidooInfo,
UPVInfo, UPVInfo,
SpeedBoatInfo, SpeedboatInfo,
GameVector,
WraithInfo,
RubberBoatInfo, RubberBoatInfo,
PushableInfo,
MinecartInfo MinecartInfo
> data; > data;
public: public:

View file

@ -462,7 +462,7 @@ void InitialiseItem(short itemNumber)
item->Active = false; item->Active = false;
item->Status = ITEM_NOT_ACTIVE; item->Status = ITEM_NOT_ACTIVE;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->HitStatus = false; item->HitStatus = false;
item->Collidable = true; item->Collidable = true;
item->LookedAt = false; item->LookedAt = false;

View file

@ -22,31 +22,31 @@ constexpr auto NUM_ITEMS = 1024;
enum AIObjectType enum AIObjectType
{ {
NO_AI = 0x0000, NO_AI = 0,
GUARD = 0x0001, GUARD = (1 << 0),
AMBUSH = 0x0002, AMBUSH = (1 << 1),
PATROL1 = 0x0004, PATROL1 = (1 << 2),
MODIFY = 0x0008, MODIFY = (1 << 3),
FOLLOW = 0x0010, FOLLOW = (1 << 4),
PATROL2 = 0x0020, PATROL2 = (1 << 5),
ALL_AIOBJ = (GUARD | AMBUSH | PATROL1 | MODIFY | FOLLOW | PATROL2) ALL_AIOBJ = (GUARD | AMBUSH | PATROL1 | MODIFY | FOLLOW | PATROL2)
}; };
enum ItemStatus enum ItemStatus
{ {
ITEM_NOT_ACTIVE = 0, ITEM_NOT_ACTIVE = 0,
ITEM_ACTIVE = 1, ITEM_ACTIVE = 1,
ITEM_DEACTIVATED = 2, ITEM_DEACTIVATED = 2,
ITEM_INVISIBLE = 3 ITEM_INVISIBLE = 3
}; };
enum ItemFlags enum ItemFlags
{ {
IFLAG_CLEAR_BODY = (1 << 7), // 0x0080 IFLAG_CLEAR_BODY = (1 << 7), // 0x0080
IFLAG_INVISIBLE = (1 << 8), // 0x0100 IFLAG_INVISIBLE = (1 << 8), // 0x0100
IFLAG_REVERSE = (1 << 14), // 0x4000 IFLAG_REVERSE = (1 << 14), // 0x4000
IFLAG_KILLED = (1 << 15), // 0x8000 IFLAG_KILLED = (1 << 15), // 0x8000
IFLAG_ACTIVATION_MASK = 0x3E00 // bits 9-13 IFLAG_ACTIVATION_MASK = 0x3E00 // bits 9-13
}; };
constexpr unsigned int ALL_JOINT_BITS = UINT_MAX; constexpr unsigned int ALL_JOINT_BITS = UINT_MAX;
@ -67,7 +67,7 @@ struct EntityAnimationData
int TargetState; int TargetState;
int RequiredState; // TODO: Phase out this weird feature. int RequiredState; // TODO: Phase out this weird feature.
bool Airborne; bool IsAirborne;
int Velocity; int Velocity;
int VerticalVelocity; int VerticalVelocity;
int LateralVelocity; int LateralVelocity;
@ -85,8 +85,10 @@ struct ItemInfo
ITEM_DATA Data; ITEM_DATA Data;
EntityAnimationData Animation; EntityAnimationData Animation;
PHD_3DPOS Pose;
PHD_3DPOS StartPose; PHD_3DPOS StartPose;
PHD_3DPOS Pose;
ROOM_VECTOR Location;
short RoomNumber;
int Floor; int Floor;
int HitPoints; int HitPoints;
@ -95,8 +97,6 @@ struct ItemInfo
bool Collidable; bool Collidable;
bool InDrawRoom; bool InDrawRoom;
ROOM_VECTOR Location;
short RoomNumber;
int BoxNumber; int BoxNumber;
int Timer; int Timer;
short Shade; short Shade;

View file

@ -339,7 +339,7 @@ bool SaveGame::Save(int slot)
CarriedWeaponInfo* info = &Lara.Weapons[i]; CarriedWeaponInfo* info = &Lara.Weapons[i];
std::vector<flatbuffers::Offset<Save::AmmoInfo>> ammos; std::vector<flatbuffers::Offset<Save::AmmoInfo>> ammos;
for (int j = 0; j < (int)WeaponAmmoType::NumAmmos; j++) for (int j = 0; j < (int)WeaponAmmoType::NumAmmoTypes; j++)
{ {
Save::AmmoInfoBuilder ammo{ fbb }; Save::AmmoInfoBuilder ammo{ fbb };
ammo.add_count(info->Ammo[j].getCount()); ammo.add_count(info->Ammo[j].getCount());
@ -466,9 +466,9 @@ bool SaveGame::Save(int slot)
creatureBuilder.add_ai_target_number(creature->AITargetNumber); creatureBuilder.add_ai_target_number(creature->AITargetNumber);
creatureOffset = creatureBuilder.Finish(); creatureOffset = creatureBuilder.Finish();
} }
else if (itemToSerialize.Data.is<QuadInfo>()) else if (itemToSerialize.Data.is<QuadBikeInfo>())
{ {
auto quad = (QuadInfo*)itemToSerialize.Data; auto quad = (QuadBikeInfo*)itemToSerialize.Data;
Save::QuadBikeBuilder quadBuilder{ fbb }; Save::QuadBikeBuilder quadBuilder{ fbb };
@ -496,13 +496,13 @@ bool SaveGame::Save(int slot)
Save::UPVBuilder upvBuilder{ fbb }; Save::UPVBuilder upvBuilder{ fbb };
upvBuilder.add_fan_rot(upv->FanRot); upvBuilder.add_fan_rot(upv->TurbineRotation);
upvBuilder.add_flags(upv->Flags); upvBuilder.add_flags(upv->Flags);
upvBuilder.add_harpoon_left(upv->HarpoonLeft); upvBuilder.add_harpoon_left(upv->HarpoonLeft);
upvBuilder.add_harpoon_timer(upv->HarpoonTimer); upvBuilder.add_harpoon_timer(upv->HarpoonTimer);
upvBuilder.add_rot(upv->Rot); upvBuilder.add_rot(upv->TurnRate.y);
upvBuilder.add_velocity(upv->Velocity); upvBuilder.add_velocity(upv->Velocity);
upvBuilder.add_x_rot(upv->XRot); upvBuilder.add_x_rot(upv->TurnRate.x);
upvOffset = upvBuilder.Finish(); upvOffset = upvBuilder.Finish();
} }
else if (itemToSerialize.Data.is<MinecartInfo>()) else if (itemToSerialize.Data.is<MinecartInfo>())
@ -534,15 +534,15 @@ bool SaveGame::Save(int slot)
kayakBuilder.add_flags(kayak->Flags); kayakBuilder.add_flags(kayak->Flags);
kayakBuilder.add_forward(kayak->Forward); kayakBuilder.add_forward(kayak->Forward);
kayakBuilder.add_front_vertical_velocity(kayak->FrontVerticalVelocity); 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_left_vertical_velocity(kayak->LeftVerticalVelocity);
kayakBuilder.add_old_pos(&Save::Position( kayakBuilder.add_old_pos(&Save::Position(
kayak->OldPos.Position.x, kayak->OldPose.Position.x,
kayak->OldPos.Position.y, kayak->OldPose.Position.y,
kayak->OldPos.Position.z, kayak->OldPose.Position.z,
kayak->OldPos.Orientation.x, kayak->OldPose.Orientation.x,
kayak->OldPos.Orientation.y, kayak->OldPose.Orientation.y,
kayak->OldPos.Orientation.z)); kayak->OldPose.Orientation.z));
kayakBuilder.add_right_vertical_velocity(kayak->RightVerticalVelocity); kayakBuilder.add_right_vertical_velocity(kayak->RightVerticalVelocity);
kayakBuilder.add_true_water(kayak->TrueWater); kayakBuilder.add_true_water(kayak->TrueWater);
kayakBuilder.add_turn(kayak->Turn); 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_triggered((itemToSerialize.Flags & (TRIGGERED | CODE_BITS | ONESHOT)) != 0);
serializedItem.add_active(itemToSerialize.Active); serializedItem.add_active(itemToSerialize.Active);
serializedItem.add_status(itemToSerialize.Status); 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_hit_stauts(itemToSerialize.HitStatus);
serializedItem.add_ai_bits(itemToSerialize.AIBits); serializedItem.add_ai_bits(itemToSerialize.AIBits);
serializedItem.add_collidable(itemToSerialize.Collidable); serializedItem.add_collidable(itemToSerialize.Collidable);
@ -614,7 +614,7 @@ bool SaveGame::Save(int slot)
serializedItem.add_data_type(Save::ItemData::Creature); serializedItem.add_data_type(Save::ItemData::Creature);
serializedItem.add_data(creatureOffset.Union()); serializedItem.add_data(creatureOffset.Union());
} }
else if (itemToSerialize.Data.is<QuadInfo>()) else if (itemToSerialize.Data.is<QuadBikeInfo>())
{ {
serializedItem.add_data_type(Save::ItemData::QuadBike); serializedItem.add_data_type(Save::ItemData::QuadBike);
serializedItem.add_data(quadOffset.Union()); serializedItem.add_data(quadOffset.Union());
@ -1284,7 +1284,7 @@ bool SaveGame::Load(int slot)
item->HitStatus = savedItem->hit_stauts(); item->HitStatus = savedItem->hit_stauts();
item->Status = savedItem->status(); item->Status = savedItem->status();
item->AIBits = savedItem->ai_bits(); item->AIBits = savedItem->ai_bits();
item->Animation.Airborne = savedItem->airborne(); item->Animation.IsAirborne = savedItem->is_airborne();
item->Collidable = savedItem->collidable(); item->Collidable = savedItem->collidable();
item->LookedAt = savedItem->looked_at(); item->LookedAt = savedItem->looked_at();
@ -1346,75 +1346,75 @@ bool SaveGame::Load(int slot)
creature->Tosspad = savedCreature->tosspad(); creature->Tosspad = savedCreature->tosspad();
SetBaddyTarget(i, savedCreature->ai_target_number()); SetBaddyTarget(i, savedCreature->ai_target_number());
} }
else if (item->Data.is<QuadInfo>()) else if (item->Data.is<QuadBikeInfo>())
{ {
auto quad = (QuadInfo*)item->Data; auto* quadBike = (QuadBikeInfo*)item->Data;
auto savedQuad = (Save::QuadBike*)savedItem->data(); auto* savedQuad = (Save::QuadBike*)savedItem->data();
quad->CanStartDrift = savedQuad->can_start_drift(); quadBike->CanStartDrift = savedQuad->can_start_drift();
quad->DriftStarting = savedQuad->drift_starting(); quadBike->DriftStarting = savedQuad->drift_starting();
quad->EngineRevs = savedQuad->engine_revs(); quadBike->EngineRevs = savedQuad->engine_revs();
quad->ExtraRotation = savedQuad->extra_rotation(); quadBike->ExtraRotation = savedQuad->extra_rotation();
quad->Flags = savedQuad->flags(); quadBike->Flags = savedQuad->flags();
quad->FrontRot = savedQuad->front_rot(); quadBike->FrontRot = savedQuad->front_rot();
quad->LeftVerticalVelocity = savedQuad->left_vertical_velocity(); quadBike->LeftVerticalVelocity = savedQuad->left_vertical_velocity();
quad->MomentumAngle = savedQuad->momentum_angle(); quadBike->MomentumAngle = savedQuad->momentum_angle();
quad->NoDismount = savedQuad->no_dismount(); quadBike->NoDismount = savedQuad->no_dismount();
quad->Pitch = savedQuad->pitch(); quadBike->Pitch = savedQuad->pitch();
quad->RearRot = savedQuad->rear_rot(); quadBike->RearRot = savedQuad->rear_rot();
quad->Revs = savedQuad->revs(); quadBike->Revs = savedQuad->revs();
quad->RightVerticalVelocity = savedQuad->right_vertical_velocity(); quadBike->RightVerticalVelocity = savedQuad->right_vertical_velocity();
quad->SmokeStart = savedQuad->smoke_start(); quadBike->SmokeStart = savedQuad->smoke_start();
quad->TurnRate = savedQuad->turn_rate(); quadBike->TurnRate = savedQuad->turn_rate();
quad->Velocity = savedQuad->velocity(); quadBike->Velocity = savedQuad->velocity();
} }
else if (item->Data.is<UPVInfo>()) else if (item->Data.is<UPVInfo>())
{ {
auto upv = (UPVInfo*)item->Data; auto* upv = (UPVInfo*)item->Data;
auto savedUpv = (Save::UPV*)savedItem->data(); auto* savedUpv = (Save::UPV*)savedItem->data();
upv->FanRot = savedUpv->fan_rot(); upv->TurbineRotation = savedUpv->fan_rot();
upv->Flags = savedUpv->flags(); upv->Flags = savedUpv->flags();
upv->HarpoonLeft = savedUpv->harpoon_left(); upv->HarpoonLeft = savedUpv->harpoon_left();
upv->HarpoonTimer = savedUpv->harpoon_timer(); upv->HarpoonTimer = savedUpv->harpoon_timer();
upv->Rot = savedUpv->rot(); upv->TurnRate.y = savedUpv->rot();
upv->Velocity = savedUpv->velocity(); upv->Velocity = savedUpv->velocity();
upv->XRot = savedUpv->x_rot(); upv->TurnRate.x = savedUpv->x_rot();
} }
else if (item->Data.is<MinecartInfo>()) else if (item->Data.is<MinecartInfo>())
{ {
auto mine = (MinecartInfo*)item->Data; auto* minecart = (MinecartInfo*)item->Data;
auto savedMine = (Save::Minecart*)savedItem->data(); auto* savedMine = (Save::Minecart*)savedItem->data();
mine->Flags = savedMine->flags(); minecart->Flags = savedMine->flags();
mine->FloorHeightFront = savedMine->floor_height_front(); minecart->FloorHeightFront = savedMine->floor_height_front();
mine->FloorHeightMiddle = savedMine->floor_height_middle(); minecart->FloorHeightMiddle = savedMine->floor_height_middle();
mine->Gradient = savedMine->gradient(); minecart->Gradient = savedMine->gradient();
mine->StopDelay = savedMine->stop_delay(); minecart->StopDelay = savedMine->stop_delay();
mine->TurnLen = savedMine->turn_len(); minecart->TurnLen = savedMine->turn_len();
mine->TurnRot = savedMine->turn_rot(); minecart->TurnRot = savedMine->turn_rot();
mine->TurnX = savedMine->turn_x(); minecart->TurnX = savedMine->turn_x();
mine->TurnZ = savedMine->turn_z(); minecart->TurnZ = savedMine->turn_z();
mine->Velocity = savedMine->velocity(); minecart->Velocity = savedMine->velocity();
mine->VerticalVelocity = savedMine->vertical_velocity(); minecart->VerticalVelocity = savedMine->vertical_velocity();
} }
else if (item->Data.is<KayakInfo>()) else if (item->Data.is<KayakInfo>())
{ {
auto kayak = (KayakInfo*)item->Data; auto* kayak = (KayakInfo*)item->Data;
auto savedKayak = (Save::Kayak*)savedItem->data(); auto* savedKayak = (Save::Kayak*)savedItem->data();
kayak->CurrentStartWake = savedKayak->flags(); kayak->CurrentStartWake = savedKayak->flags();
kayak->Flags = savedKayak->flags(); kayak->Flags = savedKayak->flags();
kayak->Forward = savedKayak->forward(); kayak->Forward = savedKayak->forward();
kayak->FrontVerticalVelocity = savedKayak->front_vertical_velocity(); 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->LeftVerticalVelocity = savedKayak->left_vertical_velocity();
kayak->OldPos.Position.x = savedKayak->old_pos()->x_pos(); kayak->OldPose.Position.x = savedKayak->old_pos()->x_pos();
kayak->OldPos.Position.y = savedKayak->old_pos()->y_pos(); kayak->OldPose.Position.y = savedKayak->old_pos()->y_pos();
kayak->OldPos.Position.z = savedKayak->old_pos()->z_pos(); kayak->OldPose.Position.z = savedKayak->old_pos()->z_pos();
kayak->OldPos.Orientation.x = savedKayak->old_pos()->x_rot(); kayak->OldPose.Orientation.x = savedKayak->old_pos()->x_rot();
kayak->OldPos.Orientation.y = savedKayak->old_pos()->y_rot(); kayak->OldPose.Orientation.y = savedKayak->old_pos()->y_rot();
kayak->OldPos.Orientation.z = savedKayak->old_pos()->z_rot(); kayak->OldPose.Orientation.z = savedKayak->old_pos()->z_rot();
kayak->RightVerticalVelocity = savedKayak->right_vertical_velocity(); kayak->RightVerticalVelocity = savedKayak->right_vertical_velocity();
kayak->TrueWater = savedKayak->true_water(); kayak->TrueWater = savedKayak->true_water();
kayak->Turn = savedKayak->turn(); kayak->Turn = savedKayak->turn();
@ -1425,21 +1425,21 @@ bool SaveGame::Load(int slot)
} }
else if (savedItem->data_type() == Save::ItemData::Short) else if (savedItem->data_type() == Save::ItemData::Short)
{ {
auto data = savedItem->data(); auto* data = savedItem->data();
auto savedData = (Save::Short*)data; auto* savedData = (Save::Short*)data;
item->Data = savedData->scalar(); item->Data = savedData->scalar();
} }
else if (savedItem->data_type() == Save::ItemData::Int) else if (savedItem->data_type() == Save::ItemData::Int)
{ {
auto data = savedItem->data(); auto* data = savedItem->data();
auto savedData = (Save::Int*)data; auto* savedData = (Save::Int*)data;
item->Data = savedData->scalar(); item->Data = savedData->scalar();
} }
} }
for (int i = 0; i < s->particles()->size(); i++) 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]; auto* particle = &Particles[i];
particle->x = particleInfo->x(); particle->x = particleInfo->x();
@ -1483,7 +1483,7 @@ bool SaveGame::Load(int slot)
for (int i = 0; i < s->bats()->size(); i++) 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]; auto* bat = &Bats[i];
bat->On = batInfo->on(); bat->On = batInfo->on();
@ -1515,7 +1515,7 @@ bool SaveGame::Load(int slot)
for (int i = 0; i < s->spiders()->size(); i++) 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]; auto* spider = &Spiders[i];
spider->On = spiderInfo->on(); spider->On = spiderInfo->on();

View file

@ -657,7 +657,7 @@ namespace TEN::Entities::Effects
!(TrInput & IN_ACTION) || !(TrInput & IN_ACTION) ||
laraItem->Animation.ActiveState != LS_IDLE || laraItem->Animation.ActiveState != LS_IDLE ||
laraItem->Animation.AnimNumber != LA_STAND_IDLE || laraItem->Animation.AnimNumber != LA_STAND_IDLE ||
laraItem->Animation.Airborne) laraItem->Animation.IsAirborne)
{ {
if (item->ObjectNumber == ID_BURNING_ROOTS) if (item->ObjectNumber == ID_BURNING_ROOTS)
ObjectCollision(itemNumber, laraItem, coll); ObjectCollision(itemNumber, laraItem, coll);

View file

@ -44,7 +44,7 @@ namespace TEN::Entities::Doors
laraItem->Animation.ActiveState == LS_IDLE && laraItem->Animation.ActiveState == LS_IDLE &&
laraItem->Animation.AnimNumber == LA_STAND_IDLE && laraItem->Animation.AnimNumber == LA_STAND_IDLE &&
!laraItem->HitStatus && !laraItem->HitStatus &&
!(doorItem->Status && doorItem->Animation.Airborne) && !(doorItem->Status && doorItem->Animation.IsAirborne) &&
laraInfo->Control.HandStatus == HandStatus::Free || laraInfo->Control.HandStatus == HandStatus::Free ||
laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber) laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber)
{ {

View file

@ -170,7 +170,7 @@ namespace TEN::Entities::Doors
auto* doorItem = &g_Level.Items[itemNumber]; auto* doorItem = &g_Level.Items[itemNumber];
if (doorItem->TriggerFlags == 2 && 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) && ((TrInput & IN_ACTION || g_Gui.GetInventoryItemChosen() == ID_CROWBAR_ITEM) &&
laraItem->Animation.ActiveState == LS_IDLE && laraItem->Animation.ActiveState == LS_IDLE &&
laraItem->Animation.AnimNumber == LA_STAND_IDLE && laraItem->Animation.AnimNumber == LA_STAND_IDLE &&

View file

@ -43,7 +43,7 @@ namespace TEN::Entities::Doors
if (TrInput & IN_ACTION && if (TrInput & IN_ACTION &&
laraItem->Animation.ActiveState == LS_UNDERWATER_IDLE && laraItem->Animation.ActiveState == LS_UNDERWATER_IDLE &&
laraInfo->Control.WaterStatus == WaterStatus::Underwater && laraInfo->Control.WaterStatus == WaterStatus::Underwater &&
!(doorItem->Status && doorItem->Animation.Airborne) && !(doorItem->Status && doorItem->Animation.IsAirborne) &&
laraInfo->Control.HandStatus == HandStatus::Free || laraInfo->Control.HandStatus == HandStatus::Free ||
laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber) laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber)
{ {

View file

@ -83,7 +83,7 @@ namespace TEN::Entities::Generic
Lara.Torch.State = TorchState::Dropping; Lara.Torch.State = TorchState::Dropping;
} }
else if (TrInput & IN_DRAW && else if (TrInput & IN_DRAW &&
!LaraItem->Animation.Airborne && !LaraItem->Animation.IsAirborne &&
!LaraItem->Animation.VerticalVelocity && !LaraItem->Animation.VerticalVelocity &&
LaraItem->Animation.ActiveState != LS_JUMP_PREPARE && LaraItem->Animation.ActiveState != LS_JUMP_PREPARE &&
LaraItem->Animation.ActiveState != LS_JUMP_UP && LaraItem->Animation.ActiveState != LS_JUMP_UP &&
@ -104,7 +104,7 @@ namespace TEN::Entities::Generic
} }
else if (Lara.Torch.State == TorchState::Throwing) 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.Locked = false;
Lara.LeftArm.FrameNumber = 0; Lara.LeftArm.FrameNumber = 0;
@ -304,7 +304,7 @@ namespace TEN::Entities::Generic
if (!(TrInput & IN_ACTION) || if (!(TrInput & IN_ACTION) ||
laraItem->Animation.ActiveState != LS_IDLE || laraItem->Animation.ActiveState != LS_IDLE ||
laraItem->Animation.AnimNumber != LA_STAND_IDLE || laraItem->Animation.AnimNumber != LA_STAND_IDLE ||
laraItem->Animation.Airborne || laraItem->Animation.IsAirborne ||
laraInfo->Control.Weapon.GunType != LaraWeaponType::Torch || laraInfo->Control.Weapon.GunType != LaraWeaponType::Torch ||
laraInfo->Control.HandStatus != HandStatus::WeaponReady || laraInfo->Control.HandStatus != HandStatus::WeaponReady ||
laraInfo->LeftArm.Locked || laraInfo->LeftArm.Locked ||

View file

@ -70,7 +70,7 @@ void CeilingTrapDoorCollision(short itemNumber, ItemInfo* laraItem, CollisionInf
if (TrInput & IN_ACTION && if (TrInput & IN_ACTION &&
laraItem->Animation.ActiveState == LS_JUMP_UP && laraItem->Animation.ActiveState == LS_JUMP_UP &&
laraItem->Animation.Airborne && laraItem->Animation.IsAirborne &&
laraInfo->Control.HandStatus == HandStatus::Free && laraInfo->Control.HandStatus == HandStatus::Free &&
trapDoorItem->Status != ITEM_ACTIVE && trapDoorItem->Status != ITEM_ACTIVE &&
itemIsAbove && itemIsAbove &&
@ -82,7 +82,7 @@ void CeilingTrapDoorCollision(short itemNumber, ItemInfo* laraItem, CollisionInf
ResetLaraFlex(laraItem); ResetLaraFlex(laraItem);
laraItem->Animation.VerticalVelocity = 0; laraItem->Animation.VerticalVelocity = 0;
laraItem->Animation.Airborne = false; laraItem->Animation.IsAirborne = false;
laraItem->Animation.AnimNumber = LA_TRAPDOOR_CEILING_OPEN; laraItem->Animation.AnimNumber = LA_TRAPDOOR_CEILING_OPEN;
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
laraItem->Animation.ActiveState = LS_FREEFALL_BIS; laraItem->Animation.ActiveState = LS_FREEFALL_BIS;

View file

@ -219,7 +219,7 @@ void HorizontalBarCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo*
laraItem->Animation.AnimNumber = LA_SWINGBAR_GRAB; laraItem->Animation.AnimNumber = LA_SWINGBAR_GRAB;
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
laraItem->Animation.VerticalVelocity = false; laraItem->Animation.VerticalVelocity = false;
laraItem->Animation.Airborne = false; laraItem->Animation.IsAirborne = false;
ResetLaraFlex(barItem); ResetLaraFlex(barItem);

View file

@ -74,7 +74,7 @@ namespace TEN::Entities::Generic
} }
else if (TrInput & IN_ACTION && isLara && else if (TrInput & IN_ACTION && isLara &&
laraInfo->Control.HandStatus == HandStatus::Free && laraInfo->Control.HandStatus == HandStatus::Free &&
laraItem->Animation.Airborne && laraItem->Animation.IsAirborne &&
laraItem->Animation.VerticalVelocity > (int)laraInfo->Control.HandStatus && // ????? laraItem->Animation.VerticalVelocity > (int)laraInfo->Control.HandStatus && // ?????
laraItem->Animation.ActiveState == LS_REACH || laraItem->Animation.ActiveState == LS_JUMP_UP) 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.ActiveState = LS_POLE_IDLE;
laraItem->Animation.VerticalVelocity = 0; laraItem->Animation.VerticalVelocity = 0;
laraItem->Animation.Airborne = false; laraItem->Animation.IsAirborne = false;
laraInfo->Control.HandStatus = HandStatus::Busy; laraInfo->Control.HandStatus = HandStatus::Busy;
poleItem->Pose.Orientation.y = rot; poleItem->Pose.Orientation.y = rot;
} }

View file

@ -181,7 +181,7 @@ namespace TEN::Entities::Generic
if (TrInput & IN_ACTION && if (TrInput & IN_ACTION &&
laraInfo->Control.HandStatus == HandStatus::Free && laraInfo->Control.HandStatus == HandStatus::Free &&
(laraItem->Animation.ActiveState == LS_REACH || laraItem->Animation.ActiveState == LS_JUMP_UP) && (laraItem->Animation.ActiveState == LS_REACH || laraItem->Animation.ActiveState == LS_JUMP_UP) &&
laraItem->Animation.Airborne && laraItem->Animation.IsAirborne &&
laraItem->Animation.VerticalVelocity > 0&& laraItem->Animation.VerticalVelocity > 0&&
rope->active) rope->active)
{ {
@ -211,7 +211,7 @@ namespace TEN::Entities::Generic
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
laraItem->Animation.VerticalVelocity = 0; laraItem->Animation.VerticalVelocity = 0;
laraItem->Animation.Airborne = false; laraItem->Animation.IsAirborne = false;
laraInfo->Control.HandStatus = HandStatus::Busy; laraInfo->Control.HandStatus = HandStatus::Busy;
laraInfo->Control.Rope.Ptr = ropeItem->TriggerFlags; laraInfo->Control.Rope.Ptr = ropeItem->TriggerFlags;
@ -644,7 +644,7 @@ namespace TEN::Entities::Generic
} }
item->Pose.Orientation.x = 0; item->Pose.Orientation.x = 0;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
Lara.Control.HandStatus = HandStatus::Free; Lara.Control.HandStatus = HandStatus::Free;
@ -671,7 +671,7 @@ namespace TEN::Entities::Generic
SetAnimation(item, stumble ? LA_JUMP_WALL_SMASH_START : LA_FALL_START); SetAnimation(item, stumble ? LA_JUMP_WALL_SMASH_START : LA_FALL_START);
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
auto* lara = GetLaraInfo(item); auto* lara = GetLaraInfo(item);
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;

View file

@ -67,12 +67,12 @@ namespace TEN::Entities::Switches
if (switchItem->Status == ITEM_NOT_ACTIVE) if (switchItem->Status == ITEM_NOT_ACTIVE)
{ {
if (!(switchItem->Flags & ONESHOT) && if (!(switchItem->Flags & IFLAG_INVISIBLE) &&
(TrInput & IN_ACTION && (TrInput & IN_ACTION &&
laraItem->Animation.ActiveState == LS_IDLE && laraItem->Animation.ActiveState == LS_IDLE &&
laraItem->Animation.AnimNumber == LA_STAND_IDLE && laraItem->Animation.AnimNumber == LA_STAND_IDLE &&
lara->Control.HandStatus == HandStatus::Free && lara->Control.HandStatus == HandStatus::Free &&
!switchItem->Animation.Airborne || !switchItem->Animation.IsAirborne ||
lara->Control.IsMoving && lara->Control.IsMoving &&
lara->InteractedItem == itemNum)) lara->InteractedItem == itemNum))
{ {

View file

@ -31,7 +31,7 @@ namespace TEN::Entities::Switches
switchItem->Flags |= 0x3E00; switchItem->Flags |= 0x3E00;
if (!TriggerActive(switchItem) && !(switchItem->Flags & ONESHOT)) if (!TriggerActive(switchItem) && !(switchItem->Flags & IFLAG_INVISIBLE))
{ {
if (switchItem->ObjectNumber == ID_JUMP_SWITCH) if (switchItem->ObjectNumber == ID_JUMP_SWITCH)
{ {

View file

@ -32,7 +32,7 @@ namespace TEN::Entities::Switches
if (TrInput & IN_ACTION && if (TrInput & IN_ACTION &&
(laraItem->Animation.ActiveState == LS_REACH || laraItem->Animation.ActiveState == LS_JUMP_UP) && (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 && laraItem->Animation.VerticalVelocity > 0 &&
laraInfo->Control.HandStatus == HandStatus::Free && laraInfo->Control.HandStatus == HandStatus::Free &&
!switchItem->Animation.ActiveState) !switchItem->Animation.ActiveState)
@ -45,7 +45,7 @@ namespace TEN::Entities::Switches
laraItem->Animation.AnimNumber = LA_JUMPSWITCH_PULL; laraItem->Animation.AnimNumber = LA_JUMPSWITCH_PULL;
laraItem->Animation.VerticalVelocity = 0; laraItem->Animation.VerticalVelocity = 0;
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;
laraItem->Animation.Airborne = false; laraItem->Animation.IsAirborne = false;
laraInfo->Control.HandStatus = HandStatus::Busy; laraInfo->Control.HandStatus = HandStatus::Busy;
switchItem->Animation.TargetState = SWITCH_ON; switchItem->Animation.TargetState = SWITCH_ON;
switchItem->Status = ITEM_ACTIVE; switchItem->Status = ITEM_ACTIVE;

View file

@ -49,7 +49,7 @@ namespace TEN::Entities::Switches
if (TrInput & IN_ACTION && if (TrInput & IN_ACTION &&
laraItem->Animation.ActiveState == LS_IDLE && laraItem->Animation.ActiveState == LS_IDLE &&
laraItem->Animation.AnimNumber == LA_STAND_IDLE && laraItem->Animation.AnimNumber == LA_STAND_IDLE &&
laraItem->Animation.Airborne == false && laraItem->Animation.IsAirborne == false &&
laraInfo->Control.HandStatus == HandStatus::Free || laraInfo->Control.HandStatus == HandStatus::Free ||
laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber) laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber)
{ {

View file

@ -58,7 +58,7 @@ namespace TEN::Entities::Switches
if (TrInput & IN_ACTION && if (TrInput & IN_ACTION &&
laraItem->Animation.ActiveState == LS_IDLE && laraItem->Animation.ActiveState == LS_IDLE &&
laraItem->Animation.AnimNumber == LA_STAND_IDLE && laraItem->Animation.AnimNumber == LA_STAND_IDLE &&
laraItem->Animation.Airborne == false && laraItem->Animation.IsAirborne == false &&
laraInfo->Control.HandStatus == HandStatus::Free && laraInfo->Control.HandStatus == HandStatus::Free &&
switchItem->Animation.ActiveState == TURN_SWITCH_STOP || switchItem->Animation.ActiveState == TURN_SWITCH_STOP ||
laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber) laraInfo->Control.IsMoving && laraInfo->InteractedItem == itemNumber)

View file

@ -30,7 +30,7 @@ namespace TEN::Entities::TR1
constexpr auto SHIFT = 75; 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) #define APE_DISPLAY_ANGLE ANGLE(45.0f)
enum ApeState enum ApeState
@ -238,7 +238,7 @@ namespace TEN::Entities::TR1
break; break;
case APE_STATE_RUN_FORWARD: case APE_STATE_RUN_FORWARD:
creatureInfo->MaxTurn = APE_RUN_TURN_ANGLE; creatureInfo->MaxTurn = APE_RUN_TURN_RATE_MAX;
if (creatureInfo->Flags == 0 && if (creatureInfo->Flags == 0 &&
AI.angle > -APE_DISPLAY_ANGLE && AI.angle > -APE_DISPLAY_ANGLE &&

View file

@ -32,8 +32,8 @@ namespace TEN::Entities::TR1
constexpr auto BEAR_REAR_CHANCE = 0x300; constexpr auto BEAR_REAR_CHANCE = 0x300;
constexpr auto BEAR_DROP_CHANCE = 0x600; constexpr auto BEAR_DROP_CHANCE = 0x600;
#define BEAR_WALK_TURN_ANGLE ANGLE(2.0f) #define BEAR_WALK_TURN_RATE_MAX ANGLE(2.0f)
#define BEAR_RUN_TURN_ANGLE ANGLE(5.0f) #define BEAR_RUN_TURN_RATE_MAX ANGLE(5.0f)
enum BearState enum BearState
{ {
@ -163,7 +163,7 @@ namespace TEN::Entities::TR1
break; break;
case BEAR_STATE_STROLL: 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) if (laraDead && item->TestBits(JointBitType::Touch, BearAttackJoints) && AI.ahead)
item->Animation.TargetState = BEAR_STATE_IDLE; item->Animation.TargetState = BEAR_STATE_IDLE;
@ -183,7 +183,7 @@ namespace TEN::Entities::TR1
break; break;
case BEAR_STATE_RUN_FORWARD: case BEAR_STATE_RUN_FORWARD:
creature->MaxTurn = BEAR_RUN_TURN_ANGLE; creature->MaxTurn = BEAR_RUN_TURN_RATE_MAX;
if (item->TestBits(JointBitType::Touch, BearAttackJoints)) if (item->TestBits(JointBitType::Touch, BearAttackJoints))
{ {

View file

@ -92,12 +92,12 @@ namespace TEN::Entities::TR1
// Compare floor heights. // Compare floor heights.
if (item->Floor >= laraFloorHeight + SECTOR(1) + 1 && // Add 1 to avoid bacon Lara dying when exiting water. 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); SetAnimation(item, LA_JUMP_WALL_SMASH_START);
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Data = -1; item->Data = -1;
item->Pose.Position.y += 50; item->Pose.Position.y += 50;
} }
@ -115,7 +115,7 @@ namespace TEN::Entities::TR1
TestTriggers(item, true); TestTriggers(item, true);
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.TargetState = LS_DEATH; item->Animation.TargetState = LS_DEATH;
item->Animation.RequiredState = LS_DEATH; item->Animation.RequiredState = LS_DEATH;
} }

View file

@ -95,7 +95,7 @@ namespace TEN::Entities::TR1
{ {
case MUTANT_STATE_SET: case MUTANT_STATE_SET:
item->Animation.TargetState = MUTANT_STATE_FALL; item->Animation.TargetState = MUTANT_STATE_FALL;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
break; break;
case MUTANT_STATE_IDLE: case MUTANT_STATE_IDLE:
@ -204,7 +204,7 @@ namespace TEN::Entities::TR1
LaraItem->Pose.Position.z = item->Pose.Position.z; LaraItem->Pose.Position.z = item->Pose.Position.z;
LaraItem->Pose.Orientation.y = item->Pose.Orientation.y; LaraItem->Pose.Orientation.y = item->Pose.Orientation.y;
LaraItem->Pose.Orientation.x = LaraItem->Pose.Orientation.z = 0; LaraItem->Pose.Orientation.x = LaraItem->Pose.Orientation.z = 0;
LaraItem->Animation.Airborne = false; LaraItem->Animation.IsAirborne = false;
LaraItem->HitPoints = -1; LaraItem->HitPoints = -1;
Lara.Air = -1; Lara.Air = -1;
Lara.Control.HandStatus = HandStatus::Busy; Lara.Control.HandStatus = HandStatus::Busy;
@ -229,7 +229,7 @@ namespace TEN::Entities::TR1
if (item->Pose.Position.y > item->Floor) if (item->Pose.Position.y > item->Floor)
{ {
item->Animation.TargetState = MUTANT_STATE_IDLE; item->Animation.TargetState = MUTANT_STATE_IDLE;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Pose.Position.y = item->Floor; item->Pose.Position.y = item->Floor;
Camera.bounce = 500; Camera.bounce = 500;
} }

View file

@ -88,13 +88,13 @@ namespace TEN::Entities::TR1
case NATLA_STATE_FALL: case NATLA_STATE_FALL:
if (item->Pose.Position.y < item->Floor) if (item->Pose.Position.y < item->Floor)
{ {
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
} }
else else
{ {
item->Animation.TargetState = NATLA_STATE_SEMI_DEATH; item->Animation.TargetState = NATLA_STATE_SEMI_DEATH;
item->Animation.Airborne = 0; item->Animation.IsAirborne = 0;
item->Pose.Position.y = item->Floor; item->Pose.Position.y = item->Floor;
timer = 0; timer = 0;
} }

View file

@ -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)) && if ((anim == DRAGON_ANIM_DEAD || (anim == DRAGON_ANIM_DEAD + 1 && frame <= DRAGON_ALMOST_LIVE)) &&
TrInput & IN_ACTION && TrInput & IN_ACTION &&
item->ObjectNumber == ID_DRAGON_BACK && item->ObjectNumber == ID_DRAGON_BACK &&
!laraItem->Animation.Airborne && !laraItem->Animation.IsAirborne &&
shift <= DRAGON_MID && shift <= DRAGON_MID &&
shift > (DRAGON_CLOSE - 350) && shift > (DRAGON_CLOSE - 350) &&
sideShift > -350 && sideShift > -350 &&
@ -194,7 +194,7 @@ void DragonCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
laraItem->Animation.TargetState = 7; laraItem->Animation.TargetState = 7;
laraItem->Pose = item->Pose; laraItem->Pose = item->Pose;
laraItem->Animation.Airborne = false; laraItem->Animation.IsAirborne = false;
laraItem->Animation.Velocity = 0; laraItem->Animation.Velocity = 0;
laraItem->Animation.VerticalVelocity = 0; laraItem->Animation.VerticalVelocity = 0;

View file

@ -64,7 +64,7 @@ void EagleControl(short itemNumber)
{ {
item->Pose.Position.y = item->Floor; item->Pose.Position.y = item->Floor;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.TargetState = 5; item->Animation.TargetState = 5;
} }
@ -83,7 +83,7 @@ void EagleControl(short itemNumber)
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
item->Animation.ActiveState = 4; item->Animation.ActiveState = 4;
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
break; break;
} }
item->Pose.Orientation.x = 0; item->Pose.Orientation.x = 0;

View file

@ -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); 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 && else if (riderItem->Status == ITEM_DEACTIVATED &&
item->Animation.Velocity == 0 && item->Animation.Velocity == 0 &&
item->Animation.VerticalVelocity == 0) item->Animation.VerticalVelocity == 0)
{ {
RemoveActiveItem(riderItemNumber); RemoveActiveItem(riderItemNumber);
riderItem->Collidable = false; riderItem->Collidable = false;
riderItem->HitPoints = NOT_TARGETABLE; riderItem->HitPoints = NOT_TARGETABLE;
riderItem->Flags |= ONESHOT; riderItem->Flags |= IFLAG_INVISIBLE;
DisableEntityAI(itemNumber); DisableEntityAI(itemNumber);
item->ObjectNumber = ID_SNOWMOBILE; item->ObjectNumber = ID_SNOWMOBILE;

View file

@ -61,7 +61,7 @@ void SwordGuardianControl(short itemNumber)
DisableEntityAI(itemNumber); DisableEntityAI(itemNumber);
KillItem(itemNumber); KillItem(itemNumber);
//item->status = ITEM_DESACTIVATED; //item->status = ITEM_DESACTIVATED;
//item->flags |= ONESHOT; //item->flags |= IFLAG_INVISIBLE;
item->Animation.ActiveState = 12; item->Animation.ActiveState = 12;
} }

View file

@ -24,7 +24,7 @@ void SpringBoardControl(short itemNumber)
LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase; LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase;
LaraItem->Animation.ActiveState = LS_JUMP_FORWARD; LaraItem->Animation.ActiveState = LS_JUMP_FORWARD;
LaraItem->Animation.TargetState = LS_JUMP_FORWARD; LaraItem->Animation.TargetState = LS_JUMP_FORWARD;
LaraItem->Animation.Airborne = true; LaraItem->Animation.IsAirborne = true;
LaraItem->Animation.VerticalVelocity = -240; LaraItem->Animation.VerticalVelocity = -240;
item->Animation.TargetState = 1; item->Animation.TargetState = 1;

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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;
};
}

View file

@ -23,53 +23,46 @@
using namespace TEN::Input; using namespace TEN::Input;
using namespace TEN::Math::Random; using namespace TEN::Math::Random;
using std::vector;
namespace TEN::Entities::Vehicles namespace TEN::Entities::Vehicles
{ {
#define DAMAGE_START 140 constexpr auto SKIDOO_RADIUS = 500;
#define DAMAGE_LENGTH 14 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) constexpr auto SKIDOO_SLOW_VELOCITY_MAX = 50;
#define SKIDOO_TURN (ANGLE(0.5f) + SKIDOO_UNDO_TURN) constexpr auto SKIDOO_NORMAL_VELOCITY_MAX = 100;
#define SKIDOO_MAX_TURN ANGLE(6.0f) constexpr auto SKIDOO_FAST_VELOCITY_MAX = 150;
#define SKIDOO_MOMENTUM_TURN ANGLE(3.0f) constexpr auto SKIDOO_TURN_VELOCITY_MAX = 15;
#define SKIDOO_MAX_MOMENTUM_TURN ANGLE(150.0f) constexpr auto SKIDOO_REVERSE_VELOCITY_MAX = 30;
#define SKIDOO_FAST_VELOCITY 150 constexpr auto SKIDOO_STEP_HEIGHT_MAX = CLICK(1); // Unused.
#define SKIDOO_MAX_VELOCITY 100 constexpr auto SKIDOO_MIN_BOUNCE = (SKIDOO_NORMAL_VELOCITY_MAX / 2) / 256;
#define SKIDOO_SLOW_VELOCITY 50
#define SKIDOO_MIN_VELOCITY 15
#define SKIDOO_ACCELERATION 10 constexpr auto SKIDOO_DAMAGE_START = 140;
#define SKIDOO_BRAKE 5 constexpr auto SKIDOO_DAMAGE_LENGTH = 14;
#define SKIDOO_SLOWDOWN 2
#define SKIDOO_REVERSE -5
#define SKIDOO_MAX_BACK -30
#define SKIDOO_MAX_KICK -80
#define SKIDOO_SLIP 100 #define SKIDOO_TURN_RATE_ACCEL ANGLE(2.5f)
#define SKIDOO_SLIP_SIDE 50 #define SKIDOO_TURN_RATE_DECEL ANGLE(2.0f)
#define SKIDOO_FRONT 550 #define SKIDOO_TURN_RATE_MAX ANGLE(6.0f)
#define SKIDOO_SIDE 260 #define SKIDOO_MOMENTUM_TURN_RATE_ACCEL ANGLE(3.0f)
#define SKIDOO_RADIUS 500 #define SKIDOO_MOMENTUM_TURN_RATE_MAX ANGLE(150.0f)
#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
enum SkidooState enum SkidooState
{ {
SKIDOO_STATE_SIT = 0, SKIDOO_STATE_DRIVE = 0,
SKIDOO_STATE_MOUNT = 1, SKIDOO_STATE_MOUNT = 1,
SKIDOO_STATE_LEFT = 2, SKIDOO_STATE_LEFT = 2,
SKIDOO_STATE_RIGHT = 3, SKIDOO_STATE_RIGHT = 3,
@ -111,62 +104,86 @@ namespace TEN::Entities::Vehicles
SKIDOO_ANIM_FALL_DEATH = 22 SKIDOO_ANIM_FALL_DEATH = 22
}; };
const vector<VehicleMountType> SkidooMountTypes =
{
VehicleMountType::LevelStart,
VehicleMountType::Left,
VehicleMountType::Right
};
SkidooInfo* GetSkidooInfo(ItemInfo* skidooItem)
{
return (SkidooInfo*)skidooItem->Data;
}
void InitialiseSkidoo(short itemNumber) void InitialiseSkidoo(short itemNumber)
{ {
auto* skidooItem = &g_Level.Items[itemNumber]; auto* skidooItem = &g_Level.Items[itemNumber];
skidooItem->Data = SkidooInfo(); skidooItem->Data = SkidooInfo();
auto* skidoo = (SkidooInfo*)skidooItem->Data; auto* skidoo = GetSkidooInfo(skidooItem);
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;
if (skidooItem->Status != ITEM_ACTIVE) if (skidooItem->Status != ITEM_ACTIVE)
{ {
AddActiveItem(itemNumber); AddActiveItem(itemNumber);
skidooItem->Status = ITEM_ACTIVE; 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) || auto mountType = GetVehicleMountType(skidooItem, laraItem, coll, SkidooMountTypes, SKIDOO_MOUNT_DISTANCE);
lara->Control.HandStatus != HandStatus::Free || if (mountType == VehicleMountType::None)
laraItem->Animation.Airborne) ObjectCollision(itemNumber, laraItem, coll);
{
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;
else 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) bool TestSkidooDismountOK(ItemInfo* skidooItem, int direction)
@ -177,10 +194,7 @@ namespace TEN::Entities::Vehicles
else else
angle = skidooItem->Pose.Orientation.y - ANGLE(90.0f); angle = skidooItem->Pose.Orientation.y - ANGLE(90.0f);
int x = skidooItem->Pose.Position.x - SKIDOO_DISMOUNT_DISTANCE * phd_sin(angle); auto probe = GetCollision(skidooItem, angle, -SKIDOO_DISMOUNT_DISTANCE);
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);
if ((probe.Position.FloorSlope || probe.Position.Floor == NO_HEIGHT) || if ((probe.Position.FloorSlope || probe.Position.Floor == NO_HEIGHT) ||
abs(probe.Position.Floor - skidooItem->Pose.Position.y) > CLICK(2) || abs(probe.Position.Floor - skidooItem->Pose.Position.y) > CLICK(2) ||
@ -193,9 +207,9 @@ namespace TEN::Entities::Vehicles
return true; 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) if (lara->Vehicle != NO_ITEM)
{ {
@ -208,12 +222,11 @@ namespace TEN::Entities::Vehicles
laraItem->Pose.Orientation.y -= ANGLE(90.0f); laraItem->Pose.Orientation.y -= ANGLE(90.0f);
SetAnimation(laraItem, LA_STAND_IDLE); SetAnimation(laraItem, LA_STAND_IDLE);
laraItem->Pose.Position.x -= SKIDOO_DISMOUNT_DISTANCE * phd_sin(laraItem->Pose.Orientation.y); TranslateItem(laraItem, laraItem->Pose.Orientation.y, -SKIDOO_DISMOUNT_DISTANCE);
laraItem->Pose.Position.z -= SKIDOO_DISMOUNT_DISTANCE * phd_cos(laraItem->Pose.Orientation.y);
laraItem->Pose.Orientation.x = 0; laraItem->Pose.Orientation.x = 0;
laraItem->Pose.Orientation.z = 0; laraItem->Pose.Orientation.z = 0;
lara->Vehicle = NO_ITEM;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
lara->Vehicle = NO_ITEM;
} }
else if (laraItem->Animation.ActiveState == SKIDOO_STATE_JUMP_OFF && else if (laraItem->Animation.ActiveState == SKIDOO_STATE_JUMP_OFF &&
(skidooItem->Pose.Position.y == skidooItem->Floor || TestLastFrame(laraItem))) (skidooItem->Pose.Position.y == skidooItem->Floor || TestLastFrame(laraItem)))
@ -224,7 +237,7 @@ namespace TEN::Entities::Vehicles
{ {
laraItem->Animation.TargetState = LS_DEATH; laraItem->Animation.TargetState = LS_DEATH;
laraItem->Animation.Velocity = 0; laraItem->Animation.Velocity = 0;
laraItem->Animation.VerticalVelocity = DAMAGE_START + DAMAGE_LENGTH; laraItem->Animation.VerticalVelocity = SKIDOO_DAMAGE_START + SKIDOO_DAMAGE_LENGTH;
ExplodeVehicle(laraItem, skidooItem); ExplodeVehicle(laraItem, skidooItem);
} }
else else
@ -238,11 +251,11 @@ namespace TEN::Entities::Vehicles
laraItem->Pose.Orientation.x = 0; laraItem->Pose.Orientation.x = 0;
laraItem->Pose.Orientation.z = 0; laraItem->Pose.Orientation.z = 0;
laraItem->Animation.Airborne = true; laraItem->Animation.IsAirborne = true;
lara->Control.HandStatus = HandStatus::Free;
lara->Control.MoveAngle = skidooItem->Pose.Orientation.y; lara->Control.MoveAngle = skidooItem->Pose.Orientation.y;
skidooItem->Flags |= ONESHOT; lara->Control.HandStatus = HandStatus::Free;
skidooItem->Collidable = false; skidooItem->Collidable = false;
skidooItem->Flags |= IFLAG_INVISIBLE;
return false; return false;
} }
@ -253,128 +266,16 @@ namespace TEN::Entities::Vehicles
return true; 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) bool SkidooControl(ItemInfo* laraItem, CollisionInfo* coll)
{ {
auto* lara = (LaraInfo*&)laraItem->Data; auto* lara = GetLaraInfo(laraItem);
auto* skidooItem = &g_Level.Items[lara->Vehicle]; auto* skidooItem = &g_Level.Items[lara->Vehicle];
auto* skidoo = (SkidooInfo*)skidooItem->Data; auto* skidoo = GetSkidooInfo(skidooItem);
Vector3Int frontLeft, frontRight; Vector3Int frontLeft, frontRight;
auto collide = SkidooDynamics(laraItem, skidooItem); auto collide = SkidooDynamics(skidooItem, laraItem);
auto heightFrontLeft = TestSkidooHeight(skidooItem, SKIDOO_FRONT, -SKIDOO_SIDE, &frontLeft); auto heightFrontLeft = GetVehicleHeight(skidooItem, SKIDOO_FRONT, -SKIDOO_SIDE, true, &frontLeft);
auto heightFrontRight = TestSkidooHeight(skidooItem, SKIDOO_FRONT, SKIDOO_SIDE, &frontRight); auto heightFrontRight = GetVehicleHeight(skidooItem, SKIDOO_FRONT, SKIDOO_SIDE, true, &frontRight);
auto probe = GetCollision(skidooItem); auto probe = GetCollision(skidooItem);
@ -398,7 +299,7 @@ namespace TEN::Entities::Vehicles
int height = probe.Position.Floor; int height = probe.Position.Floor;
int pitch = 0; int pitch = 0;
if (skidooItem->Flags & ONESHOT) if (skidooItem->Flags & IFLAG_INVISIBLE)
{ {
drive = 0; drive = 0;
collide = 0; collide = 0;
@ -417,8 +318,7 @@ namespace TEN::Entities::Vehicles
break; break;
default: default:
drive = SkidooUserControl(laraItem, skidooItem, height, &pitch); drive = SkidooUserControl(skidooItem, laraItem, height, &pitch);
break; break;
} }
} }
@ -429,7 +329,7 @@ namespace TEN::Entities::Vehicles
skidoo->TrackMesh = ((skidoo->TrackMesh & 3) == 1) ? 2 : 1; skidoo->TrackMesh = ((skidoo->TrackMesh & 3) == 1) ? 2 : 1;
skidoo->Pitch += (pitch - skidoo->Pitch) / 4; 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); SoundEffect(skidoo->Pitch ? SFX_TR2_VEHICLE_SNOWMOBILE_MOVING : SFX_TR2_VEHICLE_SNOWMOBILE_ACCELERATE, &skidooItem->Pose, SoundEnvironment::Land, pitch);
} }
else else
@ -450,10 +350,10 @@ namespace TEN::Entities::Vehicles
short xRot = phd_atan(SKIDOO_FRONT, skidooItem->Pose.Position.y - height); short xRot = phd_atan(SKIDOO_FRONT, skidooItem->Pose.Position.y - height);
short zRot = phd_atan(SKIDOO_SIDE, height - frontLeft.y); short zRot = phd_atan(SKIDOO_SIDE, height - frontLeft.y);
skidooItem->Pose.Orientation.x += ((xRot - skidooItem->Pose.Orientation.x) / 2); skidooItem->Pose.Orientation.x += (xRot - skidooItem->Pose.Orientation.x) / 2;
skidooItem->Pose.Orientation.z += ((zRot - skidooItem->Pose.Orientation.z) / 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) if (probe.RoomNumber != skidooItem->RoomNumber)
{ {
@ -466,10 +366,10 @@ namespace TEN::Entities::Vehicles
if (skidooItem->Pose.Position.y == skidooItem->Floor) if (skidooItem->Pose.Position.y == skidooItem->Floor)
ExplodeVehicle(laraItem, skidooItem); ExplodeVehicle(laraItem, skidooItem);
return 0; return false;
} }
SkidooAnimation(laraItem, skidooItem, collide, dead); SkidooAnimation(skidooItem, laraItem, collide, dead);
if (probe.RoomNumber != skidooItem->RoomNumber) if (probe.RoomNumber != skidooItem->RoomNumber)
{ {
@ -479,9 +379,7 @@ namespace TEN::Entities::Vehicles
if (laraItem->Animation.ActiveState != SKIDOO_STATE_FALLOFF) if (laraItem->Animation.ActiveState != SKIDOO_STATE_FALLOFF)
{ {
laraItem->Pose.Position.x = skidooItem->Pose.Position.x; laraItem->Pose.Position = skidooItem->Pose.Position;
laraItem->Pose.Position.y = skidooItem->Pose.Position.y;
laraItem->Pose.Position.z = skidooItem->Pose.Position.z;
laraItem->Pose.Orientation.y = skidooItem->Pose.Orientation.y; laraItem->Pose.Orientation.y = skidooItem->Pose.Orientation.y;
if (drive >= 0) if (drive >= 0)
@ -498,7 +396,7 @@ namespace TEN::Entities::Vehicles
AnimateItem(laraItem); AnimateItem(laraItem);
if (!dead && drive >= 0 && banditSkidoo) if (!dead && drive >= 0 && banditSkidoo)
SkidooGuns(laraItem, skidooItem); SkidooGuns(skidooItem, laraItem);
if (!dead) if (!dead)
{ {
@ -519,15 +417,15 @@ namespace TEN::Entities::Vehicles
DoSnowEffect(skidooItem); 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; int maxVelocity = 0;
bool drive = false;
if (skidooItem->Pose.Position.y >= (height - CLICK(1))) if (skidooItem->Pose.Position.y >= (height - CLICK(1)))
{ {
@ -536,66 +434,55 @@ namespace TEN::Entities::Vehicles
if (TrInput & IN_LOOK && skidooItem->Animation.Velocity == 0) if (TrInput & IN_LOOK && skidooItem->Animation.Velocity == 0)
LookUpDown(laraItem); LookUpDown(laraItem);
if ((TrInput & SKIDOO_IN_LEFT && !(TrInput & SKIDOO_IN_BRAKE)) || if (TrInput & (VEHICLE_IN_LEFT | VEHICLE_IN_RIGHT))
(TrInput & SKIDOO_IN_RIGHT && TrInput & SKIDOO_IN_BRAKE)) ModulateVehicleTurnRateY(&skidoo->TurnRate, SKIDOO_TURN_RATE_ACCEL, -SKIDOO_TURN_RATE_MAX, SKIDOO_TURN_RATE_MAX);
{
skidoo->TurnRate -= SKIDOO_TURN;
if (skidoo->TurnRate < -SKIDOO_MAX_TURN)
skidoo->TurnRate = -SKIDOO_MAX_TURN;
}
if ((TrInput & SKIDOO_IN_RIGHT && !(TrInput & SKIDOO_IN_BRAKE)) || if (TrInput & VEHICLE_IN_REVERSE)
(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 (skidooItem->Animation.Velocity > 0) if (skidooItem->Animation.Velocity > 0)
skidooItem->Animation.Velocity -= SKIDOO_BRAKE; skidooItem->Animation.Velocity -= SKIDOO_VELOCITY_BRAKE_DECEL;
else else
{ {
if (skidooItem->Animation.Velocity > SKIDOO_MAX_BACK) if (skidooItem->Animation.Velocity > -SKIDOO_REVERSE_VELOCITY_MAX)
skidooItem->Animation.Velocity += SKIDOO_REVERSE; skidooItem->Animation.Velocity -= SKIDOO_REVERSE_VELOCITY_ACCEL;
drive = true; drive = true;
} }
} }
else if (TrInput & SKIDOO_IN_ACCELERATE) else if (TrInput & VEHICLE_IN_ACCELERATE)
{ {
if (TrInput & SKIDOO_IN_FIRE && !skidoo->Armed) if (TrInput & VEHICLE_IN_SPEED)
maxVelocity = SKIDOO_FAST_VELOCITY; maxVelocity = SKIDOO_FAST_VELOCITY_MAX;
else if (TrInput & SKIDOO_IN_SLOW) else if (TrInput & VEHICLE_IN_SLOW)
maxVelocity = SKIDOO_SLOW_VELOCITY; maxVelocity = SKIDOO_SLOW_VELOCITY_MAX;
else else
maxVelocity = SKIDOO_MAX_VELOCITY; maxVelocity = SKIDOO_NORMAL_VELOCITY_MAX;
if (skidooItem->Animation.Velocity < maxVelocity) if (skidooItem->Animation.Velocity < maxVelocity)
skidooItem->Animation.Velocity += (SKIDOO_ACCELERATION / 2) + (SKIDOO_ACCELERATION * (skidooItem->Animation.Velocity / (2 * maxVelocity))); skidooItem->Animation.Velocity += (SKIDOO_VELOCITY_ACCEL / 2) + (SKIDOO_VELOCITY_ACCEL * (skidooItem->Animation.Velocity / (2 * maxVelocity)));
else if (skidooItem->Animation.Velocity > (maxVelocity + SKIDOO_SLOWDOWN)) else if (skidooItem->Animation.Velocity > (maxVelocity + SKIDOO_VELOCITY_DECEL))
skidooItem->Animation.Velocity -= SKIDOO_SLOWDOWN; skidooItem->Animation.Velocity -= SKIDOO_VELOCITY_DECEL;
drive = true; 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 >= 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; 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) if ((GetRandomControl() & 0x7f) < skidooItem->Animation.Velocity)
drive = true; drive = true;
} }
else else
skidooItem->Animation.Velocity = 0; 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; *pitch = skidoo->Pitch + 50;
drive = true; drive = true;
@ -604,13 +491,13 @@ namespace TEN::Entities::Vehicles
return drive; 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 && if (laraItem->Animation.ActiveState != SKIDOO_STATE_FALL &&
skidooItem->Pose.Position.y != skidooItem->Floor &&
skidooItem->Animation.VerticalVelocity > 0 && skidooItem->Animation.VerticalVelocity > 0 &&
skidooItem->Pose.Position.y != skidooItem->Floor &&
!dead) !dead)
{ {
laraItem->Animation.AnimNumber = Objects[ID_SNOWMOBILE_LARA_ANIMS].animIndex + SKIDOO_ANIM_LEAP_START; 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; 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)) TestSkidooDismountOK(skidooItem, SKIDOO_STATE_DISMOUNT_RIGHT))
{ {
laraItem->Animation.TargetState = SKIDOO_STATE_DISMOUNT_RIGHT; laraItem->Animation.TargetState = SKIDOO_STATE_DISMOUNT_RIGHT;
skidooItem->Animation.Velocity = 0; skidooItem->Animation.Velocity = 0;
} }
else if (TrInput & SKIDOO_IN_LEFT && else if (TrInput & VEHICLE_IN_LEFT &&
TestSkidooDismountOK(skidooItem, SKIDOO_STATE_DISMOUNT_LEFT)) TestSkidooDismountOK(skidooItem, SKIDOO_STATE_DISMOUNT_LEFT))
{ {
laraItem->Animation.TargetState = SKIDOO_STATE_DISMOUNT_LEFT; laraItem->Animation.TargetState = SKIDOO_STATE_DISMOUNT_LEFT;
skidooItem->Animation.Velocity = 0; skidooItem->Animation.Velocity = 0;
} }
} }
else if (TrInput & SKIDOO_IN_LEFT) else if (TrInput & VEHICLE_IN_LEFT)
laraItem->Animation.TargetState = SKIDOO_STATE_LEFT; {
else if (TrInput & SKIDOO_IN_RIGHT) if (skidooItem->Animation.Velocity >= 0)
laraItem->Animation.TargetState = SKIDOO_STATE_RIGHT; laraItem->Animation.TargetState = SKIDOO_STATE_LEFT;
else if (TrInput & (SKIDOO_IN_ACCELERATE | SKIDOO_IN_BRAKE)) else
laraItem->Animation.TargetState = SKIDOO_STATE_SIT; 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; break;
case SKIDOO_STATE_SIT: case SKIDOO_STATE_DRIVE:
if (skidooItem->Animation.Velocity == 0) if (skidooItem->Animation.Velocity == 0)
laraItem->Animation.TargetState = SKIDOO_STATE_IDLE; laraItem->Animation.TargetState = SKIDOO_STATE_IDLE;
if (dead) if (dead)
laraItem->Animation.TargetState = SKIDOO_STATE_FALLOFF; laraItem->Animation.TargetState = SKIDOO_STATE_FALLOFF;
else if (TrInput & SKIDOO_IN_LEFT) else if (TrInput & VEHICLE_IN_LEFT)
laraItem->Animation.TargetState = SKIDOO_STATE_LEFT; {
else if (TrInput & SKIDOO_IN_RIGHT) if (skidooItem->Animation.Velocity >= 0)
laraItem->Animation.TargetState = SKIDOO_STATE_RIGHT; 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; break;
case SKIDOO_STATE_LEFT: case SKIDOO_STATE_LEFT:
if (!(TrInput & SKIDOO_IN_LEFT)) if (skidooItem->Animation.Velocity >= 0)
laraItem->Animation.TargetState = SKIDOO_STATE_SIT; {
if (!(TrInput & VEHICLE_IN_LEFT))
laraItem->Animation.TargetState = SKIDOO_STATE_DRIVE;
}
else
{
if (!(TrInput & VEHICLE_IN_RIGHT))
laraItem->Animation.TargetState = SKIDOO_STATE_DRIVE;
}
break; break;
case SKIDOO_STATE_RIGHT: case SKIDOO_STATE_RIGHT:
if (!(TrInput & SKIDOO_IN_RIGHT)) if (skidooItem->Animation.Velocity >= 0)
laraItem->Animation.TargetState = SKIDOO_STATE_SIT; {
if (!(TrInput & VEHICLE_IN_RIGHT))
laraItem->Animation.TargetState = SKIDOO_STATE_DRIVE;
}
else
{
if (!(TrInput & VEHICLE_IN_LEFT))
laraItem->Animation.TargetState = SKIDOO_STATE_DRIVE;
}
break; break;
@ -701,10 +624,10 @@ namespace TEN::Entities::Vehicles
skidoo->LeftVerticalVelocity <= 0 || skidoo->LeftVerticalVelocity <= 0 ||
skidoo->RightVerticalVelocity <= 0) skidoo->RightVerticalVelocity <= 0)
{ {
laraItem->Animation.TargetState = SKIDOO_STATE_SIT; laraItem->Animation.TargetState = SKIDOO_STATE_DRIVE;
SoundEffect(SFX_TR2_VEHICLE_IMPACT3, &skidooItem->Pose); 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; laraItem->Animation.TargetState = SKIDOO_STATE_JUMP_OFF;
break; 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) int DoSkidooDynamics(int height, int verticalVelocity, int* y)
{ {
// Grounded.
if (height > *y) if (height > *y)
{ {
*y += verticalVelocity; *y += verticalVelocity;
@ -725,11 +720,12 @@ namespace TEN::Entities::Vehicles
else else
verticalVelocity += GRAVITY; verticalVelocity += GRAVITY;
} }
// Airborne.
else else
{ {
int kick = (height - *y) * 4; int kick = (height - *y) * 4;
if (kick < SKIDOO_MAX_KICK) if (kick < SKIDOO_KICK_MAX)
kick = SKIDOO_MAX_KICK; kick = SKIDOO_KICK_MAX;
verticalVelocity += (kick - verticalVelocity) / 8; verticalVelocity += (kick - verticalVelocity) / 8;
@ -740,26 +736,6 @@ namespace TEN::Entities::Vehicles
return verticalVelocity; 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) short DoSkidooShift(ItemInfo* skidooItem, Vector3Int* pos, Vector3Int* old)
{ {
int x = pos->x / SECTOR(1); int x = pos->x / SECTOR(1);
@ -860,62 +836,50 @@ namespace TEN::Entities::Vehicles
return 0; 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; Vector3Int frontLeftOld, frontRightOld, backLeftOld, backRightOld;
auto heightFrontLeftOld = TestSkidooHeight(skidooItem, SKIDOO_FRONT, -SKIDOO_SIDE, &frontLeftOld); auto heightFrontLeftOld = GetVehicleHeight(skidooItem, SKIDOO_FRONT, -SKIDOO_SIDE, true, &frontLeftOld);
auto heightFrontRightOld = TestSkidooHeight(skidooItem, SKIDOO_FRONT, SKIDOO_SIDE, &frontRightOld); auto heightFrontRightOld = GetVehicleHeight(skidooItem, SKIDOO_FRONT, SKIDOO_SIDE, true, &frontRightOld);
auto heightBackLeftOld = TestSkidooHeight(skidooItem, -SKIDOO_FRONT, -SKIDOO_SIDE, &backLeftOld); auto heightBackLeftOld = GetVehicleHeight(skidooItem, -SKIDOO_FRONT, -SKIDOO_SIDE, true, &backLeftOld);
auto heightBackRightOld = TestSkidooHeight(skidooItem, -SKIDOO_FRONT, SKIDOO_SIDE, &backRightOld); auto heightBackRightOld = GetVehicleHeight(skidooItem, -SKIDOO_FRONT, SKIDOO_SIDE, true, &backRightOld);
Vector3Int old; auto oldPos = skidooItem->Pose.Position;
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;
short rotation; short rotation;
if (skidooItem->Pose.Position.y > (skidooItem->Floor - CLICK(1))) if (skidooItem->Pose.Position.y > (skidooItem->Floor - CLICK(1)))
{ {
if (skidoo->TurnRate < -SKIDOO_UNDO_TURN) if (skidoo->TurnRate < -SKIDOO_TURN_RATE_DECEL)
skidoo->TurnRate += SKIDOO_UNDO_TURN; skidoo->TurnRate += SKIDOO_TURN_RATE_DECEL;
else if (skidoo->TurnRate > SKIDOO_UNDO_TURN) else if (skidoo->TurnRate > SKIDOO_TURN_RATE_DECEL)
skidoo->TurnRate -= SKIDOO_UNDO_TURN; skidoo->TurnRate -= SKIDOO_TURN_RATE_DECEL;
else else
skidoo->TurnRate = 0; skidoo->TurnRate = 0;
skidooItem->Pose.Orientation.y += skidoo->TurnRate + skidoo->ExtraRotation; skidooItem->Pose.Orientation.y += skidoo->TurnRate + skidoo->ExtraRotation;
rotation = skidooItem->Pose.Orientation.y - skidoo->MomentumAngle; 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; skidoo->MomentumAngle = skidooItem->Pose.Orientation.y - rotation;
} }
else 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; skidoo->MomentumAngle = skidooItem->Pose.Orientation.y - rotation;
} }
else else
skidoo->MomentumAngle += SKIDOO_MOMENTUM_TURN; skidoo->MomentumAngle += SKIDOO_MOMENTUM_TURN_RATE_ACCEL;
} }
else else
skidoo->MomentumAngle = skidooItem->Pose.Orientation.y; skidoo->MomentumAngle = skidooItem->Pose.Orientation.y;
@ -923,59 +887,58 @@ namespace TEN::Entities::Vehicles
else else
skidooItem->Pose.Orientation.y += skidoo->TurnRate + skidoo->ExtraRotation; skidooItem->Pose.Orientation.y += skidoo->TurnRate + skidoo->ExtraRotation;
skidooItem->Pose.Position.z += skidooItem->Animation.Velocity * phd_cos(skidoo->MomentumAngle); TranslateItem(skidooItem, skidoo->MomentumAngle, skidooItem->Animation.Velocity);
skidooItem->Pose.Position.x += skidooItem->Animation.Velocity * phd_sin(skidoo->MomentumAngle);
int slip = SKIDOO_SLIP * phd_sin(skidooItem->Pose.Orientation.x); int slip = SKIDOO_SLIP * phd_sin(skidooItem->Pose.Orientation.x);
if (abs(slip) > (SKIDOO_SLIP / 2)) 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.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); slip = SKIDOO_SLIP_SIDE * phd_sin(skidooItem->Pose.Orientation.z);
if (abs(slip) > (SKIDOO_SLIP_SIDE / 2)) 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.x += slip * phd_cos(skidooItem->Pose.Orientation.y);
skidooItem->Pose.Position.z -= slip * phd_sin(skidooItem->Pose.Orientation.y);
} }
Vector3Int moved; Vector3Int moved;
moved.x = skidooItem->Pose.Position.x; moved.x = skidooItem->Pose.Position.x;
moved.z = skidooItem->Pose.Position.z; moved.z = skidooItem->Pose.Position.z;
if (!(skidooItem->Flags & ONESHOT)) if (!(skidooItem->Flags & IFLAG_INVISIBLE))
DoVehicleCollision(skidooItem, SKIDOO_RADIUS); DoVehicleCollision(skidooItem, SKIDOO_RADIUS);
Vector3Int frontLeft, frontRight, backRight, backLeft; Vector3Int frontLeft, frontRight, backRight, backLeft;
rotation = 0; 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))) if (heightBackLeft < (backLeftOld.y - CLICK(1)))
rotation = DoSkidooShift(skidooItem, &backLeft, &backLeftOld); 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))) if (heightBackRight < (backRightOld.y - CLICK(1)))
rotation += DoSkidooShift(skidooItem, &backRight, &backRightOld); 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))) if (heightFrontLeft < (frontLeftOld.y - CLICK(1)))
rotation += DoSkidooShift(skidooItem, &frontLeft, &frontLeftOld); 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))) if (heightFrontRight < (frontRightOld.y - CLICK(1)))
rotation += DoSkidooShift(skidooItem, &frontRight, &frontRightOld); rotation += DoSkidooShift(skidooItem, &frontRight, &frontRightOld);
auto probe = GetCollision(skidooItem); auto probe = GetCollision(skidooItem);
if (probe.Position.Floor < (skidooItem->Pose.Position.y - CLICK(1))) 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; skidoo->ExtraRotation = rotation;
auto collide = GetSkidooCollisionAnim(skidooItem, &moved); auto collide = GetSkidooCollisionAnim(skidooItem, &moved);
if (collide) if (collide)
{ {
int newVelocity = (skidooItem->Pose.Position.z - old.z) * phd_cos(skidoo->MomentumAngle) + (skidooItem->Pose.Position.x - old.x) * phd_sin(skidoo->MomentumAngle); 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_MAX_VELOCITY + SKIDOO_ACCELERATION) && if (skidooItem->Animation.Velocity > (SKIDOO_NORMAL_VELOCITY_MAX + SKIDOO_VELOCITY_ACCEL) &&
newVelocity < (skidooItem->Animation.Velocity - 10)) newVelocity < (skidooItem->Animation.Velocity - 10))
{ {
DoDamage(laraItem, (skidooItem->Animation.Velocity - newVelocity) / 2); 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) else if (skidooItem->Animation.Velocity < 0 && newVelocity > skidooItem->Animation.Velocity)
skidooItem->Animation.Velocity = (newVelocity > 0) ? 0 : newVelocity; skidooItem->Animation.Velocity = (newVelocity > 0) ? 0 : newVelocity;
if (skidooItem->Animation.Velocity < SKIDOO_MAX_BACK) if (skidooItem->Animation.Velocity < SKIDOO_REVERSE_VELOCITY_MAX)
skidooItem->Animation.Velocity = SKIDOO_MAX_BACK; skidooItem->Animation.Velocity = SKIDOO_REVERSE_VELOCITY_MAX;
} }
return collide; return collide;
} }
void DrawSkidoo(ItemInfo* skidooItem)
{
}
} }

View file

@ -1,29 +1,28 @@
#pragma once #pragma once
#include "Game/collision/collide_room.h" #include "Game/collision/collide_room.h"
#include "Game/items.h" #include "Game/items.h"
#include "Objects/Utils/VehicleHelpers.h"
namespace TEN::Entities::Vehicles namespace TEN::Entities::Vehicles
{ {
SkidooInfo* GetSkidooInfo(ItemInfo* skidooItem);
void InitialiseSkidoo(short itemNumber); 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 TestSkidooDismountOK(ItemInfo* skidooItem, int direction);
bool TestSkidooDismount(ItemInfo* laraItem, ItemInfo* skidooItem); bool TestSkidooDismount(ItemInfo* skidooItem, ItemInfo* laraItem);
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 SkidooControl(ItemInfo* laraItem, CollisionInfo* coll); bool SkidooControl(ItemInfo* laraItem, CollisionInfo* coll);
bool SkidooUserControl(ItemInfo* laraItem, ItemInfo* skidooItem, int height, int* pitch); bool SkidooUserControl(ItemInfo* skidooItem, ItemInfo* laraItem, int height, int* pitch);
void SkidooAnimation(ItemInfo* laraItem, ItemInfo* skidooItem, int collide, bool dead); void SkidooAnimation(ItemInfo* skidooItem, ItemInfo* laraItem, int collide, bool dead);
int SkidooDynamics(ItemInfo* laraItem, ItemInfo* skidooItem); int GetSkidooCollisionAnim(ItemInfo* skidooItem, Vector3Int* moved);
int TestSkidooHeight(ItemInfo* skidooItem, int zOffset, int xOffset, Vector3Int* pos);
void SkidooGuns(ItemInfo* skidooItem, ItemInfo* laraItem);
void DoSnowEffect(ItemInfo* skidooItem);
int SkidooDynamics(ItemInfo* skidooItem, ItemInfo* laraItem);
short DoSkidooShift(ItemInfo* skidooItem, Vector3Int* pos, Vector3Int* old); short DoSkidooShift(ItemInfo* skidooItem, Vector3Int* pos, Vector3Int* old);
int DoSkidooDynamics(int height, int verticalVelocity, int* y); int DoSkidooDynamics(int height, int verticalVelocity, int* y);
void DrawSkidoo(ItemInfo* skidooItem);
} }

View file

@ -4,16 +4,16 @@ namespace TEN::Entities::Vehicles
{ {
struct SkidooInfo struct SkidooInfo
{ {
short TurnRate; int LeftVerticalVelocity = 0;
short MomentumAngle; int RightVerticalVelocity = 0;
short ExtraRotation;
int LeftVerticalVelocity; short TurnRate = 0;
int RightVerticalVelocity; short MomentumAngle = 0;
short ExtraRotation = 0;
int Pitch; int Pitch = 0;
int FlashTimer; int FlashTimer = 0;
short TrackMesh; short TrackMesh = 0;
bool Armed; bool Armed = false;
}; };
} }

View file

@ -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<VehicleMountType> 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);
}
}

View file

@ -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);
}

View file

@ -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;
};
}

View file

@ -26,7 +26,7 @@
#include "Objects/TR2/Trap/tr2_springboard.h" #include "Objects/TR2/Trap/tr2_springboard.h"
#include "Objects/TR2/Trap/tr2_killerstatue.h" #include "Objects/TR2/Trap/tr2_killerstatue.h"
/// vehicles /// vehicles
#include "Objects/TR2/Vehicles/boat.h" #include "Objects/TR2/Vehicles/speedboat.h"
#include "Objects/TR2/Vehicles/skidoo.h" #include "Objects/TR2/Vehicles/skidoo.h"
/// necessary import /// necessary import
#include "Game/control/box.h" #include "Game/control/box.h"
@ -662,9 +662,9 @@ static void StartVehicles(ObjectInfo* obj)
obj = &Objects[ID_SPEEDBOAT]; obj = &Objects[ID_SPEEDBOAT];
if (obj->loaded) if (obj->loaded)
{ {
obj->initialise = InitialiseSpeedBoat; obj->initialise = InitialiseSpeedboat;
obj->collision = SpeedBoatCollision; obj->collision = SpeedboatPlayerCollision;
obj->control = SpeedBoatControl; obj->control = SpeedboatControl;
obj->saveAnim = true; obj->saveAnim = true;
obj->saveFlags = true; obj->saveFlags = true;
obj->savePosition = true; obj->savePosition = true;
@ -676,7 +676,7 @@ static void StartVehicles(ObjectInfo* obj)
if (obj->loaded) if (obj->loaded)
{ {
obj->initialise = InitialiseSkidoo; obj->initialise = InitialiseSkidoo;
obj->collision = SkidooCollision; obj->collision = SkidooPlayerCollision;
//obj->drawRoutine = DrawSkidoo; // TODO: create a new render for the skidoo. (with track animated) //obj->drawRoutine = DrawSkidoo; // TODO: create a new render for the skidoo. (with track animated)
obj->saveAnim = true; obj->saveAnim = true;
obj->saveFlags = true; obj->saveFlags = true;

View file

@ -467,7 +467,7 @@ namespace TEN::Entities::TR3
LaraItem->Pose.Position = item->Pose.Position; LaraItem->Pose.Position = item->Pose.Position;
LaraItem->Pose.Orientation = Vector3Shrt(0, item->Pose.Orientation.y, 0); 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.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + LARA_ANIM_SHIVA_DEATH;
LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase; LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase;

View file

@ -101,7 +101,7 @@ namespace TEN::Entities::TR3
KillItem(itemNumber); KillItem(itemNumber);
DisableEntityAI(itemNumber); DisableEntityAI(itemNumber);
item->Flags |= ONESHOT; item->Flags |= IFLAG_INVISIBLE;
} }
void ControlLaserBolts(short itemNumber) void ControlLaserBolts(short itemNumber)

View file

@ -500,7 +500,7 @@ namespace TEN::Entities::TR3
KillItem(itemNumber); KillItem(itemNumber);
DisableEntityAI(itemNumber); DisableEntityAI(itemNumber);
item->Flags |= ONESHOT; item->Flags |= IFLAG_INVISIBLE;
} }
static bool TonyIsDying() static bool TonyIsDying()

View file

@ -57,7 +57,7 @@ namespace TEN::Entities::TR3
laraItem->Pose.Position = tRexItem->Pose.Position; laraItem->Pose.Position = tRexItem->Pose.Position;
laraItem->Pose.Orientation = Vector3Shrt(0, tRexItem->Pose.Orientation.y, 0); 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.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + LARA_ANIM_TREX_DEATH;
laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase;

View file

@ -114,7 +114,7 @@ void TrainCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
laraItem->Pose.Orientation.y = trainItem->Pose.Orientation.y; laraItem->Pose.Orientation.y = trainItem->Pose.Orientation.y;
laraItem->Animation.Velocity = 0; laraItem->Animation.Velocity = 0;
laraItem->Animation.VerticalVelocity = 0; laraItem->Animation.VerticalVelocity = 0;
laraItem->Animation.Airborne = false; laraItem->Animation.IsAirborne = false;
DoDamage(laraItem, INT_MAX); DoDamage(laraItem, INT_MAX);

View file

@ -13,36 +13,41 @@
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_struct.h" #include "Game/Lara/lara_struct.h"
#include "Objects/TR3/Vehicles/big_gun_info.h" #include "Objects/TR3/Vehicles/big_gun_info.h"
#include "Objects/Utils/VehicleHelpers.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/input.h" #include "Specific/input.h"
#include "Specific/setup.h" #include "Specific/setup.h"
using std::vector;
using namespace TEN::Input; using namespace TEN::Input;
namespace TEN::Entities::Vehicles namespace TEN::Entities::Vehicles
{ {
#define RECOIL_TIME 26 const vector<VehicleMountType> BigGunMountTypes =
#define RECOIL_Z 25 {
VehicleMountType::LevelStart,
VehicleMountType::Back
};
#define BGUN_NUM_UP_DOWN_FRAMES 59 constexpr auto BGUN_MOUNT_DISTANCE = CLICK(2);
#define BGUN_DISMOUNT_FRAME 30
#define BGUN_TURN_RATE ANGLE(2.0f) constexpr auto BGUN_RECOIL_TIME = 26;
#define BGUN_TURN_MAX ANGLE(16.0f) constexpr auto BGUN_RECOIL_Z = 25;
#define BGUN_IN_FIRE IN_ACTION constexpr auto BGUN_X_ORIENT_NUM_FRAMES = 59;
#define BGUN_IN_DISMOUNT (IN_ROLL | IN_JUMP) constexpr auto BGUN_X_ORIENT_MIDDLE_FRAME = 30;
#define BGUN_IN_UP IN_FORWARD
#define BGUN_IN_DOWN IN_BACK #define BGUN_TURN_RATE_ACCEL ANGLE(0.5f)
#define BGUN_IN_LEFT IN_LEFT #define BGUN_TURN_RATE_MAX ANGLE(4.0f)
#define BGUN_IN_RIGHT IN_RIGHT #define BGUN_X_ORIENT_STEP (ANGLE(80.0f) / BGUN_X_ORIENT_NUM_FRAMES)
#define BGUN_X_ORIENT_MAX ANGLE(40.0f)
enum BigGunState enum BigGunState
{ {
BGUN_STATE_MOUNT = 0, BGUN_STATE_MOUNT = 0,
BGUN_STATE_DISMOUNT = 1, BGUN_STATE_DISMOUNT = 1,
BGUN_STATE_UP_DOWN = 2, BGUN_STATE_ROTATE_VERTICALLY = 2,
BGUN_STATE_RECOIL = 3 BGUN_STATE_RECOIL = 3
}; };
@ -50,41 +55,41 @@ namespace TEN::Entities::Vehicles
{ {
BGUN_ANIM_MOUNT = 0, BGUN_ANIM_MOUNT = 0,
BGUN_ANIM_DISMOUNT = 1, BGUN_ANIM_DISMOUNT = 1,
BGUN_ANIM_UP_DOWN = 2, BGUN_ANIM_ROTATE_VERTICALLY = 2,
BGUN_ANIM_RECOIL = 3 BGUN_ANIM_RECOIL = 3
}; };
enum BigGunFlags enum BigGunFlags
{ {
BGUN_FLAG_UP_DOWN = 1, BGUN_FLAG_UP_DOWN = (1 << 0),
BGUN_FLAG_AUTO_ROT = 2, BGUN_FLAG_AUTO_ROT = (1 << 2),
BGUN_FLAG_DISMOUNT = 4, BGUN_FLAG_DISMOUNT = (1 << 3),
BGUN_FLAG_FIRE = 8 BGUN_FLAG_FIRE = (1 << 4)
}; };
BigGunInfo* GetBigGunInfo(ItemInfo* bigGunItem)
{
return (BigGunInfo*)bigGunItem->Data;
}
void BigGunInitialise(short itemNumber) void BigGunInitialise(short itemNumber)
{ {
auto* bigGunItem = &g_Level.Items[itemNumber]; auto* bigGunItem = &g_Level.Items[itemNumber];
bigGunItem->Data = BigGunInfo(); bigGunItem->Data = BigGunInfo();
auto* bigGun = (BigGunInfo*)bigGunItem->Data; auto* bigGun = GetBigGunInfo(bigGunItem);
bigGun->Rotation.x = BGUN_DISMOUNT_FRAME; bigGun->BaseOrientation = bigGunItem->Pose.Orientation;
bigGun->Rotation.z = 0; bigGun->XOrientFrame = BGUN_X_ORIENT_MIDDLE_FRAME;
bigGun->StartYRot = bigGunItem->Pose.Orientation.y;
bigGun->GunRotYAdd = 0;
bigGun->FireCount = 0;
bigGun->Flags = 0;
bigGun->BarrelRotating = false;
} }
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 // 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)*/; auto* lara = &Lara/* GetLaraInfo(laraItem)*/;
if (!(TrInput & IN_ACTION) || if (!(TrInput & IN_ACTION) ||
lara->Control.HandStatus != HandStatus::Free || lara->Control.HandStatus != HandStatus::Free ||
laraItem->Animation.Airborne) laraItem->Animation.IsAirborne)
{ {
return false; return false;
} }
@ -94,7 +99,7 @@ namespace TEN::Entities::Vehicles
int z = laraItem->Pose.Position.z - bigGunItem->Pose.Position.z; int z = laraItem->Pose.Position.z - bigGunItem->Pose.Position.z;
int distance = pow(x, 2) + pow(y, 2) + pow(z, 2); int distance = pow(x, 2) + pow(y, 2) + pow(z, 2);
if (distance > 30000) if (distance > SECTOR(30))
return false; return false;
short deltaAngle = abs(laraItem->Pose.Orientation.y - bigGunItem->Pose.Orientation.y); short deltaAngle = abs(laraItem->Pose.Orientation.y - bigGunItem->Pose.Orientation.y);
@ -104,9 +109,9 @@ namespace TEN::Entities::Vehicles
return true; 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(); short itemNumber = CreateItem();
auto* projectileItem = &g_Level.Items[itemNumber]; auto* projectileItem = &g_Level.Items[itemNumber];
@ -123,10 +128,12 @@ namespace TEN::Entities::Vehicles
InitialiseItem(itemNumber); 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->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; projectileItem->ItemFlags[0] = BGUN_FLAG_UP_DOWN;
AddActiveItem(itemNumber); 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* lara = GetLaraInfo(laraItem);
auto* bigGunItem = &g_Level.Items[itemNum];
auto* bigGun = (BigGunInfo*)bigGunItem->Data;
if (laraItem->HitPoints <= 0 || lara->Vehicle != NO_ITEM) if (laraItem->HitPoints <= 0 || lara->Vehicle != NO_ITEM)
return; return;
if (BigGunTestMount(bigGunItem, laraItem)) if (BigGunTestMount(laraItem, bigGunItem))
{ {
lara->Vehicle = itemNum; 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;
}
DoVehicleFlareDiscard(laraItem);
laraItem->Animation.AnimNumber = Objects[ID_BIGGUN_ANIMS].animIndex + BGUN_ANIM_MOUNT; 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.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.ActiveState = BGUN_STATE_MOUNT;
laraItem->Animation.TargetState = BGUN_STATE_MOUNT;
laraItem->Animation.IsAirborne = false;
laraItem->Pose = bigGunItem->Pose; laraItem->Pose = bigGunItem->Pose;
laraItem->Animation.Airborne = false;
lara->Control.HandStatus = HandStatus::Busy; lara->Control.HandStatus = HandStatus::Busy;
bigGunItem->HitPoints = 1; bigGunItem->HitPoints = 1;
bigGun->XOrientFrame = BGUN_X_ORIENT_MIDDLE_FRAME;
bigGun->Flags = 0; bigGun->Flags = 0;
bigGun->Rotation.x = BGUN_DISMOUNT_FRAME;
} }
else else
ObjectCollision(itemNum, laraItem, coll); ObjectCollision(itemNumber, laraItem, coll);
} }
bool BigGunControl(ItemInfo* laraItem, CollisionInfo* coll) bool BigGunControl(ItemInfo* laraItem, CollisionInfo* coll)
{ {
auto* lara = GetLaraInfo(laraItem); auto* lara = GetLaraInfo(laraItem);
auto* bigGunItem = &g_Level.Items[lara->Vehicle]; 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->Flags & BGUN_FLAG_UP_DOWN)
{ {
if (bigGun->BarrelRotating) if (bigGun->IsBarrelRotating)
bigGun->BarrelZRotation--; bigGun->BarrelRotation--;
if (!bigGun->BarrelZRotation) if (!bigGun->BarrelRotation)
bigGun->BarrelRotating = false; 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; bigGun->Flags = BGUN_FLAG_AUTO_ROT;
else else
{ {
if (TrInput & BGUN_IN_FIRE && bigGun->FireCount == 0) if (TrInput & VEHICLE_IN_FIRE && !bigGun->FireCount)
{ {
BigGunFire(laraItem, bigGunItem); BigGunFire(bigGunItem, laraItem);
bigGun->FireCount = RECOIL_TIME; bigGun->FireCount = BGUN_RECOIL_TIME;
bigGun->BarrelZRotation = RECOIL_Z; bigGun->BarrelRotation = BGUN_RECOIL_Z;
bigGun->BarrelRotating = true; bigGun->IsBarrelRotating = true;
} }
if (TrInput & BGUN_IN_LEFT) if (TrInput & VEHICLE_IN_UP)
{ {
if (bigGun->GunRotYAdd > 0) if (bigGun->TurnRate.x < 0)
bigGun->GunRotYAdd /= 2; bigGun->TurnRate.x /= 2;
bigGun->GunRotYAdd -= BGUN_TURN_RATE; bigGun->TurnRate.x += BGUN_TURN_RATE_ACCEL;
if (bigGun->GunRotYAdd < -BGUN_TURN_MAX) if (bigGun->TurnRate.x > (BGUN_TURN_RATE_MAX / 2))
bigGun->GunRotYAdd = -BGUN_TURN_MAX; bigGun->TurnRate.x = (BGUN_TURN_RATE_MAX / 2);
} }
else if (TrInput & BGUN_IN_RIGHT) else if (TrInput & VEHICLE_IN_DOWN)
{ {
if (bigGun->GunRotYAdd < 0) if (bigGun->TurnRate.x > 0)
bigGun->GunRotYAdd /= 2; bigGun->TurnRate.x /= 2;
bigGun->GunRotYAdd += BGUN_TURN_RATE; bigGun->TurnRate.x -= BGUN_TURN_RATE_ACCEL;
if (bigGun->GunRotYAdd > BGUN_TURN_MAX) if (bigGun->TurnRate.x < (-BGUN_TURN_RATE_MAX / 2))
bigGun->GunRotYAdd = BGUN_TURN_MAX; bigGun->TurnRate.x = (-BGUN_TURN_RATE_MAX / 2);
} }
else else
{ {
bigGun->GunRotYAdd -= bigGun->GunRotYAdd / 4; bigGun->TurnRate.x -= bigGun->TurnRate.x / 3;
if (abs(bigGun->GunRotYAdd) < BGUN_TURN_RATE) if (abs(bigGun->TurnRate.x) < BGUN_TURN_RATE_ACCEL)
bigGun->GunRotYAdd = 0; 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->TurnRate.y -= BGUN_TURN_RATE_ACCEL;
bigGun->Rotation.x++; if (bigGun->TurnRate.y < -BGUN_TURN_RATE_MAX)
else if (TrInput & BGUN_IN_DOWN && bigGun->Rotation.x) bigGun->TurnRate.y = -BGUN_TURN_RATE_MAX;
bigGun->Rotation.x--; }
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->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.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.FrameNumber = g_Level.Anims[Objects[ID_BIGGUN].animIndex + BGUN_ANIM_DISMOUNT].frameBase;
laraItem->Animation.ActiveState = BGUN_STATE_DISMOUNT; laraItem->Animation.ActiveState = BGUN_STATE_DISMOUNT;
laraItem->Animation.TargetState = BGUN_STATE_DISMOUNT; laraItem->Animation.TargetState = BGUN_STATE_DISMOUNT;
bigGun->GunRotYAdd = 0; bigGun->TurnRate.y = 0;
bigGun->BarrelRotating = false; bigGun->IsBarrelRotating = false;
bigGun->Flags = BGUN_FLAG_DISMOUNT; bigGun->Flags = BGUN_FLAG_DISMOUNT;
} }
else if (bigGun->Rotation.x > BGUN_DISMOUNT_FRAME) else if (bigGun->Rotation.x > 0)
bigGun->Rotation.x--; bigGun->Rotation.x -= BGUN_X_ORIENT_STEP;
else if (bigGun->Rotation.x < BGUN_DISMOUNT_FRAME) else if (bigGun->Rotation.x < 0)
bigGun->Rotation.x++; 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) switch (laraItem->Animation.ActiveState)
@ -276,27 +303,28 @@ namespace TEN::Entities::Vehicles
break; break;
case BGUN_STATE_UP_DOWN: case BGUN_STATE_ROTATE_VERTICALLY:
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;
bigGunItem->Animation.AnimNumber = Objects[ID_BIGGUN].animIndex + (laraItem->Animation.AnimNumber - Objects[ID_BIGGUN_ANIMS].animIndex); 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); bigGunItem->Animation.FrameNumber = g_Level.Anims[bigGunItem->Animation.AnimNumber].frameBase + (laraItem->Animation.FrameNumber - g_Level.Anims[laraItem->Animation.AnimNumber].frameBase);
if (bigGun->FireCount > 0) if (bigGun->FireCount > 0)
bigGun->FireCount--; bigGun->FireCount--;
else else
bigGun->FireCount = 0; bigGun->FireCount = 0;
bigGun->Flags = BGUN_FLAG_UP_DOWN; 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; 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; laraItem->Pose.Orientation.y = bigGunItem->Pose.Orientation.y;
coll->Setup.EnableSpasm = false;
coll->Setup.EnableObjectPush = false; coll->Setup.EnableObjectPush = false;
coll->Setup.EnableSpasm = false;
DoObjectCollision(laraItem, coll); DoObjectCollision(laraItem, coll);

View file

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "Objects/Utils/VehicleHelpers.h"
struct CollisionInfo; struct CollisionInfo;
struct ItemInfo; struct ItemInfo;
@ -6,8 +7,8 @@ struct ItemInfo;
namespace TEN::Entities::Vehicles namespace TEN::Entities::Vehicles
{ {
void BigGunInitialise(short itemNumber); void BigGunInitialise(short itemNumber);
static bool BigGunTestMount(ItemInfo* laraItem, ItemInfo* bigGunItem); static bool BigGunTestMount(ItemInfo* bigGunItem, ItemInfo* laraItem);
void BigGunFire(ItemInfo* laraItem, ItemInfo* bigGunItem); void BigGunFire(ItemInfo* bigGunItem, ItemInfo* laraItem);
void BigGunCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); void BigGunCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
bool BigGunControl(ItemInfo* laraItem, CollisionInfo* coll); bool BigGunControl(ItemInfo* laraItem, CollisionInfo* coll);
} }

View file

@ -4,14 +4,15 @@ namespace TEN::Entities::Vehicles
{ {
struct BigGunInfo struct BigGunInfo
{ {
Vector3Shrt Rotation; Vector3Shrt BaseOrientation = Vector3Shrt();
short BarrelZRotation; Vector3Shrt TurnRate = Vector3Shrt();
short StartYRot; Vector3Shrt Rotation = Vector3Shrt();
long GunRotYAdd; short BarrelRotation = 0;
int XOrientFrame = 0;
unsigned int FireCount; unsigned int FireCount = 0;
bool BarrelRotating; bool IsBarrelRotating = false;
char Flags; char Flags = NULL;
}; };
} }

View file

@ -12,60 +12,85 @@
#include "Game/Lara/lara_flare.h" #include "Game/Lara/lara_flare.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Objects/TR3/Vehicles/kayak_info.h" #include "Objects/TR3/Vehicles/kayak_info.h"
#include "Objects/Utils/VehicleHelpers.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/input.h" #include "Specific/input.h"
#include "Specific/setup.h" #include "Specific/setup.h"
using std::vector;
using namespace TEN::Input; using namespace TEN::Input;
namespace TEN::Entities::Vehicles namespace TEN::Entities::Vehicles
{ {
#define KAYAK_COLLIDE CLICK(0.25f) struct WAKE_PTS
#define DISMOUNT_DISTANCE CLICK(3) // TODO: Find accurate distance. {
#define KAYAK_TO_ENTITY_RADIUS CLICK(1) int x[2];
int y;
int z[2];
short xvel[2];
short zvel[2];
byte life;
byte pad[3];
};
static vector<int> KayakLaraLegJoints = { LM_HIPS, LM_LTHIGH, LM_LSHIN, LM_LFOOT, LM_RTHIGH, LM_RSHIN, LM_RFOOT };
static vector<VehicleMountType> KayakMountTypes =
{
VehicleMountType::LevelStart,
VehicleMountType::Left,
VehicleMountType::Right
};
#define MAX_VELOCITY 0x380000 constexpr auto KAYAK_TO_ENTITY_RADIUS = CLICK(1);
#define KAYAK_FRICTION 0x8000 constexpr auto KAYAK_COLLIDE = CLICK(0.25f);
#define KAYAK_ROTATE_FRICTION 0x50000 constexpr auto KAYAK_MOUNT_DISTANCE = CLICK(1.5f);
#define KAYAK_DEFLECT_ROTATION 0x80000 constexpr auto KAYAK_DISMOUNT_DISTANCE = CLICK(3); // TODO: Find accurate distance.
#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
#define HIT_BACK 1 constexpr int KAYAK_VELOCITY_FORWARD_ACCEL = 24 * VEHICLE_VELOCITY_SCALE;
#define HIT_FRONT 2 constexpr int KAYAK_VELOCITY_LR_ACCEL = 16 * VEHICLE_VELOCITY_SCALE;
#define HIT_LEFT 3 constexpr int KAYAK_VELOCITY_HOLD_TURN_DECEL = 0.5f * VEHICLE_VELOCITY_SCALE;
#define HIT_RIGHT 4 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_MOUNT_LEFT_FRAME GetFrameNumber(KAYAK_ANIM_MOUNT_RIGHT, 0)
#define KAYAK_IDLE_FRAME GetFrameNumber(KAYAK_ANIM_IDLE, 0) #define KAYAK_IDLE_FRAME GetFrameNumber(KAYAK_ANIM_IDLE, 0)
#define KAYAK_MOUNT_RIGHT_FRAME GetFrameNumber(KAYAK_ANIM_MOUNT_LEFT, 0) #define KAYAK_MOUNT_RIGHT_FRAME GetFrameNumber(KAYAK_ANIM_MOUNT_LEFT, 0)
#define KAYAK_DRAW_SHIFT 32 constexpr auto 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)) constexpr auto NUM_WAKE_SPRITES = 32;
#define NUM_WAKE_SPRITES 32 constexpr auto WAKE_SIZE = 32;
#define WAKE_SIZE 32 constexpr auto WAKE_VELOCITY = 4;
#define WAKE_VELOCITY 4 constexpr auto KAYAK_X = 128;
#define KAYAK_X 128 constexpr auto KAYAK_Z = 128;
#define KAYAK_Z 128 constexpr auto KAYAK_MAX_KICK = -80;
#define KAYAK_MAX_KICK -80 constexpr auto KAYAK_MIN_BOUNCE = (KAYAK_VELOCITY_MAX / 2) / VEHICLE_VELOCITY_SCALE;
#define KAYAK_MIN_BOUNCE ((MAX_VELOCITY / 2) / 256)
#define KAYAK_IN_FORWARD IN_FORWARD // TODO: Kayak control is fairly unique. Keep this? @Sezz 2022.06.25
#define KAYAK_IN_BACK IN_BACK constexpr auto KAYAK_IN_FORWARD = IN_FORWARD;
#define KAYAK_IN_LEFT IN_LEFT constexpr auto KAYAK_IN_BACK = IN_BACK;
#define KAYAK_IN_RIGHT IN_RIGHT constexpr auto KAYAK_IN_LEFT = IN_LEFT;
#define KAYAK_IN_HOLD IN_WALK constexpr auto KAYAK_IN_RIGHT = IN_RIGHT;
#define KAYAK_IN_HOLD_LEFT IN_LSTEP constexpr auto KAYAK_IN_HOLD = IN_WALK;
#define KAYAK_IN_HOLD_RIGHT IN_RSTEP constexpr auto KAYAK_IN_HOLD_LEFT = IN_LSTEP;
#define KAYAK_IN_DISMOUNT (IN_JUMP | IN_ROLL) constexpr auto KAYAK_IN_HOLD_RIGHT = IN_RSTEP;
WAKE_PTS WakePts[NUM_WAKE_SPRITES][2];
enum KayakState enum KayakState
{ {
@ -76,8 +101,8 @@ namespace TEN::Entities::Vehicles
KAYAK_STATE_MOUNT_LEFT = 4, KAYAK_STATE_MOUNT_LEFT = 4,
KAYAK_STATE_IDLE_DEATH = 5, KAYAK_STATE_IDLE_DEATH = 5,
KAYAK_STATE_FORWARD = 6, KAYAK_STATE_FORWARD = 6,
KAYAK_STATE_CAPSIZE_RECOVER = 7, // Unused. KAYAK_STATE_CAPSIZE_RECOVER = 7, // Unused.
KAYAK_STATE_CAPSIZE_DEATH = 8, // Unused. KAYAK_STATE_CAPSIZE_DEATH = 8, // Unused.
KAYAK_STATE_DISMOUNT = 9, KAYAK_STATE_DISMOUNT = 9,
KAYAK_STATE_HOLD_LEFT = 10, KAYAK_STATE_HOLD_LEFT = 10,
KAYAK_STATE_HOLD_RIGHT = 11, KAYAK_STATE_HOLD_RIGHT = 11,
@ -123,42 +148,18 @@ namespace TEN::Entities::Vehicles
KAYAK_ANIM_DISMOUNT_RIGHT = 32 KAYAK_ANIM_DISMOUNT_RIGHT = 32
}; };
enum class KayakMountType KayakInfo* GetKayakInfo(ItemInfo* kayakItem)
{ {
None, return (KayakInfo*)kayakItem->Data;
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];
void InitialiseKayak(short itemNumber) void InitialiseKayak(short itemNumber)
{ {
auto* kayakItem = &g_Level.Items[itemNumber]; auto* kayakItem = &g_Level.Items[itemNumber];
kayakItem->Data = KayakInfo(); kayakItem->Data = KayakInfo();
auto* kayak = (KayakInfo*)kayakItem->Data; auto* kayak = GetKayakInfo(kayakItem);
kayak->TurnRate = 0; kayak->OldPose = kayakItem->Pose;
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;
for (int i = 0; i < NUM_WAKE_SPRITES; i++) 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) void KayakDraw(ItemInfo* kayakItem)
{ {
DrawAnimatingItem(kayakItem); DrawAnimatingItem(kayakItem);
@ -174,19 +241,19 @@ namespace TEN::Entities::Vehicles
void KayakDoWake(ItemInfo* kayakItem, int xOffset, int zOffset, short rotate) 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) if (WakePts[kayak->CurrentStartWake][rotate].life)
return; return;
float s = phd_sin(kayakItem->Pose.Orientation.y); float sinY = phd_sin(kayakItem->Pose.Orientation.y);
float c = phd_cos(kayakItem->Pose.Orientation.y); float cosY = phd_cos(kayakItem->Pose.Orientation.y);
int x = kayakItem->Pose.Position.x + zOffset * s + xOffset * c; int x = kayakItem->Pose.Position.x + (zOffset * sinY) + (xOffset * cosY);
int z = kayakItem->Pose.Position.z + zOffset * c - xOffset * s; int z = kayakItem->Pose.Position.z + (zOffset * cosY) - (xOffset * sinY);
int probedRoomNum = GetCollision(x, kayakItem->Pose.Position.y, z, kayakItem->RoomNumber).RoomNumber; int probedRoomNumber = GetCollision(x, kayakItem->Pose.Position.y, z, kayakItem->RoomNumber).RoomNumber;
int waterHeight = GetWaterHeight(x, kayakItem->Pose.Position.y, z, probedRoomNum); int waterHeight = GetWaterHeight(x, kayakItem->Pose.Position.y, z, probedRoomNumber);
if (waterHeight != NO_HEIGHT) if (waterHeight != NO_HEIGHT)
{ {
@ -245,14 +312,14 @@ namespace TEN::Entities::Vehicles
void KayakDoRipple(ItemInfo* kayakItem, int xOffset, int zOffset) void KayakDoRipple(ItemInfo* kayakItem, int xOffset, int zOffset)
{ {
float s = phd_sin(kayakItem->Pose.Orientation.y); float sinY = phd_sin(kayakItem->Pose.Orientation.y);
float c = phd_cos(kayakItem->Pose.Orientation.y); float cosY = phd_cos(kayakItem->Pose.Orientation.y);
int x = kayakItem->Pose.Position.x + zOffset * s + xOffset * c; int x = kayakItem->Pose.Position.x + (zOffset * sinY) + (xOffset * cosY);
int z = kayakItem->Pose.Position.z + zOffset * c - xOffset * s; int z = kayakItem->Pose.Position.z + (zOffset * cosY) - (xOffset * sinY);
int probedRoomNum = GetCollision(x, kayakItem->Pose.Position.y, z, kayakItem->RoomNumber).RoomNumber; int probedRoomNumber = GetCollision(x, kayakItem->Pose.Position.y, z, kayakItem->RoomNumber).RoomNumber;
int waterHeight = GetWaterHeight(x, kayakItem->Pose.Position.y, z, probedRoomNum); int waterHeight = GetWaterHeight(x, kayakItem->Pose.Position.y, z, probedRoomNumber);
//if (waterHeight != NO_HEIGHT) //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)); // 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) int KayakGetCollisionAnim(ItemInfo* kayakItem, int xDiff, int zDiff)
{ {
xDiff = kayakItem->Pose.Position.x - xDiff; xDiff = kayakItem->Pose.Position.x - xDiff;
@ -378,10 +405,10 @@ namespace TEN::Entities::Vehicles
return verticalVelocity; return verticalVelocity;
} }
void KayakDoCurrent(ItemInfo* laraItem, ItemInfo* kayakItem) void KayakDoCurrent(ItemInfo* kayakItem, ItemInfo* laraItem)
{ {
auto* lara = GetLaraInfo(laraItem); auto* lara = GetLaraInfo(laraItem);
auto* room = &g_Level.Rooms[kayakItem->RoomNumber]; auto* room = &g_Level.Rooms[kayakItem->RoomNumber]; // Unused.
if (!lara->WaterCurrentActive) if (!lara->WaterCurrentActive)
{ {
@ -439,37 +466,10 @@ namespace TEN::Entities::Vehicles
lara->WaterCurrentActive = 0; lara->WaterCurrentActive = 0;
} }
int KayakTestHeight(ItemInfo* kayakItem, int x, int z, Vector3Int* pos) bool KayakCanGetOut(ItemInfo* kayakItem, int direction)
{
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)
{ {
Vector3Int pos; 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) if ((kayakItem->Pose.Position.y - height) > 0)
return false; return false;
@ -580,43 +580,37 @@ namespace TEN::Entities::Vehicles
return 0; 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]; Vector3Int oldPos[9];
int height[8]; int height[8];
height[0] = KayakTestHeight(kayakItem, 0, 1024, &oldPos[0]); height[0] = GetVehicleWaterHeight(kayakItem, 1024, 0, true, &oldPos[0]);
height[1] = KayakTestHeight(kayakItem, -96, 512, &oldPos[1]); height[1] = GetVehicleWaterHeight(kayakItem, 512, -96, true, &oldPos[1]);
height[2] = KayakTestHeight(kayakItem, 96, 512, &oldPos[2]); height[2] = GetVehicleWaterHeight(kayakItem, 512, 96, true, &oldPos[2]);
height[3] = KayakTestHeight(kayakItem, -128, 128, &oldPos[3]); height[3] = GetVehicleWaterHeight(kayakItem, 128, -128, true, &oldPos[3]);
height[4] = KayakTestHeight(kayakItem, 128, 128, &oldPos[4]); height[4] = GetVehicleWaterHeight(kayakItem, 128, 128, true, &oldPos[4]);
height[5] = KayakTestHeight(kayakItem, -128, -320, &oldPos[5]); height[5] = GetVehicleWaterHeight(kayakItem, -320, -128, true, &oldPos[5]);
height[6] = KayakTestHeight(kayakItem, 128, -320, &oldPos[6]); height[6] = GetVehicleWaterHeight(kayakItem, -320, 128, true, &oldPos[6]);
height[7] = KayakTestHeight(kayakItem, 0, -640, &oldPos[7]); height[7] = GetVehicleWaterHeight(kayakItem, -640, 0, true, &oldPos[7]);
for (int i = 0; i < 8; i++)
{
if (oldPos[i].y > height[i])
oldPos[i].y = height[i];
}
oldPos[8].x = kayakItem->Pose.Position.x; oldPos[8].x = kayakItem->Pose.Position.x;
oldPos[8].y = kayakItem->Pose.Position.y; oldPos[8].y = kayakItem->Pose.Position.y;
oldPos[8].z = kayakItem->Pose.Position.z; oldPos[8].z = kayakItem->Pose.Position.z;
Vector3Int frontPos, leftPos, rightPos; Vector3Int frontPos, leftPos, rightPos;
int frontHeight = KayakTestHeight(kayakItem, 0, 1024, &frontPos); int frontHeight = GetVehicleWaterHeight(kayakItem, 1024, 0, false, &frontPos);
int leftHeight = KayakTestHeight(kayakItem, -KAYAK_X, KAYAK_Z, &leftPos); int leftHeight = GetVehicleWaterHeight(kayakItem, KAYAK_Z, -KAYAK_X, false, &leftPos);
int rightHeight = KayakTestHeight(kayakItem, KAYAK_X, KAYAK_Z, &rightPos); 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.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.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->LeftVerticalVelocity = KayakDoDynamics(leftHeight, kayak->LeftVerticalVelocity, &leftPos.y);
kayak->RightVerticalVelocity = KayakDoDynamics(rightHeight, kayak->RightVerticalVelocity, &rightPos.y); kayak->RightVerticalVelocity = KayakDoDynamics(rightHeight, kayak->RightVerticalVelocity, &rightPos.y);
@ -637,28 +631,28 @@ namespace TEN::Entities::Vehicles
int rot = 0; int rot = 0;
Vector3Int pos; 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]); 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]); 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]); 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]); 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]); 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]); 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]); 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]); rot += KayakDoShift(kayakItem, &pos, &oldPos[0]);
kayakItem->Pose.Orientation.y += rot; kayakItem->Pose.Orientation.y += rot;
@ -683,9 +677,9 @@ namespace TEN::Entities::Vehicles
if (height2 == NO_HEIGHT) if (height2 == NO_HEIGHT)
{ {
GameVector kayakPos; GameVector kayakPos;
kayakPos.x = kayak->OldPos.Position.x; kayakPos.x = kayak->OldPose.Position.x;
kayakPos.y = kayak->OldPos.Position.y; kayakPos.y = kayak->OldPose.Position.y;
kayakPos.z = kayak->OldPos.Position.z; kayakPos.z = kayak->OldPose.Position.z;
kayakPos.roomNumber = kayakItem->RoomNumber; kayakPos.roomNumber = kayakItem->RoomNumber;
CameraCollisionBounds(&kayakPos, 256, 0); CameraCollisionBounds(&kayakPos, 256, 0);
@ -707,11 +701,11 @@ namespace TEN::Entities::Vehicles
int newVelocity; 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 = (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 (slip)
{ {
if (kayak->Velocity <= MAX_VELOCITY) if (kayak->Velocity <= KAYAK_VELOCITY_MAX)
kayak->Velocity = newVelocity; kayak->Velocity = newVelocity;
} }
else else
@ -723,15 +717,15 @@ namespace TEN::Entities::Vehicles
kayak->Velocity = newVelocity; kayak->Velocity = newVelocity;
} }
if (kayak->Velocity < -MAX_VELOCITY) if (kayak->Velocity < -KAYAK_VELOCITY_MAX)
kayak->Velocity = -MAX_VELOCITY; 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* lara = GetLaraInfo(laraItem);
auto* kayak = (KayakInfo*)kayakItem->Data;
if (laraItem->HitPoints <= 0 && if (laraItem->HitPoints <= 0 &&
laraItem->Animation.ActiveState != KAYAK_STATE_IDLE_DEATH) laraItem->Animation.ActiveState != KAYAK_STATE_IDLE_DEATH)
@ -746,7 +740,7 @@ namespace TEN::Entities::Vehicles
switch (laraItem->Animation.ActiveState) switch (laraItem->Animation.ActiveState)
{ {
case KAYAK_STATE_IDLE: case KAYAK_STATE_IDLE:
if (TrInput & KAYAK_IN_DISMOUNT && if (TrInput & VEHICLE_IN_DISMOUNT &&
!lara->WaterCurrentActive && !lara->WaterCurrentActive &&
!lara->WaterCurrentPull.x && !lara->WaterCurrentPull.z) !lara->WaterCurrentPull.x && !lara->WaterCurrentPull.z)
{ {
@ -811,20 +805,20 @@ namespace TEN::Entities::Vehicles
if (kayak->Forward) if (kayak->Forward)
{ {
if (!frame) if (!frame)
kayak->LeftRightCount = 0; kayak->LeftRightPaddleCount = 0;
// TODO: Sort out the bitwise operations. // TODO: Sort out the bitwise operations.
if (frame == 2 && !(kayak->LeftRightCount & 0x80)) if (frame == 2 && !(kayak->LeftRightPaddleCount & 0x80))
kayak->LeftRightCount++; kayak->LeftRightPaddleCount++;
else if (frame > 2) else if (frame > 2)
kayak->LeftRightCount &= ~0x80; kayak->LeftRightPaddleCount &= ~0x80;
if (TrInput & KAYAK_IN_FORWARD) if (TrInput & KAYAK_IN_FORWARD)
{ {
if (TrInput & KAYAK_IN_LEFT && !(TrInput & KAYAK_IN_HOLD)) 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; laraItem->Animation.TargetState = KAYAK_STATE_TURN_RIGHT;
} }
else else
@ -840,25 +834,25 @@ namespace TEN::Entities::Vehicles
{ {
if (kayak->Forward) if (kayak->Forward)
{ {
kayak->TurnRate -= KAYAK_FORWARD_ROTATION; kayak->TurnRate -= KAYAK_TURN_RATE_FORWARD_ACCEL;
if (kayak->TurnRate < -KAYAK_MAX_TURN) if (kayak->TurnRate < -KAYAK_TURN_RATE_MAX)
kayak->TurnRate = -KAYAK_MAX_TURN; kayak->TurnRate = -KAYAK_TURN_RATE_MAX;
kayak->Velocity += KAYAK_FORWARD_VELOCITY; kayak->Velocity += KAYAK_VELOCITY_FORWARD_ACCEL;
} }
else if (kayak->Turn) else if (kayak->Turn)
{ {
kayak->TurnRate -= KAYAK_HARD_ROTATION; kayak->TurnRate -= KAYAK_TURN_RATE_HOLD_ACCEL;
if (kayak->TurnRate < -KAYAK_MAX_STAT) if (kayak->TurnRate < -KAYAK_TURN_RATE_HOLD_MAX)
kayak->TurnRate = -KAYAK_MAX_STAT; kayak->TurnRate = -KAYAK_TURN_RATE_HOLD_MAX;
} }
else else
{ {
kayak->TurnRate -= KAYAK_LEFT_RIGHT_ROTATION; kayak->TurnRate -= KAYAK_TURN_RATE_LR_ACCEL;
if (kayak->TurnRate < -KAYAK_MAX_LEFT_RIGHT) if (kayak->TurnRate < -KAYAK_TURN_RATE_LR_MAX)
kayak->TurnRate = -KAYAK_MAX_LEFT_RIGHT; 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 (kayak->Forward)
{ {
if (!frame) if (!frame)
kayak->LeftRightCount = 0; kayak->LeftRightPaddleCount = 0;
if (frame == 2 && !(kayak->LeftRightCount & 0x80)) if (frame == 2 && !(kayak->LeftRightPaddleCount & 0x80))
kayak->LeftRightCount++; kayak->LeftRightPaddleCount++;
else if (frame > 2) else if (frame > 2)
kayak->LeftRightCount &= ~0x80; kayak->LeftRightPaddleCount &= ~0x80;
if (TrInput & KAYAK_IN_FORWARD) if (TrInput & KAYAK_IN_FORWARD)
{ {
if (TrInput & KAYAK_IN_RIGHT && !(TrInput & KAYAK_IN_HOLD)) 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; laraItem->Animation.TargetState = KAYAK_STATE_TURN_LEFT;
} }
else else
@ -900,25 +894,25 @@ namespace TEN::Entities::Vehicles
{ {
if (kayak->Forward) if (kayak->Forward)
{ {
kayak->TurnRate += KAYAK_FORWARD_ROTATION; kayak->TurnRate += KAYAK_TURN_RATE_FORWARD_ACCEL;
if (kayak->TurnRate > KAYAK_MAX_TURN) if (kayak->TurnRate > KAYAK_TURN_RATE_MAX)
kayak->TurnRate = KAYAK_MAX_TURN; kayak->TurnRate = KAYAK_TURN_RATE_MAX;
kayak->Velocity += KAYAK_FORWARD_VELOCITY; kayak->Velocity += KAYAK_VELOCITY_FORWARD_ACCEL;
} }
else if (kayak->Turn) else if (kayak->Turn)
{ {
kayak->TurnRate += KAYAK_HARD_ROTATION; kayak->TurnRate += KAYAK_TURN_RATE_HOLD_ACCEL;
if (kayak->TurnRate > KAYAK_MAX_STAT) if (kayak->TurnRate > KAYAK_TURN_RATE_HOLD_MAX)
kayak->TurnRate = KAYAK_MAX_STAT; kayak->TurnRate = KAYAK_TURN_RATE_HOLD_MAX;
} }
else else
{ {
kayak->TurnRate += KAYAK_LEFT_RIGHT_ROTATION; kayak->TurnRate += KAYAK_TURN_RATE_LR_ACCEL;
if (kayak->TurnRate > KAYAK_MAX_LEFT_RIGHT) if (kayak->TurnRate > KAYAK_TURN_RATE_LR_MAX)
kayak->TurnRate = KAYAK_MAX_LEFT_RIGHT; 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) if (frame == 8)
{ {
kayak->TurnRate += KAYAK_FORWARD_ROTATION; kayak->TurnRate += KAYAK_TURN_RATE_FORWARD_ACCEL;
kayak->Velocity -= KAYAK_FORWARD_VELOCITY; kayak->Velocity -= KAYAK_VELOCITY_FORWARD_ACCEL;
} }
if (frame == 31) if (frame == 31)
{ {
kayak->TurnRate -= KAYAK_FORWARD_ROTATION; kayak->TurnRate -= KAYAK_TURN_RATE_FORWARD_ACCEL;
kayak->Velocity -= KAYAK_FORWARD_VELOCITY; kayak->Velocity -= KAYAK_VELOCITY_FORWARD_ACCEL;
} }
if (frame < 15 && frame & 1) if (frame < 15 && frame & 1)
@ -967,10 +961,10 @@ namespace TEN::Entities::Vehicles
if (kayak->Velocity >= 0) if (kayak->Velocity >= 0)
{ {
kayak->TurnRate -= KAYAK_TURN_ROTATION; kayak->TurnRate -= KAYAK_TURN_ROTATION;
if (kayak->TurnRate < -KAYAK_MAX_TURN) if (kayak->TurnRate < -KAYAK_TURN_RATE_MAX)
kayak->TurnRate = -KAYAK_MAX_TURN; kayak->TurnRate = -KAYAK_TURN_RATE_MAX;
kayak->Velocity += -KAYAK_TURN_BRAKE; kayak->Velocity += -KAYAK_VELOCITY_HOLD_TURN_DECEL;
if (kayak->Velocity < 0) if (kayak->Velocity < 0)
kayak->Velocity = 0; kayak->Velocity = 0;
} }
@ -979,7 +973,7 @@ namespace TEN::Entities::Vehicles
{ {
kayak->TurnRate += KAYAK_TURN_ROTATION; kayak->TurnRate += KAYAK_TURN_ROTATION;
kayak->Velocity += KAYAK_TURN_BRAKE; kayak->Velocity += KAYAK_VELOCITY_HOLD_TURN_DECEL;
if (kayak->Velocity > 0) if (kayak->Velocity > 0)
kayak->Velocity = 0; kayak->Velocity = 0;
} }
@ -1003,10 +997,10 @@ namespace TEN::Entities::Vehicles
if (kayak->Velocity >= 0) if (kayak->Velocity >= 0)
{ {
kayak->TurnRate += KAYAK_TURN_ROTATION; kayak->TurnRate += KAYAK_TURN_ROTATION;
if (kayak->TurnRate > KAYAK_MAX_TURN) if (kayak->TurnRate > KAYAK_TURN_RATE_MAX)
kayak->TurnRate = KAYAK_MAX_TURN; kayak->TurnRate = KAYAK_TURN_RATE_MAX;
kayak->Velocity += -KAYAK_TURN_BRAKE; kayak->Velocity += -KAYAK_VELOCITY_HOLD_TURN_DECEL;
if (kayak->Velocity < 0) if (kayak->Velocity < 0)
kayak->Velocity = 0; kayak->Velocity = 0;
} }
@ -1015,7 +1009,7 @@ namespace TEN::Entities::Vehicles
{ {
kayak->TurnRate -= KAYAK_TURN_ROTATION; kayak->TurnRate -= KAYAK_TURN_ROTATION;
kayak->Velocity += KAYAK_TURN_BRAKE; kayak->Velocity += KAYAK_VELOCITY_HOLD_TURN_DECEL;
if (kayak->Velocity > 0) if (kayak->Velocity > 0)
kayak->Velocity = 0; kayak->Velocity = 0;
} }
@ -1033,7 +1027,7 @@ namespace TEN::Entities::Vehicles
{ {
kayak->Flags |= 0x80; kayak->Flags |= 0x80;
lara->MeshPtrs[LM_RHAND] = Objects[ID_KAYAK_LARA_ANIMS].meshIndex + LM_RHAND; lara->MeshPtrs[LM_RHAND] = Objects[ID_KAYAK_LARA_ANIMS].meshIndex + LM_RHAND;
laraItem->MeshBits &= ~LARA_LEG_BITS; laraItem->ClearBits(JointBitType::Mesh, KayakLaraLegJoints);
} }
break; break;
@ -1045,7 +1039,7 @@ namespace TEN::Entities::Vehicles
{ {
kayak->Flags &= ~0x80; kayak->Flags &= ~0x80;
lara->MeshPtrs[LM_RHAND] = Objects[ID_LARA_SKIN].meshIndex + LM_RHAND; 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; laraItem->Animation.TargetState = laraItem->Animation.RequiredState;
@ -1059,18 +1053,16 @@ namespace TEN::Entities::Vehicles
GetLaraJointPosition(&vec, LM_HIPS); GetLaraJointPosition(&vec, LM_HIPS);
SetAnimation(laraItem, LA_JUMP_FORWARD); SetAnimation(laraItem, LA_JUMP_FORWARD);
laraItem->Pose.Position.x = vec.x; laraItem->Pose.Position = vec;
laraItem->Pose.Position.y = vec.y;
laraItem->Pose.Position.z = vec.z;
laraItem->Pose.Orientation.x = 0; laraItem->Pose.Orientation.x = 0;
laraItem->Pose.Orientation.y = kayakItem->Pose.Orientation.y - ANGLE(90.0f); laraItem->Pose.Orientation.y = kayakItem->Pose.Orientation.y - ANGLE(90.0f);
laraItem->Pose.Orientation.z = 0; laraItem->Pose.Orientation.z = 0;
laraItem->Animation.Velocity = 40; laraItem->Animation.Velocity = 40;
laraItem->Animation.VerticalVelocity = -50; laraItem->Animation.VerticalVelocity = -50;
laraItem->Animation.Airborne = true; laraItem->Animation.IsAirborne = true;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
lara->Vehicle = NO_ITEM; lara->Vehicle = NO_ITEM;
kayak->LeftRightCount = 0; kayak->LeftRightPaddleCount = 0;
} }
break; break;
@ -1083,56 +1075,54 @@ namespace TEN::Entities::Vehicles
GetLaraJointPosition(&vec, LM_HIPS); GetLaraJointPosition(&vec, LM_HIPS);
SetAnimation(laraItem, LA_JUMP_FORWARD); SetAnimation(laraItem, LA_JUMP_FORWARD);
laraItem->Pose.Position.x = vec.x; laraItem->Pose.Position = vec;
laraItem->Pose.Position.y = vec.y;
laraItem->Pose.Position.z = vec.z;
laraItem->Pose.Orientation.x = 0; laraItem->Pose.Orientation.x = 0;
laraItem->Pose.Orientation.y = kayakItem->Pose.Orientation.y + ANGLE(90.0f); laraItem->Pose.Orientation.y = kayakItem->Pose.Orientation.y + ANGLE(90.0f);
laraItem->Pose.Orientation.z = 0; laraItem->Pose.Orientation.z = 0;
laraItem->Animation.IsAirborne = true;
laraItem->Animation.Velocity = 40; laraItem->Animation.Velocity = 40;
laraItem->Animation.VerticalVelocity = -50; laraItem->Animation.VerticalVelocity = -50;
laraItem->Animation.Airborne = true;
lara->Control.HandStatus = HandStatus::Free; lara->Control.HandStatus = HandStatus::Free;
lara->Vehicle = NO_ITEM; lara->Vehicle = NO_ITEM;
kayak->LeftRightCount = 0; kayak->LeftRightPaddleCount = 0;
} }
} }
if (kayak->Velocity > 0) if (kayak->Velocity > 0)
{ {
kayak->Velocity -= KAYAK_FRICTION; kayak->Velocity -= KAYAK_VELOCITY_FRICTION_DECEL;
if (kayak->Velocity < 0) if (kayak->Velocity < 0)
kayak->Velocity = 0; kayak->Velocity = 0;
} }
else if (kayak->Velocity < 0) else if (kayak->Velocity < 0)
{ {
kayak->Velocity += KAYAK_FRICTION; kayak->Velocity += KAYAK_VELOCITY_FRICTION_DECEL;
if (kayak->Velocity > 0) if (kayak->Velocity > 0)
kayak->Velocity = 0; kayak->Velocity = 0;
} }
if (kayak->Velocity > MAX_VELOCITY) if (kayak->Velocity > KAYAK_VELOCITY_MAX)
kayak->Velocity = MAX_VELOCITY; kayak->Velocity = KAYAK_VELOCITY_MAX;
else if (kayak->Velocity < -MAX_VELOCITY) else if (kayak->Velocity < -KAYAK_VELOCITY_MAX)
kayak->Velocity = -MAX_VELOCITY; 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) if (kayak->TurnRate >= 0)
{ {
kayak->TurnRate -= KAYAK_ROTATE_FRICTION; kayak->TurnRate -= KAYAK_TURN_RATE_FRICTION_DECEL;
if (kayak->TurnRate < 0) if (kayak->TurnRate < 0)
kayak->TurnRate = 0; kayak->TurnRate = 0;
} }
else if (kayak->TurnRate < 0) else if (kayak->TurnRate < 0)
{ {
kayak->TurnRate += KAYAK_ROTATE_FRICTION; kayak->TurnRate += KAYAK_TURN_RATE_FRICTION_DECEL;
if (kayak->TurnRate > 0) if (kayak->TurnRate > 0)
kayak->TurnRate = 0; kayak->TurnRate = 0;
} }
} }
void KayakToItemCollision(ItemInfo* laraItem, ItemInfo* kayakItem) void KayakToItemCollision(ItemInfo* kayakItem, ItemInfo* laraItem)
{ {
short roomsToCheck[128]; short roomsToCheck[128];
short numRoomsToCheck = 0; short numRoomsToCheck = 0;
@ -1198,87 +1188,32 @@ namespace TEN::Entities::Vehicles
laraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase; laraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase;
laraItem->Animation.ActiveState = 12; // TODO laraItem->Animation.ActiveState = 12; // TODO
laraItem->Animation.TargetState = 12; laraItem->Animation.TargetState = 12;
laraItem->HitPoints = -1; laraItem->Animation.IsAirborne = false;
laraItem->Animation.Velocity = 0; laraItem->Animation.Velocity = 0;
laraItem->Animation.VerticalVelocity = 0; laraItem->Animation.VerticalVelocity = 0;
laraItem->Animation.Airborne = false; laraItem->HitPoints = -1;
AnimateItem(laraItem); AnimateItem(laraItem);
lara->ExtraAnim = 1;
lara->Control.HandStatus = HandStatus::Busy; lara->Control.HandStatus = HandStatus::Busy;
lara->Control.Weapon.GunType = LaraWeaponType::None; lara->Control.Weapon.GunType = LaraWeaponType::None;
lara->ExtraAnim = 1;
lara->HitDirection = -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) bool KayakControl(ItemInfo* laraItem)
{ {
auto* lara = GetLaraInfo(laraItem); auto* lara = GetLaraInfo(laraItem);
auto* kayakItem = &g_Level.Items[lara->Vehicle]; auto* kayakItem = &g_Level.Items[lara->Vehicle];
auto* kayak = (KayakInfo*)kayakItem->Data; auto* kayak = GetKayakInfo(kayakItem);
if (TrInput & IN_LOOK) if (TrInput & IN_LOOK)
LookUpDown(laraItem); LookUpDown(laraItem);
int ofs = kayakItem->Animation.VerticalVelocity; int ofs = kayakItem->Animation.VerticalVelocity;
KayakUserInput(laraItem, kayakItem); KayakUserInput(kayakItem, laraItem);
KayakToBackground(laraItem, kayakItem); KayakToBackground(kayakItem, laraItem);
TestTriggers(kayakItem, false); TestTriggers(kayakItem, false);
auto probe = GetCollision(kayakItem); auto probe = GetCollision(kayakItem);
@ -1314,9 +1249,7 @@ namespace TEN::Entities::Vehicles
ItemNewRoom(lara->ItemNumber, probe.RoomNumber); ItemNewRoom(lara->ItemNumber, probe.RoomNumber);
} }
laraItem->Pose.Position.x = kayakItem->Pose.Position.x; laraItem->Pose.Position = kayakItem->Pose.Position;
laraItem->Pose.Position.y = kayakItem->Pose.Position.y;
laraItem->Pose.Position.z = kayakItem->Pose.Position.z;
laraItem->Pose.Orientation.x = kayakItem->Pose.Orientation.x; laraItem->Pose.Orientation.x = kayakItem->Pose.Orientation.x;
laraItem->Pose.Orientation.y = kayakItem->Pose.Orientation.y; laraItem->Pose.Orientation.y = kayakItem->Pose.Orientation.y;
laraItem->Pose.Orientation.z = kayakItem->Pose.Orientation.z / 2; laraItem->Pose.Orientation.z = kayakItem->Pose.Orientation.z / 2;
@ -1373,7 +1306,7 @@ namespace TEN::Entities::Vehicles
} }
KayakUpdateWakeFX(); KayakUpdateWakeFX();
KayakToItemCollision(laraItem, kayakItem); KayakToItemCollision(kayakItem, laraItem);
return (lara->Vehicle != NO_ITEM) ? true : false; return (lara->Vehicle != NO_ITEM) ? true : false;
} }

View file

@ -1,12 +1,16 @@
#pragma once #pragma once
#include "Objects/Utils/VehicleHelpers.h"
struct ItemInfo;
struct CollisionInfo; struct CollisionInfo;
struct Vector3Int; struct ItemInfo;
namespace TEN::Entities::Vehicles namespace TEN::Entities::Vehicles
{ {
void InitialiseKayak(short itemNumber); 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 KayakDraw(ItemInfo* kayakItem);
void KayakDoWake(ItemInfo* kayakItem, int xOffset, int zOffset, short rotate); 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 KayakGetCollisionAnim(ItemInfo* kayakItem, int xDiff, int zDiff);
int KayakDoDynamics(int height, int verticalVelocity, int* y); int KayakDoDynamics(int height, int verticalVelocity, int* y);
void KayakDoCurrent(ItemInfo* laraItem, ItemInfo* kayakItem); void KayakDoCurrent(ItemInfo* kayakItem, ItemInfo* laraItem);
int KayakTestHeight(ItemInfo* kayakItem, int x, int z, Vector3Int* pos);
bool KayakCanGetOut(ItemInfo* kayakItem, int dir); bool KayakCanGetOut(ItemInfo* kayakItem, int dir);
int KayakDoShift(ItemInfo* kayakItem, Vector3Int* pos, Vector3Int* old); int KayakDoShift(ItemInfo* kayakItem, Vector3Int* pos, Vector3Int* old);
void KayakToBackground(ItemInfo* laraItem, ItemInfo* kayakItem); void KayakToBackground(ItemInfo* kayakItem, ItemInfo* laraItem);
void KayakUserInput(ItemInfo* laraItem, ItemInfo* kayakItem); void KayakUserInput(ItemInfo* kayakItem, ItemInfo* laraItem);
void KayakToItemCollision(ItemInfo* laraItem, ItemInfo* kayakItem); void KayakToItemCollision(ItemInfo* kayakItem, ItemInfo* laraItem);
void KayakLaraRapidsDrown(ItemInfo* laraItem); void KayakLaraRapidsDrown(ItemInfo* laraItem);
void KayakCollision(short itemNum, ItemInfo* laraItem, CollisionInfo* coll);
bool KayakControl(ItemInfo* laraItem); bool KayakControl(ItemInfo* laraItem);
} }

View file

@ -5,23 +5,23 @@ namespace TEN::Entities::Vehicles
{ {
struct KayakInfo struct KayakInfo
{ {
int TurnRate; int TurnRate = 0;
int Velocity; int Velocity = 0;
int FrontVerticalVelocity; int FrontVerticalVelocity = 0;
int LeftVerticalVelocity; int LeftVerticalVelocity = 0;
int RightVerticalVelocity; int RightVerticalVelocity = 0;
unsigned int LeftRightCount; PHD_3DPOS OldPose = PHD_3DPOS();
int WaterHeight; unsigned int LeftRightPaddleCount = 0;
PHD_3DPOS OldPos; int WaterHeight = 0;
bool Turn; bool Turn = false;
bool Forward; bool Forward = false;
bool TrueWater; bool TrueWater = false;
int CurrentStartWake; int CurrentStartWake = 0;
int WakeShade; int WakeShade = 0;
char Flags; char Flags = NULL;
}; };
} }

View file

@ -12,46 +12,56 @@
#include "Game/Lara/lara_flare.h" #include "Game/Lara/lara_flare.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Objects/TR3/Vehicles/minecart_info.h" #include "Objects/TR3/Vehicles/minecart_info.h"
#include "Objects/Utils/VehicleHelpers.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/input.h" #include "Specific/input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h" #include "Specific/setup.h"
#include "Specific/prng.h" #include "Specific/prng.h"
using namespace TEN::Input;
using namespace TEN::Effects::Spark; using namespace TEN::Effects::Spark;
using namespace TEN::Input;
using namespace TEN::Math::Random; using namespace TEN::Math::Random;
using std::vector;
namespace TEN::Entities::Vehicles namespace TEN::Entities::Vehicles
{ {
const vector<VehicleMountType> MinecartMountTypes =
{
VehicleMountType::LevelStart,
VehicleMountType::Left,
VehicleMountType::Right
};
constexpr auto MINECART_RADIUS = 100; constexpr auto MINECART_RADIUS = 100;
constexpr auto MINECART_ENTITY_RADIUS = CLICK(1); constexpr auto MINECART_ENTITY_RADIUS = CLICK(1);
constexpr auto MINECART_HEIGHT = CLICK(2); constexpr auto MINECART_HEIGHT = CLICK(2);
constexpr auto MINECART_GRAVITY = SECTOR(1) + 1; 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_DISMOUNT_DISTANCE = 330;
constexpr auto MINECART_NUM_HITS = 25; constexpr auto MINECART_NUM_HITS = 25;
constexpr auto MINECART_SPEED_MIN = 10 * 256; // TODO: These two have confusing names. @Sezz constexpr auto MINECART_VELOCITY_DECEL = 6 * VEHICLE_VELOCITY_SCALE;
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_FORWARD_GRADIENT = -128; constexpr auto MINECART_SPEED_MIN = 10 * VEHICLE_VELOCITY_SCALE; // TODO: These two have confusing names. @Sezz
constexpr auto MINECART_BACK_GRADIENT = 128; 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; constexpr auto MINECART_WRENCH_MESH_TOGGLE_FRAME = 20;
#define MINECART_TERMINAL_ANGLE ANGLE(22.0f) #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_DUCK IN_CROUCH #define MINECART_IN_SWIPE (IN_ACTION | IN_DRAW)
#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
int Wheels[4] = { 2, 3, 1, 4 }; int Wheels[4] = { 2, 3, 1, 4 };
@ -144,7 +154,7 @@ namespace TEN::Entities::Vehicles
MINECART_FLAG_DEAD = (1 << 7) MINECART_FLAG_DEAD = (1 << 7)
}; };
static MinecartInfo* GetMinecartInfo(ItemInfo* minecartItem) MinecartInfo* GetMinecartInfo(ItemInfo* minecartItem)
{ {
return (MinecartInfo*)minecartItem->Data; return (MinecartInfo*)minecartItem->Data;
} }
@ -154,20 +164,75 @@ namespace TEN::Entities::Vehicles
auto* minecartItem = &g_Level.Items[itemNumber]; auto* minecartItem = &g_Level.Items[itemNumber];
minecartItem->Data = MinecartInfo(); minecartItem->Data = MinecartInfo();
auto* minecart = GetMinecartInfo(minecartItem); 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->Velocity = 0;
minecart->VerticalVelocity = 0; minecart->VerticalVelocity = 0;
minecart->Gradient = 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++) for (int i = 0; i < 2; i++)
{ {
auto pos = Vector3Int{}; auto pos = Vector3Int();
GetJointAbsPosition(item, &pos, Wheels[(left ? 0 : 2) + i]); GetJointAbsPosition(minecartItem, &pos, Wheels[(left ? 0 : 2) + i]);
TriggerFrictionSpark(&GameVector(pos.x, pos.y, pos.z, item->RoomNumber), item->Pose.Orientation, 512, 10); TriggerFrictionSpark(&GameVector(pos.x, pos.y, pos.z, minecartItem->RoomNumber), minecartItem->Pose.Orientation, 512, 10);
if (i) 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) static short GetMinecartCollision(ItemInfo* minecartItem, short angle, int distance)
{ {
auto probe = GetCollision(minecartItem, angle, distance, -LARA_HEIGHT); auto probe = GetCollision(minecartItem, angle, distance, -LARA_HEIGHT);
@ -204,41 +254,6 @@ namespace TEN::Entities::Vehicles
return (short)probe.Position.Floor; 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) static bool TestMinecartDismount(ItemInfo* laraItem, int direction)
{ {
auto* lara = GetLaraInfo(laraItem); auto* lara = GetLaraInfo(laraItem);
@ -267,7 +282,7 @@ namespace TEN::Entities::Vehicles
return true; return true;
} }
static void MinecartToEntityCollision(ItemInfo* laraItem, ItemInfo* minecartItem) static void MinecartToEntityCollision(ItemInfo* minecartItem, ItemInfo* laraItem)
{ {
auto roomsList = GetRoomList(minecartItem->RoomNumber); 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* minecart = GetMinecartInfo(minecartItem);
auto* lara = GetLaraInfo(laraItem);
auto flags = GetCollision(minecartItem).BottomBlock->Flags; auto flags = GetCollision(minecartItem).BottomBlock->Flags;
if (minecart->StopDelay) if (minecart->StopDelay)
@ -348,7 +364,7 @@ namespace TEN::Entities::Vehicles
if ((flags.MinecartStop() && !minecart->StopDelay) && if ((flags.MinecartStop() && !minecart->StopDelay) &&
((minecartItem->Pose.Position.x & 0x380) == 512 || (minecartItem->Pose.Position.z & 0x380) == 512)) ((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; minecartItem->Animation.Velocity = 0;
minecart->Velocity = 0; minecart->Velocity = 0;
@ -434,7 +450,7 @@ namespace TEN::Entities::Vehicles
minecart->Velocity = MINECART_SPEED_MIN; minecart->Velocity = MINECART_SPEED_MIN;
minecart->Velocity -= minecart->Gradient * 4; 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) 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.x = minecart->TurnX + x * 3584;
minecartItem->Pose.Position.z = minecart->TurnZ + z * 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); SoundEffect(SFX_TR3_VEHICLE_MINECART_BRAKE, &minecartItem->Pose, SoundEnvironment::Always);
TriggerWheelSparkles(minecartItem, (minecart->Flags & MINECART_FLAG_TURNING_RIGHT) != 0); TriggerWheelSparkles(minecartItem, (minecart->Flags & MINECART_FLAG_TURNING_RIGHT) != 0);
@ -525,12 +541,12 @@ namespace TEN::Entities::Vehicles
else else
TranslateItem(minecartItem, minecartItem->Pose.Orientation.y, minecartItem->Animation.Velocity); 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) if (!minecart->VerticalVelocity)
{ {
minecartItem->Pose.Position.y = minecart->FloorHeightMiddle; 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; minecart->Gradient = minecart->FloorHeightMiddle - minecart->FloorHeightFront;
} }
else else
@ -549,7 +565,7 @@ namespace TEN::Entities::Vehicles
if (minecart->VerticalVelocity > MINECART_VERTICAL_VELOCITY_MAX) if (minecart->VerticalVelocity > MINECART_VERTICAL_VELOCITY_MAX)
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; 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); auto* lara = GetLaraInfo(laraItem);
short floorHeight; short floorHeight;
@ -580,7 +597,7 @@ namespace TEN::Entities::Vehicles
laraItem->Animation.TargetState = MINECART_STATE_SWIPE; laraItem->Animation.TargetState = MINECART_STATE_SWIPE;
else if (TrInput & MINECART_IN_DUCK) else if (TrInput & MINECART_IN_DUCK)
laraItem->Animation.TargetState = MINECART_STATE_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; laraItem->Animation.TargetState = MINECART_STATE_BRAKE;
else if (minecart->Velocity == MINECART_VELOCITY_MIN || minecart->Flags & MINECART_FLAG_STOPPED) else if (minecart->Velocity == MINECART_VELOCITY_MIN || minecart->Flags & MINECART_FLAG_STOPPED)
laraItem->Animation.TargetState = MINECART_STATE_IDLE; laraItem->Animation.TargetState = MINECART_STATE_IDLE;
@ -588,9 +605,9 @@ namespace TEN::Entities::Vehicles
laraItem->Animation.TargetState = MINECART_STATE_FORWARD; laraItem->Animation.TargetState = MINECART_STATE_FORWARD;
else if (minecart->Gradient > MINECART_BACK_GRADIENT) else if (minecart->Gradient > MINECART_BACK_GRADIENT)
laraItem->Animation.TargetState = MINECART_STATE_BACK; laraItem->Animation.TargetState = MINECART_STATE_BACK;
else if (TrInput & MINECART_IN_LEFT) else if (TrInput & VEHICLE_IN_LEFT)
laraItem->Animation.TargetState = MINECART_STATE_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; laraItem->Animation.TargetState = MINECART_STATE_RIGHT;
break; break;
@ -600,7 +617,7 @@ namespace TEN::Entities::Vehicles
laraItem->Animation.TargetState = MINECART_STATE_SWIPE; laraItem->Animation.TargetState = MINECART_STATE_SWIPE;
else if (TrInput & MINECART_IN_DUCK) else if (TrInput & MINECART_IN_DUCK)
laraItem->Animation.TargetState = MINECART_STATE_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; laraItem->Animation.TargetState = MINECART_STATE_BRAKE;
else if (minecart->Gradient > MINECART_FORWARD_GRADIENT) else if (minecart->Gradient > MINECART_FORWARD_GRADIENT)
laraItem->Animation.TargetState = MINECART_STATE_MOVE; laraItem->Animation.TargetState = MINECART_STATE_MOVE;
@ -612,7 +629,7 @@ namespace TEN::Entities::Vehicles
laraItem->Animation.TargetState = MINECART_STATE_SWIPE; laraItem->Animation.TargetState = MINECART_STATE_SWIPE;
else if (TrInput & MINECART_IN_DUCK) else if (TrInput & MINECART_IN_DUCK)
laraItem->Animation.TargetState = MINECART_STATE_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; laraItem->Animation.TargetState = MINECART_STATE_BRAKE;
else if (minecart->Gradient < MINECART_BACK_GRADIENT) else if (minecart->Gradient < MINECART_BACK_GRADIENT)
laraItem->Animation.TargetState = MINECART_STATE_MOVE; laraItem->Animation.TargetState = MINECART_STATE_MOVE;
@ -624,10 +641,10 @@ namespace TEN::Entities::Vehicles
laraItem->Animation.TargetState = MINECART_STATE_SWIPE; laraItem->Animation.TargetState = MINECART_STATE_SWIPE;
else if (TrInput & MINECART_IN_DUCK) else if (TrInput & MINECART_IN_DUCK)
laraItem->Animation.TargetState = MINECART_STATE_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; laraItem->Animation.TargetState = MINECART_STATE_BRAKE;
if (!(TrInput & MINECART_IN_LEFT)) if (!(TrInput & VEHICLE_IN_LEFT))
laraItem->Animation.TargetState = MINECART_STATE_MOVE; laraItem->Animation.TargetState = MINECART_STATE_MOVE;
break; break;
@ -637,10 +654,10 @@ namespace TEN::Entities::Vehicles
laraItem->Animation.TargetState = MINECART_STATE_SWIPE; laraItem->Animation.TargetState = MINECART_STATE_SWIPE;
else if (TrInput & MINECART_IN_DUCK) else if (TrInput & MINECART_IN_DUCK)
laraItem->Animation.TargetState = MINECART_STATE_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; laraItem->Animation.TargetState = MINECART_STATE_BRAKE;
if (!(TrInput & MINECART_IN_RIGHT)) if (!(TrInput & VEHICLE_IN_RIGHT))
laraItem->Animation.TargetState = MINECART_STATE_MOVE; laraItem->Animation.TargetState = MINECART_STATE_MOVE;
break; break;
@ -653,14 +670,14 @@ namespace TEN::Entities::Vehicles
minecart->StopDelay = 64; 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; laraItem->Animation.TargetState = MINECART_STATE_DISMOUNT;
minecart->Flags &= ~MINECART_FLAG_DISMOUNT_RIGHT; 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; laraItem->Animation.TargetState = MINECART_STATE_DISMOUNT;
minecart->Flags |= MINECART_FLAG_DISMOUNT_RIGHT; minecart->Flags |= MINECART_FLAG_DISMOUNT_RIGHT;
@ -680,7 +697,7 @@ namespace TEN::Entities::Vehicles
case MINECART_STATE_DUCK: case MINECART_STATE_DUCK:
if (TrInput & MINECART_IN_SWIPE) if (TrInput & MINECART_IN_SWIPE)
laraItem->Animation.TargetState = MINECART_STATE_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; laraItem->Animation.TargetState = MINECART_STATE_BRAKE;
else if (!(TrInput & MINECART_IN_DUCK)) else if (!(TrInput & MINECART_IN_DUCK))
laraItem->Animation.TargetState = MINECART_STATE_IDLE; laraItem->Animation.TargetState = MINECART_STATE_IDLE;
@ -697,17 +714,17 @@ namespace TEN::Entities::Vehicles
laraItem->Animation.TargetState = MINECART_STATE_DUCK; laraItem->Animation.TargetState = MINECART_STATE_DUCK;
StopSoundEffect(SFX_TR3_VEHICLE_MINECART_BRAKE); 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; laraItem->Animation.TargetState = MINECART_STATE_MOVE;
StopSoundEffect(SFX_TR3_VEHICLE_MINECART_BRAKE); StopSoundEffect(SFX_TR3_VEHICLE_MINECART_BRAKE);
} }
else else
{ {
minecart->Velocity -= MINECART_DECELERATION; minecart->Velocity -= MINECART_VELOCITY_DECEL;
SoundEffect(SFX_TR3_VEHICLE_MINECART_BRAKE, &laraItem->Pose, SoundEnvironment::Always); 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, false);
TriggerWheelSparkles(minecartItem, true); TriggerWheelSparkles(minecartItem, true);
@ -797,8 +814,7 @@ namespace TEN::Entities::Vehicles
Camera.targetDistance = SECTOR(2); Camera.targetDistance = SECTOR(2);
floorHeight = GetMinecartCollision(minecartItem, minecartItem->Pose.Orientation.y, CLICK(2)); floorHeight = GetMinecartCollision(minecartItem, minecartItem->Pose.Orientation.y, CLICK(2));
if (floorHeight > -CLICK(1) && if (abs(floorHeight) < MINECART_STEP_HEIGHT)
floorHeight < CLICK(1))
{ {
if (Wibble & 7 == 0) if (Wibble & 7 == 0)
SoundEffect(SFX_TR3_VEHICLE_QUADBIKE_FRONT_IMPACT, &minecartItem->Pose, SoundEnvironment::Always); 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) 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) bool MinecartControl(ItemInfo* laraItem)
{ {
auto* lara = GetLaraInfo(laraItem); auto* lara = GetLaraInfo(laraItem);
@ -971,10 +940,10 @@ namespace TEN::Entities::Vehicles
} }
auto* minecart = GetMinecartInfo(minecartItem); auto* minecart = GetMinecartInfo(minecartItem);
DoUserInput(minecartItem, laraItem, minecart); DoUserInput(minecartItem, laraItem);
if (minecart->Flags & MINECART_FLAG_CONTROL) if (minecart->Flags & MINECART_FLAG_CONTROL)
MoveCart(laraItem, minecartItem); MoveCart(minecartItem, laraItem);
if (lara->Vehicle != NO_ITEM) if (lara->Vehicle != NO_ITEM)
laraItem->Pose = minecartItem->Pose; laraItem->Pose = minecartItem->Pose;

View file

@ -1,11 +1,15 @@
#pragma once #pragma once
#include "Objects/Utils/VehicleHelpers.h"
struct ItemInfo;
struct CollisionInfo; struct CollisionInfo;
struct ItemInfo;
namespace TEN::Entities::Vehicles namespace TEN::Entities::Vehicles
{ {
void InitialiseMinecart(short itemNumber); 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); bool MinecartControl(ItemInfo* laraItem);
} }

View file

@ -4,19 +4,20 @@ namespace TEN::Entities::Vehicles
{ {
struct MinecartInfo struct MinecartInfo
{ {
short TurnRot; int Velocity = 0;
int TurnX; int VerticalVelocity = 0;
int TurnZ;
short TurnLen;
int Velocity; short TurnRot = 0;
int VerticalVelocity; int TurnX = 0;
int Gradient; int TurnZ = 0;
unsigned int StopDelay; short TurnLen = 0;
int FloorHeightMiddle; int Gradient = 0;
int FloorHeightFront; unsigned int StopDelay = 0;
char Flags; int FloorHeightMiddle = 0;
int FloorHeightFront = 0;
char Flags = NULL;
}; };
} }

File diff suppressed because it is too large Load diff

View file

@ -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);
}

File diff suppressed because it is too large Load diff

View file

@ -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);
}

View file

@ -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;
};
}

View file

@ -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;
};
}

View file

@ -10,44 +10,47 @@
#include "Game/Lara/lara.h" #include "Game/Lara/lara.h"
#include "Game/Lara/lara_helpers.h" #include "Game/Lara/lara_helpers.h"
#include "Objects/TR3/Vehicles/rubber_boat_info.h" #include "Objects/TR3/Vehicles/rubber_boat_info.h"
#include "Objects/Utils/VehicleHelpers.h"
#include "Sound/sound.h" #include "Sound/sound.h"
#include "Specific/input.h" #include "Specific/input.h"
#include "Specific/level.h" #include "Specific/level.h"
#include "Specific/setup.h" #include "Specific/setup.h"
#include "Renderer/Renderer11Enums.h" #include "Renderer/Renderer11Enums.h"
using std::vector;
using namespace TEN::Input; using namespace TEN::Input;
namespace TEN::Entities::Vehicles namespace TEN::Entities::Vehicles
{ {
#define RBOAT_SLIP 10 const vector<VehicleMountType> RubberBoatMountTypes =
#define RBOAT_SIDE_SLIP 30 {
VehicleMountType::LevelStart,
VehicleMountType::Left,
VehicleMountType::Right,
VehicleMountType::Jump
};
#define RBOAT_FRONT 750 constexpr auto RBOAT_RADIUS = 500;
#define RBOAT_SIDE 300 constexpr auto RBOAT_FRONT = 750;
#define RBOAT_RADIUS 500 constexpr auto RBOAT_SIDE = 300;
#define RBOAT_SNOW 500 constexpr auto RBOAT_SLIP = 10;
constexpr auto RBOAT_SIDE_SLIP = 30;
constexpr auto RBOAT_MOUNT_DISTANCE = CLICK(2.25f);
#define RBOAT_MAX_VELOCITY 110 constexpr auto RBOAT_VELOCITY_ACCEL = 5;
#define RBOAT_SLOW_VELOCITY (RBOAT_MAX_VELOCITY / 3) constexpr auto RBOAT_VELOCITY_DECEL = 1;
#define RBOAT_FAST_VELOCITY (RBOAT_MAX_VELOCITY + 75) constexpr auto RBOAT_VELOCITY_BRAKE_DECEL = 5;
#define RBOAT_MIN_VELOCITY 20 constexpr auto RBOAT_REVERSE_VELOCITY_DECEL = 2;
#define RBOAT_MAX_BACK -20
#define RBOAT_MAX_KICK -80
#define RBOAT_ACCELERATION 5 constexpr auto RBOAT_VELOCITY_MIN = 20;
#define RBOAT_BRAKE 5 constexpr auto RBOAT_SLOW_VELOCITY_MAX = 37;
#define RBOAT_SLOW_DOWN 1 constexpr auto RBOAT_NORMAL_VELOCITY_MAX = 110;
#define RBOAT_UNDO_TURN ANGLE(0.25f) constexpr auto RBOAT_FAST_VELOCITY_MAX = 185;
#define RBOAT_TURN (ANGLE(0.25f) / 2) constexpr auto RBOAT_REVERSE_VELOCITY_MAX = 20;
#define RBOAT_IN_SPEED (IN_SPRINT | IN_CROUCH) #define RBOAT_TURN_RATE_ACCEL (ANGLE(0.25f) / 2)
#define RBOAT_IN_SLOW IN_WALK #define RBOAT_TURN_RATE_DECEL ANGLE(0.25f)
#define RBOAT_IN_DISMOUNT IN_ROLL #define RBOAT_TURN_RATE_MAX ANGLE(4.0f)
#define RBOAT_IN_FORWARD IN_ACTION
#define RBOAT_IN_BACK IN_JUMP
#define RBOAT_IN_LEFT IN_LEFT
#define RBOAT_IN_RIGHT IN_RIGHT
enum RubberBoatState enum RubberBoatState
{ {
@ -90,28 +93,90 @@ namespace TEN::Entities::Vehicles
RBOAT_ANIM_TURN_RIGHT_START = 22 RBOAT_ANIM_TURN_RIGHT_START = 22
}; };
enum RubberBoatMountType RubberBoatInfo* GetRubberBoatInfo(ItemInfo* rBoatItem)
{ {
RBOAT_MOUNT_NONE = 0, return (RubberBoatInfo*)rBoatItem->Data;
RBOAT_MOUNT_LEFT = 1, }
RBOAT_MOUNT_RIGHT = 2,
RBOAT_MOUNT_JUMP = 3,
RBOAT_MOUNT_LEVEL_START = 4
};
void InitialiseRubberBoat(short itemNumber) void InitialiseRubberBoat(short itemNumber)
{ {
auto* rBoatItem = &g_Level.Items[itemNumber]; auto* rBoatItem = &g_Level.Items[itemNumber];
rBoatItem->Data = RubberBoatInfo(); rBoatItem->Data = RubberBoatInfo();
auto* rBoat = (RubberBoatInfo*)rBoatItem->Data; auto* rBoat = GetRubberBoatInfo(rBoatItem);
}
rBoat->TurnRate = 0; void RubberBoatPlayerCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll)
rBoat->LeanAngle = 0; {
rBoat->ExtraRotation = 0; auto* rBoatItem = &g_Level.Items[itemNumber];
rBoat->LeftVerticalVelocity = 0; auto* lara = GetLaraInfo(laraItem);
rBoat->RightVerticalVelocity = 0;
rBoat->Water = 0; if (laraItem->HitPoints < 0 || lara->Vehicle != NO_ITEM)
rBoat->Pitch = 0; 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) 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* boatItem = &g_Level.Items[itemNumber];
auto* lara = GetLaraInfo(laraItem);
int itemNumber2 = g_Level.Rooms[boatItem->RoomNumber].itemNumber; int itemNumber2 = g_Level.Rooms[boatItem->RoomNumber].itemNumber;
while (itemNumber2 != NO_ITEM) while (itemNumber2 != NO_ITEM)
@ -368,37 +348,26 @@ namespace TEN::Entities::Vehicles
return 0; 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* rBoatItem = &g_Level.Items[itemNumber];
auto* rBoat = (RubberBoatInfo*)rBoatItem->Data; auto* rBoat = GetRubberBoatInfo(rBoatItem);
auto* lara = GetLaraInfo(laraItem);
rBoatItem->Pose.Orientation.z -= rBoat->LeanAngle; rBoatItem->Pose.Orientation.z -= rBoat->LeanAngle;
Vector3Int frontLeftOld, frontRightOld, backLeftOld, backRightOld, frontOld; Vector3Int frontLeftOld, frontRightOld, backLeftOld, backRightOld, frontOld;
int heightFrontLeftOld = TestWaterHeight(rBoatItem, RBOAT_FRONT, -RBOAT_SIDE, &frontLeftOld); int heightFrontLeftOld = GetVehicleWaterHeight(rBoatItem, RBOAT_FRONT, -RBOAT_SIDE, true, &frontLeftOld);
int heightFrontRightOld = TestWaterHeight(rBoatItem, RBOAT_FRONT, RBOAT_SIDE, &frontRightOld); int heightFrontRightOld = GetVehicleWaterHeight(rBoatItem, RBOAT_FRONT, RBOAT_SIDE, true, &frontRightOld);
int heightBackLeftOld = TestWaterHeight(rBoatItem, -RBOAT_FRONT, -RBOAT_SIDE, &backLeftOld); int heightBackLeftOld = GetVehicleWaterHeight(rBoatItem, -RBOAT_FRONT, -RBOAT_SIDE, true, &backLeftOld);
int heightBackRightOld = TestWaterHeight(rBoatItem, -RBOAT_FRONT, RBOAT_SIDE, &backRightOld); int heightBackRightOld = GetVehicleWaterHeight(rBoatItem, -RBOAT_FRONT, RBOAT_SIDE, true, &backRightOld);
int heightFrontOld = TestWaterHeight(rBoatItem, 1000, 0, &frontOld); int heightFrontOld = GetVehicleWaterHeight(rBoatItem, 1000, 0, true, &frontOld);
Vector3Int old; Vector3Int old;
old.x = rBoatItem->Pose.Position.x; old.x = rBoatItem->Pose.Position.x;
old.y = rBoatItem->Pose.Position.y; old.y = rBoatItem->Pose.Position.y;
old.z = rBoatItem->Pose.Position.z; 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; rBoatItem->Pose.Orientation.y += rBoat->TurnRate + rBoat->ExtraRotation;
rBoat->LeanAngle = rBoat->TurnRate * 6; rBoat->LeanAngle = rBoat->TurnRate * 6;
@ -427,30 +396,30 @@ namespace TEN::Entities::Vehicles
moved.x = rBoatItem->Pose.Position.x; moved.x = rBoatItem->Pose.Position.x;
moved.z = rBoatItem->Pose.Position.z; moved.z = rBoatItem->Pose.Position.z;
DoRubberBoatShift(laraItem, itemNumber); DoRubberBoatShift(itemNumber, laraItem);
Vector3Int frontLeft, frontRight, backRight, backLeft, front; Vector3Int frontLeft, frontRight, backRight, backLeft, front;
short rotation = 0; 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))) if (heightBackLeft < (backLeftOld.y - CLICK(0.5f)))
rotation = DoRubberBoatShift2(rBoatItem, &backLeft, &backLeftOld); 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))) if (heightBackRight < (backRightOld.y - CLICK(0.5f)))
rotation += DoRubberBoatShift2(rBoatItem, &backRight, &backRightOld); 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))) if (heightFrontLeft < (frontLeftOld.y - CLICK(0.5f)))
rotation += DoRubberBoatShift2(rBoatItem, &frontLeft, &frontLeftOld); 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))) if (heightFrontRight < (frontRightOld.y - CLICK(0.5f)))
rotation += DoRubberBoatShift2(rBoatItem, &frontRight, &frontRightOld); rotation += DoRubberBoatShift2(rBoatItem, &frontRight, &frontRightOld);
if (!slip) if (!slip)
{ {
int heightFront = TestWaterHeight(rBoatItem, 1000, 0, &front); int heightFront = GetVehicleWaterHeight(rBoatItem, 1000, 0, false, &front);
if (heightFront < (frontOld.y - CLICK(0.5f))) if (heightFront < (frontOld.y - CLICK(0.5f)))
DoRubberBoatShift2(rBoatItem, &front, &frontOld); 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); 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 && 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) newVelocity < rBoatItem->Animation.Velocity - 10)
{ {
DoDamage(laraItem, rBoatItem->Animation.Velocity); DoDamage(laraItem, rBoatItem->Animation.Velocity);
@ -486,7 +455,7 @@ namespace TEN::Entities::Vehicles
if (slip) if (slip)
{ {
if (rBoatItem->Animation.Velocity <= RBOAT_MAX_VELOCITY + 10) if (rBoatItem->Animation.Velocity <= RBOAT_NORMAL_VELOCITY_MAX + 10)
rBoatItem->Animation.Velocity = newVelocity; rBoatItem->Animation.Velocity = newVelocity;
} }
else else
@ -497,8 +466,8 @@ namespace TEN::Entities::Vehicles
rBoatItem->Animation.Velocity = newVelocity; rBoatItem->Animation.Velocity = newVelocity;
} }
if (rBoatItem->Animation.Velocity < RBOAT_MAX_BACK) if (rBoatItem->Animation.Velocity < -RBOAT_REVERSE_VELOCITY_MAX)
rBoatItem->Animation.Velocity = RBOAT_MAX_BACK; rBoatItem->Animation.Velocity = -RBOAT_REVERSE_VELOCITY_MAX;
} }
return collide; return collide;
@ -530,90 +499,90 @@ namespace TEN::Entities::Vehicles
return verticalVelocity; 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; bool noTurn = true;
if (rBoatItem->Pose.Position.y >= (rBoat->Water - 128) && if (rBoatItem->Pose.Position.y >= (rBoat->Water - 128) &&
rBoat->Water != NO_HEIGHT) 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)) || if ((TrInput & VEHICLE_IN_LEFT && !(TrInput & VEHICLE_IN_REVERSE)) ||
(TrInput & RBOAT_IN_RIGHT && TrInput & RBOAT_IN_BACK)) (TrInput & VEHICLE_IN_RIGHT && TrInput & VEHICLE_IN_REVERSE))
{ {
if (rBoat->TurnRate > 0) if (rBoat->TurnRate > 0)
rBoat->TurnRate -= ANGLE(0.25f); rBoat->TurnRate -= RBOAT_TURN_RATE_DECEL;
else else
{ {
rBoat->TurnRate -= ANGLE(0.25f) / 2; rBoat->TurnRate -= RBOAT_TURN_RATE_ACCEL;
if (rBoat->TurnRate < -ANGLE(4.0f)) if (rBoat->TurnRate < -RBOAT_TURN_RATE_MAX)
rBoat->TurnRate = -ANGLE(4.0f); rBoat->TurnRate = -RBOAT_TURN_RATE_MAX;
} }
noTurn = false; noTurn = false;
} }
else if ((TrInput & RBOAT_IN_RIGHT && !(TrInput & RBOAT_IN_BACK)) || else if ((TrInput & VEHICLE_IN_RIGHT && !(TrInput & VEHICLE_IN_REVERSE)) ||
(TrInput & RBOAT_IN_LEFT && TrInput & RBOAT_IN_BACK)) (TrInput & VEHICLE_IN_LEFT && TrInput & VEHICLE_IN_REVERSE))
{ {
if (rBoat->TurnRate < 0) if (rBoat->TurnRate < 0)
rBoat->TurnRate += ANGLE(0.25f); rBoat->TurnRate += RBOAT_TURN_RATE_DECEL;
else else
{ {
rBoat->TurnRate += ANGLE(0.25f) / 2; rBoat->TurnRate += RBOAT_TURN_RATE_ACCEL;
if (rBoat->TurnRate > ANGLE(4.0f)) if (rBoat->TurnRate > RBOAT_TURN_RATE_MAX)
rBoat->TurnRate = ANGLE(4.0f); rBoat->TurnRate = RBOAT_TURN_RATE_MAX;
} }
noTurn = false; noTurn = false;
} }
if (TrInput & RBOAT_IN_BACK) if (TrInput & VEHICLE_IN_REVERSE)
{ {
if (rBoatItem->Animation.Velocity > 0) if (rBoatItem->Animation.Velocity > 0)
rBoatItem->Animation.Velocity -= 5; rBoatItem->Animation.Velocity -= RBOAT_VELOCITY_BRAKE_DECEL;
else if (rBoatItem->Animation.Velocity > -20) else if (rBoatItem->Animation.Velocity > -RBOAT_REVERSE_VELOCITY_MAX)
rBoatItem->Animation.Velocity += -2; rBoatItem->Animation.Velocity -= RBOAT_REVERSE_VELOCITY_DECEL;
} }
else if (TrInput & RBOAT_IN_FORWARD) else if (TrInput & VEHICLE_IN_ACCELERATE)
{ {
int maxVelocity; int maxVelocity;
if (TrInput & RBOAT_IN_SPEED) if (TrInput & VEHICLE_IN_SPEED)
maxVelocity = 185; maxVelocity = RBOAT_FAST_VELOCITY_MAX;
else 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) if (rBoatItem->Animation.Velocity < maxVelocity)
rBoatItem->Animation.Velocity += 3 + (5 * rBoatItem->Animation.Velocity) / (maxVelocity * 2); rBoatItem->Animation.Velocity += (RBOAT_VELOCITY_ACCEL / 2 + 1) + (RBOAT_VELOCITY_ACCEL * rBoatItem->Animation.Velocity) / (maxVelocity * 2);
else if (rBoatItem->Animation.Velocity > (maxVelocity + 1)) else if (rBoatItem->Animation.Velocity > (maxVelocity + RBOAT_VELOCITY_DECEL))
rBoatItem->Animation.Velocity -= 1; 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 >= 0 &&
rBoatItem->Animation.Velocity < 20) rBoatItem->Animation.Velocity < RBOAT_VELOCITY_MIN)
{ {
if (!(TrInput & RBOAT_IN_DISMOUNT) && rBoatItem->Animation.Velocity == 0) if (!(TrInput & VEHICLE_IN_DISMOUNT) && rBoatItem->Animation.Velocity == 0)
rBoatItem->Animation.Velocity = 20; rBoatItem->Animation.Velocity = RBOAT_VELOCITY_MIN;
} }
else if (rBoatItem->Animation.Velocity > 1) else if (rBoatItem->Animation.Velocity > RBOAT_VELOCITY_DECEL)
rBoatItem->Animation.Velocity -= 1; rBoatItem->Animation.Velocity -= RBOAT_VELOCITY_DECEL;
else else
rBoatItem->Animation.Velocity = 0; rBoatItem->Animation.Velocity = 0;
} }
else else
{ {
if (TrInput & (RBOAT_IN_LEFT | RBOAT_IN_RIGHT) && if (TrInput & (VEHICLE_IN_LEFT | VEHICLE_IN_RIGHT) &&
rBoatItem->Animation.Velocity >= 0 && rBoatItem->Animation.Velocity >= 0 &&
rBoatItem->Animation.Velocity < 20) rBoatItem->Animation.Velocity < RBOAT_VELOCITY_MIN)
{ {
if (!(TrInput & RBOAT_IN_DISMOUNT) && rBoatItem->Animation.Velocity == 0) if (!(TrInput & VEHICLE_IN_DISMOUNT) && rBoatItem->Animation.Velocity == 0)
rBoatItem->Animation.Velocity = 20; rBoatItem->Animation.Velocity = RBOAT_VELOCITY_MIN;
} }
else if (rBoatItem->Animation.Velocity > 1) else if (rBoatItem->Animation.Velocity > RBOAT_VELOCITY_DECEL)
rBoatItem->Animation.Velocity -= 1; rBoatItem->Animation.Velocity -= RBOAT_VELOCITY_DECEL;
else else
rBoatItem->Animation.Velocity = 0; rBoatItem->Animation.Velocity = 0;
@ -625,59 +594,6 @@ namespace TEN::Entities::Vehicles
return noTurn; 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) static bool TestRubberBoatDismount(ItemInfo* laraItem, int direction)
{ {
auto* lara = GetLaraInfo(laraItem); auto* lara = GetLaraInfo(laraItem);
@ -710,9 +626,9 @@ namespace TEN::Entities::Vehicles
return true; 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) if (laraItem->HitPoints <= 0)
{ {
@ -750,7 +666,7 @@ namespace TEN::Entities::Vehicles
switch (laraItem->Animation.ActiveState) switch (laraItem->Animation.ActiveState)
{ {
case RBOAT_STATE_IDLE: case RBOAT_STATE_IDLE:
if (TrInput & RBOAT_IN_DISMOUNT) if (TrInput & VEHICLE_IN_DISMOUNT)
{ {
if (rBoatItem->Animation.Velocity == 0) if (rBoatItem->Animation.Velocity == 0)
{ {
@ -770,9 +686,9 @@ namespace TEN::Entities::Vehicles
if (rBoatItem->Animation.Velocity <= 0) if (rBoatItem->Animation.Velocity <= 0)
laraItem->Animation.TargetState = RBOAT_STATE_IDLE; laraItem->Animation.TargetState = RBOAT_STATE_IDLE;
if (TrInput & RBOAT_IN_RIGHT) if (TrInput & VEHICLE_IN_RIGHT)
laraItem->Animation.TargetState = RBOAT_STATE_TURN_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; laraItem->Animation.TargetState = RBOAT_STATE_TURN_LEFT;
break; break;
@ -784,7 +700,7 @@ namespace TEN::Entities::Vehicles
case RBOAT_STATE_TURN_RIGHT: case RBOAT_STATE_TURN_RIGHT:
if (rBoatItem->Animation.Velocity <= 0) if (rBoatItem->Animation.Velocity <= 0)
laraItem->Animation.TargetState = RBOAT_STATE_IDLE; laraItem->Animation.TargetState = RBOAT_STATE_IDLE;
else if (!(TrInput & RBOAT_IN_RIGHT)) else if (!(TrInput & VEHICLE_IN_RIGHT))
laraItem->Animation.TargetState = RBOAT_STATE_MOVING; laraItem->Animation.TargetState = RBOAT_STATE_MOVING;
break; break;
@ -792,7 +708,7 @@ namespace TEN::Entities::Vehicles
case RBOAT_STATE_TURN_LEFT: case RBOAT_STATE_TURN_LEFT:
if (rBoatItem->Animation.Velocity <= 0) if (rBoatItem->Animation.Velocity <= 0)
laraItem->Animation.TargetState = RBOAT_STATE_IDLE; laraItem->Animation.TargetState = RBOAT_STATE_IDLE;
else if (!(TrInput & RBOAT_IN_LEFT)) else if (!(TrInput & VEHICLE_IN_LEFT))
laraItem->Animation.TargetState = RBOAT_STATE_MOVING; laraItem->Animation.TargetState = RBOAT_STATE_MOVING;
break; 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); auto* lara = GetLaraInfo(laraItem);
@ -881,7 +797,7 @@ namespace TEN::Entities::Vehicles
laraItem->Animation.TargetState = LS_JUMP_FORWARD; laraItem->Animation.TargetState = LS_JUMP_FORWARD;
laraItem->Pose.Orientation.x = 0; laraItem->Pose.Orientation.x = 0;
laraItem->Pose.Orientation.z = 0; laraItem->Pose.Orientation.z = 0;
laraItem->Animation.Airborne = true; laraItem->Animation.IsAirborne = true;
laraItem->Animation.Velocity = 20; laraItem->Animation.Velocity = 20;
laraItem->Animation.VerticalVelocity = -40; laraItem->Animation.VerticalVelocity = -40;
lara->Vehicle = NO_ITEM; lara->Vehicle = NO_ITEM;
@ -908,10 +824,10 @@ namespace TEN::Entities::Vehicles
void RubberBoatControl(short itemNumber) void RubberBoatControl(short itemNumber)
{ {
auto* rBoatItem = &g_Level.Items[itemNumber];
auto* rBoat = GetRubberBoatInfo(rBoatItem);
auto* laraItem = LaraItem; auto* laraItem = LaraItem;
auto* lara = GetLaraInfo(laraItem); auto* lara = GetLaraInfo(laraItem);
auto* rBoatItem = &g_Level.Items[itemNumber];
auto* rBoat = (RubberBoatInfo*)rBoatItem->Data;
bool noTurn = true; bool noTurn = true;
bool drive = false; bool drive = false;
@ -919,10 +835,9 @@ namespace TEN::Entities::Vehicles
int pitch, height, ofs, nowake; int pitch, height, ofs, nowake;
Vector3Int frontLeft, frontRight; Vector3Int frontLeft, frontRight;
int collide = RubberBoatDynamics(laraItem, itemNumber); int collide = RubberBoatDynamics(itemNumber, laraItem);
int heightFrontLeft = TestWaterHeight(rBoatItem, RBOAT_FRONT, -RBOAT_SIDE, &frontLeft); int heightFrontLeft = GetVehicleWaterHeight(rBoatItem, RBOAT_FRONT, -RBOAT_SIDE, true, &frontLeft);
int heightFrontRight = TestWaterHeight(rBoatItem, RBOAT_FRONT, RBOAT_SIDE, &frontRight); int heightFrontRight = GetVehicleWaterHeight(rBoatItem, RBOAT_FRONT, RBOAT_SIDE, true, &frontRight);
if (lara->Vehicle == itemNumber) if (lara->Vehicle == itemNumber)
{ {
@ -945,24 +860,24 @@ namespace TEN::Entities::Vehicles
default: default:
drive = true; drive = true;
noTurn = RubberBoatUserControl(laraItem, rBoatItem); noTurn = RubberBoatUserControl(rBoatItem, laraItem);
break; break;
} }
} }
else else
{ {
if (rBoatItem->Animation.Velocity > RBOAT_SLOW_DOWN) if (rBoatItem->Animation.Velocity > RBOAT_VELOCITY_DECEL)
rBoatItem->Animation.Velocity -= RBOAT_SLOW_DOWN; rBoatItem->Animation.Velocity -= RBOAT_VELOCITY_DECEL;
else else
rBoatItem->Animation.Velocity = 0; rBoatItem->Animation.Velocity = 0;
} }
if (noTurn) if (noTurn)
{ {
if (rBoat->TurnRate < -RBOAT_UNDO_TURN) if (rBoat->TurnRate < -RBOAT_TURN_RATE_DECEL)
rBoat->TurnRate += RBOAT_UNDO_TURN; rBoat->TurnRate += RBOAT_TURN_RATE_DECEL;
else if (rBoat->TurnRate > RBOAT_UNDO_TURN) else if (rBoat->TurnRate > RBOAT_TURN_RATE_DECEL)
rBoat->TurnRate -= RBOAT_UNDO_TURN; rBoat->TurnRate -= RBOAT_TURN_RATE_DECEL;
else else
rBoat->TurnRate = 0; rBoat->TurnRate = 0;
} }
@ -999,7 +914,7 @@ namespace TEN::Entities::Vehicles
if (lara->Vehicle == itemNumber) if (lara->Vehicle == itemNumber)
{ {
RubberBoatAnimation(laraItem, rBoatItem, collide); RubberBoatAnimation(rBoatItem, laraItem, collide);
if (probe.RoomNumber != rBoatItem->RoomNumber) if (probe.RoomNumber != rBoatItem->RoomNumber)
{ {
@ -1033,14 +948,14 @@ namespace TEN::Entities::Vehicles
rBoat->Pitch += ((pitch - rBoat->Pitch) / 4); rBoat->Pitch += ((pitch - rBoat->Pitch) / 4);
if (rBoatItem->Animation.Velocity > 8) 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) 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) if (lara->Vehicle != itemNumber)
return; 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; 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); height = GetWaterHeight(rBoatItem->Pose.Position.x, rBoatItem->Pose.Position.y + 128, rBoatItem->Pose.Position.z, probedRoomNumber);

View file

@ -1,12 +1,16 @@
#pragma once #pragma once
#include "Objects/Utils/VehicleHelpers.h"
#include "Game/items.h" struct CollisionInfo;
#include "Game/collision/collide_room.h" struct ItemInfo;
namespace TEN::Entities::Vehicles namespace TEN::Entities::Vehicles
{ {
void InitialiseRubberBoat(short itemNumber); 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 RubberBoatControl(short itemNumber);
void DrawRubberBoat(ItemInfo* rBoatItem); void DrawRubberBoat(ItemInfo* rBoatItem);
} }

View file

@ -4,15 +4,15 @@ namespace TEN::Entities::Vehicles
{ {
struct RubberBoatInfo struct RubberBoatInfo
{ {
int TurnRate; int TurnRate = 0;
short LeanAngle; short LeanAngle = 0;
short PropellerRotation; short PropellerRotation = 0;
short ExtraRotation; short ExtraRotation = 0;
int LeftVerticalVelocity; int LeftVerticalVelocity = 0;
int RightVerticalVelocity; int RightVerticalVelocity = 0;
int Water; int Water = 0;
int Pitch; int Pitch = 0;
}; };
} }

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,16 @@
#pragma once #pragma once
#include "Game/items.h" #include "Objects/Utils/VehicleHelpers.h"
#include "Game/collision/collide_room.h"
struct CollisionInfo;
struct ItemInfo;
namespace TEN::Entities::Vehicles namespace TEN::Entities::Vehicles
{ {
void UPVInitialise(short itemNumber); 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); void UPVEffects(short itemNumber);
bool UPVControl(ItemInfo* laraItem, CollisionInfo* coll); bool UPVControl(ItemInfo* laraItem, CollisionInfo* coll);
} }

View file

@ -4,13 +4,16 @@ namespace TEN::Entities::Vehicles
{ {
struct UPVInfo struct UPVInfo
{ {
int Velocity; Vector3Shrt TurnRate;
int Rot; short TurbineRotation = 0;
int XRot; short LeftRudderRotation = 0;
short FanRot; short RightRudderRotation = 0;
unsigned int HarpoonTimer;
bool HarpoonLeft;
char Flags; int Velocity = 0;
unsigned int HarpoonTimer = 0;
bool HarpoonLeft = false;
char Flags = NULL;
}; };
} }

View file

@ -23,7 +23,7 @@
#include "Objects/TR3/Vehicles/big_gun.h" #include "Objects/TR3/Vehicles/big_gun.h"
#include "Objects/TR3/Vehicles/kayak.h" #include "Objects/TR3/Vehicles/kayak.h"
#include "Objects/TR3/Vehicles/minecart.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/upv.h"
#include "Objects/TR3/Vehicles/rubber_boat.h" #include "Objects/TR3/Vehicles/rubber_boat.h"
/// necessary import /// necessary import
@ -388,7 +388,7 @@ static void StartVehicles(ObjectInfo* obj)
if (obj->loaded) if (obj->loaded)
{ {
obj->initialise = InitialiseQuadBike; obj->initialise = InitialiseQuadBike;
obj->collision = QuadBikeCollision; obj->collision = QuadBikePlayerCollision;
obj->hitEffect = HIT_RICOCHET; obj->hitEffect = HIT_RICOCHET;
obj->savePosition = true; obj->savePosition = true;
obj->saveAnim = true; obj->saveAnim = true;
@ -401,7 +401,7 @@ static void StartVehicles(ObjectInfo* obj)
{ {
obj->initialise = InitialiseRubberBoat; obj->initialise = InitialiseRubberBoat;
obj->control = RubberBoatControl; obj->control = RubberBoatControl;
obj->collision = RubberBoatCollision; obj->collision = RubberBoatPlayerCollision;
obj->drawRoutine = DrawRubberBoat; obj->drawRoutine = DrawRubberBoat;
obj->hitEffect = HIT_RICOCHET; obj->hitEffect = HIT_RICOCHET;
obj->savePosition = true; obj->savePosition = true;
@ -415,7 +415,7 @@ static void StartVehicles(ObjectInfo* obj)
if (obj->loaded) if (obj->loaded)
{ {
obj->initialise = InitialiseKayak; obj->initialise = InitialiseKayak;
obj->collision = KayakCollision; obj->collision = KayakPlayerCollision;
//obj->drawRoutine = KayakDraw; //obj->drawRoutine = KayakDraw;
obj->hitEffect = HIT_RICOCHET; obj->hitEffect = HIT_RICOCHET;
obj->saveAnim = true; obj->saveAnim = true;
@ -429,7 +429,7 @@ static void StartVehicles(ObjectInfo* obj)
if (obj->loaded) if (obj->loaded)
{ {
obj->initialise = InitialiseMinecart; obj->initialise = InitialiseMinecart;
obj->collision = MinecartCollision; obj->collision = MinecartPlayerCollision;
obj->hitEffect = HIT_RICOCHET; obj->hitEffect = HIT_RICOCHET;
obj->saveAnim = true; obj->saveAnim = true;
obj->saveFlags = true; obj->saveFlags = true;
@ -455,7 +455,7 @@ static void StartVehicles(ObjectInfo* obj)
{ {
obj->initialise = UPVInitialise; obj->initialise = UPVInitialise;
obj->control = UPVEffects; obj->control = UPVEffects;
obj->collision = UPVCollision; obj->collision = UPVPlayerCollision;
// obj->drawRoutine = SubDraw; // obj->drawRoutine = SubDraw;
obj->hitEffect = HIT_RICOCHET; obj->hitEffect = HIT_RICOCHET;
obj->saveAnim = true; obj->saveAnim = true;

View file

@ -354,7 +354,7 @@ namespace TEN::Entities::TR4
} }
else if (creature->Mood == MoodType::Attack) 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)) if (AI.bite && AI.distance < pow(BABOON_ATTACK_RANGE, 2))
{ {
@ -471,7 +471,7 @@ namespace TEN::Entities::TR4
if (AI.ahead && Lara.TargetEntity != item) if (AI.ahead && Lara.TargetEntity != item)
item->Animation.TargetState = BABOON_STATE_IDLE; 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; item->Animation.TargetState = BABOON_STATE_IDLE;
else if (creature->Mood == MoodType::Attack) else if (creature->Mood == MoodType::Attack)
{ {

View file

@ -437,31 +437,31 @@ namespace TEN::Entities::TR4
switch (item->Animation.ActiveState) switch (item->Animation.ActiveState)
{ {
case BADDY_STATE_DEATH: case BADDY_STATE_DEATH:
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
currentCreature->LOT.IsMonkeying = false; currentCreature->LOT.IsMonkeying = false;
if (item->Pose.Position.y >= item->Floor) if (item->Pose.Position.y >= item->Floor)
{ {
item->Pose.Position.y = item->Floor; item->Pose.Position.y = item->Floor;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
} }
break; break;
case BADDY_STATE_MONKEY_TO_FREEFALL: case BADDY_STATE_MONKEY_TO_FREEFALL:
item->Animation.TargetState = BADDY_STATE_FREEFALL; item->Animation.TargetState = BADDY_STATE_FREEFALL;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
break; break;
case BADDY_STATE_FREEFALL: case BADDY_STATE_FREEFALL:
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
if (item->Pose.Position.y >= item->Floor) if (item->Pose.Position.y >= item->Floor)
{ {
item->Pose.Position.y = item->Floor; item->Pose.Position.y = item->Floor;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.TargetState = BADDY_STATE_FREEFALL_LAND_DEATH; item->Animation.TargetState = BADDY_STATE_FREEFALL_LAND_DEATH;
} }
@ -1034,7 +1034,7 @@ namespace TEN::Entities::TR4
if (item->TouchBits) if (item->TouchBits)
{ {
SetAnimation(LaraItem, LA_JUMP_UP); SetAnimation(LaraItem, LA_JUMP_UP);
LaraItem->Animation.Airborne = true; LaraItem->Animation.IsAirborne = true;
LaraItem->Animation.VerticalVelocity = 2; LaraItem->Animation.VerticalVelocity = 2;
LaraItem->Animation.VerticalVelocity = 1; LaraItem->Animation.VerticalVelocity = 1;
LaraItem->Pose.Position.y += CLICK(0.75f); LaraItem->Pose.Position.y += CLICK(0.75f);

View file

@ -155,7 +155,7 @@ namespace TEN::Entities::TR4
if (item->Pose.Position.y >= item->Floor) if (item->Pose.Position.y >= item->Floor)
{ {
item->Animation.TargetState = BAT_STATE_DEATH; item->Animation.TargetState = BAT_STATE_DEATH;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Pose.Position.y = item->Floor; item->Pose.Position.y = item->Floor;
} }
else else
@ -164,7 +164,7 @@ namespace TEN::Entities::TR4
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
item->Animation.ActiveState = BAT_STATE_DEATH_FALL; item->Animation.ActiveState = BAT_STATE_DEATH_FALL;
item->Animation.TargetState = BAT_STATE_DEATH_FALL; item->Animation.TargetState = BAT_STATE_DEATH_FALL;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
} }
} }

View file

@ -97,14 +97,14 @@ namespace TEN::Entities::TR4
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + BBEETLE_ANIM_DEATH_START; item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + BBEETLE_ANIM_DEATH_START;
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
item->Animation.ActiveState = BBEETLE_STATE_DEATH_START; item->Animation.ActiveState = BBEETLE_STATE_DEATH_START;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
} }
} }
else if (item->Pose.Position.y >= item->Floor) else if (item->Pose.Position.y >= item->Floor)
{ {
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.VerticalVelocity = 0;
item->Animation.TargetState = BBEETLE_STATE_DEATH_END; item->Animation.TargetState = BBEETLE_STATE_DEATH_END;
} }

View file

@ -369,11 +369,11 @@ namespace TEN::Entities::TR4
ItemNewRoom(itemNumber, probe.RoomNumber); ItemNewRoom(itemNumber, probe.RoomNumber);
if (item->Pose.Position.y < item->Floor) if (item->Pose.Position.y < item->Floor)
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
else else
{ {
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.VerticalVelocity = 0;
} }

View file

@ -290,7 +290,7 @@ namespace TEN::Entities::TR4
item->Animation.AnimNumber = object->animIndex + 5; item->Animation.AnimNumber = object->animIndex + 5;
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
item->Animation.ActiveState = HARPY_STATE_DEATH_START; item->Animation.ActiveState = HARPY_STATE_DEATH_START;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
item->Animation.Velocity = 0; item->Animation.Velocity = 0;
} }
@ -311,7 +311,7 @@ namespace TEN::Entities::TR4
{ {
item->Pose.Position.y = item->Floor; item->Pose.Position.y = item->Floor;
item->Animation.TargetState = HARPY_STATE_DEATH_END; item->Animation.TargetState = HARPY_STATE_DEATH_END;
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
item->Animation.VerticalVelocity = 0; item->Animation.VerticalVelocity = 0;
} }

View file

@ -650,7 +650,7 @@ namespace TEN::Entities::TR4
auto* item = &g_Level.Items[itemNumber]; auto* item = &g_Level.Items[itemNumber];
if ((!(TrInput & IN_ACTION) || if ((!(TrInput & IN_ACTION) ||
laraItem->Animation.Airborne || laraItem->Animation.IsAirborne ||
laraItem->Animation.ActiveState != LS_IDLE || laraItem->Animation.ActiveState != LS_IDLE ||
laraItem->Animation.AnimNumber != LA_STAND_IDLE || laraItem->Animation.AnimNumber != LA_STAND_IDLE ||
Lara.Control.HandStatus != HandStatus::Free || Lara.Control.HandStatus != HandStatus::Free ||

View file

@ -348,7 +348,7 @@ namespace TEN::Entities::TR4
case 14: case 14:
if (item->Animation.AnimNumber != Objects[item->Animation.AnimNumber].animIndex + 26) if (item->Animation.AnimNumber != Objects[item->Animation.AnimNumber].animIndex + 26)
{ {
item->Animation.Airborne = false; item->Animation.IsAirborne = false;
creature->MaxTurn = 0; creature->MaxTurn = 0;
creature->Target.y = LaraItem->Pose.Position.y; creature->Target.y = LaraItem->Pose.Position.y;
creature->LOT.Fly = 16; creature->LOT.Fly = 16;
@ -374,7 +374,7 @@ namespace TEN::Entities::TR4
} }
else else
{ {
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
creature->LOT.Fly = 0; creature->LOT.Fly = 0;
if ((item->Pose.Position.y - item->Floor) > 0) if ((item->Pose.Position.y - item->Floor) > 0)

View file

@ -551,7 +551,7 @@ namespace TEN::Entities::TR4
item->Animation.AnimNumber = Objects[ID_SKELETON].animIndex + 44; item->Animation.AnimNumber = Objects[ID_SKELETON].animIndex + 44;
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
item->Animation.ActiveState = 23; item->Animation.ActiveState = 23;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
creature->MaxTurn = 0; creature->MaxTurn = 0;
creature->LOT.IsJumping = false; creature->LOT.IsJumping = false;
} }
@ -685,7 +685,7 @@ namespace TEN::Entities::TR4
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 44; item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 44;
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
item->Animation.ActiveState = 23; item->Animation.ActiveState = 23;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
creature->MaxTurn = 0; creature->MaxTurn = 0;
creature->LOT.IsJumping = false; creature->LOT.IsJumping = false;
} }
@ -740,7 +740,7 @@ namespace TEN::Entities::TR4
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 47; item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 47;
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
item->Animation.ActiveState = 24; item->Animation.ActiveState = 24;
item->Animation.Airborne = true; item->Animation.IsAirborne = true;
creature->MaxTurn = 0; creature->MaxTurn = 0;
} }

View file

@ -239,7 +239,7 @@ namespace TEN::Entities::TR4
laraItem->Animation.ActiveState != LS_IDLE || laraItem->Animation.ActiveState != LS_IDLE ||
laraItem->Animation.AnimNumber != LA_STAND_IDLE || laraItem->Animation.AnimNumber != LA_STAND_IDLE ||
!laraInfo->Torch.IsLit || !laraInfo->Torch.IsLit ||
laraItem->Animation.Airborne) laraItem->Animation.IsAirborne)
{ {
if (laraItem->Animation.AnimNumber != LA_TORCH_LIGHT_3 || if (laraItem->Animation.AnimNumber != LA_TORCH_LIGHT_3 ||
g_Level.Anims[LA_TORCH_LIGHT_3].frameBase + 16 || g_Level.Anims[LA_TORCH_LIGHT_3].frameBase + 16 ||

View file

@ -129,7 +129,7 @@ namespace TEN::Entities::TR4
int bloodCount = 0; int bloodCount = 0;
if ((item->ItemFlags[0] > 1024 || if ((item->ItemFlags[0] > 1024 ||
LaraItem->Animation.Airborne) && LaraItem->Animation.IsAirborne) &&
(item->TriggerFlags & 7) > 2 && (item->TriggerFlags & 7) > 2 &&
(item->TriggerFlags & 7) < 6) (item->TriggerFlags & 7) < 6)
{ {
@ -183,14 +183,14 @@ namespace TEN::Entities::TR4
TriggerBlood(dx, yBottom - (GetRandomControl() % dy), dz, GetRandomControl() << 1, 1); 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; int heightFromFloor = GetCollision(LaraItem).Position.Floor - LaraItem->Pose.Position.y;
if (item->Pose.Position.y >= LaraItem->Pose.Position.y && if (item->Pose.Position.y >= LaraItem->Pose.Position.y &&
heightFromFloor < CLICK(1)) heightFromFloor < CLICK(1))
{ {
SetAnimation(LaraItem, LA_SPIKE_DEATH); SetAnimation(LaraItem, LA_SPIKE_DEATH);
LaraItem->Animation.Airborne = false; LaraItem->Animation.IsAirborne = false;
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show more