Docs: the throwdown kit for Lua, and Board Break and Road Rash as plugins inside their packs

contrib/throwdown-kit.md is the reference for making a throwdown mode in Lua: registering modes,
the documents (setup page, waiting card, HUD, match panel, flags, match), the throwdown_ui events,
the native flag, and the services a mode uses. Board Break's doc says how the deck is chosen now
(the pack's decks as variants, reskate.package.choose, reskate.board.fit); Road Rash's that it ships
inside its item pack. The plugin list and the README name the throwdown plugins.
This commit is contained in:
AyeZeeBB committed 2026-10-20 01:46:18 -05:00
1 parent e246acd24b
commit 91b754bfb0
5 files changed
+297 -70

No files matched your search

+4 -3
View File
@@ -15,15 +15,16 @@ the launcher, the runtime that loads into the game, and the dedicated server.
- Join dedicated servers from the in-game server browser.
- Proximity voice chat, and text chat with emotes and an optional bad-word filter.
- Parties in lobbies and on dedicated servers: invite, join, leave, promote.
- Throwdowns with other players (Jam, Spot Battle and S.K.A.T.E.), and co-op challenges.
- Throwdowns with other players (Jam, Spot Battle and S.K.A.T.E.), and co-op challenges. Plugins add
throwdown modes of their own to the Throwdowns page ([contrib/throwdown-kit.md](contrib/throwdown-kit.md)).
- **In-game menu and console.**
- Map and fast travel.
- World: time of day, population, district levels, rotating parks.
- The **Park Editor**: place, move and save objects with freecam, snapping and undo. Save a selection as a
set and place it again anywhere, on any map.
- Skater options: first person, movement, boosts, noclip.
- Hall of Meat and grind exit triggers are plugin mods now
([contrib/plugins/hall-of-meat](contrib/plugins/hall-of-meat), [contrib/plugins/grind-exits](contrib/plugins/grind-exits)).
- Hall of Meat, grind exit triggers, Board Break, Road Rash and the FPV drone are plugin mods now
([contrib/plugins](contrib/plugins)). Board Break and Road Rash ship inside their asset mods.
- Progression, controls, graphics and multiplayer settings.
- **Mods.**
- Drop a mod in `Mods/` and it is merged into the game at launch. Mods can add custom maps, loading
+56 -65
View File
@@ -4,8 +4,8 @@ A hard slam can break the local skater's board: at the nose, in the middle or at
time the grip tape holds the pieces together and the board folds or an end hangs; otherwise a piece
comes off, flies and lies where it lands, truck and wheels with it. The board is whole again once
the skater has picked it up, rides again or respawns. A plugin, [board-break](plugins/board-break)
(it was built into ReSkate until 2026-10); the broken pieces themselves come from a content mod
with the deck shapes in it.
(it was built into ReSkate until 2026-10), that ships inside the Board Break deck pack: the broken
pieces themselves are the pack's deck models.
Boards also wear: every hard landing, rail hit, grind and slam takes something out of the board of
the deck in the game, and a worn board steers lazier, wobbles, breaks from less and in the end can
@@ -13,14 +13,12 @@ give way under the rider. A boardslide or darkslide that comes down too hard ont
the board and throw the rider. A break cracks (and sounds like metal when it happened on metal),
and the body's hits in a bail are heard where and as hard as they land. The player sets all of it.
Code: the plugin's Lua in [plugins/board-break/plugin](plugins/board-break/plugin) (the break,
wear, rail snaps, sounds, its console command and menu), drawn through reskate.board
([board_pose.cpp](../Extension/Skater/board_pose.cpp)). What every board needs while a deck pack
is in the game stays in core whichever plugins run:
[breakable_deck.cpp](../Extension/Skater/breakable_deck.cpp) (which deck, its name and wheel
spacing, the `boarddeck` command), board_pose.cpp (the deck's stretches kept out of sight, the
baseplates on the deck, the wheels at the deck's spacing, on every board), and the deck's place in
the mod list in [mod_catalog.cpp](../Engine/Vfs/mod_catalog.cpp).
Code: the plugin's Lua in [plugins/board-break/plugin](plugins/board-break/plugin): the break,
wear, rail snaps, sounds, the deck, its console command and its page. It draws through
`reskate.board` (set, hide, park and toss the board's bones; `fit` every board for the deck in
the game), and picks the deck with `reskate.package.variants` and `reskate.package.choose`: the
pack's decks are variants of its mod, which its `reskate-plugin.json` declares. Nothing in ReSkate
knows Board Break.
## What the player gets
@@ -28,10 +26,9 @@ the mod list in [mod_catalog.cpp](../Engine/Vfs/mod_catalog.cpp).
in between (the force is the bail cause's that led to the wipeout). Now and then a board gives way
while it is ridden: the nose goes.
- `boardbreak 0|1`, `boardbreak snap [nose|mid|tail]` to look at a break (that one stays until
`boardbreak mend` or a respawn), from the plugin; its settings are on its page in the overlay's PLUGINS
section. `boarddeck [name|number|next]` (core) and the overlay's BREAKABLE DECK card on the
Mods page pick the deck: a press asks for that deck, the mods are applied and the level loads
again with it.
`boardbreak mend` or a respawn). Its settings are on its page in the overlay's PLUGINS section.
`boardbreak deck [name|number|next]` and the DECK card on that page pick the deck: a press asks
for that deck, the mods are applied and the level loads again with it.
- A break plays back in the replay editor as it happened.
- **Wear and tear.** Each deck's board wears from 0% (new) to 100% (worn out): a landing coming down
faster than 3.5 m/s, a hit onto a rail or ledge (harder on a slide than on the trucks), every second
@@ -46,14 +43,12 @@ the mod list in [mod_catalog.cpp](../Engine/Vfs/mod_catalog.cpp).
rider goes down with it. Grinds on the trucks never do.
- **Sounds.** A crack by the size of the break, a metal one on top when the board broke on metal,
and the body's hits in a bail, louder the harder they land.
- **Settings**, on the overlay's Skater page in a card BOARD BREAK SETTINGS (with how worn the board
is): always break (for replay clips), rail snaps, wear and tear, break sounds, body hit sounds,
break sensitivity (10% to 400%), how many breaks the grip tape holds (0% to 100%), wear speed (0%
to 500%), and New board. Always break and wear and tear are also rows in the game's Mod Options.
The console has them all: `boardbreak always|sensitivity|joined|rails|wear|wearrate|cracks|bodysounds
[value]`, `boardbreak worn [percent]`, `boardbreak status`, and for metal `boardbreak metal
mark|unmark|clear|list` and `boardbreak materials`. They are kept, with each deck's wear and the
materials marked as metal, in `%LOCALAPPDATA%\ReSkate\BoardBreak.settings`.
- **Settings**, on the plugin's page in the overlay's PLUGINS section (with how worn the board is):
always break (for replay clips), rail snaps, wear and tear, break sounds, body hit sounds, break
sensitivity, how many breaks the grip tape holds, wear speed, and New board. The console has them
all: `boardbreak always|sensitivity|joined|rails|wear|wearrate|cracks|bodysounds [value]`,
`boardbreak worn [percent]`, `boardbreak status`. They are kept, with the board's wear, in the
plugin's storage.
- Without a deck pack installed nothing breaks and nothing is changed.
## How it works
@@ -82,18 +77,15 @@ the mod list in [mod_catalog.cpp](../Engine/Vfs/mod_catalog.cpp).
that passes the hook (the local one, other players'), the stretches of a whole board are moved
away like a broken board's deck. The call into Board Break is the last thing the hook does, after
another player's pose has been written, so that pose's bones do not bring them back.
- **Which deck is in the game** is a question for the mod list, because any mod that reshapes the
deck replaces the same mesh and the one higher in the order has it. When the mods are put
together, the Board Break deck the player chose goes first, and what would fight it is set
aside for the run: the other Board Break decks, and other mods whose bundle lists name the
deck's bundle. Those are left out whole (with the breakable deck in place of its own, a wide-deck
mod has its trucks and wheels on a deck they were not made for). mods.json is not changed; the
mod list shows them as SET ASIDE.
- **Another deck without a restart.** The deck mesh is in the items superbundle, which a live apply
of the mods already refreshes, and the game reads the mesh when a level loads. Choosing a deck
saves the choice, applies the mods and loads the level again (live_mods.h).
- **The choice** is a line in `%LOCALAPPDATA%\ReSkate\BoardBreak.deck`, not a profile preference:
the mods are put together before the profile is there.
- **Which deck is in the game.** Any mod that reshapes the deck replaces the same mesh, so the
pack's decks are variants of its mod: one is in the game at a time, the one the player chose
(else the first of the manifest's `order`). The manifest names the deck's bundle in `displaces`,
so any other mod that brings a deck model is set aside while a deck is in the game (with the
breakable deck in place of its own, a wide-deck mod has its trucks and wheels on a deck they
were not made for). The mod list shows those as SET ASIDE.
- **Another deck without a restart.** `reskate.package.choose(deck)` keeps the choice, applies the
mods and loads the level again: the deck mesh is in the items superbundle, which a live apply
refreshes, and the game reads it when a level loads.
- **The sound** is a wave file from the `sounds` folder of an installed deck pack (subfolders
`light`, `medium`, `heavy`, by the size of the break), played straight to the speakers.
- **The slam's force** comes through No Bail's existing hooks: the cause it records for the watched
@@ -139,35 +131,35 @@ the mod list in [mod_catalog.cpp](../Engine/Vfs/mod_catalog.cpp).
- **The sounds** play through XAudio2 on a thread of their own, several at once and each at its own
loudness; 16-bit and 32-bit float wave files both play.
- **Wider decks, wider trucks.** The game puts each wheel on its truck's bone along the axle, about
0.0876 m out. A deck that says so (deck.ini `axle`) has its wheels set further apart, or closer,
by the render pose hook: set, not scaled, so a pose that comes by twice is not moved twice, and
only what is drawn changes. The deck's own truck model is made to match.
- Off, the board holds. Without a deck pack in the game the hook returns at once.
0.0876 m out. A deck that says so (deck.ini `axle`) has its wheels set further apart, or closer:
the plugin reads it and asks `reskate.board.fit{plates = true, axle = ...}`, which sets them in
the render pose hook (set, not scaled, so a pose that comes by twice is not moved twice, and only
what is drawn changes) and holds the baseplates on the deck, on every board. The deck's own truck
model is made to match.
- Off, the board holds. With no deck in the game the plugin fits nothing.
## The deck packs
## The deck pack
A deck is a content mod's copy of the deck model (no code), and comes in one of two ways:
A deck is a copy of the deck model (no code). The Board Break deck pack holds a deck in each
`decks/<name>/`, laid out like a mod folder (`layout.toc`, `Win32/...`), and the plugin beside
them (`reskate-plugin.json`, `plugin/`). The plugin's manifest declares the decks:
- a mod folder named `Board_Break_<Name>` holds the deck `<name>`;
- a deck pack, a mod folder of any name, holds a deck in each `decks/<name>/`, laid out like a mod
folder (`layout.toc`, `Win32/...`). The launcher installs one mod folder per package, so this is
how one package brings every deck. When the mods are put together each deck inside becomes a mod
of its own for the run, listed as `<folder>#<name>`: it is chosen, set aside and switched like a
deck in a folder of its own (its archives are placed like a disabled mod's). mods.json and the
mod list only know the pack, which counts as loaded when one of its decks is. The launcher takes
a folder for a mod only when it has a `layout.toc` of its own. That layout is not one of the
pack's decks, whatever the folder is named, and is set aside like any other deck model; it is
what an older ReSkate loads from the pack. ReSkate 2.0.6 knows a deck by its folder's name only,
so a pack in a folder named `Board_Break_<Name>` (the package `Board_Break_Pack`, installed as
`Team-Board_Break_Pack`) with a breakable deck as its own layout breaks boards there with that
one deck, and brings all its decks from this version on. The truck model with the baseplates on
the effectors belongs in the decks inside the pack only: a ReSkate that does not place those
bones would draw the baseplates turning with the hangers, and such a version reads the pack's
own layout, never `decks/`.
```json
"variants": {
"folder": "decks",
"order": ["standard", "wide", "concave", "powell", "slim", "egg", "square", "shaped", "oldschool", "pig"],
"displaces": ["deck_gen_popsicle_cas_main_bundlereftable"]
}
```
The name is what the deck goes by. The ten the feature was made with are standard, wide,
concave, powell, slim, egg, square, shaped, oldschool and pig, and the console also knows those by
number. Any other deck that follows the recipe works, with no change to ReSkate:
The pack's own `layout.toc` is set aside while one of its decks is in the game; it is what an older
ReSkate loads from the pack. The truck model with the baseplates on the effectors belongs in the
decks inside the pack only: a ReSkate that does not place those bones would draw the baseplates
turning with the hangers, and such a version reads the pack's own layout, never `decks/`.
The name is what the deck goes by (its folder's name: letters, digits, `_` and `-`). The ten the
feature was made with are in the manifest's `order`, which is also how the console numbers them.
Any other deck folder that follows the recipe works, with no change to ReSkate or the plugin:
- Start from the deck mesh as ReSkate Studio exports it (three parts on the bone Deck: the grip
sheet, the wood, and a plain copy of the whole deck). Studio only takes back a mesh whose
@@ -186,10 +178,9 @@ number. Any other deck that follows the recipe works, with no change to ReSkate:
- The sounds are `sounds/light|medium|heavy|metal|body/*.wav` in the folder of any mod that brings
a deck (a crack size without sounds of its own takes the nearest that has some), and
`sounds/metal/materials.txt` may list the level's materials that are metal, by number.
- A deck may say something of itself in a `deck.ini` in its folder (a pack's `decks/<name>/`, or the
`Board_Break_<Name>` folder): `label = Old School`, its name in the menus, and `axle = 1.17`, how
far apart its wheels sit against a standard board's (0.8 to 1.6), for a deck wider or narrower
than the standard one.
- A deck may say something of itself in a `deck.ini` in its folder (`decks/<name>/deck.ini`):
`label = Old School`, its name in the menus, and `axle = 1.17`, how far apart its wheels sit
against a standard board's (0.8 to 1.6), for a deck wider or narrower than the standard one.
## Seen working in game
@@ -232,8 +223,8 @@ too fast (the rates are a third of that now).
- The online route: all of the above ran on the offline profile.
- The replay editor on this branch (the replay was checked on the older build the feature was
developed on).
- The deck card with a deck that is not one of the eight, and with none installed (it draws
nothing then).
- The plugin's DECK card and `boardbreak deck` (2026-10-20, built and tested without the game):
choosing a deck, a deck that is not one of the ten, and a pack with no decks.
- A board that does not pass the render pose hook, with the truck model from a pack: its
baseplates would follow the hangers.
- A mod that brings a truck model of its own beside a deck pack: the pack's is the one that loads.
+11
View File
@@ -33,3 +33,14 @@ test`) that `dingosdk_plugin_tests` runs too, and its own page in the ReSkate me
pictures (`d:game_image`). `hallofmeat 0|1`.
- **grind-exits**: LT / RT in a grind pick the side of the pop (`reskate.skater.grind_exit`,
`reskate.input.hide`). `grindexit status`.
## Throwdown modes
Throwdown modes made in Lua with the throwdown kit ([throwdown-kit.md](../throwdown-kit.md)): the
plugin registers its modes and publishes what the player should see; ReSkate shows it with skate.'s
native Throwdowns page, setup page, flag, HUD and match panel.
- **throwdown-modes-test**: two modes through the whole native flow (card, setup page, flag,
waiting card) and no match. The smallest example of the kit.
- **SmokeyDawg58-ReSkate_Throwdown**: SmokeyDawg58's ten modes, with their rules, HUDs, gates and
route editor, all in Lua, inside his asset mod. `tdm help`.
+3 -2
View File
@@ -2,8 +2,9 @@
Bails leave marks on the local skater that build up over a session: road dust and a graze first,
then scrapes and bruises, then raw wounds, a bruised face and (if the player leaves it on) blood.
A plugin, [road-rash](plugins/road-rash); the pictures themselves come from a content mod. It was
built into ReSkate until 2026-10; what follows is how that worked, and how the plugin still works.
A plugin, [road-rash](plugins/road-rash), that ships inside the Road Rash item pack: the pictures
themselves are the pack's skin items. It was built into ReSkate until 2026-10; what follows is how
that worked, and how the plugin still works.
Code: the plugin's Lua in [plugins/road-rash/plugin](plugins/road-rash/plugin), and the core
service it wears its marks through, reskate.outfit:
+223
View File
@@ -0,0 +1,223 @@
# The throwdown kit: throwdown modes in Lua
A throwdown mode is a plugin. ReSkate knows no particular mode: a plugin registers its modes,
publishes documents that say what the player should see, and hears what the player does as
events. ReSkate shows the documents with skate.'s own native widgets: the Throwdowns page, the
setup page, the start flag, the waiting card, the in-game HUD and the match panel. A new mode needs
no change to ReSkate.
Two plugins use it:
- [throwdown-modes-test](plugins/throwdown-modes-test): two modes through the whole native flow
(card, setup page, flag, waiting card) and no match. The smallest example.
- [SmokeyDawg58-ReSkate_Throwdown](plugins/SmokeyDawg58-ReSkate_Throwdown): SmokeyDawg58's ten
modes (Deathrace, 1-UP, Spot Jam and the rest), with their rules, HUDs and route editor, all in
Lua. The full example.
Everything below needs the `throwdowns` permission in `reskate-plugin.json`. A plugin's documents
only drive its own modes, and its events only reach it.
## 1. Registering a mode
```lua
reskate.throwdowns.mode{
id = "duel", -- unique in the plugin, 1 to 32 characters, no "/"
name = "Duel",
description = "Beat the last total. Three misses and you're out.",
art = "UI/MyMod/Throwdowns/img_duel", -- the card's logo: a texture or image asset path
color = 0xff3434ff, -- the card's tint, 0xAABBGGRR (optional)
card = "spot", -- the stock card it is drawn like: "skate", "spot" or "jam"
replaces = "skate", -- optional: takes that stock card's place on the page
order = 10, -- its place among the plugins' modes
flag = "World/MyMod/Flags/duel_Placement", -- optional: the flag's placement preview prefab
cursor_height = 4, -- optional: metres the placement cursor may rise onto things
players = {min = 1, max = 6, default = 2},
}
```
Calling it again with the same id updates the mode; `reskate.throwdowns.remove_mode(id)` takes it
off the page. The page shows the stock S.K.A.T.E. (unless a mode replaces it), Spot Battle and
Skate Jam, then every plugin mode by `order`. Clicking a card sends the `open` event.
## 2. Documents
```lua
reskate.throwdowns.ui{setup = ..., waiting = ..., hud = ..., panel = ..., flags = ..., match = ...}
```
Publish whenever something changes; every tick is fine, since an unchanged document costs only a
comparison. Any part left out shows nothing. In documents, `mode` is the plugin's own id ("duel").
Colours are 0xAABBGGRR, times are milliseconds, positions are vectors in world metres, and Steam
IDs are strings. Texts may also be numbers.
### setup: the setup page
The stock Spot Battle setup page, relabelled. It shows while the player is on it after `open`.
```lua
setup = {
mode = "duel", title = "DUEL", art = "UI/...", description = "...", back = "BACK",
players = {caption = "Players", min = 1, max = 6, step = 1, value = 2},
timer = {caption = "Turn", min = 10, max = 120, step = 10, value = 20}, -- nil: no timer row
rows = { -- at most 12
{id = "laps", kind = "choice", caption = "Laps", options = {"1", "2", "3"}, value = 1}, -- value from 1
{id = "boost", kind = "slider", caption = "Boost", min = 0, max = 5, step = 1, value = 2},
{id = "route", kind = "button", caption = "Route", label = "Place gates"},
{id = "note", kind = "info", caption = "Area", label = "Anywhere"},
},
hide_native = {"rounds"}, -- stock rows taken off (the default)
}
```
Moving a slider or a choice sends `setup_row`; pressing a button row sends `setup_button`. Confirm
places the start flag (section 4).
### waiting: the waiting card
Shown while the flag is down and the match has not started.
```lua
waiting = {mode = "duel", title = "DUEL", art = "UI/...", status = "Enrolled skaters | Host starts",
enrolled = 1, capacity = 6, can_start = true,
countdown = {remaining_ms = 5000, total_ms = 10000}} -- optional
```
Start sends `start`, Leave sends `leave`.
### hud: the in-game HUD
Slots of the native HUD, drawn with the widget family `theme` names (`HUD_<theme>_Title_..._Widget`
and so on; a mod brings its own in its assets). Every slot is optional.
```lua
hud = {
active = true, theme = "race",
header = {title, art, status, offset = {x, y}, icon_size = 96, frame_width = 704}, -- 0: the theme's
layout = "side", -- where the board sits: "side" (right edge) or "bottom"
counters = {"LEADER 1,200", "SCORE 900"}, score_width = 480, score_art = "",
clock = Clock, -- the match clock
board = {title = "LEADERBOARD", columns = {"POS", "NAME", "SCORE"},
variant = "", -- "stamps": a stamp per mark instead of joined letters
style = "outlined", -- or "plain"
heading = "list", -- or "shared"
name_art = "", out_art = "", rank_art = "", value_art = "", value_width = 0, reserve = "",
rows = {BoardRow, ...}},
results = {title, notice, caption, heading_art = "", title_width = 0, style = "outlined", rows = {BoardRow, ...}},
intro = {title, art_key, rules, until_ms},
countdown = Clock, -- 3-2-1-GO
banner = {title, subtitle, art, serial, ms}, -- a new serial shows it again
callout = {text, style, serial, ms},
warning = {text, art, serial, ms},
card = {caption, title, image}, -- a trick card; image is a texture path
list = {caption, items = {"KICKFLIP", ...}},
position = {place = 1, medal = "gold", art = ""}, -- "gold" | "silver" | "bronze" | "white"
timers = {{caption, text = "0:12.40" or Clock}, ...}, timer_art = "",
compass = {at = vector, art = "UI/..."},
markers = {{at = vector, text = "GATE 3", distance = true}, ...}, -- distance from the skater
blackout = {text},
editor = {title, variant = "builder", -- or "compact"
prompts = {{inputs = {"UI_DpadLeft"}, caption = "Move", glyph = "", hold = 0.5}, ...},
info = {title, rows = {{caption, value}, ...}},
toast = {text, alpha},
modal = {text, actions = {{input, caption}, ...}}},
}
-- BoardRow: {rank, name, value, color, self = bool, out = bool,
-- marks = {{text = "S", on = true}, ...}, clock = Clock}
-- Clock: {remaining_ms, total_ms, sampled_ms, ticking = true, up = false}, or a stopwatch
-- {base_ms, sampled_ms, running, cap_ms}. The HUD ticks a clock itself between documents;
-- sampled_ms is reskate.time.now() when the numbers were true.
```
### panel: the match panel
The native details page the pause menu opens during a match.
```lua
panel = {title, description, art, score_caption = "SCORE", quit = "Quit",
settings = {{icon = "players", caption = "Players", value = "4"}, ...}, -- at most 3;
-- icon "players" | "timer" | "rounds" | a texture
prompts = {{input = "UI_ShoulderL", caption = "Spectate", command = "spectate"}, ...},
players = {{id = "7656...", name, value, rank, outcome, portrait = "steam", self = true}, ...}}
```
Opening it sends `panel_open`; a prompt's button sends `panel_command` with its command.
### flags: flags for other players
```lua
flags = {{id = "f1", mode = "duel", leader = "7656...", game = 123, at = vector, yaw = 0,
players = 1, capacity = 6, open = true,
stats = {{title = "LAPS", value = "3"}, ...}}} -- at most 3
```
Each shows on other players' games as a native flag (map point, bubble, map card, Enroll card).
Their Join reaches this plugin on their game as `join`.
### match: what a running match asks of the game
```lua
match = {markers = true, -- session markers and Return are blocked; the pause menu's Quit is the plugin's
input_lock = true, -- no skating input (a countdown)
offboard = true, -- the skater is kept off the board
hidden = {"7656..."},-- players whose skater, nametag, map dot and chat bubble are hidden
teleports = true, -- the plugin may teleport the skater even where noclip is off
in_activity = true} -- the game treats the player as in an activity
```
## 3. Events
```lua
reskate.on("throwdown_ui", function(e) -- e = {action, mode, args}: args are strings
end)
```
| action | args |
| --- | --- |
| `open` | game: a card was clicked, and the flag registration began (section 4) |
| `setup_row` | row id ("players" and "timer" for those), value (a choice's from 0) |
| `setup_button` | row id |
| `setup_back` | game |
| `setup_confirm` | players, timer seconds |
| `flag_placed` | game, x, y, z, yaw |
| `flag_left` | game |
| `start` | game |
| `leave` | game |
| `quit` | (the pause menu's or the panel's Quit) |
| `join` | flag id, leader, game |
| `panel_open` | |
| `panel_command` | command |
| `dialog` | dialog id, button, text, row, file text (section 5) |
## 4. The native flag
One native flag registration exists at a time. Clicking a mode's card begins it at once, with the
mode's players and the setup page, and then sends `open {game}`. Confirm on the setup page goes on
to native flag placement (`setup_confirm`, then `flag_placed`), then to the waiting card, until the
plugin starts the match or the owner leaves.
```lua
reskate.throwdowns.flag.begin(mode, game, {players, seconds, setup_page = true, cursor_height})
-- false while another registration is busy; setup_page = false goes straight to placement
reskate.throwdowns.flag.state()
-- {owned, waiting, placing, busy, mine, mode, game}: mode and game only when it is one of the plugin's
reskate.throwdowns.flag.leave() -- refused, finished or cancelled: the registration goes
```
## 5. Services a mode uses
None of these knows a throwdown mode; any plugin may use them under its permission.
| Call | Permission | What it does |
| --- | --- | --- |
| `reskate.files.list/read/write/remove(path)` | files | the plugin's own data folder, for saved routes and the like: 1 MB a file, 1000 files, 64 calls a second |
| `reskate.ui.dialog(id, {title, message, field, list, buttons, file})`, `reskate.ui.close_dialog(id)` | ui.screen and throwdowns | an overlay dialog: a text field, a list with buttons on each row, up to 4 buttons, a file to open; answers come as `dialog` events, Esc answers "cancel" |
| `reskate.skater.push(velocity)`, `push_wait()`, `push_reset()` | skater.control | the skater's velocity once it is back on its board |
| `reskate.skater.hold(on)` | skater.control | the skater held where it is |
| `reskate.physics.competition(on)`, `stock_ready()`, `prepare()` | skater.control | the game's stock physics while the player competes |
| `reskate.skater.telemetry()`, `reskate.skater.goofy()` | skater.read | speed, air time, the last jump, the physics state |
| `reskate.skater.bone_cam(word, ...)` | camera | the Bone Cam |
| `reskate.camera.free(...)`, `reskate.camera.pad_flight(on)` | camera | the free camera, flown from the pad while placing things from the air |
| `reskate.session.world()`, `reskate.session.spectate(...)` | session.read | the session's world id and map; spectating a player |
| `reskate.tricks.feed(on)`, `reskate.tricks.gesture(name)` | events.tricks | skate.'s trick list as `trick_notes` events; a flip trick's flick diagram |
| `reskate.input.pad_bits/key/wheel/game_buttons/allowed/focus` | input | the pad, keys and wheel, and game buttons kept from the game |
| `reskate.entities.set(id, blueprint, transform, hidden?)`, `remove`, `clear`, `ready`, `status` | world.entities | world objects of the plugin's own (gates, arrows, flags) |