diff --git a/src/inventory/item.rs b/src/inventory/item.rs index 211d612..334a3b3 100644 --- a/src/inventory/item.rs +++ b/src/inventory/item.rs @@ -2,19 +2,20 @@ use std::mem::swap; use bevy::prelude::*; -#[derive(Component, Clone)] +#[derive(Component, Clone, Debug)] pub struct Item { pub size: UVec2, pub position: Option, + pub rotated: bool, } impl Item { pub fn new(size: UVec2) -> Self { - Self { size, position: None } + Self { size, position: None, rotated: false } } pub fn new_positioned(size: UVec2, position: UVec2) -> Self { - Self { size, position: Some(position) } + Self { size, position: Some(position), rotated: false } } pub fn rect(&self) -> Option { @@ -36,14 +37,15 @@ impl Item { } /// Swap size.x with size.y - pub fn swap_size(&mut self) { + pub fn rotate(&mut self) { swap(&mut self.size.x, &mut self.size.y); + self.rotated = !self.rotated; } /// Get clone of item with swapped size - pub fn clone_swapped(&self) -> Self { + pub fn clone_rotated(&self) -> Self { let mut new = self.clone(); - new.swap_size(); + new.rotate(); new } } diff --git a/src/inventory/ui.rs b/src/inventory/ui.rs index 61e27a5..ad72efd 100644 --- a/src/inventory/ui.rs +++ b/src/inventory/ui.rs @@ -1,8 +1,11 @@ +use std::f32::consts::FRAC_PI_2; + use bevy::prelude::*; -use crate::{inventory::{ActiveInventory, Inventory}, ui::{UiRoot, WindowSize}}; +use crate::{inventory::{ActiveInventory, Inventory, item::Item}, ui::{UiRoot, WindowSize}}; const UI_SLOT_ASSET_PATH: &'static str = "sprites/ui/inventory_slot.png"; +const TEMP_ITEM_PATH: &'static str = "sprites/items/choco_bar.png"; #[derive(Component)] #[require(Node)] @@ -12,6 +15,10 @@ pub struct UiInventory; #[require(Node, ImageNode)] pub struct UiInventorySlot(UVec2); +#[derive(Component)] +#[require(Node, ImageNode)] +pub struct UiItem(Entity); + fn ui_inventory_bundle(inventory: &Inventory, window_size: &Res) -> impl Bundle { let window_ratio = window_size.aspect_ratio(); let (width, height) = { @@ -37,7 +44,7 @@ fn ui_inventory_bundle(inventory: &Inventory, window_size: &Res) -> ) } -fn inventory_slot_bundle(x: u32, y: u32, width: u32, height: u32, image: Handle) -> impl Bundle { +fn inventory_slot_bundle(x: u32, y: u32, image: Handle) -> impl Bundle { ( UiInventorySlot(UVec2::new(x, y)), ImageNode { @@ -50,15 +57,54 @@ fn inventory_slot_bundle(x: u32, y: u32, width: u32, height: u32, image: Handle< height: percent(100.), grid_column: GridPlacement::start(x as i16 + 1), grid_row: GridPlacement::start(y as i16 + 1), + align_items: AlignItems::Start, ..default() }, ) } +fn ui_item_bundle(item: &Item, item_entity: Entity, image: Handle) -> impl Bundle { + let (left, top, min_width, min_height, ui_transform) = match item.rotated { + true => ( + percent(100.), + percent(-100.), + percent(100. * item.size.y as f32), + percent(100. * item.size.x as f32), + UiTransform::from_rotation(Rot2::radians(-FRAC_PI_2)), + ), + false => ( + auto(), + auto(), + percent(100. * item.size.x as f32), + percent(100. * item.size.y as f32), + UiTransform::default(), + ), + }; + ( + UiItem(item_entity), + ImageNode { + image, + image_mode: NodeImageMode::Stretch, + ..default() + }, + Node { + left, + top, + min_width, + min_height, + ..default() + }, + BackgroundColor(Color::hsla(0., 0., 0., 0.5)), + ui_transform, + GlobalZIndex(1), + ) +} + pub fn setup_ui_inventory( mut commands: Commands, asset_server: Res, inventory_query: Query<(&Inventory, Option<&Children>), With>, + item_query: Query<&Item>, root_query: Query>, window_size: Res, ) { @@ -67,14 +113,31 @@ pub fn setup_ui_inventory( return; }; let ui_slot_image: Handle = asset_server.load(UI_SLOT_ASSET_PATH); - for (inventory, _children) in inventory_query { + let temp_item_image: Handle = asset_server.load(TEMP_ITEM_PATH); + for (inventory, children) in inventory_query { + let items = match children { + Some(children) => { + children.iter().filter_map(|item_entity| { + match item_query.get(item_entity) { + Ok(item) => Some((item, item_entity)), + Err(err) => { + warn!("Error querying item {item_entity}: {err}"); + None + }, + } + }).collect::>() + } + None => Vec::new(), + }; let inventory_entity = commands.spawn(ui_inventory_bundle(inventory, &window_size)) .with_children(|commands| { - for x in 0..inventory.size.x { - for y in 0..inventory.size.y { - commands.spawn(inventory_slot_bundle(x, y, inventory.size.x, inventory.size.y, ui_slot_image.clone())); + for x in 0..inventory.size.x { for y in 0..inventory.size.y { + let mut slot_commands = commands.spawn(inventory_slot_bundle(x, y, ui_slot_image.clone())); + if let Some((item, entity)) = items.iter() + .find(|(i, _)| i.position.unwrap_or_default() == UVec2::new(x, y)) { + slot_commands.with_child(ui_item_bundle(item, *entity, temp_item_image.clone())); } - } + } } }).id(); commands.entity(root).add_child(inventory_entity); diff --git a/src/player.rs b/src/player.rs index 902b2be..5968cf3 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,7 +1,7 @@ use bevy::prelude::*; use leafwing_input_manager::prelude::*; -use crate::{GameState, InputAction as Action, inventory::{ActiveInventory, Inventory}}; +use crate::{GameState, InputAction as Action, inventory::{ActiveInventory, Inventory, item::Item}}; #[derive(Component)] pub struct Player { @@ -18,7 +18,7 @@ fn player_bundle(asset_server: &Res) -> impl Bundle { Sprite::from_image(image), Transform::from_xyz(0f32, 0f32, 1f32), Action::default_input_map(), - Inventory::new(UVec2::new(12, 8)), + Inventory::new(UVec2::new(4, 4)), ActiveInventory, ) } @@ -27,7 +27,37 @@ pub fn setup_player(mut commands: Commands, asset_server: Res) { commands.spawn(player_bundle(&asset_server)); } +pub fn try_insert_item( + mut commands: Commands, + item_query: Query<&Item>, + inventory_query: Query<(Entity, &Inventory, Option<&Children>)>, +) { + let mut item = Item::new(UVec2::new(1, 1)); + for (entity, inventory, children) in inventory_query { + let children = match children { + Some(children) => &children[..], + None => &[], + }; + match inventory.find_free_space(item_query, children, item.size) { + Some((position, should_rotate)) => { + if should_rotate { + item.rotate(); + } + item.position = Some(position); + info!("Spawning item {item:?}"); + commands.entity(entity).with_child(item); + }, + None => { + warn!("Inventory does not have space for {}", item.size); + }, + } + // only first inventory + break; + } +} + pub fn handle_input( + mut commands: Commands, time: Res