mirror of https://github.com/Minestom/Minestom.git
199 lines
7.0 KiB
Java
199 lines
7.0 KiB
Java
package net.minestom.server.entity;
|
|
|
|
import net.minestom.server.collision.BoundingBox;
|
|
import net.minestom.server.entity.property.Attribute;
|
|
import net.minestom.server.event.DeathEvent;
|
|
import net.minestom.server.event.PickupItemEvent;
|
|
import net.minestom.server.instance.Chunk;
|
|
import net.minestom.server.item.ItemStack;
|
|
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.utils.Position;
|
|
|
|
import java.util.Set;
|
|
import java.util.function.Consumer;
|
|
|
|
public abstract class LivingEntity extends Entity {
|
|
|
|
protected boolean canPickupItem;
|
|
protected boolean isDead;
|
|
|
|
private float health;
|
|
|
|
// Bounding box used for items' pickup (see LivingEntity#setBoundingBox)
|
|
private BoundingBox expandedBoundingBox;
|
|
|
|
private float[] attributeValues = new float[Attribute.values().length];
|
|
|
|
private boolean isHandActive;
|
|
private boolean offHand;
|
|
private boolean riptideSpinAttack;
|
|
|
|
public LivingEntity(int entityType, Position spawnPosition) {
|
|
super(entityType, spawnPosition);
|
|
setupAttributes();
|
|
setGravity(0.02f);
|
|
}
|
|
|
|
public LivingEntity(int entityType) {
|
|
this(entityType, new Position());
|
|
}
|
|
|
|
@Override
|
|
public void update() {
|
|
// Items picking
|
|
if (canPickupItem) {
|
|
Chunk chunk = instance.getChunkAt(getPosition()); // TODO check surrounding chunks
|
|
Set<Entity> entities = instance.getChunkEntities(chunk);
|
|
BoundingBox livingBoundingBox = expandedBoundingBox;
|
|
for (Entity entity : entities) {
|
|
if (entity instanceof ItemEntity) {
|
|
ItemEntity itemEntity = (ItemEntity) entity;
|
|
if (!itemEntity.isPickable())
|
|
continue;
|
|
BoundingBox itemBoundingBox = itemEntity.getBoundingBox();
|
|
if (livingBoundingBox.intersect(itemBoundingBox)) {
|
|
synchronized (itemEntity) {
|
|
if (itemEntity.shouldRemove() || itemEntity.isRemoveScheduled())
|
|
continue;
|
|
ItemStack item = itemEntity.getItemStack();
|
|
PickupItemEvent pickupItemEvent = new PickupItemEvent(item);
|
|
callCancellableEvent(PickupItemEvent.class, pickupItemEvent, () -> {
|
|
CollectItemPacket collectItemPacket = new CollectItemPacket();
|
|
collectItemPacket.collectedEntityId = itemEntity.getEntityId();
|
|
collectItemPacket.collectorEntityId = getEntityId();
|
|
collectItemPacket.pickupItemCount = item.getAmount();
|
|
sendPacketToViewersAndSelf(collectItemPacket);
|
|
entity.remove();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Consumer<PacketWriter> getMetadataConsumer() {
|
|
return packet -> {
|
|
super.getMetadataConsumer().accept(packet);
|
|
packet.writeByte((byte) 7);
|
|
packet.writeByte(METADATA_BYTE);
|
|
byte activeHandValue = 0;
|
|
if (isHandActive) {
|
|
activeHandValue += 1;
|
|
if (offHand)
|
|
activeHandValue += 2;
|
|
if (riptideSpinAttack)
|
|
activeHandValue += 4;
|
|
}
|
|
packet.writeByte(activeHandValue);
|
|
|
|
// TODO all remaining metadata
|
|
};
|
|
}
|
|
|
|
public void kill() {
|
|
refreshIsDead(true); // So the entity isn't killed over and over again
|
|
triggerStatus((byte) 3); // Start death animation status
|
|
setHealth(0);
|
|
DeathEvent deathEvent = new DeathEvent();
|
|
callEvent(DeathEvent.class, deathEvent);
|
|
}
|
|
|
|
public void damage(float value) {
|
|
EntityAnimationPacket entityAnimationPacket = new EntityAnimationPacket();
|
|
entityAnimationPacket.entityId = getEntityId();
|
|
entityAnimationPacket.animation = EntityAnimationPacket.Animation.TAKE_DAMAGE;
|
|
sendPacketToViewersAndSelf(entityAnimationPacket);
|
|
setHealth(getHealth() - value);
|
|
}
|
|
|
|
public float getHealth() {
|
|
return health;
|
|
}
|
|
|
|
public void setHealth(float health) {
|
|
health = Math.min(health, getMaxHealth());
|
|
|
|
this.health = health;
|
|
if (this.health <= 0 && !isDead) {
|
|
kill();
|
|
}
|
|
}
|
|
|
|
public float getMaxHealth() {
|
|
return getAttributeValue(Attribute.MAX_HEALTH);
|
|
}
|
|
|
|
public void heal() {
|
|
setHealth(getAttributeValue(Attribute.MAX_HEALTH));
|
|
}
|
|
|
|
public void setAttribute(Attribute attribute, float value) {
|
|
this.attributeValues[attribute.ordinal()] = value;
|
|
}
|
|
|
|
public float getAttributeValue(Attribute attribute) {
|
|
return this.attributeValues[attribute.ordinal()];
|
|
}
|
|
|
|
public boolean isDead() {
|
|
return isDead;
|
|
}
|
|
|
|
public boolean canPickupItem() {
|
|
return canPickupItem;
|
|
}
|
|
|
|
public void setCanPickupItem(boolean canPickupItem) {
|
|
this.canPickupItem = canPickupItem;
|
|
}
|
|
|
|
@Override
|
|
public void setBoundingBox(float x, float y, float z) {
|
|
super.setBoundingBox(x, y, z);
|
|
this.expandedBoundingBox = getBoundingBox().expand(1, 0.5f, 1);
|
|
}
|
|
|
|
public void refreshActiveHand(boolean isHandActive, boolean offHand, boolean riptideSpinAttack) {
|
|
this.isHandActive = isHandActive;
|
|
this.offHand = offHand;
|
|
this.riptideSpinAttack = riptideSpinAttack;
|
|
}
|
|
|
|
public void refreshIsDead(boolean isDead) {
|
|
this.isDead = isDead;
|
|
}
|
|
|
|
protected EntityPropertiesPacket getPropertiesPacket() {
|
|
EntityPropertiesPacket propertiesPacket = new EntityPropertiesPacket();
|
|
propertiesPacket.entityId = getEntityId();
|
|
|
|
int length = Attribute.values().length;
|
|
EntityPropertiesPacket.Property[] properties = new EntityPropertiesPacket.Property[length];
|
|
for (int i = 0; i < length; i++) {
|
|
Attribute attribute = Attribute.values()[i];
|
|
EntityPropertiesPacket.Property property = new EntityPropertiesPacket.Property();
|
|
float maxValue = attribute.getMaxVanillaValue();
|
|
float value = getAttributeValue(attribute);
|
|
value = value > maxValue ? maxValue : value; // Bypass vanilla limit client-side if needed (by sending the max value allowed)
|
|
|
|
property.key = attribute.getKey();
|
|
property.value = value;
|
|
properties[i] = property;
|
|
}
|
|
|
|
propertiesPacket.properties = properties;
|
|
return propertiesPacket;
|
|
}
|
|
|
|
private void setupAttributes() {
|
|
for (Attribute attribute : Attribute.values()) {
|
|
setAttribute(attribute, attribute.getDefaultValue());
|
|
}
|
|
}
|
|
}
|