2020-04-24 03:25:58 +02:00
|
|
|
package net.minestom.server.inventory;
|
|
|
|
|
2022-01-28 07:36:35 +01:00
|
|
|
import it.unimi.dsi.fastutil.Pair;
|
2021-04-04 15:33:53 +02:00
|
|
|
import net.kyori.adventure.text.Component;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.Viewable;
|
|
|
|
import net.minestom.server.entity.Player;
|
2022-01-28 07:36:35 +01:00
|
|
|
import net.minestom.server.inventory.click.ClickProcessor;
|
|
|
|
import net.minestom.server.inventory.click.ClickResult;
|
2020-05-02 18:45:34 +02:00
|
|
|
import net.minestom.server.inventory.click.ClickType;
|
2022-01-28 07:36:35 +01:00
|
|
|
import net.minestom.server.inventory.click.DragHelper;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.item.ItemStack;
|
2020-07-28 18:40:10 +02:00
|
|
|
import net.minestom.server.network.packet.server.play.OpenWindowPacket;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.network.packet.server.play.SetSlotPacket;
|
|
|
|
import net.minestom.server.network.packet.server.play.WindowItemsPacket;
|
2020-04-29 17:27:58 +02:00
|
|
|
import net.minestom.server.network.packet.server.play.WindowPropertyPacket;
|
2020-05-22 23:19:04 +02:00
|
|
|
import net.minestom.server.utils.inventory.PlayerInventoryUtils;
|
2020-10-24 10:46:23 +02:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
2019-08-13 17:52:09 +02:00
|
|
|
|
|
|
|
import java.util.Collections;
|
2021-11-30 17:49:41 +01:00
|
|
|
import java.util.List;
|
2022-01-28 07:36:35 +01:00
|
|
|
import java.util.Map;
|
2019-08-13 17:52:09 +02:00
|
|
|
import java.util.Set;
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import java.util.concurrent.CopyOnWriteArraySet;
|
2022-05-10 10:07:55 +02:00
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
2022-01-28 07:36:35 +01:00
|
|
|
import java.util.function.Consumer;
|
|
|
|
|
|
|
|
import static net.minestom.server.utils.inventory.PlayerInventoryUtils.OFFHAND_SLOT;
|
2019-08-13 17:52:09 +02:00
|
|
|
|
2020-10-14 16:41:36 +02:00
|
|
|
/**
|
|
|
|
* Represents an inventory which can be viewed by a collection of {@link Player}.
|
|
|
|
* <p>
|
|
|
|
* You can create one with {@link Inventory#Inventory(InventoryType, String)} or by making your own subclass.
|
|
|
|
* It can then be opened using {@link Player#openInventory(Inventory)}.
|
|
|
|
*/
|
2021-10-22 01:55:55 +02:00
|
|
|
public non-sealed class Inventory extends AbstractInventory implements Viewable {
|
2022-05-10 10:07:55 +02:00
|
|
|
private static final AtomicInteger ID_COUNTER = new AtomicInteger();
|
2019-08-13 17:52:09 +02:00
|
|
|
|
2020-10-10 13:46:41 +02:00
|
|
|
// the id of this inventory
|
2020-08-19 01:24:51 +02:00
|
|
|
private final byte id;
|
2020-10-10 13:46:41 +02:00
|
|
|
// the type of this inventory
|
2020-07-23 07:36:49 +02:00
|
|
|
private final InventoryType inventoryType;
|
2021-04-04 15:33:53 +02:00
|
|
|
// the title of this inventory
|
|
|
|
private Component title;
|
2019-08-13 17:52:09 +02:00
|
|
|
|
2020-08-19 01:24:51 +02:00
|
|
|
private final int offset;
|
2019-08-13 17:52:09 +02:00
|
|
|
|
2020-10-10 13:46:41 +02:00
|
|
|
// the players currently viewing this inventory
|
2020-08-19 01:24:51 +02:00
|
|
|
private final Set<Player> viewers = new CopyOnWriteArraySet<>();
|
2020-12-14 06:27:39 +01:00
|
|
|
private final Set<Player> unmodifiableViewers = Collections.unmodifiableSet(viewers);
|
2020-10-10 13:46:41 +02:00
|
|
|
// (player -> cursor item) map, used by the click listeners
|
2020-08-19 01:24:51 +02:00
|
|
|
private final ConcurrentHashMap<Player, ItemStack> cursorPlayersItem = new ConcurrentHashMap<>();
|
2019-08-13 17:52:09 +02:00
|
|
|
|
2021-04-04 15:33:53 +02:00
|
|
|
public Inventory(@NotNull InventoryType inventoryType, @NotNull Component title) {
|
2021-04-04 03:36:31 +02:00
|
|
|
super(inventoryType.getSize());
|
2019-08-13 17:52:09 +02:00
|
|
|
this.id = generateId();
|
|
|
|
this.inventoryType = inventoryType;
|
|
|
|
this.title = title;
|
|
|
|
|
2021-04-04 03:36:31 +02:00
|
|
|
this.offset = getSize();
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
2021-04-04 15:33:53 +02:00
|
|
|
public Inventory(@NotNull InventoryType inventoryType, @NotNull String title) {
|
|
|
|
this(inventoryType, Component.text(title));
|
|
|
|
}
|
|
|
|
|
2022-05-10 10:07:55 +02:00
|
|
|
private static byte generateId() {
|
|
|
|
return (byte) Math.abs((byte) ID_COUNTER.incrementAndGet());
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
2020-07-28 19:00:25 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the inventory type.
|
2020-07-28 19:00:25 +02:00
|
|
|
*
|
|
|
|
* @return the inventory type
|
|
|
|
*/
|
2021-12-22 10:51:25 +01:00
|
|
|
public @NotNull InventoryType getInventoryType() {
|
2019-08-13 17:52:09 +02:00
|
|
|
return inventoryType;
|
|
|
|
}
|
|
|
|
|
2020-07-28 19:00:25 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the inventory title.
|
2020-07-28 19:00:25 +02:00
|
|
|
*
|
|
|
|
* @return the inventory title
|
|
|
|
*/
|
2021-12-22 10:51:25 +01:00
|
|
|
public @NotNull Component getTitle() {
|
2019-08-13 17:52:09 +02:00
|
|
|
return title;
|
|
|
|
}
|
|
|
|
|
2020-07-28 19:00:25 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Changes the inventory title.
|
2020-07-28 19:00:25 +02:00
|
|
|
*
|
|
|
|
* @param title the new inventory title
|
|
|
|
*/
|
2021-04-04 15:33:53 +02:00
|
|
|
public void setTitle(@NotNull Component title) {
|
2020-07-28 18:28:45 +02:00
|
|
|
this.title = title;
|
2020-10-02 03:54:59 +02:00
|
|
|
// Re-open the inventory
|
2021-11-30 17:49:41 +01:00
|
|
|
sendPacketToViewers(new OpenWindowPacket(getWindowId(), getInventoryType().getWindowType(), title));
|
2020-10-02 03:54:59 +02:00
|
|
|
// Send inventory items
|
2020-07-28 18:28:45 +02:00
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2020-07-28 19:00:25 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets this window id.
|
2020-07-28 19:00:25 +02:00
|
|
|
* <p>
|
2020-10-15 21:16:31 +02:00
|
|
|
* This is the id that the client will send to identify the affected inventory, mostly used by packets.
|
2020-07-28 19:00:25 +02:00
|
|
|
*
|
|
|
|
* @return the window id
|
|
|
|
*/
|
2020-04-28 19:22:47 +02:00
|
|
|
public byte getWindowId() {
|
2019-08-13 17:52:09 +02:00
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2021-04-12 00:42:32 +02:00
|
|
|
@Override
|
|
|
|
public synchronized void clear() {
|
2022-05-10 10:07:55 +02:00
|
|
|
this.cursorPlayersItem.clear();
|
2021-04-12 00:42:32 +02:00
|
|
|
super.clear();
|
|
|
|
}
|
|
|
|
|
2020-05-22 23:19:04 +02:00
|
|
|
/**
|
2020-10-25 12:28:06 +01:00
|
|
|
* Refreshes the inventory for all viewers.
|
2020-05-22 23:19:04 +02:00
|
|
|
*/
|
2021-04-04 03:36:31 +02:00
|
|
|
@Override
|
2019-08-27 05:23:25 +02:00
|
|
|
public void update() {
|
2021-12-22 10:51:25 +01:00
|
|
|
this.viewers.forEach(p -> p.sendPacket(createNewWindowItemsPacket(p)));
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
2020-05-22 23:19:04 +02:00
|
|
|
/**
|
2020-10-25 12:28:06 +01:00
|
|
|
* Refreshes the inventory for a specific viewer.
|
2020-10-26 19:14:50 +01:00
|
|
|
* <p>
|
2020-10-25 12:28:06 +01:00
|
|
|
* The player needs to be a viewer, otherwise nothing is sent.
|
2020-05-22 23:19:04 +02:00
|
|
|
*
|
|
|
|
* @param player the player to update the inventory
|
|
|
|
*/
|
2020-10-24 11:19:54 +02:00
|
|
|
public void update(@NotNull Player player) {
|
2021-10-28 21:44:19 +02:00
|
|
|
if (!isViewer(player)) return;
|
|
|
|
player.sendPacket(createNewWindowItemsPacket(player));
|
2020-05-22 23:19:04 +02:00
|
|
|
}
|
|
|
|
|
2019-08-19 17:04:19 +02:00
|
|
|
@Override
|
2021-12-22 10:51:25 +01:00
|
|
|
public @NotNull Set<Player> getViewers() {
|
2020-12-14 06:27:39 +01:00
|
|
|
return unmodifiableViewers;
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
2020-10-25 12:28:06 +01:00
|
|
|
/**
|
|
|
|
* This will not open the inventory for {@code player}, use {@link Player#openInventory(Inventory)}.
|
|
|
|
*
|
|
|
|
* @param player the viewer to add
|
|
|
|
* @return true if the player has successfully been added
|
|
|
|
*/
|
2019-08-19 17:04:19 +02:00
|
|
|
@Override
|
2020-10-24 10:46:23 +02:00
|
|
|
public boolean addViewer(@NotNull Player player) {
|
2020-07-23 07:36:49 +02:00
|
|
|
final boolean result = this.viewers.add(player);
|
2020-07-28 19:00:25 +02:00
|
|
|
update(player);
|
2020-05-23 17:57:56 +02:00
|
|
|
return result;
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
2020-10-25 12:28:06 +01:00
|
|
|
/**
|
|
|
|
* This will not close the inventory for {@code player}, use {@link Player#closeInventory()}.
|
|
|
|
*
|
|
|
|
* @param player the viewer to remove
|
|
|
|
* @return true if the player has successfully been removed
|
|
|
|
*/
|
2019-08-19 17:04:19 +02:00
|
|
|
@Override
|
2020-10-24 10:46:23 +02:00
|
|
|
public boolean removeViewer(@NotNull Player player) {
|
2020-07-23 07:36:49 +02:00
|
|
|
final boolean result = this.viewers.remove(player);
|
2021-04-24 19:14:19 +02:00
|
|
|
setCursorItem(player, ItemStack.AIR);
|
2022-01-28 07:36:35 +01:00
|
|
|
this.dragHelper.clearCache(player);
|
2020-05-23 17:57:56 +02:00
|
|
|
return result;
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
2020-05-30 14:58:00 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the cursor item of a viewer.
|
2020-05-30 14:58:00 +02:00
|
|
|
*
|
|
|
|
* @param player the player to get the cursor item from
|
2020-06-23 17:25:46 +02:00
|
|
|
* @return the player cursor item, air item if the player is not a viewer
|
2020-05-30 14:58:00 +02:00
|
|
|
*/
|
2021-12-22 10:51:25 +01:00
|
|
|
public @NotNull ItemStack getCursorItem(@NotNull Player player) {
|
2021-04-02 18:13:02 +02:00
|
|
|
return cursorPlayersItem.getOrDefault(player, ItemStack.AIR);
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
2020-06-23 17:25:46 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Changes the cursor item of a viewer,
|
|
|
|
* does nothing if <code>player</code> is not a viewer.
|
2020-06-23 17:25:46 +02:00
|
|
|
*
|
|
|
|
* @param player the player to change the cursor item
|
|
|
|
* @param cursorItem the new player cursor item
|
|
|
|
*/
|
2020-10-24 11:19:54 +02:00
|
|
|
public void setCursorItem(@NotNull Player player, @NotNull ItemStack cursorItem) {
|
2021-04-24 19:14:19 +02:00
|
|
|
final ItemStack currentCursorItem = cursorPlayersItem.getOrDefault(player, ItemStack.AIR);
|
2022-05-10 07:33:05 +02:00
|
|
|
if (!currentCursorItem.equals(cursorItem)) {
|
2021-10-28 21:44:19 +02:00
|
|
|
player.sendPacket(SetSlotPacket.createCursorPacket(cursorItem));
|
2020-12-15 05:39:28 +01:00
|
|
|
}
|
2021-04-24 19:14:19 +02:00
|
|
|
if (!cursorItem.isAir()) {
|
|
|
|
this.cursorPlayersItem.put(player, cursorItem);
|
|
|
|
} else {
|
|
|
|
this.cursorPlayersItem.remove(player);
|
|
|
|
}
|
2020-06-23 17:25:46 +02:00
|
|
|
}
|
|
|
|
|
2021-04-04 02:56:44 +02:00
|
|
|
@Override
|
2022-05-10 07:33:05 +02:00
|
|
|
protected void UNSAFE_itemInsert(int slot, @NotNull ItemStack itemStack, boolean sendPacket) {
|
2021-09-06 18:59:55 +02:00
|
|
|
itemStacks[slot] = itemStack;
|
2022-05-10 07:42:18 +02:00
|
|
|
if (sendPacket) sendPacketToViewers(new SetSlotPacket(getWindowId(), 0, (short) slot, itemStack));
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
2021-09-08 15:50:14 +02:00
|
|
|
private @NotNull WindowItemsPacket createNewWindowItemsPacket(Player player) {
|
2021-11-30 17:49:41 +01:00
|
|
|
return new WindowItemsPacket(getWindowId(), 0, List.of(getItemStacks()), cursorPlayersItem.getOrDefault(player, ItemStack.AIR));
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
2020-08-03 00:37:03 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Sends a window property to all viewers.
|
2020-08-03 00:37:03 +02:00
|
|
|
*
|
|
|
|
* @param property the property to send
|
|
|
|
* @param value the value of the property
|
2020-09-20 15:04:07 +02:00
|
|
|
* @see <a href="https://wiki.vg/Protocol#Window_Property">https://wiki.vg/Protocol#Window_Property</a>
|
2020-08-03 00:37:03 +02:00
|
|
|
*/
|
2020-10-24 11:19:54 +02:00
|
|
|
protected void sendProperty(@NotNull InventoryProperty property, short value) {
|
2021-07-22 09:54:34 +02:00
|
|
|
sendPacketToViewers(new WindowPropertyPacket(getWindowId(), property.getProperty(), value));
|
2020-04-29 17:27:58 +02:00
|
|
|
}
|
|
|
|
|
2019-08-13 17:52:09 +02:00
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public boolean leftClick(@NotNull Player player, int slot) {
|
2020-07-23 07:36:49 +02:00
|
|
|
final boolean isInWindow = isClickInWindow(slot);
|
2021-01-20 08:18:00 +01:00
|
|
|
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
2022-01-28 07:36:35 +01:00
|
|
|
var inventory = isInWindow ? this : player.getInventory();
|
|
|
|
final var tmp = handlePreClick(inventory, player, clickSlot, ClickType.LEFT_CLICK, getCursorItem(player), inventory.getItemStack(clickSlot));
|
|
|
|
if (tmp.cancelled()) {
|
|
|
|
update();
|
2021-08-13 20:41:59 +02:00
|
|
|
return false;
|
2020-05-22 23:19:04 +02:00
|
|
|
}
|
2022-01-28 07:36:35 +01:00
|
|
|
return handleResult(ClickProcessor.left(clickSlot, tmp.clicked(), tmp.cursor()),
|
|
|
|
itemStack -> setCursorItem(player, itemStack), player, inventory, ClickType.LEFT_CLICK);
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public boolean rightClick(@NotNull Player player, int slot) {
|
2020-07-23 07:36:49 +02:00
|
|
|
final boolean isInWindow = isClickInWindow(slot);
|
2021-01-20 08:18:00 +01:00
|
|
|
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
2022-01-28 07:36:35 +01:00
|
|
|
var inventory = isInWindow ? this : player.getInventory();
|
|
|
|
final var tmp = handlePreClick(inventory, player, clickSlot, ClickType.RIGHT_CLICK, getCursorItem(player), inventory.getItemStack(clickSlot));
|
|
|
|
if (tmp.cancelled()) {
|
|
|
|
update();
|
2021-08-13 20:41:59 +02:00
|
|
|
return false;
|
2020-05-22 23:19:04 +02:00
|
|
|
}
|
2022-01-28 07:36:35 +01:00
|
|
|
return handleResult(ClickProcessor.right(clickSlot, tmp.clicked(), tmp.cursor()),
|
|
|
|
itemStack -> setCursorItem(player, itemStack), player, inventory, ClickType.RIGHT_CLICK);
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public boolean shiftClick(@NotNull Player player, int slot) {
|
2020-07-23 07:36:49 +02:00
|
|
|
final boolean isInWindow = isClickInWindow(slot);
|
2021-01-20 08:18:00 +01:00
|
|
|
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
2022-06-29 20:36:03 +02:00
|
|
|
final AbstractInventory inventory = isInWindow ? this : player.getInventory();
|
|
|
|
final ItemStack item = getItemStack(clickSlot);
|
2022-01-28 07:36:35 +01:00
|
|
|
PlayerInventory playerInv = player.getInventory();
|
2022-06-29 20:36:03 +02:00
|
|
|
final var tmp = handlePreClick(inventory, player, clickSlot, ClickType.START_SHIFT_CLICK,
|
|
|
|
getCursorItem(player), inventory.getItemStack(clickSlot));
|
|
|
|
if (tmp.cancelled()) {
|
|
|
|
update();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var result = isInWindow ? ClickProcessor.shiftToPlayer(playerInv, item) : ClickProcessor.shiftToInventory(this, item);
|
|
|
|
var inverseInv = isInWindow ? player.getInventory() : this;
|
|
|
|
// TODO call pre-click for each changed slot
|
|
|
|
return handleResult(result,
|
|
|
|
itemStack -> inventory.setItemStack(clickSlot, itemStack), player, inverseInv, ClickType.SHIFT_CLICK);
|
|
|
|
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public boolean changeHeld(@NotNull Player player, int slot, int key) {
|
2020-07-23 07:36:49 +02:00
|
|
|
final PlayerInventory playerInventory = player.getInventory();
|
|
|
|
final boolean isInWindow = isClickInWindow(slot);
|
2021-01-20 08:18:00 +01:00
|
|
|
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
2022-01-28 07:36:35 +01:00
|
|
|
final int convertedKey = key == 40 ? OFFHAND_SLOT : key;
|
|
|
|
final var clickInv = isInWindow ? this : playerInventory;
|
|
|
|
final var tmp = handlePreClick(clickInv, player, clickSlot, ClickType.CHANGE_HELD,
|
|
|
|
getCursorItem(player), clickInv.getItemStack(clickSlot));
|
|
|
|
if (tmp.cancelled()) {
|
|
|
|
update();
|
2021-08-13 20:41:59 +02:00
|
|
|
return false;
|
2020-05-22 23:19:04 +02:00
|
|
|
}
|
2022-01-28 07:36:35 +01:00
|
|
|
return handleResult(ClickProcessor.held(playerInventory, clickInv, clickSlot, tmp.clicked(), convertedKey, playerInventory.getItemStack(convertedKey)),
|
|
|
|
itemStack -> clickInv.setItemStack(clickSlot, itemStack), player, playerInventory, ClickType.SHIFT_CLICK);
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public boolean middleClick(@NotNull Player player, int slot) {
|
2020-05-22 23:19:04 +02:00
|
|
|
// TODO
|
2021-08-13 20:41:59 +02:00
|
|
|
update(player);
|
2020-05-22 23:19:04 +02:00
|
|
|
return false;
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2021-06-06 01:50:28 +02:00
|
|
|
public boolean drop(@NotNull Player player, boolean all, int slot, int button) {
|
2020-07-23 07:36:49 +02:00
|
|
|
final boolean isInWindow = isClickInWindow(slot);
|
2021-01-20 08:18:00 +01:00
|
|
|
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
2022-01-28 07:36:35 +01:00
|
|
|
var inv = isInWindow ? this : player.getInventory();
|
2020-07-23 07:36:49 +02:00
|
|
|
final ItemStack cursor = getCursorItem(player);
|
2022-01-28 07:36:35 +01:00
|
|
|
final boolean outsideDrop = slot == -999;
|
|
|
|
final ItemStack clicked = outsideDrop ? ItemStack.AIR : inv.getItemStack(clickSlot);
|
|
|
|
var drop = ClickProcessor.drop(all, slot, button, clicked, cursor);
|
|
|
|
|
|
|
|
player.dropItem(drop.drop());
|
|
|
|
if (outsideDrop) {
|
|
|
|
setCursorItem(player, drop.remaining());
|
|
|
|
} else {
|
|
|
|
inv.setItemStack(clickSlot, drop.remaining());
|
2020-04-21 15:31:41 +02:00
|
|
|
}
|
2022-01-28 07:36:35 +01:00
|
|
|
// TODO events
|
2021-08-13 20:41:59 +02:00
|
|
|
return true;
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|
2019-08-14 15:54:37 +02:00
|
|
|
|
2022-01-28 07:36:35 +01:00
|
|
|
private final DragHelper dragHelper = new DragHelper();
|
|
|
|
|
2020-03-20 19:50:22 +01:00
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public boolean dragging(@NotNull Player player, int slot, int button) {
|
2022-01-28 07:36:35 +01:00
|
|
|
var playerInv = player.getInventory();
|
2020-07-23 07:36:49 +02:00
|
|
|
final boolean isInWindow = isClickInWindow(slot);
|
2021-01-20 08:18:00 +01:00
|
|
|
final int clickSlot = isInWindow ? slot : PlayerInventoryUtils.convertSlot(slot, offset);
|
2022-01-28 07:36:35 +01:00
|
|
|
final var clickInv = isInWindow ? this : playerInv;
|
|
|
|
return dragHelper.test(player, slot, button, clickSlot, clickInv,
|
|
|
|
// Start
|
|
|
|
(clickType) -> {
|
2022-06-29 19:48:14 +02:00
|
|
|
final var tmp = handlePreClick(null, player, -999, clickType,
|
2022-01-28 07:36:35 +01:00
|
|
|
getCursorItem(player), ItemStack.AIR);
|
|
|
|
if (tmp.cancelled()) {
|
|
|
|
update();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
// Step
|
|
|
|
(clickType) -> {
|
|
|
|
final var tmp = handlePreClick(clickInv, player, clickSlot, clickType,
|
|
|
|
getCursorItem(player), getItemStack(clickSlot));
|
|
|
|
return !tmp.cancelled();
|
|
|
|
},
|
|
|
|
// End
|
|
|
|
(clickType, entries) -> {
|
|
|
|
var slots = entries.stream().map(dragData -> Pair.of(dragData.inventory(), dragData.slot())).toList();
|
|
|
|
// Handle last drag
|
2022-06-29 19:48:14 +02:00
|
|
|
final var tmp = handlePreClick(null, player, -999, clickType,
|
2022-06-29 19:38:17 +02:00
|
|
|
getCursorItem(player), ItemStack.AIR);
|
|
|
|
if (tmp.cancelled()) {
|
|
|
|
update();
|
|
|
|
return false;
|
2022-01-28 07:36:35 +01:00
|
|
|
}
|
2022-06-29 19:21:38 +02:00
|
|
|
return switch (clickType) {
|
|
|
|
case END_LEFT_DRAGGING ->
|
|
|
|
handleResult(ClickProcessor.leftDrag(playerInv, this, getCursorItem(player), slots),
|
|
|
|
itemStack -> setCursorItem(player, itemStack), player, clickType);
|
|
|
|
case END_RIGHT_DRAGGING ->
|
|
|
|
handleResult(ClickProcessor.rightDrag(playerInv, this, getCursorItem(player), slots),
|
|
|
|
itemStack -> setCursorItem(player, itemStack), player, clickType);
|
|
|
|
default -> throw new IllegalStateException("Invalid click type: " + clickType);
|
|
|
|
};
|
2022-01-28 07:36:35 +01:00
|
|
|
});
|
2020-03-20 19:50:22 +01:00
|
|
|
}
|
|
|
|
|
2019-08-14 15:54:37 +02:00
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public boolean doubleClick(@NotNull Player player, int slot) {
|
2022-01-28 07:36:35 +01:00
|
|
|
return handleResult(ClickProcessor.doubleClick(player.getInventory(), this, getCursorItem(player)),
|
|
|
|
itemStack -> setCursorItem(player, itemStack), player, ClickType.DOUBLE_CLICK);
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean handleResult(ClickResult.Double result, Consumer<ItemStack> remainingSetter,
|
|
|
|
Player player, ClickType clickType) {
|
|
|
|
// Player changes
|
|
|
|
{
|
|
|
|
var inv = player.getInventory();
|
|
|
|
Map<Integer, ItemStack> playerChanges = result.playerChanges();
|
|
|
|
playerChanges.forEach((slot, itemStack) -> {
|
|
|
|
// TODO call events (conditions/pre-click)
|
|
|
|
});
|
|
|
|
|
|
|
|
playerChanges.forEach((slot, itemStack) -> {
|
|
|
|
inv.setItemStack(slot, itemStack);
|
|
|
|
callClickEvent(player, null, slot, clickType, itemStack, getCursorItem(player));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
// This inventory changes
|
|
|
|
{
|
|
|
|
Map<Integer, ItemStack> playerChanges = result.inventoryChanges();
|
|
|
|
playerChanges.forEach((slot, itemStack) -> {
|
|
|
|
// TODO call events (conditions/pre-click)
|
|
|
|
});
|
|
|
|
|
|
|
|
playerChanges.forEach((slot, itemStack) -> {
|
|
|
|
setItemStack(slot, itemStack);
|
|
|
|
callClickEvent(player, this, slot, clickType, itemStack, getCursorItem(player));
|
|
|
|
});
|
2021-08-13 20:41:59 +02:00
|
|
|
}
|
2022-01-28 07:36:35 +01:00
|
|
|
remainingSetter.accept(result.remaining());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean handleResult(ClickResult.Single result, Consumer<ItemStack> remainingSetter,
|
|
|
|
Player player, AbstractInventory inventory, ClickType clickType) {
|
|
|
|
Inventory eventInv = inventory instanceof Inventory ? ((Inventory) inventory) : null;
|
|
|
|
Map<Integer, ItemStack> changes = result.changedSlots();
|
|
|
|
changes.forEach((slot, itemStack) -> {
|
|
|
|
// TODO call events (conditions/pre-click)
|
|
|
|
});
|
|
|
|
|
|
|
|
changes.forEach((slot, itemStack) -> {
|
|
|
|
inventory.setItemStack(slot, itemStack);
|
|
|
|
callClickEvent(player, eventInv, slot, clickType, itemStack, getCursorItem(player));
|
|
|
|
});
|
|
|
|
remainingSetter.accept(result.remaining());
|
2021-08-13 20:41:59 +02:00
|
|
|
return true;
|
2020-05-22 23:19:04 +02:00
|
|
|
}
|
|
|
|
|
2021-12-22 10:51:25 +01:00
|
|
|
private boolean isClickInWindow(int slot) {
|
|
|
|
return slot < getSize();
|
|
|
|
}
|
|
|
|
|
2021-08-13 20:41:59 +02:00
|
|
|
private void updateAll(Player player) {
|
|
|
|
player.getInventory().update();
|
|
|
|
update(player);
|
2019-08-14 15:54:37 +02:00
|
|
|
}
|
2019-08-13 17:52:09 +02:00
|
|
|
}
|