mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-23 13:05:54 +02:00
Trainer 0.4.2: flip trick speed scales only the stick-speed curve (the pop keeps its height); the slow-flip switch says when a session keeps it off
This commit is contained in:
1 parent
097d306a15
commit
6fcd427bf6
6 files changed
+55
-18
No files matched your search
@@ -10,6 +10,15 @@
|
||||
AutoRevertBoost. Settings saved by 0.4.1 with the old 12 are moved to off. Turn the rule on
|
||||
yourself and a landing that counts by it alone gets the auto revert boost at most. Found and
|
||||
measured by AutoRevertBoost's authors (Sivaes, jaq and OVM).
|
||||
- **Flip trick speed no longer lowers your ollie.** The slider used to scale seven of the game's
|
||||
flip curves and shrink the range each one is read over; three of those are what a flick adds
|
||||
to the height of the pop, so a slow flip came with a low ollie. It now scales only the one
|
||||
curve that turns the stick's speed into the flip's speed. Measured on flat ground at x 0.3:
|
||||
kickflips at about half the game's turn rate, plain ollies the same height and air time as
|
||||
with the slider untouched.
|
||||
- **Let slow flips stay slow says when it is off.** In a session whose host enforces physics or
|
||||
has turned trick settings off, the switch cannot act (the likely reason it seemed to work only on
|
||||
the main map); a line under it now says so.
|
||||
|
||||
## v0.4.1 - 2026-10-06
|
||||
|
||||
|
||||
@@ -1541,6 +1541,7 @@ void build_view() {
|
||||
next->flip_speed = s.flip_speed;
|
||||
next->flip_gate = s.flip_gate;
|
||||
next->flip_gate_found = flip_gates() != 0;
|
||||
next->flip_gate_blocked = !s.flip_gate && (s.enforced || (multiplayer_session_active() && !session_boosts_allowed()));
|
||||
next->boneless_height = s.boneless_height;
|
||||
next->revert_boost = s.revert_boost;
|
||||
next->revert = s.revert;
|
||||
@@ -1758,6 +1759,7 @@ void tick(std::uintptr_t base, std::uintptr_t client, bool playing, const std::s
|
||||
find_classes();
|
||||
}
|
||||
}
|
||||
flip_speed_in_use(s.boosts.flip != 1.0f);
|
||||
want_flip_speed(s.boosts.flip);
|
||||
// The player's own choice, and only where their own trick settings count: not under a
|
||||
// host's enforced physics or in a session that has turned boosts off.
|
||||
|
||||
@@ -92,6 +92,7 @@ struct View {
|
||||
float flip_speed{1}; // board flip tricks: x of the game's own speed
|
||||
bool flip_gate{true}; // the game brings a flip round before the landing; false: slow flips stay slow
|
||||
bool flip_gate_found{}; // the rule's number was found in memory (the switch can act)
|
||||
bool flip_gate_blocked{}; // ticked, but this session's rules keep the game's own flips
|
||||
// HUD
|
||||
bool hud{}, hud_jump{}, logging{};
|
||||
// The loaded map and what its author ships for the trainer (Mods/<mod>/trainer.json).
|
||||
|
||||
@@ -65,7 +65,7 @@ int mirror_field(const MirrorSlot &slot) noexcept {
|
||||
struct FlipCurve {
|
||||
std::uintptr_t curve{}, points{};
|
||||
std::vector<float> outputs;
|
||||
float low{}, high{}; // the bounds the curve clamps its output to (+0x20, +0x24)
|
||||
float low{}, high{}; // the input range the curve is read over (MinX, MaxX at +0x20, +0x24): never written
|
||||
};
|
||||
constexpr std::size_t curve_point = 0x1c, curve_x = 0xc, curve_y = 0x14, curve_points_at = 0x18;
|
||||
// The point that tells what a curve holds: its largest output (some curves start at 0).
|
||||
@@ -81,6 +81,7 @@ struct Found {
|
||||
std::mutex mutex;
|
||||
std::vector<FlipCurve> flip;
|
||||
float flip_wanted{1}, flip_written{1};
|
||||
bool flip_used{}; // a flip speed other than the game's is set, though not written at this moment
|
||||
std::vector<std::uintptr_t> gates; // where the flick warp limit's number lives
|
||||
bool gate_kept{true}; // false: the trainer holds it off
|
||||
Copies copies;
|
||||
@@ -232,6 +233,14 @@ void collect_eights(std::uintptr_t start, std::size_t size, const std::vector<st
|
||||
}
|
||||
} __except (EXCEPTION_EXECUTE_HANDLER) {}
|
||||
}
|
||||
// The curves the flip speed slider scales, a bit per curve in memory order: only curve 4,
|
||||
// GestureSpeedToFlipSpeed, the stick's speed to the speed of the flip. Measured with the owner on
|
||||
// flat ground, 2026-10-06 (outputs x0.3, nothing else touched): kickflips at about half the
|
||||
// game's turn rate, plain ollies 0.39 to 0.40 m with 0.62 s of air, the same as untouched.
|
||||
// 0.4.0 to 0.4.2 also scaled curves 3, 6 and 7 (what a flick adds to the HEIGHT of the pop) and
|
||||
// every curve's input range at +0x20 / +0x24 (MinX and MaxX, not output bounds as was thought):
|
||||
// that made a slow flip a low ollie. Curves 0 and 1 changed neither the flip nor the pop.
|
||||
std::atomic<std::uint32_t> flip_curve_mask{0x10};
|
||||
// One curve's points: false unless it has `count` points starting at (x0, y0).
|
||||
bool read_curve(std::uintptr_t curve, std::uint32_t count, float x0, float y0, FlipCurve &out) noexcept {
|
||||
std::uintptr_t points{};
|
||||
@@ -265,18 +274,24 @@ std::vector<FlipCurve> find_flip_curves(std::uintptr_t curve_type) {
|
||||
for (const auto run : runs) {
|
||||
std::array<std::uintptr_t, 8> curves{};
|
||||
if (!peek(run, curves)) continue;
|
||||
// In memory order: 0 the flip speed against the flick, 1 the same for the legacy controls,
|
||||
// 2 a multiplier for a high flick (1 to 2: left alone), 3, 6 and 7 the speed a flick's height
|
||||
// and the stick sensitivity add (0 to 2.75), 4 flick speed to flip speed, 5 ollie speed to
|
||||
// flip speed. Scaling only 0, 1, 4 and 5 slowed a flip by a third at x0.4: the rest is 3, 6, 7.
|
||||
// In memory order: 0 and 1 a speed against the flick (new controls and legacy), 2 a
|
||||
// multiplier for a high flick (1 to 2), 3, 6 and 7 what a flick adds to the height of the
|
||||
// pop (0 to 2.75 over the stick's speed), 4 the stick's speed to the flip speed, 5 the
|
||||
// ollie's speed to the flip speed (flat 1). All seven are checked to know the run is the
|
||||
// flip trick tuning; flip_curve_mask says which are scaled.
|
||||
std::array<FlipCurve, 7> speed;
|
||||
if (!read_curve(curves[0], 3, 0.0f, 0.4f, speed[0]) || !read_curve(curves[1], 3, 0.35f, 0.65f, speed[1]) ||
|
||||
!read_curve(curves[4], 3, 0.0f, 0.05f, speed[2]) || !read_curve(curves[5], 2, 0.0f, 1.0f, speed[3]) ||
|
||||
!read_curve(curves[3], 4, 0.0f, 0.0f, speed[4]) || !read_curve(curves[6], 4, 0.0f, 0.0f, speed[5]) ||
|
||||
!read_curve(curves[7], 4, 0.0f, 0.0f, speed[6]))
|
||||
continue;
|
||||
for (auto &curve : speed)
|
||||
if (std::ranges::none_of(result, [&](const FlipCurve &known) { return known.points == curve.points; })) result.push_back(std::move(curve));
|
||||
// Which of them the flip speed scales: a bit per curve, in memory order (see flip_curve_mask).
|
||||
static constexpr std::size_t memory_index[]{0, 1, 4, 5, 3, 6, 7};
|
||||
const auto mask = flip_curve_mask.load(std::memory_order_relaxed);
|
||||
for (std::size_t i = 0; i < speed.size(); ++i)
|
||||
if (auto &curve = speed[i]; ((mask >> memory_index[i]) & 1u) &&
|
||||
std::ranges::none_of(result, [&](const FlipCurve &known) { return known.points == curve.points; }))
|
||||
result.push_back(std::move(curve));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -414,8 +429,6 @@ DWORD WINAPI search(void *) noexcept {
|
||||
if (std::ranges::any_of(f.flip, [&](const FlipCurve &known) { return known.points == curve.points; })) continue;
|
||||
if (f.flip_written != 1) {
|
||||
for (std::size_t i = 0; i < curve.outputs.size(); ++i) (void)put(curve.points + i * curve_point + curve_y, curve.outputs[i] * f.flip_written);
|
||||
(void)put(curve.curve + 0x20, curve.low > 0 ? curve.low * f.flip_written : curve.low);
|
||||
(void)put(curve.curve + 0x24, curve.high > 0 ? curve.high * f.flip_written : curve.high);
|
||||
}
|
||||
f.flip.push_back(std::move(curve));
|
||||
}
|
||||
@@ -458,9 +471,6 @@ std::size_t write_gates(Found &f) noexcept {
|
||||
}
|
||||
void write_flip_curve(const FlipCurve &curve, float factor) noexcept {
|
||||
for (std::size_t i = 0; i < curve.outputs.size(); ++i) (void)put(curve.points + i * curve_point + curve_y, curve.outputs[i] * factor);
|
||||
// The bounds move with the outputs, or the curve clamps the scaled values back.
|
||||
(void)put(curve.curve + 0x20, curve.low > 0 ? curve.low * factor : curve.low);
|
||||
(void)put(curve.curve + 0x24, curve.high > 0 ? curve.high * factor : curve.high);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -475,6 +485,15 @@ bool refresh_classes(std::uint64_t now) noexcept {
|
||||
std::size_t repaired{};
|
||||
repaired += write_gates(f);
|
||||
lost = lost || (!f.gate_kept && f.gates.empty());
|
||||
// On the ground the curves hold the game's own (the flip speed is written in the air only):
|
||||
// one that holds something else is someone else's memory now.
|
||||
if (f.flip_used && f.flip_written == 1) {
|
||||
std::erase_if(f.flip, [&](const FlipCurve &curve) {
|
||||
float y{};
|
||||
return curve.outputs.empty() || !peek(probe_address(curve), y) || !same(y, probe_output(curve));
|
||||
});
|
||||
lost = lost || f.flip.empty();
|
||||
}
|
||||
if (f.flip_written != 1) {
|
||||
std::erase_if(f.flip, [&](const FlipCurve &curve) {
|
||||
float y{};
|
||||
@@ -567,7 +586,7 @@ void want_class_value(std::size_t field, float value) noexcept {
|
||||
bool classes_wanted() noexcept {
|
||||
auto &f = found();
|
||||
std::lock_guard lock(f.mutex);
|
||||
if (f.flip_wanted != 1 || !f.gate_kept) return true;
|
||||
if (f.flip_wanted != 1 || f.flip_used || !f.gate_kept) return true;
|
||||
for (std::size_t i = 0; i < class_field_count; ++i)
|
||||
if (f.wanted[i] != class_fields[i].stock) return true;
|
||||
return false;
|
||||
@@ -587,9 +606,6 @@ std::size_t apply_classes() noexcept {
|
||||
for (const auto &curve : f.flip) {
|
||||
for (std::size_t i = 0; i < curve.outputs.size(); ++i)
|
||||
if (put(curve.points + i * curve_point + curve_y, curve.outputs[i] * f.flip_wanted)) ++count;
|
||||
// The bounds move with the outputs, or the curve clamps the scaled values back.
|
||||
(void)put(curve.curve + 0x20, curve.low > 0 ? curve.low * f.flip_wanted : curve.low);
|
||||
(void)put(curve.curve + 0x24, curve.high > 0 ? curve.high * f.flip_wanted : curve.high);
|
||||
}
|
||||
f.flip_written = f.flip_wanted;
|
||||
}
|
||||
@@ -631,6 +647,11 @@ void want_flip_speed(float factor) noexcept {
|
||||
f.dirty = true;
|
||||
}
|
||||
}
|
||||
void flip_speed_in_use(bool used) noexcept {
|
||||
auto &f = found();
|
||||
std::lock_guard lock(f.mutex);
|
||||
f.flip_used = used;
|
||||
}
|
||||
void want_flip_gate(bool kept) noexcept {
|
||||
auto &f = found();
|
||||
std::lock_guard lock(f.mutex);
|
||||
|
||||
@@ -51,6 +51,9 @@ std::string classes_summary();
|
||||
// (speed against the flick, against the ollie...); the search finds it as eight curve references
|
||||
// in a row and apply_classes multiplies the outputs of its four speed curves by this.
|
||||
void want_flip_speed(float factor) noexcept;
|
||||
// The player has a flip speed of their own set, whether or not it is written right now (it is
|
||||
// written only while the board is in the air): the curves must be found either way.
|
||||
void flip_speed_in_use(bool used) noexcept;
|
||||
// How many flip trick speed curves were found (0: the multiplier does nothing yet).
|
||||
std::size_t flip_curves() noexcept;
|
||||
// The game hurries a flip trick so that it is round 1/6 s before the landing it predicts, however
|
||||
|
||||
@@ -474,7 +474,7 @@ void trick_heights(SkateMenu &menu, const CallbacksV3 &callbacks, Page &p, const
|
||||
return true;
|
||||
};
|
||||
slider("Flip trick speed", "flip_speed", view.flip_speed, p.flip_edit, p.flip_editing,
|
||||
"How fast the board turns in a flip trick. Below 1 a flip is slower and gets that much longer to come round. On its own the game still "
|
||||
"How fast the board turns in a flip trick. It does not change how high you pop. Below 1 a flip is slower. On its own the game still "
|
||||
"hurries a slow flip so it is finished before you land: tick the switch below to stop that. Above 1 the game's own limit on how fast a "
|
||||
"board turns takes over.",
|
||||
3.0f);
|
||||
@@ -485,7 +485,8 @@ void trick_heights(SkateMenu &menu, const CallbacksV3 &callbacks, Page &p, const
|
||||
wrapped_tooltip("%s", "The game speeds a flip up so the board is round 1/6 of a second before the landing it predicts, however slow the flip speed "
|
||||
"is set. That is why a slowed flip still comes round on a small pop. Ticked: that rule is off. The board turns as "
|
||||
"slowly as you set it and lands however far round it got, so a flip you start too late or too low is a bail.");
|
||||
if (slow && !view.flip_gate_found) ImGui::TextDisabled("Not found in the game yet: it takes effect a few seconds after a level loads.");
|
||||
if (view.flip_gate_blocked) ImGui::TextDisabled("Off in this session: the host's physics rules apply here, so flips finish before landing as usual.");
|
||||
else if (slow && !view.flip_gate_found) ImGui::TextDisabled("Not found in the game yet: it takes effect a few seconds after a level loads.");
|
||||
}
|
||||
slider("No comply height", "nocomply_height", view.nocomply_height, p.nocomply_edit, p.nocomply_editing, "How high a no comply pops. 1 is the game's own.");
|
||||
slider("Boneless height", "boneless_height", view.boneless_height, p.boneless_edit, p.boneless_editing, "How high a boneless pops. 1 is the game's own.");
|
||||
|
||||
Reference in new issue
Block a user