From 9ecb38b442d72b72c64913817c504b0708f9705e Mon Sep 17 00:00:00 2001 From: Kristofers Solo Date: Sun, 8 Dec 2024 18:02:06 +0200 Subject: [PATCH] refactor(dev-tools): reorganize dev tools module code --- src/dev_tools.rs | 126 ------------------------------ src/dev_tools/mod.rs | 4 + src/dev_tools/plugin.rs | 36 +++++++++ src/dev_tools/ui/inspector.rs | 20 +++++ src/dev_tools/ui/maze_controls.rs | 117 +++++++++++++++++++++++++++ src/dev_tools/ui/mod.rs | 5 ++ src/lib.rs | 2 +- src/maze/mod.rs | 4 +- src/maze/plugin.rs | 3 +- src/maze/resources.rs | 4 + src/screens/gameplay.rs | 2 +- 11 files changed, 192 insertions(+), 131 deletions(-) delete mode 100644 src/dev_tools.rs create mode 100644 src/dev_tools/mod.rs create mode 100644 src/dev_tools/plugin.rs create mode 100644 src/dev_tools/ui/inspector.rs create mode 100644 src/dev_tools/ui/maze_controls.rs create mode 100644 src/dev_tools/ui/mod.rs diff --git a/src/dev_tools.rs b/src/dev_tools.rs deleted file mode 100644 index 52bea5c..0000000 --- a/src/dev_tools.rs +++ /dev/null @@ -1,126 +0,0 @@ -//! Development tools for the game. This plugin is only enabled in dev builds. - -use bevy::{ - dev_tools::{ - states::log_transitions, - ui_debug_overlay::{DebugUiPlugin, UiDebugOptions}, - }, - input::common_conditions::input_just_pressed, - prelude::*, - window::PrimaryWindow, -}; - -use bevy_inspector_egui::{bevy_egui::EguiContext, DefaultInspectorConfigPlugin}; - -use crate::{ - maze::{events::RecreateMazeEvent, MazeConfig}, - screens::Screen, -}; -use bevy_egui::{ - egui::{self, Button, Color32, DragValue, ScrollArea}, - EguiPlugin, -}; - -pub(super) fn plugin(app: &mut App) { - // Log `Screen` state transitions. - app.add_systems(Update, log_transitions::) - .add_plugins(EguiPlugin) - .add_plugins(DebugUiPlugin) - .add_plugins(DefaultInspectorConfigPlugin) - .add_systems(Update, inspector_ui) - // Toggle the debug overlay for UI. - .add_systems( - Update, - toggle_debug_ui.run_if(input_just_pressed(TOGGLE_KEY)), - ); -} - -const TOGGLE_KEY: KeyCode = KeyCode::Backquote; - -fn toggle_debug_ui(mut options: ResMut) { - options.toggle(); -} - -fn inspector_ui(world: &mut World) { - let Ok(egui_context) = world - .query_filtered::<&mut EguiContext, With>() - .get_single(world) - else { - return; - }; - - let mut egui_context = egui_context.clone(); - - egui::Window::new("UI").show(egui_context.get_mut(), |ui| { - ScrollArea::vertical().show(ui, |ui| { - bevy_inspector_egui::bevy_inspector::ui_for_world(world, ui); - }); - }); - - egui::Window::new("Maze Controls").show(egui_context.get_mut(), |ui| { - if let Some(mut maze_config) = world.get_resource_mut::() { - ui.heading("Maze Configuration"); - - // radius controls - ui.horizontal(|ui| { - ui.label("Radius:"); - ui.add( - DragValue::new(&mut maze_config.radius) - .speed(1) - .range(1..=100), - ); - }); - - // height controls - ui.horizontal(|ui| { - ui.label("Height:"); - ui.add( - DragValue::new(&mut maze_config.height) - .speed(0.5) - .range(1.0..=50.), - ); - }); - - // start position - ui.horizontal(|ui| { - ui.label("Start Position:"); - ui.add( - DragValue::new(&mut maze_config.start_pos.x) - .speed(1) - .prefix("x: "), - ); - ui.add( - DragValue::new(&mut maze_config.start_pos.y) - .speed(1) - .prefix("y: "), - ); - }); - - // end position - ui.horizontal(|ui| { - ui.label("End Position:"); - ui.add( - DragValue::new(&mut maze_config.end_pos.x) - .speed(1) - .prefix("x: "), - ); - ui.add( - DragValue::new(&mut maze_config.end_pos.y) - .speed(1) - .prefix("y: "), - ); - }); - - ui.add_space(8.); - - let button = Button::new("Recreate maze").fill(Color32::from_rgb(108, 108, 108)); - if ui.add(button).clicked() { - if let Some(mut event_writer) = - world.get_resource_mut::>() - { - event_writer.send(RecreateMazeEvent { floor: 1 }); - } - } - } - }); -} diff --git a/src/dev_tools/mod.rs b/src/dev_tools/mod.rs new file mode 100644 index 0000000..ad48bfc --- /dev/null +++ b/src/dev_tools/mod.rs @@ -0,0 +1,4 @@ +mod plugin; +mod ui; + +pub use plugin::DevToolsPlugin; diff --git a/src/dev_tools/plugin.rs b/src/dev_tools/plugin.rs new file mode 100644 index 0000000..660cf20 --- /dev/null +++ b/src/dev_tools/plugin.rs @@ -0,0 +1,36 @@ +use crate::screens::Screen; +use bevy::{ + dev_tools::{ + states::log_transitions, + ui_debug_overlay::{DebugUiPlugin, UiDebugOptions}, + }, + input::common_conditions::input_just_pressed, + prelude::*, +}; +use bevy_egui::EguiPlugin; +use bevy_inspector_egui::DefaultInspectorConfigPlugin; + +use super::ui::{inspector_ui, maze_controls_ui}; + +#[derive(Debug)] +pub struct DevToolsPlugin; + +impl Plugin for DevToolsPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Update, log_transitions::) + .add_plugins(EguiPlugin) + .add_plugins(DebugUiPlugin) + .add_plugins(DefaultInspectorConfigPlugin) + .add_systems(Update, (inspector_ui, maze_controls_ui)) + .add_systems( + Update, + toggle_debug_ui.run_if(input_just_pressed(TOGGLE_KEY)), + ); + } +} + +const TOGGLE_KEY: KeyCode = KeyCode::Backquote; + +fn toggle_debug_ui(mut options: ResMut) { + options.toggle(); +} diff --git a/src/dev_tools/ui/inspector.rs b/src/dev_tools/ui/inspector.rs new file mode 100644 index 0000000..04c8212 --- /dev/null +++ b/src/dev_tools/ui/inspector.rs @@ -0,0 +1,20 @@ +use bevy::{prelude::*, window::PrimaryWindow}; +use bevy_egui::egui::{self, ScrollArea}; +use bevy_inspector_egui::bevy_egui::EguiContext; + +pub(crate) fn inspector_ui(world: &mut World) { + let Ok(egui_context) = world + .query_filtered::<&mut EguiContext, With>() + .get_single(world) + else { + return; + }; + + let mut egui_context = egui_context.clone(); + + egui::Window::new("UI").show(egui_context.get_mut(), |ui| { + ScrollArea::vertical().show(ui, |ui| { + bevy_inspector_egui::bevy_inspector::ui_for_world(world, ui); + }); + }); +} diff --git a/src/dev_tools/ui/maze_controls.rs b/src/dev_tools/ui/maze_controls.rs new file mode 100644 index 0000000..f5b0a0a --- /dev/null +++ b/src/dev_tools/ui/maze_controls.rs @@ -0,0 +1,117 @@ +use std::ops::RangeInclusive; + +use bevy::{prelude::*, window::PrimaryWindow}; +use hexx::Hex; +use rand::{thread_rng, Rng}; + +use crate::maze::{events::RecreateMazeEvent, MazeConfig, MazePluginLoaded}; +use bevy_egui::{ + egui::{self, emath::Numeric, DragValue, TextEdit, Ui}, + EguiContext, +}; + +pub(crate) fn maze_controls_ui(world: &mut World) { + if world.get_resource::().is_none() { + return; + } + + let Ok(egui_context) = world + .query_filtered::<&mut EguiContext, With>() + .get_single(world) + else { + return; + }; + + let mut egui_context = egui_context.clone(); + + egui::Window::new("Maze Controls").show(egui_context.get_mut(), |ui| { + if let Some(mut maze_config) = world.get_resource_mut::() { + let mut changed = false; + ui.heading("Maze Configuration"); + + changed |= add_seed_control(ui, &mut maze_config.seed); + + changed |= add_drag_value_control(ui, "Radius:", &mut maze_config.radius, 1.0, 1..=100); + changed |= + add_drag_value_control(ui, "Height:", &mut maze_config.height, 0.5, 1.0..=50.0); + + changed |= add_position_control(ui, "Start Position:", &mut maze_config.start_pos); + changed |= add_position_control(ui, "End Position:", &mut maze_config.end_pos); + + // Trigger recreation if any value changed + if changed { + if let Some(mut event_writer) = + world.get_resource_mut::>() + { + event_writer.send(RecreateMazeEvent { floor: 1 }); + } + } + } + }); +} + +fn add_drag_value_control( + ui: &mut egui::Ui, + label: &str, + value: &mut T, + speed: f64, + range: RangeInclusive, +) -> bool { + let mut changed = false; + + ui.horizontal(|ui| { + ui.label(label); + let response = ui.add(DragValue::new(value).speed(speed).range(range)); + changed = response.changed(); + }); + changed +} + +fn add_position_control(ui: &mut Ui, label: &str, pos: &mut Hex) -> bool { + let mut changed = false; + + ui.horizontal(|ui| { + ui.label(label); + let response_x = ui.add(DragValue::new(&mut pos.x).speed(1).prefix("x: ")); + let response_y = ui.add(DragValue::new(&mut pos.y).speed(1).prefix("y: ")); + changed = response_x.changed() || response_y.changed(); + }); + changed +} + +fn add_seed_control(ui: &mut Ui, seed: &mut u64) -> bool { + let mut changed = false; + + ui.horizontal(|ui| { + ui.label("Seed:"); + + let mut seed_text = seed.to_string(); + + let response = ui.add( + TextEdit::singleline(&mut seed_text) + .desired_width(150.0) + .hint_text("Enter seed"), + ); + + // Parse text input when changed + if response.changed() { + if let Ok(new_seed) = seed_text.parse::() { + *seed = new_seed; + changed = true; + } + } + + // New random seed button + if ui.button("🎲").clicked() { + *seed = thread_rng().gen(); + changed = true; + } + + // Copy button + if ui.button("📋").clicked() { + ui.output_mut(|o| o.copied_text = seed.to_string()); + } + }); + + changed +} diff --git a/src/dev_tools/ui/mod.rs b/src/dev_tools/ui/mod.rs new file mode 100644 index 0000000..6aca3ec --- /dev/null +++ b/src/dev_tools/ui/mod.rs @@ -0,0 +1,5 @@ +mod inspector; +mod maze_controls; + +pub(crate) use inspector::inspector_ui; +pub(crate) use maze_controls::maze_controls_ui; diff --git a/src/lib.rs b/src/lib.rs index 9c126f5..00564b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,7 +70,7 @@ impl Plugin for AppPlugin { // Enable dev tools for dev builds. #[cfg(feature = "dev")] - app.add_plugins(dev_tools::plugin); + app.add_plugins(dev_tools::DevToolsPlugin); } } diff --git a/src/maze/mod.rs b/src/maze/mod.rs index 26aec4f..56894da 100644 --- a/src/maze/mod.rs +++ b/src/maze/mod.rs @@ -7,8 +7,8 @@ pub mod plugin; mod resources; mod systems; -pub use resources::MazeConfig; +pub use resources::{MazeConfig, MazePluginLoaded}; -pub fn spawn_grid(world: &mut World) { +pub fn spawn_maze(world: &mut World) { MazePlugin.apply(world); } diff --git a/src/maze/plugin.rs b/src/maze/plugin.rs index d4ce8c2..2e24d7f 100644 --- a/src/maze/plugin.rs +++ b/src/maze/plugin.rs @@ -7,7 +7,7 @@ use super::{ events::RecreateMazeEvent, resources::Layout, systems::{self, recreation::handle_maze_recreation_event}, - MazeConfig, + MazeConfig, MazePluginLoaded, }; #[derive(Default)] @@ -24,6 +24,7 @@ impl Plugin for MazePlugin { impl Command for MazePlugin { fn apply(self, world: &mut World) { + world.insert_resource(MazePluginLoaded); world.run_system_once(systems::setup::setup); } } diff --git a/src/maze/resources.rs b/src/maze/resources.rs index 77bcb0d..7e7245a 100644 --- a/src/maze/resources.rs +++ b/src/maze/resources.rs @@ -5,6 +5,10 @@ use hexx::{Hex, HexLayout, HexOrientation}; use rand::{rngs::StdRng, thread_rng, Rng, SeedableRng}; use thiserror::Error; +#[derive(Debug, Default, Reflect, Resource)] +#[reflect(Resource)] +pub struct MazePluginLoaded; + pub(crate) const WALL_SIZE: f32 = 1.0; #[derive(Debug, Error)] pub enum MazeConfigError { diff --git a/src/screens/gameplay.rs b/src/screens/gameplay.rs index f932d17..3e35397 100644 --- a/src/screens/gameplay.rs +++ b/src/screens/gameplay.rs @@ -5,7 +5,7 @@ use bevy::{input::common_conditions::input_just_pressed, prelude::*}; #[cfg(feature = "demo")] use crate::demo::level::spawn_level as spawn_level_command; #[cfg(not(feature = "demo"))] -use crate::maze::spawn_grid as spawn_level_command; +use crate::maze::spawn_maze as spawn_level_command; use crate::{asset_tracking::LoadResource, audio::Music, screens::Screen}; pub(super) fn plugin(app: &mut App) {