1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2024-10-31 19:44:20 +00:00

commit stuff from last 2 months idfk what I've exactly changed

This commit is contained in:
Alula 2021-10-08 04:41:31 +02:00
parent 2cb9335453
commit e018b53b04
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
51 changed files with 2752 additions and 362 deletions

View file

@ -69,13 +69,13 @@ lua-ffi = { git = "https://github.com/doukutsu-rs/lua-ffi.git", rev = "e0b2ff596
num-derive = "0.3.2"
num-traits = "0.2.12"
paste = "1.0.0"
pretty_env_logger = "0.4.0"
sdl2 = { version = "=0.34.2", optional = true, features = ["unsafe_textures", "bundled", "static-link"] }
sdl2-sys = { version = "=0.34.2", optional = true, features = ["bundled", "static-link"] }
serde = { version = "1", features = ["derive"] }
serde_derive = "1"
serde_json = "1.0"
serde_yaml = "0.8"
simple_logger = { version = "1.13" }
strum = "0.20"
strum_macros = "0.20"
# remove and replace when drain_filter is in stable

View file

@ -90,6 +90,21 @@ impl BMFontRenderer {
self.draw_colored_text(iter, x, y, (255, 255, 255, 255), constants, texture_set, ctx)
}
pub fn draw_colored_text_with_shadow_scaled<I: Iterator<Item = char> + Clone>(
&self,
iter: I,
x: f32,
y: f32,
scale: f32,
color: (u8, u8, u8, u8),
constants: &EngineConstants,
texture_set: &mut TextureSet,
ctx: &mut Context,
) -> GameResult {
self.draw_colored_text_scaled(iter.clone(), x + scale, y + scale, scale, (0, 0, 0, 150), constants, texture_set, ctx)?;
self.draw_colored_text_scaled(iter, x, y, scale, color, constants, texture_set, ctx)
}
pub fn draw_colored_text_scaled<I: Iterator<Item = char>>(
&self,
iter: I,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 789 B

View file

@ -120,7 +120,6 @@ impl BuiltinFS {
]),
FSNode::Directory("lightmap", vec![
FSNode::File("spot.png", include_bytes!("builtin/lightmap/spot.png")),
FSNode::File("direct.png", include_bytes!("builtin/lightmap/direct.png")),
]),
])
],

View file

@ -9,9 +9,10 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use crate::bitfield;
use crate::texture_set::G_MAG;
/// Multiply cave story degrees (0-255, which corresponds to 0°-360°) with this to get
/// Multiply cave story degrees (0-255, which corresponds to 0°-360°) with this constant to get
/// respective value in radians.
pub const CDEG_RAD: f64 = std::f64::consts::PI / 128.0;
lazy_static! {
pub static ref VERSION_BANNER: String = {
let version = option_env!("DRS_BUILD_VERSION_OVERRIDE").unwrap_or(env!("CARGO_PKG_VERSION"));
@ -57,6 +58,10 @@ bitfield! {
}
impl Flag {
pub fn any_flag(&self) -> bool {
self.0 != 0
}
pub fn hit_anything(&self) -> bool {
(self.0 & 0x2ff) != 0
}
@ -121,7 +126,6 @@ bitfield! {
// engine specific flags
pub friendly_fire, set_friendly_fire: 14;
pub wind, set_wind: 15;
}
bitfield! {

View file

@ -585,8 +585,8 @@ pub struct NPCConsts {
#[serde(default = "default_n195_background_grate")]
pub n195_background_grate: Rect<u16>,
#[serde(default = "default_n196_ironhead_motion_wall")]
pub n196_ironhead_motion_wall: [Rect<u16>; 2],
#[serde(default = "default_n196_ironhead_wall")]
pub n196_ironhead_wall: [Rect<u16>; 2],
#[serde(default = "default_n197_porcupine_fish")]
pub n197_porcupine_fish: [Rect<u16>; 4],
@ -627,8 +627,8 @@ pub struct NPCConsts {
#[serde(default = "default_n209_basu_projectile_destroyed_egg_corridor")]
pub n209_basu_projectile_destroyed_egg_corridor: [Rect<u16>; 4],
#[serde(default = "default_n210_destroyed_egg_corridor")]
pub n210_destroyed_egg_corridor: [Rect<u16>; 4],
#[serde(default = "default_n210_beetle_destroyed_egg_corridor")]
pub n210_beetle_destroyed_egg_corridor: [Rect<u16>; 4],
#[serde(default = "default_n211_small_spikes")]
pub n211_small_spikes: [Rect<u16>; 4],
@ -1074,6 +1074,9 @@ pub struct NPCConsts {
#[serde(default = "default_b03_monster_x")]
pub b03_monster_x: [Rect<u16>; 29],
#[serde(default = "default_b04_core")]
pub b04_core: [Rect<u16>; 10],
}
fn default_n001_experience() -> [Rect<u16>; 6] {
@ -3211,7 +3214,7 @@ fn default_n195_background_grate() -> Rect<u16> {
Rect { left: 112, top: 64, right: 128, bottom: 80 }
}
fn default_n196_ironhead_motion_wall() -> [Rect<u16>; 2] {
fn default_n196_ironhead_wall() -> [Rect<u16>; 2] {
[
Rect { left: 112, top: 64, right: 144, bottom: 80 },
Rect { left: 112, top: 80, right: 144, bottom: 96 },
@ -3340,7 +3343,7 @@ fn default_n209_basu_projectile_destroyed_egg_corridor() -> [Rect<u16>; 4] {
]
}
fn default_n210_destroyed_egg_corridor() -> [Rect<u16>; 4] {
fn default_n210_beetle_destroyed_egg_corridor() -> [Rect<u16>; 4] {
[
Rect { left: 0, top: 112, right: 16, bottom: 128 },
Rect { left: 16, top: 112, right: 32, bottom: 128 },
@ -4850,3 +4853,20 @@ fn default_b03_monster_x() -> [Rect<u16>; 29] {
Rect { left: 48, top: 208, right: 64, bottom: 224 },
]
}
fn default_b04_core() -> [Rect<u16>; 10] {
[
Rect { left: 0, top: 0, right: 72, bottom: 112 }, // face
Rect { left: 0, top: 112, right: 72, bottom: 224 },
Rect { left: 160, top: 0, right: 232, bottom: 112 },
Rect { left: 0, top: 0, right: 0, bottom: 0 },
Rect { left: 72, top: 0, right: 160, bottom: 112 }, // tail
Rect { left: 72, top: 112, right: 160, bottom: 224 },
Rect { left: 0, top: 0, right: 0, bottom: 0 },
Rect { left: 256, top: 0, right: 320, bottom: 40 }, // small head
Rect { left: 256, top: 40, right: 320, bottom: 80 },
Rect { left: 256, top: 80, right: 320, bottom: 120 },
]
}

View file

@ -155,7 +155,6 @@ impl BackendEventLoop for GlutinEventLoop {
state_ref.shutdown();
}
Event::Resumed => {
println!("resumed!");
{
let mut mutex = GAME_SUSPENDED.lock().unwrap();
*mutex = false;
@ -171,7 +170,6 @@ impl BackendEventLoop for GlutinEventLoop {
}
}
Event::Suspended => {
println!("suspended!");
{
let mut mutex = GAME_SUSPENDED.lock().unwrap();
*mutex = true;

View file

@ -15,7 +15,6 @@ use std::time::Instant;
use directories::ProjectDirs;
use lazy_static::lazy_static;
use pretty_env_logger::env_logger::Env;
use crate::builtin_fs::BuiltinFS;
use crate::framework::context::Context;
@ -188,9 +187,7 @@ impl Game {
}
pub fn init() -> GameResult {
pretty_env_logger::env_logger::from_env(Env::default().default_filter_or("info"))
//.filter(Some("ndk_glue"), LevelFilter::Trace)
.init();
let _ = simple_logger::init_with_level(log::Level::Info);
#[cfg(not(target_os = "android"))]
let resource_dir = if let Ok(data_dir) = env::var("CAVESTORY_DATA_DIR") {

View file

@ -62,7 +62,7 @@ impl NPC {
self.anim_counter = 0;
}
}
_ => {}
_ => (),
}
self.vel_y = clamp(self.vel_y, -0x5ff, 0x5ff);
@ -162,7 +162,7 @@ impl NPC {
self.action_counter = 0;
}
}
_ => {}
_ => (),
}
self.vel_y += 0x20;
@ -501,13 +501,12 @@ impl NPC {
}
}
println!("y: {}", self.y as f64 / 512.0);
if self.y < -32 * 0x200 {
self.npc_type = 0;
state.quake_counter = 30;
}
}
_ => {}
_ => (),
}
if self.target_x != 0 && self.rng.range(0..10) == 0 {
@ -603,7 +602,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
self.vel_y = clamp(self.vel_y, -0x5ff, 0x5ff);
@ -793,7 +792,7 @@ impl NPC {
self.action_counter = 0;
}
}
_ => {}
_ => (),
}
if self.action_num != 5 {
@ -977,7 +976,7 @@ impl NPC {
self.action_num = 0;
}
}
_ => {}
_ => (),
}
self.vel_x = clamp(self.vel_x, -0x400, 0x400);
@ -1196,7 +1195,7 @@ impl NPC {
self.action_num = 0;
}
}
_ => {}
_ => (),
}
self.vel_y += 0x20;

View file

@ -76,7 +76,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;

View file

@ -58,7 +58,7 @@ impl NPC {
state.create_caret(self.x, self.y, CaretType::Zzz, Direction::Left);
}
}
_ => {}
_ => (),
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 7 };

View file

@ -58,7 +58,7 @@ impl NPC {
self.x += self.direction.vector_x() * 0x200;
}
5 => self.anim_num = 5,
_ => {}
_ => (),
}
self.vel_y += 0x20;
@ -225,7 +225,7 @@ impl NPC {
self.action_counter2 = 0;
}
}
_ => {}
_ => (),
}
if self.action_num < 30 || self.action_num >= 40 {
@ -302,7 +302,7 @@ impl NPC {
self.anim_rect = state.constants.npc.n062_kazuma_computer[self.anim_num as usize];
}
}
_ => {}
_ => (),
}
Ok(())
@ -342,7 +342,7 @@ impl NPC {
self.vel_x = self.direction.vector_x() * 0x200;
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -412,7 +412,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
@ -467,11 +467,103 @@ impl NPC {
self.cond.set_alive(false);
}
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n167_booster_falling[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n217_itoh(&mut self, state: &mut SharedGameState) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.anim_num = 0;
self.anim_counter = 0;
self.vel_x = 0;
}
if self.rng.range(0..120) == 10 {
self.action_num = 2;
self.action_counter = 0;
self.anim_num = 1;
}
}
2 => {
self.action_counter += 1;
if self.action_counter > 8 {
self.action_num = 1;
self.anim_num = 0;
}
}
10 => {
self.anim_num = 2;
self.vel_x = 0;
}
20 => {
self.action_num = 21;
self.anim_num = 2;
self.vel_x += 0x200;
self.vel_y -= 0x400;
}
21 => {
if self.flags.hit_bottom_wall() {
self.anim_num = 3;
self.action_num = 30;
self.action_counter = 0;
self.vel_x = 0;
self.target_x = self.x;
}
}
30 => {
self.anim_num = 3;
self.action_counter += 1;
self.x = if ((self.action_counter / 2) & 1) != 0 { self.target_x + 512 } else { self.target_x }
}
40 | 41 => {
if self.action_num == 40 {
self.action_num = 41;
self.vel_y = -512;
self.anim_num = 2;
}
if self.flags.hit_bottom_wall() {
self.action_num = 42;
self.anim_num = 4;
}
}
42 => {
self.vel_x = 0;
self.anim_num = 4;
}
50 | 51 => {
if self.action_num == 50 {
self.action_num = 51;
self.action_counter = 0;
}
self.action_counter += 1;
if self.action_counter > 32 {
self.action_num = 42;
}
self.vel_x = 512;
self.animate(3, 4, 7);
}
_ => (),
}
self.vel_y += 0x40;
if self.vel_y > 0x5ff {
self.vel_y = 0x5ff;
}
self.x += self.vel_x;
self.y += self.vel_y;
self.anim_rect = state.constants.npc.n217_itoh[self.anim_num as usize];
Ok(())
}
}

View file

@ -1,11 +1,11 @@
use num_traits::{abs, clamp};
use crate::caret::CaretType;
use crate::common::Direction;
use crate::common::{Direction, Rect};
use crate::framework::error::GameResult;
use crate::npc::list::NPCList;
use crate::npc::NPC;
use crate::player::Player;
use crate::player::{Player, TargetPlayer};
use crate::rng::RNG;
use crate::shared_game_state::SharedGameState;
use crate::weapon::bullet::BulletManager;
@ -111,7 +111,7 @@ impl NPC {
self.animate(8, 1, 4);
self.x += self.direction.vector_x() * 0x100;
}
_ => {}
_ => (),
}
if self.vel_y > 0x5ff {
@ -244,7 +244,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
if self.action_num > 10 && self.action_num < 30 && bullet_manager.count_bullets_type_idx_all(6) > 0 {
@ -349,11 +349,440 @@ impl NPC {
0
};
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n165_curly_collapsed[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n180_curly_ai(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
npc_list: &NPCList,
) -> GameResult {
let player = self.get_closest_player_ref(&players);
if self.y >= player.y - 0x14000 {
if state.npc_curly_counter > 0 {
self.target_x = state.npc_curly_target.0;
self.target_y = state.npc_curly_target.1;
} else {
self.target_x = player.x;
self.target_y = player.y;
}
} else {
self.target_x = if self.y > 0x1FFFF { 0 } else { 0x280000 };
self.target_y = self.y;
}
if (self.vel_x < 0 && self.flags.hit_left_wall()) || (self.vel_x > 0 && self.flags.hit_right_wall()) {
self.vel_x = 0;
}
match self.action_num {
20 => {
self.action_num = 100;
self.anim_num = 0;
self.x = player.x;
self.y = player.y;
let mut npc = NPC::create(183, &state.npc_table);
npc.cond.set_alive(true);
npc.parent_id = self.id;
let _ = npc_list.spawn(0x100, npc);
if !state.get_flag(563) {
let mut npc = NPC::create(181, &state.npc_table);
npc.cond.set_alive(true);
npc.parent_id = self.id;
let _ = npc_list.spawn(0x100, npc);
} else {
let mut npc = NPC::create(182, &state.npc_table);
npc.cond.set_alive(true);
npc.parent_id = self.id;
let _ = npc_list.spawn(0x100, npc);
}
}
40 | 41 => {
if self.action_num == 40 {
self.action_num = 41;
self.action_counter = 0;
self.anim_num = 10;
}
self.action_counter += 1;
if self.action_counter == 750 {
self.npc_flags.set_interactable(false);
self.anim_num = 0;
}
if self.action_counter > 1000 {
self.action_num = 100;
self.anim_num = 0;
let mut npc = NPC::create(183, &state.npc_table);
npc.cond.set_alive(true);
npc.parent_id = self.id;
let _ = npc_list.spawn(0x100, npc);
if !state.get_flag(563) {
let mut npc = NPC::create(181, &state.npc_table);
npc.cond.set_alive(true);
npc.parent_id = self.id;
let _ = npc_list.spawn(0x100, npc);
} else {
let mut npc = NPC::create(182, &state.npc_table);
npc.cond.set_alive(true);
npc.parent_id = self.id;
let _ = npc_list.spawn(0x100, npc);
}
}
}
100 => {
self.anim_num = 0;
self.vel_x = 7 * self.vel_x / 8;
self.action_counter3 = 0;
if self.x <= self.target_x + 0x2000 {
if self.x < self.target_x - 0x2000 {
self.action_num = 300;
self.anim_num = 1;
self.direction = Direction::Right;
self.action_counter = self.rng.range(20..60) as u16;
}
} else {
self.action_num = 200;
self.anim_num = 1;
self.direction = Direction::Left;
self.action_counter = self.rng.range(20..60) as u16;
}
}
200 => {
self.vel_x -= 0x20;
self.direction = Direction::Left;
if self.flags.hit_left_wall() {
self.action_counter3 += 1;
} else {
self.action_counter3 = 0;
}
}
210 => {
self.vel_x -= 0x20;
self.direction = Direction::Left;
if self.flags.hit_bottom_wall() {
self.action_num = 100;
}
}
300 => {
self.vel_x += 0x20;
self.direction = Direction::Right;
if self.flags.hit_right_wall() {
self.action_counter3 += 1;
} else {
self.action_counter3 = 0;
}
}
310 => {
self.vel_x += 0x20;
self.direction = Direction::Right;
if self.flags.hit_bottom_wall() {
self.action_num = 100;
}
}
_ => (),
}
if state.npc_curly_counter > 0 {
state.npc_curly_counter -= 1;
}
if state.npc_curly_counter == 70 {
self.action_counter2 = 10;
} else if state.npc_curly_counter == 60 && self.flags.hit_bottom_wall() && self.rng.range(0..2) != 0 {
self.action_num = if self.x <= self.target_x { 310 } else { 210 };
self.anim_num = 1;
self.action_counter3 = 0;
self.vel_y = -0x600;
state.sound_manager.play_sfx(15);
}
let mut delx = self.x - self.target_x;
let dely = self.y - self.target_y;
if delx < 0 {
delx = -delx;
}
if self.action_num == 100 {
self.anim_num = if delx + 0x400 >= dely { 0 } else { 5 }
}
if self.action_num == 210 || self.action_num == 310 {
self.anim_num = if delx + 0x400 >= dely { 1 } else { 6 }
}
if self.action_num == 200 || self.action_num == 300 {
self.anim_counter += 1;
self.anim_num = (self.anim_counter / 4 % 4) + if delx + 0x400 >= dely { 1 } else { 6 };
if self.action_counter > 0 {
self.action_counter -= 1;
if self.flags.any_flag() && self.action_counter3 > 10 {
self.action_num += 10;
self.anim_num = 1;
self.action_counter3 = 0;
self.vel_y = -0x600;
state.sound_manager.play_sfx(15);
}
} else {
self.action_num = 100;
self.anim_num = 0;
}
}
if self.action_num >= 100 && self.action_num < 500 {
if self.x >= player.x - 0xA000 && self.x <= player.x + 0xA000 {
self.vel_y += 0x33;
} else {
self.vel_y += if self.flags.any_flag() { 0x10 } else { 0x33 };
}
}
self.vel_x = self.vel_x.clamp(-0x300, 0x300);
if self.vel_y > 0x5FF {
self.vel_y = 0x5FF;
}
self.x += self.vel_x;
self.y += self.vel_y;
if self.action_num >= 100 && !self.flags.hit_bottom_wall() && self.anim_num != 1000 {
if delx + 0x400 >= dely {
self.anim_num = 1;
} else {
self.anim_num = 6;
}
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 11 };
self.anim_rect = state.constants.npc.n180_curly_ai[self.anim_num as usize + dir_offset];
Ok(())
}
pub(crate) fn tick_n181_curly_ai_machine_gun(
&mut self,
state: &mut SharedGameState,
npc_list: &NPCList,
bullet_manager: &mut BulletManager,
) -> GameResult {
if let Some(parent) = self.get_parent_ref_mut(npc_list) {
if parent.anim_num > 4 {
self.direction = parent.direction;
self.x = parent.x;
self.y = parent.y - 0x1400;
self.anim_num = 1;
} else {
self.x = parent.x
+ if parent.direction == Direction::Left {
self.direction = Direction::Left;
-0x1000
} else {
self.direction = Direction::Right;
0x1000
};
self.y = parent.y;
self.anim_num = 0;
}
if parent.anim_num == 1 || parent.anim_num == 3 || parent.anim_num == 6 || parent.anim_num == 8 {
self.y -= 0x200;
}
if self.action_num == 0 {
if parent.action_counter2 == 10 {
parent.action_counter2 = 0;
self.action_num = 10;
self.action_counter = 0;
}
} else if self.action_num == 10 {
self.action_counter += 1;
if self.action_counter % 6 == 1 {
if self.anim_num != 0 {
if self.direction != Direction::Left {
bullet_manager.create_bullet(
self.x + 0x400,
self.y - 0x800,
12,
TargetPlayer::Player1,
Direction::Up,
&state.constants,
);
state.create_caret(self.x + 0x400, self.y - 0x800, CaretType::Shoot, Direction::Left);
} else {
bullet_manager.create_bullet(
self.x - 0x400,
self.y - 0x800,
12,
TargetPlayer::Player1,
Direction::Up,
&state.constants,
);
state.create_caret(self.x - 0x400, self.y - 0x800, CaretType::Shoot, Direction::Left);
}
} else if self.direction != Direction::Left {
bullet_manager.create_bullet(
self.x + 0x800,
self.y + 0x600,
12,
TargetPlayer::Player1,
Direction::Right,
&state.constants,
);
state.create_caret(self.x + 0x800, self.y + 0x600, CaretType::Shoot, Direction::Left);
} else {
bullet_manager.create_bullet(
self.x - 0x800,
self.y + 0x600,
12,
TargetPlayer::Player1,
Direction::Left,
&state.constants,
);
state.create_caret(self.x - 0x800, self.y + 0x600, CaretType::Shoot, Direction::Left);
}
}
if self.action_counter == 60 {
self.action_num = 0;
}
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
self.anim_rect = state.constants.npc.n181_curly_ai_machine_gun[self.anim_num as usize + dir_offset];
}
Ok(())
}
pub(crate) fn tick_n182_curly_ai_polar_star(
&mut self,
state: &mut SharedGameState,
npc_list: &NPCList,
bullet_manager: &mut BulletManager,
) -> GameResult {
if let Some(parent) = self.get_parent_ref_mut(npc_list) {
if parent.anim_num > 4 {
self.direction = parent.direction;
self.x = parent.x;
self.y = parent.y - 0x1400;
self.anim_num = 1;
} else {
self.x = parent.x
+ if parent.direction == Direction::Left {
self.direction = Direction::Left;
-0x1000
} else {
self.direction = Direction::Right;
0x1000
};
self.y = parent.y;
self.anim_num = 0;
}
if parent.anim_num == 1 || parent.anim_num == 3 || parent.anim_num == 6 || parent.anim_num == 8 {
self.y -= 0x200;
}
if self.action_num == 0 {
if parent.action_counter2 == 10 {
parent.action_counter2 = 0;
self.action_num = 10;
self.action_counter = 0;
}
} else if self.action_num == 10 {
self.action_counter += 1;
if self.action_counter % 6 == 1 {
if self.anim_num != 0 {
if self.direction != Direction::Left {
bullet_manager.create_bullet(
self.x + 0x400,
self.y - 0x800,
12,
TargetPlayer::Player1,
Direction::Up,
&state.constants,
);
state.create_caret(self.x + 0x400, self.y - 0x800, CaretType::Shoot, Direction::Left);
} else {
bullet_manager.create_bullet(
self.x - 0x400,
self.y - 0x800,
12,
TargetPlayer::Player1,
Direction::Up,
&state.constants,
);
state.create_caret(self.x - 0x400, self.y - 0x800, CaretType::Shoot, Direction::Left);
}
} else if self.direction != Direction::Left {
bullet_manager.create_bullet(
self.x + 0x800,
self.y + 0x600,
12,
TargetPlayer::Player1,
Direction::Right,
&state.constants,
);
state.create_caret(self.x + 0x800, self.y + 0x600, CaretType::Shoot, Direction::Left);
} else {
bullet_manager.create_bullet(
self.x - 0x800,
self.y + 0x600,
12,
TargetPlayer::Player1,
Direction::Left,
&state.constants,
);
state.create_caret(self.x - 0x800, self.y + 0x600, CaretType::Shoot, Direction::Left);
}
}
if self.action_counter == 60 {
self.action_num = 0;
}
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
self.anim_rect = state.constants.npc.n182_curly_ai_polar_star[self.anim_num as usize + dir_offset];
}
Ok(())
}
pub(crate) fn tick_n183_curly_air_tank_bubble(
&mut self,
state: &mut SharedGameState,
npc_list: &NPCList,
) -> GameResult {
if let Some(parent) = self.get_parent_ref_mut(npc_list) {
if self.action_num == 0 {
self.x = parent.x;
self.y = parent.y;
self.action_num = 1;
}
self.x += (parent.x - self.x) / 2;
self.y += (parent.y - self.y) / 2;
self.animate(1, 0, 1);
self.anim_rect = if parent.flags.in_water() {
state.constants.npc.n183_curly_air_tank_bubble[self.anim_num as usize]
} else {
Rect::new(0, 0, 0, 0)
}
}
Ok(())
}
}

View file

@ -92,7 +92,7 @@ impl NPC {
self.action_num = 0x14;
}
}
_ => {}
_ => (),
}
self.x += self.vel_x;

View file

@ -1,13 +1,14 @@
use crate::framework::error::GameResult;
use num_traits::{abs, clamp};
use crate::caret::CaretType;
use crate::common::{CDEG_RAD, Direction, Rect};
use crate::common::{Direction, Rect, CDEG_RAD};
use crate::framework::error::GameResult;
use crate::npc::list::NPCList;
use crate::npc::NPC;
use crate::player::Player;
use crate::player::{Player, TargetPlayer};
use crate::rng::RNG;
use crate::shared_game_state::SharedGameState;
use crate::weapon::bullet::BulletManager;
impl NPC {
pub(crate) fn tick_n002_behemoth(&mut self, state: &mut SharedGameState) -> GameResult {
@ -20,23 +21,25 @@ impl NPC {
match self.action_num {
0 => {
self.vel_x = match self.direction {
Direction::Left => { -0x100 }
Direction::Right => { 0x100 }
_ => { 0 }
Direction::Left => -0x100,
Direction::Right => 0x100,
_ => 0,
};
self.anim_counter += 1;
if self.anim_counter > 8 {
self.anim_counter = 0;
self.anim_num = (self.anim_num + 1) % 3;
self.anim_rect = state.constants.npc.n002_behemoth[self.anim_num as usize + if self.direction == Direction::Right { 7 } else { 0 }];
self.anim_rect = state.constants.npc.n002_behemoth
[self.anim_num as usize + if self.direction == Direction::Right { 7 } else { 0 }];
}
if self.shock > 0 {
self.action_counter = 0;
self.action_num = 1;
self.anim_num = 4;
self.anim_rect = state.constants.npc.n002_behemoth[self.anim_num as usize + if self.direction == Direction::Right { 7 } else { 0 }];
self.anim_rect = state.constants.npc.n002_behemoth
[self.anim_num as usize + if self.direction == Direction::Right { 7 } else { 0 }];
}
}
1 => {
@ -50,7 +53,8 @@ impl NPC {
self.anim_num = 6;
self.anim_counter = 0;
self.damage = 5;
self.anim_rect = state.constants.npc.n002_behemoth[self.anim_num as usize + if self.direction == Direction::Right { 7 } else { 0 }];
self.anim_rect = state.constants.npc.n002_behemoth
[self.anim_num as usize + if self.direction == Direction::Right { 7 } else { 0 }];
} else {
self.action_num = 0;
self.anim_counter = 0;
@ -59,9 +63,9 @@ impl NPC {
}
2 => {
self.vel_x = match self.direction {
Direction::Left => { -0x400 }
Direction::Right => { 0x400 }
_ => { 0 }
Direction::Left => -0x400,
Direction::Right => 0x400,
_ => 0,
};
self.action_counter += 1;
@ -80,10 +84,11 @@ impl NPC {
state.quake_counter = 8;
}
self.anim_rect = state.constants.npc.n002_behemoth[self.anim_num as usize + if self.direction == Direction::Right { 7 } else { 0 }];
self.anim_rect = state.constants.npc.n002_behemoth
[self.anim_num as usize + if self.direction == Direction::Right { 7 } else { 0 }];
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -97,14 +102,19 @@ impl NPC {
Ok(())
}
pub(crate) fn tick_n005_green_critter(&mut self, state: &mut SharedGameState, players: [&mut Player; 2]) -> GameResult {
pub(crate) fn tick_n005_green_critter(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.y += 0x600;
self.action_num = 1;
self.anim_num = 0;
self.anim_rect = state.constants.npc.n005_green_critter[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
self.anim_rect = state.constants.npc.n005_green_critter
[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
}
let player = self.get_closest_player_mut(players);
@ -120,13 +130,15 @@ impl NPC {
}
if self.action_counter >= 8
&& self.x - (112 * 0x200) < player.x
&& self.x + (112 * 0x200) > player.x
&& self.y - (80 * 0x200) < player.y
&& self.y + (80 * 0x200) > player.y {
&& self.x - 0xe000 < player.x
&& self.x + 0xe000 > player.x
&& self.y - 0xa000 < player.y
&& self.y + 0xa000 > player.y
{
if self.anim_num != 1 {
self.anim_num = 1;
self.anim_rect = state.constants.npc.n005_green_critter[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
self.anim_rect = state.constants.npc.n005_green_critter
[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
}
} else {
if self.action_counter < 8 {
@ -135,7 +147,8 @@ impl NPC {
if self.anim_num != 0 {
self.anim_num = 0;
self.anim_rect = state.constants.npc.n005_green_critter[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
self.anim_rect = state.constants.npc.n005_green_critter
[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
}
}
@ -145,22 +158,25 @@ impl NPC {
if self.anim_num != 0 {
self.anim_num = 0;
self.anim_rect = state.constants.npc.n005_green_critter[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
self.anim_rect = state.constants.npc.n005_green_critter
[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
}
}
if self.action_counter >= 8
&& self.target_x >= 100
&& self.x - (64 * 0x200) < player.x
&& self.x + (64 * 0x200) > player.x
&& self.y - (80 * 0x200) < player.y
&& self.y + (80 * 0x200) > player.y {
&& self.x - 0x8000 < player.x
&& self.x + 0x8000 > player.x
&& self.y - 0xa000 < player.y
&& self.y + 0xa000 > player.y
{
self.action_num = 2;
self.action_counter = 0;
if self.anim_num != 0 {
self.anim_num = 0;
self.anim_rect = state.constants.npc.n005_green_critter[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
self.anim_rect = state.constants.npc.n005_green_critter
[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
}
}
}
@ -171,7 +187,8 @@ impl NPC {
if self.anim_num != 2 {
self.anim_num = 2;
self.anim_rect = state.constants.npc.n005_green_critter[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
self.anim_rect = state.constants.npc.n005_green_critter
[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
}
self.vel_y = -0x5ff;
@ -194,11 +211,12 @@ impl NPC {
if self.anim_num != 0 {
self.anim_num = 0;
self.anim_rect = state.constants.npc.n005_green_critter[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
self.anim_rect = state.constants.npc.n005_green_critter
[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
}
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -218,9 +236,13 @@ impl NPC {
self.action_num = 1;
match self.direction {
Direction::Left => { self.action_num = 1; }
Direction::Right => { self.action_num = 3; }
_ => {}
Direction::Left => {
self.action_num = 1;
}
Direction::Right => {
self.action_num = 3;
}
_ => (),
}
}
1 => {
@ -301,10 +323,11 @@ impl NPC {
self.anim_num = 1;
}
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n006_green_beetle[self.anim_num as usize + if self.direction == Direction::Right { 5 } else { 0 }];
self.anim_rect = state.constants.npc.n006_green_beetle
[self.anim_num as usize + if self.direction == Direction::Right { 5 } else { 0 }];
Ok(())
}
@ -347,7 +370,7 @@ impl NPC {
self.action_num = 1;
}
}
_ => {}
_ => (),
}
if self.vel_x < 0 {
@ -367,13 +390,18 @@ impl NPC {
}
if self.anim_counter == 1 {
self.anim_rect = state.constants.npc.n007_basil[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
self.anim_rect = state.constants.npc.n007_basil
[self.anim_num as usize + if self.direction == Direction::Right { 3 } else { 0 }];
}
Ok(())
}
pub(crate) fn tick_n008_blue_beetle(&mut self, state: &mut SharedGameState, players: [&mut Player; 2]) -> GameResult {
pub(crate) fn tick_n008_blue_beetle(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
) -> GameResult {
match self.action_num {
0 => {
let player = self.get_closest_player_mut(players);
@ -394,7 +422,7 @@ impl NPC {
self.x = player.x - 256 * 0x200;
self.vel_x = 0x2ff;
}
_ => {}
_ => (),
}
} else {
self.npc_flags.set_shootable(false);
@ -431,7 +459,7 @@ impl NPC {
self.y += self.vel_y;
}
}
_ => {}
_ => (),
}
self.anim_counter += 1;
@ -445,7 +473,8 @@ impl NPC {
}
if self.anim_counter == 1 {
self.anim_rect = state.constants.npc.n008_blue_beetle[self.anim_num as usize + if self.direction == Direction::Right { 2 } else { 0 }];
self.anim_rect = state.constants.npc.n008_blue_beetle
[self.anim_num as usize + if self.direction == Direction::Right { 2 } else { 0 }];
}
Ok(())
@ -528,7 +557,7 @@ impl NPC {
}
self.animate(1, 0, 1);
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n025_lift[self.anim_num as usize];
@ -536,7 +565,12 @@ impl NPC {
Ok(())
}
pub(crate) fn tick_n058_basu(&mut self, state: &mut SharedGameState, players: [&mut Player; 2], npc_list: &NPCList) -> GameResult {
pub(crate) fn tick_n058_basu(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
npc_list: &NPCList,
) -> GameResult {
let player = self.get_closest_player_mut(players);
match self.action_num {
@ -599,13 +633,12 @@ impl NPC {
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.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 {
@ -677,6 +710,490 @@ impl NPC {
Ok(())
}
pub(crate) fn tick_n200_zombie_dragon(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
npc_list: &NPCList,
) -> GameResult {
if self.action_num < 100 && self.life < 950 {
self.action_num = 100;
self.npc_flags.set_shootable(false);
self.damage = 0;
state.sound_manager.play_sfx(72);
npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right as usize, 8, state, &self.rng);
self.create_xp_drop(state, npc_list);
}
match self.action_num {
0 | 10 => {
if self.action_num == 0 {
self.action_num = 10;
self.action_counter3 = 0;
}
let player = self.get_closest_player_mut(players);
self.animate(30, 0, 1);
if self.action_counter3 != 0 {
self.action_counter3 -= 1;
}
if self.action_counter3 == 0 && player.x > self.x - 0xE000 && player.x < self.x + 0xE000 {
self.action_num = 20;
}
}
20 | 21 => {
if self.action_num == 20 {
self.action_num = 21;
self.action_counter = 0;
}
self.action_counter += 1;
if (self.action_counter & 1) != 0 {
self.anim_num = 2;
} else {
self.anim_num = 3;
}
if self.action_counter > 30 {
self.action_num = 30;
}
let player = self.get_closest_player_mut(players);
self.direction = if player.x >= self.x { Direction::Right } else { Direction::Left };
}
30 | 31 => {
let player = self.get_closest_player_mut(players);
if self.action_num == 30 {
self.action_num = 31;
self.action_counter = 0;
self.anim_num = 4;
self.target_x = player.x;
self.target_y = player.y;
}
self.action_counter += 1;
if self.action_counter <= 39 && self.action_counter % 8 == 1 {
let px = self.x + (self.direction.vector_x() * 0x1c00) - self.target_x;
let py = self.y - self.target_y;
let deg = f64::atan2(py as f64, px as f64) + self.rng.range(-6..6) as f64 * CDEG_RAD;
let mut npc = NPC::create(202, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.x + self.direction.vector_x() * 0x1c00;
npc.y = self.y;
npc.vel_x = (deg.cos() * -1536.0) as i32;
npc.vel_y = (deg.sin() * -1536.0) as i32;
let _ = npc_list.spawn(0x100, npc);
if !player.cond.hidden() {
state.sound_manager.play_sfx(33);
}
}
if self.action_counter > 60 {
self.action_num = 10;
self.action_counter3 = self.rng.range(100..200) as u16;
self.anim_counter = 0;
}
}
100 => {
self.anim_num = 5;
}
_ => (),
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 6 };
self.anim_rect = state.constants.npc.n200_zombie_dragon[self.anim_num as usize + dir_offset];
Ok(())
}
pub(crate) fn tick_n201_zombie_dragon_dead(&mut self, state: &mut SharedGameState) -> GameResult {
let dir_offset = if self.direction == Direction::Left { 0 } else { 1 };
self.anim_rect = state.constants.npc.n201_zombie_dragon_dead[dir_offset];
Ok(())
}
pub(crate) fn tick_n202_zombie_dragon_projectile(&mut self, state: &mut SharedGameState) -> GameResult {
self.y += self.vel_y;
self.x += self.vel_x;
self.animate(1, 0, 2);
self.anim_rect = state.constants.npc.n202_zombie_dragon_projectile[self.anim_num as usize];
self.action_counter3 += 1;
if self.flags.hit_anything() || self.action_counter3 > 300 {
self.cond.set_alive(false);
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
}
Ok(())
}
pub(crate) fn tick_n203_critter_destroyed_egg_corridor(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.y += 0x600;
self.action_num = 1;
self.anim_num = 0;
}
let player = self.get_closest_player_mut(players);
if self.x > player.x {
self.direction = Direction::Left;
} else {
self.direction = Direction::Right;
}
if self.target_x < 100 {
self.target_x += 1;
}
if self.action_counter >= 8
&& self.x - 0xe000 < player.x
&& self.x + 0xe000 > player.x
&& self.y - 0xa000 < player.y
&& self.y + 0xa000 > player.y
{
self.anim_num = 1;
} else {
if self.action_counter < 8 {
self.action_counter += 1;
}
}
if self.shock > 0 {
self.action_num = 2;
self.action_counter = 0;
self.anim_num = 0;
}
if self.action_counter >= 8
&& self.target_x >= 100
&& self.x - 0x6000 < player.x
&& self.x + 0x6000 > player.x
&& self.y - 0xa000 < player.y
&& self.y + 0xa000 > player.y
{
self.action_num = 2;
self.action_counter = 0;
self.anim_num = 0;
}
}
2 => {
self.action_counter += 1;
if self.action_counter > 8 {
self.action_num = 3;
self.anim_num = 2;
self.vel_y = -0x5ff;
state.sound_manager.play_sfx(30);
if self.direction == Direction::Left {
self.vel_x = -0x100;
} else {
self.vel_x = 0x100;
}
}
}
3 => {
if self.flags.hit_bottom_wall() {
self.vel_x = 0;
self.action_counter = 0;
self.action_num = 1;
self.anim_num = 0;
state.sound_manager.play_sfx(23);
}
}
_ => (),
}
let dir_offset = if self.direction == Direction::Right { 3 } else { 0 };
self.anim_rect = state.constants.npc.n203_critter_destroyed_egg_corridor[self.anim_num as usize + dir_offset];
self.vel_y += 0x40;
if self.vel_y > 0x5ff {
self.vel_y = 0x5ff;
}
self.x += self.vel_x;
self.y += self.vel_y;
Ok(())
}
pub(crate) fn tick_n204_small_falling_spike(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
npc_list: &NPCList,
) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.target_x = self.x;
}
let player = self.get_closest_player_ref(&players);
if player.x > self.x - 0x1800 && player.x < self.x + 0x1800 && player.y > self.y {
self.action_num = 2;
}
}
2 => {
self.action_counter += 1;
self.x = if ((self.action_counter / 6) & 1) != 0 { self.target_x - 0x200 } else { self.target_x };
if self.action_counter > 30 {
self.action_num = 3;
self.anim_num = 1;
}
}
3 => {
self.vel_y += 0x20;
if self.flags.hit_anything() {
let player = self.get_closest_player_ref(&players);
if !player.cond.hidden() {
state.sound_manager.play_sfx(12);
}
npc_list.create_death_smoke(
self.x,
self.y,
self.display_bounds.right as usize,
4,
state,
&self.rng,
);
self.cond.set_alive(false);
}
}
_ => (),
}
if self.vel_y > 3072 {
self.vel_y = 3072;
}
self.y += self.vel_y;
self.anim_rect = state.constants.npc.n204_small_falling_spike[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n205_large_falling_spike(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
npc_list: &NPCList,
bullet_manager: &mut BulletManager,
) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.target_x = self.x;
self.y += 0x800;
}
let player = self.get_closest_player_ref(&players);
if player.x > self.x - 0x1800 && player.x < self.x + 0x1800 && player.y > self.y {
self.action_num = 2;
}
}
2 => {
self.action_counter += 1;
if ((self.action_counter / 6) & 1) != 0 {
self.x = self.target_x - 0x200;
} else {
self.x = self.target_x;
}
if self.action_counter > 30 {
self.action_num = 3;
self.anim_num = 1;
self.action_counter = 0;
}
}
3 => {
let player = self.get_closest_player_ref(&players);
if player.y <= self.y {
self.npc_flags.set_solid_hard(true);
self.damage = 0;
} else {
self.npc_flags.set_solid_hard(false);
self.damage = 127;
}
self.vel_y += 0x20;
self.action_counter += 1;
if self.action_counter > 8 && self.flags.any_flag() {
self.action_num = 4;
self.action_counter = 0;
self.damage = 0;
self.vel_y = 0;
self.npc_flags.set_solid_hard(true);
state.sound_manager.play_sfx(12);
npc_list.create_death_smoke(
self.x,
self.y,
self.display_bounds.right as usize,
4,
state,
&self.rng,
);
bullet_manager.create_bullet(
self.x,
self.y,
24,
TargetPlayer::Player1,
Direction::Left,
&state.constants,
);
}
}
4 => {
self.action_counter += 1;
if self.action_counter > 4 {
self.action_num = 5;
self.npc_flags.set_shootable(true);
}
}
_ => (),
}
if self.vel_y > 0xC00 {
self.vel_y = 0xC00;
}
self.y += self.vel_y;
self.anim_rect = state.constants.npc.n205_large_falling_spike[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n206_counter_bomb(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
npc_list: &NPCList,
) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.target_x = self.x;
self.target_y = self.y;
self.action_counter3 = 120;
self.action_counter = self.rng.range(0..50) as u16;
}
self.action_counter += 1;
if self.action_counter >= 50 {
self.action_counter = 0;
self.action_num = 2;
self.vel_y = 0x300;
}
}
2 => {
let player = self.get_closest_player_ref(&players);
if player.x > self.x - 0xA000 && player.x < self.x + 0xA000 {
self.action_counter = 0;
self.action_num = 3;
}
if self.shock > 0 {
self.action_counter = 0;
self.action_num = 3;
}
}
3 => {
let mut npc = NPC::create(207, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.x + 0x2000;
npc.y = self.y + 0x800;
match self.action_counter {
0 => {
npc.tsc_direction = 0;
let _ = npc_list.spawn(0x100, npc);
}
60 => {
npc.tsc_direction = 1;
let _ = npc_list.spawn(0x100, npc);
}
120 => {
npc.tsc_direction = 2;
let _ = npc_list.spawn(0x100, npc);
}
180 => {
npc.tsc_direction = 3;
let _ = npc_list.spawn(0x100, npc);
}
240 => {
npc.tsc_direction = 4;
let _ = npc_list.spawn(0x100, npc);
}
300 => {
self.hit_bounds.right = 0x10000;
self.hit_bounds.left = 0x10000;
self.hit_bounds.top = 0xC800;
self.hit_bounds.bottom = 0xC800;
self.damage = 30;
self.cond.set_explode_die(true);
state.quake_counter = 20;
state.sound_manager.play_sfx(35);
npc_list.create_death_smoke(self.x, self.y, 0x10000, 100 as usize, state, &self.rng);
}
_ => (),
}
self.action_counter += 1;
}
_ => (),
}
if self.action_num > 1 {
if self.target_y < self.y {
self.vel_y -= 0x10;
}
if self.target_y > self.y {
self.vel_y += 0x10;
}
self.vel_y = self.vel_y.clamp(-0x100, 0x100);
}
self.x += self.vel_x;
self.y += self.vel_y;
self.animate(4, 0, 2);
self.anim_rect = state.constants.npc.n206_counter_bomb[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n207_counter_bomb_countdown(&mut self, state: &mut SharedGameState) -> GameResult {
match self.action_num {
0 | 1 => {
@ -700,7 +1217,7 @@ impl NPC {
self.cond.set_alive(false);
}
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n207_counter_bomb_countdown[self.anim_num as usize % 5];
@ -708,7 +1225,12 @@ impl NPC {
Ok(())
}
pub(crate) fn tick_n208_basu_destroyed_egg_corridor(&mut self, state: &mut SharedGameState, players: [&mut Player; 2], npc_list: &NPCList) -> GameResult {
pub(crate) fn tick_n208_basu_destroyed_egg_corridor(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
npc_list: &NPCList,
) -> GameResult {
let player = self.get_closest_player_mut(players);
match self.action_num {
@ -771,13 +1293,12 @@ impl NPC {
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.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 {
@ -824,7 +1345,10 @@ impl NPC {
Ok(())
}
pub(crate) fn tick_n209_basu_projectile_destroyed_egg_corridor(&mut self, state: &mut SharedGameState) -> GameResult {
pub(crate) fn tick_n209_basu_projectile_destroyed_egg_corridor(
&mut self,
state: &mut SharedGameState,
) -> GameResult {
self.x += self.vel_x;
self.y += self.vel_y;
@ -848,4 +1372,87 @@ impl NPC {
Ok(())
}
pub(crate) fn tick_n210_beetle_destroyed_egg_corridor(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
) -> GameResult {
match self.action_num {
0 => {
let player = self.get_closest_player_mut(players);
if player.x < self.x + 0x2000 && player.x > self.x - 0x2000 {
self.npc_flags.set_shootable(true);
self.vel_y = -0x200;
self.target_y = self.y;
self.action_num = 1;
self.damage = 2;
match self.direction {
Direction::Left => {
self.x = player.x + 0x20000;
self.vel_x = -0x2ff;
}
Direction::Right => {
self.x = player.x - 0x20000;
self.vel_x = 0x2ff;
}
_ => (),
}
} else {
self.npc_flags.set_shootable(false);
self.anim_rect.left = 0;
self.anim_rect.right = 0;
self.damage = 0;
self.vel_x = 0;
self.vel_y = 0;
return Ok(());
}
}
1 => {
let player = self.get_closest_player_mut(players);
if self.x > player.x {
self.direction = Direction::Left;
self.vel_x -= 0x10;
} else {
self.direction = Direction::Right;
self.vel_x += 0x10;
}
self.vel_y += if self.y < self.target_y { 8 } else { -8 };
self.vel_x = clamp(self.vel_x, -0x2ff, 0x2ff);
self.vel_y = clamp(self.vel_y, -0x200, 0x200);
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;
}
}
_ => (),
}
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.anim_counter == 1 {
self.anim_rect = state.constants.npc.n210_beetle_destroyed_egg_corridor
[self.anim_num as usize + if self.direction == Direction::Right { 2 } else { 0 }];
}
Ok(())
}
}

View file

@ -56,7 +56,7 @@ impl NPC {
}
}
}
_ => {}
_ => (),
}
if self.shock > 0 {
@ -167,7 +167,7 @@ impl NPC {
}
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -215,7 +215,7 @@ impl NPC {
self.vel_y = clamp(self.vel_y, -0x300, 0x300);
}
_ => {}
_ => (),
}
self.x += self.vel_x;

View file

@ -120,7 +120,7 @@ impl NPC {
state.quake_counter = 30;
}
}
_ => {}
_ => (),
}
if self.action_num != 4 {
@ -198,7 +198,7 @@ impl NPC {
self.npc_flags.set_ignore_solidity(true);
}
}
_ => {}
_ => (),
}
self.x += self.vel_x;
@ -329,7 +329,7 @@ impl NPC {
state.sound_manager.play_sfx(23);
}
}
_ => {}
_ => (),
}
if self.action_num != 4 {
@ -429,7 +429,7 @@ impl NPC {
self.vel_y = 0x200;
}
}
_ => {}
_ => (),
}
self.x += self.vel_x;
@ -549,7 +549,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
if self.shock > 0 {
@ -656,7 +656,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
self.direction = if self.x <= self.target_x { Direction::Right } else { Direction::Left };
@ -835,7 +835,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
if self.action_num <= 9
@ -1021,7 +1021,7 @@ impl NPC {
npc_list.create_death_smoke(self.x, self.y, 0x2000, 16, state, &self.rng);
self.cond.set_alive(false);
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n107_malco_broken[self.anim_num as usize];
@ -1085,7 +1085,7 @@ impl NPC {
let _ = npc_list.spawn(0x100, npc.clone());
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -1183,7 +1183,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
if self.action_num <= 9
@ -1333,7 +1333,7 @@ impl NPC {
state.sound_manager.play_sfx(23);
}
}
_ => {}
_ => (),
}
self.vel_y += if self.action_num <= 50 { 0x40 } else { 0x20 };
@ -1423,7 +1423,7 @@ impl NPC {
self.cond.set_alive(false);
}
}
_ => {}
_ => (),
}
if self.action_counter % 4 == 0 && self.action_num >= 20 {

View file

@ -71,7 +71,7 @@ impl NPC {
7 => {
self.action_num = 1;
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -258,7 +258,7 @@ impl NPC {
self.vel_x2 = 0;
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -373,7 +373,7 @@ impl NPC {
self.anim_rect = state.constants.npc.n089_igor_dead[self.anim_num as usize + dir_offset];
}
}
_ => {}
_ => (),
}
Ok(())

View file

@ -75,7 +75,7 @@ impl NPC {
}
}
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n298_intro_doctor[self.anim_num as usize];
@ -97,7 +97,7 @@ impl NPC {
self.anim_num = 0;
self.action_counter = 0;
}
_ => {}
_ => (),
}
}

View file

@ -89,7 +89,7 @@ impl NPC {
state.sound_manager.play_sfx(23);
}
}
_ => {}
_ => (),
}
self.vel_y += 0x55;

View file

@ -136,7 +136,7 @@ impl NPC {
state.sound_manager.play_sfx(23);
}
}
_ => {}
_ => (),
}
if self.action_num == 4 {
@ -314,7 +314,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -349,7 +349,7 @@ impl NPC {
match self.direction {
Direction::Left => self.vel_x = 0x100,
Direction::Right => self.vel_x = -0x100,
_ => {}
_ => (),
};
state.sound_manager.play_sfx(53);
}
@ -368,7 +368,7 @@ impl NPC {
self.cond.set_explode_die(true);
}
}
_ => {}
_ => (),
}
self.vel_y += 0x20;
@ -458,7 +458,7 @@ impl NPC {
self.anim_counter = 0;
}
}
_ => {}
_ => (),
}
if player.x >= self.x {
self.direction = Direction::Right;
@ -626,7 +626,7 @@ impl NPC {
self.action_num = 1;
}
}
_ => {}
_ => (),
}
self.y += self.vel_y;
@ -692,7 +692,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n166_chaba[self.anim_num as usize];
@ -786,7 +786,7 @@ impl NPC {
self.cond.set_alive(false);
}
}
_ => {}
_ => (),
}
state.npc_super_pos = (self.x, -512000);
@ -818,7 +818,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
@ -850,7 +850,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
@ -901,7 +901,7 @@ impl NPC {
self.action_counter += 1;
}
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n168_boulder;
@ -1118,7 +1118,7 @@ impl NPC {
self.action_counter = 0;
}
}
_ => {}
_ => (),
}
self.vel_y += 0x33;
@ -1358,7 +1358,7 @@ impl NPC {
self.target_x += if self.direction != Direction::Left { 0x200 } else { -0x200 };
}
_ => {}
_ => (),
}
self.vel_x = self.vel_x.clamp(-0x400, 0x400);
@ -1419,7 +1419,7 @@ impl NPC {
self.action_num = 1;
}
_ => {}
_ => (),
}
self.animate(10, 0, 3);
@ -1454,7 +1454,7 @@ impl NPC {
self.y -= 0x3000;
self.action_num = 1;
}
_ => {}
_ => (),
}
Ok(())
@ -1480,7 +1480,7 @@ impl NPC {
_ => (),
};
}
_ => {}
_ => (),
}
self.animate(10, 0, 3);

View file

@ -91,7 +91,7 @@ impl NPC {
self.action_num = 0;
}
}
_ => {}
_ => (),
}
if self.shock > 0 && [1, 2, 4].contains(&self.action_num) {
@ -199,7 +199,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
if self.direction == Direction::Left {
@ -243,7 +243,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
@ -341,7 +341,7 @@ impl NPC {
self.anim_num = 6;
}
_ => {}
_ => (),
}
if (self.vel_x < 0 && self.flags.hit_left_wall())
@ -424,7 +424,7 @@ impl NPC {
self.action_num = 0;
}
}
_ => {}
_ => (),
}
if self.shock > 0 && [1, 2, 4].contains(&self.action_num) {

View file

@ -4,8 +4,8 @@ use crate::caret::CaretType;
use crate::common::{Direction, Rect};
use crate::components::flash::Flash;
use crate::framework::error::GameResult;
use crate::npc::{NPC, NPCLayer};
use crate::npc::list::NPCList;
use crate::npc::{NPCLayer, NPC};
use crate::player::Player;
use crate::rng::RNG;
use crate::shared_game_state::SharedGameState;
@ -84,7 +84,7 @@ impl NPC {
Direction::Up => {
self.anim_rect = state.constants.npc.n004_smoke[self.anim_num as usize + 8];
}
_ => {}
_ => (),
}
Ok(())
@ -171,7 +171,7 @@ impl NPC {
self.action_num = 1;
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -271,7 +271,7 @@ impl NPC {
self.action_num = 1;
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -289,7 +289,7 @@ impl NPC {
0 => match self.direction {
Direction::Left => self.anim_rect = state.constants.npc.n018_door[0],
Direction::Right => self.anim_rect = state.constants.npc.n018_door[1],
_ => {}
_ => (),
},
1 => {
let mut npc = NPC::create(4, &state.npc_table);
@ -307,7 +307,7 @@ impl NPC {
self.action_num = 0;
self.anim_rect = state.constants.npc.n018_door[0]
}
_ => {}
_ => (),
}
Ok(())
@ -324,7 +324,7 @@ impl NPC {
self.anim_num = self.anim_counter / 4;
self.anim_rect = state.constants.npc.n020_computer[1 + self.anim_num as usize];
}
_ => {}
_ => (),
}
Ok(())
@ -352,7 +352,7 @@ impl NPC {
1 => {
self.anim_num = (self.anim_num + 1) & 1;
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n022_teleporter[self.anim_num as usize];
@ -408,7 +408,7 @@ impl NPC {
self.anim_rect = state.constants.npc.n030_hermit_gunsmith[0];
}
}
_ => {}
_ => (),
}
} else {
if self.action_num == 0 {
@ -442,7 +442,7 @@ impl NPC {
match self.direction {
Direction::Left => self.anim_rect = state.constants.npc.n034_bed[0],
Direction::Right => self.anim_rect = state.constants.npc.n034_bed[1],
_ => {}
_ => (),
}
}
@ -472,7 +472,7 @@ impl NPC {
self.anim_rect.left = 0;
self.anim_rect.right = 0;
}
_ => {}
_ => (),
}
Ok(())
@ -485,7 +485,7 @@ impl NPC {
match self.direction {
Direction::Left => self.anim_rect = state.constants.npc.n039_save_sign[0],
Direction::Right => self.anim_rect = state.constants.npc.n039_save_sign[1],
_ => {}
_ => (),
}
}
@ -511,7 +511,7 @@ impl NPC {
match self.direction {
Direction::Left => self.anim_rect = state.constants.npc.n043_chalkboard[0],
Direction::Right => self.anim_rect = state.constants.npc.n043_chalkboard[1],
_ => {}
_ => (),
}
}
@ -555,7 +555,6 @@ impl NPC {
if self.direction == Direction::Left {
self.anim_counter = (self.anim_counter + 1) % 4;
self.anim_num = self.anim_counter / 2;
self.anim_rect = state.constants.npc.n072_sprinkler[self.anim_num as usize];
let player = self.get_closest_player_mut(players);
if self.anim_num % 2 == 0 && (player.x - self.x).abs() < 480 * 0x200 {
@ -578,6 +577,8 @@ impl NPC {
}
}
self.anim_rect = state.constants.npc.n072_sprinkler[self.anim_num as usize];
Ok(())
}
@ -637,7 +638,7 @@ impl NPC {
match self.direction {
Direction::Left => self.anim_rect = state.constants.npc.n078_pot[0],
Direction::Right => self.anim_rect = state.constants.npc.n078_pot[1],
_ => {}
_ => (),
}
}
@ -650,8 +651,7 @@ impl NPC {
self.anim_num = 0;
let player = self.get_closest_player_mut(players);
if abs(player.x - self.x) < 0x1000 && player.y < self.y + 0x1000 && player.y > self.y - 0x2000
{
if abs(player.x - self.x) < 0x1000 && player.y < self.y + 0x1000 && player.y > self.y - 0x2000 {
state.sound_manager.play_sfx(43);
self.action_num = 1;
}
@ -662,7 +662,7 @@ impl NPC {
self.anim_num = 1;
}
}
_ => {}
_ => (),
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 3 };
@ -730,7 +730,7 @@ impl NPC {
}
}
}
_ => {}
_ => (),
}
if self.anim_counter == 0 {
@ -789,7 +789,7 @@ impl NPC {
}
}
}
_ => {}
_ => (),
}
if self.anim_counter == 0 {
@ -845,7 +845,7 @@ impl NPC {
}
}
}
_ => {}
_ => (),
}
if self.anim_counter == 0 {
@ -900,7 +900,7 @@ impl NPC {
}
}
}
_ => {}
_ => (),
}
if self.anim_counter == 0 {
@ -1002,7 +1002,7 @@ impl NPC {
self.npc_flags.set_solid_hard(true);
}
}
_ => {}
_ => (),
}
self.vel_y += 0x20;
@ -1061,14 +1061,14 @@ impl NPC {
npc.direction = Direction::Right;
let _ = npc_list.spawn(0, npc);
}
_ => {}
_ => (),
}
}
match self.direction {
Direction::Left => self.anim_rect = state.constants.npc.n125_hidden_item[0],
Direction::Right => self.anim_rect = state.constants.npc.n125_hidden_item[1],
_ => {}
_ => (),
}
Ok(())
@ -1119,7 +1119,7 @@ impl NPC {
return Ok(());
}
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n146_lighting[self.anim_num as usize];
@ -1243,7 +1243,7 @@ impl NPC {
self.vel_x += 0x20;
}
}
_ => {}
_ => (),
}
self.vel_x = clamp(self.vel_x, -0x200, 0x200);
@ -1387,7 +1387,7 @@ impl NPC {
self.vel_y += 0x20;
}
}
_ => {}
_ => (),
}
self.vel_y = clamp(self.vel_y, -0x200, 0x200);
@ -1401,6 +1401,134 @@ impl NPC {
Ok(())
}
pub(crate) fn tick_n189_homing_flame(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
) -> GameResult {
let player = self.get_closest_player_ref(&players);
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.vel_x = -0x40;
}
self.y += self.vel_y;
self.action_counter += 1;
if self.action_counter > 256 {
self.action_num = 10;
}
}
10 => {
self.vel_x += if player.x >= self.x { 8 } else { -8 };
self.vel_y += if player.y >= self.y { 8 } else { -8 };
self.vel_x = self.vel_x.clamp(-0x400, 0x400);
self.vel_y = self.vel_y.clamp(-0x400, 0x400);
self.x += self.vel_x;
self.y += self.vel_y;
}
_ => (),
}
if player.x >= self.x {
self.direction = Direction::Right;
} else {
self.direction = Direction::Left;
}
self.animate(2, 0, 2);
self.anim_rect = state.constants.npc.n189_homing_flame[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n190_broken_robot(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
match self.action_num {
0 => self.anim_num = 0,
10 => {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.x;
for _ in 0..8 {
npc.y = self.y + self.rng.range(-8..8) * 0x200;
npc.vel_x = self.rng.range(-8..-2) * 0x200;
npc.vel_y = self.rng.range(-3..3) * 0x200;
npc_list.spawn(0x100, npc.clone());
}
state.sound_manager.play_sfx(72);
self.cond.set_alive(false);
}
20 => self.animate(10, 0, 1),
_ => (),
}
self.anim_rect = state.constants.npc.n190_broken_robot[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n191_water_level(&mut self, state: &mut SharedGameState) -> GameResult {
match self.action_num {
0 | 10 => {
if self.action_num == 0 {
self.action_num = 10;
self.target_y = self.y;
self.vel_y = 0x200;
}
self.vel_y += if self.y >= self.target_y { -4 } else { 4 };
self.vel_y = self.vel_y.clamp(-0x100, 0x100);
self.y += self.vel_y;
}
20 | 21 => {
if self.action_num == 20 {
self.action_num = 21;
self.action_counter = 0;
}
self.vel_y += if self.y >= self.target_y { -4 } else { 4 };
self.vel_y = self.vel_y.clamp(-0x200, 0x200);
self.y += self.vel_y;
self.action_counter += 1;
if self.action_counter > 1000 {
self.action_num = 22;
}
}
22 => {
self.vel_y += if self.y >= 0 { -4 } else { 4 };
self.vel_y = self.vel_y.clamp(-0x200, 0x200);
self.y += self.vel_y;
if self.y <= 0x7FFF || state.npc_super_pos.1 > 0 {
self.action_num = 21;
self.action_counter = 0;
}
}
30 => {
self.vel_y += if self.y >= 0 { -4 } else { 4 };
self.vel_y = self.vel_y.clamp(-0x200, 0x100);
self.y += self.vel_y;
}
_ => (),
}
state.water_level = self.y;
self.anim_rect = Rect::new(0, 0, 0, 0);
Ok(())
}
pub(crate) fn tick_n194_broken_blue_robot(&mut self, state: &mut SharedGameState) -> GameResult {
if self.action_num == 0 {
self.action_num = 1;
@ -1468,6 +1596,27 @@ impl NPC {
Ok(())
}
pub(crate) fn tick_n219_smoke_generator(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
if self.direction != Direction::Left {
let mut npc = NPC::create(199, &state.npc_table);
npc.x = self.x + self.rng.range(-160..160) * 0x200;
npc.y = self.y + self.rng.range(-128..128) * 0x200;
npc.direction = Direction::Right;
let _ = npc_list.spawn(0x100, npc);
} else if self.rng.range(0..40) == 1 {
let mut npc = NPC::create(4, &state.npc_table);
npc.x = self.x + self.rng.range(-20..20) * 0x200;
npc.y = self.y;
let _ = npc_list.spawn(0x100, npc);
}
self.anim_rect = Rect::new(0, 0, 0, 0);
Ok(())
}
pub(crate) fn tick_n222_prison_bars(&mut self, state: &mut SharedGameState) -> GameResult {
if self.action_num == 0 {
self.y -= 0x1000;
@ -1594,7 +1743,7 @@ impl NPC {
Direction::Up => self.y -= 0x400,
Direction::Right => self.x += 0x400,
Direction::Bottom => self.y += 0x400,
_ => {}
_ => (),
},
30 => {
self.x = player.x;
@ -1625,7 +1774,7 @@ impl NPC {
self.y = (player.y + npc.y) / 2;
}
}
_ => {}
_ => (),
}
Ok(())
@ -1696,7 +1845,7 @@ impl NPC {
self.anim_num += 4;
self.action_num = 1;
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n351_statue_shootable[self.anim_num as usize % 9];
@ -1801,7 +1950,7 @@ impl NPC {
self.y = npc.y - 0x2600;
}
}
_ => {}
_ => (),
}
}

View file

@ -55,7 +55,7 @@ impl NPC {
self.animate(3, 2, 3);
}
_ => {}
_ => (),
}
self.x += self.vel_x;
@ -183,7 +183,7 @@ impl NPC {
self.action_num = 14;
}
}
_ => {}
_ => (),
}
self.x += self.vel_x;
@ -350,7 +350,7 @@ impl NPC {
50 => {
self.anim_num = 8;
}
_ => {}
_ => (),
}
self.x += self.vel_x;

View file

@ -1,24 +1,25 @@
pub mod balrog;
pub mod booster;
pub mod chaco;
pub mod characters;
pub mod curly;
pub mod doctor;
pub mod egg_corridor;
pub mod first_cave;
pub mod grasstown;
pub mod igor;
pub mod intro;
pub mod last_cave;
pub mod maze;
pub mod mimiga_village;
pub mod misc;
pub mod misery;
pub mod outer_wall;
pub mod pickups;
pub mod quote;
pub mod sand_zone;
pub mod santa;
pub mod sue;
pub mod toroko;
pub mod weapon_trail;
pub(in super) mod balrog;
pub(in super) mod booster;
pub(in super) mod chaco;
pub(in super) mod characters;
pub(in super) mod curly;
pub(in super) mod doctor;
pub(in super) mod egg_corridor;
pub(in super) mod first_cave;
pub(in super) mod grasstown;
pub(in super) mod igor;
pub(in super) mod intro;
pub(in super) mod last_cave;
pub(in super) mod maze;
pub(in super) mod mimiga_village;
pub(in super) mod misc;
pub(in super) mod misery;
pub(in super) mod outer_wall;
pub(in super) mod pickups;
pub(in super) mod plantation;
pub(in super) mod quote;
pub(in super) mod sand_zone;
pub(in super) mod santa;
pub(in super) mod sue;
pub(in super) mod toroko;
pub(in super) mod weapon_trail;

View file

@ -1,12 +1,226 @@
use num_traits::abs;
use crate::common::Direction;
use crate::framework::error::GameResult;
use crate::npc::list::NPCList;
use crate::npc::NPC;
use crate::player::Player;
use crate::rng::RNG;
use crate::shared_game_state::SharedGameState;
impl NPC {
pub(crate) fn tick_n215_sandcroc_outer_wall(&mut self, state: &mut SharedGameState, players: [&mut Player; 2]) -> GameResult {
pub(crate) fn tick_n212_sky_dragon(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
npc_list: &NPCList,
) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.y -= 0x800;
}
self.animate(30, 0, 1);
}
10 | 11 => {
if self.action_num == 10 {
self.action_num = 11;
self.anim_num = 2;
self.anim_counter = 0;
self.target_y = self.y - 0x2000;
self.target_x = self.x - 0xc00;
self.vel_y = 0;
self.npc_flags.set_ignore_solidity(true);
}
self.vel_x += if self.x >= self.target_x { -8 } else { 8 };
self.vel_y += if self.y >= self.target_y { -8 } else { 8 };
self.x += self.vel_x;
self.y += self.vel_y;
self.animate(5, 2, 3);
}
20 | 21 => {
if self.action_num == 20 {
self.action_num = 21;
self.npc_flags.set_ignore_solidity(true);
}
self.vel_y += if self.y >= self.target_y { -0x10 } else { 0x10 };
self.vel_x += 0x20;
self.vel_x = self.vel_x.clamp(-0x600, 0x600);
self.x += self.vel_x;
self.y += self.vel_y;
self.animate(2, 2, 3);
}
30 => {
self.action_num = 31;
let mut npc = NPC::create(297, &state.npc_table);
npc.cond.set_alive(true);
let _ = npc_list.spawn(0x100, npc);
}
_ => (),
}
self.anim_rect = state.constants.npc.n212_sky_dragon[self.anim_num as usize];
if players[0].equip.has_mimiga_mask() && self.anim_num > 1 {
self.anim_rect.top += 40;
self.anim_rect.bottom += 40;
}
Ok(())
}
pub(crate) fn tick_n213_night_spirit(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
npc_list: &NPCList,
) -> GameResult {
let player = self.get_closest_player_ref(&players);
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.anim_num = 0;
self.target_x = self.x;
self.target_y = self.y;
}
if player.y > self.y - 0x1000 && player.y < self.y + 0x1000 {
self.y += if self.direction != Direction::Left { 0x1E000 } else { -0x1E000 };
self.action_num = 10;
self.action_counter = 0;
self.anim_num = 1;
self.vel_y = 0;
self.npc_flags.set_shootable(true);
}
}
10 => {
self.animate(2, 1, 3);
self.action_counter += 1;
if self.action_counter > 200 {
self.action_num = 20;
self.action_counter = 0;
self.anim_num = 4;
}
}
20 => {
self.animate(2, 4, 6);
self.action_counter += 1;
if self.action_counter > 50 {
self.action_num = 30;
self.action_counter = 0;
self.anim_num = 7;
}
}
30 => {
self.animate(2, 7, 9);
self.action_counter += 1;
if self.action_counter % 5 == 1 {
let mut npc = NPC::create(214, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.x;
npc.y = self.y;
npc.vel_y = self.rng.range(-0x200..0x200);
npc.vel_x = self.rng.range(2..12) * 0x80;
let _ = npc_list.spawn(0x100, npc);
state.sound_manager.play_sfx(21);
}
if self.action_counter > 50 {
self.action_num = 10;
self.action_counter = 0;
self.anim_num = 1;
}
}
40 => {
self.vel_y += if self.y >= self.target_y { -0x40 } else { 0x40 };
self.vel_y = self.vel_y.clamp(-0x400, 0x400);
self.y += if self.shock > 0 { self.vel_y / 2 } else { self.vel_y };
self.animate(2, 4, 6);
if player.y < self.target_y + 0x1E000 && player.y > self.target_y - 0x1E000 {
self.action_num = 20;
self.action_counter = 0;
self.anim_num = 4;
}
}
_ => (),
}
if self.action_num > 9 && self.action_num <= 30 {
self.vel_y += if self.y >= player.y { -0x19 } else { 0x19 };
self.vel_y = self.vel_y.clamp(-0x400, 0x400);
if self.flags.hit_top_wall() {
self.vel_y = 0x200;
}
if self.flags.hit_bottom_wall() {
self.vel_y = -0x200;
}
self.y += if self.shock > 0 { self.vel_y / 2 } else { self.vel_y };
if player.y > self.target_y + 0x1E000 || player.y < self.target_y - 0x1E000 {
self.action_num = 40;
}
}
self.anim_rect = state.constants.npc.n213_night_spirit[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n214_night_spirit_projectile(
&mut self,
state: &mut SharedGameState,
npc_list: &NPCList,
) -> GameResult {
if self.action_num == 0 {
self.action_num = 1;
self.npc_flags.set_ignore_solidity(true);
}
if self.action_num == 1 {
self.animate(2, 0, 2);
self.vel_x -= 0x19;
self.x += self.vel_x;
self.y += self.vel_y;
if self.vel_x < 0 {
self.npc_flags.set_ignore_solidity(false);
}
if self.flags.hit_anything() {
npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right as usize, 4, state, &self.rng);
state.sound_manager.play_sfx(28);
self.cond.set_alive(false);
}
}
self.anim_rect = state.constants.npc.n214_night_spirit_projectile[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n215_sandcroc_outer_wall(
&mut self,
state: &mut SharedGameState,
players: [&mut Player; 2],
) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
@ -47,7 +261,7 @@ impl NPC {
self.action_counter = 0;
self.npc_flags.set_shootable(true);
}
_ => {}
_ => (),
}
}
30 => {
@ -80,7 +294,7 @@ impl NPC {
self.action_counter += 1;
}
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n215_sandcroc_outer_wall[self.anim_num as usize];

View file

@ -187,7 +187,7 @@ impl NPC {
3 => {
self.anim_rect = state.constants.npc.n086_missile_pickup[2 + self.anim_num as usize];
}
_ => {}
_ => (),
}
if self.action_counter2 > 550 {
@ -255,7 +255,7 @@ impl NPC {
6 => {
self.anim_rect = state.constants.npc.n087_heart_pickup[2 + self.anim_num as usize];
}
_ => {}
_ => (),
}
if self.action_counter2 > 550 {

289
src/npc/ai/plantation.rs Normal file
View file

@ -0,0 +1,289 @@
use crate::common::Direction;
use crate::framework::error::GameResult;
use crate::npc::NPC;
use crate::player::Player;
use crate::rng::RNG;
use crate::shared_game_state::SharedGameState;
impl NPC {
pub(crate) fn tick_n220_shovel_brigade(&mut self, state: &mut SharedGameState) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.anim_num = 0;
self.anim_counter = 0;
}
if self.rng.range(0..120) == 10 {
self.action_num = 2;
self.action_counter = 0;
self.anim_num = 1;
}
}
2 => {
self.action_counter += 1;
if self.action_counter > 8 {
self.action_num = 1;
self.anim_num = 0;
}
}
_ => (),
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
self.anim_rect = state.constants.npc.n220_shovel_brigade[self.anim_num as usize + dir_offset];
Ok(())
}
pub(crate) fn tick_n221_shovel_brigade_walking(&mut self, state: &mut SharedGameState) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.anim_num = 0;
self.anim_counter = 0;
self.vel_x = 0;
}
if self.rng.range(0..60) == 1 {
self.action_num = 2;
self.action_counter = 0;
self.anim_num = 1;
}
if self.rng.range(0..60) == 1 {
self.action_num = 10;
self.action_counter = 0;
self.anim_num = 1;
}
}
2 => {
self.action_counter += 1;
if self.action_counter > 8 {
self.action_num = 1;
self.anim_num = 0;
}
}
10 | 11 => {
if self.action_num == 10 {
self.action_num = 11;
self.action_counter = self.rng.range(0..16) as u16;
self.anim_num = 2;
self.anim_counter = 0;
if (self.rng.range(0..9) & 1) != 0 {
self.direction = Direction::Left;
} else {
self.direction = Direction::Right;
}
}
if self.direction != Direction::Left || !self.flags.hit_left_wall() {
if self.direction == Direction::Right && self.flags.hit_right_wall() {
self.direction = Direction::Left;
}
} else {
self.direction = Direction::Right;
}
if self.direction != Direction::Left {
self.vel_x = 0x200;
} else {
self.vel_x = -0x200;
}
self.animate(4, 2, 5);
self.action_counter += 1;
if self.action_counter > 32 {
self.action_num = 0;
}
}
_ => (),
}
self.vel_y += 0x20;
if self.vel_y > 0x5FF {
self.vel_y = 0x5FF;
}
self.x += self.vel_x;
self.y += self.vel_y;
let dir_offset = if self.direction == Direction::Left { 0 } else { 6 };
self.anim_rect = state.constants.npc.n221_shovel_brigade_walking[self.anim_num as usize + dir_offset];
Ok(())
}
pub(crate) fn tick_n223_momorin(&mut self, state: &mut SharedGameState, players: [&mut Player; 2]) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.anim_num = 0;
self.anim_counter = 0;
}
if self.rng.range(0..160) == 1 {
self.action_num = 2;
self.action_counter = 0;
self.anim_num = 1;
}
}
2 => {
self.action_counter += 1;
if self.action_counter > 12 {
self.action_num = 1;
self.anim_num = 0;
}
}
3 => {
self.anim_num = 2;
}
_ => (),
}
let player = self.get_closest_player_ref(&players);
if self.action_num <= 1 && player.y < self.y + 0x2000 && player.y > self.y - 0x2000 {
if player.x >= self.x {
self.direction = Direction::Right;
} else {
self.direction = Direction::Left;
}
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 3 };
self.anim_rect = state.constants.npc.n223_momorin[self.anim_num as usize + dir_offset];
Ok(())
}
pub(crate) fn tick_n224_chie(&mut self, state: &mut SharedGameState, players: [&mut Player; 2]) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.anim_num = 0;
self.anim_counter = 0;
}
if self.rng.range(0..160) == 1 {
self.action_num = 2;
self.action_counter = 0;
self.anim_num = 1;
}
}
2 => {
self.action_counter += 1;
if self.action_counter > 12 {
self.action_num = 1;
self.anim_num = 0;
}
}
_ => (),
}
let player = self.get_closest_player_ref(&players);
if self.action_num <= 1 && player.y < self.y + 0x2000 && player.y > self.y - 0x2000 {
if player.x >= self.x {
self.direction = Direction::Right;
} else {
self.direction = Direction::Left;
}
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
self.anim_rect = state.constants.npc.n224_chie[self.anim_num as usize + dir_offset];
Ok(())
}
pub(crate) fn tick_n225_megane(&mut self, state: &mut SharedGameState) -> GameResult {
if self.action_num == 0 {
self.action_num = 1;
self.anim_num = 0;
self.anim_counter = 0;
}
if self.action_num == 1 {
if self.rng.range(0..160) == 1 {
self.action_num = 2;
self.action_counter = 0;
self.anim_num = 1;
}
} else if self.action_num == 2 {
self.action_counter += 1;
if self.action_counter > 12 {
self.action_num = 1;
self.anim_num = 0;
}
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
self.anim_rect = state.constants.npc.n225_megane[self.anim_num as usize + dir_offset];
Ok(())
}
pub(crate) fn tick_n226_kanpachi_plantation(&mut self, state: &mut SharedGameState) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.anim_num = 0;
self.anim_counter = 0;
self.vel_x = 0;
}
if self.rng.range(0..60) == 1 {
self.action_num = 2;
self.action_counter = 0;
self.anim_num = 1;
}
}
2 => {
self.action_counter += 1;
if self.action_counter > 8 {
self.action_num = 1;
self.anim_num = 0;
}
}
10 | 11 => {
if self.action_num == 10 {
self.action_num = 11;
self.anim_num = 2;
self.anim_counter = 0;
}
self.vel_x = 0x200;
self.animate(4, 2, 5);
self.action_counter += 1;
}
20 => {
self.vel_x = 0;
self.anim_num = 6;
}
_ => (),
}
self.vel_y += 0x20;
if self.vel_y > 0x5FF {
self.vel_y = 0x5FF;
}
self.x += self.vel_x;
self.y += self.vel_y;
self.anim_rect = state.constants.npc.n226_kanpachi_plantation[self.anim_num as usize];
Ok(())
}
}

View file

@ -52,7 +52,7 @@ impl NPC {
self.cond.set_alive(false);
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -109,7 +109,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -270,7 +270,7 @@ impl NPC {
}
}
}
_ => {}
_ => (),
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 9 };

View file

@ -109,7 +109,7 @@ impl NPC {
self.action_num = 6;
}
}
_ => {}
_ => (),
}
if self.life <= 100 {
@ -175,7 +175,7 @@ impl NPC {
self.anim_num = 1;
}
}
_ => {}
_ => (),
}
if self.vel_x2 < 0 && self.flags.hit_left_wall() {
@ -247,7 +247,7 @@ impl NPC {
self.action_counter = 0;
self.npc_flags.set_shootable(true);
}
_ => {}
_ => (),
}
}
3 => {
@ -280,7 +280,7 @@ impl NPC {
self.action_counter += 1;
}
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n047_sandcroc[self.anim_num as usize];
@ -382,7 +382,7 @@ impl NPC {
self.anim_num = 1;
}
}
_ => {}
_ => (),
}
if self.action_num > 9 {
@ -458,7 +458,7 @@ impl NPC {
}
}
}
_ => {}
_ => (),
}
self.vel_y = clamp(self.vel_y, -0x5ff, 0x5ff);
@ -527,7 +527,7 @@ impl NPC {
self.vel_y += 0x20;
}
}
_ => {}
_ => (),
}
if self.vel_x < 0 && self.flags.hit_left_wall() {
@ -678,7 +678,7 @@ impl NPC {
);
}
}
_ => {}
_ => (),
}
self.vel_y += 0x80;
@ -756,7 +756,7 @@ impl NPC {
self.anim_counter = 0;
}
}
_ => {}
_ => (),
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 3 };
@ -836,7 +836,7 @@ impl NPC {
self.vel_x = clamp(self.vel_x, -0x5ff, 0x5ff);
self.vel_y = clamp(self.vel_y, -0x5ff, 0x5ff);
}
_ => {}
_ => (),
}
self.x += self.vel_x;
@ -897,7 +897,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n121_colon_b[self.anim_num as usize];
@ -1020,7 +1020,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
if self.action_num > 10 && self.action_num < 20 && self.life != 1000 {
self.action_num = 20;
@ -1079,7 +1079,7 @@ impl NPC {
state.sound_manager.play_sfx(26);
}
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n124_sunstone[self.anim_num as usize];
@ -1159,7 +1159,7 @@ impl NPC {
self.vel_x = -0x400;
}
}
_ => {}
_ => (),
}
// why
@ -1234,7 +1234,7 @@ impl NPC {
}
}
}
_ => {}
_ => (),
}
self.action_counter += 1;
@ -1350,7 +1350,7 @@ impl NPC {
state.sound_manager.play_sfx(105);
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -1390,7 +1390,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
@ -1428,7 +1428,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
// todo dog stacking?
@ -1511,7 +1511,7 @@ impl NPC {
self.npc_flags.set_invulnerable(false);
}
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
if self.vel_y > 0x5FF {
@ -1604,7 +1604,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
if self.action_num > 9 {

View file

@ -62,7 +62,7 @@ impl NPC {
5 => {
self.anim_num = 6;
}
_ => {}
_ => (),
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 7 };

View file

@ -205,7 +205,7 @@ impl NPC {
self.anim_num = 9;
self.vel_y = -0x400;
}
_ => {}
_ => (),
}
if self.action_num != 14 {
@ -275,7 +275,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
self.anim_rect = state.constants.npc.n092_sue_at_pc[self.anim_num as usize];

View file

@ -148,7 +148,7 @@ impl NPC {
12 => {
self.vel_x = 0;
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -252,7 +252,7 @@ impl NPC {
self.vel_x = 0;
self.anim_num = 5;
}
_ => {}
_ => (),
}
self.vel_y += 0x40;
@ -560,7 +560,7 @@ impl NPC {
self.cond.set_alive(false);
}
}
_ => {}
_ => (),
}
if self.action_num > 100 && self.action_num <= 104 && (self.action_counter % 9) == 0 {
@ -744,7 +744,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
self.vel_y += 64;
self.vel_y = self.vel_y.clamp(-0x5ff, 0x5ff);
@ -807,7 +807,7 @@ impl NPC {
self.anim_num = 0;
}
}
_ => {}
_ => (),
}
if self.action_num > 1 {

View file

@ -24,7 +24,7 @@ impl NPC {
Direction::Up | Direction::Bottom => {
self.anim_rect = state.constants.npc.n127_machine_gun_trail_l2[self.anim_num as usize + 3];
}
_ => {}
_ => (),
}
Ok(())
@ -54,7 +54,7 @@ impl NPC {
self.display_bounds.left = 0x1000;
self.display_bounds.top = 0x800;
}
_ => {}
_ => (),
}
}
@ -71,7 +71,7 @@ impl NPC {
Direction::Bottom => {
self.anim_rect = state.constants.npc.n128_machine_gun_trail_l3[self.anim_num as usize + 15];
}
_ => {}
_ => (),
}
Ok(())

View file

@ -1,12 +1,96 @@
use crate::caret::CaretType;
use crate::common::{Direction, CDEG_RAD};
use crate::framework::error::GameResult;
use crate::npc::boss::BossNPC;
use crate::npc::list::NPCList;
use crate::npc::NPC;
use crate::player::Player;
use crate::rng::RNG;
use crate::shared_game_state::SharedGameState;
use crate::stage::Stage;
impl NPC {
pub(crate) fn tick_n178_core_blade_projectile(&mut self, state: &mut SharedGameState) -> GameResult {
if self.flags.hit_anything() {
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
self.cond.set_alive(false);
}
if self.flags.in_water() {
self.x += self.vel_x / 2;
self.y += self.vel_y / 2;
} else {
self.x += self.vel_x;
self.y += self.vel_y;
}
self.animate(1, 0, 2);
self.action_counter3 += 1;
if self.action_counter3 > 150 {
self.vanish(state);
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
}
self.anim_rect = state.constants.npc.n178_core_blade_projectile[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n179_core_wisp_projectile(&mut self, state: &mut SharedGameState) -> GameResult {
if self.flags.hit_anything() {
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
self.cond.set_alive(false);
}
self.vel_x -= 0x20;
self.vel_y = 0;
if self.vel_x < -0x400 {
self.vel_x = -0x400;
}
self.x += self.vel_x;
self.y += self.vel_y;
self.animate(1, 0, 2);
self.action_counter3 += 1;
if self.action_counter3 > 300 {
self.vanish(state);
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
}
self.anim_rect = state.constants.npc.n179_core_wisp_projectile[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n218_core_giant_ball(&mut self, state: &mut SharedGameState) -> GameResult
{
self.x += self.vel_x;
self.y += self.vel_y;
self.action_counter += 1;
if self.action_counter > 200 {
self.cond.set_alive(false);
}
self.animate(2, 0, 1);
self.anim_rect = state.constants.npc.n218_core_giant_ball[self.anim_num as usize];
Ok(())
}
}
impl BossNPC {
pub(crate) fn tick_b04_core(&mut self, state: &mut SharedGameState, players: [&mut Player; 2], npc_list: &NPCList) {
pub(crate) fn tick_b04_core(
&mut self,
state: &mut SharedGameState,
mut players: [&mut Player; 2],
npc_list: &NPCList,
stage: &mut Stage,
) {
let mut flag = false;
// i will refactor that one day
#[allow(mutable_transmutes)]
@ -83,21 +167,27 @@ impl BossNPC {
self.parts[2] = self.parts[1].clone();
self.parts[2].x = self.parts[0].x + 0x2000;
self.parts[2].x = self.parts[0].y;
self.parts[2].y = self.parts[0].y;
self.parts[3] = self.parts[1].clone();
self.parts[3].x = self.parts[0].x - 0x1000;
self.parts[3].x = self.parts[0].y + 0x8000;
self.parts[3].y = self.parts[0].y + 0x8000;
self.parts[6] = self.parts[1].clone();
self.parts[6].x = self.parts[0].x - 0x6000;
self.parts[6].x = self.parts[0].y - 0x4000;
self.parts[6].y = self.parts[0].y - 0x4000;
self.parts[7] = self.parts[1].clone();
self.parts[7].x = self.parts[0].x - 0x6000;
self.parts[7].x = self.parts[0].y + 0x4000;
self.parts[7].y = self.parts[0].y + 0x4000;
for part in self.parts.iter_mut() {
part.prev_x = part.x;
part.prev_y = part.y;
}
200 => {
}
200 | 201 => {
if self.parts[0].action_num == 200 {
self.parts[0].action_num = 201;
self.parts[0].action_counter = 0;
self.parts[11].npc_flags.set_shootable(false);
@ -107,13 +197,14 @@ impl BossNPC {
state.sound_manager.stop_sfx(41);
state.sound_manager.stop_sfx(58);
}
201 => {
self.parts[0].target_x = self.parts[0].x;
self.parts[0].target_y = self.parts[0].y;
let idx = self.parts[0].get_closest_player_idx_mut(&players);
self.parts[0].target_x = players[idx].x;
self.parts[0].target_y = players[idx].y;
self.parts[0].action_counter += 1;
if self.parts[0].action_counter > 400 {
self.parts[0].action_counter2 += 2;
self.parts[0].action_counter2 += 1;
state.sound_manager.play_sfx(115);
@ -123,24 +214,24 @@ impl BossNPC {
self.parts[0].action_counter2 = 0;
self.parts[0].action_num = 220;
}
}
self.parts[4].anim_num = 0;
self.parts[5].anim_num = 0;
flag = true;
}
}
210 | 211 => {
if self.parts[0].action_num == 210 {
self.parts[0].action_num = 211;
self.parts[0].action_counter = 0;
self.parts[0].action_counter2 = self.parts[0].life;
self.parts[0].action_counter3 = self.parts[0].life;
self.parts[11].npc_flags.set_shootable(true);
}
let player = self.parts[0].get_closest_player_mut(players);
self.parts[0].target_x = player.x;
self.parts[0].target_y = player.y;
let idx = self.parts[0].get_closest_player_idx_mut(&players);
self.parts[0].target_x = players[idx].x;
self.parts[0].target_y = players[idx].y;
if self.parts[0].shock > 0 {
*flash_counter += 1;
@ -171,7 +262,7 @@ impl BossNPC {
}
if self.parts[0].action_counter > 400
|| (self.parts[0].life as i32) < self.parts[0].action_counter2 as i32 - 200
|| (self.parts[0].life as i32) < self.parts[0].action_counter3 as i32 - 200
{
self.parts[0].action_num = 200;
self.parts[4].anim_num = 2;
@ -200,7 +291,7 @@ impl BossNPC {
npc.y = players[idx].y + self.parts[0].rng.range(-160..160) * 0x200;
let _ = npc_list.spawn(0x100, npc);
for player in players {
for player in players.iter_mut() {
player.vel_x -= 0x20;
player.cond.set_increase_acceleration(true);
}
@ -238,6 +329,53 @@ impl BossNPC {
flag = true;
}
}
500 | 501 => {
if self.parts[0].action_num == 500 {
self.parts[0].action_num = 501;
self.parts[0].action_counter = 0;
self.parts[0].vel_x = 0;
self.parts[0].vel_y = 0;
self.parts[4].anim_num = 2;
self.parts[5].anim_num = 0;
self.parts[1].action_num = 200;
self.parts[2].action_num = 200;
self.parts[3].action_num = 200;
self.parts[6].action_num = 200;
self.parts[7].action_num = 200;
state.quake_counter = 20;
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
for _ in 0..32 {
npc.x = self.parts[0].x + self.parts[0].rng.range(-0x80..0x80) * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-0x40..0x40) * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x80..0x80) * 0x200;
npc.vel_y = self.parts[0].rng.range(-0x80..0x80) * 0x200;
let _ = npc_list.spawn(0x100, npc.clone());
}
for i in 0..12 {
self.parts[i].npc_flags.set_invulnerable(false);
self.parts[i].npc_flags.set_shootable(false);
}
}
self.parts[0].action_counter += 1;
if (self.parts[0].action_counter & 0x0f) != 0 {
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(-0x40..0x40) * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-0x20..0x20) * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x80..0x80) * 0x200;
npc.vel_y = self.parts[0].rng.range(-0x80..0x80) * 0x200;
let _ = npc_list.spawn(0x100, npc);
}
}
_ => {}
}
@ -258,21 +396,21 @@ impl BossNPC {
npc.x = self.parts[4].x + self.parts[0].rng.range(-32..16) * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x200..0x200);
npc.vel_y = self.parts[0].rng.range(-0x100..0x100);
npc_list.spawn(0x100, npc.clone());
let _ = npc_list.spawn(0x100, npc.clone());
}
}
if self.parts[0].action_num >= 200 && self.parts[0].action_num < 300 {
if self.parts[0].action_counter == 140 {
if self.parts[0].action_counter == 80 {
self.parts[1].action_num = 120;
} else if self.parts[0].action_counter == 110 {
self.parts[2].action_num = 120;
} else if self.parts[0].action_counter == 140 {
self.parts[3].action_num = 120;
} else if self.parts[0].action_counter == 170 {
self.parts[6].action_num = 120;
} else if self.parts[0].action_counter == 200 {
self.parts[7].action_num = 120;
} else if self.parts[0].action_counter == 80 {
self.parts[1].action_num = 120;
} else if self.parts[0].action_counter == 110 {
self.parts[2].action_num = 120;
}
if self.parts[0].x < self.parts[0].target_x + 0x14000 {
@ -292,9 +430,251 @@ impl BossNPC {
}
}
self.parts[0].vel_x = self.parts[0].vel_x.clamp(-0x100, 0x100);
self.parts[0].vel_y = self.parts[0].vel_y.clamp(-0x100, 0x100);
self.parts[0].vel_x = self.parts[0].vel_x.clamp(-0x80, 0x80);
self.parts[0].vel_y = self.parts[0].vel_y.clamp(-0x80, 0x80);
self.parts[0].x += self.parts[0].vel_x;
self.parts[0].y += self.parts[0].vel_y;
self.tick_b04_core_face(4, state);
self.tick_b04_core_tail(5, state);
self.tick_b04_core_small_head(1, state, &players, npc_list, stage);
self.tick_b04_core_small_head(2, state, &players, npc_list, stage);
self.tick_b04_core_small_head(3, state, &players, npc_list, stage);
self.tick_b04_core_small_head(6, state, &players, npc_list, stage);
self.tick_b04_core_small_head(7, state, &players, npc_list, stage);
self.tick_b04_core_hitbox(8, state);
self.tick_b04_core_hitbox(9, state);
self.tick_b04_core_hitbox(10, state);
self.tick_b04_core_hitbox(11, state);
}
fn tick_b04_core_face(&mut self, i: usize, state: &mut SharedGameState) {
let (head, tail) = self.parts.split_at_mut(i);
let base = &mut head[0];
let part = &mut tail[0];
match part.action_num {
10 | 11 => {
if part.action_num == 10 {
part.action_num = 11;
part.anim_num = 2;
part.npc_flags.set_ignore_solidity(true);
part.display_bounds.left = 0x4800;
part.display_bounds.top = 0x7000;
}
part.x = base.x - 0x4800;
part.y = base.y;
}
50 | 51 => {
if part.action_num == 50 {
part.action_num = 51;
part.action_counter = 112;
}
part.action_counter -= 1;
if part.action_counter == 0 {
part.action_num = 100;
part.anim_num = 3;
}
part.x = base.x - 0x4800;
part.y = base.y;
}
100 => {
part.anim_num = 3;
}
_ => {}
}
if part.action_num == 51 {
part.anim_rect.bottom = part.action_counter + part.anim_rect.top;
}
part.anim_rect = state.constants.npc.b04_core[part.anim_num as usize];
}
fn tick_b04_core_tail(&mut self, i: usize, state: &mut SharedGameState) {
let (head, tail) = self.parts.split_at_mut(i);
let base = &mut head[0];
let part = &mut tail[0];
match part.action_num {
10 | 11 => {
if part.action_num == 10 {
part.action_num = 11;
part.anim_num = 0;
part.npc_flags.set_ignore_solidity(true);
part.display_bounds.left = 0x5800;
part.display_bounds.top = 0x7000;
}
part.x = base.x + 0x5800;
part.y = base.y;
}
50 | 51 => {
if part.action_num == 50 {
part.action_num = 51;
part.action_counter = 112;
}
part.action_counter -= 1;
if part.action_counter == 0 {
part.action_num = 100;
part.anim_num = 2;
}
part.x = base.x + 0x5800;
part.y = base.y;
}
100 => {
part.anim_num = 2;
}
_ => {}
}
if part.action_num == 51 {
part.anim_rect.bottom = part.action_counter + part.anim_rect.top;
}
part.anim_rect = state.constants.npc.b04_core[4 + part.anim_num as usize];
}
fn tick_b04_core_small_head(
&mut self,
i: usize,
state: &mut SharedGameState,
players: &[&mut Player; 2],
npc_list: &NPCList,
stage: &Stage,
) {
let (head, tail) = self.parts.split_at_mut(i);
let base = &mut head[0];
let part = &mut tail[0];
part.life = 1000;
match part.action_num {
10 => {
part.anim_num = 2;
part.npc_flags.set_shootable(false);
}
100 | 101 => {
if part.action_num == 100 {
part.action_num = 101;
part.anim_num = 2;
part.action_counter = 0;
part.target_x = base.x + (base.rng.range(-128..32) * 0x200);
part.target_y = base.y + (base.rng.range(-64..64) * 0x200);
part.npc_flags.set_shootable(true);
}
part.x += (part.target_x - part.x) / 16;
part.y += (part.target_y - part.y) / 16;
part.action_counter += 1;
if part.action_counter > 50 {
part.anim_num = 0;
}
}
120 | 121 => {
if part.action_num == 120 {
part.action_num = 121;
part.action_counter = 0;
}
part.action_counter += 1;
if ((part.action_counter / 2) & 1) != 0 {
part.anim_num = 0;
} else {
part.anim_num = 1;
}
if part.action_counter > 20 {
part.action_num = 130;
}
}
130 | 131 => {
if part.action_num == 130 {
part.action_num = 131;
part.anim_num = 2;
part.action_counter = 0;
part.target_x = part.x + (part.rng.range(24..48) * 0x200);
part.target_y = part.y + (part.rng.range(-4..4) * 0x200);
}
part.x += (part.target_x - part.x) / 16;
part.y += (part.target_y - part.y) / 16;
part.action_counter += 1;
if part.action_counter > 50 {
part.action_num = 140;
part.anim_num = 0;
}
if part.action_counter == 1 || part.action_counter == 3 {
let player_idx = part.get_closest_player_idx_mut(players);
let px = part.x - players[player_idx].x;
let py = part.y - players[player_idx].y;
let deg = f64::atan2(py as f64, px as f64) + part.rng.range(-2..2) as f64 * CDEG_RAD;
let mut npc = NPC::create(178, &state.npc_table);
npc.cond.set_alive(true);
npc.x = part.x;
npc.y = part.y;
npc.vel_x = (deg.cos() * -1024.0) as i32;
npc.vel_y = (deg.sin() * -1024.0) as i32;
let _ = npc_list.spawn(0x100, npc);
state.sound_manager.play_sfx(39);
}
}
140 => {
part.x += (part.target_x - part.x) / 16;
part.y += (part.target_y - part.y) / 16;
}
200 | 201 => {
if part.action_num == 200 {
part.action_num = 201;
part.anim_num = 2;
part.vel_x = 0;
part.vel_y = 0;
}
part.vel_x += 32;
part.x += part.vel_x;
if part.x > (stage.map.width as i32 * 0x2000) + 0x4000 {
part.cond.set_alive(false);
}
}
_ => (),
}
if part.shock > 0 {
part.target_x += 1024;
}
part.anim_rect = state.constants.npc.b04_core[7 + part.anim_num as usize];
}
fn tick_b04_core_hitbox(&mut self, i: usize, state: &mut SharedGameState) {
let (head, tail) = self.parts.split_at_mut(i);
let base = &mut head[0];
let part = &mut tail[0];
match part.action_counter2 {
0 => {
part.x = base.x;
part.y = base.y - 0x4000;
}
1 => {
part.x = base.x + 0x3800;
part.y = base.y;
}
2 => {
part.x = base.x + 0x800;
part.y = base.y + 0x4000;
}
3 => {
part.x = base.x - 0x3800;
part.y = base.y + 0x800;
}
_ => (),
}
}
}

View file

@ -1,7 +1,100 @@
use crate::common::Direction;
use crate::framework::error::GameResult;
use crate::npc::boss::BossNPC;
use crate::npc::NPC;
use crate::shared_game_state::SharedGameState;
use crate::rng::RNG;
impl NPC {
pub(crate) fn tick_n196_ironhead_wall(&mut self, state: &mut SharedGameState) -> GameResult {
self.x -= 0xC00;
if self.x <= 0x26000 {
self.x += 0x2C000;
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 1 };
self.anim_rect = state.constants.npc.n196_ironhead_wall[dir_offset];
Ok(())
}
pub(crate) fn tick_n197_porcupine_fish(&mut self, state: &mut SharedGameState) -> GameResult {
match self.action_num {
0 | 10 => {
if self.action_num == 0 {
self.action_num = 10;
self.anim_counter = 0;
self.vel_y = self.rng.range(-0x200..0x200);
self.vel_x = 0x800;
}
self.animate(2, 0, 1);
if self.vel_x < 0 {
self.damage = 3;
self.action_num = 20;
}
}
20 => {
self.damage = 3;
self.animate(0, 2, 3);
if self.x <= 0x5FFF {
// npc->destroy_voice = 0; // todo
self.cond.set_explode_die(true);
}
}
_ => (),
}
if self.flags.hit_top_wall() {
self.vel_y = 0x200;
}
if self.flags.hit_bottom_wall() {
self.vel_y = -0x200;
}
self.vel_x -= 0xC;
self.x += self.vel_x;
self.y += self.vel_y;
self.anim_rect = state.constants.npc.n197_porcupine_fish[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n198_ironhead_projectile(&mut self, state: &mut SharedGameState) -> GameResult {
if self.action_num == 0 {
self.action_counter += 1;
if self.action_counter > 20 {
self.action_num = 1;
self.vel_x = 0;
self.vel_y = 0;
self.action_counter3 = 0;
}
} else if self.action_num == 1 {
self.vel_x += 0x20;
}
self.animate(0, 0, 2);
self.x += self.vel_x;
self.y += self.vel_y;
self.anim_rect = state.constants.npc.n198_ironhead_projectile[self.anim_num as usize];
self.action_counter3 += 1;
if self.action_counter3 > 100 {
self.cond.set_alive(false);
}
if self.action_counter3 % 4 == 1 {
state.sound_manager.play_sfx(46);
}
Ok(())
}
}
impl BossNPC {
pub(crate) fn tick_b05_ironhead(&mut self) {
}
pub(crate) fn tick_b05_ironhead(&mut self) {}
}

View file

@ -65,7 +65,7 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager, &mut Fl
fn tick(
&mut self,
state: &mut SharedGameState,
(players, npc_list, _stage, bullet_manager, flash): (
(players, npc_list, stage, bullet_manager, flash): (
[&mut Player; 2],
&NPCList,
&mut Stage,
@ -81,7 +81,7 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager, &mut Fl
1 => self.tick_b01_omega(state, players, npc_list, bullet_manager, flash),
2 => self.tick_b02_balfrog(state, players, npc_list),
3 => self.tick_b03_monster_x(state, players, npc_list, flash),
4 => self.tick_b04_core(state, players, npc_list),
4 => self.tick_b04_core(state, players, npc_list, stage),
5 => self.tick_b05_ironhead(),
6 => self.tick_b06_twins(),
7 => self.tick_b07_undead_core(),

View file

@ -1,13 +1,13 @@
use std::io;
use std::io::Cursor;
use byteorder::{LE, ReadBytesExt};
use byteorder::{ReadBytesExt, LE};
use num_traits::abs;
use crate::bitfield;
use crate::common::{Condition, interpolate_fix9_scale, Rect};
use crate::common::Direction;
use crate::common::Flag;
use crate::common::{interpolate_fix9_scale, Condition, Rect};
use crate::components::flash::Flash;
use crate::components::number_popup::NumberPopup;
use crate::entity::GameEntity;
@ -185,7 +185,7 @@ impl NPC {
}
}
impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager, &mut Flash)> for NPC {
impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mut Flash)> for NPC {
fn tick(
&mut self,
state: &mut SharedGameState,
@ -193,7 +193,7 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager, &mut Fl
[&mut Player; 2],
&NPCList,
&mut Stage,
&BulletManager,
&mut BulletManager,
&mut Flash,
),
) -> GameResult {
@ -383,23 +383,55 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager, &mut Fl
175 => self.tick_n175_gaudi_egg(state),
176 => self.tick_n176_buyo_buyo_base(state, players, npc_list),
177 => self.tick_n177_buyo_buyo(state, players),
178 => self.tick_n178_core_blade_projectile(state),
179 => self.tick_n179_core_wisp_projectile(state),
180 => self.tick_n180_curly_ai(state, players, npc_list),
181 => self.tick_n181_curly_ai_machine_gun(state, npc_list, bullet_manager),
182 => self.tick_n182_curly_ai_polar_star(state, npc_list, bullet_manager),
183 => self.tick_n183_curly_air_tank_bubble(state, npc_list),
184 => self.tick_n184_shutter(state, npc_list),
185 => self.tick_n185_small_shutter(state),
186 => self.tick_n186_lift_block(state),
187 => self.tick_n187_fuzz_core(state, players, npc_list),
188 => self.tick_n188_fuzz(state, players, npc_list),
189 => self.tick_n189_homing_flame(state, players),
190 => self.tick_n190_broken_robot(state, npc_list),
191 => self.tick_n191_water_level(state),
192 => self.tick_n192_scooter(state),
193 => self.tick_n193_broken_scooter(state),
194 => self.tick_n194_broken_blue_robot(state),
195 => self.tick_n195_background_grate(state),
196 => self.tick_n196_ironhead_wall(state),
197 => self.tick_n197_porcupine_fish(state),
198 => self.tick_n198_ironhead_projectile(state),
199 => self.tick_n199_wind_particles(state),
200 => self.tick_n200_zombie_dragon(state, players, npc_list),
201 => self.tick_n201_zombie_dragon_dead(state),
202 => self.tick_n202_zombie_dragon_projectile(state),
203 => self.tick_n203_critter_destroyed_egg_corridor(state, players),
204 => self.tick_n204_small_falling_spike(state, players, npc_list),
205 => self.tick_n205_large_falling_spike(state, players, npc_list, bullet_manager),
206 => self.tick_n206_counter_bomb(state, players, npc_list),
207 => self.tick_n207_counter_bomb_countdown(state),
208 => self.tick_n208_basu_destroyed_egg_corridor(state, players, npc_list),
209 => self.tick_n209_basu_projectile_destroyed_egg_corridor(state),
210 => self.tick_n210_beetle_destroyed_egg_corridor(state, players),
211 => self.tick_n211_small_spikes(state),
212 => self.tick_n212_sky_dragon(state, players, npc_list),
213 => self.tick_n213_night_spirit(state, players, npc_list),
214 => self.tick_n214_night_spirit_projectile(state, npc_list),
215 => self.tick_n215_sandcroc_outer_wall(state, players),
216 => self.tick_n216_debug_cat(state),
217 => self.tick_n217_itoh(state),
218 => self.tick_n218_core_giant_ball(state),
219 => self.tick_n219_smoke_generator(state, npc_list),
220 => self.tick_n220_shovel_brigade(state),
221 => self.tick_n221_shovel_brigade_walking(state),
222 => self.tick_n222_prison_bars(state),
223 => self.tick_n223_momorin(state, players),
224 => self.tick_n224_chie(state, players),
225 => self.tick_n225_megane(state),
226 => self.tick_n226_kanpachi_plantation(state),
227 => self.tick_n227_bucket(state),
229 => self.tick_n229_red_flowers_sprouts(state),
230 => self.tick_n230_red_flowers_blooming(state),

View file

@ -125,10 +125,10 @@ pub trait PhysicalEntity {
let bounds_bottom = if self.is_player() { 0x800 } else { 0x600 };
let half_tile_size = state.tile_size.as_int() * 0x100;
// left wall
if (self.y() - self.hit_bounds().top as i32) < ((y * 2 + 1) * half_tile_size - bounds_top)
&& (self.y() + self.hit_bounds().bottom as i32) > ((y * 2 - 1) * half_tile_size + bounds_bottom)
&& (self.x() - self.hit_bounds().right as i32) < (x * 2 + 1) * half_tile_size
&& (self.y() + self.hit_bounds().bottom as i32) > ((y * 2 - 1) * half_tile_size + bounds_bottom) {
// left wall
if (self.x() - self.hit_bounds().right as i32) < (x * 2 + 1) * half_tile_size
&& (self.x() - self.hit_bounds().right as i32) > (x * 2) * half_tile_size
{
self.set_x(((x * 2 + 1) * half_tile_size) + self.hit_bounds().right as i32);
@ -147,9 +147,7 @@ pub trait PhysicalEntity {
}
// right wall
if (self.y() - self.hit_bounds().top as i32) < ((y * 2 + 1) * half_tile_size - bounds_top)
&& (self.y() + self.hit_bounds().bottom as i32) > ((y * 2 - 1) * half_tile_size + bounds_bottom)
&& (self.x() + self.hit_bounds().right as i32) > (x * 2 - 1) * half_tile_size
if (self.x() + self.hit_bounds().right as i32) > (x * 2 - 1) * half_tile_size
&& (self.x() + self.hit_bounds().right as i32) < (x * 2) * half_tile_size
{
self.set_x(((x * 2 - 1) * half_tile_size) - self.hit_bounds().right as i32);
@ -166,11 +164,12 @@ pub trait PhysicalEntity {
self.flags().set_hit_right_wall(true);
}
}
// ceiling
if ((self.x() - self.hit_bounds().right as i32) < (x * 2 + 1) * half_tile_size - bounds_x)
&& ((self.x() + self.hit_bounds().right as i32) > (x * 2 - 1) * half_tile_size + bounds_x)
&& (self.y() - self.hit_bounds().top as i32) < (y * 2 + 1) * half_tile_size
&& ((self.x() + self.hit_bounds().right as i32) > (x * 2 - 1) * half_tile_size + bounds_x) {
// ceiling
if (self.y() - self.hit_bounds().top as i32) < (y * 2 + 1) * half_tile_size
&& (self.y() - self.hit_bounds().top as i32) > (y * 2) * half_tile_size
{
self.set_y(((y * 2 + 1) * half_tile_size) + self.hit_bounds().top as i32);
@ -203,9 +202,7 @@ pub trait PhysicalEntity {
}
// floor
if ((self.x() - self.hit_bounds().right as i32) < (x * 2 + 1) * half_tile_size - bounds_x)
&& ((self.x() + self.hit_bounds().right as i32) > (x * 2 - 1) * half_tile_size + bounds_x)
&& ((self.y() + self.hit_bounds().bottom as i32) > ((y * 2 - 1) * half_tile_size))
if ((self.y() + self.hit_bounds().bottom as i32) > ((y * 2 - 1) * half_tile_size))
&& ((self.y() + self.hit_bounds().bottom as i32) < (y * 2) * half_tile_size)
{
self.set_y(((y * 2 - 1) * half_tile_size) - self.hit_bounds().bottom as i32);
@ -225,6 +222,7 @@ pub trait PhysicalEntity {
self.flags().set_hit_bottom_wall(true);
}
}
}
fn test_platform_hit(&mut self, state: &mut SharedGameState, x: i32, y: i32) {
let half_tile_size = state.tile_size.as_int() * 0x100;
@ -897,5 +895,9 @@ pub trait PhysicalEntity {
_ => {}
}
}
if self.is_player() && (self.y() - 0x800) > state.water_level {
self.flags().set_in_water(true);
}
}
}

View file

@ -1289,6 +1289,45 @@ impl GameScene {
batch.draw(ctx)?;
if layer == TileLayer::Foreground && self.stage.data.background_type == BackgroundType::Water {
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, &self.tex_background_name)?;
let rect_top = Rect {
left: 0,
top: 0,
right: 32,
bottom: 16
};
let rect_middle = Rect {
left: 0,
top: 16,
right: 32,
bottom: 48
};
let tile_start_x = frame_x as i32 / 32;
let tile_end_x = (frame_x + 16.0 + state.canvas_size.0) as i32 / 32 + 1;
let water_y = state.water_level as f32 / 512.0;
let tile_count_y = (frame_y + 16.0 + state.canvas_size.1 - water_y) as i32 / 32 + 1;
for x in tile_start_x..tile_end_x {
batch.add_rect(
(x as f32 * 32.0) - frame_x,
water_y - frame_y,
&rect_top,
);
for y in 0..tile_count_y {
batch.add_rect(
(x as f32 * 32.0) - frame_x,
(y as f32 * 32.0) + water_y - frame_y,
&rect_middle,
);
}
}
batch.draw(ctx)?;
}
Ok(())
}
@ -1518,7 +1557,7 @@ impl GameScene {
[&mut self.player1, &mut self.player2],
&self.npc_list,
&mut self.stage,
&self.bullet_manager,
&mut self.bullet_manager,
&mut self.flash,
),
)?;
@ -1698,12 +1737,12 @@ impl GameScene {
self.draw_debug_object(npc, state, ctx)?;
let text = format!("{}:{}:{}", npc.id, npc.npc_type, npc.action_num);
state.font.draw_colored_text_scaled(
state.font.draw_colored_text_with_shadow_scaled(
text.chars(),
((npc.x - self.frame.x) / 0x200) as f32,
((npc.y - self.frame.y) / 0x200) as f32,
0.5,
((npc.id & 0xf0) as u8, (npc.cond.0 >> 8) as u8, (npc.id & 0x0f << 4) as u8, 255),
(255, 255, 0, 255),
&state.constants,
&mut state.texture_set,
ctx,
@ -1789,12 +1828,16 @@ impl Scene for GameScene {
self.frame.target_y = self.player1.y;
self.frame.immediate_update(state, &self.stage);
// I'd personally set it to something higher but left it as is for accuracy.
state.water_level = 0x1e0000;
self.lighting_mode = match () {
_ if self.intro_mode => LightingMode::None,
_ if !state.constants.is_switch && (self.stage.data.background_type == BackgroundType::Black
|| self.stage.data.background.name() == "bkBlack") => LightingMode::Ambient,
_ if state.constants.is_switch && (self.stage.data.background_type == BackgroundType::Black
|| self.stage.data.background.name() == "bkBlack") => LightingMode::None,
_ if self.stage.data.background.name() == "bkFall" => LightingMode::None,
_ if self.stage.data.background_type != BackgroundType::Black
&& self.stage.data.background_type != BackgroundType::Outside
&& self.stage.data.background_type != BackgroundType::OutsideWind
@ -1857,6 +1900,8 @@ impl Scene for GameScene {
}
for _ in 0..ticks {
TextScriptVM::run(state, self, ctx)?;
match state.textscript_vm.mode {
ScriptMode::Map if state.control_flags.tick_world() => self.tick_world(state)?,
ScriptMode::StageSelect => self.stage_select.tick(state, (ctx, &self.player1, &self.player2))?,
@ -1883,12 +1928,11 @@ impl Scene for GameScene {
}
self.flash.tick(state, ())?;
TextScriptVM::run(state, self, ctx)?;
#[cfg(feature = "scripting")]
state.lua.scene_tick();
if state.control_flags.control_enabled() {
if state.control_flags.tick_world() {
self.tick = self.tick.wrapping_add(1);
}
}

View file

@ -45,7 +45,6 @@ impl LoadingScene {
impl Scene for LoadingScene {
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
println!("**TICK**");
// deferred to let the loading image draw
if self.tick == 1 {
if let Err(err) = self.load_stuff(state, ctx) {

View file

@ -362,6 +362,19 @@ setmetatable(doukutsu.rs, {
end,
})
setmetatable(doukutsu, {
__index = function(self, property)
if property == "currentStage" then
local v = __doukutsu_rs:stageCommand(0x03)
if v == nil then
v = -1
end
return v
end
end,
})
doukutsu.player = __doukutsu_rs_runtime_dont_touch._playerRef0
function doukutsu.playSfx(id)

View file

@ -208,6 +208,11 @@ declare namespace doukutsu {
*/
const rs: DoukutsuRSApi;
/**
* The number of current stage, read-only. Set to -1 if in menu.
*/
const currentStage: number;
/**
* Plays a sound effect with specified ID.
*/

View file

@ -440,8 +440,9 @@ impl Doukutsu {
LightingMode::Ambient => "ambient",
}),
0x02 => state.push(game_state.settings.shader_effects),
0x03 => state.push(game_scene.stage_id as u32),
0x101 => {
if let Some(v) = state.to_str(4) {
if let Some(v) = state.to_str(3) {
game_scene.lighting_mode = match v {
"none" => LightingMode::None,
"backgroundOnly" => LightingMode::BackgroundOnly,

View file

@ -129,6 +129,7 @@ pub struct SharedGameState {
pub npc_super_pos: (i32, i32),
pub npc_curly_target: (i32, i32),
pub npc_curly_counter: u16,
pub water_level: i32,
pub stages: Vec<StageData>,
pub frame_time: f64,
pub debugger: bool,
@ -207,8 +208,6 @@ impl SharedGameState {
}
}
println!("lookup path: {:#?}", texture_set.paths);
#[cfg(feature = "hooks")]
init_hooks();
@ -231,6 +230,7 @@ impl SharedGameState {
npc_super_pos: (0, 0),
npc_curly_target: (0, 0),
npc_curly_counter: 0,
water_level: 0,
stages: Vec::with_capacity(96),
frame_time: 0.0,
debugger: false,

View file

@ -165,7 +165,6 @@ impl OrgPlaybackEngine {
for track in 0..8 {
if let Some(note) = self.song.tracks[track].notes.iter().find(|x| x.pos == self.play_pos) {
// New note
//eprintln!("{:?}", &self.keys);
if note.key != 255 {
if self.keys[track] == 255 {
// New

View file

@ -1259,6 +1259,7 @@ impl TextScriptVM {
let skip = state.textscript_vm.flags.cutscene_skip();
state.control_flags.set_tick_world(true);
state.control_flags.set_interactions_disabled(true);
state.textscript_vm.flags.0 = 0;
state.textscript_vm.flags.set_cutscene_skip(skip);
state.textscript_vm.face = 0;
@ -1542,7 +1543,7 @@ impl TextScriptVM {
[&mut game_scene.player1, &mut game_scene.player2],
&game_scene.npc_list,
&mut game_scene.stage,
&game_scene.bullet_manager,
&mut game_scene.bullet_manager,
&mut game_scene.flash,
),
)?;

View file

@ -257,7 +257,6 @@ impl TextureSet {
.iter()
.find_map(|s| {
FILE_TYPES.iter().map(|ext| [s, name, ext].join("")).find(|path| {
println!("{}", path);
filesystem::exists(ctx, path)
})
})
@ -268,7 +267,6 @@ impl TextureSet {
.iter()
.find_map(|s| {
FILE_TYPES.iter().map(|ext| [s, name, ".glow", ext].join("")).find(|path| {
println!("{}", path);
filesystem::exists(ctx, path)
})
}).is_some();

View file

@ -1010,6 +1010,15 @@ impl Bullet {
self.anim_rect = state.constants.weapon.bullet_rects.b023_blade_slash[self.anim_num as usize + dir_offset];
}
fn tick_spike(&mut self) {
self.action_counter += 1;
if self.action_counter > 2 {
self.cond.set_alive(false);
}
self.anim_rect = Rect::new(0, 0, 0, 0);
}
fn tick_blade_1(&mut self, state: &mut SharedGameState) {
self.action_counter += 1;
if self.action_counter > self.lifetime {
@ -1597,6 +1606,7 @@ impl Bullet {
21 => self.tick_bubble_3(state, players, new_bullets),
22 => self.tick_bubble_spines(state),
23 => self.tick_blade_slash(state),
24 => self.tick_spike(),
25 => self.tick_blade_1(state),
26 => self.tick_blade_2(state),
27 => self.tick_blade_3(state, new_bullets),