mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2025-01-08 01:17:41 +01:00
Add support for FancyNpcs
This commit is contained in:
parent
0961538ab1
commit
b4a802d746
24
pom.xml
24
pom.xml
@ -192,10 +192,11 @@
|
||||
<id>clojars</id>
|
||||
<url>https://repo.clojars.org/</url>
|
||||
</repository>
|
||||
<!-- Citizens -->
|
||||
<!-- FancyNPC -->
|
||||
<repository>
|
||||
<id>citizens-repo</id>
|
||||
<url>https://maven.citizensnpcs.co/repo</url>
|
||||
<id>fancyplugins-releases</id>
|
||||
<name>FancyPlugins Repository</name>
|
||||
<url>https://repo.fancyplugins.de/releases</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
@ -398,19 +399,12 @@
|
||||
<version>1.1.13</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- Citizens -->
|
||||
<dependency>
|
||||
<groupId>net.citizensnpcs</groupId>
|
||||
<artifactId>citizens-main</artifactId>
|
||||
<version>2.0.35-SNAPSHOT</version>
|
||||
<type>jar</type>
|
||||
<!-- FancyNPCs -->
|
||||
<dependency>
|
||||
<groupId>de.oliver</groupId>
|
||||
<artifactId>FancyNpcs</artifactId>
|
||||
<version>2.4.0</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@ -19,12 +19,13 @@ import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
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 @@ public class BentoBox extends JavaPlugin implements Listener {
|
||||
|
||||
hooksManager.registerHook(new VaultHook());
|
||||
|
||||
// Citizens
|
||||
hooksManager.registerHook(new CitizensHook());
|
||||
// FancyNpcs
|
||||
hooksManager.registerHook(new FancyNpcsHook());
|
||||
|
||||
// MythicMobs
|
||||
hooksManager.registerHook(new MythicMobsHook());
|
||||
|
@ -43,7 +43,7 @@ import world.bentobox.bentobox.api.user.User;
|
||||
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<Vector, BlueprintBlock> bpBlocks = new LinkedHashMap<>();
|
||||
private final BentoBox plugin = BentoBox.getInstance();
|
||||
private Optional<MythicMobsHook> mmh;
|
||||
private Optional<CitizensHook> ch;
|
||||
private Optional<FancyNpcsHook> npc;
|
||||
|
||||
/**
|
||||
* Create a clipboard for blueprint
|
||||
@ -83,7 +83,8 @@ public class BlueprintClipboard {
|
||||
|
||||
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 class BlueprintClipboard {
|
||||
|
||||
private void copyAsync(World world, User user, List<Vector> 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 @@ public class BlueprintClipboard {
|
||||
private List<BlueprintEntity> setEntities(List<Entity> ents) {
|
||||
List<BlueprintEntity> bpEnts = new ArrayList<>();
|
||||
for (Entity entity : ents) {
|
||||
BentoBox.getInstance().logDebug("Etity = " + entity);
|
||||
BlueprintEntity bpe = new BlueprintEntity();
|
||||
|
||||
bpe.setType(entity.getType());
|
||||
|
@ -24,9 +24,9 @@ import com.google.gson.annotations.Expose;
|
||||
*/
|
||||
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 class BlueprintEntity {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 + ", " : "")
|
||||
|
@ -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<String, Object> 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<Vector, List<BlueprintEntity>> getCitizensInArea(World world, List<Vector> vectorsToCopy,
|
||||
@Nullable Vector origin) {
|
||||
Map<Vector, List<BlueprintEntity>> 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<BlueprintEntity> 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
|
||||
}
|
||||
}
|
293
src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java
Normal file
293
src/main/java/world/bentobox/bentobox/hooks/FancyNpcsHook.java
Normal file
@ -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<NpcEquipmentSlot, org.bukkit.inventory.ItemStack> 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<ActionTrigger, List<NpcAction.NpcActionData>> 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", "<empty>");
|
||||
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<ActionTrigger, List<NpcAction.NpcActionData>> 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<NpcAction.NpcActionData> 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<NpcAttribute, String> 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<? extends Vector, ? extends List<BlueprintEntity>> getNpcsInArea(World world, List<Vector> vectorsToCopy,
|
||||
@Nullable Vector origin) {
|
||||
Map<Vector, List<BlueprintEntity>> 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<BlueprintEntity> 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;
|
||||
}
|
||||
}
|
@ -940,7 +940,6 @@ public class IslandsManager {
|
||||
*/
|
||||
@NonNull
|
||||
public Map<String, Location> getHomeLocations(@NonNull Island island) {
|
||||
island.getHomes().forEach((n, l) -> BentoBox.getInstance().logDebug(n));
|
||||
return island.getHomes();
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ import org.bukkit.block.data.BlockData;
|
||||
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.BlueprintBlock;
|
||||
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 class DefaultPasteUtil {
|
||||
* @return future boolean - true if Bukkit entity spawned, false another plugin entity spawned
|
||||
*/
|
||||
public static CompletableFuture<Void> setEntity(Island island, Location location, List<BlueprintEntity> 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 class DefaultPasteUtil {
|
||||
/**
|
||||
* 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)
|
||||
|
Loading…
Reference in New Issue
Block a user