use log::info;

use case_insensitive_hashmap::CaseInsensitiveHashMap;

use crate::case_insensitive_hashmap;
use crate::common::{Flag, Rect};
use crate::player::ControlMode;
use crate::str;
use crate::text_script::TextScriptEncoding;

#[derive(Debug, Copy, Clone)]
pub struct PhysicsConsts {
    pub max_dash: isize,
    pub max_move: isize,
    pub gravity_ground: isize,
    pub gravity_air: isize,
    pub dash_ground: isize,
    pub dash_air: isize,
    pub resist: isize,
    pub jump: isize,
}

#[derive(Debug, Copy, Clone)]
pub struct BoosterConsts {
    pub fuel: usize,
    pub b2_0_up: isize,
    pub b2_0_up_nokey: isize,
    pub b2_0_down: isize,
    pub b2_0_left: isize,
    pub b2_0_right: isize,
}

#[derive(Debug, Copy, Clone)]
pub struct MyCharConsts {
    pub display_bounds: Rect<usize>,
    pub hit_bounds: Rect<usize>,
    pub life: usize,
    pub max_life: usize,
    pub control_mode: ControlMode,
    pub air_physics: PhysicsConsts,
    pub water_physics: PhysicsConsts,
    pub animations_left: [Rect<usize>; 12],
    pub animations_right: [Rect<usize>; 12],
}

#[derive(Debug)]
pub struct CaretConsts {
    pub offsets: [(isize, isize); 18],
    pub bubble_left_rects: Vec<Rect<usize>>,
    pub bubble_right_rects: Vec<Rect<usize>>,
    pub projectile_dissipation_left_rects: Vec<Rect<usize>>,
    pub projectile_dissipation_right_rects: Vec<Rect<usize>>,
    pub projectile_dissipation_up_rects: Vec<Rect<usize>>,
    pub shoot_rects: Vec<Rect<usize>>,
    pub zzz_rects: Vec<Rect<usize>>,
    pub drowned_quote_left_rect: Rect<usize>,
    pub drowned_quote_right_rect: Rect<usize>,
    pub level_up_rects: Vec<Rect<usize>>,
    pub level_down_rects: Vec<Rect<usize>>,
    pub explosion_rects: Vec<Rect<usize>>,
    pub little_particles_rects: Vec<Rect<usize>>,
    pub exhaust_rects: Vec<Rect<usize>>,
    pub question_left_rect: Rect<usize>,
    pub question_right_rect: Rect<usize>,
}

impl Clone for CaretConsts {
    fn clone(&self) -> Self {
        Self {
            offsets: self.offsets,
            bubble_left_rects: self.bubble_left_rects.clone(),
            bubble_right_rects: self.bubble_right_rects.clone(),
            projectile_dissipation_left_rects: self.projectile_dissipation_left_rects.clone(),
            projectile_dissipation_right_rects: self.projectile_dissipation_right_rects.clone(),
            projectile_dissipation_up_rects: self.projectile_dissipation_up_rects.clone(),
            shoot_rects: self.shoot_rects.clone(),
            zzz_rects: self.zzz_rects.clone(),
            drowned_quote_left_rect: self.drowned_quote_left_rect,
            drowned_quote_right_rect: self.drowned_quote_right_rect,
            level_up_rects: self.level_up_rects.clone(),
            level_down_rects: self.level_down_rects.clone(),
            explosion_rects: self.explosion_rects.clone(),
            little_particles_rects: self.little_particles_rects.clone(),
            exhaust_rects: self.exhaust_rects.clone(),
            question_left_rect: self.question_left_rect,
            question_right_rect: self.question_right_rect,
        }
    }
}


#[derive(Debug, Copy, Clone)]
pub struct BulletData {
    pub damage: u8,
    pub life: u8,
    pub lifetime: u16,
    pub flags: Flag,
    pub enemy_hit_width: u16,
    pub enemy_hit_height: u16,
    pub block_hit_width: u16,
    pub block_hit_height: u16,
    pub display_bounds: Rect<u8>,
}

#[derive(Debug, Copy, Clone)]
pub struct BulletRects {
    pub b004_polar_star_l1: [Rect<usize>; 2],
    pub b005_polar_star_l2: [Rect<usize>; 2],
    pub b006_polar_star_l3: [Rect<usize>; 2],
    pub b037_spur_l1: [Rect<usize>; 2],
    pub b038_spur_l2: [Rect<usize>; 2],
    pub b039_spur_l3: [Rect<usize>; 2],
    pub b040_spur_trail_l1: [Rect<usize>; 6],
    pub b041_spur_trail_l2: [Rect<usize>; 6],
    pub b042_spur_trail_l3: [Rect<usize>; 6],
}

#[derive(Debug)]
pub struct WeaponConsts {
    pub bullet_table: Vec<BulletData>,
    pub bullet_rects: BulletRects,
    pub level_table: [[u16; 3]; 14],
}

impl Clone for WeaponConsts {
    fn clone(&self) -> WeaponConsts {
        WeaponConsts {
            bullet_table: self.bullet_table.clone(),
            bullet_rects: self.bullet_rects,
            level_table: self.level_table,
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct WorldConsts {
    pub snack_rect: Rect<usize>,
}

#[derive(Debug, Copy, Clone)]
pub struct NPCConsts {
    pub n001_experience: [Rect<usize>; 6],
    pub n002_behemoth: [Rect<usize>; 14],
    pub n004_smoke: [Rect<usize>; 16],
    pub n005_green_critter: [Rect<usize>; 6],
    pub n006_green_beetle: [Rect<usize>; 10],
    pub n007_basil: [Rect<usize>; 6],
    pub n008_blue_beetle: [Rect<usize>; 4],
    pub n015_closed_chest: [Rect<usize>; 3],
    pub n016_save_point: [Rect<usize>; 8],
    pub n017_health_refill: [Rect<usize>; 2],
    pub n018_door: [Rect<usize>; 2],
    pub n020_computer: [Rect<usize>; 4],
    pub n021_chest_open: Rect<usize>,
    pub n022_teleporter: [Rect<usize>; 2],
    pub n023_teleporter_lights: [Rect<usize>; 8],
    pub n027_death_trap: Rect<usize>,
    pub n029_cthulhu: [Rect<usize>; 4],
    pub n030_hermit_gunsmith: [Rect<usize>; 3],
    pub n032_life_capsule: [Rect<usize>; 2],
    pub n034_bed: [Rect<usize>; 2],
    pub n035_mannan: [Rect<usize>; 8],
    pub n037_sign: [Rect<usize>; 2],
    pub n038_fireplace: [Rect<usize>; 4],
    pub n039_save_sign: [Rect<usize>; 2],
    pub n040_santa: [Rect<usize>; 14],
    pub n041_busted_door: Rect<usize>,
    pub n042_sue: [Rect<usize>; 26],
    pub n043_chalkboard: [Rect<usize>; 2],
    pub n044_polish: [Rect<usize>; 6],
    pub n045_baby: [Rect<usize>; 3],
    pub n047_sandcroc: [Rect<usize>; 5],
    pub n048_omega_projectiles: [Rect<usize>; 4],
    pub n049_skullhead: [Rect<usize>; 6],
    pub n052_sitting_blue_robot: Rect<usize>,
    pub n055_kazuma: [Rect<usize>; 12],
    pub n059_eye_door: [Rect<usize>; 4],
    pub n060_toroko: [Rect<usize>; 16],
    pub n061_king: [Rect<usize>; 20],
    pub n062_kazuma_computer: [Rect<usize>; 3],
    pub n063_toroko_stick: [Rect<usize>; 12],
    pub n064_first_cave_critter: [Rect<usize>; 6],
    pub n065_first_cave_bat: [Rect<usize>; 8],
    pub n070_sparkle: [Rect<usize>; 4],
    pub n071_chinfish: [Rect<usize>; 6],
    pub n072_sprinkler: [Rect<usize>; 2],
    pub n073_water_droplet: [Rect<usize>; 5],
    pub n074_jack: [Rect<usize>; 12],
    pub n075_kanpachi: [Rect<usize>; 2],
    pub n077_yamashita: [Rect<usize>; 3],
    pub n078_pot: [Rect<usize>; 2],
    pub n079_mahin: [Rect<usize>; 6],
    pub n211_small_spikes: [Rect<usize>; 4],
}

#[derive(Debug, Copy, Clone)]
pub struct TextScriptConsts {
    pub encoding: TextScriptEncoding,
    pub textbox_rect_top: Rect<usize>,
    pub textbox_rect_middle: Rect<usize>,
    pub textbox_rect_bottom: Rect<usize>,
    pub textbox_rect_yes_no: Rect<usize>,
    pub textbox_rect_cursor: Rect<usize>,
    pub get_item_top_left: Rect<usize>,
    pub get_item_bottom_left: Rect<usize>,
    pub get_item_top_right: Rect<usize>,
    pub get_item_right: Rect<usize>,
    pub get_item_bottom_right: Rect<usize>,
}

#[derive(Debug)]
pub struct EngineConstants {
    pub is_cs_plus: bool,
    pub my_char: MyCharConsts,
    pub booster: BoosterConsts,
    pub caret: CaretConsts,
    pub world: WorldConsts,
    pub npc: NPCConsts,
    pub weapon: WeaponConsts,
    pub tex_sizes: CaseInsensitiveHashMap<(usize, usize)>,
    pub textscript: TextScriptConsts,
    pub font_path: String,
    pub font_scale: f32,
    pub font_space_offset: f32,
    pub organya_paths: Vec<String>,
}

impl Clone for EngineConstants {
    fn clone(&self) -> EngineConstants {
        EngineConstants {
            is_cs_plus: self.is_cs_plus,
            my_char: self.my_char,
            booster: self.booster,
            caret: self.caret.clone(),
            world: self.world.clone(),
            npc: self.npc.clone(),
            weapon: self.weapon.clone(),
            tex_sizes: self.tex_sizes.clone(),
            textscript: self.textscript.clone(),
            font_path: self.font_path.clone(),
            font_scale: self.font_scale,
            font_space_offset: self.font_space_offset,
            organya_paths: self.organya_paths.clone(),
        }
    }
}

impl EngineConstants {
    pub fn defaults() -> Self {
        EngineConstants {
            is_cs_plus: false,
            my_char: MyCharConsts {
                display_bounds: Rect { left: 8 * 0x200, top: 8 * 0x200, right: 8 * 0x200, bottom: 8 * 0x200 },
                hit_bounds: Rect { left: 5 * 0x200, top: 8 * 0x200, right: 5 * 0x200, bottom: 8 * 0x200 },
                life: 3,
                max_life: 3,
                control_mode: ControlMode::Normal,
                air_physics: PhysicsConsts {
                    max_dash: 0x32c,
                    max_move: 0x5ff,
                    gravity_air: 0x20,
                    gravity_ground: 0x50,
                    dash_air: 0x20,
                    dash_ground: 0x55,
                    resist: 0x33,
                    jump: 0x500,
                },
                water_physics: PhysicsConsts {
                    max_dash: 0x196,
                    max_move: 0x2ff,
                    gravity_air: 0x10,
                    gravity_ground: 0x28,
                    dash_air: 0x10,
                    dash_ground: 0x2a,
                    resist: 0x19,
                    jump: 0x280,
                },
                animations_left: [
                    Rect { left: 0, top: 0, right: 16, bottom: 16 },
                    Rect { left: 16, top: 0, right: 32, bottom: 16 },
                    Rect { left: 0, top: 0, right: 16, bottom: 16 },
                    Rect { left: 32, top: 0, right: 48, bottom: 16 },
                    Rect { left: 0, top: 0, right: 16, bottom: 16 },
                    Rect { left: 48, top: 0, right: 64, bottom: 16 },
                    Rect { left: 64, top: 0, right: 80, bottom: 16 },
                    Rect { left: 48, top: 0, right: 64, bottom: 16 },
                    Rect { left: 80, top: 0, right: 96, bottom: 16 },
                    Rect { left: 48, top: 0, right: 64, bottom: 16 },
                    Rect { left: 96, top: 0, right: 112, bottom: 16 },
                    Rect { left: 112, top: 0, right: 128, bottom: 16 },
                ],
                animations_right: [
                    Rect { left: 0, top: 16, right: 16, bottom: 32 },
                    Rect { left: 16, top: 16, right: 32, bottom: 32 },
                    Rect { left: 0, top: 16, right: 16, bottom: 32 },
                    Rect { left: 32, top: 16, right: 48, bottom: 32 },
                    Rect { left: 0, top: 16, right: 16, bottom: 32 },
                    Rect { left: 48, top: 16, right: 64, bottom: 32 },
                    Rect { left: 64, top: 16, right: 80, bottom: 32 },
                    Rect { left: 48, top: 16, right: 64, bottom: 32 },
                    Rect { left: 80, top: 16, right: 96, bottom: 32 },
                    Rect { left: 48, top: 16, right: 64, bottom: 32 },
                    Rect { left: 96, top: 16, right: 112, bottom: 32 },
                    Rect { left: 112, top: 16, right: 128, bottom: 32 },
                ],
            },
            booster: BoosterConsts {
                fuel: 50,
                b2_0_up: -0x5ff,
                b2_0_up_nokey: -0x5ff,
                b2_0_down: 0x5ff,
                b2_0_left: -0x5ff,
                b2_0_right: 0x5ff,
            },
            caret: CaretConsts {
                offsets: [
                    (0, 0),
                    (4 * 0x200, 4 * 0x200),
                    (8 * 0x200, 8 * 0x200),
                    (8 * 0x200, 8 * 0x200),
                    (8 * 0x200, 8 * 0x200),
                    (4 * 0x200, 4 * 0x200),
                    (8 * 0x200, 8 * 0x200),
                    (4 * 0x200, 4 * 0x200),
                    (8 * 0x200, 8 * 0x200),
                    (8 * 0x200, 8 * 0x200),
                    (28 * 0x200, 8 * 0x200),
                    (4 * 0x200, 4 * 0x200),
                    (16 * 0x200, 16 * 0x200),
                    (4 * 0x200, 4 * 0x200),
                    (20 * 0x200, 20 * 0x200),
                    (4 * 0x200, 4 * 0x200),
                    (20 * 0x200, 4 * 0x200),
                    (52 * 0x200, 4 * 0x200),
                ],
                bubble_left_rects: vec![
                    Rect { left: 0, top: 64, right: 8, bottom: 72 },
                    Rect { left: 8, top: 64, right: 16, bottom: 72 },
                    Rect { left: 16, top: 64, right: 24, bottom: 72 },
                    Rect { left: 24, top: 64, right: 32, bottom: 72 },
                ],
                bubble_right_rects: vec![
                    Rect { left: 64, top: 24, right: 72, bottom: 32 },
                    Rect { left: 72, top: 24, right: 80, bottom: 32 },
                    Rect { left: 80, top: 24, right: 88, bottom: 32 },
                    Rect { left: 88, top: 24, right: 96, bottom: 32 },
                ],
                projectile_dissipation_left_rects: vec![
                    Rect { left: 0, top: 32, right: 16, bottom: 48 },
                    Rect { left: 16, top: 32, right: 32, bottom: 48 },
                    Rect { left: 32, top: 32, right: 48, bottom: 48 },
                    Rect { left: 48, top: 32, right: 64, bottom: 48 },
                ],
                projectile_dissipation_right_rects: vec![
                    Rect { left: 176, top: 0, right: 192, bottom: 16 },
                    Rect { left: 192, top: 0, right: 208, bottom: 16 },
                    Rect { left: 208, top: 0, right: 224, bottom: 16 },
                    Rect { left: 224, top: 0, right: 240, bottom: 16 },
                ],
                projectile_dissipation_up_rects: vec![
                    Rect { left: 0, top: 32, right: 16, bottom: 48 },
                    Rect { left: 32, top: 32, right: 48, bottom: 48 },
                    Rect { left: 16, top: 32, right: 32, bottom: 48 },
                ],
                shoot_rects: vec![
                    Rect { left: 0, top: 48, right: 16, bottom: 64 },
                    Rect { left: 16, top: 48, right: 32, bottom: 64 },
                    Rect { left: 32, top: 48, right: 48, bottom: 64 },
                    Rect { left: 48, top: 48, right: 64, bottom: 64 },
                ],
                zzz_rects: vec![
                    Rect { left: 32, top: 64, right: 40, bottom: 72 },
                    Rect { left: 32, top: 72, right: 40, bottom: 80 },
                    Rect { left: 40, top: 64, right: 48, bottom: 72 },
                    Rect { left: 40, top: 72, right: 48, bottom: 80 },
                    Rect { left: 40, top: 64, right: 48, bottom: 72 },
                    Rect { left: 40, top: 72, right: 48, bottom: 80 },
                    Rect { left: 40, top: 64, right: 48, bottom: 72 },
                ],
                drowned_quote_left_rect: Rect { left: 16, top: 80, right: 32, bottom: 96 },
                drowned_quote_right_rect: Rect { left: 32, top: 80, right: 48, bottom: 96 },
                level_up_rects: vec![
                    Rect { left: 0, top: 0, right: 56, bottom: 16 },
                    Rect { left: 0, top: 16, right: 56, bottom: 32 },
                ],
                level_down_rects: vec![
                    Rect { left: 0, top: 96, right: 56, bottom: 112 },
                    Rect { left: 0, top: 112, right: 56, bottom: 128 },
                ],
                explosion_rects: vec![
                    Rect { left: 112, top: 0, right: 144, bottom: 32 },
                    Rect { left: 144, top: 0, right: 176, bottom: 32 },
                ],
                little_particles_rects: vec![
                    Rect { left: 56, top: 24, right: 64, bottom: 32 },
                    Rect { left: 0, top: 0, right: 0, bottom: 0 },
                ],
                exhaust_rects: vec![
                    Rect { left: 56, top: 0, right: 64, bottom: 8 },
                    Rect { left: 64, top: 0, right: 72, bottom: 8 },
                    Rect { left: 72, top: 0, right: 80, bottom: 8 },
                    Rect { left: 80, top: 0, right: 88, bottom: 8 },
                    Rect { left: 88, top: 0, right: 96, bottom: 8 },
                    Rect { left: 96, top: 0, right: 104, bottom: 8 },
                    Rect { left: 104, top: 0, right: 112, bottom: 8 },
                ],
                question_left_rect: Rect { left: 0, top: 80, right: 16, bottom: 96 },
                question_right_rect: Rect { left: 48, top: 64, right: 64, bottom: 80 },
            },
            world: WorldConsts {
                snack_rect: Rect { left: 256, top: 48, right: 272, bottom: 64 },
            },
            npc: NPCConsts {
                n001_experience: [
                    Rect { left: 0, top: 16, right: 16, bottom: 32 },
                    Rect { left: 16, top: 16, right: 32, bottom: 32 },
                    Rect { left: 32, top: 16, right: 48, bottom: 32 },
                    Rect { left: 48, top: 16, right: 64, bottom: 32 },
                    Rect { left: 64, top: 16, right: 80, bottom: 32 },
                    Rect { left: 80, top: 16, right: 96, bottom: 32 },
                ],
                n002_behemoth: [
                    Rect { left: 32, top: 0, right: 64, bottom: 24 }, // left
                    Rect { left: 0, top: 0, right: 32, bottom: 24 },
                    Rect { left: 32, top: 0, right: 64, bottom: 24 },
                    Rect { left: 64, top: 0, right: 96, bottom: 24 },
                    Rect { left: 96, top: 0, right: 128, bottom: 24 },
                    Rect { left: 128, top: 0, right: 160, bottom: 24 },
                    Rect { left: 160, top: 0, right: 192, bottom: 24 },
                    Rect { left: 32, top: 24, right: 64, bottom: 48 }, // right
                    Rect { left: 0, top: 24, right: 32, bottom: 48 },
                    Rect { left: 32, top: 24, right: 64, bottom: 48 },
                    Rect { left: 64, top: 24, right: 96, bottom: 48 },
                    Rect { left: 96, top: 24, right: 128, bottom: 48 },
                    Rect { left: 128, top: 24, right: 160, bottom: 48 },
                    Rect { left: 160, top: 24, right: 192, bottom: 48 },
                ],
                n004_smoke: [
                    Rect { left: 16, top: 0, right: 17, bottom: 1 }, // left
                    Rect { left: 16, top: 0, right: 32, bottom: 16 },
                    Rect { left: 32, top: 0, right: 48, bottom: 16 },
                    Rect { left: 48, top: 0, right: 64, bottom: 16 },
                    Rect { left: 64, top: 0, right: 80, bottom: 16 },
                    Rect { left: 80, top: 0, right: 96, bottom: 16 },
                    Rect { left: 96, top: 0, right: 112, bottom: 16 },
                    Rect { left: 112, top: 0, right: 128, bottom: 16 },
                    Rect { left: 16, top: 0, right: 17, bottom: 1 }, // right
                    Rect { left: 80, top: 48, right: 96, bottom: 64 },
                    Rect { left: 0, top: 128, right: 16, bottom: 144 },
                    Rect { left: 16, top: 128, right: 32, bottom: 144 },
                    Rect { left: 32, top: 128, right: 48, bottom: 144 },
                    Rect { left: 48, top: 128, right: 64, bottom: 144 },
                    Rect { left: 64, top: 128, right: 80, bottom: 144 },
                    Rect { left: 80, top: 128, right: 96, bottom: 144 },
                ],
                n005_green_critter: [
                    Rect { left: 0, top: 48, right: 16, bottom: 64 }, // left
                    Rect { left: 16, top: 48, right: 32, bottom: 64 },
                    Rect { left: 32, top: 48, right: 48, bottom: 64 },
                    Rect { left: 0, top: 64, right: 16, bottom: 80 }, // right
                    Rect { left: 16, top: 64, right: 32, bottom: 80 },
                    Rect { left: 32, top: 64, right: 48, bottom: 80 },
                ],
                n006_green_beetle: [
                    Rect { left: 0, top: 80, right: 16, bottom: 96 }, // left
                    Rect { left: 16, top: 80, right: 32, bottom: 96 },
                    Rect { left: 32, top: 80, right: 48, bottom: 96 },
                    Rect { left: 48, top: 80, right: 64, bottom: 96 },
                    Rect { left: 64, top: 80, right: 80, bottom: 96 },
                    Rect { left: 0, top: 96, right: 16, bottom: 112 }, // right
                    Rect { left: 16, top: 96, right: 32, bottom: 112 },
                    Rect { left: 32, top: 96, right: 48, bottom: 112 },
                    Rect { left: 48, top: 96, right: 64, bottom: 112 },
                    Rect { left: 64, top: 96, right: 80, bottom: 112 },
                ],
                n007_basil: [
                    Rect { left: 256, top: 64, right: 288, bottom: 80 }, // left
                    Rect { left: 256, top: 80, right: 288, bottom: 96 },
                    Rect { left: 256, top: 96, right: 288, bottom: 112 },
                    Rect { left: 288, top: 64, right: 320, bottom: 80 }, // right
                    Rect { left: 288, top: 80, right: 320, bottom: 96 },
                    Rect { left: 288, top: 96, right: 320, bottom: 112 },
                ],
                n008_blue_beetle: [
                    Rect { left: 80, top: 80, right: 96, bottom: 96 },// left
                    Rect { left: 96, top: 80, right: 112, bottom: 96 },
                    Rect { left: 80, top: 96, right: 96, bottom: 112 }, // right
                    Rect { left: 96, top: 96, right: 112, bottom: 112 },
                ],
                n015_closed_chest: [
                    Rect { left: 240, top: 0, right: 256, bottom: 16 },
                    Rect { left: 256, top: 0, right: 272, bottom: 16 },
                    Rect { left: 272, top: 0, right: 288, bottom: 16 },
                ],
                n016_save_point: [
                    Rect { left: 96, top: 16, right: 112, bottom: 32 },
                    Rect { left: 112, top: 16, right: 128, bottom: 32 },
                    Rect { left: 128, top: 16, right: 144, bottom: 32 },
                    Rect { left: 144, top: 16, right: 160, bottom: 32 },
                    Rect { left: 160, top: 16, right: 176, bottom: 32 },
                    Rect { left: 176, top: 16, right: 192, bottom: 32 },
                    Rect { left: 192, top: 16, right: 208, bottom: 32 },
                    Rect { left: 208, top: 16, right: 224, bottom: 32 },
                ],
                n017_health_refill: [
                    Rect { left: 288, top: 0, right: 304, bottom: 16 },
                    Rect { left: 304, top: 0, right: 320, bottom: 16 },
                ],
                n018_door: [
                    Rect { left: 224, top: 16, right: 240, bottom: 40 },
                    Rect { left: 192, top: 112, right: 208, bottom: 136 },
                ],
                n020_computer: [
                    Rect { left: 288, top: 16, right: 320, bottom: 40 }, // left

                    Rect { left: 288, top: 40, right: 320, bottom: 64 }, // right
                    Rect { left: 288, top: 40, right: 320, bottom: 64 },
                    Rect { left: 288, top: 64, right: 320, bottom: 88 },
                ],
                n021_chest_open: Rect { left: 224, top: 40, right: 240, bottom: 48 },
                n022_teleporter: [
                    Rect { left: 240, top: 16, right: 264, bottom: 48 },
                    Rect { left: 248, top: 152, right: 272, bottom: 184 },
                ],
                n023_teleporter_lights: [
                    Rect { left: 264, top: 16, right: 288, bottom: 20 },
                    Rect { left: 264, top: 20, right: 288, bottom: 24 },
                    Rect { left: 264, top: 24, right: 288, bottom: 28 },
                    Rect { left: 264, top: 28, right: 288, bottom: 32 },
                    Rect { left: 264, top: 32, right: 288, bottom: 36 },
                    Rect { left: 264, top: 36, right: 288, bottom: 40 },
                    Rect { left: 264, top: 40, right: 288, bottom: 44 },
                    Rect { left: 264, top: 44, right: 288, bottom: 48 },
                ],
                n027_death_trap: Rect { left: 96, top: 64, right: 128, bottom: 88 },
                n029_cthulhu: [
                    Rect { left: 0, top: 192, right: 16, bottom: 216 }, // left
                    Rect { left: 16, top: 192, right: 32, bottom: 216 },
                    Rect { left: 0, top: 216, right: 16, bottom: 240 }, // right
                    Rect { left: 16, top: 216, right: 32, bottom: 240 },
                ],
                n030_hermit_gunsmith: [
                    Rect { left: 48, top: 0, right: 64, bottom: 16 },
                    Rect { left: 48, top: 16, right: 64, bottom: 32 },
                    Rect { left: 0, top: 32, right: 16, bottom: 48 },
                ],
                n032_life_capsule: [
                    Rect { left: 32, top: 96, right: 48, bottom: 112 },
                    Rect { left: 48, top: 96, right: 64, bottom: 112 },
                ],
                n034_bed: [
                    Rect { left: 192, top: 48, right: 224, bottom: 64 },
                    Rect { left: 192, top: 184, right: 224, bottom: 200 },
                ],
                n035_mannan: [
                    Rect { left: 96, top: 64, right: 120, bottom: 96 }, // left
                    Rect { left: 120, top: 64, right: 144, bottom: 96 },
                    Rect { left: 144, top: 64, right: 168, bottom: 96 },
                    Rect { left: 168, top: 64, right: 192, bottom: 96 },
                    Rect { left: 96, top: 96, right: 120, bottom: 128 }, // right
                    Rect { left: 120, top: 96, right: 144, bottom: 128 },
                    Rect { left: 144, top: 96, right: 168, bottom: 128 },
                    Rect { left: 168, top: 96, right: 192, bottom: 128 },
                ],
                n037_sign: [
                    Rect { left: 192, top: 64, right: 208, bottom: 80 },
                    Rect { left: 208, top: 64, right: 224, bottom: 80 },
                ],
                n038_fireplace: [
                    Rect { left: 128, top: 64, right: 144, bottom: 80 },
                    Rect { left: 144, top: 64, right: 160, bottom: 80 },
                    Rect { left: 160, top: 64, right: 176, bottom: 80 },
                    Rect { left: 176, top: 64, right: 192, bottom: 80 },
                ],
                n039_save_sign: [
                    Rect { left: 224, top: 64, right: 240, bottom: 80 },
                    Rect { left: 240, top: 64, right: 256, bottom: 80 },
                ],
                n040_santa: [
                    Rect { left: 0, top: 32, right: 16, bottom: 48 }, // left
                    Rect { left: 16, top: 32, right: 32, bottom: 48 },
                    Rect { left: 32, top: 32, right: 48, bottom: 48 },
                    Rect { left: 0, top: 32, right: 16, bottom: 48 },
                    Rect { left: 48, top: 32, right: 64, bottom: 48 },
                    Rect { left: 0, top: 32, right: 16, bottom: 48 },
                    Rect { left: 64, top: 32, right: 80, bottom: 48 },
                    Rect { left: 0, top: 48, right: 16, bottom: 64 }, // right
                    Rect { left: 16, top: 48, right: 32, bottom: 64 },
                    Rect { left: 32, top: 48, right: 48, bottom: 64 },
                    Rect { left: 0, top: 48, right: 16, bottom: 64 },
                    Rect { left: 48, top: 48, right: 64, bottom: 64 },
                    Rect { left: 0, top: 48, right: 16, bottom: 64 },
                    Rect { left: 64, top: 48, right: 80, bottom: 64 },
                ],
                n041_busted_door: Rect { left: 0, top: 80, right: 48, bottom: 112 },
                n042_sue: [
                    Rect { left: 0, top: 0, right: 16, bottom: 16 }, // left
                    Rect { left: 16, top: 0, right: 32, bottom: 16 },
                    Rect { left: 32, top: 0, right: 48, bottom: 16 },
                    Rect { left: 0, top: 0, right: 16, bottom: 16 },
                    Rect { left: 48, top: 0, right: 64, bottom: 16 },
                    Rect { left: 0, top: 0, right: 16, bottom: 16 },
                    Rect { left: 64, top: 0, right: 80, bottom: 16 },
                    Rect { left: 80, top: 32, right: 96, bottom: 48 },
                    Rect { left: 96, top: 32, right: 112, bottom: 48 },
                    Rect { left: 128, top: 32, right: 144, bottom: 48 },
                    Rect { left: 0, top: 0, right: 16, bottom: 16 },
                    Rect { left: 112, top: 32, right: 128, bottom: 48 },
                    Rect { left: 160, top: 32, right: 176, bottom: 48 },
                    Rect { left: 0, top: 16, right: 16, bottom: 32 }, // right
                    Rect { left: 16, top: 16, right: 32, bottom: 32 },
                    Rect { left: 32, top: 16, right: 48, bottom: 32 },
                    Rect { left: 0, top: 16, right: 16, bottom: 32 },
                    Rect { left: 48, top: 16, right: 64, bottom: 32 },
                    Rect { left: 0, top: 16, right: 16, bottom: 32 },
                    Rect { left: 64, top: 16, right: 80, bottom: 32 },
                    Rect { left: 80, top: 48, right: 96, bottom: 64 },
                    Rect { left: 96, top: 48, right: 112, bottom: 64 },
                    Rect { left: 128, top: 48, right: 144, bottom: 64 },
                    Rect { left: 0, top: 16, right: 16, bottom: 32 },
                    Rect { left: 112, top: 48, right: 128, bottom: 64 },
                    Rect { left: 160, top: 48, right: 176, bottom: 64 },
                ],
                n043_chalkboard: [
                    Rect { left: 128, top: 80, right: 168, bottom: 112 },
                    Rect { left: 168, top: 80, right: 208, bottom: 112 },
                ],
                n044_polish: [
                    Rect { left: 0, top: 0, right: 32, bottom: 32 }, // left
                    Rect { left: 96, top: 0, right: 128, bottom: 32 },
                    Rect { left: 128, top: 0, right: 160, bottom: 32 },
                    Rect { left: 0, top: 0, right: 32, bottom: 32 }, // right
                    Rect { left: 32, top: 0, right: 64, bottom: 32 },
                    Rect { left: 64, top: 0, right: 96, bottom: 32 },
                ],
                n045_baby: [
                    Rect { left: 0, top: 32, right: 16, bottom: 48 },
                    Rect { left: 16, top: 32, right: 32, bottom: 48 },
                    Rect { left: 32, top: 32, right: 48, bottom: 48 },
                ],
                n047_sandcroc: [
                    Rect { left: 0, top: 48, right: 48, bottom: 80 },
                    Rect { left: 48, top: 48, right: 96, bottom: 80 },
                    Rect { left: 96, top: 48, right: 144, bottom: 80 },
                    Rect { left: 144, top: 48, right: 192, bottom: 80 },
                    Rect { left: 192, top: 48, right: 240, bottom: 80 },
                ],
                n048_omega_projectiles: [
                    Rect { left: 288, top: 88, right: 304, bottom: 104 }, // left
                    Rect { left: 304, top: 88, right: 320, bottom: 104 },
                    Rect { left: 288, top: 104, right: 304, bottom: 120 }, // right
                    Rect { left: 304, top: 104, right: 320, bottom: 120 },
                ],
                n049_skullhead: [
                    Rect { left: 0, top: 80, right: 32, bottom: 104 }, // left
                    Rect { left: 32, top: 80, right: 64, bottom: 104 },
                    Rect { left: 64, top: 80, right: 96, bottom: 104 },
                    Rect { left: 0, top: 104, right: 32, bottom: 128 }, // right
                    Rect { left: 32, top: 104, right: 64, bottom: 128 },
                    Rect { left: 64, top: 104, right: 96, bottom: 128 },
                ],
                n052_sitting_blue_robot: Rect { left: 240, top: 96, right: 256, bottom: 112 },
                n055_kazuma: [
                    Rect { left: 192, top: 192, right: 208, bottom: 216 }, // left
                    Rect { left: 208, top: 192, right: 224, bottom: 216 },
                    Rect { left: 192, top: 192, right: 208, bottom: 216 },
                    Rect { left: 224, top: 192, right: 240, bottom: 216 },
                    Rect { left: 192, top: 192, right: 208, bottom: 216 },
                    Rect { left: 240, top: 192, right: 256, bottom: 216 },
                    Rect { left: 192, top: 216, right: 208, bottom: 240 }, // right
                    Rect { left: 208, top: 216, right: 224, bottom: 240 },
                    Rect { left: 192, top: 216, right: 208, bottom: 240 },
                    Rect { left: 224, top: 216, right: 240, bottom: 240 },
                    Rect { left: 192, top: 216, right: 208, bottom: 240 },
                    Rect { left: 240, top: 216, right: 256, bottom: 240 },
                ],
                n059_eye_door: [
                    Rect { left: 224, top: 16, right: 240, bottom: 40 },
                    Rect { left: 208, top: 80, right: 224, bottom: 104 },
                    Rect { left: 224, top: 80, right: 240, bottom: 104 },
                    Rect { left: 240, top: 80, right: 256, bottom: 104 },
                ],
                n060_toroko: [
                    Rect { left: 0, top: 64, right: 16, bottom: 80 }, // left
                    Rect { left: 16, top: 64, right: 32, bottom: 80 },
                    Rect { left: 32, top: 64, right: 48, bottom: 80 },
                    Rect { left: 16, top: 64, right: 32, bottom: 80 },
                    Rect { left: 48, top: 64, right: 64, bottom: 80 },
                    Rect { left: 16, top: 64, right: 32, bottom: 80 },
                    Rect { left: 112, top: 64, right: 128, bottom: 80 },
                    Rect { left: 128, top: 64, right: 144, bottom: 80 },
                    Rect { left: 0, top: 80, right: 16, bottom: 96 }, // right
                    Rect { left: 16, top: 80, right: 32, bottom: 96 },
                    Rect { left: 32, top: 80, right: 48, bottom: 96 },
                    Rect { left: 16, top: 80, right: 32, bottom: 96 },
                    Rect { left: 48, top: 80, right: 64, bottom: 96 },
                    Rect { left: 16, top: 80, right: 32, bottom: 96 },
                    Rect { left: 112, top: 80, right: 128, bottom: 96 },
                    Rect { left: 128, top: 80, right: 144, bottom: 96 },
                ],
                n061_king: [
                    Rect { left: 224, top: 32, right: 240, bottom: 48 }, // left
                    Rect { left: 240, top: 32, right: 256, bottom: 48 },
                    Rect { left: 256, top: 32, right: 272, bottom: 48 },
                    Rect { left: 272, top: 32, right: 288, bottom: 48 },
                    Rect { left: 288, top: 32, right: 304, bottom: 48 },
                    Rect { left: 224, top: 32, right: 240, bottom: 48 },
                    Rect { left: 304, top: 32, right: 320, bottom: 48 },
                    Rect { left: 224, top: 32, right: 240, bottom: 48 },
                    Rect { left: 272, top: 32, right: 288, bottom: 48 },
                    Rect { left: 0, top: 0, right: 0, bottom: 0 },
                    Rect { left: 224, top: 48, right: 240, bottom: 64 }, // right
                    Rect { left: 240, top: 48, right: 256, bottom: 64 },
                    Rect { left: 256, top: 48, right: 272, bottom: 64 },
                    Rect { left: 272, top: 48, right: 288, bottom: 64 },
                    Rect { left: 288, top: 48, right: 304, bottom: 64 },
                    Rect { left: 224, top: 48, right: 240, bottom: 64 },
                    Rect { left: 304, top: 48, right: 320, bottom: 64 },
                    Rect { left: 224, top: 48, right: 240, bottom: 64 },
                    Rect { left: 272, top: 48, right: 288, bottom: 64 },
                    Rect { left: 0, top: 0, right: 0, bottom: 0 },
                ],
                n062_kazuma_computer: [
                    Rect { left: 272, top: 192, right: 288, bottom: 216 },
                    Rect { left: 288, top: 192, right: 304, bottom: 216 },
                    Rect { left: 304, top: 192, right: 320, bottom: 216 },
                ],
                n063_toroko_stick: [
                    Rect { left: 64, top: 64, right: 80, bottom: 80 }, // left
                    Rect { left: 80, top: 64, right: 96, bottom: 80 },
                    Rect { left: 64, top: 64, right: 80, bottom: 80 },
                    Rect { left: 96, top: 64, right: 112, bottom: 80 },
                    Rect { left: 112, top: 64, right: 128, bottom: 80 },
                    Rect { left: 128, top: 64, right: 144, bottom: 80 },
                    Rect { left: 64, top: 80, right: 80, bottom: 96 }, // right
                    Rect { left: 80, top: 80, right: 96, bottom: 96 },
                    Rect { left: 64, top: 80, right: 80, bottom: 96 },
                    Rect { left: 96, top: 80, right: 112, bottom: 96 },
                    Rect { left: 112, top: 80, right: 128, bottom: 96 },
                    Rect { left: 128, top: 80, right: 144, bottom: 96 },
                ],
                n064_first_cave_critter: [
                    Rect { left: 0, top: 0, right: 16, bottom: 16 },
                    Rect { left: 16, top: 0, right: 32, bottom: 16 },
                    Rect { left: 32, top: 0, right: 48, bottom: 16 },
                    Rect { left: 0, top: 16, right: 16, bottom: 32 },
                    Rect { left: 16, top: 16, right: 32, bottom: 32 },
                    Rect { left: 32, top: 16, right: 48, bottom: 32 },
                ],
                n065_first_cave_bat: [
                    Rect { left: 32, top: 32, right: 48, bottom: 48 },
                    Rect { left: 48, top: 32, right: 64, bottom: 48 },
                    Rect { left: 64, top: 32, right: 80, bottom: 48 },
                    Rect { left: 80, top: 32, right: 96, bottom: 48 },
                    Rect { left: 32, top: 48, right: 48, bottom: 64 },
                    Rect { left: 48, top: 48, right: 64, bottom: 64 },
                    Rect { left: 64, top: 48, right: 80, bottom: 64 },
                    Rect { left: 80, top: 48, right: 96, bottom: 64 },
                ],
                n070_sparkle: [
                    Rect { left: 96, top: 48, right: 112, bottom: 64 },
                    Rect { left: 112, top: 48, right: 128, bottom: 64 },
                    Rect { left: 128, top: 48, right: 144, bottom: 64 },
                    Rect { left: 144, top: 48, right: 160, bottom: 64 },
                ],
                n071_chinfish: [
                    Rect { left: 64, top: 32, right: 80, bottom: 48 }, // left
                    Rect { left: 80, top: 32, right: 96, bottom: 48 },
                    Rect { left: 96, top: 32, right: 112, bottom: 48 },
                    Rect { left: 64, top: 48, right: 80, bottom: 64 }, // right
                    Rect { left: 80, top: 48, right: 96, bottom: 64 },
                    Rect { left: 96, top: 48, right: 112, bottom: 64 },
                ],
                n072_sprinkler: [
                    Rect { left: 224, top: 48, right: 240, bottom: 64 },
                    Rect { left: 240, top: 48, right: 256, bottom: 64 },
                ],
                n073_water_droplet: [
                    Rect { left: 72, top: 16, right: 74, bottom: 18 },
                    Rect { left: 74, top: 16, right: 76, bottom: 18 },
                    Rect { left: 76, top: 16, right: 78, bottom: 18 },
                    Rect { left: 78, top: 16, right: 80, bottom: 18 },
                    Rect { left: 80, top: 16, right: 82, bottom: 18 },
                ],
                n074_jack: [
                    Rect { left: 64, top: 0, right: 80, bottom: 16 }, // left
                    Rect { left: 80, top: 0, right: 96, bottom: 16 },
                    Rect { left: 96, top: 0, right: 112, bottom: 16 },
                    Rect { left: 64, top: 0, right: 80, bottom: 16 },
                    Rect { left: 112, top: 0, right: 128, bottom: 16 },
                    Rect { left: 64, top: 0, right: 80, bottom: 16 },
                    Rect { left: 64, top: 16, right: 80, bottom: 32 }, // right
                    Rect { left: 80, top: 16, right: 96, bottom: 32 },
                    Rect { left: 96, top: 16, right: 112, bottom: 32 },
                    Rect { left: 64, top: 16, right: 80, bottom: 32 },
                    Rect { left: 112, top: 16, right: 128, bottom: 32 },
                    Rect { left: 64, top: 16, right: 80, bottom: 32 },
                ],
                n075_kanpachi: [
                    Rect { left: 272, top: 32, right: 296, bottom: 56 },
                    Rect { left: 296, top: 32, right: 320, bottom: 56 },
                ],
                n077_yamashita: [
                    Rect { left: 0, top: 16, right: 48, bottom: 48 },
                    Rect { left: 48, top: 16, right: 96, bottom: 48 },
                    Rect { left: 96, top: 16, right: 144, bottom: 48 },
                ],
                n078_pot: [
                    Rect { left: 160, top: 48, right: 176, bottom: 64 },
                    Rect { left: 176, top: 48, right: 192, bottom: 64 },
                ],
                n079_mahin: [
                    Rect { left: 0, top: 0, right: 16, bottom: 16 }, // left
                    Rect { left: 16, top: 0, right: 32, bottom: 16 },
                    Rect { left: 32, top: 0, right: 48, bottom: 16 },
                    Rect { left: 0, top: 16, right: 16, bottom: 32 }, // right
                    Rect { left: 16, top: 16, right: 32, bottom: 32 },
                    Rect { left: 32, top: 16, right: 48, bottom: 32 },
                ],
                n211_small_spikes: [
                    Rect { left: 256, top: 200, right: 272, bottom: 216 },
                    Rect { left: 272, top: 200, right: 288, bottom: 216 },
                    Rect { left: 288, top: 200, right: 304, bottom: 216 },
                    Rect { left: 304, top: 200, right: 320, bottom: 216 },
                ],
            },
            weapon: WeaponConsts {
                bullet_table: vec![
                    // Null
                    BulletData { damage: 0, life: 0, lifetime: 0, flags: Flag(0), enemy_hit_width: 0, enemy_hit_height: 0, block_hit_width: 0, block_hit_height: 0, display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 } },
                    // Snake
                    BulletData { damage: 4, life: 1, lifetime: 20, flags: Flag(36), enemy_hit_width: 4, enemy_hit_height: 4, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 6, life: 1, lifetime: 23, flags: Flag(36), enemy_hit_width: 4, enemy_hit_height: 4, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 8, life: 1, lifetime: 30, flags: Flag(36), enemy_hit_width: 4, enemy_hit_height: 4, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    // Polar Star
                    BulletData { damage: 1, life: 1, lifetime: 8, flags: Flag(32), enemy_hit_width: 6, enemy_hit_height: 6, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 2, life: 1, lifetime: 12, flags: Flag(32), enemy_hit_width: 6, enemy_hit_height: 6, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 4, life: 1, lifetime: 16, flags: Flag(32), enemy_hit_width: 6, enemy_hit_height: 6, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    // Fireball
                    BulletData { damage: 2, life: 2, lifetime: 100, flags: Flag(8), enemy_hit_width: 8, enemy_hit_height: 16, block_hit_width: 4, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 3, life: 2, lifetime: 100, flags: Flag(8), enemy_hit_width: 4, enemy_hit_height: 4, block_hit_width: 4, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 3, life: 2, lifetime: 100, flags: Flag(8), enemy_hit_width: 4, enemy_hit_height: 4, block_hit_width: 4, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    // Machine Gun
                    BulletData { damage: 2, life: 1, lifetime: 20, flags: Flag(32), enemy_hit_width: 2, enemy_hit_height: 2, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 4, life: 1, lifetime: 20, flags: Flag(32), enemy_hit_width: 2, enemy_hit_height: 2, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 6, life: 1, lifetime: 20, flags: Flag(32), enemy_hit_width: 2, enemy_hit_height: 2, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    // Missile Launcher
                    BulletData { damage: 0, life: 10, lifetime: 50, flags: Flag(40), enemy_hit_width: 2, enemy_hit_height: 2, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 0, life: 10, lifetime: 70, flags: Flag(40), enemy_hit_width: 4, enemy_hit_height: 4, block_hit_width: 4, block_hit_height: 4, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 0, life: 10, lifetime: 90, flags: Flag(40), enemy_hit_width: 4, enemy_hit_height: 4, block_hit_width: 0, block_hit_height: 0, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    // Missile Launcher explosion
                    BulletData { damage: 1, life: 100, lifetime: 100, flags: Flag(20), enemy_hit_width: 16, enemy_hit_height: 16, block_hit_width: 0, block_hit_height: 0, display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 } },
                    BulletData { damage: 1, life: 100, lifetime: 100, flags: Flag(20), enemy_hit_width: 16, enemy_hit_height: 16, block_hit_width: 0, block_hit_height: 0, display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 } },
                    BulletData { damage: 1, life: 100, lifetime: 100, flags: Flag(20), enemy_hit_width: 16, enemy_hit_height: 16, block_hit_width: 0, block_hit_height: 0, display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 } },
                    // Bubbler
                    BulletData { damage: 1, life: 1, lifetime: 20, flags: Flag(8), enemy_hit_width: 2, enemy_hit_height: 2, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 4, top: 4, right: 4, bottom: 4 } },
                    BulletData { damage: 2, life: 1, lifetime: 20, flags: Flag(8), enemy_hit_width: 2, enemy_hit_height: 2, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 4, top: 4, right: 4, bottom: 4 } },
                    BulletData { damage: 2, life: 1, lifetime: 20, flags: Flag(8), enemy_hit_width: 4, enemy_hit_height: 4, block_hit_width: 4, block_hit_height: 4, display_bounds: Rect { left: 4, top: 4, right: 4, bottom: 4 } },
                    // Bubbler level 3 thorns
                    BulletData { damage: 3, life: 1, lifetime: 32, flags: Flag(32), enemy_hit_width: 2, enemy_hit_height: 2, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 4, top: 4, right: 4, bottom: 4 } },
                    // Blade slashes
                    BulletData { damage: 0, life: 100, lifetime: 0, flags: Flag(36), enemy_hit_width: 8, enemy_hit_height: 8, block_hit_width: 8, block_hit_height: 8, display_bounds: Rect { left: 12, top: 12, right: 12, bottom: 12 } },
                    // Falling spike
                    BulletData { damage: 127, life: 1, lifetime: 2, flags: Flag(4), enemy_hit_width: 8, enemy_hit_height: 4, block_hit_width: 8, block_hit_height: 4, display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 } },
                    // Blade
                    BulletData { damage: 15, life: 1, lifetime: 30, flags: Flag(36), enemy_hit_width: 8, enemy_hit_height: 8, block_hit_width: 4, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 6, life: 3, lifetime: 18, flags: Flag(36), enemy_hit_width: 10, enemy_hit_height: 10, block_hit_width: 4, block_hit_height: 2, display_bounds: Rect { left: 12, top: 12, right: 12, bottom: 12 } },
                    BulletData { damage: 1, life: 100, lifetime: 30, flags: Flag(36), enemy_hit_width: 6, enemy_hit_height: 6, block_hit_width: 4, block_hit_height: 4, display_bounds: Rect { left: 12, top: 12, right: 12, bottom: 12 } },
                    // Super Missile Launcher
                    BulletData { damage: 0, life: 10, lifetime: 30, flags: Flag(40), enemy_hit_width: 2, enemy_hit_height: 2, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 0, life: 10, lifetime: 40, flags: Flag(40), enemy_hit_width: 4, enemy_hit_height: 4, block_hit_width: 4, block_hit_height: 4, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 0, life: 10, lifetime: 40, flags: Flag(40), enemy_hit_width: 4, enemy_hit_height: 4, block_hit_width: 0, block_hit_height: 0, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    // Super Missile Launcher explosion
                    BulletData { damage: 2, life: 100, lifetime: 100, flags: Flag(20), enemy_hit_width: 12, enemy_hit_height: 12, block_hit_width: 0, block_hit_height: 0, display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 } },
                    BulletData { damage: 2, life: 100, lifetime: 100, flags: Flag(20), enemy_hit_width: 12, enemy_hit_height: 12, block_hit_width: 0, block_hit_height: 0, display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 } },
                    BulletData { damage: 2, life: 100, lifetime: 100, flags: Flag(20), enemy_hit_width: 12, enemy_hit_height: 12, block_hit_width: 0, block_hit_height: 0, display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 } },
                    // Nemesis
                    BulletData { damage: 4, life: 4, lifetime: 20, flags: Flag(32), enemy_hit_width: 4, enemy_hit_height: 4, block_hit_width: 3, block_hit_height: 3, display_bounds: Rect { left: 8, top: 8, right: 24, bottom: 8 } },
                    BulletData { damage: 4, life: 2, lifetime: 20, flags: Flag(32), enemy_hit_width: 2, enemy_hit_height: 2, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 24, bottom: 8 } },
                    BulletData { damage: 1, life: 1, lifetime: 20, flags: Flag(32), enemy_hit_width: 2, enemy_hit_height: 2, block_hit_width: 2, block_hit_height: 2, display_bounds: Rect { left: 8, top: 8, right: 24, bottom: 8 } },
                    // Spur
                    BulletData { damage: 4, life: 4, lifetime: 30, flags: Flag(64), enemy_hit_width: 6, enemy_hit_height: 6, block_hit_width: 3, block_hit_height: 3, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 8, life: 8, lifetime: 30, flags: Flag(64), enemy_hit_width: 6, enemy_hit_height: 6, block_hit_width: 3, block_hit_height: 3, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    BulletData { damage: 12, life: 12, lifetime: 30, flags: Flag(64), enemy_hit_width: 6, enemy_hit_height: 6, block_hit_width: 3, block_hit_height: 3, display_bounds: Rect { left: 8, top: 8, right: 8, bottom: 8 } },
                    // Spur trail
                    BulletData { damage: 3, life: 100, lifetime: 30, flags: Flag(32), enemy_hit_width: 6, enemy_hit_height: 6, block_hit_width: 3, block_hit_height: 3, display_bounds: Rect { left: 4, top: 4, right: 4, bottom: 4 } },
                    BulletData { damage: 6, life: 100, lifetime: 30, flags: Flag(32), enemy_hit_width: 6, enemy_hit_height: 6, block_hit_width: 3, block_hit_height: 3, display_bounds: Rect { left: 4, top: 4, right: 4, bottom: 4 } },
                    BulletData { damage: 11, life: 100, lifetime: 30, flags: Flag(32), enemy_hit_width: 6, enemy_hit_height: 6, block_hit_width: 3, block_hit_height: 3, display_bounds: Rect { left: 4, top: 4, right: 4, bottom: 4 } },
                    // Curly's Nemesis
                    BulletData { damage: 4, life: 4, lifetime: 20, flags: Flag(32), enemy_hit_width: 4, enemy_hit_height: 4, block_hit_width: 3, block_hit_height: 3, display_bounds: Rect { left: 8, top: 8, right: 24, bottom: 8 } },
                    // EnemyClear?
                    BulletData { damage: 0, life: 4, lifetime: 4, flags: Flag(4), enemy_hit_width: 0, enemy_hit_height: 0, block_hit_width: 0, block_hit_height: 0, display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 } },
                    // Whimsical Star
                    BulletData { damage: 1, life: 1, lifetime: 1, flags: Flag(36), enemy_hit_width: 1, enemy_hit_height: 1, block_hit_width: 1, block_hit_height: 1, display_bounds: Rect { left: 1, top: 1, right: 1, bottom: 1 } },
                ],
                bullet_rects: BulletRects {
                    b004_polar_star_l1: [
                        Rect { left: 128, top: 32, right: 144, bottom: 48 }, // horizontal
                        Rect { left: 144, top: 32, right: 160, bottom: 48 }, // vertical
                    ],
                    b005_polar_star_l2: [
                        Rect { left: 160, top: 32, right: 176, bottom: 48 }, // horizontal
                        Rect { left: 176, top: 32, right: 192, bottom: 48 }, // vertical
                    ],
                    b006_polar_star_l3: [
                        Rect { left: 128, top: 48, right: 144, bottom: 64 }, // horizontal
                        Rect { left: 144, top: 48, right: 160, bottom: 64 }, // vertical
                    ],
                    b037_spur_l1: [
                        Rect { left: 128, top: 32, right: 144, bottom: 48 }, // horizontal
                        Rect { left: 144, top: 32, right: 160, bottom: 48 }, // vertical
                    ],
                    b038_spur_l2: [
                        Rect { left: 160, top: 32, right: 176, bottom: 48 }, // horizontal
                        Rect { left: 176, top: 32, right: 192, bottom: 48 }, // vertical
                    ],
                    b039_spur_l3: [
                        Rect { left: 128, top: 48, right: 144, bottom: 64 }, // horizontal
                        Rect { left: 144, top: 48, right: 160, bottom: 64 }, // vertical
                    ],
                    b040_spur_trail_l1: [
                        Rect { left: 192, top: 32, right: 200, bottom: 40 }, // horizontal
                        Rect { left: 200, top: 32, right: 208, bottom: 40 },
                        Rect { left: 208, top: 32, right: 216, bottom: 40 },
                        Rect { left: 192, top: 40, right: 200, bottom: 48 }, // vertical
                        Rect { left: 200, top: 40, right: 208, bottom: 48 },
                        Rect { left: 208, top: 40, right: 216, bottom: 48 },
                    ],
                    b041_spur_trail_l2: [
                        Rect { left: 216, top: 32, right: 224, bottom: 40 }, // horizontal
                        Rect { left: 224, top: 32, right: 232, bottom: 40 },
                        Rect { left: 232, top: 32, right: 240, bottom: 40 },
                        Rect { left: 216, top: 40, right: 224, bottom: 48 }, // vertical
                        Rect { left: 224, top: 40, right: 232, bottom: 48 },
                        Rect { left: 232, top: 40, right: 240, bottom: 48 },
                    ],
                    b042_spur_trail_l3: [
                        Rect { left: 240, top: 32, right: 248, bottom: 40 }, // horizontal
                        Rect { left: 248, top: 32, right: 256, bottom: 40 },
                        Rect { left: 256, top: 32, right: 264, bottom: 40 },
                        Rect { left: 240, top: 32, right: 248, bottom: 40 }, // vertical
                        Rect { left: 248, top: 32, right: 256, bottom: 40 },
                        Rect { left: 256, top: 32, right: 264, bottom: 40 },
                    ],
                },
                level_table: [
                    [0, 0, 100],
                    [30, 40, 16],
                    [10, 20, 10],
                    [10, 20, 20],
                    [30, 40, 10],
                    [10, 20, 10],
                    [10, 20, 30],
                    [10, 20, 5],
                    [10, 20, 100],
                    [30, 60, 0],
                    [30, 60, 10],
                    [10, 20, 100],
                    [1, 1, 1],
                    [40, 60, 200],
                ],
            },
            tex_sizes: case_insensitive_hashmap! {
                "ArmsImage" => (256, 16),
                "Arms" => (320, 200),
                "bk0" => (64, 64),
                "bkBlack" => (64, 64),
                "bkBlue" => (64, 64),
                "bkFall" => (64, 64),
                "bkFog" => (320, 240),
                "bkFog480fix" => (480, 272), // nxengine
                "bkGard" => (48, 64),
                "bkGray" => (64, 64),
                "bkGreen" => (64, 64),
                "bkHellish" => (320, 240), // nxengine
                "bkHellish480fix" => (480, 272), // nxengine
                "bkLight" => (320, 240), // nxengine
                "bkLight480fix" => (480, 272), // nxengine
                "bkMaze" => (64, 64),
                "bkMoon" => (320, 240),
                "bkMoon480fix" => (480, 272), // nxengine
                "bkRed" => (32, 32),
                "bkSunset" => (320, 240), // nxengine
                "bkSunset480fix" => (480, 272), // nxengine
                "bkWater" => (32, 48),
                "Bullet" => (320, 176),
                "Caret" => (320, 240),
                "casts" => (320, 240),
                "Face" => (288, 240),
                "Face_0" => (288, 240), // nxengine
                "Face_1" => (288, 240), // nxengine
                "Face_2" => (288, 240), // nxengine
                "Fade" => (256, 32),
                "ItemImage" => (256, 128),
                "Loading" => (64, 8),
                "MyChar" => (200, 64),
                "Npc/Npc0" => (32, 32),
                "Npc/NpcAlmo1" => (320, 240),
                "Npc/NpcAlmo2" => (320, 240),
                "Npc/NpcBallos" => (320, 240),
                "Npc/NpcBllg" => (320, 96),
                "Npc/NpcCemet" => (320, 112),
                "Npc/NpcCent" => (320, 192),
                "Npc/NpcCurly" => (256, 80),
                "Npc/NpcDark" => (160, 64),
                "Npc/NpcDr" => (320, 240),
                "Npc/NpcEggs1" => (320, 112),
                "Npc/NpcEggs2" => (320, 128),
                "Npc/NpcFrog" => (320, 240),
                "Npc/NpcGuest" => (320, 184),
                "Npc/NpcHell" => (320, 160),
                "Npc/NpcHeri" => (320, 128),
                "Npc/NpcIronH" => (320, 72),
                "Npc/NpcIsland" => (320, 80),
                "Npc/NpcKings" => (96, 48),
                "Npc/NpcMaze" => (320, 192),
                "Npc/NpcMiza" => (320, 240),
                "Npc/NpcMoon" => (320, 128),
                "Npc/NpcOmg" => (320, 120),
                "Npc/NpcPlant" => (320, 48),
                "Npc/NpcPress" => (320, 240),
                "Npc/NpcPriest" => (320, 240),
                "Npc/NpcRavil" => (320, 168),
                "Npc/NpcRed" => (320, 144),
                "Npc/NpcRegu" => (320, 240),
                "Npc/NpcSand" => (320, 176),
                "Npc/NpcStream" => (64, 32),
                "Npc/NpcSym" => (320, 240),
                "Npc/NpcToro" => (320, 144),
                "Npc/NpcTwinD" => (320, 144),
                "Npc/NpcWeed" => (320, 240),
                "Npc/NpcX" => (320, 240),
                "Resource/BITMAP/Credit01" => (160, 240), // cse2
                "Resource/BITMAP/Credit02" => (160, 240), // cse2
                "Resource/BITMAP/Credit03" => (160, 240), // cse2
                "Resource/BITMAP/Credit04" => (160, 240), // cse2
                "Resource/BITMAP/Credit05" => (160, 240), // cse2
                "Resource/BITMAP/Credit06" => (160, 240), // cse2
                "Resource/BITMAP/Credit07" => (160, 240), // cse2
                "Resource/BITMAP/Credit08" => (160, 240), // cse2
                "Resource/BITMAP/Credit09" => (160, 240), // cse2
                "Resource/BITMAP/Credit10" => (160, 240), // cse2
                "Resource/BITMAP/Credit11" => (160, 240), // cse2
                "Resource/BITMAP/Credit12" => (160, 240), // cse2
                "Resource/BITMAP/Credit14" => (160, 240), // cse2
                "Resource/BITMAP/Credit15" => (160, 240), // cse2
                "Resource/BITMAP/Credit16" => (160, 240), // cse2
                "Resource/BITMAP/Credit17" => (160, 240), // cse2
                "Resource/BITMAP/Credit18" => (160, 240), // cse2
                "Resource/BITMAP/pixel" => (160, 16), // cse2
                "Resource/CURSOR/CURSOR_IKA" => (32, 32), // cse2
                "Resource/CURSOR/CURSOR_NORMAL" => (32, 32), // cse2
                "StageImage" => (256, 16),
                "Stage/Prt0" => (32, 32),
                "Stage/PrtAlmond" => (256, 96),
                "Stage/PrtBarr" => (256, 88),
                "Stage/PrtCave" => (256, 80),
                "Stage/PrtCent" => (256, 128),
                "Stage/PrtEggIn" => (256, 80),
                "Stage/PrtEggs" => (256, 240),
                "Stage/PrtEggX" => (256, 240),
                "Stage/PrtFall" => (256, 128),
                "Stage/PrtGard" => (256, 97),
                "Stage/PrtHell" => (256, 240),
                "Stage/PrtJail" => (256, 128),
                "Stage/PrtLabo" => (128, 64),
                "Stage/PrtMaze" => (256, 160),
                "Stage/PrtMimi" => (256, 160),
                "Stage/PrtOside" => (256, 64),
                "Stage/PrtPens" => (256, 64),
                "Stage/PrtRiver" => (256, 96),
                "Stage/PrtSand" => (256, 112),
                "Stage/PrtStore" => (256, 112),
                "Stage/PrtWeed" => (256, 128),
                "Stage/PrtWhite" => (256, 240),
                "TextBox" => (244, 144),
                "Title" => (320, 48),
            },
            textscript: TextScriptConsts {
                encoding: TextScriptEncoding::UTF8,
                textbox_rect_top: Rect { left: 0, top: 0, right: 244, bottom: 8 },
                textbox_rect_middle: Rect { left: 0, top: 8, right: 244, bottom: 16 },
                textbox_rect_bottom: Rect { left: 0, top: 16, right: 244, bottom: 24 },
                textbox_rect_yes_no: Rect { left: 152, top: 48, right: 244, bottom: 80 },
                textbox_rect_cursor: Rect { left: 112, top: 88, right: 128, bottom: 104 },
                get_item_top_left: Rect { left: 0, top: 0, right: 72, bottom: 16 },
                get_item_bottom_left: Rect { left: 0, top: 8, right: 72, bottom: 24 },
                get_item_top_right: Rect { left: 240, top: 0, right: 244, bottom: 8 },
                get_item_right: Rect { left: 240, top: 8, right: 244, bottom: 16 },
                get_item_bottom_right: Rect { left: 240, top: 16, right: 244, bottom: 24 },
            },
            font_path: str!("builtin/builtin_font.fnt"),
            font_scale: 1.0,
            font_space_offset: -3.0,
            organya_paths: vec![
                str!("/org/"), // NXEngine
                str!("/base/Org/"), // CS+
                str!("/Resource/ORG/"), // CSE2E
            ],
        }
    }

    pub fn apply_csplus_patches(&mut self) {
        info!("Applying Cave Story+ constants patches...");

        self.is_cs_plus = true;
        self.tex_sizes.insert(str!("Caret"), (320, 320));
        self.tex_sizes.insert(str!("MyChar"), (200, 384));
        self.tex_sizes.insert(str!("Npc/NpcRegu"), (320, 410));
        self.font_path = str!("csfont.fnt");
        self.font_scale = 0.5;
        self.font_space_offset = 2.0;
    }


    pub fn apply_csplus_nx_patches(&mut self) {
        info!("Applying Switch-specific Cave Story+ constants patches...");
    }
}