diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index fd372be..ceb4d83 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -137,6 +137,7 @@ set(UNLEASHED_RECOMP_PATCHES_CXX_SOURCES "patches/CTitleStateIntro_patches.cpp" "patches/CTitleStateMenu_patches.cpp" "patches/fps_patches.cpp" + "patches/free_camera_patches.cpp" "patches/frontend_listener.cpp" "patches/input_patches.cpp" "patches/inspire_patches.cpp" diff --git a/UnleashedRecomp/api/SWA.h b/UnleashedRecomp/api/SWA.h index fecf2ad..1779a7d 100644 --- a/UnleashedRecomp/api/SWA.h +++ b/UnleashedRecomp/api/SWA.h @@ -94,6 +94,7 @@ #include "SWA/Player/Character/EvilSonic/EvilSonic.h" #include "SWA/Player/Character/EvilSonic/EvilSonicContext.h" #include "SWA/Player/Character/EvilSonic/Hud/EvilHudGuide.h" +#include "SWA/Replay/Camera/ReplayFreeCamera.h" #include "SWA/Sequence/Unit/SequenceUnitBase.h" #include "SWA/Sequence/Unit/SequenceUnitPlayMovie.h" #include "SWA/Sequence/Utility/SequencePlayMovieWrapper.h" diff --git a/UnleashedRecomp/api/SWA/Camera/CameraController.h b/UnleashedRecomp/api/SWA/Camera/CameraController.h index 9262ed0..f7e7bb3 100644 --- a/UnleashedRecomp/api/SWA/Camera/CameraController.h +++ b/UnleashedRecomp/api/SWA/Camera/CameraController.h @@ -4,10 +4,10 @@ namespace SWA { - class CCameraController : public Hedgehog::Universe::CStateMachineBase::CStateBase + class CCameraController { public: - SWA_INSERT_PADDING(0x04); + SWA_INSERT_PADDING(0x64); be m_FieldOfView; SWA_INSERT_PADDING(0x68); }; diff --git a/UnleashedRecomp/api/SWA/Globals.h b/UnleashedRecomp/api/SWA/Globals.h index ddd6acc..c8a9ee3 100644 --- a/UnleashedRecomp/api/SWA/Globals.h +++ b/UnleashedRecomp/api/SWA/Globals.h @@ -33,6 +33,9 @@ namespace SWA // ms_IsRenderDebugPositionDraw: デバッグ位置描画 static inline bool* ms_IsRenderDebugPositionDraw; + // N/A + static inline bool* ms_IsRenderDepthOfField; + // ms_IsRenderGameMainHud: ゲームメインHUD 描画 static inline bool* ms_IsRenderGameMainHud; @@ -65,6 +68,7 @@ namespace SWA ms_IsRenderDebugDraw = (bool*)MmGetHostAddress(0x8328BB23); ms_IsRenderDebugDrawText = (bool*)MmGetHostAddress(0x8328BB25); ms_IsRenderDebugPositionDraw = (bool*)MmGetHostAddress(0x8328BB24); + ms_IsRenderDepthOfField = (bool*)MmGetHostAddress(0x83302720); ms_IsRenderGameMainHud = (bool*)MmGetHostAddress(0x8328BB27); ms_IsRenderHud = (bool*)MmGetHostAddress(0x8328BB26); ms_IsRenderHudPause = (bool*)MmGetHostAddress(0x8328BB28); diff --git a/UnleashedRecomp/api/SWA/Replay/Camera/ReplayFreeCamera.h b/UnleashedRecomp/api/SWA/Replay/Camera/ReplayFreeCamera.h new file mode 100644 index 0000000..a9ffd7a --- /dev/null +++ b/UnleashedRecomp/api/SWA/Replay/Camera/ReplayFreeCamera.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include "SWA/Camera/CameraController.h" + +namespace SWA +{ + class CReplayFreeCamera : public CCameraController + { + public: + SWA_INSERT_PADDING(0x90); + be m_Speed; + }; + + SWA_ASSERT_OFFSETOF(CReplayFreeCamera, m_Speed, 0x160); +} diff --git a/UnleashedRecomp/patches/CHudPause_patches.cpp b/UnleashedRecomp/patches/CHudPause_patches.cpp index 50e0d54..b26262f 100644 --- a/UnleashedRecomp/patches/CHudPause_patches.cpp +++ b/UnleashedRecomp/patches/CHudPause_patches.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -119,6 +120,9 @@ bool CHudPauseMiscInjectOptionsMidAsmHook(PPCRegister& pThis) PPC_FUNC_IMPL(__imp__sub_824B0930); PPC_FUNC(sub_824B0930) { + if (FreeCameraPatches::s_isActive) + return; + if (App::s_isLoading) { __imp__sub_824B0930(ctx, base); diff --git a/UnleashedRecomp/patches/camera_patches.cpp b/UnleashedRecomp/patches/camera_patches.cpp index 059f5c6..6242153 100644 --- a/UnleashedRecomp/patches/camera_patches.cpp +++ b/UnleashedRecomp/patches/camera_patches.cpp @@ -1,9 +1,10 @@ +#include "camera_patches.h" #include +#include +#include +#include #include #include -#include -#include "camera_patches.h" -#include "aspect_ratio_patches.h" void CameraAspectRatioMidAsmHook(PPCRegister& r30, PPCRegister& r31) { @@ -35,7 +36,9 @@ void CameraFieldOfViewMidAsmHook(PPCRegister& r31, PPCRegister& f31) { auto camera = (SWA::CCamera*)g_memory.Translate(r31.u32); - f31.f64 = AdjustFieldOfView(f31.f64, camera->m_HorzAspectRatio); + f31.f64 = FreeCameraPatches::s_isActive + ? FreeCameraPatches::s_fieldOfView + : AdjustFieldOfView(f31.f64, camera->m_HorzAspectRatio); } PPC_FUNC_IMPL(__imp__sub_824697B0); diff --git a/UnleashedRecomp/patches/free_camera_patches.cpp b/UnleashedRecomp/patches/free_camera_patches.cpp new file mode 100644 index 0000000..0cdb61c --- /dev/null +++ b/UnleashedRecomp/patches/free_camera_patches.cpp @@ -0,0 +1,222 @@ +#include "free_camera_patches.h" +#include +#include +#include +#include +#include + +#define DEGREES_TO_RADIANS(x) (float)(x / 180.0f * M_PI) +#define RADIANS_TO_DEGREES(x) (float)(x / M_PI * 180.0f) + +constexpr float DEFAULT_SPEED = 0.5f; +constexpr float DEFAULT_FOV = 45.0f; +constexpr float MOVE_SPEED_SLOW = 0.075f; +constexpr float MOVE_SPEED_FAST = 8.0f; +constexpr float MOVE_SPEED_MODIFIER_RATIO = 0.02f; +constexpr float FOV_MODIFIER_RATIO = 1.0f; + +static float g_baseSpeed = DEFAULT_SPEED; +static float g_baseFOV = DEFAULT_FOV; + +static bool g_isCameraLocked; +static float g_speed; +static float g_fov; + +static void ResetParameters() +{ + g_isCameraLocked = false; + g_speed = g_baseSpeed = DEFAULT_SPEED; + + *SWA::SGlobals::ms_IsRenderDepthOfField = true; + FreeCameraPatches::s_fieldOfView = g_fov = g_baseFOV = DEFAULT_FOV; +} + +bool EnableFreeCameraMidAsmHook() +{ + return Config::EnableFreeCamera; +} + +bool FreeCameraNullInputMidAsmHook() +{ + return Config::EnableFreeCamera; +} + +// Original input: D-Pad Up +bool FreeCameraActivationInputMidAsmHook(PPCRegister& r11, PPCRegister& r27, PPCRegister& r28) +{ + if (!Config::EnableFreeCamera) + return false; + + static auto isChangedCameraMode = false; + + if ((r11.u32 & SWA::eKeyState_Select) != 0) + { + if (++r28.u32 >= 2) + r28.u32 = 0; + + isChangedCameraMode = true; + } + + FreeCameraPatches::s_isActive = r28.u32 > 0; + + if (isChangedCameraMode) + { + ResetParameters(); + + switch (r28.u32) + { + case 0: + LOGN("[Free Camera] Disabled"); + break; + + case 1: + LOGN("[Free Camera] Enabled"); + break; + } + + isChangedCameraMode = false; + + if (FreeCameraPatches::s_isActive && *SWA::SGlobals::ms_IsRenderHud) + { + *SWA::SGlobals::ms_IsRenderHud = false; + } + else + { + *SWA::SGlobals::ms_IsRenderHud = true; + } + } + + return true; +} + +// Original input: D-Pad Left +void FreeCameraTeleportToPlayerInputMidAsmHook(PPCRegister& r4) +{ + if (Config::EnableFreeCamera) + r4.u32 = SWA::eKeyState_RightStick; +} + +// Original inputs: X (Square) / Y (Triangle) +bool FreeCameraSpeedInputMidAsmHook(PPCRegister& r31) +{ + if (!Config::EnableFreeCamera) + return false; + + auto pCamera = (SWA::CReplayFreeCamera*)g_memory.Translate(r31.u32); + auto pInputState = SWA::CInputState::GetInstance(); + + if (!pInputState) + return false; + + auto& rPadState = pInputState->GetPadState(); + + auto factor = App::s_deltaTime / (1.0f / 60.0f); + auto aspectRatio = (float)GameWindow::s_width / (float)GameWindow::s_height; + + if (g_isCameraLocked) + { + g_speed = 0.0f; + } + else + { + static auto isLeftTriggerSpeedModifier = false; + static auto isRightTriggerSpeedModifier = false; + + if (rPadState.IsDown(SWA::eKeyState_LeftTrigger)) + { + g_speed = MOVE_SPEED_SLOW; + isLeftTriggerSpeedModifier = true; + } + else if (isLeftTriggerSpeedModifier) + { + g_speed = g_baseSpeed; + isLeftTriggerSpeedModifier = false; + } + + if (rPadState.IsDown(SWA::eKeyState_RightTrigger)) + { + g_speed = MOVE_SPEED_FAST; + isRightTriggerSpeedModifier = true; + } + else if (isRightTriggerSpeedModifier) + { + g_speed = g_baseSpeed; + isRightTriggerSpeedModifier = false; + } + + if (isLeftTriggerSpeedModifier && isRightTriggerSpeedModifier) + g_speed = MOVE_SPEED_FAST / 3; + + if (rPadState.IsDown(SWA::eKeyState_A)) + g_speed = g_baseSpeed = DEFAULT_SPEED; + + if (rPadState.IsDown(SWA::eKeyState_B)) + { + g_baseSpeed -= MOVE_SPEED_MODIFIER_RATIO * factor; + g_speed = g_baseSpeed; + + LOGFN("[Free Camera] Speed: {}", g_speed); + } + + if (rPadState.IsDown(SWA::eKeyState_X)) + { + g_baseSpeed += MOVE_SPEED_MODIFIER_RATIO * factor; + g_speed = g_baseSpeed; + + LOGFN("[Free Camera] Speed: {}", g_speed); + } + + auto isResetFOV = rPadState.IsDown(SWA::eKeyState_Y); + auto isIncreaseFOV = rPadState.IsDown(SWA::eKeyState_DpadUp); + auto isDecreaseFOV = rPadState.IsDown(SWA::eKeyState_DpadDown); + + auto fovScaleFactor = 0.0f; + + if (isIncreaseFOV) + { + fovScaleFactor = FOV_MODIFIER_RATIO; + } + else if (isDecreaseFOV) + { + fovScaleFactor = -FOV_MODIFIER_RATIO; + } + + g_speed = std::clamp(g_speed, 0.01f, 20.0f); + g_fov = fmodf(isResetFOV ? DEFAULT_FOV : g_fov + fovScaleFactor * App::s_deltaTime * 60.0f, 180.0f); + } + + if (rPadState.IsTapped(SWA::eKeyState_DpadLeft)) + { + g_isCameraLocked = !g_isCameraLocked; + g_speed = g_baseSpeed; + + if (g_isCameraLocked) + { + LOGN("[Free Camera] Locked"); + } + else + { + LOGN("[Free Camera] Unlocked"); + } + } + + if (rPadState.IsTapped(SWA::eKeyState_DpadRight)) + { + *SWA::SGlobals::ms_IsRenderDepthOfField = !*SWA::SGlobals::ms_IsRenderDepthOfField; + + if (*SWA::SGlobals::ms_IsRenderDepthOfField) + { + LOGN("[Free Camera] Depth of Field ON"); + } + else + { + LOGN("[Free Camera] Depth of Field OFF"); + } + } + + pCamera->m_Speed = g_speed; + + FreeCameraPatches::s_fieldOfView = 2.0f * atan(tan(DEGREES_TO_RADIANS(g_fov / 2.0f) * (16.0f / 9.0f / std::min(aspectRatio, 16.0f / 9.0f)))); + + return true; +} diff --git a/UnleashedRecomp/patches/free_camera_patches.h b/UnleashedRecomp/patches/free_camera_patches.h new file mode 100644 index 0000000..77010e8 --- /dev/null +++ b/UnleashedRecomp/patches/free_camera_patches.h @@ -0,0 +1,9 @@ +#pragma once + +class FreeCameraPatches +{ +public: + static inline bool s_isActive; + + static inline float s_fieldOfView; +}; diff --git a/UnleashedRecomp/user/config_def.h b/UnleashedRecomp/user/config_def.h index c9069f9..9ab0b88 100644 --- a/UnleashedRecomp/user/config_def.h +++ b/UnleashedRecomp/user/config_def.h @@ -80,6 +80,7 @@ CONFIG_DEFINE_HIDDEN("Codes", bool, DisableAutoSaveWarning, false); CONFIG_DEFINE_HIDDEN("Codes", bool, DisableDLCIcon, false); CONFIG_DEFINE_HIDDEN("Codes", bool, DisableDWMRoundedCorners, false); CONFIG_DEFINE_HIDDEN("Codes", bool, EnableEventCollisionDebugView, false); +CONFIG_DEFINE_HIDDEN("Codes", bool, EnableFreeCamera, false); CONFIG_DEFINE_HIDDEN("Codes", bool, EnableGIMipLevelDebugView, false); CONFIG_DEFINE_HIDDEN("Codes", bool, EnableObjectCollisionDebugView, false); CONFIG_DEFINE_HIDDEN("Codes", bool, EnableStageCollisionDebugView, false); diff --git a/UnleashedRecompLib/config/SWA.toml b/UnleashedRecompLib/config/SWA.toml index de0e981..756d1da 100644 --- a/UnleashedRecompLib/config/SWA.toml +++ b/UnleashedRecompLib/config/SWA.toml @@ -1086,3 +1086,42 @@ registers = ["r4"] name = "AnimationDataMakeMidAsmHook" address = 0x82BB38E4 registers = ["r31", "r29", "r28"] + +[[midasm_hook]] +name = "EnableFreeCameraMidAsmHook" +address = 0x825389F0 +jump_address_on_true = 0x825389F4 + +[[midasm_hook]] +name = "EnableFreeCameraMidAsmHook" +address = 0x82538A18 +jump_address_on_true = 0x82538A1C + +[[midasm_hook]] +name = "FreeCameraActivationInputMidAsmHook" +address = 0x824569BC +registers = ["r11", "r27", "r28"] +jump_address_on_true = 0x824569D4 + +[[midasm_hook]] +name = "FreeCameraTeleportToPlayerInputMidAsmHook" +address = 0x8245C21C +registers = ["r4"] + +[[midasm_hook]] +name = "FreeCameraSpeedInputMidAsmHook" +address = 0x8245C318 +registers = ["r31"] +jump_address_on_true = 0x8245C38C + +# Disable "change to free camera" input. +[[midasm_hook]] +name = "FreeCameraNullInputMidAsmHook" +address = 0x8245BCE4 +jump_address_on_true = 0x8245BDB4 + +# Disable "change to pan camera" input. +[[midasm_hook]] +name = "FreeCameraNullInputMidAsmHook" +address = 0x8245BDC4 +jump_address_on_true = 0x8245BEAC