chore: port DamageType cleanup change from MelonHell 1.19.4 PR

(cherry picked from commit 9eab3d4f8b)
This commit is contained in:
mworzala 2023-11-01 22:35:18 -04:00 committed by Matt Worzala
parent d0ace48220
commit 9439b62ff0
14 changed files with 492 additions and 188 deletions

View File

@ -33,6 +33,7 @@ public class Generators {
generator.generate(resource("particles.json"), "net.minestom.server.particle", "Particle", "ParticleImpl", "Particles");
generator.generate(resource("sounds.json"), "net.minestom.server.sound", "SoundEvent", "SoundEventImpl", "SoundEvents");
generator.generate(resource("custom_statistics.json"), "net.minestom.server.statistic", "StatisticType", "StatisticTypeImpl", "StatisticTypes");
generator.generate(resource("damage_types.json"), "net.minestom.server.entity.damage", "DamageType", "DamageTypeImpl", "DamageTypes");
// Generate fluids
new FluidGenerator(resource("fluids.json"), outputFolder).generate();

View File

@ -10,7 +10,7 @@ import net.minestom.server.entity.Entity;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.ItemEntity;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.entity.damage.Damage;
import net.minestom.server.event.Event;
import net.minestom.server.event.EventNode;
import net.minestom.server.event.entity.EntityAttackEvent;
@ -28,13 +28,11 @@ import net.minestom.server.item.metadata.BundleMeta;
import net.minestom.server.monitoring.BenchmarkManager;
import net.minestom.server.monitoring.TickMonitor;
import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.time.TimeUnit;
import net.minestom.server.world.DimensionType;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicReference;
@ -51,7 +49,7 @@ public class PlayerInit {
if (entity instanceof Player) {
Player target = (Player) entity;
target.damage(DamageType.fromEntity(source), 5);
target.damage(Damage.fromEntity(source, 5));
}
if (source instanceof Player) {

View File

@ -0,0 +1,95 @@
package net.minestom.server.entity.damage;
/**
* Code autogenerated, do not edit!
*/
@SuppressWarnings("unused")
interface DamageTypes {
DamageType WITHER = DamageTypeImpl.get("minecraft:wither");
DamageType SONIC_BOOM = DamageTypeImpl.get("minecraft:sonic_boom");
DamageType WITHER_SKULL = DamageTypeImpl.get("minecraft:wither_skull");
DamageType DRY_OUT = DamageTypeImpl.get("minecraft:dry_out");
DamageType TRIDENT = DamageTypeImpl.get("minecraft:trident");
DamageType ON_FIRE = DamageTypeImpl.get("minecraft:on_fire");
DamageType FALL = DamageTypeImpl.get("minecraft:fall");
DamageType MOB_ATTACK = DamageTypeImpl.get("minecraft:mob_attack");
DamageType MOB_PROJECTILE = DamageTypeImpl.get("minecraft:mob_projectile");
DamageType THROWN = DamageTypeImpl.get("minecraft:thrown");
DamageType FALLING_STALACTITE = DamageTypeImpl.get("minecraft:falling_stalactite");
DamageType FIREBALL = DamageTypeImpl.get("minecraft:fireball");
DamageType FALLING_BLOCK = DamageTypeImpl.get("minecraft:falling_block");
DamageType PLAYER_EXPLOSION = DamageTypeImpl.get("minecraft:player_explosion");
DamageType STING = DamageTypeImpl.get("minecraft:sting");
DamageType UNATTRIBUTED_FIREBALL = DamageTypeImpl.get("minecraft:unattributed_fireball");
DamageType IN_WALL = DamageTypeImpl.get("minecraft:in_wall");
DamageType IN_FIRE = DamageTypeImpl.get("minecraft:in_fire");
DamageType ARROW = DamageTypeImpl.get("minecraft:arrow");
DamageType HOT_FLOOR = DamageTypeImpl.get("minecraft:hot_floor");
DamageType DROWN = DamageTypeImpl.get("minecraft:drown");
DamageType STARVE = DamageTypeImpl.get("minecraft:starve");
DamageType GENERIC_KILL = DamageTypeImpl.get("minecraft:generic_kill");
DamageType DRAGON_BREATH = DamageTypeImpl.get("minecraft:dragon_breath");
DamageType MOB_ATTACK_NO_AGGRO = DamageTypeImpl.get("minecraft:mob_attack_no_aggro");
DamageType LAVA = DamageTypeImpl.get("minecraft:lava");
DamageType OUTSIDE_BORDER = DamageTypeImpl.get("minecraft:outside_border");
DamageType FLY_INTO_WALL = DamageTypeImpl.get("minecraft:fly_into_wall");
DamageType LIGHTNING_BOLT = DamageTypeImpl.get("minecraft:lightning_bolt");
DamageType PLAYER_ATTACK = DamageTypeImpl.get("minecraft:player_attack");
DamageType FREEZE = DamageTypeImpl.get("minecraft:freeze");
DamageType FALLING_ANVIL = DamageTypeImpl.get("minecraft:falling_anvil");
DamageType OUT_OF_WORLD = DamageTypeImpl.get("minecraft:out_of_world");
DamageType MAGIC = DamageTypeImpl.get("minecraft:magic");
DamageType SWEET_BERRY_BUSH = DamageTypeImpl.get("minecraft:sweet_berry_bush");
DamageType FIREWORKS = DamageTypeImpl.get("minecraft:fireworks");
DamageType EXPLOSION = DamageTypeImpl.get("minecraft:explosion");
DamageType BAD_RESPAWN_POINT = DamageTypeImpl.get("minecraft:bad_respawn_point");
DamageType STALAGMITE = DamageTypeImpl.get("minecraft:stalagmite");
DamageType THORNS = DamageTypeImpl.get("minecraft:thorns");
DamageType INDIRECT_MAGIC = DamageTypeImpl.get("minecraft:indirect_magic");
DamageType CRAMMING = DamageTypeImpl.get("minecraft:cramming");
DamageType CACTUS = DamageTypeImpl.get("minecraft:cactus");
DamageType GENERIC = DamageTypeImpl.get("minecraft:generic");
}

View File

@ -6,6 +6,7 @@ import net.minestom.server.attribute.AttributeInstance;
import net.minestom.server.collision.BoundingBox;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.damage.Damage;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.entity.metadata.LivingEntityMeta;
import net.minestom.server.event.EventDispatcher;
@ -48,7 +49,7 @@ public class LivingEntity extends Entity implements EquipmentHandler {
protected boolean isDead;
protected DamageType lastDamageSource;
protected Damage lastDamage;
// Bounding box used for items' pickup (see LivingEntity#setBoundingBox)
protected BoundingBox expandedBoundingBox;
@ -310,26 +311,29 @@ public class LivingEntity extends Entity implements EquipmentHandler {
}
}
public boolean damage(@NotNull DamageType type, float amount) {
return damage(new Damage(type, null, null, null, amount));
}
/**
* Damages the entity by a value, the type of the damage also has to be specified.
*
* @param type the damage type
* @param value the amount of damage
* @param damage the damage to be applied
* @return true if damage has been applied, false if it didn't
*/
public boolean damage(@NotNull DamageType type, float value) {
public boolean damage(@NotNull Damage damage) {
if (isDead())
return false;
if (isInvulnerable() || isImmune(type)) {
if (isInvulnerable() || isImmune(damage.getType())) {
return false;
}
EntityDamageEvent entityDamageEvent = new EntityDamageEvent(this, type, value, type.getSound(this));
EntityDamageEvent entityDamageEvent = new EntityDamageEvent(this, damage, damage.getSound(this));
EventDispatcher.callCancellable(entityDamageEvent, () -> {
// Set the last damage type since the event is not cancelled
this.lastDamageSource = entityDamageEvent.getDamageType();
this.lastDamage = entityDamageEvent.getDamage();
float remainingDamage = entityDamageEvent.getDamage();
float remainingDamage = entityDamageEvent.getDamage().getAmount();
if (entityDamageEvent.shouldAnimate()) {
sendPacketToViewersAndSelf(new EntityAnimationPacket(getEntityId(), EntityAnimationPacket.Animation.TAKE_DAMAGE));
@ -410,8 +414,8 @@ public class LivingEntity extends Entity implements EquipmentHandler {
*
* @return the last damage source, null if not any
*/
public @Nullable DamageType getLastDamageSource() {
return lastDamageSource;
public @Nullable Damage getLastDamageSource() {
return lastDamage;
}
/**

View File

@ -69,7 +69,6 @@ import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.network.player.PlayerSocketConnection;
import net.minestom.server.recipe.Recipe;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.registry.Registry;
import net.minestom.server.resourcepack.ResourcePack;
import net.minestom.server.scoreboard.BelowNameTag;
import net.minestom.server.scoreboard.Team;
@ -98,8 +97,6 @@ import org.jctools.queues.MpscUnboundedXaddArrayQueue;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
@ -415,8 +412,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
// get death screen text to the killed player
{
if (lastDamageSource != null) {
deathText = lastDamageSource.buildDeathScreenText(this);
if (lastDamage != null) {
deathText = lastDamage.buildDeathScreenText(this);
} else { // may happen if killed by the server without applying damage
deathText = Component.text("Killed by poor programming.");
}
@ -424,8 +421,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
// get death message to chat
{
if (lastDamageSource != null) {
chatMessage = lastDamageSource.buildDeathMessage(this);
if (lastDamage != null) {
chatMessage = lastDamage.buildDeathMessage(this);
} else { // may happen if killed by the server without applying damage
chatMessage = Component.text(getUsername() + " was killed by poor programming.");
}
@ -920,7 +917,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
@Override
public boolean isImmune(@NotNull DamageType type) {
if (!getGameMode().canTakeDamage()) {
return type != DamageType.VOID;
return type != DamageType.OUT_OF_WORLD;
}
return super.isImmune(type);
}

View File

@ -3,6 +3,7 @@ package net.minestom.server.entity.ai.target;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.ai.TargetSelector;
import net.minestom.server.entity.damage.Damage;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.entity.damage.EntityDamage;
import org.jetbrains.annotations.NotNull;
@ -21,8 +22,8 @@ public class LastEntityDamagerTarget extends TargetSelector {
@Override
public Entity findTarget() {
final DamageType damageType = entityCreature.getLastDamageSource();
if (!(damageType instanceof EntityDamage entityDamage)) {
final Damage damage = entityCreature.getLastDamageSource();
if (!(damage instanceof EntityDamage entityDamage)) {
// No damager recorded, return null
return null;
}

View File

@ -0,0 +1,179 @@
package net.minestom.server.entity.damage;
import net.kyori.adventure.text.Component;
import net.minestom.server.coordinate.Point;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.LivingEntity;
import net.minestom.server.entity.Player;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.tag.Taggable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents a type of damage, required when calling {@link LivingEntity#damage(Damage)}
* and retrieved in {@link net.minestom.server.event.entity.EntityDamageEvent}.
* <p>
* This class can be extended if you need to include custom fields and/or methods.
*/
public class Damage implements Taggable {
private final DamageType type;
private final Entity source;
private final Entity attacker;
private final Point sourcePosition;
private final TagHandler tagHandler = TagHandler.newHandler();
private float amount;
/**
* Creates a new damage type.
*
* @param type the type of this damage
* @param amount amount of damage
*/
public Damage(@NotNull DamageType type, @Nullable Entity source, @Nullable Entity attacker, @Nullable Point sourcePosition, float amount) {
this.type = type;
this.source = source;
this.attacker = attacker;
this.sourcePosition = sourcePosition;
this.amount = amount;
}
/**
* Gets the type of this damage.
* <p>
* It does not have to be unique to this object.o
*
* @return the damage type
*/
public @NotNull DamageType getType() {
return type;
}
/**
* Gets the "attacker" of the damage.
* This is the indirect cause of the damage, like the shooter of a projectile, or null if there was none.
*
* @return the attacker
*/
public @Nullable Entity getAttacker() {
return attacker;
}
/**
* Gets the direct source of the damage.
* This is the entity that directly causes the damage, like a projectile, or null if there was none.
*
* @return the source
*/
public @Nullable Entity getSource() {
return source;
}
/**
* Gets the position of the source of the damage, or null if there is none.
* This may differ from the source entity's position.
*
* @return The source position
*/
public @Nullable Point getSourcePosition() {
return sourcePosition;
}
/**
* Builds the death message linked to this damage type.
* <p>
* Used in {@link Player#kill()} to broadcast the proper message.
*
* @param killed the player who has been killed
* @return the death message, null to do not send anything
*/
public @Nullable Component buildDeathMessage(@NotNull Player killed) {
return Component.translatable("death.attack." + type.messageId(), Component.text(killed.getUsername()));
}
/**
* Convenient method to create an {@link EntityProjectileDamage}.
*
* @param shooter the shooter
* @param projectile the actual projectile
* @param amount amount of damage
* @return a new {@link EntityProjectileDamage}
*/
public static @NotNull Damage fromProjectile(@Nullable Entity shooter, @NotNull Entity projectile, float amount) {
return new EntityProjectileDamage(shooter, projectile, amount);
}
/**
* Convenient method to create an {@link EntityDamage}.
*
* @param player the player damager
* @param amount amount of damage
* @return a new {@link EntityDamage}
*/
public static @NotNull EntityDamage fromPlayer(@NotNull Player player, float amount) {
return new EntityDamage(player, amount);
}
/**
* Convenient method to create an {@link EntityDamage}.
*
* @param entity the entity damager
* @param amount amount of damage
* @return a new {@link EntityDamage}
*/
public static @NotNull EntityDamage fromEntity(@NotNull Entity entity, float amount) {
return new EntityDamage(entity, amount);
}
public static @NotNull PositionalDamage fromPosition(@NotNull DamageType type, @NotNull Point sourcePosition, float amount) {
return new PositionalDamage(type, sourcePosition, amount);
}
/**
* Builds the text sent to a player in his death screen.
*
* @param killed the player who has been killed
* @return the death screen text, null to do not send anything
*/
public @Nullable Component buildDeathScreenText(@NotNull Player killed) {
return Component.translatable("death.attack." + type.messageId());
}
/**
* Sound event to play when the given entity is hit by this damage. Possible to return null if no sound should be played
*
* @param entity the entity hit by this damage
* @return the sound to play when the given entity is hurt by this damage type. Can be null if no sound should play
*/
public @Nullable SoundEvent getSound(@NotNull LivingEntity entity) {
if (entity instanceof Player) {
return getPlayerSound((Player) entity);
}
return getGenericSound(entity);
}
protected SoundEvent getGenericSound(@NotNull LivingEntity entity) {
return SoundEvent.ENTITY_GENERIC_HURT;
}
protected SoundEvent getPlayerSound(@NotNull Player player) {
if (type == DamageType.ON_FIRE) return SoundEvent.ENTITY_PLAYER_HURT_ON_FIRE;
return SoundEvent.ENTITY_PLAYER_HURT;
}
@Override
public @NotNull TagHandler tagHandler() {
return tagHandler;
}
public float getAmount() {
return amount;
}
public void setAmount(float amount) {
this.amount = amount;
}
}

View File

@ -1,131 +1,60 @@
package net.minestom.server.entity.damage;
import net.kyori.adventure.text.Component;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.LivingEntity;
import net.minestom.server.entity.Player;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.tag.TagHandler;
import net.minestom.server.tag.Taggable;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
/**
* Represents a type of damage, required when calling {@link LivingEntity#damage(DamageType, float)}
* and retrieved in {@link net.minestom.server.event.entity.EntityDamageEvent}.
* <p>
* This class can be extended if you need to include custom fields and/or methods.
*/
public class DamageType implements Taggable {
public static final DamageType VOID = new DamageType("attack.outOfWorld");
public static final DamageType GRAVITY = new DamageType("attack.fall");
public static final DamageType ON_FIRE = new DamageType("attack.onFire") {
@Override
protected SoundEvent getPlayerSound(@NotNull Player player) {
return SoundEvent.ENTITY_PLAYER_HURT_ON_FIRE;
}
};
private final String identifier;
private final TagHandler tagHandler = TagHandler.newHandler();
import java.util.Collection;
public sealed interface DamageType extends ProtocolObject, DamageTypes permits DamageTypeImpl {
/**
* Creates a new damage type.
* Returns the damage type registry.
*
* @param identifier the identifier of this damage type,
* does not need to be unique
* @return the damage type registry
*/
public DamageType(@NotNull String identifier) {
this.identifier = identifier;
}
/**
* Gets the identifier of this damage type.
* <p>
* It does not have to be unique to this object.o
*
* @return the damage type identifier
*/
public @NotNull String getIdentifier() {
return identifier;
}
/**
* Builds the death message linked to this damage type.
* <p>
* Used in {@link Player#kill()} to broadcast the proper message.
*
* @param killed the player who has been killed
* @return the death message, null to do not send anything
*/
public @Nullable Component buildDeathMessage(@NotNull Player killed) {
return Component.translatable("death." + identifier, Component.text(killed.getUsername()));
}
/**
* Convenient method to create an {@link EntityProjectileDamage}.
*
* @param shooter the shooter
* @param projectile the actual projectile
* @return a new {@link EntityProjectileDamage}
*/
public static @NotNull DamageType fromProjectile(@Nullable Entity shooter, @NotNull Entity projectile) {
return new EntityProjectileDamage(shooter, projectile);
}
/**
* Convenient method to create an {@link EntityDamage}.
*
* @param player the player damager
* @return a new {@link EntityDamage}
*/
public static @NotNull EntityDamage fromPlayer(@NotNull Player player) {
return new EntityDamage(player);
}
/**
* Convenient method to create an {@link EntityDamage}.
*
* @param entity the entity damager
* @return a new {@link EntityDamage}
*/
public static @NotNull EntityDamage fromEntity(@NotNull Entity entity) {
return new EntityDamage(entity);
}
/**
* Builds the text sent to a player in his death screen.
*
* @param killed the player who has been killed
* @return the death screen text, null to do not send anything
*/
public @Nullable Component buildDeathScreenText(@NotNull Player killed) {
return Component.translatable("death." + identifier);
}
/**
* Sound event to play when the given entity is hit by this damage. Possible to return null if no sound should be played
*
* @param entity the entity hit by this damage
* @return the sound to play when the given entity is hurt by this damage type. Can be null if no sound should play
*/
public @Nullable SoundEvent getSound(@NotNull LivingEntity entity) {
if (entity instanceof Player) {
return getPlayerSound((Player) entity);
}
return getGenericSound(entity);
}
protected SoundEvent getGenericSound(@NotNull LivingEntity entity) {
return SoundEvent.ENTITY_GENERIC_HURT;
}
protected SoundEvent getPlayerSound(@NotNull Player player) {
return SoundEvent.ENTITY_PLAYER_HURT;
}
@Contract(pure = true)
@NotNull Registry.DamageTypeEntry registry();
@Override
public @NotNull TagHandler tagHandler() {
return tagHandler;
default @NotNull NamespaceID namespace() {
return registry().namespace();
}
}
default double exhaustion() {
return registry().exhaustion();
}
default String messageId() {
return registry().messageId();
}
default String scaling() {
return registry().scaling();
}
NBTCompound asNBT();
static @NotNull Collection<@NotNull DamageType> values() {
return DamageTypeImpl.values();
}
static DamageType fromNamespaceId(@NotNull String namespaceID) {
return DamageTypeImpl.getSafe(namespaceID);
}
static DamageType fromNamespaceId(@NotNull NamespaceID namespaceID) {
return fromNamespaceId(namespaceID.asString());
}
static @Nullable DamageType fromId(int id) {
return DamageTypeImpl.getId(id);
}
static NBTCompound getNBT() {
return DamageTypeImpl.getNBT();
}
}

View File

@ -0,0 +1,77 @@
package net.minestom.server.entity.damage;
import net.minestom.server.registry.Registry;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTType;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
record DamageTypeImpl(Registry.DamageTypeEntry registry, int id) implements DamageType {
private static final Registry.Container<DamageType> CONTAINER;
static {
AtomicInteger i = new AtomicInteger();
CONTAINER = Registry.createContainer(Registry.Resource.DAMAGE_TYPES,
(namespace, properties) -> new DamageTypeImpl(Registry.damageType(namespace, properties), i.getAndIncrement()));
}
static DamageType get(@NotNull String namespace) {
return CONTAINER.get(namespace);
}
static DamageType getSafe(@NotNull String namespace) {
return CONTAINER.getSafe(namespace);
}
static DamageType getId(int id) {
return CONTAINER.getId(id);
}
@Override
public NBTCompound asNBT() {
var elem = new HashMap<String, NBT>();
elem.put("exhaustion", NBT.Float(registry.exhaustion()));
elem.put("message_id", NBT.String(registry.messageId()));
elem.put("scaling", NBT.String(registry.scaling()));
return NBT.Compound(elem);
}
static Collection<DamageType> values() {
return CONTAINER.values();
}
@Override
public String toString() {
return name();
}
@Override
public int id() {
return id;
}
private static NBTCompound lazyNbt = null;
static NBTCompound getNBT() {
if (lazyNbt == null) {
var damageTypes = values().stream()
.map((damageType) -> NBT.Compound(Map.of(
"id", NBT.Int(damageType.id()),
"name", NBT.String(damageType.name()),
"element", damageType.asNBT()
)))
.toList();
lazyNbt = NBT.Compound(Map.of(
"type", NBT.String("minecraft:damage_type"),
"value", NBT.List(NBTType.TAG_Compound, damageTypes)
));
}
return lazyNbt;
}
}

View File

@ -6,13 +6,10 @@ import org.jetbrains.annotations.NotNull;
/**
* Represents damage inflicted by an {@link Entity}.
*/
public class EntityDamage extends DamageType {
public class EntityDamage extends Damage {
private final Entity source;
public EntityDamage(@NotNull Entity source) {
super("entity_source");
this.source = source;
public EntityDamage(@NotNull Entity source, float amount) {
super(DamageType.MOB_ATTACK, source, source, null, amount);
}
/**
@ -20,8 +17,13 @@ public class EntityDamage extends DamageType {
*
* @return the source
*/
@NotNull
public Entity getSource() {
return source;
@Override
public @NotNull Entity getSource() {
return super.getSource();
}
}
@Override
public @NotNull Entity getAttacker() {
return getSource();
}
}

View File

@ -7,15 +7,10 @@ import org.jetbrains.annotations.Nullable;
/**
* Represents damage inflicted by an entity, via a projectile.
*/
public class EntityProjectileDamage extends DamageType {
public class EntityProjectileDamage extends Damage {
private final Entity shooter;
private final Entity projectile;
public EntityProjectileDamage(@Nullable Entity shooter, @NotNull Entity projectile) {
super("projectile_source");
this.shooter = shooter;
this.projectile = projectile;
public EntityProjectileDamage(@Nullable Entity shooter, @NotNull Entity projectile, float amount) {
super(DamageType.MOB_PROJECTILE, projectile, shooter, null, amount);
}
/**
@ -25,7 +20,7 @@ public class EntityProjectileDamage extends DamageType {
*/
@NotNull
public Entity getProjectile() {
return projectile;
return getSource();
}
/**
@ -35,6 +30,11 @@ public class EntityProjectileDamage extends DamageType {
*/
@Nullable
public Entity getShooter() {
return shooter;
return getAttacker();
}
}
@Override
public @NotNull Entity getSource() {
return super.getSource();
}
}

View File

@ -0,0 +1,19 @@
package net.minestom.server.entity.damage;
import net.minestom.server.coordinate.Point;
import org.jetbrains.annotations.NotNull;
/**
* Represents damage that is associated with a certain position.
*/
public class PositionalDamage extends Damage {
public PositionalDamage(@NotNull DamageType type, @NotNull Point sourcePosition, float amount) {
super(type, null, null, sourcePosition, amount);
}
@Override
public @NotNull Point getSourcePosition() {
return super.getSourcePosition();
}
}

View File

@ -2,6 +2,7 @@ package net.minestom.server.event.entity;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.LivingEntity;
import net.minestom.server.entity.damage.Damage;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.event.trait.CancellableEvent;
import net.minestom.server.event.trait.EntityInstanceEvent;
@ -15,17 +16,14 @@ import org.jetbrains.annotations.Nullable;
public class EntityDamageEvent implements EntityInstanceEvent, CancellableEvent {
private final Entity entity;
private final DamageType damageType;
private float damage;
private final Damage damage;
private SoundEvent sound;
private boolean animation = true;
private boolean cancelled;
public EntityDamageEvent(@NotNull LivingEntity entity, @NotNull DamageType damageType,
float damage, @Nullable SoundEvent sound) {
public EntityDamageEvent(@NotNull LivingEntity entity, @NotNull Damage damage, @Nullable SoundEvent sound) {
this.entity = entity;
this.damageType = damageType;
this.damage = damage;
this.sound = sound;
}
@ -42,28 +40,10 @@ public class EntityDamageEvent implements EntityInstanceEvent, CancellableEvent
* @return the damage type
*/
@NotNull
public DamageType getDamageType() {
return damageType;
}
/**
* Gets the damage amount.
*
* @return the damage amount
*/
public float getDamage() {
public Damage getDamage() {
return damage;
}
/**
* Changes the damage amount.
*
* @param damage the new damage amount
*/
public void setDamage(float damage) {
this.damage = damage;
}
/**
* Gets the damage sound.
*

View File

@ -53,6 +53,11 @@ public final class Registry {
return new PotionEffectEntry(namespace, main, null);
}
@ApiStatus.Internal
public static DamageTypeEntry damageType(String namespace, @NotNull Properties main) {
return new DamageTypeEntry(namespace, main, null);
}
@ApiStatus.Internal
public static Map<String, Map<String, Object>> load(Resource resource) {
Map<String, Map<String, Object>> map = new HashMap<>();
@ -409,6 +414,23 @@ public final class Registry {
}
}
public record DamageTypeEntry(NamespaceID namespace, float exhaustion,
String messageId,
String scaling,
@Nullable String effects,
@Nullable String deathMessageType,
Properties custom) implements Entry {
public DamageTypeEntry(String namespace, Properties main, Properties custom) {
this(NamespaceID.from(namespace),
(float) main.getDouble("exhaustion"),
main.getString("message_id"),
main.getString("scaling"),
main.getString("effects"),
main.getString("death_message_type"),
custom);
}
}
public record EnchantmentEntry(NamespaceID namespace, int id,
String translationKey,
double maxLevel,