//! Seperate out debugging uis/plugins in it's own module for cleanliness. use std::{fs, path::PathBuf}; use bevy::prelude::*; use bevy_inspector_egui::bevy_egui::EguiPlugin; use clap::Subcommand; use console::{ConsoleLog, exec_script}; use crate::AppState; mod console; /// Debug system set. #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] pub struct DebugSet; pub fn plugin(app: &mut App) { app.add_plugins(EguiPlugin { enable_multipass_for_primary_context: true, }) .add_event::() .init_state::() .add_systems( Update, ( handle_debug_logs, handle_enable_debug_mode.run_if(not(in_state(DebugMode::Enabled))), start_game, run_file.pipe(exec_script), ) .in_set(DebugSet), ) .add_systems(Startup, startup_file.pipe(exec_script)) .add_plugins(console::plugin); } #[derive(Event, Debug, Subcommand, PartialEq, Eq)] enum DebugEvent { /// Close the debug console. #[command(name = "close", aliases = ["close-console"])] CloseDebugConsole, /// Output a string to the console. /// /// Needed for logging in startup scripts, since the console isn't initialized yet. #[command(name = "echo", aliases = ["print", "print-to-console"])] PrintToConsole { /// Print as error. #[arg(short, long)] error: bool, /// The text to be printed in the console. text: String, }, /// Log as INFO or ERROR to stdout. Log { /// Log as error. #[arg(short, long)] error: bool, /// The text to be logged. text: String, }, /// Start the game StartGame, /// Run a tx file (the games debug scripting lang :3) #[command(name = "run")] RunFile { file: PathBuf }, /// Enable debug mode. /// Will cause the game to behave differently (mostly, not do things by default it otherwise would.) /// /// Do note while all of this is part of the debug tooling, debug mode just gives the debug tools more controls of the game. /// /// Read: In debug mode, a lot of features of the normal game aren't enabled, and might have to be turned on explicitly. /// This is to allow for easier testing. The console etc. will still stay available regardless, just might complain that debug mode isn't enabled on some commands. /// /// For best results, enable debug mode before starting the game. /// /// Debug mode cannot be disabled. Just restart the game lmao. /// /// Current debug mode behaviour: /// - Nothing #[command(name = "enable-debug", aliases = ["debug-enable"])] EnableDebugMode, } fn start_game( mut dbg_reader: EventReader, mut log: ResMut, cur_state: Res>, mut next_state: ResMut>, ) { for ev in dbg_reader.read() { if *ev == DebugEvent::StartGame { if *cur_state == AppState::Ingame { log.err("Can't start game since it's already started."); } else { log.write("Starting game..."); next_state.set(AppState::Ingame); } } } } #[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash, Reflect)] pub enum DebugMode { #[default] Disabled, Enabled, } fn handle_enable_debug_mode( mut dbg_reader: EventReader, mut debug_state: ResMut>, ) { for ev in dbg_reader.read() { if matches!(ev, DebugEvent::EnableDebugMode) { info!("Enabling debug mode. There be demons (hopefully the cute ones :3)!"); debug_state.set(DebugMode::Enabled) } } } fn handle_debug_logs(mut dbg_reader: EventReader) { for ev in dbg_reader.read() { if let DebugEvent::Log { error, text } = ev { if *error { error!("{text}") } else { info!("{text}") }; } } } fn run_file(mut dbg_reader: EventReader) -> Option<(String, String)> { for ev in dbg_reader.read() { if let DebugEvent::RunFile { file } = ev { let f = std::fs::read_to_string(file); if let Ok(f) = f { return Some((f, String::from(file.file_name().unwrap().to_string_lossy()))); } } } None } fn startup_file() -> Option<(String, String)> { let path: PathBuf = "./startup.tx".into(); fs::exists(&path).unwrap_or(false).then(|| { Some(( fs::read_to_string(&path).unwrap(), path.file_name().unwrap().to_string_lossy().to_string(), )) })? }