mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-18 17:55:28 +02:00
153 lines
7.9 KiB
C++
153 lines
7.9 KiB
C++
#include "Engine/Core/Platform/launcher_support.h"
|
|
#include "skate_menu_internal.h"
|
|
#include "Engine/Core/Profiling/profiler.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
|
|
// The SETTINGS and DEVELOPER pages.
|
|
namespace dingosdk::overlay::menu {
|
|
void ui_page(SkateMenu& menu, const Model& model, const CallbacksV3& callbacks) {
|
|
begin_card(menu, "on-screen", "ON SCREEN");
|
|
bool hidden = model.debug.game_ui_hidden;
|
|
if (toggle_row(menu, "Hide game UI", "Keep the view clear for riding and captures.", hidden,
|
|
model.debug.available && model.debug.ui_available && callbacks.queue_debug))
|
|
debug_request(menu, callbacks, {DebugAction::set_game_ui_hidden, hidden});
|
|
const auto back = dingosdk::launcher::key_name(dingosdk::launcher::overlay_keys().menu) + " always brings ReSkate back.";
|
|
note(back.c_str());
|
|
end_card();
|
|
|
|
begin_card(menu, "menu-scale", "MENU SIZE");
|
|
// Drag locally for an immediate preview, then save once on release: the
|
|
// saved value only returns through the model on a later frame.
|
|
if (!menu.scale_editing) menu.scale = model.menu_scale;
|
|
field(menu, "ReSkate menu size");
|
|
const bool resettable = menu.scale != default_menu_scale;
|
|
const float reset = ImGui::CalcTextSize("Reset").x + ImGui::GetStyle().FramePadding.x * 2;
|
|
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - reset - ImGui::GetStyle().ItemSpacing.x);
|
|
ImGui::SliderFloat("##menu-scale", &menu.scale, min_menu_scale, max_menu_scale, "%.2fx",
|
|
ImGuiSliderFlags_AlwaysClamp);
|
|
menu.scale_editing = ImGui::IsItemActive();
|
|
if (ImGui::IsItemDeactivatedAfterEdit())
|
|
send_console(menu, callbacks, "ui scale " + std::to_string(menu.scale));
|
|
ImGui::SameLine();
|
|
ImGui::BeginDisabled(!resettable);
|
|
if (ImGui::Button("Reset")) {
|
|
menu.scale = default_menu_scale;
|
|
send_console(menu, callbacks, "ui scale " + std::to_string(default_menu_scale));
|
|
}
|
|
ImGui::EndDisabled();
|
|
note("Scales this menu and its text. Saved with your profile.");
|
|
end_card();
|
|
|
|
begin_card(menu, "performance", "PERFORMANCE");
|
|
// The profiler is thread-safe: its state is read here directly, not through the model.
|
|
bool hud = dingosdk::profiler::hud();
|
|
if (toggle_row(menu, "Performance HUD", "Client update and frame timing, ReSkate's cost and CPU per thread.",
|
|
hud, callbacks.queue_console_command != nullptr))
|
|
send_console(menu, callbacks, hud ? "perf hud on" : "perf hud off");
|
|
bool window = dingosdk::profiler::window();
|
|
if (toggle_row(menu, "Profiler window", "Zones, threads and a stack sampler. Shows while this menu or the console is open.",
|
|
window))
|
|
dingosdk::profiler::set_window(window);
|
|
note("Console: perf, perf sample [seconds] [client|present|thread id], perf report, perf status.");
|
|
end_card();
|
|
if (!model.steam_offline) multiplayer_display_settings(menu, model);
|
|
}
|
|
|
|
void binds_page(SkateMenu& menu, const Model& model, const CallbacksV3& callbacks) {
|
|
begin_card(menu, "controller-binds", "CONTROLLER BINDS");
|
|
const bool available = model.bindings.available && callbacks.queue_console_command;
|
|
const auto save = [&](int action, std::uint32_t combo) {
|
|
std::array<char, 512> result{};
|
|
const auto command = std::string("bind ") +
|
|
(action == 1 ? "noclip " : action == 2 ? "forwardvelocity " : "upvelocity ") + std::to_string(combo);
|
|
const bool queued = callbacks.queue_console_command(callbacks.user, command.c_str(), result.data(), result.size());
|
|
result.back() = '\0';
|
|
feedback(menu, result[0] ? result.data() : queued ? "Saving binding..." : "Could not queue binding.");
|
|
};
|
|
ControllerInput controller;
|
|
DingoSDKOverlayReadControllerInput(&controller, true);
|
|
if (menu.recording_bind) {
|
|
if (!available || ImGui::GetTime() >= menu.bind_capture_until) {
|
|
menu.recording_bind = 0;
|
|
feedback(menu, "Recording cancelled. Your binding is unchanged.");
|
|
} else if (const auto combo = menu.bind_capture.update(controller)) {
|
|
const auto action = menu.recording_bind;
|
|
menu.recording_bind = 0;
|
|
save(action, *combo);
|
|
}
|
|
}
|
|
ImGui::BeginDisabled(!available);
|
|
if (ImGui::BeginTable("controller-binds", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp)) {
|
|
ImGui::TableSetupColumn("Action", ImGuiTableColumnFlags_WidthFixed, px(130));
|
|
ImGui::TableSetupColumn("Controller combo", ImGuiTableColumnFlags_WidthStretch);
|
|
ImGui::TableSetupColumn("##controls", ImGuiTableColumnFlags_WidthFixed, px(170));
|
|
ImGui::TableHeadersRow();
|
|
const auto row = [&](int action, const char* name, std::uint32_t combo) {
|
|
ImGui::PushID(action);
|
|
ImGui::TableNextRow(); ImGui::TableNextColumn();
|
|
ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted(name);
|
|
ImGui::TableNextColumn(); ImGui::AlignTextToFramePadding();
|
|
const auto label = menu.recording_bind == action ? "Recording..." : controller_combo_label(combo, controller.style);
|
|
ImGui::TextWrapped("%s", label.c_str());
|
|
ImGui::TableNextColumn();
|
|
if (menu.recording_bind == action) {
|
|
if (ImGui::Button("Cancel", ImVec2(-1, 0))) menu.recording_bind = 0;
|
|
} else {
|
|
ImGui::BeginDisabled(menu.recording_bind != 0);
|
|
if (ImGui::Button("Record", ImVec2(px(90), 0))) {
|
|
menu.bind_capture = {};
|
|
menu.recording_bind = action;
|
|
menu.bind_capture_until = ImGui::GetTime() + 30;
|
|
menu.feedback.clear();
|
|
}
|
|
ImGui::SameLine(); ImGui::BeginDisabled(!combo);
|
|
if (ImGui::Button("Clear", ImVec2(-1, 0))) save(action, 0);
|
|
ImGui::EndDisabled();
|
|
ImGui::EndDisabled();
|
|
}
|
|
ImGui::PopID();
|
|
};
|
|
row(1, "Noclip", model.bindings.noclip_combo);
|
|
row(2, "Forward Boost", model.bindings.forward_velocity_combo);
|
|
row(3, "Up Boost", model.bindings.up_velocity_combo);
|
|
ImGui::EndTable();
|
|
}
|
|
ImGui::EndDisabled();
|
|
if (menu.recording_bind) {
|
|
warn(!controller.available ? "Connect a controller and keep the game focused to record. PlayStation pads work directly or through DS4Windows / Steam Input." :
|
|
!menu.bind_capture.ready ? "Release all controller buttons first." :
|
|
"Hold the buttons you want together, then release them to save.");
|
|
} else {
|
|
note(("Record a button or combo, such as " + controller_combo_label(0x300, controller.style) +
|
|
". Noclip toggles; Forward Boost and Up Boost add their velocity once per press. Use a different combo for each.").c_str());
|
|
note("Saved to your profile.");
|
|
if (!model.bindings.status.empty()) note(model.bindings.status.c_str());
|
|
if (!model.bindings.available) warn("Waiting for the local profile.");
|
|
}
|
|
if (!model.steam_offline) note("The push-to-talk button is set in Multiplayer > Voice.");
|
|
end_card();
|
|
}
|
|
|
|
void settings_page(SkateMenu& menu, const Model& model, const CallbacksV3& callbacks) {
|
|
menu.settings_tab = std::min(menu.settings_tab, 2); // Mods moved to its own page
|
|
category_tabs(menu, menu.settings_tab, {"CONTROLS", "INTERFACE", "POST FX"}, "settings-tabs");
|
|
ImGui::PushID(menu.settings_tab);
|
|
ImGui::BeginChild("settings-tab", ImVec2(0, page_body_height(menu)));
|
|
switch (menu.settings_tab) {
|
|
case 0: binds_page(menu, model, callbacks); break;
|
|
case 1: ui_page(menu, model, callbacks); break;
|
|
case 2: graphics_page(menu, model, callbacks); break;
|
|
}
|
|
ImGui::EndChild();
|
|
ImGui::PopID();
|
|
}
|
|
|
|
void developer_page(SkateMenu& menu, const Model& model, const CallbacksV3& callbacks) {
|
|
ImGui::BeginChild("developer", ImVec2(0, page_body_height(menu)));
|
|
multiplayer_network_page(menu, model, callbacks);
|
|
ImGui::EndChild();
|
|
}
|
|
}
|