Accuracy fixes for Sand Zone and Labyrinth (#215)

* Accuracy fixes for Sand Zone and Labyrinth
And a couple of smaller things as well

* Remove NPCList::remove_by_type (kill_npcs_by_type does the same thing)
Also rename remove_by_event to kill_npcs_by_event for consistency
This commit is contained in:
periwinkle 2023-06-22 10:26:51 -04:00 committed by GitHub
parent b294f65656
commit f6caffd624
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 198 additions and 214 deletions

View File

@ -452,9 +452,8 @@ impl NPC {
self.vel_y = -0x800;
self.npc_flags.set_ignore_solidity(true);
for npc in npc_list.iter_alive().filter(|npc| npc.npc_type == 117 || npc.npc_type == 150) {
npc.cond.set_alive(false)
}
npc_list.kill_npcs_by_type(150, false, state);
npc_list.kill_npcs_by_type(117, false, state);
let mut npc = NPC::create(355, &state.npc_table);
npc.cond.set_alive(true);

View File

@ -1160,7 +1160,7 @@ impl NPC {
self.action_counter += 1;
if self.action_counter > 250 {
self.action_num = 22;
npc_list.remove_by_type(270, state);
npc_list.kill_npcs_by_type(270, false, state);
}
}
_ => (),

View File

@ -399,12 +399,12 @@ impl NPC {
0 | 1 => {
if self.action_num == 0 {
let deg = self.rng.range(0..255) as f64 * CDEG_RAD;
self.vel_y = (deg.cos() * -512.0) as i32;
self.target_x = self.x + 8 * ((deg + 64.0 * CDEG_RAD).cos() * -512.0) as i32;
self.vel_x = (deg.cos() * 512.0) as i32;
self.target_x = self.x + 8 * ((deg + 64.0 * CDEG_RAD).cos() * 512.0) as i32;
let deg = self.rng.range(0..255) as f64 * CDEG_RAD;
self.vel_y = (deg.sin() * -512.0) as i32;
self.target_y = self.y + 8 * ((deg + 64.0 * CDEG_RAD).sin() * -512.0) as i32;
self.vel_y = (deg.sin() * 512.0) as i32;
self.target_y = self.y + 8 * ((deg + 64.0 * CDEG_RAD).sin() * 512.0) as i32;
self.action_num = 1;
self.action_counter3 = 120;
@ -886,9 +886,9 @@ impl NPC {
state.sound_manager.play_sfx(25);
}
self.vel_y += 0x10;
self.x += self.vel_x;
self.y += self.vel_y;
self.vel_y += 0x10;
if self.action_counter != 0 && self.flags.hit_bottom_wall() {
state.sound_manager.play_sfx(35);
@ -915,6 +915,9 @@ impl NPC {
players: [&mut Player; 2],
npc_list: &NPCList,
) -> GameResult {
let player = self.get_closest_player_mut(players);
self.face_player(player);
if self.action_num == 0 {
self.action_num = 1;
self.action_counter = self.rng.range(0..50) as u16;
@ -937,14 +940,6 @@ impl NPC {
self.vel_y = self.vel_y.clamp(-0x200, 0x200);
self.y += self.vel_y;
let player = self.get_closest_player_mut(players);
if self.x <= player.x {
self.direction = Direction::Right;
} else {
self.direction = Direction::Left;
}
if self.direction != Direction::Left {
if player.y < self.y + 0xA000
&& player.y > self.y - 0xA000
@ -1048,12 +1043,12 @@ impl NPC {
self.action_num = 25;
self.action_counter = 0;
self.anim_num = 2;
self.vel_y = -0x5ff;
self.vel_y = -0x600;
if self.x >= self.target_x {
self.vel_x = -0x100;
self.vel_x = -0x80;
} else {
self.vel_x = 0x100;
self.vel_x = 0x80;
}
} else {
state.sound_manager.play_sfx(30);
@ -1151,6 +1146,10 @@ impl NPC {
self.action_num = 2;
}
// So we're going to move *before* checking collisions, huh?
self.x += self.vel_x;
self.y += self.vel_y;
let mut hit = false;
if self.flags.hit_left_wall() {
@ -1178,6 +1177,8 @@ impl NPC {
}
2 => {
self.vel_y += 0x40;
self.x += self.vel_x;
self.y += self.vel_y;
if self.flags.hit_bottom_wall() {
self.action_counter2 += 1;
@ -1190,9 +1191,6 @@ impl NPC {
_ => (),
}
self.x += self.vel_x;
self.y += self.vel_y;
self.vel_y = self.vel_y.clamp(-0x5ff, 0x5ff);
self.anim_num += 1;
@ -1277,9 +1275,7 @@ impl NPC {
self.action_num = 2;
self.action_counter = 0;
}
}
if self.action_num == 2 {
} else if self.action_num == 2 {
self.animate(3, 0, 1);
self.action_counter += 1;
@ -1415,7 +1411,7 @@ impl NPC {
for _ in 0..4 {
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + 0x2000 + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + 0x2000;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
@ -1524,9 +1520,7 @@ impl NPC {
self.action_num = 2;
self.vel_y = 0x300;
}
}
if self.action_num == 2 {
} else if self.action_num == 2 {
let player = self.get_closest_player_mut(players);
self.action_counter3 += 4;
@ -1559,24 +1553,22 @@ impl NPC {
self.action_counter3 = self.tsc_direction;
}
let player = self.get_closest_player_mut(players);
if self.action_num == 1 {
if let Some(parent) = self.get_parent_ref_mut(npc_list) {
if parent.npc_type == 187 && parent.cond.alive() {
let deg = (self.action_counter3.wrapping_add(parent.action_counter3) & 0xff) as f64 * CDEG_RAD;
self.x = parent.x + 20 * (deg.sin() * -512.0) as i32;
self.y = parent.y + 32 * (deg.cos() * -512.0) as i32;
self.x = parent.x + 20 * (deg.sin() * 512.0) as i32;
self.y = parent.y + 32 * (deg.cos() * 512.0) as i32;
} else {
self.vel_x = self.rng.range(-512..512);
self.vel_y = self.rng.range(-512..512);
self.action_num = 10;
}
}
}
let player = self.get_closest_player_mut(players);
if self.action_num == 10 {
} else if self.action_num == 10 {
self.vel_x += if player.x >= self.x { 0x20 } else { -0x20 };
self.vel_y += if player.y >= self.y { 0x20 } else { -0x20 };

View File

@ -92,11 +92,8 @@ impl NPC {
}
pub(crate) fn tick_n013_forcefield(&mut self, state: &mut SharedGameState) -> GameResult {
self.anim_counter = (self.anim_counter + 1) % 2;
if self.anim_counter == 1 {
self.anim_num = (self.anim_num + 1) % 4;
self.anim_rect = state.constants.npc.n013_forcefield[self.anim_num as usize];
}
self.anim_num = (self.anim_num + 1) % 4;
self.anim_rect = state.constants.npc.n013_forcefield[self.anim_num as usize];
Ok(())
}

View File

@ -662,7 +662,7 @@ impl NPC {
self.vel_x = 0;
self.vel_y = 0;
npc_list.remove_by_type(252, state);
npc_list.kill_npcs_by_type(252, true, state);
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);

View File

@ -13,15 +13,15 @@ use crate::util::rng::RNG;
impl NPC {
pub(crate) fn tick_n044_polish(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
match self.action_num {
0 | 1 => {
self.anim_num = 0;
self.action_num = match self.direction {
Direction::Left => 8,
Direction::Right => 2,
_ => 8,
};
}
2 => {
0 | 1 | 2 => {
if self.action_num <= 1 {
self.anim_num = 0;
self.action_num = match self.direction {
Direction::Left => 8,
Direction::Right => 2,
_ => 8,
};
}
self.vel_y += 0x20;
if self.vel_y > 0 && self.flags.hit_bottom_wall() {
self.vel_y = -0x100;
@ -575,7 +575,7 @@ impl NPC {
let parent = parent.unwrap();
let angle = self.vel_x + parent.vel_y2;
let angle = (self.vel_x + parent.vel_y2) & 0xFF;
if self.action_num < 2 {
if self.action_num == 0 {
@ -773,22 +773,25 @@ impl NPC {
self.anim_counter = self.rng.range(0..4) as u16;
self.action_counter2 = 120;
let mut angle = self.rng.range(0..255);
let angle = self.rng.range(0..255);
self.vel_x = ((angle as f64 * CDEG_RAD).cos() * -512.0) as i32;
angle += 0x40;
self.target_x = self.x + 8 * ((angle as f64 * CDEG_RAD).cos() * -512.0) as i32;
self.vel_y = ((angle as f64 * CDEG_RAD).sin() * -512.0) as i32;
angle += 0x40;
self.target_y = self.y + 8 * ((angle as f64 * CDEG_RAD).sin() * -512.0) as i32;
self.vel_x = ((angle as f64 * CDEG_RAD).cos() * 512.0) as i32;
self.target_x = self.x + 8 * (((angle + 0x40) as f64 * CDEG_RAD).cos() * 512.0) as i32;
let angle = self.rng.range(0..255);
self.vel_y = ((angle as f64 * CDEG_RAD).sin() * 512.0) as i32;
self.target_y = self.y + 8 * (((angle + 0x40) as f64 * CDEG_RAD).sin() * 512.0) as i32;
}
let player = self.get_closest_player_mut(players);
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
self.vel_x += ((self.target_x - self.x).signum() * 0x10).clamp(-0x200, 0x200);
self.vel_y += ((self.target_y - self.y).signum() * 0x10).clamp(-0x200, 0x200);
self.vel_x += (self.target_x - self.x).signum() * 0x10;
self.vel_y += (self.target_y - self.y).signum() * 0x10;
self.vel_x = clamp(self.vel_x, -0x200, 0x200);
self.vel_y = clamp(self.vel_y, -0x200, 0x200);
if self.shock != 0 {
self.action_num = 2;
@ -802,7 +805,7 @@ impl NPC {
let player = self.get_closest_player_mut(players);
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
self.vel_x += if self.y <= player.y + 0x4000 {
self.vel_x += if self.y <= player.y + 0x6000 {
(player.x - self.x).signum() * 0x10
} else {
(self.x - player.x).signum() * 0x10
@ -1020,7 +1023,7 @@ impl NPC {
}
self.vel_y += 32;
self.vel_x = self.vel_x.clamp(-0x200, 0x200);
self.vel_x = self.vel_x.clamp(-0x1FF, 0x1FF);
self.clamp_fall_speed();
self.y += self.vel_y;
@ -1223,23 +1226,24 @@ impl NPC {
}
}
}
2 => {
self.action_counter += 1;
if self.action_counter > 8 {
self.action_num = 1;
self.anim_num = 0;
}
}
_ => (),
}
self.action_counter += 1;
if self.action_counter > 8 {
self.action_num = 1;
self.anim_num = 0;
}
self.vel_y += 0x40;
self.clamp_fall_speed();
self.x += self.vel_x;
self.y += self.vel_y;
let anim = if self.direction == Direction::Left { 0 } else { 4 };
let dir_offset = if self.direction == Direction::Left { 0 } else { 4 };
self.anim_rect = state.constants.npc.n130_puppy_sitting[anim];
self.anim_rect = state.constants.npc.n130_puppy_sitting[self.anim_num as usize + dir_offset];
Ok(())
}
@ -1263,6 +1267,7 @@ impl NPC {
state: &mut SharedGameState,
players: [&mut Player; 2],
) -> GameResult {
let player = self.get_closest_player_mut(players);
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
@ -1277,13 +1282,16 @@ impl NPC {
self.anim_num = 1;
}
let player = self.get_closest_player_mut(players);
if (self.x - player.x).abs() < 0x8000 && (self.y - player.y).abs() < 0x2000 {
self.animate(4, 2, 4);
if self.anim_num == 4 && self.anim_counter == 0 {
state.sound_manager.play_sfx(105);
}
} else {
if self.anim_num == 4 {
self.anim_num = 2;
}
}
}
2 => {
@ -1343,6 +1351,10 @@ impl NPC {
_ => (),
}
if self.action_num < 100 {
self.face_player(player);
}
self.vel_y += 0x40;
self.clamp_fall_speed();

View File

@ -590,88 +590,89 @@ impl NPC {
players: [&mut Player; 2],
npc_list: &NPCList,
) -> GameResult {
if self.action_num != 1 {
if 1 < self.action_num {
if self.action_num == 10 {
if (self.flags.0 & 0xf) == 0 {
self.x += self.vel_x;
self.y += self.vel_y;
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.action_counter = 0;
}
let parent = self.get_parent_ref_mut(npc_list);
if let Some(parent) = parent {
let player = self.get_closest_player_mut(players);
if parent.direction == Direction::Left {
self.x = parent.x + 0x1400;
} else {
self.action_num = 0x14;
self.action_counter = 0;
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
state.sound_manager.play_sfx(0xc);
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
for _ in 0..4 {
npc.x = self.x;
npc.y = self.y;
npc.vel_x = self.rng.range(-0x200..0x200);
npc.vel_y = self.rng.range(-0x200..0x200);
let _ = npc_list.spawn(0x100, npc.clone());
}
self.x = parent.x + -0x1400;
}
} else if self.action_num == 0x14 {
self.y = parent.y + -0x1000;
if (parent.action_num == 0x18) || (parent.action_num == 0x34) {
self.action_num = 10;
if parent.direction == Direction::Left {
self.x = parent.x + -0x2000;
} else {
self.x = parent.x + 0x2000;
}
self.y = parent.y;
let angle = f64::atan2((self.y - player.y) as f64, (self.x - player.x) as f64);
self.vel_x = (angle.cos() * -2048.0) as i32;
self.vel_y = (angle.sin() * -2048.0) as i32;
state.sound_manager.play_sfx(0x27);
}
}
}
10 => {
if (self.flags.0 & 0xf) == 0 {
self.x += self.vel_x;
self.y += self.vel_y;
self.action_counter += 1;
if 4 < self.action_counter {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
for _ in 0..4 {
npc.x = self.x;
npc.y = self.y;
npc.vel_x = self.rng.range(-0x200..0x200);
npc.vel_y = self.rng.range(-0x200..0x200);
let _ = npc_list.spawn(0x100, npc.clone());
}
self.npc_type = 0x8e;
self.anim_num = 0;
self.action_num = 0x14;
self.vel_x = 0;
self.npc_flags.set_invulnerable(false);
self.npc_flags.set_shootable(true);
self.damage = 1;
}
}
}
if self.action_num == 0 {
self.action_num = 1;
self.action_counter = 0;
}
} else {
let parent = self.get_parent_ref_mut(npc_list);
if let Some(parent) = parent {
let player = self.get_closest_player_mut(players);
if parent.direction == Direction::Left {
self.x = parent.x + 0x1400;
} else {
self.x = parent.x + -0x1400;
}
self.action_num = 0x14;
self.action_counter = 0;
self.y = parent.y + -0x1000;
if (parent.action_num == 0x18) || (parent.action_num == 0x34) {
self.action_num = 10;
if parent.direction == Direction::Left {
self.x = parent.x + -0x2000;
} else {
self.x = parent.x + 0x2000;
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
state.sound_manager.play_sfx(0xc);
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
for _ in 0..4 {
npc.x = self.x;
npc.y = self.y;
npc.vel_x = self.rng.range(-0x200..0x200);
npc.vel_y = self.rng.range(-0x200..0x200);
let _ = npc_list.spawn(0x100, npc.clone());
}
self.y = parent.y;
let angle = f64::atan2((self.y - player.y) as f64, (self.x - player.x) as f64);
self.vel_x = (angle.cos() * -2048.0) as i32;
self.vel_y = (angle.sin() * -2048.0) as i32;
state.sound_manager.play_sfx(0x27);
}
}
20 => {
self.x += self.vel_x;
self.y += self.vel_y;
self.action_counter += 1;
if 4 < self.action_counter {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
for _ in 0..4 {
npc.x = self.x;
npc.y = self.y;
npc.vel_x = self.rng.range(-0x200..0x200);
npc.vel_y = self.rng.range(-0x200..0x200);
let _ = npc_list.spawn(0x100, npc.clone());
}
self.npc_type = 0x8e;
self.anim_num = 0;
self.action_num = 0x14;
self.vel_x = 0;
self.npc_flags.set_invulnerable(false);
self.npc_flags.set_shootable(true);
self.damage = 1;
}
}
_ => (),
}
self.anim_num += 1;

View File

@ -1754,7 +1754,7 @@ impl BossNPC {
self.parts[0].action_counter = 0;
self.parts[0].vel_x = 0;
self.parts[0].vel_y = 0;
npc_list.kill_npcs_by_type(339, true, state);
npc_list.kill_npcs_by_type(339, false, state);
}
self.parts[0].y += (0x13E00 - self.parts[0].y) / 8;

View File

@ -180,6 +180,10 @@ impl BossNPC {
self.parts[7].x = self.parts[0].x - 0x6000;
self.parts[7].y = self.parts[0].y + 0x4000;
for i in [2,3,6,7] {
self.hurt_sound[i] = 54;
}
for part in &mut self.parts {
part.prev_x = part.x;
part.prev_y = part.y;

View File

@ -81,6 +81,8 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager, &mut Fl
),
) -> GameResult {
if !self.parts[0].cond.alive() {
// Kind of hacky but fixes Monster X's damage popup being stuck on screen
self.parts[0].popup.tick(state, ())?;
return Ok(());
}

View File

@ -180,11 +180,9 @@ impl BossNPC {
self.parts[3].hit_bounds =
Rect { left: 5 * 0x200, top: 5 * 0x200, right: 5 * 0x200, bottom: 5 * 0x200 };
self.parts[3].npc_flags.set_ignore_solidity(true);
self.hurt_sound[3] = 54;
self.death_sound[3] = 71;
self.parts[4] = self.parts[3].clone();
self.parts[3].target_x = 1;
self.parts[4].target_x = 1;
self.parts[5] = self.parts[3].clone();
self.parts[6] = self.parts[3].clone();
@ -193,6 +191,11 @@ impl BossNPC {
self.parts[5].life = 100;
self.parts[6].life = 100;
for i in 3..7 {
self.hurt_sound[i] = 54;
self.death_sound[i] = 71;
}
self.parts[7].cond.set_alive(true);
self.parts[7].x = self.parts[0].x;
self.parts[7].y = self.parts[0].y;
@ -202,6 +205,7 @@ impl BossNPC {
Rect { left: 52 * 0x200, top: 24 * 0x200, right: 52 * 0x200, bottom: 24 * 0x200 };
self.parts[7].hit_bounds = Rect { left: 0x1000, top: 24 * 0x200, right: 0x1000, bottom: 0x2000 };
self.parts[7].npc_flags.set_ignore_solidity(true);
self.hurt_sound[7] = 52;
self.parts[9].cond.set_alive(true);
self.parts[9].x = self.parts[0].x - 64 * 0x200;
@ -264,8 +268,6 @@ impl BossNPC {
self.parts[0].action_num = 11;
self.parts[0].action_counter = 0;
self.parts[0].action_counter2 = 0;
self.parts[7].action_num = 2;
}
self.parts[0].action_counter += 1;
@ -466,11 +468,9 @@ impl BossNPC {
state.sound_manager.play_sfx(52);
}
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.parts[0].x + self.parts[0].rng.range(-72..72) as i32 * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-64..64) as i32 * 0x200;
let _ = npc_list.spawn(0x100, npc);
let x = self.parts[0].x + self.parts[0].rng.range(-72..72) as i32 * 0x200;
let y = self.parts[0].y + self.parts[0].rng.range(-64..64) as i32 * 0x200;
npc_list.create_death_smoke(x, y, 1, 1, state, &self.parts[0].rng);
if self.parts[0].action_counter > 100 {
self.parts[0].action_num = 1001;
@ -489,16 +489,12 @@ impl BossNPC {
part.cond.set_alive(false);
}
for npc in npc_list.iter_alive() {
if npc.npc_type == 158 {
npc.cond.set_alive(false);
}
}
npc_list.kill_npcs_by_type(158, true, state);
let mut npc = NPC::create(159, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.parts[0].x;
npc.y = self.parts[1].y - 24 * 0x200;
npc.y = self.parts[0].y - 24 * 0x200;
let _ = npc_list.spawn(0, npc);
}
@ -552,9 +548,6 @@ impl BossNPC {
match self.parts[i].action_num {
0 => {
self.parts[0].npc_flags.set_shootable(false);
self.parts[i].anim_num = 2;
}
2 => {
self.parts[i].anim_num = 0;
}
10 | 11 => {
@ -565,7 +558,7 @@ impl BossNPC {
}
if self.parts[0].shock > 0 {
self.parts[i].action_counter2 += 1;
self.parts[i].action_counter2 = self.parts[i].action_counter2.wrapping_add(1);
if self.parts[i].action_counter2 / 2 % 2 != 0 {
self.parts[i].anim_num = 1;
} else {
@ -575,16 +568,16 @@ impl BossNPC {
self.parts[i].anim_num = 0;
}
}
40 => {
self.parts[0].npc_flags.set_shootable(false);
self.parts[i].anim_num = 0;
}
_ => {}
}
self.parts[i].x = self.parts[0].x;
self.parts[i].y = self.parts[0].y;
if self.parts[0].action_num <= 10 {
self.parts[i].anim_num = 2;
}
self.parts[i].anim_rect = state.constants.npc.b03_monster_x[self.parts[i].anim_num as usize];
}
@ -812,7 +805,7 @@ impl BossNPC {
if self.parts[i].target_x < 0 {
self.parts[i].target_x = 0;
self.parts[i].action_num = 0;
self.parts[7].action_num = 40;
self.parts[7].action_num = 0;
self.parts[13].action_num = 0;
self.parts[14].action_num = 0;
self.parts[15].action_num = 0;

View File

@ -75,6 +75,7 @@ impl BossNPC {
self.parts[0].display_bounds =
Rect { left: 40 * 0x200, top: 40 * 0x200, right: 40 * 0x200, bottom: 0x2000 };
self.parts[0].hit_bounds = Rect { left: 0x1000, top: 24 * 0x200, right: 0x1000, bottom: 0x2000 };
self.hurt_sound[0] = 52;
self.parts[1].cond.set_alive(true);
self.parts[1].display_bounds =
@ -100,6 +101,7 @@ impl BossNPC {
self.parts[4].display_bounds = self.parts[3].display_bounds;
self.parts[4].hit_bounds = self.parts[3].hit_bounds;
self.parts[4].npc_flags = self.parts[3].npc_flags;
self.parts[4].x = self.parts[0].x - 0x2000;
self.parts[4].y = self.parts[3].y;
self.parts[4].direction = Direction::Right;
self.hurt_sound[4] = 52;
@ -166,7 +168,7 @@ impl BossNPC {
}
60 => {
self.parts[0].action_counter += 1;
if self.parts[0].action_counter % 3 == 0 && (20..80).contains(&self.parts[0].action_counter) {
if self.parts[0].action_counter % 3 == 0 && (21..80).contains(&self.parts[0].action_counter) {
let mut npc = NPC::create(48, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.parts[0].x;
@ -195,6 +197,8 @@ impl BossNPC {
match self.parts[0].anim_num {
1 => self.parts[0].damage = 20,
0 => {
state.sound_manager.stop_sfx(102);
state.sound_manager.play_sfx(12);
self.parts[0].action_num = 80;
self.parts[0].action_counter = 0;
self.parts[0].npc_flags.set_shootable(false);
@ -297,7 +301,7 @@ impl BossNPC {
state.sound_manager.play_sfx(12);
state.sound_manager.play_sfx(25);
state.sound_manager.play_sfx(102);
state.sound_manager.stop_sfx(102);
}
1 => {
self.parts[0].damage = 20;
@ -427,13 +431,9 @@ impl BossNPC {
self.parts[0].action_num = 150;
self.parts[0].action_counter = 0;
self.parts[0].damage = 0;
self.parts[5].damage = 5;
self.parts[5].damage = 0;
for npc in npc_list.iter_alive() {
if npc.npc_type == 48 {
npc.cond.set_alive(false);
}
}
npc_list.kill_npcs_by_type(48, true, state);
}
}
}

View File

@ -225,7 +225,7 @@ impl BossNPC {
1020 => {
self.parts[0].action_counter += 1;
if self.parts[0].action_counter > 50 {
npc_list.remove_by_type(211, state);
npc_list.kill_npcs_by_type(211, true, state);
self.parts[0].cond.set_alive(false);
self.parts[1].cond.set_alive(false);

View File

@ -266,13 +266,13 @@ impl NPCList {
match npc.size {
1 => {
self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 3, state, &npc.rng);
self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 4, state, &npc.rng);
}
2 => {
self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 7, state, &npc.rng);
self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 8, state, &npc.rng);
}
3 => {
self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 12, state, &npc.rng);
self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 16, state, &npc.rng);
}
_ => {}
};
@ -348,7 +348,7 @@ impl NPCList {
}
/// Removes NPCs whose event number matches the provided one.
pub fn remove_by_event(&self, event_num: u16, state: &mut SharedGameState) {
pub fn kill_npcs_by_event(&self, event_num: u16, state: &mut SharedGameState) {
for npc in self.iter_alive() {
if npc.event_num == event_num {
npc.cond.set_alive(false);
@ -357,23 +357,6 @@ impl NPCList {
}
}
/// Removes NPCs (and creates a smoke effect) whose type IDs match the provided one.
pub fn remove_by_type(&self, npc_type: u16, state: &mut SharedGameState) {
for npc in self.iter_alive() {
if npc.npc_type == npc_type {
npc.cond.set_alive(false);
state.set_flag(npc.flag_num as usize, true);
match npc.size {
1 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 4, state, &npc.rng),
2 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 8, state, &npc.rng),
3 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 16, state, &npc.rng),
_ => {}
};
}
}
}
/// Creates NPC death smoke diffusing in random directions.
#[inline]
pub fn create_death_smoke(

View File

@ -262,19 +262,20 @@ impl Player {
}
if state.control_flags.control_enabled() {
let trigger_only_down = self.controller.trigger_down()
&& !self.controller.trigger_up()
&& !self.controller.trigger_left()
&& !self.controller.trigger_right()
&& !self.controller.trigger_shoot();
let only_down = self.controller.move_down()
&& !self.controller.move_up()
&& !self.controller.move_left()
&& !self.controller.move_right()
&& !self.controller.shoot();
&& !self.controller.shoot()
&& !self.controller.jump()
&& !self.controller.prev_weapon()
&& !self.controller.next_weapon()
&& !self.controller.map()
&& !self.controller.inventory()
&& !self.controller.strafe();
// Leaving the skip button unchecked as a "feature" :)
if trigger_only_down
if self.controller.trigger_down()
&& only_down
&& !self.cond.interacted()
&& !state.control_flags.interactions_disabled()

View File

@ -1369,14 +1369,14 @@ impl TextScriptVM {
TSCOpCode::DNP => {
let event_num = read_cur_varint(&mut cursor)? as u16;
game_scene.npc_list.remove_by_event(event_num, state);
game_scene.npc_list.kill_npcs_by_event(event_num, state);
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
TSCOpCode::DNA => {
let npc_remove_type = read_cur_varint(&mut cursor)? as u16;
game_scene.npc_list.remove_by_type(npc_remove_type, state);
game_scene.npc_list.kill_npcs_by_type(npc_remove_type, true, state);
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}

View File

@ -1953,8 +1953,8 @@ impl PhysicalEntity for Bullet {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
npc.direction = Direction::Left;
npc.x = (x * 16 + 8) * 0x200;
npc.y = (y * 16 + 8) * 0x200;
npc.x = (x + ox) * tile_size;
npc.y = (y + oy) * tile_size;
for _ in 0..4 {
npc.vel_x = npc.rng.range(-0x200..0x200) as i32;