From edfdcfd0dbc6e016a596e5064f04a84bb71a8851 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Thu, 23 Dec 2021 21:15:37 -0800 Subject: [PATCH] Improve Piglin Barter API --- .../api/0416-Improve-Piglin-Barter-API.patch | 108 ++++++++ .../0973-Improve-Piglin-Barter-API.patch | 261 ++++++++++++++++++ 2 files changed, 369 insertions(+) create mode 100644 patches/api/0416-Improve-Piglin-Barter-API.patch create mode 100644 patches/server/0973-Improve-Piglin-Barter-API.patch diff --git a/patches/api/0416-Improve-Piglin-Barter-API.patch b/patches/api/0416-Improve-Piglin-Barter-API.patch new file mode 100644 index 0000000000..df338c7e47 --- /dev/null +++ b/patches/api/0416-Improve-Piglin-Barter-API.patch @@ -0,0 +1,108 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Thu, 27 May 2021 18:14:55 -0700 +Subject: [PATCH] Improve Piglin Barter API + + +diff --git a/src/main/java/org/bukkit/entity/Piglin.java b/src/main/java/org/bukkit/entity/Piglin.java +index d4cb4b0ed1d9766a87867dcf1a3a839526ba9332..74a6322521e28f7d03407643f6567cabd570cc81 100644 +--- a/src/main/java/org/bukkit/entity/Piglin.java ++++ b/src/main/java/org/bukkit/entity/Piglin.java +@@ -26,6 +26,9 @@ public interface Piglin extends PiglinAbstract, InventoryHolder, com.destroystok + + /** + * Adds a material to the allowed list of materials to barter with. ++ *
++ * All materials returned by {@link #getBarterList()} are also considered ++ * items "Materials of Interest" that the piglins will seek out. + * + * @param material The material to add + * +@@ -35,10 +38,9 @@ public interface Piglin extends PiglinAbstract, InventoryHolder, com.destroystok + + /** + * Removes a material from the allowed list of materials to barter with. +- * +- * Note: It's not possible to override the default +- * bartering item gold_ingots as payment. To block gold_ingots see +- * {@link org.bukkit.event.entity.PiglinBarterEvent}. ++ *
++ * Note: By default, piglins already barter with {@link Material#GOLD_INGOT} ++ * so you must remove that if you want to clear the barter materials. + * + * @param material The material to remove + * +@@ -57,9 +59,9 @@ public interface Piglin extends PiglinAbstract, InventoryHolder, com.destroystok + + /** + * Removes a material from the list of materials the piglin will pickup. +- * +- * Note: It's not possible to override the default list of +- * item the piglin will pickup. To cancel pickup see ++ *
++ * Note: By default, piglins already are interested in {@link org.bukkit.Tag#ITEMS_PIGLIN_LOVED} items ++ * and this cannot override that. To cancel pickup see + * {@link org.bukkit.event.entity.EntityPickupItemEvent}. + * + * @param material The material you want removed from the interest list +@@ -71,7 +73,7 @@ public interface Piglin extends PiglinAbstract, InventoryHolder, com.destroystok + * Returns a immutable set of materials the piglins will pickup. + *
+ * Note: This set will not include the items that are set +- * by default. To interact with those items see ++ * by default ({@link org.bukkit.Tag#ITEMS_PIGLIN_LOVED}). To interact with those items see + * {@link org.bukkit.event.entity.EntityPickupItemEvent}. + * + * @return An immutable materials set +@@ -81,10 +83,9 @@ public interface Piglin extends PiglinAbstract, InventoryHolder, com.destroystok + + /** + * Returns a immutable set of materials the piglins will barter with. +- * +- * Note: This set will not include the items that are set +- * by default. To interact with those items see +- * {@link org.bukkit.event.entity.PiglinBarterEvent}. ++ *
++ * Note: This set will include the items that are set ++ * by default. + * + * @return An immutable materials set + */ +diff --git a/src/main/java/org/bukkit/event/entity/PiglinBarterEvent.java b/src/main/java/org/bukkit/event/entity/PiglinBarterEvent.java +index bd67b7cba78b9bbdd82a5a40048e658a979e3108..3e3254382b6e9fd09a767bdf7177d4a1731ce5b4 100644 +--- a/src/main/java/org/bukkit/event/entity/PiglinBarterEvent.java ++++ b/src/main/java/org/bukkit/event/entity/PiglinBarterEvent.java +@@ -18,12 +18,33 @@ public class PiglinBarterEvent extends EntityEvent implements Cancellable { + private boolean cancelled; + private final List outcome; + private final ItemStack input; ++ private final java.util.UUID throwerUUID; // Paper + ++ @Deprecated // Paper + public PiglinBarterEvent(@NotNull Piglin what, @NotNull ItemStack input, @NotNull List outcome) { ++ // Paper start ++ this(what, input, outcome, null); ++ } ++ ++ /** ++ * Gets the UUID of the thrower of the ItemStack that caused this barter ++ * event. This could be a {@link org.bukkit.entity.Player} or an {@link org.bukkit.entity.Entity} ++ * or whatever some plugin set the thrower to via {@link org.bukkit.entity.Item#setThrower(java.util.UUID)}. ++ * ++ * @return the thrower UUID or null if none ++ */ ++ @org.jetbrains.annotations.Nullable ++ public java.util.UUID getThrowerUUID() { ++ return this.throwerUUID; ++ } ++ ++ public PiglinBarterEvent(@NotNull Piglin what, @NotNull ItemStack input, @NotNull List outcome, @org.jetbrains.annotations.Nullable java.util.UUID throwerUUID) { ++ // Paper end + super(what); + + this.input = input; + this.outcome = outcome; ++ this.throwerUUID = throwerUUID; // Paper + } + + @NotNull diff --git a/patches/server/0973-Improve-Piglin-Barter-API.patch b/patches/server/0973-Improve-Piglin-Barter-API.patch new file mode 100644 index 0000000000..ec6e580355 --- /dev/null +++ b/patches/server/0973-Improve-Piglin-Barter-API.patch @@ -0,0 +1,261 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jake Potrebic +Date: Thu, 23 Dec 2021 21:12:58 -0800 +Subject: [PATCH] Improve Piglin Barter API + + +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/SetEntityLookTarget.java b/src/main/java/net/minecraft/world/entity/ai/behavior/SetEntityLookTarget.java +index 8139d81f0b7bf8dcc6cc55bfc5e017c905c41b4f..5a6bda82f2e49d2ce454faa7094c79abe3025d57 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/SetEntityLookTarget.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/SetEntityLookTarget.java +@@ -29,11 +29,17 @@ public class SetEntityLookTarget { + } + + public static OneShot create(Predicate predicate, float maxDistance) { ++ // Paper start ++ return create((target, entity) -> predicate.test(target), maxDistance); ++ } ++ ++ public static OneShot create(java.util.function.BiPredicate biPredicate, float maxDistance) { ++ // Paper end + float f = maxDistance * maxDistance; + return BehaviorBuilder.create((context) -> { + return context.group(context.absent(MemoryModuleType.LOOK_TARGET), context.present(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES)).apply(context, (lookTarget, visibleMobs) -> { + return (world, entity, time) -> { +- Optional optional = context.get(visibleMobs).findClosest(predicate.and((target) -> { ++ Optional optional = context.get(visibleMobs).findClosest(((Predicate) (target -> biPredicate.test(target, entity))).and((target) -> { // Paper + return target.distanceToSqr(entity) <= (double)f && !entity.hasPassenger(target); + })); + if (optional.isEmpty()) { +diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/PiglinSpecificSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/PiglinSpecificSensor.java +index 14288ff577e0020c3751751220be1ff28169035a..d18c1045e116bc8c3a7c5f931ab8a88ee15a9296 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/sensing/PiglinSpecificSensor.java ++++ b/src/main/java/net/minecraft/world/entity/ai/sensing/PiglinSpecificSensor.java +@@ -73,7 +73,10 @@ public class PiglinSpecificSensor extends Sensor { + optional6 = Optional.of(player); + } + +- if (optional7.isEmpty() && !player.isSpectator() && PiglinAi.isPlayerHoldingLovedItem(player)) { ++ // Paper start ++ boolean isHoldingLovedItem = entity instanceof Piglin piglin && PiglinAi.isPlayerHoldingPiglinLovedItem(player, piglin); // Paper - should always be piglin ++ if (optional7.isEmpty() && !player.isSpectator() && isHoldingLovedItem) { ++ // Paper end + optional7 = Optional.of(player); + } + } else if (!optional.isEmpty() || !(livingEntity instanceof WitherSkeleton) && !(livingEntity instanceof WitherBoss)) { +diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +index afa7ecfa8453da510ec5ccecb1ceeb1d9893d259..54e7cab9cda5cfbdec118451e006089b1f9d23fd 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -88,9 +88,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + protected static final ImmutableList>> SENSOR_TYPES = ImmutableList.of(SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.NEAREST_ITEMS, SensorType.HURT_BY, SensorType.PIGLIN_SPECIFIC_SENSOR); + protected static final ImmutableList> MEMORY_TYPES = ImmutableList.of(MemoryModuleType.LOOK_TARGET, MemoryModuleType.DOORS_TO_CLOSE, MemoryModuleType.NEAREST_LIVING_ENTITIES, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, MemoryModuleType.NEAREST_VISIBLE_PLAYER, MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, MemoryModuleType.NEAREST_VISIBLE_ADULT_PIGLINS, MemoryModuleType.NEARBY_ADULT_PIGLINS, MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, MemoryModuleType.ITEM_PICKUP_COOLDOWN_TICKS, MemoryModuleType.HURT_BY, MemoryModuleType.HURT_BY_ENTITY, new MemoryModuleType[]{MemoryModuleType.WALK_TARGET, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.ATTACK_TARGET, MemoryModuleType.ATTACK_COOLING_DOWN, MemoryModuleType.INTERACTION_TARGET, MemoryModuleType.PATH, MemoryModuleType.ANGRY_AT, MemoryModuleType.UNIVERSAL_ANGER, MemoryModuleType.AVOID_TARGET, MemoryModuleType.ADMIRING_ITEM, MemoryModuleType.TIME_TRYING_TO_REACH_ADMIRE_ITEM, MemoryModuleType.ADMIRING_DISABLED, MemoryModuleType.DISABLE_WALK_TO_ADMIRE_ITEM, MemoryModuleType.CELEBRATE_LOCATION, MemoryModuleType.DANCING, MemoryModuleType.HUNTED_RECENTLY, MemoryModuleType.NEAREST_VISIBLE_BABY_HOGLIN, MemoryModuleType.NEAREST_VISIBLE_NEMESIS, MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED, MemoryModuleType.RIDE_TARGET, MemoryModuleType.VISIBLE_ADULT_PIGLIN_COUNT, MemoryModuleType.VISIBLE_ADULT_HOGLIN_COUNT, MemoryModuleType.NEAREST_VISIBLE_HUNTABLE_HOGLIN, MemoryModuleType.NEAREST_TARGETABLE_PLAYER_NOT_WEARING_GOLD, MemoryModuleType.NEAREST_PLAYER_HOLDING_WANTED_ITEM, MemoryModuleType.ATE_RECENTLY, MemoryModuleType.NEAREST_REPELLENT}); + // CraftBukkit start - Custom bartering and interest list +- public Set allowedBarterItems = new HashSet<>(); ++ public Set allowedBarterItems = new HashSet<>(Set.of(PiglinAi.BARTERING_ITEM)); // Paper + public Set interestItems = new HashSet<>(); + // CraftBukkit end ++ public @Nullable UUID itemThrower = null; // Paper ++ private static final String PAPER_ITEM_THROWER_NBT_KEY = "Paper.ItemThrower"; + + public Piglin(EntityType type, Level world) { + super(type, world); +@@ -110,13 +112,20 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + + this.writeInventoryToTag(nbt); + // CraftBukkit start ++ if (this.allowedBarterItems.size() != 1 || this.allowedBarterItems.iterator().next() != PiglinAi.BARTERING_ITEM) { // Paper - only save list if its not the default + ListTag barterList = new ListTag(); + this.allowedBarterItems.stream().map(BuiltInRegistries.ITEM::getKey).map(ResourceLocation::toString).map(StringTag::valueOf).forEach(barterList::add); + nbt.put("Bukkit.BarterList", barterList); ++ } // Paper + ListTag interestList = new ListTag(); + this.interestItems.stream().map(BuiltInRegistries.ITEM::getKey).map(ResourceLocation::toString).map(StringTag::valueOf).forEach(interestList::add); + nbt.put("Bukkit.InterestList", interestList); + // CraftBukkit end ++ // Paper start ++ if (this.itemThrower != null) { ++ nbt.putUUID(PAPER_ITEM_THROWER_NBT_KEY, this.itemThrower); ++ } ++ // Paper end + } + + @Override +@@ -126,9 +135,20 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + this.setCannotHunt(nbt.getBoolean("CannotHunt")); + this.readInventoryFromTag(nbt); + // CraftBukkit start ++ if (nbt.contains("Bukkit.BarterList", net.minecraft.nbt.Tag.TAG_LIST)) { // Paper + this.allowedBarterItems = nbt.getList("Bukkit.BarterList", 8).stream().map(Tag::getAsString).map(ResourceLocation::tryParse).map(BuiltInRegistries.ITEM::get).collect(Collectors.toCollection(HashSet::new)); ++ // Paper start ++ } else { ++ this.allowedBarterItems = new HashSet<>(Set.of(PiglinAi.BARTERING_ITEM)); ++ } ++ // Paper end + this.interestItems = nbt.getList("Bukkit.InterestList", 8).stream().map(Tag::getAsString).map(ResourceLocation::tryParse).map(BuiltInRegistries.ITEM::get).collect(Collectors.toCollection(HashSet::new)); + // CraftBukkit end ++ // Paper start ++ if (nbt.hasUUID(PAPER_ITEM_THROWER_NBT_KEY)) { ++ this.itemThrower = nbt.getUUID(PAPER_ITEM_THROWER_NBT_KEY); ++ } ++ // Paper end + } + + @VisibleForDebug +@@ -351,7 +371,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + + @Override + public PiglinArmPose getArmPose() { +- return this.isDancing() ? PiglinArmPose.DANCING : (PiglinAi.isLovedItem(this.getOffhandItem()) ? PiglinArmPose.ADMIRING_ITEM : (this.isAggressive() && this.isHoldingMeleeWeapon() ? PiglinArmPose.ATTACKING_WITH_MELEE_WEAPON : (this.isChargingCrossbow() ? PiglinArmPose.CROSSBOW_CHARGE : (this.isAggressive() && this.isHolding(Items.CROSSBOW) ? PiglinArmPose.CROSSBOW_HOLD : PiglinArmPose.DEFAULT)))); ++ return this.isDancing() ? PiglinArmPose.DANCING : (PiglinAi.isLovedItem(this.getOffhandItem(), this) ? PiglinArmPose.ADMIRING_ITEM : (this.isAggressive() && this.isHoldingMeleeWeapon() ? PiglinArmPose.ATTACKING_WITH_MELEE_WEAPON : (this.isChargingCrossbow() ? PiglinArmPose.CROSSBOW_CHARGE : (this.isAggressive() && this.isHolding(Items.CROSSBOW) ? PiglinArmPose.CROSSBOW_HOLD : PiglinArmPose.DEFAULT)))); // Paper + } + + public boolean isDancing() { +@@ -397,7 +417,7 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + } + + protected void holdInOffHand(ItemStack stack) { +- if (stack.is(PiglinAi.BARTERING_ITEM) || this.allowedBarterItems.contains(stack.getItem())) { // CraftBukkit - Changes to accept custom payment items ++ if (this.allowedBarterItems.contains(stack.getItem())) { // CraftBukkit - Changes to accept custom payment items // Paper + this.setItemSlot(EquipmentSlot.OFFHAND, stack); + this.setGuaranteedDrop(EquipmentSlot.OFFHAND); + } else { +@@ -406,6 +426,16 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + + } + ++ // Paper start - clear item thrower if offhand stack is set ++ @Override ++ public void setItemSlot(EquipmentSlot slot, ItemStack stack) { ++ if (slot == EquipmentSlot.OFFHAND && !ItemStack.matches(stack, this.getItemBySlot(EquipmentSlot.OFFHAND))) { ++ this.itemThrower = null; ++ } ++ super.setItemSlot(slot, stack); ++ } ++ // Paper end ++ + @Override + public boolean wantsToPickUp(ItemStack stack) { + return this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && this.canPickUpLoot() && PiglinAi.wantsToPickup(this, stack); +diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java +index 2e107f10d497a5696c7bc90ae3c5ecea98edb3e1..f98745a44ac7f140465fd883e447bc588502b02d 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java ++++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinAi.java +@@ -146,7 +146,7 @@ public class PiglinAi { + } + + private static void initIdleActivity(Brain piglin) { +- piglin.addActivity(Activity.IDLE, 10, ImmutableList.of(SetEntityLookTarget.create(PiglinAi::isPlayerHoldingLovedItem, 14.0F), StartAttacking.create(AbstractPiglin::isAdult, PiglinAi::findNearestValidAttackTarget), BehaviorBuilder.triggerIf(Piglin::canHunt, StartHuntingHoglin.create()), PiglinAi.avoidRepellent(), PiglinAi.babySometimesRideBabyHoglin(), PiglinAi.createIdleLookBehaviors(), PiglinAi.createIdleMovementBehaviors(), SetLookAndInteract.create(EntityType.PLAYER, 4))); ++ piglin.addActivity(Activity.IDLE, 10, ImmutableList.of(SetEntityLookTarget.create(PiglinAi::isPlayerHoldingPiglinLovedItem, 14.0F), StartAttacking.create(AbstractPiglin::isAdult, PiglinAi::findNearestValidAttackTarget), BehaviorBuilder.triggerIf(Piglin::canHunt, StartHuntingHoglin.create()), PiglinAi.avoidRepellent(), PiglinAi.babySometimesRideBabyHoglin(), PiglinAi.createIdleLookBehaviors(), PiglinAi.createIdleMovementBehaviors(), SetLookAndInteract.create(EntityType.PLAYER, 4))); // Paper + } + + private static void initFightActivity(Piglin piglin, Brain brain) { +@@ -156,7 +156,7 @@ public class PiglinAi { + } + + private static void initCelebrateActivity(Brain brain) { +- brain.addActivityAndRemoveMemoryWhenStopped(Activity.CELEBRATE, 10, ImmutableList.of(PiglinAi.avoidRepellent(), SetEntityLookTarget.create(PiglinAi::isPlayerHoldingLovedItem, 14.0F), StartAttacking.create(AbstractPiglin::isAdult, PiglinAi::findNearestValidAttackTarget), BehaviorBuilder.triggerIf((entitypiglin) -> { ++ brain.addActivityAndRemoveMemoryWhenStopped(Activity.CELEBRATE, 10, ImmutableList.of(PiglinAi.avoidRepellent(), SetEntityLookTarget.create(PiglinAi::isPlayerHoldingPiglinLovedItem, 14.0F), StartAttacking.create(AbstractPiglin::isAdult, PiglinAi::findNearestValidAttackTarget), BehaviorBuilder.triggerIf((entitypiglin) -> { // Paper + return !entitypiglin.isDancing(); + }, GoToTargetLocation.create(MemoryModuleType.CELEBRATE_LOCATION, 2, 1.0F)), BehaviorBuilder.triggerIf(Piglin::isDancing, GoToTargetLocation.create(MemoryModuleType.CELEBRATE_LOCATION, 4, 0.6F)), new RunOne<>(ImmutableList.of(Pair.of(SetEntityLookTarget.create(EntityType.PIGLIN, 8.0F), 1), Pair.of(RandomStroll.stroll(0.6F, 2, 1), 1), Pair.of(new DoNothing(10, 20), 1)))), MemoryModuleType.CELEBRATE_LOCATION); + } +@@ -171,7 +171,7 @@ public class PiglinAi { + + private static void initRideHoglinActivity(Brain brain) { + // CraftBukkit - decompile error +- brain.addActivityAndRemoveMemoryWhenStopped(Activity.RIDE, 10, ImmutableList.of(Mount.create(0.8F), SetEntityLookTarget.create(PiglinAi::isPlayerHoldingLovedItem, 8.0F), BehaviorBuilder.sequence(BehaviorBuilder.triggerIf(Entity::isPassenger), TriggerGate.triggerOneShuffled(ImmutableList., Integer>>builder().addAll(PiglinAi.createLookBehaviors()).add(Pair.of(BehaviorBuilder.triggerIf((entitypiglin) -> { ++ brain.addActivityAndRemoveMemoryWhenStopped(Activity.RIDE, 10, ImmutableList.of(Mount.create(0.8F), SetEntityLookTarget.create(PiglinAi::isPlayerHoldingPiglinLovedItem, 8.0F), BehaviorBuilder.sequence(BehaviorBuilder.triggerIf(Entity::isPassenger), TriggerGate.triggerOneShuffled(ImmutableList., Integer>>builder().addAll(PiglinAi.createLookBehaviors()).add(Pair.of(BehaviorBuilder.triggerIf((entitypiglin) -> { // Paper + return true; + }), 1)).build())), DismountOrSkipMounting.create(8, PiglinAi::wantsToStopRiding)), MemoryModuleType.RIDE_TARGET); + } +@@ -261,6 +261,11 @@ public class PiglinAi { + piglin.getBrain().eraseMemory(MemoryModuleType.TIME_TRYING_TO_REACH_ADMIRE_ITEM); + PiglinAi.holdInOffhand(piglin, itemstack); + PiglinAi.admireGoldItem(piglin); ++ // Paper start - set thrower after setting the item in offhand ++ if (PiglinAi.isBarterCurrency(itemstack, piglin)) { ++ piglin.itemThrower = drop.thrower; ++ } ++ // Paper end + } else if (PiglinAi.isFood(itemstack) && !PiglinAi.hasEatenRecently(piglin)) { + PiglinAi.eat(piglin); + } else { +@@ -298,6 +303,7 @@ public class PiglinAi { + protected static void stopHoldingOffHandItem(Piglin piglin, boolean barter) { + ItemStack itemstack = piglin.getItemInHand(InteractionHand.OFF_HAND); + ++ java.util.UUID itemThrower = piglin.itemThrower; // Paper + piglin.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY); + boolean flag1; + +@@ -305,7 +311,7 @@ public class PiglinAi { + flag1 = PiglinAi.isBarterCurrency(itemstack, piglin); // CraftBukkit - Changes to allow custom payment for bartering + if (barter && flag1) { + // CraftBukkit start +- PiglinBarterEvent event = CraftEventFactory.callPiglinBarterEvent(piglin, PiglinAi.getBarterResponseItems(piglin), itemstack); ++ PiglinBarterEvent event = CraftEventFactory.callPiglinBarterEvent(piglin, PiglinAi.getBarterResponseItems(piglin), itemstack, itemThrower); // Paper + if (!event.isCancelled()) { + PiglinAi.throwItems(piglin, event.getOutcome().stream().map(CraftItemStack::asNMSCopy).collect(Collectors.toList())); + } +@@ -504,6 +510,7 @@ public class PiglinAi { + + PiglinAi.holdInOffhand(piglin, itemstack1); + PiglinAi.admireGoldItem(piglin); ++ piglin.itemThrower = player.getUUID(); // Paper + PiglinAi.stopWalking(piglin); + return InteractionResult.CONSUME; + } else { +@@ -771,7 +778,7 @@ public class PiglinAi { + + // CraftBukkit start - Changes to allow custom payment for bartering + private static boolean isBarterCurrency(ItemStack itemstack, Piglin piglin) { +- return PiglinAi.isBarterCurrency(itemstack) || piglin.allowedBarterItems.contains(itemstack.getItem()); ++ return piglin.allowedBarterItems.contains(itemstack.getItem()); // Paper - don't check default item, is included in the set by default + } + // CraftBukkit end + +@@ -795,6 +802,11 @@ public class PiglinAi { + return !PiglinAi.seesPlayerHoldingLovedItem(piglin); + } + ++ // Paper start ++ public static boolean isPlayerHoldingPiglinLovedItem(LivingEntity target, Piglin piglin) { ++ return target.getType() == EntityType.PLAYER && target.isHolding(item -> PiglinAi.isLovedItem(item, piglin)); ++ } ++ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper end + public static boolean isPlayerHoldingLovedItem(LivingEntity target) { + return target.getType() == EntityType.PLAYER && target.isHolding(PiglinAi::isLovedItem); + } +diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/StartAdmiringItemIfSeen.java b/src/main/java/net/minecraft/world/entity/monster/piglin/StartAdmiringItemIfSeen.java +index f0536a7c3a77e17434894f35dc4f17e99fb1d9d0..3c0ab2861521b2045cf8cee4bd813b7a122b41ee 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/piglin/StartAdmiringItemIfSeen.java ++++ b/src/main/java/net/minecraft/world/entity/monster/piglin/StartAdmiringItemIfSeen.java +@@ -12,7 +12,7 @@ public class StartAdmiringItemIfSeen { + return context.group(context.present(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM), context.absent(MemoryModuleType.ADMIRING_ITEM), context.absent(MemoryModuleType.ADMIRING_DISABLED), context.absent(MemoryModuleType.DISABLE_WALK_TO_ADMIRE_ITEM)).apply(context, (nearestVisibleWantedItem, admiringItem, admiringDisabled, disableWalkToAdmireItem) -> { + return (world, entity, time) -> { + ItemEntity itemEntity = context.get(nearestVisibleWantedItem); +- if (!PiglinAi.isLovedItem(itemEntity.getItem())) { ++ if ((entity instanceof Piglin piglin && !PiglinAi.isLovedItem(itemEntity.getItem(), piglin)) || (!(entity instanceof Piglin) && !PiglinAi.isLovedItem(itemEntity.getItem()))) { // Paper - get loved items from piglin entity + return false; + } else { + admiringItem.setWithExpiry(true, (long)duration); +diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +index 819c9c020f4d5a1373f68850134960d24b8fc308..9929375b1d48305cc217e56bb2ea414d85ae5178 100644 +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -1932,8 +1932,14 @@ public class CraftEventFactory { + return event; + } + ++ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper + public static PiglinBarterEvent callPiglinBarterEvent(net.minecraft.world.entity.monster.piglin.Piglin piglin, List outcome, ItemStack input) { +- PiglinBarterEvent event = new PiglinBarterEvent((Piglin) piglin.getBukkitEntity(), CraftItemStack.asBukkitCopy(input), outcome.stream().map(CraftItemStack::asBukkitCopy).collect(Collectors.toList())); ++ // Paper start ++ return callPiglinBarterEvent(piglin, outcome, input, null); ++ } ++ public static PiglinBarterEvent callPiglinBarterEvent(net.minecraft.world.entity.monster.piglin.Piglin piglin, List outcome, ItemStack input, @Nullable java.util.UUID throwerUUID) { ++ PiglinBarterEvent event = new PiglinBarterEvent((Piglin) piglin.getBukkitEntity(), CraftItemStack.asBukkitCopy(input), outcome.stream().map(CraftItemStack::asBukkitCopy).collect(Collectors.toList()), throwerUUID); ++ // Paper end + Bukkit.getPluginManager().callEvent(event); + return event; + }