From e1edd5f35dc024bdf0d0a11fa76b383d59821dbb Mon Sep 17 00:00:00 2001 From: Hyper <34012267+hyperbx@users.noreply.github.com> Date: Fri, 7 Feb 2025 20:11:25 +0000 Subject: [PATCH] Fix Tornado Defense boss firing missiles too frequently (#306) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Skyth (Asilkan) <19259897+blueskythlikesclouds@users.noreply.github.com> Co-authored-by: DarĂ­o <538504+DarioSamo@users.noreply.github.com> --- UnleashedRecomp/api/SWA.h | 4 +++ .../ExtraStage/Tails/Enemy/Boss/ExStageBoss.h | 36 +++++++++++++++++++ .../Tails/Enemy/Boss/State/StateBase.h | 9 +++++ .../Tails/Enemy/Boss/State/StateBattle.h | 18 ++++++++++ .../ExtraStage/Tails/Player/ExPlayerTails.h | 35 ++++++++++++++++++ UnleashedRecomp/patches/fps_patches.cpp | 34 ++++++++++++++++++ UnleashedRecompLib/config/SWA.toml | 11 ++++++ 7 files changed, 147 insertions(+) create mode 100644 UnleashedRecomp/api/SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h create mode 100644 UnleashedRecomp/api/SWA/ExtraStage/Tails/Enemy/Boss/State/StateBase.h create mode 100644 UnleashedRecomp/api/SWA/ExtraStage/Tails/Enemy/Boss/State/StateBattle.h create mode 100644 UnleashedRecomp/api/SWA/ExtraStage/Tails/Player/ExPlayerTails.h diff --git a/UnleashedRecomp/api/SWA.h b/UnleashedRecomp/api/SWA.h index 316e5bc..f451f2b 100644 --- a/UnleashedRecomp/api/SWA.h +++ b/UnleashedRecomp/api/SWA.h @@ -62,6 +62,10 @@ #include "SWA/Camera/Camera.h" #include "SWA/Camera/CameraController.h" #include "SWA/CharacterUtility/CharacterProxy.h" +#include "SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h" +#include "SWA/ExtraStage/Tails/Enemy/Boss/State/StateBase.h" +#include "SWA/ExtraStage/Tails/Enemy/Boss/State/StateBattle.h" +#include "SWA/ExtraStage/Tails/Player/ExPlayerTails.h" #include "SWA/Globals.h" #include "SWA/HUD/GeneralWindow/GeneralWindow.h" #include "SWA/HUD/Loading/Loading.h" diff --git a/UnleashedRecomp/api/SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h b/UnleashedRecomp/api/SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h new file mode 100644 index 0000000..90fec50 --- /dev/null +++ b/UnleashedRecomp/api/SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +namespace SWA +{ + class CExStageBoss + { + public: + class CStateBase; + class CStateBattle; + + class CExStageBossStateContext + { + public: + SWA_INSERT_PADDING(0x14C); + be m_SplineProgress; + SWA_INSERT_PADDING(0x0C); + be m_SplineSpeed; + SWA_INSERT_PADDING(0x28); + be m_Field188; + be m_Field18C; + SWA_INSERT_PADDING(0x21); + bool m_IsBattleStart; + SWA_INSERT_PADDING(0x36E); + be m_Field520; + }; + }; + + SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_SplineProgress, 0x14C); + SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_SplineSpeed, 0x15C); + SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_Field188, 0x188); + SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_Field18C, 0x18C); + SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_IsBattleStart, 0x1B1); + SWA_ASSERT_OFFSETOF(CExStageBoss::CExStageBossStateContext, m_Field520, 0x520); +} diff --git a/UnleashedRecomp/api/SWA/ExtraStage/Tails/Enemy/Boss/State/StateBase.h b/UnleashedRecomp/api/SWA/ExtraStage/Tails/Enemy/Boss/State/StateBase.h new file mode 100644 index 0000000..7aaa9b8 --- /dev/null +++ b/UnleashedRecomp/api/SWA/ExtraStage/Tails/Enemy/Boss/State/StateBase.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include "SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h" + +namespace SWA +{ + class CExStageBoss::CStateBase : public Hedgehog::Universe::CStateMachineBase::CStateBase {}; +} diff --git a/UnleashedRecomp/api/SWA/ExtraStage/Tails/Enemy/Boss/State/StateBattle.h b/UnleashedRecomp/api/SWA/ExtraStage/Tails/Enemy/Boss/State/StateBattle.h new file mode 100644 index 0000000..4fbfdd4 --- /dev/null +++ b/UnleashedRecomp/api/SWA/ExtraStage/Tails/Enemy/Boss/State/StateBattle.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "SWA/ExtraStage/Tails/Enemy/Boss/ExStageBoss.h" + +namespace SWA +{ + class CExStageBoss::CStateBattle : public CExStageBoss::CStateBase + { + public: + SWA_INSERT_PADDING(0x08); + be m_Field68; + be m_FramesSinceLastMissile; + }; + + SWA_ASSERT_OFFSETOF(CExStageBoss::CStateBattle, m_Field68, 0x68); + SWA_ASSERT_OFFSETOF(CExStageBoss::CStateBattle, m_FramesSinceLastMissile, 0x6C); +} diff --git a/UnleashedRecomp/api/SWA/ExtraStage/Tails/Player/ExPlayerTails.h b/UnleashedRecomp/api/SWA/ExtraStage/Tails/Player/ExPlayerTails.h new file mode 100644 index 0000000..c91eb1e --- /dev/null +++ b/UnleashedRecomp/api/SWA/ExtraStage/Tails/Player/ExPlayerTails.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace SWA +{ + class CExPlayerTails + { + public: + class CExPlayerTailsStateContext + { + public: + SWA_INSERT_PADDING(0x1F8); + be m_SplineBossStart; + be m_SplineEnd; + SWA_INSERT_PADDING(0x30); + be m_SplineProgress; + SWA_INSERT_PADDING(0x10); + xpointer m_Field244; + SWA_INSERT_PADDING(0x18); + be m_SplineSpeed; + SWA_INSERT_PADDING(0x0C); + be m_State; // 0 - Intro; 1 - Boss Intro; 3 - Boss + }; + + class CStateBase : public Hedgehog::Universe::CStateMachineBase::CStateBase {}; + }; + + SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_SplineBossStart, 0x1F8); + SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_SplineEnd, 0x1FC); + SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_SplineProgress, 0x230); + SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_Field244, 0x244); + SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_SplineSpeed, 0x260); + SWA_ASSERT_OFFSETOF(CExPlayerTails::CExPlayerTailsStateContext, m_State, 0x270); +} diff --git a/UnleashedRecomp/patches/fps_patches.cpp b/UnleashedRecomp/patches/fps_patches.cpp index 97f36b4..33f4ca2 100644 --- a/UnleashedRecomp/patches/fps_patches.cpp +++ b/UnleashedRecomp/patches/fps_patches.cpp @@ -125,3 +125,37 @@ void WaitVsyncMidAsmHook() void ApplicationFrameLimiterMidAsmHook() { } + +// Tornado Defense boss increments timers without respecting delta time. +// We run the update function with a 30 FPS time step to ensure all timers update at the correct rate. +static constexpr size_t EX_STAGE_BOSS_STATE_BATTLE_SIZE = 0x70; + +void CExStageBossCStateBattleAllocMidAsmHook(PPCRegister& r3) +{ + r3.u32 += sizeof(float); +} + +void CExStageBossCStateBattleCtorMidAsmHook(PPCRegister& r3) +{ + new (g_memory.base + r3.u32 + EX_STAGE_BOSS_STATE_BATTLE_SIZE) float(0); +} + +// SWA::CExStageBoss::CStateBattle::Update +PPC_FUNC_IMPL(__imp__sub_82B00D00); +PPC_FUNC(sub_82B00D00) +{ + constexpr auto referenceDeltaTime = 1.0f / 30.0f; + constexpr auto deltaTimeTolerance = 0.0001f; + + auto pElapsedTime = (float*)(base + ctx.r3.u32 + EX_STAGE_BOSS_STATE_BATTLE_SIZE); + + *pElapsedTime += std::min(App::s_deltaTime, 1.0 / 15.0); + + if ((*pElapsedTime + deltaTimeTolerance) > referenceDeltaTime) + { + __imp__sub_82B00D00(ctx, base); + *pElapsedTime -= referenceDeltaTime; + } + + *pElapsedTime = std::max(*pElapsedTime, 0.0f); +} diff --git a/UnleashedRecompLib/config/SWA.toml b/UnleashedRecompLib/config/SWA.toml index f7aeefd..1e47101 100644 --- a/UnleashedRecompLib/config/SWA.toml +++ b/UnleashedRecompLib/config/SWA.toml @@ -936,3 +936,14 @@ address = 0x82BADADC registers = ["r4", "r5", "r6", "r30"] jump_address_on_true = 0x82BAD9F0 jump_address_on_false = 0x82BADAFC + +[[midasm_hook]] +name = "CExStageBossCStateBattleAllocMidAsmHook" +address = 0x82B026DC +registers = ["r3"] +after_instruction = true + +[[midasm_hook]] +name = "CExStageBossCStateBattleCtorMidAsmHook" +address = 0x82B026E4 +registers = ["r3"]