Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2303,6 +2303,35 @@ loadable play content. Everything a user does must be visible to connected peers
(`scripts/level-templates.cjs` via author-templates' `kit` type, `seed: false`); a scene load
now ends a running simulation first. Suites pack-refs, pack-undo, level-templates (`LIVE=1`
runs it against a build's own feed + pack CDN).
- **ROADMAP 31 — TOWERS BECOMES A GAME** (31-towers; T1/T2 of the Quest feedback, "after the basic
level nothing happens, just like hooray"). Two core rules any game can use, and a core MODULE.
· `playReach.js` (pure leaf, vitest): `play.reach` (metres, OMITTED when absent) limits Interact /
Play grabs to that distance from the player's BODY — the segment feet..eye, never the eye (a crate
at your feet is 1.7 m from the eye). Desktop Play: `playInteractState.mode = 'toofar'` (the
crosshair says "Too far — get closer"), the press carries nothing, a carry is held inside the
reach (`carryLimit`); the editor's Interact cursor carry has no body and is unaffected. VR:
`gripTargetOf` refuses an out-of-reach body (`lastGripRefusalDebug`) and the grip buzzes `fail`.
Configure Scene ▸ Physics ▸ Play mode ▸ Limit grab reach.
· JUMP IN VR: the Character Controller node's `jumpHeight` jumps on right A in Interact walking
(`vrJumpHeight()`; A stays push-to-talk otherwise, `pttByA` so a switch mid-hold cannot strand the
mic). Desktop walk mode always jumped on Space. The capsule stands on dynamic pieces (a query
shape: it never shoves them).
· `src/modules/towers/` — a CORE module (the pong precedent: it may reach core internals through
PRIMED DYNAMIC imports; `api.spawn` still does not exist), dormant unless the scene holds the
`Towers game` marker. `levels.js` is the pure leaf (12 levels, stars = win + par pieces + par
time, unlock = a star on the level before, the rack layout, the tower measure, the judge, the
gust schedule, the outline cells). `module.js`: ONE AUTHORITY (the physics initiator, else the
lowest peer id) deals pieces as TRANSIENT duplicates of templates parked on a vault under the
floor, judges ~10x/s, pushes gusts and writes the `tw*` game variables; every peer derives the
rest (the HUD words through the `towersinfo` value node wired into HUD Text's FORMAT, the
moments, saving stars via `api.storage`). HUD presses carry no presser, so every peer watches the
hudbutton stamps (first sight = history, `age` > 2.5 s = a joiner's history) and only the
authority acts; unlocks read the AUTHORITY's progress. `towerswobble` is a module EFFECT so the
plate is a kinematic body — a module effect's `base` is `{pos, rot: [x,y,z], scale}` ARRAYS, not
THREE objects (reading `base.rotation` throws every frame into noteFrameFailure, silently). The
shell's `api.game.levels`/`setHelp`/`onRestart` are feature-detected. The template def is the
arena + templates + HUD + a small graph; `block` (Wedge/Arch/Corner/Stairs) and `thumb.dress`
(card-only objects) are new author-kit fields. Suites play-reach-jump, towers-levels, game-towers.

## Replication golden rules

Expand Down
18 changes: 18 additions & 0 deletions MODULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,24 @@ data (replicated like any node edit); the slide is a pure function of
`(data, time)` running on each peer — no motion messages at all. The
`registerClickHandler` consumes the click so pressing doesn't select the button.

## Walkthrough: the towers module (a game's rules as a core module)

`src/modules/towers/` runs the **Towers** template's twelve levels. It shows how a game with
real rules splits: the scene (the template) is the arena, the piece templates, the HUD and a
small graph; the module is the rules. Worth copying:

- **The rules as a pure leaf** (`levels.js`) — the level table, the star rule, the unlocks,
the verdict stepper — tested with vitest, no browser.
- **One authority** decides anything shared (the physics initiator, else the lowest peer id):
it deals the pieces, judges the round and writes the result into game variables
(`api.game.setVar`); every peer derives its HUD words from those through a value node wired
into HUD Text's format (`api.registerValueNode`).
- **HUD buttons without a presser**: a press is a replicated stamp on a `hudbutton` node, so
every peer watches `api.flow.triggerStamp` and only the authority acts (first sight = history).
- **Progress on this device** with `api.storage` (stars, unlocks — never replicated).
- **The shell's level picker, help and restart** (`api.game.levels`, `setHelp`, `onRestart`),
feature-detected so the module also runs on an app without them.

## Manager, dev mode & gallery (17-A2/A3)

User modules install, update, disable and remove **live** — `deactivateModule`
Expand Down
Loading
Loading