Essentials/Essentials/src/main/java/com/earth2me/essentials/craftbukkit/Inventories.java

406 lines
15 KiB
Java

package com.earth2me.essentials.craftbukkit;
import com.earth2me.essentials.utils.MaterialUtil;
import com.earth2me.essentials.utils.VersionUtil;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
public final class Inventories {
private static final int HELM_SLOT = 39;
private static final int CHEST_SLOT = 38;
private static final int LEG_SLOT = 37;
private static final int BOOT_SLOT = 36;
private static final boolean HAS_OFFHAND = VersionUtil.getServerBukkitVersion().isHigherThanOrEqualTo(VersionUtil.v1_9_R01);
private static final int INVENTORY_SIZE = HAS_OFFHAND ? 41 : 40;
private Inventories() {
}
public static ItemStack getItemInHand(final Player player) {
if (!HAS_OFFHAND) {
//noinspection deprecation
return player.getInventory().getItemInHand();
}
final PlayerInventory inventory = player.getInventory();
final ItemStack main = inventory.getItemInMainHand();
return !isEmpty(main) ? main : inventory.getItemInOffHand();
}
public static ItemStack getItemInMainHand(final Player player) {
if (!HAS_OFFHAND) {
//noinspection deprecation
return player.getInventory().getItemInHand();
}
return player.getInventory().getItemInMainHand();
}
public static void setItemInMainHand(final Player player, final ItemStack stack) {
if (HAS_OFFHAND) {
player.getInventory().setItemInMainHand(stack);
} else {
//noinspection deprecation
player.setItemInHand(stack);
}
}
public static void setItemInMainHand(final EntityEquipment entityEquipment, final ItemStack stack) {
if (HAS_OFFHAND) {
entityEquipment.setItemInMainHand(stack);
} else {
//noinspection deprecation
entityEquipment.setItemInHand(stack);
}
}
public static void setItemInMainHandDropChance(final EntityEquipment entityEquipment, final float chance) {
if (HAS_OFFHAND) {
entityEquipment.setItemInMainHandDropChance(chance);
} else {
//noinspection deprecation
entityEquipment.setItemInHandDropChance(chance);
}
}
public static boolean containsAtLeast(final Player player, final ItemStack item, int amount) {
for (final ItemStack invItem : player.getInventory().getContents()) {
if (isEmpty(invItem)) {
continue;
}
if (invItem.isSimilar(item)) {
amount -= invItem.getAmount();
if (amount <= 0) {
return true;
}
}
}
return false;
}
public static boolean hasSpace(final Player player, final int maxStack, final boolean includeArmor, ItemStack... items) {
items = normalizeItems(cloneItems(items));
final InventoryData inventoryData = parseInventoryData(player.getInventory(), items, maxStack, includeArmor);
final List<Integer> emptySlots = inventoryData.getEmptySlots();
for (final ItemStack item : items) {
if (isEmpty(item)) {
continue;
}
final int itemMax = Math.max(maxStack, item.getMaxStackSize());
final List<Integer> partialSlots = inventoryData.getPartialSlots().get(item);
while (true) {
if (partialSlots == null || partialSlots.isEmpty()) {
if (emptySlots.isEmpty()) {
return false;
}
emptySlots.remove(0);
if (item.getAmount() > itemMax) {
item.setAmount(item.getAmount() - itemMax);
} else {
break;
}
} else {
final int slot = partialSlots.remove(0);
ItemStack existing = player.getInventory().getItem(slot);
if (isEmpty(existing)) {
existing = item.clone();
existing.setAmount(0);
}
final int amount = item.getAmount();
final int existingAmount = existing.getAmount();
if (amount + existingAmount <= itemMax) {
break;
} else {
item.setAmount(amount + existingAmount - itemMax);
}
}
}
}
return true;
}
public static Map<Integer, ItemStack> addItem(final Player player, final ItemStack... items) {
return addItem(player, 0, false, items);
}
public static Map<Integer, ItemStack> addItem(final Player player, final int maxStack, final ItemStack... items) {
return addItem(player, maxStack, false, items);
}
public static Map<Integer, ItemStack> addItem(final Player player, final int maxStack, final boolean allowArmor, ItemStack... items) {
items = normalizeItems(cloneItems(items));
final Map<Integer, ItemStack> leftover = new HashMap<>();
final InventoryData inventoryData = parseInventoryData(player.getInventory(), items, maxStack, allowArmor);
final List<Integer> emptySlots = inventoryData.getEmptySlots();
for (int i = 0; i < items.length; i++) {
final ItemStack item = items[i];
if (isEmpty(item)) {
continue;
}
final int itemMax = Math.max(maxStack, item.getMaxStackSize());
final List<Integer> partialSlots = inventoryData.getPartialSlots().get(item);
while (true) {
if (partialSlots == null || partialSlots.isEmpty()) {
if (emptySlots.isEmpty()) {
leftover.put(i, item);
break;
}
final int slot = emptySlots.remove(0);
if (item.getAmount() > itemMax) {
final ItemStack split = item.clone();
split.setAmount(itemMax);
player.getInventory().setItem(slot, split);
item.setAmount(item.getAmount() - itemMax);
} else {
player.getInventory().setItem(slot, item);
break;
}
} else {
final int slot = partialSlots.remove(0);
ItemStack existing = player.getInventory().getItem(slot);
if (isEmpty(existing)) {
existing = item.clone();
existing.setAmount(0);
}
final int amount = item.getAmount();
final int existingAmount = existing.getAmount();
if (amount + existingAmount <= itemMax) {
existing.setAmount(amount + existingAmount);
player.getInventory().setItem(slot, existing);
break;
} else {
existing.setAmount(itemMax);
player.getInventory().setItem(slot, existing);
item.setAmount(amount + existingAmount - itemMax);
}
}
}
}
return leftover;
}
public static ItemStack[] getInventory(final Player player, final boolean includeArmor) {
final ItemStack[] items = new ItemStack[INVENTORY_SIZE];
for (int i = 0; i < items.length; i++) {
if (!includeArmor && isArmorSlot(i)) {
items[i] = null;
continue;
}
items[i] = player.getInventory().getItem(i);
}
return items;
}
public static void removeItemExact(final Player player, final ItemStack toRemove, final boolean includeArmor) {
removeItems(player, itemStack -> itemStack.equals(toRemove), includeArmor);
}
public static int removeItemSimilar(final Player player, final ItemStack toRemove, final boolean includeArmor) {
return removeItems(player, itemStack -> itemStack.isSimilar(toRemove), includeArmor);
}
public static int removeItems(final Player player, final Predicate<ItemStack> removePredicate, final boolean includeArmor) {
int removedAmount = 0;
final ItemStack[] items = player.getInventory().getContents();
for (int i = 0; i < items.length; i++) {
if (!includeArmor && isArmorSlot(i)) {
continue;
}
final ItemStack item = items[i];
if (isEmpty(item)) {
continue;
}
if (removePredicate.test(item)) {
removedAmount += item.getAmount();
item.setAmount(0);
player.getInventory().setItem(i, item);
}
}
return removedAmount;
}
public static boolean removeItemAmount(final Player player, final ItemStack toRemove, int amount) {
final List<Integer> clearSlots = new ArrayList<>();
final ItemStack[] items = player.getInventory().getContents();
for (int i = 0; i < items.length; i++) {
final ItemStack item = items[i];
if (isEmpty(item)) {
continue;
}
if (item.isSimilar(toRemove)) {
if (item.getAmount() >= amount) {
item.setAmount(item.getAmount() - amount);
player.getInventory().setItem(i, item);
for (final int slot : clearSlots) {
clearSlot(player, slot);
}
return true;
} else {
amount -= item.getAmount();
clearSlots.add(i);
}
if (amount == 0) {
for (final int slot : clearSlots) {
clearSlot(player, slot);
}
return true;
}
}
}
return false;
}
public static void clearSlot(final Player player, final int slot) {
final ItemStack item = player.getInventory().getItem(slot);
if (!isEmpty(item)) {
item.setAmount(0);
player.getInventory().setItem(slot, item);
}
}
public static void setSlot(final Player inventory, final int slot, final ItemStack item) {
inventory.getInventory().setItem(slot, item);
}
private static ItemStack[] normalizeItems(final ItemStack[] items) {
if (items.length <= 1) {
return items;
}
final ItemStack[] normalizedItems = new ItemStack[items.length];
int nextNormalizedIndex = 0;
inputLoop:
for (final ItemStack item : items) {
if (isEmpty(item)) {
continue;
}
for (int j = 0; j < nextNormalizedIndex; j++) {
final ItemStack normalizedItem = normalizedItems[j];
if (isEmpty(normalizedItem)) {
continue;
}
if (item.isSimilar(normalizedItem)) {
normalizedItem.setAmount(normalizedItem.getAmount() + item.getAmount());
continue inputLoop;
}
}
normalizedItems[nextNormalizedIndex++] = item;
}
return normalizedItems;
}
private static ItemStack[] cloneItems(final ItemStack[] items) {
final ItemStack[] clonedItems = new ItemStack[items.length];
for (int i = 0; i < items.length; i++) {
final ItemStack item = items[i];
if (isEmpty(item)) {
continue;
}
clonedItems[i] = item.clone();
}
return clonedItems;
}
private static InventoryData parseInventoryData(final Inventory inventory, final ItemStack[] items, final int maxStack, final boolean includeArmor) {
final ItemStack[] inventoryContents = inventory.getContents();
final List<Integer> emptySlots = new ArrayList<>();
final HashMap<ItemStack, List<Integer>> partialSlots = new HashMap<>();
for (int i = 0; i < inventoryContents.length; i++) {
if (!includeArmor && isArmorSlot(i)) {
continue;
}
final ItemStack invItem = inventoryContents[i];
if (isEmpty(invItem)) {
emptySlots.add(i);
} else {
for (final ItemStack newItem : items) {
if (invItem.getAmount() < Math.max(maxStack, invItem.getMaxStackSize()) && invItem.isSimilar(newItem)) {
partialSlots.computeIfAbsent(newItem, k -> new ArrayList<>()).add(i);
}
}
}
}
// Convert empty armor slots to partial slots if we have armor items in the inventory, otherwise remove them from the empty slots.
if (includeArmor) {
ItemStack helm = null;
ItemStack chest = null;
ItemStack legs = null;
ItemStack boots = null;
for (final ItemStack item : items) {
if (isEmpty(item)) {
continue;
}
if (helm == null && MaterialUtil.isHelmet(item.getType())) {
helm = item;
if (emptySlots.contains(HELM_SLOT)) {
partialSlots.computeIfAbsent(helm, k -> new ArrayList<>()).add(HELM_SLOT);
}
} else if (chest == null && MaterialUtil.isChestplate(item.getType())) {
chest = item;
if (emptySlots.contains(CHEST_SLOT)) {
partialSlots.computeIfAbsent(chest, k -> new ArrayList<>()).add(CHEST_SLOT);
}
} else if (legs == null && MaterialUtil.isLeggings(item.getType())) {
legs = item;
if (emptySlots.contains(LEG_SLOT)) {
partialSlots.computeIfAbsent(legs, k -> new ArrayList<>()).add(LEG_SLOT);
}
} else if (boots == null && MaterialUtil.isBoots(item.getType())) {
boots = item;
if (emptySlots.contains(BOOT_SLOT)) {
partialSlots.computeIfAbsent(boots, k -> new ArrayList<>()).add(BOOT_SLOT);
}
}
}
emptySlots.remove((Object) HELM_SLOT);
emptySlots.remove((Object) CHEST_SLOT);
emptySlots.remove((Object) LEG_SLOT);
emptySlots.remove((Object) BOOT_SLOT);
}
return new InventoryData(emptySlots, partialSlots);
}
private static boolean isEmpty(final ItemStack stack) {
return stack == null || MaterialUtil.isAir(stack.getType());
}
private static boolean isArmorSlot(final int slot) {
return slot == HELM_SLOT || slot == CHEST_SLOT || slot == LEG_SLOT || slot == BOOT_SLOT;
}
}