diff --git a/assets/background.png b/assets/background.png new file mode 100644 index 0000000..31cab28 Binary files /dev/null and b/assets/background.png differ diff --git a/src/deck.rs b/src/deck.rs index f4bee9b..a977ca9 100644 --- a/src/deck.rs +++ b/src/deck.rs @@ -55,14 +55,12 @@ impl Deck { let rs = RandomState::new(); let mut base = Self::cards(); let len = base.len(); - (0..2).iter().for_each(|_| { - (1..len).into_iter().for_each(|i| { - let a = rs.hash_one(base[i]) % (len as u64); - let b = rs.hash_one(base[i - 1]) % (len as u64); - if a > b { - base.swap(a as usize, b as usize); - } - }); + (1..len).into_iter().for_each(|i| { + let a = rs.hash_one(base[i]) % (len as u64); + let b = rs.hash_one(base[i - 1]) % (len as u64); + if a > b { + base.swap(a as usize, b as usize); + } }); base } diff --git a/src/main.rs b/src/main.rs index 2966f55..478ca02 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ mod boot; mod debug; mod deck; +mod menu; mod play; mod setup; +mod view; use bevy::prelude::*; @@ -10,10 +12,12 @@ fn main() { App::new() .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_plugins(( + view::ViewPlugin, deck::DeckPlugin, boot::BootPlugin, setup::SetupPlugin, play::PlayPlugin, + menu::MenuPlugin, debug::DebugPlugin, )) .init_state::() @@ -25,5 +29,5 @@ pub(crate) enum GameState { #[default] Boot, Setup, - Play, + Main, } diff --git a/src/menu.rs b/src/menu.rs new file mode 100644 index 0000000..188d2cc --- /dev/null +++ b/src/menu.rs @@ -0,0 +1,25 @@ +use bevy::{color::palettes::css::TEAL, prelude::*}; + +use crate::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); + } +} + +fn setup(mut commands: Commands) { + commands + .spawn((ViewState::Menu, Node::default())) + .with_children(|parent| { + parent + .spawn((Button, BackgroundColor(TEAL.into()))) + .with_children(|parent| { + parent.spawn(Text("Play".to_string())); + }) + .observe(button_set_state(ViewState::Play)); + }); +} diff --git a/src/play.rs b/src/play.rs index f3f25d2..d6d4a2e 100644 --- a/src/play.rs +++ b/src/play.rs @@ -7,7 +7,7 @@ pub struct PlayPlugin; impl Plugin for PlayPlugin { fn build(&self, app: &mut App) { app.add_observer(serve_new_cards) - .add_systems(OnEnter(GameState::Play), serve_cards) + .add_systems(OnEnter(GameState::Main), serve_cards) .add_systems(Update, rotate_cards); } } @@ -64,7 +64,7 @@ pub(crate) fn reset_rotation( /// Check a set when the "Set" button is clicked pub(crate) fn check_set( - _trigger: Trigger>, + trigger: Trigger>, mut query: Query<(Entity, &Card, &mut Visibility, &mut Transform), With>, mut commands: Commands, ) { diff --git a/src/setup.rs b/src/setup.rs index 4c35b12..d85a39f 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -1,5 +1,6 @@ use crate::{deck::*, *}; use bevy::{color::palettes::css::TEAL, prelude::*}; +use view::ViewState; pub struct SetupPlugin; @@ -8,9 +9,10 @@ impl Plugin for SetupPlugin { app.add_systems( OnEnter(GameState::Setup), ( + setup_background, setup_cards, - setup_camera, setup_set_check_button, + setup_camera, start_play, ) .chain(), @@ -20,26 +22,30 @@ impl Plugin for SetupPlugin { /// Setup drawing our cards on the screen pub(crate) fn setup_cards(mut commands: Commands, deck: Res) { - Deck::iter_shuffled() - .enumerate() - .for_each(|(i, this_card)| { - let this = deck - .cards - .get(&this_card) - .unwrap_or_else(|| panic!("fech card sprite {:?}", this_card)) - .clone(); - let this_sprite = Sprite { - custom_size: Some(Vec2::new(80.0, 128.0)), - ..this - }; - let order = play::DeckOrder(i as u8); - commands - .spawn((this_sprite, this_card, order, Visibility::Hidden)) - .observe(play::place_card) - .observe(debug::set_debug_card) - .observe(debug::hide_debug_card) - .observe(play::reset_rotation) - .observe(play::toggle_selected); + commands + .spawn((Transform::default(), Visibility::default(), ViewState::Play)) + .with_children(|parent| { + Deck::iter_shuffled() + .enumerate() + .for_each(|(i, this_card)| { + let this = deck + .cards + .get(&this_card) + .unwrap_or_else(|| panic!("fech card sprite {:?}", this_card)) + .clone(); + let this_sprite = Sprite { + custom_size: Some(Vec2::new(80.0, 128.0)), + ..this + }; + let order = play::DeckOrder(i as u8); + parent + .spawn((this_sprite, this_card, order, Visibility::Hidden)) + .observe(play::place_card) + .observe(debug::set_debug_card) + .observe(debug::hide_debug_card) + .observe(play::reset_rotation) + .observe(play::toggle_selected); + }); }); } @@ -54,8 +60,20 @@ pub(crate) fn setup_camera(mut commands: Commands) { )); } -#[derive(Component)] -pub(crate) struct CheckSet; +pub(crate) fn setup_background( + mut commands: Commands, + server: Res, + window: Query<&Window>, +) { + commands.spawn(( + Transform::default().with_translation(Vec3::new(0.0, 0.0, -1.0)), + Sprite { + image: server.load("background.png"), + custom_size: Some(window.single().resolution.size() * 1.1), + ..default() + }, + )); +} pub(crate) fn setup_set_check_button(mut commands: Commands) { commands @@ -66,11 +84,12 @@ pub(crate) fn setup_set_check_button(mut commands: Commands) { align_items: AlignItems::Center, ..default() }, + ViewState::Play, BackgroundColor(Color::BLACK.with_alpha(0.8).into()), )) .with_children(|parent| { parent - .spawn((Button, CheckSet, BackgroundColor(TEAL.into()))) + .spawn((Button, BackgroundColor(TEAL.into()))) .with_children(|parent| { parent.spawn(Text("Set!?".to_string())); }) @@ -79,6 +98,11 @@ pub(crate) fn setup_set_check_button(mut commands: Commands) { } /// Finish the setup state by progressing to the play state -pub(crate) fn start_play(mut game_state: ResMut>) { - game_state.set(GameState::Play); +pub(crate) fn start_play( + mut game_state: ResMut>, + mut view_state: ResMut>, +) { + info!("starting play"); + game_state.set(GameState::Main); + view_state.set(ViewState::Menu); } diff --git a/src/view.rs b/src/view.rs new file mode 100644 index 0000000..3cbc88d --- /dev/null +++ b/src/view.rs @@ -0,0 +1,52 @@ +use bevy::{prelude::*, state::state::FreelyMutableState}; + +/// Deck and Cards +pub struct ViewPlugin; + +impl Plugin for ViewPlugin { + fn build(&self, app: &mut App) { + app.add_observer(state_monitor).init_state::(); + } +} + +/// +/// State component for which view an entity is associated with +/// +#[derive(States, Component, Default, Clone, Eq, PartialEq, Hash, Debug)] +pub(crate) enum ViewState { + #[default] + None, + Menu, + Play, + HowToPlay, + About, + Sets, + Deck, +} + +pub(crate) fn button_set_state( + s: S, +) -> impl Fn(Trigger>, ResMut>) { + move |_trigger: Trigger>, mut next: ResMut>| next.set(s.clone()) +} + +fn state_monitor( + trigger: Trigger>, + mut entities: Query<(&ViewState, &mut Visibility)>, +) { + entities + .iter_mut() + .for_each(|(view_state, mut visibility)| { + info!( + "Transition from {:?} to {:?}", + trigger.event().exited, + trigger.event().entered + ); + if Some(view_state) == trigger.event().exited.as_ref() { + *visibility = Visibility::Hidden; + } + if Some(view_state) == trigger.event().entered.as_ref() { + *visibility = Visibility::Inherited; + } + }); +} diff --git a/todo.txt b/todo.txt index 19d805e..8b9361d 100644 --- a/todo.txt +++ b/todo.txt @@ -7,3 +7,7 @@ TODO: * Background! * Print "no set"/"too many cards" messages * Add "quit game" button +* View all cards with some indication of in-set +* Menu +* About/Credits +* How To Play