From 138cbcf6a338cd2468dc0f26ada268bf7a31f921 Mon Sep 17 00:00:00 2001 From: Christian Semmler Date: Mon, 16 Mar 2026 17:44:05 -0700 Subject: [PATCH] Add LMB forward movement to third person camera When LMB is held in 3rd person mode, the character walks forward in the camera's facing direction. Works simultaneously with RMB camera rotation. Transitions between 1st and 3rd person are seamless while holding LMB. Also fixes RMB mouse cursor not reappearing when releasing after a 3rd-to-1st person transition. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../lego/legoomni/include/legonavcontroller.h | 1 + extensions/include/extensions/fwd.h | 1 + .../extensions/thirdpersoncamera/controller.h | 14 +++++++++ .../thirdpersoncamera/inputhandler.h | 3 ++ .../thirdpersoncamera/orbitcamera.h | 3 +- extensions/src/thirdpersoncamera.cpp | 13 ++++++++ .../src/thirdpersoncamera/controller.cpp | 31 +++++++++++++++++-- .../src/thirdpersoncamera/inputhandler.cpp | 18 ++++++----- .../src/thirdpersoncamera/orbitcamera.cpp | 9 ++++-- 9 files changed, 80 insertions(+), 13 deletions(-) diff --git a/LEGO1/lego/legoomni/include/legonavcontroller.h b/LEGO1/lego/legoomni/include/legonavcontroller.h index 5401f971..37e4fbd7 100644 --- a/LEGO1/lego/legoomni/include/legonavcontroller.h +++ b/LEGO1/lego/legoomni/include/legonavcontroller.h @@ -124,6 +124,7 @@ class LegoNavController : public MxCore { protected: friend class Extensions::ThirdPersonCamera::OrbitCamera; + friend class Extensions::ThirdPersonCamera::Controller; float CalculateNewVel(float p_targetVel, float p_currentVel, float p_accel, float p_time); float CalculateNewTargetVel(int p_pos, int p_center, float p_max); diff --git a/extensions/include/extensions/fwd.h b/extensions/include/extensions/fwd.h index e78bead5..aed06b0a 100644 --- a/extensions/include/extensions/fwd.h +++ b/extensions/include/extensions/fwd.h @@ -11,6 +11,7 @@ class CharacterCloner; } namespace ThirdPersonCamera { +class Controller; class OrbitCamera; } } // namespace Extensions diff --git a/extensions/include/extensions/thirdpersoncamera/controller.h b/extensions/include/extensions/thirdpersoncamera/controller.h index a1901160..602f2655 100644 --- a/extensions/include/extensions/thirdpersoncamera/controller.h +++ b/extensions/include/extensions/thirdpersoncamera/controller.h @@ -74,6 +74,19 @@ class Controller { bool ConsumeAutoDisable() { return m_input.ConsumeAutoDisable(); } bool ConsumeAutoEnable() { return m_input.ConsumeAutoEnable(); } + bool IsLeftButtonHeld() const { return m_input.IsLeftButtonHeld(); } + bool IsLmbForwardEngaged() const { return m_lmbForwardEngaged; } + void SetLmbForwardEngaged(bool p_engaged) { m_lmbForwardEngaged = p_engaged; } + + MxBool HandleFirstPersonForward( + LegoNavController* p_nav, + const Vector3& p_curPos, + const Vector3& p_curDir, + Vector3& p_newPos, + Vector3& p_newDir, + float p_deltaTime + ); + float GetOrbitDistance() const { return m_orbit.GetOrbitDistance(); } void SetOrbitDistance(float p_distance) { m_orbit.SetOrbitDistance(p_distance); } void ResetTouchState() { m_input.ResetTouchState(); } @@ -106,6 +119,7 @@ class Controller { bool m_enabled; bool m_active; bool m_pendingWorldTransition; + bool m_lmbForwardEngaged; LegoROI* m_playerROI; }; diff --git a/extensions/include/extensions/thirdpersoncamera/inputhandler.h b/extensions/include/extensions/thirdpersoncamera/inputhandler.h index 8ecd1746..7b4e07d7 100644 --- a/extensions/include/extensions/thirdpersoncamera/inputhandler.h +++ b/extensions/include/extensions/thirdpersoncamera/inputhandler.h @@ -21,6 +21,8 @@ class InputHandler { int GetTouchCount() const { return m_touch.count; } SDL_FingerID GetFingerID(int p_idx) const { return m_touch.id[p_idx]; } + bool IsLeftButtonHeld() const { return m_leftButtonHeld; } + bool ConsumeAutoDisable(); bool ConsumeAutoEnable(); @@ -43,6 +45,7 @@ class InputHandler { bool m_wantsAutoDisable; bool m_wantsAutoEnable; bool m_rightButtonHeld; + bool m_leftButtonHeld; float m_savedMouseX; float m_savedMouseY; }; diff --git a/extensions/include/extensions/thirdpersoncamera/orbitcamera.h b/extensions/include/extensions/thirdpersoncamera/orbitcamera.h index e60b1351..36599deb 100644 --- a/extensions/include/extensions/thirdpersoncamera/orbitcamera.h +++ b/extensions/include/extensions/thirdpersoncamera/orbitcamera.h @@ -36,7 +36,8 @@ class OrbitCamera { Vector3& p_newPos, Vector3& p_newDir, float p_deltaTime, - bool p_isInMultiPartEmote + bool p_isInMultiPartEmote, + bool p_lmbHeld ); void AdjustYaw(float p_delta) { m_absoluteYaw += p_delta; } diff --git a/extensions/src/thirdpersoncamera.cpp b/extensions/src/thirdpersoncamera.cpp index cfb926c6..70aac3f1 100644 --- a/extensions/src/thirdpersoncamera.cpp +++ b/extensions/src/thirdpersoncamera.cpp @@ -119,8 +119,15 @@ void ThirdPersonCameraExt::OnSDLEvent(SDL_Event* p_event) s_camera->HandleSDLEventImpl(p_event); + if (p_event->type == SDL_EVENT_MOUSE_BUTTON_UP && p_event->button.button == SDL_BUTTON_LEFT) { + s_camera->SetLmbForwardEngaged(false); + } + if (s_camera->ConsumeAutoDisable()) { s_camera->Disable(/*p_preserveTouch=*/true); + if (s_camera->IsLeftButtonHeld()) { + s_camera->SetLmbForwardEngaged(true); + } } else if (s_camera->ConsumeAutoEnable()) { // Clear the movement system's touch state for camera-owned fingers only, @@ -145,6 +152,9 @@ void ThirdPersonCameraExt::OnSDLEvent(SDL_Event* p_event) s_camera->SetOrbitDistance(ThirdPersonCamera::Controller::MIN_DISTANCE); s_camera->Enable(); + if (s_camera->IsLeftButtonHeld()) { + s_camera->SetLmbForwardEngaged(true); + } } } @@ -202,6 +212,9 @@ MxBool ThirdPersonCameraExt::HandleNavOverride( } if (!s_camera->IsActive()) { + if (s_camera->IsLmbForwardEngaged()) { + return s_camera->HandleFirstPersonForward(p_nav, p_curPos, p_curDir, p_newPos, p_newDir, p_deltaTime); + } return FALSE; } diff --git a/extensions/src/thirdpersoncamera/controller.cpp b/extensions/src/thirdpersoncamera/controller.cpp index 878e5def..a7a912ad 100644 --- a/extensions/src/thirdpersoncamera/controller.cpp +++ b/extensions/src/thirdpersoncamera/controller.cpp @@ -30,7 +30,7 @@ using namespace Extensions::ThirdPersonCamera; Controller::Controller() : m_animator(CharacterAnimatorConfig{/*.saveEmoteTransform=*/true, /*.propSuffix=*/0}), m_enabled(false), - m_active(false), m_pendingWorldTransition(false), m_playerROI(nullptr) + m_active(false), m_pendingWorldTransition(false), m_lmbForwardEngaged(false), m_playerROI(nullptr) { } @@ -59,6 +59,7 @@ void Controller::Deactivate() m_active = false; m_pendingWorldTransition = false; + m_lmbForwardEngaged = false; m_animator.StopROISounds(); m_animator.StopClickAnimation(); m_display.DestroyDisplayClone(); @@ -365,7 +366,8 @@ MxBool Controller::HandleCameraRelativeMovement( p_newPos, p_newDir, p_deltaTime, - m_animator.IsInMultiPartEmote() + m_animator.IsInMultiPartEmote(), + m_input.IsLeftButtonHeld() ); } @@ -374,6 +376,31 @@ void Controller::HandleSDLEventImpl(SDL_Event* p_event) m_input.HandleSDLEvent(p_event, m_orbit, m_active); } +MxBool Controller::HandleFirstPersonForward( + LegoNavController* p_nav, + const Vector3& p_curPos, + const Vector3& p_curDir, + Vector3& p_newPos, + Vector3& p_newDir, + float p_deltaTime +) +{ + float accel = p_nav->m_maxLinearAccel; + p_nav->m_linearVel += accel * p_deltaTime; + if (p_nav->m_linearVel > p_nav->m_maxLinearVel) { + p_nav->m_linearVel = p_nav->m_maxLinearVel; + } + + float speed = p_nav->m_linearVel * p_deltaTime; + p_newPos[0] = p_curPos[0] + p_curDir[0] * speed; + p_newPos[1] = p_curPos[1] + p_curDir[1] * speed; + p_newPos[2] = p_curPos[2] + p_curDir[2] * speed; + p_newDir = p_curDir; + + p_nav->m_rotationalVel = 0.0f; + return TRUE; +} + void Controller::ReinitForCharacter() { if (!GameState() || IsRestrictedArea(GameState()->m_currentArea)) { diff --git a/extensions/src/thirdpersoncamera/inputhandler.cpp b/extensions/src/thirdpersoncamera/inputhandler.cpp index e2288715..376c04bc 100644 --- a/extensions/src/thirdpersoncamera/inputhandler.cpp +++ b/extensions/src/thirdpersoncamera/inputhandler.cpp @@ -8,8 +8,8 @@ using namespace Extensions::ThirdPersonCamera; InputHandler::InputHandler() - : m_touch{}, m_wantsAutoDisable(false), m_wantsAutoEnable(false), m_rightButtonHeld(false), m_savedMouseX(0.0f), - m_savedMouseY(0.0f) + : m_touch{}, m_wantsAutoDisable(false), m_wantsAutoEnable(false), m_rightButtonHeld(false), + m_leftButtonHeld(false), m_savedMouseX(0.0f), m_savedMouseY(0.0f) { } @@ -116,21 +116,23 @@ void InputHandler::HandleSDLEvent(SDL_Event* p_event, OrbitCamera& p_orbit, bool case SDL_EVENT_MOUSE_BUTTON_UP: { if (p_event->button.button == SDL_BUTTON_RIGHT) { m_rightButtonHeld = p_event->button.down; - if (!p_active) { - break; - } SDL_Window* window = SDL_GetWindowFromID(p_event->button.windowID); if (window) { if (m_rightButtonHeld) { - SDL_GetMouseState(&m_savedMouseX, &m_savedMouseY); - SDL_SetWindowRelativeMouseMode(window, true); + if (p_active) { + SDL_GetMouseState(&m_savedMouseX, &m_savedMouseY); + SDL_SetWindowRelativeMouseMode(window, true); + } } - else { + else if (SDL_GetWindowRelativeMouseMode(window)) { SDL_SetWindowRelativeMouseMode(window, false); SDL_WarpMouseInWindow(window, m_savedMouseX, m_savedMouseY); } } } + else if (p_event->button.button == SDL_BUTTON_LEFT) { + m_leftButtonHeld = p_event->button.down; + } break; } diff --git a/extensions/src/thirdpersoncamera/orbitcamera.cpp b/extensions/src/thirdpersoncamera/orbitcamera.cpp index 80cdc75e..2bfc649c 100644 --- a/extensions/src/thirdpersoncamera/orbitcamera.cpp +++ b/extensions/src/thirdpersoncamera/orbitcamera.cpp @@ -147,7 +147,8 @@ MxBool OrbitCamera::HandleCameraRelativeMovement( Vector3& p_newPos, Vector3& p_newDir, float p_deltaTime, - bool p_isInMultiPartEmote + bool p_isInMultiPartEmote, + bool p_lmbHeld ) { LegoInputManager* inputManager = InputManager(); @@ -180,8 +181,12 @@ MxBool OrbitCamera::HandleCameraRelativeMovement( moveDirX += camRightX; moveDirZ += camRightZ; } + if (p_lmbHeld) { + moveDirX += camForwardX; + moveDirZ += camForwardZ; + } - if (keyFlags == 0 && inputManager) { + if (keyFlags == 0 && !p_lmbHeld && inputManager) { MxU32 joystickX, joystickY, povPosition; if (inputManager->GetJoystickState(&joystickX, &joystickY, &povPosition) == SUCCESS) { float jx = (joystickX - 50.0f) / 50.0f;