543 lines
31 KiB
Java
543 lines
31 KiB
Java
/*
|
|
* 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> enchantment, ItemStack piece) {
|
|
ItemEnchantments enchantments = piece.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY);
|
|
return enchantments.getLevel(enchantment);
|
|
}
|
|
|
|
public static ItemEnchantments updateEnchantments(ItemStack itemStack, Consumer<ItemEnchantments.Mutable> consumer) {
|
|
DataComponentType<ItemEnchantments> 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<ItemEnchantments> 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<Holder<Enchantment>> 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<Holder<Enchantment>> 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<Item> 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> enchantment, LivingEntity entity) {
|
|
Collection<ItemStack> 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<Item> 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<Item> 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<Holder<Attribute>, 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<Holder<Attribute>, 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<Enchantment> tag) {
|
|
ItemEnchantments enchantments = item.getOrDefault(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY);
|
|
for (Object2IntMap.Entry<Holder<Enchantment>> 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 <T> Optional<T> pickHighestLevel(ItemStack itemStack, DataComponentType<List<T>> componentType) {
|
|
Pair<List<T>, 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 <T> @Nullable Pair<T, Integer> getHighestLevel(ItemStack item, DataComponentType<T> 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<EnchantedItemInUse> getRandomItemWith(DataComponentType<?> componentType, LivingEntity source, Predicate<ItemStack> predicate) {
|
|
ArrayList<EnchantedItemInUse> items = new ArrayList<EnchantedItemInUse>();
|
|
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<Holder<Enchantment>> 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<? extends HolderSet<Enchantment>> 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<Holder<Enchantment>> source) {
|
|
List<EnchantmentInstance> 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<EnchantmentInstance> selectEnchantment(RandomSource random, ItemStack itemStack, int enchantmentCost, Stream<Holder<Enchantment>> 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<EnchantmentInstance> 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<EnchantmentInstance> enchants, EnchantmentInstance target) {
|
|
enchants.removeIf(e -> !Enchantment.areCompatible(target.enchantment(), e.enchantment()));
|
|
}
|
|
|
|
public static boolean isEnchantmentCompatible(Collection<Holder<Enchantment>> enchants, Holder<Enchantment> target) {
|
|
for (Holder<Enchantment> existing : enchants) {
|
|
if (Enchantment.areCompatible(existing, target)) continue;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static List<EnchantmentInstance> getAvailableEnchantmentResults(int value, ItemStack itemStack, Stream<Holder<Enchantment>> 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<Enchantment>)holder, level));
|
|
break;
|
|
}
|
|
});
|
|
return results;
|
|
}
|
|
|
|
public static void enchantItemFromProvider(ItemStack itemStack, RegistryAccess registryAccess, ResourceKey<EnchantmentProvider> 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<Enchantment> var1, int var2);
|
|
}
|
|
|
|
@FunctionalInterface
|
|
private static interface EnchantmentInSlotVisitor {
|
|
public void accept(Holder<Enchantment> var1, int var2, EnchantedItemInUse var3);
|
|
}
|
|
}
|
|
|