From 7396e8f7c42b6f54a8bac04013e7effc67d9843c Mon Sep 17 00:00:00 2001 From: Bukkit/Spigot Date: Wed, 9 Jul 2014 16:19:27 -0500 Subject: [PATCH] Recalculate damage modifiers in event for old method. Fixes BUKKIT-5681 When we added the new API in EntityDamageEvent to give control over the various things that modify the final damage done we caused a change in behavior for users of the old #setDamage(double) method. Before changing the damage would happen before the modifiers were calculated so they would be based on the final damage value from the event. Now they are calculated at the beginning so changing the damage does not change the modifiers. To allow the old style and the new to coexist we now expose the vanilla modifer calculations to the event in the form of Function objects. These are used in #setDamage(double) to calculate the difference in the modifier between the old damage and the new and apply this difference to the current modifier. The difference is between the vanilla values for both damage values and is applied on top of the event's modifier value as this should make old and new API usage work together in a way that isn't surprising. By: Travis Watkins --- .../entity/EntityDamageByBlockEvent.java | 5 ++- .../entity/EntityDamageByEntityEvent.java | 5 ++- .../event/entity/EntityDamageEvent.java | 42 +++++++++++++++++-- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java b/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java index 49bf2a84cb..2ff121e3c5 100644 --- a/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java @@ -2,6 +2,7 @@ package org.bukkit.event.entity; import java.util.Map; +import com.google.common.base.Function; import org.bukkit.block.Block; import org.bukkit.entity.Entity; @@ -22,8 +23,8 @@ public class EntityDamageByBlockEvent extends EntityDamageEvent { this.damager = damager; } - public EntityDamageByBlockEvent(final Block damager, final Entity damagee, final DamageCause cause, final Map modifiers) { - super(damagee, cause, modifiers); + public EntityDamageByBlockEvent(final Block damager, final Entity damagee, final DamageCause cause, final Map modifiers, final Map> modifierFunctions) { + super(damagee, cause, modifiers, modifierFunctions); this.damager = damager; } diff --git a/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByEntityEvent.java b/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByEntityEvent.java index e7ea32f594..49e74c3037 100644 --- a/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByEntityEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageByEntityEvent.java @@ -2,6 +2,7 @@ package org.bukkit.event.entity; import java.util.Map; +import com.google.common.base.Function; import org.bukkit.entity.Entity; /** @@ -21,8 +22,8 @@ public class EntityDamageByEntityEvent extends EntityDamageEvent { this.damager = damager; } - public EntityDamageByEntityEvent(final Entity damager, final Entity damagee, final DamageCause cause, final Map modifiers) { - super(damagee, cause, modifiers); + public EntityDamageByEntityEvent(final Entity damager, final Entity damagee, final DamageCause cause, final Map modifiers, final Map> modifierFunctions) { + super(damagee, cause, modifiers, modifierFunctions); this.damager = damager; } diff --git a/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java b/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java index 17d9548721..2cc0799673 100644 --- a/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/entity/EntityDamageEvent.java @@ -10,6 +10,8 @@ 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; /** @@ -18,7 +20,9 @@ import com.google.common.collect.ImmutableMap; 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; @@ -30,16 +34,20 @@ public class EntityDamageEvent extends EntityEvent implements Cancellable { @Deprecated public EntityDamageEvent(final Entity damagee, final DamageCause cause, final double damage) { - this(damagee, cause, new EnumMap(ImmutableMap.of(DamageModifier.BASE, 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) { + 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() { @@ -149,11 +157,39 @@ public class EntityDamageEvent extends EntityEvent implements Cancellable { } /** - * Sets the raw amount of damage caused by the event + * 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); }