diff --git a/Extension/Trainer/CHANGELOG.md b/Extension/Trainer/CHANGELOG.md index d00b1b0d..7ef9b9f9 100644 --- a/Extension/Trainer/CHANGELOG.md +++ b/Extension/Trainer/CHANGELOG.md @@ -29,7 +29,9 @@ can be tried: tell us what it does. numbers, so a slowed flip (and every value of the game's tuning classes: push speeds, on-foot values) quietly stopped applying. The trainer now checks once a second, writes back what the game undid and looks for the new objects. The slider also scales the three flick-influence - curves it used to leave alone, which is why x0.4 only slowed a flip by a third. + curves it used to leave alone, which is why x0.4 only slowed a flip by a third. And below 1 it + stretches the flip catch times by the same factor: the game hurries any flip that would not + finish inside its catch time, so slowed flips used to snap back to full speed. - No comply and boneless heights now also apply to weak upward launches (riding downhill). - The menu tab is called PHYSICS. diff --git a/Extension/Trainer/trainer.cpp b/Extension/Trainer/trainer.cpp index 1888eca7..1da8ebcd 100644 --- a/Extension/Trainer/trainer.cpp +++ b/Extension/Trainer/trainer.cpp @@ -139,6 +139,7 @@ struct State { int class_search_tries{}; std::uint64_t class_search_seen{}; // the searches that had finished when this level loaded bool class_list_wanted{}; // the player has the list of every value open on this level + float flip_applied{1}; // the flip speed the catch times were last set for std::uint64_t class_entity{}; // the skater the last search was for std::uint64_t class_research_after{}; // no new search before this, however many skaters come and go std::vector class_entries; // the entries that are fields of those classes @@ -490,12 +491,21 @@ double sane(const Entry &e, double value) { if (e.kind == Kind::curve || e.kind == Kind::graph) return std::clamp(value, 0.0, 1.0e6); return std::clamp(value, -1.0e6, 1.0e6); } +// The held flip class's catch times: how long each kind of flip may take to come round. +bool slowed_catch_time(std::string_view key) { + return key.starts_with("heldflip.") && key.ends_with("catchtime"); +} // What a class field should hold: the player's value, or the host's while a session's host // sets everyone's physics. void want_class(const Entry &e) { auto &s = state(); const auto field = static_cast(e.class_field); - want_class_value(field, s.enforced ? s.host_classes[field] : static_cast(e.touched ? e.value : e.stock)); + auto value = s.enforced ? s.host_classes[field] : static_cast(e.touched ? e.value : e.stock); + // The game hurries any flip that would not come round inside its catch time (measured: at + // every flip speed below about x0.9 a kickflip turned at 360 degrees per 0.2 s, the stock + // catch time). A flip slowed by the trick slider is given that much longer. + if (s.boosts.flip < 1.0f && slowed_catch_time(e.key)) value /= std::max(s.boosts.flip, 0.05f); + want_class_value(field, value); } void apply_entry(Entry &e) { auto &s = state(); @@ -1688,6 +1698,12 @@ void tick(std::uintptr_t base, std::uintptr_t client, bool playing, const std::s } } want_flip_speed(s.boosts.flip); + if (s.boosts.flip != s.flip_applied) { + // The catch times follow the flip speed (want_class). + s.flip_applied = s.boosts.flip; + for (const auto &e : s.entries) + if (e.class_field >= 0 && slowed_catch_time(e.key)) want_class(e); + } (void)apply_classes(); // The game builds its tuning objects again with a new skater (a respawn, a teleport, a // session change) and they start at its own numbers: look again, but not in a flurry.