#pragma once #include #include "Runtime/RetroTypes.hpp" #include "Runtime/rstl.hpp" namespace metaforce { enum class ERumbleFxId { Zero = 0, One = 1, CameraShake = 6, EscapeSequenceShake = 7, PlayerBump = 11, PlayerGunCharge = 12, PlayerMissileFire = 13, PlayerGrappleFire = 14, PlayerLand = 15, PlayerGrappleSwoosh = 17, IntroBossProjectile = 19, Twenty = 20, TwentyOne = 21, TwentyTwo = 22, TwentyThree = 23 }; enum class ERumblePriority { None = 0, One = 1, Two = 2, Three = 3 }; struct SAdsrData { float x0_attackGain = 0.f; float x4_autoReleaseDur = 0.f; float x8_attackDur = 0.f; float xc_decayDur = 0.f; float x10_sustainGain = 0.f; float x14_releaseDur = 0.f; bool x18_24_hasSustain : 1 = false; bool x18_25_autoRelease : 1 = false; constexpr SAdsrData() noexcept = default; constexpr SAdsrData(float attackGain, float autoReleaseDur, float attackDur, float decayDur, float sustainGain, float releaseDur, bool hasSustain, bool autoRelease) noexcept : x0_attackGain(attackGain) , x4_autoReleaseDur(autoReleaseDur) , x8_attackDur(attackDur) , xc_decayDur(decayDur) , x10_sustainGain(sustainGain) , x14_releaseDur(releaseDur) , x18_24_hasSustain(hasSustain) , x18_25_autoRelease(autoRelease) {} }; struct SAdsrDelta { enum class EPhase { Stop, PrePulse, Attack, Decay, Sustain, Release }; float x0_curIntensity = 0.f; float x4_attackTime = 0.f; float x8_decayTime = 0.f; float xc_releaseTime = 0.f; float x10_autoReleaseTime = 0.f; float x14_attackIntensity = 0.f; float x18_sustainIntensity = 0.f; ERumblePriority x1c_priority; EPhase x20_phase; constexpr SAdsrDelta(EPhase phase, ERumblePriority priority) noexcept : x0_curIntensity(phase == EPhase::PrePulse ? 2.f : 0.f), x1c_priority(priority), x20_phase(phase) {} constexpr SAdsrDelta(EPhase phase) noexcept : x1c_priority(ERumblePriority::None), x20_phase(phase) {} static constexpr SAdsrDelta Stopped() noexcept { return SAdsrDelta(EPhase::Stop); } static constexpr SAdsrDelta Start(ERumblePriority priority, bool prePulse) noexcept { return SAdsrDelta(prePulse ? EPhase::PrePulse : EPhase::Attack, priority); } }; class CRumbleVoice { std::vector x0_datas; std::vector x10_deltas; rstl::reserved_vector x20_handleIds; s16 x2c_usedChannels = 0; u8 x2e_lastId = 0; bool UpdateChannel(SAdsrDelta& delta, const SAdsrData& data, float dt); public: CRumbleVoice(); s16 CreateRumbleHandle(s16 idx); bool OwnsSustained(s16 id) const; s16 GetFreeChannel() const; float GetIntensity() const; bool Update(float dt); void HardReset(); s16 Activate(const SAdsrData& data, s16 idx, float gain, ERumblePriority prio); void Deactivate(s16 id, bool b1); ERumblePriority GetPriority(s16 idx) const { return x10_deltas[idx].x1c_priority; } }; } // namespace metaforce