diff --git a/src/common.rs b/src/common.rs index e50622b..f93fd72 100644 --- a/src/common.rs +++ b/src/common.rs @@ -5,6 +5,10 @@ use num_traits::real::Real; use crate::bitfield; +/// Multiply cave story degrees (0-255, which corresponds to 0°-360°) with this to get +/// respective value in radians. +pub const CDEG_RAD: f64 = std::f64::consts::PI * 0.003950617283950617; + bitfield! { #[derive(Clone, Copy)] pub struct Flag(u32); @@ -70,6 +74,9 @@ bitfield! { pub increase_acceleration, set_increase_acceleration: 5; // 0x20 pub cond_x40, set_cond_x40: 6; // 0x40 pub alive, set_alive: 7; // 0x80 + + // engine specific flags + pub drs_destroyed, set_drs_destroyed: 15; } bitfield! { diff --git a/src/engine_constants.rs b/src/engine_constants.rs index 4699bef..9ca459e 100644 --- a/src/engine_constants.rs +++ b/src/engine_constants.rs @@ -164,9 +164,14 @@ pub struct NPCConsts { pub n021_chest_open: Rect<usize>, pub n022_teleporter: [Rect<usize>; 2], pub n023_teleporter_lights: [Rect<usize>; 8], + pub n024_power_critter: [Rect<usize>; 12], + pub n025_lift: [Rect<usize>; 2], + pub n026_bat_flying: [Rect<usize>; 8], pub n027_death_trap: Rect<usize>, + pub n028_flying_critter: [Rect<usize>; 12], pub n029_cthulhu: [Rect<usize>; 4], pub n030_hermit_gunsmith: [Rect<usize>; 3], + pub n031_bat_hanging: [Rect<usize>; 10], pub n032_life_capsule: [Rect<usize>; 2], pub n034_bed: [Rect<usize>; 2], pub n035_mannan: [Rect<usize>; 8], @@ -184,6 +189,7 @@ pub struct NPCConsts { pub n049_skullhead: [Rect<usize>; 6], pub n052_sitting_blue_robot: Rect<usize>, pub n055_kazuma: [Rect<usize>; 12], + pub n058_basu: [Rect<usize>; 6], pub n059_eye_door: [Rect<usize>; 4], pub n060_toroko: [Rect<usize>; 16], pub n061_king: [Rect<usize>; 20], @@ -228,12 +234,15 @@ pub struct NPCConsts { pub n112_quote_teleport_in: [Rect<usize>; 4], pub n129_fireball_snake_trail: [Rect<usize>; 18], pub n149_horizontal_moving_block: Rect<usize>, + pub n150_quote: [Rect<usize>; 18], + pub n154_gaudi_dead: [Rect<usize>; 6], pub n157_vertical_moving_block: Rect<usize>, pub n199_wind_particles: [Rect<usize>; 5], pub n211_small_spikes: [Rect<usize>; 4], pub n298_intro_doctor: [Rect<usize>; 8], pub n299_intro_balrog_misery: [Rect<usize>; 2], pub n300_intro_demon_crown: Rect<usize>, + pub n361_gaudi_dashing: [Rect<usize>; 4], } #[derive(Debug, Copy, Clone)] @@ -692,7 +701,49 @@ impl EngineConstants { Rect { left: 264, top: 40, right: 288, bottom: 44 }, Rect { left: 264, top: 44, right: 288, bottom: 48 }, ], + n024_power_critter: [ + Rect { left: 0, top: 0, right: 24, bottom: 24 }, // left + Rect { left: 24, top: 0, right: 48, bottom: 24 }, + Rect { left: 48, top: 0, right: 72, bottom: 24 }, + Rect { left: 72, top: 0, right: 96, bottom: 24 }, + Rect { left: 96, top: 0, right: 120, bottom: 24 }, + Rect { left: 120, top: 0, right: 144, bottom: 24 }, + Rect { left: 0, top: 24, right: 24, bottom: 48 }, // right + Rect { left: 24, top: 24, right: 48, bottom: 48 }, + Rect { left: 48, top: 24, right: 72, bottom: 48 }, + Rect { left: 72, top: 24, right: 96, bottom: 48 }, + Rect { left: 96, top: 24, right: 120, bottom: 48 }, + Rect { left: 120, top: 24, right: 144, bottom: 48 }, + ], + n025_lift: [ + Rect { left: 256, top: 64, right: 288, bottom: 80 }, + Rect { left: 256, top: 80, right: 288, bottom: 96 }, + ], + n026_bat_flying: [ + Rect { left: 32, top: 80, right: 48, bottom: 96 }, // left + Rect { left: 48, top: 80, right: 64, bottom: 96 }, + Rect { left: 64, top: 80, right: 80, bottom: 96 }, + Rect { left: 80, top: 80, right: 96, bottom: 96 }, + Rect { left: 32, top: 96, right: 48, bottom: 112 }, // right + Rect { left: 48, top: 96, right: 64, bottom: 112 }, + Rect { left: 64, top: 96, right: 80, bottom: 112 }, + Rect { left: 80, top: 96, right: 96, bottom: 112 }, + ], n027_death_trap: Rect { left: 96, top: 64, right: 128, bottom: 88 }, + n028_flying_critter: [ + Rect { left: 0, top: 48, right: 16, bottom: 64 }, // left + Rect { left: 16, top: 48, right: 32, bottom: 64 }, + Rect { left: 32, top: 48, right: 48, bottom: 64 }, + Rect { left: 48, top: 48, right: 64, bottom: 64 }, + Rect { left: 64, top: 48, right: 80, bottom: 64 }, + Rect { left: 80, top: 48, right: 96, bottom: 64 }, + Rect { left: 0, top: 64, right: 16, bottom: 80 }, // right + Rect { left: 16, top: 64, right: 32, bottom: 80 }, + Rect { left: 32, top: 64, right: 48, bottom: 80 }, + Rect { left: 48, top: 64, right: 64, bottom: 80 }, + Rect { left: 64, top: 64, right: 80, bottom: 80 }, + Rect { left: 80, top: 64, right: 96, bottom: 80 }, + ], n029_cthulhu: [ Rect { left: 0, top: 192, right: 16, bottom: 216 }, // left Rect { left: 16, top: 192, right: 32, bottom: 216 }, @@ -704,6 +755,18 @@ impl EngineConstants { Rect { left: 48, top: 16, right: 64, bottom: 32 }, Rect { left: 0, top: 32, right: 16, bottom: 48 }, ], + n031_bat_hanging: [ + Rect { left: 0, top: 80, right: 16, bottom: 96 }, // left + Rect { left: 16, top: 80, right: 32, bottom: 96 }, + Rect { left: 32, top: 80, right: 48, bottom: 96 }, + Rect { left: 48, top: 80, right: 64, bottom: 96 }, + Rect { left: 64, top: 80, right: 80, bottom: 96 }, + Rect { left: 0, top: 96, right: 16, bottom: 112 }, // right + Rect { left: 16, top: 96, right: 32, bottom: 112 }, + Rect { left: 32, top: 96, right: 48, bottom: 112 }, + Rect { left: 48, top: 96, right: 64, bottom: 112 }, + Rect { left: 64, top: 96, right: 80, bottom: 112 }, + ], n032_life_capsule: [ Rect { left: 32, top: 96, right: 48, bottom: 112 }, Rect { left: 48, top: 96, right: 64, bottom: 112 }, @@ -834,6 +897,14 @@ impl EngineConstants { Rect { left: 192, top: 216, right: 208, bottom: 240 }, Rect { left: 240, top: 216, right: 256, bottom: 240 }, ], + n058_basu: [ + Rect { left: 192, top: 0, right: 216, bottom: 24 }, // left + Rect { left: 216, top: 0, right: 240, bottom: 24 }, + Rect { left: 240, top: 0, right: 264, bottom: 24 }, + Rect { left: 192, top: 24, right: 216, bottom: 48 }, // right + Rect { left: 216, top: 24, right: 240, bottom: 48 }, + Rect { left: 240, top: 24, right: 264, bottom: 48 }, + ], n059_eye_door: [ Rect { left: 224, top: 16, right: 240, bottom: 40 }, Rect { left: 208, top: 80, right: 224, bottom: 104 }, @@ -1260,6 +1331,34 @@ impl EngineConstants { Rect { left: 208, top: 80, right: 224, bottom: 96 }, ], n149_horizontal_moving_block: Rect { left: 16, top: 0, right: 48, bottom: 32 }, + n150_quote: [ + Rect { left: 0, top: 0, right: 16, bottom: 16 }, // left + Rect { left: 48, top: 0, right: 64, bottom: 16 }, + Rect { left: 144, top: 0, right: 160, bottom: 16 }, + Rect { left: 16, top: 0, right: 32, bottom: 16 }, + Rect { left: 0, top: 0, right: 16, bottom: 16 }, + Rect { left: 32, top: 0, right: 48, bottom: 16 }, + Rect { left: 0, top: 0, right: 16, bottom: 16 }, + Rect { left: 160, top: 0, right: 176, bottom: 16 }, + Rect { left: 112, top: 0, right: 128, bottom: 16 }, + Rect { left: 0, top: 16, right: 16, bottom: 32 }, //right + Rect { left: 48, top: 16, right: 64, bottom: 32 }, + Rect { left: 144, top: 16, right: 160, bottom: 32 }, + Rect { left: 16, top: 16, right: 32, bottom: 32 }, + Rect { left: 0, top: 16, right: 16, bottom: 32 }, + Rect { left: 32, top: 16, right: 48, bottom: 32 }, + Rect { left: 0, top: 16, right: 16, bottom: 32 }, + Rect { left: 160, top: 16, right: 176, bottom: 32 }, + Rect { left: 112, top: 16, right: 128, bottom: 32 }, + ], + n154_gaudi_dead: [ + Rect { left: 168, top: 24, right: 192, bottom: 48 }, // left + Rect { left: 192, top: 24, right: 216, bottom: 48 }, + Rect { left: 216, top: 24, right: 240, bottom: 48 }, + Rect { left: 168, top: 0, right: 192, bottom: 24 }, // right + Rect { left: 192, top: 0, right: 216, bottom: 24 }, + Rect { left: 216, top: 0, right: 240, bottom: 24 }, + ], n157_vertical_moving_block: Rect { left: 16, top: 0, right: 48, bottom: 32 }, n199_wind_particles: [ Rect { left: 72, top: 16, right: 74, bottom: 18 }, @@ -1289,6 +1388,12 @@ impl EngineConstants { Rect { left: 48, top: 0, right: 96, bottom: 48 }, ], n300_intro_demon_crown: Rect { left: 192, top: 80, right: 208, bottom: 96 }, + n361_gaudi_dashing: [ + Rect { left: 48, top: 48, right: 72, bottom: 72 }, // left + Rect { left: 72, top: 48, right: 96, bottom: 72 }, + Rect { left: 48, top: 72, right: 72, bottom: 96 }, // right + Rect { left: 72, top: 72, right: 96, bottom: 96 }, + ], }, weapon: WeaponConsts { bullet_table: vec![ diff --git a/src/lib.rs b/src/lib.rs index 9265bc0..e1e9478 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -183,6 +183,7 @@ impl Game { } KeyCode::F10 => { state.settings.debug_outlines = !state.settings.debug_outlines } KeyCode::F11 => { state.settings.god_mode = !state.settings.god_mode } + KeyCode::F12 => { state.settings.infinite_booster = !state.settings.infinite_booster } _ => {} } } diff --git a/src/map.rs b/src/map.rs index c2a8dee..d545244 100644 --- a/src/map.rs +++ b/src/map.rs @@ -56,7 +56,6 @@ impl Map { pub fn get_attribute(&self, x: usize, y: usize) -> u8 { if x >= self.width || y >= self.height { - log::warn!("x: {} > {}, y: {} > {}", x, self.width, y, self.height); return 0; } diff --git a/src/npc/characters.rs b/src/npc/characters.rs index 27b5a05..6dbb2d6 100644 --- a/src/npc/characters.rs +++ b/src/npc/characters.rs @@ -1,4 +1,4 @@ -use num_traits::clamp; +use num_traits::{clamp, abs}; use crate::common::Direction; use crate::ggez::GameResult; @@ -7,6 +7,25 @@ use crate::player::Player; use crate::shared_game_state::SharedGameState; impl NPC { + pub(crate) fn tick_n029_cthulhu(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { + if self.action_num == 0 { + self.action_num = 1; + self.anim_num = 0; + self.anim_counter = 0; + } + + if abs(self.x - player.x) < 48 * 0x200 && self.y - 48 * 0x200 < player.y && self.y + 16 * 0x200 > player.y { + self.anim_num = 1; + } else { + self.anim_num = 0; + } + + let dir_offset = if self.direction == Direction::Left { 0 } else { 2 }; + self.anim_rect = state.constants.npc.n029_cthulhu[self.anim_num as usize + dir_offset]; + + Ok(()) + } + pub(crate) fn tick_n052_sitting_blue_robot(&mut self, state: &mut SharedGameState) -> GameResult { if self.action_num == 0 { self.action_num = 1; diff --git a/src/npc/egg_corridor.rs b/src/npc/egg_corridor.rs index 0d8a714..47e8cfc 100644 --- a/src/npc/egg_corridor.rs +++ b/src/npc/egg_corridor.rs @@ -1,11 +1,12 @@ use nalgebra::clamp; +use num_traits::abs; -use crate::common::Direction; +use crate::caret::CaretType; +use crate::common::{Direction, Rect, CDEG_RAD}; use crate::ggez::GameResult; -use crate::npc::NPC; +use crate::npc::{NPC, NPCMap}; use crate::player::Player; use crate::shared_game_state::SharedGameState; -use crate::caret::CaretType; impl NPC { pub(crate) fn tick_n002_behemoth(&mut self, state: &mut SharedGameState) -> GameResult { @@ -442,6 +443,209 @@ impl NPC { Ok(()) } + pub(crate) fn tick_n025_lift(&mut self, state: &mut SharedGameState) -> GameResult { + match self.action_num { + 0 | 1 => { + if self.action_num == 0 { + self.x += 8 * 0x200; + self.action_num = 1; + self.anim_num = 0; + self.anim_counter = 1; + } + + self.action_counter += 1; + if self.action_counter > 150 { + self.action_num = 2; + self.action_counter = 0; + } + } + 2 => { + self.action_counter += 1; + if self.action_counter > 64 { + self.action_num = 3; + self.action_counter = 0; + } else { + self.y -= 0x200; + } + } + 3 => { + self.action_counter += 1; + if self.action_counter > 150 { + self.action_num = 4; + self.action_counter = 0; + } + } + 4 => { + self.action_counter += 1; + if self.action_counter > 64 { + self.action_num = 5; + self.action_counter = 0; + } else { + self.y -= 0x200; + } + } + 5 => { + self.action_counter += 1; + if self.action_counter > 150 { + self.action_num = 6; + self.action_counter = 0; + } + } + 6 => { + self.action_counter += 1; + if self.action_counter > 64 { + self.action_num = 7; + self.action_counter = 0; + } else { + self.y += 0x200; + } + } + 7 => { + self.action_counter += 1; + if self.action_counter > 150 { + self.action_num = 8; + self.action_counter = 0; + } + } + 8 => { + self.action_counter += 1; + if self.action_counter > 64 { + self.action_num = 1; + self.action_counter = 0; + } else { + self.y += 0x200; + } + } + _ => {} + } + + if [2, 4, 6, 8].contains(&self.action_num) { + self.anim_counter += 1; + if self.anim_counter > 1 { + self.anim_num += 1; + if self.anim_num > 1 { + self.anim_num = 0; + } + } + } + + if self.anim_counter == 1 { + self.anim_rect = state.constants.npc.n025_lift[self.anim_num as usize]; + } + + Ok(()) + } + + pub(crate) fn tick_n058_basu(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { + match self.action_num { + 0 => { + if player.x < self.x + 16 * 0x200 && player.x > self.x - 16 * 0x200 { + self.target_x = self.x; + self.target_y = self.y; + self.action_num = 1; + self.action_counter = 0; + self.action_counter2 = 0; + self.damage = 6; + self.vel_y = -0x100; + self.tsc_direction = self.direction as u16; + self.npc_flags.set_shootable(true); + + self.x = player.x + self.direction.vector_x() * 16 * 16 * 0x200; + self.vel_x = self.direction.vector_x() * 0x2ff; + } else { + self.anim_rect = Rect::new(0, 0, 0, 0); + self.damage = 0; + self.vel_x = 0; + self.vel_y = 0; + self.npc_flags.set_shootable(false); + } + + return Ok(()); + } + 1 => { + if self.x > player.x { + self.direction = Direction::Left; + self.vel_x -= 0x10; + } else { + self.direction = Direction::Right; + self.vel_x += 0x10; + } + + if self.flags.hit_left_wall() { + self.vel_x = 0x200; + } + + if self.flags.hit_right_wall() { + self.vel_x = -0x200; + } + + self.vel_y += ((self.target_y - self.y).signum() | 1) * 0x08; + + if self.shock > 0 { + self.x += self.vel_x / 2; + self.y += self.vel_y / 2; + } else { + self.x += self.vel_x; + self.y += self.vel_y; + } + + if player.x > self.x + 400 * 0x200 || player.x < self.x - 400 * 0x200 { + self.action_num = 0; + self.vel_x = 0; + self.x = self.target_x; + self.damage = 0; + self.direction = Direction::from_int_facing(self.tsc_direction as usize) + .unwrap_or(Direction::Left); + self.anim_rect = Rect::new(0, 0, 0, 0); + return Ok(()); + } + } + _ => {} + } + + if self.action_counter < 150 { + self.action_counter += 1; + } 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(); + + (state.game_rng.range(-6..6) as u8) as f64 * CDEG_RAD; + + let mut npc = NPCMap::create_npc(84, &state.npc_table); + npc.cond.set_alive(true); + npc.x = self.x; + npc.y = self.y; + npc.vel_x = (angle.cos() * 1024.0) as isize; + npc.vel_y = (angle.sin() * 1024.0) as isize; + + state.new_npcs.push(npc); + } + + if self.action_counter2 > 8 { + self.action_counter = 0; + self.action_counter2 = 0; + } + } + + self.anim_counter += 1; + if self.anim_counter > 1 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 1 { + self.anim_num = 0; + } + } + + if self.action_counter > 120 && self.action_counter / 2 % 2 == 1 && self.anim_num == 1 { + self.anim_num = 2; + } + + let dir_offset = if self.direction == Direction::Left { 0 } else { 3 }; + self.anim_rect = state.constants.npc.n058_basu[self.anim_num as usize + dir_offset]; + + Ok(()) + } + pub(crate) fn tick_n084_basu_projectile(&mut self, state: &mut SharedGameState) -> GameResult { self.x += self.vel_x; self.y += self.vel_y; diff --git a/src/npc/grasstown.rs b/src/npc/grasstown.rs index e69de29..aaae5ee 100644 --- a/src/npc/grasstown.rs +++ b/src/npc/grasstown.rs @@ -0,0 +1,349 @@ +use num_traits::abs; +use num_traits::clamp; + +use crate::common::Direction; +use crate::ggez::GameResult; +use crate::npc::NPC; +use crate::player::Player; +use crate::shared_game_state::SharedGameState; + +impl NPC { + pub(crate) fn tick_n024_power_critter(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { + match self.action_num { + 0 | 1 => { + if self.action_num == 0 { + self.y += 3 * 0x200; + self.action_num = 1; + } + + if self.action_counter >= 8 + && abs(self.x - player.x) < (112 * 0x200) + && abs(self.y - player.y) < (80 * 0x200) { + if self.x > player.x { + self.direction = Direction::Left; + } else { + self.direction = Direction::Right; + } + + self.anim_num = 1; + } else { + if self.action_counter < 8 { + self.action_counter += 1; + } + + self.anim_num = 0; + } + + if self.shock > 0 { + self.action_num = 2; + self.action_counter = 0; + self.anim_num = 0; + } + + if self.action_counter >= 8 + && abs(self.x - player.x) < 96 * 0x200 + && self.y - 96 * 0x200 < player.y + && self.y + 48 * 0x200 > player.y { + self.action_num = 2; + self.action_counter = 0; + self.anim_num = 0; + } + } + 2 => { + self.action_counter += 1; + if self.action_counter > 8 { + if self.x > player.x { + self.direction = Direction::Left; + } else { + self.direction = Direction::Right; + } + + self.action_num = 3; + self.anim_num = 2; + self.vel_x = self.direction.vector_x() * 0x100; + self.vel_y = -0x5ff; + state.sound_manager.play_sfx(108); + } + } + 3 => { + if self.vel_y > 0x200 { + self.target_y = self.y; + self.action_num = 4; + self.action_counter = 0; + self.anim_num = 3; + } + } + 4 => { + if self.x > player.x { + self.direction = Direction::Left; + } else { + self.direction = Direction::Right; + } + + self.action_counter += 1; + + if (self.flags.hit_left_wall() + || self.flags.hit_right_wall() + || self.flags.hit_top_wall()) || self.action_counter > 100 { + self.action_num = 5; + self.anim_num = 2; + self.vel_x /= 2; + self.damage = 12; + } + + 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; + } + } + } + 5 => { + if self.flags.hit_bottom_wall() { + self.vel_x = 0; + self.action_num = 1; + self.action_counter = 0; + self.anim_num = 0; + self.damage = 2; + + state.sound_manager.play_sfx(23); + state.quake_counter = 30; + } + } + _ => {} + } + + if self.action_num != 4 { + self.vel_y += 0x40; + if self.vel_y > 0x5ff { + self.vel_y = 0x5ff; + } + } else { + self.vel_x = clamp(self.vel_x + if self.x < player.x { 0x20 } else { -0x20 }, -0x200, 0x200); + self.vel_y = clamp(self.vel_x + if self.y > self.target_y { -0x10 } else { 0x10 }, -0x200, 0x200); + } + + 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.n024_power_critter[self.anim_num as usize + dir_offset]; + + Ok(()) + } + + pub(crate) fn tick_n026_bat_flying(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { + match self.action_num { + 0 | 1 => { + if self.action_num == 0 { + let angle = state.game_rng.range(0..0xff); + self.vel_x = ((angle as f64 * 1.40625).cos() * 512.0) as isize; + self.target_x = self.x + ((angle as f64 * 1.40625 + std::f64::consts::FRAC_2_PI).cos() * 8.0 * 512.0) as isize; + + let angle = state.game_rng.range(0..0xff); + self.vel_y = ((angle as f64 * 1.40625).sin() * 512.0) as isize; + self.target_y = self.y + ((angle as f64 * 1.40625 + std::f64::consts::FRAC_2_PI).sin() * 8.0 * 512.0) as isize; + + self.action_num = 1; + self.action_counter2 = 120; + } + + if self.x > player.x { + self.direction = Direction::Left; + } else { + self.direction = Direction::Right; + } + + self.vel_x = clamp(self.vel_x + 0x10 * (self.target_x - self.x).signum(), -0x200, 0x200); + self.vel_y = clamp(self.vel_y + 0x10 * (self.target_y - self.y).signum(), -0x200, 0x200); + + if self.action_counter2 < 120 { + self.action_counter2 += 1; + } else if abs(self.x - player.x) < 8 * 0x200 + && self.y < player.y + && self.y + 96 * 0x200 > player.y { + self.vel_x /= 2; + self.vel_y = 0; + self.action_num = 3; + self.npc_flags.set_ignore_solidity(false); + } + } + 3 => { + self.vel_y += 0x40; + if self.vel_y > 0x5ff { + self.vel_y = 0x5ff; + } + + if self.flags.hit_bottom_wall() { + self.vel_x *= 2; + self.vel_y = 0; + self.action_counter2 = 0; + self.action_num = 1; + self.npc_flags.set_ignore_solidity(true); + } + } + _ => {} + } + + self.x += self.vel_x; + self.y += self.vel_y; + + if self.action_num == 3 { + self.anim_num = 3; + } else { + self.anim_counter += 1; + if self.anim_counter > 1 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 2 { + self.anim_num = 0; + } + } + } + + let dir_offset = if self.direction == Direction::Left { 0 } else { 4 }; + self.anim_rect = state.constants.npc.n026_bat_flying[self.anim_num as usize + dir_offset]; + + Ok(()) + } + + pub(crate) fn tick_n028_flying_critter(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { + match self.action_num { + 0 | 1 => { + if self.action_num == 0 { + self.y += 3 * 0x200; + self.action_num = 1; + } + + if self.action_counter >= 8 + && abs(self.x - player.x) < 96 * 0x200 + && self.y - 128 * 0x200 < player.y + && self.y + 48 * 0x200 > player.y { + if self.x > player.x { + self.direction = Direction::Left; + } else { + self.direction = Direction::Right; + } + + self.anim_num = 1; + } else { + if self.action_counter < 8 { + self.action_counter += 1; + } + + self.anim_num = 0; + } + + if self.shock > 0 { + self.action_num = 2; + self.action_counter = 0; + self.anim_num = 0; + } + + if self.action_counter >= 8 + && abs(self.x - player.x) < 96 * 0x200 + && self.y - 96 * 0x200 < player.y + && self.y + 48 * 0x200 > player.y { + self.action_num = 2; + self.action_counter = 0; + self.anim_num = 0; + } + } + 2 => { + self.action_counter += 1; + if self.action_counter > 8 { + if self.x > player.x { + self.direction = Direction::Left; + } else { + self.direction = Direction::Right; + } + + self.action_num = 3; + self.anim_num = 2; + self.vel_x = self.direction.vector_x() * 0x100; + self.vel_y = -0x4cc; + state.sound_manager.play_sfx(30); + } + } + 3 => { + if self.vel_y > 0x100 { + self.target_y = self.y; + self.action_num = 4; + self.action_counter = 0; + self.anim_num = 3; + } + } + 4 => { + if self.x > player.x { + self.direction = Direction::Left; + } else { + self.direction = Direction::Right; + } + + self.action_counter += 1; + + if (self.flags.hit_left_wall() + || self.flags.hit_right_wall() + || self.flags.hit_top_wall()) || self.action_counter > 100 { + self.action_num = 5; + self.anim_num = 2; + self.vel_x /= 2; + self.damage = 3; + } + + if self.action_counter % 4 == 1 { + state.sound_manager.play_sfx(110); + } + + if self.flags.hit_bottom_wall() { + self.vel_y = -0x200; + } + + 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; + } + } + } + 5 => { + if self.flags.hit_bottom_wall() { + self.vel_x = 0; + self.action_num = 1; + self.action_counter = 0; + self.anim_num = 0; + self.damage = 2; + + state.sound_manager.play_sfx(23); + } + } + _ => {} + } + + if self.action_num != 4 { + self.vel_y += 0x40; + if self.vel_y > 0x5ff { + self.vel_y = 0x5ff; + } + } else { + self.vel_x = clamp(self.vel_x + if self.x < player.x { 0x20 } else { -0x20 }, -0x200, 0x200); + self.vel_y = clamp(self.vel_x + if self.y > self.target_y { -0x10 } else { 0x10 }, -0x200, 0x200); + } + + 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.n028_flying_critter[self.anim_num as usize + dir_offset]; + + Ok(()) + } +} diff --git a/src/npc/maze.rs b/src/npc/maze.rs index 0e1365e..333a545 100644 --- a/src/npc/maze.rs +++ b/src/npc/maze.rs @@ -1,5 +1,95 @@ +use num_traits::{abs, clamp}; + +use crate::common::Direction; +use crate::ggez::GameResult; use crate::npc::NPC; +use crate::player::Player; +use crate::shared_game_state::SharedGameState; impl NPC { + pub(crate) fn tick_n154_gaudi_dead(&mut self, state: &mut SharedGameState) -> GameResult { + let dir_offset = if self.direction == Direction::Left { 0 } else { 3 }; + self.anim_rect = state.constants.npc.n154_gaudi_dead[self.anim_num as usize + dir_offset]; + Ok(()) + } + + pub(crate) fn tick_n361_gaudi_dashing(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { + match self.action_num { + 0 | 1 => { + if self.action_num == 0 { + self.vel_x2 = 0; + self.vel_y2 = 0; + self.action_num = 1; + } + + if (self.direction == Direction::Right && player.x > self.x + 272 * 0x200 && player.x < self.x + 288 * 0x200) + || (self.direction == Direction::Left && player.x < self.x - 272 * 0x200 && player.x > self.x - 288 * 0x200) { + self.action_num = 10; + } else { + return Ok(()); + } + } + 10 | 11 => { + if self.action_num == 10 { + self.npc_flags.set_shootable(true); + self.action_num = 11; + self.damage = 5; + } + + if self.x > player.x { + self.direction = Direction::Left; + } else { + self.direction = Direction::Right; + } + + self.anim_counter += 1; + if self.anim_counter > 1 { + self.anim_counter = 0; + self.anim_num += 1; + + if self.anim_num > 1 { + self.anim_num = 0; + } + } + + self.vel_x2 += self.direction.vector_x() * 0x10; + self.vel_y2 += (player.y - self.y).signum() * 0x10; + + if self.vel_x2 < 0 && self.flags.hit_left_wall() { + self.vel_x2 /= -2; + } + + if self.vel_x2 > 0 && self.flags.hit_right_wall() { + self.vel_x2 /= -2; + } + + if self.vel_y2 < 0 && self.flags.hit_top_wall() { + self.vel_y2 *= -1; + } + + if self.vel_y2 > 0 && self.flags.hit_bottom_wall() { + self.vel_y2 /= -2; + } + + self.vel_x2 = clamp(self.vel_x2, -0x5ff, 0x5ff); + self.vel_y2 = clamp(self.vel_y2, -0x5ff, 0x5ff); + + self.x += self.vel_x2; + self.y += self.vel_y2; + } + _ => {} + } + + if self.life <= 985 { + self.cond.set_drs_destroyed(true); + self.npc_type = 154; + self.action_num = 0; + } + + let dir_offset = if self.direction == Direction::Left { 0 } else { 2 }; + self.anim_rect = state.constants.npc.n361_gaudi_dashing[self.anim_num as usize + dir_offset]; + + Ok(()) + } } diff --git a/src/npc/misc.rs b/src/npc/misc.rs index 671421e..39f7028 100644 --- a/src/npc/misc.rs +++ b/src/npc/misc.rs @@ -370,6 +370,21 @@ impl NPC { Ok(()) } + pub(crate) fn tick_n023_teleporter_lights(&mut self, state: &mut SharedGameState) -> GameResult { + self.anim_counter += 1; + if self.anim_counter > 1 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 7 { + self.anim_num = 0; + } + } else if self.anim_counter == 1 { + self.anim_rect = state.constants.npc.n023_teleporter_lights[self.anim_num as usize]; + } + + Ok(()) + } + pub(crate) fn tick_n027_death_trap(&mut self, state: &mut SharedGameState) -> GameResult { if self.action_num == 0 { self.action_num = 1; diff --git a/src/npc/mod.rs b/src/npc/mod.rs index 2dbd901..a99bdea 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -73,11 +73,19 @@ pub struct NPC { pub npc_type: u16, pub x: isize, pub y: isize, + /// X velocity, affected by physics code pub vel_x: isize, + /// Y velocity, affected by physics code pub vel_y: isize, + /// X velocity, unaffected by physics code + pub vel_x2: isize, + /// Y velocity, unaffected by physics code + pub vel_y2: isize, pub target_x: isize, pub target_y: isize, + /// Previous X position, used by frame interpolator pub prev_x: isize, + /// Previous Y position, used by frame interpolator pub prev_y: isize, pub exp: u16, pub size: u8, @@ -88,6 +96,9 @@ pub struct NPC { pub flags: Flag, pub npc_flags: NPCFlag, pub direction: Direction, + /// Raw direction value set by TSC because some NPCs have it set outside 0-4 range, + /// breaking the direction type. + pub tsc_direction: u16, pub display_bounds: Rect<usize>, pub hit_bounds: Rect<usize>, pub parent_id: u16, @@ -101,7 +112,7 @@ pub struct NPC { pub anim_rect: Rect<usize>, } -static PARTICLE_NPCS: [u16; 8] = [1, 4, 73, 86, 87, 129, 199, 355]; +static PARTICLE_NPCS: [u16; 9] = [1, 4, 73, 84, 86, 87, 129, 199, 355]; impl NPC { pub fn get_start_index(&self) -> u16 { @@ -120,6 +131,8 @@ impl NPC { y: 0, vel_x: 0, vel_y: 0, + vel_x2: 0, + vel_y2: 0, target_x: 0, target_y: 0, prev_x: 0, @@ -133,6 +146,7 @@ impl NPC { flags: Flag(0), npc_flags: NPCFlag(0), direction: Direction::Left, + tsc_direction: 0, display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 }, hit_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 }, parent_id: 0, @@ -174,7 +188,13 @@ impl GameEntity<(&mut Player, &HashMap<u16, RefCell<NPC>>, &mut Stage)> for NPC 20 => self.tick_n020_computer(state), 21 => self.tick_n021_chest_open(state), 22 => self.tick_n022_teleporter(state), + 23 => self.tick_n023_teleporter_lights(state), + 24 => self.tick_n024_power_critter(state, player), + 25 => self.tick_n025_lift(state), + 26 => self.tick_n026_bat_flying(state, player), 27 => self.tick_n027_death_trap(state), + 28 => self.tick_n028_flying_critter(state, player), + 29 => self.tick_n029_cthulhu(state, player), 30 => self.tick_n030_gunsmith(state), 32 => self.tick_n032_life_capsule(state), 34 => self.tick_n034_bed(state), @@ -187,6 +207,7 @@ impl GameEntity<(&mut Player, &HashMap<u16, RefCell<NPC>>, &mut Stage)> for NPC 46 => self.tick_n046_hv_trigger(state, player), 52 => self.tick_n052_sitting_blue_robot(state), 55 => self.tick_n055_kazuma(state), + 58 => self.tick_n058_basu(state, player), 59 => self.tick_n059_eye_door(state, player), 60 => self.tick_n060_toroko(state, player), 61 => self.tick_n061_king(state), @@ -225,12 +246,15 @@ impl GameEntity<(&mut Player, &HashMap<u16, RefCell<NPC>>, &mut Stage)> for NPC 112 => self.tick_n112_quote_teleport_in(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), + 154 => self.tick_n154_gaudi_dead(state), 157 => self.tick_n157_vertical_moving_block(state, player), 199 => self.tick_n199_wind_particles(state), 211 => self.tick_n211_small_spikes(state), 298 => self.tick_n298_intro_doctor(state), 299 => self.tick_n299_intro_balrog_misery(state), 300 => self.tick_n300_intro_demon_crown(state), + 361 => self.tick_n361_gaudi_dashing(state, player), _ => Ok(()), }?; @@ -389,6 +413,8 @@ impl NPCMap { y: data.y as isize * 16 * 0x200, vel_x: 0, vel_y: 0, + vel_x2: 0, + vel_y2: 0, target_x: 0, target_y: 0, prev_x: 0, @@ -405,6 +431,7 @@ impl NPCMap { cond: Condition(0x00), flags: Flag(0), direction: if npc_flags.spawn_facing_right() { Direction::Right } else { Direction::Left }, + tsc_direction: 0, npc_flags, display_bounds, hit_bounds, @@ -437,6 +464,8 @@ impl NPCMap { y: 0, vel_x: 0, vel_y: 0, + vel_x2: 0, + vel_y2: 0, target_x: 0, target_y: 0, prev_x: 0, @@ -453,6 +482,7 @@ impl NPCMap { cond: Condition(0x00), flags: Flag(0), direction: if npc_flags.spawn_facing_right() { Direction::Right } else { Direction::Left }, + tsc_direction: 0, npc_flags, display_bounds, hit_bounds, diff --git a/src/npc/quote.rs b/src/npc/quote.rs index ab48450..4c0e488 100644 --- a/src/npc/quote.rs +++ b/src/npc/quote.rs @@ -1,8 +1,8 @@ -use crate::ggez::GameResult; -use crate::npc::NPC; -use crate::shared_game_state::SharedGameState; use crate::common::Direction; +use crate::ggez::GameResult; +use crate::npc::{NPC, NPCMap}; use crate::player::Player; +use crate::shared_game_state::SharedGameState; impl NPC { pub fn tick_n111_quote_teleport_out(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { @@ -131,4 +131,158 @@ impl NPC { Ok(()) } + + pub(crate) fn tick_n150_quote(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult { + match self.action_num { + 0 => { + self.action_num = 1; + self.anim_num = 0; + + if self.tsc_direction > 10 { + self.x = player.x; + self.y = player.y; + + self.direction = Direction::from_int(self.tsc_direction.saturating_sub(10) as usize) + .unwrap_or(Direction::Left); + } else { + self.direction = Direction::from_int(self.tsc_direction as usize) + .unwrap_or(Direction::Left); + } + } + 2 => { + self.anim_num = 1; + } + 10 => { + self.action_num = 11; + self.anim_num = 2; + + state.sound_manager.play_sfx(71); + + let mut npc = NPCMap::create_npc(4, &state.npc_table); + + for _ in 0..4 { + 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); + } + } + 11 => { + self.anim_num = 2; + } + 20 => { + self.action_num = 21; + self.action_counter = 63; + + state.sound_manager.play_sfx(29); + } + 21 => { + if self.action_counter > 0 { + self.action_counter -= 1; + } else { + self.cond.set_alive(false); + } + } + 50 | 51 => { + if self.action_num == 50 { + self.action_num = 51; + self.anim_num = 3; + 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 > 6 { + self.anim_num = 3; + } + } + + self.x += self.direction.vector_x() * 0x200; + } + 60 | 61 => { + if self.action_num == 60 { + self.action_num = 61; + self.anim_num = 7; + self.target_x = self.x; + self.target_y = self.y; + } + + self.target_y += 0x100; + self.x = self.target_x + state.game_rng.range(-1..1) as isize * 0x200; + self.y = self.target_y + state.game_rng.range(-1..1) as isize * 0x200; + } + 70 | 71 => { + if self.action_num == 70 { + self.action_num = 71; + self.action_counter = 0; + self.anim_num = 3; + self.anim_counter = 0; + } + + self.x += (self.direction.vector_x() as isize | 1) * 0x100; + + self.anim_counter += 1; + if self.anim_counter > 8 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 6 { + self.anim_num = 3; + } + } + } + 80 => { + self.anim_num = 8; + } + 99 | 100 | 101 => { + if self.action_num == 99 || self.action_num == 100 { + self.action_num = 101; + self.anim_num = 3; + self.anim_counter = 0; + } + + self.vel_y += 0x40; + if self.vel_y > 0x5ff { + self.vel_y = 0x5ff; + } + + if self.flags.hit_bottom_wall() { + self.vel_y = 0; + self.action_num = 102; + } + + self.y += self.vel_y; + } + 102 => { + self.anim_counter += 1; + if self.anim_counter > 8 { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > 6 { + self.anim_num = 3; + } + } + } + _ => {} + } + + let dir_offset = if self.direction == Direction::Left { 0 } else { 9 }; + self.anim_rect = state.constants.npc.n150_quote[self.anim_num as usize + dir_offset]; + + if self.action_num == 21 { + self.anim_rect.bottom = self.anim_rect.top + self.action_counter as usize / 4; + } + + if player.equip.has_mimiga_mask() { + self.anim_rect.top += 32; + self.anim_rect.bottom += 32; + } + + Ok(()) + } } diff --git a/src/npc/santa.rs b/src/npc/santa.rs index e69de29..0e1365e 100644 --- a/src/npc/santa.rs +++ b/src/npc/santa.rs @@ -0,0 +1,5 @@ +use crate::npc::NPC; + +impl NPC { + +} diff --git a/src/player.rs b/src/player.rs index f911dfb..d9d5794 100644 --- a/src/player.rs +++ b/src/player.rs @@ -158,7 +158,9 @@ impl Player { if self.flags.hit_bottom_wall() || self.flags.hit_right_slope() || self.flags.hit_left_slope() { self.booster_switch = 0; - if self.equip.has_booster_0_8() || self.equip.has_booster_2_0() { + if state.settings.infinite_booster { + self.booster_fuel = usize::MAX; + } else if self.equip.has_booster_0_8() || self.equip.has_booster_2_0() { self.booster_fuel = state.constants.booster.fuel; } else { self.booster_fuel = 0; @@ -212,7 +214,7 @@ impl Player { if self.vel_y > 0x100 { // 0.5fix9 self.vel_y /= 2; } - } else if self.equip.has_booster_2_0() { + } else if (state.settings.infinite_booster || self.equip.has_booster_2_0()) { if state.key_state.up() { self.booster_switch = 2; self.vel_x = 0; @@ -254,7 +256,8 @@ impl Player { } } - if self.equip.has_booster_2_0() && self.booster_switch != 0 && (!state.key_state.jump() || self.booster_fuel == 0) { + if (state.settings.infinite_booster || self.equip.has_booster_2_0()) && self.booster_switch != 0 + && (!state.key_state.jump() || self.booster_fuel == 0) { match self.booster_switch { 1 => { self.vel_x /= 2 } 2 => { self.vel_y /= 2 } @@ -297,13 +300,13 @@ impl Player { self.vel_y -= 0x80; } if self.flags.force_right() { - self.vel_x += 0x80; + self.vel_x += 0x88; } if self.flags.force_down() { self.vel_y += 0x55; } - if self.equip.has_booster_2_0() && self.booster_switch != 0 { + if (state.settings.infinite_booster || self.equip.has_booster_2_0()) && self.booster_switch != 0 { match self.booster_switch { 1 => { if self.flags.hit_left_wall() || self.flags.hit_right_wall() { @@ -386,12 +389,11 @@ impl Player { if !self.splash && self.flags.in_water() { let vertical_splash = !self.flags.hit_bottom_wall() && self.vel_y > 0x200; let horizontal_splash = self.vel_x > 0x200 || self.vel_x < -0x200; - let should_splash = vertical_splash || horizontal_splash; - if should_splash { + if vertical_splash || horizontal_splash { + let mut droplet = NPCMap::create_npc(73, &state.npc_table); + for _ in 0..7 { - let mut droplet = NPCMap::create_npc(73, &state.npc_table); - droplet.cond.set_alive(true); droplet.direction = if self.flags.water_splash_facing_right() { Direction::Right } else { Direction::Left }; droplet.x = self.x + (state.game_rng.range(-8..8) * 0x200) as isize; @@ -424,17 +426,7 @@ impl Player { } // camera - if self.direction == Direction::Left { - self.index_x -= 0x200; // 1.0fix9 - if self.index_x < -0x8000 { // -64.0fix9 - self.index_x = -0x8000; - } - } else { // possible bug? - self.index_x += 0x200; // 1.0fix9 - if self.index_x > 0x8000 { // -64.0fix9 - self.index_x = 0x8000; - } - } + self.index_x = clamp(self.index_x + self.direction.vector_x() * 0x200, -0x8000, 0x8000); if state.control_flags.control_enabled() && state.key_state.up() { self.index_y -= 0x200; // 1.0fix9 diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index fae4fe9..c6c1571 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -783,6 +783,11 @@ impl GameScene { for npc_id in self.npc_map.npc_ids.iter() { if let Some(npc_cell) = self.npc_map.npcs.get(npc_id) { let mut npc = npc_cell.borrow_mut(); + + if npc.cond.drs_destroyed() { + dead_npcs.push(npc.id); + } + if !npc.cond.alive() { continue; } @@ -856,9 +861,11 @@ impl GameScene { } } - if npc.cond.explode_die() { + if npc.cond.explode_die() && !npc.cond.drs_destroyed() { dead_npcs.push(npc.id); } + + npc.cond.set_drs_destroyed(false); } } @@ -1058,30 +1065,31 @@ impl GameScene { continue; } - // top - state.texture_set.draw_rect(Rect::new_size((npc.x - npc.hit_bounds.right as isize - self.frame.x) / 0x200, - (npc.y - npc.hit_bounds.top as isize - self.frame.y) / 0x200, - (npc.hit_bounds.right + npc.hit_bounds.right) as isize / 0x200, - 1), - [0.0, if npc.flags.hit_top_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?; - // bottom - state.texture_set.draw_rect(Rect::new_size((npc.x - npc.hit_bounds.right as isize - self.frame.x) / 0x200, - (npc.y + npc.hit_bounds.bottom as isize - self.frame.y) / 0x200 - 1, - (npc.hit_bounds.right + npc.hit_bounds.right) as isize / 0x200, - 1), - [0.0, if npc.flags.hit_bottom_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?; - // left - state.texture_set.draw_rect(Rect::new_size((npc.x - npc.hit_bounds.right as isize - self.frame.x) / 0x200, - (npc.y - npc.hit_bounds.top as isize - self.frame.y) / 0x200, - 1, - (npc.hit_bounds.top + npc.hit_bounds.bottom) as isize / 0x200), - [0.0, if npc.flags.hit_left_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?; - // right - state.texture_set.draw_rect(Rect::new_size((npc.x + npc.hit_bounds.right as isize - self.frame.x) / 0x200 - 1, - (npc.y - npc.hit_bounds.top as isize - self.frame.y) / 0x200, - 1, - (npc.hit_bounds.top + npc.hit_bounds.bottom) as isize / 0x200), - [0.0, if npc.flags.hit_right_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?; + // todo faster way to draw dynamic rectangles + // // top + // state.texture_set.draw_rect(Rect::new_size((npc.x - npc.hit_bounds.right as isize - self.frame.x) / 0x200, + // (npc.y - npc.hit_bounds.top as isize - self.frame.y) / 0x200, + // (npc.hit_bounds.right + npc.hit_bounds.right) as isize / 0x200, + // 1), + // [0.0, if npc.flags.hit_top_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?; + // // bottom + // state.texture_set.draw_rect(Rect::new_size((npc.x - npc.hit_bounds.right as isize - self.frame.x) / 0x200, + // (npc.y + npc.hit_bounds.bottom as isize - self.frame.y) / 0x200 - 1, + // (npc.hit_bounds.right + npc.hit_bounds.right) as isize / 0x200, + // 1), + // [0.0, if npc.flags.hit_bottom_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?; + // // left + // state.texture_set.draw_rect(Rect::new_size((npc.x - npc.hit_bounds.right as isize - self.frame.x) / 0x200, + // (npc.y - npc.hit_bounds.top as isize - self.frame.y) / 0x200, + // 1, + // (npc.hit_bounds.top + npc.hit_bounds.bottom) as isize / 0x200), + // [0.0, if npc.flags.hit_left_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?; + // // right + // state.texture_set.draw_rect(Rect::new_size((npc.x + npc.hit_bounds.right as isize - self.frame.x) / 0x200 - 1, + // (npc.y - npc.hit_bounds.top as isize - self.frame.y) / 0x200, + // 1, + // (npc.hit_bounds.top + npc.hit_bounds.bottom) as isize / 0x200), + // [0.0, if npc.flags.hit_right_wall() { 1.0 } else { 0.0 }, 1.0, 1.0], ctx)?; { let hit_rect_size = clamp(npc.hit_rect_size(), 1, 4); @@ -1108,7 +1116,7 @@ impl GameScene { batch.add_rect(((npc.x - self.frame.x) / 0x200) as f32 - 3.0, ((npc.y - self.frame.y) / 0x200) as f32 - 3.0, - &caret_rect); + &caret2_rect); batch.draw(ctx)?; } diff --git a/src/shared_game_state.rs b/src/shared_game_state.rs index a4166bd..4a9805a 100644 --- a/src/shared_game_state.rs +++ b/src/shared_game_state.rs @@ -50,6 +50,7 @@ impl TimingMode { pub struct Settings { pub god_mode: bool, + pub infinite_booster: bool, pub speed: f64, pub original_textures: bool, pub lighting_efects: bool, @@ -143,6 +144,7 @@ impl SharedGameState { sound_manager: SoundManager::new(ctx)?, settings: Settings { god_mode: false, + infinite_booster: false, speed: 1.0, original_textures: false, lighting_efects: true, diff --git a/src/text_script.rs b/src/text_script.rs index 1b04867..526d144 100644 --- a/src/text_script.rs +++ b/src/text_script.rs @@ -22,12 +22,12 @@ use crate::ggez::{Context, GameResult}; use crate::ggez::GameError::ParseError; use crate::npc::NPCMap; use crate::player::ControlMode; +use crate::profile::GameProfile; use crate::scene::game_scene::GameScene; use crate::scene::title_scene::TitleScene; use crate::shared_game_state::SharedGameState; use crate::str; use crate::weapon::WeaponType; -use crate::profile::GameProfile; /// Engine's text script VM operation codes. #[derive(EnumString, Debug, FromPrimitive, PartialEq)] @@ -1121,7 +1121,8 @@ impl TextScriptVM { OpCode::ANP => { let event_num = read_cur_varint(&mut cursor)? as u16; let action_num = read_cur_varint(&mut cursor)? as u16; - let direction = read_cur_varint(&mut cursor)? as usize; + let tsc_direction = read_cur_varint(&mut cursor)? as usize; + let direction = Direction::from_int_facing(tsc_direction).unwrap_or(Direction::Left); for npc_id in game_scene.npc_map.npc_ids.iter() { if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) { @@ -1129,15 +1130,16 @@ impl TextScriptVM { if npc.cond.alive() && npc.event_num == event_num { npc.action_num = action_num; + npc.tsc_direction = tsc_direction as u16; - if direction == 4 { + if direction == Direction::FacingPlayer { npc.direction = if game_scene.player.x < npc.x { Direction::Right } else { Direction::Left }; - } else if let Some(dir) = Direction::from_int(direction) { - npc.direction = dir; + } else { + npc.direction = direction; } } } @@ -1148,8 +1150,8 @@ impl TextScriptVM { OpCode::CNP | OpCode::INP => { let event_num = read_cur_varint(&mut cursor)? as u16; let new_type = read_cur_varint(&mut cursor)? as u16; - let direction = Direction::from_int_facing(read_cur_varint(&mut cursor)? as usize) - .unwrap_or(Direction::Left); + let tsc_direction = read_cur_varint(&mut cursor)? as usize; + let direction = Direction::from_int_facing(tsc_direction).unwrap_or(Direction::Left); for npc_id in game_scene.npc_map.npc_ids.iter() { if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) { @@ -1187,6 +1189,7 @@ impl TextScriptVM { npc.anim_counter = 0; npc.vel_x = 0; npc.vel_y = 0; + npc.tsc_direction = tsc_direction as u16; if direction == Direction::FacingPlayer { npc.direction = if game_scene.player.x < npc.x { @@ -1209,8 +1212,8 @@ impl TextScriptVM { let event_num = read_cur_varint(&mut cursor)? as u16; let x = read_cur_varint(&mut cursor)? as isize; let y = read_cur_varint(&mut cursor)? as isize; - let direction = Direction::from_int_facing(read_cur_varint(&mut cursor)? as usize) - .unwrap_or(Direction::Left); + let tsc_direction = read_cur_varint(&mut cursor)? as usize; + let direction = Direction::from_int_facing(tsc_direction).unwrap_or(Direction::Left); for npc_id in game_scene.npc_map.npc_ids.iter() { if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) { @@ -1219,6 +1222,7 @@ impl TextScriptVM { if npc.cond.alive() && npc.event_num == event_num { npc.x = x * 16 * 0x200; npc.y = y * 16 * 0x200; + npc.tsc_direction = tsc_direction as u16; if direction == Direction::FacingPlayer { npc.direction = if game_scene.player.x < npc.x { @@ -1240,14 +1244,15 @@ impl TextScriptVM { OpCode::SNP => { let npc_type = read_cur_varint(&mut cursor)? as u16; let x = read_cur_varint(&mut cursor)? as isize; - let y = read_cur_varint(&mut cursor)?as isize ; - let direction = Direction::from_int_facing(read_cur_varint(&mut cursor)? as usize) - .unwrap_or(Direction::Left); + let y = read_cur_varint(&mut cursor)? as isize; + let tsc_direction = read_cur_varint(&mut cursor)? as usize; + let direction = Direction::from_int_facing(tsc_direction).unwrap_or(Direction::Left); let mut npc = NPCMap::create_npc(npc_type, &state.npc_table); npc.cond.set_alive(true); npc.x = x * 16 * 0x200; npc.y = y * 16 * 0x200; + npc.tsc_direction = tsc_direction as u16; if direction == Direction::FacingPlayer { npc.direction = if game_scene.player.x < npc.x {