From 2b0b744ec5b3095d0010eba6f4d6cf11d2aa8e2f Mon Sep 17 00:00:00 2001 From: AyeZeeBB Date: Tue, 20 Oct 2026 18:46:02 -0500 Subject: [PATCH] Animator Studio: not opened during a bail, and the walk back from the character room always ends - The studio waits until a bail is over (its state, or 4 s after the bail event): held still mid-bail, the game took the hold away and the skater went on bailing behind the studio. - The character room's walk back gives up after 8 s: with no skater ready (a respawn) it could not step, and the studio stayed on its loading screen. --- Extension/Plugins/Test/plugin_tests.cpp | 8 ++++++++ Extension/World/character_room.cpp | 12 +++++++++++- contrib/plugins/animator-studio/plugin/main.lua | 8 ++++++++ contrib/plugins/animator-studio/reskate-plugin.json | 2 +- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Extension/Plugins/Test/plugin_tests.cpp b/Extension/Plugins/Test/plugin_tests.cpp index 291e9e22..ca2b8fe2 100644 --- a/Extension/Plugins/Test/plugin_tests.cpp +++ b/Extension/Plugins/Test/plugin_tests.cpp @@ -2352,6 +2352,14 @@ void animator_studio_records_and_saves() { running(host, "animator-studio", environment); check(host.run_console("studio status") && environment.printed("studio: closed; nothing recorded; 0 saved"), "It starts closed:\n" + printed_lines(environment) + environment.all()); + // Not in the middle of a bail: the game would take the studio's hold of the skater away. + environment.local_skater = SkaterState{{1, 0, 2}, 0, 0, 0, false, 0}; + game.state = 301; + environment.lines.clear(); + check(host.run_console("studio open") && host.run_console("studio status") && environment.printed("studio: closed") && + !game.called("freeze animator-studio on"), + "It does not open during a bail:\n" + printed_lines(environment) + environment.all()); + game.state.reset(); environment.local_skater = SkaterState{{1, 0, 2}, 0, 0, 0, false, 0}; check(host.run_console("studio record") && game.called("record animator-studio 10.0"), "It records\n" + environment.all()); ticks(host, environment, 3); diff --git a/Extension/World/character_room.cpp b/Extension/World/character_room.cpp index 01334fe6..e88d3c29 100644 --- a/Extension/World/character_room.cpp +++ b/Extension/World/character_room.cpp @@ -49,6 +49,9 @@ constexpr std::uint64_t post_retry_ms = 250, step_deadline_ms = 4000; constexpr std::uint64_t page_settle_ms = 300, queue_settle_ms = 1500; constexpr std::uint64_t tab_wait_ms = 1500, skater_wait_ms = 10000; constexpr std::uint64_t open_wait_ms = 30000, leave_wait_ms = 2000; +// The walk back ends by then whatever happens: without a skater ready (a bail's respawn, a load) it +// cannot step, and the room is left behind rather than waited on. +constexpr std::uint64_t leave_give_up_ms = 8000; constexpr int max_scans = 3; constexpr std::uint64_t scan_gap_ms = 15000; // The way back to the world: the Skaters page's Back closes the menu. ToggleMenu is for a menu that is still open. @@ -75,7 +78,7 @@ struct State { std::uint64_t page_shown_at{}; int leave_steps_left{}; bool leave_pressed{}; - std::uint64_t leave_step_deadline{}; + std::uint64_t leave_step_deadline{}, leave_deadline{}; std::string failure; }; State& state() { static auto* value = new State; return *value; } @@ -337,6 +340,7 @@ void leave() noexcept { skater_animation::watch_room(false); s.leave_steps_left = static_cast(way_back.size()); s.leave_pressed = false; + s.leave_deadline = GetTickCount64() + leave_give_up_ms; s.phase.store(Phase::leaving, std::memory_order_release); } Phase phase() noexcept { return state().phase.load(std::memory_order_acquire); } @@ -363,6 +367,12 @@ void tick(std::uintptr_t base, std::uintptr_t skater, bool ready, bool prepare) s.skater = skater; s.skater_since = GetTickCount64(); } + if (s.phase.load(std::memory_order_acquire) == Phase::leaving && now >= s.leave_deadline) { + s.leave_steps_left = 0; + s.leave_pressed = false; + s.phase.store(Phase::idle, std::memory_order_release); + return; + } if (!ready || !base) return; const auto p = s.phase.load(std::memory_order_acquire); // The scan is not cheap: for a walk, or once a map ahead of one, a few times at most. diff --git a/contrib/plugins/animator-studio/plugin/main.lua b/contrib/plugins/animator-studio/plugin/main.lua index f6206be4..b4f91643 100644 --- a/contrib/plugins/animator-studio/plugin/main.lua +++ b/contrib/plugins/animator-studio/plugin/main.lua @@ -608,6 +608,8 @@ local function begin_leave() loading = {what = "leaving", since = t, until_ = t + 0.8, close = true} end end +local last_bail = -1e9 -- now() of the skater's last bail +reskate.on("bail", function() last_bail = now() end) local function open_studio() if studio.open then return end local s = reskate.skater.get() @@ -615,6 +617,12 @@ local function open_studio() reskate.ui.toast("The studio needs a skater.", "warn") return end + -- Held still in the middle of a bail, the skater is lost when the game takes the hold away, and he + -- goes on bailing behind the studio. A bail lasts about 3.5 s. + if reskate.skater.state() == "wipeout" or now() - last_bail < 4 then + reskate.ui.toast("Get back on your board first: the studio opens once the bail is over.", "warn") + return + end anchor = {s.position.x, s.position.y + 0.9, s.position.z} orbit.yaw = s.heading + 0.6 eased, face_skater = nil, true diff --git a/contrib/plugins/animator-studio/reskate-plugin.json b/contrib/plugins/animator-studio/reskate-plugin.json index 94ca1ba7..e2d167db 100644 --- a/contrib/plugins/animator-studio/reskate-plugin.json +++ b/contrib/plugins/animator-studio/reskate-plugin.json @@ -2,6 +2,6 @@ "api": 1, "entry": "plugin/main.lua", "side": "client", - "permissions": ["skater.animation", "skater.read", "skater.control", "camera", "hud", "input", "ui.screen", "room", + "permissions": ["skater.animation", "skater.read", "skater.control", "events.skater", "camera", "hud", "input", "ui.screen", "room", "files", "overlay", "console", "menu"] }