2025-06-18 17:13:52 +02:00
//! Seperate out debugging uis/plugins in it's own module for cleanliness.
2025-06-18 19:29:45 +02:00
use std ::{ fs , path ::PathBuf } ;
2025-06-18 17:13:52 +02:00
use bevy ::prelude ::* ;
use bevy_inspector_egui ::bevy_egui ::EguiPlugin ;
use clap ::Subcommand ;
2025-06-18 19:29:45 +02:00
use console ::{ ConsoleLog , exec_script } ;
2025-06-18 17:13:52 +02:00
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 ::< DebugEvent > ( )
2025-06-21 18:19:28 +02:00
. init_state ::< DebugMode > ( )
2025-06-18 19:29:45 +02:00
. add_systems (
Update ,
2025-06-21 18:19:28 +02:00
(
handle_debug_logs ,
handle_enable_debug_mode . run_if ( not ( in_state ( DebugMode ::Enabled ) ) ) ,
start_game ,
run_file . pipe ( exec_script ) ,
)
. in_set ( DebugSet ) ,
2025-06-18 19:29:45 +02:00
)
. add_systems ( Startup , startup_file . pipe ( exec_script ) )
2025-06-18 17:13:52 +02:00
. 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.
2025-06-18 19:29:45 +02:00
///
/// Needed for logging in startup scripts, since the console isn't initialized yet.
2025-06-18 17:13:52 +02:00
#[ command(name = " echo " , aliases = [ " print " , " print-to-console " ] ) ]
PrintToConsole {
2025-06-18 19:29:45 +02:00
/// Print as error.
2025-06-18 17:13:52 +02:00
#[ arg(short, long) ]
error : bool ,
/// The text to be printed in the console.
text : String ,
} ,
2025-06-18 19:29:45 +02:00
/// Log as INFO or ERROR to stdout.
Log {
/// Log as error.
#[ arg(short, long) ]
error : bool ,
/// The text to be logged.
text : String ,
} ,
2025-06-18 17:13:52 +02:00
/// Start the game
StartGame ,
2025-06-18 19:29:45 +02:00
/// Run a tx file (the games debug scripting lang :3)
#[ command(name = " run " ) ]
RunFile { file : PathBuf } ,
2025-06-21 18:19:28 +02:00
/// 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 ,
2025-06-18 17:13:52 +02:00
}
fn start_game (
mut dbg_reader : EventReader < DebugEvent > ,
mut log : ResMut < ConsoleLog > ,
cur_state : Res < State < AppState > > ,
mut next_state : ResMut < NextState < AppState > > ,
) {
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 ) ;
}
}
}
}
2025-06-18 19:29:45 +02:00
2025-06-21 18:19:28 +02:00
#[ derive(States, Default, Debug, Clone, PartialEq, Eq, Hash, Reflect) ]
pub enum DebugMode {
#[ default ]
Disabled ,
Enabled ,
}
fn handle_enable_debug_mode (
mut dbg_reader : EventReader < DebugEvent > ,
mut debug_state : ResMut < NextState < DebugMode > > ,
) {
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 )
}
}
}
2025-06-18 19:29:45 +02:00
fn handle_debug_logs ( mut dbg_reader : EventReader < DebugEvent > ) {
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 < DebugEvent > ) -> 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 ( ) ,
) )
} ) ?
}