Merge remote-tracking branch 'origin/2.X' into 2.X

This commit is contained in:
Pyrbu 2023-10-02 03:14:52 +02:00
commit 2128243fa2
16 changed files with 176 additions and 49 deletions

@ -0,0 +1,7 @@
package lol.pyr.znpcsplus.util;
public enum LookType {
FIXED,
CLOSEST_PLAYER,
PER_PLAYER
}

@ -127,7 +127,7 @@ public class ZNpcsPlus extends JavaPlugin {
ConfigManager configManager = new ConfigManager(getDataFolder()); ConfigManager configManager = new ConfigManager(getDataFolder());
MojangSkinCache skinCache = new MojangSkinCache(configManager); MojangSkinCache skinCache = new MojangSkinCache(configManager);
EntityPropertyRegistryImpl propertyRegistry = new EntityPropertyRegistryImpl(skinCache); EntityPropertyRegistryImpl propertyRegistry = new EntityPropertyRegistryImpl(skinCache, configManager);
PacketFactory packetFactory = setupPacketFactory(scheduler, propertyRegistry); PacketFactory packetFactory = setupPacketFactory(scheduler, propertyRegistry);
propertyRegistry.registerTypes(packetFactory); propertyRegistry.registerTypes(packetFactory);
@ -276,6 +276,7 @@ public class ZNpcsPlus extends JavaPlugin {
registerEnumParser(manager, OcelotType.class, incorrectUsageMessage); registerEnumParser(manager, OcelotType.class, incorrectUsageMessage);
registerEnumParser(manager, PandaGene.class, incorrectUsageMessage); registerEnumParser(manager, PandaGene.class, incorrectUsageMessage);
registerEnumParser(manager, PuffState.class, incorrectUsageMessage); registerEnumParser(manager, PuffState.class, incorrectUsageMessage);
registerEnumParser(manager, LookType.class, incorrectUsageMessage);
manager.registerCommand("npc", new MultiCommand(loadHelpMessage("root")) manager.registerCommand("npc", new MultiCommand(loadHelpMessage("root"))
.addSubcommand("center", new CenterCommand(npcRegistry)) .addSubcommand("center", new CenterCommand(npcRegistry))

@ -3,6 +3,7 @@ package lol.pyr.znpcsplus.conversion.citizens.model.traits;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry; import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.conversion.citizens.model.SectionCitizensTrait; import lol.pyr.znpcsplus.conversion.citizens.model.SectionCitizensTrait;
import lol.pyr.znpcsplus.npc.NpcImpl; import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.util.LookType;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -16,7 +17,7 @@ public class LookTrait extends SectionCitizensTrait {
@Override @Override
public @NotNull NpcImpl apply(NpcImpl npc, ConfigurationSection section) { 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; return npc;
} }
} }

@ -28,13 +28,16 @@ import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.skin.Skin; import lol.pyr.znpcsplus.skin.Skin;
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache; import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
import lol.pyr.znpcsplus.skin.descriptor.FetchingDescriptor; 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.skin.descriptor.PrefetchedDescriptor;
import lol.pyr.znpcsplus.util.BungeeConnector; import lol.pyr.znpcsplus.util.BungeeConnector;
import lol.pyr.znpcsplus.util.ItemSerializationUtil; import lol.pyr.znpcsplus.util.ItemSerializationUtil;
import lol.pyr.znpcsplus.util.LookType;
import lol.pyr.znpcsplus.util.NpcLocation; import lol.pyr.znpcsplus.util.NpcLocation;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.DyeColor;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.io.BufferedReader; 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()))); npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new PrefetchedDescriptor(new Skin(model.getSkin(), model.getSignature())));
} }
Map<String, Object> 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); NpcEntryImpl entry = new NpcEntryImpl(String.valueOf(model.getId()), npc);
entry.enableEverything(); entry.enableEverything();
entries.add(entry); entries.add(entry);

@ -19,6 +19,7 @@ public class ZNpcsModel {
private List<String> hologramLines; private List<String> hologramLines;
private List<ZNpcsAction> clickActions; private List<ZNpcsAction> clickActions;
private Map<String, String> npcEquip; private Map<String, String> npcEquip;
private Map<String, Object> npcToggleValues;
private Map<String, String[]> customizationMap; private Map<String, String[]> customizationMap;
public int getId() { public int getId() {
@ -57,6 +58,10 @@ public class ZNpcsModel {
return npcEquip; return npcEquip;
} }
public Map<String, Object> getNpcToggleValues() {
return npcToggleValues;
}
public Map<String, String[]> getCustomizationMap() { public Map<String, String[]> getCustomizationMap() {
return customizationMap; return customizationMap;
} }

@ -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.EntityProperty;
import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry; import lol.pyr.znpcsplus.api.entity.EntityPropertyRegistry;
import lol.pyr.znpcsplus.api.skin.SkinDescriptor; 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.*;
import lol.pyr.znpcsplus.entity.properties.villager.VillagerLevelProperty; import lol.pyr.znpcsplus.entity.properties.villager.VillagerLevelProperty;
import lol.pyr.znpcsplus.entity.properties.villager.VillagerProfessionProperty; import lol.pyr.znpcsplus.entity.properties.villager.VillagerProfessionProperty;
@ -42,9 +43,9 @@ import java.util.stream.Collectors;
public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
private final Map<Class<?>, PropertySerializer<?>> serializerMap = new HashMap<>(); private final Map<Class<?>, PropertySerializer<?>> serializerMap = new HashMap<>();
private final Map<String, EntityPropertyImpl<?>> byName = new HashMap<>(); private final Map<String, EntityPropertyImpl<?>> byName = new HashMap<>();
private final ConfigManager configManager;
public EntityPropertyRegistryImpl(MojangSkinCache skinCache) { public EntityPropertyRegistryImpl(MojangSkinCache skinCache, ConfigManager configManager) {
registerSerializer(new BooleanPropertySerializer());
registerSerializer(new ComponentPropertySerializer()); registerSerializer(new ComponentPropertySerializer());
registerSerializer(new NamedTextColorPropertySerializer()); registerSerializer(new NamedTextColorPropertySerializer());
registerSerializer(new SkinDescriptorSerializer(skinCache)); registerSerializer(new SkinDescriptorSerializer(skinCache));
@ -52,7 +53,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
registerSerializer(new ColorPropertySerializer()); registerSerializer(new ColorPropertySerializer());
registerSerializer(new Vector3fPropertySerializer()); registerSerializer(new Vector3fPropertySerializer());
registerSerializer(new BlockStatePropertySerializer()); registerSerializer(new BlockStatePropertySerializer());
registerSerializer(new IntegerPropertySerializer()); registerSerializer(new LookTypeSerializer());
registerEnumSerializer(NpcPose.class); registerEnumSerializer(NpcPose.class);
registerEnumSerializer(DyeColor.class); registerEnumSerializer(DyeColor.class);
@ -76,6 +77,10 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
registerEnumSerializer(PandaGene.class); registerEnumSerializer(PandaGene.class);
registerEnumSerializer(PuffState.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 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 NameProperty(legacyNames, optionalComponents));
register(new DinnerboneProperty(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 GlowProperty(packetFactory));
register(new BitsetProperty("fire", 0, 0x01)); register(new BitsetProperty("fire", 0, 0x01));
register(new BitsetProperty("invisible", 0, 0x20)); register(new BitsetProperty("invisible", 0, 0x20));
@ -372,13 +380,14 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
// Player // Player
NBTProperty.NBTDecoder<ParrotVariant> parrotVariantDecoder = (variant) -> { NBTProperty.NBTDecoder<ParrotVariant> parrotVariantDecoder = (variant) -> {
NBTCompound compound = new NBTCompound(); NBTCompound compound = new NBTCompound();
if (variant == null) return compound;
compound.setTag("id", new NBTString("minecraft:parrot")); compound.setTag("id", new NBTString("minecraft:parrot"));
compound.setTag("Variant", new NBTInt(variant.ordinal())); compound.setTag("Variant", new NBTInt(variant.ordinal()));
return compound; return compound;
}; };
int shoulderIndex = skinLayersIndex+2; int shoulderIndex = skinLayersIndex+2;
register(new NBTProperty<>("shoulder_entity_left", 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)); register(new NBTProperty<>("shoulder_entity_right", ParrotVariant.class, shoulderIndex, parrotVariantDecoder, true));
if (!ver.isNewerThanOrEquals(ServerVersion.V_1_13)) return; if (!ver.isNewerThanOrEquals(ServerVersion.V_1_13)) return;
// Pufferfish // Pufferfish
@ -515,6 +524,16 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
serializerMap.put(clazz, new EnumPropertySerializer<>(clazz)); serializerMap.put(clazz, new EnumPropertySerializer<>(clazz));
} }
private void registerPrimitiveSerializers(Class<?>... classes) {
for (Class<?> clazz : classes) {
registerPrimitiveSerializer(clazz);
}
}
private <T> void registerPrimitiveSerializer(Class<T> clazz) {
serializerMap.put(clazz, new PrimitivePropertySerializer<>(clazz));
}
private <T> void register(EntityPropertyImpl<?> property) { private <T> void register(EntityPropertyImpl<?> property) {
if (byName.containsKey(property.getName())) if (byName.containsKey(property.getName()))
throw new IllegalArgumentException("Duplicate property name: " + property.getName()); throw new IllegalArgumentException("Duplicate property name: " + property.getName());

@ -60,6 +60,10 @@ public class PacketEntity implements PropertyHolder {
else packetFactory.spawnEntity(player, this, properties); 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) { public void despawn(Player player) {
packetFactory.destroyEntity(player, this, properties); packetFactory.destroyEntity(player, this, properties);
} }

@ -0,0 +1,30 @@
package lol.pyr.znpcsplus.entity;
import java.lang.reflect.InvocationTargetException;
public class PrimitivePropertySerializer<T> implements PropertySerializer<T> {
private final Class<T> clazz;
public PrimitivePropertySerializer(Class<T> 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<T> getTypeClass() {
return clazz;
}
}

@ -14,32 +14,43 @@ public class NBTProperty<T> extends EntityPropertyImpl<T> {
private final EntityDataType<NBTCompound> type; private final EntityDataType<NBTCompound> type;
private final NBTDecoder<T> decoder; private final NBTDecoder<T> decoder;
private final int index; 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<T> clazz, int index, NBTDecoder<T> decoder, EntityDataType<NBTCompound> type) { public NBTProperty(String name, T defaultValue, Class<T> clazz, int index, NBTDecoder<T> decoder, boolean allowNull, EntityDataType<NBTCompound> type) {
super(name, defaultValue, clazz); super(name, defaultValue, clazz);
this.decoder = decoder; this.decoder = decoder;
this.index = index; this.index = index;
this.allowNull = allowNull;
this.type = type; this.type = type;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public NBTProperty(String name, T defaultValue, int index, NBTDecoder<T> decoder) { public NBTProperty(String name, T defaultValue, int index, NBTDecoder<T> decoder, boolean allowNull) {
this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, EntityDataTypes.NBT); this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, allowNull, EntityDataTypes.NBT);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public NBTProperty(String name, T defaultValue, int index, NBTDecoder<T> decoder, EntityDataType<NBTCompound> type) { public NBTProperty(String name, T defaultValue, int index, NBTDecoder<T> decoder) {
this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, type); this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, false, EntityDataTypes.NBT);
}
@SuppressWarnings("unchecked")
public NBTProperty(String name, T defaultValue, int index, NBTDecoder<T> decoder, boolean allowNull, EntityDataType<NBTCompound> type) {
this(name, defaultValue, (Class<T>) defaultValue.getClass(), index, decoder, allowNull, type);
}
public NBTProperty(String name, Class<T> clazz, int index, NBTDecoder<T> decoder, boolean allowNull) {
this(name, null, clazz, index, decoder, allowNull, EntityDataTypes.NBT);
} }
public NBTProperty(String name, Class<T> clazz, int index, NBTDecoder<T> decoder) { public NBTProperty(String name, Class<T> clazz, int index, NBTDecoder<T> decoder) {
this(name, null, clazz, index, decoder, EntityDataTypes.NBT); this(name, null, clazz, index, decoder, false, EntityDataTypes.NBT);
} }
@Override @Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) { public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
T value = entity.getProperty(this); T value = entity.getProperty(this);
if (value == null) return; if (value == null && !allowNull) return;
properties.put(index, newEntityData(index, type, decoder.decode(value))); properties.put(index, newEntityData(index, type, decoder.decode(value)));
} }

@ -1,25 +0,0 @@
package lol.pyr.znpcsplus.entity.serializers;
import lol.pyr.znpcsplus.entity.PropertySerializer;
public class IntegerPropertySerializer implements PropertySerializer<Integer> {
@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<Integer> getTypeClass() {
return Integer.class;
}
}

@ -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<LookType> {
@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<LookType> getTypeClass() {
return LookType.class;
}
}

@ -83,6 +83,16 @@ public class NpcImpl extends Viewable implements Npc {
hologram.setLocation(location.withY(location.getY() + type.getHologramOffset())); 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() { public HologramImpl getHologram() {
return hologram; return hologram;
} }

@ -110,8 +110,9 @@ public class NpcTypeImpl implements NpcType {
public NpcTypeImpl build() { public NpcTypeImpl build() {
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion(); ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
addProperties("fire", "invisible", "silent", "look", addProperties("fire", "invisible", "silent", "look", "look_distance", "view_distance",
"potion_color", "potion_ambient", "dinnerbone"); "potion_color", "potion_ambient");
if (!type.equals(EntityTypes.PLAYER)) addProperties("dinnerbone");
// TODO: make this look nicer after completing the rest of the properties // 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_9)) addProperties("glow");
if (version.isNewerThanOrEquals(ServerVersion.V_1_14)) { if (version.isNewerThanOrEquals(ServerVersion.V_1_14)) {

@ -22,4 +22,5 @@ public interface PacketFactory {
void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties); void sendAllMetadata(Player player, PacketEntity entity, PropertyHolder properties);
void sendEquipment(Player player, PacketEntity entity, Equipment equipment); void sendEquipment(Player player, PacketEntity entity, Equipment equipment);
void sendMetadata(Player player, PacketEntity entity, List<EntityData> data); void sendMetadata(Player player, PacketEntity entity, List<EntityData> data);
void sendHeadRotation(Player player, PacketEntity entity, float yaw, float pitch);
} }

@ -137,6 +137,12 @@ public class V1_8PacketFactory implements PacketFactory {
sendPacket(player, new WrapperPlayServerEntityMetadata(entity.getEntityId(), data)); 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 @Override
public void sendEquipment(Player player, PacketEntity entity, Equipment equipment) { public void sendEquipment(Player player, PacketEntity entity, Equipment equipment) {
sendPacket(player, new WrapperPlayServerEntityEquipment(entity.getEntityId(), Collections.singletonList(equipment))); sendPacket(player, new WrapperPlayServerEntityEquipment(entity.getEntityId(), Collections.singletonList(equipment)));

@ -8,6 +8,7 @@ import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.npc.NpcEntryImpl; import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcImpl; import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl; import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
import lol.pyr.znpcsplus.util.LookType;
import lol.pyr.znpcsplus.util.NpcLocation; import lol.pyr.znpcsplus.util.NpcLocation;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -26,15 +27,18 @@ public class NpcProcessorTask extends BukkitRunnable {
} }
public void run() { public void run() {
double distSq = NumberConversions.square(configManager.getConfig().viewDistance()); EntityPropertyImpl<Integer> viewDistanceProperty = propertyRegistry.getByName("view_distance", Integer.class); // Not sure why this is an Integer, but it is
double lookPropertyDistSq = NumberConversions.square(configManager.getConfig().lookPropertyDistance()); EntityPropertyImpl<LookType> lookProperty = propertyRegistry.getByName("look", LookType.class);
EntityPropertyImpl<Boolean> lookProperty = propertyRegistry.getByName("look", Boolean.class); EntityPropertyImpl<Double> lookDistanceProperty = propertyRegistry.getByName("look_distance", Double.class);
double lookDistance;
for (NpcEntryImpl entry : npcRegistry.getProcessable()) { for (NpcEntryImpl entry : npcRegistry.getProcessable()) {
NpcImpl npc = entry.getNpc(); NpcImpl npc = entry.getNpc();
if (!npc.isEnabled()) continue; if (!npc.isEnabled()) continue;
double closestDist = Double.MAX_VALUE; double closestDist = Double.MAX_VALUE;
Player closest = null; Player closest = null;
LookType lookType = npc.getProperty(lookProperty);
lookDistance = NumberConversions.square(npc.getProperty(lookDistanceProperty));
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
if (!player.getWorld().equals(npc.getWorld())) { if (!player.getWorld().equals(npc.getWorld())) {
if (npc.isVisibleTo(player)) npc.hide(player); if (npc.isVisibleTo(player)) npc.hide(player);
@ -43,7 +47,7 @@ public class NpcProcessorTask extends BukkitRunnable {
double distance = player.getLocation().distanceSquared(npc.getBukkitLocation()); double distance = player.getLocation().distanceSquared(npc.getBukkitLocation());
// visibility // visibility
boolean inRange = distance <= distSq; boolean inRange = distance <= NumberConversions.square(npc.getProperty(viewDistanceProperty));
if (!inRange && npc.isVisibleTo(player)) { if (!inRange && npc.isVisibleTo(player)) {
NpcDespawnEvent event = new NpcDespawnEvent(player, entry); NpcDespawnEvent event = new NpcDespawnEvent(player, entry);
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
@ -60,12 +64,18 @@ public class NpcProcessorTask extends BukkitRunnable {
closestDist = distance; closestDist = distance;
closest = player; 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 // look property
if (closest != null && npc.getProperty(lookProperty) && lookPropertyDistSq >= closestDist) { if (lookType.equals(LookType.CLOSEST_PLAYER)) {
NpcLocation expected = npc.getLocation().lookingAt(closest.getLocation().add(0, -npc.getType().getHologramOffset(), 0)); if (closest != null && lookDistance >= closestDist) {
if (!expected.equals(npc.getLocation())) npc.setLocation(expected); NpcLocation expected = npc.getLocation().lookingAt(closest.getLocation().add(0, -npc.getType().getHologramOffset(), 0));
if (!expected.equals(npc.getLocation())) npc.setHeadRotation(expected.getYaw(), expected.getPitch());
}
} }
} }
} }