99 lines
4.1 KiB
Java
99 lines
4.1 KiB
Java
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.
|
|
*
|
|
* <p>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.
|
|
*
|
|
* <p>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<Holder<Attribute>, 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.
|
|
*
|
|
* <p>Only additive modifiers are counted, which is all this mod grants.
|
|
*/
|
|
static double value(LivingEntity entity, Holder<Attribute> 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];
|
|
}
|
|
}
|