diff --git a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java index eb8d158af..ba1c4ad46 100644 --- a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java +++ b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java @@ -8,6 +8,7 @@ import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPCRegistry; +import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.trait.MobType; import net.citizensnpcs.api.trait.trait.Owner; import net.citizensnpcs.api.trait.trait.Spawned; @@ -70,6 +71,10 @@ public class NPCCommands { public void age(CommandContext args, CommandSender sender, NPC npc) throws CommandException { Age trait = npc.getTrait(Age.class); + boolean toggleLock = args.hasFlag('l'); + if (toggleLock) + Messaging.send(sender, "Age " + (trait.toggle() ? "locked" : "unlocked") + "."); + if (args.argsLength() > 1) { int age = 0; String ageStr = "an adult"; @@ -77,7 +82,7 @@ public class NPCCommands { age = args.getInteger(1); if (age < -24000 || age > 0) throw new CommandException("Invalid age. Valid: adult, baby, number between -24000 and 0"); - ageStr = "age " + age; + ageStr = "age " + StringHelper.wrap(age); } catch (NumberFormatException ex) { if (args.getString(1).equalsIgnoreCase("baby")) { age = -24000; @@ -87,11 +92,9 @@ public class NPCCommands { } trait.setAge(age); - Messaging.send(sender, StringHelper.wrap(npc.getName()) + " is now " + ageStr + "."); - } - - if (args.hasFlag('l')) - Messaging.send(sender, "Age " + (trait.toggle() ? "locked" : "unlocked") + "."); + Messaging.sendF(sender, StringHelper.wrap(npc.getName()) + " is now %s.", ageStr); + } else if (!toggleLock) + trait.describe(sender); } @Command( @@ -104,19 +107,19 @@ public class NPCCommands { public void behaviour(CommandContext args, CommandSender sender, NPC npc) throws CommandException { Iterable files = Splitter.on(',').split(args.getJoinedStrings(1, ',')); if (args.hasFlag('r')) { - npc.getTrait(Behaviour.class).addScripts(files); - sender.sendMessage(ChatColor.GREEN + "Behaviours added."); - } else { npc.getTrait(Behaviour.class).removeScripts(files); sender.sendMessage(ChatColor.GREEN + "Behaviours removed."); + } else { + npc.getTrait(Behaviour.class).addScripts(files); + sender.sendMessage(ChatColor.GREEN + "Behaviours added."); } } @Command( aliases = { "npc" }, - usage = "controllable", + usage = "controllable|control", desc = "Toggles whether the NPC can be ridden and controlled", - modifiers = { "controllable" }, + modifiers = { "controllable", "control" }, min = 1, max = 1, permission = "npc.controllable") @@ -155,16 +158,15 @@ public class NPCCommands { @Command( aliases = { "npc" }, - usage = "create [name] ((-b) --type (type) --char (char) --behaviour (behaviour))", + usage = "create [name] ((-b) --type (type) --trait ('trait1, trait2...') --b (behaviour))", desc = "Create a new NPC", flags = "b", modifiers = { "create" }, min = 2, - max = 5, permission = "npc.create") @Requirements public void create(CommandContext args, final Player player, NPC npc) { - String name = args.getString(1); + String name = StringHelper.parseColors(args.getJoinedStrings(1)); if (name.length() > 16) { Messaging.sendError(player, "NPC names cannot be longer than 16 characters. The name has been shortened."); @@ -183,6 +185,7 @@ public class NPCCommands { type = EntityType.PLAYER; } } + npc = npcRegistry.createNPC(type, name); String msg = ChatColor.GREEN + "You created " + StringHelper.wrap(npc.getName()) + " at your location"; @@ -197,8 +200,17 @@ public class NPCCommands { msg += " as a baby"; } } + if (args.hasValueFlag("trait")) { + Iterable parts = Splitter.on(",").trimResults().split(args.getFlag("trait")); + for (String tr : parts) { + Class clazz = CitizensAPI.getTraitFactory().getTraitClass(tr); + if (clazz != null) + npc.addTrait(clazz); + } + msg += " with the specified traits"; + } - if (args.hasValueFlag("behaviour")) { + if (args.hasValueFlag("b")) { npc.getTrait(Behaviour.class).addScripts(Splitter.on(",").split(args.getFlag("behaviour"))); msg += " with the specified behaviours"; } @@ -393,32 +405,28 @@ public class NPCCommands { @Command( aliases = { "npc" }, - usage = "profession [profession]", + usage = "profession|prof [profession]", desc = "Set a NPC's profession", - modifiers = { "profession" }, + modifiers = { "profession", "prof" }, min = 2, max = 2, permission = "npc.profession") @Requirements(selected = true, ownership = true, types = { EntityType.VILLAGER }) public void profession(CommandContext args, CommandSender sender, NPC npc) throws CommandException { String profession = args.getString(1); + Profession parsed; try { - npc.getTrait(VillagerProfession.class) - .setProfession(Profession.valueOf(profession.toUpperCase())); - Messaging.send(sender, StringHelper.wrap(npc.getName()) + " is now the profession " - + StringHelper.wrap(profession.toUpperCase()) + "."); + parsed = Profession.valueOf(profession.toUpperCase()); } catch (IllegalArgumentException ex) { throw new CommandException("'" + profession + "' is not a valid profession."); } + npc.getTrait(VillagerProfession.class).setProfession(parsed); + Messaging.send(sender, + StringHelper.wrap(npc.getName()) + " is now a " + StringHelper.wrap(profession) + "."); } - @Command( - aliases = { "npc" }, - usage = "remove (all)", - desc = "Remove a NPC", - modifiers = { "remove" }, - min = 1, - max = 2) + @Command(aliases = { "npc" }, usage = "remove|rem (all)", desc = "Remove a NPC", modifiers = { "remove", + "rem" }, min = 1, max = 2) @Requirements public void remove(CommandContext args, CommandSender sender, NPC npc) throws CommandException { if (args.argsLength() == 2) { @@ -466,9 +474,9 @@ public class NPCCommands { @Command( aliases = { "npc" }, - usage = "select [id]", + usage = "select|sel [id]", desc = "Select a NPC with the given ID", - modifiers = { "select" }, + modifiers = { "select", "sel" }, min = 2, max = 2, permission = "npc.select") @@ -547,7 +555,7 @@ public class NPCCommands { } @Command(aliases = { "npc" }, usage = "tphere", desc = "Teleport a NPC to your location", modifiers = { - "tphere", "move" }, min = 1, max = 1, permission = "npc.tphere") + "tphere", "tph", "move" }, min = 1, max = 1, permission = "npc.tphere") public void tphere(CommandContext args, Player player, NPC npc) { // Spawn the NPC if it isn't spawned to prevent NPEs if (!npc.isSpawned()) diff --git a/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index f8b498661..fd9a92c7a 100644 --- a/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -139,10 +139,10 @@ public abstract class CitizensNPC extends AbstractNPC { getTrait(CurrentLocation.class).setLocation(loc); getTrait(Spawned.class).setSpawned(true); + navigator.onSpawn(); // Modify NPC using traits after the entity has been created for (Trait trait : traits.values()) trait.onSpawn(); - navigator.onSpawn(); return true; } diff --git a/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java b/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java index e16ccdf26..ae5d447fa 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java +++ b/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java @@ -46,6 +46,8 @@ public class CitizensNavigator implements Navigator { @Override public float getSpeed() { + if (speed == -1) + throw new IllegalStateException("NPC has not been spawned"); return speed; } @@ -88,6 +90,8 @@ public class CitizensNavigator implements Navigator { @Override public void setSpeed(float speed) { this.speed = speed; + if (isNavigating()) + executing.setSpeed(this.speed); } @Override diff --git a/src/main/java/net/citizensnpcs/npc/ai/MCNavigationStrategy.java b/src/main/java/net/citizensnpcs/npc/ai/MCNavigationStrategy.java index 1eb98892d..acbdb1232 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/MCNavigationStrategy.java +++ b/src/main/java/net/citizensnpcs/npc/ai/MCNavigationStrategy.java @@ -38,6 +38,11 @@ public class MCNavigationStrategy implements PathStrategy { return TargetType.LOCATION; } + @Override + public void setSpeed(float speed) { + navigation.a(speed); + } + @Override public boolean update() { return navigation.f(); diff --git a/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java b/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java index 7dda1b802..777a0868c 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java +++ b/src/main/java/net/citizensnpcs/npc/ai/MCTargetStrategy.java @@ -84,6 +84,10 @@ public class MCTargetStrategy implements PathStrategy, EntityTarget { } private static final int ATTACK_DELAY_TICKS = 20; - private static final double ATTACK_DISTANCE = 1.75 * 1.75; + + @Override + public void setSpeed(float speed) { + navigation.a(speed); + } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/npc/ai/PathStrategy.java b/src/main/java/net/citizensnpcs/npc/ai/PathStrategy.java index 43ea83e69..556b79832 100644 --- a/src/main/java/net/citizensnpcs/npc/ai/PathStrategy.java +++ b/src/main/java/net/citizensnpcs/npc/ai/PathStrategy.java @@ -10,4 +10,6 @@ public interface PathStrategy { TargetType getTargetType(); boolean update(); + + void setSpeed(float speed); } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/trait/Age.java b/src/main/java/net/citizensnpcs/trait/Age.java index 36930889b..53ddf62f4 100644 --- a/src/main/java/net/citizensnpcs/trait/Age.java +++ b/src/main/java/net/citizensnpcs/trait/Age.java @@ -3,7 +3,10 @@ package net.citizensnpcs.trait; import net.citizensnpcs.api.exception.NPCLoadException; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.util.DataKey; +import net.citizensnpcs.util.Messaging; +import net.citizensnpcs.util.StringHelper; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Ageable; public class Age extends Trait implements Toggleable { @@ -64,4 +67,9 @@ public class Age extends Trait implements Toggleable { public String toString() { return "Age{age=" + age + ",locked=" + locked + "}"; } + + public void describe(CommandSender sender) { + Messaging.sendF(sender, "%s's age is %s and %s locked.", StringHelper.wrap(npc.getName()), + StringHelper.wrap(age), StringHelper.wrap(locked ? "is" : "isn't")); + } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/trait/Controllable.java b/src/main/java/net/citizensnpcs/trait/Controllable.java index 3de66e324..dd7ea7cd6 100644 --- a/src/main/java/net/citizensnpcs/trait/Controllable.java +++ b/src/main/java/net/citizensnpcs/trait/Controllable.java @@ -72,6 +72,7 @@ public class Controllable extends Trait implements Toggleable { boolean onGround = handle.onGround; handle.motX += handle.passenger.motX * (onGround ? GROUND_SPEED : AIR_SPEED); handle.motZ += handle.passenger.motZ * (onGround ? GROUND_SPEED : AIR_SPEED); + handle.e(npc.getNavigator().getSpeed()); } @Override diff --git a/src/main/java/net/citizensnpcs/trait/LookClose.java b/src/main/java/net/citizensnpcs/trait/LookClose.java index 835a92043..af0fd650e 100644 --- a/src/main/java/net/citizensnpcs/trait/LookClose.java +++ b/src/main/java/net/citizensnpcs/trait/LookClose.java @@ -40,7 +40,7 @@ public class LookClose extends Trait implements Toggleable, CommandConfigurable } private void findNewTarget() { - List nearby = npc.getBukkitEntity().getNearbyEntities(range / 2, range, range / 2); + List nearby = npc.getBukkitEntity().getNearbyEntities(range, range, range); final Location npcLocation = npc.getBukkitEntity().getLocation(); Collections.sort(nearby, new Comparator() { @Override diff --git a/src/main/java/net/citizensnpcs/util/StringHelper.java b/src/main/java/net/citizensnpcs/util/StringHelper.java index 8ffe3c014..fa87062bc 100644 --- a/src/main/java/net/citizensnpcs/util/StringHelper.java +++ b/src/main/java/net/citizensnpcs/util/StringHelper.java @@ -64,6 +64,7 @@ public class StringHelper { for (ChatColor color : ChatColor.values()) { parsed = parsed.replace("<" + color.getChar() + ">", color.toString()); } + parsed = ChatColor.translateAlternateColorCodes('&', parsed); return parsed; }