mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-26 02:54:36 +01:00
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.
777 lines
46 KiB
C++
777 lines
46 KiB
C++
#pragma once
|
|
#include "plugin_wave.h"
|
|
#include "Engine/Core/Json/json.h"
|
|
#include "Extension/AnimationStudio/anim_core.h"
|
|
#include "Extension/Throwdowns/throwdown_kit.h"
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <filesystem>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
// The game behind the wider set of the plugin API (docs: ReSkate Plugin System Design): what plugins
|
|
// may read and change of the skater, the world, the session and the rest. Every function has an
|
|
// "unavailable" default, so the host runs without a game (the tests give it a fake one) and the
|
|
// game side fills in what it can (plugin_game_runtime.cpp). Client thread throughout.
|
|
//
|
|
// A string a function returns is empty when it worked, and otherwise says why not, for the plugin.
|
|
namespace dingosdk::plugins {
|
|
using Vec3 = std::array<float, 3>;
|
|
inline constexpr char unavailable[] = "unavailable here";
|
|
|
|
// The game's camera as it is now: where it is, the way it looks, and its vertical field of view.
|
|
struct CameraView {
|
|
Vec3 position{}, forward{};
|
|
float fov{};
|
|
Vec3 right{}, up{};
|
|
bool free_camera{}; // the view is Freecam's, verified selected (not just asked for)
|
|
};
|
|
// What a game layer reads of the local skater each tick (reskate.skater.telemetry): the trainer's
|
|
// telemetry and its last measured jump, the physics state the skater's selector chose (with No
|
|
// Bail's wipeout and runout counts), the Bone Cam's slams and where the body is during a bail.
|
|
struct SkaterTelemetry {
|
|
bool skater{}, airborne{};
|
|
Vec3 position{};
|
|
float heading{}; // the trainer's degrees: 0 faces +Z, forward (sin, 0, cos)
|
|
float speed{}, vertical{}, air_time{}, height{}, top_speed{};
|
|
std::uint32_t physics_state{};
|
|
struct Jump {
|
|
std::uint64_t serial{};
|
|
float takeoff_speed{}, takeoff_angle{}, air_time{}, height{}, distance{}, drop{}, landing_speed{};
|
|
float spin{}, spin_rate{}, flip{}, board_turn{};
|
|
std::uint32_t state{};
|
|
Vec3 takeoff{}, landing{};
|
|
} last;
|
|
struct Watch {
|
|
bool valid{}, ragdoll_known{}, ragdoll{};
|
|
std::uint32_t state{}, previous{};
|
|
float previous_seconds{};
|
|
std::uint64_t changes{}, wipeouts{}, runouts{};
|
|
} watch;
|
|
bool teleport_pending{}, slam{}, struck{};
|
|
std::optional<Vec3> body; // the hips, during a bail (Bone Cam)
|
|
};
|
|
struct TrickNote {
|
|
std::uint64_t entry{};
|
|
char kind{}; // 'n' name, 's' side, 'l' landing, 'b' body spin, 'r' board spin, 'k' stance, 'm' modifier
|
|
std::string text;
|
|
};
|
|
// Input for a plugin that flies something: the flight keys and mouse look (as Freecam reads them),
|
|
// a gamepad's sticks, triggers and buttons (XInput's bits), a joystick or radio's axes and buttons.
|
|
struct FlightInput {
|
|
float right{}, up{}, forward{}, look_x{}, look_y{};
|
|
bool boost{};
|
|
};
|
|
struct PadInput {
|
|
float left_x{}, left_y{}, right_x{}, right_y{}, left_trigger{}, right_trigger{};
|
|
std::uint32_t buttons{};
|
|
bool playstation{};
|
|
};
|
|
struct StickInput {
|
|
std::array<float, 6> axes{};
|
|
std::uint32_t buttons{};
|
|
std::string name;
|
|
};
|
|
// A sound a plugin plays (reskate.audio): its wave, or the motor synth when it has none.
|
|
struct SoundStart {
|
|
std::shared_ptr<const Wave> wave;
|
|
float volume{1}, pitch{1}, range{40}, throttle{}, speed{};
|
|
bool loop{};
|
|
std::optional<Vec3> position; // empty: at the local skater
|
|
};
|
|
struct SoundChange {
|
|
std::optional<float> volume, pitch, range, throttle, speed;
|
|
std::optional<std::optional<Vec3>> position;
|
|
};
|
|
// What the local skater's physics did since the last tick (events.skater).
|
|
struct SkaterNews {
|
|
struct Landing {
|
|
float down_speed{}, speed{};
|
|
bool rail{};
|
|
};
|
|
struct Hit {
|
|
std::string bone; // as the game names its body ("Left forearm")
|
|
float impact{};
|
|
bool vehicle{}, board{};
|
|
};
|
|
struct Fall {
|
|
float seconds{}, left{}, right{};
|
|
struct Bone {
|
|
std::string name;
|
|
float impact{}, slide{};
|
|
};
|
|
std::vector<Bone> bones; // those that hit or slid
|
|
};
|
|
std::vector<Landing> landings;
|
|
std::vector<Hit> hits;
|
|
std::vector<Fall> falls;
|
|
};
|
|
// One physics step of the local skater (events.physics): when (the steady clock, ms, as
|
|
// reskate.time.now), how long it simulated, and what the body did; only the bodies touching something.
|
|
struct PhysicsStep {
|
|
double at{};
|
|
float seconds{};
|
|
bool wipeout{}, airborne{};
|
|
std::optional<bool> ragdoll;
|
|
std::optional<Vec3> velocity;
|
|
std::optional<float> spin;
|
|
struct Body {
|
|
std::string name; // as body_hit names it
|
|
float impact{}, slide{};
|
|
bool board{}, vehicle{}, world{}; // world: anything but the board and vehicles
|
|
};
|
|
std::vector<Body> bodies;
|
|
};
|
|
// What a plugin asks of the next pop out of a grind: "toe", "heel", "left" or "right" (of travel),
|
|
// the player's stance (0 not set, 1 regular, 2 goofy: what toe and heel need), and 0.1 to 1 of the
|
|
// full sideways speed. And a pop as it went (Extension/Skater/grind_exit.h).
|
|
struct GrindExitWish {
|
|
std::string side;
|
|
int stance{};
|
|
float strength{1};
|
|
};
|
|
struct GrindPop {
|
|
bool applied{};
|
|
std::string reason, basis;
|
|
int toward{}; // +1 the left of travel, -1 the right, 0 the game's own
|
|
float before{}, after{};
|
|
};
|
|
// The last slam: its force and the game's cause number, and how long ago (ms).
|
|
struct SlamNews {
|
|
float force{};
|
|
int cause{-1};
|
|
double ago_ms{};
|
|
};
|
|
struct CatalogItem {
|
|
std::string key, title, category;
|
|
};
|
|
// A park editor object on the map being played: its layout id, the item, where and how it sits.
|
|
struct GameObject {
|
|
std::uint64_t id{};
|
|
std::string item;
|
|
Vec3 position{};
|
|
std::array<float, 4> rotation{0, 0, 0, 1}; // x, y, z, w
|
|
float scale{1};
|
|
};
|
|
struct Destination {
|
|
std::string id, name, map; // fast travel
|
|
};
|
|
struct LevelInfo {
|
|
std::string asset, name;
|
|
bool can_load{};
|
|
};
|
|
// A skater's body as capsules: `part` indexes body_part_names; a and b are its ends in the world.
|
|
struct BodyCapsule {
|
|
std::uint8_t part{};
|
|
Vec3 a{}, b{};
|
|
float radius{};
|
|
};
|
|
struct Body {
|
|
std::uint64_t player{}; // 0: the local skater
|
|
std::vector<BodyCapsule> capsules;
|
|
};
|
|
inline constexpr std::array<const char*, 7> body_part_names{"head", "body", "left_arm", "right_arm", "left_leg", "right_leg", "board"};
|
|
// Another player's skater as the session last heard it.
|
|
struct PeerSkater {
|
|
std::uint64_t id{};
|
|
Vec3 position{};
|
|
float heading{}; // as the local skater's
|
|
double age_ms{}; // since its pose arrived
|
|
};
|
|
struct ChatLine {
|
|
std::uint64_t sequence{}, sender{};
|
|
std::string name, text;
|
|
bool local{}, server{};
|
|
};
|
|
struct Track {
|
|
bool available{}, playing{};
|
|
std::string app, title, artist, album;
|
|
std::uint64_t revision{};
|
|
};
|
|
struct ThrowdownState {
|
|
bool active{}, host{};
|
|
struct Player {
|
|
std::string name;
|
|
int letters{};
|
|
bool up{}, out{};
|
|
};
|
|
std::vector<Player> skate; // S.K.A.T.E.'s players and letters, while one is shown
|
|
};
|
|
// A throwdown mode on the game's Throwdowns page: a card on its picker, the game's own setup page
|
|
// (relabelled from the plugin's setup document) and the game's start flag. The plugin runs it.
|
|
struct ThrowdownMode {
|
|
std::string id, name, description;
|
|
std::string art; // the logo: a texture or image asset the plugin's mod adds, by path; empty: the card's own
|
|
std::string flag; // the placement-preview prefab its native flag shows, by path; empty: Spot Battle's
|
|
std::string card = "spot"; // the stock card it is cloned from: "skate", "spot" or "jam"
|
|
std::string replaces; // the stock throwdown whose place it takes: "skate", "spot", "jam" or empty
|
|
std::uint32_t color{};
|
|
int order{}; // among the plugin modes on the picker
|
|
int min_players = 1, max_players = 6, players = 4;
|
|
float cursor_height{}; // > 0: its flag's placement cursor may rise this far (metres)
|
|
};
|
|
// reskate.ui.dialog: an overlay dialog over the game (plugin_dialog.cpp). Its answers come back to
|
|
// the plugin as throwdown_ui events with action "dialog" (throwdown_kit.h).
|
|
struct DialogSpec {
|
|
std::string id, title, message;
|
|
struct Field {
|
|
bool present{};
|
|
std::string text;
|
|
std::size_t max{64};
|
|
} field;
|
|
struct Row {
|
|
std::string text, sub;
|
|
std::vector<std::string> buttons; // each answers with its name and the row
|
|
};
|
|
bool list{};
|
|
std::vector<Row> rows;
|
|
std::string empty; // shown in a list with no rows
|
|
struct Button {
|
|
std::string id, label;
|
|
};
|
|
std::vector<Button> buttons;
|
|
struct File {
|
|
bool present{};
|
|
std::string label, filter; // filter: "*.json" or "*.json;*.txt"
|
|
} file;
|
|
};
|
|
struct Progress {
|
|
bool available{};
|
|
struct District {
|
|
std::string id, name;
|
|
int rank{};
|
|
};
|
|
std::vector<District> districts;
|
|
struct Challenge {
|
|
std::string id, type, neighborhood;
|
|
int attempts{}, completed_goals{}, goal_count{};
|
|
};
|
|
std::vector<Challenge> challenges;
|
|
double score{}, score_cap{};
|
|
int score_level{};
|
|
};
|
|
|
|
class Game {
|
|
public:
|
|
virtual ~Game() = default;
|
|
// ---- the local skater ----------------------------------------------------------------------
|
|
// Its physics state (100s riding, 200s in the air, 300 a wipeout, 400s grinding, 504 off the
|
|
// board), or empty without a skater; and how many wipeouts it has had this session.
|
|
virtual std::optional<std::uint32_t> physics_state() { return std::nullopt; }
|
|
virtual std::uint64_t wipeouts() { return 0; }
|
|
// "noclip_on", "noclip_off", "boost_forward", "boost_up", "boost_offboard": queued as the
|
|
// console's own are, under the host's rules.
|
|
virtual std::string control(std::string_view) { return unavailable; }
|
|
// The game's HUD hidden while any plugin asks; given back when the last one stops asking.
|
|
virtual void hide_hud(std::string_view /*plugin*/, bool) {}
|
|
// ---- the world -------------------------------------------------------------------------------
|
|
virtual std::vector<CatalogItem> object_catalog() { return {}; }
|
|
virtual std::vector<GameObject> objects() { return {}; }
|
|
// "place" (item and pose), "move" (id and pose) or "delete" (id): through the park editor,
|
|
// under the session's object rules and limits. One at a time: it is busy until done.
|
|
virtual std::string edit_object(std::string_view, const GameObject&) { return unavailable; }
|
|
// A plugin's own world objects (world_entities.h): an entity of a world object blueprint at a
|
|
// full transform (rows: right, up, forward axes with their scale, then the position), kept by the
|
|
// plugin's id for it. Only this player's game shows them. Status: 0 none, 1 waiting, 2 loading,
|
|
// 3 missing, 4 ready, 5 hidden.
|
|
// With `attach`, kept on a joint of a skater (player 0: the local one) and the transform in that
|
|
// joint's frame, moved with it every frame.
|
|
struct EntityAttach {
|
|
std::uint64_t player{};
|
|
std::uint16_t joint{};
|
|
};
|
|
virtual std::string entity_set(std::string_view /*plugin*/, std::uint32_t, std::string_view, const std::array<float, 16>&, bool,
|
|
std::optional<EntityAttach> /*attach*/) {
|
|
return unavailable;
|
|
}
|
|
virtual void entity_remove(std::string_view /*plugin*/, std::uint32_t) {}
|
|
virtual void entity_clear(std::string_view /*plugin*/) {}
|
|
virtual int entity_status(std::string_view /*plugin*/, std::uint32_t) { return 0; }
|
|
virtual bool entities_ready(std::string_view /*plugin*/) { return false; }
|
|
// kind "tod" (key: morning, noon, afternoon, evening, night, weatherday, weathernight, default),
|
|
// "control" (key: fog, fog_distance, sky_brightness, clouds, wind_strength, wind_direction,
|
|
// traffic, pedestrians; value a number, -1 the default) or "park" (key: a lot, value a layout).
|
|
virtual std::string set_environment(std::string_view, std::string_view, std::string_view) { return unavailable; }
|
|
virtual std::vector<Destination> destinations() { return {}; }
|
|
virtual std::vector<LevelInfo> levels() { return {}; }
|
|
// kind "location" (a destination's id) or "level" (a level's asset); a guest is refused.
|
|
virtual std::string travel(std::string_view, std::string_view) { return unavailable; }
|
|
// "spawn" (count) or "clear"; refused in a session, where others would never see them.
|
|
virtual std::string ai(std::string_view, int) { return unavailable; }
|
|
virtual int ai_count() { return 0; }
|
|
// ---- the session ----------------------------------------------------------------------------
|
|
virtual std::vector<PeerSkater> peer_skaters() { return {}; }
|
|
// Every skater's body in the session as this game drew it last (plugin_session_feed.h body_of):
|
|
// the other players' (hidden ones too) and the local skater's, as player 0. Asking keeps them read
|
|
// for two seconds, so the first ask comes back empty.
|
|
virtual std::vector<Body> bodies() { return {}; }
|
|
// Another player's skater, board, sound, nametag and map marker not shown on this screen while
|
|
// any plugin hides them (at most 256 between them).
|
|
virtual std::string hide_player(std::string_view /*plugin*/, std::uint64_t, bool /*hidden*/) { return unavailable; }
|
|
virtual std::vector<std::uint64_t> hidden_players(std::string_view /*plugin*/) { return {}; }
|
|
// A short tag beside a player's nametag, the plugin's own (empty text takes it away).
|
|
virtual void set_nametag(std::string_view /*plugin*/, std::uint64_t, std::string, std::uint32_t) {}
|
|
// 0 mutes, 1 is as heard, up to 10. The local player's ears only.
|
|
virtual std::string voice_volume(std::uint64_t, float) { return unavailable; }
|
|
// Chat lines after `sequence`, as this player sees them (filtered when they filter).
|
|
virtual std::vector<ChatLine> chat_since(std::uint64_t) { return {}; }
|
|
virtual std::string send_chat(std::string_view) { return unavailable; }
|
|
// A line in this player's chat only, under the plugin's name with a "Plugin" tag.
|
|
virtual std::string local_chat(std::string_view /*plugin*/, std::string_view, std::uint32_t /*colour, 0 the usual*/) { return unavailable; }
|
|
virtual ThrowdownState throwdowns() { return {}; }
|
|
// A drop the local player hosts where the skater stands, for chosen players of the session
|
|
// (`series` "JamSession", "SpotBattle" or "ThrowdownSkate"): they see it and join it with the
|
|
// game's own Join, and it starts once all have. Its status: "", "asked", "spawning", "open",
|
|
// "started", "cancelled", or why it failed.
|
|
virtual std::string host_throwdown(std::string_view /*plugin*/, std::string_view /*series*/, std::vector<std::uint64_t> /*players*/,
|
|
int /*max players*/) {
|
|
return unavailable;
|
|
}
|
|
virtual void cancel_throwdown(std::string_view /*plugin*/) {}
|
|
virtual std::string throwdown_status(std::string_view /*plugin*/) { return {}; }
|
|
// Adds the mode to the Throwdowns page, or updates it (by id).
|
|
virtual std::string set_throwdown_mode(std::string_view /*plugin*/, const ThrowdownMode&) { return unavailable; }
|
|
virtual void remove_throwdown_mode(std::string_view /*plugin*/, std::string_view /*id*/) {}
|
|
// The throwdown kit (throwdown_kit.h): the plugin's documents for the native UI, and the one
|
|
// native flag registration (`mode` is the SDK's id, "<plugin>/<id>"). Leave lets go of the
|
|
// registration only when it is one of the plugin's modes.
|
|
virtual std::string publish_throwdown_ui(std::string_view /*plugin*/, const Json&) { return unavailable; }
|
|
virtual bool throwdown_flag_begin(std::string_view /*mode*/, std::uint32_t /*game*/, const throwdowns::kit::flag::Options&) { return false; }
|
|
virtual throwdowns::kit::flag::State throwdown_flag_state() { return {}; }
|
|
virtual void throwdown_flag_leave(std::string_view /*plugin*/) {}
|
|
// ---- general services for a game layer in Lua (plugin_api_services.cpp) ----------------------
|
|
// `plugin`: whose read it is (slam and struck are news since that plugin's last read).
|
|
virtual std::optional<SkaterTelemetry> skater_telemetry(std::string_view /*plugin*/) { return std::nullopt; }
|
|
// The pad's XInput button bits, LT 0x10000 and RT 0x20000 (empty: no pad). `menu`: read while
|
|
// ReSkate's menu is open too.
|
|
virtual std::optional<std::uint32_t> pad_bits(bool /*menu*/) { return std::nullopt; }
|
|
virtual bool key_down(int /*virtual_key*/) { return false; }
|
|
virtual int mouse_wheel() { return 0; }
|
|
// The mouse since this plugin's last read (reskate.input.mouse): raw counts right and down, the
|
|
// wheel in notches (away from you positive). Empty while the game is not in front or a menu is open.
|
|
struct MouseMotion {
|
|
double x{}, y{}, wheel{};
|
|
};
|
|
virtual std::optional<MouseMotion> mouse_motion(std::string_view /*plugin*/) { return std::nullopt; }
|
|
// The mouse over the game window while a plugin's screen is open (reskate.input.cursor): where
|
|
// (pixels from the top left), its buttons, the wheel's notches since the last read, and whether
|
|
// it is over the screen's own panel.
|
|
struct Cursor {
|
|
float x{}, y{}, wheel{};
|
|
bool left{}, right{}, middle{}, over_screen{};
|
|
};
|
|
virtual std::optional<Cursor> cursor() { return std::nullopt; }
|
|
// What was typed since the last call while a plugin's screen is open (reskate.input.text);
|
|
// `capture` keeps the typing the plugin's for 300 ms more.
|
|
virtual std::string typed_text(bool /*capture*/) { return {}; }
|
|
// The game menu's character room (Extension/World/character_room.h; reskate.room): walked to, the
|
|
// local skater shown in it once there, and walked back from. One plugin at a time. state: "idle",
|
|
// "opening", "open", "leaving" or "failed" (with why).
|
|
virtual std::string room_open(std::string_view /*plugin*/) { return unavailable; }
|
|
virtual void room_leave(std::string_view /*plugin*/) {}
|
|
struct RoomState {
|
|
std::string state, why;
|
|
};
|
|
virtual RoomState room_state(std::string_view /*plugin*/) { return {"idle", {}}; }
|
|
// The game menu's preview (the skater on the Skaters page, the board on a Board tab) turned by a
|
|
// stick of a stand-in pad: `value` -1..1 of the stick held for 300 ms (call it every tick), 0 lets
|
|
// go; `stick` 0 left x, 1 left y, 2 right x, 3 right y. Only while the game's menu is open.
|
|
virtual std::string room_turn(std::string_view /*plugin*/, float /*value*/, int /*stick*/) { return unavailable; }
|
|
// The game's menu: whether it is open, whether it shows the Skaters page, and the navigations of
|
|
// the open page's buttons (how the game names where each goes: "CASPresetPurchase", ...).
|
|
struct GameMenu {
|
|
bool open{}, skaters{};
|
|
std::vector<std::string> buttons;
|
|
};
|
|
virtual GameMenu game_menu() { return {}; }
|
|
// The customization grid that has the focus (Extension/Customization/item_browser.h), when one does.
|
|
struct MenuGrid {
|
|
std::string focused, title, category; // the highlighted item: key, title, catalog category
|
|
std::vector<std::string> items; // every item's key in the grid
|
|
unsigned shown{}, total{};
|
|
};
|
|
virtual std::optional<MenuGrid> menu_grid() { return std::nullopt; }
|
|
// ---- capture: the screen as the game draws it (Extension/Rendering/Capture) ---------------------
|
|
// One plugin records at a time; frames are numbered from 0 as they are taken.
|
|
struct CaptureStart {
|
|
float left{}, top{}, right{1}, bottom{1};
|
|
bool menu{}; // fractions of the game's menu frame (at most 21:9), else of the screen
|
|
bool depth{true};
|
|
std::uint32_t height{}, skip{}, most{2400};
|
|
double seconds{60};
|
|
};
|
|
virtual std::string capture_start(std::string_view /*plugin*/, const CaptureStart&) { return unavailable; }
|
|
virtual void capture_stop(std::string_view /*plugin*/) {}
|
|
virtual void capture_discard(std::string_view /*plugin*/) {}
|
|
struct CaptureStatus {
|
|
std::string state; // "idle", "recording", "stopped", "saving", "saved", "failed"
|
|
std::uint32_t taken{}, kept{};
|
|
bool depth{};
|
|
std::string message, file; // file: its path in the plugin's data folder
|
|
};
|
|
// Another plugin's capture reads as idle.
|
|
virtual CaptureStatus capture_status(std::string_view /*plugin*/) { return {"idle"}; }
|
|
virtual float capture_compare(std::string_view /*plugin*/, std::uint32_t, std::uint32_t) { return -1; }
|
|
virtual std::vector<std::uint32_t> capture_kept(std::string_view /*plugin*/) { return {}; }
|
|
struct CaptureSave {
|
|
std::vector<std::uint32_t> frames;
|
|
std::string format{"gif"}; // "gif", "apng", "png", "webm"
|
|
std::uint32_t fps{30}, size{512};
|
|
std::optional<std::uint32_t> background; // 0xRRGGBB; none: transparent
|
|
std::string cutout{"auto"}; // "auto", "depth", "colour", "none"
|
|
bool solid{true};
|
|
std::string card_title;
|
|
std::vector<std::pair<std::string, std::string>> card; // label, value
|
|
std::string path; // in the plugin's data folder
|
|
};
|
|
virtual std::string capture_save(std::string_view /*plugin*/, const CaptureSave&) { return unavailable; }
|
|
// Game buttons kept from the game while asked (overlay::hide_game_buttons/hold_game_buttons),
|
|
// and all game input paused for half a second at a time.
|
|
virtual void game_buttons(std::string_view /*plugin*/, std::uint16_t /*hidden*/, std::uint16_t /*held*/, bool /*paused*/) {}
|
|
// The game window has the focus, ReSkate's menu is shut and the native UI lets the game play.
|
|
virtual bool input_allowed() { return false; }
|
|
virtual std::string free_camera(std::string_view /*plugin*/, bool) { return unavailable; }
|
|
// The free camera flown from the pad while placing from the air (Extension/Skater/pad_flight.h);
|
|
// one plugin holds it at a time.
|
|
virtual std::string pad_flight(std::string_view /*plugin*/, bool) { return unavailable; }
|
|
// The stock physics while the local player competes (set_competition_stock_physics).
|
|
virtual void competition(std::string_view /*plugin*/, bool) {}
|
|
virtual bool stock_physics_ready() { return false; }
|
|
// physics_tuning::prepare(): the game's stock tuning read, without switching anything.
|
|
virtual void prepare_physics() {}
|
|
// The session's world (it changes with the map or session), whether the local player's mods bar
|
|
// them from scoring, and the map's name.
|
|
virtual std::string world_map() { return {}; }
|
|
virtual std::uint64_t world_id() { return 0; }
|
|
virtual bool world_barred() { return false; }
|
|
virtual std::optional<bool> stance_goofy() { return std::nullopt; }
|
|
virtual void trick_feed(std::string_view /*plugin*/, bool) {}
|
|
virtual std::vector<TrickNote> take_trick_notes() { return {}; }
|
|
// The Bone Cam (Extension/Skater/bone_cam.h): no words for its setting, "status", "bail",
|
|
// "ringing", "game on|off" (the plugin's game asks for it), or the bonecam console words.
|
|
virtual std::string bone_cam(std::string_view /*plugin*/, const std::vector<std::string>& /*words*/) { return {}; }
|
|
// Three focus checks: the game window has focus, ReSkate's overlay is open, and the throwdown
|
|
// flow lets gameplay input through.
|
|
struct InputFocus {
|
|
bool focused{}, interface_open{}, gameplay{};
|
|
};
|
|
virtual InputFocus input_focus() { return {}; }
|
|
// Watch another player's skater the way the native party menu does (0: back to our own).
|
|
virtual void spectate(std::uint64_t /*player*/) {}
|
|
// The flick diagram of a flip trick as skate. names it (trick_gestures.h): read in the
|
|
// background the first time; empty until then, or for a trick without one.
|
|
virtual std::vector<std::array<float, 2>> trick_gesture(std::string_view /*trick*/) { return {}; }
|
|
// What the game shows and records of the local skater's tricks (Extension/Skater/trick_rewrite.h):
|
|
// the ticker's label `from` shown as `to` (empty: that label is the game's again), and the next
|
|
// trick with one of `from`'s ids, within `within_ms`, recorded as `to`. Empty when done, else why not.
|
|
virtual std::string trick_label(std::string_view /*plugin*/, std::string_view /*from*/, std::string_view /*to*/) { return unavailable; }
|
|
// ---- content: a folder of every enabled mod, as the plugin's manifest names it ("content") -------
|
|
struct ContentFile {
|
|
std::string mod, title; // the mod's folder name and its own name
|
|
std::string path; // under <mod>/<folder>/, with / between folders
|
|
std::uint64_t size{};
|
|
};
|
|
// The files there, mod by mod in load order; `folder` is checked against the manifest already.
|
|
virtual std::string content_list(std::string_view /*folder*/, std::vector<ContentFile>& /*files*/) { return unavailable; }
|
|
// One of them: `path` is checked already (safe_relative_path). Empty when read, else why not.
|
|
virtual std::string content_read(std::string_view /*mod*/, std::string_view /*folder*/, std::string_view /*path*/, std::string& /*text*/) {
|
|
return unavailable;
|
|
}
|
|
virtual std::string trick_record_as(std::string_view /*plugin*/, std::vector<std::uint32_t> /*from*/, std::uint32_t /*to*/, int /*within_ms*/) {
|
|
return unavailable;
|
|
}
|
|
// ---- files: the plugin's own data folder, <game>/Mods/.reskate_plugin_data/<plugin>/ -----------
|
|
// Paths are relative to it and already checked (plugin_manifest.h's safe_relative_path). A listing
|
|
// names the files and folders in `folder` (empty: the top), folders ending in "/".
|
|
virtual std::string files_list(std::string_view /*plugin*/, std::string_view /*folder*/, std::vector<std::string>& /*names*/) { return unavailable; }
|
|
virtual std::string files_read(std::string_view /*plugin*/, std::string_view /*path*/, std::string& /*text*/) { return unavailable; }
|
|
virtual std::string files_write(std::string_view /*plugin*/, std::string_view /*path*/, std::string_view /*text*/) { return unavailable; }
|
|
virtual std::string files_remove(std::string_view /*plugin*/, std::string_view /*path*/) { return unavailable; }
|
|
// The data folder (or a folder in it) opened in the player's file manager.
|
|
virtual std::string files_reveal(std::string_view /*plugin*/, std::string_view /*folder*/) { return unavailable; }
|
|
// The same, for a file up to `most` bytes (an animation clip's, say).
|
|
virtual std::string files_read_bytes(std::string_view plugin, std::string_view path, std::string& out, std::size_t /*most*/) {
|
|
return files_read(plugin, path, out);
|
|
}
|
|
virtual std::string files_write_bytes(std::string_view plugin, std::string_view path, std::string_view bytes, std::size_t /*most*/) {
|
|
return files_write(plugin, path, bytes);
|
|
}
|
|
// ---- ui.dialog -----------------------------------------------------------------------------------
|
|
// One dialog at a time over the game; the same id again updates it (what was typed is kept).
|
|
virtual std::string dialog_open(std::string_view /*plugin*/, const DialogSpec&) { return unavailable; }
|
|
virtual void dialog_close(std::string_view /*plugin*/, std::string_view /*id, empty: any of the plugin's*/) {}
|
|
// ---- skater.push / hold ----------------------------------------------------------------------------
|
|
// The skater's velocity set to this (metres per second, world) once it is back on its board, and
|
|
// the skater held where it is (no input, no physics) while asked. A pending push may wait two
|
|
// seconds more (the skater is still getting back on its board) or be dropped.
|
|
virtual std::string skater_push(std::string_view /*plugin*/, const Vec3&) { return unavailable; }
|
|
virtual std::string skater_push_wait(std::string_view /*plugin*/) { return unavailable; }
|
|
virtual std::string skater_push_reset(std::string_view /*plugin*/) { return unavailable; }
|
|
virtual std::string skater_hold(std::string_view /*plugin*/, bool) { return unavailable; }
|
|
// ---- the rest ----------------------------------------------------------------------------------
|
|
// The music an outside player (Spotify and the like) is playing, and "play", "pause",
|
|
// "next" or "previous" for it.
|
|
virtual Track track() { return {}; }
|
|
virtual std::string music(std::string_view) { return unavailable; }
|
|
// The game's own soundtrack on the player's headphones (none while a Spotify station is on:
|
|
// then the outside player's song is the one heard), and "next" for it.
|
|
virtual std::optional<Track> game_track() { return std::nullopt; }
|
|
virtual std::string game_music(std::string_view) { return unavailable; }
|
|
virtual Progress progress() { return {}; }
|
|
virtual std::optional<std::string> setting(std::string_view) { return std::nullopt; }
|
|
virtual std::vector<std::string> setting_names() { return {}; }
|
|
// A game setting held at `value` while the plugin keeps it, then put back as it was found
|
|
// (reskate.settings.set); no value lets go. One plugin holds a setting at a time.
|
|
virtual std::string set_setting(std::string_view /*plugin*/, std::string_view /*name*/, std::optional<std::string> /*value*/) { return unavailable; }
|
|
// ---- camera ------------------------------------------------------------------------------------
|
|
// The view from a pose (rows right, up, back and position, as follow_camera.h builds them) with
|
|
// a vertical field of view in degrees (0 the camera's own). One plugin holds it at a time, and
|
|
// Spectate takes it back while it runs; the game's own camera returns on release.
|
|
virtual std::optional<CameraView> camera_view() { return std::nullopt; }
|
|
virtual std::string set_camera(std::string_view /*plugin*/, const std::array<float, 16>&, float) { return unavailable; }
|
|
// Spectate's camera on a session player (0 stops the one this plugin started).
|
|
virtual std::string follow_player(std::string_view /*plugin*/, std::uint64_t) { return unavailable; }
|
|
virtual void release_camera(std::string_view /*plugin*/) {}
|
|
// "" while the plugin's view is the one shown, otherwise why not.
|
|
virtual std::string camera_status(std::string_view /*plugin*/) { return unavailable; }
|
|
// Holds the skater where it is (noclip, with no input), while any plugin asks.
|
|
virtual std::string freeze(std::string_view /*plugin*/, bool) { return unavailable; }
|
|
// ---- the session's authority (plugins' server copies) --------------------------------------------
|
|
// Drops a player from the session; a chat line to everyone; one to one player (a dedicated
|
|
// server's only: a lobby has no private chat); moves a player's skater (their game does the move).
|
|
virtual std::string kick(std::uint64_t, std::string_view /*why*/) { return unavailable; }
|
|
virtual std::string announce(std::string_view /*plugin*/, std::string_view) { return unavailable; }
|
|
virtual std::string tell(std::string_view /*plugin*/, std::uint64_t, std::string_view) { return unavailable; }
|
|
virtual std::string move_player(std::uint64_t, const Vec3&) { return unavailable; }
|
|
// Bans a player by SteamID64, connected or not (a dedicated server's only); lifts a ban.
|
|
virtual std::string ban(std::uint64_t) { return unavailable; }
|
|
virtual std::string unban(std::uint64_t) { return unavailable; }
|
|
// A player's permissions, true where they may: what the server holds for them now, or nothing
|
|
// for one it cannot hold any for. Applying makes them exactly these.
|
|
struct Permissions {
|
|
bool noclip = true, nobail = true, boosts = true, tuning = true, objects = true, ollies = true;
|
|
};
|
|
virtual std::optional<Permissions> permissions(std::uint64_t) { return std::nullopt; }
|
|
virtual std::string apply_permissions(std::string_view /*plugin*/, std::uint64_t, const Permissions&) { return unavailable; }
|
|
// ---- skater.animation: clips on the local skater's body (Extension/Skater/skater_animation.h) ------
|
|
// One plugin plays at a time; none while a throwdown runs. A replacement plays when the game does
|
|
// `trick` ("kickflip") with that pop ("" any, "low", "mid", "high"); a null clip takes it away.
|
|
using ClipRef = std::shared_ptr<const anim_studio::Clip>;
|
|
// `at`: seconds into the clip it starts from; `paused`: held there (seek and pause move it).
|
|
// The game's own animations (Extension/AnimationStudio/game_clips.h): the names containing `containing`
|
|
// (lower case), at most `most`; and one read as a clip. Empty, and why, while they are still read or
|
|
// when that one is not a clip on the skater's rig.
|
|
virtual std::vector<std::string> game_clip_names(std::string_view /*containing*/, std::size_t /*most*/, std::string& why) {
|
|
why = unavailable;
|
|
return {};
|
|
}
|
|
virtual ClipRef game_clip(std::string_view /*name*/, std::string& why) {
|
|
why = unavailable;
|
|
return nullptr;
|
|
}
|
|
// The stance a clip of the skater stands in at its first frame (true: goofy), from the skeleton the
|
|
// game's clips are read with; empty when it cannot be told, or before the game's animations are read.
|
|
virtual std::optional<bool> clip_goofy(const ClipRef&) { return std::nullopt; }
|
|
// The clip mirrored for the other stance (game_clips.h Library::mirror), or empty and why.
|
|
virtual ClipRef mirror_clip(const ClipRef&, std::string& why) {
|
|
why = unavailable;
|
|
return nullptr;
|
|
}
|
|
virtual std::string play_clip(std::string_view /*plugin*/, ClipRef, float /*speed*/, float /*blend*/, bool /*loop*/, float /*at*/, bool /*paused*/,
|
|
bool /*floor*/) {
|
|
return unavailable;
|
|
}
|
|
virtual void stop_clip(std::string_view /*plugin*/) {}
|
|
virtual void seek_clip(std::string_view /*plugin*/, float /*seconds*/) {}
|
|
virtual void pause_clip(std::string_view /*plugin*/, bool /*paused*/) {}
|
|
virtual std::optional<float> clip_time(std::string_view /*plugin*/) { return std::nullopt; }
|
|
virtual bool clip_playing(std::string_view /*plugin*/) { return false; }
|
|
// An arm of the local skater reaching (Extension/Skater/arm_reach.h): its hand joint to `at` in the
|
|
// world, turned to `rotation` when given, `weight` of the way; held 0.3 s, set every tick. One
|
|
// plugin at a time; none ends that plugin's. Empty when done, else why not.
|
|
struct Reach {
|
|
bool left{};
|
|
Vec3 at{};
|
|
std::optional<std::array<float, 4>> rotation;
|
|
float weight{1};
|
|
};
|
|
virtual std::string skater_reach(std::string_view /*plugin*/, std::optional<Reach>) { return unavailable; }
|
|
// Joints of the local skater turned (in their own frame) and scaled on top of the animation, in
|
|
// the pose drawn and sent (Extension/Skater/skater_animation.h pose); held 0.3 s, so set every
|
|
// tick; empty ends it. One plugin at a time.
|
|
struct JointOffset {
|
|
std::uint16_t joint{};
|
|
std::array<float, 4> rotation{0, 0, 0, 1};
|
|
float scale{1};
|
|
Vec3 offset{}; // added to its position, in its parent's frame
|
|
};
|
|
virtual std::string skater_pose(std::string_view /*plugin*/, const std::vector<JointOffset>&, float /*weight*/) { return unavailable; }
|
|
// Where a hand joint of the local skater is in the world, as last evaluated (after any reach).
|
|
struct HandPlace {
|
|
Vec3 position{};
|
|
std::array<float, 4> rotation{0, 0, 0, 1};
|
|
};
|
|
virtual std::optional<HandPlace> skater_hand(bool /*left*/) { return std::nullopt; }
|
|
// Where a joint (0..394) of a skater is in the world: player 0 the local skater as its pose was
|
|
// last evaluated, else another player's as the session draws it. Asking keeps it read for two
|
|
// seconds, so the first ask comes back empty.
|
|
struct JointPlace {
|
|
Vec3 position{};
|
|
std::array<float, 4> rotation{0, 0, 0, 1};
|
|
float scale{1};
|
|
};
|
|
virtual std::optional<JointPlace> skater_joint(std::uint64_t /*player*/, std::uint16_t /*joint*/) { return std::nullopt; }
|
|
// The physics tuning's values, as the trainer has them (Extension/Trainer/trainer.h): read,
|
|
// found by words, and set over the player's own while the plugin keeps them (never saved).
|
|
struct Tuning {
|
|
std::string id, label, group, kind; // kind: number, integer, flag, multiplier
|
|
double value{}, stock{};
|
|
std::string tuner; // the plugin whose value it is, if any
|
|
};
|
|
virtual std::optional<Tuning> tuning(std::string_view /*id*/, std::string& why) {
|
|
why = unavailable;
|
|
return std::nullopt;
|
|
}
|
|
virtual std::vector<Tuning> find_tuning(std::string_view /*text*/, std::size_t /*most*/) { return {}; }
|
|
virtual std::string tune(std::string_view /*plugin*/, std::string_view /*id*/, std::optional<double> /*value*/) { return unavailable; }
|
|
virtual std::string replace_trick(std::string_view /*plugin*/, std::string /*trick*/, std::string /*pop*/, ClipRef, float /*speed*/) {
|
|
return unavailable;
|
|
}
|
|
// Records the skater's pose for so many seconds; the clip comes from take_recording once done.
|
|
virtual std::string record_clip(std::string_view /*plugin*/, float /*seconds*/) { return unavailable; }
|
|
virtual ClipRef take_recording(std::string_view /*plugin*/) { return nullptr; }
|
|
// The game's name for the trick the skater is doing now ("kickflip"), or empty.
|
|
virtual std::string current_trick() { return {}; }
|
|
// ---- outfit: items worn over the local skater's outfit (Extension/Skater/outfit_overlay.h) ----------
|
|
struct OutfitSlot {
|
|
std::string name, item;
|
|
std::vector<float> parameters;
|
|
};
|
|
virtual std::string outfit_set(std::string_view /*plugin*/, std::string_view /*slot*/, std::string_view /*item*/, std::vector<float>, bool) {
|
|
return unavailable;
|
|
}
|
|
virtual void outfit_clear(std::string_view /*plugin*/, std::string_view /*slot, empty: all*/) {}
|
|
virtual std::string outfit_find(std::string_view) { return {}; }
|
|
virtual std::vector<OutfitSlot> outfit_slots() { return {}; }
|
|
virtual std::string outfit_status() { return unavailable; }
|
|
// What the skater wears now (the selected preset): `slot` the kind of item ("Headwear", "Top", ...,
|
|
// "Shoes", in the order worn from head to foot; clothes and accessories only), or the board's part
|
|
// ("Deck", "Grip tape", "Grip cutout", "Trucks", "Wheels").
|
|
struct WornItem {
|
|
std::string slot, item, title;
|
|
};
|
|
virtual std::vector<WornItem> outfit_worn() { return {}; }
|
|
virtual std::vector<WornItem> board_parts() { return {}; }
|
|
// An item's title as the game shows it (its catalog's, the one an item grid showed, or its key
|
|
// made readable).
|
|
virtual std::string item_title(std::string_view key) { return std::string(key); }
|
|
// ---- board: what is drawn of the local skateboard (Extension/Skater/board_pose.h) ------------------
|
|
struct BoardBone {
|
|
Vec3 position{};
|
|
std::array<float, 4> rotation{0, 0, 0, 1};
|
|
Vec3 scale{1, 1, 1};
|
|
};
|
|
virtual std::optional<std::array<BoardBone, 16>> board_bones() { return std::nullopt; }
|
|
virtual std::string board_set(std::string_view /*plugin*/, int, const BoardBone&, bool /*world*/) { return unavailable; }
|
|
virtual std::string board_hide(std::string_view /*plugin*/, int) { return unavailable; }
|
|
virtual void board_clear(std::string_view /*plugin*/, int /*bone, -1: all*/) {}
|
|
virtual void board_park(std::string_view /*plugin*/, std::vector<int>) {}
|
|
virtual std::string board_toss(std::string_view /*plugin*/, std::vector<int>, const Vec3&, const Vec3&, float) { return unavailable; }
|
|
// How every board is fitted (board_pose.h fit); none: the plugin's fit ends.
|
|
struct BoardFit {
|
|
bool plates{};
|
|
float axle = 1.0f;
|
|
};
|
|
virtual std::string board_fit(std::string_view /*plugin*/, std::optional<BoardFit>) { return unavailable; }
|
|
// ---- variants: of the plugin's own mod (Engine/Vfs/mod_list.h Variants) -----------------------------
|
|
struct VariantState {
|
|
std::vector<std::string> installed; // its "order" first
|
|
std::string chosen; // empty: none was
|
|
std::string in_play; // empty: none of them is in the game
|
|
};
|
|
// Empty when the mod declares no variants.
|
|
virtual std::optional<VariantState> variants(std::string_view /*plugin*/, const std::filesystem::path& /*package*/) { return std::nullopt; }
|
|
// The variant saved as the mod's choice, the mods applied again and the level loaded again with it.
|
|
virtual std::string choose_variant(std::string_view /*plugin*/, const std::filesystem::path& /*package*/, std::string_view /*variant*/) {
|
|
return unavailable;
|
|
}
|
|
// ---- the skater's physics -----------------------------------------------------------------------
|
|
virtual SkaterNews take_skater_news() { return {}; }
|
|
virtual std::optional<Vec3> board_velocity() { return std::nullopt; }
|
|
virtual std::optional<SlamNews> last_slam() { return std::nullopt; }
|
|
// Every physics step since the last tick (kept once asked for: at most 512).
|
|
virtual std::vector<PhysicsStep> take_physics_steps() { return {}; }
|
|
// The skater's x-ray ready while any plugin asks (Extension/Skater/xray.h); d:skeleton draws it.
|
|
virtual std::string xray(std::string_view /*plugin*/, bool) { return unavailable; }
|
|
// skate.'s bottom left HUD hidden while any plugin asks; false keeps the plugin ready to hide it.
|
|
virtual std::string hud_corner(std::string_view /*plugin*/, bool) { return unavailable; }
|
|
// The game run at `speed` (0.05 to 1; 1 drops the ask) while it is the slowest asked; never in a
|
|
// multiplayer session. And the speed it runs at for plugins now.
|
|
virtual std::string game_speed(std::string_view /*plugin*/, float) { return unavailable; }
|
|
virtual float game_speed_now() { return 1; }
|
|
// The next pop out of a grind (Extension/Skater/grind_exit.h); empty: the game's own exit. One
|
|
// plugin at a time. Its pops come back from take_grind_pops.
|
|
virtual std::string grind_exit(std::string_view /*plugin*/, std::optional<GrindExitWish>) { return unavailable; }
|
|
virtual std::vector<GrindPop> take_grind_pops(std::string_view /*plugin*/) { return {}; }
|
|
// Pad controls kept from the game while any plugin asks: the triggers, and the left stick's
|
|
// left/right while the skater grinds.
|
|
// The skater carried up to a speed (m/s, 0 off) for the next 300 ms, eased down to it with may_slow.
|
|
virtual std::string cruise(std::string_view /*plugin*/, float /*speed*/, bool /*may_slow*/) { return unavailable; }
|
|
// The board's velocity set (or `add`ed to) by the next physics step with the skater on its board,
|
|
// within 500 ms.
|
|
virtual std::string set_velocity(std::string_view /*plugin*/, const Vec3& /*velocity*/, bool /*add*/) { return unavailable; }
|
|
// What of the pad the game does not see (reskate.input.hide): the triggers, the left stick's
|
|
// sideways while grinding, the right stick and the stick clicks.
|
|
struct PadHidden {
|
|
bool left_trigger{}, right_trigger{}, left_x_grinding{}, right_stick{}, left_stick_click{}, right_stick_click{};
|
|
};
|
|
virtual std::string hide_pad(std::string_view /*plugin*/, const PadHidden&) { return unavailable; }
|
|
// A camcorder look over the game's picture (reskate.screen.look; Extension/UI/Overlay/screen_look.h
|
|
// says what each is) for the next 300 ms; empty ends it. One plugin at a time.
|
|
struct ScreenLook {
|
|
float bend{}, fov{60}, age{};
|
|
bool bars{}, hood{}, grade{}, soft{}, scanlines{}, grain{}, dropouts{}, head_switching{}, tracking{}, smear{}, iris{};
|
|
double rolling{-1};
|
|
std::array<std::string, 5> text;
|
|
};
|
|
virtual std::string screen_look(std::string_view /*plugin*/, std::optional<ScreenLook>) { return unavailable; }
|
|
// A hand slaps the lens, while this plugin has the look.
|
|
virtual std::string screen_slap(std::string_view /*plugin*/) { return unavailable; }
|
|
// The skater bails as from a slam of `force` (0.5 to 3); not in a throwdown. While No Bail protects
|
|
// the skater the wipeout is dropped, as any other is.
|
|
virtual std::string wipeout(float /*force*/) { return unavailable; }
|
|
// ---- audio: plugins' own sounds, in a level ---------------------------------------------------------
|
|
// Its id, or 0 when the plugin plays 8 already (or there is no level).
|
|
virtual std::uint64_t play_sound(std::string_view /*plugin*/, SoundStart) { return 0; }
|
|
virtual bool change_sound(std::string_view /*plugin*/, std::uint64_t, const SoundChange&) { return false; }
|
|
virtual void stop_sound(std::string_view /*plugin*/, std::uint64_t) {}
|
|
virtual bool sound_playing(std::string_view /*plugin*/, std::uint64_t) { return false; }
|
|
// ---- input -------------------------------------------------------------------------------------
|
|
// The flight keys and mouse only while the plugin's camera is shown; pads while the game has the
|
|
// focus. Capture keeps the game from the controller too, for 250 ms a call (renewed each tick),
|
|
// and only while the plugin's camera is shown.
|
|
virtual std::optional<FlightInput> flight_input(std::string_view /*plugin*/) { return std::nullopt; }
|
|
virtual std::optional<PadInput> gamepad() { return std::nullopt; }
|
|
virtual std::optional<StickInput> joystick() { return std::nullopt; }
|
|
virtual std::string capture_input(std::string_view /*plugin*/, bool) { return unavailable; }
|
|
// A named action on a key or pad combo (controller_bindings.h's encoding); presses come back
|
|
// from take_presses as (plugin, action), only while the game itself has the input.
|
|
virtual std::string bind(std::string_view /*plugin*/, std::string_view, std::uint32_t) { return unavailable; }
|
|
virtual std::vector<std::pair<std::string, std::string>> take_presses() { return {}; }
|
|
// Everything a plugin holds of the game (its HUD hide, nametags, binds), when it stops.
|
|
virtual void release(std::string_view /*plugin*/) {}
|
|
};
|
|
|
|
// "F7", "Ctrl+K", "Shift+Alt+1", "Space"; or a pad combo, "Pad.LB+Pad.RB", "Pad.A", "Pad.Up"...
|
|
// As controller_bindings.h encodes binds; 0 for a name that is not one.
|
|
std::uint32_t combo_of(std::string_view name);
|
|
// A physics state as plugins hear it: "riding", "air", "wipeout", "grinding", "off_board",
|
|
// "planted", "other" or "none".
|
|
const char* skater_state_name(std::uint32_t state);
|
|
}
|