diff --git a/api/src/main/java/lol/pyr/znpcsplus/util/LookType.java b/api/src/main/java/lol/pyr/znpcsplus/util/LookType.java new file mode 100644 index 0000000..e6af244 --- /dev/null +++ b/api/src/main/java/lol/pyr/znpcsplus/util/LookType.java @@ -0,0 +1,7 @@ +package lol.pyr.znpcsplus.util; + +public enum LookType { + FIXED, + CLOSEST_PLAYER, + PER_PLAYER +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java index fbe9169..4fae9cf 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java @@ -127,7 +127,7 @@ public class ZNpcsPlus extends JavaPlugin { ConfigManager configManager = new ConfigManager(getDataFolder()); MojangSkinCache skinCache = new MojangSkinCache(configManager); - EntityPropertyRegistryImpl propertyRegistry = new EntityPropertyRegistryImpl(skinCache); + EntityPropertyRegistryImpl propertyRegistry = new EntityPropertyRegistryImpl(skinCache, configManager); PacketFactory packetFactory = setupPacketFactory(scheduler, propertyRegistry); propertyRegistry.registerTypes(packetFactory); @@ -276,6 +276,7 @@ public class ZNpcsPlus extends JavaPlugin { registerEnumParser(manager, OcelotType.class, incorrectUsageMessage); registerEnumParser(manager, PandaGene.class, incorrectUsageMessage); registerEnumParser(manager, PuffState.class, incorrectUsageMessage); + registerEnumParser(manager, LookType.class, incorrectUsageMessage); manager.registerCommand("npc", new MultiCommand(loadHelpMessage("root")) .addSubcommand("center", new CenterCommand(npcRegistry)) diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/conversion/citizens/model/traits/LookTrait.java b/plugin/src/main/java/lol/pyr/znpcsplus/conversion/citizens/model/traits/LookTrait.java index 0854124..650b6a6 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/conversion/citizens/model/traits/LookTrait.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/conversion/citizens/model/traits/LookTrait.java @@ -3,6 +3,7 @@ package lol.pyr.znpcsplus.conversion.citizens.model.traits; import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry; import lol.pyr.znpcsplus.conversion.citizens.model.SectionCitizensTrait; import lol.pyr.znpcsplus.npc.NpcImpl; +import lol.pyr.znpcsplus.util.LookType; import org.bukkit.configuration.ConfigurationSection; import org.jetbrains.annotations.NotNull; @@ -16,7 +17,7 @@ public class LookTrait extends SectionCitizensTrait { @Override public @NotNull NpcImpl apply(NpcImpl npc, ConfigurationSection section) { - if (section.getBoolean("enabled")) npc.setProperty(registry.getByName("look", Boolean.class), true); + if (section.getBoolean("enabled")) npc.setProperty(registry.getByName("look", LookType.class), LookType.CLOSEST_PLAYER); return npc; } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/ZNpcImporter.java b/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/ZNpcImporter.java index 39ce7e7..09233b6 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/ZNpcImporter.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/ZNpcImporter.java @@ -28,13 +28,16 @@ import lol.pyr.znpcsplus.scheduling.TaskScheduler; import lol.pyr.znpcsplus.skin.Skin; import lol.pyr.znpcsplus.skin.cache.MojangSkinCache; import lol.pyr.znpcsplus.skin.descriptor.FetchingDescriptor; +import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor; import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor; import lol.pyr.znpcsplus.util.BungeeConnector; import lol.pyr.znpcsplus.util.ItemSerializationUtil; +import lol.pyr.znpcsplus.util.LookType; import lol.pyr.znpcsplus.util.NpcLocation; import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.DyeColor; import org.bukkit.inventory.ItemStack; import java.io.BufferedReader; @@ -129,6 +132,23 @@ public class ZNpcImporter implements DataImporter { npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new PrefetchedDescriptor(new Skin(model.getSkin(), model.getSignature()))); } + Map toggleValues = model.getNpcToggleValues(); + if (toggleValues != null) { + if (toggleValues.containsKey("look")) { + npc.setProperty(propertyRegistry.getByName("look", LookType.class), LookType.CLOSEST_PLAYER); + } + if (toggleValues.containsKey("mirror")) { + npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new MirrorDescriptor(skinCache)); + } + if (toggleValues.containsKey("glow")) { + try { + npc.setProperty(propertyRegistry.getByName("glow", DyeColor.class), DyeColor.valueOf((String) toggleValues.get("glow"))); + } catch (IllegalArgumentException e) { + npc.setProperty(propertyRegistry.getByName("glow", DyeColor.class), DyeColor.WHITE); + } + } + } + NpcEntryImpl entry = new NpcEntryImpl(String.valueOf(model.getId()), npc); entry.enableEverything(); entries.add(entry); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/model/ZNpcsModel.java b/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/model/ZNpcsModel.java index 4502b23..60ec99b 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/model/ZNpcsModel.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/conversion/znpcs/model/ZNpcsModel.java @@ -19,6 +19,7 @@ public class ZNpcsModel { private List hologramLines; private List clickActions; private Map npcEquip; + private Map npcToggleValues; private Map customizationMap; public int getId() { @@ -57,6 +58,10 @@ public class ZNpcsModel { return npcEquip; } + public Map getNpcToggleValues() { + return npcToggleValues; + } + public Map getCustomizationMap() { return customizationMap; } 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 c5b08dc..eaaea72 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java @@ -10,6 +10,7 @@ import com.github.retrooper.packetevents.protocol.player.EquipmentSlot; import lol.pyr.znpcsplus.api.entity.EntityProperty; import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry; import lol.pyr.znpcsplus.api.skin.SkinDescriptor; +import lol.pyr.znpcsplus.config.ConfigManager; import lol.pyr.znpcsplus.entity.properties.*; import lol.pyr.znpcsplus.entity.properties.villager.VillagerLevelProperty; import lol.pyr.znpcsplus.entity.properties.villager.VillagerProfessionProperty; @@ -42,9 +43,9 @@ import java.util.stream.Collectors; public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { private final Map, PropertySerializer> serializerMap = new HashMap<>(); private final Map> byName = new HashMap<>(); + private final ConfigManager configManager; - public EntityPropertyRegistryImpl(MojangSkinCache skinCache) { - registerSerializer(new BooleanPropertySerializer()); + public EntityPropertyRegistryImpl(MojangSkinCache skinCache, ConfigManager configManager) { registerSerializer(new ComponentPropertySerializer()); registerSerializer(new NamedTextColorPropertySerializer()); registerSerializer(new SkinDescriptorSerializer(skinCache)); @@ -52,7 +53,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { registerSerializer(new ColorPropertySerializer()); registerSerializer(new Vector3fPropertySerializer()); registerSerializer(new BlockStatePropertySerializer()); - registerSerializer(new IntegerPropertySerializer()); + registerSerializer(new LookTypeSerializer()); registerEnumSerializer(NpcPose.class); registerEnumSerializer(DyeColor.class); @@ -76,6 +77,10 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { registerEnumSerializer(PandaGene.class); registerEnumSerializer(PuffState.class); + registerPrimitiveSerializers(Integer.class, Boolean.class, Double.class, Float.class, Long.class, Short.class, Byte.class, String.class); + + this.configManager = configManager; + /* registerType("using_item", false); // TODO: fix it for 1.8 and add new property to use offhand item and riptide animation @@ -142,7 +147,10 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { register(new NameProperty(legacyNames, optionalComponents)); register(new DinnerboneProperty(legacyNames, optionalComponents)); - register(new DummyProperty<>("look", false)); + register(new DummyProperty<>("look", LookType.FIXED)); + register(new DummyProperty<>("look_distance", configManager.getConfig().lookPropertyDistance())); + register(new DummyProperty<>("view_distance", configManager.getConfig().viewDistance())); + register(new GlowProperty(packetFactory)); register(new BitsetProperty("fire", 0, 0x01)); register(new BitsetProperty("invisible", 0, 0x20)); @@ -372,13 +380,14 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { // Player NBTProperty.NBTDecoder parrotVariantDecoder = (variant) -> { NBTCompound compound = new NBTCompound(); + if (variant == null) return compound; compound.setTag("id", new NBTString("minecraft:parrot")); compound.setTag("Variant", new NBTInt(variant.ordinal())); return compound; }; int shoulderIndex = skinLayersIndex+2; - register(new NBTProperty<>("shoulder_entity_left", ParrotVariant.class, shoulderIndex++, parrotVariantDecoder)); - register(new NBTProperty<>("shoulder_entity_right", ParrotVariant.class, shoulderIndex, parrotVariantDecoder)); + register(new NBTProperty<>("shoulder_entity_left", ParrotVariant.class, shoulderIndex++, parrotVariantDecoder, true)); + register(new NBTProperty<>("shoulder_entity_right", ParrotVariant.class, shoulderIndex, parrotVariantDecoder, true)); if (!ver.isNewerThanOrEquals(ServerVersion.V_1_13)) return; // Pufferfish @@ -515,6 +524,16 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { serializerMap.put(clazz, new EnumPropertySerializer<>(clazz)); } + private void registerPrimitiveSerializers(Class... classes) { + for (Class clazz : classes) { + registerPrimitiveSerializer(clazz); + } + } + + private void registerPrimitiveSerializer(Class clazz) { + serializerMap.put(clazz, new PrimitivePropertySerializer<>(clazz)); + } + private void register(EntityPropertyImpl property) { if (byName.containsKey(property.getName())) throw new IllegalArgumentException("Duplicate property name: " + property.getName()); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/PacketEntity.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/PacketEntity.java index aef8b03..a827221 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/entity/PacketEntity.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/PacketEntity.java @@ -60,6 +60,10 @@ public class PacketEntity implements PropertyHolder { else packetFactory.spawnEntity(player, this, properties); } + public void setHeadRotation(Player player, float yaw, float pitch) { + packetFactory.sendHeadRotation(player, this, yaw, pitch); + } + public void despawn(Player player) { packetFactory.destroyEntity(player, this, properties); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/PrimitivePropertySerializer.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/PrimitivePropertySerializer.java new file mode 100644 index 0000000..ad18eaf --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/PrimitivePropertySerializer.java @@ -0,0 +1,30 @@ +package lol.pyr.znpcsplus.entity; + +import java.lang.reflect.InvocationTargetException; + +public class PrimitivePropertySerializer implements PropertySerializer { + private final Class clazz; + + public PrimitivePropertySerializer(Class clazz) { + this.clazz = clazz; + } + + @Override + public String serialize(T property) { + return String.valueOf(property); + } + + @Override + public T deserialize(String property) { + try { + return clazz.getConstructor(String.class).newInstance(property); + } catch (InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + throw new NullPointerException("Failed to deserialize property " + property + " of type " + clazz.getName() + "!"); + } + } + + @Override + public Class getTypeClass() { + return clazz; + } +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/NBTProperty.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/NBTProperty.java index 9829790..9faa28d 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/NBTProperty.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/NBTProperty.java @@ -14,32 +14,43 @@ public class NBTProperty extends EntityPropertyImpl { private final EntityDataType type; private final NBTDecoder decoder; private final int index; + private final boolean allowNull; // This means that the decoder can have null input, not that the property can be null - public NBTProperty(String name, T defaultValue, Class clazz, int index, NBTDecoder decoder, EntityDataType type) { + public NBTProperty(String name, T defaultValue, Class clazz, int index, NBTDecoder decoder, boolean allowNull, EntityDataType type) { super(name, defaultValue, clazz); this.decoder = decoder; this.index = index; + this.allowNull = allowNull; this.type = type; } @SuppressWarnings("unchecked") - public NBTProperty(String name, T defaultValue, int index, NBTDecoder decoder) { - this(name, defaultValue, (Class) defaultValue.getClass(), index, decoder, EntityDataTypes.NBT); + public NBTProperty(String name, T defaultValue, int index, NBTDecoder decoder, boolean allowNull) { + this(name, defaultValue, (Class) defaultValue.getClass(), index, decoder, allowNull, EntityDataTypes.NBT); } @SuppressWarnings("unchecked") - public NBTProperty(String name, T defaultValue, int index, NBTDecoder decoder, EntityDataType type) { - this(name, defaultValue, (Class) defaultValue.getClass(), index, decoder, type); + public NBTProperty(String name, T defaultValue, int index, NBTDecoder decoder) { + this(name, defaultValue, (Class) defaultValue.getClass(), index, decoder, false, EntityDataTypes.NBT); + } + + @SuppressWarnings("unchecked") + public NBTProperty(String name, T defaultValue, int index, NBTDecoder decoder, boolean allowNull, EntityDataType type) { + this(name, defaultValue, (Class) defaultValue.getClass(), index, decoder, allowNull, type); + } + + public NBTProperty(String name, Class clazz, int index, NBTDecoder decoder, boolean allowNull) { + this(name, null, clazz, index, decoder, allowNull, EntityDataTypes.NBT); } public NBTProperty(String name, Class clazz, int index, NBTDecoder decoder) { - this(name, null, clazz, index, decoder, EntityDataTypes.NBT); + this(name, null, clazz, index, decoder, false, EntityDataTypes.NBT); } @Override public void apply(Player player, PacketEntity entity, boolean isSpawned, Map properties) { T value = entity.getProperty(this); - if (value == null) return; + if (value == null && !allowNull) return; properties.put(index, newEntityData(index, type, decoder.decode(value))); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/serializers/IntegerPropertySerializer.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/serializers/IntegerPropertySerializer.java deleted file mode 100644 index e918668..0000000 --- a/plugin/src/main/java/lol/pyr/znpcsplus/entity/serializers/IntegerPropertySerializer.java +++ /dev/null @@ -1,25 +0,0 @@ -package lol.pyr.znpcsplus.entity.serializers; - -import lol.pyr.znpcsplus.entity.PropertySerializer; - -public class IntegerPropertySerializer implements PropertySerializer { - @Override - public String serialize(Integer property) { - return String.valueOf(property); - } - - @Override - public Integer deserialize(String property) { - try { - return Integer.parseInt(property); - } catch (NumberFormatException e) { - e.printStackTrace(); - } - return null; - } - - @Override - public Class getTypeClass() { - return Integer.class; - } -} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/serializers/LookTypeSerializer.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/serializers/LookTypeSerializer.java new file mode 100644 index 0000000..0d06d9a --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/serializers/LookTypeSerializer.java @@ -0,0 +1,26 @@ +package lol.pyr.znpcsplus.entity.serializers; + +import lol.pyr.znpcsplus.entity.PropertySerializer; +import lol.pyr.znpcsplus.util.LookType; + +public class LookTypeSerializer implements PropertySerializer { + @Override + public String serialize(LookType property) { + return property.name(); + } + + @Override + public LookType deserialize(String property) { + if (property.equals("true")) return LookType.CLOSEST_PLAYER; + try { + return LookType.valueOf(property); + } catch (IllegalArgumentException ignored) { + return LookType.FIXED; + } + } + + @Override + public Class getTypeClass() { + return LookType.class; + } +} 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 c41f151..fb17ad4 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcImpl.java @@ -83,6 +83,16 @@ public class NpcImpl extends Viewable implements Npc { hologram.setLocation(location.withY(location.getY() + type.getHologramOffset())); } + public void setHeadRotation(Player player, float yaw, float pitch) { + entity.setHeadRotation(player, yaw, pitch); + } + + public void setHeadRotation(float yaw, float pitch) { + for (Player player : getViewers()) { + entity.setHeadRotation(player, yaw, pitch); + } + } + public HologramImpl getHologram() { return hologram; } 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 3bc2af7..56cbd06 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java @@ -110,8 +110,9 @@ public class NpcTypeImpl implements NpcType { public NpcTypeImpl build() { ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion(); - addProperties("fire", "invisible", "silent", "look", - "potion_color", "potion_ambient", "dinnerbone"); + addProperties("fire", "invisible", "silent", "look", "look_distance", "view_distance", + "potion_color", "potion_ambient"); + if (!type.equals(EntityTypes.PLAYER)) addProperties("dinnerbone"); // TODO: make this look nicer after completing the rest of the properties if (version.isNewerThanOrEquals(ServerVersion.V_1_9)) addProperties("glow"); if (version.isNewerThanOrEquals(ServerVersion.V_1_14)) { diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/packets/PacketFactory.java b/plugin/src/main/java/lol/pyr/znpcsplus/packets/PacketFactory.java index 87df253..462a99d 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/packets/PacketFactory.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/packets/PacketFactory.java @@ -22,4 +22,5 @@ public interface PacketFactory { void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties); void sendEquipment(Player player, PacketEntity entity, Equipment equipment); void sendMetadata(Player player, PacketEntity entity, List data); + void sendHeadRotation(Player player, PacketEntity entity, float yaw, float pitch); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_8PacketFactory.java b/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_8PacketFactory.java index b0ec7de..26f13f0 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_8PacketFactory.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/packets/V1_8PacketFactory.java @@ -137,6 +137,12 @@ public class V1_8PacketFactory implements PacketFactory { sendPacket(player, new WrapperPlayServerEntityMetadata(entity.getEntityId(), data)); } + @Override + public void sendHeadRotation(Player player, PacketEntity entity, float yaw, float pitch) { + sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(),yaw)); + sendPacket(player, new WrapperPlayServerEntityRotation(entity.getEntityId(), yaw, pitch, true)); + } + @Override public void sendEquipment(Player player, PacketEntity entity, Equipment equipment) { sendPacket(player, new WrapperPlayServerEntityEquipment(entity.getEntityId(), Collections.singletonList(equipment))); 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 1e91139..2f45c55 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/tasks/NpcProcessorTask.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/tasks/NpcProcessorTask.java @@ -8,6 +8,7 @@ import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl; import lol.pyr.znpcsplus.npc.NpcEntryImpl; import lol.pyr.znpcsplus.npc.NpcImpl; import lol.pyr.znpcsplus.npc.NpcRegistryImpl; +import lol.pyr.znpcsplus.util.LookType; import lol.pyr.znpcsplus.util.NpcLocation; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -26,15 +27,18 @@ public class NpcProcessorTask extends BukkitRunnable { } public void run() { - double distSq = NumberConversions.square(configManager.getConfig().viewDistance()); - double lookPropertyDistSq = NumberConversions.square(configManager.getConfig().lookPropertyDistance()); - EntityPropertyImpl lookProperty = propertyRegistry.getByName("look", Boolean.class); + 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); + double lookDistance; for (NpcEntryImpl entry : npcRegistry.getProcessable()) { NpcImpl npc = entry.getNpc(); if (!npc.isEnabled()) continue; double closestDist = Double.MAX_VALUE; Player closest = null; + LookType lookType = npc.getProperty(lookProperty); + lookDistance = NumberConversions.square(npc.getProperty(lookDistanceProperty)); for (Player player : Bukkit.getOnlinePlayers()) { if (!player.getWorld().equals(npc.getWorld())) { if (npc.isVisibleTo(player)) npc.hide(player); @@ -43,7 +47,7 @@ public class NpcProcessorTask extends BukkitRunnable { double distance = player.getLocation().distanceSquared(npc.getBukkitLocation()); // visibility - boolean inRange = distance <= distSq; + boolean inRange = distance <= NumberConversions.square(npc.getProperty(viewDistanceProperty)); if (!inRange && npc.isVisibleTo(player)) { NpcDespawnEvent event = new NpcDespawnEvent(player, entry); Bukkit.getPluginManager().callEvent(event); @@ -60,12 +64,18 @@ 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()); + } } } // look property - if (closest != null && npc.getProperty(lookProperty) && lookPropertyDistSq >= closestDist) { - NpcLocation expected = npc.getLocation().lookingAt(closest.getLocation().add(0, -npc.getType().getHologramOffset(), 0)); - if (!expected.equals(npc.getLocation())) npc.setLocation(expected); + if (lookType.equals(LookType.CLOSEST_PLAYER)) { + 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()); + } } } }