From 9548ddea62e4295fe3abdf5919941f913fcf6473 Mon Sep 17 00:00:00 2001 From: Jeremy Schroeder Date: Thu, 27 Dec 2012 22:28:39 -0500 Subject: [PATCH] Add -c flag to '/npc anchor --save anchor_name' to save location of the cursor. --- .../citizensnpcs/command/CommandContext.java | 492 ++-- .../command/command/NPCCommands.java | 2070 +++++++++-------- 2 files changed, 1290 insertions(+), 1272 deletions(-) diff --git a/src/main/java/net/citizensnpcs/command/CommandContext.java b/src/main/java/net/citizensnpcs/command/CommandContext.java index 9d3776ecb..1f906602b 100644 --- a/src/main/java/net/citizensnpcs/command/CommandContext.java +++ b/src/main/java/net/citizensnpcs/command/CommandContext.java @@ -1,242 +1,252 @@ -// $Id$ -/* - * Copyright (C) 2010 sk89q - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.citizensnpcs.command; - -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.bukkit.Location; -import org.bukkit.command.BlockCommandSender; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - -public class CommandContext { - protected String[] args; - protected final Set flags = new HashSet(); - private Location location = null; - private final CommandSender sender; - protected final Map valueFlags = Maps.newHashMap(); - - public CommandContext(CommandSender sender, String[] args) { - this.sender = sender; - int i = 1; - for (; i < args.length; i++) { - // initial pass for quotes - args[i] = args[i].trim(); - if (args[i].length() == 0) { - // Ignore this - continue; - } else if (args[i].charAt(0) == '\'' || args[i].charAt(0) == '"') { - char quote = args[i].charAt(0); - String quoted = args[i].substring(1); // remove initial quote - for (int inner = i + 1; inner < args.length; inner++) { - if (args[inner].isEmpty()) - continue; - String test = args[inner].trim(); - quoted += " " + test; - if (test.charAt(test.length() - 1) == quote) { - args[i] = quoted.substring(0, quoted.length() - 1); - for (int j = i + 1; j != inner; ++j) - args[j] = ""; - // remove ending quote - break; - } - } - } - } - for (i = 1; i < args.length; ++i) { - // second pass for flags - if (args[i].length() == 0) - continue; - if (i + 1 < args.length && args[i].length() > 2 && args[i].matches("^--[a-zA-Z]+$")) { - int inner = i + 1; - while (args[inner].length() == 0) { - // later args may have been quoted - ++inner; - if (inner >= args.length) { - inner = -1; - break; - } - } - - if (inner != -1) { - valueFlags.put(args[i].toLowerCase().replaceFirst("--", ""), args[inner]); - args[i] = ""; - args[inner] = ""; - } - } else if (args[i].charAt(0) == '-' && args[i].matches("^-[a-zA-Z]+$")) { - for (int k = 1; k < args[i].length(); k++) - flags.add(args[i].charAt(k)); - args[i] = ""; - } - } - List copied = Lists.newArrayList(); - for (String arg : args) { - arg = arg.trim(); - if (arg == null || arg.isEmpty()) - continue; - copied.add(arg.trim()); - } - this.args = copied.toArray(new String[copied.size()]); - } - - public CommandContext(String[] args) { - this(null, args); - } - - public int argsLength() { - return args.length - 1; - } - - public String getCommand() { - return args[0]; - } - - public double getDouble(int index) throws NumberFormatException { - return Double.parseDouble(args[index + 1]); - } - - public double getDouble(int index, double def) throws NumberFormatException { - return index + 1 < args.length ? Double.parseDouble(args[index + 1]) : def; - } - - public String getFlag(String ch) { - return valueFlags.get(ch); - } - - public String getFlag(String ch, String def) { - final String value = valueFlags.get(ch); - if (value == null) { - return def; - } - - return value; - } - - public double getFlagDouble(String ch) throws NumberFormatException { - return Double.parseDouble(valueFlags.get(ch)); - } - - public double getFlagDouble(String ch, double def) throws NumberFormatException { - final String value = valueFlags.get(ch); - if (value == null) { - return def; - } - - return Double.parseDouble(value); - } - - public int getFlagInteger(String ch) throws NumberFormatException { - return Integer.parseInt(valueFlags.get(ch)); - } - - public int getFlagInteger(String ch, int def) throws NumberFormatException { - final String value = valueFlags.get(ch); - if (value == null) { - return def; - } - - return Integer.parseInt(value); - } - - public Set getFlags() { - return flags; - } - - public int getInteger(int index) throws NumberFormatException { - return Integer.parseInt(args[index + 1]); - } - - public int getInteger(int index, int def) throws NumberFormatException { - if (index + 1 < args.length) { - try { - return Integer.parseInt(args[index + 1]); - } catch (NumberFormatException ex) { - } - } - return def; - } - - public String getJoinedStrings(int initialIndex) { - return getJoinedStrings(initialIndex, ' '); - } - - public String getJoinedStrings(int initialIndex, char delimiter) { - initialIndex = initialIndex + 1; - StringBuilder buffer = new StringBuilder(args[initialIndex]); - for (int i = initialIndex + 1; i < args.length; i++) - buffer.append(delimiter).append(args[i]); - return buffer.toString().trim(); - } - - public String[] getPaddedSlice(int index, int padding) { - String[] slice = new String[args.length - index + padding]; - System.arraycopy(args, index, slice, padding, args.length - index); - return slice; - } - - public Location getSenderLocation() { - if (location != null || sender == null) - return location; - if (sender instanceof Player) - location = ((Player) sender).getLocation(); - else if (sender instanceof BlockCommandSender) - location = ((BlockCommandSender) sender).getBlock().getLocation(); - return location; - } - - public String[] getSlice(int index) { - String[] slice = new String[args.length - index]; - System.arraycopy(args, index, slice, 0, args.length - index); - return slice; - } - - public String getString(int index) { - return args[index + 1]; - } - - public String getString(int index, String def) { - return index + 1 < args.length ? args[index + 1] : def; - } - - public Map getValueFlags() { - return valueFlags; - } - - public boolean hasFlag(char ch) { - return flags.contains(ch); - } - - public boolean hasValueFlag(String ch) { - return valueFlags.containsKey(ch); - } - - public int length() { - return args.length; - } - - public boolean matches(String command) { - return args[0].equalsIgnoreCase(command); - } +// $Id$ +/* + * Copyright (C) 2010 sk89q + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.citizensnpcs.command; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bukkit.Location; +import org.bukkit.command.BlockCommandSender; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +public class CommandContext { + protected String[] args; + protected final Set flags = new HashSet(); + private Location location = null; + private final CommandSender sender; + protected final Map valueFlags = Maps.newHashMap(); + + public CommandContext(CommandSender sender, String[] args) { + this.sender = sender; + int i = 1; + for (; i < args.length; i++) { + // initial pass for quotes + args[i] = args[i].trim(); + if (args[i].length() == 0) { + // Ignore this + continue; + } else if (args[i].charAt(0) == '\'' || args[i].charAt(0) == '"') { + char quote = args[i].charAt(0); + String quoted = args[i].substring(1); // remove initial quote + for (int inner = i + 1; inner < args.length; inner++) { + if (args[inner].isEmpty()) + continue; + String test = args[inner].trim(); + quoted += " " + test; + if (test.charAt(test.length() - 1) == quote) { + args[i] = quoted.substring(0, quoted.length() - 1); + for (int j = i + 1; j != inner; ++j) + args[j] = ""; + // remove ending quote + break; + } + } + } + } + for (i = 1; i < args.length; ++i) { + // second pass for flags + if (args[i].length() == 0) + continue; + if (i + 1 < args.length && args[i].length() > 2 && args[i].matches("^--[a-zA-Z]+$")) { + int inner = i + 1; + while (args[inner].length() == 0) { + // later args may have been quoted + ++inner; + if (inner >= args.length) { + inner = -1; + break; + } + } + + if (inner != -1) { + valueFlags.put(args[i].toLowerCase().replaceFirst("--", ""), args[inner]); + args[i] = ""; + args[inner] = ""; + } + } else if (args[i].charAt(0) == '-' && args[i].matches("^-[a-zA-Z]+$")) { + for (int k = 1; k < args[i].length(); k++) + flags.add(args[i].charAt(k)); + args[i] = ""; + } + } + List copied = Lists.newArrayList(); + for (String arg : args) { + arg = arg.trim(); + if (arg == null || arg.isEmpty()) + continue; + copied.add(arg.trim()); + } + this.args = copied.toArray(new String[copied.size()]); + } + + public CommandContext(String[] args) { + this(null, args); + } + + public int argsLength() { + return args.length - 1; + } + + public String getCommand() { + return args[0]; + } + + public double getDouble(int index) throws NumberFormatException { + return Double.parseDouble(args[index + 1]); + } + + public double getDouble(int index, double def) throws NumberFormatException { + return index + 1 < args.length ? Double.parseDouble(args[index + 1]) : def; + } + + public String getFlag(String ch) { + return valueFlags.get(ch); + } + + public String getFlag(String ch, String def) { + final String value = valueFlags.get(ch); + if (value == null) { + return def; + } + + return value; + } + + public double getFlagDouble(String ch) throws NumberFormatException { + return Double.parseDouble(valueFlags.get(ch)); + } + + public double getFlagDouble(String ch, double def) throws NumberFormatException { + final String value = valueFlags.get(ch); + if (value == null) { + return def; + } + + return Double.parseDouble(value); + } + + public int getFlagInteger(String ch) throws NumberFormatException { + return Integer.parseInt(valueFlags.get(ch)); + } + + public int getFlagInteger(String ch, int def) throws NumberFormatException { + final String value = valueFlags.get(ch); + if (value == null) { + return def; + } + + return Integer.parseInt(value); + } + + public Set getFlags() { + return flags; + } + + public int getInteger(int index) throws NumberFormatException { + return Integer.parseInt(args[index + 1]); + } + + public int getInteger(int index, int def) throws NumberFormatException { + if (index + 1 < args.length) { + try { + return Integer.parseInt(args[index + 1]); + } catch (NumberFormatException ex) { + } + } + return def; + } + + public String getJoinedStrings(int initialIndex) { + return getJoinedStrings(initialIndex, ' '); + } + + public String getJoinedStrings(int initialIndex, char delimiter) { + initialIndex = initialIndex + 1; + StringBuilder buffer = new StringBuilder(args[initialIndex]); + for (int i = initialIndex + 1; i < args.length; i++) + buffer.append(delimiter).append(args[i]); + return buffer.toString().trim(); + } + + public String[] getPaddedSlice(int index, int padding) { + String[] slice = new String[args.length - index + padding]; + System.arraycopy(args, index, slice, padding, args.length - index); + return slice; + } + + public Location getSenderLocation() { + if (location != null || sender == null) + return location; + if (sender instanceof Player) + location = ((Player) sender).getLocation(); + else if (sender instanceof BlockCommandSender) + location = ((BlockCommandSender) sender).getBlock().getLocation(); + return location; + } + + public Location getSenderTargetBlockLocation() { + if (location != null || sender == null) + return location; + if (sender instanceof Player) + location = ((Player) sender).getTargetBlock(null, 50).getLocation(); + else if (sender instanceof BlockCommandSender) + location = ((BlockCommandSender) sender).getBlock().getLocation(); + return location; + } + + public String[] getSlice(int index) { + String[] slice = new String[args.length - index]; + System.arraycopy(args, index, slice, 0, args.length - index); + return slice; + } + + public String getString(int index) { + return args[index + 1]; + } + + public String getString(int index, String def) { + return index + 1 < args.length ? args[index + 1] : def; + } + + public Map getValueFlags() { + return valueFlags; + } + + public boolean hasFlag(char ch) { + return flags.contains(ch); + } + + public boolean hasValueFlag(String ch) { + return valueFlags.containsKey(ch); + } + + public int length() { + return args.length; + } + + public boolean matches(String command) { + return args[0].equalsIgnoreCase(command); + } } \ No newline at end of file diff --git a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java index 8fd59a003..9230d6eca 100644 --- a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java +++ b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java @@ -72,1035 +72,1043 @@ import com.google.common.collect.Lists; @Requirements(selected = true, ownership = true) public class NPCCommands { - private final NPCRegistry npcRegistry; - private final NPCSelector selector; - - public NPCCommands(Citizens plugin) { - npcRegistry = CitizensAPI.getNPCRegistry(); - selector = plugin.getNPCSelector(); - } - - @Command( - aliases = { "npc" }, - usage = "age [age] (-l)", - desc = "Set the age of a NPC", - help = Messages.COMMAND_AGE_HELP, - flags = "l", - modifiers = { "age" }, - min = 1, - max = 2, - permission = "npc.age") - @Requirements(selected = true, ownership = true, types = { EntityType.CHICKEN, EntityType.COW, - EntityType.OCELOT, EntityType.PIG, EntityType.SHEEP, EntityType.VILLAGER, EntityType.WOLF }) - 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.sendTr(sender, trait.toggle() ? Messages.AGE_LOCKED : Messages.AGE_UNLOCKED); - } - if (args.argsLength() <= 1) { - if (!toggleLock) - trait.describe(sender); - return; - } - int age = 0; - try { - age = args.getInteger(1); - if (age < -24000 || age > 0) - throw new CommandException(Messages.INVALID_AGE); - Messaging.sendTr(sender, Messages.AGE_SET_NORMAL, npc.getName(), age); - } catch (NumberFormatException ex) { - if (args.getString(1).equalsIgnoreCase("baby")) { - age = -24000; - Messaging.sendTr(sender, Messages.AGE_SET_BABY, npc.getName()); - } else if (args.getString(1).equalsIgnoreCase("adult")) { - age = 0; - Messaging.sendTr(sender, Messages.AGE_SET_ADULT, npc.getName()); - } else - throw new CommandException(Messages.INVALID_AGE); - } - - trait.setAge(age); - } - - @Command( - aliases = { "npc" }, - usage = "anchor (--save [name]|--assume [name]|--remove [name]) (-a)", - desc = "Changes/Saves/Lists NPC's location anchor(s)", - flags = "a", - modifiers = { "anchor" }, - min = 1, - max = 2, - permission = "npc.anchor") - @Requirements(selected = true, ownership = true, types = EntityType.PLAYER) - public void anchor(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - Anchors trait = npc.getTrait(Anchors.class); - if (args.hasValueFlag("save")) { - if (args.getFlag("save").isEmpty()) - throw new CommandException(Messages.INVALID_ANCHOR_NAME); - - if (args.getSenderLocation() == null) - throw new ServerCommandException(); - - if (trait.addAnchor(args.getFlag("save"), args.getSenderLocation())) { - Messaging.sendTr(sender, Messages.ANCHOR_ADDED); - } else - throw new CommandException(Messages.ANCHOR_ALREADY_EXISTS, args.getFlag("save")); - } else if (args.hasValueFlag("assume")) { - if (args.getFlag("assume").isEmpty()) - throw new CommandException(Messages.INVALID_ANCHOR_NAME); - - Anchor anchor = trait.getAnchor(args.getFlag("assume")); - if (anchor == null) - throw new CommandException(Messages.ANCHOR_MISSING, args.getFlag("assume")); - npc.getBukkitEntity().teleport(anchor.getLocation()); - } else if (args.hasValueFlag("remove")) { - if (args.getFlag("remove").isEmpty()) - throw new CommandException(Messages.INVALID_ANCHOR_NAME); - if (trait.removeAnchor(trait.getAnchor(args.getFlag("remove")))) - Messaging.sendTr(sender, Messages.ANCHOR_REMOVED); - else - throw new CommandException(Messages.ANCHOR_MISSING, args.getFlag("remove")); - } else if (!args.hasFlag('a')) { - 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); - } - - int page = args.getInteger(1, 1); - if (!paginator.sendPage(sender, page)) - throw new CommandException(Messages.COMMAND_PAGE_MISSING); - } - - // Assume Player's position - if (!args.hasFlag('a')) - return; - if (sender instanceof ConsoleCommandSender) - throw new ServerCommandException(); - npc.getBukkitEntity().teleport(args.getSenderLocation()); - } - - @Command( - aliases = { "npc" }, - usage = "behaviour [scripts] (-r)", - desc = "Sets the behaviour of a NPC", - help = Messages.BEHAVIOUR_HELP, - modifiers = { "behaviour", "behavior", "ai" }, - flags = "r", - min = 2, - permission = "npc.behaviour") - 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).removeScripts(files); - Messaging.sendTr(sender, Messages.BEHAVIOURS_REMOVED); - } else { - npc.getTrait(Behaviour.class).addScripts(files); - Messaging.sendTr(sender, Messages.BEHAVIOURS_ADDED); - } - } - - @Command( - aliases = { "npc" }, - usage = "controllable|control -f", - desc = "Toggles whether the NPC can be ridden and controlled", - modifiers = { "controllable", "control" }, - min = 1, - max = 1, - flags = "f") - public void controllable(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - if ((npc.isSpawned() && !sender.hasPermission("citizens.npc.controllable." - + npc.getBukkitEntity().getType().toString().toLowerCase())) - || !sender.hasPermission("citizens.npc.controllable")) - throw new NoPermissionsException(); - Controllable trait = npc.getTrait(Controllable.class); - boolean enabled = trait.toggle(); - String key = enabled ? Messages.CONTROLLABLE_SET : Messages.CONTROLLABLE_REMOVED; - Messaging.sendTr(sender, key, npc.getName()); - } - - @Command( - aliases = { "npc" }, - usage = "copy (--name newname)", - desc = "Copies an NPC", - modifiers = { "copy" }, - min = 1, - max = 1, - permission = "npc.copy") - public void copy(CommandContext args, CommandSender sender, NPC npc) { - EntityType type = npc.getTrait(MobType.class).getType(); - String name = args.getFlag("name", npc.getFullName()); - CitizensNPC copy = (CitizensNPC) npcRegistry.createNPC(type, name); - CitizensNPC from = (CitizensNPC) npc; - - DataKey key = new MemoryDataKey(); - from.save(key); - copy.load(key); - - if (copy.isSpawned() && args.getSenderLocation() != null) { - Location location = args.getSenderLocation(); - copy.getBukkitEntity().teleport(location); - copy.getTrait(CurrentLocation.class).setLocation(location); - } - - for (Trait trait : copy.getTraits()) - trait.onCopy(); - Messaging.sendTr(sender, Messages.NPC_COPIED, npc.getName()); - selector.select(sender, copy); - } - - @Command( - aliases = { "npc" }, - usage = "create [name] ((-b,u) --at (x:y:z:world) --type (type) --trait ('trait1, trait2...') --b (behaviours))", - desc = "Create a new NPC", - flags = "bu", - modifiers = { "create" }, - min = 2, - permission = "npc.create") - @Requirements - public void create(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - String name = StringHelper.parseColors(args.getJoinedStrings(1)); - if (name.length() > 16) { - Messaging.sendErrorTr(sender, Messages.NPC_NAME_TOO_LONG); - name = name.substring(0, 15); - } - if (name.length() <= 0) - throw new CommandException(); - - EntityType type = EntityType.PLAYER; - if (args.hasValueFlag("type")) { - String inputType = args.getFlag("type"); - type = Util.matchEntityType(inputType); - if (type == null) { - Messaging.sendErrorTr(sender, Messages.NPC_CREATE_INVALID_MOBTYPE, inputType); - type = EntityType.PLAYER; - } else if (!LivingEntity.class.isAssignableFrom(type.getEntityClass())) { - Messaging.sendErrorTr(sender, Messages.NOT_LIVING_MOBTYPE, type); - type = EntityType.PLAYER; - } - } - - npc = npcRegistry.createNPC(type, name); - String msg = "You created [[" + npc.getName() + "]]"; - - int age = 0; - if (args.hasFlag('b')) { - if (!Ageable.class.isAssignableFrom(type.getEntityClass())) - Messaging.sendErrorTr(sender, Messages.MOBTYPE_CANNOT_BE_AGED, type.name().toLowerCase() - .replace("_", "-")); - else { - age = -24000; - msg += " as a baby"; - } - } - - if (args.hasValueFlag("b") || args.hasValueFlag("behaviours") || args.hasValueFlag("behaviors")) { - String behaviours = args.getFlag("b", args.getFlag("behaviours", args.getFlag("behaviors"))); - npc.getTrait(Behaviour.class).addScripts(Splitter.on(',').split(behaviours)); - msg += " with the specified behaviours"; - } - - msg += "."; - - // Initialize necessary traits - if (!Setting.SERVER_OWNS_NPCS.asBoolean()) - npc.getTrait(Owner.class).setOwner(sender.getName()); - npc.getTrait(MobType.class).setType(type); - - Location spawnLoc = null; - if (sender instanceof Player) { - spawnLoc = args.getSenderLocation(); - PlayerCreateNPCEvent event = new PlayerCreateNPCEvent((Player) sender, npc); - Bukkit.getPluginManager().callEvent(event); - if (event.isCancelled()) { - npc.destroy(); - String reason = "Couldn't create NPC."; - if (!event.getCancelReason().isEmpty()) - reason += " Reason: " + event.getCancelReason(); - throw new CommandException(reason); - } - } else if (sender instanceof BlockCommandSender) { - spawnLoc = args.getSenderLocation(); - } - - if (args.hasValueFlag("at")) { - String[] parts = Iterables.toArray(Splitter.on(':').split(args.getFlag("at")), String.class); - if (parts.length > 0) { - String worldName = args.getSenderLocation() != null ? args.getSenderLocation().getWorld() - .getName() : ""; - int x = 0, y = 0, z = 0; - float yaw = 0F, pitch = 0F; - switch (parts.length) { - case 6: - pitch = Float.parseFloat(parts[5]); - case 5: - yaw = Float.parseFloat(parts[4]); - case 4: - worldName = parts[3]; - case 3: - x = Integer.parseInt(parts[0]); - y = Integer.parseInt(parts[1]); - z = Integer.parseInt(parts[2]); - break; - default: - throw new CommandException(Messages.INVALID_SPAWN_LOCATION); - } - World world = Bukkit.getWorld(worldName); - if (world == null) - throw new CommandException(Messages.INVALID_SPAWN_LOCATION); - spawnLoc = new Location(world, x, y, z, yaw, pitch); - } else { - Player search = Bukkit.getPlayerExact(args.getFlag("at")); - if (search == null) - throw new CommandException(Messages.PLAYER_NOT_FOUND_FOR_SPAWN); - spawnLoc = search.getLocation(); - } - } - if (spawnLoc == null) { - npc.destroy(); - throw new CommandException(Messages.INVALID_SPAWN_LOCATION); - } - - if (!args.hasFlag('u')) - npc.spawn(spawnLoc); - - if (args.hasValueFlag("trait")) { - Iterable parts = Splitter.on(',').trimResults().split(args.getFlag("trait")); - StringBuilder builder = new StringBuilder(); - for (String tr : parts) { - Trait trait = CitizensAPI.getTraitFactory().getTrait(tr); - if (trait == null) - continue; - npc.addTrait(trait); - builder.append(StringHelper.wrap(tr) + ", "); - } - if (builder.length() > 0) - builder.delete(builder.length() - 2, builder.length()); - msg += " with traits " + builder.toString(); - } - - if (args.hasValueFlag("template")) { - Iterable parts = Splitter.on(',').trimResults().split(args.getFlag("template")); - StringBuilder builder = new StringBuilder(); - for (String part : parts) { - Template template = Template.byName(part); - if (template == null) - continue; - template.apply(npc); - builder.append(StringHelper.wrap(part) + ", "); - } - if (builder.length() > 0) - builder.delete(builder.length() - 2, builder.length()); - msg += " with templates " + builder.toString(); - } - - // Set age after entity spawns - if (npc.getBukkitEntity() instanceof Ageable) - npc.getTrait(Age.class).setAge(age); - selector.select(sender, npc); - Messaging.send(sender, msg); - } - - @Command( - aliases = { "npc" }, - usage = "despawn (id)", - desc = "Despawn a NPC", - modifiers = { "despawn" }, - min = 1, - max = 2, - permission = "npc.despawn") - @Requirements - public void despawn(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - if (npc == null || args.argsLength() == 2) { - if (args.argsLength() < 2) - throw new CommandException(Messages.COMMAND_MUST_HAVE_SELECTED); - int id = args.getInteger(1); - npc = CitizensAPI.getNPCRegistry().getById(id); - if (npc == null) - throw new CommandException(Messages.NO_NPC_WITH_ID_FOUND, id); - } - npc.getTrait(Spawned.class).setSpawned(false); - npc.despawn(); - Messaging.sendTr(sender, Messages.NPC_DESPAWNED, npc.getName()); - } - - @Command( - aliases = { "npc" }, - usage = "gamemode [gamemode]", - desc = "Changes the gamemode", - modifiers = { "gravity" }, - min = 1, - max = 2, - permission = "npc.gravity") - @Requirements(selected = true, ownership = true, types = { EntityType.PLAYER }) - public void gamemode(CommandContext args, CommandSender sender, NPC npc) { - Player player = (Player) npc.getBukkitEntity(); - if (args.argsLength() == 1) { - Messaging.sendTr(sender, Messages.GAMEMODE_DESCRIBE, npc.getName(), player.getGameMode().name() - .toLowerCase()); - return; - } - GameMode mode = null; - try { - int value = args.getInteger(1); - mode = GameMode.getByValue(value); - } catch (NumberFormatException ex) { - try { - mode = GameMode.valueOf(args.getString(1)); - } catch (IllegalArgumentException e) { - } - } - if (mode == null) { - Messaging.sendErrorTr(sender, Messages.GAMEMODE_INVALID, args.getString(1)); - return; - } - player.setGameMode(mode); - Messaging.sendTr(sender, Messages.GAMEMODE_SET, mode.name().toLowerCase()); - } - - @Command( - aliases = { "npc" }, - usage = "gravity", - desc = "Toggles gravity", - modifiers = { "gravity" }, - min = 1, - max = 1, - permission = "npc.gravity") - public void gravity(CommandContext args, CommandSender sender, NPC npc) { - boolean enabled = npc.getTrait(Gravity.class).toggle(); - String key = enabled ? Messages.GRAVITY_ENABLED : Messages.GRAVITY_DISABLED; - Messaging.sendTr(sender, key, npc.getName()); - } - - @Command( - aliases = { "npc" }, - usage = "list (page) ((-a) --owner (owner) --type (type) --char (char))", - desc = "List NPCs", - flags = "a", - modifiers = { "list" }, - min = 1, - max = 2, - permission = "npc.list") - @Requirements - public void list(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - List npcs = new ArrayList(); - - if (args.hasFlag('a')) { - for (NPC add : npcRegistry) - npcs.add(add); - } else if (args.getValueFlags().size() == 0 && sender instanceof Player) { - for (NPC add : npcRegistry) { - if (!npcs.contains(add) && add.getTrait(Owner.class).isOwnedBy(sender)) - npcs.add(add); - } - } else { - if (args.hasValueFlag("owner")) { - String name = args.getFlag("owner"); - for (NPC add : npcRegistry) { - if (!npcs.contains(add) && add.getTrait(Owner.class).isOwnedBy(name)) - npcs.add(add); - } - } - - if (args.hasValueFlag("type")) { - EntityType type = Util.matchEntityType(args.getFlag("type")); - - if (type == null) - throw new CommandException(Messages.COMMAND_INVALID_MOBTYPE, type); - - for (NPC add : npcRegistry) { - if (!npcs.contains(add) && add.getTrait(MobType.class).getType() == type) - npcs.add(add); - } - } - } - - Paginator paginator = new Paginator().header("NPCs"); - paginator.addLine("Key: ID Name"); - for (int i = 0; i < npcs.size(); i += 2) { - String line = "" + npcs.get(i).getId() + " " + npcs.get(i).getName(); - if (npcs.size() >= i + 2) - line += " " + "" + npcs.get(i + 1).getId() + " " + npcs.get(i + 1).getName(); - paginator.addLine(line); - } - - int page = args.getInteger(1, 1); - if (!paginator.sendPage(sender, page)) - throw new CommandException(Messages.COMMAND_PAGE_MISSING); - } - - @Command( - aliases = { "npc" }, - usage = "lookclose", - desc = "Toggle whether a NPC will look when a player is near", - modifiers = { "lookclose", "look", "rotate" }, - min = 1, - max = 1, - permission = "npc.lookclose") - public void lookClose(CommandContext args, CommandSender sender, NPC npc) { - Messaging.sendTr(sender, npc.getTrait(LookClose.class).toggle() ? Messages.LOOKCLOSE_SET - : Messages.LOOKCLOSE_STOPPED, npc.getName()); - } - - @Command( - aliases = { "npc" }, - usage = "mount", - desc = "Mounts a controllable NPC", - modifiers = { "mount" }, - min = 1, - max = 1, - permission = "npc.controllable") - public void mount(CommandContext args, Player player, NPC npc) { - boolean enabled = npc.hasTrait(Controllable.class) && npc.getTrait(Controllable.class).isEnabled(); - if (!enabled) { - Messaging.sendTr(player, Messages.NPC_NOT_CONTROLLABLE, npc.getName()); - return; - } - boolean success = npc.getTrait(Controllable.class).mount(player); - if (!success) - Messaging.sendTr(player, Messages.FAILED_TO_MOUNT_NPC, npc.getName()); - } - - @Command( - aliases = { "npc" }, - usage = "moveto x:y:z:world | x y z world", - desc = "Teleports a NPC to a given location", - modifiers = "moveto", - min = 1, - permission = "npc.moveto") - public void moveto(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - // Spawn the NPC if it isn't spawned to prevent NPEs - if (!npc.isSpawned()) - npc.spawn(npc.getTrait(CurrentLocation.class).getLocation()); - Location current = npc.getBukkitEntity().getLocation(); - Location to; - if (args.argsLength() > 1) { - String[] parts = Iterables.toArray(Splitter.on(':').split(args.getJoinedStrings(1, ':')), - String.class); - if (parts.length != 4 && parts.length != 3) - throw new CommandException(Messages.MOVETO_FORMAT); - double x = Double.parseDouble(parts[0]); - double y = Double.parseDouble(parts[1]); - double z = Double.parseDouble(parts[2]); - World world = parts.length == 4 ? Bukkit.getWorld(parts[3]) : current.getWorld(); - if (world == null) - throw new CommandException(Messages.WORLD_NOT_FOUND); - to = new Location(world, x, y, z, current.getYaw(), current.getPitch()); - } else { - to = current.clone(); - if (args.hasValueFlag("x")) - to.setX(args.getFlagDouble("x")); - if (args.hasValueFlag("y")) - to.setY(args.getFlagDouble("y")); - if (args.hasValueFlag("z")) - to.setZ(args.getFlagDouble("z")); - if (args.hasValueFlag("yaw")) - to.setYaw((float) args.getFlagDouble("yaw")); - if (args.hasValueFlag("pitch")) - to.setPitch((float) args.getFlagDouble("pitch")); - if (args.hasValueFlag("world")) { - World world = Bukkit.getWorld(args.getFlag("world")); - if (world == null) - throw new CommandException(Messages.WORLD_NOT_FOUND); - to.setWorld(world); - } - } - - npc.getBukkitEntity().teleport(to, TeleportCause.COMMAND); - - Messaging.sendTr(sender, Messages.MOVETO_TELEPORTED, npc.getName(), to); - } - - @Command(aliases = { "npc" }, desc = "Show basic NPC information", max = 0, permission = "npc.info") - public void npc(CommandContext args, CommandSender sender, NPC npc) { - Messaging.send(sender, StringHelper.wrapHeader(npc.getName())); - Messaging.send(sender, " ID: " + npc.getId()); - Messaging.send(sender, " Type: " + npc.getTrait(MobType.class).getType()); - if (npc.isSpawned()) { - Location loc = npc.getBukkitEntity().getLocation(); - String format = " Spawned at %d, %d, %d in world %s"; - Messaging.send(sender, String.format(format, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), - loc.getWorld().getName())); - } - Messaging.send(sender, " Traits"); - for (Trait trait : npc.getTraits()) { - if (CitizensAPI.getTraitFactory().isInternalTrait(trait)) - continue; - String message = " - " + trait.getName(); - Messaging.send(sender, message); - } - } - - @Command( - aliases = { "npc" }, - usage = "owner [name]", - desc = "Set the owner of an NPC", - modifiers = { "owner" }, - min = 1, - max = 2, - permission = "npc.owner") - public void owner(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - Owner ownerTrait = npc.getTrait(Owner.class); - if (args.argsLength() == 1) { - Messaging.sendTr(sender, Messages.NPC_OWNER, npc.getName(), ownerTrait.getOwner()); - return; - } - String name = args.getString(1); - if (ownerTrait.isOwnedBy(name)) - throw new CommandException(Messages.ALREADY_OWNER, name, npc.getName()); - ownerTrait.setOwner(name); - boolean serverOwner = name.equalsIgnoreCase(Owner.SERVER); - Messaging.sendTr(sender, serverOwner ? Messages.OWNER_SET_SERVER : Messages.OWNER_SET, npc.getName(), - name); - } - - @Command( - aliases = { "npc" }, - usage = "pathrange [range]", - desc = "Sets an NPC's pathfinding range", - modifiers = { "pathrange", "pathfindingrange", "prange" }, - min = 2, - max = 2, - permission = "npc.pathfindingrange") - public void pathfindingRange(CommandContext args, CommandSender sender, NPC npc) { - double range = Math.max(1, args.getDouble(1)); - npc.getNavigator().getDefaultParameters().range((float) range); - Messaging.sendTr(sender, Messages.PATHFINDING_RANGE_SET, range); - } - - @Command( - aliases = { "npc" }, - usage = "playerlist (-a,r)", - desc = "Sets whether the NPC is put in the playerlist", - modifiers = { "playerlist" }, - min = 1, - max = 1, - flags = "ar", - permission = "npc.playerlist") - @Requirements(types = EntityType.PLAYER) - public void playerlist(CommandContext args, CommandSender sender, NPC npc) { - boolean remove = !npc.data().get("removefromplayerlist", - Setting.REMOVE_PLAYERS_FROM_PLAYER_LIST.asBoolean()); - if (args.hasFlag('a')) - remove = false; - else if (args.hasFlag('r')) - remove = true; - npc.data().setPersistent("removefromplayerlist", remove); - if (npc.isSpawned()) - NMS.addOrRemoveFromPlayerList(npc.getBukkitEntity(), remove); - Messaging.sendTr(sender, remove ? Messages.REMOVED_FROM_PLAYERLIST : Messages.ADDED_TO_PLAYERLIST, - npc.getName()); - } - - @Command( - aliases = { "npc" }, - usage = "pose (--save [name]|--assume [name]|--remove [name]) (-a)", - desc = "Changes/Saves/Lists NPC's head pose(s)", - flags = "a", - modifiers = { "pose" }, - min = 1, - max = 2, - permission = "npc.pose") - @Requirements(selected = true, ownership = true, types = EntityType.PLAYER) - public void pose(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - Poses trait = npc.getTrait(Poses.class); - if (args.hasValueFlag("save")) { - if (args.getFlag("save").isEmpty()) - throw new CommandException(Messages.INVALID_POSE_NAME); - - if (args.getSenderLocation() == null) - throw new ServerCommandException(); - - if (trait.addPose(args.getFlag("save"), args.getSenderLocation())) { - Messaging.sendTr(sender, Messages.POSE_ADDED); - } else - throw new CommandException(Messages.POSE_ALREADY_EXISTS, args.getFlag("assume")); - } else if (args.hasValueFlag("assume")) { - String pose = args.getFlag("assume"); - if (pose.isEmpty()) - throw new CommandException(Messages.INVALID_POSE_NAME); - - if (!trait.hasPose(pose)) - throw new CommandException(Messages.POSE_MISSING, pose); - trait.assumePose(pose); - } else if (args.hasValueFlag("remove")) { - if (args.getFlag("remove").isEmpty()) - throw new CommandException(Messages.INVALID_POSE_NAME); - if (trait.removePose(args.getFlag("remove"))) { - Messaging.sendTr(sender, Messages.POSE_REMOVED); - } else - throw new CommandException(Messages.POSE_MISSING, args.getFlag("remove")); - } else if (!args.hasFlag('a')) { - trait.describe(sender, args.getInteger(1, 1)); - } - - // Assume Player's pose - if (!args.hasFlag('a')) - return; - if (args.getSenderLocation() == null) - throw new ServerCommandException(); - Location location = args.getSenderLocation(); - trait.assumePose(location); - } - - @Command( - aliases = { "npc" }, - usage = "power", - desc = "Toggle a creeper NPC as powered", - modifiers = { "power" }, - min = 1, - max = 1, - permission = "npc.power") - @Requirements(selected = true, ownership = true, types = { EntityType.CREEPER }) - public void power(CommandContext args, CommandSender sender, NPC npc) { - Messaging.sendTr(sender, npc.getTrait(Powered.class).toggle() ? Messages.POWERED_SET - : Messages.POWERED_STOPPED); - } - - @Command( - aliases = { "npc" }, - usage = "profession|prof [profession]", - desc = "Set a NPC's 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 { - parsed = Profession.valueOf(profession.toUpperCase()); - } catch (IllegalArgumentException ex) { - throw new CommandException(Messages.INVALID_PROFESSION); - } - npc.getTrait(VillagerProfession.class).setProfession(parsed); - Messaging.sendTr(sender, Messages.PROFESSION_SET, npc.getName(), profession); - } - - @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) { - if (!args.getString(1).equalsIgnoreCase("all")) - throw new CommandException(Messages.REMOVE_INCORRECT_SYNTAX); - if (!sender.hasPermission("citizens.admin.remove.all") && !sender.hasPermission("citizens.admin")) - throw new NoPermissionsException(); - npcRegistry.deregisterAll(); - Messaging.sendTr(sender, Messages.REMOVED_ALL_NPCS); - return; - } - if (!(sender instanceof Player)) - throw new CommandException(Messages.COMMAND_MUST_BE_INGAME); - Player player = (Player) sender; - if (npc == null) - throw new CommandException(Messages.COMMAND_MUST_HAVE_SELECTED); - if (!npc.getTrait(Owner.class).isOwnedBy(player)) - throw new CommandException(Messages.COMMAND_MUST_BE_OWNER); - if (!player.hasPermission("citizens.npc.remove") && !player.hasPermission("citizens.admin")) - throw new NoPermissionsException(); - npc.destroy(); - Messaging.sendTr(player, Messages.NPC_REMOVED, npc.getName()); - } - - @Command( - aliases = { "npc" }, - usage = "rename [name]", - desc = "Rename a NPC", - modifiers = { "rename" }, - min = 2, - permission = "npc.rename") - public void rename(CommandContext args, CommandSender sender, NPC npc) { - String oldName = npc.getName(); - String newName = args.getJoinedStrings(1); - if (newName.length() > 16) { - Messaging.sendErrorTr(sender, Messages.NPC_NAME_TOO_LONG); - newName = newName.substring(0, 15); - } - Location prev = npc.isSpawned() ? npc.getBukkitEntity().getLocation() : null; - npc.despawn(); - npc.setName(newName); - if (prev != null) - npc.spawn(prev); - - Messaging.sendTr(sender, Messages.NPC_RENAMED, oldName, newName); - } - - @Command( - aliases = { "npc" }, - usage = "select|sel [id|name] (--r range)", - desc = "Select a NPC with the given ID or name", - modifiers = { "select", "sel" }, - min = 1, - max = 2, - permission = "npc.select") - @Requirements - public void select(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - NPC toSelect = null; - if (args.argsLength() <= 1) { - if (!(sender instanceof Player)) - throw new ServerCommandException(); - double range = Math.abs(args.getFlagDouble("r", 10)); - Entity player = (Player) sender; - final Location location = args.getSenderLocation(); - List search = player.getNearbyEntities(range, range, range); - Collections.sort(search, new Comparator() { - @Override - public int compare(Entity o1, Entity o2) { - double d = o1.getLocation().distanceSquared(location) - - o2.getLocation().distanceSquared(location); - return d > 0 ? 1 : d < 0 ? -1 : 0; - } - }); - for (Entity possibleNPC : search) { - NPC test = npcRegistry.getNPC(possibleNPC); - if (test == null) - continue; - toSelect = test; - break; - } - } else { - try { - int id = args.getInteger(1); - toSelect = npcRegistry.getById(id); - } catch (NumberFormatException ex) { - String name = args.getString(1); - List possible = Lists.newArrayList(); - for (NPC test : npcRegistry) { - if (test.getName().equalsIgnoreCase(name)) - possible.add(test); - } - if (possible.size() == 1) - toSelect = possible.get(0); - else if (possible.size() > 1) { - SelectionPrompt.start(selector, (Player) sender, possible); - return; - } - } - } - if (toSelect == null || !toSelect.getTrait(Spawned.class).shouldSpawn()) - throw new CommandException(Messages.NPC_NOT_FOUND); - if (npc != null && toSelect.getId() == npc.getId()) - throw new CommandException(Messages.NPC_ALREADY_SELECTED); - selector.select(sender, toSelect); - Messaging.sendWithNPC(sender, Setting.SELECTION_MESSAGE.asString(), toSelect); - } - - @Command( - aliases = { "npc" }, - usage = "skeletontype [type]", - desc = "Sets the NPC's skeleton type", - modifiers = { "skeletontype", "sktype" }, - min = 2, - max = 2, - permission = "npc.skeletontype") - @Requirements(selected = true, ownership = true, types = EntityType.SKELETON) - public void skeletonType(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - SkeletonType type = (type = SkeletonType.getType(args.getInteger(1))) == null ? SkeletonType - .valueOf(args.getString(1)) : type; - if (type == null) - throw new CommandException(Messages.INVALID_SKELETON_TYPE); - npc.getTrait(NPCSkeletonType.class).setType(type); - Messaging.sendTr(sender, Messages.SKELETON_TYPE_SET, npc.getName(), type); - } - - @Command( - aliases = { "npc" }, - usage = "size [size]", - desc = "Sets the NPC's size", - modifiers = { "size" }, - min = 1, - max = 2, - permission = "npc.size") - @Requirements(selected = true, ownership = true, types = { EntityType.MAGMA_CUBE, EntityType.SLIME }) - public void slimeSize(CommandContext args, CommandSender sender, NPC npc) { - SlimeSize trait = npc.getTrait(SlimeSize.class); - if (args.argsLength() <= 1) { - trait.describe(sender); - return; - } - int size = Math.max(1, args.getInteger(1)); - trait.setSize(size); - Messaging.sendTr(sender, Messages.SIZE_SET, npc.getName(), size); - } - - @Command( - aliases = { "npc" }, - usage = "spawn [id]", - desc = "Spawn an existing NPC", - modifiers = { "spawn" }, - min = 2, - max = 2, - permission = "npc.spawn") - @Requirements(ownership = true) - public void spawn(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - NPC respawn = npcRegistry.getById(args.getInteger(1)); - if (respawn == null) - throw new CommandException(Messages.NO_NPC_WITH_ID_FOUND, args.getInteger(1)); - if (respawn.isSpawned()) - throw new CommandException(Messages.NPC_ALREADY_SPAWNED, respawn.getName()); - - Location location = respawn.getTrait(CurrentLocation.class).getLocation(); - if (location == null) { - if (args.getSenderLocation() == null) - throw new CommandException(Messages.NO_STORED_SPAWN_LOCATION); - - location = args.getSenderLocation(); - } - if (respawn.spawn(location)) { - selector.select(sender, respawn); - Messaging.sendTr(sender, Messages.NPC_SPAWNED, respawn.getName()); - } - } - - @Command( - aliases = { "npc" }, - usage = "speak message to speak --target npcid|player_name --type vocal_type", - desc = "Uses the NPCs SpeechController to talk", - modifiers = { "speak" }, - min = 2, - permission = "npc.speak") - public void speak(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - - String type = npc.getTrait(Speech.class).getDefaultVocalChord(); - String message = StringHelper.parseColors(args.getJoinedStrings(1)); - - if (message.length() <= 0) { - Messaging.send(sender, "Default Vocal Chord for " + npc.getName() + ": " + npc.getTrait(Speech.class).getDefaultVocalChord()); - return; - } - - SpeechContext context = new SpeechContext(message); - - if (args.hasValueFlag("target")) { - if (args.getFlag("target").matches("\\d+")) { - NPC target = CitizensAPI.getNPCRegistry().getById(Integer.valueOf(args.getFlag("target"))); - if ( target != null) - context.addRecipient(target.getBukkitEntity()); - } else { - Player player = Bukkit.getPlayer(args.getFlag("target")); - if (player != null) - context.addRecipient(player); - } - } - - if (args.hasValueFlag("type")) { - if (CitizensAPI.getSpeechFactory().isRegistered(args.getFlag("type"))) - type = args.getFlag("type"); - } - - npc.getDefaultSpeechController().speak(context, type); - } - - @Command( - aliases = { "npc" }, - usage = "speed [speed]", - desc = "Sets the movement speed of an NPC as a percentage", - modifiers = { "speed" }, - min = 2, - max = 2, - permission = "npc.speed") - public void speed(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - float newSpeed = (float) Math.abs(args.getDouble(1)); - if (newSpeed >= Setting.MAX_SPEED.asDouble()) - throw new CommandException(Messages.SPEED_MODIFIER_ABOVE_LIMIT); - npc.getNavigator().getDefaultParameters().speedModifier(newSpeed); - - Messaging.sendTr(sender, Messages.SPEED_MODIFIER_SET, newSpeed); - } - - @Command( - aliases = { "npc" }, - usage = "tp", - desc = "Teleport to a NPC", - modifiers = { "tp", "teleport" }, - min = 1, - max = 1, - permission = "npc.tp") - public void tp(CommandContext args, Player player, NPC npc) { - Location to = npc.getTrait(CurrentLocation.class).getLocation(); - player.teleport(to, TeleportCause.COMMAND); - Messaging.sendTr(player, Messages.TELEPORTED_TO_NPC, npc.getName()); - } - - @Command(aliases = { "npc" }, usage = "tphere", desc = "Teleport a NPC to your location", modifiers = { - "tphere", "tph", "move" }, min = 1, max = 1, permission = "npc.tphere") - public void tphere(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - if (args.getSenderLocation() == null) - throw new ServerCommandException(); - // Spawn the NPC if it isn't spawned to prevent NPEs - if (!npc.isSpawned()) { - npc.spawn(args.getSenderLocation()); - } else - npc.getBukkitEntity().teleport(args.getSenderLocation(), TeleportCause.COMMAND); - Messaging.sendTr(sender, Messages.NPC_TELEPORTED, npc.getName()); - } - - @Command( - aliases = { "npc" }, - usage = "type [type]", - desc = "Sets an NPC's entity type", - modifiers = { "type" }, - min = 2, - max = 2, - permission = "npc.type") - public void type(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - EntityType type = Util.matchEntityType(args.getString(1)); - if (type == null) - throw new CommandException(Messages.INVALID_ENTITY_TYPE, args.getString(1)); - ((CitizensNPC) npc).setEntityController(EntityControllers.createForType(type)); - Messaging.sendTr(sender, Messages.ENTITY_TYPE_SET, npc.getName(), args.getString(1)); - } - - @Command( - aliases = { "npc" }, - usage = "vulnerable (-t)", - desc = "Toggles an NPC's vulnerability", - modifiers = { "vulnerable" }, - min = 1, - max = 1, - flags = "t", - permission = "npc.vulnerable") - public void vulnerable(CommandContext args, CommandSender sender, NPC npc) { - boolean vulnerable = !npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true); - if (args.hasFlag('t')) { - npc.data().set(NPC.DEFAULT_PROTECTED_METADATA, vulnerable); - } else { - npc.data().setPersistent(NPC.DEFAULT_PROTECTED_METADATA, vulnerable); - } - String key = vulnerable ? Messages.VULNERABLE_STOPPED : Messages.VULNERABLE_SET; - Messaging.sendTr(sender, key, npc.getName()); - } - - @Command( - aliases = { "npc" }, - usage = "zombiemod (-b(aby), -v(illager))", - desc = "Sets a zombie NPC to be a baby or villager", - modifiers = { "zombie", "zombiemod" }, - flags = "bv", - min = 1, - max = 1, - permission = "npc.zombiemodifier") - @Requirements(selected = true, ownership = true, types = EntityType.ZOMBIE) - public void zombieModifier(CommandContext args, CommandSender sender, NPC npc) throws CommandException { - ZombieModifier trait = npc.getTrait(ZombieModifier.class); - if (args.hasFlag('b')) { - boolean isBaby = trait.toggleBaby(); - Messaging.sendTr(sender, isBaby ? Messages.ZOMBIE_BABY_SET : Messages.ZOMBIE_BABY_UNSET, - npc.getName()); - } - if (args.hasFlag('v')) { - boolean isVillager = trait.toggleVillager(); - Messaging.sendTr(sender, isVillager ? Messages.ZOMBIE_VILLAGER_SET - : Messages.ZOMBIE_VILLAGER_UNSET, npc.getName()); - } - } + private final NPCRegistry npcRegistry; + private final NPCSelector selector; + + public NPCCommands(Citizens plugin) { + npcRegistry = CitizensAPI.getNPCRegistry(); + selector = plugin.getNPCSelector(); + } + + @Command( + aliases = { "npc" }, + usage = "age [age] (-l)", + desc = "Set the age of a NPC", + help = Messages.COMMAND_AGE_HELP, + flags = "l", + modifiers = { "age" }, + min = 1, + max = 2, + permission = "npc.age") + @Requirements(selected = true, ownership = true, types = { EntityType.CHICKEN, EntityType.COW, + EntityType.OCELOT, EntityType.PIG, EntityType.SHEEP, EntityType.VILLAGER, EntityType.WOLF }) + 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.sendTr(sender, trait.toggle() ? Messages.AGE_LOCKED : Messages.AGE_UNLOCKED); + } + if (args.argsLength() <= 1) { + if (!toggleLock) + trait.describe(sender); + return; + } + int age = 0; + try { + age = args.getInteger(1); + if (age < -24000 || age > 0) + throw new CommandException(Messages.INVALID_AGE); + Messaging.sendTr(sender, Messages.AGE_SET_NORMAL, npc.getName(), age); + } catch (NumberFormatException ex) { + if (args.getString(1).equalsIgnoreCase("baby")) { + age = -24000; + Messaging.sendTr(sender, Messages.AGE_SET_BABY, npc.getName()); + } else if (args.getString(1).equalsIgnoreCase("adult")) { + age = 0; + Messaging.sendTr(sender, Messages.AGE_SET_ADULT, npc.getName()); + } else + throw new CommandException(Messages.INVALID_AGE); + } + + trait.setAge(age); + } + + @Command( + aliases = { "npc" }, + usage = "anchor (--save [name]|--assume [name]|--remove [name]) (-a)(-c)", + desc = "Changes/Saves/Lists NPC's location anchor(s)", + flags = "ac", + modifiers = { "anchor" }, + min = 1, + max = 2, + permission = "npc.anchor") + @Requirements(selected = true, ownership = true, types = EntityType.PLAYER) + public void anchor(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + Anchors trait = npc.getTrait(Anchors.class); + if (args.hasValueFlag("save")) { + if (args.getFlag("save").isEmpty()) + throw new CommandException(Messages.INVALID_ANCHOR_NAME); + + if (args.getSenderLocation() == null) + throw new ServerCommandException(); + + if (args.hasFlag('c')) { + if (trait.addAnchor(args.getFlag("save"), args.getSenderTargetBlockLocation())) { + Messaging.sendTr(sender, Messages.ANCHOR_ADDED); + } else + throw new CommandException(Messages.ANCHOR_ALREADY_EXISTS, args.getFlag("save")); + } + else { + if (trait.addAnchor(args.getFlag("save"), args.getSenderLocation())) { + Messaging.sendTr(sender, Messages.ANCHOR_ADDED); + } else + throw new CommandException(Messages.ANCHOR_ALREADY_EXISTS, args.getFlag("save")); + } + } else if (args.hasValueFlag("assume")) { + if (args.getFlag("assume").isEmpty()) + throw new CommandException(Messages.INVALID_ANCHOR_NAME); + + Anchor anchor = trait.getAnchor(args.getFlag("assume")); + if (anchor == null) + throw new CommandException(Messages.ANCHOR_MISSING, args.getFlag("assume")); + npc.getBukkitEntity().teleport(anchor.getLocation()); + } else if (args.hasValueFlag("remove")) { + if (args.getFlag("remove").isEmpty()) + throw new CommandException(Messages.INVALID_ANCHOR_NAME); + if (trait.removeAnchor(trait.getAnchor(args.getFlag("remove")))) + Messaging.sendTr(sender, Messages.ANCHOR_REMOVED); + else + throw new CommandException(Messages.ANCHOR_MISSING, args.getFlag("remove")); + } else if (!args.hasFlag('a')) { + 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); + } + + int page = args.getInteger(1, 1); + if (!paginator.sendPage(sender, page)) + throw new CommandException(Messages.COMMAND_PAGE_MISSING); + } + + // Assume Player's position + if (!args.hasFlag('a')) + return; + if (sender instanceof ConsoleCommandSender) + throw new ServerCommandException(); + npc.getBukkitEntity().teleport(args.getSenderLocation()); + } + + @Command( + aliases = { "npc" }, + usage = "behaviour [scripts] (-r)", + desc = "Sets the behaviour of a NPC", + help = Messages.BEHAVIOUR_HELP, + modifiers = { "behaviour", "behavior", "ai" }, + flags = "r", + min = 2, + permission = "npc.behaviour") + 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).removeScripts(files); + Messaging.sendTr(sender, Messages.BEHAVIOURS_REMOVED); + } else { + npc.getTrait(Behaviour.class).addScripts(files); + Messaging.sendTr(sender, Messages.BEHAVIOURS_ADDED); + } + } + + @Command( + aliases = { "npc" }, + usage = "controllable|control -f", + desc = "Toggles whether the NPC can be ridden and controlled", + modifiers = { "controllable", "control" }, + min = 1, + max = 1, + flags = "f") + public void controllable(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + if ((npc.isSpawned() && !sender.hasPermission("citizens.npc.controllable." + + npc.getBukkitEntity().getType().toString().toLowerCase())) + || !sender.hasPermission("citizens.npc.controllable")) + throw new NoPermissionsException(); + Controllable trait = npc.getTrait(Controllable.class); + boolean enabled = trait.toggle(); + String key = enabled ? Messages.CONTROLLABLE_SET : Messages.CONTROLLABLE_REMOVED; + Messaging.sendTr(sender, key, npc.getName()); + } + + @Command( + aliases = { "npc" }, + usage = "copy (--name newname)", + desc = "Copies an NPC", + modifiers = { "copy" }, + min = 1, + max = 1, + permission = "npc.copy") + public void copy(CommandContext args, CommandSender sender, NPC npc) { + EntityType type = npc.getTrait(MobType.class).getType(); + String name = args.getFlag("name", npc.getFullName()); + CitizensNPC copy = (CitizensNPC) npcRegistry.createNPC(type, name); + CitizensNPC from = (CitizensNPC) npc; + + DataKey key = new MemoryDataKey(); + from.save(key); + copy.load(key); + + if (copy.isSpawned() && args.getSenderLocation() != null) { + Location location = args.getSenderLocation(); + copy.getBukkitEntity().teleport(location); + copy.getTrait(CurrentLocation.class).setLocation(location); + } + + for (Trait trait : copy.getTraits()) + trait.onCopy(); + Messaging.sendTr(sender, Messages.NPC_COPIED, npc.getName()); + selector.select(sender, copy); + } + + @Command( + aliases = { "npc" }, + usage = "create [name] ((-b,u) --at (x:y:z:world) --type (type) --trait ('trait1, trait2...') --b (behaviours))", + desc = "Create a new NPC", + flags = "bu", + modifiers = { "create" }, + min = 2, + permission = "npc.create") + @Requirements + public void create(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + String name = StringHelper.parseColors(args.getJoinedStrings(1)); + if (name.length() > 16) { + Messaging.sendErrorTr(sender, Messages.NPC_NAME_TOO_LONG); + name = name.substring(0, 15); + } + if (name.length() <= 0) + throw new CommandException(); + + EntityType type = EntityType.PLAYER; + if (args.hasValueFlag("type")) { + String inputType = args.getFlag("type"); + type = Util.matchEntityType(inputType); + if (type == null) { + Messaging.sendErrorTr(sender, Messages.NPC_CREATE_INVALID_MOBTYPE, inputType); + type = EntityType.PLAYER; + } else if (!LivingEntity.class.isAssignableFrom(type.getEntityClass())) { + Messaging.sendErrorTr(sender, Messages.NOT_LIVING_MOBTYPE, type); + type = EntityType.PLAYER; + } + } + + npc = npcRegistry.createNPC(type, name); + String msg = "You created [[" + npc.getName() + "]]"; + + int age = 0; + if (args.hasFlag('b')) { + if (!Ageable.class.isAssignableFrom(type.getEntityClass())) + Messaging.sendErrorTr(sender, Messages.MOBTYPE_CANNOT_BE_AGED, type.name().toLowerCase() + .replace("_", "-")); + else { + age = -24000; + msg += " as a baby"; + } + } + + if (args.hasValueFlag("b") || args.hasValueFlag("behaviours") || args.hasValueFlag("behaviors")) { + String behaviours = args.getFlag("b", args.getFlag("behaviours", args.getFlag("behaviors"))); + npc.getTrait(Behaviour.class).addScripts(Splitter.on(',').split(behaviours)); + msg += " with the specified behaviours"; + } + + msg += "."; + + // Initialize necessary traits + if (!Setting.SERVER_OWNS_NPCS.asBoolean()) + npc.getTrait(Owner.class).setOwner(sender.getName()); + npc.getTrait(MobType.class).setType(type); + + Location spawnLoc = null; + if (sender instanceof Player) { + spawnLoc = args.getSenderLocation(); + PlayerCreateNPCEvent event = new PlayerCreateNPCEvent((Player) sender, npc); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + npc.destroy(); + String reason = "Couldn't create NPC."; + if (!event.getCancelReason().isEmpty()) + reason += " Reason: " + event.getCancelReason(); + throw new CommandException(reason); + } + } else if (sender instanceof BlockCommandSender) { + spawnLoc = args.getSenderLocation(); + } + + if (args.hasValueFlag("at")) { + String[] parts = Iterables.toArray(Splitter.on(':').split(args.getFlag("at")), String.class); + if (parts.length > 0) { + String worldName = args.getSenderLocation() != null ? args.getSenderLocation().getWorld() + .getName() : ""; + int x = 0, y = 0, z = 0; + float yaw = 0F, pitch = 0F; + switch (parts.length) { + case 6: + pitch = Float.parseFloat(parts[5]); + case 5: + yaw = Float.parseFloat(parts[4]); + case 4: + worldName = parts[3]; + case 3: + x = Integer.parseInt(parts[0]); + y = Integer.parseInt(parts[1]); + z = Integer.parseInt(parts[2]); + break; + default: + throw new CommandException(Messages.INVALID_SPAWN_LOCATION); + } + World world = Bukkit.getWorld(worldName); + if (world == null) + throw new CommandException(Messages.INVALID_SPAWN_LOCATION); + spawnLoc = new Location(world, x, y, z, yaw, pitch); + } else { + Player search = Bukkit.getPlayerExact(args.getFlag("at")); + if (search == null) + throw new CommandException(Messages.PLAYER_NOT_FOUND_FOR_SPAWN); + spawnLoc = search.getLocation(); + } + } + if (spawnLoc == null) { + npc.destroy(); + throw new CommandException(Messages.INVALID_SPAWN_LOCATION); + } + + if (!args.hasFlag('u')) + npc.spawn(spawnLoc); + + if (args.hasValueFlag("trait")) { + Iterable parts = Splitter.on(',').trimResults().split(args.getFlag("trait")); + StringBuilder builder = new StringBuilder(); + for (String tr : parts) { + Trait trait = CitizensAPI.getTraitFactory().getTrait(tr); + if (trait == null) + continue; + npc.addTrait(trait); + builder.append(StringHelper.wrap(tr) + ", "); + } + if (builder.length() > 0) + builder.delete(builder.length() - 2, builder.length()); + msg += " with traits " + builder.toString(); + } + + if (args.hasValueFlag("template")) { + Iterable parts = Splitter.on(',').trimResults().split(args.getFlag("template")); + StringBuilder builder = new StringBuilder(); + for (String part : parts) { + Template template = Template.byName(part); + if (template == null) + continue; + template.apply(npc); + builder.append(StringHelper.wrap(part) + ", "); + } + if (builder.length() > 0) + builder.delete(builder.length() - 2, builder.length()); + msg += " with templates " + builder.toString(); + } + + // Set age after entity spawns + if (npc.getBukkitEntity() instanceof Ageable) + npc.getTrait(Age.class).setAge(age); + selector.select(sender, npc); + Messaging.send(sender, msg); + } + + @Command( + aliases = { "npc" }, + usage = "despawn (id)", + desc = "Despawn a NPC", + modifiers = { "despawn" }, + min = 1, + max = 2, + permission = "npc.despawn") + @Requirements + public void despawn(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + if (npc == null || args.argsLength() == 2) { + if (args.argsLength() < 2) + throw new CommandException(Messages.COMMAND_MUST_HAVE_SELECTED); + int id = args.getInteger(1); + npc = CitizensAPI.getNPCRegistry().getById(id); + if (npc == null) + throw new CommandException(Messages.NO_NPC_WITH_ID_FOUND, id); + } + npc.getTrait(Spawned.class).setSpawned(false); + npc.despawn(); + Messaging.sendTr(sender, Messages.NPC_DESPAWNED, npc.getName()); + } + + @Command( + aliases = { "npc" }, + usage = "gamemode [gamemode]", + desc = "Changes the gamemode", + modifiers = { "gravity" }, + min = 1, + max = 2, + permission = "npc.gravity") + @Requirements(selected = true, ownership = true, types = { EntityType.PLAYER }) + public void gamemode(CommandContext args, CommandSender sender, NPC npc) { + Player player = (Player) npc.getBukkitEntity(); + if (args.argsLength() == 1) { + Messaging.sendTr(sender, Messages.GAMEMODE_DESCRIBE, npc.getName(), player.getGameMode().name() + .toLowerCase()); + return; + } + GameMode mode = null; + try { + int value = args.getInteger(1); + mode = GameMode.getByValue(value); + } catch (NumberFormatException ex) { + try { + mode = GameMode.valueOf(args.getString(1)); + } catch (IllegalArgumentException e) { + } + } + if (mode == null) { + Messaging.sendErrorTr(sender, Messages.GAMEMODE_INVALID, args.getString(1)); + return; + } + player.setGameMode(mode); + Messaging.sendTr(sender, Messages.GAMEMODE_SET, mode.name().toLowerCase()); + } + + @Command( + aliases = { "npc" }, + usage = "gravity", + desc = "Toggles gravity", + modifiers = { "gravity" }, + min = 1, + max = 1, + permission = "npc.gravity") + public void gravity(CommandContext args, CommandSender sender, NPC npc) { + boolean enabled = npc.getTrait(Gravity.class).toggle(); + String key = enabled ? Messages.GRAVITY_ENABLED : Messages.GRAVITY_DISABLED; + Messaging.sendTr(sender, key, npc.getName()); + } + + @Command( + aliases = { "npc" }, + usage = "list (page) ((-a) --owner (owner) --type (type) --char (char))", + desc = "List NPCs", + flags = "a", + modifiers = { "list" }, + min = 1, + max = 2, + permission = "npc.list") + @Requirements + public void list(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + List npcs = new ArrayList(); + + if (args.hasFlag('a')) { + for (NPC add : npcRegistry) + npcs.add(add); + } else if (args.getValueFlags().size() == 0 && sender instanceof Player) { + for (NPC add : npcRegistry) { + if (!npcs.contains(add) && add.getTrait(Owner.class).isOwnedBy(sender)) + npcs.add(add); + } + } else { + if (args.hasValueFlag("owner")) { + String name = args.getFlag("owner"); + for (NPC add : npcRegistry) { + if (!npcs.contains(add) && add.getTrait(Owner.class).isOwnedBy(name)) + npcs.add(add); + } + } + + if (args.hasValueFlag("type")) { + EntityType type = Util.matchEntityType(args.getFlag("type")); + + if (type == null) + throw new CommandException(Messages.COMMAND_INVALID_MOBTYPE, type); + + for (NPC add : npcRegistry) { + if (!npcs.contains(add) && add.getTrait(MobType.class).getType() == type) + npcs.add(add); + } + } + } + + Paginator paginator = new Paginator().header("NPCs"); + paginator.addLine("Key: ID Name"); + for (int i = 0; i < npcs.size(); i += 2) { + String line = "" + npcs.get(i).getId() + " " + npcs.get(i).getName(); + if (npcs.size() >= i + 2) + line += " " + "" + npcs.get(i + 1).getId() + " " + npcs.get(i + 1).getName(); + paginator.addLine(line); + } + + int page = args.getInteger(1, 1); + if (!paginator.sendPage(sender, page)) + throw new CommandException(Messages.COMMAND_PAGE_MISSING); + } + + @Command( + aliases = { "npc" }, + usage = "lookclose", + desc = "Toggle whether a NPC will look when a player is near", + modifiers = { "lookclose", "look", "rotate" }, + min = 1, + max = 1, + permission = "npc.lookclose") + public void lookClose(CommandContext args, CommandSender sender, NPC npc) { + Messaging.sendTr(sender, npc.getTrait(LookClose.class).toggle() ? Messages.LOOKCLOSE_SET + : Messages.LOOKCLOSE_STOPPED, npc.getName()); + } + + @Command( + aliases = { "npc" }, + usage = "mount", + desc = "Mounts a controllable NPC", + modifiers = { "mount" }, + min = 1, + max = 1, + permission = "npc.controllable") + public void mount(CommandContext args, Player player, NPC npc) { + boolean enabled = npc.hasTrait(Controllable.class) && npc.getTrait(Controllable.class).isEnabled(); + if (!enabled) { + Messaging.sendTr(player, Messages.NPC_NOT_CONTROLLABLE, npc.getName()); + return; + } + boolean success = npc.getTrait(Controllable.class).mount(player); + if (!success) + Messaging.sendTr(player, Messages.FAILED_TO_MOUNT_NPC, npc.getName()); + } + + @Command( + aliases = { "npc" }, + usage = "moveto x:y:z:world | x y z world", + desc = "Teleports a NPC to a given location", + modifiers = "moveto", + min = 1, + permission = "npc.moveto") + public void moveto(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + // Spawn the NPC if it isn't spawned to prevent NPEs + if (!npc.isSpawned()) + npc.spawn(npc.getTrait(CurrentLocation.class).getLocation()); + Location current = npc.getBukkitEntity().getLocation(); + Location to; + if (args.argsLength() > 1) { + String[] parts = Iterables.toArray(Splitter.on(':').split(args.getJoinedStrings(1, ':')), + String.class); + if (parts.length != 4 && parts.length != 3) + throw new CommandException(Messages.MOVETO_FORMAT); + double x = Double.parseDouble(parts[0]); + double y = Double.parseDouble(parts[1]); + double z = Double.parseDouble(parts[2]); + World world = parts.length == 4 ? Bukkit.getWorld(parts[3]) : current.getWorld(); + if (world == null) + throw new CommandException(Messages.WORLD_NOT_FOUND); + to = new Location(world, x, y, z, current.getYaw(), current.getPitch()); + } else { + to = current.clone(); + if (args.hasValueFlag("x")) + to.setX(args.getFlagDouble("x")); + if (args.hasValueFlag("y")) + to.setY(args.getFlagDouble("y")); + if (args.hasValueFlag("z")) + to.setZ(args.getFlagDouble("z")); + if (args.hasValueFlag("yaw")) + to.setYaw((float) args.getFlagDouble("yaw")); + if (args.hasValueFlag("pitch")) + to.setPitch((float) args.getFlagDouble("pitch")); + if (args.hasValueFlag("world")) { + World world = Bukkit.getWorld(args.getFlag("world")); + if (world == null) + throw new CommandException(Messages.WORLD_NOT_FOUND); + to.setWorld(world); + } + } + + npc.getBukkitEntity().teleport(to, TeleportCause.COMMAND); + + Messaging.sendTr(sender, Messages.MOVETO_TELEPORTED, npc.getName(), to); + } + + @Command(aliases = { "npc" }, desc = "Show basic NPC information", max = 0, permission = "npc.info") + public void npc(CommandContext args, CommandSender sender, NPC npc) { + Messaging.send(sender, StringHelper.wrapHeader(npc.getName())); + Messaging.send(sender, " ID: " + npc.getId()); + Messaging.send(sender, " Type: " + npc.getTrait(MobType.class).getType()); + if (npc.isSpawned()) { + Location loc = npc.getBukkitEntity().getLocation(); + String format = " Spawned at %d, %d, %d in world %s"; + Messaging.send(sender, String.format(format, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), + loc.getWorld().getName())); + } + Messaging.send(sender, " Traits"); + for (Trait trait : npc.getTraits()) { + if (CitizensAPI.getTraitFactory().isInternalTrait(trait)) + continue; + String message = " - " + trait.getName(); + Messaging.send(sender, message); + } + } + + @Command( + aliases = { "npc" }, + usage = "owner [name]", + desc = "Set the owner of an NPC", + modifiers = { "owner" }, + min = 1, + max = 2, + permission = "npc.owner") + public void owner(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + Owner ownerTrait = npc.getTrait(Owner.class); + if (args.argsLength() == 1) { + Messaging.sendTr(sender, Messages.NPC_OWNER, npc.getName(), ownerTrait.getOwner()); + return; + } + String name = args.getString(1); + if (ownerTrait.isOwnedBy(name)) + throw new CommandException(Messages.ALREADY_OWNER, name, npc.getName()); + ownerTrait.setOwner(name); + boolean serverOwner = name.equalsIgnoreCase(Owner.SERVER); + Messaging.sendTr(sender, serverOwner ? Messages.OWNER_SET_SERVER : Messages.OWNER_SET, npc.getName(), + name); + } + + @Command( + aliases = { "npc" }, + usage = "pathrange [range]", + desc = "Sets an NPC's pathfinding range", + modifiers = { "pathrange", "pathfindingrange", "prange" }, + min = 2, + max = 2, + permission = "npc.pathfindingrange") + public void pathfindingRange(CommandContext args, CommandSender sender, NPC npc) { + double range = Math.max(1, args.getDouble(1)); + npc.getNavigator().getDefaultParameters().range((float) range); + Messaging.sendTr(sender, Messages.PATHFINDING_RANGE_SET, range); + } + + @Command( + aliases = { "npc" }, + usage = "playerlist (-a,r)", + desc = "Sets whether the NPC is put in the playerlist", + modifiers = { "playerlist" }, + min = 1, + max = 1, + flags = "ar", + permission = "npc.playerlist") + @Requirements(types = EntityType.PLAYER) + public void playerlist(CommandContext args, CommandSender sender, NPC npc) { + boolean remove = !npc.data().get("removefromplayerlist", + Setting.REMOVE_PLAYERS_FROM_PLAYER_LIST.asBoolean()); + if (args.hasFlag('a')) + remove = false; + else if (args.hasFlag('r')) + remove = true; + npc.data().setPersistent("removefromplayerlist", remove); + if (npc.isSpawned()) + NMS.addOrRemoveFromPlayerList(npc.getBukkitEntity(), remove); + Messaging.sendTr(sender, remove ? Messages.REMOVED_FROM_PLAYERLIST : Messages.ADDED_TO_PLAYERLIST, + npc.getName()); + } + + @Command( + aliases = { "npc" }, + usage = "pose (--save [name]|--assume [name]|--remove [name]) (-a)", + desc = "Changes/Saves/Lists NPC's head pose(s)", + flags = "a", + modifiers = { "pose" }, + min = 1, + max = 2, + permission = "npc.pose") + @Requirements(selected = true, ownership = true, types = EntityType.PLAYER) + public void pose(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + Poses trait = npc.getTrait(Poses.class); + if (args.hasValueFlag("save")) { + if (args.getFlag("save").isEmpty()) + throw new CommandException(Messages.INVALID_POSE_NAME); + + if (args.getSenderLocation() == null) + throw new ServerCommandException(); + + if (trait.addPose(args.getFlag("save"), args.getSenderLocation())) { + Messaging.sendTr(sender, Messages.POSE_ADDED); + } else + throw new CommandException(Messages.POSE_ALREADY_EXISTS, args.getFlag("assume")); + } else if (args.hasValueFlag("assume")) { + String pose = args.getFlag("assume"); + if (pose.isEmpty()) + throw new CommandException(Messages.INVALID_POSE_NAME); + + if (!trait.hasPose(pose)) + throw new CommandException(Messages.POSE_MISSING, pose); + trait.assumePose(pose); + } else if (args.hasValueFlag("remove")) { + if (args.getFlag("remove").isEmpty()) + throw new CommandException(Messages.INVALID_POSE_NAME); + if (trait.removePose(args.getFlag("remove"))) { + Messaging.sendTr(sender, Messages.POSE_REMOVED); + } else + throw new CommandException(Messages.POSE_MISSING, args.getFlag("remove")); + } else if (!args.hasFlag('a')) { + trait.describe(sender, args.getInteger(1, 1)); + } + + // Assume Player's pose + if (!args.hasFlag('a')) + return; + if (args.getSenderLocation() == null) + throw new ServerCommandException(); + Location location = args.getSenderLocation(); + trait.assumePose(location); + } + + @Command( + aliases = { "npc" }, + usage = "power", + desc = "Toggle a creeper NPC as powered", + modifiers = { "power" }, + min = 1, + max = 1, + permission = "npc.power") + @Requirements(selected = true, ownership = true, types = { EntityType.CREEPER }) + public void power(CommandContext args, CommandSender sender, NPC npc) { + Messaging.sendTr(sender, npc.getTrait(Powered.class).toggle() ? Messages.POWERED_SET + : Messages.POWERED_STOPPED); + } + + @Command( + aliases = { "npc" }, + usage = "profession|prof [profession]", + desc = "Set a NPC's 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 { + parsed = Profession.valueOf(profession.toUpperCase()); + } catch (IllegalArgumentException ex) { + throw new CommandException(Messages.INVALID_PROFESSION); + } + npc.getTrait(VillagerProfession.class).setProfession(parsed); + Messaging.sendTr(sender, Messages.PROFESSION_SET, npc.getName(), profession); + } + + @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) { + if (!args.getString(1).equalsIgnoreCase("all")) + throw new CommandException(Messages.REMOVE_INCORRECT_SYNTAX); + if (!sender.hasPermission("citizens.admin.remove.all") && !sender.hasPermission("citizens.admin")) + throw new NoPermissionsException(); + npcRegistry.deregisterAll(); + Messaging.sendTr(sender, Messages.REMOVED_ALL_NPCS); + return; + } + if (!(sender instanceof Player)) + throw new CommandException(Messages.COMMAND_MUST_BE_INGAME); + Player player = (Player) sender; + if (npc == null) + throw new CommandException(Messages.COMMAND_MUST_HAVE_SELECTED); + if (!npc.getTrait(Owner.class).isOwnedBy(player)) + throw new CommandException(Messages.COMMAND_MUST_BE_OWNER); + if (!player.hasPermission("citizens.npc.remove") && !player.hasPermission("citizens.admin")) + throw new NoPermissionsException(); + npc.destroy(); + Messaging.sendTr(player, Messages.NPC_REMOVED, npc.getName()); + } + + @Command( + aliases = { "npc" }, + usage = "rename [name]", + desc = "Rename a NPC", + modifiers = { "rename" }, + min = 2, + permission = "npc.rename") + public void rename(CommandContext args, CommandSender sender, NPC npc) { + String oldName = npc.getName(); + String newName = args.getJoinedStrings(1); + if (newName.length() > 16) { + Messaging.sendErrorTr(sender, Messages.NPC_NAME_TOO_LONG); + newName = newName.substring(0, 15); + } + Location prev = npc.isSpawned() ? npc.getBukkitEntity().getLocation() : null; + npc.despawn(); + npc.setName(newName); + if (prev != null) + npc.spawn(prev); + + Messaging.sendTr(sender, Messages.NPC_RENAMED, oldName, newName); + } + + @Command( + aliases = { "npc" }, + usage = "select|sel [id|name] (--r range)", + desc = "Select a NPC with the given ID or name", + modifiers = { "select", "sel" }, + min = 1, + max = 2, + permission = "npc.select") + @Requirements + public void select(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + NPC toSelect = null; + if (args.argsLength() <= 1) { + if (!(sender instanceof Player)) + throw new ServerCommandException(); + double range = Math.abs(args.getFlagDouble("r", 10)); + Entity player = (Player) sender; + final Location location = args.getSenderLocation(); + List search = player.getNearbyEntities(range, range, range); + Collections.sort(search, new Comparator() { + @Override + public int compare(Entity o1, Entity o2) { + double d = o1.getLocation().distanceSquared(location) + - o2.getLocation().distanceSquared(location); + return d > 0 ? 1 : d < 0 ? -1 : 0; + } + }); + for (Entity possibleNPC : search) { + NPC test = npcRegistry.getNPC(possibleNPC); + if (test == null) + continue; + toSelect = test; + break; + } + } else { + try { + int id = args.getInteger(1); + toSelect = npcRegistry.getById(id); + } catch (NumberFormatException ex) { + String name = args.getString(1); + List possible = Lists.newArrayList(); + for (NPC test : npcRegistry) { + if (test.getName().equalsIgnoreCase(name)) + possible.add(test); + } + if (possible.size() == 1) + toSelect = possible.get(0); + else if (possible.size() > 1) { + SelectionPrompt.start(selector, (Player) sender, possible); + return; + } + } + } + if (toSelect == null || !toSelect.getTrait(Spawned.class).shouldSpawn()) + throw new CommandException(Messages.NPC_NOT_FOUND); + if (npc != null && toSelect.getId() == npc.getId()) + throw new CommandException(Messages.NPC_ALREADY_SELECTED); + selector.select(sender, toSelect); + Messaging.sendWithNPC(sender, Setting.SELECTION_MESSAGE.asString(), toSelect); + } + + @Command( + aliases = { "npc" }, + usage = "skeletontype [type]", + desc = "Sets the NPC's skeleton type", + modifiers = { "skeletontype", "sktype" }, + min = 2, + max = 2, + permission = "npc.skeletontype") + @Requirements(selected = true, ownership = true, types = EntityType.SKELETON) + public void skeletonType(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + SkeletonType type = (type = SkeletonType.getType(args.getInteger(1))) == null ? SkeletonType + .valueOf(args.getString(1)) : type; + if (type == null) + throw new CommandException(Messages.INVALID_SKELETON_TYPE); + npc.getTrait(NPCSkeletonType.class).setType(type); + Messaging.sendTr(sender, Messages.SKELETON_TYPE_SET, npc.getName(), type); + } + + @Command( + aliases = { "npc" }, + usage = "size [size]", + desc = "Sets the NPC's size", + modifiers = { "size" }, + min = 1, + max = 2, + permission = "npc.size") + @Requirements(selected = true, ownership = true, types = { EntityType.MAGMA_CUBE, EntityType.SLIME }) + public void slimeSize(CommandContext args, CommandSender sender, NPC npc) { + SlimeSize trait = npc.getTrait(SlimeSize.class); + if (args.argsLength() <= 1) { + trait.describe(sender); + return; + } + int size = Math.max(1, args.getInteger(1)); + trait.setSize(size); + Messaging.sendTr(sender, Messages.SIZE_SET, npc.getName(), size); + } + + @Command( + aliases = { "npc" }, + usage = "spawn [id]", + desc = "Spawn an existing NPC", + modifiers = { "spawn" }, + min = 2, + max = 2, + permission = "npc.spawn") + @Requirements(ownership = true) + public void spawn(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + NPC respawn = npcRegistry.getById(args.getInteger(1)); + if (respawn == null) + throw new CommandException(Messages.NO_NPC_WITH_ID_FOUND, args.getInteger(1)); + if (respawn.isSpawned()) + throw new CommandException(Messages.NPC_ALREADY_SPAWNED, respawn.getName()); + + Location location = respawn.getTrait(CurrentLocation.class).getLocation(); + if (location == null) { + if (args.getSenderLocation() == null) + throw new CommandException(Messages.NO_STORED_SPAWN_LOCATION); + + location = args.getSenderLocation(); + } + if (respawn.spawn(location)) { + selector.select(sender, respawn); + Messaging.sendTr(sender, Messages.NPC_SPAWNED, respawn.getName()); + } + } + + @Command( + aliases = { "npc" }, + usage = "speak message to speak --target npcid|player_name --type vocal_type", + desc = "Uses the NPCs SpeechController to talk", + modifiers = { "speak" }, + min = 2, + permission = "npc.speak") + public void speak(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + + String type = npc.getTrait(Speech.class).getDefaultVocalChord(); + String message = StringHelper.parseColors(args.getJoinedStrings(1)); + + if (message.length() <= 0) { + Messaging.send(sender, "Default Vocal Chord for " + npc.getName() + ": " + npc.getTrait(Speech.class).getDefaultVocalChord()); + return; + } + + SpeechContext context = new SpeechContext(message); + + if (args.hasValueFlag("target")) { + if (args.getFlag("target").matches("\\d+")) { + NPC target = CitizensAPI.getNPCRegistry().getById(Integer.valueOf(args.getFlag("target"))); + if ( target != null) + context.addRecipient(target.getBukkitEntity()); + } else { + Player player = Bukkit.getPlayer(args.getFlag("target")); + if (player != null) + context.addRecipient(player); + } + } + + if (args.hasValueFlag("type")) { + if (CitizensAPI.getSpeechFactory().isRegistered(args.getFlag("type"))) + type = args.getFlag("type"); + } + + npc.getDefaultSpeechController().speak(context, type); + } + + @Command( + aliases = { "npc" }, + usage = "speed [speed]", + desc = "Sets the movement speed of an NPC as a percentage", + modifiers = { "speed" }, + min = 2, + max = 2, + permission = "npc.speed") + public void speed(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + float newSpeed = (float) Math.abs(args.getDouble(1)); + if (newSpeed >= Setting.MAX_SPEED.asDouble()) + throw new CommandException(Messages.SPEED_MODIFIER_ABOVE_LIMIT); + npc.getNavigator().getDefaultParameters().speedModifier(newSpeed); + + Messaging.sendTr(sender, Messages.SPEED_MODIFIER_SET, newSpeed); + } + + @Command( + aliases = { "npc" }, + usage = "tp", + desc = "Teleport to a NPC", + modifiers = { "tp", "teleport" }, + min = 1, + max = 1, + permission = "npc.tp") + public void tp(CommandContext args, Player player, NPC npc) { + Location to = npc.getTrait(CurrentLocation.class).getLocation(); + player.teleport(to, TeleportCause.COMMAND); + Messaging.sendTr(player, Messages.TELEPORTED_TO_NPC, npc.getName()); + } + + @Command(aliases = { "npc" }, usage = "tphere", desc = "Teleport a NPC to your location", modifiers = { + "tphere", "tph", "move" }, min = 1, max = 1, permission = "npc.tphere") + public void tphere(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + if (args.getSenderLocation() == null) + throw new ServerCommandException(); + // Spawn the NPC if it isn't spawned to prevent NPEs + if (!npc.isSpawned()) { + npc.spawn(args.getSenderLocation()); + } else + npc.getBukkitEntity().teleport(args.getSenderLocation(), TeleportCause.COMMAND); + Messaging.sendTr(sender, Messages.NPC_TELEPORTED, npc.getName()); + } + + @Command( + aliases = { "npc" }, + usage = "type [type]", + desc = "Sets an NPC's entity type", + modifiers = { "type" }, + min = 2, + max = 2, + permission = "npc.type") + public void type(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + EntityType type = Util.matchEntityType(args.getString(1)); + if (type == null) + throw new CommandException(Messages.INVALID_ENTITY_TYPE, args.getString(1)); + ((CitizensNPC) npc).setEntityController(EntityControllers.createForType(type)); + Messaging.sendTr(sender, Messages.ENTITY_TYPE_SET, npc.getName(), args.getString(1)); + } + + @Command( + aliases = { "npc" }, + usage = "vulnerable (-t)", + desc = "Toggles an NPC's vulnerability", + modifiers = { "vulnerable" }, + min = 1, + max = 1, + flags = "t", + permission = "npc.vulnerable") + public void vulnerable(CommandContext args, CommandSender sender, NPC npc) { + boolean vulnerable = !npc.data().get(NPC.DEFAULT_PROTECTED_METADATA, true); + if (args.hasFlag('t')) { + npc.data().set(NPC.DEFAULT_PROTECTED_METADATA, vulnerable); + } else { + npc.data().setPersistent(NPC.DEFAULT_PROTECTED_METADATA, vulnerable); + } + String key = vulnerable ? Messages.VULNERABLE_STOPPED : Messages.VULNERABLE_SET; + Messaging.sendTr(sender, key, npc.getName()); + } + + @Command( + aliases = { "npc" }, + usage = "zombiemod (-b(aby), -v(illager))", + desc = "Sets a zombie NPC to be a baby or villager", + modifiers = { "zombie", "zombiemod" }, + flags = "bv", + min = 1, + max = 1, + permission = "npc.zombiemodifier") + @Requirements(selected = true, ownership = true, types = EntityType.ZOMBIE) + public void zombieModifier(CommandContext args, CommandSender sender, NPC npc) throws CommandException { + ZombieModifier trait = npc.getTrait(ZombieModifier.class); + if (args.hasFlag('b')) { + boolean isBaby = trait.toggleBaby(); + Messaging.sendTr(sender, isBaby ? Messages.ZOMBIE_BABY_SET : Messages.ZOMBIE_BABY_UNSET, + npc.getName()); + } + if (args.hasFlag('v')) { + boolean isVillager = trait.toggleVillager(); + Messaging.sendTr(sender, isVillager ? Messages.ZOMBIE_VILLAGER_SET + : Messages.ZOMBIE_VILLAGER_UNSET, npc.getName()); + } + } } \ No newline at end of file