@gitcade/library
v1.10.1
Published
GitCade Component Library — game-agnostic, param-driven behaviors and systems for the GitCade SDK. The logic half of the marketplace (Phase 2A); entities/art/audio/UI/FX arrive in Phase 2B.
Downloads
1,575
Maintainers
Readme
@gitcade/library
The GitCade Component Library — the logic half (Phase 2A): game-agnostic,
param-driven behaviors and systems built on the frozen
@gitcade/sdk. Phase 2B adds the presentational half (entities, art,
audio, UI, FX) and extends the same CATALOG.json.
Nothing here changes the SDK schema. Every part is a plain BehaviorFn /
SystemFn registered as a new type through the SDK's registration API
(registry.registerBehavior / registry.registerSystem). Games reference parts
by type in their scene/entity JSON and pin a libraryVersion in game.json.
Install & register
import { createGame } from "@gitcade/sdk";
import { createLibraryRegistry } from "@gitcade/library";
// SDK built-ins + every library part, on a fresh per-game registry:
const registry = createLibraryRegistry();
const game = createGame({ manifest, config, scenes }, { canvas, registry });
game.start();registerLibrary(registry) adds the library onto an existing registry (e.g. one
that already has a game's custom-behaviors/).
What's inside (27 parts, all MIT)
Most parts are v1.0.0; a few have grown additive v1.1.0 revisions (wave-spawner,
follow-path, ai-aim-and-fire). Each part carries its own semver in CATALOG.json.
Behaviors
- movement —
move-4dir,move-platformer,move-topdown-360,move-grid-step,auto-scroll,follow-path,scale-by-state(0.2.1) - platformer collision — now the SDK's collision-resolution PHASE (
World.resolveBodies()over acollidercomponent: solid push-out, slopes, carry, two-body push), not library behaviors (the oldtilemap-collide/solid-collide/ride-platformwere retired into it) - combat —
shoot,melee-swing,contact-damage,health-and-death - ai —
ai-chase,ai-flee,ai-patrol,ai-wander,ai-aim-and-fire - interaction —
collect-on-touch,trigger-zone,portal
Systems
- progression —
score(storage-persisted high score),lives-respawn,timer-countdown,level-progression - spawning —
wave-spawner(v1.1.0: optionalplacement: "free-cell"scatter + level-drivendensityPerLevel/intervalPerLevelramp),place-on-free-cell(0.2.0) - rules —
win-lose-conditions - economy —
simple-inventory,currency,upgrade-tree,transaction(0.2.0) - persistence —
persistence(0.2.0) — declarative cross-run save/load
Each part ships: an implementation (src/), a JSON definition + metadata
(parts/), and a unit test (test/).
New in 0.2.0
Built on the SDK 0.2.0 primitives (additive — existing games bump
libraryVersion to opt in):
transaction(system) — generic afford → deduct → emit, backed by the SDKworld.canAfford/world.spendassist. The buy-and-place economycurrencyandupgrade-treedon't cover.persistence(system) — round-tripsmanifest.persist.keysthrough theworld.storagebridge: restores on boot (live value wins), saves on change/interval. No host JS, no wire-protocol change.place-on-free-cell(system) — on a trigger event, spawns a prototype at a verified-free grid cell (world.rng-deterministic, tilemap-aware). Replaces hand-rolled free-cell food/pickup placement.wave-spawnerplacement: "free-cell"— optional scatter across free grid cells; default"literal"is the exact 0.1.x behavior. (v1.1.0) optionaldensityPerLevel/intervalPerLevelscale wave size + cadence byworld.state.level— the spawn-pressure half of difficulty (the speed half isscale-by-state).tap-emit(ui) — emits a game event when an entity is clicked (reads the SDK click edge + topmost pick), so a button becomes a pure-datascene.flow.onedge: title → play → game-over with no host code.
New in 0.2.1
Small additive engine-cleanup wave (existing 0.2.0 games keep building and behave
identically; see audit/SDK-0.2.0-BUILD-NOTES.md §0.2.1):
scale-by-state(behavior) — ramp a live entity field (velocity or an entity-state value like hp) by1 + perLevel*(level-1)read from aworld.statelevel counter.set/multiply/oncemodes generalize the hand-rolled difficulty ramps two games shipped (auto-scroll speed, per-enemy speed/hp).snapToGrid/randomFreeCellare now re-exported from the package index (import { snapToGrid } from "@gitcade/library") — previously internal, so games inlined the grid-snap formula.place-on-free-cell/randomFreeCellgainexcludeTags(andrandomFreeCellanexcludeCells) — block extra cells beyondoccupiedTag(e.g. a marker at a soon-to-be-occupied cell), partplace-on-free-cellbumped to v1.1.0.- Engine-side (SDK 0.2.1): the persistence-vs-seeding race is fixed so a persisted,
system-seeded key (e.g.
currencycoins) restores authoritatively on boot with no per-game scene-flow workaround.persistencenow claims its keys and seed systems defer to the pending restore.
The composition contract (inherited from the SDK)
- All balance lives in
config.json. A part receives a numeric balance value only as a"$cfg.<key>"reference, resolved by the SDK before the function runs. The validator FAILS any non-structural numeric literal in params. Part default definitions inparts/*.jsonfollow this rule (the catalog test enforces it). - Movement parts SET velocity; order an SDK
velocitybehavior AFTER them so it integrates position (the same composition Pong uses).move-grid-stepand the platformer floor-clamp write position directly. - Stateful systems namespace their scratch under a
stateKeyparam onworld.state, so multiple instances coexist. - Persistence goes through
world.storage(the SDK postMessage bridge), never rawlocalStorage.
The reuse proof
proofs/ contains four distinct mini-genres built from the same four parts —
ai-chase + contact-damage + wave-spawner + health-and-death — proving the
parts are genuinely reusable. See proofs/README.md.
CATALOG.json
CATALOG.json is the machine-readable index Phase 6 ingests. It is generated
from parts/*.json by scripts/build-catalog.mjs (npm run catalog) and
validated against catalog.schema.json. Do not hand-edit it — edit the per-part
files and regenerate.
Scripts
npm run build # tsup → dual ESM/CJS + d.ts
npm run catalog # regenerate CATALOG.json from parts/
npm test # vitest — one unit test per part + the catalog test
npm run typecheck # tsc --noEmit