2020-04-24 03:25:58 +02:00
|
|
|
package net.minestom.server.entity;
|
2019-08-10 08:44:35 +02:00
|
|
|
|
2020-07-24 02:31:10 +02:00
|
|
|
import com.extollit.gaming.ai.path.HydrazinePathFinder;
|
2020-09-01 21:16:07 +02:00
|
|
|
import com.extollit.gaming.ai.path.model.IPath;
|
2020-11-17 14:47:49 +01:00
|
|
|
import net.minestom.server.attribute.Attributes;
|
2020-08-06 11:56:43 +02:00
|
|
|
import net.minestom.server.entity.ai.GoalSelector;
|
|
|
|
import net.minestom.server.entity.ai.TargetSelector;
|
2020-11-29 23:14:21 +01:00
|
|
|
import net.minestom.server.entity.pathfinding.NavigableEntity;
|
2020-07-24 02:49:55 +02:00
|
|
|
import net.minestom.server.entity.pathfinding.PFPathingEntity;
|
2020-05-28 19:15:55 +02:00
|
|
|
import net.minestom.server.event.entity.EntityAttackEvent;
|
2020-05-07 15:46:21 +02:00
|
|
|
import net.minestom.server.event.item.ArmorEquipEvent;
|
2020-07-24 02:31:10 +02:00
|
|
|
import net.minestom.server.instance.Instance;
|
2020-05-06 22:35:32 +02:00
|
|
|
import net.minestom.server.item.ItemStack;
|
2020-08-07 06:36:03 +02:00
|
|
|
import net.minestom.server.network.packet.server.play.EntityEquipmentPacket;
|
2020-11-10 18:13:24 +01:00
|
|
|
import net.minestom.server.network.packet.server.play.EntityMovementPacket;
|
2020-08-07 06:36:03 +02:00
|
|
|
import net.minestom.server.network.packet.server.play.SpawnLivingEntityPacket;
|
2020-04-24 03:25:58 +02:00
|
|
|
import net.minestom.server.network.player.PlayerConnection;
|
|
|
|
import net.minestom.server.utils.Position;
|
2020-05-15 18:03:28 +02:00
|
|
|
import net.minestom.server.utils.time.TimeUnit;
|
2020-10-24 10:46:23 +02:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
2020-10-24 11:19:54 +02:00
|
|
|
import org.jetbrains.annotations.Nullable;
|
2019-08-10 08:44:35 +02:00
|
|
|
|
2020-08-06 11:56:43 +02:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.function.Supplier;
|
|
|
|
|
2020-11-29 23:14:21 +01:00
|
|
|
public abstract class EntityCreature extends LivingEntity implements NavigableEntity {
|
|
|
|
|
|
|
|
// TODO all pathfinding requests should be process in another thread
|
2020-12-01 18:21:19 +01:00
|
|
|
private final Object pathLock = new Object();
|
2020-03-29 20:58:30 +02:00
|
|
|
|
2020-09-24 01:50:25 +02:00
|
|
|
private final PFPathingEntity pathingEntity = new PFPathingEntity(this);
|
2020-07-24 02:31:10 +02:00
|
|
|
private HydrazinePathFinder pathFinder;
|
2020-09-01 21:16:07 +02:00
|
|
|
private IPath path;
|
2020-08-06 13:46:30 +02:00
|
|
|
private Position pathPosition;
|
2020-03-29 20:58:30 +02:00
|
|
|
|
2020-10-22 12:55:53 +02:00
|
|
|
protected final List<GoalSelector> goalSelectors = new ArrayList<>();
|
|
|
|
protected final List<TargetSelector> targetSelectors = new ArrayList<>();
|
2020-08-06 11:56:43 +02:00
|
|
|
private GoalSelector currentGoalSelector;
|
|
|
|
|
|
|
|
private Entity target;
|
|
|
|
|
2020-05-06 22:35:32 +02:00
|
|
|
// Equipments
|
|
|
|
private ItemStack mainHandItem;
|
|
|
|
private ItemStack offHandItem;
|
|
|
|
|
|
|
|
private ItemStack helmet;
|
|
|
|
private ItemStack chestplate;
|
|
|
|
private ItemStack leggings;
|
|
|
|
private ItemStack boots;
|
|
|
|
|
2020-10-31 01:38:57 +01:00
|
|
|
public EntityCreature(@NotNull EntityType entityType, @NotNull Position spawnPosition) {
|
2020-05-25 01:12:12 +02:00
|
|
|
super(entityType, spawnPosition);
|
2020-05-23 04:20:01 +02:00
|
|
|
|
|
|
|
this.mainHandItem = ItemStack.getAirItem();
|
|
|
|
this.offHandItem = ItemStack.getAirItem();
|
|
|
|
|
|
|
|
this.helmet = ItemStack.getAirItem();
|
|
|
|
this.chestplate = ItemStack.getAirItem();
|
|
|
|
this.leggings = ItemStack.getAirItem();
|
|
|
|
this.boots = ItemStack.getAirItem();
|
2020-05-25 19:54:36 +02:00
|
|
|
|
|
|
|
heal();
|
2019-08-10 08:44:35 +02:00
|
|
|
}
|
|
|
|
|
2020-10-31 01:38:57 +01:00
|
|
|
public EntityCreature(@NotNull EntityType entityType, @NotNull Position spawnPosition, @Nullable Instance instance) {
|
|
|
|
this(entityType, spawnPosition);
|
|
|
|
|
|
|
|
if (instance != null) {
|
|
|
|
setInstance(instance);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-24 20:34:01 +02:00
|
|
|
@Override
|
2020-06-01 00:51:31 +02:00
|
|
|
public void update(long time) {
|
2020-08-06 11:56:43 +02:00
|
|
|
|
2020-08-06 18:32:56 +02:00
|
|
|
if (getInstance() == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-09 14:13:01 +02:00
|
|
|
// Goal selectors
|
2020-08-06 11:56:43 +02:00
|
|
|
{
|
|
|
|
// Supplier used to get the next goal selector which should start
|
|
|
|
// (null if not found)
|
|
|
|
final Supplier<GoalSelector> goalSelectorSupplier = () -> {
|
|
|
|
for (GoalSelector goalSelector : goalSelectors) {
|
|
|
|
final boolean start = goalSelector.shouldStart();
|
|
|
|
if (start) {
|
|
|
|
return goalSelector;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
|
|
|
|
// true if the goal selector changed this tick
|
|
|
|
boolean newGoalSelector = false;
|
|
|
|
|
|
|
|
if (currentGoalSelector == null) {
|
|
|
|
// No goal selector, get a new one
|
|
|
|
this.currentGoalSelector = goalSelectorSupplier.get();
|
|
|
|
newGoalSelector = currentGoalSelector != null;
|
|
|
|
} else {
|
|
|
|
final boolean stop = currentGoalSelector.shouldEnd();
|
|
|
|
if (stop) {
|
|
|
|
// The current goal selector stopped, find a new one
|
|
|
|
this.currentGoalSelector.end();
|
|
|
|
this.currentGoalSelector = goalSelectorSupplier.get();
|
|
|
|
newGoalSelector = currentGoalSelector != null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start the new goal selector
|
|
|
|
if (newGoalSelector) {
|
|
|
|
this.currentGoalSelector.start();
|
|
|
|
}
|
|
|
|
|
2020-08-09 14:13:01 +02:00
|
|
|
// Execute tick for the current goal selector
|
2020-08-06 11:56:43 +02:00
|
|
|
if (currentGoalSelector != null) {
|
2020-08-09 05:13:36 +02:00
|
|
|
currentGoalSelector.tick(time);
|
2020-08-06 11:56:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-04-09 14:25:42 +02:00
|
|
|
// Path finding
|
2020-11-29 23:14:21 +01:00
|
|
|
pathFindingTick(getAttributeValue(Attributes.MOVEMENT_SPEED));
|
2020-07-31 18:55:08 +02:00
|
|
|
|
|
|
|
super.update(time);
|
2020-07-24 02:31:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-29 22:52:07 +01:00
|
|
|
public void setInstance(@NotNull Instance instance) {
|
2020-07-24 02:31:10 +02:00
|
|
|
super.setInstance(instance);
|
|
|
|
this.pathFinder = new HydrazinePathFinder(pathingEntity, instance.getInstanceSpace());
|
2019-08-24 20:34:01 +02:00
|
|
|
}
|
|
|
|
|
2020-05-26 22:53:58 +02:00
|
|
|
@Override
|
|
|
|
public void spawn() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-08-24 20:34:01 +02:00
|
|
|
@Override
|
2019-08-21 16:50:52 +02:00
|
|
|
public void kill() {
|
2020-04-05 10:15:21 +02:00
|
|
|
super.kill();
|
|
|
|
|
|
|
|
// Needed for proper death animation (wait for it to finish before destroying the entity)
|
2020-05-15 18:03:28 +02:00
|
|
|
scheduleRemove(1000, TimeUnit.MILLISECOND);
|
2019-08-11 13:57:23 +02:00
|
|
|
}
|
|
|
|
|
2019-08-19 17:04:19 +02:00
|
|
|
@Override
|
2020-10-24 10:46:23 +02:00
|
|
|
public boolean addViewer(@NotNull Player player) {
|
2020-07-24 02:31:10 +02:00
|
|
|
final boolean result = super.addViewer(player);
|
2020-05-29 02:11:41 +02:00
|
|
|
|
2020-08-12 07:46:07 +02:00
|
|
|
final PlayerConnection playerConnection = player.getPlayerConnection();
|
2019-08-10 08:44:35 +02:00
|
|
|
|
2020-11-10 18:13:24 +01:00
|
|
|
EntityMovementPacket entityMovementPacket = new EntityMovementPacket();
|
|
|
|
entityMovementPacket.entityId = getEntityId();
|
2020-02-09 15:34:09 +01:00
|
|
|
|
2020-03-29 20:58:30 +02:00
|
|
|
SpawnLivingEntityPacket spawnLivingEntityPacket = new SpawnLivingEntityPacket();
|
|
|
|
spawnLivingEntityPacket.entityId = getEntityId();
|
|
|
|
spawnLivingEntityPacket.entityUuid = getUuid();
|
2020-08-03 18:45:58 +02:00
|
|
|
spawnLivingEntityPacket.entityType = getEntityType().getId();
|
2020-03-29 20:58:30 +02:00
|
|
|
spawnLivingEntityPacket.position = getPosition();
|
|
|
|
spawnLivingEntityPacket.headPitch = 0;
|
2020-02-09 15:34:09 +01:00
|
|
|
|
2020-11-10 18:13:24 +01:00
|
|
|
playerConnection.sendPacket(entityMovementPacket);
|
2020-03-29 20:58:30 +02:00
|
|
|
playerConnection.sendPacket(spawnLivingEntityPacket);
|
2020-05-24 19:22:58 +02:00
|
|
|
playerConnection.sendPacket(getVelocityPacket());
|
2020-02-11 16:48:06 +01:00
|
|
|
playerConnection.sendPacket(getMetadataPacket());
|
2020-05-24 19:22:58 +02:00
|
|
|
|
2020-05-25 01:12:12 +02:00
|
|
|
// Equipments synchronization
|
2020-05-30 21:56:12 +02:00
|
|
|
syncEquipments(playerConnection);
|
2020-05-25 01:12:12 +02:00
|
|
|
|
2020-05-24 19:22:58 +02:00
|
|
|
if (hasPassenger()) {
|
|
|
|
playerConnection.sendPacket(getPassengersPacket());
|
|
|
|
}
|
|
|
|
|
2020-05-23 17:57:56 +02:00
|
|
|
return result;
|
2019-08-10 08:44:35 +02:00
|
|
|
}
|
2020-04-09 14:25:42 +02:00
|
|
|
|
2020-08-06 11:56:43 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the goal selectors of this entity.
|
2020-08-06 11:56:43 +02:00
|
|
|
*
|
|
|
|
* @return a modifiable list containing the entity goal selectors
|
|
|
|
*/
|
2020-10-24 11:19:54 +02:00
|
|
|
@NotNull
|
2020-08-06 11:56:43 +02:00
|
|
|
public List<GoalSelector> getGoalSelectors() {
|
|
|
|
return goalSelectors;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the target selectors of this entity.
|
2020-08-06 11:56:43 +02:00
|
|
|
*
|
|
|
|
* @return a modifiable list containing the entity target selectors
|
|
|
|
*/
|
2020-10-24 11:19:54 +02:00
|
|
|
@NotNull
|
2020-08-06 11:56:43 +02:00
|
|
|
public List<TargetSelector> getTargetSelectors() {
|
|
|
|
return targetSelectors;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Gets the entity target.
|
2020-08-06 11:56:43 +02:00
|
|
|
*
|
|
|
|
* @return the entity target
|
|
|
|
*/
|
2020-10-24 11:19:54 +02:00
|
|
|
@Nullable
|
2020-08-06 11:56:43 +02:00
|
|
|
public Entity getTarget() {
|
|
|
|
return target;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-29 22:52:07 +01:00
|
|
|
* Changes the entity target.
|
2020-08-06 11:56:43 +02:00
|
|
|
*
|
|
|
|
* @param target the new entity target
|
|
|
|
*/
|
2020-10-24 11:19:54 +02:00
|
|
|
public void setTarget(@NotNull Entity target) {
|
2020-08-06 11:56:43 +02:00
|
|
|
this.target = target;
|
|
|
|
}
|
|
|
|
|
2020-10-24 11:19:54 +02:00
|
|
|
@NotNull
|
2020-05-06 22:35:32 +02:00
|
|
|
@Override
|
|
|
|
public ItemStack getItemInMainHand() {
|
|
|
|
return mainHandItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public void setItemInMainHand(@NotNull ItemStack itemStack) {
|
|
|
|
this.mainHandItem = itemStack;
|
2020-05-06 22:35:32 +02:00
|
|
|
syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
|
|
|
|
}
|
|
|
|
|
2020-10-24 11:19:54 +02:00
|
|
|
@NotNull
|
2020-05-06 22:35:32 +02:00
|
|
|
@Override
|
|
|
|
public ItemStack getItemInOffHand() {
|
|
|
|
return offHandItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public void setItemInOffHand(@NotNull ItemStack itemStack) {
|
|
|
|
this.offHandItem = itemStack;
|
2020-05-06 22:35:32 +02:00
|
|
|
syncEquipment(EntityEquipmentPacket.Slot.OFF_HAND);
|
|
|
|
}
|
|
|
|
|
2020-10-24 11:19:54 +02:00
|
|
|
@NotNull
|
2020-05-06 22:35:32 +02:00
|
|
|
@Override
|
|
|
|
public ItemStack getHelmet() {
|
|
|
|
return helmet;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public void setHelmet(@NotNull ItemStack itemStack) {
|
2020-05-06 22:42:04 +02:00
|
|
|
this.helmet = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.HELMET);
|
2020-05-06 22:35:32 +02:00
|
|
|
syncEquipment(EntityEquipmentPacket.Slot.HELMET);
|
|
|
|
}
|
|
|
|
|
2020-10-24 11:19:54 +02:00
|
|
|
@NotNull
|
2020-05-06 22:35:32 +02:00
|
|
|
@Override
|
|
|
|
public ItemStack getChestplate() {
|
|
|
|
return chestplate;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public void setChestplate(@NotNull ItemStack itemStack) {
|
2020-05-06 22:42:04 +02:00
|
|
|
this.chestplate = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.CHESTPLATE);
|
2020-05-06 22:35:32 +02:00
|
|
|
syncEquipment(EntityEquipmentPacket.Slot.CHESTPLATE);
|
|
|
|
}
|
|
|
|
|
2020-10-24 11:19:54 +02:00
|
|
|
@NotNull
|
2020-05-06 22:35:32 +02:00
|
|
|
@Override
|
|
|
|
public ItemStack getLeggings() {
|
|
|
|
return leggings;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public void setLeggings(@NotNull ItemStack itemStack) {
|
2020-05-06 22:42:04 +02:00
|
|
|
this.leggings = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.LEGGINGS);
|
2020-05-06 22:35:32 +02:00
|
|
|
syncEquipment(EntityEquipmentPacket.Slot.LEGGINGS);
|
|
|
|
}
|
|
|
|
|
2020-10-24 11:19:54 +02:00
|
|
|
@NotNull
|
2020-05-06 22:35:32 +02:00
|
|
|
@Override
|
|
|
|
public ItemStack getBoots() {
|
|
|
|
return boots;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public void setBoots(@NotNull ItemStack itemStack) {
|
2020-05-06 22:42:04 +02:00
|
|
|
this.boots = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.BOOTS);
|
2020-05-06 22:35:32 +02:00
|
|
|
syncEquipment(EntityEquipmentPacket.Slot.BOOTS);
|
|
|
|
}
|
|
|
|
|
2020-05-28 19:15:55 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Calls a {@link EntityAttackEvent} with this entity as the source and {@code target} as the target.
|
2020-05-28 19:15:55 +02:00
|
|
|
*
|
2020-06-03 15:17:34 +02:00
|
|
|
* @param target the entity target
|
|
|
|
* @param swingHand true to swing the entity main hand, false otherwise
|
2020-05-28 19:15:55 +02:00
|
|
|
*/
|
2020-10-24 11:19:54 +02:00
|
|
|
public void attack(@NotNull Entity target, boolean swingHand) {
|
2020-06-03 15:17:34 +02:00
|
|
|
if (swingHand)
|
|
|
|
swingMainHand();
|
2020-05-28 19:15:55 +02:00
|
|
|
EntityAttackEvent attackEvent = new EntityAttackEvent(this, target);
|
|
|
|
callEvent(EntityAttackEvent.class, attackEvent);
|
|
|
|
}
|
|
|
|
|
2020-06-03 15:17:34 +02:00
|
|
|
/**
|
2020-10-15 21:16:31 +02:00
|
|
|
* Calls a {@link EntityAttackEvent} with this entity as the source and {@code target} as the target.
|
2020-06-03 15:17:34 +02:00
|
|
|
* <p>
|
2020-10-15 21:16:31 +02:00
|
|
|
* This does not trigger the hand animation.
|
2020-06-03 15:17:34 +02:00
|
|
|
*
|
|
|
|
* @param target the entity target
|
|
|
|
*/
|
2020-10-24 11:19:54 +02:00
|
|
|
public void attack(@NotNull Entity target) {
|
2020-06-03 15:17:34 +02:00
|
|
|
attack(target, false);
|
|
|
|
}
|
|
|
|
|
2020-11-29 23:14:21 +01:00
|
|
|
@Override
|
|
|
|
public void pathFindingTick(float speed) {
|
2020-12-04 18:25:24 +01:00
|
|
|
synchronized (pathLock) {
|
2020-12-01 18:21:19 +01:00
|
|
|
NavigableEntity.super.pathFindingTick(speed);
|
|
|
|
}
|
2020-04-09 14:25:42 +02:00
|
|
|
}
|
|
|
|
|
2020-11-29 23:14:21 +01:00
|
|
|
@Override
|
2020-10-24 11:19:54 +02:00
|
|
|
public boolean setPathTo(@Nullable Position position) {
|
2020-12-04 18:25:24 +01:00
|
|
|
synchronized (pathLock) {
|
2020-12-01 18:21:19 +01:00
|
|
|
return NavigableEntity.super.setPathTo(position);
|
|
|
|
}
|
2020-08-06 13:46:30 +02:00
|
|
|
}
|
|
|
|
|
2020-10-24 11:19:54 +02:00
|
|
|
@Nullable
|
2020-11-29 23:14:21 +01:00
|
|
|
@Override
|
2020-08-06 13:46:30 +02:00
|
|
|
public Position getPathPosition() {
|
|
|
|
return pathPosition;
|
2020-04-25 23:51:45 +02:00
|
|
|
}
|
|
|
|
|
2020-11-29 23:14:21 +01:00
|
|
|
@Override
|
|
|
|
public void setPathPosition(Position pathPosition) {
|
|
|
|
this.pathPosition = pathPosition;
|
2020-11-27 10:47:35 +01:00
|
|
|
}
|
|
|
|
|
2020-11-29 23:14:21 +01:00
|
|
|
@Nullable
|
|
|
|
@Override
|
|
|
|
public IPath getPath() {
|
|
|
|
return path;
|
|
|
|
}
|
2020-11-26 12:46:49 +01:00
|
|
|
|
2020-11-29 23:14:21 +01:00
|
|
|
@Override
|
|
|
|
public void setPath(IPath path) {
|
|
|
|
this.path = path;
|
2020-04-09 14:25:42 +02:00
|
|
|
}
|
|
|
|
|
2020-10-24 11:19:54 +02:00
|
|
|
@NotNull
|
2020-11-29 23:14:21 +01:00
|
|
|
@Override
|
2020-07-31 21:58:49 +02:00
|
|
|
public PFPathingEntity getPathingEntity() {
|
|
|
|
return pathingEntity;
|
|
|
|
}
|
|
|
|
|
2020-11-29 23:14:21 +01:00
|
|
|
@Nullable
|
|
|
|
@Override
|
|
|
|
public HydrazinePathFinder getPathFinder() {
|
|
|
|
return pathFinder;
|
|
|
|
}
|
2020-07-31 21:02:37 +02:00
|
|
|
|
2020-11-29 23:14:21 +01:00
|
|
|
@NotNull
|
|
|
|
@Override
|
|
|
|
public Entity getNavigableEntity() {
|
|
|
|
return this;
|
2020-07-31 21:02:37 +02:00
|
|
|
}
|
|
|
|
|
2020-10-24 11:19:54 +02:00
|
|
|
private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull ArmorEquipEvent.ArmorSlot armorSlot) {
|
2020-05-14 18:59:01 +02:00
|
|
|
ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(this, itemStack, armorSlot);
|
2020-05-06 22:42:04 +02:00
|
|
|
callEvent(ArmorEquipEvent.class, armorEquipEvent);
|
|
|
|
return armorEquipEvent.getArmorItem();
|
|
|
|
}
|
2019-08-10 08:44:35 +02:00
|
|
|
}
|