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:
Alexey 2026-03-13 13:42:53 +03:00
commit 0c8259583a
3 changed files with 155 additions and 24 deletions

View file

@ -9,19 +9,22 @@ const TEMP_ITEM_PATH: &'static str = "sprites/items/choco_bar.png";
#[derive(Component, Reflect)]
#[require(Node)]
pub struct UiInventory(Entity);
pub struct UiInventory(pub Entity);
#[derive(Component, Reflect)]
#[require(Node, ImageNode)]
pub struct UiInventorySlot(UVec2);
pub struct UiInventorySlot(pub UVec2);
#[derive(Component, Reflect)]
#[require(Node, ImageNode)]
pub struct UiItem(Entity);
pub struct UiItem(pub Entity);
#[derive(Component)]
#[derive(Component, Reflect)]
pub struct HoveredItem;
#[derive(Component, Reflect)]
pub struct DraggedItem(pub Item);
fn ui_item_node_data(item: &Item) -> (Val, Val, Val, Val, UiTransform) {
match item.rotated {
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>>) {
if let Ok(mut image) = query.get_mut(e.event_target()) {
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()) {
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()) {
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>>) {
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 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()) {
transform.translation = Val2::ZERO;
commands.entity(e.event_target()).remove::<DraggedItem>();
}
}
fn on_item_drag_drop(
event: On<Pointer<DragDrop>>,
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>,
mut item_query: Query<&mut Item>,
slot_query: Query<(&ChildOf, &UiInventorySlot, 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;
};
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 => &[],
};
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) {
let mut item = item_query.get_mut(*item_entity).unwrap();
item.position = Some(*new_position);
commands.entity(ui_item_entity).insert(ChildOf(event.event_target()));
}
*/
}
pub fn on_ui_rotate(
@ -119,9 +181,9 @@ pub fn on_ui_rotate(
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), 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 {
continue;
};
@ -133,18 +195,25 @@ pub fn on_ui_rotate(
Some(children) => &children[..],
None => &[],
};
if inventory.can_rotate(item_query.as_readonly(), children, ui_item.0) {
let Ok(mut item) = item_query.get_mut(ui_item.0) else {
continue;
};
item.rotate();
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;
let (was_rotated, item) = match maybe_dragged {
Some(mut temp_item) => {
temp_item.0.rotate();
(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);
}
}
}
@ -278,6 +347,7 @@ pub fn setup_ui_inventory(
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);
});