--- a/net/minecraft/world/entity/monster/ZombieVillager.java
+++ b/net/minecraft/world/entity/monster/ZombieVillager.java
@@ -3,6 +3,8 @@
 import com.mojang.logging.LogUtils;
 import com.mojang.serialization.DataResult;
 import com.mojang.serialization.Dynamic;
+import java.util.Iterator;
+import java.util.Objects;
 import java.util.UUID;
 import javax.annotation.Nullable;
 import net.minecraft.Util;
@@ -15,10 +17,6 @@
 import net.minecraft.network.syncher.EntityDataAccessor;
 import net.minecraft.network.syncher.EntityDataSerializers;
 import net.minecraft.network.syncher.SynchedEntityData;
-import net.minecraft.server.level.ServerLevel;
-import net.minecraft.server.level.ServerPlayer;
-import net.minecraft.sounds.SoundEvent;
-import net.minecraft.sounds.SoundEvents;
 import net.minecraft.world.DifficultyInstance;
 import net.minecraft.world.InteractionHand;
 import net.minecraft.world.InteractionResult;
@@ -49,12 +47,21 @@
 import net.minecraft.world.level.block.state.BlockState;
 import org.slf4j.Logger;
 
+// CraftBukkit start
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.sounds.SoundEvent;
+import net.minecraft.sounds.SoundEvents;
+import org.bukkit.event.entity.CreatureSpawnEvent;
+import org.bukkit.event.entity.EntityTransformEvent;
+// CraftBukkit end
+
 public class ZombieVillager extends Zombie implements VillagerDataHolder {
+
     private static final Logger LOGGER = LogUtils.getLogger();
     public static final EntityDataAccessor<Boolean> DATA_CONVERTING_ID = SynchedEntityData.defineId(ZombieVillager.class, EntityDataSerializers.BOOLEAN);
-    private static final EntityDataAccessor<VillagerData> DATA_VILLAGER_DATA = SynchedEntityData.defineId(
-        ZombieVillager.class, EntityDataSerializers.VILLAGER_DATA
-    );
+    private static final EntityDataAccessor<VillagerData> DATA_VILLAGER_DATA = SynchedEntityData.defineId(ZombieVillager.class, EntityDataSerializers.VILLAGER_DATA);
     private static final int VILLAGER_CONVERSION_WAIT_MIN = 3600;
     private static final int VILLAGER_CONVERSION_WAIT_MAX = 6000;
     private static final int MAX_SPECIAL_BLOCKS_COUNT = 14;
@@ -67,29 +74,34 @@
     @Nullable
     private MerchantOffers tradeOffers;
     private int villagerXp;
+    private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field
 
     public ZombieVillager(EntityType<? extends ZombieVillager> type, Level world) {
         super(type, world);
-        BuiltInRegistries.VILLAGER_PROFESSION
-            .getRandom(this.random)
-            .ifPresent(profession -> this.setVillagerData(this.getVillagerData().setProfession(profession.value())));
+        BuiltInRegistries.VILLAGER_PROFESSION.getRandom(this.random).ifPresent((holder_c) -> {
+            this.setVillagerData(this.getVillagerData().setProfession((VillagerProfession) holder_c.value()));
+        });
     }
 
     @Override
     protected void defineSynchedData(SynchedEntityData.Builder builder) {
         super.defineSynchedData(builder);
-        builder.define(DATA_CONVERTING_ID, false);
-        builder.define(DATA_VILLAGER_DATA, new VillagerData(VillagerType.PLAINS, VillagerProfession.NONE, 1));
+        builder.define(ZombieVillager.DATA_CONVERTING_ID, false);
+        builder.define(ZombieVillager.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));
+        DataResult<Tag> dataresult = VillagerData.CODEC.encodeStart(NbtOps.INSTANCE, this.getVillagerData()); // CraftBukkit - decompile error
+        Logger logger = ZombieVillager.LOGGER;
+
+        Objects.requireNonNull(logger);
+        dataresult.resultOrPartial(logger::error).ifPresent((nbtbase) -> {
+            nbt.put("VillagerData", nbtbase);
+        });
         if (this.tradeOffers != null) {
-            nbt.put(
-                "Offers", MerchantOffers.CODEC.encodeStart(this.registryAccess().createSerializationContext(NbtOps.INSTANCE), this.tradeOffers).getOrThrow()
-            );
+            nbt.put("Offers", (Tag) MerchantOffers.CODEC.encodeStart(this.registryAccess().createSerializationContext(NbtOps.INSTANCE), this.tradeOffers).getOrThrow());
         }
 
         if (this.gossips != null) {
@@ -108,15 +120,21 @@
     public void readAdditionalSaveData(CompoundTag nbt) {
         super.readAdditionalSaveData(nbt);
         if (nbt.contains("VillagerData", 10)) {
-            DataResult<VillagerData> dataResult = VillagerData.CODEC.parse(new Dynamic<>(NbtOps.INSTANCE, nbt.get("VillagerData")));
-            dataResult.resultOrPartial(LOGGER::error).ifPresent(this::setVillagerData);
+            DataResult<VillagerData> dataresult = VillagerData.CODEC.parse(new Dynamic(NbtOps.INSTANCE, nbt.get("VillagerData")));
+            Logger logger = ZombieVillager.LOGGER;
+
+            Objects.requireNonNull(logger);
+            dataresult.resultOrPartial(logger::error).ifPresent(this::setVillagerData);
         }
 
         if (nbt.contains("Offers")) {
-            MerchantOffers.CODEC
-                .parse(this.registryAccess().createSerializationContext(NbtOps.INSTANCE), nbt.get("Offers"))
-                .resultOrPartial(Util.prefix("Failed to load offers: ", LOGGER::warn))
-                .ifPresent(offerData -> this.tradeOffers = offerData);
+            DataResult<MerchantOffers> dataresult1 = MerchantOffers.CODEC.parse(this.registryAccess().createSerializationContext(NbtOps.INSTANCE), nbt.get("Offers")); // CraftBukkit - decompile error
+            Logger logger1 = ZombieVillager.LOGGER;
+
+            Objects.requireNonNull(logger1);
+            dataresult1.resultOrPartial(Util.prefix("Failed to load offers: ", logger1::warn)).ifPresent((merchantrecipelist) -> {
+                this.tradeOffers = merchantrecipelist;
+            });
         }
 
         if (nbt.contains("Gossips", 9)) {
@@ -130,27 +148,35 @@
         if (nbt.contains("Xp", 3)) {
             this.villagerXp = nbt.getInt("Xp");
         }
+
     }
 
     @Override
     public void tick() {
         if (!this.level().isClientSide && this.isAlive() && this.isConverting()) {
             int i = this.getConversionProgress();
+            // CraftBukkit start - Use wall time instead of ticks for villager conversion
+            int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
+            i *= elapsedTicks;
+            // CraftBukkit end
+
             this.villagerConversionTime -= i;
             if (this.villagerConversionTime <= 0) {
-                this.finishConversion((ServerLevel)this.level());
+                this.finishConversion((ServerLevel) this.level());
             }
         }
 
         super.tick();
+        this.lastTick = MinecraftServer.currentTick; // CraftBukkit
     }
 
     @Override
     public InteractionResult mobInteract(Player player, InteractionHand hand) {
-        ItemStack itemStack = player.getItemInHand(hand);
-        if (itemStack.is(Items.GOLDEN_APPLE)) {
+        ItemStack itemstack = player.getItemInHand(hand);
+
+        if (itemstack.is(Items.GOLDEN_APPLE)) {
             if (this.hasEffect(MobEffects.WEAKNESS)) {
-                itemStack.consume(1, player);
+                itemstack.consume(1, player);
                 if (!this.level().isClientSide) {
                     this.startConverting(player.getUUID(), this.random.nextInt(2401) + 3600);
                 }
@@ -175,92 +201,108 @@
     }
 
     public boolean isConverting() {
-        return this.getEntityData().get(DATA_CONVERTING_ID);
+        return (Boolean) this.getEntityData().get(ZombieVillager.DATA_CONVERTING_ID);
     }
 
     public void startConverting(@Nullable UUID uuid, int delay) {
+    // Paper start - missing entity behaviour api - converting without entity event
+        this.startConverting(uuid, delay, true);
+    }
+
+    public void startConverting(@Nullable UUID uuid, int delay, boolean broadcastEntityEvent) {
+    // Paper end - missing entity behaviour api - converting without entity event
         this.conversionStarter = uuid;
         this.villagerConversionTime = delay;
-        this.getEntityData().set(DATA_CONVERTING_ID, true);
-        this.removeEffect(MobEffects.WEAKNESS);
-        this.addEffect(new MobEffectInstance(MobEffects.DAMAGE_BOOST, delay, Math.min(this.level().getDifficulty().getId() - 1, 0)));
-        this.level().broadcastEntityEvent(this, (byte)16);
+        this.getEntityData().set(ZombieVillager.DATA_CONVERTING_ID, true);
+        // CraftBukkit start
+        this.removeEffect(MobEffects.WEAKNESS, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION);
+        this.addEffect(new MobEffectInstance(MobEffects.DAMAGE_BOOST, delay, Math.min(this.level().getDifficulty().getId() - 1, 0)), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION);
+        // CraftBukkit end
+        if (broadcastEntityEvent) this.level().broadcastEntityEvent(this, (byte) 16); // Paper - missing entity behaviour api - converting without entity event
     }
 
     @Override
     public void handleEntityEvent(byte status) {
         if (status == 16) {
             if (!this.isSilent()) {
-                this.level()
-                    .playLocalSound(
-                        this.getX(),
-                        this.getEyeY(),
-                        this.getZ(),
-                        SoundEvents.ZOMBIE_VILLAGER_CURE,
-                        this.getSoundSource(),
-                        1.0F + this.random.nextFloat(),
-                        this.random.nextFloat() * 0.7F + 0.3F,
-                        false
-                    );
+                this.level().playLocalSound(this.getX(), this.getEyeY(), this.getZ(), SoundEvents.ZOMBIE_VILLAGER_CURE, this.getSoundSource(), 1.0F + this.random.nextFloat(), this.random.nextFloat() * 0.7F + 0.3F, false);
             }
+
         } else {
             super.handleEntityEvent(status);
         }
     }
 
     private void finishConversion(ServerLevel world) {
-        Villager villager = this.convertTo(EntityType.VILLAGER, false);
-        if (villager != null) {
-            for (EquipmentSlot equipmentSlot : this.dropPreservedEquipment(
-                stack -> !EnchantmentHelper.has(stack, EnchantmentEffectComponents.PREVENT_ARMOR_CHANGE)
-            )) {
-                SlotAccess slotAccess = villager.getSlot(equipmentSlot.getIndex() + 300);
-                slotAccess.set(this.getItemBySlot(equipmentSlot));
+        // CraftBukkit start
+        Villager entityvillager = (Villager) this.convertTo(EntityType.VILLAGER, false, EntityTransformEvent.TransformReason.CURED, CreatureSpawnEvent.SpawnReason.CURED);
+        if (entityvillager == null) {
+            ((org.bukkit.entity.ZombieVillager) this.getBukkitEntity()).setConversionTime(-1); // SPIGOT-5208: End conversion to stop event spam
+            return;
+        }
+        // CraftBukkit end
+
+        if (entityvillager != null) {
+            this.forceDrops = true; // CraftBukkit
+            Iterator iterator = this.dropPreservedEquipment((itemstack) -> {
+                return !EnchantmentHelper.has(itemstack, EnchantmentEffectComponents.PREVENT_ARMOR_CHANGE);
+            }).iterator();
+            this.forceDrops = false; // CraftBukkit
+
+            while (iterator.hasNext()) {
+                EquipmentSlot enumitemslot = (EquipmentSlot) iterator.next();
+                SlotAccess slotaccess = entityvillager.getSlot(enumitemslot.getIndex() + 300);
+
+                slotaccess.set(this.getItemBySlot(enumitemslot));
             }
 
-            villager.setVillagerData(this.getVillagerData());
+            entityvillager.setVillagerData(this.getVillagerData());
             if (this.gossips != null) {
-                villager.setGossips(this.gossips);
+                entityvillager.setGossips(this.gossips);
             }
 
             if (this.tradeOffers != null) {
-                villager.setOffers(this.tradeOffers.copy());
+                entityvillager.setOffers(this.tradeOffers.copy());
             }
 
-            villager.setVillagerXp(this.villagerXp);
-            villager.finalizeSpawn(world, world.getCurrentDifficultyAt(villager.blockPosition()), MobSpawnType.CONVERSION, null);
-            villager.refreshBrain(world);
+            entityvillager.setVillagerXp(this.villagerXp);
+            entityvillager.finalizeSpawn(world, world.getCurrentDifficultyAt(entityvillager.blockPosition()), MobSpawnType.CONVERSION, (SpawnGroupData) null);
+            entityvillager.refreshBrain(world);
             if (this.conversionStarter != null) {
-                Player player = world.getPlayerByUUID(this.conversionStarter);
-                if (player instanceof ServerPlayer) {
-                    CriteriaTriggers.CURED_ZOMBIE_VILLAGER.trigger((ServerPlayer)player, this, villager);
-                    world.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, player, villager);
+            Player entityhuman = world.getGlobalPlayerByUUID(this.conversionStarter); // Paper - check global player list where appropriate
+
+                if (entityhuman instanceof ServerPlayer) {
+                    CriteriaTriggers.CURED_ZOMBIE_VILLAGER.trigger((ServerPlayer) entityhuman, this, entityvillager);
+                    world.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, entityhuman, entityvillager);
                 }
             }
 
-            villager.addEffect(new MobEffectInstance(MobEffects.CONFUSION, 200, 0));
+            entityvillager.addEffect(new MobEffectInstance(MobEffects.CONFUSION, 200, 0), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION); // CraftBukkit
             if (!this.isSilent()) {
-                world.levelEvent(null, 1027, this.blockPosition(), 0);
+                world.levelEvent((Player) null, 1027, this.blockPosition(), 0);
             }
+
         }
     }
 
     private int getConversionProgress() {
         int i = 1;
+
         if (this.random.nextFloat() < 0.01F) {
             int j = 0;
-            BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
+            BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
 
-            for (int k = (int)this.getX() - 4; k < (int)this.getX() + 4 && j < 14; k++) {
-                for (int l = (int)this.getY() - 4; l < (int)this.getY() + 4 && j < 14; l++) {
-                    for (int m = (int)this.getZ() - 4; m < (int)this.getZ() + 4 && j < 14; m++) {
-                        BlockState blockState = this.level().getBlockState(mutableBlockPos.set(k, l, m));
-                        if (blockState.is(Blocks.IRON_BARS) || blockState.getBlock() instanceof BedBlock) {
+            for (int k = (int) this.getX() - 4; k < (int) this.getX() + 4 && j < 14; ++k) {
+                for (int l = (int) this.getY() - 4; l < (int) this.getY() + 4 && j < 14; ++l) {
+                    for (int i1 = (int) this.getZ() - 4; i1 < (int) this.getZ() + 4 && j < 14; ++i1) {
+                        BlockState iblockdata = this.level().getBlockState(blockposition_mutableblockposition.set(k, l, i1));
+
+                        if (iblockdata.is(Blocks.IRON_BARS) || iblockdata.getBlock() instanceof BedBlock) {
                             if (this.random.nextFloat() < 0.3F) {
-                                i++;
+                                ++i;
                             }
 
-                            j++;
+                            ++j;
                         }
                     }
                 }
@@ -272,9 +314,7 @@
 
     @Override
     public float getVoicePitch() {
-        return this.isBaby()
-            ? (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 2.0F
-            : (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F;
+        return this.isBaby() ? (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 2.0F : (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F;
     }
 
     @Override
@@ -319,17 +359,18 @@
 
     @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.tradeOffers = null;
         }
 
-        this.entityData.set(DATA_VILLAGER_DATA, villagerData);
+        this.entityData.set(ZombieVillager.DATA_VILLAGER_DATA, villagerData);
     }
 
     @Override
     public VillagerData getVillagerData() {
-        return this.entityData.get(DATA_VILLAGER_DATA);
+        return (VillagerData) this.entityData.get(ZombieVillager.DATA_VILLAGER_DATA);
     }
 
     public int getVillagerXp() {
