diff --git a/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/src/main/java/net/citizensnpcs/commands/NPCCommands.java index 080ea1180..b0013a915 100644 --- a/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -181,12 +181,19 @@ public class NPCCommands { Paginator paginator = new Paginator().header("Anchors"); paginator.addLine("Key: ID Name World Location (X,Y,Z)"); for (int i = 0; i < trait.getAnchors().size(); i++) { - String line = "" + i + " " + trait.getAnchors().get(i).getName() + " " - + trait.getAnchors().get(i).getLocation().getWorld().getName() + " " - + trait.getAnchors().get(i).getLocation().getBlockX() + ", " - + trait.getAnchors().get(i).getLocation().getBlockY() + ", " - + trait.getAnchors().get(i).getLocation().getBlockZ(); - paginator.addLine(line); + if (trait.getAnchors().get(i).isLoaded()) { + String line = "" + i + " " + trait.getAnchors().get(i).getName() + " " + + trait.getAnchors().get(i).getLocation().getWorld().getName() + " " + + trait.getAnchors().get(i).getLocation().getBlockX() + ", " + + trait.getAnchors().get(i).getLocation().getBlockY() + ", " + + trait.getAnchors().get(i).getLocation().getBlockZ(); + paginator.addLine(line); + } else { + String[] parts = trait.getAnchors().get(i).getUnloadedValue(); + String line = "" + i + " " + trait.getAnchors().get(i).getName() + " " + + parts[0] + " " + parts[1] + ", " + parts[2] + ", " + parts[3] + " (unloaded)"; + paginator.addLine(line); + } } int page = args.getInteger(1, 1); @@ -1004,7 +1011,7 @@ public class NPCCommands { if (range > 0 && test.isSpawned() && !Util.locationWithinRange(args.getSenderLocation(), test.getBukkitEntity() - .getLocation(), range)) + .getLocation(), range)) continue; possible.add(test); } diff --git a/src/main/java/net/citizensnpcs/npc/ai/speech/Chat.java b/src/main/java/net/citizensnpcs/npc/ai/speech/Chat.java index 051b78ca8..4525bba3b 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/speech/Chat.java +++ b/src/main/java/net/citizensnpcs/npc/ai/speech/Chat.java @@ -1,5 +1,6 @@ package net.citizensnpcs.npc.ai.speech; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -61,7 +62,7 @@ public class Chat implements VocalChord { else { // Multiple recipients String text = Setting.CHAT_FORMAT_TO_TARGET.asString().replace("", npc.getName()) .replace("", context.getMessage()); - List targetNames = Collections.emptyList(); + List targetNames = new ArrayList(); // Talk to each recipient for (Talkable entity : context) { entity.talkTo(context, text, this); @@ -72,20 +73,20 @@ public class Chat implements VocalChord { return; String targets = ""; int max = Setting.CHAT_MAX_NUMBER_OF_TARGETS.asInt(); - String[] format = Setting.CHAT_FORMAT_WITH_TARGETS_TO_BYSTANDERS.asString().split("\\|"); + String[] format = Setting.CHAT_MULTIPLE_TARGETS_FORMAT.asString().split("\\|"); if (format.length != 4) - Messaging.severe("npc.chat.format.with-target-to-bystanders invalid!"); + Messaging.severe("npc.chat.options.multiple-targets-format invalid!"); if (max == 1) { - targets = format[0].replace("", targetNames.get(0)) + format[3]; + targets = format[0].replace("", targetNames.get(0)) + format[3]; } else if (max == 2 || targetNames.size() == 2) { if (targetNames.size() == 2) { - targets = format[0].replace("", targetNames.get(0)) - + format[2].replace("", targetNames.get(1)); + targets = format[0].replace("", targetNames.get(0)) + + format[2].replace("", targetNames.get(1)); } else - targets = format[0].replace("", targetNames.get(0)) - + format[1].replace("", targetNames.get(1)) + format[3]; + targets = format[0].replace("", targetNames.get(0)) + + format[1].replace("", targetNames.get(1)) + format[3]; } else if (max >= 3) { - targets = format[0].replace("", targetNames.get(0)); + targets = format[0].replace("", targetNames.get(0)); int x = 1; for (x = 1; x < max - 1; x++) { @@ -114,17 +115,20 @@ public class Chat implements VocalChord { // Continue if a LivingEntity, which is compatible with // TalkableEntity if (bystander instanceof LivingEntity) { + + boolean should_talk = true; // Exclude targeted recipients if (context.hasRecipients()) { for (Talkable target : context) - if (target.getEntity() == bystander) - continue; - else - new TalkableEntity((LivingEntity) bystander).talkNear(context, text, this); - } else - // Found a nearby LivingEntity, make it Talkable and - // talkNear it + if (target.getEntity().equals(bystander)) + should_talk = false; + } + + // Found a nearby LivingEntity, make it Talkable and + // talkNear it if 'should_talk' + if (should_talk) new TalkableEntity((LivingEntity) bystander).talkNear(context, text, this); + } } diff --git a/src/main/java/net/citizensnpcs/trait/Anchors.java b/src/main/java/net/citizensnpcs/trait/Anchors.java index 9dd8ab50d..b3918a2dc 100644 --- a/src/main/java/net/citizensnpcs/trait/Anchors.java +++ b/src/main/java/net/citizensnpcs/trait/Anchors.java @@ -12,9 +12,13 @@ import net.citizensnpcs.util.Messages; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldUnloadEvent; public class Anchors extends Trait { - private final List anchors = new ArrayList(); + private List anchors = new ArrayList(); public Anchors() { super("anchors"); @@ -41,14 +45,20 @@ public class Anchors extends Trait { @Override public void load(DataKey key) throws NPCLoadException { - for (DataKey sub : key.getRelative("list").getIntegerSubKeys()) + for (DataKey sub : key.getRelative("list").getIntegerSubKeys()) { + String[] parts = sub.getString("").split(";"); + Location location; try { - String[] parts = sub.getString("").split(";"); - anchors.add(new Anchor(parts[0], new Location(Bukkit.getServer().getWorld(parts[1]), Double - .valueOf(parts[2]), Double.valueOf(parts[3]), Double.valueOf(parts[4])))); + location = new Location(Bukkit.getServer().getWorld(parts[1]), Double + .valueOf(parts[2]), Double.valueOf(parts[3]), Double.valueOf(parts[4])); + anchors.add(new Anchor(parts[0], location)); } catch (NumberFormatException e) { Messaging.logTr(Messages.SKIPPING_INVALID_ANCHOR, sub.name(), e.getMessage()); + } catch (NullPointerException e) { + // Invalid world/location/etc. Still enough data to build an unloaded anchor + anchors.add(new Anchor(parts[0], sub.getString("").split(";", 2)[1])); } + } } public boolean removeAnchor(Anchor anchor) { @@ -65,4 +75,12 @@ public class Anchors extends Trait { for (int i = 0; i < anchors.size(); i++) key.setString("list." + String.valueOf(i), anchors.get(i).stringValue()); } + + @EventHandler + public void checkWorld(WorldLoadEvent event) { + for (Anchor anchor : anchors) + if (!anchor.isLoaded()) + anchor.load(); + } + } diff --git a/src/main/java/net/citizensnpcs/util/Anchor.java b/src/main/java/net/citizensnpcs/util/Anchor.java index 3cd716318..21f1d7d0a 100644 --- a/src/main/java/net/citizensnpcs/util/Anchor.java +++ b/src/main/java/net/citizensnpcs/util/Anchor.java @@ -2,6 +2,7 @@ package net.citizensnpcs.util; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; +import org.bukkit.Bukkit; import org.bukkit.Location; /* @@ -9,12 +10,39 @@ import org.bukkit.Location; */ public class Anchor { - private final Location location; + private Location location; private final String name; + // Needed for Anchors defined that can't currently have a valid 'Location' + private final String unloaded_value; + + // Allow construction of anchor for unloaded worlds + public Anchor(String name, String unloaded_value) { + this.location = null; + this.unloaded_value = unloaded_value; + this.name = name; + } + public Anchor(String name, Location location) { this.location = location; this.name = name; + this.unloaded_value = location.getWorld().getName() + ';' + + location.getX() + ';' + location.getY() + ';' + location.getZ(); + } + + public boolean isLoaded() { + return location != null; + } + + public boolean load() { + try { + String[] parts = getUnloadedValue(); + this.location = new Location(Bukkit.getWorld(parts[0]), + Double.valueOf(parts[1]), Double.valueOf(parts[2]), Double.valueOf(parts[3])); + } catch (Exception e) { + // Still not able to be loaded + } + return location != null; } @Override @@ -27,7 +55,7 @@ public class Anchor { return false; Anchor op = (Anchor) object; - return new EqualsBuilder().append(name, op.getName()).isEquals(); + return new EqualsBuilder().append(name, op.name).isEquals(); } public Location getLocation() { @@ -38,20 +66,30 @@ public class Anchor { return name; } + /** + * Returns a String[] of the 'world_name, x, y, z' information needed to create the Location + * that is associated with the Anchor, in that order. + * + * @return a String array of the anchor's location data + */ + public String[] getUnloadedValue() { + return unloaded_value.split(";"); + } + @Override public int hashCode() { return new HashCodeBuilder(13, 21).append(name).toHashCode(); } + // A friendly representation for use in saves.yml public String stringValue() { - return name + ";" + location.getWorld().getName() + ";" + location.getX() + ";" + location.getY() + ";" - + location.getZ(); + return name + ';' + unloaded_value; } @Override public String toString() { - return "Name: " + name + " World: " + location.getWorld().getName() + " Location: " + location.getBlockX() - + "," + location.getBlockY() + "," + location.getBlockZ(); + String[] parts = getUnloadedValue(); + return "Anchor{Name='" + name + "';World='" + parts[0] + "';Location='" + parts[1] + ',' + parts[2] + ',' + parts[3] + "';}"; } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/util/Pose.java b/src/main/java/net/citizensnpcs/util/Pose.java index af7c75a98..ccfffa29a 100644 --- a/src/main/java/net/citizensnpcs/util/Pose.java +++ b/src/main/java/net/citizensnpcs/util/Pose.java @@ -49,12 +49,12 @@ public class Pose { } public String stringValue() { - return name + ";" + pitch + ";" + yaw; + return name + ';' + pitch + ';' + yaw; } @Override public String toString() { - return "Name: " + name + " Pitch: " + pitch + " Yaw: " + yaw; + return "Pose{Name='" + name + "';Pitch='" + pitch + "';Yaw='" + yaw + "';}"; } } \ No newline at end of file