mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-27 22:07:07 +01:00
Trainer 0.5.0: built on ReSkate 2.0.6
TUNE as one page of folding sections, presets above the tabs, Skate 3 grind and transition feel, Dark Pop, manual pop height, slow flips pop higher, per-trick speeds, and a release audit of stock / resets / presets. See Extension/Trainer/CHANGELOG.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wk6Y7hSKoBANMm9ZHbStLg
This commit is contained in:
commit
f027d319be
391 files changed
+47172
-5566
No files matched your search
@@ -1,16 +1,26 @@
|
||||
#include "client_source_spawn.h"
|
||||
#include "client_source_spawn_internal.h"
|
||||
#include "no_bail.h"
|
||||
#include "offboard_flight.h"
|
||||
#include "Engine/Core/Log/logging.h"
|
||||
#include "Engine/Core/Platform/launcher_support.h"
|
||||
#include "Engine/Game/Build/20260929/engine.h"
|
||||
#include "Engine/Game/Multiplayer/session_tools.h"
|
||||
#include "free_flight.h"
|
||||
#include "Engine/Game/Skater/first_person_cycle.h"
|
||||
#include "Engine/Game/Skater/velocity_boost.h"
|
||||
#include "reskate_version.h"
|
||||
#include "Extension/Multiplayer/Remote/native_skater.h"
|
||||
#include "Extension/Objects/ParkEditor/park_editor_runtime.h"
|
||||
#include "Extension/Profile/local_profile_runtime.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <format>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
@@ -60,6 +70,148 @@ std::array<float, 3> debug_position(SourceReader& reader, std::uintptr_t address
|
||||
return {row[0], row[1], row[2]};
|
||||
}
|
||||
}
|
||||
// ---- Dark Pop (auto release) -----------------------------------------------------------
|
||||
// The selector hook in no_bail.cpp times each flight, lends No Bail's protection to fired flights and turns their
|
||||
// bailed landings into pops; input_capture.cpp shows the game a timed release of D-pad Right. This tick publishes the switch and the timing, and reports (and optionally boosts) each flight.
|
||||
// Nothing is read until Dark Pop is switched on.
|
||||
extern "C" void DingoSDKOverlayDarkPopEnable(bool enabled) noexcept;
|
||||
extern "C" std::uint64_t DingoSDKOverlayDarkPopGamePolls() noexcept;
|
||||
namespace {
|
||||
void dark_pop_tick(InteractiveDebug& debug, std::uintptr_t client, std::uintptr_t entity,
|
||||
const NoclipBodies& bodies, bool ready) {
|
||||
const bool allowed = debug.dark_pop && ready && session_boosts_allowed() && !debug.noclip && !debug.park_editor;
|
||||
dark_pop_update(allowed, debug.dark_pop_release);
|
||||
DingoSDKOverlayDarkPopEnable(allowed);
|
||||
if (!debug.dark_pop) return;
|
||||
const auto last = dark_pop_last();
|
||||
if (last.pop_landings != debug.dark_pop_landings_seen) {
|
||||
debug.dark_pop_landings_seen = last.pop_landings;
|
||||
logging::log(logging::Level::info, logging::Channel::skater, "Dark Pop: the pop's flight landed (state {}){}, {} dismount(s) held off so far",
|
||||
last.pop_landed, last.pop_landed >= 100 && last.pop_landed < 200 ? ", riding away" : ", not riding away", last.holds);
|
||||
}
|
||||
if (last.flights == debug.dark_pop_seen) return;
|
||||
debug.dark_pop_seen = last.flights;
|
||||
if (!last.pressed) {
|
||||
// A flight without the press as the game saw it. Said a few times, so a controller the game does not read through
|
||||
// XInput (Dark Pop cannot act on it) or a press before the RB catch shows up in the log.
|
||||
if (debug.dark_pop_misses < 12) {
|
||||
++debug.dark_pop_misses;
|
||||
const auto polls = DingoSDKOverlayDarkPopGamePolls();
|
||||
logging::log(logging::Level::info, logging::Channel::skater,
|
||||
"Dark Pop: {:.3f} s flight, no D-pad Right after an RB catch seen in it ({} controller reads by the game so far{}).",
|
||||
last.seconds, polls, polls ? "" : ": the game is not reading this controller through XInput, so Dark Pop cannot act");
|
||||
}
|
||||
return;
|
||||
}
|
||||
const bool popped = last.landed == 200;
|
||||
const float strength = std::min(debug.dark_pop_strength, 25.0f);
|
||||
const bool boost = popped && strength >= 0.5f && source_state().velocity_guard_active.load(std::memory_order_acquire) &&
|
||||
!bodies.offboard && !debug.up_velocity.valid;
|
||||
if (boost) debug.up_velocity = {client, entity, bodies.core, GetTickCount64() + 1000, {0.0f, strength, 0.0f}, true};
|
||||
if (popped) ++debug.dark_pop_count;
|
||||
const char* outcome = popped ? (last.forced ? "bail turned into a pop" : "popped") :
|
||||
last.landed >= 100 && last.landed < 200 ? "rode away (no pop)" : "bailed";
|
||||
const auto polls = DingoSDKOverlayDarkPopGamePolls();
|
||||
logging::log(logging::Level::info, logging::Channel::skater,
|
||||
"Dark Pop: {:.3f} s flight, release shown {} -> game wanted {}, {}{}{}", last.seconds,
|
||||
last.release_after >= 0.0f ? std::format("{:.3f} s after takeoff", last.release_after) : std::string("only at landing"),
|
||||
last.wanted, outcome, boost ? std::format(", +{:.1f} m/s up", strength) : std::string(),
|
||||
polls ? std::string() : std::string(" (the game has not read the controller through XInput, so it saw no change)"));
|
||||
}
|
||||
}
|
||||
// ---- Dark pop probe -----------------------------------------------------------
|
||||
// Off unless switched on. While on it writes logs\dark-pop-probe.csv beside the game: one row per tick
|
||||
// with the physics state, the controller buttons, 40 words of the skater's request block (where the game
|
||||
// keeps "mount", "dismount" and similar requests), 64 floats of the board's first body and the skater's
|
||||
// transform. Doing a few natural dark pops (and failures) with it on shows which of those values the
|
||||
// game changes at the moment a pop happens, which is what a faithful Dark Pop needs to know.
|
||||
namespace {
|
||||
std::ofstream& probe_file() { static auto* file = new std::ofstream; return *file; }
|
||||
std::ofstream& fast_file() { static auto* file = new std::ofstream; return *file; }
|
||||
std::int64_t g_fast_base = 0;
|
||||
// Writes what the selector hook queued: logs\dark-pop-fast.csv, one row per physics-state choice with its time
|
||||
// in microseconds, the state before, the state the game wanted, the state returned, the buttons and 18 words.
|
||||
void fast_probe_flush() {
|
||||
auto& file = fast_file();
|
||||
if (!file.is_open()) return;
|
||||
static FastProbeRow rows[2048];
|
||||
LARGE_INTEGER frequency{};
|
||||
QueryPerformanceFrequency(&frequency);
|
||||
if (frequency.QuadPart <= 0) return;
|
||||
char text[24];
|
||||
for (int pass = 0; pass < 8; ++pass) {
|
||||
const auto count = fast_probe_drain(rows, 2048);
|
||||
for (std::size_t i = 0; i < count; ++i) {
|
||||
const auto& row = rows[i];
|
||||
if (!g_fast_base) g_fast_base = row.ticks;
|
||||
file << (row.ticks - g_fast_base) * 1000000 / frequency.QuadPart << ',' << row.current << ',' << row.wanted << ','
|
||||
<< row.chosen << ',' << row.buttons;
|
||||
for (const auto word : row.words) { std::snprintf(text, sizeof text, ",%x", word); file << text; }
|
||||
file << '\n';
|
||||
}
|
||||
if (count < 2048) break;
|
||||
}
|
||||
}
|
||||
void probe_tick(InteractiveDebug& debug, const std::array<float, 16>& transform, const NoclipBodies& bodies, bool ready) {
|
||||
auto& file = probe_file();
|
||||
if (!debug.probe || !ready) {
|
||||
if (file.is_open()) file.close();
|
||||
if (fast_file().is_open()) { fast_probe_enable(false); fast_probe_flush(); fast_file().close(); }
|
||||
return;
|
||||
}
|
||||
if (!file.is_open()) {
|
||||
wchar_t module[MAX_PATH]{};
|
||||
const auto length = GetModuleFileNameW(nullptr, module, MAX_PATH);
|
||||
if (!length || length >= MAX_PATH) { debug.probe = false; return; }
|
||||
// A new pair of files each time the probe is switched on, named by the time, so nothing is overwritten.
|
||||
char stamp[32]{};
|
||||
const std::time_t now_time = std::time(nullptr);
|
||||
std::tm local{};
|
||||
localtime_s(&local, &now_time);
|
||||
std::strftime(stamp, sizeof stamp, "%Y%m%d-%H%M%S", &local);
|
||||
const auto path = std::filesystem::path(module).parent_path() / "logs" / (std::string("dark-pop-probe-") + stamp + ".csv");
|
||||
std::error_code error;
|
||||
std::filesystem::create_directories(path.parent_path(), error);
|
||||
file.open(path, std::ios::out | std::ios::trunc);
|
||||
if (!file) { debug.probe = false; return; }
|
||||
file << "ms,state,pad";
|
||||
for (int i = 0; i < 40; ++i) file << ",c" << i;
|
||||
for (int i = 0; i < 64; ++i) file << ",b" << i;
|
||||
for (int i = 0; i < 16; ++i) file << ",t" << i;
|
||||
file << "\n";
|
||||
auto& fast = fast_file();
|
||||
fast.open(path.parent_path() / (std::string("dark-pop-fast-") + stamp + ".csv"), std::ios::out | std::ios::trunc);
|
||||
if (fast) {
|
||||
fast << "us,current,wanted,chosen,pad";
|
||||
for (int i = 0; i < 18; ++i) fast << ",w" << i;
|
||||
fast << "\n";
|
||||
g_fast_base = 0;
|
||||
fast_probe_enable(true);
|
||||
}
|
||||
logging::write(logging::Level::info, logging::Channel::skater, std::format("Dark pop probe: writing logs\\dark-pop-probe-{0}.csv and logs\\dark-pop-fast-{0}.csv", stamp));
|
||||
}
|
||||
// Guarded same-process copies of larger blocks (value<T> is limited to 16 bytes).
|
||||
SourceReader reader;
|
||||
std::uint32_t state{};
|
||||
std::array<std::uint32_t, 40> words{};
|
||||
std::array<float, 64> board{};
|
||||
source_require(reader.raw(bodies.context + 0x1414, &state, sizeof state) &&
|
||||
reader.raw(bodies.context + 0x13a0, words.data(), sizeof words) &&
|
||||
reader.raw(bodies.parts[0], board.data(), sizeof board),
|
||||
"Dark pop probe: the skater's data could not be read.");
|
||||
ControllerInput pad;
|
||||
DingoSDKOverlayReadControllerInput(&pad);
|
||||
char text[32];
|
||||
file << GetTickCount64() << ',' << state << ',' << (pad.available ? pad.buttons : 0u);
|
||||
for (const auto word : words) { std::snprintf(text, sizeof text, ",%x", word); file << text; }
|
||||
for (const auto value : board) { std::snprintf(text, sizeof text, ",%.4g", static_cast<double>(value)); file << text; }
|
||||
for (const auto value : transform) { std::snprintf(text, sizeof text, ",%.4g", static_cast<double>(value)); file << text; }
|
||||
file << '\n';
|
||||
fast_probe_flush();
|
||||
static ULONGLONG last_flush = 0;
|
||||
if (const auto now = GetTickCount64(); now - last_flush > 1000) { file.flush(); if (fast_file().is_open()) fast_file().flush(); last_flush = now; }
|
||||
}
|
||||
}
|
||||
std::array<float, 16> debug_skater(std::uintptr_t base, std::uintptr_t client, overlay::DebugModel& model) {
|
||||
SourceReader reader;
|
||||
source_require(reader.pointer(client) == base + addr::engine::client_vtable, "Client changed.");
|
||||
@@ -212,7 +364,9 @@ void debug_flight_tick(SourceTrial& trial, std::uintptr_t client, bool ready, bo
|
||||
arm.thread.store(GetCurrentThreadId(), std::memory_order_release);
|
||||
if (debug.first_person_waiting) {
|
||||
debug.first_person_waiting = false;
|
||||
debug.status = "First person on. The camera follows the skater's head.";
|
||||
debug.status = debug.first_person_settings.stabilize
|
||||
? "First person on. True first person keeps the view level and steady."
|
||||
: "First person on. The camera follows the skater's head.";
|
||||
}
|
||||
} else {
|
||||
next = step_free_flight(debug.flight_matrix, input ? *input : idle, seconds, debug.flight_speed);
|
||||
@@ -242,12 +396,18 @@ constexpr const char* no_bail = "NoBail";
|
||||
constexpr const char* flight_speed = "FlightSpeed";
|
||||
constexpr const char* forward_velocity = "ForwardVelocity";
|
||||
constexpr const char* up_velocity = "UpVelocity";
|
||||
constexpr const char* offboard_up_velocity = "OffboardUpVelocity";
|
||||
constexpr const char* first_person_fov = "FirstPerson.Fov";
|
||||
constexpr const char* free_camera_fov = "FreeCamera.Fov";
|
||||
constexpr const char* spring = "FirstPerson.Spring";
|
||||
constexpr std::array<const char*, 3> offset{"FirstPerson.OffsetX", "FirstPerson.OffsetY", "FirstPerson.OffsetZ"};
|
||||
constexpr std::array<const char*, 3> rotation{"FirstPerson.Pitch", "FirstPerson.Yaw", "FirstPerson.Roll"};
|
||||
constexpr std::array<const char*, 4> strength{"FirstPerson.Up", "FirstPerson.Down", "FirstPerson.Left", "FirstPerson.Right"};
|
||||
constexpr const char* stabilize = "FirstPerson.Stabilize";
|
||||
constexpr const char* follow_flips = "FirstPerson.FollowFlips";
|
||||
constexpr const char* board_only = "FirstPerson.ThirdPersonOnFoot";
|
||||
constexpr const char* camera_cycle = "FirstPerson.CameraCycle";
|
||||
constexpr std::array<const char*, 4> steady{"FirstPerson.Smoothing", "FirstPerson.HeadPitch", "FirstPerson.HeadRoll", "FirstPerson.Bob"};
|
||||
constexpr ULONGLONG delay_ms = 750;
|
||||
|
||||
std::optional<float> number(const char* key) {
|
||||
@@ -272,6 +432,7 @@ void save_debug(InteractiveDebug& debug) noexcept {
|
||||
{saved::flight_speed, static_cast<double>(debug.flight_speed)},
|
||||
{saved::forward_velocity, static_cast<double>(debug.forward_velocity_speed)},
|
||||
{saved::up_velocity, static_cast<double>(debug.up_velocity_speed)},
|
||||
{saved::offboard_up_velocity, static_cast<double>(debug.offboard_up_velocity_speed)},
|
||||
{saved::first_person_fov, static_cast<double>(debug.first_person_fov)},
|
||||
{saved::free_camera_fov, static_cast<double>(debug.free_camera_fov)},
|
||||
{saved::spring, arm.enabled}};
|
||||
@@ -281,6 +442,12 @@ void save_debug(InteractiveDebug& debug) noexcept {
|
||||
}
|
||||
const std::array<float, 4> strengths{arm.up, arm.down, arm.left, arm.right};
|
||||
for (std::size_t i = 0; i < 4; ++i) values.emplace_back(saved::strength[i], static_cast<double>(strengths[i]));
|
||||
values.emplace_back(saved::stabilize, arm.stabilize);
|
||||
values.emplace_back(saved::follow_flips, arm.follow_flips);
|
||||
values.emplace_back(saved::board_only, arm.board_only);
|
||||
values.emplace_back(saved::camera_cycle, arm.camera_cycle);
|
||||
const std::array<float, 4> steady{arm.smoothing, arm.head_pitch, arm.head_roll, arm.bob};
|
||||
for (std::size_t i = 0; i < 4; ++i) values.emplace_back(saved::steady[i], static_cast<double>(steady[i]));
|
||||
profile_runtime::set_local_values(values);
|
||||
} catch (...) { /* Saving is best effort; the choices already apply. */ }
|
||||
}
|
||||
@@ -300,6 +467,8 @@ void load_saved_debug(InteractiveDebug& debug) noexcept {
|
||||
debug.forward_velocity_speed = *speed;
|
||||
if (const auto speed = saved::number(saved::up_velocity); speed && *speed >= 1.0f && *speed <= 25.0f)
|
||||
debug.up_velocity_speed = *speed;
|
||||
if (const auto speed = saved::number(saved::offboard_up_velocity); speed && *speed >= 1.0f && *speed <= 25.0f)
|
||||
debug.offboard_up_velocity_speed = *speed;
|
||||
if (const auto fov = saved::number(saved::first_person_fov);
|
||||
fov && *fov >= first_person_fov_min && *fov <= first_person_fov_max) {
|
||||
debug.first_person_fov = *fov;
|
||||
@@ -317,6 +486,13 @@ void load_saved_debug(InteractiveDebug& debug) noexcept {
|
||||
const std::array<float*, 4> strengths{&arm.up, &arm.down, &arm.left, &arm.right};
|
||||
for (std::size_t i = 0; i < 4; ++i)
|
||||
if (const auto value = saved::number(saved::strength[i])) *strengths[i] = *value;
|
||||
if (const auto enabled = profile_runtime::local_preference(saved::stabilize)) arm.stabilize = *enabled;
|
||||
if (const auto enabled = profile_runtime::local_preference(saved::follow_flips)) arm.follow_flips = *enabled;
|
||||
if (const auto enabled = profile_runtime::local_preference(saved::board_only)) arm.board_only = *enabled;
|
||||
if (const auto enabled = profile_runtime::local_preference(saved::camera_cycle)) arm.camera_cycle = *enabled;
|
||||
const std::array<float*, 4> steady{&arm.smoothing, &arm.head_pitch, &arm.head_roll, &arm.bob};
|
||||
for (std::size_t i = 0; i < 4; ++i)
|
||||
if (const auto value = saved::number(saved::steady[i])) *steady[i] = *value;
|
||||
if (first_person::valid(arm)) {
|
||||
debug.first_person_settings = arm;
|
||||
first_person_arm().settings = arm;
|
||||
@@ -372,10 +548,13 @@ void debug_action(SourceTrial& trial, std::uintptr_t client, bool can_control, b
|
||||
source_require(!debug.park_editor || debug.editor_transition || request.action == overlay::DebugAction::restore_debug ||
|
||||
request.action == overlay::DebugAction::set_camera_speed || request.action == overlay::DebugAction::set_forward_velocity_speed ||
|
||||
request.action == overlay::DebugAction::set_up_velocity_speed ||
|
||||
request.action == overlay::DebugAction::set_offboard_up_velocity_speed ||
|
||||
request.action == overlay::DebugAction::set_first_person_fov ||
|
||||
request.action == overlay::DebugAction::set_free_camera_fov ||
|
||||
(request.action >= overlay::DebugAction::set_first_person_spring && request.action <= overlay::DebugAction::reset_first_person_arm) ||
|
||||
request.action == overlay::DebugAction::set_no_bail,
|
||||
request.action == overlay::DebugAction::set_no_bail || request.action == overlay::DebugAction::set_dark_pop_strength ||
|
||||
request.action == overlay::DebugAction::set_dark_pop_release ||
|
||||
(request.action == overlay::DebugAction::set_dark_pop_enabled && !request.enabled),
|
||||
"Close Park Editor before changing camera or HUD modes.");
|
||||
if (request.action >= overlay::DebugAction::set_first_person_spring &&
|
||||
request.action <= overlay::DebugAction::reset_first_person_arm) {
|
||||
@@ -393,17 +572,33 @@ void debug_action(SourceTrial& trial, std::uintptr_t client, bool can_control, b
|
||||
case Action::set_first_person_spring_down: settings.down = request.value; break;
|
||||
case Action::set_first_person_spring_left: settings.left = request.value; break;
|
||||
case Action::set_first_person_spring_right: settings.right = request.value; break;
|
||||
case Action::reset_first_person_arm: settings = {}; break;
|
||||
case Action::set_first_person_stabilize: settings.stabilize = request.enabled; break;
|
||||
case Action::set_first_person_follow_flips: settings.follow_flips = request.enabled; break;
|
||||
case Action::set_first_person_smoothing: settings.smoothing = request.value; break;
|
||||
case Action::set_first_person_head_pitch: settings.head_pitch = request.value; break;
|
||||
case Action::set_first_person_head_roll: settings.head_roll = request.value; break;
|
||||
case Action::set_first_person_bob: settings.bob = request.value; break;
|
||||
case Action::set_first_person_board_only: settings.board_only = request.enabled; break;
|
||||
case Action::set_first_person_camera_cycle: settings.camera_cycle = request.enabled; break;
|
||||
// Resets the view; which steps the camera height button goes through is kept.
|
||||
case Action::reset_first_person_arm: settings = {.camera_cycle = settings.camera_cycle}; break;
|
||||
default: break;
|
||||
}
|
||||
source_require(first_person::valid(settings), "First-person arm setting is outside its supported range.");
|
||||
auto& arm = first_person_arm();
|
||||
if (settings.enabled != debug.first_person_settings.enabled || request.action == Action::reset_first_person_arm)
|
||||
if (settings.enabled != debug.first_person_settings.enabled ||
|
||||
settings.stabilize != debug.first_person_settings.stabilize || request.action == Action::reset_first_person_arm)
|
||||
arm.spring.reset();
|
||||
debug.first_person_settings = settings;
|
||||
arm.settings = settings;
|
||||
mark_debug_changed(debug);
|
||||
debug.status = "First-person arm settings updated.";
|
||||
debug.status = request.action == Action::set_first_person_stabilize
|
||||
? (settings.stabilize ? "True first person on: level horizon, steady view." : "True first person off: the view follows the raw head.")
|
||||
: request.action == Action::set_first_person_board_only
|
||||
? (settings.board_only ? "Third person while walking; first person on the board." : "First person while walking too.")
|
||||
: request.action == Action::set_first_person_camera_cycle
|
||||
? (settings.camera_cycle ? "Camera height button: high, low, first person." : "Camera height button: high and low only.")
|
||||
: "First-person settings updated.";
|
||||
return;
|
||||
}
|
||||
if (request.action == overlay::DebugAction::set_free_camera_fov) {
|
||||
@@ -424,13 +619,48 @@ void debug_action(SourceTrial& trial, std::uintptr_t client, bool can_control, b
|
||||
return;
|
||||
}
|
||||
if (request.action == overlay::DebugAction::set_forward_velocity_speed ||
|
||||
request.action == overlay::DebugAction::set_up_velocity_speed) {
|
||||
const bool up = request.action == overlay::DebugAction::set_up_velocity_speed;
|
||||
source_require(std::isfinite(request.value) && request.value >= 1.0f && request.value <= (up ? 25.0f : 300.0f),
|
||||
up ? "Up Boost speed must be between 1 and 25." : "Forward Boost speed must be between 1 and 300.");
|
||||
(up ? debug.up_velocity_speed : debug.forward_velocity_speed) = request.value;
|
||||
request.action == overlay::DebugAction::set_up_velocity_speed ||
|
||||
request.action == overlay::DebugAction::set_offboard_up_velocity_speed) {
|
||||
if (request.action == overlay::DebugAction::set_offboard_up_velocity_speed) {
|
||||
source_require(std::isfinite(request.value) && request.value >= 1.0f && request.value <= 25.0f,
|
||||
"Off-board Up Boost speed must be between 1 and 25.");
|
||||
debug.offboard_up_velocity_speed = request.value;
|
||||
debug.status = "Off-board Up Boost speed updated.";
|
||||
} else {
|
||||
const bool up = request.action == overlay::DebugAction::set_up_velocity_speed;
|
||||
source_require(std::isfinite(request.value) && request.value >= 1.0f && request.value <= (up ? 25.0f : 300.0f),
|
||||
up ? "Up Boost speed must be between 1 and 25." : "Forward Boost speed must be between 1 and 300.");
|
||||
(up ? debug.up_velocity_speed : debug.forward_velocity_speed) = request.value;
|
||||
debug.status = up ? "Up Boost speed updated." : "Forward Boost speed updated.";
|
||||
}
|
||||
mark_debug_changed(debug);
|
||||
debug.status = up ? "Up Boost speed updated." : "Forward Boost speed updated.";
|
||||
return;
|
||||
}
|
||||
if (request.action == overlay::DebugAction::set_dark_pop_strength) {
|
||||
source_require(std::isfinite(request.value) && request.value >= 0.0f && request.value <= 25.0f,
|
||||
"Dark Pop extra pop must be between 0 and 25.");
|
||||
debug.dark_pop_strength = request.value;
|
||||
debug.status = "Dark Pop extra pop updated.";
|
||||
return;
|
||||
}
|
||||
if (request.action == overlay::DebugAction::set_dark_pop_release) {
|
||||
source_require(std::isfinite(request.value) && request.value >= 0.55f && request.value <= 0.90f,
|
||||
"Dark Pop release must be between 0.55 and 0.90 seconds after takeoff.");
|
||||
debug.dark_pop_release = request.value;
|
||||
debug.status = "Dark Pop release timing updated.";
|
||||
return;
|
||||
}
|
||||
if (request.action == overlay::DebugAction::set_dark_pop_enabled && !request.enabled) {
|
||||
debug.dark_pop = false;
|
||||
dark_pop_update(false, debug.dark_pop_release);
|
||||
DingoSDKOverlayDarkPopEnable(false);
|
||||
debug.status = "Dark Pop disabled.";
|
||||
logging::write(logging::Level::info, logging::Channel::skater, "Dark Pop disabled");
|
||||
return;
|
||||
}
|
||||
if (request.action == overlay::DebugAction::set_probe_enabled) {
|
||||
debug.probe = request.enabled;
|
||||
debug.status = request.enabled ? "Dark pop probe on: writing a new pair of files in logs, named by the time." : "Dark pop probe off.";
|
||||
return;
|
||||
}
|
||||
if (request.action == overlay::DebugAction::restore_debug) {
|
||||
@@ -443,8 +673,13 @@ void debug_action(SourceTrial& trial, std::uintptr_t client, bool can_control, b
|
||||
debug.forward_velocity_speed = 20.0f;
|
||||
debug.forward_velocity.valid = false;
|
||||
debug.up_velocity_speed = 20.0f;
|
||||
debug.offboard_up_velocity_speed = 20.0f;
|
||||
debug.up_velocity.valid = false;
|
||||
debug.offboard_up_velocity.valid = false;
|
||||
debug.no_bail = false;
|
||||
debug.dark_pop = false;
|
||||
dark_pop_update(false, debug.dark_pop_release);
|
||||
DingoSDKOverlayDarkPopEnable(false);
|
||||
debug_stop_noclip(debug);
|
||||
clear_no_bail();
|
||||
// Level loads drop temporary modes, never the player's saved choices.
|
||||
@@ -475,27 +710,73 @@ void debug_action(SourceTrial& trial, std::uintptr_t client, bool can_control, b
|
||||
return;
|
||||
}
|
||||
source_require(can_control, "Wait for an active local level before changing debug settings.");
|
||||
if (request.action == overlay::DebugAction::add_forward_velocity || request.action == overlay::DebugAction::add_up_velocity) {
|
||||
if (request.action == overlay::DebugAction::add_forward_velocity || request.action == overlay::DebugAction::add_up_velocity || request.action == overlay::DebugAction::add_offboard_up_velocity) {
|
||||
source_require(session_boosts_allowed(), "The host has turned off boosts in this session.");
|
||||
const bool up = request.action == overlay::DebugAction::add_up_velocity;
|
||||
const bool offboard_boost = request.action == overlay::DebugAction::add_offboard_up_velocity;
|
||||
const bool up = request.action == overlay::DebugAction::add_up_velocity || offboard_boost;
|
||||
source_require(!debug.noclip, "Disable Noclip before using velocity boosts.");
|
||||
source_require(source_state().velocity_guard_active.load(std::memory_order_acquire),
|
||||
"Velocity boosts are unavailable for this game build.");
|
||||
overlay::DebugModel skater;
|
||||
const auto transform = debug_skater(trial.base, client, skater);
|
||||
const auto bodies = debug_noclip_bodies(trial.base, client, skater.skater_identity);
|
||||
source_require(!bodies.offboard, "Velocity boosts require the skater to be on the board.");
|
||||
if (offboard_boost) {
|
||||
source_require(bodies.offboard || bodies.wipeout, "Off-board Up Boost requires walking, falling or gliding.");
|
||||
} else {
|
||||
source_require(!bodies.offboard, "Velocity boosts require the skater to be on the board.");
|
||||
}
|
||||
SourceReader reader;
|
||||
source_require(reader.value<std::uint8_t>(skater.skater_identity, 0x7e0) == 0,
|
||||
"Wait for the current teleport before using velocity boosts.");
|
||||
const auto board_velocity = up ? std::array<float, 3>{} :
|
||||
reader.value<std::array<float, 3>>(bodies.parts[0], 0x70);
|
||||
reader.verify();
|
||||
const auto velocity = velocity_boost_delta(transform, up ? debug.up_velocity_speed : debug.forward_velocity_speed,
|
||||
up ? VelocityBoostDirection::up : VelocityBoostDirection::forward);
|
||||
const float speed = offboard_boost ? debug.offboard_up_velocity_speed : (up ? debug.up_velocity_speed : debug.forward_velocity_speed);
|
||||
const auto velocity = velocity_boost_delta(transform, speed,
|
||||
up ? VelocityBoostDirection::up : VelocityBoostDirection::forward, board_velocity);
|
||||
source_require(velocity.has_value(), "Velocity boost direction or speed is invalid.");
|
||||
auto& boost = up ? debug.up_velocity : debug.forward_velocity;
|
||||
source_require(!boost.valid, "Velocity boost is already queued.");
|
||||
auto& boost = offboard_boost ? debug.offboard_up_velocity : (up ? debug.up_velocity : debug.forward_velocity);
|
||||
source_require(offboard_boost || !boost.valid, "Velocity boost is already queued.");
|
||||
boost = {client, skater.skater_identity, bodies.core, GetTickCount64() + 1000, *velocity, true};
|
||||
debug.status = up ? "Up Boost queued." : "Forward Boost queued.";
|
||||
if (offboard_boost) source_state().offboard_boost_core.store(bodies.core, std::memory_order_release);
|
||||
if (offboard_boost) {
|
||||
// Give the player the impulse on this client tick. The motion hook
|
||||
// keeps its upward speed briefly if native off-board drive rewrites it.
|
||||
const float current_up = reader.value<float>(bodies.parts[9], 0x74);
|
||||
const float target_up = std::max(speed, current_up + speed);
|
||||
source_require(std::isfinite(target_up) && target_up <= 100000,
|
||||
"Off-board boost produced an invalid upward speed.");
|
||||
std::array<std::array<float, 3>, 32> velocities{};
|
||||
std::array<std::uint32_t, 32> flags{};
|
||||
for (std::size_t i = 9; i < bodies.parts.size(); ++i) {
|
||||
velocities[i] = reader.value<std::array<float, 3>>(bodies.parts[i], 0x70);
|
||||
flags[i] = reader.value<std::uint32_t>(bodies.parts[i], 0x60);
|
||||
velocities[i][1] = std::max(velocities[i][1], target_up);
|
||||
}
|
||||
reader.verify();
|
||||
if (bodies.offboard)
|
||||
source_require(ensure_offboard_up_velocity(trial.base, bodies.core, bodies.context,
|
||||
bodies.rig_wrapper, target_up), "Off-board boost motion ownership changed.");
|
||||
for (std::size_t i = 9; i < bodies.parts.size(); ++i) {
|
||||
debug_write(bodies.parts[i] + 0x70, velocities[i]);
|
||||
debug_write(bodies.parts[i] + 0x60, flags[i] | 8u);
|
||||
}
|
||||
boost.velocity[1] = target_up;
|
||||
boost.applied = true;
|
||||
boost.expires = GetTickCount64() + 180;
|
||||
++debug.offboard_up_velocity_updates;
|
||||
debug.status = "Off-board up velocity added.";
|
||||
} else debug.status = up ? "Up Boost queued." : "Forward Boost queued.";
|
||||
return;
|
||||
}
|
||||
if (request.action == overlay::DebugAction::set_dark_pop_enabled) {
|
||||
source_require(session_boosts_allowed(), "The host has turned off boosts in this session.");
|
||||
debug.dark_pop = true;
|
||||
mark_debug_changed(debug);
|
||||
debug.status = "Dark Pop enabled. Catch with RB, press D-pad Right in the air; the mod times the release.";
|
||||
logging::log(logging::Level::info, logging::Channel::skater, "Dark Pop (auto release) enabled, release {:.3f} s after takeoff{}",
|
||||
debug.dark_pop_release,
|
||||
dingosdk::reskate_modified_build ? std::format(" (modified build {} {})", dingosdk::reskate_mod_id, dingosdk::reskate_mod_version) : std::string());
|
||||
return;
|
||||
}
|
||||
if (request.action == overlay::DebugAction::set_no_bail) {
|
||||
@@ -530,6 +811,7 @@ void debug_action(SourceTrial& trial, std::uintptr_t client, bool can_control, b
|
||||
source_require(session_noclip_allowed(), "The host has turned off noclip in this session.");
|
||||
debug.forward_velocity.valid = false;
|
||||
debug.up_velocity.valid = false;
|
||||
debug.offboard_up_velocity.valid = false;
|
||||
source_require(no_bail_available(), "Noclip requires the native No Bail hooks.");
|
||||
source_require(source_state().velocity_guard_active.load(std::memory_order_acquire),
|
||||
"Velocity flight is unavailable for this game build.");
|
||||
@@ -658,6 +940,93 @@ void debug_action(SourceTrial& trial, std::uintptr_t client, bool can_control, b
|
||||
}
|
||||
source_require(false, "Unknown debug request.");
|
||||
}
|
||||
// "Third person on foot": runs only while first person is on or this paused
|
||||
// it, so players who never use first person are untouched. Switches through
|
||||
// the same actions as the menu toggle, after the state has held briefly.
|
||||
void first_person_board_tick(SourceTrial& trial, std::uintptr_t client, bool can_control, bool phase, DWORD error) {
|
||||
auto& debug = trial.debug;
|
||||
// Choosing Freecam or the Park Editor while handed back ends the hand-back.
|
||||
if (debug.first_person_paused && (debug.park_editor || (debug.camera_owned && !debug.first_person)))
|
||||
debug.first_person_paused = false;
|
||||
// With the option off, a hand-back still in progress resumes below.
|
||||
if (!debug.first_person_paused && !(debug.first_person && debug.first_person_settings.board_only)) return;
|
||||
if (!can_control || !phase || debug.park_editor || debug.noclip || debug.editor_transition) return;
|
||||
const auto now = GetTickCount64();
|
||||
const auto on_foot = debug.first_person_settings.board_only ? first_person_on_foot(trial.base, client) : std::optional<bool>(false);
|
||||
if (!on_foot) return;
|
||||
if (*on_foot != debug.first_person_last_on_foot) {
|
||||
debug.first_person_last_on_foot = *on_foot;
|
||||
debug.first_person_foot_since = now;
|
||||
}
|
||||
const auto held = now - debug.first_person_foot_since;
|
||||
if (now < debug.first_person_retry_after) return;
|
||||
try {
|
||||
if (debug.first_person && *on_foot && held >= 250) {
|
||||
debug_action(trial, client, can_control, phase, {overlay::DebugAction::set_first_person, false}, error);
|
||||
debug.first_person_paused = true;
|
||||
debug.status = "On foot: third person until you are back on the board.";
|
||||
} else if (debug.first_person_paused && !*on_foot && held >= 120) {
|
||||
debug_action(trial, client, can_control, phase, {overlay::DebugAction::set_first_person, true}, error);
|
||||
debug.first_person_paused = !debug.first_person;
|
||||
}
|
||||
} catch (const SourceGuard& guard) {
|
||||
debug.status = guard.message;
|
||||
debug.first_person_retry_after = now + 500;
|
||||
} catch (...) {
|
||||
debug.first_person_retry_after = now + 500;
|
||||
}
|
||||
}
|
||||
// "First person in the camera cycle" (Engine/Game/Skater/first_person_cycle.h). The game writes
|
||||
// its camera height option on the thread that handles the button, so each tick publishes what
|
||||
// that write needs, and the next tick carries out the step it asked for.
|
||||
namespace {
|
||||
enum CycleFlag : std::uint8_t { cycle_enabled = 1, cycle_first_person = 2, cycle_can_enter = 4 };
|
||||
std::atomic<std::uint8_t> cycle_flags{}, cycle_request{};
|
||||
// When the flags were published: a tick that stopped running (loading, no local skater) leaves
|
||||
// the game's own height alone.
|
||||
std::atomic<ULONGLONG> cycle_published{};
|
||||
constexpr ULONGLONG cycle_flags_lifetime_ms = 250;
|
||||
}
|
||||
bool first_person_camera_height_write(bool high) noexcept {
|
||||
const auto published = cycle_published.load(std::memory_order_acquire);
|
||||
const auto flags = cycle_flags.load(std::memory_order_acquire);
|
||||
const auto now = GetTickCount64();
|
||||
if (now < published || now - published > cycle_flags_lifetime_ms) return high;
|
||||
const auto write = first_person::cycle_height_write(
|
||||
{(flags & cycle_enabled) != 0, (flags & cycle_first_person) != 0, (flags & cycle_can_enter) != 0}, high);
|
||||
if (write.step != first_person::CycleStep::none)
|
||||
cycle_request.store(static_cast<std::uint8_t>(write.step), std::memory_order_release);
|
||||
return write.high;
|
||||
}
|
||||
void first_person_cycle_tick(SourceTrial& trial, std::uintptr_t client, bool can_control, bool phase, DWORD error) {
|
||||
auto& debug = trial.debug;
|
||||
const auto now = GetTickCount64();
|
||||
const bool enabled = debug.first_person_settings.camera_cycle && can_control && phase &&
|
||||
!debug.park_editor && !debug.noclip && !debug.editor_transition;
|
||||
const auto step = static_cast<first_person::CycleStep>(cycle_request.exchange(0, std::memory_order_acq_rel));
|
||||
if (enabled && step != first_person::CycleStep::none) {
|
||||
// The player's own First person choice, so it replaces an on-foot hand-back.
|
||||
debug.first_person_paused = false;
|
||||
try {
|
||||
debug_action(trial, client, can_control, phase,
|
||||
{overlay::DebugAction::set_first_person, step == first_person::CycleStep::enter}, error);
|
||||
} catch (const SourceGuard& guard) {
|
||||
debug.status = guard.message;
|
||||
// The press kept the low camera; the next ones switch height as the game does.
|
||||
debug.first_person_cycle_retry_after = now + 2000;
|
||||
} catch (...) {
|
||||
debug.first_person_cycle_retry_after = now + 2000;
|
||||
}
|
||||
}
|
||||
bool can_enter = false;
|
||||
if (enabled && !debug.first_person && !debug.camera_owned && now >= debug.first_person_cycle_retry_after) {
|
||||
const auto on_foot = first_person_on_foot(trial.base, client);
|
||||
can_enter = on_foot && !*on_foot;
|
||||
}
|
||||
cycle_flags.store(static_cast<std::uint8_t>((enabled ? cycle_enabled : 0) | (debug.first_person ? cycle_first_person : 0) |
|
||||
(can_enter ? cycle_can_enter : 0)), std::memory_order_release);
|
||||
cycle_published.store(now, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -669,6 +1038,7 @@ overlay::DebugModel on_client_debug_tick(std::uintptr_t base, std::uintptr_t cli
|
||||
SourceLastError error;
|
||||
overlay::DebugModel model;
|
||||
auto& state = source_state();
|
||||
model.free_camera = state.free_camera_active.load(std::memory_order_acquire);
|
||||
if (!state.initialized.load(std::memory_order_acquire) || state.busy.test_and_set(std::memory_order_acquire)) return model;
|
||||
SourceBusyScope scope{state.busy};
|
||||
auto& trial = state.trial;
|
||||
@@ -678,6 +1048,7 @@ overlay::DebugModel on_client_debug_tick(std::uintptr_t base, std::uintptr_t cli
|
||||
if (!debug.saved_loaded) {
|
||||
debug.saved_loaded = true;
|
||||
load_saved_debug(debug);
|
||||
profile_runtime::set_camera_height_write_listener(&first_person_camera_height_write);
|
||||
}
|
||||
if (debug.save_pending && GetTickCount64() >= debug.save_due) save_debug(debug);
|
||||
if (debug.no_bail_restore && can_control && !debug.park_editor && GetTickCount64() >= debug.no_bail_restore_after) {
|
||||
@@ -687,6 +1058,7 @@ overlay::DebugModel on_client_debug_tick(std::uintptr_t base, std::uintptr_t cli
|
||||
if (!can_control) {
|
||||
debug.forward_velocity.valid = false;
|
||||
debug.up_velocity.valid = false;
|
||||
debug.offboard_up_velocity.valid = false;
|
||||
}
|
||||
if (debug.park_editor && !lobby_object_placement_allowed() && can_control && camera_phase_observed) {
|
||||
try {
|
||||
@@ -704,10 +1076,19 @@ overlay::DebugModel on_client_debug_tick(std::uintptr_t base, std::uintptr_t cli
|
||||
}
|
||||
const bool no_bail_allowed = session_no_bail_allowed();
|
||||
if (request) {
|
||||
// The player's own First person choice replaces any on-foot hand-back.
|
||||
if (request->action == overlay::DebugAction::set_first_person || request->action == overlay::DebugAction::restore_debug)
|
||||
debug.first_person_paused = false;
|
||||
try { debug_action(trial, client, can_control, camera_phase_observed, *request, error.value); }
|
||||
catch (const SourceGuard& guard) { debug.status = guard.message; }
|
||||
catch (...) { debug.status = "Debug action failed; inspect the current state before retrying."; }
|
||||
}
|
||||
first_person_board_tick(trial, client, can_control, camera_phase_observed, error.value);
|
||||
first_person_cycle_tick(trial, client, can_control, camera_phase_observed, error.value);
|
||||
// Camera ownership survives a busy tick or a failed presentation snapshot.
|
||||
// Input capture must follow the lease, not those transient model failures.
|
||||
state.free_camera_active.store(debug.camera_owned && !debug.first_person, std::memory_order_release);
|
||||
model.free_camera = debug.camera_owned && !debug.first_person;
|
||||
std::optional<SourceCameraSnapshot> flight_camera;
|
||||
try { debug_flight_tick(trial, client, can_control, camera_phase_observed, flight_input, error.value, flight_camera); }
|
||||
catch (const SourceGuard& guard) { debug_stop_noclip(debug); debug.status = guard.message; }
|
||||
@@ -726,7 +1107,8 @@ overlay::DebugModel on_client_debug_tick(std::uintptr_t base, std::uintptr_t cli
|
||||
const auto camera = flight_camera ? *flight_camera : source_camera_snapshot(trial, client);
|
||||
const bool owned_view = camera.mode == 1 && camera.active == camera.identity.camera;
|
||||
model.free_camera = owned_view && !debug.first_person;
|
||||
model.first_person = owned_view && debug.first_person;
|
||||
// Handed back on foot still reads as on, so the toggle can switch it off.
|
||||
model.first_person = (owned_view && debug.first_person) || debug.first_person_paused;
|
||||
model.first_person_fov = debug.first_person_fov;
|
||||
model.free_camera_fov = debug.free_camera_fov;
|
||||
model.camera_available = can_control && camera_phase_observed && !debug.camera_ambiguous;
|
||||
@@ -767,8 +1149,13 @@ overlay::DebugModel on_client_debug_tick(std::uintptr_t base, std::uintptr_t cli
|
||||
debug.noclip ? "Disable Noclip before using Up Boost." :
|
||||
!state.velocity_guard_active.load(std::memory_order_acquire) ? "Up Boost is unavailable for this game build." :
|
||||
"Local skater physics could not be read.";
|
||||
model.offboard_up_velocity_unavailable = !can_control ? "Waiting for active local controls." :
|
||||
debug.park_editor ? "Close Park Editor before using Off-board Up Boost." :
|
||||
debug.noclip ? "Disable Noclip before using Off-board Up Boost." :
|
||||
!state.velocity_guard_active.load(std::memory_order_acquire) ? "Off-board Up Boost is unavailable for this game build." :
|
||||
"Local skater physics could not be read.";
|
||||
try {
|
||||
(void)debug_skater(base, client, model);
|
||||
const auto skater_transform = debug_skater(base, client, model);
|
||||
const auto bodies = debug_noclip_bodies(base, client, model.skater_identity);
|
||||
if (debug.forward_velocity.valid && (GetTickCount64() >= debug.forward_velocity.expires ||
|
||||
debug.forward_velocity.entity != model.skater_identity || debug.forward_velocity.core != bodies.core)) {
|
||||
@@ -798,6 +1185,26 @@ overlay::DebugModel on_client_debug_tick(std::uintptr_t base, std::uintptr_t cli
|
||||
} else if (debug.up_velocity.valid) {
|
||||
model.up_velocity_unavailable = "Up Boost is being applied.";
|
||||
}
|
||||
if (debug.offboard_up_velocity.valid && (GetTickCount64() >= debug.offboard_up_velocity.expires ||
|
||||
debug.offboard_up_velocity.entity != model.skater_identity || debug.offboard_up_velocity.core != bodies.core ||
|
||||
(!debug.offboard_up_velocity.applied && !bodies.offboard && !bodies.wipeout))) {
|
||||
const bool applied = debug.offboard_up_velocity.applied;
|
||||
debug.offboard_up_velocity.valid = false;
|
||||
if (!applied) debug.status = "Off-board Up Boost cancelled because skater physics changed.";
|
||||
}
|
||||
if (can_control && !debug.park_editor && !debug.noclip &&
|
||||
state.velocity_guard_active.load(std::memory_order_acquire) && (bodies.offboard || bodies.wipeout)) {
|
||||
model.offboard_up_velocity_available = true;
|
||||
model.offboard_up_velocity_unavailable.clear();
|
||||
} else if (!bodies.offboard && !bodies.wipeout) {
|
||||
model.offboard_up_velocity_unavailable = "Off-board Up Boost requires walking, falling or gliding.";
|
||||
} else if (debug.offboard_up_velocity.valid) {
|
||||
model.offboard_up_velocity_unavailable = "Off-board Up Boost is being applied.";
|
||||
}
|
||||
try { dark_pop_tick(debug, client, model.skater_identity, bodies, can_control); }
|
||||
catch (...) {}
|
||||
try { probe_tick(debug, skater_transform, bodies, can_control); }
|
||||
catch (...) {}
|
||||
model.no_bail_available = can_control && update_no_bail(client, model.skater_identity, debug.no_bail && no_bail_allowed,
|
||||
debug.noclip && debug.noclip_velocity.valid, debug.noclip_velocity.expires);
|
||||
if (model.camera_available) {
|
||||
@@ -814,14 +1221,16 @@ overlay::DebugModel on_client_debug_tick(std::uintptr_t base, std::uintptr_t cli
|
||||
if (model.camera_available) model.noclip_unavailable = guard.message;
|
||||
model.forward_velocity_unavailable = guard.message;
|
||||
model.up_velocity_unavailable = guard.message;
|
||||
model.offboard_up_velocity_unavailable = guard.message;
|
||||
} catch (...) {
|
||||
if (model.camera_available) model.noclip_unavailable = "Local skater physics could not be read.";
|
||||
model.forward_velocity_unavailable = "Local skater physics could not be read.";
|
||||
model.up_velocity_unavailable = "Local skater physics could not be read.";
|
||||
model.offboard_up_velocity_unavailable = model.up_velocity_unavailable;
|
||||
}
|
||||
if (!session_boosts_allowed()) {
|
||||
model.forward_velocity_available = model.up_velocity_available = false;
|
||||
model.forward_velocity_unavailable = model.up_velocity_unavailable = "The host has turned off boosts in this session.";
|
||||
model.forward_velocity_available = model.up_velocity_available = model.offboard_up_velocity_available = false;
|
||||
model.forward_velocity_unavailable = model.up_velocity_unavailable = model.offboard_up_velocity_unavailable = "The host has turned off boosts in this session.";
|
||||
}
|
||||
if (!model.no_bail_available) {
|
||||
clear_no_bail();
|
||||
@@ -848,8 +1257,20 @@ overlay::DebugModel on_client_debug_tick(std::uintptr_t base, std::uintptr_t cli
|
||||
model.forward_velocity_updates = debug.forward_velocity_updates;
|
||||
model.up_velocity_speed = debug.up_velocity_speed;
|
||||
model.up_velocity_updates = debug.up_velocity_updates;
|
||||
model.offboard_up_velocity_speed = debug.offboard_up_velocity_speed;
|
||||
model.offboard_up_velocity_updates = debug.offboard_up_velocity_updates;
|
||||
state.offboard_boost_core.store(debug.offboard_up_velocity.valid ? debug.offboard_up_velocity.core : 0,
|
||||
std::memory_order_release);
|
||||
model.dark_pop = debug.dark_pop;
|
||||
model.dark_pop_strength = debug.dark_pop_strength;
|
||||
model.dark_pop_release = debug.dark_pop_release;
|
||||
model.dark_pop_count = debug.dark_pop_count;
|
||||
model.probe = debug.probe;
|
||||
return model;
|
||||
}
|
||||
bool client_free_camera_active() noexcept {
|
||||
return source_state().free_camera_active.load(std::memory_order_acquire);
|
||||
}
|
||||
bool restore_client_debug(std::uintptr_t base, std::uintptr_t client, bool camera_phase_observed) {
|
||||
const overlay::DebugRequest request{overlay::DebugAction::restore_debug};
|
||||
const auto model = on_client_debug_tick(base, client, false, camera_phase_observed, &request);
|
||||
|
||||
Reference in new issue
Block a user