feat: Added Inventory and Item components

- Moved input tests into nested module
- Added inventory tests
This commit is contained in:
Alexey 2026-03-06 16:41:17 +03:00
commit dab3134f15
4 changed files with 260 additions and 72 deletions

35
src/inventory/item.rs Normal file
View file

@ -0,0 +1,35 @@
use bevy::prelude::*;
#[derive(Component, Clone)]
pub struct Item {
pub size: UVec2,
pub position: Option<UVec2>,
}
impl Item {
pub fn new(size: UVec2) -> Self {
Self { size, position: None }
}
pub fn new_positioned(size: UVec2, position: UVec2) -> Self {
Self { size, position: Some(position) }
}
pub fn rect(&self) -> Option<URect> {
let Some(position) = self.position else {
return None;
};
Some(URect::from_corners(position, position + self.size))
}
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()
}
}

39
src/inventory/mod.rs Normal file
View file

@ -0,0 +1,39 @@
use bevy::prelude::*;
pub mod item;
#[derive(Component)]
pub struct Inventory {
pub size: UVec2,
}
impl Inventory {
pub fn new(size: UVec2) -> Self {
Self { size }
}
pub fn can_fit(
&self,
item_query: Query<&item::Item>,
contained_items: &Children,
queried_size: UVec2,
queried_position: UVec2,
) -> bool {
let item_corner = queried_size + queried_position;
if item_corner.x > self.size.x || item_corner.y > self.size.y {
return false;
}
for entity in contained_items {
let Ok(item) = item_query.get(*entity) else {
warn!("Could not query inventory child ({entity}), probably not item?");
continue;
};
if item.overlaps(queried_size, queried_position) {
return false;
}
}
true
}
}

View file

@ -1,6 +1,7 @@
pub mod player; pub mod player;
pub mod layout; pub mod layout;
pub mod input; pub mod input;
pub mod inventory;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

View file

@ -1,18 +1,18 @@
use super::*; mod input {
use leafwing_input_manager::prelude::*; use super::super::*;
use leafwing_input_manager::prelude::*;
#[derive(Actionlike, Reflect, Clone, Debug, PartialEq, Eq, Hash, Default)] #[derive(Actionlike, Reflect, Clone, Debug, PartialEq, Eq, Hash, Default)]
enum Action { enum Action {
#[default] #[default]
#[actionlike(DualAxis)] #[actionlike(DualAxis)]
DualAxis, DualAxis,
#[actionlike(Axis)] #[actionlike(Axis)]
SingleAxis, SingleAxis,
Button, Button,
} }
#[test] #[test]
fn input_asset_from_map() { fn asset_from_map() {
let mut input_map = InputMap::default(); let mut input_map = InputMap::default();
input_map.insert(Action::Button, KeyCode::KeyE); input_map.insert(Action::Button, KeyCode::KeyE);
input_map.insert(Action::Button, GamepadButton::East); input_map.insert(Action::Button, GamepadButton::East);
@ -41,10 +41,10 @@ fn input_asset_from_map() {
let input_asset = input::InputAsset::from(input_map); let input_asset = input::InputAsset::from(input_map);
assert_eq!(input_asset, expected_input_asset); assert_eq!(input_asset, expected_input_asset);
} }
#[test] #[test]
fn input_map_from_asset() { fn map_from_asset() {
let mut input_asset = input::InputAsset::default(); let mut input_asset = input::InputAsset::default();
input_asset.insert(Action::Button, input::MultiInput { input_asset.insert(Action::Button, input::MultiInput {
keyboard: Some(vec![KeyCode::KeyE]), keyboard: Some(vec![KeyCode::KeyE]),
@ -73,4 +73,117 @@ fn input_map_from_asset() {
let input_map = InputMap::from(input_asset); let input_map = InputMap::from(input_asset);
assert_eq!(input_map, expected_input_map); assert_eq!(input_map, expected_input_map);
}
}
mod inventory {
use bevy::prelude::*;
use crate::inventory::{Inventory, item::Item};
fn inventory() -> Inventory {
Inventory::new(UVec2::splat(4))
}
fn item_a() -> Item {
Item::new_positioned(UVec2::new(1, 2), UVec2::new(0, 0))
}
fn item_b() -> Item {
Item::new_positioned(UVec2::new(2, 2), UVec2::new(0, 2))
}
fn item_c() -> Item {
Item::new_positioned(UVec2::new(3, 2), UVec2::new(1, 0))
}
fn item_d() -> Item {
Item::new_positioned(UVec2::new(2, 2), UVec2::new(2, 2))
}
fn item_e() -> Item {
Item::new_positioned(UVec2::new(1, 1), UVec2::new(0, 0))
}
fn item_f() -> Item {
Item::new_positioned(UVec2::new(5, 5), UVec2::new(0, 0))
}
fn item_g() -> Item {
Item::new_positioned(UVec2::new(2, 2), UVec2::new(3, 3))
}
#[derive(Resource)]
struct Items(Vec<Item>, usize);
fn insert_item(
mut commands: Commands,
mut items: ResMut<Items>,
item_query: Query<&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 {
println!("{q_size} {q_pos}");
assert!(inventory.can_fit(item_query, children, q_size, q_pos));
}
let item_entity = commands.spawn(item.clone()).id();
commands.entity(entity).add_child(item_entity);
items.1 += 1;
}
fn failed_insert_item(
mut commands: Commands,
mut items: ResMut<Items>,
item_query: Query<&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 {
println!("{q_size} {q_pos}");
assert!(!inventory.can_fit(item_query, children, q_size, q_pos));
}
let item_entity = commands.spawn(item.clone()).id();
commands.entity(entity).add_child(item_entity);
items.1 += 1;
}
#[test]
fn everything_fits() {
let mut world = World::new();
let system = world.register_system(insert_item);
world.insert_resource(Items(vec![item_a(), item_b(), item_c(), item_d()], 0));
world.spawn(inventory());
for _ in 0..4 {
world.run_system(system).expect("Error on running system");
}
}
#[test]
fn items_e_f_g_do_not_fit() {
let mut world = World::new();
let system = world.register_system(failed_insert_item);
world.insert_resource(Items(vec![item_e(), item_f(), item_g()], 0));
world.spawn(inventory())
.with_child(item_a())
.with_child(item_b())
.with_child(item_c())
.with_child(item_d());
for _ in 0..3 {
world.run_system(system).expect("Error on running system");
}
}
} }