From 79fbbb9e982671eb3915ff015f60e8bb9994a24b Mon Sep 17 00:00:00 2001 From: Pirulax Date: Sat, 24 Feb 2024 16:04:53 +0100 Subject: [PATCH] RpAnimBlend Plugin (#694) * Fix `ManageTasks` * Flush all loggers in `unreachable` * Flush loggers every 100ms * Fix random nan bug + add more asserts to anim code * Fix `CAnimBlendAssociation::UpdateTime` * fix `CAnimBlendAssociation::UpdateBlend` * CMatix: Add `bKeepPos` to `RotateX/Y/Z` * Fix `CVehicle::SetComponentRotation` * FxManager_c: Add note * Refactor `CStreaming::AddEntity` * Refactor `CCollision::CalculateTrianglePlanes` * Refactor `CCollision::RemoveTrianglePlanes` * Fix anim flag function names * Proper fix of `RtAnimInterpolatorSetCurrentAnim` (UV anims need original interpolator code) (Fixes #602 #526) * Add missing `case TASK_NONE` in `CTaskComplexEnterCar::CreateNextSubTask` * Fix `CPathFind::SetLinksBridgeLights` * Template wrapper instead of 1000+ lines of copy paste * Verious smaller fixes --- source/Base.h | 3 + source/InjectHooksMain.cpp | 4 + source/app/app_debug.cpp | 4 +- source/extensions/FixedFloat.hpp | 3 +- source/extensions/FixedQuat.hpp | 2 +- .../game_sa/Animation/AnimAssocDescriptions.h | 1156 +++++------ .../Animation/AnimBlendAssociation.cpp | 96 +- .../game_sa/Animation/AnimBlendAssociation.h | 115 +- .../game_sa/Animation/AnimBlendClumpData.cpp | 22 +- source/game_sa/Animation/AnimBlendClumpData.h | 33 +- source/game_sa/Animation/AnimBlendFrameData.h | 37 +- .../game_sa/Animation/AnimBlendHierarchy.cpp | 54 +- source/game_sa/Animation/AnimBlendHierarchy.h | 6 +- source/game_sa/Animation/AnimBlendNode.cpp | 4 +- source/game_sa/Animation/AnimBlendNode.h | 12 +- .../game_sa/Animation/AnimBlendSequence.cpp | 115 +- source/game_sa/Animation/AnimBlendSequence.h | 18 +- .../Animation/AnimBlendStaticAssociation.cpp | 10 +- source/game_sa/Animation/AnimManager.cpp | 94 +- source/game_sa/Animation/AnimManager.h | 19 +- source/game_sa/BoneNode_c.cpp | 34 +- source/game_sa/BoneNode_c.h | 8 +- source/game_sa/BulletInfo.cpp | 2 +- source/game_sa/CarEnterExit.cpp | 6 +- source/game_sa/Collision/Collision.cpp | 34 +- source/game_sa/Collision/CollisionData.cpp | 4 +- source/game_sa/Core/Link.h | 19 +- source/game_sa/Core/LinkList.h | 52 +- source/game_sa/Core/Matrix.cpp | 26 +- source/game_sa/Core/Matrix.h | 6 +- source/game_sa/Core/Quaternion.cpp | 11 +- source/game_sa/Core/Quaternion.h | 7 +- source/game_sa/CutsceneMgr.cpp | 18 +- source/game_sa/Entity/Entity.cpp | 8 +- .../game_sa/Entity/Object/CutsceneObject.cpp | 8 +- source/game_sa/Entity/Ped/Ped.cpp | 30 +- source/game_sa/Entity/Ped/Ped.h | 1 + source/game_sa/Entity/Ped/PlayerPed.cpp | 4 +- source/game_sa/Entity/Vehicle/Vehicle.cpp | 25 +- source/game_sa/Enums/eBoneTag.h | 2 +- source/game_sa/Events/EventHandler.cpp | 2 +- source/game_sa/Events/EventScriptCommand.cpp | 2 +- source/game_sa/Fx/FxManager.cpp | 2 +- source/game_sa/IKChain_c.cpp | 8 +- source/game_sa/Models/ClumpModelInfo.cpp | 6 +- source/game_sa/PathFind.cpp | 2 +- source/game_sa/PedIK.cpp | 22 +- source/game_sa/PedIntelligence.cpp | 2 +- .../Plugins/RpAnimBlendPlugin/RpAnimBlend.cpp | 1797 ++++++++++++++++- .../Plugins/RpAnimBlendPlugin/RpAnimBlend.h | 311 ++- source/game_sa/RenderWare/rw/rpdbgerr.h | 316 +++ source/game_sa/RenderWare/rw/rphanim.h | 437 +++- source/game_sa/RenderWare/rw/rpplugin.h | 36 + source/game_sa/RenderWare/rw/rtanim.cpp | 111 +- source/game_sa/RenderWare/rw/rtanim.h | 6 +- source/game_sa/RenderWare/rw/rtquat.cpp | 44 - source/game_sa/RenderWare/rw/rtquat.h | 597 +++++- source/game_sa/Replay.cpp | 2 +- source/game_sa/RpHAnimBlendInterpFrame.h | 5 +- .../game_sa/Scripts/CommandParser/ReadArg.hpp | 2 +- source/game_sa/Scripts/Commands/Character.cpp | 6 +- source/game_sa/Shadows.cpp | 4 +- source/game_sa/Streaming.cpp | 63 +- .../Tasks/TaskTypes/TaskComplexDie.cpp | 2 +- .../Tasks/TaskTypes/TaskComplexEnterCar.cpp | 4 +- .../Tasks/TaskTypes/TaskComplexFacial.cpp | 5 + .../TaskTypes/TaskComplexInAirAndLand.cpp | 2 +- .../Tasks/TaskTypes/TaskSimpleAnim.cpp | 8 +- .../Tasks/TaskTypes/TaskSimpleArrestPed.cpp | 2 +- .../Tasks/TaskTypes/TaskSimpleBeHit.cpp | 4 +- .../Tasks/TaskTypes/TaskSimpleCarGetIn.cpp | 2 +- .../Tasks/TaskTypes/TaskSimpleClimb.cpp | 12 +- .../game_sa/Tasks/TaskTypes/TaskSimpleDie.cpp | 12 +- .../Tasks/TaskTypes/TaskSimpleDuck.cpp | 12 +- .../Tasks/TaskTypes/TaskSimpleEvasiveStep.cpp | 4 +- .../Tasks/TaskTypes/TaskSimpleFacial.cpp | 2 +- .../Tasks/TaskTypes/TaskSimpleFall.cpp | 14 +- .../TaskTypes/TaskSimpleGoToPointFine.cpp | 10 +- .../Tasks/TaskTypes/TaskSimpleHoldEntity.cpp | 10 +- .../Tasks/TaskTypes/TaskSimpleIKLookAt.cpp | 2 +- .../Tasks/TaskTypes/TaskSimpleIKPointArm.cpp | 2 +- .../Tasks/TaskTypes/TaskSimpleInAir.cpp | 4 +- .../Tasks/TaskTypes/TaskSimpleJetPack.cpp | 8 +- .../Tasks/TaskTypes/TaskSimpleJump.cpp | 8 +- .../Tasks/TaskTypes/TaskSimpleLand.cpp | 2 +- .../TaskTypes/TaskSimplePlayerOnFoot.cpp | 8 +- .../Tasks/TaskTypes/TaskSimpleShakeFist.cpp | 4 +- .../Tasks/TaskTypes/TaskSimpleSitDown.cpp | 2 +- .../Tasks/TaskTypes/TaskSimpleStandUp.cpp | 2 +- .../Tasks/TaskTypes/TaskSimpleSwim.cpp | 6 +- .../TaskTypes/TaskSimpleThrowProjectile.cpp | 4 +- source/game_sa/VisibilityPlugins.cpp | 2 +- source/game_sa/Weapon.cpp | 4 +- source/toolsmenu/UIRenderer.cpp | 9 +- 94 files changed, 4757 insertions(+), 1310 deletions(-) create mode 100644 source/game_sa/RenderWare/rw/rpdbgerr.h create mode 100644 source/game_sa/RenderWare/rw/rpplugin.h diff --git a/source/Base.h b/source/Base.h index f3208dfc8d..8ec5543fb5 100644 --- a/source/Base.h +++ b/source/Base.h @@ -68,6 +68,9 @@ template spdlog::error(mbMsg); spdlog::dump_backtrace(); + spdlog::apply_all([](std::shared_ptr l) { // Flush all sinks immidiately + l->flush(); + }); const auto result = MessageBox( NULL, diff --git a/source/InjectHooksMain.cpp b/source/InjectHooksMain.cpp index a1aa95d0fd..febff8da07 100644 --- a/source/InjectHooksMain.cpp +++ b/source/InjectHooksMain.cpp @@ -731,11 +731,15 @@ void InjectHooksMain() { CLocalisation::InjectHooks(); CSimpleVariablesSaveStructure::InjectHooks(); CPedGeometryAnalyser::InjectHooks(); + NodeNamePlugin::InjectHooks(); JPegPlugin::InjectHooks(); PipelinePlugin::InjectHooks(); + RpAnimBlendPlugin::InjectHooks(); CCollisionPlugin::InjectHooks(); BreakablePlugin::InjectHooks(); + RtAnim::InjectHooks(); + CIplStore::InjectHooks(); cHandlingDataMgr::InjectHooks(); CLoadingScreen::InjectHooks(); diff --git a/source/app/app_debug.cpp b/source/app/app_debug.cpp index aef7ad9be5..e1ecf9b483 100644 --- a/source/app/app_debug.cpp +++ b/source/app/app_debug.cpp @@ -197,6 +197,7 @@ LONG WINAPI WindowsExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) { } notsa::Logging::Logging() { + using namespace std::chrono_literals; #if 0 while (!IsDebuggerPresent()) { Sleep(1); @@ -213,7 +214,8 @@ notsa::Logging::Logging() { m_sinks.emplace_back(std::make_shared()); spdlog::set_default_logger(Create("default")); - + spdlog::flush_every(100ms); + AddVectoredExceptionHandler(1, WindowsExceptionHandler); } diff --git a/source/extensions/FixedFloat.hpp b/source/extensions/FixedFloat.hpp index fa7d823706..96f07679d5 100644 --- a/source/extensions/FixedFloat.hpp +++ b/source/extensions/FixedFloat.hpp @@ -13,7 +13,8 @@ class FixedFloat { constexpr operator float() const { return static_cast(value) / CompressValue; } - void Set(float v, bool round) { value = round ? static_cast(v * CompressValue + 0.5f) : static_cast(v * CompressValue); } + void Set(float v, bool round) { value = round ? static_cast(v * CompressValue + 0.5f) : static_cast(v * CompressValue); } + //float Get(bool round) const { return round ? (float)v * CompressValue + 0.5f; } // I'm not ready for this //friend FixedFloat operator+(const FixedFloat& a, const FixedFloat& b) { return a.value + b.value; } diff --git a/source/extensions/FixedQuat.hpp b/source/extensions/FixedQuat.hpp index 32f26f83e8..07965c7106 100644 --- a/source/extensions/FixedQuat.hpp +++ b/source/extensions/FixedQuat.hpp @@ -6,7 +6,7 @@ template struct FixedQuat { constexpr FixedQuat() = default; - constexpr FixedQuat(CQuaternion q) : x(q.x), y(q.y), z(q.z) {} + constexpr FixedQuat(CQuaternion q) : x(q.x), y(q.y), z(q.z), w(q.w) {} constexpr FixedQuat(T X, T Y, T Z, T W) : x(X), y(Y), z(Z), w(W) {} constexpr operator CQuaternion() const { return CQuaternion{ x, y, z, w }; } diff --git a/source/game_sa/Animation/AnimAssocDescriptions.h b/source/game_sa/Animation/AnimAssocDescriptions.h index ecd368c818..3224a1cf67 100644 --- a/source/game_sa/Animation/AnimAssocDescriptions.h +++ b/source/game_sa/Animation/AnimAssocDescriptions.h @@ -4,463 +4,463 @@ #include "AnimationStyleDescriptor.h" static AnimDescriptor aStdAnimDescs[] = { // 0x8A7788 191 - { ANIM_ID_WALK, ANIMATION_LOOPED | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y | ANIMATION_WALK }, - { ANIM_ID_RUN, ANIMATION_LOOPED | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y | ANIMATION_WALK }, - { ANIM_ID_SPRINT, ANIMATION_LOOPED | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y | ANIMATION_WALK }, - { ANIM_ID_IDLE, ANIMATION_LOOPED }, - { ANIM_ID_ROADCROSS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_WALK_START, ANIMATION_TRANSLATE_Y }, - { ANIM_ID_RUN_STOP, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_RUN_STOPR, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_IDLE_HBHB_0, ANIMATION_LOOPED | ANIMATION_PARTIAL }, - { ANIM_ID_IDLE_HBHB_1, ANIMATION_LOOPED | ANIMATION_PARTIAL }, - { ANIM_ID_IDLE_TIRED, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_IDLE_ARMED, ANIMATION_LOOPED | ANIMATION_PARTIAL }, - { ANIM_ID_IDLE_CHAT, ANIMATION_LOOPED | ANIMATION_PARTIAL }, - { ANIM_ID_IDLE_TAXI, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_SWIM_TREAD, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_KO_SHOT_FRONT_0, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_800 }, - { ANIM_ID_KO_SHOT_FRONT_1, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_800 }, - { ANIM_ID_KO_SHOT_FRONT_2, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_800 }, - { ANIM_ID_KO_SHOT_FRONT_3, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_800 }, - { ANIM_ID_KO_SHOT_FACE, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_KO_SHOT_STOM, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_GAS_CWR, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_KD_LEFT, ANIMATION_PARTIAL }, - { ANIM_ID_KD_RIGHT, ANIMATION_PARTIAL }, - { ANIM_ID_KO_SKID_FRONT, ANIMATION_PARTIAL }, - { ANIM_ID_KO_SPIN_R, ANIMATION_PARTIAL }, - { ANIM_ID_KO_SKID_BACK, ANIMATION_PARTIAL | ANIMATION_800 }, - { ANIM_ID_KO_SPIN_L, ANIMATION_PARTIAL }, - { ANIM_ID_SHOT_PARTIAL, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_SHOT_LEFTP, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_SHOT_PARTIAL_B, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_SHOT_RIGHTP, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_HIT_FRONT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_HIT_L, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_HIT_BACK, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_HIT_R, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_FLOOR_HIT, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_HIT_WALK, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_HIT_WALL, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FLOOR_HIT_F, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_800 }, - { ANIM_ID_HIT_BEHIND, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_FIGHTSH_FWD, ANIMATION_LOOPED | ANIMATION_PARTIAL | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y | ANIMATION_WALK }, - { ANIM_ID_FIGHTSH_LEFT, ANIMATION_LOOPED | ANIMATION_PARTIAL | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_WALK }, - { ANIM_ID_FIGHTSH_BWD, ANIMATION_LOOPED | ANIMATION_PARTIAL | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y | ANIMATION_WALK }, - { ANIM_ID_FIGHTSH_RIGHT, ANIMATION_LOOPED | ANIMATION_PARTIAL | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_WALK }, - { ANIM_ID_FIGHTSHF, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHTSHB, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT2IDLE, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_BOMBER, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_GUN_STAND, ANIMATION_LOOPED }, - { ANIM_ID_GUNMOVE_FWD, ANIMATION_LOOPED | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y | ANIMATION_WALK }, - { ANIM_ID_GUNMOVE_L, ANIMATION_LOOPED | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_WALK }, - { ANIM_ID_GUNMOVE_BWD, ANIMATION_LOOPED | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y | ANIMATION_WALK }, - { ANIM_ID_GUNMOVE_R, ANIMATION_LOOPED | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_WALK }, - { ANIM_ID_GUN_2_IDLE, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_WEAPON_CROUCH, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_GUNCROUCHFWD, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_WALK }, - { ANIM_ID_CROUCH_ROLL_L, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X }, - { ANIM_ID_GUNCROUCHBWD, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_WALK }, - { ANIM_ID_CROUCH_ROLL_R, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X }, - { ANIM_ID_CAR_SIT, ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_CAR_LSIT, ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_CAR_SITP, ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_CAR_SITPLO, ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_CAR_SIT_WEAK, ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_CAR_SIT_PRO, ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_DRIVE_L, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_R, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_LO_L, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_LO_R, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_L_WEAK, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_R_WEAK, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_L_PRO, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_R_PRO, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVEBY_L, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVEBY_R, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVEBYL_L, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVEBYL_R, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_CAR_LB, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_CAR_LB_WEAK, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_CAR_LB_PRO, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_BOAT, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_BOAT_L, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_BOAT_R, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_BOAT_BACK, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_L_SLOW, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_R_SLOW, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_L_WEAK_SLOW, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_R_WEAK_SLOW, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_L_PRO_SLOW, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_R_PRO_SLOW, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_TRUCK, ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_DRIVE_TRUCK_L, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_TRUCK_R, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_DRIVE_TRUCK_BACK, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_KART_DRIVE, ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_KART_L, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_KART_R, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_KART_LB, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_PICKUPR, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BIKE_PICKUPL, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BIKE_PULLUPR, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BIKE_PULLUPL, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BIKE_ELBOWL, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BIKE_ELBOWR, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BIKE_FALL_OFF, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_BIKE_FALLR, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_CAR_HOOKERTALK, ANIMATION_LOOPED | ANIMATION_PARTIAL }, - { ANIM_ID_DEFAULT_CAR_CRAWLOUTRHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DEFAULT_CAR_CRAWLOUTRHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DEFAULT_CAR_ROLLOUT_LHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_800 }, - { ANIM_ID_DEFAULT_CAR_ROLLOUT_RHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_800 }, - { ANIM_ID_GETUP_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_GETUP_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_GETUP_2, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_GETUP_FRONT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_800 }, - { ANIM_ID_JUMP_LAUNCH, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_JUMP_LAUNCH_R, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_JUMP_GLIDE, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_JUMP_LAND, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FALL_FALL, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_FALL_GLIDE, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_FALL_LAND, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FALL_COLLAPSE, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FALL_BACK, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_FALL_FRONT, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_800 }, - { ANIM_ID_EV_STEP, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_EV_DIVE, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_800 }, - { ANIM_ID_CLIMB_JUMP, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CLIMB_IDLE, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CLIMB_PULL, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CLIMB_STAND, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CLIMB_STAND_FINISH, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CLIMB_JUMP_B, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CLIMB_JUMP2FALL, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_XPRESSSCRATCH, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_200 }, - { ANIM_ID_TURN_180, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_TURN_L, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_TURN_R, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_ARRESTGUN, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_DROWN, ANIMATION_PARTIAL }, - { ANIM_ID_DUCK_COWER, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_HANDSUP, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_HANDSCOWER, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FUCKU, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_PHONE_IN, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_PHONE_OUT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_PHONE_TALK, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_SEAT_DOWN, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_SEAT_UP, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_SEAT_IDLE, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_ATM, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_ABSEIL, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_WALK_DOORPARTIAL, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_FACSURP, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND | ANIMATION_INDESTRUCTIBLE }, - { ANIM_ID_FACSURPM, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND | ANIMATION_INDESTRUCTIBLE }, - { ANIM_ID_FACURIOS, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND | ANIMATION_INDESTRUCTIBLE }, - { ANIM_ID_FACANGER_0, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND | ANIMATION_INDESTRUCTIBLE }, - { ANIM_ID_FACANGER_1, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND | ANIMATION_INDESTRUCTIBLE }, - { ANIM_ID_FACANGER_2, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND | ANIMATION_INDESTRUCTIBLE }, - { ANIM_ID_FACTALK, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND | ANIMATION_INDESTRUCTIBLE }, - { ANIM_ID_FACGUM, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND | ANIMATION_INDESTRUCTIBLE }, - { ANIM_ID_TAP_HAND, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_TAP_HANDP, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_SHOVE_PARTIAL, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_FLEE_LKAROUND_01, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_ENDCHAT_01, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_ENDCHAT_02, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_ENDCHAT_03, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_SMOKE_IN_CAR, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_PASS_SMOKE_IN_CAR, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_DAM_ARML_FRMBK, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_ARML_FRMFT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_ARML_FRMLT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_ARMR_FRMBK, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_ARMR_FRMFT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_ARMR_FRMRT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_LEGL_FRMBK, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_LEGL_FRMFT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_LEGL_FRMLT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_LEGR_FRMBK, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_LEGR_FRMFT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_LEGR_FRMRT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_STOMACH_FRMBK, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_STOMACH_FRMFT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_STOMACH_FRMLT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DAM_STOMACH_FRMRT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_DEAD_LHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_DEAD_RHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_TUNE_RADIO, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_GANG_GUNSTAND, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME }, + { ANIM_ID_WALK, ANIMATION_IS_LOOPED | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_RUN, ANIMATION_IS_LOOPED | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_SPRINT, ANIMATION_IS_LOOPED | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_IDLE, ANIMATION_IS_LOOPED }, + { ANIM_ID_ROADCROSS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_WALK_START, ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_RUN_STOP, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_RUN_STOPR, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_IDLE_HBHB_0, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL }, + { ANIM_ID_IDLE_HBHB_1, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL }, + { ANIM_ID_IDLE_TIRED, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_IDLE_ARMED, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL }, + { ANIM_ID_IDLE_CHAT, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL }, + { ANIM_ID_IDLE_TAXI, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_SWIM_TREAD, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_KO_SHOT_FRONT_0, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_800 }, + { ANIM_ID_KO_SHOT_FRONT_1, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_800 }, + { ANIM_ID_KO_SHOT_FRONT_2, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_800 }, + { ANIM_ID_KO_SHOT_FRONT_3, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_800 }, + { ANIM_ID_KO_SHOT_FACE, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_KO_SHOT_STOM, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_GAS_CWR, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_KD_LEFT, ANIMATION_IS_PARTIAL }, + { ANIM_ID_KD_RIGHT, ANIMATION_IS_PARTIAL }, + { ANIM_ID_KO_SKID_FRONT, ANIMATION_IS_PARTIAL }, + { ANIM_ID_KO_SPIN_R, ANIMATION_IS_PARTIAL }, + { ANIM_ID_KO_SKID_BACK, ANIMATION_IS_PARTIAL | ANIMATION_800 }, + { ANIM_ID_KO_SPIN_L, ANIMATION_IS_PARTIAL }, + { ANIM_ID_SHOT_PARTIAL, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_SHOT_LEFTP, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_SHOT_PARTIAL_B, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_SHOT_RIGHTP, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_HIT_FRONT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_HIT_L, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_HIT_BACK, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_HIT_R, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_FLOOR_HIT, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_HIT_WALK, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_HIT_WALL, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FLOOR_HIT_F, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_800 }, + { ANIM_ID_HIT_BEHIND, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_FIGHTSH_FWD, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_FIGHTSH_LEFT, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_FIGHTSH_BWD, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_FIGHTSH_RIGHT, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_FIGHTSHF, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHTSHB, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT2IDLE, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_BOMBER, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_GUN_STAND, ANIMATION_IS_LOOPED }, + { ANIM_ID_GUNMOVE_FWD, ANIMATION_IS_LOOPED | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_GUNMOVE_L, ANIMATION_IS_LOOPED | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_GUNMOVE_BWD, ANIMATION_IS_LOOPED | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_GUNMOVE_R, ANIMATION_IS_LOOPED | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_GUN_2_IDLE, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_WEAPON_CROUCH, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_GUNCROUCHFWD, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_CROUCH_ROLL_L, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY }, + { ANIM_ID_GUNCROUCHBWD, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_WALK }, + { ANIM_ID_CROUCH_ROLL_R, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY }, + { ANIM_ID_CAR_SIT, ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_CAR_LSIT, ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_CAR_SITP, ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_CAR_SITPLO, ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_CAR_SIT_WEAK, ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_CAR_SIT_PRO, ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_DRIVE_L, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_R, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_LO_L, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_LO_R, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_L_WEAK, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_R_WEAK, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_L_PRO, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_R_PRO, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVEBY_L, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVEBY_R, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVEBYL_L, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVEBYL_R, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_CAR_LB, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_CAR_LB_WEAK, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_CAR_LB_PRO, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_BOAT, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_BOAT_L, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_BOAT_R, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_BOAT_BACK, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_L_SLOW, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_R_SLOW, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_L_WEAK_SLOW, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_R_WEAK_SLOW, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_L_PRO_SLOW, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_R_PRO_SLOW, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_TRUCK, ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_DRIVE_TRUCK_L, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_TRUCK_R, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_DRIVE_TRUCK_BACK, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_KART_DRIVE, ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_KART_L, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_KART_R, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_KART_LB, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_PICKUPR, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BIKE_PICKUPL, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BIKE_PULLUPR, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BIKE_PULLUPL, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BIKE_ELBOWL, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BIKE_ELBOWR, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BIKE_FALL_OFF, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_BIKE_FALLR, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_CAR_HOOKERTALK, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DEFAULT_CAR_CRAWLOUTRHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DEFAULT_CAR_CRAWLOUTRHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DEFAULT_CAR_ROLLOUT_LHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_800 }, + { ANIM_ID_DEFAULT_CAR_ROLLOUT_RHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_800 }, + { ANIM_ID_GETUP_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_GETUP_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_GETUP_2, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_GETUP_FRONT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_800 }, + { ANIM_ID_JUMP_LAUNCH, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_JUMP_LAUNCH_R, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_JUMP_GLIDE, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_JUMP_LAND, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FALL_FALL, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_FALL_GLIDE, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_FALL_LAND, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FALL_COLLAPSE, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FALL_BACK, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_FALL_FRONT, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_800 }, + { ANIM_ID_EV_STEP, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_EV_DIVE, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_800 }, + { ANIM_ID_CLIMB_JUMP, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CLIMB_IDLE, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CLIMB_PULL, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CLIMB_STAND, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CLIMB_STAND_FINISH, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CLIMB_JUMP_B, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CLIMB_JUMP2FALL, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_XPRESSSCRATCH, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_200 }, + { ANIM_ID_TURN_180, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_TURN_L, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_TURN_R, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_ARRESTGUN, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_DROWN, ANIMATION_IS_PARTIAL }, + { ANIM_ID_DUCK_COWER, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_HANDSUP, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_HANDSCOWER, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FUCKU, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_PHONE_IN, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_PHONE_OUT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_PHONE_TALK, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_SEAT_DOWN, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_SEAT_UP, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_SEAT_IDLE, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_ATM, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_ABSEIL, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_WALK_DOORPARTIAL, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_FACSURP, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND | ANIMATION_FACIAL }, + { ANIM_ID_FACSURPM, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND | ANIMATION_FACIAL }, + { ANIM_ID_FACURIOS, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND | ANIMATION_FACIAL }, + { ANIM_ID_FACANGER_0, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND | ANIMATION_FACIAL }, + { ANIM_ID_FACANGER_1, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND | ANIMATION_FACIAL }, + { ANIM_ID_FACANGER_2, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND | ANIMATION_FACIAL }, + { ANIM_ID_FACTALK, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND | ANIMATION_FACIAL }, + { ANIM_ID_FACGUM, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND | ANIMATION_FACIAL }, + { ANIM_ID_TAP_HAND, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_TAP_HANDP, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_SHOVE_PARTIAL, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_FLEE_LKAROUND_01, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_ENDCHAT_01, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_ENDCHAT_02, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_ENDCHAT_03, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_SMOKE_IN_CAR, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_PASS_SMOKE_IN_CAR, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_DAM_ARML_FRMBK, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_ARML_FRMFT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_ARML_FRMLT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_ARMR_FRMBK, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_ARMR_FRMFT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_ARMR_FRMRT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_LEGL_FRMBK, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_LEGL_FRMFT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_LEGL_FRMLT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_LEGR_FRMBK, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_LEGR_FRMFT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_LEGR_FRMRT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_STOMACH_FRMBK, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_STOMACH_FRMFT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_STOMACH_FRMLT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DAM_STOMACH_FRMRT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_DEAD_LHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_DEAD_RHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_TUNE_RADIO, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_GANG_GUNSTAND, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE }, }; static AnimDescriptor aQuadDescs[] = { // 0x8A7FE8 12 - { ANIM_ID_BIKE_RIDE, ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_BIKE_STILL, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_LEFT, ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_RIGHT, ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_BACK, ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_FWD, ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_PUSHES, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_HIT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BIKE_DRIVEBYLHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_DRIVEBYRHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_DRIVEBYFT, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_PASSENGER, ANIMATION_FREEZE_LAST_FRAME }, + { ANIM_ID_BIKE_RIDE, ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_BIKE_STILL, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_LEFT, ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_RIGHT, ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_BACK, ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_FWD, ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_PUSHES, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_HIT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BIKE_DRIVEBYLHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_DRIVEBYRHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_DRIVEBYFT, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_PASSENGER, ANIMATION_IS_BLEND_AUTO_REMOVE }, }; static AnimDescriptor aBikesDescs[] = { // 0x8A7F70 12 - { ANIM_ID_BIKE_RIDE, ANIMATION_FREEZE_LAST_FRAME }, - { ANIM_ID_BIKE_STILL, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_LEFT, ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_RIGHT, ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_BACK, ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_FWD, ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_PUSHES, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_HIT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BIKE_DRIVEBYLHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_DRIVEBYRHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_DRIVEBYFT, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, - { ANIM_ID_BIKE_PASSENGER, ANIMATION_FREEZE_LAST_FRAME }, + { ANIM_ID_BIKE_RIDE, ANIMATION_IS_BLEND_AUTO_REMOVE }, + { ANIM_ID_BIKE_STILL, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_LEFT, ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_RIGHT, ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_BACK, ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_FWD, ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_PUSHES, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_HIT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BIKE_DRIVEBYLHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_DRIVEBYRHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_DRIVEBYFT, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_SECONDARY_TASK_ANIM }, + { ANIM_ID_BIKE_PASSENGER, ANIMATION_IS_BLEND_AUTO_REMOVE }, }; static AnimDescriptor aPlayerIdleAnimDescs[] = { // 0x8A8914 4 - { ANIM_ID_STRETCH, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_TIME, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_SHLDR, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_STRLEG, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_STRETCH, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_TIME, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_SHLDR, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_STRLEG, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aDoorDescs[] = { // 0x8A7D88 2 - { ANIM_ID_DOOR_LHINGE_O, ANIMATION_UNLOCK_LAST_FRAME }, - { ANIM_ID_DOOR_RHINGE_O, ANIMATION_UNLOCK_LAST_FRAME }, + { ANIM_ID_DOOR_LHINGE_O, ANIMATION_IS_FINISH_AUTO_REMOVE }, + { ANIM_ID_DOOR_RHINGE_O, ANIMATION_IS_FINISH_AUTO_REMOVE }, }; static AnimDescriptor aWeaponDescs2[] = { // 0x8A81F0 4 - { ANIM_ID_FIRE, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CROUCHFIRE, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_RELOAD, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_CROUCHRELOAD, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_FIRE, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CROUCHFIRE, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_RELOAD, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_CROUCHRELOAD, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aWeaponDescs[] = { // 0x8A81D0 4 - { ANIM_ID_FIRE, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_CROUCHFIRE, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_RELOAD, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_CROUCHRELOAD, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_FIRE, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_CROUCHFIRE, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_RELOAD, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_CROUCHRELOAD, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aWeaponDescs3[] = { // 0x8A8230 3 - { ANIM_ID_GRENADE_WEAPON_START_THROW, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_GRENADE_WEAPON_THROWU, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_GRENADE_WEAPON_THROW, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_GRENADE_WEAPON_START_THROW, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_GRENADE_WEAPON_THROWU, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_GRENADE_WEAPON_THROW, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aWeaponDescs1[] = { // 0x8A8210 4 - { ANIM_ID_FIRE, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_CROUCHFIRE, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_RELOAD, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_CROUCHRELOAD, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_FIRE, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_CROUCHFIRE, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_RELOAD, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_CROUCHRELOAD, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aCarAnimDescs1[] = { // 0x8AA468 40 - { ANIM_ID_CAR_ALIGN_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGN_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGNHI_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGNHI_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_BIKE_FRONT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_PULLOUT_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_PULLOUT_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_UNKNOWN_15, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_SHUFFLE_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_SHUFFLE_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_UNKNOWN_26, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_JACKEDLHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_800 }, - { ANIM_ID_CAR_JACKEDRHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_800 }, - { ANIM_ID_CAR_CLOSE_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ROLLOUT_LHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_800 }, - { ANIM_ID_CAR_ROLLOUT_RHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_800 }, - { ANIM_ID_CAR_ROLLDOOR, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_FALLOUT_LHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_FALLOUT_RHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_DOORLOCKED_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_DOORLOCKED_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_CAR_ALIGN_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGN_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGNHI_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGNHI_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_BIKE_FRONT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_PULLOUT_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_PULLOUT_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_UNKNOWN_15, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_SHUFFLE_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_SHUFFLE_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_UNKNOWN_26, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_JACKEDLHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_800 }, + { ANIM_ID_CAR_JACKEDRHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_800 }, + { ANIM_ID_CAR_CLOSE_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ROLLOUT_LHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_800 }, + { ANIM_ID_CAR_ROLLOUT_RHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_800 }, + { ANIM_ID_CAR_ROLLDOOR, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_FALLOUT_LHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_FALLOUT_RHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_DOORLOCKED_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_DOORLOCKED_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aMedicAnimDescs[] = { // 0x8A88FC 1 - { ANIM_ID_CPR, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_CPR, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aWeaponDescs4[] = { // 0x8A8408 10 - { ANIM_ID_FIGHT_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_2, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_3, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_G, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_FIGHT_M, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_FIGHT_HIT_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_HIT_2, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_HIT_3, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_FIGHT_BLOCK, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_FIGHT_IDLE, ANIMATION_LOOPED | ANIMATION_TRANSLATE_Y }, + { ANIM_ID_FIGHT_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_2, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_3, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_G, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_FIGHT_M, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_FIGHT_HIT_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_HIT_2, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_HIT_3, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_FIGHT_BLOCK, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_FIGHT_IDLE, ANIMATION_IS_LOOPED | ANIMATION_CAN_EXTRACT_VELOCITY }, }; static AnimDescriptor aWeaponDescs5[] = { // 0x8A8458 10 - { ANIM_ID_FIGHT_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_2, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_3, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_G, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_FIGHT_M, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_HIT_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_HIT_2, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_HIT_3, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_FIGHT_FIGHT_BLOCK, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_FIGHT_IDLE, ANIMATION_LOOPED | ANIMATION_TRANSLATE_Y }, + { ANIM_ID_FIGHT_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_2, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_3, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_G, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_FIGHT_M, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_HIT_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_HIT_2, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_HIT_3, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_FIGHT_FIGHT_BLOCK, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_FIGHT_IDLE, ANIMATION_IS_LOOPED | ANIMATION_CAN_EXTRACT_VELOCITY }, }; static AnimDescriptor aBeachAnimDescs[] = { // 0x8A857C 5 - { ANIM_ID_PARKSIT_M_LOOP, ANIMATION_LOOPED | ANIMATION_PARTIAL }, - { ANIM_ID_LAY_BAC_LOOP, ANIMATION_LOOPED | ANIMATION_PARTIAL }, - { ANIM_ID_PARKSIT_W_LOOP, ANIMATION_LOOPED | ANIMATION_PARTIAL }, - { ANIM_ID_BATHER, ANIMATION_LOOPED | ANIMATION_PARTIAL }, - { ANIM_ID_SITNWAIT_LOOP_W, ANIMATION_LOOPED | ANIMATION_PARTIAL }, + { ANIM_ID_PARKSIT_M_LOOP, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL }, + { ANIM_ID_LAY_BAC_LOOP, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL }, + { ANIM_ID_PARKSIT_W_LOOP, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BATHER, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL }, + { ANIM_ID_SITNWAIT_LOOP_W, ANIMATION_IS_LOOPED | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aDbzAnimDescs[] = { // 0x8A8528 8 - { ANIM_ID_DRIVEBYLHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DRIVEBYTOP_LHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DRIVEBYLHS_FWD, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DRIVEBYLHS_BWD, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DRIVEBYRHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DRIVEBYTOP_RHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DRIVEBYRHS_FWD, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DRIVEBYRHS_BWD, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_DRIVEBYLHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DRIVEBYTOP_LHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DRIVEBYLHS_FWD, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DRIVEBYLHS_BWD, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DRIVEBYRHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DRIVEBYTOP_RHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DRIVEBYRHS_FWD, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DRIVEBYRHS_BWD, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aSunbatheAnimDescs[] = { // 0x8A85E8 16 - { ANIM_ID_PARKSIT_M_IN, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X }, - { ANIM_ID_LAY_BAC_IN, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X }, - { ANIM_ID_PARKSIT_W_IN, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X }, - { ANIM_ID_BATHERDOWN, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X }, - { ANIM_ID_SITNWAIT_IN_W, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X }, - { ANIM_ID_PARKSIT_M_OUT, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X }, - { ANIM_ID_LAY_BAC_OUT, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X }, - { ANIM_ID_PARKSIT_W_OUT, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X }, - { ANIM_ID_BATHERUP, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X }, - { ANIM_ID_SITNWAIT_OUT_W, ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X }, - { ANIM_ID_PARKSIT_M_IDLEA, ANIMATION_PARTIAL }, - { ANIM_ID_PARKSIT_M_IDLEB, ANIMATION_PARTIAL }, - { ANIM_ID_PARKSIT_M_IDLEC, ANIMATION_PARTIAL }, - { ANIM_ID_PARKSIT_W_IDLEA, ANIMATION_PARTIAL }, - { ANIM_ID_PARKSIT_W_IDLEB, ANIMATION_PARTIAL }, - { ANIM_ID_PARKSIT_W_IDLEC, ANIMATION_PARTIAL }, + { ANIM_ID_PARKSIT_M_IN, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY }, + { ANIM_ID_LAY_BAC_IN, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY }, + { ANIM_ID_PARKSIT_W_IN, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY }, + { ANIM_ID_BATHERDOWN, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY }, + { ANIM_ID_SITNWAIT_IN_W, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY }, + { ANIM_ID_PARKSIT_M_OUT, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY }, + { ANIM_ID_LAY_BAC_OUT, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY }, + { ANIM_ID_PARKSIT_W_OUT, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY }, + { ANIM_ID_BATHERUP, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY }, + { ANIM_ID_SITNWAIT_OUT_W, ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY }, + { ANIM_ID_PARKSIT_M_IDLEA, ANIMATION_IS_PARTIAL }, + { ANIM_ID_PARKSIT_M_IDLEB, ANIMATION_IS_PARTIAL }, + { ANIM_ID_PARKSIT_M_IDLEC, ANIMATION_IS_PARTIAL }, + { ANIM_ID_PARKSIT_W_IDLEA, ANIMATION_IS_PARTIAL }, + { ANIM_ID_PARKSIT_W_IDLEB, ANIMATION_IS_PARTIAL }, + { ANIM_ID_PARKSIT_W_IDLEC, ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aRiotAnimDescs[] = { // 0x8A8950 7 - { ANIM_ID_RIOT_ANGRY, ANIMATION_LOOPED | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_RIOT_ANGRY_B, ANIMATION_LOOPED | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_RIOT_CHANT, ANIMATION_LOOPED | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_RIOT_PUNCHES, ANIMATION_LOOPED | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_RIOT_SHOUT, ANIMATION_LOOPED | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_RIOT_CHALLENGE, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_RIOT_FUKU, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_RIOT_ANGRY, ANIMATION_IS_LOOPED | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_RIOT_ANGRY_B, ANIMATION_IS_LOOPED | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_RIOT_CHANT, ANIMATION_IS_LOOPED | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_RIOT_PUNCHES, ANIMATION_IS_LOOPED | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_RIOT_SHOUT, ANIMATION_IS_LOOPED | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_RIOT_CHALLENGE, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_RIOT_FUKU, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aStripAnimDescs[] = { // 0x8A89A4 7 - { ANIM_ID_STRIP_A, ANIMATION_LOOPED | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_STRIP_B, ANIMATION_LOOPED | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_STRIP_C, ANIMATION_LOOPED | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_STRIP_D, ANIMATION_LOOPED | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_STRIP_E, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_STRIP_F, ANIMATION_LOOPED | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_STRIP_G, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_STRIP_A, ANIMATION_IS_LOOPED | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_STRIP_B, ANIMATION_IS_LOOPED | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_STRIP_C, ANIMATION_IS_LOOPED | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_STRIP_D, ANIMATION_IS_LOOPED | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_STRIP_E, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_STRIP_F, ANIMATION_IS_LOOPED | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_STRIP_G, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aGangsAnimDescs[] = { // 0x8A86E0 29 - { ANIM_ID_PRTIAL_GNGTLKA, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_PRTIAL_GNGTLKB, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_PRTIAL_GNGTLKC, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_PRTIAL_GNGTLKD, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_PRTIAL_GNGTLKE, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_PRTIAL_GNGTLKF, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_PRTIAL_GNGTLKG, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_PRTIAL_GNGTLKH, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_DEALER_DEAL, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DRUGS_BUY, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_HNDSHKAA, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_HNDSHKBA, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_HNDSHKCA, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_HNDSHKCB, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_HNDSHKDA, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_HNDSHKEA, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_HNDSHKFA, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_SHAKE_CARA, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_SHAKE_CARSH, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_SHAKE_CARK, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_DRNKBR_PRTL, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_SMKCIG_PRTL, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_DRNKBR_PRTL_F, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_SMKCIG_PRTL_F, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_LEANIN, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_LEANIDLE, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_LEANOUT, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_INVITE_YES, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_INVITE_NO, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_PRTIAL_GNGTLKA, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_PRTIAL_GNGTLKB, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_PRTIAL_GNGTLKC, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_PRTIAL_GNGTLKD, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_PRTIAL_GNGTLKE, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_PRTIAL_GNGTLKF, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_PRTIAL_GNGTLKG, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_PRTIAL_GNGTLKH, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_DEALER_DEAL, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DRUGS_BUY, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_HNDSHKAA, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_HNDSHKBA, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_HNDSHKCA, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_HNDSHKCB, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_HNDSHKDA, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_HNDSHKEA, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_HNDSHKFA, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_SHAKE_CARA, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_SHAKE_CARSH, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_SHAKE_CARK, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_DRNKBR_PRTL, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_SMKCIG_PRTL, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_DRNKBR_PRTL_F, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_SMKCIG_PRTL_F, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_LEANIN, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_LEANIDLE, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_LEANOUT, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_INVITE_YES, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_INVITE_NO, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aAttractorsAnimDescs[] = { // 0x8A87D4 3 - { ANIM_ID_STEPSIT_IN, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_STEPSIT_OUT, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_STEPSIT_LOOP, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_STEPSIT_IN, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_STEPSIT_OUT, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_STEPSIT_LOOP, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aSwimAnimDescs[] = { // 0x8A8C54 6 - { ANIM_ID_SWIM_BREAST, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_SWIM_CRAWL, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_MOVEMENT | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_SWIM_DIVE_UNDER, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_SWIM_UNDER, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, - { ANIM_ID_SWIM_GLIDE, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_SWIM_JUMPOUT, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_SWIM_BREAST, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_SWIM_CRAWL, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_IS_SYNCRONISED | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_SWIM_DIVE_UNDER, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_SWIM_UNDER, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, + { ANIM_ID_SWIM_GLIDE, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_SWIM_JUMPOUT, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aFatTiredAnimDescs[] = { // 0x8A8CC4 1 - { ANIM_ID_IDLE_TIRED, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_IDLE_TIRED, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aHandSignalAnimDescs[] = { // 0x8A8A2C 5 - { ANIM_ID_GSIGN1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_GSIGN2, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_GSIGN3, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_GSIGN4, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_GSIGN5, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_GSIGN1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_GSIGN2, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_GSIGN3, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_GSIGN4, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_GSIGN5, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aHandSignalLAnimDescs[] = { // 0x8A8A54 5 - { ANIM_ID_GSIGN1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_GSIGN2, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_GSIGN3, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_GSIGN4, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_GSIGN5, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_GSIGN1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_GSIGN2, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_GSIGN3, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_GSIGN4, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_GSIGN5, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aHandAnimDescs[] = { // 0x8A8A7C 5 @@ -472,217 +472,217 @@ static AnimDescriptor aHandAnimDescs[] = { // 0x8A8A7C 5 }; static AnimDescriptor aCarryDescs[] = { // 0x8A8CA8 3 - { ANIM_ID_LIFTUP, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CRRY_PRTIAL, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_PUTDWN, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_LIFTUP, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CRRY_PRTIAL, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_PUTDWN, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aIntHouseAnimDescs[] = { // 0x8A8848 10 - { ANIM_ID_BED_IN_L, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BED_LOOP_L, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BED_OUT_L, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BED_IN_R, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BED_LOOP_R, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_BED_OUT_R, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_LOU_IN, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_LOU_OUT, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_LOU_LOOP, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_WASH_UP, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_BED_IN_L, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BED_LOOP_L, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BED_OUT_L, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BED_IN_R, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BED_LOOP_R, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_BED_OUT_R, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_LOU_IN, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_LOU_OUT, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_LOU_LOOP, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_WASH_UP, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aIntOfficeAnimDescs[] = { // 0x8A8898 9 - { ANIM_ID_OFF_SIT_IN, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_OFF_SIT_2IDLE_180, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_OFF_SIT_IDLE_LOOP, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_OFF_SIT_TYPE_LOOP, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_OFF_SIT_BORED_LOOP, ANIMATION_LOOPED | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_OFF_SIT_CRASH, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_OFF_SIT_DRINK, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_OFF_SIT_READ, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_OFF_SIT_WATCH, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_OFF_SIT_IN, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_OFF_SIT_2IDLE_180, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_OFF_SIT_IDLE_LOOP, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_OFF_SIT_TYPE_LOOP, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_OFF_SIT_BORED_LOOP, ANIMATION_IS_LOOPED | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_OFF_SIT_CRASH, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_OFF_SIT_DRINK, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_OFF_SIT_READ, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_OFF_SIT_WATCH, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aLowCarAnimDiscs[] = { // 0x8AA0A8 40 - { ANIM_ID_CAR_ALIGN_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGN_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGNHI_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGNHI_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_BIKE_FRONT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_PULLOUT_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_PULLOUT_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_UNKNOWN_15, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_SHUFFLE_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_SHUFFLE_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_UNKNOWN_26, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_JACKEDLHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_JACKEDRHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ROLLOUT_LHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_800 }, - { ANIM_ID_CAR_ROLLOUT_RHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_800 }, - { ANIM_ID_CAR_ROLLDOOR, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_FALLOUT_LHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_FALLOUT_RHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_DOORLOCKED_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_DOORLOCKED_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_CAR_ALIGN_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGN_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGNHI_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGNHI_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_BIKE_FRONT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_PULLOUT_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_PULLOUT_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_UNKNOWN_15, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_SHUFFLE_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_SHUFFLE_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_UNKNOWN_26, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_JACKEDLHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_JACKEDRHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ROLLOUT_LHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_800 }, + { ANIM_ID_CAR_ROLLOUT_RHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_800 }, + { ANIM_ID_CAR_ROLLDOOR, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_FALLOUT_LHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_FALLOUT_RHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_DOORLOCKED_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_DOORLOCKED_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aIntShopAnimDescs[] = { // 0x8A88E0 3 - { ANIM_ID_SHOP_SHELF, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_SHOP_PAY, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_SHOP_CASHIER, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_SHOP_SHELF, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_SHOP_PAY, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_SHOP_CASHIER, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aStealthDescs[] = { // 0x8A84B8 4 - { ANIM_ID_KILL_PARTIAL, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_ADD_TO_BLEND }, - { ANIM_ID_KILL_KNIFE_PLAYER, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_KILL_KNIFE_PED_DAMAGE, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_KILL_KNIFE_PED_DIE, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y }, + { ANIM_ID_KILL_PARTIAL, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_DONT_ADD_TO_PARTIAL_BLEND }, + { ANIM_ID_KILL_KNIFE_PLAYER, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_KILL_KNIFE_PED_DAMAGE, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_KILL_KNIFE_PED_DIE, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY }, }; static AnimDescriptor aCarAnimDescs2[] = { // 0x8A9F68 40 - { ANIM_ID_CAR_ALIGN_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGN_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGNHI_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGNHI_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_BIKE_FRONT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_PULLOUT_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_PULLOUT_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_UNKNOWN_15, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_SHUFFLE_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_SHUFFLE_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_UNKNOWN_26, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_JACKEDLHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_JACKEDRHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ROLLOUT_LHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_800 }, - { ANIM_ID_CAR_ROLLOUT_RHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_800 }, - { ANIM_ID_CAR_ROLLDOOR, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_FALLOUT_LHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_FALLOUT_RHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_DOORLOCKED_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_DOORLOCKED_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_CAR_ALIGN_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGN_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGNHI_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGNHI_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_BIKE_FRONT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_PULLOUT_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_PULLOUT_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_UNKNOWN_15, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_SHUFFLE_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_SHUFFLE_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_UNKNOWN_26, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_JACKEDLHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_JACKEDRHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ROLLOUT_LHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_800 }, + { ANIM_ID_CAR_ROLLOUT_RHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_800 }, + { ANIM_ID_CAR_ROLLDOOR, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_FALLOUT_LHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_FALLOUT_RHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_DOORLOCKED_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_DOORLOCKED_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aTruckAnimDescs[] = { // 0x8AA1E8 40 - { ANIM_ID_CAR_ALIGN_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGN_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGNHI_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGNHI_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_BIKE_FRONT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_PULLOUT_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_PULLOUT_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_UNKNOWN_15, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_SHUFFLE_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_SHUFFLE_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_UNKNOWN_26, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_JACKEDLHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_JACKEDRHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ROLLOUT_LHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_800 }, - { ANIM_ID_CAR_ROLLOUT_RHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_800 }, - { ANIM_ID_CAR_ROLLDOOR, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_FALLOUT_LHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_FALLOUT_RHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_DOORLOCKED_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_DOORLOCKED_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_CAR_ALIGN_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGN_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGNHI_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGNHI_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_BIKE_FRONT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_PULLOUT_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_PULLOUT_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_UNKNOWN_15, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_SHUFFLE_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_SHUFFLE_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_UNKNOWN_26, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_JACKEDLHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_JACKEDRHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ROLLOUT_LHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_800 }, + { ANIM_ID_CAR_ROLLOUT_RHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_800 }, + { ANIM_ID_CAR_ROLLDOOR, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_FALLOUT_LHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_FALLOUT_RHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_DOORLOCKED_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_DOORLOCKED_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; static AnimDescriptor aStdBikeAnimDescs[] = { // 0x8AA328 40 - { ANIM_ID_CAR_ALIGN_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGN_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGNHI_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ALIGNHI_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_OPEN_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETIN_BIKE_FRONT, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_PULLOUT_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_PULLOUT_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_UNKNOWN_15, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSEDOOR_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_SHUFFLE_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_SHUFFLE_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_GETOUT_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_UNKNOWN_26, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_JACKEDLHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_800 }, - { ANIM_ID_CAR_JACKEDRHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_800 }, - { ANIM_ID_CAR_CLOSE_LHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_RHS_0, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_LHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_CLOSE_RHS_1, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_ROLLOUT_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_800 }, - { ANIM_ID_CAR_ROLLOUT_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL | ANIMATION_TRANSLATE_Y | ANIMATION_TRANSLATE_X | ANIMATION_800 }, - { ANIM_ID_CAR_ROLLDOOR, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_FALLOUT_LHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_FALLOUT_RHS, ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_DOORLOCKED_LHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, - { ANIM_ID_CAR_DOORLOCKED_RHS, ANIMATION_UNLOCK_LAST_FRAME | ANIMATION_PARTIAL }, + { ANIM_ID_CAR_ALIGN_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGN_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGNHI_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ALIGNHI_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_OPEN_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETIN_BIKE_FRONT, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_PULLOUT_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_PULLOUT_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_UNKNOWN_15, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSEDOOR_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_SHUFFLE_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_SHUFFLE_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_GETOUT_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_UNKNOWN_26, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_JACKEDLHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_800 }, + { ANIM_ID_CAR_JACKEDRHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_800 }, + { ANIM_ID_CAR_CLOSE_LHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_RHS_0, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_LHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_CLOSE_RHS_1, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_ROLLOUT_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_800 }, + { ANIM_ID_CAR_ROLLOUT_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL | ANIMATION_CAN_EXTRACT_VELOCITY | ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_800 }, + { ANIM_ID_CAR_ROLLDOOR, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_FALLOUT_LHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_FALLOUT_RHS, ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_DOORLOCKED_LHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, + { ANIM_ID_CAR_DOORLOCKED_RHS, ANIMATION_IS_FINISH_AUTO_REMOVE | ANIMATION_IS_PARTIAL }, }; diff --git a/source/game_sa/Animation/AnimBlendAssociation.cpp b/source/game_sa/Animation/AnimBlendAssociation.cpp index d8fd747413..43642f5b08 100644 --- a/source/game_sa/Animation/AnimBlendAssociation.cpp +++ b/source/game_sa/Animation/AnimBlendAssociation.cpp @@ -62,9 +62,9 @@ CAnimBlendAssociation::CAnimBlendAssociation(CAnimBlendAssociation& assoc) { m_TimeStep = 0.0f; m_nCallbackType = ANIM_BLEND_CALLBACK_NONE; Init(assoc); - if ((m_Flags & ANIMATION_BLOCK_REFERENCED) == 0) { + if ((m_Flags & ANIMATION_REFERENCE_BLOCK) == 0) { CAnimManager::AddAnimBlockRef(m_BlendHier->m_nAnimBlockId); - m_Flags |= ANIMATION_BLOCK_REFERENCED; + m_Flags |= ANIMATION_REFERENCE_BLOCK; } } @@ -78,9 +78,9 @@ CAnimBlendAssociation::CAnimBlendAssociation(CAnimBlendStaticAssociation& assoc) m_TimeStep = 0.0f; m_nCallbackType = ANIM_BLEND_CALLBACK_NONE; Init(assoc); - if ((m_Flags & ANIMATION_BLOCK_REFERENCED) == 0) { + if ((m_Flags & ANIMATION_REFERENCE_BLOCK) == 0) { CAnimManager::AddAnimBlockRef(m_BlendHier->m_nAnimBlockId); - m_Flags |= ANIMATION_BLOCK_REFERENCED; + m_Flags |= ANIMATION_REFERENCE_BLOCK; } } @@ -90,7 +90,7 @@ CAnimBlendAssociation::~CAnimBlendAssociation() { CMemoryMgr::FreeAlign(m_BlendNodes); } m_Link.Remove(); - if (m_Flags & ANIMATION_BLOCK_REFERENCED) { + if (m_Flags & ANIMATION_REFERENCE_BLOCK) { CAnimManager::RemoveAnimBlockRef(m_BlendHier->m_nAnimBlockId); } } @@ -101,8 +101,8 @@ float CAnimBlendAssociation::GetTimeProgress() const { // 0x4CED50 void CAnimBlendAssociation::Init(RpClump* clump, CAnimBlendHierarchy* animHierarchy) { - CAnimBlendClumpData* animClumpData = RpClumpGetAnimBlendClumpData(clump); - m_NumBlendNodes = animClumpData->m_NumFrames; + CAnimBlendClumpData* animClumpData = RpAnimBlendClumpGetData(clump); + m_NumBlendNodes = animClumpData->m_NumFrameData; AllocateAnimBlendNodeArray(m_NumBlendNodes); for (auto i = 0; i < m_NumBlendNodes; i++) { m_BlendNodes[i].m_BlendAssoc = this; @@ -119,7 +119,7 @@ void CAnimBlendAssociation::Init(RpClump* clump, CAnimBlendHierarchy* animHierar if (!frame) { continue; } - m_BlendNodes[frame - animClumpData->m_Frames].m_Seq = &seq; + m_BlendNodes[frame - animClumpData->m_FrameDatas].m_Seq = &seq; } } @@ -153,7 +153,7 @@ void CAnimBlendAssociation::Init(CAnimBlendStaticAssociation& assoc) { // 0x4CEB70 void CAnimBlendAssociation::Start(float currentTime) { - m_Flags |= ANIMATION_STARTED; + m_Flags |= ANIMATION_IS_PLAYING; SetCurrentTime(currentTime); } @@ -190,7 +190,7 @@ void CAnimBlendAssociation::SetBlendTo(float blendAmount, float blendDelta) { // 0x4CEA80 void CAnimBlendAssociation::SetCurrentTime(float currentTime) { for (m_CurrentTime = currentTime; m_CurrentTime >= m_BlendHier->m_fTotalTime; m_CurrentTime -= m_BlendHier->m_fTotalTime) { - if (!IsRepeating()) { + if (!IsLooped()) { m_CurrentTime = m_BlendHier->m_fTotalTime; break; } @@ -234,13 +234,14 @@ void CAnimBlendAssociation::SyncAnimation(CAnimBlendAssociation* syncWith) { } // 0x4D1490 -bool CAnimBlendAssociation::UpdateBlend(float mult) { - m_BlendAmount += mult * m_BlendDelta; - if (m_BlendAmount <= 0.0f && m_BlendDelta < 0.0f) { - // We're faded out and are not fading in +bool CAnimBlendAssociation::UpdateBlend(float timeStep) { + m_BlendAmount += m_BlendDelta * timeStep; + + if (m_BlendAmount <= 0.0f && m_BlendDelta < 0.0f) { // We're faded out and are not fading in m_BlendAmount = 0.0f; - m_BlendDelta = std::max(0.0f, m_BlendDelta); - if (m_Flags & ANIMATION_FREEZE_LAST_FRAME) { + m_BlendDelta = std::max(0.0f, m_BlendDelta); + + if (m_Flags & ANIMATION_IS_BLEND_AUTO_REMOVE) { if (m_nCallbackType == ANIM_BLEND_CALLBACK_DELETE || m_nCallbackType == ANIM_BLEND_CALLBACK_FINISH) { // condition simplified m_pCallbackFunc(this, m_pCallbackData); SetFinishCallback(CDefaultAnimCallback::DefaultAnimCB, nullptr); @@ -250,60 +251,70 @@ bool CAnimBlendAssociation::UpdateBlend(float mult) { } } - if (m_BlendAmount > 1.0f) { - // Maximally faded in, clamp values + if (m_BlendAmount > 1.0f) { // Maximally faded in, clamp values m_BlendAmount = 1.0f; - m_BlendDelta = std::min(0.0f, m_BlendDelta); + m_BlendDelta = std::min(0.0f, m_BlendDelta); } return true; } // 0x4D13D0 -bool CAnimBlendAssociation::UpdateTime(float a1, float a2) { - UNUSED(a1); - UNUSED(a2); +bool CAnimBlendAssociation::UpdateTime(float timeStep, float timeMult) { + UNUSED(timeStep); + UNUSED(timeMult); - if (!IsRunning()) + if (!IsPlaying()) { return true; + } - if (m_CurrentTime >= (double)m_BlendHier->m_fTotalTime) { - SetFlag(ANIMATION_STARTED, true); + // Finished yet? + if (m_CurrentTime >= (double)m_BlendHier->GetTotalTime()) { + SetFlag(ANIMATION_IS_PLAYING, false); return true; } + // Okay, so advance... m_CurrentTime += m_TimeStep; - if (m_CurrentTime < m_BlendHier->m_fTotalTime) { + + // Is it finished now? + if (m_CurrentTime < m_BlendHier->GetTotalTime()) { return true; } - if (IsRepeating()) { - m_CurrentTime -= m_BlendHier->m_fTotalTime; + // Should it be repeating? If so, jump to beginning + if (IsLooped()) { + m_CurrentTime -= m_BlendHier->GetTotalTime(); return true; } - m_CurrentTime = m_BlendHier->m_fTotalTime; - if (m_Flags & ANIMATION_UNLOCK_LAST_FRAME) { - SetFlag(ANIMATION_FREEZE_LAST_FRAME, true); + // Anim has finished + m_CurrentTime = m_BlendHier->GetTotalTime(); + + // Maybe auto-remove + if (m_Flags & ANIMATION_IS_FINISH_AUTO_REMOVE) { + SetFlag(ANIMATION_IS_BLEND_AUTO_REMOVE, true); m_BlendDelta = -4.0f; } + // Call finish callback (if any) if (m_nCallbackType == ANIM_BLEND_CALLBACK_FINISH) { m_nCallbackType = ANIM_BLEND_CALLBACK_NONE; m_pCallbackFunc(this, m_pCallbackData); SetFinishCallback(CDefaultAnimCallback::DefaultAnimCB, nullptr); } + + // And we're done return true; } // 0x4D13A0 -void CAnimBlendAssociation::UpdateTimeStep(float speedMult, float timeMult) { - if (IsRunning()) { - if (IsMoving()) { - m_TimeStep = m_BlendHier->m_fTotalTime * timeMult * speedMult; - } else { - m_TimeStep = m_Speed * speedMult; - } +void CAnimBlendAssociation::UpdateTimeStep(float timeStep, float totalTimeRecp) { + if (IsPlaying()) { + m_TimeStep = IsSyncronised() + ? m_BlendHier->m_fTotalTime * totalTimeRecp + : m_Speed; + m_TimeStep *= timeStep; } } @@ -313,14 +324,13 @@ bool CAnimBlendAssociation::HasFinished() const { // 0x4CEA50 void CAnimBlendAssociation::ReferenceAnimBlock() { - if (m_Flags & ANIMATION_BLOCK_REFERENCED) { + if (m_Flags & ANIMATION_REFERENCE_BLOCK) { return; } CAnimManager::AddAnimBlockRef(m_BlendHier->m_nAnimBlockId); - SetFlag(ANIMATION_FREEZE_TRANSLATION, true); + SetFlag(ANIMATION_IGNORE_ROOT_TRANSLATION, true); } -// 0x4CEB60 -CAnimBlendNode* CAnimBlendAssociation::GetNode(int32 nodeIndex) { - return &m_BlendNodes[nodeIndex]; +std::span CAnimBlendAssociation::GetNodes() { + return std::span{ m_BlendNodes, m_NumBlendNodes }; } diff --git a/source/game_sa/Animation/AnimBlendAssociation.h b/source/game_sa/Animation/AnimBlendAssociation.h index c43fca1b08..9196ef3010 100644 --- a/source/game_sa/Animation/AnimBlendAssociation.h +++ b/source/game_sa/Animation/AnimBlendAssociation.h @@ -17,23 +17,27 @@ class CAnimBlendHierarchy; class CAnimBlendStaticAssociation; enum eAnimationFlags { - ANIMATION_DEFAULT = 0, //0x0, - ANIMATION_STARTED = 1 << 0, //0x1, - ANIMATION_LOOPED = 1 << 1, //0x2, - ANIMATION_FREEZE_LAST_FRAME = 1 << 2, //0x4, - ANIMATION_UNLOCK_LAST_FRAME = 1 << 3, //0x8, // Animation will be stuck on last frame, if not set - ANIMATION_PARTIAL = 1 << 4, //0x10, // TODO: Flag name is possibly incorrect? Following the usual logic (like `ANIMATION_MOVEMENT`), it should be `ANIMATION_GET_IN_CAR` (See `RemoveGetInAnims`) - ANIMATION_MOVEMENT = 1 << 5, //0x20, - ANIMATION_TRANSLATE_Y = 1 << 6, //0x40, - ANIMATION_TRANSLATE_X = 1 << 7, //0x80, - ANIMATION_WALK = 1 << 8, //0x100, - ANIMATION_200 = 1 << 9, //0x200, - ANIMATION_ADD_TO_BLEND = 1 << 10, //0x400, // Possibly should be renamed to ANIMATION_IDLE, see `CPed::PlayFootSteps()` - ANIMATION_800 = 1 << 11, //0x800, - ANIMATION_SECONDARY_TASK_ANIM= 1 << 12, //0x1000, - ANIMATION_FREEZE_TRANSLATION = 1 << 13, //0x2000, - ANIMATION_BLOCK_REFERENCED = 1 << 14, //0x4000, - ANIMATION_INDESTRUCTIBLE = 1 << 15, //0x8000 // The animation is never destroyed if this flag is set, NO MATTER WHAT + ANIMATION_DEFAULT = 0, //0x0, + ANIMATION_IS_PLAYING = 1 << 0, //0x1, + ANIMATION_IS_LOOPED = 1 << 1, //0x2, + ANIMATION_IS_BLEND_AUTO_REMOVE = 1 << 2, //0x4, + ANIMATION_IS_FINISH_AUTO_REMOVE = 1 << 3, //0x8, // Animation will be stuck on last frame, if not set + ANIMATION_IS_PARTIAL = 1 << 4, //0x10, // TODO: Flag name is possibly incorrect? Following the usual logic (like `ANIMATION_MOVEMENT`), it should be `ANIMATION_GET_IN_CAR` (See `RemoveGetInAnims`) + ANIMATION_IS_SYNCRONISED = 1 << 5, //0x20, + ANIMATION_CAN_EXTRACT_VELOCITY = 1 << 6, //0x40, + ANIMATION_CAN_EXTRACT_X_VELOCITY = 1 << 7, //0x80, + + // ** User defined flags ** + ANIMATION_WALK = 1 << 8, //0x100, + ANIMATION_200 = 1 << 9, //0x200, + ANIMATION_DONT_ADD_TO_PARTIAL_BLEND = 1 << 10, //0x400, // Possibly should be renamed to ANIMATION_IDLE, see `CPed::PlayFootSteps()` + ANIMATION_800 = 1 << 11, //0x800, + ANIMATION_SECONDARY_TASK_ANIM = 1 << 12, //0x1000, + // ** + + ANIMATION_IGNORE_ROOT_TRANSLATION = 1 << 13, //0x2000, + ANIMATION_REFERENCE_BLOCK = 1 << 14, //0x4000, + ANIMATION_FACIAL = 1 << 15, //0x8000 // The animation is never destroyed if this flag is set, NO MATTER WHAT }; class CDefaultAnimCallback { @@ -45,6 +49,35 @@ class CDefaultAnimCallback { class CAnimBlendLink { public: + template + struct BaseIterator { + public: + using iterator_category = std::forward_iterator_tag; // Actually it's bidirectional, but there are quirks, so let's pretend like its not + using difference_type = std::ptrdiff_t; + using value_type = Y; + using pointer = Y*; + using reference = Y&; + + BaseIterator() = default; + BaseIterator(CAnimBlendLink* link) : m_Link{ link } {} + + reference operator*() const { return *DeRefLink(); } + pointer operator->() { return DeRefLink(); } + + auto& operator++() { assert(m_Link); m_Link = DeRefLink()->GetLink().next; return *this; } + auto operator++(int) { const auto tmp{ *this }; ++(*this); return tmp; } + + friend bool operator==(const BaseIterator& lhs, const BaseIterator& rhs) { return lhs.m_Link == rhs.m_Link; } + friend bool operator!=(const BaseIterator& lhs, const BaseIterator& rhs) { return !(lhs == rhs); } + private: + auto DeRefLink() const { return CAnimBlendAssociation::FromLink(m_Link); } + private: + CAnimBlendLink* m_Link; + }; + + using iterator = BaseIterator; + using const_iterator = BaseIterator; + CAnimBlendLink* next{}; CAnimBlendLink* prev{}; @@ -73,6 +106,11 @@ class CAnimBlendLink { } Init(); } + + bool IsEmpty() const { return !next; } + + auto begin() { return iterator{this}; } + auto end() { return iterator{nullptr}; } }; /*! @@ -92,15 +130,15 @@ class NOTSA_EXPORT_VTABLE CAnimBlendAssociation { CAnimBlendLink m_Link; uint16 m_NumBlendNodes; notsa::WEnumS16 m_AnimGroupId; - CAnimBlendNode* m_BlendNodes; // NOTE: Order of these depends on order of nodes in Clump this was built from + CAnimBlendNode* m_BlendNodes; //!< Node per-node animations - NOTE: Order of these depends on order of nodes in Clump this was built from CAnimBlendHierarchy* m_BlendHier; float m_BlendAmount; - float m_BlendDelta; // How much `BlendAmount` changes over time + float m_BlendDelta; //!< How much `BlendAmount` changes over time float m_CurrentTime; float m_Speed; float m_TimeStep; notsa::WEnumS16 m_AnimId; - uint16 m_Flags; // TODO: use bitfield + uint16 m_Flags; // Callback shit eAnimBlendCallbackType m_nCallbackType; @@ -124,17 +162,16 @@ class NOTSA_EXPORT_VTABLE CAnimBlendAssociation { AnimationId GetAnimId() const { return m_AnimId; } - [[nodiscard]] bool IsRunning() const { return (m_Flags & ANIMATION_STARTED) != 0; } - [[nodiscard]] bool IsRepeating() const { return (m_Flags & ANIMATION_LOOPED) != 0; } - [[nodiscard]] bool IsPartial() const { return (m_Flags & ANIMATION_PARTIAL) != 0; } - [[nodiscard]] bool IsMoving() const { return (m_Flags & ANIMATION_MOVEMENT) != 0; } - [[nodiscard]] bool HasYTranslation() const { return (m_Flags & ANIMATION_TRANSLATE_X) != 0; } - [[nodiscard]] bool HasXTranslation() const { return (m_Flags & ANIMATION_TRANSLATE_Y) != 0; } - [[nodiscard]] bool IsIndestructible() const { return (m_Flags & ANIMATION_INDESTRUCTIBLE) != 0; } + [[nodiscard]] bool IsPlaying() const { return (m_Flags & ANIMATION_IS_PLAYING) != 0; } + [[nodiscard]] bool IsLooped() const { return (m_Flags & ANIMATION_IS_LOOPED) != 0; } + [[nodiscard]] bool IsPartial() const { return (m_Flags & ANIMATION_IS_PARTIAL) != 0; } + [[nodiscard]] bool IsSyncronised() const { return (m_Flags & ANIMATION_IS_SYNCRONISED) != 0; } + [[nodiscard]] bool CanExtractXVelocity() const { return (m_Flags & ANIMATION_CAN_EXTRACT_X_VELOCITY) != 0; } + [[nodiscard]] bool CanExtractVelocity() const { return (m_Flags & ANIMATION_CAN_EXTRACT_VELOCITY) != 0; } + [[nodiscard]] bool IsFacial() const { return (m_Flags & ANIMATION_FACIAL) != 0; } void AllocateAnimBlendNodeArray(int32 count); void FreeAnimBlendNodeArray(); - CAnimBlendNode* GetNode(int32 nodeIndex); void Init(RpClump* clump, CAnimBlendHierarchy* hierarchy); void Init(CAnimBlendAssociation& source); @@ -154,8 +191,8 @@ class NOTSA_EXPORT_VTABLE CAnimBlendAssociation { * @brief Sync the play time of this animation with another */ void SyncAnimation(CAnimBlendAssociation* syncWith); - bool UpdateBlend(float mult); - bool UpdateTime(float a1, float a2); + bool UpdateBlend(float timeStep); + bool UpdateTime(float timeStep, float timeMult); void UpdateTimeStep(float speedMult, float timeMult); bool HasFinished() const; [[nodiscard]] uint32 GetHashKey() const noexcept; @@ -168,17 +205,25 @@ class NOTSA_EXPORT_VTABLE CAnimBlendAssociation { m_Flags &= ~(int)flag; } + bool HasFlag(eAnimationFlags flag) const { return m_Flags & flag; } + static CAnimBlendAssociation* FromLink(CAnimBlendLink* link) { return (CAnimBlendAssociation*)((byte*)link - offsetof(CAnimBlendAssociation, m_Link)); } - void SetSpeed(float speed) { - m_Speed = speed; - } + auto GetSpeed() const { return m_Speed; } + void SetSpeed(float speed) { m_Speed = speed; } + + + std::span GetNodes(); + CAnimBlendNode* GetNode(int32 nodeIndex) { return &GetNodes()[nodeIndex]; } // 0x4CEB60 + CAnimBlendNode* GetNodesPtr() { return m_BlendNodes; } - auto GetNodes() { return std::span{ &m_BlendNodes, m_NumBlendNodes }; } void SetDefaultFinishCallback() { SetFinishCallback(CDefaultAnimCallback::DefaultAnimCB, nullptr); } - auto GetHier() const { return m_BlendHier; } + + auto& GetLink() { return m_Link; } + auto GetHier() const { return m_BlendHier; } + private: friend void InjectHooksMain(); static void InjectHooks(); diff --git a/source/game_sa/Animation/AnimBlendClumpData.cpp b/source/game_sa/Animation/AnimBlendClumpData.cpp index 1e809a4883..f1b1e7ab20 100644 --- a/source/game_sa/Animation/AnimBlendClumpData.cpp +++ b/source/game_sa/Animation/AnimBlendClumpData.cpp @@ -16,31 +16,31 @@ void CAnimBlendClumpData::InjectHooks() { // 0x4CF0E0 CAnimBlendClumpData::CAnimBlendClumpData() { - m_NumFrames = 0; + m_NumFrameData = 0; m_PedPosition = nullptr; - m_Frames = nullptr; + m_FrameDatas = nullptr; } // 0x4CF100 CAnimBlendClumpData::~CAnimBlendClumpData() { - m_Anims.Remove(); - if (m_Frames) { - CMemoryMgr::FreeAlign(&m_Frames); + m_AnimList.Remove(); + if (m_FrameDatas) { + CMemoryMgr::FreeAlign(&m_FrameDatas); } } // 0x4CF140 -void CAnimBlendClumpData::SetNumberOfBones(int32 numBones) { - if (m_Frames) { - CMemoryMgr::FreeAlign(&m_Frames); +void CAnimBlendClumpData::SetNumberOfBones(uint32 numBones) { + if (m_FrameDatas) { + CMemoryMgr::FreeAlign(&m_FrameDatas); } - m_NumFrames = numBones; - m_Frames = reinterpret_cast(CMemoryMgr::MallocAlign(sizeof(AnimBlendFrameData) * numBones, 64)); + m_NumFrameData = numBones; + m_FrameDatas = static_cast(CMemoryMgr::MallocAlign(sizeof(AnimBlendFrameData) * numBones, 64)); } // 0x4CF190 void CAnimBlendClumpData::ForAllFrames(void (*callback)(AnimBlendFrameData*, void*), void* data) { - for (auto& frame : std::span{ m_Frames, m_NumFrames }) { + for (auto& frame : std::span{ m_FrameDatas, m_NumFrameData }) { callback(&frame, data); } } diff --git a/source/game_sa/Animation/AnimBlendClumpData.h b/source/game_sa/Animation/AnimBlendClumpData.h index e6d867bdb6..3aff4871f2 100644 --- a/source/game_sa/Animation/AnimBlendClumpData.h +++ b/source/game_sa/Animation/AnimBlendClumpData.h @@ -12,10 +12,13 @@ class CAnimBlendClumpData { public: - CAnimBlendLink m_Anims; //< List of `CAnimBlendAssociation` - uint32 m_NumFrames; + CAnimBlendLink m_AnimList; //!< List of `CAnimBlendAssociation` - List of anims that are being played on this clump + union { + uint32 m_NumFrameData; // For skinned clumps + uint32 m_NumBones; // For non-skinned clumps + }; CVector* m_PedPosition; - AnimBlendFrameData* m_Frames; + AnimBlendFrameData* m_FrameDatas; // There's always at least 1 frame present public: static void InjectHooks(); @@ -26,10 +29,28 @@ class CAnimBlendClumpData { CAnimBlendClumpData* Destructor() { this->CAnimBlendClumpData::~CAnimBlendClumpData(); return this; } void ForAllFrames(void (*callback)(AnimBlendFrameData*, void*), void* data); + + /*! + * @notsa + * @brief Iterate all frames (Using a functor, usually a lambda) + * @param Fn The functor to be called + */ + template + void ForAllFramesF(Functor&& Fn) { + for (auto& frame : std::span{ m_FrameDatas, m_NumFrameData }) { + std::invoke(Fn, &frame); + } + } + + auto& GetAnims() { return m_AnimList; } + +private: void ForAllFramesInSPR(void (*callback)(AnimBlendFrameData*, void*), void* data, uint32 a3); void LoadFramesIntoSPR(); - void SetNumberOfBones(int32 numBones); - auto GetFrames() const { return m_Frames; } -}; +public: + AnimBlendFrameData& GetRootFrameData() const { assert(m_NumFrameData >= 1); return m_FrameDatas[0]; } + void SetNumberOfBones(uint32 numBones); + auto GetFrames() const { return m_FrameDatas; } +}; VALIDATE_SIZE(CAnimBlendClumpData, 0x14); diff --git a/source/game_sa/Animation/AnimBlendFrameData.h b/source/game_sa/Animation/AnimBlendFrameData.h index 9347c1cf29..5bbd140241 100644 --- a/source/game_sa/Animation/AnimBlendFrameData.h +++ b/source/game_sa/Animation/AnimBlendFrameData.h @@ -13,35 +13,28 @@ class AnimBlendFrameData { public: union { struct { - uint8 m_bf1 : 1; // doesn't seem to be used - uint8 m_IsIFrameOrientationToAffectedByNodes : 1; // m_IFrame orientation will be affected - uint8 m_IsIFrameTranslationToAffectedByNodes : 1; // m_IFrame translation will be affected - uint8 m_bIsInitialized : 1; - uint8 m_bUpdateSkinnedWith3dVelocityExtraction : 1; - uint8 m_bCheckBlendNodeClumpKeyFrames : 1; // key frames of CAnimBlendNode bones will be checked - uint8 m_bIsCompressed : 1; - uint8 m_bUpdatingFrame : 1; // doesn't seem to be used + bool bf1 : 1; //!< doesn't seem to be used + bool KeyFramesIgnoreNodeOrientation : 1; //!< Whenever orientation + bool KeyFramesIgnoreNodeTranslation : 1; //!< Whenever translation + bool HasVelocity : 1; //!< If true the translation is used to move the ped + bool HasZVelocity : 1; //!< If true 3D velocity extraction is used, otherwise 2D + bool NeedsKeyFrameUpdate : 1; //!< If `RpAnimBlendNodeUpdateKeyFrames` needs to be called on update + bool IsCompressed : 1; //!< Is the anim data for this frame compressed + bool IsUpdatingFrame : 1; //!< Doesn't seem to be used }; - uint8 m_nFlags; + uint8 Flags; }; - /* todo union { - RwV3d_0 m_posn; - RwV3d_0 m_bonePosition; + CVector BonePos; // For skinned clumps (?) + CVector FramePos; // For non-skinned clumps (?) }; - */ - CVector m_vecOffset; + union { - RpHAnimBlendInterpFrame* m_pIFrame; // TODO: Rename to `m_pStdKeyFrame` - RwFrame* m_pFrame; + RpHAnimBlendInterpFrame* KeyFrame; // For skinned clumps + RwFrame* Frame; // For non-skinned clumps }; - uint32 m_nNodeId; // In case of peds it's ePedBone (NOTE: I might be wrong, see `IsPedHeadAbovePos`) - - // NOTSA - CQuaternion& GetFrameOrientation() const { return m_pIFrame->orientation; } - CQuaternion& GetFrameOrientation() { return m_pIFrame->orientation; } - CVector& GetFrameTranslation() const { return m_pIFrame->translation; } + uint32 BoneTag; // If `BONE_UNKNOWN` (-1) this is a non-skinned clump, otherwise a skinned one }; VALIDATE_SIZE(AnimBlendFrameData, 0x18); diff --git a/source/game_sa/Animation/AnimBlendHierarchy.cpp b/source/game_sa/Animation/AnimBlendHierarchy.cpp index 736e642c5a..ef0b1dd3a4 100644 --- a/source/game_sa/Animation/AnimBlendHierarchy.cpp +++ b/source/game_sa/Animation/AnimBlendHierarchy.cpp @@ -110,17 +110,22 @@ void* CAnimBlendHierarchy::GetSequenceBlock() const { // 0x4CF5F0 void CAnimBlendHierarchy::Uncompress() { assert(m_nSeqCount > 0); + assert(m_bIsCompressed); - auto compressedSeq = GetSequenceBlock(); + //NOTSA_LOG_TRACE("Uncompress(this={:#x})", LOG_PTR(this)); - auto uncompressedSeq = AllocSequenceBlock(false); + const auto cSeqData = GetSequenceBlock(); + + // Now uncompress sequence data + auto uSeqData = AllocSequenceBlock(false); for (auto& s : GetSequences()) { - s.Uncompress(uncompressedSeq); - uncompressedSeq += s.GetDataSize(false); + s.Uncompress(uSeqData); + uSeqData += s.GetDataSize(false); } - if (compressedSeq) { - CMemoryMgr::Free(compressedSeq); + // Now we no longer need the old compressed data + if (cSeqData) { + CMemoryMgr::Free(cSeqData); } m_bIsCompressed = false; @@ -132,34 +137,33 @@ void CAnimBlendHierarchy::Uncompress() { // 0x4CF6C0 void CAnimBlendHierarchy::CompressKeyframes() const { - auto frameData = AllocSequenceBlock(true); - auto oldFrameData = GetSequenceBlock(); + assert(!m_bIsCompressed); + + //NOTSA_LOG_TRACE("CompressKeyframes(this={:#x})", LOG_PTR(this)); + const auto uSeqData = GetSequenceBlock(); + + // Compress current data + auto cSeqData = AllocSequenceBlock(true); for (auto& sequence : GetSequences()) { - sequence.CompressKeyframes(frameData); - frameData += sequence.GetDataSize(true); + sequence.CompressKeyframes(cSeqData); + cSeqData += sequence.GetDataSize(true); } - if (oldFrameData) { - CMemoryMgr::Free(oldFrameData); + // Now we no longer need the old uncompressed data + if (uSeqData) { + CMemoryMgr::Free(uSeqData); } } // 0x4CF760 void CAnimBlendHierarchy::RemoveUncompressedData() { assert(m_nSeqCount > 0); + assert(!m_bIsCompressed); - auto frameData = AllocSequenceBlock(true); - auto oldFrameData = GetSequenceBlock(); + //NOTSA_LOG_TRACE("RemoveUncompressedData(this={:#x})", LOG_PTR(this)); - for (auto& sequence : GetSequences()) { - sequence.RemoveUncompressedData(frameData); - frameData = (uint8*)((size_t)frameData + sequence.GetDataSize(true)); - } - - if (oldFrameData) { - CMemoryMgr::Free(oldFrameData); - } + CompressKeyframes(); m_bIsCompressed = true; } @@ -177,6 +181,12 @@ void CAnimBlendHierarchy::Print() { } } +// notsa +void CAnimBlendHierarchy::SetNumSequences(size_t n) { + m_nSeqCount = (uint16)n; + m_pSequences = new CAnimBlendSequence[n]; // Yes, they used `new` +} + uint32 CAnimBlendHierarchy::GetIndex() const { return CAnimManager::GetAnimIndex(this); } diff --git a/source/game_sa/Animation/AnimBlendHierarchy.h b/source/game_sa/Animation/AnimBlendHierarchy.h index 667fada5e2..aa58674c7d 100644 --- a/source/game_sa/Animation/AnimBlendHierarchy.h +++ b/source/game_sa/Animation/AnimBlendHierarchy.h @@ -17,7 +17,7 @@ class CAnimBlendHierarchy { public: uint32 m_hashKey; - CAnimBlendSequence* m_pSequences; + CAnimBlendSequence* m_pSequences; //!< Per-node animations - NOTE: Order of these depends on the order of nodes in Clump this was built from uint16 m_nSeqCount; bool m_bIsCompressed; bool m_bKeepCompressed; @@ -54,8 +54,10 @@ class CAnimBlendHierarchy { auto GetSequences() const { return std::span{ m_pSequences, (size_t)m_nSeqCount }; } auto GetHashKey() const { return m_hashKey; } - auto GetTotalTime() const { return m_fTotalTime; } + bool IsRunningCompressed() const { return m_bKeepCompressed; } + bool IsUncompressed() const { return !m_bIsCompressed; } + void SetNumSequences(size_t n); uint32 GetIndex() const; diff --git a/source/game_sa/Animation/AnimBlendNode.cpp b/source/game_sa/Animation/AnimBlendNode.cpp index 01ddd9cfa4..0d67b4e70e 100644 --- a/source/game_sa/Animation/AnimBlendNode.cpp +++ b/source/game_sa/Animation/AnimBlendNode.cpp @@ -47,7 +47,7 @@ bool CAnimBlendNode::FindKeyFrame(float time) { if (m_KFCurr + 1 >= m_Seq->m_FramesNum) { // reached end of animation - if (!m_BlendAssoc->IsRepeating()) { + if (!m_BlendAssoc->IsLooped()) { CalcDeltas(); m_KFRemainingTime = 0.0f; return false; @@ -131,7 +131,7 @@ bool CAnimBlendNode::SetupKeyFrameCompressed() { // 0x4D0160 - Unused bool CAnimBlendNode::UpdateTime() { - if (m_BlendAssoc->IsRunning()) { + if (m_BlendAssoc->IsPlaying()) { m_KFRemainingTime -= m_BlendAssoc->m_TimeStep; if (m_KFRemainingTime <= 0.0f) { return NextKeyFrameNoCalc(); diff --git a/source/game_sa/Animation/AnimBlendNode.h b/source/game_sa/Animation/AnimBlendNode.h index 59cb59a817..549b424d01 100644 --- a/source/game_sa/Animation/AnimBlendNode.h +++ b/source/game_sa/Animation/AnimBlendNode.h @@ -8,6 +8,8 @@ #pragma once #include "Quaternion.h" +#include "AnimBlendSequence.h" +#include "AnimBlendAssociation.h" //! Animation of a single node (bone) class CAnimBlendNode { @@ -58,9 +60,11 @@ class CAnimBlendNode { bool UpdateTime(); // Unused bool SetupKeyFrameCompressed(); bool FindKeyFrame(float time); + auto GetAnimAssoc() const { return m_BlendAssoc; } + bool IsValid() const { return !!m_Seq; } + auto GetSeq() const { return m_Seq; } + auto GetRootKF() const { return &m_Seq[0]; } - -private: // Generic implementations //! @notsa //! @brief Inverse progress of current frame (Eg.: How much is left of this frame) template @@ -124,7 +128,7 @@ class CAnimBlendNode { if (m_KFCurr >= m_Seq->m_FramesNum) { // reached end of animation - if (!m_BlendAssoc->IsRepeating()) { + if (!m_BlendAssoc->IsLooped()) { m_KFCurr--; m_KFRemainingTime = 0.0f; return false; @@ -156,7 +160,7 @@ class CAnimBlendNode { trans = CVector{0.0f, 0.0f, 0.0f}; rot = CQuaternion{0.0f, 0.0f, 0.0f, 0.0f}; - if (m_BlendAssoc->IsRunning()) { + if (m_BlendAssoc->IsPlaying()) { m_KFRemainingTime -= m_BlendAssoc->m_TimeStep; if (m_KFRemainingTime <= 0.0f) { looped = I_NextKeyFrame(); diff --git a/source/game_sa/Animation/AnimBlendSequence.cpp b/source/game_sa/Animation/AnimBlendSequence.cpp index 56486f8e3a..ddd338ce1f 100644 --- a/source/game_sa/Animation/AnimBlendSequence.cpp +++ b/source/game_sa/Animation/AnimBlendSequence.cpp @@ -30,27 +30,27 @@ CAnimBlendSequence::~CAnimBlendSequence() { } } -// 0x4D0F40 -void CAnimBlendSequence::CompressKeyframes(uint8* frameData) { +template +bool CAnimBlendSequence::ConvertKeyFrames(byte* pDataBlock) { + constexpr auto HasTranslation = requires{ To::Trans; }; + + assert(HasTranslation == m_bHasTranslation); + if (m_FramesNum == 0) { - return; + return false; } - void* frames = (frameData ? frameData : CMemoryMgr::Malloc(GetDataSize(true))); - if (m_bHasTranslation) { - auto* kftc = (KeyFrameTransCompressed*)frames; - auto* kf = (KeyFrameTrans*)m_Frames; - for (auto i = 0; i < m_FramesNum; i++, kf++, kftc++) { - kftc->Rot = kf->Rot; - kftc->SetDeltaTime(kf->DeltaTime); - kftc->Trans = kf->Trans; - } - } else { - auto* kfc = (KeyFrameCompressed*)frames; - auto* kf = (KeyFrame*)m_Frames; - for (auto i = 0; i < m_FramesNum; i++, kf++, kfc++) { - kfc->Rot = kf->Rot; - kfc->SetDeltaTime(kf->DeltaTime); + void* const outFrames = pDataBlock + ? pDataBlock + : CMemoryMgr::Malloc(GetDataSize(true)); + + auto* inKF = static_cast(m_Frames); + auto* outKF = static_cast(outFrames); + for (auto i = m_FramesNum; i --> 0; inKF++, outKF++) { + outKF->Rot = inKF->Rot; + outKF->SetDeltaTime(inKF->DeltaTime); + if constexpr (HasTranslation) { + outKF->Trans = inKF->Trans; } } @@ -58,9 +58,34 @@ void CAnimBlendSequence::CompressKeyframes(uint8* frameData) { CMemoryMgr::Free(m_Frames); } - m_Frames = frames; - m_bUsingExternalMemory = frameData != nullptr; - m_bIsCompressed = true; + m_Frames = outFrames; + m_bUsingExternalMemory = pDataBlock != nullptr; + + return true; +} + +// 0x4D0F40 +void CAnimBlendSequence::CompressKeyframes(uint8* pDataBlock) { + assert(!m_bIsCompressed); + + if (m_bHasTranslation + ? ConvertKeyFrames(pDataBlock) + : ConvertKeyFrames(pDataBlock) + ) { + m_bIsCompressed = true; + } +} + +// 0x4D0D40 +void CAnimBlendSequence::Uncompress(uint8* pDataBlock) { + assert(m_bIsCompressed); + + if (m_bHasTranslation + ? ConvertKeyFrames(pDataBlock) + : ConvertKeyFrames(pDataBlock) + ) { + m_bIsCompressed = false; + } } // 0x4D12A0 @@ -84,14 +109,14 @@ void CAnimBlendSequence::RemoveQuaternionFlips() const { return; } - KeyFrame* frame = GetUKeyFrame(0); - CQuaternion last = frame->Rot; - for (auto i = 1; i < m_FramesNum; i++) { - frame = GetUKeyFrame(i); - if (DotProduct(last, frame->Rot) < 0.0f) { - frame->Rot = -frame->Rot; + KeyFrame* f; + CQuaternion last = GetUKeyFrame(0)->Rot; + for (auto i = 1u; i < m_FramesNum; i++) { + f = GetUKeyFrame(i); + if (DotProduct(last, f->Rot) < 0.0f) { + f->Rot = -f->Rot; } - last = frame->Rot; + last = f->Rot; } } @@ -100,7 +125,7 @@ void CAnimBlendSequence::SetName(const char* name) { if (notsa::IsFixBugs()) { m_IsUsingBoneTag = false; } - m_NameHashKey = CKeyGen::GetUppercaseKey(name); + m_NameHashKey = CKeyGen::GetUppercaseKey(name); } // 0x4D0C70 @@ -121,38 +146,6 @@ void CAnimBlendSequence::SetNumFrames(uint32 count, bool bHasTranslation, bool c m_bIsCompressed = compressed; // condition has been removed } -// 0x4D0D40 -void CAnimBlendSequence::Uncompress(uint8* frameData) { - if (m_FramesNum == 0) { - return; - } - - void* newFrames = (frameData ? frameData : CMemoryMgr::Malloc(GetDataSize(false))); - if (m_bHasTranslation) { - auto* kfc = (KeyFrameTransCompressed*)m_Frames; // kfc = Kentucky Fried Chkicken - auto* kf = (KeyFrameTrans*)newFrames; - for (auto i = 0u; i < m_FramesNum; i++, kf++, kfc++) { - kf->Rot = kfc->Rot; - kf->DeltaTime = kfc->DeltaTime; - kf->Trans = kfc->Trans; - } - } else { - auto* kfc = (KeyFrameCompressed*)m_Frames; - auto* kf = (KeyFrame*)newFrames; - for (auto i = 0u; i < m_FramesNum; i++, kf++, kfc++) { - kf->Rot = kfc->Rot; - kf->DeltaTime = kfc->DeltaTime; - } - } - - if (!m_bUsingExternalMemory) { - CMemoryMgr::Free(m_Frames); - } - m_Frames = newFrames; - m_bUsingExternalMemory = frameData != nullptr; - m_bIsCompressed = false; -} - // 0x4D1150 bool CAnimBlendSequence::MoveMemory() { if (m_bUsingExternalMemory || !m_Frames) { diff --git a/source/game_sa/Animation/AnimBlendSequence.h b/source/game_sa/Animation/AnimBlendSequence.h index dbec76bda3..408cea2a5a 100644 --- a/source/game_sa/Animation/AnimBlendSequence.h +++ b/source/game_sa/Animation/AnimBlendSequence.h @@ -40,7 +40,7 @@ class CAnimBlendSequence { */ template auto GetKeyFrame(size_t n) const { - assert(IsCompressed() == AsCompressed); + assert(IsCompressed() == AsCompressed); // NOTE: If this is ever hit see the note in `UncompressAnimation` if constexpr (AsCompressed) { return GetCKeyFrame(n); } else { @@ -57,7 +57,7 @@ class CAnimBlendSequence { */ KeyFrameTrans* GetUKeyFrame(size_t n) const { // `U` = Uncompressed assert(n < m_FramesNum); - assert(!m_bIsCompressed); + assert(!m_bIsCompressed); // NOTE: If this is ever hit see the note in `UncompressAnimation` return static_cast(m_bHasTranslation ? // Lie a little by always up-casting to `KeyFrameTrans*` to make our lifes easier &((KeyFrameTrans*)m_Frames)[n] : &((KeyFrame*)m_Frames)[n] @@ -73,7 +73,7 @@ class CAnimBlendSequence { */ KeyFrameTransCompressed* GetCKeyFrame(size_t n) const { // `C` = Compressed assert(n < m_FramesNum); - assert(m_bIsCompressed); + assert(m_bIsCompressed); // NOTE: If this is ever hit see the note in `UncompressAnimation` return static_cast(m_bHasTranslation ? // Lie a little by always up-casting to `KeyFrameTransCompressed*` to make our lifes easier &((KeyFrameTransCompressed*)m_Frames)[n] : &((KeyFrameCompressed*)m_Frames)[n] @@ -83,11 +83,21 @@ class CAnimBlendSequence { CAnimBlendSequence* Constructor() { this->CAnimBlendSequence::CAnimBlendSequence(); return this; } CAnimBlendSequence* Destructor() { this->CAnimBlendSequence::~CAnimBlendSequence(); return this; } +private: + /*! + * @brief Implementation for compression/uncompression of key-frames + * @tparam From Key-Frame type to convert from + * @tparam To Key-Frame type to convert to + * @param [opt] frameData Pre-allocated frame data buffer + */ + template + bool ConvertKeyFrames(byte* pDataBlock); + private: //! The bone/frame this sequence is associated with //! Thanks to jte for some info union { - eBoneTag32 m_BoneTag{BONE_UNKNOWN}; // IF m_IsUsingBoneTag == TRUE + eBoneTag16 m_BoneTag{BONE_UNKNOWN}; // IF m_IsUsingBoneTag == TRUE uint32 m_NameHashKey; // IF m_IsUsingBoneTag == FALSE }; diff --git a/source/game_sa/Animation/AnimBlendStaticAssociation.cpp b/source/game_sa/Animation/AnimBlendStaticAssociation.cpp index afc013a605..788412ba00 100644 --- a/source/game_sa/Animation/AnimBlendStaticAssociation.cpp +++ b/source/game_sa/Animation/AnimBlendStaticAssociation.cpp @@ -31,10 +31,10 @@ void CAnimBlendStaticAssociation::Init(RpClump* clump, CAnimBlendHierarchy* h) { return; } - const auto clumpAnimData = RpClumpGetAnimBlendClumpData(clump); + const auto clumpAnimData = RpAnimBlendClumpGetData(clump); assert(clumpAnimData); - m_NumBlendNodes = clumpAnimData->m_NumFrames; + m_NumBlendNodes = clumpAnimData->m_NumFrameData; AllocateSequenceArray(m_NumBlendNodes); // Initialize sequences from the data in the clump @@ -42,13 +42,13 @@ void CAnimBlendStaticAssociation::Init(RpClump* clump, CAnimBlendHierarchy* h) { if (!seq.m_FramesNum) { continue; } - const auto frame = seq.IsUsingBoneTag() + const auto frameData = seq.IsUsingBoneTag() ? RpAnimBlendClumpFindBone(clump, seq.GetBoneTag()) : RpAnimBlendClumpFindFrameFromHashKey(clump, seq.GetNameHashKey()); - if (!frame) { + if (!frameData) { continue; } - const auto nodeIdx = frame - clumpAnimData->m_Frames; + const auto nodeIdx = frameData - clumpAnimData->m_FrameDatas; assert(nodeIdx >= 0 && nodeIdx < m_NumBlendNodes); m_BlendSeqs[nodeIdx] = &seq; } diff --git a/source/game_sa/Animation/AnimManager.cpp b/source/game_sa/Animation/AnimManager.cpp index fa2e5eee08..cae02b2a99 100644 --- a/source/game_sa/Animation/AnimManager.cpp +++ b/source/game_sa/Animation/AnimManager.cpp @@ -56,7 +56,7 @@ void CAnimManager::Initialise() { ms_numAnimations = 0; ms_numAnimBlocks = 0; ms_numAnimAssocDefinitions = 118; // ANIM_TOTAL_GROUPS aka NUM_ANIM_ASSOC_GROUPS - ms_animCache.Init(50); + ms_AnimCache.Init(50); ReadAnimAssociationDefinitions(); RegisterAnimBlock("ped"); } @@ -105,7 +105,7 @@ void CAnimManager::Shutdown() { ms_aAnimations[i].Shutdown(); } - ms_animCache.Shutdown(); + ms_AnimCache.Shutdown(); delete[] ms_aAnimAssocGroups; } @@ -208,13 +208,13 @@ CAnimBlendStaticAssociation* CAnimManager::GetAnimAssociation(AssocGroupId group } CAnimBlendAssociation* CAnimManager::AddAnimationToClump(RpClump* clump, CAnimBlendAssociation* anim) { - const auto clumpAnims = &RpClumpGetAnimBlendClumpData(clump)->m_Anims; + const auto clumpAnims = &RpAnimBlendClumpGetData(clump)->m_AnimList; CAnimBlendAssociation* syncWith{}; - if (anim->IsMoving()) { + if (anim->IsSyncronised()) { for (auto l = clumpAnims->next; l; l = l->next) { const auto a = CAnimBlendAssociation::FromLink(l); - if (a->IsMoving()) { + if (a->IsSyncronised()) { syncWith = a; break; } @@ -223,7 +223,7 @@ CAnimBlendAssociation* CAnimManager::AddAnimationToClump(RpClump* clump, CAnimBl if (syncWith) { anim->SyncAnimation(syncWith); - anim->m_Flags |= ANIMATION_STARTED; + anim->m_Flags |= ANIMATION_IS_PLAYING; } else { anim->Start(0.0f); } @@ -250,16 +250,18 @@ CAnimBlendAssociation* CAnimManager::AddAnimation(RpClump* clump, CAnimBlendHier // 0x4D3B30 CAnimBlendAssociation* CAnimManager::AddAnimationAndSync(RpClump* clump, CAnimBlendAssociation* syncWith, AssocGroupId groupId, AnimationId animId) { const auto a = CreateAnimAssociation(groupId, animId); - if (a->IsMoving() && syncWith) { + if (a->IsSyncronised() && syncWith) { a->SyncAnimation(syncWith); - a->m_Flags |= ANIMATION_STARTED; + a->m_Flags |= ANIMATION_IS_PLAYING; } else { a->Start(0.0f); } - RpClumpGetAnimBlendClumpData(clump)->m_Anims.Prepend(&a->m_Link); - return a; + RpAnimBlendClumpGetData(clump)->m_AnimList.Prepend(&a->m_Link); + + return a; } + // 0x4D3BA0 AnimAssocDefinition* CAnimManager::AddAnimAssocDefinition(const char* groupName, const char* blockName, uint32 modelIndex, uint32 animsCount, AnimDescriptor* descriptor) { const auto def = &ms_aAnimAssocDefinitions[ms_numAnimAssocDefinitions++]; @@ -394,54 +396,60 @@ int32 CAnimManager::GetNumRefsToAnimBlock(int32 index) { // 0x4D41C0 void CAnimManager::UncompressAnimation(CAnimBlendHierarchy* h) { - if (h->m_bKeepCompressed) { - if (h->m_fTotalTime == 0.f) { + if (h->IsRunningCompressed()) { // Keep as compressed? + if (h->GetTotalTime() == 0.f) { h->CalcTotalTimeCompressed(); } - } else if (h->m_bIsCompressed) { - auto l = ms_animCache.Insert(h); - if (!l) { // Not more free links? - // Remove least recently added item - const auto llr = ms_animCache.GetTail(); + } else if (!h->IsUncompressed()) { // Need to uncompress? + assert(!h->m_Link); // Sanity check + auto l = ms_AnimCache.Insert(h); + if (!l) { // No more free links? (This is totally normal as animations aren't compressed back unless the cache is full) + // Remove least recently used item + const auto llr = ms_AnimCache.GetTail(); llr->data->RemoveUncompressedData(); + + // TODO: If the anim is still in use this will corrupt the animation data, and (hopefully) hit the assert in `GetKeyFrame`! + // There's currently no way for us to tell if the animation is in use, so there's no better solution for now. RemoveFromUncompressedCache(llr->data); // Now try again, this time it should succeed - VERIFY(l = ms_animCache.Insert(h)); + VERIFY(l = ms_AnimCache.Insert(h)); } h->m_Link = l; h->Uncompress(); - } else if (h->m_Link) { // Already uncompressed, add to cache - h->m_Link->Insert(ms_animCache.GetHead()); + } else if (h->m_Link) { // Already uncompressed, mark as recently-used in cache + h->m_Link->Remove(); // Remove from current position + ms_AnimCache.Insert(*h->m_Link); // Now re-insert at head } } // 0x4D42A0 void CAnimManager::RemoveFromUncompressedCache(CAnimBlendHierarchy* h) { if (const auto l = h->m_Link) { - l->data->m_Link = nullptr; - ms_animCache.Remove(l); + assert(l->data == h); + ms_AnimCache.Remove(l); + h->m_Link = nullptr; } } // 0x4D4410 CAnimBlendAssociation* CAnimManager::BlendAnimation(RpClump* clump, CAnimBlendHierarchy* toBlendHier, int32 toBlendFlags, float blendDelta) { - const auto clumpAnimData = RpClumpGetAnimBlendClumpData(clump); // Get running anim data + const auto clumpAnimData = RpAnimBlendClumpGetData(clump); // Get running anim data CAnimBlendAssociation* running{}; // Running instance of this anim bool bFadeThisOut = false; - for (auto l = clumpAnimData->m_Anims.next; l; l = l->next) { + for (auto l = clumpAnimData->m_AnimList.next; l; l = l->next) { const auto a = CAnimBlendAssociation::FromLink(l); assert(a->m_BlendHier); if (a->m_BlendHier && a->m_BlendHier == toBlendHier) { // Found an instance of this anim running running = a; - } else if (((toBlendFlags & ANIMATION_PARTIAL) == 0) == a->IsPartial()) { + } else if (((toBlendFlags & ANIMATION_IS_PARTIAL) == 0) == a->IsPartial()) { if (a->m_BlendAmount <= 0.f) { a->m_BlendDelta = -1.f; - } else if (const auto bd = a->GetBlendAmount() * -blendDelta; (bd >= a->GetBlendDelta() && (toBlendFlags & ANIMATION_PARTIAL)) || (a->m_BlendHier->m_nAnimBlockId && a->m_BlendHier->m_nAnimBlockId == toBlendHier->m_nAnimBlockId)) { + } else if (const auto bd = a->GetBlendAmount() * -blendDelta; (bd >= a->GetBlendDelta() && (toBlendFlags & ANIMATION_IS_PARTIAL)) || (a->m_BlendHier->m_nAnimBlockId && a->m_BlendHier->m_nAnimBlockId == toBlendHier->m_nAnimBlockId)) { a->m_BlendDelta = std::min(-0.05f, bd); } - a->SetFlag(ANIMATION_FREEZE_LAST_FRAME); + a->SetFlag(ANIMATION_IS_BLEND_AUTO_REMOVE); bFadeThisOut = true; } } @@ -452,7 +460,7 @@ CAnimBlendAssociation* CAnimManager::BlendAnimation(RpClump* clump, CAnimBlendHi if (running->HasFinished()) { running->Start(); } - UncompressAnimation(running->m_BlendHier); // Not sure if this is really necessary? + UncompressAnimation(running->m_BlendHier); // Make sure anim doesn't get removed return running; } @@ -461,11 +469,11 @@ CAnimBlendAssociation* CAnimManager::BlendAnimation(RpClump* clump, CAnimBlendHi a->m_Flags = toBlendFlags; a->ReferenceAnimBlock(); UncompressAnimation(a->m_BlendHier); - clumpAnimData->m_Anims.Prepend(&a->m_Link); + clumpAnimData->m_AnimList.Prepend(&a->m_Link); a->Start(); - if (bFadeThisOut || (toBlendFlags & ANIMATION_PARTIAL)) { + if (bFadeThisOut || (toBlendFlags & ANIMATION_IS_PARTIAL)) { a->SetBlend(0.f, blendDelta); - UncompressAnimation(toBlendHier); // Why call this again? + UncompressAnimation(toBlendHier); // No need to call this again here, but doesn't hurt.... } else { a->m_BlendAmount = 1.f; } @@ -474,31 +482,31 @@ CAnimBlendAssociation* CAnimManager::BlendAnimation(RpClump* clump, CAnimBlendHi // 0x4D4610 CAnimBlendAssociation* CAnimManager::BlendAnimation(RpClump* clump, AssocGroupId groupId, AnimationId animId, float blendDelta) { - const auto clumpAnimData = RpClumpGetAnimBlendClumpData(clump); // Get running anim data + const auto clumpAnimData = RpAnimBlendClumpGetData(clump); // Get running anim data const auto toBlendAnim = GetAssocGroups()[groupId].GetAnimation(animId); - const bool toBlendIsMoving = toBlendAnim->m_Flags & ANIMATION_MOVEMENT; - const bool toBlendIsPartial = toBlendAnim->m_Flags & ANIMATION_PARTIAL; - const bool toBlendIsIndestructible = toBlendAnim->m_Flags & ANIMATION_INDESTRUCTIBLE; + const bool toBlendIsMoving = toBlendAnim->m_Flags & ANIMATION_IS_SYNCRONISED; + const bool toBlendIsPartial = toBlendAnim->m_Flags & ANIMATION_IS_PARTIAL; + const bool toBlendIsIndestructible = toBlendAnim->m_Flags & ANIMATION_FACIAL; CAnimBlendAssociation *running{}, *movingAnim{}; // Running instance of this anim, and the last moving anim in the chain bool bFadeThisOut = false; - for (auto l = clumpAnimData->m_Anims.next; l; l = l->next) { + for (auto l = clumpAnimData->m_AnimList.next; l; l = l->next) { const auto a = CAnimBlendAssociation::FromLink(l); - if (toBlendIsMoving && a->IsMoving()) { + if (toBlendIsMoving && a->IsSyncronised()) { movingAnim = a; } if (a->m_AnimId == animId && a->m_AnimGroupId == groupId) { running = a; - } else if (toBlendIsPartial == a->IsPartial() && toBlendIsIndestructible == a->IsIndestructible()) { + } else if (toBlendIsPartial == a->IsPartial() && toBlendIsIndestructible == a->IsFacial()) { if (a->m_BlendAmount <= 0.f) { a->m_BlendDelta = -1.f; } else if (const auto bd = a->GetBlendAmount() * -blendDelta; bd <= a->GetBlendDelta() || !toBlendIsPartial) { a->m_BlendDelta = std::min(-0.05f, bd); } - a->SetFlag(ANIMATION_FREEZE_LAST_FRAME); + a->SetFlag(ANIMATION_IS_BLEND_AUTO_REMOVE); bFadeThisOut = true; } } @@ -823,9 +831,8 @@ inline void CAnimManager::LoadAnimFile_ANP23(RwStream* s, const IFPSectionHeader hier->m_nAnimBlockId = ablockId; hier->m_bKeepCompressed = false; - // Allocate sequences - TODO: MSVC garbage - hier->m_nSeqCount = numSeq; - hier->m_pSequences = new CAnimBlendSequence[numSeq]; // Yes, they used `new` + // Allocate sequences now + hier->SetNumSequences(numSeq); // Read sequences for (size_t seqN = 0; seqN < numSeq; seqN++) { @@ -837,7 +844,8 @@ inline void CAnimManager::LoadAnimFile_ANP23(RwStream* s, const IFPSectionHeader const auto numFrames = RwStreamRead(s); const auto boneTag = RwStreamRead(s); - // Only 1 of these will be valid + // Only 1 of these will be valid in the end + // If BoneTag != -1 then it overwrites the name. seq->SetName(seqName); seq->SetBoneTag(boneTag); diff --git a/source/game_sa/Animation/AnimManager.h b/source/game_sa/Animation/AnimManager.h index a46ab68499..3884917d44 100644 --- a/source/game_sa/Animation/AnimManager.h +++ b/source/game_sa/Animation/AnimManager.h @@ -32,7 +32,7 @@ class CAnimManager { static inline int32& ms_numAnimations = *(int32*)0xB4EA2C; static inline std::array& ms_aAnimBlocks = *(std::array*)0xB5D4A0; - static inline CLinkList& ms_animCache = *(CLinkList*)0xB5EB20; + static inline CLinkList& ms_AnimCache = *(CLinkList*)0xB5EB20; public: static void InjectHooks(); @@ -67,7 +67,7 @@ class CAnimManager { static CAnimBlendAssociation* AddAnimation(RpClump* clump, AssocGroupId groupId, AnimationId animId); static CAnimBlendAssociation* AddAnimation(RpClump* clump, CAnimBlendHierarchy* hier, int32 clumpAssocFlag); - static CAnimBlendAssociation* AddAnimationAndSync(RpClump* clump, CAnimBlendAssociation* animBlendAssoc, AssocGroupId groupId, AnimationId animId); + static CAnimBlendAssociation* AddAnimationAndSync(RpClump* clump, CAnimBlendAssociation* syncWith, AssocGroupId groupId, AnimationId animId); static AnimAssocDefinition* AddAnimAssocDefinition(const char* groupName, const char* blockName, uint32 modelIndex, uint32 animsCount, AnimDescriptor* descriptor); static void AddAnimToAssocDefinition(AnimAssocDefinition* definition, const char* animName); static void AddAnimBlockRef(int32 index); @@ -79,6 +79,21 @@ class CAnimManager { static void RemoveAnimBlockRef(int32 index); static void RemoveAnimBlockRefWithoutDelete(int32 index); static void RemoveFromUncompressedCache(CAnimBlendHierarchy* hier); + + /*! + * @addr 0x4D41C0 + * + * @brief Uncompress animation data (Unless the animation has the keep-compressed flag). + * @brief Also marks the anim as recently-used in the LRU cache + * + * @details This function does a 2 things: + * @details - Uncompress anim data (Unless the animation has the keep-compressed flag) + * @details - Since it uses a LRU cache, the hierarchy is put at the front (thus marking it as recently used) + * @details So even if the anim is already un-compressed this function should be called + * @details to mark it as recently-used in the LRU cache. + + * @param hier The hierarchy to uncompress + */ static void UncompressAnimation(CAnimBlendHierarchy* hier); static CAnimBlendAssociation* BlendAnimation(RpClump* clump, CAnimBlendHierarchy* animBlendHier, int32 flags, float clumpAssocBlendData = 8.f); static CAnimBlendAssociation* BlendAnimation(RpClump* clump, AssocGroupId groupId, AnimationId animId, float clumpAssocBlendData = 8.f); diff --git a/source/game_sa/BoneNode_c.cpp b/source/game_sa/BoneNode_c.cpp index fa03d8f4b2..f5b94ef2e8 100644 --- a/source/game_sa/BoneNode_c.cpp +++ b/source/game_sa/BoneNode_c.cpp @@ -32,8 +32,8 @@ void BoneNode_c::InjectHooks() { bool BoneNode_c::Init(int32 boneTag, RpHAnimBlendInterpFrame* interpFrame) { m_BoneTag = static_cast(boneTag); m_InterpFrame = interpFrame; - m_Orientation = interpFrame->orientation; - m_Pos = interpFrame->translation; + m_Orientation = interpFrame->q; + m_Pos = interpFrame->t; m_Parent = nullptr; m_Childs.RemoveAll(); @@ -60,7 +60,7 @@ void BoneNode_c::InitLimits() { } // 0x6171F0 -void BoneNode_c::EulerToQuat(const CVector& angles, CQuaternion& quat) { +void BoneNode_c::EulerToQuat(const CVector& angles, RtQuat& quat) { const CVector halfRadAngles = { DegreesToRadians(angles.x) / 2.f, DegreesToRadians(angles.y) / 2.f, @@ -74,25 +74,25 @@ void BoneNode_c::EulerToQuat(const CVector& angles, CQuaternion& quat) { float cy = std::cos(halfRadAngles.z); float sy = std::sin(halfRadAngles.z); - quat.w = cr * cp * cy + sr * sp * sy; - quat.x = sr * cp * cy - cr * sp * sy; - quat.y = cr * sp * cy + sr * cp * sy; - quat.z = cr * cp * sy - sr * sp * cy; + quat.real = cr * cp * cy + sr * sp * sy; + quat.imag.x = sr * cp * cy - cr * sp * sy; + quat.imag.y = cr * sp * cy + sr * cp * sy; + quat.imag.z = cr * cp * sy - sr * sp * cy; } // 0x617080 -void BoneNode_c::QuatToEuler(const CQuaternion& quat, CVector& angles) { +void BoneNode_c::QuatToEuler(const RtQuat& quat, CVector& angles) { // refactor this fuck - const auto v9 = 2.0f * (quat.x * quat.z - quat.y * quat.w); + const auto v9 = 2.0f * (quat.imag.x * quat.imag.z - quat.imag.y * quat.real); const auto v10 = std::sqrt(1.0f - sq(v9)); - angles.y = RadiansToDegrees(std::atan2(2.0f * (quat.y * quat.w - quat.x * quat.z), v10)); + angles.y = RadiansToDegrees(std::atan2(2.0f * (quat.imag.y * quat.real - quat.imag.x * quat.imag.z), v10)); if (std::abs(v9) == 1.0f) { - angles.x = RadiansToDegrees(std::atan2(-2.0f * (quat.y * quat.z - quat.x * quat.w), 1.0f - 2.0f * (sq(quat.x) + sq(quat.z)))); + angles.x = RadiansToDegrees(std::atan2(-2.0f * (quat.imag.y * quat.imag.z - quat.imag.x * quat.real), 1.0f - 2.0f * (sq(quat.imag.x) + sq(quat.imag.z)))); angles.z = RadiansToDegrees(0.0f); } else { - angles.x = RadiansToDegrees(std::atan2(2.0f * (quat.x * quat.w + quat.y * quat.z) / v10, (1.0f - 2.0f * (sq(quat.x) + sq(quat.y))) / v10)); - angles.z = RadiansToDegrees(std::atan2(2.0f * (quat.z * quat.w + quat.x * quat.y) / v10, (1.0f - 2.0f * (sq(quat.y) + sq(quat.z))) / v10)); + angles.x = RadiansToDegrees(std::atan2(2.0f * (quat.imag.x * quat.real + quat.imag.y * quat.imag.z) / v10, (1.0f - 2.0f * (sq(quat.imag.x) + sq(quat.imag.y))) / v10)); + angles.z = RadiansToDegrees(std::atan2(2.0f * (quat.imag.z * quat.real + quat.imag.x * quat.imag.y) / v10, (1.0f - 2.0f * (sq(quat.imag.y) + sq(quat.imag.z))) / v10)); } } @@ -179,11 +179,11 @@ void BoneNode_c::Limit(float blend) { // 0x616E30 void BoneNode_c::BlendKeyframe(float blend) { - auto src = m_InterpFrame->orientation; + auto src = m_InterpFrame->q; auto dst = m_Orientation; RtQuatSlerpCache cache; - RtQuatSetupSlerpCache(src.AsRtQuat(), dst.AsRtQuat(), &cache); - RtQuatSlerp(m_InterpFrame->orientation.AsRtQuat(), src.AsRtQuat(), dst.AsRtQuat(), blend, &cache); + RtQuatSetupSlerpCache(&src, &dst, &cache); + RtQuatSlerp(&m_InterpFrame->q, &src, &dst, blend, &cache); } // 0x616CB0 @@ -218,7 +218,7 @@ void BoneNode_c::AddChild(BoneNode_c* children) { void BoneNode_c::CalcWldMat(const RwMatrix* boneMatrix) { RwMatrix rotMatrix = [this] { CMatrix mat{}; - mat.SetRotate(m_Orientation); + mat.SetRotate(CQuaternion{m_Orientation}); mat.GetPosition() = m_Pos; return mat.ToRwMatrix(); }(); diff --git a/source/game_sa/BoneNode_c.h b/source/game_sa/BoneNode_c.h index 8bd746882f..67cc322bff 100644 --- a/source/game_sa/BoneNode_c.h +++ b/source/game_sa/BoneNode_c.h @@ -23,8 +23,8 @@ class BoneNode_c : public ListItem_c { bool Init(int32 boneTag, RpHAnimBlendInterpFrame* interpFrame); void InitLimits(); - static void EulerToQuat(const CVector& angles, CQuaternion& quat); - static void QuatToEuler(const CQuaternion& quat, CVector& angles); + static void EulerToQuat(const CVector& angles, RtQuat& quat); + static void QuatToEuler(const RtQuat& quat, CVector& angles); static int32 GetIdFromBoneTag(eBoneTag32 bone); void ClampLimitsCurrent(bool LimitX, bool LimitY, bool LimitZ); @@ -55,8 +55,8 @@ class BoneNode_c : public ListItem_c { private: eBoneTag m_BoneTag; RpHAnimBlendInterpFrame* m_InterpFrame; - CQuaternion m_Orientation; // TODO: Use RtQuat - CVector m_Pos; // * * * + RtQuat m_Orientation; + CVector m_Pos; BoneNode_c* m_Parent; TList_c m_Childs; RwMatrix m_WorldMat; diff --git a/source/game_sa/BulletInfo.cpp b/source/game_sa/BulletInfo.cpp index 3a3bdb2d8e..a12fc70d14 100644 --- a/source/game_sa/BulletInfo.cpp +++ b/source/game_sa/BulletInfo.cpp @@ -118,7 +118,7 @@ void CBulletInfo::Update() { if (auto assoc = CAnimManager::BlendAnimation(hitPed->m_pRwClump, ANIM_GROUP_DEFAULT, anim, 8.0f)) { assoc->SetCurrentTime(0.0f); - assoc->SetFlag(ANIMATION_UNLOCK_LAST_FRAME, false); + assoc->SetFlag(ANIMATION_IS_FINISH_AUTO_REMOVE, false); // std::cout << "Blood anim\n"; } } diff --git a/source/game_sa/CarEnterExit.cpp b/source/game_sa/CarEnterExit.cpp index c917e9c18f..17a6001647 100644 --- a/source/game_sa/CarEnterExit.cpp +++ b/source/game_sa/CarEnterExit.cpp @@ -540,7 +540,7 @@ void CCarEnterExit::QuitEnteringCar(CPed* ped, CVehicle* vehicle, int32 doorId, // 0x64F680 void CCarEnterExit::RemoveCarSitAnim(const CPed* ped) { for (auto anim = RpAnimBlendClumpGetFirstAssociation(ped->m_pRwClump, ANIMATION_SECONDARY_TASK_ANIM); anim; anim = RpAnimBlendGetNextAssociation(anim, ANIMATION_SECONDARY_TASK_ANIM)) { - anim->SetFlag(ANIMATION_FREEZE_LAST_FRAME); + anim->SetFlag(ANIMATION_IS_BLEND_AUTO_REMOVE); anim->m_BlendDelta = -1000.f; } CAnimManager::BlendAnimation(ped->m_pRwClump, ped->m_nAnimGroup, ANIM_ID_IDLE, 1000.0); @@ -548,8 +548,8 @@ void CCarEnterExit::RemoveCarSitAnim(const CPed* ped) { // 0x64F6E0 void CCarEnterExit::RemoveGetInAnims(const CPed* ped) { - for (auto anim = RpAnimBlendClumpGetFirstAssociation(ped->m_pRwClump, ANIMATION_PARTIAL); anim; anim = RpAnimBlendGetNextAssociation(anim, ANIMATION_PARTIAL)) { - anim->SetFlag(ANIMATION_FREEZE_LAST_FRAME); + for (auto anim = RpAnimBlendClumpGetFirstAssociation(ped->m_pRwClump, ANIMATION_IS_PARTIAL); anim; anim = RpAnimBlendGetNextAssociation(anim, ANIMATION_IS_PARTIAL)) { + anim->SetFlag(ANIMATION_IS_BLEND_AUTO_REMOVE); anim->m_BlendDelta = -1000.f; } } diff --git a/source/game_sa/Collision/Collision.cpp b/source/game_sa/Collision/Collision.cpp index 3c829882bf..397fb0d67c 100644 --- a/source/game_sa/Collision/Collision.cpp +++ b/source/game_sa/Collision/Collision.cpp @@ -61,24 +61,25 @@ void CCollision::SortOutCollisionAfterLoad() { void CCollision::CalculateTrianglePlanes(CCollisionData* colData) { ZoneScoped; - if (!colData->m_nNumTriangles) + if (!colData->m_nNumTriangles) { // No triangles => no planes to calculate return; + } - if (colData->m_pTrianglePlanes) { - auto* link = colData->GetLinkPtr(); - link->Remove(); - ms_colModelCache.usedListHead.Insert(link); + if (colData->m_pTrianglePlanes) { // Planes already calculated? + ms_colModelCache.Insert(*colData->GetLinkPtr()); // Re-insert link at front } else { - auto* link = ms_colModelCache.Insert(colData); - if (!link) { - auto* toRemove = ms_colModelCache.usedListTail.prev; - toRemove->data->RemoveTrianglePlanes(); - ms_colModelCache.Remove(toRemove); - link = ms_colModelCache.Insert(colData); - } + auto l = ms_colModelCache.Insert(colData); + if (!l) { // No more free space? + // Remove least-recently used item + const auto llr = ms_colModelCache.GetTail(); + llr->data->RemoveTrianglePlanes(); + ms_colModelCache.Remove(llr); + // This should succeed now + VERIFY(l = ms_colModelCache.Insert(colData)); + } colData->CalculateTrianglePlanes(); - colData->SetLinkPtr(link); + colData->SetLinkPtr(l); } } @@ -86,11 +87,12 @@ void CCollision::CalculateTrianglePlanes(CCollisionData* colData) { void CCollision::RemoveTrianglePlanes(CCollisionData* colData) { ZoneScoped; - if (!colData->m_pTrianglePlanes) + if (!colData->m_pTrianglePlanes) { return; + } - auto* link = colData->GetLinkPtr(); - ms_colModelCache.Remove(link); + const auto l = colData->GetLinkPtr(); + ms_colModelCache.Remove(l); colData->RemoveTrianglePlanes(); } diff --git a/source/game_sa/Collision/CollisionData.cpp b/source/game_sa/Collision/CollisionData.cpp index 7b2d02337f..7b40de4fda 100644 --- a/source/game_sa/Collision/CollisionData.cpp +++ b/source/game_sa/Collision/CollisionData.cpp @@ -224,7 +224,9 @@ CLink* CCollisionData::GetLinkPtr() { auto* linkPtr = static_cast(&m_pTrianglePlanes[m_nNumTriangles]); auto space = sizeof(CColTrianglePlane); auto* alignedAddress = std::align(4, sizeof(CLink*), linkPtr, space); // 4 bytes aligned address - return *static_cast**>(alignedAddress); + const auto l = *static_cast**>(alignedAddress); + assert(l->data == this); // Sanity check + return l; } auto CCollisionData::GetNumFaceGroups() const -> uint32 { diff --git a/source/game_sa/Core/Link.h b/source/game_sa/Core/Link.h index be403d86c8..4e97afc63a 100644 --- a/source/game_sa/Core/Link.h +++ b/source/game_sa/Core/Link.h @@ -18,12 +18,19 @@ class CLink { prev->next = next; } - void Insert(CLink* link) { - link->next = next; - next->prev = link; - link->prev = this; - next = link; + /*! + * @brief Insert `this` into a list. + * @brief If `this` is already in another list, `Remove()` must first be called! (Not doing so will result in the list (`this` is in) getting corrupted) + * @param after The link to insert `this` after. + */ + void Insert(CLink* after) { + assert(after); + + next = after->next; + next->prev = this; + + prev = after; + prev->next = this; } }; - VALIDATE_SIZE(CLink, 0xC); diff --git a/source/game_sa/Core/LinkList.h b/source/game_sa/Core/LinkList.h index 0f432a13e2..db6a8c5080 100644 --- a/source/game_sa/Core/LinkList.h +++ b/source/game_sa/Core/LinkList.h @@ -10,11 +10,11 @@ template class CLinkList { public: - CLink usedListHead; - CLink usedListTail; - CLink freeListHead; - CLink freeListTail; - CLink* links; + CLink usedListHead{}; + CLink usedListTail{}; + CLink freeListHead{}; + CLink freeListTail{}; + CLink* links{}; void* operator new(unsigned size) { return ((void*(__cdecl*)(uint32))0x821195)(size); @@ -29,15 +29,24 @@ template class CLinkList { usedListTail.prev = &usedListHead; freeListHead.next = &freeListTail; freeListTail.prev = &freeListHead; + links = new CLink[count]; for (int32 i = count - 1; i >= 0; i--) { - freeListHead.Insert(&links[i]); + links[i].Insert(&freeListHead); } } void Shutdown() { - delete[] links; - links = nullptr; + delete[] std::exchange(links, nullptr); + } + + /*! + * @brief Insert `link` at head + * @param link The link to insert + */ + void Insert(CLink& link) { + link.Remove(); + link.Insert(&usedListHead); } CLink* Insert(T const& data) { @@ -45,8 +54,7 @@ template class CLinkList { if (link == &freeListTail) return nullptr; link->data = data; - link->Remove(); - usedListHead.Insert(link); + Insert(*link); return link; } @@ -61,7 +69,8 @@ template class CLinkList { return nullptr; link->data = data; link->Remove(); - i->prev->Insert(link); + link->Insert(i->prev); + //i->prev->Insert(link); return link; } @@ -71,13 +80,28 @@ template class CLinkList { } } - void Remove(CLink* link) { - link->Remove(); - freeListHead.Insert(link); + auto Remove(CLink* l) { + l->Remove(); + l->Insert(&freeListHead); + return l; } auto GetTail() { return usedListTail.prev; } + auto& GetTailLink() { return usedListTail; } + auto GetHead() { return usedListHead.next; } + auto& GetHeadLink() { return usedListHead; } + + + //void SetHead(CLink* l) { + // auto& h = usedListHead; + // + // l->next = h.next; + // h.next->prev = l; + // + // l->prev = reinterpret_cast*>(this); + // h.next = l; + //} }; VALIDATE_SIZE(CLinkList, 0x34); diff --git a/source/game_sa/Core/Matrix.cpp b/source/game_sa/Core/Matrix.cpp index 2ad766d0ed..735c7ea58e 100644 --- a/source/game_sa/Core/Matrix.cpp +++ b/source/game_sa/Core/Matrix.cpp @@ -36,13 +36,13 @@ void CMatrix::InjectHooks() RH_ScopedInstall(SetRotateXOnly, 0x59AFA0); RH_ScopedInstall(SetRotateYOnly, 0x59AFE0); RH_ScopedInstall(SetRotateZOnly, 0x59B020); + RH_ScopedOverloadedInstall(SetRotate, "xyz", 0x59B120, void(CMatrix::*)(float, float, float)); RH_ScopedInstall(SetRotateX, 0x59B060); RH_ScopedInstall(SetRotateY, 0x59B0A0); RH_ScopedInstall(SetRotateZ, 0x59B0E0); - RH_ScopedOverloadedInstall(SetRotate, "xyz", 0x59B120, void(CMatrix::*)(float, float, float)); - RH_ScopedInstall(RotateX, 0x59B1E0); - RH_ScopedInstall(RotateY, 0x59B2C0); - RH_ScopedInstall(RotateZ, 0x59B390); + //RH_ScopedInstall(RotateX, 0x59B1E0, {.enabled }); // has NOTSA args, cant hook + //RH_ScopedInstall(RotateY, 0x59B2C0, {.enabled }); // has NOTSA args, cant hook + //RH_ScopedInstall(RotateZ, 0x59B390, {.enabled }); // has NOTSA args, cant hook RH_ScopedInstall(Rotate, 0x59B460); RH_ScopedInstall(Reorthogonalise, 0x59B6A0); RH_ScopedInstall(CopyToRwMatrix, 0x59B8B0); @@ -240,34 +240,40 @@ void CMatrix::SetRotate(float x, float y, float z) m_pos.Set(0.0F, 0.0F, 0.0F); } -void CMatrix::RotateX(float angle) +void CMatrix::RotateX(float angle, bool bKeepPos) { auto rotMat = CMatrix(); rotMat.SetRotateX(angle); m_right = rotMat.TransformVector(m_right); m_forward = rotMat.TransformVector(m_forward); m_up = rotMat.TransformVector(m_up); - m_pos = rotMat.TransformVector(m_pos); + if (!bKeepPos) { + m_pos = rotMat.TransformVector(m_pos); + } } -void CMatrix::RotateY(float angle) +void CMatrix::RotateY(float angle, bool bKeepPos) { auto rotMat = CMatrix(); rotMat.SetRotateY(angle); m_right = rotMat.TransformVector(m_right); m_forward = rotMat.TransformVector(m_forward); m_up = rotMat.TransformVector(m_up); - m_pos = rotMat.TransformVector(m_pos); + if (!bKeepPos) { + m_pos = rotMat.TransformVector(m_pos); + } } -void CMatrix::RotateZ(float angle) +void CMatrix::RotateZ(float angle, bool bKeepPos) { auto rotMat = CMatrix(); rotMat.SetRotateZ(angle); m_right = rotMat.TransformVector(m_right); m_forward = rotMat.TransformVector(m_forward); m_up = rotMat.TransformVector(m_up); - m_pos = rotMat.TransformVector(m_pos); + if (!bKeepPos) { + m_pos = rotMat.TransformVector(m_pos); + } } // rotate on 3 axes diff --git a/source/game_sa/Core/Matrix.h b/source/game_sa/Core/Matrix.h index a168fbf11c..46a492306e 100644 --- a/source/game_sa/Core/Matrix.h +++ b/source/game_sa/Core/Matrix.h @@ -119,9 +119,9 @@ class CMatrix { void SetRotateY(float angle); void SetRotateZ(float angle); void SetRotate(float x, float y, float z); // set rotate on 3 axes (Values are in radians) - void RotateX(float angle); - void RotateY(float angle); - void RotateZ(float angle); + void RotateX(float angle, bool bKeepPos = false); // NOTSA bKeepPos + void RotateY(float angle, bool bKeepPos = false); // NOTSA bKeepPos + void RotateZ(float angle, bool bKeepPos = false); // NOTSA bKeepPos void Rotate(CVector rotation); // rotate on 3 axes void Reorthogonalise(); void CopyToRwMatrix(RwMatrix* matrix) const; // similar to UpdateRW(RwMatrixTag *) diff --git a/source/game_sa/Core/Quaternion.cpp b/source/game_sa/Core/Quaternion.cpp index 5413f1b9fc..098090e570 100644 --- a/source/game_sa/Core/Quaternion.cpp +++ b/source/game_sa/Core/Quaternion.cpp @@ -99,11 +99,16 @@ void CQuaternion::Copy(const CQuaternion& from) { } // Gets a dot product for quats -void CQuaternion::Dot(const CQuaternion& a) { - ((void(__thiscall*)(CQuaternion*, const CQuaternion&))0x4CFA00)(this, a); +float CQuaternion::Dot(const CQuaternion& rhs) { + return this->w * rhs.w + this->z * rhs.z + this->y * rhs.y + this->x * rhs.x; } // Normalises a quat void CQuaternion::Normalise() { - ((void(__thiscall*)(CQuaternion*))0x4D1610)(this); + const auto sqMag = GetLengthSquared(); + if (sqMag == 0.f) { + w = 1.0; + } else { + *this = *this / std::sqrt(sqMag); + } } diff --git a/source/game_sa/Core/Quaternion.h b/source/game_sa/Core/Quaternion.h index 07bba2e55a..0e8dbed0c8 100644 --- a/source/game_sa/Core/Quaternion.h +++ b/source/game_sa/Core/Quaternion.h @@ -24,7 +24,8 @@ class CQuaternion { public: static void InjectHooks(); - constexpr CQuaternion() {}; + constexpr CQuaternion(const RtQuat& q) : imag{q.imag}, real{q.real} {} + constexpr CQuaternion() : x{}, y{}, z{}, w{} {} constexpr CQuaternion(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {} // Quat to matrix @@ -68,7 +69,7 @@ class CQuaternion { void Copy(const CQuaternion& from); // Gets a dot product for quats - void Dot(const CQuaternion& a); + float Dot(const CQuaternion& a); // Normalises a quat void Normalise(); @@ -112,6 +113,8 @@ class CQuaternion { // NOTSA RtQuat* AsRtQuat() { return (RtQuat*)this; } + + operator RtQuat() const { return RtQuat{x, y, z, w}; } }; VALIDATE_SIZE(CQuaternion, 0x10); diff --git a/source/game_sa/CutsceneMgr.cpp b/source/game_sa/CutsceneMgr.cpp index e66121dabd..d04ddb2322 100644 --- a/source/game_sa/CutsceneMgr.cpp +++ b/source/game_sa/CutsceneMgr.cpp @@ -50,7 +50,7 @@ void CCutsceneMgr::AttachObjectToBone(CCutsceneObject* attachment, CCutsceneObje // 0x5B0480 void CCutsceneMgr::AttachObjectToFrame(CCutsceneObject* attachment, CEntity* object, const char* frameName) { attachment->m_pAttachmentObject = nullptr; - attachment->m_pAttachToFrame = RpAnimBlendClumpFindFrame(object->m_pRwClump, frameName)->m_pFrame; + attachment->m_pAttachToFrame = RpAnimBlendClumpFindFrame(object->m_pRwClump, frameName)->Frame; } // 0x5B04B0 @@ -874,20 +874,20 @@ void CCutsceneMgr::SetCutsceneAnim(const char* animName, CObject* object) { const auto cpyOfTheAnim = ms_cutsceneAssociations.CopyAnimation(animName); CStreaming::IHaveUsedStreamingMemory(); - cpyOfTheAnim->SetFlag(ANIMATION_TRANSLATE_Y, true); + cpyOfTheAnim->SetFlag(ANIMATION_CAN_EXTRACT_VELOCITY, true); cpyOfTheAnim->Start(0.f); - const auto blendData = RpClumpGetAnimBlendClumpData(object->m_pRwClump); - blendData->m_Anims.Prepend(&cpyOfTheAnim->m_Link); + const auto blendData = RpAnimBlendClumpGetData(object->m_pRwClump); + blendData->m_AnimList.Prepend(&cpyOfTheAnim->m_Link); if (cpyOfTheAnim->m_BlendHier->m_bKeepCompressed) { - blendData->m_Frames->m_bIsCompressed = true; + blendData->m_FrameDatas[0].IsCompressed = true; } } // 0x5B0420 void CCutsceneMgr::SetCutsceneAnimToLoop(const char* animName) { - ms_cutsceneAssociations.GetAnimation(animName)->m_Flags |= ANIMATION_LOOPED; + ms_cutsceneAssociations.GetAnimation(animName)->m_Flags |= ANIMATION_IS_LOOPED; } // 0x5B0440 @@ -910,8 +910,8 @@ void CCutsceneMgr::SetupCutsceneToStart() { if (const auto anim = RpAnimBlendClumpGetFirstAssociation(csobj->m_pRwClump)) { if (csobj->m_pAttachToFrame) { - anim->SetFlag(ANIMATION_TRANSLATE_Y, false); - anim->SetFlag(ANIMATION_STARTED, true); + anim->SetFlag(ANIMATION_CAN_EXTRACT_VELOCITY, false); + anim->SetFlag(ANIMATION_IS_PLAYING, true); } else { // Get anim translation and offset the object's position by it const CVector animTrans = anim->m_BlendHier->m_bIsCompressed @@ -920,7 +920,7 @@ void CCutsceneMgr::SetupCutsceneToStart() { SetObjPos(ms_cutsceneOffset + animTrans); // Start the anim - anim->SetFlag(ANIMATION_STARTED, true); + anim->SetFlag(ANIMATION_IS_PLAYING, true); } } else { // Object has no animation applied SetObjPos(ms_cutsceneOffset); diff --git a/source/game_sa/Entity/Entity.cpp b/source/game_sa/Entity/Entity.cpp index cb1371821b..e74c77c3b9 100644 --- a/source/game_sa/Entity/Entity.cpp +++ b/source/game_sa/Entity/Entity.cpp @@ -390,16 +390,12 @@ void CEntity::DeleteRwObject() m_pRwObject = nullptr; auto mi = CModelInfo::GetModelInfo(m_nModelIndex); mi->RemoveRef(); - CStreaming::RemoveEntity(m_pStreamingLink); - m_pStreamingLink = nullptr; + CStreaming::RemoveEntity(std::exchange(m_pStreamingLink, nullptr)); if (IsBuilding()) --gBuildings; - if (mi->GetModelType() == MODEL_INFO_CLUMP - && mi->IsRoad() - && !IsObject()) { - + if (mi->GetModelType() == MODEL_INFO_CLUMP && mi->IsRoad() && !IsObject()) { CWorld::ms_listMovingEntityPtrs.DeleteItem(this); } diff --git a/source/game_sa/Entity/Object/CutsceneObject.cpp b/source/game_sa/Entity/Object/CutsceneObject.cpp index ea9536b02b..753d30b7fb 100644 --- a/source/game_sa/Entity/Object/CutsceneObject.cpp +++ b/source/game_sa/Entity/Object/CutsceneObject.cpp @@ -42,9 +42,9 @@ void CCutsceneObject::SetModelIndex(unsigned index) { CEntity::SetModelIndex(index); if (RwObjectGetType(m_pRwObject) == rpCLUMP) { RpAnimBlendClumpInit(m_pRwClump); - auto* animData = RpClumpGetAnimBlendClumpData(m_pRwClump); + auto* animData = RpAnimBlendClumpGetData(m_pRwClump); animData->m_PedPosition = &m_vecMoveSpeed; - animData->m_Frames->m_bUpdateSkinnedWith3dVelocityExtraction = true; + animData->m_FrameDatas[0].HasZVelocity = true; CCutsceneObject::SetupCarPipeAtomicsForClump(index, m_pRwClump); } GetModelInfo()->m_nAlpha = 0xFF; @@ -102,10 +102,10 @@ void CCutsceneObject::PreRender() { const auto* firstAtomic = GetFirstAtomic(m_pRwClump); if (firstAtomic) { if (RpSkinGeometryGetSkin(RpAtomicGetGeometry(firstAtomic))) { - auto* animData = RpClumpGetAnimBlendClumpData(m_pRwClump); + auto* animData = RpAnimBlendClumpGetData(m_pRwClump); auto* morphTarget = RpGeometryGetMorphTarget(RpAtomicGetGeometry(firstAtomic), 0); auto* sphere = RpMorphTargetGetBoundingSphere(morphTarget); - sphere->center = animData->m_Frames[0].GetFrameTranslation(); + sphere->center = animData->m_FrameDatas[0].BonePos; } } } diff --git a/source/game_sa/Entity/Ped/Ped.cpp b/source/game_sa/Entity/Ped/Ped.cpp index 0b323191a3..fc584d1653 100644 --- a/source/game_sa/Entity/Ped/Ped.cpp +++ b/source/game_sa/Entity/Ped/Ped.cpp @@ -422,10 +422,10 @@ void CPed::SetMoveAnim() { case PEDMOVE_WALK: case PEDMOVE_RUN: case PEDMOVE_SPRINT: { - for (auto assoc = RpAnimBlendClumpGetFirstAssociation(m_pRwClump, ANIMATION_PARTIAL); assoc; assoc = RpAnimBlendGetNextAssociation(assoc, ANIMATION_PARTIAL)) { - if ((assoc->m_Flags & ANIMATION_UNLOCK_LAST_FRAME) == 0 && (assoc->m_Flags & ANIMATION_ADD_TO_BLEND) == 0) { + for (auto assoc = RpAnimBlendClumpGetFirstAssociation(m_pRwClump, ANIMATION_IS_PARTIAL); assoc; assoc = RpAnimBlendGetNextAssociation(assoc, ANIMATION_IS_PARTIAL)) { + if ((assoc->m_Flags & ANIMATION_IS_FINISH_AUTO_REMOVE) == 0 && (assoc->m_Flags & ANIMATION_DONT_ADD_TO_PARTIAL_BLEND) == 0) { assoc->m_BlendDelta = -2.f; - assoc->SetFlag(ANIMATION_FREEZE_LAST_FRAME, true); + assoc->SetFlag(ANIMATION_IS_BLEND_AUTO_REMOVE, true); } } @@ -618,7 +618,7 @@ RpHAnimHierarchy& CPed::GetAnimHierarchy() const { } CAnimBlendClumpData& CPed::GetAnimBlendData() const { - return *RpClumpGetAnimBlendClumpData(m_pRwClump); + return *RpAnimBlendClumpGetData(m_pRwClump); } /*! @@ -716,8 +716,8 @@ void CPed::SetMoveAnimSpeed(CAnimBlendAssociation* association) { */ void CPed::StopNonPartialAnims() { for (auto assoc = RpAnimBlendClumpGetFirstAssociation(m_pRwClump); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { - if ((assoc->m_Flags & ANIMATION_PARTIAL) == 0) { - assoc->SetFlag(ANIMATION_STARTED, false); + if ((assoc->m_Flags & ANIMATION_IS_PARTIAL) == 0) { + assoc->SetFlag(ANIMATION_IS_PLAYING, false); } } } @@ -727,8 +727,8 @@ void CPed::StopNonPartialAnims() { */ void CPed::RestartNonPartialAnims() { for (auto assoc = RpAnimBlendClumpGetFirstAssociation(m_pRwClump); assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { - if ((assoc->m_Flags & ANIMATION_PARTIAL) == 0) { - assoc->SetFlag(ANIMATION_STARTED, true); + if ((assoc->m_Flags & ANIMATION_IS_PARTIAL) == 0) { + assoc->SetFlag(ANIMATION_IS_PLAYING, true); } } } @@ -2207,7 +2207,7 @@ void CPed::PlayFootSteps() { } else { if ((lastAssoc->m_nFlags & ANIMATION_ADD_TO_BLEND) == 0) { if (lastAssoc->m_nAnimId != ANIM_ID_FIGHT_IDLE) { - if (lastAssoc->m_nFlags & ANIMATION_PARTIAL || bIsDucking) { + if (lastAssoc->m_nFlags & ANIMATION_IS_PARTIAL || bIsDucking) { idleBlendTotal += lastAssoc->m_fBlendAmount; } } @@ -3165,7 +3165,7 @@ RwObject* SetPedAtomicVisibilityCB(RwObject* rwObject, void* data) { void CPed::RemoveBodyPart(ePedNode pedNode, char localDir) { UNUSED(localDir); - if (m_apBones[pedNode]->m_pIFrame) { + if (m_apBones[pedNode]->KeyFrame) { if (CLocalisation::ShootLimbs()) { bRemoveHead = true; m_nBodypartToRemove = pedNode; @@ -3197,7 +3197,7 @@ uint8 CPed::DoesLOSBulletHitPed(CColPoint& colPoint) { RwV3d headPos{}; // TODO: Doesn't this just return the position of the matrix? Eg.: `BoneMatrix.pos` ? - RwV3dTransformPoint(&headPos, &zero, &GetBoneMatrix((eBoneTag)m_apBones[ePedNode::PED_NODE_HEAD]->m_nNodeId)); + RwV3dTransformPoint(&headPos, &zero, &GetBoneMatrix((eBoneTag)m_apBones[ePedNode::PED_NODE_HEAD]->BoneTag)); if (m_nPedState == PEDSTATE_FALL || colPoint.m_vecPoint.z < headPos.z) { // Ped falling, adjust return 1; @@ -3219,8 +3219,8 @@ void CPed::RemoveWeaponAnims(int32 likeUnused, float blendDelta) { bool bFoundNotPartialAnim{}; for (auto i = 0; i < 34; i++) { // TODO: Magic number `34` if (const auto assoc = RpAnimBlendClumpGetAssociation(m_pRwClump, ANIM_ID_FIRE)) { - assoc->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; - if ((assoc->m_Flags & ANIMATION_PARTIAL)) { + assoc->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; + if ((assoc->m_Flags & ANIMATION_IS_PARTIAL)) { assoc->m_BlendDelta = blendDelta; } else { bFoundNotPartialAnim = true; @@ -3242,7 +3242,7 @@ bool CPed::IsPedHeadAbovePos(float zPos) { RwV3d headPos{}; // TODO: Doesn't this just return the position of the matrix? Eg.: `BoneMatrix.pos` ? - RwV3dTransformPoint(&headPos, &zero, &GetBoneMatrix((eBoneTag)m_apBones[ePedNode::PED_NODE_HEAD]->m_nNodeId)); + RwV3dTransformPoint(&headPos, &zero, &GetBoneMatrix((eBoneTag)m_apBones[ePedNode::PED_NODE_HEAD]->BoneTag)); return zPos + GetPosition().z < headPos.z; } @@ -3360,7 +3360,7 @@ void CPed::SetModelIndex(uint32 modelIndex) { } // Deal with animation stuff once again - RpClumpGetAnimBlendClumpData(m_pRwClump)->m_PedPosition = (CVector*)&m_vecAnimMovingShiftLocal; + RpAnimBlendClumpGetData(m_pRwClump)->m_PedPosition = (CVector*)&m_vecAnimMovingShiftLocal; // Create hit col model if (!mi->m_pHitColModel) { diff --git a/source/game_sa/Entity/Ped/Ped.h b/source/game_sa/Entity/Ped/Ped.h index 4232b26452..582bb77d06 100644 --- a/source/game_sa/Entity/Ped/Ped.h +++ b/source/game_sa/Entity/Ped/Ped.h @@ -40,6 +40,7 @@ class CAnimBlendClumpData; struct RpHAnimHierarchy; enum ePedNode : int32 { + PED_NODE_NULL = 0, PED_NODE_UPPER_TORSO = 1, PED_NODE_HEAD = 2, PED_NODE_LEFT_ARM = 3, diff --git a/source/game_sa/Entity/Ped/PlayerPed.cpp b/source/game_sa/Entity/Ped/PlayerPed.cpp index 141dfdbfeb..537569f777 100644 --- a/source/game_sa/Entity/Ped/PlayerPed.cpp +++ b/source/game_sa/Entity/Ped/PlayerPed.cpp @@ -268,7 +268,7 @@ void CPlayerPed::ReApplyMoveAnims() { addedAnim->m_BlendDelta = anim->m_BlendDelta; addedAnim->m_BlendAmount = anim->m_BlendAmount; - anim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; + anim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; anim->m_BlendDelta = -1000.0f; } } @@ -757,7 +757,7 @@ void CPlayerPed::MakeChangesForNewWeapon(eWeaponType weaponType) { if (auto anim = RpAnimBlendClumpGetAssociation(m_pRwClump, ANIM_ID_FIRE)) - anim->m_Flags |= ANIMATION_STARTED & ANIMATION_UNLOCK_LAST_FRAME; + anim->m_Flags |= ANIMATION_IS_PLAYING & ANIMATION_IS_FINISH_AUTO_REMOVE; TheCamera.ClearPlayerWeaponMode(); } diff --git a/source/game_sa/Entity/Vehicle/Vehicle.cpp b/source/game_sa/Entity/Vehicle/Vehicle.cpp index 727fb149e7..45c6ddb05a 100644 --- a/source/game_sa/Entity/Vehicle/Vehicle.cpp +++ b/source/game_sa/Entity/Vehicle/Vehicle.cpp @@ -3526,27 +3526,20 @@ bool CVehicle::BladeColSectorList(CPtrList& ptrList, CColModel& colModel, CMatri } // 0x6DBA30 -void CVehicle::SetComponentRotation(RwFrame* component, eRotationAxis axis, float angle, bool bResetPosition) { +void CVehicle::SetComponentRotation(RwFrame* component, eRotationAxis axis, float angle, bool bSetRotate) { if (!component) { return; } CMatrix mat{ RwFrameGetMatrix(component) }; - std::invoke( - [bResetPosition, axis]() { - // We're using the `Only` version of `SetRotate`, that way the position - // That way 0x6DBB69 can be omitted (and 0x6DBB02 because it's just there to cancel out the former) - switch (axis) { - case AXIS_Z: return bResetPosition ? &CMatrix::SetRotateZOnly : &CMatrix::RotateZ; - case AXIS_Y: return bResetPosition ? &CMatrix::SetRotateYOnly : &CMatrix::RotateY; - case AXIS_X: return bResetPosition ? &CMatrix::SetRotateXOnly : &CMatrix::RotateX; - default: - NOTSA_UNREACHABLE("Supress warn"); - return &CMatrix::RotateX; - } - }(), - &mat, angle - ); + + switch (axis) { + case AXIS_X: bSetRotate ? mat.SetRotateXOnly(angle) : mat.RotateX(angle, true); break; + case AXIS_Y: bSetRotate ? mat.SetRotateYOnly(angle) : mat.RotateY(angle, true); break; + case AXIS_Z: bSetRotate ? mat.SetRotateZOnly(angle) : mat.RotateZ(angle, true); break; + default: NOTSA_UNREACHABLE(); + } + mat.UpdateRW(); } diff --git a/source/game_sa/Enums/eBoneTag.h b/source/game_sa/Enums/eBoneTag.h index 0daf99bf58..fdea9690cf 100644 --- a/source/game_sa/Enums/eBoneTag.h +++ b/source/game_sa/Enums/eBoneTag.h @@ -11,7 +11,7 @@ enum eBoneTag : int16 { BONE_UNKNOWN = -1, - BONE_NORMAL = 0, // Normal or Root, both are same + BONE_ROOT = 0, // Normal or Root, both are same BONE_PELVIS = 1, BONE_SPINE = 2, BONE_SPINE1 = 3, diff --git a/source/game_sa/Events/EventHandler.cpp b/source/game_sa/Events/EventHandler.cpp index ad2fca6fb6..0c2067f9bd 100644 --- a/source/game_sa/Events/EventHandler.cpp +++ b/source/game_sa/Events/EventHandler.cpp @@ -1059,7 +1059,7 @@ void CEventHandler::ComputeDamageResponse(CEventDamage* e, CTask* tactive, CTask // Otherwise if (!e->m_bFallDown && !notsa::contains({ ANIM_ID_NO_ANIMATION_SET, ANIM_ID_DOOR_LHINGE_O }, e->m_nAnimID)) { - if (CAnimManager::GetAnimAssociation(e->m_nAnimGroup, e->m_nAnimID)->m_Flags & ANIMATION_ADD_TO_BLEND) { // 0x4C04B7 + if (CAnimManager::GetAnimAssociation(e->m_nAnimGroup, e->m_nAnimID)->m_Flags & ANIMATION_DONT_ADD_TO_PARTIAL_BLEND) { // 0x4C04B7 if (!e->GetAnimAdded()) { if (!notsa::contains({ANIM_ID_SHOT_PARTIAL, ANIM_ID_SHOT_LEFTP, ANIM_ID_SHOT_PARTIAL_B, ANIM_ID_SHOT_RIGHTP}, e->m_nAnimID)) { const auto a = CAnimManager::BlendAnimation(m_Ped->m_pRwClump, e->m_nAnimGroup, e->m_nAnimID, e->m_fAnimBlend); diff --git a/source/game_sa/Events/EventScriptCommand.cpp b/source/game_sa/Events/EventScriptCommand.cpp index 7db2a60a45..a87a00475e 100644 --- a/source/game_sa/Events/EventScriptCommand.cpp +++ b/source/game_sa/Events/EventScriptCommand.cpp @@ -48,7 +48,7 @@ int32 CEventScriptCommand::GetEventPriority() const const int32 taskId = m_task->GetTaskType(); if (taskId == TASK_SIMPLE_NAMED_ANIM) { CTaskSimpleRunAnim* pTaskRunAnim = static_cast(m_task); - if (pTaskRunAnim->m_nFlags & ANIMATION_LOOPED) + if (pTaskRunAnim->m_nFlags & ANIMATION_IS_LOOPED) return 71; } if (taskId == TASK_SIMPLE_DIE || taskId == TASK_SIMPLE_SWIM || taskId == TASK_COMPLEX_USE_MOBILE_PHONE) diff --git a/source/game_sa/Fx/FxManager.cpp b/source/game_sa/Fx/FxManager.cpp index b23455e7d7..1467fa71aa 100644 --- a/source/game_sa/Fx/FxManager.cpp +++ b/source/game_sa/Fx/FxManager.cpp @@ -100,7 +100,7 @@ void FxManager_c::DestroyFxSystem(FxSystem_c* system) { for (Particle_c* particle = particles.GetHead(); particle; particle = particles.GetNext(particle)) { if (particle->m_System == system) { - prim->m_Particles.RemoveItem(particle); + prim->m_Particles.RemoveItem(particle); // TODO: Pre-cache next here, because if the item is removed it corrupts the iterator m_FxEmitterParticles.AddItem(particle); } } diff --git a/source/game_sa/IKChain_c.cpp b/source/game_sa/IKChain_c.cpp index ae3e5044b9..74619a278f 100644 --- a/source/game_sa/IKChain_c.cpp +++ b/source/game_sa/IKChain_c.cpp @@ -45,7 +45,7 @@ bool IKChain_c::Init( const auto frames = m_Ped->GetAnimBlendData().GetFrames(); - if (frames[0].m_bCheckBlendNodeClumpKeyFrames || !frames[0].m_bUpdatingFrame) { + if (frames[0].NeedsKeyFrameUpdate || !frames[0].IsUpdatingFrame) { return false; } @@ -55,8 +55,8 @@ bool IKChain_c::Init( // Check if frame of this bone has non-zero translation { - const auto& boneFrame = frames[RpHAnimIDGetIndex(&m_Ped->GetAnimHierarchy(), (RwInt32)effectorBone)].m_pIFrame; - if (boneFrame->translation.IsZero()) { + const auto& boneFrame = frames[RpHAnimIDGetIndex(&m_Ped->GetAnimHierarchy(), (RwInt32)effectorBone)].KeyFrame; + if (boneFrame->t.IsZero()) { return false; } } @@ -270,7 +270,7 @@ void IKChain_c::SetupBones(eBoneTag32 effectorBone, CVector effectorPos, eBoneTa m_BonesCount = 0; for (auto boneIt = effectorBone; boneIt != pivotBone; boneIt = GetBoneLinkPrev(boneIt)) { const auto node = g_boneNodeMan.GetBoneNode(); - node->Init(boneIt, frames[RpHAnimIDGetIndex(&m_Ped->GetAnimHierarchy(), (RwInt32)boneIt)].m_pIFrame); + node->Init(boneIt, frames[RpHAnimIDGetIndex(&m_Ped->GetAnimHierarchy(), (RwInt32)boneIt)].KeyFrame); bones[m_BonesCount++] = node; } diff --git a/source/game_sa/Models/ClumpModelInfo.cpp b/source/game_sa/Models/ClumpModelInfo.cpp index 047d3f3e36..1d064bab93 100644 --- a/source/game_sa/Models/ClumpModelInfo.cpp +++ b/source/game_sa/Models/ClumpModelInfo.cpp @@ -21,8 +21,8 @@ void CClumpModelInfo::InjectHooks() RH_ScopedVMTInstall(Shutdown, 0x4C4E60); RH_ScopedVMTInstall(DeleteRwObject, 0x4C4E70); RH_ScopedVMTInstall(GetRwModelType, 0x4C5730); - // clang moment: RH_ScopedVirtualOverloadedInstall(CreateInstance, "void", 0x4C5140, RwObject * (CClumpModelInfo::*)()); - // clang moment: RH_ScopedVirtualOverloadedInstall(CreateInstance, "mat", 0x4C5110, RwObject * (CClumpModelInfo::*)(RwMatrix*)); + RH_ScopedVMTOverloadedInstall(CreateInstance, "void", 0x4C5140, RwObject * (CClumpModelInfo::*)()); + RH_ScopedVMTOverloadedInstall(CreateInstance, "mat", 0x4C5110, RwObject * (CClumpModelInfo::*)(RwMatrix*)); RH_ScopedVMTInstall(SetAnimFile, 0x4C5200); RH_ScopedVMTInstall(ConvertAnimFileIndex, 0x4C5250); RH_ScopedVMTInstall(GetAnimFileIndex, 0x4C5740); @@ -109,7 +109,7 @@ RwObject* CClumpModelInfo::CreateInstance() if (bHasAnimBlend) { RpAnimBlendClumpInit(clonedClump); if (const auto anim = CAnimManager::GetAnimation(m_nKey, &CAnimManager::GetAnimBlocks()[m_nAnimFileIndex])) { - CAnimManager::BlendAnimation(clonedClump, anim, ANIMATION_LOOPED, 1.0F); + CAnimManager::BlendAnimation(clonedClump, anim, ANIMATION_IS_LOOPED, 1.0F); } } diff --git a/source/game_sa/PathFind.cpp b/source/game_sa/PathFind.cpp index 85ff5eabc6..1327e7dec5 100644 --- a/source/game_sa/PathFind.cpp +++ b/source/game_sa/PathFind.cpp @@ -489,7 +489,7 @@ void CPathFind::ComputeRoute(uint8 nodeType, const CVector& vecStart, const CVec } void CPathFind::SetLinksBridgeLights(float fXMin, float fXMax, float fYMin, float fYMax, bool value) { - const auto areaRect = CRect{ {fXMax, fYMax}, {fXMin, fYMin} }; + const auto areaRect = CRect{ {fXMin, fYMax}, {fXMax, fYMin} }; for (auto areaId = 0u; areaId < NUM_PATH_MAP_AREAS; areaId++) { if (!IsAreaLoaded(areaId)) { diff --git a/source/game_sa/PedIK.cpp b/source/game_sa/PedIK.cpp index 3f9b9e7494..23da9b7d46 100644 --- a/source/game_sa/PedIK.cpp +++ b/source/game_sa/PedIK.cpp @@ -26,9 +26,9 @@ void CPedIK::InjectHooks() { // 0x5FDDB0 void CPedIK::RotateTorso(AnimBlendFrameData* bone, LimbOrientation& orientation, bool changeRoll) { - const auto quat = bone->m_pIFrame->orientation.AsRtQuat(); - RtQuatRotate(quat, &XaxisIK, RadiansToDegrees(orientation.m_fYaw), rwCOMBINEREPLACE); - RtQuatRotate(quat, &ZaxisIK, RadiansToDegrees(orientation.m_fPitch), rwCOMBINEPRECONCAT); + const auto q = &bone->KeyFrame->q; + RtQuatRotate(q, &XaxisIK, RadiansToDegrees(orientation.m_fYaw), rwCOMBINEREPLACE); + RtQuatRotate(q, &ZaxisIK, RadiansToDegrees(orientation.m_fPitch), rwCOMBINEPRECONCAT); m_pPed->bDontAcceptIKLookAts = true; } @@ -62,9 +62,9 @@ void CPedIK::RotateTorsoForArm(const CVector& direction) { if (resultAngle != DegreesToRadians(0.0f)) { const auto degreesHalf = RadiansToDegrees(resultAngle / 2.0f); if (bRotateWithNeck) { // android doesn't have this check - RtQuatRotate(m_pPed->m_apBones[PED_NODE_NECK]->m_pIFrame->orientation.AsRtQuat(), &XaxisIK, degreesHalf, rwCOMBINEPOSTCONCAT); + RtQuatRotate(&m_pPed->m_apBones[PED_NODE_NECK]->KeyFrame->q, &XaxisIK, degreesHalf, rwCOMBINEPOSTCONCAT); } - RtQuatRotate(m_pPed->m_apBones[PED_NODE_UPPER_TORSO]->m_pIFrame->orientation.AsRtQuat(), &XaxisIK, degreesHalf, rwCOMBINEPOSTCONCAT); + RtQuatRotate(&m_pPed->m_apBones[PED_NODE_UPPER_TORSO]->KeyFrame->q, &XaxisIK, degreesHalf, rwCOMBINEPOSTCONCAT); } } @@ -75,7 +75,7 @@ bool CPedIK::PointGunInDirection(float zAngle, float distance, bool flag, float const auto angle = CGeneral::LimitRadianAngle(zAngle - m_pPed->m_fCurrentRotation); const auto hier = GetAnimHierarchyFromSkinClump(m_pPed->m_pRwClump); - const auto index = RpHAnimIDGetIndex(hier, m_pPed->m_apBones[PED_NODE_RIGHT_CLAVICLE]->m_nNodeId); + const auto index = RpHAnimIDGetIndex(hier, m_pPed->m_apBones[PED_NODE_RIGHT_CLAVICLE]->BoneTag); // unused code // auto* boneMatrix = RwMatrixCreate(); @@ -109,9 +109,9 @@ bool CPedIK::PointGunInDirection(float zAngle, float distance, bool flag, float flag ? std::sin(headAngle) : std::cos(headAngle) }; - auto Torsoframe = m_pPed->m_apBones[PED_NODE_UPPER_TORSO]; - RtQuatRotate(Torsoframe->GetFrameOrientation().AsRtQuat(), &axis, RadiansToDegrees(m_TorsoOrient.m_fPitch), rwCOMBINEPOSTCONCAT); - RtQuatRotate(Torsoframe->GetFrameOrientation().AsRtQuat(), &XaxisIK, RadiansToDegrees(m_TorsoOrient.m_fYaw), rwCOMBINEPOSTCONCAT); + const auto torsoQ = &m_pPed->m_apBones[PED_NODE_UPPER_TORSO]->KeyFrame->q; + RtQuatRotate(torsoQ, &axis, RadiansToDegrees(m_TorsoOrient.m_fPitch), rwCOMBINEPOSTCONCAT); + RtQuatRotate(torsoQ, &XaxisIK, RadiansToDegrees(m_TorsoOrient.m_fYaw), rwCOMBINEPOSTCONCAT); m_pPed->bUpdateMatricesRequired = true; return canReach; @@ -146,7 +146,7 @@ void CPedIK::PointGunAtPosition(const CVector& posn, float normalize) { // 0x5FE0E0 void CPedIK::PitchForSlope() { - const auto clumpData = RpClumpGetAnimBlendClumpData(m_pPed->m_pRwClump); + const auto clumpData = RpAnimBlendClumpGetData(m_pPed->m_pRwClump); const auto hier = GetAnimHierarchyFromSkinClump(m_pPed->m_pRwClump); if (std::abs(m_fBodyRoll) > 0.01f) { @@ -172,7 +172,7 @@ void CPedIK::PitchForSlope() { }; const auto RotateBone = [clumpData, &hier](eBoneTag bone, float angle, const CVector& axis = ZaxisIK) { - RtQuatRotate(clumpData->m_Frames[RpHAnimIDGetIndex(hier, bone)].GetFrameOrientation().AsRtQuat(), &axis, angle, rwCOMBINEPRECONCAT); + RtQuatRotate(&clumpData->m_FrameDatas[RpHAnimIDGetIndex(hier, bone)].KeyFrame->q, &axis, angle, rwCOMBINEPRECONCAT); }; if (std::abs(m_fSlopePitch) > 0.01f) { diff --git a/source/game_sa/PedIntelligence.cpp b/source/game_sa/PedIntelligence.cpp index 65d72f13d0..10eb4f971e 100644 --- a/source/game_sa/PedIntelligence.cpp +++ b/source/game_sa/PedIntelligence.cpp @@ -355,7 +355,7 @@ bool CPedIntelligence::GetUsingParachute() { return false; } - auto animAssoc = RpAnimBlendClumpGetFirstAssociation(m_pPed->m_pRwClump, ANIMATION_PARTIAL); + auto animAssoc = RpAnimBlendClumpGetFirstAssociation(m_pPed->m_pRwClump, ANIMATION_IS_PARTIAL); if (!animAssoc) { return false; } diff --git a/source/game_sa/Plugins/RpAnimBlendPlugin/RpAnimBlend.cpp b/source/game_sa/Plugins/RpAnimBlendPlugin/RpAnimBlend.cpp index c98e5dd38b..829bfc388f 100644 --- a/source/game_sa/Plugins/RpAnimBlendPlugin/RpAnimBlend.cpp +++ b/source/game_sa/Plugins/RpAnimBlendPlugin/RpAnimBlend.cpp @@ -2,185 +2,1854 @@ #include "RpAnimBlend.h" -// 0x4D6150 -bool RpAnimBlendPluginAttach() { - return plugin::CallAndReturn(); +static uint32& ClumpOffset = *(uint32*)0xB5F878; + +CAnimBlendClumpData*& RpAnimBlendClumpGetData(RpClump* clump) { + return *RWPLUGINOFFSET(CAnimBlendClumpData*, clump, ClumpOffset); +} + +// 0x4D5F40 +void* ClumpAnimConstruct(void* object, RwInt32 offsetInObject, RwInt32 sizeInObject) { + UNUSED(offsetInObject); + UNUSED(sizeInObject); + + const auto clump = static_cast(object); + + RpAnimBlendClumpGetData(clump) = nullptr; + + return object; +} + +// 0x4D6110 +void* ClumpAnimDestruct(void* object, RwInt32 offsetInObject, RwInt32 sizeInObject) { + UNUSED(offsetInObject); + UNUSED(sizeInObject); + + const auto clump = static_cast(object); + + if (auto& bd = RpAnimBlendClumpGetData(clump)) { + RpAnimBlendClumpRemoveAllAssociations(clump); + delete std::exchange(bd, nullptr); + } + + return object; +} + +// 0x4D5F90 +void* ClumpAnimCopy(void* dstObject, const void* srcObject, RwInt32 offsetInObject, RwInt32 sizeInObject) { + UNUSED(dstObject); + UNUSED(srcObject); + UNUSED(offsetInObject); + UNUSED(sizeInObject); + + return nullptr; } // 0x4D5FA0 -void* RtAnimBlendKeyFrameApply(void* result, void* frame) { - return plugin::CallAndReturn(result, frame); - auto matrix = static_cast(result); - auto quat = static_cast(frame); +void RtAnimBlendKeyFrameApply(void* voidMat, void* voidFrame) { // Semantically same as the original `RpHAnimBlendKeyFrameApply` + const auto mat = static_cast(voidMat); + const auto frame = static_cast(voidFrame); + + RtQuatUnitConvertToMatrix(&frame->q, mat); + RwV3dAssign(RwMatrixGetPos(mat), &frame->t); +} + +// 0x4D60C0 +void RpAnimBlendKeyFrameInterpolate(void* voidOut, void* voidIn1, void* voidIn2, float time, void* customData) { + UNUSED(voidIn1); + UNUSED(voidIn2); + UNUSED(time); + UNUSED(customData); + + const auto out = static_cast(voidOut); + *out = {}; +} + +// 0x4D6150 +bool RpAnimBlendPluginAttach() { + ClumpOffset = RpClumpRegisterPlugin( + sizeof(CAnimBlendClumpData*), + rwID_RPANIMBLENDPLUGIN, + &ClumpAnimConstruct, + &ClumpAnimDestruct, + &ClumpAnimCopy + ); + + if (ClumpOffset == -1) { + return false; + } + + RtAnimInterpolatorInfo rtInfo{ + .typeID = rwID_RPANIMBLENDPLUGIN, + + .interpKeyFrameSize = sizeof(RpHAnimBlendInterpFrame), + .animKeyFrameSize = sizeof(RpHAnimKeyFrame), + + .keyFrameApplyCB = RtAnimBlendKeyFrameApply, + .keyFrameBlendCB = RpHAnimKeyFrameBlend, + .keyFrameInterpolateCB = RpAnimBlendKeyFrameInterpolate, + .keyFrameAddCB = RpHAnimKeyFrameAdd, + .keyFrameMulRecipCB = RpHAnimKeyFrameMulRecip, + .keyFrameStreamReadCB = RpHAnimKeyFrameStreamRead, + .keyFrameStreamWriteCB = RpHAnimKeyFrameStreamWrite, + .keyFrameStreamGetSizeCB = RpHAnimKeyFrameStreamGetSize, + + .customDataSize = 0 + }; + RtAnimRegisterInterpolationScheme(&rtInfo); + + return true; } // 0x4D5F50 -CAnimBlendClumpData* RpAnimBlendAllocateData(RpClump* clump) { - return plugin::CallAndReturn(clump); +void RpAnimBlendAllocateData(RpClump* clump) { + RpAnimBlendClumpGetData(clump) = new CAnimBlendClumpData; +} + +// 0x4D6510 +void RpAnimBlendClumpInitSkinned(RpClump* clump) { // Can't hook, `clump` passed in eax + const auto bd = RpAnimBlendClumpGetData(clump); + + constexpr size_t MAX_NUM_BONES = 64; + + const auto skinGeo = RpAtomicGetGeometry(GetFirstAtomic(clump)); + const auto skin = RpSkinGeometryGetSkin(skinGeo); + const auto nBones = RpSkinGetNumBones(skin); + const auto rpHAHier = GetAnimHierarchyFromSkinClump(clump); + + bd->SetNumberOfBones(nBones); + + // Get bone positions + CVector bonePositions[MAX_NUM_BONES]; + { // 0x735360 (SkinGetBonePositionsToTable) + assert(MAX_NUM_BONES >= nBones); + + bonePositions[0] = CVector{0.f, 0.f, 0.f}; // root bone + + uint32 nodeStk[MAX_NUM_BONES]{}; + uint32* nodeStkPtr{nodeStk}; + uint32 currNodeIdx{}; + + for (uint32 i = 1; i < nBones; i++) { // Intentionally starting at 1 + // Calculate inverse matrix of this bone + RwMatrix invBoneMat; + RwMatrixInvert(&invBoneMat, &RpSkinGetSkinToBoneMatrices(skin)[i]); // Originally they did a copy here, not sure why + + // Calculate position of this bone + RwV3dTransformPoint( + &bonePositions[i], + RwMatrixGetPos(&invBoneMat), + &RpSkinGetSkinToBoneMatrices(skin)[currNodeIdx] + ); + + // Handle node stack now + const auto nodeFlags = rpHAHier->pNodeInfo[i].flags; + if (nodeFlags & rpHANIMPUSHPARENTMATRIX) { + *++nodeStkPtr = currNodeIdx; + } + currNodeIdx = nodeFlags & rpHANIMPOPPARENTMATRIX + ? *nodeStkPtr-- + : i; + } + } + + // Now, fill in the frame blend data from the positions we've just calculated + for (size_t i = 0; i < nBones; i++) { + const auto fd = &bd->m_FrameDatas[i]; // Frame blend data + + fd->KeyFrame = (RpHAnimBlendInterpFrame*)rtANIMGETINTERPFRAME(rpHAHier->currentAnim, i); + fd->BoneTag = rpHAHier->pNodeInfo[i].nodeID; + fd->BonePos = bonePositions[i]; + } + + // Initialize all frames now + bd->ForAllFramesF([](AnimBlendFrameData* fd) { // 0x4D6500 (FrameInitCBskin) + fd->Flags = 0; + }); +} + +RwFrame* SetFrameDataFrameCB(RwFrame* f, void* data) { + const auto fd = static_cast(data); + (*fd)->Frame = f; + (*fd)++; + RwFrameForAllChildren(f, SetFrameDataFrameCB, fd); + return f; +} + +// 0x +void RpAnimBlendClumpInitNonSkinned(RpClump* clump) { + const auto bd = RpAnimBlendClumpGetData(clump); + + // Number of bones is the count number of child frames (excluding root) + bd->SetNumberOfBones(RwFrameCount(RpClumpGetFrame(clump)) - 1); // They didn't use RwFrameCount, but I will + + // Assign frames to our frame data recursively + auto fd = bd->m_FrameDatas; + RwFrameForAllChildren(RpClumpGetFrame(clump), SetFrameDataFrameCB, &fd); + + // Initialize all frames now (Could've done this in the above step too, oh well) + bd->ForAllFramesF([](AnimBlendFrameData* fd) { // 0x4D6640 (FrameInitCBnonskin) + fd->Flags = 0; + fd->FramePos = *RwMatrixGetPos(RwFrameGetMatrix(fd->Frame)); + fd->BoneTag = BONE_UNKNOWN; + }); +} + +// 0x4D6720 +void RpAnimBlendClumpInit(RpClump* clump) { + RpAnimBlendAllocateData(clump); // Moved this common piece out of the 2 functions + if (IsClumpSkinned(clump)) { + RpAnimBlendClumpInitSkinned(clump); + } else { + RpAnimBlendClumpInitNonSkinned(clump); + } + RpAnimBlendClumpGetData(clump)->GetRootFrameData().HasVelocity = true; } // 0x4D6790 -CAnimBlendAssociation* RpAnimBlendClumpAddAssociation(RpClump* clump, CAnimBlendAssociation* association, uint32 flags, float startTime, float blendAmount) { - return plugin::CallAndReturn(clump, association, flags, startTime, blendAmount); +CAnimBlendAssociation* RpAnimBlendClumpAddAssociation(RpClump* clump, CAnimBlendAssociation* association, uint32 playFlags, float startTime, float blendAmount) { + NOTSA_UNREACHABLE("Unused function"); +} + +//! @notsa +CAnimBlendLink& RpAnimBlendClumpGetAssociations(RpClump* clump) { + const auto bd = RpAnimBlendClumpGetData(clump); + + return bd->m_AnimList; +} + +// 0x4D58A0 +eBoneTag32 ConvertPedNode2BoneTag(ePedNode pedNode) { + switch (pedNode) { + case PED_NODE_UPPER_TORSO: return BONE_SPINE1; + case PED_NODE_HEAD: return BONE_HEAD; + case PED_NODE_LEFT_ARM: return BONE_L_UPPER_ARM; + case PED_NODE_RIGHT_ARM: return BONE_R_UPPER_ARM; + case PED_NODE_LEFT_HAND: return BONE_L_HAND; + case PED_NODE_RIGHT_HAND: return BONE_R_HAND; + case PED_NODE_LEFT_LEG: return BONE_L_THIGH; + case PED_NODE_RIGHT_LEG: return BONE_R_THIGH; + case PED_NODE_LEFT_FOOT: return BONE_L_FOOT; + case PED_NODE_RIGHT_FOOT: return BONE_R_FOOT; + case PED_NODE_RIGHT_LOWER_LEG: return BONE_R_CALF; + case PED_NODE_LEFT_LOWER_LEG: return BONE_L_CALF; + case PED_NODE_LEFT_LOWER_ARM: return BONE_L_FORE_ARM; + case PED_NODE_RIGHT_LOWER_ARM: return BONE_R_FORE_ARM; + case PED_NODE_LEFT_CLAVICLE: return BONE_L_CLAVICLE; + case PED_NODE_RIGHT_CLAVICLE: return BONE_R_CLAVICLE; + case PED_NODE_NECK: return BONE_NECK; + case PED_NODE_JAW: return BONE_JAW; + default: return BONE_UNKNOWN; + } +} + +// 0x4D56F0 +const char* ConvertBoneTag2BoneName(eBoneTag32 boneTag) { + switch (boneTag) { + case BONE_R_BREAST: return "R Breast"; + case BONE_L_BREAST: return "L Breast"; + case BONE_BELLY: return "Belly"; + case BONE_ROOT: return "Root"; + case BONE_PELVIS: return "Pelvis"; + case BONE_SPINE: return "Spine"; + case BONE_SPINE1: return "Spine1"; + case BONE_NECK: return "Neck"; + case BONE_HEAD: return "Head"; + case BONE_L_BROW: return "L Brow"; + case BONE_R_BROW: return "R Brow"; + case BONE_JAW: return "Jaw"; + case BONE_R_CLAVICLE: return "Bip01 R Clavicle"; + case BONE_R_UPPER_ARM: return "R UpperArm"; + case BONE_R_FORE_ARM: return "R Forearm"; + case BONE_R_HAND: return "R Hand"; + case BONE_R_FINGER: return "R Fingers"; + case BONE_R_FINGER_01: return "R Finger01"; + case BONE_L_CLAVICLE: return "Bip01 L Clavicle"; + case BONE_L_UPPER_ARM: return "L UpperArm"; + case BONE_L_FORE_ARM: return "L Forearm"; + case BONE_L_HAND: return "L Hand"; + case BONE_L_FINGER: return "L Fingers"; + case BONE_L_FINGER_01: return "L Finger01"; + case BONE_L_THIGH: return "L Thigh"; + case BONE_L_CALF: return "L Calf"; + case BONE_L_FOOT: return "L Foot"; + case BONE_L_TOE_0: return "L Toe"; + case BONE_R_THIGH: return "R Thigh"; + case BONE_R_CALF: return "R Calf"; + case BONE_R_FOOT: return "R Foot"; + case BONE_R_TOE_0: return "R Toe"; + default: return nullptr; + } } // 0x4D6BE0 CAnimBlendAssociation* RpAnimBlendClumpExtractAssociations(RpClump* clump) { - return plugin::CallAndReturn(clump); + const auto bd = RpAnimBlendClumpGetData(clump); + + const auto head = std::exchange(bd->m_AnimList.next, nullptr); + return CAnimBlendAssociation::FromLink(head); +} + +// 0x4D6C30 +void RpAnimBlendClumpGiveAssociations(RpClump* clump, CAnimBlendAssociation* associations) { + const auto bd = RpAnimBlendClumpGetData(clump); + + // Delete all animations of this clump + RpAnimBlendClumpRemoveAllAssociations(clump); + + // Use new list of associations + bd->m_AnimList.next = &associations->GetLink(); + associations->GetLink().prev = &bd->m_AnimList; } // 0x4D64A0 -void RpAnimBlendClumpFillFrameArray(RpClump* clump, AnimBlendFrameData** frameData) { - plugin::Call<0x4D64A0, RpClump*, AnimBlendFrameData**>(clump, frameData); +void RpAnimBlendClumpFillFrameArray(RpClump* clump, AnimBlendFrameData** outFrameData) { + const auto bd = RpAnimBlendClumpGetData(clump); + + if (IsClumpSkinned(clump)) { // 0x4D6450 (FillFrameArrayIndiciesSkinned) + const auto ah = GetAnimHierarchyFromClump(clump); + for (size_t i = PED_NODE_UPPER_TORSO; i < TOTAL_PED_NODES; i++) { + outFrameData[i] = &bd->m_FrameDatas[RpHAnimIDGetIndex(ah, ConvertPedNode2BoneTag((ePedNode)i))]; + } + } else { + bd->ForAllFramesF([&](AnimBlendFrameData* fd) { // 0x4D6430 (FillFrameArrayIndiciesNonSkinned) + outFrameData[CVisibilityPlugins::GetFrameHierarchyId(fd->Frame)] = fd; + }); + } } // 0x4D6400 -AnimBlendFrameData* RpAnimBlendClumpFindBone(RpClump* clump, uint32 id) { - return plugin::CallAndReturn(clump, id); +AnimBlendFrameData* RpAnimBlendClumpFindBone(RpClump* clump, eBoneTag32 boneTag) { + const auto bd = RpAnimBlendClumpGetData(clump); + + AnimBlendFrameData* ret{}; + bd->ForAllFramesF([&](AnimBlendFrameData* f) { + if (f->BoneTag == boneTag) { + ret = f; + } + }); + return ret; } // 0x4D62A0 -AnimBlendFrameData* RpAnimBlendClumpFindFrame(RpClump* clump, const char* name) { - return plugin::CallAndReturn(clump, name); +AnimBlendFrameData* RpAnimBlendClumpFindFrame(RpClump* clump, const char* needle) { + const auto bd = RpAnimBlendClumpGetData(clump); + + const auto needlesv = notsa::ci_string_view{ needle }; + AnimBlendFrameData* ret{}; + if (IsClumpSkinned(clump)) { + bd->ForAllFramesF([&](AnimBlendFrameData* f) { // 0x4D6240 + const auto boneName = ConvertBoneTag2BoneName((eBoneTag)f->BoneTag); + if (boneName && needlesv == boneName) { + ret = f; + } + }); + } else { + bd->ForAllFramesF([&](AnimBlendFrameData* f) { // 0x4D6240 + if (needlesv == GetFrameNodeName(f->Frame)) { + ret = f; + } + }); + } + return ret; } // 0x4D6370 -AnimBlendFrameData* RpAnimBlendClumpFindFrameFromHashKey(RpClump* clump, uint32 key) { - return plugin::CallAndReturn(clump, key); +AnimBlendFrameData* RpAnimBlendClumpFindFrameFromHashKey(RpClump* clump, uint32 boneNameKey) { + const auto bd = RpAnimBlendClumpGetData(clump); + + AnimBlendFrameData* ret{}; + if (IsClumpSkinned(clump)) { + bd->ForAllFramesF([&](AnimBlendFrameData* f) { // 0x4D6310 + const auto boneName = ConvertBoneTag2BoneName((eBoneTag)f->BoneTag); + if (boneName && boneNameKey == CKeyGen::GetUppercaseKey(boneName)) { + ret = f; + } + }); + } else { + bd->ForAllFramesF([&](AnimBlendFrameData* f) { // 0x4D6340 + if (boneNameKey == CKeyGen::GetUppercaseKey(GetFrameNodeName(f->Frame))) { + ret = f; + } + }); + } + return ret; +} + +// notsa +template +CAnimBlendAssociation* RpAnimBlendClumpFindAssociationIf_N(RpClump* clump, Fn&& Pred, size_t n) { + const auto bd = RpAnimBlendClumpGetData(clump); + + for (auto l = bd->m_AnimList.next; l;) { + const auto a = CAnimBlendAssociation::FromLink(l); + if (Pred(*a)) { + if (n-- == 0) { + return a; + } + } + l = a->GetLink().next; + } + return nullptr; } // 0x4D68E0 -CAnimBlendAssociation* RpAnimBlendClumpGetAssociation(RpClump* clump, bool bStopFunctionConfusion, CAnimBlendHierarchy* hierarchy) { - return plugin::CallAndReturn(clump, bStopFunctionConfusion, hierarchy); +CAnimBlendAssociation* RpAnimBlendClumpGetAssociation(RpClump* clump, bool, CAnimBlendHierarchy* h) { + return RpAnimBlendClumpFindAssociationIf_N(clump, [h](const CAnimBlendAssociation& a) { + return a.GetHier() == h; + }, 0); } // 0x4D6870 CAnimBlendAssociation* RpAnimBlendClumpGetAssociation(RpClump* clump, const char* name) { - return plugin::CallAndReturn(clump, name); + return RpAnimBlendClumpFindAssociationIf_N(clump, [nameKey = CKeyGen::GetUppercaseKey(name)](const CAnimBlendAssociation& a){ + return a.GetHashKey() == nameKey; + }, 0); } // AnimationId animId // 0x4D68B0 CAnimBlendAssociation* RpAnimBlendClumpGetAssociation(RpClump* clump, uint32 animId) { - return plugin::CallAndReturn(clump, animId); + return RpAnimBlendClumpFindAssociationIf_N(clump, [animId](const CAnimBlendAssociation& a){ + return a.GetAnimId() == animId; + }, 0); } // 0x4D15E0 CAnimBlendAssociation* RpAnimBlendClumpGetFirstAssociation(RpClump* clump) { - return plugin::CallAndReturn(clump); + const auto bd = RpAnimBlendClumpGetData(clump); + + return RpAnimBlendClumpIsInitialized(clump) && bd->m_AnimList.next + ? CAnimBlendAssociation::FromLink(bd->m_AnimList.next) + : nullptr; } // 0x4D6A70 CAnimBlendAssociation* RpAnimBlendClumpGetFirstAssociation(RpClump* clump, uint32 flags) { - return plugin::CallAndReturn(clump, flags); + return RpAnimBlendClumpFindAssociationIf_N(clump, [flags](const CAnimBlendAssociation& a) { + return a.m_Flags & flags; + }, 0); } // 0x4D6910 CAnimBlendAssociation* RpAnimBlendClumpGetMainAssociation(RpClump* clump, CAnimBlendAssociation** pp2ndAnim, float* pBlendVal2nd) { - return plugin::CallAndReturn(clump, pp2ndAnim, pBlendVal2nd); + const auto bd = RpAnimBlendClumpGetData(clump); + + CAnimBlendAssociation *aA{}, *aB{}; + float bA{}, bB{}; + RpAnimBlendClumpForEachAssociation(clump, [&](CAnimBlendAssociation* a) { + if (a->IsPartial()) { + return; + } + const auto blend = a->GetBlendAmount(); + if (blend > bA) { // Found a new main? + aB = std::exchange(aA, a); + bB = std::exchange(bA, blend); + } else if (blend > bB) { // Found a new secondary? + aB = a; + bB = blend; + } + }); + + if (pp2ndAnim) { + *pp2ndAnim = aB; + } + if (pBlendVal2nd) { + *pBlendVal2nd = bB; + } + + return aA; + + /* + * Code works, but i think it I overcomplicated it.... + // Array sorted descending by blend amount + struct { + CAnimBlendAssociation* anim{}; + float blendAmnt{}; + } sorted[2]; + + // Fill array + for (auto l = bd->m_AnimList.next; l;) { + const auto a = CAnimBlendAssociation::FromLink(l); + if (!a->IsPartial()) { + const auto it = rng::upper_bound( + sorted, + a->GetBlendAmount(), + [](float l, float r) { return l > r; }, // Descending + [](auto& v) { return v.blendAmnt; } // By blend amount + ); + if (it != std::end(sorted)) { + std::shift_right(it, std::end(sorted), 1); // Make space for insert + *it = { a, a->GetBlendAmount() }; + } + } + l = a->GetLink().next; + } + + if (pp2ndAnim) { + *pp2ndAnim = sorted[1].anim; + } + if (pBlendVal2nd) { + *pBlendVal2nd = sorted[1].blendAmnt; + } + + return sorted[0].anim; + */ } // 0x4D6A30 -CAnimBlendAssociation* RpAnimBlendClumpGetMainAssociation_N(RpClump* clump, int32 n) { - return plugin::CallAndReturn(clump, n); +CAnimBlendAssociation* RpAnimBlendClumpGetMainAssociation_N(RpClump* clump, uint32 n) { + return RpAnimBlendClumpFindAssociationIf_N(clump, [](const CAnimBlendAssociation& a){ + return !a.IsPartial(); + }, n); } // 0x4D69A0 CAnimBlendAssociation* RpAnimBlendClumpGetMainPartialAssociation(RpClump* clump) { - return plugin::CallAndReturn(clump); + const auto bd = RpAnimBlendClumpGetData(clump); + + CAnimBlendAssociation *mA{}; + float mB{}; + RpAnimBlendClumpForEachAssociation(clump, [&](CAnimBlendAssociation* a) { + if (!a->IsPartial()) { + return; + } + if (a->GetBlendAmount() > mB) { + mA = a; + mB = a->GetBlendAmount(); + } + }); + return mA; } // 0x4D69F0 CAnimBlendAssociation* RpAnimBlendClumpGetMainPartialAssociation_N(RpClump* clump, int32 n) { - return plugin::CallAndReturn(clump, n); + return RpAnimBlendClumpFindAssociationIf_N(clump, [](const CAnimBlendAssociation& a){ + return a.IsPartial(); + }, n); +} + +// notsa +template +uint32 RpAnimBlendClumpCountAssociationsIf(RpClump* clump, Fn&& Pred) { + return rng::count_if(RpAnimBlendClumpGetAssociations(clump), Pred); } // 0x4D6B60 uint32 RpAnimBlendClumpGetNumAssociations(RpClump* clump) { - return plugin::CallAndReturn(clump); + return RpAnimBlendClumpCountAssociationsIf(clump, [](const CAnimBlendAssociation& a){ + return true; // Count all + }); } // 0x4D6BB0 uint32 RpAnimBlendClumpGetNumNonPartialAssociations(RpClump* clump) { - return plugin::CallAndReturn(clump); + return RpAnimBlendClumpCountAssociationsIf(clump, [](const CAnimBlendAssociation& a){ + return !a.IsPartial(); + }); } // 0x4D6B80 uint32 RpAnimBlendClumpGetNumPartialAssociations(RpClump* clump) { - return plugin::CallAndReturn(clump); -} - -// 0x4D6C30 -void RpAnimBlendClumpGiveAssociations(RpClump* clump, CAnimBlendAssociation* association) { - plugin::Call<0x4D6C30, RpClump*, CAnimBlendAssociation*>(clump, association); -} - -// 0x4D6720 -void RpAnimBlendClumpInit(RpClump* clump) { - plugin::Call<0x4D6720, RpClump*>(clump); + return RpAnimBlendClumpCountAssociationsIf(clump, [](const CAnimBlendAssociation& a){ + return a.IsPartial(); + }); } // 0x4D6760 bool RpAnimBlendClumpIsInitialized(RpClump* clump) { - return plugin::CallAndReturn(clump); + const auto bd = RpAnimBlendClumpGetData(clump); + + return bd && bd->m_NumFrameData; } // 0x4D6B00 void RpAnimBlendClumpPauseAllAnimations(RpClump* clump) { - plugin::Call<0x4D6B00, RpClump*>(clump); + for (auto& a : RpAnimBlendClumpGetAssociations(clump)) { + a.SetFlag(ANIMATION_IS_PLAYING, false); + } } +// 0x4D6B30 +void RpAnimBlendClumpUnPauseAllAnimations(RpClump* clump) { + for (auto& a : RpAnimBlendClumpGetAssociations(clump)) { + a.SetFlag(ANIMATION_IS_PLAYING, true); + } +} + + // 0x4D6C00 void RpAnimBlendClumpRemoveAllAssociations(RpClump* clump) { - plugin::Call<0x4D6C00, RpClump*>(clump); + RpAnimBlendClumpForEachAssociation(clump, [](CAnimBlendAssociation* a) { // Musn't use a for loop here, because it doesn't pre-cache `next` + delete a; + }); } // 0x4D6820 void RpAnimBlendClumpRemoveAssociations(RpClump* clump, uint32 flags) { - plugin::Call<0x4D6820, RpClump*, uint32>(clump, flags); + RpAnimBlendClumpForEachAssociation(clump, [=](CAnimBlendAssociation* a) { // Musn't use a for loop here, because it doesn't pre-cache `next` + if (!flags || (a->m_Flags & flags)) { + delete a; + } + }); } // 0x4D67E0 void RpAnimBlendClumpSetBlendDeltas(RpClump* clump, uint32 flags, float delta) { - plugin::Call<0x4D67E0, RpClump*, uint32, float>(clump, flags, delta); + RpAnimBlendClumpForEachAssociation(clump, [=](CAnimBlendAssociation* a) { + if (!flags || (a->m_Flags & flags)) { + a->SetBlendDelta(delta); + } + }); } -// 0x4D6B30 -void RpAnimBlendClumpUnPauseAllAnimations(RpClump* clump) { - plugin::Call<0x4D6B30, RpClump*>(clump); +struct AnimBlendUpdateData { // OG name + bool32 IncludePartial{}; //!< Has non-moving anim (Eg.: Anim with MOVEMENT flag NOT set) + CAnimBlendNode* BlendNodeArrays[12]{}; //!< Null terminated array of arrays. + //!< Each entry is incremented on each call of the appropriate `FrameUpdateCallBack` + //!< So that on every call they point to the blend node of the given frame + //!< (This works because the sequences are sorted the same way as the frames apprear in the clump) +}; + +static auto& gpAnimBlendClump = StaticRef(0xB4EA0C); + +float CalculateTotalBlendOfPartial(AnimBlendUpdateData* c, AnimBlendFrameData* fd, bool checkDontAddToBlend) { + float sum{}; + if (c->IncludePartial) { + for (auto it = c->BlendNodeArrays; *it; it++) { + const auto node = *it; + if (!node->IsValid()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + if (assoc->HasFlag(ANIMATION_IS_PARTIAL) && (!checkDontAddToBlend || !assoc->HasFlag(ANIMATION_DONT_ADD_TO_PARTIAL_BLEND))) { + sum += assoc->GetBlendAmount(); + } + } + } + return sum; +} + +#define USE_COPY_PASTE_FRAME_UPDATE 0 + +#pragma region "R* Frame Update Functions" +// 0x4D1DB0 +void FrameUpdateCallBackWithVelocityExtractionCompressedSkinned(AnimBlendUpdateData* c, AnimBlendFrameData* fd) { + const auto partialScale = 1.f - CalculateTotalBlendOfPartial(c, fd, false); + + CVector currV{}; // Velocity + for (auto it = c->BlendNodeArrays; *it; it++) { + const auto node = *it; + + if (!node->IsValid() || !node->GetRootKF()->HasTranslation()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + if (!assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + CVector t; + node->GetCurrentTranslationCompressed(t, partialScale); + currV += t; + } + + CQuaternion nextQ{}; + CVector nextT{}; // Translation + CVector deltaV{}, loopedDeltaV{}; // 3D Velocity + bool hasLoopedVelocity{}; + for (auto it = c->BlendNodeArrays; *it; ++*it, it++) { // NOTE: Increments NodeArray pointer too! + const auto node = *it; + + if (!node->IsValid()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + + const auto blendScale = partialScale; + CVector t; + CQuaternion q; + const auto looped = node->UpdateCompressed(t, q, blendScale); + + nextQ = nextQ + q; + + if (!node->GetRootKF()->HasTranslation()) { + continue; + } + + nextT += t; + + if (!assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + deltaV += t; + + if (hasLoopedVelocity |= looped) { + CVector t2; + node->GetEndTranslationCompressed(t2, blendScale); + loopedDeltaV += t; + } + } + + if (!fd->KeyFramesIgnoreNodeOrientation) { + nextQ.Normalise(); + fd->KeyFrame->q = nextQ; + } + + if (!fd->KeyFramesIgnoreNodeTranslation) { + // Update world positions (This moves the ped around the world) + const auto wsPos = gpAnimBlendClump->m_PedPosition; + *wsPos += deltaV - currV; + if (hasLoopedVelocity) { + *wsPos += loopedDeltaV; + } + + // Update key-frame translation + fd->KeyFrame->t = nextT - deltaV + fd->BonePos; + } +} + +// 0x4D2E40 +// FrameUpdateCallBackT +void FrameUpdateCallBackCompressedSkinned(AnimBlendFrameData* fd, void* data) { + const auto c = static_cast(data); + + if (fd->HasVelocity && gpAnimBlendClump->m_PedPosition) { + FrameUpdateCallBackWithVelocityExtractionCompressedSkinned(c, fd); + return; + } + + const auto partialScale = 1.f - CalculateTotalBlendOfPartial(c, fd, false); + + // Calculate new key-frame translation, rotation and blend values + CVector nextT{}; + CQuaternion nextQ{}; + float nextBlendT{}; + for (auto it = c->BlendNodeArrays; *it; ++*it, it++) { // NOTE: Increments NodeArray pointer too! + const auto node = *it; + + if (!node->IsValid()) { + continue; + } + + CVector t; + CQuaternion q; + node->Update(t, q, partialScale); + + // Sum translation + if (node->GetRootKF()->HasTranslation()) { + nextT += t; + nextBlendT += node->GetAnimAssoc()->GetBlendAmount(); + } + + // Sum rotation + nextQ = nextQ + (q.Dot(nextQ) >= 0.f ? q : -q); + } + + // Apply rotation to kf + if (!fd->KeyFramesIgnoreNodeOrientation) { + nextQ.Normalise(); + fd->KeyFrame->q = nextQ; + } + + // Apply translation to kf + if (!fd->KeyFramesIgnoreNodeTranslation) { + fd->KeyFrame->t = lerp(fd->BonePos, nextT, nextBlendT); + } +} + +// 0x4D27F0 +// FrameUpdateCallBackT +void FrameUpdateCallBackWithVelocityExtractionCompressedNonSkinned(AnimBlendUpdateData* c, AnimBlendFrameData* fd) { + const auto partialScale = 1.f - CalculateTotalBlendOfPartial(c, fd, false); + + CVector currV{}; // Velocity + for (auto it = c->BlendNodeArrays; *it; it++) { + const auto node = *it; + + if (!node->IsValid() || !node->GetRootKF()->HasTranslation()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + if (!assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + CVector t; + node->GetCurrentTranslationCompressed(t, partialScale); + currV += t; + } + + CQuaternion nextQ{}; + CVector nextT{}; // Translation + CVector deltaV{}, loopedDeltaV{}; // 3D Velocity + bool hasLoopedVelocity{}; + for (auto it = c->BlendNodeArrays; *it; ++*it, it++) { // NOTE: Increments NodeArray pointer too! + const auto node = *it; + + if (!node->IsValid()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + + const auto blendScale = partialScale; + CVector t; + CQuaternion q; + const auto looped = node->UpdateCompressed(t, q, blendScale); + + nextQ = nextQ + q; + + if (!node->GetRootKF()->HasTranslation()) { + continue; + } + + nextT += t; + + if (!assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + deltaV += t; + + if (hasLoopedVelocity |= looped) { + CVector t2; + node->GetEndTranslationCompressed(t2, blendScale); + loopedDeltaV += t; + } + } + + const auto fmat = RwFrameGetMatrix(fd->Frame); + + if (!fd->KeyFramesIgnoreNodeOrientation) { + RwMatrixSetIdentity(fmat); + nextQ.Normalise(); + nextQ.Get(fmat); + } + + if (!fd->KeyFramesIgnoreNodeTranslation) { + // Update world positions (This moves the ped around the world) + const auto wsPos = gpAnimBlendClump->m_PedPosition; + *wsPos += deltaV - currV; + if (hasLoopedVelocity) { + *wsPos += loopedDeltaV; + } + + // Update key-frame translation + CVector t = nextT - deltaV + fd->BonePos; + RwV3dAssign(RwMatrixGetPos(fmat), &t); + } + + RwMatrixUpdate(fmat); +} + +// 0x4D32D0 +// FrameUpdateCallBackT +void FrameUpdateCallBackCompressedNonSkinned(AnimBlendFrameData* fd, void* data) { + const auto c = static_cast(data); + + if (fd->HasVelocity && gpAnimBlendClump->m_PedPosition) { + FrameUpdateCallBackWithVelocityExtractionCompressedNonSkinned(c, fd); + return; + } + + // 0x4D2C23 + const auto partialScale = 1.f - CalculateTotalBlendOfPartial(c, fd, false); + + // 0x4D2D69 - Calculate new key-frame translation, rotation and blend values + CVector nextT{}; + CQuaternion nextQ{}; + float nextBlendT{}; + for (auto it = c->BlendNodeArrays; *it; ++*it, it++) { // NOTE: Increments NodeArray pointer too! + const auto node = *it; + + if (!node->IsValid()) { + continue; + } + + CVector t; + CQuaternion q; + node->UpdateCompressed(t, q, partialScale); + + // Sum translation + if (node->GetRootKF()->HasTranslation()) { + nextT += t; + nextBlendT += node->GetAnimAssoc()->GetBlendAmount(); + } + + // Sum rotation + nextQ = nextQ + q; + } + const auto fmat = RwFrameGetMatrix(fd->Frame); + + // Apply rotation to frame + if (!fd->KeyFramesIgnoreNodeOrientation) { + RwMatrixSetIdentity(fmat); + nextQ.Normalise(); + nextQ.Get(fmat); + } + + // Apply translation to frame + if (!fd->KeyFramesIgnoreNodeTranslation) { + CVector t = lerp(fd->FramePos, nextT, nextBlendT); + RwV3dAssign(RwMatrixGetPos(fmat), &t); + } + + RwMatrixUpdate(fmat); +} + +// 0x4D1A50 +// FrameUpdateCallBackT +void FrameUpdateCallBackSkinnedWith3dVelocityExtraction(AnimBlendUpdateData* c, AnimBlendFrameData* fd) { // `c` is passed in `eax`, can't hook + const auto partialScale = 1.f - CalculateTotalBlendOfPartial(c, fd, true); + + // 0x4D1B32 + CVector currV{}; // Velocity + for (auto it = c->BlendNodeArrays; *it; it++) { + const auto node = *it; + + if (!node->IsValid() || !node->GetRootKF()->HasTranslation()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + if (!assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + CVector t; + node->GetCurrentTranslation(t, partialScale); + currV += t; + } + + // 0x4D1B97 + CQuaternion nextQ{}; + CVector nextT{}; // Translation + CVector deltaV{}, loopedDeltaV{}; // 3D Velocity + bool hasLoopedVelocity{}; + for (auto it = c->BlendNodeArrays; *it; ++*it, it++) { // NOTE: Increments NodeArray pointer too! + const auto node = *it; + + if (!node->IsValid()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + + const auto blendScale = partialScale; + CVector t; + CQuaternion q; + const auto looped = node->Update(t, q, blendScale); + + nextQ = nextQ + q; + + if (!node->GetRootKF()->HasTranslation()) { + continue; + } + + nextT += t; + + if (!assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + deltaV += t; + + if (hasLoopedVelocity |= looped) { + CVector t2; + node->GetEndTranslation(t2, blendScale); + loopedDeltaV += t; + } + } + + // 0x4D1CB2 + if (!fd->KeyFramesIgnoreNodeOrientation) { + nextQ.Normalise(); + fd->KeyFrame->q = nextQ; + } + + // 0x4D1CD7 + if (!fd->KeyFramesIgnoreNodeTranslation) { + // Update world positions (This moves the ped around the world) + const auto wsPos = gpAnimBlendClump->m_PedPosition; + *wsPos += deltaV - currV; + if (hasLoopedVelocity) { + *wsPos += loopedDeltaV; + } + + // Update key-frame translation + fd->KeyFrame->t = nextT - deltaV + fd->BonePos; + } +} + +// 0x4D1680 +// FrameUpdateCallBackT +void FrameUpdateCallBackSkinnedWithVelocityExtraction(AnimBlendUpdateData* c, AnimBlendFrameData* fd) { // `c` is passed in `eax`, can't hook + const auto partialScale = 1.f - CalculateTotalBlendOfPartial(c, fd, true); + + // 0x4D174C + CVector2D currV{}; // Velocity + for (auto it = c->BlendNodeArrays; *it; it++) { + const auto node = *it; + + if (!node->IsValid() || !node->GetRootKF()->HasTranslation()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + if (assoc->HasFlag(ANIMATION_IGNORE_ROOT_TRANSLATION) || !assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + CVector t; + node->GetCurrentTranslation(t, partialScale); + currV.y += t.y; + if (assoc->HasFlag(ANIMATION_CAN_EXTRACT_X_VELOCITY)) { + currV.x += t.x; + } + } + + // 0x4D17C4 + CQuaternion nextQ{}; // Rotation + CVector nextT{}; // Translation + CVector2D deltaV{}, loopedDeltaV{}; // 2D Velocity + bool hasLoopedVelocity{}; + for (auto it = c->BlendNodeArrays; *it; ++*it, it++) { // NOTE: Increments NodeArray pointer too! + const auto node = *it; + + if (!node->IsValid()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + + CVector t; + CQuaternion q; + const auto looped = node->Update(t, q, partialScale); + + nextQ = q.Dot(nextQ) >= 0.f + ? nextQ + q + : nextQ - q; + + if (!node->GetRootKF()->HasTranslation() || assoc->HasFlag(ANIMATION_IGNORE_ROOT_TRANSLATION)) { + continue; + } + + nextT += t; + + if (!assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + deltaV.y += t.y; + if (assoc->HasFlag(ANIMATION_CAN_EXTRACT_X_VELOCITY)) { + deltaV.x += t.x; + } + + if (hasLoopedVelocity |= looped) { + CVector t2; + node->GetEndTranslation(t2, partialScale); + loopedDeltaV.y += t2.y; + if (assoc->HasFlag(ANIMATION_CAN_EXTRACT_X_VELOCITY)) { + loopedDeltaV.x += t2.x; + } + } + } + + // 0x4D195C + if (!fd->KeyFramesIgnoreNodeOrientation) { + nextQ.Normalise(); + fd->KeyFrame->q = nextQ; + } + + // 0x4D1980 + if (!fd->KeyFramesIgnoreNodeTranslation) { + // Update world positions (This moves the ped around the world) + const auto wsPos = gpAnimBlendClump->m_PedPosition; + wsPos->x = deltaV.x - currV.x; + wsPos->y = deltaV.y - currV.y; + if (hasLoopedVelocity) { + wsPos->x += loopedDeltaV.x; + wsPos->y += loopedDeltaV.y; + } + + // Update key-frame translation + const auto kfT = &fd->KeyFrame->t; + *kfT = nextT - CVector{deltaV}; + kfT->x += fd->BonePos.x; + kfT->y += fd->BonePos.y; + if (kfT->z >= -0.8f) { + kfT->z += kfT->z >= -0.4f + ? fd->BonePos.z + : (kfT->z * 2.5f + 2.f) * fd->BonePos.z; + } + } +} + +// 0x4D2B90 +// FrameUpdateCallBackT +void FrameUpdateCallBackSkinned(AnimBlendFrameData* fd, void* data) { + const auto c = static_cast(data); + + if (fd->HasVelocity && gpAnimBlendClump->m_PedPosition) { + if (fd->HasZVelocity) { + FrameUpdateCallBackSkinnedWith3dVelocityExtraction(c, fd); + } else { + FrameUpdateCallBackSkinnedWithVelocityExtraction(c, fd); + } + return; + } + + // 0x4D2C23 + const auto partialScale = 1.f - CalculateTotalBlendOfPartial(c, fd, false); + + // 0x4D2D69 - Calculate new key-frame translation, rotation and blend values + CVector nextT{}; + CQuaternion nextQ{}; + float nextBlendT{}; + for (auto it = c->BlendNodeArrays; *it; ++*it, it++) { // NOTE: Increments NodeArray pointer too! + const auto node = *it; + + if (!node->IsValid()) { + continue; + } + + CVector t; + CQuaternion q; + node->Update(t, q, partialScale); + + // Sum translation + if (node->GetRootKF()->HasTranslation()) { + nextT += t; + nextBlendT += node->GetAnimAssoc()->GetBlendAmount(); + } + + // Sum rotation + nextQ = nextQ + (q.Dot(nextQ) >= 0.f ? q : -q); + } + + // 0x4D2D73 - Apply rotation to kf + if (!fd->KeyFramesIgnoreNodeOrientation) { + nextQ.Normalise(); + fd->KeyFrame->q = nextQ; + } + + // 0x4D2D9D - Apply translation to kf + if (!fd->KeyFramesIgnoreNodeTranslation) { + fd->KeyFrame->t = lerp(fd->BonePos, nextT, nextBlendT); + } +} + +// 0x4D2450 +// FrameUpdateCallBackT +void FrameUpdateCallBackNonSkinnedWith3dVelocityExtraction(AnimBlendUpdateData* c, AnimBlendFrameData* fd) { + const auto partialScale = 1.f - CalculateTotalBlendOfPartial(c, fd, false); + + CVector currV{}; // Velocity + for (auto it = c->BlendNodeArrays; *it; it++) { + const auto node = *it; + + if (!node->IsValid() || !node->GetRootKF()->HasTranslation()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + if (!assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + CVector t; + node->GetCurrentTranslation(t, partialScale); + currV += t; + } + + CQuaternion nextQ{}; + CVector nextT{}; // Translation + CVector deltaV{}, loopedDeltaV{}; // 3D Velocity + bool hasLoopedVelocity{}; + for (auto it = c->BlendNodeArrays; *it; ++*it, it++) { // NOTE: Increments NodeArray pointer too! + const auto node = *it; + + if (!node->IsValid()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + + const auto blendScale = partialScale; + CVector t; + CQuaternion q; + const auto looped = node->Update(t, q, blendScale); + + nextQ = nextQ + q; + + if (!node->GetRootKF()->HasTranslation()) { + continue; + } + + nextT += t; + + if (!assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + deltaV += t; + + if (hasLoopedVelocity |= looped) { + CVector t2; + node->GetEndTranslation(t2, blendScale); + loopedDeltaV += t; + } + } + + const auto fmat = RwFrameGetMatrix(fd->Frame); + + if (!fd->KeyFramesIgnoreNodeOrientation) { + RwMatrixSetIdentity(fmat); + nextQ.Normalise(); + nextQ.Get(fmat); + } + + if (!fd->KeyFramesIgnoreNodeTranslation) { + // Update world positions (This moves the ped around the world) + const auto wsPos = gpAnimBlendClump->m_PedPosition; + *wsPos += deltaV - currV; + if (hasLoopedVelocity) { + *wsPos += loopedDeltaV; + } + + // Update key-frame translation + CVector t = nextT - deltaV + fd->BonePos; + RwV3dAssign(RwMatrixGetPos(fmat), &t); + } + + RwMatrixUpdate(fmat); +} + +// 0x4D2100 +// FrameUpdateCallBackT +void FrameUpdateCallBackNonSkinnedWithVelocityExtraction(AnimBlendUpdateData* c, AnimBlendFrameData* fd) { + const auto partialScale = 1.f - CalculateTotalBlendOfPartial(c, fd, false); + + CVector2D currV{}; // Velocity + for (auto it = c->BlendNodeArrays; *it; it++) { + const auto node = *it; + + if (!node->IsValid() || !node->GetRootKF()->HasTranslation()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + if (assoc->HasFlag(ANIMATION_IGNORE_ROOT_TRANSLATION) || !assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + CVector t; + node->GetCurrentTranslation(t, partialScale); + currV.y += t.y; + if (assoc->HasFlag(ANIMATION_CAN_EXTRACT_X_VELOCITY)) { + currV.x += t.x; + } + } + + CQuaternion nextQ{}; // Rotation + CVector nextT{}; // Translation + CVector2D deltaV{}, loopedDeltaV{}; // 2D Velocity + bool hasLoopedVelocity{}; + for (auto it = c->BlendNodeArrays; *it; ++*it, it++) { // NOTE: Increments NodeArray pointer too! + const auto node = *it; + + if (!node->IsValid()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + + CVector t; + CQuaternion q; + const auto looped = node->Update(t, q, partialScale); + + nextQ = q.Dot(nextQ) >= 0.f + ? nextQ + q + : nextQ - q; + + if (!node->GetRootKF()->HasTranslation() || assoc->HasFlag(ANIMATION_IGNORE_ROOT_TRANSLATION)) { + continue; + } + + nextT += t; + + if (!assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + deltaV.y += t.y; + if (assoc->HasFlag(ANIMATION_CAN_EXTRACT_X_VELOCITY)) { + deltaV.x += t.x; + } + + if (hasLoopedVelocity |= looped) { + CVector t2; + node->GetEndTranslation(t2, partialScale); + loopedDeltaV.y += t2.y; + if (assoc->HasFlag(ANIMATION_CAN_EXTRACT_X_VELOCITY)) { + loopedDeltaV.x += t2.x; + } + } + } + + const auto fmat = RwFrameGetMatrix(fd->Frame); + + if (!fd->KeyFramesIgnoreNodeOrientation) { + RwMatrixSetIdentity(fmat); + nextQ.Normalise(); + nextQ.Get(fmat); + } + + if (!fd->KeyFramesIgnoreNodeTranslation) { + // Update world positions (This moves the ped around the world) + const auto wsPos = gpAnimBlendClump->m_PedPosition; + wsPos->x = deltaV.x - currV.x; + wsPos->y = deltaV.y - currV.y; + if (hasLoopedVelocity) { + wsPos->x += loopedDeltaV.x; + wsPos->y += loopedDeltaV.y; + } + + // Update key-frame translation + CVector t = nextT - CVector{deltaV}; + t.x += fd->BonePos.x; + t.y += fd->BonePos.y; + RwV3dAssign(RwMatrixGetPos(fmat), &t); + + //if (kfT->z >= -0.8f) { + // kfT->z += kfT->z >= -0.4f + // ? fd->BonePos.z + // : (kfT->z * 2.5f + 2.f) * fd->BonePos.z; + //} + } + + RwMatrixUpdate(fmat); +} + +// 0x4D30A0 +// FrameUpdateCallBackT +void FrameUpdateCallBackNonSkinned(AnimBlendFrameData* fd, void* data) { + const auto c = static_cast(data); + + if (fd->HasVelocity && gpAnimBlendClump->m_PedPosition) { + if (fd->HasZVelocity) { + FrameUpdateCallBackNonSkinnedWith3dVelocityExtraction(c, fd); + } else { + FrameUpdateCallBackNonSkinnedWithVelocityExtraction(c, fd); + } + return; + } + + // 0x4D3131 + const auto partialScale = 1.f - CalculateTotalBlendOfPartial(c, fd, false); + + // 0x4D3170 - Calculate new key-frame translation, rotation and blend values + CVector nextT{}; + CQuaternion nextQ{}; + float nextBlendT{}; + for (auto it = c->BlendNodeArrays; *it; ++*it, it++) { // NOTE: Increments NodeArray pointer too! + const auto node = *it; + + if (!node->IsValid()) { + continue; + } + + CVector t; + CQuaternion q; + node->Update(t, q, partialScale); + + // Sum translation + if (node->GetRootKF()->HasTranslation()) { + nextT += t; + nextBlendT += node->GetAnimAssoc()->GetBlendAmount(); + } + + // Sum rotation + nextQ = nextQ + q; + } + + const auto fmat = RwFrameGetMatrix(fd->Frame); + + // 0x4D322F - Apply rotation to frame + if (!fd->KeyFramesIgnoreNodeOrientation) { + RwMatrixSetIdentity(fmat); + nextQ.Normalise(); + nextQ.Get(fmat); + } + + // 0x4D3278 - Apply translation to frame + if (!fd->KeyFramesIgnoreNodeTranslation) { + CVector t = lerp(fd->FramePos, nextT, nextBlendT); + RwV3dAssign(RwMatrixGetPos(fmat), &t); + } + + // 0x4D32BE + RwMatrixUpdate(fmat); +} + +#pragma endregion + +template +struct NeedsRemoveQuatFlips : std::false_type {}; +template<> +struct NeedsRemoveQuatFlips : std::true_type {}; // FrameUpdateCallBackCompressedSkinned +template<> +struct NeedsRemoveQuatFlips : std::true_type {}; // FrameUpdateCallBackSkinned +template<> +struct NeedsRemoveQuatFlips : std::true_type {}; // FrameUpdateCallBackSkinnedWithVelocityExtraction +template<> +struct NeedsRemoveQuatFlips : std::true_type {}; // FrameUpdateCallBackNonSkinnedWithVelocityExtraction + +/*! + * @brief Copy-paste eliminated per-tick frame update + * @tparam IsCompressed If the anim data is compressed + * @tparam IsSkinned If the frame is skinned or not + * @tparam ExtractVelocity Whenever to use the translation to update the ped's position + * @tparam Extract3DVelocity Whenever to use Z translation too for updating the ped's position + * @param fd Frame Data + * @param c Context +*/ +template +void FrameUpdateCallBackT(AnimBlendFrameData* fd, AnimBlendUpdateData* c) { + const auto partialScale = 1.f - CalculateTotalBlendOfPartial(c, fd, true); + + constexpr bool IsFixBugs = true; + + const auto GetVelocityFromTranslation = [](CAnimBlendNode* node, CVector t) { + return CVector{ + !IsFixBugs && Extract3DVelocity || node->GetAnimAssoc()->HasFlag(ANIMATION_CAN_EXTRACT_X_VELOCITY) // BUGFIX: Check this flag for 3D too (Like they did for 2D) + ? t.x + : 0.f, + t.y, + Extract3DVelocity + ? t.z + : 0.f + }; + }; + + // (if `ExtractVelocity`): Extract current velocity + CVector currV{}; // Current velocity (before update), Z only used if `ExtractZVelocity` + if constexpr (ExtractVelocity) { + for (auto it = c->BlendNodeArrays; *it; it++) { + const auto node = *it; + + if (!node->IsValid() || !node->GetRootKF()->HasTranslation()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + if (!assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + if (IsFixBugs || !Extract3DVelocity) { + if (assoc->HasFlag(ANIMATION_IGNORE_ROOT_TRANSLATION)) { // BUGFIX: Check this flag for 3D too (Like they did for 2D) + continue; + } + } + + CVector t; + node->I_GetCurrentTranslation(t, partialScale); + currV += GetVelocityFromTranslation(node, t); + } + } + + CQuaternion nextQ{}; // Rotation after update + CVector nextT{}; // Translation after update + + // if `ExtractVelocity`: + CVector nextV{}, loopedNextV{}; // Velocity after update, Z only used if `ExtractZVelocity` + bool hasLoopedVelocity{}; + // else: + float nextBlendT{}; // Key-frame blend + + for (auto it = c->BlendNodeArrays; *it; ++*it, it++) { // NOTE: Increments NodeArray pointer too! + const auto node = *it; + + if (!node->IsValid()) { + continue; + } + + const auto assoc = node->GetAnimAssoc(); + + const auto blendScale = partialScale; + CVector t; + CQuaternion q; + const auto looped = node->I_Update(t, q, blendScale); + + // NOTE: Not sure if doing this at all is necessary, but let's keep it the way it was. + if constexpr (NeedsRemoveQuatFlips{}) { + nextQ = nextQ + (q.Dot(nextQ) >= 0.f ? q : -q); + } else { + nextQ = nextQ + q; + } + + if (!node->GetRootKF()->HasTranslation()) { + continue; + } + + if (IsFixBugs || ExtractVelocity && !Extract3DVelocity) { + if (assoc->HasFlag(ANIMATION_IGNORE_ROOT_TRANSLATION)) { // BUGFIX: Check this flag for 3D too (Like they did for 2D) + continue; + } + } + + nextT += t; + if constexpr (!ExtractVelocity) { + nextBlendT += node->GetAnimAssoc()->GetBlendAmount(); + } + + if constexpr (!ExtractVelocity) { + continue; + } + + if (!assoc->HasFlag(ANIMATION_CAN_EXTRACT_VELOCITY)) { + continue; + } + + CVector cT; + node->I_GetCurrentTranslation(cT, partialScale); + nextV += GetVelocityFromTranslation(node, cT); + + if (hasLoopedVelocity |= looped) { + CVector eT; + node->I_GetEndTranslation(eT, blendScale); + loopedNextV += GetVelocityFromTranslation(node, eT); + } + } + + const auto fmat = IsSkinned + ? nullptr + : RwFrameGetMatrix(fd->Frame); + + if (!fd->KeyFramesIgnoreNodeOrientation) { + nextQ.Normalise(); + + if constexpr (IsSkinned) { + fd->KeyFrame->q = nextQ; + } else { + RwMatrixSetIdentity(fmat); + nextQ.Get(fmat); // Set delta rotation as the rotation of the frame + } + } + + if (!fd->KeyFramesIgnoreNodeTranslation) { + // Apply velocity + if constexpr (ExtractVelocity) { + const auto wsPos = gpAnimBlendClump->m_PedPosition; + + wsPos->x = nextV.x - currV.x; + wsPos->y = nextV.y - currV.y; + if constexpr (Extract3DVelocity) { + wsPos->z = nextV.z - currV.z; + } + + if (hasLoopedVelocity) { + wsPos->x += loopedNextV.x; + wsPos->y += loopedNextV.y; + if constexpr (Extract3DVelocity) { + wsPos->z += loopedNextV.z; + } + } + } + + // Apply translation + RwV3d* t = IsSkinned + ? &fd->KeyFrame->t + : RwMatrixGetPos(fmat); + + const CVector nodePos = IsSkinned + ? fd->BonePos + : fd->FramePos; + + if constexpr (ExtractVelocity) { + t->x = nextT.x - nextV.x + nodePos.x; + t->y = nextT.y - nextV.y + nodePos.y; + + if constexpr (Extract3DVelocity) { + t->z = nextT.z - nextV.z + nodePos.z; + } else if constexpr (IsSkinned && !IsCompressed) { // FrameUpdateCallBackSkinnedWithVelocityExtraction + t->z = nextT.z; + if (t->z >= -0.8f) { + t->z += t->z >= -0.4f + ? fd->BonePos.z + : (t->z * 2.5f + 2.f) * fd->BonePos.z; + } + } // otherwise `z` remains untouched + } else { + *t = lerp(nodePos, nextT, nextBlendT); + } + } + + if constexpr (!IsSkinned) { + RwMatrixUpdate(fmat); + } +} + +/*! + * @brief Per-tick frame update wrapper + * @tparam IsCompressed If the anim data is compressed + * @tparam IsSkinned If the frame is skinned or not + * @param fd Frame Data + * @param c Context (Real type: `AnimBlendUpdateData*`) +*/ +template +void FrameUpdateCallBackW(AnimBlendFrameData* fd, void* data) { + const auto c = static_cast(data); + +#if USE_COPY_PASTE_FRAME_UPDATE // Templated function + if constexpr (IsCompressed) { + if constexpr (IsSkinned) { + FrameUpdateCallBackCompressedSkinned(fd, c); + } else { + FrameUpdateCallBackCompressedNonSkinned(fd, c); + } + } else { + if constexpr (IsSkinned) { + FrameUpdateCallBackSkinned(fd, c); + } else { + FrameUpdateCallBackNonSkinned(fd, c); + } + } +#else + if (fd->HasVelocity && gpAnimBlendClump->m_PedPosition) { + if (fd->HasZVelocity) { // NOTE: Originally compressed anims immediately did 3D velocity, so this flag may not be set on compressed anims? + FrameUpdateCallBackT(fd, c); + } else { + FrameUpdateCallBackT(fd, c); + } + } else { + FrameUpdateCallBackT(fd, c); + } +#endif +} + +// 0x4D2E10 +void FrameUpdateCallBackOffscreen(AnimBlendFrameData* fd, void* data) { + const auto c = static_cast(data); + + if (fd->HasVelocity && gpAnimBlendClump->m_PedPosition) { +#if USE_COPY_PASTE_FRAME_UPDATE + FrameUpdateCallBackSkinnedWithVelocityExtraction(c, fd); +#else + FrameUpdateCallBackT(fd, c); +#endif + } +} + +// 0x4D1570 +void RpAnimBlendNodeUpdateKeyFrames(AnimBlendUpdateData* c, AnimBlendFrameData* frames, uint32 nFrames) { + for (auto it = c->BlendNodeArrays; *it; it++) { + const auto nodeArray = *it; // #nodes == nFrames + + const auto assoc = nodeArray[0].GetAnimAssoc(); + for (size_t frameN = 0; frameN < nFrames; frameN++) { + const auto fd = &frames[frameN]; + + if (fd->HasVelocity && gpAnimBlendClump->m_PedPosition) { + continue; + } + + const auto node = &nodeArray[frameN]; + if (!node->IsValid()) { + continue; + } + + node->FindKeyFrame(assoc->GetCurrentTime()); + } + } } // 0x4D34F0 -void RpAnimBlendClumpUpdateAnimations(RpClump* clump, float step, bool onScreen) { - plugin::Call<0x4D34F0, RpClump*, float, bool>(clump, step, onScreen); +void RpAnimBlendClumpUpdateAnimations(RpClump* clump, float timeStep, bool isOnScreen) { + const auto bd = RpAnimBlendClumpGetData(clump); + + if (bd->m_AnimList.IsEmpty()) { + return; + } + + gpAnimBlendClump = bd; + + AnimBlendUpdateData ctx{}; + size_t nodesCnt{}; + + float totalTime{}, totalBlendAmnt{}; + + // 0x4D351F + RpAnimBlendClumpForEachAssociation(clump, [&](CAnimBlendAssociation* a) { + if (!a->UpdateBlend(timeStep)) { + return; + } + const auto ah = a->GetHier(); + if (ah->GetSequences().empty()) { + return; + } + CAnimManager::UncompressAnimation(a->GetHier()); + if (nodesCnt + 1 <= std::size(ctx.BlendNodeArrays) - 1) { // - 1 for null terminator + ctx.BlendNodeArrays[nodesCnt++] = a->GetNodesPtr(); + } + if (a->IsSyncronised()) { + totalTime += ah->GetTotalTime() / a->GetSpeed() * a->GetBlendAmount(); + totalBlendAmnt += a->GetBlendAmount(); + } else { + ctx.IncludePartial = true; + } + }); + ctx.BlendNodeArrays[nodesCnt] = nullptr; // Null terminator + + // 0x4D35A2 - Update animation's timesteps + const auto animTimeMult = totalTime == 0.f + ? 1.f + : 1.f / totalTime * totalBlendAmnt; + RpAnimBlendClumpForEachAssociation(clump, [&](CAnimBlendAssociation* a) { + a->UpdateTimeStep(timeStep, animTimeMult); + }); + + // 0x4D360E - Update all animations's frames + const auto rootFD = &bd->GetRootFrameData(); + rootFD->IsUpdatingFrame = true; + + if (rootFD->IsCompressed) { + bd->ForAllFrames( + IsClumpSkinned(clump) + ? &FrameUpdateCallBackW // FrameUpdateCallBackCompressedSkinned + : &FrameUpdateCallBackW, // FrameUpdateCallBackCompressedNonSkinned + &ctx + ); + } else if (isOnScreen) { + if (rootFD->NeedsKeyFrameUpdate) { + RpAnimBlendNodeUpdateKeyFrames(&ctx, bd->m_FrameDatas, bd->m_NumFrameData); + } + + bd->ForAllFrames( + IsClumpSkinned(clump) + ? FrameUpdateCallBackW // FrameUpdateCallBackSkinned + : FrameUpdateCallBackW, // FrameUpdateCallBackNonSkinned + &ctx + ); + + rootFD->NeedsKeyFrameUpdate = false; + } else { + bd->ForAllFrames(FrameUpdateCallBackOffscreen, &ctx); + rootFD->NeedsKeyFrameUpdate = true; + } + + // 0x4D3715 - Update all animation's times + RpAnimBlendClumpForEachAssociation(clump, [&](CAnimBlendAssociation* a) { + a->UpdateTime(timeStep, animTimeMult); + }); + + // 0x4D3764 + RwFrameUpdateObjects(RpClumpGetFrame(clump)); } // 0x4D60E0 RtAnimAnimation* RpAnimBlendCreateAnimationForHierarchy(RpHAnimHierarchy* hierarchy) { - return plugin::CallAndReturn(hierarchy); + if (!hierarchy) { + return nullptr; + } + const auto rtA = RtAnimAnimationCreate( + rwID_RPANIMBLENDPLUGIN, + 0, + 0, + 0.f + ); + if (rtA) { + rtA->numFrames = 2 * hierarchy->numNodes; + } + return rtA; } // 0x4D5EF0 -char* RpAnimBlendFrameGetName(RwFrame* frame) { - return plugin::CallAndReturn(frame); +const char* RpAnimBlendFrameGetName(RwFrame* frame) { + return GetFrameNodeName(frame); } // 0x4D5F00 -void RpAnimBlendFrameSetName(RwFrame* frame, char* name) { - plugin::Call<0x4D5F00, RwFrame*, char*>(frame, name); +void RpAnimBlendFrameSetName(RwFrame* frame, const char* name) { + SetFrameNodeName(frame, name); } // 0x4D6AB0 CAnimBlendAssociation* RpAnimBlendGetNextAssociation(CAnimBlendAssociation* association) { - return plugin::CallAndReturn(association); + const auto next = association->GetLink().next; + return next + ? CAnimBlendAssociation::FromLink(next) + : nullptr; } // 0x4D6AD0 CAnimBlendAssociation* RpAnimBlendGetNextAssociation(CAnimBlendAssociation* association, uint32 flags) { - return plugin::CallAndReturn(association, flags); + assert(flags); + + for (auto l = association->GetLink().next; l;) { + const auto a = CAnimBlendAssociation::FromLink(l); + l = a->GetLink().next; + if (a->m_Flags & flags) { + return a; + } + } + return nullptr; } -// 0x4D60C0 -void RpAnimBlendKeyFrameInterpolate(void* voidOut, void* voidIn1, void* voidIn2, float time, void* customData) { - plugin::Call<0x4D60C0, void*, void*, void*, float, void*>(voidOut, voidIn1, voidIn2, time, customData); +void RpAnimBlendPlugin::InjectHooks() { + RH_ScopedNamespaceName("RpAnimBlend"); + RH_ScopedCategory("Plugins"); + + RH_ScopedGlobalInstall(RpAnimBlendPluginAttach, 0x4D6150); + RH_ScopedGlobalInstall(RpAnimBlendClumpInit, 0x4D6720); + RH_ScopedGlobalInstall(ClumpAnimConstruct, 0x4D5F40); + RH_ScopedGlobalInstall(ClumpAnimDestruct, 0x4D6110); + RH_ScopedGlobalInstall(ClumpAnimCopy, 0x4D5F90); + RH_ScopedGlobalInstall(RpAnimBlendAllocateData, 0x4D5F50); + RH_ScopedGlobalInstall(RtAnimBlendKeyFrameApply, 0x4D5FA0); + RH_ScopedGlobalInstall(RpAnimBlendKeyFrameInterpolate, 0x4D60C0); + RH_ScopedGlobalInstall(RpAnimBlendClumpAddAssociation, 0x4D6790); + RH_ScopedGlobalInstall(RpAnimBlendClumpExtractAssociations, 0x4D6BE0); + RH_ScopedGlobalInstall(RpAnimBlendClumpGiveAssociations, 0x4D6C30); + RH_ScopedGlobalInstall(RpAnimBlendClumpFillFrameArray, 0x4D64A0); + RH_ScopedGlobalInstall(RpAnimBlendClumpFindBone, 0x4D6400); + RH_ScopedGlobalInstall(RpAnimBlendClumpFindFrame, 0x4D62A0); + RH_ScopedGlobalInstall(RpAnimBlendClumpFindFrameFromHashKey, 0x4D6370); + RH_ScopedGlobalOverloadedInstall(RpAnimBlendClumpGetAssociation, "Hier", 0x4D68E0, CAnimBlendAssociation*(*)(RpClump*, bool, CAnimBlendHierarchy*)); + RH_ScopedGlobalOverloadedInstall(RpAnimBlendClumpGetAssociation, "AnimName", 0x4D6870, CAnimBlendAssociation*(*)(RpClump*, const char*)); + RH_ScopedGlobalOverloadedInstall(RpAnimBlendClumpGetAssociation, "AnimId", 0x4D68B0, CAnimBlendAssociation*(*)(RpClump*, uint32)); + RH_ScopedGlobalOverloadedInstall(RpAnimBlendClumpGetFirstAssociation, "", 0x4D15E0, CAnimBlendAssociation*(*)(RpClump*)); + RH_ScopedGlobalOverloadedInstall(RpAnimBlendClumpGetFirstAssociation, "Flags", 0x4D6A70, CAnimBlendAssociation*(*)(RpClump*, uint32)); + RH_ScopedGlobalInstall(RpAnimBlendClumpGetMainAssociation, 0x4D6910); + RH_ScopedGlobalInstall(RpAnimBlendClumpGetMainAssociation_N, 0x4D6A30); + RH_ScopedGlobalInstall(RpAnimBlendClumpGetMainPartialAssociation, 0x4D69A0); + RH_ScopedGlobalInstall(RpAnimBlendClumpGetMainPartialAssociation_N, 0x4D69F0); + RH_ScopedGlobalInstall(RpAnimBlendClumpGetNumAssociations, 0x4D6B60); + RH_ScopedGlobalInstall(RpAnimBlendClumpGetNumNonPartialAssociations, 0x4D6BB0); + RH_ScopedGlobalInstall(RpAnimBlendClumpGetNumPartialAssociations, 0x4D6B80); + RH_ScopedGlobalInstall(RpAnimBlendClumpIsInitialized, 0x4D6760); + RH_ScopedGlobalInstall(RpAnimBlendClumpPauseAllAnimations, 0x4D6B00); + RH_ScopedGlobalInstall(RpAnimBlendClumpUnPauseAllAnimations, 0x4D6B30); + RH_ScopedGlobalInstall(RpAnimBlendClumpRemoveAllAssociations, 0x4D6C00); + RH_ScopedGlobalInstall(RpAnimBlendClumpRemoveAssociations, 0x4D6820); + RH_ScopedGlobalInstall(RpAnimBlendClumpSetBlendDeltas, 0x4D67E0); + RH_ScopedGlobalInstall(RpAnimBlendCreateAnimationForHierarchy, 0x4D60E0); + RH_ScopedGlobalInstall(RpAnimBlendFrameGetName, 0x4D5EF0); + RH_ScopedGlobalInstall(RpAnimBlendFrameSetName, 0x4D5F00); + RH_ScopedGlobalOverloadedInstall(RpAnimBlendGetNextAssociation, "Any", 0x4D6AB0, CAnimBlendAssociation * (*)(CAnimBlendAssociation * association)); + RH_ScopedGlobalOverloadedInstall(RpAnimBlendGetNextAssociation, "Flags", 0x4D6AD0, CAnimBlendAssociation * (*)(CAnimBlendAssociation * association, uint32 flags)); + + //RH_ScopedGlobalInstall(FrameUpdateCallBackCompressedSkinned, 0x4D2E40, {.reversed=false}); + //RH_ScopedGlobalInstall(FrameUpdateCallBackCompressedNonSkinned, 0x4D32D0, {.reversed=false}); + //RH_ScopedGlobalInstall(FrameUpdateCallBackSkinned, 0x4D2B90); + //RH_ScopedGlobalInstall(FrameUpdateCallBackNonSkinned, 0x4D30A0, {.reversed=false}); + //RH_ScopedGlobalInstall(FrameUpdateCallBackOffscreen, 0x4D2E10); + RH_ScopedGlobalInstall(RpAnimBlendClumpUpdateAnimations, 0x4D34F0); } diff --git a/source/game_sa/Plugins/RpAnimBlendPlugin/RpAnimBlend.h b/source/game_sa/Plugins/RpAnimBlendPlugin/RpAnimBlend.h index ae9a9cdff0..6ce64ee0de 100644 --- a/source/game_sa/Plugins/RpAnimBlendPlugin/RpAnimBlend.h +++ b/source/game_sa/Plugins/RpAnimBlendPlugin/RpAnimBlend.h @@ -1,62 +1,319 @@ #pragma once -static inline uint32& ClumpOffset = *(uint32*)0xB5F878; +class CAnimBlendClumpData; +class AnimBlendFrameData; +class CAnimBlendAssociation; +struct RtAnimAnimation; -#define RpClumpGetAnimBlendClumpData(clump) (*(CAnimBlendClumpData **)(((uint32)(clump) + ClumpOffset))) +/*! +* @brief RpAnimBlend plugin unique rwID +*/ +#define rwID_RPANIMBLENDPLUGIN MAKECHUNKID(rwVENDORID_DEVELOPER, 0xFB) +/*! + * @brief Get RpAnimBlend data associated with this clump + * @param clump + * @return +*/ +CAnimBlendClumpData*& RpAnimBlendClumpGetData(RpClump* clump); + +namespace RpAnimBlendPlugin { + void InjectHooks(); +}; + +/*! + * @brief Attach this plugin to RW + * @return If the plugin was successfully attached +*/ bool RpAnimBlendPluginAttach(); -CAnimBlendClumpData* RpAnimBlendAllocateData(RpClump* clump); +/*! + * @unused + */ CAnimBlendAssociation* RpAnimBlendClumpAddAssociation(RpClump* clump, CAnimBlendAssociation* association, uint32 flags, float startTime, float blendAmount); + +/*! + * @notsa + * @brief Get the head link for all animations associated with this clump + * @param clump The clump + * @return The linked list head for all animations +*/ +CAnimBlendLink& RpAnimBlendClumpGetAssociations(RpClump* clump); + +/*! + * @notsa + * @brief Iterate over all associations of a clump + * @note The IterFn shouldn't modify the clump's association list + * @param clump The clump containing the associations + * @param IterFn The function to be called for iterator +*/ +template +void RpAnimBlendClumpForEachAssociation(RpClump* clump, Fn&& IterFn) { + const auto bd = RpAnimBlendClumpGetData(clump); + + for (auto l = bd->m_AnimList.next; l;) { + const auto a = CAnimBlendAssociation::FromLink(l); + l = a->GetLink().next; + std::invoke(IterFn, a); + } +} + +/*! + * @addr 0x4D6BE0 + * @brief Take ownership of all associations of the clump + * @brief It leaves this clump with no associations + * @brief The opposite of this function is `RpAnimBlendClumpGiveAssociations` + * @param clump The clump containing the associations + * @return The first animation of the clump that has a link to all other animations +*/ CAnimBlendAssociation* RpAnimBlendClumpExtractAssociations(RpClump* clump); -void RpAnimBlendClumpFillFrameArray(RpClump* clump, AnimBlendFrameData** frameData); -AnimBlendFrameData* RpAnimBlendClumpFindBone(RpClump* clump, uint32 id); +/*! +* @addr 0x4D6BE0 +* @brief Give ownership of a list of associations to this clump +* @brief The opposite of this function is `RpAnimBlendClumpExtractAssociations` +* @param clump The clump to give the ownership of associations to +*/ +void RpAnimBlendClumpGiveAssociations(RpClump* clump, CAnimBlendAssociation* associations); + +/*! + * @addr 0x4D64A0 + * @brief Fill frame array + * @param clump The clump with the frames + * @param outFrameData Array to be filled +*/ +void RpAnimBlendClumpFillFrameArray(RpClump* clump, AnimBlendFrameData** outFrameData); + +/*! + * @addr 0x4D6400 + * @brief Find FrameData of a bone + * @param clump The clump the bone is part of + * @param boneTag The BoneTag of the bone + * @return FrameData of the specified bone +*/ +AnimBlendFrameData* RpAnimBlendClumpFindBone(RpClump* clump, eBoneTag32 boneTag); + +/*! + * @addr 0x4D62A0 + * @brief Find FrameData of a bone + * @param clump The clump the bone is part of + * @param name The name of the bone. For skinned clumps this is the name of the bone tag, for non-skinned ones it's the frame node name. + * @return FrameData of the specified bone + */ AnimBlendFrameData* RpAnimBlendClumpFindFrame(RpClump* clump, const char* name); -AnimBlendFrameData* RpAnimBlendClumpFindFrameFromHashKey(RpClump* clump, uint32 key); -CAnimBlendAssociation* RpAnimBlendClumpGetAssociation(RpClump* clump, bool bStopFunctionConfusion, CAnimBlendHierarchy* hierarchy); +/*! + * @addr 0x4D6370 + * @brief Find FrameData of a bone + * @param clump The clump the bone is part of + * @param boneNameKey The hash (key) of the bone's name. For skinned clumps this is the name of the bone tag, for non-skinned ones it's the frame node name. + * @return FrameData of the specified bone + */ +AnimBlendFrameData* RpAnimBlendClumpFindFrameFromHashKey(RpClump* clump, uint32 boneNameKey); + +/*! + * @addr 0x4D68E0 + * @brief Find animation of based on a hierarchy + * @param clump The clump containing the associations + * @param hierarchy The hierarchy to search for + * @return The first animation of the given hierarchy, or null +*/ +CAnimBlendAssociation* RpAnimBlendClumpGetAssociation(RpClump* clump, bool, CAnimBlendHierarchy* hierarchy); + +/*! + * @addr 0x4D6870 + * @brief Find animation of the given name + * @param clump The clump containing the associations + * @param name Name of the animation to search for + * @return The first animation with the given name, or null +*/ CAnimBlendAssociation* RpAnimBlendClumpGetAssociation(RpClump* clump, const char* name); + +/*! + * @addr 0x4D6870 + * @brief Find animation of the given id + * @param clump The clump containing the associations + * @param animId ID of the animation to search for + * @return The first animation with the given id, or null +*/ CAnimBlendAssociation* RpAnimBlendClumpGetAssociation(RpClump* clump, uint32 animId); +/*! + * @addr 0x4D15E0 + * @brief Get the first animation associated with the clump + * @param clump The clump containing the associations + * @return The first animation associated with the clump, or null +*/ CAnimBlendAssociation* RpAnimBlendClumpGetFirstAssociation(RpClump* clump); + +/*! + * @addr 0x4D6A70 + * @param clump The clump containing the associations + * @param flags The flags of which at least one is expected to be set + * @return The first anim with any of the flags set, or null if no animations are present on the clump/no animations have the specified flags set. +*/ CAnimBlendAssociation* RpAnimBlendClumpGetFirstAssociation(RpClump* clump, uint32 flags); +/*! + * @addr 0x4D6910 + * @brief Get the 1st (and optionally 2nd) non-partial animations with the highest blend value + * @param clump The clump containing the associations + * @param [opt,out] pp2ndAnim The 2nd animation + * @param [opt,out] pBlendVal2nd The 2nd animation's blend amount + * @return The 1st non-partial animation with the highest blend value, or null +*/ CAnimBlendAssociation* RpAnimBlendClumpGetMainAssociation(RpClump* clump, CAnimBlendAssociation** pp2ndAnim, float* pBlendVal2nd); -CAnimBlendAssociation* RpAnimBlendClumpGetMainAssociation_N(RpClump* clump, int32 n); +/*! + * @addr 0x4D6A30 + * @brief Get the `n`th non-partial animation + * @param clump The clump containing the associations + * @param n The `n` + * @return The `n`th non-partial animation +*/ +CAnimBlendAssociation* RpAnimBlendClumpGetMainAssociation_N(RpClump* clump, uint32 n); + +/*! + * @brief 0x4D69A0 + * @param clump The clump containing the associations + * @return +*/ CAnimBlendAssociation* RpAnimBlendClumpGetMainPartialAssociation(RpClump* clump); + +/*! + * @brief 0x4D69F0 + * @brief Get the `n`th partial animation + * @param clump The clump containing the associations + * @param n The `n` + * @return The `n`th partial animation +*/ CAnimBlendAssociation* RpAnimBlendClumpGetMainPartialAssociation_N(RpClump* clump, int32 n); +/*! + * @brief 0x4D6B60 + * @param clump The clump containing the associations + * @return The number of associations (animations) this clump has +*/ uint32 RpAnimBlendClumpGetNumAssociations(RpClump* clump); + +/*! +* @brief 0x4D6BB0 +* @param clump The clump containing the associations +* @return The number of non-partial associations (animations) this clump has +*/ uint32 RpAnimBlendClumpGetNumNonPartialAssociations(RpClump* clump); + +/*! +* @brief 0x4D6B80 +* @param clump The clump containing the associations +* @return The number of partial associations (animations) this clump has +*/ uint32 RpAnimBlendClumpGetNumPartialAssociations(RpClump* clump); -void RpAnimBlendClumpGiveAssociations(RpClump* clump, CAnimBlendAssociation* association); +/*! + * @addr 0x4D6720 + * @brief Initialize clump data for RpAnimBlend + * @param clump The clump +*/ void RpAnimBlendClumpInit(RpClump* clump); + +/*! + * @addr 0x4D6760 + * @brief Check if the RpAnimBlend data of the clump is initialized + * @param clump The clump to check + * @return Whenever the RpAnimBlend data of the clump is initialized +*/ bool RpAnimBlendClumpIsInitialized(RpClump* clump); + +/*! + * @addr 0x4D6B00 + * @brief Pause all animations of a clump + * @param clump The clump to pause the animations of +*/ void RpAnimBlendClumpPauseAllAnimations(RpClump* clump); -void RpAnimBlendClumpRemoveAllAssociations(RpClump* clump); -void RpAnimBlendClumpRemoveAssociations(RpClump* clump, uint32 flags); -void RpAnimBlendClumpSetBlendDeltas(RpClump* clump, uint32 flags, float delta); + +/*! + * @addr 0x4D6B30 + * @brief Un-pause all animations of a clump + * @param clump The clump to un-pause the animations of + */ void RpAnimBlendClumpUnPauseAllAnimations(RpClump* clump); + +/*! + * @addr 0x4D6C00 + * @brief Remove all animations of a clump + * @param clump The clump to remove all animations of + */ +void RpAnimBlendClumpRemoveAllAssociations(RpClump* clump); + +/*! + * @addr 0x4D6820 + * @brief Remove all animations of a clump + * @param flags Flags to check for. If any of these flags is the animation will be removed. Ignored if `0`. + * @param clump The clump to remove all animations of + */ +void RpAnimBlendClumpRemoveAssociations(RpClump* clump, uint32 flags = 0); + +/*! + * @addr 0x4D67E0 + * @brief Set the blend delta to the specified value for all animations of the clump + * @param clump The clump to which the animations belong to + * @param flags Flags to check for. If any of these flags is set, the anim is processed. Ignored if `0`. + * @param delta The blend delta to set +*/ +void RpAnimBlendClumpSetBlendDeltas(RpClump* clump, uint32 flags, float blendDelta); + +/*! + * @addr 0x4D34F0 + * @brief Update all animations associated with the clump + * @param clump The clump + * @param step Time step + * @param onScreen If the clump is visible on the screen +*/ void RpAnimBlendClumpUpdateAnimations(RpClump* clump, float step, bool onScreen); + +/*! + * @addr 0x4D60E0 + * @brief R* hacking + * @detail In III and VC R* wrote their own interpolated animation data into a dummy + * @detail RtAnimAnimation and practically didn't use the RtAnimInterpolator. + * @detail In SA they went a step further and now write into the interpolation frames of + * @detail the RtAnimInterpolator instead, almost completely avoid RtAnimAnimation. + * @detail Credit: https://gtaforums.com/topic/669045-silentpatch/page/154/#comment-1068971599 + * @param hierarchy The animation hierarchy + * @return The animation +*/ RtAnimAnimation* RpAnimBlendCreateAnimationForHierarchy(RpHAnimHierarchy* hierarchy); -char* RpAnimBlendFrameGetName(RwFrame* frame); -void RpAnimBlendFrameSetName(RwFrame* frame, char* name); -CAnimBlendAssociation* RpAnimBlendGetNextAssociation(CAnimBlendAssociation* association); -CAnimBlendAssociation* RpAnimBlendGetNextAssociation(CAnimBlendAssociation* association, uint32 flags); -void RpAnimBlendKeyFrameInterpolate(void* voidOut, void* voidIn1, void* voidIn2, float time, void* customData); -void* RtAnimBlendKeyFrameApply(void* result, void* frame); /*! -* @notsa -* @brief Iterate over all the anims associated with the clump + * @addr 0x4D5EF0 + * @brief Get the name of a (bone?) frame + * @param frame The frame to get the name of + * @return Name of the frame */ -void RpAnimBlendClumpIterateAssociations(RpClump* clump, auto&& IterFn) { - for (auto a = RpAnimBlendClumpGetFirstAssociation(clump); a; a = RpAnimBlendGetNextAssociation(a)) { - if (!IterFn(a)) { - break; - } - } -} +const char* RpAnimBlendFrameGetName(RwFrame* frame); + +/*! + * @addr 0x4D5F00 + * @brief Set the name of a (bone?) frame + * @param frame The frame to set the name of + * @param name The new name +*/ +void RpAnimBlendFrameSetName(RwFrame* frame, const char* name); + +/*! + * @addr 0x4D6AB0 + * @brief Get the next animation in the linked list + * @param association The animation to get the next animation of + * @return The animation after `association` +*/ +CAnimBlendAssociation* RpAnimBlendGetNextAssociation(CAnimBlendAssociation* association); + +/*! + * @addr 0x4D6AD0 + * @brief Get the next animation in the linked list + * @param association The animation to get the next animation of + * @param flags The flags that need to be set (Any of them being set is enough) + * @return The animation after `association` + */ +CAnimBlendAssociation* RpAnimBlendGetNextAssociation(CAnimBlendAssociation* association, uint32 flags); diff --git a/source/game_sa/RenderWare/rw/rpdbgerr.h b/source/game_sa/RenderWare/rw/rpdbgerr.h new file mode 100644 index 0000000000..b0eda0b026 --- /dev/null +++ b/source/game_sa/RenderWare/rw/rpdbgerr.h @@ -0,0 +1,316 @@ +/*************************************************************************** + * * + * Module : badebug.h * + * * + * Purpose : Debug handling * + * * + **************************************************************************/ + +#ifndef RWDEBUG_H +#define RWDEBUG_H + +#if (defined(RWDEBUG) && defined(RWVERBOSE)) +#if (defined(_MSC_VER)) +#if (_MSC_VER>=1000) + +/* Pick up _ASSERTE macro */ +#ifdef _XBOX +#include +#endif /* _XBOX */ +#if (defined(RWMEMDEBUG) && !defined(_CRTDBG_MAP_ALLOC)) +#define _CRTDBG_MAP_ALLOC +#endif /* defined(RWMEMDEBUG) && !defined(_CRTDBG_MAP_ALLOC)) */ +#include +#undef RWASSERTE +#define RWASSERTE(_condition) _ASSERTE(_condition) +#endif /* (_MSC_VER>=1000) */ +#endif /* (defined(_MSC_VER)) */ +#endif /* (defined(RWDEBUG) && defined(RWVERBOSE)) */ + +#if (!defined(RWASSERTE)) +#define RWASSERTE(_condition) /* No-Op */ +#endif /* (!defined(RWASSERTE)) */ + +#if (!defined(RWPENTER)) +#define RWPENTER(_func) /* No-Op */ +#endif /* (!defined(RWPENTER)) */ + +#if (!defined(RWPEXIT)) +#define RWPEXIT(_func) /* No-Op */ +#endif /* (!defined(RWPEXIT)) */ + +/**************************************************************************** + Includes + */ + +#include + +#include "rpplugin.h" + +/**************************************************************************** + Defines + */ + +#ifdef RWDEBUG + +#if (!(defined(RWDEBUGSTACKDEPTH))) +#define RWDEBUGSTACKDEPTH (RWSRCGLOBAL(debugStackDepth)) +#endif /* (!(defined(RWDEBUGSTACKDEPTH))) */ + +/* Message macros */ + +#ifdef RWTRACE + +/* Note RWTRACE should only be defined for internal builds. It should + * also only be used rarely. It will cause the generation of Trace + * messages for all functions. Not just those directly called from + * the application + */ + +#define RWAPIFUNCTION(function) \ +static const RwChar __dbFunctionName[] = function; \ +const RwInt32 startstackdepth = RWDEBUGSTACKDEPTH++; \ +RWPENTER(__dbFunctionName); \ +if (RWSRCGLOBAL(debugTrace)) \ +{ \ + RwDebugSendMessage(rwDEBUGTRACE, \ + __dbFunctionName, \ + _rwdbsprintf("Enter %s [Depth %d]", \ + (startstackdepth)?"SPI":"API", \ + (int)startstackdepth)); \ +} + +#define RWFUNCTION(function) RWAPIFUNCTION(function) + +#define RWRETURN(result) \ +do \ +{ \ + RwInt32 _validateStackDepth = --RWDEBUGSTACKDEPTH; \ + if (_validateStackDepth != startstackdepth) \ + { \ + RwDebugSendMessage(rwDEBUGERROR, \ + __dbFunctionName, \ + _rwdberrcommon(E_RW_DEBUGSTACK)); \ + RWDEBUGSTACKDEPTH = startstackdepth; \ + } \ + if (RWSRCGLOBAL(debugTrace)) \ + { \ + RwDebugSendMessage(rwDEBUGTRACE, \ + __dbFunctionName, RWSTRING("Exit")); \ + } \ + RWASSERTE(_validateStackDepth == startstackdepth); \ + RWPEXIT(__dbFunctionName); \ + return (result); \ +} \ +while (0) + +#define RWRETURNVOID() \ +do \ +{ \ + RwInt32 _validateStackDepth = --RWDEBUGSTACKDEPTH; \ + if (_validateStackDepth != startstackdepth) \ + { \ + RwDebugSendMessage(rwDEBUGERROR, \ + __dbFunctionName, \ + _rwdberrcommon (E_RW_DEBUGSTACK)); \ + RWDEBUGSTACKDEPTH = startstackdepth; \ + } \ + if (RWSRCGLOBAL(debugTrace)) \ + { \ + RwDebugSendMessage(rwDEBUGTRACE, \ + __dbFunctionName, RWSTRING("Exit")); \ + } \ + RWASSERTE(_validateStackDepth == startstackdepth); \ + RWPEXIT(__dbFunctionName); \ + return; \ +} \ +while(0) + +#else /* RWTRACE */ + +/* Defining RWSTACKDEPTHCHECKING in a non-RWTRACE build will enable stack + depth checking on entry and exit of API and non API functions. It also + allows a trace of the entry and exit from the RenderWare Graphics + library. + This functionality is disabled by default because it is not thread-safe + */ +#ifdef RWSTACKDEPTHCHECKING + +#define RWSTACKDEPTHCHECKONENTRY() \ + const RwInt32 startstackdepth = RWDEBUGSTACKDEPTH++; \ + if (RWSRCGLOBAL(debugTrace) && !startstackdepth) \ + { \ + RwDebugSendMessage(rwDEBUGTRACE, \ + __dbFunctionName, RWSTRING("Enter")); \ + } + +#define RWSTACKDEPTHCHECKONRETURN() \ +MACRO_START \ +{ \ + RwInt32 _validateStackDepth = --RWDEBUGSTACKDEPTH; \ + if (_validateStackDepth != startstackdepth) \ + { \ + RwDebugSendMessage(rwDEBUGERROR, \ + __dbFunctionName, \ + _rwdberrcommon(E_RW_DEBUGSTACK)); \ + RWDEBUGSTACKDEPTH = startstackdepth; \ + } \ + if (RWSRCGLOBAL(debugTrace) && (!startstackdepth)) \ + { \ + RwDebugSendMessage(rwDEBUGTRACE, \ + __dbFunctionName, RWSTRING("Exit")); \ + } \ + RWASSERTE(_validateStackDepth == startstackdepth); \ +} \ +MACRO_STOP + +#else /* RWSTACKDEPTHCHECKING */ + +#define RWSTACKDEPTHCHECKONENTRY() +#define RWSTACKDEPTHCHECKONRETURN() + +#endif /* RWSTACKDEPTHCHECKING */ + + +#define RWAPIFUNCTION(function) \ +static const RwChar __dbFunctionName[] = function; \ +do { } while (__dbFunctionName != __dbFunctionName); \ +RWSTACKDEPTHCHECKONENTRY(); \ +RWPENTER(__dbFunctionName); + +#define RWFUNCTION(function) RWAPIFUNCTION(function) + +#define RWRETURN(result) \ +MACRO_START \ +{ \ + RWSTACKDEPTHCHECKONRETURN(); \ + RWPEXIT(__dbFunctionName); \ + return (result); \ +} \ +MACRO_STOP + +#define RWRETURNVOID() \ +MACRO_START \ +{ \ + RWSTACKDEPTHCHECKONRETURN(); \ + RWPEXIT(__dbFunctionName); \ + return; \ +} \ +MACRO_STOP + +#endif /* RWTRACE */ + +#define RWERROR(ecode) \ +do \ +{ \ + RwError _rwErrorCode; \ + \ + _rwErrorCode.pluginID = rwPLUGIN_ID; \ + _rwErrorCode.errorCode = _rwerror ecode; \ + \ + RwErrorSet(&_rwErrorCode); \ + \ + if (_rwErrorCode.errorCode & 0x80000000) \ + { \ + RwDebugSendMessage(rwDEBUGERROR, \ + __dbFunctionName, \ + _rwdberrcommon ecode); \ + } \ + else \ + { \ + RwDebugSendMessage(rwDEBUGERROR, \ + __dbFunctionName, \ + rwPLUGIN_ERRFUNC ecode); \ + } \ +} \ +while(0); + +//{@ 20050511 DDonSS : ¸Þ¼¼Áö Ãâ·Â ÁöÁ¤ÇÒ¼ö ÀÖµµ·Ï º¯°æ +#define RWMESSAGE(args) \ +do \ +{ \ + if ( RWSRCGLOBAL( debugMessage ) ) \ + { \ + RwDebugSendMessage(rwDEBUGMESSAGE, \ + __dbFunctionName, \ + _rwdbsprintf args); \ + } \ +} \ +while (0) +//}@ DDonSS + +#define RWASSERT(condition) \ +do \ +{ \ + if (!(condition)) \ + { \ + RwDebugSendMessage(rwDEBUGASSERT, \ + __dbFunctionName, \ + RWSTRING(#condition)); \ + } \ + RWASSERTE(condition); \ +} \ +while (0) + +#define RWASSERTM(condition, messageArgs) \ +do \ +{ \ + if (!(condition)) \ + { \ + RwDebugSendMessage(rwDEBUGASSERT, \ + __dbFunctionName, \ + RWSTRING(#condition)); \ + RwDebugSendMessage(rwDEBUGMESSAGE, \ + __dbFunctionName, \ + _rwdbsprintf messageArgs); \ + } \ + RWASSERTE(condition); \ +} \ +while (0) + +#else /* RWDEBUG */ + +#define RWRETURN(value) return(value) +#define RWRETURNVOID() return +#define RWERROR(errorcode) \ +do \ +{ \ + RwError _rwErrorCode; \ + \ + _rwErrorCode.pluginID = rwPLUGIN_ID; \ + _rwErrorCode.errorCode = _rwerror errorcode; \ + \ + RwErrorSet(&_rwErrorCode); \ +} \ +while (0) +#define RWFUNCTION(name) +#define RWAPIFUNCTION(name) +#define RWASSERT(condition) +#define RWASSERTM(condition, messageArgs) +#define RWMESSAGE(args) + +#endif + +#ifdef RWSTACKDEPTHCHECKING +#define RWVALIDATEDEBUGSTACKDEPTH() \ + RWASSERT(1 == (RWDEBUGSTACKDEPTH - startstackdepth)) +#else /* RWSTACKDEPTHCHECKING */ +#define RWVALIDATEDEBUGSTACKDEPTH() +#endif /* RWSTACKDEPTHCHECKING */ + +/**************************************************************************** + Functions + */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +RwChar *rwPLUGIN_ERRFUNC(RwInt32 code, ...); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* RWDEBUG_H */ diff --git a/source/game_sa/RenderWare/rw/rphanim.h b/source/game_sa/RenderWare/rw/rphanim.h index 9964540b34..db02cf1e7b 100644 --- a/source/game_sa/RenderWare/rw/rphanim.h +++ b/source/game_sa/RenderWare/rw/rphanim.h @@ -1,10 +1,3 @@ -/* - Plugin-SDK file - Authors: GTA Community. See more here - https://github.com/DK22Pac/plugin-sdk - Do not delete this comment block. Respect others' work! -*/ -#pragma once /****************************************** * * * RenderWare(TM) Graphics Library * @@ -33,6 +26,8 @@ * Purpose : Hierarchical animation * * * **************************************************************************/ +#ifndef RPHANIM_H +#define RPHANIM_H /** * Hierarchal animation plugin @@ -61,15 +56,16 @@ #include #include -#include "rwcore.h" -#include "rpworld.h" +#include +#include -#include "rpcriter.h" /* Note: each vendor can choose their own method for +#include /* Note: each vendor can choose their own method for * allocation of unique ID's. This file defines * the ID's used by Criterion. */ -#include "rtquat.h" -#include "rtanim.h" +//#include /* automatically generated header file */ +#include +#include #define rpHANIMSTREAMCURRENTVERSION 0x100 @@ -256,34 +252,401 @@ struct RpHAnimFrameExtension RpHAnimHierarchy *hierarchy; /**< Pointer to Animation hierarchy attached to this RwFrame */ }; +/*--- Plugin API Functions ---*/ + +#define RpHAnimHierarchySetFlagsMacro(hierarchy,_flags) \ + (((hierarchy)->flags = _flags), (hierarchy)) + +#define RpHAnimHierarchyGetFlagsMacro(hierarchy) \ + ((hierarchy)->flags) + +#define RpHAnimKeyFrameToMatrixMacro(_matrix,_voidIFrame) \ +MACRO_START \ +{ \ + RpHAnimInterpFrame * iFrame = (RpHAnimInterpFrame *)(_voidIFrame); \ + \ + /* \ + * RpHAnim uses the same types of quaternion as RtQuat \ + * hence no conjugate call as in RpSkin \ + */ \ + \ + RtQuatUnitConvertToMatrix(&iFrame->q,(_matrix)); \ + \ + (_matrix)->pos.x = iFrame->t.x; \ + (_matrix)->pos.y = iFrame->t.y; \ + (_matrix)->pos.z = iFrame->t.z; \ +} \ +MACRO_STOP + + + +#if (! defined(RWDEBUG)) + +#define RpHAnimHierarchySetFlags(hierarchy,_flags) \ + RpHAnimHierarchySetFlagsMacro(hierarchy,_flags) + +#define RpHAnimHierarchyGetFlags(hierarchy) \ + (RpHAnimHierarchyFlag)RpHAnimHierarchyGetFlagsMacro(hierarchy) +#endif /* (! defined(RWDEBUG)) */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +extern RpHAnimAtomicGlobalVars RpHAnimAtomicGlobals; + +#if (defined(RWDEBUG)) + +extern RpHAnimHierarchy * +RpHAnimHierarchySetFlags(RpHAnimHierarchy *hierarchy, + RpHAnimHierarchyFlag flags); + +extern RpHAnimHierarchyFlag +RpHAnimHierarchyGetFlags(RpHAnimHierarchy *hierarchy); + +#endif /* (defined(RWDEBUG)) */ + +/* Animation hierarchy creation */ +extern void +RpHAnimHierarchySetFreeListCreateParams(RwInt32 blockSize,RwInt32 numBlocksToPrealloc); + +extern RpHAnimHierarchy * +RpHAnimHierarchyCreate(RwInt32 numNodes, + RwUInt32 *nodeFlags, + RwInt32 *nodeIDs, + RpHAnimHierarchyFlag flags, + RwInt32 maxInterpKeyFrameSize); + +extern RpHAnimHierarchy * +RpHAnimHierarchyCreateFromHierarchy(RpHAnimHierarchy *hierarchy, + RpHAnimHierarchyFlag flags, + RwInt32 maxInterpKeyFrameSize); + +extern RpHAnimHierarchy * +RpHAnimHierarchyDestroy(RpHAnimHierarchy *hierarchy); + +extern RpHAnimHierarchy * +RpHAnimHierarchyCreateSubHierarchy(RpHAnimHierarchy *parentHierarchy, + RwInt32 startNode, + RpHAnimHierarchyFlag flags, + RwInt32 maxInterpKeyFrameSize); + +extern RtAnimAnimation * +RpHAnimRemoveDuplicates( RtAnimAnimation *animsrc, + RwInt32 numBones, + RwReal tolerance, + RwReal averageNodeSize); + +extern RtAnimAnimation * +RpHAnimAnimationOptimize( RtAnimAnimation *animsrc, + RwUInt32 *pushpops, + RwInt32 numBones, + RwReal tolerance, + RwReal averageNodeSize, + RwBool removeDuplicatesFirst); + +extern RpHAnimHierarchy * +RpHAnimHierarchyAttach(RpHAnimHierarchy *hierarchy); + +extern RpHAnimHierarchy * +RpHAnimHierarchyDetach(RpHAnimHierarchy *hierarchy); + +extern RpHAnimHierarchy * +RpHAnimHierarchyAttachFrameIndex(RpHAnimHierarchy *hierarchy, + RwInt32 nodeIndex); + +extern RpHAnimHierarchy * +RpHAnimHierarchyDetachFrameIndex(RpHAnimHierarchy *hierarchy, + RwInt32 nodeIndex); + +extern RwBool +RpHAnimFrameSetHierarchy(RwFrame *frame, + RpHAnimHierarchy *hierarchy); + +extern RpHAnimHierarchy * +RpHAnimFrameGetHierarchy(RwFrame *frame); + +/* Macros for legacy support of old function names */ +#define RpHAnimSetHierarchy(frame,hierarchy) \ + RpHAnimFrameSetHierarchy(frame,hierarchy) +#define RpHAnimGetHierarchy(frame) RpHAnimFrameGetHierarchy(frame) + +extern RwMatrix * +RpHAnimHierarchyGetMatrixArray(RpHAnimHierarchy *hierarchy); + +extern RwBool +RpHAnimHierarchyUpdateMatrices(RpHAnimHierarchy *hierarchy); + +/* Macro for legacy support of old function name */ +#define RpHAnimUpdateHierarchyMatrices RpHAnimHierarchyUpdateMatrices + +extern RwInt32 +RpHAnimIDGetIndex(RpHAnimHierarchy *hierarchy, + RwInt32 ID); + +/* Plugin support */ + +extern RwBool +RpHAnimPluginAttach(void); + +/* Hanim keyframe functions */ + +extern void +RpHAnimKeyFrameApply(void *matrix, + void *voidIFrame); + +extern void +RpHAnimKeyFrameBlend(void *voidOut, + void *voidIn1, + void *voidIn2, + RwReal alpha); + +extern void +RpHAnimKeyFrameInterpolate(void *voidOut, + void *voidIn1, + void *voidIn2, + RwReal time, + void *customData); + +extern void +RpHAnimKeyFrameAdd(void *voidOut, + void *voidIn1, + void *voidIn2); + +extern void +RpHAnimKeyFrameMulRecip(void *voidFrame, + void *voidStart); + +extern RtAnimAnimation * +RpHAnimKeyFrameStreamRead(RwStream *stream, + RtAnimAnimation *animation); + +extern RwBool +RpHAnimKeyFrameStreamWrite(const RtAnimAnimation *animation, + RwStream *stream); + +extern RwInt32 +RpHAnimKeyFrameStreamGetSize(const RtAnimAnimation *animation); + +/* Access to RwFrame ID's */ + +extern RwBool +RpHAnimFrameSetID(RwFrame *frame, + RwInt32 id); + +extern RwInt32 +RpHAnimFrameGetID(RwFrame *frame); + +/* + * Utility Functions + */ +#define RpHAnimHierarchySetCurrentAnimMacro(hierarchy,anim)\ + RtAnimInterpolatorSetCurrentAnim((hierarchy)->currentAnim,anim) + +#define RpHAnimHierarchyGetCurrentAnimMacro(hierarchy)\ + RtAnimInterpolatorGetCurrentAnim((hierarchy)->currentAnim) + +#define RpHAnimHierarchySetCurrentAnimTimeMacro(hierarchy,time)\ + RtAnimInterpolatorSetCurrentTime((hierarchy)->currentAnim,time) + +#define RpHAnimHierarchyAddAnimTimeMacro(hierarchy,time)\ + RtAnimInterpolatorAddAnimTime((hierarchy)->currentAnim,time) + +#define RpHAnimHierarchySubAnimTimeMacro(hierarchy,time)\ + RtAnimInterpolatorSubAnimTime((hierarchy)->currentAnim,time) + +#define RpHAnimHierarchySetKeyFrameCallBacksMacro(hierarchy,keyFrameTypeID) \ + RtAnimInterpolatorSetKeyFrameCallBacks((hierarchy)->currentAnim,\ + keyFrameTypeID) + +#define RpHAnimHierarchyBlendMacro(outHierarchy,inHierarchy1,inHierarchy2,alpha)\ + RtAnimInterpolatorBlend((outHierarchy)->currentAnim,\ + (inHierarchy1)->currentAnim,\ + (inHierarchy2)->currentAnim,\ + alpha) + +#define RpHAnimHierarchyAddTogetherMacro(outHierarchy,inHierarchy1,inHierarchy2)\ + RtAnimInterpolatorAddTogether((outHierarchy)->currentAnim,\ + (inHierarchy1)->currentAnim,\ + (inHierarchy2)->currentAnim) + + +#define RpHAnimHierarchySetAnimCallBackMacro(hierarchy,callBack,time,data)\ + RtAnimInterpolatorSetAnimCallBack((hierarchy)->currentAnim,callBack,time,data) + +#define RpHAnimHierarchySetAnimLoopCallBackMacro(hierarchy,callBack,data)\ + RtAnimInterpolatorSetAnimLoopCallBack((hierarchy)->currentAnim,callBack,data) + +#define RpHAnimHierarchyBlendSubHierarchyMacro(outHierarchy,inHierarchy1,inHierarchy2,alpha)\ + RtAnimInterpolatorBlendSubInterpolator((outHierarchy)->currentAnim,(inHierarchy1)->currentAnim,(inHierarchy2)->currentAnim,alpha) + +#define RpHAnimHierarchyAddSubHierarchyMacro(outHierarchy,mainHierarchy,subHierarchy)\ + RtAnimInterpolatorAddSubInterpolator((outHierarchy)->currentAnim,(mainHierarchy)->currentAnim,(subHierarchy)->currentAnim) + +#define RpHAnimHierarchyCopyMacro(outHierarchy,inHierarchy)\ + RtAnimInterpolatorCopy((outHierarchy)->currentAnim,(inHierarchy)->currentAnim) + + + +#ifdef RWDEBUG +extern RwBool +RpHAnimHierarchySetCurrentAnim(RpHAnimHierarchy *hierarchy, + RtAnimAnimation *anim); + +extern RtAnimAnimation * +RpHAnimHierarchyGetCurrentAnim(RpHAnimHierarchy *hierarchy); + +extern RwBool +RpHAnimHierarchySetCurrentAnimTime(RpHAnimHierarchy *hierarchy, + RwReal time); + +extern RwBool +RpHAnimHierarchyAddAnimTime(RpHAnimHierarchy *hierarchy, + RwReal time); + +extern RwBool +RpHAnimHierarchySubAnimTime(RpHAnimHierarchy *hierarchy, + RwReal time); + +extern RwBool +RpHAnimHierarchySetKeyFrameCallBacks(RpHAnimHierarchy *hierarchy, + RwInt32 keyFrameTypeID); + +extern void +RpHAnimHierarchySetAnimCallBack(RpHAnimHierarchy *hierarchy, + RtAnimCallBack callBack, + RwReal time, + void *data); + +extern RwBool +RpHAnimHierarchyBlend(RpHAnimHierarchy *outHierarchy, + RpHAnimHierarchy *inHierarchy1, + RpHAnimHierarchy *inHierarchy2, + RwReal alpha); + +extern RwBool +RpHAnimHierarchyAddTogether(RpHAnimHierarchy *outHierarchy, + RpHAnimHierarchy *inHierarchy1, + RpHAnimHierarchy *inHierarchy2); + +extern void +RpHAnimHierarchySetAnimLoopCallBack(RpHAnimHierarchy *hierarchy, + RtAnimCallBack callBack, + void *data); +extern RwBool +RpHAnimHierarchyBlendSubHierarchy(RpHAnimHierarchy *outHierarchy, + RpHAnimHierarchy *inHierarchy1, + RpHAnimHierarchy *inHierarchy2, + RwReal alpha); +extern RwBool +RpHAnimHierarchyAddSubHierarchy(RpHAnimHierarchy *outHierarchy, + RpHAnimHierarchy *mainHierarchy1, + RpHAnimHierarchy *subHierarchy2); +extern RwBool +RpHAnimHierarchyCopy(RpHAnimHierarchy *outHierarchy, + RpHAnimHierarchy *inHierarchy); + +#else + +#define RpHAnimHierarchySetCurrentAnim(hierarchy,anim) \ + RpHAnimHierarchySetCurrentAnimMacro((hierarchy),(anim)) + +#define RpHAnimHierarchyGetCurrentAnim(hierarchy) \ + RpHAnimHierarchyGetCurrentAnimMacro((hierarchy)) + +#define RpHAnimHierarchySetCurrentAnimTime(hierarchy,time) \ + RpHAnimHierarchySetCurrentAnimTimeMacro((hierarchy),(time)) + +#define RpHAnimHierarchyAddAnimTime(hierarchy,time) \ + RpHAnimHierarchyAddAnimTimeMacro((hierarchy),(time)) + +#define RpHAnimHierarchySubAnimTime(hierarchy,time) \ + RpHAnimHierarchySubAnimTimeMacro((hierarchy),(time)) + +#define RpHAnimHierarchySetKeyFrameCallBacks(hierarchy,keyFrameTypeID) \ + RpHAnimHierarchySetKeyFrameCallBacksMacro((hierarchy),(keyFrameTypeID)) + +#define RpHAnimHierarchyBlend(outHierarchy,inHierarchy1,inHierarchy2,alpha) \ + RpHAnimHierarchyBlendMacro((outHierarchy),(inHierarchy1),(inHierarchy2),(alpha)) + +#define RpHAnimHierarchyAddTogether(outHierarchy,inHierarchy1,inHierarchy2) \ + RpHAnimHierarchyAddTogetherMacro((outHierarchy),(inHierarchy1),(inHierarchy2)) + +#define RpHAnimHierarchySetAnimCallBack(hierarchy,callBack,time,data)\ + RpHAnimHierarchySetAnimCallBackMacro((hierarchy),(callBack),(time),(data)) + +#define RpHAnimHierarchySetAnimLoopCallBack(hierarchy,callBack,data)\ + RpHAnimHierarchySetAnimLoopCallBackMacro((hierarchy),(callBack),(data)) + +#define RpHAnimHierarchyBlendSubHierarchy(outHierarchy,inHierarchy1,inHierarchy2,alpha)\ + RpHAnimHierarchyBlendSubHierarchyMacro((outHierarchy),(inHierarchy1),(inHierarchy2),(alpha)) + +#define RpHAnimHierarchyAddSubHierarchy(outHierarchy,mainHierarchy,subHierarchy)\ + RpHAnimHierarchyAddSubHierarchyMacro((outHierarchy),(mainHierarchy),(subHierarchy)) + +#define RpHAnimHierarchyCopy(outHierarchy,inHierarchy)\ + RpHAnimHierarchyCopyMacro((outHierarchy),(inHierarchy)) + +#endif /* RWDEBUG */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + /* Legacy TypeDef */ typedef RtAnimAnimation RpHAnimAnimation; typedef RpHAnimKeyFrame RpHAnimStdKeyFrame; -void RpHAnimHierarchySetFreeListCreateParams(RwInt32 blockSize, RwInt32 numBlocksToPrealloc); // 0x7C45E0 -RpHAnimHierarchy* RpHAnimHierarchyCreate(RwInt32 numNodes, RwUInt32* nodeFlags, RwInt32* nodeIDs, RpHAnimHierarchyFlag flags, RwInt32 maxInterpKeyFrameSize); // 0x7C4C30 -RpHAnimHierarchy* RpHAnimHierarchyCreateFromHierarchy(RpHAnimHierarchy* hierarchy, RpHAnimHierarchyFlag flags, RwInt32 maxInterpKeyFrameSize); // 0x7C4ED0 -RpHAnimHierarchy* RpHAnimHierarchyDestroy(RpHAnimHierarchy* hierarchy); // 0x7C4D30 -RpHAnimHierarchy* RpHAnimHierarchyCreateSubHierarchy(RpHAnimHierarchy* parentHierarchy, RwInt32 startNode, RpHAnimHierarchyFlag flags, RwInt32 maxInterpKeyFrameSize); // 0x7C4DB0 -RpHAnimHierarchy* RpHAnimHierarchyAttach(RpHAnimHierarchy* hierarchy); // 0x7C4F40 -RpHAnimHierarchy* RpHAnimHierarchyDetach(RpHAnimHierarchy* hierarchy); // 0x7C4FF0 -RpHAnimHierarchy* RpHAnimHierarchyAttachFrameIndex(RpHAnimHierarchy* hierarchy, RwInt32 nodeIndex); // 0x7C5020 -RpHAnimHierarchy* RpHAnimHierarchyDetachFrameIndex(RpHAnimHierarchy* hierarchy, RwInt32 nodeIndex); // 0x7C5100 -RwBool RpHAnimFrameSetHierarchy(RwFrame* frame, RpHAnimHierarchy* hierarchy); // 0x7C5130 -RpHAnimHierarchy* RpHAnimFrameGetHierarchy(RwFrame* frame); // 0x7C5160 -RwMatrix* RpHAnimHierarchyGetMatrixArray(RpHAnimHierarchy* hierarchy); // 0x7C5120 -RwBool RpHAnimHierarchyUpdateMatrices(RpHAnimHierarchy* hierarchy); // 0x7C51D0 -RwInt32 RpHAnimIDGetIndex(RpHAnimHierarchy* hierarchy, RwInt32 ID); // 0x7C51A0 -RwBool RpHAnimPluginAttach(); // 0x7C4600 -void RpHAnimKeyFrameApply(void* matrix, void* voidIFrame); // 0x7C5B80 -void RpHAnimKeyFrameBlend(void* voidOut, void* voidIn1, void* voidIn2, RwReal alpha); // 0x7C60C0 -void RpHAnimKeyFrameInterpolate(void* voidOut, void* voidIn1, void* voidIn2, RwReal time, void* customData); // 0x7C5CA0 -void RpHAnimKeyFrameAdd(void* voidOut, void* voidIn1, void* voidIn2); // 0x7C6720 -void RpHAnimKeyFrameMulRecip(void* voidFrame, void* voidStart); // 0x7C65C0 -RtAnimAnimation* RpHAnimKeyFrameStreamRead(RwStream* stream, RtAnimAnimation* animation); // 0x7C64C0 -RwBool RpHAnimKeyFrameStreamWrite(const RtAnimAnimation* animation, RwStream* stream); // 0x7C6540 -RwInt32 RpHAnimKeyFrameStreamGetSize(const RtAnimAnimation* animation); // 0x7C65B0 -RwBool RpHAnimFrameSetID(RwFrame* frame, RwInt32 id); // 0x7C5170 -RwInt32 RpHAnimFrameGetID(RwFrame* frame); // 0x7C5190 +/* Legacy Macros */ + + +/* Animations */ + + +#define RpHAnimAnimationCreate(typeID,numFrames,flags,duration)\ + RtAnimAnimationCreate((typeID),(numFrames),(flags),(duration)) + + +#define RpHAnimAnimationDestroy(animation)\ + RtAnimAnimationDestroy((animation)) + +#define RpHAnimAnimationGetTypeID(animation)\ + RtAnimAnimationGetTypeID((animation)) + + +#define RpHAnimAnimationRead(filename)\ + RtAnimAnimationRead((filename)) + + +#define RpHAnimAnimationWrite(animation,filename)\ + RtAnimAnimationWrite((animation),(filename)) + + +#define RpHAnimAnimationStreamRead(stream)\ + RtAnimAnimationStreamRead((stream)) + + +#define RpHAnimAnimationStreamWrite(animation,stream)\ + RtAnimAnimationStreamWrite((animation),(stream)) + + +#define RpHAnimAnimationStreamGetSize(animation)\ + RtAnimAnimationStreamGetSize((animation)) + + +#define RpHAnimAnimationMakeDelta(animation,numNodes,time)\ + RtAnimAnimationMakeDelta((animation),(numNodes),(time)) + + +/* Animation Interpolator */ + +#define RpHAnimHierarchyStdKeyFrameAddAnimTime(hierarchy,time)\ + RpHAnimHierarchyHAnimKeyFrameAddAnimTime((hierarchy),(time)) + +#define RpHAnimHierarchyHAnimKeyFrameAddAnimTime(hierarchy,time)\ + RpHAnimHierarchyAddAnimTime((hierarchy),(time)) + +#endif /* RPHANIM_H */ diff --git a/source/game_sa/RenderWare/rw/rpplugin.h b/source/game_sa/RenderWare/rw/rpplugin.h new file mode 100644 index 0000000000..e45f6b1584 --- /dev/null +++ b/source/game_sa/RenderWare/rw/rpplugin.h @@ -0,0 +1,36 @@ +#if (!defined(RPPLUGIN_H)) +#define RPPLUGIN_H + +/* + * This is a required file for each plugin. It should be generated by the + * plugin vendor. The information contained in this file should be unique + * derived from the vendor id / name to prevent conflict with other plugins + */ + +#if (defined(RWDEBUG) && !defined(RPERROR_H)) +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ +extern long rtQuatStackDepth; +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#define RWDEBUGSTACKDEPTH rtQuatStackDepth +#endif /* (defined(RWDEBUG) !defined(RPERROR_H)) */ + +#include "rpcriter.h" /* Note: each vendor can choose their own method for + * allocation of unique ID's. This file defines + * the ID's used by Criterion. + */ + +/* rwPLUGIN_ID must be defined in order to use the RenderWare debug/error + * mechanism + */ + +#define rwPLUGIN_ID rwID_QUATPLUGIN +#define rwPLUGIN_ERRFUNC _rwdb_CriterionQuat +#define rwPLUGIN_ERRENUM e_rwdb_CriterionQuat +#define rwPLUGIN_ERRENUMLAST e_rwdb_CriterionQuatLAST + +#endif /* (!defined(RPPLUGIN_H)) */ diff --git a/source/game_sa/RenderWare/rw/rtanim.cpp b/source/game_sa/RenderWare/rw/rtanim.cpp index 88a288e0ee..4cd35d6ad6 100644 --- a/source/game_sa/RenderWare/rw/rtanim.cpp +++ b/source/game_sa/RenderWare/rw/rtanim.cpp @@ -1,5 +1,6 @@ #include "StdInc.h" +#include "rpdbgerr.h" #include "rtanim.h" void RtAnimAnimationFreeListCreateParams(RwInt32 blockSize, RwInt32 numBlocksToPrealloc) { @@ -57,9 +58,106 @@ RtAnimInterpolator* RtAnimInterpolatorCreate(RwInt32 numNodes, RwInt32 maxInterp void RtAnimInterpolatorDestroy(RtAnimInterpolator* anim) { ((void(__cdecl *)(RtAnimInterpolator*))0x7CD590)(anim); } - -RwBool RtAnimInterpolatorSetCurrentAnim(RtAnimInterpolator* animI, RtAnimAnimation* anim) { - return ((RwBool(__cdecl *)(RtAnimInterpolator*, RtAnimAnimation*))0x7CD5A0)(animI, anim); + +/** + * \ingroup rtanim + * \ref RtAnimInterpolatorSetCurrentAnim sets the current animation on the + * animation interpolator. It is assumed that the animation is designed for the + * animation interpolator it is being set on since no animation interpolator structure + * comparisons are made. The animation defines the interpolation schemes used + * and they will be setup on the animation interpolator at this call. The maximum + * keyframe size of the animation interpolator must be sufficient to support the + * keyframe size required by the interpolation scheme. The animation is + * initialized to time zero at this call. + * + * \param animI Pointer to the \ref RtAnimInterpolator. + * \param anim Pointer to the \ref RtAnimAnimation. + * + * \return \ref RwBool, TRUE on success, FALSE if an error occurs. + * + */ +RwBool +RtAnimInterpolatorSetCurrentAnim(RtAnimInterpolator *animI, + RtAnimAnimation *anim) +{ + RtAnimInterpolatorInfo *interpInfo; + RwInt32 i; + void *kf1, *kf2, *interpFrame; + + RWAPIFUNCTION(RWSTRING("RtAnimInterpolatorSetCurrentAnim")); + RWASSERT(animI); + RWASSERT(anim); + RWASSERT(anim->numFrames >= animI->numNodes * 2); + + animI->pCurrentAnim = anim; + animI->currentTime = 0.0f; + + /* Get interpolator functions */ + interpInfo = anim->interpInfo; + animI->currentInterpKeyFrameSize = interpInfo->interpKeyFrameSize; + RWASSERT(animI->currentInterpKeyFrameSize <= animI->maxInterpKeyFrameSize); + animI->currentAnimKeyFrameSize = interpInfo->animKeyFrameSize; + + animI->keyFrameApplyCB = interpInfo->keyFrameApplyCB; + animI->keyFrameBlendCB = interpInfo->keyFrameBlendCB; + animI->keyFrameInterpolateCB = interpInfo->keyFrameInterpolateCB; + animI->keyFrameAddCB = interpInfo->keyFrameAddCB; + + /* + * Set up initial interpolation frames for time=0 + * First copy the time=0 keyframes to the interpolated frame array + */ + //TODO: convert these don't copy + for (i=0; i < animI->numNodes; i++) + { + animI->keyFrameInterpolateCB(rtANIMGETINTERPFRAME(animI, i), + ((RwUInt8 *)anim->pFrames) + (i * animI->currentAnimKeyFrameSize), + ((RwUInt8 *)anim->pFrames) + ((i + animI->numNodes) * animI->currentAnimKeyFrameSize), + 0.0f, anim->customData); + } + + /* + * Now initialize the interpolated frame headers to point to the initial + * keyframe pairs. + */ + if (!notsa::IsFixBugs() || anim->interpInfo->typeID != rwID_RPANIMBLENDPLUGIN) { + /* + * In RW there is an RtAnimAnimation, which holds the raw keyframes of an animation. + * To play an animation, an RtAnimAnimation is given to an RtAnimInterpolator which + * then interpolates the keyframes into interpolation frames which are then used to + * animate e.g. a hierarchy by generating matrices from them. + * But R* hasn't really used RW's animation system since III. + * In III and VC R* wrote their own interpolated animation data into a dummy + * RtAnimAnimation and practically didn't use the RtAnimInterpolator. + * In SA they went a step further and now write into the interpolation frames of + * the RtAnimInterpolator instead, almost completely avoid RtAnimAnimation. + * Almost! They're still attaching an RtAnimAnimation to an RtAnimInterpolator + * and this is where stuff breaks. + * + * So, instead of corrupting our lovely `RpHAnimBlendInterpFrame`'s, we leave them alone ;) + * + * For more info see: https://gtaforums.com/topic/669045-silentpatch/page/154/#comment-1068971599 + */ + + interpFrame = rtANIMGETINTERPFRAME(animI, 0); + kf1 = anim->pFrames; + kf2 = (RwInt8 *)anim->pFrames + animI->numNodes * animI->currentAnimKeyFrameSize; + + for (i=0; i < animI->numNodes; i++) + { + RtAnimInterpFrameHeader *hdr = (RtAnimInterpFrameHeader *)interpFrame; + + hdr->keyFrame1 = (RtAnimKeyFrameHeader*)kf1; + hdr->keyFrame2 = (RtAnimKeyFrameHeader*)kf2; + + interpFrame = (RwInt8 *)interpFrame + animI->currentInterpKeyFrameSize; + kf1 = (RwInt8 *)kf1 + animI->currentAnimKeyFrameSize; + kf2 = (RwInt8 *)kf2 + animI->currentAnimKeyFrameSize; + } + } + animI->pNextFrame = (RwUInt8 *)anim->pFrames + (animI->numNodes * animI->currentAnimKeyFrameSize * 2); + + RWRETURN(TRUE); } RwBool RtAnimInterpolatorSetKeyFrameCallBacks(RtAnimInterpolator* anim, RwInt32 keyFrameTypeID) { @@ -113,3 +211,10 @@ RwBool RtAnimInterpolatorBlendSubInterpolator(RtAnimInterpolator* outAnim, RtAni RwBool RtAnimInterpolatorAddSubInterpolator(RtAnimInterpolator* outAnim, RtAnimInterpolator* mainAnim, RtAnimInterpolator* subAnim) { return ((RwBool(__cdecl *)(RtAnimInterpolator*, RtAnimInterpolator*, RtAnimInterpolator*))0x7CDEF0)(outAnim, mainAnim, subAnim); } + +void RtAnim::InjectHooks() { + RH_ScopedNamespaceName("RtAnim"); + RH_ScopedCategory("Plugins"); + + RH_ScopedGlobalInstall(RtAnimInterpolatorSetCurrentAnim, 0x7CD5A0); +} diff --git a/source/game_sa/RenderWare/rw/rtanim.h b/source/game_sa/RenderWare/rw/rtanim.h index 962b18a03a..53cf0942e7 100644 --- a/source/game_sa/RenderWare/rw/rtanim.h +++ b/source/game_sa/RenderWare/rw/rtanim.h @@ -172,7 +172,7 @@ struct RtAnimInterpolatorInfo /**< The ID of the interpolation scheme. */ RwInt32 interpKeyFrameSize; /**< Size, in bytes, of the interpolated keyframe structure. */ - //RwInt32 animKeyFrameSize; + RwInt32 animKeyFrameSize; /**< Size, in bytes, of the animation keyframe structure. */ RtAnimKeyFrameApplyCallBack keyFrameApplyCB; /**< Pointer to a function that converts a keyframe to the needed @@ -456,3 +456,7 @@ RwBool RtAnimInterpolatorAddTogether(RtAnimInterpolator* outAnim, RtAnimInterpol RtAnimInterpolator* RtAnimInterpolatorCreateSubInterpolator(RtAnimInterpolator* parentAnim, RwInt32 startNode, RwInt32 numNodes, RwInt32 maxInterpKeyFrameSize); // 0x7CDCB0 RwBool RtAnimInterpolatorBlendSubInterpolator(RtAnimInterpolator* outAnim, RtAnimInterpolator* inAnim1, RtAnimInterpolator* inAnim2, RwReal alpha); // 0x7CDCF0 RwBool RtAnimInterpolatorAddSubInterpolator(RtAnimInterpolator* outAnim, RtAnimInterpolator* mainAnim, RtAnimInterpolator* subAnim); // 0x7CDEF0 + +namespace RtAnim { + void InjectHooks(); +}; diff --git a/source/game_sa/RenderWare/rw/rtquat.cpp b/source/game_sa/RenderWare/rw/rtquat.cpp index 30e3ff82b3..60303bd6a8 100644 --- a/source/game_sa/RenderWare/rw/rtquat.cpp +++ b/source/game_sa/RenderWare/rw/rtquat.cpp @@ -2,50 +2,6 @@ #include "rtquat.h" -void RtQuatConvertToMatrix(const RtQuat* const qpQuat, RwMatrix* const mpMatrix) { - RwReal rS; - RwV3d rV; - RwV3d rW; - RwV3d square; - RwV3d cross; - - rS = ((RwReal)2) / (qpQuat->imag.x * qpQuat->imag.x - + qpQuat->imag.y * qpQuat->imag.y - + qpQuat->imag.z * qpQuat->imag.z - + qpQuat->real * qpQuat->real); - - RwV3dScale(&rV, &(qpQuat)->imag, rS); - RwV3dScale(&rW, &rV, (qpQuat)->real); - - square.x = (qpQuat)->imag.x * rV.x; - square.y = (qpQuat)->imag.y * rV.y; - square.z = (qpQuat)->imag.z * rV.z; - - cross.x = (qpQuat)->imag.y * rV.z; - cross.y = (qpQuat)->imag.z * rV.x; - cross.z = (qpQuat)->imag.x * rV.y; - - (mpMatrix)->right.x = ((RwReal)1) - (square.y + square.z); - (mpMatrix)->right.y = cross.z + rW.z; - (mpMatrix)->right.z = cross.y - rW.y; - - (mpMatrix)->up.x = cross.z - rW.z; - (mpMatrix)->up.y = ((RwReal)1) - (square.z + square.x); - (mpMatrix)->up.z = cross.x + rW.x; - - (mpMatrix)->at.x = cross.y + rW.y; - (mpMatrix)->at.y = cross.x - rW.x; - (mpMatrix)->at.z = ((RwReal)1) - (square.x + square.y); - - /* Set position */ - (mpMatrix)->pos.x = ((RwReal)0); - (mpMatrix)->pos.y = ((RwReal)0); - (mpMatrix)->pos.z = ((RwReal)0); - - /* Matrix is orthogonal */ - mpMatrix->flags = rwMATRIXTYPEORTHONORMAL & ~rwMATRIXINTERNALIDENTITY; -} - RwBool RtQuatConvertFromMatrix(RtQuat* qpQuat, const RwMatrix* const mpMatrix) { return ((RwBool(__cdecl*)(RtQuat*, const RwMatrix* const))0x7EB5C0)(qpQuat, mpMatrix); } diff --git a/source/game_sa/RenderWare/rw/rtquat.h b/source/game_sa/RenderWare/rw/rtquat.h index 7ab10519f2..30063ad5c2 100644 --- a/source/game_sa/RenderWare/rw/rtquat.h +++ b/source/game_sa/RenderWare/rw/rtquat.h @@ -1,10 +1,3 @@ -/* - Plugin-SDK file - Authors: GTA Community. See more here - https://github.com/DK22Pac/plugin-sdk - Do not delete this comment block. Respect others' work! -*/ -#pragma once /* * Data structures for Quaternions * See http://www-groups.dcs.st-and.ac.uk/~history/Mathematicians/Hamilton.html @@ -12,6 +5,9 @@ * Copyright (c) Criterion Software Limited */ +#ifndef RTQUAT_H +#define RTQUAT_H + /** * \defgroup rtquat RtQuat * \ingroup mathtools @@ -42,10 +38,11 @@ Includes */ -#include - +#include /* renderware */ -#include "rwcore.h" +#include "rwplcore.h" + +//#include "rtquat.rpe" /* automatically generated header file */ #define RW_TOL_ORTHONORMAL ((RwReal)0.01) @@ -67,15 +64,583 @@ struct RtQuat RwReal real; /**< The real part */ }; + +/**************************************************************************** + Defines + */ + +#define RtQuatInitMacro( result, _x, _y, _z, _w) \ +MACRO_START \ +{ \ + (result)->real = (_w); \ + (result)->imag.x = (_x); \ + (result)->imag.y = (_y); \ + (result)->imag.z = (_z); \ +} \ +MACRO_STOP + +#if (!defined(RtQuatAssignMacro)) +#define RtQuatAssignMacro(_target, _source) \ + ( *(_target) = *(_source) ) +#endif /* (!defined(RtQuatAssignMacro)) */ + +#define RtQuatAddMacro( result, q1, q2 ) \ +MACRO_START \ +{ \ + (result)->real = (q1)->real + (q2)->real; \ + RwV3dAddMacro(&(result)->imag, &(q1)->imag, &(q2)->imag); \ +} \ +MACRO_STOP + +#define RtQuatIncrementRealPartMacro(result, s, q) \ +MACRO_START \ +{ \ + (result)->real = (q)->real + s; \ + (result)->imag.x = (q)->imag.x; \ + (result)->imag.y = (q)->imag.y; \ + (result)->imag.z = (q)->imag.z; \ +} \ +MACRO_STOP + +#define RtQuatDecrementRealPartMacro(result, s, q) \ +MACRO_START \ +{ \ + (result)->real = (q)->real - s; \ + (result)->imag.x = (q)->imag.x; \ + (result)->imag.y = (q)->imag.y; \ + (result)->imag.z = (q)->imag.z; \ +} \ +MACRO_STOP + +#define RtQuatIncrementMacro( result, dq ) \ +MACRO_START \ +{ \ + (result)->real = (result)->real + (dq)->real; \ + RwV3dAddMacro(&(result)->imag, &(result)->imag, &(dq)->imag); \ +} \ +MACRO_STOP + +#define RtQuatSubMacro( result, q1, q2 ) \ +MACRO_START \ +{ \ + (result)->real = (q1)->real - (q2)->real; \ + RwV3dSubMacro(&(result)->imag, &(q1)->imag, &(q2)->imag); \ +} \ +MACRO_STOP + +#define RtQuatNegateMacro( result, q ) \ +MACRO_START \ +{ \ + (result)->real = -(q)->real; \ + (result)->imag.x = -(q)->imag.x; \ + (result)->imag.y = -(q)->imag.y; \ + (result)->imag.z = -(q)->imag.z; \ +} \ +MACRO_STOP + +#define RtQuatConjugateMacro( result, q) \ +MACRO_START \ +{ \ + (result)->real = (q)->real; \ + (result)->imag.x = -(q)->imag.x; \ + (result)->imag.y = -(q)->imag.y; \ + (result)->imag.z = -(q)->imag.z; \ +} \ +MACRO_STOP + +#define RtQuatScaleMacro(result, q, scale ) \ +MACRO_START \ +{ \ + (result)->real = (q)->real * scale; \ + RwV3dScaleMacro(&(result)->imag, &(q)->imag, scale); \ +} \ +MACRO_STOP + +#define RtQuatModulusSquaredMacro( q ) \ + ((q)->real * (q)->real + \ + RwV3dDotProductMacro(&(q)->imag, &(q)->imag)) + +#define RtQuatModulusMacro( result, q ) \ +MACRO_START \ +{ \ + (result) = (q); \ + rwSqrtMacro(&result, result); \ +} \ +MACRO_STOP + +#define RtQuatMultiplyMacro( result, q1, q2) \ +MACRO_START \ +{ \ + /* \ + * Assumes q1 != result != q2 \ + */ \ + (result)->real = \ + (q1)->real * (q2)->real - \ + RwV3dDotProductMacro(&(q1)->imag,&(q2)->imag); \ + RwV3dCrossProductMacro(&(result)->imag, &(q1)->imag, &(q2)->imag); \ + RwV3dIncrementScaledMacro(&(result)->imag, &(q2)->imag, (q1)->real); \ + RwV3dIncrementScaledMacro(&(result)->imag, &(q1)->imag, (q2)->real); \ +} \ +MACRO_STOP + +#define RtQuatReciprocalMacro( result, q) \ +MACRO_START \ +{ \ + /* \ + * Assumes result != q \ + */ \ + RwReal val = RtQuatModulusSquaredMacro(q); \ + \ + if (val > (RwReal) 0) \ + { \ + val = ((RwReal)1) / val; \ + (result)->real = (q)->real * val; \ + val = -val; \ + RwV3dScaleMacro(&(result)->imag, &(q)->imag, val); \ + } \ +} \ +MACRO_STOP + +#define RtQuatSquareMacro( result, q ) \ +MACRO_START \ +{ \ + /* \ + * Assumes result != q \ + */ \ + RwReal val = ((RwReal)2) * (q)->real ; \ + \ + (result)->real = \ + (q)->real * (q)->real - \ + RwV3dDotProductMacro(&(q)->imag, &(q)->imag); \ + RwV3dScaleMacro(&(result)->imag, &(q)->imag, val); \ +} \ +MACRO_STOP + +#define RtQuatSquareRootMacro( result, q ) \ +MACRO_START \ +{ \ + /* \ + * Assumes result != q \ + * other root is of course -result \ + */ \ + RwReal val; \ + \ + RtQuatModulusMacro(val,q); \ + val = ((q)->real + val) * ((RwReal) 0.5); \ + \ + if (val > ((RwReal)0)) \ + { \ + rwSqrtMacro(&val, val); \ + (result)->real = val; \ + val = ((RwReal)0.5) / val; \ + RwV3dScale(&(result)->imag, &(q)->imag, val); \ + } \ + else \ + { \ + result->imag.x = -(q)->real; \ + rwSqrtMacro(&(result->imag.x), result->imag.x); \ + result->imag.y = ((RwReal)0); \ + result->imag.x = ((RwReal)0); \ + result->real = ((RwReal)0); \ + } \ +} \ +MACRO_STOP + +#define RtQuatLogMacro(result, q) \ +MACRO_START \ +{ \ + /* \ + * Assumes result != q \ + */ \ + const RwReal mod2 = RtQuatModulusSquaredMacro(q); \ + RwReal sin_b; \ + RwReal radians; \ + RwReal factor; \ + \ + sin_b = RwV3dDotProduct(&(q)->imag, &(q)->imag); \ + rwSqrtMacro(&sin_b, sin_b); \ + radians = (RwReal) RwATan2(sin_b, (q)->real); \ + factor = (sin_b > (RwReal) 0) ? (((RwReal)radians) / sin_b) : 0 ; \ + \ + RwV3dScaleMacro(&(result)->imag, &(q)->imag, factor); \ + (result)->real = ((RwReal) RwLog(mod2)) * ((RwReal) 0.5); \ + \ +} \ +MACRO_STOP + +#define RtQuatExpMacro(result, q) \ +MACRO_START \ +{ \ + /* \ + * Assumes result != q \ + */ \ + const RwReal exp_a = (RwReal)RwExp((q)->real); \ + RwReal mod_b; \ + RwReal factor; \ + \ + mod_b = RwV3dDotProduct(&(q)->imag, &(q)->imag); \ + rwSqrtMacro(&mod_b, mod_b); \ + factor = ( (mod_b > (RwReal) 0) ? \ + (exp_a * ((RwReal)RwSin(mod_b)) / mod_b) : \ + 0 ) ; \ + \ + RwV3dScaleMacro(&(result)->imag, &(q)->imag, factor); \ + (result)->real = exp_a * (RwReal)RwCos(mod_b); \ +} \ +MACRO_STOP + +#define RtQuatPowMacro( result, q, e) \ +MACRO_START \ +{ \ + RtQuat qLog; \ + \ + RtQuatLogMacro(&qLog, q); \ + RtQuatScaleMacro(&qLog, &qLog, e); \ + RtQuatExpMacro(result, &qLog); \ +} \ +MACRO_STOP + +#define RtQuatUnitLogMacro(result, q) \ +MACRO_START \ +{ \ + /* \ + * Assumes result != q \ + */ \ + RwReal sin_b ; \ + RwReal radians ; \ + RwReal factor ; \ + \ + sin_b = RwV3dDotProduct(&(q)->imag, &(q)->imag); \ + rwSqrtMacro(&sin_b, sin_b); \ + radians = (RwReal)RwATan2(sin_b, (q)->real); \ + factor = (sin_b > (RwReal) 0) ? (((RwReal)radians) / sin_b) : 0 ; \ + \ + RwV3dScaleMacro(&(result)->imag, &(q)->imag, factor); \ + (result)->real = (RwReal)0; \ + \ +} \ +MACRO_STOP + +#define RtQuatUnitExpMacro(result, q) \ +MACRO_START \ +{ \ + /* \ + * Assumes result != q \ + */ \ + RwReal mod_b; \ + RwReal factor; \ + \ + mod_b = RwV3dDotProduct(&(q)->imag, &(q)->imag); \ + rwSqrtMacro(&mod_b, mod_b); \ + factor = (mod_b > (RwReal) 0) ? (((RwReal)RwSin(mod_b)) / mod_b) : 0 ; \ + \ + RwV3dScaleMacro(&(result)->imag, &(q)->imag, factor); \ + (result)->real = (RwReal)RwCos(mod_b); \ + \ +} \ +MACRO_STOP + +#define RtQuatUnitPowMacro( result, q, e) \ +MACRO_START \ +{ \ + RtQuat qLog; \ + \ + RtQuatUnitLogMacro(&qLog, q); \ + RwV3dScaleMacro(&qLog.imag, &qLog.imag, e); \ + RtQuatUnitExpMacro(result, &qLog); \ +} \ +MACRO_STOP + +#define RtQuatConvertToMatrixMacro(qpQuat, mpMatrix) \ +MACRO_START \ +{ \ + RwReal rS; \ + RwV3d rV; \ + RwV3d rW; \ + RwV3d square; \ + RwV3d cross; \ + \ + rS = ((RwReal) 2) / RtQuatModulusSquaredMacro((qpQuat)); \ + \ + RwV3dScale(&rV, &(qpQuat)->imag, rS); \ + RwV3dScale(&rW, &rV, (qpQuat)->real); \ + \ + square.x = (qpQuat)->imag.x * rV.x; \ + square.y = (qpQuat)->imag.y * rV.y; \ + square.z = (qpQuat)->imag.z * rV.z; \ + \ + cross.x = (qpQuat)->imag.y * rV.z; \ + cross.y = (qpQuat)->imag.z * rV.x; \ + cross.z = (qpQuat)->imag.x * rV.y; \ + \ + (mpMatrix)->right.x = ((RwReal) 1) - (square.y + square.z); \ + (mpMatrix)->right.y = cross.z + rW.z; \ + (mpMatrix)->right.z = cross.y - rW.y; \ + \ + (mpMatrix)->up.x = cross.z - rW.z; \ + (mpMatrix)->up.y = ((RwReal) 1) - (square.z + square.x); \ + (mpMatrix)->up.z = cross.x + rW.x; \ + \ + (mpMatrix)->at.x = cross.y + rW.y; \ + (mpMatrix)->at.y = cross.x - rW.x; \ + (mpMatrix)->at.z = ((RwReal) 1) - (square.x + square.y); \ + \ + /* Set position */ \ + (mpMatrix)->pos.x = ((RwReal) 0); \ + (mpMatrix)->pos.y = ((RwReal) 0); \ + (mpMatrix)->pos.z = ((RwReal) 0); \ + \ + /* Matrix is orthogonal */ \ + rwMatrixSetFlags((mpMatrix), \ + (rwMATRIXTYPEORTHONORMAL & \ + ~rwMATRIXINTERNALIDENTITY) ); \ + \ +} \ +MACRO_STOP + +#define RtQuatUnitConvertToMatrixMacro(qpQuat, mpMatrix) \ +MACRO_START \ +{ \ + const RwReal x = (qpQuat)->imag.x; \ + const RwReal y = (qpQuat)->imag.y; \ + const RwReal z = (qpQuat)->imag.z; \ + const RwReal w = (qpQuat)->real; \ + RwV3d square; \ + RwV3d cross; \ + RwV3d wimag; \ + \ + square.x = x * x; \ + square.y = y * y; \ + square.z = z * z; \ + \ + cross.x = y * z; \ + cross.y = z * x; \ + cross.z = x * y; \ + \ + wimag.x = w * x; \ + wimag.y = w * y; \ + wimag.z = w * z; \ + \ + (mpMatrix)->right.x = 1 - 2 * (square.y + square.z); \ + (mpMatrix)->right.y = 2 * (cross.z + wimag.z); \ + (mpMatrix)->right.z = 2 * (cross.y - wimag.y); \ + \ + (mpMatrix)->up.x = 2 * (cross.z - wimag.z); \ + (mpMatrix)->up.y = 1 - 2 * (square.x + square.z); \ + (mpMatrix)->up.z = 2 * (cross.x + wimag.x); \ + \ + (mpMatrix)->at.x = 2 * (cross.y + wimag.y); \ + (mpMatrix)->at.y = 2 * (cross.x - wimag.x); \ + (mpMatrix)->at.z = (1 - 2 * (square.x + square.y)); \ + \ + /* Set position */ \ + (mpMatrix)->pos.x = ((RwReal) 0); \ + (mpMatrix)->pos.y = ((RwReal) 0); \ + (mpMatrix)->pos.z = ((RwReal) 0); \ + \ + /* Matrix is orthonormal */ \ + rwMatrixSetFlags((mpMatrix), \ + (rwMATRIXTYPEORTHONORMAL & \ + ~rwMATRIXINTERNALIDENTITY) ); \ +} \ +MACRO_STOP + +#if (! ( defined(RWDEBUG) || defined(RWSUPPRESSINLINE) )) + +#define RtQuatInit( result, _x, _y, _z, _w) \ + RtQuatInitMacro( result, _x, _y, _z, _w) + +#define RtQuatAssign( to, from ) \ + RtQuatAssignMacro( to, from ) + +#define RtQuatAdd( result, q1, q2 ) \ + RtQuatAddMacro( result, q1, q2 ) + +#define RtQuatIncrementRealPart(result, s, q) \ + RtQuatIncrementRealPartMacro(result, s, q) + +#define RtQuatDecrementRealPart(result, s, q) \ + RtQuatDecrementRealPartMacro(result, s, q) + +#define RtQuatIncrement( result, dq ) \ + RtQuatIncrementMacro( result, dq ) + +#define RtQuatSub( result, q1, q2 ) \ + RtQuatSubMacro( result, q1, q2 ) + +#define RtQuatNegate( result, q ) \ + RtQuatNegateMacro( result, q ) + +#define RtQuatConjugate( result, q) \ + RtQuatConjugateMacro( result, q) + +#define RtQuatScale(result, q, scale ) \ + RtQuatScaleMacro(result, q, scale ) + +#define RtQuatModulusSquared( q ) \ + RtQuatModulusSquaredMacro( q ) + +#define RtQuatMultiply( result, q1, q2) \ + RtQuatMultiplyMacro( result, q1, q2) + +#define RtQuatReciprocal( result, q) \ + RtQuatReciprocalMacro( result, q) + +#define RtQuatSquare( result, q ) \ + RtQuatSquareMacro( result, q ) + +#define RtQuatSquareRoot( result, q ) \ + RtQuatSquareRootMacro( result, q ) + +#define RtQuatLog( result, q ) \ + RtQuatLogMacro( result, q ) + +#define RtQuatExp( result, q ) \ + RtQuatExpMacro( result, q ) + +#define RtQuatPow( result, q, e ) \ + RtQuatPowMacro( result, q, e ) + +#define RtQuatUnitLog( result, q ) \ + RtQuatUnitLogMacro( result, q ) + +#define RtQuatUnitExp( result, q ) \ + RtQuatUnitExpMacro( result, q ) + +#define RtQuatUnitPow( result, q, e ) \ + RtQuatUnitPowMacro( result, q, e ) + +#define RtQuatConvertToMatrix(qpQuat, mpMatrix) \ + RtQuatConvertToMatrixMacro(qpQuat, mpMatrix) + +#define RtQuatUnitConvertToMatrix(qpQuat, mpMatrix) \ + RtQuatUnitConvertToMatrixMacro(qpQuat, mpMatrix) + +#endif /* (! ( defined(RWDEBUG) || defined(RWSUPPRESSINLINE) )) */ + +/**************************************************************************** + Function prototypes + */ +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +extern RwBool +RtQuatConvertFromMatrix(RtQuat * const qpQuat, + const RwMatrix * const mpMatrix); + +extern RtQuat * +RtQuatRotate(RtQuat * quat, + const RwV3d * axis, + RwReal angle, + RwOpCombineType combineOp); + +extern const RtQuat * +RtQuatQueryRotate(const RtQuat *quat, + RwV3d * unitAxis, + RwReal * angle); + +extern RwV3d * +RtQuatTransformVectors(RwV3d * vectorsOut, + const RwV3d * vectorsIn, + const RwInt32 numPoints, + const RtQuat *quat); + +extern RwReal +RtQuatModulus(RtQuat * q); + +#if ( defined(RWDEBUG) || defined(RWSUPPRESSINLINE) ) + +extern void +RtQuatInit(RtQuat * result, RwReal x, RwReal y, RwReal z, RwReal w); + +extern void +RtQuatAssign(RtQuat * to, RtQuat * from); + +extern void +RtQuatAdd(RtQuat * result, RtQuat * q1, RtQuat * q2); + +extern void +RtQuatIncrementRealPart(RtQuat * result, RwReal s, RtQuat * q); + +extern void +RtQuatDecrementRealPart(RtQuat * result, RwReal s, RtQuat * q); + +extern void +RtQuatIncrement(RtQuat * result, RtQuat * dq); + +extern void +RtQuatSub(RtQuat * result, RtQuat * q1, RtQuat * q2); + +extern void +RtQuatNegate(RtQuat * result, RtQuat * q); + +extern void +RtQuatConjugate(RtQuat * result, RtQuat * q); + +extern void +RtQuatScale(RtQuat * result, RtQuat * q, RwReal scale); + +extern RwReal +RtQuatModulusSquared(RtQuat * q); + +extern void +RtQuatMultiply(RtQuat * result, RtQuat * q1, RtQuat * q2); + +extern void +RtQuatReciprocal(RtQuat * result, RtQuat * q); + +extern void +RtQuatSquare(RtQuat * result, RtQuat * q); + +extern void +RtQuatSquareRoot(RtQuat * result, RtQuat * q); + +extern void +RtQuatLog(RtQuat * result, RtQuat * q); + +extern void +RtQuatExp(RtQuat * result, RtQuat * q); + +extern void +RtQuatPow(RtQuat * result, RtQuat * q, RwReal e); + +extern void +RtQuatUnitLog(RtQuat * result, RtQuat * q); + +extern void +RtQuatUnitExp(RtQuat * result, RtQuat * q); + +extern void +RtQuatUnitPow(RtQuat * result, RtQuat * q, RwReal e); + +extern void +RtQuatConvertToMatrix(const RtQuat * const qpQuat, + RwMatrix * const mpMatrix); + +extern void +RtQuatUnitConvertToMatrix(const RtQuat * const qpQuat, + RwMatrix * const mpMatrix); + +#endif /* ( defined(RWDEBUG) || defined(RWSUPPRESSINLINE) ) */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + /* * Backwards compatibility code */ typedef RtQuat RpQuat; -void RtQuatConvertToMatrix(const RtQuat* const qpQuat, RwMatrix* const mpMatrix); -RwBool RtQuatConvertFromMatrix(RtQuat * qpQuat, const RwMatrix * const mpMatrix); // 0x7EB5C0 -RtQuat* RtQuatRotate(RtQuat* quat, const RwV3d* axis, RwReal angle, RwOpCombineType combineOp); // 0x7EB7C0 -const RtQuat* RtQuatQueryRotate(const RtQuat* quat, RwV3d* unitAxis, RwReal* angle); // 0x7EBA80 -RwV3d* RtQuatTransformVectors(RwV3d* vectorsOut, const RwV3d* vectorsIn, const RwInt32 numPoints, const RtQuat* quat); // 0x7EBBB0 -RwReal RtQuatModulus(RtQuat* q); // 0x7EBD10 +#define RpAnimQuatConvertFromMatrix(qpQuat, mpMatrix) \ + RtQuatConvertFromMatrix(qpQuat, mpMatrix) + +#define RpAnimQuatConvertToMatrix(qpQuat,mpMatrix) \ + RtQuatUnitConvertToMatrix(qpQuat,mpMatrix) + + +#endif /* RTQUAT_H */ + diff --git a/source/game_sa/Replay.cpp b/source/game_sa/Replay.cpp index 9d7875075a..10a55904dd 100644 --- a/source/game_sa/Replay.cpp +++ b/source/game_sa/Replay.cpp @@ -228,7 +228,7 @@ void CReplay::RetrievePedAnimation(CPed* ped, const CStoredAnimationState& state } } - RpAnimBlendClumpRemoveAssociations(ped->m_pRwClump, ANIMATION_PARTIAL); + RpAnimBlendClumpRemoveAssociations(ped->m_pRwClump, ANIMATION_IS_PARTIAL); if (auto third = state[2]; third.m_nGroupId1 && third.m_nAnimId) { if (/*third.m_nGroupId1 >= 0 &&*/ third.m_nAnimId != 3u) { if (auto animBlock = CAnimManager::GetAssocGroups()[third.m_nGroupId2].m_AnimBlock; animBlock && animBlock->IsLoaded) { diff --git a/source/game_sa/RpHAnimBlendInterpFrame.h b/source/game_sa/RpHAnimBlendInterpFrame.h index 2827175424..3ff8836d74 100644 --- a/source/game_sa/RpHAnimBlendInterpFrame.h +++ b/source/game_sa/RpHAnimBlendInterpFrame.h @@ -8,9 +8,8 @@ #include "RenderWare.h" -// Fuck Rw Types struct RpHAnimBlendInterpFrame { - CQuaternion orientation; - CVector translation; + RtQuat q{0.f, 0.f, 0.f, 1.f}; + CVector t{0.f, 0.f, 0.f}; }; VALIDATE_SIZE(RpHAnimBlendInterpFrame, 0x1C); diff --git a/source/game_sa/Scripts/CommandParser/ReadArg.hpp b/source/game_sa/Scripts/CommandParser/ReadArg.hpp index 1be4080f07..0ad9e3625c 100644 --- a/source/game_sa/Scripts/CommandParser/ReadArg.hpp +++ b/source/game_sa/Scripts/CommandParser/ReadArg.hpp @@ -107,7 +107,7 @@ inline T Read(CRunningScript* S) { // This check here also means that all other branches must either return by-value or a pointer (not a refernce) if constexpr (std::is_reference_v) { const auto ptr = Read*>(S); - assert(ptr); + assert(ptr); // This assert is usually hit if the implementation defines an argument with a different type than the original. Eg.: `CVehicle&` instead of `CPed&`. return *ptr; } else if constexpr (std::is_same_v) { return { Read(S), Read(S), Read(S) }; diff --git a/source/game_sa/Scripts/Commands/Character.cpp b/source/game_sa/Scripts/Commands/Character.cpp index 105df831bf..ffb44889a1 100644 --- a/source/game_sa/Scripts/Commands/Character.cpp +++ b/source/game_sa/Scripts/Commands/Character.cpp @@ -433,11 +433,11 @@ auto LocateCharAnyMeansObject2D(CRunningScript& S, CPed& ped, CObject& obj, CVec return LocateCharEntity2D(S, ped, obj, radius, highlightArea, false, false); } -auto LocateCharOnFootObject2D(CRunningScript& S, CPed& ped, CVehicle& obj, CVector2D radius, bool highlightArea) { +auto LocateCharOnFootObject2D(CRunningScript& S, CPed& ped, CObject& obj, CVector2D radius, bool highlightArea) { return LocateCharEntity2D(S, ped, obj, radius, highlightArea, false, true); } -auto LocateCharInCarObject2D(CRunningScript& S, CPed& ped, CVehicle& obj, CVector2D radius, bool highlightArea) { +auto LocateCharInCarObject2D(CRunningScript& S, CPed& ped, CObject& obj, CVector2D radius, bool highlightArea) { return LocateCharEntity2D(S, ped, obj, radius, highlightArea, true, false); } @@ -1210,7 +1210,7 @@ auto IsCharPlayingAnim(CPed& ped, const char* animName) { // SET_CHAR_ANIM_PLAYING_FLAG auto SetCharAnimPlayingFlag(CPed& ped, const char* animName, bool started) { if (const auto anim = RpAnimBlendClumpGetAssociation(ped.m_pRwClump, animName)) { - anim->SetFlag(ANIMATION_STARTED, started); + anim->SetFlag(ANIMATION_IS_PLAYING, started); } } diff --git a/source/game_sa/Shadows.cpp b/source/game_sa/Shadows.cpp index 01afcf70e9..b32836c0c4 100644 --- a/source/game_sa/Shadows.cpp +++ b/source/game_sa/Shadows.cpp @@ -363,7 +363,7 @@ void CShadows::StoreShadowForPedObject(CPed* ped, float displacementX, float dis // But we really should fix this in a sensible way in the future. assert(ped->IsPed() || ped->IsObject()); - const auto bonePos = ped->GetBonePosition(BONE_NORMAL); + const auto bonePos = ped->GetBonePosition(BONE_ROOT); const auto& camPos = TheCamera.GetPosition(); const auto boneToCamDist2DSq = (bonePos - camPos).SquaredMagnitude2D(); @@ -407,7 +407,7 @@ void CShadows::StoreRealTimeShadow(CPhysical* physical, float displacementX, flo } const auto& camPos = TheCamera.GetPosition(); const auto shdwPos = physical->IsPed() - ? physical->AsPed()->GetBonePosition(BONE_NORMAL) + ? physical->AsPed()->GetBonePosition(BONE_ROOT) : physical->GetPosition(); const auto shdwToCamDist2DSq = (shdwPos - camPos).SquaredMagnitude2D(); diff --git a/source/game_sa/Streaming.cpp b/source/game_sa/Streaming.cpp index 4bbb6681b9..94089b1b2e 100644 --- a/source/game_sa/Streaming.cpp +++ b/source/game_sa/Streaming.cpp @@ -176,24 +176,24 @@ CLink* CStreaming::AddEntity(CEntity* entity) { case ENTITY_TYPE_PED: case ENTITY_TYPE_VEHICLE: return nullptr; - default: - break; } - CLink* link = ms_rwObjectInstances.Insert(entity); - if (!link) { - CLink* previousLink = ms_rwObjectInstances.usedListTail.prev; - for (; previousLink != &ms_rwObjectInstances.usedListHead; previousLink = previousLink->prev) { - CEntity* pentity = previousLink->data; - if (!pentity->m_bImBeingRendered && !pentity->m_bStreamingDontDelete) + auto l = ms_rwObjectInstances.Insert(entity); + if (!l) { // No more entries left + // Okay, try deleting something now + for (auto it = ms_rwObjectInstances.GetTailLink().prev; it != &ms_rwObjectInstances.GetHeadLink(); it = it->prev) { + const auto e = it->data; + + if (!e->m_bImBeingRendered && !e->m_bStreamingDontDelete) { + e->DeleteRwObject(); break; + } } - if (previousLink == &ms_rwObjectInstances.usedListHead) - return ms_rwObjectInstances.Insert(entity); - previousLink->data->DeleteRwObject(); - link = ms_rwObjectInstances.Insert(entity); + + // Try inserting again, should succeed now because we've deleted something (Ideally anyways) + VERIFY(l = ms_rwObjectInstances.Insert(entity)); } - return link; + return l; } // Returns file index @@ -719,8 +719,9 @@ void CStreaming::DeleteAllRwObjects() { next = it->GetNext(); auto* entity = reinterpret_cast(it->m_item); - if (!entity->m_bImBeingRendered && !entity->m_bStreamingDontDelete) + if (!entity->m_bImBeingRendered && !entity->m_bStreamingDontDelete) { entity->DeleteRwObject(); + } } }; @@ -746,35 +747,37 @@ bool CStreaming::DeleteLeastUsedEntityRwObject(bool bNotOnScreen, int32 streamin const float fCameraFarPlane = RwCameraGetFarClipPlane(TheCamera.m_pRwCamera); CPlayerPed* player = FindPlayerPed(); - for (auto prevLink = ms_rwObjectInstances.usedListTail.prev; prevLink != &ms_rwObjectInstances.usedListHead;) { - CEntity* entity = prevLink->data; - prevLink = prevLink->prev; // Has to be set here, because at the end of the loop it might be invalid. + for (auto it = ms_rwObjectInstances.GetTail(); it != &ms_rwObjectInstances.GetHeadLink();) { + const auto e = it->data; + it = it->prev; // Has to be set here, because at the end of the loop it might be invalid. - if (entity->m_bImBeingRendered || entity->m_bStreamingDontDelete) + if (e->m_bImBeingRendered || e->m_bStreamingDontDelete) { continue; + } + + const auto mi = CModelInfo::GetModelInfo(e->m_nModelIndex); - const auto mi = CModelInfo::GetModelInfo(entity->m_nModelIndex); float drawDistanceRadius = TheCamera.m_fLODDistMultiplier * mi->m_fDrawDistance; - if (entity->m_bIsBIGBuilding) + if (e->m_bIsBIGBuilding) drawDistanceRadius *= CRenderer::ms_lowLodDistScale; - const CVector entityPos = entity->m_pLod ? entity->m_pLod->GetPosition() : entity->GetPosition(); + const CVector entityPos = e->m_pLod ? e->m_pLod->GetPosition() : e->GetPosition(); const float fEntityToCamDist = DistanceBetweenPoints(TheCamera.GetPosition(), entityPos); - CEntity* const pEntityLastLod = entity->FindLastLOD(); + CEntity* const pEntityLastLod = e->FindLastLOD(); const float fModelRadius = mi->GetColModel()->GetBoundRadius(); if (ms_bLoadingScene || bNotOnScreen && !pEntityLastLod->GetIsOnScreen() - || !entity->IsInCurrentAreaOrBarberShopInterior() + || !e->IsInCurrentAreaOrBarberShopInterior() || drawDistanceRadius + 50.0f < fEntityToCamDist || fModelRadius + fCameraFarPlane < fEntityToCamDist ) { - CStreamingInfo& streamingInfo = GetInfo(entity->m_nModelIndex); + CStreamingInfo& streamingInfo = GetInfo(e->m_nModelIndex); if (streamingInfo.InList() && !streamingInfo.AreAnyFlagsSetOutOf(streamingFlags)) { - if (!player || player->bInVehicle || player->m_pContactEntity != entity) { - entity->DeleteRwObject(); - if (!CModelInfo::GetModelInfo(entity->m_nModelIndex)->m_nRefCount) { - RemoveModel(entity->m_nModelIndex); + if (!player || player->bInVehicle || player->m_pContactEntity != e) { + e->DeleteRwObject(); + if (!CModelInfo::GetModelInfo(e->m_nModelIndex)->m_nRefCount) { + RemoveModel(e->m_nModelIndex); return true; } } @@ -1395,7 +1398,7 @@ void CStreaming::PossiblyStreamCarOutAfterCreation(int32 modelId) { void CStreaming::RenderEntity(CLink* streamingLink) { if (streamingLink && streamingLink != ms_renderEntityLink) { streamingLink->Remove(); - ms_renderEntityLink->Insert(streamingLink); + streamingLink->Insert(ms_renderEntityLink); ms_renderEntityLink = streamingLink; } } @@ -3154,7 +3157,7 @@ bool CStreaming::StreamAmbulanceAndMedic(bool bStreamForAccident) { } if (CTheZones::m_CurrLevel == eLevelName::LEVEL_NAME_COUNTRY_SIDE || !bStreamForAccident) - return false; // Models for `LEVEL_NAME_COUNTRY_SIDE` are always loaded (I guess so, since they aren't set as deletable above) + return false; // Models for `LEVEL_NAME_COUNTRY_SIDE` are always loaded (I guess so, since they aren't set as deleteable above) // Load medic and ambulance model for current level int32 medicModel = ms_aDefaultMedicModel[CTheZones::m_CurrLevel]; diff --git a/source/game_sa/Tasks/TaskTypes/TaskComplexDie.cpp b/source/game_sa/Tasks/TaskTypes/TaskComplexDie.cpp index 76ed71a512..f185d3a796 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskComplexDie.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskComplexDie.cpp @@ -142,4 +142,4 @@ CTask* CTaskComplexDie::CreateFirstSubTask(CPed* ped) { } return new CTaskSimpleDie(m_nAnimGroup, m_nAnimID, m_fBlendDelta, m_fAnimSpeed); -} \ No newline at end of file +} diff --git a/source/game_sa/Tasks/TaskTypes/TaskComplexEnterCar.cpp b/source/game_sa/Tasks/TaskTypes/TaskComplexEnterCar.cpp index 289fdd2cd1..e7b63ce647 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskComplexEnterCar.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskComplexEnterCar.cpp @@ -308,6 +308,8 @@ CTask* CTaskComplexEnterCar::CreateNextSubTask(CPed* ped) { } case TASK_SIMPLE_CAR_SET_PED_IN_AS_PASSENGER: return C(TASK_FINISHED); + case TASK_NONE: + return C(TASK_FINISHED); case TASK_SIMPLE_CAR_QUICK_DRAG_PED_OUT: { // 0x63F2ED CCarEnterExit::MakeUndraggedPassengerPedsLeaveCar(m_Car, m_DraggedPed, ped); return C(TASK_SIMPLE_CAR_SET_PED_IN_AS_DRIVER); @@ -693,7 +695,7 @@ CTask* CTaskComplexEnterCar::CreateSubTask(eTaskType taskType, CPed* ped) { return new CTaskComplexFallAndGetUp{ IsTargetDoorOnTheLeft() ? ANIM_ID_KO_SPIN_L : ANIM_ID_KO_SPIN_R, ANIM_GROUP_DEFAULT, 2000 }; } case TASK_NONE: - return new CTaskSimpleLeaveGroup{}; // 0x63E0F8 - NOTE/TODO: Weird + return new CTaskSimpleNone{}; // 0x63E0F8 - NOTE/TODO: Weird case TASK_SIMPLE_STAND_STILL: return new CTaskSimpleStandStill{}; // 0x63E0CE default: diff --git a/source/game_sa/Tasks/TaskTypes/TaskComplexFacial.cpp b/source/game_sa/Tasks/TaskTypes/TaskComplexFacial.cpp index 2dd2df68e1..04bde9510e 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskComplexFacial.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskComplexFacial.cpp @@ -67,11 +67,13 @@ CTask* CTaskComplexFacial::ControlSubTask(CPed* ped) { if (!ped->IsAlive() && m_pSubTask->MakeAbortable(ped, ABORT_PRIORITY_IMMEDIATE)) { return new CTaskSimplePause{ 5'000 }; } + if (m_IsAborting && m_pSubTask->MakeAbortable(ped)) { m_IsAborting = false; m_TalkingLastFrame = false; return new CTaskSimplePause{ 5'000 }; } + if (RpAnimBlendClumpGetAssociation(ped->m_pRwClump, ANIM_ID_IDLE_CHAT)) { m_RequestB = eFacialExpression::NONE; m_TalkingLastFrame = true; @@ -79,10 +81,12 @@ CTask* CTaskComplexFacial::ControlSubTask(CPed* ped) { ? m_pSubTask : new CTaskSimpleFacial{ eFacialExpression::TALKING, 0 }; } + if (m_TalkingLastFrame && m_pSubTask->MakeAbortable(ped)) { m_TalkingLastFrame = false; return new CTaskSimplePause{ 5'000 }; } + if (m_RequestA != eFacialExpression::NONE) { // Check for a new request... const auto requestedFacialType = std::exchange(m_RequestA, eFacialExpression::NONE); if (const auto st = CTask::DynCast(m_pSubTask)) { // There's a sub-task already running, just change it @@ -93,5 +97,6 @@ CTask* CTaskComplexFacial::ControlSubTask(CPed* ped) { } return new CTaskSimpleFacial{ requestedFacialType, m_DurationA }; // Otherwise create the task } + return m_pSubTask; } diff --git a/source/game_sa/Tasks/TaskTypes/TaskComplexInAirAndLand.cpp b/source/game_sa/Tasks/TaskTypes/TaskComplexInAirAndLand.cpp index f72744707d..93ea900e03 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskComplexInAirAndLand.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskComplexInAirAndLand.cpp @@ -50,7 +50,7 @@ CTask* CTaskComplexInAirAndLand::CreateNextSubTask(CPed* ped) { if (m_bUsingFallGlide) { if (subTask->m_pAnim) { - subTask->m_pAnim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; + subTask->m_pAnim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; subTask->m_pAnim->m_BlendDelta = -8.0F; subTask->m_pAnim->SetFinishCallback(CDefaultAnimCallback::DefaultAnimCB, nullptr); subTask->m_pAnim = nullptr; diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleAnim.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleAnim.cpp index 2936b49218..3b865950ac 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleAnim.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleAnim.cpp @@ -22,7 +22,7 @@ CTaskSimpleAnim::~CTaskSimpleAnim() { return; m_pAnim->SetFinishCallback(CDefaultAnimCallback::DefaultAnimCB, nullptr); - m_pAnim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; + m_pAnim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; if (!m_bHoldLastFrame && m_pAnim->m_BlendAmount > 0.0f && m_pAnim->m_BlendDelta >= 0.0f @@ -50,7 +50,7 @@ bool CTaskSimpleAnim::MakeAbortable(CPed* ped, eAbortPriority priority, const CE if (scriptCommand->m_task) { if (scriptCommand->m_task->GetTaskType() == TASK_SIMPLE_NAMED_ANIM) { if (m_pAnim) - m_pAnim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; + m_pAnim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; bSkipBlend = true; } @@ -60,9 +60,9 @@ bool CTaskSimpleAnim::MakeAbortable(CPed* ped, eAbortPriority priority, const CE if (!bSkipBlend) { if (m_pAnim) { - m_pAnim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; + m_pAnim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; if (!m_bHoldLastFrame) { - if (m_pAnim->m_Flags & ANIMATION_PARTIAL) + if (m_pAnim->m_Flags & ANIMATION_IS_PARTIAL) m_pAnim->m_BlendDelta = fBlend; else CAnimManager::BlendAnimation(ped->m_pRwClump, ped->m_nAnimGroup, ANIM_ID_IDLE, -fBlend); diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleArrestPed.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleArrestPed.cpp index e888686041..badfef2ee1 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleArrestPed.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleArrestPed.cpp @@ -94,7 +94,7 @@ bool CTaskSimpleArrestPed::ProcessPed(CPed* ped) { // 0x68B7E0 void CTaskSimpleArrestPed::StartAnim(CPed* ped) { m_Assoc = CAnimManager::BlendAnimation(ped->m_pRwClump, ANIM_GROUP_DEFAULT, ANIM_ID_ARRESTGUN, 4.0f); - m_Assoc->SetFlag(ANIMATION_STARTED, true); + m_Assoc->SetFlag(ANIMATION_IS_PLAYING, true); m_Assoc->SetDeleteCallback(FinishAnimArrestPedCB, this); if (m_Ped->IsPlayer()) { diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleBeHit.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleBeHit.cpp index 24c8074f36..3e671ae875 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleBeHit.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleBeHit.cpp @@ -134,8 +134,8 @@ bool CTaskSimpleBeHit::MakeAbortable(CPed* ped, eAbortPriority priority, const C switch (priority) { case ABORT_PRIORITY_LEISURE: { if (m_Anim) { - if ((m_Anim->m_Flags & ANIMATION_STARTED) == 0) { - m_Anim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; + if ((m_Anim->m_Flags & ANIMATION_IS_PLAYING) == 0) { + m_Anim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; m_Anim->m_BlendDelta = -4.f; } } diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleCarGetIn.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleCarGetIn.cpp index 39e000a396..e87384fd00 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleCarGetIn.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleCarGetIn.cpp @@ -124,7 +124,7 @@ bool CTaskSimpleCarGetIn::ProcessPed(CPed* ped) { } // Apply move force for BMX like vehicles - if (m_veh && m_veh->IsSubBMX() && m_anim && (m_anim->m_Flags & ANIMATION_STARTED)) { + if (m_veh && m_veh->IsSubBMX() && m_anim && (m_anim->m_Flags & ANIMATION_IS_PLAYING)) { switch (m_anim->m_AnimId) { case ANIM_ID_CAR_GETIN_LHS_0: case ANIM_ID_CAR_GETIN_RHS_0: diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleClimb.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleClimb.cpp index 2cdd23c472..dedfca98f2 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleClimb.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleClimb.cpp @@ -162,7 +162,7 @@ bool CTaskSimpleClimb::ProcessPed(CPed* ped) { ped->m_vecMoveSpeed += vecClimbEntSpeed; - if (!(m_pAnim->m_Flags & ANIMATION_STARTED) || m_pAnim->m_AnimId == ANIM_ID_CLIMB_IDLE) { + if (!(m_pAnim->m_Flags & ANIMATION_IS_PLAYING) || m_pAnim->m_AnimId == ANIM_ID_CLIMB_IDLE) { m_nGetToPosCounter += (uint16)CTimer::GetTimeStepInMS(); if (m_nGetToPosCounter > 1000 || m_pAnim->m_AnimId == ANIM_ID_CLIMB_IDLE && m_nGetToPosCounter > 500) { m_bInvalidClimb = true; @@ -175,12 +175,12 @@ bool CTaskSimpleClimb::ProcessPed(CPed* ped) { ped->m_vecMoveSpeed = relPosn / CTimer::GetTimeStep(); ped->m_vecMoveSpeed += vecClimbEntSpeed; - if (!(m_pAnim->m_Flags & ANIMATION_STARTED) && m_nHeightForAnim == CLIMB_STANDUP) { + if (!(m_pAnim->m_Flags & ANIMATION_IS_PLAYING) && m_nHeightForAnim == CLIMB_STANDUP) { if (TestForVault(ped, &posn, fAngle)) { m_nHeightForAnim = CLIMB_VAULT; m_bChangeAnimation = true; } else - m_pAnim->m_Flags |= ANIMATION_STARTED; + m_pAnim->m_Flags |= ANIMATION_IS_PLAYING; } } } @@ -263,7 +263,7 @@ bool CTaskSimpleClimb::ProcessPed(CPed* ped) { if (pad && pad->GetExitVehicle()) { MakeAbortable(ped); - } else if (m_nHeightForPos != CLIMB_STANDUP && m_nHeightForPos != CLIMB_VAULT || !m_pAnim || !(m_pAnim->m_Flags & ANIMATION_STARTED)) { + } else if (m_nHeightForPos != CLIMB_STANDUP && m_nHeightForPos != CLIMB_VAULT || !m_pAnim || !(m_pAnim->m_Flags & ANIMATION_IS_PLAYING)) { if (m_nHeightForAnim == CLIMB_STANDUP && m_nHeightForPos < CLIMB_STANDUP && TestForVault(ped, &posn, fAngle)) { m_nHeightForAnim = CLIMB_VAULT; } @@ -606,7 +606,7 @@ void CTaskSimpleClimb::StartAnim(CPed* ped) { m_nHeightForAnim = CLIMB_STANDUP; m_nHeightForPos = CLIMB_STANDUP; m_pAnim = CAnimManager::BlendAnimation(ped->m_pRwClump, ANIM_GROUP_DEFAULT, ANIM_ID_CLIMB_STAND, 4.0f); - m_pAnim->m_Flags &= ~ANIMATION_STARTED; + m_pAnim->m_Flags &= ~ANIMATION_IS_PLAYING; } else { m_pAnim->SetDeleteCallback(CDefaultAnimCallback::DefaultAnimCB, nullptr); m_pAnim = CAnimManager::BlendAnimation(ped->m_pRwClump, ANIM_GROUP_DEFAULT, ANIM_ID_CLIMB_PULL, 1000.0f); @@ -656,7 +656,7 @@ void CTaskSimpleClimb::StartAnim(CPed* ped) { if (m_pAnim) { m_pAnim->SetDeleteCallback(DeleteAnimCB, this); - if (ped->m_pPlayerData && m_pAnim->m_Flags & ANIMATION_STARTED && + if (ped->m_pPlayerData && m_pAnim->m_Flags & ANIMATION_IS_PLAYING && (m_pAnim->m_AnimId == ANIM_ID_CLIMB_PULL || m_pAnim->m_AnimId == ANIM_ID_CLIMB_STAND || m_pAnim->m_AnimId == ANIM_ID_CLIMB_JUMP_B)) { m_pAnim->m_Speed = CStats::GetFatAndMuscleModifier(STAT_MOD_1); } diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleDie.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleDie.cpp index a501f94e52..1b406b0a8b 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleDie.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleDie.cpp @@ -53,12 +53,12 @@ void CTaskSimpleDie::StartAnim(CPed* ped) { m_animAssociation = CAnimManager::BlendAnimation(ped->m_pRwClump, m_animGroupId, m_animId, m_blendDelta); m_animAssociation->SetFinishCallback(FinishAnimDieCB, this); - m_animAssociation->m_Flags &= ANIMATION_TRANSLATE_X | ANIMATION_TRANSLATE_Y - | ANIMATION_MOVEMENT - | ANIMATION_PARTIAL - | ANIMATION_FREEZE_LAST_FRAME - | ANIMATION_LOOPED - | ANIMATION_STARTED; + m_animAssociation->m_Flags &= ANIMATION_CAN_EXTRACT_X_VELOCITY | ANIMATION_CAN_EXTRACT_VELOCITY + | ANIMATION_IS_SYNCRONISED + | ANIMATION_IS_PARTIAL + | ANIMATION_IS_BLEND_AUTO_REMOVE + | ANIMATION_IS_LOOPED + | ANIMATION_IS_PLAYING; if (m_animSpeed > 0.0f) m_animAssociation->m_Speed = m_animSpeed; diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleDuck.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleDuck.cpp index 539321038d..6c523b2caa 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleDuck.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleDuck.cpp @@ -44,7 +44,7 @@ CTaskSimpleDuck::CTaskSimpleDuck(const CTaskSimpleDuck& o) : CTaskSimpleDuck::~CTaskSimpleDuck() { if (m_DuckAnim) { m_DuckAnim->SetDefaultFinishCallback(); - if (m_DuckAnim->m_BlendAmount > 0.f && m_DuckAnim->m_BlendDelta >= 0.f && (m_DuckAnim->m_Flags & ANIMATION_PARTIAL)) { + if (m_DuckAnim->m_BlendAmount > 0.f && m_DuckAnim->m_BlendDelta >= 0.f && (m_DuckAnim->m_Flags & ANIMATION_IS_PARTIAL)) { m_DuckAnim->m_BlendDelta = -8.f; } } @@ -225,7 +225,7 @@ void CTaskSimpleDuck::SetMoveAnim(CPed* ped) { }else if (m_MoveCmd.y == 0.f) { //> 0x693AF4 - Blend out the animation out if no more movement if (m_MoveAnim) { if (IsCurrentMoveAnimGunCrouch()) { - m_MoveAnim->SetFlag(ANIMATION_STARTED, false); + m_MoveAnim->SetFlag(ANIMATION_IS_PLAYING, false); m_MoveAnim->SetBlendDelta(-4.f); } } @@ -263,7 +263,7 @@ bool CTaskSimpleDuck::MakeAbortable(CPed* ped, eAbortPriority priority, CEvent c case ABORT_PRIORITY_IMMEDIATE: { // 0x69210C // Replace/blend duck anim with idle anim if (m_DuckAnim) { - if (m_DuckAnim->m_Flags & ANIMATION_PARTIAL) { + if (m_DuckAnim->m_Flags & ANIMATION_IS_PARTIAL) { m_DuckAnim->m_BlendDelta = -1000.f; } else { CAnimManager::BlendAnimation(ped->m_pRwClump, ped->m_nAnimGroup, ANIM_ID_IDLE, 1000.f); @@ -308,7 +308,7 @@ bool CTaskSimpleDuck::MakeAbortable(CPed* ped, eAbortPriority priority, CEvent c if (m_DuckAnim) { if (m_DuckAnim->m_BlendAmount > 0.f && m_DuckAnim->m_BlendDelta >= 0.f) { - if (m_DuckAnim->m_Flags & ANIMATION_PARTIAL) { + if (m_DuckAnim->m_Flags & ANIMATION_IS_PARTIAL) { m_DuckAnim->m_BlendDelta = blendDelta; } CAnimManager::BlendAnimation(ped->m_pRwClump, ped->m_nAnimGroup, ANIM_ID_IDLE, -blendDelta); @@ -325,7 +325,7 @@ bool CTaskSimpleDuck::MakeAbortable(CPed* ped, eAbortPriority priority, CEvent c if (m_MoveAnim->m_BlendAmount > 0.f && m_MoveAnim->m_BlendDelta >= 0.f) { if (priority == ABORT_PRIORITY_URGENT || notsa::contains({ ANIM_ID_GUNCROUCHFWD, ANIM_ID_GUNCROUCHBWD }, m_MoveAnim->GetAnimId())) { m_MoveAnim->m_BlendDelta = blendDelta; - m_MoveAnim->SetFlag(ANIMATION_STARTED, false); + m_MoveAnim->SetFlag(ANIMATION_IS_PLAYING, false); } } @@ -417,7 +417,7 @@ bool CTaskSimpleDuck::ProcessPed(CPed* ped) { if (const auto weaponCruchAnim = RpAnimBlendClumpGetAssociation(ped->m_pRwClump, ANIM_ID_WEAPON_CROUCH)) { // 0x69454F if (weaponCruchAnim->m_BlendAmount > 0 && weaponCruchAnim->m_BlendDelta >= 0.f) { - if (weaponCruchAnim->m_Flags & ANIMATION_PARTIAL) { + if (weaponCruchAnim->m_Flags & ANIMATION_IS_PARTIAL) { weaponCruchAnim->m_BlendDelta = -4.f; } else { CAnimManager::BlendAnimation(ped->m_pRwClump, ANIM_GROUP_DEFAULT, ANIM_ID_IDLE, 4.f); diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleEvasiveStep.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleEvasiveStep.cpp index 8f0c60bc13..08fcc145b7 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleEvasiveStep.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleEvasiveStep.cpp @@ -54,14 +54,14 @@ bool CTaskSimpleEvasiveStep::ProcessPed(CPed* ped) { // 0x655EA0 void CTaskSimpleEvasiveStep::StartAnim(CPed* ped) { m_Assoc = CAnimManager::BlendAnimation(ped->m_pRwClump, ANIM_GROUP_DEFAULT, ANIM_ID_EV_STEP, 8.0f); - m_Assoc->SetFlag(ANIMATION_FREEZE_LAST_FRAME, false); + m_Assoc->SetFlag(ANIMATION_IS_BLEND_AUTO_REMOVE, false); m_Assoc->SetFinishCallback(FinishAnimEvasiveStepCB, this); } // 0x653290 void CTaskSimpleEvasiveStep::FinishAnimEvasiveStepCB(CAnimBlendAssociation* assoc, void* data) { auto task = static_cast(data); - task->m_Assoc->SetFlag(ANIMATION_FREEZE_LAST_FRAME, true); + task->m_Assoc->SetFlag(ANIMATION_IS_BLEND_AUTO_REMOVE, true); if (task->m_Assoc->m_BlendDelta >= 0.0f) { task->m_Assoc->m_BlendDelta = -4.0f; } diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleFacial.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleFacial.cpp index 1091e68bd5..53d99e7a20 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleFacial.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleFacial.cpp @@ -61,7 +61,7 @@ bool CTaskSimpleFacial::ProcessPed(CPed* ped) { return false; } if (anim) { - anim->SetFlag(ANIMATION_FREEZE_LAST_FRAME, true); + anim->SetFlag(ANIMATION_IS_BLEND_AUTO_REMOVE, true); anim->SetBlendDelta(-4.0f); } return true; diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleFall.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleFall.cpp index 9b0bd8bd4f..02d2f9b5c8 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleFall.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleFall.cpp @@ -161,15 +161,15 @@ bool CTaskSimpleFall::StartAnim(CPed* ped) { m_pAnim->Start(0.0F); m_pAnim->SetBlend(0.0F, 8.0F); - m_pAnim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; - m_pAnim->m_Flags &= ~ANIMATION_UNLOCK_LAST_FRAME; + m_pAnim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; + m_pAnim->m_Flags &= ~ANIMATION_IS_FINISH_AUTO_REMOVE; m_pAnim->SetFinishCallback(CTaskSimpleFall::FinishFallAnimCB, this); } else { m_pAnim = CAnimManager::BlendAnimation(ped->m_pRwClump, m_nAnimGroup, m_nAnimId, 8.0F); - m_pAnim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; - m_pAnim->m_Flags &= ~ANIMATION_UNLOCK_LAST_FRAME; + m_pAnim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; + m_pAnim->m_Flags &= ~ANIMATION_IS_FINISH_AUTO_REMOVE; m_pAnim->SetFinishCallback(CTaskSimpleFall::FinishFallAnimCB, this); if (m_nAnimId == ANIM_ID_BIKE_FALLR) m_pAnim->SetCurrentTime(0.4F); @@ -188,7 +188,7 @@ void CTaskSimpleFall::ProcessFall(CPed* ped) ) { CAnimBlendAssociation* anim; - auto pFirstAnim = RpAnimBlendClumpGetFirstAssociation(ped->m_pRwClump, ANIMATION_PARTIAL); + auto pFirstAnim = RpAnimBlendClumpGetFirstAssociation(ped->m_pRwClump, ANIMATION_IS_PARTIAL); if (pFirstAnim && (pFirstAnim->m_AnimId == ANIM_ID_FALL_BACK || pFirstAnim->m_AnimId == ANIM_ID_FALL_FRONT)) anim = pFirstAnim; @@ -230,8 +230,8 @@ void CTaskSimpleFall::ProcessFall(CPed* ped) } else { - auto firstAnim = RpAnimBlendClumpGetFirstAssociation(ped->m_pRwClump, ANIMATION_PARTIAL); - if (firstAnim && !(firstAnim->m_Flags & ANIMATION_STARTED)) + auto firstAnim = RpAnimBlendClumpGetFirstAssociation(ped->m_pRwClump, ANIMATION_IS_PARTIAL); + if (firstAnim && !(firstAnim->m_Flags & ANIMATION_IS_PLAYING)) ped->bKnockedUpIntoAir = false; } } diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleGoToPointFine.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleGoToPointFine.cpp index 120eb54be5..bd37bcc863 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleGoToPointFine.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleGoToPointFine.cpp @@ -127,7 +127,7 @@ void CTaskSimpleGoToPointFine::SetBlendedMoveAnim(CPed* ped) { runAnimAssoc->m_BlendAmount = 0.0f; runAnimAssoc->m_Speed = 1.0f; } - runAnimAssoc->m_Flags |= ANIMATION_STARTED; + runAnimAssoc->m_Flags |= ANIMATION_IS_PLAYING; runAnimAssoc->m_BlendDelta = 0.0f; runAnimAssoc->m_BlendAmount = 3.0f - m_fMoveRatio; if (!sprintAnimAssoc) { @@ -135,7 +135,7 @@ void CTaskSimpleGoToPointFine::SetBlendedMoveAnim(CPed* ped) { sprintAnimAssoc->m_BlendAmount = 0.0f; sprintAnimAssoc->m_Speed = 1.0f; } - sprintAnimAssoc->m_Flags |= ANIMATION_STARTED; + sprintAnimAssoc->m_Flags |= ANIMATION_IS_PLAYING; sprintAnimAssoc->m_BlendDelta = 0.0f; moveState = PEDMOVE_SPRINT; sprintAnimAssoc->m_BlendAmount = m_fMoveRatio - 2.0f; @@ -145,7 +145,7 @@ void CTaskSimpleGoToPointFine::SetBlendedMoveAnim(CPed* ped) { walkAnimAssoc->m_BlendAmount = 0.0f; walkAnimAssoc->m_Speed = 1.0f; } - walkAnimAssoc->m_Flags |= ANIMATION_STARTED; + walkAnimAssoc->m_Flags |= ANIMATION_IS_PLAYING; walkAnimAssoc->m_BlendDelta = 0.0f; walkAnimAssoc->m_BlendAmount = 2.0f - m_fMoveRatio; if (!runAnimAssoc) { @@ -153,7 +153,7 @@ void CTaskSimpleGoToPointFine::SetBlendedMoveAnim(CPed* ped) { runAnimAssoc->m_BlendAmount = 0.0f; runAnimAssoc->m_Speed = 1.0f; } - runAnimAssoc->m_Flags |= ANIMATION_STARTED; + runAnimAssoc->m_Flags |= ANIMATION_IS_PLAYING; runAnimAssoc->m_BlendDelta = 0.0f; runAnimAssoc->m_BlendAmount = m_fMoveRatio - 1.0f; delete sprintAnimAssoc; @@ -165,7 +165,7 @@ void CTaskSimpleGoToPointFine::SetBlendedMoveAnim(CPed* ped) { walkAnimAssoc->m_BlendAmount = 0.0f; walkAnimAssoc->m_Speed = 1.0f; } - walkAnimAssoc->m_Flags |= ANIMATION_STARTED; + walkAnimAssoc->m_Flags |= ANIMATION_IS_PLAYING; walkAnimAssoc->m_BlendAmount = 1.0f; walkAnimAssoc->m_BlendDelta = 0.0f; delete runAnimAssoc; diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleHoldEntity.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleHoldEntity.cpp index 97f2a76505..fa3bb98b38 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleHoldEntity.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleHoldEntity.cpp @@ -303,7 +303,7 @@ bool CTaskSimpleHoldEntity::SetPedPosition(CPed* ped) { if (m_bBoneFlags & HOLD_ENTITY_UPDATE_TRANSLATION_ONLY) { CVector entityToHoldPos = m_vecPosition; RpHAnimHierarchy* pHAnimHierarchy = GetAnimHierarchyFromSkinClump(ped->m_pRwClump); - int32 animIndex = RpHAnimIDGetIndex(pHAnimHierarchy, ped->m_apBones[m_nBoneFrameId]->m_nNodeId); + int32 animIndex = RpHAnimIDGetIndex(pHAnimHierarchy, ped->m_apBones[m_nBoneFrameId]->BoneTag); RwMatrix* pBoneMatrix = &RpHAnimHierarchyGetMatrixArray(pHAnimHierarchy)[animIndex]; RwV3dTransformPoints((RwV3d*)&entityToHoldPos, (RwV3d*)&entityToHoldPos, 1, pBoneMatrix); m_pEntityToHold->GetMatrix().UpdateMatrix(pBoneMatrix); @@ -312,7 +312,7 @@ bool CTaskSimpleHoldEntity::SetPedPosition(CPed* ped) { else { CVector entityToHoldPos = ped->GetMatrix().TransformVector(m_vecPosition); RpHAnimHierarchy* pHAnimHierarchy = GetAnimHierarchyFromSkinClump(ped->m_pRwClump); - int32 animIndex = RpHAnimIDGetIndex(pHAnimHierarchy, ped->m_apBones[m_nBoneFrameId]->m_nNodeId); + int32 animIndex = RpHAnimIDGetIndex(pHAnimHierarchy, ped->m_apBones[m_nBoneFrameId]->BoneTag); RwMatrix* pBoneMatrix = RpHAnimHierarchyGetMatrixArray(pHAnimHierarchy); entityToHoldPos += *RwMatrixGetPos(&pBoneMatrix[animIndex]); CMatrix rotationMatrix(ped->GetMatrix()); @@ -381,7 +381,7 @@ void CTaskSimpleHoldEntity::FinishAnimHoldEntityCB(CAnimBlendAssociation* animAs // 0x692FF0 void CTaskSimpleHoldEntity::StartAnim(CPed* ped) { if (m_pAnimBlendHierarchy) { - m_animFlags |= ANIMATION_ADD_TO_BLEND | ANIMATION_FREEZE_LAST_FRAME | ANIMATION_PARTIAL; + m_animFlags |= ANIMATION_DONT_ADD_TO_PARTIAL_BLEND | ANIMATION_IS_BLEND_AUTO_REMOVE | ANIMATION_IS_PARTIAL; m_pAnimBlendAssociation = CAnimManager::BlendAnimation(ped->m_pRwClump, m_pAnimBlendHierarchy, m_animFlags, 4.0f); } else { if (m_nAnimGroupId && !m_pAnimBlock) { @@ -398,9 +398,9 @@ void CTaskSimpleHoldEntity::StartAnim(CPed* ped) { m_pAnimBlock = animBlock; } m_pAnimBlendAssociation = CAnimManager::BlendAnimation(ped->m_pRwClump, m_nAnimGroupId, m_nAnimId, 4.0f); - m_pAnimBlendAssociation->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; + m_pAnimBlendAssociation->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; if (GetTaskType() == TASK_SIMPLE_HOLD_ENTITY) { - m_pAnimBlendAssociation->m_Flags |= ANIMATION_ADD_TO_BLEND; + m_pAnimBlendAssociation->m_Flags |= ANIMATION_DONT_ADD_TO_PARTIAL_BLEND; } } diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleIKLookAt.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleIKLookAt.cpp index e499a70992..ea2424b311 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleIKLookAt.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleIKLookAt.cpp @@ -21,7 +21,7 @@ void CTaskSimpleIKLookAt::InjectHooks() { CTaskSimpleIKLookAt::CTaskSimpleIKLookAt(Const char* name, CEntity* lookAtEntity, int32 time, eBoneTag pedBoneID, CVector lookAtOffset, bool useTorso, float speed, int32 blendTime, int8 priority ) - : CTaskSimpleIKChain{name, BONE_HEAD, { 0.f, 0.05f, 0.f }, BONE_NORMAL, lookAtEntity, pedBoneID, lookAtOffset, speed, time, blendTime} + : CTaskSimpleIKChain{name, BONE_HEAD, { 0.f, 0.05f, 0.f }, BONE_ROOT, lookAtEntity, pedBoneID, lookAtOffset, speed, time, blendTime} { m_nPriority = priority; m_bUseTorso = useTorso; diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleIKPointArm.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleIKPointArm.cpp index d1440592c6..11ac9215b8 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleIKPointArm.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleIKPointArm.cpp @@ -21,7 +21,7 @@ CTaskSimpleIKPointArm::CTaskSimpleIKPointArm(const char* purpose, eIKArm arm, CE purpose, IKArmToBoneTag(arm), CVector{ 0.f, 0.05f, 0.f }, - BONE_NORMAL, + BONE_ROOT, targetEntity, offsetBoneTag, offsetPos, diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleInAir.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleInAir.cpp index 11106789ac..40dfbf4f03 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleInAir.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleInAir.cpp @@ -171,7 +171,7 @@ bool CTaskSimpleInAir::ProcessPed(CPed* ped) if (m_pAnim && m_bUsingFallGlide) { m_pAnim->m_BlendDelta = -1000.0F; - m_pAnim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; + m_pAnim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; m_pAnim->SetFinishCallback(CDefaultAnimCallback::DefaultAnimCB, nullptr); m_pAnim = nullptr; } @@ -217,7 +217,7 @@ bool CTaskSimpleInAir::MakeAbortable(CPed* ped, eAbortPriority priority, const C if (m_pAnim) { m_pAnim->m_BlendDelta = -8.0F; - m_pAnim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; + m_pAnim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; m_pAnim->SetFinishCallback(CDefaultAnimCallback::DefaultAnimCB, nullptr); m_pAnim = nullptr; } diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleJetPack.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleJetPack.cpp index a2ce672161..f364389f66 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleJetPack.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleJetPack.cpp @@ -431,10 +431,10 @@ void CTaskSimpleJetPack::ApplyRollAndPitch(CPed* ped) { if (ped->bIsStanding || ped->bWasStanding) { return; } - for (const auto nodeId : { PED_NODE_LEFT_LEG, PED_NODE_RIGHT_LEG }) { - const auto orientation = ped->m_apBones[nodeId]->GetFrameOrientation().AsRtQuat(); - RtQuatRotate(orientation, &CPedIK::ZaxisIK, RWRAD2DEG(m_LegSwingFwd), rwCOMBINEPOSTCONCAT); - RtQuatRotate(orientation, &CPedIK::YaxisIK, RWRAD2DEG(m_LegSwingSide), rwCOMBINEPOSTCONCAT); + for (const auto nodeId : { PED_NODE_LEFT_LEG, PED_NODE_RIGHT_LEG }) { // Rotate legs according to current swing values + const auto q = &ped->m_apBones[nodeId]->KeyFrame->q; + RtQuatRotate(q, &CPedIK::ZaxisIK, RWRAD2DEG(m_LegSwingFwd), rwCOMBINEPOSTCONCAT); + RtQuatRotate(q, &CPedIK::YaxisIK, RWRAD2DEG(m_LegSwingSide), rwCOMBINEPOSTCONCAT); } ped->bUpdateMatricesRequired = true; } diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleJump.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleJump.cpp index 4f5b7d6e4e..5070d06615 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleJump.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleJump.cpp @@ -51,7 +51,7 @@ CTask* CTaskSimpleJump::Clone() const { // 0x679B60 bool CTaskSimpleJump::MakeAbortable(CPed* ped, eAbortPriority priority, const CEvent* event) { if (m_pAnim) { - m_pAnim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; + m_pAnim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; m_pAnim->m_BlendDelta = -4.0F; } @@ -161,7 +161,7 @@ void CTaskSimpleJump::Launch(CPed* ped) { if (!m_pClimbEntity) { if (m_bClimbJump) { auto anim = CAnimManager::BlendAnimation(ped->m_pRwClump, ANIM_GROUP_DEFAULT, ANIM_ID_CLIMB_JUMP, 8.0F); - anim->m_Flags |= ANIMATION_UNLOCK_LAST_FRAME; + anim->m_Flags |= ANIMATION_IS_FINISH_AUTO_REMOVE; } else CAnimManager::BlendAnimation(ped->m_pRwClump, ANIM_GROUP_DEFAULT, ANIM_ID_JUMP_GLIDE, 8.0F); } @@ -169,7 +169,7 @@ void CTaskSimpleJump::Launch(CPed* ped) { if (ped->bDoBloodyFootprints && CLocalisation::Blood()) { auto hier = GetAnimHierarchyFromSkinClump(ped->m_pRwClump); CVector v; - RwV3dTransformPoints(&v, &v, 1, &RpHAnimHierarchyGetMatrixArray(hier)[RpHAnimIDGetIndex(hier, ped->m_apBones[PED_NODE_LEFT_FOOT]->m_nNodeId)]); + RwV3dTransformPoints(&v, &v, 1, &RpHAnimHierarchyGetMatrixArray(hier)[RpHAnimIDGetIndex(hier, ped->m_apBones[PED_NODE_LEFT_FOOT]->BoneTag)]); CVector v1 = ped->GetForward() * 0.2F; v += v1 + CVector(0.0F, 0.0F, -0.1F); @@ -177,7 +177,7 @@ void CTaskSimpleJump::Launch(CPed* ped) { 3000, 1.0F); v.Set(0.0F, 0.0F, 0.0F); - RwV3dTransformPoints(&v, &v, 1, &RpHAnimHierarchyGetMatrixArray(hier)[RpHAnimIDGetIndex(hier, ped->m_apBones[PED_NODE_RIGHT_FOOT]->m_nNodeId)]); + RwV3dTransformPoints(&v, &v, 1, &RpHAnimHierarchyGetMatrixArray(hier)[RpHAnimIDGetIndex(hier, ped->m_apBones[PED_NODE_RIGHT_FOOT]->BoneTag)]); v += v1 + CVector(0.0F, 0.0F, -0.1F); CShadows::AddPermanentShadow(SHADOW_DEFAULT, gpBloodPoolTex, &v, v1.x * 0.26F, v1.y * 0.26F, ped->GetForward().x * 0.14F, ped->GetForward().y * 0.14F, 255, 255, 0, 0, 4.0F, 3000, 1.0F); diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleLand.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleLand.cpp index 9554d0b0d3..4ce552791d 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleLand.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleLand.cpp @@ -89,7 +89,7 @@ bool CTaskSimpleLand::MakeAbortable(CPed* ped, eAbortPriority priority, const CE if (m_pAnim) { m_pAnim->m_BlendDelta = -1000.0F; - m_pAnim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; + m_pAnim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; m_pAnim->SetFinishCallback(CDefaultAnimCallback::DefaultAnimCB, nullptr); // doesn't make sense, since there is only one callback function m_pAnim->SetDeleteCallback(CDefaultAnimCallback::DefaultAnimCB, nullptr); m_pAnim = nullptr; diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimplePlayerOnFoot.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimplePlayerOnFoot.cpp index d064547c97..4457bb6572 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimplePlayerOnFoot.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimplePlayerOnFoot.cpp @@ -722,7 +722,7 @@ void CTaskSimplePlayerOnFoot::PlayIdleAnimations(CPlayerPed* player) { if (!(player->bIsLooking && player->bIsRestoringLook) && touchTimeDelta - gLastTouchTimeDelta > 20000) { // Check if the player already has any anims from the idle anim block playing already bool anyIdleAnims = false; - RpAnimBlendClumpIterateAssociations(player->m_pRwClump, [&](CAnimBlendAssociation* a) { + RpAnimBlendClumpForEachAssociation(player->m_pRwClump, [&](CAnimBlendAssociation* a) { if (CAnimManager::IsAnimInBlock(a->GetHier(), playerIdlesAnimBlock)) { anyIdleAnims = true; return false; @@ -867,7 +867,7 @@ void CTaskSimplePlayerOnFoot::PlayerControlDucked(CPlayerPed* player) { return; } auto pNewAnimation = CAnimManager::BlendAnimation(player->m_pRwClump, player->m_nAnimGroup, ANIM_ID_RUN, gDuckAnimBlendData); - pNewAnimation->m_Flags |= ANIMATION_STARTED; + pNewAnimation->m_Flags |= ANIMATION_IS_PLAYING; player->m_pPlayerData->m_fMoveBlendRatio = 1.5f; pedMoveState = PEDMOVE_RUN; } else { @@ -875,7 +875,7 @@ void CTaskSimplePlayerOnFoot::PlayerControlDucked(CPlayerPed* player) { return; } auto pNewAnimation = CAnimManager::BlendAnimation(player->m_pRwClump, player->m_nAnimGroup, ANIM_ID_WALK, gDuckAnimBlendData); - pNewAnimation->m_Flags |= ANIMATION_STARTED; + pNewAnimation->m_Flags |= ANIMATION_IS_PLAYING; player->m_pPlayerData->m_fMoveBlendRatio = 1.5f; pedMoveState = PEDMOVE_WALK; } @@ -883,7 +883,7 @@ void CTaskSimplePlayerOnFoot::PlayerControlDucked(CPlayerPed* player) { player->m_nSwimmingMoveState = pedMoveState; } else if (pedMoveBlendRatio > 0.5f) { auto pNewAnimation = CAnimManager::BlendAnimation(player->m_pRwClump, ANIM_GROUP_DEFAULT, ANIM_ID_GUNMOVE_FWD, gDuckAnimBlendData); - pNewAnimation->m_Flags |= ANIMATION_STARTED; + pNewAnimation->m_Flags |= ANIMATION_IS_PLAYING; player->m_pPlayerData->m_fMoveBlendRatio = 1.0f; moveSpeed.x = 1.0f; moveSpeed.y = 0.0f; diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleShakeFist.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleShakeFist.cpp index 75f5368a3c..efa7a712a3 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleShakeFist.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleShakeFist.cpp @@ -41,8 +41,8 @@ void CTaskSimpleShakeFist::FinishAnimShakeFistCB(CAnimBlendAssociation*, void* d // 0x692DF0 void CTaskSimpleShakeFist::StartAnim(CPed* ped) { m_anim = CAnimManager::BlendAnimation(ped->m_pRwClump, ANIM_GROUP_DEFAULT, ANIM_ID_FUCKU, 4.f); - m_anim->SetFlag(ANIMATION_UNLOCK_LAST_FRAME); - m_anim->SetFlag(ANIMATION_FREEZE_LAST_FRAME); + m_anim->SetFlag(ANIMATION_IS_FINISH_AUTO_REMOVE); + m_anim->SetFlag(ANIMATION_IS_BLEND_AUTO_REMOVE); m_anim->SetDeleteCallback(FinishAnimShakeFistCB, this); } diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleSitDown.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleSitDown.cpp index f675321934..cd56619303 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleSitDown.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleSitDown.cpp @@ -78,7 +78,7 @@ bool CTaskSimpleSitDown::MakeAbortable(CPed* ped, eAbortPriority priority, CEven } case ABORT_PRIORITY_LEISURE: { if (m_anim) { - m_anim->SetFlag(ANIMATION_FREEZE_LAST_FRAME, true); + m_anim->SetFlag(ANIMATION_IS_BLEND_AUTO_REMOVE, true); m_anim->m_BlendDelta = -4.f; } return false; diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleStandUp.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleStandUp.cpp index dfbdd3a2a9..6a63b90e3d 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleStandUp.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleStandUp.cpp @@ -74,7 +74,7 @@ bool CTaskSimpleStandUp::MakeAbortable(CPed*, eAbortPriority priority, CEvent co } case ABORT_PRIORITY_LEISURE: { if (m_anim) { - m_anim->m_Flags |= ANIMATION_FREEZE_LAST_FRAME; + m_anim->m_Flags |= ANIMATION_IS_BLEND_AUTO_REMOVE; m_anim->m_BlendDelta = -4.f; } return false; diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleSwim.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleSwim.cpp index 3edccc4d29..60b138156b 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleSwim.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleSwim.cpp @@ -128,7 +128,7 @@ bool CTaskSimpleSwim::ProcessPed(CPed* ped) { ped->m_nMoveState = PEDMOVE_STILL; if (assoc) { if (assoc->m_AnimId == ANIM_ID_CLIMB_JUMP) - assoc->m_Flags |= ANIMATION_UNLOCK_LAST_FRAME; + assoc->m_Flags |= ANIMATION_IS_FINISH_AUTO_REMOVE; else assoc->m_BlendDelta = -4.0f; @@ -241,7 +241,7 @@ void CTaskSimpleSwim::ProcessSwimAnims(CPed* ped) { m_bFinishedBlending = true; } } - RpAnimBlendClumpSetBlendDeltas(player->m_pRwClump, 0x10, -8.0f); // todo: ANIMATION_PARTIAL ? + RpAnimBlendClumpSetBlendDeltas(player->m_pRwClump, 0x10, -8.0f); // todo: ANIMATION_IS_PARTIAL ? FxSystem_c::SafeKillAndClear(player->GetActiveWeapon().m_FxSystem); // Removes fire or something in water if (player->IsPlayer() && !m_nSwimState) { @@ -400,7 +400,7 @@ void CTaskSimpleSwim::ProcessSwimAnims(CPed* ped) { if (assocJumpOut) { if (assocJumpOut->m_BlendHier->m_fTotalTime / 4.0f <= assocJumpOut->m_TimeStep + assocJumpOut->m_CurrentTime) { assocJumpOut = CAnimManager::BlendAnimation(player->m_pRwClump, ANIM_GROUP_DEFAULT, ANIM_ID_CLIMB_JUMP, 8.0f); - assocJumpOut->m_Flags |= ANIMATION_UNLOCK_LAST_FRAME; + assocJumpOut->m_Flags |= ANIMATION_IS_FINISH_AUTO_REMOVE; m_AnimID = ANIM_ID_CLIMB_JUMP; } break; diff --git a/source/game_sa/Tasks/TaskTypes/TaskSimpleThrowProjectile.cpp b/source/game_sa/Tasks/TaskTypes/TaskSimpleThrowProjectile.cpp index 5277db4186..062f35b22c 100644 --- a/source/game_sa/Tasks/TaskTypes/TaskSimpleThrowProjectile.cpp +++ b/source/game_sa/Tasks/TaskTypes/TaskSimpleThrowProjectile.cpp @@ -143,7 +143,7 @@ bool CTaskSimpleThrowProjectile::ProcessPed(CPed* ped) { const auto animFireTime = m_Anim->m_AnimId == ANIM_ID_GRENADE_WEAPON_THROW ? wi->m_fAnimLoop2Fire : wi->m_fAnimLoopFire; - if (animFireTime < m_Anim->m_CurrentTime && (m_Anim->m_CurrentTime - m_Anim->m_TimeStep) <= animFireTime && m_Anim->IsRunning()) { + if (animFireTime < m_Anim->m_CurrentTime && (m_Anim->m_CurrentTime - m_Anim->m_TimeStep) <= animFireTime && m_Anim->IsPlaying()) { if (ped->IsPlayer()) { if (!m_bButtonReleased) { m_ButtonCounter = CTimer::GetTimeInMS() - m_ButtonCounter; @@ -157,7 +157,7 @@ bool CTaskSimpleThrowProjectile::ProcessPed(CPed* ped) { } CVector firePos; - RwV3dTransformPoint(&firePos, &wi->m_vecFireOffset, &ped->GetBoneMatrix((eBoneTag)ped->m_apBones[PED_NODE_RIGHT_HAND]->m_nNodeId)); + RwV3dTransformPoint(&firePos, &wi->m_vecFireOffset, &ped->GetBoneMatrix((eBoneTag)ped->m_apBones[PED_NODE_RIGHT_HAND]->BoneTag)); ped->GetActiveWeapon().Fire(ped, &firePos, &firePos, nullptr, m_TargetPos.IsZero() ? nullptr : &m_TargetPos, nullptr); } } diff --git a/source/game_sa/VisibilityPlugins.cpp b/source/game_sa/VisibilityPlugins.cpp index 89934b1525..c2d9459997 100644 --- a/source/game_sa/VisibilityPlugins.cpp +++ b/source/game_sa/VisibilityPlugins.cpp @@ -1007,7 +1007,7 @@ void CVisibilityPlugins::RenderWeaponPedsForPC() { int32 animIDIndex = RpHAnimIDGetIndex(pRpAnimHierarchy, boneID); RwMatrix* pRightHandMatrix = &RpHAnimHierarchyGetMatrixArray(pRpAnimHierarchy)[animIDIndex]; { // todo: NOTSA - if (boneID == BONE_NORMAL) { + if (boneID == BONE_ROOT) { pRightHandMatrix = ped->GetModellingMatrix(); } } diff --git a/source/game_sa/Weapon.cpp b/source/game_sa/Weapon.cpp index 8c6be5b0e0..e2c0835de2 100644 --- a/source/game_sa/Weapon.cpp +++ b/source/game_sa/Weapon.cpp @@ -185,7 +185,7 @@ bool CWeapon::GenerateDamageEvent(CPed* victim, CEntity* creator, eWeaponType we RpAnimBlendClumpGetFirstAssociation(victim->m_pRwClump, ANIMATION_800) ? ANIM_ID_FLOOR_HIT_F : ANIM_ID_FLOOR_HIT ); if (floorHitAnim) { - floorHitAnim->SetFlag(ANIMATION_UNLOCK_LAST_FRAME, false); + floorHitAnim->SetFlag(ANIMATION_IS_FINISH_AUTO_REMOVE, false); floorHitAnim->Start(); } return true; @@ -247,7 +247,7 @@ bool CWeapon::GenerateDamageEvent(CPed* victim, CEntity* creator, eWeaponType we eventDmg.m_fAnimBlend ); a->m_Speed = eventDmg.m_fAnimSpeed; - a->SetFlag(ANIMATION_STARTED); + a->SetFlag(ANIMATION_IS_PLAYING); break; } } diff --git a/source/toolsmenu/UIRenderer.cpp b/source/toolsmenu/UIRenderer.cpp index f926ed99e1..e02919183e 100644 --- a/source/toolsmenu/UIRenderer.cpp +++ b/source/toolsmenu/UIRenderer.cpp @@ -188,7 +188,14 @@ void UIRenderer::DebugCode() { CMessages::AddBigMessage("PRESS ~k~~PED_ANSWER_PHONE~ TO FUCK", 1000, eMessageStyle::STYLE_BOTTOM_RIGHT); } if (pad->IsStandardKeyJustPressed('7')) { - CMessages::AddMessageWithNumberQ("PRESS ~k~~PED_ANSWER_PHONE~TO FUCK ~1~~1~~1~", 1000, 0, 1, 2, 3, 4, 5, 6); + //const auto DoTest = [&](auto&& fn) { + // CAnimBlendAssociation* a{}; + // float b{}; + // const auto m = fn(player->m_pRwClump, &a, &b); + // DEV_LOG("Main={} (Blend: {}); Secondary={} (Blend: {})\n", (void*)m, m->GetBlendAmount(), (void*)a, b); + //}; + //DoTest(&RpAnimBlendClumpGetMainAssociation); + //DoTest(&plugin::CallAndReturn); } if (pad->IsStandardKeyJustPressed('T')) {