feat(engine): add Menu dropdown for Stats/Achievements/Profile/Settings/Leaderboard
Continues the UI-first pass. The five informational overlays were each behind a single-key shortcut (S/A/P/O/L) with no visible UI affordance. Add a "Menu ▾" button to the action bar that toggles a popover with one row per overlay. Each row dispatches the same code path the keyboard accelerator uses by writing a new `Toggle*RequestEvent`: - Stats → ToggleStatsRequestEvent - Achievements → ToggleAchievementsRequestEvent - Profile → ToggleProfileRequestEvent - Settings → ToggleSettingsRequestEvent - Leaderboard → ToggleLeaderboardRequestEvent Each plugin's existing toggle handler now reads either its key or the matching request event so the spawn / despawn / fetch logic stays in the owning plugin (the popover never duplicates that behaviour). Action bar order is now (left → right): Menu ▾ Undo Pause Help Modes ▾ New Game Menu sits on the far left because it's a navigation aggregator; New Game stays on the far right as the most consequential action. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,9 @@ use solitaire_data::{
|
||||
save_progress_to,
|
||||
};
|
||||
|
||||
use crate::events::{AchievementUnlockedEvent, GameWonEvent, XpAwardedEvent};
|
||||
use crate::events::{
|
||||
AchievementUnlockedEvent, GameWonEvent, ToggleAchievementsRequestEvent, XpAwardedEvent,
|
||||
};
|
||||
use crate::game_plugin::GameMutation;
|
||||
use crate::progress_plugin::{LevelUpEvent, ProgressResource, ProgressStoragePath, ProgressUpdate};
|
||||
use crate::resources::GameStateResource;
|
||||
@@ -73,6 +75,7 @@ impl Plugin for AchievementPlugin {
|
||||
.add_message::<AchievementUnlockedEvent>()
|
||||
.add_message::<GameWonEvent>()
|
||||
.add_message::<XpAwardedEvent>()
|
||||
.add_message::<ToggleAchievementsRequestEvent>()
|
||||
// Run after GameMutation (so GameWonEvent is available), after
|
||||
// StatsUpdate (so stats reflect this win), and after ProgressUpdate
|
||||
// (so daily_challenge_streak is up to date for daily_devotee).
|
||||
@@ -197,14 +200,17 @@ pub fn display_name_for(id: &str) -> String {
|
||||
.unwrap_or_else(|| id.to_string())
|
||||
}
|
||||
|
||||
/// Toggle the achievements overlay with the `A` key.
|
||||
/// Toggle the achievements overlay — `A` keyboard accelerator or
|
||||
/// `ToggleAchievementsRequestEvent` from the HUD Menu popover.
|
||||
fn toggle_achievements_screen(
|
||||
mut commands: Commands,
|
||||
keys: Res<ButtonInput<KeyCode>>,
|
||||
mut requests: MessageReader<ToggleAchievementsRequestEvent>,
|
||||
achievements: Res<AchievementsResource>,
|
||||
screens: Query<Entity, With<AchievementsScreen>>,
|
||||
) {
|
||||
if !keys.just_pressed(KeyCode::KeyA) {
|
||||
let button_clicked = requests.read().count() > 0;
|
||||
if !keys.just_pressed(KeyCode::KeyA) && !button_clicked {
|
||||
return;
|
||||
}
|
||||
if let Ok(entity) = screens.single() {
|
||||
|
||||
@@ -129,6 +129,31 @@ pub struct StartTimeAttackRequestEvent;
|
||||
#[derive(Message, Debug, Clone, Copy, Default)]
|
||||
pub struct StartDailyChallengeRequestEvent;
|
||||
|
||||
/// Request to toggle the Stats overlay. Fired by the HUD Menu-popover
|
||||
/// "Stats" row alongside the existing `S` accelerator.
|
||||
#[derive(Message, Debug, Clone, Copy, Default)]
|
||||
pub struct ToggleStatsRequestEvent;
|
||||
|
||||
/// Request to toggle the Achievements overlay. Fired by the HUD
|
||||
/// Menu-popover "Achievements" row alongside the existing `A` accelerator.
|
||||
#[derive(Message, Debug, Clone, Copy, Default)]
|
||||
pub struct ToggleAchievementsRequestEvent;
|
||||
|
||||
/// Request to toggle the Profile overlay. Fired by the HUD Menu-popover
|
||||
/// "Profile" row alongside the existing `P` accelerator.
|
||||
#[derive(Message, Debug, Clone, Copy, Default)]
|
||||
pub struct ToggleProfileRequestEvent;
|
||||
|
||||
/// Request to toggle the Settings overlay. Fired by the HUD Menu-popover
|
||||
/// "Settings" row alongside the existing `O` accelerator.
|
||||
#[derive(Message, Debug, Clone, Copy, Default)]
|
||||
pub struct ToggleSettingsRequestEvent;
|
||||
|
||||
/// Request to toggle the Leaderboard overlay. Fired by the HUD
|
||||
/// Menu-popover "Leaderboard" row alongside the existing `L` accelerator.
|
||||
#[derive(Message, Debug, Clone, Copy, Default)]
|
||||
pub struct ToggleLeaderboardRequestEvent;
|
||||
|
||||
/// Fired by `SyncPlugin` after a pull task resolves and the merged result has
|
||||
/// been persisted to disk. `Ok(SyncResponse)` carries the merged payload plus
|
||||
/// any `ConflictReport`s the merge produced. `Err(String)` carries a
|
||||
|
||||
@@ -18,7 +18,9 @@ use crate::progress_plugin::ProgressResource;
|
||||
use crate::events::{
|
||||
HelpRequestEvent, InfoToastEvent, NewGameRequestEvent, PauseRequestEvent,
|
||||
StartChallengeRequestEvent, StartDailyChallengeRequestEvent, StartTimeAttackRequestEvent,
|
||||
StartZenRequestEvent, UndoRequestEvent,
|
||||
StartZenRequestEvent, ToggleAchievementsRequestEvent, ToggleLeaderboardRequestEvent,
|
||||
ToggleProfileRequestEvent, ToggleSettingsRequestEvent, ToggleStatsRequestEvent,
|
||||
UndoRequestEvent,
|
||||
};
|
||||
use crate::font_plugin::FontResource;
|
||||
use crate::game_plugin::GameMutation;
|
||||
@@ -143,6 +145,27 @@ pub enum ModeOption {
|
||||
TimeAttack,
|
||||
}
|
||||
|
||||
/// Marker on the "Menu" action button. Click toggles the [`MenuPopover`]
|
||||
/// which exposes the Stats / Achievements / Profile / Settings /
|
||||
/// Leaderboard overlays without needing the S/A/P/O/L hotkeys.
|
||||
#[derive(Component, Debug)]
|
||||
pub struct MenuButton;
|
||||
|
||||
/// Marker on the dropdown panel that opens below the [`MenuButton`].
|
||||
#[derive(Component, Debug)]
|
||||
pub struct MenuPopover;
|
||||
|
||||
/// One row inside the [`MenuPopover`]. The variant selects which
|
||||
/// `Toggle*RequestEvent` the click handler fires.
|
||||
#[derive(Component, Debug, Clone, Copy)]
|
||||
pub enum MenuOption {
|
||||
Stats,
|
||||
Achievements,
|
||||
Profile,
|
||||
Settings,
|
||||
Leaderboard,
|
||||
}
|
||||
|
||||
/// HUD Z-layer — above cards (which start at z=0) but below overlay screens.
|
||||
const Z_HUD: i32 = 50;
|
||||
|
||||
@@ -170,6 +193,11 @@ impl Plugin for HudPlugin {
|
||||
.add_message::<StartChallengeRequestEvent>()
|
||||
.add_message::<StartTimeAttackRequestEvent>()
|
||||
.add_message::<StartDailyChallengeRequestEvent>()
|
||||
.add_message::<ToggleStatsRequestEvent>()
|
||||
.add_message::<ToggleAchievementsRequestEvent>()
|
||||
.add_message::<ToggleProfileRequestEvent>()
|
||||
.add_message::<ToggleSettingsRequestEvent>()
|
||||
.add_message::<ToggleLeaderboardRequestEvent>()
|
||||
.add_systems(Startup, (spawn_hud, spawn_action_buttons))
|
||||
.add_systems(Update, update_hud.after(GameMutation))
|
||||
.add_systems(Update, announce_auto_complete.after(GameMutation))
|
||||
@@ -183,6 +211,8 @@ impl Plugin for HudPlugin {
|
||||
handle_help_button,
|
||||
handle_modes_button,
|
||||
handle_mode_option_click,
|
||||
handle_menu_button,
|
||||
handle_menu_option_click,
|
||||
paint_action_buttons,
|
||||
),
|
||||
);
|
||||
@@ -292,6 +322,7 @@ fn spawn_action_buttons(font_res: Option<Res<FontResource>>, mut commands: Comma
|
||||
ZIndex(Z_HUD),
|
||||
))
|
||||
.with_children(|row| {
|
||||
spawn_action_button(row, MenuButton, "Menu \u{25BE}", &font);
|
||||
spawn_action_button(row, UndoButton, "Undo", &font);
|
||||
spawn_action_button(row, PauseButton, "Pause", &font);
|
||||
spawn_action_button(row, HelpButton, "Help", &font);
|
||||
@@ -520,6 +551,130 @@ fn handle_mode_option_click(
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggles the [`MenuPopover`]: spawns it on first click, despawns it on
|
||||
/// second click. The popover lists the five overlays previously only
|
||||
/// reachable via the S / A / P / O / L hotkeys.
|
||||
fn handle_menu_button(
|
||||
interaction_query: Query<&Interaction, (With<MenuButton>, Changed<Interaction>)>,
|
||||
popovers: Query<Entity, With<MenuPopover>>,
|
||||
font_res: Option<Res<FontResource>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
let pressed = interaction_query
|
||||
.iter()
|
||||
.any(|i| *i == Interaction::Pressed);
|
||||
if !pressed {
|
||||
return;
|
||||
}
|
||||
if let Ok(entity) = popovers.single() {
|
||||
commands.entity(entity).despawn();
|
||||
} else {
|
||||
spawn_menu_popover(&mut commands, font_res.as_deref());
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawns the menu popover anchored just below the action bar, with one
|
||||
/// row per overlay. Each row dispatches its corresponding
|
||||
/// `Toggle*RequestEvent` so the existing toggle handler runs (and the
|
||||
/// HUD never duplicates spawn / despawn / fetch logic).
|
||||
fn spawn_menu_popover(commands: &mut Commands, font_res: Option<&FontResource>) {
|
||||
let font = TextFont {
|
||||
font: font_res.map(|f| f.0.clone()).unwrap_or_default(),
|
||||
font_size: 15.0,
|
||||
..default()
|
||||
};
|
||||
|
||||
let rows: [(MenuOption, &'static str); 5] = [
|
||||
(MenuOption::Stats, "Stats"),
|
||||
(MenuOption::Achievements, "Achievements"),
|
||||
(MenuOption::Profile, "Profile"),
|
||||
(MenuOption::Settings, "Settings"),
|
||||
(MenuOption::Leaderboard, "Leaderboard"),
|
||||
];
|
||||
|
||||
commands
|
||||
.spawn((
|
||||
MenuPopover,
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
right: Val::Px(12.0),
|
||||
top: Val::Px(50.0),
|
||||
flex_direction: FlexDirection::Column,
|
||||
row_gap: Val::Px(4.0),
|
||||
padding: UiRect::all(Val::Px(8.0)),
|
||||
border_radius: BorderRadius::all(Val::Px(6.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::srgba(0.10, 0.12, 0.15, 0.96)),
|
||||
ZIndex(Z_HUD + 5),
|
||||
))
|
||||
.with_children(|panel| {
|
||||
for (option, label) in rows {
|
||||
panel
|
||||
.spawn((
|
||||
option,
|
||||
ActionButton,
|
||||
Button,
|
||||
Node {
|
||||
padding: UiRect::axes(Val::Px(12.0), Val::Px(6.0)),
|
||||
justify_content: JustifyContent::FlexStart,
|
||||
align_items: AlignItems::Center,
|
||||
min_width: Val::Px(150.0),
|
||||
border_radius: BorderRadius::all(Val::Px(4.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(ACTION_BTN_IDLE),
|
||||
))
|
||||
.with_children(|b| {
|
||||
b.spawn((Text::new(label), font.clone(), TextColor(Color::WHITE)));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Dispatches the click on a menu row to the matching toggle event,
|
||||
/// then despawns the popover.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn handle_menu_option_click(
|
||||
interaction_query: Query<(&Interaction, &MenuOption), Changed<Interaction>>,
|
||||
popovers: Query<Entity, With<MenuPopover>>,
|
||||
mut stats: MessageWriter<ToggleStatsRequestEvent>,
|
||||
mut achievements: MessageWriter<ToggleAchievementsRequestEvent>,
|
||||
mut profile: MessageWriter<ToggleProfileRequestEvent>,
|
||||
mut settings: MessageWriter<ToggleSettingsRequestEvent>,
|
||||
mut leaderboard: MessageWriter<ToggleLeaderboardRequestEvent>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
let mut clicked_any = false;
|
||||
for (interaction, option) in &interaction_query {
|
||||
if *interaction != Interaction::Pressed {
|
||||
continue;
|
||||
}
|
||||
clicked_any = true;
|
||||
match option {
|
||||
MenuOption::Stats => {
|
||||
stats.write(ToggleStatsRequestEvent);
|
||||
}
|
||||
MenuOption::Achievements => {
|
||||
achievements.write(ToggleAchievementsRequestEvent);
|
||||
}
|
||||
MenuOption::Profile => {
|
||||
profile.write(ToggleProfileRequestEvent);
|
||||
}
|
||||
MenuOption::Settings => {
|
||||
settings.write(ToggleSettingsRequestEvent);
|
||||
}
|
||||
MenuOption::Leaderboard => {
|
||||
leaderboard.write(ToggleLeaderboardRequestEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
if clicked_any
|
||||
&& let Ok(entity) = popovers.single() {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
|
||||
/// Visual feedback for every action button — paints idle / hover / pressed
|
||||
/// states by mutating `BackgroundColor` whenever the interaction state
|
||||
/// changes. One query covers all action buttons via the shared
|
||||
|
||||
@@ -14,7 +14,7 @@ use bevy::tasks::{futures_lite::future, AsyncComputeTaskPool, Task};
|
||||
use solitaire_data::settings::SyncBackend;
|
||||
use solitaire_sync::LeaderboardEntry;
|
||||
|
||||
use crate::events::InfoToastEvent;
|
||||
use crate::events::{InfoToastEvent, ToggleLeaderboardRequestEvent};
|
||||
use crate::settings_plugin::SettingsResource;
|
||||
use crate::sync_plugin::SyncProviderResource;
|
||||
|
||||
@@ -73,6 +73,7 @@ impl Plugin for LeaderboardPlugin {
|
||||
.init_resource::<ClosedThisFrame>()
|
||||
.init_resource::<OptInTask>()
|
||||
.init_resource::<OptOutTask>()
|
||||
.add_message::<ToggleLeaderboardRequestEvent>()
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
@@ -99,18 +100,22 @@ fn reset_closed_flag(mut flag: ResMut<ClosedThisFrame>) {
|
||||
flag.0 = false;
|
||||
}
|
||||
|
||||
/// `L` key — open or close the leaderboard panel.
|
||||
/// On open, starts a new fetch if no data is cached or a fetch is not in flight.
|
||||
/// `L` keyboard accelerator or `ToggleLeaderboardRequestEvent` from the
|
||||
/// HUD Menu popover — open or close the leaderboard panel. On open,
|
||||
/// starts a new fetch if no data is cached or a fetch is not in flight.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn toggle_leaderboard_screen(
|
||||
mut commands: Commands,
|
||||
keys: Res<ButtonInput<KeyCode>>,
|
||||
mut requests: MessageReader<ToggleLeaderboardRequestEvent>,
|
||||
screens: Query<Entity, With<LeaderboardScreen>>,
|
||||
data: Res<LeaderboardResource>,
|
||||
provider: Option<Res<SyncProviderResource>>,
|
||||
mut task_res: ResMut<LeaderboardFetchTask>,
|
||||
mut closed_flag: ResMut<ClosedThisFrame>,
|
||||
) {
|
||||
if !keys.just_pressed(KeyCode::KeyL) {
|
||||
let button_clicked = requests.read().count() > 0;
|
||||
if !keys.just_pressed(KeyCode::KeyL) && !button_clicked {
|
||||
return;
|
||||
}
|
||||
if let Ok(entity) = screens.single() {
|
||||
|
||||
@@ -71,14 +71,16 @@ pub use events::{
|
||||
ManualSyncRequestEvent, MoveRejectedEvent, MoveRequestEvent, NewGameConfirmEvent,
|
||||
NewGameRequestEvent, PauseRequestEvent, StartChallengeRequestEvent,
|
||||
StartDailyChallengeRequestEvent, StartTimeAttackRequestEvent, StartZenRequestEvent,
|
||||
StateChangedEvent, SyncCompleteEvent, UndoRequestEvent, XpAwardedEvent,
|
||||
StateChangedEvent, SyncCompleteEvent, ToggleAchievementsRequestEvent,
|
||||
ToggleLeaderboardRequestEvent, ToggleProfileRequestEvent, ToggleSettingsRequestEvent,
|
||||
ToggleStatsRequestEvent, UndoRequestEvent, XpAwardedEvent,
|
||||
};
|
||||
pub use game_plugin::{ConfirmNewGameScreen, GameMutation, GameOverScreen, GamePlugin, GameStatePath};
|
||||
pub use help_plugin::{HelpPlugin, HelpScreen};
|
||||
pub use home_plugin::{HomePlugin, HomeScreen};
|
||||
pub use hud_plugin::{
|
||||
ActionButton, HelpButton, HudAutoComplete, HudPlugin, ModeOption, ModesButton, ModesPopover,
|
||||
NewGameButton, PauseButton, UndoButton,
|
||||
ActionButton, HelpButton, HudAutoComplete, HudPlugin, MenuButton, MenuOption, MenuPopover,
|
||||
ModeOption, ModesButton, ModesPopover, NewGameButton, PauseButton, UndoButton,
|
||||
};
|
||||
pub use leaderboard_plugin::{LeaderboardPlugin, LeaderboardResource, LeaderboardScreen};
|
||||
pub use input_plugin::InputPlugin;
|
||||
|
||||
@@ -10,6 +10,7 @@ use solitaire_core::achievement::achievement_by_id;
|
||||
use solitaire_data::SyncBackend;
|
||||
|
||||
use crate::achievement_plugin::AchievementsResource;
|
||||
use crate::events::ToggleProfileRequestEvent;
|
||||
use crate::progress_plugin::ProgressResource;
|
||||
use crate::resources::{SyncStatus, SyncStatusResource};
|
||||
use crate::settings_plugin::SettingsResource;
|
||||
@@ -24,7 +25,8 @@ pub struct ProfilePlugin;
|
||||
|
||||
impl Plugin for ProfilePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Update, toggle_profile_screen);
|
||||
app.add_message::<ToggleProfileRequestEvent>()
|
||||
.add_systems(Update, toggle_profile_screen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +34,7 @@ impl Plugin for ProfilePlugin {
|
||||
fn toggle_profile_screen(
|
||||
mut commands: Commands,
|
||||
keys: Res<ButtonInput<KeyCode>>,
|
||||
mut requests: MessageReader<ToggleProfileRequestEvent>,
|
||||
settings: Option<Res<SettingsResource>>,
|
||||
sync_status: Option<Res<SyncStatusResource>>,
|
||||
progress: Option<Res<ProgressResource>>,
|
||||
@@ -39,7 +42,8 @@ fn toggle_profile_screen(
|
||||
stats: Option<Res<StatsResource>>,
|
||||
screens: Query<Entity, With<ProfileScreen>>,
|
||||
) {
|
||||
if !keys.just_pressed(KeyCode::KeyP) {
|
||||
let button_clicked = requests.read().count() > 0;
|
||||
if !keys.just_pressed(KeyCode::KeyP) && !button_clicked {
|
||||
return;
|
||||
}
|
||||
if let Ok(entity) = screens.single() {
|
||||
|
||||
@@ -16,7 +16,7 @@ use bevy::prelude::*;
|
||||
use solitaire_core::game_state::DrawMode;
|
||||
use solitaire_data::{load_settings_from, save_settings_to, settings_file_path, settings::Theme, AnimSpeed, Settings};
|
||||
|
||||
use crate::events::ManualSyncRequestEvent;
|
||||
use crate::events::{ManualSyncRequestEvent, ToggleSettingsRequestEvent};
|
||||
use crate::progress_plugin::ProgressResource;
|
||||
use crate::resources::{SettingsScrollPos, SyncStatus, SyncStatusResource};
|
||||
|
||||
@@ -146,6 +146,7 @@ impl Plugin for SettingsPlugin {
|
||||
.init_resource::<SettingsScrollPos>()
|
||||
.add_message::<SettingsChangedEvent>()
|
||||
.add_message::<ManualSyncRequestEvent>()
|
||||
.add_message::<ToggleSettingsRequestEvent>()
|
||||
.add_message::<bevy::input::mouse::MouseWheel>()
|
||||
.add_systems(Update, (handle_volume_keys, toggle_settings_screen, scroll_settings_panel));
|
||||
|
||||
@@ -206,12 +207,15 @@ fn handle_volume_keys(
|
||||
changed.write(SettingsChangedEvent(settings.0.clone()));
|
||||
}
|
||||
|
||||
/// Opens or closes the Settings panel when `O` is pressed.
|
||||
/// Opens or closes the Settings panel — `O` keyboard accelerator or
|
||||
/// `ToggleSettingsRequestEvent` from the HUD Menu popover.
|
||||
fn toggle_settings_screen(
|
||||
keys: Res<ButtonInput<KeyCode>>,
|
||||
mut requests: MessageReader<ToggleSettingsRequestEvent>,
|
||||
mut screen: ResMut<SettingsScreen>,
|
||||
) {
|
||||
if keys.just_pressed(KeyCode::KeyO) {
|
||||
let button_clicked = requests.read().count() > 0;
|
||||
if keys.just_pressed(KeyCode::KeyO) || button_clicked {
|
||||
screen.0 = !screen.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@ use solitaire_data::{
|
||||
|
||||
use crate::auto_complete_plugin::AutoCompleteState;
|
||||
use crate::challenge_plugin::challenge_progress_label;
|
||||
use crate::events::{ForfeitEvent, GameWonEvent, InfoToastEvent, NewGameRequestEvent};
|
||||
use crate::events::{
|
||||
ForfeitEvent, GameWonEvent, InfoToastEvent, NewGameRequestEvent, ToggleStatsRequestEvent,
|
||||
};
|
||||
use crate::game_plugin::GameMutation;
|
||||
use crate::progress_plugin::ProgressResource;
|
||||
use crate::resources::GameStateResource;
|
||||
@@ -81,6 +83,7 @@ impl Plugin for StatsPlugin {
|
||||
.add_message::<NewGameRequestEvent>()
|
||||
.add_message::<ForfeitEvent>()
|
||||
.add_message::<InfoToastEvent>()
|
||||
.add_message::<ToggleStatsRequestEvent>()
|
||||
// record_abandoned must read `move_count` BEFORE handle_new_game
|
||||
// clobbers it with a fresh game. These are NOT in StatsUpdate because
|
||||
// StatsUpdate (as a set) is ordered after GameMutation by external
|
||||
@@ -181,12 +184,14 @@ fn handle_forfeit(
|
||||
fn toggle_stats_screen(
|
||||
mut commands: Commands,
|
||||
keys: Res<ButtonInput<KeyCode>>,
|
||||
mut requests: MessageReader<ToggleStatsRequestEvent>,
|
||||
stats: Res<StatsResource>,
|
||||
progress: Option<Res<ProgressResource>>,
|
||||
time_attack: Option<Res<TimeAttackResource>>,
|
||||
screens: Query<Entity, With<StatsScreen>>,
|
||||
) {
|
||||
if !keys.just_pressed(KeyCode::KeyS) {
|
||||
let button_clicked = requests.read().count() > 0;
|
||||
if !keys.just_pressed(KeyCode::KeyS) && !button_clicked {
|
||||
return;
|
||||
}
|
||||
if let Ok(entity) = screens.single() {
|
||||
|
||||
Reference in New Issue
Block a user