incanto 0.57.0 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +6 -4
  2. package/bin/incanto-check.mjs +27 -0
  3. package/bin/incanto-multiplay.mjs +170 -0
  4. package/bin/incanto-new.mjs +29 -7
  5. package/bin/incanto-playtest.mjs +28 -2
  6. package/bin/incanto-verify.mjs +155 -19
  7. package/bin/incanto.mjs +107 -0
  8. package/dist/2d.d.ts +8 -2
  9. package/dist/2d.js +3 -3
  10. package/dist/3d.d.ts +9 -3
  11. package/dist/3d.js +4 -4
  12. package/dist/{behavior-l08AEbq9.d.ts → behavior-DWKTUzKI.d.ts} +10 -0
  13. package/dist/{create-game-C5jQYPah.js → create-game-BiW8Men_.js} +61 -13
  14. package/dist/{create-game-DpbUrMOQ.js → create-game-CHDLDQsQ.js} +6 -6
  15. package/dist/debug.d.ts +1 -1
  16. package/dist/debug.js +2 -2
  17. package/dist/{duplicate-BPLZDZpd.js → duplicate-DJQd44CD.js} +1 -1
  18. package/dist/{environment-presets-CvvQr_bJ.js → environment-presets-DRAz5EV9.js} +12 -10
  19. package/dist/{gameplay-BVphcxmE.js → gameplay-BBEjPFsR.js} +62 -34
  20. package/dist/gameplay.d.ts +1 -1
  21. package/dist/gameplay.js +1 -1
  22. package/dist/index.d.ts +36 -8
  23. package/dist/index.js +8 -8
  24. package/dist/{json-BLk7H2Qa.js → json-CwwhxQgb.js} +7 -1
  25. package/dist/{loader-BcrRSjxB.js → loader-D8n7TU8W.js} +142 -5
  26. package/dist/{loader-BbEMTuWg.d.ts → loader-TvkRFbyL.d.ts} +1 -1
  27. package/dist/net.d.ts +2 -2
  28. package/dist/net.js +1 -1
  29. package/dist/{pathfinding-mEN4V1CU.d.ts → pathfinding-BqWBb0kh.d.ts} +1 -1
  30. package/dist/{physics-2d-BLcvEFDR.js → physics-2d-BaRSRrrZ.js} +14 -3
  31. package/dist/{physics-3d-QBrfIT2Y.js → physics-3d-CYxjh-HW.js} +15 -4
  32. package/dist/quiet-rapier-BAJ4K94N.js +46 -0
  33. package/dist/react.d.ts +1 -1
  34. package/dist/react.js +2 -2
  35. package/dist/{register-C6ZBFRjd.js → register-BpFcgdcL.js} +57 -27
  36. package/dist/{register-Ch70uByv.js → register-CDrAQqPp.js} +90 -41
  37. package/dist/{registry-C7u42TID.js → registry-WWcQcfMr.js} +1 -1
  38. package/dist/{replay-s7I2GstT.js → replay-CEPyQtF_.js} +31 -7
  39. package/dist/{replay-Dw6gMlYA.d.ts → replay-O-yAGM76.d.ts} +1 -1
  40. package/dist/{split-screen-B0baBwxI.d.ts → split-screen-BQ3tAsf-.d.ts} +38 -1
  41. package/dist/{split-screen-DLsUrleX.js → split-screen-DDMZutQ6.js} +56 -13
  42. package/dist/{sprite-animation-C0wXLBZJ.js → sprite-animation-CY-mrr1L.js} +1 -1
  43. package/dist/{src-CGjmPw65.js → src-CY21B462.js} +1 -1
  44. package/dist/{teardown-Cs113S9F.js → teardown-RApWnM1G.js} +1 -1
  45. package/dist/{test-it1VekWs.js → test-DHYuFyAu.js} +292 -31
  46. package/dist/test.d.ts +104 -6
  47. package/dist/test.js +3 -3
  48. package/dist/vite.js +2 -2
  49. package/editor/assets/{agent8-CGT7r3Mb.js → agent8-BoRGtVxK.js} +1 -1
  50. package/editor/assets/{debug-BxWSIHG3.js → debug-CzdyCg75.js} +1 -1
  51. package/editor/assets/{index-CV1m-aX5.js → index-VesuVEhe.js} +91 -91
  52. package/editor/index.html +1 -1
  53. package/package.json +3 -1
  54. package/schemas/scene.schema.json +1174 -70
  55. package/skills/incanto-building-2d-games.md +13 -0
  56. package/skills/incanto-building-3d-games.md +3 -3
  57. package/skills/incanto-localization.md +40 -8
  58. package/skills/incanto-multiplayer.md +57 -4
  59. package/skills/incanto-node-reference.md +18 -0
  60. package/skills/incanto-physics-and-input.md +26 -0
  61. package/skills/incanto-playtesting.md +19 -5
  62. package/skills/incanto-verifying-your-game.md +33 -4
  63. package/templates-app/beacon-isle-3d/package.json +1 -1
  64. package/templates-app/beacon-isle-3d/src/game.scene.json +7 -6
  65. package/templates-app/platformer-2d/PROJECT/Context.md +70 -0
  66. package/templates-app/platformer-2d/PROJECT/Requirements.md +63 -0
  67. package/templates-app/platformer-2d/PROJECT/Status.md +60 -0
  68. package/templates-app/platformer-2d/PROJECT/Structure.md +77 -0
  69. package/templates-app/platformer-2d/docs/project-2d-rules.md +61 -0
  70. package/templates-app/platformer-2d/index.html +99 -0
  71. package/templates-app/platformer-2d/package.json +23 -0
  72. package/templates-app/platformer-2d/src/behaviors.ts +541 -0
  73. package/templates-app/platformer-2d/src/game.scene.json +2061 -0
  74. package/templates-app/platformer-2d/src/main.ts +68 -0
  75. package/templates-app/platformer-2d/tsconfig.json +13 -0
  76. package/templates-app/platformer-2d/verify.ts +275 -0
  77. package/templates-app/platformer-2d/vite.config.ts +12 -0
  78. package/templates-app/star-survivor/PROJECT/Context.md +55 -0
  79. package/templates-app/star-survivor/PROJECT/Requirements.md +47 -0
  80. package/templates-app/star-survivor/PROJECT/Status.md +44 -0
  81. package/templates-app/star-survivor/PROJECT/Structure.md +63 -0
  82. package/templates-app/star-survivor/docs/project-2d-rules.md +53 -0
  83. package/templates-app/star-survivor/index.html +232 -0
  84. package/templates-app/star-survivor/package.json +23 -0
  85. package/templates-app/star-survivor/src/behaviors.ts +624 -0
  86. package/templates-app/star-survivor/src/game.scene.json +464 -0
  87. package/templates-app/star-survivor/src/main.ts +49 -0
  88. package/templates-app/star-survivor/tsconfig.json +13 -0
  89. package/templates-app/star-survivor/verify.ts +193 -0
  90. package/templates-app/star-survivor/vite.config.ts +12 -0
  91. package/templates-app/tps-3d/package.json +1 -1
  92. package/templates-app/tps-3d/src/game.scene.json +6 -3
  93. package/templates-app/tps-3d/verify.ts +17 -1
  94. package/templates-app/village-quest-3d/package.json +1 -1
  95. package/templates-app/village-quest-3d/src/grove.scene.json +14 -13
  96. package/templates-app/village-quest-3d/src/village.scene.json +5 -5
@@ -0,0 +1,60 @@
1
+ # Status — platformer-2d (Incanto)
2
+
3
+ ## Implemented
4
+
5
+ - Full-window single-canvas page with loading overlay, an on-screen JUMP button,
6
+ and the iframe GAME_SIZE harness.
7
+ - `game.scene.json` — **Castle Run**: viewport (`design [960,540]`, `fit:
8
+ expand`), `physics.gravity [0,1800]`, `move` (A/D + arrows) / `jump`
9
+ (Space/W/Up) input, `knight`/`goblin` sheets + `coin`/`gem` textures, and the
10
+ level —
11
+ - `Game` root: `ScoreKeeper` (`scoreToWin 100000`, `lives 3`).
12
+ - Parallax castle backdrop (sky, sun, clouds, far castle band + towers, near
13
+ castle band + battlements) via `ParallaxLayer`.
14
+ - `GroundA–D` stone with green moss caps and gaps; `Plat1–4` floating
15
+ platforms; `MovePlat` (`Patrol` horizontal) + `Lift` (`Oscillate` vertical),
16
+ both group `platform` (rideable).
17
+ - `Spikes1/2` (`hazard`), `DeathPlane` (`pit`), `Checkpoint`, `Goal`.
18
+ - `Gob1–3` goblins (`Patrol` + `GoblinSkin`, group `enemy`).
19
+ - 12 coins + 2 gems (`Pickup` + bobbing `Oscillate` sprite).
20
+ - `Player` (`CharacterBody2D` + `PlayerController`) with `Skin` (knight) +
21
+ `Feet` sensor; `Camera` (`FollowCam`); SFX players; HUD + banner UILayers.
22
+ - Custom code in `src/behaviors.ts`: `PlayerController` (coyote/buffer/double-
23
+ jump/variable height/stomp/knockback/hearts+lives/checkpoint respawn/platform
24
+ carry/anim), `GoblinSkin`, `FollowCam` (follow + shake), `ParallaxLayer`,
25
+ `HudUpdater`. Particle puffs on double-jump/stomp/land/checkpoint via
26
+ `Particles2D`.
27
+ - `connections`: each coin/gem `collected → addScore` (+ SFX); root `won/lost →
28
+ SfxWin/SfxLose`.
29
+ - `main.ts`: inject the four asset urls, `createGame2D`, optional music, JUMP
30
+ button, remove loader, expose `window.game`. Node uids omitted (loader-
31
+ generated).
32
+
33
+ ## Works (verified)
34
+
35
+ - `bun run check` → 1/1 scene valid. `bun run typecheck` → clean. `bun run lint`
36
+ → clean.
37
+ - `bun run verify` (headless `runScript`): 13/13 assertions pass — run right,
38
+ jump off the ground and settle, a coin raises the score (Pickup → addScore), a
39
+ STOMP removes a goblin and scores, a side hit costs a heart, dying respawns the
40
+ knight at the checkpoint, the goal emits `won` once, and a pit fall with the
41
+ last life emits `lost`.
42
+ - Played in a real browser (`bun run dev`, Playwright-driven): the knight + the
43
+ goblins render as their real animated sheets; coins/gems render as sprites;
44
+ ground has moss caps; the parallax castle bands scroll; HUD shows ◆ coins,
45
+ ♥♥♥ hearts and lives. A tap jump is a short hop (~41px), a held jump is full
46
+ (~156px), and a mid-air press double-jumps — variable height + double-jump
47
+ confirmed. FollowCam tracks + clamps to the level bounds. ZERO console errors.
48
+
49
+ ## Not Included (on purpose — and the next polish passes)
50
+
51
+ - Background castle is styled `ColorRect2D` (no tilemap node in v0): two parallax
52
+ layers (`CastleFarLayer` 0.82 / `CastleNearLayer` 0.6), each a Node2D with ONE
53
+ `ParallaxLayer` whose children (wall band + towers + crenellation merlons + warm
54
+ glowing windows + a gate + a pennant) inherit the scroll. Reads clearly as a
55
+ castle skyline; swap for tile/sprite art later.
56
+ - No wall-jump/dash, no enemy variety beyond the goblin, no melee (the knight
57
+ has an attack sheet 12–17 if you want it), no bundled music (SFX presets only).
58
+ - One hand-authored level — extend by copying nodes into the right GROUP
59
+ (`enemy`/`hazard`/`pit`/`checkpoint`/`platform`); `PlayerController` picks them
60
+ up with no extra wiring.
@@ -0,0 +1,77 @@
1
+ # Structure — platformer-2d (Incanto)
2
+
3
+ ## `index.html`
4
+
5
+ Full-window page: inline favicon, loading overlay (removed once the game boots),
6
+ one `#game` canvas, an on-screen `#btn-jump` button (mobile parity), the
7
+ iframe-embed GAME_SIZE script, and the module entry.
8
+
9
+ ## `src/game.scene.json`
10
+
11
+ THE whole level (open it with `npx incanto-editor`):
12
+
13
+ - scene header — `environment.background`, `viewport` (`design [960,540]`, `fit:
14
+ expand`), `physics.gravity [0,1800]`, `move`/`jump` input, and the
15
+ `knight`/`goblin` spritesheets + `coin`/`gem` texture `assets` (urls injected
16
+ in `main.ts`).
17
+ - `Game` root — `ScoreKeeper` (`scoreToWin 100000`, `lives 3`).
18
+ - Parallax castle backdrop — `Sky`/`Sun`/`Cloud1–3` (far), `CastleFar` band +
19
+ `TowerFarA–C`, `CastleNear` band + `BattlementA/B` — `ColorRect2D` +
20
+ `ParallaxLayer` (factor 0.6–0.96, higher = farther).
21
+ - `GroundA–D` — `StaticBody2D` stone (brown body + green `Moss` cap) with GAPS
22
+ (pits) between them.
23
+ - `Plat1–4` — static floating platforms; `MovePlat` (`Patrol` pingpong, group
24
+ `platform`) ferries the first pit; `Lift` (`Oscillate` vertical, group
25
+ `platform`) crosses the spike pit.
26
+ - `Spikes1/2` (group `hazard`) and `DeathPlane` (group `pit`, far below).
27
+ - `Checkpoint` (group `checkpoint`, blue flag) and `Goal` (group `goal`, gold
28
+ flag + gate).
29
+ - `Gob1–3` — Node2D (group `enemy`) + `Patrol`; children `Skin`
30
+ (`AnimatedSprite2D` goblin) + `AI` (`GoblinSkin`).
31
+ - `Coins/Coin1–12` — Area2D `Pickup` (value 10) with a bobbing `coin` sprite
32
+ child; `Gem1/2` — Area2D `Pickup` (value 50) with a bobbing `gem` sprite.
33
+ - `Player` — `CharacterBody2D` (group `player`, capsule) + `PlayerController`;
34
+ children `Skin` (`AnimatedSprite2D` knight idle/run/jump) + `Feet` (Area2D
35
+ sensor for moving-platform carry).
36
+ - `Camera` — `Camera2D` (`current`) + `FollowCam` (look-ahead, clamped `limits`,
37
+ screen-shake).
38
+ - `SfxJump/Coin/Stomp/Hurt/Checkpoint/Win/Lose` — `AudioPlayer` SFX presets
39
+ (PlayerController plays jump/stomp/hurt/checkpoint directly; coins/win/lose via
40
+ `connections`).
41
+ - `HUD` — UILayer (top-left, `HudUpdater`): Coin / Hearts / Lives / Hint Labels.
42
+ `BannerLayer` — UILayer (center) win/lose `Banner`.
43
+ - `connections` — each coin/gem `collected → ScoreKeeper.addScore` (+ SFX); root
44
+ `won/lost → SfxWin/SfxLose`. (Stomp/hit/checkpoint/respawn/win-trigger are
45
+ resolved by `PlayerController`, not connections.)
46
+
47
+ ## `src/behaviors.ts`
48
+
49
+ - `PlayerController` — the heart: run/gravity/coyote/buffer/double-jump/variable
50
+ height, stomp + bounce, side-hit knockback, hearts+lives, checkpoint respawn,
51
+ moving-platform carry (via the `Feet` sensor), sprite clip + facing + i-frame
52
+ blink. All enemy/hazard/pit/checkpoint/goal contact is AABB against groups in
53
+ `resolveContacts()`.
54
+ - `GoblinSkin` — face the patrol heading + play the goblin walk clip.
55
+ - `FollowCam` — follow (look-ahead + smoothing + world clamp) + `shake()`.
56
+ - `ParallaxLayer` — re-anchor a backdrop to `cameraX · factor` for depth.
57
+ - `HudUpdater` — score/hearts/lives Labels + win/lose banner.
58
+
59
+ ## `src/main.ts`
60
+
61
+ Boot: inject the four built-in asset urls, `createGame2D({ canvas, scene,
62
+ behaviors })`, optional `engine.music`, the JUMP button, remove the loader,
63
+ expose `window.game`.
64
+
65
+ ## `verify.ts`
66
+
67
+ Browserless proof via `incanto/test` `runScript`: run+jump (off the ground and
68
+ back), coin scoring, STOMP (goblin removed + scored), side-hit costs a heart,
69
+ checkpoint respawn (die → back at the flag), WIN (goal → `won`), LOSE (pit with
70
+ the last life → `lost`). Run with `bun run verify`.
71
+
72
+ ## `docs/project-2d-rules.md`
73
+
74
+ The vibe-coding rules: read the skills, JSON structure, built-ins first, omit
75
+ uids, declared assets, y-down space, collider props, GROUP-DRIVEN centralised
76
+ damage (§8), deferred engine access (§11), debug draw, delta discipline, verify
77
+ like a user.
@@ -0,0 +1,61 @@
1
+ <userRequest>
2
+ YOU MUST follow these rules EXACTLY when vibe-coding on this Incanto template
3
+ (Castle Run — a 2D adventure platformer):
4
+
5
+ 1. 📖 READ THE SKILLS FIRST: `node_modules/incanto/skills/` is the engine
6
+ manual, version-pinned to what is installed. Start with
7
+ `incanto-building-2d-games.md` and `incanto-scene-json-authoring.md`, then
8
+ `incanto-gameplay-behaviors.md` (Pickup/Patrol/Oscillate/ScoreKeeper) and
9
+ `incanto-physics-and-input.md` (2D bodies, the input map, units).
10
+ 2. 🧱 STRUCTURE IS JSON: scenes, nodes, props, assets, input maps and
11
+ connections all live in `src/game.scene.json`. Add level geometry there (or
12
+ with `npx incanto-editor`), NOT by constructing nodes ad-hoc in code.
13
+ 3. 🧠 LOGIC IS BEHAVIORS: prefer a BUILT-IN behavior over custom code (Pickup
14
+ coins/gems, Patrol goblins + the moving platform, Oscillate the lift +
15
+ bobbing pickups, ScoreKeeper score/lives/win-lose, AudioPlayer SFX — all
16
+ wired in JSON). Custom code = small TypeScript Behavior classes registered via
17
+ `createGame2D({ behaviors })` and attached with `"script": { "name": "Name" }`.
18
+ This game's custom behaviors are the platformer GAME FEEL the library leaves
19
+ open: `PlayerController` (coyote-time, jump-buffer, double-jump, variable jump
20
+ height, stomp, knockback, hearts+lives, checkpoint respawn, moving-platform
21
+ carry), `GoblinSkin`, `FollowCam` (follow + screen-shake), `ParallaxLayer`,
22
+ `HudUpdater`.
23
+ 4. 🔑 UIDS ARE GENERATED: omit `uid` and the loader assigns one (via the engine's
24
+ `newUid`). NEVER hand-invent readable uid strings.
25
+ 5. 📦 ASSETS ARE DECLARED: `assets` entries need `type` + `url`; reference them as
26
+ `"$key"`. The knight + goblin sheets and the coin + gem textures are BUILT-INS
27
+ (`incanto/assets/...`) imported in `main.ts` and injected into the asset url
28
+ placeholders before boot. Free extra key-values are preserved — use them for
29
+ notes (frame ranges, license, source).
30
+ 6. ⬇️ Y IS DOWN: 2D space is y-down pixels, (0,0) top-left, clockwise degrees.
31
+ Positive gravity falls (`physics.gravity` is `[0, 1800]`). Up is NEGATIVE
32
+ velocity; a jump subtracts y.
33
+ 7. 🧲 COLLIDERS ARE PROPS: `"collider": { "shape": "rect"|"circle"|"capsule", … }`
34
+ on a body node — never child shape nodes. Wrong shapes hard-fail at load.
35
+ 7b. 🦶 STAND THE FEET ON THE GROUND: a sprite-sheet `anchor` is the artist's pivot
36
+ (hip/centre), NOT the feet, and the frame has transparent padding below the art —
37
+ so a character will FLOAT unless you align it. MEASURE the feet (bottom opaque row
38
+ ÷ frame height) and set `anchor.y` so the visible feet sit at the collider bottom
39
+ (here the knight is `0.464`, goblins `0.348`, not the sheet's ~0.61). See
40
+ `incanto-building-2d-games.md` → AnimatedSprite2D "stand the feet on the ground".
41
+ 8. 🎯 DAMAGE IS CENTRALISED + GROUP-DRIVEN: the player takes NO built-in Health —
42
+ `PlayerController` owns hearts/lives and resolves ALL contact by AABB against
43
+ GROUPS each frame, so there's one authority and never a stomp-vs-damage
44
+ double-trigger. Tag content with groups: `enemy` (stompable from above, hurts
45
+ on the side), `hazard` (spikes → lose a heart), `pit` (death plane → instant
46
+ life loss), `checkpoint` (sets respawn), `goal` (win), `platform` (a moving
47
+ body the Feet sensor rides). Add an enemy/hazard = add the node in the right
48
+ group; no wiring needed.
49
+ 9. 🐛 DEBUG WHEN UNSURE: `createGame2D({ debug: true })` (or `VITE_INCANTO_DEBUG=1`)
50
+ shows the Explorer/Inspector/Stats; `physics.debugDraw = true` draws colliders.
51
+ 10. 🎬 DELTA DISCIPLINE: only write props that differ from defaults — the loader
52
+ treats unknown/garbage props as hard errors, which is your friend.
53
+ 11. 🧭 ENGINE ACCESS IS DEFERRED: `this.engine` is NOT available in `onReady`
54
+ during `loadScene` (the scene isn't attached yet) — read scene config
55
+ (gravity, etc.) lazily on the first `fixedUpdate`. `getNode`/signals are fine
56
+ in `onReady`.
57
+ 12. ✅ VERIFY LIKE A USER: `bun run check` (scene valid) → `bun run verify`
58
+ (headless run/jump/coin/stomp/checkpoint/win/lose proof) → `bun run dev`,
59
+ open the browser, and actually PLAY what you changed (run, double-jump, stomp
60
+ a goblin, ride the lift, die and respawn at the flag) before declaring done.
61
+ </userRequest>
@@ -0,0 +1,99 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><rect width=%22100%22 height=%22100%22 rx=%2220%22 fill=%22%236ee7dc%22/><text x=%2250%22 y=%2268%22 font-size=%2255%22 text-anchor=%22middle%22 font-family=%22Arial%22 font-weight=%22bold%22 fill=%22%230b0d14%22>in</text></svg>" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
7
+ <title>Castle Run — Incanto platformer</title>
8
+ <style>
9
+ html,
10
+ body {
11
+ margin: 0;
12
+ padding: 0;
13
+ height: 100%;
14
+ overflow: hidden;
15
+ font-family: Arial, sans-serif;
16
+ background: #6aa9cf;
17
+ }
18
+ canvas {
19
+ display: block;
20
+ width: 100%;
21
+ height: 100%;
22
+ }
23
+ .loading-container {
24
+ position: fixed;
25
+ inset: 0;
26
+ z-index: 50;
27
+ display: flex;
28
+ flex-direction: column;
29
+ align-items: center;
30
+ justify-content: center;
31
+ background: #6aa9cf;
32
+ color: white;
33
+ }
34
+ .loading-title {
35
+ font-size: 1.5rem;
36
+ font-weight: bold;
37
+ margin-bottom: 1rem;
38
+ }
39
+ .spinner {
40
+ width: 30px;
41
+ height: 30px;
42
+ border: 3px solid rgba(255, 255, 255, 0.3);
43
+ border-radius: 50%;
44
+ border-top-color: white;
45
+ animation: spin 1s ease-in-out infinite;
46
+ }
47
+ @keyframes spin {
48
+ to {
49
+ transform: rotate(360deg);
50
+ }
51
+ }
52
+ .round-btn {
53
+ position: fixed;
54
+ bottom: 2rem;
55
+ right: 2rem;
56
+ z-index: 1001;
57
+ width: 88px;
58
+ height: 88px;
59
+ border: none;
60
+ border-radius: 9999px;
61
+ background: rgba(255, 209, 102, 0.45);
62
+ color: #1d2a44;
63
+ font-weight: bold;
64
+ font-size: 0.9rem;
65
+ cursor: pointer;
66
+ user-select: none;
67
+ -webkit-user-select: none;
68
+ touch-action: none;
69
+ transition: transform 0.1s;
70
+ }
71
+ .round-btn:active {
72
+ transform: scale(0.9);
73
+ }
74
+ </style>
75
+ </head>
76
+ <body>
77
+ <div class="loading-container" id="loading">
78
+ <h2 class="loading-title">Castle Run</h2>
79
+ <div class="spinner"></div>
80
+ </div>
81
+ <canvas id="game"></canvas>
82
+ <button type="button" id="btn-jump" class="round-btn">JUMP</button>
83
+ <script>
84
+ // agent8 embed harness: report the game size to a hosting iframe parent.
85
+ function postGameSize() {
86
+ window.parent.postMessage(
87
+ { type: 'GAME_SIZE_RESPONSE', width: document.body.scrollWidth, height: document.body.scrollHeight },
88
+ '*',
89
+ );
90
+ }
91
+ window.addEventListener('load', postGameSize);
92
+ window.addEventListener('resize', postGameSize);
93
+ window.addEventListener('message', (e) => {
94
+ if (e.data && e.data.type === 'REQUEST_GAME_SIZE') postGameSize();
95
+ });
96
+ </script>
97
+ <script type="module" src="/src/main.ts"></script>
98
+ </body>
99
+ </html>
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "platformer-2d",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite",
7
+ "build": "vite build",
8
+ "typecheck": "tsc --noEmit",
9
+ "check": "incanto-check src",
10
+ "verify": "bun verify.ts"
11
+ },
12
+ "dependencies": {
13
+ "@dimforge/rapier2d-compat": "0.19.3",
14
+ "incanto": "^0.59.0",
15
+ "three": "^0.184.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^24.0.0",
19
+ "@types/three": "^0.184.1",
20
+ "typescript": "^6.0.3",
21
+ "vite": "^8.0.16"
22
+ }
23
+ }