diff --git a/src/engine_constants.rs b/src/engine_constants.rs index 95e1a28..9841d69 100644 --- a/src/engine_constants.rs +++ b/src/engine_constants.rs @@ -78,7 +78,20 @@ pub struct WorldConsts { pub struct NPCConsts { pub n016_save_point: [Rect; 8], pub n017_health_refill: [Rect; 2], - pub n018_door_rects: [Rect; 2], + pub n018_door: [Rect; 2], + pub n020_computer: [Rect; 4], + pub n021_chest_open: Rect, + pub n022_teleporter: [Rect; 2], + pub n023_teleporter_lights: [Rect; 8], + pub n027_death_trap: Rect, + pub n029_cthulhu: [Rect; 4], + pub n030_hermit_gunsmith: [Rect; 3], + pub n032_life_capsule: [Rect; 2], + pub n034_bed: [Rect; 2], + pub n035_mannan: [Rect; 8], + pub n037_sign: [Rect; 2], + pub n038_fireplace: [Rect; 4], + pub n039_save_sign: [Rect; 2], } #[derive(Debug, Copy, Clone)] @@ -256,9 +269,74 @@ impl EngineConstants { Rect { left: 288, top: 0, right: 304, bottom: 16 }, Rect { left: 304, top: 0, right: 320, bottom: 16 }, ], - n018_door_rects: [ + n018_door: [ Rect { left: 224, top: 16, right: 240, bottom: 40 }, Rect { left: 192, top: 112, right: 208, bottom: 136 }, + ], + n020_computer: [ + Rect { left: 288, top: 16, right: 320, bottom: 40 }, // left + Rect { left: 288, top: 40, right: 320, bottom: 64 }, // right + Rect { left: 288, top: 40, right: 320, bottom: 64 }, + Rect { left: 288, top: 64, right: 320, bottom: 88 }, + ], + n021_chest_open: Rect { left: 224, top: 40, right: 240, bottom: 48 }, + n022_teleporter: [ + Rect { left: 240, top: 16, right: 264, bottom: 48 }, + Rect { left: 248, top: 152, right: 272, bottom: 184 }, + ], + n023_teleporter_lights: [ + Rect { left: 264, top: 16, right: 288, bottom: 20 }, + Rect { left: 264, top: 20, right: 288, bottom: 24 }, + Rect { left: 264, top: 24, right: 288, bottom: 28 }, + Rect { left: 264, top: 28, right: 288, bottom: 32 }, + Rect { left: 264, top: 32, right: 288, bottom: 36 }, + Rect { left: 264, top: 36, right: 288, bottom: 40 }, + Rect { left: 264, top: 40, right: 288, bottom: 44 }, + Rect { left: 264, top: 44, right: 288, bottom: 48 }, + ], + n027_death_trap: Rect { left: 96, top: 64, right: 128, bottom: 88 }, + n029_cthulhu: [ + Rect { left: 0, top: 192, right: 16, bottom: 216 }, // left + Rect { left: 16, top: 192, right: 32, bottom: 216 }, + Rect { left: 0, top: 216, right: 16, bottom: 240 }, // right + Rect { left: 16, top: 216, right: 32, bottom: 240 }, + ], + n030_hermit_gunsmith: [ + Rect { left: 48, top: 0, right: 64, bottom: 16 }, + Rect { left: 48, top: 16, right: 64, bottom: 32 }, + Rect { left: 0, top: 32, right: 16, bottom: 48 }, + ], + n032_life_capsule: [ + Rect { left: 32, top: 96, right: 48, bottom: 112 }, + Rect { left: 48, top: 96, right: 64, bottom: 112 }, + ], + n034_bed: [ + Rect { left: 192, top: 48, right: 224, bottom: 64 }, + Rect { left: 192, top: 184, right: 224, bottom: 200 }, + ], + n035_mannan: [ + Rect { left: 96, top: 64, right: 120, bottom: 96 }, // left + Rect { left: 120, top: 64, right: 144, bottom: 96 }, + Rect { left: 144, top: 64, right: 168, bottom: 96 }, + Rect { left: 168, top: 64, right: 192, bottom: 96 }, + Rect { left: 96, top: 96, right: 120, bottom: 128 }, // right + Rect { left: 120, top: 96, right: 144, bottom: 128 }, + Rect { left: 144, top: 96, right: 168, bottom: 128 }, + Rect { left: 168, top: 96, right: 192, bottom: 128 }, + ], + n037_sign: [ + Rect { left: 192, top: 64, right: 208, bottom: 80 }, + Rect { left: 208, top: 64, right: 224, bottom: 80 }, + ], + n038_fireplace: [ + Rect { left: 128, top: 64, right: 144, bottom: 80 }, + Rect { left: 144, top: 64, right: 160, bottom: 80 }, + Rect { left: 160, top: 64, right: 176, bottom: 80 }, + Rect { left: 176, top: 64, right: 192, bottom: 80 }, + ], + n039_save_sign: [ + Rect { left: 224, top: 64, right: 240, bottom: 80 }, + Rect { left: 240, top: 64, right: 256, bottom: 80 }, ] }, tex_sizes: hashmap! { diff --git a/src/entity.rs b/src/entity.rs index ebfba61..79e28c9 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -3,8 +3,8 @@ use crate::ggez::{Context, GameResult}; use crate::frame::Frame; use crate::SharedGameState; -pub trait GameEntity { - fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult; +pub trait GameEntity { + fn tick(&mut self, state: &mut SharedGameState, custom: C) -> GameResult; fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, frame: &Frame) -> GameResult; } diff --git a/src/map.rs b/src/map.rs index 3584d0b..330892d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -60,7 +60,7 @@ pub struct NPCData { pub id: u16, pub x: i16, pub y: i16, - pub flag_id: u16, + pub flag_num: u16, pub event_num: u16, pub npc_type: u16, pub flags: u16, @@ -88,7 +88,7 @@ impl NPCData { for i in 0..count { let x = data.read_i16::()?; let y = data.read_i16::()?; - let flag_id = data.read_u16::()?; + let flag_num = data.read_u16::()?; let event_num = data.read_u16::()?; let npc_type = data.read_u16::()?; let flags = data.read_u16::()?; @@ -100,7 +100,7 @@ impl NPCData { id: 170 + i as u16, x, y, - flag_id, + flag_num, event_num, npc_type, flags, diff --git a/src/npc/misc.rs b/src/npc/misc.rs index 0b49bff..0c6e3e8 100644 --- a/src/npc/misc.rs +++ b/src/npc/misc.rs @@ -8,8 +8,24 @@ impl NPC { Ok(()) } - pub(crate) fn tick_n016_save_point(&mut self, state: &mut SharedGameState) -> GameResult { + if self.action_num == 0 { + self.action_num = 1; + + if self.direction == Direction::Right { + self.npc_flags.set_interactable(false); + self.vel_y = -0x200; + } + } + + if self.flags.hit_bottom_wall() { + self.npc_flags.set_interactable(true); + } + + self.anim_counter = (self.anim_counter + 1) % 24; + self.anim_num = self.anim_counter / 3; + self.anim_rect = state.constants.npc.n016_save_point[self.anim_num as usize]; + Ok(()) } @@ -76,8 +92,8 @@ impl NPC { match self.action_num { 0 => { match self.direction { - Direction::Left => { self.anim_rect = state.constants.npc.n018_door_rects[0] } - Direction::Right => { self.anim_rect = state.constants.npc.n018_door_rects[1] } + Direction::Left => { self.anim_rect = state.constants.npc.n018_door[0] } + Direction::Right => { self.anim_rect = state.constants.npc.n018_door[1] } _ => {} } } @@ -90,4 +106,68 @@ impl NPC { Ok(()) } + + pub(crate) fn tick_n020_computer(&mut self, state: &mut SharedGameState) -> GameResult { + Ok(()) + } + + pub(crate) fn tick_n027_death_trap(&mut self, state: &mut SharedGameState) -> GameResult { + if self.action_num == 0 { + self.action_num = 1; + self.anim_rect = state.constants.npc.n027_death_trap; + } + + Ok(()) + } + + pub(crate) fn tick_n032_life_capsule(&mut self, state: &mut SharedGameState) -> GameResult { + self.anim_counter = (self.anim_counter + 1) % 4; + self.anim_num = self.anim_counter / 2; + self.anim_rect = state.constants.npc.n032_life_capsule[self.anim_num as usize]; + + Ok(()) + } + + pub(crate) fn tick_n034_bed(&mut self, state: &mut SharedGameState) -> GameResult { + match self.direction { + Direction::Left => { self.anim_rect = state.constants.npc.n034_bed[0] } + Direction::Right => { self.anim_rect = state.constants.npc.n034_bed[1] } + _ => {} + } + + Ok(()) + } + + pub(crate) fn tick_n037_sign(&mut self, state: &mut SharedGameState) -> GameResult { + self.anim_counter = (self.anim_counter + 1) % 4; + self.anim_num = self.anim_counter / 2; + self.anim_rect = state.constants.npc.n037_sign[self.anim_num as usize]; + + Ok(()) + } + + pub(crate) fn tick_n038_fireplace(&mut self, state: &mut SharedGameState) -> GameResult { + match self.action_num { + 0 => { + self.anim_counter = (self.anim_counter + 1) % 16; + self.anim_num = self.anim_counter / 4; + self.anim_rect = state.constants.npc.n038_fireplace[self.anim_num as usize]; + } + 10 => {} + 11 => {} + _ => {} + } + + Ok(()) + } + + pub(crate) fn tick_n039_save_sign(&mut self, state: &mut SharedGameState) -> GameResult { + match self.direction { + Direction::Left => { self.anim_rect = state.constants.npc.n039_save_sign[0] } + Direction::Right => { self.anim_rect = state.constants.npc.n039_save_sign[1] } + _ => {} + } + + Ok(()) + } } diff --git a/src/npc/mod.rs b/src/npc/mod.rs index 533fa31..124c1b2 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -2,6 +2,7 @@ use std::collections::{BTreeSet, HashMap}; use std::io; use std::io::Cursor; +use bitvec::vec::BitVec; use byteorder::{LE, ReadBytesExt}; use crate::{bitfield, SharedGameState}; @@ -12,6 +13,7 @@ use crate::entity::GameEntity; use crate::frame::Frame; use crate::ggez::{Context, GameResult}; use crate::map::NPCData; +use crate::player::Player; use crate::str; pub mod misc; @@ -58,20 +60,28 @@ pub struct NPC { pub hit_bounds: Rect, pub action_num: u16, pub anim_num: u16, + pub flag_num: u16, pub event_num: u16, pub action_counter: u16, pub anim_counter: u16, pub anim_rect: Rect, } -impl GameEntity for NPC { - fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { +impl GameEntity<&mut Player> for NPC { + fn tick(&mut self, state: &mut SharedGameState, scene: &mut Player) -> GameResult { // maybe use macros? match self.npc_type { 0 => { NPC::tick_n000_null(self, state) } 16 => { NPC::tick_n016_save_point(self, state) } 17 => { NPC::tick_n017_health_refill(self, state) } 18 => { NPC::tick_n018_door(self, state) } + 20 => { NPC::tick_n020_computer(self, state) } + 27 => { NPC::tick_n027_death_trap(self, state) } + 32 => { NPC::tick_n032_life_capsule(self, state) } + 34 => { NPC::tick_n034_bed(self, state) } + 37 => { NPC::tick_n037_sign(self, state) } + 38 => { NPC::tick_n038_fireplace(self, state) } + 39 => { NPC::tick_n039_save_sign(self, state) } _ => { Ok(()) } } } @@ -132,10 +142,11 @@ impl NPCMap { target_y: 0, action_num: 0, anim_num: 0, + flag_num: data.flag_num, event_num: data.event_num, life: table.get_life(data.npc_type), cond: Condition(0x00), - flags: Flag(data.flag_id as u32), + flags: Flag(data.flag_num as u32), npc_flags: NPCFlag(data.flags), direction: Direction::Left, display_bounds: table.get_display_bounds(data.npc_type), @@ -151,6 +162,15 @@ impl NPCMap { self.npcs.get_mut(&data.id).unwrap() } + + pub fn remove_by_event(&mut self, event_num: u16, game_flags: &mut BitVec) { + for npc in self.npcs.values_mut() { + if npc.event_num == event_num { + npc.cond.set_alive(false); + game_flags.set(npc.flag_num as usize, true); + } + } + } } pub struct NPCTableEntry { @@ -275,7 +295,6 @@ impl NPCTable { } } - pub fn get_hit_bounds(&self, npc_type: u16) -> Rect { if let Some(npc) = self.entries.get(npc_type as usize) { Rect { diff --git a/src/player.rs b/src/player.rs index b600b31..4ef3920 100644 --- a/src/player.rs +++ b/src/player.rs @@ -501,8 +501,8 @@ impl Player { } } -impl GameEntity for Player { - fn tick(&mut self, state: &mut SharedGameState, _ctx: &mut Context) -> GameResult { +impl GameEntity<()> for Player { + fn tick(&mut self, state: &mut SharedGameState, _cust: ()) -> GameResult { if !self.cond.alive() { return Ok(()); } diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index 49ddf8e..84ceb84 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -417,11 +417,11 @@ impl Scene for GameScene { for npc_data in npcs.iter() { let npc = self.npc_map.create_npc_from_data(&state.npc_table, npc_data); if npc.npc_flags.appear_when_flag_set() { - if let Some(true) = state.game_flags.get(npc_data.flag_id as usize) { + if let Some(true) = state.game_flags.get(npc_data.flag_num as usize) { npc.cond.set_alive(true); } } else if npc.npc_flags.hide_unless_flag_set() { - if let Some(false) = state.game_flags.get(npc_data.flag_id as usize) { + if let Some(false) = state.game_flags.get(npc_data.flag_num as usize) { npc.cond.set_alive(true); } } else { @@ -461,20 +461,20 @@ impl Scene for GameScene { } if state.control_flags.flag_x01() { - self.player.tick(state, ctx)?; + self.player.tick(state, ())?; self.player.flags.0 = 0; state.tick_carets(); self.player.tick_map_collisions(state, &self.stage); self.player.tick_npc_collisions(state, &mut self.npc_map); - self.frame.update(state, &self.player, &self.stage); - } - - for npc_id in self.npc_map.npc_ids.iter() { - if let Some(npc) = self.npc_map.npcs.get_mut(npc_id) { - npc.tick(state, ctx)?; + for npc_id in self.npc_map.npc_ids.iter() { + if let Some(npc) = self.npc_map.npcs.get_mut(npc_id) { + npc.tick(state, &mut self.player)?; + } } + + self.frame.update(state, &self.player, &self.stage); } if state.control_flags.control_enabled() { @@ -518,6 +518,7 @@ impl Scene for GameScene { self.draw_hud(state, ctx)?; } + self.draw_fade(state, ctx)?; if self.map_name_counter > 0 { let width = state.font.text_width(self.stage.data.name.chars(), &state.constants); state.font.draw_text(self.stage.data.name.chars(), @@ -525,7 +526,6 @@ impl Scene for GameScene { &state.constants, &mut state.texture_set, ctx)?; } - self.draw_fade(state, ctx)?; self.draw_text_boxes(state, ctx)?; self.draw_number(state.canvas_size.0 - 8.0, 8.0, timer::fps(ctx) as usize, Alignment::Right, state, ctx)?; diff --git a/src/text_script.rs b/src/text_script.rs index a1b78b8..00d25d2 100644 --- a/src/text_script.rs +++ b/src/text_script.rs @@ -552,6 +552,7 @@ impl TextScriptVM { } OpCode::MLp => { let life = read_cur_varint(&mut cursor)? as usize; + game_scene.player.life += life; game_scene.player.max_life += life; exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); @@ -664,6 +665,13 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } + OpCode::DNP => { + let event_num = read_cur_varint(&mut cursor)? as u16; + + game_scene.npc_map.remove_by_event(event_num, &mut state.game_flags); + + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } // unimplemented opcodes // Zero operands OpCode::AEp | OpCode::CAT | OpCode::CIL | OpCode::CPS | @@ -677,7 +685,7 @@ impl TextScriptVM { } // One operand codes OpCode::BOA | OpCode::BSL | OpCode::FOB | OpCode::FOM | OpCode::UNI | - OpCode::MYB | OpCode::GIT | OpCode::NUM | OpCode::DNA | OpCode::DNP | + OpCode::MYB | OpCode::GIT | OpCode::NUM | OpCode::DNA | OpCode::MPp | OpCode::SKm | OpCode::SKp | OpCode::EQp | OpCode::EQm | OpCode::ITp | OpCode::ITm | OpCode::AMm | OpCode::UNJ | OpCode::MPJ | OpCode::YNJ | OpCode::XX1 | OpCode::SIL | OpCode::LIp | OpCode::SOU |