--- a/net/minecraft/world/entity/npc/Villager.java
+++ b/net/minecraft/world/entity/npc/Villager.java
@@ -6,10 +6,13 @@
 import com.google.common.collect.ImmutableSet;
 import com.mojang.datafixers.util.Pair;
 import com.mojang.logging.LogUtils;
+import com.mojang.serialization.DataResult;
 import com.mojang.serialization.Dynamic;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.BiPredicate;
@@ -89,23 +92,22 @@
 import net.minecraft.world.phys.AABB;
 import org.slf4j.Logger;
 
+// CraftBukkit start
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.event.entity.EntityRemoveEvent;
+import org.bukkit.event.entity.EntityTransformEvent;
+import org.bukkit.event.entity.VillagerReplenishTradeEvent;
+// CraftBukkit end
+
 public class Villager extends AbstractVillager implements ReputationEventHandler, VillagerDataHolder {
+
     private static final Logger LOGGER = LogUtils.getLogger();
     private static final EntityDataAccessor<VillagerData> DATA_VILLAGER_DATA = SynchedEntityData.defineId(Villager.class, EntityDataSerializers.VILLAGER_DATA);
     public static final int BREEDING_FOOD_THRESHOLD = 12;
     public static final Map<Item, Integer> FOOD_POINTS = ImmutableMap.of(Items.BREAD, 4, Items.POTATO, 1, Items.CARROT, 1, Items.BEETROOT, 1);
     private static final int TRADES_PER_LEVEL = 2;
-    private static final Set<Item> WANTED_ITEMS = ImmutableSet.of(
-        Items.BREAD,
-        Items.POTATO,
-        Items.CARROT,
-        Items.WHEAT,
-        Items.WHEAT_SEEDS,
-        Items.BEETROOT,
-        Items.BEETROOT_SEEDS,
-        Items.TORCHFLOWER_SEEDS,
-        Items.PITCHER_POD
-    );
+    private static final Set<Item> WANTED_ITEMS = ImmutableSet.of(Items.BREAD, Items.POTATO, Items.CARROT, Items.WHEAT, Items.WHEAT_SEEDS, Items.BEETROOT, new Item[]{Items.BEETROOT_SEEDS, Items.TORCHFLOWER_SEEDS, Items.PITCHER_POD});
     private static final int MAX_GOSSIP_TOPICS = 10;
     private static final int GOSSIP_COOLDOWN = 1200;
     private static final int GOSSIP_DECAY_INTERVAL = 24000;
@@ -121,7 +123,7 @@
     private Player lastTradedPlayer;
     private boolean chasing;
     private int foodLevel;
-    private final GossipContainer gossips = new GossipContainer();
+    private final GossipContainer gossips;
     private long lastGossipTime;
     private long lastGossipDecayTime;
     private int villagerXp;
@@ -129,59 +131,17 @@
     public int numberOfRestocksToday;
     private long lastRestockCheckDayTime;
     private boolean assignProfessionWhenSpawned;
-    private static final ImmutableList<MemoryModuleType<?>> MEMORY_TYPES = ImmutableList.of(
-        MemoryModuleType.HOME,
-        MemoryModuleType.JOB_SITE,
-        MemoryModuleType.POTENTIAL_JOB_SITE,
-        MemoryModuleType.MEETING_POINT,
-        MemoryModuleType.NEAREST_LIVING_ENTITIES,
-        MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES,
-        MemoryModuleType.VISIBLE_VILLAGER_BABIES,
-        MemoryModuleType.NEAREST_PLAYERS,
-        MemoryModuleType.NEAREST_VISIBLE_PLAYER,
-        MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER,
-        MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM,
-        MemoryModuleType.ITEM_PICKUP_COOLDOWN_TICKS,
-        MemoryModuleType.WALK_TARGET,
-        MemoryModuleType.LOOK_TARGET,
-        MemoryModuleType.INTERACTION_TARGET,
-        MemoryModuleType.BREED_TARGET,
-        MemoryModuleType.PATH,
-        MemoryModuleType.DOORS_TO_CLOSE,
-        MemoryModuleType.NEAREST_BED,
-        MemoryModuleType.HURT_BY,
-        MemoryModuleType.HURT_BY_ENTITY,
-        MemoryModuleType.NEAREST_HOSTILE,
-        MemoryModuleType.SECONDARY_JOB_SITE,
-        MemoryModuleType.HIDING_PLACE,
-        MemoryModuleType.HEARD_BELL_TIME,
-        MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE,
-        MemoryModuleType.LAST_SLEPT,
-        MemoryModuleType.LAST_WOKEN,
-        MemoryModuleType.LAST_WORKED_AT_POI,
-        MemoryModuleType.GOLEM_DETECTED_RECENTLY
-    );
-    private static final ImmutableList<SensorType<? extends Sensor<? super Villager>>> SENSOR_TYPES = ImmutableList.of(
-        SensorType.NEAREST_LIVING_ENTITIES,
-        SensorType.NEAREST_PLAYERS,
-        SensorType.NEAREST_ITEMS,
-        SensorType.NEAREST_BED,
-        SensorType.HURT_BY,
-        SensorType.VILLAGER_HOSTILES,
-        SensorType.VILLAGER_BABIES,
-        SensorType.SECONDARY_POIS,
-        SensorType.GOLEM_DETECTED
-    );
-    public static final Map<MemoryModuleType<GlobalPos>, BiPredicate<Villager, Holder<PoiType>>> POI_MEMORIES = ImmutableMap.of(
-        MemoryModuleType.HOME,
-        (villager, holder) -> holder.is(PoiTypes.HOME),
-        MemoryModuleType.JOB_SITE,
-        (villager, holder) -> villager.getVillagerData().getProfession().heldJobSite().test(holder),
-        MemoryModuleType.POTENTIAL_JOB_SITE,
-        (villager, holder) -> VillagerProfession.ALL_ACQUIRABLE_JOBS.test(holder),
-        MemoryModuleType.MEETING_POINT,
-        (villager, holder) -> holder.is(PoiTypes.MEETING)
-    );
+    private static final ImmutableList<MemoryModuleType<?>> MEMORY_TYPES = ImmutableList.of(MemoryModuleType.HOME, MemoryModuleType.JOB_SITE, MemoryModuleType.POTENTIAL_JOB_SITE, MemoryModuleType.MEETING_POINT, MemoryModuleType.NEAREST_LIVING_ENTITIES, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, MemoryModuleType.VISIBLE_VILLAGER_BABIES, MemoryModuleType.NEAREST_PLAYERS, MemoryModuleType.NEAREST_VISIBLE_PLAYER, MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, MemoryModuleType.ITEM_PICKUP_COOLDOWN_TICKS, new MemoryModuleType[]{MemoryModuleType.WALK_TARGET, MemoryModuleType.LOOK_TARGET, MemoryModuleType.INTERACTION_TARGET, MemoryModuleType.BREED_TARGET, MemoryModuleType.PATH, MemoryModuleType.DOORS_TO_CLOSE, MemoryModuleType.NEAREST_BED, MemoryModuleType.HURT_BY, MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.NEAREST_HOSTILE, MemoryModuleType.SECONDARY_JOB_SITE, MemoryModuleType.HIDING_PLACE, MemoryModuleType.HEARD_BELL_TIME, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.LAST_SLEPT, MemoryModuleType.LAST_WOKEN, MemoryModuleType.LAST_WORKED_AT_POI, MemoryModuleType.GOLEM_DETECTED_RECENTLY});
+    private static final ImmutableList<SensorType<? extends Sensor<? super Villager>>> SENSOR_TYPES = ImmutableList.of(SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.NEAREST_ITEMS, SensorType.NEAREST_BED, SensorType.HURT_BY, SensorType.VILLAGER_HOSTILES, SensorType.VILLAGER_BABIES, SensorType.SECONDARY_POIS, SensorType.GOLEM_DETECTED);
+    public static final Map<MemoryModuleType<GlobalPos>, BiPredicate<Villager, Holder<PoiType>>> POI_MEMORIES = ImmutableMap.of(MemoryModuleType.HOME, (entityvillager, holder) -> {
+        return holder.is(PoiTypes.HOME);
+    }, MemoryModuleType.JOB_SITE, (entityvillager, holder) -> {
+        return entityvillager.getVillagerData().getProfession().heldJobSite().test(holder);
+    }, MemoryModuleType.POTENTIAL_JOB_SITE, (entityvillager, holder) -> {
+        return VillagerProfession.ALL_ACQUIRABLE_JOBS.test(holder);
+    }, MemoryModuleType.MEETING_POINT, (entityvillager, holder) -> {
+        return holder.is(PoiTypes.MEETING);
+    });
 
     public Villager(EntityType<? extends Villager> entityType, Level world) {
         this(entityType, world, VillagerType.PLAINS);
@@ -189,7 +149,8 @@
 
     public Villager(EntityType<? extends Villager> entityType, Level world, VillagerType type) {
         super(entityType, world);
-        ((GroundPathNavigation)this.getNavigation()).setCanOpenDoors(true);
+        this.gossips = new GossipContainer();
+        ((GroundPathNavigation) this.getNavigation()).setCanOpenDoors(true);
         this.getNavigation().setCanFloat(true);
         this.setCanPickUpLoot(true);
         this.setVillagerData(this.getVillagerData().setType(type).setProfession(VillagerProfession.NONE));
@@ -197,54 +158,49 @@
 
     @Override
     public Brain<Villager> getBrain() {
-        return (Brain<Villager>)super.getBrain();
+        return (Brain<Villager>) super.getBrain(); // CraftBukkit - decompile error
     }
 
     @Override
     protected Brain.Provider<Villager> brainProvider() {
-        return Brain.provider(MEMORY_TYPES, SENSOR_TYPES);
+        return Brain.provider(Villager.MEMORY_TYPES, Villager.SENSOR_TYPES);
     }
 
     @Override
     protected Brain<?> makeBrain(Dynamic<?> dynamic) {
-        Brain<Villager> brain = this.brainProvider().makeBrain(dynamic);
-        this.registerBrainGoals(brain);
-        return brain;
+        Brain<Villager> behaviorcontroller = this.brainProvider().makeBrain(dynamic);
+
+        this.registerBrainGoals(behaviorcontroller);
+        return behaviorcontroller;
     }
 
     public void refreshBrain(ServerLevel world) {
-        Brain<Villager> brain = this.getBrain();
-        brain.stopAll(world, this);
-        this.brain = brain.copyWithoutBehaviors();
+        Brain<Villager> behaviorcontroller = this.getBrain();
+
+        behaviorcontroller.stopAll(world, this);
+        this.brain = behaviorcontroller.copyWithoutBehaviors();
         this.registerBrainGoals(this.getBrain());
     }
 
     private void registerBrainGoals(Brain<Villager> brain) {
-        VillagerProfession villagerProfession = this.getVillagerData().getProfession();
+        VillagerProfession villagerprofession = this.getVillagerData().getProfession();
+
         if (this.isBaby()) {
             brain.setSchedule(Schedule.VILLAGER_BABY);
             brain.addActivity(Activity.PLAY, VillagerGoalPackages.getPlayPackage(0.5F));
         } else {
             brain.setSchedule(Schedule.VILLAGER_DEFAULT);
-            brain.addActivityWithConditions(
-                Activity.WORK,
-                VillagerGoalPackages.getWorkPackage(villagerProfession, 0.5F),
-                ImmutableSet.of(Pair.of(MemoryModuleType.JOB_SITE, MemoryStatus.VALUE_PRESENT))
-            );
+            brain.addActivityWithConditions(Activity.WORK, VillagerGoalPackages.getWorkPackage(villagerprofession, 0.5F), ImmutableSet.of(Pair.of(MemoryModuleType.JOB_SITE, MemoryStatus.VALUE_PRESENT)));
         }
 
-        brain.addActivity(Activity.CORE, VillagerGoalPackages.getCorePackage(villagerProfession, 0.5F));
-        brain.addActivityWithConditions(
-            Activity.MEET,
-            VillagerGoalPackages.getMeetPackage(villagerProfession, 0.5F),
-            ImmutableSet.of(Pair.of(MemoryModuleType.MEETING_POINT, MemoryStatus.VALUE_PRESENT))
-        );
-        brain.addActivity(Activity.REST, VillagerGoalPackages.getRestPackage(villagerProfession, 0.5F));
-        brain.addActivity(Activity.IDLE, VillagerGoalPackages.getIdlePackage(villagerProfession, 0.5F));
-        brain.addActivity(Activity.PANIC, VillagerGoalPackages.getPanicPackage(villagerProfession, 0.5F));
-        brain.addActivity(Activity.PRE_RAID, VillagerGoalPackages.getPreRaidPackage(villagerProfession, 0.5F));
-        brain.addActivity(Activity.RAID, VillagerGoalPackages.getRaidPackage(villagerProfession, 0.5F));
-        brain.addActivity(Activity.HIDE, VillagerGoalPackages.getHidePackage(villagerProfession, 0.5F));
+        brain.addActivity(Activity.CORE, VillagerGoalPackages.getCorePackage(villagerprofession, 0.5F));
+        brain.addActivityWithConditions(Activity.MEET, VillagerGoalPackages.getMeetPackage(villagerprofession, 0.5F), ImmutableSet.of(Pair.of(MemoryModuleType.MEETING_POINT, MemoryStatus.VALUE_PRESENT)));
+        brain.addActivity(Activity.REST, VillagerGoalPackages.getRestPackage(villagerprofession, 0.5F));
+        brain.addActivity(Activity.IDLE, VillagerGoalPackages.getIdlePackage(villagerprofession, 0.5F));
+        brain.addActivity(Activity.PANIC, VillagerGoalPackages.getPanicPackage(villagerprofession, 0.5F));
+        brain.addActivity(Activity.PRE_RAID, VillagerGoalPackages.getPreRaidPackage(villagerprofession, 0.5F));
+        brain.addActivity(Activity.RAID, VillagerGoalPackages.getRaidPackage(villagerprofession, 0.5F));
+        brain.addActivity(Activity.HIDE, VillagerGoalPackages.getHidePackage(villagerprofession, 0.5F));
         brain.setCoreActivities(ImmutableSet.of(Activity.CORE));
         brain.setDefaultActivity(Activity.IDLE);
         brain.setActiveActivityIfPossible(Activity.IDLE);
@@ -255,55 +211,86 @@
     protected void ageBoundaryReached() {
         super.ageBoundaryReached();
         if (this.level() instanceof ServerLevel) {
-            this.refreshBrain((ServerLevel)this.level());
+            this.refreshBrain((ServerLevel) this.level());
         }
+
     }
 
     public static AttributeSupplier.Builder createAttributes() {
-        return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.5).add(Attributes.FOLLOW_RANGE, 48.0);
+        return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.5D).add(Attributes.FOLLOW_RANGE, 48.0D);
     }
 
     public boolean assignProfessionWhenSpawned() {
         return this.assignProfessionWhenSpawned;
     }
 
+    // Spigot Start
+    @Override
+    public void inactiveTick() {
+        // SPIGOT-3874, SPIGOT-3894, SPIGOT-3846, SPIGOT-5286 :(
+        // Paper start
+        if (this.getUnhappyCounter() > 0) {
+            this.setUnhappyCounter(this.getUnhappyCounter() - 1);
+        }
+        if (this.isEffectiveAi()) {
+            if (this.level().spigotConfig.tickInactiveVillagers) {
+                this.customServerAiStep();
+            } else {
+                this.customServerAiStep(true);
+            }
+        }
+        maybeDecayGossip();
+        // Paper end
+
+        super.inactiveTick();
+    }
+    // Spigot End
+
     @Override
+    @Deprecated // Paper
     protected void customServerAiStep() {
+        // Paper start
+        this.customServerAiStep(false);
+    }
+    protected void customServerAiStep(final boolean inactive) {
+        // Paper end
         this.level().getProfiler().push("villagerBrain");
-        this.getBrain().tick((ServerLevel)this.level(), this);
+        if (!inactive) this.getBrain().tick((ServerLevel) this.level(), this); // Paper
         this.level().getProfiler().pop();
         if (this.assignProfessionWhenSpawned) {
             this.assignProfessionWhenSpawned = false;
         }
 
         if (!this.isTrading() && this.updateMerchantTimer > 0) {
-            this.updateMerchantTimer--;
+            --this.updateMerchantTimer;
             if (this.updateMerchantTimer <= 0) {
                 if (this.increaseProfessionLevelOnUpdate) {
                     this.increaseMerchantCareer();
                     this.increaseProfessionLevelOnUpdate = false;
                 }
 
-                this.addEffect(new MobEffectInstance(MobEffects.REGENERATION, 200, 0));
+                this.addEffect(new MobEffectInstance(MobEffects.REGENERATION, 200, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.VILLAGER_TRADE); // CraftBukkit
             }
         }
 
         if (this.lastTradedPlayer != null && this.level() instanceof ServerLevel) {
-            ((ServerLevel)this.level()).onReputationEvent(ReputationEventType.TRADE, this.lastTradedPlayer, this);
-            this.level().broadcastEntityEvent(this, (byte)14);
+            ((ServerLevel) this.level()).onReputationEvent(ReputationEventType.TRADE, this.lastTradedPlayer, this);
+            this.level().broadcastEntityEvent(this, (byte) 14);
             this.lastTradedPlayer = null;
         }
 
-        if (!this.isNoAi() && this.random.nextInt(100) == 0) {
-            Raid raid = ((ServerLevel)this.level()).getRaidAt(this.blockPosition());
+        if (!inactive && !this.isNoAi() && this.random.nextInt(100) == 0) { // Paper
+            Raid raid = ((ServerLevel) this.level()).getRaidAt(this.blockPosition());
+
             if (raid != null && raid.isActive() && !raid.isOver()) {
-                this.level().broadcastEntityEvent(this, (byte)42);
+                this.level().broadcastEntityEvent(this, (byte) 42);
             }
         }
 
         if (this.getVillagerData().getProfession() == VillagerProfession.NONE && this.isTrading()) {
             this.stopTrading();
         }
+        if (inactive) return; // Paper
 
         super.customServerAiStep();
     }
@@ -320,31 +307,35 @@
 
     @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.isSleeping()) {
-            return super.mobInteract(player, hand);
-        } else if (this.isBaby()) {
-            this.setUnhappy();
-            return InteractionResult.sidedSuccess(this.level().isClientSide);
-        } else {
-            if (!this.level().isClientSide) {
-                boolean bl = this.getOffers().isEmpty();
-                if (hand == InteractionHand.MAIN_HAND) {
-                    if (bl) {
-                        this.setUnhappy();
+        ItemStack itemstack = player.getItemInHand(hand);
+
+        if (!itemstack.is(Items.VILLAGER_SPAWN_EGG) && this.isAlive() && !this.isTrading() && !this.isSleeping()) {
+            if (this.isBaby()) {
+                this.setUnhappy();
+                return InteractionResult.sidedSuccess(this.level().isClientSide);
+            } else {
+                if (!this.level().isClientSide) {
+                    boolean flag = this.getOffers().isEmpty();
+
+                    if (hand == InteractionHand.MAIN_HAND) {
+                        if (flag) {
+                            this.setUnhappy();
+                        }
+
+                        player.awardStat(Stats.TALKED_TO_VILLAGER);
                     }
 
-                    player.awardStat(Stats.TALKED_TO_VILLAGER);
-                }
+                    if (flag) {
+                        return InteractionResult.CONSUME;
+                    }
 
-                if (bl) {
-                    return InteractionResult.CONSUME;
+                    this.startTrading(player);
                 }
 
-                this.startTrading(player);
+                return InteractionResult.sidedSuccess(this.level().isClientSide);
             }
-
-            return InteractionResult.sidedSuccess(this.level().isClientSide);
+        } else {
+            return super.mobInteract(player, hand);
         }
     }
 
@@ -353,6 +344,7 @@
         if (!this.level().isClientSide()) {
             this.makeSound(SoundEvents.VILLAGER_NO);
         }
+
     }
 
     private void startTrading(Player customer) {
@@ -363,11 +355,13 @@
 
     @Override
     public void setTradingPlayer(@Nullable Player customer) {
-        boolean bl = this.getTradingPlayer() != null && customer == null;
+        boolean flag = this.getTradingPlayer() != null && customer == null;
+
         super.setTradingPlayer(customer);
-        if (bl) {
+        if (flag) {
             this.stopTrading();
         }
+
     }
 
     @Override
@@ -378,9 +372,14 @@
 
     private void resetSpecialPrices() {
         if (!this.level().isClientSide()) {
-            for (MerchantOffer merchantOffer : this.getOffers()) {
-                merchantOffer.resetSpecialPriceDiff();
+            Iterator iterator = this.getOffers().iterator();
+
+            while (iterator.hasNext()) {
+                MerchantOffer merchantrecipe = (MerchantOffer) iterator.next();
+
+                merchantrecipe.resetSpecialPriceDiff();
             }
+
         }
     }
 
@@ -396,39 +395,49 @@
 
     public void restock() {
         this.updateDemand();
+        Iterator iterator = this.getOffers().iterator();
 
-        for (MerchantOffer merchantOffer : this.getOffers()) {
-            merchantOffer.resetUses();
+        while (iterator.hasNext()) {
+            MerchantOffer merchantrecipe = (MerchantOffer) iterator.next();
+
+            // CraftBukkit start
+            VillagerReplenishTradeEvent event = new VillagerReplenishTradeEvent((org.bukkit.entity.Villager) this.getBukkitEntity(), merchantrecipe.asBukkit());
+            Bukkit.getPluginManager().callEvent(event);
+            if (!event.isCancelled()) {
+                merchantrecipe.resetUses();
+            }
+            // CraftBukkit end
         }
 
         this.resendOffersToTradingPlayer();
         this.lastRestockGameTime = this.level().getGameTime();
-        this.numberOfRestocksToday++;
+        ++this.numberOfRestocksToday;
     }
 
     private void resendOffersToTradingPlayer() {
-        MerchantOffers merchantOffers = this.getOffers();
-        Player player = this.getTradingPlayer();
-        if (player != null && !merchantOffers.isEmpty()) {
-            player.sendMerchantOffers(
-                player.containerMenu.containerId,
-                merchantOffers,
-                this.getVillagerData().getLevel(),
-                this.getVillagerXp(),
-                this.showProgressBar(),
-                this.canRestock()
-            );
+        MerchantOffers merchantrecipelist = this.getOffers();
+        Player entityhuman = this.getTradingPlayer();
+
+        if (entityhuman != null && !merchantrecipelist.isEmpty()) {
+            entityhuman.sendMerchantOffers(entityhuman.containerMenu.containerId, merchantrecipelist, this.getVillagerData().getLevel(), this.getVillagerXp(), this.showProgressBar(), this.canRestock());
         }
+
     }
 
     private boolean needsToRestock() {
-        for (MerchantOffer merchantOffer : this.getOffers()) {
-            if (merchantOffer.needsRestock()) {
-                return true;
+        Iterator iterator = this.getOffers().iterator();
+
+        MerchantOffer merchantrecipe;
+
+        do {
+            if (!iterator.hasNext()) {
+                return false;
             }
-        }
 
-        return false;
+            merchantrecipe = (MerchantOffer) iterator.next();
+        } while (!merchantrecipe.needsRestock());
+
+        return true;
     }
 
     private boolean allowedToRestock() {
@@ -436,19 +445,21 @@
     }
 
     public boolean shouldRestock() {
-        long l = this.lastRestockGameTime + 12000L;
-        long m = this.level().getGameTime();
-        boolean bl = m > l;
-        long n = this.level().getDayTime();
+        long i = this.lastRestockGameTime + 12000L;
+        long j = this.level().getGameTime();
+        boolean flag = j > i;
+        long k = this.level().getDayTime();
+
         if (this.lastRestockCheckDayTime > 0L) {
-            long o = this.lastRestockCheckDayTime / 24000L;
-            long p = n / 24000L;
-            bl |= p > o;
+            long l = this.lastRestockCheckDayTime / 24000L;
+            long i1 = k / 24000L;
+
+            flag |= i1 > l;
         }
 
-        this.lastRestockCheckDayTime = n;
-        if (bl) {
-            this.lastRestockGameTime = m;
+        this.lastRestockCheckDayTime = k;
+        if (flag) {
+            this.lastRestockGameTime = j;
             this.resetNumberOfRestocks();
         }
 
@@ -457,13 +468,24 @@
 
     private void catchUpDemand() {
         int i = 2 - this.numberOfRestocksToday;
+
         if (i > 0) {
-            for (MerchantOffer merchantOffer : this.getOffers()) {
-                merchantOffer.resetUses();
+            Iterator iterator = this.getOffers().iterator();
+
+            while (iterator.hasNext()) {
+                MerchantOffer merchantrecipe = (MerchantOffer) iterator.next();
+
+                // CraftBukkit start
+                VillagerReplenishTradeEvent event = new VillagerReplenishTradeEvent((org.bukkit.entity.Villager) this.getBukkitEntity(), merchantrecipe.asBukkit());
+                Bukkit.getPluginManager().callEvent(event);
+                if (!event.isCancelled()) {
+                    merchantrecipe.resetUses();
+                }
+                // CraftBukkit end
             }
         }
 
-        for (int j = 0; j < i; j++) {
+        for (int j = 0; j < i; ++j) {
             this.updateDemand();
         }
 
@@ -471,43 +493,65 @@
     }
 
     private void updateDemand() {
-        for (MerchantOffer merchantOffer : this.getOffers()) {
-            merchantOffer.updateDemand();
+        Iterator iterator = this.getOffers().iterator();
+
+        while (iterator.hasNext()) {
+            MerchantOffer merchantrecipe = (MerchantOffer) iterator.next();
+
+            merchantrecipe.updateDemand();
         }
+
     }
 
     private void updateSpecialPrices(Player player) {
         int i = this.getPlayerReputation(player);
+
         if (i != 0) {
-            for (MerchantOffer merchantOffer : this.getOffers()) {
-                merchantOffer.addToSpecialPriceDiff(-Mth.floor((float)i * merchantOffer.getPriceMultiplier()));
+            Iterator iterator = this.getOffers().iterator();
+
+            while (iterator.hasNext()) {
+                MerchantOffer merchantrecipe = (MerchantOffer) iterator.next();
+                if (merchantrecipe.ignoreDiscounts) continue; // Paper - Add ignore discounts API
+
+                merchantrecipe.addToSpecialPriceDiff(-Mth.floor((float) i * merchantrecipe.getPriceMultiplier()));
             }
         }
 
         if (player.hasEffect(MobEffects.HERO_OF_THE_VILLAGE)) {
-            MobEffectInstance mobEffectInstance = player.getEffect(MobEffects.HERO_OF_THE_VILLAGE);
-            int j = mobEffectInstance.getAmplifier();
+            MobEffectInstance mobeffect = player.getEffect(MobEffects.HERO_OF_THE_VILLAGE);
+            int j = mobeffect.getAmplifier();
+            Iterator iterator1 = this.getOffers().iterator();
 
-            for (MerchantOffer merchantOffer2 : this.getOffers()) {
-                double d = 0.3 + 0.0625 * (double)j;
-                int k = (int)Math.floor(d * (double)merchantOffer2.getBaseCostA().getCount());
-                merchantOffer2.addToSpecialPriceDiff(-Math.max(k, 1));
+            while (iterator1.hasNext()) {
+                MerchantOffer merchantrecipe1 = (MerchantOffer) iterator1.next();
+                if (merchantrecipe1.ignoreDiscounts) continue; // Paper - Add ignore discounts API
+                double d0 = 0.3D + 0.0625D * (double) j;
+                int k = (int) Math.floor(d0 * (double) merchantrecipe1.getBaseCostA().getCount());
+
+                merchantrecipe1.addToSpecialPriceDiff(-Math.max(k, 1));
             }
         }
+
     }
 
     @Override
     protected void defineSynchedData(SynchedEntityData.Builder builder) {
         super.defineSynchedData(builder);
-        builder.define(DATA_VILLAGER_DATA, new VillagerData(VillagerType.PLAINS, VillagerProfession.NONE, 1));
+        builder.define(Villager.DATA_VILLAGER_DATA, new VillagerData(VillagerType.PLAINS, VillagerProfession.NONE, 1));
     }
 
     @Override
     public void addAdditionalSaveData(CompoundTag nbt) {
         super.addAdditionalSaveData(nbt);
-        VillagerData.CODEC.encodeStart(NbtOps.INSTANCE, this.getVillagerData()).resultOrPartial(LOGGER::error).ifPresent(tag -> nbt.put("VillagerData", tag));
-        nbt.putByte("FoodLevel", (byte)this.foodLevel);
-        nbt.put("Gossips", this.gossips.store(NbtOps.INSTANCE));
+        DataResult<Tag> dataresult = VillagerData.CODEC.encodeStart(NbtOps.INSTANCE, this.getVillagerData()); // CraftBukkit - decompile error
+        Logger logger = Villager.LOGGER;
+
+        Objects.requireNonNull(logger);
+        dataresult.resultOrPartial(logger::error).ifPresent((nbtbase) -> {
+            nbt.put("VillagerData", nbtbase);
+        });
+        nbt.putByte("FoodLevel", (byte) this.foodLevel);
+        nbt.put("Gossips", (Tag) this.gossips.store(NbtOps.INSTANCE));
         nbt.putInt("Xp", this.villagerXp);
         nbt.putLong("LastRestock", this.lastRestockGameTime);
         nbt.putLong("LastGossipDecay", this.lastGossipDecayTime);
@@ -515,24 +559,29 @@
         if (this.assignProfessionWhenSpawned) {
             nbt.putBoolean("AssignProfessionWhenSpawned", true);
         }
+
     }
 
     @Override
     public void readAdditionalSaveData(CompoundTag nbt) {
         super.readAdditionalSaveData(nbt);
         if (nbt.contains("VillagerData", 10)) {
-            VillagerData.CODEC
-                .parse(NbtOps.INSTANCE, nbt.get("VillagerData"))
-                .resultOrPartial(LOGGER::error)
-                .ifPresent(villagerData -> this.entityData.set(DATA_VILLAGER_DATA, villagerData));
+            DataResult<VillagerData> dataresult = VillagerData.CODEC.parse(new Dynamic(NbtOps.INSTANCE, nbt.get("VillagerData")));
+            Logger logger = Villager.LOGGER;
+
+            Objects.requireNonNull(logger);
+            dataresult.resultOrPartial(logger::error).ifPresent((villagerdata) -> {
+                this.entityData.set(Villager.DATA_VILLAGER_DATA, villagerdata);
+            });
         }
 
         if (nbt.contains("FoodLevel", 1)) {
             this.foodLevel = nbt.getByte("FoodLevel");
         }
 
-        ListTag listTag = nbt.getList("Gossips", 10);
-        this.gossips.update(new Dynamic<>(NbtOps.INSTANCE, listTag));
+        ListTag nbttaglist = nbt.getList("Gossips", 10);
+
+        this.gossips.update(new Dynamic(NbtOps.INSTANCE, nbttaglist));
         if (nbt.contains("Xp", 3)) {
             this.villagerXp = nbt.getInt("Xp");
         }
@@ -541,13 +590,14 @@
         this.lastGossipDecayTime = nbt.getLong("LastGossipDecay");
         this.setCanPickUpLoot(true);
         if (this.level() instanceof ServerLevel) {
-            this.refreshBrain((ServerLevel)this.level());
+            this.refreshBrain((ServerLevel) this.level());
         }
 
         this.numberOfRestocksToday = nbt.getInt("RestocksToday");
         if (nbt.contains("AssignProfessionWhenSpawned")) {
             this.assignProfessionWhenSpawned = nbt.getBoolean("AssignProfessionWhenSpawned");
         }
+
     }
 
     @Override
@@ -558,11 +608,7 @@
     @Nullable
     @Override
     protected SoundEvent getAmbientSound() {
-        if (this.isSleeping()) {
-            return null;
-        } else {
-            return this.isTrading() ? SoundEvents.VILLAGER_TRADE : SoundEvents.VILLAGER_AMBIENT;
-        }
+        return this.isSleeping() ? null : (this.isTrading() ? SoundEvents.VILLAGER_TRADE : SoundEvents.VILLAGER_AMBIENT);
     }
 
     @Override
@@ -581,23 +627,25 @@
 
     @Override
     public void setVillagerData(VillagerData villagerData) {
-        VillagerData villagerData2 = this.getVillagerData();
-        if (villagerData2.getProfession() != villagerData.getProfession()) {
+        VillagerData villagerdata1 = this.getVillagerData();
+
+        if (villagerdata1.getProfession() != villagerData.getProfession()) {
             this.offers = null;
         }
 
-        this.entityData.set(DATA_VILLAGER_DATA, villagerData);
+        this.entityData.set(Villager.DATA_VILLAGER_DATA, villagerData);
     }
 
     @Override
     public VillagerData getVillagerData() {
-        return this.entityData.get(DATA_VILLAGER_DATA);
+        return (VillagerData) this.entityData.get(Villager.DATA_VILLAGER_DATA);
     }
 
     @Override
     protected void rewardTradeXp(MerchantOffer offer) {
         int i = 3 + this.random.nextInt(4);
-        this.villagerXp = this.villagerXp + offer.getXp();
+
+        this.villagerXp += offer.getXp();
         this.lastTradedPlayer = this.getTradingPlayer();
         if (this.shouldIncreaseLevel()) {
             this.updateMerchantTimer = 40;
@@ -606,12 +654,13 @@
         }
 
         if (offer.shouldRewardExp()) {
-            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.5D, this.getZ(), i, org.bukkit.entity.ExperienceOrb.SpawnReason.VILLAGER_TRADE, this.getTradingPlayer(), this)); // Paper
         }
+
     }
 
-    public void setChasing(boolean bl) {
-        this.chasing = bl;
+    public void setChasing(boolean flag) {
+        this.chasing = flag;
     }
 
     public boolean isChasing() {
@@ -621,9 +670,9 @@
     @Override
     public void setLastHurtByMob(@Nullable LivingEntity attacker) {
         if (attacker != null && this.level() instanceof ServerLevel) {
-            ((ServerLevel)this.level()).onReputationEvent(ReputationEventType.VILLAGER_HURT, attacker, this);
+            ((ServerLevel) this.level()).onReputationEvent(ReputationEventType.VILLAGER_HURT, attacker, this);
             if (this.isAlive() && attacker instanceof Player) {
-                this.level().broadcastEntityEvent(this, (byte)13);
+                this.level().broadcastEntityEvent(this, (byte) 13);
             }
         }
 
@@ -632,8 +681,9 @@
 
     @Override
     public void die(DamageSource damageSource) {
-        LOGGER.info("Villager {} died, message: '{}'", this, damageSource.getLocalizedDeathMessage(this).getString());
+        if (org.spigotmc.SpigotConfig.logVillagerDeaths) Villager.LOGGER.info("Villager {} died, message: '{}'", this, damageSource.getLocalizedDeathMessage(this).getString()); // Spigot
         Entity entity = damageSource.getEntity();
+
         if (entity != null) {
             this.tellWitnessesThatIWasMurdered(entity);
         }
@@ -650,29 +700,39 @@
     }
 
     private void tellWitnessesThatIWasMurdered(Entity killer) {
-        if (this.level() instanceof ServerLevel serverLevel) {
-            Optional<NearestVisibleLivingEntities> optional = this.brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES);
+        Level world = this.level();
+
+        if (world instanceof ServerLevel worldserver) {
+            Optional optional = this.brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES);
+
             if (!optional.isEmpty()) {
-                optional.get()
-                    .findAll(ReputationEventHandler.class::isInstance)
-                    .forEach(observer -> serverLevel.onReputationEvent(ReputationEventType.VILLAGER_KILLED, killer, (ReputationEventHandler)observer));
+                NearestVisibleLivingEntities nearestvisiblelivingentities = (NearestVisibleLivingEntities) optional.get();
+
+                Objects.requireNonNull(ReputationEventHandler.class);
+                nearestvisiblelivingentities.findAll(ReputationEventHandler.class::isInstance).forEach((entityliving) -> {
+                    worldserver.onReputationEvent(ReputationEventType.VILLAGER_KILLED, killer, (ReputationEventHandler) entityliving);
+                });
             }
         }
     }
 
     public void releasePoi(MemoryModuleType<GlobalPos> pos) {
         if (this.level() instanceof ServerLevel) {
-            MinecraftServer minecraftServer = ((ServerLevel)this.level()).getServer();
-            this.brain.getMemory(pos).ifPresent(posx -> {
-                ServerLevel serverLevel = minecraftServer.getLevel(posx.dimension());
-                if (serverLevel != null) {
-                    PoiManager poiManager = serverLevel.getPoiManager();
-                    Optional<Holder<PoiType>> optional = poiManager.getType(posx.pos());
-                    BiPredicate<Villager, Holder<PoiType>> biPredicate = POI_MEMORIES.get(pos);
-                    if (optional.isPresent() && biPredicate.test(this, optional.get())) {
-                        poiManager.release(posx.pos());
-                        DebugPackets.sendPoiTicketCountPacket(serverLevel, posx.pos());
+            MinecraftServer minecraftserver = ((ServerLevel) this.level()).getServer();
+
+            this.brain.getMemory(pos).ifPresent((globalpos) -> {
+                ServerLevel worldserver = minecraftserver.getLevel(globalpos.dimension());
+
+                if (worldserver != null) {
+                    PoiManager villageplace = worldserver.getPoiManager();
+                    Optional<Holder<PoiType>> optional = villageplace.getType(globalpos.pos());
+                    BiPredicate<Villager, Holder<PoiType>> bipredicate = (BiPredicate) Villager.POI_MEMORIES.get(pos);
+
+                    if (optional.isPresent() && bipredicate.test(this, (Holder) optional.get())) {
+                        villageplace.release(globalpos.pos());
+                        DebugPackets.sendPoiTicketCountPacket(worldserver, globalpos.pos());
                     }
+
                 }
             });
         }
@@ -689,15 +749,17 @@
 
     private void eatUntilFull() {
         if (this.hungry() && this.countFoodPointsInInventory() != 0) {
-            for (int i = 0; i < this.getInventory().getContainerSize(); i++) {
-                ItemStack itemStack = this.getInventory().getItem(i);
-                if (!itemStack.isEmpty()) {
-                    Integer integer = FOOD_POINTS.get(itemStack.getItem());
+            for (int i = 0; i < this.getInventory().getContainerSize(); ++i) {
+                ItemStack itemstack = this.getInventory().getItem(i);
+
+                if (!itemstack.isEmpty()) {
+                    Integer integer = (Integer) Villager.FOOD_POINTS.get(itemstack.getItem());
+
                     if (integer != null) {
-                        int j = itemStack.getCount();
+                        int j = itemstack.getCount();
 
-                        for (int k = j; k > 0; k--) {
-                            this.foodLevel = this.foodLevel + integer;
+                        for (int k = j; k > 0; --k) {
+                            this.foodLevel += integer;
                             this.getInventory().removeItem(i, 1);
                             if (!this.hungry()) {
                                 return;
@@ -706,11 +768,14 @@
                     }
                 }
             }
+
         }
     }
 
     public int getPlayerReputation(Player player) {
-        return this.gossips.getReputation(player.getUUID(), gossipType -> true);
+        return this.gossips.getReputation(player.getUUID(), (reputationtype) -> {
+            return true;
+        });
     }
 
     private void digestFood(int amount) {
@@ -728,6 +793,7 @@
 
     private boolean shouldIncreaseLevel() {
         int i = this.getVillagerData().getLevel();
+
         return VillagerData.canLevelUp(i) && this.villagerXp >= VillagerData.getMaxXpPerLevel(i);
     }
 
@@ -738,9 +804,9 @@
 
     @Override
     protected Component getTypeName() {
-        return Component.translatable(
-            this.getType().getDescriptionId() + "." + BuiltInRegistries.VILLAGER_PROFESSION.getKey(this.getVillagerData().getProfession()).getPath()
-        );
+        String s = this.getType().getDescriptionId();
+
+        return Component.translatable(s + "." + BuiltInRegistries.VILLAGER_PROFESSION.getKey(this.getVillagerData().getProfession()).getPath());
     }
 
     @Override
@@ -756,6 +822,7 @@
         } else {
             super.handleEntityEvent(status);
         }
+
     }
 
     @Nullable
@@ -765,10 +832,7 @@
             this.setVillagerData(this.getVillagerData().setProfession(VillagerProfession.NONE));
         }
 
-        if (spawnReason == MobSpawnType.COMMAND
-            || spawnReason == MobSpawnType.SPAWN_EGG
-            || MobSpawnType.isSpawner(spawnReason)
-            || spawnReason == MobSpawnType.DISPENSER) {
+        if (spawnReason == MobSpawnType.COMMAND || spawnReason == MobSpawnType.SPAWN_EGG || MobSpawnType.isSpawner(spawnReason) || spawnReason == MobSpawnType.DISPENSER) {
             this.setVillagerData(this.getVillagerData().setType(VillagerType.byBiome(world.getBiome(this.blockPosition()))));
         }
 
@@ -781,46 +845,62 @@
 
     @Nullable
     @Override
-    public Villager getBreedOffspring(ServerLevel serverLevel, AgeableMob ageableMob) {
-        double d = this.random.nextDouble();
-        VillagerType villagerType;
-        if (d < 0.5) {
-            villagerType = VillagerType.byBiome(serverLevel.getBiome(this.blockPosition()));
-        } else if (d < 0.75) {
-            villagerType = this.getVillagerData().getType();
+    public Villager getBreedOffspring(ServerLevel world, AgeableMob entity) {
+        double d0 = this.random.nextDouble();
+        VillagerType villagertype;
+
+        if (d0 < 0.5D) {
+            villagertype = VillagerType.byBiome(world.getBiome(this.blockPosition()));
+        } else if (d0 < 0.75D) {
+            villagertype = this.getVillagerData().getType();
         } else {
-            villagerType = ((Villager)ageableMob).getVillagerData().getType();
+            villagertype = ((Villager) entity).getVillagerData().getType();
         }
 
-        Villager villager = new Villager(EntityType.VILLAGER, serverLevel, villagerType);
-        villager.finalizeSpawn(serverLevel, serverLevel.getCurrentDifficultyAt(villager.blockPosition()), MobSpawnType.BREEDING, null);
-        return villager;
+        Villager entityvillager = new Villager(EntityType.VILLAGER, world, villagertype);
+
+        entityvillager.finalizeSpawn(world, world.getCurrentDifficultyAt(entityvillager.blockPosition()), MobSpawnType.BREEDING, (SpawnGroupData) null);
+        return entityvillager;
     }
 
     @Override
     public void thunderHit(ServerLevel world, LightningBolt lightning) {
         if (world.getDifficulty() != Difficulty.PEACEFUL) {
-            LOGGER.info("Villager {} was struck by lightning {}.", this, lightning);
-            Witch witch = EntityType.WITCH.create(world);
-            if (witch != null) {
-                witch.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
-                witch.finalizeSpawn(world, world.getCurrentDifficultyAt(witch.blockPosition()), MobSpawnType.CONVERSION, null);
-                witch.setNoAi(this.isNoAi());
+            // Paper - Add EntityZapEvent; move log down, event can cancel
+            Witch entitywitch = (Witch) EntityType.WITCH.create(world);
+
+            if (entitywitch != null) {
+                // Paper start - Add EntityZapEvent
+                if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityZapEvent(this, lightning, entitywitch).isCancelled()) {
+                    return;
+                }
+                if (org.spigotmc.SpigotConfig.logVillagerDeaths) Villager.LOGGER.info("Villager {} was struck by lightning {}.", this, lightning); // Move down
+                // Paper end - Add EntityZapEvent
+
+                entitywitch.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
+                entitywitch.finalizeSpawn(world, world.getCurrentDifficultyAt(entitywitch.blockPosition()), MobSpawnType.CONVERSION, (SpawnGroupData) null);
+                entitywitch.setNoAi(this.isNoAi());
                 if (this.hasCustomName()) {
-                    witch.setCustomName(this.getCustomName());
-                    witch.setCustomNameVisible(this.isCustomNameVisible());
+                    entitywitch.setCustomName(this.getCustomName());
+                    entitywitch.setCustomNameVisible(this.isCustomNameVisible());
                 }
 
-                witch.setPersistenceRequired();
-                world.addFreshEntityWithPassengers(witch);
+                entitywitch.setPersistenceRequired();
+                // CraftBukkit start
+                if (CraftEventFactory.callEntityTransformEvent(this, entitywitch, EntityTransformEvent.TransformReason.LIGHTNING).isCancelled()) {
+                    return;
+                }
+                world.addFreshEntityWithPassengers(entitywitch, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING);
+                // CraftBukkit end
                 this.releaseAllPois();
-                this.discard();
+                this.discard(EntityRemoveEvent.Cause.TRANSFORMATION); // CraftBukkit - add Bukkit remove cause
             } else {
                 super.thunderHit(world, lightning);
             }
         } else {
             super.thunderHit(world, lightning);
         }
+
     }
 
     @Override
@@ -831,7 +911,8 @@
     @Override
     public boolean wantsToPickUp(ItemStack stack) {
         Item item = stack.getItem();
-        return (WANTED_ITEMS.contains(item) || this.getVillagerData().getProfession().requestedItems().contains(item)) && this.getInventory().canAddItem(stack);
+
+        return (Villager.WANTED_ITEMS.contains(item) || this.getVillagerData().getProfession().requestedItems().contains(item)) && this.getInventory().canAddItem(stack);
     }
 
     public boolean hasExcessFood() {
@@ -843,32 +924,49 @@
     }
 
     private int countFoodPointsInInventory() {
-        SimpleContainer simpleContainer = this.getInventory();
-        return FOOD_POINTS.entrySet().stream().mapToInt(item -> simpleContainer.countItem(item.getKey()) * item.getValue()).sum();
+        SimpleContainer inventorysubcontainer = this.getInventory();
+
+        return Villager.FOOD_POINTS.entrySet().stream().mapToInt((entry) -> {
+            return inventorysubcontainer.countItem((Item) entry.getKey()) * (Integer) entry.getValue();
+        }).sum();
     }
 
     public boolean hasFarmSeeds() {
-        return this.getInventory().hasAnyMatching(stack -> stack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS));
+        return this.getInventory().hasAnyMatching((itemstack) -> {
+            return itemstack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS);
+        });
     }
 
     @Override
     protected void updateTrades() {
-        VillagerData villagerData = this.getVillagerData();
-        Int2ObjectMap<VillagerTrades.ItemListing[]> int2ObjectMap2;
+        // Paper start - More vanilla friendly methods to update trades
+        updateTrades(TRADES_PER_LEVEL);
+    }
+
+    public boolean updateTrades(int amount) {
+        // Paper end - More vanilla friendly methods to update trades
+        VillagerData villagerdata = this.getVillagerData();
+        Int2ObjectMap int2objectmap;
+
         if (this.level().enabledFeatures().contains(FeatureFlags.TRADE_REBALANCE)) {
-            Int2ObjectMap<VillagerTrades.ItemListing[]> int2ObjectMap = VillagerTrades.EXPERIMENTAL_TRADES.get(villagerData.getProfession());
-            int2ObjectMap2 = int2ObjectMap != null ? int2ObjectMap : VillagerTrades.TRADES.get(villagerData.getProfession());
+            Int2ObjectMap<VillagerTrades.ItemListing[]> int2objectmap1 = (Int2ObjectMap) VillagerTrades.EXPERIMENTAL_TRADES.get(villagerdata.getProfession());
+
+            int2objectmap = int2objectmap1 != null ? int2objectmap1 : (Int2ObjectMap) VillagerTrades.TRADES.get(villagerdata.getProfession());
         } else {
-            int2ObjectMap2 = VillagerTrades.TRADES.get(villagerData.getProfession());
+            int2objectmap = (Int2ObjectMap) VillagerTrades.TRADES.get(villagerdata.getProfession());
         }
 
-        if (int2ObjectMap2 != null && !int2ObjectMap2.isEmpty()) {
-            VillagerTrades.ItemListing[] itemListings = int2ObjectMap2.get(villagerData.getLevel());
-            if (itemListings != null) {
-                MerchantOffers merchantOffers = this.getOffers();
-                this.addOffersFromItemListings(merchantOffers, itemListings, 2);
+        if (int2objectmap != null && !int2objectmap.isEmpty()) {
+            VillagerTrades.ItemListing[] avillagertrades_imerchantrecipeoption = (VillagerTrades.ItemListing[]) int2objectmap.get(villagerdata.getLevel());
+
+            if (avillagertrades_imerchantrecipeoption != null) {
+                MerchantOffers merchantrecipelist = this.getOffers();
+
+                this.addOffersFromItemListings(merchantrecipelist, avillagertrades_imerchantrecipeoption, amount); // Paper - More vanilla friendly methods to update trades
+                return true; // Paper - More vanilla friendly methods to update trades
             }
         }
+        return false; // Paper - More vanilla friendly methods to update trades
     }
 
     public void gossip(ServerLevel world, Villager villager, long time) {
@@ -881,25 +979,26 @@
     }
 
     private void maybeDecayGossip() {
-        long l = this.level().getGameTime();
+        long i = this.level().getGameTime();
+
         if (this.lastGossipDecayTime == 0L) {
-            this.lastGossipDecayTime = l;
-        } else if (l >= this.lastGossipDecayTime + 24000L) {
+            this.lastGossipDecayTime = i;
+        } else if (i >= this.lastGossipDecayTime + 24000L) {
             this.gossips.decay();
-            this.lastGossipDecayTime = l;
+            this.lastGossipDecayTime = i;
         }
     }
 
     public void spawnGolemIfNeeded(ServerLevel world, long time, int requiredCount) {
         if (this.wantsToSpawnGolem(time)) {
-            AABB aABB = this.getBoundingBox().inflate(10.0, 10.0, 10.0);
-            List<Villager> list = world.getEntitiesOfClass(Villager.class, aABB);
-            List<Villager> list2 = list.stream().filter(villager -> villager.wantsToSpawnGolem(time)).limit(5L).collect(Collectors.toList());
-            if (list2.size() >= requiredCount) {
-                if (!SpawnUtil.trySpawnMob(
-                        EntityType.IRON_GOLEM, MobSpawnType.MOB_SUMMONED, world, this.blockPosition(), 10, 8, 6, SpawnUtil.Strategy.LEGACY_IRON_GOLEM
-                    )
-                    .isEmpty()) {
+            AABB axisalignedbb = this.getBoundingBox().inflate(10.0D, 10.0D, 10.0D);
+            List<Villager> list = world.getEntitiesOfClass(Villager.class, axisalignedbb);
+            List<Villager> list1 = (List) list.stream().filter((entityvillager) -> {
+                return entityvillager.wantsToSpawnGolem(time);
+            }).limit(5L).collect(Collectors.toList());
+
+            if (list1.size() >= requiredCount) {
+                if (SpawnUtil.trySpawnMob(EntityType.IRON_GOLEM, MobSpawnType.MOB_SUMMONED, world, this.blockPosition(), 10, 8, 6, SpawnUtil.Strategy.LEGACY_IRON_GOLEM, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_DEFENSE, () -> {GolemSensor.golemDetected(this);}).isPresent()) { // CraftBukkit // Paper - Set Golem Last Seen to stop it from spawning another one
                     list.forEach(GolemSensor::golemDetected);
                 }
             }
@@ -907,7 +1006,7 @@
     }
 
     public boolean wantsToSpawnGolem(long time) {
-        return this.golemSpawnConditionsMet(this.level().getGameTime()) && !this.brain.hasMemoryValue(MemoryModuleType.GOLEM_DETECTED_RECENTLY);
+        return !this.golemSpawnConditionsMet(this.level().getGameTime()) ? false : !this.brain.hasMemoryValue(MemoryModuleType.GOLEM_DETECTED_RECENTLY);
     }
 
     @Override
@@ -922,6 +1021,7 @@
         } else if (interaction == ReputationEventType.VILLAGER_KILLED) {
             this.gossips.add(entity.getUUID(), GossipType.MAJOR_NEGATIVE, 25);
         }
+
     }
 
     @Override
@@ -943,7 +1043,7 @@
     }
 
     public void setGossips(Tag nbt) {
-        this.gossips.update(new Dynamic<>(NbtOps.INSTANCE, nbt));
+        this.gossips.update(new Dynamic(NbtOps.INSTANCE, nbt));
     }
 
     @Override
@@ -955,7 +1055,7 @@
     @Override
     public void startSleeping(BlockPos pos) {
         super.startSleeping(pos);
-        this.brain.setMemory(MemoryModuleType.LAST_SLEPT, this.level().getGameTime());
+        this.brain.setMemory(MemoryModuleType.LAST_SLEPT, this.level().getGameTime()); // CraftBukkit - decompile error
         this.brain.eraseMemory(MemoryModuleType.WALK_TARGET);
         this.brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
     }
@@ -963,11 +1063,12 @@
     @Override
     public void stopSleeping() {
         super.stopSleeping();
-        this.brain.setMemory(MemoryModuleType.LAST_WOKEN, this.level().getGameTime());
+        this.brain.setMemory(MemoryModuleType.LAST_WOKEN, this.level().getGameTime()); // CraftBukkit - decompile error
     }
 
     private boolean golemSpawnConditionsMet(long worldTime) {
         Optional<Long> optional = this.brain.getMemory(MemoryModuleType.LAST_SLEPT);
-        return optional.isPresent() && worldTime - optional.get() < 24000L;
+
+        return optional.isPresent() ? worldTime - (Long) optional.get() < 24000L : false;
     }
 }
