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 }