1
0
Fork 0
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:
Alula 2020-11-01 20:05:29 +01:00
parent 057818cfda
commit 8d0ad30e09
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
16 changed files with 1052 additions and 67 deletions

View file

@ -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! {

View file

@ -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![

View file

@ -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 }
_ => {}
}
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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(())
}
}

View file

@ -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(())
}
}

View file

@ -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;

View file

@ -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,

View file

@ -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(())
}
}

View file

@ -0,0 +1,5 @@
use crate::npc::NPC;
impl NPC {
}

View file

@ -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

View file

@ -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)?;
}

View file

@ -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,

View file

@ -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 {