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..47928e6 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java @@ -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/entity/EntityPropertyRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java index 61e04c3..a7c7b61 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java @@ -53,6 +53,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { registerSerializer(new Vector3fPropertySerializer()); registerSerializer(new BlockStatePropertySerializer()); registerSerializer(new IntegerPropertySerializer()); + registerSerializer(new LookTypeSerializer()); registerEnumSerializer(NpcPose.class); registerEnumSerializer(DyeColor.class); @@ -142,7 +143,7 @@ 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 GlowProperty(packetFactory)); register(new BitsetProperty("fire", 0, 0x01)); register(new BitsetProperty("invisible", 0, 0x20)); 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/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..cd6c52b 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,10 @@ 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 HologramImpl getHologram() { return hologram; } 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..8145730 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; @@ -28,13 +29,14 @@ 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 lookProperty = propertyRegistry.getByName("look", LookType.class); 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); for (Player player : Bukkit.getOnlinePlayers()) { if (!player.getWorld().equals(npc.getWorld())) { if (npc.isVisibleTo(player)) npc.hide(player); @@ -60,12 +62,18 @@ public class NpcProcessorTask extends BukkitRunnable { closestDist = distance; closest = player; } + if (lookType.equals(LookType.PER_PLAYER) && lookPropertyDistSq >= 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 && lookPropertyDistSq >= closestDist) { + NpcLocation expected = npc.getLocation().lookingAt(closest.getLocation().add(0, -npc.getType().getHologramOffset(), 0)); + if (!expected.equals(npc.getLocation())) npc.setLocation(expected); + } } } }