diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java index 024a28a..8105e76 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java @@ -126,6 +126,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { register(new DummyProperty<>("look", LookType.FIXED)); register(new DummyProperty<>("look_distance", configManager.getConfig().lookPropertyDistance())); + register(new DummyProperty<>("look_return", false)); register(new DummyProperty<>("view_distance", configManager.getConfig().viewDistance())); register(new DummyProperty<>("permission_required", false)); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java index d340f32..2be57a3 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java @@ -25,6 +25,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; public class NpcImpl extends Viewable implements Npc { @@ -40,6 +41,8 @@ public class NpcImpl extends Viewable implements Npc { private final Map, Object> propertyMap = new HashMap<>(); private final List actions = new ArrayList<>(); + private final Map playerLookMap = new ConcurrentHashMap<>(); + protected NpcImpl(UUID uuid, EntityPropertyRegistryImpl propertyRegistry, ConfigManager configManager, LegacyComponentSerializer textSerializer, World world, NpcTypeImpl type, NpcLocation location, PacketFactory packetFactory) { this(uuid, propertyRegistry, configManager, packetFactory, textSerializer, world.getName(), type, location); } @@ -87,20 +90,34 @@ public class NpcImpl extends Viewable implements Npc { public void setLocation(NpcLocation location) { this.location = location; + playerLookMap.clear(); + playerLookMap.putAll(getViewers().stream().collect(Collectors.toMap(Player::getUniqueId, player -> new float[]{location.getYaw(), location.getPitch()}))); entity.setLocation(location); hologram.setLocation(location.withY(location.getY() + type.getHologramOffset())); } public void setHeadRotation(Player player, float yaw, float pitch) { + if (getHeadYaw(player) == yaw && getHeadPitch(player) == pitch) return; + playerLookMap.put(player.getUniqueId(), new float[]{yaw, pitch}); entity.setHeadRotation(player, yaw, pitch); } public void setHeadRotation(float yaw, float pitch) { for (Player player : getViewers()) { + if (getHeadYaw(player) == yaw && getHeadPitch(player) == pitch) continue; + playerLookMap.put(player.getUniqueId(), new float[]{yaw, pitch}); entity.setHeadRotation(player, yaw, pitch); } } + public float getHeadYaw(Player player) { + return playerLookMap.getOrDefault(player.getUniqueId(), new float[]{location.getYaw(), location.getPitch()})[0]; + } + + public float getHeadPitch(Player player) { + return playerLookMap.getOrDefault(player.getUniqueId(), new float[]{location.getYaw(), location.getPitch()})[1]; + } + public HologramImpl getHologram() { return hologram; } @@ -128,11 +145,13 @@ public class NpcImpl extends Viewable implements Npc { @Override protected CompletableFuture UNSAFE_show(Player player) { + playerLookMap.put(player.getUniqueId(), new float[]{location.getYaw(), location.getPitch()}); return CompletableFuture.allOf(entity.spawn(player), hologram.show(player)); } @Override protected void UNSAFE_hide(Player player) { + playerLookMap.remove(player.getUniqueId()); entity.despawn(player); hologram.hide(player); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java index f62adb8..207e75c 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java @@ -114,7 +114,7 @@ public class NpcTypeImpl implements NpcType { public NpcTypeImpl build() { ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion(); - addProperties("fire", "invisible", "silent", "look", "look_distance", "view_distance", + addProperties("fire", "invisible", "silent", "look", "look_distance", "look_return", "view_distance", "potion_color", "potion_ambient", "display_name", "permission_required", "player_knockback", "player_knockback_exempt_permission", "player_knockback_distance", "player_knockback_vertical", "player_knockback_horizontal", "player_knockback_cooldown", "player_knockback_sound", "player_knockback_sound_name", diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/tasks/NpcProcessorTask.java b/plugin/src/main/java/lol/pyr/znpcsplus/tasks/NpcProcessorTask.java index e0cdcf1..a975546 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/tasks/NpcProcessorTask.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/tasks/NpcProcessorTask.java @@ -33,6 +33,7 @@ public class NpcProcessorTask extends BukkitRunnable { EntityPropertyImpl viewDistanceProperty = propertyRegistry.getByName("view_distance", Integer.class); // Not sure why this is an Integer, but it is EntityPropertyImpl lookProperty = propertyRegistry.getByName("look", LookType.class); EntityPropertyImpl lookDistanceProperty = propertyRegistry.getByName("look_distance", Double.class); + EntityPropertyImpl lookReturnProperty = propertyRegistry.getByName("look_return", Boolean.class); EntityPropertyImpl permissionRequiredProperty = propertyRegistry.getByName("permission_required", Boolean.class); EntityPropertyImpl playerKnockbackProperty = propertyRegistry.getByName("player_knockback", Boolean.class); EntityPropertyImpl playerKnockbackExemptPermissionProperty = propertyRegistry.getByName("player_knockback_exempt_permission", String.class); @@ -45,6 +46,7 @@ public class NpcProcessorTask extends BukkitRunnable { EntityPropertyImpl playerKnockbackSoundVolumeProperty = propertyRegistry.getByName("player_knockback_sound_volume", Float.class); EntityPropertyImpl playerKnockbackSoundPitchProperty = propertyRegistry.getByName("player_knockback_sound_pitch", Float.class); double lookDistance; + boolean lookReturn; boolean permissionRequired; boolean playerKnockback; String playerKnockbackExemptPermission = null; @@ -64,6 +66,7 @@ public class NpcProcessorTask extends BukkitRunnable { Player closest = null; LookType lookType = npc.getProperty(lookProperty); lookDistance = NumberConversions.square(npc.getProperty(lookDistanceProperty)); + lookReturn = npc.getProperty(lookReturnProperty); permissionRequired = npc.getProperty(permissionRequiredProperty); playerKnockback = npc.getProperty(playerKnockbackProperty); if (playerKnockback) { @@ -106,9 +109,13 @@ public class NpcProcessorTask extends BukkitRunnable { closestDist = distance; closest = player; } - if (lookType.equals(LookType.PER_PLAYER) && lookDistance >= distance) { - NpcLocation expected = npc.getLocation().lookingAt(player.getLocation().add(0, -npc.getType().getHologramOffset(), 0)); - if (!expected.equals(npc.getLocation())) npc.setHeadRotation(player, expected.getYaw(), expected.getPitch()); + if (lookType.equals(LookType.PER_PLAYER)) { + if (lookDistance >= distance) { + NpcLocation expected = npc.getLocation().lookingAt(player.getLocation().add(0, -npc.getType().getHologramOffset(), 0)); + npc.setHeadRotation(player, expected.getYaw(), expected.getPitch()); + } else if (lookReturn) { + npc.setHeadRotation(player, npc.getLocation().getYaw(), npc.getLocation().getPitch()); + } } // player knockback @@ -132,7 +139,11 @@ public class NpcProcessorTask extends BukkitRunnable { if (closest != null && lookDistance >= closestDist) { NpcLocation expected = npc.getLocation().lookingAt(closest.getLocation().add(0, -npc.getType().getHologramOffset(), 0)); if (!expected.equals(npc.getLocation())) npc.setHeadRotation(expected.getYaw(), expected.getPitch()); + } else if (lookReturn) { + npc.setHeadRotation(npc.getLocation().getYaw(), npc.getLocation().getPitch()); } + } else if (lookType.equals(LookType.FIXED)) { + npc.setHeadRotation(npc.getLocation().getYaw(), npc.getLocation().getPitch()); } } }