diff --git a/.github/workflows/art-pipeline.yml b/.github/workflows/art-pipeline.yml index 661bd3f..29a085c 100644 --- a/.github/workflows/art-pipeline.yml +++ b/.github/workflows/art-pipeline.yml @@ -26,3 +26,4 @@ jobs: - run: python scripts/collision_check.py - run: python scripts/export_stage.py --verify --out tmp/stage-export - run: python scripts/check_docs.py + - run: python scripts/assets.py status --strict diff --git a/art/inventory.csv b/art/inventory.csv index c83a2c3..f4a0d07 100644 --- a/art/inventory.csv +++ b/art/inventory.csv @@ -40,3 +40,8 @@ hidden-garden bench,stage prop,prologue (4) – Dexter sits,painted in hg-0-0 05 rusty-creek layers,stage layer,Region 1,3 chunks × 7 layers,clean + anomaly,rusty-creek sources,approved,art ✅; collision ⬜ (docs/07) surfaces / atmosphere data,data,"(2), (3)",–,–,–,approved,"art/stages/hidden-garden/{atmosphere,surfaces}.json and rusty-creek/surfaces.json (#166, #168), exported with the stage" it-props,sprite,"the storm drain (hg-3-1): balloon, eyes in the pipe, paper boat",balloon ~18 · eyes 7 · boat 9,balloon 3 · eyes 3 · boat 3,food sheet look + Dexter's eyes + style master,exported,stage/props.ts places them from the zone's spawn markers; awaiting Alexander's review +rubber-chicken,sprite,"secret (hg-0-0, behind the gate pillar)",37×9 (lying),rest 1 · squeak 1,food sheet look + style master,exported,playtest T2; squeaks (synth); awaiting Alexander's review +arena-platform,stage prop,Jenny's arena (rc-2-0 anomaly),202×25 girder,platform 1,maze girder crop + coffee-machine look,drawn,composed into art/stages/rusty-creek/arena/right.png by scripts/arena_platforms.py; awaiting review +arena-chain,stage prop,Jenny's arena (rc-2-0 anomaly),"14 px wide, tiled",chain 2,arena-platform look,drawn,"the platforms hang on it, tiled up to the girder above; awaiting review" +arena-wall,stage prop,Jenny's arena walls (both ends),56×300,lamp 2,coffee-machine look + Jenny sheet,exported,the arena's walls while the fight runs; despeckle 12; awaiting review +hero-ladder,sprite,hero (3) – ladder ends,76,ladder-top 4 · ladder-bottom 3,hero sheet + pose photos,exported,the climb onto and off a ladder (traversal.ts times are pinned to these clips) diff --git a/art/sprites/previews/readability-audit.png b/art/sprites/previews/readability-audit.png index 95db52d..984422f 100644 Binary files a/art/sprites/previews/readability-audit.png and b/art/sprites/previews/readability-audit.png differ diff --git a/game/src/story/CoffeeVisit.ts b/game/src/story/CoffeeVisit.ts index ccf3930..e5fd2c3 100644 --- a/game/src/story/CoffeeVisit.ts +++ b/game/src/story/CoffeeVisit.ts @@ -2,6 +2,7 @@ import Phaser from 'phaser'; import { gameEvents, type Position } from '../core/events'; import { VIEW_WIDTH } from '../core/viewport'; import { sound } from '../fx/sound/Audio'; +import { ACTOR_RIM, addRim } from '../stage/atmosphere/pipeline'; import { pocketMap } from '../ui/mapModel'; import { createAnims, type AtlasAnim } from './atlases'; import { dexterFor, VisitSequence as Visit, type VisitPhase } from './coffeeSequence'; @@ -124,6 +125,8 @@ export class CoffeeVisit extends Phaser.Scene { const from = { x: station.at.x - 8, y: station.at.y - 14 }; const to = { x: this.hero.x + Math.sign(station.at.x - this.hero.x) * 18, y: this.hero.y }; const item = this.sprite(host, from.x, from.y, atlas, frame, color, 14, 10); + // The audit (#159): the pocket computer melts into the garden path at 1x without the actors' rim. + if (item instanceof Phaser.GameObjects.Sprite) addRim(item, ACTOR_RIM.colour, ACTOR_RIM.strength); host.tweens.addCounter({ from: 0, to: 1, delay, duration: 520, ease: 'Sine.easeIn', onUpdate: tween => { diff --git a/scripts/assets.py b/scripts/assets.py index 84cacd7..3771128 100644 --- a/scripts/assets.py +++ b/scripts/assets.py @@ -111,6 +111,12 @@ def status(rows: list[dict[str, str]], board_map: dict[str, dict]) -> tuple[list issues += problems if sheet and sheet != derived and not (sheet == "approved" and derived == "approved"): issues.append(f"{row['id']}: the sheet says '{sheet}', the files say '{derived}'") + # The other way round: an atlas the game loads that the plan never listed is an asset nobody tracks. + listed = {row["id"] for row in rows} + for path in sorted(SPRITES.glob("*.anims.json")): + aid = path.name[: -len(".anims.json")] + if aid not in listed: + issues.append(f"{aid}: exported to the game but not in art/inventory.csv") return table, issues diff --git a/scripts/test_assets.py b/scripts/test_assets.py index 1850d6c..25055e4 100644 --- a/scripts/test_assets.py +++ b/scripts/test_assets.py @@ -57,6 +57,13 @@ def test_status_flags_a_sheet_that_drifted_and_next_skips_done_rows(self): self.assertEqual(issues, ["walker: the sheet says 'exported', the files say 'approved'"]) self.assertEqual(a.upcoming(rows, self.boards), ["plan [planned] -> python scripts/make_boards.py draw plan"]) + def test_status_flags_an_exported_atlas_the_inventory_never_listed(self): + self.export("walker", {"walk": {"frames": 8}}) + self.export("stray", {"idle": {"frames": 2}}) + rows = [{"id": "walker", "kind": "sprite", "frames/anims": "walk 8", "status": "approved"}] + _, issues = a.status(rows, self.boards) + self.assertEqual(issues, ["stray: exported to the game but not in art/inventory.csv"]) + def test_check_fails_a_clip_that_stays_unreadable_with_the_rim(self): import readability self.export("walker", {"walk": {"frames": 8}})