dotzuki-engine 0.1.0

A general-purpose JRPG game engine built from Game Boy tile rendering principles
Documentation
use super::{
    collision::detect_collisions_for_sprite, oam::prepare_oam_data,
    MovementStatus, MOVEMENT_STAY, MOVEMENT_WALK, NUM_SPRITESTATEDATA_STRUCTS, SpriteTable,
};

pub fn update_sprites(table: &mut SpriteTable, screen_height: u8) {
    update_player_sprite(table);

    for i in 1..NUM_SPRITESTATEDATA_STRUCTS {
        update_npc_sprite(table, i);
    }

    detect_all_sprite_collisions(table);

    prepare_oam_data(table, screen_height);
}

fn update_player_sprite(table: &mut SpriteTable) {
    let sd1 = &table.data1[0];
    if !sd1.is_active() {
        return;
    }

    update_sprite_image_index(table, 0);
}

fn update_npc_sprite(table: &mut SpriteTable, idx: usize) {
    let sd1 = &table.data1[idx];
    if !sd1.is_active() {
        return;
    }

    let status = sd1.movement_status();
    match status {
        MovementStatus::Uninitialized => {}
        MovementStatus::Ready => {
            try_start_npc_movement(table, idx);
        }
        MovementStatus::Delayed => {
            update_movement_delay(table, idx);
        }
        MovementStatus::Moving => {
            continue_npc_movement(table, idx);
        }
    }

    update_sprite_image_index(table, idx);
}

fn try_start_npc_movement(table: &mut SpriteTable, idx: usize) {
    let sd2 = &table.data2[idx];
    if sd2.movement_byte1 == MOVEMENT_STAY {
        return;
    }

    if sd2.movement_byte1 == MOVEMENT_WALK {
        let sd2 = &mut table.data2[idx];
        if sd2.movement_delay > 0 {
            sd2.movement_delay -= 1;
            return;
        }
    }
}

fn update_movement_delay(table: &mut SpriteTable, idx: usize) {
    let sd2 = &mut table.data2[idx];
    if sd2.movement_delay > 0 {
        sd2.movement_delay -= 1;
    } else {
        table.data1[idx].movement_status =
            (table.data1[idx].movement_status & 0x80) | MovementStatus::Ready as u8;
    }
}

fn continue_npc_movement(table: &mut SpriteTable, idx: usize) {
    let sd1 = &mut table.data1[idx];

    sd1.y_pixels = (sd1.y_pixels as i16 + sd1.y_step_vector as i16) as u8;
    sd1.x_pixels = (sd1.x_pixels as i16 + sd1.x_step_vector as i16) as u8;

    let sd2 = &mut table.data2[idx];
    if sd2.walk_anim_counter > 0 {
        sd2.walk_anim_counter -= 1;
    } else {
        sd1.y_step_vector = 0;
        sd1.x_step_vector = 0;
        sd1.movement_status = (sd1.movement_status & 0x80) | MovementStatus::Ready as u8;
    }
}

fn update_sprite_image_index(table: &mut SpriteTable, idx: usize) {
    let sd1 = &table.data1[idx];
    let sd2 = &table.data2[idx];

    if sd2.image_base_offset == 0 && sd1.picture_id == 0 {
        return;
    }

    let facing = sd1.facing_direction;
    let anim_frame = sd1.anim_frame_counter & 0x03;

    let facing_base: u8 = match facing & 0x0C {
        0x00 => 0x00,
        0x04 => 0x04,
        0x08 => 0x08,
        0x0C => 0x0C,
        _ => 0x00,
    };

    let image_idx = (sd2.image_base_offset << 4) | facing_base | anim_frame;
    table.data1[idx].image_index = image_idx;
}

fn detect_all_sprite_collisions(table: &mut SpriteTable) {
    for i in 0..NUM_SPRITESTATEDATA_STRUCTS {
        if table.data1[i].is_active() {
            detect_collisions_for_sprite(table, i);
        }
    }
}