diff --git a/art/sprites/README.md b/art/sprites/README.md
index 36cd1e5..64772fe 100644
--- a/art/sprites/README.md
+++ b/art/sprites/README.md
@@ -1,21 +1,22 @@
# Sprite boards
-Sprite reference boards: the hero sheet (approved) and the animation, enemy, item and boss boards that follow the [visual direction](../direction/README.md). Each board is drawn from real photos of the pose or object, keyed on magenta and crushed to a transparent 1x image. Frame lists and status are in [07 – Asset manifest](../../docs/07-ASSET-MANIFEST.md); the pipeline is in [09](../../docs/09-ART-PIPELINE.md). The [poncho photo board](source-boards/ponchos.png) is the source for the hero's poncho.
+Sprite reference boards: the hero sheet (approved) and the animation, enemy, item and boss boards that follow the [visual direction](../direction/README.md). Each board is drawn from real photos of the pose or object, keyed on magenta and crushed to a transparent 1x image. Frame lists and status are in [07 – Asset manifest](../../docs/07-ASSET-MANIFEST.md); the pipeline is in [09](../../docs/09-ART-PIPELINE.md). The [poncho photo board](source-boards/ponchos.png) is the source for the hero's poncho.
| | |
| --- | --- |
-| [](hero.md)
**[The student · hero design](hero.md)** | [](hero-move.md)
**[The student · walk](hero-move.md)** |
-| [](hero-idle.md)
**[The student · idle variants](hero-idle.md)** | [](hero-run.md)
**[The student · run](hero-run.md)** |
-| [](hero-hurt.md)
**[The student · hurt and interact](hero-hurt.md)** | [](hero-death.md)
**[The student · death](hero-death.md)** |
-| [](hero-air.md)
**[The student · jump, fall, climb](hero-air.md)** | [](hero-ladder.md)
**[The student · ladder top and bottom](hero-ladder.md)** |
-| [](chair-side.md)
**[Chair · side sweep](chair-side.md)** | [](chair-overhead.md)
**[Chair · overhead slam](chair-overhead.md)** |
-| [](chair-block.md)
**[Chair · block](chair-block.md)** | [](enemy-drone.md)
**[Enemy · drone](enemy-drone.md)** |
-| [](enemy-rover.md)
**[Enemy · rover](enemy-rover.md)** | [](enemy-crab.md)
**[Enemy · crab](enemy-crab.md)** |
-| [](fx.md)
**[FX · black fluid](fx.md)** | [](boss-jenny.md)
**[Boss · Jenny, attacks](boss-jenny.md)** |
-| [](boss-jenny-2.md)
**[Boss · Jenny, stunned and debris](boss-jenny-2.md)** | [](pocket-computer.md)
**[Item · pocket computer](pocket-computer.md)** |
-| [](mensa-food.md)
**[Mensa · food pickups](mensa-food.md)** | [](coffee-machine.md)
**[Prop · coffee machine (save point)](coffee-machine.md)** |
-| [](dexter.md)
**[Dexter · moves](dexter.md)** | [](dexter-acts.md)
**[Dexter · gestures](dexter-acts.md)** |
-| [](fx-leaves.md)
**[FX · falling leaves](fx-leaves.md)** | [](fx-ground.md)
**[FX · footstep puffs](fx-ground.md)** |
-| [](fx-water.md)
**[FX · pond ripples and lily pad](fx-water.md)** | [](dexter-prologue.md)
**[Dexter · prologue additions](dexter-prologue.md)** |
-| [](menu-key.md)
**[Menu · the Hidden Garden at first light](menu-key.md)** | [](rubber-chicken.md)
**[Secret · the rubber chicken](rubber-chicken.md)** |
-| [](arena-wall.md)
**[Prop · Jenny's arena wall](arena-wall.md)** | |
+| [](hero.md)
**[The student · hero design](hero.md)** | [](hero-move.md)
**[The student · walk](hero-move.md)** |
+| [](hero-idle.md)
**[The student · idle variants](hero-idle.md)** | [](hero-run.md)
**[The student · run](hero-run.md)** |
+| [](hero-hurt.md)
**[The student · hurt and interact](hero-hurt.md)** | [](hero-death.md)
**[The student · death](hero-death.md)** |
+| [](hero-air.md)
**[The student · jump, fall, climb](hero-air.md)** | [](hero-ladder.md)
**[The student · ladder top and bottom](hero-ladder.md)** |
+| [](chair-side.md)
**[Chair · side sweep](chair-side.md)** | [](chair-overhead.md)
**[Chair · overhead slam](chair-overhead.md)** |
+| [](chair-block.md)
**[Chair · block](chair-block.md)** | [](enemy-drone.md)
**[Enemy · drone](enemy-drone.md)** |
+| [](enemy-rover.md)
**[Enemy · rover](enemy-rover.md)** | [](enemy-crab.md)
**[Enemy · crab](enemy-crab.md)** |
+| [](fx.md)
**[FX · black fluid](fx.md)** | [](boss-jenny.md)
**[Boss · Jenny, attacks](boss-jenny.md)** |
+| [](boss-jenny-2.md)
**[Boss · Jenny, stunned and debris](boss-jenny-2.md)** | [](pocket-computer.md)
**[Item · pocket computer](pocket-computer.md)** |
+| [](mensa-food.md)
**[Mensa · food pickups](mensa-food.md)** | [](coffee-machine.md)
**[Prop · coffee machine (save point)](coffee-machine.md)** |
+| [](dexter.md)
**[Dexter · moves](dexter.md)** | [](dexter-acts.md)
**[Dexter · gestures](dexter-acts.md)** |
+| [](fx-leaves.md)
**[FX · falling leaves](fx-leaves.md)** | [](fx-ground.md)
**[FX · footstep puffs](fx-ground.md)** |
+| [](fx-water.md)
**[FX · pond ripples and lily pad](fx-water.md)** | [](dexter-prologue.md)
**[Dexter · prologue additions](dexter-prologue.md)** |
+| [](menu-key.md)
**[Menu · the Hidden Garden at first light](menu-key.md)** | [](rubber-chicken.md)
**[Secret · the rubber chicken](rubber-chicken.md)** |
+| [](arena-wall.md)
**[Prop · Jenny's arena wall](arena-wall.md)** | [](arena-platform.md)
**[Prop · Jenny's arena platforms](arena-platform.md)** |
+| [](arena-chain.md)
**[Prop · the platforms' chains](arena-chain.md)** | |
diff --git a/art/sprites/arena-chain.md b/art/sprites/arena-chain.md
new file mode 100644
index 0000000..7ea5538
--- /dev/null
+++ b/art/sprites/arena-chain.md
@@ -0,0 +1,25 @@
+# Prop · the platforms' chains
+
+
+[← Prop · Jenny's arena platforms](arena-platform.md) · [Sprite boards](README.md) · **31 / 31** · [Sprite boards →](README.md)
+
+
+
+
+**Review:** 🟨 Awaiting Alexander's review (25 Oct). The platforms hang from the girder over the arena: scripts/arena_platforms.py tiles this chain's link period from each platform end up to the girder's underside. The second frame came out as a second chain instead of an eye-bolt, so it is unused.
+
+## Brief
+
+On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground shadows), with NO text at all - no titles, no captions, no labels: Image 1 is an approved prop from this game, a rusty steel girder platform: draw in exactly its pixel-art look, pixel grain, palette and rusty orange-on-steel colours. Prop sprite sheet, 1 row, exactly 2 frames, side by side with a wide magenta gap: frame 1, ONE heavy rusted steel CHAIN hanging perfectly straight and vertical, about as long as the platform in image 1 is wide, made of many identical oval links alternating face-on and edge-on, the top and bottom links cut flat; frame 2, ONE heavy steel EYE-BOLT anchor: a small rusted base plate with two rivets and a thick eye ring on top, with a single rusted shackle through the ring - the piece that would sit on top of the platform where the chain is fixed. Both frames at the SAME pixel scale (the chain's links as thick as the shackle). No platform, no people, no scenery.
+
+## Inputs
+
+- [arena-platform-preview.png](images/arena-platform-preview.png)
+- [coffee-machine-preview.png](images/coffee-machine-preview.png)
+- [style-master-preview.png](../direction/images/style-master-preview.png)
+
+Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/arena-chain.png` · rebuild: `python scripts/make_boards.py draw arena-chain --redo --config art/sprites/boards.json`
+
+
+[← Prop · Jenny's arena platforms](arena-platform.md) · [Sprite boards](README.md) · **31 / 31** · [Sprite boards →](README.md)
+
diff --git a/art/sprites/arena-platform.md b/art/sprites/arena-platform.md
new file mode 100644
index 0000000..b838b24
--- /dev/null
+++ b/art/sprites/arena-platform.md
@@ -0,0 +1,25 @@
+# Prop · Jenny's arena platforms
+
+
+[← Prop · Jenny's arena wall](arena-wall.md) · [Sprite boards](README.md) · **30 / 31** · [Prop · the platforms' chains →](arena-chain.md)
+
+
+
+
+**Review:** 🟨 Awaiting Alexander's review (25 Oct). Replaces the strip cut from the maze plate, which read flat and floated with no reason (Alexander). Drawn with volume from the coffee-machine sheet's look and the maze's own girder as the material reference: a riveted box girder with a grated underside, 202 × 25 px in the game. The prompt asked for eye-bolts and chain stubs, and the model drew the platform alone, so it is locked and the chains are their own board.
+
+## Brief
+
+On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground shadows, no dust under the objects), with NO text at all - no titles, no captions, no frame numbers, no labels, no readable words or logos: Image 1 is an approved prop sheet from this game: draw in exactly that pixel-art look - solid three-dimensional objects drawn as detailed 2D pixel art with volume, the same pixel grain, palette and warm rim light. Image 2 shows the rusty steel girders of the level these props hang in: copy their material exactly - pale blue-grey painted steel eaten by orange rust, riveted flanges, a dark grated underside. Prop sprite sheet, 2 rows. Row 1, exactly 1 frame: ONE short girder PLATFORM segment hung like a stage rigging bar, seen from slightly below like image 2: a long flat box beam about FIVE times as long as it is tall, its front face with rivets, a lit top flange, a dark grated underside with depth, solid end caps showing its thickness; on top, near each end, a heavy steel eye-bolt with the lowest three links of a rusted chain rising straight up from it. Row 2, exactly 1 frame: ONE long straight vertical rusted steel CHAIN hanging perfectly straight up and down, many identical links, about as long as the platform in row 1, the same chain as on the platform. Both rows at the SAME pixel scale. Rows separated by a wide empty magenta band. No people, no scenery, no sky.
+
+## Inputs
+
+- [coffee-machine-preview.png](images/coffee-machine-preview.png)
+- [girder-reference.png](../stages/rusty-creek/arena/girder-reference.png)
+- [style-master-preview.png](../direction/images/style-master-preview.png)
+
+Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/arena-platform.png` · rebuild: `python scripts/make_boards.py draw arena-platform --redo --config art/sprites/boards.json`
+
+
+[← Prop · Jenny's arena wall](arena-wall.md) · [Sprite boards](README.md) · **30 / 31** · [Prop · the platforms' chains →](arena-chain.md)
+
diff --git a/art/sprites/arena-wall.md b/art/sprites/arena-wall.md
index 8e43a54..9648b58 100644
--- a/art/sprites/arena-wall.md
+++ b/art/sprites/arena-wall.md
@@ -1,12 +1,12 @@
-# Prop · Jenny's arena wall
+# Prop · Jenny's arena wall
-[← Secret · the rubber chicken](rubber-chicken.md) · [Sprite boards](README.md) · **29 / 29** · [Sprite boards →](README.md)
+[← Secret · the rubber chicken](rubber-chicken.md) · [Sprite boards](README.md) · **29 / 31** · [Prop · Jenny's arena platforms →](arena-platform.md)
-
+
-**Review:** 🟨 Awaiting Alexander's review (25 Oct). Playtest J7: the arena's walls were code-drawn rectangles with hazard stripes. First draw: a barrier column of red-and-white crowd-control panels strapped to a rusted pole, with a caged warning lamp (lit, dark). 300 px tall in the game, as tall as the walls. The lamp's halo specks are dropped by "despeckle".
+**Review:** 🟨 Awaiting Alexander's review (25 Oct). Playtest J7: the arena's walls were code-drawn rectangles with hazard stripes. First draw: a barrier column of red-and-white crowd-control panels strapped to a rusted pole, with a caged warning lamp (lit, dark). 300 px tall in the game, as tall as the walls. The lamp's halo specks are dropped by "despeckle".
## Brief
@@ -21,5 +21,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/arena-wall.png` · rebuild: `python scripts/make_boards.py draw arena-wall --redo --config art/sprites/boards.json`
-[← Secret · the rubber chicken](rubber-chicken.md) · [Sprite boards](README.md) · **29 / 29** · [Sprite boards →](README.md)
+[← Secret · the rubber chicken](rubber-chicken.md) · [Sprite boards](README.md) · **29 / 31** · [Prop · Jenny's arena platforms →](arena-platform.md)
diff --git a/art/sprites/boards.json b/art/sprites/boards.json
index 7c65fee..27b0a3a 100644
--- a/art/sprites/boards.json
+++ b/art/sprites/boards.json
@@ -1,6 +1,6 @@
{
"name": "Sprite boards",
- "readme": "Sprite reference boards: the hero sheet (approved) and the animation, enemy, item and boss boards that follow the [visual direction](../direction/README.md). Each board is drawn from real photos of the pose or object, keyed on magenta and crushed to a transparent 1x image. Frame lists and status are in [07 – Asset manifest](../../docs/07-ASSET-MANIFEST.md); the pipeline is in [09](../../docs/09-ART-PIPELINE.md). The [poncho photo board](source-boards/ponchos.png) is the source for the hero's poncho.",
+ "readme": "Sprite reference boards: the hero sheet (approved) and the animation, enemy, item and boss boards that follow the [visual direction](../direction/README.md). Each board is drawn from real photos of the pose or object, keyed on magenta and crushed to a transparent 1x image. Frame lists and status are in [07 – Asset manifest](../../docs/07-ASSET-MANIFEST.md); the pipeline is in [09](../../docs/09-ART-PIPELINE.md). The [poncho photo board](source-boards/ponchos.png) is the source for the hero's poncho.",
"model": "gpt-image-2.5-sunburst",
"size": "1536x1024",
"style": "Detailed modern HI-BIT PIXEL ART in the look of a modern detailed pixel-art side-scroller: crisp square pixels at a fine 2-pixel grain, rich naturalistic palette with warm autumn light, careful pixel shading and texture (rust, fabric weave, plastic sheen), no anti-aliasing blur, no smooth vector gradients, not chunky 8-bit, not chibi. This is a SPRITE SHEET, not an illustration: no landscape, no sky, no scenery, no panels, no UI frames. Rules: strict side view unless the brief says otherwise; every sprite on the same invisible ground line, same scale within the sheet, evenly spaced with clear empty space between sprites so each can be cut out; frames progress by small continuous changes so they animate smoothly. Solid flat pure magenta #FF00FF background everywhere, no floor, no cast shadows on the background, no frame borders. ABSOLUTELY NO TEXT: no titles, no frame numbers, no arrows, no captions, no labels, no logos - only sprites on magenta. Match the pixel grain, palette and warm rim light of the game's style master (the LAST image), but draw nothing of its scenery. WORLD SCALE: the hero is an adult 6 ft (183 cm) tall; Dexter (the Deggster panther plush) is 1.5 ft (45 cm); Dexter's final form is 8.5 ft (260 cm). Keep real-world proportions between characters and props (a chair seat is at the hero's knee height, a cafeteria tray is as wide as the hero's shoulders).",
@@ -8,7 +8,7 @@
"boards": [
{
"id": "hero",
- "title": "The student · hero design",
+ "title": "The student · hero design",
"factor": 2,
"inputs": [
"out:hero-design",
@@ -18,11 +18,11 @@
"prompt": "Keep the body, height (6 ft adult), proportions, pose, backpack, grey slim jeans and chunky white sneakers of the hero in the first image, and the poncho's CUT, tassel fringe and geometric zig-zag weave from the second image, but RECOLOUR and redesign the poncho as the university's own THD poncho: heather-grey base, deep THD blue and teal zig-zag bands, with a few thin colourful accent stripes (orange, cream, a little red). The poncho has its OWN deep hood, always up. The face is NEVER visible: inside the hood is only solid dark shadow - no eyes, no glasses, no hair, no chin. Gender must be impossible to tell: no visible hair, no visible face, loose silhouette. No hoodie, no scarf. Clean pixel-art CHARACTER SHEET on a flat magenta background, no scenery and no panels. Row 1, full-body turnaround, 5 figures at identical scale: front, three-quarter front, side facing right, three-quarter back, BACK - on the back the poncho has a bold woven THD crest patch in cream and blue with the letters THD clearly readable. Row 2: a close-up of the hood (pure shadow inside), a close-up of the THD back patch, then a scale lineup on one ground line: the hero, a 1.5 ft black panther plush mascot in a grey T-shirt at knee height (third image), and an 8.5 ft tall shadowy panther-humanoid silhouette with glowing blue eyes.",
"locked": true,
"note": "Approved 23 Oct 2026. Drawn from earlier iterations (design anchor outside Git); never redrawn - every other board uses its committed preview.",
- "review": "✅ Approved 23 Oct. Adult 6 ft, face never visible, THD poncho in grey / THD blue / teal with colourful accents, THD crest on the back, scale lineup with Dexter (1.5 ft) and his final form (8.5 ft). Fourth iteration: chibi proportions, a hoodie and a visible face were rejected along the way."
+ "review": "✅ Approved 23 Oct. Adult 6 ft, face never visible, THD poncho in grey / THD blue / teal with colourful accents, THD crest on the back, scale lineup with Dexter (1.5 ft) and his final form (8.5 ft). Fourth iteration: chibi proportions, a hoodie and a visible face were rejected along the way."
},
{
"id": "hero-move",
- "title": "The student · walk",
+ "title": "The student · walk",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -52,11 +52,11 @@
]
]
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "hero-idle",
- "title": "The student · idle variants",
+ "title": "The student · idle variants",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -92,11 +92,11 @@
]
]
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "hero-run",
- "title": "The student · run",
+ "title": "The student · run",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -129,11 +129,11 @@
]
]
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "companion-robot",
- "title": "Campus robot · collectible companion",
+ "title": "Campus robot · collectible companion",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -147,7 +147,7 @@
},
{
"id": "hero-hurt",
- "title": "The student · hurt and interact",
+ "title": "The student · hurt and interact",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -192,11 +192,11 @@
]
]
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "hero-death",
- "title": "The student · death",
+ "title": "The student · death",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -222,11 +222,11 @@
]
]
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "hero-air",
- "title": "The student · jump, fall, climb",
+ "title": "The student · jump, fall, climb",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -266,11 +266,11 @@
],
"ref": "jump-08"
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "hero-ladder",
- "title": "The student · ladder top and bottom",
+ "title": "The student · ladder top and bottom",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -300,7 +300,7 @@
},
{
"id": "chair-side",
- "title": "Chair · side sweep",
+ "title": "Chair · side sweep",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -341,11 +341,11 @@
]
]
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "chair-overhead",
- "title": "Chair · overhead slam",
+ "title": "Chair · overhead slam",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -385,11 +385,11 @@
]
]
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "chair-block",
- "title": "Chair · block",
+ "title": "Chair · block",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -428,11 +428,11 @@
]
]
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "tray-throw",
- "title": "Tray · throw and return",
+ "title": "Tray · throw and return",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -447,7 +447,7 @@
},
{
"id": "enemy-drone",
- "title": "Enemy · drone",
+ "title": "Enemy · drone",
"factor": 2,
"inputs": [
"ref2:20261023_135552.jpg",
@@ -491,11 +491,11 @@
]
]
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "enemy-rover",
- "title": "Enemy · rover",
+ "title": "Enemy · rover",
"factor": 2,
"inputs": [
"ref2:20261023_125948.jpg",
@@ -534,11 +534,11 @@
]
]
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "enemy-crab",
- "title": "Enemy · crab",
+ "title": "Enemy · crab",
"factor": 2,
"inputs": [
"ref2:20261023_125939.jpg",
@@ -579,11 +579,11 @@
]
]
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "fx",
- "title": "FX · black fluid",
+ "title": "FX · black fluid",
"factor": 2,
"inputs": [
"repo:art/direction/images/cast-preview.png",
@@ -602,11 +602,11 @@
]
]
},
- "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "boss-jenny",
- "title": "Boss · Jenny, attacks",
+ "title": "Boss · Jenny, attacks",
"factor": 2,
"inputs": [
"ref2:20261023_130003.jpg",
@@ -642,11 +642,11 @@
],
"ref": "rev-00"
},
- "review": "✅ Approved 24 Oct by Alexander. 270 px long like the approved direction (docs/04), one livery in both sheets; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 24 Oct by Alexander. 270 px long like the approved direction (docs/04), one livery in both sheets; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "boss-jenny-2",
- "title": "Boss · Jenny, stunned and debris",
+ "title": "Boss · Jenny, stunned and debris",
"factor": 2,
"inputs": [
"out:boss-jenny@0,110,1536,190",
@@ -695,11 +695,11 @@
],
"ref": "reboot-01"
},
- "review": "✅ Approved 24 Oct by Alexander. 270 px long like the approved direction (docs/04), one livery in both sheets; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 24 Oct by Alexander. 270 px long like the approved direction (docs/04), one livery in both sheets; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "pocket-computer",
- "title": "Item · pocket computer",
+ "title": "Item · pocket computer",
"factor": 2,
"inputs": [
"ref2:20261023_125955.jpg",
@@ -727,11 +727,11 @@
]
]
},
- "review": "✅ Approved 24 Oct by Alexander (#154); exported to game/public/assets/sprites/ with export_atlas.py. Closed case 19 × 10 px (≈ 45 cm at the hero's 76 px = 183 cm): the photographed suitcase, shrunk to carry; open with the green screen 22 px tall. The map screens are the approved UI atlas (#127), so this board has no screen mock-ups."
+ "review": "✅ Approved 24 Oct by Alexander (#154); exported to game/public/assets/sprites/ with export_atlas.py. Closed case 19 × 10 px (≈ 45 cm at the hero's 76 px = 183 cm): the photographed suitcase, shrunk to carry; open with the green screen 22 px tall. The map screens are the approved UI atlas (#127), so this board has no screen mock-ups."
},
{
"id": "mensa-food",
- "title": "Mensa · food pickups",
+ "title": "Mensa · food pickups",
"factor": 2,
"inputs": [
"repo:art/sprites/images/coffee-machine-preview.png",
@@ -765,11 +765,11 @@
]
]
},
- "review": "✅ Approved 24 Oct by Alexander (#154); exported to game/public/assets/sprites/ with export_atlas.py. Third draw: the first two copied only the muffin; anchoring on the approved coffee-machine sheet gave four distinct items. One frame each (the model ignored the sparkle frames; the game adds the sparkle). Muffin 16 × 14, cake 18 × 14, wafer 17 × 13, Leberkäse roll 18 × 18 px – around the documented 16 px."
+ "review": "✅ Approved 24 Oct by Alexander (#154); exported to game/public/assets/sprites/ with export_atlas.py. Third draw: the first two copied only the muffin; anchoring on the approved coffee-machine sheet gave four distinct items. One frame each (the model ignored the sparkle frames; the game adds the sparkle). Muffin 16 × 14, cake 18 × 14, wafer 17 × 13, Leberkäse roll 18 × 18 px – around the documented 16 px."
},
{
"id": "coffee-machine",
- "title": "Prop · coffee machine (save point)",
+ "title": "Prop · coffee machine (save point)",
"factor": 2,
"inputs": [
"ref2:20261023_125138.jpg",
@@ -795,11 +795,11 @@
]
]
},
- "review": "✅ Approved 24 Oct by Alexander (#154); exported to game/public/assets/sprites/ with export_atlas.py. 42 × 80 px in the game: fills the 40 × 80 coffee-1 box in Rusty Creek, a little taller than the 76 px hero on its base cabinet, as in the photos. Lineup in the stage: previews/coffee-visit-lineup.png."
+ "review": "✅ Approved 24 Oct by Alexander (#154); exported to game/public/assets/sprites/ with export_atlas.py. 42 × 80 px in the game: fills the 40 × 80 coffee-1 box in Rusty Creek, a little taller than the 76 px hero on its base cabinet, as in the photos. Lineup in the stage: previews/coffee-visit-lineup.png."
},
{
"id": "mensa-drinks",
- "title": "Mensa · drinks and coffee",
+ "title": "Mensa · drinks and coffee",
"factor": 2,
"inputs": [
"ref2:20261023_124941.jpg",
@@ -814,7 +814,7 @@
},
{
"id": "wayfinder",
- "title": "Wayfinder sign · prop and pole weapon",
+ "title": "Wayfinder sign · prop and pole weapon",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -828,7 +828,7 @@
},
{
"id": "dexter",
- "title": "Dexter · moves",
+ "title": "Dexter · moves",
"factor": 2,
"inputs": [
"repo:art/direction/images/dexter-stages-preview.png@535,585,125,205",
@@ -873,11 +873,11 @@
]
]
},
- "review": "✅ Approved 24 Oct by Alexander (#141). 27 px = 0.35 of the hero as in the approved Dexter stages (docs/04, compare.py lineup); exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 24 Oct by Alexander (#141). 27 px = 0.35 of the hero as in the approved Dexter stages (docs/04, compare.py lineup); exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "dexter-acts",
- "title": "Dexter · gestures",
+ "title": "Dexter · gestures",
"factor": 2,
"inputs": [
"repo:art/direction/images/dexter-stages-preview.png@535,585,125,205",
@@ -922,11 +922,11 @@
]
]
},
- "review": "✅ Approved 24 Oct by Alexander (#141). 27 px = 0.35 of the hero as in the approved Dexter stages (docs/04, compare.py lineup); exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
+ "review": "✅ Approved 24 Oct by Alexander (#141). 27 px = 0.35 of the hero as in the approved Dexter stages (docs/04, compare.py lineup); exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size."
},
{
"id": "fx-leaves",
- "title": "FX · falling leaves",
+ "title": "FX · falling leaves",
"factor": 2,
"inputs": [
"repo:art/sprites/images/fx-preview.png",
@@ -965,11 +965,11 @@
]
]
},
- "review": "🟡 Drawn 24 Oct for the prologue (workstream 5); exported for the slice, awaiting Alexander's review. Second draw: the first take's thin outlines and fine veins melted into the orange trees at 8 px (6 of 8 test frames LOW in scripts/readability.py); thick dark outlines and lighter fills brought it to 2 of 8. 7–10 px in the game."
+ "review": "🟡 Drawn 24 Oct for the prologue (workstream 5); exported for the slice, awaiting Alexander's review. Second draw: the first take's thin outlines and fine veins melted into the orange trees at 8 px (6 of 8 test frames LOW in scripts/readability.py); thick dark outlines and lighter fills brought it to 2 of 8. 7–10 px in the game."
},
{
"id": "fx-ground",
- "title": "FX · footstep puffs",
+ "title": "FX · footstep puffs",
"factor": 2,
"inputs": [
"repo:art/sprites/images/fx-preview.png",
@@ -1016,11 +1016,11 @@
]
]
},
- "review": "🟡 Drawn 24 Oct for the prologue (workstream 5); exported for the slice, awaiting Alexander's review. Dust 21–30 × 5–12 px, grass and leaf kick ≈ 25 × 14, splash ≈ 25 × 16 – about one step wide next to the 76 px hero. Rows are `even` (they break into specks)."
+ "review": "🟡 Drawn 24 Oct for the prologue (workstream 5); exported for the slice, awaiting Alexander's review. Dust 21–30 × 5–12 px, grass and leaf kick ≈ 25 × 14, splash ≈ 25 × 16 – about one step wide next to the 76 px hero. Rows are `even` (they break into specks)."
},
{
"id": "fx-water",
- "title": "FX · pond ripples and lily pad",
+ "title": "FX · pond ripples and lily pad",
"factor": 2,
"inputs": [
"repo:art/sprites/images/fx-preview.png",
@@ -1061,11 +1061,11 @@
],
"_measured": "Lily pads in hg-4-0 (x 670, y 660) measure about 68 x 14-17 px; height 16 on lily-bob-01 gives 58 x 16. The sparkle is halved (scale 0.5) to a 12 px glint."
},
- "review": "🟡 Drawn 24 Oct for the prologue (workstream 5); exported for the slice, awaiting Alexander's review. Lily pad 58 × 14–16 px, measured against the painted pads in hg-4-0 (≈ 68 × 14–17); ripple 37–71 px wide; sparkle halved to a 12 px glint. The plate's pads are browner than this olive pad – tint in code if it stands out."
+ "review": "🟡 Drawn 24 Oct for the prologue (workstream 5); exported for the slice, awaiting Alexander's review. Lily pad 58 × 14–16 px, measured against the painted pads in hg-4-0 (≈ 68 × 14–17); ripple 37–71 px wide; sparkle halved to a 12 px glint. The plate's pads are browner than this olive pad – tint in code if it stands out."
},
{
"id": "dexter-prologue",
- "title": "Dexter · prologue additions",
+ "title": "Dexter · prologue additions",
"factor": 2,
"inputs": [
"repo:art/sprites/images/dexter-preview.png",
@@ -1099,11 +1099,11 @@
]
]
},
- "review": "✅ Approved 24 Oct by Alexander; exported to game/public/assets/sprites/ with export_atlas.py."
+ "review": "✅ Approved 24 Oct by Alexander; exported to game/public/assets/sprites/ with export_atlas.py."
},
{
"id": "menu-key",
- "title": "Menu · the Hidden Garden at first light",
+ "title": "Menu · the Hidden Garden at first light",
"factor": 2,
"keyed": false,
"size": "2560x1440",
@@ -1115,11 +1115,11 @@
"repo:art/direction/images/hidden-garden-preview.png@0,0,1536,560"
],
"prompt": "Key art for the game's title menu, a full 16:9 picture in exactly the hi-bit pixel-art look of the first image (the style master) - the magenta and sprite-sheet rules do NOT apply to this one image: it is a full scene. ABSOLUTELY NO TEXT, no logo, no title, no letters, no UI, no characters, no people, no animals. The scene: the Hidden Garden pond from images 2 and 3 at FIRST LIGHT, early morning - the low sun just rising behind the far conifer forest on the left, a pale gold-and-rose sky turning blue above, thin mist lying over the still pond. Strict side view like a game screen, everything parallel to the screen: the far hills and dark conifers, the band of autumn trees in yellow, orange and russet, the tall golden reed bed, the low stone wall and the small timber jetty at the water's edge, the empty park bench on the left. The pond fills the lower third and mirrors everything like a Kingdom Two Crowns reflection, with lily pads and a few floating maple leaves, and a dark blurred foreground of autumn leaves and grass along the bottom edge. Leave calm, simple sky in the upper middle (the game puts its title there). Cozy and beautiful, and only on a second look a little too still: in the reflection right below the jetty, one small patch reflects something that is not there - a faint rusty blue-green girder - very subtle.",
- "review": "🟡 Drawn 24 Oct for the prologue menu (workstream 5); exported to game/public/assets/ui/menu-key.png with scripts/export_key_art.py, awaiting Alexander's review."
+ "review": "🟡 Drawn 24 Oct for the prologue menu (workstream 5); exported to game/public/assets/ui/menu-key.png with scripts/export_key_art.py, awaiting Alexander's review."
},
{
"id": "intro-anomaly",
- "title": "Intro · the lake anomaly",
+ "title": "Intro · the lake anomaly",
"factor": 2,
"inputs": [
"repo:art/sprites/images/hero-preview.png",
@@ -1132,7 +1132,7 @@
},
{
"id": "rubber-chicken",
- "title": "Secret · the rubber chicken",
+ "title": "Secret · the rubber chicken",
"factor": 2,
"inputs": [
"repo:art/sprites/images/mensa-food-preview.png",
@@ -1155,11 +1155,11 @@
]
]
},
- "review": "🟨 Awaiting Alexander's review (25 Oct). Playtest T2 (Lex): the rubber chicken must be in the game. First draw, two frames (resting, squeezed mid-squeak); about 34 x 9 px in the game, a little bigger than a real one so it reads next to the 76 px hero."
+ "review": "🟨 Awaiting Alexander's review (25 Oct). Playtest T2 (Lex): the rubber chicken must be in the game. First draw, two frames (resting, squeezed mid-squeak); about 34 x 9 px in the game, a little bigger than a real one so it reads next to the 76 px hero."
},
{
"id": "arena-wall",
- "title": "Prop · Jenny's arena wall",
+ "title": "Prop · Jenny's arena wall",
"factor": 2,
"inputs": [
"repo:art/sprites/images/coffee-machine-preview.png",
@@ -1180,7 +1180,54 @@
]
},
"despeckle": 12,
- "review": "🟨 Awaiting Alexander's review (25 Oct). Playtest J7: the arena's walls were code-drawn rectangles with hazard stripes. First draw: a barrier column of red-and-white crowd-control panels strapped to a rusted pole, with a caged warning lamp (lit, dark). 300 px tall in the game, as tall as the walls. The lamp's halo specks are dropped by \"despeckle\"."
+ "review": "🟨 Awaiting Alexander's review (25 Oct). Playtest J7: the arena's walls were code-drawn rectangles with hazard stripes. First draw: a barrier column of red-and-white crowd-control panels strapped to a rusted pole, with a caged warning lamp (lit, dark). 300 px tall in the game, as tall as the walls. The lamp's halo specks are dropped by \"despeckle\"."
+ },
+ {
+ "id": "arena-platform",
+ "title": "Prop · Jenny's arena platforms",
+ "factor": 2,
+ "inputs": [
+ "repo:art/sprites/images/coffee-machine-preview.png",
+ "repo:art/stages/rusty-creek/arena/girder-reference.png",
+ "repo:art/direction/images/style-master-preview.png"
+ ],
+ "prompt": "On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground shadows, no dust under the objects), with NO text at all - no titles, no captions, no frame numbers, no labels, no readable words or logos: Image 1 is an approved prop sheet from this game: draw in exactly that pixel-art look - solid three-dimensional objects drawn as detailed 2D pixel art with volume, the same pixel grain, palette and warm rim light. Image 2 shows the rusty steel girders of the level these props hang in: copy their material exactly - pale blue-grey painted steel eaten by orange rust, riveted flanges, a dark grated underside. Prop sprite sheet, 2 rows. Row 1, exactly 1 frame: ONE short girder PLATFORM segment hung like a stage rigging bar, seen from slightly below like image 2: a long flat box beam about FIVE times as long as it is tall, its front face with rivets, a lit top flange, a dark grated underside with depth, solid end caps showing its thickness; on top, near each end, a heavy steel eye-bolt with the lowest three links of a rusted chain rising straight up from it. Row 2, exactly 1 frame: ONE long straight vertical rusted steel CHAIN hanging perfectly straight up and down, many identical links, about as long as the platform in row 1, the same chain as on the platform. Both rows at the SAME pixel scale. Rows separated by a wide empty magenta band. No people, no scenery, no sky.",
+ "atlas": {
+ "height": 25,
+ "rows": [
+ [
+ {
+ "anim": "platform",
+ "frames": 1
+ }
+ ]
+ ]
+ },
+ "locked": true,
+ "review": "🟨 Awaiting Alexander's review (25 Oct). Replaces the strip cut from the maze plate, which read flat and floated with no reason (Alexander). Drawn with volume from the coffee-machine sheet's look and the maze's own girder as the material reference: a riveted box girder with a grated underside, 202 × 25 px in the game. The prompt asked for eye-bolts and chain stubs, and the model drew the platform alone, so it is locked and the chains are their own board."
+ },
+ {
+ "id": "arena-chain",
+ "title": "Prop · the platforms' chains",
+ "factor": 2,
+ "inputs": [
+ "repo:art/sprites/images/arena-platform-preview.png",
+ "repo:art/sprites/images/coffee-machine-preview.png",
+ "repo:art/direction/images/style-master-preview.png"
+ ],
+ "prompt": "On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground shadows), with NO text at all - no titles, no captions, no labels: Image 1 is an approved prop from this game, a rusty steel girder platform: draw in exactly its pixel-art look, pixel grain, palette and rusty orange-on-steel colours. Prop sprite sheet, 1 row, exactly 2 frames, side by side with a wide magenta gap: frame 1, ONE heavy rusted steel CHAIN hanging perfectly straight and vertical, about as long as the platform in image 1 is wide, made of many identical oval links alternating face-on and edge-on, the top and bottom links cut flat; frame 2, ONE heavy steel EYE-BOLT anchor: a small rusted base plate with two rivets and a thick eye ring on top, with a single rusted shackle through the ring - the piece that would sit on top of the platform where the chain is fixed. Both frames at the SAME pixel scale (the chain's links as thick as the shackle). No platform, no people, no scenery.",
+ "atlas": {
+ "height": 120,
+ "rows": [
+ [
+ {
+ "anim": "chain",
+ "frames": 2
+ }
+ ]
+ ]
+ },
+ "review": "🟨 Awaiting Alexander's review (25 Oct). The platforms hang from the girder over the arena: scripts/arena_platforms.py tiles this chain's link period from each platform end up to the girder's underside. The second frame came out as a second chain instead of an eye-bolt, so it is unused."
}
]
}
diff --git a/art/sprites/boss-jenny-2.md b/art/sprites/boss-jenny-2.md
index 521ba86..5334b22 100644
--- a/art/sprites/boss-jenny-2.md
+++ b/art/sprites/boss-jenny-2.md
@@ -1,12 +1,12 @@
-# Boss · Jenny, stunned and debris
+# Boss · Jenny, stunned and debris
-[← Boss · Jenny, attacks](boss-jenny.md) · [Sprite boards](README.md) · **17 / 29** · [Item · pocket computer →](pocket-computer.md)
+[← Boss · Jenny, attacks](boss-jenny.md) · [Sprite boards](README.md) · **17 / 31** · [Item · pocket computer →](pocket-computer.md)
-
+
-**Review:** ✅ Approved 24 Oct by Alexander. 270 px long like the approved direction (docs/04), one livery in both sheets; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 24 Oct by Alexander. 270 px long like the approved direction (docs/04), one livery in both sheets; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -29,5 +29,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/boss-jenny-2.png` · rebuild: `python scripts/make_boards.py draw boss-jenny-2 --redo --config art/sprites/boards.json`
-[← Boss · Jenny, attacks](boss-jenny.md) · [Sprite boards](README.md) · **17 / 29** · [Item · pocket computer →](pocket-computer.md)
+[← Boss · Jenny, attacks](boss-jenny.md) · [Sprite boards](README.md) · **17 / 31** · [Item · pocket computer →](pocket-computer.md)
diff --git a/art/sprites/boss-jenny.md b/art/sprites/boss-jenny.md
index f03ded6..75e189f 100644
--- a/art/sprites/boss-jenny.md
+++ b/art/sprites/boss-jenny.md
@@ -1,12 +1,12 @@
-# Boss · Jenny, attacks
+# Boss · Jenny, attacks
-[← FX · black fluid](fx.md) · [Sprite boards](README.md) · **16 / 29** · [Boss · Jenny, stunned and debris →](boss-jenny-2.md)
+[← FX · black fluid](fx.md) · [Sprite boards](README.md) · **16 / 31** · [Boss · Jenny, stunned and debris →](boss-jenny-2.md)
-
+
-**Review:** ✅ Approved 24 Oct by Alexander. 270 px long like the approved direction (docs/04), one livery in both sheets; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 24 Oct by Alexander. 270 px long like the approved direction (docs/04), one livery in both sheets; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -29,5 +29,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/boss-jenny.png` · rebuild: `python scripts/make_boards.py draw boss-jenny --redo --config art/sprites/boards.json`
-[← FX · black fluid](fx.md) · [Sprite boards](README.md) · **16 / 29** · [Boss · Jenny, stunned and debris →](boss-jenny-2.md)
+[← FX · black fluid](fx.md) · [Sprite boards](README.md) · **16 / 31** · [Boss · Jenny, stunned and debris →](boss-jenny-2.md)
diff --git a/art/sprites/chair-block.md b/art/sprites/chair-block.md
index f3fdbc1..867fd45 100644
--- a/art/sprites/chair-block.md
+++ b/art/sprites/chair-block.md
@@ -1,12 +1,12 @@
-# Chair · block
+# Chair · block
-[← Chair · overhead slam](chair-overhead.md) · [Sprite boards](README.md) · **11 / 29** · [Enemy · drone →](enemy-drone.md)
+[← Chair · overhead slam](chair-overhead.md) · [Sprite boards](README.md) · **11 / 31** · [Enemy · drone →](enemy-drone.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -30,5 +30,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/chair-block.png` · rebuild: `python scripts/make_boards.py draw chair-block --redo --config art/sprites/boards.json`
-[← Chair · overhead slam](chair-overhead.md) · [Sprite boards](README.md) · **11 / 29** · [Enemy · drone →](enemy-drone.md)
+[← Chair · overhead slam](chair-overhead.md) · [Sprite boards](README.md) · **11 / 31** · [Enemy · drone →](enemy-drone.md)
diff --git a/art/sprites/chair-overhead.md b/art/sprites/chair-overhead.md
index 4b9385c..8b0a1c6 100644
--- a/art/sprites/chair-overhead.md
+++ b/art/sprites/chair-overhead.md
@@ -1,12 +1,12 @@
-# Chair · overhead slam
+# Chair · overhead slam
-[← Chair · side sweep](chair-side.md) · [Sprite boards](README.md) · **10 / 29** · [Chair · block →](chair-block.md)
+[← Chair · side sweep](chair-side.md) · [Sprite boards](README.md) · **10 / 31** · [Chair · block →](chair-block.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -31,5 +31,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/chair-overhead.png` · rebuild: `python scripts/make_boards.py draw chair-overhead --redo --config art/sprites/boards.json`
-[← Chair · side sweep](chair-side.md) · [Sprite boards](README.md) · **10 / 29** · [Chair · block →](chair-block.md)
+[← Chair · side sweep](chair-side.md) · [Sprite boards](README.md) · **10 / 31** · [Chair · block →](chair-block.md)
diff --git a/art/sprites/chair-side.md b/art/sprites/chair-side.md
index 83e3229..8f21b8a 100644
--- a/art/sprites/chair-side.md
+++ b/art/sprites/chair-side.md
@@ -1,12 +1,12 @@
-# Chair · side sweep
+# Chair · side sweep
-[← The student · ladder top and bottom](hero-ladder.md) · [Sprite boards](README.md) · **9 / 29** · [Chair · overhead slam →](chair-overhead.md)
+[← The student · ladder top and bottom](hero-ladder.md) · [Sprite boards](README.md) · **9 / 31** · [Chair · overhead slam →](chair-overhead.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -30,5 +30,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/chair-side.png` · rebuild: `python scripts/make_boards.py draw chair-side --redo --config art/sprites/boards.json`
-[← The student · ladder top and bottom](hero-ladder.md) · [Sprite boards](README.md) · **9 / 29** · [Chair · overhead slam →](chair-overhead.md)
+[← The student · ladder top and bottom](hero-ladder.md) · [Sprite boards](README.md) · **9 / 31** · [Chair · overhead slam →](chair-overhead.md)
diff --git a/art/sprites/coffee-machine.md b/art/sprites/coffee-machine.md
index 576d057..7ce6f68 100644
--- a/art/sprites/coffee-machine.md
+++ b/art/sprites/coffee-machine.md
@@ -1,12 +1,12 @@
-# Prop · coffee machine (save point)
+# Prop · coffee machine (save point)
-[← Mensa · food pickups](mensa-food.md) · [Sprite boards](README.md) · **20 / 29** · [Dexter · moves →](dexter.md)
+[← Mensa · food pickups](mensa-food.md) · [Sprite boards](README.md) · **20 / 31** · [Dexter · moves →](dexter.md)
-
+
-**Review:** ✅ Approved 24 Oct by Alexander (#154); exported to game/public/assets/sprites/ with export_atlas.py. 42 × 80 px in the game: fills the 40 × 80 coffee-1 box in Rusty Creek, a little taller than the 76 px hero on its base cabinet, as in the photos. Lineup in the stage: previews/coffee-visit-lineup.png.
+**Review:** ✅ Approved 24 Oct by Alexander (#154); exported to game/public/assets/sprites/ with export_atlas.py. 42 × 80 px in the game: fills the 40 × 80 coffee-1 box in Rusty Creek, a little taller than the 76 px hero on its base cabinet, as in the photos. Lineup in the stage: previews/coffee-visit-lineup.png.
## In motion
@@ -27,5 +27,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/coffee-machine.png` · rebuild: `python scripts/make_boards.py draw coffee-machine --redo --config art/sprites/boards.json`
-[← Mensa · food pickups](mensa-food.md) · [Sprite boards](README.md) · **20 / 29** · [Dexter · moves →](dexter.md)
+[← Mensa · food pickups](mensa-food.md) · [Sprite boards](README.md) · **20 / 31** · [Dexter · moves →](dexter.md)
diff --git a/art/sprites/dexter-acts.md b/art/sprites/dexter-acts.md
index dbba39d..1833da5 100644
--- a/art/sprites/dexter-acts.md
+++ b/art/sprites/dexter-acts.md
@@ -1,12 +1,12 @@
-# Dexter · gestures
+# Dexter · gestures
-[← Dexter · moves](dexter.md) · [Sprite boards](README.md) · **22 / 29** · [FX · falling leaves →](fx-leaves.md)
+[← Dexter · moves](dexter.md) · [Sprite boards](README.md) · **22 / 31** · [FX · falling leaves →](fx-leaves.md)
-
+
-**Review:** ✅ Approved 24 Oct by Alexander (#141). 27 px = 0.35 of the hero as in the approved Dexter stages (docs/04, compare.py lineup); exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 24 Oct by Alexander (#141). 27 px = 0.35 of the hero as in the approved Dexter stages (docs/04, compare.py lineup); exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -30,5 +30,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/dexter-acts.png` · rebuild: `python scripts/make_boards.py draw dexter-acts --redo --config art/sprites/boards.json`
-[← Dexter · moves](dexter.md) · [Sprite boards](README.md) · **22 / 29** · [FX · falling leaves →](fx-leaves.md)
+[← Dexter · moves](dexter.md) · [Sprite boards](README.md) · **22 / 31** · [FX · falling leaves →](fx-leaves.md)
diff --git a/art/sprites/dexter-prologue.md b/art/sprites/dexter-prologue.md
index 3feda47..2e72984 100644
--- a/art/sprites/dexter-prologue.md
+++ b/art/sprites/dexter-prologue.md
@@ -1,12 +1,12 @@
-# Dexter · prologue additions
+# Dexter · prologue additions
-[← FX · pond ripples and lily pad](fx-water.md) · [Sprite boards](README.md) · **26 / 29** · [Menu · the Hidden Garden at first light →](menu-key.md)
+[← FX · pond ripples and lily pad](fx-water.md) · [Sprite boards](README.md) · **26 / 31** · [Menu · the Hidden Garden at first light →](menu-key.md)
-
+
-**Review:** ✅ Approved 24 Oct by Alexander; exported to game/public/assets/sprites/ with export_atlas.py.
+**Review:** ✅ Approved 24 Oct by Alexander; exported to game/public/assets/sprites/ with export_atlas.py.
## In motion
@@ -29,5 +29,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/dexter-prologue.png` · rebuild: `python scripts/make_boards.py draw dexter-prologue --redo --config art/sprites/boards.json`
-[← FX · pond ripples and lily pad](fx-water.md) · [Sprite boards](README.md) · **26 / 29** · [Menu · the Hidden Garden at first light →](menu-key.md)
+[← FX · pond ripples and lily pad](fx-water.md) · [Sprite boards](README.md) · **26 / 31** · [Menu · the Hidden Garden at first light →](menu-key.md)
diff --git a/art/sprites/dexter.md b/art/sprites/dexter.md
index 46db0a2..57c38f6 100644
--- a/art/sprites/dexter.md
+++ b/art/sprites/dexter.md
@@ -1,12 +1,12 @@
-# Dexter · moves
+# Dexter · moves
-[← Prop · coffee machine (save point)](coffee-machine.md) · [Sprite boards](README.md) · **21 / 29** · [Dexter · gestures →](dexter-acts.md)
+[← Prop · coffee machine (save point)](coffee-machine.md) · [Sprite boards](README.md) · **21 / 31** · [Dexter · gestures →](dexter-acts.md)
-
+
-**Review:** ✅ Approved 24 Oct by Alexander (#141). 27 px = 0.35 of the hero as in the approved Dexter stages (docs/04, compare.py lineup); exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 24 Oct by Alexander (#141). 27 px = 0.35 of the hero as in the approved Dexter stages (docs/04, compare.py lineup); exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -30,5 +30,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/dexter.png` · rebuild: `python scripts/make_boards.py draw dexter --redo --config art/sprites/boards.json`
-[← Prop · coffee machine (save point)](coffee-machine.md) · [Sprite boards](README.md) · **21 / 29** · [Dexter · gestures →](dexter-acts.md)
+[← Prop · coffee machine (save point)](coffee-machine.md) · [Sprite boards](README.md) · **21 / 31** · [Dexter · gestures →](dexter-acts.md)
diff --git a/art/sprites/enemy-crab.md b/art/sprites/enemy-crab.md
index 38906db..b54db9c 100644
--- a/art/sprites/enemy-crab.md
+++ b/art/sprites/enemy-crab.md
@@ -1,12 +1,12 @@
-# Enemy · crab
+# Enemy · crab
-[← Enemy · rover](enemy-rover.md) · [Sprite boards](README.md) · **14 / 29** · [FX · black fluid →](fx.md)
+[← Enemy · rover](enemy-rover.md) · [Sprite boards](README.md) · **14 / 31** · [FX · black fluid →](fx.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -29,5 +29,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/enemy-crab.png` · rebuild: `python scripts/make_boards.py draw enemy-crab --redo --config art/sprites/boards.json`
-[← Enemy · rover](enemy-rover.md) · [Sprite boards](README.md) · **14 / 29** · [FX · black fluid →](fx.md)
+[← Enemy · rover](enemy-rover.md) · [Sprite boards](README.md) · **14 / 31** · [FX · black fluid →](fx.md)
diff --git a/art/sprites/enemy-drone.md b/art/sprites/enemy-drone.md
index 4d2b4b1..b97ce59 100644
--- a/art/sprites/enemy-drone.md
+++ b/art/sprites/enemy-drone.md
@@ -1,12 +1,12 @@
-# Enemy · drone
+# Enemy · drone
-[← Chair · block](chair-block.md) · [Sprite boards](README.md) · **12 / 29** · [Enemy · rover →](enemy-rover.md)
+[← Chair · block](chair-block.md) · [Sprite boards](README.md) · **12 / 31** · [Enemy · rover →](enemy-rover.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -30,5 +30,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/enemy-drone.png` · rebuild: `python scripts/make_boards.py draw enemy-drone --redo --config art/sprites/boards.json`
-[← Chair · block](chair-block.md) · [Sprite boards](README.md) · **12 / 29** · [Enemy · rover →](enemy-rover.md)
+[← Chair · block](chair-block.md) · [Sprite boards](README.md) · **12 / 31** · [Enemy · rover →](enemy-rover.md)
diff --git a/art/sprites/enemy-rover.md b/art/sprites/enemy-rover.md
index 8976818..c6aa592 100644
--- a/art/sprites/enemy-rover.md
+++ b/art/sprites/enemy-rover.md
@@ -1,12 +1,12 @@
-# Enemy · rover
+# Enemy · rover
-[← Enemy · drone](enemy-drone.md) · [Sprite boards](README.md) · **13 / 29** · [Enemy · crab →](enemy-crab.md)
+[← Enemy · drone](enemy-drone.md) · [Sprite boards](README.md) · **13 / 31** · [Enemy · crab →](enemy-crab.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -29,5 +29,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/enemy-rover.png` · rebuild: `python scripts/make_boards.py draw enemy-rover --redo --config art/sprites/boards.json`
-[← Enemy · drone](enemy-drone.md) · [Sprite boards](README.md) · **13 / 29** · [Enemy · crab →](enemy-crab.md)
+[← Enemy · drone](enemy-drone.md) · [Sprite boards](README.md) · **13 / 31** · [Enemy · crab →](enemy-crab.md)
diff --git a/art/sprites/fx-ground.md b/art/sprites/fx-ground.md
index 717f2cb..758c4aa 100644
--- a/art/sprites/fx-ground.md
+++ b/art/sprites/fx-ground.md
@@ -1,12 +1,12 @@
-# FX · footstep puffs
+# FX · footstep puffs
-[← FX · falling leaves](fx-leaves.md) · [Sprite boards](README.md) · **24 / 29** · [FX · pond ripples and lily pad →](fx-water.md)
+[← FX · falling leaves](fx-leaves.md) · [Sprite boards](README.md) · **24 / 31** · [FX · pond ripples and lily pad →](fx-water.md)
-
+
-**Review:** 🟡 Drawn 24 Oct for the prologue (workstream 5); exported for the slice, awaiting Alexander's review. Dust 21–30 × 5–12 px, grass and leaf kick ≈ 25 × 14, splash ≈ 25 × 16 – about one step wide next to the 76 px hero. Rows are `even` (they break into specks).
+**Review:** 🟡 Drawn 24 Oct for the prologue (workstream 5); exported for the slice, awaiting Alexander's review. Dust 21–30 × 5–12 px, grass and leaf kick ≈ 25 × 14, splash ≈ 25 × 16 – about one step wide next to the 76 px hero. Rows are `even` (they break into specks).
## In motion
@@ -29,5 +29,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/fx-ground.png` · rebuild: `python scripts/make_boards.py draw fx-ground --redo --config art/sprites/boards.json`
-[← FX · falling leaves](fx-leaves.md) · [Sprite boards](README.md) · **24 / 29** · [FX · pond ripples and lily pad →](fx-water.md)
+[← FX · falling leaves](fx-leaves.md) · [Sprite boards](README.md) · **24 / 31** · [FX · pond ripples and lily pad →](fx-water.md)
diff --git a/art/sprites/fx-leaves.md b/art/sprites/fx-leaves.md
index f6b0423..77b6db3 100644
--- a/art/sprites/fx-leaves.md
+++ b/art/sprites/fx-leaves.md
@@ -1,12 +1,12 @@
-# FX · falling leaves
+# FX · falling leaves
-[← Dexter · gestures](dexter-acts.md) · [Sprite boards](README.md) · **23 / 29** · [FX · footstep puffs →](fx-ground.md)
+[← Dexter · gestures](dexter-acts.md) · [Sprite boards](README.md) · **23 / 31** · [FX · footstep puffs →](fx-ground.md)
-
+
-**Review:** 🟡 Drawn 24 Oct for the prologue (workstream 5); exported for the slice, awaiting Alexander's review. Second draw: the first take's thin outlines and fine veins melted into the orange trees at 8 px (6 of 8 test frames LOW in scripts/readability.py); thick dark outlines and lighter fills brought it to 2 of 8. 7–10 px in the game.
+**Review:** 🟡 Drawn 24 Oct for the prologue (workstream 5); exported for the slice, awaiting Alexander's review. Second draw: the first take's thin outlines and fine veins melted into the orange trees at 8 px (6 of 8 test frames LOW in scripts/readability.py); thick dark outlines and lighter fills brought it to 2 of 8. 7–10 px in the game.
## In motion
@@ -29,5 +29,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/fx-leaves.png` · rebuild: `python scripts/make_boards.py draw fx-leaves --redo --config art/sprites/boards.json`
-[← Dexter · gestures](dexter-acts.md) · [Sprite boards](README.md) · **23 / 29** · [FX · footstep puffs →](fx-ground.md)
+[← Dexter · gestures](dexter-acts.md) · [Sprite boards](README.md) · **23 / 31** · [FX · footstep puffs →](fx-ground.md)
diff --git a/art/sprites/fx-water.md b/art/sprites/fx-water.md
index 0955222..d30384b 100644
--- a/art/sprites/fx-water.md
+++ b/art/sprites/fx-water.md
@@ -1,12 +1,12 @@
-# FX · pond ripples and lily pad
+# FX · pond ripples and lily pad
-[← FX · footstep puffs](fx-ground.md) · [Sprite boards](README.md) · **25 / 29** · [Dexter · prologue additions →](dexter-prologue.md)
+[← FX · footstep puffs](fx-ground.md) · [Sprite boards](README.md) · **25 / 31** · [Dexter · prologue additions →](dexter-prologue.md)
-
+
-**Review:** 🟡 Drawn 24 Oct for the prologue (workstream 5); exported for the slice, awaiting Alexander's review. Lily pad 58 × 14–16 px, measured against the painted pads in hg-4-0 (≈ 68 × 14–17); ripple 37–71 px wide; sparkle halved to a 12 px glint. The plate's pads are browner than this olive pad – tint in code if it stands out.
+**Review:** 🟡 Drawn 24 Oct for the prologue (workstream 5); exported for the slice, awaiting Alexander's review. Lily pad 58 × 14–16 px, measured against the painted pads in hg-4-0 (≈ 68 × 14–17); ripple 37–71 px wide; sparkle halved to a 12 px glint. The plate's pads are browner than this olive pad – tint in code if it stands out.
## In motion
@@ -28,5 +28,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/fx-water.png` · rebuild: `python scripts/make_boards.py draw fx-water --redo --config art/sprites/boards.json`
-[← FX · footstep puffs](fx-ground.md) · [Sprite boards](README.md) · **25 / 29** · [Dexter · prologue additions →](dexter-prologue.md)
+[← FX · footstep puffs](fx-ground.md) · [Sprite boards](README.md) · **25 / 31** · [Dexter · prologue additions →](dexter-prologue.md)
diff --git a/art/sprites/fx.md b/art/sprites/fx.md
index c445175..71f0388 100644
--- a/art/sprites/fx.md
+++ b/art/sprites/fx.md
@@ -1,12 +1,12 @@
-# FX · black fluid
+# FX · black fluid
-[← Enemy · crab](enemy-crab.md) · [Sprite boards](README.md) · **15 / 29** · [Boss · Jenny, attacks →](boss-jenny.md)
+[← Enemy · crab](enemy-crab.md) · [Sprite boards](README.md) · **15 / 31** · [Boss · Jenny, attacks →](boss-jenny.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -26,5 +26,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/fx.png` · rebuild: `python scripts/make_boards.py draw fx --redo --config art/sprites/boards.json`
-[← Enemy · crab](enemy-crab.md) · [Sprite boards](README.md) · **15 / 29** · [Boss · Jenny, attacks →](boss-jenny.md)
+[← Enemy · crab](enemy-crab.md) · [Sprite boards](README.md) · **15 / 31** · [Boss · Jenny, attacks →](boss-jenny.md)
diff --git a/art/sprites/hero-air.md b/art/sprites/hero-air.md
index cd605b5..421be17 100644
--- a/art/sprites/hero-air.md
+++ b/art/sprites/hero-air.md
@@ -1,12 +1,12 @@
-# The student · jump, fall, climb
+# The student · jump, fall, climb
-[← The student · death](hero-death.md) · [Sprite boards](README.md) · **7 / 29** · [The student · ladder top and bottom →](hero-ladder.md)
+[← The student · death](hero-death.md) · [Sprite boards](README.md) · **7 / 31** · [The student · ladder top and bottom →](hero-ladder.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -26,5 +26,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/hero-air.png` · rebuild: `python scripts/make_boards.py draw hero-air --redo --config art/sprites/boards.json`
-[← The student · death](hero-death.md) · [Sprite boards](README.md) · **7 / 29** · [The student · ladder top and bottom →](hero-ladder.md)
+[← The student · death](hero-death.md) · [Sprite boards](README.md) · **7 / 31** · [The student · ladder top and bottom →](hero-ladder.md)
diff --git a/art/sprites/hero-death.md b/art/sprites/hero-death.md
index 50b1c8e..62fc554 100644
--- a/art/sprites/hero-death.md
+++ b/art/sprites/hero-death.md
@@ -1,12 +1,12 @@
-# The student · death
+# The student · death
-[← The student · hurt and interact](hero-hurt.md) · [Sprite boards](README.md) · **6 / 29** · [The student · jump, fall, climb →](hero-air.md)
+[← The student · hurt and interact](hero-hurt.md) · [Sprite boards](README.md) · **6 / 31** · [The student · jump, fall, climb →](hero-air.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -26,5 +26,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/hero-death.png` · rebuild: `python scripts/make_boards.py draw hero-death --redo --config art/sprites/boards.json`
-[← The student · hurt and interact](hero-hurt.md) · [Sprite boards](README.md) · **6 / 29** · [The student · jump, fall, climb →](hero-air.md)
+[← The student · hurt and interact](hero-hurt.md) · [Sprite boards](README.md) · **6 / 31** · [The student · jump, fall, climb →](hero-air.md)
diff --git a/art/sprites/hero-hurt.md b/art/sprites/hero-hurt.md
index 6204a09..1f5b205 100644
--- a/art/sprites/hero-hurt.md
+++ b/art/sprites/hero-hurt.md
@@ -1,12 +1,12 @@
-# The student · hurt and interact
+# The student · hurt and interact
-[← The student · run](hero-run.md) · [Sprite boards](README.md) · **5 / 29** · [The student · death →](hero-death.md)
+[← The student · run](hero-run.md) · [Sprite boards](README.md) · **5 / 31** · [The student · death →](hero-death.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -26,5 +26,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/hero-hurt.png` · rebuild: `python scripts/make_boards.py draw hero-hurt --redo --config art/sprites/boards.json`
-[← The student · run](hero-run.md) · [Sprite boards](README.md) · **5 / 29** · [The student · death →](hero-death.md)
+[← The student · run](hero-run.md) · [Sprite boards](README.md) · **5 / 31** · [The student · death →](hero-death.md)
diff --git a/art/sprites/hero-idle.md b/art/sprites/hero-idle.md
index e67d68e..4e627c0 100644
--- a/art/sprites/hero-idle.md
+++ b/art/sprites/hero-idle.md
@@ -1,12 +1,12 @@
-# The student · idle variants
+# The student · idle variants
-[← The student · walk](hero-move.md) · [Sprite boards](README.md) · **3 / 29** · [The student · run →](hero-run.md)
+[← The student · walk](hero-move.md) · [Sprite boards](README.md) · **3 / 31** · [The student · run →](hero-run.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -26,5 +26,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/hero-idle.png` · rebuild: `python scripts/make_boards.py draw hero-idle --redo --config art/sprites/boards.json`
-[← The student · walk](hero-move.md) · [Sprite boards](README.md) · **3 / 29** · [The student · run →](hero-run.md)
+[← The student · walk](hero-move.md) · [Sprite boards](README.md) · **3 / 31** · [The student · run →](hero-run.md)
diff --git a/art/sprites/hero-ladder.md b/art/sprites/hero-ladder.md
index 7f5fa05..92fab68 100644
--- a/art/sprites/hero-ladder.md
+++ b/art/sprites/hero-ladder.md
@@ -1,10 +1,10 @@
-# The student · ladder top and bottom
+# The student · ladder top and bottom
-[← The student · jump, fall, climb](hero-air.md) · [Sprite boards](README.md) · **8 / 29** · [Chair · side sweep →](chair-side.md)
+[← The student · jump, fall, climb](hero-air.md) · [Sprite boards](README.md) · **8 / 31** · [Chair · side sweep →](chair-side.md)
-
+
**Review:** Not reviewed yet.
@@ -26,5 +26,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/hero-ladder.png` · rebuild: `python scripts/make_boards.py draw hero-ladder --redo --config art/sprites/boards.json`
-[← The student · jump, fall, climb](hero-air.md) · [Sprite boards](README.md) · **8 / 29** · [Chair · side sweep →](chair-side.md)
+[← The student · jump, fall, climb](hero-air.md) · [Sprite boards](README.md) · **8 / 31** · [Chair · side sweep →](chair-side.md)
diff --git a/art/sprites/hero-move.md b/art/sprites/hero-move.md
index b710510..e0b5e21 100644
--- a/art/sprites/hero-move.md
+++ b/art/sprites/hero-move.md
@@ -1,12 +1,12 @@
-# The student · walk
+# The student · walk
-[← The student · hero design](hero.md) · [Sprite boards](README.md) · **2 / 29** · [The student · idle variants →](hero-idle.md)
+[← The student · hero design](hero.md) · [Sprite boards](README.md) · **2 / 31** · [The student · idle variants →](hero-idle.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -26,5 +26,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/hero-move.png` · rebuild: `python scripts/make_boards.py draw hero-move --redo --config art/sprites/boards.json`
-[← The student · hero design](hero.md) · [Sprite boards](README.md) · **2 / 29** · [The student · idle variants →](hero-idle.md)
+[← The student · hero design](hero.md) · [Sprite boards](README.md) · **2 / 31** · [The student · idle variants →](hero-idle.md)
diff --git a/art/sprites/hero-run.md b/art/sprites/hero-run.md
index 8987cd1..b0e9f94 100644
--- a/art/sprites/hero-run.md
+++ b/art/sprites/hero-run.md
@@ -1,12 +1,12 @@
-# The student · run
+# The student · run
-[← The student · idle variants](hero-idle.md) · [Sprite boards](README.md) · **4 / 29** · [The student · hurt and interact →](hero-hurt.md)
+[← The student · idle variants](hero-idle.md) · [Sprite boards](README.md) · **4 / 31** · [The student · hurt and interact →](hero-hurt.md)
-
+
-**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
+**Review:** ✅ Approved 23 Oct by Alexander. Counts match docs/07; exported to game/public/assets/sprites/ with export_atlas.py. The looping previews below are at twice game size.
## In motion
@@ -26,5 +26,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/hero-run.png` · rebuild: `python scripts/make_boards.py draw hero-run --redo --config art/sprites/boards.json`
-[← The student · idle variants](hero-idle.md) · [Sprite boards](README.md) · **4 / 29** · [The student · hurt and interact →](hero-hurt.md)
+[← The student · idle variants](hero-idle.md) · [Sprite boards](README.md) · **4 / 31** · [The student · hurt and interact →](hero-hurt.md)
diff --git a/art/sprites/hero.md b/art/sprites/hero.md
index 2580a90..9f41122 100644
--- a/art/sprites/hero.md
+++ b/art/sprites/hero.md
@@ -1,12 +1,12 @@
-# The student · hero design
+# The student · hero design
-[← Sprite boards](README.md) · [Sprite boards](README.md) · **1 / 29** · [The student · walk →](hero-move.md)
+[← Sprite boards](README.md) · [Sprite boards](README.md) · **1 / 31** · [The student · walk →](hero-move.md)
-
+
-**Review:** ✅ Approved 23 Oct. Adult 6 ft, face never visible, THD poncho in grey / THD blue / teal with colourful accents, THD crest on the back, scale lineup with Dexter (1.5 ft) and his final form (8.5 ft). Fourth iteration: chibi proportions, a hoodie and a visible face were rejected along the way.
+**Review:** ✅ Approved 23 Oct. Adult 6 ft, face never visible, THD poncho in grey / THD blue / teal with colourful accents, THD crest on the back, scale lineup with Dexter (1.5 ft) and his final form (8.5 ft). Fourth iteration: chibi proportions, a hoodie and a visible face were rejected along the way.
## Brief
@@ -21,5 +21,5 @@ Keep the body, height (6 ft adult), proportions, pose, backpack, grey slim jeans
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/hero.png` · rebuild: `python scripts/make_boards.py draw hero --redo --config art/sprites/boards.json`
-[← Sprite boards](README.md) · [Sprite boards](README.md) · **1 / 29** · [The student · walk →](hero-move.md)
+[← Sprite boards](README.md) · [Sprite boards](README.md) · **1 / 31** · [The student · walk →](hero-move.md)
diff --git a/art/sprites/images/arena-chain-preview.png b/art/sprites/images/arena-chain-preview.png
new file mode 100644
index 0000000..4b8738c
Binary files /dev/null and b/art/sprites/images/arena-chain-preview.png differ
diff --git a/art/sprites/images/arena-chain.png b/art/sprites/images/arena-chain.png
new file mode 100644
index 0000000..ce40a58
Binary files /dev/null and b/art/sprites/images/arena-chain.png differ
diff --git a/art/sprites/images/arena-platform-preview.png b/art/sprites/images/arena-platform-preview.png
new file mode 100644
index 0000000..535de77
Binary files /dev/null and b/art/sprites/images/arena-platform-preview.png differ
diff --git a/art/sprites/images/arena-platform.png b/art/sprites/images/arena-platform.png
new file mode 100644
index 0000000..52a5859
Binary files /dev/null and b/art/sprites/images/arena-platform.png differ
diff --git a/art/sprites/mensa-food.md b/art/sprites/mensa-food.md
index adb77e1..1838a1b 100644
--- a/art/sprites/mensa-food.md
+++ b/art/sprites/mensa-food.md
@@ -1,12 +1,12 @@
-# Mensa · food pickups
+# Mensa · food pickups
-[← Item · pocket computer](pocket-computer.md) · [Sprite boards](README.md) · **19 / 29** · [Prop · coffee machine (save point) →](coffee-machine.md)
+[← Item · pocket computer](pocket-computer.md) · [Sprite boards](README.md) · **19 / 31** · [Prop · coffee machine (save point) →](coffee-machine.md)
-
+
-**Review:** ✅ Approved 24 Oct by Alexander (#154); exported to game/public/assets/sprites/ with export_atlas.py. Third draw: the first two copied only the muffin; anchoring on the approved coffee-machine sheet gave four distinct items. One frame each (the model ignored the sparkle frames; the game adds the sparkle). Muffin 16 × 14, cake 18 × 14, wafer 17 × 13, Leberkäse roll 18 × 18 px – around the documented 16 px.
+**Review:** ✅ Approved 24 Oct by Alexander (#154); exported to game/public/assets/sprites/ with export_atlas.py. Third draw: the first two copied only the muffin; anchoring on the approved coffee-machine sheet gave four distinct items. One frame each (the model ignored the sparkle frames; the game adds the sparkle). Muffin 16 × 14, cake 18 × 14, wafer 17 × 13, Leberkäse roll 18 × 18 px – around the documented 16 px.
## In motion
@@ -30,5 +30,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/mensa-food.png` · rebuild: `python scripts/make_boards.py draw mensa-food --redo --config art/sprites/boards.json`
-[← Item · pocket computer](pocket-computer.md) · [Sprite boards](README.md) · **19 / 29** · [Prop · coffee machine (save point) →](coffee-machine.md)
+[← Item · pocket computer](pocket-computer.md) · [Sprite boards](README.md) · **19 / 31** · [Prop · coffee machine (save point) →](coffee-machine.md)
diff --git a/art/sprites/menu-key.md b/art/sprites/menu-key.md
index 203b962..02d547f 100644
--- a/art/sprites/menu-key.md
+++ b/art/sprites/menu-key.md
@@ -1,12 +1,12 @@
-# Menu · the Hidden Garden at first light
+# Menu · the Hidden Garden at first light
-[← Dexter · prologue additions](dexter-prologue.md) · [Sprite boards](README.md) · **27 / 29** · [Secret · the rubber chicken →](rubber-chicken.md)
+[← Dexter · prologue additions](dexter-prologue.md) · [Sprite boards](README.md) · **27 / 31** · [Secret · the rubber chicken →](rubber-chicken.md)
-
+
-**Review:** 🟡 Drawn 24 Oct for the prologue menu (workstream 5); exported to game/public/assets/ui/menu-key.png with scripts/export_key_art.py, awaiting Alexander's review.
+**Review:** 🟡 Drawn 24 Oct for the prologue menu (workstream 5); exported to game/public/assets/ui/menu-key.png with scripts/export_key_art.py, awaiting Alexander's review.
## Brief
@@ -22,5 +22,5 @@ Key art for the game's title menu, a full 16:9 picture in exactly the hi-bit pix
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/menu-key.png` · rebuild: `python scripts/make_boards.py draw menu-key --redo --config art/sprites/boards.json`
-[← Dexter · prologue additions](dexter-prologue.md) · [Sprite boards](README.md) · **27 / 29** · [Secret · the rubber chicken →](rubber-chicken.md)
+[← Dexter · prologue additions](dexter-prologue.md) · [Sprite boards](README.md) · **27 / 31** · [Secret · the rubber chicken →](rubber-chicken.md)
diff --git a/art/sprites/pocket-computer.md b/art/sprites/pocket-computer.md
index c77a836..8af85ac 100644
--- a/art/sprites/pocket-computer.md
+++ b/art/sprites/pocket-computer.md
@@ -1,12 +1,12 @@
-# Item · pocket computer
+# Item · pocket computer
-[← Boss · Jenny, stunned and debris](boss-jenny-2.md) · [Sprite boards](README.md) · **18 / 29** · [Mensa · food pickups →](mensa-food.md)
+[← Boss · Jenny, stunned and debris](boss-jenny-2.md) · [Sprite boards](README.md) · **18 / 31** · [Mensa · food pickups →](mensa-food.md)
-
+
-**Review:** ✅ Approved 24 Oct by Alexander (#154); exported to game/public/assets/sprites/ with export_atlas.py. Closed case 19 × 10 px (≈ 45 cm at the hero's 76 px = 183 cm): the photographed suitcase, shrunk to carry; open with the green screen 22 px tall. The map screens are the approved UI atlas (#127), so this board has no screen mock-ups.
+**Review:** ✅ Approved 24 Oct by Alexander (#154); exported to game/public/assets/sprites/ with export_atlas.py. Closed case 19 × 10 px (≈ 45 cm at the hero's 76 px = 183 cm): the photographed suitcase, shrunk to carry; open with the green screen 22 px tall. The map screens are the approved UI atlas (#127), so this board has no screen mock-ups.
## In motion
@@ -28,5 +28,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/pocket-computer.png` · rebuild: `python scripts/make_boards.py draw pocket-computer --redo --config art/sprites/boards.json`
-[← Boss · Jenny, stunned and debris](boss-jenny-2.md) · [Sprite boards](README.md) · **18 / 29** · [Mensa · food pickups →](mensa-food.md)
+[← Boss · Jenny, stunned and debris](boss-jenny-2.md) · [Sprite boards](README.md) · **18 / 31** · [Mensa · food pickups →](mensa-food.md)
diff --git a/art/sprites/rubber-chicken.md b/art/sprites/rubber-chicken.md
index 3150e54..b13b1a5 100644
--- a/art/sprites/rubber-chicken.md
+++ b/art/sprites/rubber-chicken.md
@@ -1,12 +1,12 @@
-# Secret · the rubber chicken
+# Secret · the rubber chicken
-[← Menu · the Hidden Garden at first light](menu-key.md) · [Sprite boards](README.md) · **28 / 29** · [Prop · Jenny's arena wall →](arena-wall.md)
+[← Menu · the Hidden Garden at first light](menu-key.md) · [Sprite boards](README.md) · **28 / 31** · [Prop · Jenny's arena wall →](arena-wall.md)
-
+
-**Review:** 🟨 Awaiting Alexander's review (25 Oct). Playtest T2 (Lex): the rubber chicken must be in the game. First draw, two frames (resting, squeezed mid-squeak); about 34 x 9 px in the game, a little bigger than a real one so it reads next to the 76 px hero.
+**Review:** 🟨 Awaiting Alexander's review (25 Oct). Playtest T2 (Lex): the rubber chicken must be in the game. First draw, two frames (resting, squeezed mid-squeak); about 34 x 9 px in the game, a little bigger than a real one so it reads next to the 76 px hero.
## Brief
@@ -20,5 +20,5 @@ On a flat, solid, pure magenta #FF00FF background (no paper colour, no ground sh
Model `gpt-image-2.5-sunburst` · pixel factor 2 · source image `images/rubber-chicken.png` · rebuild: `python scripts/make_boards.py draw rubber-chicken --redo --config art/sprites/boards.json`
-[← Menu · the Hidden Garden at first light](menu-key.md) · [Sprite boards](README.md) · **28 / 29** · [Prop · Jenny's arena wall →](arena-wall.md)
+[← Menu · the Hidden Garden at first light](menu-key.md) · [Sprite boards](README.md) · **28 / 31** · [Prop · Jenny's arena wall →](arena-wall.md)
diff --git a/art/stages/rusty-creek/arena/girder-reference.png b/art/stages/rusty-creek/arena/girder-reference.png
new file mode 100644
index 0000000..644ee24
Binary files /dev/null and b/art/stages/rusty-creek/arena/girder-reference.png differ
diff --git a/art/stages/rusty-creek/arena/right.png b/art/stages/rusty-creek/arena/right.png
index dfb8568..3c125c0 100644
Binary files a/art/stages/rusty-creek/arena/right.png and b/art/stages/rusty-creek/arena/right.png differ
diff --git a/art/stages/rusty-creek/previews/arena-platforms.png b/art/stages/rusty-creek/previews/arena-platforms.png
index 55520db..e62807c 100644
Binary files a/art/stages/rusty-creek/previews/arena-platforms.png and b/art/stages/rusty-creek/previews/arena-platforms.png differ
diff --git a/art/stages/rusty-creek/rc-2-0.anomaly.collision.json b/art/stages/rusty-creek/rc-2-0.anomaly.collision.json
index 82a34e7..4f2cbf6 100644
--- a/art/stages/rusty-creek/rc-2-0.anomaly.collision.json
+++ b/art/stages/rusty-creek/rc-2-0.anomaly.collision.json
@@ -225,21 +225,21 @@
],
[
[
- 1220,
+ 1180,
476
],
[
- 1370,
+ 1379,
476
]
],
[
[
- 1460,
+ 1430,
476
],
[
- 1610,
+ 1629,
476
]
]
diff --git a/docs/02-GAME-DESIGN.md b/docs/02-GAME-DESIGN.md
index 065ceab..b062f1f 100644
--- a/docs/02-GAME-DESIGN.md
+++ b/docs/02-GAME-DESIGN.md
@@ -48,7 +48,7 @@ A ☰ button in the top-right corner, Esc or the pad's Start open the **game men
- **Controls** – the table above, for keyboard and gamepad.
- **Settings** – sound on/off and volume, zoom (the game's own view or 133 / 200 / 286 % wider), full screen, **Hero** (a brighter, cooler outline on the hero, easier to see against the leaves; playtest R1), and **Keys**. In Keys, click an action and press its new key (Esc keeps the old one). The key then belongs to that action alone and is taken from any other. The choice is saved in the browser, and "Reset to the defaults" brings back both laptop layouts. Esc, Tab and the debug keys stay the game's (`player/physicalKeys.ts` `Bindings`).
- **Debug** – debug mode and its switches (`game/src/core/debug.ts`); the corner line names every switch that is on, so a playtest screenshot records what was showing:
- - **Layers**: every stage layer by id and z, switched off in all chunks at once; with the **purple backdrop** any hole or seam in a plate behind it shows at once (the hot-dog rule, docs/09).
+ - **Layers**: every stage layer by id and z, switched off in all chunks at once; with the **purple backdrop** any hole or seam in a plate behind it shows at once (the hot-dog rule, docs/09). **White instead** shows what purple hides: dark specks and a dark fringe. Check every layer against both.
- **Collision**: walk lines, one-way beams, ladders, walls, water, hazards, triggers (the F2 legend).
- **Actors**: the hero's body, speed and ground; every rover, crab and drone with its hitbox, facing arrow, sight box (red when it sees the hero), patrol path and turn points, phase and health; Jenny's arena, box, facing and phase; Dexter's position, state and next stop.
- **FPS and position**: frame rate, the hero's position and chunk.
diff --git a/docs/09-ART-PIPELINE.md b/docs/09-ART-PIPELINE.md
index 950aa1f..20323ba 100644
--- a/docs/09-ART-PIPELINE.md
+++ b/docs/09-ART-PIPELINE.md
@@ -127,6 +127,12 @@ flowchart TB
`art/sprites/boards.json`: the hero sheet (locked) comes first, the pose or object photos follow, and the style master goes last. The model draws on a flat magenta key. `crush` keys the magenta at full size, downscales ×2 with premultiplied alpha (so no purple fringes), quantises to 96 colours, and writes a transparent 1× PNG plus a readable preview. Chair photos are split into **block**, **overhead/up-down** and **side** sequences by file name. Every board stays "to do" until the direction is approved; see [07](07-ASSET-MANIFEST.md).
+**Every new sprite and layer is checked over purple, then over white (Alexander, 25 Oct), before it ships:**
+- offline, `python scripts/backdrop_check.py [--holes]` puts it over both and counts key specks, soft pixels and, for plates, enclosed holes;
+- in the game, debug mode → Purple backdrop, and "white instead", with each layer shown alone.
+
+A glow the model draws as a soft halo crushes into scattered specks; `"despeckle": N` drops islands under N px. A plate cut from a soft source keeps its pinholes, so fill them before it ships.
+
Every animation board gets an `atlas` entry when it is drawn, because the review preview is cut with it. Export into the game only once its Design review is **Approved**. `rows` lists the board's rows of sprites from top to bottom: the animations in each row, left to right, with their frame counts from [07](07-ASSET-MANIFEST.md), or `"skip"` for a row that isn't exported (close-ups, scale lineups). `height` is the game height in pixels of the tallest frame of the first animation; the hero's is 76, in an 80 px frame. When the first animation has no standing pose (a jump), `"ref": "jump-08"` names the frame that is 76 px instead. An animation may continue at the start of the next row (death 6 + 6: list it again with the rest of its frames). `"even": true` keeps a row's specks and cuts it along seams without the weight check, for frames that break into dust. `"float": true` on an animation drawn in the air (jump, fall) puts each frame's lowest pixel on the baseline, because the game already lifts the body. `fps` defaults to 12 and `loop` to true. A sheet holds at most about 30 frames in four rows; the model miscounts bigger sheets, so split them (`hero-move`, `hero-run`, `hero-idle`). Open every brief with the magenta background and the no-text rule: put later in the brief, the model sometimes draws on paper with captions.
### How a sprite board is made
diff --git a/docs/meetings/playtest-24-oct.md b/docs/meetings/playtest-24-oct.md
index 5dee97c..5bcc25d 100644
--- a/docs/meetings/playtest-24-oct.md
+++ b/docs/meetings/playtest-24-oct.md
@@ -25,7 +25,7 @@ Vatsal and Lex played the live slice on 24 Oct and recorded what to fix ([A15](t
| J3 | One Jenny hit leaves you nearly dead (3 of 5) → at most half the health | 🔧 |
| J4 | Make it obvious she must crash into something to become hittable – the satisfaction of figuring it out | ✅ crash shake, sound and smoke; stun-ending warning; reboot sparks (fights branch) |
| J5 | Scraps should hurt but must be clearly visible | 🔧 |
-| J6 | An invisible wall in the arena; the arena is "awful to fight in" with limited movement – add platforms in the middle, maybe a slightly higher jump to clear her | ✅ two girder platforms over her floor (`art/stages/rusty-creek/arena/`, an overlay layer): reachable with a full jump, and she drives under them – stand on one and let her pass; the walls are drawn whenever they are closed (#186) |
+| J6 | An invisible wall in the arena; the arena is "awful to fight in" with limited movement – add platforms in the middle, maybe a slightly higher jump to clear her | ✅ two girder platforms hung on chains from the girder over her arena (`scripts/arena_platforms.py`, boards `arena-platform` and `arena-chain`, an overlay layer): reachable with a full jump, and she drives under them – stand on one and let her pass; the walls are drawn whenever they are closed (#186) |
| J7 | After Jenny: a wall at the right end that says "you can't pass until you beat her", not just the camera stopping | ✅ both walls are drawn as rusted hazard-striped barriers whenever they are closed, and the fight (and the walls) start as soon as you are inside the arena (#186); they fade out when she is beaten |
| J8 | Landing on top of an enemy hurts the hero | Intended – no change |
| J9 | Double jump | Rejected (Lex: Metroidvanias give it last or never) |
diff --git a/game/src/core/debug.test.ts b/game/src/core/debug.test.ts
index dc63f7d..598ac9d 100644
--- a/game/src/core/debug.test.ts
+++ b/game/src/core/debug.test.ts
@@ -1,5 +1,5 @@
import { describe as suite, expect, it } from 'vitest';
-import { DebugStore, describe } from './debug';
+import { DebugStore, describe, backdropColour } from './debug';
suite('debug mode switches', () => {
it('shows nothing until debug mode is on', () => {
@@ -27,5 +27,12 @@ suite('debug mode switches', () => {
store.set({ on: true, actors: false, backdrop: true, zoom: 0.5 });
store.toggleLayer('01-far');
expect(describe(store.state)).toBe('debug: backdrop collision stats | hidden: 01-far | zoom 0.5');
+ store.set({ white: true });
+ expect(describe(store.state)).toMatch(/^debug: backdrop-white /);
+ });
+
+ it('paints the backdrop purple, or white to check a layer against both', () => {
+ expect(backdropColour({ white: false })).toBe(0x9b30ff);
+ expect(backdropColour({ white: true })).toBe(0xffffff);
});
});
diff --git a/game/src/core/debug.ts b/game/src/core/debug.ts
index abf5da5..3474725 100644
--- a/game/src/core/debug.ts
+++ b/game/src/core/debug.ts
@@ -8,6 +8,8 @@ export interface DebugState {
on: boolean;
/** The back of the world painted purple, so a hole in any plate shows at once. */
backdrop: boolean;
+ /** The backdrop white instead of purple: dark specks and a dark fringe show there, light ones on purple. */
+ white: boolean;
/** Walk lines, one-way beams, ladders, walls, water, hazards and triggers. */
collision: boolean;
/** Hitboxes, facing, sight, patrol paths and state of the hero, the enemies, Jenny and Dexter. */
@@ -25,7 +27,7 @@ type Listener = (state: DebugState) => void;
export const ZOOMS = [1, 0.75, 0.5, 0.35] as const;
export class DebugStore {
- private value: DebugState = { on: false, backdrop: false, collision: true, actors: true, stats: true, hidden: new Set(), zoom: 1 };
+ private value: DebugState = { on: false, backdrop: false, white: false, collision: true, actors: true, stats: true, hidden: new Set(), zoom: 1 };
private listeners = new Set();
get state(): DebugState { return this.value; }
set(patch: Partial>) { this.value = { ...this.value, ...patch }; this.emit(); }
@@ -44,9 +46,14 @@ export class DebugStore {
export const debugStore = new DebugStore();
/** One line per switch that is on – printed in the corner so a screenshot records the debug setup. */
+/** The debug backdrop's colour: purple, or white when asked (check every layer against both). */
+export const BACKDROPS = { purple: 0x9b30ff, white: 0xffffff } as const;
+export const backdropColour = (state: Pick) => state.white ? BACKDROPS.white : BACKDROPS.purple;
+
export function describe(state: DebugState): string {
if (!state.on) return '';
- const on = (['backdrop', 'collision', 'actors', 'stats'] as const).filter(key => state[key]);
+ const on = (['backdrop', 'collision', 'actors', 'stats'] as const).filter(key => state[key])
+ .map(key => key === 'backdrop' && state.white ? 'backdrop-white' : key);
const hidden = [...state.hidden].sort();
return [`debug: ${on.join(' ') || 'nothing'}`, hidden.length ? `hidden: ${hidden.join(' ')}` : '',
state.zoom !== 1 ? `zoom ${state.zoom}` : ''].filter(Boolean).join(' | ');
diff --git a/game/src/enemies/arena.test.ts b/game/src/enemies/arena.test.ts
index 82e9f8b..4b80e0b 100644
--- a/game/src/enemies/arena.test.ts
+++ b/game/src/enemies/arena.test.ts
@@ -3,8 +3,8 @@ import { JENNY_BOX } from './jenny';
import { GRAVITY, JUMP_SPEED } from '../player/jump';
import arena from '../../../art/stages/rusty-creek/rc-2-0.anomaly.collision.json';
-/** The platforms' plate (art/stages/rusty-creek/arena/right.png): beams of the maze's girder, this thick. */
-const BEAM = 32;
+/** The platforms (arena-platform board, scripts/arena_platforms.py): girders this thick, hung on chains. */
+const BEAM = 25;
/** The walk line's height at x (the lowest line under it). */
function floorAt(x: number): number {
diff --git a/game/src/stage/debugOverlay.ts b/game/src/stage/debugOverlay.ts
index 9524868..4fd5730 100644
--- a/game/src/stage/debugOverlay.ts
+++ b/game/src/stage/debugOverlay.ts
@@ -1,5 +1,5 @@
import Phaser from 'phaser';
-import { debugStore, describe, type DebugState } from '../core/debug';
+import { backdropColour, debugStore, describe, type DebugState } from '../core/debug';
import { BODY_HEIGHT, BODY_WIDTH } from '../player/body';
import { SPOT_RANGE } from '../enemies/rover';
import { CRAB_SPOT } from '../enemies/crab';
@@ -36,7 +36,6 @@ export interface DexterLike { x: number; state: string; stop?: { x: number }; ro
/** How far each kind sees ahead (px), from its model's constants; the sight box is one body high. */
export const SIGHT: Record = { rover: SPOT_RANGE, crab: CRAB_SPOT, drone: DRONE_SPOT };
-const PURPLE = 0x9b30ff;
const ALERT = new Set(['tell', 'charge', 'stab', 'dive']);
/** The sight box in front of an enemy: from its centre `range` px the way it faces, one body high. */
@@ -72,7 +71,7 @@ export class DebugOverlay {
private apply(state: DebugState) {
// Layer visibility: the stage's culling (render.ts) asks debugStore each frame.
const camera = this.scene.cameras.main;
- camera.setBackgroundColor(debugStore.shows('backdrop') ? PURPLE : this.background);
+ camera.setBackgroundColor(debugStore.shows('backdrop') ? backdropColour(state) : this.background);
camera.setZoom(state.zoom); // a Settings choice: wider views work with debug mode off too
this.collision.setVisible(debugStore.shows('collision'));
this.stats.setVisible(debugStore.shows('stats'));
diff --git a/game/src/ui/menu.ts b/game/src/ui/menu.ts
index 020e0c1..e6e7e9e 100644
--- a/game/src/ui/menu.ts
+++ b/game/src/ui/menu.ts
@@ -200,6 +200,7 @@ export class GameMenu {
field('Debug mode', check(s.on, 'On – draws over the game; screenshots record what is on', on => debugStore.set({ on }))),
field('Show', row(
check(s.backdrop, 'Purple backdrop', v => debugStore.set({ backdrop: v })),
+ check(s.white, '… white instead (check every layer against both)', v => debugStore.set({ white: v })),
check(s.collision, 'Collision', v => debugStore.set({ collision: v })),
check(s.actors, 'Actors: boxes, facing, sight, paths', v => debugStore.set({ actors: v })),
check(s.stats, 'FPS and position', v => debugStore.set({ stats: v })))),
diff --git a/scripts/arena_platforms.py b/scripts/arena_platforms.py
new file mode 100644
index 0000000..053494a
--- /dev/null
+++ b/scripts/arena_platforms.py
@@ -0,0 +1,97 @@
+"""Jenny's arena platforms (playtest J6): two girder platforms hung on chains from the girder over her arena.
+
+ python scripts/arena_platforms.py # writes art/stages/rusty-creek/arena/right.png and the one-way lines
+
+The art is two sprite boards in the game's look (art/sprites boards.json): `arena-platform`, a short box girder in
+the maze's own material, and `arena-chain`, a rusted chain. Exported at game size (export_atlas.py, into tmp/), they
+are composed into one overlay plate for the right chunk's anomaly state (export.json `05-arena`, "overlay"):
+each platform at PLATFORMS, and a chain from each end of it straight up to the underside of the girder above,
+measured from the traversal plate – so nothing floats. The same numbers write the platforms' one-way lines into
+rc-2-0.anomaly.collision.json (any earlier lines over the arena floor are replaced).
+
+Heights (arena.test.ts checks them): the tops 84–89 px over the walk line – a full jump is 96 px – and the bottoms
+clear Jenny's 56 px body, so she drives under them.
+"""
+
+from __future__ import annotations
+
+import json
+import subprocess
+import sys
+from pathlib import Path
+
+import numpy as np
+from PIL import Image
+
+ROOT = Path(__file__).resolve().parents[1]
+REGION = ROOT / "art" / "stages" / "rusty-creek"
+ATLAS = ROOT / "tmp" / "arena-atlas"
+PLATFORMS = [(1180, 476), (1430, 476)] # left edge, top (chunk px); 202 px wide
+CHAIN_INSET = 18 # chain centre from each platform end (px)
+ARENA = (1126, 1700) # the arena floor's x range: one-way lines in it are the platforms'
+
+
+def frame(board: str, name: str) -> Image.Image:
+ sheet = Image.open(ATLAS / f"{board}.png").convert("RGBA")
+ f = json.loads((ATLAS / f"{board}.json").read_text(encoding="utf-8"))["frames"][name]["frame"]
+ cell = sheet.crop((f["x"], f["y"], f["x"] + f["w"], f["y"] + f["h"]))
+ return cell.crop(cell.getbbox())
+
+
+def period(chain: Image.Image) -> int:
+ """The chain's repeat (two links), from the autocorrelation of its rows' opacity."""
+ rows = (np.asarray(chain.getchannel("A")) > 0).sum(1).astype(float)
+ rows -= rows.mean()
+ scores = {p: float((rows[:-p] * rows[p:]).mean()) for p in range(6, len(rows) // 2)}
+ return max(scores, key=scores.get)
+
+
+def chain_strip(chain: Image.Image, length: int) -> Image.Image:
+ """A straight chain `length` px long: whole periods of the drawn chain, from its middle, stacked."""
+ p = period(chain)
+ top = (chain.height // 2) // p * p
+ unit = chain.crop((0, top, chain.width, top + p))
+ strip = Image.new("RGBA", (chain.width, length), (0, 0, 0, 0))
+ for y in range(0, length, p):
+ strip.alpha_composite(unit, (0, y)) if y + p <= length else strip.alpha_composite(unit.crop((0, 0, chain.width, length - y)), (0, y))
+ return strip
+
+
+def underside(traversal: Image.Image, x: int, below: int) -> int:
+ """The lowest opaque row of the structure above `below` at column x: where the chain is fixed."""
+ column = np.asarray(traversal.getchannel("A"))[:below, x] > 128
+ rows = np.nonzero(column)[0]
+ if not len(rows):
+ raise ValueError(f"nothing to hang a chain from above x {x}")
+ return int(rows[-1]) + 1
+
+
+def build() -> tuple[Image.Image, list]:
+ platform = frame("arena-platform", "platform-00")
+ chain = frame("arena-chain", "chain-00")
+ traversal = Image.open(REGION / "images" / "05-traversal-right.png").convert("RGBA")
+ plate = Image.new("RGBA", traversal.size, (0, 0, 0, 0))
+ lines = []
+ for x, top in PLATFORMS:
+ for cx in (x + CHAIN_INSET, x + platform.width - CHAIN_INSET):
+ hook = underside(traversal, cx, top)
+ plate.alpha_composite(chain_strip(chain, top - hook + 4), (cx - chain.width // 2, hook))
+ plate.alpha_composite(platform, (x, top))
+ lines.append([[x, top], [x + platform.width, top]])
+ return plate, lines
+
+
+def main() -> None:
+ subprocess.run([sys.executable, str(ROOT / "scripts" / "export_atlas.py"), "arena-platform", "arena-chain",
+ "--out", str(ATLAS)], check=True, capture_output=True)
+ plate, lines = build()
+ plate.save(REGION / "arena" / "right.png", optimize=True)
+ path = REGION / "rc-2-0.anomaly.collision.json"
+ data = json.loads(path.read_text(encoding="utf-8"))
+ data["oneWay"] = [l for l in data["oneWay"] if not (ARENA[0] <= l[0][0] <= ARENA[1] and l[0][1] > 400)] + lines
+ path.write_text(json.dumps(data, indent=1, ensure_ascii=False) + "\n", encoding="utf-8")
+ print(f"arena platforms: {len(lines)} on chains -> {plate.size}, one-way lines {lines}")
+
+
+if __name__ == "__main__":
+ main()