view src/th06/ @ 660:31fc0d881105

Make ecl_vm compile, and use it in eclrenderer (doesn’t render yet).
author Emmanuel Gil Peyrot <>
date Sat, 10 Aug 2019 14:41:30 +0200
parents 3a9d82a02c88
children 598f3125cbac
line wrap: on
line source

//! Module providing an Enemy struct, to be changed by EclRunner.

use crate::th06::anm0::Anm0;
use crate::th06::anm0_vm::{Sprite, AnmRunner};
use crate::th06::interpolator::{Interpolator1, Interpolator2};
use crate::util::prng::Prng;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::{Rc, Weak};

/// The 2D position of an object in the game.
#[derive(Debug, Clone, Copy, Default)]
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)]
pub struct Offset {
    dx: f32,
    dy: f32,

impl Position {
    /// Create said position.
    pub fn new(x: f32, y: f32) -> Position {
        Position { x, y }

impl Offset {
    /// Create said offset.
    pub fn new(dx: f32, dy: f32) -> Offset {
        Offset { dx, dy }

impl std::ops::Add<Offset> for Position {
    type Output = Position;
    fn add(self, offset: Offset) -> Position {
        Position {
            x: self.x + offset.dx,
            y: self.y + offset.dy,

#[derive(Debug, Clone)]
struct Callback;

#[derive(Debug, Clone)]
struct Laser;

#[derive(Debug, Clone, Default)]
struct Process;

/// God struct of our game.
pub struct Game {
    enemies: Vec<Rc<RefCell<Enemy>>>,
    anmrunners: Vec<Rc<RefCell<AnmRunner>>>,
    prng: Rc<RefCell<Prng>>,
    rank: i32,
    difficulty: i32,

impl Game {
    /// Create said god struct.
    pub fn new(prng: Rc<RefCell<Prng>>) -> Game {
        Game {
            enemies: Vec::new(),
            anmrunners: Vec::new(),
            rank: 0,
            difficulty: 0,

    /// Run the simulation for a single frame.
    pub fn run_frame(&mut self) {
        for eclrunner in self.eclrunners {

        for anmrunner in self.anmrunners.iter() {
            let mut anmrunner = anmrunner.borrow_mut();

    /// Returns a list of all sprites currently being displayed on screen.
    pub fn get_sprites(&self) -> Vec<Rc<RefCell<Sprite>>> {
        let mut sprites = vec![];
        for anmrunner in self.anmrunners.iter() {
            let anmrunner = anmrunner.borrow();
            let sprite = anmrunner.get_sprite();

/// Common to all elements in game.
struct Element {
    pos: Position,
    removed: bool,
    anmrunner: AnmRunner,

/// The enemy struct, containing everything pertaining to an enemy.
pub struct Enemy {
    // Common to all elements in game.
    pub(crate) pos: Position,
    pub(crate) removed: bool,
    pub(crate) anmrunner: Weak<RefCell<AnmRunner>>,

    // Specific to enemy.
    // Floats.
    pub(crate) z: f32,
    pub(crate) angle: f32,
    pub(crate) speed: f32,
    pub(crate) rotation_speed: f32,
    pub(crate) acceleration: f32,

    // Ints.
    pub(crate) type_: u32,
    pub(crate) bonus_dropped: u32,
    pub(crate) die_score: u32,
    /// XXX
    pub frame: u32,
    pub(crate) life: u32,
    pub(crate) death_flags: u32,
    pub(crate) current_laser_id: u32,
    pub(crate) low_life_trigger: Option<u32>,
    pub(crate) timeout: Option<u32>,
    pub(crate) remaining_lives: u32,
    pub(crate) bullet_launch_interval: u32,
    pub(crate) bullet_launch_timer: u32,
    pub(crate) death_anim: u32,
    pub(crate) direction: u32,
    pub(crate) update_mode: u32,

    // Bools.
    pub(crate) visible: bool,
    pub(crate) was_visible: bool,
    pub(crate) touchable: bool,
    pub(crate) collidable: bool,
    pub(crate) damageable: bool,
    pub(crate) boss: bool,
    pub(crate) automatic_orientation: bool,
    pub(crate) delay_attack: bool,

    // Tuples.
    pub(crate) difficulty_coeffs: (f32, f32, u32, u32, u32, u32),
    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) movement_dependant_sprites: Option<(f32, f32, f32, f32)>,
    pub(crate) screen_box: Option<(f32, f32, f32, f32)>,

    // Callbacks.
    death_callback: Option<Callback>,
    boss_callback: Option<Callback>,
    low_life_callback: Option<Callback>,
    timeout_callback: Option<Callback>,

    // Laser.
    laser_by_id: HashMap<u32, Laser>,

    // Options.
    // TODO: actually a 8 element array.
    options: Vec<Element>,

    // Interpolators.
    pub(crate) interpolator: Option<Interpolator2<f32>>,
    pub(crate) speed_interpolator: Option<Interpolator1<f32>>,

    // Misc stuff, do we need them?
    pub(crate) anm0: Weak<RefCell<Anm0>>,
    process: Rc<RefCell<Process>>,
    pub(crate) game: Weak<RefCell<Game>>,
    pub(crate) prng: Weak<RefCell<Prng>>,
    pub(crate) hitbox_half_size: [f32; 2],

impl Enemy {
    /// Create a new enemy.
    pub fn new(pos: Position, life: i32, bonus_dropped: u32, die_score: u32, anm0: Weak<RefCell<Anm0>>, game: Weak<RefCell<Game>>) -> Rc<RefCell<Enemy>> {
        let game_rc = game.upgrade().unwrap();
        let enemy = Enemy {
            visible: true,
            life: if life < 0 { 1 } else { life as u32 },
            touchable: true,
            collidable: true,
            damageable: true,
            difficulty_coeffs: (-0.5, 0.5, 0, 0, 0, 0),
        let enemy = Rc::new(RefCell::new(enemy));

    /// Sets the animation to the one indexed by index in the current anm0.
    pub fn set_anim(&mut self, index: u8) {
        let anm0 = self.anm0.upgrade().unwrap();
        let game =;
        let sprite = Rc::new(RefCell::new(Sprite::new()));
        let anmrunner = AnmRunner::new(&*anm0.borrow(), index, sprite, self.prng.clone(), 0);
        let anmrunner = Rc::new(RefCell::new(anmrunner));
        self.anmrunner = Rc::downgrade(&anmrunner);

    /// Sets the current position of the enemy.
    pub fn set_pos(&mut self, x: f32, y: f32, z: f32) {
        self.pos.x = x;
        self.pos.y = y;
        self.z = z;

    /// Sets the hitbox around the enemy.
    pub fn set_hitbox(&mut self, width: f32, height: f32) {
        self.hitbox_half_size = [width, height];

    pub(crate) fn get_rank(&self) -> i32 {
        let game =;
        let game = game.borrow();

    pub(crate) fn get_difficulty(&self) -> i32 {
        let game =;
        let game = game.borrow();

mod tests {
    use super::*;
    use std::io::{self, Read};
    use std::fs::File;

    fn enemy() {
        let file = File::open("EoSD/ST/stg1enm.anm").unwrap();
        let mut file = io::BufReader::new(file);
        let mut buf = vec![];
        file.read_to_end(&mut buf).unwrap();
        let anm0 = Anm0::from_slice(&buf).unwrap();
        let anm0 = Rc::new(RefCell::new(anm0));
        let prng = Rc::new(RefCell::new(Prng::new(0)));
        let game = Game::new(prng);
        let game = Rc::new(RefCell::new(game));
        let mut enemy = Enemy::new(Position::new(0., 0.), 500, 0, 640, Rc::downgrade(&anm0), Rc::downgrade(&game));