# Collectible tokens across levels (collect.tokens) ## What it is for Things to pick up across the levels, described as data: kinds of tokens (many common ones, a few rare ones) with a value, a magnet that pulls the common ones in, a respawn rule (back every run, kept for good, or back after a delay), placements per level (single or in rows), a wallet per kind, a per-level tally ("12 / 100", best kept) and thresholds that run actions once. The level is read from a state variable: only its tokens are in the world. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "collect.tokens" } gripforge_gamekit_configure { project_id: "gkp_…", id: "collect.tokens", config: { "levelVar": "course.id", "magnetRadius": 3 } } ``` Only `game.engine` is required. The three collections are documents of your game: ```json { "token_kinds": [ { "id": "coin", "name": "Coin" }, { "id": "gem", "rarity": "rare", "value": 5 } ], "token_placements": [ { "id": "row", "kind": "coin", "level": "one", "at": [2, 0, 0], "count": 2, "step": [2, 0, 0] } ], "token_thresholds": [ { "id": "two", "kind": "coin", "count": 2, "actions": [ { "type": "give_tokens", "kind": "gem", "amount": 1 } ] } ] } ``` ## First example With `levelVar` set to `stage`, nothing is in the world until `stage` becomes `one`; then the row lays out `row.1`, `row.2` and the gem `g`. A coin inside the magnet radius is pulled to the player and collected; the wallet goes up and `tokens.collected` fires with the level tally. ```text collect.tokens.live() -> [row.1, row.2, g] (a few ticks) -> tokens.collected { id: "row.1", kind: "coin", value: 1, count: 1, level: "one", got: 1, total: 2 } collect.tokens.collect("row.2") -> true (the threshold "two" is reached and gives a gem) ``` ## Settings and defaults | Setting | Default | Meaning | |---|---|---| | `levelVar` | `course.id` | state variable naming the current level; placements of another level are not in the world | | `restartTopic` | `course.started` | event that (re)starts the level: tokens that respawn per level come back | | `magnetRadius` | 3 | magnet tokens closer than this to the player are pulled in, metres | | `magnetSpeed` | 10 | speed of a pulled token, m/s | | `pickRadius` | 0.9 | a token this close to the player is picked up, metres | | `pickOffset` | 0.65 | height of the pickup point above the feet, metres | Token kind fields: `id`, `name`, `rarity` (`common`: magnet, back every run; `rare`: no magnet, kept for good), `value` (default 1), `magnet`, `respawn` (`level`, `never`, `timed` with `respawnSeconds`), `color`. A threshold has `kind`, `count`, `scope` (`total` or `level`) and up to 24 `actions` run once. ## Services, events, variables - `collect.tokens` (service): `kinds()`, `count(kind)`, `add(kind, amount)`, `spend(kind, amount)`, `level()`, `tally(level?)`, `collect(id)`, `live()`, `reached(threshold)`. - Events: `tokens.collected`, `tokens.threshold` (once per game), `tokens.respawned`; it listens to `course.started`. - Variables: `tokens.current` (the current level); the wallets are the variables tokens., the tallies tokens.level. and tokens.levelTotal.. - Content verbs: `give_tokens` (action; a negative amount takes, never below 0) and `tokens_at_least` (condition, with `scope`). ## Hooks and modding | Hook | Mutable | Asked | |---|---|---| | `tokens.collect` | `value` | before a token is picked up; cancelling leaves it in the world, asked again while the player stays on it | | `tokens.reach` | - | before a threshold is reached; cancelling asks again at the next change of the wallet or tally | Moddable: `magnetRadius`, `magnetSpeed`, `pickRadius`, `pickOffset` and the collections `token_kinds`, `token_placements`, `token_thresholds`. ## Limits Placements of an unknown kind are ignored. The counters HUD is drawn by the kit on the web; other engines read the variables. Runs on the web and in the native player. ## Versions Kit 1.1.0 (hooks and moddable collections); service `collect.tokens` 1.0.0.