aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Zombie.java.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Zombie.java.patch')
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Zombie.java.patch718
1 files changed, 718 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Zombie.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Zombie.java.patch
new file mode 100644
index 0000000000..d2568e3ef0
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Zombie.java.patch
@@ -0,0 +1,718 @@
+--- a/net/minecraft/world/entity/monster/Zombie.java
++++ b/net/minecraft/world/entity/monster/Zombie.java
+@@ -6,17 +6,6 @@
+ import java.util.UUID;
+ import java.util.function.Predicate;
+ import javax.annotation.Nullable;
+-import net.minecraft.core.BlockPos;
+-import net.minecraft.nbt.CompoundTag;
+-import net.minecraft.nbt.NbtOps;
+-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.sounds.SoundEvent;
+-import net.minecraft.sounds.SoundEvents;
+-import net.minecraft.sounds.SoundSource;
+-import net.minecraft.tags.FluidTags;
+ import net.minecraft.util.Mth;
+ import net.minecraft.util.RandomSource;
+ import net.minecraft.world.Difficulty;
+@@ -24,15 +13,15 @@
+ import net.minecraft.world.damagesource.DamageSource;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
++import net.minecraft.world.entity.EntityPose;
+ import net.minecraft.world.entity.EntitySelector;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
++import net.minecraft.world.entity.EnumMonsterType;
+ import net.minecraft.world.entity.EquipmentSlot;
++import net.minecraft.world.entity.GroupDataEntity;
+ import net.minecraft.world.entity.LivingEntity;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.MobType;
+ import net.minecraft.world.entity.PathfinderMob;
+-import net.minecraft.world.entity.Pose;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.SpawnPlacements;
+ import net.minecraft.world.entity.ai.attributes.AttributeInstance;
+ import net.minecraft.world.entity.ai.attributes.AttributeModifier;
+@@ -63,31 +52,53 @@
+ import net.minecraft.world.level.NaturalSpawner;
+ import net.minecraft.world.level.ServerLevelAccessor;
+ import net.minecraft.world.level.block.Blocks;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import org.joml.Vector3f;
++import net.minecraft.core.BlockPos;
++import net.minecraft.nbt.CompoundTag;
++import net.minecraft.nbt.NbtOps;
++import net.minecraft.nbt.Tag;
++import net.minecraft.network.syncher.EntityDataAccessor;
++import net.minecraft.network.syncher.EntityDataSerializers;
++import net.minecraft.network.syncher.SynchedEntityData;
++// CraftBukkit start
++import net.minecraft.server.MinecraftServer;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.sounds.SoundEvent;
++import net.minecraft.sounds.SoundEvents;
++import net.minecraft.sounds.SoundSource;
++import net.minecraft.tags.FluidTags;
++import org.bukkit.event.entity.CreatureSpawnEvent;
++import org.bukkit.event.entity.EntityCombustByEntityEvent;
++import org.bukkit.event.entity.EntityTargetEvent;
++import org.bukkit.event.entity.EntityTransformEvent;
++// CraftBukkit end
+
+ public class Zombie extends Monster {
++
+ private static final UUID SPEED_MODIFIER_BABY_UUID = UUID.fromString("B9766B59-9566-4402-BC1F-2EE2A276D836");
+- private static final AttributeModifier SPEED_MODIFIER_BABY = new AttributeModifier(
+- SPEED_MODIFIER_BABY_UUID, "Baby speed boost", 0.5, AttributeModifier.Operation.MULTIPLY_BASE
+- );
++ private static final AttributeModifier SPEED_MODIFIER_BABY = new AttributeModifier(Zombie.SPEED_MODIFIER_BABY_UUID, "Baby speed boost", 0.5D, AttributeModifier.Operation.MULTIPLY_BASE);
+ private static final EntityDataAccessor<Boolean> DATA_BABY_ID = SynchedEntityData.defineId(Zombie.class, EntityDataSerializers.BOOLEAN);
+ private static final EntityDataAccessor<Integer> DATA_SPECIAL_TYPE_ID = SynchedEntityData.defineId(Zombie.class, EntityDataSerializers.INT);
+- private static final EntityDataAccessor<Boolean> DATA_DROWNED_CONVERSION_ID = SynchedEntityData.defineId(Zombie.class, EntityDataSerializers.BOOLEAN);
++ public static final EntityDataAccessor<Boolean> DATA_DROWNED_CONVERSION_ID = SynchedEntityData.defineId(Zombie.class, EntityDataSerializers.BOOLEAN);
+ public static final float ZOMBIE_LEADER_CHANCE = 0.05F;
+ public static final int REINFORCEMENT_ATTEMPTS = 50;
+ public static final int REINFORCEMENT_RANGE_MAX = 40;
+ public static final int REINFORCEMENT_RANGE_MIN = 7;
+ protected static final float BABY_EYE_HEIGHT_ADJUSTMENT = 0.81F;
+ private static final float BREAK_DOOR_CHANCE = 0.1F;
+- private static final Predicate<Difficulty> DOOR_BREAKING_PREDICATE = difficulty -> difficulty == Difficulty.HARD;
+- private final BreakDoorGoal breakDoorGoal = new BreakDoorGoal(this, DOOR_BREAKING_PREDICATE);
++ private static final Predicate<Difficulty> DOOR_BREAKING_PREDICATE = (enumdifficulty) -> {
++ return enumdifficulty == Difficulty.HARD;
++ };
++ private final BreakDoorGoal breakDoorGoal;
+ private boolean canBreakDoors;
+ private int inWaterTime;
+- private int conversionTime;
++ public int conversionTime;
++ private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field
+
+ public Zombie(EntityType<? extends Zombie> entityType, Level level) {
+ super(entityType, level);
++ this.breakDoorGoal = new BreakDoorGoal(this, Zombie.DOOR_BREAKING_PREDICATE);
+ }
+
+ public Zombie(Level level) {
+@@ -96,17 +107,17 @@
+
+ @Override
+ protected void registerGoals() {
+- this.goalSelector.addGoal(4, new Zombie.ZombieAttackTurtleEggGoal(this, 1.0, 3));
++ this.goalSelector.addGoal(4, new Zombie.ZombieAttackTurtleEggGoal(this, 1.0D, 3));
+ this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F));
+ this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
+ this.addBehaviourGoals();
+ }
+
+ protected void addBehaviourGoals() {
+- this.goalSelector.addGoal(2, new ZombieAttackGoal(this, 1.0, false));
+- this.goalSelector.addGoal(6, new MoveThroughVillageGoal(this, 1.0, true, 4, this::canBreakDoors));
+- this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0));
+- this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers(ZombifiedPiglin.class));
++ this.goalSelector.addGoal(2, new ZombieAttackGoal(this, 1.0D, false));
++ this.goalSelector.addGoal(6, new MoveThroughVillageGoal(this, 1.0D, true, 4, this::canBreakDoors));
++ this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D));
++ this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers(ZombifiedPiglin.class));
+ this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false));
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
+@@ -114,24 +125,19 @@
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes()
+- .add(Attributes.FOLLOW_RANGE, 35.0)
+- .add(Attributes.MOVEMENT_SPEED, 0.23F)
+- .add(Attributes.ATTACK_DAMAGE, 3.0)
+- .add(Attributes.ARMOR, 2.0)
+- .add(Attributes.SPAWN_REINFORCEMENTS_CHANCE);
++ return Monster.createMonsterAttributes().add(Attributes.FOLLOW_RANGE, 35.0D).add(Attributes.MOVEMENT_SPEED, 0.23000000417232513D).add(Attributes.ATTACK_DAMAGE, 3.0D).add(Attributes.ARMOR, 2.0D).add(Attributes.SPAWN_REINFORCEMENTS_CHANCE);
+ }
+
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.getEntityData().define(DATA_BABY_ID, false);
+- this.getEntityData().define(DATA_SPECIAL_TYPE_ID, 0);
+- this.getEntityData().define(DATA_DROWNED_CONVERSION_ID, false);
++ this.getEntityData().define(Zombie.DATA_BABY_ID, false);
++ this.getEntityData().define(Zombie.DATA_SPECIAL_TYPE_ID, 0);
++ this.getEntityData().define(Zombie.DATA_DROWNED_CONVERSION_ID, false);
+ }
+
+ public boolean isUnderWaterConverting() {
+- return this.getEntityData().get(DATA_DROWNED_CONVERSION_ID);
++ return (Boolean) this.getEntityData().get(Zombie.DATA_DROWNED_CONVERSION_ID);
+ }
+
+ public boolean canBreakDoors() {
+@@ -142,7 +148,7 @@
+ if (this.supportsBreakDoorGoal() && GoalUtils.hasGroundPathNavigation(this)) {
+ if (this.canBreakDoors != canBreakDoors) {
+ this.canBreakDoors = canBreakDoors;
+- ((GroundPathNavigation)this.getNavigation()).setCanOpenDoors(canBreakDoors);
++ ((GroundPathNavigation) this.getNavigation()).setCanOpenDoors(canBreakDoors);
+ if (canBreakDoors) {
+ this.goalSelector.addGoal(1, this.breakDoorGoal);
+ } else {
+@@ -153,6 +159,7 @@
+ this.goalSelector.removeGoal(this.breakDoorGoal);
+ this.canBreakDoors = false;
+ }
++
+ }
+
+ protected boolean supportsBreakDoorGoal() {
+@@ -161,13 +168,13 @@
+
+ @Override
+ public boolean isBaby() {
+- return this.getEntityData().get(DATA_BABY_ID);
++ return (Boolean) this.getEntityData().get(Zombie.DATA_BABY_ID);
+ }
+
+ @Override
+ public int getExperienceReward() {
+ if (this.isBaby()) {
+- this.xpReward = (int)((double)this.xpReward * 2.5);
++ this.xpReward = (int) ((double) this.xpReward * 2.5D);
+ }
+
+ return super.getExperienceReward();
+@@ -175,19 +182,21 @@
+
+ @Override
+ public void setBaby(boolean childZombie) {
+- this.getEntityData().set(DATA_BABY_ID, childZombie);
++ this.getEntityData().set(Zombie.DATA_BABY_ID, childZombie);
+ if (this.level() != null && !this.level().isClientSide) {
+- AttributeInstance attribute = this.getAttribute(Attributes.MOVEMENT_SPEED);
+- attribute.removeModifier(SPEED_MODIFIER_BABY.getId());
++ AttributeInstance attributemodifiable = this.getAttribute(Attributes.MOVEMENT_SPEED);
++
++ attributemodifiable.removeModifier(Zombie.SPEED_MODIFIER_BABY.getId());
+ if (childZombie) {
+- attribute.addTransientModifier(SPEED_MODIFIER_BABY);
++ attributemodifiable.addTransientModifier(Zombie.SPEED_MODIFIER_BABY);
+ }
+ }
++
+ }
+
+ @Override
+ public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
+- if (DATA_BABY_ID.equals(key)) {
++ if (Zombie.DATA_BABY_ID.equals(key)) {
+ this.refreshDimensions();
+ }
+
+@@ -202,13 +211,16 @@
+ public void tick() {
+ if (!this.level().isClientSide && this.isAlive() && !this.isNoAi()) {
+ if (this.isUnderWaterConverting()) {
+- this.conversionTime--;
++ // CraftBukkit start - Use wall time instead of ticks for conversion
++ int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
++ this.conversionTime -= elapsedTicks;
++ // CraftBukkit end
+ if (this.conversionTime < 0) {
+ this.doUnderWaterConversion();
+ }
+ } else if (this.convertsInWater()) {
+ if (this.isEyeInFluid(FluidTags.WATER)) {
+- this.inWaterTime++;
++ ++this.inWaterTime;
+ if (this.inWaterTime >= 600) {
+ this.startUnderWaterConversion(300);
+ }
+@@ -219,18 +231,21 @@
+ }
+
+ super.tick();
++ this.lastTick = MinecraftServer.currentTick; // CraftBukkit
+ }
+
+ @Override
+ public void aiStep() {
+ if (this.isAlive()) {
+ boolean flag = this.isSunSensitive() && this.isSunBurnTick();
++
+ if (flag) {
+- ItemStack itemBySlot = this.getItemBySlot(EquipmentSlot.HEAD);
+- if (!itemBySlot.isEmpty()) {
+- if (itemBySlot.isDamageableItem()) {
+- itemBySlot.setDamageValue(itemBySlot.getDamageValue() + this.random.nextInt(2));
+- if (itemBySlot.getDamageValue() >= itemBySlot.getMaxDamage()) {
++ ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD);
++
++ if (!itemstack.isEmpty()) {
++ if (itemstack.isDamageableItem()) {
++ itemstack.setDamageValue(itemstack.getDamageValue() + this.random.nextInt(2));
++ if (itemstack.getDamageValue() >= itemstack.getMaxDamage()) {
+ this.broadcastBreakEvent(EquipmentSlot.HEAD);
+ this.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY);
+ }
+@@ -248,24 +263,32 @@
+ super.aiStep();
+ }
+
+- private void startUnderWaterConversion(int conversionTime) {
++ public void startUnderWaterConversion(int conversionTime) {
++ this.lastTick = MinecraftServer.currentTick; // CraftBukkit
+ this.conversionTime = conversionTime;
+- this.getEntityData().set(DATA_DROWNED_CONVERSION_ID, true);
++ this.getEntityData().set(Zombie.DATA_DROWNED_CONVERSION_ID, true);
+ }
+
+ protected void doUnderWaterConversion() {
+ this.convertToZombieType(EntityType.DROWNED);
+ if (!this.isSilent()) {
+- this.level().levelEvent(null, 1040, this.blockPosition(), 0);
++ this.level().levelEvent((Player) null, 1040, this.blockPosition(), 0);
+ }
++
+ }
+
+ protected void convertToZombieType(EntityType<? extends Zombie> entityType) {
+- Zombie zombie = this.convertTo(entityType, true);
+- if (zombie != null) {
+- zombie.handleAttributes(zombie.level().getCurrentDifficultyAt(zombie.blockPosition()).getSpecialMultiplier());
+- zombie.setCanBreakDoors(zombie.supportsBreakDoorGoal() && this.canBreakDoors());
++ Zombie entityzombie = (Zombie) this.convertTo(entityType, true, EntityTransformEvent.TransformReason.DROWNED, CreatureSpawnEvent.SpawnReason.DROWNED);
++
++ if (entityzombie != null) {
++ entityzombie.handleAttributes(entityzombie.level().getCurrentDifficultyAt(entityzombie.blockPosition()).getSpecialMultiplier());
++ entityzombie.setCanBreakDoors(entityzombie.supportsBreakDoorGoal() && this.canBreakDoors());
++ // CraftBukkit start - SPIGOT-5208: End conversion to stop event spam
++ } else {
++ ((org.bukkit.entity.Zombie) getBukkitEntity()).setConversionTime(-1);
++ // CraftBukkit end
+ }
++
+ }
+
+ protected boolean isSunSensitive() {
+@@ -279,44 +302,35 @@
+ } else if (!(this.level() instanceof ServerLevel)) {
+ return false;
+ } else {
+- ServerLevel serverLevel = (ServerLevel)this.level();
+- LivingEntity target = this.getTarget();
+- if (target == null && source.getEntity() instanceof LivingEntity) {
+- target = (LivingEntity)source.getEntity();
++ ServerLevel worldserver = (ServerLevel) this.level();
++ LivingEntity entityliving = this.getTarget();
++
++ if (entityliving == null && source.getEntity() instanceof LivingEntity) {
++ entityliving = (LivingEntity) source.getEntity();
+ }
+
+- if (target != null
+- && this.level().getDifficulty() == Difficulty.HARD
+- && (double)this.random.nextFloat() < this.getAttributeValue(Attributes.SPAWN_REINFORCEMENTS_CHANCE)
+- && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
+- int floor = Mth.floor(this.getX());
+- int floor1 = Mth.floor(this.getY());
+- int floor2 = Mth.floor(this.getZ());
+- Zombie zombie = new Zombie(this.level());
++ if (entityliving != null && this.level().getDifficulty() == Difficulty.HARD && (double) this.random.nextFloat() < this.getAttributeValue(Attributes.SPAWN_REINFORCEMENTS_CHANCE) && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
++ int i = Mth.floor(this.getX());
++ int j = Mth.floor(this.getY());
++ int k = Mth.floor(this.getZ());
++ Zombie entityzombie = new Zombie(this.level());
+
+- for (int i = 0; i < 50; i++) {
+- int i1 = floor + Mth.nextInt(this.random, 7, 40) * Mth.nextInt(this.random, -1, 1);
+- int i2 = floor1 + Mth.nextInt(this.random, 7, 40) * Mth.nextInt(this.random, -1, 1);
+- int i3 = floor2 + Mth.nextInt(this.random, 7, 40) * Mth.nextInt(this.random, -1, 1);
+- BlockPos blockPos = new BlockPos(i1, i2, i3);
+- EntityType<?> type = zombie.getType();
+- SpawnPlacements.Type placementType = SpawnPlacements.getPlacementType(type);
+- if (NaturalSpawner.isSpawnPositionOk(placementType, this.level(), blockPos, type)
+- && SpawnPlacements.checkSpawnRules(type, serverLevel, MobSpawnType.REINFORCEMENT, blockPos, this.level().random)) {
+- zombie.setPos((double)i1, (double)i2, (double)i3);
+- if (!this.level().hasNearbyAlivePlayer((double)i1, (double)i2, (double)i3, 7.0)
+- && this.level().isUnobstructed(zombie)
+- && this.level().noCollision(zombie)
+- && !this.level().containsAnyLiquid(zombie.getBoundingBox())) {
+- zombie.setTarget(target);
+- zombie.finalizeSpawn(
+- serverLevel, this.level().getCurrentDifficultyAt(zombie.blockPosition()), MobSpawnType.REINFORCEMENT, null, null
+- );
+- serverLevel.addFreshEntityWithPassengers(zombie);
+- this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE)
+- .addPermanentModifier(new AttributeModifier("Zombie reinforcement caller charge", -0.05F, AttributeModifier.Operation.ADDITION));
+- zombie.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE)
+- .addPermanentModifier(new AttributeModifier("Zombie reinforcement callee charge", -0.05F, AttributeModifier.Operation.ADDITION));
++ for (int l = 0; l < 50; ++l) {
++ int i1 = i + Mth.nextInt(this.random, 7, 40) * Mth.nextInt(this.random, -1, 1);
++ int j1 = j + Mth.nextInt(this.random, 7, 40) * Mth.nextInt(this.random, -1, 1);
++ int k1 = k + Mth.nextInt(this.random, 7, 40) * Mth.nextInt(this.random, -1, 1);
++ BlockPos blockposition = new BlockPos(i1, j1, k1);
++ EntityType<?> entitytypes = entityzombie.getType();
++ SpawnPlacements.Surface entitypositiontypes_surface = SpawnPlacements.getPlacementType(entitytypes);
++
++ if (NaturalSpawner.isSpawnPositionOk(entitypositiontypes_surface, this.level(), blockposition, entitytypes) && SpawnPlacements.checkSpawnRules(entitytypes, worldserver, EnumMobSpawn.REINFORCEMENT, blockposition, this.level().random)) {
++ entityzombie.setPos((double) i1, (double) j1, (double) k1);
++ if (!this.level().hasNearbyAlivePlayer((double) i1, (double) j1, (double) k1, 7.0D) && this.level().isUnobstructed(entityzombie) && this.level().noCollision((Entity) entityzombie) && !this.level().containsAnyLiquid(entityzombie.getBoundingBox())) {
++ entityzombie.setTarget(entityliving, EntityTargetEvent.TargetReason.REINFORCEMENT_TARGET, true); // CraftBukkit
++ entityzombie.finalizeSpawn(worldserver, this.level().getCurrentDifficultyAt(entityzombie.blockPosition()), EnumMobSpawn.REINFORCEMENT, (GroupDataEntity) null, (CompoundTag) null);
++ worldserver.addFreshEntityWithPassengers(entityzombie, CreatureSpawnEvent.SpawnReason.REINFORCEMENTS); // CraftBukkit
++ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).addPermanentModifier(new AttributeModifier("Zombie reinforcement caller charge", -0.05000000074505806D, AttributeModifier.Operation.ADDITION));
++ entityzombie.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).addPermanentModifier(new AttributeModifier("Zombie reinforcement callee charge", -0.05000000074505806D, AttributeModifier.Operation.ADDITION));
+ break;
+ }
+ }
+@@ -330,10 +344,19 @@
+ @Override
+ public boolean doHurtTarget(Entity entity) {
+ boolean flag = super.doHurtTarget(entity);
++
+ if (flag) {
+- float effectiveDifficulty = this.level().getCurrentDifficultyAt(this.blockPosition()).getEffectiveDifficulty();
+- if (this.getMainHandItem().isEmpty() && this.isOnFire() && this.random.nextFloat() < effectiveDifficulty * 0.3F) {
+- entity.setSecondsOnFire(2 * (int)effectiveDifficulty);
++ float f = this.level().getCurrentDifficultyAt(this.blockPosition()).getEffectiveDifficulty();
++
++ if (this.getMainHandItem().isEmpty() && this.isOnFire() && this.random.nextFloat() < f * 0.3F) {
++ // CraftBukkit start
++ EntityCombustByEntityEvent event = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 2 * (int) f); // PAIL: fixme
++ this.level().getCraftServer().getPluginManager().callEvent(event);
++
++ if (!event.isCancelled()) {
++ entity.setSecondsOnFire(event.getDuration(), false);
++ }
++ // CraftBukkit end
+ }
+ }
+
+@@ -360,26 +383,28 @@
+ }
+
+ @Override
+- protected void playStepSound(BlockPos pos, BlockState block) {
++ protected void playStepSound(BlockPos pos, IBlockData block) {
+ this.playSound(this.getStepSound(), 0.15F, 1.0F);
+ }
+
+ @Override
+- public MobType getMobType() {
+- return MobType.UNDEAD;
++ public EnumMonsterType getMobType() {
++ return EnumMonsterType.UNDEAD;
+ }
+
+ @Override
+ protected void populateDefaultEquipmentSlots(RandomSource random, DifficultyInstance difficulty) {
+ super.populateDefaultEquipmentSlots(random, difficulty);
+ if (random.nextFloat() < (this.level().getDifficulty() == Difficulty.HARD ? 0.05F : 0.01F)) {
+- int randomInt = random.nextInt(3);
+- if (randomInt == 0) {
++ int i = random.nextInt(3);
++
++ if (i == 0) {
+ this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.IRON_SWORD));
+ } else {
+ this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.IRON_SHOVEL));
+ }
+ }
++
+ }
+
+ @Override
+@@ -400,107 +425,124 @@
+ if (compound.contains("DrownedConversionTime", 99) && compound.getInt("DrownedConversionTime") > -1) {
+ this.startUnderWaterConversion(compound.getInt("DrownedConversionTime"));
+ }
++
+ }
+
+ @Override
+ public boolean killedEntity(ServerLevel level, LivingEntity entity) {
+ boolean flag = super.killedEntity(level, entity);
+- if ((level.getDifficulty() == Difficulty.NORMAL || level.getDifficulty() == Difficulty.HARD) && entity instanceof Villager villager) {
++
++ if ((level.getDifficulty() == Difficulty.NORMAL || level.getDifficulty() == Difficulty.HARD) && entity instanceof Villager) {
++ Villager entityvillager = (Villager) entity;
++
+ if (level.getDifficulty() != Difficulty.HARD && this.random.nextBoolean()) {
+ return flag;
+ }
++ // CraftBukkit start
++ flag = zombifyVillager(level, entityvillager, this.blockPosition(), this.isSilent(), CreatureSpawnEvent.SpawnReason.INFECTION) == null;
++ }
+
+- ZombieVillager zombieVillager = villager.convertTo(EntityType.ZOMBIE_VILLAGER, false);
+- if (zombieVillager != null) {
+- zombieVillager.finalizeSpawn(
+- level, level.getCurrentDifficultyAt(zombieVillager.blockPosition()), MobSpawnType.CONVERSION, new Zombie.ZombieGroupData(false, true), null
+- );
+- zombieVillager.setVillagerData(villager.getVillagerData());
+- zombieVillager.setGossips(villager.getGossips().store(NbtOps.INSTANCE));
+- zombieVillager.setTradeOffers(villager.getOffers().createTag());
+- zombieVillager.setVillagerXp(villager.getVillagerXp());
+- if (!this.isSilent()) {
+- level.levelEvent(null, 1026, this.blockPosition(), 0);
++ return flag;
++ }
++
++ public static ZombieVillager zombifyVillager(ServerLevel worldserver, Villager entityvillager, net.minecraft.core.BlockPos blockPosition, boolean silent, CreatureSpawnEvent.SpawnReason spawnReason) {
++ {
++ ZombieVillager entityzombievillager = (ZombieVillager) entityvillager.convertTo(EntityType.ZOMBIE_VILLAGER, false, EntityTransformEvent.TransformReason.INFECTION, spawnReason);
++ // CraftBukkit end
++
++ if (entityzombievillager != null) {
++ entityzombievillager.finalizeSpawn(worldserver, worldserver.getCurrentDifficultyAt(entityzombievillager.blockPosition()), EnumMobSpawn.CONVERSION, new Zombie.ZombieGroupData(false, true), (CompoundTag) null);
++ entityzombievillager.setVillagerData(entityvillager.getVillagerData());
++ entityzombievillager.setGossips((Tag) entityvillager.getGossips().store(NbtOps.INSTANCE));
++ entityzombievillager.setTradeOffers(entityvillager.getOffers().createTag());
++ entityzombievillager.setVillagerXp(entityvillager.getVillagerXp());
++ // CraftBukkit start
++ if (!silent) {
++ worldserver.levelEvent((Player) null, 1026, blockPosition, 0);
+ }
+
+- flag = false;
++ // flag = false;
+ }
+- }
+
+- return flag;
++ return entityzombievillager;
++ }
++ // CraftBukkit end
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return this.isBaby() ? 0.93F : 1.74F;
+ }
+
+ @Override
+ public boolean canHoldItem(ItemStack stack) {
+- return (!stack.is(Items.EGG) || !this.isBaby() || !this.isPassenger()) && super.canHoldItem(stack);
++ return stack.is(Items.EGG) && this.isBaby() && this.isPassenger() ? false : super.canHoldItem(stack);
+ }
+
+ @Override
+ public boolean wantsToPickUp(ItemStack stack) {
+- return !stack.is(Items.GLOW_INK_SAC) && super.wantsToPickUp(stack);
++ return stack.is(Items.GLOW_INK_SAC) ? false : super.wantsToPickUp(stack);
+ }
+
+ @Nullable
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
+- RandomSource random = level.getRandom();
+- SpawnGroupData var11 = super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
+- float specialMultiplier = difficulty.getSpecialMultiplier();
+- this.setCanPickUpLoot(random.nextFloat() < 0.55F * specialMultiplier);
+- if (var11 == null) {
+- var11 = new Zombie.ZombieGroupData(getSpawnAsBabyOdds(random), true);
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
++ RandomSource randomsource = level.getRandom();
++ Object object = super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
++ float f = difficulty.getSpecialMultiplier();
++
++ this.setCanPickUpLoot(randomsource.nextFloat() < 0.55F * f);
++ if (object == null) {
++ object = new Zombie.ZombieGroupData(getSpawnAsBabyOdds(randomsource), true);
+ }
+
+- if (var11 instanceof Zombie.ZombieGroupData zombieGroupData) {
+- if (zombieGroupData.isBaby) {
++ if (object instanceof Zombie.ZombieGroupData) {
++ Zombie.ZombieGroupData entityzombie_groupdatazombie = (Zombie.ZombieGroupData) object;
++
++ if (entityzombie_groupdatazombie.isBaby) {
+ this.setBaby(true);
+- if (zombieGroupData.canSpawnJockey) {
+- if ((double)random.nextFloat() < 0.05) {
+- List<Chicken> entitiesOfClass = level.getEntitiesOfClass(
+- Chicken.class, this.getBoundingBox().inflate(5.0, 3.0, 5.0), EntitySelector.ENTITY_NOT_BEING_RIDDEN
+- );
+- if (!entitiesOfClass.isEmpty()) {
+- Chicken chicken = entitiesOfClass.get(0);
+- chicken.setChickenJockey(true);
+- this.startRiding(chicken);
++ if (entityzombie_groupdatazombie.canSpawnJockey) {
++ if ((double) randomsource.nextFloat() < 0.05D) {
++ List<Chicken> list = level.getEntitiesOfClass(Chicken.class, this.getBoundingBox().inflate(5.0D, 3.0D, 5.0D), EntitySelector.ENTITY_NOT_BEING_RIDDEN);
++
++ if (!list.isEmpty()) {
++ Chicken entitychicken = (Chicken) list.get(0);
++
++ entitychicken.setChickenJockey(true);
++ this.startRiding(entitychicken);
+ }
+- } else if ((double)random.nextFloat() < 0.05) {
+- Chicken chicken1 = EntityType.CHICKEN.create(this.level());
+- if (chicken1 != null) {
+- chicken1.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F);
+- chicken1.finalizeSpawn(level, difficulty, MobSpawnType.JOCKEY, null, null);
+- chicken1.setChickenJockey(true);
+- this.startRiding(chicken1);
+- level.addFreshEntity(chicken1);
++ } else if ((double) randomsource.nextFloat() < 0.05D) {
++ Chicken entitychicken1 = (Chicken) EntityType.CHICKEN.create(this.level());
++
++ if (entitychicken1 != null) {
++ entitychicken1.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F);
++ entitychicken1.finalizeSpawn(level, difficulty, EnumMobSpawn.JOCKEY, (GroupDataEntity) null, (CompoundTag) null);
++ entitychicken1.setChickenJockey(true);
++ this.startRiding(entitychicken1);
++ level.addFreshEntity(entitychicken1, CreatureSpawnEvent.SpawnReason.MOUNT); // CraftBukkit
+ }
+ }
+ }
+ }
+
+- this.setCanBreakDoors(this.supportsBreakDoorGoal() && random.nextFloat() < specialMultiplier * 0.1F);
+- this.populateDefaultEquipmentSlots(random, difficulty);
+- this.populateDefaultEquipmentEnchantments(random, difficulty);
++ this.setCanBreakDoors(this.supportsBreakDoorGoal() && randomsource.nextFloat() < f * 0.1F);
++ this.populateDefaultEquipmentSlots(randomsource, difficulty);
++ this.populateDefaultEquipmentEnchantments(randomsource, difficulty);
+ }
+
+ if (this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) {
+- LocalDate localDate = LocalDate.now();
+- int i = localDate.get(ChronoField.DAY_OF_MONTH);
+- int i1 = localDate.get(ChronoField.MONTH_OF_YEAR);
+- if (i1 == 10 && i == 31 && random.nextFloat() < 0.25F) {
+- this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(random.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN));
++ LocalDate localdate = LocalDate.now();
++ int i = localdate.get(ChronoField.DAY_OF_MONTH);
++ int j = localdate.get(ChronoField.MONTH_OF_YEAR);
++
++ if (j == 10 && i == 31 && randomsource.nextFloat() < 0.25F) {
++ this.setItemSlot(EquipmentSlot.HEAD, new ItemStack(randomsource.nextFloat() < 0.1F ? Blocks.JACK_O_LANTERN : Blocks.CARVED_PUMPKIN));
+ this.armorDropChances[EquipmentSlot.HEAD.getIndex()] = 0.0F;
+ }
+ }
+
+- this.handleAttributes(specialMultiplier);
+- return (SpawnGroupData)var11;
++ this.handleAttributes(f);
++ return (GroupDataEntity) object;
+ }
+
+ public static boolean getSpawnAsBabyOdds(RandomSource random) {
+@@ -509,32 +551,28 @@
+
+ protected void handleAttributes(float difficulty) {
+ this.randomizeReinforcementsChance();
+- this.getAttribute(Attributes.KNOCKBACK_RESISTANCE)
+- .addPermanentModifier(new AttributeModifier("Random spawn bonus", this.random.nextDouble() * 0.05F, AttributeModifier.Operation.ADDITION));
+- double d = this.random.nextDouble() * 1.5 * (double)difficulty;
+- if (d > 1.0) {
+- this.getAttribute(Attributes.FOLLOW_RANGE)
+- .addPermanentModifier(new AttributeModifier("Random zombie-spawn bonus", d, AttributeModifier.Operation.MULTIPLY_TOTAL));
++ this.getAttribute(Attributes.KNOCKBACK_RESISTANCE).addPermanentModifier(new AttributeModifier("Random spawn bonus", this.random.nextDouble() * 0.05000000074505806D, AttributeModifier.Operation.ADDITION));
++ double d0 = this.random.nextDouble() * 1.5D * (double) difficulty;
++
++ if (d0 > 1.0D) {
++ this.getAttribute(Attributes.FOLLOW_RANGE).addPermanentModifier(new AttributeModifier("Random zombie-spawn bonus", d0, AttributeModifier.Operation.MULTIPLY_TOTAL));
+ }
+
+ if (this.random.nextFloat() < difficulty * 0.05F) {
+- this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE)
+- .addPermanentModifier(new AttributeModifier("Leader zombie bonus", this.random.nextDouble() * 0.25 + 0.5, AttributeModifier.Operation.ADDITION));
+- this.getAttribute(Attributes.MAX_HEALTH)
+- .addPermanentModifier(
+- new AttributeModifier("Leader zombie bonus", this.random.nextDouble() * 3.0 + 1.0, AttributeModifier.Operation.MULTIPLY_TOTAL)
+- );
++ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).addPermanentModifier(new AttributeModifier("Leader zombie bonus", this.random.nextDouble() * 0.25D + 0.5D, AttributeModifier.Operation.ADDITION));
++ this.getAttribute(Attributes.MAX_HEALTH).addPermanentModifier(new AttributeModifier("Leader zombie bonus", this.random.nextDouble() * 3.0D + 1.0D, AttributeModifier.Operation.MULTIPLY_TOTAL));
+ this.setCanBreakDoors(this.supportsBreakDoorGoal());
+ }
++
+ }
+
+ protected void randomizeReinforcementsChance() {
+- this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * 0.1F);
++ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * 0.10000000149011612D);
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height + 0.0625F * f, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height + 0.0625F * f, 0.0F);
+ }
+
+ @Override
+@@ -545,41 +583,51 @@
+ @Override
+ protected void dropCustomDeathLoot(DamageSource source, int looting, boolean recentlyHit) {
+ super.dropCustomDeathLoot(source, looting, recentlyHit);
+- if (source.getEntity() instanceof Creeper creeper && creeper.canDropMobsSkull()) {
+- ItemStack skull = this.getSkull();
+- if (!skull.isEmpty()) {
+- creeper.increaseDroppedSkulls();
+- this.spawnAtLocation(skull);
++ Entity entity = source.getEntity();
++
++ if (entity instanceof Creeper) {
++ Creeper entitycreeper = (Creeper) entity;
++
++ if (entitycreeper.canDropMobsSkull()) {
++ ItemStack itemstack = this.getSkull();
++
++ if (!itemstack.isEmpty()) {
++ entitycreeper.increaseDroppedSkulls();
++ this.spawnAtLocation(itemstack);
++ }
+ }
+ }
++
+ }
+
+ protected ItemStack getSkull() {
+ return new ItemStack(Items.ZOMBIE_HEAD);
+ }
+
+- class ZombieAttackTurtleEggGoal extends RemoveBlockGoal {
+- ZombieAttackTurtleEggGoal(PathfinderMob mob, double speedModifier, int verticalSearchRange) {
+- super(Blocks.TURTLE_EGG, mob, speedModifier, verticalSearchRange);
++ private class ZombieAttackTurtleEggGoal extends RemoveBlockGoal {
++
++ ZombieAttackTurtleEggGoal(PathfinderMob mob, double speedModifier, int i) {
++ super(Blocks.TURTLE_EGG, mob, speedModifier, i);
+ }
+
+ @Override
+ public void playDestroyProgressSound(LevelAccessor level, BlockPos pos) {
+- level.playSound(null, pos, SoundEvents.ZOMBIE_DESTROY_EGG, SoundSource.HOSTILE, 0.5F, 0.9F + Zombie.this.random.nextFloat() * 0.2F);
++ level.playSound((Player) null, pos, SoundEvents.ZOMBIE_DESTROY_EGG, SoundSource.HOSTILE, 0.5F, 0.9F + Zombie.this.random.nextFloat() * 0.2F);
+ }
+
+ @Override
+ public void playBreakSound(Level level, BlockPos pos) {
+- level.playSound(null, pos, SoundEvents.TURTLE_EGG_BREAK, SoundSource.BLOCKS, 0.7F, 0.9F + level.random.nextFloat() * 0.2F);
++ level.playSound((Player) null, pos, SoundEvents.TURTLE_EGG_BREAK, SoundSource.BLOCKS, 0.7F, 0.9F + level.random.nextFloat() * 0.2F);
+ }
+
+ @Override
+ public double acceptedDistance() {
+- return 1.14;
++ return 1.14D;
+ }
+ }
+
+- public static class ZombieGroupData implements SpawnGroupData {
++ public static class ZombieGroupData implements GroupDataEntity {
++
+ public final boolean isBaby;
+ public final boolean canSpawnJockey;
+