Quellcode durchsuchen

feat(companion): replace default animation pack

Erman HAVUÇ vor 3 Wochen
Ursprung
Commit
f015fb1525

+ 0 - 1
.gitignore

@@ -101,5 +101,4 @@ companion/target/
 # Deepwork session artifacts
 .slim/deepwork/
 .codegraph/
-companion/VIDEOS/
 .worktrees/

+ 32 - 0
companion/VIDEOS/README.md

@@ -0,0 +1,32 @@
+# Companion animation sources
+
+These MP4 files are source media for default Companion animations. Runtime does
+not decode them directly; release binaries embed JPEG sprite sheets from
+`companion/animations/`.
+
+Each sheet uses first three seconds at 24 FPS: 72 square `200x200` frames in a
+`12x6` grid, producing a `2400x1200` JPEG.
+
+| Source | Runtime animation |
+| --- | --- |
+| `idle.mp4` | `intro.jpg` |
+| `question.mp4` | `question.jpg` (`input` state) |
+| `unknown.mp4` | `unknown.jpg` (unknown/custom agents) |
+| Other `<name>.mp4` files | Matching `<name>.jpg` agent animation |
+
+Regenerate sheets from repository root with FFmpeg:
+
+```bash
+for video in companion/VIDEOS/*.mp4; do
+  name=$(basename "$video" .mp4)
+  output_name=$name
+  if [ "$name" = "idle" ]; then
+    output_name=intro
+  fi
+  ffmpeg -hide_banner -loglevel error -y \
+    -i "$video" \
+    -vf "trim=duration=3,setpts=PTS-STARTPTS,fps=24,scale=200:200,setsar=1,tile=12x6" \
+    -frames:v 1 -q:v 2 \
+    "companion/animations/$output_name.jpg"
+done
+```

BIN
companion/VIDEOS/council.mp4


BIN
companion/VIDEOS/designer.mp4


BIN
companion/VIDEOS/explorer.mp4


BIN
companion/VIDEOS/fixer.mp4


BIN
companion/VIDEOS/idle.mp4


BIN
companion/VIDEOS/librarian.mp4


BIN
companion/VIDEOS/observer.mp4


BIN
companion/VIDEOS/oracle.mp4


BIN
companion/VIDEOS/orchestrator.mp4


BIN
companion/VIDEOS/question.mp4


BIN
companion/VIDEOS/unknown.mp4


BIN
companion/animations/council.jpg


BIN
companion/animations/designer.jpg


BIN
companion/animations/explorer.jpg


BIN
companion/animations/fixer.jpg


BIN
companion/animations/intro.jpg


BIN
companion/animations/librarian.jpg


BIN
companion/animations/observer.jpg


BIN
companion/animations/oracle.jpg


BIN
companion/animations/orchestrator.jpg


BIN
companion/animations/question.jpg


BIN
companion/animations/unknown.jpg


+ 22 - 9
companion/src/gifs.rs

@@ -2,7 +2,7 @@ use egui::{ColorImage, Context, Rect, TextureHandle, TextureId, TextureOptions};
 use std::collections::HashMap;
 
 const DEFAULT_SPEED: f32 = 1.0;
-const BASE_SPEED_MULTIPLIER: f32 = 2.0;
+const BASE_SPEED_MULTIPLIER: f32 = 1.0;
 const FRAME_RATE: f32 = 24.0;
 const FRAME_COUNT: usize = 72;
 const SHEET_COLS: usize = 12;
@@ -30,11 +30,13 @@ impl Gifs {
         sheets.insert("input", include_bytes!("../animations/question.jpg"));
         sheets.insert("intro", include_bytes!("../animations/intro.jpg"));
         sheets.insert("librarian", include_bytes!("../animations/librarian.jpg"));
+        sheets.insert("observer", include_bytes!("../animations/observer.jpg"));
         sheets.insert("oracle", include_bytes!("../animations/oracle.jpg"));
         sheets.insert(
             "orchestrator",
             include_bytes!("../animations/orchestrator.jpg"),
         );
+        sheets.insert("unknown", include_bytes!("../animations/unknown.jpg"));
         Self {
             sheets,
             textures: HashMap::new(),
@@ -92,10 +94,13 @@ impl Gifs {
         if gif_pack != "default" {
             return "orchestrator";
         }
+        if agent.starts_with("councillor-") {
+            return "council";
+        }
         self.sheets
             .get_key_value(agent)
             .map(|(name, _)| *name)
-            .unwrap_or("intro")
+            .unwrap_or("unknown")
     }
 }
 
@@ -154,11 +159,11 @@ mod tests {
     use super::{frame_index, frame_uv, normalized_speed, Gifs, FRAME_COUNT};
 
     #[test]
-    fn unknown_agents_use_the_neutral_intro_animation() {
+    fn unknown_agents_use_the_unknown_animation() {
         let gifs = Gifs::new();
 
         for agent in ["plan", "build", "general", "explore", "custom-agent"] {
-            assert_eq!(gifs.resolve_name(agent, "default"), "intro");
+            assert_eq!(gifs.resolve_name(agent, "default"), "unknown");
         }
     }
 
@@ -170,6 +175,7 @@ mod tests {
             "orchestrator",
             "explorer",
             "librarian",
+            "observer",
             "oracle",
             "designer",
             "fixer",
@@ -179,7 +185,14 @@ mod tests {
     }
 
     #[test]
-    fn speed_is_clamped_and_defaults_fast() {
+    fn dynamic_councillors_use_the_council_animation() {
+        let gifs = Gifs::new();
+
+        assert_eq!(gifs.resolve_name("councillor-alpha", "default"), "council");
+    }
+
+    #[test]
+    fn speed_is_clamped_and_defaults_to_one() {
         assert_eq!(normalized_speed(f32::NAN), 1.0);
         assert_eq!(normalized_speed(0.1), 0.25);
         assert_eq!(normalized_speed(9.0), 4.0);
@@ -188,18 +201,18 @@ mod tests {
     #[test]
     fn classic_loop_wraps_forward() {
         assert_eq!(frame_index(0.0, 1.0, "classic"), 0);
-        assert_eq!(frame_index(1.5, 1.0, "classic"), 0);
-        assert_eq!(frame_index(1.5 + 1.0 / 48.0, 1.0, "classic"), 1);
+        assert_eq!(frame_index(3.0, 1.0, "classic"), 0);
+        assert_eq!(frame_index(3.0 + 1.0 / 24.0, 1.0, "classic"), 1);
     }
 
     #[test]
     fn smooth_loop_ping_pongs_without_duplicate_endpoints() {
         assert_eq!(frame_index(0.0, 1.0, "smooth"), 0);
         assert_eq!(
-            frame_index((FRAME_COUNT - 1) as f64 / 48.0, 1.0, "smooth"),
+            frame_index((FRAME_COUNT - 1) as f64 / 24.0, 1.0, "smooth"),
             71
         );
-        assert_eq!(frame_index(FRAME_COUNT as f64 / 48.0, 1.0, "smooth"), 70);
+        assert_eq!(frame_index(FRAME_COUNT as f64 / 24.0, 1.0, "smooth"), 70);
     }
 
     #[test]

+ 3 - 1
docs/companion.md

@@ -36,7 +36,9 @@ You can enable the companion by adding a `companion` section to your setting con
 
 - **`companion.gifPack`**:
   - `default` (default) - the bundled companion animation set generated from
-    the MP4 sources in `companion/VIDEOS/`.
+    tracked MP4 sources in `companion/VIDEOS/`. See
+    [source-video README](../companion/VIDEOS/README.md) for mappings and
+    regeneration instructions.
 
 - **`companion.loopStyle`**:
   - `classic` (default) - forward playback that loops back to the first frame.