use bevy::{ color::palettes::css::{BLACK, WHITE}, prelude::*, }; use crate::{ boot, deck::{Card, Deck, ItemColor, ItemNumber, ItemPattern, ItemShape}, play::{check_for_sets, check_set}, view::{button_set_state, ViewState}, }; /// Game Menu pub struct MenuPlugin; impl Plugin for MenuPlugin { fn build(&self, app: &mut App) { app.add_systems( Startup, ( setup, setup_about, setup_how_to_play.after(boot::load), setup_deck, setup_sets, setup_set_check_button, ), ); } } fn button_builder(node: Node) -> (Button, BackgroundColor, Node, BorderColor) { ( Button, BackgroundColor(BLACK.with_alpha(0.9).into()), Node { padding: UiRect::all(Val::Px(10.)), margin: UiRect::top(Val::Px(25.)), border: UiRect::all(Val::Px(1.0)), ..node }, BorderColor(WHITE.into()), ) } fn setup(mut commands: Commands) { commands .spawn(( ViewState::Menu, Node { position_type: PositionType::Absolute, justify_content: JustifyContent::Center, align_items: AlignItems::Center, width: Val::Percent(100.0), height: Val::Percent(100.0), ..default() }, )) .with_children(|parent| { parent .spawn(Node { flex_direction: FlexDirection::Column, align_items: AlignItems::Center, justify_content: JustifyContent::Center, ..default() }) .with_children(|parent| { parent .spawn(button_builder(Node::default())) .with_children(|parent| { parent.spawn(Text("Play".to_string())); }) .observe(button_hover_on) .observe(button_hover_off) .observe(button_set_state(ViewState::Play)); parent .spawn(button_builder(Node::default())) .with_children(|parent| { parent.spawn(Text("About".to_string())); }) .observe(button_hover_on) .observe(button_hover_off) .observe(button_set_state(ViewState::About)); parent .spawn(button_builder(Node::default())) .with_children(|parent| { parent.spawn(Text("How to Play".to_string())); }) .observe(button_hover_on) .observe(button_hover_off) .observe(button_set_state(ViewState::HowToPlay)); #[cfg(not(target_arch = "wasm32"))] parent .spawn(button_builder(Node::default())) .with_children(|parent| { parent.spawn(Text("Quit".to_string())); }) .observe(button_hover_on) .observe(button_hover_off) .observe(quit_button); }); }); commands .spawn((ViewState::Play, Node::default())) .with_children(|parent| { parent .spawn((button_builder(Node::default()), GlobalZIndex(1))) .with_children(|parent| { parent.spawn(Text("Menu".to_string())); }) .observe(button_hover_on) .observe(button_hover_off) .observe(button_set_state(ViewState::Menu)); /* parent .spawn((button_builder(Node::default()), GlobalZIndex(1))) .with_children(|parent| { parent.spawn(Text("Deck".to_string())); }) .observe(button_hover_on) .observe(button_hover_off) .observe(button_set_state(ViewState::Deck)); */ /* parent .spawn((button_builder(Node::default()), GlobalZIndex(1))) .with_children(|parent| { parent.spawn(Text("Sets".to_string())); }) .observe(button_hover_on) .observe(button_hover_off) .observe(button_set_state(ViewState::Sets)); */ parent .spawn((button_builder(Node::default()), GlobalZIndex(1))) .with_children(|parent| { parent.spawn(Text("Help!".to_string())); }) .observe(button_hover_on) .observe(button_hover_off) .observe(check_for_sets); }); } fn setup_about(mut commands: Commands) { commands .spawn((ViewState::About, Node::default())) .with_children(|parent| { parent .spawn(button_builder(Node::default())) .with_children(|parent| { parent.spawn(Text("Menu".to_string())); }) .observe(button_set_state(ViewState::Menu)); }); } fn setup_how_to_play(mut commands: Commands, deck: Res) { commands .spawn(( ViewState::HowToPlay, Node { position_type: PositionType::Absolute, width: Val::Percent(100.0), height: Val::Percent(100.0), align_items: AlignItems::Center, justify_content: JustifyContent::Center, flex_direction: FlexDirection::Column, ..default() }, )) .with_children(|parent| { parent .spawn(button_builder(Node { position_type: PositionType::Absolute, top: Val::Px(0.0), left: Val::Px(0.0), ..default() })) .with_children(|parent| { parent.spawn(Text("Menu".to_string())); }) .observe(button_set_state(ViewState::Menu)); parent.spawn((Node { width: Val::Px(500.0), align_items: AlignItems::Center, justify_content: JustifyContent::Center, flex_direction: FlexDirection::Column, ..default() }, BackgroundColor(BLACK.with_alpha(0.9).into()), )).with_children(|parent| { parent.spawn((Text("How to Play".into()), TextFont::default().with_font_size(30.0))); let most_matching = ( Card { color: ItemColor::Red, shape: ItemShape::Squiggle, number: ItemNumber::One, pattern: ItemPattern::Solid, }, Card { color: ItemColor::Red, shape: ItemShape::Squiggle, number: ItemNumber::Two, pattern: ItemPattern::Solid, }, Card { color: ItemColor::Red, shape: ItemShape::Squiggle, number: ItemNumber::Three, pattern: ItemPattern::Solid, } ); let most_not_matching = ( Card { color: ItemColor::Green, shape: ItemShape::Oval, number: ItemNumber::Three, pattern: ItemPattern::Solid, }, Card { color: ItemColor::Purple, shape: ItemShape::Squiggle, number: ItemNumber::Three, pattern: ItemPattern::Striped, }, Card { color: ItemColor::Red, shape: ItemShape::Diamond, number: ItemNumber::Three, pattern: ItemPattern::Open, } ); let not_valid = ( Card { color: ItemColor::Purple, shape: ItemShape::Oval, number: ItemNumber::Three, pattern: ItemPattern::Striped, }, Card { color: ItemColor::Purple, shape: ItemShape::Oval, number: ItemNumber::Two, pattern: ItemPattern::Striped, }, Card { color: ItemColor::Purple, shape: ItemShape::Oval, number: ItemNumber::Three, pattern: ItemPattern::Open, } ); [ ("Find groups of 3 cards", None), ("All cards in a set must either match or differ in their traits: Color, Number, Shading, and Shape.", None), ("For example all agree on Color, Shading, and Shape but not Number...", Some(most_matching)), ("...or all agree on Number but not Color, Shading, and Shape...", Some(most_not_matching)), ("...but never partial agreement...", Some(not_valid)), ("When in doubt, click the 'Help!' button to get an assist.", None), ] .iter() .for_each(|(t, example)| { parent.spawn(( Node { max_width: Val::Percent(100.0), flex_direction: FlexDirection::Row, align_items: AlignItems::Center, ..default() }, )).with_children(|parent| { parent.spawn(( Node { margin: UiRect::all(Val::Px(5.0)), padding: UiRect::all(Val::Px(5.0)), ..default() }, Text(t.to_string()), )); parent.spawn(Node { ..default() }).with_children(|parent| { if let Some((a, b, c)) = example { [a, b, c].iter().for_each(|x| { let sprite = deck.cards.get(*x).unwrap(); parent.spawn(( Node { height: Val::Px(100.0), ..default() }, ImageNode { image: sprite.image.clone(), texture_atlas: sprite.texture_atlas.clone(), ..default() } )); }); }; }); }); }); }); }); } fn setup_deck(mut commands: Commands) { commands .spawn((ViewState::Deck, Node::default())) .with_children(|parent| { parent .spawn(button_builder(Node::default())) .with_children(|parent| { parent.spawn(Text("Back".to_string())); }) .observe(button_set_state(ViewState::Play)); }); } fn setup_sets(mut commands: Commands) { commands .spawn((ViewState::Sets, Node::default())) .with_children(|parent| { parent .spawn(button_builder(Node::default())) .with_children(|parent| { parent.spawn(Text("Back".to_string())); }) .observe(button_set_state(ViewState::Play)); }); } pub(crate) fn setup_set_check_button(mut commands: Commands) { commands .spawn(( Node { width: Val::Percent(100.0), position_type: PositionType::Absolute, top: Val::Px(0.0), flex_direction: FlexDirection::Column, align_items: AlignItems::Center, ..default() }, Visibility::default(), ViewState::Play, )) .with_children(|parent| { parent .spawn(button_builder(Node::default())) .with_children(|parent| { parent.spawn(Text("Set!".to_string())); }) .observe(check_set); }); } fn quit_button(_trigger: Trigger>, mut exit_event: EventWriter) { exit_event.send(AppExit::Success); } fn button_hover_on(trigger: Trigger>, mut query: Query<&mut BackgroundColor>) { let mut background_color = query.get_mut(trigger.entity()).unwrap(); background_color.0.set_alpha(1.0); } fn button_hover_off(trigger: Trigger>, mut query: Query<&mut BackgroundColor>) { let mut background_color = query.get_mut(trigger.entity()).unwrap(); background_color.0.set_alpha(0.9); }