Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make menu states hiearchical #660

Merged
merged 1 commit into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 7 additions & 16 deletions crates/menu/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
use aftergame::AfterGamePlugin;
use bevy::{app::PluginGroupBuilder, prelude::*};
use create::CreateGamePlugin;
use de_core::{gresult::GameResult, nested_state, state::AppState};
use gamelisting::GameListingPlugin;
use mainmenu::MainMenuPlugin;
use mapselection::MapSelectionPlugin;
use menu::MenuPlugin;
use signin::SignInPlugin;
use menu::{MenuPlugin, ScreenStatePlugin};
use multiplayer::MultiplayerPlugin;
use singleplayer::SinglePlayerPlugin;

mod aftergame;
mod create;
mod gamelisting;
mod mainmenu;
mod mapselection;
mod menu;
mod requests;
mod signin;
mod multiplayer;
mod singleplayer;

pub struct MenuPluginGroup;
Expand All @@ -26,27 +21,23 @@ impl PluginGroup for MenuPluginGroup {
PluginGroupBuilder::start::<Self>()
.add(MenuStatePlugin)
.add(MenuPlugin)
.add(ScreenStatePlugin::<MenuState>::default())
.add(MainMenuPlugin)
.add(MapSelectionPlugin)
.add(SignInPlugin)
.add(GameListingPlugin)
.add(SinglePlayerPlugin)
.add(CreateGamePlugin)
.add(MultiplayerPlugin)
.add(AfterGamePlugin)
}
}

nested_state!(
AppState::InMenu -> MenuState,
doc = "Top-level menu state. Each variant corresponds to a single menu screen.",
doc = "Top-level menu state. Each variant corresponds to menu section or a single menu screen.",
enter = menu_entered_system,
variants = {
MainMenu,
SinglePlayerGame,
SignIn,
GameListing,
GameCreation,
MultiPlayerGame,
Multiplayer,
AfterGame,
}
);
Expand Down
2 changes: 1 addition & 1 deletion crates/menu/src/mainmenu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn setup(mut commands: GuiCommands, menu: Res<Menu>) {
button(
&mut commands,
column_node,
ButtonAction::SwithState(MenuState::SignIn),
ButtonAction::SwithState(MenuState::Multiplayer),
"Multiplayer",
);
button(&mut commands, column_node, ButtonAction::Quit, "Quit Game");
Expand Down
21 changes: 19 additions & 2 deletions crates/menu/src/menu.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::marker::PhantomData;

use bevy::prelude::*;
use de_core::state::AppState;
use de_gui::{ButtonCommands, GuiCommands, OuterStyle};
Expand All @@ -10,7 +12,6 @@ impl Plugin for MenuPlugin {
fn build(&self, app: &mut App) {
app.add_systems(OnEnter(AppState::InMenu), setup)
.add_systems(OnExit(AppState::InMenu), cleanup)
.add_systems(PreUpdate, clean_up_root.run_if(resource_exists::<Menu>()))
.add_systems(
Update,
(
Expand All @@ -23,6 +24,22 @@ impl Plugin for MenuPlugin {
}
}

/// This plugin handles state transitions leading to new screen (i.e. screen
/// cleaning).
#[derive(Default)]
pub(crate) struct ScreenStatePlugin<S: States> {
_marker: PhantomData<S>,
}

impl<S: States> Plugin for ScreenStatePlugin<S> {
fn build(&self, app: &mut App) {
app.add_systems(
PreUpdate,
clean_up_root::<S>.run_if(resource_exists::<Menu>()),
);
}
}

#[derive(Resource)]
pub(crate) struct Menu {
root_node: Entity,
Expand Down Expand Up @@ -51,7 +68,7 @@ enum ButtonAction {
Close,
}

fn clean_up_root(mut commands: Commands, state: Res<NextState<MenuState>>, menu: Res<Menu>) {
fn clean_up_root<S: States>(mut commands: Commands, state: Res<NextState<S>>, menu: Res<Menu>) {
if state.0.is_none() {
return;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ use de_lobby_client::CreateGameRequest;
use de_lobby_model::{GameConfig, GameMap, GameSetup, Validatable};
use de_map::hash::MapHash;

use super::MultiplayerState;
use crate::{
mapselection::{MapSelectedEvent, SelectMapEvent},
menu::Menu,
requests::{Receiver, RequestsPlugin, Sender},
MenuState,
multiplayer::requests::{Receiver, RequestsPlugin, Sender},
};

pub(crate) struct CreateGamePlugin;
pub(super) struct CreateGamePlugin;

impl Plugin for CreateGamePlugin {
fn build(&self, app: &mut App) {
app.add_plugins(RequestsPlugin::<CreateGameRequest>::new())
.add_event::<CreateGameEvent>()
.add_systems(OnEnter(MenuState::GameCreation), setup)
.add_systems(OnExit(MenuState::GameCreation), cleanup)
.add_systems(OnEnter(MultiplayerState::GameCreation), setup)
.add_systems(OnExit(MultiplayerState::GameCreation), cleanup)
.add_systems(
Update,
(
Expand All @@ -35,13 +35,13 @@ impl Plugin for CreateGamePlugin {
.after(CreateSet::MapSelected),
response_system,
)
.run_if(in_state(MenuState::GameCreation)),
.run_if(in_state(MultiplayerState::GameCreation)),
);
}
}

#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, SystemSet)]
pub(crate) enum CreateSet {
enum CreateSet {
Buttons,
MapSelected,
}
Expand Down Expand Up @@ -271,13 +271,13 @@ fn create_game_system(
}

fn response_system(
mut next_state: ResMut<NextState<MenuState>>,
mut next_state: ResMut<NextState<MultiplayerState>>,
mut receiver: Receiver<CreateGameRequest>,
mut toasts: EventWriter<ToastEvent>,
) {
if let Some(result) = receiver.receive() {
match result {
Ok(_) => next_state.set(MenuState::MultiPlayerGame),
Ok(_) => next_state.set(MultiplayerState::GameJoined),
Err(error) => toasts.send(ToastEvent::new(error)),
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@ use de_gui::{ButtonCommands, GuiCommands, LabelCommands, OuterStyle, ToastEvent}
use de_lobby_client::{ListGamesRequest, RequestEvent, ResponseEvent};
use de_lobby_model::GamePartial;

use crate::{menu::Menu, MenuState};
use super::MultiplayerState;
use crate::menu::Menu;

const REFRESH_INTERVAL: Duration = Duration::from_secs(10);

pub(crate) struct GameListingPlugin;
pub(super) struct GameListingPlugin;

impl Plugin for GameListingPlugin {
fn build(&self, app: &mut App) {
app.add_systems(OnEnter(MenuState::GameListing), setup)
.add_systems(OnExit(MenuState::GameListing), cleanup)
app.add_systems(OnEnter(MultiplayerState::GameListing), setup)
.add_systems(OnExit(MultiplayerState::GameListing), cleanup)
.add_systems(
Update,
(refresh_system, list_games_system, button_system)
.run_if(in_state(MenuState::GameListing)),
.run_if(in_state(MultiplayerState::GameListing)),
);
}
}
Expand Down Expand Up @@ -183,14 +184,14 @@ fn list_games_system(
}

fn button_system(
mut next_state: ResMut<NextState<MenuState>>,
mut next_state: ResMut<NextState<MultiplayerState>>,
interactions: Query<(&Interaction, &ButtonAction), Changed<Interaction>>,
mut toasts: EventWriter<ToastEvent>,
) {
for (&interaction, action) in interactions.iter() {
if let Interaction::Pressed = interaction {
match action {
ButtonAction::Create => next_state.set(MenuState::GameCreation),
ButtonAction::Create => next_state.set(MultiplayerState::GameCreation),
ButtonAction::Join => {
toasts.send(ToastEvent::new("Not yet implemented (issue #301)."))
}
Expand Down
40 changes: 40 additions & 0 deletions crates/menu/src/multiplayer/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use bevy::prelude::*;
use de_core::nested_state;

use self::{create::CreateGamePlugin, gamelisting::GameListingPlugin, signin::SignInPlugin};
use crate::{menu::ScreenStatePlugin, MenuState};

mod create;
mod gamelisting;
mod requests;
mod signin;

pub(super) struct MultiplayerPlugin;

impl Plugin for MultiplayerPlugin {
fn build(&self, app: &mut App) {
app.add_plugins((
MultiplayerStatePlugin,
ScreenStatePlugin::<MultiplayerState>::default(),
SignInPlugin,
GameListingPlugin,
CreateGamePlugin,
));
}
}

nested_state!(
MenuState::Multiplayer -> MultiplayerState,
doc = "Each state corresponds to an individual multiplayer related menu screen.",
enter = multiplayer_entered_system,
variants = {
SignIn,
GameListing,
GameCreation,
GameJoined,
}
);

fn multiplayer_entered_system(mut next_state: ResMut<NextState<MultiplayerState>>) {
next_state.set(MultiplayerState::SignIn);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::marker::PhantomData;

use bevy::{ecs::system::SystemParam, prelude::*};
use de_core::state::AppState;
use de_lobby_client::{LobbyRequest, RequestEvent, ResponseEvent, Result};

pub(crate) struct RequestsPlugin<T>
use crate::MenuState;

pub(super) struct RequestsPlugin<T>
where
T: LobbyRequest,
{
Expand All @@ -15,7 +16,7 @@ impl<T> RequestsPlugin<T>
where
T: LobbyRequest,
{
pub(crate) fn new() -> Self {
pub(super) fn new() -> Self {
Self {
_marker: PhantomData,
}
Expand All @@ -27,13 +28,13 @@ where
T: LobbyRequest,
{
fn build(&self, app: &mut App) {
app.add_systems(OnEnter(AppState::InMenu), setup::<T>)
.add_systems(OnExit(AppState::InMenu), cleanup::<T>);
app.add_systems(OnEnter(MenuState::Multiplayer), setup::<T>)
.add_systems(OnExit(MenuState::Multiplayer), cleanup::<T>);
}
}

#[derive(SystemParam)]
pub(crate) struct Sender<'w, T>
pub(super) struct Sender<'w, T>
where
T: LobbyRequest,
{
Expand All @@ -45,14 +46,14 @@ impl<'w, T> Sender<'w, T>
where
T: LobbyRequest,
{
pub(crate) fn send(&mut self, request: T) {
pub(super) fn send(&mut self, request: T) {
self.requests
.send(RequestEvent::new(self.counter.increment(), request));
}
}

#[derive(SystemParam)]
pub(crate) struct Receiver<'w, 's, T>
pub(super) struct Receiver<'w, 's, T>
where
T: LobbyRequest,
{
Expand All @@ -67,7 +68,7 @@ where
/// Returns the response result corresponding the ID of the last request.
/// Responses to earlier requests or requests not made via Sender are
/// ignored.
pub(crate) fn receive(&mut self) -> Option<&Result<T::Response>> {
pub(super) fn receive(&mut self) -> Option<&Result<T::Response>> {
self.responses
.iter()
.filter_map(|e| {
Expand All @@ -82,7 +83,7 @@ where
}

#[derive(Resource)]
pub(crate) struct Counter<T>
pub(super) struct Counter<T>
where
T: LobbyRequest,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ use de_gui::{
use de_lobby_client::{Authentication, LobbyRequest, SignInRequest, SignUpRequest};
use de_lobby_model::{User, UserWithPassword, UsernameAndPassword};

use crate::{
menu::Menu,
use super::{
requests::{Receiver, RequestsPlugin, Sender},
MenuState,
MultiplayerState,
};
use crate::menu::Menu;

pub(crate) struct SignInPlugin;
pub(super) struct SignInPlugin;

impl Plugin for SignInPlugin {
fn build(&self, app: &mut App) {
app.add_plugins((
RequestsPlugin::<SignInRequest>::new(),
RequestsPlugin::<SignUpRequest>::new(),
))
.add_systems(OnEnter(MenuState::SignIn), setup)
.add_systems(OnExit(MenuState::SignIn), cleanup)
.add_systems(OnEnter(MultiplayerState::SignIn), setup)
.add_systems(OnExit(MultiplayerState::SignIn), cleanup)
.add_systems(
Update,
(
Expand All @@ -30,7 +30,7 @@ impl Plugin for SignInPlugin {
response_system::<SignUpRequest>,
auth_system,
)
.run_if(in_state(MenuState::SignIn)),
.run_if(in_state(MultiplayerState::SignIn)),
);
}
}
Expand Down Expand Up @@ -204,8 +204,8 @@ where
}
}

fn auth_system(mut next_state: ResMut<NextState<MenuState>>, auth: Res<Authentication>) {
fn auth_system(mut next_state: ResMut<NextState<MultiplayerState>>, auth: Res<Authentication>) {
if auth.is_authenticated() {
next_state.set(MenuState::GameListing);
next_state.set(MultiplayerState::GameListing);
}
}