From 035b2c284878e93b82f938d8f049a1b6d1c0d51b Mon Sep 17 00:00:00 2001 From: Alula Date: Sun, 6 Dec 2020 16:48:43 +0100 Subject: [PATCH 1/6] Do not process NPC collissions if player isn't alive --- src/player/player_hit.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/player/player_hit.rs b/src/player/player_hit.rs index 6569eec..af3185f 100644 --- a/src/player/player_hit.rs +++ b/src/player/player_hit.rs @@ -318,6 +318,10 @@ impl Player { } pub fn tick_npc_collisions(&mut self, id: TargetPlayer, state: &mut SharedGameState, npc_map: &mut NPCMap, inventory: &mut Inventory) { + if !self.cond.alive() { + return; + } + for npc_cell in npc_map.npcs.values() { let mut npc = npc_cell.borrow_mut(); if !npc.cond.alive() { continue; } From ad4fc4b4810e6e9c1ea5f2d00b08a1d4834b3933 Mon Sep 17 00:00:00 2001 From: Alula Date: Sun, 6 Dec 2020 20:31:36 +0100 Subject: [PATCH 2/6] recolor the water --- src/builtin/shaders/water_150.frag.glsl | 8 +++++--- src/scene/game_scene.rs | 5 +++-- src/shaders.rs | 2 ++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/builtin/shaders/water_150.frag.glsl b/src/builtin/shaders/water_150.frag.glsl index e5eb233..bb1bf09 100644 --- a/src/builtin/shaders/water_150.frag.glsl +++ b/src/builtin/shaders/water_150.frag.glsl @@ -11,16 +11,18 @@ layout (std140) uniform Globals { layout (std140) uniform WaterShaderParams { vec2 u_Resolution; + vec2 u_FramePos; float u_Tick; }; void main() { vec2 wave = v_Uv; - wave.x += sin(v_Uv.x * 40.0 + u_Tick / 20.0) * (sin(u_Tick / 10.0) * 0.01); - wave.y -= cos(v_Uv.y * 20.0 + u_Tick / 5.0) * (sin(u_Tick / 20.0) * 0.01); + wave.x += sin((-u_FramePos.y / u_Resolution.y + v_Uv.x * 16.0) + u_Tick / 20.0) * 2.0 / u_Resolution.x; + wave.y -= cos((-u_FramePos.x / u_Resolution.x + v_Uv.y * 16.0) + u_Tick / 5.0) * 2.0 / u_Resolution.y; float off = 0.4 / u_Resolution.y; vec4 color = texture(t_Texture, wave); color.r = texture(t_Texture, wave + off).r; color.b = texture(t_Texture, wave - off).b; - Target0 = vec4(0.7, 0.8, 1.2, 1.0) * color * v_Color; + + Target0 = (vec4(0.4, 0.6, 0.8, 1.0) * 0.3) + (color * v_Color * 0.7); } diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index 84ecfc6..2d38371 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -649,8 +649,11 @@ impl GameScene { } fn draw_water(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { + let (frame_x, frame_y) = self.frame.xy_interpolated(state.frame_time, state.scale); + { state.shaders.water_shader_params.resolution = [state.canvas_size.0, state.canvas_size.1]; + state.shaders.water_shader_params.frame_pos = [frame_x, frame_y]; state.shaders.water_shader_params.t = self.tick as f32; let _lock = graphics::use_shader(ctx, &state.shaders.water_shader); state.shaders.water_shader.send(ctx, state.shaders.water_shader_params)?; @@ -666,8 +669,6 @@ impl GameScene { // cheap, clones a reference underneath let mut tmp_batch = SpriteBatch::new(state.tmp_canvas.image().clone()); - let (frame_x, frame_y) = self.frame.xy_interpolated(state.frame_time, state.scale); - let tile_start_x = clamp(self.frame.x / 0x200 / 16, 0, self.stage.map.width as isize) as usize; let tile_start_y = clamp(self.frame.y / 0x200 / 16, 0, self.stage.map.height as isize) as usize; let tile_end_x = clamp((self.frame.x / 0x200 + 8 + state.canvas_size.0 as isize) / 16 + 1, 0, self.stage.map.width as isize) as usize; diff --git a/src/shaders.rs b/src/shaders.rs index 291571f..9256fca 100644 --- a/src/shaders.rs +++ b/src/shaders.rs @@ -5,6 +5,7 @@ use ggez::{Context, GameResult}; gfx_defines! { constant WaterShaderParams { resolution: [f32; 2] = "u_Resolution", + frame_pos: [f32; 2] = "u_FramePos", t: f32 = "u_Tick", } } @@ -19,6 +20,7 @@ impl Shaders { let water_shader_params = WaterShaderParams { t: 0.0, resolution: [0.0, 0.0], + frame_pos: [0.0, 0.0], }; Ok(Shaders { From bafad448680200b436968426b382e50835dd7465 Mon Sep 17 00:00:00 2001 From: Alula Date: Sun, 6 Dec 2020 21:20:26 +0100 Subject: [PATCH 3/6] Bind RNG per-NPC, not global game state --- src/npc/balrog.rs | 58 ++++++++-------- src/npc/booster.rs | 20 ++++++ src/npc/boss/balfrog.rs | 60 ++++++++-------- src/npc/chaco.rs | 2 +- src/npc/characters.rs | 8 +-- src/npc/egg_corridor.rs | 2 +- src/npc/first_cave.rs | 2 +- src/npc/grasstown.rs | 142 +++++++++----------------------------- src/npc/igor.rs | 34 ++++----- src/npc/mimiga_village.rs | 12 ++-- src/npc/misc.rs | 98 +++++++++++++------------- src/npc/misery.rs | 10 +-- src/npc/mod.rs | 17 ++++- src/npc/npc_utils.rs | 33 ++++----- src/npc/pickups.rs | 20 +++--- src/npc/quote.rs | 8 +-- src/npc/santa.rs | 2 +- src/npc/sue.rs | 6 +- src/npc/toroko.rs | 2 +- src/rng.rs | 50 ++++++++++---- 20 files changed, 285 insertions(+), 301 deletions(-) create mode 100644 src/npc/booster.rs diff --git a/src/npc/balrog.rs b/src/npc/balrog.rs index 675a654..2c0800c 100644 --- a/src/npc/balrog.rs +++ b/src/npc/balrog.rs @@ -35,10 +35,10 @@ impl NPC { for _ in 0..3 { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -104,7 +104,7 @@ impl NPC { npc.y = self.y + 4 * 0x200; // 4.0fix9 let mut angle = ((self.y + 4 * 0x200 - player.y) as f64 / (self.x - player.y) as f64).atan(); - angle += state.game_rng.range(-16..16) as f64 * std::f64::consts::FRAC_PI_8; + angle += self.rng.range(-16..16) as f64 * std::f64::consts::FRAC_PI_8; npc.vel_x = (angle.cos() * 512.0) as isize; // 1.0fix9 npc.vel_y = (angle.sin() * 512.0) as isize; @@ -221,7 +221,7 @@ impl NPC { self.anim_num = 0; } - if state.game_rng.range(0..100) == 0 { + if self.rng.range(0..100) == 0 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; @@ -294,10 +294,10 @@ impl NPC { for _ in 0..3 { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -516,14 +516,14 @@ impl NPC { _ => {} } - if self.target_x != 0 && state.game_rng.range(0..10) == 0 { + if self.target_x != 0 && self.rng.range(0..10) == 0 { let mut npc = NPCMap::create_npc(4, &state.npc_table); npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -557,10 +557,10 @@ impl NPC { for _ in 0..16 { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -595,7 +595,7 @@ impl NPC { } } 3 => { - if state.game_rng.range(0..100) == 0 { + if self.rng.range(0..100) == 0 { self.action_num = 4; self.action_counter = 0; self.anim_num = 1; @@ -681,7 +681,7 @@ impl NPC { self.action_counter = 0; let angle = f64::atan2((self.y - player.y) as f64, (self.x - player.x) as f64) - + state.game_rng.range(-16..16) as f64 * CDEG_RAD; + + self.rng.range(-16..16) as f64 * CDEG_RAD; let mut npc = NPCMap::create_npc(11, &state.npc_table); npc.cond.set_alive(true); @@ -765,17 +765,17 @@ impl NPC { for _ in 0..8 { npc_smoke.cond.set_alive(true); npc_smoke.direction = Direction::Left; - npc_smoke.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc_smoke.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc_smoke.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc_smoke.vel_y = state.game_rng.range(-0x600..0) as isize; + npc_smoke.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc_smoke.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc_smoke.vel_x = self.rng.range(-0x155..0x155) as isize; + npc_smoke.vel_y = self.rng.range(-0x600..0) as isize; npc_proj.cond.set_alive(true); npc_proj.direction = Direction::Left; - npc_proj.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc_proj.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc_proj.vel_x = state.game_rng.range(-0x400..0x400) as isize; - npc_proj.vel_y = state.game_rng.range(-0x400..0) as isize; + npc_proj.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc_proj.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc_proj.vel_x = self.rng.range(-0x400..0x400) as isize; + npc_proj.vel_y = self.rng.range(-0x400..0) as isize; state.new_npcs.push(npc_smoke); state.new_npcs.push(npc_proj); diff --git a/src/npc/booster.rs b/src/npc/booster.rs new file mode 100644 index 0000000..986f476 --- /dev/null +++ b/src/npc/booster.rs @@ -0,0 +1,20 @@ +use ggez::GameResult; + +use crate::npc::NPC; +use crate::shared_game_state::SharedGameState; + +impl NPC { + pub(crate) fn tick_n113_professor_booster(&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; + } + } + _ => {} + } + Ok(()) + } +} diff --git a/src/npc/boss/balfrog.rs b/src/npc/boss/balfrog.rs index b58faf2..cfcc605 100644 --- a/src/npc/boss/balfrog.rs +++ b/src/npc/boss/balfrog.rs @@ -77,10 +77,10 @@ impl BossNPC { for _ in 0..8 { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.parts[0].x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.parts[0].y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as isize * 0x200; + npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize; + npc.vel_y = self.parts[0].rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -164,8 +164,8 @@ impl BossNPC { let mut npc = NPCMap::create_npc(110, &state.npc_table); npc.cond.set_alive(true); - npc.x = state.game_rng.range(4..16) as isize * 16 * 0x200; - npc.y = state.game_rng.range(0..4) as isize * 16 * 0x200; + npc.x = self.parts[0].rng.range(4..16) as isize * 16 * 0x200; + npc.y = self.parts[0].rng.range(0..4) as isize * 16 * 0x200; npc.direction = Direction::FacingPlayer; state.new_npcs.push(npc); @@ -175,10 +175,10 @@ impl BossNPC { for _ in 0..4 { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.parts[0].x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.parts[0].y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as isize * 0x200; + npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize; + npc.vel_y = self.parts[0].rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -239,7 +239,7 @@ impl BossNPC { let py = self.parts[0].y - 8 * 0x200 - player.y; let deg = f64::atan2(py as f64, px as f64) - + state.game_rng.range(-16..16) as f64 * CDEG_RAD; + + self.parts[0].rng.range(-16..16) as f64 * CDEG_RAD; // todo rand let mut npc = NPCMap::create_npc(108, &state.npc_table); @@ -322,8 +322,8 @@ impl BossNPC { let mut npc = NPCMap::create_npc(104, &state.npc_table); for _ in 0..2 { npc.cond.set_alive(true); - npc.x = state.game_rng.range(4..16) as isize * 16 * 0x200; - npc.y = state.game_rng.range(0..4) as isize * 16 * 0x200; + npc.x = self.parts[0].rng.range(4..16) as isize * 16 * 0x200; + npc.y = self.parts[0].rng.range(0..4) as isize * 16 * 0x200; npc.direction = Direction::FacingPlayer; state.new_npcs.push(npc); @@ -332,8 +332,8 @@ impl BossNPC { let mut npc = NPCMap::create_npc(110, &state.npc_table); for _ in 0..6 { npc.cond.set_alive(true); - npc.x = state.game_rng.range(4..16) as isize * 16 * 0x200; - npc.y = state.game_rng.range(0..4) as isize * 16 * 0x200; + npc.x = self.parts[0].rng.range(4..16) as isize * 16 * 0x200; + npc.y = self.parts[0].rng.range(0..4) as isize * 16 * 0x200; npc.direction = Direction::FacingPlayer; state.new_npcs.push(npc); @@ -342,10 +342,10 @@ impl BossNPC { let mut npc = NPCMap::create_npc(4, &state.npc_table); for _ in 0..8 { npc.cond.set_alive(true); - npc.x = self.parts[0].x + state.game_rng.range(-12..12) as isize * 0x200; + npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as isize * 0x200; npc.y = self.parts[0].y + self.parts[0].hit_bounds.bottom as isize; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize; + npc.vel_y = self.parts[0].rng.range(-0x600..0) as isize; npc.direction = Direction::Left; state.new_npcs.push(npc); @@ -380,10 +380,10 @@ impl BossNPC { let mut npc = NPCMap::create_npc(4, &state.npc_table); for _ in 0..8 { npc.cond.set_alive(true); - npc.x = self.parts[0].x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.parts[0].y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as isize * 0x200; + npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize; + npc.vel_y = self.parts[0].rng.range(-0x600..0) as isize; npc.direction = Direction::Left; state.new_npcs.push(npc); } @@ -393,10 +393,10 @@ impl BossNPC { if (self.parts[0].action_counter % 5) == 0 { let mut npc = NPCMap::create_npc(4, &state.npc_table); npc.cond.set_alive(true); - npc.x = self.parts[0].x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.parts[0].y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as isize * 0x200; + npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize; + npc.vel_y = self.parts[0].rng.range(-0x600..0) as isize; npc.direction = Direction::Left; state.new_npcs.push(npc); } @@ -435,10 +435,10 @@ impl BossNPC { if (self.parts[0].action_counter % 9) == 0 { let mut npc = NPCMap::create_npc(4, &state.npc_table); npc.cond.set_alive(true); - npc.x = self.parts[0].x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.parts[0].y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as isize * 0x200; + npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize; + npc.vel_y = self.parts[0].rng.range(-0x600..0) as isize; npc.direction = Direction::Left; state.new_npcs.push(npc); } diff --git a/src/npc/chaco.rs b/src/npc/chaco.rs index 21505ec..ffc4978 100644 --- a/src/npc/chaco.rs +++ b/src/npc/chaco.rs @@ -16,7 +16,7 @@ impl NPC { self.anim_counter = 0; } - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; diff --git a/src/npc/characters.rs b/src/npc/characters.rs index d517057..113d5f7 100644 --- a/src/npc/characters.rs +++ b/src/npc/characters.rs @@ -96,7 +96,7 @@ impl NPC { self.vel_x = 0; } - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; @@ -235,14 +235,14 @@ impl NPC { self.anim_rect = state.constants.npc.n062_kazuma_computer[self.anim_num as usize]; } - if state.game_rng.range(0..80) == 1 { + if self.rng.range(0..80) == 1 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; self.anim_rect = state.constants.npc.n062_kazuma_computer[self.anim_num as usize]; } - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 3; self.action_counter = 0; self.anim_num = 2; @@ -282,7 +282,7 @@ impl NPC { self.vel_x = 0; } - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; diff --git a/src/npc/egg_corridor.rs b/src/npc/egg_corridor.rs index 3828b28..2b5582e 100644 --- a/src/npc/egg_corridor.rs +++ b/src/npc/egg_corridor.rs @@ -619,7 +619,7 @@ impl NPC { self.action_counter2 += 1; if (self.action_counter2 % 8) == 0 && abs(self.x - player.x) < 160 * 0x200 { let angle = ((self.y - player.y) as f64 / (self.x - player.x) as f64).atan() - + (state.game_rng.range(-6..6) as u8) as f64 * CDEG_RAD; + + (self.rng.range(-6..6) as u8) as f64 * CDEG_RAD; let mut npc = NPCMap::create_npc(84, &state.npc_table); npc.cond.set_alive(true); diff --git a/src/npc/first_cave.rs b/src/npc/first_cave.rs index 9c19b5c..771611f 100644 --- a/src/npc/first_cave.rs +++ b/src/npc/first_cave.rs @@ -189,7 +189,7 @@ impl NPC { self.target_y = self.y; self.action_num = 1; - self.action_counter = state.game_rng.range(0..50) as u16; + self.action_counter = self.rng.range(0..50) as u16; } self.action_counter += 1; diff --git a/src/npc/grasstown.rs b/src/npc/grasstown.rs index dafd1f0..6c449a7 100644 --- a/src/npc/grasstown.rs +++ b/src/npc/grasstown.rs @@ -98,14 +98,7 @@ impl NPC { state.sound_manager.play_sfx(110); } - self.anim_counter += 1; - if self.anim_counter > 0 { - self.anim_counter = 0; - self.anim_num += 1; - if self.anim_num > 5 { - self.anim_num = 3; - } - } + self.animate(0, 3, 5); } } 5 => { @@ -148,11 +141,11 @@ impl NPC { match self.action_num { 0 | 1 => { if self.action_num == 0 { - let angle = state.game_rng.range(0..0xff); + let angle = self.rng.range(0..0xff); self.vel_x = ((angle as f64 * 1.40625).cos() * 512.0) as isize; self.target_x = self.x + ((angle as f64 * 1.40625 + std::f64::consts::FRAC_2_PI).cos() * 8.0 * 512.0) as isize; - let angle = state.game_rng.range(0..0xff); + let angle = self.rng.range(0..0xff); self.vel_y = ((angle as f64 * 1.40625).sin() * 512.0) as isize; self.target_y = self.y + ((angle as f64 * 1.40625 + std::f64::consts::FRAC_2_PI).sin() * 8.0 * 512.0) as isize; @@ -203,14 +196,7 @@ impl NPC { if self.action_num == 3 { self.anim_num = 3; } else { - self.anim_counter += 1; - if self.anim_counter > 1 { - self.anim_counter = 0; - self.anim_num += 1; - if self.anim_num > 2 { - self.anim_num = 0; - } - } + self.animate(1, 0, 2); } let dir_offset = if self.direction == Direction::Left { 0 } else { 4 }; @@ -312,14 +298,7 @@ impl NPC { self.vel_y = -0x200; } - self.anim_counter += 1; - if self.anim_counter > 0 { - self.anim_counter = 0; - self.anim_num += 1; - if self.anim_num > 5 { - self.anim_num = 3; - } - } + self.animate(0, 3, 5); } } 5 => { @@ -364,7 +343,7 @@ impl NPC { self.action_num = 1; } - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; @@ -413,15 +392,7 @@ impl NPC { } } 5 => { - self.anim_counter += 1; - if self.anim_counter > 1 { - self.anim_counter = 0; - self.anim_num += 1; - if self.anim_num > 4 { - self.anim_num = 2; - } - } - + self.animate(1, 2, 4); self.direction = if player.x < self.x { Direction::Left } else { Direction::Right }; self.vel_x += (player.x - self.x).signum() * 0x10; @@ -620,7 +591,7 @@ impl NPC { 0 | 1 | 10 => { if self.action_num == 0 { self.action_num = 1; - self.action_counter = state.game_rng.range(0..50) as u16; + self.action_counter = self.rng.range(0..50) as u16; self.target_x = self.x; self.target_y = self.y; @@ -721,15 +692,7 @@ impl NPC { } pub(crate) fn tick_n101_malco_screen(&mut self, state: &mut SharedGameState) -> GameResult { - self.anim_counter += 1; - if self.anim_counter > 3 { - self.anim_counter = 0; - self.anim_num += 1; - if self.anim_num > 2 { - self.anim_num = 0; - } - } - + self.animate(3, 0, 2); self.anim_rect = state.constants.npc.n101_malco_screen[self.anim_num as usize]; Ok(()) @@ -741,15 +704,7 @@ impl NPC { self.y += 8 * 0x200; } - self.anim_counter += 1; - if self.anim_counter > 0 { - self.anim_counter = 0; - self.anim_num += 1; - if self.anim_num > 3 { - self.anim_num = 0; - } - } - + self.animate(0, 0, 3); self.anim_rect = state.constants.npc.n102_malco_computer_wave[self.anim_num as usize]; Ok(()) @@ -762,14 +717,7 @@ impl NPC { self.vel_x += self.direction.vector_x() * 0x20; - self.anim_counter += 1; - if self.anim_counter > 0 { - self.anim_counter = 0; - self.anim_num += 1; - if self.anim_num > 2 { - self.anim_num = 0; - } - } + self.animate(0, 0, 2); self.x += self.vel_x; @@ -800,7 +748,7 @@ impl NPC { self.vel_y = 0; if self.tsc_direction == 4 { - self.direction = if (state.game_rng.next() & 1) == 0 { Direction::Left } else { Direction::Right }; + self.direction = if (self.rng.next_u16() & 1) == 0 { Direction::Left } else { Direction::Right }; self.tsc_direction = self.direction as u16; self.action_num = 3; self.anim_num = 2; @@ -811,7 +759,7 @@ impl NPC { self.action_counter += 1; - if state.game_rng.range(0..50) == 1 { + if self.rng.range(0..50) == 1 { self.action_num = 2; self.action_counter = 0; self.anim_num = 0; @@ -821,7 +769,7 @@ impl NPC { 1 => { self.action_counter += 1; - if state.game_rng.range(0..50) == 1 { + if self.rng.range(0..50) == 1 { self.action_num = 2; self.action_counter = 0; self.anim_num = 0; @@ -830,15 +778,7 @@ impl NPC { } 2 => { self.action_counter += 1; - self.anim_counter += 1; - - if self.anim_counter > 2 { - self.anim_counter = 0; - self.anim_num += 1; - if self.anim_num > 1 { - self.anim_num = 0; - } - } + self.animate(2, 0, 1); if self.action_counter > 18 { self.action_num = 1; @@ -884,7 +824,7 @@ impl NPC { && ((self.shock > 0) || (abs(self.x - player.x) < 160 * 0x200 && abs(self.y - player.y) < 64 * 0x200) - && state.game_rng.range(0..50) == 2) { + && self.rng.range(0..50) == 2) { self.direction = if self.x >= player.x { Direction::Left } else { @@ -931,8 +871,8 @@ impl NPC { npc.cond.set_alive(true); npc.x = self.x; npc.y = self.y; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -996,8 +936,8 @@ impl NPC { npc.cond.set_alive(true); npc.x = self.x; npc.y = self.y; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -1036,8 +976,8 @@ impl NPC { npc.cond.set_alive(true); npc.x = self.x; npc.y = self.y; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -1059,14 +999,7 @@ impl NPC { self.anim_counter = 0; } - self.anim_counter += 1; - if self.anim_counter > 4 { - self.anim_counter = 0; - self.anim_num += 1; - if self.anim_num > 9 { - self.anim_num = 6; - } - } + self.animate(4, 6, 9); } 110 => { self.cond.set_drs_destroyed(true); @@ -1094,7 +1027,7 @@ impl NPC { self.action_counter = 0; } - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; @@ -1125,8 +1058,8 @@ impl NPC { npc.direction = Direction::Left; npc.x = self.x; npc.y = self.y; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -1159,7 +1092,7 @@ impl NPC { self.vel_y = 0; if self.tsc_direction == 4 { - self.direction = if (state.game_rng.next() & 1) == 0 { Direction::Left } else { Direction::Right }; + self.direction = if (self.rng.next_u16() & 1) == 0 { Direction::Left } else { Direction::Right }; self.tsc_direction = self.direction as u16; self.action_num = 3; self.anim_num = 2; @@ -1170,7 +1103,7 @@ impl NPC { self.action_counter += 1; - if state.game_rng.range(0..50) == 1 { + if self.rng.range(0..50) == 1 { self.action_num = 2; self.action_counter = 0; self.anim_num = 0; @@ -1180,7 +1113,7 @@ impl NPC { 1 => { self.action_counter += 1; - if state.game_rng.range(0..50) == 1 { + if self.rng.range(0..50) == 1 { self.action_num = 2; self.action_counter = 0; self.anim_num = 0; @@ -1189,15 +1122,8 @@ impl NPC { } 2 => { self.action_counter += 1; - self.anim_counter += 1; - if self.anim_counter > 2 { - self.anim_counter = 0; - self.anim_num += 1; - if self.anim_num > 1 { - self.anim_num = 0; - } - } + self.animate(2, 0, 1); if self.action_counter > 18 { self.action_num = 1; @@ -1243,7 +1169,7 @@ impl NPC { && ((self.shock > 0) || (abs(self.x - player.x) < 160 * 0x200 && abs(self.y - player.y) < 64 * 0x200) - && state.game_rng.range(0..50) == 2) { + && self.rng.range(0..50) == 2) { self.direction = if self.x >= player.x { Direction::Left } else { @@ -1297,8 +1223,8 @@ impl NPC { self.target_y = self.y; } - self.x = self.target_x + state.game_rng.range(-1..1) as isize * 0x200; - self.y = self.target_y + state.game_rng.range(-1..1) as isize * 0x200; + self.x = self.target_x + self.rng.range(-1..1) as isize * 0x200; + self.y = self.target_y + self.rng.range(-1..1) as isize * 0x200; self.action_counter += 1; if self.action_counter > 30 { @@ -1318,7 +1244,7 @@ impl NPC { self.vel_x += 0x20; self.x += self.vel_x; - self.y = self.target_y + state.game_rng.range(-1..1) as isize * 0x200; + self.y = self.target_y + self.rng.range(-1..1) as isize * 0x200; self.action_counter += 1; if self.action_counter > 10 { diff --git a/src/npc/igor.rs b/src/npc/igor.rs index 5ca1780..fd88776 100644 --- a/src/npc/igor.rs +++ b/src/npc/igor.rs @@ -203,10 +203,10 @@ impl NPC { for _ in 0..4 { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -233,7 +233,7 @@ impl NPC { self.action_counter += 1; if self.action_counter > 100 && self.action_counter % 6 == 1 { - let deg = (if self.direction == Direction::Left { 0x88 } else { 0xf8 } + state.game_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 isize; let vel_y = (deg.sin() * 1536.0) as isize; @@ -289,10 +289,10 @@ impl NPC { for _ in 0..8 { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -309,10 +309,10 @@ impl NPC { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -349,10 +349,10 @@ impl NPC { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } diff --git a/src/npc/mimiga_village.rs b/src/npc/mimiga_village.rs index ba5d859..3bc7e9c 100644 --- a/src/npc/mimiga_village.rs +++ b/src/npc/mimiga_village.rs @@ -23,7 +23,7 @@ impl NPC { self.anim_rect = state.constants.npc.n069_pignon[self.anim_num as usize + dir_offset]; } - if state.game_rng.range(0..100) == 1 { + if self.rng.range(0..100) == 1 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; @@ -32,7 +32,7 @@ impl NPC { } - if state.game_rng.range(0..150) == 1 { + if self.rng.range(0..150) == 1 { self.action_num = 3; self.action_counter = 50; self.anim_num = 0; @@ -185,7 +185,7 @@ impl NPC { match self.action_num { 1 => { - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; @@ -219,7 +219,7 @@ impl NPC { } 2 => { self.anim_num = 0; - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 3; self.action_counter = 0; self.anim_num = 1; @@ -374,7 +374,7 @@ impl NPC { self.anim_rect = state.constants.npc.n081_giant_pignon[self.anim_num as usize + dir_offset]; } - if state.game_rng.range(0..100) == 1 { + if self.rng.range(0..100) == 1 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; @@ -383,7 +383,7 @@ impl NPC { } - if state.game_rng.range(0..150) == 1 { + if self.rng.range(0..150) == 1 { self.action_num = 3; self.action_counter = 50; self.anim_num = 0; diff --git a/src/npc/misc.rs b/src/npc/misc.rs index 6efd3fb..2697b46 100644 --- a/src/npc/misc.rs +++ b/src/npc/misc.rs @@ -48,13 +48,13 @@ impl NPC { pub(crate) fn tick_n004_smoke(&mut self, state: &mut SharedGameState) -> GameResult { if self.action_num == 0 { self.action_num = 1; - self.anim_num = state.game_rng.range(0..4) as u16; - self.anim_counter = state.game_rng.range(0..3) as u16; + self.anim_num = self.rng.range(0..4) as u16; + self.anim_counter = self.rng.range(0..3) as u16; if self.direction == Direction::Left || self.direction == Direction::Up { - let angle = state.game_rng.range(0..31415) as f32 / 5000.0; - self.vel_x = (angle.cos() * state.game_rng.range(0x200..0x5ff) as f32) as isize; - self.vel_y = (angle.sin() * state.game_rng.range(0x200..0x5ff) as f32) as isize; + let angle = self.rng.range(0..31415) as f32 / 5000.0; + self.vel_x = (angle.cos() * self.rng.range(0x200..0x5ff) as f32) as isize; + self.vel_y = (angle.sin() * self.rng.range(0x200..0x5ff) as f32) as isize; } } else { self.vel_x = (self.vel_x * 20) / 21; @@ -147,10 +147,10 @@ impl NPC { for _ in 0..4 { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -160,7 +160,7 @@ impl NPC { } self.anim_num = 0; - if state.game_rng.range(0..30) == 0 { + if self.rng.range(0..30) == 0 { self.action_num = 2; } } @@ -228,7 +228,7 @@ impl NPC { match self.action_num { 1 => { - let rand = state.game_rng.range(0..30); + let rand = self.rng.range(0..30); if rand < 10 { self.action_num = 2; @@ -238,7 +238,7 @@ impl NPC { self.action_num = 4; } - self.action_counter = state.game_rng.range(0x10..0x40) as u16; + self.action_counter = self.rng.range(0x10..0x40) as u16; self.anim_counter = 0; } 2 => { @@ -308,8 +308,8 @@ impl NPC { npc.direction = Direction::Left; npc.x = self.x; npc.y = self.y; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -406,7 +406,7 @@ impl NPC { self.action_num = 1; self.anim_counter = 0; - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 2; self.action_counter = 8; self.anim_rect = state.constants.npc.n030_hermit_gunsmith[1]; @@ -572,13 +572,13 @@ impl NPC { droplet.direction = Direction::Left; droplet.x = self.x; droplet.y = self.y; - droplet.vel_x = 2 * state.game_rng.range(-0x200..0x200) as isize; - droplet.vel_y = 3 * state.game_rng.range(-0x200..0x80) as isize; + droplet.vel_x = 2 * self.rng.range(-0x200..0x200) as isize; + droplet.vel_y = 3 * self.rng.range(-0x200..0x80) as isize; state.new_npcs.push(droplet); if self.action_counter % 2 == 0 { - droplet.vel_x = 2 * state.game_rng.range(-0x200..0x200) as isize; - droplet.vel_y = 3 * state.game_rng.range(-0x200..0x80) as isize; + droplet.vel_x = 2 * self.rng.range(-0x200..0x200) as isize; + droplet.vel_y = 3 * self.rng.range(-0x200..0x80) as isize; state.new_npcs.push(droplet); } } @@ -590,7 +590,7 @@ impl NPC { pub(crate) fn tick_n073_water_droplet(&mut self, state: &mut SharedGameState, stage: &Stage) -> GameResult { self.vel_y += 0x20; - self.anim_rect = state.constants.npc.n073_water_droplet[state.game_rng.range(0..4) as usize]; + self.anim_rect = state.constants.npc.n073_water_droplet[self.rng.range(0..4) as usize]; if self.vel_y > 0x5ff { self.vel_y = 0x5ff; @@ -694,12 +694,12 @@ 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 - && state.game_rng.range(0..5) == 1 { + && self.rng.range(0..5) == 1 { let mut particle = NPCMap::create_npc(199, &state.npc_table); particle.cond.set_alive(true); particle.direction = Direction::Left; particle.x = self.x; - particle.y = self.y + (state.game_rng.range(-8..8) * 0x200) as isize; + particle.y = self.y + (self.rng.range(-8..8) * 0x200) as isize; state.new_npcs.push(particle); } } @@ -747,11 +747,11 @@ 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 - && state.game_rng.range(0..5) == 1 { + && self.rng.range(0..5) == 1 { let mut particle = NPCMap::create_npc(199, &state.npc_table); particle.cond.set_alive(true); particle.direction = Direction::Up; - particle.x = self.x + (state.game_rng.range(-8..8) * 0x200) as isize; + particle.x = self.x + (self.rng.range(-8..8) * 0x200) as isize; particle.y = self.y; state.new_npcs.push(particle); } @@ -799,12 +799,12 @@ 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 - && state.game_rng.range(0..5) == 1 { + && self.rng.range(0..5) == 1 { let mut particle = NPCMap::create_npc(199, &state.npc_table); particle.cond.set_alive(true); particle.direction = Direction::Right; particle.x = self.x; - particle.y = self.y + (state.game_rng.range(-8..8) * 0x200) as isize; + particle.y = self.y + (self.rng.range(-8..8) * 0x200) as isize; state.new_npcs.push(particle); } } @@ -848,11 +848,11 @@ 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 - && state.game_rng.range(0..5) == 1 { + && self.rng.range(0..5) == 1 { let mut particle = NPCMap::create_npc(199, &state.npc_table); particle.cond.set_alive(true); particle.direction = Direction::Bottom; - particle.x = self.x + (state.game_rng.range(-8..8) * 0x200) as isize; + particle.x = self.x + (self.rng.range(-8..8) * 0x200) as isize; particle.y = self.y; state.new_npcs.push(particle); } @@ -944,8 +944,8 @@ impl NPC { npc.cond.set_alive(true); npc.x = self.x; npc.y = self.y; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -1014,10 +1014,10 @@ impl NPC { for _ in 0..3 { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -1063,10 +1063,10 @@ impl NPC { for _ in 0..3 { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -1135,10 +1135,10 @@ impl NPC { for _ in 0..3 { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -1184,10 +1184,10 @@ impl NPC { for _ in 0..3 { npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200; - npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.x = self.x + self.rng.range(-12..12) as isize * 0x200; + npc.y = self.y + self.rng.range(-12..12) as isize * 0x200; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -1232,9 +1232,9 @@ impl NPC { pub(crate) fn tick_n199_wind_particles(&mut self, state: &mut SharedGameState) -> GameResult { if self.action_num == 0 { self.action_num = 1; - self.anim_num = state.game_rng.range(0..2) as u16; - self.vel_x = self.direction.vector_x() * (state.game_rng.range(4..8) * 0x200 / 2) as isize; - self.vel_y = self.direction.vector_y() * (state.game_rng.range(4..8) * 0x200 / 2) as isize; + self.anim_num = self.rng.range(0..2) as u16; + self.vel_x = self.direction.vector_x() * (self.rng.range(4..8) * 0x200 / 2) as isize; + self.vel_y = self.direction.vector_y() * (self.rng.range(4..8) * 0x200 / 2) as isize; } self.anim_counter += 1; diff --git a/src/npc/misery.rs b/src/npc/misery.rs index 0a016fc..f3e9982 100644 --- a/src/npc/misery.rs +++ b/src/npc/misery.rs @@ -104,7 +104,7 @@ impl NPC { state.sound_manager.play_sfx(29); } - self.x = self.target_x + state.game_rng.range(-1..1) as isize * 0x200; + self.x = self.target_x + self.rng.range(-1..1) as isize * 0x200; self.action_counter += 1; if self.action_counter >= 32 { @@ -240,7 +240,7 @@ impl NPC { self.anim_num = 2; } - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 2; self.action_counter = 0; self.anim_num = 3; @@ -354,7 +354,7 @@ impl NPC { npc.x = self.x + 8 * 0x200; npc.y = self.y - 8 * 0x200; npc.vel_x = 0x600; - npc.vel_y = state.game_rng.range(-0x200..0) as isize; + npc.vel_y = self.rng.range(-0x200..0) as isize; npc.cond.set_alive(true); state.new_npcs.push(npc); @@ -376,7 +376,7 @@ impl NPC { self.anim_counter -= 1; self.anim_num = 1; } else { - if state.game_rng.range(0..100) == 1 { + if self.rng.range(0..100) == 1 { self.anim_counter = 30; } @@ -391,7 +391,7 @@ impl NPC { self.action_counter -= 1; self.anim_num = 3; } else { - if state.game_rng.range(0..100) == 1 { + if self.rng.range(0..100) == 1 { self.anim_counter = 30; } diff --git a/src/npc/mod.rs b/src/npc/mod.rs index e1d5d24..3828309 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -2,7 +2,6 @@ use std::cell::RefCell; use std::collections::{BTreeMap, HashSet}; use std::io; use std::io::Cursor; -use std::ops::DerefMut; use bitvec::vec::BitVec; use byteorder::{LE, ReadBytesExt}; @@ -20,11 +19,13 @@ use crate::map::NPCData; use crate::npc::boss::BossNPC; use crate::physics::PhysicalEntity; use crate::player::Player; +use crate::rng::Xoroshiro32PlusPlus; use crate::shared_game_state::SharedGameState; use crate::stage::Stage; use crate::str; pub mod balrog; +pub mod booster; pub mod boss; pub mod chaco; pub mod characters; @@ -112,6 +113,7 @@ pub struct NPC { pub action_counter2: u16, pub anim_counter: u16, pub anim_rect: Rect, + pub rng: Xoroshiro32PlusPlus, } static PARTICLE_NPCS: [u16; 11] = [1, 4, 11, 73, 84, 86, 87, 108, 129, 199, 355]; @@ -160,6 +162,7 @@ impl NPC { action_counter2: 0, anim_counter: 0, anim_rect: Rect { left: 0, top: 0, right: 0, bottom: 0 }, + rng: Xoroshiro32PlusPlus::new(0), } } } @@ -472,6 +475,11 @@ impl NPCMap { action_counter2: 0, anim_counter: 0, anim_rect: Rect::new(0, 0, 0, 0), + rng: Xoroshiro32PlusPlus::new((data.id as u32) + .wrapping_sub(data.npc_type as u32) + .wrapping_add(data.flag_num as u32) + .wrapping_mul(214013) + .wrapping_add(2531011) >> 5), }; let cell = RefCell::new(npc); @@ -524,6 +532,7 @@ impl NPCMap { action_counter2: 0, anim_counter: 0, anim_rect: Rect::new(0, 0, 0, 0), + rng: Xoroshiro32PlusPlus::new(0), } } @@ -700,6 +709,12 @@ impl NPCMap { }; } + npc.rng = Xoroshiro32PlusPlus::new((npc.id as u32) + .wrapping_sub(npc.npc_type as u32) + .wrapping_add(npc.flag_num as u32) + .wrapping_mul(214013) + .wrapping_add(2531011) >> 5); + self.ids.insert(id); self.npcs.insert(id, RefCell::new(*npc)); } diff --git a/src/npc/npc_utils.rs b/src/npc/npc_utils.rs index 83b003a..ec9f665 100644 --- a/src/npc/npc_utils.rs +++ b/src/npc/npc_utils.rs @@ -4,6 +4,18 @@ use crate::npc::NPC; use crate::player::Player; impl NPC { + pub fn animate(&mut self, ticks_between_frames: u16, start_frame: u16, end_frame: u16) { + self.anim_counter += 1; + if self.anim_counter > ticks_between_frames { + self.anim_counter = 0; + self.anim_num += 1; + if self.anim_num > end_frame { + self.anim_num = start_frame; + } + } + } + + /// Returns index of player that's closest to the current NPC. pub fn get_closest_player_idx_mut<'a>(&self, players: &[&'a mut Player; 2]) -> usize { let mut max_dist = f64::MAX; let mut player_idx = 0; @@ -26,25 +38,10 @@ impl NPC { player_idx } + /// Returns a reference to closest player. pub fn get_closest_player_mut<'a>(&self, players: [&'a mut Player; 2]) -> &'a mut Player { - let mut max_dist = f64::MAX; - let mut player_idx = 0; + let idx = self.get_closest_player_idx_mut(&players); - for (idx, player) in players.iter().enumerate() { - if !player.cond.alive() || player.cond.hidden() { - continue; - } - - let dist_x = abs(self.x - player.x) as f64; - let dist_y = abs(self.y - player.y) as f64; - let dist = (dist_x * dist_x + dist_y * dist_y).sqrt(); - - if dist < max_dist { - max_dist = dist; - player_idx = idx; - } - } - - players[player_idx] + players[idx] } } diff --git a/src/npc/pickups.rs b/src/npc/pickups.rs index 829db18..82e4960 100644 --- a/src/npc/pickups.rs +++ b/src/npc/pickups.rs @@ -11,8 +11,8 @@ impl NPC { if self.action_num == 0 { self.action_num = 1; - self.vel_x = state.game_rng.range(-0x80..0x80) as isize; - self.vel_y = state.game_rng.range(-0x7f..0x100) as isize; + self.vel_x = self.rng.range(-0x80..0x80) as isize; + self.vel_y = self.rng.range(-0x7f..0x100) as isize; } self.vel_x -= 0x8; @@ -40,12 +40,12 @@ impl NPC { } else { if self.action_num == 0 { self.action_num = 1; - self.anim_num = state.game_rng.range(0..4) as u16; + self.anim_num = self.rng.range(0..4) as u16; - self.vel_x = state.game_rng.range(-0x200..0x200) as isize; - self.vel_y = state.game_rng.range(-0x400..0) as isize; + self.vel_x = self.rng.range(-0x200..0x200) as isize; + self.vel_y = self.rng.range(-0x400..0) as isize; - self.direction = if state.game_rng.range(0..1) != 0 { + self.direction = if self.rng.range(0..1) != 0 { Direction::Left } else { Direction::Right @@ -161,8 +161,8 @@ impl NPC { if state.control_flags.wind() { if self.action_num == 0 { self.action_num = 1; - self.vel_x = state.game_rng.range(0x7f..0x100) as isize; - self.vel_y = state.game_rng.range(-0x20..0x20) as isize; + self.vel_x = self.rng.range(0x7f..0x100) as isize; + self.vel_y = self.rng.range(-0x20..0x20) as isize; } self.vel_x -= 0x08; @@ -229,8 +229,8 @@ impl NPC { if state.control_flags.wind() { if self.action_num == 0 { self.action_num = 1; - self.vel_x = state.game_rng.range(0x7f..0x100) as isize; - self.vel_y = state.game_rng.range(-0x20..0x20) as isize; + self.vel_x = self.rng.range(0x7f..0x100) as isize; + self.vel_y = self.rng.range(-0x20..0x20) as isize; } self.vel_x -= 0x08; diff --git a/src/npc/quote.rs b/src/npc/quote.rs index 7df1ff9..550e777 100644 --- a/src/npc/quote.rs +++ b/src/npc/quote.rs @@ -164,8 +164,8 @@ impl NPC { npc.direction = Direction::Left; npc.x = self.x; npc.y = self.y; - npc.vel_x = state.game_rng.range(-0x155..0x155) as isize; - npc.vel_y = state.game_rng.range(-0x600..0) as isize; + npc.vel_x = self.rng.range(-0x155..0x155) as isize; + npc.vel_y = self.rng.range(-0x600..0) as isize; state.new_npcs.push(npc); } @@ -213,8 +213,8 @@ impl NPC { } self.target_y += 0x100; - self.x = self.target_x + state.game_rng.range(-1..1) as isize * 0x200; - self.y = self.target_y + state.game_rng.range(-1..1) as isize * 0x200; + self.x = self.target_x + self.rng.range(-1..1) as isize * 0x200; + self.y = self.target_y + self.rng.range(-1..1) as isize * 0x200; } 70 | 71 => { if self.action_num == 70 { diff --git a/src/npc/santa.rs b/src/npc/santa.rs index 4a795df..f3edbd4 100644 --- a/src/npc/santa.rs +++ b/src/npc/santa.rs @@ -16,7 +16,7 @@ impl NPC { self.anim_counter = 0; } - if state.game_rng.range(0..120) == 0 { + if self.rng.range(0..120) == 0 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; diff --git a/src/npc/sue.rs b/src/npc/sue.rs index c053e1b..f637dc8 100644 --- a/src/npc/sue.rs +++ b/src/npc/sue.rs @@ -20,7 +20,7 @@ impl NPC { self.vel_x = 0; } - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; @@ -254,13 +254,13 @@ impl NPC { } } - if state.game_rng.range(0..80) == 1 { + if self.rng.range(0..80) == 1 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; } - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 3; self.action_counter = 0; self.anim_num = 2; diff --git a/src/npc/toroko.rs b/src/npc/toroko.rs index 80c42bd..cf8eb03 100644 --- a/src/npc/toroko.rs +++ b/src/npc/toroko.rs @@ -17,7 +17,7 @@ impl NPC { self.vel_x = 0; } - if state.game_rng.range(0..120) == 10 { + if self.rng.range(0..120) == 10 { self.action_num = 2; self.action_counter = 0; self.anim_num = 1; diff --git a/src/rng.rs b/src/rng.rs index c73344a..84c7f13 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -3,16 +3,6 @@ use std::cell::Cell; /// Deterministic XorShift-based random number generator pub struct RNG(Cell<(u64, u64, u64, u64)>); -#[inline] -fn rol64(x: u64, shift: u64) -> u64 -{ - if shift == 0 || shift == 64 { - x - } else { - (x << shift) | (x >> (64 - shift)) - } -} - impl RNG { pub fn new(seed: i32) -> Self { Self(Cell::new((seed as u64, @@ -24,7 +14,7 @@ impl RNG { pub fn next_u64(&self) -> i32 { let mut state = self.0.get(); - let result = rol64(state.1.wrapping_mul(5), 7).wrapping_mul(9); + let result = state.1.wrapping_mul(5).rotate_left(5).wrapping_mul(9); let t = state.1 << 17; state.2 ^= state.0; @@ -33,7 +23,7 @@ impl RNG { state.0 ^= state.3; state.2 ^= t; - state.3 = rol64(state.3, 45); + state.3 = state.3.rotate_left(45); self.0.replace(state); result as i32 @@ -61,3 +51,39 @@ impl RNG { range.start.wrapping_add((self.next_u32() >> 2) as i32 % (range.end.wrapping_sub(range.start).wrapping_add(1))) } } + +#[derive(Debug, Copy, Clone)] +pub struct Xoroshiro32PlusPlus(u16, u16); + +impl Xoroshiro32PlusPlus { + pub fn new(seed: u32) -> Xoroshiro32PlusPlus { + Xoroshiro32PlusPlus( + (seed & 0xffff) as u16, + (seed >> 16 & 0xffff) as u16 + ) + } + + pub fn next_u16(&mut self) -> u16 { + let mut result = (self.0.wrapping_add(self.1)).rotate_left(9).wrapping_add(self.0); + + self.1 ^= self.0; + self.0 = self.0.rotate_left(13) ^ self.1 ^ (self.1 << 5); + self.1 = self.1.rotate_left(10); + + result + } + + pub fn dump_state(&self) -> u32 { + (self.0 as u32) | (self.1 as u32) << 16 + } + + pub fn load_state(&mut self, state: u32) { + self.0 = (state & 0xffff) as u16; + self.1 = ((state >> 16) & 0xffff) as u16; + } + + pub fn range(&mut self, range: std::ops::Range) -> i32 { + let num = ((self.next_u16() as u32) << 16 | self.next_u16() as u32) >> 2; + range.start.wrapping_add(num as i32 % (range.end.wrapping_sub(range.start).wrapping_add(1))) + } +} From 4d1e23a0329e0e3b442f35a7f53f7e2d03dd7c90 Mon Sep 17 00:00:00 2001 From: Alula Date: Mon, 7 Dec 2020 00:41:57 +0100 Subject: [PATCH 4/6] fix snack block offset --- src/bullet.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bullet.rs b/src/bullet.rs index d67df26..c62e0b9 100644 --- a/src/bullet.rs +++ b/src/bullet.rs @@ -605,8 +605,8 @@ impl PhysicalEntity for Bullet { let mut npc = NPCMap::create_npc(4, &state.npc_table); npc.cond.set_alive(true); npc.direction = Direction::Left; - npc.x = x * 16 * 0x200; - npc.y = y * 16 * 0x200; + npc.x = (x * 16 + 8) * 0x200;sta + npc.y = (y * 16 + 8) * 0x200; for _ in 0..4 { npc.vel_x = state.game_rng.range(-0x200..0x200) as isize; From 0292e27e117ce9f5b8bcae8c1cb5f6dd2e55b149 Mon Sep 17 00:00:00 2001 From: Alula Date: Mon, 7 Dec 2020 00:42:19 +0100 Subject: [PATCH 5/6] flag documentation --- src/common.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/common.rs b/src/common.rs index 9cbae05..937224a 100644 --- a/src/common.rs +++ b/src/common.rs @@ -24,15 +24,24 @@ bitfield! { pub struct Flag(u32); impl Debug; - pub hit_left_wall, set_hit_left_wall: 0; // 0x01 - pub hit_top_wall, set_hit_top_wall: 1; // 0x02 - pub hit_right_wall, set_hit_right_wall: 2; // 0x04 - pub hit_bottom_wall, set_hit_bottom_wall: 3; // 0x08 - pub hit_right_slope, set_hit_right_slope: 4; // 0x10 - pub hit_left_slope, set_hit_left_slope: 5; // 0x20 - pub snack_destroy, set_snack_destroy: 6; // 0x40 - pub flag_x80, set_flag_x80: 7; // 0x80 - pub in_water, set_in_water: 8; // 0x100 + /// Set if left wall was hit. (corresponds to flag & 0x01) + pub hit_left_wall, set_hit_left_wall: 0; + /// Set if top wall was hit. (corresponds to flag & 0x02) + pub hit_top_wall, set_hit_top_wall: 1; + /// Set if right wall was hit. (corresponds to flag & 0x04) + pub hit_right_wall, set_hit_right_wall: 2; + /// Set if bottom wall was hit. (corresponds to flag & 0x08) + pub hit_bottom_wall, set_hit_bottom_wall: 3; + /// Set if entity stays on right slope. (corresponds to flag & 0x10) + pub hit_right_slope, set_hit_right_slope: 4; + /// Set if entity stays on left slope. (corresponds to flag & 0x20) + pub hit_left_slope, set_hit_left_slope: 5; + /// Unknown purpose (corresponds to flag & 0x40) + pub flag_x40, set_flag_x40: 6; + /// Unknown purpose (corresponds to flag & 0x80) + pub flag_x80, set_flag_x80: 7; + /// Set if entity is in water. (corresponds to flag & 0x100) + pub in_water, set_in_water: 8; pub weapon_hit_block, set_weapon_hit_block: 9; // 0x200 pub hit_by_spike, set_hit_by_spike: 10; // 0x400 pub water_splash_facing_right, set_water_splash_facing_right: 11; // 0x800 From 749684c437c269333f38f60d9ceed9b3283c7570 Mon Sep 17 00:00:00 2001 From: Alula Date: Mon, 7 Dec 2020 00:42:58 +0100 Subject: [PATCH 6/6] Add missing NPCs (Egg Corridor, Sand Zone, Arthur's House) --- src/npc/booster.rs | 74 ++++++++++ src/npc/characters.rs | 38 ++++- src/npc/misc.rs | 50 +++++++ src/npc/mod.rs | 10 +- src/npc/sand_zone.rs | 327 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 494 insertions(+), 5 deletions(-) diff --git a/src/npc/booster.rs b/src/npc/booster.rs index 986f476..f25ff81 100644 --- a/src/npc/booster.rs +++ b/src/npc/booster.rs @@ -2,6 +2,7 @@ use ggez::GameResult; use crate::npc::NPC; use crate::shared_game_state::SharedGameState; +use crate::common::Direction; impl NPC { pub(crate) fn tick_n113_professor_booster(&mut self, state: &mut SharedGameState) -> GameResult { @@ -12,9 +13,82 @@ impl NPC { self.anim_num = 0; self.anim_counter = 0; } + + if self.rng.range(0..120) == 10 { + self.action_num = 2; + self.action_counter = 0; + self.anim_num = 1; + } + } + 2 => { + self.action_counter += 1; + if self.action_counter > 8 { + self.action_num = 1; + self.anim_num = 0; + } + } + 3 | 4 => { + if self.action_num == 3 { + self.action_num = 4; + self.anim_num = 2; + self.anim_counter = 0; + } + + self.animate(5, 2, 5); + + self.x += self.direction.vector_x() * 0x200; + } + 5 => { + self.anim_num = 6; + } + 30 | 31 => { + if self.action_num == 30 { + self.action_num = 31; + self.anim_num = 0; + self.anim_counter = 0; + self.hit_bounds.bottom = 16 * 0x200; + self.x -= 16 * 0x200; + self.y += 8 * 0x200; + } + + self.action_counter += 1; + if self.action_counter == 64 { + self.action_num = 32; + self.action_counter = 0; + } + } + 32 => { + self.action_counter += 1; + if self.action_counter > 20 { + self.action_num = 33; + self.anim_num = 1; + self.hit_bounds.bottom = 8 * 0x200; + } + } + 33 => { + if self.flags.hit_bottom_wall() { + self.action_num = 34; + self.action_counter = 0; + self.anim_num = 0; + } } _ => {} } + + self.vel_y += 0x40; + self.y += self.vel_y; + + let dir_offset = if self.direction == Direction::Left { 0 } else { 7 }; + + self.anim_rect = state.constants.npc.n113_professor_booster[self.anim_num as usize + dir_offset]; + + if self.action_num == 31 { + self.anim_rect.bottom = self.action_counter / 4 + self.anim_rect.top; + if self.action_counter / 2 % 2 != 0 { + self.anim_rect.left += 1; + } + } + Ok(()) } } diff --git a/src/npc/characters.rs b/src/npc/characters.rs index 113d5f7..e4624d2 100644 --- a/src/npc/characters.rs +++ b/src/npc/characters.rs @@ -331,12 +331,42 @@ impl NPC { self.x += self.vel_x; self.y += self.vel_y; - if self.direction == Direction::Left { - self.anim_rect = state.constants.npc.n074_jack[self.anim_num as usize]; - } else { - self.anim_rect = state.constants.npc.n074_jack[self.anim_num as usize + 6]; + let dir_offset = if self.direction == Direction::Left { 0 } else { 6 }; + + self.anim_rect = state.constants.npc.n074_jack[self.anim_num as usize + dir_offset]; + + Ok(()) + } + + pub(crate) fn tick_n151_blue_robot_standing(&mut self, state: &mut SharedGameState) -> GameResult { + match self.action_num { + 0 | 1 => { + if self.action_num == 0{ + self.action_num = 1; + self.anim_num = 0; + self.anim_counter = 0; + } + + if self.rng.range(0..100) == 0 { + self.action_num = 2; + self.action_counter = 0; + self.anim_num = 1; + } + } + 2 => { + self.action_counter += 1; + if self.action_counter > 16 { + 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.n151_blue_robot_standing[self.anim_num as usize + dir_offset]; + Ok(()) } } diff --git a/src/npc/misc.rs b/src/npc/misc.rs index 2697b46..e76f8d8 100644 --- a/src/npc/misc.rs +++ b/src/npc/misc.rs @@ -976,6 +976,41 @@ impl NPC { Ok(()) } + pub(crate) fn tick_n125_hidden_item(&mut self, state: &mut SharedGameState) -> GameResult { + if self.life < 990 { + self.cond.set_drs_destroyed(true); + match self.direction { + /// hidden heart + Direction::Left => { + let mut npc = NPCMap::create_npc(87, &state.npc_table); + npc.cond.set_alive(true); + npc.x = self.x; + npc.y = self.y; + npc.direction = Direction::Right; + state.new_npcs.push(npc); + } + /// hidden missile + Direction::Right => { + let mut npc = NPCMap::create_npc(86, &state.npc_table); + npc.cond.set_alive(true); + npc.x = self.x; + npc.y = self.y; + npc.direction = Direction::Right; + state.new_npcs.push(npc); + } + _ => {} + } + } + + match self.direction { + Direction::Left => self.anim_rect = state.constants.npc.n125_hidden_item[0], + Direction::Right => self.anim_rect = state.constants.npc.n125_hidden_item[1], + _ => {} + } + + Ok(()) + } + pub(crate) fn tick_n149_horizontal_moving_block(&mut self, state: &mut SharedGameState, players: [&mut Player; 2]) -> GameResult { match self.action_num { 0 => { @@ -1265,4 +1300,19 @@ impl NPC { Ok(()) } + + pub(crate) fn tick_n234_red_flowers_picked(&mut self, state: &mut SharedGameState) -> GameResult { + if self.action_num == 0 { + self.action_num = 1; + self.y += 16 * 0x200; + + match self.direction { + Direction::Left => self.anim_rect = state.constants.npc.n234_red_flowers_picked[0], + Direction::Right => self.anim_rect = state.constants.npc.n234_red_flowers_picked[1], + _ => self.anim_rect = state.constants.npc.n234_red_flowers_picked[1], + } + } + + Ok(()) + } } diff --git a/src/npc/mod.rs b/src/npc/mod.rs index 3828309..af958d5 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -116,7 +116,7 @@ pub struct NPC { pub rng: Xoroshiro32PlusPlus, } -static PARTICLE_NPCS: [u16; 11] = [1, 4, 11, 73, 84, 86, 87, 108, 129, 199, 355]; +static PARTICLE_NPCS: [u16; 12] = [1, 4, 11, 45, 73, 84, 86, 87, 108, 129, 199, 355]; impl NPC { pub fn get_start_index(&self) -> u16 { @@ -214,7 +214,10 @@ impl GameEntity<([&mut Player; 2], &BTreeMap>, &mut Stage)> fo 41 => self.tick_n041_busted_door(state), 42 => self.tick_n042_sue(state, players, map), 43 => self.tick_n043_chalkboard(state), + 44 => self.tick_n044_polish(state), + 45 => self.tick_n045_baby(state), 46 => self.tick_n046_hv_trigger(players), + 47 => self.tick_n047_sandcroc(state, players), 52 => self.tick_n052_sitting_blue_robot(state), 55 => self.tick_n055_kazuma(state), 58 => self.tick_n058_basu(state, players), @@ -271,10 +274,14 @@ impl GameEntity<([&mut Player; 2], &BTreeMap>, &mut Stage)> fo 110 => self.tick_n110_puchi(state, players), 111 => self.tick_n111_quote_teleport_out(state, players), 112 => self.tick_n112_quote_teleport_in(state, players), + 113 => self.tick_n113_professor_booster(state), 114 => self.tick_n114_press(state, players), + 124 => self.tick_n124_sunstone(state), + 125 => self.tick_n125_hidden_item(state), 129 => self.tick_n129_fireball_snake_trail(state), 149 => self.tick_n149_horizontal_moving_block(state, players), 150 => self.tick_n150_quote(state, players), + 151 => self.tick_n151_blue_robot_standing(state), 154 => self.tick_n154_gaudi_dead(state), 157 => self.tick_n157_vertical_moving_block(state, players), 192 => self.tick_n192_scooter(state), @@ -282,6 +289,7 @@ impl GameEntity<([&mut Player; 2], &BTreeMap>, &mut Stage)> fo 194 => self.tick_n194_broken_blue_robot(state), 199 => self.tick_n199_wind_particles(state), 211 => self.tick_n211_small_spikes(state), + 234 => self.tick_n234_red_flowers_picked(state), 298 => self.tick_n298_intro_doctor(state), 299 => self.tick_n299_intro_balrog_misery(state), 300 => self.tick_n300_intro_demon_crown(state), diff --git a/src/npc/sand_zone.rs b/src/npc/sand_zone.rs index e69de29..d9cf17f 100644 --- a/src/npc/sand_zone.rs +++ b/src/npc/sand_zone.rs @@ -0,0 +1,327 @@ +use ggez::GameResult; +use num_traits::{abs, clamp}; + +use crate::common::Direction; +use crate::npc::{NPC, NPCMap}; +use crate::player::Player; +use crate::shared_game_state::SharedGameState; + +impl NPC { + pub(crate) fn tick_n044_polish(&mut self, state: &mut SharedGameState) -> GameResult { + match self.action_num { + 0 | 1 => { + self.anim_num = 0; + self.action_num = match self.direction { + Direction::Left => 8, + Direction::Right => 2, + _ => 8, + }; + } + 2 => { + self.vel_y += 0x20; + if self.vel_y > 0 && self.flags.hit_bottom_wall() { + self.vel_y = -0x100; + self.vel_x += 0x100; + } + + if self.flags.hit_right_wall() { + self.action_num = 3; + } + } + 3 => { + self.vel_x += 0x20; + if self.vel_x > 0 && self.flags.hit_right_wall() { + self.vel_x = -0x100; + self.vel_y -= 0x100; + } + + if self.flags.hit_top_wall() { + self.action_num = 4; + } + } + 4 => { + self.vel_y -= 0x20; + if self.vel_y < 0 && self.flags.hit_top_wall() { + self.vel_y = 0x100; + self.vel_x -= 0x100; + } + + if self.flags.hit_left_wall() { + self.action_num = 5; + } + } + 5 => { + self.vel_x -= 0x20; + if self.vel_x < 0 && self.flags.hit_left_wall() { + self.vel_x = 0x100; + self.vel_y += 0x100; + } + + if self.flags.hit_bottom_wall() { + self.action_num = 2; + } + } + 6 => { + self.vel_y += 0x20; + if self.vel_y > 0 && self.flags.hit_bottom_wall() { + self.vel_y = -0x100; + self.vel_x -= 0x100; + } + + if self.flags.hit_left_wall() { + self.action_num = 7; + } + } + 7 => { + self.vel_x -= 0x20; + if self.vel_x < 0 && self.flags.hit_left_wall() { + self.vel_x = 0x100; + self.vel_y -= 0x100; + } + + if self.flags.hit_top_wall() { + self.action_num = 8; + } + } + 8 => { + self.vel_y -= 0x20; + if self.vel_y < 0 && self.flags.hit_top_wall() { + self.vel_y = 0x100; + self.vel_x += 0x100; + } + + if self.flags.hit_right_wall() { + self.action_num = 9; + } + } + 9 => { + self.vel_x += 0x20; + if self.vel_x > 0 && self.flags.hit_right_wall() { + self.vel_x = -0x100; + self.vel_y += 0x100; + } + + if self.flags.hit_bottom_wall() { + self.action_num = 6; + } + } + _ => {} + } + + if self.life <= 100 { + self.cond.set_drs_destroyed(true); + state.sound_manager.play_sfx(25); + + let mut npc = NPCMap::create_npc(45, &state.npc_table); + npc.cond.set_alive(true); + npc.x = self.x; + npc.y = self.y; + for _ in 0..9 { + state.new_npcs.push(npc); + } + } + + self.vel_x = clamp(self.vel_x, -0x200, 0x200); + self.vel_y = clamp(self.vel_y, -0x200, 0x200); + + if self.shock > 0 { + self.x += self.vel_x / 2; + self.y += self.vel_y / 2; + } else { + self.x += self.vel_x; + self.y += self.vel_y; + } + + if self.action_num > 1 && self.action_num <= 9 { + self.anim_num += 1; + if self.anim_num > 2 { + self.anim_num = 1; + } + } + + let dir_offset = if self.direction == Direction::Left { 0 } else { 3 }; + + self.anim_rect = state.constants.npc.n044_polish[self.anim_num as usize + dir_offset]; + + Ok(()) + } + + pub(crate) fn tick_n045_baby(&mut self, state: &mut SharedGameState) -> GameResult { + if self.action_num == 0 { + self.action_num = 2; + self.vel_x = if self.rng.next_u16() & 1 != 0 { + self.rng.range(-0x200..-0x100) as isize + } else { + self.rng.range(0x100..0x200) as isize + }; + self.vel_y = if self.rng.next_u16() & 1 != 0 { + self.rng.range(-0x200..-0x100) as isize + } else { + self.rng.range(0x100..0x200) as isize + }; + self.vel_x2 = self.vel_x; + self.vel_y2 = self.vel_y; + } + + match self.action_num { + 1 | 2 => { + self.anim_num += 1; + if self.anim_num > 2 { + self.anim_num = 1; + } + } + _ => {} + } + + if self.vel_x2 < 0 && self.flags.hit_left_wall() { + self.vel_x2 = -self.vel_x2; + } + + if self.vel_x2 > 0 && self.flags.hit_right_wall() { + 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; + } + + self.vel_x2 = clamp(self.vel_x2, -0x200, 0x200); + self.vel_y2 = clamp(self.vel_y2, -0x200, 0x200); + + if self.shock > 0 { + self.x += self.vel_x2 / 2; + self.y += self.vel_y2 / 2; + } else { + self.x += self.vel_x2; + self.y += self.vel_y2; + } + + self.anim_rect = state.constants.npc.n045_baby[self.anim_num as usize]; + + Ok(()) + } + + pub(crate) fn tick_n047_sandcroc(&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_counter = 0; + self.anim_num = 0; + self.target_y = self.y; + self.npc_flags.set_shootable(false); + self.npc_flags.set_ignore_solidity(false); + self.npc_flags.set_invulnerable(false); + self.npc_flags.set_solid_soft(false); + } + + let player = self.get_closest_player_mut(players); + if abs(self.x - player.x) < 8 * 0x200 && player.y > self.y && player.y < self.y + 8 * 0x200 { + self.action_num = 2; + self.action_counter = 0; + state.sound_manager.play_sfx(102); + } + + self.x += (player.x - self.x).signum() * 2 * 0x200; + } + 2 => { + self.anim_counter += 1; + if self.anim_counter > 3 { + self.anim_num += 1; + self.anim_counter = 0; + } + + match self.anim_num { + 3 => self.damage = 10, + 4 => { + self.action_num = 3; + self.action_counter = 0; + self.npc_flags.set_shootable(true); + } + _ => {} + } + } + 3 => { + self.damage = 0; + self.npc_flags.set_solid_soft(true); + + self.action_counter += 1; + if self.shock > 0 { + self.action_num = 4; + self.action_counter = 0; + } + } + 4 => { + self.npc_flags.set_ignore_solidity(true); + self.y += 0x200; + self.action_counter += 1; + if self.action_counter == 32 { + self.action_num = 5; + self.action_counter = 0; + self.npc_flags.set_solid_soft(false); + self.npc_flags.set_shootable(false); + } + } + 5 => { + if self.action_counter > 99 { + self.y = self.target_y; + self.action_num = 0; + self.anim_num = 0; + } else { + self.action_counter += 1; + } + } + _ => {} + } + + self.anim_rect = state.constants.npc.n047_sandcroc[self.anim_num as usize]; + + Ok(()) + } + + pub(crate) fn tick_n124_sunstone(&mut self, state: &mut SharedGameState) -> GameResult { + match self.action_num { + 0 | 1 => { + if self.action_num ==0 { + self.action_num = 1; + self.x += 8 * 0x200; + self.y += 8 * 0x200; + } + + self.npc_flags.set_ignore_solidity(false); + self.anim_num = 0; + } + 10 | 11 => { + if self.action_num == 10 { + self.action_num = 11; + self.action_counter = 0; + self.anim_num = 1; + + self.npc_flags.set_ignore_solidity(true); + } + + match self.direction { + Direction::Left => self.x -= 0x80, + Direction::Up => self.y -= 0x80, + Direction::Right => self.x += 0x80, + Direction::Bottom => self.y += 0x80, + Direction::FacingPlayer => {} + } + + state.quake_counter= 20; + if self.action_counter % 8 == 0 { + state.sound_manager.play_sfx(26); + } + } + _ =>{} + } + + self.anim_rect = state.constants.npc.n124_sunstone[self.anim_num as usize]; + + Ok(()) + } +}