diff --git a/src/engine_constants.rs b/src/engine_constants.rs index 9ca459e..cba6764 100644 --- a/src/engine_constants.rs +++ b/src/engine_constants.rs @@ -243,6 +243,7 @@ pub struct NPCConsts { pub n299_intro_balrog_misery: [Rect; 2], pub n300_intro_demon_crown: Rect, pub n361_gaudi_dashing: [Rect; 4], + pub b02_balfrog: [Rect; 18], } #[derive(Debug, Copy, Clone)] @@ -1394,6 +1395,26 @@ impl EngineConstants { Rect { left: 48, top: 72, right: 72, bottom: 96 }, // right Rect { left: 72, top: 72, right: 96, bottom: 96 }, ], + b02_balfrog: [ + Rect { left: 0, top: 0, right: 0, bottom: 0 }, // left + Rect { left: 0, top: 48, right: 80, bottom: 112 }, + Rect { left: 0, top: 112, right: 80, bottom: 176 }, + Rect { left: 0, top: 176, right: 80, bottom: 240 }, + Rect { left: 160, top: 48, right: 240, bottom: 112 }, + Rect { left: 160, top: 112, right: 240, bottom: 200 }, + Rect { left: 200, top: 0, right: 240, bottom: 24 }, + Rect { left: 80, top: 0, right: 120, bottom: 24 }, + Rect { left: 120, top: 0, right: 160, bottom: 24 }, + Rect { left: 0, top: 0, right: 0, bottom: 0 }, // right + Rect { left: 80, top: 48, right: 160, bottom: 112 }, + Rect { left: 80, top: 112, right: 160, bottom: 176 }, + Rect { left: 80, top: 176, right: 160, bottom: 240 }, + Rect { left: 240, top: 48, right: 320, bottom: 112 }, + Rect { left: 240, top: 112, right: 320, bottom: 200 }, + Rect { left: 200, top: 24, right: 240, bottom: 48 }, + Rect { left: 80, top: 24, right: 120, bottom: 48 }, + Rect { left: 120, top: 24, right: 160, bottom: 48 }, + ], }, weapon: WeaponConsts { bullet_table: vec![ diff --git a/src/npc/boss/balfrog.rs b/src/npc/boss/balfrog.rs new file mode 100644 index 0000000..60cc101 --- /dev/null +++ b/src/npc/boss/balfrog.rs @@ -0,0 +1,86 @@ +use crate::common::{Direction, Rect}; +use crate::npc::boss::BossNPC; +use crate::npc::NPCMap; +use crate::shared_game_state::SharedGameState; + +impl BossNPC { + pub(crate) fn tick_b02_balfrog(&mut self, state: &mut SharedGameState) { + match self.parts[0].action_num { + 0 => { + self.parts[0].x = 6 * 16 * 0x200; + self.parts[0].y = 12 * 16 * 0x200; + self.parts[0].direction = Direction::Right; + self.parts[0].display_bounds = Rect { + left: 48 * 0x200, + top: 48 * 0x200, + right: 32 * 0x200, + bottom: 16 * 0x200, + }; + self.parts[0].hit_bounds = Rect { + left: 24 * 0x200, + top: 16 * 0x200, + right: 24 * 0x200, + bottom: 16 * 0x200, + }; + self.parts[0].size = 3; + self.parts[0].exp = 1; + self.parts[0].event_num = 1000; + self.parts[0].npc_flags.set_event_when_killed(true); + self.parts[0].npc_flags.set_show_damage(true); + self.parts[0].life = 300; + } + 10 => { + self.parts[0].action_num = 11; + self.parts[0].anim_num = 3; + self.parts[0].cond.set_alive(true); + self.parts[0].anim_rect = state.constants.npc.b02_balfrog[9]; + + self.parts[1].cond.set_alive(true); + self.parts[1].cond.set_damage_boss(true); + self.parts[1].damage = 5; + + self.parts[2].cond.set_alive(true); + self.parts[2].damage = 5; + + let mut npc = NPCMap::create_npc(4, &state.npc_table); + + 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; + + state.new_npcs.push(npc); + } + } + 20 | 21 => { + if self.parts[0].action_num == 20 { + self.parts[0].action_num = 0; + self.parts[0].action_counter = 0 + } + + self.parts[0].action_counter += 1; + if self.parts[0].action_counter / 2 % 2 != 0 { + self.parts[0].anim_num = 3; + } else { + self.parts[0].anim_num = 0; + } + } + _ => {} + } + + self.parts[0].vel_y += 0x40; + if self.parts[0].vel_y > 0x5ff { + self.parts[0].vel_y = 0x5ff; + } + + self.parts[0].x += self.parts[0].vel_x; + self.parts[0].y += self.parts[0].vel_y; + + let dir_offset = if self.parts[0].direction == Direction::Left { 0 } else { 9 }; + + self.parts[0].anim_rect = state.constants.npc.b02_balfrog[self.parts[0].anim_num as usize + dir_offset]; + } +} diff --git a/src/npc/boss/ballos.rs b/src/npc/boss/ballos.rs new file mode 100644 index 0000000..114c463 --- /dev/null +++ b/src/npc/boss/ballos.rs @@ -0,0 +1,7 @@ +use crate::npc::boss::BossNPC; + +impl BossNPC { + pub(crate) fn tick_b09_ballos(&mut self) { + + } +} diff --git a/src/npc/boss/core.rs b/src/npc/boss/core.rs new file mode 100644 index 0000000..266f790 --- /dev/null +++ b/src/npc/boss/core.rs @@ -0,0 +1,7 @@ +use crate::npc::boss::BossNPC; + +impl BossNPC { + pub(crate) fn tick_b04_core(&mut self) { + + } +} diff --git a/src/npc/boss/ironhead.rs b/src/npc/boss/ironhead.rs new file mode 100644 index 0000000..e739c7e --- /dev/null +++ b/src/npc/boss/ironhead.rs @@ -0,0 +1,7 @@ +use crate::npc::boss::BossNPC; + +impl BossNPC { + pub(crate) fn tick_b05_ironhead(&mut self) { + + } +} diff --git a/src/npc/boss/mod.rs b/src/npc/boss/mod.rs index 374eda7..5c47359 100644 --- a/src/npc/boss/mod.rs +++ b/src/npc/boss/mod.rs @@ -1,13 +1,81 @@ -use crate::npc::NPC; +use std::cell::RefCell; +use std::collections::HashMap; -pub struct BossNPCMap { - pub parts: [NPC; 16] +use crate::ggez::{GameResult, Context}; +use crate::npc::NPC; +use crate::player::Player; +use crate::shared_game_state::SharedGameState; +use crate::stage::Stage; +use crate::entity::GameEntity; +use crate::frame::Frame; +use crate::common::{Direction, interpolate_fix9_scale}; + +pub mod balfrog; +pub mod ballos; +pub mod core; +pub mod ironhead; +pub mod monster_x; +pub mod omega; +pub mod press; +pub mod twins; +pub mod undead_core; + +pub struct BossNPC { + pub boss_type: u16, + pub parts: [NPC; 16], } -impl BossNPCMap { - pub fn new() -> BossNPCMap { - BossNPCMap { +impl BossNPC { + pub fn new() -> BossNPC { + BossNPC { + boss_type: 0, parts: [NPC::empty(); 16], } } } + +impl GameEntity<(&mut Player, &HashMap>, &mut Stage)> for BossNPC { + fn tick(&mut self, state: &mut SharedGameState, (player, map, stage): (&mut Player, &HashMap>, &mut Stage)) -> GameResult { + match self.boss_type { + 1 => self.tick_b01_omega(), + 2 => self.tick_b02_balfrog(state), + 3 => self.tick_b03_monster_x(), + 4 => self.tick_b04_core(), + 5 => self.tick_b05_ironhead(), + 6 => self.tick_b06_twins(), + 7 => self.tick_b07_undead_core(), + 8 => self.tick_b09_ballos(), + _ => {} + } + Ok(()) + } + + fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, frame: &Frame) -> GameResult { + let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, state.npc_table.tex_npc2_name.as_str())?; + + for npc in self.parts.iter() { + if !npc.cond.alive() || npc.cond.hidden() { + continue; + } + + let off_x = if npc.direction == Direction::Left { npc.display_bounds.left } else { npc.display_bounds.right } as isize; + let shock = if npc.shock > 0 { + (2 * ((npc.shock as isize / 2) % 2) - 1) as f32 + } else { 0.0 }; + + batch.add_rect( + interpolate_fix9_scale(npc.prev_x - off_x - frame.prev_x, + npc.x - off_x - frame.x, + state.frame_time, state.scale) + shock, + interpolate_fix9_scale(npc.prev_y - npc.display_bounds.top as isize - frame.prev_y, + npc.y - npc.display_bounds.top as isize - frame.y, + state.frame_time, state.scale), + &npc.anim_rect, + ); + } + + batch.draw(ctx)?; + + Ok(()) + } +} diff --git a/src/npc/boss/monster_x.rs b/src/npc/boss/monster_x.rs new file mode 100644 index 0000000..98d10ef --- /dev/null +++ b/src/npc/boss/monster_x.rs @@ -0,0 +1,7 @@ +use crate::npc::boss::BossNPC; + +impl BossNPC { + pub(crate) fn tick_b03_monster_x(&mut self) { + + } +} diff --git a/src/npc/boss/omega.rs b/src/npc/boss/omega.rs new file mode 100644 index 0000000..3b8ebf7 --- /dev/null +++ b/src/npc/boss/omega.rs @@ -0,0 +1,7 @@ +use crate::npc::boss::BossNPC; + +impl BossNPC { + pub(crate) fn tick_b01_omega(&mut self) { + + } +} diff --git a/src/npc/boss/press.rs b/src/npc/boss/press.rs new file mode 100644 index 0000000..1882e57 --- /dev/null +++ b/src/npc/boss/press.rs @@ -0,0 +1,7 @@ +use crate::npc::boss::BossNPC; + +impl BossNPC { + pub(crate) fn tick_b08_press(&mut self) { + + } +} diff --git a/src/npc/boss/twins.rs b/src/npc/boss/twins.rs new file mode 100644 index 0000000..5077467 --- /dev/null +++ b/src/npc/boss/twins.rs @@ -0,0 +1,7 @@ +use crate::npc::boss::BossNPC; + +impl BossNPC { + pub(crate) fn tick_b06_twins(&mut self) { + + } +} diff --git a/src/npc/boss/undead_core.rs b/src/npc/boss/undead_core.rs new file mode 100644 index 0000000..09604b8 --- /dev/null +++ b/src/npc/boss/undead_core.rs @@ -0,0 +1,7 @@ +use crate::npc::boss::BossNPC; + +impl BossNPC { + pub(crate) fn tick_b07_undead_core(&mut self) { + + } +} diff --git a/src/npc/mod.rs b/src/npc/mod.rs index a99bdea..12d427e 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -17,7 +17,7 @@ use crate::entity::GameEntity; use crate::frame::Frame; use crate::ggez::{Context, GameResult}; use crate::map::NPCData; -use crate::npc::boss::BossNPCMap; +use crate::npc::boss::BossNPC; use crate::physics::PhysicalEntity; use crate::player::Player; use crate::shared_game_state::SharedGameState; @@ -379,7 +379,7 @@ pub struct NPCMap { /// Do not iterate over this directly outside render pipeline. pub npcs: HashMap>, /// NPCMap but for bosses and of static size. - pub boss_map: BossNPCMap, + pub boss_map: BossNPC, } impl NPCMap { @@ -388,7 +388,7 @@ impl NPCMap { NPCMap { npc_ids: BTreeSet::new(), npcs: HashMap::with_capacity(256), - boss_map: BossNPCMap::new(), + boss_map: BossNPC::new(), } } diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index a295b27..914dbd6 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -914,6 +914,7 @@ impl GameScene { } } } + self.npc_map.boss_map.tick(state, (&mut self.player, &self.npc_map.npcs, &mut self.stage))?; self.npc_map.process_npc_changes(state); self.npc_map.garbage_collect(); @@ -929,6 +930,9 @@ impl GameScene { } } } + for npc in self.npc_map.boss_map.parts.iter_mut() { + npc.tick_map_collisions(state, &mut self.stage); + } self.npc_map.process_npc_changes(state); self.npc_map.garbage_collect(); @@ -1220,6 +1224,13 @@ impl Scene for GameScene { } } + for npc in self.npc_map.boss_map.parts.iter_mut() { + if npc.cond.alive() { + npc.prev_x = npc.x; + npc.prev_y = npc.y; + } + } + Ok(()) } @@ -1291,6 +1302,7 @@ impl Scene for GameScene { npc.draw(state, ctx, &self.frame)?; } } + self.npc_map.boss_map.draw(state, ctx, &self.frame)?; self.draw_bullets(state, ctx)?; self.player.draw(state, ctx, &self.frame)?; self.draw_tiles(state, ctx, TileLayer::Foreground)?; diff --git a/src/text_script.rs b/src/text_script.rs index 180386c..b4a577c 100644 --- a/src/text_script.rs +++ b/src/text_script.rs @@ -1140,6 +1140,13 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } + OpCode::BOA => { + let action_num = read_cur_varint(&mut cursor)? as u16; + + game_scene.npc_map.boss_map.parts[0].action_num = action_num; + + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } OpCode::ANP => { let event_num = read_cur_varint(&mut cursor)? as u16; let action_num = read_cur_varint(&mut cursor)? as u16; @@ -1400,7 +1407,7 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } // One operand codes - OpCode::BOA | OpCode::BSL | OpCode::FOB | OpCode::NUM | + OpCode::BSL | OpCode::NUM | OpCode::MPp | OpCode::SKm | OpCode::SKp | OpCode::UNJ | OpCode::MPJ | OpCode::XX1 | OpCode::SIL | OpCode::SSS | OpCode::ACH | OpCode::S2MV => {