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 }