From 7cb9a33f791660357e6ea715a8b8566b47b16f5a Mon Sep 17 00:00:00 2001 From: extrawurst <776816+extrawurst@users.noreply.github.com> Date: Sun, 29 Jun 2025 22:53:27 +0200 Subject: [PATCH] Pause menu dummy (#54) --- crates/client/src/main.rs | 2 + crates/client/src/ui/mod.rs | 7 + crates/client/src/ui/pause.rs | 187 ++++++++++++++++++++++ crates/shared/src/backpack/backpack_ui.rs | 5 +- crates/shared/src/cash.rs | 4 +- crates/shared/src/control/controls.rs | 22 ++- crates/shared/src/control/mod.rs | 7 + crates/shared/src/lib.rs | 3 + justfile | 2 +- 9 files changed, 232 insertions(+), 7 deletions(-) create mode 100644 crates/client/src/ui/mod.rs create mode 100644 crates/client/src/ui/pause.rs diff --git a/crates/client/src/main.rs b/crates/client/src/main.rs index 508bfda..9b87415 100644 --- a/crates/client/src/main.rs +++ b/crates/client/src/main.rs @@ -1,4 +1,5 @@ mod debug; +mod ui; use crate::utils::{auto_rotate, explosions}; use avian3d::prelude::*; @@ -129,6 +130,7 @@ fn main() { app.add_plugins(heal_effect::plugin); app.add_plugins(tb_entities::plugin); app.add_plugins(explosions::plugin); + app.add_plugins(ui::plugin); app.init_state::(); diff --git a/crates/client/src/ui/mod.rs b/crates/client/src/ui/mod.rs new file mode 100644 index 0000000..ef5b746 --- /dev/null +++ b/crates/client/src/ui/mod.rs @@ -0,0 +1,7 @@ +mod pause; + +use bevy::prelude::*; + +pub fn plugin(app: &mut App) { + app.add_plugins(pause::plugin); +} diff --git a/crates/client/src/ui/pause.rs b/crates/client/src/ui/pause.rs new file mode 100644 index 0000000..1ff3eb3 --- /dev/null +++ b/crates/client/src/ui/pause.rs @@ -0,0 +1,187 @@ +use bevy::{color::palettes::css::BLACK, prelude::*}; +use shared::{HEDZ_GREEN, HEDZ_PURPLE, control::CharacterInputEnabled, loading_assets::UIAssets}; + +use crate::GameState; + +#[derive(States, Default, Clone, Eq, PartialEq, Debug, Hash)] +#[states(scoped_entities)] +enum PauseMenuState { + #[default] + Closed, + Open, +} + +#[derive(Component, PartialEq, Eq, Clone, Copy)] +enum ProgressBar { + Music, + Sound, +} + +#[derive(Resource)] +struct PauseMenuSelection(ProgressBar); + +pub fn plugin(app: &mut App) { + app.init_state::(); + + app.add_systems(Update, open_pause_menu.run_if(in_state(GameState::Playing))); + app.add_systems( + Update, + (selection_input, selection_changed).run_if(in_state(PauseMenuState::Open)), + ); + app.add_systems(OnEnter(PauseMenuState::Open), setup); +} + +fn open_pause_menu( + state: Res>, + mut next_state: ResMut>, + mut char_controls: ResMut, + keyboard: Res>, +) { + if keyboard.just_pressed(KeyCode::Escape) { + let menu_open = match state.get() { + PauseMenuState::Closed => { + next_state.set(PauseMenuState::Open); + true + } + PauseMenuState::Open => { + next_state.set(PauseMenuState::Closed); + false + } + }; + + if menu_open { + *char_controls = CharacterInputEnabled::Off; + } else { + *char_controls = CharacterInputEnabled::On; + } + } +} + +fn setup(mut commands: Commands, assets: Res) { + commands.spawn(( + Name::new("pause-menu"), + Node { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + flex_direction: FlexDirection::Column, + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + row_gap: Val::Px(10.), + ..default() + }, + BackgroundColor(Color::linear_rgba(0., 0., 0., 0.6)), + StateScoped(PauseMenuState::Open), + children![ + spawn_progress(ProgressBar::Music, 100, assets.font.clone()), + spawn_progress(ProgressBar::Sound, 80, assets.font.clone()) + ], + )); + + commands.insert_resource(PauseMenuSelection(ProgressBar::Music)); +} + +fn spawn_progress(bar: ProgressBar, value: u8, font: Handle) -> impl Bundle { + ( + Node { + width: Val::Px(500.0), + height: Val::Px(60.0), + border: UiRect::all(Val::Px(8.)), + align_items: AlignItems::Center, + row_gap: Val::Px(10.), + ..default() + }, + BackgroundColor(BLACK.into()), + BorderRadius::all(Val::Px(100.)), + BorderColor(HEDZ_PURPLE.into()), + BoxShadow::new( + BLACK.into(), + Val::Px(2.), + Val::Px(2.), + Val::Px(4.), + Val::Px(4.), + ), + bar, + children![ + ( + Node { + width: Val::Percent(100.0), + margin: UiRect::left(Val::Px(10.)), + ..default() + }, + Text::new(match bar { + ProgressBar::Music => "MUSIC".to_string(), + ProgressBar::Sound => "SOUND".to_string(), + }), + TextFont { + font: font.clone(), + font_size: 16.0, + ..Default::default() + }, + TextColor(HEDZ_GREEN.into()), + TextLayout::new_with_justify(JustifyText::Left), + ), + ( + Node { + margin: UiRect::horizontal(Val::Px(5.)), + ..default() + }, + Text::new("<".to_string()), + TextFont { + font: font.clone(), + font_size: 16.0, + ..Default::default() + }, + TextColor(HEDZ_GREEN.into()), + TextLayout::new_with_justify(JustifyText::Left).with_no_wrap(), + ), + ( + Text::new(format!("{value}",)), + TextFont { + font: font.clone(), + font_size: 16.0, + ..Default::default() + }, + TextColor(HEDZ_GREEN.into()), + TextLayout::new_with_justify(JustifyText::Left).with_no_wrap(), + ), + ( + Node { + margin: UiRect::horizontal(Val::Px(5.)), + ..default() + }, + Text::new(">".to_string()), + TextFont { + font, + font_size: 16.0, + ..Default::default() + }, + TextColor(HEDZ_GREEN.into()), + TextLayout::new_with_justify(JustifyText::Left).with_no_wrap(), + ) + ], + ) +} + +fn selection_input(mut state: ResMut, keyboard: Res>) { + if keyboard.just_pressed(KeyCode::ArrowUp) || keyboard.just_pressed(KeyCode::ArrowDown) { + state.0 = match state.0 { + ProgressBar::Music => ProgressBar::Sound, + ProgressBar::Sound => ProgressBar::Music, + } + } +} + +fn selection_changed( + state: Res, + mut query: Query<(&mut BorderColor, &ProgressBar)>, +) { + if state.is_changed() { + for (mut border, bar) in query.iter_mut() { + border.0 = if *bar == state.0 { + HEDZ_GREEN.into() + } else { + HEDZ_PURPLE.into() + } + } + } +} diff --git a/crates/shared/src/backpack/backpack_ui.rs b/crates/shared/src/backpack/backpack_ui.rs index 6be0f9d..90d37e8 100644 --- a/crates/shared/src/backpack/backpack_ui.rs +++ b/crates/shared/src/backpack/backpack_ui.rs @@ -1,6 +1,7 @@ use super::{BackbackSwapEvent, Backpack, UiHeadState}; use crate::{ - GameState, global_observer, heads::HeadsImages, loading_assets::UIAssets, sounds::PlaySound, + GameState, HEDZ_GREEN, global_observer, heads::HeadsImages, loading_assets::UIAssets, + sounds::PlaySound, }; use bevy::{ecs::spawn::SpawnIter, prelude::*}; @@ -96,7 +97,7 @@ fn setup(mut commands: Commands, assets: Res) { font_size: 34.0, ..default() }, - TextColor(Color::Srgba(Srgba::rgb(0., 1., 0.))), + TextColor(HEDZ_GREEN.into()), TextLayout::new_with_justify(JustifyText::Center), Node { position_type: PositionType::Absolute, diff --git a/crates/shared/src/cash.rs b/crates/shared/src/cash.rs index ecce897..12ae89e 100644 --- a/crates/shared/src/cash.rs +++ b/crates/shared/src/cash.rs @@ -1,4 +1,4 @@ -use crate::{GameState, global_observer, loading_assets::UIAssets, sounds::PlaySound}; +use crate::{GameState, HEDZ_GREEN, global_observer, loading_assets::UIAssets, sounds::PlaySound}; use bevy::prelude::*; #[derive(Component, Reflect, Default)] @@ -70,7 +70,7 @@ fn setup(mut commands: Commands, assets: Res) { font_size: 34.0, ..default() }, - TextColor(Color::Srgba(Srgba::rgb(0., 1., 0.))), + TextColor(HEDZ_GREEN.into()), TextLayout::new_with_justify(JustifyText::Center), Node { position_type: PositionType::Absolute, diff --git a/crates/shared/src/control/controls.rs b/crates/shared/src/control/controls.rs index 81cd60b..4ae5cb6 100644 --- a/crates/shared/src/control/controls.rs +++ b/crates/shared/src/control/controls.rs @@ -3,7 +3,7 @@ use crate::{ GameState, abilities::{TriggerCashHeal, TriggerState}, backpack::BackpackAction, - control::ControllerSet, + control::{CharacterInputEnabled, ControllerSet}, heads::SelectActiveHead, }; use bevy::{ @@ -32,7 +32,14 @@ pub fn plugin(app: &mut App) { ) .chain() .in_set(ControllerSet::CollectInputs) - .run_if(in_state(GameState::Playing)), + .run_if( + in_state(GameState::Playing) + .and(resource_exists_and_equals(CharacterInputEnabled::On)), + ), + ); + app.add_systems( + Update, + char_controls_state.run_if(in_state(GameState::Playing)), ); } @@ -43,6 +50,17 @@ pub struct ControllerSettings { pub jump_force: f32, } +fn char_controls_state( + state: Res, + mut controls: ResMut, + mut control_state: ResMut, +) { + if state.is_changed() && matches!(*state, CharacterInputEnabled::Off) { + *controls = Controls::default(); + *control_state = ControlState::default(); + } +} + fn combine_controls(controls: Res, mut combined_controls: ResMut) { let keyboard = controls.keyboard_state; diff --git a/crates/shared/src/control/mod.rs b/crates/shared/src/control/mod.rs index 7b1293e..a487dd6 100644 --- a/crates/shared/src/control/mod.rs +++ b/crates/shared/src/control/mod.rs @@ -35,6 +35,12 @@ struct Controls { gamepad_state: Option, } +#[derive(Resource, Debug, PartialEq, Eq)] +pub enum CharacterInputEnabled { + On, + Off, +} + #[derive(Event)] pub struct ControllerSwitchEvent; @@ -44,6 +50,7 @@ pub struct SelectedController(ControllerSet); pub fn plugin(app: &mut App) { app.init_resource::(); app.init_resource::(); + app.insert_resource(CharacterInputEnabled::On); app.add_plugins(controls::plugin); app.add_plugins(controller_common::plugin); diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index b8dabe2..3c84e18 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -32,6 +32,9 @@ pub mod water; use bevy::{core_pipeline::tonemapping::Tonemapping, prelude::*}; use utils::{billboards, squish_animation}; +pub const HEDZ_GREEN: Srgba = Srgba::rgb(0.0, 1.0, 0.0); +pub const HEDZ_PURPLE: Srgba = Srgba::rgb(91. / 256., 4. / 256., 138. / 256.); + #[derive(Resource, Reflect, Debug)] #[reflect(Resource)] pub struct DebugVisuals { diff --git a/justfile b/justfile index 7820c4c..f68a8a9 100644 --- a/justfile +++ b/justfile @@ -21,5 +21,5 @@ check: cargo sort --check --workspace cargo fmt --check cargo b - cargo test cargo clippy + cargo test