changeset 686:aefe5b5f481e

ecl_vm: implement the SetBulletAttributes opcodes.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Sat, 17 Aug 2019 04:28:24 +0200
parents 11d7e4d6947a
children ac092b70c39a
files src/th06/ecl.rs src/th06/ecl_vm.rs src/th06/enemy.rs
diffstat 3 files changed, 158 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/src/th06/ecl.rs
+++ b/src/th06/ecl.rs
@@ -241,13 +241,13 @@ declare_sub_instructions!{
     63 => fn StopInAccel(duration: i32),
     65 => fn SetScreenBox(xmin: f32, ymin: f32, xmax: f32, ymax: f32),
     66 => fn ClearScreenBox(),
-    67 => fn SetBulletAttributes1(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: i32),
-    68 => fn SetBulletAttributes2(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: i32),
-    69 => fn SetBulletAttributes3(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: i32),
-    70 => fn SetBulletAttributes4(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: i32),
-    71 => fn SetBulletAttributes5(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: i32),
-    74 => fn SetBulletAttributes6(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: i32),
-    75 => fn SetBulletAttributes7(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: i32),
+    67 => fn SetBulletAttributes1(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: u32),
+    68 => fn SetBulletAttributes2(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: u32),
+    69 => fn SetBulletAttributes3(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: u32),
+    70 => fn SetBulletAttributes4(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: u32),
+    71 => fn SetBulletAttributes5(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: u32),
+    74 => fn SetBulletAttributes6(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: u32),
+    75 => fn SetBulletAttributes7(anim: i16, sprite_index_offset: i16, bullets_per_shot: i32, number_of_shots: i32, speed: f32, speed2: f32, launch_angle: f32, angle: f32, flags: u32),
     76 => fn SetBulletInterval(interval: i32),
     77 => fn SetBulletIntervalEx(interval: i32),
     78 => fn DelayAttack(),
--- a/src/th06/ecl_vm.rs
+++ b/src/th06/ecl_vm.rs
@@ -1,11 +1,29 @@
 //! ECL runner.
 
 use crate::th06::ecl::{Ecl, SubInstruction};
-use crate::th06::enemy::Enemy;
+use crate::th06::enemy::{Enemy, BulletAttributes};
 use crate::util::prng::Prng;
 use std::cell::RefCell;
 use std::rc::Rc;
 
+macro_rules! gen_SetBulletAttributes {
+    ($self:ident, $opcode:tt, $anim:ident, $sprite_index_offset:ident, $bullets_per_shot:ident,
+     $number_of_shots:ident, $speed:ident, $speed2:ident, $launch_angle:ident, $angle:ident,
+     $flags:ident) => {{
+        let sprite_index_offset = $self.get_i32($sprite_index_offset as i32) as i16;
+        let bullets_per_shot = $self.get_i32($bullets_per_shot) as i16;
+        let number_of_shots = $self.get_i32($number_of_shots) as i16;
+        let speed = $self.get_f32($speed);
+        let speed2 = $self.get_f32($speed2);
+        let launch_angle = $self.get_f32($launch_angle);
+        let angle = $self.get_f32($angle);
+
+        let mut enemy = $self.enemy.borrow_mut();
+        enemy.set_bullet_attributes($opcode, $anim, sprite_index_offset, bullets_per_shot,
+                                    number_of_shots, speed, speed2, launch_angle, angle, $flags);
+    }};
+}
+
 type Variables = ([i32; 4], [f32; 4], [i32; 4]);
 
 /// Interpreter for enemy scripts.
@@ -537,7 +555,57 @@ impl EclRunner {
                 let mut enemy = self.enemy.borrow_mut();
                 enemy.screen_box = None;
             }
+
             // 67 to 75 are set bullet attributes and it seems a pain to reverse rn
+            SubInstruction::SetBulletAttributes1(anim, sprite_index_offset, bullets_per_shot,
+                                                 number_of_shots, speed, speed2, launch_angle,
+                                                 angle, flags) => {
+                gen_SetBulletAttributes!(self, 67, anim, sprite_index_offset, bullets_per_shot,
+                                         number_of_shots, speed, speed2, launch_angle, angle,
+                                         flags);
+            }
+            SubInstruction::SetBulletAttributes2(anim, sprite_index_offset, bullets_per_shot,
+                                                 number_of_shots, speed, speed2, launch_angle,
+                                                 angle, flags) => {
+                gen_SetBulletAttributes!(self, 68, anim, sprite_index_offset, bullets_per_shot,
+                                         number_of_shots, speed, speed2, launch_angle, angle,
+                                         flags);
+            }
+            SubInstruction::SetBulletAttributes3(anim, sprite_index_offset, bullets_per_shot,
+                                                 number_of_shots, speed, speed2, launch_angle,
+                                                 angle, flags) => {
+                gen_SetBulletAttributes!(self, 69, anim, sprite_index_offset, bullets_per_shot,
+                                         number_of_shots, speed, speed2, launch_angle, angle,
+                                         flags);
+            }
+            SubInstruction::SetBulletAttributes4(anim, sprite_index_offset, bullets_per_shot,
+                                                 number_of_shots, speed, speed2, launch_angle,
+                                                 angle, flags) => {
+                gen_SetBulletAttributes!(self, 70, anim, sprite_index_offset, bullets_per_shot,
+                                         number_of_shots, speed, speed2, launch_angle, angle,
+                                         flags);
+            }
+            SubInstruction::SetBulletAttributes5(anim, sprite_index_offset, bullets_per_shot,
+                                                 number_of_shots, speed, speed2, launch_angle,
+                                                 angle, flags) => {
+                gen_SetBulletAttributes!(self, 71, anim, sprite_index_offset, bullets_per_shot,
+                                         number_of_shots, speed, speed2, launch_angle, angle,
+                                         flags);
+            }
+            SubInstruction::SetBulletAttributes6(anim, sprite_index_offset, bullets_per_shot,
+                                                 number_of_shots, speed, speed2, launch_angle,
+                                                 angle, flags) => {
+                gen_SetBulletAttributes!(self, 74, anim, sprite_index_offset, bullets_per_shot,
+                                         number_of_shots, speed, speed2, launch_angle, angle,
+                                         flags);
+            }
+            SubInstruction::SetBulletAttributes7(anim, sprite_index_offset, bullets_per_shot,
+                                                 number_of_shots, speed, speed2, launch_angle,
+                                                 angle, flags) => {
+                gen_SetBulletAttributes!(self, 75, anim, sprite_index_offset, bullets_per_shot,
+                                         number_of_shots, speed, speed2, launch_angle, angle,
+                                         flags);
+            }
 
 
 
--- a/src/th06/enemy.rs
+++ b/src/th06/enemy.rs
@@ -10,14 +10,14 @@ use std::collections::HashMap;
 use std::rc::{Rc, Weak};
 
 /// The 2D position of an object in the game.
-#[derive(Debug, Clone, Copy, Default)]
+#[derive(Debug, Clone, Copy, Default, PartialEq)]
 pub struct Position {
     pub(crate) x: f32,
     pub(crate) y: f32,
 }
 
 /// An offset which can be added to a Position.
-#[derive(Debug, Clone, Copy, Default)]
+#[derive(Debug, Clone, Copy, Default, PartialEq)]
 pub struct Offset {
     dx: f32,
     dy: f32,
@@ -120,10 +120,10 @@ struct Element {
 pub(crate) struct DifficultyCoeffs {
     pub(crate) speed_a: f32,
     pub(crate) speed_b: f32,
-    pub(crate) nb_a: u16,
-    pub(crate) nb_b: u16,
-    pub(crate) shots_a: u16,
-    pub(crate) shots_b: u16,
+    pub(crate) nb_a: i16,
+    pub(crate) nb_b: i16,
+    pub(crate) shots_a: i16,
+    pub(crate) shots_b: i16,
 }
 
 impl Default for DifficultyCoeffs {
@@ -139,6 +139,32 @@ impl Default for DifficultyCoeffs {
     }
 }
 
+#[derive(Debug, Clone, Default, PartialEq)]
+pub(crate) struct BulletAttributes {
+    pub(crate) anim: i16,
+    pub(crate) sprite_index_offset: i16,
+    pub(crate) pos: Position, // Doesn’t have a z field.
+    pub(crate) launch_angle: f32,
+    pub(crate) angle: f32,
+    pub(crate) speed: f32,
+    pub(crate) speed2: f32,
+    pub(crate) extended_attributes: (i32, i32, i32, i32, f32, f32, f32, f32),
+    // unknown: x32,
+    pub(crate) bullets_per_shot: i16,
+    pub(crate) number_of_shots: i16,
+    pub(crate) bullet_type: i16,
+    // zero: x32,
+    pub(crate) flags: u32,
+    pub(crate) ins84_param: i32,
+}
+
+impl BulletAttributes {
+    /// Fire!
+    pub fn fire(&mut self) {
+        println!("PAN!");
+    }
+}
+
 #[derive(PartialEq)]
 pub(crate) enum Direction {
     Left,
@@ -198,9 +224,8 @@ pub struct Enemy {
 
     // Tuples.
     pub(crate) difficulty_coeffs: DifficultyCoeffs,
-    pub(crate) extended_bullet_attributes: Option<(u32, u32, u32, u32, f32, f32, f32, f32)>,
-    pub(crate) bullet_attributes: Option<(i16, i16, u32, u32, u32, f32, f32, f32, f32, u32)>,
-    pub(crate) bullet_launch_offset: Offset,
+    pub(crate) bullet_attributes: BulletAttributes,
+    pub(crate) bullet_offset: Offset,
     pub(crate) movement_dependant_sprites: Option<(u8, u8, u8, u8)>,
     pub(crate) screen_box: Option<(f32, f32, f32, f32)>,
 
@@ -274,6 +299,54 @@ impl Enemy {
         self.hitbox_half_size = [width / 2., height / 2.];
     }
 
+    /// Defines the attributes for the next bullet fired, and fire it if delay_attack isn’t set!
+    pub fn set_bullet_attributes(&mut self, opcode: u16, anim: i16, sprite_index_offset: i16,
+                                 bullets_per_shot: i16, number_of_shots: i16, speed: f32,
+                                 speed2: f32, launch_angle: f32, angle: f32, flags: u32) {
+        // Get the coeffs for the current difficulty.
+        let difficulty = self.get_difficulty() as i16;
+        let coeff_nb = self.difficulty_coeffs.nb_a + (self.difficulty_coeffs.nb_b - self.difficulty_coeffs.nb_a) * difficulty / 32;
+        let coeff_shots = self.difficulty_coeffs.shots_a + (self.difficulty_coeffs.shots_b - self.difficulty_coeffs.shots_a) * difficulty / 32;
+        let coeff_speed = self.difficulty_coeffs.speed_a + (self.difficulty_coeffs.speed_b - self.difficulty_coeffs.speed_a) * difficulty as f32 / 32.;
+
+        let opcode = 67;
+        let mut bullet = &mut self.bullet_attributes;
+
+        bullet.anim = anim;
+        bullet.bullet_type = opcode - 67;
+        bullet.sprite_index_offset = sprite_index_offset;
+
+        bullet.bullets_per_shot = bullets_per_shot + coeff_nb;
+        if bullet.bullets_per_shot < 1 {
+            bullet.bullets_per_shot = 1;
+        }
+
+        bullet.number_of_shots = number_of_shots + coeff_shots;
+        if bullet.number_of_shots < 1 {
+            bullet.number_of_shots = 1;
+        }
+
+        bullet.pos = self.pos + self.bullet_offset;
+
+        bullet.speed = speed + coeff_speed;
+        if bullet.speed < 0.3 {
+            bullet.speed = 0.3;
+        }
+
+        bullet.speed2 = speed2 + coeff_speed / 2.;
+        if bullet.speed2 < 0.3 {
+            bullet.speed2 = 0.3;
+        }
+
+        bullet.launch_angle = launch_angle.atan2(0.);
+        bullet.angle = angle;
+        bullet.flags = flags;
+
+        if !self.delay_attack {
+            bullet.fire();
+        }
+    }
+
     /// Run all interpolators and such, and update internal variables once per
     /// frame.
     pub fn update(&mut self) {