generated from 2ndbeam/bevy-template
feat: Updated dragged item behavior
- Added Inventory.can_replace - Added tests for Inventory.can_replace - Dragged item is now a copy of original item - Dragged item can be rotated while dragged
This commit is contained in:
parent
beacc28ace
commit
0c8259583a
3 changed files with 155 additions and 24 deletions
|
|
@ -80,6 +80,23 @@ impl Inventory {
|
||||||
self.can_fit(item_query, children.as_slice(), rotated_item.size, *position)
|
self.can_fit(item_query, children.as_slice(), rotated_item.size, *position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn can_replace(
|
||||||
|
&self,
|
||||||
|
item_query: Query<&item::Item>,
|
||||||
|
contained_items: &[Entity],
|
||||||
|
replacable_item: Entity,
|
||||||
|
queried_item: &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::<Vec<Entity>>();
|
||||||
|
self.can_fit(item_query, children.as_slice(), queried_item.size, *position)
|
||||||
|
}
|
||||||
|
|
||||||
fn find_free_space_inner(
|
fn find_free_space_inner(
|
||||||
&self,
|
&self,
|
||||||
item_query: Query<&item::Item>,
|
item_query: Query<&item::Item>,
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,22 @@ const TEMP_ITEM_PATH: &'static str = "sprites/items/choco_bar.png";
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
#[require(Node)]
|
#[require(Node)]
|
||||||
pub struct UiInventory(Entity);
|
pub struct UiInventory(pub Entity);
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
#[require(Node, ImageNode)]
|
#[require(Node, ImageNode)]
|
||||||
pub struct UiInventorySlot(UVec2);
|
pub struct UiInventorySlot(pub UVec2);
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
#[require(Node, ImageNode)]
|
#[require(Node, ImageNode)]
|
||||||
pub struct UiItem(Entity);
|
pub struct UiItem(pub Entity);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Reflect)]
|
||||||
pub struct HoveredItem;
|
pub struct HoveredItem;
|
||||||
|
|
||||||
|
#[derive(Component, Reflect)]
|
||||||
|
pub struct DraggedItem(pub Item);
|
||||||
|
|
||||||
fn ui_item_node_data(item: &Item) -> (Val, Val, Val, Val, UiTransform) {
|
fn ui_item_node_data(item: &Item) -> (Val, Val, Val, Val, UiTransform) {
|
||||||
match item.rotated {
|
match item.rotated {
|
||||||
true => (
|
true => (
|
||||||
|
|
@ -41,6 +44,15 @@ fn ui_item_node_data(item: &Item) -> (Val, Val, Val, Val, UiTransform) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 on_slot_over(e: On<Pointer<Over>>, mut query: Query<&mut ImageNode, With<UiInventorySlot>>) {
|
fn on_slot_over(e: On<Pointer<Over>>, mut query: Query<&mut ImageNode, With<UiInventorySlot>>) {
|
||||||
if let Ok(mut image) = query.get_mut(e.event_target()) {
|
if let Ok(mut image) = query.get_mut(e.event_target()) {
|
||||||
image.color = Color::WHITE.darker(0.3);
|
image.color = Color::WHITE.darker(0.3);
|
||||||
|
|
@ -53,40 +65,72 @@ fn on_slot_out(e: On<Pointer<Out>>, mut query: Query<&mut ImageNode, With<UiInve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_item_over(e: On<Pointer<Over>>, mut commands: Commands, query: Query<Entity, With<UiItem>>) {
|
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()) {
|
if let Ok(_) = query.get(e.event_target()) {
|
||||||
commands.entity(e.event_target()).insert(HoveredItem);
|
commands.entity(e.event_target()).insert(HoveredItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_item_out(e: On<Pointer<Out>>, mut commands: Commands, query: Query<Entity, With<UiItem>>) {
|
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()) {
|
if let Ok(_) = query.get(e.event_target()) {
|
||||||
commands.entity(e.event_target()).remove::<HoveredItem>();
|
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>,
|
||||||
|
) {
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
commands.entity(e.event_target()).insert(DraggedItem(item.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn on_item_drag(e: On<Pointer<Drag>>, mut query: Query<&mut UiTransform, With<UiItem>>) {
|
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()) {
|
if let Ok(mut transform) = query.get_mut(e.event_target()) {
|
||||||
transform.translation = Val2::px(e.distance.x, e.distance.y);
|
transform.translation = Val2::px(e.distance.x, e.distance.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_item_drag_end(e: On<Pointer<DragEnd>>, mut query: Query<&mut UiTransform, With<UiItem>>) {
|
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()) {
|
if let Ok(mut transform) = query.get_mut(e.event_target()) {
|
||||||
transform.translation = Val2::ZERO;
|
transform.translation = Val2::ZERO;
|
||||||
|
commands.entity(e.event_target()).remove::<DraggedItem>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_item_drag_drop(
|
fn on_item_drag_drop(
|
||||||
event: On<Pointer<DragDrop>>,
|
event: On<Pointer<DragDrop>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
ui_item_query: Query<(Entity, &UiItem)>,
|
mut ui_item_query: Query<(Entity, &UiItem, &mut DraggedItem, &mut Node, &mut UiTransform)>,
|
||||||
ui_inventory_query: Query<&UiInventory>,
|
ui_inventory_query: Query<&UiInventory>,
|
||||||
mut item_query: Query<&mut Item>,
|
mut item_query: Query<&mut Item>,
|
||||||
slot_query: Query<(&ChildOf, &UiInventorySlot, Option<&Children>)>,
|
slot_query: Query<(&ChildOf, &UiInventorySlot, Option<&Children>)>,
|
||||||
inventory_query: Query<(&Inventory, Option<&Children>)>,
|
inventory_query: Query<(&Inventory, Option<&Children>)>,
|
||||||
) {
|
) {
|
||||||
let Ok((ui_item_entity, UiItem(item_entity))) = ui_item_query.get(event.dropped) else {
|
let Ok((ui_item_entity, UiItem(item_entity), mut dragged_item, node, ui_transform)) = ui_item_query.get_mut(event.dropped) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Ok((slot_parent, UiInventorySlot(new_position), slot_children)) = slot_query.get(event.event_target()) else {
|
let Ok((slot_parent, UiInventorySlot(new_position), slot_children)) = slot_query.get(event.event_target()) else {
|
||||||
|
|
@ -107,11 +151,29 @@ fn on_item_drag_drop(
|
||||||
None => &[],
|
None => &[],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let temp_item = &mut dragged_item.0;
|
||||||
|
temp_item.position = Some(*new_position);
|
||||||
|
|
||||||
|
if inventory.can_replace(item_query.as_readonly(), items, *item_entity, temp_item) {
|
||||||
|
info!("Replaced 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(event.event_target()));
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
if inventory.can_move(item_query.as_readonly(), items, *item_entity, *new_position) {
|
if inventory.can_move(item_query.as_readonly(), items, *item_entity, *new_position) {
|
||||||
let mut item = item_query.get_mut(*item_entity).unwrap();
|
let mut item = item_query.get_mut(*item_entity).unwrap();
|
||||||
item.position = Some(*new_position);
|
item.position = Some(*new_position);
|
||||||
commands.entity(ui_item_entity).insert(ChildOf(event.event_target()));
|
commands.entity(ui_item_entity).insert(ChildOf(event.event_target()));
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_ui_rotate(
|
pub fn on_ui_rotate(
|
||||||
|
|
@ -119,9 +181,9 @@ pub fn on_ui_rotate(
|
||||||
mut item_query: Query<&mut Item>,
|
mut item_query: Query<&mut Item>,
|
||||||
parent_query: Query<&ChildOf>,
|
parent_query: Query<&ChildOf>,
|
||||||
inventory_query: Query<(&Inventory, Option<&Children>)>,
|
inventory_query: Query<(&Inventory, Option<&Children>)>,
|
||||||
ui_item_query: Query<(&UiItem, &mut UiTransform, &mut Node), With<HoveredItem>>,
|
ui_item_query: Query<(&UiItem, &mut UiTransform, &mut Node, Option<&mut DraggedItem>), With<HoveredItem>>,
|
||||||
) {
|
) {
|
||||||
for (ui_item, mut ui_transform, mut node) in ui_item_query {
|
for (ui_item, ui_transform, node, maybe_dragged) in ui_item_query {
|
||||||
let Ok(item_parent) = parent_query.get(ui_item.0) else {
|
let Ok(item_parent) = parent_query.get(ui_item.0) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
@ -133,18 +195,25 @@ pub fn on_ui_rotate(
|
||||||
Some(children) => &children[..],
|
Some(children) => &children[..],
|
||||||
None => &[],
|
None => &[],
|
||||||
};
|
};
|
||||||
|
let (was_rotated, item) = match maybe_dragged {
|
||||||
if inventory.can_rotate(item_query.as_readonly(), children, ui_item.0) {
|
Some(mut temp_item) => {
|
||||||
let Ok(mut item) = item_query.get_mut(ui_item.0) else {
|
temp_item.0.rotate();
|
||||||
continue;
|
(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();
|
item.rotate();
|
||||||
let (left, top, min_width, min_height, new_ui_transform) = ui_item_node_data(&item);
|
out_item = Some(item.clone());
|
||||||
node.left = left;
|
}
|
||||||
node.top = top;
|
}
|
||||||
node.min_width = min_width;
|
(result, out_item)
|
||||||
node.min_height = min_height;
|
},
|
||||||
ui_transform.rotation = new_ui_transform.rotation;
|
};
|
||||||
|
if was_rotated {
|
||||||
|
update_ui_node(&item.unwrap(), node, ui_transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -278,6 +347,7 @@ pub fn setup_ui_inventory(
|
||||||
commands.spawn(ui_item_bundle(item, *entity, temp_item_image.clone()))
|
commands.spawn(ui_item_bundle(item, *entity, temp_item_image.clone()))
|
||||||
.observe(on_item_over)
|
.observe(on_item_over)
|
||||||
.observe(on_item_out)
|
.observe(on_item_out)
|
||||||
|
.observe(on_item_drag_start)
|
||||||
.observe(on_item_drag)
|
.observe(on_item_drag)
|
||||||
.observe(on_item_drag_end);
|
.observe(on_item_drag_end);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
46
src/tests.rs
46
src/tests.rs
|
|
@ -78,7 +78,7 @@ mod input {
|
||||||
|
|
||||||
mod inventory {
|
mod inventory {
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use crate::inventory::{Inventory, item::Item};
|
use crate::inventory::{Inventory, item::Item, ui::DraggedItem};
|
||||||
|
|
||||||
fn inventory() -> Inventory {
|
fn inventory() -> Inventory {
|
||||||
Inventory::new(UVec2::splat(4))
|
Inventory::new(UVec2::splat(4))
|
||||||
|
|
@ -192,6 +192,21 @@ mod inventory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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]
|
#[test]
|
||||||
fn everything_fits() {
|
fn everything_fits() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|
@ -346,4 +361,33 @@ mod inventory {
|
||||||
|
|
||||||
world.run_system(system).expect("Error on running system");
|
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()), 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()), RotatingMarker(false)))
|
||||||
|
.with_child(item_b())
|
||||||
|
.with_child(item_c())
|
||||||
|
.with_child(item_d());
|
||||||
|
|
||||||
|
world.run_system(system).expect("Error on running system");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue