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.WindowManager;
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 org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@ -113,10 +112,16 @@ abstract class IndexedGUI implements GUI {
cancelled = virtualInventory.setItemStack(player, index, event.getCursor());
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:
event.setCancelled(true);
cancelled = true;
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);
ItemUpdateEvent updateEvent = new ItemUpdateEvent(virtualInventory, index, player, invStack, null);
@ -146,8 +151,9 @@ abstract class IndexedGUI implements GUI {
break;
default:
// TODO: Hotbar swap
// action not supported
event.setCancelled(true);
cancelled = true;
break;
}

@ -4,6 +4,7 @@ import de.studiocode.invui.gui.GUI;
import org.bukkit.Bukkit;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
public class InventoryUtils {
@ -19,4 +20,16 @@ public class InventoryUtils {
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.util.ArrayUtils;
import de.studiocode.invui.util.Pair;
import de.studiocode.invui.virtualinventory.event.ItemUpdateEvent;
import de.studiocode.invui.window.Window;
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 index The slot index
* @param itemStack The {@link ItemStack} that should be put on that slot
*
* @return If the action has been cancelled
*/
public boolean setItemStack(Player player, int index, ItemStack itemStack) {
@ -339,9 +337,10 @@ public class VirtualInventory implements ConfigurationSerializable {
int amountLeft = originalAmount;
// find all slots where the item partially fits and add it there
Pair<Integer, ItemStack> partialSlot;
while ((partialSlot = findPartialSlot(itemStack)) != null && amountLeft != 0)
for (int partialSlot : findPartialSlots(itemStack)) {
amountLeft = addTo(player, partialSlot, amountLeft);
if (amountLeft == 0) break;
}
if (amountLeft != 0) {
// there are still items left, put the rest on an empty slot
@ -361,19 +360,52 @@ public class VirtualInventory implements ConfigurationSerializable {
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++) {
ItemStack currentStack = items[i];
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) {
int index = partialSlot.getFirst();
ItemStack itemStack = partialSlot.getSecond();
private int findFullSlot(ItemStack itemStack) {
for (int i = 0; i < items.length; i++) {
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);
@ -391,6 +423,27 @@ public class VirtualInventory implements ConfigurationSerializable {
} 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
* 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.itembuilder.ItemBuilder;
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.split.SimpleSplitWindow;
import de.studiocode.invui.window.impl.single.SimpleWindow;
@ -67,6 +68,17 @@ public interface Window extends GUIParent {
*/
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}
* that its underlying {@link Inventory} is being opened.

@ -116,9 +116,21 @@ public class WindowManager implements Listener {
w.get().handleClick(event);
} else {
Optional<Window> w1 = findWindow(event.getView().getTopInventory());
// player shift-clicked from lower inventory to window
if (w1.isPresent() && event.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY)
w1.get().handleItemShift(event);
// player inventory (not merged window) clicked
if (w1.isPresent()) {
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
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

@ -2,6 +2,7 @@ package de.studiocode.invui.window.impl.single;
import de.studiocode.invui.gui.GUI;
import de.studiocode.invui.gui.SlotElement;
import de.studiocode.invui.util.InventoryUtils;
import de.studiocode.invui.util.Pair;
import de.studiocode.invui.window.Window;
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);
}
@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
protected Pair<GUI, Integer> getGuiAt(int index) {
return index < gui.getSize() ? new Pair<>(gui, index) : null;
@ -74,11 +87,6 @@ public abstract class SingleWindow extends BaseWindow {
return gui.getSlotElement(index);
}
@Override
public void handleItemShift(InventoryClickEvent event) {
gui.handleItemShift(event);
}
@Override
public void handleViewerDeath(PlayerDeathEvent event) {
// empty