Fit Check as a plugin: screen capture, the menu's preview turned and read, and what the skater wears

Pull request 326 split into general plugin APIs and contrib/plugins/fit-check:
- reskate.capture (start, stop, discard, state, compare, kept, save): the screen
  recorded before the overlay draws, cut out by the game's depth or by colour,
  saved as GIF, APNG, PNG or WebM with an optional card; the "capture" permission.
- reskate.room.turn: a stand-in pad's stick held over to turn the menu's preview.
- reskate.menu.game and reskate.menu.grid: what the game's menu and its item grid show.
- reskate.outfit.worn, board and title: what the skater wears, by name.
- reskate.files.reveal: a data folder opened in Explorer.
The plugin finds where the turn comes round, eases the stick off, picks evenly
spaced frames and saves them with the board's parts or the outfit on the card.
This commit is contained in:
AyeZeeBB committed 2026-10-21 17:16:56 -05:00
1 parent 79df4762c4
commit dce293e25d
54 files changed
+5579 -10

No files matched your search

+22 -1
View File
@@ -1,6 +1,8 @@
#include "scripted_pad.h"
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <mutex>
namespace dingosdk::overlay {
@@ -13,6 +15,10 @@ struct State {
std::chrono::steady_clock::time_point last_tick, done_at;
bool active{};
DWORD packet{};
// A stick held (hold_stick).
int stick{};
SHORT value{};
std::chrono::steady_clock::time_point held_until{};
};
State &state() { static auto *value = new State; return *value; }
constexpr std::chrono::milliseconds linger{2000}, no_ticks{250}, tick_length{17};
@@ -41,12 +47,27 @@ void pad_tick() noexcept {
std::lock_guard lock(s.mutex);
if (s.active) advance(s, std::chrono::steady_clock::now());
}
void hold_stick(int stick, float value, unsigned milliseconds) noexcept {
auto &s = state();
std::lock_guard lock(s.mutex);
s.stick = stick < 0 || stick > 3 ? 2 : stick;
s.value = static_cast<SHORT>(std::lround(std::clamp(value, -1.0f, 1.0f) * 32767.0f));
s.held_until = value == 0 ? std::chrono::steady_clock::time_point{}
: std::chrono::steady_clock::now() + std::chrono::milliseconds(milliseconds);
}
bool scripted_pad(DWORD user, XINPUT_STATE &output) noexcept {
auto &s = state();
if (user != 0) return false;
std::lock_guard lock(s.mutex);
if (!s.active) return false;
const auto now = std::chrono::steady_clock::now();
if (!s.active) {
if (now >= s.held_until) return false;
output.Gamepad = XINPUT_GAMEPAD{};
SHORT* axes[] = {&output.Gamepad.sThumbLX, &output.Gamepad.sThumbLY, &output.Gamepad.sThumbRX, &output.Gamepad.sThumbRY};
*axes[s.stick] = s.value;
output.dwPacketNumber = ++s.packet;
return true;
}
// With no game ticks, time alone moves the script on.
while (s.current + 1 < s.steps.size() && now - s.last_tick > no_ticks) advance(s, s.last_tick + tick_length);
const bool done = s.current + 1 == s.steps.size();