package eu.abdelbaki.cmmodular; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import net.minecraft.core.Holder; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.Attribute; import net.minecraft.world.entity.ai.attributes.AttributeInstance; import net.minecraft.world.entity.ai.attributes.AttributeMap; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.neoforged.bus.api.IEventBus; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.entity.player.PlayerEvent; /** * Keeps an attribute that a piece of equipment grants from quietly going * missing. * *

Modifiers off worn and held items are transient: vanilla reads them when * the equipment changes and puts them in the entity's attribute map, and that * map is the only place they live. Change dimension and they can be gone - the * player is rebuilt on the way through, no equipment change follows, and so * nothing puts them back until the piece is taken off and put on again. Cinder * essence's fire immunity is the one that gets noticed, because the Nether is * exactly where it was wanted. * *

Two answers, because a modifier can be missing in two places. The map is * rebuilt from the equipment after a dimension change or a respawn, which is * what re-equipping would have done and which fixes every attribute rather than * only this mod's. And when one of this mod's own attributes is read, the * equipment is consulted as a fallback - the same data vanilla applies from, so * the answer is the same whether or not the map ever lost it. */ public final class EquipmentAttributes { private EquipmentAttributes() { } static void register(IEventBus modBus) { NeoForge.EVENT_BUS.addListener(EquipmentAttributes::onChangedDimension); NeoForge.EVENT_BUS.addListener(EquipmentAttributes::onRespawn); } private static void onChangedDimension(PlayerEvent.PlayerChangedDimensionEvent event) { reapply(event.getEntity()); } private static void onRespawn(PlayerEvent.PlayerRespawnEvent event) { reapply(event.getEntity()); } /** Puts every modifier the worn and held items grant back into the map. */ private static void reapply(Player player) { AttributeMap attributes = player.getAttributes(); for (EquipmentSlot slot : EquipmentSlot.values()) { ItemStack stack = player.getItemBySlot(slot); if (stack.isEmpty()) { continue; } Multimap, AttributeModifier> modifiers = HashMultimap.create(); stack.forEachModifier(slot, modifiers::put); // Each modifier is removed and added again, so this is a no-op // wherever one survived and a repair wherever one did not. attributes.addTransientAttributeModifiers(modifiers); } } /** * What one of this mod's attributes is worth on this entity, falling back * to the equipment when the attribute map has lost it. * *

Only additive modifiers are counted, which is all this mod grants. */ static double value(LivingEntity entity, Holder attribute) { AttributeInstance instance = entity.getAttribute(attribute); double live = instance == null ? 0.0D : instance.getValue(); if (live > 0.0D) { return live; } double[] worn = {0.0D}; for (EquipmentSlot slot : EquipmentSlot.values()) { ItemStack stack = entity.getItemBySlot(slot); if (stack.isEmpty()) { continue; } stack.forEachModifier(slot, (holder, modifier) -> { if (holder.value() == attribute.value() && modifier.operation() == AttributeModifier.Operation.ADD_VALUE) { worn[0] += modifier.amount(); } }); } return worn[0]; } }