001    package org.bukkit.event.entity;
002    
003    import java.util.EnumMap;
004    import java.util.Map;
005    
006    import org.apache.commons.lang.Validate;
007    import org.bukkit.entity.Entity;
008    import org.bukkit.entity.Player;
009    import org.bukkit.event.Cancellable;
010    import org.bukkit.event.HandlerList;
011    import org.bukkit.util.NumberConversions;
012    
013    import com.google.common.base.Function;
014    import com.google.common.base.Functions;
015    import com.google.common.collect.ImmutableMap;
016    
017    /**
018     * Stores data for damage events
019     */
020    public class EntityDamageEvent extends EntityEvent implements Cancellable {
021        private static final HandlerList handlers = new HandlerList();
022        private static final DamageModifier[] MODIFIERS = DamageModifier.values();
023        private static final Function<? super Double, Double> ZERO = Functions.constant(-0.0);
024        private final Map<DamageModifier, Double> modifiers;
025        private final Map<DamageModifier, ? extends Function<? super Double, Double>> modifierFunctions;
026        private final Map<DamageModifier, Double> originals;
027        private boolean cancelled;
028        private final DamageCause cause;
029    
030        @Deprecated
031        public EntityDamageEvent(final Entity damagee, final DamageCause cause, final int damage) {
032            this(damagee, cause, (double) damage);
033        }
034    
035        @Deprecated
036        public EntityDamageEvent(final Entity damagee, final DamageCause cause, final double damage) {
037            this(damagee, cause, new EnumMap<DamageModifier, Double>(ImmutableMap.of(DamageModifier.BASE, damage)), new EnumMap<DamageModifier, Function<? super Double, Double>>(ImmutableMap.of(DamageModifier.BASE, ZERO)));
038        }
039    
040        public EntityDamageEvent(final Entity damagee, final DamageCause cause, final Map<DamageModifier, Double> modifiers, final Map<DamageModifier, ? extends Function<? super Double, Double>> modifierFunctions) {
041            super(damagee);
042            Validate.isTrue(modifiers.containsKey(DamageModifier.BASE), "BASE DamageModifier missing");
043            Validate.isTrue(!modifiers.containsKey(null), "Cannot have null DamageModifier");
044            Validate.noNullElements(modifiers.values(), "Cannot have null modifier values");
045            Validate.isTrue(modifiers.keySet().equals(modifierFunctions.keySet()), "Must have a modifier function for each DamageModifier");
046            Validate.noNullElements(modifierFunctions.values(), "Cannot have null modifier function");
047            this.originals = new EnumMap<DamageModifier, Double>(modifiers);
048            this.cause = cause;
049            this.modifiers = modifiers;
050            this.modifierFunctions = modifierFunctions;
051        }
052    
053        public boolean isCancelled() {
054            return cancelled;
055        }
056    
057        public void setCancelled(boolean cancel) {
058            cancelled = cancel;
059        }
060    
061        /**
062         * Gets the original damage for the specified modifier, as defined at this
063         * event's construction.
064         *
065         * @param type the modifier
066         * @throws IllegalArgumentException if type is null
067         */
068        public double getOriginalDamage(DamageModifier type) throws IllegalArgumentException {
069            final Double damage = originals.get(type);
070            if (damage != null) {
071                return damage;
072            }
073            if (type == null) {
074                throw new IllegalArgumentException("Cannot have null DamageModifier");
075            }
076            return 0;
077        }
078    
079        /**
080         * Sets the damage for the specified modifier.
081         *
082         * @param damage the scalar value of the damage's modifier
083         * @see #getFinalDamage()
084         * @throws IllegalArgumentException if type is null
085         * @throws UnsupportedOperationException if the caller does not support
086         *     the particular DamageModifier, or to rephrase, when {@link
087         *     #isApplicable(DamageModifier)} returns false
088         */
089        public void setDamage(DamageModifier type, double damage) throws IllegalArgumentException, UnsupportedOperationException {
090            if (!modifiers.containsKey(type)) {
091                throw type == null ? new IllegalArgumentException("Cannot have null DamageModifier") : new UnsupportedOperationException(type + " is not applicable to " + getEntity());
092            }
093            modifiers.put(type, damage);
094        }
095    
096        /**
097         * Gets the damage change for some modifier
098         *
099         * @return The raw amount of damage caused by the event
100         * @throws IllegalArgumentException if type is null
101         * @see DamageModifier#BASE
102         */
103        public double getDamage(DamageModifier type) throws IllegalArgumentException {
104            Validate.notNull(type, "Cannot have null DamageModifier");
105            final Double damage = modifiers.get(type);
106            return damage == null ? 0 : damage;
107        }
108    
109        /**
110         * This checks to see if a particular modifier is valid for this event's
111         * caller, such that, {@link #setDamage(DamageModifier, double)} will not
112         * throw an {@link UnsupportedOperationException}.
113         * <p>
114         * {@link DamageModifier#BASE} is always applicable.
115         *
116         * @param type the modifier
117         * @return true if the modifier is supported by the caller, false otherwise
118         * @throws IllegalArgumentException if type is null
119         */
120        public boolean isApplicable(DamageModifier type) throws IllegalArgumentException {
121            Validate.notNull(type, "Cannot have null DamageModifier");
122            return modifiers.containsKey(type);
123        }
124    
125        /**
126         * Gets the raw amount of damage caused by the event
127         *
128         * @return The raw amount of damage caused by the event
129         * @see DamageModifier#BASE
130         */
131        public double getDamage() {
132            return getDamage(DamageModifier.BASE);
133        }
134    
135        /**
136         * Gets the amount of damage caused by the event after all damage
137         * reduction is applied.
138         *
139         * @return the amount of damage caused by the event
140         */
141        public final double getFinalDamage() {
142            double damage = 0;
143            for (DamageModifier modifier : MODIFIERS) {
144                damage += getDamage(modifier);
145            }
146            return damage;
147        }
148    
149        /**
150         * This method exists for legacy reasons to provide backwards
151         * compatibility. It will not exist at runtime and should not be used
152         * under any circumstances.
153         */
154        @Deprecated
155        public int _INVALID_getDamage() {
156            return NumberConversions.ceil(getDamage());
157        }
158    
159        /**
160         * Sets the raw amount of damage caused by the event.
161         * <p>
162         * For compatibility this also recalculates the modifiers and scales
163         * them by the difference between the modifier for the previous damage
164         * value and the new one.
165         *
166         * @param damage The raw amount of damage caused by the event
167         */
168        public void setDamage(double damage) {
169            // These have to happen in the same order as the server calculates them, keep the enum sorted
170            double remaining = damage;
171            double oldRemaining = getDamage(DamageModifier.BASE);
172            for (DamageModifier modifier : MODIFIERS) {
173                if (!isApplicable(modifier)) {
174                    continue;
175                }
176    
177                Function<? super Double, Double> modifierFunction = modifierFunctions.get(modifier);
178                double newVanilla = modifierFunction.apply(remaining);
179                double oldVanilla = modifierFunction.apply(oldRemaining);
180                double difference = oldVanilla - newVanilla;
181    
182                // Don't allow value to cross zero, assume zero values should be negative
183                double old = getDamage(modifier);
184                if (old > 0) {
185                    setDamage(modifier, Math.max(0, old - difference));
186                } else {
187                    setDamage(modifier, Math.min(0, old - difference));
188                }
189                remaining += newVanilla;
190                oldRemaining += oldVanilla;
191            }
192    
193            setDamage(DamageModifier.BASE, damage);
194        }
195    
196        /**
197         * This method exists for legacy reasons to provide backwards
198         * compatibility. It will not exist at runtime and should not be used
199         * under any circumstances.
200         */
201        @Deprecated
202        public void _INVALID_setDamage(int damage) {
203            setDamage(damage);
204        }
205    
206        /**
207         * Gets the cause of the damage.
208         *
209         * @return A DamageCause value detailing the cause of the damage.
210         */
211        public DamageCause getCause() {
212            return cause;
213        }
214    
215        @Override
216        public HandlerList getHandlers() {
217            return handlers;
218        }
219    
220        public static HandlerList getHandlerList() {
221            return handlers;
222        }
223    
224        /**
225         * An enum to specify the types of modifier
226         */
227        public enum DamageModifier {
228            /**
229             * This represents the amount of damage being done, also known as the
230             * raw {@link EntityDamageEvent#getDamage()}.
231             */
232            BASE,
233            /**
234             * This represents the damage reduced by a wearing a helmet when hit
235             * by a falling block.
236             */
237            HARD_HAT,
238            /**
239             * This represents  the damage reduction caused by blocking, only present for
240             * {@link Player Players}.
241             */
242            BLOCKING,
243            /**
244             * This represents the damage reduction caused by wearing armor.
245             */
246            ARMOR,
247            /**
248             * This represents the damage reduction caused by the Resistance potion effect.
249             */
250            RESISTANCE,
251            /**
252             * This represents the damage reduction caused by the combination of:
253             * <ul>
254             * <li>
255             *     Armor enchantments
256             * </li><li>
257             *     Witch's potion resistance
258             * </li>
259             * </ul>
260             */
261            MAGIC,
262            /**
263             * This represents the damage reduction caused by the absorption potion
264             * effect.
265             */
266            ABSORPTION,
267            ;
268        }
269    
270        /**
271         * An enum to specify the cause of the damage
272         */
273        public enum DamageCause {
274    
275            /**
276             * Damage caused when an entity contacts a block such as a Cactus.
277             * <p>
278             * Damage: 1 (Cactus)
279             */
280            CONTACT,
281            /**
282             * Damage caused when an entity attacks another entity.
283             * <p>
284             * Damage: variable
285             */
286            ENTITY_ATTACK,
287            /**
288             * Damage caused when attacked by a projectile.
289             * <p>
290             * Damage: variable
291             */
292            PROJECTILE,
293            /**
294             * Damage caused by being put in a block
295             * <p>
296             * Damage: 1
297             */
298            SUFFOCATION,
299            /**
300             * Damage caused when an entity falls a distance greater than 3 blocks
301             * <p>
302             * Damage: fall height - 3.0
303             */
304            FALL,
305            /**
306             * Damage caused by direct exposure to fire
307             * <p>
308             * Damage: 1
309             */
310            FIRE,
311            /**
312             * Damage caused due to burns caused by fire
313             * <p>
314             * Damage: 1
315             */
316            FIRE_TICK,
317            /**
318             * Damage caused due to a snowman melting
319             * <p>
320             * Damage: 1
321             */
322            MELTING,
323            /**
324             * Damage caused by direct exposure to lava
325             * <p>
326             * Damage: 4
327             */
328            LAVA,
329            /**
330             * Damage caused by running out of air while in water
331             * <p>
332             * Damage: 2
333             */
334            DROWNING,
335            /**
336             * Damage caused by being in the area when a block explodes.
337             * <p>
338             * Damage: variable
339             */
340            BLOCK_EXPLOSION,
341            /**
342             * Damage caused by being in the area when an entity, such as a
343             * Creeper, explodes.
344             * <p>
345             * Damage: variable
346             */
347            ENTITY_EXPLOSION,
348            /**
349             * Damage caused by falling into the void
350             * <p>
351             * Damage: 4 for players
352             */
353            VOID,
354            /**
355             * Damage caused by being struck by lightning
356             * <p>
357             * Damage: 5
358             */
359            LIGHTNING,
360            /**
361             * Damage caused by committing suicide using the command "/kill"
362             * <p>
363             * Damage: 1000
364             */
365            SUICIDE,
366            /**
367             * Damage caused by starving due to having an empty hunger bar
368             * <p>
369             * Damage: 1
370             */
371            STARVATION,
372            /**
373             * Damage caused due to an ongoing poison effect
374             * <p>
375             * Damage: 1
376             */
377            POISON,
378            /**
379             * Damage caused by being hit by a damage potion or spell
380             * <p>
381             * Damage: variable
382             */
383            MAGIC,
384            /**
385             * Damage caused by Wither potion effect
386             */
387            WITHER,
388            /**
389             * Damage caused by being hit by a falling block which deals damage
390             * <p>
391             * <b>Note:</b> Not every block deals damage
392             * <p>
393             * Damage: variable
394             */
395            FALLING_BLOCK,
396            /**
397             * Damage caused in retaliation to another attack by the Thorns
398             * enchantment.
399             * <p>
400             * Damage: 1-4 (Thorns)
401             */
402            THORNS,
403            /**
404             * Custom damage.
405             * <p>
406             * Damage: variable
407             */
408            CUSTOM
409        }
410    }