doukutsu-rs/src/game/npc/boss/undead_core.rs

1096 lines
40 KiB
Rust

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;
}
_ => (),
}
}
}