From b4a802d746b3b5c841e1df24579a84754d005e5d Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 7 Dec 2024 13:02:25 -0800 Subject: [PATCH] Add support for FancyNpcs --- pom.xml | 24 +- .../world/bentobox/bentobox/BentoBox.java | 7 +- .../blueprints/BlueprintClipboard.java | 14 +- .../dataobjects/BlueprintEntity.java | 16 +- .../bentobox/bentobox/hooks/CitizensHook.java | 215 ------------- .../bentobox/hooks/FancyNpcsHook.java | 293 ++++++++++++++++++ .../bentobox/managers/IslandsManager.java | 1 - .../bentobox/util/DefaultPasteUtil.java | 27 +- 8 files changed, 336 insertions(+), 261 deletions(-) delete mode 100644 src/main/java/world/bentobox/bentobox/hooks/CitizensHook.java create mode 100644 src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java diff --git a/pom.xml b/pom.xml index 845415559..c8ed60cb8 100644 --- a/pom.xml +++ b/pom.xml @@ -192,10 +192,11 @@ clojars https://repo.clojars.org/ - + - citizens-repo - https://maven.citizensnpcs.co/repo + fancyplugins-releases + FancyPlugins Repository + https://repo.fancyplugins.de/releases @@ -398,19 +399,12 @@ 1.1.13 compile - - - net.citizensnpcs - citizens-main - 2.0.35-SNAPSHOT - jar + + + de.oliver + FancyNpcs + 2.4.0 provided - - - * - * - - diff --git a/src/main/java/world/bentobox/bentobox/BentoBox.java b/src/main/java/world/bentobox/bentobox/BentoBox.java index ae4f06a60..ff315d7de 100644 --- a/src/main/java/world/bentobox/bentobox/BentoBox.java +++ b/src/main/java/world/bentobox/bentobox/BentoBox.java @@ -19,12 +19,13 @@ import world.bentobox.bentobox.api.configuration.Config; import world.bentobox.bentobox.api.events.BentoBoxReadyEvent; +import world.bentobox.bentobox.api.hooks.Hook; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.Notifier; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.commands.BentoBoxCommand; import world.bentobox.bentobox.database.DatabaseSetup; -import world.bentobox.bentobox.hooks.CitizensHook; +import world.bentobox.bentobox.hooks.FancyNpcsHook; import world.bentobox.bentobox.hooks.ItemsAdderHook; import world.bentobox.bentobox.hooks.MultipaperHook; import world.bentobox.bentobox.hooks.MultiverseCoreHook; @@ -193,8 +194,8 @@ private void completeSetup(long loadTime) { hooksManager.registerHook(new VaultHook()); - // Citizens - hooksManager.registerHook(new CitizensHook()); + // FancyNpcs + hooksManager.registerHook(new FancyNpcsHook()); // MythicMobs hooksManager.registerHook(new MythicMobsHook()); diff --git a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java index 315ef66e8..dc10da175 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/BlueprintClipboard.java @@ -43,7 +43,7 @@ import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; -import world.bentobox.bentobox.hooks.CitizensHook; +import world.bentobox.bentobox.hooks.FancyNpcsHook; import world.bentobox.bentobox.hooks.MythicMobsHook; /** @@ -70,7 +70,7 @@ public class BlueprintClipboard { private final Map bpBlocks = new LinkedHashMap<>(); private final BentoBox plugin = BentoBox.getInstance(); private Optional mmh; - private Optional ch; + private Optional npc; /** * Create a clipboard for blueprint @@ -83,7 +83,8 @@ public BlueprintClipboard(@NonNull Blueprint blueprint) { public BlueprintClipboard() { // Citizens Hook - ch = plugin.getHooks().getHook("Citizens").filter(CitizensHook.class::isInstance).map(CitizensHook.class::cast); + npc = plugin.getHooks().getHook("FancyNpcs").filter(FancyNpcsHook.class::isInstance) + .map(FancyNpcsHook.class::cast); // MythicMobs Hook mmh = plugin.getHooks().getHook("MythicMobs").filter(MythicMobsHook.class::isInstance) .map(MythicMobsHook.class::cast); @@ -137,10 +138,10 @@ public boolean copy(User user, boolean copyAir, boolean copyBiome) { private void copyAsync(World world, User user, List vectorsToCopy, int speed, boolean copyAir, boolean copyBiome) { copying = false; - // Citizens - if (ch.isPresent()) { + // FancyNpcs + if (npc.isPresent()) { // Add all the citizens for the area in one go. This is pretty fast. - bpEntities.putAll(ch.get().getCitizensInArea(world, vectorsToCopy, origin)); + bpEntities.putAll(npc.get().getNpcsInArea(world, vectorsToCopy, origin)); } // Repeating copy task @@ -308,7 +309,6 @@ private BlueprintCreatureSpawner getSpawner(CreatureSpawner spawner) { private List setEntities(List ents) { List bpEnts = new ArrayList<>(); for (Entity entity : ents) { - BentoBox.getInstance().logDebug("Etity = " + entity); BlueprintEntity bpe = new BlueprintEntity(); bpe.setType(entity.getType()); diff --git a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java index 3c1e1959a..2ce285fee 100644 --- a/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java +++ b/src/main/java/world/bentobox/bentobox/blueprints/dataobjects/BlueprintEntity.java @@ -24,9 +24,9 @@ */ public class BlueprintEntity { - // Citizens storage + // Npc storage @Expose - private String citizen; + private String npc; // MythicMobs storage public record MythicMobRecord(String type, String displayName, double level, float power, String stance) { @@ -310,22 +310,22 @@ public void setMythicMobsRecord(MythicMobRecord mmr) { } /** - * @return the citizen + * @return the npc */ - public String getCitizen() { - return citizen; + public String getNpc() { + return npc; } /** * @param citizen the citizen to set */ - public void setCitizen(String citizen) { - this.citizen = citizen; + public void setNpc(String citizen) { + this.npc = citizen; } @Override public String toString() { - return "BlueprintEntity [" + (citizen != null ? "citizen=" + citizen + ", " : "") + return "BlueprintEntity [" + (npc != null ? "npc=" + npc + ", " : "") + (MMtype != null ? "MMtype=" + MMtype + ", " : "") + (MMLevel != null ? "MMLevel=" + MMLevel + ", " : "") + (MMStance != null ? "MMStance=" + MMStance + ", " : "") diff --git a/src/main/java/world/bentobox/bentobox/hooks/CitizensHook.java b/src/main/java/world/bentobox/bentobox/hooks/CitizensHook.java deleted file mode 100644 index e7d295f4e..000000000 --- a/src/main/java/world/bentobox/bentobox/hooks/CitizensHook.java +++ /dev/null @@ -1,215 +0,0 @@ -package world.bentobox.bentobox.hooks; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; - -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.MemorySection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.EntityType; -import org.bukkit.util.Vector; -import org.eclipse.jdt.annotation.Nullable; - -import net.citizensnpcs.api.CitizensAPI; -import net.citizensnpcs.api.npc.NPC; -import net.citizensnpcs.api.npc.NPCRegistry; -import net.citizensnpcs.api.util.DataKey; -import net.citizensnpcs.api.util.MemoryDataKey; -import world.bentobox.bentobox.BentoBox; -import world.bentobox.bentobox.api.hooks.Hook; -import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; - -/** - * Provides copy and pasting of Citizens in blueprints - * - * @author tastybento - * @since 3.1.0 - */ -public class CitizensHook extends Hook { - - public CitizensHook() { - super("Citizens", Material.PLAYER_HEAD); - } - - public static DataKey toDataKey(String serializedData) { - DataKey dataKey = new MemoryDataKey(); - YamlConfiguration y = new YamlConfiguration(); - try { - y.loadFromString(serializedData); - } catch (InvalidConfigurationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - insertIntoDataKey(dataKey, "", y); - return dataKey; - } - - private static void insertIntoDataKey(DataKey dataKey, String parentPath, ConfigurationSection config2) { - // Manually set the keys and see where it breaks - dataKey.setString("name", "bentest"); - dataKey.setString("uuid", UUID.randomUUID().toString()); - dataKey.setString("traits.type", "PLAYER"); - dataKey.setDouble("traits.location.bodyYaw", -111.14996); - dataKey.setDouble("traits.location.x", 0.0); - dataKey.setDouble("traits.location.y", 0.0); - dataKey.setDouble("traits.location.z", 0.0); - dataKey.setDouble("traits.location.yaw", -111.15); - dataKey.setDouble("traits.location.pitch", 42.0); - dataKey.setBoolean("spawned", true); - dataKey.setString("traitnames", "type,location,spawned"); - dataKey.setString("metadata.cached-skin-uuid", "f4579458-a42d-431c-bbec-f7183536f633"); - dataKey.setString("skintrait.signature", - "QBxcn0hclFKgSvGXjivL5W6F43uPuUFQgrvumsIPZEpdzR+LKXgl+OCfdoFCvjF303mDcpvMPcAJ9XEcbA/JLbeGkcfUupw326vjsz422lABA8Uys8yR/3lKD+KXfmvtpqiuOphMLvE21vVZQb0uP9g+1XgFO6puttcB3vGmenIM7jFE3uyQ8ma44VMqv/QBz8RHCw6jn+HsuIqS/VBQ/wv+/FVDOYd+qq4nbIXEyfZK/mvRlq4+AaTskxL4N6OkKqb1mREvmyZLbjFpoWTAmnPUHpUqc3yuky+v63mUpah7uEGwfO3FymBkSrAxgBSs1rASst9nS8M3icEBft+ea+roYBH1DLz4QNDKSIINFcpejPWHzLkCY20EW0Dn0Eaam5+Ps16aBPQ55bFX+ztrqSrRuVsFB0SuyxpXu6tA7OF28umJ+tn8345HxyjGvV84gjwzwAn+FrBrskxnSwPxnIHffht1W0m00e+8+ykKla2/J//66A352TZEIkVIfrKh8X7x/A6Y1UGItSDaOEA51Dna1OMZzsYJ7u0cBc4k7XIzJtGucVoV9tMzvhq3vmyTVwD6GEEMtPXhl3jXmklkAB4MIS1Of49tva2/KwPndmOXn2kFMEMzADtpOapIjCtR1y2uWmG9QWSyRY0bEma9dkCgZcPj56xoxcGHwaznm6s="); - dataKey.setString("skintrait.textureRaw", - "ewogICJ0aW1lc3RhbXAiIDogMTczMjgyMDQ1ODU0MCwKICAicHJvZmlsZUlkIiA6ICJmNDU3OTQ1OGE0MmQ0MzFjYmJlY2Y3MTgzNTM2ZjYzMyIsCiAgInByb2ZpbGVOYW1lIiA6ICJCZW4iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2FiODM4NThlYmM4ZWU4NWMzZTU0YWIxM2FhYmZjYzFlZjJhZDQ0NmQ2YTkwMGU0NzFjM2YzM2I3ODkwNmE1YiIKICAgIH0KICB9Cn0="); - - /* - * metadata: - cached-skin-uuid: f4579458-a42d-431c-bbec-f7183536f633 - cached-skin-uuid-name: ben - name: ben - uuid: 93837773-5f4b-4337-8f2f-96a85c9665eb - traits: - type: PLAYER - location: - bodyYaw: -111.14996 - world: bskyblock_world - x: -1.2024 - y: 85.0 - z: -1.2274 - yaw: -111.15 - pitch: 42.0 - spawned: true - skintrait: - signature: QBxcn0hclFKgSvGXjivL5W6F43uPuUFQgrvumsIPZEpdzR+LKXgl+OCfdoFCvjF303mDcpvMPcAJ9XEcbA/JLbeGkcfUupw326vjsz422lABA8Uys8yR/3lKD+KXfmvtpqiuOphMLvE21vVZQb0uP9g+1XgFO6puttcB3vGmenIM7jFE3uyQ8ma44VMqv/QBz8RHCw6jn+HsuIqS/VBQ/wv+/FVDOYd+qq4nbIXEyfZK/mvRlq4+AaTskxL4N6OkKqb1mREvmyZLbjFpoWTAmnPUHpUqc3yuky+v63mUpah7uEGwfO3FymBkSrAxgBSs1rASst9nS8M3icEBft+ea+roYBH1DLz4QNDKSIINFcpejPWHzLkCY20EW0Dn0Eaam5+Ps16aBPQ55bFX+ztrqSrRuVsFB0SuyxpXu6tA7OF28umJ+tn8345HxyjGvV84gjwzwAn+FrBrskxnSwPxnIHffht1W0m00e+8+ykKla2/J//66A352TZEIkVIfrKh8X7x/A6Y1UGItSDaOEA51Dna1OMZzsYJ7u0cBc4k7XIzJtGucVoV9tMzvhq3vmyTVwD6GEEMtPXhl3jXmklkAB4MIS1Of49tva2/KwPndmOXn2kFMEMzADtpOapIjCtR1y2uWmG9QWSyRY0bEma9dkCgZcPj56xoxcGHwaznm6s= - textureRaw: ewogICJ0aW1lc3RhbXAiIDogMTczMjgyMDQ1ODU0MCwKICAicHJvZmlsZUlkIiA6ICJmNDU3OTQ1OGE0MmQ0MzFjYmJlY2Y3MTgzNTM2ZjYzMyIsCiAgInByb2ZpbGVOYW1lIiA6ICJCZW4iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2FiODM4NThlYmM4ZWU4NWMzZTU0YWIxM2FhYmZjYzFlZjJhZDQ0NmQ2YTkwMGU0NzFjM2YzM2I3ODkwNmE1YiIKICAgIH0KICB9Cn0= - owner: - uuid: 36b2293a-ac0a-4f73-bc35-b12af97bee2b - traitnames: type,scoreboardtrait,location,spawned,skintrait,inventory,owner - navigator: - speedmodifier: 1.0 - avoidwater: false - usedefaultstuckaction: false - */ - - config2.getKeys(true).forEach(key -> { - if (key.contains("metadatahdhdhdhdhhd")) { - String path = parentPath.isEmpty() ? key : (parentPath + "." + key); - Object value = config2.get(key); - if (value instanceof MemorySection config) { - insertIntoDataKey(dataKey, path, config); - } else { - BentoBox.getInstance().logDebug("Setting: " + path + " to " + value); - dataKey.setRaw(path, value); - } - } - }); - } - - - public String serializeNPC(NPC npc) { - if (npc == null) { - throw new IllegalArgumentException("NPC cannot be null."); - } - - MemoryDataKey dataKey = new MemoryDataKey(); - npc.save(dataKey); - // Convert MemoryDataKey to a YAML string - YamlConfiguration yaml = new YamlConfiguration(); - for (Entry en : dataKey.getValuesDeep().entrySet()) { - BentoBox.getInstance().logDebug("Serial key = " + en.getKey() + " = " + en.getValue()); - yaml.set(en.getKey(), en.getValue()); - } - return yaml.saveToString(); - } - - /** - * Get a map of serialized Citizens that are in a set of locations - * @param world world where this occurs - * @param vectorsToCopy list of locations in that world as vectors - * @param origin - * @return map - */ - public Map> getCitizensInArea(World world, List vectorsToCopy, - @Nullable Vector origin) { - Map> bpEntities = new HashMap<>(); - for (NPC npc : CitizensAPI.getNPCRegistry()) { - if (npc.isSpawned()) { - Location npcLocation = npc.getEntity().getLocation(); - Vector spot = new Vector(npcLocation.getBlockX(), npcLocation.getBlockY(), npcLocation.getBlockZ()); - if (npcLocation.getWorld().equals(world) && vectorsToCopy.contains(spot)) { - BlueprintEntity cit = new BlueprintEntity(); - cit.setType(npc.getEntity().getType()); // Must be set to be pasted - cit.setCitizen(serializeNPC(npc)); - // Retrieve or create the list, then add the entity - List entities = bpEntities.getOrDefault(spot, new ArrayList<>()); - entities.add(cit); - // Create position - Vector origin2 = origin == null ? new Vector(0, 0, 0) : origin; - int x = spot.getBlockX() - origin2.getBlockX(); - int y = spot.getBlockY() - origin2.getBlockY(); - int z = spot.getBlockZ() - origin2.getBlockZ(); - Vector pos = new Vector(x, y, z); - // Store - bpEntities.put(pos, entities); // Update the map - } - } - } - return bpEntities; - } - - /** - * Spawn a Citizen - * @param serializedData serialized data - * @param location location - * @return true if spawn is successful - */ - public boolean spawnCitizen(EntityType type, String serializedData, Location location) { - BentoBox.getInstance().logDebug("spawn Citizen at " + location + " with this data " + serializedData); - if (serializedData == null || location == null) { - throw new IllegalArgumentException("Serialized data and location cannot be null."); - } - - // Load the serialized data into a YamlConfiguration - DataKey npcDataKey = toDataKey(serializedData); - NPCRegistry registry = CitizensAPI.getNPCRegistry(); - try { - String mobTypeName = npcDataKey.getString("traits.type", EntityType.PLAYER.name()); - String name = npcDataKey.getString("name", ""); - // TODO This does not take any (version-specific) mob type migrations into account (e.g. - // for pig zombies). However, these migrations are currently also broken in Citizens - // itself (SimpleNPCDataStore does not account for mob type migrations either). - EntityType mobType = EntityType.valueOf(mobTypeName); - NPC npc = registry.createNPC(mobType, name); - npc.setBukkitEntityType(EntityType.valueOf(mobTypeName)); - npc.load(npcDataKey); // Load the serialized data into the NPC - return npc.spawn(location); - } catch (Exception e) { - e.printStackTrace(); - } - return false; - } - - - @Override - public boolean hook() { - boolean hooked = this.isPluginAvailable(); - if (!hooked) { - BentoBox.getInstance().logError("Could not hook into Citizens"); - } - return hooked; // The hook process shouldn't fail - } - - @Override - public String getFailureCause() { - return null; // The hook process shouldn't fail - } -} diff --git a/src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java b/src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java new file mode 100644 index 000000000..acc5ba7d8 --- /dev/null +++ b/src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java @@ -0,0 +1,293 @@ +package world.bentobox.bentobox.hooks; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; +import org.eclipse.jdt.annotation.Nullable; + +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import de.oliver.fancynpcs.api.Npc; +import de.oliver.fancynpcs.api.NpcAttribute; +import de.oliver.fancynpcs.api.NpcData; +import de.oliver.fancynpcs.api.actions.ActionTrigger; +import de.oliver.fancynpcs.api.actions.NpcAction; +import de.oliver.fancynpcs.api.utils.NpcEquipmentSlot; +import de.oliver.fancynpcs.api.utils.SkinFetcher; +import net.kyori.adventure.text.format.NamedTextColor; +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.hooks.Hook; +import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; + +/** + * Provides copy and pasting of FancyNPCs in blueprints + * + * @author tastybento + * @since 3.1.0 + */ +public class FancyNpcsHook extends Hook { + + public FancyNpcsHook() { + super("FancyNpcs", Material.PLAYER_HEAD); + } + + public String serializeNPC(Npc npc, Vector origin) { + if (npc == null) { + throw new IllegalArgumentException("NPC cannot be null."); + } + YamlConfiguration npcConfig = new YamlConfiguration(); + NpcData data = npc.getData(); + npcConfig.set("name", data.getName()); // Stored just for reference + npcConfig.set("creator", data.getCreator().toString()); + npcConfig.set("displayName", data.getDisplayName()); + npcConfig.set("type", data.getType().name()); + npcConfig.set("location.world", data.getLocation().getWorld().getName()); // This will not be used + // Location is stored relative to the origin, and just stored for reference. x,y,z are not used + npcConfig.set("location.x", data.getLocation().getX() - origin.getBlockX()); + npcConfig.set("location.y", data.getLocation().getY() - origin.getBlockY()); + npcConfig.set("location.z", data.getLocation().getZ() - origin.getBlockZ()); + // Only yaw and pitch are used + npcConfig.set("location.yaw", data.getLocation().getYaw()); + npcConfig.set("location.pitch", data.getLocation().getPitch()); + npcConfig.set("showInTab", data.isShowInTab()); + npcConfig.set("spawnEntity", data.isSpawnEntity()); + npcConfig.set("collidable", data.isCollidable()); + npcConfig.set("glowing", data.isGlowing()); + npcConfig.set("glowingColor", data.getGlowingColor().toString()); + npcConfig.set("turnToPlayer", data.isTurnToPlayer()); + npcConfig.set("messages", null); + npcConfig.set("playerCommands", null); + npcConfig.set("serverCommands", null); + npcConfig.set("sendMessagesRandomly", null); + npcConfig.set("interactionCooldown", data.getInteractionCooldown()); + npcConfig.set("scale", data.getScale()); + + if (data.getSkin() != null) { + npcConfig.set("skin.identifier", data.getSkin().identifier()); + } else { + npcConfig.set("skin.identifier", null); + } + npcConfig.set("skin.mirrorSkin", data.isMirrorSkin()); + + if (data.getEquipment() != null) { + for (Entry entry : data.getEquipment().entrySet()) { + npcConfig.set("equipment." + entry.getKey().name(), entry.getValue()); + } + } + + for (NpcAttribute attribute : FancyNpcsPlugin.get().getAttributeManager() + .getAllAttributesForEntityType(data.getType())) { + String value = data.getAttributes().getOrDefault(attribute, null); + npcConfig.set("attributes." + attribute.getName(), value); + } + + npcConfig.set("actions", null); + for (Map.Entry> entry : npc.getData().getActions().entrySet()) { + for (NpcAction.NpcActionData actionData : entry.getValue()) { + if (actionData == null) { + continue; + } + + npcConfig.set("actions." + entry.getKey().name() + "." + actionData.order() + ".action", + actionData.action().getName()); + npcConfig.set("actions." + entry.getKey().name() + "." + actionData.order() + ".value", + actionData.value()); + } + } + + return npcConfig.saveToString(); + } + + public boolean spawnNpc(String yaml, Location pos) throws InvalidConfigurationException { + YamlConfiguration npcConfig = new YamlConfiguration(); + npcConfig.loadFromString(yaml); + + String name = UUID.randomUUID().toString(); // Create a unique name + + UUID creator = UUID.randomUUID(); // Random creator + + String displayName = npcConfig.getString("displayName", ""); + EntityType type = EntityType.valueOf(npcConfig.getString("type", "PLAYER").toUpperCase(Locale.ENGLISH)); + + // Create the spawn location + Location location = null; + double x = pos.getBlockX(); + double y = pos.getBlockY(); + double z = pos.getBlockZ(); + // Add in the yaw and pitch + float yaw = (float) npcConfig.getDouble("location.yaw"); + float pitch = (float) npcConfig.getDouble("location.pitch"); + + location = new Location(pos.getWorld(), x, y, z, yaw, pitch); + + + String skinIdentifier = npcConfig.getString("skin.identifier", npcConfig.getString("skin.uuid", "")); + SkinFetcher.SkinData skin = null; + if (!skinIdentifier.isEmpty()) { + skin = new SkinFetcher.SkinData(skinIdentifier, "", ""); + } + + if (npcConfig.isSet("skin.value") && npcConfig.isSet("skin.signature")) { + + String value = npcConfig.getString("skin.value"); + String signature = npcConfig.getString("skin.signature"); + + if (value != null && !value.isEmpty() && signature != null && !signature.isEmpty()) { + skin = new SkinFetcher.SkinData(skinIdentifier, value, signature); + SkinFetcher.SkinData oldSkinData = new SkinFetcher.SkinData(skinIdentifier, value, signature); + SkinFetcher.skinCache.put(skinIdentifier, oldSkinData); + FancyNpcsPlugin.get().getSkinCache().upsert(new SkinFetcher.SkinCacheData(oldSkinData, + System.currentTimeMillis(), 1000 * 60 * 60 * 24)); + } + } + + boolean mirrorSkin = npcConfig.getBoolean("skin.mirrorSkin"); + + boolean showInTab = npcConfig.getBoolean("showInTab"); + boolean spawnEntity = npcConfig.getBoolean("spawnEntity"); + boolean collidable = npcConfig.getBoolean("collidable", true); + boolean glowing = npcConfig.getBoolean("glowing"); + NamedTextColor glowingColor = NamedTextColor.NAMES + .value(npcConfig.getString("glowingColor", "white")); + boolean turnToPlayer = npcConfig.getBoolean("turnToPlayer"); + + Map> actions = new ConcurrentHashMap<>(); + + ConfigurationSection actiontriggerSection = npcConfig.getConfigurationSection("actions"); + if (actiontriggerSection != null) { + actiontriggerSection.getKeys(false).forEach(trigger -> { + ActionTrigger actionTrigger = ActionTrigger.getByName(trigger); + if (actionTrigger == null) { + BentoBox.getInstance().logWarning("Could not find action trigger: " + trigger); + return; + } + + List actionList = new ArrayList<>(); + ConfigurationSection actionsSection = npcConfig.getConfigurationSection("actions." + trigger); + if (actionsSection != null) { + actionsSection.getKeys(false).forEach(order -> { + String actionName = npcConfig + .getString("actions." + trigger + "." + order + ".action"); + String value = npcConfig.getString("actions." + trigger + "." + order + ".value"); + NpcAction action = FancyNpcsPlugin.get().getActionManager().getActionByName(actionName); + if (action == null) { + BentoBox.getInstance().logWarning("Could not find action: " + actionName); + return; + } + + try { + actionList.add(new NpcAction.NpcActionData(Integer.parseInt(order), action, value)); + } catch (NumberFormatException e) { + BentoBox.getInstance().logWarning("Could not parse order: " + order); + } + }); + + actions.put(actionTrigger, actionList); + } + }); + } + + float interactionCooldown = (float) npcConfig.getDouble("interactionCooldown", 0); + float scale = (float) npcConfig.getDouble("scale", 1); + + Map attributes = new HashMap<>(); + if (npcConfig.isConfigurationSection("attributes")) { + for (String attrName : npcConfig.getConfigurationSection("attributes").getKeys(false)) { + NpcAttribute attribute = FancyNpcsPlugin.get().getAttributeManager().getAttributeByName(type, + attrName); + if (attribute == null) { + BentoBox.getInstance().logWarning("Could not find attribute: " + attrName); + continue; + } + + String value = npcConfig.getString("attributes." + attrName); + if (!attribute.isValidValue(value)) { + BentoBox.getInstance().logWarning("Invalid value for attribute: " + attrName); + continue; + } + + attributes.put(attribute, value); + } + } + + FancyNpcsPlugin.get().getNpcManager().getNpc(name); + + // When we make a copy, we need to use a new ID + String newId = UUID.randomUUID().toString(); + NpcData data = new NpcData(newId, name, creator, displayName, skin, location, showInTab, spawnEntity, + collidable, glowing, glowingColor, type, new HashMap<>(), turnToPlayer, null, actions, + interactionCooldown, scale, attributes, mirrorSkin); + Npc npc = FancyNpcsPlugin.get().getNpcAdapter().apply(data); + + if (npcConfig.isConfigurationSection("equipment")) { + for (String equipmentSlotStr : npcConfig.getConfigurationSection("equipment").getKeys(false)) { + NpcEquipmentSlot equipmentSlot = NpcEquipmentSlot.parse(equipmentSlotStr); + ItemStack item = npcConfig.getItemStack("equipment." + equipmentSlotStr); + npc.getData().addEquipment(equipmentSlot, item); + } + } + + Bukkit.getScheduler().runTask(getPlugin(), () -> { + FancyNpcsPlugin.get().getNpcManager().registerNpc(npc); + npc.create(); + npc.spawnForAll(); + }); + + return true; + } + + @Override + public boolean hook() { + boolean hooked = this.isPluginAvailable(); + if (!hooked) { + BentoBox.getInstance().logError("Could not hook into FancyNpcs"); + } + return hooked; // The hook process shouldn't fail + } + + @Override + public String getFailureCause() { + return null; // The hook process shouldn't fail + } + + public Map> getNpcsInArea(World world, List vectorsToCopy, + @Nullable Vector origin) { + Map> bpEntities = new HashMap<>(); + for (Npc npc : FancyNpcsPlugin.get().getNpcManager().getAllNpcs()) { + Location npcLocation = npc.getData().getLocation(); + Vector spot = new Vector(npcLocation.getBlockX(), npcLocation.getBlockY(), npcLocation.getBlockZ()); + if (npcLocation.getWorld().equals(world) && vectorsToCopy.contains(spot)) { + BlueprintEntity cit = new BlueprintEntity(); + cit.setType(npc.getData().getType()); + cit.setNpc(this.serializeNPC(npc, origin)); + // Retrieve or create the list, then add the entity + List entities = bpEntities.getOrDefault(spot, new ArrayList<>()); + entities.add(cit); + // Create position + Vector origin2 = origin == null ? new Vector(0, 0, 0) : origin; + int x = spot.getBlockX() - origin2.getBlockX(); + int y = spot.getBlockY() - origin2.getBlockY(); + int z = spot.getBlockZ() - origin2.getBlockZ(); + Vector pos = new Vector(x, y, z); + // Store + bpEntities.put(pos, entities); // Update the map + } + } + return bpEntities; + } +} diff --git a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java index 681fd69e7..53c25b4ba 100644 --- a/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java +++ b/src/main/java/world/bentobox/bentobox/managers/IslandsManager.java @@ -940,7 +940,6 @@ public boolean renameHomeLocation(@NonNull Island island, @NonNull String oldNam */ @NonNull public Map getHomeLocations(@NonNull Island island) { - island.getHomes().forEach((n, l) -> BentoBox.getInstance().logDebug(n)); return island.getHomes(); } diff --git a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java index c22ba05fe..86ee2e070 100644 --- a/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java +++ b/src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java @@ -21,6 +21,7 @@ import org.bukkit.block.data.type.WallSign; import org.bukkit.block.sign.Side; import org.bukkit.block.sign.SignSide; +import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; @@ -33,7 +34,7 @@ import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner; import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity; import world.bentobox.bentobox.database.objects.Island; -import world.bentobox.bentobox.hooks.CitizensHook; +import world.bentobox.bentobox.hooks.FancyNpcsHook; import world.bentobox.bentobox.hooks.MythicMobsHook; import world.bentobox.bentobox.nms.PasteHandler; @@ -173,8 +174,6 @@ public static void setSpawner(CreatureSpawner spawner, BlueprintCreatureSpawner * @return future boolean - true if Bukkit entity spawned, false another plugin entity spawned */ public static CompletableFuture setEntity(Island island, Location location, List list) { - BentoBox.getInstance().logDebug("List of entities to paste at " + location); - list.forEach(bpe -> BentoBox.getInstance().logDebug(bpe)); World world = location.getWorld(); assert world != null; return Util.getChunkAtAsync(location).thenRun(() -> list.stream().filter(k -> k.getType() != null) @@ -184,21 +183,25 @@ public static CompletableFuture setEntity(Island island, Location location /** * Spawn an entity * @param k the blueprint entity definition - * @param location location + * @param location location to paste the entity * @param island island * @return true if Bukkit entity spawned, false another plugin entity spawned */ static boolean spawnBlueprintEntity(BlueprintEntity k, Location location, Island island) { - BentoBox.getInstance().logDebug("pasting entity " + k.getType() + " at " + location); - // Citizens entity - if (k.getCitizen() != null && plugin.getHooks().getHook("Citizens").filter(mmh -> mmh instanceof CitizensHook) - .map(mmh -> ((CitizensHook) mmh).spawnCitizen(k.getType(), k.getCitizen(), location)).orElse(false)) { - BentoBox.getInstance().logDebug("Citizen spawning done"); - // Citizen has spawned. + // Npc entity + if (k.getNpc() != null + && plugin.getHooks().getHook("FancyNpcs").filter(mmh -> mmh instanceof FancyNpcsHook).map(mmh -> { + try { + return ((FancyNpcsHook) mmh).spawnNpc(k.getNpc(), location); + } catch (InvalidConfigurationException e) { + plugin.logError("FancyNpc loading failed in blueprint."); + return false; + } + }).orElse(false)) { + // Npc has spawned. return false; - } else { - BentoBox.getInstance().logDebug("Citizen spawning failed"); } + // Mythic Mobs entity if (k.getMythicMobsRecord() != null && plugin.getHooks().getHook("MythicMobs") .filter(mmh -> mmh instanceof MythicMobsHook)