From 66708ac3146f114b95e899a45ff7484b28445cb7 Mon Sep 17 00:00:00 2001 From: fullwall Date: Mon, 5 Feb 2024 00:05:51 +0800 Subject: [PATCH 01/11] Add /npc command cycle --- .../citizensnpcs/commands/NPCCommands.java | 18 +++++++--- .../citizensnpcs/npc/CitizensNPCRegistry.java | 4 +-- .../npc/ai/AStarNavigationStrategy.java | 8 ++--- .../npc/ai/CitizensNavigator.java | 14 ++++---- .../ai/StraightLineNavigationStrategy.java | 7 ++-- .../net/citizensnpcs/trait/CommandTrait.java | 14 ++++++-- .../net/citizensnpcs/trait/ShopTrait.java | 36 +++++-------------- .../waypoint/GuidedWaypointProvider.java | 13 +++---- .../java/net/citizensnpcs/util/Messages.java | 2 ++ main/src/main/resources/en.json | 4 ++- 10 files changed, 64 insertions(+), 56 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java index dd760e007..13d7b019c 100644 --- a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -455,7 +455,7 @@ public class NPCCommands { @Command( aliases = { "npc" }, - usage = "command|cmd (add [command] | remove [id|all] | 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]", + usage = "command|cmd (add [command] | remove [id|all] | permissions [permissions] | sequential | cycle | 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" }, @@ -469,7 +469,7 @@ public class NPCCommands { @Flag(value = "delay", defValue = "0") Duration delay, @Arg( value = 1, - completions = { "add", "remove", "permissions", "persistsequence", "sequential", "random", + completions = { "add", "remove", "permissions", "persistsequence", "sequential", "cycle", "random", "hideerrors", "errormsg", "clearerror", "expcost", "itemcost", "cost" }) String action) throws CommandException { CommandTrait commands = npc.getOrAddTrait(CommandTrait.class); @@ -521,6 +521,11 @@ public class NPCCommands { Messaging.sendTr(sender, commands.getExecutionMode() == ExecutionMode.SEQUENTIAL ? Messages.COMMANDS_SEQUENTIAL_SET : Messages.COMMANDS_SEQUENTIAL_UNSET); + } else if (action.equalsIgnoreCase("cycle")) { + commands.setExecutionMode( + commands.getExecutionMode() == ExecutionMode.CYCLE ? ExecutionMode.LINEAR : ExecutionMode.CYCLE); + Messaging.sendTr(sender, commands.getExecutionMode() == ExecutionMode.CYCLE ? Messages.COMMANDS_CYCLE_SET + : Messages.COMMANDS_CYCLE_UNSET); } else if (action.equalsIgnoreCase("persistsequence")) { if (args.argsLength() == 2) { commands.setPersistSequence(!commands.persistSequence()); @@ -2078,11 +2083,12 @@ public class NPCCommands { @Command( aliases = { "npc" }, - usage = "pathto me | here | cursor | [x] [y] [z] (--margin [distance margin])", + usage = "pathto me | here | cursor | [x] [y] [z] (--margin [distance margin]) (-s[traight line])", desc = "Starts pathfinding to a certain location", modifiers = { "pathto" }, min = 2, max = 4, + flags = "s", permission = "citizens.npc.pathto") public void pathto(CommandContext args, CommandSender sender, NPC npc, @Arg(value = 1, completions = { "me", "here", "cursor" }) String option, @Flag("margin") Double margin) @@ -2100,7 +2106,11 @@ public class NPCCommands { loc.setY(args.getDouble(2)); loc.setZ(args.getDouble(3)); } - npc.getNavigator().setTarget(loc); + if (args.hasFlag('s')) { + npc.getNavigator().setStraightLineTarget(loc); + } else { + npc.getNavigator().setTarget(loc); + } if (margin != null) { npc.getNavigator().getLocalParameters().distanceMargin(margin); } diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java index 726a47d18..44bb5292b 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPCRegistry.java @@ -191,9 +191,7 @@ public class CitizensNPCRegistry implements NPCRegistry { public NPC getNPC(Entity entity) { if (entity == null) return null; - if (entity instanceof NPCHolder) - return ((NPCHolder) entity).getNPC(); - return NMS.getNPC(entity); + return entity instanceof NPCHolder ? ((NPCHolder) entity).getNPC() : NMS.getNPC(entity); } @Override diff --git a/main/src/main/java/net/citizensnpcs/npc/ai/AStarNavigationStrategy.java b/main/src/main/java/net/citizensnpcs/npc/ai/AStarNavigationStrategy.java index fa9444789..d33d928ed 100644 --- a/main/src/main/java/net/citizensnpcs/npc/ai/AStarNavigationStrategy.java +++ b/main/src/main/java/net/citizensnpcs/npc/ai/AStarNavigationStrategy.java @@ -98,7 +98,7 @@ public class AStarNavigationStrategy extends AbstractPathStrategy { } Location loc = npc.getEntity().getLocation(); /* Proper door movement - gets stuck on corners at times - + Block block = currLoc.getWorld().getBlockAt(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ()); if (MinecraftBlockExaminer.isDoor(block.getType())) { Door door = (Door) block.getState().getData(); @@ -112,8 +112,8 @@ public class AStarNavigationStrategy extends AbstractPathStrategy { double dX = dest.getX() - loc.getX(); double dZ = dest.getZ() - loc.getZ(); double dY = dest.getY() - loc.getY(); - double xzDistance = dX * dX + dZ * dZ; - if (Math.abs(dY) < 1 && Math.sqrt(xzDistance) <= params.distanceMargin()) { + double xzDistance = Math.sqrt(dX * dX + dZ * dZ); + if (Math.abs(dY) < 1 && xzDistance <= params.distanceMargin()) { plan.update(npc); if (plan.isComplete()) return true; @@ -128,7 +128,7 @@ public class AStarNavigationStrategy extends AbstractPathStrategy { } else { Vector dir = dest.toVector().subtract(npc.getEntity().getLocation().toVector()).normalize().multiply(0.2); boolean liquidOrInLiquid = MinecraftBlockExaminer.isLiquidOrInLiquid(loc.getBlock()); - if (dY >= 1 && Math.sqrt(xzDistance) <= 0.4 || dY >= 0.2 && liquidOrInLiquid) { + if (dY >= 1 && xzDistance <= 0.4 || dY >= 0.2 && liquidOrInLiquid) { dir.add(new Vector(0, 0.75, 0)); } npc.getEntity().setVelocity(dir); diff --git a/main/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java b/main/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java index 097947b23..6eb764705 100644 --- a/main/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java +++ b/main/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java @@ -334,12 +334,13 @@ public class CitizensNavigator implements Navigator, Runnable { return; } setTarget(params -> { - if (npc.isFlyable()) + if (npc.isFlyable()) { return new FlyingAStarNavigationStrategy(npc, path, params); - else if (params.useNewPathfinder() || !(npc.getEntity() instanceof LivingEntity)) + } else if (params.useNewPathfinder() || !(npc.getEntity() instanceof LivingEntity)) { return new AStarNavigationStrategy(npc, path, params); - else + } else { return new MCNavigationStrategy(npc, path, params); + } }); } @@ -353,12 +354,13 @@ public class CitizensNavigator implements Navigator, Runnable { } Location target = targetIn.clone(); setTarget(params -> { - if (npc.isFlyable()) + if (npc.isFlyable()) { return new FlyingAStarNavigationStrategy(npc, target, params); - else if (params.useNewPathfinder() || !(npc.getEntity() instanceof LivingEntity)) + } else if (params.useNewPathfinder() || !(npc.getEntity() instanceof LivingEntity)) { return new AStarNavigationStrategy(npc, target, params); - else + } else { return new MCNavigationStrategy(npc, target, params); + } }); } diff --git a/main/src/main/java/net/citizensnpcs/npc/ai/StraightLineNavigationStrategy.java b/main/src/main/java/net/citizensnpcs/npc/ai/StraightLineNavigationStrategy.java index c40de4d2c..5e434b4bc 100644 --- a/main/src/main/java/net/citizensnpcs/npc/ai/StraightLineNavigationStrategy.java +++ b/main/src/main/java/net/citizensnpcs/npc/ai/StraightLineNavigationStrategy.java @@ -62,9 +62,12 @@ public class StraightLineNavigationStrategy extends AbstractPathStrategy { return true; Location currLoc = npc.getEntity().getLocation(); - if (currLoc.distance(destination) <= params.distanceMargin()) + if (currLoc.distance(destination) <= params.distanceMargin()) { + if (npc.isFlyable()) { + npc.getEntity().setVelocity(new Vector(0, 0, 0)); + } return true; - + } if (target != null) { destination = params.entityTargetLocationMapper().apply(target); } diff --git a/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java b/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java index e4eb7bdec..9b680d7cf 100644 --- a/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/CommandTrait.java @@ -77,6 +77,7 @@ public class CommandTrait extends Trait { private boolean hideErrorMessages; @Persist private final List itemRequirements = Lists.newArrayList(); + private int lastUsedId = -1; @Persist private boolean persistSequence = false; @Persist(keyType = UUID.class, reify = true, value = "cooldowns") @@ -278,7 +279,7 @@ public class CommandTrait extends Trait { return; } int max = -1; - if (executionMode == ExecutionMode.SEQUENTIAL) { + if (executionMode == ExecutionMode.SEQUENTIAL || executionMode == ExecutionMode.CYCLE) { Collections.sort(commandList, Comparator.comparing(o1 -> o1.id)); max = commandList.size() > 0 ? commandList.get(commandList.size() - 1).id : -1; } @@ -287,6 +288,13 @@ public class CommandTrait extends Trait { } for (NPCCommand command : commandList) { PlayerNPCCommand info = null; + if (executionMode == ExecutionMode.CYCLE) { + if (command.id <= lastUsedId) { + if (lastUsedId != max) + continue; + lastUsedId = -1; + } + } if (executionMode == ExecutionMode.SEQUENTIAL && (info = playerTracking.get(player.getUniqueId())) != null) { if (info.lastUsedHand != hand) { @@ -300,7 +308,8 @@ public class CommandTrait extends Trait { } } runCommand(player, hand, command); - if (executionMode == ExecutionMode.SEQUENTIAL || (charged != null && !charged)) + if (executionMode == ExecutionMode.SEQUENTIAL || executionMode == ExecutionMode.CYCLE + || (charged != null && !charged)) break; } } @@ -508,6 +517,7 @@ public class CommandTrait extends Trait { } public enum ExecutionMode { + CYCLE, LINEAR, RANDOM, SEQUENTIAL; diff --git a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java index 986dfe145..6ab3c990c 100644 --- a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java @@ -31,7 +31,6 @@ import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.gui.CitizensInventoryClickEvent; import net.citizensnpcs.api.gui.ClickHandler; import net.citizensnpcs.api.gui.InputMenus; -import net.citizensnpcs.api.gui.InputMenus.Choice; import net.citizensnpcs.api.gui.InventoryMenu; import net.citizensnpcs.api.gui.InventoryMenuPage; import net.citizensnpcs.api.gui.InventoryMenuPattern; @@ -114,8 +113,6 @@ public class ShopTrait extends Trait { @Persist private String title; @Persist - private ShopType type = ShopType.COMMAND; - @Persist private String viewPermission; private NPCShop() { @@ -712,42 +709,31 @@ public class ShopTrait extends Trait { @Override public void initialise(MenuContext ctx) { this.ctx = ctx; - ctx.getSlot(2) + ctx.getSlot(0) .setDescription("Edit permission required to view shop
" + shop.getRequiredPermission()); - ctx.getSlot(6).setDescription("Edit shop title
" + shop.title); + ctx.getSlot(4).setDescription("Edit shop title
" + shop.title); if (trait != null) { - ctx.getSlot(8).setDescription( + ctx.getSlot(6).setDescription( "Show shop on right click
" + shop.getName().equals(trait.rightClickShop)); } } - @MenuSlot(slot = { 0, 4 }, material = Material.FEATHER, amount = 1, title = "Edit shop items") + @MenuSlot(slot = { 0, 2 }, material = Material.FEATHER, amount = 1, title = "Edit shop items") public void onEditItems(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { ctx.getMenu().transition(new NPCShopContentsEditor(shop)); } - @MenuSlot(slot = { 0, 2 }, compatMaterial = { "OAK_SIGN", "SIGN" }, amount = 1) + @MenuSlot(slot = { 0, 0 }, compatMaterial = { "OAK_SIGN", "SIGN" }, amount = 1) public void onPermissionChange(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { ctx.getMenu().transition(InputMenus.stringSetter(shop::getRequiredPermission, shop::setPermission)); } - @MenuSlot(slot = { 0, 6 }, material = Material.NAME_TAG, amount = 1) + @MenuSlot(slot = { 0, 4 }, material = Material.NAME_TAG, amount = 1) public void onSetTitle(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { ctx.getMenu().transition(InputMenus.stringSetter(() -> shop.title, newTitle -> shop.title = newTitle)); } - @MenuSlot(slot = { 0, 0 }, material = Material.BOOK, amount = 1, title = "Edit shop type") - public void onShopTypeChange(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { - ctx.getMenu().transition(InputMenus. picker("Edit shop type", - chosen -> shop.type = chosen.getValue(), - Choice. of(ShopType.BUY, Material.DIAMOND, "Players buy items", - shop.type == ShopType.BUY), - Choice.of(ShopType.SELL, Material.EMERALD, "Players sell items", shop.type == ShopType.SELL), - Choice.of(ShopType.COMMAND, Util.getFallbackMaterial("ENDER_EYE", "ENDER_PEARL"), - "Clicks trigger commands only", shop.type == ShopType.COMMAND))); - } - - @MenuSlot(slot = { 0, 8 }, compatMaterial = { "COMMAND_BLOCK", "COMMAND" }, amount = 1) + @MenuSlot(slot = { 0, 6 }, compatMaterial = { "COMMAND_BLOCK", "COMMAND" }, amount = 1) public void onToggleRightClick(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { event.setCancelled(true); if (trait == null) @@ -758,7 +744,7 @@ public class ShopTrait extends Trait { } else { trait.rightClickShop = shop.name; } - ctx.getSlot(8) + ctx.getSlot(6) .setDescription("Show shop on right click
" + shop.getName().equals(trait.rightClickShop)); } } @@ -830,12 +816,6 @@ public class ShopTrait extends Trait { } } - public enum ShopType { - BUY, - COMMAND, - SELL; - } - static { NPCShopAction.register(ItemAction.class, "items", new ItemActionGUI()); NPCShopAction.register(PermissionAction.class, "permissions", new PermissionActionGUI()); diff --git a/main/src/main/java/net/citizensnpcs/trait/waypoint/GuidedWaypointProvider.java b/main/src/main/java/net/citizensnpcs/trait/waypoint/GuidedWaypointProvider.java index e98221a83..f3b90d581 100644 --- a/main/src/main/java/net/citizensnpcs/trait/waypoint/GuidedWaypointProvider.java +++ b/main/src/main/java/net/citizensnpcs/trait/waypoint/GuidedWaypointProvider.java @@ -271,14 +271,15 @@ public class GuidedWaypointProvider implements EnumerableWaypointProvider { tree.clear(); treePlusDestinations.clear(); for (Waypoint waypoint : guides) { - tree.put(new long[] { waypoint.getLocation().getBlockX(), waypoint.getLocation().getBlockY(), - waypoint.getLocation().getBlockZ() }, waypoint); - treePlusDestinations.put(new long[] { waypoint.getLocation().getBlockX(), - waypoint.getLocation().getBlockY(), waypoint.getLocation().getBlockZ() }, waypoint); + Location location = waypoint.getLocation(); + tree.put(new long[] { location.getBlockX(), location.getBlockY(), location.getBlockZ() }, waypoint); + treePlusDestinations.put(new long[] { location.getBlockX(), location.getBlockY(), location.getBlockZ() }, + waypoint); } for (Waypoint waypoint : destinations) { - treePlusDestinations.put(new long[] { waypoint.getLocation().getBlockX(), - waypoint.getLocation().getBlockY(), waypoint.getLocation().getBlockZ() }, waypoint); + Location location = waypoint.getLocation(); + treePlusDestinations.put(new long[] { location.getBlockX(), location.getBlockY(), location.getBlockZ() }, + waypoint); } if (currentGoal != null) { currentGoal.onProviderChanged(); diff --git a/main/src/main/java/net/citizensnpcs/util/Messages.java b/main/src/main/java/net/citizensnpcs/util/Messages.java index 33f0148b5..1993e18f0 100644 --- a/main/src/main/java/net/citizensnpcs/util/Messages.java +++ b/main/src/main/java/net/citizensnpcs/util/Messages.java @@ -79,6 +79,8 @@ public class Messages { public static final String COMMAND_TRIGGER_PROMPT = "citizens.editors.waypoints.triggers.command.prompt"; public static final String COMMAND_UNKNOWN_COMMAND_ID = "citizens.commands.npc.command.unknown-id"; public static final String COMMANDS_CLEARED = "citizens.commands.npc.command.cleared"; + public static final String COMMANDS_CYCLE_SET = "citizens.commands.npc.command.cycle-set"; + public static final String COMMANDS_CYCLE_UNSET = "citizens.commands.npc.command.cycle-unset"; public static final String COMMANDS_PERSIST_SEQUENCE_SET = "citizens.commands.npc.command.persist-sequence-set"; public static final String COMMANDS_PERSIST_SEQUENCE_UNSET = "citizens.commands.npc.command.persist-sequence-unset"; public static final String COMMANDS_RANDOM_SET = "citizens.commands.npc.commands.random-set"; diff --git a/main/src/main/resources/en.json b/main/src/main/resources/en.json index 38a508072..e5f1b7738 100644 --- a/main/src/main/resources/en.json +++ b/main/src/main/resources/en.json @@ -52,6 +52,8 @@ "citizens.commands.npc.chunkload.unset" : "[[{0}]] will no longer force chunks to be loaded.", "citizens.commands.npc.collidable.set" : "[[{0}]] will now collide with entities.", "citizens.commands.npc.collidable.unset" : "[[{0}]] will no longer collide with entities.", + "citizens.commands.npc.command.cycle-set": "[[{0}]] will now cycle through commands on player click", + "citizens.commands.npc.command.cycle-unset": "[[{0}]] will no longer cycle through commands on player click", "citizens.commands.npc.command.command-added" : "Command [[{0}]] added with id [[{1}]].", "citizens.commands.npc.command.command-removed" : "Command [[{0}]] removed.", "citizens.commands.npc.command.cleared" : "[[{0}]]''s commands cleared.", @@ -60,7 +62,7 @@ "citizens.commands.npc.command.describe-format" : "
- {0} [{1}s] [cost:{2}] [exp:{3}] [-]", "citizens.commands.npc.command.errors-cleared" : "Errors cleared for [[{0}]].", "citizens.commands.npc.command.experience-cost-set" : "Set xp level cost per click to [[{0}]].", - "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.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]] or [[/npc command cycle]].", "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.individual-cost-set" : "Set cost per click to [[{0}]] for command id [[{1}]].", From f9c4dbe614685d86c6b45e14965c91dc7cc4f00f Mon Sep 17 00:00:00 2001 From: fullwall Date: Mon, 5 Feb 2024 00:13:42 +0800 Subject: [PATCH 02/11] Minor shulker fix --- .../nms/v1_10_R1/entity/ShulkerController.java | 2 +- .../nms/v1_11_R1/entity/ShulkerController.java | 7 +------ .../nms/v1_12_R1/entity/ShulkerController.java | 2 +- .../nms/v1_13_R2/entity/ShulkerController.java | 2 +- .../nms/v1_14_R1/entity/ShulkerController.java | 2 +- .../nms/v1_15_R1/entity/ShulkerController.java | 2 +- .../nms/v1_16_R3/entity/ShulkerController.java | 2 +- .../nms/v1_17_R1/entity/ShulkerController.java | 2 +- .../nms/v1_18_R2/entity/ShulkerController.java | 2 +- .../nms/v1_19_R3/entity/ShulkerController.java | 2 +- .../nms/v1_20_R3/entity/ShulkerController.java | 2 +- 11 files changed, 11 insertions(+), 16 deletions(-) diff --git a/v1_10_R1/src/main/java/net/citizensnpcs/nms/v1_10_R1/entity/ShulkerController.java b/v1_10_R1/src/main/java/net/citizensnpcs/nms/v1_10_R1/entity/ShulkerController.java index 75995cd38..c30d2fa91 100644 --- a/v1_10_R1/src/main/java/net/citizensnpcs/nms/v1_10_R1/entity/ShulkerController.java +++ b/v1_10_R1/src/main/java/net/citizensnpcs/nms/v1_10_R1/entity/ShulkerController.java @@ -179,7 +179,7 @@ public class ShulkerController extends MobEntityController { @Override protected EntityAIBodyControl s() { - return new EntityAIBodyControl(this); + return npc == null ? super.s() : new EntityAIBodyControl(this); } @Override diff --git a/v1_11_R1/src/main/java/net/citizensnpcs/nms/v1_11_R1/entity/ShulkerController.java b/v1_11_R1/src/main/java/net/citizensnpcs/nms/v1_11_R1/entity/ShulkerController.java index 43c0c19be..93873c68b 100644 --- a/v1_11_R1/src/main/java/net/citizensnpcs/nms/v1_11_R1/entity/ShulkerController.java +++ b/v1_11_R1/src/main/java/net/citizensnpcs/nms/v1_11_R1/entity/ShulkerController.java @@ -1,7 +1,6 @@ package net.citizensnpcs.nms.v1_11_R1.entity; import org.bukkit.Bukkit; -import org.bukkit.DyeColor; import org.bukkit.craftbukkit.v1_11_R1.CraftServer; import org.bukkit.craftbukkit.v1_11_R1.entity.CraftEntity; import org.bukkit.craftbukkit.v1_11_R1.entity.CraftShulker; @@ -180,11 +179,7 @@ public class ShulkerController extends MobEntityController { @Override protected EntityAIBodyControl s() { - return new EntityAIBodyControl(this); - } - - public void setColor(DyeColor color) { - this.datawatcher.set(bw, color.getWoolData()); + return npc == null ? super.s() : new EntityAIBodyControl(this); } @Override diff --git a/v1_12_R1/src/main/java/net/citizensnpcs/nms/v1_12_R1/entity/ShulkerController.java b/v1_12_R1/src/main/java/net/citizensnpcs/nms/v1_12_R1/entity/ShulkerController.java index 1caaca15c..92b6f3fd3 100644 --- a/v1_12_R1/src/main/java/net/citizensnpcs/nms/v1_12_R1/entity/ShulkerController.java +++ b/v1_12_R1/src/main/java/net/citizensnpcs/nms/v1_12_R1/entity/ShulkerController.java @@ -186,7 +186,7 @@ public class ShulkerController extends MobEntityController { @Override protected EntityAIBodyControl s() { - return new EntityAIBodyControl(this); + return npc == null ? super.s() : new EntityAIBodyControl(this); } @Override diff --git a/v1_13_R2/src/main/java/net/citizensnpcs/nms/v1_13_R2/entity/ShulkerController.java b/v1_13_R2/src/main/java/net/citizensnpcs/nms/v1_13_R2/entity/ShulkerController.java index 7934c0102..5dc747caa 100644 --- a/v1_13_R2/src/main/java/net/citizensnpcs/nms/v1_13_R2/entity/ShulkerController.java +++ b/v1_13_R2/src/main/java/net/citizensnpcs/nms/v1_13_R2/entity/ShulkerController.java @@ -206,7 +206,7 @@ public class ShulkerController extends MobEntityController { @Override protected EntityAIBodyControl o() { - return new EntityAIBodyControl(this); + return npc == null ? super.o() : new EntityAIBodyControl(this); } @Override diff --git a/v1_14_R1/src/main/java/net/citizensnpcs/nms/v1_14_R1/entity/ShulkerController.java b/v1_14_R1/src/main/java/net/citizensnpcs/nms/v1_14_R1/entity/ShulkerController.java index 727599738..2dfee3e99 100644 --- a/v1_14_R1/src/main/java/net/citizensnpcs/nms/v1_14_R1/entity/ShulkerController.java +++ b/v1_14_R1/src/main/java/net/citizensnpcs/nms/v1_14_R1/entity/ShulkerController.java @@ -202,7 +202,7 @@ public class ShulkerController extends MobEntityController { @Override protected EntityAIBodyControl o() { - return new EntityAIBodyControl(this); + return npc == null ? super.o() : new EntityAIBodyControl(this); } @Override diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ShulkerController.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ShulkerController.java index ca83ed8a1..d6432eb79 100644 --- a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ShulkerController.java +++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/entity/ShulkerController.java @@ -203,7 +203,7 @@ public class ShulkerController extends MobEntityController { @Override protected EntityAIBodyControl o() { - return new EntityAIBodyControl(this); + return npc == null ? super.o() : new EntityAIBodyControl(this); } @Override diff --git a/v1_16_R3/src/main/java/net/citizensnpcs/nms/v1_16_R3/entity/ShulkerController.java b/v1_16_R3/src/main/java/net/citizensnpcs/nms/v1_16_R3/entity/ShulkerController.java index bbfd86165..101e4d22c 100644 --- a/v1_16_R3/src/main/java/net/citizensnpcs/nms/v1_16_R3/entity/ShulkerController.java +++ b/v1_16_R3/src/main/java/net/citizensnpcs/nms/v1_16_R3/entity/ShulkerController.java @@ -203,7 +203,7 @@ public class ShulkerController extends MobEntityController { @Override protected EntityAIBodyControl r() { - return new EntityAIBodyControl(this); + return npc == null ? super.r() : new EntityAIBodyControl(this); } @Override diff --git a/v1_17_R1/src/main/java/net/citizensnpcs/nms/v1_17_R1/entity/ShulkerController.java b/v1_17_R1/src/main/java/net/citizensnpcs/nms/v1_17_R1/entity/ShulkerController.java index a65ccc237..215445747 100644 --- a/v1_17_R1/src/main/java/net/citizensnpcs/nms/v1_17_R1/entity/ShulkerController.java +++ b/v1_17_R1/src/main/java/net/citizensnpcs/nms/v1_17_R1/entity/ShulkerController.java @@ -91,7 +91,7 @@ public class ShulkerController extends MobEntityController { @Override protected BodyRotationControl createBodyControl() { - return new BodyRotationControl(this); + return npc == null ? super.createBodyControl() : new BodyRotationControl(this); } @Override diff --git a/v1_18_R2/src/main/java/net/citizensnpcs/nms/v1_18_R2/entity/ShulkerController.java b/v1_18_R2/src/main/java/net/citizensnpcs/nms/v1_18_R2/entity/ShulkerController.java index 91659252e..8bdae3d8c 100644 --- a/v1_18_R2/src/main/java/net/citizensnpcs/nms/v1_18_R2/entity/ShulkerController.java +++ b/v1_18_R2/src/main/java/net/citizensnpcs/nms/v1_18_R2/entity/ShulkerController.java @@ -92,7 +92,7 @@ public class ShulkerController extends MobEntityController { @Override protected BodyRotationControl createBodyControl() { - return new BodyRotationControl(this); + return npc == null ? super.createBodyControl() : new BodyRotationControl(this); } @Override diff --git a/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/entity/ShulkerController.java b/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/entity/ShulkerController.java index f85bf2668..905068a1c 100644 --- a/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/entity/ShulkerController.java +++ b/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/entity/ShulkerController.java @@ -93,7 +93,7 @@ public class ShulkerController extends MobEntityController { @Override protected BodyRotationControl createBodyControl() { - return new BodyRotationControl(this); + return npc == null ? super.createBodyControl() : new BodyRotationControl(this); } @Override diff --git a/v1_20_R3/src/main/java/net/citizensnpcs/nms/v1_20_R3/entity/ShulkerController.java b/v1_20_R3/src/main/java/net/citizensnpcs/nms/v1_20_R3/entity/ShulkerController.java index 1cc15c0bb..2b6c76b97 100644 --- a/v1_20_R3/src/main/java/net/citizensnpcs/nms/v1_20_R3/entity/ShulkerController.java +++ b/v1_20_R3/src/main/java/net/citizensnpcs/nms/v1_20_R3/entity/ShulkerController.java @@ -92,7 +92,7 @@ public class ShulkerController extends MobEntityController { @Override protected BodyRotationControl createBodyControl() { - return new BodyRotationControl(this); + return npc == null ? super.createBodyControl() : new BodyRotationControl(this); } @Override From d557be5f35fb4eca9f054dca843591e9ce833b18 Mon Sep 17 00:00:00 2001 From: fullwall Date: Sat, 10 Feb 2024 23:58:04 +0800 Subject: [PATCH 03/11] Use new pathfinder should be loaded --- .../main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java b/main/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java index 6eb764705..0a7eca469 100644 --- a/main/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java +++ b/main/src/main/java/net/citizensnpcs/npc/ai/CitizensNavigator.java @@ -154,6 +154,9 @@ public class CitizensNavigator implements Navigator, Runnable { if (root.keyExists("pathfindingrange")) { defaultParams.range((float) root.getDouble("pathfindingrange")); } + if (root.keyExists("usenewpathfinder")) { + defaultParams.useNewPathfinder(root.getBoolean("usenewpathfinder")); + } if (root.keyExists("stationaryticks")) { defaultParams.stationaryTicks(root.getInt("stationaryticks")); } From e60bb33508afd22a9a61e66a3e94b2b017fcf9a3 Mon Sep 17 00:00:00 2001 From: fullwall Date: Sun, 11 Feb 2024 00:57:37 +0800 Subject: [PATCH 04/11] Add new metric --- main/src/main/java/net/citizensnpcs/Citizens.java | 1 + 1 file changed, 1 insertion(+) diff --git a/main/src/main/java/net/citizensnpcs/Citizens.java b/main/src/main/java/net/citizensnpcs/Citizens.java index eff10e12b..5791873e4 100644 --- a/main/src/main/java/net/citizensnpcs/Citizens.java +++ b/main/src/main/java/net/citizensnpcs/Citizens.java @@ -548,6 +548,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { return 0; return Iterables.size(npcRegistry); })); + metrics.addCustomChart(new Metrics.SimplePie("storage_type", () -> Setting.STORAGE_TYPE.asString())); metrics.addCustomChart(new Metrics.SingleLineChart("using_templates", () -> Math.min(1, Iterables.size(Template.getTemplates())))); metrics.addCustomChart(new Metrics.SimplePie("locale", () -> Locale.getDefault().getLanguage())); From 4d1b3ee357adc789b6bb40f6580ad1a0ddfe6f53 Mon Sep 17 00:00:00 2001 From: fullwall Date: Sun, 11 Feb 2024 01:13:49 +0800 Subject: [PATCH 05/11] Remove STORAGE_TYPE setting, fix a few setting comments --- .../main/java/net/citizensnpcs/Citizens.java | 13 ++---------- .../main/java/net/citizensnpcs/Settings.java | 21 +++++++++---------- .../citizensnpcs/commands/NPCCommands.java | 7 +++++-- .../net/citizensnpcs/npc/CitizensNPC.java | 8 ++++--- .../net/citizensnpcs/trait/SneakTrait.java | 9 -------- 5 files changed, 22 insertions(+), 36 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/Citizens.java b/main/src/main/java/net/citizensnpcs/Citizens.java index 5791873e4..6cc7925ac 100644 --- a/main/src/main/java/net/citizensnpcs/Citizens.java +++ b/main/src/main/java/net/citizensnpcs/Citizens.java @@ -60,7 +60,6 @@ import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.TraitFactory; import net.citizensnpcs.api.trait.TraitInfo; import net.citizensnpcs.api.util.Messaging; -import net.citizensnpcs.api.util.NBTStorage; import net.citizensnpcs.api.util.Placeholders; import net.citizensnpcs.api.util.SpigotUtil; import net.citizensnpcs.api.util.Storage; @@ -177,14 +176,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { } private NPCDataStore createStorage(File folder) { - Storage saves = null; - String type = Setting.STORAGE_TYPE.asString(); - if (type.equalsIgnoreCase("nbt")) { - saves = new NBTStorage(new File(folder, Setting.STORAGE_FILE.asString()), "Citizens NPC Storage"); - } - if (saves == null) { - saves = new YamlStorage(new File(folder, Setting.STORAGE_FILE.asString()), "Citizens NPC Storage"); - } + Storage saves = new YamlStorage(new File(folder, Setting.STORAGE_FILE.asString()), "Citizens NPC Storage"); if (!saves.load()) return null; @@ -548,7 +540,6 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { return 0; return Iterables.size(npcRegistry); })); - metrics.addCustomChart(new Metrics.SimplePie("storage_type", () -> Setting.STORAGE_TYPE.asString())); metrics.addCustomChart(new Metrics.SingleLineChart("using_templates", () -> Math.min(1, Iterables.size(Template.getTemplates())))); metrics.addCustomChart(new Metrics.SimplePie("locale", () -> Locale.getDefault().getLanguage())); @@ -613,7 +604,7 @@ public class Citizens extends JavaPlugin implements CitizensPlugin { Messaging.logTr(Messages.NUM_LOADED_NOTIFICATION, Iterables.size(npcRegistry), "?"); startMetrics(); - scheduleSaveTask(Setting.SAVE_TASK_DELAY.asTicks()); + scheduleSaveTask(Setting.SAVE_TASK_FREQUENCY.asTicks()); Bukkit.getPluginManager().callEvent(new CitizensEnableEvent()); new PlayerUpdateTask().runTaskTimer(Citizens.this, 0, 1); enabled = true; diff --git a/main/src/main/java/net/citizensnpcs/Settings.java b/main/src/main/java/net/citizensnpcs/Settings.java index 97781235d..a4d2d0620 100644 --- a/main/src/main/java/net/citizensnpcs/Settings.java +++ b/main/src/main/java/net/citizensnpcs/Settings.java @@ -140,7 +140,7 @@ public class Settings { "The distance in blocks where the NPC will switch to walking straight towards the target instead of pathfinding
Currently only for dynamic targets like entities", "npc.pathfinding.straight-line-targeting-distance", 5), DEFAULT_STUCK_ACTION( - "The default action to perform when NPCs are unable to find a path or are stuck in the same block for too long. Supported options are: 'teleport to destination' or 'none'", + "The default action to perform when NPCs are unable to find a path or are stuck in the same block for too long
Supported options are: 'teleport to destination' or 'none'", "npc.pathfinding.default-stuck-action", "none"), DEFAULT_TALK_CLOSE("npc.default.talk-close.enabled", false), DEFAULT_TALK_CLOSE_RANGE("Default talk close range in blocks", "npc.default.talk-close.range", 5), @@ -167,7 +167,7 @@ public class Settings { "npc.pathfinding.disable-mc-fallback-navigation", true), DISABLE_TABLIST("Whether to remove NPCs from the tablist", "npc.tablist.disable", true), DISPLAY_ENTITY_HOLOGRAMS( - "Whether to use display entities for holograms by default. In theory more performant than armor stands. Requires 1.19.4 or above. Defaults to false", + "Whether to use display entities for holograms by default (in theory more performant than armor stands)
Requires 1.19.4 or above, defaults to false", "npc.hologram.use-display-entities", false), ENTITY_SPAWN_WAIT_DURATION( "Entities are no longer spawned until the chunks are loaded from disk
Wait for chunk loading for one second by default, increase if your disk is slow", @@ -176,7 +176,7 @@ public class Settings { FOLLOW_ACROSS_WORLDS("Whether /npc follow will teleport across worlds to follow its target", "npc.follow.teleport-across-worlds", false), HIGHLIGHT_COLOUR("general.color-scheme.message-highlight", "yellow"), - HOLOGRAM_ALWAYS_UPDATE_POSITION("Whether to always update the hologram position every tick.", + HOLOGRAM_ALWAYS_UPDATE_POSITION("Whether to always update the hologram position every tick", "npc.hologram.always-update-position", false), HOLOGRAM_UPDATE_RATE("How often to update hologram names (including placeholders)", "npc.hologram.update-rate-ticks", "npc.hologram.update-rate", "1s"), @@ -184,7 +184,7 @@ public class Settings { INITIAL_PLAYER_JOIN_SKIN_PACKET_DELAY("How long to wait before sending skins to joined players", "npc.skins.player-join-update-delay-ticks", "npc.skins.player-join-update-delay", "1s"), KEEP_CHUNKS_LOADED("Whether to keep NPC chunks loaded", "npc.chunks.always-keep-loaded", false), - LOCALE("Controls translation files - defaults to your system language, set to en if English required", + LOCALE("Controls translation files - defaults to your system language, set to 'en' if English required", "general.translation.locale", ""), MAX_CONTROLLABLE_FLIGHT_SPEED( "The maximum flying speed that controllable NPCs can reach, in Minecraft velocity units", @@ -228,7 +228,7 @@ public class Settings { "Please wait for {minutes} minutes and {seconds_over} seconds."), NPC_COST("The default cost to create an NPC", "economy.npc.cost", 100D), NPC_SKIN_FETCH_DEFAULT( - "Whether to try and look for the player skin for all new NPCs. If this is set to false and you create an NPC named Dinnerbone, the NPC will have the default (steve/alex/...) skin rather than trying to fetch the Dinnerbone skin.", + "Whether to try and look for the player skin for all new NPCs
If this is set to false and you create an NPC named Dinnerbone, the NPC will have the default (steve/alex/etc) skin rather than trying to fetch the Dinnerbone skin", "npc.skins.try-fetch-default-skin", true), NPC_SKIN_RETRY_DELAY("How long before retrying skin requests (typically due to Mojang rate limiting)", "npc.skins.retry-delay", "5s"), @@ -248,20 +248,19 @@ public class Settings { REMOVE_PLAYERS_FROM_PLAYER_LIST("Whether to remove NPCs from the Java list of players", "npc.player.remove-from-list", true), RESET_YAW_ON_SPAWN( - "Whether to reset NPC yaw on spawn. Currently this is implemented by an arm swing animation due to Minecraft limitations.", + "Whether to reset NPC yaw on spawn
Currently this is implemented by an arm swing animation due to Minecraft limitations", "npc.default.reset-yaw-on-spawn", true), RESOURCE_PACK_PATH("The resource pack path to save resource packs to", "general.resource-pack-path", "plugins/Citizens/resourcepack"), - SAVE_TASK_DELAY("How often to save NPCs to disk", "storage.save-task.delay", "1hr"), + SAVE_TASK_FREQUENCY("How often to save NPCs to disk", "storage.save-task.delay", "storage.save-task-frequency", + "1hr"), SELECTION_ITEM("The default item in hand to select an NPC", "npc.selection.item", "stick"), SELECTION_MESSAGE("npc.selection.message", "Selected [[]] (ID [[]])."), SERVER_OWNS_NPCS("Whether the server owns NPCs rather than individual players", "npc.server-ownership", false), SHOP_GLOBAL_VIEW_PERMISSION( - "The global view permission that players need to view any NPC shop. Defaults to empty (no permission required).", + "The global view permission that players need to view any NPC shop
Defaults to empty (no permission required).", "npc.shops.global-view-permission", ""), STORAGE_FILE("storage.file", "saves.yml"), - STORAGE_TYPE("Although technically Citizens can use NBT storage, it is not well tested and YAML is recommended", - "storage.type", "yaml"), TABLIST_REMOVE_PACKET_DELAY("How long to wait before sending the tablist remove packet", "npc.tablist.remove-packet-delay", "1t"), TALK_CLOSE_TO_NPCS("Whether to talk to NPCs (and therefore bystanders) as well as players", @@ -270,7 +269,7 @@ public class Settings { USE_BOAT_CONTROLS("Whether to change vehicle direction with movement instead of strafe controls", "npc.controllable.use-boat-controls", true), USE_NEW_PATHFINDER( - "Whether to use the Citizens pathfinder instead of the Minecraft pathfinder
Much more flexible, but may have different performance characteristics", + "Whether to use the Citizens pathfinder instead of the Minecraft pathfinder
Much more flexible, but may have different performance to Minecraft's pathfinder", "npc.pathfinding.use-new-finder", false), USE_SCOREBOARD_TEAMS("npc.scoreboard-teams.enable", true), WARN_ON_RELOAD("general.reload-warning-enabled", true),; diff --git a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java index 13d7b019c..a9c34bea8 100644 --- a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -339,7 +339,7 @@ public class NPCCommands { @Command( aliases = { "npc" }, - usage = "armorstand --visible [visible] --small [small] --gravity [gravity] --arms [arms] --baseplate [baseplate] --(body|leftarm|leftleg|rightarm|rightleg)pose [angle x,y,z]", + usage = "armorstand --visible [visible] --small [small] --marker [marker] --gravity [gravity] --arms [arms] --baseplate [baseplate] --(body|leftarm|leftleg|rightarm|rightleg)pose [angle x,y,z]", desc = "Edit armorstand properties", modifiers = { "armorstand" }, min = 1, @@ -349,7 +349,7 @@ public class NPCCommands { @Requirements(selected = true, ownership = true, types = EntityType.ARMOR_STAND) public void armorstand(CommandContext args, CommandSender sender, NPC npc, @Flag("visible") Boolean visible, @Flag("small") Boolean small, @Flag("gravity") Boolean gravity, @Flag("arms") Boolean arms, - @Flag("baseplate") Boolean baseplate) throws CommandException { + @Flag("marker") Boolean marker, @Flag("baseplate") Boolean baseplate) throws CommandException { ArmorStandTrait trait = npc.getOrAddTrait(ArmorStandTrait.class); if (visible != null) { trait.setVisible(visible); @@ -360,6 +360,9 @@ public class NPCCommands { if (gravity != null) { trait.setGravity(gravity); } + if (marker != null) { + trait.setMarker(marker); + } if (arms != null) { trait.setHasArms(arms); } diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index cefb23394..9aac7aa3b 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -275,6 +275,11 @@ public class CitizensNPC extends AbstractNPC { } } + @Override + public void setSneaking(boolean sneaking) { + getOrAddTrait(SneakTrait.class).setSneaking(sneaking); + } + @Override public boolean spawn(Location at) { return spawn(at, SpawnReason.PLUGIN); @@ -532,9 +537,6 @@ public class CitizensNPC extends AbstractNPC { } if (getEntity() instanceof Player) { updateUsingItemState((Player) getEntity()); - if (data().has(NPC.Metadata.SNEAKING) && !hasTrait(SneakTrait.class)) { - addTrait(SneakTrait.class); - } } } navigator.run(); diff --git a/main/src/main/java/net/citizensnpcs/trait/SneakTrait.java b/main/src/main/java/net/citizensnpcs/trait/SneakTrait.java index ccb727e81..05d53d9b8 100644 --- a/main/src/main/java/net/citizensnpcs/trait/SneakTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/SneakTrait.java @@ -1,6 +1,5 @@ package net.citizensnpcs.trait; -import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.persistence.Persist; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.TraitName; @@ -30,14 +29,6 @@ public class SneakTrait extends Trait { apply(); } - @Override - public void run() { - if (npc.data().has(NPC.Metadata.SNEAKING)) { - setSneaking(npc.data().get(NPC.Metadata.SNEAKING)); - npc.data().remove(NPC.Metadata.SNEAKING); - } - } - public void setSneaking(boolean sneak) { sneaking = sneak; apply(); From 19f08c4607dc96bdfd80c41d992dca006c7a7a4d Mon Sep 17 00:00:00 2001 From: fullwall Date: Sun, 18 Feb 2024 00:44:38 +0800 Subject: [PATCH 06/11] Attempt to replace the entity tracker without sending packets for initial spawn. EXPERIMENTAL: needs testing. Also add /npc armorstand --headpose --- .../java/net/citizensnpcs/EventListen.java | 8 ++++- .../main/java/net/citizensnpcs/Settings.java | 27 ++++++++------ .../citizensnpcs/commands/NPCCommands.java | 5 ++- .../net/citizensnpcs/npc/CitizensNPC.java | 35 ++++++++++++++----- .../java/net/citizensnpcs/npc/skin/Skin.java | 4 +-- .../npc/skin/SkinPacketTracker.java | 2 +- .../npc/skin/SkinUpdateTracker.java | 2 +- 7 files changed, 57 insertions(+), 26 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/EventListen.java b/main/src/main/java/net/citizensnpcs/EventListen.java index c72deaa84..e33be5b62 100644 --- a/main/src/main/java/net/citizensnpcs/EventListen.java +++ b/main/src/main/java/net/citizensnpcs/EventListen.java @@ -106,6 +106,7 @@ import net.citizensnpcs.api.util.Messaging; import net.citizensnpcs.editor.Editor; import net.citizensnpcs.npc.ai.NPCHolder; import net.citizensnpcs.npc.skin.SkinUpdateTracker; +import net.citizensnpcs.npc.skin.SkinnableEntity; import net.citizensnpcs.trait.ClickRedirectTrait; import net.citizensnpcs.trait.CommandTrait; import net.citizensnpcs.trait.Controllable; @@ -462,6 +463,12 @@ public class EventListen implements Listener { @EventHandler(ignoreCancelled = true) public void onNPCLinkToPlayer(NPCLinkToPlayerEvent event) { NPC npc = event.getNPC(); + if (npc.getEntity() instanceof SkinnableEntity) { + SkinnableEntity skinnable = (SkinnableEntity) npc.getEntity(); + if (skinnable.getSkinTracker().getSkin() != null) { + skinnable.getSkinTracker().getSkin().apply(skinnable); + } + } if (npc.isSpawned() && npc.getEntity().getType() == EntityType.PLAYER) { onNPCPlayerLinkToPlayer(event); } @@ -483,7 +490,6 @@ public class EventListen implements Listener { NMS.sendPositionUpdateNearby(tracker, false, null, null, NMS.getHeadYaw(tracker)); }, Setting.TABLIST_REMOVE_PACKET_DELAY.asTicks() + 1); - boolean resetYaw = event.getNPC().data().get(NPC.Metadata.RESET_YAW_ON_SPAWN, Setting.RESET_YAW_ON_SPAWN.asBoolean()); boolean sendTabRemove = NMS.sendTabListAdd(event.getPlayer(), (Player) tracker); diff --git a/main/src/main/java/net/citizensnpcs/Settings.java b/main/src/main/java/net/citizensnpcs/Settings.java index a4d2d0620..d8946cdc7 100644 --- a/main/src/main/java/net/citizensnpcs/Settings.java +++ b/main/src/main/java/net/citizensnpcs/Settings.java @@ -274,10 +274,10 @@ public class Settings { USE_SCOREBOARD_TEAMS("npc.scoreboard-teams.enable", true), WARN_ON_RELOAD("general.reload-warning-enabled", true),; - protected String comments; + private String comments; private Duration duration; - protected String migrate; - protected String path; + private String migrate; + private final String path; protected Object value; Setting(String path, Object value) { @@ -347,14 +347,7 @@ public class Settings { } protected void loadFromKey(DataKey root) { - if (SUPPORTS_SET_COMMENTS && root.keyExists(path)) { - try { - ((YamlKey) root).getSection("").setComments(path, - comments == null ? null : Arrays.asList(comments.split("
"))); - } catch (Throwable t) { - SUPPORTS_SET_COMMENTS = false; - } - } + setComments(root); if (migrate != null && root.keyExists(migrate) && !root.keyExists(path)) { value = root.getRaw(migrate); root.removeKey(migrate); @@ -365,6 +358,18 @@ public class Settings { protected void setAtKey(DataKey root) { root.setRaw(path, value); + setComments(root); + } + + private void setComments(DataKey root) { + if (SUPPORTS_SET_COMMENTS && root.keyExists(path)) { + try { + ((YamlKey) root).getSection("").setComments(path, + comments == null ? null : Arrays.asList(comments.split("
"))); + } catch (Throwable t) { + SUPPORTS_SET_COMMENTS = false; + } + } } } diff --git a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java index a9c34bea8..97d5906a9 100644 --- a/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java +++ b/main/src/main/java/net/citizensnpcs/commands/NPCCommands.java @@ -339,7 +339,7 @@ public class NPCCommands { @Command( aliases = { "npc" }, - usage = "armorstand --visible [visible] --small [small] --marker [marker] --gravity [gravity] --arms [arms] --baseplate [baseplate] --(body|leftarm|leftleg|rightarm|rightleg)pose [angle x,y,z]", + usage = "armorstand --visible [visible] --small [small] --marker [marker] --gravity [gravity] --arms [arms] --baseplate [baseplate] --(head|body|leftarm|leftleg|rightarm|rightleg)pose [angle x,y,z]", desc = "Edit armorstand properties", modifiers = { "armorstand" }, min = 1, @@ -370,6 +370,9 @@ public class NPCCommands { trait.setHasBaseplate(baseplate); } ArmorStand ent = (ArmorStand) npc.getEntity(); + if (args.hasValueFlag("headpose")) { + ent.setHeadPose(args.parseEulerAngle(args.getFlag("headpose"))); + } if (args.hasValueFlag("bodypose")) { ent.setBodyPose(args.parseEulerAngle(args.getFlag("bodypose"))); } diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index 9aac7aa3b..29343e64f 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java @@ -306,7 +306,20 @@ public class CitizensNPC extends AbstractNPC { entityController.create(at.clone(), this); getEntity().setMetadata("NPC", new FixedMetadataValue(CitizensAPI.getPlugin(), true)); getEntity().setMetadata("NPC-ID", new FixedMetadataValue(CitizensAPI.getPlugin(), getId())); - + // Spawning the entity will create an entity tracker that is not controlled by Citizens. This is fixed later in + // spawning; to avoid sending packets twice, try to hide the entity initially + if (SUPPORT_VISIBLE_BY_DEFAULT) { + try { + getEntity().setVisibleByDefault(false); + } catch (NoSuchMethodError err) { + SUPPORT_VISIBLE_BY_DEFAULT = false; + } + } + if (!SUPPORT_VISIBLE_BY_DEFAULT && getEntity().getType() == EntityType.PLAYER) { + for (Player player : Bukkit.getServer().getOnlinePlayers()) { + player.hidePlayer((Player) getEntity()); + } + } if (getEntity() instanceof SkinnableEntity && !hasTrait(SkinLayers.class)) { ((SkinnableEntity) getEntity()).setSkinFlags(EnumSet.allOf(SkinLayers.Layer.class)); } @@ -331,15 +344,12 @@ public class CitizensNPC extends AbstractNPC { Bukkit.getPluginManager().callEvent(new NPCNeedsRespawnEvent(this, at)); return false; } - // send skin packets, if applicable, before other NMS packets are sent - SkinnableEntity skinnable = getEntity() instanceof SkinnableEntity ? (SkinnableEntity) getEntity() : null; - if (skinnable != null) { - skinnable.getSkinTracker().onSpawnNPC(); - } NMS.setLocationDirectly(getEntity(), at); NMS.setHeadYaw(getEntity(), at.getYaw()); NMS.setBodyYaw(getEntity(), at.getYaw()); + // Paper now doesn't actually set entities as valid for a few ticks while adding entities to chunks + // Need to check the entity is really valid for a few ticks before finalising spawning Location to = at; Consumer postSpawn = new Consumer() { private int timer; @@ -371,7 +381,6 @@ public class CitizensNPC extends AbstractNPC { return; } navigator.onSpawn(); - for (Trait trait : Iterables.toArray(traits.values(), Trait.class)) { try { trait.onSpawn(); @@ -380,9 +389,16 @@ public class CitizensNPC extends AbstractNPC { ex.printStackTrace(); } } - EntityType type = getEntity().getType(); + // Replace the entity tracker and attempt to show the entity NMS.replaceTracker(getEntity()); - + if (SUPPORT_VISIBLE_BY_DEFAULT) { + getEntity().setVisibleByDefault(true); + } else if (getEntity().getType() == EntityType.PLAYER) { + for (Player player : Bukkit.getServer().getOnlinePlayers()) { + player.showPlayer((Player) getEntity()); + } + } + EntityType type = getEntity().getType(); if (type.isAlive()) { LivingEntity entity = (LivingEntity) getEntity(); entity.setRemoveWhenFarAway(false); @@ -617,4 +633,5 @@ public class CitizensNPC extends AbstractNPC { private static boolean SUPPORT_PICKUP_ITEMS = true; private static boolean SUPPORT_SILENT = true; private static boolean SUPPORT_USE_ITEM = true; + private static boolean SUPPORT_VISIBLE_BY_DEFAULT = true; } diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/Skin.java b/main/src/main/java/net/citizensnpcs/npc/skin/Skin.java index 5129560d2..764f3b456 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/Skin.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/Skin.java @@ -99,9 +99,9 @@ public class Skin { && !npc.getOrAddTrait(SkinTrait.class).fetchDefaultSkin()) return false; - if (hasFetched) + if (hasFetched) { return true; - else { + } else { if (!fetching) { fetch(); } diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java b/main/src/main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java index 06412cad2..7b31a879f 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/SkinPacketTracker.java @@ -191,7 +191,7 @@ public class SkinPacketTracker { inProgress.put(player.getUniqueId(), entry); skin.apply(entity); if (NMS.sendTabListAdd(player, entity.getBukkitEntity())) { - scheduleRemovePacket(entry, 2); + scheduleRemovePacket(entry, Setting.TABLIST_REMOVE_PACKET_DELAY.asTicks()); } } diff --git a/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java b/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java index bb5775928..e7dcdb90c 100644 --- a/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java +++ b/main/src/main/java/net/citizensnpcs/npc/skin/SkinUpdateTracker.java @@ -279,7 +279,7 @@ public class SkinUpdateTracker { } /** - * Update a player with skin related packets from nearby skinnable NPC's. + * Update a player with skin related packets from nearby skinnable NPCs. * * @param player * The player to update. From f498391c1fb073a1ea8b09aff07c9fed9e397092 Mon Sep 17 00:00:00 2001 From: fullwall Date: Sun, 18 Feb 2024 21:06:02 +0800 Subject: [PATCH 07/11] Replace $/{ in shop placeholders --- main/src/main/java/net/citizensnpcs/trait/ShopTrait.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java index 6ab3c990c..6d664746e 100644 --- a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java @@ -446,8 +446,11 @@ public class ShopTrait extends Trait { StringBuffer sb = new StringBuffer(); Matcher matcher = PLACEHOLDER_REGEX.matcher(string); while (matcher.find()) { - matcher.appendReplacement(sb, Joiner.on(", ").join( - Iterables.transform(matcher.group(1).equals("cost") ? cost : result, NPCShopAction::describe))); + matcher.appendReplacement(sb, + Joiner.on(", ") + .join(Iterables.transform(matcher.group(1).equalsIgnoreCase("cost") ? cost : result, + NPCShopAction::describe)) + .replace("$", "\\$").replace("{", "\\{")); } matcher.appendTail(sb); return sb.toString(); From 8864d8979128fb47f1351ddb53dd7acaeaa0479a Mon Sep 17 00:00:00 2001 From: fullwall Date: Mon, 19 Feb 2024 05:41:48 +0800 Subject: [PATCH 08/11] Preliminary trader view support for /npc shop --- .../net/citizensnpcs/trait/ShopTrait.java | 197 +++++++++++++++--- .../citizensnpcs/trait/shop/ItemAction.java | 12 +- .../main/java/net/citizensnpcs/util/NMS.java | 5 +- .../java/net/citizensnpcs/util/NMSBridge.java | 2 +- .../nms/v1_10_R1/util/NMSImpl.java | 2 +- .../nms/v1_11_R1/util/NMSImpl.java | 2 +- .../nms/v1_12_R1/util/NMSImpl.java | 2 +- .../nms/v1_13_R2/util/NMSImpl.java | 2 +- .../nms/v1_14_R1/util/NMSImpl.java | 2 +- .../nms/v1_15_R1/util/NMSImpl.java | 2 +- .../nms/v1_16_R3/util/NMSImpl.java | 2 +- .../nms/v1_17_R1/util/NMSImpl.java | 2 +- .../nms/v1_18_R2/util/NMSImpl.java | 2 +- .../nms/v1_19_R3/util/NMSImpl.java | 2 +- .../nms/v1_20_R3/util/NMSImpl.java | 2 +- .../nms/v1_8_R3/util/NMSImpl.java | 2 +- 16 files changed, 191 insertions(+), 49 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java index 6d664746e..5b576ca7b 100644 --- a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java @@ -13,10 +13,19 @@ import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.inventory.InventoryType.SlotType; +import org.bukkit.event.inventory.TradeSelectEvent; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.Merchant; +import org.bukkit.inventory.MerchantRecipe; import org.bukkit.inventory.meta.ItemMeta; import com.google.common.base.Joiner; @@ -31,6 +40,7 @@ import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.gui.CitizensInventoryClickEvent; import net.citizensnpcs.api.gui.ClickHandler; import net.citizensnpcs.api.gui.InputMenus; +import net.citizensnpcs.api.gui.InputMenus.Choice; import net.citizensnpcs.api.gui.InventoryMenu; import net.citizensnpcs.api.gui.InventoryMenuPage; import net.citizensnpcs.api.gui.InventoryMenuPattern; @@ -91,7 +101,6 @@ public class ShopTrait extends Trait { @Override public void onRemove() { - Messaging.debug("Removing", npc, "default shop due to onRemove"); shops.deleteShop(getDefaultShop()); } @@ -113,6 +122,8 @@ public class ShopTrait extends Trait { @Persist private String title; @Persist + private ShopType type = ShopType.DEFAULT; + @Persist private String viewPermission; private NPCShop() { @@ -138,7 +149,11 @@ public class ShopTrait extends Trait { Messaging.sendError(sender, "Empty shop"); return; } - InventoryMenu.createSelfRegistered(new NPCShopViewer(this, sender)).present(sender); + if (type == ShopType.TRADER) { + CitizensAPI.registerEvents(new NPCTraderShopViewer(this, sender)); + } else { + InventoryMenu.createSelfRegistered(new NPCShopViewer(this, sender)).present(sender); + } } public void displayEditor(ShopTrait trait, Player sender) { @@ -160,6 +175,14 @@ public class ShopTrait extends Trait { return viewPermission; } + public ShopType getShopType() { + return type; + } + + public String getTitle() { + return title == null ? "" : title; + } + public void removePage(int index) { for (int i = 0; i < pages.size(); i++) { if (pages.get(i).index == index) { @@ -177,6 +200,14 @@ public class ShopTrait extends Trait { viewPermission = null; } } + + public void setShopType(ShopType type) { + this.type = type; + } + + public void setTitle(String title) { + this.title = title; + } } @Menu(title = "NPC Shop Contents Editor", type = InventoryType.CHEST, dimensions = { 5, 9 }) @@ -233,11 +264,12 @@ public class ShopTrait extends Trait { })); }); } - InventoryMenuSlot prev = ctx.getSlot(4 * 9 + 3); - InventoryMenuSlot edit = ctx.getSlot(4 * 9 + 4); - InventoryMenuSlot next = ctx.getSlot(4 * 9 + 5); + InventoryMenuSlot prev = ctx.getSlot(shop.getShopType().prevSlotIndex); + InventoryMenuSlot edit = ctx.getSlot(shop.getShopType().editSlotIndex); + InventoryMenuSlot next = ctx.getSlot(shop.getShopType().nextSlotIndex); if (page > 0) { - prev.setItemStack(shopPage.getNextPageItem(null, 4 * 9 + 3), "Previous page (" + newPage + ")"); + prev.setItemStack(shopPage.getNextPageItem(null, shop.getShopType().prevSlotIndex), + "Previous page (" + newPage + ")"); Consumer prevItemEditor = prev.getClickHandlers().get(0); prev.setClickHandler(evt -> { if (evt.isShiftClick()) { @@ -248,7 +280,7 @@ public class ShopTrait extends Trait { changePage(page - 1); }); } - next.setItemStack(shopPage.getNextPageItem(null, 4 * 9 + 5), + next.setItemStack(shopPage.getNextPageItem(null, shop.getShopType().nextSlotIndex), page + 1 >= shop.pages.size() ? "New page" : "Next page (" + (newPage + 1) + ")"); Consumer nextItemEditor = next.getClickHandlers().get(0); next.setClickHandler(evt -> { @@ -271,6 +303,11 @@ public class ShopTrait extends Trait { }); } + @Override + public Inventory createInventory(String title) { + return Bukkit.createInventory(null, shop.getShopType().inventorySize, "NPC Shop Contents Editor"); + } + @Override public void initialise(MenuContext ctx) { this.ctx = ctx; @@ -395,23 +432,22 @@ public class ShopTrait extends Trait { } } - public void onClick(NPCShop shop, InventoryClickEvent event, boolean secondClick) { - Player player = (Player) event.getWhoClicked(); + public void onClick(NPCShop shop, Player player, boolean shiftClick, boolean secondClick) { if (purchases.containsKey(player.getUniqueId()) && timesPurchasable > 0 && purchases.get(player.getUniqueId()) == timesPurchasable) { if (alreadyPurchasedMessage != null) { - Messaging.sendColorless(event.getWhoClicked(), placeholders(alreadyPurchasedMessage, player)); + Messaging.sendColorless(player, placeholders(alreadyPurchasedMessage, player)); } return; } if (clickToConfirmMessage != null && !secondClick) { - Messaging.sendColorless(event.getWhoClicked(), placeholders(clickToConfirmMessage, player)); + Messaging.sendColorless(player, placeholders(clickToConfirmMessage, player)); return; } int max = Integer.MAX_VALUE; - if (maxRepeatsOnShiftClick && event.isShiftClick()) { + if (maxRepeatsOnShiftClick && shiftClick) { for (NPCShopAction action : cost) { - int r = action.getMaxRepeats(event.getWhoClicked()); + int r = action.getMaxRepeats(player); if (r != -1) { max = Math.min(max, r); } @@ -420,19 +456,19 @@ public class ShopTrait extends Trait { return; } int repeats = max == Integer.MAX_VALUE ? 1 : max; - List take = apply(cost, action -> action.take(event.getWhoClicked(), repeats)); + List take = apply(cost, action -> action.take(player, repeats)); if (take == null) { if (costMessage != null) { - Messaging.sendColorless(event.getWhoClicked(), placeholders(costMessage, player)); + Messaging.sendColorless(player, placeholders(costMessage, player)); } return; } - if (apply(result, action -> action.grant(event.getWhoClicked(), repeats)) == null) { + if (apply(result, action -> action.grant(player, repeats)) == null) { take.forEach(Transaction::rollback); return; } if (resultMessage != null) { - Messaging.sendColorless(event.getWhoClicked(), placeholders(resultMessage, player)); + Messaging.sendColorless(player, placeholders(resultMessage, player)); } if (timesPurchasable > 0) { int timesPurchasedAlready = purchases.get(player.getUniqueId()) == null ? 0 @@ -714,7 +750,7 @@ public class ShopTrait extends Trait { this.ctx = ctx; ctx.getSlot(0) .setDescription("Edit permission required to view shop
" + shop.getRequiredPermission()); - ctx.getSlot(4).setDescription("Edit shop title
" + shop.title); + ctx.getSlot(4).setDescription("Edit shop title
" + shop.getTitle()); if (trait != null) { ctx.getSlot(6).setDescription( "Show shop on right click
" + shop.getName().equals(trait.rightClickShop)); @@ -731,9 +767,23 @@ public class ShopTrait extends Trait { ctx.getMenu().transition(InputMenus.stringSetter(shop::getRequiredPermission, shop::setPermission)); } + @MenuSlot(slot = { 0, 8 }, material = Material.CHEST, amount = 1, title = "Set shop type") + public void onSetInventoryType(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { + ctx.getMenu() + .transition(InputMenus.picker("Set shop type", + (Choice choice) -> shop.setShopType(choice.getValue()), + Choice.of(ShopType.DEFAULT, Material.CHEST, "Default (5x9 chest)", + shop.type == ShopType.DEFAULT), + Choice.of(ShopType.CHEST_4X9, Material.CHEST, "4x9 chest", shop.type == ShopType.CHEST_4X9), + Choice.of(ShopType.CHEST_3X9, Material.CHEST, "3x9 chest", shop.type == ShopType.CHEST_3X9), + Choice.of(ShopType.CHEST_2X9, Material.CHEST, "2x9 chest", shop.type == ShopType.CHEST_2X9), + Choice.of(ShopType.CHEST_1X9, Material.CHEST, "1x9 chest", shop.type == ShopType.CHEST_1X9), + Choice.of(ShopType.TRADER, Material.EMERALD, "Trader", shop.type == ShopType.TRADER))); + } + @MenuSlot(slot = { 0, 4 }, material = Material.NAME_TAG, amount = 1) public void onSetTitle(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { - ctx.getMenu().transition(InputMenus.stringSetter(() -> shop.title, newTitle -> shop.title = newTitle)); + ctx.getMenu().transition(InputMenus.stringSetter(shop::getTitle, shop::setTitle)); } @MenuSlot(slot = { 0, 6 }, compatMaterial = { "COMMAND_BLOCK", "COMMAND" }, amount = 1) @@ -782,15 +832,16 @@ public class ShopTrait extends Trait { ctx.getSlot(i).setItemStack(item.getDisplayItem(player)); ctx.getSlot(i).setClickHandler(evt -> { evt.setCancelled(true); - item.onClick(shop, evt, lastClickedItem == item); + item.onClick(shop, (Player) evt.getWhoClicked(), evt.isShiftClick(), lastClickedItem == item); lastClickedItem = item; }); } - InventoryMenuSlot prev = ctx.getSlot(4 * 9 + 3); - InventoryMenuSlot next = ctx.getSlot(4 * 9 + 5); + InventoryMenuSlot prev = ctx.getSlot(shop.getShopType().prevSlotIndex); + InventoryMenuSlot next = ctx.getSlot(shop.getShopType().nextSlotIndex); if (currentPage > 0) { prev.clear(); - prev.setItemStack(page.getPreviousPageItem(player, 4 * 9 + 3), "Previous page (" + newPage + ")"); + prev.setItemStack(page.getPreviousPageItem(player, shop.getShopType().prevSlotIndex), + "Previous page (" + newPage + ")"); prev.setClickHandler(evt -> { evt.setCancelled(true); changePage(currentPage - 1); @@ -798,7 +849,8 @@ public class ShopTrait extends Trait { } if (currentPage + 1 < shop.pages.size()) { next.clear(); - next.setItemStack(page.getNextPageItem(player, 4 * 9 + 5), "Next page (" + (newPage + 1) + ")"); + next.setItemStack(page.getNextPageItem(player, shop.getShopType().nextSlotIndex), + "Next page (" + (newPage + 1) + ")"); next.setClickHandler(evt -> { evt.setCancelled(true); changePage(currentPage + 1); @@ -808,8 +860,8 @@ public class ShopTrait extends Trait { @Override public Inventory createInventory(String title) { - return Bukkit.createInventory(null, 45, shop.title == null || shop.title.isEmpty() ? "Shop" - : Messaging.parseComponents(Placeholders.replace(shop.title, player))); + return Bukkit.createInventory(null, shop.getShopType().inventorySize, shop.getTitle().isEmpty() ? "Shop" + : Messaging.parseComponents(Placeholders.replace(shop.getTitle(), player))); } @Override @@ -819,6 +871,99 @@ public class ShopTrait extends Trait { } } + public static class NPCTraderShopViewer implements Listener { + private int lastClickedTrade = -1; + private final Player player; + private int selectedTrade = -1; + private final NPCShop shop; + private final Map trades; + private final InventoryView view; + + public NPCTraderShopViewer(NPCShop shop, Player player) { + this.shop = shop; + this.player = player; + Map tradesMap = Maps.newHashMap(); + Merchant merchant = Bukkit.createMerchant(shop.getTitle()); + List recipes = Lists.newArrayList(); + for (NPCShopPage page : shop.pages) { + for (NPCShopItem item : page.items.values()) { + ItemStack result = item.getDisplayItem(player); + if (result == null) + continue; + MerchantRecipe recipe = new MerchantRecipe(result.clone(), 100000000); + for (NPCShopAction action : item.cost) { + if (action instanceof ItemAction) { + for (ItemStack stack : ((ItemAction) action).items) { + recipe.addIngredient(stack.clone()); + if (recipe.getIngredients().size() == 2) + break; + } + } + } + if (recipe.getIngredients().size() == 0) + continue; + tradesMap.put(recipes.size(), item); + recipes.add(recipe); + } + } + merchant.setRecipes(recipes); + trades = tradesMap; + view = player.openMerchant(merchant, true); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent evt) { + if (!evt.getView().equals(view)) + return; + evt.setCancelled(true); + if (evt.getSlotType() != SlotType.RESULT) + return; + trades.get(selectedTrade).onClick(shop, player, evt.getClick().isShiftClick(), + lastClickedTrade == selectedTrade); + lastClickedTrade = selectedTrade; + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent evt) { + if (!evt.getPlayer().equals(player)) + return; + HandlerList.unregisterAll(this); + } + + @EventHandler + public void onTradeSelect(TradeSelectEvent evt) { + if (!evt.getView().equals(view)) + return; + selectedTrade = evt.getIndex(); + lastClickedTrade = -1; + } + } + + public enum ShopType { + CHEST_1X9(1 * 9, 7, 6, 8), + CHEST_2X9(2 * 9), + CHEST_3X9(3 * 9), + CHEST_4X9(4 * 9), + DEFAULT(5 * 9), + TRADER(5 * 9); + + private final int editSlotIndex; + private final int inventorySize; + private final int nextSlotIndex; + private final int prevSlotIndex; + + ShopType(int inventorySize) { + this(inventorySize, inventorySize - 9 + 3, inventorySize - 9 + 4, inventorySize - 9 + 5); + } + + ShopType(int inventorySize, int prevSlotIndex, int editSlotIndex, int nextSlotIndex) { + this.inventorySize = inventorySize; + this.prevSlotIndex = prevSlotIndex; + this.editSlotIndex = editSlotIndex; + this.nextSlotIndex = nextSlotIndex; + } + } + static { NPCShopAction.register(ItemAction.class, "items", new ItemActionGUI()); NPCShopAction.register(PermissionAction.class, "permissions", new PermissionActionGUI()); diff --git a/main/src/main/java/net/citizensnpcs/trait/shop/ItemAction.java b/main/src/main/java/net/citizensnpcs/trait/shop/ItemAction.java index 90fe45fcb..274b24805 100644 --- a/main/src/main/java/net/citizensnpcs/trait/shop/ItemAction.java +++ b/main/src/main/java/net/citizensnpcs/trait/shop/ItemAction.java @@ -56,18 +56,18 @@ public class ItemAction extends NPCShopAction { ItemStack[] contents = source.getContents(); for (int i = 0; i < contents.length; i++) { ItemStack toMatch = contents[i]; - if (toMatch == null || toMatch.getType() == Material.AIR || tooDamaged(toMatch)) { + if (toMatch == null || toMatch.getType() == Material.AIR || tooDamaged(toMatch)) continue; - } + toMatch = toMatch.clone(); for (int j = 0; j < items.size(); j++) { - if (toMatch == null) { + if (toMatch == null) break; - } + ItemStack item = items.get(j); - if (req.get(j) <= 0 || !matches(item, toMatch)) { + if (req.get(j) <= 0 || !matches(item, toMatch)) continue; - } + int remaining = req.get(j); int taken = toMatch.getAmount() > remaining ? remaining : toMatch.getAmount(); diff --git a/main/src/main/java/net/citizensnpcs/util/NMS.java b/main/src/main/java/net/citizensnpcs/util/NMS.java index ab74489d4..2f8c7f85c 100644 --- a/main/src/main/java/net/citizensnpcs/util/NMS.java +++ b/main/src/main/java/net/citizensnpcs/util/NMS.java @@ -716,7 +716,7 @@ public class NMS { } public static void openHorseScreen(Tameable horse, Player equipper) { - BRIDGE.openHorseScreen(horse, equipper); + BRIDGE.openHorseInventory(horse, equipper); } public static void playAnimation(PlayerAnimation animation, Player player, Iterable to) { @@ -986,9 +986,6 @@ public class NMS { } catch (ClassNotFoundException e) { PAPER_KNOCKBACK_EVENT_EXISTS = false; } - } - - static { giveReflectiveAccess(Field.class, NMS.class); MODIFIERS_FIELD = NMS.getField(Field.class, "modifiers", false); } diff --git a/main/src/main/java/net/citizensnpcs/util/NMSBridge.java b/main/src/main/java/net/citizensnpcs/util/NMSBridge.java index 9846ac6ca..1f92f596b 100644 --- a/main/src/main/java/net/citizensnpcs/util/NMSBridge.java +++ b/main/src/main/java/net/citizensnpcs/util/NMSBridge.java @@ -151,7 +151,7 @@ public interface NMSBridge { public InventoryView openAnvilInventory(Player player, Inventory anvil, String title); - public void openHorseScreen(Tameable horse, Player equipper); + public void openHorseInventory(Tameable horse, Player equipper); public void playAnimation(PlayerAnimation animation, Player player, Iterable to); diff --git a/v1_10_R1/src/main/java/net/citizensnpcs/nms/v1_10_R1/util/NMSImpl.java b/v1_10_R1/src/main/java/net/citizensnpcs/nms/v1_10_R1/util/NMSImpl.java index 15fd90c8a..00616e900 100644 --- a/v1_10_R1/src/main/java/net/citizensnpcs/nms/v1_10_R1/util/NMSImpl.java +++ b/v1_10_R1/src/main/java/net/citizensnpcs/nms/v1_10_R1/util/NMSImpl.java @@ -949,7 +949,7 @@ public class NMSImpl implements NMSBridge { } @Override - public void openHorseScreen(Tameable horse, Player equipper) { + public void openHorseInventory(Tameable horse, Player equipper) { EntityLiving handle = NMSImpl.getHandle((LivingEntity) horse); EntityLiving equipperHandle = NMSImpl.getHandle(equipper); if (handle == null || equipperHandle == null) diff --git a/v1_11_R1/src/main/java/net/citizensnpcs/nms/v1_11_R1/util/NMSImpl.java b/v1_11_R1/src/main/java/net/citizensnpcs/nms/v1_11_R1/util/NMSImpl.java index 09d5f123e..4eda0cd95 100644 --- a/v1_11_R1/src/main/java/net/citizensnpcs/nms/v1_11_R1/util/NMSImpl.java +++ b/v1_11_R1/src/main/java/net/citizensnpcs/nms/v1_11_R1/util/NMSImpl.java @@ -1002,7 +1002,7 @@ public class NMSImpl implements NMSBridge { } @Override - public void openHorseScreen(Tameable horse, Player equipper) { + public void openHorseInventory(Tameable horse, Player equipper) { EntityLiving handle = NMSImpl.getHandle((LivingEntity) horse); EntityLiving equipperHandle = NMSImpl.getHandle(equipper); if (handle == null || equipperHandle == null) diff --git a/v1_12_R1/src/main/java/net/citizensnpcs/nms/v1_12_R1/util/NMSImpl.java b/v1_12_R1/src/main/java/net/citizensnpcs/nms/v1_12_R1/util/NMSImpl.java index a529b0967..2ea000ca1 100644 --- a/v1_12_R1/src/main/java/net/citizensnpcs/nms/v1_12_R1/util/NMSImpl.java +++ b/v1_12_R1/src/main/java/net/citizensnpcs/nms/v1_12_R1/util/NMSImpl.java @@ -1009,7 +1009,7 @@ public class NMSImpl implements NMSBridge { } @Override - public void openHorseScreen(Tameable horse, Player equipper) { + public void openHorseInventory(Tameable horse, Player equipper) { EntityLiving handle = NMSImpl.getHandle((LivingEntity) horse); EntityLiving equipperHandle = NMSImpl.getHandle(equipper); if (handle == null || equipperHandle == null) diff --git a/v1_13_R2/src/main/java/net/citizensnpcs/nms/v1_13_R2/util/NMSImpl.java b/v1_13_R2/src/main/java/net/citizensnpcs/nms/v1_13_R2/util/NMSImpl.java index eb859b996..5569ae67c 100644 --- a/v1_13_R2/src/main/java/net/citizensnpcs/nms/v1_13_R2/util/NMSImpl.java +++ b/v1_13_R2/src/main/java/net/citizensnpcs/nms/v1_13_R2/util/NMSImpl.java @@ -1045,7 +1045,7 @@ public class NMSImpl implements NMSBridge { } @Override - public void openHorseScreen(Tameable horse, Player equipper) { + public void openHorseInventory(Tameable horse, Player equipper) { EntityLiving handle = NMSImpl.getHandle((LivingEntity) horse); EntityLiving equipperHandle = NMSImpl.getHandle(equipper); if (handle == null || equipperHandle == null) diff --git a/v1_14_R1/src/main/java/net/citizensnpcs/nms/v1_14_R1/util/NMSImpl.java b/v1_14_R1/src/main/java/net/citizensnpcs/nms/v1_14_R1/util/NMSImpl.java index 081f413a2..cb6434035 100644 --- a/v1_14_R1/src/main/java/net/citizensnpcs/nms/v1_14_R1/util/NMSImpl.java +++ b/v1_14_R1/src/main/java/net/citizensnpcs/nms/v1_14_R1/util/NMSImpl.java @@ -1106,7 +1106,7 @@ public class NMSImpl implements NMSBridge { } @Override - public void openHorseScreen(Tameable horse, Player equipper) { + public void openHorseInventory(Tameable horse, Player equipper) { EntityLiving handle = NMSImpl.getHandle((LivingEntity) horse); EntityLiving equipperHandle = NMSImpl.getHandle(equipper); if (handle == null || equipperHandle == null) diff --git a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/util/NMSImpl.java b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/util/NMSImpl.java index f1acf66e6..d4d142385 100644 --- a/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/util/NMSImpl.java +++ b/v1_15_R1/src/main/java/net/citizensnpcs/nms/v1_15_R1/util/NMSImpl.java @@ -1123,7 +1123,7 @@ public class NMSImpl implements NMSBridge { } @Override - public void openHorseScreen(Tameable horse, Player equipper) { + public void openHorseInventory(Tameable horse, Player equipper) { EntityLiving handle = NMSImpl.getHandle(horse); EntityHuman equipperHandle = (EntityHuman) NMSImpl.getHandle(equipper); if (handle == null || equipperHandle == null) diff --git a/v1_16_R3/src/main/java/net/citizensnpcs/nms/v1_16_R3/util/NMSImpl.java b/v1_16_R3/src/main/java/net/citizensnpcs/nms/v1_16_R3/util/NMSImpl.java index 384c112f8..6cf99c401 100644 --- a/v1_16_R3/src/main/java/net/citizensnpcs/nms/v1_16_R3/util/NMSImpl.java +++ b/v1_16_R3/src/main/java/net/citizensnpcs/nms/v1_16_R3/util/NMSImpl.java @@ -1147,7 +1147,7 @@ public class NMSImpl implements NMSBridge { } @Override - public void openHorseScreen(Tameable horse, Player equipper) { + public void openHorseInventory(Tameable horse, Player equipper) { EntityLiving handle = NMSImpl.getHandle(horse); EntityHuman equipperHandle = (EntityHuman) NMSImpl.getHandle(equipper); if (handle == null || equipperHandle == null) diff --git a/v1_17_R1/src/main/java/net/citizensnpcs/nms/v1_17_R1/util/NMSImpl.java b/v1_17_R1/src/main/java/net/citizensnpcs/nms/v1_17_R1/util/NMSImpl.java index c6a706315..cfc707690 100644 --- a/v1_17_R1/src/main/java/net/citizensnpcs/nms/v1_17_R1/util/NMSImpl.java +++ b/v1_17_R1/src/main/java/net/citizensnpcs/nms/v1_17_R1/util/NMSImpl.java @@ -1153,7 +1153,7 @@ public class NMSImpl implements NMSBridge { } @Override - public void openHorseScreen(Tameable horse, Player equipper) { + public void openHorseInventory(Tameable horse, Player equipper) { LivingEntity handle = NMSImpl.getHandle(horse); ServerPlayer equipperHandle = (ServerPlayer) NMSImpl.getHandle(equipper); if (handle == null || equipperHandle == null) diff --git a/v1_18_R2/src/main/java/net/citizensnpcs/nms/v1_18_R2/util/NMSImpl.java b/v1_18_R2/src/main/java/net/citizensnpcs/nms/v1_18_R2/util/NMSImpl.java index 737932091..7b93ef115 100644 --- a/v1_18_R2/src/main/java/net/citizensnpcs/nms/v1_18_R2/util/NMSImpl.java +++ b/v1_18_R2/src/main/java/net/citizensnpcs/nms/v1_18_R2/util/NMSImpl.java @@ -1162,7 +1162,7 @@ public class NMSImpl implements NMSBridge { } @Override - public void openHorseScreen(Tameable horse, Player equipper) { + public void openHorseInventory(Tameable horse, Player equipper) { LivingEntity handle = NMSImpl.getHandle(horse); ServerPlayer equipperHandle = (ServerPlayer) NMSImpl.getHandle(equipper); if (handle == null || equipperHandle == null) diff --git a/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/NMSImpl.java b/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/NMSImpl.java index 7ee300441..1d1d9dfdc 100644 --- a/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/NMSImpl.java +++ b/v1_19_R3/src/main/java/net/citizensnpcs/nms/v1_19_R3/util/NMSImpl.java @@ -1289,7 +1289,7 @@ public class NMSImpl implements NMSBridge { } @Override - public void openHorseScreen(Tameable horse, Player equipper) { + public void openHorseInventory(Tameable horse, Player equipper) { LivingEntity handle = getHandle(horse); ServerPlayer equipperHandle = (ServerPlayer) getHandle(equipper); if (handle == null || equipperHandle == null) diff --git a/v1_20_R3/src/main/java/net/citizensnpcs/nms/v1_20_R3/util/NMSImpl.java b/v1_20_R3/src/main/java/net/citizensnpcs/nms/v1_20_R3/util/NMSImpl.java index 9a9599003..5f5dd336c 100644 --- a/v1_20_R3/src/main/java/net/citizensnpcs/nms/v1_20_R3/util/NMSImpl.java +++ b/v1_20_R3/src/main/java/net/citizensnpcs/nms/v1_20_R3/util/NMSImpl.java @@ -1260,7 +1260,7 @@ public class NMSImpl implements NMSBridge { } @Override - public void openHorseScreen(Tameable horse, Player equipper) { + public void openHorseInventory(Tameable horse, Player equipper) { LivingEntity handle = getHandle(horse); ServerPlayer equipperHandle = (ServerPlayer) getHandle(equipper); if (handle == null || equipperHandle == null) diff --git a/v1_8_R3/src/main/java/net/citizensnpcs/nms/v1_8_R3/util/NMSImpl.java b/v1_8_R3/src/main/java/net/citizensnpcs/nms/v1_8_R3/util/NMSImpl.java index 9c3409f75..e0641b6ee 100644 --- a/v1_8_R3/src/main/java/net/citizensnpcs/nms/v1_8_R3/util/NMSImpl.java +++ b/v1_8_R3/src/main/java/net/citizensnpcs/nms/v1_8_R3/util/NMSImpl.java @@ -885,7 +885,7 @@ public class NMSImpl implements NMSBridge { } @Override - public void openHorseScreen(Tameable horse, Player equipper) { + public void openHorseInventory(Tameable horse, Player equipper) { EntityLiving handle = NMSImpl.getHandle((LivingEntity) horse); EntityLiving equipperHandle = NMSImpl.getHandle(equipper); if (handle == null || equipperHandle == null) From a8f37635f819898761e950a3f3f708b8b9d52d11 Mon Sep 17 00:00:00 2001 From: fullwall Date: Mon, 19 Feb 2024 05:50:16 +0800 Subject: [PATCH 09/11] Make trader view work, with limitations --- main/src/main/java/net/citizensnpcs/trait/ShopTrait.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java index 5b576ca7b..a4af620b5 100644 --- a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java @@ -918,6 +918,13 @@ public class ShopTrait extends Trait { evt.setCancelled(true); if (evt.getSlotType() != SlotType.RESULT) return; + // TODO: work around crafting slot limitations in minecraft + player.getInventory().addItem(evt.getClickedInventory().getItem(0)); + evt.getClickedInventory().setItem(0, null); + if (evt.getClickedInventory().getItem(1) != null) { + player.getInventory().addItem(evt.getClickedInventory().getItem(1)); + evt.getClickedInventory().setItem(1, null); + } trades.get(selectedTrade).onClick(shop, player, evt.getClick().isShiftClick(), lastClickedTrade == selectedTrade); lastClickedTrade = selectedTrade; From 7f306216dc925646105998a34328b4ffc9fc3a44 Mon Sep 17 00:00:00 2001 From: fullwall Date: Wed, 21 Feb 2024 23:31:17 +0800 Subject: [PATCH 10/11] Fix off by one error in entity equipment updating in 1.8.8 --- .../citizensnpcs/nms/v1_8_R3/entity/EntityHumanNPC.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/v1_8_R3/src/main/java/net/citizensnpcs/nms/v1_8_R3/entity/EntityHumanNPC.java b/v1_8_R3/src/main/java/net/citizensnpcs/nms/v1_8_R3/entity/EntityHumanNPC.java index f26e839dc..a9ae60aa6 100644 --- a/v1_8_R3/src/main/java/net/citizensnpcs/nms/v1_8_R3/entity/EntityHumanNPC.java +++ b/v1_8_R3/src/main/java/net/citizensnpcs/nms/v1_8_R3/entity/EntityHumanNPC.java @@ -378,11 +378,11 @@ public class EntityHumanNPC extends EntityPlayer implements NPCHolder, Skinnable updateEffects = true; boolean itemChanged = false; - for (int slot = 0; slot < this.inventory.armor.length; slot++) { + for (int slot = 0; slot < 5; slot++) { ItemStack equipment = getEquipment(slot); ItemStack cache = equipmentCache.get(slot); if (((cache != null) || (equipment != null)) - && (cache == null ^ equipment == null || !ItemStack.equals(cache, equipment))) { + && (cache == null ^ equipment == null || !ItemStack.matches(cache, equipment))) { itemChanged = true; if (cache != null) { this.getAttributeMap().a(cache.B()); @@ -396,8 +396,8 @@ public class EntityHumanNPC extends EntityPlayer implements NPCHolder, Skinnable if (!itemChanged) return; Location current = getBukkitEntity().getLocation(packetLocationCache); - Packet[] packets = new Packet[this.inventory.armor.length]; - for (int i = 0; i < this.inventory.armor.length; i++) { + Packet[] packets = new Packet[5]; + for (int i = 0; i < 5; i++) { packets[i] = new PacketPlayOutEntityEquipment(getId(), i, getEquipment(i)); } NMSImpl.sendPacketsNearby(getBukkitEntity(), current, packets); From 3c8429aa6d69ae44ff1aeb694b24463c6efa6e23 Mon Sep 17 00:00:00 2001 From: fullwall Date: Sun, 25 Feb 2024 01:07:30 +0800 Subject: [PATCH 11/11] Nullcheck getShopType() --- .../net/citizensnpcs/trait/ShopTrait.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java index a4af620b5..339c25ba9 100644 --- a/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java +++ b/main/src/main/java/net/citizensnpcs/trait/ShopTrait.java @@ -176,7 +176,7 @@ public class ShopTrait extends Trait { } public ShopType getShopType() { - return type; + return type == null ? type = ShopType.DEFAULT : type; } public String getTitle() { @@ -769,16 +769,19 @@ public class ShopTrait extends Trait { @MenuSlot(slot = { 0, 8 }, material = Material.CHEST, amount = 1, title = "Set shop type") public void onSetInventoryType(InventoryMenuSlot slot, CitizensInventoryClickEvent event) { - ctx.getMenu() - .transition(InputMenus.picker("Set shop type", - (Choice choice) -> shop.setShopType(choice.getValue()), - Choice.of(ShopType.DEFAULT, Material.CHEST, "Default (5x9 chest)", - shop.type == ShopType.DEFAULT), - Choice.of(ShopType.CHEST_4X9, Material.CHEST, "4x9 chest", shop.type == ShopType.CHEST_4X9), - Choice.of(ShopType.CHEST_3X9, Material.CHEST, "3x9 chest", shop.type == ShopType.CHEST_3X9), - Choice.of(ShopType.CHEST_2X9, Material.CHEST, "2x9 chest", shop.type == ShopType.CHEST_2X9), - Choice.of(ShopType.CHEST_1X9, Material.CHEST, "1x9 chest", shop.type == ShopType.CHEST_1X9), - Choice.of(ShopType.TRADER, Material.EMERALD, "Trader", shop.type == ShopType.TRADER))); + ctx.getMenu().transition(InputMenus.picker("Set shop type", + (Choice choice) -> shop.setShopType(choice.getValue()), + Choice.of(ShopType.DEFAULT, Material.CHEST, "Default (5x9 chest)", + shop.getShopType() == ShopType.DEFAULT), + Choice.of(ShopType.CHEST_4X9, Material.CHEST, "4x9 chest", + shop.getShopType() == ShopType.CHEST_4X9), + Choice.of(ShopType.CHEST_3X9, Material.CHEST, "3x9 chest", + shop.getShopType() == ShopType.CHEST_3X9), + Choice.of(ShopType.CHEST_2X9, Material.CHEST, "2x9 chest", + shop.getShopType() == ShopType.CHEST_2X9), + Choice.of(ShopType.CHEST_1X9, Material.CHEST, "1x9 chest", + shop.getShopType() == ShopType.CHEST_1X9), + Choice.of(ShopType.TRADER, Material.EMERALD, "Trader", shop.getShopType() == ShopType.TRADER))); } @MenuSlot(slot = { 0, 4 }, material = Material.NAME_TAG, amount = 1) @@ -916,7 +919,7 @@ public class ShopTrait extends Trait { if (!evt.getView().equals(view)) return; evt.setCancelled(true); - if (evt.getSlotType() != SlotType.RESULT) + if (evt.getSlotType() != SlotType.RESULT || !evt.getAction().name().contains("PICKUP")) return; // TODO: work around crafting slot limitations in minecraft player.getInventory().addItem(evt.getClickedInventory().getItem(0));