Newer
Older
ultramine_bukkit / src / main / java / org / bukkit / event / entity / EntityDamageEvent.java
@vlad20012 vlad20012 on 24 Feb 2017 10 KB initial
package org.bukkit.event.entity;

import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang.Validate;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.util.NumberConversions;

import java.util.EnumMap;
import java.util.Map;

/**
 * Stores data for damage events
 */
public class EntityDamageEvent extends EntityEvent implements Cancellable
{
	private static final HandlerList handlers = new HandlerList();
	private static final DamageModifier[] MODIFIERS = DamageModifier.values();
	private static final Function<? super Double, Double> ZERO = Functions.constant(-0.0);
	private final Map<DamageModifier, Double> modifiers;
	private final Map<DamageModifier, ? extends Function<? super Double, Double>> modifierFunctions;
	private final Map<DamageModifier, Double> originals;
	private boolean cancelled;
	private final DamageCause cause;

	@Deprecated
	public EntityDamageEvent(final Entity damagee, final DamageCause cause, final int damage)
	{
		this(damagee, cause, (double) damage);
	}

	@Deprecated
	public EntityDamageEvent(final Entity damagee, final DamageCause cause, final double damage)
	{
		this(damagee, cause, new EnumMap<DamageModifier, Double>(ImmutableMap.of(DamageModifier.BASE, damage)), new EnumMap<DamageModifier, Function<? super Double, Double>>(ImmutableMap.of(DamageModifier.BASE, ZERO)));
	}

	public EntityDamageEvent(final Entity damagee, final DamageCause cause, final Map<DamageModifier, Double> modifiers, final Map<DamageModifier, ? extends Function<? super Double, Double>> modifierFunctions)
	{
		super(damagee);
		Validate.isTrue(modifiers.containsKey(DamageModifier.BASE), "BASE DamageModifier missing");
		Validate.isTrue(!modifiers.containsKey(null), "Cannot have null DamageModifier");
		Validate.noNullElements(modifiers.values(), "Cannot have null modifier values");
		Validate.isTrue(modifiers.keySet().equals(modifierFunctions.keySet()), "Must have a modifier function for each DamageModifier");
		Validate.noNullElements(modifierFunctions.values(), "Cannot have null modifier function");
		this.originals = new EnumMap<DamageModifier, Double>(modifiers);
		this.cause = cause;
		this.modifiers = modifiers;
		this.modifierFunctions = modifierFunctions;
	}

	public boolean isCancelled()
	{
		return cancelled;
	}

	public void setCancelled(boolean cancel)
	{
		cancelled = cancel;
	}

	/**
	 * Gets the original damage for the specified modifier, as defined at this
	 * event's construction.
	 *
	 * @param type the modifier
	 * @throws IllegalArgumentException if type is null
	 */
	public double getOriginalDamage(DamageModifier type) throws IllegalArgumentException
	{
		final Double damage = originals.get(type);
		if(damage != null)
		{
			return damage;
		}
		if(type == null)
		{
			throw new IllegalArgumentException("Cannot have null DamageModifier");
		}
		return 0;
	}

	/**
	 * Sets the damage for the specified modifier.
	 *
	 * @param damage the scalar value of the damage's modifier
	 * @throws IllegalArgumentException      if type is null
	 * @throws UnsupportedOperationException if the caller does not support
	 *                                       the particular DamageModifier, or to rephrase, when {@link
	 *                                       #isApplicable(DamageModifier)} returns false
	 * @see #getFinalDamage()
	 */
	public void setDamage(DamageModifier type, double damage) throws IllegalArgumentException, UnsupportedOperationException
	{
		if(!modifiers.containsKey(type))
		{
			throw type == null ? new IllegalArgumentException("Cannot have null DamageModifier") : new UnsupportedOperationException(type + " is not applicable to " + getEntity());
		}
		modifiers.put(type, damage);
	}

	/**
	 * Gets the damage change for some modifier
	 *
	 * @return The raw amount of damage caused by the event
	 * @throws IllegalArgumentException if type is null
	 * @see DamageModifier#BASE
	 */
	public double getDamage(DamageModifier type) throws IllegalArgumentException
	{
		Validate.notNull(type, "Cannot have null DamageModifier");
		final Double damage = modifiers.get(type);
		return damage == null ? 0 : damage;
	}

	/**
	 * This checks to see if a particular modifier is valid for this event's
	 * caller, such that, {@link #setDamage(DamageModifier, double)} will not
	 * throw an {@link UnsupportedOperationException}.
	 * <p>
	 * {@link DamageModifier#BASE} is always applicable.
	 *
	 * @param type the modifier
	 * @return true if the modifier is supported by the caller, false otherwise
	 * @throws IllegalArgumentException if type is null
	 */
	public boolean isApplicable(DamageModifier type) throws IllegalArgumentException
	{
		Validate.notNull(type, "Cannot have null DamageModifier");
		return modifiers.containsKey(type);
	}

	/**
	 * Gets the raw amount of damage caused by the event
	 *
	 * @return The raw amount of damage caused by the event
	 * @see DamageModifier#BASE
	 */
	public double getDamage()
	{
		return getDamage(DamageModifier.BASE);
	}

	/**
	 * Gets the amount of damage caused by the event after all damage
	 * reduction is applied.
	 *
	 * @return the amount of damage caused by the event
	 */
	public final double getFinalDamage()
	{
		double damage = 0;
		for(DamageModifier modifier : MODIFIERS)
		{
			damage += getDamage(modifier);
		}
		return damage;
	}

	/**
	 * This method exists for legacy reasons to provide backwards
	 * compatibility. It will not exist at runtime and should not be used
	 * under any circumstances.
	 */
	@Deprecated
	public int _INVALID_getDamage()
	{
		return NumberConversions.ceil(getDamage());
	}

	/**
	 * Sets the raw amount of damage caused by the event.
	 * <p>
	 * For compatibility this also recalculates the modifiers and scales
	 * them by the difference between the modifier for the previous damage
	 * value and the new one.
	 *
	 * @param damage The raw amount of damage caused by the event
	 */
	public void setDamage(double damage)
	{
		// These have to happen in the same order as the server calculates them, keep the enum sorted
		double remaining = damage;
		double oldRemaining = getDamage(DamageModifier.BASE);
		for(DamageModifier modifier : MODIFIERS)
		{
			if(!isApplicable(modifier))
			{
				continue;
			}

			Function<? super Double, Double> modifierFunction = modifierFunctions.get(modifier);
			double newVanilla = modifierFunction.apply(remaining);
			double oldVanilla = modifierFunction.apply(oldRemaining);
			double difference = oldVanilla - newVanilla;

			// Don't allow value to cross zero, assume zero values should be negative
			double old = getDamage(modifier);
			if(old > 0)
			{
				setDamage(modifier, Math.max(0, old - difference));
			}
			else
			{
				setDamage(modifier, Math.min(0, old - difference));
			}
			remaining += newVanilla;
			oldRemaining += oldVanilla;
		}

		setDamage(DamageModifier.BASE, damage);
	}

	/**
	 * This method exists for legacy reasons to provide backwards
	 * compatibility. It will not exist at runtime and should not be used
	 * under any circumstances.
	 */
	@Deprecated
	public void _INVALID_setDamage(int damage)
	{
		setDamage(damage);
	}

	/**
	 * Gets the cause of the damage.
	 *
	 * @return A DamageCause value detailing the cause of the damage.
	 */
	public DamageCause getCause()
	{
		return cause;
	}

	@Override
	public HandlerList getHandlers()
	{
		return handlers;
	}

	public static HandlerList getHandlerList()
	{
		return handlers;
	}

	/**
	 * An enum to specify the types of modifier
	 */
	public enum DamageModifier
	{
		/**
		 * This represents the amount of damage being done, also known as the
		 * raw {@link EntityDamageEvent#getDamage()}.
		 */
		BASE,
		/**
		 * This represents the damage reduced by a wearing a helmet when hit
		 * by a falling block.
		 */
		HARD_HAT,
		/**
		 * This represents  the damage reduction caused by blocking, only present for
		 * {@link Player Players}.
		 */
		BLOCKING,
		/**
		 * This represents the damage reduction caused by wearing armor.
		 */
		ARMOR,
		/**
		 * This represents the damage reduction caused by the Resistance potion effect.
		 */
		RESISTANCE,
		/**
		 * This represents the damage reduction caused by the combination of:
		 * <ul>
		 * <li>
		 * Armor enchantments
		 * </li><li>
		 * Witch's potion resistance
		 * </li>
		 * </ul>
		 */
		MAGIC,
		/**
		 * This represents the damage reduction caused by the absorption potion
		 * effect.
		 */
		ABSORPTION,;
	}

	/**
	 * An enum to specify the cause of the damage
	 */
	public enum DamageCause
	{

		/**
		 * Damage caused when an entity contacts a block such as a Cactus.
		 * <p>
		 * Damage: 1 (Cactus)
		 */
		CONTACT,
		/**
		 * Damage caused when an entity attacks another entity.
		 * <p>
		 * Damage: variable
		 */
		ENTITY_ATTACK,
		/**
		 * Damage caused when attacked by a projectile.
		 * <p>
		 * Damage: variable
		 */
		PROJECTILE,
		/**
		 * Damage caused by being put in a block
		 * <p>
		 * Damage: 1
		 */
		SUFFOCATION,
		/**
		 * Damage caused when an entity falls a distance greater than 3 blocks
		 * <p>
		 * Damage: fall height - 3.0
		 */
		FALL,
		/**
		 * Damage caused by direct exposure to fire
		 * <p>
		 * Damage: 1
		 */
		FIRE,
		/**
		 * Damage caused due to burns caused by fire
		 * <p>
		 * Damage: 1
		 */
		FIRE_TICK,
		/**
		 * Damage caused due to a snowman melting
		 * <p>
		 * Damage: 1
		 */
		MELTING,
		/**
		 * Damage caused by direct exposure to lava
		 * <p>
		 * Damage: 4
		 */
		LAVA,
		/**
		 * Damage caused by running out of air while in water
		 * <p>
		 * Damage: 2
		 */
		DROWNING,
		/**
		 * Damage caused by being in the area when a block explodes.
		 * <p>
		 * Damage: variable
		 */
		BLOCK_EXPLOSION,
		/**
		 * Damage caused by being in the area when an entity, such as a
		 * Creeper, explodes.
		 * <p>
		 * Damage: variable
		 */
		ENTITY_EXPLOSION,
		/**
		 * Damage caused by falling into the void
		 * <p>
		 * Damage: 4 for players
		 */
		VOID,
		/**
		 * Damage caused by being struck by lightning
		 * <p>
		 * Damage: 5
		 */
		LIGHTNING,
		/**
		 * Damage caused by committing suicide using the command "/kill"
		 * <p>
		 * Damage: 1000
		 */
		SUICIDE,
		/**
		 * Damage caused by starving due to having an empty hunger bar
		 * <p>
		 * Damage: 1
		 */
		STARVATION,
		/**
		 * Damage caused due to an ongoing poison effect
		 * <p>
		 * Damage: 1
		 */
		POISON,
		/**
		 * Damage caused by being hit by a damage potion or spell
		 * <p>
		 * Damage: variable
		 */
		MAGIC,
		/**
		 * Damage caused by Wither potion effect
		 */
		WITHER,
		/**
		 * Damage caused by being hit by a falling block which deals damage
		 * <p>
		 * <b>Note:</b> Not every block deals damage
		 * <p>
		 * Damage: variable
		 */
		FALLING_BLOCK,
		/**
		 * Damage caused in retaliation to another attack by the Thorns
		 * enchantment.
		 * <p>
		 * Damage: 1-4 (Thorns)
		 */
		THORNS,
		/**
		 * Custom damage.
		 * <p>
		 * Damage: variable
		 */
		CUSTOM
	}
}