diff --git a/main/src/main/java/net/citizensnpcs/Citizens.java b/main/src/main/java/net/citizensnpcs/Citizens.java index 14b23cd72..df3d46f8c 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; @@ -612,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/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 97781235d..d8946cdc7 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,15 +269,15 @@ 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),; - 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) { @@ -348,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); @@ -366,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 5eee58838..59532567f 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] --(head|body|leftarm|leftleg|rightarm|rightleg)pose [angle x,y,z]", desc = "编辑盔甲架属性", 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); } @@ -367,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"))); } @@ -455,7 +461,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 = "控制单击NPC时将运行的命令", help = Messages.NPC_COMMAND_HELP, modifiers = { "command", "cmd" }, @@ -469,7 +475,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 +527,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 +2089,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 = "开始寻路到某个位置", 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 +2112,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/CitizensNPC.java b/main/src/main/java/net/citizensnpcs/npc/CitizensNPC.java index cefb23394..29343e64f 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); @@ -301,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)); } @@ -326,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; @@ -366,7 +381,6 @@ public class CitizensNPC extends AbstractNPC { return; } navigator.onSpawn(); - for (Trait trait : Iterables.toArray(traits.values(), Trait.class)) { try { trait.onSpawn(); @@ -375,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); @@ -532,9 +553,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(); @@ -615,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/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..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")); } @@ -334,12 +337,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 +357,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/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. 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..339c25ba9 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; @@ -92,7 +101,6 @@ public class ShopTrait extends Trait { @Override public void onRemove() { - Messaging.debug("Removing", npc, "default shop due to onRemove"); shops.deleteShop(getDefaultShop()); } @@ -114,7 +122,7 @@ public class ShopTrait extends Trait { @Persist private String title; @Persist - private ShopType type = ShopType.COMMAND; + private ShopType type = ShopType.DEFAULT; @Persist private String viewPermission; @@ -141,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) { @@ -163,6 +175,14 @@ public class ShopTrait extends Trait { return viewPermission; } + public ShopType getShopType() { + return type == null ? type = ShopType.DEFAULT : 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) { @@ -180,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 }) @@ -236,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()) { @@ -251,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 -> { @@ -274,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; @@ -398,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); } @@ -423,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 @@ -449,8 +482,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(); @@ -712,42 +748,48 @@ 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.getTitle()); 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, 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.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) 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, 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 +800,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)); } } @@ -793,15 +835,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); @@ -809,7 +852,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); @@ -819,8 +863,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 @@ -830,10 +874,104 @@ 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 || !evt.getAction().name().contains("PICKUP")) + 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; + } + + @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 { - BUY, - COMMAND, - SELL; + 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 { 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(); 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/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/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/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}]].", 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_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/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_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/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_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/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_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/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_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/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_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/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_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/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_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/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_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/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_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/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 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/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); 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)