From 2f0b7cb9ecde0b51f361a0e9f9a682b0c6db3656 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Thu, 27 Nov 2025 12:42:49 -0800 Subject: [PATCH] Initial CMorphBall matches --- config/GM8E01_00/symbols.txt | 2 +- include/MetroidPrime/Player/CMorphBall.hpp | 37 +++--- .../MetroidPrime/Player/CMorphBallShadow.hpp | 9 ++ include/MetroidPrime/Player/CPlayer.hpp | 1 + src/MetroidPrime/Player/CMorphBall.cpp | 110 ++++++++++++++++++ src/MetroidPrime/Player/CPlayerState.cpp | 2 +- 6 files changed, 143 insertions(+), 18 deletions(-) create mode 100644 include/MetroidPrime/Player/CMorphBallShadow.hpp create mode 100644 src/MetroidPrime/Player/CMorphBall.cpp diff --git a/config/GM8E01_00/symbols.txt b/config/GM8E01_00/symbols.txt index b2468102..bfbefa90 100644 --- a/config/GM8E01_00/symbols.txt +++ b/config/GM8E01_00/symbols.txt @@ -6267,7 +6267,7 @@ GetBallTouchRadius__10CMorphBallCFv = .text:0x800F8DF8; // type:function size:0x GetBallRadius__10CMorphBallCFv = .text:0x800F8E04; // type:function size:0xC scope:global __dt__10CMorphBallFv = .text:0x800F8E10; // type:function size:0x764 __ct__10CMorphBallFR7CPlayerf = .text:0x800F9574; // type:function size:0xE98 scope:global -fn_800FA40C = .text:0x800FA40C; // type:function size:0x2C +__dl__24TOneStatic<10CMorphBall>FPv = .text:0x800FA40C; // type:function size:0x2C fn_800FA438 = .text:0x800FA438; // type:function size:0x7C fn_800FA4B4 = .text:0x800FA4B4; // type:function size:0x78 __sinit_CMorphBall_cpp = .text:0x800FA52C; // type:function size:0x64 scope:local diff --git a/include/MetroidPrime/Player/CMorphBall.hpp b/include/MetroidPrime/Player/CMorphBall.hpp index b7857f36..bea5fa53 100644 --- a/include/MetroidPrime/Player/CMorphBall.hpp +++ b/include/MetroidPrime/Player/CMorphBall.hpp @@ -98,38 +98,41 @@ public: // ResetMorphBallTransitionFlash__10CMorphBallFv global // CreateSpiderBallParticles__10CMorphBallFRC9CVector3fRC9CVector3f global ESpiderBallState GetSpiderBallState() const { return x187c_spiderBallState; } - // GetMorphBallModel__10CMorphBallFRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>f global - // LoadAnimationTokens__10CMorphBallFRCQ24rstl66basic_string,Q24rstl17rmemory_allocator> global - // IsBoosting__10CMorphBallCFv weak - // GetBoostChargeTimer__10CMorphBallCFv weak + static CModelData* GetMorphballModel(const rstl::string& name, const float radius); + // GetMorphBallModel__10CMorphBallFRCQ24rstl66basic_string,Q24rstl17rmemory_allocator>f + // global + // LoadAnimationTokens__10CMorphBallFRCQ24rstl66basic_string,Q24rstl17rmemory_allocator> + // global IsBoosting__10CMorphBallCFv weak GetBoostChargeTimer__10CMorphBallCFv weak // GetWallBumpCounter__10CMorphBallCFv weak // GetBallContactMaterials__10CMorphBallCFv weak void ComputeBallMovement(const CFinalInput&, CStateManager&, float); - // ComputeBoostBallMovement__10CMorphBallFRC11CFinalInputRC13CStateManagerf global - // IsMovementAllowed__10CMorphBallCFv global + void ComputeBoostBallMovement(const CFinalInput& input, const CStateManager& mgr, float dt); + bool IsMovementAllowed() const; // EnterBoosting__10CMorphBallFv global // SwitchToTire__10CMorphBallFv global - // ComputeMarioMovement__10CMorphBallFRC11CFinalInputR13CStateManagerf global + void ComputeMarioMovement(const CFinalInput& input, CStateManager& mgr, float dt); // SetSpiderBallState__10CMorphBallFQ210CMorphBall16ESpiderBallState weak - // UpdateSpiderBall__10CMorphBallFRC11CFinalInputR13CStateManagerf global - // CheckForSwitchToSpiderBallSwinging__10CMorphBallCFR13CStateManager global - // FindClosestSpiderBallWaypoint__10CMorphBallCFR13CStateManagerRC9CVector3fR9CVector3fR9CVector3fR9CVector3fRfR9CVector3fRbR12CTransform4f global - // SetSpiderBallSwingingState__10CMorphBallFb global + void UpdateSpiderBall(const CFinalInput& input, CStateManager& mgr, float dt); + bool CheckForSwitchToSpiderBallSwinging(CStateManager& mgr) const; + // FindClosestSpiderBallWaypoint__10CMorphBallCFR13CStateManagerRC9CVector3fR9CVector3fR9CVector3fR9CVector3fRfR9CVector3fRbR12CTransform4f + void SetSpiderBallSwingingState(const bool state); + bool IsSpiderBallSwinging() const { return x18be_spiderBallSwinging; } // ResetSpiderBallSwingControllerMovementTimer__10CMorphBallFv global - // ApplySpiderBallSwingingForces__10CMorphBallFRC11CFinalInputR13CStateManagerf global + void ApplySpiderBallSwingingForces(const CFinalInput& input, CStateManager& mgr, float dt); // GetSpiderBallControllerMovement__10CMorphBallCFRC11CFinalInputbb global // UpdateSpiderBallSwingControllerMovementTimer__10CMorphBallFff global // GetSpiderBallSwingControllerMovementScalar__10CMorphBallCFv global - // ApplySpiderBallRollForces__10CMorphBallFRC11CFinalInputR13CStateManagerf global - // CalculateSpiderBallAttractionSurfaceForces__10CMorphBallCFRC11CFinalInputR13CStateManagerRC12CTransform4f global - // ForwardInput__10CMorphBallCFRC11CFinalInput global + void ApplySpiderBallRollForces(const CFinalInput& input, CStateManager& mgr, float dt); + // CalculateSpiderBallAttractionSurfaceForces__10CMorphBallCFRC11CFinalInputR13CStateManagerRC12CTransform4f + float ForwardInput(const CFinalInput& input) const; // BallTurnInput__10CMorphBallCFRC11CFinalInput global // ComputeMaxSpeed__10CMorphBallCFv global bool GetIsInHalfPipeModeInAir() const; // GetTouchedHalfPipeRecently__10CMorphBallCFv global // ComputeLiftForces__10CMorphBallFRC9CVector3fRC9CVector3fRC13CStateManager global void UpdateBallDynamics(CStateManager&, float); - bool BallCloseToCollision(const CStateManager& mgr, float dist, const CMaterialFilter& filter) const; + bool BallCloseToCollision(const CStateManager& mgr, float dist, + const CMaterialFilter& filter) const; // UpdateHalfPipeStatus__10CMorphBallFR13CStateManagerf global // CalculateSurfaceToWorld__10CMorphBallCFRC9CVector3fRC9CVector3fRC9CVector3f global // UpdateMarbleDynamics__10CMorphBallFR13CStateManagerfRC9CVector3f global @@ -149,6 +152,8 @@ public: u32 GetMorphballModelShader() const { return x5c_ballModelShader; } // name? + void SetDamageTimer(const float time); + private: struct CSpiderBallElectricityManager { uint x0_effectIdx; diff --git a/include/MetroidPrime/Player/CMorphBallShadow.hpp b/include/MetroidPrime/Player/CMorphBallShadow.hpp new file mode 100644 index 00000000..185fcccd --- /dev/null +++ b/include/MetroidPrime/Player/CMorphBallShadow.hpp @@ -0,0 +1,9 @@ +#ifndef _CMORPHBALLSHADOW +#define _CMORPHBALLSHADOW + +class CMorphBallShadow { +public: + CMorphBallShadow(); + ~CMorphBallShadow(); +}; +#endif // _CMORPHBALLSHADOW diff --git a/include/MetroidPrime/Player/CPlayer.hpp b/include/MetroidPrime/Player/CPlayer.hpp index 48d4e835..863ea726 100644 --- a/include/MetroidPrime/Player/CPlayer.hpp +++ b/include/MetroidPrime/Player/CPlayer.hpp @@ -362,6 +362,7 @@ public: TUniqueId GetScanningObjectId() const { return x3b4_scanningObject; } EGrappleState GetGrappleState() const { return x3b8_grappleState; } bool IsInFreeLook() const { return x3dc_inFreeLook; } + bool IsLookButtonHeld() const { return x3dd_lookButtonHeld; } bool GetFreeLookStickState() const { return x3de_lookAnalogHeld; } TUniqueId GetAimTargetId() const { return x3f4_aimTarget; } EPlayerCameraState GetCameraState() const { return x2f4_cameraState; } diff --git a/src/MetroidPrime/Player/CMorphBall.cpp b/src/MetroidPrime/Player/CMorphBall.cpp new file mode 100644 index 00000000..fb4a8851 --- /dev/null +++ b/src/MetroidPrime/Player/CMorphBall.cpp @@ -0,0 +1,110 @@ +#include "MetroidPrime/Player/CMorphBall.hpp" + +#include "Collision/CMaterialList.hpp" +#include "Kyoto/Input/CFinalInput.hpp" +#include "MetroidPrime/CActorLights.hpp" +#include "MetroidPrime/CControlMapper.hpp" +#include "MetroidPrime/CWorldShadow.hpp" +#include "MetroidPrime/Player/CMorphBallShadow.hpp" +#include "MetroidPrime/Player/CPlayer.hpp" + +#include "MetroidPrime/TGameTypes.hpp" + +#include "Kyoto/Math/CTransform4f.hpp" +#include "Kyoto/Particles/CElementGen.hpp" +#include "Kyoto/Particles/CParticleSwoosh.hpp" +#include "MetroidPrime/Tweaks/CTweakBall.hpp" +#include "MetroidPrime/Tweaks/CTweakPlayer.hpp" + +CMorphBall::CMorphBall(CPlayer& player, float radius) +: x0_player(player) +, x4_loadedModelId(-1) +, x8_ballGlowColorIdx(0) +, xc_radius(radius) +, x10_boostControlForce(CVector3f::Zero()) +, x1c_controlForce(CVector3f::Zero()) +, x28_tireMode(false) +, x2c_tireLeanAngle(0.f) +, x30_ballTiltAngle(0.f) +, x38_collisionSphere(CSphere(CVector3f(0.f, 0.f, radius), radius), + CMaterialList(kMT_Player, kMT_Solid, kMT_GroundCollider)) +, x58_ballModel(GetMorphballModel(rstl::string_l("SamusBallAncs"), radius)) +, x5c_ballModelShader(0) +, x60_spiderBallGlassModel(GetMorphballModel(rstl::string_l("SamusSpiderBallGalssCMDL"), radius)) +, x64_spiderBallGlassModelShader(0) +, x68_lowPolyBallModel(GetMorphballModel(rstl::string_l("SamusBallLowPolyCMDL"), radius)) +, x6c_lowPolyBallModelShader(0) +, x187c_spiderBallState(kSBS_Inactive) +, x1880_playerToSpiderNormal(CVector3f::Zero()) +, x188c_spiderPullMovement(0.f) +, x1890_spiderTrackPoint(CVector3f::Zero()) +, x189c_spiderInterpBetweenPoints(CVector3f::Zero()) +, x18a8_spiderBetweenPoints(CVector3f::Zero()) +, x18b4_linVelDamp(0.f) +, x18b8_angVelDamp(0.f) +, x18bc_spiderNearby(false) +, x18bd_touchingSpider(false) +, x18be_spiderBallSwinging(false) +, x18bf_spiderSwingInAir(true) +, x18c0_isSpiderSurface(false) +, x18c4_spiderSurfaceTransform(CTransform4f::Identity()) +, x18f4_spiderSurfacePivotAngle(0.f) +, x18f8_spiderSurfacePivotTargetAngle(0.f) +, x18fc_refPullVel(0.f) +, x1900_playerToSpiderTrackDist(0.f) +, x1904_swingControlDir(0.f) +, x1908_swingControlTime(0.f) +, x190c_normSpiderSurfaceForces(0.f, 0.f) +, x1914_spiderTrackForceMag(0.f) +, x1918_spiderViewControlMag(0.f) +, x191c_damageTimer(0.f) +, x1920_spiderForcesReset(false) +, x1924_surfaceToWorld(CTransform4f::Identity()) +, x1c0c_wakeEffectIdx(-1) +, x1c10_ballInnerGlowLight(kInvalidUniqueId) {} + +CMorphBall::~CMorphBall() {} + +float CMorphBall::GetBallRadius() const { return gpTweakPlayer->GetPlayerBallHalfExtent(); } +float CMorphBall::GetBallTouchRadius() const { return gpTweakBall->GetBallTouchRadius(); } + +float CMorphBall::ForwardInput(const CFinalInput& input) const { + if (!IsMovementAllowed()) { + return 0.f; + } + + const float forwardInput = ControlMapper::GetAnalogInput(ControlMapper::kC_Forward, input); + const float backwardInput = ControlMapper::GetAnalogInput(ControlMapper::kC_Backward, input); + + return forwardInput - backwardInput; +} + +void CMorphBall::ComputeBallMovement(const CFinalInput& input, CStateManager& mgr, float dt) { + ComputeBoostBallMovement(input, mgr, dt); + ComputeMarioMovement(input, mgr, dt); +} + +bool CMorphBall::IsMovementAllowed() const { + if (!gpTweakPlayer->GetMoveDuringFreeLook() && + (x0_player.IsInFreeLook() || x0_player.IsLookButtonHeld())) { + return false; + } + + if (x0_player.IsMorphBallTransitioning()) { + return false; + } + + return !(x1e00_disableControlCooldown > 0.f); +} + +void CMorphBall::SetDamageTimer(const float time) { x191c_damageTimer = time; } + +void CMorphBall::UpdateSpiderBall(const CFinalInput& input, CStateManager& mgr, float dt) { + SetSpiderBallSwingingState(CheckForSwitchToSpiderBallSwinging(mgr)); + + if (IsSpiderBallSwinging()) { + ApplySpiderBallSwingingForces(input, mgr, dt); + } else { + ApplySpiderBallRollForces(input, mgr, dt); + } +} diff --git a/src/MetroidPrime/Player/CPlayerState.cpp b/src/MetroidPrime/Player/CPlayerState.cpp index 6bd9a854..6de322e7 100644 --- a/src/MetroidPrime/Player/CPlayerState.cpp +++ b/src/MetroidPrime/Player/CPlayerState.cpp @@ -100,7 +100,7 @@ CPlayerState::CPlayerState(CInputStream& stream) int amount = 0; int capacity = 0; - int maxValue = kPowerUpMax[i]; + uint maxValue = kPowerUpMax[i]; if (maxValue != 0) { uint bitCount = GetBitCount(maxValue); amount = stream.ReadBits(bitCount);