mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-11-30 08:08:18 +00:00
normal ending...
This commit is contained in:
parent
1b424f0b80
commit
eaaa11d4f6
|
|
@ -196,11 +196,11 @@ impl Caret {
|
|||
}
|
||||
|
||||
match self.direction {
|
||||
Direction::Left => self.x -= 0x400, // 2.0fix9
|
||||
Direction::Left => self.x -= 0x400,
|
||||
Direction::Up => self.y -= 0x400,
|
||||
Direction::Right => self.x += 0x400,
|
||||
Direction::Bottom => self.y += 0x400,
|
||||
Direction::FacingPlayer => {}
|
||||
Direction::FacingPlayer => (),
|
||||
}
|
||||
}
|
||||
CaretType::DrownedQuote => {
|
||||
|
|
@ -302,8 +302,8 @@ impl Caret {
|
|||
if self.anim_num == 0 {
|
||||
match self.direction {
|
||||
Direction::Left => {
|
||||
self.vel_x = rng.range(-0x600..0x600) as i32; // -3.0fix9..3.0fix9
|
||||
self.vel_y = rng.range(-0x200..0x200) as i32; // -1.0fix9..1.0fix9
|
||||
self.vel_x = rng.range(-0x600..0x600) as i32;
|
||||
self.vel_y = rng.range(-0x200..0x200) as i32;
|
||||
}
|
||||
Direction::Up => {
|
||||
self.vel_y = rng.range(-3..-1) as i32 * 0x200;
|
||||
|
|
|
|||
|
|
@ -530,3 +530,29 @@ impl From<Color> for [f32; 4] {
|
|||
[color.r, color.g, color.b, color.a]
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SliceExt {
|
||||
type Item;
|
||||
|
||||
fn get_two_mut(&mut self, a: usize, b: usize) -> Option<(&mut Self::Item, &mut Self::Item)>;
|
||||
}
|
||||
|
||||
impl<T> SliceExt for [T] {
|
||||
type Item = T;
|
||||
|
||||
fn get_two_mut(&mut self, a: usize, b: usize) -> Option<(&mut Self::Item, &mut Self::Item)> {
|
||||
if a == b {
|
||||
None
|
||||
} else {
|
||||
if a >= self.len() || b >= self.len() {
|
||||
None
|
||||
} else {
|
||||
unsafe {
|
||||
let ar = &mut *(self.get_unchecked_mut(a) as *mut _);
|
||||
let br = &mut *(self.get_unchecked_mut(b) as *mut _);
|
||||
Some((ar, br))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
56
src/components/falling_island.rs
Normal file
56
src/components/falling_island.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use crate::common::{Color, Rect};
|
||||
use crate::entity::GameEntity;
|
||||
use crate::frame::Frame;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::graphics;
|
||||
use crate::scripting::tsc::text_script::TextScriptExecutionState;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
||||
pub struct FallingIsland {}
|
||||
|
||||
impl FallingIsland {
|
||||
pub fn new() -> FallingIsland {
|
||||
FallingIsland {}
|
||||
}
|
||||
}
|
||||
|
||||
impl GameEntity<()> for FallingIsland {
|
||||
fn tick(&mut self, state: &mut SharedGameState, custom: ()) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, _frame: &Frame) -> GameResult {
|
||||
let (pos_x, pos_y) =
|
||||
if let TextScriptExecutionState::FallingIsland(_, _, pos_x, pos_y, _, _) = state.textscript_vm.state {
|
||||
(pos_x, pos_y)
|
||||
} else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let off_x = (state.canvas_size.0 - 320.0) * 0.5;
|
||||
let clip_rect: Rect = Rect::new_size(
|
||||
((off_x + 80.0) * state.scale) as _,
|
||||
(80.0 * state.scale) as _,
|
||||
(160.0 * state.scale) as _,
|
||||
(80.0 * state.scale) as _,
|
||||
);
|
||||
|
||||
graphics::clear(ctx, Color::from_rgb(0, 0, 32));
|
||||
graphics::set_clip_rect(ctx, Some(clip_rect))?;
|
||||
|
||||
static RECT_BG: Rect<u16> = Rect { left: 0, top: 0, right: 160, bottom: 80 };
|
||||
static RECT_ISLAND: Rect<u16> = Rect { left: 160, top: 0, right: 200, bottom: 24 };
|
||||
static RECT_TERRAIN: Rect<u16> = Rect { left: 160, top: 48, right: 320, bottom: 80 };
|
||||
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, &state.npc_table.tex_npc1_name)?;
|
||||
batch.add_rect(off_x + 80.0, 80.0, &RECT_BG);
|
||||
batch.add_rect(off_x + (pos_x as f32 / 512.0) - 20.0, (pos_y as f32 / 512.0) - 12.0, &RECT_ISLAND);
|
||||
batch.add_rect(off_x + 80.0, 128.0, &RECT_TERRAIN);
|
||||
batch.draw(ctx)?;
|
||||
|
||||
graphics::set_clip_rect(ctx, None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
pub mod boss_life_bar;
|
||||
pub mod credits;
|
||||
pub mod draw_common;
|
||||
pub mod falling_island;
|
||||
pub mod flash;
|
||||
pub mod hud;
|
||||
pub mod inventory;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ use crate::common::Rect;
|
|||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct NPCConsts {
|
||||
// pub n000_null: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n001_experience")]
|
||||
pub n001_experience: [Rect<u16>; 6],
|
||||
|
||||
|
|
@ -13,7 +12,6 @@ pub struct NPCConsts {
|
|||
pub n002_behemoth: [Rect<u16>; 14],
|
||||
|
||||
// pub n003_dead_enemy: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n004_smoke")]
|
||||
pub n004_smoke: [Rect<u16>; 16],
|
||||
|
||||
|
|
@ -141,7 +139,6 @@ pub struct NPCConsts {
|
|||
pub n045_baby: [Rect<u16>; 3],
|
||||
|
||||
// pub n046_hv_trigger: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n047_sandcroc")]
|
||||
pub n047_sandcroc: [Rect<u16>; 5],
|
||||
|
||||
|
|
@ -230,7 +227,6 @@ pub struct NPCConsts {
|
|||
pub n075_kanpachi: [Rect<u16>; 2],
|
||||
|
||||
// pub n076_flowers: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n077_yamashita")]
|
||||
pub n077_yamashita: [Rect<u16>; 3],
|
||||
|
||||
|
|
@ -319,7 +315,6 @@ pub struct NPCConsts {
|
|||
pub n105_hey_bubble_low: [Rect<u16>; 2],
|
||||
|
||||
// pub n106_hey_bubble_high: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n107_malco_broken")]
|
||||
pub n107_malco_broken: [Rect<u16>; 10],
|
||||
|
||||
|
|
@ -456,7 +451,6 @@ pub struct NPCConsts {
|
|||
pub n151_blue_robot_standing: [Rect<u16>; 4],
|
||||
|
||||
// pub n152_shutter_stuck: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n153_gaudi")]
|
||||
pub n153_gaudi: [Rect<u16>; 14],
|
||||
|
||||
|
|
@ -572,7 +566,6 @@ pub struct NPCConsts {
|
|||
pub n190_broken_robot: [Rect<u16>; 2],
|
||||
|
||||
// pub n191_water_level: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n192_scooter")]
|
||||
pub n192_scooter: [Rect<u16>; 4],
|
||||
|
||||
|
|
@ -655,7 +648,6 @@ pub struct NPCConsts {
|
|||
pub n218_core_giant_ball: [Rect<u16>; 2],
|
||||
|
||||
// pub n219_smoke_generator: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n220_shovel_brigade")]
|
||||
pub n220_shovel_brigade: [Rect<u16>; 4],
|
||||
|
||||
|
|
@ -695,8 +687,8 @@ pub struct NPCConsts {
|
|||
#[serde(default = "default_n232_orangebell")]
|
||||
pub n232_orangebell: [Rect<u16>; 6],
|
||||
|
||||
#[serde(default = "default_n233_orangebell_hat")]
|
||||
pub n233_orangebell_hat: [Rect<u16>; 8],
|
||||
#[serde(default = "default_n233_orangebell_bat")]
|
||||
pub n233_orangebell_bat: [Rect<u16>; 8],
|
||||
|
||||
#[serde(default = "default_n234_red_flowers_picked")]
|
||||
pub n234_red_flowers_picked: [Rect<u16>; 2],
|
||||
|
|
@ -726,7 +718,6 @@ pub struct NPCConsts {
|
|||
pub n242_bat_last_cave: [Rect<u16>; 8],
|
||||
|
||||
// pub n243_bat_generator: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n244_lava_drop")]
|
||||
pub n244_lava_drop: Rect<u16>,
|
||||
|
||||
|
|
@ -742,8 +733,8 @@ pub struct NPCConsts {
|
|||
#[serde(default = "default_n248_misery_boss_vanishing")]
|
||||
pub n248_misery_boss_vanishing: [Rect<u16>; 3],
|
||||
|
||||
#[serde(default = "default_n249_misery_boss_energy_shot")]
|
||||
pub n249_misery_boss_energy_shot: [Rect<u16>; 2],
|
||||
#[serde(default = "default_n249_misery_boss_appearing")]
|
||||
pub n249_misery_boss_appearing: [Rect<u16>; 2],
|
||||
|
||||
#[serde(default = "default_n250_misery_boss_lighting_ball")]
|
||||
pub n250_misery_boss_lighting_ball: [Rect<u16>; 3],
|
||||
|
|
@ -787,14 +778,14 @@ pub struct NPCConsts {
|
|||
#[serde(default = "default_n263_doctor_boss")]
|
||||
pub n263_doctor_boss: [Rect<u16>; 18],
|
||||
|
||||
#[serde(default = "default_n264_doctor_boss_red_wave_projectile")]
|
||||
pub n264_doctor_boss_red_wave_projectile: Rect<u16>,
|
||||
#[serde(default = "default_n264_doctor_boss_red_projectile")]
|
||||
pub n264_doctor_boss_red_projectile: Rect<u16>,
|
||||
|
||||
#[serde(default = "default_n265_doctor_boss_red_ball_projectile")]
|
||||
pub n265_doctor_boss_red_ball_projectile: [Rect<u16>; 3],
|
||||
#[serde(default = "default_n265_doctor_boss_red_projectile_trail")]
|
||||
pub n265_doctor_boss_red_projectile_trail: [Rect<u16>; 3],
|
||||
|
||||
#[serde(default = "default_n266_doctor_boss_red_ball_projectile_bouncing")]
|
||||
pub n266_doctor_boss_red_ball_projectile_bouncing: [Rect<u16>; 2],
|
||||
#[serde(default = "default_n266_doctor_boss_red_projectile_bouncing")]
|
||||
pub n266_doctor_boss_red_projectile_bouncing: [Rect<u16>; 2],
|
||||
|
||||
#[serde(default = "default_n267_muscle_doctor")]
|
||||
pub n267_muscle_doctor: [Rect<u16>; 20],
|
||||
|
|
@ -811,7 +802,6 @@ pub struct NPCConsts {
|
|||
// pub n271_ironhead_block: () // Defined in code
|
||||
|
||||
// pub n272_ironhead_block_generator: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n273_droll_projectile")]
|
||||
pub n273_droll_projectile: [Rect<u16>; 3],
|
||||
|
||||
|
|
@ -837,7 +827,6 @@ pub struct NPCConsts {
|
|||
pub n280_sue_teleported: [Rect<u16>; 4],
|
||||
|
||||
// pub n281_doctor_energy_form: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n282_mini_undead_core_active")]
|
||||
pub n282_mini_undead_core_active: [Rect<u16>; 3],
|
||||
|
||||
|
|
@ -869,17 +858,14 @@ pub struct NPCConsts {
|
|||
pub n291_mini_undead_core_inactive: [Rect<u16>; 2],
|
||||
|
||||
// pub n292_quake: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n293_undead_core_energy_shot")]
|
||||
pub n293_undead_core_energy_shot: [Rect<u16>; 2],
|
||||
|
||||
// pub n294_quake_falling_block_generator: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n295_cloud")]
|
||||
pub n295_cloud: [Rect<u16>; 4],
|
||||
|
||||
// pub n296_cloud_generator: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n297_sue_dragon_mouth")]
|
||||
pub n297_sue_dragon_mouth: Rect<u16>,
|
||||
|
||||
|
|
@ -896,7 +882,6 @@ pub struct NPCConsts {
|
|||
pub n301_misery_fish_missile: [Rect<u16>; 8],
|
||||
|
||||
// pub n302_camera_focus_marker: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n303_curly_machine_gun")]
|
||||
pub n303_curly_machine_gun: [Rect<u16>; 4],
|
||||
|
||||
|
|
@ -961,7 +946,6 @@ pub struct NPCConsts {
|
|||
pub n323_bute_spinning: [Rect<u16>; 4],
|
||||
|
||||
// pub n324_bute_generator: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n325_heavy_press_lighting")]
|
||||
pub n325_heavy_press_lighting: [Rect<u16>; 7],
|
||||
|
||||
|
|
@ -996,7 +980,6 @@ pub struct NPCConsts {
|
|||
pub n335_ikachan: [Rect<u16>; 3],
|
||||
|
||||
// pub n336_ikachan_generator: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n337_numahachi")]
|
||||
pub n337_numahachi: [Rect<u16>; 2],
|
||||
|
||||
|
|
@ -1004,7 +987,6 @@ pub struct NPCConsts {
|
|||
pub n338_green_devil: [Rect<u16>; 4],
|
||||
|
||||
// pub n339_green_devil_generator: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n340_ballos")]
|
||||
pub n340_ballos: [Rect<u16>; 22],
|
||||
|
||||
|
|
@ -1048,7 +1030,6 @@ pub struct NPCConsts {
|
|||
pub n353_bute_sword_flying: [Rect<u16>; 8],
|
||||
|
||||
// pub n354_invisible_deathtrap_wall: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n355_quote_and_curly_on_balrog")]
|
||||
pub n355_quote_and_curly_on_balrog: [Rect<u16>; 4],
|
||||
|
||||
|
|
@ -1062,7 +1043,6 @@ pub struct NPCConsts {
|
|||
pub n358_misery_credits: [Rect<u16>; 5],
|
||||
|
||||
// pub n359_water_droplet_generator: () // Defined in code
|
||||
|
||||
#[serde(default = "default_n360_credits_thank_you")]
|
||||
pub n360_credits_thank_you: Rect<u16>,
|
||||
|
||||
|
|
@ -1077,6 +1057,15 @@ pub struct NPCConsts {
|
|||
|
||||
#[serde(default = "default_b04_core")]
|
||||
pub b04_core: [Rect<u16>; 10],
|
||||
|
||||
#[serde(default = "default_b05_ironhead")]
|
||||
pub b05_ironhead: [Rect<u16>; 18],
|
||||
|
||||
#[serde(default = "default_b06_sisters")]
|
||||
pub b06_sisters: [Rect<u16>; 14],
|
||||
|
||||
#[serde(default = "default_b07_undead_core")]
|
||||
pub b07_undead_core: [Rect<u16>; 15],
|
||||
}
|
||||
|
||||
fn default_n001_experience() -> [Rect<u16>; 6] {
|
||||
|
|
@ -1280,17 +1269,11 @@ fn default_n016_save_point() -> [Rect<u16>; 8] {
|
|||
}
|
||||
|
||||
fn default_n017_health_refill() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 288, top: 0, right: 304, bottom: 16 },
|
||||
Rect { left: 304, top: 0, right: 320, bottom: 16 },
|
||||
]
|
||||
[Rect { left: 288, top: 0, right: 304, bottom: 16 }, Rect { left: 304, top: 0, right: 320, bottom: 16 }]
|
||||
}
|
||||
|
||||
fn default_n018_door() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 224, top: 16, right: 240, bottom: 40 },
|
||||
Rect { left: 192, top: 112, right: 208, bottom: 136 },
|
||||
]
|
||||
[Rect { left: 224, top: 16, right: 240, bottom: 40 }, Rect { left: 192, top: 112, right: 208, bottom: 136 }]
|
||||
}
|
||||
|
||||
fn default_n019_balrog_bust_in() -> [Rect<u16>; 8] {
|
||||
|
|
@ -1320,10 +1303,7 @@ fn default_n021_chest_open() -> Rect<u16> {
|
|||
}
|
||||
|
||||
fn default_n022_teleporter() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 240, top: 16, right: 264, bottom: 48 },
|
||||
Rect { left: 248, top: 152, right: 272, bottom: 184 },
|
||||
]
|
||||
[Rect { left: 240, top: 16, right: 264, bottom: 48 }, Rect { left: 248, top: 152, right: 272, bottom: 184 }]
|
||||
}
|
||||
|
||||
fn default_n023_teleporter_lights() -> [Rect<u16>; 8] {
|
||||
|
|
@ -1357,10 +1337,7 @@ fn default_n024_power_critter() -> [Rect<u16>; 12] {
|
|||
}
|
||||
|
||||
fn default_n025_lift() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 256, top: 64, right: 288, bottom: 80 },
|
||||
Rect { left: 256, top: 80, right: 288, bottom: 96 },
|
||||
]
|
||||
[Rect { left: 256, top: 64, right: 288, bottom: 80 }, Rect { left: 256, top: 80, right: 288, bottom: 96 }]
|
||||
}
|
||||
|
||||
fn default_n026_bat_flying() -> [Rect<u16>; 8] {
|
||||
|
|
@ -1430,24 +1407,15 @@ fn default_n031_bat_hanging() -> [Rect<u16>; 10] {
|
|||
}
|
||||
|
||||
fn default_n032_life_capsule() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 32, top: 96, right: 48, bottom: 112 },
|
||||
Rect { left: 48, top: 96, right: 64, bottom: 112 },
|
||||
]
|
||||
[Rect { left: 32, top: 96, right: 48, bottom: 112 }, Rect { left: 48, top: 96, right: 64, bottom: 112 }]
|
||||
}
|
||||
|
||||
fn default_n033_balrog_bouncing_projectile() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 240, top: 64, right: 256, bottom: 80 },
|
||||
Rect { left: 240, top: 80, right: 256, bottom: 96 },
|
||||
]
|
||||
[Rect { left: 240, top: 64, right: 256, bottom: 80 }, Rect { left: 240, top: 80, right: 256, bottom: 96 }]
|
||||
}
|
||||
|
||||
fn default_n034_bed() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 192, top: 48, right: 224, bottom: 64 },
|
||||
Rect { left: 192, top: 184, right: 224, bottom: 200 },
|
||||
]
|
||||
[Rect { left: 192, top: 48, right: 224, bottom: 64 }, Rect { left: 192, top: 184, right: 224, bottom: 200 }]
|
||||
}
|
||||
|
||||
fn default_n035_mannan() -> [Rect<u16>; 8] {
|
||||
|
|
@ -1481,10 +1449,7 @@ fn default_n036_balrog_hover() -> [Rect<u16>; 12] {
|
|||
}
|
||||
|
||||
fn default_n037_sign() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 192, top: 64, right: 208, bottom: 80 },
|
||||
Rect { left: 208, top: 64, right: 224, bottom: 80 },
|
||||
]
|
||||
[Rect { left: 192, top: 64, right: 208, bottom: 80 }, Rect { left: 208, top: 64, right: 224, bottom: 80 }]
|
||||
}
|
||||
|
||||
fn default_n038_fireplace() -> [Rect<u16>; 4] {
|
||||
|
|
@ -1497,10 +1462,7 @@ fn default_n038_fireplace() -> [Rect<u16>; 4] {
|
|||
}
|
||||
|
||||
fn default_n039_save_sign() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 224, top: 64, right: 240, bottom: 80 },
|
||||
Rect { left: 240, top: 64, right: 256, bottom: 80 },
|
||||
]
|
||||
[Rect { left: 224, top: 64, right: 240, bottom: 80 }, Rect { left: 240, top: 64, right: 256, bottom: 80 }]
|
||||
}
|
||||
|
||||
fn default_n040_santa() -> [Rect<u16>; 14] {
|
||||
|
|
@ -1558,10 +1520,7 @@ fn default_n042_sue() -> [Rect<u16>; 26] {
|
|||
}
|
||||
|
||||
fn default_n043_chalkboard() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 128, top: 80, right: 168, bottom: 112 },
|
||||
Rect { left: 168, top: 80, right: 208, bottom: 112 },
|
||||
]
|
||||
[Rect { left: 128, top: 80, right: 168, bottom: 112 }, Rect { left: 168, top: 80, right: 208, bottom: 112 }]
|
||||
}
|
||||
|
||||
fn default_n044_polish() -> [Rect<u16>; 6] {
|
||||
|
|
@ -1912,10 +1871,7 @@ fn default_n071_chinfish() -> [Rect<u16>; 6] {
|
|||
}
|
||||
|
||||
fn default_n072_sprinkler() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 224, top: 48, right: 240, bottom: 64 },
|
||||
Rect { left: 240, top: 48, right: 256, bottom: 64 },
|
||||
]
|
||||
[Rect { left: 224, top: 48, right: 240, bottom: 64 }, Rect { left: 240, top: 48, right: 256, bottom: 64 }]
|
||||
}
|
||||
|
||||
fn default_n073_water_droplet() -> [Rect<u16>; 5] {
|
||||
|
|
@ -1946,10 +1902,7 @@ fn default_n074_jack() -> [Rect<u16>; 12] {
|
|||
}
|
||||
|
||||
fn default_n075_kanpachi() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 272, top: 32, right: 296, bottom: 56 },
|
||||
Rect { left: 296, top: 32, right: 320, bottom: 56 },
|
||||
]
|
||||
[Rect { left: 272, top: 32, right: 296, bottom: 56 }, Rect { left: 296, top: 32, right: 320, bottom: 56 }]
|
||||
}
|
||||
|
||||
fn default_n077_yamashita() -> [Rect<u16>; 3] {
|
||||
|
|
@ -1961,10 +1914,7 @@ fn default_n077_yamashita() -> [Rect<u16>; 3] {
|
|||
}
|
||||
|
||||
fn default_n078_pot() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 160, top: 48, right: 176, bottom: 64 },
|
||||
Rect { left: 176, top: 48, right: 192, bottom: 64 },
|
||||
]
|
||||
[Rect { left: 160, top: 48, right: 176, bottom: 64 }, Rect { left: 176, top: 48, right: 192, bottom: 64 }]
|
||||
}
|
||||
|
||||
fn default_n079_mahin() -> [Rect<u16>; 6] {
|
||||
|
|
@ -2231,10 +2181,7 @@ fn default_n099_fan_down() -> [Rect<u16>; 3] {
|
|||
}
|
||||
|
||||
fn default_n100_grate() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 272, top: 48, right: 288, bottom: 64 },
|
||||
Rect { left: 272, top: 48, right: 288, bottom: 64 },
|
||||
]
|
||||
[Rect { left: 272, top: 48, right: 288, bottom: 64 }, Rect { left: 272, top: 48, right: 288, bottom: 64 }]
|
||||
}
|
||||
|
||||
fn default_n101_malco_screen() -> [Rect<u16>; 3] {
|
||||
|
|
@ -2277,10 +2224,7 @@ fn default_n104_frog() -> [Rect<u16>; 6] {
|
|||
}
|
||||
|
||||
fn default_n105_hey_bubble_low() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 128, top: 32, right: 144, bottom: 48 },
|
||||
Rect { left: 128, top: 32, right: 128, bottom: 32 },
|
||||
]
|
||||
[Rect { left: 128, top: 32, right: 144, bottom: 48 }, Rect { left: 128, top: 32, right: 128, bottom: 32 }]
|
||||
}
|
||||
|
||||
fn default_n107_malco_broken() -> [Rect<u16>; 10] {
|
||||
|
|
@ -2445,10 +2389,7 @@ fn default_n119_table_chair() -> Rect<u16> {
|
|||
}
|
||||
|
||||
fn default_n120_colon_a() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 64, top: 0, right: 80, bottom: 16 },
|
||||
Rect { left: 64, top: 16, right: 80, bottom: 32 },
|
||||
]
|
||||
[Rect { left: 64, top: 0, right: 80, bottom: 16 }, Rect { left: 64, top: 16, right: 80, bottom: 32 }]
|
||||
}
|
||||
|
||||
fn default_n121_colon_b() -> [Rect<u16>; 3] {
|
||||
|
|
@ -2494,17 +2435,11 @@ fn default_n123_curly_boss_bullet() -> [Rect<u16>; 4] {
|
|||
}
|
||||
|
||||
fn default_n124_sunstone() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 160, top: 0, right: 192, bottom: 32 },
|
||||
Rect { left: 192, top: 0, right: 224, bottom: 32 },
|
||||
]
|
||||
[Rect { left: 160, top: 0, right: 192, bottom: 32 }, Rect { left: 192, top: 0, right: 224, bottom: 32 }]
|
||||
}
|
||||
|
||||
fn default_n125_hidden_item() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 0, top: 96, right: 16, bottom: 112 },
|
||||
Rect { left: 16, top: 96, right: 32, bottom: 112 },
|
||||
]
|
||||
[Rect { left: 0, top: 96, right: 16, bottom: 112 }, Rect { left: 16, top: 96, right: 32, bottom: 112 }]
|
||||
}
|
||||
|
||||
fn default_n126_puppy_running() -> [Rect<u16>; 12] {
|
||||
|
|
@ -2597,10 +2532,7 @@ fn default_n130_puppy_sitting() -> [Rect<u16>; 8] {
|
|||
}
|
||||
|
||||
fn default_n131_puppy_sleeping() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 144, top: 144, right: 160, bottom: 160 },
|
||||
Rect { left: 144, top: 160, right: 160, bottom: 176 },
|
||||
]
|
||||
[Rect { left: 144, top: 144, right: 160, bottom: 160 }, Rect { left: 144, top: 160, right: 160, bottom: 176 }]
|
||||
}
|
||||
|
||||
fn default_n132_puppy_barking() -> [Rect<u16>; 10] {
|
||||
|
|
@ -2661,10 +2593,7 @@ fn default_n137_large_door_frame() -> Rect<u16> {
|
|||
}
|
||||
|
||||
fn default_n138_large_door() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 96, top: 112, right: 112, bottom: 136 },
|
||||
Rect { left: 112, top: 112, right: 128, bottom: 136 },
|
||||
]
|
||||
[Rect { left: 96, top: 112, right: 112, bottom: 136 }, Rect { left: 112, top: 112, right: 128, bottom: 136 }]
|
||||
}
|
||||
|
||||
fn default_n139_doctor() -> [Rect<u16>; 6] {
|
||||
|
|
@ -2712,10 +2641,7 @@ fn default_n140_toroko_frenzied() -> [Rect<u16>; 28] {
|
|||
}
|
||||
|
||||
fn default_n141_toroko_block_projectile() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 288, top: 32, right: 304, bottom: 48 },
|
||||
Rect { left: 304, top: 32, right: 320, bottom: 48 },
|
||||
]
|
||||
[Rect { left: 288, top: 32, right: 304, bottom: 48 }, Rect { left: 304, top: 32, right: 320, bottom: 48 }]
|
||||
}
|
||||
|
||||
fn default_n142_flower_cub() -> [Rect<u16>; 5] {
|
||||
|
|
@ -2729,10 +2655,7 @@ fn default_n142_flower_cub() -> [Rect<u16>; 5] {
|
|||
}
|
||||
|
||||
fn default_n143_jenka_collapsed() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 208, top: 32, right: 224, bottom: 48 },
|
||||
Rect { left: 208, top: 48, right: 224, bottom: 64 },
|
||||
]
|
||||
[Rect { left: 208, top: 32, right: 224, bottom: 48 }, Rect { left: 208, top: 48, right: 224, bottom: 64 }]
|
||||
}
|
||||
|
||||
fn default_n144_toroko_teleporting_in() -> [Rect<u16>; 10] {
|
||||
|
|
@ -2751,10 +2674,7 @@ fn default_n144_toroko_teleporting_in() -> [Rect<u16>; 10] {
|
|||
}
|
||||
|
||||
fn default_n145_king_sword() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 96, top: 32, right: 112, bottom: 48 },
|
||||
Rect { left: 112, top: 32, right: 128, bottom: 48 },
|
||||
]
|
||||
[Rect { left: 96, top: 32, right: 112, bottom: 48 }, Rect { left: 112, top: 32, right: 128, bottom: 48 }]
|
||||
}
|
||||
|
||||
fn default_n146_lighting() -> [Rect<u16>; 5] {
|
||||
|
|
@ -2785,10 +2705,7 @@ fn default_n147_critter_purple() -> [Rect<u16>; 12] {
|
|||
}
|
||||
|
||||
fn default_n148_critter_purple_projectile() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 96, top: 96, right: 104, bottom: 104 },
|
||||
Rect { left: 104, top: 96, right: 112, bottom: 104 },
|
||||
]
|
||||
[Rect { left: 96, top: 96, right: 104, bottom: 104 }, Rect { left: 104, top: 96, right: 112, bottom: 104 }]
|
||||
}
|
||||
|
||||
fn default_n149_horizontal_moving_block() -> Rect<u16> {
|
||||
|
|
@ -2829,19 +2746,19 @@ fn default_n151_blue_robot_standing() -> [Rect<u16>; 4] {
|
|||
|
||||
fn default_n153_gaudi() -> [Rect<u16>; 14] {
|
||||
[
|
||||
Rect { left: 0, top: 0, right: 24, bottom: 24 }, // 0 0 // left
|
||||
Rect { left: 24, top: 0, right: 48, bottom: 24 }, // 1 1
|
||||
Rect { left: 48, top: 0, right: 72, bottom: 24 }, // 2 2
|
||||
Rect { left: 0, top: 0, right: 24, bottom: 24 }, // 3 3
|
||||
Rect { left: 72, top: 0, right: 96, bottom: 24 }, // 4 4
|
||||
Rect { left: 0, top: 0, right: 24, bottom: 24 }, // 5 5
|
||||
Rect { left: 0, top: 0, right: 24, bottom: 24 }, // 0 0 // left
|
||||
Rect { left: 24, top: 0, right: 48, bottom: 24 }, // 1 1
|
||||
Rect { left: 48, top: 0, right: 72, bottom: 24 }, // 2 2
|
||||
Rect { left: 0, top: 0, right: 24, bottom: 24 }, // 3 3
|
||||
Rect { left: 72, top: 0, right: 96, bottom: 24 }, // 4 4
|
||||
Rect { left: 0, top: 0, right: 24, bottom: 24 }, // 5 5
|
||||
Rect { left: 96, top: 48, right: 120, bottom: 72 }, // 6 20
|
||||
Rect { left: 0, top: 24, right: 24, bottom: 48 }, // 0 0 // right
|
||||
Rect { left: 24, top: 24, right: 48, bottom: 48 }, // 1 1
|
||||
Rect { left: 48, top: 24, right: 72, bottom: 48 }, // 2 2
|
||||
Rect { left: 0, top: 24, right: 24, bottom: 48 }, // 3 3
|
||||
Rect { left: 72, top: 24, right: 96, bottom: 48 }, // 4 4
|
||||
Rect { left: 0, top: 24, right: 24, bottom: 48 }, // 5 5
|
||||
Rect { left: 0, top: 24, right: 24, bottom: 48 }, // 0 0 // right
|
||||
Rect { left: 24, top: 24, right: 48, bottom: 48 }, // 1 1
|
||||
Rect { left: 48, top: 24, right: 72, bottom: 48 }, // 2 2
|
||||
Rect { left: 0, top: 24, right: 24, bottom: 48 }, // 3 3
|
||||
Rect { left: 72, top: 24, right: 96, bottom: 48 }, // 4 4
|
||||
Rect { left: 0, top: 24, right: 24, bottom: 48 }, // 5 5
|
||||
Rect { left: 96, top: 72, right: 120, bottom: 96 }, // 6 20
|
||||
]
|
||||
}
|
||||
|
|
@ -2859,14 +2776,14 @@ fn default_n154_gaudi_dead() -> [Rect<u16>; 6] {
|
|||
|
||||
fn default_n155_gaudi_flying() -> [Rect<u16>; 8] {
|
||||
[
|
||||
Rect { left: 0, top: 48, right: 24, bottom: 72 }, // 0 14 // left
|
||||
Rect { left: 24, top: 48, right: 48, bottom: 72 }, // 1 15
|
||||
Rect { left: 288, top: 0, right: 312, bottom: 24 }, // 2 18
|
||||
Rect { left: 24, top: 48, right: 48, bottom: 72 }, // 3 19
|
||||
Rect { left: 0, top: 72, right: 24, bottom: 96 }, // 0 14 // right
|
||||
Rect { left: 24, top: 72, right: 48, bottom: 96 }, // 1 15
|
||||
Rect { left: 0, top: 48, right: 24, bottom: 72 }, // 0 14 // left
|
||||
Rect { left: 24, top: 48, right: 48, bottom: 72 }, // 1 15
|
||||
Rect { left: 288, top: 0, right: 312, bottom: 24 }, // 2 18
|
||||
Rect { left: 24, top: 48, right: 48, bottom: 72 }, // 3 19
|
||||
Rect { left: 0, top: 72, right: 24, bottom: 96 }, // 0 14 // right
|
||||
Rect { left: 24, top: 72, right: 48, bottom: 96 }, // 1 15
|
||||
Rect { left: 288, top: 24, right: 312, bottom: 48 }, // 2 18
|
||||
Rect { left: 24, top: 72, right: 48, bottom: 96 }, // 3 19
|
||||
Rect { left: 24, top: 72, right: 48, bottom: 96 }, // 3 19
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -2955,10 +2872,7 @@ fn default_n165_curly_collapsed() -> [Rect<u16>; 3] {
|
|||
}
|
||||
|
||||
fn default_n166_chaba() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 144, top: 104, right: 184, bottom: 128 },
|
||||
Rect { left: 184, top: 104, right: 224, bottom: 128 },
|
||||
]
|
||||
[Rect { left: 144, top: 104, right: 184, bottom: 128 }, Rect { left: 184, top: 104, right: 224, bottom: 128 }]
|
||||
}
|
||||
|
||||
fn default_n167_booster_falling() -> [Rect<u16>; 3] {
|
||||
|
|
@ -3064,10 +2978,7 @@ fn default_n176_buyo_buyo_base() -> [Rect<u16>; 6] {
|
|||
}
|
||||
|
||||
fn default_n177_buyo_buyo() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 192, top: 128, right: 208, bottom: 144 },
|
||||
Rect { left: 208, top: 128, right: 224, bottom: 144 },
|
||||
]
|
||||
[Rect { left: 192, top: 128, right: 208, bottom: 144 }, Rect { left: 208, top: 128, right: 224, bottom: 144 }]
|
||||
}
|
||||
|
||||
fn default_n178_core_blade_projectile() -> [Rect<u16>; 3] {
|
||||
|
|
@ -3132,10 +3043,7 @@ fn default_n182_curly_ai_polar_star() -> [Rect<u16>; 4] {
|
|||
}
|
||||
|
||||
fn default_n183_curly_air_tank_bubble() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 56, top: 96, right: 80, bottom: 120 },
|
||||
Rect { left: 80, top: 96, right: 104, bottom: 120 },
|
||||
]
|
||||
[Rect { left: 56, top: 96, right: 80, bottom: 120 }, Rect { left: 80, top: 96, right: 104, bottom: 120 }]
|
||||
}
|
||||
|
||||
fn default_n184_shutter() -> [Rect<u16>; 4] {
|
||||
|
|
@ -3187,10 +3095,7 @@ fn default_n189_homing_flame() -> [Rect<u16>; 3] {
|
|||
}
|
||||
|
||||
fn default_n190_broken_robot() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 192, top: 32, right: 208, bottom: 48 },
|
||||
Rect { left: 208, top: 32, right: 224, bottom: 48 },
|
||||
]
|
||||
[Rect { left: 192, top: 32, right: 208, bottom: 48 }, Rect { left: 208, top: 32, right: 224, bottom: 48 }]
|
||||
}
|
||||
|
||||
fn default_n192_scooter() -> [Rect<u16>; 4] {
|
||||
|
|
@ -3215,10 +3120,7 @@ fn default_n195_background_grate() -> Rect<u16> {
|
|||
}
|
||||
|
||||
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 },
|
||||
]
|
||||
[Rect { left: 112, top: 64, right: 144, bottom: 80 }, Rect { left: 112, top: 80, right: 144, bottom: 96 }]
|
||||
}
|
||||
|
||||
fn default_n197_porcupine_fish() -> [Rect<u16>; 4] {
|
||||
|
|
@ -3266,10 +3168,7 @@ fn default_n200_zombie_dragon() -> [Rect<u16>; 12] {
|
|||
}
|
||||
|
||||
fn default_n201_zombie_dragon_dead() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 200, top: 0, right: 240, bottom: 40 },
|
||||
Rect { left: 200, top: 40, right: 240, bottom: 80 },
|
||||
]
|
||||
[Rect { left: 200, top: 0, right: 240, bottom: 40 }, Rect { left: 200, top: 40, right: 240, bottom: 80 }]
|
||||
}
|
||||
|
||||
fn default_n202_zombie_dragon_projectile() -> [Rect<u16>; 3] {
|
||||
|
|
@ -3292,17 +3191,11 @@ fn default_n203_critter_destroyed_egg_corridor() -> [Rect<u16>; 6] {
|
|||
}
|
||||
|
||||
fn default_n204_small_falling_spike() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 240, top: 80, right: 256, bottom: 96 },
|
||||
Rect { left: 240, top: 144, right: 256, bottom: 160 },
|
||||
]
|
||||
[Rect { left: 240, top: 80, right: 256, bottom: 96 }, Rect { left: 240, top: 144, right: 256, bottom: 160 }]
|
||||
}
|
||||
|
||||
fn default_n205_large_falling_spike() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 112, top: 80, right: 128, bottom: 112 },
|
||||
Rect { left: 128, top: 80, right: 144, bottom: 112 },
|
||||
]
|
||||
[Rect { left: 112, top: 80, right: 128, bottom: 112 }, Rect { left: 128, top: 80, right: 144, bottom: 112 }]
|
||||
}
|
||||
|
||||
fn default_n206_counter_bomb() -> [Rect<u16>; 3] {
|
||||
|
|
@ -3421,10 +3314,7 @@ fn default_n217_itoh() -> [Rect<u16>; 8] {
|
|||
}
|
||||
|
||||
fn default_n218_core_giant_ball() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 256, top: 120, right: 288, bottom: 152 },
|
||||
Rect { left: 288, top: 120, right: 320, bottom: 152 },
|
||||
]
|
||||
[Rect { left: 256, top: 120, right: 288, bottom: 152 }, Rect { left: 288, top: 120, right: 320, bottom: 152 }]
|
||||
}
|
||||
|
||||
fn default_n220_shovel_brigade() -> [Rect<u16>; 4] {
|
||||
|
|
@ -3516,24 +3406,15 @@ fn default_n228_droll() -> [Rect<u16>; 8] {
|
|||
}
|
||||
|
||||
fn default_n229_red_flowers_sprouts() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 0, top: 96, right: 48, bottom: 112 },
|
||||
Rect { left: 0, top: 112, right: 48, bottom: 128 },
|
||||
]
|
||||
[Rect { left: 0, top: 96, right: 48, bottom: 112 }, Rect { left: 0, top: 112, right: 48, bottom: 128 }]
|
||||
}
|
||||
|
||||
fn default_n230_red_flowers_blooming() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 48, top: 96, right: 96, bottom: 128 },
|
||||
Rect { left: 96, top: 96, right: 144, bottom: 128 },
|
||||
]
|
||||
[Rect { left: 48, top: 96, right: 96, bottom: 128 }, Rect { left: 96, top: 96, right: 144, bottom: 128 }]
|
||||
}
|
||||
|
||||
fn default_n231_rocket() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 176, top: 32, right: 208, bottom: 48 },
|
||||
Rect { left: 176, top: 48, right: 208, bottom: 64 },
|
||||
]
|
||||
[Rect { left: 176, top: 32, right: 208, bottom: 48 }, Rect { left: 176, top: 48, right: 208, bottom: 64 }]
|
||||
}
|
||||
|
||||
fn default_n232_orangebell() -> [Rect<u16>; 6] {
|
||||
|
|
@ -3547,7 +3428,7 @@ fn default_n232_orangebell() -> [Rect<u16>; 6] {
|
|||
]
|
||||
}
|
||||
|
||||
fn default_n233_orangebell_hat() -> [Rect<u16>; 8] {
|
||||
fn default_n233_orangebell_bat() -> [Rect<u16>; 8] {
|
||||
[
|
||||
Rect { left: 256, top: 0, right: 272, bottom: 16 },
|
||||
Rect { left: 272, top: 0, right: 288, bottom: 16 },
|
||||
|
|
@ -3561,10 +3442,7 @@ fn default_n233_orangebell_hat() -> [Rect<u16>; 8] {
|
|||
}
|
||||
|
||||
fn default_n234_red_flowers_picked() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 144, top: 96, right: 192, bottom: 112 },
|
||||
Rect { left: 144, top: 112, right: 192, bottom: 128 },
|
||||
]
|
||||
[Rect { left: 144, top: 96, right: 192, bottom: 112 }, Rect { left: 144, top: 112, right: 192, bottom: 128 }]
|
||||
}
|
||||
|
||||
fn default_n235_midorin() -> [Rect<u16>; 8] {
|
||||
|
|
@ -3610,10 +3488,7 @@ fn default_n238_press_sideways() -> [Rect<u16>; 3] {
|
|||
}
|
||||
|
||||
fn default_n239_cage_bars() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 192, top: 48, right: 256, bottom: 80 },
|
||||
Rect { left: 96, top: 112, right: 144, bottom: 144 },
|
||||
]
|
||||
[Rect { left: 192, top: 48, right: 256, bottom: 80 }, Rect { left: 96, top: 112, right: 144, bottom: 144 }]
|
||||
}
|
||||
|
||||
fn default_n240_mimiga_jailed() -> [Rect<u16>; 12] {
|
||||
|
|
@ -3709,11 +3584,8 @@ fn default_n248_misery_boss_vanishing() -> [Rect<u16>; 3] {
|
|||
]
|
||||
}
|
||||
|
||||
fn default_n249_misery_boss_energy_shot() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 48, top: 48, right: 64, bottom: 64 },
|
||||
Rect { left: 64, top: 48, right: 80, bottom: 64 },
|
||||
]
|
||||
fn default_n249_misery_boss_appearing() -> [Rect<u16>; 2] {
|
||||
[Rect { left: 48, top: 48, right: 64, bottom: 64 }, Rect { left: 64, top: 48, right: 80, bottom: 64 }]
|
||||
}
|
||||
|
||||
fn default_n250_misery_boss_lighting_ball() -> [Rect<u16>; 3] {
|
||||
|
|
@ -3725,10 +3597,7 @@ fn default_n250_misery_boss_lighting_ball() -> [Rect<u16>; 3] {
|
|||
}
|
||||
|
||||
fn default_n251_misery_boss_lighting() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 80, top: 32, right: 96, bottom: 64 },
|
||||
Rect { left: 96, top: 32, right: 112, bottom: 64 },
|
||||
]
|
||||
[Rect { left: 80, top: 32, right: 96, bottom: 64 }, Rect { left: 96, top: 32, right: 112, bottom: 64 }]
|
||||
}
|
||||
|
||||
fn default_n252_misery_boss_bats() -> [Rect<u16>; 8] {
|
||||
|
|
@ -3745,17 +3614,11 @@ fn default_n252_misery_boss_bats() -> [Rect<u16>; 8] {
|
|||
}
|
||||
|
||||
fn default_n253_experience_capsule() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 0, top: 64, right: 16, bottom: 80 },
|
||||
Rect { left: 16, top: 64, right: 32, bottom: 80 },
|
||||
]
|
||||
[Rect { left: 0, top: 64, right: 16, bottom: 80 }, Rect { left: 16, top: 64, right: 32, bottom: 80 }]
|
||||
}
|
||||
|
||||
fn default_n254_helicopter() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 0, top: 0, right: 128, bottom: 64 },
|
||||
Rect { left: 0, top: 64, right: 128, bottom: 128 },
|
||||
]
|
||||
[Rect { left: 0, top: 0, right: 128, bottom: 64 }, Rect { left: 0, top: 64, right: 128, bottom: 128 }]
|
||||
}
|
||||
|
||||
fn default_n255_helicopter_blades() -> [Rect<u16>; 8] {
|
||||
|
|
@ -3795,10 +3658,7 @@ fn default_n258_mimiga_sleeping() -> Rect<u16> {
|
|||
}
|
||||
|
||||
fn default_n259_curly_unconscious() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 224, top: 96, right: 240, bottom: 112 },
|
||||
Rect { left: 224, top: 112, right: 240, bottom: 128 },
|
||||
]
|
||||
[Rect { left: 224, top: 96, right: 240, bottom: 112 }, Rect { left: 224, top: 112, right: 240, bottom: 128 }]
|
||||
}
|
||||
|
||||
fn default_n260_shovel_brigade_caged() -> [Rect<u16>; 6] {
|
||||
|
|
@ -3853,11 +3713,11 @@ fn default_n263_doctor_boss() -> [Rect<u16>; 18] {
|
|||
]
|
||||
}
|
||||
|
||||
fn default_n264_doctor_boss_red_wave_projectile() -> Rect<u16> {
|
||||
fn default_n264_doctor_boss_red_projectile() -> Rect<u16> {
|
||||
Rect { left: 288, top: 0, right: 304, bottom: 16 }
|
||||
}
|
||||
|
||||
fn default_n265_doctor_boss_red_ball_projectile() -> [Rect<u16>; 3] {
|
||||
fn default_n265_doctor_boss_red_projectile_trail() -> [Rect<u16>; 3] {
|
||||
[
|
||||
Rect { left: 288, top: 16, right: 304, bottom: 32 },
|
||||
Rect { left: 288, top: 32, right: 304, bottom: 48 },
|
||||
|
|
@ -3865,11 +3725,8 @@ fn default_n265_doctor_boss_red_ball_projectile() -> [Rect<u16>; 3] {
|
|||
]
|
||||
}
|
||||
|
||||
fn default_n266_doctor_boss_red_ball_projectile_bouncing() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 304, top: 16, right: 320, bottom: 32 },
|
||||
Rect { left: 304, top: 32, right: 320, bottom: 48 },
|
||||
]
|
||||
fn default_n266_doctor_boss_red_projectile_bouncing() -> [Rect<u16>; 2] {
|
||||
[Rect { left: 304, top: 16, right: 320, bottom: 32 }, Rect { left: 304, top: 32, right: 320, bottom: 48 }]
|
||||
}
|
||||
|
||||
fn default_n267_muscle_doctor() -> [Rect<u16>; 20] {
|
||||
|
|
@ -3934,10 +3791,7 @@ fn default_n269_red_bat_bouncing() -> [Rect<u16>; 6] {
|
|||
}
|
||||
|
||||
fn default_n270_doctor_red_energy() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 170, top: 34, right: 174, bottom: 38 },
|
||||
Rect { left: 170, top: 42, right: 174, bottom: 46 },
|
||||
]
|
||||
[Rect { left: 170, top: 34, right: 174, bottom: 38 }, Rect { left: 170, top: 42, right: 174, bottom: 46 }]
|
||||
}
|
||||
|
||||
fn default_n273_droll_projectile() -> [Rect<u16>; 3] {
|
||||
|
|
@ -4017,10 +3871,7 @@ fn default_n278_little_family() -> [Rect<u16>; 6] {
|
|||
}
|
||||
|
||||
fn default_n279_large_falling_block() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 0, top: 16, right: 32, bottom: 48 },
|
||||
Rect { left: 16, top: 0, right: 32, bottom: 16 },
|
||||
]
|
||||
[Rect { left: 0, top: 16, right: 32, bottom: 48 }, Rect { left: 16, top: 0, right: 32, bottom: 16 }]
|
||||
}
|
||||
|
||||
fn default_n280_sue_teleported() -> [Rect<u16>; 4] {
|
||||
|
|
@ -4155,17 +4006,11 @@ fn default_n290_bat_misery() -> [Rect<u16>; 6] {
|
|||
}
|
||||
|
||||
fn default_n291_mini_undead_core_inactive() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 256, top: 80, right: 320, bottom: 120 },
|
||||
Rect { left: 256, top: 0, right: 320, bottom: 40 },
|
||||
]
|
||||
[Rect { left: 256, top: 80, right: 320, bottom: 120 }, Rect { left: 256, top: 0, right: 320, bottom: 40 }]
|
||||
}
|
||||
|
||||
fn default_n293_undead_core_energy_shot() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 240, top: 200, right: 280, bottom: 240 },
|
||||
Rect { left: 280, top: 200, right: 320, bottom: 240 },
|
||||
]
|
||||
[Rect { left: 240, top: 200, right: 280, bottom: 240 }, Rect { left: 280, top: 200, right: 320, bottom: 240 }]
|
||||
}
|
||||
|
||||
fn default_n295_cloud() -> [Rect<u16>; 4] {
|
||||
|
|
@ -4195,10 +4040,7 @@ fn default_n298_intro_doctor() -> [Rect<u16>; 8] {
|
|||
}
|
||||
|
||||
fn default_n299_intro_balrog_misery() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 0, top: 0, right: 48, bottom: 48 },
|
||||
Rect { left: 48, top: 0, right: 96, bottom: 48 },
|
||||
]
|
||||
[Rect { left: 0, top: 0, right: 48, bottom: 48 }, Rect { left: 48, top: 0, right: 96, bottom: 48 }]
|
||||
}
|
||||
|
||||
fn default_n300_intro_demon_crown() -> Rect<u16> {
|
||||
|
|
@ -4500,10 +4342,7 @@ fn default_n326_sue_itoh_human_transition() -> [Rect<u16>; 16] {
|
|||
}
|
||||
|
||||
fn default_n327_sneeze() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 240, top: 80, right: 256, bottom: 96 },
|
||||
Rect { left: 256, top: 80, right: 272, bottom: 96 },
|
||||
]
|
||||
[Rect { left: 240, top: 80, right: 256, bottom: 96 }, Rect { left: 256, top: 80, right: 272, bottom: 96 }]
|
||||
}
|
||||
|
||||
fn default_n328_human_transform_machine() -> Rect<u16> {
|
||||
|
|
@ -4511,10 +4350,7 @@ fn default_n328_human_transform_machine() -> Rect<u16> {
|
|||
}
|
||||
|
||||
fn default_n329_laboratory_fan() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 48, top: 0, right: 64, bottom: 16 },
|
||||
Rect { left: 64, top: 0, right: 80, bottom: 16 },
|
||||
]
|
||||
[Rect { left: 48, top: 0, right: 64, bottom: 16 }, Rect { left: 64, top: 0, right: 80, bottom: 16 }]
|
||||
}
|
||||
|
||||
fn default_n330_rolling() -> [Rect<u16>; 3] {
|
||||
|
|
@ -4543,10 +4379,7 @@ fn default_n332_ballos_shockwave() -> [Rect<u16>; 3] {
|
|||
}
|
||||
|
||||
fn default_n333_ballos_lighting() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 80, top: 120, right: 104, bottom: 144 },
|
||||
Rect { left: 104, top: 120, right: 128, bottom: 144 },
|
||||
]
|
||||
[Rect { left: 80, top: 120, right: 104, bottom: 144 }, Rect { left: 104, top: 120, right: 128, bottom: 144 }]
|
||||
}
|
||||
|
||||
fn default_n334_sweat() -> [Rect<u16>; 4] {
|
||||
|
|
@ -4567,10 +4400,7 @@ fn default_n335_ikachan() -> [Rect<u16>; 3] {
|
|||
}
|
||||
|
||||
fn default_n337_numahachi() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 256, top: 112, right: 288, bottom: 152 },
|
||||
Rect { left: 288, top: 112, right: 320, bottom: 152 },
|
||||
]
|
||||
[Rect { left: 256, top: 112, right: 288, bottom: 152 }, Rect { left: 288, top: 112, right: 320, bottom: 152 }]
|
||||
}
|
||||
|
||||
fn default_n338_green_devil() -> [Rect<u16>; 4] {
|
||||
|
|
@ -4630,10 +4460,7 @@ fn default_n343_ballos_2_cutscene() -> Rect<u16> {
|
|||
}
|
||||
|
||||
fn default_n344_ballos_2_eyes() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 272, top: 0, right: 296, bottom: 16 },
|
||||
Rect { left: 296, top: 0, right: 320, bottom: 16 },
|
||||
]
|
||||
[Rect { left: 272, top: 0, right: 296, bottom: 16 }, Rect { left: 296, top: 0, right: 320, bottom: 16 }]
|
||||
}
|
||||
|
||||
fn default_n345_ballos_skull_projectile() -> [Rect<u16>; 4] {
|
||||
|
|
@ -4659,10 +4486,7 @@ fn default_n347_hoppy() -> [Rect<u16>; 4] {
|
|||
}
|
||||
|
||||
fn default_n348_ballos_4_spikes() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 128, top: 152, right: 160, bottom: 176 },
|
||||
Rect { left: 160, top: 152, right: 192, bottom: 176 },
|
||||
]
|
||||
[Rect { left: 128, top: 152, right: 160, bottom: 176 }, Rect { left: 160, top: 152, right: 192, bottom: 176 }]
|
||||
}
|
||||
|
||||
fn default_n349_statue() -> Rect<u16> {
|
||||
|
|
@ -4758,10 +4582,7 @@ fn default_n355_quote_and_curly_on_balrog() -> [Rect<u16>; 4] {
|
|||
}
|
||||
|
||||
fn default_n356_balrog_rescuing() -> [Rect<u16>; 2] {
|
||||
[
|
||||
Rect { left: 240, top: 128, right: 280, bottom: 152 },
|
||||
Rect { left: 240, top: 152, right: 280, bottom: 176 },
|
||||
]
|
||||
[Rect { left: 240, top: 128, right: 280, bottom: 152 }, Rect { left: 240, top: 152, right: 280, bottom: 176 }]
|
||||
}
|
||||
|
||||
fn default_n357_puppy_ghost() -> Rect<u16> {
|
||||
|
|
@ -4822,28 +4643,35 @@ fn default_b02_balfrog() -> [Rect<u16>; 18] {
|
|||
|
||||
fn default_b03_monster_x() -> [Rect<u16>; 29] {
|
||||
[
|
||||
Rect { left: 216, top: 0, right: 320, bottom: 48 }, // face
|
||||
// face
|
||||
Rect { left: 216, top: 0, right: 320, bottom: 48 },
|
||||
Rect { left: 216, top: 48, right: 320, bottom: 96 },
|
||||
Rect { left: 216, top: 144, right: 320, bottom: 192 },
|
||||
Rect { left: 0, top: 0, right: 72, bottom: 32 }, // tracks up
|
||||
// tracks up
|
||||
Rect { left: 0, top: 0, right: 72, bottom: 32 },
|
||||
Rect { left: 0, top: 32, right: 72, bottom: 64 },
|
||||
Rect { left: 72, top: 0, right: 144, bottom: 32 },
|
||||
Rect { left: 144, top: 0, right: 216, bottom: 32 },
|
||||
Rect { left: 72, top: 32, right: 144, bottom: 64 },
|
||||
Rect { left: 144, top: 32, right: 216, bottom: 64 },
|
||||
Rect { left: 0, top: 64, right: 72, bottom: 96 }, // tracks down
|
||||
// tracks down
|
||||
Rect { left: 0, top: 64, right: 72, bottom: 96 },
|
||||
Rect { left: 0, top: 96, right: 72, bottom: 128 },
|
||||
Rect { left: 72, top: 64, right: 144, bottom: 96 },
|
||||
Rect { left: 144, top: 64, right: 216, bottom: 96 },
|
||||
Rect { left: 72, top: 96, right: 144, bottom: 128 },
|
||||
Rect { left: 144, top: 96, right: 216, bottom: 128 },
|
||||
Rect { left: 0, top: 128, right: 72, bottom: 160 }, // frame
|
||||
// frame
|
||||
Rect { left: 0, top: 128, right: 72, bottom: 160 },
|
||||
Rect { left: 72, top: 128, right: 144, bottom: 160 },
|
||||
Rect { left: 0, top: 160, right: 72, bottom: 192 },
|
||||
Rect { left: 72, top: 160, right: 144, bottom: 192 },
|
||||
Rect { left: 216, top: 96, right: 264, bottom: 144 }, // shield left
|
||||
Rect { left: 264, top: 96, right: 312, bottom: 144 }, // shield right
|
||||
Rect { left: 0, top: 192, right: 16, bottom: 208 }, // part 4
|
||||
// shield left
|
||||
Rect { left: 216, top: 96, right: 264, bottom: 144 },
|
||||
// shield right
|
||||
Rect { left: 264, top: 96, right: 312, bottom: 144 },
|
||||
// part 4
|
||||
Rect { left: 0, top: 192, right: 16, bottom: 208 },
|
||||
Rect { left: 16, top: 192, right: 32, bottom: 208 },
|
||||
Rect { left: 32, top: 192, right: 48, bottom: 208 },
|
||||
Rect { left: 48, top: 192, right: 64, bottom: 208 },
|
||||
|
|
@ -4856,16 +4684,87 @@ fn default_b03_monster_x() -> [Rect<u16>; 29] {
|
|||
|
||||
fn default_b04_core() -> [Rect<u16>; 10] {
|
||||
[
|
||||
Rect { left: 0, top: 0, right: 72, bottom: 112 }, // face
|
||||
// face
|
||||
Rect { left: 0, top: 0, right: 72, bottom: 112 },
|
||||
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
|
||||
// tail
|
||||
Rect { left: 72, top: 0, right: 160, bottom: 112 },
|
||||
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
|
||||
// small head
|
||||
Rect { left: 256, top: 0, right: 320, bottom: 40 },
|
||||
Rect { left: 256, top: 40, right: 320, bottom: 80 },
|
||||
Rect { left: 256, top: 80, right: 320, bottom: 120 },
|
||||
]
|
||||
}
|
||||
|
||||
fn default_b05_ironhead() -> [Rect<u16>; 18] {
|
||||
[
|
||||
// set 1
|
||||
Rect { left: 0, top: 0, right: 64, bottom: 24 },
|
||||
Rect { left: 64, top: 0, right: 128, bottom: 24 },
|
||||
Rect { left: 128, top: 0, right: 192, bottom: 24 },
|
||||
Rect { left: 64, top: 0, right: 128, bottom: 24 },
|
||||
Rect { left: 0, top: 0, right: 64, bottom: 24 },
|
||||
Rect { left: 192, top: 0, right: 256, bottom: 24 },
|
||||
Rect { left: 256, top: 0, right: 320, bottom: 24 },
|
||||
Rect { left: 192, top: 0, right: 256, bottom: 24 },
|
||||
Rect { left: 256, top: 48, right: 320, bottom: 72 },
|
||||
// set 2
|
||||
Rect { left: 0, top: 24, right: 64, bottom: 48 },
|
||||
Rect { left: 64, top: 24, right: 128, bottom: 48 },
|
||||
Rect { left: 128, top: 24, right: 192, bottom: 48 },
|
||||
Rect { left: 64, top: 24, right: 128, bottom: 48 },
|
||||
Rect { left: 0, top: 24, right: 64, bottom: 48 },
|
||||
Rect { left: 192, top: 24, right: 256, bottom: 48 },
|
||||
Rect { left: 256, top: 24, right: 320, bottom: 48 },
|
||||
Rect { left: 192, top: 24, right: 256, bottom: 48 },
|
||||
Rect { left: 256, top: 48, right: 320, bottom: 72 },
|
||||
]
|
||||
}
|
||||
|
||||
fn default_b06_sisters() -> [Rect<u16>; 14] {
|
||||
[
|
||||
// head
|
||||
Rect { left: 0, top: 80, right: 40, bottom: 112 },
|
||||
Rect { left: 40, top: 80, right: 80, bottom: 112 },
|
||||
Rect { left: 80, top: 80, right: 120, bottom: 112 },
|
||||
Rect { left: 120, top: 80, right: 160, bottom: 112 },
|
||||
Rect { left: 0, top: 112, right: 40, bottom: 144 },
|
||||
Rect { left: 40, top: 112, right: 80, bottom: 144 },
|
||||
Rect { left: 80, top: 112, right: 120, bottom: 144 },
|
||||
Rect { left: 120, top: 112, right: 160, bottom: 144 },
|
||||
// body
|
||||
Rect { left: 0, top: 0, right: 40, bottom: 40 },
|
||||
Rect { left: 40, top: 0, right: 80, bottom: 40 },
|
||||
Rect { left: 80, top: 0, right: 120, bottom: 40 },
|
||||
Rect { left: 0, top: 40, right: 40, bottom: 80 },
|
||||
Rect { left: 40, top: 40, right: 80, bottom: 80 },
|
||||
Rect { left: 80, top: 40, right: 120, bottom: 80 },
|
||||
]
|
||||
}
|
||||
|
||||
fn default_b07_undead_core() -> [Rect<u16>; 15] {
|
||||
[
|
||||
// face
|
||||
Rect { left: 0, top: 0, right: 0, bottom: 0 },
|
||||
Rect { left: 160, top: 112, right: 232, bottom: 152 },
|
||||
Rect { left: 160, top: 152, right: 232, bottom: 192 },
|
||||
Rect { left: 160, top: 192, right: 232, bottom: 232 },
|
||||
Rect { left: 248, top: 160, right: 320, bottom: 200 },
|
||||
// head
|
||||
Rect { left: 0, top: 0, right: 72, bottom: 112 },
|
||||
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 },
|
||||
// tail
|
||||
Rect { left: 72, top: 0, right: 160, bottom: 112 },
|
||||
Rect { left: 72, top: 112, right: 160, bottom: 224 },
|
||||
Rect { left: 0, top: 0, right: 0, bottom: 0 },
|
||||
// small head
|
||||
Rect { left: 256, top: 0, right: 320, bottom: 40 },
|
||||
Rect { left: 256, top: 40, right: 320, bottom: 80 },
|
||||
Rect { left: 256, top: 80, right: 320, bottom: 120 },
|
||||
]
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ pub trait BackendRenderer {
|
|||
|
||||
fn draw_outline_rect(&mut self, rect: Rect, line_width: usize, color: Color) -> GameResult;
|
||||
|
||||
fn set_clip_rect(&mut self, rect: Option<Rect>) -> GameResult;
|
||||
|
||||
fn imgui(&self) -> GameResult<&mut imgui::Context>;
|
||||
|
||||
fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult;
|
||||
|
|
|
|||
|
|
@ -118,6 +118,10 @@ impl BackendRenderer for NullRenderer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn set_clip_rect(&mut self, rect: Option<Rect>) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn imgui(&self) -> GameResult<&mut imgui::Context> {
|
||||
unsafe { Ok(&mut *self.0.as_ptr()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ use std::ffi::c_void;
|
|||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use imgui::internal::RawWrapper;
|
||||
use imgui::{ConfigFlags, DrawCmd, DrawData, ImString, Key, MouseCursor, TextureId};
|
||||
use imgui::internal::RawWrapper;
|
||||
use sdl2::{EventPump, keyboard, pixels, Sdl, VideoSubsystem};
|
||||
use sdl2::event::{Event, WindowEvent};
|
||||
use sdl2::keyboard::Scancode;
|
||||
use sdl2::mouse::{Cursor, SystemCursor};
|
||||
|
|
@ -14,7 +15,6 @@ use sdl2::pixels::PixelFormatEnum;
|
|||
use sdl2::render::{Texture, TextureCreator, WindowCanvas};
|
||||
use sdl2::video::GLProfile;
|
||||
use sdl2::video::WindowContext;
|
||||
use sdl2::{keyboard, pixels, EventPump, Sdl, VideoSubsystem};
|
||||
|
||||
use crate::common::{Color, Rect};
|
||||
use crate::framework::backend::{
|
||||
|
|
@ -570,6 +570,23 @@ impl BackendRenderer for SDL2Renderer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn set_clip_rect(&mut self, rect: Option<Rect>) -> GameResult {
|
||||
let mut refs = self.refs.borrow_mut();
|
||||
|
||||
if let Some(rect) = &rect {
|
||||
refs.canvas.set_clip_rect(Some(sdl2::rect::Rect::new(
|
||||
rect.left as i32,
|
||||
rect.top as i32,
|
||||
rect.width() as u32,
|
||||
rect.height() as u32,
|
||||
)));
|
||||
} else {
|
||||
refs.canvas.set_clip_rect(None);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn imgui(&self) -> GameResult<&mut imgui::Context> {
|
||||
unsafe { Ok(&mut *self.imgui.as_ptr()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,15 @@ pub fn draw_outline_rect(ctx: &mut Context, rect: Rect, line_width: usize, color
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_clip_rect(ctx: &mut Context, rect: Option<Rect>) -> GameResult {
|
||||
if let Some(renderer) = ctx.renderer.as_mut() {
|
||||
return renderer.set_clip_rect(rect);
|
||||
}
|
||||
|
||||
Err(GameError::RenderError("Rendering backend hasn't been initialized yet.".to_string()))
|
||||
}
|
||||
|
||||
|
||||
pub fn imgui_context(ctx: &Context) -> GameResult<&mut imgui::Context> {
|
||||
if let Some(renderer) = ctx.renderer.as_ref() {
|
||||
return renderer.imgui();
|
||||
|
|
|
|||
|
|
@ -1159,6 +1159,23 @@ impl BackendRenderer for OpenGLRenderer {
|
|||
fn supports_vertex_draw(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn set_clip_rect(&mut self, rect: Option<Rect>) -> GameResult {
|
||||
if let Some((_, gl)) = self.get_context() {
|
||||
unsafe {
|
||||
if let Some(rect) = &rect {
|
||||
gl.gl.Enable(gl::SCISSOR_TEST);
|
||||
gl.gl.Scissor(rect.left as GLint, rect.top as GLint, rect.width() as GLint, rect.height() as GLint);
|
||||
} else {
|
||||
gl.gl.Disable(gl::SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RenderError("No OpenGL context available!".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenGLRenderer {
|
||||
|
|
|
|||
13
src/lib.rs
13
src/lib.rs
|
|
@ -22,6 +22,7 @@ use crate::framework::ui::UI;
|
|||
use crate::framework::vfs::PhysicalFS;
|
||||
use crate::scene::loading_scene::LoadingScene;
|
||||
use crate::scene::Scene;
|
||||
use crate::scripting::tsc::text_script::ScriptMode;
|
||||
use crate::shared_game_state::{SharedGameState, TimingMode};
|
||||
use crate::texture_set::{G_MAG, I_MAG};
|
||||
|
||||
|
|
@ -99,16 +100,22 @@ impl Game {
|
|||
if let Some(scene) = self.scene.as_mut() {
|
||||
let state_ref = unsafe { &mut *self.state.get() };
|
||||
|
||||
let speed = if state_ref.textscript_vm.mode == ScriptMode::Map && state_ref.textscript_vm.flags.cutscene_skip() {
|
||||
4.0 * state_ref.settings.speed
|
||||
} else {
|
||||
1.0 * state_ref.settings.speed
|
||||
};
|
||||
|
||||
match state_ref.settings.timing_mode {
|
||||
TimingMode::_50Hz | TimingMode::_60Hz => {
|
||||
let last_tick = self.next_tick;
|
||||
|
||||
while self.start_time.elapsed().as_nanos() >= self.next_tick && self.loops < 10 {
|
||||
if (state_ref.settings.speed - 1.0).abs() < 0.01 {
|
||||
if (speed - 1.0).abs() < 0.01 {
|
||||
self.next_tick += state_ref.settings.timing_mode.get_delta() as u128;
|
||||
} else {
|
||||
self.next_tick +=
|
||||
(state_ref.settings.timing_mode.get_delta() as f64 / state_ref.settings.speed) as u128;
|
||||
(state_ref.settings.timing_mode.get_delta() as f64 / speed) as u128;
|
||||
}
|
||||
self.loops += 1;
|
||||
}
|
||||
|
|
@ -117,7 +124,7 @@ impl Game {
|
|||
log::warn!("Frame skip is way too high, a long system lag occurred?");
|
||||
self.last_tick = self.start_time.elapsed().as_nanos();
|
||||
self.next_tick = self.last_tick
|
||||
+ (state_ref.settings.timing_mode.get_delta() as f64 / state_ref.settings.speed) as u128;
|
||||
+ (state_ref.settings.timing_mode.get_delta() as f64 / speed) as u128;
|
||||
self.loops = 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
252
src/npc/ai/balcony.rs
Normal file
252
src/npc/ai/balcony.rs
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
use crate::common::Direction;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::NPC;
|
||||
use crate::player::Player;
|
||||
use crate::rng::RNG;
|
||||
use crate::{GameResult, SharedGameState};
|
||||
|
||||
impl NPC {
|
||||
pub(crate) fn tick_n254_helicopter(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
|
||||
match self.action_num {
|
||||
0 => {
|
||||
self.action_num = 1;
|
||||
|
||||
// blades
|
||||
let mut npc = NPC::create(255, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
||||
npc.x = self.x + 0x2400;
|
||||
npc.y = self.y - 0x7200;
|
||||
npc.parent_id = self.id;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc.clone());
|
||||
|
||||
npc.x = self.x - 0x4000;
|
||||
npc.y = self.y - 0x6800;
|
||||
npc.direction = Direction::Right;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
20 => {
|
||||
self.action_num = 21;
|
||||
self.action_counter = 0;
|
||||
self.action_counter2 = 60;
|
||||
}
|
||||
30 => {
|
||||
self.action_num = 21;
|
||||
|
||||
// momorin
|
||||
let mut npc = NPC::create(223, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x - 0x1600;
|
||||
npc.y = self.y - 0x1c00;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
40 => {
|
||||
self.action_num = 21;
|
||||
|
||||
// momorin
|
||||
let mut npc = NPC::create(223, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x - 0x1200;
|
||||
npc.y = self.y - 0x1c00;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
|
||||
// santa
|
||||
let mut npc = NPC::create(40, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x - 0x2c00;
|
||||
npc.y = self.y - 0x1c00;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
|
||||
// chaco
|
||||
let mut npc = NPC::create(223, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x - 0x4600;
|
||||
npc.y = self.y - 0x1c00;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 1 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n254_helicopter[dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n255_helicopter_blades(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
if self.direction == Direction::Left {
|
||||
self.display_bounds.left = 0x7000;
|
||||
self.display_bounds.right = 0x7000;
|
||||
} else {
|
||||
self.display_bounds.left = 0x5000;
|
||||
self.display_bounds.right = 0x5000;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(parent) = self.get_parent_ref_mut(npc_list) {
|
||||
if parent.action_num >= 20 {
|
||||
self.action_num = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
10 | 11 => {
|
||||
self.action_num = 11;
|
||||
|
||||
self.anim_num += 1;
|
||||
if self.anim_num > 3 {
|
||||
self.anim_num = 0;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 4 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n255_helicopter_blades[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n260_shovel_brigade_caged(
|
||||
&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.x += 0x200;
|
||||
self.y -= 0x400;
|
||||
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;
|
||||
}
|
||||
}
|
||||
10 => {
|
||||
self.action_num = 11;
|
||||
self.anim_num = 2;
|
||||
|
||||
// create heart
|
||||
let mut npc = NPC::create(87, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x;
|
||||
npc.y = self.y - 0x2000;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if player.x < self.x { Direction::Left } else { Direction::Right };
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 3 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n260_shovel_brigade_caged[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n261_chie_caged(&mut self, state: &mut SharedGameState, players: [&mut Player; 2]) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.x -= 0x200;
|
||||
self.y -= 0x400;
|
||||
self.action_num = 1;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 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);
|
||||
self.direction = if player.x < self.x { Direction::Left } else { Direction::Right };
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n261_chie_caged[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n262_chaco_caged(&mut self, state: &mut SharedGameState, players: [&mut Player; 2]) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.x -= 0x200;
|
||||
self.y -= 0x400;
|
||||
self.action_num = 1;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 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);
|
||||
self.direction = if player.x < self.x { Direction::Left } else { Direction::Right };
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n262_chaco_caged[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1212,7 +1212,6 @@ impl NPC {
|
|||
|
||||
self.anim_rect = state.constants.npc.n169_balrog_shooting_missiles[self.anim_num as usize + dir_offset];
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1280,4 +1279,37 @@ impl NPC {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n306_balrog_nurse(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 0;
|
||||
self.y += 0x800;
|
||||
}
|
||||
|
||||
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.n306_balrog_nurse[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -566,4 +566,248 @@ impl NPC {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n278_little_family(&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_counter == 10 {
|
||||
self.action_num = 11;
|
||||
self.action_counter = self.rng.range(0..16) as u16;
|
||||
self.anim_num = 0;
|
||||
self.direction = if self.rng.range(0..9) % 2 != 0 { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
|
||||
if self.direction == Direction::Left && self.flags.hit_left_wall() {
|
||||
self.direction = Direction::Right;
|
||||
}
|
||||
|
||||
if self.direction == Direction::Right && self.flags.hit_right_wall() {
|
||||
self.direction = Direction::Left;
|
||||
}
|
||||
|
||||
self.vel_x = self.direction.vector_x() * 0x100;
|
||||
|
||||
self.animate(4, 0, 1);
|
||||
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 offset = match self.event_num {
|
||||
200 => 0,
|
||||
210 => 2,
|
||||
_ => 4,
|
||||
};
|
||||
|
||||
self.anim_rect = state.constants.npc.n278_little_family[self.anim_num as usize + offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n305_small_puppy(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.y -= 0x2000;
|
||||
self.anim_counter = self.rng.range(0..6) as u16;
|
||||
}
|
||||
|
||||
if self.action_num == 1 {
|
||||
self.animate(6, 0, 1);
|
||||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n305_small_puppy[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n326_sue_itoh_human_transition(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.anim_num = 0;
|
||||
self.x += 0x2000;
|
||||
self.y -= 0x1000;
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 80 {
|
||||
self.action_num = 10;
|
||||
self.action_counter = 0;
|
||||
} else {
|
||||
if self.direction != Direction::Left {
|
||||
if self.action_counter == 50 {
|
||||
self.anim_num = 1;
|
||||
}
|
||||
if self.action_counter == 60 {
|
||||
self.anim_num = 0;
|
||||
}
|
||||
} else {
|
||||
if self.action_counter == 30 {
|
||||
self.anim_num = 1;
|
||||
}
|
||||
if self.action_counter == 40 {
|
||||
self.anim_num = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10 => {
|
||||
self.action_counter += 1;
|
||||
if self.action_counter <= 50 {
|
||||
self.anim_num = if self.action_counter & 2 != 0 { 2 } else { 3 };
|
||||
} else {
|
||||
self.action_num = 15;
|
||||
self.anim_num = 4;
|
||||
|
||||
let actr: &mut i16 = unsafe { std::mem::transmute(&mut self.action_counter) };
|
||||
if self.direction == Direction::Left {
|
||||
*actr = -20;
|
||||
} else {
|
||||
*actr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
15 => {
|
||||
let actr: &mut i16 = unsafe { std::mem::transmute(&mut self.action_counter) };
|
||||
*actr += 1;
|
||||
if *actr > 40 {
|
||||
*actr = 0;
|
||||
self.action_num = 20;
|
||||
}
|
||||
}
|
||||
20 => {
|
||||
self.vel_y += 0x40;
|
||||
if self.vel_y > 0x5FF {
|
||||
self.vel_y = 0x5FF;
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 50 {
|
||||
self.action_num = 30;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 6;
|
||||
|
||||
let mut npc = NPC::create(327, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
||||
npc.x = self.x;
|
||||
npc.y = if self.direction == Direction::Left { self.y - 0x1000 } else { self.y - 0x2000 };
|
||||
npc.parent_id = self.id;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
}
|
||||
30 => {
|
||||
self.action_counter += 1;
|
||||
if self.action_counter == 30 {
|
||||
self.anim_num = 7;
|
||||
}
|
||||
if self.action_counter == 40 {
|
||||
self.action_num = 40;
|
||||
}
|
||||
}
|
||||
40 | 41 => {
|
||||
if self.action_num == 40 {
|
||||
self.action_num = 41;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 0;
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter == 30 {
|
||||
self.anim_num = 1;
|
||||
}
|
||||
if self.action_counter == 40 {
|
||||
self.anim_num = 0;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 8 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n326_sue_itoh_human_transition[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n327_sneeze(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
|
||||
self.action_counter += 1;
|
||||
|
||||
if self.action_num == 0 {
|
||||
if self.action_counter < 4 {
|
||||
self.y -= 0x400;
|
||||
}
|
||||
|
||||
if let Some(parent) = self.get_parent_ref_mut(npc_list) {
|
||||
if parent.anim_num == 7 {
|
||||
self.action_num = 1;
|
||||
self.anim_num = 1;
|
||||
self.target_x = self.x;
|
||||
self.target_y = self.y;
|
||||
}
|
||||
}
|
||||
} else if self.action_num == 1 {
|
||||
if self.action_counter >= 48 {
|
||||
self.x = self.target_x;
|
||||
self.y = self.target_y;
|
||||
} else {
|
||||
self.x += self.target_x + self.rng.range(-1..1) * 0x200;
|
||||
self.y += self.target_y + self.rng.range(-1..1) * 0x200;
|
||||
}
|
||||
}
|
||||
|
||||
if self.action_counter > 70 {
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n327_sneeze[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -786,7 +786,7 @@ impl NPC {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n259_curly_unconcious(
|
||||
pub(crate) fn tick_n259_curly_unconscious(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
|
|
|
|||
1010
src/npc/ai/doctor.rs
1010
src/npc/ai/doctor.rs
File diff suppressed because it is too large
Load diff
75
src/npc/ai/hell.rs
Normal file
75
src/npc/ai/hell.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
use crate::{GameResult, SharedGameState};
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::Direction;
|
||||
use crate::npc::NPC;
|
||||
use crate::rng::RNG;
|
||||
|
||||
impl NPC {
|
||||
pub(crate) fn tick_n337_numahachi(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.y -= 0x1000;
|
||||
}
|
||||
|
||||
if self.action_num == 1 {
|
||||
self.action_num = 2;
|
||||
self.anim_num = 0;
|
||||
self.vel_x = 0;
|
||||
}
|
||||
|
||||
if self.action_num == 2{
|
||||
self.animate(50, 0, 1);
|
||||
}
|
||||
|
||||
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.n337_numahachi[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n357_puppy_ghost(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
match self.action_num {
|
||||
0 => {
|
||||
self.anim_rect = state.constants.npc.n357_puppy_ghost;
|
||||
self.action_counter += 1;
|
||||
}
|
||||
10 | 11 => {
|
||||
if self.action_num == 10 {
|
||||
self.action_num = 11;
|
||||
self.action_counter = 0;
|
||||
state.sound_manager.play_sfx(29);
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
self.anim_rect = state.constants.npc.n357_puppy_ghost;
|
||||
|
||||
if self.action_counter & 2 != 0 {
|
||||
self.anim_rect.right = self.anim_rect.left;
|
||||
}
|
||||
|
||||
if self.action_counter > 50 {
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if self.action_counter & 8 == 1 {
|
||||
state.create_caret(
|
||||
self.x + self.rng.range(-8..8) * 0x200,
|
||||
self.y + 0x1000,
|
||||
CaretType::LittleParticles,
|
||||
Direction::Up,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::common::{CDEG_RAD, Direction};
|
||||
use crate::common::{Direction, CDEG_RAD};
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::NPC;
|
||||
|
|
@ -88,7 +88,12 @@ impl NPC {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n088_igor_boss(&mut self, state: &mut SharedGameState, players: [&mut Player; 2], npc_list: &NPCList) -> GameResult {
|
||||
pub(crate) fn tick_n088_igor_boss(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
|
|
@ -98,14 +103,7 @@ impl NPC {
|
|||
self.vel_x = 0;
|
||||
}
|
||||
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 5 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
if self.anim_num > 1 {
|
||||
self.anim_num = 0;
|
||||
}
|
||||
}
|
||||
self.animate(5, 0, 1);
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 50 {
|
||||
|
|
@ -131,14 +129,7 @@ impl NPC {
|
|||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 3 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
if self.anim_num > 5 {
|
||||
self.anim_num = 2;
|
||||
}
|
||||
}
|
||||
self.animate(3, 2, 5);
|
||||
|
||||
self.vel_x = self.direction.vector_x() * 0x200;
|
||||
|
||||
|
|
@ -155,8 +146,9 @@ impl NPC {
|
|||
self.damage = 2;
|
||||
self.vel_x = (self.vel_x * 3) / 2;
|
||||
self.vel_y = -0x400;
|
||||
} else if (self.direction == Direction::Left && self.x - 24 * 0x200 < player.x)
|
||||
|| (self.direction == Direction::Right && self.x + 24 * 0x200 > player.x) {
|
||||
} else if (self.direction == Direction::Left && self.x - 0x3000 < player.x)
|
||||
|| (self.direction == Direction::Right && self.x + 0x3000 > player.x)
|
||||
{
|
||||
self.action_num = 4;
|
||||
}
|
||||
}
|
||||
|
|
@ -174,7 +166,7 @@ impl NPC {
|
|||
self.action_counter = 0;
|
||||
self.anim_num = 7;
|
||||
self.damage = 5;
|
||||
self.hit_bounds.left = 24 * 0x200;
|
||||
self.hit_bounds.left = 0x3000;
|
||||
self.hit_bounds.top = 1;
|
||||
|
||||
state.sound_manager.play_sfx(70);
|
||||
|
|
@ -227,13 +219,15 @@ impl NPC {
|
|||
self.action_num = 10;
|
||||
self.action_counter = 0;
|
||||
|
||||
let player = self.get_closest_player_mut(players);
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if player.x < self.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 100 && self.action_counter % 6 == 1 {
|
||||
let deg = (if self.direction == Direction::Left { 0x88 } else { 0xf8 } + self.rng.range(-16..16)) as f64 * CDEG_RAD;
|
||||
let deg = (if self.direction == Direction::Left { 0x88 } else { 0xf8 } + self.rng.range(-16..16))
|
||||
as f64
|
||||
* CDEG_RAD;
|
||||
let vel_x = (deg.cos() * 1536.0) as i32;
|
||||
let vel_y = (deg.sin() * 1536.0) as i32;
|
||||
|
||||
|
|
@ -270,12 +264,18 @@ impl NPC {
|
|||
self.y += self.vel_y;
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 12 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n088_igor_boss[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n089_igor_dead(&mut self, state: &mut SharedGameState, players: [&mut Player; 2], npc_list: &NPCList) -> GameResult {
|
||||
pub(crate) fn tick_n089_igor_dead(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
|
|
@ -327,13 +327,13 @@ impl NPC {
|
|||
self.action_counter += 1;
|
||||
if (self.action_counter & 0x02) != 0 && self.action_counter < 100 {
|
||||
self.anim_num = 0;
|
||||
self.display_bounds.left = 20 * 0x200;
|
||||
self.display_bounds.right = 20 * 0x200;
|
||||
self.display_bounds.top = 20 * 0x200;
|
||||
self.display_bounds.left = 0x2800;
|
||||
self.display_bounds.right = 0x2800;
|
||||
self.display_bounds.top = 0x2800;
|
||||
} else {
|
||||
self.anim_num = 1;
|
||||
self.display_bounds.left = 12 * 0x200;
|
||||
self.display_bounds.right = 12 * 0x200;
|
||||
self.display_bounds.left = 0x1800;
|
||||
self.display_bounds.right = 0x1800;
|
||||
self.display_bounds.top = 0x1000;
|
||||
}
|
||||
|
||||
|
|
@ -378,4 +378,158 @@ impl NPC {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n268_igor_enemy(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
if self.x < player.x - 0x28000
|
||||
|| self.x > player.x + 0x28000
|
||||
|| self.y < player.y - 0x1E000
|
||||
|| self.y > player.y + 0x1E000
|
||||
{
|
||||
self.action_num = 1;
|
||||
}
|
||||
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.y += 0x1000;
|
||||
}
|
||||
|
||||
self.animate(20, 0, 1);
|
||||
|
||||
if self.x < player.x + 0xE000
|
||||
&& self.x > player.x - 0xE000
|
||||
&& self.x < player.x + 0x6000
|
||||
&& self.x > player.x - 0xE000
|
||||
{
|
||||
self.action_num = 10;
|
||||
}
|
||||
|
||||
if self.shock != 0 {
|
||||
self.action_num = 10;
|
||||
}
|
||||
}
|
||||
10 | 11 => {
|
||||
if self.action_num == 10 {
|
||||
self.action_num = 11;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 0;
|
||||
self.action_counter = 0;
|
||||
self.direction = if self.x < player.x { Direction::Right } else { Direction::Left };
|
||||
}
|
||||
|
||||
self.vel_x = self.direction.vector_x() * 0x200;
|
||||
if self.x < player.x + 0x8000 && self.x > player.x - 0x8000 {
|
||||
self.action_num = 20;
|
||||
self.action_counter = 0;
|
||||
}
|
||||
|
||||
if self.vel_x < 0 && self.flags.hit_left_wall() {
|
||||
self.action_num = 20;
|
||||
self.action_counter = 0;
|
||||
}
|
||||
|
||||
if self.vel_x > 0 && self.flags.hit_right_wall() {
|
||||
self.action_num = 20;
|
||||
self.action_counter = 0;
|
||||
}
|
||||
|
||||
self.animate(4, 2, 5);
|
||||
}
|
||||
20 => {
|
||||
self.vel_x = 0;
|
||||
self.anim_num = 6;
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 10 {
|
||||
self.action_num = 30;
|
||||
|
||||
self.vel_x = self.direction.vector_x() * 0x200;
|
||||
self.vel_y = -0x5FF;
|
||||
|
||||
state.sound_manager.play_sfx(108);
|
||||
}
|
||||
}
|
||||
30 => {
|
||||
self.anim_num = 7;
|
||||
if self.flags.hit_bottom_wall() {
|
||||
self.action_num = 40;
|
||||
self.action_counter = 0;
|
||||
state.quake_counter = 20;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
}
|
||||
40 => {
|
||||
self.vel_x = 0;
|
||||
self.anim_num = 6;
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 30 {
|
||||
self.action_num = 50;
|
||||
}
|
||||
}
|
||||
50 | 51 => {
|
||||
if self.action_num == 50 {
|
||||
self.action_num = 51;
|
||||
self.action_counter = 0;
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if player.x < self.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 30 && self.action_counter % 4 == 1 {
|
||||
let deg = (if self.direction == Direction::Left { 0x88 } else { 0xf8 } + self.rng.range(-16..16))
|
||||
as f64
|
||||
* CDEG_RAD;
|
||||
let vel_x = (deg.cos() * 1536.0) as i32;
|
||||
let vel_y = (deg.sin() * 1536.0) as i32;
|
||||
|
||||
let mut npc = NPC::create(11, &state.npc_table);
|
||||
|
||||
npc.cond.set_alive(true);
|
||||
npc.direction = Direction::Left;
|
||||
npc.x = self.x;
|
||||
npc.y = self.y + 0x800;
|
||||
npc.vel_x = vel_x;
|
||||
npc.vel_y = vel_y;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
state.sound_manager.play_sfx(12);
|
||||
}
|
||||
|
||||
if self.action_counter < 50 && self.action_counter & 2 != 0 {
|
||||
self.anim_num = 9;
|
||||
} else {
|
||||
self.anim_num = 8;
|
||||
}
|
||||
|
||||
if self.action_counter > 82 {
|
||||
self.action_num = 10;
|
||||
self.direction = if player.x < self.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.vel_y += 0x33;
|
||||
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 { 10 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n268_igor_enemy[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
use crate::caret::CaretType;
|
||||
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 {
|
||||
|
|
@ -18,7 +21,7 @@ impl NPC {
|
|||
self.anim_num = 0;
|
||||
}
|
||||
|
||||
let player = self.get_closest_player_mut(players);
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
if self.x > player.x {
|
||||
self.direction = Direction::Left;
|
||||
} else {
|
||||
|
|
@ -106,4 +109,371 @@ impl NPC {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n242_bat_last_cave(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
loop {
|
||||
match self.action_num {
|
||||
0 => {
|
||||
self.action_num = 1;
|
||||
self.target_x = self.x;
|
||||
self.target_y = self.y;
|
||||
self.action_counter = self.rng.range(0..50) as u16;
|
||||
continue;
|
||||
}
|
||||
1 if self.action_counter > 0 => {
|
||||
self.action_counter -= 1;
|
||||
}
|
||||
1 => {
|
||||
self.action_num = 2;
|
||||
self.vel_y = 0x400;
|
||||
continue;
|
||||
}
|
||||
2 => {
|
||||
self.vel_x = self.direction.vector_x() * 0x100;
|
||||
self.vel_y += (self.target_y - self.y).signum() * 0x10;
|
||||
self.vel_y = self.vel_y.clamp(-0x300, 0x300);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
self.animate(1, 0, 2);
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 4 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n242_bat_last_cave[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n243_bat_generator(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.action_counter = self.rng.range(0..500) as u16;
|
||||
}
|
||||
|
||||
if self.action_counter > 0 {
|
||||
self.action_counter -= 1;
|
||||
} else {
|
||||
self.action_num = 0;
|
||||
|
||||
let mut npc = NPC::create(242, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x;
|
||||
npc.y = self.y + self.rng.range(-32..32) * 0x200;
|
||||
npc.direction = self.direction;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
}
|
||||
_ => return Ok(()),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n244_lava_drop(&mut self, state: &mut SharedGameState, players: [&mut Player; 2]) -> GameResult {
|
||||
self.vel_y += 0x40;
|
||||
|
||||
// idfk why was that there in original code but I'll leave it there in case
|
||||
let mut hit = self.flags.hit_anything();
|
||||
|
||||
if self.action_counter > 10 && self.flags.in_water() {
|
||||
hit = true;
|
||||
}
|
||||
|
||||
if hit {
|
||||
for _ in 0..3 {
|
||||
state.create_caret(self.x, self.y, CaretType::Bubble, Direction::Right);
|
||||
}
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
if self.x > player.x - 0x20000
|
||||
&& self.x < player.x + 0x20000
|
||||
&& self.y > player.y - 0x14000
|
||||
&& self.y < player.y + 0x14000
|
||||
{
|
||||
state.sound_manager.play_sfx(21);
|
||||
}
|
||||
|
||||
self.cond.set_alive(false);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.vel_y > 0x5ff {
|
||||
self.vel_y = 0x5ff;
|
||||
}
|
||||
|
||||
self.y += self.vel_y;
|
||||
self.anim_rect = state.constants.npc.n244_lava_drop;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n245_lava_drop_generator(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
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.action_counter = self.event_num;
|
||||
}
|
||||
|
||||
self.anim_num = 0;
|
||||
if self.anim_counter > 0 {
|
||||
self.anim_counter -= 1;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.action_num = 10;
|
||||
self.anim_counter = 0;
|
||||
}
|
||||
10 => {
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 10 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
|
||||
if self.anim_num > 3 {
|
||||
self.anim_num = 0;
|
||||
self.action_num = 1;
|
||||
self.action_counter = self.flag_num;
|
||||
|
||||
let mut npc = NPC::create(244, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x;
|
||||
npc.y = self.y;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.x = if (self.anim_counter & 2) != 0 { self.target_x } else { self.target_x + 0x200 };
|
||||
self.anim_rect = state.constants.npc.n245_lava_drop_generator[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n276_red_demon(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 | 2 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.y -= 0x1000;
|
||||
}
|
||||
|
||||
if self.action_num == 1 {
|
||||
self.vel_x = 0;
|
||||
self.action_num = 2;
|
||||
self.anim_num = 0;
|
||||
}
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
|
||||
|
||||
self.animate(20, 0, 1);
|
||||
|
||||
if self.shock > 0 {
|
||||
self.action_num = 10;
|
||||
}
|
||||
}
|
||||
10 | 11 => {
|
||||
if self.action_num == 10 {
|
||||
self.action_num = 11;
|
||||
self.anim_num = 2;
|
||||
self.action_counter = 0;
|
||||
self.npc_flags.set_shootable(true);
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
match self.action_counter {
|
||||
30 | 40 | 50 => {
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
let mut npc = NPC::create(277, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x;
|
||||
npc.y = self.y;
|
||||
|
||||
let angle = f64::atan2((player.y - 0x1400 - self.y) as f64, (player.x - self.x) as f64);
|
||||
npc.vel_x = (2048.0 * angle.cos()) as i32;
|
||||
npc.vel_y = (2048.0 * angle.sin()) as i32;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
|
||||
state.sound_manager.play_sfx(39);
|
||||
}
|
||||
34 | 44 | 54 => {
|
||||
self.anim_num = 3;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if self.action_counter > 60 {
|
||||
self.action_num = 20;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 2;
|
||||
}
|
||||
}
|
||||
20 => {
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 20 {
|
||||
self.action_num = 21;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 5;
|
||||
|
||||
self.vel_y = -0x5ff;
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.vel_x = if self.x < player.x { 0x100 } else { -0x100 };
|
||||
}
|
||||
}
|
||||
21 => {
|
||||
self.action_counter += 1;
|
||||
match self.action_counter {
|
||||
30 | 40 | 50 => {
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
let mut npc = NPC::create(277, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
||||
let angle = f64::atan2((player.y - 0x1400 - self.y) as f64, (player.x - self.x) as f64);
|
||||
npc.vel_x = (2048.0 * angle.cos()) as i32;
|
||||
npc.vel_y = (2048.0 * angle.sin()) as i32;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
|
||||
state.sound_manager.play_sfx(39);
|
||||
}
|
||||
34 | 44 => {
|
||||
self.anim_num = 5;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if self.action_counter > 53 {
|
||||
self.anim_num = 7;
|
||||
}
|
||||
|
||||
if self.flags.hit_bottom_wall() {
|
||||
self.action_num = 22;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 2;
|
||||
state.quake_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
}
|
||||
22 => {
|
||||
self.vel_x /= 2;
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 22 {
|
||||
self.action_num = 10;
|
||||
}
|
||||
}
|
||||
50 => {
|
||||
self.npc_flags.set_shootable(false);
|
||||
self.damage = 0;
|
||||
|
||||
if self.flags.hit_bottom_wall() {
|
||||
self.action_num = 51;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 2;
|
||||
state.quake_counter = 10;
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
51 => {
|
||||
self.vel_x = 7 * self.vel_x / 8;
|
||||
self.anim_num = 8;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.vel_y += 0x20;
|
||||
if self.vel_y > 0x5FF {
|
||||
self.vel_y = 0x5FF;
|
||||
}
|
||||
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
if self.action_num < 50 {
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if self.x < player.x { Direction::Right } else { Direction::Left };
|
||||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 9 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n276_red_demon[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n277_red_demon_projectile(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
}
|
||||
|
||||
if self.action_num == 1 {
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
if self.flags.any_flag() {
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x;
|
||||
npc.y = self.y;
|
||||
|
||||
for _ in 0..3 {
|
||||
let _ = npc_list.spawn(0x100, npc.clone());
|
||||
}
|
||||
|
||||
self.vanish(state);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter % 5 == 0 {
|
||||
state.sound_manager.play_sfx(110);
|
||||
}
|
||||
|
||||
self.anim_num += 1;
|
||||
if self.anim_num > 2 {
|
||||
self.anim_num = 0;
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n277_red_demon_projectile[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1596,4 +1596,33 @@ impl NPC {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n304_gaudi_hospital(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.y += 0x1400;
|
||||
}
|
||||
|
||||
self.anim_num = 0;
|
||||
}
|
||||
10 => {
|
||||
self.anim_num = 1;
|
||||
}
|
||||
20 | 21 => {
|
||||
if self.action_num == 20 {
|
||||
self.action_num = 21;
|
||||
self.anim_num = 2;
|
||||
}
|
||||
|
||||
self.animate(10, 2, 3);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n304_gaudi_hospital[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
use num_traits::{abs, clamp};
|
||||
use std::hint::unreachable_unchecked;
|
||||
|
||||
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;
|
||||
|
|
@ -610,7 +610,7 @@ impl NPC {
|
|||
self.cond.set_alive(false);
|
||||
}
|
||||
|
||||
if self.y > stage.map.height as i32 * 0x2000 {
|
||||
if self.y > stage.map.height as i32 * state.tile_size.as_int() * 0x200 {
|
||||
// out of map
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
|
|
@ -651,7 +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 (player.x - self.x).abs() < 0x1000 && player.y < self.y + 0x1000 && player.y > self.y - 0x2000 {
|
||||
state.sound_manager.play_sfx(43);
|
||||
self.action_num = 1;
|
||||
}
|
||||
|
|
@ -666,6 +666,7 @@ impl NPC {
|
|||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 3 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n085_terminal[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
|
|
@ -706,8 +707,8 @@ impl NPC {
|
|||
|
||||
{
|
||||
let i = self.get_closest_player_idx_mut(&players);
|
||||
if abs(players[i].x - self.x) < 480 * 0x200
|
||||
&& abs(players[i].y - self.y) < 240 * 0x200
|
||||
if (players[i].x - self.x).abs() < 480 * 0x200
|
||||
&& (players[i].y - self.y).abs() < 240 * 0x200
|
||||
&& self.rng.range(0..5) == 1
|
||||
{
|
||||
let mut particle = NPC::create(199, &state.npc_table);
|
||||
|
|
@ -724,7 +725,7 @@ impl NPC {
|
|||
continue;
|
||||
}
|
||||
|
||||
if abs(player.y - self.y) < 0x1000 && player.x < self.x && player.x > self.x - 96 * 0x200 {
|
||||
if (player.y - self.y).abs() < 0x1000 && player.x < self.x && player.x > self.x - 96 * 0x200 {
|
||||
player.vel_x -= 0x88;
|
||||
player.cond.set_increase_acceleration(true);
|
||||
}
|
||||
|
|
@ -766,8 +767,8 @@ impl NPC {
|
|||
|
||||
{
|
||||
let i = self.get_closest_player_idx_mut(&players);
|
||||
if abs(players[i].x - self.x) < 480 * 0x200
|
||||
&& abs(players[i].y - self.y) < 240 * 0x200
|
||||
if (players[i].x - self.x).abs() < 480 * 0x200
|
||||
&& (players[i].y - self.y).abs() < 240 * 0x200
|
||||
&& self.rng.range(0..5) == 1
|
||||
{
|
||||
let mut particle = NPC::create(199, &state.npc_table);
|
||||
|
|
@ -784,7 +785,7 @@ impl NPC {
|
|||
continue;
|
||||
}
|
||||
|
||||
if abs(player.x - self.x) < 0x1000 && player.y < self.y && player.y > self.y - 96 * 0x200 {
|
||||
if (player.x - self.x).abs() < 0x1000 && player.y < self.y && player.y > self.y - 96 * 0x200 {
|
||||
player.vel_y -= 0x88;
|
||||
}
|
||||
}
|
||||
|
|
@ -825,8 +826,8 @@ impl NPC {
|
|||
|
||||
{
|
||||
let i = self.get_closest_player_idx_mut(&players);
|
||||
if abs(players[i].x - self.x) < 480 * 0x200
|
||||
&& abs(players[i].y - self.y) < 240 * 0x200
|
||||
if (players[i].x - self.x).abs() < 480 * 0x200
|
||||
&& (players[i].y - self.y).abs() < 240 * 0x200
|
||||
&& self.rng.range(0..5) == 1
|
||||
{
|
||||
let mut particle = NPC::create(199, &state.npc_table);
|
||||
|
|
@ -839,7 +840,7 @@ impl NPC {
|
|||
}
|
||||
|
||||
for player in players.iter_mut() {
|
||||
if abs(player.y - self.y) < 0x1000 && player.x > self.x && player.x < self.x + 96 * 0x200 {
|
||||
if (player.y - self.y).abs() < 0x1000 && player.x > self.x && player.x < self.x + 96 * 0x200 {
|
||||
player.vel_x += 0x88;
|
||||
player.cond.set_increase_acceleration(true);
|
||||
}
|
||||
|
|
@ -881,8 +882,8 @@ impl NPC {
|
|||
|
||||
{
|
||||
let i = self.get_closest_player_idx_mut(&players);
|
||||
if abs(players[i].x - self.x) < 480 * 0x200
|
||||
&& abs(players[i].y - self.y) < 240 * 0x200
|
||||
if (players[i].x - self.x).abs() < 480 * 0x200
|
||||
&& (players[i].y - self.y).abs() < 240 * 0x200
|
||||
&& self.rng.range(0..5) == 1
|
||||
{
|
||||
let mut particle = NPC::create(199, &state.npc_table);
|
||||
|
|
@ -895,7 +896,7 @@ impl NPC {
|
|||
}
|
||||
|
||||
for player in players.iter_mut() {
|
||||
if abs(player.x - self.x) < 0x1000 && player.y > self.y && player.y < self.y + 96 * 0x200 {
|
||||
if (player.x - self.x).abs() < 0x1000 && player.y > self.y && player.y < self.y + 96 * 0x200 {
|
||||
player.vel_y += 0x88;
|
||||
}
|
||||
}
|
||||
|
|
@ -959,14 +960,7 @@ impl NPC {
|
|||
}
|
||||
}
|
||||
10 => {
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 2 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
if self.anim_num > 2 {
|
||||
self.anim_num = 2;
|
||||
}
|
||||
}
|
||||
self.animate(2, 2, 2);
|
||||
|
||||
for player in players.iter() {
|
||||
if player.y > self.y {
|
||||
|
|
@ -1246,7 +1240,7 @@ impl NPC {
|
|||
_ => (),
|
||||
}
|
||||
|
||||
self.vel_x = clamp(self.vel_x, -0x200, 0x200);
|
||||
self.vel_x = self.vel_x.clamp(-0x200, 0x200);
|
||||
self.x += self.vel_x;
|
||||
|
||||
if self.anim_num != 149 {
|
||||
|
|
@ -1390,7 +1384,7 @@ impl NPC {
|
|||
_ => (),
|
||||
}
|
||||
|
||||
self.vel_y = clamp(self.vel_y, -0x200, 0x200);
|
||||
self.vel_y = self.vel_y.clamp(-0x200, 0x200);
|
||||
self.y += self.vel_y;
|
||||
|
||||
if self.anim_num != 149 {
|
||||
|
|
@ -1798,6 +1792,91 @@ impl NPC {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n246_press_proximity(
|
||||
&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;
|
||||
}
|
||||
|
||||
for player in players.iter() {
|
||||
if player.x < self.x + 0x1000
|
||||
&& player.x > self.x - 0x1000
|
||||
&& player.y > self.y + 0x1000
|
||||
&& player.y < self.y + 0x10000
|
||||
{
|
||||
self.action_num = 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
5 => {
|
||||
if self.flags.hit_bottom_wall() {
|
||||
self.action_num = 10;
|
||||
self.anim_counter = 0;
|
||||
self.anim_num = 1;
|
||||
}
|
||||
}
|
||||
10 => {
|
||||
self.animate(2, 2, 2);
|
||||
|
||||
for player in players.iter() {
|
||||
if player.y > self.y {
|
||||
self.npc_flags.set_solid_hard(false);
|
||||
self.damage = 127;
|
||||
} else {
|
||||
self.npc_flags.set_solid_hard(true);
|
||||
self.damage = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if self.flags.hit_bottom_wall() {
|
||||
if self.anim_num > 1 {
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x;
|
||||
npc.y = self.y;
|
||||
|
||||
for _ in 0..4 {
|
||||
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
|
||||
npc.vel_y = self.rng.range(-0x600..0) as i32;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc.clone());
|
||||
}
|
||||
|
||||
state.quake_counter = 10;
|
||||
state.sound_manager.play_sfx(26);
|
||||
}
|
||||
|
||||
self.action_num = 1;
|
||||
self.anim_num = 0;
|
||||
self.damage = 0;
|
||||
self.npc_flags.set_solid_hard(true);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if self.action_num >= 5 {
|
||||
self.vel_y += 0x80;
|
||||
|
||||
if self.vel_y > 0x5ff {
|
||||
self.vel_y = 0x5ff;
|
||||
}
|
||||
|
||||
self.y += self.vel_y;
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n246_press_proximity[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n253_experience_capsule(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
|
|
@ -1832,12 +1911,368 @@ impl NPC {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n271_ironhead_block(&mut self, state: &mut SharedGameState, stage: &mut Stage) -> GameResult {
|
||||
if self.vel_x < 0 && self.x < -0x2000
|
||||
|| self.vel_x > 0 && self.x > stage.map.width as i32 * state.tile_size.as_int() * 0x200 + 0x2000
|
||||
{
|
||||
self.cond.set_alive(false);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
if self.rng.range(0..9) == 9 {
|
||||
self.anim_rect = Rect::new(0, 64, 32, 96);
|
||||
self.display_bounds = Rect::new(0x2000, 0x2000, 0x2000, 0x2000);
|
||||
self.hit_bounds = Rect::new(0x1800, 0x1800, 0x1800, 0x1800);
|
||||
} else {
|
||||
self.anim_rect =
|
||||
Rect::new_size(16 * (self.rng.range(0..3) as u16), 16 * (self.rng.range(0..3) as u16 / 3), 16, 16);
|
||||
}
|
||||
|
||||
self.vel_x = self.direction.vector_x() * 2 * self.rng.range(256..512);
|
||||
self.vel_y = self.rng.range(-512..512);
|
||||
}
|
||||
|
||||
if self.vel_y < 0 && self.y - (self.hit_bounds.top as i32) < 0x1000 {
|
||||
self.vel_y = -self.vel_y;
|
||||
state.create_caret(self.x, self.y - 0x1000, CaretType::LittleParticles, Direction::Left);
|
||||
state.create_caret(self.x, self.y - 0x1000, CaretType::LittleParticles, Direction::Left);
|
||||
}
|
||||
|
||||
if self.vel_y > 0 && (self.hit_bounds.bottom as i32) + self.y > 0x1D000 {
|
||||
self.vel_y = -self.vel_y;
|
||||
state.create_caret(self.x, self.y + 0x1000, CaretType::LittleParticles, Direction::Left);
|
||||
state.create_caret(self.x, self.y + 0x1000, CaretType::LittleParticles, Direction::Left);
|
||||
}
|
||||
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n272_ironhead_block_generator(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.action_counter = self.rng.range(0..200) as u16;
|
||||
}
|
||||
|
||||
if self.action_num == 1 {
|
||||
if self.action_counter > 0 {
|
||||
self.action_counter -= 1;
|
||||
} else {
|
||||
self.action_num = 0;
|
||||
|
||||
let mut npc = NPC::create(271, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
||||
npc.x = self.x;
|
||||
npc.y = self.y + self.rng.range(-32..32) * 0x200;
|
||||
npc.direction = self.direction;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n279_large_falling_block(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
stage: &mut Stage,
|
||||
) -> GameResult {
|
||||
match self.action_num {
|
||||
0 => {
|
||||
if self.action_num == 0 {
|
||||
match self.direction {
|
||||
Direction::Left => {
|
||||
self.action_num = 100;
|
||||
self.npc_flags.set_invulnerable(true);
|
||||
self.anim_num = 0;
|
||||
}
|
||||
Direction::Up => {
|
||||
self.anim_num = 0;
|
||||
self.action_num = 10;
|
||||
}
|
||||
Direction::Right => {
|
||||
self.action_num = 100;
|
||||
self.npc_flags.set_invulnerable(true);
|
||||
self.anim_num = 1;
|
||||
self.display_bounds = Rect::new(0x1000, 0x1000, 0x1000, 0x1000);
|
||||
self.hit_bounds = Rect::new(0x1000, 0x1000, 0x1000, 0x1000);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if self.direction == Direction::Up {
|
||||
self.action_num = 11;
|
||||
self.action_counter = 16;
|
||||
|
||||
if self.action_counter > 0 {
|
||||
self.action_counter = self.action_counter.saturating_sub(2);
|
||||
} else {
|
||||
self.action_num = 100;
|
||||
self.npc_flags.set_invulnerable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
10 | 11 => {
|
||||
if self.action_num == 10 {
|
||||
self.action_num = 11;
|
||||
self.action_counter = 16;
|
||||
}
|
||||
|
||||
if self.action_counter > 0 {
|
||||
self.action_counter = self.action_counter.saturating_sub(2);
|
||||
} else {
|
||||
self.action_num = 100;
|
||||
self.npc_flags.set_invulnerable(true);
|
||||
}
|
||||
}
|
||||
100 => {
|
||||
self.vel_y += 0x40;
|
||||
if self.vel_y > 0x700 {
|
||||
self.vel_y = 0x700;
|
||||
}
|
||||
|
||||
if self.y > 0x10000 {
|
||||
self.npc_flags.set_ignore_solidity(false);
|
||||
}
|
||||
|
||||
if self.flags.hit_bottom_wall() {
|
||||
self.vel_y = -0x200;
|
||||
self.action_num = 110;
|
||||
self.npc_flags.set_ignore_solidity(true);
|
||||
state.sound_manager.play_sfx(26);
|
||||
state.quake_counter = 10;
|
||||
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
||||
for _ in 0..4 {
|
||||
npc.x = self.x + self.rng.range(-12..12) * 0x200;
|
||||
npc.y = self.y + 0x2000;
|
||||
npc.vel_x = self.rng.range(-0x155..0x155);
|
||||
npc.vel_y = self.rng.range(-0x600..0);
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
110 => {
|
||||
self.vel_y += 0x40;
|
||||
if self.y > stage.map.height as i32 * state.tile_size.as_int() * 0x200 + 0x4000 {
|
||||
self.cond.set_alive(false);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.damage = if player.y > self.y { 10 } else { 0 };
|
||||
|
||||
self.y += self.vel_y;
|
||||
|
||||
self.anim_rect = state.constants.npc.n279_large_falling_block[self.anim_num as usize];
|
||||
|
||||
if self.action_num == 11 {
|
||||
self.anim_rect.top += self.action_counter;
|
||||
self.anim_rect.bottom += self.action_counter;
|
||||
self.display_bounds.top = (16u32).saturating_sub(self.action_counter as u32) * 0x200;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n292_quake(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
state.quake_counter = 10;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n294_quake_falling_block_generator(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
stage: &mut Stage,
|
||||
) -> GameResult {
|
||||
match self.action_num {
|
||||
0 => {
|
||||
if self.x < (stage.map.width as i32 - 6) * state.tile_size.as_int() * 0x200 {
|
||||
self.action_num = 1;
|
||||
self.action_counter = 0;
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
self.action_counter += 1;
|
||||
let player = &players[0];
|
||||
if player.equip.has_booster_2_0() {
|
||||
self.x = player.x + 0x8000;
|
||||
if self.x < 0x34000 {
|
||||
self.x = 0x34000;
|
||||
}
|
||||
} else {
|
||||
self.x = player.x + 0xC000;
|
||||
if self.x < 0x2E000 {
|
||||
self.x = 0x2E000;
|
||||
}
|
||||
}
|
||||
|
||||
let map_start = (stage.map.width as i32 - 10) * state.tile_size.as_int() * 0x200;
|
||||
if self.x > map_start {
|
||||
self.x = map_start;
|
||||
}
|
||||
|
||||
if self.action_counter > 24 {
|
||||
let mut npc = NPC::create(279, &state.npc_table);
|
||||
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x
|
||||
+ if player.equip.has_booster_2_0() {
|
||||
self.rng.range(-14..14)
|
||||
} else {
|
||||
self.rng.range(-11..11)
|
||||
} * state.tile_size.as_int()
|
||||
* 0x200;
|
||||
npc.y = player.y - 0x1C000;
|
||||
npc.direction = if self.rng.range(0..10) & 1 != 0 { Direction::Left } else { Direction::Right };
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
|
||||
self.action_counter = self.rng.range(0..15) as u16;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n295_cloud(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.anim_num = self.tsc_direction % 4;
|
||||
|
||||
match self.tsc_direction {
|
||||
0 => {
|
||||
self.vel_y = -0x1000;
|
||||
self.display_bounds.right = 0xD000;
|
||||
self.display_bounds.left = 0xD000;
|
||||
}
|
||||
1 => {
|
||||
self.vel_y = -0x800;
|
||||
self.display_bounds.right = 0x7000;
|
||||
self.display_bounds.left = 0x7000;
|
||||
}
|
||||
2 => {
|
||||
self.vel_y = -0x400;
|
||||
self.display_bounds.right = 0x4000;
|
||||
self.display_bounds.left = 0x4000;
|
||||
}
|
||||
3 => {
|
||||
self.vel_y = -0x200;
|
||||
self.display_bounds.right = 0x2800;
|
||||
self.display_bounds.left = 0x2800;
|
||||
}
|
||||
4 => {
|
||||
self.vel_x = -0x400;
|
||||
self.display_bounds.right = 0xD000;
|
||||
self.display_bounds.left = 0xD000;
|
||||
}
|
||||
5 => {
|
||||
self.vel_x = -0x200;
|
||||
self.display_bounds.right = 0x7000;
|
||||
self.display_bounds.left = 0x7000;
|
||||
}
|
||||
6 => {
|
||||
self.vel_x = -0x100;
|
||||
self.display_bounds.right = 0x4000;
|
||||
self.display_bounds.left = 0x4000;
|
||||
}
|
||||
7 => {
|
||||
self.vel_x = -0x80;
|
||||
self.display_bounds.right = 0x2800;
|
||||
self.display_bounds.left = 0x2800;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if self.action_num == 1 {
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
if self.x < -0x8000 {
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
|
||||
if self.y < -0x4000 {
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n295_cloud[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n296_cloud_generator(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
|
||||
self.action_counter += 1;
|
||||
if self.action_counter <= 16 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.action_counter = self.rng.range(0..16) as u16;
|
||||
let dir = self.rng.range(0..100) as u16 & 3;
|
||||
let mut npc = NPC::create(295, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
||||
if self.direction != Direction::Left {
|
||||
let layer = match dir {
|
||||
0 => 128,
|
||||
1 => 85,
|
||||
2 => 64,
|
||||
3 => 0,
|
||||
_ => unsafe {
|
||||
unreachable_unchecked();
|
||||
},
|
||||
};
|
||||
|
||||
npc.x = self.x;
|
||||
npc.y = self.y + self.rng.range(-7..7) * state.tile_size.as_int() * 0x200;
|
||||
npc.tsc_direction = dir + 4;
|
||||
|
||||
let _ = npc_list.spawn(layer, npc);
|
||||
} else {
|
||||
let layer = match dir {
|
||||
0 => 384,
|
||||
1 => 128,
|
||||
2 => 64,
|
||||
3 => 0,
|
||||
_ => unsafe { unreachable_unchecked() },
|
||||
};
|
||||
|
||||
npc.x = self.x + self.rng.range(-10..10) * state.tile_size.as_int() * 0x200;
|
||||
npc.y = self.y;
|
||||
npc.tsc_direction = dir;
|
||||
|
||||
let _ = npc_list.spawn(layer, npc);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n297_sue_dragon_mouth(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
|
||||
if let Some(npc) = self.get_parent_ref_mut(npc_list) {
|
||||
self.x = npc.x + 0x2000;
|
||||
|
|
@ -2102,7 +2537,7 @@ impl NPC {
|
|||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
let player = self.get_closest_player_mut(players);
|
||||
if abs(player.x - self.x) < 320 * 0x200
|
||||
if (player.x - self.x).abs() < 320 * 0x200
|
||||
&& player.y < self.y + 320 * 0x200
|
||||
&& player.y > self.y - 160 * 0x200
|
||||
&& self.rng.range(0..100) == 2
|
||||
|
|
@ -2117,4 +2552,16 @@ impl NPC {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n360_credits_thank_you(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.x -= 0x1000;
|
||||
self.y -= 0x1000;
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n360_credits_thank_you;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,14 @@ use crate::caret::CaretType;
|
|||
use crate::common::{Direction, CDEG_RAD};
|
||||
use crate::components::flash::Flash;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::boss::BossNPC;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::NPC;
|
||||
use crate::physics::PhysicalEntity;
|
||||
use crate::player::Player;
|
||||
use crate::rng::RNG;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::stage::Stage;
|
||||
|
||||
impl NPC {
|
||||
pub(crate) fn tick_n066_misery_bubble(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
|
||||
|
|
@ -573,7 +576,7 @@ impl NPC {
|
|||
npc.y = self.target_y;
|
||||
let _ = npc_list.spawn(0x100, npc.clone());
|
||||
|
||||
npc.x = self.x - 0x2000;
|
||||
npc.x = self.target_x - 0x2000;
|
||||
npc.direction = Direction::Right;
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
|
|
@ -716,7 +719,7 @@ impl NPC {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n249_misery_boss_energy_shot(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
pub(crate) fn tick_n249_misery_boss_appearing(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
self.action_counter2 += 1;
|
||||
if self.action_counter2 > 8 {
|
||||
self.cond.set_alive(false);
|
||||
|
|
@ -724,12 +727,616 @@ impl NPC {
|
|||
|
||||
if self.direction == Direction::Left {
|
||||
self.x -= 0x400;
|
||||
self.anim_rect = state.constants.npc.n249_misery_boss_energy_shot[0];
|
||||
self.anim_rect = state.constants.npc.n249_misery_boss_appearing[0];
|
||||
} else {
|
||||
self.x += 0x400;
|
||||
self.anim_rect = state.constants.npc.n249_misery_boss_energy_shot[1];
|
||||
self.anim_rect = state.constants.npc.n249_misery_boss_appearing[1];
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n250_misery_boss_lighting_ball(
|
||||
&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_y = self.y;
|
||||
self.vel_x = 0;
|
||||
self.vel_y = -0x200;
|
||||
}
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
self.vel_x += if self.x < player.x { 0x10 } else { -0x10 };
|
||||
self.vel_y += if self.y < self.target_y { 0x20 } else { -0x20 };
|
||||
|
||||
self.vel_x = self.vel_x.clamp(-0x200, 0x200);
|
||||
self.vel_y = self.vel_y.clamp(-0x200, 0x200);
|
||||
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
self.animate(2, 0, 1);
|
||||
|
||||
if player.x > self.x - 0x1000 && player.x < self.x + 0x1000 && player.y > self.y {
|
||||
self.action_num = 10;
|
||||
}
|
||||
}
|
||||
10 | 11 => {
|
||||
if self.action_num == 10 {
|
||||
self.action_num = 11;
|
||||
self.action_counter = 0;
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 10 {
|
||||
let mut npc = NPC::create(251, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
||||
npc.x = self.x;
|
||||
npc.y = self.y;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
|
||||
state.sound_manager.play_sfx(101);
|
||||
self.cond.set_alive(false);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.anim_num = if (self.action_counter & 2) != 0 { 2 } else { 1 };
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n250_misery_boss_lighting_ball[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n251_misery_boss_lighting(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
}
|
||||
|
||||
if self.action_num == 1 {
|
||||
self.anim_num = (self.anim_num + 1) & 1;
|
||||
self.y += 0x1000;
|
||||
|
||||
if self.flags.any_flag() {
|
||||
npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right as usize, 3, state, &self.rng);
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n251_misery_boss_lighting[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n252_misery_boss_bats(
|
||||
&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.action_counter = 0;
|
||||
self.action_counter2 = self.tsc_direction;
|
||||
}
|
||||
|
||||
self.action_counter2 += 2;
|
||||
self.action_counter2 &= 0xff;
|
||||
|
||||
if self.action_counter < 192 {
|
||||
self.action_counter += 1;
|
||||
}
|
||||
|
||||
if let Some(parent) = self.get_parent_ref_mut(npc_list) {
|
||||
self.x = parent.x
|
||||
+ self.action_counter as i32 * ((self.action_counter2 as f64 * CDEG_RAD).cos() * -512.0) as i32
|
||||
/ 4;
|
||||
self.y = parent.y
|
||||
+ self.action_counter as i32 * ((self.action_counter2 as f64 * CDEG_RAD).sin() * -512.0) as i32
|
||||
/ 4;
|
||||
|
||||
if parent.action_num == 151 {
|
||||
self.action_num = 10;
|
||||
self.anim_num = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
10 | 11 => {
|
||||
if self.action_num == 10 {
|
||||
self.action_num = 11;
|
||||
self.npc_flags.set_shootable(true);
|
||||
self.npc_flags.set_invulnerable(false);
|
||||
self.npc_flags.set_ignore_solidity(false);
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
let deg = f64::atan2((self.y - player.y) as f64, (self.x - player.x) as f64);
|
||||
let deg = deg + self.rng.range(-3..3) as f64 * CDEG_RAD;
|
||||
|
||||
self.vel_x = (deg.cos() * -512.0) as i32;
|
||||
self.vel_y = (deg.sin() * -512.0) as i32;
|
||||
|
||||
self.anim_num = 1;
|
||||
self.anim_counter = 0;
|
||||
|
||||
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
if self.flags.hit_anything() {
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x;
|
||||
npc.y = self.y;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc.clone());
|
||||
let _ = npc_list.spawn(0x100, npc.clone());
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
|
||||
self.animate(4, 1, 3);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 4 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n252_misery_boss_bats[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n283_misery_possessed(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
stage: &mut Stage,
|
||||
boss: &mut BossNPC,
|
||||
) -> GameResult {
|
||||
if self.action_num < 100 && (!boss.parts[0].cond.alive() || self.life < 400) {
|
||||
self.action_num = 100;
|
||||
}
|
||||
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 9 {
|
||||
self.action_num = 1;
|
||||
self.y -= 0x1000;
|
||||
state.sound_manager.play_sfx(29);
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
self.anim_num = if self.action_counter & 2 != 0 { 9 } else { 0 };
|
||||
}
|
||||
10 => {
|
||||
self.action_num = 11;
|
||||
self.anim_num = 9;
|
||||
}
|
||||
20 | 21 => {
|
||||
if self.action_num == 21 {
|
||||
self.action_num = 21;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 0;
|
||||
self.anim_counter = 0;
|
||||
|
||||
state.npc_super_pos.0 = 0;
|
||||
}
|
||||
|
||||
self.vel_x = 7 * self.vel_x / 8;
|
||||
self.vel_y = 7 * self.vel_y / 8;
|
||||
|
||||
self.animate(20, 0, 1);
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 100 {
|
||||
self.action_num = 30;
|
||||
}
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
self.direction = if player.x > self.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
30 | 31 => {
|
||||
if self.action_num == 31 {
|
||||
self.action_num = 31;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 2;
|
||||
self.action_counter3 = self.life;
|
||||
}
|
||||
|
||||
self.animate(1, 2, 3);
|
||||
|
||||
if self.flags.hit_bottom_wall() {
|
||||
self.vel_y = -0x200;
|
||||
}
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
self.vel_x += if self.x > boss.parts[0].x { -0x20 } else { 0x20 };
|
||||
self.vel_y += if self.y > player.y { -0x10 } else { 0x10 };
|
||||
|
||||
self.vel_x = self.vel_x.clamp(-0x200, 0x200);
|
||||
self.vel_y = self.vel_y.clamp(-0x200, 0x200);
|
||||
|
||||
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 150 && (self.life + 20 < self.action_counter3 || state.npc_super_pos.0 != 0) {
|
||||
state.npc_super_pos.0 = 0;
|
||||
self.action_num = 40;
|
||||
}
|
||||
|
||||
if boss.parts[0].anim_num != 0 && self.action_counter > 250 {
|
||||
self.action_num = 50;
|
||||
}
|
||||
}
|
||||
40 | 41 => {
|
||||
if self.action_num == 40 {
|
||||
self.action_num = 41;
|
||||
self.action_counter = 0;
|
||||
self.vel_x = 0;
|
||||
self.vel_y = 0;
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
|
||||
self.action_counter3 = if player.y >= 0x14000 { 289 } else { 290 };
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
self.anim_num = if self.action_counter & 2 != 0 { 4 } else { 5 };
|
||||
|
||||
if self.action_counter % 6 == 1 {
|
||||
state.sound_manager.play_sfx(39);
|
||||
|
||||
let mut npc = NPC::create(self.action_counter3, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
||||
if self.action_counter3 == 289 {
|
||||
npc.x = self.x + self.rng.range(-64..64) * 0x200;
|
||||
npc.y = self.y + self.rng.range(-32..32) * 0x200;
|
||||
} else {
|
||||
npc.x = self.x + self.rng.range(-32..32) * 0x200;
|
||||
npc.y = self.y + self.rng.range(-64..64) * 0x200;
|
||||
}
|
||||
|
||||
npc.x = npc.x.clamp(
|
||||
2 * state.tile_size.as_int() * 0x200,
|
||||
(stage.map.width as i32 - 2) * state.tile_size.as_int() * 0x200,
|
||||
);
|
||||
npc.y = npc.y.clamp(
|
||||
2 * state.tile_size.as_int() * 0x200,
|
||||
(stage.map.height as i32 - 2) * state.tile_size.as_int() * 0x200,
|
||||
);
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
|
||||
if self.action_counter > 50 {
|
||||
self.action_num = 42;
|
||||
self.action_counter = 0;
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
}
|
||||
42 => {
|
||||
self.action_counter += 1;
|
||||
self.anim_num = 6;
|
||||
if self.action_counter > 50 {
|
||||
self.action_num = 30;
|
||||
self.vel_y = -0x200;
|
||||
self.vel_x = self.direction.vector_x() * 0x200;
|
||||
}
|
||||
}
|
||||
50 | 51 => {
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
if self.action_num == 50 {
|
||||
self.action_num = 51;
|
||||
self.action_counter = 0;
|
||||
self.vel_x = 0;
|
||||
self.vel_y = 0;
|
||||
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
|
||||
|
||||
state.sound_manager.play_sfx(103);
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
self.anim_num = if self.action_counter & 2 != 0 { 4 } else { 5 };
|
||||
|
||||
let period = if player.equip.has_booster_2_0() { 10 } else { 24 };
|
||||
if self.action_counter % period == 1 {
|
||||
state.sound_manager.play_sfx(39);
|
||||
|
||||
let mut npc = NPC::create(301, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x + 0x1400 * self.direction.vector_x();
|
||||
npc.y = self.y;
|
||||
npc.tsc_direction = match ((self.action_counter / 6) & 3, self.direction) {
|
||||
(0, Direction::Left) => 0x58,
|
||||
(1, Direction::Left) => 0x6C,
|
||||
(2, Direction::Left) => 0x94,
|
||||
(3, Direction::Left) => 0xA8,
|
||||
(0, _) => 0xD8,
|
||||
(1, _) => 0xEC,
|
||||
(2, _) => 0x14,
|
||||
(3, _) => 0x28,
|
||||
_ => unsafe {
|
||||
unreachable_unchecked();
|
||||
},
|
||||
};
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
|
||||
if self.action_counter > 50 {
|
||||
self.action_num = 42;
|
||||
self.action_counter = 0;
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
}
|
||||
99 => {
|
||||
self.vel_x = 0;
|
||||
self.vel_y = 0;
|
||||
self.anim_num = 9;
|
||||
self.npc_flags.set_shootable(false);
|
||||
}
|
||||
100 | 101 => {
|
||||
if self.action_num == 100 {
|
||||
self.action_num = 101;
|
||||
self.anim_num = 9;
|
||||
self.damage = 0;
|
||||
self.npc_flags.set_shootable(false);
|
||||
self.npc_flags.set_ignore_solidity(true);
|
||||
self.vel_y = -0x200;
|
||||
self.shock += 50;
|
||||
self.hit_bounds.bottom = 0x1800;
|
||||
boss.parts[0].anim_num += 1;
|
||||
}
|
||||
|
||||
self.vel_y += 0x20;
|
||||
if self.y > 0x1B000 - self.hit_bounds.bottom as i32 {
|
||||
self.y = 0x1B000 - self.hit_bounds.bottom as i32;
|
||||
self.action_num = 102;
|
||||
self.anim_num = 10;
|
||||
self.vel_x = 0;
|
||||
self.vel_y = 0;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.x += if self.shock > 0 { self.vel_x / 2 } else { self.vel_x };
|
||||
self.y += self.vel_y;
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 11 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n283_misery_possessed[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n289_critter_orange(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
stage: &mut Stage,
|
||||
) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.anim_num = 2;
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if player.x > self.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 16 {
|
||||
self.action_num = 10;
|
||||
self.display_bounds.top = 0x1000;
|
||||
self.display_bounds.bottom = 0x1000;
|
||||
self.damage = 2;
|
||||
self.npc_flags.set_shootable(true);
|
||||
}
|
||||
}
|
||||
10 => {
|
||||
if self.flags.hit_bottom_wall() {
|
||||
self.action_num = 11;
|
||||
self.anim_num = 0;
|
||||
self.action_counter = 0;
|
||||
self.vel_x = 0;
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if player.x > self.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
}
|
||||
11 => {
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 10 {
|
||||
self.anim_num = 2;
|
||||
self.action_counter2 += 1;
|
||||
self.action_num = if self.action_counter2 > 4 { 12 } else { 10 };
|
||||
|
||||
state.sound_manager.play_sfx(30);
|
||||
self.vel_x = self.direction.vector_x() * 0x200;
|
||||
self.vel_y = -0x600;
|
||||
}
|
||||
}
|
||||
12 => {
|
||||
self.npc_flags.set_ignore_solidity(true);
|
||||
|
||||
if self.y > stage.map.height as i32 * state.tile_size.as_int() * 0x200 {
|
||||
self.vanish(state);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if self.action_num >= 10 {
|
||||
self.vel_y += 0x40;
|
||||
}
|
||||
|
||||
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 { 3 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n289_critter_orange[self.anim_num as usize + dir_offset];
|
||||
|
||||
if self.action_num == 1 {
|
||||
self.anim_rect.top += 8 - self.action_counter / 2;
|
||||
self.anim_rect.bottom -= 8 + self.action_counter / 2;
|
||||
self.display_bounds.top = (self.action_counter as u32 * 0x200) / 2;
|
||||
self.display_bounds.bottom = (self.action_counter as u32 * 0x200) / 2;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n290_bat_misery(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
stage: &mut Stage,
|
||||
) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.anim_num = 2;
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 16 {
|
||||
self.action_num = 10;
|
||||
self.display_bounds.top = 0x1000;
|
||||
self.display_bounds.bottom = 0x1000;
|
||||
self.damage = 2;
|
||||
self.npc_flags.set_shootable(true);
|
||||
self.target_y = self.y;
|
||||
self.vel_y = 0x400;
|
||||
}
|
||||
}
|
||||
10 => {
|
||||
self.animate(2, 0, 2);
|
||||
|
||||
self.vel_y += if self.y >= self.target_y { -0x40 } else { 0x40 };
|
||||
self.vel_x += if self.direction == Direction::Left { -0x10 } else { 0x10 };
|
||||
|
||||
if self.x < 0
|
||||
|| self.y < 0
|
||||
|| self.x > (stage.map.width as i32 * state.tile_size.as_int())
|
||||
|| self.y > (stage.map.height as i32 * state.tile_size.as_int())
|
||||
{
|
||||
self.vanish(state);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 3 };
|
||||
self.anim_rect = state.constants.npc.n290_bat_misery[self.anim_num as usize + dir_offset];
|
||||
|
||||
if self.action_num == 1 {
|
||||
self.anim_rect.top += 8 - self.action_counter / 2;
|
||||
self.anim_rect.bottom -= 8 + self.action_counter / 2;
|
||||
self.display_bounds.top = (self.action_counter as u32 * 0x200) / 2;
|
||||
self.display_bounds.bottom = (self.action_counter as u32 * 0x200) / 2;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n301_misery_fish_missile(
|
||||
&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.action_counter2 = self.tsc_direction;
|
||||
}
|
||||
|
||||
let radians = self.action_counter2 as f64 * CDEG_RAD;
|
||||
self.vel_x = 2 * (radians.cos() * 512.0) as i32;
|
||||
self.vel_y = 2 * (radians.sin() * 512.0) as i32;
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
let player = self.get_closest_player_mut(players);
|
||||
let direction = f64::atan2(-(self.y - player.y) as f64, -(self.x - player.x) as f64);
|
||||
|
||||
if direction < radians {
|
||||
if radians - direction < std::f64::consts::PI {
|
||||
self.action_counter2 = self.action_counter2.wrapping_sub(1) & 0xff;
|
||||
} else {
|
||||
self.action_counter2 = (self.action_counter2 + 1) & 0xff;
|
||||
}
|
||||
} else {
|
||||
if direction - radians < std::f64::consts::PI {
|
||||
self.action_counter2 = (self.action_counter2 + 1) & 0xff;
|
||||
} else {
|
||||
self.action_counter2 = self.action_counter2.wrapping_sub(1) & 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 2 {
|
||||
self.anim_counter = 0;
|
||||
state.create_caret(self.x, self.y, CaretType::Exhaust, Direction::FacingPlayer);
|
||||
}
|
||||
|
||||
self.anim_num = (self.action_counter2 + 0x10) / 0x20;
|
||||
|
||||
if self.anim_num > 7 {
|
||||
self.anim_num = 7;
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n301_misery_fish_missile[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,27 @@
|
|||
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;
|
||||
pub(super) mod balcony;
|
||||
pub(super) mod balrog;
|
||||
pub(super) mod booster;
|
||||
pub(super) mod chaco;
|
||||
pub(super) mod characters;
|
||||
pub(super) mod curly;
|
||||
pub(super) mod doctor;
|
||||
pub(super) mod egg_corridor;
|
||||
pub(super) mod first_cave;
|
||||
pub(super) mod grasstown;
|
||||
pub(super) mod hell;
|
||||
pub(super) mod igor;
|
||||
pub(super) mod intro;
|
||||
pub(super) mod last_cave;
|
||||
pub(super) mod maze;
|
||||
pub(super) mod mimiga_village;
|
||||
pub(super) mod misc;
|
||||
pub(super) mod misery;
|
||||
pub(super) mod outer_wall;
|
||||
pub(super) mod pickups;
|
||||
pub(super) mod plantation;
|
||||
pub(super) mod quote;
|
||||
pub(super) mod sand_zone;
|
||||
pub(super) mod santa;
|
||||
pub(super) mod sue;
|
||||
pub(super) mod toroko;
|
||||
pub(super) mod weapon_trail;
|
||||
|
|
|
|||
|
|
@ -301,4 +301,77 @@ impl NPC {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n347_hoppy(&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;
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
if player.y < self.y + 0x10000 && player.y > self.y - 0x10000 {
|
||||
self.action_num = 10;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 1;
|
||||
}
|
||||
}
|
||||
10 => {
|
||||
self.action_counter += 1;
|
||||
if self.action_counter == 4 {
|
||||
self.anim_num = 2;
|
||||
}
|
||||
|
||||
if self.action_counter > 12 {
|
||||
self.action_num = 12;
|
||||
self.anim_num = 3;
|
||||
self.vel_x = 0x700;
|
||||
state.sound_manager.play_sfx(6);
|
||||
}
|
||||
}
|
||||
12 => {
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
if player.y < self.y {
|
||||
self.vel_y = -0xAA;
|
||||
} else {
|
||||
self.vel_y = 0xAA;
|
||||
}
|
||||
|
||||
if self.flags.hit_left_wall() {
|
||||
self.action_num = 13;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 2;
|
||||
self.vel_x = 0;
|
||||
self.vel_y = 0;
|
||||
} else {
|
||||
self.vel_x -= 0x2A;
|
||||
if self.vel_x < -0x5FF {
|
||||
self.vel_x = -0x5FF;
|
||||
}
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
}
|
||||
}
|
||||
13 => {
|
||||
self.action_counter += 1;
|
||||
if self.action_counter == 2 {
|
||||
self.anim_num = 1;
|
||||
}
|
||||
|
||||
if self.action_counter == 6 {
|
||||
self.anim_num = 0;
|
||||
}
|
||||
|
||||
if self.action_counter > 16 {
|
||||
self.action_num = 1;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n347_hoppy[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::caret::CaretType;
|
||||
use crate::common::Direction;
|
||||
use crate::common::{Direction, CDEG_RAD};
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::NPC;
|
||||
|
|
@ -481,4 +481,733 @@ impl NPC {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n232_orangebell(&mut self, state: &mut SharedGameState, 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.vel_y = 0x200;
|
||||
|
||||
for _ in 0..8 {
|
||||
let mut npc = NPC::create(233, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
||||
npc.x = self.x;
|
||||
npc.y = self.y;
|
||||
npc.direction = self.direction;
|
||||
npc.parent_id = self.id;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
}
|
||||
|
||||
if self.vel_x < 0 && self.flags.hit_left_wall() {
|
||||
self.direction = Direction::Right;
|
||||
}
|
||||
|
||||
if self.vel_x > 0 && self.flags.hit_right_wall() {
|
||||
self.direction = Direction::Left;
|
||||
}
|
||||
|
||||
self.vel_x = self.direction.vector_x() * 0x100;
|
||||
|
||||
self.vel_y += if self.y < self.target_y { 8 } else { -8 };
|
||||
|
||||
self.vel_y = self.vel_y.clamp(-0x200, 0x200);
|
||||
|
||||
self.animate(5, 0, 2);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
self.anim_rect = state.constants.npc.n232_orangebell[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n233_orangebell_bat(
|
||||
&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.vel_x = ((self.rng.range(0..255) as f64 * CDEG_RAD).cos() * -512.0) as i32;
|
||||
self.vel_y = ((self.rng.range(0..255) as f64 * CDEG_RAD).sin() * -512.0) as i32;
|
||||
|
||||
self.action_counter2 = 120;
|
||||
self.vel_y2 = self.rng.range(-32..32) * 0x200;
|
||||
}
|
||||
|
||||
if let Some(parent) = self.get_parent_ref_mut(npc_list) {
|
||||
if parent.npc_type == 232 {
|
||||
self.target_x = parent.x;
|
||||
self.target_y = parent.y;
|
||||
self.direction = parent.direction;
|
||||
}
|
||||
}
|
||||
|
||||
self.vel_x += (self.target_x - self.x).signum() * 8;
|
||||
self.vel_y += ((self.target_y + self.vel_y2) - self.y).signum() * 0x20;
|
||||
|
||||
self.vel_x = self.vel_x.clamp(-0x400, 0x400);
|
||||
self.vel_y = self.vel_y.clamp(-0x400, 0x400);
|
||||
|
||||
if self.action_counter2 >= 120 {
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
if self.x - 0x1000 < player.x
|
||||
&& self.x + 0x1000 > player.x
|
||||
&& self.y < player.y
|
||||
&& self.y + 0x16000 > player.y
|
||||
{
|
||||
self.vel_x /= 4;
|
||||
self.vel_y = 0;
|
||||
self.action_num = 3;
|
||||
self.npc_flags.set_ignore_solidity(false);
|
||||
}
|
||||
} else {
|
||||
self.action_counter2 += 1;
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
self.vel_y += 0x40;
|
||||
if self.vel_y > 0x5ff {
|
||||
self.vel_y = 0x5ff;
|
||||
}
|
||||
|
||||
if self.flags.hit_bottom_wall() {
|
||||
self.vel_y = 0;
|
||||
self.vel_x *= 2;
|
||||
self.action_counter2 = 0;
|
||||
self.action_num = 1;
|
||||
self.npc_flags.set_ignore_solidity(true);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
if self.action_num == 3 {
|
||||
self.anim_num = 3;
|
||||
} else {
|
||||
self.animate(1, 0, 2);
|
||||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 4 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n233_orangebell_bat[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n235_midorin(&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..30) == 1 {
|
||||
self.action_num = 2;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 1;
|
||||
}
|
||||
|
||||
if self.rng.range(0..30) == 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;
|
||||
|
||||
self.direction = if self.rng.range(0..9) & 1 != 0 { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
|
||||
if self.direction == Direction::Left && self.flags.hit_left_wall() {
|
||||
self.direction = Direction::Right;
|
||||
}
|
||||
|
||||
if self.direction == Direction::Right && self.flags.hit_right_wall() {
|
||||
self.direction = Direction::Left;
|
||||
}
|
||||
|
||||
self.vel_x = self.direction.vector_x() * 0x400;
|
||||
|
||||
self.animate(1, 2, 3);
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 64 {
|
||||
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;
|
||||
|
||||
if self.anim_num == 2 {
|
||||
self.hit_bounds.top = 0xa00;
|
||||
} else {
|
||||
self.hit_bounds.top = 0x800;
|
||||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 4 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n235_midorin[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n236_gunfish(
|
||||
&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.action_counter = self.rng.range(0..50) as u16;
|
||||
self.target_x = self.x;
|
||||
self.target_y = self.y;
|
||||
self.vel_y = 0;
|
||||
}
|
||||
|
||||
if self.action_counter > 0 {
|
||||
self.action_counter -= 1;
|
||||
} else {
|
||||
self.action_num = 2;
|
||||
self.vel_y = 0x200;
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
if self.x >= player.x {
|
||||
self.direction = Direction::Left;
|
||||
} else {
|
||||
self.direction = Direction::Right;
|
||||
}
|
||||
|
||||
if player.x - 0x10000 < self.x
|
||||
&& player.x + 0x10000 > self.x
|
||||
&& player.y - 0x4000 < self.y
|
||||
&& player.y + 0x14000 > self.y
|
||||
{
|
||||
self.action_counter += 1;
|
||||
}
|
||||
|
||||
if self.action_counter > 80 {
|
||||
self.action_num = 10;
|
||||
self.action_counter = 0;
|
||||
}
|
||||
|
||||
self.animate(1, 0, 1);
|
||||
}
|
||||
10 => {
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 20 {
|
||||
self.action_num = 20;
|
||||
self.action_counter = 0;
|
||||
}
|
||||
|
||||
self.animate(1, 2, 3);
|
||||
}
|
||||
20 => {
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 60 {
|
||||
self.action_num = 2;
|
||||
self.action_counter = 0;
|
||||
}
|
||||
|
||||
if self.action_counter % 10 == 3 {
|
||||
state.sound_manager.play_sfx(39);
|
||||
|
||||
let mut npc = NPC::create(237, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
||||
if self.direction == Direction::Left {
|
||||
npc.x = self.x - 0x1000;
|
||||
npc.y = self.y - 0x1000;
|
||||
npc.vel_x = -0x400;
|
||||
npc.vel_y = -0x400;
|
||||
} else {
|
||||
npc.x = self.x + 0x1000;
|
||||
npc.y = self.y - 0x1000;
|
||||
npc.vel_x = 0x400;
|
||||
npc.vel_y = -0x400;
|
||||
}
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
|
||||
self.animate(1, 4, 5);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if self.y < self.target_y {
|
||||
self.vel_y += 0x10;
|
||||
} else {
|
||||
self.vel_y -= 0x10;
|
||||
}
|
||||
|
||||
self.vel_y = self.vel_y.clamp(-0x100, 0x100);
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 6 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n236_gunfish[dir_offset + self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n237_gunfish_projectile(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
}
|
||||
|
||||
if self.action_num == 1 {
|
||||
self.action_counter += 1;
|
||||
|
||||
let hit = self.flags.hit_anything() || (self.action_counter > 10 && self.flags.in_water());
|
||||
|
||||
if hit {
|
||||
for _ in 0..5 {
|
||||
state.create_caret(self.x, self.y, CaretType::Bubble, Direction::Left);
|
||||
}
|
||||
|
||||
state.sound_manager.play_sfx(21);
|
||||
self.cond.set_alive(false);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
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.n237_gunfish_projectile;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n240_mimiga_jailed(&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;
|
||||
|
||||
self.direction = if self.rng.range(0..9) & 1 != 0 { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
|
||||
if self.direction == Direction::Left && self.flags.hit_left_wall() {
|
||||
self.direction = Direction::Right;
|
||||
}
|
||||
|
||||
if self.direction == Direction::Right && self.flags.hit_right_wall() {
|
||||
self.direction = Direction::Left;
|
||||
}
|
||||
|
||||
self.vel_x = self.direction.vector_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.n240_mimiga_jailed[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n273_droll_projectile(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
}
|
||||
|
||||
if self.action_num == 1 {
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
if self.flags.any_flag() {
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x;
|
||||
npc.y = self.y;
|
||||
|
||||
for _ in 0..3 {
|
||||
let _ = npc_list.spawn(0x100, npc.clone());
|
||||
}
|
||||
|
||||
self.vanish(state);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter % 5 != 0 {
|
||||
state.sound_manager.play_sfx(110);
|
||||
}
|
||||
|
||||
self.anim_num += 1;
|
||||
if self.anim_num > 2 {
|
||||
self.anim_num = 0;
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n273_droll_projectile[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n274_droll(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 | 2 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.y -= 0x1000;
|
||||
self.target_x = self.x;
|
||||
}
|
||||
|
||||
if self.action_num == 1 {
|
||||
self.vel_x = 0;
|
||||
self.action_num = 2;
|
||||
self.anim_num = 0;
|
||||
}
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
|
||||
|
||||
self.animate(40, 0, 1);
|
||||
|
||||
if self.shock > 0 {
|
||||
self.action_num = 10;
|
||||
}
|
||||
}
|
||||
10 | 11 => {
|
||||
if self.action_num == 10 {
|
||||
self.action_num = 11;
|
||||
self.anim_num = 2;
|
||||
self.action_counter = 0;
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 10 {
|
||||
self.action_num = 12;
|
||||
self.anim_num = 3;
|
||||
self.action_counter2 = 0;
|
||||
|
||||
self.vel_y = -0x600;
|
||||
self.vel_x = self.direction.vector_x() * 0x200;
|
||||
}
|
||||
}
|
||||
12 => {
|
||||
if self.vel_y > 0 {
|
||||
self.anim_num = 4;
|
||||
if self.action_counter2 == 0 {
|
||||
self.action_counter2 += 1;
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
let mut npc = NPC::create(273, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.x;
|
||||
npc.y = self.y;
|
||||
|
||||
let angle = f64::atan2((player.y - 0x1400 - self.y) as f64, (player.x - self.x) as f64);
|
||||
npc.vel_x = (2048.0 * angle.cos()) as i32;
|
||||
npc.vel_y = (2048.0 * angle.sin()) as i32;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
|
||||
state.sound_manager.play_sfx(39);
|
||||
}
|
||||
}
|
||||
|
||||
if self.vel_y > 0x200 {
|
||||
self.anim_num = 5;
|
||||
}
|
||||
|
||||
if self.flags.hit_bottom_wall() {
|
||||
self.action_num = 13;
|
||||
self.anim_num = 2;
|
||||
self.action_counter = 0;
|
||||
self.vel_x = 0;
|
||||
}
|
||||
}
|
||||
13 => {
|
||||
self.vel_x /= 2;
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 10 {
|
||||
self.action_num = 1;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.vel_y += 0x55;
|
||||
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.n274_droll[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n275_puppy_plantation(
|
||||
&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.action_counter = 0;
|
||||
}
|
||||
|
||||
if self.rng.range(0..120) == 10 {
|
||||
self.action_num = 2;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 1;
|
||||
}
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
if self.x - 0x8000 < player.x
|
||||
&& self.x + 0x8000 > player.x
|
||||
&& self.y - 0x4000 < player.y
|
||||
&& self.y + 0x2000 > player.y
|
||||
{
|
||||
self.animate(3, 2, 3);
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 8 {
|
||||
self.action_num = 1;
|
||||
self.anim_num = 0;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
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.n275_puppy_plantation[self.anim_num as usize];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n308_stumpy(&mut self, state: &mut SharedGameState, players: [&mut Player; 2]) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
self.action_num = 1;
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
if player.x < self.x + 0x1e000
|
||||
&& player.x > self.x - 0x1e000
|
||||
&& player.y < self.y + 0x18000
|
||||
&& player.y > self.y - 0x18000
|
||||
{
|
||||
self.action_num = 10;
|
||||
}
|
||||
}
|
||||
10 | 11 => {
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
if self.action_num == 10 {
|
||||
self.action_num = 11;
|
||||
self.action_counter = 0;
|
||||
self.vel_x2 = 0;
|
||||
self.vel_y2 = 0;
|
||||
|
||||
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 50 {
|
||||
self.action_num = 20;
|
||||
}
|
||||
|
||||
self.anim_counter += 1;
|
||||
// this is a typo in original exe
|
||||
if self.action_counter > 1 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
|
||||
if self.anim_num > 1 {
|
||||
self.anim_num = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if player.x > self.x + 0x28000
|
||||
&& player.x < self.x - 0x28000
|
||||
&& player.y > self.y + 0x1e000
|
||||
&& player.y < self.y - 0x1e000
|
||||
{
|
||||
self.action_num = 0;
|
||||
}
|
||||
}
|
||||
20 | 21 => {
|
||||
if self.action_num == 20 {
|
||||
self.action_num = 21;
|
||||
self.action_counter = 0;
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
let deg = f64::atan2((self.y - player.y) as f64, (self.x - player.x) as f64);
|
||||
let deg = deg + self.rng.range(-3..3) as f64 * CDEG_RAD;
|
||||
|
||||
self.vel_x2 = (deg.cos() * -1024.0) as i32;
|
||||
self.vel_y2 = (deg.sin() * -1024.0) as i32;
|
||||
|
||||
self.direction = if self.vel_x2 < 0 { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
|
||||
if self.vel_x2 < 0 && self.flags.hit_left_wall() {
|
||||
self.direction = Direction::Right;
|
||||
self.vel_x2 = -self.vel_x2;
|
||||
}
|
||||
|
||||
if self.vel_x2 > 0 && self.flags.hit_right_wall() {
|
||||
self.direction = Direction::Left;
|
||||
self.vel_x2 = -self.vel_x2;
|
||||
}
|
||||
|
||||
if self.vel_y2 < 0 && self.flags.hit_top_wall() {
|
||||
self.vel_y2 = -self.vel_y2;
|
||||
}
|
||||
|
||||
if self.vel_y2 > 0 && self.flags.hit_bottom_wall() {
|
||||
self.vel_y2 = -self.vel_y2;
|
||||
}
|
||||
|
||||
if self.flags.in_water() {
|
||||
self.vel_y2 = -0x200;
|
||||
}
|
||||
|
||||
self.x += self.vel_x2;
|
||||
self.y += self.vel_y2;
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 50 {
|
||||
self.action_num = 10;
|
||||
}
|
||||
|
||||
self.anim_num += 1;
|
||||
if self.anim_num > 1 {
|
||||
self.anim_num = 0;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n308_stumpy[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::framework::error::GameResult;
|
||||
use num_traits::abs;
|
||||
|
||||
use crate::common::Direction;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::NPC;
|
||||
use crate::player::Player;
|
||||
use crate::rng::RNG;
|
||||
|
|
@ -24,13 +24,8 @@ impl NPC {
|
|||
}
|
||||
|
||||
let player = self.get_closest_player_mut(players);
|
||||
if abs(self.x - player.x) < 32 * 0x200
|
||||
&& self.y - 32 * 0x200 < player.y && self.y + 0x2000 > player.y {
|
||||
self.direction = if self.x > player.x {
|
||||
Direction::Left
|
||||
} else {
|
||||
Direction::Right
|
||||
};
|
||||
if abs(self.x - player.x) < 32 * 0x200 && self.y - 32 * 0x200 < player.y && self.y + 0x2000 > player.y {
|
||||
self.direction = if self.x > player.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
|
|
@ -71,4 +66,38 @@ impl NPC {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n307_santa_caged(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 0;
|
||||
self.x += 0x200;
|
||||
self.y -= 0x400;
|
||||
}
|
||||
|
||||
if self.rng.range(0..160) == 10 {
|
||||
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 dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n307_santa_caged[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
use crate::framework::error::GameResult;
|
||||
|
||||
use crate::common::Direction;
|
||||
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 fn tick_n042_sue(&mut self, state: &mut SharedGameState, players: [&mut Player; 2], npc_list: &NPCList) -> GameResult {
|
||||
pub fn tick_n042_sue(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
|
|
@ -111,7 +117,8 @@ impl NPC {
|
|||
self.vel_y = 0;
|
||||
self.action_num = 14;
|
||||
|
||||
self.parent_id = npc_list.iter_alive()
|
||||
self.parent_id = npc_list
|
||||
.iter_alive()
|
||||
.find_map(|npc| if npc.event_num == 501 { Some(npc.id) } else { None })
|
||||
.unwrap_or(0);
|
||||
}
|
||||
|
|
@ -143,19 +150,13 @@ impl NPC {
|
|||
let _ = npc_list.spawn(0x80, npc);
|
||||
}
|
||||
|
||||
state.npc_super_pos = (
|
||||
self.x - 24 * 0x200,
|
||||
self.y - 0x1000
|
||||
);
|
||||
state.npc_super_pos = (self.x - 24 * 0x200, self.y - 0x1000);
|
||||
}
|
||||
17 => {
|
||||
self.vel_x = 0;
|
||||
self.anim_num = 12;
|
||||
|
||||
state.npc_super_pos = (
|
||||
self.x,
|
||||
self.y - 0x1000
|
||||
);
|
||||
state.npc_super_pos = (self.x, self.y - 0x1000);
|
||||
}
|
||||
20 | 21 => {
|
||||
if self.action_num == 20 {
|
||||
|
|
@ -282,4 +283,312 @@ impl NPC {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n280_sue_teleported(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.action_counter = 0;
|
||||
self.anim_counter = 0;
|
||||
self.x += 0xc00;
|
||||
self.target_x = self.x;
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 64 {
|
||||
self.action_num = 2;
|
||||
self.action_counter = 0;
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
self.anim_num = 0;
|
||||
if self.flags.hit_bottom_wall() {
|
||||
self.action_num = 4;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 1;
|
||||
state.sound_manager.play_sfx(23);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if self.action_num > 1 {
|
||||
self.vel_y += 0x20;
|
||||
if self.vel_y > 0x5FF {
|
||||
self.vel_y = 0x5FF;
|
||||
}
|
||||
|
||||
self.y += self.vel_y;
|
||||
}
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n280_sue_teleported[self.anim_num as usize + dir_offset];
|
||||
|
||||
if self.action_num == 1 {
|
||||
self.anim_rect.bottom = self.anim_rect.top + self.action_counter / 4;
|
||||
|
||||
self.x = if self.action_counter & 2 != 0 { self.target_x } else { self.target_x + 0x200 };
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n284_sue_possessed(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
stage: &mut Stage,
|
||||
boss: &mut BossNPC,
|
||||
) -> GameResult {
|
||||
if self.action_num < 100 && (!boss.parts[0].cond.alive() || self.life < 400) {
|
||||
self.action_num = 100;
|
||||
}
|
||||
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.y -= 0x800;
|
||||
self.action_counter3 = self.life;
|
||||
|
||||
state.sound_manager.play_sfx(29);
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter & 2 != 0 {
|
||||
self.display_bounds.top = 0x2000;
|
||||
self.display_bounds.right = 0x2000;
|
||||
self.display_bounds.left = 0x2000;
|
||||
self.anim_num = 11;
|
||||
} else {
|
||||
self.display_bounds.top = 0x600;
|
||||
self.display_bounds.right = 0x1000;
|
||||
self.display_bounds.left = 0x1000;
|
||||
self.anim_num = 12;
|
||||
}
|
||||
|
||||
if self.action_counter > 50 {
|
||||
self.action_num = 10;
|
||||
}
|
||||
}
|
||||
10 => {
|
||||
self.action_num = 11;
|
||||
self.anim_num = 11;
|
||||
self.display_bounds.top = 0x2000;
|
||||
self.display_bounds.right = 0x2000;
|
||||
self.display_bounds.left = 0x2000;
|
||||
|
||||
npc_list.kill_npcs_by_type(257, true, state);
|
||||
}
|
||||
20 | 21 => {
|
||||
if self.action_num == 20 {
|
||||
self.action_num = 21;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 0;
|
||||
self.anim_counter = 0;
|
||||
self.damage = 0;
|
||||
self.npc_flags.set_shootable(true);
|
||||
self.npc_flags.set_ignore_solidity(false);
|
||||
}
|
||||
|
||||
self.vel_x = 7 * self.vel_x / 8;
|
||||
self.vel_y = 7 * self.vel_y / 8;
|
||||
|
||||
self.animate(20, 0, 1);
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 80 {
|
||||
self.action_num = 30;
|
||||
}
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
self.direction = if player.x > self.x { Direction::Left } else { Direction::Right };
|
||||
|
||||
if self.life + 50 < self.action_counter3 {
|
||||
self.action_counter3 = self.life;
|
||||
state.npc_super_pos.0 = 10;
|
||||
}
|
||||
}
|
||||
30 | 31 => {
|
||||
if self.action_num == 30 {
|
||||
self.action_num = 31;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 2;
|
||||
self.vel_x = 0;
|
||||
self.vel_y = 0;
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 16 {
|
||||
self.action_counter2 += 1;
|
||||
self.action_counter2 &= 3;
|
||||
|
||||
self.action_num = match self.action_counter2 {
|
||||
0 | 2 => 32,
|
||||
1 | 3 => 34,
|
||||
_ => self.action_num,
|
||||
};
|
||||
}
|
||||
}
|
||||
32 | 33 => {
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
if self.action_num == 32 {
|
||||
self.action_num = 33;
|
||||
self.action_counter = 0;
|
||||
self.npc_flags.set_shootable(false);
|
||||
self.npc_flags.set_ignore_solidity(false);
|
||||
self.target_x = if player.x >= self.x { player.x + 0x14000 } else { player.x - 0x14000 };
|
||||
self.target_y = player.y;
|
||||
|
||||
let angle = f64::atan2((self.y - self.target_y) as f64, (self.x - self.target_x) as f64);
|
||||
|
||||
self.vel_x = (-1536.0 * angle.cos()) as i32;
|
||||
self.vel_y = (-1536.0 * angle.sin()) as i32;
|
||||
|
||||
let half_w = stage.map.width as i32 * state.tile_size.as_int() * 0x200 / 2;
|
||||
let half_h = stage.map.height as i32 * state.tile_size.as_int() * 0x200 / 2;
|
||||
|
||||
if ((self.x < half_w && self.vel_x > 0) || (self.x > half_w && self.vel_x < 0))
|
||||
|| ((self.y < half_h && self.vel_y > 0) || (self.y > half_h && self.vel_y < 0))
|
||||
{
|
||||
self.npc_flags.set_ignore_solidity(true);
|
||||
}
|
||||
|
||||
self.direction = if self.vel_x <= 0 { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
self.anim_num = if self.action_counter & 2 != 0 { 3 } else { 8 };
|
||||
|
||||
if self.action_counter > 50 || (self.flags.hit_right_wall() || self.flags.hit_left_wall()) {
|
||||
self.action_num = 20;
|
||||
}
|
||||
}
|
||||
34 | 35 => {
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
|
||||
if self.action_num == 34 {
|
||||
self.action_num = 35;
|
||||
self.action_counter = 0;
|
||||
self.damage = 4;
|
||||
self.npc_flags.set_ignore_solidity(false);
|
||||
self.target_x = player.x;
|
||||
self.target_y = player.y;
|
||||
|
||||
let angle = f64::atan2((self.y - self.target_y) as f64, (self.x - self.target_x) as f64);
|
||||
|
||||
self.vel_x = (-1536.0 * angle.cos()) as i32;
|
||||
self.vel_y = (-1536.0 * angle.sin()) as i32;
|
||||
|
||||
let half_w = stage.map.width as i32 * state.tile_size.as_int() * 0x200 / 2;
|
||||
let half_h = stage.map.height as i32 * state.tile_size.as_int() * 0x200 / 2;
|
||||
|
||||
if ((self.x < half_w && self.vel_x > 0) || (self.x > half_w && self.vel_x < 0))
|
||||
|| ((self.y < half_h && self.vel_y > 0) || (self.y > half_h && self.vel_y < 0))
|
||||
{
|
||||
self.npc_flags.set_ignore_solidity(true);
|
||||
}
|
||||
|
||||
self.direction = if self.vel_x <= 0 { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 20 && self.shock != 0 {
|
||||
self.action_num = 40;
|
||||
} else if self.action_counter > 50 || (self.flags.hit_right_wall() || self.flags.hit_left_wall()) {
|
||||
self.action_num = 20;
|
||||
}
|
||||
|
||||
self.animate(1, 4, 7);
|
||||
|
||||
if self.action_counter % 5 == 1 {
|
||||
state.sound_manager.play_sfx(109);
|
||||
}
|
||||
}
|
||||
40 | 41 => {
|
||||
if self.action_num == 40 {
|
||||
self.action_num = 41;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 2;
|
||||
self.damage = 0;
|
||||
self.npc_flags.set_ignore_solidity(false);
|
||||
}
|
||||
|
||||
self.vel_x = 7 * self.vel_x / 8;
|
||||
self.vel_y = 7 * self.vel_y / 8;
|
||||
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 6 {
|
||||
self.action_num = 42;
|
||||
self.action_counter = 0;
|
||||
|
||||
self.vel_x = self.direction.vector_x() * 0x200;
|
||||
self.vel_y = -0x200;
|
||||
}
|
||||
}
|
||||
42 => {
|
||||
self.anim_num = 9;
|
||||
if self.flags.hit_bottom_wall() {
|
||||
self.action_num = 43;
|
||||
self.action_counter = 0;
|
||||
self.anim_num = 2;
|
||||
|
||||
let player = self.get_closest_player_ref(&players);
|
||||
self.direction = if self.x < player.x { Direction::Right } else { Direction::Left };
|
||||
}
|
||||
|
||||
self.vel_y += 0x20;
|
||||
if self.vel_y > 0x5FF {
|
||||
self.vel_y = 0x5FF;
|
||||
}
|
||||
}
|
||||
43 => {
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > 16 {
|
||||
self.action_num = 20;
|
||||
}
|
||||
}
|
||||
99 => {
|
||||
self.vel_x = 0;
|
||||
self.vel_y = 0;
|
||||
self.anim_num = 9;
|
||||
self.npc_flags.set_shootable(false);
|
||||
}
|
||||
100 | 101 => {
|
||||
if self.action_num == 100 {
|
||||
self.action_num = 101;
|
||||
self.anim_num = 9;
|
||||
self.damage = 0;
|
||||
self.npc_flags.set_shootable(false);
|
||||
self.npc_flags.set_ignore_solidity(true);
|
||||
self.shock += 50;
|
||||
boss.parts[0].anim_num += 1;
|
||||
}
|
||||
|
||||
self.vel_y += 0x20;
|
||||
if self.y > 0x1B000 - self.hit_bounds.bottom as i32 {
|
||||
self.y = 0x1B000 - self.hit_bounds.bottom as i32;
|
||||
self.action_num = 102;
|
||||
self.anim_num = 10;
|
||||
self.vel_x = 0;
|
||||
self.vel_y = 0;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.x += if self.shock > 0 { self.vel_x / 2 } else { self.vel_x };
|
||||
self.y += self.vel_y;
|
||||
|
||||
let dir_offset = if self.direction == Direction::Left { 0 } else { 13 };
|
||||
|
||||
self.anim_rect = state.constants.npc.n284_sue_possessed[self.anim_num as usize + dir_offset];
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,14 +20,7 @@ impl NPC {
|
|||
self.y += self.vel_y;
|
||||
|
||||
self.action_counter += 1;
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 1 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
if self.anim_num > 2 {
|
||||
self.anim_num = 0;
|
||||
}
|
||||
}
|
||||
self.animate(1, 0, 2);
|
||||
|
||||
self.anim_rect = state.constants.npc.n108_balfrog_projectile[self.anim_num as usize];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use crate::common::Direction;
|
||||
use crate::common::{Direction, Rect};
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::boss::BossNPC;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::NPC;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::player::Player;
|
||||
use crate::rng::RNG;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
|
||||
impl NPC {
|
||||
pub(crate) fn tick_n196_ironhead_wall(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
|
|
@ -96,5 +98,174 @@ impl NPC {
|
|||
}
|
||||
|
||||
impl BossNPC {
|
||||
pub(crate) fn tick_b05_ironhead(&mut self) {}
|
||||
pub(crate) fn tick_b05_ironhead(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
) {
|
||||
match self.parts[0].action_num {
|
||||
0 => {
|
||||
self.parts[0].cond.set_alive(true);
|
||||
self.parts[0].exp = 1;
|
||||
self.parts[0].direction = Direction::Right;
|
||||
self.parts[0].action_num = 100;
|
||||
self.parts[0].x = 0x14000;
|
||||
self.parts[0].y = 0x10000;
|
||||
self.hurt_sound[0] = 54;
|
||||
self.parts[0].npc_flags.set_event_when_killed(true);
|
||||
self.parts[0].npc_flags.set_shootable(true);
|
||||
self.parts[0].npc_flags.set_ignore_solidity(true);
|
||||
self.parts[0].npc_flags.set_show_damage(true);
|
||||
self.parts[0].size = 3;
|
||||
self.parts[0].damage = 10;
|
||||
self.parts[0].event_num = 1000;
|
||||
self.parts[0].life = 400;
|
||||
self.parts[0].display_bounds = Rect::new(0x5000, 0x1800, 0x3000, 0x1800);
|
||||
self.parts[0].hit_bounds = Rect::new(0x2000, 0x1400, 0x2000, 0x1400);
|
||||
}
|
||||
100 | 101 => {
|
||||
if self.parts[0].action_num == 100 {
|
||||
self.parts[0].action_num = 101;
|
||||
self.parts[0].action_counter = 0;
|
||||
self.parts[0].npc_flags.set_shootable(false);
|
||||
}
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
if self.parts[0].action_counter > 50 {
|
||||
self.parts[0].action_num = 250;
|
||||
self.parts[0].action_counter = 0;
|
||||
}
|
||||
|
||||
if self.parts[0].action_counter % 4 == 0 {
|
||||
let mut npc = NPC::create(197, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.parts[0].rng.range(15..18) * state.tile_size.as_int() * 0x200;
|
||||
npc.y = self.parts[0].rng.range(2..13) * state.tile_size.as_int() * 0x200;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
}
|
||||
250 | 251 => {
|
||||
if self.parts[0].action_num == 250 {
|
||||
self.parts[0].action_num = 251;
|
||||
if self.parts[0].direction == Direction::Right {
|
||||
let player = self.parts[0].get_closest_player_ref(&players);
|
||||
|
||||
self.parts[0].x = 0x1E000;
|
||||
self.parts[0].y = player.y;
|
||||
} else {
|
||||
self.parts[0].x = 0x5A000;
|
||||
self.parts[0].y = self.parts[0].rng.range(2..13) * state.tile_size.as_int() * 0x200;
|
||||
}
|
||||
|
||||
self.parts[0].target_x = self.parts[0].x;
|
||||
self.parts[0].target_y = self.parts[0].y;
|
||||
self.parts[0].vel_y = self.parts[0].rng.range(-0x200..0x200);
|
||||
self.parts[0].vel_x = self.parts[0].rng.range(-0x200..0x200);
|
||||
self.parts[0].npc_flags.set_shootable(true);
|
||||
}
|
||||
|
||||
if self.parts[0].direction == Direction::Right {
|
||||
self.parts[0].target_x += 0x400;
|
||||
} else {
|
||||
self.parts[0].target_x -= 0x200;
|
||||
if self.parts[0].target_y >= self.parts[0].y {
|
||||
self.parts[0].target_y -= 0x200;
|
||||
} else {
|
||||
self.parts[0].target_y += 0x200;
|
||||
}
|
||||
}
|
||||
|
||||
self.parts[0].vel_x += if self.parts[0].x >= self.parts[0].target_x { -8 } else { 8 };
|
||||
self.parts[0].vel_y += if self.parts[0].y >= self.parts[0].target_y { -8 } else { 8 };
|
||||
self.parts[0].vel_y = self.parts[0].vel_y.clamp(-0x200, 0x200);
|
||||
self.parts[0].x += self.parts[0].vel_x;
|
||||
self.parts[0].y += self.parts[0].vel_y;
|
||||
|
||||
if self.parts[0].direction == Direction::Right {
|
||||
if self.parts[0].x > 0x5A000 {
|
||||
self.parts[0].direction = Direction::Left;
|
||||
self.parts[0].action_num = 100;
|
||||
}
|
||||
} else if self.parts[0].x < 0x22000 {
|
||||
self.parts[0].direction = Direction::Right;
|
||||
self.parts[0].action_num = 100;
|
||||
}
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
if [300, 310, 320].contains(&self.parts[0].action_counter) {
|
||||
state.sound_manager.play_sfx(39);
|
||||
|
||||
let mut npc = NPC::create(198, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
npc.x = self.parts[0].x + 0x1400;
|
||||
npc.y = self.parts[0].y + 0x200;
|
||||
npc.vel_x = self.parts[0].rng.range(-3..0) * 0x200;
|
||||
npc.vel_y = self.parts[0].rng.range(-3..3) * 0x200;
|
||||
npc.direction = Direction::Right;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
|
||||
self.parts[0].animate(2, 0, 7);
|
||||
}
|
||||
1000 | 1001 => {
|
||||
if self.parts[0].action_num == 1000 {
|
||||
self.parts[0].action_num = 1001;
|
||||
self.parts[0].anim_num = 8;
|
||||
self.parts[0].npc_flags.set_shootable(false);
|
||||
self.parts[0].damage = 0;
|
||||
self.parts[0].target_x = self.parts[0].x;
|
||||
self.parts[0].target_y = self.parts[0].y;
|
||||
state.quake_counter = 20;
|
||||
for _ in 0..32 {
|
||||
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(-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;
|
||||
npc.direction = Direction::Left;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
|
||||
npc_list.kill_npcs_by_type(197, true, state);
|
||||
npc_list.kill_npcs_by_type(271, true, state);
|
||||
npc_list.kill_npcs_by_type(272, true, state);
|
||||
}
|
||||
|
||||
self.parts[0].target_x -= 0x200;
|
||||
self.parts[0].x = self.parts[0].target_x + self.parts[0].rng.range(-1..1) * 0x200;
|
||||
self.parts[0].y = self.parts[0].target_y + self.parts[0].rng.range(-1..1) * 0x200;
|
||||
|
||||
if self.parts[0].action_counter % 4 == 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(-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;
|
||||
npc.direction = Direction::Left;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let offset = if self.parts[0].shock != 0 {
|
||||
self.parts[19].action_counter += 1;
|
||||
if self.parts[19].action_counter & 2 != 0 {
|
||||
0
|
||||
} else {
|
||||
9
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
self.parts[0].anim_rect = state.constants.npc.b05_ironhead[self.parts[0].anim_num as usize + offset];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ pub mod ironhead;
|
|||
pub mod monster_x;
|
||||
pub mod omega;
|
||||
pub mod press;
|
||||
pub mod twins;
|
||||
pub mod sisters;
|
||||
pub mod undead_core;
|
||||
|
||||
pub struct BossNPC {
|
||||
|
|
@ -82,9 +82,9 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager, &mut Fl
|
|||
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, stage),
|
||||
5 => self.tick_b05_ironhead(),
|
||||
6 => self.tick_b06_twins(),
|
||||
7 => self.tick_b07_undead_core(),
|
||||
5 => self.tick_b05_ironhead(state, players, npc_list),
|
||||
6 => self.tick_b06_sisters(state, players, npc_list, flash),
|
||||
7 => self.tick_b07_undead_core(state, npc_list, stage, flash),
|
||||
8 => self.tick_b08_press(),
|
||||
9 => self.tick_b09_ballos(),
|
||||
_ => {}
|
||||
|
|
|
|||
464
src/npc/boss/sisters.rs
Normal file
464
src/npc/boss/sisters.rs
Normal file
|
|
@ -0,0 +1,464 @@
|
|||
use crate::common::{Direction, Rect, SliceExt, CDEG_RAD};
|
||||
use crate::components::flash::Flash;
|
||||
use crate::npc::boss::BossNPC;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::NPC;
|
||||
use crate::player::Player;
|
||||
use crate::rng::RNG;
|
||||
use crate::SharedGameState;
|
||||
|
||||
impl BossNPC {
|
||||
pub(crate) fn tick_b06_sisters(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
players: [&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
flash: &mut Flash,
|
||||
) {
|
||||
match self.parts[0].action_num {
|
||||
0 => {
|
||||
self.parts[0].cond.set_alive(true);
|
||||
self.parts[0].direction = Direction::Left;
|
||||
self.parts[0].action_num = 10;
|
||||
self.parts[0].exp = 0;
|
||||
self.parts[0].x = 0x14000;
|
||||
self.parts[0].y = 0x10000;
|
||||
self.parts[0].display_bounds = Rect::new(0x1000, 0x1000, 0x10000, 0x1000);
|
||||
self.parts[0].hit_bounds = Rect::new(0x1000, 0x1000, 0x1000, 0x1000);
|
||||
self.hurt_sound[0] = 54;
|
||||
self.parts[0].npc_flags.set_ignore_solidity(true);
|
||||
self.parts[0].npc_flags.set_event_when_killed(true);
|
||||
self.parts[0].size = 3;
|
||||
self.parts[0].damage = 0;
|
||||
self.parts[0].event_num = 1000;
|
||||
self.parts[0].life = 500;
|
||||
self.parts[0].action_counter3 = self.parts[0].rng.range(700..1200) as u16;
|
||||
self.parts[0].target_x = 0xB4;
|
||||
self.parts[0].target_y = 0x3D;
|
||||
|
||||
self.parts[2].display_bounds = Rect::new(0x2800, 0x2000, 0x2800, 0x2000);
|
||||
self.parts[2].hit_bounds = Rect::new(0x1800, 0x1400, 0x1800, 0x1400);
|
||||
self.parts[2].npc_flags.set_ignore_solidity(true);
|
||||
self.parts[2].npc_flags.set_invulnerable(true);
|
||||
self.parts[2].parent_id = 3;
|
||||
self.parts[2].cond.set_alive(true);
|
||||
self.parts[2].cond.set_damage_boss(true);
|
||||
self.parts[2].damage = 10;
|
||||
|
||||
self.parts[3].cond.set_alive(true);
|
||||
self.parts[3].display_bounds = Rect::new(0x2800, 0x2800, 0x2800, 0x2800);
|
||||
self.parts[3].hit_bounds = Rect::new(0x1800, 0x400, 0x1800, 0x2000);
|
||||
self.parts[3].npc_flags.set_ignore_solidity(true);
|
||||
self.parts[3].parent_id = 0;
|
||||
self.parts[3].damage = 10;
|
||||
|
||||
self.parts[4] = self.parts[2].clone();
|
||||
self.parts[4].id = 4;
|
||||
self.parts[4].parent_id = 5;
|
||||
|
||||
self.parts[5] = self.parts[3].clone();
|
||||
self.parts[5].id = 5;
|
||||
self.parts[5].action_counter2 = 128;
|
||||
}
|
||||
20 => {
|
||||
self.parts[0].target_x -= 1;
|
||||
if self.parts[0].target_x <= 112 {
|
||||
self.parts[0].action_num = 100;
|
||||
self.parts[0].action_counter = 0;
|
||||
self.parts[2].action_num = 100;
|
||||
self.parts[4].action_num = 100;
|
||||
self.parts[3].action_num = 100;
|
||||
self.parts[5].action_num = 100;
|
||||
}
|
||||
}
|
||||
100 => {
|
||||
let actr2: &mut i16 = unsafe { std::mem::transmute(&mut self.parts[0].action_counter2) };
|
||||
let mut b = true;
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
if self.parts[0].action_counter < 100 {
|
||||
*actr2 += 1;
|
||||
} else if self.parts[0].action_counter < 120 {
|
||||
*actr2 += 2;
|
||||
} else if self.parts[0].action_counter < self.parts[0].action_counter3 {
|
||||
*actr2 += 4;
|
||||
} else if self.parts[0].action_counter < self.parts[0].action_counter3 + 40 {
|
||||
*actr2 += 2;
|
||||
} else if self.parts[0].action_counter < self.parts[0].action_counter3 + 60 {
|
||||
*actr2 += 1;
|
||||
} else {
|
||||
self.parts[0].action_counter = 0;
|
||||
self.parts[0].action_num = 110;
|
||||
self.parts[0].action_counter3 = self.parts[0].rng.range(400..700) as u16;
|
||||
b = false;
|
||||
}
|
||||
|
||||
if b && *actr2 >= 0x400 {
|
||||
*actr2 -= 0x400;
|
||||
}
|
||||
}
|
||||
110 => {
|
||||
let actr2: &mut i16 = unsafe { std::mem::transmute(&mut self.parts[0].action_counter2) };
|
||||
let mut b = true;
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
if self.parts[0].action_counter < 20 {
|
||||
*actr2 -= 1;
|
||||
} else if self.parts[0].action_counter < 60 {
|
||||
*actr2 -= 2;
|
||||
} else if self.parts[0].action_counter < self.parts[0].action_counter2 {
|
||||
*actr2 -= 4;
|
||||
} else if self.parts[0].action_counter < self.parts[0].action_counter2 + 40 {
|
||||
*actr2 -= 2;
|
||||
} else if self.parts[0].action_counter < self.parts[0].action_counter2 + 60 {
|
||||
*actr2 -= 1;
|
||||
} else {
|
||||
if self.parts[0].life >= 300 {
|
||||
self.parts[0].action_counter = 0;
|
||||
self.parts[0].action_num = 100;
|
||||
*actr2 = self.parts[0].rng.range(400..700) as i16;
|
||||
} else {
|
||||
self.parts[0].action_counter = 0;
|
||||
self.parts[0].action_num = 400;
|
||||
self.parts[2].action_num = 400;
|
||||
self.parts[4].action_num = 400;
|
||||
b = false;
|
||||
}
|
||||
}
|
||||
|
||||
if b && *actr2 <= 0 {
|
||||
*actr2 += 0x400;
|
||||
}
|
||||
}
|
||||
400 => {
|
||||
self.parts[0].action_counter += 1;
|
||||
if self.parts[0].action_counter > 100 {
|
||||
self.parts[0].action_counter = 0;
|
||||
self.parts[0].action_num = 401;
|
||||
}
|
||||
}
|
||||
401 => {
|
||||
let actr2: &mut i16 = unsafe { std::mem::transmute(&mut self.parts[0].action_counter2) };
|
||||
let mut b = true;
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
if self.parts[0].action_counter < 100 {
|
||||
*actr2 += 1;
|
||||
} else if self.parts[0].action_counter < 120 {
|
||||
*actr2 += 2;
|
||||
} else if self.parts[0].action_counter < 500 {
|
||||
*actr2 += 2;
|
||||
} else if self.parts[0].action_counter < 540 {
|
||||
*actr2 += 4;
|
||||
} else if self.parts[0].action_counter < 560 {
|
||||
*actr2 += 2;
|
||||
} else {
|
||||
self.parts[0].action_num = 100;
|
||||
self.parts[0].action_counter = 0;
|
||||
self.parts[2].action_num = 100;
|
||||
self.parts[4].action_num = 100;
|
||||
b = false;
|
||||
}
|
||||
|
||||
if b && *actr2 >= 0x400 {
|
||||
*actr2 -= 0x400;
|
||||
}
|
||||
}
|
||||
1000 | 1001 => {
|
||||
if self.parts[0].action_num == 1000 {
|
||||
self.parts[0].action_num = 1001;
|
||||
self.parts[0].action_counter = 0;
|
||||
self.parts[2].action_num = 1000;
|
||||
self.parts[3].action_num = 1000;
|
||||
self.parts[4].action_num = 1000;
|
||||
self.parts[5].action_num = 1000;
|
||||
|
||||
npc_list.create_death_smoke(
|
||||
self.parts[0].x,
|
||||
self.parts[0].y,
|
||||
self.parts[0].display_bounds.right as usize,
|
||||
40,
|
||||
state,
|
||||
&self.parts[0].rng,
|
||||
);
|
||||
}
|
||||
|
||||
self.parts[0].action_counter += 1;
|
||||
if self.parts[0].action_counter > 100 {
|
||||
self.parts[0].action_num = 1010;
|
||||
}
|
||||
|
||||
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(-128..128) * 0x200;
|
||||
npc.y = self.parts[0].y + self.parts[0].rng.range(-70..70) * 0x200;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
}
|
||||
1010 => {
|
||||
let actr2: &mut i16 = unsafe { std::mem::transmute(&mut self.parts[0].action_counter2) };
|
||||
*actr2 += 4;
|
||||
|
||||
if *actr2 >= 0x400 {
|
||||
*actr2 -= 0x400;
|
||||
}
|
||||
|
||||
if self.parts[0].target_x > 8 {
|
||||
self.parts[0].target_x -= 1;
|
||||
}
|
||||
if self.parts[0].target_y > 0 {
|
||||
self.parts[0].target_y -= 1;
|
||||
}
|
||||
if self.parts[0].target_x < -8 {
|
||||
self.parts[0].target_x += 1;
|
||||
}
|
||||
if self.parts[0].target_y < 0 {
|
||||
self.parts[0].target_y += 1;
|
||||
}
|
||||
|
||||
if self.parts[0].target_y == 0 {
|
||||
self.parts[0].action_num = 1020;
|
||||
self.parts[0].action_counter = 0;
|
||||
|
||||
flash.set_cross(self.parts[0].x, self.parts[0].y);
|
||||
state.sound_manager.play_sfx(35);
|
||||
}
|
||||
}
|
||||
1020 => {
|
||||
self.parts[0].action_counter += 1;
|
||||
if self.parts[0].action_counter > 50 {
|
||||
npc_list.kill_npcs_by_type(211, true, state);
|
||||
|
||||
self.parts[0].cond.set_alive(false);
|
||||
self.parts[1].cond.set_alive(false);
|
||||
self.parts[2].cond.set_alive(false);
|
||||
self.parts[3].cond.set_alive(false);
|
||||
self.parts[4].cond.set_alive(false);
|
||||
self.parts[5].cond.set_alive(false);
|
||||
|
||||
self.parts[0].action_num = 0;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.tick_b06_sisters_dragon_head(2, state, &players, npc_list);
|
||||
self.tick_b06_sisters_dragon_body(3, state, &players);
|
||||
self.tick_b06_sisters_dragon_head(4, state, &players, npc_list);
|
||||
self.tick_b06_sisters_dragon_body(5, state, &players);
|
||||
|
||||
self.parts[0].anim_rect = Rect::new(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
fn tick_b06_sisters_dragon_head(
|
||||
&mut self,
|
||||
i: usize,
|
||||
state: &mut SharedGameState,
|
||||
players: &[&mut Player; 2],
|
||||
npc_list: &NPCList,
|
||||
) {
|
||||
let parent = self.parts[i].parent_id as usize;
|
||||
let (base, part) = if let Some(x) = self.parts.get_two_mut(parent, i) {
|
||||
x
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
match part.action_num {
|
||||
0 => {
|
||||
part.action_num = 1;
|
||||
}
|
||||
100 | 200 | 201 => {
|
||||
if part.action_num == 100 {
|
||||
part.action_num = 200;
|
||||
}
|
||||
|
||||
if part.action_num == 200 {
|
||||
part.action_num = 201;
|
||||
part.anim_num = 0;
|
||||
part.hit_bounds.left = 0x2000;
|
||||
part.npc_flags.set_shootable(false);
|
||||
part.action_counter2 = part.rng.range(100..200) as u16;
|
||||
}
|
||||
|
||||
if part.action_counter2 > 0 {
|
||||
part.action_counter2 -= 1;
|
||||
} else {
|
||||
part.action_num = 210;
|
||||
part.action_counter = 0;
|
||||
part.action_counter3 = 0;
|
||||
}
|
||||
}
|
||||
210 => {
|
||||
part.action_counter += 1;
|
||||
if part.action_counter == 3 {
|
||||
part.anim_num = 1;
|
||||
}
|
||||
|
||||
if part.action_counter == 6 {
|
||||
part.anim_num = 2;
|
||||
part.hit_bounds.left = 0x1000;
|
||||
part.npc_flags.set_shootable(true);
|
||||
part.action_counter3 = 0;
|
||||
}
|
||||
|
||||
if part.action_counter > 150 {
|
||||
part.action_num = 220;
|
||||
part.action_counter = 0;
|
||||
}
|
||||
|
||||
if part.shock != 0 {
|
||||
part.action_counter3 += 1;
|
||||
}
|
||||
|
||||
if part.action_counter3 > 10 {
|
||||
part.action_num = 300;
|
||||
part.action_counter = 0;
|
||||
part.anim_num = 3;
|
||||
part.hit_bounds.left = 0x2000;
|
||||
|
||||
state.sound_manager.play_sfx(51);
|
||||
npc_list.remove_by_type(211, state);
|
||||
}
|
||||
}
|
||||
220 => {
|
||||
part.action_counter += 1;
|
||||
if part.action_counter % 8 == 1 {
|
||||
let mut npc = NPC::create(202, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
||||
npc.x = part.x + 0x1000 * part.direction.vector_x();
|
||||
npc.y = part.y;
|
||||
|
||||
let player = part.get_closest_player_ref(players);
|
||||
let angle = f64::atan2((player.y - npc.y) as f64, (player.x - npc.x) as f64)
|
||||
+ (part.rng.range(-6..6) as f64 * CDEG_RAD);
|
||||
|
||||
npc.vel_x = (angle.cos() * 512.0) as i32;
|
||||
npc.vel_y = (angle.sin() * 512.0) as i32;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
|
||||
state.sound_manager.play_sfx(33);
|
||||
}
|
||||
|
||||
if part.action_counter > 50 {
|
||||
part.action_num = 200;
|
||||
}
|
||||
}
|
||||
300 => {
|
||||
part.action_counter += 1;
|
||||
if part.action_counter > 100 {
|
||||
part.action_num = 200;
|
||||
}
|
||||
}
|
||||
400 | 401 => {
|
||||
if part.action_num == 400 {
|
||||
part.action_num = 401;
|
||||
part.action_counter = 0;
|
||||
part.anim_num = 0;
|
||||
part.hit_bounds.left = 0x2000;
|
||||
part.npc_flags.set_shootable(false);
|
||||
}
|
||||
|
||||
part.action_counter += 1;
|
||||
if part.action_counter == 3 {
|
||||
part.anim_num = 1;
|
||||
}
|
||||
|
||||
if part.action_counter == 6 {
|
||||
part.anim_num = 2;
|
||||
part.hit_bounds.left = 0x1000;
|
||||
part.npc_flags.set_shootable(true);
|
||||
part.action_counter3 = 0;
|
||||
}
|
||||
|
||||
if part.action_counter > 20 && part.action_counter % 32 == 1 {
|
||||
let mut npc = NPC::create(202, &state.npc_table);
|
||||
npc.cond.set_alive(true);
|
||||
|
||||
let player = part.get_closest_player_ref(players);
|
||||
let angle = f64::atan2((player.y - npc.y) as f64, (player.x - npc.x) as f64)
|
||||
+ (part.rng.range(-6..6) as f64 * CDEG_RAD);
|
||||
|
||||
npc.x = part.x + 0x1000 * part.direction.vector_x();
|
||||
npc.y = part.y;
|
||||
|
||||
npc.vel_x = (angle.cos() * 512.0) as i32;
|
||||
npc.vel_y = (angle.sin() * 512.0) as i32;
|
||||
|
||||
let _ = npc_list.spawn(0x100, npc);
|
||||
|
||||
state.sound_manager.play_sfx(33);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
part.direction = base.direction;
|
||||
part.x = base.x + 0x800 * part.direction.vector_x();
|
||||
part.y = base.y - 0x1000;
|
||||
|
||||
let dir_offset = if part.direction == Direction::Left { 0 } else { 4 };
|
||||
|
||||
part.anim_rect = state.constants.npc.b06_sisters[part.anim_num as usize + dir_offset];
|
||||
}
|
||||
|
||||
fn tick_b06_sisters_dragon_body(&mut self, i: usize, state: &mut SharedGameState, players: &[&mut Player; 2]) {
|
||||
let parent = self.parts[i].parent_id as usize;
|
||||
let (base, part) = if let Some(x) = self.parts.get_two_mut(parent, i) {
|
||||
x
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
match part.action_num {
|
||||
0 | 10 => {
|
||||
if part.action_num == 0 {
|
||||
part.action_num = 10;
|
||||
let angle =
|
||||
((part.action_counter2) as u8).wrapping_add((base.action_counter2 / 4) as u8) as f64 * CDEG_RAD;
|
||||
part.x += base.x + base.target_x as i32 * (angle.cos() * -512.0) as i32;
|
||||
part.y += base.y + base.target_y as i32 * (angle.sin() * -512.0) as i32;
|
||||
}
|
||||
|
||||
let player = part.get_closest_player_ref(players);
|
||||
part.direction = if part.x > player.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
100 => {
|
||||
let angle =
|
||||
((part.action_counter2) as u8).wrapping_add((base.action_counter2 / 4) as u8) as f64 * CDEG_RAD;
|
||||
part.target_x = base.x + base.target_x as i32 * (angle.cos() * -512.0) as i32;
|
||||
part.target_y = base.y + base.target_y as i32 * (angle.sin() * -512.0) as i32;
|
||||
|
||||
part.x += (part.target_x - part.x) / 8;
|
||||
part.y += (part.target_y - part.y) / 8;
|
||||
|
||||
let player = part.get_closest_player_ref(players);
|
||||
part.direction = if part.x > player.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
1000 | 1001 => {
|
||||
if part.action_num == 1000 {
|
||||
part.action_num = 1001;
|
||||
part.npc_flags.set_shootable(false);
|
||||
}
|
||||
|
||||
let angle =
|
||||
((part.action_counter2) as u8).wrapping_add((base.action_counter2 / 4) as u8) as f64 * CDEG_RAD;
|
||||
part.target_x = base.x + base.target_x as i32 * (angle.cos() * -512.0) as i32;
|
||||
part.target_y = base.y + base.target_y as i32 * (angle.sin() * -512.0) as i32;
|
||||
|
||||
part.x += (part.target_x - part.x) / 8;
|
||||
part.y += (part.target_y - part.y) / 8;
|
||||
|
||||
part.direction = if part.x > base.x { Direction::Left } else { Direction::Right };
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
part.animate(2, 0, 2);
|
||||
|
||||
let dir_offset = if part.direction == Direction::Left { 0 } else { 3 };
|
||||
part.anim_rect = state.constants.npc.b06_sisters[part.anim_num as usize + dir_offset + 8];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
use crate::npc::boss::BossNPC;
|
||||
|
||||
impl BossNPC {
|
||||
pub(crate) fn tick_b06_twins(&mut self) {
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,6 @@ use std::io;
|
|||
use std::io::Cursor;
|
||||
|
||||
use byteorder::{ReadBytesExt, LE};
|
||||
use num_traits::abs;
|
||||
|
||||
use crate::bitfield;
|
||||
use crate::common::Direction;
|
||||
|
|
@ -14,6 +13,7 @@ use crate::entity::GameEntity;
|
|||
use crate::frame::Frame;
|
||||
use crate::framework::context::Context;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::npc::boss::BossNPC;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::physics::PhysicalEntity;
|
||||
use crate::player::Player;
|
||||
|
|
@ -192,7 +192,8 @@ impl NPC {
|
|||
|
||||
if let Some(batch) = state.texture_set.get_or_load_batch(ctx, &state.constants, texture)?.glow() {
|
||||
let off_x =
|
||||
if self.direction == Direction::Left { self.display_bounds.left } else { self.display_bounds.right } as i32;
|
||||
if self.direction == Direction::Left { self.display_bounds.left } else { self.display_bounds.right }
|
||||
as i32;
|
||||
let shock = if self.shock > 0 { (2 * ((self.shock as i32 / 2) % 2) - 1) as f32 } else { 0.0 };
|
||||
|
||||
let (frame_x, frame_y) = frame.xy_interpolated(state.frame_time);
|
||||
|
|
@ -214,16 +215,17 @@ impl NPC {
|
|||
}
|
||||
}
|
||||
|
||||
impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mut Flash)> for NPC {
|
||||
impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mut Flash, &mut BossNPC)> for NPC {
|
||||
fn tick(
|
||||
&mut self,
|
||||
state: &mut SharedGameState,
|
||||
(players, npc_list, stage, bullet_manager, flash): (
|
||||
(players, npc_list, stage, bullet_manager, flash, boss): (
|
||||
[&mut Player; 2],
|
||||
&NPCList,
|
||||
&mut Stage,
|
||||
&mut BulletManager,
|
||||
&mut Flash,
|
||||
&mut BossNPC,
|
||||
),
|
||||
) -> GameResult {
|
||||
#[allow(unused_assignments)]
|
||||
|
|
@ -467,31 +469,96 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mu
|
|||
229 => self.tick_n229_red_flowers_sprouts(state),
|
||||
230 => self.tick_n230_red_flowers_blooming(state),
|
||||
231 => self.tick_n231_rocket(state, players, npc_list),
|
||||
232 => self.tick_n232_orangebell(state, npc_list),
|
||||
233 => self.tick_n233_orangebell_bat(state, players, npc_list),
|
||||
234 => self.tick_n234_red_flowers_picked(state),
|
||||
235 => self.tick_n235_midorin(state),
|
||||
236 => self.tick_n236_gunfish(state, players, npc_list),
|
||||
237 => self.tick_n237_gunfish_projectile(state),
|
||||
238 => self.tick_n238_press_sideways(state, players, npc_list),
|
||||
239 => self.tick_n239_cage_bars(state),
|
||||
240 => self.tick_n240_mimiga_jailed(state),
|
||||
241 => self.tick_n241_critter_red(state, players),
|
||||
242 => self.tick_n242_bat_last_cave(state),
|
||||
243 => self.tick_n243_bat_generator(state, npc_list),
|
||||
244 => self.tick_n244_lava_drop(state, players),
|
||||
245 => self.tick_n245_lava_drop_generator(state, npc_list),
|
||||
246 => self.tick_n246_press_proximity(state, players, npc_list),
|
||||
247 => self.tick_n247_misery_boss(state, players, npc_list),
|
||||
248 => self.tick_n248_misery_boss_vanishing(state),
|
||||
249 => self.tick_n249_misery_boss_energy_shot(state),
|
||||
249 => self.tick_n249_misery_boss_appearing(state),
|
||||
250 => self.tick_n250_misery_boss_lighting_ball(state, players, npc_list),
|
||||
251 => self.tick_n251_misery_boss_lighting(state, npc_list),
|
||||
252 => self.tick_n252_misery_boss_bats(state, players, npc_list),
|
||||
253 => self.tick_n253_experience_capsule(state, npc_list),
|
||||
254 => self.tick_n254_helicopter(state, npc_list),
|
||||
255 => self.tick_n255_helicopter_blades(state, npc_list),
|
||||
256 => self.tick_n256_doctor_facing_away(state, npc_list),
|
||||
257 => self.tick_n257_red_crystal(state),
|
||||
258 => self.tick_n258_mimiga_sleeping(state),
|
||||
259 => self.tick_n259_curly_unconcious(state, players, npc_list),
|
||||
259 => self.tick_n259_curly_unconscious(state, players, npc_list),
|
||||
260 => self.tick_n260_shovel_brigade_caged(state, players, npc_list),
|
||||
261 => self.tick_n261_chie_caged(state, players),
|
||||
262 => self.tick_n262_chaco_caged(state, players),
|
||||
263 => self.tick_n263_doctor_boss(state, players, npc_list),
|
||||
264 => self.tick_n264_doctor_boss_red_projectile(state, npc_list, stage),
|
||||
265 => self.tick_n265_doctor_boss_red_projectile_trail(state),
|
||||
266 => self.tick_n266_doctor_boss_red_projectile_bouncing(state, npc_list),
|
||||
267 => self.tick_n267_muscle_doctor(state, players, npc_list),
|
||||
268 => self.tick_n268_igor_enemy(state, players, npc_list),
|
||||
269 => self.tick_n269_red_bat_bouncing(state),
|
||||
270 => self.tick_n270_doctor_red_energy(state, npc_list),
|
||||
271 => self.tick_n271_ironhead_block(state, stage),
|
||||
272 => self.tick_n272_ironhead_block_generator(state, npc_list),
|
||||
273 => self.tick_n273_droll_projectile(state, npc_list),
|
||||
274 => self.tick_n274_droll(state, players, npc_list),
|
||||
275 => self.tick_n275_puppy_plantation(state, players),
|
||||
276 => self.tick_n276_red_demon(state, players, npc_list),
|
||||
277 => self.tick_n277_red_demon_projectile(state, npc_list),
|
||||
278 => self.tick_n278_little_family(state),
|
||||
279 => self.tick_n279_large_falling_block(state, players, npc_list, stage),
|
||||
280 => self.tick_n280_sue_teleported(state),
|
||||
281 => self.tick_n281_doctor_energy_form(state, npc_list),
|
||||
282 => self.tick_n282_mini_undead_core_active(state, players),
|
||||
283 => self.tick_n283_misery_possessed(state, players, npc_list, stage, boss),
|
||||
284 => self.tick_n284_sue_possessed(state, players, npc_list, stage, boss),
|
||||
285 => self.tick_n285_undead_core_spiral_projectile(state, npc_list, stage),
|
||||
286 => self.tick_n286_undead_core_spiral_projectile_trail(state),
|
||||
287 => self.tick_n287_orange_smoke(state),
|
||||
288 => self.tick_n288_undead_core_exploding_rock(state, players, npc_list, stage),
|
||||
289 => self.tick_n289_critter_orange(state, players, stage),
|
||||
290 => self.tick_n290_bat_misery(state, players, stage),
|
||||
291 => self.tick_n291_mini_undead_core_inactive(state),
|
||||
292 => self.tick_n292_quake(state),
|
||||
293 => self.tick_n293_undead_core_energy_shot(state, npc_list),
|
||||
294 => self.tick_n294_quake_falling_block_generator(state, players, npc_list, stage),
|
||||
295 => self.tick_n295_cloud(state),
|
||||
296 => self.tick_n296_cloud_generator(state, npc_list),
|
||||
297 => self.tick_n297_sue_dragon_mouth(state, npc_list),
|
||||
298 => self.tick_n298_intro_doctor(state),
|
||||
299 => self.tick_n299_intro_balrog_misery(state),
|
||||
300 => self.tick_n300_intro_demon_crown(state),
|
||||
301 => self.tick_n301_misery_fish_missile(state, players),
|
||||
302 => self.tick_n302_camera_focus_marker(state, players, npc_list),
|
||||
304 => self.tick_n304_gaudi_hospital(state),
|
||||
305 => self.tick_n305_small_puppy(state),
|
||||
306 => self.tick_n306_balrog_nurse(state),
|
||||
307 => self.tick_n307_santa_caged(state),
|
||||
308 => self.tick_n308_stumpy(state, players),
|
||||
326 => self.tick_n326_sue_itoh_human_transition(state, npc_list),
|
||||
327 => self.tick_n327_sneeze(state, npc_list),
|
||||
328 => self.tick_n328_human_transform_machine(state),
|
||||
329 => self.tick_n329_laboratory_fan(state),
|
||||
337 => self.tick_n337_numahachi(state),
|
||||
347 => self.tick_n347_hoppy(state, players),
|
||||
349 => self.tick_n349_statue(state),
|
||||
351 => self.tick_n351_statue_shootable(state, npc_list),
|
||||
352 => self.tick_n352_ending_characters(state, npc_list),
|
||||
355 => self.tick_n355_quote_and_curly_on_balrog(state, npc_list),
|
||||
357 => self.tick_n357_puppy_ghost(state),
|
||||
358 => self.tick_n358_misery_credits(state),
|
||||
359 => self.tick_n359_water_droplet_generator(state, players, npc_list),
|
||||
360 => self.tick_n360_credits_thank_you(state),
|
||||
_ => {
|
||||
#[cfg(feature = "hooks")]
|
||||
{
|
||||
|
|
@ -509,11 +576,11 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mu
|
|||
self.shock -= 1;
|
||||
}
|
||||
|
||||
if abs(self.prev_x - self.x) > 0x1000 {
|
||||
if (self.prev_x - self.x).abs() > 0x1000 {
|
||||
self.prev_x = self.x;
|
||||
}
|
||||
|
||||
if abs(self.prev_y - self.y) > 0x1000 {
|
||||
if (self.prev_y - self.y).abs() > 0x1000 {
|
||||
self.prev_y = self.y;
|
||||
}
|
||||
|
||||
|
|
|
|||
128
src/npc/utils.rs
128
src/npc/utils.rs
|
|
@ -1,46 +1,48 @@
|
|||
///! Various utility functions for NPC-related objects
|
||||
|
||||
use num_traits::abs;
|
||||
|
||||
use crate::weapon::bullet::Bullet;
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::{Condition, Direction, Flag, Rect};
|
||||
use crate::map::NPCData;
|
||||
use crate::npc::{NPC, NPCFlag, NPCTable, NPCLayer};
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::player::Player;
|
||||
use crate::rng::{RNG, Xoroshiro32PlusPlus};
|
||||
use crate::shared_game_state::{SharedGameState, TileSize};
|
||||
use crate::components::number_popup::NumberPopup;
|
||||
use crate::map::NPCData;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::{NPCFlag, NPCLayer, NPCTable, NPC};
|
||||
use crate::player::Player;
|
||||
use crate::rng::{Xoroshiro32PlusPlus, RNG};
|
||||
use crate::shared_game_state::{SharedGameState, TileSize};
|
||||
use crate::weapon::bullet::Bullet;
|
||||
|
||||
impl NPC {
|
||||
/// Initializes the RNG. Called when the [NPC] is being added to an [NPCList].
|
||||
pub(crate) fn init_rng(&mut self) {
|
||||
self.rng = Xoroshiro32PlusPlus::new((self.id as u32)
|
||||
.wrapping_sub(self.npc_type as u32)
|
||||
.wrapping_add(self.flag_num as u32)
|
||||
.wrapping_mul(214013)
|
||||
.wrapping_add(2531011) >> 5);
|
||||
self.rng = Xoroshiro32PlusPlus::new(
|
||||
(self.id as u32)
|
||||
.wrapping_sub(self.npc_type as u32)
|
||||
.rotate_right(5)
|
||||
.wrapping_sub(self.flag_num as u32)
|
||||
.rotate_right((self.event_num % 13) as u32)
|
||||
.wrapping_mul(214013)
|
||||
.rotate_right(13)
|
||||
.wrapping_add(2531011)
|
||||
.rotate_right(5),
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a new NPC object with properties that have been populated with data from given NPC data table.
|
||||
pub fn create(npc_type: u16, table: &NPCTable) -> NPC {
|
||||
let display_bounds = table.get_display_bounds(npc_type);
|
||||
let hit_bounds = table.get_hit_bounds(npc_type);
|
||||
let (size, life, damage, flags, exp, spritesheet_id) =
|
||||
match table.get_entry(npc_type) {
|
||||
Some(entry) => {
|
||||
(
|
||||
entry.size,
|
||||
entry.life,
|
||||
entry.damage as u16,
|
||||
entry.npc_flags,
|
||||
entry.experience as u16,
|
||||
entry.spritesheet_id as u16
|
||||
)
|
||||
}
|
||||
None => { (2, 0, 0, NPCFlag(0), 0, 0) }
|
||||
};
|
||||
let (size, life, damage, flags, exp, spritesheet_id) = match table.get_entry(npc_type) {
|
||||
Some(entry) => (
|
||||
entry.size,
|
||||
entry.life,
|
||||
entry.damage as u16,
|
||||
entry.npc_flags,
|
||||
entry.experience as u16,
|
||||
entry.spritesheet_id as u16,
|
||||
),
|
||||
None => (2, 0, 0, NPCFlag(0), 0, 0),
|
||||
};
|
||||
let npc_flags = NPCFlag(flags.0);
|
||||
|
||||
NPC {
|
||||
|
|
@ -161,19 +163,16 @@ impl NPC {
|
|||
|
||||
/// Returns true if the [NPC] collides with a [Bullet].
|
||||
pub fn collides_with_bullet(&self, bullet: &Bullet) -> bool {
|
||||
(
|
||||
self.npc_flags.shootable()
|
||||
&& (self.x - self.hit_bounds.right as i32) < (bullet.x + bullet.enemy_hit_width as i32)
|
||||
&& (self.x + self.hit_bounds.right as i32) > (bullet.x - bullet.enemy_hit_width as i32)
|
||||
&& (self.y - self.hit_bounds.top as i32) < (bullet.y + bullet.enemy_hit_height as i32)
|
||||
&& (self.y + self.hit_bounds.bottom as i32) > (bullet.y - bullet.enemy_hit_height as i32)
|
||||
) || (
|
||||
self.npc_flags.invulnerable()
|
||||
(self.npc_flags.shootable()
|
||||
&& (self.x - self.hit_bounds.right as i32) < (bullet.x + bullet.enemy_hit_width as i32)
|
||||
&& (self.x + self.hit_bounds.right as i32) > (bullet.x - bullet.enemy_hit_width as i32)
|
||||
&& (self.y - self.hit_bounds.top as i32) < (bullet.y + bullet.enemy_hit_height as i32)
|
||||
&& (self.y + self.hit_bounds.bottom as i32) > (bullet.y - bullet.enemy_hit_height as i32))
|
||||
|| (self.npc_flags.invulnerable()
|
||||
&& (self.x - self.hit_bounds.right as i32) < (bullet.x + bullet.hit_bounds.right as i32)
|
||||
&& (self.x + self.hit_bounds.right as i32) > (bullet.x - bullet.hit_bounds.left as i32)
|
||||
&& (self.y - self.hit_bounds.top as i32) < (bullet.y + bullet.hit_bounds.bottom as i32)
|
||||
&& (self.y + self.hit_bounds.bottom as i32) > (bullet.y - bullet.hit_bounds.top as i32)
|
||||
)
|
||||
&& (self.y + self.hit_bounds.bottom as i32) > (bullet.y - bullet.hit_bounds.top as i32))
|
||||
}
|
||||
|
||||
/// Creates experience drop for this NPC.
|
||||
|
|
@ -247,9 +246,15 @@ impl NPCList {
|
|||
}
|
||||
|
||||
match npc.size {
|
||||
1 => { self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 3, state, &npc.rng); }
|
||||
2 => { self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 7, state, &npc.rng); }
|
||||
3 => { self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 12, state, &npc.rng); }
|
||||
1 => {
|
||||
self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 3, state, &npc.rng);
|
||||
}
|
||||
2 => {
|
||||
self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 7, state, &npc.rng);
|
||||
}
|
||||
3 => {
|
||||
self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 12, state, &npc.rng);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
|
@ -264,9 +269,15 @@ impl NPCList {
|
|||
}
|
||||
|
||||
match npc.size {
|
||||
1 => { self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 3, state, &npc.rng); }
|
||||
2 => { self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 7, state, &npc.rng); }
|
||||
3 => { self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 12, state, &npc.rng); }
|
||||
1 => {
|
||||
self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 3, state, &npc.rng);
|
||||
}
|
||||
2 => {
|
||||
self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 7, state, &npc.rng);
|
||||
}
|
||||
3 => {
|
||||
self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 12, state, &npc.rng);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
|
|
@ -344,18 +355,43 @@ impl NPCList {
|
|||
|
||||
/// Creates NPC death smoke diffusing in random directions.
|
||||
#[inline]
|
||||
pub fn create_death_smoke(&self, x: i32, y: i32, radius: usize, amount: usize, state: &mut SharedGameState, rng: &dyn RNG) {
|
||||
pub fn create_death_smoke(
|
||||
&self,
|
||||
x: i32,
|
||||
y: i32,
|
||||
radius: usize,
|
||||
amount: usize,
|
||||
state: &mut SharedGameState,
|
||||
rng: &dyn RNG,
|
||||
) {
|
||||
self.create_death_smoke_common(x, y, radius, amount, Direction::Left, state, rng)
|
||||
}
|
||||
|
||||
/// Creates NPC death smoke diffusing upwards.
|
||||
#[inline]
|
||||
pub fn create_death_smoke_up(&self, x: i32, y: i32, radius: usize, amount: usize, state: &mut SharedGameState, rng: &dyn RNG) {
|
||||
pub fn create_death_smoke_up(
|
||||
&self,
|
||||
x: i32,
|
||||
y: i32,
|
||||
radius: usize,
|
||||
amount: usize,
|
||||
state: &mut SharedGameState,
|
||||
rng: &dyn RNG,
|
||||
) {
|
||||
self.create_death_smoke_common(x, y, radius, amount, Direction::Up, state, rng)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_death_smoke_common(&self, x: i32, y: i32, radius: usize, amount: usize, direction: Direction, state: &mut SharedGameState, rng: &dyn RNG) {
|
||||
fn create_death_smoke_common(
|
||||
&self,
|
||||
x: i32,
|
||||
y: i32,
|
||||
radius: usize,
|
||||
amount: usize,
|
||||
direction: Direction,
|
||||
state: &mut SharedGameState,
|
||||
rng: &dyn RNG,
|
||||
) {
|
||||
let radius = (radius / 0x200) as i32;
|
||||
|
||||
let mut npc = NPC::create(4, &state.npc_table);
|
||||
|
|
|
|||
|
|
@ -472,8 +472,8 @@ impl Player {
|
|||
state.constants.player.air_physics.max_move
|
||||
};
|
||||
|
||||
self.vel_x = clamp(self.vel_x, -max_move, max_move);
|
||||
self.vel_y = clamp(self.vel_y, -max_move, max_move);
|
||||
self.vel_x = self.vel_x.clamp(-max_move, max_move);
|
||||
self.vel_y = self.vel_y.clamp(-max_move, max_move);
|
||||
|
||||
if !self.splash && self.flags.in_water() {
|
||||
let vertical_splash = !self.flags.hit_bottom_wall() && self.vel_y > 0x200;
|
||||
|
|
@ -551,8 +551,100 @@ impl Player {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn tick_ironhead(&mut self, _state: &mut SharedGameState) -> GameResult {
|
||||
// todo ironhead boss controls
|
||||
fn tick_ironhead(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
self.up = false;
|
||||
self.down = false;
|
||||
|
||||
if state.control_flags.control_enabled() {
|
||||
if self.controller.move_left() || self.controller.move_right() {
|
||||
if self.controller.move_left() {
|
||||
self.vel_x -= 0x100;
|
||||
}
|
||||
if self.controller.move_right() {
|
||||
self.vel_x += 0x100;
|
||||
}
|
||||
} else if self.vel_x > 0x7f || self.vel_x < -0x7f {
|
||||
self.vel_x += 0x80 * -self.vel_x.signum();
|
||||
} else {
|
||||
self.vel_x = 0;
|
||||
}
|
||||
|
||||
if self.controller.move_up() || self.controller.move_down() {
|
||||
if self.controller.move_up() {
|
||||
self.vel_y -= 0x100;
|
||||
}
|
||||
if self.controller.move_down() {
|
||||
self.vel_y += 0x100;
|
||||
}
|
||||
} else if self.vel_y > 0x7f || self.vel_y < -0x7f {
|
||||
self.vel_y += 0x80 * -self.vel_y.signum();
|
||||
} else {
|
||||
self.vel_y = 0;
|
||||
}
|
||||
} else {
|
||||
if self.vel_x > 0x7f || self.vel_x < -0x7f {
|
||||
self.vel_x += 0x80 * -self.vel_x.signum();
|
||||
} else {
|
||||
self.vel_x = 0;
|
||||
}
|
||||
|
||||
if self.vel_y > 0x7f || self.vel_y < -0x7f {
|
||||
self.vel_y += 0x80 * -self.vel_y.signum();
|
||||
} else {
|
||||
self.vel_y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if self.vel_y < -0x200 && self.flags.hit_top_wall() {
|
||||
state.create_caret(self.x, self.y - self.hit_bounds.top as i32, CaretType::LittleParticles, Direction::FacingPlayer);
|
||||
}
|
||||
|
||||
if self.vel_y > 0x200 && self.flags.hit_bottom_wall() {
|
||||
state.create_caret(self.x, self.y + self.hit_bounds.bottom as i32, CaretType::LittleParticles, Direction::FacingPlayer);
|
||||
}
|
||||
|
||||
self.vel_x = self.vel_x.clamp(-0x400, 0x400);
|
||||
self.vel_y = self.vel_y.clamp(-0x400, 0x400);
|
||||
|
||||
if self.controller.move_left() && self.controller.move_up() {
|
||||
if self.vel_x < -0x30c {
|
||||
self.vel_x = -0x30c;
|
||||
}
|
||||
if self.vel_y < -0x30c {
|
||||
self.vel_y = -0x30c;
|
||||
}
|
||||
}
|
||||
|
||||
if self.controller.move_right() && self.controller.move_up() {
|
||||
if self.vel_x > 0x30c {
|
||||
self.vel_x = 0x30c;
|
||||
}
|
||||
if self.vel_y < -0x30c {
|
||||
self.vel_y = -0x30c;
|
||||
}
|
||||
}
|
||||
|
||||
if self.controller.move_left() && self.controller.move_down() {
|
||||
if self.vel_x < -0x30c {
|
||||
self.vel_x = -0x30c;
|
||||
}
|
||||
if self.vel_y > 0x30c {
|
||||
self.vel_y = 0x30c;
|
||||
}
|
||||
}
|
||||
|
||||
if self.controller.move_right() && self.controller.move_down() {
|
||||
if self.vel_x > 0x30c {
|
||||
self.vel_x = 0x30c;
|
||||
}
|
||||
if self.vel_y > 0x30c {
|
||||
self.vel_y = 0x30c;
|
||||
}
|
||||
}
|
||||
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::common::{interpolate_fix9_scale, Color, Direction, FadeDirection, Fad
|
|||
use crate::components::boss_life_bar::BossLifeBar;
|
||||
use crate::components::credits::Credits;
|
||||
use crate::components::draw_common::Alignment;
|
||||
use crate::components::falling_island::FallingIsland;
|
||||
use crate::components::flash::Flash;
|
||||
use crate::components::hud::HUD;
|
||||
use crate::components::inventory::InventoryUI;
|
||||
|
|
@ -32,9 +33,11 @@ use crate::rng::XorShift;
|
|||
use crate::scene::title_scene::TitleScene;
|
||||
use crate::scene::Scene;
|
||||
use crate::scripting::tsc::credit_script::CreditScriptVM;
|
||||
use crate::scripting::tsc::text_script::{
|
||||
ConfirmSelection, ScriptMode, TextScriptExecutionState, TextScriptLine, TextScriptVM,
|
||||
};
|
||||
use crate::shared_game_state::{SharedGameState, TileSize};
|
||||
use crate::stage::{BackgroundType, Stage};
|
||||
use crate::scripting::tsc::text_script::{ConfirmSelection, ScriptMode, TextScriptExecutionState, TextScriptLine, TextScriptVM};
|
||||
use crate::texture_set::SpriteBatch;
|
||||
use crate::weapon::bullet::BulletManager;
|
||||
use crate::weapon::{Weapon, WeaponType};
|
||||
|
|
@ -48,6 +51,7 @@ pub struct GameScene {
|
|||
pub stage_select: StageSelect,
|
||||
pub flash: Flash,
|
||||
pub credits: Credits,
|
||||
pub falling_island: FallingIsland,
|
||||
pub inventory_ui: InventoryUI,
|
||||
pub hud_player1: HUD,
|
||||
pub hud_player2: HUD,
|
||||
|
|
@ -136,6 +140,7 @@ impl GameScene {
|
|||
stage_select: StageSelect::new(),
|
||||
flash: Flash::new(),
|
||||
credits: Credits::new(),
|
||||
falling_island: FallingIsland::new(),
|
||||
inventory_ui: InventoryUI::new(),
|
||||
hud_player1: HUD::new(Alignment::Left),
|
||||
hud_player2: HUD::new(Alignment::Right),
|
||||
|
|
@ -398,7 +403,11 @@ impl GameScene {
|
|||
|
||||
for y in 0..(state.canvas_size.1 as i32 / 16 + 1) {
|
||||
if direction == FadeDirection::Left {
|
||||
batch.add_rect(state.canvas_size.0 - x as f32 * 16.0 - 16.0, y as f32 * 16.0, &rect);
|
||||
batch.add_rect(
|
||||
state.canvas_size.0 - x as f32 * 16.0 - 16.0,
|
||||
y as f32 * 16.0,
|
||||
&rect,
|
||||
);
|
||||
} else {
|
||||
batch.add_rect(x as f32 * 16.0, y as f32 * 16.0, &rect);
|
||||
}
|
||||
|
|
@ -813,14 +822,14 @@ impl GameScene {
|
|||
for npc in self.npc_list.iter_alive() {
|
||||
if npc.x < (self.frame.x - 128 * 0x200 - npc.display_bounds.width() as i32 * 0x200)
|
||||
|| npc.x
|
||||
> (self.frame.x
|
||||
+ 128 * 0x200
|
||||
+ (state.canvas_size.0 as i32 + npc.display_bounds.width() as i32) * 0x200)
|
||||
&& npc.y < (self.frame.y - 128 * 0x200 - npc.display_bounds.height() as i32 * 0x200)
|
||||
> (self.frame.x
|
||||
+ 128 * 0x200
|
||||
+ (state.canvas_size.0 as i32 + npc.display_bounds.width() as i32) * 0x200)
|
||||
&& npc.y < (self.frame.y - 128 * 0x200 - npc.display_bounds.height() as i32 * 0x200)
|
||||
|| npc.y
|
||||
> (self.frame.y
|
||||
+ 128 * 0x200
|
||||
+ (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200)
|
||||
> (self.frame.y
|
||||
+ 128 * 0x200
|
||||
+ (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1209,6 +1218,27 @@ impl GameScene {
|
|||
(10, 50, 255),
|
||||
batch,
|
||||
),
|
||||
270 => self.draw_light(
|
||||
interpolate_fix9_scale(npc.prev_x - self.frame.prev_x, npc.x - self.frame.x, state.frame_time),
|
||||
interpolate_fix9_scale(npc.prev_y - self.frame.prev_y, npc.y - self.frame.y, state.frame_time),
|
||||
0.4,
|
||||
(192, 0, 0),
|
||||
batch,
|
||||
),
|
||||
285 | 287 => self.draw_light(
|
||||
interpolate_fix9_scale(npc.prev_x - self.frame.prev_x, npc.x - self.frame.x, state.frame_time),
|
||||
interpolate_fix9_scale(npc.prev_y - self.frame.prev_y, npc.y - self.frame.y, state.frame_time),
|
||||
1.0,
|
||||
(150, 90, 0),
|
||||
batch,
|
||||
),
|
||||
293 => self.draw_light(
|
||||
interpolate_fix9_scale(npc.prev_x - self.frame.prev_x, npc.x - self.frame.x, state.frame_time),
|
||||
interpolate_fix9_scale(npc.prev_y - self.frame.prev_y, npc.y - self.frame.y, state.frame_time),
|
||||
4.0,
|
||||
(255, 255, 255),
|
||||
batch,
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -1227,7 +1257,6 @@ impl GameScene {
|
|||
canvas.add(SpriteBatchCommand::DrawRect(rect, rect));
|
||||
canvas.draw()?;
|
||||
|
||||
|
||||
graphics::set_render_target(ctx, Some(canvas))?;
|
||||
graphics::draw_rect(
|
||||
ctx,
|
||||
|
|
@ -1620,6 +1649,7 @@ impl GameScene {
|
|||
&mut self.stage,
|
||||
&mut self.bullet_manager,
|
||||
&mut self.flash,
|
||||
&mut self.boss,
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
|
@ -1950,6 +1980,7 @@ impl Scene for GameScene {
|
|||
| TextScriptExecutionState::WaitTicks(_, _, _)
|
||||
| TextScriptExecutionState::WaitInput(_, _, _)
|
||||
| TextScriptExecutionState::Msg(_, _, _, _)
|
||||
| TextScriptExecutionState::FallingIsland(_, _, _, _, _, _)
|
||||
if !state.control_flags.control_enabled() && !state.textscript_vm.flags.cutscene_skip() =>
|
||||
{
|
||||
if self.player1.controller.inventory() {
|
||||
|
|
@ -1966,61 +1997,59 @@ impl Scene for GameScene {
|
|||
}
|
||||
}
|
||||
|
||||
let mut ticks = 1;
|
||||
if state.textscript_vm.mode == ScriptMode::Map && state.textscript_vm.flags.cutscene_skip() {
|
||||
ticks = 4;
|
||||
}
|
||||
match state.textscript_vm.mode {
|
||||
ScriptMode::Map => {
|
||||
TextScriptVM::run(state, self, ctx)?;
|
||||
|
||||
for _ in 0..ticks {
|
||||
match state.textscript_vm.mode {
|
||||
ScriptMode::Map => {
|
||||
TextScriptVM::run(state, self, ctx)?;
|
||||
|
||||
if state.control_flags.tick_world() {
|
||||
self.tick_world(state)?;
|
||||
match state.textscript_vm.state {
|
||||
TextScriptExecutionState::FallingIsland(_, _, _, _, _, _) => (),
|
||||
_ => {
|
||||
if state.control_flags.tick_world() {
|
||||
self.tick_world(state)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
ScriptMode::StageSelect => {
|
||||
self.stage_select.tick(state, (ctx, &self.player1, &self.player2))?;
|
||||
|
||||
TextScriptVM::run(state, self, ctx)?;
|
||||
}
|
||||
ScriptMode::Inventory => {
|
||||
self.inventory_ui.tick(state, (ctx, &mut self.player1, &mut self.inventory_player1))?;
|
||||
|
||||
TextScriptVM::run(state, self, ctx)?;
|
||||
}
|
||||
}
|
||||
ScriptMode::StageSelect => {
|
||||
self.stage_select.tick(state, (ctx, &self.player1, &self.player2))?;
|
||||
|
||||
if state.control_flags.credits_running() {
|
||||
self.skip_counter = 0;
|
||||
CreditScriptVM::run(state, ctx)?;
|
||||
TextScriptVM::run(state, self, ctx)?;
|
||||
}
|
||||
ScriptMode::Inventory => {
|
||||
self.inventory_ui.tick(state, (ctx, &mut self.player1, &mut self.inventory_player1))?;
|
||||
|
||||
match state.fade_state {
|
||||
FadeState::FadeOut(tick, direction) if tick < 15 => {
|
||||
state.fade_state = FadeState::FadeOut(tick + 1, direction);
|
||||
}
|
||||
FadeState::FadeOut(tick, _) if tick == 15 => {
|
||||
state.fade_state = FadeState::Hidden;
|
||||
}
|
||||
FadeState::FadeIn(tick, direction) if tick > -15 => {
|
||||
state.fade_state = FadeState::FadeIn(tick - 1, direction);
|
||||
}
|
||||
FadeState::FadeIn(tick, _) if tick == -15 => {
|
||||
state.fade_state = FadeState::Visible;
|
||||
}
|
||||
_ => {}
|
||||
TextScriptVM::run(state, self, ctx)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.flash.tick(state, ())?;
|
||||
if state.control_flags.credits_running() {
|
||||
self.skip_counter = 0;
|
||||
CreditScriptVM::run(state, ctx)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "scripting-lua")]
|
||||
state.lua.scene_tick();
|
||||
|
||||
if state.control_flags.tick_world() {
|
||||
self.tick = self.tick.wrapping_add(1);
|
||||
match state.fade_state {
|
||||
FadeState::FadeOut(tick, direction) if tick < 15 => {
|
||||
state.fade_state = FadeState::FadeOut(tick + 1, direction);
|
||||
}
|
||||
FadeState::FadeOut(tick, _) if tick == 15 => {
|
||||
state.fade_state = FadeState::Hidden;
|
||||
}
|
||||
FadeState::FadeIn(tick, direction) if tick > -15 => {
|
||||
state.fade_state = FadeState::FadeIn(tick - 1, direction);
|
||||
}
|
||||
FadeState::FadeIn(tick, _) if tick == -15 => {
|
||||
state.fade_state = FadeState::Visible;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.flash.tick(state, ())?;
|
||||
|
||||
#[cfg(feature = "scripting-lua")]
|
||||
state.lua.scene_tick();
|
||||
|
||||
if state.control_flags.tick_world() {
|
||||
self.tick = self.tick.wrapping_add(1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -2105,7 +2134,10 @@ impl Scene for GameScene {
|
|||
self.player1.popup.draw(state, ctx, &self.frame)?;
|
||||
self.player2.popup.draw(state, ctx, &self.frame)?;
|
||||
|
||||
if state.settings.shader_effects && self.lighting_mode == LightingMode::Ambient {
|
||||
if !state.control_flags.credits_running()
|
||||
&& state.settings.shader_effects
|
||||
&& self.lighting_mode == LightingMode::Ambient
|
||||
{
|
||||
self.draw_light_map(state, ctx)?;
|
||||
}
|
||||
self.flash.draw(state, ctx, &self.frame)?;
|
||||
|
|
@ -2206,6 +2238,7 @@ impl Scene for GameScene {
|
|||
self.credits.draw(state, ctx, &self.frame)?;
|
||||
}
|
||||
|
||||
self.falling_island.draw(state, ctx, &self.frame)?;
|
||||
self.draw_text_boxes(state, ctx)?;
|
||||
|
||||
if self.skip_counter > 0 {
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ pub enum TextScriptExecutionState {
|
|||
WaitStanding(u16, u32),
|
||||
WaitConfirmation(u16, u32, u16, u8, ConfirmSelection),
|
||||
WaitFade(u16, u32),
|
||||
FallingIsland(u16, u32, i32, i32, u16, bool),
|
||||
SaveProfile(u16, u32),
|
||||
LoadProfile,
|
||||
Reset,
|
||||
|
|
@ -517,7 +518,31 @@ impl TextScriptVM {
|
|||
}
|
||||
break;
|
||||
}
|
||||
TextScriptExecutionState::FallingIsland(event, ip, pos_x, mut pos_y, mut tick, mode) => {
|
||||
if tick == 900 {
|
||||
state.textscript_vm.state = TextScriptExecutionState::Running(event, ip);
|
||||
break;
|
||||
}
|
||||
|
||||
tick += 1;
|
||||
|
||||
if mode {
|
||||
if tick < 350 {
|
||||
pos_y += 0x33;
|
||||
} else if tick < 500 {
|
||||
pos_y += 0x19;
|
||||
} else if tick < 600 {
|
||||
pos_y += 0xC;
|
||||
} else if tick == 750 {
|
||||
tick = 900;
|
||||
}
|
||||
} else {
|
||||
pos_y += 0x33;
|
||||
}
|
||||
|
||||
state.textscript_vm.state = TextScriptExecutionState::FallingIsland(event, ip, pos_x, pos_y, tick, mode);
|
||||
break;
|
||||
}
|
||||
TextScriptExecutionState::SaveProfile(event, ip) => {
|
||||
state.save_game(game_scene, ctx)?;
|
||||
state.textscript_vm.state = TextScriptExecutionState::Running(event, ip);
|
||||
|
|
@ -1326,6 +1351,7 @@ impl TextScriptVM {
|
|||
&mut game_scene.stage,
|
||||
&mut game_scene.bullet_manager,
|
||||
&mut game_scene.flash,
|
||||
&mut game_scene.boss,
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
|
@ -1561,22 +1587,45 @@ impl TextScriptVM {
|
|||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
TSCOpCode::CPS => {
|
||||
state.sound_manager.stop_sfx(58);
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
TSCOpCode::SPS => {
|
||||
state.sound_manager.loop_sfx(58);
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
TSCOpCode::CSS => {
|
||||
state.sound_manager.stop_sfx(40);
|
||||
state.sound_manager.stop_sfx(41);
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
TSCOpCode::SSS => {
|
||||
let _freq = read_cur_varint(&mut cursor)?;
|
||||
|
||||
// todo change freq
|
||||
state.sound_manager.loop_sfx(40);
|
||||
state.sound_manager.loop_sfx(41);
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
TSCOpCode::XX1 => {
|
||||
let mode = read_cur_varint(&mut cursor)?;
|
||||
|
||||
exec_state = TextScriptExecutionState::FallingIsland(event, cursor.position() as u32, 0x15000, 0x8000, 0, mode != 0);
|
||||
}
|
||||
// unimplemented opcodes
|
||||
// Zero operands
|
||||
TSCOpCode::CPS
|
||||
| TSCOpCode::KE2
|
||||
| TSCOpCode::CSS
|
||||
| TSCOpCode::MLP
|
||||
| TSCOpCode::SPS
|
||||
| TSCOpCode::FR2
|
||||
| TSCOpCode::STC
|
||||
| TSCOpCode::HM2 => {
|
||||
TSCOpCode::KE2 | TSCOpCode::MLP | TSCOpCode::FR2 | TSCOpCode::STC | TSCOpCode::HM2 => {
|
||||
log::warn!("unimplemented opcode: {:?}", op);
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
// One operand codes
|
||||
TSCOpCode::UNJ | TSCOpCode::XX1 | TSCOpCode::SSS | TSCOpCode::ACH => {
|
||||
TSCOpCode::UNJ | TSCOpCode::ACH => {
|
||||
let par_a = read_cur_varint(&mut cursor)?;
|
||||
|
||||
log::warn!("unimplemented opcode: {:?} {}", op, par_a);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ impl Weapon {
|
|||
bullet_manager: &mut BulletManager,
|
||||
state: &mut SharedGameState,
|
||||
) {
|
||||
const BULLETS: [u16; 6] = [44, 45, 46, 47, 48, 49];
|
||||
const BULLETS: [u16; 6] = [37, 38, 39, 40, 41, 42];
|
||||
|
||||
let mut shoot = false;
|
||||
let btype;
|
||||
|
|
@ -22,7 +22,7 @@ impl Weapon {
|
|||
self.add_xp(if player.equip.has_turbocharge() { 3 } else { 2 }, player, state);
|
||||
self.counter1 += 1;
|
||||
|
||||
if (self.counter1 / 2 % 2) != 0 {
|
||||
if self.counter1 & 2 != 0 {
|
||||
match self.level {
|
||||
WeaponLevel::Level1 => {
|
||||
state.sound_manager.play_sfx(59);
|
||||
|
|
|
|||
Loading…
Reference in a new issue