From feb0a97c170ab937ed78cfd15fcbbeda5c06f035 Mon Sep 17 00:00:00 2001 From: andrewnakas Date: Sat, 24 Oct 2026 15:33:48 -0600 Subject: [PATCH] Trainer: Grind lock-on moves only the lock-on distance; the rest are sliders below it Since 0.8.4 the Grind lock-on dial (Sticky Grinds) also doubled the reach from the air and the weight of an edge in the landing choice, so with it turned up every pop near a rail or ledge was steered onto it and slowed the skater (reported on ReSkate 2.1.2). The dial now moves physicsmode.grindlockdist only. The air reach, lock-on angle and speeds and the edge weight are their own sliders in GRINDS, and air reach a turned-up dial saved goes back to stock once. Co-Authored-By: Claude Opus 5.5 (1M context) --- Extension/Trainer/trainer.cpp | 21 +++++++++++++++++++++ Extension/Trainer/trainer_presets.cpp | 23 ++++++++++++----------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Extension/Trainer/trainer.cpp b/Extension/Trainer/trainer.cpp index 9e69de57..5153e689 100644 --- a/Extension/Trainer/trainer.cpp +++ b/Extension/Trainer/trainer.cpp @@ -164,6 +164,7 @@ struct State { bool flip_pop{}; // a slowed flip trick pops higher, so it has the air time it needs bool flip_flick{}; // a slow flick of the stick turns the board as fast as a full one bool flip_limit{true}; // the game's flip landing limit: a flip still turning near the landing is hurried round + bool air_reach_fixed{}; // the turned-up reach from the air that Sticky Grinds saved before 2026-10-24 is cleared (once) bool grind_watch{}; // the grind card is asking what the game's switches read (`trainer grind watch`) std::map> folds; // the menu's sections and cards the player has opened or closed std::array flip_trick = [] { @@ -473,6 +474,7 @@ void load_store() { s.flip_pop = o.value("flip_pop", false); s.flip_flick = o.value("flip_flick", false); s.flip_limit = o.value("flip_landing_limit", true); + s.air_reach_fixed = o.value("air_reach_fixed", false); for (std::size_t i = 0; i < flip_tricks.size(); ++i) s.flip_trick[i] = std::clamp(o.value(std::string(flip_tricks[i].key), 1.0f), flip_low, flip_high); s.boneless_height = std::clamp(o.value("boneless_height", 1.0f), height_low, height_high); @@ -576,6 +578,7 @@ void save_store() { options["flip_pop"] = s.flip_pop; options["flip_flick"] = s.flip_flick; options["flip_landing_limit"] = s.flip_limit; + options["air_reach_fixed"] = s.air_reach_fixed; for (std::size_t i = 0; i < flip_tricks.size(); ++i) if (s.flip_trick[i] != 1.0f) options[std::string(flip_tricks[i].key)] = s.flip_trick[i]; options["boneless_height"] = s.boneless_height; @@ -917,6 +920,24 @@ void adopt(const tuning::Values &live) { } if (e.touched) apply_entry(e); } + // Grind lock-on turned up (0.8.4 to 0.8.6) doubled the reach from the air too, which steered pops near rails onto them. + // The dial moves only the lock-on distance now; the reach it saved turned up goes back to the game's own, once. + if (!s.air_reach_fixed) { + std::size_t cleared{}; + constexpr std::string_view reach[]{"physicstrajectory.scoregrind", "physicsgrindsair.maxdist50_50", "physicsgrindsair.maxdist5_0", + "physicsgrindsair.maxdisttipslide", "physicsgrindsair.maxdistboardslide"}; + for (const auto key : reach) { + auto *e = find_entry(key); + if (!e || e->frozen || !(e->value > e->stock)) continue; + for (auto &[name, preset] : s.user) + if (const auto held = preset.find(e->key); held != preset.end() && held->second > e->stock) preset.erase(held); + set_entry(*e, e->stock); + ++cleared; + } + s.air_reach_fixed = true; + s.save_due = true; + if (cleared) say(logging::Level::info, std::format("Trainer: {} turned-up grind reach values from the air are the game's own again.", cleared)); + } // A linked value saved by itself (before links existed, or edited in the file) still // drives its graphs. for (auto &e : s.entries) { diff --git a/Extension/Trainer/trainer_presets.cpp b/Extension/Trainer/trainer_presets.cpp index dad0761b..01ad6d48 100644 --- a/Extension/Trainer/trainer_presets.cpp +++ b/Extension/Trainer/trainer_presets.cpp @@ -106,6 +106,12 @@ std::string_view essential_name(std::string_view key, int *rank, std::uint8_t *t {"physicsmode.grindlockdist", "Grind lock-on distance", grinds}, {"physicsgrindsair.maxdistboardslide", "Grind lock-on: reach for board slides (m)", grinds}, {"physicsgrindsair.maxdisttipslide", "Grind lock-on: reach for nose and tail slides (m)", grinds}, + {"physicsgrindsair.maxdist50_50", "Grind lock-on: reach for 50-50s (m)", grinds}, + {"physicsgrindsair.maxdist5_0", "Grind lock-on: reach for 5-0s and nose grinds (m)", grinds}, + {"physicstrajectory.grindlandingmaxangle", "Grind lock-on: widest angle onto the edge (deg)", grinds}, + {"physicstrajectory.grindmaxspeedsqr", "Grind lock-on: fastest it still locks on (speed squared)", grinds}, + {"physicstrajectory.grindmaxspeeddownontogrind", "Grind lock-on: fastest drop onto the edge (m/s)", grinds}, + {"physicstrajectory.scoregrind", "Landing choice: weight of an edge to grind", grinds}, {"physicsgrind.grind_tipslide_minangletoprimitive", "Nose and tail slides: least angle to the rail (deg)", grinds}, {"physicsgrind.slowgrindexitspeed", "Grinds end below this speed (m/s; -1 never)", grinds, true}, {"physicsgrind.copingexitstarttime", "Coping grinds may end after (s; -1 never)", transition, true}, @@ -175,7 +181,6 @@ std::string_view essential_name(std::string_view key, int *rank, std::uint8_t *t {"physicstrajectory.naturalairoffvertsscalar", "Rolling off a vert lip without a pop: speed kept out from the wall", transition}, {"physicstrajectory.scoretransition", "Landing choice: weight of coming back into the transition", transition}, {"physicstrajectory.mintimeafterapexfortransition", "Landing choice: soonest after the top of the air (s)", transition}, - {"physicstrajectory.scoregrind", "Landing choice: weight of an edge to grind", grinds}, {"physicstrajectory.scoremiddlebonus", "Landing choice: bonus for the middle of the fan", transition}, {"physicstrajectory.scorelandingdirection", "Landing choice: weight of facing the way you travel", transition}, {"physicstrajectory.maxtrajectoryadjust", "In the air: most speed added to reach the landing (m/s)", transition}, @@ -183,9 +188,6 @@ std::string_view essential_name(std::string_view key, int *rank, std::uint8_t *t {"physicstrajectory.coneanglez", "Landing search: fan width side to side (deg)", transition}, {"physicstrajectory.mintimefortrajectorytobevalid", "Shortest air that gets a planned landing (s)", transition}, {"physicsairstates.naturalairmaxdist", "Rolling off an edge: longest gap still ridden over (m)", transition}, - {"physicsgrindsair.maxdist50_50", "Grind lock-on: reach for 50-50s (m)", grinds}, - {"physicsgrindsair.maxdist5_0", "Grind lock-on: reach for 5-0s and nose grinds (m)", grinds}, - {"physicstrajectory.grindlandingmaxangle", "Grind lock-on: widest angle onto the edge (deg)", grinds}, {"physicspumping.pumpoutoftransitionscalar", "Pumping: push out of the top of a transition", pumping}, {"physicsantipumping.maxdownwardangledeg", "Anti-pump: steepest downward push that costs speed (deg)", pumping}, }; @@ -227,6 +229,8 @@ std::string_view essential_help(std::string_view key) { {"physicsmode.grindlockdist", "How far from a rail or ledge the board still snaps onto it, in metres. Skate 3: 0.9 normal, 0.15 hardcore."}, {"physicsgrindsair.maxdistboardslide", "How far away a board slide still locks on, in metres. Skate 3 used 0.75: you had to be closer."}, {"physicsgrindsair.maxdisttipslide", "How far away a nose or tail slide still locks on, in metres. Skate 3 used 0.7."}, + {"physicstrajectory.grindmaxspeedsqr", "The fastest you can be going and still lock into a grind, as the speed times itself (m/s squared). Higher catches grinds at higher speeds."}, + {"physicstrajectory.grindmaxspeeddownontogrind", "The fastest you can be coming down onto a rail or ledge and still lock into a grind, in m/s."}, {"physicsgrind.grind_tipslide_minangletoprimitive", "How far the board must be turned to the rail before a grind is a nose or tail slide. The older value was 25."}, {"physicsgrind.slowgrindexitspeed", "A grind slower than this ends by itself. -1 (skate.) never does; the older value was 0.15 m/s. Not confirmed to do anything yet."}, {"physicsgrind.copingexitstarttime", "Seconds before the game may end a grind on coping. -1 (skate.) never; the older value was 0.1. Not confirmed to do anything yet."}, @@ -492,13 +496,10 @@ const std::vector &builtin_presets() { {"physicswipeout.wipeout_ maxspeed", true, 2.5}, {"physicswipeout.wipeout_ maxdisp", true, 3.0}, {"physicswipeout.wipeout_ relativevel", true, 3.0}, {"wipeout.on forcelimits", true, 3.0}, {"wipeout.runoutwipeoutforcelimits", true, 3.0}, {"physicsmode.wipeout_ acceleration", true, 3.0}, {"physicsmode.wipeoutcheckforbadlanding", false, 0.0}}}, - {"Sticky Grinds", "Grinds lock on from further away and at higher speeds.", - {{"physicsmode.grindlockdist", true, 2.0}, {"physicstrajectory.grindmaxspeedsqr", true, 4.0}, - {"physicstrajectory.grindmaxspeeddownontogrind", true, 2.0}, {"physicstrajectory.grindlandingmaxangle", true, 2.0}, - // The reach from the air: without these a dial at 0 still pulls the board onto an edge in flight. - {"physicsgrindsair.maxdist50_50", true, 2.0}, {"physicsgrindsair.maxdist5_0", true, 2.0}, - {"physicsgrindsair.maxdisttipslide", true, 2.0}, {"physicsgrindsair.maxdistboardslide", true, 2.0}, - {"physicstrajectory.scoregrind", true, 2.0}}}, + // One value only (the owner's ask, 2026-10-24): the reach from the air, the lock-on speeds and angle and the weight of + // an edge are sliders of their own below it. Moved with it (0.8.4 to 0.8.6), a turned-up dial steered every pop near a + // rail or ledge onto it and slowed the skater. + {"Sticky Grinds", "Grinds lock on from further away.", {{"physicsmode.grindlockdist", true, 2.0}}}, {"Slick Grinds", "Grinds and slides keep their speed.", {{"physicsgrind.commonfrictionscalar", true, 0.4}, {"physicsgrind.curbfrictionscalar", true, 0.4}}}, {"Smooth Surfaces", "Rough ground rides like polished concrete.", {{"physicsmode.makesurfacessmooth", false, 1.0}}},