diff --git a/soh/soh/Enhancements/controls/Mouse.cpp b/soh/soh/Enhancements/controls/Mouse.cpp new file mode 100644 index 000000000..ae3842a1a --- /dev/null +++ b/soh/soh/Enhancements/controls/Mouse.cpp @@ -0,0 +1,153 @@ +#include "Mouse.h" +#include "soh/OTRGlobals.h" +#include "z64player.h" +#include "global.h" +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +static Ship::Coords mouseCoord = {}; +static Ship::Coords mouseCoordRel = {}; + +#define CVAR_ENABLE_MOUSE_NAME CVAR_SETTING("EnableMouse") +#define CVAR_ENABLE_MOUSE_DEFAULT 0 +#define CVAR_ENABLE_MOUSE_VALUE CVarGetInteger(CVAR_ENABLE_MOUSE_NAME, CVAR_ENABLE_MOUSE_DEFAULT) +#define MOUSE_ENABLED (CVAR_ENABLE_MOUSE_VALUE && GetWindow()->IsMouseCaptured()) + +std::shared_ptr GetWindow() { + return OTRGlobals::Instance->context->GetWindow(); +} + +extern "C" { +void Mouse_UpdatePos() { + mouseCoord = GetWindow()->GetMousePos(); +} + +void Mouse_UpdatePosRel() { + mouseCoordRel = GetWindow()->GetMouseDelta(); +} + +void Mouse_UpdateAll() { + Mouse_UpdatePos(); + Mouse_UpdatePosRel(); +} + +void Mouse_HandleThirdPerson(f32* newCamX, f32* newCamY) { + if (MOUSE_ENABLED) { + *newCamX -= mouseCoordRel.x * 40.0f; + *newCamY += mouseCoordRel.y * 40.0f; + } +} + +void Mouse_HandleFirstPerson(Player* player) { + f32 xAxisMulti = CVarGetFloat(CVAR_SETTING("FirstPersonCameraSensitivity.X"), 1.0f); + f32 yAxisMulti = CVarGetFloat(CVAR_SETTING("FirstPersonCameraSensitivity.Y"), 1.0f); + s8 invertXAxisMulti = ((CVarGetInteger(CVAR_SETTING("Controls.InvertAimingXAxis"), 0) + && !CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0)) + || (!CVarGetInteger(CVAR_SETTING("Controls.InvertAimingXAxis"), 0) + && CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0))) ? -1 : 1; + s8 invertYAxisMulti = CVarGetInteger(CVAR_SETTING("Controls.InvertAimingYAxis"), 1) ? 1 : -1; + if (MOUSE_ENABLED) { + player->actor.focus.rot.y -= mouseCoordRel.x * 6.0f * xAxisMulti * invertXAxisMulti; + player->actor.focus.rot.x += mouseCoordRel.y * 6.0f * yAxisMulti * invertYAxisMulti; + } +} + +void Mouse_RecenterCursor() { + u32 width = GetWindow()->GetWidth(); + u32 height = GetWindow()->GetHeight(); + if (MOUSE_ENABLED) { + GetWindow()->SetMousePos({(s32) (width/2), (s32) (height/2)}); + } +} + +void Mouse_HandleShield(f32* sp50, f32* sp54) { + if (MOUSE_ENABLED) { + s32 width = GetWindow()->GetWidth(); + s32 height = GetWindow()->GetHeight(); + f32 xBound = 7200 / ((f32)width / 2); + f32 yBound = 6000 / ((f32)height / 2); + *sp50 += (mouseCoord.x - (width / 2)) * xBound * (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? 1 : -1); + *sp54 += (mouseCoord.y - (height / 2)) * yBound; + *sp50 = CLAMP(*sp50, -7200, 7200); + *sp54 = CLAMP(*sp54, -6000, 6000); + } +} + +static s8 iterMouse = 0; +static f32 mouseQuickspinX[5] = {}; +static f32 mouseQuickspinY[5] = {}; +static u8 quickspinCount = 0; + +void Mouse_UpdateQuickspinCount() { + if (MOUSE_ENABLED) { + quickspinCount = (quickspinCount + 1) % 5; + mouseQuickspinX[quickspinCount] = mouseCoord.x; + mouseQuickspinY[quickspinCount] = mouseCoord.y; + } else { + quickspinCount = 0; + } +} + +bool Mouse_HandleQuickspin(bool* should, s8* iter2, s8* sp3C) { + s8 temp1; + s8 temp2; + s32 i; + if (!MOUSE_ENABLED) { + return *should = false; + } + + for (i = 0; i < 4; i++, iter2++) { + // Calculating angles as per z_lib.c:func_80077D10() + f32 relY = mouseQuickspinY[i + 1] - mouseQuickspinY[i]; + f32 relX = mouseQuickspinX[i + 1] - mouseQuickspinX[i]; + s16 aTan = Math_Atan2S(relY, -relX); + iterMouse = (u16)(aTan + 0x2000) >> 9; // See z_player.c:Player_ProcessControlStick() + if ((*iter2 = iterMouse) < 0) { + return *should = false; + } + *iter2 *= 2; + } + temp1 = sp3C[0] - sp3C[1]; + if (ABS(temp1) < 10) { + return *should = false; + } + iter2 = &sp3C[1]; + for (i = 1; i < 3; i++, iter2++) { + temp2 = *iter2 - *(iter2 + 1); + if ((ABS(temp2) < 10) || (temp2 * temp1 < 0)) { + return *should = false; + } + } + + return *should = true; +} + +// Hook handlers + +void Mouse_RegisterRecenterCursorOnShield() { + COND_HOOK(OnPlayerHoldUpShield, true, Mouse_RecenterCursor); +} + +void Mouse_RegisterHandleFirstPerson() { + COND_HOOK(OnPlayerFirstPersonControl, true, Mouse_HandleFirstPerson); +} + +void Mouse_RegisterHandleShield() { + COND_HOOK(OnPlayerShieldControl, true, Mouse_HandleShield); +} + +void Mouse_RegisterUpdateQuickspinCount() { + COND_HOOK(OnPlayerProcessStick, true, Mouse_UpdateQuickspinCount); +} + +void Mouse_RegisterHandleQuickspin() { + REGISTER_VB_SHOULD(VB_SHOULD_QUICKSPIN, { Mouse_HandleQuickspin(should, va_arg(args, s8*), va_arg(args, s8*)); } ); +} + +static RegisterShipInitFunc initFunc_shieldRecenter(Mouse_RegisterRecenterCursorOnShield, { CVAR_ENABLE_MOUSE_NAME }); +static RegisterShipInitFunc initFunc_firstPerson(Mouse_RegisterHandleFirstPerson, { CVAR_ENABLE_MOUSE_NAME }); +static RegisterShipInitFunc initFunc_quickspinCount(Mouse_RegisterUpdateQuickspinCount, { CVAR_ENABLE_MOUSE_NAME }); +static RegisterShipInitFunc initFunc_quickspin(Mouse_RegisterHandleQuickspin, { CVAR_ENABLE_MOUSE_NAME }); +static RegisterShipInitFunc initFunc_shieldMove(Mouse_RegisterHandleShield, { CVAR_ENABLE_MOUSE_NAME }); +} //extern "C" diff --git a/soh/soh/Enhancements/controls/Mouse.h b/soh/soh/Enhancements/controls/Mouse.h new file mode 100644 index 000000000..38d84e078 --- /dev/null +++ b/soh/soh/Enhancements/controls/Mouse.h @@ -0,0 +1,25 @@ +#ifndef MOUSE_H +#define MOUSE_H + +#pragma once + +#include + +struct Player; + +#ifdef __cplusplus +extern "C" { +#endif +void Mouse_UpdateAll(); +void Mouse_RecenterCursor(); +void Mouse_HandleThirdPerson(f32* newCamX, f32* newCamY); +void Mouse_HandleFirstPerson(struct Player* player); +void Mouse_HandleShield(f32* sp50, f32* sp54); +bool Mouse_HandleQuickspin(bool* should, s8* iter2, s8* sp3C); +void Mouse_UpdateQuickspinCount(); +#ifdef __cplusplus +}; //extern "C" +#endif + +//MOUSE_H +#endif diff --git a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp index 7dbc74a75..dcef762cf 100644 --- a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp +++ b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp @@ -172,8 +172,8 @@ void SohInputEditorWindow::DrawAnalogPreview(const char* label, ImVec2 stick, fl #define BUTTON_COLOR_KEYBOARD_BEIGE ImVec4(0.651f, 0.482f, 0.357f, 0.5f) #define BUTTON_COLOR_KEYBOARD_BEIGE_HOVERED ImVec4(0.651f, 0.482f, 0.357f, 1.0f) -#define BUTTON_COLOR_MOUSE_BEIGE ImVec4(0.5f, 0.5f, 0.5f, 0.5f) -#define BUTTON_COLOR_MOUSE_BEIGE_HOVERED ImVec4(0.5f, 0.5f, 0.5f, 1.0f) +#define BUTTON_COLOR_MOUSE_GRAY ImVec4(0.5f, 0.5f, 0.5f, 0.5f) +#define BUTTON_COLOR_MOUSE_GRAY_HOVERED ImVec4(0.5f, 0.5f, 0.5f, 1.0f) #define BUTTON_COLOR_GAMEPAD_BLUE ImVec4(0.0f, 0.255f, 0.976f, 0.5f) #define BUTTON_COLOR_GAMEPAD_BLUE_HOVERED ImVec4(0.0f, 0.255f, 0.976f, 1.0f) @@ -198,8 +198,8 @@ void SohInputEditorWindow::GetButtonColorsForDeviceType(Ship::PhysicalDeviceType buttonHoveredColor = BUTTON_COLOR_KEYBOARD_BEIGE_HOVERED; break; case Ship::PhysicalDeviceType::Mouse: - buttonColor = BUTTON_COLOR_MOUSE_BEIGE; - buttonHoveredColor = BUTTON_COLOR_MOUSE_BEIGE_HOVERED; + buttonColor = BUTTON_COLOR_MOUSE_GRAY; + buttonHoveredColor = BUTTON_COLOR_MOUSE_GRAY_HOVERED; break; case Ship::PhysicalDeviceType::SDLGamepad: buttonColor = BUTTON_COLOR_GAMEPAD_BLUE; @@ -265,6 +265,7 @@ void SohInputEditorWindow::DrawButtonLineEditMappingButton(uint8_t port, N64Butt icon = ICON_FA_GAMEPAD; break; case MAPPING_TYPE_KEYBOARD: + case MAPPING_TYPE_MOUSE: icon = ICON_FA_KEYBOARD_O; break; case MAPPING_TYPE_UNKNOWN: @@ -1343,6 +1344,9 @@ void SohInputEditorWindow::DrawOcarinaControlPanel() { void SohInputEditorWindow::DrawCameraControlPanel() { ImVec2 cursor = ImGui::GetCursorPos(); ImGui::SetCursorPos(ImVec2(cursor.x + 5, cursor.y + 5)); + CVarCheckbox("Enable Mouse Controls", CVAR_SETTING("EnableMouse"), CheckboxOptions().Color(THEME_COLOR) + .Tooltip("Allows for using the mouse to control the camera (must enable Free Look), " + "aim with the shield, and perform quickspin attacks (quickly rotate the mouse then press B)")); Ship::GuiWindow::BeginGroupPanel("Aiming/First-Person Camera", ImGui::GetContentRegionAvail()); CVarCheckbox("Right Stick Aiming", CVAR_SETTING("Controls.RightStickAim"), CheckboxOptions().Color(THEME_COLOR) .Tooltip("Allows for aiming with the right stick in:\n-First-Person/C-Up view\n-Weapon Aiming")); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 3bd2ef36e..59d511941 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -33,6 +33,10 @@ DEFINE_HOOK(OnTimestamp, (u8 item)); DEFINE_HOOK(OnPlayerBonk, ()); DEFINE_HOOK(OnPlayerHealthChange, (int16_t amount)); DEFINE_HOOK(OnPlayerBottleUpdate, (int16_t contents)); +DEFINE_HOOK(OnPlayerHoldUpShield, ()); +DEFINE_HOOK(OnPlayerFirstPersonControl, (Player* player)); +DEFINE_HOOK(OnPlayerProcessStick, ()); +DEFINE_HOOK(OnPlayerShieldControl, (float_t* sp50, float_t* sp54)); DEFINE_HOOK(OnPlayDestroy, ()); DEFINE_HOOK(OnPlayDrawEnd, ()); DEFINE_HOOK(OnVanillaBehavior, (GIVanillaBehavior flag, bool* result, va_list originalArgs)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 9c861062b..40c47e9a9 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -142,6 +142,22 @@ void GameInteractor_ExecuteOnPlayerBottleUpdate(int16_t contents) { GameInteractor::Instance->ExecuteHooks(contents); } +void GameInteractor_ExecuteOnPlayerHoldUpShield() { + GameInteractor::Instance->ExecuteHooks(); +} + +void GameInteractor_ExecuteOnPlayerFirstPersonControl(Player* player) { + GameInteractor::Instance->ExecuteHooks(player); +} + +void GameInteractor_ExecuteOnPlayerShieldControl(float_t* sp50, float_t* sp54) { + GameInteractor::Instance->ExecuteHooks(sp50, sp54); +} + +void GameInteractor_ExecuteOnPlayerProcessStick() { + GameInteractor::Instance->ExecuteHooks(); +} + void GameInteractor_ExecuteOnPlayDestroy() { GameInteractor::Instance->ExecuteHooks(); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index d20864012..10ab3a877 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -35,6 +35,10 @@ void GameInteractor_ExecuteOnTimestamp (u8 item); void GameInteractor_ExecuteOnPlayerBonk(); void GameInteractor_ExecuteOnPlayerHealthChange(int16_t amount); void GameInteractor_ExecuteOnPlayerBottleUpdate(int16_t contents); +void GameInteractor_ExecuteOnPlayerHoldUpShield(); +void GameInteractor_ExecuteOnPlayerFirstPersonControl(Player* player); +void GameInteractor_ExecuteOnPlayerShieldControl(float_t* sp50, float_t* sp54); +void GameInteractor_ExecuteOnPlayerProcessStick(); void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price); void GameInteractor_ExecuteOnPlayDestroy(); void GameInteractor_ExecuteOnPlayDrawEnd(); diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 5d25bd12d..8aace9a90 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -1679,6 +1679,15 @@ typedef enum { // - `*VBFishingData` VB_SHOULD_SET_FISHING_RECORD, + // #### `result` + // ```c + // false + // ``` + // #### `args` + // - `*s8 iter2` + // - `s8 sp3C[4]` + VB_SHOULD_QUICKSPIN, + // #### `result` // ```c // (interactedActor->id == ACTOR_BG_TOKI_SWD) && LINK_IS_ADULT diff --git a/soh/soh/SohGui/SohGui.cpp b/soh/soh/SohGui/SohGui.cpp index 56da5eba0..40996fc59 100644 --- a/soh/soh/SohGui/SohGui.cpp +++ b/soh/soh/SohGui/SohGui.cpp @@ -113,6 +113,7 @@ namespace SohGui { gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Press - to access enhancements menu"); #else gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Press F1 to access enhancements menu"); + gui->GetGameOverlay()->TextDrawNotification(30.0f, true, "Press F2 to enable the mouse cursor"); #endif }*/ diff --git a/soh/src/code/padmgr.c b/soh/src/code/padmgr.c index a8bf778af..13a82162f 100644 --- a/soh/src/code/padmgr.c +++ b/soh/src/code/padmgr.c @@ -3,6 +3,7 @@ #include #include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/controls/Mouse.h" #include "soh/OTRGlobals.h" #include "soh/ResourceManagerHelpers.h" @@ -317,6 +318,8 @@ void PadMgr_HandleRetraceMsg(PadMgr* padMgr) { osRecvMesg(queue, NULL, OS_MESG_BLOCK); osContGetReadData(padMgr->pads); + Mouse_UpdateAll(); + for (i = 0; i < __osMaxControllers; i++) { padMgr->padStatus[i].status = Controller_ShouldRumble(i); } diff --git a/soh/src/code/z_camera.c b/soh/src/code/z_camera.c index 7167daceb..7b95c70af 100644 --- a/soh/src/code/z_camera.c +++ b/soh/src/code/z_camera.c @@ -7,6 +7,7 @@ #include "overlays/actors/ovl_En_Horse/z_en_horse.h" #include "soh/frame_interpolation.h" +#include "soh/Enhancements/controls/Mouse.h" s16 Camera_ChangeSettingFlags(Camera* camera, s16 setting, s16 flags); s32 Camera_ChangeModeFlags(Camera* camera, s16 mode, u8 flags); @@ -1422,6 +1423,8 @@ s32 SetCameraManual(Camera* camera) { f32 newCamX = -D_8015BD7C->state.input[0].cur.right_stick_x * 10.0f; f32 newCamY = D_8015BD7C->state.input[0].cur.right_stick_y * 10.0f; + Mouse_HandleThirdPerson(&newCamX, &newCamY); + if ((fabsf(newCamX) >= 15.0f || fabsf(newCamY) >= 15.0f) && camera->play->manualCamera == false) { camera->play->manualCamera = true; @@ -1485,8 +1488,17 @@ s32 Camera_Free(Camera* camera) { camera->animState = 0; - f32 newCamX = -D_8015BD7C->state.input[0].cur.right_stick_x * 10.0f * (CVarGetFloat(CVAR_SETTING("FreeLook.CameraSensitivity.X"), 1.0f)); - f32 newCamY = D_8015BD7C->state.input[0].cur.right_stick_y * 10.0f * (CVarGetFloat(CVAR_SETTING("FreeLook.CameraSensitivity.Y"), 1.0f)); + f32 newCamX = -D_8015BD7C->state.input[0].cur.right_stick_x * 10.0f; + f32 newCamY = +D_8015BD7C->state.input[0].cur.right_stick_y * 10.0f; + + /* Disable mouse movement when holding down the shield */ + if (!(camera->player->stateFlags1 & 0x400000)) { + Mouse_HandleThirdPerson(&newCamX, &newCamY); + } + + newCamX *= (CVarGetFloat(CVAR_SETTING("FreeLook.CameraSensitivity.X"), 1.0f)); + newCamY *= (CVarGetFloat(CVAR_SETTING("FreeLook.CameraSensitivity.Y"), 1.0f)); + bool invertXAxis = (CVarGetInteger(CVAR_SETTING("FreeLook.InvertXAxis"), 0) && !CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0)) || (!CVarGetInteger(CVAR_SETTING("FreeLook.InvertXAxis"), 0) && CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0)); camera->play->camX += newCamX * (invertXAxis ? -1 : 1); diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 8acdee4a4..312a74215 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -2082,6 +2082,8 @@ void Player_ProcessControlStick(PlayState* play, Player* this) { direction = (u16)((s16)(sControlStickWorldYaw - this->actor.shape.rot.y) + 0x2000) >> 14; } + GameInteractor_ExecuteOnPlayerProcessStick(); + this->controlStickSpinAngles[this->controlStickDataIndex] = spinAngle; this->controlStickDirections[this->controlStickDataIndex] = direction; } @@ -4281,6 +4283,10 @@ s32 Player_CanSpinAttack(Player* this) { iter = &this->controlStickSpinAngles[0]; iter2 = &sp3C[0]; + if (GameInteractor_Should(VB_SHOULD_QUICKSPIN, false, iter2, sp3C)) { + return true; + } + for (i = 0; i < 4; i++, iter++, iter2++) { if ((*iter2 = *iter) < 0) { return false; @@ -6432,6 +6438,8 @@ s32 Player_ActionHandler_11(Player* this, PlayState* play) { Player_DetachHeldActor(play, this); if (Player_SetupAction(play, this, Player_Action_80843188, 0)) { + GameInteractor_ExecuteOnPlayerHoldUpShield(); + this->stateFlags1 |= PLAYER_STATE1_SHIELDING; if (!Player_IsChildWithHylianShield(this)) { @@ -9260,6 +9268,7 @@ void Player_Action_80843188(Player* this, PlayState* play) { sp54 = sControlInput->rel.stick_y * 100 * (CVarGetInteger(CVAR_SETTING("Controls.InvertShieldAimingYAxis"), 1) ? 1 : -1); sp50 = sControlInput->rel.stick_x * (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? 120 : -120) * (CVarGetInteger(CVAR_SETTING("Controls.InvertShieldAimingXAxis"), 0) ? -1 : 1); sp4E = this->actor.shape.rot.y - Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)); + GameInteractor_ExecuteOnPlayerShieldControl(&sp50, &sp54); sp40 = Math_CosS(sp4E); sp4C = (Math_SinS(sp4E) * sp50) + (sp54 * sp40); @@ -12658,6 +12667,8 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) { f32 xAxisMulti = CVarGetFloat(CVAR_SETTING("FirstPersonCameraSensitivity.X"), 1.0f); f32 yAxisMulti = CVarGetFloat(CVAR_SETTING("FirstPersonCameraSensitivity.Y"), 1.0f); + GameInteractor_ExecuteOnPlayerFirstPersonControl(this); + if (!func_8002DD78(this) && !func_808334B4(this) && (arg2 == 0)) { // First person without weapon // Y Axis if (!CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0)) {