generated from 2ndbeam/bevy-template
refactor: Non-breaking refactor
- Everything should work as before - Added blank submodules in layout - Added blank submodules in player - Moved inventory::ui submodule to ui::inventory - Added blank submodules in ui::inventory - Re-exported ui::inventory as inventory::ui for temporary compatibility - Split tests submodules into different files
This commit is contained in:
parent
d65ca6fe97
commit
3094a8af13
14 changed files with 880 additions and 872 deletions
|
|
@ -1,479 +1 @@
|
|||
use std::f32::consts::FRAC_PI_2;
|
||||
|
||||
use bevy::{ecs::relationship::RelatedSpawner, prelude::*, ui_widgets::{ControlOrientation, CoreScrollbarThumb, Scrollbar}};
|
||||
|
||||
use crate::{inventory::{ActiveInventory, Inventory, item::Item}, player::Player, ui::{UiRoot, UiRotateEvent}};
|
||||
|
||||
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, Reflect)]
|
||||
#[require(Node)]
|
||||
pub struct UiInventoryManager;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[require(Node)]
|
||||
pub struct UiInventory(pub Entity);
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[require(Node, ImageNode)]
|
||||
pub struct UiInventorySlot(pub UVec2);
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[require(Node, ImageNode)]
|
||||
pub struct UiItem(pub Entity);
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
pub struct HoveredItem;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
pub struct HoveredSlot;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
pub struct DraggedItem(pub Item, pub UVec2);
|
||||
|
||||
fn ui_item_node_data(item: &Item) -> (Val, Val, Val, Val, UiTransform) {
|
||||
match item.rotated {
|
||||
true => (
|
||||
percent(50. * (item.size.x as f32 - item.size.y as f32)),
|
||||
percent(50. * (item.size.y as f32 - item.size.x as f32)),
|
||||
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(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_ui_node(item: &Item, mut node: Mut<'_, Node>, mut ui_transform: Mut<'_, UiTransform>) {
|
||||
let (left, top, min_width, min_height, new_ui_transform) = ui_item_node_data(item);
|
||||
node.left = left;
|
||||
node.top = top;
|
||||
node.min_width = min_width;
|
||||
node.min_height = min_height;
|
||||
ui_transform.rotation = new_ui_transform.rotation;
|
||||
}
|
||||
|
||||
fn reset_slots_colors(query: Query<&mut ImageNode, With<UiInventorySlot>>) {
|
||||
for mut image in query {
|
||||
image.color = Color::WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
fn on_slot_over(
|
||||
e: On<Pointer<Over>>,
|
||||
mut commands: Commands,
|
||||
mut image_query: Query<&mut ImageNode, With<UiInventorySlot>>,
|
||||
hovered_slots: Query<Entity, With<HoveredSlot>>,
|
||||
has_dragged_item: Option<Single<(), With<DraggedItem>>>,
|
||||
) {
|
||||
if let Ok(mut image) = image_query.get_mut(e.event_target()) {
|
||||
image.color = Color::WHITE.darker(0.3);
|
||||
};
|
||||
for slot_id in hovered_slots {
|
||||
commands.entity(slot_id).remove::<HoveredSlot>();
|
||||
}
|
||||
if has_dragged_item.is_none() {
|
||||
commands.entity(e.event_target()).insert(HoveredSlot);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_slot_out(e: On<Pointer<Out>>, mut query: Query<&mut ImageNode, With<UiInventorySlot>>) {
|
||||
if let Ok(mut image) = query.get_mut(e.event_target()) {
|
||||
image.color = Color::WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_over(
|
||||
e: On<Pointer<Over>>,
|
||||
mut commands: Commands,
|
||||
query: Query<(), With<UiItem>>,
|
||||
has_hovered_item: Option<Single<(), With<HoveredItem>>>,
|
||||
) {
|
||||
if has_hovered_item.is_some() {
|
||||
return;
|
||||
}
|
||||
if let Ok(_) = query.get(e.event_target()) {
|
||||
commands.entity(e.event_target()).insert(HoveredItem);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_out(
|
||||
e: On<Pointer<Out>>,
|
||||
mut commands: Commands,
|
||||
query: Query<(), (With<UiItem>, With<HoveredItem>)>,
|
||||
) {
|
||||
if let Ok(_) = query.get(e.event_target()) {
|
||||
commands.entity(e.event_target()).remove::<HoveredItem>();
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_drag_start(
|
||||
e: On<Pointer<DragStart>>,
|
||||
mut commands: Commands,
|
||||
ui_query: Query<&UiItem, With<HoveredItem>>,
|
||||
item_query: Query<&Item>,
|
||||
hovered_slot: Single<&UiInventorySlot, With<HoveredSlot>>
|
||||
) {
|
||||
if let Ok(ui_item) = ui_query.get(e.event_target()) {
|
||||
let Ok(item) = item_query.get(ui_item.0) else {
|
||||
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;
|
||||
commands.entity(e.event_target()).insert(DraggedItem(item.clone(), diff));
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_drag(e: On<Pointer<Drag>>, mut query: Query<&mut UiTransform, With<UiItem>>) {
|
||||
if let Ok(mut transform) = query.get_mut(e.event_target()) {
|
||||
transform.translation = Val2::px(e.distance.x, e.distance.y);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_drag_end(
|
||||
e: On<Pointer<DragEnd>>,
|
||||
mut commands: Commands,
|
||||
mut query: Query<&mut UiTransform, With<UiItem>>,
|
||||
) {
|
||||
if let Ok(mut transform) = query.get_mut(e.event_target()) {
|
||||
transform.translation = Val2::ZERO;
|
||||
commands.entity(e.event_target()).remove::<DraggedItem>();
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_drag_drop(
|
||||
event: On<Pointer<DragDrop>>,
|
||||
mut commands: Commands,
|
||||
mut ui_item_query: Query<(Entity, &UiItem, &mut DraggedItem, &mut Node, &mut UiTransform)>,
|
||||
ui_inventory_query: Query<&UiInventory>,
|
||||
mut item_query: Query<&mut Item>,
|
||||
slot_query: Query<(&ChildOf, &UiInventorySlot, Option<&Children>)>,
|
||||
slot_id_query: Query<(Entity, &UiInventorySlot, &ChildOf), With<UiInventorySlot>>,
|
||||
inventory_query: Query<(&Inventory, Option<&Children>)>,
|
||||
) {
|
||||
let Ok((ui_item_entity, UiItem(item_entity), mut dragged_item, node, ui_transform)) = ui_item_query.get_mut(event.dropped) else {
|
||||
return;
|
||||
};
|
||||
let Ok((slot_parent, UiInventorySlot(new_position), slot_children)) = slot_query.get(event.event_target()) else {
|
||||
return;
|
||||
};
|
||||
if slot_children.is_some() {
|
||||
return;
|
||||
}
|
||||
let Ok(UiInventory(inventory_id)) = ui_inventory_query.get(slot_parent.0) else {
|
||||
return;
|
||||
};
|
||||
let Ok((inventory, inventory_children)) = inventory_query.get(*inventory_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let items = match inventory_children {
|
||||
Some(children) => &children[..],
|
||||
None => &[],
|
||||
};
|
||||
|
||||
let actual_position = new_position.as_ivec2() - dragged_item.1.as_ivec2();
|
||||
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());
|
||||
}
|
||||
|
||||
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 {
|
||||
return;
|
||||
};
|
||||
|
||||
if inventory.can_replace(item_query.as_readonly(), items, *item_entity, temp_item) {
|
||||
let mut item = item_query.get_mut(*item_entity).unwrap();
|
||||
item.position = temp_item.position;
|
||||
item.size = temp_item.size;
|
||||
item.rotated = temp_item.rotated;
|
||||
commands.entity(ui_item_entity).insert(ChildOf(slot_id));
|
||||
commands.entity(*item_entity).insert(ChildOf(*inventory_id));
|
||||
commands.entity(slot_id).insert(HoveredSlot);
|
||||
update_ui_node(item.as_ref(), node, ui_transform);
|
||||
} else {
|
||||
if let Ok(item) = item_query.get(*item_entity) {
|
||||
update_ui_node(item, node, ui_transform);
|
||||
}
|
||||
}
|
||||
commands.run_system_cached(reset_slots_colors);
|
||||
}
|
||||
|
||||
pub fn on_ui_rotate(
|
||||
_: On<UiRotateEvent>,
|
||||
mut item_query: Query<&mut Item>,
|
||||
parent_query: Query<&ChildOf>,
|
||||
inventory_query: Query<(&Inventory, Option<&Children>)>,
|
||||
ui_item_query: Query<(&UiItem, &mut UiTransform, &mut Node, Option<&mut DraggedItem>), With<HoveredItem>>,
|
||||
) {
|
||||
for (ui_item, ui_transform, node, maybe_dragged) in ui_item_query {
|
||||
let Ok(item_parent) = parent_query.get(ui_item.0) else {
|
||||
continue;
|
||||
};
|
||||
let Ok((inventory, children)) = inventory_query.get(item_parent.0) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let children = match children {
|
||||
Some(children) => &children[..],
|
||||
None => &[],
|
||||
};
|
||||
let (was_rotated, item) = match maybe_dragged {
|
||||
Some(mut temp_item) => {
|
||||
temp_item.0.rotate();
|
||||
temp_item.1 = uvec2(temp_item.1.y, temp_item.1.x);
|
||||
(true, Some(temp_item.0.clone()))
|
||||
},
|
||||
None => {
|
||||
let result = inventory.can_rotate(item_query.as_readonly(), children, ui_item.0);
|
||||
let mut out_item = None;
|
||||
if result {
|
||||
if let Ok(mut item) = item_query.get_mut(ui_item.0) {
|
||||
item.rotate();
|
||||
out_item = Some(item.clone());
|
||||
}
|
||||
}
|
||||
(result, out_item)
|
||||
},
|
||||
};
|
||||
if was_rotated {
|
||||
update_ui_node(&item.unwrap(), node, ui_transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ui_manager_bundle(children: Vec<Entity>, aligned_left: bool) -> impl Bundle {
|
||||
let left = if aligned_left { Val::ZERO } else { percent(50.) };
|
||||
(
|
||||
UiInventoryManager,
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
left,
|
||||
width: percent(50.),
|
||||
height: percent(100.),
|
||||
scrollbar_width: 8.,
|
||||
display: Display::Grid,
|
||||
grid_template_columns: vec![RepeatedGridTrack::flex(1, 1.), RepeatedGridTrack::auto(1)],
|
||||
grid_template_rows: vec![RepeatedGridTrack::flex(1, 1.), RepeatedGridTrack::auto(1)],
|
||||
..default()
|
||||
},
|
||||
Pickable::IGNORE,
|
||||
GlobalZIndex::default(),
|
||||
Children::spawn(SpawnWith(move |parent: &mut RelatedSpawner<ChildOf>| {
|
||||
let scroll_area_id = parent.spawn((
|
||||
Node {
|
||||
width: percent(100.),
|
||||
height: percent(100.),
|
||||
display: Display::Flex,
|
||||
flex_direction: FlexDirection::Column,
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::SpaceAround,
|
||||
overflow: Overflow::scroll_y(),
|
||||
..default()
|
||||
},
|
||||
ScrollPosition(Vec2::ZERO),
|
||||
)).add_children(children.as_slice()).id();
|
||||
parent.spawn((
|
||||
Node {
|
||||
min_width: px(8),
|
||||
grid_row: GridPlacement::start(1),
|
||||
grid_column: GridPlacement::start(2),
|
||||
..default()
|
||||
},
|
||||
Scrollbar {
|
||||
orientation: ControlOrientation::Vertical,
|
||||
target: scroll_area_id,
|
||||
min_thumb_length: 8.0,
|
||||
},
|
||||
BackgroundColor(Color::hsl(0., 0., 0.5)),
|
||||
children![(
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
border_radius: BorderRadius::all(px(4.)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::hsl(0., 0., 0.3)),
|
||||
CoreScrollbarThumb,
|
||||
)],
|
||||
));
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
fn ui_inventory_bundle(
|
||||
inventory: &Inventory,
|
||||
inventory_entity: Entity,
|
||||
) -> impl Bundle {
|
||||
(
|
||||
UiInventory(inventory_entity),
|
||||
Node {
|
||||
align_self: AlignSelf::Stretch,
|
||||
align_content: AlignContent::Center,
|
||||
display: Display::Grid,
|
||||
width: percent(100.),
|
||||
aspect_ratio: Some(inventory.size.x as f32 / inventory.size.y as f32),
|
||||
grid_auto_columns: vec![GridTrack::percent(100. / inventory.size.x as f32)],
|
||||
grid_auto_rows: vec![GridTrack::percent(100. / inventory.size.y as f32)],
|
||||
..default()
|
||||
},
|
||||
Pickable::IGNORE,
|
||||
GlobalZIndex::default(),
|
||||
Name::new(format!("UiInventory ({}x{})", inventory.size.x, inventory.size.y)),
|
||||
)
|
||||
}
|
||||
|
||||
fn inventory_slot_bundle(x: u32, y: u32, image: Handle<Image>) -> impl Bundle {
|
||||
(
|
||||
UiInventorySlot(UVec2::new(x, y)),
|
||||
ImageNode {
|
||||
color: Color::WHITE,
|
||||
image,
|
||||
image_mode: NodeImageMode::Stretch,
|
||||
..default()
|
||||
},
|
||||
Node {
|
||||
width: percent(100.),
|
||||
height: percent(100.),
|
||||
grid_column: GridPlacement::start(x as i16 + 1),
|
||||
grid_row: GridPlacement::start(y as i16 + 1),
|
||||
aspect_ratio: Some(1.),
|
||||
..default()
|
||||
},
|
||||
Pickable {
|
||||
should_block_lower: true,
|
||||
is_hoverable: true,
|
||||
},
|
||||
GlobalZIndex(1),
|
||||
BackgroundColor::DEFAULT,
|
||||
Name::new(format!("UiInventorySlot({x},{y})")),
|
||||
)
|
||||
}
|
||||
|
||||
fn ui_item_bundle(item: &Item, item_entity: Entity, image: Handle<Image>) -> impl Bundle {
|
||||
let (left, top, min_width, min_height, ui_transform) = ui_item_node_data(item);
|
||||
(
|
||||
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(2),
|
||||
Pickable {
|
||||
should_block_lower: false,
|
||||
is_hoverable: true,
|
||||
},
|
||||
Name::new(format!("UiItem ({},{})",
|
||||
item.position.unwrap_or_default().x,
|
||||
item.position.unwrap_or_default().y,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn setup_ui_inventory(
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
inventory_query: Query<(Option<&ChildOf>, Entity, &Inventory, Option<&Children>)>,
|
||||
player_query: Query<(), With<Player>>,
|
||||
active_inventory_query: Query<Entity, With<ActiveInventory>>,
|
||||
item_query: Query<&Item>,
|
||||
root_query: Query<Entity, With<UiRoot>>,
|
||||
) {
|
||||
let Ok(root) = root_query.single() else {
|
||||
error!("Query contains more than one UiRoot");
|
||||
return;
|
||||
};
|
||||
let ui_slot_image: Handle<Image> = asset_server.load(UI_SLOT_ASSET_PATH);
|
||||
let temp_item_image: Handle<Image> = 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::<Entity>() {
|
||||
let is_player = match inventory_parent {
|
||||
Some(parent) => {
|
||||
if player_query.get(parent.0)
|
||||
.is_ok() { true }
|
||||
else {
|
||||
if active_inventory_query.get(inventory_entity)
|
||||
.is_ok() { false }
|
||||
else { continue; }
|
||||
}
|
||||
},
|
||||
None => {
|
||||
if active_inventory_query.get(inventory_entity)
|
||||
.is_ok() { false }
|
||||
else { continue; }
|
||||
},
|
||||
};
|
||||
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(_) => None,
|
||||
}
|
||||
}).collect::<Vec<(&Item, Entity)>>()
|
||||
}
|
||||
None => Vec::new(),
|
||||
};
|
||||
let inventory_id = commands.spawn(ui_inventory_bundle(inventory, inventory_entity))
|
||||
.with_children(|commands| {
|
||||
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()));
|
||||
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)) {
|
||||
slot_commands.with_children(|commands| {
|
||||
commands.spawn(ui_item_bundle(item, *entity, temp_item_image.clone()))
|
||||
.observe(on_item_over)
|
||||
.observe(on_item_out)
|
||||
.observe(on_item_drag_start)
|
||||
.observe(on_item_drag)
|
||||
.observe(on_item_drag_end);
|
||||
});
|
||||
}
|
||||
} }
|
||||
}).id();
|
||||
if is_player {
|
||||
player_inventory_ids.push(inventory_id);
|
||||
} else {
|
||||
active_inventory_ids.push(inventory_id);
|
||||
}
|
||||
}
|
||||
let player_inventory_manager = commands.spawn(ui_manager_bundle(player_inventory_ids, true)).id();
|
||||
let active_inventory_manager = commands.spawn(ui_manager_bundle(active_inventory_ids, false)).id();
|
||||
for entity in active_inventory_query {
|
||||
commands.entity(entity).remove::<ActiveInventory>();
|
||||
}
|
||||
commands.entity(root).add_children(&[player_inventory_manager, active_inventory_manager]);
|
||||
}
|
||||
|
||||
pub fn clear_ui_inventory(
|
||||
mut commands: Commands,
|
||||
inventory_query: Query<Entity, With<UiInventoryManager>>,
|
||||
) {
|
||||
for entity in inventory_query {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
pub use crate::ui::inventory::*;
|
||||
|
|
|
|||
0
src/layout/container.rs
Normal file
0
src/layout/container.rs
Normal file
0
src/layout/door.rs
Normal file
0
src/layout/door.rs
Normal file
|
|
@ -3,6 +3,10 @@ use bevy_rapier2d::prelude::*;
|
|||
|
||||
use crate::{GameState, PIXELS_PER_METER, inventory::{ActiveInventory, Inventory, item::Item}, player::Player};
|
||||
|
||||
pub mod container;
|
||||
pub mod door;
|
||||
pub mod systems;
|
||||
|
||||
const DOOR_OPENED_ASSET: &'static str = "sprites/interactive/door_opened.png";
|
||||
const DOOR_CLOSED_ASSET: &'static str = "sprites/interactive/door_closed.png";
|
||||
const CRATE_CLOSED_ASSET: &'static str = "sprites/interactive/crate_closed.png";
|
||||
0
src/layout/systems.rs
Normal file
0
src/layout/systems.rs
Normal file
|
|
@ -4,6 +4,8 @@ use leafwing_input_manager::prelude::*;
|
|||
|
||||
use crate::{GameState, InputAction as Action, PIXELS_PER_METER, inventory::{Inventory, item::Item}, layout::{InteractionEvent, MayInteract}};
|
||||
|
||||
pub mod systems;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
pub struct Player {
|
||||
// px/s
|
||||
0
src/player/systems.rs
Normal file
0
src/player/systems.rs
Normal file
393
src/tests.rs
393
src/tests.rs
|
|
@ -1,393 +0,0 @@
|
|||
mod input {
|
||||
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,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_from_map() {
|
||||
let mut input_map = InputMap::default();
|
||||
input_map.insert(Action::Button, KeyCode::KeyE);
|
||||
input_map.insert(Action::Button, GamepadButton::East);
|
||||
|
||||
input_map.insert_axis(Action::SingleAxis, GamepadAxis::LeftStickX);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
mod inventory {
|
||||
use bevy::prelude::*;
|
||||
use crate::inventory::{Inventory, item::Item, ui::DraggedItem};
|
||||
|
||||
fn inventory() -> Inventory {
|
||||
Inventory::new(UVec2::splat(4))
|
||||
}
|
||||
|
||||
/// 1x2 0;0
|
||||
fn item_a() -> Item {
|
||||
Item::new_positioned(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))
|
||||
}
|
||||
|
||||
/// 3x2 1;0
|
||||
fn item_c() -> Item {
|
||||
Item::new_positioned(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))
|
||||
}
|
||||
|
||||
/// 1x1 0;0
|
||||
fn item_e() -> Item {
|
||||
Item::new_positioned(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))
|
||||
}
|
||||
|
||||
/// 2x2 3;3
|
||||
fn item_g() -> Item {
|
||||
Item::new_positioned(UVec2::new(2, 2), UVec2::new(3, 3))
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct Items(Vec<Item>, usize, bool);
|
||||
|
||||
#[derive(Component, Resource)]
|
||||
struct MovableItem(UVec2, bool);
|
||||
|
||||
#[derive(Component)]
|
||||
struct RotatingMarker(bool);
|
||||
|
||||
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 {
|
||||
if items.2 {
|
||||
assert!(inventory.can_fit(item_query, children, q_size, q_pos));
|
||||
} else {
|
||||
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 try_to_move(
|
||||
item_query: Query<&Item>,
|
||||
inventory_query: Query<(&Inventory, &Children)>,
|
||||
movable_item: Query<(Entity, &MovableItem)>,
|
||||
) {
|
||||
let (inventory, children) = inventory_query.single().unwrap();
|
||||
let (movable_item, MovableItem(query_pos, assertion)) = movable_item.single().unwrap();
|
||||
if *assertion {
|
||||
assert!(inventory.can_move(item_query, children, movable_item, *query_pos));
|
||||
} else {
|
||||
assert!(!inventory.can_move(item_query, children, movable_item, *query_pos));
|
||||
}
|
||||
}
|
||||
|
||||
fn find_space(
|
||||
item_query: Query<&Item>,
|
||||
inventory_query: Query<(&Inventory, &Children)>,
|
||||
movable_item: Res<MovableItem>,
|
||||
) {
|
||||
let (inventory, children) = inventory_query.single().unwrap();
|
||||
let MovableItem(query_size, assertion) = *movable_item;
|
||||
if assertion {
|
||||
assert!(inventory.find_free_space(item_query, children, query_size).is_some());
|
||||
} else {
|
||||
assert!(inventory.find_free_space(item_query, children, query_size).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
fn rotate_in_place(
|
||||
item_query: Query<&Item>,
|
||||
rotated_item: Query<(Entity, &RotatingMarker)>,
|
||||
inventory_query: Query<(&Inventory, &Children)>,
|
||||
) {
|
||||
let (inventory, children) = inventory_query.single().unwrap();
|
||||
let (rotatable_id, RotatingMarker(assertion)) = rotated_item.single().unwrap();
|
||||
if *assertion {
|
||||
assert!(inventory.can_rotate(item_query, children, rotatable_id));
|
||||
} else {
|
||||
assert!(!inventory.can_rotate(item_query, children, rotatable_id));
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_item(
|
||||
item_query: Query<&Item>,
|
||||
replacable_item: Query<(Entity, &DraggedItem, &RotatingMarker)>,
|
||||
inventory_query: Query<(&Inventory, &Children)>,
|
||||
) {
|
||||
let (inventory, children) = inventory_query.single().unwrap();
|
||||
let (replacable_id, DraggedItem(replacing_item, _), RotatingMarker(assertion)) = replacable_item.single()
|
||||
.unwrap();
|
||||
if *assertion {
|
||||
assert!(inventory.can_replace(item_query, children, replacable_id, replacing_item));
|
||||
} else {
|
||||
assert!(!inventory.can_replace(item_query, children, replacable_id, replacing_item));
|
||||
}
|
||||
}
|
||||
|
||||
#[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, true));
|
||||
|
||||
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(insert_item);
|
||||
|
||||
world.insert_resource(Items(vec![item_e(), item_f(), item_g()], 0, false));
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_move_item() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(try_to_move);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), MovableItem(UVec2::new(3, 2), true)))
|
||||
.with_child(item_b())
|
||||
.with_child(item_c());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_move_item() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(try_to_move);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), MovableItem(UVec2::new(3, 2), false)))
|
||||
.with_child(item_b())
|
||||
.with_child(item_c())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_item_slightly() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(try_to_move);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), MovableItem(UVec2::new(0, 1), true)))
|
||||
.with_child(item_c())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_find_space_for_item() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(find_space);
|
||||
|
||||
world.insert_resource(MovableItem(UVec2::new(2, 2), true));
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child(item_a())
|
||||
.with_child(item_b())
|
||||
.with_child(item_c());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_find_space_for_item() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(find_space);
|
||||
|
||||
world.insert_resource(MovableItem(UVec2::new(2, 2), false));
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child(item_a())
|
||||
.with_child(item_b())
|
||||
.with_child(item_c())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn item_fits_if_rotated() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(find_space);
|
||||
|
||||
world.insert_resource(MovableItem(UVec2::new(2, 3), true));
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child(item_a())
|
||||
.with_child(item_b())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rotate_item_in_place() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(rotate_in_place);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), RotatingMarker(true)))
|
||||
.with_child(item_b())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_rotate_item_in_place() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(rotate_in_place);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), RotatingMarker(false)))
|
||||
.with_child(item_b())
|
||||
.with_child(item_c())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_replace_item() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(replace_item);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), DraggedItem(item_c(), UVec2::ZERO), RotatingMarker(true)))
|
||||
.with_child(item_b())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_replace_item() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(replace_item);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), DraggedItem(item_c(), UVec2::ZERO), RotatingMarker(false)))
|
||||
.with_child(item_b())
|
||||
.with_child(item_c())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
}
|
||||
75
src/tests/input.rs
Normal file
75
src/tests/input.rs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
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,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_from_map() {
|
||||
let mut input_map = InputMap::default();
|
||||
input_map.insert(Action::Button, KeyCode::KeyE);
|
||||
input_map.insert(Action::Button, GamepadButton::East);
|
||||
|
||||
input_map.insert_axis(Action::SingleAxis, GamepadAxis::LeftStickX);
|
||||
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);
|
||||
}
|
||||
313
src/tests/inventory.rs
Normal file
313
src/tests/inventory.rs
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
use bevy::prelude::*;
|
||||
use crate::inventory::{Inventory, item::Item, ui::DraggedItem};
|
||||
|
||||
fn inventory() -> Inventory {
|
||||
Inventory::new(UVec2::splat(4))
|
||||
}
|
||||
|
||||
/// 1x2 0;0
|
||||
fn item_a() -> Item {
|
||||
Item::new_positioned(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))
|
||||
}
|
||||
|
||||
/// 3x2 1;0
|
||||
fn item_c() -> Item {
|
||||
Item::new_positioned(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))
|
||||
}
|
||||
|
||||
/// 1x1 0;0
|
||||
fn item_e() -> Item {
|
||||
Item::new_positioned(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))
|
||||
}
|
||||
|
||||
/// 2x2 3;3
|
||||
fn item_g() -> Item {
|
||||
Item::new_positioned(UVec2::new(2, 2), UVec2::new(3, 3))
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct Items(Vec<Item>, usize, bool);
|
||||
|
||||
#[derive(Component, Resource)]
|
||||
struct MovableItem(UVec2, bool);
|
||||
|
||||
#[derive(Component)]
|
||||
struct RotatingMarker(bool);
|
||||
|
||||
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 {
|
||||
if items.2 {
|
||||
assert!(inventory.can_fit(item_query, children, q_size, q_pos));
|
||||
} else {
|
||||
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 try_to_move(
|
||||
item_query: Query<&Item>,
|
||||
inventory_query: Query<(&Inventory, &Children)>,
|
||||
movable_item: Query<(Entity, &MovableItem)>,
|
||||
) {
|
||||
let (inventory, children) = inventory_query.single().unwrap();
|
||||
let (movable_item, MovableItem(query_pos, assertion)) = movable_item.single().unwrap();
|
||||
if *assertion {
|
||||
assert!(inventory.can_move(item_query, children, movable_item, *query_pos));
|
||||
} else {
|
||||
assert!(!inventory.can_move(item_query, children, movable_item, *query_pos));
|
||||
}
|
||||
}
|
||||
|
||||
fn find_space(
|
||||
item_query: Query<&Item>,
|
||||
inventory_query: Query<(&Inventory, &Children)>,
|
||||
movable_item: Res<MovableItem>,
|
||||
) {
|
||||
let (inventory, children) = inventory_query.single().unwrap();
|
||||
let MovableItem(query_size, assertion) = *movable_item;
|
||||
if assertion {
|
||||
assert!(inventory.find_free_space(item_query, children, query_size).is_some());
|
||||
} else {
|
||||
assert!(inventory.find_free_space(item_query, children, query_size).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
fn rotate_in_place(
|
||||
item_query: Query<&Item>,
|
||||
rotated_item: Query<(Entity, &RotatingMarker)>,
|
||||
inventory_query: Query<(&Inventory, &Children)>,
|
||||
) {
|
||||
let (inventory, children) = inventory_query.single().unwrap();
|
||||
let (rotatable_id, RotatingMarker(assertion)) = rotated_item.single().unwrap();
|
||||
if *assertion {
|
||||
assert!(inventory.can_rotate(item_query, children, rotatable_id));
|
||||
} else {
|
||||
assert!(!inventory.can_rotate(item_query, children, rotatable_id));
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_item(
|
||||
item_query: Query<&Item>,
|
||||
replacable_item: Query<(Entity, &DraggedItem, &RotatingMarker)>,
|
||||
inventory_query: Query<(&Inventory, &Children)>,
|
||||
) {
|
||||
let (inventory, children) = inventory_query.single().unwrap();
|
||||
let (replacable_id, DraggedItem(replacing_item, _), RotatingMarker(assertion)) = replacable_item.single()
|
||||
.unwrap();
|
||||
if *assertion {
|
||||
assert!(inventory.can_replace(item_query, children, replacable_id, replacing_item));
|
||||
} else {
|
||||
assert!(!inventory.can_replace(item_query, children, replacable_id, replacing_item));
|
||||
}
|
||||
}
|
||||
|
||||
#[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, true));
|
||||
|
||||
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(insert_item);
|
||||
|
||||
world.insert_resource(Items(vec![item_e(), item_f(), item_g()], 0, false));
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_move_item() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(try_to_move);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), MovableItem(UVec2::new(3, 2), true)))
|
||||
.with_child(item_b())
|
||||
.with_child(item_c());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_move_item() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(try_to_move);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), MovableItem(UVec2::new(3, 2), false)))
|
||||
.with_child(item_b())
|
||||
.with_child(item_c())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn move_item_slightly() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(try_to_move);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), MovableItem(UVec2::new(0, 1), true)))
|
||||
.with_child(item_c())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_find_space_for_item() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(find_space);
|
||||
|
||||
world.insert_resource(MovableItem(UVec2::new(2, 2), true));
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child(item_a())
|
||||
.with_child(item_b())
|
||||
.with_child(item_c());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_find_space_for_item() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(find_space);
|
||||
|
||||
world.insert_resource(MovableItem(UVec2::new(2, 2), false));
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child(item_a())
|
||||
.with_child(item_b())
|
||||
.with_child(item_c())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn item_fits_if_rotated() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(find_space);
|
||||
|
||||
world.insert_resource(MovableItem(UVec2::new(2, 3), true));
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child(item_a())
|
||||
.with_child(item_b())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rotate_item_in_place() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(rotate_in_place);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), RotatingMarker(true)))
|
||||
.with_child(item_b())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn failed_rotate_item_in_place() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(rotate_in_place);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), RotatingMarker(false)))
|
||||
.with_child(item_b())
|
||||
.with_child(item_c())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_replace_item() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(replace_item);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), DraggedItem(item_c(), UVec2::ZERO), RotatingMarker(true)))
|
||||
.with_child(item_b())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_replace_item() {
|
||||
let mut world = World::new();
|
||||
|
||||
let system = world.register_system(replace_item);
|
||||
|
||||
world.spawn(inventory())
|
||||
.with_child((item_a(), DraggedItem(item_c(), UVec2::ZERO), RotatingMarker(false)))
|
||||
.with_child(item_b())
|
||||
.with_child(item_c())
|
||||
.with_child(item_d());
|
||||
|
||||
world.run_system(system).expect("Error on running system");
|
||||
}
|
||||
2
src/tests/mod.rs
Normal file
2
src/tests/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
mod input;
|
||||
mod inventory;
|
||||
481
src/ui/inventory/mod.rs
Normal file
481
src/ui/inventory/mod.rs
Normal file
|
|
@ -0,0 +1,481 @@
|
|||
use std::f32::consts::FRAC_PI_2;
|
||||
|
||||
use bevy::{ecs::relationship::RelatedSpawner, prelude::*, ui_widgets::{ControlOrientation, CoreScrollbarThumb, Scrollbar}};
|
||||
|
||||
use crate::{inventory::{ActiveInventory, Inventory, item::Item}, player::Player, ui::{UiRoot, UiRotateEvent}};
|
||||
|
||||
pub mod systems;
|
||||
|
||||
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, Reflect)]
|
||||
#[require(Node)]
|
||||
pub struct UiInventoryManager;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[require(Node)]
|
||||
pub struct UiInventory(pub Entity);
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[require(Node, ImageNode)]
|
||||
pub struct UiInventorySlot(pub UVec2);
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[require(Node, ImageNode)]
|
||||
pub struct UiItem(pub Entity);
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
pub struct HoveredItem;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
pub struct HoveredSlot;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
pub struct DraggedItem(pub Item, pub UVec2);
|
||||
|
||||
fn ui_item_node_data(item: &Item) -> (Val, Val, Val, Val, UiTransform) {
|
||||
match item.rotated {
|
||||
true => (
|
||||
percent(50. * (item.size.x as f32 - item.size.y as f32)),
|
||||
percent(50. * (item.size.y as f32 - item.size.x as f32)),
|
||||
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(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_ui_node(item: &Item, mut node: Mut<'_, Node>, mut ui_transform: Mut<'_, UiTransform>) {
|
||||
let (left, top, min_width, min_height, new_ui_transform) = ui_item_node_data(item);
|
||||
node.left = left;
|
||||
node.top = top;
|
||||
node.min_width = min_width;
|
||||
node.min_height = min_height;
|
||||
ui_transform.rotation = new_ui_transform.rotation;
|
||||
}
|
||||
|
||||
fn reset_slots_colors(query: Query<&mut ImageNode, With<UiInventorySlot>>) {
|
||||
for mut image in query {
|
||||
image.color = Color::WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
fn on_slot_over(
|
||||
e: On<Pointer<Over>>,
|
||||
mut commands: Commands,
|
||||
mut image_query: Query<&mut ImageNode, With<UiInventorySlot>>,
|
||||
hovered_slots: Query<Entity, With<HoveredSlot>>,
|
||||
has_dragged_item: Option<Single<(), With<DraggedItem>>>,
|
||||
) {
|
||||
if let Ok(mut image) = image_query.get_mut(e.event_target()) {
|
||||
image.color = Color::WHITE.darker(0.3);
|
||||
};
|
||||
for slot_id in hovered_slots {
|
||||
commands.entity(slot_id).remove::<HoveredSlot>();
|
||||
}
|
||||
if has_dragged_item.is_none() {
|
||||
commands.entity(e.event_target()).insert(HoveredSlot);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_slot_out(e: On<Pointer<Out>>, mut query: Query<&mut ImageNode, With<UiInventorySlot>>) {
|
||||
if let Ok(mut image) = query.get_mut(e.event_target()) {
|
||||
image.color = Color::WHITE;
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_over(
|
||||
e: On<Pointer<Over>>,
|
||||
mut commands: Commands,
|
||||
query: Query<(), With<UiItem>>,
|
||||
has_hovered_item: Option<Single<(), With<HoveredItem>>>,
|
||||
) {
|
||||
if has_hovered_item.is_some() {
|
||||
return;
|
||||
}
|
||||
if let Ok(_) = query.get(e.event_target()) {
|
||||
commands.entity(e.event_target()).insert(HoveredItem);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_out(
|
||||
e: On<Pointer<Out>>,
|
||||
mut commands: Commands,
|
||||
query: Query<(), (With<UiItem>, With<HoveredItem>)>,
|
||||
) {
|
||||
if let Ok(_) = query.get(e.event_target()) {
|
||||
commands.entity(e.event_target()).remove::<HoveredItem>();
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_drag_start(
|
||||
e: On<Pointer<DragStart>>,
|
||||
mut commands: Commands,
|
||||
ui_query: Query<&UiItem, With<HoveredItem>>,
|
||||
item_query: Query<&Item>,
|
||||
hovered_slot: Single<&UiInventorySlot, With<HoveredSlot>>
|
||||
) {
|
||||
if let Ok(ui_item) = ui_query.get(e.event_target()) {
|
||||
let Ok(item) = item_query.get(ui_item.0) else {
|
||||
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;
|
||||
commands.entity(e.event_target()).insert(DraggedItem(item.clone(), diff));
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_drag(e: On<Pointer<Drag>>, mut query: Query<&mut UiTransform, With<UiItem>>) {
|
||||
if let Ok(mut transform) = query.get_mut(e.event_target()) {
|
||||
transform.translation = Val2::px(e.distance.x, e.distance.y);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_drag_end(
|
||||
e: On<Pointer<DragEnd>>,
|
||||
mut commands: Commands,
|
||||
mut query: Query<&mut UiTransform, With<UiItem>>,
|
||||
) {
|
||||
if let Ok(mut transform) = query.get_mut(e.event_target()) {
|
||||
transform.translation = Val2::ZERO;
|
||||
commands.entity(e.event_target()).remove::<DraggedItem>();
|
||||
}
|
||||
}
|
||||
|
||||
fn on_item_drag_drop(
|
||||
event: On<Pointer<DragDrop>>,
|
||||
mut commands: Commands,
|
||||
mut ui_item_query: Query<(Entity, &UiItem, &mut DraggedItem, &mut Node, &mut UiTransform)>,
|
||||
ui_inventory_query: Query<&UiInventory>,
|
||||
mut item_query: Query<&mut Item>,
|
||||
slot_query: Query<(&ChildOf, &UiInventorySlot, Option<&Children>)>,
|
||||
slot_id_query: Query<(Entity, &UiInventorySlot, &ChildOf), With<UiInventorySlot>>,
|
||||
inventory_query: Query<(&Inventory, Option<&Children>)>,
|
||||
) {
|
||||
let Ok((ui_item_entity, UiItem(item_entity), mut dragged_item, node, ui_transform)) = ui_item_query.get_mut(event.dropped) else {
|
||||
return;
|
||||
};
|
||||
let Ok((slot_parent, UiInventorySlot(new_position), slot_children)) = slot_query.get(event.event_target()) else {
|
||||
return;
|
||||
};
|
||||
if slot_children.is_some() {
|
||||
return;
|
||||
}
|
||||
let Ok(UiInventory(inventory_id)) = ui_inventory_query.get(slot_parent.0) else {
|
||||
return;
|
||||
};
|
||||
let Ok((inventory, inventory_children)) = inventory_query.get(*inventory_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let items = match inventory_children {
|
||||
Some(children) => &children[..],
|
||||
None => &[],
|
||||
};
|
||||
|
||||
let actual_position = new_position.as_ivec2() - dragged_item.1.as_ivec2();
|
||||
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());
|
||||
}
|
||||
|
||||
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 {
|
||||
return;
|
||||
};
|
||||
|
||||
if inventory.can_replace(item_query.as_readonly(), items, *item_entity, temp_item) {
|
||||
let mut item = item_query.get_mut(*item_entity).unwrap();
|
||||
item.position = temp_item.position;
|
||||
item.size = temp_item.size;
|
||||
item.rotated = temp_item.rotated;
|
||||
commands.entity(ui_item_entity).insert(ChildOf(slot_id));
|
||||
commands.entity(*item_entity).insert(ChildOf(*inventory_id));
|
||||
commands.entity(slot_id).insert(HoveredSlot);
|
||||
update_ui_node(item.as_ref(), node, ui_transform);
|
||||
} else {
|
||||
if let Ok(item) = item_query.get(*item_entity) {
|
||||
update_ui_node(item, node, ui_transform);
|
||||
}
|
||||
}
|
||||
commands.run_system_cached(reset_slots_colors);
|
||||
}
|
||||
|
||||
pub fn on_ui_rotate(
|
||||
_: On<UiRotateEvent>,
|
||||
mut item_query: Query<&mut Item>,
|
||||
parent_query: Query<&ChildOf>,
|
||||
inventory_query: Query<(&Inventory, Option<&Children>)>,
|
||||
ui_item_query: Query<(&UiItem, &mut UiTransform, &mut Node, Option<&mut DraggedItem>), With<HoveredItem>>,
|
||||
) {
|
||||
for (ui_item, ui_transform, node, maybe_dragged) in ui_item_query {
|
||||
let Ok(item_parent) = parent_query.get(ui_item.0) else {
|
||||
continue;
|
||||
};
|
||||
let Ok((inventory, children)) = inventory_query.get(item_parent.0) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let children = match children {
|
||||
Some(children) => &children[..],
|
||||
None => &[],
|
||||
};
|
||||
let (was_rotated, item) = match maybe_dragged {
|
||||
Some(mut temp_item) => {
|
||||
temp_item.0.rotate();
|
||||
temp_item.1 = uvec2(temp_item.1.y, temp_item.1.x);
|
||||
(true, Some(temp_item.0.clone()))
|
||||
},
|
||||
None => {
|
||||
let result = inventory.can_rotate(item_query.as_readonly(), children, ui_item.0);
|
||||
let mut out_item = None;
|
||||
if result {
|
||||
if let Ok(mut item) = item_query.get_mut(ui_item.0) {
|
||||
item.rotate();
|
||||
out_item = Some(item.clone());
|
||||
}
|
||||
}
|
||||
(result, out_item)
|
||||
},
|
||||
};
|
||||
if was_rotated {
|
||||
update_ui_node(&item.unwrap(), node, ui_transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ui_manager_bundle(children: Vec<Entity>, aligned_left: bool) -> impl Bundle {
|
||||
let left = if aligned_left { Val::ZERO } else { percent(50.) };
|
||||
(
|
||||
UiInventoryManager,
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
left,
|
||||
width: percent(50.),
|
||||
height: percent(100.),
|
||||
scrollbar_width: 8.,
|
||||
display: Display::Grid,
|
||||
grid_template_columns: vec![RepeatedGridTrack::flex(1, 1.), RepeatedGridTrack::auto(1)],
|
||||
grid_template_rows: vec![RepeatedGridTrack::flex(1, 1.), RepeatedGridTrack::auto(1)],
|
||||
..default()
|
||||
},
|
||||
Pickable::IGNORE,
|
||||
GlobalZIndex::default(),
|
||||
Children::spawn(SpawnWith(move |parent: &mut RelatedSpawner<ChildOf>| {
|
||||
let scroll_area_id = parent.spawn((
|
||||
Node {
|
||||
width: percent(100.),
|
||||
height: percent(100.),
|
||||
display: Display::Flex,
|
||||
flex_direction: FlexDirection::Column,
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::SpaceAround,
|
||||
overflow: Overflow::scroll_y(),
|
||||
..default()
|
||||
},
|
||||
ScrollPosition(Vec2::ZERO),
|
||||
)).add_children(children.as_slice()).id();
|
||||
parent.spawn((
|
||||
Node {
|
||||
min_width: px(8),
|
||||
grid_row: GridPlacement::start(1),
|
||||
grid_column: GridPlacement::start(2),
|
||||
..default()
|
||||
},
|
||||
Scrollbar {
|
||||
orientation: ControlOrientation::Vertical,
|
||||
target: scroll_area_id,
|
||||
min_thumb_length: 8.0,
|
||||
},
|
||||
BackgroundColor(Color::hsl(0., 0., 0.5)),
|
||||
children![(
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
border_radius: BorderRadius::all(px(4.)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(Color::hsl(0., 0., 0.3)),
|
||||
CoreScrollbarThumb,
|
||||
)],
|
||||
));
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
fn ui_inventory_bundle(
|
||||
inventory: &Inventory,
|
||||
inventory_entity: Entity,
|
||||
) -> impl Bundle {
|
||||
(
|
||||
UiInventory(inventory_entity),
|
||||
Node {
|
||||
align_self: AlignSelf::Stretch,
|
||||
align_content: AlignContent::Center,
|
||||
display: Display::Grid,
|
||||
width: percent(100.),
|
||||
aspect_ratio: Some(inventory.size.x as f32 / inventory.size.y as f32),
|
||||
grid_auto_columns: vec![GridTrack::percent(100. / inventory.size.x as f32)],
|
||||
grid_auto_rows: vec![GridTrack::percent(100. / inventory.size.y as f32)],
|
||||
..default()
|
||||
},
|
||||
Pickable::IGNORE,
|
||||
GlobalZIndex::default(),
|
||||
Name::new(format!("UiInventory ({}x{})", inventory.size.x, inventory.size.y)),
|
||||
)
|
||||
}
|
||||
|
||||
fn inventory_slot_bundle(x: u32, y: u32, image: Handle<Image>) -> impl Bundle {
|
||||
(
|
||||
UiInventorySlot(UVec2::new(x, y)),
|
||||
ImageNode {
|
||||
color: Color::WHITE,
|
||||
image,
|
||||
image_mode: NodeImageMode::Stretch,
|
||||
..default()
|
||||
},
|
||||
Node {
|
||||
width: percent(100.),
|
||||
height: percent(100.),
|
||||
grid_column: GridPlacement::start(x as i16 + 1),
|
||||
grid_row: GridPlacement::start(y as i16 + 1),
|
||||
aspect_ratio: Some(1.),
|
||||
..default()
|
||||
},
|
||||
Pickable {
|
||||
should_block_lower: true,
|
||||
is_hoverable: true,
|
||||
},
|
||||
GlobalZIndex(1),
|
||||
BackgroundColor::DEFAULT,
|
||||
Name::new(format!("UiInventorySlot({x},{y})")),
|
||||
)
|
||||
}
|
||||
|
||||
fn ui_item_bundle(item: &Item, item_entity: Entity, image: Handle<Image>) -> impl Bundle {
|
||||
let (left, top, min_width, min_height, ui_transform) = ui_item_node_data(item);
|
||||
(
|
||||
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(2),
|
||||
Pickable {
|
||||
should_block_lower: false,
|
||||
is_hoverable: true,
|
||||
},
|
||||
Name::new(format!("UiItem ({},{})",
|
||||
item.position.unwrap_or_default().x,
|
||||
item.position.unwrap_or_default().y,
|
||||
)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn setup_ui_inventory(
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
inventory_query: Query<(Option<&ChildOf>, Entity, &Inventory, Option<&Children>)>,
|
||||
player_query: Query<(), With<Player>>,
|
||||
active_inventory_query: Query<Entity, With<ActiveInventory>>,
|
||||
item_query: Query<&Item>,
|
||||
root_query: Query<Entity, With<UiRoot>>,
|
||||
) {
|
||||
let Ok(root) = root_query.single() else {
|
||||
error!("Query contains more than one UiRoot");
|
||||
return;
|
||||
};
|
||||
let ui_slot_image: Handle<Image> = asset_server.load(UI_SLOT_ASSET_PATH);
|
||||
let temp_item_image: Handle<Image> = 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::<Entity>() {
|
||||
let is_player = match inventory_parent {
|
||||
Some(parent) => {
|
||||
if player_query.get(parent.0)
|
||||
.is_ok() { true }
|
||||
else {
|
||||
if active_inventory_query.get(inventory_entity)
|
||||
.is_ok() { false }
|
||||
else { continue; }
|
||||
}
|
||||
},
|
||||
None => {
|
||||
if active_inventory_query.get(inventory_entity)
|
||||
.is_ok() { false }
|
||||
else { continue; }
|
||||
},
|
||||
};
|
||||
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(_) => None,
|
||||
}
|
||||
}).collect::<Vec<(&Item, Entity)>>()
|
||||
}
|
||||
None => Vec::new(),
|
||||
};
|
||||
let inventory_id = commands.spawn(ui_inventory_bundle(inventory, inventory_entity))
|
||||
.with_children(|commands| {
|
||||
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()));
|
||||
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)) {
|
||||
slot_commands.with_children(|commands| {
|
||||
commands.spawn(ui_item_bundle(item, *entity, temp_item_image.clone()))
|
||||
.observe(on_item_over)
|
||||
.observe(on_item_out)
|
||||
.observe(on_item_drag_start)
|
||||
.observe(on_item_drag)
|
||||
.observe(on_item_drag_end);
|
||||
});
|
||||
}
|
||||
} }
|
||||
}).id();
|
||||
if is_player {
|
||||
player_inventory_ids.push(inventory_id);
|
||||
} else {
|
||||
active_inventory_ids.push(inventory_id);
|
||||
}
|
||||
}
|
||||
let player_inventory_manager = commands.spawn(ui_manager_bundle(player_inventory_ids, true)).id();
|
||||
let active_inventory_manager = commands.spawn(ui_manager_bundle(active_inventory_ids, false)).id();
|
||||
for entity in active_inventory_query {
|
||||
commands.entity(entity).remove::<ActiveInventory>();
|
||||
}
|
||||
commands.entity(root).add_children(&[player_inventory_manager, active_inventory_manager]);
|
||||
}
|
||||
|
||||
pub fn clear_ui_inventory(
|
||||
mut commands: Commands,
|
||||
inventory_query: Query<Entity, With<UiInventoryManager>>,
|
||||
) {
|
||||
for entity in inventory_query {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
0
src/ui/inventory/systems.rs
Normal file
0
src/ui/inventory/systems.rs
Normal file
|
|
@ -3,6 +3,8 @@ use leafwing_input_manager::prelude::*;
|
|||
|
||||
use crate::UiAction;
|
||||
|
||||
pub mod inventory;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[require(Node)]
|
||||
pub struct UiRoot;
|
||||
Loading…
Add table
Add a link
Reference in a new issue