843 lines
33 KiB
Rust
843 lines
33 KiB
Rust
use num_traits::{abs, clamp};
|
|
|
|
use crate::caret::CaretType;
|
|
use crate::common::{Direction, Rect, CDEG_RAD};
|
|
use crate::components::flash::Flash;
|
|
use crate::framework::error::GameResult;
|
|
use crate::npc::boss::BossNPC;
|
|
use crate::npc::list::NPCList;
|
|
use crate::npc::NPC;
|
|
use crate::player::Player;
|
|
use crate::rng::RNG;
|
|
use crate::shared_game_state::SharedGameState;
|
|
|
|
impl NPC {
|
|
pub(crate) fn tick_n158_fish_missile(
|
|
&mut self,
|
|
state: &mut SharedGameState,
|
|
players: [&mut Player; 2],
|
|
) -> GameResult {
|
|
match self.action_num {
|
|
0 | 1 => {
|
|
if self.action_num == 0 {
|
|
self.action_num = 1;
|
|
|
|
self.action_counter2 = match self.direction {
|
|
Direction::Left => 0xa0,
|
|
Direction::Up => 0xe0,
|
|
Direction::Right => 0x20,
|
|
Direction::Bottom => 0x60,
|
|
Direction::FacingPlayer => 0,
|
|
};
|
|
}
|
|
|
|
let radians = self.action_counter2 as f64 * CDEG_RAD;
|
|
self.vel_x = 2 * (radians.cos() * 512.0) as i32;
|
|
self.vel_y = 2 * (radians.sin() * 512.0) as i32;
|
|
self.x += self.vel_x;
|
|
self.y += self.vel_y;
|
|
|
|
let player = self.get_closest_player_mut(players);
|
|
let direction = f64::atan2(-(self.y - player.y) as f64, -(self.x - player.x) as f64);
|
|
|
|
if direction < radians {
|
|
if radians - direction < std::f64::consts::PI {
|
|
self.action_counter2 = self.action_counter2.wrapping_sub(1) & 0xff;
|
|
} else {
|
|
self.action_counter2 = (self.action_counter2 + 1) & 0xff;
|
|
}
|
|
} else {
|
|
if direction - radians < std::f64::consts::PI {
|
|
self.action_counter2 = (self.action_counter2 + 1) & 0xff;
|
|
} else {
|
|
self.action_counter2 = self.action_counter2.wrapping_sub(1) & 0xff;
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
self.anim_counter += 1;
|
|
if self.anim_counter > 2 {
|
|
self.anim_counter = 0;
|
|
state.create_caret(self.x, self.y, CaretType::Exhaust, Direction::FacingPlayer);
|
|
}
|
|
|
|
self.anim_num = (self.action_counter2 + 0x10) / 0x20;
|
|
|
|
if self.anim_num > 7 {
|
|
self.anim_num = 7;
|
|
}
|
|
|
|
self.anim_rect = state.constants.npc.n158_fish_missile[self.anim_num as usize];
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl BossNPC {
|
|
pub(crate) fn tick_b03_monster_x(
|
|
&mut self,
|
|
state: &mut SharedGameState,
|
|
players: [&mut Player; 2],
|
|
npc_list: &NPCList,
|
|
flash: &mut Flash,
|
|
) {
|
|
match self.parts[0].action_num {
|
|
0 => {
|
|
self.parts[0].life = 1;
|
|
self.parts[0].x = -320 * 0x200;
|
|
}
|
|
1 => {
|
|
self.parts[0].life = 700;
|
|
self.parts[0].exp = 1;
|
|
self.parts[0].action_num = 2;
|
|
self.parts[0].anim_num = 0;
|
|
self.parts[0].x = 2048 * 0x200;
|
|
self.parts[0].y = 200 * 0x200;
|
|
self.parts[0].size = 3;
|
|
self.parts[0].event_num = 1000;
|
|
self.parts[0].hit_bounds =
|
|
Rect { left: 24 * 0x200, top: 24 * 0x200, right: 24 * 0x200, bottom: 24 * 0x200 };
|
|
self.parts[0].npc_flags.set_ignore_solidity(true);
|
|
self.parts[0].npc_flags.set_event_when_killed(true);
|
|
self.parts[0].npc_flags.set_show_damage(true);
|
|
self.hurt_sound[0] = 54;
|
|
|
|
self.parts[1].cond.set_alive(true);
|
|
self.parts[1].size = 3;
|
|
self.parts[1].direction = Direction::Left;
|
|
self.parts[1].display_bounds =
|
|
Rect { left: 24 * 0x200, top: 24 * 0x200, right: 24 * 0x200, bottom: 24 * 0x200 };
|
|
self.parts[1].npc_flags.set_ignore_solidity(true);
|
|
|
|
self.parts[2] = self.parts[1].clone();
|
|
self.parts[2].direction = Direction::Right;
|
|
|
|
self.parts[3].cond.set_alive(true);
|
|
self.parts[3].life = 60;
|
|
self.parts[3].size = 2;
|
|
self.parts[3].target_x = 0;
|
|
self.parts[3].display_bounds =
|
|
Rect { left: 8 * 0x200, top: 8 * 0x200, right: 8 * 0x200, bottom: 8 * 0x200 };
|
|
self.parts[3].hit_bounds =
|
|
Rect { left: 5 * 0x200, top: 5 * 0x200, right: 5 * 0x200, bottom: 5 * 0x200 };
|
|
self.parts[3].npc_flags.set_ignore_solidity(true);
|
|
self.hurt_sound[3] = 54;
|
|
self.death_sound[3] = 71;
|
|
|
|
self.parts[4] = self.parts[3].clone();
|
|
self.parts[3].target_x = 1;
|
|
|
|
self.parts[5] = self.parts[3].clone();
|
|
self.parts[6] = self.parts[3].clone();
|
|
self.parts[5].target_x = 2;
|
|
self.parts[6].target_x = 3;
|
|
self.parts[5].life = 100;
|
|
self.parts[6].life = 100;
|
|
|
|
self.parts[7].cond.set_alive(true);
|
|
self.parts[7].x = self.parts[0].x;
|
|
self.parts[7].y = self.parts[0].y;
|
|
self.parts[7].size = 3;
|
|
self.parts[7].anim_num = 0;
|
|
self.parts[7].display_bounds =
|
|
Rect { left: 52 * 0x200, top: 24 * 0x200, right: 52 * 0x200, bottom: 24 * 0x200 };
|
|
self.parts[7].hit_bounds =
|
|
Rect { left: 8 * 0x200, top: 24 * 0x200, right: 8 * 0x200, bottom: 16 * 0x200 };
|
|
self.parts[7].npc_flags.set_ignore_solidity(true);
|
|
|
|
self.parts[9].cond.set_alive(true);
|
|
self.parts[9].x = self.parts[0].x - 64 * 0x200;
|
|
self.parts[9].y = self.parts[0].y - 56 * 0x200;
|
|
self.parts[9].size = 3;
|
|
self.parts[9].action_num = 0;
|
|
self.parts[9].direction = Direction::Up;
|
|
self.parts[9].display_bounds =
|
|
Rect { left: 36 * 0x200, top: 8 * 0x200, right: 36 * 0x200, bottom: 24 * 0x200 };
|
|
self.parts[9].hit_bounds =
|
|
Rect { left: 28 * 0x200, top: 8 * 0x200, right: 28 * 0x200, bottom: 16 * 0x200 };
|
|
self.hurt_sound[9] = 52;
|
|
self.parts[9].npc_flags.set_rear_and_top_not_hurt(true);
|
|
self.parts[9].npc_flags.set_ignore_solidity(true);
|
|
self.parts[9].npc_flags.set_invulnerable(true);
|
|
self.parts[9].npc_flags.set_solid_soft(true);
|
|
|
|
self.parts[10] = self.parts[9].clone();
|
|
self.parts[10].x = self.parts[0].x + 64 * 0x200;
|
|
|
|
self.parts[11] = self.parts[9].clone();
|
|
self.parts[11].x = self.parts[0].x - 64 * 0x200;
|
|
self.parts[11].y = self.parts[0].y + 56 * 0x200;
|
|
self.parts[11].direction = Direction::Bottom;
|
|
self.parts[11].display_bounds.top = 24 * 0x200;
|
|
self.parts[11].display_bounds.bottom = 8 * 0x200;
|
|
self.parts[11].hit_bounds.top = 16 * 0x200;
|
|
self.parts[11].hit_bounds.bottom = 8 * 0x200;
|
|
|
|
self.parts[12] = self.parts[11].clone();
|
|
self.parts[12].x = self.parts[0].x + 64 * 0x200;
|
|
|
|
self.parts[13] = self.parts[9].clone();
|
|
self.parts[13].display_bounds =
|
|
Rect { left: 30 * 0x200, top: 16 * 0x200, right: 42 * 0x200, bottom: 16 * 0x200 };
|
|
self.parts[13].action_counter2 = 9;
|
|
self.parts[13].anim_num = 0;
|
|
self.parts[13].npc_flags.0 = 0;
|
|
self.parts[13].npc_flags.set_ignore_solidity(true);
|
|
|
|
self.parts[14] = self.parts[13].clone();
|
|
self.parts[14].action_counter2 = 10;
|
|
self.parts[14].anim_num = 1;
|
|
self.parts[14].display_bounds.left = 42 * 0x200;
|
|
self.parts[14].display_bounds.right = 30 * 0x200;
|
|
|
|
self.parts[15] = self.parts[13].clone();
|
|
self.parts[15].action_counter2 = 11;
|
|
self.parts[15].anim_num = 2;
|
|
self.parts[15].display_bounds.top = 16 * 0x200;
|
|
self.parts[15].display_bounds.bottom = 16 * 0x200;
|
|
|
|
self.parts[16] = self.parts[15].clone();
|
|
self.parts[16].action_counter2 = 12;
|
|
self.parts[16].anim_num = 3;
|
|
self.parts[16].display_bounds.left = 42 * 0x200;
|
|
self.parts[16].display_bounds.right = 30 * 0x200;
|
|
|
|
for npc in self.parts.iter_mut() {
|
|
npc.init_rng();
|
|
}
|
|
}
|
|
10 | 11 => {
|
|
if self.parts[0].action_num == 10 {
|
|
self.parts[0].action_num = 11;
|
|
self.parts[0].action_counter = 0;
|
|
self.parts[0].action_counter2 = 0;
|
|
}
|
|
|
|
self.parts[0].action_counter += 1;
|
|
if self.parts[0].action_counter > 100 {
|
|
self.parts[0].action_counter = 0;
|
|
|
|
let player_idx = self.parts[0].get_closest_player_idx_mut(&players);
|
|
self.parts[0].action_num = if self.parts[0].x > players[player_idx].x { 100 } else { 200 };
|
|
}
|
|
}
|
|
100 | 101 => {
|
|
if self.parts[0].action_num == 100 {
|
|
self.parts[0].action_num = 101;
|
|
self.parts[0].action_counter = 0;
|
|
self.parts[0].action_counter2 += 1;
|
|
}
|
|
|
|
self.parts[0].action_counter += 1;
|
|
|
|
match self.parts[0].action_counter {
|
|
4 => self.parts[9].action_num = 100,
|
|
8 => self.parts[10].action_num = 100,
|
|
10 => self.parts[11].action_num = 100,
|
|
12 => self.parts[12].action_num = 100,
|
|
_ => {}
|
|
}
|
|
|
|
if self.parts[0].action_counter > 120 && self.parts[0].action_counter2 > 2 {
|
|
self.parts[0].action_num = 300;
|
|
}
|
|
|
|
let player_idx = self.parts[0].get_closest_player_idx_mut(&players);
|
|
if self.parts[0].action_counter > 121 && players[player_idx].x > self.parts[0].x {
|
|
self.parts[0].action_num = 200;
|
|
}
|
|
}
|
|
200 | 201 => {
|
|
if self.parts[0].action_num == 200 {
|
|
self.parts[0].action_num = 201;
|
|
self.parts[0].action_counter = 0;
|
|
self.parts[0].action_counter2 += 1;
|
|
}
|
|
|
|
self.parts[0].action_counter += 1;
|
|
|
|
match self.parts[0].action_counter {
|
|
4 => self.parts[9].action_num = 200,
|
|
8 => self.parts[10].action_num = 200,
|
|
10 => self.parts[11].action_num = 200,
|
|
12 => self.parts[12].action_num = 200,
|
|
_ => {}
|
|
}
|
|
|
|
if self.parts[0].action_counter > 120 && self.parts[0].action_counter2 > 2 {
|
|
self.parts[0].action_num = 400;
|
|
}
|
|
|
|
let player_idx = self.parts[0].get_closest_player_idx_mut(&players);
|
|
if self.parts[0].action_counter > 121 && players[player_idx].x < self.parts[0].x {
|
|
self.parts[0].action_num = 100;
|
|
}
|
|
}
|
|
300 | 301 => {
|
|
if self.parts[0].action_num == 300 {
|
|
self.parts[0].action_num = 301;
|
|
self.parts[0].action_counter = 0;
|
|
}
|
|
|
|
self.parts[0].action_counter += 1;
|
|
|
|
match self.parts[0].action_counter {
|
|
4 => self.parts[9].action_num = 300,
|
|
8 => self.parts[10].action_num = 300,
|
|
10 => self.parts[11].action_num = 300,
|
|
12 => self.parts[12].action_num = 300,
|
|
_ => {}
|
|
}
|
|
|
|
if self.parts[0].action_counter > 50 {
|
|
if !self.parts[3].cond.alive()
|
|
&& !self.parts[4].cond.alive()
|
|
&& !self.parts[5].cond.alive()
|
|
&& !self.parts[6].cond.alive()
|
|
{
|
|
self.parts[0].action_num = 600;
|
|
} else {
|
|
self.parts[0].action_num = 500;
|
|
}
|
|
}
|
|
}
|
|
400 | 401 => {
|
|
if self.parts[0].action_num == 400 {
|
|
self.parts[0].action_num = 401;
|
|
self.parts[0].action_counter = 0;
|
|
}
|
|
|
|
self.parts[0].action_counter += 1;
|
|
|
|
match self.parts[0].action_counter {
|
|
4 => self.parts[9].action_num = 400,
|
|
8 => self.parts[10].action_num = 400,
|
|
10 => self.parts[11].action_num = 400,
|
|
12 => self.parts[12].action_num = 400,
|
|
_ => {}
|
|
}
|
|
|
|
if self.parts[0].action_counter > 50 {
|
|
if !self.parts[3].cond.alive()
|
|
&& !self.parts[4].cond.alive()
|
|
&& !self.parts[5].cond.alive()
|
|
&& !self.parts[6].cond.alive()
|
|
{
|
|
self.parts[0].action_num = 600;
|
|
} else {
|
|
self.parts[0].action_num = 500;
|
|
}
|
|
}
|
|
}
|
|
500 | 501 => {
|
|
if self.parts[0].action_num == 500 {
|
|
self.parts[0].action_num = 501;
|
|
self.parts[0].action_counter = 0;
|
|
self.parts[1].action_num = 10;
|
|
self.parts[2].action_num = 10;
|
|
}
|
|
|
|
self.parts[0].action_counter += 1;
|
|
|
|
if self.parts[0].action_counter > 300 {
|
|
self.parts[0].action_num = 502;
|
|
self.parts[0].action_counter = 0;
|
|
}
|
|
|
|
if !self.parts[3].cond.alive()
|
|
&& !self.parts[4].cond.alive()
|
|
&& !self.parts[5].cond.alive()
|
|
&& !self.parts[6].cond.alive()
|
|
{
|
|
self.parts[0].action_num = 502;
|
|
self.parts[0].action_counter = 0;
|
|
}
|
|
}
|
|
502 | 503 => {
|
|
if self.parts[0].action_num == 502 {
|
|
self.parts[0].action_num = 503;
|
|
self.parts[0].action_counter = 0;
|
|
self.parts[0].action_counter2 = 0;
|
|
self.parts[1].action_num = 20;
|
|
self.parts[2].action_num = 20;
|
|
}
|
|
|
|
self.parts[0].action_counter += 1;
|
|
|
|
if self.parts[0].action_counter > 50 {
|
|
let player_idx = self.parts[0].get_closest_player_idx_mut(&players);
|
|
self.parts[0].action_num = if self.parts[0].x > players[player_idx].x { 100 } else { 200 };
|
|
}
|
|
}
|
|
600 | 601 => {
|
|
if self.parts[0].action_num == 600 {
|
|
self.parts[0].action_num = 601;
|
|
self.parts[0].action_counter = 0;
|
|
self.parts[0].vel_y2 = self.parts[0].life as i32;
|
|
self.parts[1].action_num = 30;
|
|
self.parts[2].action_num = 30;
|
|
}
|
|
|
|
self.parts[0].action_counter += 1;
|
|
if (self.parts[0].life as i32) < self.parts[0].vel_y2.saturating_sub(200)
|
|
|| self.parts[0].action_counter > 300
|
|
{
|
|
self.parts[0].action_num = 602;
|
|
self.parts[0].action_counter = 0;
|
|
}
|
|
}
|
|
602 | 603 => {
|
|
if self.parts[0].action_num == 602 {
|
|
self.parts[0].action_num = 603;
|
|
self.parts[0].action_counter = 0;
|
|
self.parts[0].action_counter2 = 0;
|
|
self.parts[1].action_num = 40;
|
|
self.parts[2].action_num = 40;
|
|
}
|
|
|
|
self.parts[0].action_counter += 1;
|
|
|
|
if self.parts[0].action_counter > 50 {
|
|
let player_idx = self.parts[0].get_closest_player_idx_mut(&players);
|
|
self.parts[0].action_num = if self.parts[0].x > players[player_idx].x { 100 } else { 200 };
|
|
}
|
|
}
|
|
1000 => {
|
|
state.quake_counter = 2;
|
|
|
|
self.parts[0].action_counter += 1;
|
|
if self.parts[0].action_counter % 8 == 0 {
|
|
state.sound_manager.play_sfx(52);
|
|
}
|
|
|
|
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(-72..72) as i32 * 0x200;
|
|
npc.y = self.parts[0].y + self.parts[0].rng.range(-64..64) as i32 * 0x200;
|
|
let _ = npc_list.spawn(0x100, npc);
|
|
|
|
if self.parts[0].action_counter > 100 {
|
|
self.parts[0].action_num = 1001;
|
|
self.parts[0].action_counter = 0;
|
|
flash.set_cross(self.parts[0].x, self.parts[0].y);
|
|
state.sound_manager.play_sfx(35);
|
|
}
|
|
}
|
|
1001 => {
|
|
state.quake_counter = 40;
|
|
self.parts[0].action_counter += 1;
|
|
|
|
if self.parts[0].action_counter > 50 {
|
|
for part in self.parts.iter_mut() {
|
|
part.cond.set_alive(false);
|
|
}
|
|
|
|
for npc in npc_list.iter_alive() {
|
|
if npc.npc_type == 158 {
|
|
npc.cond.set_alive(false);
|
|
}
|
|
}
|
|
|
|
let mut npc = NPC::create(159, &state.npc_table);
|
|
npc.cond.set_alive(true);
|
|
npc.x = self.parts[0].x;
|
|
npc.y = self.parts[1].y - 24 * 0x200;
|
|
|
|
let _ = npc_list.spawn(0, npc);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
self.tick_b03_monster_x_track(9, state, &players);
|
|
self.tick_b03_monster_x_track(10, state, &players);
|
|
self.tick_b03_monster_x_track(11, state, &players);
|
|
self.tick_b03_monster_x_track(12, state, &players);
|
|
|
|
self.parts[0].x +=
|
|
(((self.parts[9].x + self.parts[10].x + self.parts[11].x + self.parts[12].x) / 4) - self.parts[0].x) / 16;
|
|
self.tick_b03_monster_x_face(7, state);
|
|
|
|
self.tick_b03_monster_x_frame(13, state, npc_list);
|
|
self.tick_b03_monster_x_frame(14, state, npc_list);
|
|
self.tick_b03_monster_x_frame(15, state, npc_list);
|
|
self.tick_b03_monster_x_frame(16, state, npc_list);
|
|
|
|
self.tick_b03_monster_x_shield(1, state);
|
|
self.tick_b03_monster_x_shield(2, state);
|
|
|
|
if self.parts[3].cond.alive() {
|
|
self.tick_b03_monster_x_eye(3, state, &players, npc_list);
|
|
}
|
|
if self.parts[4].cond.alive() {
|
|
self.tick_b03_monster_x_eye(4, state, &players, npc_list);
|
|
}
|
|
if self.parts[5].cond.alive() {
|
|
self.tick_b03_monster_x_eye(5, state, &players, npc_list);
|
|
}
|
|
if self.parts[6].cond.alive() {
|
|
self.tick_b03_monster_x_eye(6, state, &players, npc_list);
|
|
}
|
|
|
|
if self.parts[0].life == 0 && self.parts[0].action_num < 1000 {
|
|
self.parts[0].action_num = 1000;
|
|
self.parts[0].action_counter = 0;
|
|
self.parts[0].shock = 150;
|
|
|
|
self.parts[9].action_num = 300;
|
|
self.parts[10].action_num = 300;
|
|
self.parts[11].action_num = 300;
|
|
self.parts[12].action_num = 300;
|
|
}
|
|
}
|
|
|
|
fn tick_b03_monster_x_face(&mut self, i: usize, state: &mut SharedGameState) {
|
|
match self.parts[i].action_num {
|
|
0 => {
|
|
self.parts[0].npc_flags.set_shootable(false);
|
|
self.parts[i].anim_num = 2;
|
|
}
|
|
10 | 11 => {
|
|
if self.parts[i].action_num == 10 {
|
|
self.parts[i].action_num = 11;
|
|
self.parts[i].action_counter = (self.parts[i].target_x * 10 + 40) as u16;
|
|
self.parts[i].anim_num = 2;
|
|
self.parts[0].npc_flags.set_shootable(true);
|
|
}
|
|
|
|
if self.parts[0].shock > 0 {
|
|
self.parts[i].action_counter2 += 1;
|
|
if self.parts[i].action_counter2 / 2 % 2 != 0 {
|
|
self.parts[i].anim_num = 1;
|
|
} else {
|
|
self.parts[i].anim_num = 0;
|
|
}
|
|
} else {
|
|
self.parts[i].anim_num = 0;
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
self.parts[7].x = self.parts[0].x;
|
|
self.parts[7].y = self.parts[0].y;
|
|
|
|
self.parts[i].anim_rect = state.constants.npc.b03_monster_x[self.parts[i].anim_num as usize];
|
|
}
|
|
|
|
fn tick_b03_monster_x_track(&mut self, i: usize, state: &mut SharedGameState, players: &[&mut Player; 2]) {
|
|
match self.parts[i].action_num {
|
|
10 => {
|
|
self.parts[i].anim_num = 0;
|
|
self.parts[i].npc_flags.set_bouncy(false);
|
|
}
|
|
100 | 101 => {
|
|
if self.parts[i].action_num == 100 {
|
|
self.parts[i].action_num = 101;
|
|
self.parts[i].action_counter = 0;
|
|
self.parts[i].anim_num = 2;
|
|
self.parts[i].anim_counter = 0;
|
|
self.parts[i].npc_flags.set_bouncy(true);
|
|
}
|
|
|
|
self.parts[i].action_counter += 1;
|
|
if self.parts[i].action_counter > 30 {
|
|
self.parts[i].action_num = 102;
|
|
}
|
|
|
|
self.parts[i].animate(0, 2, 3);
|
|
self.parts[i].vel_x -= 0x20;
|
|
}
|
|
102 | 103 => {
|
|
if self.parts[i].action_num == 102 {
|
|
self.parts[i].action_num = 103;
|
|
self.parts[i].anim_num = 0;
|
|
self.parts[i].anim_counter = 0;
|
|
self.parts[i].npc_flags.set_bouncy(false);
|
|
}
|
|
|
|
self.parts[i].action_counter += 1;
|
|
self.parts[i].animate(1, 0, 1);
|
|
self.parts[i].vel_x -= 0x20;
|
|
}
|
|
200 | 201 => {
|
|
if self.parts[i].action_num == 200 {
|
|
self.parts[i].action_num = 201;
|
|
self.parts[i].action_counter = 0;
|
|
self.parts[i].anim_num = 4;
|
|
self.parts[i].anim_counter = 0;
|
|
self.parts[i].npc_flags.set_bouncy(true);
|
|
self.parts[i].npc_flags.set_rear_and_top_not_hurt(true);
|
|
}
|
|
|
|
self.parts[i].action_counter += 1;
|
|
if self.parts[i].action_counter > 30 {
|
|
self.parts[i].action_num = 202;
|
|
}
|
|
|
|
self.parts[i].animate(0, 4, 5);
|
|
self.parts[i].vel_x += 0x20;
|
|
}
|
|
202 | 203 => {
|
|
if self.parts[i].action_num == 202 {
|
|
self.parts[i].action_num = 203;
|
|
self.parts[i].anim_num = 0;
|
|
self.parts[i].anim_counter = 0;
|
|
self.parts[i].npc_flags.set_bouncy(false);
|
|
}
|
|
|
|
self.parts[i].action_counter += 1;
|
|
|
|
self.parts[i].animate(1, 0, 1);
|
|
self.parts[i].vel_x += 0x20;
|
|
}
|
|
300 | 301 => {
|
|
if self.parts[i].action_num == 300 {
|
|
self.parts[i].action_num = 301;
|
|
self.parts[i].anim_num = 4;
|
|
self.parts[i].anim_counter = 0;
|
|
self.parts[i].npc_flags.set_bouncy(true);
|
|
}
|
|
|
|
self.parts[i].animate(0, 4, 5);
|
|
|
|
self.parts[i].vel_x += 0x20;
|
|
if self.parts[i].vel_x > 0 {
|
|
self.parts[i].vel_x = 0;
|
|
self.parts[i].action_num = 10;
|
|
}
|
|
}
|
|
400 | 401 => {
|
|
if self.parts[i].action_num == 400 {
|
|
self.parts[i].action_num = 401;
|
|
self.parts[i].anim_num = 2;
|
|
self.parts[i].anim_counter = 0;
|
|
self.parts[i].npc_flags.set_bouncy(true);
|
|
}
|
|
|
|
self.parts[i].animate(0, 2, 3);
|
|
|
|
self.parts[i].vel_x -= 0x20;
|
|
if self.parts[i].vel_x < 0 {
|
|
self.parts[i].vel_x = 0;
|
|
self.parts[i].action_num = 10;
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
if self.parts[i].action_counter % 2 == 1 && [101, 201, 301, 401].contains(&self.parts[i].action_num) {
|
|
state.sound_manager.play_sfx(112);
|
|
}
|
|
|
|
if self.parts[i].action_counter % 4 == 1 && [103, 203].contains(&self.parts[i].action_num) {
|
|
state.sound_manager.play_sfx(111);
|
|
}
|
|
|
|
let player_idx = self.parts[i].get_closest_player_idx_mut(players);
|
|
if self.parts[i].action_num >= 100 && abs(players[player_idx].y - self.parts[i].y) < 4 * 0x200 {
|
|
self.parts[i].damage = 10;
|
|
self.parts[i].npc_flags.set_rear_and_top_not_hurt(true);
|
|
} else {
|
|
self.parts[i].damage = 0;
|
|
self.parts[i].npc_flags.set_rear_and_top_not_hurt(false);
|
|
}
|
|
|
|
self.parts[i].vel_x = clamp(self.parts[i].vel_x, -0x400, 0x400);
|
|
self.parts[i].x += self.parts[i].vel_x;
|
|
|
|
let dir_offset = if self.parts[i].direction == Direction::Up { 3 } else { 9 };
|
|
|
|
self.parts[i].anim_rect = state.constants.npc.b03_monster_x[self.parts[i].anim_num as usize + dir_offset];
|
|
}
|
|
|
|
fn tick_b03_monster_x_frame(&mut self, i: usize, state: &mut SharedGameState, npc_list: &NPCList) {
|
|
match self.parts[i].action_num {
|
|
10 | 11 => {
|
|
if self.parts[i].action_num == 10 {
|
|
self.parts[i].action_num = 11;
|
|
self.parts[i].action_counter = (self.parts[i].anim_num * 30 + 30) as u16;
|
|
}
|
|
|
|
if self.parts[i].action_counter > 0 {
|
|
self.parts[i].action_counter -= 1;
|
|
} else {
|
|
self.parts[i].action_counter = 120;
|
|
state.sound_manager.play_sfx(39);
|
|
|
|
let mut npc = NPC::create(158, &state.npc_table);
|
|
npc.cond.set_alive(true);
|
|
|
|
match self.parts[i].anim_num {
|
|
0 => {
|
|
npc.direction = Direction::Bottom;
|
|
npc.x = self.parts[i].x + -30 * 0x200;
|
|
npc.y = self.parts[i].y + 6 * 0x200;
|
|
}
|
|
1 => {
|
|
npc.direction = Direction::Right;
|
|
npc.x = self.parts[i].x + 30 * 0x200;
|
|
npc.y = self.parts[i].y + 6 * 0x200;
|
|
}
|
|
2 => {
|
|
npc.direction = Direction::Left;
|
|
npc.x = self.parts[i].x - 30 * 0x200;
|
|
npc.y = self.parts[i].y - 6 * 0x200;
|
|
}
|
|
3 => {
|
|
npc.direction = Direction::Up;
|
|
npc.x = self.parts[i].x + 30 * 0x200;
|
|
npc.y = self.parts[i].y - 6 * 0x200;
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
let _ = npc_list.spawn(0x100, npc);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
self.parts[i].x = (self.parts[0].x + self.parts[self.parts[i].action_counter2 as usize].x) / 2;
|
|
self.parts[i].y = (self.parts[0].y + self.parts[self.parts[i].action_counter2 as usize].y) / 2;
|
|
|
|
self.parts[i].anim_rect = state.constants.npc.b03_monster_x[15 + self.parts[i].anim_num as usize];
|
|
}
|
|
|
|
fn tick_b03_monster_x_shield(&mut self, i: usize, state: &mut SharedGameState) {
|
|
match self.parts[i].action_num {
|
|
10 => {
|
|
self.parts[i].target_x += 0x200;
|
|
|
|
if self.parts[i].target_x > 32 * 0x200 {
|
|
self.parts[i].target_x = 32 * 0x200;
|
|
self.parts[i].action_num = 0;
|
|
self.parts[3].action_num = 10;
|
|
self.parts[4].action_num = 10;
|
|
self.parts[5].action_num = 10;
|
|
self.parts[6].action_num = 10;
|
|
}
|
|
}
|
|
20 => {
|
|
self.parts[i].target_x -= 0x200;
|
|
|
|
if self.parts[i].target_x < 0 {
|
|
self.parts[i].target_x = 0;
|
|
self.parts[i].action_num = 0;
|
|
self.parts[3].action_num = 0;
|
|
self.parts[4].action_num = 0;
|
|
self.parts[5].action_num = 0;
|
|
self.parts[6].action_num = 0;
|
|
}
|
|
}
|
|
30 => {
|
|
self.parts[i].target_x += 0x200;
|
|
|
|
if self.parts[i].target_x > 20 * 0x200 {
|
|
self.parts[i].target_x = 20 * 0x200;
|
|
self.parts[i].action_num = 0;
|
|
self.parts[7].action_num = 10;
|
|
self.parts[13].action_num = 10;
|
|
self.parts[14].action_num = 10;
|
|
self.parts[15].action_num = 10;
|
|
self.parts[16].action_num = 10;
|
|
}
|
|
}
|
|
40 => {
|
|
self.parts[i].target_x -= 0x200;
|
|
|
|
if self.parts[i].target_x < 0 {
|
|
self.parts[i].target_x = 0;
|
|
self.parts[i].action_num = 0;
|
|
self.parts[7].action_num = 0;
|
|
self.parts[13].action_num = 0;
|
|
self.parts[14].action_num = 0;
|
|
self.parts[15].action_num = 0;
|
|
self.parts[16].action_num = 0;
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
self.parts[i].x = self.parts[0].x + self.parts[i].direction.vector_x() * (24 * 0x200 + self.parts[i].target_x);
|
|
self.parts[i].y = self.parts[0].y;
|
|
|
|
let dir_offset = if self.parts[i].direction == Direction::Left { 19 } else { 20 };
|
|
|
|
self.parts[i].anim_rect = state.constants.npc.b03_monster_x[dir_offset];
|
|
}
|
|
|
|
fn tick_b03_monster_x_eye(
|
|
&mut self,
|
|
i: usize,
|
|
state: &mut SharedGameState,
|
|
players: &[&mut Player; 2],
|
|
npc_list: &NPCList,
|
|
) {
|
|
match self.parts[i].action_num {
|
|
0 => {
|
|
self.parts[i].npc_flags.set_shootable(false);
|
|
self.parts[i].anim_num = 0;
|
|
}
|
|
10 | 11 => {
|
|
if self.parts[i].action_num == 10 {
|
|
self.parts[i].action_num = 11;
|
|
self.parts[i].action_counter = (self.parts[i].target_x * 10 + 40) as u16;
|
|
self.parts[i].npc_flags.set_shootable(true);
|
|
}
|
|
|
|
self.parts[i].anim_num =
|
|
if self.parts[i].action_counter < 16 && self.parts[i].action_counter / 2 % 2 != 0 { 1 } else { 0 };
|
|
|
|
if self.parts[i].action_counter > 0 {
|
|
self.parts[i].action_counter -= 1;
|
|
} else {
|
|
let player_idx = self.parts[i].get_closest_player_idx_mut(players);
|
|
let px = self.parts[i].x - players[player_idx].x;
|
|
let py = self.parts[i].y - players[player_idx].y;
|
|
|
|
let deg = f64::atan2(py as f64, px as f64) + self.parts[i].rng.range(-2..2) as f64 * CDEG_RAD;
|
|
|
|
let mut npc = NPC::create(156, &state.npc_table);
|
|
npc.cond.set_alive(true);
|
|
npc.x = self.parts[i].x;
|
|
npc.y = self.parts[i].y;
|
|
npc.vel_x = (deg.cos() * -1536.0) as i32;
|
|
npc.vel_y = (deg.sin() * -1536.0) as i32;
|
|
|
|
let _ = npc_list.spawn(0x100, npc);
|
|
|
|
state.sound_manager.play_sfx(39);
|
|
self.parts[i].action_counter = 40;
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
match self.parts[i].target_x {
|
|
0 => {
|
|
self.parts[i].x = self.parts[0].x - 22 * 0x200;
|
|
self.parts[i].y = self.parts[0].y - 16 * 0x200;
|
|
}
|
|
1 => {
|
|
self.parts[i].x = self.parts[0].x + 28 * 0x200;
|
|
self.parts[i].y = self.parts[0].y - 16 * 0x200;
|
|
}
|
|
2 => {
|
|
self.parts[i].x = self.parts[0].x - 15 * 0x200;
|
|
self.parts[i].y = self.parts[0].y + 14 * 0x200;
|
|
}
|
|
3 => {
|
|
self.parts[i].x = self.parts[0].x + 17 * 0x200;
|
|
self.parts[i].y = self.parts[0].y + 14 * 0x200;
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
self.parts[i].anim_rect = state.constants.npc.b03_monster_x
|
|
[21 + self.parts[i].target_x as usize + 4 * self.parts[i].anim_num as usize];
|
|
}
|
|
}
|