diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java index 52abae7..5829d99 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java @@ -152,7 +152,7 @@ public class ZNpcsPlus { pluginManager.registerEvents(new UpdateNotificationListener(this, adventure, updateChecker, scheduler), bootstrap); } - scheduler.runDelayedTimerAsync(new NpcProcessorTask(npcRegistry, propertyRegistry), 60L, 3L); + scheduler.runDelayedTimerAsync(new NpcProcessorTask(npcRegistry, propertyRegistry, userManager), 60L, 3L); scheduler.runDelayedTimerAsync(new HologramRefreshTask(npcRegistry), 60L, 20L); scheduler.runDelayedTimerAsync(new SkinCacheCleanTask(skinCache), 1200, 1200); pluginManager.registerEvents(new ViewableHideOnLeaveListener(), bootstrap); @@ -275,6 +275,7 @@ public class ZNpcsPlus { registerEnumParser(manager, SnifferState.class, incorrectUsageMessage); registerEnumParser(manager, RabbitType.class, incorrectUsageMessage); registerEnumParser(manager, AttachDirection.class, incorrectUsageMessage); + registerEnumParser(manager, Sound.class, incorrectUsageMessage); manager.registerCommand("npc", new MultiCommand(bootstrap.loadHelpMessage("root")) .addSubcommand("center", new CenterCommand(npcRegistry)) 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 adf1b35..64b43a5 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java @@ -24,6 +24,7 @@ import lol.pyr.znpcsplus.util.*; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.Color; import org.bukkit.DyeColor; +import org.bukkit.Sound; import java.util.*; import java.util.stream.Collectors; @@ -84,6 +85,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { registerEnumSerializer(SnifferState.class); registerEnumSerializer(RabbitType.class); registerEnumSerializer(AttachDirection.class); + registerEnumSerializer(Sound.class); registerPrimitiveSerializers(Integer.class, Boolean.class, Double.class, Float.class, Long.class, Short.class, Byte.class, String.class); @@ -122,6 +124,20 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { register(new DummyProperty<>("permission_required", false)); + register(new DummyProperty<>("player_knockback", false)); + register(new DummyProperty<>("player_knockback_exempt_permission", String.class)); + register(new DummyProperty<>("player_knockback_distance", 0.4)); + register(new DummyProperty<>("player_knockback_vertical", 0.4)); + register(new DummyProperty<>("player_knockback_horizontal", 0.9)); + register(new DummyProperty<>("player_knockback_cooldown", 1500)); + register(new DummyProperty<>("player_knockback_sound", false)); + register(new DummyProperty<>("player_knockback_sound_volume", 1.0f)); + register(new DummyProperty<>("player_knockback_sound_pitch", 1.0f)); + register(new DummyProperty<>("player_knockback_sound_name", Sound.valueOf( + PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_9) ? + "VILLAGER_NO" : "ENTITY_VILLAGER_NO" + ))); + 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/npc/NpcTypeImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java index 3e5119d..565493f 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java @@ -115,7 +115,10 @@ public class NpcTypeImpl implements NpcType { public NpcTypeImpl build() { ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion(); addProperties("fire", "invisible", "silent", "look", "look_distance", "view_distance", - "potion_color", "potion_ambient", "display_name", "permission_required"); + "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"); // TODO: make this look nicer after completing the rest of the properties if (version.isNewerThanOrEquals(ServerVersion.V_1_9)) addProperties("glow"); 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 f92838d..e0cdcf1 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/tasks/NpcProcessorTask.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/tasks/NpcProcessorTask.java @@ -7,20 +7,26 @@ 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.user.User; +import lol.pyr.znpcsplus.user.UserManager; import lol.pyr.znpcsplus.util.LookType; import lol.pyr.znpcsplus.util.NpcLocation; import org.bukkit.Bukkit; +import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.NumberConversions; +import org.bukkit.util.Vector; public class NpcProcessorTask extends BukkitRunnable { private final NpcRegistryImpl npcRegistry; private final EntityPropertyRegistryImpl propertyRegistry; + private final UserManager userManager; - public NpcProcessorTask(NpcRegistryImpl npcRegistry, EntityPropertyRegistryImpl propertyRegistry) { + public NpcProcessorTask(NpcRegistryImpl npcRegistry, EntityPropertyRegistryImpl propertyRegistry,UserManager userManager) { this.npcRegistry = npcRegistry; this.propertyRegistry = propertyRegistry; + this.userManager = userManager; } public void run() { @@ -28,7 +34,28 @@ public class NpcProcessorTask extends BukkitRunnable { EntityPropertyImpl lookProperty = propertyRegistry.getByName("look", LookType.class); EntityPropertyImpl lookDistanceProperty = propertyRegistry.getByName("look_distance", Double.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); + EntityPropertyImpl playerKnockbackDistanceProperty = propertyRegistry.getByName("player_knockback_distance", Double.class); + EntityPropertyImpl playerKnockbackVerticalProperty = propertyRegistry.getByName("player_knockback_vertical", Double.class); + EntityPropertyImpl playerKnockbackHorizontalProperty = propertyRegistry.getByName("player_knockback_horizontal", Double.class); + EntityPropertyImpl playerKnockbackCooldownProperty = propertyRegistry.getByName("player_knockback_cooldown", Integer.class); + EntityPropertyImpl playerKnockbackSoundProperty = propertyRegistry.getByName("player_knockback_sound", Boolean.class); + EntityPropertyImpl playerKnockbackSoundNameProperty = propertyRegistry.getByName("player_knockback_sound_name", Sound.class); + EntityPropertyImpl playerKnockbackSoundVolumeProperty = propertyRegistry.getByName("player_knockback_sound_volume", Float.class); + EntityPropertyImpl playerKnockbackSoundPitchProperty = propertyRegistry.getByName("player_knockback_sound_pitch", Float.class); double lookDistance; + boolean permissionRequired; + boolean playerKnockback; + String playerKnockbackExemptPermission = null; + double playerKnockbackDistance = 0; + double playerKnockbackVertical = 0; + double playerKnockbackHorizontal = 0; + int playerKnockbackCooldown = 0; + boolean playerKnockbackSound = false; + Sound playerKnockbackSoundName = null; + float playerKnockbackSoundVolume = 0; + float playerKnockbackSoundPitch = 0; for (NpcEntryImpl entry : npcRegistry.getProcessable()) { NpcImpl npc = entry.getNpc(); if (!npc.isEnabled()) continue; @@ -37,7 +64,19 @@ public class NpcProcessorTask extends BukkitRunnable { Player closest = null; LookType lookType = npc.getProperty(lookProperty); lookDistance = NumberConversions.square(npc.getProperty(lookDistanceProperty)); - boolean permissionRequired = npc.getProperty(permissionRequiredProperty); + permissionRequired = npc.getProperty(permissionRequiredProperty); + playerKnockback = npc.getProperty(playerKnockbackProperty); + if (playerKnockback) { + playerKnockbackExemptPermission = npc.getProperty(playerKnockbackExemptPermissionProperty); + playerKnockbackDistance = NumberConversions.square(npc.getProperty(playerKnockbackDistanceProperty)); + playerKnockbackVertical = npc.getProperty(playerKnockbackVerticalProperty); + playerKnockbackHorizontal = npc.getProperty(playerKnockbackHorizontalProperty); + playerKnockbackCooldown = npc.getProperty(playerKnockbackCooldownProperty); + playerKnockbackSound = npc.getProperty(playerKnockbackSoundProperty); + playerKnockbackSoundName = npc.getProperty(playerKnockbackSoundNameProperty); + playerKnockbackSoundVolume = npc.getProperty(playerKnockbackSoundVolumeProperty); + playerKnockbackSoundPitch = npc.getProperty(playerKnockbackSoundPitchProperty); + } for (Player player : Bukkit.getOnlinePlayers()) { if (!player.getWorld().equals(npc.getWorld())) { if (npc.isVisibleTo(player)) npc.hide(player); @@ -71,6 +110,21 @@ public class NpcProcessorTask extends BukkitRunnable { 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()); } + + // player knockback + User user = userManager.get(player.getUniqueId()); + if (playerKnockbackExemptPermission == null || !player.hasPermission(playerKnockbackExemptPermission)) { + if (playerKnockback && distance <= playerKnockbackDistance && user.canKnockback(playerKnockbackCooldown)) { + double x = npc.getLocation().getX() - player.getLocation().getX(); + double z = npc.getLocation().getZ() - player.getLocation().getZ(); + double angle = Math.atan2(z, x); + double knockbackX = -Math.cos(angle) * playerKnockbackHorizontal; + double knockbackZ = -Math.sin(angle) * playerKnockbackHorizontal; + player.setVelocity(player.getVelocity().add(new Vector(knockbackX, playerKnockbackVertical, knockbackZ))); + if (playerKnockbackSound) + player.playSound(player.getLocation(), playerKnockbackSoundName, playerKnockbackSoundVolume, playerKnockbackSoundPitch); + } + } } } // look property diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/user/User.java b/plugin/src/main/java/lol/pyr/znpcsplus/user/User.java index 2d9e45f..216001e 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/user/User.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/user/User.java @@ -11,6 +11,7 @@ import java.util.UUID; public class User { private final UUID uuid; private long lastNpcInteraction; + private long lastNpcKnockback; private final Map actionCooldownMap = new HashMap<>(); public User(UUID uuid) { @@ -29,6 +30,14 @@ public class User { return false; } + public boolean canKnockback(int cooldown) { + if (System.currentTimeMillis() - lastNpcKnockback > cooldown) { + lastNpcKnockback = System.currentTimeMillis(); + return true; + } + return false; + } + public UUID getUuid() { return uuid; }