All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.handyplus.lib.util.ItemStackUtil Maven / Gradle / Ivy

The newest version!
package cn.handyplus.lib.util;

import cn.handyplus.lib.adapter.HandySchedulerUtil;
import cn.handyplus.lib.constants.BaseConstants;
import cn.handyplus.lib.constants.VersionCheckEnum;
import cn.handyplus.lib.core.CollUtil;
import cn.handyplus.lib.core.MapUtil;
import cn.handyplus.lib.core.StrUtil;
import cn.handyplus.lib.expand.XMaterial;
import org.bukkit.Material;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.material.MaterialData;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;

/**
 * ItemStack工具类
 *
 * @author handy
 * @since 1.1.8
 */
public class ItemStackUtil {

    private ItemStackUtil() {
    }

    /**
     * 序列化itemStack为String
     *
     * @param itemStack 物品
     * @return String
     */
    public static String itemStackSerialize(ItemStack itemStack) {
        YamlConfiguration yml = new YamlConfiguration();
        yml.set("item", itemStack);
        return yml.saveToString();
    }

    /**
     * 反序列化String为itemStack
     *
     * @param str 物品str
     * @return ItemStack
     */
    public static ItemStack itemStackDeserialize(String str) {
        return itemStackDeserialize(str, Material.AIR);
    }

    /**
     * 反序列化String为itemStack
     *
     * @param str      物品str
     * @param material 解析异常返回的默认物品
     * @return ItemStack
     * @since 3.6.9
     */
    public static ItemStack itemStackDeserialize(String str, Material material) {
        YamlConfiguration yml = new YamlConfiguration();
        ItemStack item;
        try {
            yml.loadFromString(str);
            item = yml.getItemStack("item", new ItemStack(material));
        } catch (Exception ex) {
            item = new ItemStack(material);
        }
        return item;
    }

    /**
     * 物品生成
     *
     * @param material 材质
     * @return 自定义物品
     * @since 2.0.3
     */
    public static ItemStack getItemStack(String material) {
        return getItemStack(material, null, null);
    }

    /**
     * 物品生成
     *
     * @param material    材质
     * @param displayName 名称
     * @return 自定义物品
     */
    public static ItemStack getItemStack(String material, String displayName) {
        return getItemStack(material, displayName, null);
    }

    /**
     * 物品生成
     *
     * @param material    材质
     * @param displayName 名称
     * @param loreList    lore
     * @return 自定义物品
     */
    public static ItemStack getItemStack(String material, String displayName, List loreList) {
        return getItemStack(material, displayName, loreList, false);
    }

    /**
     * 物品生成
     *
     * @param material    材质
     * @param displayName 名称
     * @param loreList    lore
     * @param isEnchant   附魔效果
     * @return 自定义物品
     */
    public static ItemStack getItemStack(String material, String displayName, List loreList, Boolean isEnchant) {
        return getItemStack(material, displayName, loreList, isEnchant, 0);
    }

    /**
     * 物品生成
     *
     * @param material        材质
     * @param displayName     名称
     * @param loreList        lore
     * @param isEnchant       附魔效果
     * @param customModelData 自定义模型id
     * @return 自定义物品
     * @since 2.0.3
     */
    public static ItemStack getItemStack(String material, String displayName, List loreList, Boolean isEnchant, int customModelData) {
        return getItemStack(material, displayName, loreList, isEnchant, customModelData, true);
    }

    /**
     * 物品生成
     *
     * @param material        材质
     * @param displayName     名称
     * @param loreList        lore
     * @param isEnchant       附魔效果
     * @param customModelData 自定义模型id
     * @param hideFlag        隐藏标签
     * @return 自定义物品
     * @since 2.1.7
     */
    public static ItemStack getItemStack(String material, String displayName, List loreList, Boolean isEnchant, int customModelData, boolean hideFlag) {
        return getItemStack(material, displayName, loreList, isEnchant, customModelData, hideFlag, null);
    }

    /**
     * 物品生成
     *
     * @param material        材质名称
     * @param displayName     名称
     * @param loreList        lore
     * @param isEnchant       附魔效果
     * @param customModelData 自定义模型id
     * @param hideFlag        隐藏标签
     * @param replaceMap      lore替换map
     * @return 自定义物品
     * @since 2.3.5
     */
    public static ItemStack getItemStack(String material, String displayName, List loreList, Boolean isEnchant, int customModelData, boolean hideFlag, Map replaceMap) {
        return getItemStack(material, displayName, loreList, isEnchant, customModelData, hideFlag, replaceMap, true);
    }

    /**
     * 物品生成
     *
     * @param material        材质
     * @param displayName     名称
     * @param loreList        lore
     * @param isEnchant       附魔效果
     * @param customModelData 自定义模型id
     * @param hideFlag        隐藏标签
     * @param replaceMap      lore替换map
     * @param hideEnchant     隐藏附魔效果
     * @return 自定义物品
     * @since 2.6.6
     */
    public static ItemStack getItemStack(String material, String displayName, List loreList, boolean isEnchant, int customModelData, boolean hideFlag, Map replaceMap, boolean hideEnchant) {
        return getItemStack(material, displayName, loreList, isEnchant, customModelData, hideFlag, replaceMap, hideEnchant, null);
    }

    /**
     * 物品生成
     *
     * @param material        材质
     * @param displayName     名称
     * @param loreList        lore
     * @param isEnchant       附魔效果
     * @param customModelData 自定义模型id
     * @param hideFlag        隐藏标签
     * @param replaceMap      lore替换map
     * @param hideEnchant     隐藏附魔效果
     * @param customData      自定义数据
     * @return 自定义物品
     * @since 3.5.3
     */
    public static ItemStack getItemStack(String material, String displayName, List loreList, boolean isEnchant, int customModelData, boolean hideFlag, Map replaceMap, boolean hideEnchant, String customData) {
        // 生成item物品
        ItemStack itemStack = getItemByMaterial(material);
        ItemMeta itemMeta = getItemMeta(itemStack);
        if (StrUtil.isNotEmpty(displayName)) {
            itemMeta.setDisplayName(BaseUtil.replaceChatColor(displayName));
        }
        if (CollUtil.isNotEmpty(loreList)) {
            itemMeta.setLore(BaseUtil.replaceChatColor(loreReplaceMap(loreList, replaceMap)));
        }
        // 附魔效果
        if (isEnchant) {
            ItemMetaUtil.setEnchant(itemMeta);
        }
        // 隐藏附魔效果
        if (hideEnchant) {
            ItemMetaUtil.hideEnchant(itemMeta);
        }
        // 隐藏物品属性
        if (hideFlag) {
            ItemMetaUtil.hideAttributes(itemMeta);
        }
        // 设置自定义数据
        if (StrUtil.isNotEmpty(customData)) {
            ItemMetaUtil.setPersistentData(itemMeta, customData, "system");
        }
        // 模型效果
        if (customModelData > 0) {
            ItemMetaUtil.setCustomModelData(itemMeta, customModelData);
        }
        itemStack.setItemMeta(itemMeta);
        return itemStack;
    }

    /**
     * 物品减少计算
     *
     * @param player    玩家
     * @param itemStack 指定物品
     * @param amount    数量
     * @return 是否成功扣除
     * @since 3.10.6
     */
    public static Boolean removeItem(Player player, ItemStack itemStack, Integer amount) {
        return removeItem(player, itemStack, amount, true);
    }

    /**
     * 物品减少计算
     *
     * @param player    玩家
     * @param itemStack 指定物品
     * @param amount    数量
     * @param strict    严格模式 true是走原版isSimilar ,false走简单对比
     * @return 是否成功扣除
     * @since 3.10.6
     */
    public static Boolean removeItem(Player player, ItemStack itemStack, Integer amount, boolean strict) {
        if (player == null || !player.isOnline()) {
            return false;
        }
        PlayerInventory playerInventory = player.getInventory();
        ItemStack[] contents;
        if (BaseConstants.VERSION_ID <= VersionCheckEnum.V_1_8_8.getVersionId()) {
            contents = playerInventory.getContents();
        } else {
            contents = playerInventory.getStorageContents();
        }
        int num = 0;
        List items = new ArrayList<>();
        for (ItemStack item : contents) {
            if (item == null || Material.AIR.equals(item.getType())) {
                continue;
            }
            // 严格模式
            if (strict) {
                if (!item.isSimilar(itemStack)) {
                    continue;
                }
            } else {
                if (!isSimilar(item, itemStack)) {
                    continue;
                }
            }
            num += item.getAmount();
            items.add(item);
            // 如果数量够了就不继续循环了
            if (num >= amount) {
                break;
            }
        }
        if (num == amount) {
            for (ItemStack item : items) {
                playerInventory.removeItem(item);
            }
            return true;
        }
        if (num > amount) {
            for (ItemStack item : items) {
                if (amount == 0) {
                    return true;
                }
                if (amount >= item.getAmount()) {
                    amount = amount - item.getAmount();
                    playerInventory.removeItem(item);
                } else {
                    item.setAmount(item.getAmount() - amount);
                    amount = 0;
                }
            }
            return true;
        }
        return false;
    }

    /**
     * 物品包含判断
     *
     * @param playerInventory 玩家背包
     * @param itemStack       指定物品
     * @param amount          数量
     * @param strict          严格模式 true是走原版isSimilar ,false走简单对比
     * @return true 包含
     * @since 3.6.1
     */
    public static Boolean containsItem(PlayerInventory playerInventory, ItemStack itemStack, Integer amount, boolean strict) {
        ItemStack[] contents;
        if (BaseConstants.VERSION_ID <= VersionCheckEnum.V_1_8_8.getVersionId()) {
            contents = playerInventory.getContents();
        } else {
            contents = playerInventory.getStorageContents();
        }
        int num = 0;
        for (ItemStack item : contents) {
            if (item == null || Material.AIR.equals(item.getType())) {
                continue;
            }
            // 严格模式
            if (strict) {
                if (!item.isSimilar(itemStack)) {
                    continue;
                }
            } else {
                if (!isSimilar(item, itemStack)) {
                    continue;
                }
            }
            num += item.getAmount();
            // 如果数量够了就不继续循环了
            if (num >= amount) {
                break;
            }
        }
        return num >= amount;
    }

    /**
     * 进行物品发送
     *
     * @param player    玩家
     * @param itemStack 物品
     * @param amount    发送数量
     * @since 3.5.9
     */
    public static boolean addItem(Player player, ItemStack itemStack, int amount) {
        return addItem(player, itemStack, amount, null);
    }

    /**
     * 进行物品发送
     *
     * @param player    玩家
     * @param itemStack 物品
     * @param amount    发送数量
     * @param msg       提醒消息
     * @return true 有多余物品掉在地上
     * @since 3.5.9
     */
    public static boolean addItem(Player player, ItemStack itemStack, int amount, String msg) {
        // 给玩家添加物品
        Map dropItemMap = addItemReturnDropItemMap(player, itemStack, amount);
        if (dropItemMap.isEmpty()) {
            return false;
        }
        // 同步处理背包多余物品掉落在地上
        HandySchedulerUtil.runTask(() -> {
            for (Integer key : dropItemMap.keySet()) {
                player.getWorld().dropItem(player.getLocation(), dropItemMap.get(key));
            }
        });
        MessageUtil.sendMessage(player, msg);
        return true;
    }

    /**
     * 进行物品发送
     *
     * @param player    玩家
     * @param itemStack 物品
     * @param amount    发送数量
     * @since 3.7.9
     */
    public static Map addItemReturnDropItemMap(Player player, ItemStack itemStack, int amount) {
        PlayerInventory playerInventory = player.getInventory();
        Map dropItemMap = MapUtil.of();
        int maxStackSize = itemStack.getMaxStackSize();
        if (amount > maxStackSize) {
            // 如果发送数量大于最大上限
            itemStack.setAmount(maxStackSize);
            dropItemMap.putAll(playerInventory.addItem(itemStack));
            dropItemMap.putAll(addItemReturnDropItemMap(player, itemStack, amount - maxStackSize));
        } else {
            // 小于等于直接发送
            itemStack.setAmount(amount);
            dropItemMap.putAll(playerInventory.addItem(itemStack));
        }
        return dropItemMap;
    }

    /**
     * 获取材质
     *
     * @param materialStr 材质
     * @return Material
     */
    public static Material getMaterial(String materialStr) {
        return getMaterial(materialStr, Material.STONE);
    }

    /**
     * 获取材质
     *
     * @param materialStr     材质
     * @param defaultMaterial 未找到的的默认材质
     * @return Material
     */
    public static Material getMaterial(String materialStr, Material defaultMaterial) {
        if (StrUtil.isEmpty(materialStr)) {
            return defaultMaterial;
        }
        materialStr = materialStr.toUpperCase(Locale.ROOT);
        Material material = Material.getMaterial(materialStr);
        if (material != null) {
            return material;
        }
        if (BaseConstants.VERSION_ID > VersionCheckEnum.V_1_12.getVersionId()) {
            material = Material.getMaterial("LEGACY_" + materialStr, true);
            if (material != null) {
                return material;
            }
        }
        // 小于1.8处理
        if (BaseConstants.VERSION_ID < VersionCheckEnum.V_1_8.getVersionId()) {
            return defaultMaterial;
        }
        return XMaterial.matchXMaterial(materialStr).orElse(XMaterial.matchXMaterial(defaultMaterial)).parseMaterial();
    }

    /**
     * 获取材质的物品
     *
     * @param materialStr 材质
     * @return ItemStack
     * @since 3.5.3
     */
    public static ItemStack getItemByMaterial(String materialStr) {
        return getItemByMaterial(materialStr, Material.STONE);
    }

    /**
     * 获取对应材质的物品
     *
     * @param materialStr     材质
     * @param defaultMaterial 未找到的的默认材质
     * @return ItemStack
     * @since 3.5.3
     */
    public static ItemStack getItemByMaterial(String materialStr, Material defaultMaterial) {
        if (StrUtil.isEmpty(materialStr)) {
            return new ItemStack(defaultMaterial);
        }
        materialStr = materialStr.toUpperCase(Locale.ROOT);
        Material material = Material.getMaterial(materialStr);
        if (material != null) {
            return new ItemStack(material);
        }
        if (BaseConstants.VERSION_ID > VersionCheckEnum.V_1_12.getVersionId()) {
            material = Material.getMaterial("LEGACY_" + materialStr, true);
            if (material != null) {
                return new ItemStack(material);
            }
        }
        // 小于1.8处理
        if (BaseConstants.VERSION_ID < VersionCheckEnum.V_1_8.getVersionId()) {
            return new ItemStack(defaultMaterial);
        }
        return XMaterial.matchXMaterial(materialStr).orElse(XMaterial.matchXMaterial(defaultMaterial)).parseItem();
    }

    /**
     * 正确获取主手物品
     *
     * @param playerInventory 玩家背包
     * @return ItemStack
     * @since 1.6.0
     */
    @SuppressWarnings("deprecation")
    public static ItemStack getItemInMainHand(PlayerInventory playerInventory) {
        if (BaseConstants.VERSION_ID < VersionCheckEnum.V_1_9.getVersionId()) {
            return playerInventory.getItemInHand();
        }
        return playerInventory.getItemInMainHand();
    }

    /**
     * 正确获取主手物品
     *
     * @param playerInventory 玩家背包
     * @param itemStack       物品
     * @since 3.7.1
     */
    @SuppressWarnings("deprecation")
    public static void setItemInMainHand(PlayerInventory playerInventory, ItemStack itemStack) {
        if (BaseConstants.VERSION_ID < VersionCheckEnum.V_1_9.getVersionId()) {
            playerInventory.setItemInHand(itemStack);
            return;
        }
        playerInventory.setItemInMainHand(itemStack);
    }

    /**
     * 获取必定不为空的ItemMeta
     *
     * @param itemStack ItemStack
     * @return ItemMeta
     * @since 1.7.0
     */
    public static ItemMeta getItemMeta(ItemStack itemStack) {
        if (itemStack == null) {
            itemStack = new ItemStack(Material.STONE);
        }
        ItemMeta itemMeta = itemStack.getItemMeta();
        if (itemMeta == null) {
            return new ItemStack(Material.STONE).getItemMeta();
        }
        return itemMeta;
    }

    /**
     * 进行lore的变量替换
     *
     * @param loreList   原本lore
     * @param replaceMap 替换map
     * @return 新lore
     * @since 2.3.5
     */
    public static List loreReplaceMap(List loreList, Map replaceMap) {
        List newLoreList = new ArrayList<>();
        if (replaceMap != null && !replaceMap.isEmpty() && CollUtil.isNotEmpty(loreList)) {
            for (String lore : loreList) {
                for (String key : replaceMap.keySet()) {
                    if (lore.contains("${" + key + "}") && replaceMap.get(key) != null) {
                        lore = StrUtil.replace(lore, key, replaceMap.get(key));
                    }
                }
                newLoreList.add(lore);
            }
        } else {
            newLoreList.addAll(loreList);
        }
        return newLoreList;
    }

    /**
     * 进行lore的变量批量替换
     *
     * @param loreList   原本loreList
     * @param replaceMap 替换map
     * @param def        默认值
     * @return 新loreList
     * @since 2.3.5
     */
    public static List loreBatchReplaceMap(List loreList, Map> replaceMap, String def) {
        if (CollUtil.isEmpty(loreList) || replaceMap == null || replaceMap.isEmpty()) {
            return loreList;
        }
        List newLoreList = new ArrayList<>();
        for (String lore : loreList) {
            newLoreList.addAll(loreBatchReplaceMap(lore, replaceMap, def));
        }
        return newLoreList;
    }

    /**
     * 进行lore的变量批量替换
     *
     * @param lore       原本lore
     * @param replaceMap 替换map
     * @param def        默认值
     * @return 新lore
     * @since 2.3.5
     */
    public static List loreBatchReplaceMap(String lore, Map> replaceMap, String def) {
        List loreList = new ArrayList<>();
        if (StrUtil.isEmpty(lore)) {
            loreList.add(lore);
            return loreList;
        }
        if (replaceMap == null || replaceMap.isEmpty()) {
            loreList.add(lore);
            return loreList;
        }
        if (StrUtil.isEmpty(def)) {
            def = "";
        }
        for (String key : replaceMap.keySet()) {
            if (lore.contains("${" + key + "}")) {
                List replaceList = replaceMap.get(key);
                if (CollUtil.isEmpty(replaceList)) {
                    loreList.add(StrUtil.replace(lore, key, def));
                } else {
                    for (String replaceStr : replaceList) {
                        loreList.add(StrUtil.replace(lore, key, replaceStr));
                    }
                }
                break;
            }
        }
        if (CollUtil.isEmpty(loreList)) {
            loreList.add(lore);
        }
        return loreList;
    }

    /**
     * 设置数据容器
     *
     * @param itemStack  物品
     * @param customData 自定义数据
     * @param key        key
     * @since 3.1.3
     */
    public static void setPersistentData(ItemStack itemStack, String customData, String key) {
        ItemMeta itemMeta = itemStack.getItemMeta();
        // 设置数据
        ItemMetaUtil.setPersistentData(itemMeta, customData, key);
        // 重新设置回去
        itemStack.setItemMeta(itemMeta);
    }

    /**
     * 获取数据
     *
     * @param itemStack 物品
     * @param key       key
     * @return 自定义数据
     * @since 3.1.3
     */
    public static Optional getPersistentData(ItemStack itemStack, String key) {
        ItemMeta itemMeta = itemStack.getItemMeta();
        return ItemMetaUtil.getPersistentData(itemMeta, key);
    }

    /**
     * 简单判断物品是否相等
     * 1. 判断null
     * 2. 判断物品类型
     * 3. 判断物品名称
     * 4. 判断物品lore
     *
     * @param one 物品一
     * @param two 物品二
     * @return true相等
     * @since 3.3.7
     */
    public static boolean isSimilar(ItemStack one, ItemStack two) {
        if (one == two) {
            return true;
        }
        if (one == null || two == null) {
            return false;
        }
        // 1.判断类型
        if (!one.getType().equals(two.getType())) {
            return false;
        }
        // 2. 1.13以下判断物品数据是否相同(针对子ID)
        if (BaseConstants.VERSION_ID < VersionCheckEnum.V_1_13.getVersionId()) {
            MaterialData oneData = one.getData();
            MaterialData twoData = two.getData();
            if (oneData != null && twoData != null && oneData.getData() != twoData.getData()) {
                return false;
            }
        }
        // 3. 判断元数据是否存在
        ItemMeta oneItemMeta = one.getItemMeta();
        ItemMeta twoItemMeta = two.getItemMeta();
        if (oneItemMeta == twoItemMeta) {
            return true;
        }
        if (oneItemMeta == null || twoItemMeta == null) {
            return false;
        }
        // 4. 判断名称
        if (!StrUtil.equals(oneItemMeta.getDisplayName(), twoItemMeta.getDisplayName())) {
            return false;
        }
        // 5.判断lore
        return CollUtil.equals(oneItemMeta.getLore(), twoItemMeta.getLore());
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy