diff --git a/sbepis/src/menus.rs b/sbepis/src/menus.rs index 1ad30a9e..f84045ca 100644 --- a/sbepis/src/menus.rs +++ b/sbepis/src/menus.rs @@ -1,22 +1,32 @@ +use bevy::ecs::schedule::SystemConfigs; use bevy::prelude::*; use bevy::window::{CursorGrabMode, PrimaryWindow}; use leafwing_input_manager::plugin::InputManagerPlugin; -use leafwing_input_manager::prelude::ActionState; +use leafwing_input_manager::prelude::{ActionState, InputMap}; use leafwing_input_manager::{Actionlike, InputControlKind}; +use crate::input::input_managers_where_button_just_pressed; +use crate::iter_system::IteratorSystemTrait; + pub struct MenusPlugin; impl Plugin for MenusPlugin { fn build(&self, app: &mut App) { app.register_type::() + .register_type::>() + .register_type::>() .init_resource::() .add_event::() .add_event::() + .add_plugins(InputManagerMenuPlugin::::default()) .add_systems( Update, ( activate_stack_current.run_if(resource_changed::), show_mouse, hide_mouse, + hide_menus, + despawn_menus, + close_menu_on(MenuAction::CloseMenu), ), ); } @@ -55,6 +65,12 @@ pub struct MenuWithMouse; #[derive(Component)] pub struct MenuWithoutMouse; +#[derive(Component)] +pub struct MenuHidesWhenClosed; + +#[derive(Component)] +pub struct MenuDespawnsWhenClosed; + #[derive(Resource, Default, Debug, Reflect)] pub struct MenuStack { stack: Vec, @@ -88,6 +104,18 @@ pub struct MenuActivated(pub Entity); #[derive(Event)] pub struct MenuDeactivated(pub Entity); +#[derive(Clone, Copy, Eq, PartialEq, Hash, Reflect, Debug)] +pub enum MenuAction { + CloseMenu, +} +impl Actionlike for MenuAction { + fn input_control_kind(&self) -> InputControlKind { + match self { + MenuAction::CloseMenu => InputControlKind::Button, + } + } +} + fn activate_stack_current( mut menu_stack: ResMut, mut ev_activated: EventWriter, @@ -157,14 +185,47 @@ fn disable_input_managers( } } -#[derive(Clone, Copy, Eq, PartialEq, Hash, Reflect, Debug)] -pub enum MenuAction { - CloseMenu, +pub fn close_menu(In(menu): In, mut menu_stack: ResMut) { + menu_stack.remove(menu); } -impl Actionlike for MenuAction { - fn input_control_kind(&self) -> InputControlKind { - match self { - MenuAction::CloseMenu => InputControlKind::Button, + +fn hide_menus( + mut ev_deactivated: EventReader, + mut menus: Query<&mut Visibility, With>, +) { + for MenuDeactivated(menu) in ev_deactivated.read() { + if let Ok(mut visibility) = menus.get_mut(*menu) { + *visibility = Visibility::Hidden; + } + } +} + +fn despawn_menus( + mut ev_deactivated: EventReader, + mut menus: Query>, + mut commands: Commands, +) { + for MenuDeactivated(menu) in ev_deactivated.read() { + if let Ok(menu) = menus.get_mut(*menu) { + commands.entity(menu).despawn_recursive(); } } } + +pub fn show_menu( + mut menus: Query<(Entity, &mut Visibility), With>, + mut menu_stack: ResMut, +) { + let (quest_screen, mut visibility) = menus + .get_single_mut() + .expect("Single menu with marker not found"); + *visibility = Visibility::Inherited; + menu_stack.push(quest_screen); +} + +pub fn close_menu_on(action: Action) -> SystemConfigs { + input_managers_where_button_just_pressed(action) + .iter_map(close_menu) + .map(|_| ()) + .into_configs() +} diff --git a/sbepis/src/player_controller/mod.rs b/sbepis/src/player_controller/mod.rs index 79fd70f1..11124eb9 100644 --- a/sbepis/src/player_controller/mod.rs +++ b/sbepis/src/player_controller/mod.rs @@ -78,7 +78,8 @@ fn setup( .with(PlayerAction::Use, MouseButton::Left) .with(PlayerAction::Interact, KeyCode::KeyE) .with(PlayerAction::NextWeapon, MouseScrollDirection::UP) - .with(PlayerAction::PrevWeapon, MouseScrollDirection::DOWN), + .with(PlayerAction::PrevWeapon, MouseScrollDirection::DOWN) + .with(PlayerAction::OpenQuestScreen, KeyCode::KeyJ), false, ), Menu, @@ -187,6 +188,7 @@ pub enum PlayerAction { Interact, NextWeapon, PrevWeapon, + OpenQuestScreen, } impl Actionlike for PlayerAction { fn input_control_kind(&self) -> InputControlKind { @@ -199,6 +201,7 @@ impl Actionlike for PlayerAction { PlayerAction::Interact => InputControlKind::Button, PlayerAction::NextWeapon => InputControlKind::Button, PlayerAction::PrevWeapon => InputControlKind::Button, + PlayerAction::OpenQuestScreen => InputControlKind::Button, } } } diff --git a/sbepis/src/questing.rs b/sbepis/src/questing.rs index 8957428a..da3aea15 100644 --- a/sbepis/src/questing.rs +++ b/sbepis/src/questing.rs @@ -9,12 +9,12 @@ use rand::distributions::{Distribution, Standard}; use rand::Rng; use uuid::Uuid; -use crate::camera::{PlayerCamera, PlayerCameraNode}; +use crate::camera::PlayerCamera; use crate::input::{ button_just_pressed, input_manager_bundle, input_managers_where_button_just_pressed, }; use crate::iter_system::IteratorSystemTrait; -use crate::menus::{InputManagerMenuPlugin, Menu, MenuStack, MenuWithInputManager, MenuWithMouse}; +use crate::menus::*; use crate::player_controller::PlayerAction; use crate::some_or_return; @@ -32,6 +32,7 @@ impl Plugin for QuestingPlugin { .add_systems( Update, ( + // FIXME: Sometimes pressing interact will open and then immediately close the quest screen interact_with_quest_giver .pipe(propose_quest) .run_if(button_just_pressed(PlayerAction::Interact)), @@ -40,12 +41,10 @@ impl Plugin for QuestingPlugin { .iter_map(get_proposed_quest) .iter_map(finish_quest) .map(|_| ()), - input_managers_where_button_just_pressed(QuestProposalAction::Accept) - .iter_map(close_proposal) - .map(|_| ()), - input_managers_where_button_just_pressed(QuestProposalAction::Decline) - .iter_map(close_proposal) - .map(|_| ()), + close_menu_on(QuestProposalAction::Accept), + close_menu_on(QuestProposalAction::Decline), + show_menu:: + .run_if(button_just_pressed(PlayerAction::OpenQuestScreen)), ), ); @@ -160,24 +159,32 @@ pub struct QuestProposal { } fn spawn_quest_screen(mut commands: Commands) { - commands.spawn(( - Name::new("Quest Screen"), - NodeBundle { - style: Style { - width: Val::Percent(100.0), - height: Val::Percent(100.0), - flex_direction: FlexDirection::Column, - justify_content: JustifyContent::Center, - align_items: AlignItems::Center, - display: bevy::ui::Display::None, + commands + .spawn(( + NodeBundle { + style: Style { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + flex_direction: FlexDirection::Column, + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + ..default() + }, + background_color: bevy::color::palettes::css::GRAY.with_alpha(0.5).into(), + visibility: Visibility::Hidden, ..default() }, - background_color: bevy::color::palettes::css::GRAY.with_alpha(0.5).into(), - ..default() - }, - QuestScreen::default(), - PlayerCameraNode, - )); + input_manager_bundle( + InputMap::default().with(MenuAction::CloseMenu, KeyCode::KeyJ), + false, + ), + Menu, + MenuWithMouse, + MenuWithInputManager, + MenuHidesWhenClosed, + QuestScreen::default(), + )) + .insert(Name::new("Quest Screen")); } fn interact_with_quest_giver( @@ -260,6 +267,7 @@ fn propose_quest( Menu, MenuWithMouse, MenuWithInputManager, + MenuDespawnsWhenClosed, QuestProposal { quest_id }, )) .insert(Name::new(format!("Quest Proposal for {quest_id}"))) @@ -295,15 +303,6 @@ fn finish_quest( quest_giver.given_quest = None; } -fn close_proposal( - In(input): In, - mut commands: Commands, - mut menu_stack: ResMut, -) { - menu_stack.remove(input); - commands.entity(input).despawn_recursive(); -} - fn update_quest_screen_nodes( mut commands: Commands, quests: Res,