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

This commit is contained in:
Pyrbu 2024-12-29 13:51:48 +01:00
commit a4e3605d99
18 changed files with 285 additions and 13 deletions

View file

@ -30,10 +30,28 @@ dependencies {
implementation project(":api")
}
ext {
gitBranch = System.getenv('GIT_BRANCH') ?: ''
gitCommitHash = System.getenv('GIT_COMMIT') ?: ''
buildId = System.getenv('BUILD_ID') ?: ''
}
shadowJar {
archivesBaseName = "ZNPCsPlus"
archiveClassifier.set ""
manifest {
if (gitBranch?.trim()) {
attributes('Git-Branch': gitBranch)
}
if (gitCommitHash?.trim()) {
attributes('Git-Commit': gitCommitHash)
}
if (buildId?.trim()) {
attributes('Build-Id': buildId)
}
}
relocate "org.objectweb.asm", "lol.pyr.znpcsplus.libraries.asm"
relocate "me.lucko.jarrelocator", "lol.pyr.znpcsplus.libraries.jarrelocator"

View file

@ -342,6 +342,7 @@ public class ZNpcsPlus {
.addSubcommand("delete", new ActionDeleteCommand(npcRegistry))
.addSubcommand("edit", new ActionEditCommand(npcRegistry, actionRegistry))
.addSubcommand("list", new ActionListCommand(npcRegistry)))
.addSubcommand("version", new VersionCommand(this))
);
}

View file

@ -26,12 +26,18 @@ public class CreateCommand implements CommandHandler {
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " create <id> <type>");
context.setUsage(context.getLabel() + " create <id> [<type>]");
Player player = context.ensureSenderIsPlayer();
String id = context.popString();
if (npcRegistry.getById(id) != null) context.halt(Component.text("NPC with that ID already exists.", NamedTextColor.RED));
NpcTypeImpl type = context.parse(NpcTypeImpl.class);
NpcTypeImpl type;
if (context.argSize() == 1) {
type = context.parse(NpcTypeImpl.class);
} else {
type = typeRegistry.getByName("player");
}
NpcEntryImpl entry = npcRegistry.create(id, player.getWorld(), type, new NpcLocation(player.getLocation()));
entry.enableEverything();

View file

@ -21,9 +21,14 @@ public class ToggleCommand implements CommandHandler {
@Override
public void run(CommandContext context) throws CommandExecutionException {
context.setUsage(context.getLabel() + " toggle <id>");
context.setUsage(context.getLabel() + " toggle <id> [<enable/disable>]");
NpcImpl npc = context.parse(NpcEntryImpl.class).getNpc();
boolean enabled = !npc.isEnabled();
boolean enabled;
if (context.argSize() == 1) {
enabled = context.popString().equalsIgnoreCase("enable");
} else {
enabled = !npc.isEnabled();
}
npc.setEnabled(enabled);
context.send(Component.text("NPC has been " + (enabled ? "enabled" : "disabled"), NamedTextColor.GREEN));
}
@ -31,6 +36,7 @@ public class ToggleCommand implements CommandHandler {
@Override
public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds());
if (context.argSize() == 2) return context.suggestLiteral("enable", "disable");
return Collections.emptyList();
}
}

View file

@ -0,0 +1,64 @@
package lol.pyr.znpcsplus.commands;
import lol.pyr.director.adventure.command.CommandContext;
import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.ZNpcsPlus;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
public class VersionCommand implements CommandHandler {
private final String pluginVersion;
private final String gitBranch;
private final String gitCommitHash;
private final String buildId;
public VersionCommand(ZNpcsPlus plugin) {
pluginVersion = plugin.getDescription().getVersion();
String gitBranch = "";
String gitCommitHash = "";
String buildId = "";
try {
URL jarUrl = getClass().getProtectionDomain().getCodeSource().getLocation();
JarFile jarFile = new JarFile(jarUrl.toURI().getPath());
Attributes attributes = jarFile.getManifest().getMainAttributes();
gitBranch = attributes.getValue("Git-Branch");
gitCommitHash = attributes.getValue("Git-Commit-Hash");
buildId = attributes.getValue("Build-Id");
} catch (IOException | URISyntaxException e) {
e.printStackTrace();
}
this.gitBranch = gitBranch;
this.gitCommitHash = gitCommitHash;
this.buildId = buildId;
}
@Override
public void run(CommandContext context) throws CommandExecutionException {
StringBuilder versionBuilder = new StringBuilder("This server is running ZNPCsPlus version ").append(pluginVersion);
if (gitBranch != null && !gitBranch.isEmpty()) {
versionBuilder.append("-").append(gitBranch);
}
if (gitCommitHash != null && !gitCommitHash.isEmpty()) {
versionBuilder.append("@").append(gitCommitHash);
}
if (buildId != null && !buildId.isEmpty()) {
versionBuilder.append(" (Build #").append(buildId).append(")");
}
String version = versionBuilder.toString();
context.send(Component.text(version, NamedTextColor.GREEN)
.hoverEvent(Component.text("Click to copy version to clipboard"))
.clickEvent(ClickEvent.copyToClipboard(version)));
}
}

View file

@ -11,6 +11,7 @@ import lol.pyr.director.adventure.command.CommandHandler;
import lol.pyr.director.common.command.CommandExecutionException;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.properties.attributes.AttributeProperty;
import lol.pyr.znpcsplus.npc.NpcEntryImpl;
import lol.pyr.znpcsplus.npc.NpcImpl;
import lol.pyr.znpcsplus.npc.NpcRegistryImpl;
@ -124,6 +125,15 @@ public class PropertySetCommand implements CommandHandler {
value = context.parse(type);
valueName = value == null ? "NONE" : ((Vector3i) value).toPrettyString();
}
else if (property instanceof AttributeProperty) {
value = context.parse(type);
if ((double) value < ((AttributeProperty) property).getMinValue() || (double) value > ((AttributeProperty) property).getMaxValue()) {
double sanitizedValue = ((AttributeProperty) property).sanitizeValue((double) value);
context.send(Component.text("WARNING: Value " + value + " is out of range for property " + property.getName() + ", setting to " + sanitizedValue, NamedTextColor.YELLOW));
value = sanitizedValue;
}
valueName = String.valueOf(value);
}
else {
try {
value = context.parse(type);

View file

@ -189,12 +189,15 @@ public class ZNpcImporter implements DataImporter {
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) {
if (toggleValues.containsKey("glow") && (boolean) toggleValues.get("glow")) {
if (!model.getGlowName().isEmpty())
try {
npc.setProperty(propertyRegistry.getByName("glow", DyeColor.class), DyeColor.valueOf(model.getGlowName()));
} catch (IllegalArgumentException e) {
npc.setProperty(propertyRegistry.getByName("glow", DyeColor.class), DyeColor.WHITE);
}
else
npc.setProperty(propertyRegistry.getByName("glow", DyeColor.class), DyeColor.WHITE);
}
}
}

View file

@ -84,4 +84,8 @@ public class ZNpcsModel {
public String getSignature() {
return signature;
}
public String getGlowName() {
return glowName;
}
}

View file

@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.entity;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
@ -15,6 +16,7 @@ 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.attributes.AttributeProperty;
import lol.pyr.znpcsplus.entity.properties.villager.VillagerLevelProperty;
import lol.pyr.znpcsplus.entity.properties.villager.VillagerProfessionProperty;
import lol.pyr.znpcsplus.entity.properties.villager.VillagerTypeProperty;
@ -126,6 +128,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));
@ -153,6 +156,16 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
linkProperties("glow", "fire", "invisible");
register(new BooleanProperty("silent", 4, false, legacyBooleans));
// Attribute Max Health
register(new AttributeProperty(packetFactory, "attribute_max_health", Attributes.MAX_HEALTH));
// Health - LivingEntity
int healthIndex = 6;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) healthIndex = 9;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_14)) healthIndex = 8;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_10)) healthIndex = 7;
register(new HealthProperty(healthIndex));
final int tameableIndex;
if (ver.isNewerThanOrEquals(ServerVersion.V_1_17)) tameableIndex = 17;
else if (ver.isNewerThanOrEquals(ServerVersion.V_1_15)) tameableIndex = 16;

View file

@ -0,0 +1,27 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.protocol.attribute.Attributes;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import org.bukkit.entity.Player;
import java.util.Map;
public class HealthProperty extends EntityPropertyImpl<Float> {
private final int index;
public HealthProperty(int index) {
super("health", 20f, Float.class);
this.index = index;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
float health = entity.getProperty(this);
health = (float) Attributes.MAX_HEALTH.sanitizeValue(health);
properties.put(index, new EntityData(index, EntityDataTypes.FLOAT, health));
}
}

View file

@ -0,0 +1,63 @@
package lol.pyr.znpcsplus.entity.properties.attributes;
import com.github.retrooper.packetevents.protocol.attribute.Attribute;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.packets.PacketFactory;
import org.bukkit.entity.Player;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class AttributeProperty extends EntityPropertyImpl<Double> {
private final PacketFactory packetFactory;
private final Attribute attribute;
public AttributeProperty(PacketFactory packetFactory, String name, Attribute attribute) {
super(name, attribute.getDefaultValue(), Double.class);
this.packetFactory = packetFactory;
this.attribute = attribute;
}
public double getMinValue() {
return attribute.getMinValue();
}
public double getMaxValue() {
return attribute.getMaxValue();
}
public double sanitizeValue(double value) {
return attribute.sanitizeValue(value);
}
@Override
public List<EntityData> applyStandalone(Player player, PacketEntity packetEntity, boolean isSpawned) {
apply(player, packetEntity, isSpawned, Collections.emptyList());
return Collections.emptyList();
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
}
public void apply(Player player, PacketEntity entity, boolean isSpawned, List<WrapperPlayServerUpdateAttributes.Property> properties) {
Double value = entity.getProperty(this);
if (value == null) {
return;
}
value = attribute.sanitizeValue(value);
if (isSpawned) {
packetFactory.sendAttribute(player, entity, new WrapperPlayServerUpdateAttributes.Property(attribute, value, Collections.emptyList()));
} else {
properties.add(new WrapperPlayServerUpdateAttributes.Property(attribute, value, Collections.emptyList()));
}
}
public Attribute getAttribute() {
return attribute;
}
}

View file

@ -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<EntityPropertyImpl<?>, Object> propertyMap = new HashMap<>();
private final List<InteractionAction> actions = new ArrayList<>();
private final Map<UUID, float[]> 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<Void> 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);
}

View file

@ -114,12 +114,15 @@ 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",
"player_knockback_sound_volume", "player_knockback_sound_pitch");
if (!type.equals(EntityTypes.PLAYER)) addProperties("dinnerbone");
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.LIVINGENTITY)) {
addProperties("health", "attribute_max_health");
}
// 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)) {

View file

@ -2,6 +2,7 @@ package lol.pyr.znpcsplus.packets;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.player.Equipment;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.util.NamedColor;
@ -25,4 +26,6 @@ public interface PacketFactory {
void sendHeadRotation(Player player, PacketEntity entity, float yaw, float pitch);
void sendHandSwing(Player player, PacketEntity entity, boolean offHand);
void setPassengers(Player player, int vehicle, int... passengers);
void sendAllAttributes(Player player, PacketEntity entity, PropertyHolder properties);
void sendAttribute(Player player, PacketEntity entity, WrapperPlayServerUpdateAttributes.Property property);
}

View file

@ -1,6 +1,7 @@
package lol.pyr.znpcsplus.packets;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
@ -27,6 +28,7 @@ public class V1_17PacketFactory extends V1_8PacketFactory {
sendPacket(player, new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(),
npcLocationToVector(location), location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.of(new Vector3d())));
sendAllMetadata(player, entity, properties);
if (EntityTypes.isTypeInstanceOf(entity.getType(), EntityTypes.LIVINGENTITY)) sendAllAttributes(player, entity, properties);
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
}
}

View file

@ -36,6 +36,7 @@ public class V1_20_2PacketFactory extends V1_19_3PacketFactory {
npcLocationToVector(location), location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.of(new Vector3d())));
sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(), location.getYaw()));
sendAllMetadata(player, entity, properties);
sendAllAttributes(player, entity, properties);
scheduler.runLaterAsync(() -> removeTabPlayer(player, entity), configManager.getConfig().tabHideDelay());
});
}

View file

@ -18,6 +18,7 @@ import lol.pyr.znpcsplus.config.ConfigManager;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.entity.properties.attributes.AttributeProperty;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import lol.pyr.znpcsplus.skin.BaseSkinDescriptor;
import lol.pyr.znpcsplus.util.NamedColor;
@ -55,6 +56,7 @@ public class V1_8PacketFactory implements PacketFactory {
entity.getUuid(), npcLocationToVector(location), location.getYaw(), location.getPitch(), Collections.emptyList()));
sendPacket(player, new WrapperPlayServerEntityHeadLook(entity.getEntityId(), location.getYaw()));
sendAllMetadata(player, entity, properties);
sendAllAttributes(player, entity, properties);
scheduler.runLaterAsync(() -> removeTabPlayer(player, entity), configManager.getConfig().tabHideDelay());
});
}
@ -70,6 +72,7 @@ public class V1_8PacketFactory implements PacketFactory {
new WrapperPlayServerSpawnEntity(entity.getEntityId(), Optional.of(entity.getUuid()), entity.getType(), npcLocationToVector(location),
location.getPitch(), location.getYaw(), location.getYaw(), 0, Optional.empty()));
sendAllMetadata(player, entity, properties);
if (EntityTypes.isTypeInstanceOf(type, EntityTypes.LIVINGENTITY)) sendAllAttributes(player, entity, properties);
createTeam(player, entity, properties.getProperty(propertyRegistry.getByName("glow", NamedColor.class)));
}
@ -187,4 +190,19 @@ public class V1_8PacketFactory implements PacketFactory {
WrapperPlayServerEntityAnimation.EntityAnimationType.SWING_OFF_HAND :
WrapperPlayServerEntityAnimation.EntityAnimationType.SWING_MAIN_ARM));
}
@Override
public void sendAllAttributes(Player player, PacketEntity entity, PropertyHolder properties) {
List<WrapperPlayServerUpdateAttributes.Property> attributesList = new ArrayList<>();
properties.getAppliedProperties()
.stream()
.filter(property -> property instanceof AttributeProperty)
.forEach(property -> ((AttributeProperty) property).apply(player, entity, false, attributesList));
sendPacket(player, new WrapperPlayServerUpdateAttributes(entity.getEntityId(), attributesList));
}
@Override
public void sendAttribute(Player player, PacketEntity entity, WrapperPlayServerUpdateAttributes.Property property) {
sendPacket(player, new WrapperPlayServerUpdateAttributes(entity.getEntityId(), Collections.singletonList(property)));
}
}

View file

@ -33,6 +33,7 @@ public class NpcProcessorTask extends BukkitRunnable {
EntityPropertyImpl<Integer> viewDistanceProperty = propertyRegistry.getByName("view_distance", Integer.class); // Not sure why this is an Integer, but it is
EntityPropertyImpl<LookType> lookProperty = propertyRegistry.getByName("look", LookType.class);
EntityPropertyImpl<Double> lookDistanceProperty = propertyRegistry.getByName("look_distance", Double.class);
EntityPropertyImpl<Boolean> lookReturnProperty = propertyRegistry.getByName("look_return", Boolean.class);
EntityPropertyImpl<Boolean> permissionRequiredProperty = propertyRegistry.getByName("permission_required", Boolean.class);
EntityPropertyImpl<Boolean> playerKnockbackProperty = propertyRegistry.getByName("player_knockback", Boolean.class);
EntityPropertyImpl<String> playerKnockbackExemptPermissionProperty = propertyRegistry.getByName("player_knockback_exempt_permission", String.class);
@ -45,6 +46,7 @@ public class NpcProcessorTask extends BukkitRunnable {
EntityPropertyImpl<Float> playerKnockbackSoundVolumeProperty = propertyRegistry.getByName("player_knockback_sound_volume", Float.class);
EntityPropertyImpl<Float> 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());
}
}
}