diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index 8ba63c043..47f4c034a 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -12,6 +12,9 @@ import net.minestom.server.network.packet.PacketWriter; import net.minestom.server.network.packet.server.play.CollectItemPacket; import net.minestom.server.network.packet.server.play.EntityAnimationPacket; import net.minestom.server.network.packet.server.play.EntityPropertiesPacket; +import net.minestom.server.network.packet.server.play.SoundEffectPacket; +import net.minestom.server.sound.Sound; +import net.minestom.server.sound.SoundCategory; import net.minestom.server.utils.Position; import net.minestom.server.utils.time.TimeUnit; @@ -33,8 +36,22 @@ public abstract class LivingEntity extends Entity { private boolean isHandActive; private boolean offHand; private boolean riptideSpinAttack; + + /** + * Time at which this entity must be extinguished + */ private long fireExtinguishTime; + /** + * Last time the fire damage was applied + */ + private long lastFireDamageTime; + + /** + * Period, in ms, between two fire damage applications + */ + private long fireDamagePeriod = 1000L; + public LivingEntity(int entityType, Position spawnPosition) { super(entityType, spawnPosition); setupAttributes(); @@ -50,6 +67,11 @@ public abstract class LivingEntity extends Entity { if(isOnFire()) { if(System.currentTimeMillis() > fireExtinguishTime) { setOnFire(false); + } else { + if(System.currentTimeMillis() - lastFireDamageTime > fireDamagePeriod) { + damage(DamageType.ON_FIRE, 1.0f); + lastFireDamageTime = System.currentTimeMillis(); + } } } @@ -151,6 +173,21 @@ public abstract class LivingEntity extends Entity { entityAnimationPacket.animation = EntityAnimationPacket.Animation.TAKE_DAMAGE; sendPacketToViewersAndSelf(entityAnimationPacket); setHealth(getHealth() - damage); + + // play damage sound + Sound sound = type.getSound(this); + if(sound != null) { + SoundCategory soundCategory; + if(this instanceof Player) { + soundCategory = SoundCategory.PLAYERS; + } else { + // TODO: separate living entity categories + soundCategory = SoundCategory.HOSTILE; + } + + SoundEffectPacket damageSoundPacket = SoundEffectPacket.create(soundCategory, sound, getPosition().getX(), getPosition().getY(), getPosition().getZ(), 1.0f, 1.0f); + sendPacketToViewersAndSelf(damageSoundPacket); + } }); return !entityDamageEvent.isCancelled(); @@ -258,4 +295,12 @@ public abstract class LivingEntity extends Entity { damage(DamageType.VOID, 10f); } } + + public long getFireDamagePeriod() { + return fireDamagePeriod; + } + + public void setFireDamagePeriod(long fireDamagePeriod) { + this.fireDamagePeriod = fireDamagePeriod; + } } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 6e6169d5a..9e6a217b2 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -419,9 +419,9 @@ public class Player extends LivingEntity { SoundEffectPacket soundEffectPacket = new SoundEffectPacket(); soundEffectPacket.soundId = sound.getId(); soundEffectPacket.soundCategory = soundCategory; - soundEffectPacket.x = x; - soundEffectPacket.y = y; - soundEffectPacket.z = z; + soundEffectPacket.x = x*8; + soundEffectPacket.y = y*8; + soundEffectPacket.z = z*8; soundEffectPacket.volume = volume; soundEffectPacket.pitch = pitch; playerConnection.sendPacket(soundEffectPacket); diff --git a/src/main/java/net/minestom/server/entity/damage/DamageType.java b/src/main/java/net/minestom/server/entity/damage/DamageType.java index 47df17bc4..548281534 100644 --- a/src/main/java/net/minestom/server/entity/damage/DamageType.java +++ b/src/main/java/net/minestom/server/entity/damage/DamageType.java @@ -3,7 +3,9 @@ package net.minestom.server.entity.damage; import club.thectm.minecraft.text.TextBuilder; import club.thectm.minecraft.text.TextObject; import net.minestom.server.entity.Entity; +import net.minestom.server.entity.LivingEntity; import net.minestom.server.entity.Player; +import net.minestom.server.sound.Sound; /** * Represents a type of damage @@ -12,7 +14,12 @@ public class DamageType { 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"); + public static final DamageType ON_FIRE = new DamageType("attack.onFire") { + @Override + protected Sound getPlayerSound(Player player) { + return Sound.ENTITY_PLAYER_HURT_ON_FIRE; + } + }; private final String identifier; public DamageType(String identifier) { @@ -38,4 +45,24 @@ public class DamageType { public TextObject buildDeathScreenMessage(Player killed) { return buildChatMessage(killed); } + + /** + * 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 Sound getSound(LivingEntity entity) { + if(entity instanceof Player) { + return getPlayerSound((Player)entity); + } + return getGenericSound(entity); + } + + protected Sound getGenericSound(LivingEntity entity) { + return Sound.ENTITY_GENERIC_HURT; + } + + protected Sound getPlayerSound(Player player) { + return Sound.ENTITY_PLAYER_HURT; + } } diff --git a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java index aab548a45..b876c2bd4 100644 --- a/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java +++ b/src/main/java/net/minestom/server/network/packet/server/play/SoundEffectPacket.java @@ -3,6 +3,7 @@ package net.minestom.server.network.packet.server.play; import net.minestom.server.network.packet.PacketWriter; import net.minestom.server.network.packet.server.ServerPacket; import net.minestom.server.network.packet.server.ServerPacketIdentifier; +import net.minestom.server.sound.Sound; import net.minestom.server.sound.SoundCategory; public class SoundEffectPacket implements ServerPacket { @@ -13,6 +14,19 @@ public class SoundEffectPacket implements ServerPacket { public float volume; public float pitch; + public static SoundEffectPacket create(SoundCategory category, Sound sound, float x, float y, float z, float volume, float pitch) { + SoundEffectPacket packet = new SoundEffectPacket(); + packet.soundId = sound.getId(); + packet.soundCategory = category; + // *8 converts to fixed-point representation with 3 bits for fractional part + packet.x = (int) (x * 8); + packet.y = (int) (y * 8); + packet.z = (int) (z * 8); + packet.volume = volume; + packet.pitch = pitch; + return packet; + } + @Override public void write(PacketWriter writer) { writer.writeVarInt(soundId);