diff --git a/assets/sprite-packs/default/manifest.json b/assets/sprite-packs/default/manifest.json index 299bf24..5612651 100644 --- a/assets/sprite-packs/default/manifest.json +++ b/assets/sprite-packs/default/manifest.json @@ -7,25 +7,80 @@ "animations": [ { "name": "idle", - "fps": 6, - "frames": [0, 1] + "fps": 8, + "frames": [0, 1, 2, 3, 4, 5, 6, 7] }, { "name": "active", + "fps": 8, + "frames": [8, 9, 10, 11, 12, 13, 14, 15] + }, + { + "name": "happy", + "fps": 8, + "frames": [8, 9, 10, 11, 12, 13, 14, 15] + }, + { + "name": "love", + "fps": 8, + "frames": [8, 9, 10, 11, 12, 13, 14, 15] + }, + { + "name": "excited", + "fps": 8, + "frames": [16, 17, 18, 19, 20, 21, 22, 23] + }, + { + "name": "celebrate", "fps": 10, - "frames": [1, 0] + "frames": [16, 17, 18, 19, 20, 21, 22, 23], + "one_shot": true }, { "name": "success", "fps": 10, - "frames": [0, 1, 0], + "frames": [16, 17, 18, 19, 20, 21, 22, 23], "one_shot": true }, + { + "name": "sleepy", + "fps": 8, + "frames": [24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "snoring", + "fps": 8, + "frames": [24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "working", + "fps": 8, + "frames": [32, 33, 34, 35, 36, 37, 38, 39] + }, + { + "name": "angry", + "fps": 8, + "frames": [40, 41, 42, 43, 44, 45, 46, 47] + }, + { + "name": "surprised", + "fps": 8, + "frames": [40, 41, 42, 43, 44, 45, 46, 47] + }, + { + "name": "shy", + "fps": 8, + "frames": [40, 41, 42, 43, 44, 45, 46, 47] + }, { "name": "error", "fps": 8, - "frames": [1, 0, 1], - "one_shot": true + "frames": [40, 41, 42, 43, 44, 45, 46, 47] + }, + { + "name": "dragging", + "fps": 8, + "frames": [48, 49, 50, 51, 52, 53, 54, 55] } ], "anchor": { diff --git a/assets/sprite-packs/demogorgon/manifest.json b/assets/sprite-packs/demogorgon/manifest.json index a32e656..b99a648 100644 --- a/assets/sprite-packs/demogorgon/manifest.json +++ b/assets/sprite-packs/demogorgon/manifest.json @@ -7,25 +7,80 @@ "animations": [ { "name": "idle", - "fps": 6, - "frames": [0, 1] + "fps": 8, + "frames": [0, 1, 2, 3, 4, 5, 6, 7] }, { "name": "active", + "fps": 8, + "frames": [8, 9, 10, 11, 12, 13, 14, 15] + }, + { + "name": "happy", + "fps": 8, + "frames": [8, 9, 10, 11, 12, 13, 14, 15] + }, + { + "name": "love", + "fps": 8, + "frames": [8, 9, 10, 11, 12, 13, 14, 15] + }, + { + "name": "excited", + "fps": 8, + "frames": [16, 17, 18, 19, 20, 21, 22, 23] + }, + { + "name": "celebrate", "fps": 10, - "frames": [1, 0] + "frames": [16, 17, 18, 19, 20, 21, 22, 23], + "one_shot": true }, { "name": "success", "fps": 10, - "frames": [0, 1, 0], + "frames": [16, 17, 18, 19, 20, 21, 22, 23], "one_shot": true }, + { + "name": "sleepy", + "fps": 8, + "frames": [24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "snoring", + "fps": 8, + "frames": [24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "working", + "fps": 8, + "frames": [32, 33, 34, 35, 36, 37, 38, 39] + }, + { + "name": "angry", + "fps": 8, + "frames": [40, 41, 42, 43, 44, 45, 46, 47] + }, + { + "name": "surprised", + "fps": 8, + "frames": [40, 41, 42, 43, 44, 45, 46, 47] + }, + { + "name": "shy", + "fps": 8, + "frames": [40, 41, 42, 43, 44, 45, 46, 47] + }, { "name": "error", "fps": 8, - "frames": [1, 0, 1], - "one_shot": true + "frames": [40, 41, 42, 43, 44, 45, 46, 47] + }, + { + "name": "dragging", + "fps": 8, + "frames": [48, 49, 50, 51, 52, 53, 54, 55] } ], "anchor": { diff --git a/assets/sprite-packs/ferris/manifest.json b/assets/sprite-packs/ferris/manifest.json index 8618138..ed3e514 100644 --- a/assets/sprite-packs/ferris/manifest.json +++ b/assets/sprite-packs/ferris/manifest.json @@ -7,25 +7,80 @@ "animations": [ { "name": "idle", - "fps": 6, - "frames": [0, 1] + "fps": 8, + "frames": [0, 1, 2, 3, 4, 5, 6, 7] }, { "name": "active", + "fps": 8, + "frames": [8, 9, 10, 11, 12, 13, 14, 15] + }, + { + "name": "happy", + "fps": 8, + "frames": [8, 9, 10, 11, 12, 13, 14, 15] + }, + { + "name": "love", + "fps": 8, + "frames": [8, 9, 10, 11, 12, 13, 14, 15] + }, + { + "name": "excited", + "fps": 8, + "frames": [16, 17, 18, 19, 20, 21, 22, 23] + }, + { + "name": "celebrate", "fps": 10, - "frames": [1, 0] + "frames": [16, 17, 18, 19, 20, 21, 22, 23], + "one_shot": true }, { "name": "success", "fps": 10, - "frames": [0, 1, 0], + "frames": [16, 17, 18, 19, 20, 21, 22, 23], "one_shot": true }, + { + "name": "sleepy", + "fps": 8, + "frames": [24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "snoring", + "fps": 8, + "frames": [24, 25, 26, 27, 28, 29, 30, 31] + }, + { + "name": "working", + "fps": 8, + "frames": [32, 33, 34, 35, 36, 37, 38, 39] + }, + { + "name": "angry", + "fps": 8, + "frames": [40, 41, 42, 43, 44, 45, 46, 47] + }, + { + "name": "surprised", + "fps": 8, + "frames": [40, 41, 42, 43, 44, 45, 46, 47] + }, + { + "name": "shy", + "fps": 8, + "frames": [40, 41, 42, 43, 44, 45, 46, 47] + }, { "name": "error", "fps": 8, - "frames": [1, 0, 1], - "one_shot": true + "frames": [40, 41, 42, 43, 44, 45, 46, 47] + }, + { + "name": "dragging", + "fps": 8, + "frames": [48, 49, 50, 51, 52, 53, 54, 55] } ], "anchor": { diff --git a/crates/sprimo-app/src/main.rs b/crates/sprimo-app/src/main.rs index 7ad9db9..4635e93 100644 --- a/crates/sprimo-app/src/main.rs +++ b/crates/sprimo-app/src/main.rs @@ -1034,7 +1034,7 @@ fn default_animation_for_state(state: FrontendState) -> &'static str { FrontendState::Active => "active", FrontendState::Success => "success", FrontendState::Error => "error", - FrontendState::Dragging => "idle", + FrontendState::Dragging => "dragging", FrontendState::Hidden => "idle", } } diff --git a/crates/sprimo-runtime-core/src/lib.rs b/crates/sprimo-runtime-core/src/lib.rs index 7ceda69..c3ea898 100644 --- a/crates/sprimo-runtime-core/src/lib.rs +++ b/crates/sprimo-runtime-core/src/lib.rs @@ -252,7 +252,7 @@ fn default_animation_for_state(state: sprimo_protocol::v1::FrontendState) -> &'s sprimo_protocol::v1::FrontendState::Active => "active", sprimo_protocol::v1::FrontendState::Success => "success", sprimo_protocol::v1::FrontendState::Error => "error", - sprimo_protocol::v1::FrontendState::Dragging => "idle", + sprimo_protocol::v1::FrontendState::Dragging => "dragging", sprimo_protocol::v1::FrontendState::Hidden => "idle", } } @@ -284,6 +284,22 @@ mod tests { assert_eq!(snapshot.current_animation, "active"); } + #[test] + fn dragging_state_maps_to_dragging_animation() { + let temp = TempDir::new().expect("tempdir"); + let path = temp.path().join("config.toml"); + let core = RuntimeCore::new_with_config(path, AppConfig::default(), CapabilityFlags::default()) + .expect("core init"); + core.apply_command(&FrontendCommand::SetState { + state: FrontendState::Dragging, + ttl_ms: None, + }) + .expect("apply"); + let snapshot = core.snapshot().read().expect("snapshot lock").clone(); + assert_eq!(snapshot.state, FrontendState::Dragging); + assert_eq!(snapshot.current_animation, "dragging"); + } + #[test] fn click_through_flag_is_ignored_and_forced_false() { let temp = TempDir::new().expect("tempdir"); diff --git a/docs/SPRITE_PACK_SCHEMA.md b/docs/SPRITE_PACK_SCHEMA.md index 6d4b72a..4056765 100644 --- a/docs/SPRITE_PACK_SCHEMA.md +++ b/docs/SPRITE_PACK_SCHEMA.md @@ -75,3 +75,20 @@ For `sprimo-tauri`, when manifest `image` is exactly `sprite.png`: - `frame_height = image_height / 7` - manifest `frame_width` and `frame_height` are ignored for this case. - animation frame indices are validated against the fixed grid frame count (`56`). + +## Recommended 7x8 Row Semantics + +For `sprite.png` packs using the fixed `8x7` topology, the project convention is: + +- row 1 (`0..7`): `idle` +- row 2 (`8..15`): `happy`, `love`, compatibility alias `active` +- row 3 (`16..23`): `excited`, `celebrate`, compatibility alias `success` +- row 4 (`24..31`): `sleepy`, `snoring` +- row 5 (`32..39`): `working` +- row 6 (`40..47`): `angry`, `surprised`, `shy`, compatibility alias `error` +- row 7 (`48..55`): `dragging` + +Default one-shot policy: + +- `celebrate` and `success` are one-shot. +- other row animations loop by default. diff --git a/docs/TAURI_FRONTEND_DESIGN.md b/docs/TAURI_FRONTEND_DESIGN.md index d1ed744..c420365 100644 --- a/docs/TAURI_FRONTEND_DESIGN.md +++ b/docs/TAURI_FRONTEND_DESIGN.md @@ -60,6 +60,14 @@ Frontend: from runtime snapshot events. - For `sprite.png` packs in tauri runtime, frame size is now derived from atlas dimensions with a fixed `8x7` grid topology to keep splitting stable across packaged asset resolutions. +- `sprite.png` animation naming now follows row semantics with backward-compatible aliases: + - row1: `idle` + - row2: `happy`/`love` + alias `active` + - row3: `excited`/`celebrate` + alias `success` + - row4: `sleepy`/`snoring` + - row5: `working` + - row6: `angry`/`surprised`/`shy` + alias `error` + - row7: `dragging` - React/Vite frontend now supports two window modes: - `main`: transparent overlay sprite renderer - `settings`: pop-out settings window for character and window controls diff --git a/issues/issue6.md b/issues/issue6.md new file mode 100644 index 0000000..d4f7598 --- /dev/null +++ b/issues/issue6.md @@ -0,0 +1,57 @@ +## Title + +Adopt row-based 7x8 sprite animation semantics with backward-compatible state aliases. + +## Severity + +P2 + +## Summary + +Standardize `sprite.png` packs so each of the 7 rows maps to semantic animation groups, while +keeping runtime compatibility with existing state names (`active`, `success`, `error`). + +## Scope + +- `assets/sprite-packs/{default,ferris,demogorgon}/manifest.json` +- `crates/sprimo-runtime-core/src/lib.rs` +- `crates/sprimo-app/src/main.rs` +- docs: + - `docs/SPRITE_PACK_SCHEMA.md` + - `docs/TAURI_FRONTEND_DESIGN.md` + +## Row Mapping Contract + +- row 1 (`0..7`): `idle` +- row 2 (`8..15`): `happy`, `love`, alias `active` +- row 3 (`16..23`): `excited`, `celebrate`, alias `success` +- row 4 (`24..31`): `sleepy`, `snoring` +- row 5 (`32..39`): `working` +- row 6 (`40..47`): `angry`, `surprised`, `shy`, alias `error` +- row 7 (`48..55`): `dragging` + +One-shot defaults: + +- `celebrate` and `success`: one-shot +- all others: loop + +## Implementation Notes + +1. Runtime state mapping updated: +- `Dragging` now maps to `"dragging"` instead of `"idle"` in runtime-core and Bevy frontend. +2. All bundled sprite-pack manifests now expose row-based names and compatibility aliases. +3. Added runtime-core unit test to confirm `SetState::Dragging` selects `"dragging"`. +4. Updated schema/design docs to formalize the row convention. + +## Verification + +### Commands Run + +- [x] `cargo test -p sprimo-runtime-core` +- [x] `cargo check -p sprimo-tauri` +- [x] `cargo check -p sprimo-app` + +### Result + +- Status: `Fix Implemented` +- Notes: packaged runtime visual verification pending.