VirtualInventory Cursor Collect

This commit is contained in:
NichtStudioCode 2021-02-12 13:16:20 +01:00
parent 9ec262a122
commit 39a8941f62
7 changed files with 136 additions and 27 deletions

@ -15,7 +15,6 @@ import de.studiocode.invui.virtualinventory.event.ItemUpdateEvent;
import de.studiocode.invui.window.Window; import de.studiocode.invui.window.Window;
import de.studiocode.invui.window.WindowManager; import de.studiocode.invui.window.WindowManager;
import de.studiocode.invui.window.impl.merged.MergedWindow; import de.studiocode.invui.window.impl.merged.MergedWindow;
import de.studiocode.invui.window.impl.merged.combined.CombinedWindow;
import de.studiocode.invui.window.impl.merged.split.SplitWindow; import de.studiocode.invui.window.impl.merged.split.SplitWindow;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -113,10 +112,16 @@ abstract class IndexedGUI implements GUI {
cancelled = virtualInventory.setItemStack(player, index, event.getCursor()); cancelled = virtualInventory.setItemStack(player, index, event.getCursor());
break; break;
case COLLECT_TO_CURSOR:
cancelled = true;
ItemStack newCursor = cursor.clone();
newCursor.setAmount(virtualInventory.collectToCursor(player, newCursor));
player.setItemOnCursor(newCursor);
break;
case MOVE_TO_OTHER_INVENTORY: case MOVE_TO_OTHER_INVENTORY:
event.setCancelled(true); cancelled = true;
Window window = WindowManager.getInstance().findOpenWindow(player).orElse(null); Window window = WindowManager.getInstance().findOpenWindow(player).orElse(null);
if (window instanceof CombinedWindow) break; // can't move if there is no other gui
ItemStack invStack = virtualInventory.getItemStack(index); ItemStack invStack = virtualInventory.getItemStack(index);
ItemUpdateEvent updateEvent = new ItemUpdateEvent(virtualInventory, index, player, invStack, null); ItemUpdateEvent updateEvent = new ItemUpdateEvent(virtualInventory, index, player, invStack, null);
@ -146,8 +151,9 @@ abstract class IndexedGUI implements GUI {
break; break;
default: default:
// TODO: Hotbar swap
// action not supported // action not supported
event.setCancelled(true); cancelled = true;
break; break;
} }

@ -4,6 +4,7 @@ import de.studiocode.invui.gui.GUI;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
public class InventoryUtils { public class InventoryUtils {
@ -19,4 +20,16 @@ public class InventoryUtils {
else return Bukkit.createInventory(null, type, title); else return Bukkit.createInventory(null, type, title);
} }
public static boolean containsSimilar(Inventory inventory, ItemStack itemStack) {
for (int i = 0; i < inventory.getSize(); i++) {
ItemStack currentStack = inventory.getItem(i);
if (currentStack != null && currentStack.getType().isAir()) currentStack = null;
if ((currentStack == null && itemStack == null)
|| (currentStack != null && currentStack.isSimilar(itemStack))) return true;
}
return false;
}
} }

@ -2,7 +2,6 @@ package de.studiocode.invui.virtualinventory;
import de.studiocode.invui.InvUI; import de.studiocode.invui.InvUI;
import de.studiocode.invui.util.ArrayUtils; import de.studiocode.invui.util.ArrayUtils;
import de.studiocode.invui.util.Pair;
import de.studiocode.invui.virtualinventory.event.ItemUpdateEvent; import de.studiocode.invui.virtualinventory.event.ItemUpdateEvent;
import de.studiocode.invui.window.Window; import de.studiocode.invui.window.Window;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -104,7 +103,6 @@ public class VirtualInventory implements ConfigurationSerializable {
* @param player The player that did this or <code>null</code> if it wasn't a player. * @param player The player that did this or <code>null</code> if it wasn't a player.
* @param index The slot index * @param index The slot index
* @param itemStack The {@link ItemStack} that should be put on that slot * @param itemStack The {@link ItemStack} that should be put on that slot
*
* @return If the action has been cancelled * @return If the action has been cancelled
*/ */
public boolean setItemStack(Player player, int index, ItemStack itemStack) { public boolean setItemStack(Player player, int index, ItemStack itemStack) {
@ -339,9 +337,10 @@ public class VirtualInventory implements ConfigurationSerializable {
int amountLeft = originalAmount; int amountLeft = originalAmount;
// find all slots where the item partially fits and add it there // find all slots where the item partially fits and add it there
Pair<Integer, ItemStack> partialSlot; for (int partialSlot : findPartialSlots(itemStack)) {
while ((partialSlot = findPartialSlot(itemStack)) != null && amountLeft != 0)
amountLeft = addTo(player, partialSlot, amountLeft); amountLeft = addTo(player, partialSlot, amountLeft);
if (amountLeft == 0) break;
}
if (amountLeft != 0) { if (amountLeft != 0) {
// there are still items left, put the rest on an empty slot // there are still items left, put the rest on an empty slot
@ -361,19 +360,52 @@ public class VirtualInventory implements ConfigurationSerializable {
return amountLeft; return amountLeft;
} }
private Pair<Integer, ItemStack> findPartialSlot(ItemStack itemStack) { public int collectToCursor(Player player, ItemStack itemStack) {
int amount = itemStack.getAmount();
int maxStackSize = itemStack.getMaxStackSize();
if (amount < itemStack.getMaxStackSize()) {
// find partial slots and take items from there
for (int partialSlot : findPartialSlots(itemStack)) {
amount += takeFrom(player, partialSlot, maxStackSize - amount);
if (amount == maxStackSize) break;
}
// if only taking from partial stacks wasn't enough, take from a full slot
if (amount < itemStack.getMaxStackSize()) {
int fullSlot = findFullSlot(itemStack);
if (fullSlot != -1) {
amount += takeFrom(player, fullSlot, maxStackSize - amount);
}
}
}
return amount;
}
private List<Integer> findPartialSlots(ItemStack itemStack) {
List<Integer> partialSlots = new ArrayList<>();
for (int i = 0; i < items.length; i++) { for (int i = 0; i < items.length; i++) {
ItemStack currentStack = items[i]; ItemStack currentStack = items[i];
if (currentStack != null && currentStack.getAmount() < currentStack.getMaxStackSize() if (currentStack != null && currentStack.getAmount() < currentStack.getMaxStackSize()
&& currentStack.isSimilar(itemStack)) return new Pair<>(i, currentStack); && currentStack.isSimilar(itemStack)) partialSlots.add(i);
} }
return null; return partialSlots;
} }
private int addTo(Player player, Pair<Integer, ItemStack> partialSlot, int amount) { private int findFullSlot(ItemStack itemStack) {
int index = partialSlot.getFirst(); for (int i = 0; i < items.length; i++) {
ItemStack itemStack = partialSlot.getSecond(); ItemStack currentStack = items[i];
if (currentStack != null
&& currentStack.getAmount() == currentStack.getMaxStackSize()
&& currentStack.isSimilar(itemStack)) return i;
}
return -1;
}
private int addTo(Player player, int index, int amount) {
ItemStack itemStack = items[index];
int maxAddable = Math.min(itemStack.getMaxStackSize() - itemStack.getAmount(), amount); int maxAddable = Math.min(itemStack.getMaxStackSize() - itemStack.getAmount(), amount);
@ -391,6 +423,27 @@ public class VirtualInventory implements ConfigurationSerializable {
} else return amount; } else return amount;
} }
private int takeFrom(Player player, int index, int maxTake) {
ItemStack itemStack = items[index];
int amount = itemStack.getAmount();
int take = Math.min(amount, maxTake);
ItemStack newStack;
if (take != amount) {
newStack = itemStack.clone();
newStack.setAmount(amount - take);
} else newStack = null;
ItemUpdateEvent event = createAndCallEvent(index, player, itemStack, newStack);
if (!event.isCancelled()) {
items[index] = newStack;
notifyWindows();
return take;
}
return 0;
}
/** /**
* Adds a {@link Window} to the window set, telling the {@link VirtualInventory} that it is * Adds a {@link Window} to the window set, telling the {@link VirtualInventory} that it is
* currently being displayed in that {@link Window}. * currently being displayed in that {@link Window}.

@ -5,6 +5,7 @@ import de.studiocode.invui.gui.GUIParent;
import de.studiocode.invui.item.Item; import de.studiocode.invui.item.Item;
import de.studiocode.invui.item.itembuilder.ItemBuilder; import de.studiocode.invui.item.itembuilder.ItemBuilder;
import de.studiocode.invui.virtualinventory.VirtualInventory; import de.studiocode.invui.virtualinventory.VirtualInventory;
import de.studiocode.invui.window.impl.merged.MergedWindow;
import de.studiocode.invui.window.impl.merged.combined.SimpleCombinedWindow; import de.studiocode.invui.window.impl.merged.combined.SimpleCombinedWindow;
import de.studiocode.invui.window.impl.merged.split.SimpleSplitWindow; import de.studiocode.invui.window.impl.merged.split.SimpleSplitWindow;
import de.studiocode.invui.window.impl.single.SimpleWindow; import de.studiocode.invui.window.impl.single.SimpleWindow;
@ -67,6 +68,17 @@ public interface Window extends GUIParent {
*/ */
void handleItemShift(InventoryClickEvent event); void handleItemShift(InventoryClickEvent event);
/**
* A method called by the {@link WindowManager} to notify the {@link Window}
* that a {@link Player} is trying to collect {@link ItemStack} to the cursor
* by double-clicking in the player inventory.
* This method is not called when the player inventory is also part of the
* {@link Window}. ({@link MergedWindow})
*
* @param event The {@link InventoryClickEvent} associated with this action.
*/
void handleCursorCollect(InventoryClickEvent event);
/** /**
* A method called by the {@link WindowManager} to notify the {@link Window} * A method called by the {@link WindowManager} to notify the {@link Window}
* that its underlying {@link Inventory} is being opened. * that its underlying {@link Inventory} is being opened.

@ -116,9 +116,21 @@ public class WindowManager implements Listener {
w.get().handleClick(event); w.get().handleClick(event);
} else { } else {
Optional<Window> w1 = findWindow(event.getView().getTopInventory()); Optional<Window> w1 = findWindow(event.getView().getTopInventory());
// player shift-clicked from lower inventory to window // player inventory (not merged window) clicked
if (w1.isPresent() && event.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY) if (w1.isPresent()) {
w1.get().handleItemShift(event); Window window = w1.get();
switch (event.getAction()) {
// items have been shift clicked from player inv to Window
case MOVE_TO_OTHER_INVENTORY:
window.handleItemShift(event);
break;
// items have been collected by clicking a slot in the player inv
case COLLECT_TO_CURSOR:
window.handleCursorCollect(event);
break;
}
}
} }
} }

@ -120,7 +120,12 @@ public abstract class MergedWindow extends BaseWindow {
@Override @Override
public void handleItemShift(InventoryClickEvent event) { public void handleItemShift(InventoryClickEvent event) {
// empty // empty, should not be called by the WindowManager
}
@Override
public void handleCursorCollect(InventoryClickEvent event) {
// empty, should not be called by the WindowManager
} }
@Override @Override

@ -2,6 +2,7 @@ package de.studiocode.invui.window.impl.single;
import de.studiocode.invui.gui.GUI; import de.studiocode.invui.gui.GUI;
import de.studiocode.invui.gui.SlotElement; import de.studiocode.invui.gui.SlotElement;
import de.studiocode.invui.util.InventoryUtils;
import de.studiocode.invui.util.Pair; import de.studiocode.invui.util.Pair;
import de.studiocode.invui.window.Window; import de.studiocode.invui.window.Window;
import de.studiocode.invui.window.impl.BaseWindow; import de.studiocode.invui.window.impl.BaseWindow;
@ -64,6 +65,18 @@ public abstract class SingleWindow extends BaseWindow {
gui.handleClick(event.getSlot(), (Player) event.getWhoClicked(), event.getClick(), event); gui.handleClick(event.getSlot(), (Player) event.getWhoClicked(), event.getClick(), event);
} }
@Override
public void handleItemShift(InventoryClickEvent event) {
gui.handleItemShift(event);
}
@Override
public void handleCursorCollect(InventoryClickEvent event) {
// TODO: Allow collecting from player inventory and VirtualInventory
// only cancel when this would affect the window inventory
if (InventoryUtils.containsSimilar(inventory, event.getCursor())) event.setCancelled(true);
}
@Override @Override
protected Pair<GUI, Integer> getGuiAt(int index) { protected Pair<GUI, Integer> getGuiAt(int index) {
return index < gui.getSize() ? new Pair<>(gui, index) : null; return index < gui.getSize() ? new Pair<>(gui, index) : null;
@ -74,11 +87,6 @@ public abstract class SingleWindow extends BaseWindow {
return gui.getSlotElement(index); return gui.getSlotElement(index);
} }
@Override
public void handleItemShift(InventoryClickEvent event) {
gui.handleItemShift(event);
}
@Override @Override
public void handleViewerDeath(PlayerDeathEvent event) { public void handleViewerDeath(PlayerDeathEvent event) {
// empty // empty