/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.google.common.collect.Maps * com.mojang.datafixers.kinds.App * com.mojang.datafixers.kinds.Applicative * com.mojang.serialization.Codec * com.mojang.serialization.MapCodec * com.mojang.serialization.codecs.RecordCodecBuilder * it.unimi.dsi.fastutil.objects.ObjectArraySet * org.apache.commons.lang3.mutable.MutableFloat */ package net.minecraft.world.item.enchantment; import com.google.common.collect.Maps; import com.mojang.datafixers.kinds.App; import com.mojang.datafixers.kinds.Applicative; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import it.unimi.dsi.fastutil.objects.ObjectArraySet; import java.util.ArrayList; import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import net.minecraft.ChatFormatting; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.core.RegistryCodecs; import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.Registries; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.CommonComponents; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.network.chat.ComponentUtils; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.Style; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.Identifier; import net.minecraft.resources.RegistryFixedCodec; import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.EnchantmentTags; import net.minecraft.util.ExtraCodecs; import net.minecraft.util.RandomSource; import net.minecraft.util.Unit; import net.minecraft.util.Util; 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.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.enchantment.ConditionalEffect; import net.minecraft.world.item.enchantment.EnchantedItemInUse; import net.minecraft.world.item.enchantment.EnchantmentEffectComponents; import net.minecraft.world.item.enchantment.EnchantmentTarget; import net.minecraft.world.item.enchantment.TargetedConditionalEffect; import net.minecraft.world.item.enchantment.effects.EnchantmentAttributeEffect; import net.minecraft.world.item.enchantment.effects.EnchantmentEntityEffect; import net.minecraft.world.item.enchantment.effects.EnchantmentLocationBasedEffect; import net.minecraft.world.item.enchantment.effects.EnchantmentValueEffect; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.loot.LootContext; import net.minecraft.world.level.storage.loot.LootParams; import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; import net.minecraft.world.level.storage.loot.parameters.LootContextParams; import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; import net.minecraft.world.phys.Vec3; import org.apache.commons.lang3.mutable.MutableFloat; public record Enchantment(Component description, EnchantmentDefinition definition, HolderSet exclusiveSet, DataComponentMap effects) { public static final int MAX_LEVEL = 255; public static final Codec DIRECT_CODEC = RecordCodecBuilder.create(i -> i.group((App)ComponentSerialization.CODEC.fieldOf("description").forGetter(Enchantment::description), (App)EnchantmentDefinition.CODEC.forGetter(Enchantment::definition), (App)RegistryCodecs.homogeneousList(Registries.ENCHANTMENT).optionalFieldOf("exclusive_set", HolderSet.direct(new Holder[0])).forGetter(Enchantment::exclusiveSet), (App)EnchantmentEffectComponents.CODEC.optionalFieldOf("effects", (Object)DataComponentMap.EMPTY).forGetter(Enchantment::effects)).apply((Applicative)i, Enchantment::new)); public static final Codec> CODEC = RegistryFixedCodec.create(Registries.ENCHANTMENT); public static final StreamCodec> STREAM_CODEC = ByteBufCodecs.holderRegistry(Registries.ENCHANTMENT); public static Cost constantCost(int base) { return new Cost(base, 0); } public static Cost dynamicCost(int base, int perLevel) { return new Cost(base, perLevel); } public static EnchantmentDefinition definition(HolderSet supportedItems, HolderSet primaryItems, int weight, int maxLevel, Cost minCost, Cost maxCost, int anvilCost, EquipmentSlotGroup ... slots) { return new EnchantmentDefinition(supportedItems, Optional.of(primaryItems), weight, maxLevel, minCost, maxCost, anvilCost, List.of(slots)); } public static EnchantmentDefinition definition(HolderSet supportedItems, int weight, int maxLevel, Cost minCost, Cost maxCost, int anvilCost, EquipmentSlotGroup ... slots) { return new EnchantmentDefinition(supportedItems, Optional.empty(), weight, maxLevel, minCost, maxCost, anvilCost, List.of(slots)); } public Map getSlotItems(LivingEntity entity) { EnumMap itemStacks = Maps.newEnumMap(EquipmentSlot.class); for (EquipmentSlot slot : EquipmentSlot.VALUES) { ItemStack itemStack; if (!this.matchingSlot(slot) || (itemStack = entity.getItemBySlot(slot)).isEmpty()) continue; itemStacks.put(slot, itemStack); } return itemStacks; } public HolderSet getSupportedItems() { return this.definition.supportedItems(); } public boolean matchingSlot(EquipmentSlot slot) { return this.definition.slots().stream().anyMatch(group -> group.test(slot)); } public boolean isPrimaryItem(ItemStack item) { return this.isSupportedItem(item) && (this.definition.primaryItems.isEmpty() || item.is(this.definition.primaryItems.get())); } public boolean isSupportedItem(ItemStack item) { return item.is(this.definition.supportedItems); } public int getWeight() { return this.definition.weight(); } public int getAnvilCost() { return this.definition.anvilCost(); } public int getMinLevel() { return 1; } public int getMaxLevel() { return this.definition.maxLevel(); } public int getMinCost(int level) { return this.definition.minCost().calculate(level); } public int getMaxCost(int level) { return this.definition.maxCost().calculate(level); } @Override public String toString() { return "Enchantment " + this.description.getString(); } public static boolean areCompatible(Holder enchantment, Holder other) { return !enchantment.equals(other) && !enchantment.value().exclusiveSet.contains(other) && !other.value().exclusiveSet.contains(enchantment); } public static Component getFullname(Holder enchantment, int level) { MutableComponent result = enchantment.value().description.copy(); result = enchantment.is(EnchantmentTags.CURSE) ? ComponentUtils.mergeStyles(result, Style.EMPTY.withColor(ChatFormatting.RED)) : ComponentUtils.mergeStyles(result, Style.EMPTY.withColor(ChatFormatting.GRAY)); if (level != 1 || enchantment.value().getMaxLevel() != 1) { result.append(CommonComponents.SPACE).append(Component.translatable("enchantment.level." + level)); } return result; } public boolean canEnchant(ItemStack itemStack) { return this.definition.supportedItems().contains(itemStack.getItemHolder()); } public List getEffects(DataComponentType> type) { return this.effects.getOrDefault(type, List.of()); } public boolean isImmuneToDamage(ServerLevel serverLevel, int enchantmentLevel, Entity victim, DamageSource source) { LootContext context = Enchantment.damageContext(serverLevel, enchantmentLevel, victim, source); for (ConditionalEffect filteredEffect : this.getEffects(EnchantmentEffectComponents.DAMAGE_IMMUNITY)) { if (!filteredEffect.matches(context)) continue; return true; } return false; } public void modifyDamageProtection(ServerLevel serverLevel, int enchantmentLevel, ItemStack item, Entity victim, DamageSource source, MutableFloat protection) { LootContext context = Enchantment.damageContext(serverLevel, enchantmentLevel, victim, source); for (ConditionalEffect conditionalEffect : this.getEffects(EnchantmentEffectComponents.DAMAGE_PROTECTION)) { if (!conditionalEffect.matches(context)) continue; protection.setValue(((EnchantmentValueEffect)conditionalEffect.effect()).process(enchantmentLevel, victim.getRandom(), protection.floatValue())); } } public void modifyDurabilityChange(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, MutableFloat change) { this.modifyItemFilteredCount(EnchantmentEffectComponents.ITEM_DAMAGE, serverLevel, enchantmentLevel, itemStack, change); } public void modifyAmmoCount(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, MutableFloat change) { this.modifyItemFilteredCount(EnchantmentEffectComponents.AMMO_USE, serverLevel, enchantmentLevel, itemStack, change); } public void modifyPiercingCount(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, MutableFloat count) { this.modifyItemFilteredCount(EnchantmentEffectComponents.PROJECTILE_PIERCING, serverLevel, enchantmentLevel, itemStack, count); } public void modifyBlockExperience(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, MutableFloat count) { this.modifyItemFilteredCount(EnchantmentEffectComponents.BLOCK_EXPERIENCE, serverLevel, enchantmentLevel, itemStack, count); } public void modifyMobExperience(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, Entity killer, MutableFloat experience) { this.modifyEntityFilteredValue(EnchantmentEffectComponents.MOB_EXPERIENCE, serverLevel, enchantmentLevel, itemStack, killer, experience); } public void modifyDurabilityToRepairFromXp(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, MutableFloat change) { this.modifyItemFilteredCount(EnchantmentEffectComponents.REPAIR_WITH_XP, serverLevel, enchantmentLevel, itemStack, change); } public void modifyTridentReturnToOwnerAcceleration(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, Entity trident, MutableFloat count) { this.modifyEntityFilteredValue(EnchantmentEffectComponents.TRIDENT_RETURN_ACCELERATION, serverLevel, enchantmentLevel, itemStack, trident, count); } public void modifyTridentSpinAttackStrength(RandomSource random, int enchantmentLevel, MutableFloat strength) { this.modifyUnfilteredValue(EnchantmentEffectComponents.TRIDENT_SPIN_ATTACK_STRENGTH, random, enchantmentLevel, strength); } public void modifyFishingTimeReduction(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, Entity fisher, MutableFloat timeReduction) { this.modifyEntityFilteredValue(EnchantmentEffectComponents.FISHING_TIME_REDUCTION, serverLevel, enchantmentLevel, itemStack, fisher, timeReduction); } public void modifyFishingLuckBonus(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, Entity fisher, MutableFloat luck) { this.modifyEntityFilteredValue(EnchantmentEffectComponents.FISHING_LUCK_BONUS, serverLevel, enchantmentLevel, itemStack, fisher, luck); } public void modifyDamage(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, Entity victim, DamageSource damageSource, MutableFloat amount) { this.modifyDamageFilteredValue(EnchantmentEffectComponents.DAMAGE, serverLevel, enchantmentLevel, itemStack, victim, damageSource, amount); } public void modifyFallBasedDamage(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, Entity victim, DamageSource damageSource, MutableFloat amount) { this.modifyDamageFilteredValue(EnchantmentEffectComponents.SMASH_DAMAGE_PER_FALLEN_BLOCK, serverLevel, enchantmentLevel, itemStack, victim, damageSource, amount); } public void modifyKnockback(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, Entity victim, DamageSource damageSource, MutableFloat amount) { this.modifyDamageFilteredValue(EnchantmentEffectComponents.KNOCKBACK, serverLevel, enchantmentLevel, itemStack, victim, damageSource, amount); } public void modifyArmorEffectivness(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, Entity victim, DamageSource damageSource, MutableFloat amount) { this.modifyDamageFilteredValue(EnchantmentEffectComponents.ARMOR_EFFECTIVENESS, serverLevel, enchantmentLevel, itemStack, victim, damageSource, amount); } public void doPostAttack(ServerLevel serverLevel, int enchantmentLevel, EnchantedItemInUse item, EnchantmentTarget forTarget, Entity victim, DamageSource damageSource) { for (TargetedConditionalEffect effect : this.getEffects(EnchantmentEffectComponents.POST_ATTACK)) { if (forTarget != effect.enchanted()) continue; Enchantment.doPostAttack(effect, serverLevel, enchantmentLevel, item, victim, damageSource); } } public static void doPostAttack(TargetedConditionalEffect effect, ServerLevel serverLevel, int enchantmentLevel, EnchantedItemInUse item, Entity victim, DamageSource damageSource) { if (effect.matches(Enchantment.damageContext(serverLevel, enchantmentLevel, victim, damageSource))) { Entity target; switch (effect.affected()) { default: { throw new MatchException(null, null); } case ATTACKER: { Entity entity = damageSource.getEntity(); break; } case DAMAGING_ENTITY: { Entity entity = damageSource.getDirectEntity(); break; } case VICTIM: { Entity entity = target = victim; } } if (target != null) { effect.effect().apply(serverLevel, enchantmentLevel, item, target, target.position()); } } } public void doLunge(ServerLevel serverLevel, int enchantmentLevel, EnchantedItemInUse item, Entity user) { Enchantment.applyEffects(this.getEffects(EnchantmentEffectComponents.POST_PIERCING_ATTACK), Enchantment.entityContext(serverLevel, enchantmentLevel, user, user.position()), e -> e.apply(serverLevel, enchantmentLevel, item, user, user.position())); } public void modifyProjectileCount(ServerLevel serverLevel, int enchantmentLevel, ItemStack weapon, Entity shooter, MutableFloat count) { this.modifyEntityFilteredValue(EnchantmentEffectComponents.PROJECTILE_COUNT, serverLevel, enchantmentLevel, weapon, shooter, count); } public void modifyProjectileSpread(ServerLevel serverLevel, int enchantmentLevel, ItemStack weapon, Entity shooter, MutableFloat angle) { this.modifyEntityFilteredValue(EnchantmentEffectComponents.PROJECTILE_SPREAD, serverLevel, enchantmentLevel, weapon, shooter, angle); } public void modifyCrossbowChargeTime(RandomSource random, int enchantmentLevel, MutableFloat time) { this.modifyUnfilteredValue(EnchantmentEffectComponents.CROSSBOW_CHARGE_TIME, random, enchantmentLevel, time); } public void modifyUnfilteredValue(DataComponentType component, RandomSource random, int enchantmentLevel, MutableFloat value) { EnchantmentValueEffect effect = this.effects.get(component); if (effect != null) { value.setValue(effect.process(enchantmentLevel, random, value.floatValue())); } } public void tick(ServerLevel serverLevel, int enchantmentLevel, EnchantedItemInUse item, Entity entity) { Enchantment.applyEffects(this.getEffects(EnchantmentEffectComponents.TICK), Enchantment.entityContext(serverLevel, enchantmentLevel, entity, entity.position()), e -> e.apply(serverLevel, enchantmentLevel, item, entity, entity.position())); } public void onProjectileSpawned(ServerLevel serverLevel, int enchantmentLevel, EnchantedItemInUse weapon, Entity projectile) { Enchantment.applyEffects(this.getEffects(EnchantmentEffectComponents.PROJECTILE_SPAWNED), Enchantment.entityContext(serverLevel, enchantmentLevel, projectile, projectile.position()), e -> e.apply(serverLevel, enchantmentLevel, weapon, projectile, projectile.position())); } public void onHitBlock(ServerLevel serverLevel, int enchantmentLevel, EnchantedItemInUse weapon, Entity projectile, Vec3 position, BlockState hitBlock) { Enchantment.applyEffects(this.getEffects(EnchantmentEffectComponents.HIT_BLOCK), Enchantment.blockHitContext(serverLevel, enchantmentLevel, projectile, position, hitBlock), e -> e.apply(serverLevel, enchantmentLevel, weapon, projectile, position)); } private void modifyItemFilteredCount(DataComponentType>> effectType, ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, MutableFloat value) { Enchantment.applyEffects(this.getEffects(effectType), Enchantment.itemContext(serverLevel, enchantmentLevel, itemStack), e -> value.setValue(e.process(enchantmentLevel, serverLevel.getRandom(), value.floatValue()))); } private void modifyEntityFilteredValue(DataComponentType>> effectType, ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, Entity entity, MutableFloat value) { Enchantment.applyEffects(this.getEffects(effectType), Enchantment.entityContext(serverLevel, enchantmentLevel, entity, entity.position()), e -> value.setValue(e.process(enchantmentLevel, entity.getRandom(), value.floatValue()))); } private void modifyDamageFilteredValue(DataComponentType>> effectType, ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack, Entity victim, DamageSource damageSource, MutableFloat value) { Enchantment.applyEffects(this.getEffects(effectType), Enchantment.damageContext(serverLevel, enchantmentLevel, victim, damageSource), e -> value.setValue(e.process(enchantmentLevel, victim.getRandom(), value.floatValue()))); } public static LootContext damageContext(ServerLevel serverLevel, int enchantmentLevel, Entity victim, DamageSource source) { LootParams params = new LootParams.Builder(serverLevel).withParameter(LootContextParams.THIS_ENTITY, victim).withParameter(LootContextParams.ENCHANTMENT_LEVEL, enchantmentLevel).withParameter(LootContextParams.ORIGIN, victim.position()).withParameter(LootContextParams.DAMAGE_SOURCE, source).withOptionalParameter(LootContextParams.ATTACKING_ENTITY, source.getEntity()).withOptionalParameter(LootContextParams.DIRECT_ATTACKING_ENTITY, source.getDirectEntity()).create(LootContextParamSets.ENCHANTED_DAMAGE); return new LootContext.Builder(params).create(Optional.empty()); } private static LootContext itemContext(ServerLevel serverLevel, int enchantmentLevel, ItemStack itemStack) { LootParams params = new LootParams.Builder(serverLevel).withParameter(LootContextParams.TOOL, itemStack).withParameter(LootContextParams.ENCHANTMENT_LEVEL, enchantmentLevel).create(LootContextParamSets.ENCHANTED_ITEM); return new LootContext.Builder(params).create(Optional.empty()); } private static LootContext locationContext(ServerLevel serverLevel, int enchantmentLevel, Entity entity, boolean active) { LootParams params = new LootParams.Builder(serverLevel).withParameter(LootContextParams.THIS_ENTITY, entity).withParameter(LootContextParams.ENCHANTMENT_LEVEL, enchantmentLevel).withParameter(LootContextParams.ORIGIN, entity.position()).withParameter(LootContextParams.ENCHANTMENT_ACTIVE, active).create(LootContextParamSets.ENCHANTED_LOCATION); return new LootContext.Builder(params).create(Optional.empty()); } private static LootContext entityContext(ServerLevel serverLevel, int enchantmentLevel, Entity entity, Vec3 position) { LootParams params = new LootParams.Builder(serverLevel).withParameter(LootContextParams.THIS_ENTITY, entity).withParameter(LootContextParams.ENCHANTMENT_LEVEL, enchantmentLevel).withParameter(LootContextParams.ORIGIN, position).create(LootContextParamSets.ENCHANTED_ENTITY); return new LootContext.Builder(params).create(Optional.empty()); } private static LootContext blockHitContext(ServerLevel serverLevel, int enchantmentLevel, Entity entity, Vec3 position, BlockState hitBlock) { LootParams params = new LootParams.Builder(serverLevel).withParameter(LootContextParams.THIS_ENTITY, entity).withParameter(LootContextParams.ENCHANTMENT_LEVEL, enchantmentLevel).withParameter(LootContextParams.ORIGIN, position).withParameter(LootContextParams.BLOCK_STATE, hitBlock).create(LootContextParamSets.HIT_BLOCK); return new LootContext.Builder(params).create(Optional.empty()); } private static void applyEffects(List> effects, LootContext filterData, Consumer action) { for (ConditionalEffect conditionalEffect : effects) { if (!conditionalEffect.matches(filterData)) continue; action.accept(conditionalEffect.effect()); } } public void runLocationChangedEffects(ServerLevel serverLevel, int enchantmentLevel, EnchantedItemInUse item, LivingEntity entity) { EquipmentSlot slot = item.inSlot(); if (slot == null) { return; } Map> activeLocationDependentEffects = entity.activeLocationDependentEnchantments(slot); if (!this.matchingSlot(slot)) { Set activeEffects = activeLocationDependentEffects.remove(this); if (activeEffects != null) { activeEffects.forEach(effect -> effect.onDeactivated(item, entity, entity.position(), enchantmentLevel)); } return; } ObjectArraySet activeEffects = activeLocationDependentEffects.get(this); for (ConditionalEffect filteredEffect : this.getEffects(EnchantmentEffectComponents.LOCATION_CHANGED)) { boolean wasActive; EnchantmentLocationBasedEffect effect2 = (EnchantmentLocationBasedEffect)filteredEffect.effect(); boolean bl = wasActive = activeEffects != null && activeEffects.contains(effect2); if (filteredEffect.matches(Enchantment.locationContext(serverLevel, enchantmentLevel, entity, wasActive))) { if (!wasActive) { if (activeEffects == null) { activeEffects = new ObjectArraySet(); activeLocationDependentEffects.put(this, (Set)activeEffects); } activeEffects.add((EnchantmentLocationBasedEffect)effect2); } effect2.onChangedBlock(serverLevel, enchantmentLevel, item, entity, entity.position(), !wasActive); continue; } if (activeEffects == null || !activeEffects.remove(effect2)) continue; effect2.onDeactivated(item, entity, entity.position(), enchantmentLevel); } if (activeEffects != null && activeEffects.isEmpty()) { activeLocationDependentEffects.remove(this); } } public void stopLocationBasedEffects(int enchantmentLevel, EnchantedItemInUse item, LivingEntity entity) { EquipmentSlot slot = item.inSlot(); if (slot == null) { return; } Set activeEffects = entity.activeLocationDependentEnchantments(slot).remove(this); if (activeEffects == null) { return; } for (EnchantmentLocationBasedEffect effect : activeEffects) { effect.onDeactivated(item, entity, entity.position(), enchantmentLevel); } } public static Builder enchantment(EnchantmentDefinition definition) { return new Builder(definition); } public record EnchantmentDefinition(HolderSet supportedItems, Optional> primaryItems, int weight, int maxLevel, Cost minCost, Cost maxCost, int anvilCost, List slots) { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> i.group((App)RegistryCodecs.homogeneousList(Registries.ITEM).fieldOf("supported_items").forGetter(EnchantmentDefinition::supportedItems), (App)RegistryCodecs.homogeneousList(Registries.ITEM).optionalFieldOf("primary_items").forGetter(EnchantmentDefinition::primaryItems), (App)ExtraCodecs.intRange(1, 1024).fieldOf("weight").forGetter(EnchantmentDefinition::weight), (App)ExtraCodecs.intRange(1, 255).fieldOf("max_level").forGetter(EnchantmentDefinition::maxLevel), (App)Cost.CODEC.fieldOf("min_cost").forGetter(EnchantmentDefinition::minCost), (App)Cost.CODEC.fieldOf("max_cost").forGetter(EnchantmentDefinition::maxCost), (App)ExtraCodecs.NON_NEGATIVE_INT.fieldOf("anvil_cost").forGetter(EnchantmentDefinition::anvilCost), (App)EquipmentSlotGroup.CODEC.listOf().fieldOf("slots").forGetter(EnchantmentDefinition::slots)).apply((Applicative)i, EnchantmentDefinition::new)); } public record Cost(int base, int perLevelAboveFirst) { public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group((App)Codec.INT.fieldOf("base").forGetter(Cost::base), (App)Codec.INT.fieldOf("per_level_above_first").forGetter(Cost::perLevelAboveFirst)).apply((Applicative)i, Cost::new)); public int calculate(int level) { return this.base + this.perLevelAboveFirst * (level - 1); } } public static class Builder { private final EnchantmentDefinition definition; private HolderSet exclusiveSet = HolderSet.direct(new Holder[0]); private final Map, List> effectLists = new HashMap(); private final DataComponentMap.Builder effectMapBuilder = DataComponentMap.builder(); public Builder(EnchantmentDefinition definition) { this.definition = definition; } public Builder exclusiveWith(HolderSet set) { this.exclusiveSet = set; return this; } public Builder withEffect(DataComponentType>> type, E effect, LootItemCondition.Builder condition) { this.getEffectsList(type).add(new ConditionalEffect(effect, Optional.of(condition.build()))); return this; } public Builder withEffect(DataComponentType>> type, E effect) { this.getEffectsList(type).add(new ConditionalEffect(effect, Optional.empty())); return this; } public Builder withEffect(DataComponentType>> type, EnchantmentTarget enchanted, EnchantmentTarget affected, E effect, LootItemCondition.Builder condition) { this.getEffectsList(type).add(new TargetedConditionalEffect(enchanted, affected, effect, Optional.of(condition.build()))); return this; } public Builder withEffect(DataComponentType>> type, EnchantmentTarget enchanted, EnchantmentTarget affected, E effect) { this.getEffectsList(type).add(new TargetedConditionalEffect(enchanted, affected, effect, Optional.empty())); return this; } public Builder withEffect(DataComponentType> type, EnchantmentAttributeEffect effect) { this.getEffectsList(type).add(effect); return this; } public Builder withSpecialEffect(DataComponentType type, E effect) { this.effectMapBuilder.set(type, effect); return this; } public Builder withEffect(DataComponentType type) { this.effectMapBuilder.set(type, Unit.INSTANCE); return this; } private List getEffectsList(DataComponentType> type) { return this.effectLists.computeIfAbsent(type, k -> { ArrayList newList = new ArrayList(); this.effectMapBuilder.set(type, newList); return newList; }); } public Enchantment build(Identifier descriptionKey) { return new Enchantment(Component.translatable(Util.makeDescriptionId("enchantment", descriptionKey)), this.definition, this.exclusiveSet, this.effectMapBuilder.build()); } } }