diff --git a/Extension/Trainer/trainer.cpp b/Extension/Trainer/trainer.cpp index 1d185c07..cfdb7aa7 100644 --- a/Extension/Trainer/trainer.cpp +++ b/Extension/Trainer/trainer.cpp @@ -245,6 +245,7 @@ struct State { return a; }(); std::uint64_t tr_option_at{}; + int grind_assist{-1}; // the game's own Grind Assist (grind_assist_setting), read with the options above // What the player's own game settings hold for the settings the trainer can force (read with the options above): // a row that is not forced shows this, not the game's default. std::array grind_own = [] { @@ -2289,6 +2290,10 @@ void drive_transition(std::uintptr_t base, std::uintptr_t client, std::uint64_t if (now >= s.tr_option_at) { // The game's own options: whatever its Settings menu (or the trainer) last set. s.tr_option_at = now + 500; + if (const auto held = profile_runtime::local_game_option(grind_assist_setting); (held ? static_cast(std::lround(*held)) : -1) != s.grind_assist) { + s.grind_assist = held ? static_cast(std::lround(*held)) : -1; + s.view_due = true; + } for (std::size_t i = 0; i < transition_options.size(); ++i) { const auto held = profile_runtime::local_game_option(transition_options[i].setting); const int value = held ? static_cast(std::lround(*held)) : transition_options[i].stock; @@ -2531,6 +2536,7 @@ void build_view() { next->grind_own = s.grind_own; next->tr_own = s.tr_own; next->tr_option = s.tr_option; + next->grind_assist = s.grind_assist; next->offboard_height = s.offboard_height; next->flip_speed = s.flip_speed; next->underflip_speed = s.underflip_speed; @@ -3398,8 +3404,18 @@ std::string run(std::string_view verb, const std::vector &a) { if (v == "transition") return transition_command(a); if (v == "foot") return foot_command(a); if (v == "grind") { - // trainer grind | grind watch 0|1 | grind side | grind |all game|off|on + // trainer grind | grind watch 0|1 | grind side | grind assist <0-100> | grind |all game|off|on const auto what = lower(arg(0)), to = lower(arg(1)); + if (what == "assist") { + // The player's own game setting, as its Settings menu sets it: in any session, never part of presets or resets. + const auto value = number(to); + if (!value || !(*value >= 0 && *value <= 100)) return "error: usage: trainer grind assist <0 to 100> (the game's own Grind Assist)"; + if (!profile_runtime::set_local_game_option(grind_assist_setting, std::round(*value), false)) return "error: the game's settings are not ready yet."; + s.grind_assist = static_cast(std::lround(*value)); + s.tr_option_at = 0; // read back from the game on the next tick + s.view_due = true; + return std::format("Grind Assist (the game's own setting) is {}.", s.grind_assist); + } const auto word = [](std::int8_t value) { return value < 0 ? "-" : value ? "on" : "off"; }; if (what.empty()) { std::string out = std::format("Side force off grinds x {:.2f}.", s.grind_side); diff --git a/Extension/Trainer/trainer.h b/Extension/Trainer/trainer.h index 47941c30..4bf2f94b 100644 --- a/Extension/Trainer/trainer.h +++ b/Extension/Trainer/trainer.h @@ -137,6 +137,7 @@ struct View { bool tr_spine{}; std::array tr_setting{}; std::array tr_option{}; // the game's own options, as the game holds them now + int grind_assist{-1}; // the game's own Grind Assist as the game holds it (-1: not saved in the profile yet) // What the player's own game settings hold for the settings the trainer can force (0 off / 1 on). std::array grind_own{}; std::array tr_own{}; diff --git a/Extension/Trainer/trainer_gamestate.h b/Extension/Trainer/trainer_gamestate.h index d6fd6ba1..47b21edb 100644 --- a/Extension/Trainer/trainer_gamestate.h +++ b/Extension/Trainer/trainer_gamestate.h @@ -272,6 +272,8 @@ struct TransitionOption { int stock{}; // the game's default int low{}, high{}; }; +// The game's own Grind Assist (its Settings menu): what the trainer's Grind lock-on row shows and sets, 0 to 100. +inline constexpr std::string_view grind_assist_setting = "egf_grindassistamount"; inline constexpr std::array transition_options{{ {"pumpassist", "Pump assist", "The game's own Pump Assist: how much of a pump you get without pressing for it. The game takes the larger of your left stick and this, " "so at its default of 85 the stick only adds the last 15 percent. 0 is fully manual: no press, no pump. Skate 3 gave about 70 percent of a pump by " diff --git a/Extension/Trainer/trainer_page.cpp b/Extension/Trainer/trainer_page.cpp index b79b2cb9..64e532b7 100644 --- a/Extension/Trainer/trainer_page.cpp +++ b/Extension/Trainer/trainer_page.cpp @@ -1088,10 +1088,29 @@ void topic_body(SkateMenu &menu, const Model &model, const CallbacksV3 &callback ImGui::EndDisabled(); from_trickline({"trick-pops", "trick-spins"}); break; - case trainer::topic_grinds: + case trainer::topic_grinds: { + // Grind lock-on is the game's own Grind Assist: the same number as its Settings menu, changed from either place. Never + // MODIFIED, in no preset or reset, and the player's own in any session (the owner's ask, 2026-10-24). + ImGui::PushID("grind-assist"); + const char *help = "Your game's own Grind Assist, the same number as in its Settings menu: change it here or there. How much the game " + "helps the board onto a rail or ledge. It is your setting, so it never counts as MODIFIED and no preset or reset " + "changes it. The lock-on distance, the reach from the air and the lock-on speeds and angle are the game's tuning " + "values, in the rows of this section."; + field(menu, "Grind lock-on (GAME SETTING: Grind Assist)", help); + if (view.grind_assist < 0) { + ImGui::TextDisabled("not saved yet: set it once in the game's Settings"); + } else { + double assist = view.grind_assist; + ImGui::BeginDisabled(!callbacks.queue_console_command); + if (slider_double("##assist", &assist, 0.0, 100.0, "%.0f")) trainer_command(menu, callbacks, std::format("grind assist {:.0f}", assist)); + ImGui::EndDisabled(); + } + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) wrapped_tooltip("%s", help); + ImGui::PopID(); from_trickline({"grind-feel", "grind-stick", "grind-exits"}); dials_card(menu, callbacks, p, view, topic, can_edit); break; + } case trainer::topic_trickline: from_trickline({"trickline", "trick-revert"}); // Dark Pop is ReSkate's own switch (not a tuning value), so it is live even where the tuning is locked. diff --git a/Extension/Trainer/trainer_presets.cpp b/Extension/Trainer/trainer_presets.cpp index 01ad6d48..0ba68179 100644 --- a/Extension/Trainer/trainer_presets.cpp +++ b/Extension/Trainer/trainer_presets.cpp @@ -365,7 +365,7 @@ PresetDial preset_dial(std::string_view name) { {"Fast Flips", "Body flip speed", topic_pop}, {"Fast Spins", "Body spin speed", topic_pop}, {"Hard To Bail", "Bail resistance (higher: harder to bail)", topic_bails}, - {"Sticky Grinds", "Grind lock-on", topic_grinds}, + {"Sticky Grinds", "", topic_grinds}, // a switch: Grind lock-on is the game's own Grind Assist (the owner's ask, 2026-10-24) {"Slick Grinds", "Grind friction", topic_grinds}, {"Moon Jump", "On foot: jump height", topic_foot}, {"Fast On Foot", "On foot: sprint speed", topic_foot},