mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-17 16:17:06 +02:00
Grind exits: pop-out strength slider
A Pop-out strength setting (10-100%, kept) scales the sideways speed of trigger-set grind exits, for rails next to a wall, where the full exit carries the skater into it. 100% is the default and exactly the previous behaviour. - ReSkate menu: a slider in the GRIND EXITS card, sent when it is let go, in steps of 5. - skate.'s Mod Options and the tools menu: a row stepping 100 / 75 / 50 / 25%. - Console: grindexit strength <10-100>; kept in control.txt with the other grind exit settings. - Below 100%, an exit the game was already sending toward the chosen side is scaled too, so the setting applies to every trigger-set exit.
This commit is contained in:
10 files changed
+78
-9
No files matched your search
@@ -26,6 +26,9 @@ ReSkate menu's skater page, or the console (`~`) with `grindexit exits on|off`).
|
||||
grind, the stick's left / right reaches the game at the pop instead of before it (it cannot
|
||||
lean-balance the grind meanwhile), so the game's own lean exit cannot drag the board toward the
|
||||
spin and into the rail (`grindexit leanguard on|off`).
|
||||
- **Pop-out strength** (`grindexit strength 10-100`, kept): how far to the side a trigger exit
|
||||
pops, as a share of the full exit (100%, the default, is the game's own sideways speed). Lower it
|
||||
for rails next to a wall. Very low values leave little clearance on exits over the rail.
|
||||
- **Multiplayer**: works in any session or server, whatever the host's settings, throwdowns
|
||||
included. It adds no speed or height (the sideways speed is the game's own); it only changes which
|
||||
input picks the exit side. Server checks do not see it, since it changes no game data.
|
||||
|
||||
@@ -13,6 +13,7 @@ void register_grind_exit_commands(Commands ®istry) {
|
||||
{"exits", "Grind exit triggers: LT/RT pressed while grinding pick the side you pop out to (kept)", "on|off|status"},
|
||||
{"stance", "Grind exits: your skater's stance, for toe/heel sides (kept)", "goofy|regular"},
|
||||
{"sides", "Grind exits: LT toe / RT heel, or LT left / RT right of travel (kept)", "toeheel|travel"},
|
||||
{"strength", "Grind exits: how far to the side a trigger exit pops, percent of the full exit (kept; 100 is the game's own)", "<10-100>"},
|
||||
{"guard", "Grind exits: hide a trigger used to pick the side from the game until released (no accidental grab)", "on|off"},
|
||||
{"leanguard", "Grind exits: the left stick's left/right reaches the game at the pop while a trigger picks the side", "on|off"},
|
||||
{"prewindow", "Grind exits: seconds a trigger may be pressed before the grind connects and still count (0.4)", "<seconds>"},
|
||||
|
||||
@@ -32,6 +32,7 @@ constexpr double release_grace_seconds = 0.1; // a trigger let g
|
||||
constexpr double axes_fresh_seconds = 0.25; // the skater's facing, sampled on the client tick
|
||||
constexpr std::size_t rail_steps = 6; // grind steps the rail's direction is averaged over
|
||||
constexpr float min_rail_speed = 0.5f, min_kept_side_speed = 1.0f;
|
||||
constexpr int min_strength = 10; // pop-out strength floor, percent: below it a trigger barely picks a side
|
||||
constexpr float min_toe_across = 0.35f; // below this the toe points along the rail (slides): left/right of travel instead
|
||||
|
||||
enum class Stance : int { unset, regular, goofy };
|
||||
@@ -78,6 +79,7 @@ struct Control {
|
||||
std::atomic<std::uintptr_t> base{};
|
||||
std::atomic<bool> started{}, enabled{}, guard{true}, lean_guard{true}, hooked{};
|
||||
std::atomic<float> side_speed{gx::measured_side_speed}, prewindow{0.4f};
|
||||
std::atomic<int> strength{100}; // pop-out strength, percent of the sideways speed (10-100)
|
||||
std::atomic<int> stance{static_cast<int>(Stance::unset)}, sides{static_cast<int>(Sides::toeheel)};
|
||||
std::atomic<bool> wanted{}; // the player's on/off choice, kept; `enabled` is whether it is installed now
|
||||
// Physics thread -> the launch hook and the pad filter.
|
||||
@@ -155,6 +157,11 @@ void load_settings(Control& c) {
|
||||
else if (line == "sides=toeheel") c.sides.store(static_cast<int>(Sides::toeheel));
|
||||
else if (line == "enabled=1") c.wanted.store(true);
|
||||
else if (line == "enabled=0") c.wanted.store(false);
|
||||
else if (line.starts_with("strength=")) {
|
||||
int value{};
|
||||
const auto parsed = std::from_chars(line.data() + 9, line.data() + line.size(), value);
|
||||
if (parsed.ec == std::errc{} && value >= min_strength && value <= 100) c.strength.store(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
void save_settings(Control& c) {
|
||||
@@ -166,6 +173,7 @@ void save_settings(Control& c) {
|
||||
if (stance != Stance::unset) out << "stance=" << (stance == Stance::goofy ? "goofy" : "regular") << "\n";
|
||||
out << "sides=" << (static_cast<Sides>(c.sides.load()) == Sides::travel ? "travel" : "toeheel") << "\n";
|
||||
out << "enabled=" << (c.wanted.load() ? 1 : 0) << "\n";
|
||||
out << "strength=" << c.strength.load() << "\n";
|
||||
}
|
||||
|
||||
// ---- physics side --------------------------------------------------------------------------
|
||||
@@ -338,9 +346,13 @@ void modify_launch(std::uintptr_t caller_rva, std::uintptr_t, float (&v)[3]) noe
|
||||
if (d.trigger == 0) { d.reason = both ? Reason::both_triggers : Reason::no_trigger; record(c, d); return; }
|
||||
d.toward = toward_side(c, d.trigger, nx, nz, now, d);
|
||||
const float side = static_cast<float>(d.toward);
|
||||
if (d.before_side * side >= min_kept_side_speed) { d.reason = Reason::kept; record(c, d); return; }
|
||||
const float magnitude = std::abs(d.before_side) >= min_kept_side_speed ? std::abs(d.before_side)
|
||||
: c.side_speed.load(std::memory_order_relaxed);
|
||||
// Pop-out strength scales the sideways speed of every trigger-set exit; at 100% (the default)
|
||||
// the game's own exit toward the chosen side is kept as it is.
|
||||
const int strength = c.strength.load(std::memory_order_relaxed);
|
||||
if (strength >= 100 && d.before_side * side >= min_kept_side_speed) { d.reason = Reason::kept; record(c, d); return; }
|
||||
const float full = std::abs(d.before_side) >= min_kept_side_speed ? std::abs(d.before_side)
|
||||
: c.side_speed.load(std::memory_order_relaxed);
|
||||
const float magnitude = full * static_cast<float>(strength) / 100.0f;
|
||||
const float change = side * magnitude - d.before_side;
|
||||
v[0] += change * nx;
|
||||
v[2] += change * nz;
|
||||
@@ -361,11 +373,11 @@ std::string status_text(Control& c) {
|
||||
const auto stance = static_cast<Stance>(c.stance.load());
|
||||
const bool toeheel = static_cast<Sides>(c.sides.load()) == Sides::toeheel;
|
||||
std::string text = std::format(
|
||||
"Grind exit control: {}. Sides: {}. Stance: {}. Grab guard {}, lean guard {}, pre-press window {:.2f} s, side speed {:.2f} m/s. "
|
||||
"Pops out of grinds seen: {}, sides set: {}.",
|
||||
"Grind exit control: {}. Sides: {}. Stance: {}. Pop-out strength {}%. Grab guard {}, lean guard {}, pre-press window {:.2f} s, "
|
||||
"side speed {:.2f} m/s. Pops out of grinds seen: {}, sides set: {}.",
|
||||
on ? "ON" : "off", toeheel ? "LT toe / RT heel" : "LT left / RT right of travel",
|
||||
stance == Stance::goofy ? "goofy" : stance == Stance::regular ? "regular" : "NOT SET (type: grindexit stance goofy|regular)",
|
||||
c.guard.load() ? "on" : "off", c.lean_guard.load() ? "on" : "off", c.prewindow.load(), c.side_speed.load(), c.pops.load(),
|
||||
c.strength.load(), c.guard.load() ? "on" : "off", c.lean_guard.load() ? "on" : "off", c.prewindow.load(), c.side_speed.load(), c.pops.load(),
|
||||
c.applied.load());
|
||||
if (on && toeheel && stance == Stance::unset) text += " Until the stance is set, LT/RT are left/right of travel.";
|
||||
if (on && !c.hooked.load()) text += " The launch hook is not ready yet (it installs once the game's code is recognised).";
|
||||
@@ -398,6 +410,7 @@ bool available() noexcept { return control().started.load(std::memory_order_acqu
|
||||
bool enabled() noexcept { return control().enabled.load(std::memory_order_acquire); }
|
||||
bool toe_heel() noexcept { return static_cast<Sides>(control().sides.load()) == Sides::toeheel; }
|
||||
int stance() noexcept { return control().stance.load(); }
|
||||
int strength() noexcept { return control().strength.load(); }
|
||||
|
||||
bool start(std::uintptr_t image_base) noexcept {
|
||||
auto& c = control();
|
||||
@@ -492,6 +505,15 @@ std::string command(const std::vector<std::string>& words) {
|
||||
try { save_settings(c); } catch (...) {}
|
||||
return status_text(c);
|
||||
}
|
||||
if (verb == "strength") {
|
||||
int value{};
|
||||
const auto parsed = std::from_chars(arg.data(), arg.data() + arg.size(), value);
|
||||
if (parsed.ec != std::errc{} || parsed.ptr != arg.data() + arg.size() || value < min_strength || value > 100)
|
||||
return "Usage: grindexit strength <10 to 100> (percent of the full pop-out to the side; 100 is the game's own)";
|
||||
c.strength.store(value);
|
||||
try { save_settings(c); } catch (...) {}
|
||||
return status_text(c);
|
||||
}
|
||||
if (verb == "guard" || verb == "leanguard") {
|
||||
if (arg != "on" && arg != "off") return "Usage: grindexit " + verb + " on|off";
|
||||
(verb == "guard" ? c.guard : c.lean_guard).store(arg == "on");
|
||||
@@ -510,6 +532,6 @@ std::string command(const std::vector<std::string>& words) {
|
||||
c.side_speed.store(value);
|
||||
return status_text(c);
|
||||
}
|
||||
return "Usage: grindexit exits on|off|status, stance goofy|regular, sides toeheel|travel, guard on|off, leanguard on|off, prewindow <s>, sidespeed <m/s>";
|
||||
return "Usage: grindexit exits on|off|status, stance goofy|regular, sides toeheel|travel, strength <10-100>, guard on|off, leanguard on|off, prewindow <s>, sidespeed <m/s>";
|
||||
}
|
||||
} // namespace dingosdk::grind_exit_control
|
||||
@@ -20,6 +20,8 @@
|
||||
// sideways speed when it had one, mirrored to the chosen side, else `sidespeed` (1.8 m/s, the
|
||||
// game's own). Along the rail, the pop's height, the trick, the flip and the body's rotation
|
||||
// are the game's.
|
||||
// - Pop-out strength (10-100%, kept): scales the sideways speed of every trigger-set exit, for
|
||||
// rails next to a wall. 100% is the behaviour above, the game's own full exit.
|
||||
// - Grab guard: a claimed trigger is hidden from the game until it is let go, so holding it
|
||||
// through the pop does not start a grab; press it again in the air to grab.
|
||||
// - Lean guard: while a claimed trigger is held in a grind, the left stick's left/right is
|
||||
@@ -40,7 +42,7 @@ bool start(std::uintptr_t image_base) noexcept;
|
||||
// Client thread, every tick, with the local client: keeps the launch hook ready, samples the
|
||||
// skater's facing, writes the physics thread's decisions to the log.
|
||||
void on_client_tick(std::uintptr_t client) noexcept;
|
||||
// The console verbs: exits on|off|status, stance goofy|regular, sides toeheel|travel,
|
||||
// The console verbs: exits on|off|status, stance goofy|regular, sides toeheel|travel, strength <10-100>,
|
||||
// guard on|off, leanguard on|off, prewindow <seconds>, sidespeed <m/s>.
|
||||
std::string command(const std::vector<std::string>& words);
|
||||
|
||||
@@ -49,4 +51,5 @@ bool available() noexcept;
|
||||
bool enabled() noexcept;
|
||||
bool toe_heel() noexcept; // else left/right of travel
|
||||
int stance() noexcept; // 0 not set, 1 regular, 2 goofy
|
||||
int strength() noexcept; // pop-out strength, percent of the sideways speed (10-100; 100 the full exit)
|
||||
}
|
||||
@@ -177,6 +177,16 @@ int main() {
|
||||
"which needs no stance");
|
||||
activate(state, model, cb, "grind-exits", "");
|
||||
check(capture.command == "grindexit exits off", "and the triggers turn off");
|
||||
// Pop-out strength steps down from the full exit and wraps back to it.
|
||||
check(grind_row("grind-exit-strength").title == "Pop-out strength: 100%", "Pop-out strength shows the full exit");
|
||||
activate(state, model, cb, "grind-exit-strength", "");
|
||||
check(capture.command == "grindexit strength 75", "and steps down");
|
||||
model.grind_exit.strength = 40;
|
||||
activate(state, model, cb, "grind-exit-strength", "");
|
||||
check(capture.command == "grindexit strength 25", "a slider value steps to the next lower step");
|
||||
model.grind_exit.strength = 25;
|
||||
activate(state, model, cb, "grind-exit-strength", "");
|
||||
check(capture.command == "grindexit strength 100", "and the lowest wraps to the full exit");
|
||||
model.multiplayer.local_name = "steam_name";
|
||||
model.player_card = {true, "Card Name", {}};
|
||||
const auto card_page = render(state, model, cb, player_section);
|
||||
|
||||
@@ -219,6 +219,11 @@ inline void activate(State& s, const overlay::Model& m, const overlay::Callbacks
|
||||
} else if (command == "grind-exit-stance") {
|
||||
// Not set or regular -> goofy -> regular.
|
||||
console(m.grind_exit.stance == 2 ? "grindexit stance regular" : "grindexit stance goofy", m.grind_exit.available);
|
||||
} else if (command == "grind-exit-strength") {
|
||||
// 100 -> 75 -> 50 -> 25 -> 100 (the ReSkate menu's slider sets any value from 10 to 100).
|
||||
const int strength = m.grind_exit.strength;
|
||||
const int next = strength > 75 ? 75 : strength > 50 ? 50 : strength > 25 ? 25 : 100;
|
||||
console("grindexit strength " + std::to_string(next), m.grind_exit.available);
|
||||
} else if (command == "board-wear-reset") {
|
||||
console("boardwear reset", m.offline.board_wear.available && m.offline.board_wear.effective);
|
||||
} else if (command == "challenges") {
|
||||
@@ -331,6 +336,8 @@ inline Page render(State& s, const overlay::Model& m, const overlay::CallbacksV3
|
||||
button(p.main, "grind-exit-stance",
|
||||
std::string("Your stance: ") + (grind.stance == 2 ? "Goofy" : grind.stance == 1 ? "Regular" : "Not set"),
|
||||
"grind-exit-stance", grind_console);
|
||||
button(p.main, "grind-exit-strength", "Pop-out strength: " + std::to_string(grind.strength) + "%", "grind-exit-strength",
|
||||
grind_console);
|
||||
toggle(p.main, "board-wear", "Board wear", m.offline.board_wear.effective,
|
||||
m.offline.board_wear.available && cb.queue_offline_feature);
|
||||
button(p.main, "board-wear-reset", "Reset board wear", "board-wear-reset",
|
||||
|
||||
@@ -1175,6 +1175,7 @@ void tools_page(Ui &ui, ImVec2 a, ImVec2 z) {
|
||||
if (grind.toe_heel &&
|
||||
list.setting("Your stance", grind.stance == 2 ? "Goofy" : grind.stance == 1 ? "Regular" : "Not set", grind.available && console))
|
||||
act("grind-exit-stance");
|
||||
if (list.setting("Pop-out strength", std::to_string(grind.strength) + "%", grind.available && console)) act("grind-exit-strength");
|
||||
const auto &wear = m.offline.board_wear;
|
||||
if (list.setting("Board wear", on_off(wear.effective), wear.available && cb.queue_offline_feature)) act("board-wear");
|
||||
if (list.setting("Reset board wear", {}, wear.available && wear.effective && console)) act("board-wear-reset");
|
||||
|
||||
@@ -239,6 +239,7 @@ struct BoardBreakModel {
|
||||
struct GrindExitModel {
|
||||
bool available = false, enabled = false, toe_heel = true;
|
||||
int stance = 0;
|
||||
int strength = 100; // pop-out strength, percent (10-100)
|
||||
};
|
||||
|
||||
struct Model {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "skate_menu_internal.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
@@ -207,6 +208,25 @@ void movement_controls(SkateMenu& menu, const Model& model, const CallbacksV3& c
|
||||
send_console(menu, callbacks, grind_stance == 1 ? "grindexit stance goofy" : "grindexit stance regular");
|
||||
if (grind.stance == 0) warn("Pick your stance. Until then LT / RT are left / right of travel.");
|
||||
}
|
||||
{
|
||||
// Sent once the slider is let go (not every frame of a drag); the dragged value shows
|
||||
// until the setting comes back, in steps of 5.
|
||||
static int edited = -1;
|
||||
static double edited_at = 0;
|
||||
int strength = edited >= 0 ? edited : grind.strength;
|
||||
field(menu, "Pop-out strength", "How far to the side a trigger exit pops. 100% is the full exit; lower it for rails next to a wall.");
|
||||
ImGui::BeginDisabled(!grind_console);
|
||||
if (ImGui::SliderInt("##grind-strength", &strength, 10, 100, "%d%%", ImGuiSliderFlags_AlwaysClamp)) {
|
||||
edited = std::clamp((strength + 2) / 5 * 5, 10, 100);
|
||||
edited_at = ImGui::GetTime();
|
||||
}
|
||||
if (ImGui::IsItemDeactivatedAfterEdit() && edited >= 0) {
|
||||
send_console(menu, callbacks, "grindexit strength " + std::to_string(edited));
|
||||
edited_at = ImGui::GetTime();
|
||||
}
|
||||
if (edited >= 0 && !ImGui::IsItemActive() && (grind.strength == edited || ImGui::GetTime() - edited_at > 2.0)) edited = -1;
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
end_card();
|
||||
|
||||
begin_card(menu, "boosts", "BOOSTS", "Buttons are set in Settings > Controls");
|
||||
|
||||
@@ -962,7 +962,8 @@ void update_model(std::uintptr_t client, TickState& frame) {
|
||||
model.board_break = {dingosdk::board_break::available(), dingosdk::board_break::enabled(), !breakable_deck.empty(),
|
||||
dingosdk::board_break::deck_label(breakable_deck)};
|
||||
model.grind_exit = {dingosdk::grind_exit_control::available(), dingosdk::grind_exit_control::enabled(),
|
||||
dingosdk::grind_exit_control::toe_heel(), dingosdk::grind_exit_control::stance()};
|
||||
dingosdk::grind_exit_control::toe_heel(), dingosdk::grind_exit_control::stance(),
|
||||
dingosdk::grind_exit_control::strength()};
|
||||
model.bindings = dingosdk::local_profile_controller_bindings();
|
||||
model.parks = dingosdk::local_profile_parks();
|
||||
model.world = dingosdk::local_profile_world_layers();
|
||||
|
||||
Reference in new issue
Block a user