diff --git a/Cargo.lock b/Cargo.lock index 8b4d53a..8cd5c91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2422,7 +2422,7 @@ dependencies = [ [[package]] name = "expedition_demo" -version = "0.2.0" +version = "0.3.0" dependencies = [ "bevy", "bevy_common_assets", diff --git a/Cargo.toml b/Cargo.toml index 1f0d84d..8046927 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ cargo-features = ["codegen-backend"] [package] name = "expedition_demo" -version = "0.2.0" +version = "0.3.0" edition = "2024" [dependencies] diff --git a/src/inventory/mod.rs b/src/inventory.rs similarity index 70% rename from src/inventory/mod.rs rename to src/inventory.rs index 3304bac..9e78d7c 100644 --- a/src/inventory/mod.rs +++ b/src/inventory.rs @@ -1,6 +1,6 @@ use bevy::prelude::*; -pub mod item; +use crate::item::Item; /// Marker that this inventory will show up when UI is built #[derive(Component, Reflect, Default, Clone, Copy, Debug, PartialEq, Eq)] @@ -20,12 +20,11 @@ impl Inventory { pub fn can_fit( &self, - item_query: Query<&item::Item>, + item_query: Query<&Item>, contained_items: &[Entity], - queried_size: UVec2, - queried_position: UVec2, + queried_item: &Item, ) -> bool { - let item_corner = queried_size + queried_position; + let item_corner = queried_item.size + queried_item.position; if item_corner.x > self.size.x || item_corner.y > self.size.y { return false; } @@ -35,7 +34,7 @@ impl Inventory { continue; }; - if item.overlaps(queried_size, queried_position) { + if item.overlaps(queried_item) { return false; } } @@ -44,40 +43,37 @@ impl Inventory { pub fn can_move( &self, - item_query: Query<&item::Item>, + item_query: Query<&Item>, contained_items: &[Entity], queried_item: Entity, queried_position: UVec2, ) -> bool { - let Ok(item::Item {size, ..}) = item_query.get(queried_item) else { + let Ok(item) = item_query.get(queried_item) else { return false; }; let children = contained_items.iter() .filter_map(|e| if e.ne(&&queried_item) { Some(*e) } else { None }) .collect::>(); - self.can_fit(item_query, children.as_slice(), *size, queried_position) + let mock_item = Item::new(item.size, queried_position); + self.can_fit(item_query, children.as_slice(), &mock_item) } pub fn can_replace( &self, - item_query: Query<&item::Item>, + item_query: Query<&Item>, contained_items: &[Entity], replacable_item: Entity, - queried_item: &item::Item, + queried_item: &Item, ) -> bool { - let Some(position) = &queried_item.position else { - warn!("Trying to query rotated item without position"); - return false; - }; let children = contained_items.iter() .filter_map(|e| if e.ne(&&replacable_item) { Some(*e) } else { None }) .collect::>(); - self.can_fit(item_query, children.as_slice(), queried_item.size, *position) + self.can_fit(item_query, children.as_slice(), queried_item) } pub fn can_rotate( &self, - item_query: Query<&item::Item>, + item_query: Query<&Item>, contained_items: &[Entity], queried_item: Entity, ) -> bool { @@ -85,20 +81,16 @@ impl Inventory { error!("Could not query item"); return false; }; - let Some(position) = &item.position else { - warn!("Trying to query rotated item without position"); - return false; - }; let children = contained_items.iter() .filter_map(|e| if e.ne(&&queried_item) { Some(*e) } else { None }) .collect::>(); let rotated_item = item.clone_rotated(); - self.can_fit(item_query, children.as_slice(), rotated_item.size, *position) + self.can_fit(item_query, children.as_slice(), &rotated_item) } fn find_free_space_inner( &self, - item_query: Query<&item::Item>, + item_query: Query<&Item>, contained_items: &[Entity], queried_size: UVec2, was_swapped: bool, @@ -108,9 +100,10 @@ impl Inventory { }; for x in 0..=tries_x { for y in 0..=tries_y { - let tested_pos = UVec2::new(x, y); - if self.can_fit(item_query, contained_items, queried_size, tested_pos) { - return Some((tested_pos, was_swapped)); + let pos = UVec2::new(x, y); + let mock_item = Item::new(queried_size, pos); + if self.can_fit(item_query, contained_items, &mock_item) { + return Some((pos, was_swapped)); } } } @@ -119,7 +112,7 @@ impl Inventory { pub fn find_free_space( &self, - item_query: Query<&item::Item>, + item_query: Query<&Item>, contained_items: &[Entity], queried_size: UVec2, ) -> Option<(UVec2, bool)> { diff --git a/src/inventory/item/lockpick.rs b/src/item/lockpick.rs similarity index 90% rename from src/inventory/item/lockpick.rs rename to src/item/lockpick.rs index dfde1c5..3eb0087 100644 --- a/src/inventory/item/lockpick.rs +++ b/src/item/lockpick.rs @@ -13,7 +13,7 @@ pub struct Lockpick; pub fn lockpick_bundle(asset_server: &Res, position: UVec2) -> impl Bundle { let image = asset_server.load(LOCKPICK_SPRITE); ( - Item::new_positioned(uvec2(1, 1), position), + Item::new(uvec2(1, 1), position), ItemImage(image), Lockpick, ) diff --git a/src/inventory/item/mod.rs b/src/item/mod.rs similarity index 53% rename from src/inventory/item/mod.rs rename to src/item/mod.rs index d7ec437..915416f 100644 --- a/src/inventory/item/mod.rs +++ b/src/item/mod.rs @@ -6,7 +6,7 @@ pub mod lockpick; #[derive(Component, Debug, Deref, DerefMut, PartialEq, Eq, Default, Clone, Reflect)] #[reflect(Component, Debug, PartialEq, Default, Clone)] -pub struct ItemImage(Handle); +pub struct ItemImage(pub Handle); // TODO: get rid of Option in position, it's no longer needed #[derive(Component, Clone, Debug, Reflect, PartialEq, Eq)] @@ -14,7 +14,7 @@ pub struct ItemImage(Handle); #[require(ItemImage)] pub struct Item { pub size: UVec2, - pub position: Option, + pub position: UVec2, pub rotated: bool, } @@ -22,37 +22,24 @@ impl Default for Item { fn default() -> Self { Self { size: uvec2(1, 1), - position: Some(uvec2(0, 0)), + position: uvec2(0, 0), rotated: false, } } } impl Item { - pub fn new(size: UVec2) -> Self { - Self { size, position: None, rotated: false } - } - - pub fn new_positioned(size: UVec2, position: UVec2) -> Self { - Self { size, position: Some(position), rotated: false } - } - - pub fn rect(&self) -> Option { - let Some(position) = self.position else { - return None; - }; - - Some(URect::from_corners(position, position + self.size)) + pub fn new(size: UVec2, position: UVec2) -> Self { + Self { size, position, rotated: false } } - pub fn overlaps(&self, other_size: UVec2, other_position: UVec2) -> bool { - let Some(rect) = self.rect() else { - return false; - }; - - let other_rect = URect::from_corners(other_position, other_position + other_size); - - !rect.intersect(other_rect).is_empty() + #[inline(always)] + pub fn rect(&self) -> URect { + URect::from_corners(self.position, self.position + self.size) + } + + pub fn overlaps(&self, other: &Item) -> bool { + !self.rect().intersect(other.rect()).is_empty() } /// Swap size.x with size.y diff --git a/src/layout/lock.rs b/src/layout/lock.rs index 4f0a6f8..a26a51c 100644 --- a/src/layout/lock.rs +++ b/src/layout/lock.rs @@ -1,7 +1,12 @@ use bevy::prelude::*; use bevy_rapier2d::prelude::*; -use crate::{inventory::{Inventory, item::lockpick::Lockpick}, meters, player::Player}; +use crate::{ + meters, + inventory::Inventory, + item::lockpick::Lockpick, + player::Player, +}; use super::*; diff --git a/src/layout/systems.rs b/src/layout/systems.rs index 288083e..edcc387 100644 --- a/src/layout/systems.rs +++ b/src/layout/systems.rs @@ -7,7 +7,7 @@ use bevy_rapier2d::prelude::*; use crate::{ meters, - inventory::item::lockpick::lockpick_bundle, + item::lockpick::lockpick_bundle, player::{ Player, player_bundle, diff --git a/src/lib.rs b/src/lib.rs index 28fc673..9db5949 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ use bevy_rapier2d::{ pub mod input; pub mod inventory; +pub mod item; pub mod layout; pub mod player; #[cfg(test)] diff --git a/src/tests/inventory.rs b/src/tests/inventory.rs index 6516de7..e70d704 100644 --- a/src/tests/inventory.rs +++ b/src/tests/inventory.rs @@ -2,8 +2,8 @@ use bevy::prelude::*; use crate::{ inventory::{ Inventory, - item::Item, }, + item::Item, ui::inventory::DraggedItem, }; @@ -13,37 +13,37 @@ fn inventory() -> Inventory { /// 1x2 0;0 fn item_a() -> Item { - Item::new_positioned(UVec2::new(1, 2), UVec2::new(0, 0)) + Item::new(UVec2::new(1, 2), UVec2::new(0, 0)) } /// 2x2 0;2 fn item_b() -> Item { - Item::new_positioned(UVec2::new(2, 2), UVec2::new(0, 2)) + Item::new(UVec2::new(2, 2), UVec2::new(0, 2)) } /// 3x2 1;0 fn item_c() -> Item { - Item::new_positioned(UVec2::new(3, 2), UVec2::new(1, 0)) + Item::new(UVec2::new(3, 2), UVec2::new(1, 0)) } /// 2x2 2;2 fn item_d() -> Item { - Item::new_positioned(UVec2::new(2, 2), UVec2::new(2, 2)) + Item::new(UVec2::new(2, 2), UVec2::new(2, 2)) } /// 1x1 0;0 fn item_e() -> Item { - Item::new_positioned(UVec2::new(1, 1), UVec2::new(0, 0)) + Item::new(UVec2::new(1, 1), UVec2::new(0, 0)) } /// 5x5 0;0 fn item_f() -> Item { - Item::new_positioned(UVec2::new(5, 5), UVec2::new(0, 0)) + Item::new(UVec2::new(5, 5), UVec2::new(0, 0)) } /// 2x2 3;3 fn item_g() -> Item { - Item::new_positioned(UVec2::new(2, 2), UVec2::new(3, 3)) + Item::new(UVec2::new(2, 2), UVec2::new(3, 3)) } #[derive(Resource)] @@ -62,14 +62,12 @@ fn insert_item( inventory_query: Query<(Entity, &Inventory, Option<&Children>)> ) { let item = &items.0[items.1]; - let q_size = item.size; - let q_pos = item.position.unwrap(); let (entity, inventory, children) = inventory_query.single().unwrap(); if let Some(children) = children { if items.2 { - assert!(inventory.can_fit(item_query, children, q_size, q_pos)); + assert!(inventory.can_fit(item_query, children, item)); } else { - assert!(!inventory.can_fit(item_query, children, q_size, q_pos)); + assert!(!inventory.can_fit(item_query, children, item)); } } let item_entity = commands.spawn(item.clone()).id(); diff --git a/src/ui/inventory/bundles.rs b/src/ui/inventory/bundles.rs index 8e8df87..b3ebf69 100644 --- a/src/ui/inventory/bundles.rs +++ b/src/ui/inventory/bundles.rs @@ -141,8 +141,8 @@ pub fn ui_item_bundle(item: &Item, item_entity: Entity, image: Handle) -> is_hoverable: true, }, Name::new(format!("UiItem ({},{})", - item.position.unwrap_or_default().x, - item.position.unwrap_or_default().y, + item.position.x, + item.position.y, )), ) } diff --git a/src/ui/inventory/mod.rs b/src/ui/inventory/mod.rs index 3e81139..c5534df 100644 --- a/src/ui/inventory/mod.rs +++ b/src/ui/inventory/mod.rs @@ -5,8 +5,8 @@ use bevy::prelude::*; use crate::{ inventory::{ Inventory, - item::Item, }, + item::Item, ui::UiRotateEvent, }; diff --git a/src/ui/inventory/observers.rs b/src/ui/inventory/observers.rs index 8ebd2e4..307cc53 100644 --- a/src/ui/inventory/observers.rs +++ b/src/ui/inventory/observers.rs @@ -62,11 +62,8 @@ pub fn on_item_drag_start( error!("UiItem {} is pointing to non-existing Item", e.event_target()); return; }; - let Some(item_position) = item.position else { - return; - }; let slot_position = hovered_slot.0; - let diff = slot_position - item_position; + let diff = slot_position - item.position; commands.entity(e.event_target()).insert(DraggedItem(item.clone(), diff)); } } @@ -123,15 +120,18 @@ pub fn on_item_drag_drop( info!("{actual_position:?}"); let temp_item = &mut dragged_item.0; if actual_position.is_negative_bitmask() == 0 { - temp_item.position = Some(actual_position.as_uvec2()); + temp_item.position = actual_position.as_uvec2(); } - let Some((slot_id, _, _)) = slot_id_query.iter().find(|(_, slot_pos, slot_parent_id)| slot_pos.0 == temp_item.position.unwrap() && slot_parent_id == &slot_parent) else { + let Some((slot_id, _, _)) = slot_id_query.iter().find(|(_, slot_pos, slot_parent_id)| slot_pos.0 == temp_item.position && slot_parent_id == &slot_parent) else { return; }; if inventory.can_replace(item_query.as_readonly(), items, *item_entity, temp_item) { - let mut item = item_query.get_mut(*item_entity).unwrap(); + let Ok(mut item) = item_query.get_mut(*item_entity) else { + error!("Cannot get item to replace"); + return; + }; item.position = temp_item.position; item.size = temp_item.size; item.rotated = temp_item.rotated; diff --git a/src/ui/inventory/systems.rs b/src/ui/inventory/systems.rs index fc52a04..ccaad34 100644 --- a/src/ui/inventory/systems.rs +++ b/src/ui/inventory/systems.rs @@ -2,14 +2,21 @@ use bevy::prelude::*; use crate::{ inventory::ActiveInventory, + item::{ + Item, + ItemImage, + }, player::Player, ui::UiRoot, }; -use super::{*, bundles::*, observers::*}; +use super::{ + *, + bundles::*, + observers::*, +}; const UI_SLOT_ASSET_PATH: &'static str = "sprites/ui/inventory_slot.png"; -const TEMP_ITEM_PATH: &'static str = "sprites/items/choco_bar.png"; pub fn setup_ui_inventory( mut commands: Commands, @@ -18,6 +25,7 @@ pub fn setup_ui_inventory( player_query: Query<(), With>, active_inventory_query: Query>, item_query: Query<&Item>, + item_image_query: Query<&ItemImage>, root_query: Query>, ) { let Ok(root) = root_query.single() else { @@ -25,7 +33,6 @@ pub fn setup_ui_inventory( return; }; let ui_slot_image: Handle = asset_server.load(UI_SLOT_ASSET_PATH); - let temp_item_image: Handle = asset_server.load(TEMP_ITEM_PATH); let (mut player_inventory_ids, mut active_inventory_ids) = (Vec::new(), Vec::new()); for (inventory_parent, inventory_entity, inventory, children) in inventory_query.iter().sort::() { let is_player = match inventory_parent { @@ -48,10 +55,14 @@ pub fn setup_ui_inventory( Some(children) => { children.iter().filter_map(|item_entity| { match item_query.get(item_entity) { - Ok(item) => Some((item, item_entity)), + Ok(item) => { + let item_image = item_image_query.get(item_entity) + .expect("ItemImage is required on Item"); + Some((item, item_entity, item_image)) + }, Err(_) => None, } - }).collect::>() + }).collect::>() } None => Vec::new(), }; @@ -62,10 +73,10 @@ pub fn setup_ui_inventory( slot_commands.observe(on_slot_over) .observe(on_slot_out) .observe(on_item_drag_drop); - if let Some((item, entity)) = items.iter() - .find(|(i, _)| i.position.unwrap_or_default() == UVec2::new(x, y)) { + if let Some((item, entity, item_image)) = items.iter() + .find(|(i, _, _)| i.position == UVec2::new(x, y)) { slot_commands.with_children(|commands| { - commands.spawn(ui_item_bundle(item, *entity, temp_item_image.clone())) + commands.spawn(ui_item_bundle(item, *entity, item_image.0.clone())) .observe(on_item_over) .observe(on_item_out) .observe(on_item_drag_start)