changeset 741:3555845f8cf4

Make it so we can use more than a single anm0 in an EclRunner.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 07 Jan 2020 00:06:18 +0100
parents 8d29dac12219
children 0a250ddfae79
files examples/stagerunner.rs src/th06/anm0_vm.rs src/th06/enemy.rs
diffstat 3 files changed, 85 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/examples/stagerunner.rs
+++ b/examples/stagerunner.rs
@@ -92,24 +92,32 @@ fn main() {
     // Parse arguments.
     let args: Vec<_> = env::args().collect();
     if args.len() != 4 {
-        eprintln!("Usage: {} <ECL file> <ANM file> <easy|normal|hard|lunatic>", args[0]);
+        eprintln!("Usage: {} <unarchived ST.DAT directory> <stage number> <easy|normal|hard|lunatic>", args[0]);
         return;
     }
-    let ecl_filename = Path::new(&args[1]);
-    let anm_filename = Path::new(&args[2]);
+    let directory = Path::new(&args[1]);
+    let stage_number: u8 = args[2].parse().expect("stage");
     let rank: Rank = args[3].parse().expect("rank");
 
     // Open the ECL file.
-    let buf = load_file_into_vec(ecl_filename);
+    let buf = load_file_into_vec(directory.join(format!("ecldata{}.ecl", stage_number)));
     let (_, ecl) = Ecl::from_slice(&buf).unwrap();
     assert_eq!(ecl.mains.len(), 1);
     let main = ecl.mains[0].clone();
 
     // Open the ANM file.
-    let buf = load_file_into_vec(anm_filename);
+    let anm_filename = directory.join(format!("stg{}enm.anm", stage_number));
+    let buf = load_file_into_vec(&anm_filename);
     let (_, mut anms) = Anm0::from_slice(&buf).unwrap();
     let anm0 = anms.pop().unwrap();
-    let anm0 = Rc::new(RefCell::new(anm0));
+
+    // Open the second ANM file.
+    let anm2_filename = directory.join(format!("stg{}enm2.anm", stage_number));
+    let buf = load_file_into_vec(&anm2_filename);
+    let (_, mut anms) = Anm0::from_slice(&buf).unwrap();
+    let anm0_bis = anms.pop().unwrap();
+
+    let anms = [anm0, anm0_bis];
 
     // Get the time since January 1970 as a seed for the PRNG.
     let time = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap();
@@ -125,7 +133,15 @@ fn main() {
     let mut surface = GlfwSurface::new(WindowDim::Windowed(384, 448), "Touhou", WindowOpt::default()).unwrap();
 
     // Open the image atlas matching this ANM.
-    let tex = load_anm_image(&mut surface, &anm0.borrow(), anm_filename).expect("image loading");
+    let mut textures = vec![];
+    for anm0 in anms.iter() {
+        let tex = load_anm_image(&mut surface, &anm0, &anm_filename).expect("image loading");
+        textures.push(tex);
+    }
+
+    let anms = Rc::new(RefCell::new(anms));
+    let tex = textures.pop().unwrap();
+    let tex = textures.pop().unwrap();
 
     // set the uniform interface to our type so that we can read textures from the shader
     let program =
@@ -171,7 +187,7 @@ fn main() {
                     MainInstruction::SpawnEnemyMirroredRandom(x, y, z, life, bonus, score) => (x, y, z, life, bonus, score, true),
                     _ => continue,
                 };
-                let enemy = Enemy::new(Position::new(x, y), life, bonus, score, mirror, Rc::downgrade(&anm0), Rc::downgrade(&game));
+                let enemy = Enemy::new(Position::new(x, y), life, bonus, score, mirror, Rc::downgrade(&anms), Rc::downgrade(&game));
                 let runner = EclRunner::new(&ecl, enemy, sub);
                 ecl_runners.push(runner);
             }
--- a/src/th06/anm0_vm.rs
+++ b/src/th06/anm0_vm.rs
@@ -214,9 +214,50 @@ impl Sprite {
     }
 }
 
+struct Anms {
+    inner: Rc<RefCell<[Anm0; 2]>>,
+}
+
+impl Anms {
+    fn new(anms: Rc<RefCell<[Anm0; 2]>>) -> Anms {
+        Anms {
+            inner: anms,
+        }
+    }
+
+    fn load_sprite(&self, sprite: &mut Sprite, id: u8) {
+        let anms = self.inner.borrow();
+        let mut anm = None;
+        let mut texcoords = None;
+        'anm: for anm0 in anms.iter() {
+            for sp in anm0.sprites.iter() {
+                if sp.index == id as u32 {
+                    texcoords = Some(sp);
+                    anm = Some(anm0.clone());
+                    break 'anm;
+                }
+            }
+        }
+        sprite.anm = anm;
+        if let Some(texcoords) = texcoords {
+            sprite.texcoords = [texcoords.x, texcoords.y, texcoords.width, texcoords.height];
+        }
+    }
+
+    fn get_script(&self, id: u8) -> Script {
+        let anms = self.inner.borrow();
+        for anm0 in anms.iter() {
+            if anm0.scripts.contains_key(&id) {
+                return anm0.scripts[&id].clone();
+            }
+        }
+        unreachable!();
+    }
+}
+
 /// Interpreter for `Anm0` instructions to update a `Sprite`.
 pub struct AnmRunner {
-    anm: Anm0,
+    anms: Anms,
     sprite: Rc<RefCell<Sprite>>,
     prng: Weak<RefCell<Prng>>,
     running: bool,
@@ -231,15 +272,17 @@ pub struct AnmRunner {
 
 impl AnmRunner {
     /// Create a new `AnmRunner`.
-    pub fn new(anm: &Anm0, script_id: u8, sprite: Rc<RefCell<Sprite>>, prng: Weak<RefCell<Prng>>, sprite_index_offset: u32) -> AnmRunner {
+    pub fn new(anms: Rc<RefCell<[Anm0; 2]>>, script_id: u8, sprite: Rc<RefCell<Sprite>>, prng: Weak<RefCell<Prng>>, sprite_index_offset: u32) -> AnmRunner {
+        let anms = Anms::new(anms);
+        let script = anms.get_script(script_id);
         let mut runner = AnmRunner {
-            anm: anm.clone(),
+            anms,
             sprite: sprite,
             prng,
             running: true,
             waiting: false,
 
-            script: anm.scripts[&script_id].clone(),
+            script,
             frame: 0,
             timeout: None,
             instruction_pointer: 0,
@@ -321,9 +364,7 @@ impl AnmRunner {
                 self.running = false;
             }
             Instruction::LoadSprite(sprite_index) => {
-                sprite.anm = Some(self.anm.clone());
-                let texcoords = &self.anm.sprites[(sprite_index + self.sprite_index_offset) as usize];
-                sprite.texcoords = [texcoords.x, texcoords.y, texcoords.width, texcoords.height];
+                self.anms.load_sprite(&mut sprite, (sprite_index + self.sprite_index_offset) as u8);
             }
             Instruction::SetScale(sx, sy) => {
                 sprite.rescale = [sx, sy];
@@ -375,11 +416,7 @@ impl AnmRunner {
                     amplitude = (rand as u32) % amplitude;
                 }
                 let sprite_index = min_index + amplitude;
-
-                // TODO: refactor that with Instruction::LoadSprite.
-                sprite.anm = Some(self.anm.clone());
-                let texcoords = &self.anm.sprites[(sprite_index + self.sprite_index_offset) as usize];
-                sprite.texcoords = [texcoords.x, texcoords.y, texcoords.width, texcoords.height];
+                self.anms.load_sprite(&mut sprite, (sprite_index + self.sprite_index_offset) as u8);
             }
             Instruction::Move(x, y, z) => {
                 sprite.dest_offset = [x, y, z];
--- a/src/th06/enemy.rs
+++ b/src/th06/enemy.rs
@@ -308,7 +308,7 @@ pub struct Enemy {
     pub(crate) speed_interpolator: Option<Interpolator1<f32>>,
 
     // Misc stuff, do we need them?
-    pub(crate) anm0: Weak<RefCell<Anm0>>,
+    pub(crate) anm0: Weak<RefCell<[Anm0; 2]>>,
     process: Rc<RefCell<Process>>,
     pub(crate) game: Weak<RefCell<Game>>,
     pub(crate) prng: Weak<RefCell<Prng>>,
@@ -317,7 +317,7 @@ pub struct Enemy {
 
 impl Enemy {
     /// Create a new enemy.
-    pub fn new(pos: Position, life: i16, bonus_dropped: i16, die_score: u32, mirror: bool, anm0: Weak<RefCell<Anm0>>, game: Weak<RefCell<Game>>) -> Rc<RefCell<Enemy>> {
+    pub fn new(pos: Position, life: i16, bonus_dropped: i16, die_score: u32, mirror: bool, anm0: Weak<RefCell<[Anm0; 2]>>, game: Weak<RefCell<Game>>) -> Rc<RefCell<Enemy>> {
         let game_rc = game.upgrade().unwrap();
         let mut enemy = Enemy {
             pos,
@@ -346,7 +346,7 @@ impl Enemy {
         let anm0 = self.anm0.upgrade().unwrap();
         let game = self.game.upgrade().unwrap();
         let sprite = Rc::new(RefCell::new(Sprite::new()));
-        let anmrunner = AnmRunner::new(&*anm0.borrow(), index, sprite, self.prng.clone(), 0);
+        let anmrunner = AnmRunner::new(anm0, index, sprite, self.prng.clone(), 0);
         let anmrunner = Rc::new(RefCell::new(anmrunner));
         self.anmrunner = Rc::downgrade(&anmrunner);
         (*game.borrow_mut()).anmrunners.push(anmrunner);
@@ -550,7 +550,15 @@ mod tests {
         file.read_to_end(&mut buf).unwrap();
         let (_, mut anms) = Anm0::from_slice(&buf).unwrap();
         let anm0 = anms.pop().unwrap();
-        let anm0 = Rc::new(RefCell::new(anm0));
+
+        let file = File::open("EoSD/ST/stg1enm2.anm").unwrap();
+        let mut file = io::BufReader::new(file);
+        let mut buf = vec![];
+        file.read_to_end(&mut buf).unwrap();
+        let (_, mut anms) = Anm0::from_slice(&buf).unwrap();
+        let anm0_bis = anms.pop().unwrap();
+
+        let anm0 = Rc::new(RefCell::new([anm0, anm0_bis]));
         let prng = Rc::new(RefCell::new(Prng::new(0)));
         let game = Game::new(prng, Rank::EASY);
         let game = Rc::new(RefCell::new(game));