ExcellentEnchants-spigot/Core/src/main/java/su/nightexpress/excellentenchants/enchantment/util/EnchantUtils.java

360 lines
15 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package su.nightexpress.excellentenchants.enchantment.util;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import su.nexmedia.engine.utils.CollectionsUtil;
import su.nexmedia.engine.utils.EntityUtil;
import su.nexmedia.engine.utils.ItemUtil;
import su.nexmedia.engine.utils.PDCUtil;
import su.nexmedia.engine.utils.random.Rnd;
import su.nightexpress.excellentenchants.ExcellentEnchantsAPI;
import su.nightexpress.excellentenchants.api.enchantment.IEnchantment;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant;
import su.nightexpress.excellentenchants.config.Config;
import su.nightexpress.excellentenchants.config.ObtainSettings;
import su.nightexpress.excellentenchants.enchantment.EnchantPopulator;
import su.nightexpress.excellentenchants.enchantment.EnchantRegistry;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.type.ObtainType;
import su.nightexpress.excellentenchants.tier.Tier;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class EnchantUtils {
public static final NamespacedKey KEY_LORE_SIZE = new NamespacedKey(ExcellentEnchantsAPI.PLUGIN, "lore_size");
public static boolean isEnchantable(@NotNull ItemStack item) {
if (item.getType().isAir()) return false;
return item.getType() == Material.ENCHANTED_BOOK || Stream.of(EnchantmentTarget.values()).anyMatch(target -> target.includes(item));
}
public static boolean populate(@NotNull ItemStack item, @NotNull ObtainType obtainType) {
int enchantsHad = getAmount(item);
getPopulationCandidates(item, obtainType).forEach((enchantment, level) -> {
add(item, enchantment, level, false);
});
updateDisplay(item);
return getAmount(item) != enchantsHad;
}
@NotNull
public static Set<ExcellentEnchant> populateFilter(@NotNull Set<ExcellentEnchant> enchants,
@NotNull ObtainType obtainType, @Nullable ItemStack item) {
Set<ExcellentEnchant> set = enchants.stream()
.filter(enchant -> enchant.getObtainChance(obtainType) > 0)
.filter(enchant -> item == null || enchant.canEnchantItem(item))
.collect(Collectors.toCollection(HashSet::new));
set.removeIf(enchant -> obtainType == ObtainType.ENCHANTING && (enchant.isTreasure() || enchant.isCursed()));
return set;
}
@NotNull
public static Map<Enchantment, Integer> getPopulationCandidates(@NotNull ItemStack item, @NotNull ObtainType obtainType) {
return getPopulationCandidates(item, obtainType, new HashMap<>(), (enchant) -> enchant.generateLevel(obtainType));
}
@NotNull
public static Map<Enchantment, Integer> getPopulationCandidates(@NotNull ItemStack item, @NotNull ObtainType obtainType,
@NotNull Map<Enchantment, Integer> enchantsPrepared,
@NotNull Function<ExcellentEnchant, Integer> levelFunc) {
Map<Enchantment, Integer> enchantsToAdd = new HashMap<>(enchantsPrepared);
ObtainSettings settings = Config.getObtainSettings(obtainType).orElse(null);
if (settings == null || !Rnd.chance(settings.getEnchantsCustomGenerationChance())) return enchantsToAdd;
int enchMax = settings.getEnchantsTotalMax();
int enchRoll = Rnd.get(settings.getEnchantsCustomMin(), settings.getEnchantsCustomMax());
// Класс для исключения неудачных попыток.
EnchantPopulator populator = new EnchantPopulator(obtainType, item);
// Добавляем сколько можем, пока нужное количество не будет добавлено или не закончатся чары и/или тиры.
while (!populator.isEmpty() && enchRoll > 0) {
// Достигнут максимум чар (любых) для итема, заканчиваем.
if (enchantsToAdd.size() >= enchMax) break;
Tier tier = populator.getTierByChance();
if (tier == null) break; // Нет тира?
ExcellentEnchant enchant = populator.getEnchantByChance(tier);
// В тире нет подходящих чар (вообще) для итема, исключаем и идем дальше.
if (enchant == null) {
populator.getEnchants().remove(tier);
continue;
}
// Среди уже добавленных чар есть конфликты с тем, что нашли.
// Исключаем, идем дальше.
if (enchantsToAdd.keySet().stream().anyMatch(has -> has.conflictsWith(enchant) || enchant.conflictsWith(has))) {
populator.getEnchants(tier).remove(enchant);
continue;
}
// Не получилось сгенерировать подходящий уровень.
// Исключаем, идем дальше.
int level = levelFunc.apply(enchant);
if (level < enchant.getStartLevel()) {
populator.getEnchants(tier).remove(enchant);
continue;
}
// Добавляем чар, засчитываем попытку.
populator.getEnchants(tier).remove(enchant);
enchantsToAdd.put(enchant, level);
enchRoll--;
}
return enchantsToAdd;
}
public static boolean add(@NotNull ItemStack item, @NotNull Enchantment enchantment, int level, boolean force) {
if (!force && !enchantment.canEnchantItem(item)) return false;
remove(item, enchantment);
ItemMeta meta = item.getItemMeta();
if (meta == null) return false;
if (meta instanceof EnchantmentStorageMeta storageMeta) {
if (!storageMeta.addStoredEnchant(enchantment, level, true)) return false;
}
else {
if (!meta.addEnchant(enchantment, level, true)) return false;
}
item.setItemMeta(meta);
return true;
}
public static void remove(@NotNull ItemStack item, @NotNull Enchantment enchantment) {
ItemMeta meta = item.getItemMeta();
if (meta instanceof EnchantmentStorageMeta storageMeta) {
storageMeta.removeStoredEnchant(enchantment);
}
else {
meta.removeEnchant(enchantment);
}
item.setItemMeta(meta);
}
public static boolean updateDisplay(@NotNull ItemStack item) {
if (Config.ENCHANTMENTS_DISPLAY_MODE.get() != 1) return false;
if (!isEnchantable(item)) {
PDCUtil.remove(item, KEY_LORE_SIZE);
return false;
}
Map<ExcellentEnchant, Integer> enchants = getExcellents(item);
int sizeHas = PDCUtil.getInt(item, KEY_LORE_SIZE).orElse(0);
int sizeReal = enchants.size();
ItemMeta meta = item.getItemMeta();
if (meta == null) return false;
List<String> lore = meta.getLore() == null ? new ArrayList<>() : meta.getLore();
for (int index = 0; index < sizeHas && !lore.isEmpty(); index++) {
lore.remove(0);
}
//lore.removeIf(str -> enchants.keySet().stream().anyMatch(enchant -> str.contains(enchant.getDisplayName())));
if (!meta.hasItemFlag(ItemFlag.HIDE_ENCHANTS)) {
if (Config.ENCHANTMENTS_DESCRIPTION_ENABLED.get()) {
enchants.forEach((enchant, level) -> {
lore.addAll(0, enchant.formatDescription(level));
});
sizeReal += enchants.keySet().stream().map(ExcellentEnchant::getDescription).mapToInt(List::size).sum();
}
enchants.forEach((enchant, level) -> {
lore.add(0, enchant.getNameFormatted(level, getCharges(meta, enchant)));
});
}
else sizeReal = 0;
meta.setLore(lore);
if (sizeReal > 0) {
PDCUtil.set(meta, KEY_LORE_SIZE, sizeReal);
}
item.setItemMeta(meta);
return true;
}
@NotNull
public static Map<Enchantment, Integer> getAll(@NotNull ItemStack item) {
ItemMeta meta = item.getItemMeta();
return meta == null ? Collections.emptyMap() : getAll(meta);
}
@NotNull
public static Map<Enchantment, Integer> getAll(@NotNull ItemMeta meta) {
return (meta instanceof EnchantmentStorageMeta meta2) ? meta2.getStoredEnchants() : meta.getEnchants();
}
public static int getAmount(@NotNull ItemStack item) {
return getAll(item).size();
}
public static boolean contains(@NotNull ItemStack item, @NotNull Enchantment enchantment) {
return getLevel(item, enchantment) > 0;
}
public static int getLevel(@NotNull ItemStack item, @NotNull Enchantment enchant) {
return getAll(item).getOrDefault(enchant, 0);
}
public static int getCharges(@NotNull ItemStack item, @NotNull ExcellentEnchant enchant) {
return enchant.isChargesEnabled() ? PDCUtil.getInt(item, enchant.getChargesKey()).orElse(0) : -1;
}
public static int getCharges(@NotNull ItemMeta meta, @NotNull ExcellentEnchant enchant) {
return enchant.isChargesEnabled() ? PDCUtil.getInt(meta, enchant.getChargesKey()).orElse(0) : -1;
}
public static boolean isOutOfCharges(@NotNull ItemStack item, @NotNull ExcellentEnchant enchant) {
return enchant.isChargesEnabled() && getCharges(item, enchant) == 0;
}
public static boolean isFullOfCharges(@NotNull ItemStack item, @NotNull ExcellentEnchant enchant) {
if (!enchant.isChargesEnabled()) return true;
int level = getLevel(item, enchant);
int max = enchant.getChargesMax(level);
return getCharges(item, enchant) == max;
}
public static void consumeCharges(@NotNull ItemStack item, @NotNull ExcellentEnchant enchant) {
if (!enchant.isChargesEnabled()) return;
int level = getLevel(item, enchant);
int has = getCharges(item, enchant);
int use = enchant.getChargesConsumeAmount(level);
setCharges(item, enchant, has - use);
}
public static void restoreCharges(@NotNull ItemStack item, @NotNull ExcellentEnchant enchant) {
if (!enchant.isChargesEnabled()) return;
int level = getLevel(item, enchant);
int max = enchant.getChargesMax(level);
setCharges(item, enchant, max);
}
public static void rechargeCharges(@NotNull ItemStack item, @NotNull ExcellentEnchant enchant) {
if (!enchant.isChargesEnabled()) return;
int level = getLevel(item, enchant);
int recharge = enchant.getChargesRechargeAmount(level);
int has = getCharges(item, enchant);
setCharges(item, enchant, has + recharge);
}
public static void setCharges(@NotNull ItemStack item, @NotNull ExcellentEnchant enchant, int charges) {
if (!enchant.isChargesEnabled()) return;
int level = getLevel(item, enchant);
int max = enchant.getChargesMax(level);
PDCUtil.set(item, enchant.getChargesKey(), Math.max(0, Math.min(charges, max)));
}
public static int getExcellentAmount(@NotNull ItemStack item) {
return getExcellents(item).size();
}
@NotNull
public static Map<ExcellentEnchant, Integer> getExcellents(@NotNull ItemStack item) {
return getExcellents(getAll(item));
}
@NotNull
public static Map<ExcellentEnchant, Integer> getExcellents(@NotNull ItemMeta meta) {
return getExcellents(getAll(meta));
}
@NotNull
private static Map<ExcellentEnchant, Integer> getExcellents(@NotNull Map<Enchantment, Integer> enchants) {
Map<ExcellentEnchant, Integer> map = new HashMap<>();
enchants.forEach((enchantment, level) -> {
ExcellentEnchant excellent = EnchantRegistry.get(enchantment.getKey());
if (excellent != null) {
map.put(excellent, level);
}
});
return map;
}
@NotNull
public static <T extends IEnchantment> Map<T, Integer> getExcellents(@NotNull ItemStack item, @NotNull Class<T> clazz) {
Map<T, Integer> map = new HashMap<>();
getAll(item).forEach((enchantment, level) -> {
ExcellentEnchant excellent = EnchantRegistry.get(enchantment.getKey());
if (excellent == null || !clazz.isAssignableFrom(excellent.getClass())) return;
map.put(clazz.cast(excellent), level);
});
return CollectionsUtil.sort(map, Comparator.comparing(p -> p.getKey().getPriority(), Comparator.reverseOrder()));
}
@NotNull
public static Map<EquipmentSlot, ItemStack> getEnchantedEquipment(@NotNull LivingEntity entity) {
Map<EquipmentSlot, ItemStack> equipment = EntityUtil.getEquippedItems(entity);
equipment.entrySet().removeIf(entry -> {
ItemStack item = entry.getValue();
EquipmentSlot slot = entry.getKey();
if (item == null || item.getType().isAir() || item.getType() == Material.ENCHANTED_BOOK) return true;
if ((slot == EquipmentSlot.HAND || slot == EquipmentSlot.OFF_HAND) && ItemUtil.isArmor(item)) return true;
return !item.hasItemMeta();
});
return equipment;
}
@NotNull
public static Map<ItemStack, Map<ExcellentEnchant, Integer>> getEquipped(@NotNull LivingEntity entity) {
Map<ItemStack, Map<ExcellentEnchant, Integer>> map = new HashMap<>();
getEnchantedEquipment(entity).values().forEach(item -> {
map.computeIfAbsent(item, k -> new LinkedHashMap<>()).putAll(getExcellents(item));
});
return map;
}
@NotNull
public static <T extends IEnchantment> Map<ItemStack, Map<T, Integer>> getEquipped(@NotNull LivingEntity entity,
@NotNull Class<T> clazz) {
Map<ItemStack, Map<T, Integer>> map = new HashMap<>();
getEnchantedEquipment(entity).values().forEach(item -> {
map.computeIfAbsent(item, k -> new LinkedHashMap<>()).putAll(getExcellents(item, clazz));
});
return map;
}
public static void updateEquippedEffects(@NotNull LivingEntity entity) {
getEquipped(entity, PassiveEnchant.class).forEach((item, enchants) -> {
enchants.forEach((enchant, level) -> {
if (enchant instanceof Potioned potioned) {
if (enchant.isOutOfCharges(item)) return;
if (enchant.onTrigger(entity, item, level)) {
enchant.consumeCharges(item);
}
}
});
});
}
}