From 14fd2c97c37ae08e41a40f644fe1c6027ee36608 Mon Sep 17 00:00:00 2001 From: ComputerKWasTaken Date: Tue, 6 Oct 2026 20:40:03 -0500 Subject: [PATCH] Fix Forward Boost direction while riding Fakie --- CMakeLists.txt | 2 + .../Game/Skater/Test/velocity_boost_tests.cpp | 57 +++++++++++++++++++ Engine/Game/Skater/velocity_boost.h | 13 ++++- Extension/Skater/client_debug.cpp | 4 +- 4 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 Engine/Game/Skater/Test/velocity_boost_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9eb4ff8..ee98477 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,8 @@ if(DINGOSDK_BUILD_MULTIPLAYER_TESTS) add_test(NAME multiplayer_prediction COMMAND dingosdk_multiplayer_prediction_tests) endif() if(DINGOSDK_BUILD_MULTIPLAYER_TESTS AND WIN32) + add_executable(dingosdk_velocity_boost_tests Engine/Game/Skater/Test/velocity_boost_tests.cpp) + add_test(NAME velocity_boost COMMAND dingosdk_velocity_boost_tests) if(EXISTS "${PROJECT_SOURCE_DIR}/tests/first_person_spring_tests.cpp") add_executable(dingosdk_first_person_spring_tests tests/first_person_spring_tests.cpp) target_include_directories(dingosdk_first_person_spring_tests PRIVATE "${PROJECT_SOURCE_DIR}") diff --git a/Engine/Game/Skater/Test/velocity_boost_tests.cpp b/Engine/Game/Skater/Test/velocity_boost_tests.cpp new file mode 100644 index 0000000..f9aa04f --- /dev/null +++ b/Engine/Game/Skater/Test/velocity_boost_tests.cpp @@ -0,0 +1,57 @@ +#include "Engine/Game/Skater/velocity_boost.h" +#include +#include +#include + +namespace { +void check(bool condition, const char* message) { + if (!condition) { std::cerr << message << '\n'; std::exit(1); } +} +bool near(float a, float b) { return std::abs(a - b) < .001f; } +} + +int main() { + using dingosdk::VelocityBoostDirection; + using dingosdk::velocity_boost_delta; + constexpr auto forward = VelocityBoostDirection::forward; + constexpr auto up = VelocityBoostDirection::up; + std::array transform{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}; + // Rotate through world headings; changing preferred stance does not change + // this contract: facing-aligned travel boosts forward, fakie travel reverses it. + for (int heading = 0; heading < 360; heading += 15) { + const float angle = static_cast(heading) * 3.14159265f / 180.f; + transform[8] = std::sin(angle); + transform[10] = std::cos(angle); + for (const float speed : {1.f, 75.f, 300.f}) { + for (const float travel : {12.f, -12.f}) { + const std::array motion{travel * transform[8], 0, travel * transform[10]}; + const auto delta = velocity_boost_delta(transform, speed, forward, motion); + check(delta.has_value(), "Valid riding boost rejected"); + const float sign = travel < 0 ? -1.f : 1.f; + check(near((*delta)[0], sign * speed * transform[8]) && near((*delta)[2], sign * speed * transform[10]), + "Boost opposes riding direction"); + check(near(std::hypot((*delta)[0], (*delta)[2]), speed), "Configured boost magnitude changed"); + } + } + } + transform[8] = 0; transform[10] = 1; + for (const auto motion : {std::array{0, 0, 0}, {0, 0, -.05f}, {12, 0, 0}, {0, -50, 0}, {0, 50, 0}}) { + const auto delta = velocity_boost_delta(transform, 75, forward, motion); + check(delta && near((*delta)[2], 75), "Stationary, sideways or vertical motion reversed boost"); + } + // Preserve the original three-dimensional facing axis and magnitude on ramps. + transform[9] = .6f; transform[10] = .8f; + const auto ramp = velocity_boost_delta(transform, 75, forward, {0, -100, -12}); + check(ramp && near((*ramp)[1], -45) && near((*ramp)[2], -60), "Fakie ramp direction or magnitude changed"); + const auto rising = velocity_boost_delta(transform, 75, forward, {0, -100, 12}); + check(rising && near((*rising)[1], 45) && near((*rising)[2], 60), "Vertical motion overrode forward travel"); + const auto upward = velocity_boost_delta(transform, 25, up, {0, -100, -12}); + check(upward && *upward == std::array{0, 25, 0}, "Up Boost changed in fakie"); + for (const float speed : {0.f, 301.f, std::numeric_limits::quiet_NaN()}) + check(!velocity_boost_delta(transform, speed, forward), "Invalid forward speed accepted"); + check(!velocity_boost_delta(transform, 26, up), "Invalid up speed accepted"); + check(!velocity_boost_delta({}, 75, forward), "Degenerate facing accepted"); + check(!velocity_boost_delta(transform, 75, forward, {0, 0, std::numeric_limits::infinity()}), + "Invalid travel velocity accepted"); + std::cout << "Velocity boost regressions passed.\n"; +} diff --git a/Engine/Game/Skater/velocity_boost.h b/Engine/Game/Skater/velocity_boost.h index 7bb2b9b..00edd6d 100644 --- a/Engine/Game/Skater/velocity_boost.h +++ b/Engine/Game/Skater/velocity_boost.h @@ -7,7 +7,8 @@ namespace dingosdk { enum class VelocityBoostDirection { forward, up }; inline std::optional> velocity_boost_delta( - const std::array& transform, float speed, VelocityBoostDirection direction) noexcept { + const std::array& transform, float speed, VelocityBoostDirection direction, + const std::array& board_velocity = {}) noexcept { const float maximum = direction == VelocityBoostDirection::up ? 25.0f : 300.0f; if (!std::isfinite(speed) || speed < 1.0f || speed > maximum) return {}; if (direction == VelocityBoostDirection::up) return std::array{0, speed, 0}; @@ -17,6 +18,16 @@ inline std::optional> velocity_boost_delta( if (!std::isfinite(length) || length <= .01f) return {}; const auto scale = speed / std::sqrt(length); for (auto& component : delta) component *= scale; + // Fakie travel opposes the skater's facing axis. Use horizontal motion so + // falling/rising cannot reverse a boost, and retain facing at rest or sideways. + const float horizontal_length = std::hypot(delta[0], delta[2]); + if (horizontal_length > .01f) { + const float travel = (delta[0] * board_velocity[0] + delta[2] * board_velocity[2]) / horizontal_length; + if (!std::isfinite(travel)) return {}; + // Ignore tiny backwards drift around standstill (0.1 world units/s). + if (travel < -.1f) + for (auto& component : delta) component = -component; + } return delta; } } diff --git a/Extension/Skater/client_debug.cpp b/Extension/Skater/client_debug.cpp index db209dc..251b984 100644 --- a/Extension/Skater/client_debug.cpp +++ b/Extension/Skater/client_debug.cpp @@ -488,9 +488,11 @@ void debug_action(SourceTrial& trial, std::uintptr_t client, bool can_control, b SourceReader reader; source_require(reader.value(skater.skater_identity, 0x7e0) == 0, "Wait for the current teleport before using velocity boosts."); + const auto board_velocity = up ? std::array{} : + reader.value>(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); + 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.");