From dcf3b66173e299b2e8afd16f43b33f69d81e9d3e Mon Sep 17 00:00:00 2001 From: foxtacles Date: Sat, 7 Mar 2026 08:32:21 -0800 Subject: [PATCH] Fix 3rd person camera 180-degree flip after cam anim ends (#5) * Fix 3rd person camera 180-degree flip after cam anim ends When a cam anim (NPC interaction cutscene) ends, FUN_1004b6d0 places the player actor at the camera's final position using the camera's direction, which uses standard convention (z = visual forward). The 3rd person camera relies on backward-z convention (z = visual backward, from TurnAround). This mismatch caused the camera to face 180 degrees wrong permanently after any cam anim. Add a HandleCamAnimEnd extension hook in FUN_1004b6d0 that, when the 3rd person camera is active, flips the ROI direction back to backward-z and re-establishes the correct camera position. --- .../legoomni/src/common/legoanimmmpresenter.cpp | 7 +++++++ extensions/include/extensions/multiplayer.h | 3 +++ .../extensions/multiplayer/thirdpersoncamera.h | 1 + extensions/src/multiplayer.cpp | 7 +++++++ .../src/multiplayer/thirdpersoncamera.cpp | 17 +++++++++++++++++ 5 files changed, 35 insertions(+) diff --git a/LEGO1/lego/legoomni/src/common/legoanimmmpresenter.cpp b/LEGO1/lego/legoomni/src/common/legoanimmmpresenter.cpp index 3512727e..b3bf1aa7 100644 --- a/LEGO1/lego/legoomni/src/common/legoanimmmpresenter.cpp +++ b/LEGO1/lego/legoomni/src/common/legoanimmmpresenter.cpp @@ -3,6 +3,7 @@ #include "3dmanager/lego3dmanager.h" #include "decomp.h" #include "define.h" +#include "extensions/multiplayer.h" #include "islepathactor.h" #include "legoanimationmanager.h" #include "legoanimpresenter.h" @@ -20,6 +21,8 @@ #include "mxtimer.h" #include "mxutilities.h" +using namespace Extensions; + DECOMP_SIZE_ASSERT(LegoAnimMMPresenter, 0x74) // FUNCTION: LEGO1 0x1004a8d0 @@ -480,6 +483,10 @@ MxBool LegoAnimMMPresenter::FUN_1004b6d0(MxLong p_time) } actor->SetActorState(LegoPathActor::c_initial); + + if (m_tranInfo->m_unk0x29) { + Extension::Call(HandleCamAnimEnd, actor); + } } return TRUE; diff --git a/extensions/include/extensions/multiplayer.h b/extensions/include/extensions/multiplayer.h index 117b7d47..9408d1f5 100644 --- a/extensions/include/extensions/multiplayer.h +++ b/extensions/include/extensions/multiplayer.h @@ -38,6 +38,7 @@ class MultiplayerExt { static void HandleActorEnter(IslePathActor* p_actor); static void HandleActorExit(IslePathActor* p_actor); + static void HandleCamAnimEnd(LegoPathActor* p_actor); static MxBool ShouldInvertMovement(LegoPathActor* p_actor); // Returns true if the multiplayer connection was rejected (e.g. room full). @@ -59,6 +60,7 @@ constexpr auto HandleWorldEnable = &MultiplayerExt::HandleWorldEnable; constexpr auto HandleEntityNotify = &MultiplayerExt::HandleEntityNotify; constexpr auto HandleActorEnter = &MultiplayerExt::HandleActorEnter; constexpr auto HandleActorExit = &MultiplayerExt::HandleActorExit; +constexpr auto HandleCamAnimEnd = &MultiplayerExt::HandleCamAnimEnd; constexpr auto ShouldInvertMovement = &MultiplayerExt::ShouldInvertMovement; constexpr auto CheckRejected = &MultiplayerExt::CheckRejected; #else @@ -66,6 +68,7 @@ constexpr decltype(&MultiplayerExt::HandleWorldEnable) HandleWorldEnable = nullp constexpr decltype(&MultiplayerExt::HandleEntityNotify) HandleEntityNotify = nullptr; constexpr decltype(&MultiplayerExt::HandleActorEnter) HandleActorEnter = nullptr; constexpr decltype(&MultiplayerExt::HandleActorExit) HandleActorExit = nullptr; +constexpr decltype(&MultiplayerExt::HandleCamAnimEnd) HandleCamAnimEnd = nullptr; constexpr decltype(&MultiplayerExt::ShouldInvertMovement) ShouldInvertMovement = nullptr; constexpr decltype(&MultiplayerExt::CheckRejected) CheckRejected = nullptr; #endif diff --git a/extensions/include/extensions/multiplayer/thirdpersoncamera.h b/extensions/include/extensions/multiplayer/thirdpersoncamera.h index f4bc30d5..c70de598 100644 --- a/extensions/include/extensions/multiplayer/thirdpersoncamera.h +++ b/extensions/include/extensions/multiplayer/thirdpersoncamera.h @@ -30,6 +30,7 @@ class ThirdPersonCamera { // Core hooks void OnActorEnter(IslePathActor* p_actor); void OnActorExit(IslePathActor* p_actor); + void OnCamAnimEnd(LegoPathActor* p_actor); // Called every frame from NetworkManager::Tickle() void Tick(float p_deltaTime); diff --git a/extensions/src/multiplayer.cpp b/extensions/src/multiplayer.cpp index ed21e4e8..7373c993 100644 --- a/extensions/src/multiplayer.cpp +++ b/extensions/src/multiplayer.cpp @@ -124,6 +124,13 @@ void MultiplayerExt::HandleActorExit(IslePathActor* p_actor) } } +void MultiplayerExt::HandleCamAnimEnd(LegoPathActor* p_actor) +{ + if (s_networkManager) { + s_networkManager->GetThirdPersonCamera().OnCamAnimEnd(p_actor); + } +} + MxBool MultiplayerExt::ShouldInvertMovement(LegoPathActor* p_actor) { if (s_networkManager && UserActor() == p_actor) { diff --git a/extensions/src/multiplayer/thirdpersoncamera.cpp b/extensions/src/multiplayer/thirdpersoncamera.cpp index de32de18..8696cff6 100644 --- a/extensions/src/multiplayer/thirdpersoncamera.cpp +++ b/extensions/src/multiplayer/thirdpersoncamera.cpp @@ -207,6 +207,23 @@ void ThirdPersonCamera::OnActorExit(IslePathActor* p_actor) } } +void ThirdPersonCamera::OnCamAnimEnd(LegoPathActor* p_actor) +{ + if (!m_active) { + return; + } + + // FUN_1004b6d0's PlaceActor set the ROI with standard direction + // (z = visual forward). The 3rd person camera needs backward-z. + // Flip the ROI direction, then re-setup the camera. + LegoROI* roi = (m_currentVehicleType == VEHICLE_NONE) ? m_playerROI : p_actor->GetROI(); + if (roi) { + FlipROIDirection(roi); + } + + SetupCamera(p_actor); +} + void ThirdPersonCamera::Tick(float p_deltaTime) { if (!m_active) {