Implemented D-Pad support for World Map, Super Sonic (WIP) and Bobsleigh

This commit is contained in:
Hyper 2025-01-22 17:02:25 +00:00
parent 4e149b0640
commit 99bb91ef50
8 changed files with 472 additions and 16 deletions

View file

@ -59,6 +59,7 @@
#include "SWA/CSD/CsdTexListMirage.h"
#include "SWA/CSD/GameObjectCSD.h"
#include "SWA/Camera/Camera.h"
#include "SWA/Camera/CameraController.h"
#include "SWA/HUD/GeneralWindow/GeneralWindow.h"
#include "SWA/HUD/Loading/Loading.h"
#include "SWA/HUD/Pause/HudPause.h"
@ -73,6 +74,7 @@
#include "SWA/Inspire/InspireTextureAnimationInfo.h"
#include "SWA/Inspire/InspireTextureOverlay.h"
#include "SWA/Inspire/InspireTextureOverlayInfo.h"
#include "SWA/Menu/MenuWindowBase.h"
#include "SWA/Movie/MovieDisplayer.h"
#include "SWA/Movie/MovieManager.h"
#include "SWA/Player/Character/EvilSonic/EvilSonic.h"
@ -97,6 +99,9 @@
#include "SWA/System/GameMode/Title/TitleMenu.h"
#include "SWA/System/GameMode/Title/TitleStateBase.h"
#include "SWA/System/GameMode/Title/TitleStateIntro.h"
#include "SWA/System/GameMode/Title/TitleStateWorldMap.h"
#include "SWA/System/GameMode/WorldMap/WorldMapCamera.h"
#include "SWA/System/GameMode/WorldMap/WorldMapCursor.h"
#include "SWA/System/GameObject.h"
#include "SWA/System/GameParameter.h"
#include "SWA/System/GammaController.h"

View file

@ -0,0 +1,17 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CCameraController : public Hedgehog::Universe::CStateMachineBase::CStateBase
{
public:
SWA_INSERT_PADDING(0x04);
be<float> m_FieldOfView;
SWA_INSERT_PADDING(0x68);
};
SWA_ASSERT_OFFSETOF(CCameraController, m_FieldOfView, 0x64);
SWA_ASSERT_SIZEOF(CCameraController, 0xD0);
}

View file

@ -0,0 +1,12 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CMenuWindowBase
{
public:
SWA_INSERT_PADDING(0x10);
};
}

View file

@ -0,0 +1,14 @@
#pragma once
#include <SWA.inl>
#include <SWA/System/GameMode/WorldMap/WorldMapCursor.h>
namespace SWA
{
class CTitleStateWorldMap : public CTitleStateBase
{
public:
SWA_INSERT_PADDING(0x08);
xpointer<CWorldMapCursor> m_pWorldMapCursor;
};
}

View file

@ -0,0 +1,26 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CWorldMapCamera : public CCameraController
{
public:
be<float> m_Pitch;
be<float> m_Yaw;
be<float> m_Distance;
be<float> m_RotationSpeed;
SWA_INSERT_PADDING(0x08);
bool m_CanMove;
SWA_INSERT_PADDING(0x34);
be<float> m_TiltToEarthTransitionSpeed;
};
SWA_ASSERT_OFFSETOF(CWorldMapCamera, m_Pitch, 0xD0);
SWA_ASSERT_OFFSETOF(CWorldMapCamera, m_Yaw, 0xD4);
SWA_ASSERT_OFFSETOF(CWorldMapCamera, m_Distance, 0xD8);
SWA_ASSERT_OFFSETOF(CWorldMapCamera, m_RotationSpeed, 0xDC);
SWA_ASSERT_OFFSETOF(CWorldMapCamera, m_CanMove, 0xE8);
SWA_ASSERT_OFFSETOF(CWorldMapCamera, m_TiltToEarthTransitionSpeed, 0x120);
}

View file

@ -0,0 +1,24 @@
#pragma once
#include <SWA.inl>
namespace SWA
{
class CWorldMapCursor : public CMenuWindowBase
{
public:
SWA_INSERT_PADDING(0x24);
be<float> m_LeftStickVertical;
be<float> m_LeftStickHorizontal;
bool m_IsCursorMoving;
SWA_INSERT_PADDING(0x07);
be<float> m_CursorY;
be<float> m_CursorX;
};
SWA_ASSERT_OFFSETOF(CWorldMapCursor, m_LeftStickVertical, 0x34);
SWA_ASSERT_OFFSETOF(CWorldMapCursor, m_LeftStickHorizontal, 0x38);
SWA_ASSERT_OFFSETOF(CWorldMapCursor, m_IsCursorMoving, 0x3C);
SWA_ASSERT_OFFSETOF(CWorldMapCursor, m_CursorY, 0x44);
SWA_ASSERT_OFFSETOF(CWorldMapCursor, m_CursorX, 0x48);
}

View file

@ -1,28 +1,161 @@
#include <api/SWA.h>
#include <ui/sdl_listener.h>
#include <app.h>
#include <exports.h>
static void SetDPadAnalogDirectionX(PPCRegister& pPadState, PPCRegister& x, bool negate)
constexpr float WORLD_MAP_TOUCH_CANCEL_DEADZONE = 0.31f;
constexpr float WORLD_MAP_TOUCH_DAMPING_FACTOR = 0.99f;
constexpr float WORLD_MAP_TOUCH_FLICK_ACCELERATION_X = 0.4f;
constexpr float WORLD_MAP_TOUCH_FLICK_ACCELERATION_Y = 0.2f;
constexpr float WORLD_MAP_TOUCH_FLICK_TERMINAL_VELOCITY = 40.0f;
constexpr float WORLD_MAP_TOUCH_FLICK_THRESHOLD = 2.25f;
constexpr float WORLD_MAP_TOUCH_SENSITIVITY_MULTIPLIER = 1.35f;
constexpr float WORLD_MAP_TOUCH_SMOOTHING_FACTOR = 0.8f;
static bool g_isTouchActive;
static float g_worldMapTouchVelocityX;
static float g_worldMapTouchVelocityY;
class SDLEventListenerForInputPatches : public SDLEventListener
{
static inline int ms_touchpadFingerCount;
static inline float ms_touchpadX;
static inline float ms_touchpadY;
static inline float ms_touchpadDeltaX;
static inline float ms_touchpadDeltaY;
static inline float ms_touchpadPrevX;
static inline float ms_touchpadPrevY;
public:
static void Update(float deltaTime)
{
/* NOTE (Hyper): this code was written at 144Hz and was
discovered later to be faulty at any other frame rate,
so this is here to account for that without changing
all the constants that I had tuned. */
constexpr auto referenceDeltaTime = 1.0f / 144.0f;
if (g_isTouchActive)
{
constexpr auto sensitivity = WORLD_MAP_TOUCH_SENSITIVITY_MULTIPLIER;
auto dxNorm = ms_touchpadDeltaX / referenceDeltaTime;
auto dyNorm = ms_touchpadDeltaY / referenceDeltaTime;
auto dxSens = dxNorm * sensitivity;
auto dySens = dyNorm * sensitivity;
auto smoothing = powf(WORLD_MAP_TOUCH_SMOOTHING_FACTOR, deltaTime / referenceDeltaTime);
g_worldMapTouchVelocityX = smoothing * g_worldMapTouchVelocityX + (1.0f - smoothing) * dxSens;
g_worldMapTouchVelocityY = smoothing * g_worldMapTouchVelocityY + (1.0f - smoothing) * dySens;
constexpr auto flickThreshold = WORLD_MAP_TOUCH_FLICK_THRESHOLD;
if (fabs(dxSens) > flickThreshold || fabs(dySens) > flickThreshold)
{
constexpr auto flickAccelX = WORLD_MAP_TOUCH_FLICK_ACCELERATION_X;
constexpr auto flickAccelY = WORLD_MAP_TOUCH_FLICK_ACCELERATION_Y;
g_worldMapTouchVelocityX += dxNorm * flickAccelX * (deltaTime / referenceDeltaTime);
g_worldMapTouchVelocityY += dyNorm * flickAccelY * (deltaTime / referenceDeltaTime);
}
constexpr auto terminalVelocity = WORLD_MAP_TOUCH_FLICK_TERMINAL_VELOCITY;
g_worldMapTouchVelocityX = std::clamp(g_worldMapTouchVelocityX, -terminalVelocity, terminalVelocity);
g_worldMapTouchVelocityY = std::clamp(g_worldMapTouchVelocityY, -terminalVelocity, terminalVelocity);
}
else
{
auto dampingFactor = powf(WORLD_MAP_TOUCH_DAMPING_FACTOR, deltaTime / referenceDeltaTime);
g_worldMapTouchVelocityX *= dampingFactor;
g_worldMapTouchVelocityY *= dampingFactor;
}
}
void OnSDLEvent(SDL_Event* event) override
{
switch (event->type)
{
case SDL_CONTROLLERTOUCHPADMOTION:
{
g_isTouchActive = true;
if (ms_touchpadFingerCount > 1)
{
g_isTouchActive = false;
break;
}
ms_touchpadX = event->ctouchpad.x;
ms_touchpadY = event->ctouchpad.y;
ms_touchpadDeltaX = ms_touchpadX - ms_touchpadPrevX;
ms_touchpadDeltaY = ms_touchpadY - ms_touchpadPrevY;
ms_touchpadPrevX = ms_touchpadX;
ms_touchpadPrevY = ms_touchpadY;
break;
}
case SDL_CONTROLLERTOUCHPADDOWN:
ms_touchpadFingerCount++;
ms_touchpadPrevX = event->ctouchpad.x;
ms_touchpadPrevY = event->ctouchpad.y;
break;
case SDL_CONTROLLERTOUCHPADUP:
g_isTouchActive = false;
ms_touchpadFingerCount--;
break;
}
}
}
g_sdlEventListenerForInputPatches;
// -------------- COMMON --------------- //
static bool IsDPadActive(SWA::SPadState* pPadState)
{
return pPadState->IsDown(SWA::eKeyState_DpadUp) ||
pPadState->IsDown(SWA::eKeyState_DpadDown) ||
pPadState->IsDown(SWA::eKeyState_DpadLeft) ||
pPadState->IsDown(SWA::eKeyState_DpadRight);
}
static void SetDPadAnalogDirectionX(PPCRegister& pPadState, PPCRegister& x, bool invert, float max = 1.0f)
{
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (pGuestPadState->IsDown(SWA::eKeyState_DpadLeft))
x.f64 = negate ? 1.0f : -1.0f;
x.f64 = invert ? max : -max;
if (pGuestPadState->IsDown(SWA::eKeyState_DpadRight))
x.f64 = negate ? -1.0f : 1.0f;
x.f64 = invert ? -max : max;
}
static void SetDPadAnalogDirectionY(PPCRegister& pPadState, PPCRegister& y, bool negate)
static void SetDPadAnalogDirectionY(PPCRegister& pPadState, PPCRegister& y, bool invert, float max = 1.0f)
{
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (pGuestPadState->IsDown(SWA::eKeyState_DpadUp))
y.f64 = negate ? -1.0f : 1.0f;
y.f64 = invert ? -max : max;
if (pGuestPadState->IsDown(SWA::eKeyState_DpadDown))
y.f64 = negate ? 1.0f : -1.0f;
y.f64 = invert ? max : -max;
}
// -------------- PLAYER --------------- //
void PostureDPadSupportMidAsmHook(PPCRegister& pPadState, PPCRegister& x, PPCRegister& y)
{
SetDPadAnalogDirectionX(pPadState, x, false);
SetDPadAnalogDirectionY(pPadState, y, false);
}
void PostureDPadSupportInvertYMidAsmHook(PPCRegister& pPadState, PPCRegister& x, PPCRegister& y)
{
SetDPadAnalogDirectionX(pPadState, x, false);
SetDPadAnalogDirectionY(pPadState, y, true);
@ -38,8 +171,148 @@ void PostureDPadSupportYMidAsmHook(PPCRegister& pPadState, PPCRegister& y)
SetDPadAnalogDirectionY(pPadState, y, false);
}
void PostureDPadSupportPathLocalMidAsmHook(PPCRegister& pPadState, PPCRegister& x, PPCRegister& y)
void PostureSpaceHurrierDPadSupportXMidAsmHook(PPCRegister& pPadState, PPCVRegister& vector)
{
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (pGuestPadState->IsDown(SWA::eKeyState_DpadLeft))
vector.f32[3] = -1.0f;
if (pGuestPadState->IsDown(SWA::eKeyState_DpadRight))
vector.f32[3] = 1.0f;
}
void PostureSpaceHurrierDPadSupportYMidAsmHook(PPCRegister& pPadState, PPCVRegister& vector)
{
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (pGuestPadState->IsDown(SWA::eKeyState_DpadUp))
vector.f32[3] = 1.0f;
if (pGuestPadState->IsDown(SWA::eKeyState_DpadDown))
vector.f32[3] = -1.0f;
}
// ------------- WORLD MAP ------------- //
bool WorldMapTouchSupportMidAsmHook()
{
SDLEventListenerForInputPatches::Update(App::s_deltaTime);
return fabs(g_worldMapTouchVelocityX) > 0 || fabs(g_worldMapTouchVelocityY) > 0;
}
bool WorldMapTouchMagnetismSupportMidAsmHook(PPCRegister& f0)
{
return fabs(g_worldMapTouchVelocityX) > f0.f64 || fabs(g_worldMapTouchVelocityY) > f0.f64;
}
void TouchAndDPadSupportWorldMapXMidAsmHook(PPCRegister& pPadState, PPCRegister& x)
{
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (fabs(pGuestPadState->LeftStickHorizontal) > WORLD_MAP_TOUCH_CANCEL_DEADZONE ||
fabs(pGuestPadState->LeftStickVertical) > WORLD_MAP_TOUCH_CANCEL_DEADZONE)
{
g_worldMapTouchVelocityX = 0;
}
if (IsDPadActive(pGuestPadState))
{
g_worldMapTouchVelocityX = 0;
SetDPadAnalogDirectionX(pPadState, x, false);
}
else
{
if (fabs(g_worldMapTouchVelocityX) > 0)
x.f64 = -g_worldMapTouchVelocityX;
}
}
void TouchAndDPadSupportWorldMapYMidAsmHook(PPCRegister& pPadState, PPCRegister& y)
{
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
if (fabs(pGuestPadState->LeftStickHorizontal) > WORLD_MAP_TOUCH_CANCEL_DEADZONE ||
fabs(pGuestPadState->LeftStickVertical) > WORLD_MAP_TOUCH_CANCEL_DEADZONE)
{
g_worldMapTouchVelocityY = 0;
}
if (IsDPadActive(pGuestPadState))
{
g_worldMapTouchVelocityY = 0;
SetDPadAnalogDirectionY(pPadState, y, false);
}
else
{
if (fabs(g_worldMapTouchVelocityY) > 0)
y.f64 = g_worldMapTouchVelocityY;
}
}
/* This hook is unique as it is created after a label that is branched to
if input should be prohibited, resulting in the pad state being a nullptr.
We check the condition that enables that branch here for safety. */
void TouchAndDPadSupportWorldMapMagnetismXMidAsmHook(PPCRegister& pPadState, PPCRegister& x, PPCRegister& isInputProhibited)
{
if (isInputProhibited.u8 || !pPadState.u32)
return;
TouchAndDPadSupportWorldMapXMidAsmHook(pPadState, x);
}
// SWA::CWorldMapCamera::Update
PPC_FUNC_IMPL(__imp__sub_82486968);
PPC_FUNC(sub_82486968)
{
auto pWorldMapCamera = (SWA::CWorldMapCamera*)g_memory.Translate(ctx.r3.u32);
// Reset vertical velocity if maximum pitch reached.
if (fabs(pWorldMapCamera->m_Pitch) >= 80.0f)
g_worldMapTouchVelocityY = 0;
__imp__sub_82486968(ctx, base);
}
// World Map cursor move hook.
PPC_FUNC_IMPL(__imp__sub_8256C938);
PPC_FUNC(sub_8256C938)
{
auto pWorldMapCursor = (SWA::CWorldMapCursor*)g_memory.Translate(ctx.r3.u32);
pWorldMapCursor->m_IsCursorMoving = g_isTouchActive;
if (ctx.r4.u8)
{
pWorldMapCursor->m_LeftStickVertical = 0;
pWorldMapCursor->m_LeftStickHorizontal = 0;
}
else if (auto pInputState = SWA::CInputState::GetInstance())
{
auto& rPadState = pInputState->GetPadState();
pWorldMapCursor->m_LeftStickVertical = rPadState.LeftStickVertical;
pWorldMapCursor->m_LeftStickHorizontal = rPadState.LeftStickHorizontal;
if (rPadState.IsDown(SWA::eKeyState_DpadUp))
pWorldMapCursor->m_LeftStickVertical = 1.0f;
if (rPadState.IsDown(SWA::eKeyState_DpadDown))
pWorldMapCursor->m_LeftStickVertical = -1.0f;
if (rPadState.IsDown(SWA::eKeyState_DpadLeft))
pWorldMapCursor->m_LeftStickHorizontal = -1.0f;
if (rPadState.IsDown(SWA::eKeyState_DpadRight))
pWorldMapCursor->m_LeftStickHorizontal = 1.0f;
if (sqrtf((pWorldMapCursor->m_LeftStickHorizontal * pWorldMapCursor->m_LeftStickHorizontal) +
(pWorldMapCursor->m_LeftStickVertical * pWorldMapCursor->m_LeftStickVertical)) > 0.7f)
{
pWorldMapCursor->m_IsCursorMoving = true;
}
}
}

View file

@ -550,25 +550,25 @@ address = 0x824DC9D4
# CPlayerSpeedPostureInputOnPath
[[midasm_hook]]
name = "PostureDPadSupportMidAsmHook"
name = "PostureDPadSupportInvertYMidAsmHook"
address = 0x8234F194
registers = ["r31", "f13", "f0"]
# CPlayerSpeedPostureInputOnPathLocal
[[midasm_hook]]
name = "PostureDPadSupportPathLocalMidAsmHook"
name = "PostureDPadSupportMidAsmHook"
address = 0x8234F610
registers = ["r30", "f0", "f13"]
# CPlayerSpeedPostureInput3DStandard
[[midasm_hook]]
name = "PostureDPadSupportMidAsmHook"
name = "PostureDPadSupportInvertYMidAsmHook"
address = 0x8234EEE8
registers = ["r31", "f12", "f13"]
# CEvilPostureInputStandard
[[midasm_hook]]
name = "PostureDPadSupportMidAsmHook"
name = "PostureDPadSupportInvertYMidAsmHook"
address = 0x823CDA60
registers = ["r3", "f11", "f12"]
@ -580,9 +580,94 @@ registers = ["r3", "f0"]
# CEvilPostureInputStandard
[[midasm_hook]]
name = "PostureDPadSupportYMidAsmHook"
address = 0x823CDA88
registers = ["r3", "f12"]
name = "PostureDPadSupportXMidAsmHook"
address = 0x823CDA74
registers = ["r3", "f0"]
# SWA::CObjBobsleigh::CStateMode3D
[[midasm_hook]]
name = "PostureDPadSupportXMidAsmHook"
address = 0x8266B5F0
registers = ["r29", "f13"]
# SWA::CObjBobsleigh::CStateMode3D
[[midasm_hook]]
name = "PostureDPadSupportXMidAsmHook"
address = 0x8266B8B4
registers = ["r29", "f0"]
# SWA::CObjBobsleigh::CStateMode3D
[[midasm_hook]]
name = "PostureDPadSupportXMidAsmHook"
address = 0x8266B618
registers = ["r29", "f0"]
# SWA::CObjBobsleigh::CStateMode3D
[[midasm_hook]]
name = "PostureDPadSupportXMidAsmHook"
address = 0x8266B6AC
registers = ["r29", "f0"]
# CSuperSonicPostureInputSpaceHurrier
[[midasm_hook]]
name = "PostureSpaceHurrierDPadSupportXMidAsmHook"
address = 0x82455DD8
registers = ["r30", "v61"]
# CSuperSonicPostureInputSpaceHurrier
[[midasm_hook]]
name = "PostureSpaceHurrierDPadSupportYMidAsmHook"
address = 0x82455DC8
registers = ["r30", "v63"]
# CWorldMapCamera - disable rotation deadzone for touch
[[midasm_hook]]
name = "WorldMapTouchSupportMidAsmHook"
address = 0x824862EC
jump_address_on_true = 0x824862F0
# CWorldMapCamera - disable flag magnetism for touch
[[midasm_hook]]
name = "WorldMapTouchMagnetismSupportMidAsmHook"
address = 0x824866D4
registers = ["f0"]
jump_address_on_true = 0x82486838
# CWorldMapCamera - touch and D-Pad support for camera adjustment threshold on the X axis
[[midasm_hook]]
name = "TouchAndDPadSupportWorldMapXMidAsmHook"
address = 0x824862D8
registers = ["r30", "f12"]
# CWorldMapCamera - touch and D-Pad support for adjusing camera yaw
[[midasm_hook]]
name = "TouchAndDPadSupportWorldMapXMidAsmHook"
address = 0x82486318
registers = ["r30", "f12"]
# CWorldMapCamera - touch and D-Pad support for camera adjustment threshold on the Y axis
[[midasm_hook]]
name = "TouchAndDPadSupportWorldMapYMidAsmHook"
address = 0x824862CC
registers = ["r30", "f0"]
# CWorldMapCamera - touch and D-Pad support for adjusing camera pitch
[[midasm_hook]]
name = "TouchAndDPadSupportWorldMapYMidAsmHook"
address = 0x824862F4
registers = ["r30", "f0"]
# CWorldMapCamera - touch and D-Pad support for flag magnetism on the X axis
[[midasm_hook]]
name = "TouchAndDPadSupportWorldMapMagnetismXMidAsmHook"
address = 0x82486660
registers = ["r27", "f29", "r28"]
# CWorldMapCamera - touch and D-Pad support for flag magnetism on the Y axis
[[midasm_hook]]
name = "TouchAndDPadSupportWorldMapYMidAsmHook"
address = 0x8248665C
registers = ["r27", "f28"]
[[midasm_hook]]
name = "LoadingUpdateMidAsmHook"