From 5b64e4fd2ce77f621ac6a9b3510e73b3fbda896b Mon Sep 17 00:00:00 2001 From: madeline Date: Sat, 8 Aug 2026 19:20:46 -0700 Subject: [PATCH] trigger viewer, more commands --- files.cmake | 1 + include/dusk/d_trigger_view.h | 7 + include/dusk/settings.h | 17 ++ res/rml/command_console.rcss | 14 + src/d/d_camera.cpp | 9 + src/d/d_drawlist.cpp | 2 +- src/d/d_s_play.cpp | 5 + src/dusk/commands.cpp | 265 ++++++++++++++++++- src/dusk/commands.hpp | 7 + src/dusk/d_trigger_view.cpp | 417 ++++++++++++++++++++++++++++++ src/dusk/imgui/ImGuiMenuTools.cpp | 20 ++ src/dusk/settings.cpp | 17 +- src/dusk/ui/ui.cpp | 2 +- src/m_Do/m_Do_main.cpp | 3 + 14 files changed, 782 insertions(+), 4 deletions(-) create mode 100644 include/dusk/d_trigger_view.h create mode 100644 src/dusk/d_trigger_view.cpp diff --git a/files.cmake b/files.cmake index 2b5632c2f2..4429a5cb2e 100644 --- a/files.cmake +++ b/files.cmake @@ -1443,6 +1443,7 @@ set(DUSK_FILES src/dusk/layout.cpp src/dusk/logging.cpp src/dusk/settings.cpp + src/dusk/d_trigger_view.cpp src/dusk/speedrun.cpp src/dusk/string.cpp src/dusk/stubs.cpp diff --git a/include/dusk/d_trigger_view.h b/include/dusk/d_trigger_view.h new file mode 100644 index 0000000000..598cb0bb20 --- /dev/null +++ b/include/dusk/d_trigger_view.h @@ -0,0 +1,7 @@ +#pragma once + +namespace dusk { +namespace TriggerView { +void execute(); +} +} diff --git a/include/dusk/settings.h b/include/dusk/settings.h index ad88bab92e..1b27d39412 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -292,8 +292,25 @@ struct CollisionViewSettings { float drawRange; }; +struct TriggerViewSettings { + bool loadZones; + bool eventAreas; + bool switchAreas; + bool eventTags; + bool midnaStops; + bool twilightGates; + bool checkpoints; + bool paths; + bool transformDists; + bool attentionDists; + bool purpleMistAvoid; + bool leevers; + float opacity; +}; + struct TransientSettings { CollisionViewSettings collisionView; + TriggerViewSettings triggerView; bool skipFrameRateLimit; bool moveLinkActive; bool stateShareLoadActive; diff --git a/res/rml/command_console.rcss b/res/rml/command_console.rcss index 44074d40a5..909d5148c6 100644 --- a/res/rml/command_console.rcss +++ b/res/rml/command_console.rcss @@ -40,6 +40,10 @@ output[open] { overflow-y: scroll; } +console:not([open]) { + pointer-events: none; +} + line { display: block; white-space: nowrap; @@ -49,6 +53,16 @@ line.cmd { color: #FFD966; } +line.copyable { + color: #80C8FF; + cursor: pointer; +} + +line.copyable:hover { + color: #FFFFFF; + background-color: rgba(255, 255, 255, 12%); +} + console input { display: none; width: 100%; diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index 50e9f073b6..043bdc5632 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -29,6 +29,7 @@ #endif #if TARGET_PC +#include "dusk/commands.hpp" #include "dusk/frame_interpolation.h" #include "dusk/logging.h" #include "dusk/action_bindings.h" @@ -1052,6 +1053,11 @@ void dCamera_c::debugDrawInit() { bool dCamera_c::Run() { #if TARGET_PC ResetView(); + if (dusk::isCameraDetached()) { + mFrameCounter++; + mTicks++; + return true; + } if (executeDebugFlyCam()) { mFrameCounter++; mTicks++; @@ -11083,6 +11089,9 @@ camera_class* dCam_getCamera() { dCamera_c* dCam_getBody() { camera_process_class* camera = (camera_process_class*)dCam_getCamera(); +#if TARGET_PC + if (camera == nullptr) { return nullptr; } +#endif return &camera->mCamera; } diff --git a/src/d/d_drawlist.cpp b/src/d/d_drawlist.cpp index 1f16b2f655..e99c92fbf0 100644 --- a/src/d/d_drawlist.cpp +++ b/src/d/d_drawlist.cpp @@ -18,7 +18,7 @@ #include #include "JSystem/JKernel/JKRHeap.h" #include "absl/container/flat_hash_map.h" -#include "client/TracyScoped.hpp" +#include "tracy/Tracy.hpp" #include "dusk/frame_interpolation.h" #include "dusk/gx_helper.h" #include "dusk/logging.h" diff --git a/src/d/d_s_play.cpp b/src/d/d_s_play.cpp index 0e84b060db..ade1b368a0 100644 --- a/src/d/d_s_play.cpp +++ b/src/d/d_s_play.cpp @@ -41,6 +41,7 @@ #if TARGET_PC #include "dusk/autosave.h" +#include "dusk/d_trigger_view.h" #include "dusk/memory.h" #include "dusk/ui/ui.hpp" #endif @@ -675,6 +676,10 @@ static int dScnPly_Draw(dScnPly_c* i_this) { dComIfG_Ccsp()->Draw(); dComIfG_Bgsp().Draw(); + #if TARGET_PC + dusk::TriggerView::execute(); + #endif + #if DEBUG dPath_Draw(); #endif diff --git a/src/dusk/commands.cpp b/src/dusk/commands.cpp index 3f2ad03b53..8af2498035 100644 --- a/src/dusk/commands.cpp +++ b/src/dusk/commands.cpp @@ -1,11 +1,14 @@ #include "commands.hpp" #include "JSystem/JUtility/JUTGamePad.h" +#include "SSystem/SComponent/c_math.h" #include "SSystem/SComponent/c_sxyz.h" #include "SSystem/SComponent/c_xyz.h" #include "c/c_damagereaction.h" #include "d/actor/d_a_alink.h" +#include "d/d_com_inf_actor.h" #include "d/d_com_inf_game.h" +#include "d/d_camera.h" #include "d/d_kankyo.h" #include "d/d_stage.h" #include "dusk/game_clock.h" @@ -192,10 +195,78 @@ static int FindActorCallback(void* p, void* ctx) { return 1; } +struct CameraFlyState { + bool active = false; + cXyz startEye; + cXyz startCenter; + cXyz endEye; + cXyz endCenter; + float duration = 0.0f; + float elapsed = 0.0f; +}; +CameraFlyState s_cameraFly; + +static dCamera_c* getCamera() { return dCam_getBody(); } + +static cXyz anglesToDir(s16 h, s16 v) { + return cXyz(cM_scos(v) * cM_ssin(h), cM_ssin(v), cM_scos(v) * cM_scos(h)); +} + +static void cameraToAngles(dCamera_c* cam, s16& h, s16& v) { + const cXyz eye = cam->iEye(); + const cXyz ctr = cam->iCenter(); + const float dx = ctr.x - eye.x; + const float dy = ctr.y - eye.y; + const float dz = ctr.z - eye.z; + const float hDist = sqrtf(dx * dx + dz * dz); + h = cM_atan2s(dx, dz); + v = cM_atan2s(dy, hDist); +} + +static float smoothstep(float t) { + t = std::clamp(t, 0.0f, 1.0f); + return t * t * (3.0f - 2.0f * t); +} + +static constexpr float kCamTargetDist = 100.0f; + } // namespace +bool isCameraDetached() { return s_cameraFly.active; } + +void processCameraCommands() { + if (!s_cameraFly.active) { return; } + dCamera_c* cam = getCamera(); + if (cam == nullptr) { s_cameraFly.active = false; return; } + + cXyz eye, center; + + if (s_cameraFly.duration <= 0.0f) { + eye = s_cameraFly.endEye; + center = s_cameraFly.endCenter; + } else { + s_cameraFly.elapsed += dusk::game_clock::sim_pace(); + const float t = smoothstep(s_cameraFly.elapsed / s_cameraFly.duration); + eye = s_cameraFly.startEye + (s_cameraFly.endEye - s_cameraFly.startEye) * t; + center = s_cameraFly.startCenter + (s_cameraFly.endCenter - s_cameraFly.startCenter) * t; + + if (s_cameraFly.elapsed >= s_cameraFly.duration) { + eye = s_cameraFly.endEye; + center = s_cameraFly.endCenter; + s_cameraFly.startEye = s_cameraFly.endEye; + s_cameraFly.startCenter = s_cameraFly.endCenter; + s_cameraFly.duration = 0.0f; + } + } + + cam->Reset(center, eye); + cam->mDebugFlyCam.initialized = false; +} + void runCommand(std::string_view cmdLine, CommandState& state, const CommandOutput& output) { - output(fmt::format(FMT_STRING("> {}"), cmdLine)); + if (state.echoEnabled) { + output(fmt::format(FMT_STRING("> {}"), cmdLine)); + } if (!cmdLine.empty()) { if (state.history.empty() || state.history.back() != cmdLine) { @@ -457,6 +528,27 @@ void runCommand(std::string_view cmdLine, CommandState& state, const CommandOutp return; } + if (cmd == "freeze" || cmd == "unfreeze") { + const bool doFreeze = (cmd == "freeze"); + if (args.size() < 2) { + g_dComIfAc_gameInfo.mPause = doFreeze; + output(doFreeze ? "Global freeze on" : "Global freeze off"); + return; + } + auto* ac = ParseActorArg(args[1], state.foundProcId, output); + if (ac == nullptr) { + return; + } + if (doFreeze) { + fpcM_PauseEnable(ac, 1); + output(fmt::format(FMT_STRING("Froze actor {}"), (unsigned int)ac->id)); + } else { + fpcM_PauseDisable(ac, 1); + output(fmt::format(FMT_STRING("Unfroze actor {}"), (unsigned int)ac->id)); + } + return; + } + if (cmd == "rate") { if (args.size() >= 2) { const auto hz = ParseLong(args[1]); @@ -565,6 +657,153 @@ void runCommand(std::string_view cmdLine, CommandState& state, const CommandOutp return; } + if (cmd == "camera") { + const std::string sub = args.size() >= 2 ? args[1] : ""; + + if (sub == "attach") { + s_cameraFly.active = false; + output("Camera attached"); + return; + } + + auto* cam = getCamera(); + if (cam == nullptr) { output("Error: camera not available"); return; } + + if (sub == "detach") { + s_cameraFly.active = true; + s_cameraFly.duration = 0.0f; + s_cameraFly.startEye = s_cameraFly.endEye = cam->iEye(); + s_cameraFly.startCenter = s_cameraFly.endCenter = cam->iCenter(); + output("Camera detached"); + return; + } + + if (sub == "tp") { + // camera tp @ + if (args.size() >= 3 && !args[2].empty() && args[2][0] == '@') { + auto* ac = ParseActorArg(args[2], state.foundProcId, output); + if (ac == nullptr) { return; } + const cXyz actorPos = ac->current.pos; + const cXyz dir = anglesToDir(ac->shape_angle.y, 0); + const cXyz newEye = cXyz(actorPos.x - dir.x * kCamTargetDist, + actorPos.y - dir.y * kCamTargetDist + 50.0f, + actorPos.z - dir.z * kCamTargetDist); + s_cameraFly.active = true; s_cameraFly.duration = 0.0f; + s_cameraFly.startEye = s_cameraFly.endEye = newEye; + s_cameraFly.startCenter = s_cameraFly.endCenter = actorPos; + output(fmt::format(FMT_STRING("Camera moved to actor {}"), ac->id)); + return; + } + // camera tp x y z [h] [v] + if (args.size() < 5) { + output("Usage: camera tp [h_angle] [v_angle] | camera tp @"); + return; + } + const auto pos = ParseXYZ(args, 2); + if (!pos) { output("Error: invalid coordinates"); return; } + s16 h = 0, v = 0; + cameraToAngles(cam, h, v); + if (args.size() >= 6) { + const auto hv = ParseLong(args[5]); + if (!hv) { output("Error: invalid h_angle"); return; } + h = (s16)*hv; + } + if (args.size() >= 7) { + const auto vv = ParseLong(args[6]); + if (!vv) { output("Error: invalid v_angle"); return; } + v = (s16)*vv; + } + const cXyz dir = anglesToDir(h, v); + const cXyz center = cXyz(pos->x + dir.x * kCamTargetDist, + pos->y + dir.y * kCamTargetDist, + pos->z + dir.z * kCamTargetDist); + s_cameraFly.active = true; s_cameraFly.duration = 0.0f; + s_cameraFly.startEye = s_cameraFly.endEye = *pos; + s_cameraFly.startCenter = s_cameraFly.endCenter = center; + output(fmt::format(FMT_STRING("Camera teleported to ({:.2f}, {:.2f}, {:.2f})"), + pos->x, pos->y, pos->z)); + return; + } + + if (sub == "pos") { + s16 h = 0, v = 0; + cameraToAngles(cam, h, v); + const cXyz eye = cam->iEye(); + output(fmt::format(FMT_STRING("Camera: ({:.2f}, {:.2f}, {:.2f}) h={} v={}"), + eye.x, eye.y, eye.z, (int)h, (int)v)); + return; + } + + if (sub == "fly") { + // camera fly