--- a/net/minecraft/world/entity/npc/WanderingTrader.java
+++ b/net/minecraft/world/entity/npc/WanderingTrader.java
@@ -43,25 +43,37 @@
 import net.minecraft.world.item.trading.MerchantOffers;
 import net.minecraft.world.level.Level;
 import net.minecraft.world.phys.Vec3;
+// CraftBukkit start
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftMerchantRecipe;
+import org.bukkit.entity.AbstractVillager;
+import org.bukkit.event.entity.VillagerAcquireTradeEvent;
+// CraftBukkit end
+
+public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVillager {
 
-public class WanderingTrader extends AbstractVillager {
     private static final int NUMBER_OF_TRADE_OFFERS = 5;
     @Nullable
     public BlockPos wanderTarget;
     private int despawnDelay;
+    // Paper start - Add more WanderingTrader API
+    public boolean canDrinkPotion = true;
+    public boolean canDrinkMilk = true;
+    // Paper end
 
     public WanderingTrader(EntityType<? extends WanderingTrader> type, Level world) {
         super(type, world);
+        //this.setDespawnDelay(48000); // CraftBukkit - set default from MobSpawnerTrader // Paper - move back to MobSpawnerTrader - Vanilla behavior is that only traders spawned by it have this value set.
     }
 
     @Override
     protected void registerGoals() {
         this.goalSelector.addGoal(0, new FloatGoal(this));
-        this.goalSelector.addGoal(0, new UseItemGoal<>(this, PotionUtils.setPotion(new ItemStack(Items.POTION), Potions.INVISIBILITY), SoundEvents.WANDERING_TRADER_DISAPPEARED, (wanderingTrader) -> {
-            return this.level.isNight() && !wanderingTrader.isInvisible();
+        this.goalSelector.addGoal(0, new UseItemGoal<>(this, PotionUtils.setPotion(new ItemStack(Items.POTION), Potions.INVISIBILITY), SoundEvents.WANDERING_TRADER_DISAPPEARED, (entityvillagertrader) -> {
+            return this.canDrinkPotion && this.level.isNight() && !entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API
         }));
-        this.goalSelector.addGoal(0, new UseItemGoal<>(this, new ItemStack(Items.MILK_BUCKET), SoundEvents.WANDERING_TRADER_REAPPEARED, (wanderingTrader) -> {
-            return this.level.isDay() && wanderingTrader.isInvisible();
+        this.goalSelector.addGoal(0, new UseItemGoal<>(this, new ItemStack(Items.MILK_BUCKET), SoundEvents.WANDERING_TRADER_REAPPEARED, (entityvillagertrader) -> {
+            return canDrinkMilk && this.level.isDay() && entityvillagertrader.isInvisible(); // Paper - Add more WanderingTrader API
         }));
         this.goalSelector.addGoal(1, new TradeWithPlayerGoal(this));
         this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Zombie.class, 8.0F, 0.5D, 0.5D));
@@ -93,8 +105,9 @@
 
     @Override
     public InteractionResult mobInteract(Player player, InteractionHand hand) {
-        ItemStack itemStack = player.getItemInHand(hand);
-        if (!itemStack.is(Items.VILLAGER_SPAWN_EGG) && this.isAlive() && !this.isTrading() && !this.isBaby()) {
+        ItemStack itemstack = player.getItemInHand(hand);
+
+        if (!itemstack.is(Items.VILLAGER_SPAWN_EGG) && this.isAlive() && !this.isTrading() && !this.isBaby()) {
             if (hand == InteractionHand.MAIN_HAND) {
                 player.awardStat(Stats.TALKED_TO_VILLAGER);
             }
@@ -116,16 +129,28 @@
 
     @Override
     protected void updateTrades() {
-        VillagerTrades.ItemListing[] itemListings = VillagerTrades.WANDERING_TRADER_TRADES.get(1);
-        VillagerTrades.ItemListing[] itemListings2 = VillagerTrades.WANDERING_TRADER_TRADES.get(2);
-        if (itemListings != null && itemListings2 != null) {
-            MerchantOffers merchantOffers = this.getOffers();
-            this.addOffersFromItemListings(merchantOffers, itemListings, 5);
-            int i = this.random.nextInt(itemListings2.length);
-            VillagerTrades.ItemListing itemListing = itemListings2[i];
-            MerchantOffer merchantOffer = itemListing.getOffer(this, this.random);
-            if (merchantOffer != null) {
-                merchantOffers.add(merchantOffer);
+        VillagerTrades.ItemListing[] avillagertrades_imerchantrecipeoption = (VillagerTrades.ItemListing[]) VillagerTrades.WANDERING_TRADER_TRADES.get(1);
+        VillagerTrades.ItemListing[] avillagertrades_imerchantrecipeoption1 = (VillagerTrades.ItemListing[]) VillagerTrades.WANDERING_TRADER_TRADES.get(2);
+
+        if (avillagertrades_imerchantrecipeoption != null && avillagertrades_imerchantrecipeoption1 != null) {
+            MerchantOffers merchantrecipelist = this.getOffers();
+
+            this.addOffersFromItemListings(merchantrecipelist, avillagertrades_imerchantrecipeoption, 5);
+            int i = this.random.nextInt(avillagertrades_imerchantrecipeoption1.length);
+            VillagerTrades.ItemListing villagertrades_imerchantrecipeoption = avillagertrades_imerchantrecipeoption1[i];
+            MerchantOffer merchantrecipe = villagertrades_imerchantrecipeoption.getOffer(this, this.random);
+
+            if (merchantrecipe != null) {
+                // CraftBukkit start
+                VillagerAcquireTradeEvent event = new VillagerAcquireTradeEvent((AbstractVillager) getBukkitEntity(), merchantrecipe.asBukkit());
+                // Suppress during worldgen
+                if (this.valid) {
+                    Bukkit.getPluginManager().callEvent(event);
+                }
+                if (!event.isCancelled()) {
+                    merchantrecipelist.add(CraftMerchantRecipe.fromBukkit(event.getRecipe()).toMinecraft());
+                }
+                // CraftBukkit end
             }
 
         }
@@ -164,7 +189,8 @@
     protected void rewardTradeXp(MerchantOffer offer) {
         if (offer.shouldRewardExp()) {
             int i = 3 + this.random.nextInt(4);
-            this.level.addFreshEntity(new ExperienceOrb(this.level, this.getX(), this.getY() + 0.5D, this.getZ(), i));
+
+            this.level.addFreshEntity(new ExperienceOrb(this.level, this.getX(), this.getY() + 0.5D, this.getZ(), i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper
         }
 
     }
@@ -232,40 +258,44 @@
         return this.wanderTarget;
     }
 
-    class WanderToPositionGoal extends Goal {
+    private class WanderToPositionGoal extends Goal {
+
         final WanderingTrader trader;
         final double stopDistance;
         final double speedModifier;
 
-        WanderToPositionGoal(WanderingTrader trader, double proximityDistance, double speed) {
-            this.trader = trader;
-            this.stopDistance = proximityDistance;
-            this.speedModifier = speed;
+        WanderToPositionGoal(WanderingTrader entityvillagertrader, double d0, double d1) {
+            this.trader = entityvillagertrader;
+            this.stopDistance = d0;
+            this.speedModifier = d1;
             this.setFlags(EnumSet.of(Goal.Flag.MOVE));
         }
 
         @Override
         public void stop() {
-            this.trader.setWanderTarget((BlockPos)null);
+            this.trader.setWanderTarget((BlockPos) null);
             WanderingTrader.this.navigation.stop();
         }
 
         @Override
         public boolean canUse() {
-            BlockPos blockPos = this.trader.getWanderTarget();
-            return blockPos != null && this.isTooFarAway(blockPos, this.stopDistance);
+            BlockPos blockposition = this.trader.getWanderTarget();
+
+            return blockposition != null && this.isTooFarAway(blockposition, this.stopDistance);
         }
 
         @Override
         public void tick() {
-            BlockPos blockPos = this.trader.getWanderTarget();
-            if (blockPos != null && WanderingTrader.this.navigation.isDone()) {
-                if (this.isTooFarAway(blockPos, 10.0D)) {
-                    Vec3 vec3 = (new Vec3((double)blockPos.getX() - this.trader.getX(), (double)blockPos.getY() - this.trader.getY(), (double)blockPos.getZ() - this.trader.getZ())).normalize();
-                    Vec3 vec32 = vec3.scale(10.0D).add(this.trader.getX(), this.trader.getY(), this.trader.getZ());
-                    WanderingTrader.this.navigation.moveTo(vec32.x, vec32.y, vec32.z, this.speedModifier);
+            BlockPos blockposition = this.trader.getWanderTarget();
+
+            if (blockposition != null && WanderingTrader.this.navigation.isDone()) {
+                if (this.isTooFarAway(blockposition, 10.0D)) {
+                    Vec3 vec3d = (new Vec3((double) blockposition.getX() - this.trader.getX(), (double) blockposition.getY() - this.trader.getY(), (double) blockposition.getZ() - this.trader.getZ())).normalize();
+                    Vec3 vec3d1 = vec3d.scale(10.0D).add(this.trader.getX(), this.trader.getY(), this.trader.getZ());
+
+                    WanderingTrader.this.navigation.moveTo(vec3d1.x, vec3d1.y, vec3d1.z, this.speedModifier);
                 } else {
-                    WanderingTrader.this.navigation.moveTo((double)blockPos.getX(), (double)blockPos.getY(), (double)blockPos.getZ(), this.speedModifier);
+                    WanderingTrader.this.navigation.moveTo((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), this.speedModifier);
                 }
             }
 
