earthbound-battle-backgrounds 0.1.0

Emulate and render the battle backgrounds from EarthBound / Mother 2.
Documentation
use crate::rom::background_palette::BackgroundPalette;
use crate::rom::battle_background::BattleBackground;

#[derive(Debug)]
pub struct PaletteCycle {
    ty: i16,
    start1: i16,
    end1: i16,
    start2: i16,
    end2: i16,
    speed: f32,
    cycle_countdown: f32,
    cycle_count: i16,
    original_colors: Vec<Vec<u32>>,
    now_colors: Vec<Vec<u32>>,
}

impl PaletteCycle {
    pub fn new(background: &BattleBackground, palette: &BackgroundPalette) -> Self {
        // TODO: Why divide by 2?
        let speed = f32::from(background.palette_cycle_speed()) / 2.;

        let mut original_colors = palette.get_color_matrix().to_vec();

        let mut now_colors = vec![vec![]; original_colors.len()];

        // Duplicate the original colors to make cycle math easier.
        for sub_palette_number in 0..original_colors.len() {
            now_colors[sub_palette_number] = vec![0; 16];
            for i in 16..32 {
                if i >= original_colors[sub_palette_number].len() {
                    original_colors[sub_palette_number].resize(i + 1, 0);
                }

                original_colors[sub_palette_number][i] =
                    original_colors[sub_palette_number][i - 16];
                now_colors[sub_palette_number][i - 16] = original_colors[sub_palette_number][i];
            }
        }

        PaletteCycle {
            ty: background.palette_cycle_type(),
            start1: background.palette_cycle_1_start() as i16,
            end1: background.palette_cycle_1_end() as i16,
            start2: background.palette_cycle_2_start() as i16,
            end2: background.palette_cycle_2_end() as i16,
            speed,
            cycle_countdown: speed,
            cycle_count: 0,
            original_colors,
            now_colors,
        }
    }

    pub fn get_colors(&self, sub_palette: usize) -> &[u32] {
        &self.now_colors[sub_palette]
    }

    pub fn cycle(&mut self) -> bool {
        if self.speed == 0. {
            return false;
        }
        self.cycle_countdown -= 1.;
        if self.cycle_countdown <= 0. {
            self.cycle_colors();
            self.cycle_count += 1;
            self.cycle_countdown = self.speed;
            return true;
        }
        false
    }

    pub fn cycle_colors(&mut self) {
        if self.ty == 1 || self.ty == 2 {
            let cycle_length = self.end1 - self.start1 + 1;
            let cycle_1_position = self.cycle_count % cycle_length;
            for sub_palette_number in 0..self.original_colors.len() {
                for i in self.start1..=self.end1 {
                    let mut new_color: i16 = i - cycle_1_position;
                    if new_color < self.start1 {
                        new_color += cycle_length;
                    }
                    self.now_colors[sub_palette_number][i as usize] =
                        self.original_colors[sub_palette_number][new_color as usize];
                }
            }
        }
        if self.ty == 2 {
            let cycle_length = self.end2 - self.start2 + 1;
            let cycle_2_position = self.cycle_count % cycle_length;
            for sub_palette_number in 0..self.original_colors.len() {
                for i in self.start2..=self.end2 {
                    let mut new_color: i16 = i - cycle_2_position;
                    if new_color < self.start2 {
                        new_color += cycle_length;
                    }
                    self.now_colors[sub_palette_number][i as usize] =
                        self.original_colors[sub_palette_number][new_color as usize];
                }
            }
        }
        if self.ty == 3 {
            let cycle_length = self.end1 - self.start1 + 1;
            let cycle_1_position = self.cycle_count % (cycle_length * 2);
            for sub_palette_number in 0..self.original_colors.len() {
                for i in self.start1..=self.end1 {
                    let mut new_color: i16 = i + cycle_1_position;
                    let mut difference;
                    if new_color > self.end1 {
                        difference = new_color - self.end1 - 1;
                        new_color = self.end1 - difference;
                        if new_color < self.start1 {
                            difference = self.start1 - new_color - 1;
                            new_color = self.start1 + difference;
                        }
                    }
                    self.now_colors[sub_palette_number][i as usize] =
                        self.original_colors[sub_palette_number][new_color as usize];
                }
            }
        }
    }
}