mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2025-12-31 08:21:11 -06:00
455 lines
13 KiB
C++
455 lines
13 KiB
C++
#include <api/SWA.h>
|
|
#include <hid/hid.h>
|
|
#include <app.h>
|
|
#include <exports.h>
|
|
#include <sdl_listener.h>
|
|
|
|
constexpr double WORLD_MAP_ROTATE_DEADZONE = 0.69999999;
|
|
constexpr double WORLD_MAP_CURSOR_DEADZONE = 0.30000001;
|
|
|
|
class WorldMapCursorParams
|
|
{
|
|
public:
|
|
float Damping{ 0.99f };
|
|
float FlickAccelX{ 0.25f };
|
|
float FlickAccelY{ 0.1f };
|
|
float FlickTerminalVelocity{ 40.0f };
|
|
float FlickThreshold{ 2.25f };
|
|
float SensitivityX{};
|
|
float SensitivityY{};
|
|
float Smoothing{ 0.8f };
|
|
};
|
|
|
|
class WorldMapCursorParamsProspero : public WorldMapCursorParams
|
|
{
|
|
public:
|
|
WorldMapCursorParamsProspero()
|
|
{
|
|
SensitivityX = 1.15f;
|
|
SensitivityY = 1.05f;
|
|
}
|
|
}
|
|
g_worldMapCursorParamsProspero;
|
|
|
|
class WorldMapCursorParamsOrbis : public WorldMapCursorParams
|
|
{
|
|
public:
|
|
WorldMapCursorParamsOrbis()
|
|
{
|
|
SensitivityX = 0.95f;
|
|
SensitivityY = 1.0f;
|
|
}
|
|
}
|
|
g_worldMapCursorParamsOrbis;
|
|
|
|
#ifdef UNLEASHED_RECOMP_UI_KBM_SUPPORT
|
|
class WorldMapCursorParamsMouse : public WorldMapCursorParams
|
|
{
|
|
public:
|
|
WorldMapCursorParamsMouse()
|
|
{
|
|
FlickAccelX = 0.025f;
|
|
FlickAccelY = 0.025f;
|
|
FlickThreshold = 7.5f;
|
|
SensitivityX = 0.15f;
|
|
SensitivityY = 0.15f;
|
|
}
|
|
}
|
|
g_worldMapCursorParamsMouse;
|
|
#endif
|
|
|
|
WorldMapCursorParams g_worldMapCursorParams{};
|
|
|
|
static bool g_isCursorActive;
|
|
|
|
static float g_worldMapCursorVelocityX;
|
|
static float g_worldMapCursorVelocityY;
|
|
|
|
class SDLEventListenerForInputPatches : public SDLEventListener
|
|
{
|
|
static inline bool ms_isMouseDown;
|
|
|
|
static inline int ms_touchpadFingerCount;
|
|
|
|
static inline float ms_cursorX;
|
|
static inline float ms_cursorY;
|
|
static inline float ms_cursorDeltaX;
|
|
static inline float ms_cursorDeltaY;
|
|
static inline float ms_cursorPrevX;
|
|
static inline float ms_cursorPrevY;
|
|
|
|
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_isCursorActive)
|
|
{
|
|
auto dxNorm = ms_cursorDeltaX / referenceDeltaTime;
|
|
auto dyNorm = ms_cursorDeltaY / referenceDeltaTime;
|
|
auto dxSens = dxNorm * g_worldMapCursorParams.SensitivityX;
|
|
auto dySens = dyNorm * g_worldMapCursorParams.SensitivityY;
|
|
|
|
auto smoothing = powf(g_worldMapCursorParams.Smoothing, deltaTime / referenceDeltaTime);
|
|
|
|
g_worldMapCursorVelocityX = smoothing * g_worldMapCursorVelocityX + (1.0f - smoothing) * dxSens;
|
|
g_worldMapCursorVelocityY = smoothing * g_worldMapCursorVelocityY + (1.0f - smoothing) * dySens;
|
|
|
|
auto flickThreshold = g_worldMapCursorParams.FlickThreshold;
|
|
|
|
if (fabs(dxSens) > flickThreshold || fabs(dySens) > flickThreshold)
|
|
{
|
|
g_worldMapCursorVelocityX += dxNorm * g_worldMapCursorParams.FlickAccelX * (deltaTime / referenceDeltaTime);
|
|
g_worldMapCursorVelocityY += dyNorm * g_worldMapCursorParams.FlickAccelY * (deltaTime / referenceDeltaTime);
|
|
}
|
|
|
|
auto terminalVelocity = g_worldMapCursorParams.FlickTerminalVelocity;
|
|
|
|
g_worldMapCursorVelocityX = std::clamp(g_worldMapCursorVelocityX, -terminalVelocity, terminalVelocity);
|
|
g_worldMapCursorVelocityY = std::clamp(g_worldMapCursorVelocityY, -terminalVelocity, terminalVelocity);
|
|
}
|
|
else
|
|
{
|
|
auto dampingFactor = powf(g_worldMapCursorParams.Damping, deltaTime / referenceDeltaTime);
|
|
|
|
g_worldMapCursorVelocityX *= dampingFactor;
|
|
g_worldMapCursorVelocityY *= dampingFactor;
|
|
}
|
|
}
|
|
|
|
bool OnSDLEvent(SDL_Event* event) override
|
|
{
|
|
if (!hid::IsInputAllowed())
|
|
return false;
|
|
|
|
switch (event->type)
|
|
{
|
|
#ifdef UNLEASHED_RECOMP_UI_KBM_SUPPORT
|
|
case SDL_MOUSEMOTION:
|
|
{
|
|
if (!ms_isMouseDown)
|
|
break;
|
|
|
|
g_isCursorActive = true;
|
|
|
|
ms_cursorDeltaX = (float)event->motion.xrel / 100.0f;
|
|
ms_cursorDeltaY = (float)event->motion.yrel / 100.0f;
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
{
|
|
if (event->button.button == SDL_BUTTON_LEFT)
|
|
{
|
|
g_worldMapCursorParams = g_worldMapCursorParamsMouse;
|
|
ms_isMouseDown = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
|
{
|
|
if (event->button.button == SDL_BUTTON_LEFT)
|
|
{
|
|
g_isCursorActive = false;
|
|
ms_isMouseDown = false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
case SDL_CONTROLLERTOUCHPADMOTION:
|
|
{
|
|
g_isCursorActive = true;
|
|
|
|
if (ms_touchpadFingerCount > 1)
|
|
{
|
|
g_isCursorActive = false;
|
|
break;
|
|
}
|
|
|
|
ms_cursorX = event->ctouchpad.x;
|
|
ms_cursorY = event->ctouchpad.y;
|
|
ms_cursorDeltaX = ms_cursorX - ms_cursorPrevX;
|
|
ms_cursorDeltaY = ms_cursorY - ms_cursorPrevY;
|
|
ms_cursorPrevX = ms_cursorX;
|
|
ms_cursorPrevY = ms_cursorY;
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_CONTROLLERTOUCHPADDOWN:
|
|
{
|
|
g_worldMapCursorParams = hid::g_inputDeviceExplicit == hid::EInputDeviceExplicit::DualSense
|
|
? (WorldMapCursorParams)g_worldMapCursorParamsProspero
|
|
: (WorldMapCursorParams)g_worldMapCursorParamsOrbis;
|
|
|
|
ms_touchpadFingerCount++;
|
|
ms_cursorPrevX = event->ctouchpad.x;
|
|
ms_cursorPrevY = event->ctouchpad.y;
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_CONTROLLERTOUCHPADUP:
|
|
g_isCursorActive = false;
|
|
ms_touchpadFingerCount--;
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
g_sdlEventListenerForInputPatches;
|
|
|
|
// -------------- COMMON --------------- //
|
|
|
|
static bool IsDPadThreshold(const SWA::SPadState* pPadState)
|
|
{
|
|
if (Config::DisableDPadMovement)
|
|
return false;
|
|
|
|
return pPadState->IsDown(SWA::eKeyState_DpadUp) ||
|
|
pPadState->IsDown(SWA::eKeyState_DpadDown) ||
|
|
pPadState->IsDown(SWA::eKeyState_DpadLeft) ||
|
|
pPadState->IsDown(SWA::eKeyState_DpadRight);
|
|
}
|
|
|
|
static bool IsLeftStickThreshold(const SWA::SPadState* pPadState, double deadzone = 0)
|
|
{
|
|
return sqrtl((pPadState->LeftStickHorizontal * pPadState->LeftStickHorizontal) +
|
|
(pPadState->LeftStickVertical * pPadState->LeftStickVertical)) > deadzone;
|
|
}
|
|
|
|
static bool IsCursorThreshold(double deadzone = 0, bool isBelowThreshold = false)
|
|
{
|
|
auto sqrt = sqrtl((g_worldMapCursorVelocityX * g_worldMapCursorVelocityX) +
|
|
(g_worldMapCursorVelocityY * g_worldMapCursorVelocityY));
|
|
|
|
if (isBelowThreshold)
|
|
return sqrt < deadzone;
|
|
|
|
return sqrt >= deadzone;
|
|
}
|
|
|
|
static void SetDPadAnalogDirectionX(PPCRegister& pPadState, PPCRegister& x, bool invert, float max = 1.0f)
|
|
{
|
|
if (Config::DisableDPadMovement)
|
|
return;
|
|
|
|
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
|
|
|
|
if (pGuestPadState->IsDown(SWA::eKeyState_DpadLeft))
|
|
x.f64 = invert ? max : -max;
|
|
|
|
if (pGuestPadState->IsDown(SWA::eKeyState_DpadRight))
|
|
x.f64 = invert ? -max : max;
|
|
}
|
|
|
|
static void SetDPadAnalogDirectionY(PPCRegister& pPadState, PPCRegister& y, bool invert, float max = 1.0f)
|
|
{
|
|
if (Config::DisableDPadMovement)
|
|
return;
|
|
|
|
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
|
|
|
|
if (pGuestPadState->IsDown(SWA::eKeyState_DpadUp))
|
|
y.f64 = invert ? -max : max;
|
|
|
|
if (pGuestPadState->IsDown(SWA::eKeyState_DpadDown))
|
|
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);
|
|
}
|
|
|
|
void PostureDPadSupportXMidAsmHook(PPCRegister& pPadState, PPCRegister& x)
|
|
{
|
|
SetDPadAnalogDirectionX(pPadState, x, false);
|
|
}
|
|
|
|
void PostureDPadSupportYMidAsmHook(PPCRegister& pPadState, PPCRegister& y)
|
|
{
|
|
SetDPadAnalogDirectionY(pPadState, y, false);
|
|
}
|
|
|
|
void PostureSpaceHurrierDPadSupportXMidAsmHook(PPCRegister& pPadState, PPCVRegister& vector)
|
|
{
|
|
if (Config::DisableDPadMovement)
|
|
return;
|
|
|
|
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)
|
|
{
|
|
if (Config::DisableDPadMovement)
|
|
return;
|
|
|
|
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;
|
|
}
|
|
|
|
void SetXButtonHomingMidAsmHook(PPCRegister& r1)
|
|
{
|
|
auto pXButtonHoming = (bool*)(g_memory.base + r1.u32 + 0x63);
|
|
|
|
*pXButtonHoming = !Config::HomingAttackOnJump;
|
|
}
|
|
|
|
bool IsHomingAttackOnJump()
|
|
{
|
|
return Config::HomingAttackOnJump;
|
|
}
|
|
|
|
// ------------- WORLD MAP ------------- //
|
|
|
|
bool WorldMapDeadzoneMidAsmHook(PPCRegister& pPadState)
|
|
{
|
|
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
|
|
|
|
if (IsDPadThreshold(pGuestPadState) || IsLeftStickThreshold(pGuestPadState))
|
|
{
|
|
g_worldMapCursorVelocityX = 0;
|
|
g_worldMapCursorVelocityY = 0;
|
|
}
|
|
else
|
|
{
|
|
SDLEventListenerForInputPatches::Update(App::s_deltaTime);
|
|
|
|
/* Reduce noise if the cursor is resting in
|
|
place, but allow much precise values for
|
|
proper interpolation to zero. */
|
|
if (IsCursorThreshold(0.05, true))
|
|
return !g_isCursorActive;
|
|
|
|
return IsCursorThreshold();
|
|
}
|
|
|
|
return IsDPadThreshold(pGuestPadState) || IsLeftStickThreshold(pGuestPadState, WORLD_MAP_ROTATE_DEADZONE);
|
|
}
|
|
|
|
bool WorldMapMagnetismMidAsmHook(PPCRegister& f0)
|
|
{
|
|
if (IsCursorThreshold(f0.f64, true))
|
|
{
|
|
g_worldMapCursorVelocityX = 0;
|
|
g_worldMapCursorVelocityY = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void WorldMapHidSupportXMidAsmHook(PPCRegister& pPadState, PPCRegister& x)
|
|
{
|
|
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
|
|
|
|
if (IsDPadThreshold(pGuestPadState))
|
|
{
|
|
SetDPadAnalogDirectionX(pPadState, x, false);
|
|
}
|
|
else if (fabs(g_worldMapCursorVelocityX) > 0)
|
|
{
|
|
x.f64 = -g_worldMapCursorVelocityX;
|
|
}
|
|
}
|
|
|
|
void WorldMapHidSupportYMidAsmHook(PPCRegister& pPadState, PPCRegister& y)
|
|
{
|
|
auto pGuestPadState = (SWA::SPadState*)g_memory.Translate(pPadState.u32);
|
|
|
|
if (IsDPadThreshold(pGuestPadState))
|
|
{
|
|
SetDPadAnalogDirectionY(pPadState, y, false);
|
|
}
|
|
else if (fabs(g_worldMapCursorVelocityY) > 0)
|
|
{
|
|
y.f64 = g_worldMapCursorVelocityY;
|
|
}
|
|
}
|
|
|
|
// 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_worldMapCursorVelocityY = 0;
|
|
|
|
__imp__sub_82486968(ctx, base);
|
|
}
|
|
|
|
// World Map cursor move hook.
|
|
PPC_FUNC(sub_8256C938)
|
|
{
|
|
auto pWorldMapCursor = (SWA::CWorldMapCursor*)g_memory.Translate(ctx.r3.u32);
|
|
|
|
pWorldMapCursor->m_IsCursorMoving = g_isCursorActive && IsCursorThreshold(1.0);
|
|
|
|
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 (!Config::DisableDPadMovement)
|
|
{
|
|
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 (sqrtl((pWorldMapCursor->m_LeftStickHorizontal * pWorldMapCursor->m_LeftStickHorizontal) +
|
|
(pWorldMapCursor->m_LeftStickVertical * pWorldMapCursor->m_LeftStickVertical)) > WORLD_MAP_ROTATE_DEADZONE)
|
|
{
|
|
pWorldMapCursor->m_IsCursorMoving = true;
|
|
}
|
|
}
|
|
}
|