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 857a6c856d..7f7aa8cd69 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -120,6 +120,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..e559a56d95 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,104 @@ 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; + } + + if (!mDebugFlyCam.initialized && (event->mEventStatus != 0 || dComIfGp_isPauseFlag())) { + dusk::getSettings().game.debugFlyCam.setValue(false); + return false; + } + + 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; + } + + 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; + 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..2a39ef85eb 100644 --- a/src/dusk/imgui/ImGuiCameraOverlay.cpp +++ b/src/dusk/imgui/ImGuiCameraOverlay.cpp @@ -1,9 +1,12 @@ #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" #include "ImGuiConsole.hpp" #include "ImGuiMenuTools.hpp" +#include "dusk/settings.h" namespace dusk { void ImGuiMenuTools::ShowCameraOverlay() { @@ -46,70 +49,25 @@ 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; + bool eventRunning = (dComIfGp_event_runCheck() || dComIfGp_isPauseFlag()) && !getSettings().game.debugFlyCam; + if (eventRunning) { + ImGui::BeginDisabled(); } - else if (ImGui::IsKeyDown(ImGuiKey_RightArrow)) { - eyeYawDeg -= rotSpeed; - if (eyeYawDeg < 0.0f) - eyeYawDeg += 360.0f; - - changed = true; + config::ImGuiCheckbox("Fly Mode", getSettings().game.debugFlyCam); + 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"); + } } - 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; + if (eventRunning) { + ImGui::EndDisabled(); } - 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 d359c569c8..9114bfbee3 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -79,6 +79,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}, @@ -204,6 +205,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);