diff --git a/include/Kyoto/Audio/CSfxManager.hpp b/include/Kyoto/Audio/CSfxManager.hpp index b7ed946a..74a46d20 100644 --- a/include/Kyoto/Audio/CSfxManager.hpp +++ b/include/Kyoto/Audio/CSfxManager.hpp @@ -29,6 +29,7 @@ public: static void PitchBend(CSfxHandle handle, int pitch); static CSfxHandle SfxStart(u16 id, u8 vol, u8 pan, bool useAcoustics, s16 prio, bool looped, s32 areaId); + static bool IsPlaying(const CSfxHandle& handle); }; #endif diff --git a/include/MetroidPrime/CStateManager.hpp b/include/MetroidPrime/CStateManager.hpp index d73213ed..64007186 100644 --- a/include/MetroidPrime/CStateManager.hpp +++ b/include/MetroidPrime/CStateManager.hpp @@ -15,6 +15,7 @@ #include "MetroidPrime/Cameras/CCameraBlurPass.hpp" #include "MetroidPrime/Cameras/CCameraFilterPass.hpp" #include "MetroidPrime/TGameTypes.hpp" +#include "MetroidPrime/Weapons/WeaponTypes.hpp" #include "rstl/auto_ptr.hpp" #include "rstl/list.hpp" @@ -123,6 +124,8 @@ public: TAreaId GetNextAreaId() const { return x8cc_nextAreaId; } void SetCurrentAreaId(TAreaId); + bool CanCreateProjectile(TUniqueId, EWeaponType, int) const; + CMazeState* CurrentMaze(); const CMazeState* GetCurrentMaze() const; void SetCurrentMaze(rstl::single_ptr< CMazeState > maze); diff --git a/include/MetroidPrime/Player/CPlayer.hpp b/include/MetroidPrime/Player/CPlayer.hpp index 2eec3b5a..a3cdc0ed 100644 --- a/include/MetroidPrime/Player/CPlayer.hpp +++ b/include/MetroidPrime/Player/CPlayer.hpp @@ -171,6 +171,8 @@ public: TUniqueId GetOrbitTargetId() const { return x310_orbitTargetId; } TUniqueId GetOrbitNextTargetId() const { return x33c_orbitNextTargetId; } TUniqueId GetScanningObjectId() const { return x3b4_scanningObject; } + EGrappleState GetGrappleState() const { return x3b8_grappleState; } + EGunHolsterState GetGunHolsterState() const { return x498_gunHolsterState; } private: struct CVisorSteam { diff --git a/include/MetroidPrime/Player/CPlayerGun.hpp b/include/MetroidPrime/Player/CPlayerGun.hpp index 63f9b0ac..68104e8f 100644 --- a/include/MetroidPrime/Player/CPlayerGun.hpp +++ b/include/MetroidPrime/Player/CPlayerGun.hpp @@ -60,10 +60,10 @@ public: kBW_PowerBomb, }; enum EPhazonBeamState { - kKBS_Inactive, - kKBS_Entering, - kKBS_Exiting, - kKBS_Active, + kPBS_Inactive, + kPBS_Entering, + kPBS_Exiting, + kPBS_Active, }; enum EChargePhase { kCP_NotCharging, diff --git a/include/MetroidPrime/Weapons/CAuxWeapon.hpp b/include/MetroidPrime/Weapons/CAuxWeapon.hpp index 69d5f177..5b447945 100644 --- a/include/MetroidPrime/Weapons/CAuxWeapon.hpp +++ b/include/MetroidPrime/Weapons/CAuxWeapon.hpp @@ -7,6 +7,8 @@ class CAuxWeapon { public: explicit CAuxWeapon(TUniqueId playerId); + bool IsComboFxActive(const CStateManager& mgr) const; + private: u8 x0_pad[0x84]; }; diff --git a/include/MetroidPrime/Weapons/CGunWeapon.hpp b/include/MetroidPrime/Weapons/CGunWeapon.hpp index e35622c6..605b0bc8 100644 --- a/include/MetroidPrime/Weapons/CGunWeapon.hpp +++ b/include/MetroidPrime/Weapons/CGunWeapon.hpp @@ -83,6 +83,7 @@ public: CAABox GetBounds() const; CAABox GetBounds(const CTransform4f& xf) const; + const SWeaponInfo& GetWeaponInfo() const; private: // x0 is vtable diff --git a/include/MetroidPrime/Weapons/CPowerBomb.hpp b/include/MetroidPrime/Weapons/CPowerBomb.hpp new file mode 100644 index 00000000..473dbe2e --- /dev/null +++ b/include/MetroidPrime/Weapons/CPowerBomb.hpp @@ -0,0 +1,21 @@ +#ifndef _CPOWERBOMB_HPP_ +#define _CPOWERBOMB_HPP_ + +#include "MetroidPrime/Weapons/CWeapon.hpp" + +class CElementGen; + +class CPowerBomb : public CWeapon { + bool x158_24_canStartFilter : 1; + bool x158_25_filterEnabled : 1; + float x15c_curTime; + float x160_curRadius; + float x164_radiusIncrement; + rstl::single_ptr x168_particle; + float x16c_radius; + +public: + float GetCurTime() const { return x15c_curTime; } +}; + +#endif // _CPOWERBOMB_HPP_ diff --git a/include/MetroidPrime/Weapons/WeaponCommon.hpp b/include/MetroidPrime/Weapons/WeaponCommon.hpp index bf9e0bb8..8a7ddb06 100644 --- a/include/MetroidPrime/Weapons/WeaponCommon.hpp +++ b/include/MetroidPrime/Weapons/WeaponCommon.hpp @@ -17,6 +17,8 @@ enum EGunAnimType { kGAT_ToBeam }; +CSfxHandle play_sfx(u16 sfx, bool underwater, bool looped, short pan); + } #endif _WEAPONCOMMON_HPP diff --git a/src/MetroidPrime/Player/CPlayerGun.cpp b/src/MetroidPrime/Player/CPlayerGun.cpp index da0ce81d..1d0555e3 100644 --- a/src/MetroidPrime/Player/CPlayerGun.cpp +++ b/src/MetroidPrime/Player/CPlayerGun.cpp @@ -15,6 +15,7 @@ #include "MetroidPrime/Weapons/CPhazonBeam.hpp" #include "MetroidPrime/Weapons/CPlasmaBeam.hpp" #include "MetroidPrime/Weapons/CPowerBeam.hpp" +#include "MetroidPrime/Weapons/CPowerBomb.hpp" #include "MetroidPrime/Weapons/CWaveBeam.hpp" #include "MetroidPrime/Weapons/GunController/CGunMotion.hpp" #include "MetroidPrime/Weapons/WeaponTypes.hpp" @@ -157,7 +158,7 @@ CPlayerGun::CPlayerGun(TUniqueId playerId) , x330_chargeState(kCS_Normal) , x334_(0) , x338_nextState(kNS_StatusQuo) -, x33c_phazonBeamState(kKBS_Inactive) +, x33c_phazonBeamState(kPBS_Inactive) , x340_chargeBeamFactor(0.f) , x344_comboXferTimer(0.f) , x348_chargeCooldownTimer(0.f) @@ -667,7 +668,110 @@ CPlayerGun::CGunMorph::EMorphEvent CPlayerGun::CGunMorph::Update(float inY, floa return ret; } -void CPlayerGun::UpdateWeaponFire(float, CPlayerState&, CStateManager&) {} +void CPlayerGun::UpdateWeaponFire(float dt, CPlayerState& playerState, CStateManager& mgr) { + u32 oldFiring = x2ec_lastFireButtonStates; + x2ec_lastFireButtonStates = x2f4_fireButtonStates; + u32 pressedStates = x2f4_fireButtonStates & (oldFiring ^ x2f4_fireButtonStates); + x2f0_pressedFireButtonStates = pressedStates; + u32 releasedStates = oldFiring & (oldFiring ^ x2f4_fireButtonStates); + x832_28_readyForShot = false; + + CPlayer& player = *mgr.Player(); + if (!x832_24_coolingCharge && !x834_30_inBigStrike) { + float coolDown = x72c_currentBeam->GetWeaponInfo().x0_coolDown; + if ((pressedStates & 0x1) == 0) { + if (x390_cooldown >= coolDown) { + x390_cooldown = coolDown; + if (player.GetMorphballTransitionState() == CPlayer::kMS_Unmorphed && + playerState.ItemEnabled(CPlayerState::kIT_ChargeBeam) && + player.GetGunHolsterState() == CPlayer::kGH_Drawn && + player.GetGrappleState() == CPlayer::kGS_None && + playerState.GetTransitioningVisor() != CPlayerState::kPV_Scan && + playerState.GetCurrentVisor() != CPlayerState::kPV_Scan && + (x2ec_lastFireButtonStates & 0x1) != 0 && x32c_chargePhase == kCP_NotCharging) { + x832_28_readyForShot = true; + pressedStates |= 0x1; + x390_cooldown = 0.f; + } + } + } else if (x390_cooldown >= coolDown) { + x832_28_readyForShot = true; + x390_cooldown = 0.f; + } + x390_cooldown += dt; + } + + if (x834_28_requestImmediateRecharge) + x834_28_requestImmediateRecharge = (x2ec_lastFireButtonStates & 0x1) != 0; + + if (player.GetMorphballTransitionState() == CPlayer::kMS_Morphed) { + x835_28_bombReady = false; + x835_29_powerBombReady = false; + if (!x835_31_actorAttached) { + x835_28_bombReady = true; + if (x53a_powerBomb != kInvalidUniqueId && !mgr.CanCreateProjectile(x538_playerId, kWT_PowerBomb, 1)) { + const CPowerBomb* pb = static_cast(mgr.GetObjectById(x53a_powerBomb)); + if (pb && pb->GetCurTime() <= 4.25f) { + x835_28_bombReady = false; + } else { + x53a_powerBomb = kInvalidUniqueId; + } + } + if (((pressedStates & 0x1) != 0 || x32c_chargePhase != kCP_NotCharging) && + playerState.HasPowerUp(CPlayerState::kIT_MorphBallBombs)) { + if (x835_28_bombReady) + DropBomb(kBW_Bomb, mgr); + } else if (playerState.HasPowerUp(CPlayerState::kIT_PowerBombs) && + playerState.GetItemAmount(CPlayerState::kIT_PowerBombs) > 0) { + x835_29_powerBombReady = mgr.CanCreateProjectile(x538_playerId, kWT_PowerBomb, 1) && + mgr.CanCreateProjectile(x538_playerId, kWT_Bomb, 1); + if ((pressedStates & 0x2) != 0 && x835_29_powerBombReady) + DropBomb(kBW_PowerBomb, mgr); + } + } + } else if ((x2f8_stateFlags & 0x8) != 0x8 && + player.GetMorphballTransitionState() == CPlayer::kMS_Unmorphed) { + if ((pressedStates & 0x2) != 0 && x318_comboAmmoIdx == 0 && (x2f8_stateFlags & 0x2) != 0x2 && + x32c_chargePhase == kCP_NotCharging) { + u32 missileCount = playerState.GetItemAmount(CPlayerState::kIT_Missiles); + if (x338_nextState != kNS_EnterMissile && x338_nextState != kNS_ExitMissile) { + if (playerState.HasPowerUp(CPlayerState::kIT_Missiles) && missileCount > 0) { + x300_remainingMissiles = missileCount; + if (x300_remainingMissiles > 5) + x300_remainingMissiles = 5; + if (!x835_25_inPhazonBeam) { + x2f8_stateFlags &= ~0x1; + x2f8_stateFlags |= 0x6; + x318_comboAmmoIdx = 1; + x31c_missileMode = kMM_Active; + } + FireSecondary(dt, mgr); + } else { + if (!CSfxManager::IsPlaying(x2e4_invalidSfx)) { + x2e4_invalidSfx = NWeaponTypes::play_sfx(1781, x834_27_underwater, false, 0x4a); + } else { + x2e4_invalidSfx.Clear(); + } + } + } + } else { + if (x3a4_fidget.GetState() == CFidget::kS_NoFidget) { + if ((x2f8_stateFlags & 0x10) == 0x10 && x744_auxWeapon->IsComboFxActive(mgr)) { + if (x2ec_lastFireButtonStates == 0 || + (x310_currentBeam == CPlayerState::kBI_Wave && x833_29_pointBlankWorldSurface)) { + StopContinuousBeam(mgr, (x2f8_stateFlags & 0x8) == 0x8); + } + } else { + if (mgr.GetPlayerState()->ItemEnabled(CPlayerState::kIT_ChargeBeam) && + x33c_phazonBeamState == kPBS_Inactive) + ProcessChargeState(releasedStates, pressedStates, mgr, dt); + else + ProcessNormalState(releasedStates, pressedStates, mgr, dt); + } + } + } + } +} void CPlayerGun::ResetIdle(CStateManager& mgr) { CFidget::EState fidgetState = x3a4_fidget.GetState();