diff --git a/Cargo.lock b/Cargo.lock index 69f109ead6d22e49e5fac115e68fd4f0139060c4..72e7089cb8c6d625f2ede39bacc35cd6346979cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2080,7 +2080,7 @@ dependencies = [ [[package]] name = "kayak_font" version = "0.1.0" -source = "git+https://github.com/StarArawn/kayak_ui.git?rev=220694d12a5aeffe680fdaf2a8e5ac3ed9d81ae7#220694d12a5aeffe680fdaf2a8e5ac3ed9d81ae7" +source = "git+https://github.com/StarArawn/kayak_ui.git?rev=a85cab76839f22e5f5a1002b864bad3923e0f375#a85cab76839f22e5f5a1002b864bad3923e0f375" dependencies = [ "anyhow", "bevy", @@ -2097,7 +2097,7 @@ dependencies = [ [[package]] name = "kayak_ui" version = "0.1.0" -source = "git+https://github.com/StarArawn/kayak_ui.git?rev=220694d12a5aeffe680fdaf2a8e5ac3ed9d81ae7#220694d12a5aeffe680fdaf2a8e5ac3ed9d81ae7" +source = "git+https://github.com/StarArawn/kayak_ui.git?rev=a85cab76839f22e5f5a1002b864bad3923e0f375#a85cab76839f22e5f5a1002b864bad3923e0f375" dependencies = [ "bevy", "bitflags", @@ -2115,7 +2115,7 @@ dependencies = [ [[package]] name = "kayak_ui_macros" version = "0.1.0" -source = "git+https://github.com/StarArawn/kayak_ui.git?rev=220694d12a5aeffe680fdaf2a8e5ac3ed9d81ae7#220694d12a5aeffe680fdaf2a8e5ac3ed9d81ae7" +source = "git+https://github.com/StarArawn/kayak_ui.git?rev=a85cab76839f22e5f5a1002b864bad3923e0f375#a85cab76839f22e5f5a1002b864bad3923e0f375" dependencies = [ "find-crate", "proc-macro-crate", diff --git a/Cargo.toml b/Cargo.toml index 2828020c059ba3467a22039785e3b9086e86972c..5c5bbe743e9d41985d6440325d04de0118476a6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,8 +19,8 @@ iyes_loopless = "0.9.1" micro_musicbox = { version = "0.5.0", features = ["mp3"] } micro_banimate = { version = "0.2.1", features = ["ecs_tilemap"] } -kayak_ui = { rev = "220694d12a5aeffe680fdaf2a8e5ac3ed9d81ae7", git = "https://github.com/StarArawn/kayak_ui" } -kayak_font = { rev = "220694d12a5aeffe680fdaf2a8e5ac3ed9d81ae7", git = "https://github.com/StarArawn/kayak_ui.git" } +kayak_ui = { rev = "a85cab76839f22e5f5a1002b864bad3923e0f375", git = "https://github.com/StarArawn/kayak_ui" } +kayak_font = { rev = "a85cab76839f22e5f5a1002b864bad3923e0f375", git = "https://github.com/StarArawn/kayak_ui.git" } [workspace.dependencies.bevy] version = "0.9.0" diff --git a/assets/resources.apack b/assets/resources.apack index c99c8b44b46485fc671c03ded4875daa3ddd020c..bfc8a3e5a34ea718c989465f9af5cf66f3b07eef 100644 --- a/assets/resources.apack +++ b/assets/resources.apack @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:edc6d705e89fdbbb006ec4b772f1299fdb5e7a6be207261d4dee8d070e6b402c -size 1578160 +oid sha256:a1edb177ccd8eef0f5b3cdcb4ea5e2804c88a62bf4e5cee12f98a7bfbc1b5261 +size 1579824 diff --git a/game_core/src/persistance/save_file.rs b/game_core/src/persistance/save_file.rs index c0d746610ce150a59dfcf271d88f1f9f6e57125f..a4096406ebe6278a94bfb70166606f1d2c9ae979 100644 --- a/game_core/src/persistance/save_file.rs +++ b/game_core/src/persistance/save_file.rs @@ -14,8 +14,8 @@ use crate::persistance::fs_utils::{get_root_save_dir, AUTOSAVE_NAME}; use crate::states::Player; use crate::system::flow::AppState; use crate::world::{ - CurrentResidence, EncounterState, HungerState, PendingLoadState, TradingState, TravelPath, - TravelTarget, + CurrentResidence, DistanceTravelled, EncounterState, HungerState, PendingLoadState, + TradingState, TravelPath, TravelTarget, }; #[derive(Serialize, Deserialize, Debug, Resource)] @@ -23,8 +23,12 @@ pub struct PersistenceState { pub player_location: Vec3, pub player_inventory: TradingState, pub player_hunger: HungerState, + #[serde(default)] pub travel_path: Option<TravelPath>, + #[serde(default)] pub travel_target: Option<TravelTarget>, + #[serde(default)] + pub player_total_distance: DistanceTravelled, pub previous_location: CurrentResidence, pub encounter_state: EncounterState, pub town_states: HashMap<String, (TradingState, HungerState)>, @@ -52,31 +56,44 @@ impl LoadFileEvent { } } +type PlayerInfoQuery<'a, 'b> = Query< + 'a, + 'b, + ( + &'static Transform, + Option<&'static TravelPath>, + Option<&'static TravelTarget>, + &'static CurrentResidence, + &'static TradingState, + &'static HungerState, + &'static DistanceTravelled, + ), + With<Player>, +>; + pub fn sync_state_to_persistence( mut commands: Commands, mut state: Option<ResMut<PersistenceState>>, - player_query: Query< - ( - &Transform, - Option<&TravelPath>, - Option<&TravelTarget>, - &CurrentResidence, - ), - With<Player>, - >, + player_query: PlayerInfoQuery, encounters: Res<EncounterState>, - hunger: Option<Res<HungerState>>, - trading: Option<Res<TradingState>>, ) { match state { Some(mut state) => { - if let Some((transform, maybe_travel, maybe_target, residence)) = - player_query.iter().next() + if let Some(( + transform, + maybe_travel, + maybe_target, + residence, + trading, + hunger, + distance, + )) = player_query.iter().next() { *state = PersistenceState { player_location: transform.translation, - player_inventory: trading.map(|r| r.clone()).unwrap_or_default(), - player_hunger: hunger.map(|r| r.clone()).unwrap_or_default(), + player_inventory: trading.clone(), + player_hunger: hunger.clone(), + player_total_distance: *distance, travel_path: maybe_travel.cloned(), travel_target: maybe_target.cloned(), previous_location: residence.clone(), @@ -86,13 +103,21 @@ pub fn sync_state_to_persistence( } } None => { - if let Some((transform, maybe_travel, maybe_target, residence)) = - player_query.iter().next() + if let Some(( + transform, + maybe_travel, + maybe_target, + residence, + trading, + hunger, + distance, + )) = player_query.iter().next() { commands.insert_resource(PersistenceState { player_location: transform.translation, - player_inventory: trading.map(|r| r.clone()).unwrap_or_default(), - player_hunger: hunger.map(|r| r.clone()).unwrap_or_default(), + player_inventory: trading.clone(), + player_hunger: hunger.clone(), + player_total_distance: *distance, travel_path: maybe_travel.cloned(), travel_target: maybe_target.cloned(), previous_location: residence.clone(), @@ -136,9 +161,7 @@ pub fn handle_save_event( pub fn handle_load_event(mut commands: Commands, mut events: ResMut<Events<LoadFileEvent>>) { let root_dir = get_root_save_dir().expect("Could not find root dir for saves"); for event in events.drain() { - log::info!("Trying to load file"); let path = root_dir.join(event.filename.as_deref().unwrap_or(AUTOSAVE_NAME)); - log::info!("Path is {}", path.display()); match std::fs::File::open(path) { Ok(file) => match serde_json::from_reader::<File, PersistenceState>(file) { Ok(value) => { diff --git a/game_core/src/ui/components/a_text_box.rs b/game_core/src/ui/components/a_text_box.rs index 8cab85aa44bfc210c1d5b4e32aceef28fc020c4c..a418532bfeda5a58408f162b372c3488136f44ff 100644 --- a/game_core/src/ui/components/a_text_box.rs +++ b/game_core/src/ui/components/a_text_box.rs @@ -411,7 +411,7 @@ pub fn render_text_box_widget( </ElementBundle> </ClipBundle> </BackgroundBundle> - } + }; } } diff --git a/game_core/src/ui/components/button.rs b/game_core/src/ui/components/button.rs index ba2adbb0ef66ecc2252344cccbdb47f1ffb995b9..09d0882833a041d2f496a59ed900c2e0e5bffc45 100644 --- a/game_core/src/ui/components/button.rs +++ b/game_core/src/ui/components/button.rs @@ -287,7 +287,7 @@ pub fn render_button_widget( ); }} </NinePatchBundle> - } + }; } } true diff --git a/game_core/src/ui/components/debug_info.rs b/game_core/src/ui/components/debug_info.rs index 8e5e9010f66fdf69327485c5aa6bca25e9fd244c..e546da0da399d05fb4f5cc13a1d42ab756daf7da 100644 --- a/game_core/src/ui/components/debug_info.rs +++ b/game_core/src/ui/components/debug_info.rs @@ -31,40 +31,7 @@ pub fn render_debug_info( <ElementBundle styles={element_style}> <TextWidgetBundle text={simple_text(VERSION, "mono", 32.0)} /> </ElementBundle> - } + }; true } - -// #[widget] -// pub fn DebugInfo() { -// // let f = WidgetN -// let container_style = Style { -// position_type: PositionType::SelfDirected.into(), -// bottom: px(20.0), -// right: px(20.0), -// left: stretch(1.0), -// top: stretch(1.0), -// width: px(120.0), -// height: Units::Auto.into(), -// ..Default::default() -// }; -// let text_style = Style { -// color: Color::WHITE.into(), -// font_size: (24.0).into(), -// left: stretch(1.0), -// font: context -// .get_font_id("mono") -// .map(StyleProp::Value) -// .unwrap_or(StyleProp::Unset), -// ..Default::default() -// }; -// -// let val = env!("CARGO_PKG_VERSION"); -// -// rsx! { -// <Element styles={Some(container_style)}> -// <Text styles={Some(text_style)} content={format!("v{}", val)} /> -// </Element> -// } -// } diff --git a/game_core/src/ui/components/h_divider.rs b/game_core/src/ui/components/h_divider.rs new file mode 100644 index 0000000000000000000000000000000000000000..f2b75d9cadedba47b70343c16e4800eb4ee29b89 --- /dev/null +++ b/game_core/src/ui/components/h_divider.rs @@ -0,0 +1,49 @@ +use bevy::prelude::*; +use kayak_ui::prelude::{ + ComputedStyles, KStyle, KayakWidgetContext, RenderCommand, StyleProp, Widget, +}; + +use crate::basic_widget; +use crate::ui::prelude::{px, stretch, value}; + +#[derive(Component, Debug, Clone, PartialEq)] +pub struct HDividerWidgetProps { + pub width: f32, + pub padding: f32, + pub color: Color, +} + +impl Default for HDividerWidgetProps { + fn default() -> Self { + Self { + width: 1.0, + padding: 5.0, + color: Color::ANTIQUE_WHITE, + } + } +} + +impl Widget for HDividerWidgetProps {} + +basic_widget!(HDividerWidgetProps => HDividerWidget); + +pub fn render_h_divider( + In((_, entity)): In<(KayakWidgetContext, Entity)>, + _: Commands, + mut query: Query<(&HDividerWidgetProps, &KStyle, &mut ComputedStyles)>, +) -> bool { + if let Ok((props, style, mut computed)) = query.get_mut(entity) { + *computed = KStyle { + render_command: value(RenderCommand::Quad), + background_color: StyleProp::Value(props.color), + width: px(props.width), + left: px(props.padding), + right: px(props.padding), + height: stretch(1.0), + ..Default::default() + } + .with_style(style) + .into(); + } + true +} diff --git a/game_core/src/ui/components/image_button.rs b/game_core/src/ui/components/image_button.rs index da5eb6dd1974656655f3dc0aad42fa60d7240fe2..fc6626ade205768958ca6b676b21b544b273853c 100644 --- a/game_core/src/ui/components/image_button.rs +++ b/game_core/src/ui/components/image_button.rs @@ -245,7 +245,7 @@ pub fn render_image_button_widget( ImageButtonContent::None => {} } } </NinePatchBundle> - } + }; } } true diff --git a/game_core/src/ui/components/inset_icon.rs b/game_core/src/ui/components/inset_icon.rs index 89814c20538f55b165fd44f2508194ffad1924fc..e05e25dbdb4d33edc7297a4563dfe351a4c52121 100644 --- a/game_core/src/ui/components/inset_icon.rs +++ b/game_core/src/ui/components/inset_icon.rs @@ -80,7 +80,7 @@ pub fn render_inset_icon_widget( image={KImage(handle)} styles={image_styles} /> - } + }; } IconContent::Atlas(name, index) => { let atlas_handle = assets.atlas(name); @@ -99,7 +99,7 @@ pub fn render_inset_icon_widget( }} styles={image_styles} /> - } + }; } } IconContent::None => {} diff --git a/game_core/src/ui/components/mod.rs b/game_core/src/ui/components/mod.rs index 7a1147c971b6958abb76740e606f7758a1f9deea..31e028e23d6b76b040b20da7807e93fcf4994265 100644 --- a/game_core/src/ui/components/mod.rs +++ b/game_core/src/ui/components/mod.rs @@ -3,6 +3,7 @@ mod a_text_box; mod button; mod debug_info; +mod h_divider; mod image_button; mod inset_icon; mod panel; @@ -15,6 +16,7 @@ pub use self::button::{ button_props, render_button_widget, ButtonWidget, ButtonWidgetProps, ButtonWidgetState, }; pub use self::debug_info::{render_debug_info, DebugInfoProps, DebugInfoWidget}; +pub use self::h_divider::{render_h_divider, HDividerWidget, HDividerWidgetProps}; pub use self::image_button::{ render_image_button_widget, ImageButtonWidget, ImageButtonWidgetProps, ImageButtonWidgetState, }; diff --git a/game_core/src/ui/components/panel.rs b/game_core/src/ui/components/panel.rs index 45eca813056217e17bdb120711ab3046e25b3177..ac1f19ec517c36d92e4d9e6550a58b1b44f40229 100644 --- a/game_core/src/ui/components/panel.rs +++ b/game_core/src/ui/components/panel.rs @@ -36,7 +36,6 @@ pub fn render_panel_widget( }; *computed = KStyle { - render_command: value(RenderCommand::Layout), min_height: px(edge_size * 2.0 + 8.0), min_width: px(edge_size * 2.0 + 8.0), padding: value(Edge::all(Units::Stretch(0.0))), @@ -65,61 +64,7 @@ pub fn render_panel_widget( styles={inner_style} children={children.clone()} /> - } + }; } true } - -// #[widget] -// pub fn Panel(props: PanelProps) { -// let styles = Some( -// Style::default() -// .with_style(Style { -// min_height: Units::Pixels(72.0).into(), -// min_width: Units::Pixels(72.0).into(), -// ..Default::default() -// }) -// .with_style(props.get_styles()) -// .with_style(Style { -// color: Color::WHITE.into(), -// padding: StyleProp::Value(Edge::all(Units::Pixels(32.0))), -// ..Default::default() -// }), -// ); -// -// let children = props.get_children(); -// let patch = context.get_ninepatch("panel"); -// -// rsx! { -// <NinePatch border={Edge::all(4.0 * 8.0)} handle={patch} styles={styles}> -// { children } -// </NinePatch> -// } -// } -// -// #[widget] -// pub fn BasicPanel(props: PanelProps) { -// let styles = Some( -// Style::default() -// .with_style(Style { -// min_height: Units::Pixels(28.0).into(), -// min_width: Units::Pixels(28.0).into(), -// ..Default::default() -// }) -// .with_style(props.get_styles()) -// .with_style(Style { -// color: Color::WHITE.into(), -// padding: StyleProp::Value(Edge::all(Units::Pixels(12.0))), -// ..Default::default() -// }), -// ); -// -// let children = props.get_children(); -// let patch = context.get_ninepatch("basic_panel"); -// -// rsx! { -// <NinePatch border={Edge::all(12.0)} handle={patch} styles={styles} on_event={props.get_on_event()} on_layout={props.get_on_layout()}> -// { children } -// </NinePatch> -// } -// } diff --git a/game_core/src/ui/screens/in_game.rs b/game_core/src/ui/screens/in_game.rs index 1ee476ddc6969d73ce8e595e235adfec1b13f7c4..e152050dd705d1e45b3624f6389a40351a81bed3 100644 --- a/game_core/src/ui/screens/in_game.rs +++ b/game_core/src/ui/screens/in_game.rs @@ -75,8 +75,11 @@ pub fn render_game_panels( } }} + { if show_distance_panel { + constructor! { <StatsPanel /> } + }} </ElementBundle> - } + }; true } @@ -89,7 +92,7 @@ pub fn render_in_game_ui(mut commands: Commands) { <KayakAppBundle> <InGameLayout /> </KayakAppBundle> - } + }; commands.spawn((UICameraBundle::new(widget_context), StateUIRoot)); } diff --git a/game_core/src/ui/screens/main_menu.rs b/game_core/src/ui/screens/main_menu.rs index ed2f0c3cfd6f7d3806b0adf01ec340ca28443c84..d5b0a69b07dfb2cc7df7b834788402bf8ab9ee98 100644 --- a/game_core/src/ui/screens/main_menu.rs +++ b/game_core/src/ui/screens/main_menu.rs @@ -119,7 +119,7 @@ pub fn render_main_menu_layout( /> </ElementBundle> </ElementBundle> - } + }; true } @@ -132,7 +132,7 @@ pub fn render_menu_ui(mut commands: Commands) { <KayakAppBundle> <MainMenuLayout /> </KayakAppBundle> - } + }; commands.spawn((UICameraBundle::new(widget_context), StateUIRoot)); } diff --git a/game_core/src/ui/sync/mod.rs b/game_core/src/ui/sync/mod.rs index d6358a72b17dccf006a1c82ee9659129e3a7daa1..0dc64da5189b7143b89b17bbb151f091c1920d1c 100644 --- a/game_core/src/ui/sync/mod.rs +++ b/game_core/src/ui/sync/mod.rs @@ -3,24 +3,23 @@ use iyes_loopless::prelude::ConditionSet; use crate::system::flow::AppState; +mod sync_stats; mod sync_travel; pub struct UISyncPlugin; impl Plugin for UISyncPlugin { fn build(&self, app: &mut App) { - app.insert_resource(sync_travel::UITravelInfo { - travel_options: Vec::new(), - is_in_town: false, - current_town: None, - distance_remaining: 0.0, - }) - .add_system_set( - ConditionSet::new() - .run_in_state(AppState::InGame) - .with_system(sync_travel::sync_player_travel_info_to_ui) - .into(), - ); + app.init_resource::<UITravelInfo>() + .init_resource::<UIStatsData>() + .add_system_set( + ConditionSet::new() + .run_in_state(AppState::InGame) + .with_system(sync_travel::sync_player_travel_info_to_ui) + .with_system(sync_stats::sync_player_stats_info_to_ui) + .into(), + ); } } +pub use sync_stats::UIStatsData; pub use sync_travel::UITravelInfo; diff --git a/game_core/src/ui/sync/sync_stats.rs b/game_core/src/ui/sync/sync_stats.rs new file mode 100644 index 0000000000000000000000000000000000000000..c36f2a735fb1d41995f5a77a0ff6702c2cc92de2 --- /dev/null +++ b/game_core/src/ui/sync/sync_stats.rs @@ -0,0 +1,30 @@ +use bevy::prelude::*; + +use crate::states::Player; +use crate::world::{DistanceTravelled, HungerState, TradingState}; + +#[derive(Resource, Debug, Default, Copy, Clone)] +pub struct UIStatsData { + pub money: usize, + pub distance_travelled: f32, + pub sustenance: usize, +} + +pub fn sync_player_stats_info_to_ui( + player_query: Query<(&DistanceTravelled, &TradingState, &HungerState), With<Player>>, + mut ui_info: ResMut<UIStatsData>, +) { + if let Ok((distance, trading, hunger)) = player_query.get_single() { + if ui_info.money != trading.gold.max(0) as usize { + ui_info.money = trading.gold.max(0) as usize; + } + + if ui_info.distance_travelled != distance.0 { + ui_info.distance_travelled = distance.0; + } + + if ui_info.sustenance != hunger.sustenance { + ui_info.sustenance = hunger.sustenance; + } + } +} diff --git a/game_core/src/ui/sync/sync_travel.rs b/game_core/src/ui/sync/sync_travel.rs index 585f492b45f649e764e5788250be8035e26c32d5..522ce4f12eb20a3c5d33434035318de8b2e3bde9 100644 --- a/game_core/src/ui/sync/sync_travel.rs +++ b/game_core/src/ui/sync/sync_travel.rs @@ -4,7 +4,7 @@ use bevy::prelude::*; use crate::states::Player; use crate::world::{CurrentResidence, MapQuery, TownPaths, TravelPath}; -#[derive(Resource, Clone)] +#[derive(Resource, Clone, Default)] pub struct UITravelInfo { pub distance_remaining: f32, pub is_in_town: bool, diff --git a/game_core/src/ui/utilities.rs b/game_core/src/ui/utilities.rs index 8c5613ec7760594850ebcf15b96ef46c7d80f6e0..8a4e3fbd0f2e0bfacb9f2f80f40128c2608e0973 100644 --- a/game_core/src/ui/utilities.rs +++ b/game_core/src/ui/utilities.rs @@ -157,7 +157,7 @@ pub mod context { use crate::register_widget; use crate::ui::components::*; - use crate::ui::sync::UITravelInfo; + use crate::ui::sync::{UIStatsData, UITravelInfo}; use crate::ui::widgets::*; use crate::world::EncounterState; @@ -196,6 +196,12 @@ pub mod context { EmptyState, render_v_divider ); + register_widget!( + widget_context, + HDividerWidgetProps, + EmptyState, + render_h_divider + ); register_widget!( widget_context, ATextBoxProps, @@ -222,6 +228,13 @@ pub mod context { UITravelInfo, render_transit_panel ); + register_widget_with_resource!( + widget_context, + StatsPanelProps, + EmptyState, + UIStatsData, + render_stats_panel + ); register_widget_with_resource!( widget_context, EncounterPanelProps, diff --git a/game_core/src/ui/widgets/encounter_panel.rs b/game_core/src/ui/widgets/encounter_panel.rs index 12217f014629bdc1c2bbb8729c7900c073a289be..39fa3083de94188b6a825ac61231eb9d40f36437 100644 --- a/game_core/src/ui/widgets/encounter_panel.rs +++ b/game_core/src/ui/widgets/encounter_panel.rs @@ -94,6 +94,8 @@ pub fn render_encounter_panel( constructor! { <BackgroundBundle styles={KStyle { + + pointer_events: value(PointerEvents::None), render_command: value(RenderCommand::Quad), layout_type: value(LayoutType::Row), height: value(Units::Auto), @@ -151,7 +153,7 @@ pub fn render_encounter_panel( } </ElementBundle> </ElementBundle> - } + }; } EncounterState::Consequence(..) => {} EncounterState::NoEncounter => {} diff --git a/game_core/src/ui/widgets/mod.rs b/game_core/src/ui/widgets/mod.rs index d3e04d4803d5f6337436333bd16df787e46d0b5d..c706e56d449f46fe3f0443b567c7f7c80ba2e7bd 100644 --- a/game_core/src/ui/widgets/mod.rs +++ b/game_core/src/ui/widgets/mod.rs @@ -1,9 +1,11 @@ mod encounter_panel; mod shop_panel; +mod stats_panel; mod town_menu; mod transit_panel; pub use encounter_panel::{render_encounter_panel, EncounterPanel, EncounterPanelProps}; +pub use stats_panel::{render_stats_panel, StatsPanel, StatsPanelProps}; pub use town_menu::{ render_town_menu_panel, TownMenuPanel, TownMenuPanelProps, TownMenuPanelState, }; diff --git a/game_core/src/ui/widgets/shop_panel.rs b/game_core/src/ui/widgets/shop_panel.rs index 00c8ed3ca3d64a635f06a5c128788a806ea40aab..b008d4432c52e56d8e6a5b5aeadfa4f322b48d88 100644 --- a/game_core/src/ui/widgets/shop_panel.rs +++ b/game_core/src/ui/widgets/shop_panel.rs @@ -102,13 +102,13 @@ pub fn render_transit_panel( props={ButtonWidgetProps::text(format!("{}: {}", &place, format_ui_distance(*distance)), 28.0)} on_event={buysell_button_factory(place.clone())} /> - } + }; } } </ScrollBoxBundle> </ScrollContextProviderBundle> </ElementBundle> - } + }; true } diff --git a/game_core/src/ui/widgets/stats_panel.rs b/game_core/src/ui/widgets/stats_panel.rs new file mode 100644 index 0000000000000000000000000000000000000000..c1c7432ee7e92174a1319d28746b4329ec480277 --- /dev/null +++ b/game_core/src/ui/widgets/stats_panel.rs @@ -0,0 +1,105 @@ +use bevy::prelude::*; +use kayak_ui::prelude::*; +use kayak_ui::widgets::{BackgroundBundle, ElementBundle, TextProps, TextWidgetBundle}; + +use crate::system::utilities::format_ui_distance; +use crate::ui::sync::UIStatsData; +use crate::ui::{components::*, prelude::*, widgets::*}; +use crate::{basic_widget, empty_props}; + +empty_props!(StatsPanelProps); +basic_widget!(StatsPanelProps => StatsPanel); + +pub fn render_stats_panel( + In((widget_context, entity)): In<(KayakWidgetContext, Entity)>, + mut commands: Commands, + ui_data: Res<UIStatsData>, +) -> bool { + let parent_id = Some(entity); + // let root_style = KStyle { + // position_type: value(KPositionType::SelfDirected), + // top: stretch(1.0), + // left: stretch(1.0), + // right: stretch(1.0), + // bottom: px(50.0), + // height: px(50.0), + // width: stretch(0.6), + // max_width: px(200.0), + // layout_type: value(LayoutType::Row), + // padding: value(Edge::all(Units::Stretch(1.0))), + // ..Default::default() + // }; + // + let root_style = KStyle { + position_type: value(KPositionType::SelfDirected), + top: px(15.0), + left: stretch(1.0), + right: stretch(1.0), + bottom: stretch(1.0), + height: px(70.0), + layout_type: value(LayoutType::Column), + ..Default::default() + }; + // + let distance_travelled = format_ui_distance(ui_data.distance_travelled); + + let inner_container_style = KStyle { + height: px(60.0), + padding: edge_px(5.0), + col_between: px(5.0), + left: stretch(1.0), + right: stretch(1.0), + layout_type: value(LayoutType::Row), + ..Default::default() + }; + + rsx! { + <PanelWidget styles={root_style}> + <ElementBundle styles={inner_container_style}> + <InsetIconWidget props={InsetIconProps { size: 28.0, image: IconContent::Atlas(String::from("icons"), 14)}} /> + <TextWidgetBundle + text={TextProps { + content: distance_travelled, + size: 28.0, + ..Default::default() + }} + styles={KStyle { + color: value(Color::BLACK), + bottom: px(6.0), + ..Default::default() + }} + /> + + <InsetIconWidget props={InsetIconProps { size: 28.0, image: IconContent::Atlas(String::from("icons"), 8)}} /> + <TextWidgetBundle + text={TextProps { + content: format!("{}g", ui_data.money), + size: 28.0, + ..Default::default() + }} + styles={KStyle { + color: value(Color::BLACK), + bottom: px(6.0), + ..Default::default() + }} + /> + + <InsetIconWidget props={InsetIconProps { size: 28.0, image: IconContent::Atlas(String::from("icons"), 15)}} /> + <TextWidgetBundle + text={TextProps { + content: format!("{}", ui_data.sustenance), + size: 28.0, + ..Default::default() + }} + styles={KStyle { + color: value(Color::BLACK), + bottom: px(6.0), + ..Default::default() + }} + /> + </ElementBundle> + </PanelWidget> + }; + + true +} diff --git a/game_core/src/ui/widgets/town_menu.rs b/game_core/src/ui/widgets/town_menu.rs index f015185fcbc9e129f35b000bc3aaadf6babf1e7e..c204ab84d97692985b164b9ccae4f9d57a70555d 100644 --- a/game_core/src/ui/widgets/town_menu.rs +++ b/game_core/src/ui/widgets/town_menu.rs @@ -173,14 +173,14 @@ pub fn render_town_menu_panel( TownMenuTab::Travel => { constructor! { <TransitPanel /> - } + }; }, TownMenuTab::Merchant => {} TownMenuTab::Tavern => {} } } </PanelWidget> - } + }; } true diff --git a/game_core/src/ui/widgets/transit_panel.rs b/game_core/src/ui/widgets/transit_panel.rs index d610107afbda5243e22e29369391e832303c542e..4015846d1478be01a4e9ad74d11970c9a05d4929 100644 --- a/game_core/src/ui/widgets/transit_panel.rs +++ b/game_core/src/ui/widgets/transit_panel.rs @@ -102,13 +102,13 @@ pub fn render_transit_panel( props={ButtonWidgetProps::text(format!("{}: {}", &place, format_ui_distance(*distance)), 28.0)} on_event={transit_button_factory(place.clone())} /> - } + }; } } </ScrollBoxBundle> </ScrollContextProviderBundle> </ElementBundle> - } + }; true } diff --git a/game_core/src/world/mod.rs b/game_core/src/world/mod.rs index 0f03c264b405103d2e3dfc9a9af4e507c04c1537..91dbefcd4547fddabc1298d87249b311cf90c048 100644 --- a/game_core/src/world/mod.rs +++ b/game_core/src/world/mod.rs @@ -2,9 +2,6 @@ use bevy::app::App; use bevy::prelude::{Commands, Plugin}; use iyes_loopless::prelude::{AppLooplessStateExt, ConditionSet}; -use crate::system::flow::AppState; -use crate::world::utils::ActiveLevel; - mod debug; mod encounters; mod generators; @@ -15,6 +12,17 @@ mod travel; mod utils; mod world_query; +pub use encounters::{EncounterState, WorldZones}; +pub use spawning::PendingLoadState; +pub use towns::{CurrentResidence, PathingResult, TownPaths, TravelPath, TravelTarget}; +pub use trading::{HungerState, ItemName, TradeGood, TradingState}; +pub use travel::DistanceTravelled; +pub use world_query::{CameraBounds, MapQuery}; + +use crate::system::flow::AppState; +use crate::world::spawning::PopulateWorldEvent; +use crate::world::utils::ActiveLevel; + pub struct WorldPlugin; impl Plugin for WorldPlugin { fn build(&self, app: &mut App) { @@ -42,11 +50,3 @@ impl Plugin for WorldPlugin { ); } } - -pub use encounters::{EncounterState, WorldZones}; -pub use spawning::PendingLoadState; -pub use towns::{CurrentResidence, PathingResult, TownPaths, TravelPath, TravelTarget}; -pub use trading::{HungerState, ItemName, TradeGood, TradingState}; -pub use world_query::{CameraBounds, MapQuery}; - -use crate::world::spawning::PopulateWorldEvent; diff --git a/game_core/src/world/spawning.rs b/game_core/src/world/spawning.rs index ac864850dd37c32784f5cdcdbe83942855ab721c..73240a8920a8e59b75d644c6f4c26f1dc0864d3a 100644 --- a/game_core/src/world/spawning.rs +++ b/game_core/src/world/spawning.rs @@ -10,6 +10,7 @@ use crate::states::Player; use crate::system::camera::ChaseCam; use crate::world::encounters::WorldZones; use crate::world::towns::{CurrentResidence, TownPaths}; +use crate::world::travel::DistanceTravelled; use crate::world::utils::{grid_to_px, px_to_grid, ActiveLevel, WorldLinked, TILE_SCALE_F32}; use crate::world::world_query::MapQuery; use crate::world::{EncounterState, HungerState, TradingState, TravelPath, TravelTarget}; @@ -251,27 +252,37 @@ pub fn populate_world( } } } + + cmds.insert( + pending_load + .as_ref() + .map(|pending| pending.0.player_total_distance) + .unwrap_or_else(|| DistanceTravelled(0.0)), + ); + cmds.insert( + pending_load + .as_ref() + .map(|pending| pending.0.player_inventory.clone()) + .unwrap_or_else(|| TradingState { + gold: 500, + items: Default::default(), + }), + ); + cmds.insert( + pending_load + .as_ref() + .map(|pending| pending.0.player_hunger.clone()) + .unwrap_or_else(|| HungerState { + starvation_ticks: 0.0, + sustenance: 100, + }), + ); } let world_zones = WorldZones::from_entities(level.px_hei, MapQuery::get_entities_of(level)); commands.insert_resource(trade_routes); commands.insert_resource(world_zones); - - commands.insert_resource( - pending_load - .as_ref() - .map(|pending| &pending.0.player_inventory) - .cloned() - .unwrap_or_default(), - ); - commands.insert_resource( - pending_load - .as_ref() - .map(|pending| &pending.0.player_hunger) - .cloned() - .unwrap_or_default(), - ); } } @@ -282,8 +293,6 @@ pub fn clean_game_state( ) { commands.remove_resource::<PendingLoadState>(); commands.remove_resource::<PersistenceState>(); - commands.remove_resource::<TradingState>(); - commands.remove_resource::<HungerState>(); commands.insert_resource(TownPaths::default()); commands.insert_resource(WorldZones::default()); diff --git a/game_core/src/world/trading.rs b/game_core/src/world/trading.rs index 212ebc20cdef8844a7459cb629303a2a461da87d..6b70332c73d832cb124840b9125af521daca31a0 100644 --- a/game_core/src/world/trading.rs +++ b/game_core/src/world/trading.rs @@ -1,4 +1,4 @@ -use bevy::prelude::Resource; +use bevy::prelude::{Component, Resource}; use bevy::utils::hashbrown::hash_map::Entry; use bevy::utils::HashMap; use num_traits::AsPrimitive; @@ -8,13 +8,13 @@ use crate::ui::components::IconContent; pub type ItemName = String; -#[derive(Resource, Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, Component)] pub struct TradingState { pub items: HashMap<ItemName, usize>, pub gold: isize, } -#[derive(Resource, Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Serialize, Deserialize, Component)] pub struct HungerState { pub sustenance: usize, pub starvation_ticks: f32, diff --git a/game_core/src/world/travel.rs b/game_core/src/world/travel.rs index 03a1e8bf407b47bc69942ef501a21ac1c6ed60ec..a321936460de2e77051dfd6287d11786f8ddc2e0 100644 --- a/game_core/src/world/travel.rs +++ b/game_core/src/world/travel.rs @@ -2,12 +2,16 @@ use std::ops::SubAssign; use bevy::math::Vec3Swizzles; use bevy::prelude::*; +use serde::{Deserialize, Serialize}; use crate::persistance::SaveFileEvent; use crate::world::encounters::EncounterState; use crate::world::towns::{CurrentResidence, TravelPath, TravelTarget}; use crate::world::PathingResult; +#[derive(Component, Serialize, Deserialize, Debug, Copy, Clone, Default)] +pub struct DistanceTravelled(pub f32); + pub fn tick_travelling_merchant( mut commands: Commands, time: Res<Time>, @@ -17,6 +21,7 @@ pub fn tick_travelling_merchant( &mut TravelPath, &mut TravelTarget, &mut CurrentResidence, + &mut DistanceTravelled, )>, encounter_state: Res<EncounterState>, mut autosave: EventWriter<SaveFileEvent>, @@ -26,8 +31,11 @@ pub fn tick_travelling_merchant( } let delta = time.delta_seconds(); - for (entity, mut transform, mut path, mut target, mut residence) in &mut merchant_query { + for (entity, mut transform, mut path, mut target, mut residence, mut high_score) in + &mut merchant_query + { let step = path.get_step(transform.translation.xy(), 16.0) * delta; + high_score.0 += step.length(); transform.translation.sub_assign(step.extend(0.0)); match path.check_position(transform.translation.xy()) {