diff --git a/src/live_debugger/command_line.rs b/src/live_debugger/command_line.rs index 5bfedf3..6bfb957 100644 --- a/src/live_debugger/command_line.rs +++ b/src/live_debugger/command_line.rs @@ -3,10 +3,11 @@ use num_traits::FromPrimitive; use crate::framework::error::{GameError::CommandLineError, GameResult}; use crate::npc::NPC; use crate::scene::game_scene::GameScene; +use crate::scripting::tsc::text_script::{ScriptMode, TextScript, TextScriptEncoding}; use crate::shared_game_state::SharedGameState; use crate::weapon::WeaponType; -#[derive(Clone, Copy)] +#[derive(Clone)] pub enum CommandLineCommand { AddItem(u16), RemoveItem(u16), @@ -20,6 +21,7 @@ pub enum CommandLineCommand { RemoveXP(u16), SetMaxHP(u16), SpawnNPC(u16), + TSC(String), } impl CommandLineCommand { @@ -30,6 +32,10 @@ impl CommandLineCommand { let command = components[0]; + if command.starts_with("<") { + return Some(CommandLineCommand::TSC(components.join(" ").replace("\\n", "\n"))); + } + match command.replacen("/", "", 1).as_str() { "add_item" => { if components.len() < 2 { @@ -138,6 +144,14 @@ impl CommandLineCommand { return Some(CommandLineCommand::SpawnNPC(npc_id.unwrap())); } } + "tsc" => { + if components.len() < 2 { + return None; + } + + let script = components[1..].join(" ").replace("\\n", "\n"); + return Some(CommandLineCommand::TSC(script)); + } _ => return None, } @@ -145,7 +159,7 @@ impl CommandLineCommand { } pub fn execute(&mut self, game_scene: &mut GameScene, state: &mut SharedGameState) -> GameResult { - match *self { + match self.clone() { CommandLineCommand::AddItem(item_id) => { game_scene.inventory_player1.add_item(item_id); } @@ -213,6 +227,19 @@ impl CommandLineCommand { npc.x = game_scene.player1.x + game_scene.player1.direction.vector_x() * (0x2000 * 3); game_scene.npc_list.spawn(0x100, npc)?; } + CommandLineCommand::TSC(script) => { + log::info!("Executing TSC script: {}", format!("#9999\n{}", script)); + match TextScript::compile(format!("#9999\n{}", script).as_bytes(), true, TextScriptEncoding::UTF8) { + Ok(text_script) => { + state.textscript_vm.set_debug_script(text_script); + state.textscript_vm.set_mode(ScriptMode::Debug); + state.textscript_vm.start_script(9999); + } + Err(err) => { + return Err(CommandLineError(format!("Error compiling TSC: {}", err))); + } + }; + } } Ok(()) @@ -233,6 +260,7 @@ impl CommandLineCommand { CommandLineCommand::RemoveXP(xp_count) => format!("/remove_xp {}", xp_count), CommandLineCommand::SetMaxHP(hp_count) => format!("/set_max_hp {}", hp_count), CommandLineCommand::SpawnNPC(npc_id) => format!("/spawn_npc {}", npc_id), + CommandLineCommand::TSC(script) => format!("/tsc {}", script.replace("\n", "\\n")), } } @@ -254,6 +282,7 @@ impl CommandLineCommand { CommandLineCommand::RemoveXP(xp_count) => format!("Removed {} XP from current weapon.", xp_count), CommandLineCommand::SetMaxHP(hp_count) => format!("Set max HP of player to {}.", hp_count), CommandLineCommand::SpawnNPC(npc_id) => format!("Spawned NPC ID {} in front of player.", npc_id), + CommandLineCommand::TSC(_) => "Executed TSC script.".to_string(), } } } @@ -283,7 +312,7 @@ impl CommandLineParser { match command { Some(command) => { - self.command_history.push(command); + self.command_history.push(command.clone()); self.cursor = self.command_history.len() - 1; Some(command) diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index 8d66cc5..f665968 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -1806,7 +1806,7 @@ impl Scene for GameScene { self.map_system.tick(state, ctx, &self.stage, [&self.player1, &self.player2])?; match state.textscript_vm.mode { - ScriptMode::Map => { + ScriptMode::Map | ScriptMode::Debug => { TextScriptVM::run(state, self, ctx)?; match state.textscript_vm.state { @@ -1991,7 +1991,7 @@ impl Scene for GameScene { } match state.textscript_vm.mode { - ScriptMode::Map if state.control_flags.control_enabled() => { + ScriptMode::Map | ScriptMode::Debug if state.control_flags.control_enabled() => { self.hud_player1.draw(state, ctx, &self.frame)?; self.hud_player2.draw(state, ctx, &self.frame)?; self.boss_life_bar.draw(state, ctx, &self.frame)?; @@ -2158,11 +2158,11 @@ impl Scene for GameScene { self.map_system.draw(state, ctx, &self.stage, [&self.player1, &self.player2])?; self.fade.draw(state, ctx, &self.frame)?; - if state.textscript_vm.mode == ScriptMode::Map { + if state.textscript_vm.mode == ScriptMode::Map || state.textscript_vm.mode == ScriptMode::Debug { self.nikumaru.draw(state, ctx, &self.frame)?; } - if state.textscript_vm.mode == ScriptMode::Map + if (state.textscript_vm.mode == ScriptMode::Map || state.textscript_vm.mode == ScriptMode::Debug) && state.textscript_vm.state != TextScriptExecutionState::MapSystem && self.map_name_counter > 0 { diff --git a/src/scripting/tsc/text_script.rs b/src/scripting/tsc/text_script.rs index 1482f75..b4b73c3 100644 --- a/src/scripting/tsc/text_script.rs +++ b/src/scripting/tsc/text_script.rs @@ -68,6 +68,7 @@ pub enum ScriptMode { Map, Inventory, StageSelect, + Debug, } impl Not for ConfirmSelection { @@ -145,12 +146,20 @@ pub struct Scripts { pub inventory_script: TextScript, /// StageSelect.tsc - used by teleport target selector pub stage_select_script: TextScript, + /// Debug TSC sink - used by the debug command line + pub debug_script: TextScript, } impl Scripts { pub fn find_script(&self, mode: ScriptMode, event_num: u16) -> Option<&Vec> { match mode { - ScriptMode::Map => { + ScriptMode::Map | ScriptMode::Debug => { + if mode == ScriptMode::Debug { + if let Some(tsc) = self.debug_script.event_map.get(&event_num) { + return Some(tsc); + } + } + if let Some(tsc) = self.scene_script.event_map.get(&event_num) { return Some(tsc); } else if let Some(tsc) = self.global_script.event_map.get(&event_num) { @@ -181,6 +190,7 @@ impl TextScriptVM { scene_script: TextScript::new(), inventory_script: TextScript::new(), stage_select_script: TextScript::new(), + debug_script: TextScript::new(), })), state: TextScriptExecutionState::Ended, stack: Vec::with_capacity(6), @@ -236,6 +246,11 @@ impl TextScriptVM { scripts.stage_select_script = script; } + pub fn set_debug_script(&mut self, script: TextScript) { + let mut scripts = self.scripts.borrow_mut(); + scripts.debug_script = script; + } + pub fn set_substitution_rect_map(&mut self, rect_map: HashMap>) { self.substitution_rect_map = rect_map; } @@ -670,6 +685,10 @@ impl TextScriptVM { state.textscript_vm.flags.set_background_visible(false); state.textscript_vm.stack.clear(); + if state.textscript_vm.mode == ScriptMode::Debug { + state.textscript_vm.set_mode(ScriptMode::Map); + } + game_scene.player1.cond.set_interacted(false); game_scene.player2.cond.set_interacted(false);