From 5f2968fa0b4aba26a81d52256025d23b2ca507d5 Mon Sep 17 00:00:00 2001 From: Alula Date: Thu, 26 Nov 2020 09:41:28 +0100 Subject: [PATCH] full implementation of grasstown npcs --- src/engine_constants/npcs.rs | 18 +- src/npc/balrog.rs | 190 +++++++++++++- src/npc/egg_corridor.rs | 3 +- src/npc/grasstown.rs | 474 ++++++++++++++++++++++++++++++++--- src/npc/misc.rs | 100 ++++++++ src/npc/mod.rs | 13 + src/npc/santa.rs | 67 +++++ src/scene/game_scene.rs | 78 +++--- 8 files changed, 864 insertions(+), 79 deletions(-) diff --git a/src/engine_constants/npcs.rs b/src/engine_constants/npcs.rs index 7f0ea0e..f5b0681 100644 --- a/src/engine_constants/npcs.rs +++ b/src/engine_constants/npcs.rs @@ -311,8 +311,8 @@ pub struct NPCConsts { #[serde(default = "default_n102_malco_computer_wave")] pub n102_malco_computer_wave: [Rect; 4], - #[serde(default = "default_n103_manaan_projectile")] - pub n103_manaan_projectile: [Rect; 6], + #[serde(default = "default_n103_mannan_projectile")] + pub n103_mannan_projectile: [Rect; 6], #[serde(default = "default_n104_frog")] pub n104_frog: [Rect; 6], @@ -322,14 +322,14 @@ pub struct NPCConsts { // pub n106_hey_bubble_high: () // Defined in code - #[serde(default = "default_n107_malco_powered_on")] - pub n107_malco_powered_on: [Rect; 10], + #[serde(default = "default_n107_malco_broken")] + pub n107_malco_broken: [Rect; 10], #[serde(default = "default_n108_balfrog_projectile")] pub n108_balfrog_projectile: [Rect; 3], - #[serde(default = "default_n109_malco_broken")] - pub n109_malco_broken: [Rect; 4], + #[serde(default = "default_n109_malco_powered_on")] + pub n109_malco_powered_on: [Rect; 4], #[serde(default = "default_n110_puchi")] pub n110_puchi: [Rect; 6], @@ -2243,7 +2243,7 @@ fn default_n102_malco_computer_wave() -> [Rect; 4] { ] } -fn default_n103_manaan_projectile() -> [Rect; 6] { +fn default_n103_mannan_projectile() -> [Rect; 6] { [ Rect { left: 192, top: 96, right: 208, bottom: 120 }, Rect { left: 208, top: 96, right: 224, bottom: 120 }, @@ -2272,7 +2272,7 @@ fn default_n105_hey_bubble_low() -> [Rect; 2] { ] } -fn default_n107_malco_powered_on() -> [Rect; 10] { +fn default_n107_malco_broken() -> [Rect; 10] { [ Rect { left: 144, top: 0, right: 160, bottom: 24 }, Rect { left: 160, top: 0, right: 176, bottom: 24 }, @@ -2295,7 +2295,7 @@ fn default_n108_balfrog_projectile() -> [Rect; 3] { ] } -fn default_n109_malco_broken() -> [Rect; 4] { +fn default_n109_malco_powered_on() -> [Rect; 4] { [ Rect { left: 240, top: 0, right: 256, bottom: 24 }, Rect { left: 256, top: 0, right: 272, bottom: 24 }, diff --git a/src/npc/balrog.rs b/src/npc/balrog.rs index 0219b01..dbda3d1 100644 --- a/src/npc/balrog.rs +++ b/src/npc/balrog.rs @@ -5,7 +5,7 @@ use ggez::GameResult; use num_traits::clamp; use crate::caret::CaretType; -use crate::common::Direction; +use crate::common::{CDEG_RAD, Direction}; use crate::npc::{NPC, NPCMap}; use crate::player::Player; use crate::shared_game_state::SharedGameState; @@ -611,6 +611,194 @@ impl NPC { Ok(()) } + pub(crate) fn tick_n033_balrog_bouncing_projectile(&mut self, state: &mut SharedGameState) -> GameResult { + if self.flags.hit_left_wall() || self.flags.hit_right_wall() { + self.cond.set_alive(false); + state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left); + } else if self.flags.hit_bottom_wall() { + self.vel_y = -2 * 0x200; + } + + self.vel_y += 0x2a; + + self.x += self.vel_x; + self.y += self.vel_y; + + self.anim_counter += 1; + if self.anim_counter > 2 { + self.anim_counter = 0; + + self.anim_num += 1; + if self.anim_num > 1 { + self.anim_num = 0; + } + } + + self.anim_rect = state.constants.npc.n033_balrog_bouncing_projectile[self.anim_num as usize]; + + self.action_counter += 1; + if self.action_counter > 250 { + self.cond.set_alive(false); + state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left); + } + + Ok(()) + } + + pub(crate) fn tick_n036_balrog_hover(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { + match self.action_num { + 0 | 1 => { + if self.action_num == 0 { + self.action_num = 1; + } + + self.action_counter += 1; + if self.action_counter > 12 { + self.action_num = 2; + self.action_counter = 0; + self.anim_num = 1; + self.vel_x2 = 3; + } + } + 2 => { + self.action_counter += 1; + if self.action_counter > 16 { + self.vel_x2 -= 1; + self.action_counter = 0; + + let angle = f64::atan2((self.y - player.y) as f64, (self.x - player.x) as f64) + + state.game_rng.range(-16..16) as f64 * CDEG_RAD; + + let mut npc = NPCMap::create_npc(11, &state.npc_table); + npc.cond.set_alive(true); + npc.vel_x = (angle.cos() * -512.0) as isize; + npc.vel_y = (angle.sin() * -512.0) as isize; + npc.x = self.x; + npc.y = self.y + 4 * 0x200; + + state.new_npcs.push(npc); + state.sound_manager.play_sfx(39); + + if self.vel_x2 == 0 { + self.action_num = 3; + self.action_counter = 0; + } + } + } + 3 => { + self.action_counter += 1; + if self.action_counter > 3 { + self.action_num = 4; + self.action_counter = 0; + self.anim_num = 3; + + self.vel_x = (player.x - self.x) / 100; + self.vel_y = -3 * 0x200; + } + } + 4 => { + if self.vel_y > -0x200 { + if self.life > 60 { + self.action_num = 5; + self.action_counter = 0; + self.anim_num = 4; + self.anim_counter = 0; + self.target_y = self.y; + } else { + self.action_num = 6; + } + } + } + 5 => { + self.anim_counter += 1; + if self.anim_counter > 1 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 5 { + self.anim_num = 4; + state.sound_manager.play_sfx(47); + } + } + + self.action_counter += 1; + if self.action_counter > 100 { + self.action_num = 6; + self.anim_num = 3; + } + + self.vel_y += ((self.target_y - self.y).signum() | 1) * 0x40; + self.vel_y = clamp(self.vel_y, -0x200, 0x200); + } + 6 => { + if self.y + 16 * 0x200 < player.y { + self.damage = 10; + } else { + self.damage = 0; + } + + if self.flags.hit_bottom_wall() { + self.action_num = 7; + self.action_counter = 0; + self.anim_num = 2; + self.damage = 0; + + state.sound_manager.play_sfx(25); + state.sound_manager.play_sfx(26); + state.quake_counter = 30; + + let mut npc_smoke = NPCMap::create_npc(4, &state.npc_table); + let mut npc_proj = NPCMap::create_npc(33, &state.npc_table); + for _ in 0..8 { + npc_smoke.cond.set_alive(true); + npc_smoke.direction = Direction::Left; + npc_smoke.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; + npc_smoke.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; + npc_smoke.vel_x = state.game_rng.range(-0x155..0x155) as isize; + npc_smoke.vel_y = state.game_rng.range(-0x600..0) as isize; + + npc_proj.cond.set_alive(true); + npc_proj.direction = Direction::Left; + npc_proj.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; + npc_proj.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; + npc_proj.vel_x = state.game_rng.range(-0x400..0x400) as isize; + npc_proj.vel_y = state.game_rng.range(-0x400..0) as isize; + + state.new_npcs.push(npc_smoke); + state.new_npcs.push(npc_proj); + } + } + } + 7 => { + self.vel_x = 0; + + self.action_counter += 1; + if self.action_counter > 3 { + self.action_num = 1; + self.action_counter = 0; + } + } + _ => {} + } + + if self.action_num != 5 { + self.vel_y += 0x33; + self.direction = if self.x < player.x { Direction::Right } else { Direction::Left }; + } + + 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 { 6 }; + + self.anim_rect = state.constants.npc.n036_balrog_hover[self.anim_num as usize + dir_offset]; + + Ok(()) + } + pub(crate) fn tick_n068_balrog_running(&mut self, state: &mut SharedGameState, player: &mut Player) -> GameResult { match self.action_num { 0 | 1 => { diff --git a/src/npc/egg_corridor.rs b/src/npc/egg_corridor.rs index d12a35d..82cf541 100644 --- a/src/npc/egg_corridor.rs +++ b/src/npc/egg_corridor.rs @@ -607,7 +607,7 @@ impl NPC { } else { self.action_counter2 += 1; if (self.action_counter2 % 8) == 0 && abs(self.x - player.x) < 160 * 0x200 { - let angle = ((player.y - self.y) as f64 / (player.x - self.x) as f64).atan() + let angle = ((self.y - player.y) as f64 / (self.x - player.x) as f64).atan() + (state.game_rng.range(-6..6) as u8) as f64 * CDEG_RAD; let mut npc = NPCMap::create_npc(84, &state.npc_table); @@ -618,6 +618,7 @@ impl NPC { npc.vel_y = (angle.sin() * 1024.0) as isize; state.new_npcs.push(npc); + state.sound_manager.play_sfx(39); } if self.action_counter2 > 8 { diff --git a/src/npc/grasstown.rs b/src/npc/grasstown.rs index b5258f8..3d83479 100644 --- a/src/npc/grasstown.rs +++ b/src/npc/grasstown.rs @@ -1,8 +1,8 @@ +use ggez::GameResult; use num_traits::abs; use num_traits::clamp; use crate::common::Direction; -use ggez::GameResult; use crate::npc::{NPC, NPCMap}; use crate::player::Player; use crate::shared_game_state::SharedGameState; @@ -17,8 +17,8 @@ impl NPC { } if self.action_counter >= 8 - && abs(self.x - player.x) < (112 * 0x200) - && abs(self.y - player.y) < (80 * 0x200) { + && abs(self.x - player.x) < (128 * 0x200) + && self.y - 128 * 0x200 < player.y && self.y + 48 * 0x200 > player.y { if self.x > player.x { self.direction = Direction::Left; } else { @@ -74,7 +74,7 @@ impl NPC { } } 4 => { - if self.x > player.x { + if self.x >= player.x { self.direction = Direction::Left; } else { self.direction = Direction::Right; @@ -89,18 +89,18 @@ impl NPC { self.anim_num = 2; self.vel_x /= 2; self.damage = 12; - } + } else { + if self.action_counter % 4 == 1 { + state.sound_manager.play_sfx(110); + } - if self.action_counter % 4 == 1 { - state.sound_manager.play_sfx(110); - } - - self.anim_counter += 1; - if self.anim_counter > 0 { - self.anim_counter = 0; - self.anim_num += 1; - if self.anim_num > 5 { - self.anim_num = 3; + self.anim_counter += 1; + if self.anim_counter > 0 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 5 { + self.anim_num = 3; + } } } } @@ -347,8 +347,102 @@ impl NPC { Ok(()) } + pub(crate) fn tick_n031_bat_hanging(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { + match self.action_num { + 0 | 1 => { + if self.action_num == 0 { + self.action_num = 1; + } + + if state.game_rng.range(0..120) == 10 { + self.action_num = 2; + self.action_counter = 0; + self.anim_num = 1; + } + + if abs(self.x - player.x) < 8 * 0x200 + && self.y - 8 * 0x200 < player.y + && self.y + 96 * 0x200 > player.y { + self.action_num = 3; + self.anim_num = 0; + } + } + 2 => { + self.action_counter += 1; + if self.action_counter > 8 { + self.action_num = 1; + self.anim_num = 0; + } + } + 3 => { + self.anim_num = 0; + + if self.shock > 0 || abs(self.x - player.x) > 20 * 0x200 { + self.action_num = 4; + self.action_counter = 0; + self.anim_num = 1; + self.anim_counter = 0; + } + } + 4 => { + self.vel_y += 0x20; + if self.vel_y > 0x5ff { + self.vel_y = 0x5ff; + } + + self.action_counter += 1; + if self.action_counter >= 20 && (self.flags.hit_bottom_wall() || self.y > player.y - 16 * 0x200) { + self.action_num = 5; + self.anim_num = 2; + self.anim_counter = 0; + self.target_y = self.y; + + if self.flags.hit_bottom_wall() { + self.vel_y = -0x200; + } + } + } + 5 => { + self.anim_counter += 1; + if self.anim_counter > 1 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 4 { + self.anim_num = 2; + } + } + + self.direction = if player.x < self.x { Direction::Left} else {Direction::Right}; + + self.vel_x += (player.x - self.x).signum() * 0x10; + self.vel_y += (self.target_y - self.y).signum() * 0x10; + + self.vel_x = clamp(self.vel_x, -0x200, 0x200); + self.vel_y = clamp(self.vel_y, -0x200, 0x200); + + if self.flags.hit_bottom_wall() { + self.vel_y = -0x200; + } + + if self.flags.hit_top_wall() { + self.vel_y = 0x200; + } + } + _ => {} + } + + self.x += self.vel_x; + self.y += self.vel_y; + + let dir_offset = if self.direction == Direction::Left { 0 } else { 5 }; + + self.anim_rect = state.constants.npc.n031_bat_hanging[self.anim_num as usize + dir_offset]; + + Ok(()) + } + pub(crate) fn tick_n035_mannan(&mut self, state: &mut SharedGameState) -> GameResult { - if self.action_num <= 2 && self.life <= 89 { + if self.action_num <= 2 && self.life < 90 { self.action_num = 3; self.action_counter = 0; self.anim_num = 2; @@ -380,20 +474,21 @@ impl NPC { self.action_num = 4; } } - } else if self.action_num >= 0 && self.shock > 0 { + } else if self.shock > 0 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; let mut npc = NPCMap::create_npc(103, &state.npc_table); npc.cond.set_alive(true); - npc.x = self.x + self.direction.vector_x() * 32 * 0x200; - npc.y = self.y + 32 * 0x200; + npc.x = self.x + self.direction.vector_x() * 8 * 0x200; + npc.y = self.y + 8 * 0x200; npc.direction = self.direction; state.new_npcs.push(npc); } let dir_offset = if self.direction == Direction::Left { 0 } else { 4 }; + self.anim_rect = state.constants.npc.n035_mannan[self.anim_num as usize + dir_offset]; Ok(()) @@ -542,13 +637,14 @@ impl NPC { if self.anim_counter > 5 { self.anim_counter = 0; self.anim_num += 1; - if self.anim_num == 2 { - self.vel_x += self.direction.vector_x() * 0x100; - self.vel_y -= 0x200; - } else if self.anim_num > 2 { - self.action_num = 12; - self.anim_num = 3; - } + } + + if self.anim_num == 2 { + self.vel_x += self.direction.vector_x() * 0x100; + self.vel_y -= 0x200; + } else if self.anim_num > 2 { + self.action_num = 12; + self.anim_num = 3; } } 12 => { @@ -590,10 +686,94 @@ impl NPC { self.y += self.vel_y; } - if self.anim_counter == 1 { - let dir_offset = if self.direction == Direction::Left { 0 } else { 4 }; - self.anim_rect = state.constants.npc.n095_jelly[self.anim_num as usize + dir_offset]; + let dir_offset = if self.direction == Direction::Left { 0 } else { 4 }; + + self.anim_rect = state.constants.npc.n095_jelly[self.anim_num as usize + dir_offset]; + + Ok(()) + } + + pub(crate) fn tick_n100_grate(&mut self, state: &mut SharedGameState) -> GameResult { + if self.action_num == 0 { + self.y += 16 * 0x200; + self.action_num = 1; + + self.anim_rect = if self.direction == Direction::Left { + state.constants.npc.n100_grate[0] + } else { + state.constants.npc.n100_grate[1] + }; } + + Ok(()) + } + + pub(crate) fn tick_n101_malco_screen(&mut self, state: &mut SharedGameState) -> GameResult { + self.anim_counter += 1; + if self.anim_counter > 3 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 2 { + self.anim_num = 0; + } + } + + self.anim_rect = state.constants.npc.n101_malco_screen[self.anim_num as usize]; + + Ok(()) + } + + pub(crate) fn tick_n102_malco_computer_wave(&mut self, state: &mut SharedGameState) -> GameResult { + if self.action_num == 0 { + self.action_num = 1; + self.y += 8 * 0x200; + } + + self.anim_counter += 1; + if self.anim_counter > 0 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 3 { + self.anim_num = 0; + } + } + + self.anim_rect = state.constants.npc.n102_malco_computer_wave[self.anim_num as usize]; + + Ok(()) + } + + pub(crate) fn tick_n103_mannan_projectile(&mut self, state: &mut SharedGameState) -> GameResult { + if self.action_num == 0 { + self.action_num == 1; + } + + self.vel_x += self.direction.vector_x() * 0x20; + + self.anim_counter += 1; + if self.anim_counter > 0 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 2 { + self.anim_num = 0; + } + } + + self.x += self.vel_x; + + self.action_counter2 += 1; + if self.action_counter2 > 100 { + self.cond.set_alive(false); + } + + if self.action_counter2 % 4 == 1 { + state.sound_manager.play_sfx(46); + } + + let dir_offset = if self.direction == Direction::Left { 0 } else { 3 }; + + self.anim_rect = state.constants.npc.n103_mannan_projectile[self.anim_num as usize + dir_offset]; + Ok(()) } @@ -717,6 +897,241 @@ impl NPC { Ok(()) } + pub(crate) fn tick_n107_malco_broken(&mut self, state: &mut SharedGameState) -> GameResult { + match self.action_num { + 0 => { + self.action_num = 1; + + if self.direction == Direction::Right { + self.anim_num = 5; + } + } + 10 | 11 => { + if self.action_num == 10 { + self.action_num = 11; + self.action_counter = 0; + self.anim_counter = 0; + + let mut npc = NPCMap::create_npc(4, &state.npc_table); + for _ in 0..4 { + npc.cond.set_alive(true); + npc.x = self.x; + npc.y = self.y; + npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; + npc.vel_y = state.game_rng.range(-0x600..0) as isize; + + state.new_npcs.push(npc); + } + } + + self.anim_counter += 1; + if self.anim_counter > 1 { + state.sound_manager.play_sfx(43); + + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 1 { + self.anim_num = 0; + } + } + + self.action_counter += 1; + if self.action_counter > 100 { + self.action_num = 12; + } + } + 12 | 13 => { + if self.action_num == 12 { + self.action_num = 13; + self.action_counter = 0; + self.anim_num = 1; + } + + self.action_counter += 1; + if self.action_counter > 50 { + self.action_num = 14; + } + } + 14 | 15 => { + if self.action_num == 14 { + self.action_num = 15; + self.action_counter = 0; + } + + if self.action_counter / 2 % 2 != 0 { + self.x += 0x200; + state.sound_manager.play_sfx(11); + } else { + self.x -= 0x200; + } + + self.action_counter += 1; + if self.action_counter > 50 { + self.action_num = 16; + } + } + 16 | 17 => { + if self.action_num == 16 { + self.action_num = 17; + self.action_counter = 0; + self.anim_num = 2; + state.sound_manager.play_sfx(12); + + let mut npc = NPCMap::create_npc(4, &state.npc_table); + for _ in 0..8 { + npc.cond.set_alive(true); + npc.x = self.x; + npc.y = self.y; + npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; + npc.vel_y = state.game_rng.range(-0x600..0) as isize; + + state.new_npcs.push(npc); + } + } + + self.action_counter += 1; + if self.action_counter > 150 { + self.action_num = 18; + } + } + 18 | 19 => { + if self.action_num == 18 { + self.action_num = 19; + self.action_counter = 0; + self.anim_num = 3; + self.anim_counter = 0; + } + + self.anim_counter += 1; + if self.anim_counter > 3 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 4 { + self.anim_num = 3; + state.sound_manager.play_sfx(11); + } + } + + self.action_counter += 1; + if self.action_counter > 100 { + self.action_num = 20; + state.sound_manager.play_sfx(12); + + let mut npc = NPCMap::create_npc(4, &state.npc_table); + for _ in 0..4 { + npc.cond.set_alive(true); + npc.x = self.x; + npc.y = self.y; + npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; + npc.vel_y = state.game_rng.range(-0x600..0) as isize; + + state.new_npcs.push(npc); + } + } + } + 20 => { + self.anim_num = 4; + } + 21 => { + self.action_num = 22; + self.anim_num = 5; + + state.sound_manager.play_sfx(51); + } + 100 | 101 => { + if self.action_num == 100 { + self.action_num = 101; + self.anim_num = 6; + self.anim_counter = 0; + } + + self.anim_counter += 1; + if self.anim_counter > 4 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 9 { + self.anim_num = 6; + } + } + } + 110 => { + self.cond.set_drs_destroyed(true); + self.cond.set_alive(false); + } + _ => {} + } + + self.anim_rect = state.constants.npc.n107_malco_broken[self.anim_num as usize]; + + Ok(()) + } + + pub(crate) fn tick_n109_malco_powered_on(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { + match self.action_num { + 0 | 1 => { + if self.action_num == 0 { + if self.action_counter > 0 { + self.action_counter -= 1; + } else { + self.action_num = 1; + } + + self.anim_num = 0; + self.action_counter = 0; + } + + if state.game_rng.range(0..120) == 10 { + self.action_num = 2; + self.action_counter = 0; + self.anim_num = 1; + } + + if abs(self.x - player.x) < 32 * 0x200 + && self.y - 32 * 0x200 < player.y + && self.y + 16 * 0x200 > player.y { + self.direction = if self.x > player.x { Direction::Left } else { Direction::Right }; + } + } + 2 => { + self.action_counter += 1; + if self.action_counter > 8 { + self.action_num = 1; + self.anim_num = 0; + } + } + 10 => { + self.action_num = 0; + state.sound_manager.play_sfx(12); + + let mut npc = NPCMap::create_npc(4, &state.npc_table); + for _ in 0..8 { + npc.cond.set_alive(true); + npc.direction = Direction::Left; + npc.x = self.x; + npc.y = self.y; + npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; + npc.vel_y = state.game_rng.range(-0x600..0) as isize; + + state.new_npcs.push(npc); + } + } + _ => {} + } + + self.vel_y += 0x40; + if self.vel_y > 0x5ff { + self.vel_y = 0x5ff; + } + + self.y += self.vel_y; + + let dir_offset = if self.direction == Direction::Left { 0 } else { 2 }; + + self.anim_rect = state.constants.npc.n109_malco_powered_on[self.anim_num as usize + dir_offset]; + + Ok(()) + } + pub(crate) fn tick_n110_puchi(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { match self.action_num { 0 => { @@ -832,6 +1247,7 @@ impl NPC { self.y += self.vel_y; let dir_offset = if self.direction == Direction::Left { 0 } else { 3 }; + self.anim_rect = state.constants.npc.n110_puchi[self.anim_num as usize + dir_offset]; Ok(()) diff --git a/src/npc/misc.rs b/src/npc/misc.rs index 214c1b4..0e36a8b 100644 --- a/src/npc/misc.rs +++ b/src/npc/misc.rs @@ -841,6 +841,106 @@ impl NPC { Ok(()) } + pub(crate) fn tick_n105_hey_bubble_low(&mut self, state: &mut SharedGameState) -> GameResult { + self.action_counter += 1; + + if self.action_counter < 5 { + self.y -= 0x200 + } else if self.action_counter > 30 { + self.cond.set_alive(false); + } + + self.anim_rect = state.constants.npc.n105_hey_bubble_low[self.anim_num as usize]; + + Ok(()) + } + + pub(crate) fn tick_n106_hey_bubble_high(&mut self, state: &mut SharedGameState) -> GameResult { + if self.action_num == 0 { + self.action_num = 1; + + let mut npc = NPCMap::create_npc(105, &state.npc_table); + npc.cond.set_alive(true); + npc.x = self.x; + npc.y = self.y - 8 * 0x200; + + state.new_npcs.push(npc); + } + + Ok(()) + } + + pub(crate) fn tick_n114_press(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { + match self.action_num { + 0 | 1 => { + if self.action_num == 0 { + self.action_num = 1; + self.y -= 4 * 0x200; + } + + if !self.flags.hit_bottom_wall() { + self.action_num = 10; + self.anim_num = 1; + self.anim_counter = 0; + } + } + 10 => { + self.anim_counter += 1; + if self.anim_counter > 2{ + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 2 { + self.anim_num = 2; + } + } + + if player.y > self.y { + self.npc_flags.set_solid_hard(false); + self.damage = 127; + } else { + self.npc_flags.set_solid_hard(true); + self.damage = 0; + } + + if self.flags.hit_bottom_wall() { + if self.anim_num > 1 { + let mut npc = NPCMap::create_npc(4, &state.npc_table); + for _ in 0..4 { + npc.cond.set_alive(true); + npc.x = self.x; + npc.y = self.y; + npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; + npc.vel_y = state.game_rng.range(-0x600..0) as isize; + + state.new_npcs.push(npc); + } + + state.quake_counter = 10; + state.sound_manager.play_sfx(26); + } + + self.action_num = 1; + self.anim_num = 0; + self.damage = 0; + self.npc_flags.set_solid_hard(true); + } + } + _ => {} + } + + self.vel_y += 0x20; + + if self.vel_y > 0x5ff { + self.vel_y = 0x5ff; + } + + self.y += self.vel_y; + + self.anim_rect = state.constants.npc.n114_press[self.anim_num as usize]; + + Ok(()) + } + pub(crate) fn tick_n149_horizontal_moving_block(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { match self.action_num { 0 => { diff --git a/src/npc/mod.rs b/src/npc/mod.rs index 65f51b0..2631951 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -197,12 +197,16 @@ impl GameEntity<(&mut Player, &BTreeMap>, &mut Stage)> for NPC 28 => self.tick_n028_flying_critter(state, player), 29 => self.tick_n029_cthulhu(state, player), 30 => self.tick_n030_gunsmith(state), + 31 => self.tick_n031_bat_hanging(state, player), 32 => self.tick_n032_life_capsule(state), + 33 => self.tick_n033_balrog_bouncing_projectile(state), 34 => self.tick_n034_bed(state), 35 => self.tick_n035_mannan(state), + 36 => self.tick_n036_balrog_hover(state, player), 37 => self.tick_n037_sign(state), 38 => self.tick_n038_fireplace(state), 39 => self.tick_n039_save_sign(state), + 40 => self.tick_n040_santa(state, player), 41 => self.tick_n041_busted_door(state), 42 => self.tick_n042_sue(state, player, map), 43 => self.tick_n043_chalkboard(state), @@ -250,11 +254,20 @@ impl GameEntity<(&mut Player, &BTreeMap>, &mut Stage)> for NPC 97 => self.tick_n097_fan_up(state, player), 98 => self.tick_n098_fan_right(state, player), 99 => self.tick_n099_fan_down(state, player), + 100 => self.tick_n100_grate(state), + 101 => self.tick_n101_malco_screen(state), + 102 => self.tick_n102_malco_computer_wave(state), + 103 => self.tick_n103_mannan_projectile(state), 104 => self.tick_n104_frog(state, player), + 105 => self.tick_n105_hey_bubble_low(state), + 106 => self.tick_n106_hey_bubble_high(state), + 107 => self.tick_n107_malco_broken(state), 108 => self.tick_n108_balfrog_projectile(state), + 109 => self.tick_n109_malco_powered_on(state, player), 110 => self.tick_n110_puchi(state, player), 111 => self.tick_n111_quote_teleport_out(state, player), 112 => self.tick_n112_quote_teleport_in(state, player), + 114 => self.tick_n114_press(state, player), 129 => self.tick_n129_fireball_snake_trail(state), 149 => self.tick_n149_horizontal_moving_block(state, player), 150 => self.tick_n150_quote(state, player), diff --git a/src/npc/santa.rs b/src/npc/santa.rs index 0e1365e..3968db8 100644 --- a/src/npc/santa.rs +++ b/src/npc/santa.rs @@ -1,5 +1,72 @@ +use ggez::GameResult; + use crate::npc::NPC; +use crate::shared_game_state::SharedGameState; +use crate::player::Player; +use num_traits::abs; +use crate::common::Direction; impl NPC { + pub(crate) fn tick_n040_santa(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { + match self.action_num { + 0 | 1 =>{ + if self.action_num == 0 { + self.action_num = 1; + self.anim_num = 0; + self.anim_counter = 0; + } + if state.game_rng.range(0..120) == 0 { + self.action_num = 2; + self.action_counter = 0; + self.anim_num = 1; + } + + if abs(self.x - player.x) < 32 * 0x200 + && self.y - 32 * 0x200 < player.y && self.y + 16 * 0x200 > player.y { + self.direction = if self.x > player.x { + Direction::Left + } else { + Direction::Right + }; + } + } + 2 => { + self.action_counter += 1; + if self.action_counter > 8 { + self.action_num = 1; + self.anim_num = 0; + } + } + 3 | 4 => { + if self.action_num == 3 { + self.action_num = 4; + self.anim_num =2; + self.anim_counter = 0; + } + + self.anim_counter += 1; + if self.anim_counter > 4 { + self.anim_counter = 0; + self.anim_num += 1; + + if self.anim_num > 5 { + self.anim_num = 2; + } + } + + self.x += self.direction.vector_x() * 0x200; + } + 5 => { + self.anim_num = 6; + } + _ => {} + } + + let dir_offset = if self.direction == Direction::Left { 0 } else { 7 }; + + self.anim_rect = state.constants.npc.n040_santa[self.anim_num as usize + dir_offset]; + + Ok(()) + } } diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index b870bc3..4d46667 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -1398,45 +1398,6 @@ impl Scene for GameScene { Ok(()) } - fn draw_tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { - self.frame.prev_x = self.frame.x; - self.frame.prev_y = self.frame.y; - self.player.prev_x = self.player.x; - self.player.prev_y = self.player.y; - - for npc_cell in self.npc_map.npcs.values() { - let mut npc = npc_cell.borrow_mut(); - - if npc.cond.alive() { - npc.prev_x = npc.x; - npc.prev_y = npc.y; - } - } - - for npc in self.npc_map.boss_map.parts.iter_mut() { - if npc.cond.alive() { - npc.prev_x = npc.x; - npc.prev_y = npc.y; - } - } - - for bullet in self.bullet_manager.bullets.iter_mut() { - if bullet.cond.alive() { - bullet.prev_x = bullet.x; - bullet.prev_y = bullet.y; - } - } - - for caret in state.carets.iter_mut() { - if caret.cond.alive() { - caret.prev_x = caret.x; - caret.prev_y = caret.y; - } - } - - Ok(()) - } - fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { state.update_key_trigger(); @@ -1481,6 +1442,45 @@ impl Scene for GameScene { Ok(()) } + fn draw_tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { + self.frame.prev_x = self.frame.x; + self.frame.prev_y = self.frame.y; + self.player.prev_x = self.player.x; + self.player.prev_y = self.player.y; + + for npc_cell in self.npc_map.npcs.values() { + let mut npc = npc_cell.borrow_mut(); + + if npc.cond.alive() { + npc.prev_x = npc.x; + npc.prev_y = npc.y; + } + } + + for npc in self.npc_map.boss_map.parts.iter_mut() { + if npc.cond.alive() { + npc.prev_x = npc.x; + npc.prev_y = npc.y; + } + } + + for bullet in self.bullet_manager.bullets.iter_mut() { + if bullet.cond.alive() { + bullet.prev_x = bullet.x; + bullet.prev_y = bullet.y; + } + } + + for caret in state.carets.iter_mut() { + if caret.cond.alive() { + caret.prev_x = caret.x; + caret.prev_y = caret.y; + } + } + + Ok(()) + } + fn draw(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { graphics::set_canvas(ctx, Some(&state.game_canvas)); self.draw_background(state, ctx)?;