package org.bukkit.event.entity; import java.util.EnumMap; import java.util.Map; import org.apache.commons.lang.Validate; import org.bukkit.Material; 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 com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.collect.ImmutableMap; /** * 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 ZERO = Functions.constant(-0.0); private final Map modifiers; private final Map> modifierFunctions; private final Map originals; private boolean cancelled; private final DamageCause cause; public EntityDamageEvent(final Entity damagee, final DamageCause cause, final double damage) { this(damagee, cause, new EnumMap(ImmutableMap.of(DamageModifier.BASE, damage)), new EnumMap>(ImmutableMap.of(DamageModifier.BASE, ZERO))); } public EntityDamageEvent(final Entity damagee, final DamageCause cause, final Map modifiers, final Map> 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(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 * @return the original damage * @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 type the damage modifier * @param damage the scalar value of the damage's modifier * @see #getFinalDamage() * @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 */ 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 * * @param type the damage 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}. *

* {@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; } /** * Sets the raw amount of damage caused by the event. *

* 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 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); } /** * 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 * * @deprecated This API is responsible for a large number of implementation * problems and is in general unsustainable to maintain. It is likely to be * removed very soon in a subsequent release. Please see * https://www.spigotmc.org/threads/194446/ for more information. */ @Deprecated 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: *

    *
  • * Armor enchantments *
  • * Witch's potion resistance *
  • *
*/ 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. *

* Damage: 1 (Cactus) */ CONTACT, /** * Damage caused when an entity attacks another entity. *

* Damage: variable */ ENTITY_ATTACK, /** * Damage caused when an entity attacks another entity in a sweep attack. *

* Damage: variable */ ENTITY_SWEEP_ATTACK, /** * Damage caused when attacked by a projectile. *

* Damage: variable */ PROJECTILE, /** * Damage caused by being put in a block *

* Damage: 1 */ SUFFOCATION, /** * Damage caused when an entity falls a distance greater than 3 blocks *

* Damage: fall height - 3.0 */ FALL, /** * Damage caused by direct exposure to fire *

* Damage: 1 */ FIRE, /** * Damage caused due to burns caused by fire *

* Damage: 1 */ FIRE_TICK, /** * Damage caused due to a snowman melting *

* Damage: 1 */ MELTING, /** * Damage caused by direct exposure to lava *

* Damage: 4 */ LAVA, /** * Damage caused by running out of air while in water *

* Damage: 2 */ DROWNING, /** * Damage caused by being in the area when a block explodes. *

* Damage: variable */ BLOCK_EXPLOSION, /** * Damage caused by being in the area when an entity, such as a * Creeper, explodes. *

* Damage: variable */ ENTITY_EXPLOSION, /** * Damage caused by falling into the void *

* Damage: 4 for players */ VOID, /** * Damage caused by being struck by lightning *

* Damage: 5 */ LIGHTNING, /** * Damage caused by committing suicide using the command "/kill" *

* Damage: 1000 */ SUICIDE, /** * Damage caused by starving due to having an empty hunger bar *

* Damage: 1 */ STARVATION, /** * Damage caused due to an ongoing poison effect *

* Damage: 1 */ POISON, /** * Damage caused by being hit by a damage potion or spell *

* Damage: variable */ MAGIC, /** * Damage caused by Wither potion effect */ WITHER, /** * Damage caused by being hit by a falling block which deals damage *

* Note: Not every block deals damage *

* Damage: variable */ FALLING_BLOCK, /** * Damage caused in retaliation to another attack by the Thorns * enchantment. *

* Damage: 1-4 (Thorns) */ THORNS, /** * Damage caused by a dragon breathing fire. *

* Damage: variable */ DRAGON_BREATH, /** * Custom damage. *

* Damage: variable */ CUSTOM, /** * Damage caused when an entity runs into a wall. *

* Damage: variable */ FLY_INTO_WALL, /** * Damage caused when an entity steps on {@link Material#MAGMA_BLOCK}. *

* Damage: 1 */ HOT_FLOOR, /** * Damage caused when an entity is colliding with too many entities due * to the maxEntityCramming game rule. *

* Damage: 6 */ CRAMMING, /** * Damage caused when an entity that should be in water is not. *

* Damage: 1 */ DRYOUT } }