From 36fd5f8879975208ab094ffc36ec3d9ed8a27207 Mon Sep 17 00:00:00 2001 From: Alula <6276139+alula@users.noreply.github.com> Date: Mon, 15 Mar 2021 22:11:40 +0100 Subject: [PATCH] bugfixes for certain NPCs, add Curly --- src/npc/ai/curly.rs | 274 +++++++++++++++++++++++++++++++++++ src/npc/ai/last_cave.rs | 6 +- src/npc/ai/mimiga_village.rs | 46 ++---- src/npc/ai/mod.rs | 1 + src/npc/mod.rs | 4 +- 5 files changed, 294 insertions(+), 37 deletions(-) create mode 100644 src/npc/ai/curly.rs diff --git a/src/npc/ai/curly.rs b/src/npc/ai/curly.rs new file mode 100644 index 0000000..ad464ae --- /dev/null +++ b/src/npc/ai/curly.rs @@ -0,0 +1,274 @@ +use num_traits::{abs, clamp}; + +use crate::common::Direction; +use crate::framework::error::GameResult; +use crate::npc::list::NPCList; +use crate::npc::NPC; +use crate::player::Player; +use crate::rng::RNG; +use crate::shared_game_state::SharedGameState; +use crate::weapon::bullet::BulletManager; + +impl NPC { + pub(crate) fn tick_n117_curly( + &mut self, + state: &mut SharedGameState, + players: [&mut Player; 2], + npc_list: &NPCList, + ) -> GameResult { + match self.action_num { + 0 | 1 => { + if self.action_num == 0 { + let player = self.get_closest_player_mut(players); + + if self.direction == Direction::FacingPlayer { + self.direction = if self.x <= player.x { Direction::Right } else { Direction::Left }; + } + + self.action_num = 1; + self.anim_num = 0; + self.anim_counter = 0; + } + + self.vel_x = 0; + self.vel_y += 0x40; + } + 3 | 4 => { + if self.action_num == 3 { + self.action_num = 4; + self.anim_num = 1; + self.anim_counter = 0; + } + + self.animate(4, 1, 4); + + self.vel_x = self.direction.vector_x() * 0x200; + self.vel_y += 0x40; + } + 5 => { + self.action_num = 6; + self.anim_num = 5; + npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right, 8, state, &self.rng); + } + 6 => { + self.anim_num = 5; + } + 10 | 11 => { + let player = self.get_closest_player_mut(players); + + if self.action_num == 10 { + self.action_num = 11; + self.anim_num = 1; + self.anim_counter = 0; + self.direction = if self.x <= player.x { Direction::Right } else { Direction::Left }; + } + + self.animate(4, 1, 4); + self.x += self.direction.vector_x() * 0x200; + + if abs(self.x - player.x) > 0x2800 { + self.action_num = 0; + } + } + 20 => { + self.vel_x = 0; + self.anim_num = 6; + } + 21 => { + self.vel_x = 0; + self.anim_num = 9; + } + 30 | 31 => { + if self.action_num == 30 { + self.action_num = 31; + self.action_counter = 0; + self.vel_y = -0x400; + } + + self.anim_num = 7; + self.vel_x = self.direction.vector_x() * 0x200; + self.vel_y += 0x40; + + self.action_counter += 1; + if self.action_counter > 0 && self.flags.hit_bottom_wall() { + self.action_num = 32; + } + } + 32 => { + self.vel_x = 0; + self.vel_y += 0x40; + self.anim_num = 8; + } + 70 | 71 => { + if self.action_num == 70 { + self.action_num = 71; + self.action_counter = 0; + self.anim_num = 1; + self.anim_counter = 0; + } + + self.animate(8, 1, 4); + self.x += self.direction.vector_x() * 0x100; + } + _ => {} + } + + if self.vel_y > 0x5ff { + self.vel_y = 0x5ff; + } + + self.x += self.vel_x; + self.y += self.vel_y; + + let dir_offset = if self.direction == Direction::Left { 0 } else { 10 }; + + self.anim_rect = state.constants.npc.n117_curly[self.anim_num as usize + dir_offset]; + + Ok(()) + } + + pub(crate) fn tick_n118_curly_boss( + &mut self, + state: &mut SharedGameState, + players: [&mut Player; 2], + npc_list: &NPCList, + bullet_manager: &BulletManager, + ) -> GameResult { + match self.action_num { + 0 => { + self.action_num = 1; + self.anim_num = 0; + self.anim_counter = 0; + } + 10 | 11 => { + if self.action_num == 10 { + self.action_num = 11; + self.action_counter = self.rng.range(50..100) as u16; + self.anim_num = 0; + + let player = self.get_closest_player_mut(players); + self.direction = if self.x <= player.x { Direction::Right } else { Direction::Left }; + self.npc_flags.set_shootable(true); + self.npc_flags.set_invulnerable(false); + } + + if self.action_counter > 0 { + self.action_counter -= 1; + } else { + self.action_num = 13; + } + } + 13 | 14 => { + if self.action_num == 13 { + self.action_num = 14; + self.action_counter = self.rng.range(50..100) as u16; + self.anim_num = 3; + + let player = self.get_closest_player_mut(players); + self.direction = if self.x <= player.x { Direction::Right } else { Direction::Left }; + } + + self.animate(2, 3, 6); + + self.vel_x += self.direction.vector_x() * 0x40; + + if self.action_counter > 0 { + self.action_counter -= 1; + } else { + self.npc_flags.set_shootable(true); + self.action_num = 20; + self.action_counter = 0; + state.sound_manager.play_sfx(103); + } + } + 20 => { + let player = self.get_closest_player_mut(players); + + self.direction = if self.x <= player.x { Direction::Right } else { Direction::Left }; + + self.vel_x = 8 * self.vel_x / 9; + + self.anim_num += 1; + if self.anim_num > 1 { + self.anim_num = 0; + } + + self.action_counter += 1; + if self.action_counter > 50 { + self.action_num = 21; + self.action_counter = 0; + } + } + 21 => { + self.action_counter += 1; + if self.action_counter % 4 == 1 { + let player = self.get_closest_player_mut(players); + + let facing_up = (self.direction == Direction::Left && self.x < player.x) + || (self.direction == Direction::Right && self.x > player.x); + + let mut npc = NPC::create(123, &state.npc_table); + npc.cond.set_alive(true); + + if facing_up { + self.anim_num = 2; + npc.x = self.x; + npc.y = self.y - 0x1000; + npc.direction = Direction::Up; + } else { + self.anim_num = 0; + self.x += self.direction.opposite().vector_x() * 0x200; + + npc.x = self.x + self.direction.vector_x() * 0x1000; + npc.y = self.y + 0x800; + npc.direction = self.direction; + } + + let _ = npc_list.spawn(256, npc); + } + + if self.action_counter > 30 { + self.action_num = 10; + } + } + 30 => { + self.anim_num += 1; + if self.anim_num > 8 { + self.anim_num = 7; + } + + self.action_counter += 1; + if self.action_counter > 30 { + self.action_num = 10; + self.anim_num = 0; + } + } + _ => {} + } + + if self.action_num > 10 && self.action_num < 30 && bullet_manager.count_bullets_type_idx_all(6) > 0 { + self.action_num = 30; + self.action_counter = 0; + self.anim_num = 7; + + self.vel_x = 0; + self.npc_flags.set_shootable(false); + self.npc_flags.set_invulnerable(true); + } + + self.vel_x = clamp(self.vel_x, -0x1ff, 0x1ff); + self.vel_y += 0x20; + if self.vel_y > 0x5ff { + self.vel_y = 0x5ff; + } + + self.x += self.vel_x; + self.y += self.vel_y; + + let dir_offset = if self.direction == Direction::Left { 0 } else { 9 }; + + self.anim_rect = state.constants.npc.n118_curly_boss[self.anim_num as usize + dir_offset]; + + Ok(()) + } +} diff --git a/src/npc/ai/last_cave.rs b/src/npc/ai/last_cave.rs index 743b7ff..5b5312d 100644 --- a/src/npc/ai/last_cave.rs +++ b/src/npc/ai/last_cave.rs @@ -100,11 +100,7 @@ impl NPC { self.x += self.vel_x; self.y += self.vel_y; - let dir_offset = if self.direction == Direction::Left { - 0 - } else { - 3 - }; + let dir_offset = if self.direction == Direction::Left { 0 } else { 3 }; self.anim_rect = state.constants.npc.n241_critter_red[self.anim_num as usize + dir_offset]; diff --git a/src/npc/ai/mimiga_village.rs b/src/npc/ai/mimiga_village.rs index 1f604d9..5e8955f 100644 --- a/src/npc/ai/mimiga_village.rs +++ b/src/npc/ai/mimiga_village.rs @@ -362,8 +362,6 @@ impl NPC { } pub(crate) fn tick_n081_giant_pignon(&mut self, state: &mut SharedGameState, players: [&mut Player; 2]) -> GameResult { - let dir_offset = if self.direction == Direction::Left { 0 } else { 6 }; - match self.action_num { 0 | 1 => { if self.action_num == 0 { @@ -371,25 +369,22 @@ impl NPC { self.anim_num = 0; self.anim_counter = 0; self.vel_x = 0; - - self.anim_rect = state.constants.npc.n081_giant_pignon[self.anim_num as usize + dir_offset]; } if self.rng.range(0..100) == 1 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; + } else { + if self.rng.range(0..150) == 1 { + self.direction = self.direction.opposite(); + } - self.anim_rect = state.constants.npc.n081_giant_pignon[self.anim_num as usize + dir_offset]; - } - - - if self.rng.range(0..150) == 1 { - self.action_num = 3; - self.action_counter = 50; - self.anim_num = 0; - - self.anim_rect = state.constants.npc.n081_giant_pignon[self.anim_num as usize + dir_offset]; + if self.rng.range(0..150) == 1 { + self.action_num = 3; + self.action_counter = 50; + self.anim_num = 0; + } } } 2 => { @@ -397,8 +392,6 @@ impl NPC { if self.action_counter > 8 { self.action_num = 1; self.anim_num = 0; - - self.anim_rect = state.constants.npc.n081_giant_pignon[self.anim_num as usize + dir_offset]; } } 3 | 4 => { @@ -406,8 +399,6 @@ impl NPC { self.action_num = 4; self.anim_num = 2; self.anim_counter = 0; - - self.anim_rect = state.constants.npc.n081_giant_pignon[self.anim_num as usize + dir_offset]; } if self.action_counter > 0 { @@ -416,16 +407,7 @@ impl NPC { self.action_num = 0; } - self.anim_counter += 1; - if self.anim_counter > 2 { - self.anim_counter = 0; - self.anim_num += 1; - if self.anim_num > 4 { - self.anim_num = 2; - } - - self.anim_rect = state.constants.npc.n081_giant_pignon[self.anim_num as usize + dir_offset]; - } + self.animate(2, 2, 4); if self.flags.hit_left_wall() { self.direction = Direction::Right; @@ -435,7 +417,7 @@ impl NPC { self.direction = Direction::Left; } - self.vel_x = self.direction.vector_x() * 0x100; // 0.5fix9 + self.vel_x = self.direction.vector_x() * 0x100; } 5 => { if self.flags.hit_bottom_wall() { @@ -451,8 +433,6 @@ impl NPC { self.vel_y = -0x200; self.anim_num = 5; self.action_num = 5; - - self.anim_rect = state.constants.npc.n081_giant_pignon[self.anim_num as usize + dir_offset]; } self.vel_y += 0x40; @@ -464,6 +444,10 @@ impl NPC { self.x += self.vel_x; self.y += self.vel_y; + let dir_offset = if self.direction == Direction::Left { 0 } else { 6 }; + + self.anim_rect = state.constants.npc.n081_giant_pignon[self.anim_num as usize + dir_offset]; + Ok(()) } diff --git a/src/npc/ai/mod.rs b/src/npc/ai/mod.rs index b4e51fd..91cac6d 100644 --- a/src/npc/ai/mod.rs +++ b/src/npc/ai/mod.rs @@ -2,6 +2,7 @@ pub mod balrog; pub mod booster; pub mod chaco; pub mod characters; +pub mod curly; pub mod egg_corridor; pub mod first_cave; pub mod grasstown; diff --git a/src/npc/mod.rs b/src/npc/mod.rs index 8c3d6d9..03c864f 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -140,7 +140,7 @@ impl NPC { } impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager)> for NPC { - fn tick(&mut self, state: &mut SharedGameState, (players, npc_list, stage, _bullet_manager): ([&mut Player; 2], &NPCList, &mut Stage, &BulletManager)) -> GameResult { + fn tick(&mut self, state: &mut SharedGameState, (players, npc_list, stage, bullet_manager): ([&mut Player; 2], &NPCList, &mut Stage, &BulletManager)) -> GameResult { match self.npc_type { 0 => self.tick_n000_null(), 1 => self.tick_n001_experience(state), @@ -256,6 +256,8 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager)> for NP 113 => self.tick_n113_professor_booster(state), 114 => self.tick_n114_press(state, players, npc_list), 116 => self.tick_n116_red_petals(state), + 117 => self.tick_n117_curly(state, players, npc_list), + 118 => self.tick_n118_curly_boss(state, players, npc_list, bullet_manager), 119 => self.tick_n119_table_chair(state), 120 => self.tick_n120_colon_a(state), 124 => self.tick_n124_sunstone(state),