use std::hint::unreachable_unchecked;

use crate::common::{CDEG_RAD, Direction, Rect, SliceExt};
use crate::components::flash::Flash;
use crate::framework::error::GameResult;
use crate::game::npc::boss::BossNPC;
use crate::game::npc::list::NPCList;
use crate::game::npc::NPC;
use crate::game::player::Player;
use crate::game::shared_game_state::SharedGameState;
use crate::game::stage::Stage;
use crate::util::rng::RNG;

impl NPC {
    pub(crate) fn tick_n282_mini_undead_core_active(
        &mut self,
        state: &mut SharedGameState,
        players: [&mut Player; 2],
    ) -> GameResult {
        if self.action_num == 0 {
            self.action_num = 20;
            self.target_y = self.y;
            self.vel_y = if self.rng.range(0..100) & 1 != 0 { -0x100 } else { 0x100 };
        }

        if self.action_num == 20 {
            self.vel_x = -0x200;
            if self.x < -0x8000 {
                self.cond.set_alive(false);
            }

            if self.target_y < self.y {
                self.vel_y -= 0x10;
            }
            if self.target_y > self.y {
                self.vel_y += 0x10;
            }

            self.vel_y = self.vel_y.clamp(-0x100, 0x100);

            let player = self.get_closest_player_ref(&players);
            if player.flags.hit_bottom_wall()
                && player.y < self.y - 0x800
                && player.x > self.x - 0x3000
                && player.x < self.x + 0x3000
            {
                self.target_y = 0x12000;
                self.anim_num = 2;
            } else if self.anim_num != 1 {
                self.anim_num = 0;
            }

            if player.flags.hit_left_wall()
                && player.x < self.x - self.hit_bounds.right as i32
                && player.x > self.x - self.hit_bounds.right as i32 - 0x1000
                && player.hit_bounds.bottom as i32 + player.y > self.y - self.hit_bounds.top as i32
                && (player.y - player.hit_bounds.top as i32) < self.hit_bounds.bottom as i32 + self.y
            {
                self.npc_flags.set_solid_hard(false);
                self.anim_num = 1;
            } else if player.flags.hit_right_wall()
                && player.x > self.hit_bounds.right as i32 + self.x
                && player.x < self.x + self.hit_bounds.right as i32 + 0x1000
                && player.hit_bounds.bottom as i32 + player.y > self.y - self.hit_bounds.top as i32
                && (player.y - player.hit_bounds.top as i32) < self.hit_bounds.bottom as i32 + self.y
            {
                self.npc_flags.set_solid_hard(false);
                self.anim_num = 1;
            } else if player.flags.hit_top_wall()
                && player.y < self.y - self.hit_bounds.top as i32
                && player.y > self.y - self.hit_bounds.top as i32 - 0x1000
                && player.hit_bounds.left as i32 + player.x > self.x - self.hit_bounds.right as i32
                && (player.x - player.hit_bounds.right as i32) < self.hit_bounds.left as i32 + self.x
            {
                self.npc_flags.set_solid_hard(false);
                self.anim_num = 1;
            } else if player.flags.hit_bottom_wall()
                && player.y > self.hit_bounds.bottom as i32 + self.y - 0x800
                && player.y < self.y + self.hit_bounds.bottom as i32 + 0x1800
                && player.hit_bounds.left as i32 + player.x > self.x - self.hit_bounds.right as i32 - 0x800
                && (player.x - player.hit_bounds.right as i32) < self.x + self.hit_bounds.left as i32 + 0x800
            {
                self.npc_flags.set_solid_hard(false);
                self.anim_num = 1;
            }
        }

        self.x += self.vel_x;
        self.y += self.vel_y;

        self.anim_rect = state.constants.npc.n282_mini_undead_core_active[self.anim_num as usize];

        Ok(())
    }

    pub(crate) fn tick_n291_mini_undead_core_inactive(&mut self, state: &mut SharedGameState) -> GameResult {
        if self.action_num == 0 {
            self.action_num = 20;
            if self.direction == Direction::Right {
                self.npc_flags.set_solid_hard(false);
                self.anim_num = 1;
            }
        }

        self.anim_rect = state.constants.npc.n291_mini_undead_core_inactive[self.anim_num as usize];

        Ok(())
    }

    pub(crate) fn tick_n293_undead_core_energy_shot(
        &mut self,
        state: &mut SharedGameState,
        npc_list: &NPCList,
    ) -> GameResult {
        if self.action_num == 0 {
            self.action_num = 1;
        }

        if self.action_num == 1 {
            self.anim_num += 1;
            if self.anim_num > 1 {
                self.anim_num = 0;
            }

            let mut npc = NPC::create(4, &state.npc_table);
            npc.cond.set_alive(true);
            npc.x = self.x + self.rng.range(0..16) * 0x200;
            npc.y = self.y + self.rng.range(-16..16) * 0x200;

            let _ = npc_list.spawn(0x100, npc);

            self.x -= 0x1000;
            if self.x < -0x4000 {
                self.cond.set_alive(false);
            }
        }

        self.anim_rect = state.constants.npc.n293_undead_core_energy_shot[self.anim_num as usize];

        Ok(())
    }

    pub(crate) fn tick_n285_undead_core_spiral_projectile(
        &mut self,
        state: &mut SharedGameState,
        npc_list: &NPCList,
        stage: &mut Stage,
    ) -> GameResult {
        if self.x < 0 || self.x > stage.map.width as i32 * state.tile_size.as_int() * 0x200 {
            self.vanish(state);
            return Ok(());
        }

        if self.action_num == 0 {
            self.action_num = 1;
            self.target_x = self.x;
            self.target_y = self.y;
            self.action_counter2 = self.tsc_direction / 8;
            self.tsc_direction &= 7;
        }

        if self.action_num == 1 {
            self.action_counter2 += 24;
            self.action_counter2 &= 0xFF;

            if self.action_counter < 128 {
                self.action_counter += 1;
            }

            self.vel_x += self.direction.vector_x() * 0x15;
            self.target_x += self.vel_x;

            self.x = self.target_x + 4 * ((self.action_counter2 as f64).cos() * 512.0) as i32;
            self.y = self.target_y + 6 * ((self.action_counter2 as f64).sin() * 512.0) as i32;

            let mut npc = NPC::create(286, &state.npc_table);
            npc.cond.set_alive(true);
            npc.x = self.x;
            npc.y = self.y;

            let _ = npc_list.spawn(0x100, npc);
        }

        self.anim_rect = state.constants.npc.n285_undead_core_spiral_projectile;

        Ok(())
    }

    pub(crate) fn tick_n286_undead_core_spiral_projectile_trail(&mut self, state: &mut SharedGameState) -> GameResult {
        self.anim_counter += 1;
        if self.anim_counter > 0 {
            self.anim_counter = 0;
            self.anim_num += 1;
        }

        if self.anim_num < 3 {
            self.anim_rect = state.constants.npc.n286_undead_core_spiral_projectile_trail[self.anim_num as usize];
        } else {
            self.cond.set_alive(false);
        }

        Ok(())
    }

    pub(crate) fn tick_n287_orange_smoke(&mut self, state: &mut SharedGameState) -> GameResult {
        if self.action_num == 0 {
            self.vel_x = self.rng.range(-4..4) * 0x200;
            self.action_num = 1;
        } else if self.action_num == 1 {
            self.vel_x = 20 * self.vel_x / 21;
            self.vel_y = 20 * self.vel_y / 21;
            self.x += self.vel_x;
            self.y += self.vel_y;
        }

        self.anim_counter += 1;
        if self.anim_counter > 1 {
            self.anim_counter = 0;
            self.anim_num += 1;
        }

        if self.anim_num < 7 {
            self.anim_rect = state.constants.npc.n287_orange_smoke[self.anim_num as usize];
        } else {
            self.cond.set_alive(false);
        }

        Ok(())
    }

    pub(crate) fn tick_n288_undead_core_exploding_rock(
        &mut self,
        state: &mut SharedGameState,
        players: [&mut Player; 2],
        npc_list: &NPCList,
        stage: &mut Stage,
    ) -> GameResult {
        match self.action_num {
            0 | 1 => {
                if self.action_num == 0 {
                    self.action_num = 1;
                    self.vel_x = -0x200;
                }

                match self.direction {
                    Direction::Up => {
                        self.vel_y -= 0x20;
                        if self.vel_y < -0x5FF {
                            self.vel_y = -0x5FF;
                        }

                        if self.flags.hit_top_wall() {
                            self.action_num = 2;
                        }
                    }
                    Direction::Bottom => {
                        self.vel_y += 0x20;
                        self.clamp_fall_speed();

                        if self.flags.hit_bottom_wall() {
                            self.action_num = 2;
                        }
                    }
                    _ => (),
                }

                self.animate(3, 0, 1);
            }
            2 | 3 => {
                if self.action_num == 2 {
                    let player = self.get_closest_player_ref(&players);

                    self.action_num = 3;
                    self.action_counter = 0;
                    self.npc_flags.set_ignore_solidity(true);
                    self.vel_x = if self.x > player.x { -0x400 } else { 0x400 };
                    self.vel_y = 0;

                    self.display_bounds = Rect::new(0x1800, 0x1800, 0x1800, 0x1800);
                    state.sound_manager.play_sfx(44);
                }

                self.anim_num += 1;
                if self.anim_num > 4 {
                    self.anim_num = 2;
                }

                self.action_counter += 1;
                if self.action_counter & 3 == 1 {
                    let mut npc = NPC::create(287, &state.npc_table);
                    npc.cond.set_alive(true);

                    npc.x = self.x;
                    npc.y = self.y;
                    npc.vel_y = self.direction.opposite().vector_y() * 0x400;

                    let _ = npc_list.spawn(0x100, npc);
                }

                if self.x < 0x2000 || self.x > (stage.map.width as i32 - 1) * state.tile_size.as_int() * 0x200 {
                    self.cond.set_alive(false);
                }
            }
            _ => (),
        }

        self.x += self.vel_x;
        self.y += self.vel_y;

        self.anim_rect = state.constants.npc.n288_undead_core_exploding_rock[self.anim_num as usize];

        Ok(())
    }
}

impl BossNPC {
    pub(crate) fn tick_b07_undead_core(
        &mut self,
        state: &mut SharedGameState,
        npc_list: &NPCList,
        stage: &mut Stage,
        flash: &mut Flash,
    ) {
        let mut v19 = false;

        match self.parts[0].action_num {
            1 => {
                self.parts[0].action_num = 10;
                self.parts[0].exp = 1;
                self.parts[0].cond.set_alive(true);
                self.parts[0].npc_flags.set_ignore_solidity(true);
                self.parts[0].npc_flags.set_invulnerable(true);
                self.parts[0].npc_flags.set_show_damage(true);
                self.parts[0].life = 700;
                self.hurt_sound[0] = 114;
                self.parts[0].x = 0x4a000;
                self.parts[0].y = 0xf000;
                self.parts[0].vel_x = 0;
                self.parts[0].vel_y = 0;
                self.parts[0].event_num = 1000;
                self.parts[0].npc_flags.set_event_when_killed(true);

                self.parts[3].cond.set_alive(true);
                self.parts[3].action_num = 0;

                self.parts[4].cond.set_alive(true);
                self.parts[4].action_num = 10;

                self.parts[5].cond.set_alive(true);
                self.parts[5].action_num = 10;

                self.parts[8].cond.set_alive(true);
                self.parts[8].npc_flags.set_ignore_solidity(true);
                self.parts[8].display_bounds.left = 0;
                self.parts[8].display_bounds.top = 0;
                self.parts[8].hit_bounds.right = 0x5000;
                self.parts[8].hit_bounds.top = 0x2000;
                self.parts[8].hit_bounds.bottom = 0x2000;
                self.parts[8].action_counter2 = 0;

                self.parts[9] = self.parts[8].clone();
                self.parts[9].hit_bounds.right = 0x4800;
                self.parts[9].hit_bounds.top = 0x3000;
                self.parts[9].hit_bounds.bottom = 0x3000;
                self.parts[9].action_counter2 = 1;

                self.parts[10] = self.parts[8].clone();
                self.parts[10].hit_bounds.right = 0x5800;
                self.parts[10].hit_bounds.top = 0x1000;
                self.parts[10].hit_bounds.bottom = 0x1000;
                self.parts[10].action_counter2 = 2;

                self.parts[11] = self.parts[8].clone();
                self.parts[11].cond.set_damage_boss(true);
                self.parts[11].hit_bounds.right = 0x2800;
                self.parts[11].hit_bounds.top = 0x2800;
                self.parts[11].hit_bounds.bottom = 0x2800;
                self.parts[11].action_counter2 = 3;

                self.parts[1].cond.set_alive(true);
                self.parts[1].action_num = 0;
                self.parts[1].npc_flags.set_shootable(true);
                self.parts[1].npc_flags.set_ignore_solidity(true);
                self.parts[1].life = 1000;
                self.hurt_sound[1] = 54;
                self.parts[1].hit_bounds.right = 0x3000;
                self.parts[1].hit_bounds.top = 0x2000;
                self.parts[1].hit_bounds.bottom = 0x2000;
                self.parts[1].display_bounds.left = 0x4000;
                self.parts[1].display_bounds.top = 0x2800;
                self.parts[1].parent_id = 0;

                self.parts[2] = self.parts[1].clone();
                self.parts[2].action_counter3 = 128;

                self.parts[6] = self.parts[1].clone();
                self.parts[6].action_counter2 = 1;

                self.parts[7] = self.parts[1].clone();
                self.parts[7].action_counter2 = 1;
                self.parts[7].action_counter3 = 128;

                for i in [2, 6, 7] {
                    self.hurt_sound[i] = self.hurt_sound[1];
                }

                self.parts[19].action_counter = self.parts[0].life;

                for i in 0u16..20 {
                    self.parts[i as usize].id = i;
                }
            }
            15 => {
                self.parts[0].action_num = 16;
                self.parts[0].direction = Direction::Left;
                self.parts[3].action_num = 10;
                self.parts[4].anim_num = 0;
                v19 = true;
            }
            20 => {
                self.parts[0].action_num = 210;
                self.parts[0].direction = Direction::Left;
                self.parts[1].action_num = 5;
                self.parts[2].action_num = 5;
                self.parts[6].action_num = 5;
                self.parts[7].action_num = 5;
                v19 = true;
            }
            200 | 201 => {
                if self.parts[0].action_num == 200 {
                    self.parts[0].action_num = 201;
                    self.parts[0].action_counter = 0;
                    self.parts[3].action_num = 0;
                    self.parts[4].anim_num = 2;
                    self.parts[5].anim_num = 0;
                    self.parts[8].npc_flags.set_invulnerable(false);
                    self.parts[9].npc_flags.set_invulnerable(false);
                    self.parts[10].npc_flags.set_invulnerable(false);
                    self.parts[11].npc_flags.set_shootable(false);

                    state.npc_super_pos.1 = 0;
                    state.sound_manager.stop_sfx(40);
                    state.sound_manager.stop_sfx(41);
                    state.sound_manager.stop_sfx(58);

                    v19 = true;
                }

                self.parts[0].action_counter += 1;
                if (self.parts[0].direction == Direction::Right
                    || self.parts[0].anim_num > 0
                    || self.parts[0].life < 200)
                    && self.parts[0].action_counter > 200
                {
                    self.parts[0].action_counter2 += 1;
                    state.sound_manager.play_sfx(115);

                    if self.parts[0].life >= 200 {
                        self.parts[0].action_num = if self.parts[0].action_counter2 <= 2 { 210 } else { 220 };
                    } else {
                        self.parts[0].action_num = 230;
                    }
                }
            }
            210 | 211 => {
                if self.parts[0].action_num == 210 {
                    self.parts[0].action_num = 211;
                    self.parts[0].action_counter = 0;
                    self.parts[3].action_num = 10;
                    self.parts[8].npc_flags.set_invulnerable(true);
                    self.parts[9].npc_flags.set_invulnerable(true);
                    self.parts[10].npc_flags.set_invulnerable(true);
                    self.parts[11].npc_flags.set_shootable(true);
                    self.parts[19].action_counter = self.parts[0].life;
                    v19 = true;
                }

                self.parts[19].action_counter2 += 1;
                if self.parts[0].shock != 0 && (self.parts[19].action_counter2 & 2) != 0 {
                    self.parts[4].anim_num = 1;
                    self.parts[5].anim_num = 1;
                } else {
                    self.parts[4].anim_num = 0;
                    self.parts[5].anim_num = 0;
                }

                self.parts[0].action_counter += 1;
                if self.parts[0].action_counter % 100 == 1 {
                    state.npc_curly_counter = self.parts[0].rng.range(80..100) as u16;
                    state.npc_curly_target = (self.parts[11].x, self.parts[11].y);
                }

                if self.parts[0].action_counter < 300 {
                    if self.parts[0].action_counter % 120 == 1 {
                        let mut npc = NPC::create(288, &state.npc_table);
                        npc.cond.set_alive(true);
                        npc.x = self.parts[0].x - 0x4000;
                        npc.y = self.parts[0].y - 0x2000;
                        npc.direction = Direction::Up;

                        let _ = npc_list.spawn(0x20, npc);
                    }

                    if self.parts[0].action_counter % 120 == 61 {
                        let mut npc = NPC::create(288, &state.npc_table);
                        npc.cond.set_alive(true);
                        npc.x = self.parts[0].x - 0x4000;
                        npc.y = self.parts[0].y + 0x2000;
                        npc.direction = Direction::Bottom;

                        let _ = npc_list.spawn(0x20, npc);
                    }
                }

                if self.parts[0].life + 50 < self.parts[19].action_counter || self.parts[0].action_counter > 400 {
                    self.parts[0].action_num = 200;
                }
            }
            220 | 221 => {
                if self.parts[0].action_num == 220 {
                    state.npc_super_pos.1 = 1;

                    self.parts[0].action_num = 221;
                    self.parts[0].action_counter = 0;
                    self.parts[0].action_counter2 = 0;
                    self.parts[3].action_num = 20;
                    self.parts[8].npc_flags.set_invulnerable(true);
                    self.parts[9].npc_flags.set_invulnerable(true);
                    self.parts[10].npc_flags.set_invulnerable(true);
                    self.parts[11].npc_flags.set_shootable(true);
                    self.parts[19].action_counter = self.parts[0].life;
                    v19 = true;

                    state.quake_counter = 100;
                    state.quake_rumble_counter = 100;
                }

                self.parts[0].action_counter += 1;
                if self.parts[0].action_counter % 40 == 1 {
                    let (x, y) = match self.parts[0].rng.range(0..3) {
                        0 => (self.parts[1].x, self.parts[1].y),
                        1 => (self.parts[2].x, self.parts[2].y),
                        2 => (self.parts[6].x, self.parts[6].y),
                        3 => (self.parts[7].x, self.parts[7].y),
                        _ => unsafe {
                            unreachable_unchecked();
                        },
                    };

                    state.sound_manager.play_sfx(25);

                    let mut npc = NPC::create(285, &state.npc_table);
                    npc.cond.set_alive(true);
                    npc.x = x - 0x2000;
                    npc.y = y;
                    let _ = npc_list.spawn(0x100, npc.clone());

                    npc.tsc_direction = 1024;
                    let _ = npc_list.spawn(0x100, npc);
                }

                self.parts[19].action_counter2 += 1;

                if self.parts[0].shock != 0 && (self.parts[19].action_counter2 & 2) != 0 {
                    self.parts[4].anim_num = 1;
                    self.parts[5].anim_num = 1;
                } else {
                    self.parts[4].anim_num = 0;
                    self.parts[5].anim_num = 0;
                }

                if self.parts[0].life + 150 < self.parts[19].action_counter
                    || self.parts[0].action_counter > 400
                    || self.parts[0].life < 200
                {
                    self.parts[0].action_num = 200;
                }
            }
            230 | 231 => {
                if self.parts[0].action_num == 230 {
                    self.parts[0].action_num = 231;
                    self.parts[0].action_counter = 0;
                    self.parts[3].action_num = 30;
                    self.parts[8].npc_flags.set_invulnerable(true);
                    self.parts[9].npc_flags.set_invulnerable(true);
                    self.parts[10].npc_flags.set_invulnerable(true);
                    self.parts[11].npc_flags.set_shootable(true);

                    state.sound_manager.play_sfx(25);

                    let mut npc = NPC::create(285, &state.npc_table);
                    npc.cond.set_alive(true);
                    npc.x = self.parts[3].x - 0x2000;
                    npc.y = self.parts[3].y;

                    let _ = npc_list.spawn(0x100, npc.clone());

                    npc.tsc_direction = 1024;
                    let _ = npc_list.spawn(0x100, npc.clone());

                    npc.tsc_direction = 0;
                    npc.x = self.parts[3].x;
                    npc.y = self.parts[3].y - 0x2000;
                    let _ = npc_list.spawn(0x100, npc.clone());

                    npc.tsc_direction = 1024;
                    let _ = npc_list.spawn(0x100, npc.clone());

                    npc.tsc_direction = 0;
                    npc.x = self.parts[3].x;
                    npc.y = self.parts[3].y + 0x2000;
                    let _ = npc_list.spawn(0x100, npc.clone());

                    npc.tsc_direction = 1024;
                    let _ = npc_list.spawn(0x100, npc);

                    self.parts[19].action_counter = self.parts[0].life;
                    v19 = true;
                }

                self.parts[19].action_counter2 += 1;
                if self.parts[0].shock != 0 && (self.parts[19].action_counter2 & 2) != 0 {
                    self.parts[4].anim_num = 1;
                    self.parts[5].anim_num = 1;
                } else {
                    self.parts[4].anim_num = 0;
                    self.parts[5].anim_num = 0;
                }

                self.parts[0].action_counter += 1;
                if self.parts[0].action_counter % 100 == 1 {
                    state.npc_curly_counter = self.parts[0].rng.range(80..100) as u16;
                    state.npc_curly_target = (self.parts[11].x, self.parts[11].y);
                }

                if self.parts[0].action_counter % 120 == 1 {
                    let mut npc = NPC::create(288, &state.npc_table);
                    npc.cond.set_alive(true);
                    npc.x = self.parts[0].x - 0x4000;
                    npc.y = self.parts[0].y - 0x2000;
                    npc.direction = Direction::Up;

                    let _ = npc_list.spawn(0x20, npc.clone());
                }

                if self.parts[0].action_counter % 120 == 61 {
                    let mut npc = NPC::create(288, &state.npc_table);
                    npc.cond.set_alive(true);
                    npc.x = self.parts[0].x - 0x4000;
                    npc.y = self.parts[0].y + 0x2000;
                    npc.direction = Direction::Bottom;

                    let _ = npc_list.spawn(0x20, npc.clone());
                }
            }
            500 | 501 => {
                if self.parts[0].action_num == 500 {
                    state.sound_manager.stop_sfx(40);
                    state.sound_manager.stop_sfx(41);
                    state.sound_manager.stop_sfx(58);

                    self.parts[0].action_num = 501;
                    self.parts[0].action_counter = 0;
                    self.parts[0].vel_x = 0;
                    self.parts[0].vel_y = 0;
                    self.parts[3].action_num = 0;
                    self.parts[4].anim_num = 2;
                    self.parts[5].anim_num = 0;
                    self.parts[1].action_num = 5;
                    self.parts[2].action_num = 5;
                    self.parts[6].action_num = 5;
                    self.parts[7].action_num = 5;

                    state.quake_counter = 20;
                    state.quake_rumble_counter = 20;

                    let mut npc = NPC::create(4, &state.npc_table);
                    npc.cond.set_alive(true);

                    for _ in 0..100 {
                        npc.x = self.parts[0].x + self.parts[0].rng.range(-128..128) * 0x200;
                        npc.y = self.parts[0].y + self.parts[0].rng.range(-64..64) * 0x200;
                        npc.vel_x = self.parts[0].rng.range(-128..128) * 0x200;
                        npc.vel_y = self.parts[0].rng.range(-128..128) * 0x200;

                        let _ = npc_list.spawn(0, npc.clone());
                    }

                    npc_list.kill_npcs_by_type(282, true, state);

                    self.parts[11].npc_flags.set_shootable(false);

                    for i in 0..12 {
                        self.parts[i].npc_flags.set_invulnerable(false);
                    }
                }

                self.parts[0].action_counter += 1;
                if self.parts[0].action_counter % 16 != 0 {
                    let mut npc = NPC::create(4, &state.npc_table);
                    npc.cond.set_alive(true);
                    npc.x = self.parts[0].x + self.parts[0].rng.range(-64..64) * 0x200;
                    npc.y = self.parts[0].y + self.parts[0].rng.range(-32..32) * 0x200;
                    npc.vel_x = self.parts[0].rng.range(-128..128) * 0x200;
                    npc.vel_y = self.parts[0].rng.range(-128..128) * 0x200;

                    let _ = npc_list.spawn(0x100, npc);
                }

                self.parts[0].x += 0x40;
                self.parts[0].y += 0x80;

                if self.parts[0].action_counter > 200 {
                    self.parts[0].action_counter = 0;
                    self.parts[0].action_num = 1000;
                }
            }
            1000 => {
                state.quake_counter = 100;
                state.quake_rumble_counter = 100;

                self.parts[0].action_counter += 1;
                if self.parts[0].action_counter % 8 == 0 {
                    state.sound_manager.play_sfx(44);
                }

                npc_list.create_death_smoke(
                    self.parts[0].x + self.parts[0].rng.range(-72..72) * 0x200,
                    self.parts[0].y + self.parts[0].rng.range(-64..64) * 0x200,
                    1,
                    1,
                    state,
                    &self.parts[0].rng,
                );

                if self.parts[0].action_counter > 100 {
                    self.parts[0].action_counter = 0;
                    self.parts[0].action_num = 1001;

                    state.sound_manager.play_sfx(35);
                    flash.set_cross(self.parts[0].x, self.parts[0].y);
                }
            }
            1001 => {
                state.quake_counter = 40;
                state.quake_rumble_counter = 40;

                self.parts[0].action_counter += 1;
                if self.parts[0].action_counter > 50 {
                    for i in 0..20 {
                        self.parts[i].cond.set_alive(false);
                    }

                    npc_list.kill_npcs_by_type(158, true, state);
                    npc_list.kill_npcs_by_type(301, true, state);
                }
            }
            _ => (),
        }

        if v19 {
            state.quake_counter = 20;
            state.quake_rumble_counter = 20;

            state.sound_manager.play_sfx(26);

            if self.parts[0].action_num == 201 {
                self.parts[7].action_num = 10;
                self.parts[6].action_num = 10;
                self.parts[2].action_num = 10;
                self.parts[1].action_num = 10;
            }

            if self.parts[0].action_num == 221 {
                self.parts[7].action_num = 20;
                self.parts[6].action_num = 20;
                self.parts[2].action_num = 20;
                self.parts[1].action_num = 20;
            }

            if self.parts[0].action_num == 231 {
                self.parts[7].action_num = 30;
                self.parts[6].action_num = 30;
                self.parts[2].action_num = 30;
                self.parts[1].action_num = 30;
            }

            let mut npc = NPC::create(4, &state.npc_table);
            npc.cond.set_alive(true);

            for _ in 0..8 {
                npc.x = self.parts[4].x + self.parts[0].rng.range(-32..16) * 0x200;
                npc.y = self.parts[4].y;
                npc.vel_x = self.parts[0].rng.range(-0x200..0x200);
                npc.vel_y = self.parts[0].rng.range(-0x100..0x100);

                let _ = npc_list.spawn(0x100, npc.clone());
            }
        }

        if self.parts[0].action_num >= 200 && self.parts[0].action_num < 300 {
            if self.parts[0].x < 0x18000 {
                self.parts[0].direction = Direction::Right;
            }
            if self.parts[0].x > (stage.map.width as i32 - 4) * state.tile_size.as_int() * 0x200 {
                self.parts[0].direction = Direction::Left;
            }

            self.parts[0].vel_x += self.parts[0].direction.vector_x() * 4;
        }

        if [201, 211, 221, 231].contains(&self.parts[0].action_num) {
            self.parts[0].action_counter3 += 1;
            if self.parts[0].action_counter3 == 150 {
                self.parts[0].action_counter3 = 0;

                let mut npc = NPC::create(282, &state.npc_table);
                npc.cond.set_alive(true);
                npc.x = stage.map.width as i32 * state.tile_size.as_int() * 0x200 + 0x40;
                npc.y = (self.parts[0].rng.range(-1..3) + 10) * state.tile_size.as_int() * 0x200;

                let _ = npc_list.spawn(0x30, npc);
            } else if self.parts[0].action_counter3 == 75 {
                let mut npc = NPC::create(282, &state.npc_table);
                npc.cond.set_alive(true);
                npc.x = stage.map.width as i32 * state.tile_size.as_int() * 0x200 + 0x40;
                npc.y = (self.parts[0].rng.range(-3..0) + 3) * state.tile_size.as_int() * 0x200;

                let _ = npc_list.spawn(0x30, npc);
            }
        }

        self.parts[0].vel_x = self.parts[0].vel_x.clamp(-0x80, 0x80);
        self.parts[0].vel_y = self.parts[0].vel_y.clamp(-0x80, 0x80);

        self.parts[0].x += self.parts[0].vel_x;
        self.parts[0].y += self.parts[0].vel_y;

        self.tick_b07_undead_core_face(3, state, npc_list);
        self.tick_b07_undead_core_head(4, state);
        self.tick_b07_undead_core_tail(5, state);
        self.tick_b07_undead_core_small_head(1, state, stage);
        self.tick_b07_undead_core_small_head(2, state, stage);
        self.tick_b07_undead_core_small_head(6, state, stage);
        self.tick_b07_undead_core_small_head(7, state, stage);
        self.tick_b07_undead_core_hitbox(8);
        self.tick_b07_undead_core_hitbox(9);
        self.tick_b07_undead_core_hitbox(10);
        self.tick_b07_undead_core_hitbox(11);
    }

    fn tick_b07_undead_core_face(&mut self, i: usize, state: &mut SharedGameState, npc_list: &NPCList) {
        let (head, tail) = self.parts.split_at_mut(i);
        let base = &mut head[0];
        let part = &mut tail[0];

        match part.action_num {
            0 => {
                part.anim_num = 0;
            }
            10 => {
                part.anim_num = 1;
            }
            20 => {
                part.anim_num = 2;
            }
            30 | 31 => {
                if part.action_num == 30 {
                    part.action_num = 31;
                    part.anim_num = 3;
                    part.action_counter = 100;
                }

                part.action_counter += 1;
                if part.action_counter > 300 {
                    part.action_counter = 0;
                }

                if part.action_counter > 250 && part.action_counter % 16 == 1 {
                    state.sound_manager.play_sfx(26);
                }

                if part.action_counter > 250 && part.action_counter % 16 == 7 {
                    state.sound_manager.play_sfx(101);

                    let mut npc = NPC::create(293, &state.npc_table);
                    npc.cond.set_alive(true);
                    npc.x = part.x;
                    npc.y = part.y;

                    let _ = npc_list.spawn(0x80, npc);
                }

                if part.action_counter == 200 {
                    state.sound_manager.play_sfx(116);
                }

                part.anim_num = if part.action_counter > 200 && part.action_counter & 1 != 0 { 4 } else { 3 };
            }
            _ => (),
        }

        part.display_bounds.right = 0x4800;
        part.display_bounds.left = 0x4800;
        part.display_bounds.top = 0x2800;
        part.x = base.x - 0x4800;
        part.y = base.y + 0x800;
        part.npc_flags.set_ignore_solidity(true);

        part.anim_rect = state.constants.npc.b07_undead_core[part.anim_num as usize];
    }

    fn tick_b07_undead_core_head(&mut self, i: usize, state: &mut SharedGameState) {
        let (head, tail) = self.parts.split_at_mut(i);
        let base = &mut head[0];
        let part = &mut tail[0];

        match part.action_num {
            10 | 11 => {
                if part.action_num == 10 {
                    part.action_num = 11;
                    part.anim_num = 2;
                    part.npc_flags.set_ignore_solidity(true);
                    part.display_bounds.left = 0x4800;
                    part.display_bounds.top = 0x7000;
                }

                part.x = base.x - 0x4800;
                part.y = base.y;
            }
            50 | 51 => {
                if part.action_num == 50 {
                    part.action_num = 51;
                    part.action_counter = 112;
                }

                part.action_counter = part.action_counter.saturating_sub(1);
                if part.action_counter == 0 {
                    part.action_num = 100;
                    part.anim_num = 3;
                }
            }
            100 => {
                part.anim_num = 3;
            }
            _ => (),
        }

        part.anim_rect = state.constants.npc.b07_undead_core[part.anim_num as usize + 5];

        if part.action_num == 51 {
            part.anim_rect.bottom = part.action_counter + part.anim_rect.top;
        }
    }

    fn tick_b07_undead_core_tail(&mut self, i: usize, state: &mut SharedGameState) {
        let (head, tail) = self.parts.split_at_mut(i);
        let base = &mut head[0];
        let part = &mut tail[0];

        match part.action_num {
            10 | 11 => {
                if part.action_num == 10 {
                    part.action_num = 11;
                    part.anim_num = 0;
                    part.npc_flags.set_ignore_solidity(true);
                    part.display_bounds.left = 0x5800;
                    part.display_bounds.top = 0x7000;
                }

                part.x = base.x + 0x5800;
                part.y = base.y;
            }
            50 | 51 => {
                if part.action_num == 50 {
                    part.action_num = 51;
                    part.action_counter = 112;
                }

                part.action_counter = part.action_counter.saturating_sub(1);
                if part.action_counter == 0 {
                    part.action_num = 100;
                    part.anim_num = 2;
                }
            }
            100 => {
                part.anim_num = 2;
            }
            _ => (),
        }

        part.anim_rect = state.constants.npc.b07_undead_core[part.anim_num as usize + 9];

        if part.action_num == 51 {
            part.anim_rect.bottom = part.action_counter + part.anim_rect.top;
        }
    }

    fn tick_b07_undead_core_small_head(&mut self, i: usize, state: &mut SharedGameState, stage: &mut Stage) {
        let parent = self.parts[i].parent_id as usize;
        let (base, part) = if let Some(x) = self.parts.get_two_mut(parent, i) {
            x
        } else {
            return;
        };

        if !part.cond.alive() {
            return;
        }

        part.life = 1000;
        match part.action_num {
            0 => {
                part.npc_flags.set_shootable(false);
            }
            5 => {
                part.anim_num = 0;
                part.npc_flags.set_shootable(false);
                part.action_counter3 += 1;
                part.action_counter3 &= 0xff;
            }
            10 => {
                part.anim_num = 0;
                part.npc_flags.set_shootable(false);
                part.action_counter3 += 2;
                part.action_counter3 &= 0xff;
            }
            20 => {
                part.anim_num = 1;
                part.npc_flags.set_shootable(false);
                part.action_counter3 += 2;
                part.action_counter3 &= 0xff;
            }
            30 => {
                part.anim_num = 0;
                part.npc_flags.set_shootable(false);
                part.action_counter3 += 4;
                part.action_counter3 &= 0xff;
            }
            200 | 201 => {
                if part.action_num == 200 {
                    part.action_num = 201;
                    part.anim_num = 2;
                    part.vel_x = 0;
                    part.vel_y = 0;
                }

                part.vel_x += 0x20;
                part.x += part.vel_x;
                if part.x > (stage.map.width as i32 + 2) * state.tile_size.as_int() * 0x200 {
                    part.cond.set_alive(false);
                }
            }
            _ => (),
        }

        if part.action_num < 50 {
            let angle =
                if part.action_counter2 != 0 { part.action_counter3 + 0x80 } else { part.action_counter3 + 0x180 };

            let angle = (angle / 2) as f64 * CDEG_RAD;

            part.x = base.x + 0x30 * (angle.cos() * 512.0) as i32 - 0x1000;
            part.y = base.y + 0x50 * (angle.sin() * 512.0) as i32;
        }

        part.anim_rect = state.constants.npc.b07_undead_core[part.anim_num as usize + 12];
    }

    fn tick_b07_undead_core_hitbox(&mut self, i: usize) {
        let (head, tail) = self.parts.split_at_mut(i);
        let base = &mut head[0];
        let part = &mut tail[0];

        match part.action_counter2 {
            0 => {
                part.x = base.x;
                part.y = base.y - 0x4000;
            }
            1 => {
                part.x = base.x + 0x3800;
                part.y = base.y;
            }
            2 => {
                part.x = base.x + 0x800;
                part.y = base.y + 0x4000;
            }
            3 => {
                part.x = base.x - 0x3800;
                part.y = base.y + 0x800;
            }
            _ => (),
        }
    }
}
