|
|
@@ -1,104 +1,148 @@
|
|
|
package me.lethunderhawk.npc.manager;
|
|
|
|
|
|
-import com.comphenix.protocol.PacketType;
|
|
|
-import com.comphenix.protocol.ProtocolLibrary;
|
|
|
-import com.comphenix.protocol.events.PacketAdapter;
|
|
|
-import com.comphenix.protocol.events.PacketContainer;
|
|
|
-import com.comphenix.protocol.events.PacketEvent;
|
|
|
-import com.comphenix.protocol.wrappers.EnumWrappers;
|
|
|
-import com.google.common.cache.Cache;
|
|
|
-import com.google.common.cache.CacheBuilder;
|
|
|
-import me.lethunderhawk.fluxapi.Services;
|
|
|
-import me.lethunderhawk.main.Main;
|
|
|
-import me.lethunderhawk.npc.event.NPCClickAction;
|
|
|
-import me.lethunderhawk.npc.event.NPCInteractionEvent;
|
|
|
-import me.lethunderhawk.npc.util.NPC;
|
|
|
-import me.lethunderhawk.npc.util.NPCOptions;
|
|
|
-import me.lethunderhawk.npc.util.versioned.NPC_1_21_10;
|
|
|
-import org.bukkit.Bukkit;
|
|
|
+import me.lethunderhawk.npc.abstraction.DummyNPC;
|
|
|
+import me.lethunderhawk.npc.abstraction.NPC;
|
|
|
+import me.lethunderhawk.npc.abstraction.NPCOptions;
|
|
|
+import org.bukkit.Location;
|
|
|
+import org.bukkit.entity.Entity;
|
|
|
+import org.bukkit.entity.EntityType;
|
|
|
+import org.bukkit.entity.Mannequin;
|
|
|
import org.bukkit.entity.Player;
|
|
|
-import org.bukkit.plugin.java.JavaPlugin;
|
|
|
+import org.bukkit.event.EventHandler;
|
|
|
+import org.bukkit.event.Listener;
|
|
|
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
|
|
+import org.bukkit.event.player.PlayerInteractAtEntityEvent;
|
|
|
+import org.bukkit.event.player.PlayerMoveEvent;
|
|
|
+import org.bukkit.inventory.EquipmentSlot;
|
|
|
|
|
|
-import java.util.HashSet;
|
|
|
-import java.util.Optional;
|
|
|
-import java.util.Set;
|
|
|
-import java.util.concurrent.TimeUnit;
|
|
|
+import java.util.Collection;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.UUID;
|
|
|
|
|
|
-public class NPCManager {
|
|
|
-
|
|
|
- private final Set<NPC> registeredNPCs = new HashSet<>();
|
|
|
- private final JavaPlugin plugin;
|
|
|
+public class NPCManager implements Listener {
|
|
|
+ private static final double FOCUS_RANGE_SQUARED = 250;
|
|
|
+ public int focusRange = 30;
|
|
|
+ private final Map<UUID, NPC> npcs = new HashMap<>();
|
|
|
|
|
|
public NPCManager() {
|
|
|
- this.plugin = Services.get(Main.class);
|
|
|
- ProtocolLibrary.getProtocolManager().addPacketListener(
|
|
|
- new PacketAdapter(plugin, PacketType.Play.Client.USE_ENTITY) {
|
|
|
- @Override
|
|
|
- public void onPacketReceiving(PacketEvent event) {
|
|
|
- PacketContainer packet = event.getPacket();
|
|
|
-
|
|
|
- int id = packet.getIntegers().read(0);
|
|
|
- com.comphenix.protocol.wrappers.WrappedEnumEntityUseAction useAction = packet.getEnumEntityUseActions().read(0);
|
|
|
- if(useAction == null) return;
|
|
|
- EnumWrappers.Hand hand = useAction.getHand();
|
|
|
- EnumWrappers.EntityUseAction action = packet.getEntityUseActions().readSafely(0);
|
|
|
- if(action == null) return;
|
|
|
- if(hand == EnumWrappers.Hand.MAIN_HAND && action == EnumWrappers.EntityUseAction.INTERACT){
|
|
|
- handleEntityClick(event.getPlayer(), id, NPCClickAction.INTERACT);
|
|
|
- return;
|
|
|
- }
|
|
|
- if(hand == EnumWrappers.Hand.MAIN_HAND && action == EnumWrappers.EntityUseAction.ATTACK){
|
|
|
- handleEntityClick(event.getPlayer(), id, NPCClickAction.ATTACK);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
- );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Registers a new NPC with {@link EntityType} set as {@link Mannequin} with attributes
|
|
|
+ * @param displayName The name of the NPC
|
|
|
+ * @param textureValue The texture of the NPC
|
|
|
+ * @param textureSignature The signature of the NPC's texture
|
|
|
+ * @param location The location to spawn it in
|
|
|
+ */
|
|
|
+ public void addNPC(String displayName, String textureValue, String textureSignature, Location location ){
|
|
|
+ NPCOptions npcOptions = new NPCOptions()
|
|
|
+ .setName(displayName)
|
|
|
+ .setTexture(textureValue)
|
|
|
+ .setSignature(textureSignature)
|
|
|
+ .setLocation(location);
|
|
|
+
|
|
|
+ NPC npc = new DummyNPC(npcOptions);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Registers a new NPC
|
|
|
+ * @param npcOptions The NPC's options
|
|
|
+ */
|
|
|
+ public void addNPC(NPCOptions npcOptions){
|
|
|
+ NPC npc = new DummyNPC(npcOptions);
|
|
|
+ }
|
|
|
+
|
|
|
+ @EventHandler
|
|
|
+ public void onEntityDamageByEntityEvent(EntityDamageByEntityEvent e) {
|
|
|
+ if(!(e.getDamager() instanceof Player player)) return;
|
|
|
+
|
|
|
+ NPC npc = getNPC(e.getEntity());
|
|
|
+ if(npc == null) return;
|
|
|
+
|
|
|
+ e.setCancelled(true);
|
|
|
+ if(player.isSneaking()){
|
|
|
+ npc.onShiftLeftClick(player);
|
|
|
+ }else{
|
|
|
+ npc.onLeftClick(player);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ @EventHandler
|
|
|
+ public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent e) {
|
|
|
+ Player player = e.getPlayer();
|
|
|
+
|
|
|
+ NPC npc = getNPC(e.getRightClicked());
|
|
|
+ if(npc == null) return;
|
|
|
+
|
|
|
+ if(e.getHand() != EquipmentSlot.HAND) return;
|
|
|
+ if(player.isSneaking()){
|
|
|
+ npc.onShiftRightClick(player);
|
|
|
+ }else{
|
|
|
+ npc.onRightClick(player);
|
|
|
+ }
|
|
|
+ e.setCancelled(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ @EventHandler(ignoreCancelled = true)
|
|
|
+ public void onPlayerMove(PlayerMoveEvent event) {
|
|
|
+
|
|
|
+ // Ignore pure rotation (huge performance gain)
|
|
|
+ if (event.getFrom().getX() == event.getTo().getX()
|
|
|
+ && event.getFrom().getY() == event.getTo().getY()
|
|
|
+ && event.getFrom().getZ() == event.getTo().getZ()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Player player = event.getPlayer();
|
|
|
+ Location playerLocation = player.getEyeLocation();
|
|
|
+
|
|
|
+ for (NPC npc : npcs.values()) {
|
|
|
+ if(!npc.getOptions().isLookingAtNearest()) return;
|
|
|
+ Location npcLocation = npc.getEntity().getLocation();
|
|
|
+
|
|
|
+ // Skip different worlds immediately
|
|
|
+ if (!npcLocation.getWorld().equals(playerLocation.getWorld())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Use squared distance (NO sqrt → much faster)
|
|
|
+ if (npcLocation.distanceSquared(playerLocation) > FOCUS_RANGE_SQUARED) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Optional: Visibility check if really needed
|
|
|
+ if (!player.canSee(npc.getEntity())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ npc.lookAt(playerLocation);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- private final Cache<Player, NPC> clickedNPCCache = CacheBuilder.newBuilder()
|
|
|
- .expireAfterWrite(1L, TimeUnit.SECONDS)
|
|
|
- .build();
|
|
|
-
|
|
|
- private void handleEntityClick(Player player, int entityId, NPCClickAction action) {
|
|
|
- registeredNPCs.stream()
|
|
|
- .filter(npc -> npc.getId() == entityId)
|
|
|
- .forEach(npc -> Bukkit.getServer().getScheduler().runTaskLater(plugin, () -> {
|
|
|
- /*NPC previouslyClickedNPC = clickedNPCCache.getIfPresent(player);
|
|
|
- if (previouslyClickedNPC != null && previouslyClickedNPC.equals(npc)) return; // If they've clicked this same NPC in the last 0.5 seconds ignore this click
|
|
|
- clickedNPCCache.put(player, npc);*/
|
|
|
-
|
|
|
- NPCInteractionEvent event = new NPCInteractionEvent(npc, player, action);
|
|
|
- Bukkit.getPluginManager().callEvent(event);
|
|
|
- }, 2));
|
|
|
+ private NPC getNPC(Entity entity){
|
|
|
+ UUID entityUUID = entity.getUniqueId();
|
|
|
+ return npcs.getOrDefault(entityUUID, null);
|
|
|
}
|
|
|
|
|
|
- public NPC newNPC(NPCOptions options) {
|
|
|
- NPC npc = new NPC_1_21_10(options.getName(), options.getLocation());
|
|
|
- registeredNPCs.add(npc);
|
|
|
- return npc;
|
|
|
+ public void registerNPC(NPC toRegister){
|
|
|
+ npcs.put(toRegister.getEntity().getUniqueId(), toRegister);
|
|
|
}
|
|
|
|
|
|
- public Optional<NPC> findNPC(String name) {
|
|
|
- return registeredNPCs.stream()
|
|
|
- .filter(npc -> npc.getName().equalsIgnoreCase(name))
|
|
|
- .findFirst();
|
|
|
+ public NPC findByUUID(String uuid){
|
|
|
+ UUID manUUID = UUID.fromString(uuid);
|
|
|
+ return npcs.get(manUUID);
|
|
|
}
|
|
|
|
|
|
- public void deleteNPC(NPC npc) {
|
|
|
- npc.delete();
|
|
|
- registeredNPCs.remove(npc);
|
|
|
+ public Collection<UUID> getRegisteredNPCsUUID() {
|
|
|
+ return npcs.keySet();
|
|
|
}
|
|
|
|
|
|
- public void deleteAllNPCs() {
|
|
|
- // Copy the set to prevent concurrent modification exception
|
|
|
- Set<NPC> npcsCopy = new HashSet<>(registeredNPCs);
|
|
|
- npcsCopy.forEach(this::deleteNPC);
|
|
|
+ public void deleteNPCByUUIDString(String uuid) {
|
|
|
+ npcs.remove(UUID.fromString(uuid)).getEntity().remove();
|
|
|
}
|
|
|
- public NPC getNPCbyId(int id) {
|
|
|
- if(registeredNPCs.isEmpty()) return null;
|
|
|
- Optional<NPC> optionalNPC = registeredNPCs.stream().filter(npc -> npc.getId() == id).findFirst();
|
|
|
- return optionalNPC.orElse(null);
|
|
|
+
|
|
|
+ public void renameNPC(UUID entityUUID, String newName) {
|
|
|
+ npcs.get(entityUUID).rename(newName);
|
|
|
}
|
|
|
-}
|
|
|
+}
|