diff --git a/src/profile.rs b/src/profile.rs index 572d3b5..896780b 100644 --- a/src/profile.rs +++ b/src/profile.rs @@ -1,6 +1,6 @@ use std::io; -use byteorder::{ReadBytesExt, WriteBytesExt, BE, LE}; +use byteorder::{BE, LE, ReadBytesExt, WriteBytesExt}; use num_traits::{clamp, FromPrimitive}; use crate::common::{Direction, FadeState}; @@ -43,6 +43,7 @@ pub struct GameProfile { pub weapon_data: [WeaponData; 8], pub items: [u32; 32], pub teleporter_slots: [TeleporterSlotData; 8], + pub map_flags: [u8; 128], pub flags: [u8; 1000], } @@ -93,6 +94,10 @@ impl GameProfile { state.teleporter_slots.push((slot.index as u16, slot.event_num as u16)); } + for (idx, &flag) in self.map_flags.iter().enumerate() { + state.set_map_flag(idx, flag != 0); + } + for (idx, &flags) in self.flags.iter().enumerate() { if flags & 0b00000001 != 0 { state.game_flags.set(idx * 8, true); @@ -197,6 +202,15 @@ impl GameProfile { } } + let mut map_flags = [0u8; 128]; + for (idx, map_flag) in state.map_flags.iter().enumerate() { + if let Some(out) = map_flags.get_mut(idx) { + *out = if *map_flag { 1 } else { 0 }; + } else { + break; + } + } + let mut bidx = 0; let mut flags = [0u8; 1000]; for bits in state.game_flags.as_raw_slice() { @@ -204,6 +218,8 @@ impl GameProfile { for b in bytes.iter() { if let Some(out) = flags.get_mut(bidx) { *out = *b; + } else { + break; } bidx += 1; } @@ -226,6 +242,7 @@ impl GameProfile { weapon_data, items, teleporter_slots, + map_flags, flags, } } @@ -333,8 +350,8 @@ impl GameProfile { slot.event_num = data.read_u32::()?; } - let mut something = [0u8; 0x80]; - data.read_exact(&mut something)?; + let mut map_flags = [0u8; 0x80]; + data.read_exact(&mut map_flags)?; if data.read_u32::()? != 0x464c4147 { return Err(ResourceLoadError(str!("Invalid FLAG signature"))); @@ -360,6 +377,7 @@ impl GameProfile { weapon_data, items, teleporter_slots, + map_flags, flags, }) } diff --git a/src/shared_game_state.rs b/src/shared_game_state.rs index feeefbb..0362dda 100644 --- a/src/shared_game_state.rs +++ b/src/shared_game_state.rs @@ -91,6 +91,7 @@ pub struct SharedGameState { pub control_flags: ControlFlags, pub game_flags: BitVec, pub skip_flags: BitVec, + pub map_flags: BitVec, pub fade_state: FadeState, /// RNG used by game state, using it for anything else might cause unintended side effects and break replays. pub game_rng: XorShift, @@ -163,6 +164,7 @@ impl SharedGameState { control_flags: ControlFlags(0), game_flags: bitvec::bitvec![0; 8000], skip_flags: bitvec::bitvec![0; 64], + map_flags: bitvec::bitvec![0; 64], fade_state: FadeState::Hidden, game_rng: XorShift::new(0), effect_rng: XorShift::new(123), @@ -236,6 +238,8 @@ impl SharedGameState { next_scene.player1.cond.set_alive(true); next_scene.player1.x = 10 * 16 * 0x200; next_scene.player1.y = 8 * 16 * 0x200; + + self.reset_map_flags(); self.fade_state = FadeState::Hidden; self.textscript_vm.state = TextScriptExecutionState::Running(200, 0); @@ -253,6 +257,8 @@ impl SharedGameState { next_scene.player1.x = 3 * 16 * 0x200; next_scene.player1.y = 3 * 16 * 0x200; next_scene.intro_mode = true; + + self.reset_map_flags(); self.fade_state = FadeState::Hidden; self.textscript_vm.state = TextScriptExecutionState::Running(100, 0); @@ -387,4 +393,24 @@ impl SharedGameState { false } } + + pub fn reset_map_flags(&mut self) { + self.map_flags = bitvec::bitvec![0; 128]; + } + + pub fn set_map_flag(&mut self, id: usize, value: bool) { + if id < self.map_flags.len() { + self.map_flags.set(id, value); + } else { + log::warn!("Attempted to set an out-of-bounds map flag {}:", id); + } + } + + pub fn get_map_flag(&self, id: usize) -> bool { + if let Some(flag) = self.map_flags.get(id) { + *flag + } else { + false + } + } } diff --git a/src/text_script.rs b/src/text_script.rs index 1c69a64..99ff6cc 100644 --- a/src/text_script.rs +++ b/src/text_script.rs @@ -974,6 +974,16 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } } + OpCode::MPJ => { + let event_num = read_cur_varint(&mut cursor)? as u16; + + if state.get_map_flag(game_scene.stage_id) { + state.textscript_vm.clear_text_box(); + exec_state = TextScriptExecutionState::Running(event_num, 0); + } else { + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } + } OpCode::ITJ => { let item_id = read_cur_varint(&mut cursor)? as u16; let event_num = read_cur_varint(&mut cursor)? as u16; @@ -1090,8 +1100,8 @@ impl TextScriptVM { npc.x = pos_x as i32 * 16 * 0x200; npc.y = pos_y as i32 * 16 * 0x200; - game_scene.npc_list.spawn(0x100, npc.clone())?; - game_scene.npc_list.spawn(0x100, npc)?; + let _ = game_scene.npc_list.spawn(0x100, npc.clone()); + let _ = game_scene.npc_list.spawn(0x100, npc); } exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); @@ -1269,10 +1279,10 @@ impl TextScriptVM { npc.x = partner.x; npc.y = partner.y; - game_scene.npc_list.spawn(0x100, npc.clone())?; - game_scene.npc_list.spawn(0x100, npc.clone())?; - game_scene.npc_list.spawn(0x100, npc.clone())?; - game_scene.npc_list.spawn(0x100, npc)?; + let _ = game_scene.npc_list.spawn(0x100, npc.clone()); + let _ = game_scene.npc_list.spawn(0x100, npc.clone()); + let _ = game_scene.npc_list.spawn(0x100, npc.clone()); + let _ = game_scene.npc_list.spawn(0x100, npc); exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } @@ -1537,7 +1547,7 @@ impl TextScriptVM { npc.direction = direction; } - game_scene.npc_list.spawn(0x100, npc)?; + let _ = game_scene.npc_list.spawn(0x100, npc); exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } @@ -1677,6 +1687,13 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::LoadProfile; } + OpCode::MPp => { + let stage_id = read_cur_varint(&mut cursor)? as u16; + + state.set_map_flag(stage_id as usize, true); + + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } // unimplemented opcodes // Zero operands OpCode::CIL @@ -1694,9 +1711,7 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } // One operand codes - OpCode::MPp - | OpCode::UNJ - | OpCode::MPJ + OpCode::UNJ | OpCode::XX1 | OpCode::SIL | OpCode::SSS