+ * Not to be confused with {@link #getDirectEntity()}, the causing entity is + * the entity to which the damage is ultimately attributed if the receiver + * is killed. If, for example, the receiver was damaged by a projectile, the + * shooter/thrower would be returned. + * + * @return an Entity or null + */ + @Nullable + public Entity getCausingEntity(); + + /** + * Get the {@link Entity} that directly caused the damage. + *
+ * Not to be confused with {@link #getCausingEntity()}, the direct entity is + * the entity that actually inflicted the damage. If, for example, the + * receiver was damaged by a projectile, the projectile would be returned. + * + * @return an Entity or null + */ + @Nullable + public Entity getDirectEntity(); + + /** + * Get the {@link Location} from where the damage originated. This will only + * be present if an entity did not cause the damage. + * + * @return the location, or null if none + */ + @Nullable + public Location getDamageLocation(); + + /** + * Get the {@link Location} from where the damage originated. + *
+ * This is a convenience method to get the final location of the damage. + * This method will attempt to return + * {@link #getDamageLocation() the damage location}. If this is null, the + * {@link #getCausingEntity() causing entity location} will be returned. + * Finally if there is no damage location nor a causing entity, null will be + * returned. + * + * @return the source of the location or null. + */ + @Nullable + public Location getSourceLocation(); + + /** + * Get if this damage is indirect. + *
+ * Damage is considered indirect if {@link #getCausingEntity()} is not equal + * to {@link #getDirectEntity()}. This will be the case, for example, if a + * skeleton shot an arrow or a player threw a potion. + * + * @return {@code true} if is indirect, {@code false} otherwise. + */ + public boolean isIndirect(); + + /** + * Get the amount of hunger exhaustion caused by this damage. + * + * @return the amount of hunger exhaustion caused. + */ + public float getFoodExhaustion(); + + /** + * Gets if this source of damage scales with difficulty. + * + * @return {@code True} if scales. + */ + public boolean scalesWithDifficulty(); + + /** + * Create a new {@link DamageSource.Builder}. + * + * @param damageType the {@link DamageType} to use + * @return a {@link DamageSource.Builder} + */ + @NotNull + @SuppressWarnings("deprecation") + public static Builder builder(@NotNull DamageType damageType) { + return Bukkit.getUnsafe().createDamageSourceBuilder(damageType); + } + + /** + * Utility class to make building a {@link DamageSource} easier. Only a + * {@link DamageType} is required. + */ + public static interface Builder { + + /** + * Set the {@link Entity} that caused the damage. + * + * @param entity the entity + * @return this instance. Allows for chained method calls + * @see DamageSource#getCausingEntity() + */ + @NotNull + public Builder withCausingEntity(@NotNull Entity entity); + + /** + * Set the {@link Entity} that directly inflicted the damage. + * + * @param entity the entity + * @return this instance. Allows for chained method calls + * @see DamageSource#getDirectEntity() + */ + @NotNull + public Builder withDirectEntity(@NotNull Entity entity); + + /** + * Set the {@link Location} of the source of damage. + * + * @param location the location where the damage occurred + * @return this instance. Allows for chained method calls + * @see DamageSource#getSourceLocation() + */ + @NotNull + public Builder withDamageLocation(@NotNull Location location); + + /** + * Create a new {@link DamageSource} instance using the supplied + * parameters. + * + * @return the damage source instance + */ + @NotNull + public DamageSource build(); + } +} diff --git a/src/main/java/org/bukkit/damage/DamageType.java b/src/main/java/org/bukkit/damage/DamageType.java new file mode 100644 index 000000000000..5fef0f8b7957 --- /dev/null +++ b/src/main/java/org/bukkit/damage/DamageType.java @@ -0,0 +1,117 @@ +package org.bukkit.damage; + +import com.google.common.base.Preconditions; +import org.bukkit.Keyed; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.Translatable; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * Represent a type of damage that an entity can receive. + *
+ * Constants in this class include the base types provided by the vanilla + * server. Data packs are capable of registering more types of damage which may + * be obtained through the {@link Registry#DAMAGE_TYPE}. + * + * @see Minecraft Wiki + */ +@ApiStatus.Experimental +public interface DamageType extends Keyed, Translatable { + + public static final DamageType IN_FIRE = getDamageType("in_fire"); + public static final DamageType LIGHTNING_BOLT = getDamageType("lightning_bolt"); + public static final DamageType ON_FIRE = getDamageType("on_fire"); + public static final DamageType LAVA = getDamageType("lava"); + public static final DamageType HOT_FLOOR = getDamageType("hot_floor"); + public static final DamageType IN_WALL = getDamageType("in_wall"); + public static final DamageType CRAMMING = getDamageType("cramming"); + public static final DamageType DROWN = getDamageType("drown"); + public static final DamageType STARVE = getDamageType("starve"); + public static final DamageType CACTUS = getDamageType("cactus"); + public static final DamageType FALL = getDamageType("fall"); + public static final DamageType FLY_INTO_WALL = getDamageType("fly_into_wall"); + public static final DamageType OUT_OF_WORLD = getDamageType("out_of_world"); + public static final DamageType GENERIC = getDamageType("generic"); + public static final DamageType MAGIC = getDamageType("magic"); + public static final DamageType WITHER = getDamageType("wither"); + public static final DamageType DRAGON_BREATH = getDamageType("dragon_breath"); + public static final DamageType DRY_OUT = getDamageType("dry_out"); + public static final DamageType SWEET_BERRY_BUSH = getDamageType("sweet_berry_bush"); + public static final DamageType FREEZE = getDamageType("freeze"); + public static final DamageType STALAGMITE = getDamageType("stalagmite"); + public static final DamageType FALLING_BLOCK = getDamageType("falling_block"); + public static final DamageType FALLING_ANVIL = getDamageType("falling_anvil"); + public static final DamageType FALLING_STALACTITE = getDamageType("falling_stalactite"); + public static final DamageType STING = getDamageType("sting"); + public static final DamageType MOB_ATTACK = getDamageType("mob_attack"); + public static final DamageType MOB_ATTACK_NO_AGGRO = getDamageType("mob_attack_no_aggro"); + public static final DamageType PLAYER_ATTACK = getDamageType("player_attack"); + public static final DamageType ARROW = getDamageType("arrow"); + public static final DamageType TRIDENT = getDamageType("trident"); + public static final DamageType MOB_PROJECTILE = getDamageType("mob_projectile"); + public static final DamageType FIREWORKS = getDamageType("fireworks"); + public static final DamageType FIREBALL = getDamageType("fireball"); + public static final DamageType UNATTRIBUTED_FIREBALL = getDamageType("unattributed_fireball"); + public static final DamageType WITHER_SKULL = getDamageType("wither_skull"); + public static final DamageType THROWN = getDamageType("thrown"); + public static final DamageType INDIRECT_MAGIC = getDamageType("indirect_magic"); + public static final DamageType THORNS = getDamageType("thorns"); + public static final DamageType EXPLOSION = getDamageType("explosion"); + public static final DamageType PLAYER_EXPLOSION = getDamageType("player_explosion"); + public static final DamageType SONIC_BOOM = getDamageType("sonic_boom"); + public static final DamageType BAD_RESPAWN_POINT = getDamageType("bad_respawn_point"); + public static final DamageType OUTSIDE_BORDER = getDamageType("outside_border"); + public static final DamageType GENERIC_KILL = getDamageType("generic_kill"); + + @NotNull + private static DamageType getDamageType(@NotNull String key) { + NamespacedKey namespacedKey = NamespacedKey.minecraft(key); + return Preconditions.checkNotNull(Registry.DAMAGE_TYPE.get(namespacedKey), "No DamageType found for %s. This is a bug.", namespacedKey); + } + + /** + * {@inheritDoc} + *
+ * The returned key is that of the death message sent when this damage type + * is responsible for the death of an entity. + *
+ * Note This translation key is only used if
+ * {@link #getDeathMessageType()} is {@link DeathMessageType#DEFAULT}
+ */
+ @NotNull
+ @Override
+ public String getTranslationKey();
+
+ /**
+ * Get the {@link DamageScaling} for this damage type.
+ *
+ * @return the damage scaling
+ */
+ @NotNull
+ public DamageScaling getDamageScaling();
+
+ /**
+ * Get the {@link DamageEffect} for this damage type.
+ *
+ * @return the damage effect
+ */
+ @NotNull
+ public DamageEffect getDamageEffect();
+
+ /**
+ * Get the {@link DeathMessageType} for this damage type.
+ *
+ * @return the death message type
+ */
+ @NotNull
+ public DeathMessageType getDeathMessageType();
+
+ /**
+ * Get the amount of hunger exhaustion caused by this damage type.
+ *
+ * @return the exhaustion
+ */
+ public float getExhaustion();
+}
diff --git a/src/main/java/org/bukkit/damage/DeathMessageType.java b/src/main/java/org/bukkit/damage/DeathMessageType.java
new file mode 100644
index 000000000000..e182eb4df2ab
--- /dev/null
+++ b/src/main/java/org/bukkit/damage/DeathMessageType.java
@@ -0,0 +1,26 @@
+package org.bukkit.damage;
+
+import org.jetbrains.annotations.ApiStatus;
+
+/**
+ * Represents a type of death message used by a {@link DamageSource}.
+ */
+@ApiStatus.Experimental
+public enum DeathMessageType {
+
+ /**
+ * No special death message logic is applied.
+ */
+ DEFAULT,
+ /**
+ * Shows a variant of fall damage death instead of a regular death message.
+ *
+ * While a DamageCause may indicate a specific Bukkit-assigned cause of damage,
+ * {@link #getDamageSource()} may expose additional types of damage such as custom
+ * damage types provided by data packs, as well as any direct or indirect entities,
+ * locations, or other contributing factors to the damage being inflicted. The
+ * alternative is generally preferred, but DamageCauses provided to this event
+ * should largely encompass most common use cases for developers if a simple cause
+ * is required.
*
- * @return A DamageCause value detailing the cause of the damage.
+ * @return a DamageCause value detailing the cause of the damage.
*/
@NotNull
public DamageCause getCause() {
return cause;
}
+ /**
+ * Get the source of damage.
+ *
+ * @return a DamageSource detailing the source of the damage.
+ */
+ @NotNull
+ public DamageSource getDamageSource() {
+ return damageSource;
+ }
+
@NotNull
@Override
public HandlerList getHandlers() {
@@ -210,7 +227,7 @@ public static HandlerList getHandlerList() {
* @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.
+ * this thread for more information.
*/
@Deprecated
public enum DamageModifier {
+ * Example: death.fell.assist.item
+ */
+ FALL_VARIANTS,
+ /**
+ * Shows the intentional game design death message instead of a regular
+ * death message.
+ */
+ INTENTIONAL_GAME_DESIGN;
+}
diff --git a/src/main/java/org/bukkit/damage/package-info.java b/src/main/java/org/bukkit/damage/package-info.java
new file mode 100644
index 000000000000..c2ff9942be2a
--- /dev/null
+++ b/src/main/java/org/bukkit/damage/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Classes concerning damage types and sources applicable to living entities.
+ */
+@org.jetbrains.annotations.ApiStatus.Experimental
+package org.bukkit.damage;
diff --git a/src/main/java/org/bukkit/entity/Damageable.java b/src/main/java/org/bukkit/entity/Damageable.java
index a9341849945f..c4537080ee18 100644
--- a/src/main/java/org/bukkit/entity/Damageable.java
+++ b/src/main/java/org/bukkit/entity/Damageable.java
@@ -1,6 +1,9 @@
package org.bukkit.entity;
import org.bukkit.attribute.Attribute;
+import org.bukkit.damage.DamageSource;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
@@ -15,14 +18,24 @@ public interface Damageable extends Entity {
void damage(double amount);
/**
- * Deals the given amount of damage to this entity, from a specified
- * entity.
+ * Deals the given amount of damage to this entity from a specified
+ * {@link Entity}.
*
- * @param amount Amount of damage to deal
- * @param source Entity which to attribute this damage from
+ * @param amount amount of damage to deal
+ * @param source entity to which the damage should be attributed
*/
void damage(double amount, @Nullable Entity source);
+ /**
+ * Deals the given amount of damage to this entity from a specified
+ * {@link DamageSource}.
+ *
+ * @param amount amount of damage to deal
+ * @param damageSource source to which the damage should be attributed
+ */
+ @ApiStatus.Experimental
+ void damage(double amount, @NotNull DamageSource damageSource);
+
/**
* Gets the entity's health from 0 to {@link #getMaxHealth()}, where 0 is dead.
*
diff --git a/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java b/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java
index 461727dc7f06..5ba9f7edf3bb 100644
--- a/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java
+++ b/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java
@@ -3,6 +3,7 @@
import com.google.common.base.Function;
import java.util.Map;
import org.bukkit.block.Block;
+import org.bukkit.damage.DamageSource;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -13,13 +14,13 @@
public class EntityDamageByBlockEvent extends EntityDamageEvent {
private final Block damager;
- public EntityDamageByBlockEvent(@Nullable final Block damager, @NotNull final Entity damagee, @NotNull final DamageCause cause, final double damage) {
- super(damagee, cause, damage);
+ public EntityDamageByBlockEvent(@Nullable final Block damager, @NotNull final Entity damagee, @NotNull final DamageCause cause, @NotNull final DamageSource damageSource, final double damage) {
+ super(damagee, cause, damageSource, damage);
this.damager = damager;
}
- public EntityDamageByBlockEvent(@Nullable final Block damager, @NotNull final Entity damagee, @NotNull final DamageCause cause, @NotNull final Map