From c18bd96ddbac1401cfcc65e80bd9ab3dda221c9b Mon Sep 17 00:00:00 2001 From: Ben Frankel Date: Fri, 26 Jul 2024 22:18:46 -0700 Subject: [PATCH] Make level up menu fully functional --- src/screen/playing/hud.rs | 2 +- src/screen/playing/level_up_menu.rs | 100 +++++++++++++++++++++++----- src/screen/playing/pause_menu.rs | 2 +- src/ui/font.rs | 14 ++-- 4 files changed, 96 insertions(+), 22 deletions(-) diff --git a/src/screen/playing/hud.rs b/src/screen/playing/hud.rs index c0a095a..1b2798a 100644 --- a/src/screen/playing/hud.rs +++ b/src/screen/playing/hud.rs @@ -205,7 +205,7 @@ fn arrow(entity: Entity, world: &mut World) { let config = SystemState::>::new(world).get(world); let config = r!(config.get()); let height = config.card_height / 18.0 * 5.0; - let margin = config.card_height / 18.0 * 2.0; + let margin = config.card_height / 18.0; let texture = world.resource::().arrow.clone(); world.entity_mut(entity).insert(( diff --git a/src/screen/playing/level_up_menu.rs b/src/screen/playing/level_up_menu.rs index dd51759..0d21778 100644 --- a/src/screen/playing/level_up_menu.rs +++ b/src/screen/playing/level_up_menu.rs @@ -19,8 +19,7 @@ use crate::screen::playing::PlayingMenu; use crate::ui::prelude::*; use crate::util::prelude::*; -// TODO: Random card selection to add to deck. -// TODO: Helpful message if the player is at deck capacity. +// TODO: Helpful message if the player is at deck capacity? pub(super) fn plugin(app: &mut App) { app.add_systems( StateFlush, @@ -34,7 +33,7 @@ pub(super) fn plugin(app: &mut App) { .run_if(on_event::()), ); - app.configure::(); + app.configure::<(LevelUpMenuAction, ToggleDisplay)>(); } fn open_level_up_menu(mut commands: Commands, ui_root: Res) { @@ -64,7 +63,7 @@ fn level_up_menu(mut entity: EntityWorldMut) { NodeBundle { style: Style { height: VMin(60.0), - top: Vw(-1.5), + top: Vw(-2.5), align_items: AlignItems::Center, justify_content: JustifyContent::SpaceBetween, flex_direction: FlexDirection::Column, @@ -77,6 +76,7 @@ fn level_up_menu(mut entity: EntityWorldMut) { )) .with_children(|children| { children.spawn_with(header); + children.spawn_with(instructions_container); children.spawn_with(card_options_container); children.spawn_with(button_container); }); @@ -100,6 +100,42 @@ fn header(mut entity: EntityWorldMut) { )); } +fn instructions_container(mut entity: EntityWorldMut) { + entity + .insert(( + Name::new("InstructionsContainer"), + NodeBundle { + style: Style { + display: Display::None, + flex_direction: FlexDirection::Column, + row_gap: Vh(2.3), + ..default() + }, + ..default() + }, + ToggleDisplay, + )) + .with_children(|children| { + for (i, text) in [ + "You can sort your cards during a level up:", + "", + "- [b]Select: [r] A/D or Arrow Keys", + "- [b]Move: [r] Shift + A/D", + "- [b]Discard:[r] Delete", + ] + .into_iter() + .enumerate() + { + children.spawn(( + Name::new(format!("InstructionsParagraph{}", i)), + TextBundle::from_sections(parse_rich(text)), + DynamicFontSize::new(Vw(3.0)).with_step(8.0), + ThemeColorForText(vec![ThemeColor::BodyText]), + )); + } + }); +} + fn card_options_container(entity: Entity, world: &mut World) { let config = SystemState::>::new(world).get(world); let config = r!(config.get()); @@ -126,6 +162,7 @@ fn card_options_container(entity: Entity, world: &mut World) { }, ..default() }, + ToggleDisplay, )) .with_children(|children| { for key in card_keys { @@ -161,12 +198,24 @@ fn card_button(key: impl Into) -> impl EntityCommand { let key = key.into(); move |mut entity: EntityWorldMut| { - entity.add(card(key, None)).insert(( + entity.add(card(key.clone(), None)).insert(( Interaction::default(), - On::>::run(|| { - // TODO: Add card to deck, then run the same logic as the skip button. - println!("Click!"); - }), + On::>::run( + move |deck_display_query: Query<&Selection, With>, + mut deck_query: Query<&mut Deck>, + mut toggle_query: Query<&mut Style, With>| { + for selection in &deck_display_query { + let mut deck = c!(deck_query.get_mut(selection.0)); + deck.add(key.clone()); + } + for mut style in &mut toggle_query { + style.display = match style.display { + Display::None => Display::Flex, + _ => Display::None, + }; + } + }, + ), )); } } @@ -211,15 +260,22 @@ fn button_container(mut entity: EntityWorldMut) { .insert((Name::new("ButtonContainer"), NodeBundle::default())) .with_children(|children| { children.spawn_with(skip_button); - children.spawn_with(ready_button); + children.spawn_with(dance_button); }); } fn skip_button(mut entity: EntityWorldMut) { entity.add(widget::menu_button("Skip")).insert(( - // TODO: Hide this button + the card options container, - // and show the ready button + the instructions container. - On::>::run(PlayingMenu::disable), + On::>::run( + move |mut toggle_query: Query<&mut Style, With>| { + for mut style in &mut toggle_query { + style.display = match style.display { + Display::None => Display::Flex, + _ => Display::None, + }; + } + }, + ), Style { height: Vw(8.0), width: Vw(28.0), @@ -227,11 +283,12 @@ fn skip_button(mut entity: EntityWorldMut) { justify_content: JustifyContent::Center, ..default() }, + ToggleDisplay, )); } -fn ready_button(mut entity: EntityWorldMut) { - entity.add(widget::menu_button("Ready?")).insert(( +fn dance_button(mut entity: EntityWorldMut) { + entity.add(widget::menu_button("Dance~")).insert(( On::>::run(PlayingMenu::disable), Style { display: Display::None, @@ -241,6 +298,7 @@ fn ready_button(mut entity: EntityWorldMut) { justify_content: JustifyContent::Center, ..default() }, + ToggleDisplay, )); } @@ -352,3 +410,15 @@ fn card_discard( deck.discard(); } } + +/// A marker component for entities that should toggle between +/// `Display::None` and `Display::Flexbox` to swap sub-menus. +#[derive(Component, Reflect)] +#[reflect(Component)] +struct ToggleDisplay; + +impl Configure for ToggleDisplay { + fn configure(app: &mut App) { + app.register_type::(); + } +} diff --git a/src/screen/playing/pause_menu.rs b/src/screen/playing/pause_menu.rs index 96a3e31..d6e64fc 100644 --- a/src/screen/playing/pause_menu.rs +++ b/src/screen/playing/pause_menu.rs @@ -77,7 +77,7 @@ fn button_container(mut entity: EntityWorldMut) { style: Style { align_items: AlignItems::Center, flex_direction: FlexDirection::Column, - margin: UiRect::vertical(VMin(7.0)), + margin: UiRect::top(VMin(6.0)), row_gap: Vw(2.5), ..default() }, diff --git a/src/ui/font.rs b/src/ui/font.rs index c962a51..5f22028 100644 --- a/src/ui/font.rs +++ b/src/ui/font.rs @@ -101,7 +101,7 @@ fn apply_dynamic_font_size( } /// Parses a "rich text" string with tags `"[r]"`, `"[b]"`, and `"[t]"`. -pub fn parse_rich(text: &str) -> Text { +pub fn parse_rich(text: &str) -> Vec { let styles = HashMap::from([ ( "r", @@ -136,7 +136,11 @@ pub fn parse_rich(text: &str) -> Text { /// - `"[tag]"` will set the text style to `styles["tag"]` for the following text. /// - If `styles["tag"]` is not found, `"[tag]"` will be interpreted as literal text. /// - Tags cannot be escaped. To allow literal `"[tag]"`, don't use `"tag"` as a key. -pub fn parse_rich_custom(text: &str, styles: &HashMap<&str, TextStyle>, start_tag: &str) -> Text { +pub fn parse_rich_custom( + text: &str, + styles: &HashMap<&str, TextStyle>, + start_tag: &str, +) -> Vec { let mut sections = vec![]; let mut lo = 0; @@ -176,7 +180,7 @@ pub fn parse_rich_custom(text: &str, styles: &HashMap<&str, TextStyle>, start_ta sections.push(section); } - Text::from_sections(sections) + sections } #[cfg(test)] @@ -278,8 +282,8 @@ mod tests { ), ] { let got = parse_rich_custom(case, &styles, "regular"); - assert_eq!(got.sections.len(), want.len()); - for (got, want) in got.sections.iter().zip(&want) { + assert_eq!(got.len(), want.len()); + for (got, want) in got.iter().zip(&want) { assert_eq!(got.value, want.value); assert_eq!(got.style.font, want.style.font); assert_eq!(got.style.font_size, want.style.font_size);