mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-03-24 02:49:21 +00:00
add various NPCs and fixes
This commit is contained in:
parent
057818cfda
commit
8d0ad30e09
|
@ -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! {
|
||||
|
|
|
@ -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![
|
||||
|
|
|
@ -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 }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
160
src/npc/quote.rs
160
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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
use crate::npc::NPC;
|
||||
|
||||
impl NPC {
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)?;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue