package choonster.testmod3.capability.maxhealth;
import choonster.testmod3.Logger;
import choonster.testmod3.api.capability.maxhealth.IMaxHealth;
import choonster.testmod3.util.Constants;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.attributes.AttributeMap;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.ai.attributes.IAttributeInstance;
import net.minecraft.network.play.server.SPacketEntityProperties;
import net.minecraft.world.WorldServer;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.UUID;
/**
* Default implementation of {@link IMaxHealth}.
*
* @author Choonster
*/
public class MaxHealth implements IMaxHealth {
/**
* The ID of the {@link AttributeModifier}.
*/
protected static final UUID MODIFIER_ID = UUID.fromString("d5d0d878-b3c2-469b-ba89-ac01c0635a9c");
/**
* The name of the {@link AttributeModifier}.
*/
protected static final String MODIFIER_NAME = "Bonus Max Health";
/**
* The minimum max health a player can have.
*/
protected static final float MIN_AMOUNT = 2.0f;
/**
* The entity this is attached to.
*/
private final EntityLivingBase entity;
/**
* The bonus max health.
*/
private float bonusMaxHealth;
/**
* The dummy max health attribute. Used to avoid setting the entity's actual attribute to an invalid value.
*/
private final IAttributeInstance dummyMaxHealthAttribute = new AttributeMap().registerAttribute(SharedMonsterAttributes.MAX_HEALTH);
public MaxHealth(@Nullable final EntityLivingBase entity) {
this.entity = entity;
}
/**
* Get the bonus max health.
*
* @return The bonus max health
*/
@Override
public final float getBonusMaxHealth() {
return bonusMaxHealth;
}
/**
* Set the bonus max health.
*
* @param bonusMaxHealth The bonus max health
*/
@Override
public final void setBonusMaxHealth(final float bonusMaxHealth) {
this.bonusMaxHealth = bonusMaxHealth;
onBonusMaxHealthChanged();
}
/**
* Add an amount to the current bonus max health.
*
* @param healthToAdd The amount of health to add
*/
@Override
public final void addBonusMaxHealth(final float healthToAdd) {
setBonusMaxHealth(getBonusMaxHealth() + healthToAdd);
}
/**
* Synchronise the entity's max health to watching clients.
*/
@Override
public void synchronise() {
if (entity != null && !entity.getEntityWorld().isRemote) {
final IAttributeInstance entityMaxHealthAttribute = entity.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH);
final SPacketEntityProperties packet = new SPacketEntityProperties(entity.getEntityId(), Collections.singleton(entityMaxHealthAttribute));
((WorldServer) entity.getEntityWorld()).getEntityTracker().sendToTrackingAndSelf(entity, packet);
}
}
/**
* Create the {@link AttributeModifier}.
*
* @return The AttributeModifier
*/
protected AttributeModifier createModifier() {
return new AttributeModifier(MODIFIER_ID, MODIFIER_NAME, getBonusMaxHealth(), Constants.ATTRIBUTE_MODIFIER_OPERATION_ADD);
}
/**
* Called when the bonus max health changes to re-apply the {@link AttributeModifier}.
*/
protected void onBonusMaxHealthChanged() {
if (entity == null) return;
final IAttributeInstance entityMaxHealthAttribute = entity.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH);
// Remove all modifiers from the dummy attribute
dummyMaxHealthAttribute.getModifiers().forEach(dummyMaxHealthAttribute::removeModifier);
// Copy the base value and modifiers except this class's from the entity's attribute to the dummy attribute
dummyMaxHealthAttribute.setBaseValue(entityMaxHealthAttribute.getBaseValue());
entityMaxHealthAttribute.getModifiers().stream()
.filter(modifier -> !modifier.getID().equals(MODIFIER_ID))
.forEach(dummyMaxHealthAttribute::applyModifier);
AttributeModifier modifier = createModifier();
dummyMaxHealthAttribute.applyModifier(modifier);
// Increment bonus max health by 0.5 until the max health is at least 2.0 (1 heart).
// We do this to avoid setting the entity's max health to 0, which would kill it (and prevent it from respawning if it's a player).
// The attribute itself will prevent its value from exceeding the maximum, so adding more than the maximum max health is harmless.
while (dummyMaxHealthAttribute.getAttributeValue() < MIN_AMOUNT) {
dummyMaxHealthAttribute.removeModifier(modifier);
bonusMaxHealth += 0.5f;
modifier = createModifier();
dummyMaxHealthAttribute.applyModifier(modifier);
}
final float newAmount = getBonusMaxHealth();
final float oldAmount;
final AttributeModifier oldModifier = entityMaxHealthAttribute.getModifier(MODIFIER_ID);
if (oldModifier != null) {
entityMaxHealthAttribute.removeModifier(oldModifier);
oldAmount = (float) oldModifier.getAmount();
Logger.debug(CapabilityMaxHealth.LOG_MARKER, "Max Health Changed! Entity: %s - Old: %s - New: %s", entity, CapabilityMaxHealth.formatMaxHealth(oldAmount), CapabilityMaxHealth.formatMaxHealth(newAmount));
} else {
oldAmount = 0.0f;
Logger.debug(CapabilityMaxHealth.LOG_MARKER, "Max Health Added! Entity: %s - New: %s", entity, CapabilityMaxHealth.formatMaxHealth(newAmount));
}
entityMaxHealthAttribute.applyModifier(modifier);
final float amountToHeal = newAmount - oldAmount;
if (amountToHeal > 0) {
entity.heal(amountToHeal);
}
}
}