/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.google.common.collect.Lists * com.mojang.datafixers.util.Pair * it.unimi.dsi.fastutil.objects.Object2IntMap$Entry * org.apache.commons.lang3.mutable.MutableBoolean * org.apache.commons.lang3.mutable.MutableFloat * org.apache.commons.lang3.mutable.MutableObject * org.jspecify.annotations.Nullable */ package net.minecraft.world.item.enchantment; import com.google.common.collect.Lists; import com.mojang.datafixers.util.Pair; import it.unimi.dsi.fastutil.objects.Object2IntMap; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.core.RegistryAccess; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.TagKey; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.util.Util; import net.minecraft.util.random.WeightedRandom; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.EquipmentSlotGroup; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.Attribute; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.Enchantable; import net.minecraft.world.item.enchantment.EnchantedItemInUse; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.item.enchantment.EnchantmentEffectComponents; import net.minecraft.world.item.enchantment.EnchantmentInstance; import net.minecraft.world.item.enchantment.EnchantmentTarget; import net.minecraft.world.item.enchantment.ItemEnchantments; import net.minecraft.world.item.enchantment.effects.EnchantmentValueEffect; import net.minecraft.world.item.enchantment.providers.EnchantmentProvider; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.loot.LootContext; import net.minecraft.world.phys.Vec3; import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableFloat; import org.apache.commons.lang3.mutable.MutableObject; import org.jspecify.annotations.Nullable; public class EnchantmentHelper { public static int getItemEnchantmentLevel(Holder enchantment, ItemStack piece) { ItemEnchantments enchantments = piece.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY); return enchantments.getLevel(enchantment); } public static ItemEnchantments updateEnchantments(ItemStack itemStack, Consumer consumer) { DataComponentType componentType = EnchantmentHelper.getComponentType(itemStack); ItemEnchantments oldEnchantments = itemStack.get(componentType); if (oldEnchantments == null) { return ItemEnchantments.EMPTY; } ItemEnchantments.Mutable mutableEnchantments = new ItemEnchantments.Mutable(oldEnchantments); consumer.accept(mutableEnchantments); ItemEnchantments newEnchantments = mutableEnchantments.toImmutable(); itemStack.set(componentType, newEnchantments); return newEnchantments; } public static boolean canStoreEnchantments(ItemStack itemStack) { return itemStack.has(EnchantmentHelper.getComponentType(itemStack)); } public static void setEnchantments(ItemStack itemStack, ItemEnchantments enchantments) { itemStack.set(EnchantmentHelper.getComponentType(itemStack), enchantments); } public static ItemEnchantments getEnchantmentsForCrafting(ItemStack itemStack) { return itemStack.getOrDefault(EnchantmentHelper.getComponentType(itemStack), ItemEnchantments.EMPTY); } private static DataComponentType getComponentType(ItemStack itemStack) { return itemStack.is(Items.ENCHANTED_BOOK) ? DataComponents.STORED_ENCHANTMENTS : DataComponents.ENCHANTMENTS; } public static boolean hasAnyEnchantments(ItemStack itemStack) { return !itemStack.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY).isEmpty() || !itemStack.getOrDefault(DataComponents.STORED_ENCHANTMENTS, ItemEnchantments.EMPTY).isEmpty(); } public static int processDurabilityChange(ServerLevel serverLevel, ItemStack itemStack, int amount) { MutableFloat modifiedAmount = new MutableFloat((float)amount); EnchantmentHelper.runIterationOnItem(itemStack, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyDurabilityChange(serverLevel, level, itemStack, modifiedAmount)); return modifiedAmount.intValue(); } public static int processAmmoUse(ServerLevel serverLevel, ItemStack weapon, ItemStack ammo, int amount) { MutableFloat modifiedAmount = new MutableFloat((float)amount); EnchantmentHelper.runIterationOnItem(weapon, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyAmmoCount(serverLevel, level, ammo, modifiedAmount)); return modifiedAmount.intValue(); } public static int processBlockExperience(ServerLevel serverLevel, ItemStack itemStack, int amount) { MutableFloat modifiedAmount = new MutableFloat((float)amount); EnchantmentHelper.runIterationOnItem(itemStack, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyBlockExperience(serverLevel, level, itemStack, modifiedAmount)); return modifiedAmount.intValue(); } public static int processMobExperience(ServerLevel serverLevel, @Nullable Entity killer, Entity killed, int amount) { if (killer instanceof LivingEntity) { LivingEntity livingKiller = (LivingEntity)killer; MutableFloat modifiedAmount = new MutableFloat((float)amount); EnchantmentHelper.runIterationOnEquipment(livingKiller, (enchantment, level, item) -> ((Enchantment)enchantment.value()).modifyMobExperience(serverLevel, level, item.itemStack(), killed, modifiedAmount)); return modifiedAmount.intValue(); } return amount; } public static ItemStack createBook(EnchantmentInstance enchant) { ItemStack itemStack = new ItemStack(Items.ENCHANTED_BOOK); itemStack.enchant(enchant.enchantment(), enchant.level()); return itemStack; } private static void runIterationOnItem(ItemStack piece, EnchantmentVisitor method) { ItemEnchantments enchantments = piece.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY); for (Object2IntMap.Entry> entry : enchantments.entrySet()) { method.accept((Holder)entry.getKey(), entry.getIntValue()); } } private static void runIterationOnItem(ItemStack piece, EquipmentSlot slot, LivingEntity owner, EnchantmentInSlotVisitor method) { if (piece.isEmpty()) { return; } ItemEnchantments itemEnchantments = piece.get(DataComponents.ENCHANTMENTS); if (itemEnchantments == null || itemEnchantments.isEmpty()) { return; } EnchantedItemInUse itemInUse = new EnchantedItemInUse(piece, slot, owner); for (Object2IntMap.Entry> entry : itemEnchantments.entrySet()) { Holder enchantment = (Holder)entry.getKey(); if (!((Enchantment)enchantment.value()).matchingSlot(slot)) continue; method.accept(enchantment, entry.getIntValue(), itemInUse); } } private static void runIterationOnEquipment(LivingEntity owner, EnchantmentInSlotVisitor method) { for (EquipmentSlot slot : EquipmentSlot.VALUES) { EnchantmentHelper.runIterationOnItem(owner.getItemBySlot(slot), slot, owner, method); } } public static boolean isImmuneToDamage(ServerLevel serverLevel, LivingEntity victim, DamageSource source) { MutableBoolean result = new MutableBoolean(); EnchantmentHelper.runIterationOnEquipment(victim, (enchantment, level, item) -> result.setValue(result.isTrue() || ((Enchantment)enchantment.value()).isImmuneToDamage(serverLevel, level, victim, source))); return result.isTrue(); } public static float getDamageProtection(ServerLevel serverLevel, LivingEntity victim, DamageSource source) { MutableFloat result = new MutableFloat(0.0f); EnchantmentHelper.runIterationOnEquipment(victim, (enchantment, level, item) -> ((Enchantment)enchantment.value()).modifyDamageProtection(serverLevel, level, item.itemStack(), victim, source, result)); return result.floatValue(); } public static float modifyDamage(ServerLevel serverLevel, ItemStack itemStack, Entity victim, DamageSource damageSource, float damage) { MutableFloat result = new MutableFloat(damage); EnchantmentHelper.runIterationOnItem(itemStack, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyDamage(serverLevel, level, itemStack, victim, damageSource, result)); return result.floatValue(); } public static float modifyFallBasedDamage(ServerLevel serverLevel, ItemStack itemStack, Entity victim, DamageSource damageSource, float damage) { MutableFloat result = new MutableFloat(damage); EnchantmentHelper.runIterationOnItem(itemStack, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyFallBasedDamage(serverLevel, level, itemStack, victim, damageSource, result)); return result.floatValue(); } public static float modifyArmorEffectiveness(ServerLevel serverLevel, ItemStack itemStack, Entity victim, DamageSource damageSource, float armorFraction) { MutableFloat result = new MutableFloat(armorFraction); EnchantmentHelper.runIterationOnItem(itemStack, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyArmorEffectivness(serverLevel, level, itemStack, victim, damageSource, result)); return result.floatValue(); } public static float modifyKnockback(ServerLevel serverLevel, ItemStack itemStack, Entity victim, DamageSource damageSource, float knockback) { MutableFloat result = new MutableFloat(knockback); EnchantmentHelper.runIterationOnItem(itemStack, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyKnockback(serverLevel, level, itemStack, victim, damageSource, result)); return result.floatValue(); } public static void doPostAttackEffects(ServerLevel serverLevel, Entity victim, DamageSource damageSource) { Entity entity = damageSource.getEntity(); if (entity instanceof LivingEntity) { LivingEntity attacker = (LivingEntity)entity; EnchantmentHelper.doPostAttackEffectsWithItemSource(serverLevel, victim, damageSource, attacker.getWeaponItem()); } else { EnchantmentHelper.doPostAttackEffectsWithItemSource(serverLevel, victim, damageSource, null); } } public static void doLungeEffects(ServerLevel serverLevel, Entity entity) { if (entity instanceof LivingEntity) { LivingEntity user = (LivingEntity)entity; EnchantmentHelper.runIterationOnItem(entity.getWeaponItem(), EquipmentSlot.MAINHAND, user, (enchantment, level, item) -> ((Enchantment)enchantment.value()).doLunge(serverLevel, level, item, entity)); } } public static void doPostAttackEffectsWithItemSource(ServerLevel serverLevel, Entity victim, DamageSource damageSource, @Nullable ItemStack source) { EnchantmentHelper.doPostAttackEffectsWithItemSourceOnBreak(serverLevel, victim, damageSource, source, null); } public static void doPostAttackEffectsWithItemSourceOnBreak(ServerLevel serverLevel, Entity victim, DamageSource damageSource, @Nullable ItemStack source, @Nullable Consumer attackerlessOnBreak) { if (victim instanceof LivingEntity) { LivingEntity livingVictim = (LivingEntity)victim; EnchantmentHelper.runIterationOnEquipment(livingVictim, (enchantment, level, item) -> ((Enchantment)enchantment.value()).doPostAttack(serverLevel, level, item, EnchantmentTarget.VICTIM, victim, damageSource)); } if (source != null) { Entity entity = damageSource.getEntity(); if (entity instanceof LivingEntity) { LivingEntity attacker = (LivingEntity)entity; EnchantmentHelper.runIterationOnItem(source, EquipmentSlot.MAINHAND, attacker, (enchantment, level, item) -> ((Enchantment)enchantment.value()).doPostAttack(serverLevel, level, item, EnchantmentTarget.ATTACKER, victim, damageSource)); } else if (attackerlessOnBreak != null) { EnchantedItemInUse item2 = new EnchantedItemInUse(source, null, null, attackerlessOnBreak); EnchantmentHelper.runIterationOnItem(source, (enchantment, level) -> ((Enchantment)enchantment.value()).doPostAttack(serverLevel, level, item2, EnchantmentTarget.ATTACKER, victim, damageSource)); } } } public static void runLocationChangedEffects(ServerLevel serverLevel, LivingEntity entity) { EnchantmentHelper.runIterationOnEquipment(entity, (enchantment, level, item) -> ((Enchantment)enchantment.value()).runLocationChangedEffects(serverLevel, level, item, entity)); } public static void runLocationChangedEffects(ServerLevel serverLevel, ItemStack stack, LivingEntity entity, EquipmentSlot slot) { EnchantmentHelper.runIterationOnItem(stack, slot, entity, (enchantment, level, item) -> ((Enchantment)enchantment.value()).runLocationChangedEffects(serverLevel, level, item, entity)); } public static void stopLocationBasedEffects(LivingEntity entity) { EnchantmentHelper.runIterationOnEquipment(entity, (enchantment, level, item) -> ((Enchantment)enchantment.value()).stopLocationBasedEffects(level, item, entity)); } public static void stopLocationBasedEffects(ItemStack stack, LivingEntity entity, EquipmentSlot slot) { EnchantmentHelper.runIterationOnItem(stack, slot, entity, (enchantment, level, item) -> ((Enchantment)enchantment.value()).stopLocationBasedEffects(level, item, entity)); } public static void tickEffects(ServerLevel serverLevel, LivingEntity entity) { EnchantmentHelper.runIterationOnEquipment(entity, (enchantment, level, item) -> ((Enchantment)enchantment.value()).tick(serverLevel, level, item, entity)); } public static int getEnchantmentLevel(Holder enchantment, LivingEntity entity) { Collection allowedSlots = enchantment.value().getSlotItems(entity).values(); int bestLevel = 0; for (ItemStack piece : allowedSlots) { int newLevel = EnchantmentHelper.getItemEnchantmentLevel(enchantment, piece); if (newLevel <= bestLevel) continue; bestLevel = newLevel; } return bestLevel; } public static int processProjectileCount(ServerLevel serverLevel, ItemStack weapon, Entity shooter, int count) { MutableFloat modifiedCount = new MutableFloat((float)count); EnchantmentHelper.runIterationOnItem(weapon, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyProjectileCount(serverLevel, level, weapon, shooter, modifiedCount)); return Math.max(0, modifiedCount.intValue()); } public static float processProjectileSpread(ServerLevel serverLevel, ItemStack weapon, Entity shooter, float angle) { MutableFloat modifiedAngle = new MutableFloat(angle); EnchantmentHelper.runIterationOnItem(weapon, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyProjectileSpread(serverLevel, level, weapon, shooter, modifiedAngle)); return Math.max(0.0f, modifiedAngle.floatValue()); } public static int getPiercingCount(ServerLevel serverLevel, ItemStack weapon, ItemStack ammo) { MutableFloat modifiedAmount = new MutableFloat(0.0f); EnchantmentHelper.runIterationOnItem(weapon, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyPiercingCount(serverLevel, level, ammo, modifiedAmount)); return Math.max(0, modifiedAmount.intValue()); } public static void onProjectileSpawned(ServerLevel serverLevel, ItemStack weapon, Projectile projectileEntity, Consumer onBreak) { LivingEntity le; Entity entity = projectileEntity.getOwner(); LivingEntity owner = entity instanceof LivingEntity ? (le = (LivingEntity)entity) : null; EnchantedItemInUse item = new EnchantedItemInUse(weapon, null, owner, onBreak); EnchantmentHelper.runIterationOnItem(weapon, (enchantment, level) -> ((Enchantment)enchantment.value()).onProjectileSpawned(serverLevel, level, item, projectileEntity)); } public static void onHitBlock(ServerLevel serverLevel, ItemStack weapon, @Nullable LivingEntity owner, Entity entity, @Nullable EquipmentSlot slot, Vec3 hitLocation, BlockState hitBlock, Consumer onBreak) { EnchantedItemInUse item = new EnchantedItemInUse(weapon, slot, owner, onBreak); EnchantmentHelper.runIterationOnItem(weapon, (enchantment, level) -> ((Enchantment)enchantment.value()).onHitBlock(serverLevel, level, item, entity, hitLocation, hitBlock)); } public static int modifyDurabilityToRepairFromXp(ServerLevel serverLevel, ItemStack item, int durability) { MutableFloat modifiedDurability = new MutableFloat((float)durability); EnchantmentHelper.runIterationOnItem(item, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyDurabilityToRepairFromXp(serverLevel, level, item, modifiedDurability)); return Math.max(0, modifiedDurability.intValue()); } public static float processEquipmentDropChance(ServerLevel serverLevel, LivingEntity entity, DamageSource killingBlow, float chance) { MutableFloat modifiedChance = new MutableFloat(chance); RandomSource random = entity.getRandom(); EnchantmentHelper.runIterationOnEquipment(entity, (enchantment, level, item) -> { LootContext context = Enchantment.damageContext(serverLevel, level, entity, killingBlow); ((Enchantment)enchantment.value()).getEffects(EnchantmentEffectComponents.EQUIPMENT_DROPS).forEach(filteredEffect -> { if (filteredEffect.enchanted() == EnchantmentTarget.VICTIM && filteredEffect.affected() == EnchantmentTarget.VICTIM && filteredEffect.matches(context)) { modifiedChance.setValue(((EnchantmentValueEffect)filteredEffect.effect()).process(level, random, modifiedChance.floatValue())); } }); }); Entity attacker = killingBlow.getEntity(); if (attacker instanceof LivingEntity) { LivingEntity livingAttacker = (LivingEntity)attacker; EnchantmentHelper.runIterationOnEquipment(livingAttacker, (enchantment, level, item) -> { LootContext context = Enchantment.damageContext(serverLevel, level, entity, killingBlow); ((Enchantment)enchantment.value()).getEffects(EnchantmentEffectComponents.EQUIPMENT_DROPS).forEach(filteredEffect -> { if (filteredEffect.enchanted() == EnchantmentTarget.ATTACKER && filteredEffect.affected() == EnchantmentTarget.VICTIM && filteredEffect.matches(context)) { modifiedChance.setValue(((EnchantmentValueEffect)filteredEffect.effect()).process(level, random, modifiedChance.floatValue())); } }); }); } return modifiedChance.floatValue(); } public static void forEachModifier(ItemStack itemStack, EquipmentSlotGroup slot, BiConsumer, AttributeModifier> consumer) { EnchantmentHelper.runIterationOnItem(itemStack, (enchantment, level) -> ((Enchantment)enchantment.value()).getEffects(EnchantmentEffectComponents.ATTRIBUTES).forEach(effect -> { if (((Enchantment)enchantment.value()).definition().slots().contains(slot)) { consumer.accept(effect.attribute(), effect.getModifier(level, slot)); } })); } public static void forEachModifier(ItemStack itemStack, EquipmentSlot slot, BiConsumer, AttributeModifier> consumer) { EnchantmentHelper.runIterationOnItem(itemStack, (enchantment, level) -> ((Enchantment)enchantment.value()).getEffects(EnchantmentEffectComponents.ATTRIBUTES).forEach(effect -> { if (((Enchantment)enchantment.value()).matchingSlot(slot)) { consumer.accept(effect.attribute(), effect.getModifier(level, slot)); } })); } public static int getFishingLuckBonus(ServerLevel serverLevel, ItemStack rod, Entity fisher) { MutableFloat modifiedSpeed = new MutableFloat(0.0f); EnchantmentHelper.runIterationOnItem(rod, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyFishingLuckBonus(serverLevel, level, rod, fisher, modifiedSpeed)); return Math.max(0, modifiedSpeed.intValue()); } public static float getFishingTimeReduction(ServerLevel serverLevel, ItemStack rod, Entity fisher) { MutableFloat modifiedSpeed = new MutableFloat(0.0f); EnchantmentHelper.runIterationOnItem(rod, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyFishingTimeReduction(serverLevel, level, rod, fisher, modifiedSpeed)); return Math.max(0.0f, modifiedSpeed.floatValue()); } public static int getTridentReturnToOwnerAcceleration(ServerLevel serverLevel, ItemStack weapon, Entity trident) { MutableFloat modifiedAcceleration = new MutableFloat(0.0f); EnchantmentHelper.runIterationOnItem(weapon, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyTridentReturnToOwnerAcceleration(serverLevel, level, weapon, trident, modifiedAcceleration)); return Math.max(0, modifiedAcceleration.intValue()); } public static float modifyCrossbowChargingTime(ItemStack crossbow, LivingEntity holder, float time) { MutableFloat modifiedTime = new MutableFloat(time); EnchantmentHelper.runIterationOnItem(crossbow, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyCrossbowChargeTime(holder.getRandom(), level, modifiedTime)); return Math.max(0.0f, modifiedTime.floatValue()); } public static float getTridentSpinAttackStrength(ItemStack trident, LivingEntity holder) { MutableFloat strength = new MutableFloat(0.0f); EnchantmentHelper.runIterationOnItem(trident, (enchantment, level) -> ((Enchantment)enchantment.value()).modifyTridentSpinAttackStrength(holder.getRandom(), level, strength)); return strength.floatValue(); } public static boolean hasTag(ItemStack item, TagKey tag) { ItemEnchantments enchantments = item.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY); for (Object2IntMap.Entry> entry : enchantments.entrySet()) { Holder enchantment = (Holder)entry.getKey(); if (!enchantment.is(tag)) continue; return true; } return false; } public static boolean has(ItemStack item, DataComponentType effectType) { MutableBoolean found = new MutableBoolean(false); EnchantmentHelper.runIterationOnItem(item, (enchantment, level) -> { if (((Enchantment)enchantment.value()).effects().has(effectType)) { found.setTrue(); } }); return found.booleanValue(); } public static Optional pickHighestLevel(ItemStack itemStack, DataComponentType> componentType) { Pair, Integer> picked = EnchantmentHelper.getHighestLevel(itemStack, componentType); if (picked != null) { List list = (List)picked.getFirst(); int enchantmentLevel = (Integer)picked.getSecond(); return Optional.of(list.get(Math.min(enchantmentLevel, list.size()) - 1)); } return Optional.empty(); } /* * Issues handling annotations - annotations may be inaccurate */ public static @Nullable Pair getHighestLevel(ItemStack item, DataComponentType effectType) { @Nullable MutableObject found = new MutableObject(); EnchantmentHelper.runIterationOnItem(item, (enchantment, level) -> { Object effect; if ((found.get() == null || (Integer)((Pair)found.get()).getSecond() < level) && (effect = ((Enchantment)enchantment.value()).effects().get(effectType)) != null) { found.setValue((Object)Pair.of(effect, (Object)level)); } }); return (Pair)found.get(); } public static Optional getRandomItemWith(DataComponentType componentType, LivingEntity source, Predicate predicate) { ArrayList items = new ArrayList(); for (EquipmentSlot slot : EquipmentSlot.VALUES) { ItemStack item = source.getItemBySlot(slot); if (!predicate.test(item)) continue; ItemEnchantments enchantments = item.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY); for (Object2IntMap.Entry> entry : enchantments.entrySet()) { Holder enchantment = (Holder)entry.getKey(); if (!((Enchantment)enchantment.value()).effects().has(componentType) || !((Enchantment)enchantment.value()).matchingSlot(slot)) continue; items.add(new EnchantedItemInUse(item, slot, source)); } } return Util.getRandomSafe(items, source.getRandom()); } public static int getEnchantmentCost(RandomSource random, int slot, int bookcases, ItemStack itemStack) { Enchantable enchantable = itemStack.get(DataComponents.ENCHANTABLE); if (enchantable == null) { return 0; } if (bookcases > 15) { bookcases = 15; } int selected = random.nextInt(8) + 1 + (bookcases >> 1) + random.nextInt(bookcases + 1); if (slot == 0) { return Math.max(selected / 3, 1); } if (slot == 1) { return selected * 2 / 3 + 1; } return Math.max(selected, bookcases * 2); } public static ItemStack enchantItem(RandomSource random, ItemStack itemStack, int enchantmentCost, RegistryAccess registryAccess, Optional> set) { return EnchantmentHelper.enchantItem(random, itemStack, enchantmentCost, set.map(HolderSet::stream).orElseGet(() -> registryAccess.lookupOrThrow(Registries.ENCHANTMENT).listElements().map(h -> h))); } public static ItemStack enchantItem(RandomSource random, ItemStack itemStack, int enchantmentCost, Stream> source) { List enchants = EnchantmentHelper.selectEnchantment(random, itemStack, enchantmentCost, source); if (itemStack.is(Items.BOOK)) { itemStack = new ItemStack(Items.ENCHANTED_BOOK); } for (EnchantmentInstance enchant : enchants) { itemStack.enchant(enchant.enchantment(), enchant.level()); } return itemStack; } public static List selectEnchantment(RandomSource random, ItemStack itemStack, int enchantmentCost, Stream> source) { ArrayList results = Lists.newArrayList(); Enchantable enchantable = itemStack.get(DataComponents.ENCHANTABLE); if (enchantable == null) { return results; } enchantmentCost += 1 + random.nextInt(enchantable.value() / 4 + 1) + random.nextInt(enchantable.value() / 4 + 1); float randomSpan = (random.nextFloat() + random.nextFloat() - 1.0f) * 0.15f; List enchantments = EnchantmentHelper.getAvailableEnchantmentResults(enchantmentCost = Mth.clamp(Math.round((float)enchantmentCost + (float)enchantmentCost * randomSpan), 1, Integer.MAX_VALUE), itemStack, source); if (!enchantments.isEmpty()) { WeightedRandom.getRandomItem(random, enchantments, EnchantmentInstance::weight).ifPresent(results::add); while (random.nextInt(50) <= enchantmentCost) { if (!results.isEmpty()) { EnchantmentHelper.filterCompatibleEnchantments(enchantments, (EnchantmentInstance)results.getLast()); } if (enchantments.isEmpty()) break; WeightedRandom.getRandomItem(random, enchantments, EnchantmentInstance::weight).ifPresent(results::add); enchantmentCost /= 2; } } return results; } public static void filterCompatibleEnchantments(List enchants, EnchantmentInstance target) { enchants.removeIf(e -> !Enchantment.areCompatible(target.enchantment(), e.enchantment())); } public static boolean isEnchantmentCompatible(Collection> enchants, Holder target) { for (Holder existing : enchants) { if (Enchantment.areCompatible(existing, target)) continue; return false; } return true; } public static List getAvailableEnchantmentResults(int value, ItemStack itemStack, Stream> source) { ArrayList results = Lists.newArrayList(); boolean isBook = itemStack.is(Items.BOOK); source.filter(enchantment -> ((Enchantment)enchantment.value()).isPrimaryItem(itemStack) || isBook).forEach(holder -> { Enchantment enchantment = (Enchantment)holder.value(); for (int level = enchantment.getMaxLevel(); level >= enchantment.getMinLevel(); --level) { if (value < enchantment.getMinCost(level) || value > enchantment.getMaxCost(level)) continue; results.add(new EnchantmentInstance((Holder)holder, level)); break; } }); return results; } public static void enchantItemFromProvider(ItemStack itemStack, RegistryAccess registryAccess, ResourceKey providerKey, DifficultyInstance difficulty, RandomSource random) { EnchantmentProvider provider = registryAccess.lookupOrThrow(Registries.ENCHANTMENT_PROVIDER).getValue(providerKey); if (provider != null) { EnchantmentHelper.updateEnchantments(itemStack, enchantments -> provider.enchant(itemStack, (ItemEnchantments.Mutable)enchantments, random, difficulty)); } } @FunctionalInterface private static interface EnchantmentVisitor { public void accept(Holder var1, int var2); } @FunctionalInterface private static interface EnchantmentInSlotVisitor { public void accept(Holder var1, int var2, EnchantedItemInUse var3); } }