diff --git a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java index e1e5348d4..e3c5d6ead 100644 --- a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -444,7 +444,7 @@ public class NPCCommands { @Command( aliases = { "npc" }, - usage = "command|cmd (add [command] | remove [id] | permissions [permissions] | sequential | random | clearerror [type] (name|uuid) | errormsg [type] [msg] | persistsequence [true|false] | (exp|item)cost [cost]) (-s(hift)) (-l[eft]/-r[ight]) (-p[layer] -o[p]), --cooldown --gcooldown [seconds] --delay [ticks] --permissions [perms] --n [max # of uses]", + usage = "command|cmd (add [command] | remove [id] | permissions [permissions] | sequential | random | clearerror [type] (name|uuid) | errormsg [type] [msg] | persistsequence [true|false] | cost [cost] (id) | expcost [cost] (id) | itemcost (id)) (-s(hift)) (-l[eft]/-r[ight]) (-p[layer] -o[p]), --cooldown --gcooldown [seconds] --delay [ticks] --permissions [perms] --n [max # of uses]", desc = "Controls commands which will be run when clicking on an NPC", help = Messages.NPC_COMMAND_HELP, modifiers = { "command", "cmd" }, @@ -459,7 +459,7 @@ public class NPCCommands { @Arg( value = 1, completions = { "add", "remove", "permissions", "persistsequence", "sequential", "random", - "hideerrors", "errormsg", "clearerror", "expcost", "itemcost" }) String action) + "hideerrors", "errormsg", "clearerror", "expcost", "itemcost", "cost" }) String action) throws CommandException { CommandTrait commands = npc.getOrAddTrait(CommandTrait.class); if (args.argsLength() == 1) { @@ -532,11 +532,29 @@ public class NPCCommands { Messaging.sendTr(sender, Messages.COMMAND_TEMPORARY_PERMISSIONS_SET, Joiner.on(' ').join(temporaryPermissions)); } else if (action.equalsIgnoreCase("cost")) { - commands.setCost(args.getDouble(2)); - Messaging.sendTr(sender, Messages.COMMAND_COST_SET, args.getDouble(2)); + if (args.argsLength() == 2) { + throw new CommandException(Messages.COMMAND_MISSING_COST); + } + if (args.argsLength() == 4) { + commands.setCost(args.getDouble(2), args.getInteger(3)); + Messaging.sendTr(sender, Messages.COMMAND_INDIVIDUAL_COST_SET, args.getDouble(2) == -1 ? "-1 (default)" : args.getDouble(2), args.getInteger(3)); + } + else { + commands.setCost(args.getDouble(2)); + Messaging.sendTr(sender, Messages.COMMAND_COST_SET, args.getDouble(2)); + } } else if (action.equalsIgnoreCase("expcost")) { - commands.setExperienceCost(args.getInteger(2)); - Messaging.sendTr(sender, Messages.COMMAND_EXPERIENCE_COST_SET, args.getInteger(2)); + if (args.argsLength() == 2) { + throw new CommandException(Messages.COMMAND_MISSING_COST); + } + if (args.argsLength() == 4) { + commands.setExperienceCost(args.getInteger(2), args.getInteger(3)); + Messaging.sendTr(sender, Messages.COMMAND_INDIVIDUAL_EXPERIENCE_COST_SET, args.getInteger(2) == -1 ? "-1 (default)" : args.getInteger(2), args.getInteger(3)); + } + else { + commands.setExperienceCost(args.getInteger(2)); + Messaging.sendTr(sender, Messages.COMMAND_EXPERIENCE_COST_SET, args.getInteger(2)); + } } else if (action.equalsIgnoreCase("hideerrors")) { commands.setHideErrorMessages(!commands.isHideErrorMessages()); Messaging.sendTr(sender, commands.isHideErrorMessages() ? Messages.COMMAND_HIDE_ERROR_MESSAGES_SET @@ -549,7 +567,12 @@ public class NPCCommands { } else if (action.equalsIgnoreCase("itemcost")) { if (!(sender instanceof Player)) throw new CommandException(CommandMessages.MUST_BE_INGAME); - InventoryMenu.createSelfRegistered(new ItemRequirementGUI(commands)).present(((Player) sender)); + if (args.argsLength() == 2) { + InventoryMenu.createSelfRegistered(new ItemRequirementGUI(commands)).present(((Player) sender)); + } + else { + InventoryMenu.createSelfRegistered(new ItemRequirementGUI(commands, args.getInteger(2))).present(((Player) sender)); + } } else if (action.equalsIgnoreCase("errormsg")) { CommandTraitError which = Util.matchEnum(CommandTraitError.values(), args.getString(2)); if (which == null) diff --git a/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java b/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java index a5e7d1a72..1c06a83e4 100644 --- a/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java @@ -45,6 +45,7 @@ import net.citizensnpcs.api.persistence.Persister; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.TraitName; import net.citizensnpcs.api.util.DataKey; +import net.citizensnpcs.api.util.ItemStorage; import net.citizensnpcs.api.util.Messaging; import net.citizensnpcs.api.util.Translator; import net.citizensnpcs.trait.shop.ExperienceAction; @@ -93,7 +94,7 @@ public class CommandTrait extends Trait { return id; } - private Transaction chargeCommandCosts(Player player, Hand hand) { + private Transaction chargeCommandCosts(Player player, Hand hand, int id) { NPCShopAction action = null; if (player.hasPermission("citizens.npc.command.ignoreerrors.*")) return Transaction.success(); @@ -117,6 +118,26 @@ public class CommandTrait extends Trait { stack.getAmount()); } } + if (hasCost(id) && !player.hasPermission("citizens.npc.command.ignoreerrors.cost")) { + action = new MoneyAction(commands.get(id).cost); + if (!action.take(player, 1).isPossible()) { + sendErrorMessage(player, CommandTraitError.MISSING_MONEY, null, commands.get(id).cost); + } + } + if (hasExperienceCost(id) && !player.hasPermission("citizens.npc.command.ignoreerrors.expcost")) { + action = new ExperienceAction(commands.get(id).experienceCost); + if (!action.take(player, 1).isPossible()) { + sendErrorMessage(player, CommandTraitError.MISSING_EXPERIENCE, null, commands.get(id).experienceCost); + } + } + if (hasItemCost(id) && !player.hasPermission("citizens.npc.command.ignoreerrors.itemcost")) { + action = new ItemAction(commands.get(id).itemCost); + if (!action.take(player, 1).isPossible()) { + ItemStack stack = commands.get(id).itemCost.get(0); + sendErrorMessage(player, CommandTraitError.MISSING_ITEM, null, Util.prettyEnum(stack.getType()), + stack.getAmount()); + } + } return action == null ? Transaction.success() : action.take(player, 1); } @@ -173,37 +194,46 @@ public class CommandTrait extends Trait { right.add(command); } } - String output = ""; + List outputList = Lists.newArrayList(); if (cost > 0) { - output += "Cost: " + StringHelper.wrap(cost); + outputList.add("Cost: " + StringHelper.wrap(cost)); } if (experienceCost > 0) { - output += " XP cost: " + StringHelper.wrap(experienceCost); + outputList.add("XP cost: " + StringHelper.wrap(experienceCost)); } if (left.size() > 0) { - output += Messaging.tr(Messages.COMMAND_LEFT_HAND_HEADER); + outputList.add(Messaging.tr(Messages.COMMAND_LEFT_HAND_HEADER)); for (NPCCommand command : left) { - output += describe(command); + outputList.add(describe(command)); } } if (right.size() > 0) { - output += Messaging.tr(Messages.COMMAND_RIGHT_HAND_HEADER); + outputList.add(Messaging.tr(Messages.COMMAND_RIGHT_HAND_HEADER)); for (NPCCommand command : right) { - output += describe(command); + outputList.add(describe(command)); } } - if (output.isEmpty()) { - output = Messaging.tr(Messages.COMMAND_NO_COMMANDS_ADDED); + if (outputList.isEmpty()) { + outputList.add(Messaging.tr(Messages.COMMAND_NO_COMMANDS_ADDED)); } else { - output = executionMode + " " + output; + outputList.add(0, executionMode.toString()); } - Messaging.send(sender, output); + StringBuilder output = new StringBuilder(); + for (String item : outputList) { + output.append(item); + output.append(" "); + } + + Messaging.send(sender, output.toString().trim()); } private String describe(NPCCommand command) { - String output = Messaging.tr(Messages.COMMAND_DESCRIBE_TEMPLATE, command.command, StringHelper.wrap( - command.cooldown != 0 ? command.cooldown : Setting.NPC_COMMAND_GLOBAL_COMMAND_COOLDOWN.asSeconds()), + String output = Messaging.tr(Messages.COMMAND_DESCRIBE_TEMPLATE, + command.command, + StringHelper.wrap(command.cooldown != 0 ? command.cooldown : Setting.NPC_COMMAND_GLOBAL_COMMAND_COOLDOWN.asSeconds()), + StringHelper.wrap(hasCost(command.id) ? command.cost : "default"), + StringHelper.wrap(hasExperienceCost(command.id) ? command.experienceCost : "default"), command.id); if (command.globalCooldown > 0) { output += "[global " + StringHelper.wrap(command.globalCooldown) + "s]"; @@ -285,7 +315,7 @@ public class CommandTrait extends Trait { } Transaction charge = null; if (charged == null) { - charge = chargeCommandCosts(player, hand); + charge = chargeCommandCosts(player, hand, command.id); if (!charge.isPossible()) { charged = false; return; @@ -330,6 +360,10 @@ public class CommandTrait extends Trait { return cost; } + public double getCost(int id) { + return commands.get(id).cost; + } + public ExecutionMode getExecutionMode() { return executionMode; } @@ -338,6 +372,14 @@ public class CommandTrait extends Trait { return experienceCost; } + public int getExperienceCost(int id) { + return commands.get(id).experienceCost; + } + + public List getItemCost(int id) { + return commands.get(id).itemCost; + } + private int getNewId() { int i = 0; while (commands.containsKey(i)) { @@ -350,6 +392,18 @@ public class CommandTrait extends Trait { return commands.containsKey(id); } + public boolean hasCost(int id) { + return commands.get(id).cost != -1; + } + + public boolean hasExperienceCost(int id) { + return commands.get(id).experienceCost != -1; + } + + public boolean hasItemCost(int id) { + return !commands.get(id).itemCost.isEmpty(); + } + public boolean isHideErrorMessages() { return hideErrorMessages; } @@ -375,8 +429,7 @@ public class CommandTrait extends Trait { } } - private void sendErrorMessage(Player player, CommandTraitError msg, Function transform, - Object... objects) { + private void sendErrorMessage(Player player, CommandTraitError msg, Function transform, Object... objects) { if (hideErrorMessages) { return; } @@ -399,6 +452,10 @@ public class CommandTrait extends Trait { this.cost = cost; } + public void setCost(double cost, int id) { + commands.get(id).cost = cost; + } + public void setCustomErrorMessage(CommandTraitError which, String message) { customErrorMessages.put(which, message); } @@ -411,6 +468,10 @@ public class CommandTrait extends Trait { this.experienceCost = experienceCost; } + public void setExperienceCost(int experienceCost, int id) { + commands.get(id).experienceCost = experienceCost; + } + public void setHideErrorMessages(boolean hide) { this.hideErrorMessages = hide; } @@ -424,6 +485,11 @@ public class CommandTrait extends Trait { temporaryPermissions.addAll(permissions); } + public void setItemCost(List itemCost, int id) { + commands.get(id).itemCost.clear(); + commands.get(id).itemCost.addAll(itemCost); + } + public enum CommandTraitError { MAXIMUM_TIMES_USED(Setting.NPC_COMMAND_MAXIMUM_TIMES_USED_MESSAGE), MISSING_EXPERIENCE(Setting.NPC_COMMAND_NOT_ENOUGH_EXPERIENCE_MESSAGE), @@ -463,6 +529,7 @@ public class CommandTrait extends Trait { public static class ItemRequirementGUI extends InventoryMenuPage { private Inventory inventory; private CommandTrait trait; + private int id = -1; private ItemRequirementGUI() { throw new UnsupportedOperationException(); @@ -472,11 +539,23 @@ public class CommandTrait extends Trait { this.trait = trait; } + public ItemRequirementGUI(CommandTrait trait, int id) { + this.trait = trait; + this.id = id; + } + @Override public void initialise(MenuContext ctx) { this.inventory = ctx.getInventory(); - for (ItemStack stack : trait.itemRequirements) { - inventory.addItem(stack.clone()); + if (id == -1) { + for (ItemStack stack : trait.itemRequirements) { + inventory.addItem(stack.clone()); + } + } + else { + for (ItemStack stack : trait.commands.get(id).itemCost) { + inventory.addItem(stack.clone()); + } } } @@ -493,8 +572,13 @@ public class CommandTrait extends Trait { requirements.add(stack); } } - this.trait.itemRequirements.clear(); - this.trait.itemRequirements.addAll(requirements); + if (id == -1) { + this.trait.itemRequirements.clear(); + this.trait.itemRequirements.addAll(requirements); + } + else { + this.trait.setItemCost(requirements, id); + } } } @@ -511,9 +595,13 @@ public class CommandTrait extends Trait { boolean op; List perms; boolean player; + double cost; + int experienceCost; + List itemCost; public NPCCommand(int id, String command, Hand hand, boolean player, boolean op, int cooldown, - List perms, int n, int delay, int globalCooldown) { + List perms, int n, int delay, int globalCooldown, double cost, + int experienceCost, List itemCost) { this.id = id; this.command = command; this.hand = hand; @@ -526,6 +614,9 @@ public class CommandTrait extends Trait { this.globalCooldown = globalCooldown; List split = Splitter.on(' ').omitEmptyStrings().trimResults().limit(2).splitToList(command); this.bungeeServer = split.size() == 2 && split.get(0).equalsIgnoreCase("server") ? split.get(1) : null; + this.cost = cost; + this.experienceCost = experienceCost; + this.itemCost = itemCost; } public String getEncodedKey() { @@ -549,6 +640,9 @@ public class CommandTrait extends Trait { boolean op; List perms = Lists.newArrayList(); boolean player; + double cost = -1; + int experienceCost = -1; + List itemCost = Lists.newArrayList(); public NPCCommandBuilder(String command, Hand hand) { this.command = command; @@ -566,7 +660,7 @@ public class CommandTrait extends Trait { } private NPCCommand build(int id) { - return new NPCCommand(id, command, hand, player, op, cooldown, perms, n, delay, globalCooldown); + return new NPCCommand(id, command, hand, player, op, cooldown, perms, n, delay, globalCooldown, cost, experienceCost, itemCost); } public NPCCommandBuilder command(String command) { @@ -611,6 +705,21 @@ public class CommandTrait extends Trait { this.player = player; return this; } + + public NPCCommandBuilder cost(double cost) { + this.cost = cost; + return this; + } + + public NPCCommandBuilder experienceCost(int experienceCost) { + this.experienceCost = experienceCost; + return this; + } + + public NPCCommandBuilder itemCost(List itemCost) { + this.itemCost = itemCost; + return this; + } } private static class NPCCommandPersister implements Persister { @@ -623,10 +732,16 @@ public class CommandTrait extends Trait { for (DataKey key : root.getRelative("permissions").getIntegerSubKeys()) { perms.add(key.getString("")); } + List items = Lists.newArrayList(); + for (DataKey key : root.getRelative("itemCost").getIntegerSubKeys()) { + items.add(ItemStorage.loadItemStack(key)); + } + double cost = root.keyExists("cost") ? root.getDouble("cost") : -1; + int exp = root.keyExists("experienceCost") ? root.getInt("experienceCost") : -1; return new NPCCommand(Integer.parseInt(root.name()), root.getString("command"), Hand.valueOf(root.getString("hand")), Boolean.valueOf(root.getString("player")), Boolean.valueOf(root.getString("op")), root.getInt("cooldown"), perms, root.getInt("n"), - root.getInt("delay"), root.getInt("globalcooldown")); + root.getInt("delay"), root.getInt("globalcooldown"), cost, exp, items); } @Override @@ -642,6 +757,11 @@ public class CommandTrait extends Trait { for (int i = 0; i < instance.perms.size(); i++) { root.setString("permissions." + i, instance.perms.get(i)); } + root.setDouble("cost", instance.cost); + root.setInt("experienceCost", instance.experienceCost); + for (int i = 0; i < instance.itemCost.size(); i++) { + ItemStorage.saveItem(root.getRelative("itemCost." + i), instance.itemCost.get(i)); + } } } @@ -774,4 +894,4 @@ public class CommandTrait extends Trait { return StrSubstitutor.replace(t, map, "{", "}"); } } -} \ No newline at end of file +} diff --git a/main/src/main/java/net/citizensnpcs/util/Messages.java b/main/src/main/java/net/citizensnpcs/util/Messages.java index 34c0dc1cf..f2e033778 100644 --- a/main/src/main/java/net/citizensnpcs/util/Messages.java +++ b/main/src/main/java/net/citizensnpcs/util/Messages.java @@ -62,8 +62,11 @@ public class Messages { public static final String COMMAND_ADDED = "citizens.commands.npc.command.command-added"; public static final String COMMAND_AGE_HELP = "citizens.commands.npc.age.help"; public static final String COMMAND_COST_SET = "citizens.commands.npc.command.cost-set"; + public static final String COMMAND_INDIVIDUAL_COST_SET = "citizens.commands.npc.command.individual-cost-set"; + public static final String COMMAND_MISSING_COST = "citizens.commands.npc.command.cost-missing"; public static final String COMMAND_DESCRIBE_TEMPLATE = "citizens.commands.npc.command.describe-format"; public static final String COMMAND_EXPERIENCE_COST_SET = "citizens.commands.npc.command.experience-cost-set"; + public static final String COMMAND_INDIVIDUAL_EXPERIENCE_COST_SET = "citizens.commands.npc.command.individual-experience-cost-set"; public static final String COMMAND_HELP_HEADER = "citizens.commands.help.header"; public static final String COMMAND_HIDE_ERROR_MESSAGES_SET = "citizens.commands.npc.command.hide-error-messages-set"; public static final String COMMAND_HIDE_ERROR_MESSAGES_UNSET = "citizens.commands.npc.command.hide-error-messages-unset"; diff --git a/main/src/main/resources/messages_en.properties b/main/src/main/resources/messages_en.properties index 09cf2aa11..5445d5f04 100644 --- a/main/src/main/resources/messages_en.properties +++ b/main/src/main/resources/messages_en.properties @@ -58,15 +58,18 @@ citizens.commands.npc.command.persist-sequence-set=Command sequences will now be citizens.commands.npc.command.persist-sequence-unset=Command sequences will no longer be saved across server restarts. citizens.commands.npc.command.none-added=No commands have been added. citizens.commands.npc.command.cost-set=Set cost per click to [[{0}]]. +citizens.commands.npc.command.individual-cost-set=Set cost per click to [[{0}]] for command id [[{1}]]. +citizens.commands.npc.command.cost-missing=Missing cost to set. citizens.commands.npc.command.invalid-error-message=Invalid error message. Valid messages are [[{0}]]. citizens.commands.npc.command.hide-error-messages-set=Now hiding error messages. citizens.commands.npc.command.hide-error-messages-unset=No longer hiding error messages. citizens.commands.npc.command.experience-cost-set=Set experience cost per click to [[{0}]]. +citizens.commands.npc.command.individual-experience-cost-set=Set experience cost per click to [[{0}]] for command id [[{1}]]. citizens.commands.npc.command.left-hand-header=Commands to run on [[left click]]: citizens.commands.npc.command.right-hand-header=Commands to run on [[right click]]: citizens.commands.npc.command.command-removed=Command [[{0}]] removed. citizens.commands.npc.command.command-added=Command [[{0}]] added with id [[{1}]]. -citizens.commands.npc.command.describe-format=
- {0} [{1}s] [-] +citizens.commands.npc.command.describe-format=
- {0} [{1}s] [cost:{2}] [exp:{3}] [-] citizens.commands.npc.command.help=
Use the [[-l]] flag to make the command run on left click, [[-r]] on right click (default).
Set the per-player cooldown before the command can be used again using [[--cooldown]] (in [[seconds]]).
Set the server-wide cooldown in seconds using [[--gcooldown]].
[[--delay]] will wait the specified amount in [[ticks]] before executing the command.
[[--permissions]] will set the command to require specific permissions (separate multiple with commas).
[[--n]] will only let the player run the command that number of times.
Use [[-o]] to temporarily execute the command as an op and [[-p]] to run the command as the clicking player instead of the server.
To give the player temporary permissions instead of op, use [[/npc command permissions]].
Set the cost of each click with [[/npc command cost/expcost/itemcost]].
Commands can be executed one by one instead of all at once by using [[/npc command sequential]]. citizens.commands.npc.command.unknown-id=Unknown command id [[{0}]] for this NPC. citizens.commands.npc.command.temporary-permissions-set=Temporary permissions set to [[{0}]]. @@ -494,4 +497,4 @@ citizens.sub-plugins.load=Loading {0} citizens.traits.age-description={0}''s age is [[{1}]]. Locked is [[{2}]]. citizens.waypoints.available-providers-header=List of available providers citizens.waypoints.current-provider=The current waypoint provider is [[{0}]]. -citizens.waypoints.set-provider=Set the waypoint provider to [[{0}]]. \ No newline at end of file +citizens.waypoints.set-provider=Set the waypoint provider to [[{0}]].