diff --git a/src/scene/title_scene.rs b/src/scene/title_scene.rs index bbe7068..90c4ea3 100644 --- a/src/scene/title_scene.rs +++ b/src/scene/title_scene.rs @@ -222,6 +222,7 @@ impl Scene for TitleScene { }, CurrentMenu::StartGame => { if self.tick == 10 { + state.reset_skip_flags(); state.start_new_game(ctx)?; } } diff --git a/src/shared_game_state.rs b/src/shared_game_state.rs index 6ed9da2..feeefbb 100644 --- a/src/shared_game_state.rs +++ b/src/shared_game_state.rs @@ -30,6 +30,7 @@ use crate::stage::StageData; use crate::str; use crate::text_script::{ScriptMode, TextScriptExecutionState, TextScriptVM}; use crate::texture_set::TextureSet; +use bitvec::array::BitArray; #[derive(PartialEq, Eq, Copy, Clone)] pub enum TimingMode { @@ -89,6 +90,7 @@ pub struct SharedGameState { pub timing_mode: TimingMode, pub control_flags: ControlFlags, pub game_flags: BitVec, + pub skip_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, @@ -160,6 +162,7 @@ impl SharedGameState { timing_mode: TimingMode::_50Hz, control_flags: ControlFlags(0), game_flags: bitvec::bitvec![0; 8000], + skip_flags: bitvec::bitvec![0; 64], fade_state: FadeState::Hidden, game_rng: XorShift::new(0), effect_rng: XorShift::new(123), @@ -364,4 +367,24 @@ impl SharedGameState { false } } + + pub fn reset_skip_flags(&mut self) { + self.skip_flags = bitvec::bitvec![0; 64]; + } + + pub fn set_skip_flag(&mut self, id: usize, value: bool) { + if id < self.skip_flags.len() { + self.skip_flags.set(id, value); + } else { + log::warn!("Attempted to set an out-of-bounds skip flag {}:", id); + } + } + + pub fn get_skip_flag(&self, id: usize) -> bool { + if let Some(flag) = self.skip_flags.get(id) { + *flag + } else { + false + } + } } diff --git a/src/text_script.rs b/src/text_script.rs index 1555e6f..1c69a64 100644 --- a/src/text_script.rs +++ b/src/text_script.rs @@ -262,16 +262,18 @@ pub enum OpCode { SSS, // ---- Cave Story+ specific opcodes ---- - /// { - state.start_intro(ctx)?; + state.reset(); + state.start_new_game(ctx)?; break; } } @@ -937,8 +940,13 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::WaitInput(event, cursor.position() as u32, 0); } OpCode::FLp | OpCode::FLm => { - let flag_num = read_cur_varint(&mut cursor)? as usize; - state.game_flags.set(flag_num, op == OpCode::FLp); + let flag_num = read_cur_varint(&mut cursor)? as u16; + state.set_flag(flag_num as usize, op == OpCode::FLp); + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } + OpCode::SKp | OpCode::SKm => { + let flag_num = read_cur_varint(&mut cursor)? as u16; + state.set_skip_flag(flag_num as usize, op == OpCode::SKp); exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } OpCode::FFm => { @@ -1023,6 +1031,17 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } } + OpCode::SKJ => { + let flag_id = read_cur_varint(&mut cursor)? as u16; + let event_num = read_cur_varint(&mut cursor)? as u16; + + if state.get_skip_flag(flag_id as usize) { + state.textscript_vm.clear_text_box(); + exec_state = TextScriptExecutionState::Running(event_num, 0); + } else { + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } + } OpCode::EVE => { let event_num = read_cur_varint(&mut cursor)? as u16; @@ -1035,7 +1054,7 @@ impl TextScriptVM { let saved_state = TextScriptExecutionState::Running(event, cursor.position() as u32); state.textscript_vm.stack.push(saved_state); - // TODO: it's a jump but we don't know if it cleans the textbox yet. + state.textscript_vm.clear_text_box(); exec_state = TextScriptExecutionState::Running(event_num, 0); } OpCode::POP => { @@ -1231,11 +1250,30 @@ impl TextScriptVM { partner.x = executor.x + if param == 0 { -16 * 0x200 } else { 16 * 0x200 }; partner.y = executor.y; } - _ => { - log::warn!("stub: <2MV unknown param"); + 2..=10 => { + log::warn!("<2MV unknown param"); + } + // what the fuck + i => { + let distance = i as i32 / 10; + + partner.vel_x = 0; + partner.vel_y = 0; + partner.x = executor.x + if (param % 10) == 1 { distance * 0x200 } else { -distance * 0x200 }; + partner.y = executor.y; } } + let mut npc = NPC::create(4, &state.npc_table); + npc.cond.set_alive(true); + 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)?; + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } OpCode::UNI => { @@ -1515,7 +1553,7 @@ impl TextScriptVM { let item_id = read_cur_varint(&mut cursor)? as u16; state.sound_manager.play_sfx(38); - + if !game_scene.inventory_player1.has_item(item_id) { game_scene.inventory_player1.add_item(item_id); } @@ -1657,8 +1695,6 @@ impl TextScriptVM { } // One operand codes OpCode::MPp - | OpCode::SKm - | OpCode::SKp | OpCode::UNJ | OpCode::MPJ | OpCode::XX1 @@ -1669,15 +1705,6 @@ impl TextScriptVM { log::warn!("unimplemented opcode: {:?} {}", op, par_a); - exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); - } - // Two operand codes - OpCode::SKJ => { - let par_a = read_cur_varint(&mut cursor)?; - let par_b = read_cur_varint(&mut cursor)?; - - log::warn!("unimplemented opcode: {:?} {} {}", op, par_a, par_b); - exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } }