From 6c243e5f4dd58ccf635c483c9b99b03012dbfcf5 Mon Sep 17 00:00:00 2001 From: Franklin Date: Wed, 15 Nov 2023 12:20:01 -0400 Subject: [PATCH] Inventory UI & system combined. Dropping works, pickup works, work on UI --- src/comps/core/inventory/player_inventory.rs | 7 +- src/comps/core/inventory/slot.rs | 15 +- src/comps/core/markers/inventory_screen.rs | 5 + src/logic/core/player/inventory.rs | 3 +- src/ui/game/inventory/interactables.rs | 22 +++ src/ui/game/inventory/menu.rs | 162 ++++++++++++++++++- src/ui/game/inventory/mod.rs | 1 + src/ui/game/inventory/plugin.rs | 4 +- 8 files changed, 205 insertions(+), 14 deletions(-) create mode 100644 src/ui/game/inventory/interactables.rs diff --git a/src/comps/core/inventory/player_inventory.rs b/src/comps/core/inventory/player_inventory.rs index c279f04..9357823 100644 --- a/src/comps/core/inventory/player_inventory.rs +++ b/src/comps/core/inventory/player_inventory.rs @@ -52,6 +52,7 @@ impl PlayerInventory { let mut inventory_slot = match slot { PlayerInventorySlotType::Primary => &mut self.primary, PlayerInventorySlotType::Secondary => &mut self.secondary, + _ => unimplemented!(), }; let Some(firearm) = item.get_firearm() else { return; }; inventory_slot.item = Some(firearm.get_item_box()); @@ -60,6 +61,7 @@ impl PlayerInventory { match slot { PlayerInventorySlotType::Primary => self.primary.item = None, PlayerInventorySlotType::Secondary => self.secondary.item = None, + _ => unimplemented!(), } } } @@ -91,6 +93,7 @@ pub fn drop_slot_in_game_world( PlayerInventorySlotType::Secondary => { player_inventory.get_secondary_mut() }, + _ => { unimplemented!() } }; match &item_inventory_slot.item { @@ -111,7 +114,7 @@ pub fn drop_slot_in_game_world( ); } player_inventory.drop_item(PlayerInventorySlotType::Primary); - inventory_changed_events.send(PlayerInventoryChangedEvent { item: Some(firearm.get_item_arc()), slot_type: PlayerInventorySlotType::Primary }); + inventory_changed_events.send(PlayerInventoryChangedEvent { item: None, slot_type: PlayerInventorySlotType::Primary }); } FirearmType::Secondary => { if let Some(secondary) = player_inventory.get_secondary() { @@ -124,7 +127,7 @@ pub fn drop_slot_in_game_world( ); } player_inventory.drop_item(PlayerInventorySlotType::Secondary); - inventory_changed_events.send(PlayerInventoryChangedEvent { item: Some(firearm.get_item_arc()), slot_type: PlayerInventorySlotType::Secondary }); + inventory_changed_events.send(PlayerInventoryChangedEvent { item: None, slot_type: PlayerInventorySlotType::Secondary }); } } }, diff --git a/src/comps/core/inventory/slot.rs b/src/comps/core/inventory/slot.rs index f2f31e9..fe8aeb7 100644 --- a/src/comps/core/inventory/slot.rs +++ b/src/comps/core/inventory/slot.rs @@ -2,10 +2,23 @@ use bevy::reflect::Reflect; -#[derive(Clone, Copy, Reflect, Default)] +#[derive(Clone, Copy, Reflect, Default, PartialEq, Eq, PartialOrd, Ord)] pub enum PlayerInventorySlotType { #[default] Primary, Secondary, + Backpack, + BodyArmor, //Slots, // TODO: Pass which slot +} + +impl PlayerInventorySlotType { + pub fn to_slot_title(&self) -> String { + match self { + PlayerInventorySlotType::Primary => String::from("Primary"), + PlayerInventorySlotType::Secondary => String::from("Secondary"), + PlayerInventorySlotType::Backpack => String::from("Backpack"), + PlayerInventorySlotType::BodyArmor => String::from("Body Armor"), + } + } } \ No newline at end of file diff --git a/src/comps/core/markers/inventory_screen.rs b/src/comps/core/markers/inventory_screen.rs index 81aa0a0..f0b2d7c 100644 --- a/src/comps/core/markers/inventory_screen.rs +++ b/src/comps/core/markers/inventory_screen.rs @@ -1,4 +1,9 @@ use bevy::ecs::component::Component; +use crate::comps::core::inventory::slot::PlayerInventorySlotType; + #[derive(Component)] pub struct InventoryScreenUiMarker; + +#[derive(Component, PartialEq, Eq, PartialOrd, Ord)] +pub struct InventorySlotUiMarker(pub PlayerInventorySlotType); \ No newline at end of file diff --git a/src/logic/core/player/inventory.rs b/src/logic/core/player/inventory.rs index 8819f67..dcf2860 100644 --- a/src/logic/core/player/inventory.rs +++ b/src/logic/core/player/inventory.rs @@ -28,8 +28,7 @@ pub fn update_player_inventory_system( player_inventory::drop_slot_in_game_world(&mut commands, player_transform, &mut inventory_changed_events, &mut player_inventory, &assets_gltf, &loaded_gltf_assets, event.item.get_item_slot()); player_inventory.pickup_item(event.item.as_ref(), event.item.get_item_slot()); - // TODO: Equip - + inventory_changed_events.send(PlayerInventoryChangedEvent { item: Some(event.item.clone()), slot_type: event.item.get_item_slot() }) } } } diff --git a/src/ui/game/inventory/interactables.rs b/src/ui/game/inventory/interactables.rs new file mode 100644 index 0000000..c93063d --- /dev/null +++ b/src/ui/game/inventory/interactables.rs @@ -0,0 +1,22 @@ +use bevy::prelude::*; + +use crate::comps::core::markers::inventory_screen::InventorySlotUiMarker; + +pub fn update_interactable_slot( + //mut commands: Commands, + mut query: Query<(Entity, &Interaction, &mut Style), (Changed, With)> +) { + for (_, interaction, mut style) in query.iter_mut() { + match *interaction { + Interaction::Pressed => { + println!("Clicked an Inventory Slot"); + } + Interaction::Hovered => { + style.border = UiRect::all(Val::Px(1.5)); + } + Interaction::None => { + style.border = UiRect::all(Val::Px(0.5)); + } + } + } +} \ No newline at end of file diff --git a/src/ui/game/inventory/menu.rs b/src/ui/game/inventory/menu.rs index 8b0274d..f27e91c 100644 --- a/src/ui/game/inventory/menu.rs +++ b/src/ui/game/inventory/menu.rs @@ -1,7 +1,7 @@ use bevy::{prelude::*, ui::FocusPolicy}; use crate::{ - comps::core::markers::inventory_screen::InventoryScreenUiMarker, + comps::core::{markers::inventory_screen::{InventoryScreenUiMarker, InventorySlotUiMarker}, inventory::slot::PlayerInventorySlotType, events::inventory_changed::PlayerInventoryChangedEvent}, ui::game::game_ui_state::{GameUiState, GameUiWindow}, }; @@ -23,7 +23,7 @@ pub fn setup_inventory_screen(mut commands: Commands) { ..Default::default() }, visibility: Visibility::Hidden, - background_color: BackgroundColor(Color::rgba(0.0, 0.0, 0.0, 0.9)), + background_color: BackgroundColor(Color::rgba(0.0, 0.0, 0.0, 0.8)), focus_policy: FocusPolicy::Block, ..Default::default() }, @@ -36,28 +36,30 @@ pub fn setup_inventory_screen(mut commands: Commands) { .spawn(NodeBundle { style: Style { display: Display::Flex, - width: Val::Percent(50.0), + width: Val::Percent(40.0), height: Val::Percent(100.0), align_items: AlignItems::Center, - justify_content: JustifyContent::Center, + justify_content: JustifyContent::SpaceEvenly, padding: UiRect::all(Val::Percent(5.0)), flex_direction: FlexDirection::Column, + border: UiRect::all(Val::Px(0.5)), ..Default::default() }, - background_color: BackgroundColor(Color::RED), + background_color: BackgroundColor(Color::Rgba { red: 0.0, green: 0.0, blue: 0.0, alpha: 0.86 }), + border_color: BorderColor(Color::WHITE), ..Default::default() }) .set_parent(background_id) .id(); // Right panel - let right_panel_id = commands + let _right_panel_id = commands .spawn(NodeBundle { style: Style { display: Display::Flex, width: Val::Percent(50.0), height: Val::Percent(100.0), align_items: AlignItems::Center, - justify_content: JustifyContent::Center, + justify_content: JustifyContent::SpaceEvenly, padding: UiRect::all(Val::Percent(5.0)), flex_direction: FlexDirection::Column, ..Default::default() @@ -66,6 +68,130 @@ pub fn setup_inventory_screen(mut commands: Commands) { }) .set_parent(background_id) .id(); + // Inventory Equipment title + let _ = commands.spawn( + TextBundle { + text: Text::from_section("Equipment", TextStyle { font_size: 32.0, color: Color::WHITE, ..Default::default() }), + ..Default::default() + }.with_text_alignment(TextAlignment::Center) + ).set_parent(left_panel_id); + + // First slot (Backpack and knife) + let body_slots_container = commands + .spawn(NodeBundle { + style: Style { + display: Display::Flex, + width: Val::Percent(90.0), + height: Val::Percent(30.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::SpaceBetween, + //padding: UiRect::all(Val::Percent(5.0)), + flex_direction: FlexDirection::Row, + ..Default::default() + }, + ..Default::default() + }).set_parent(left_panel_id).id(); + // Body Armor container + let body_armor_slot_container = commands + .spawn((NodeBundle { + style: Style { + display: Display::Flex, + width: Val::Percent(47.0), + height: Val::Percent(100.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + padding: UiRect::all(Val::Percent(5.0)), + flex_direction: FlexDirection::Column, + border: UiRect::all(Val::Px(0.5)), + ..Default::default() + }, + border_color: BorderColor(Color::WHITE), + background_color: BackgroundColor(Color::Rgba { red: 0.0, green: 0.0, blue: 0.0, alpha: 0.96 }), + ..Default::default() + }, Interaction::default(), InventorySlotUiMarker(PlayerInventorySlotType::BodyArmor) + )).set_parent(body_slots_container).id(); + // Backpack container + let backpack_slot_container = commands + .spawn((NodeBundle { + style: Style { + display: Display::Flex, + width: Val::Percent(47.0), + height: Val::Percent(100.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + padding: UiRect::all(Val::Percent(5.0)), + flex_direction: FlexDirection::Column, + border: UiRect::all(Val::Px(0.5)), + ..Default::default() + }, + border_color: BorderColor(Color::WHITE), + background_color: BackgroundColor(Color::Rgba { red: 0.0, green: 0.0, blue: 0.0, alpha: 0.96 }), + ..Default::default() + }, Interaction::default(), InventorySlotUiMarker(PlayerInventorySlotType::Backpack) + )).set_parent(body_slots_container).id(); + // Body Armor Text + commands.spawn(TextBundle { + text: Text::from_section("Body Armor", TextStyle { font_size: 28.0, color: Color::WHITE, ..Default::default() }), + ..Default::default() + }.with_text_alignment(TextAlignment::Center)) + .set_parent(body_armor_slot_container); + // Backpack Text + commands.spawn(TextBundle { + text: Text::from_section("Backpack", TextStyle { font_size: 28.0, color: Color::WHITE, ..Default::default() }), + ..Default::default() + }.with_text_alignment(TextAlignment::Center)) + .set_parent(backpack_slot_container); + + // Primary Slot + let primary_slot = commands + .spawn((NodeBundle { + style: Style { + display: Display::Flex, + width: Val::Percent(90.0), + height: Val::Percent(20.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + padding: UiRect::all(Val::Percent(5.0)), + flex_direction: FlexDirection::Column, + border: UiRect::all(Val::Px(0.5)), + ..Default::default() + }, + border_color: BorderColor(Color::WHITE), + background_color: BackgroundColor(Color::Rgba { red: 0.0, green: 0.0, blue: 0.0, alpha: 0.96 }), + ..Default::default() + }, Interaction::default(), InventorySlotUiMarker(PlayerInventorySlotType::Primary) + )).set_parent(left_panel_id).id(); + // Secondary Slot + let secondary_slot = commands + .spawn((NodeBundle { + style: Style { + display: Display::Flex, + width: Val::Percent(90.0), + height: Val::Percent(20.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + padding: UiRect::all(Val::Percent(5.0)), + flex_direction: FlexDirection::Column, + border: UiRect::all(Val::Px(0.5)), + ..Default::default() + }, + border_color: BorderColor(Color::WHITE), + background_color: BackgroundColor(Color::Rgba { red: 0.0, green: 0.0, blue: 0.0, alpha: 0.96 }), + ..Default::default() + }, Interaction::default(), InventorySlotUiMarker(PlayerInventorySlotType::Secondary) + )).set_parent(left_panel_id).id(); + // Primary Slot Text + commands.spawn(TextBundle { + text: Text::from_section("Primary", TextStyle { font_size: 28.0, color: Color::WHITE, ..Default::default() }), + ..Default::default() + }.with_text_alignment(TextAlignment::Center)) + .set_parent(primary_slot); + // Secondary Slot Text + commands.spawn(TextBundle { + text: Text::from_section("Secondary", TextStyle { font_size: 28.0, color: Color::WHITE, ..Default::default() }), + ..Default::default() + }.with_text_alignment(TextAlignment::Center)) + .set_parent(secondary_slot); } @@ -73,6 +199,9 @@ pub fn update_inventory_screen( //mut commands: Commands, game_ui_state: Res, mut inventory_screen_query: Query<&mut Visibility, With>, + inventory_slot_query: Query<(&InventorySlotUiMarker, &Children)>, + mut inventory_slot_text_query: Query<(&mut Text, Entity)>, + mut inventory_changed_events: EventReader, ) { for mut visibility in inventory_screen_query.iter_mut() { if game_ui_state.is_showing_window(GameUiWindow::InventoryMenu) { @@ -81,4 +210,23 @@ pub fn update_inventory_screen( *visibility = Visibility::Hidden; } } + + for event in inventory_changed_events.read() { + for (inventory_slot_ui_marker, children) in inventory_slot_query.iter() { + for (mut text, text_entity) in inventory_slot_text_query.iter_mut() { + for child in children { + if child == &text_entity && event.slot_type == inventory_slot_ui_marker.0 { + // Mutate text + if let Some(section) = text.sections.first_mut() { + section.value = match &event.item { + Some(item) => item.inventory_title(), + None => inventory_slot_ui_marker.0.to_slot_title(), + } + } + } + } + } + } + } } + diff --git a/src/ui/game/inventory/mod.rs b/src/ui/game/inventory/mod.rs index 738357c..d8f640f 100644 --- a/src/ui/game/inventory/mod.rs +++ b/src/ui/game/inventory/mod.rs @@ -1,2 +1,3 @@ pub mod menu; pub mod plugin; +pub mod interactables; \ No newline at end of file diff --git a/src/ui/game/inventory/plugin.rs b/src/ui/game/inventory/plugin.rs index f1fbe12..64971f1 100644 --- a/src/ui/game/inventory/plugin.rs +++ b/src/ui/game/inventory/plugin.rs @@ -1,12 +1,12 @@ use bevy::prelude::*; -use super::menu::{setup_inventory_screen, update_inventory_screen}; +use super::{menu::{setup_inventory_screen, update_inventory_screen}, interactables::update_interactable_slot}; pub struct InventoryMenuPlugin; impl Plugin for InventoryMenuPlugin { fn build(&self, app: &mut App) { app.add_systems(Startup, setup_inventory_screen); - app.add_systems(Update, update_inventory_screen); + app.add_systems(Update, (update_inventory_screen, update_interactable_slot)); } }