From 7141fc8495387fca8b3f82a704ea6d4eaaf43200 Mon Sep 17 00:00:00 2001 From: Jeremy Schroeder Date: Sun, 9 Dec 2012 18:46:37 -0500 Subject: [PATCH] Make location readable with /npc command. Update POM. Bad merge with POM? --- pom.xml | 264 +-- .../command/command/NPCCommands.java | 2068 +++++++++-------- 2 files changed, 1168 insertions(+), 1164 deletions(-) diff --git a/pom.xml b/pom.xml index c65ae5c23..bbd29c67c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,133 +1,133 @@ - - - - 4.0.0 - - net.citizensnpcs - citizens - 2.0.4-SNAPSHOT - Citizens - - - UTF-8 - LATEST - [2.0.4-SNAPSHOT,) - 1.2.19-SNAPSHOT - 1.4.12 - Unknown - Unknown - - - - - bukkit-repo - http://repo.bukkit.org/content/groups/public/ - - - everything - http://repo.citizensnpcs.com - - - vault-repo - http://ci.herocraftonline.com/plugin/repository/everything - - - - - - org.bukkit - craftbukkit - ${craftbukkit.version} - jar - provided - - - net.citizensnpcs - citizensapi - ${citizensapi.version} - jar - compile - - - net.milkbowl.vault - Vault - ${vault.version} - jar - provided - - - org.powermock - powermock-module-junit4 - ${powermock.version} - test - - - org.powermock - powermock-api-mockito - ${powermock.version} - test - - - - http://www.citizensnpcs.com - - jenkins - http://ci.citizensnpcs.com - - - scm:git:git://github.com/CitizensDev/Citizens2.git - scm:git:git:@github.com:CitizensDev/Citizens2.git - https://github.com/CitizensDev/Citizens2/tree/master/ - - - clean package install - ${basedir}/src/main/java - - - - . - true - ${basedir}/src/main/resources - - plugin.yml - *.properties - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - 1.6 - 1.6 - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.3.2 - - - - org.apache.maven.plugins - maven-shade-plugin - 1.4 - - - package - - shade - - - - - - - + + + + 4.0.0 + + net.citizensnpcs + citizens + 2.0.4-SNAPSHOT + Citizens + + + UTF-8 + LATEST + 2.0.4-SNAPSHOT + 1.2.19-SNAPSHOT + 1.4.12 + Unknown + Unknown + + + + + bukkit-repo + http://repo.bukkit.org/content/groups/public/ + + + everything + http://repo.citizensnpcs.com + + + vault-repo + http://ci.herocraftonline.com/plugin/repository/everything + + + + + + org.bukkit + craftbukkit + ${craftbukkit.version} + jar + provided + + + net.citizensnpcs + citizensapi + ${citizensapi.version} + jar + compile + + + net.milkbowl.vault + Vault + ${vault.version} + jar + provided + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + org.powermock + powermock-api-mockito + ${powermock.version} + test + + + + http://www.citizensnpcs.com + + jenkins + http://ci.citizensnpcs.com + + + scm:git:git://github.com/CitizensDev/Citizens2.git + scm:git:git:@github.com:CitizensDev/Citizens2.git + https://github.com/CitizensDev/Citizens2/tree/master/ + + + clean package install + ${basedir}/src/main/java + + + + . + true + ${basedir}/src/main/resources + + plugin.yml + *.properties + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.3.2 + + + + org.apache.maven.plugins + maven-shade-plugin + 1.4 + + + package + + shade + + + + + + + \ 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 b80e7069e..a1feec5cf 100644 --- a/src/main/java/net/citizensnpcs/command/command/NPCCommands.java +++ b/src/main/java/net/citizensnpcs/command/command/NPCCommands.java @@ -1,1033 +1,1037 @@ -package net.citizensnpcs.command.command; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import net.citizensnpcs.Citizens; -import net.citizensnpcs.Settings.Setting; -import net.citizensnpcs.api.CitizensAPI; -import net.citizensnpcs.api.event.PlayerCreateNPCEvent; -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; -import net.citizensnpcs.api.util.DataKey; -import net.citizensnpcs.api.util.MemoryDataKey; -import net.citizensnpcs.command.Command; -import net.citizensnpcs.command.CommandContext; -import net.citizensnpcs.command.Requirements; -import net.citizensnpcs.command.exception.CommandException; -import net.citizensnpcs.command.exception.NoPermissionsException; -import net.citizensnpcs.command.exception.ServerCommandException; -import net.citizensnpcs.npc.CitizensNPC; -import net.citizensnpcs.npc.EntityControllers; -import net.citizensnpcs.npc.NPCSelector; -import net.citizensnpcs.npc.Template; -import net.citizensnpcs.trait.Age; -import net.citizensnpcs.trait.Anchors; -import net.citizensnpcs.trait.Behaviour; -import net.citizensnpcs.trait.Controllable; -import net.citizensnpcs.trait.CurrentLocation; -import net.citizensnpcs.trait.Gravity; -import net.citizensnpcs.trait.LookClose; -import net.citizensnpcs.trait.NPCSkeletonType; -import net.citizensnpcs.trait.Poses; -import net.citizensnpcs.trait.Powered; -import net.citizensnpcs.trait.SlimeSize; -import net.citizensnpcs.trait.VillagerProfession; -import net.citizensnpcs.util.Anchor; -import net.citizensnpcs.util.Messages; -import net.citizensnpcs.util.Messaging; -import net.citizensnpcs.util.NMS; -import net.citizensnpcs.util.Paginator; -import net.citizensnpcs.util.StringHelper; -import net.citizensnpcs.util.Util; - -import org.bukkit.Bukkit; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.World; -import org.bukkit.command.BlockCommandSender; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Ageable; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; -import org.bukkit.entity.Skeleton.SkeletonType; -import org.bukkit.entity.Villager.Profession; -import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; - -import com.google.common.base.Splitter; -import com.google.common.collect.Iterables; -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", "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); - } - 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, final 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()) - Messaging.send(sender, " Spawned at: " + npc.getBukkitEntity().getLocation()); - 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 = "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", - 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()); - } +package net.citizensnpcs.command.command; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import net.citizensnpcs.Citizens; +import net.citizensnpcs.Settings.Setting; +import net.citizensnpcs.api.CitizensAPI; +import net.citizensnpcs.api.event.PlayerCreateNPCEvent; +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; +import net.citizensnpcs.api.util.DataKey; +import net.citizensnpcs.api.util.MemoryDataKey; +import net.citizensnpcs.command.Command; +import net.citizensnpcs.command.CommandContext; +import net.citizensnpcs.command.Requirements; +import net.citizensnpcs.command.exception.CommandException; +import net.citizensnpcs.command.exception.NoPermissionsException; +import net.citizensnpcs.command.exception.ServerCommandException; +import net.citizensnpcs.npc.CitizensNPC; +import net.citizensnpcs.npc.EntityControllers; +import net.citizensnpcs.npc.NPCSelector; +import net.citizensnpcs.npc.Template; +import net.citizensnpcs.trait.Age; +import net.citizensnpcs.trait.Anchors; +import net.citizensnpcs.trait.Behaviour; +import net.citizensnpcs.trait.Controllable; +import net.citizensnpcs.trait.CurrentLocation; +import net.citizensnpcs.trait.Gravity; +import net.citizensnpcs.trait.LookClose; +import net.citizensnpcs.trait.NPCSkeletonType; +import net.citizensnpcs.trait.Poses; +import net.citizensnpcs.trait.Powered; +import net.citizensnpcs.trait.SlimeSize; +import net.citizensnpcs.trait.VillagerProfession; +import net.citizensnpcs.util.Anchor; +import net.citizensnpcs.util.Messages; +import net.citizensnpcs.util.Messaging; +import net.citizensnpcs.util.NMS; +import net.citizensnpcs.util.Paginator; +import net.citizensnpcs.util.StringHelper; +import net.citizensnpcs.util.Util; + +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.BlockCommandSender; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Ageable; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Skeleton.SkeletonType; +import org.bukkit.entity.Villager.Profession; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +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", "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); + } + 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, final 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()) + Messaging.send(sender, " Spawned at: " + + "X: " + npc.getBukkitEntity().getLocation().getBlockX() + + " Y: " + npc.getBukkitEntity().getLocation().getBlockY() + + " Z: " + npc.getBukkitEntity().getLocation().getBlockZ() + + " in world " + npc.getBukkitEntity().getLocation().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 = "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", + 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()); + } } \ No newline at end of file