From bfdb44dbcfc51edba3bb402bee3e68e999d85300 Mon Sep 17 00:00:00 2001 From: Louis Capitanchik <contact@louiscap.co> Date: Sat, 10 Dec 2022 19:16:36 +0000 Subject: [PATCH] Implement settings menu --- game_core/src/ui/components/button.rs | 4 +- game_core/src/ui/mod.rs | 1 + .../src/ui/screens/main_menu_settings.rs | 129 ++++++++ game_core/src/ui/screens/mod.rs | 2 + game_core/src/ui/utilities.rs | 8 + game_core/src/ui/widgets/main_menu_panel.rs | 2 +- game_core/src/ui/widgets/mod.rs | 2 + game_core/src/ui/widgets/settings_panel.rs | 278 ++++++++++++++++++ game_core/src/ui/widgets/tips_panel.rs | 58 ++-- 9 files changed, 453 insertions(+), 31 deletions(-) create mode 100644 game_core/src/ui/screens/main_menu_settings.rs create mode 100644 game_core/src/ui/widgets/settings_panel.rs diff --git a/game_core/src/ui/components/button.rs b/game_core/src/ui/components/button.rs index 8d4947e..213d45e 100644 --- a/game_core/src/ui/components/button.rs +++ b/game_core/src/ui/components/button.rs @@ -124,10 +124,10 @@ pub fn render_button_widget( } if should_click { - params.p2().play_sfx("ui_ping"); + params.p2().play_ui_sfx("ui_ping"); } if should_proing { - params.p2().play_sfx("ui_confirm"); + params.p2().play_ui_sfx("ui_confirm"); } (event_dispatcher_context, event) diff --git a/game_core/src/ui/mod.rs b/game_core/src/ui/mod.rs index 9fde625..36f6dc9 100644 --- a/game_core/src/ui/mod.rs +++ b/game_core/src/ui/mod.rs @@ -93,6 +93,7 @@ mod _config { .add_enter_system(AppState::Menu, super::screens::render_menu_ui) .add_enter_system(AppState::InGame, super::screens::render_in_game_ui) .add_enter_system(AppState::Tips, super::screens::render_menu_tips_ui) + .add_enter_system(AppState::Settings, super::screens::render_menu_settings_ui) .add_exit_system(AppState::InGame, remove_ui) .add_exit_system(AppState::Tips, remove_ui) .add_exit_system(AppState::Settings, remove_ui) diff --git a/game_core/src/ui/screens/main_menu_settings.rs b/game_core/src/ui/screens/main_menu_settings.rs new file mode 100644 index 0000000..378f59b --- /dev/null +++ b/game_core/src/ui/screens/main_menu_settings.rs @@ -0,0 +1,129 @@ +use bevy::prelude::*; +use iyes_loopless::prelude::NextState; +use kayak_ui::prelude::*; +use kayak_ui::widgets::{ + ElementBundle, KImage, KImageBundle, KayakAppBundle, TextProps, TextWidgetBundle, +}; + +use crate::assets::AssetHandles; +use crate::persistance::{fs_utils, LoadFileEvent}; +use crate::system::flow::AppState; +use crate::ui::components::*; +use crate::ui::context::*; +use crate::ui::prelude::{pct, px, stretch, value}; +use crate::ui::sync::UITravelInfo; +use crate::ui::utilities::context::create_root_context; +use crate::ui::utilities::StateUIRoot; +use crate::ui::widgets::*; +use crate::world::EncounterState; +use crate::{ + empty_props, on_button_click, parent_widget, register_widget, register_widget_with_context, + register_widget_with_many_resources, register_widget_with_resource, +}; + +empty_props!(MainMenuSettingsProps); +parent_widget!(MainMenuSettingsProps => MainMenuSettingsLayout); + +#[derive(Component, PartialEq, Eq, Clone, Default)] +pub struct MainMenuState { + bg_idx: usize, +} + +pub fn render_main_menu_settings_layout( + In((widget_context, entity)): In<(KayakWidgetContext, Entity)>, + mut commands: Commands, + assets: Res<AssetHandles>, + context_query: Query<&MainMenuContext>, + state_query: Query<&MainMenuState>, +) -> bool { + let parent_id = Some(entity); + let has_autosave = fs_utils::has_auto_save(); + + let state_entity = widget_context.use_state( + &mut commands, + entity, + MainMenuState { + bg_idx: fastrand::usize(1..6), + }, + ); + + if let Ok(state) = state_query.get(state_entity) { + log::info!("Rendering main menu"); + + let root_styles = KStyle { + ..Default::default() + }; + + let image_styles = KStyle { + position_type: value(KPositionType::SelfDirected), + ..Default::default() + }; + + let contents_container_style = KStyle { + position_type: value(KPositionType::SelfDirected), + width: pct(65.0), + height: pct(80.0), + min_width: px(400.0), + min_height: px(300.0), + max_width: px(500.0), + max_height: px(400.0), + top: stretch(1.0), + left: stretch(1.0), + right: stretch(1.0), + bottom: stretch(1.0), + layout_type: value(LayoutType::Column), + row_between: px(20.0), + ..Default::default() + }; + + let header_style = KStyle { + left: stretch(1.0), + right: stretch(1.0), + ..Default::default() + }; + + let image = KImage(assets.image(format!("menu_background_{}", state.bg_idx))); + + let button_style = KStyle { + width: px(300.0), + left: stretch(1.0), + right: stretch(1.0), + ..Default::default() + }; + + rsx! { + <ElementBundle styles={root_styles}> + <KImageBundle styles={image_styles} image={image} /> + <SettingsPanel /> + </ElementBundle> + }; + } + + true +} + +pub fn render_menu_settings_ui(mut commands: Commands) { + let parent_id = None; + let mut widget_context = create_main_menu_context(); + + rsx! { + <KayakAppBundle> + <MainMenuSettingsLayout /> + </KayakAppBundle> + }; + + commands.spawn((UICameraBundle::new(widget_context), StateUIRoot)); +} + +fn create_main_menu_context() -> KayakRootContext { + let mut widget_context = create_root_context(); + + register_widget!( + widget_context, + MainMenuSettingsProps, + MainMenuState, + render_main_menu_settings_layout + ); + + widget_context +} diff --git a/game_core/src/ui/screens/mod.rs b/game_core/src/ui/screens/mod.rs index 520c779..f057158 100644 --- a/game_core/src/ui/screens/mod.rs +++ b/game_core/src/ui/screens/mod.rs @@ -1,7 +1,9 @@ mod in_game; mod main_menu; +mod main_menu_settings; mod main_menu_tips; pub use in_game::render_in_game_ui; pub use main_menu::render_menu_ui; +pub use main_menu_settings::render_menu_settings_ui; pub use main_menu_tips::render_menu_tips_ui; diff --git a/game_core/src/ui/utilities.rs b/game_core/src/ui/utilities.rs index 9cbcae4..62c0237 100644 --- a/game_core/src/ui/utilities.rs +++ b/game_core/src/ui/utilities.rs @@ -226,6 +226,7 @@ pub mod context { use kayak_ui::prelude::{widget_update, EmptyState, KayakRootContext}; use kayak_ui::widgets::KayakWidgetsContextPlugin; use kayak_ui::KayakUIPlugin; + use micro_musicbox::prelude::AudioSettings; use crate::assets::AssetHandles; use crate::register_widget; @@ -361,6 +362,13 @@ pub mod context { EncounterState, render_encounter_panel ); + register_widget_with_resource!( + widget_context, + SettingsPanelProps, + EmptyState, + AudioSettings, + render_settings_panel + ); } } } diff --git a/game_core/src/ui/widgets/main_menu_panel.rs b/game_core/src/ui/widgets/main_menu_panel.rs index 4dba389..7e181fa 100644 --- a/game_core/src/ui/widgets/main_menu_panel.rs +++ b/game_core/src/ui/widgets/main_menu_panel.rs @@ -115,9 +115,9 @@ pub fn render_main_menu_panel( props={ButtonWidgetProps { text: String::from("Settings"), font_size: 32.0, - is_disabled: true, ..Default::default() }} + on_event={on_settings} /> </ElementBundle> }; diff --git a/game_core/src/ui/widgets/mod.rs b/game_core/src/ui/widgets/mod.rs index 058f709..7f9232c 100644 --- a/game_core/src/ui/widgets/mod.rs +++ b/game_core/src/ui/widgets/mod.rs @@ -1,5 +1,6 @@ mod encounter_panel; mod main_menu_panel; +mod settings_panel; mod shop_panel; mod stats_panel; mod tavern_panel; @@ -9,6 +10,7 @@ mod transit_panel; pub use encounter_panel::{render_encounter_panel, EncounterPanel, EncounterPanelProps}; pub use main_menu_panel::{render_main_menu_panel, MainMenuPanel, MainMenuPanelProps}; +pub use settings_panel::{render_settings_panel, SettingsPanel, SettingsPanelProps}; pub use shop_panel::{render_shop_panel, ShopPanel, ShopPanelProps}; pub use stats_panel::{render_stats_panel, StatsPanel, StatsPanelProps}; pub use tavern_panel::{render_tavern_panel, TavernPanel, TavernPanelProps}; diff --git a/game_core/src/ui/widgets/settings_panel.rs b/game_core/src/ui/widgets/settings_panel.rs new file mode 100644 index 0000000..56caf57 --- /dev/null +++ b/game_core/src/ui/widgets/settings_panel.rs @@ -0,0 +1,278 @@ +use bevy::prelude::*; +use iyes_loopless::prelude::NextState; +use kayak_ui::prelude::*; +use kayak_ui::widgets::{ + BackgroundBundle, ElementBundle, KImage, KImageBundle, TextProps, TextWidgetBundle, +}; +use micro_musicbox::prelude::AudioSettings; + +use crate::assets::AssetHandles; +use crate::persistance::{fs_utils, LoadFileEvent}; +use crate::system::flow::AppState; +use crate::ui::components::*; +use crate::ui::context::{MainMenuContext, MainMenuView}; +use crate::ui::prelude::{edge_px, pct, px, stretch, value}; +use crate::ui::widgets::*; +use crate::{basic_widget, empty_props, on_button_click}; + +empty_props!(SettingsPanelProps); +basic_widget!(SettingsPanelProps => SettingsPanel); + +#[derive(Default, Component, Clone, Copy, Eq, PartialEq)] +pub struct SettingsPanelState { + tip_index: usize, +} + +pub fn render_settings_panel( + In((widget_context, entity)): In<(KayakWidgetContext, Entity)>, + mut commands: Commands, + assets: Res<AssetHandles>, + audio: Res<AudioSettings>, +) -> bool { + let parent_id = Some(entity); + + let state_entity = + widget_context.use_state(&mut commands, entity, SettingsPanelState::default()); + + let contents_container_style = KStyle { + position_type: value(KPositionType::SelfDirected), + width: pct(65.0), + height: pct(80.0), + min_width: px(400.0), + min_height: px(300.0), + max_width: px(500.0), + max_height: px(400.0), + top: stretch(1.0), + left: stretch(1.0), + right: stretch(1.0), + bottom: stretch(1.0), + layout_type: value(LayoutType::Column), + row_between: px(20.0), + color: value(Color::BLACK), + ..Default::default() + }; + + let header_style = KStyle { + left: stretch(1.0), + right: stretch(1.0), + bottom: px(30.0), + ..Default::default() + }; + + let tip_style = KStyle { + height: stretch(1.0), + padding_left: px(20.0), + padding_right: px(20.0), + ..Default::default() + }; + + let button_style = KStyle { + ..Default::default() + }; + + let controls_style = KStyle { + top: stretch(1.0), + layout_type: value(LayoutType::Row), + col_between: stretch(1.0), + ..Default::default() + }; + + let row_style = KStyle { + layout_type: value(LayoutType::Row), + col_between: px(10.0), + height: px(60.0), + padding_top: stretch(1.0), + padding_bottom: stretch(1.0), + ..Default::default() + }; + + rsx! { + <ElementBundle styles={contents_container_style}> + <PanelWidget> + <TextWidgetBundle + text={TextProps { + content: String::from("Settings"), + font: Some(String::from("header")), + size: 52.0, + ..Default::default() + }} + styles={header_style} + /> + + <BackgroundBundle + styles={row_style.clone()} + > + <TextWidgetBundle + text={TextProps { + content: String::from("Master Volume"), + size: 32.0, + ..Default::default() + }} + /> + <BackgroundBundle styles={KStyle { width: stretch(1.0), ..Default::default() }} /> + <ButtonWidget + props={ButtonWidgetProps { + text: String::from("-"), + font_size: 24.0, + ..Default::default() + }} + on_event={on_button_click!(ResMut<AudioSettings>, |mut settings: ResMut< + AudioSettings, + >| { + settings.master_volume = (settings.master_volume - 0.05).max(0.0); + })} + /> + <BackgroundBundle + styles={KStyle { + padding_left: stretch(1.0), + padding_right: stretch(1.0), + padding_top: stretch(1.0), + padding_bottom: stretch(1.0), + width: px(100.0), + ..Default::default() + }} + > + <TextWidgetBundle + text={TextProps { + content: format!("{:.0}", audio.master_volume * 100.0), + size: 32.0, + ..Default::default() + }} + /> + </BackgroundBundle> + <ButtonWidget + props={ButtonWidgetProps { + text: String::from("+"), + font_size: 24.0, + ..Default::default() + }} + on_event={on_button_click!(ResMut<AudioSettings>, |mut settings: ResMut< + AudioSettings, + >| { + settings.master_volume = (settings.master_volume + 0.05).min(1.0); + })} + /> + </BackgroundBundle> + <BackgroundBundle + styles={row_style.clone()} + > + <TextWidgetBundle + text={TextProps { + content: String::from("Music Volume"), + size: 32.0, + ..Default::default() + }} + /> + <BackgroundBundle styles={KStyle { width: stretch(1.0), ..Default::default() }} /> + <ButtonWidget + props={ButtonWidgetProps { + text: String::from("-"), + font_size: 24.0, + ..Default::default() + }} + on_event={on_button_click!(ResMut<AudioSettings>, |mut settings: ResMut< + AudioSettings, + >| { + settings.music_volume = (settings.music_volume - 0.05).max(0.0); + })} + /> + <BackgroundBundle + styles={KStyle { + padding_left: stretch(1.0), + padding_right: stretch(1.0), + padding_top: stretch(1.0), + padding_bottom: stretch(1.0), + width: px(100.0), + ..Default::default() + }} + > + <TextWidgetBundle + text={TextProps { + content: format!("{:.0}", audio.music_volume * 100.0), + size: 32.0, + ..Default::default() + }} + /> + </BackgroundBundle> + <ButtonWidget + props={ButtonWidgetProps { + text: String::from("+"), + font_size: 24.0, + ..Default::default() + }} + on_event={on_button_click!(ResMut<AudioSettings>, |mut settings: ResMut< + AudioSettings, + >| { + settings.music_volume = (settings.music_volume + 0.05).min(1.0); + })} + /> + </BackgroundBundle> + + <BackgroundBundle + styles={row_style} + > + <TextWidgetBundle + text={TextProps { + content: String::from("UI Volume"), + size: 32.0, + ..Default::default() + }} + /> + <BackgroundBundle styles={KStyle { width: stretch(1.0), ..Default::default() }} /> + <ButtonWidget + props={ButtonWidgetProps { + text: String::from("-"), + font_size: 24.0, + ..Default::default() + }} + on_event={on_button_click!(ResMut<AudioSettings>, |mut settings: ResMut< + AudioSettings, + >| { + settings.ui_volume = (settings.ui_volume - 0.05).max(0.0); + })} + /> + <BackgroundBundle + styles={KStyle { + padding_left: stretch(1.0), + padding_right: stretch(1.0), + padding_top: stretch(1.0), + padding_bottom: stretch(1.0), + width: px(100.0), + ..Default::default() + }} + > + <TextWidgetBundle + text={TextProps { + content: format!("{:.0}", audio.ui_volume * 100.0), + size: 32.0, + ..Default::default() + }} + /> + </BackgroundBundle> + <ButtonWidget + props={ButtonWidgetProps { + text: String::from("+"), + font_size: 24.0, + ..Default::default() + }} + on_event={on_button_click!(ResMut<AudioSettings>, |mut settings: ResMut< + AudioSettings, + >| { + settings.ui_volume = (settings.ui_volume + 0.05).min(1.0); + })} + /> + </BackgroundBundle> + </PanelWidget> + + <ButtonWidget + props={ButtonWidgetProps { text: String::from("Back"), font_size: 32.0, ..Default::default() }} + on_event={on_button_click!(Commands, |mut commands: Commands| { + commands.insert_resource(NextState(AppState::Menu)); + })} + /> + </ElementBundle> + + }; + + true +} diff --git a/game_core/src/ui/widgets/tips_panel.rs b/game_core/src/ui/widgets/tips_panel.rs index 2095181..3e85e27 100644 --- a/game_core/src/ui/widgets/tips_panel.rs +++ b/game_core/src/ui/widgets/tips_panel.rs @@ -80,7 +80,10 @@ pub fn render_tips_panel( }; let controls_style = KStyle { - top: stretch(1.0), + // top: stretch(1.0), + height: px(60.0), + padding_top: stretch(1.0), + padding_bottom: stretch(1.0), layout_type: value(LayoutType::Row), col_between: stretch(1.0), ..Default::default() @@ -114,36 +117,36 @@ pub fn render_tips_panel( .insert_resource(NextState(AppState::Menu))); rsx! { - <ElementBundle> - <PanelWidget styles={contents_container_style}> - <TextWidgetBundle - text={TextProps { - content: format!("Tip #{}", state.tip_index + 1), - font: Some(String::from("header")), - size: 52.0, - ..Default::default() - }} - styles={header_style} - /> - <BackgroundBundle - styles={KStyle { - height: stretch(1.0), - width: pct(80.0), - left: stretch(1.0), - right: stretch(1.0), - top: px(30.0), - bottom: px(30.0), - ..Default::default() - }} - > + <ElementBundle styles={contents_container_style}> + <PanelWidget> <TextWidgetBundle text={TextProps { - content: tips[state.tip_index].to_string(), - size: 32.0, + content: format!("Tip #{}", state.tip_index + 1), + font: Some(String::from("header")), + size: 52.0, ..Default::default() }} + styles={header_style} /> - </BackgroundBundle> + <BackgroundBundle + styles={KStyle { + width: pct(80.0), + left: stretch(1.0), + right: stretch(1.0), + top: px(30.0), + bottom: px(30.0), + ..Default::default() + }} + > + <TextWidgetBundle + text={TextProps { + content: tips[state.tip_index].to_string(), + size: 32.0, + ..Default::default() + }} + /> + </BackgroundBundle> + </PanelWidget> <BackgroundBundle styles={controls_style}> <ButtonWidget styles={button_style.clone()} @@ -175,8 +178,7 @@ pub fn render_tips_panel( on_event={on_next_tip} /> </BackgroundBundle> - </PanelWidget> - </ElementBundle> + </ElementBundle> }; } -- GitLab