From 1b852d0bf753b2b02687ef8e9deb1ba0475889d2 Mon Sep 17 00:00:00 2001 From: Nicholas Westerhausen <2317381+nwesterhausen@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:11:17 -0500 Subject: [PATCH 01/31] feat: add settings resources and plugins to library --- Cargo.lock | 102 +++++++++++- game/Cargo.toml | 1 + game/src/main.rs | 5 + game/src/resources/plugin.rs | 2 + game/src/settings_menu/mod.rs | 13 ++ game_library/Cargo.toml | 1 + game_library/src/camera_scale.rs | 3 +- game_library/src/font_resource.rs | 28 ++++ game_library/src/lib.rs | 1 + game_library/src/settings.rs | 261 ++++++++++++++++++++++++++++++ 10 files changed, 415 insertions(+), 2 deletions(-) create mode 100644 game/src/settings_menu/mod.rs create mode 100644 game_library/src/settings.rs diff --git a/Cargo.lock b/Cargo.lock index 2f79222..30ef2c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -836,6 +836,24 @@ dependencies = [ "thread_local", ] +[[package]] +name = "bevy_pkv" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91c1f9dc84f86cb77ba29677e9fa45975e3de72aeb8e9bf72abd637a7fe6dd6" +dependencies = [ + "bevy_ecs", + "cfg_aliases", + "directories", + "redb", + "rmp-serde", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "bevy_ptr" version = "0.12.1" @@ -1631,6 +1649,27 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -1722,6 +1761,7 @@ version = "0.2.2" dependencies = [ "bevy", "bevy-inspector-egui", + "bevy_pkv", "built", "egui_dock", "embed-resource", @@ -2053,6 +2093,7 @@ version = "0.3.1" dependencies = [ "bevy", "bevy-inspector-egui", + "bevy_pkv", "serde", "serde_default_utils", "serde_yaml", @@ -2665,6 +2706,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + [[package]] name = "libredox" version = "0.0.2" @@ -3189,13 +3241,19 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "orbclient" version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" dependencies = [ - "libredox", + "libredox 0.0.2", ] [[package]] @@ -3478,6 +3536,15 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d463f2884048e7153449a55166f91028d5b0ea53c79377099ce4e8cf0cf9bb" +[[package]] +name = "redb" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72623e6275cd430215b741f41ebda34db93a13ebde253f908b70871c46afc5ba" +dependencies = [ + "libc", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -3496,6 +3563,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox 0.0.1", + "thiserror", +] + [[package]] name = "regex" version = "1.10.2" @@ -3552,6 +3630,28 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" +[[package]] +name = "rmp" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rodio" version = "0.17.3" diff --git a/game/Cargo.toml b/game/Cargo.toml index fe6bc95..d633f2e 100644 --- a/game/Cargo.toml +++ b/game/Cargo.toml @@ -26,6 +26,7 @@ bevy-inspector-egui = "0.22.1" winit = "0.28" image = "0.24" rand = "0.8.5" +bevy_pkv = "0.9.1" [lints.rust] unsafe_code = "forbid" diff --git a/game/src/main.rs b/game/src/main.rs index 6efe103..eac8032 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -6,6 +6,7 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use bevy::{asset::AssetMetaCheck, prelude::*}; +use game_library::settings::SettingsPlugin; use leafwing_input_manager::plugin::InputManagerPlugin; mod app_info; @@ -20,6 +21,7 @@ mod game; mod main_menu; mod player; mod resources; +mod settings_menu; mod spells; mod splash_screen; @@ -48,6 +50,7 @@ fn main() { }), ..Default::default() }) + // Nearest neighbor scaling (pixel art) .set(ImagePlugin::default_nearest()), ) // Add the debug plugin if in debug mode @@ -97,6 +100,8 @@ impl Plugin for ElementalistDefaultPlugins { app.insert_resource(ClearColor(Color::DARK_GREEN)); // Add the window icon app.add_systems(Startup, app_systems::set_window_icon); + // Add the settings plugin + app.add_plugins(SettingsPlugin); } } diff --git a/game/src/resources/plugin.rs b/game/src/resources/plugin.rs index 9791ca2..2ff02ca 100644 --- a/game/src/resources/plugin.rs +++ b/game/src/resources/plugin.rs @@ -1,4 +1,5 @@ use bevy::prelude::*; +use bevy_pkv::PkvStore; use game_library::{ font_resource::{change_font, ChangeFont, FontResource}, @@ -21,6 +22,7 @@ impl Plugin for ElementalistResourcesPlugin { app.insert_resource(CursorPosition::default()); app.insert_resource(SpellChoices::default()); app.insert_resource(FontResource::default()); + app.insert_resource(PkvStore::new("games.nwest.one", "Elementalist")); // ### GRAPHICS RESOURCES ### app.add_systems(Startup, load_spell_atlas); diff --git a/game/src/settings_menu/mod.rs b/game/src/settings_menu/mod.rs new file mode 100644 index 0000000..532cbd5 --- /dev/null +++ b/game/src/settings_menu/mod.rs @@ -0,0 +1,13 @@ +//! Settings Menu +//! +//! This has the various "scenes" which are part of the settings menu. The idea is for this to be +//! in the middle of the screen, and the background to be blurred out; that way it can be used in +//! any context and still be readable. +//! +//! Settings should be stored and retrieved from the [`bevy_pkv::PkvStore`] resource. Our settings +//! structs should already be serializable, and they should be usable. +//! +//! See [`game_library::settings`] for the settings resources and other things. +//! +//! This module will have to handle drawing things to the screen and sending the appropriate +//! events on setting changes. diff --git a/game_library/Cargo.toml b/game_library/Cargo.toml index 3b4886f..6b03695 100644 --- a/game_library/Cargo.toml +++ b/game_library/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] bevy = "0.12.1" bevy-inspector-egui = "0.22.1" +bevy_pkv = "0.9.1" serde = "1.0.195" serde_default_utils = "0.2.1" serde_yaml = "0.9.30" diff --git a/game_library/src/camera_scale.rs b/game_library/src/camera_scale.rs index 6c05cb2..a180143 100644 --- a/game_library/src/camera_scale.rs +++ b/game_library/src/camera_scale.rs @@ -61,6 +61,7 @@ use bevy::prelude::*; use bevy_inspector_egui::prelude::*; +use serde::{Deserialize, Serialize}; /// Component that stores the current camera scale level. /// @@ -77,7 +78,7 @@ use bevy_inspector_egui::prelude::*; /// These are available as constants on the struct: /// /// * [`CameraScaleLevel::LEVELS`] -#[derive(Component, Reflect, InspectorOptions, Clone, Copy)] +#[derive(Component, Reflect, InspectorOptions, Clone, Copy, Debug, Serialize, Deserialize)] #[reflect(Component, InspectorOptions)] #[allow(clippy::module_name_repetitions)] pub struct CameraScaleLevel { diff --git a/game_library/src/font_resource.rs b/game_library/src/font_resource.rs index 9c65afa..a04d0d9 100644 --- a/game_library/src/font_resource.rs +++ b/game_library/src/font_resource.rs @@ -7,6 +7,7 @@ use bevy::prelude::*; use bevy_inspector_egui::prelude::*; +use serde::{Deserialize, Serialize}; /// A resource to hold handles for the fonts used in the game. #[derive(Resource, Debug, Clone, Default, Reflect, InspectorOptions)] @@ -36,6 +37,33 @@ pub enum FontChoice { Console, } +/// The generic font-family options. This is used in [`crate::settings::AccessibilitySettings`] +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Hash, + Reflect, + InspectorOptions, + Serialize, + Deserialize, + Default, +)] +#[reflect(InspectorOptions)] +pub enum FontFamily { + /// "Fancy" display text. + #[default] + Display, + /// OpenDyslexic + Dyslexic, + /// Sans-serif + SansSerif, + /// Monospace + Monospace, +} + /// Change a font choice. Sending this event will update the specified font /// choice to the specified font. #[derive(Event)] diff --git a/game_library/src/lib.rs b/game_library/src/lib.rs index 4acb8f9..6570e14 100644 --- a/game_library/src/lib.rs +++ b/game_library/src/lib.rs @@ -25,6 +25,7 @@ pub mod enums; pub mod events; pub mod font_resource; pub mod math; +pub mod settings; mod acceleration; mod attribute; diff --git a/game_library/src/settings.rs b/game_library/src/settings.rs new file mode 100644 index 0000000..42aa773 --- /dev/null +++ b/game_library/src/settings.rs @@ -0,0 +1,261 @@ +//! Settings resources for the game. +//! +//! The base settings menu should have these options: +//! +//! - Accessibility +//! - Audio +//! - Video +//! - Gameplay +//! - Controls +//! - Back/Close +//! +//! Each of these options should have a sub-menu, which can be navigated to by pressing select or +//! clicking on the option. The sub-menu should have a back button, which returns to the main menu. +//! +//! The accessibility menu should have these options: +//! +//! - Font Choice (Default, Dyslexic, Sans-Serif) +//! - Back +//! +//! The audio menu should have these options: +//! +//! - Master Volume +//! - Music Volume +//! - SFX Volume +//! - Back +//! +//! The video menu should have these options: +//! +//! - Display Scale +//! - HUD Scaling +//! - Back +//! +//! The gameplay menu should have these options: +//! +//! - Auto-Aim +//! - Auto-Cast +//! - Back +//! +//! The controls menu should have these options: +//! +//! - Keybinds +//! - Keybinds (Controller) +//! - Back +//! +//! The keybinds menu should have these options (and these are the same for controller): +//! +//! - (Options for each action -- see [`crate::events::PlayerAction`]) +//! - (Options for each menu interaction -- see [`crate::events::MenuInteraction`]) +//! - Back +use bevy::prelude::*; +use bevy_pkv::PkvStore; +use serde::{Deserialize, Serialize}; + +use crate::{font_resource::FontFamily, CameraScaleLevel, Volume}; + +/// Volume settings. +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq, Serialize, Resource)] +#[allow(clippy::module_name_repetitions)] +pub struct VolumeSettings { + /// Master volume. + pub master: Volume, + /// Music volume. + pub music: Volume, + /// SFX volume. + pub sfx: Volume, +} + +/// Video settings. +#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, Resource)] +#[allow(clippy::module_name_repetitions)] +pub struct VideoSettings { + /// Display scale. + pub display_scale: CameraScaleLevel, + /// HUD scale. + pub hud_scale: f32, +} + +/// Gameplay settings. +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq, Serialize, Resource)] +#[allow(clippy::module_name_repetitions)] +pub struct GameplaySettings { + /// Auto-aim. + pub auto_aim: bool, + /// Auto-cast. + pub auto_cast: bool, +} + +/// Accessibility settings. +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq, Serialize, Resource)] +#[allow(clippy::module_name_repetitions)] +pub struct AccessibilitySettings { + /// Font choice (default, dyslexic, sans-serif). + pub font_family: FontFamily, +} + +/// Plugin for settings which will register the settings resources and run the `first_load` system. +/// +/// This also registers the [`SettingChanged`] event and a system to flush the settings to the +/// [`bevy_pkv::PkvStore`] when the [`SettingChanged`] event is sent. +#[allow(clippy::module_name_repetitions)] +pub struct SettingsPlugin; + +impl Plugin for SettingsPlugin { + fn build(&self, app: &mut App) { + app.add_event::() + .init_resource::() + .init_resource::() + .init_resource::() + .init_resource::() + .add_systems(Startup, first_load) + .add_systems(Update, flush_settings_to_store); + } +} + +/// Event to indicate a setting was changed. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, Event)] +pub struct SettingChanged(pub SettingCategory); + +/// The category of a setting. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)] +pub enum SettingCategory { + /// Volume settings. + Volume, + /// Video settings. + Video, + /// Gameplay settings. + Gameplay, + /// Accessibility settings. + Accessibility, +} + +impl SettingCategory { + /// Returns the name of the setting category. + /// + /// This could be used as a key to save in the [`bevy_pkv::PkvStore`]. + /// + /// # Example + /// + /// ``` + /// use game_library::SettingCategory; + /// + /// assert_eq!(SettingCategory::Volume.name(), "Volume"); + /// ``` + #[must_use] + pub const fn name(&self) -> &'static str { + match self { + Self::Volume => "Volume", + Self::Video => "Video", + Self::Gameplay => "Gameplay", + Self::Accessibility => "Accessibility", + } + } +} + +/// System to run on the first load of the game. It will either load the settings from the +/// [`bevy_pkv::PkvStore`] or store default settings in the [`bevy_pkv::PkvStore`]. +fn first_load( + mut volume_settings: ResMut, + mut video_settings: ResMut, + mut gameplay_settings: ResMut, + mut accessibility_settings: ResMut, + mut pkv_store: ResMut, +) { + // Load the settings from the pkv store. + if let Ok(volume) = pkv_store.get::(SettingCategory::Volume.name()) { + *volume_settings = volume; + } else { + let _ = pkv_store + .set(SettingCategory::Volume.name(), &VolumeSettings::default()) + .map_err(|err| { + tracing::error!("failed to save volume settings to disk: {}", err); + }); + } + + if let Ok(video) = pkv_store.get::(SettingCategory::Video.name()) { + *video_settings = video; + } else { + let _ = pkv_store + .set(SettingCategory::Video.name(), &VideoSettings::default()) + .map_err(|err| { + tracing::error!("failed to save video settings to disk: {}", err); + }); + } + + if let Ok(gameplay) = pkv_store.get::(SettingCategory::Gameplay.name()) { + *gameplay_settings = gameplay; + } else { + let _ = pkv_store + .set( + SettingCategory::Gameplay.name(), + &GameplaySettings::default(), + ) + .map_err(|err| { + tracing::error!("failed to save gameplay settings to disk: {}", err); + }); + } + + if let Ok(accessibility) = + pkv_store.get::(SettingCategory::Accessibility.name()) + { + *accessibility_settings = accessibility; + } else { + let _ = pkv_store + .set( + SettingCategory::Accessibility.name(), + &AccessibilitySettings::default(), + ) + .map_err(|err| { + tracing::error!("failed to save accessibility settings to disk: {}", err); + }); + } +} + +/// System that runs on [`Update`] and reacts to the [`SettingChanged`] event. +/// +/// This will save the settings to the [`bevy_pkv::PkvStore`]. +#[allow(clippy::needless_pass_by_value)] +fn flush_settings_to_store( + volume_settings: Res, + video_settings: Res, + gameplay_settings: Res, + accessibility_settings: Res, + mut pkv_store: ResMut, + mut setting_changed_events: EventReader, +) { + for setting_changed_event in setting_changed_events.read() { + match setting_changed_event.0 { + SettingCategory::Volume => { + let _ = pkv_store + .set(SettingCategory::Volume.name(), &*volume_settings) + .map_err(|err| { + tracing::error!("failed to save volume settings to disk: {}", err); + }); + } + SettingCategory::Video => { + let _ = pkv_store + .set(SettingCategory::Video.name(), &*video_settings) + .map_err(|err| { + tracing::error!("failed to save video settings to disk: {}", err); + }); + } + SettingCategory::Gameplay => { + let _ = pkv_store + .set(SettingCategory::Gameplay.name(), &*gameplay_settings) + .map_err(|err| { + tracing::error!("failed to save gameplay settings to disk: {}", err); + }); + } + SettingCategory::Accessibility => { + let _ = pkv_store + .set( + SettingCategory::Accessibility.name(), + &*accessibility_settings, + ) + .map_err(|err| { + tracing::error!("failed to save accessibility settings to disk: {}", err); + }); + } + } + } +} From 000a0aa8b88c7377e4072d361852f857f766725c Mon Sep 17 00:00:00 2001 From: Nicholas Westerhausen <2317381+nwesterhausen@users.noreply.github.com> Date: Tue, 23 Jan 2024 20:33:47 -0500 Subject: [PATCH 02/31] fix: doctest SettingsCategory --- game_library/src/settings.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/game_library/src/settings.rs b/game_library/src/settings.rs index 42aa773..9de104c 100644 --- a/game_library/src/settings.rs +++ b/game_library/src/settings.rs @@ -137,7 +137,7 @@ impl SettingCategory { /// # Example /// /// ``` - /// use game_library::SettingCategory; + /// use game_library::settings::SettingCategory; /// /// assert_eq!(SettingCategory::Volume.name(), "Volume"); /// ``` From af8fcb747391fc482211dff9779887468db42624 Mon Sep 17 00:00:00 2001 From: Nicholas Westerhausen <2317381+nwesterhausen@users.noreply.github.com> Date: Thu, 25 Jan 2024 15:03:08 -0500 Subject: [PATCH 03/31] chore: remove duplicate cargo config entry --- .cargo/config.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 1d9721a..9e4a448 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -27,8 +27,3 @@ rustflags = [ [target.x86_64-pc-windows-msvc] linker = "rust-lld.exe" rustflags = ["-Zshare-generics=n"] - -# Optional: Uncommenting the following improves compile times, but reduces the amount of debug info to 'line number tables only' -# In most cases the gains are negligible, but if you are on macos and have slow compile times you should see significant gains. -[profile.dev] -debug = 1 From 826b524cca562b5809516150cc9d9432d8ed8d25 Mon Sep 17 00:00:00 2001 From: Nicholas Westerhausen <2317381+nwesterhausen@users.noreply.github.com> Date: Thu, 25 Jan 2024 18:58:10 -0500 Subject: [PATCH 04/31] wip: menu state transition setup --- game/src/main.rs | 2 ++ game/src/main_menu/systems.rs | 4 ++- game/src/settings_menu/base.rs | 44 ++++++++++++++++++++++++++++++++ game/src/settings_menu/mod.rs | 7 +++++ game/src/settings_menu/plugin.rs | 24 +++++++++++++++++ game/src/settings_menu/state.rs | 24 +++++++++++++++++ 6 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 game/src/settings_menu/base.rs create mode 100644 game/src/settings_menu/plugin.rs create mode 100644 game/src/settings_menu/state.rs diff --git a/game/src/main.rs b/game/src/main.rs index eac8032..b6b5117 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -102,6 +102,8 @@ impl Plugin for ElementalistDefaultPlugins { app.add_systems(Startup, app_systems::set_window_icon); // Add the settings plugin app.add_plugins(SettingsPlugin); + // Add the menu plugin + app.add_plugins(settings_menu::SettingsMenuPlugin); } } diff --git a/game/src/main_menu/systems.rs b/game/src/main_menu/systems.rs index 4b41ebd..f4f036c 100644 --- a/game/src/main_menu/systems.rs +++ b/game/src/main_menu/systems.rs @@ -2,7 +2,7 @@ use bevy::{app::AppExit, prelude::*}; use game_library::font_resource::FontResource; use rand::seq::SliceRandom; -use crate::AppState; +use crate::{settings_menu, AppState}; use super::{button_actions::ButtonAction, components::SelectedOption, state::MenuState}; @@ -57,6 +57,7 @@ pub fn menu_actions( mut game_state: ResMut>, mut font_resource: ResMut, asset_server: Res, + mut settings_state: ResMut>, ) { // Loop through all the buttons that have been interacted with for (interaction, menu_button_action) in &interaction_query { @@ -71,6 +72,7 @@ pub fn menu_actions( } ButtonAction::Settings | ButtonAction::BackToSettings => { menu_state.set(MenuState::Settings); + settings_state.set(settings_menu::MenuState::Main); } ButtonAction::BackToMenu => menu_state.set(MenuState::Main), ButtonAction::SettingsAudio => menu_state.set(MenuState::SettingsAudio), diff --git a/game/src/settings_menu/base.rs b/game/src/settings_menu/base.rs new file mode 100644 index 0000000..22c72e8 --- /dev/null +++ b/game/src/settings_menu/base.rs @@ -0,0 +1,44 @@ +//! Base systems for the menu. + +use bevy::{prelude::*, sprite::MaterialMesh2dBundle, window::PrimaryWindow}; + +use super::state::MenuState; + +/// An entity tag for ease of cleanup when the menu is disabled. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Component)] +pub struct MenuEntity; + +/// Clear the background (draw a blur) only when the menu is not disabled. +pub(super) fn clear_background( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + window_query: Query<&Window, With>, +) { + let Ok(window) = window_query.get_single() else { + tracing::warn!("Failed to get window size for menu background"); + return; + }; + // Quad + commands.spawn(( + MaterialMesh2dBundle { + mesh: meshes + .add(shape::Quad::new(Vec2::new(window.width(), window.height())).into()) + .into(), + material: materials.add(ColorMaterial::from(Color::LIME_GREEN)), + transform: Transform::from_xyz(0., 0., 0.), + ..default() + }, + MenuEntity, + )); +} + +/// Cleanup the menu entities when the menu is disabled. +pub(super) fn cleanup_menu_entities( + mut commands: Commands, + menu_query: Query>, +) { + for entity in menu_query.iter() { + commands.entity(entity).despawn_recursive(); + } +} diff --git a/game/src/settings_menu/mod.rs b/game/src/settings_menu/mod.rs index 532cbd5..52a7c74 100644 --- a/game/src/settings_menu/mod.rs +++ b/game/src/settings_menu/mod.rs @@ -11,3 +11,10 @@ //! //! This module will have to handle drawing things to the screen and sending the appropriate //! events on setting changes. + +mod base; +mod plugin; +mod state; + +pub use plugin::SettingsMenuPlugin; +pub use state::MenuState; diff --git a/game/src/settings_menu/plugin.rs b/game/src/settings_menu/plugin.rs new file mode 100644 index 0000000..7ccd405 --- /dev/null +++ b/game/src/settings_menu/plugin.rs @@ -0,0 +1,24 @@ +//! The plugin which adds the necessary states and systems to the app for +//! the settings menu to work. + +use bevy::prelude::*; + +use super::{ + base::{cleanup_menu_entities, clear_background}, + state::MenuState, +}; + +/// The settings menu plugin. +pub struct SettingsMenuPlugin; + +impl Plugin for SettingsMenuPlugin { + fn build(&self, app: &mut App) { + app.add_state::(); + // Whatever we do to blur the background should happen (constantly?) while + // the menu is not disabled. This has its own state check, so it should + // be fine to run it constantly. + app.add_systems(OnEnter(MenuState::Main), clear_background); + app.add_systems(OnEnter(MenuState::Disabled), cleanup_menu_entities); + // Then we should have systems for each of the menu states. + } +} diff --git a/game/src/settings_menu/state.rs b/game/src/settings_menu/state.rs new file mode 100644 index 0000000..03d5d27 --- /dev/null +++ b/game/src/settings_menu/state.rs @@ -0,0 +1,24 @@ +//! The states that the settings menu can be in. + +use bevy::prelude::*; + +/// The menu states. +/// +/// The menu can be in one of these states at any given time. Each state +/// roughly corresponds to a different screen in the menu. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, States)] +pub enum MenuState { + /// Disabled (default) state. The menu is not opened. + #[default] + Disabled, + /// The main menu screen. This should have links to the other screens. + Main, + /// The graphics/display menu screen. + Display, + /// The audio menu screen. + Audio, + /// The controls menu screen. + Controls, + /// The gameplay menu screen. + Gameplay, +} From 173978c98fe7737645ca5e42259bba528a274c14 Mon Sep 17 00:00:00 2001 From: Nicholas Westerhausen <2317381+nwesterhausen@users.noreply.github.com> Date: Thu, 25 Jan 2024 21:59:51 -0500 Subject: [PATCH 05/31] feat: move settings menu into it's own module It should be displayable from the game now too, but that hasn't been hooked up yet! --- game/src/app_state.rs | 17 ++- game/src/app_systems.rs | 2 +- game/src/common/colors.rs | 43 ++++++++ game/src/common/mod.rs | 1 + game/src/main.rs | 7 +- game/src/main_menu/button_actions.rs | 8 -- game/src/main_menu/components.rs | 22 +--- game/src/main_menu/plugin.rs | 40 +------ game/src/main_menu/screens/mod.rs | 10 -- game/src/main_menu/state.rs | 5 - game/src/main_menu/systems.rs | 37 ++----- game/src/resources/mod.rs | 2 + game/src/resources/plugin.rs | 3 +- game/src/resources/return_to_state.rs | 15 +++ .../screens => settings_menu}/audio.rs | 30 ++++-- game/src/settings_menu/base.rs | 35 +++--- game/src/settings_menu/button_actions.rs | 102 ++++++++++++++++++ .../screens => settings_menu}/controls.rs | 24 +++-- .../video.rs => settings_menu/display.rs} | 43 ++++---- .../screens => settings_menu}/gameplay.rs | 30 ++++-- .../settings.rs => settings_menu/main.rs} | 33 +++--- game/src/settings_menu/mod.rs | 6 ++ game/src/settings_menu/plugin.rs | 68 ++++++++++-- game/src/splash_screen/plugin.rs | 4 +- 24 files changed, 383 insertions(+), 204 deletions(-) create mode 100644 game/src/common/colors.rs create mode 100644 game/src/resources/return_to_state.rs rename game/src/{main_menu/screens => settings_menu}/audio.rs (78%) create mode 100644 game/src/settings_menu/button_actions.rs rename game/src/{main_menu/screens => settings_menu}/controls.rs (81%) rename game/src/{main_menu/screens/video.rs => settings_menu/display.rs} (75%) rename game/src/{main_menu/screens => settings_menu}/gameplay.rs (77%) rename game/src/{main_menu/screens/settings.rs => settings_menu/main.rs} (88%) diff --git a/game/src/app_state.rs b/game/src/app_state.rs index 1d9aca8..837b9b4 100644 --- a/game/src/app_state.rs +++ b/game/src/app_state.rs @@ -13,8 +13,23 @@ pub enum AppState { AppLoading, /// Main menu screen /// - /// Has sub-states for each menu screen (audio, video, etc) + /// Has buttons for: + /// + /// * Play + /// * Settings + /// * Exit MainMenu, + /// Settings menu + /// + /// Sub-states: + /// + /// * Disabled (when the menu is not open) + /// * Main (the main settings menu) + /// * Display + /// * Audio + /// * Controls + /// * Gameplay + SettingsMenu, /// In game /// /// Sub-states: diff --git a/game/src/app_systems.rs b/game/src/app_systems.rs index 0806b2d..7573a6d 100644 --- a/game/src/app_systems.rs +++ b/game/src/app_systems.rs @@ -6,7 +6,7 @@ use winit::window::Icon; use crate::app_info; /// Despawn all entities with the given tag (component) -pub fn despawn_screen(to_despawn: Query>, mut commands: Commands) { +pub fn despawn_with_tag(to_despawn: Query>, mut commands: Commands) { for entity in &to_despawn { commands.entity(entity).despawn_recursive(); } diff --git a/game/src/common/colors.rs b/game/src/common/colors.rs new file mode 100644 index 0000000..bd5cde8 --- /dev/null +++ b/game/src/common/colors.rs @@ -0,0 +1,43 @@ +//! A sub-set of colors from the color pallette used in the game. + +use bevy::prelude::*; + +/// The dark color used for the background. +/// +///
+/// Hex code #2b222a +pub const BACKGROUND_COLOR: Color = Color::rgba(0.169, 0.133, 0.165, 1.); +/// The dark color used for the background at 50% opacity. +pub const BACKGROUND_COLOR_50: Color = Color::rgba(0.169, 0.133, 0.165, 0.5); + +/// The clear color used for the game. +/// +///
+/// Hex code #1a1f2e +pub const CLEAR_COLOR: Color = Color::rgba(0.102, 0.122, 0.18, 1.); +/// The clear color used for the game at 50% opacity. +pub const CLEAR_COLOR_50: Color = Color::rgba(0.102, 0.122, 0.18, 0.5); + +/// The color used for text. +/// +///
+/// Hex code #e1a845 +pub const TEXT_COLOR: Color = Color::rgba(1., 0.658, 0.27, 1.); + +/// The color used for selected text. +/// +///
+/// Hex code #e5efef +pub const SELECTED_TEXT_COLOR: Color = Color::rgba(0.898, 0.937, 0.937, 1.); + +/// The color used for hovered text. +/// +///
+/// Hex code #eff37c +pub const HOVERED_TEXT_COLOR: Color = Color::rgba(0.937, 0.953, 0.486, 1.); + +/// The color used for hovered text (alternate). +/// +///
+/// Hex code #8d4830 +pub const HOVERED_TEXT_COLOR_ALTERNATE: Color = Color::rgba(0.553, 0.282, 0.188, 1.); diff --git a/game/src/common/mod.rs b/game/src/common/mod.rs index f5acfd7..1075a73 100644 --- a/game/src/common/mod.rs +++ b/game/src/common/mod.rs @@ -1 +1,2 @@ +pub mod colors; pub mod movement; diff --git a/game/src/main.rs b/game/src/main.rs index b6b5117..68ba6b4 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -9,11 +9,12 @@ use bevy::{asset::AssetMetaCheck, prelude::*}; use game_library::settings::SettingsPlugin; use leafwing_input_manager::plugin::InputManagerPlugin; +pub(crate) mod common; + mod app_info; mod app_state; mod app_systems; mod camera; -mod common; #[cfg(debug_assertions)] mod dev_systems; mod events; @@ -26,7 +27,7 @@ mod spells; mod splash_screen; pub use app_state::AppState; -pub use app_systems::despawn_screen; +pub use app_systems::despawn_with_tag; use events::{MenuInteraction, PlayerAction}; fn main() { @@ -97,7 +98,7 @@ impl Plugin for ElementalistDefaultPlugins { // Never attempts to look up meta files. The default meta configuration will be used for each asset. app.insert_resource(AssetMetaCheck::Never); // The clear color is the color the screen is cleared to before each frame is drawn - app.insert_resource(ClearColor(Color::DARK_GREEN)); + app.insert_resource(ClearColor(common::colors::CLEAR_COLOR)); // Add the window icon app.add_systems(Startup, app_systems::set_window_icon); // Add the settings plugin diff --git a/game/src/main_menu/button_actions.rs b/game/src/main_menu/button_actions.rs index 1f1f48e..a3040c8 100644 --- a/game/src/main_menu/button_actions.rs +++ b/game/src/main_menu/button_actions.rs @@ -6,12 +6,4 @@ pub enum ButtonAction { StartGame, Settings, Quit, - BackToMenu, - BackToSettings, - SettingsAudio, - SettingsVideo, - SettingsControls, - SettingsGameplay, - // ChangeFont .. maybe we have to include internal values too.. - ChangeFont, } diff --git a/game/src/main_menu/components.rs b/game/src/main_menu/components.rs index c360e0e..2fe473a 100644 --- a/game/src/main_menu/components.rs +++ b/game/src/main_menu/components.rs @@ -4,26 +4,6 @@ use bevy::prelude::*; #[derive(Component)] pub struct OnMainMenuScreen; -// Tag component used to tag entities added on the settings menu screen -#[derive(Component)] -pub struct OnSettingsMenuScreen; - -// Tag component used to tag entities added on the display settings menu screen -#[derive(Component)] -pub struct OnVideoSettingsMenuScreen; - -// Tag component used to tag entities added on the sound settings menu screen -#[derive(Component)] -pub struct OnAudioSettingsMenuScreen; - -/// Tag component used to tag entities added on the controls settings menu screen -#[derive(Component)] -pub struct OnControlsSettingsMenuScreen; - -/// Tag component used to tag entities added on the gameplay settings menu screen -#[derive(Component)] -pub struct OnGameplaySettingsMenuScreen; - -// Tag component used to mark which setting is currently selected +/// Tag component used to mark which setting is currently selected #[derive(Component)] pub struct SelectedOption; diff --git a/game/src/main_menu/plugin.rs b/game/src/main_menu/plugin.rs index 41e1d41..cdad188 100644 --- a/game/src/main_menu/plugin.rs +++ b/game/src/main_menu/plugin.rs @@ -1,6 +1,6 @@ use bevy::prelude::*; -use crate::{despawn_screen, AppState}; +use crate::{despawn_with_tag, AppState}; use super::{ components, screens, @@ -36,43 +36,7 @@ impl Plugin for MainMenuPlugin { .add_systems(OnEnter(MenuState::Main), screens::main_menu) .add_systems( OnExit(MenuState::Main), - despawn_screen::, - ) - // Settings screen - .add_systems(OnEnter(MenuState::Settings), screens::settings) - .add_systems( - OnExit(MenuState::Settings), - despawn_screen::, - ) - // Audio settings screen - .add_systems(OnEnter(MenuState::SettingsAudio), screens::audio_settings) - .add_systems( - OnExit(MenuState::SettingsAudio), - despawn_screen::, - ) - // Video settings screen - .add_systems(OnEnter(MenuState::SettingsVideo), screens::video_settings) - .add_systems( - OnExit(MenuState::SettingsVideo), - despawn_screen::, - ) - // Controls settings screen - .add_systems( - OnEnter(MenuState::SettingsControls), - screens::controls_settings, - ) - .add_systems( - OnExit(MenuState::SettingsControls), - despawn_screen::, - ) - // Gameplay settings screen - .add_systems( - OnEnter(MenuState::SettingsGameplay), - screens::gameplay_settings, - ) - .add_systems( - OnExit(MenuState::SettingsGameplay), - despawn_screen::, + despawn_with_tag::, ); } } diff --git a/game/src/main_menu/screens/mod.rs b/game/src/main_menu/screens/mod.rs index 0c2b0ad..7de3d66 100644 --- a/game/src/main_menu/screens/mod.rs +++ b/game/src/main_menu/screens/mod.rs @@ -1,13 +1,3 @@ -mod audio; -mod controls; -mod gameplay; mod main_screen; -mod settings; -mod video; -pub use audio::audio_settings_setup as audio_settings; -pub use controls::controls_settings_setup as controls_settings; -pub use gameplay::gameplay_settings_setup as gameplay_settings; pub use main_screen::main_menu_setup as main_menu; -pub use settings::settings_setup as settings; -pub use video::video_settings_setup as video_settings; diff --git a/game/src/main_menu/state.rs b/game/src/main_menu/state.rs index 88a2b39..f623449 100644 --- a/game/src/main_menu/state.rs +++ b/game/src/main_menu/state.rs @@ -4,11 +4,6 @@ use bevy::prelude::*; #[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)] pub enum MenuState { Main, - Settings, - SettingsAudio, - SettingsVideo, - SettingsControls, - SettingsGameplay, #[default] Disabled, } diff --git a/game/src/main_menu/systems.rs b/game/src/main_menu/systems.rs index f4f036c..80f5606 100644 --- a/game/src/main_menu/systems.rs +++ b/game/src/main_menu/systems.rs @@ -1,8 +1,6 @@ use bevy::{app::AppExit, prelude::*}; -use game_library::font_resource::FontResource; -use rand::seq::SliceRandom; -use crate::{settings_menu, AppState}; +use crate::{resources::ReturnToState, AppState}; use super::{button_actions::ButtonAction, components::SelectedOption, state::MenuState}; @@ -55,9 +53,7 @@ pub fn menu_actions( mut app_exit_events: EventWriter, mut menu_state: ResMut>, mut game_state: ResMut>, - mut font_resource: ResMut, - asset_server: Res, - mut settings_state: ResMut>, + mut return_to_state: ResMut, ) { // Loop through all the buttons that have been interacted with for (interaction, menu_button_action) in &interaction_query { @@ -70,30 +66,13 @@ pub fn menu_actions( game_state.set(AppState::InGame); menu_state.set(MenuState::Disabled); } - ButtonAction::Settings | ButtonAction::BackToSettings => { - menu_state.set(MenuState::Settings); - settings_state.set(settings_menu::MenuState::Main); + ButtonAction::Settings => { + // Set the return to state to the main menu + return_to_state.0 = AppState::MainMenu; + // Set the game state to the settings menu + game_state.set(AppState::SettingsMenu); + menu_state.set(MenuState::Disabled); } - ButtonAction::BackToMenu => menu_state.set(MenuState::Main), - ButtonAction::SettingsAudio => menu_state.set(MenuState::SettingsAudio), - ButtonAction::SettingsVideo => menu_state.set(MenuState::SettingsVideo), - ButtonAction::SettingsControls => menu_state.set(MenuState::SettingsControls), - ButtonAction::SettingsGameplay => menu_state.set(MenuState::SettingsGameplay), - ButtonAction::ChangeFont => { - // For now, choose a random font and then load the handle into display - let font_choices = [ - "ui/fonts/AlmendraDisplay-Regular.ttf".to_string(), - "ui/fonts/OpenDyslexic-Regular.otf".to_string(), - "ui/fonts/RedHatDisplay-Regular.ttf".to_string(), - ]; - let Some(font_choice) = font_choices.choose(&mut rand::thread_rng()) else { - tracing::error!("Failed to choose a random font"); - continue; - }; - tracing::info!("Changing font to: {}", font_choice); - font_resource.display_font = asset_server.load(font_choice); - } // A catch-all for any button actions we haven't handled (yet) - // _ => tracing::error!("Unhandled button action: {:?}", menu_button_action), } } } diff --git a/game/src/resources/mod.rs b/game/src/resources/mod.rs index 2ebed14..499b962 100644 --- a/game/src/resources/mod.rs +++ b/game/src/resources/mod.rs @@ -6,8 +6,10 @@ mod cursor_position; mod fonts; mod plugin; +mod return_to_state; mod spritesheet; pub use cursor_position::*; pub use plugin::ElementalistResourcesPlugin; +pub use return_to_state::ReturnToState; pub use spritesheet::SpellAtlas; diff --git a/game/src/resources/plugin.rs b/game/src/resources/plugin.rs index 2ff02ca..810720f 100644 --- a/game/src/resources/plugin.rs +++ b/game/src/resources/plugin.rs @@ -8,7 +8,7 @@ use game_library::{ use super::{ cursor_position::update_cursor_position, fonts::set_initial_fonts, - spritesheet::load_spell_atlas, + spritesheet::load_spell_atlas, ReturnToState, }; pub struct ElementalistResourcesPlugin; @@ -22,6 +22,7 @@ impl Plugin for ElementalistResourcesPlugin { app.insert_resource(CursorPosition::default()); app.insert_resource(SpellChoices::default()); app.insert_resource(FontResource::default()); + app.init_resource::(); app.insert_resource(PkvStore::new("games.nwest.one", "Elementalist")); // ### GRAPHICS RESOURCES ### diff --git a/game/src/resources/return_to_state.rs b/game/src/resources/return_to_state.rs new file mode 100644 index 0000000..0458645 --- /dev/null +++ b/game/src/resources/return_to_state.rs @@ -0,0 +1,15 @@ +//! Helper to know what state of the app to return to when entering the menu. + +use bevy::prelude::*; + +use crate::AppState; + +/// Helper to know what state of the app to return to when entering the menu. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Resource)] +pub struct ReturnToState(pub AppState); + +impl Default for ReturnToState { + fn default() -> Self { + Self(AppState::MainMenu) + } +} diff --git a/game/src/main_menu/screens/audio.rs b/game/src/settings_menu/audio.rs similarity index 78% rename from game/src/main_menu/screens/audio.rs rename to game/src/settings_menu/audio.rs index e7172f3..1d5d13b 100644 --- a/game/src/main_menu/screens/audio.rs +++ b/game/src/settings_menu/audio.rs @@ -1,13 +1,21 @@ +//! Has systems for the display settings menu. + use bevy::prelude::*; -use game_library::font_resource::FontResource; +use game_library::{font_resource::FontResource, settings::VolumeSettings}; + +use crate::common::colors; + +use super::button_actions::ButtonAction; -use crate::main_menu::{button_actions::ButtonAction, components::OnAudioSettingsMenuScreen}; +/// Component for tagging entities that are part of the display settings menu. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Component)] +pub(super) struct AudioSettingsMenuEntity; -/// System to setup the main menu screen -/// -/// When the main menu screen is entered, we spawn the main menu entities. This includes the -/// background, the title, and the buttons. -pub fn audio_settings_setup(mut commands: Commands, fonts: Res) { +pub(super) fn show_audio_settings( + mut commands: Commands, + fonts: Res, + mut volume_settings: ResMut, +) { // Common style for all buttons on the screen let button_style = Style { margin: UiRect::px(10., 10., 0., 20.), @@ -17,7 +25,7 @@ pub fn audio_settings_setup(mut commands: Commands, fonts: Res) { }; let button_text_style = TextStyle { font_size: 40.0, - color: Color::WHITE, + color: colors::TEXT_COLOR, font: fonts.display_font.clone(), }; @@ -34,7 +42,7 @@ pub fn audio_settings_setup(mut commands: Commands, fonts: Res) { }, ..default() }, - OnAudioSettingsMenuScreen, + AudioSettingsMenuEntity, )) .with_children(|parent| { // Game Title @@ -44,7 +52,7 @@ pub fn audio_settings_setup(mut commands: Commands, fonts: Res) { TextStyle { font: fonts.display_font.clone(), font_size: 72.0, - color: Color::WHITE, + color: colors::TEXT_COLOR, }, ), style: Style { @@ -74,7 +82,7 @@ pub fn audio_settings_setup(mut commands: Commands, fonts: Res) { background_color: Color::NONE.into(), ..default() }, - ButtonAction::BackToSettings, + ButtonAction::BackToMenu, )) .with_children(|button| { button diff --git a/game/src/settings_menu/base.rs b/game/src/settings_menu/base.rs index 22c72e8..bf53e9b 100644 --- a/game/src/settings_menu/base.rs +++ b/game/src/settings_menu/base.rs @@ -2,43 +2,54 @@ use bevy::{prelude::*, sprite::MaterialMesh2dBundle, window::PrimaryWindow}; -use super::state::MenuState; +use super::MenuState; + +use crate::common::colors::BACKGROUND_COLOR_50; /// An entity tag for ease of cleanup when the menu is disabled. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Component)] pub struct MenuEntity; +/// A tag specifically for the menu background. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Component)] +pub struct MenuBackground; + /// Clear the background (draw a blur) only when the menu is not disabled. pub(super) fn clear_background( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, window_query: Query<&Window, With>, + existing_background_query: Query>, ) { + // Check if the background already exists. + if let Ok(_) = existing_background_query.get_single() { + // If it does, just return. + return; + } + let Ok(window) = window_query.get_single() else { tracing::warn!("Failed to get window size for menu background"); return; }; - // Quad + + // Quad that draws over the whole screen. commands.spawn(( MaterialMesh2dBundle { mesh: meshes .add(shape::Quad::new(Vec2::new(window.width(), window.height())).into()) .into(), - material: materials.add(ColorMaterial::from(Color::LIME_GREEN)), - transform: Transform::from_xyz(0., 0., 0.), + material: materials.add(ColorMaterial::from(BACKGROUND_COLOR_50)), + transform: Transform::from_xyz(0., 0., 10.), ..default() }, MenuEntity, )); } -/// Cleanup the menu entities when the menu is disabled. -pub(super) fn cleanup_menu_entities( - mut commands: Commands, - menu_query: Query>, -) { - for entity in menu_query.iter() { - commands.entity(entity).despawn_recursive(); - } +/// System to setup settings menu. +/// +/// When the settings menu is entered, we should setup the menu. +pub(super) fn transition_to_base_menu(mut menu_state: ResMut>) { + menu_state.set(MenuState::Main); } diff --git a/game/src/settings_menu/button_actions.rs b/game/src/settings_menu/button_actions.rs new file mode 100644 index 0000000..1dbf4e6 --- /dev/null +++ b/game/src/settings_menu/button_actions.rs @@ -0,0 +1,102 @@ +//! Actions that can be performed by the buttons in the settings menu. + +use bevy::prelude::*; + +use crate::{common::colors, resources::ReturnToState, AppState}; + +use super::MenuState; + +/// All of the various "buttons" that can be clicked in any of the main menu screens +#[derive(Component, Debug, Eq, PartialEq, Hash, Clone, Copy)] +pub(super) enum ButtonAction { + /// Close the menu. + CloseMenu, + /// Go back to the "main" settings menu. + BackToMenu, + /// Go to the audio menu + SettingsAudio, + /// Go to the display menu + SettingsDisplay, + /// Go to the controls menu + SettingsControls, + /// Go to the gameplay menu + SettingsGameplay, +} + +/// Tag component used to mark which setting is currently selected +#[derive(Component)] +pub(super) struct SelectedOption; + +/// System for changing button colors when hovered, etc +/// +/// * `interaction_query`: grabs all the buttons that have been interacted with, with the components +/// Interaction, children, and if they are a selected option (e.g. part of a radio group). It grabs +/// what has changed about the interaction (i.e. if it has changed at all) +/// * `text_query`: let's us grab the text component of the button +#[allow(clippy::type_complexity)] +pub(super) fn button_system( + mut interaction_query: Query< + (&Interaction, &Children, Option<&SelectedOption>), + (Changed, With