From 901ce2ee4cde952d1badf7b22daf459de4a06056 Mon Sep 17 00:00:00 2001 From: Pheenoh Date: Sun, 3 May 2026 12:43:55 -0600 Subject: [PATCH 1/2] add debug fly cam option --- include/d/d_camera.h | 18 +++++ include/dusk/settings.h | 1 + src/d/d_camera.cpp | 97 +++++++++++++++++++++++++++ src/dusk/imgui/ImGuiCameraOverlay.cpp | 70 +++---------------- src/dusk/imgui/ImGuiMenuGame.cpp | 1 + src/dusk/settings.cpp | 2 + 6 files changed, 127 insertions(+), 62 deletions(-) diff --git a/include/d/d_camera.h b/include/d/d_camera.h index 105c9cae9f..c481262a46 100644 --- a/include/d/d_camera.h +++ b/include/d/d_camera.h @@ -118,6 +118,18 @@ class camera_class; class dCamera_c; typedef bool (dCamera_c::*engine_fn)(s32); +#if TARGET_PC +struct DebugFlyCam { + bool initialized; + f32 pitch; + f32 yaw; + cXyz savedCenter; + cXyz savedEye; + f32 savedFovy; + cSAngle savedBank; +}; +#endif + class dCamera_c { public: class dCamInfo_c { @@ -1028,6 +1040,8 @@ public: bool test2Camera(s32); #if TARGET_PC bool freeCamera(); + bool executeDebugFlyCam(); + void deactivateDebugFlyCam(); #endif bool towerCamera(s32); bool hookshotCamera(s32); @@ -1376,6 +1390,10 @@ public: /* 0x970 */ dCamSetup_c mCamSetup; /* 0xAEC */ dCamParam_c mCamParam; /* 0xB0C */ u8 field_0xb0c; + +#if TARGET_PC + DebugFlyCam mDebugFlyCam; +#endif }; // Size: 0xB10 dCamera_c* dCam_getBody(); diff --git a/include/dusk/settings.h b/include/dusk/settings.h index e11647e8e4..a42d1e8b7f 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -119,6 +119,7 @@ struct UserSettings { ConfigVar invertCameraXAxis; ConfigVar invertCameraYAxis; ConfigVar freeCameraSensitivity; + ConfigVar debugFlyCam; // Cheats ConfigVar infiniteHearts; diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index d74cf24c43..54afccdf51 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -1040,6 +1040,11 @@ void dCamera_c::debugDrawInit() { bool dCamera_c::Run() { #if TARGET_PC ResetView(); + if (executeDebugFlyCam()) { + mFrameCounter++; + mTicks++; + return true; + } #endif daAlink_c* link = daAlink_getAlinkActorClass(); @@ -7474,7 +7479,99 @@ bool dCamera_c::test2Camera(s32 param_0) { return false; } +static constexpr f32 FLYCAM_SPEED = 0.5f; +static constexpr f32 FLYCAM_FAST_SPEED = 4.0f; +static constexpr f32 FLYCAM_ROTATION_SPEED = 0.002f; +static constexpr f32 FLYCAM_TRIGGER_DEADZONE = 20.0f; + #if TARGET_PC +bool dCamera_c::executeDebugFlyCam() { + if (!dusk::getSettings().game.debugFlyCam) { + if (mDebugFlyCam.initialized) { + deactivateDebugFlyCam(); + } + return false; + } + + dEvt_control_c* event = dComIfGp_getEvent(); + if (event == nullptr) { + return false; + } + + event->mEventStatus = 1; + dComIfGp_getEventManager().setCameraPlay(1); + + if (!mDebugFlyCam.initialized) { + mDebugFlyCam.savedCenter = mCenter; + mDebugFlyCam.savedEye = mEye; + mDebugFlyCam.savedFovy = mFovy; + mDebugFlyCam.savedBank = mBank; + + f32 dx = mCenter.x - mEye.x; + f32 dy = mCenter.y - mEye.y; + f32 dz = mCenter.z - mEye.z; + mDebugFlyCam.yaw = atan2f(dz, dx); + f32 horizontal = sqrtf(dx * dx + dz * dz); + mDebugFlyCam.pitch = atan2f(dy, horizontal); + + mDebugFlyCam.initialized = true; + } + + interface_of_controller_pad& pad = mDoCPd_c::getCpadInfo(0); + f32 stickY = pad.mMainStickPosY * 72.0f; + f32 stickX = pad.mMainStickPosX * 72.0f; + f32 cStickY = pad.mCStickPosY * 59.0f; + f32 cStickX = pad.mCStickPosX * 59.0f; + f32 trigL = pad.mTriggerLeft * 150.0f; + f32 trigR = pad.mTriggerRight * 150.0f; + + f32 verticalDisp = 0.0f; + if (trigR >= FLYCAM_TRIGGER_DEADZONE) { + verticalDisp += trigR; + } + if (trigL >= FLYCAM_TRIGGER_DEADZONE) { + verticalDisp -= trigL; + } + + f32 moveDy = stickY * sinf(mDebugFlyCam.pitch) + verticalDisp; + f32 moveDx = stickY * cosf(mDebugFlyCam.yaw) * cosf(mDebugFlyCam.pitch) - stickX * sinf(mDebugFlyCam.yaw); + f32 moveDz = stickY * sinf(mDebugFlyCam.yaw) * cosf(mDebugFlyCam.pitch) + stickX * cosf(mDebugFlyCam.yaw); + + f32 speed = mDoCPd_c::getHoldZ(PAD_1) ? FLYCAM_FAST_SPEED : FLYCAM_SPEED; + + mEye.x += speed * moveDx; + mEye.y += speed * moveDy; + mEye.z += speed * moveDz; + + static constexpr f32 FLYCAM_TARGET_DIST = 100.0f; + mCenter.x = mEye.x + cosf(mDebugFlyCam.yaw) * cosf(mDebugFlyCam.pitch) * FLYCAM_TARGET_DIST; + mCenter.z = mEye.z + sinf(mDebugFlyCam.yaw) * cosf(mDebugFlyCam.pitch) * FLYCAM_TARGET_DIST; + mCenter.y = mEye.y + sinf(mDebugFlyCam.pitch) * FLYCAM_TARGET_DIST; + + Reset(mCenter, mEye); + + f32 yawInput = dusk::getSettings().game.invertCameraXAxis ? cStickX : -cStickX; + mDebugFlyCam.yaw += yawInput * FLYCAM_ROTATION_SPEED; + mDebugFlyCam.yaw = fmodf(mDebugFlyCam.yaw + 2.0f * (f32)M_PI, 2.0f * (f32)M_PI); + + f32 maxPitch = (f32)M_PI / 2.0f - 0.1f; + f32 minPitch = -(f32)M_PI / 2.0f + 0.1f; + mDebugFlyCam.pitch = std::clamp(mDebugFlyCam.pitch + cStickY * FLYCAM_ROTATION_SPEED, minPitch, maxPitch); + + return true; +} + +void dCamera_c::deactivateDebugFlyCam() { + Reset(mDebugFlyCam.savedCenter, mDebugFlyCam.savedEye, mDebugFlyCam.savedFovy, mDebugFlyCam.savedBank.Val()); + + dEvt_control_c* event = dComIfGp_getEvent(); + if (event != nullptr) { + event->mEventStatus = 0; + } + dComIfGp_getEventManager().setCameraPlay(0); + mDebugFlyCam.initialized = false; +} + bool dCamera_c::freeCamera() { if (dusk::getSettings().game.freeCamera && mGear == 1) { mGear = 0; diff --git a/src/dusk/imgui/ImGuiCameraOverlay.cpp b/src/dusk/imgui/ImGuiCameraOverlay.cpp index aa3d1ac093..8b78c9cf90 100644 --- a/src/dusk/imgui/ImGuiCameraOverlay.cpp +++ b/src/dusk/imgui/ImGuiCameraOverlay.cpp @@ -2,8 +2,10 @@ #include "SSystem/SComponent/c_xyz.h" #include "imgui.h" +#include "ImGuiConfig.hpp" #include "ImGuiConsole.hpp" #include "ImGuiMenuTools.hpp" +#include "dusk/settings.h" namespace dusk { void ImGuiMenuTools::ShowCameraOverlay() { @@ -46,70 +48,14 @@ namespace dusk { ImGui::InputFloat("Camera FOV", &dCam->mFovy); - ImGui::SeparatorText("Free-look Data"); + ImGui::SeparatorText("Options"); - static float eyeYawDeg = 0.0f; - static float moveSpeed = 5000.0f; - static float rotSpeed = 5.0f; - static cXyz freeLookPos = cXyz::Zero; - static bool freeLookActive = false; - - bool changed = false; - - if (ImGui::IsKeyDown(ImGuiKey_LeftArrow)) { - eyeYawDeg += rotSpeed; - if (eyeYawDeg >= 360.0f) - eyeYawDeg -= 360.0f; - - changed = true; + config::ImGuiCheckbox("Fly Mode", getSettings().game.debugFlyCam); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Detach camera and fly freely.\n" + "Left stick: move, C-stick: look\n" + "L/R triggers: up/down, Z: fast"); } - else if (ImGui::IsKeyDown(ImGuiKey_RightArrow)) { - eyeYawDeg -= rotSpeed; - if (eyeYawDeg < 0.0f) - eyeYawDeg += 360.0f; - - changed = true; - } - cSAngle yawAngle = cSAngle(eyeYawDeg); - cXyz frontDir = cXyz(yawAngle.Sin(), 0.0f, yawAngle.Cos()); - - if (ImGui::IsKeyDown(ImGuiKey_UpArrow)) { - freeLookPos -= frontDir * moveSpeed * ImGui::GetIO().DeltaTime; - changed = true; - } - else if (ImGui::IsKeyDown(ImGuiKey_DownArrow)) { - freeLookPos += frontDir * moveSpeed * ImGui::GetIO().DeltaTime; - changed = true; - } - - if (ImGui::IsKeyDown(ImGuiKey_LeftShift)) { - freeLookPos += cXyz::BaseY * moveSpeed * ImGui::GetIO().DeltaTime; - changed = true; - } - - if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl)) { - freeLookPos -= cXyz::BaseY * moveSpeed * ImGui::GetIO().DeltaTime; - changed = true; - } - - if (!freeLookActive && changed) { - freeLookPos += dCam->Center(); - freeLookActive = true; - } - - if (ImGui::IsKeyDown(ImGuiKey_R)) { - freeLookPos = cXyz::Zero; - freeLookActive = false; - } - - if (freeLookActive) { - dCam->Reset(freeLookPos, freeLookPos + (frontDir * 100.0f)); - } - - ImGui::InputFloat("Free-look Yaw", &eyeYawDeg); - ImGui::InputFloat3("Free-look Position", &freeLookPos.x); - ImGui::InputFloat("Free-look Move Speed", &moveSpeed); - ImGui::InputFloat("Free-look Rotation Speed", &rotSpeed); ShowCornerContextMenu(m_cameraOverlayCorner, 0); diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index c70c55c2fb..616e0ddbac 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -601,6 +601,7 @@ namespace dusk { getSettings().game.freeMagicArmor.setValue(false); getSettings().game.enableTurboKeybind.setValue(false); + getSettings().game.debugFlyCam.setValue(false); } SpeedrunInfo m_speedrunInfo; diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 49df869dd4..f09dd95061 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -78,6 +78,7 @@ UserSettings g_userSettings = { .invertCameraXAxis {"game.invertCameraXAxis", false}, .invertCameraYAxis {"game.invertCameraYAxis", false}, .freeCameraSensitivity {"game.freeCameraSensitivity", 1.0f}, + .debugFlyCam {"game.debugFlyCam", false}, // Cheats .infiniteHearts {"game.infiniteHearts", false}, @@ -203,6 +204,7 @@ void registerSettings() { Register(g_userSettings.game.gyroInvertPitch); Register(g_userSettings.game.gyroInvertYaw); Register(g_userSettings.game.freeCamera); + Register(g_userSettings.game.debugFlyCam); Register(g_userSettings.backend.isoPath); Register(g_userSettings.backend.graphicsBackend); From 3c5ade556575626dfd88e3c0ed3a49f588de1204 Mon Sep 17 00:00:00 2001 From: Pheenoh Date: Mon, 4 May 2026 20:15:02 -0600 Subject: [PATCH 2/2] don't allow activation if paused or in event --- src/d/d_camera.cpp | 9 +++++++-- src/dusk/imgui/ImGuiCameraOverlay.cpp | 20 ++++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index 54afccdf51..e559a56d95 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -7498,8 +7498,10 @@ bool dCamera_c::executeDebugFlyCam() { return false; } - event->mEventStatus = 1; - dComIfGp_getEventManager().setCameraPlay(1); + if (!mDebugFlyCam.initialized && (event->mEventStatus != 0 || dComIfGp_isPauseFlag())) { + dusk::getSettings().game.debugFlyCam.setValue(false); + return false; + } if (!mDebugFlyCam.initialized) { mDebugFlyCam.savedCenter = mCenter; @@ -7517,6 +7519,9 @@ bool dCamera_c::executeDebugFlyCam() { mDebugFlyCam.initialized = true; } + event->mEventStatus = 1; + dComIfGp_getEventManager().setCameraPlay(1); + interface_of_controller_pad& pad = mDoCPd_c::getCpadInfo(0); f32 stickY = pad.mMainStickPosY * 72.0f; f32 stickX = pad.mMainStickPosX * 72.0f; diff --git a/src/dusk/imgui/ImGuiCameraOverlay.cpp b/src/dusk/imgui/ImGuiCameraOverlay.cpp index 8b78c9cf90..2a39ef85eb 100644 --- a/src/dusk/imgui/ImGuiCameraOverlay.cpp +++ b/src/dusk/imgui/ImGuiCameraOverlay.cpp @@ -1,5 +1,6 @@ #include "f_op/f_op_camera_mng.h" #include "SSystem/SComponent/c_xyz.h" +#include "d/d_com_inf_game.h" #include "imgui.h" #include "ImGuiConfig.hpp" @@ -50,11 +51,22 @@ namespace dusk { ImGui::SeparatorText("Options"); + bool eventRunning = (dComIfGp_event_runCheck() || dComIfGp_isPauseFlag()) && !getSettings().game.debugFlyCam; + if (eventRunning) { + ImGui::BeginDisabled(); + } config::ImGuiCheckbox("Fly Mode", getSettings().game.debugFlyCam); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Detach camera and fly freely.\n" - "Left stick: move, C-stick: look\n" - "L/R triggers: up/down, Z: fast"); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + if (eventRunning) { + ImGui::SetTooltip("Cannot enable while paused or during an active event."); + } else { + ImGui::SetTooltip("Detach camera and fly freely.\n" + "Left stick: move, C-stick: look\n" + "L/R triggers: up/down, Z: fast"); + } + } + if (eventRunning) { + ImGui::EndDisabled(); } ShowCornerContextMenu(m_cameraOverlayCorner, 0);