generated from 2ndbeam/bevy-template
feat: Added Inventory and Item components
- Moved input tests into nested module - Added inventory tests
This commit is contained in:
parent
957717671a
commit
dab3134f15
4 changed files with 260 additions and 72 deletions
35
src/inventory/item.rs
Normal file
35
src/inventory/item.rs
Normal 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
39
src/inventory/mod.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
247
src/tests.rs
247
src/tests.rs
|
|
@ -1,76 +1,189 @@
|
||||||
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)]
|
||||||
|
enum Action {
|
||||||
|
#[default]
|
||||||
|
#[actionlike(DualAxis)]
|
||||||
|
DualAxis,
|
||||||
|
#[actionlike(Axis)]
|
||||||
|
SingleAxis,
|
||||||
|
Button,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Actionlike, Reflect, Clone, Debug, PartialEq, Eq, Hash, Default)]
|
#[test]
|
||||||
enum Action {
|
fn asset_from_map() {
|
||||||
#[default]
|
let mut input_map = InputMap::default();
|
||||||
#[actionlike(DualAxis)]
|
input_map.insert(Action::Button, KeyCode::KeyE);
|
||||||
DualAxis,
|
input_map.insert(Action::Button, GamepadButton::East);
|
||||||
#[actionlike(Axis)]
|
|
||||||
SingleAxis,
|
input_map.insert_axis(Action::SingleAxis, GamepadAxis::LeftStickX);
|
||||||
Button,
|
input_map.insert_axis(Action::SingleAxis, VirtualAxis::ad());
|
||||||
|
|
||||||
|
input_map.insert_dual_axis(Action::DualAxis, GamepadStick::RIGHT);
|
||||||
|
input_map.insert_dual_axis(Action::DualAxis, VirtualDPad::wasd());
|
||||||
|
|
||||||
|
let mut expected_input_asset = input::InputAsset::default();
|
||||||
|
expected_input_asset.insert(Action::Button, input::MultiInput {
|
||||||
|
keyboard: Some(vec![KeyCode::KeyE]),
|
||||||
|
gamepad: Some(vec![GamepadButton::East]),
|
||||||
|
mouse: None,
|
||||||
|
}.into());
|
||||||
|
expected_input_asset.insert(Action::SingleAxis, input::InputKind::Axis(vec![
|
||||||
|
Box::new(GamepadAxis::LeftStickX),
|
||||||
|
Box::new(VirtualAxis::ad()),
|
||||||
|
]));
|
||||||
|
expected_input_asset.insert(Action::DualAxis, input::InputKind::DualAxis(vec![
|
||||||
|
Box::new(GamepadStick::RIGHT),
|
||||||
|
Box::new(VirtualDPad::wasd()),
|
||||||
|
]));
|
||||||
|
|
||||||
|
let input_asset = input::InputAsset::from(input_map);
|
||||||
|
|
||||||
|
assert_eq!(input_asset, expected_input_asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn map_from_asset() {
|
||||||
|
let mut input_asset = input::InputAsset::default();
|
||||||
|
input_asset.insert(Action::Button, input::MultiInput {
|
||||||
|
keyboard: Some(vec![KeyCode::KeyE]),
|
||||||
|
gamepad: Some(vec![GamepadButton::East]),
|
||||||
|
mouse: None,
|
||||||
|
}.into());
|
||||||
|
input_asset.insert(Action::SingleAxis, input::InputKind::Axis(vec![
|
||||||
|
Box::new(GamepadAxis::LeftStickX),
|
||||||
|
Box::new(VirtualAxis::ad()),
|
||||||
|
]));
|
||||||
|
input_asset.insert(Action::DualAxis, input::InputKind::DualAxis(vec![
|
||||||
|
Box::new(GamepadStick::RIGHT),
|
||||||
|
Box::new(VirtualDPad::wasd()),
|
||||||
|
]));
|
||||||
|
|
||||||
|
let mut expected_input_map = InputMap::default();
|
||||||
|
expected_input_map.insert(Action::Button, KeyCode::KeyE);
|
||||||
|
expected_input_map.insert(Action::Button, GamepadButton::East);
|
||||||
|
|
||||||
|
expected_input_map.insert_axis(Action::SingleAxis, GamepadAxis::LeftStickX);
|
||||||
|
expected_input_map.insert_axis(Action::SingleAxis, VirtualAxis::ad());
|
||||||
|
|
||||||
|
expected_input_map.insert_dual_axis(Action::DualAxis, GamepadStick::RIGHT);
|
||||||
|
expected_input_map.insert_dual_axis(Action::DualAxis, VirtualDPad::wasd());
|
||||||
|
|
||||||
|
let input_map = InputMap::from(input_asset);
|
||||||
|
|
||||||
|
assert_eq!(input_map, expected_input_map);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
mod inventory {
|
||||||
fn input_asset_from_map() {
|
use bevy::prelude::*;
|
||||||
let mut input_map = InputMap::default();
|
use crate::inventory::{Inventory, item::Item};
|
||||||
input_map.insert(Action::Button, KeyCode::KeyE);
|
|
||||||
input_map.insert(Action::Button, GamepadButton::East);
|
fn inventory() -> Inventory {
|
||||||
|
Inventory::new(UVec2::splat(4))
|
||||||
|
}
|
||||||
|
|
||||||
input_map.insert_axis(Action::SingleAxis, GamepadAxis::LeftStickX);
|
fn item_a() -> Item {
|
||||||
input_map.insert_axis(Action::SingleAxis, VirtualAxis::ad());
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
input_map.insert_dual_axis(Action::DualAxis, GamepadStick::RIGHT);
|
fn item_e() -> Item {
|
||||||
input_map.insert_dual_axis(Action::DualAxis, VirtualDPad::wasd());
|
Item::new_positioned(UVec2::new(1, 1), UVec2::new(0, 0))
|
||||||
|
}
|
||||||
|
|
||||||
let mut expected_input_asset = input::InputAsset::default();
|
fn item_f() -> Item {
|
||||||
expected_input_asset.insert(Action::Button, input::MultiInput {
|
Item::new_positioned(UVec2::new(5, 5), UVec2::new(0, 0))
|
||||||
keyboard: Some(vec![KeyCode::KeyE]),
|
}
|
||||||
gamepad: Some(vec![GamepadButton::East]),
|
|
||||||
mouse: None,
|
|
||||||
}.into());
|
|
||||||
expected_input_asset.insert(Action::SingleAxis, input::InputKind::Axis(vec![
|
|
||||||
Box::new(GamepadAxis::LeftStickX),
|
|
||||||
Box::new(VirtualAxis::ad()),
|
|
||||||
]));
|
|
||||||
expected_input_asset.insert(Action::DualAxis, input::InputKind::DualAxis(vec![
|
|
||||||
Box::new(GamepadStick::RIGHT),
|
|
||||||
Box::new(VirtualDPad::wasd()),
|
|
||||||
]));
|
|
||||||
|
|
||||||
let input_asset = input::InputAsset::from(input_map);
|
fn item_g() -> Item {
|
||||||
|
Item::new_positioned(UVec2::new(2, 2), UVec2::new(3, 3))
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(input_asset, expected_input_asset);
|
#[derive(Resource)]
|
||||||
}
|
struct Items(Vec<Item>, usize);
|
||||||
|
|
||||||
#[test]
|
fn insert_item(
|
||||||
fn input_map_from_asset() {
|
mut commands: Commands,
|
||||||
let mut input_asset = input::InputAsset::default();
|
mut items: ResMut<Items>,
|
||||||
input_asset.insert(Action::Button, input::MultiInput {
|
item_query: Query<&Item>,
|
||||||
keyboard: Some(vec![KeyCode::KeyE]),
|
inventory_query: Query<(Entity, &Inventory, Option<&Children>)>
|
||||||
gamepad: Some(vec![GamepadButton::East]),
|
) {
|
||||||
mouse: None,
|
let item = &items.0[items.1];
|
||||||
}.into());
|
let q_size = item.size;
|
||||||
input_asset.insert(Action::SingleAxis, input::InputKind::Axis(vec![
|
let q_pos = item.position.unwrap();
|
||||||
Box::new(GamepadAxis::LeftStickX),
|
let (entity, inventory, children) = inventory_query.single().unwrap();
|
||||||
Box::new(VirtualAxis::ad()),
|
if let Some(children) = children {
|
||||||
]));
|
println!("{q_size} {q_pos}");
|
||||||
input_asset.insert(Action::DualAxis, input::InputKind::DualAxis(vec![
|
assert!(inventory.can_fit(item_query, children, q_size, q_pos));
|
||||||
Box::new(GamepadStick::RIGHT),
|
}
|
||||||
Box::new(VirtualDPad::wasd()),
|
let item_entity = commands.spawn(item.clone()).id();
|
||||||
]));
|
commands.entity(entity).add_child(item_entity);
|
||||||
|
items.1 += 1;
|
||||||
let mut expected_input_map = InputMap::default();
|
}
|
||||||
expected_input_map.insert(Action::Button, KeyCode::KeyE);
|
|
||||||
expected_input_map.insert(Action::Button, GamepadButton::East);
|
fn failed_insert_item(
|
||||||
|
mut commands: Commands,
|
||||||
expected_input_map.insert_axis(Action::SingleAxis, GamepadAxis::LeftStickX);
|
mut items: ResMut<Items>,
|
||||||
expected_input_map.insert_axis(Action::SingleAxis, VirtualAxis::ad());
|
item_query: Query<&Item>,
|
||||||
|
inventory_query: Query<(Entity, &Inventory, Option<&Children>)>
|
||||||
expected_input_map.insert_dual_axis(Action::DualAxis, GamepadStick::RIGHT);
|
) {
|
||||||
expected_input_map.insert_dual_axis(Action::DualAxis, VirtualDPad::wasd());
|
let item = &items.0[items.1];
|
||||||
|
let q_size = item.size;
|
||||||
let input_map = InputMap::from(input_asset);
|
let q_pos = item.position.unwrap();
|
||||||
|
let (entity, inventory, children) = inventory_query.single().unwrap();
|
||||||
assert_eq!(input_map, expected_input_map);
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue