--- a/net/minecraft/world/entity/npc/WanderingTrader.java
+++ b/net/minecraft/world/entity/npc/WanderingTrader.java
@@ -47,11 +_,23 @@
 import net.minecraft.world.phys.Vec3;
 import org.apache.commons.lang3.tuple.Pair;
 
-public class WanderingTrader extends AbstractVillager implements Consumable.OverrideConsumeSound {
+// CraftBukkit start
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.inventory.CraftMerchantRecipe;
+import org.bukkit.entity.AbstractVillager;
+import org.bukkit.event.entity.EntityRemoveEvent;
+import org.bukkit.event.entity.VillagerAcquireTradeEvent;
+// CraftBukkit end
+
+public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVillager implements Consumable.OverrideConsumeSound {
     private static final int NUMBER_OF_TRADE_OFFERS = 5;
     @Nullable
     private BlockPos wanderTarget;
     private int despawnDelay;
+    // Paper start - Add more WanderingTrader API
+    public boolean canDrinkPotion = true;
+    public boolean canDrinkMilk = true;
+    // Paper end - Add more WanderingTrader API
 
     public WanderingTrader(EntityType<? extends WanderingTrader> entityType, Level level) {
         super(entityType, level);
@@ -67,7 +_,7 @@
                     this,
                     PotionContents.createItemStack(Items.POTION, Potions.INVISIBILITY),
                     SoundEvents.WANDERING_TRADER_DISAPPEARED,
-                    wanderingTrader -> this.level().isNight() && !wanderingTrader.isInvisible()
+                    wanderingTrader -> this.canDrinkPotion && this.level().isNight() && !wanderingTrader.isInvisible() // Paper - Add more WanderingTrader API
                 )
             );
         this.goalSelector
@@ -77,7 +_,7 @@
                     this,
                     new ItemStack(Items.MILK_BUCKET),
                     SoundEvents.WANDERING_TRADER_REAPPEARED,
-                    wanderingTrader -> this.level().isDay() && wanderingTrader.isInvisible()
+                    wanderingTrader -> this.canDrinkMilk && this.level().isDay() && wanderingTrader.isInvisible() // Paper - Add more WanderingTrader API
                 )
             );
         this.goalSelector.addGoal(1, new TradeWithPlayerGoal(this));
@@ -145,7 +_,16 @@
                 VillagerTrades.ItemListing itemListing = itemListings1[randomInt];
                 MerchantOffer offer = itemListing.getOffer(this, this.random);
                 if (offer != null) {
-                    offers.add(offer);
+                    // CraftBukkit start
+                    VillagerAcquireTradeEvent event = new VillagerAcquireTradeEvent((AbstractVillager) this.getBukkitEntity(), offer.asBukkit());
+                    // Suppress during worldgen
+                    if (this.valid) {
+                        Bukkit.getPluginManager().callEvent(event);
+                    }
+                    if (!event.isCancelled()) {
+                        offers.add(CraftMerchantRecipe.fromBukkit(event.getRecipe()).toMinecraft());
+                    }
+                    // CraftBukkit end
                 }
             }
         }
@@ -189,7 +_,7 @@
     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.5, this.getZ(), i));
+            this.level().addFreshEntity(new ExperienceOrb(this.level(), this.getX(), this.getY() + 0.5, this.getZ(), i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper
         }
     }
 
@@ -241,7 +_,7 @@
 
     private void maybeDespawn() {
         if (this.despawnDelay > 0 && !this.isTrading() && --this.despawnDelay == 0) {
-            this.discard();
+            this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
         }
     }
 
