aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/mache-vineflower/net/minecraft/world/entity/monster
diff options
context:
space:
mode:
authorMiniDigger | Martin <[email protected]>2024-01-14 11:04:49 +0100
committerMiniDigger | Martin <[email protected]>2024-01-14 11:04:49 +0100
commitbee74680e607c2e29b038329f62181238911cd83 (patch)
tree708fd1a4a0227d9071243adf2a42d5e9e96cde4a /patch-remap/mache-vineflower/net/minecraft/world/entity/monster
parent0a44692ef6ff6e255d48eb3ba1bb114166eafda9 (diff)
downloadPaper-softspoon.tar.gz
Paper-softspoon.zip
add remapped patches as a testsoftspoon
Diffstat (limited to 'patch-remap/mache-vineflower/net/minecraft/world/entity/monster')
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/AbstractSkeleton.java.patch238
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/CaveSpider.java.patch78
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Creeper.java.patch319
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Drowned.java.patch580
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/ElderGuardian.java.patch54
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/EnderMan.java.patch692
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Evoker.java.patch410
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Ghast.java.patch392
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Guardian.java.patch574
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Husk.java.patch63
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Illusioner.java.patch324
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Phantom.java.patch668
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Ravager.java.patch313
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Shulker.java.patch853
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Silverfish.java.patch282
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Skeleton.java.patch103
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Slime.java.patch665
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/SpellcasterIllager.java.patch271
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Spider.java.patch278
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Strider.java.patch583
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Vex.java.patch402
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Witch.java.patch313
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/WitherSkeleton.java.patch110
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Zombie.java.patch718
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/ZombieVillager.java.patch389
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/ZombifiedPiglin.java.patch202
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java.patch133
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/piglin/Piglin.java.patch474
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/piglin/PiglinAi.java.patch967
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/monster/warden/Warden.java.patch615
30 files changed, 12063 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/AbstractSkeleton.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/AbstractSkeleton.java.patch
new file mode 100644
index 0000000000..c20b81663c
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/AbstractSkeleton.java.patch
@@ -0,0 +1,238 @@
+--- a/net/minecraft/world/entity/monster/AbstractSkeleton.java
++++ b/net/minecraft/world/entity/monster/AbstractSkeleton.java
+@@ -12,14 +12,14 @@
+ import net.minecraft.world.DifficultyInstance;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
++import net.minecraft.world.entity.EntityPose;
+ 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.ai.attributes.AttributeSupplier;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+ import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
+@@ -44,11 +44,12 @@
+ import net.minecraft.world.level.Level;
+ 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;
+
+ public abstract class AbstractSkeleton extends Monster implements RangedAttackMob {
+- private final RangedBowAttackGoal<AbstractSkeleton> bowGoal = new RangedBowAttackGoal<>(this, 1.0, 20, 15.0F);
+- private final MeleeAttackGoal meleeGoal = new MeleeAttackGoal(this, 1.2, false) {
++
++ private final RangedBowAttackGoal<AbstractSkeleton> bowGoal = new RangedBowAttackGoal<>(this, 1.0D, 20, 15.0F);
++ private final MeleeAttackGoal meleeGoal = new MeleeAttackGoal(this, 1.2D, false) {
+ @Override
+ public void stop() {
+ super.stop();
+@@ -70,51 +71,53 @@
+ @Override
+ protected void registerGoals() {
+ this.goalSelector.addGoal(2, new RestrictSunGoal(this));
+- this.goalSelector.addGoal(3, new FleeSunGoal(this, 1.0));
+- this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Wolf.class, 6.0F, 1.0, 1.2));
+- this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0));
++ this.goalSelector.addGoal(3, new FleeSunGoal(this, 1.0D));
++ this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Wolf.class, 6.0F, 1.0D, 1.2D));
++ this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 1.0D));
+ this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
+ this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
+- this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
++ this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0]));
+ this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Turtle.class, 10, true, false, Turtle.BABY_ON_LAND_SELECTOR));
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.25);
++ return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.25D);
+ }
+
+ @Override
+- protected void playStepSound(BlockPos pos, BlockState block) {
++ protected void playStepSound(BlockPos pos, IBlockData block) {
+ this.playSound(this.getStepSound(), 0.15F, 1.0F);
+ }
+
+ abstract SoundEvent getStepSound();
+
+ @Override
+- public MobType getMobType() {
+- return MobType.UNDEAD;
++ public EnumMonsterType getMobType() {
++ return EnumMonsterType.UNDEAD;
+ }
+
+ @Override
+ public void aiStep() {
+- boolean isSunBurnTick = this.isSunBurnTick();
+- if (isSunBurnTick) {
+- 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()) {
++ boolean flag = this.isSunBurnTick();
++
++ if (flag) {
++ 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);
+ }
+ }
+
+- isSunBurnTick = false;
++ flag = false;
+ }
+
+- if (isSunBurnTick) {
++ if (flag) {
+ this.setSecondsOnFire(8);
+ }
+ }
+@@ -125,9 +128,14 @@
+ @Override
+ public void rideTick() {
+ super.rideTick();
+- if (this.getControlledVehicle() instanceof PathfinderMob pathfinderMob) {
+- this.yBodyRot = pathfinderMob.yBodyRot;
++ Entity entity = this.getControlledVehicle();
++
++ if (entity instanceof PathfinderMob) {
++ PathfinderMob entitycreature = (PathfinderMob) entity;
++
++ this.yBodyRot = entitycreature.yBodyRot;
+ }
++
+ }
+
+ @Override
+@@ -138,58 +146,73 @@
+
+ @Nullable
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
+- SpawnGroupData var10 = super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
+- RandomSource random = level.getRandom();
+- this.populateDefaultEquipmentSlots(random, difficulty);
+- this.populateDefaultEquipmentEnchantments(random, difficulty);
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
++ spawnData = super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
++ RandomSource randomsource = level.getRandom();
++
++ this.populateDefaultEquipmentSlots(randomsource, difficulty);
++ this.populateDefaultEquipmentEnchantments(randomsource, difficulty);
+ this.reassessWeaponGoal();
+- this.setCanPickUpLoot(random.nextFloat() < 0.55F * difficulty.getSpecialMultiplier());
++ this.setCanPickUpLoot(randomsource.nextFloat() < 0.55F * difficulty.getSpecialMultiplier());
+ 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;
+ }
+ }
+
+- return var10;
++ return spawnData;
+ }
+
+ public void reassessWeaponGoal() {
+ if (this.level() != null && !this.level().isClientSide) {
+ this.goalSelector.removeGoal(this.meleeGoal);
+ this.goalSelector.removeGoal(this.bowGoal);
+- ItemStack itemInHand = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW));
+- if (itemInHand.is(Items.BOW)) {
+- int i = 20;
++ ItemStack itemstack = this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW));
++
++ if (itemstack.is(Items.BOW)) {
++ byte b0 = 20;
++
+ if (this.level().getDifficulty() != Difficulty.HARD) {
+- i = 40;
++ b0 = 40;
+ }
+
+- this.bowGoal.setMinAttackInterval(i);
++ this.bowGoal.setMinAttackInterval(b0);
+ this.goalSelector.addGoal(4, this.bowGoal);
+ } else {
+ this.goalSelector.addGoal(4, this.meleeGoal);
+ }
++
+ }
+ }
+
+ @Override
+ public void performRangedAttack(LivingEntity target, float distanceFactor) {
+- ItemStack projectile = this.getProjectile(this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW)));
+- AbstractArrow arrow = this.getArrow(projectile, distanceFactor);
+- double d = target.getX() - this.getX();
+- double d1 = target.getY(0.3333333333333333) - arrow.getY();
++ ItemStack itemstack = this.getProjectile(this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW)));
++ AbstractArrow entityarrow = this.getArrow(itemstack, distanceFactor);
++ double d0 = target.getX() - this.getX();
++ double d1 = target.getY(0.3333333333333333D) - entityarrow.getY();
+ double d2 = target.getZ() - this.getZ();
+- double squareRoot = Math.sqrt(d * d + d2 * d2);
+- arrow.shoot(d, d1 + squareRoot * 0.2F, d2, 1.6F, (float)(14 - this.level().getDifficulty().getId() * 4));
++ double d3 = Math.sqrt(d0 * d0 + d2 * d2);
++
++ entityarrow.shoot(d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, (float) (14 - this.level().getDifficulty().getId() * 4));
++ // CraftBukkit start
++ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, this.getMainHandItem(), null, entityarrow, net.minecraft.world.EnumHand.MAIN_HAND, 0.8F, true);
++ if (event.isCancelled()) {
++ event.getProjectile().remove();
++ return;
++ }
++
++ if (event.getProjectile() == entityarrow.getBukkitEntity()) {
++ this.level().addFreshEntity(entityarrow);
++ }
++ // CraftBukkit end
+ this.playSound(SoundEvents.SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F));
+- this.level().addFreshEntity(arrow);
++ // this.level().addFreshEntity(entityarrow); // CraftBukkit - moved up
+ }
+
+ protected AbstractArrow getArrow(ItemStack arrowStack, float velocity) {
+@@ -213,10 +236,11 @@
+ if (!this.level().isClientSide) {
+ this.reassessWeaponGoal();
+ }
++
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return 1.74F;
+ }
+
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/CaveSpider.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/CaveSpider.java.patch
new file mode 100644
index 0000000000..0bb3955f74
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/CaveSpider.java.patch
@@ -0,0 +1,78 @@
+--- a/net/minecraft/world/entity/monster/CaveSpider.java
++++ b/net/minecraft/world/entity/monster/CaveSpider.java
+@@ -8,11 +8,11 @@
+ import net.minecraft.world.effect.MobEffects;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
++import net.minecraft.world.entity.EntityPose;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
++import net.minecraft.world.entity.GroupDataEntity;
+ import net.minecraft.world.entity.LivingEntity;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.Pose;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+ import net.minecraft.world.level.Level;
+@@ -20,27 +20,29 @@
+ import org.joml.Vector3f;
+
+ public class CaveSpider extends Spider {
++
+ public CaveSpider(EntityType<? extends CaveSpider> entityType, Level level) {
+ super(entityType, level);
+ }
+
+ public static AttributeSupplier.Builder createCaveSpider() {
+- return Spider.createAttributes().add(Attributes.MAX_HEALTH, 12.0);
++ return Spider.createAttributes().add(Attributes.MAX_HEALTH, 12.0D);
+ }
+
+ @Override
+ public boolean doHurtTarget(Entity entity) {
+ if (super.doHurtTarget(entity)) {
+ if (entity instanceof LivingEntity) {
+- int i = 0;
++ byte b0 = 0;
++
+ if (this.level().getDifficulty() == Difficulty.NORMAL) {
+- i = 7;
++ b0 = 7;
+ } else if (this.level().getDifficulty() == Difficulty.HARD) {
+- i = 15;
++ b0 = 15;
+ }
+
+- if (i > 0) {
+- ((LivingEntity)entity).addEffect(new MobEffectInstance(MobEffects.POISON, i * 20, 0), this);
++ if (b0 > 0) {
++ ((LivingEntity) entity).addEffect(new MobEffectInstance(MobEffects.POISON, b0 * 20, 0), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit
+ }
+ }
+
+@@ -52,20 +54,18 @@
+
+ @Nullable
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
+ return spawnData;
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return 0.45F;
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height, 0.0F);
+ }
+
+ @Override
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Creeper.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Creeper.java.patch
new file mode 100644
index 0000000000..4a441084d9
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Creeper.java.patch
@@ -0,0 +1,319 @@
+--- a/net/minecraft/world/entity/monster/Creeper.java
++++ b/net/minecraft/world/entity/monster/Creeper.java
+@@ -1,6 +1,7 @@
+ package net.minecraft.world.entity.monster;
+
+ import java.util.Collection;
++import java.util.Iterator;
+ import javax.annotation.Nullable;
+ import net.minecraft.nbt.CompoundTag;
+ import net.minecraft.network.syncher.EntityDataAccessor;
+@@ -11,7 +12,7 @@
+ import net.minecraft.sounds.SoundEvents;
+ import net.minecraft.tags.ItemTags;
+ import net.minecraft.util.Mth;
+-import net.minecraft.world.InteractionHand;
++import net.minecraft.world.EnumHand;
+ import net.minecraft.world.InteractionResult;
+ import net.minecraft.world.damagesource.DamageSource;
+ import net.minecraft.world.effect.MobEffectInstance;
+@@ -38,17 +39,25 @@
+ import net.minecraft.world.entity.player.Player;
+ import net.minecraft.world.item.ItemStack;
+ import net.minecraft.world.item.Items;
++import net.minecraft.world.level.IMaterial;
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.level.gameevent.GameEvent;
+
++// CraftBukkit start;
++import org.bukkit.event.entity.CreatureSpawnEvent;
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++import org.bukkit.event.entity.ExplosionPrimeEvent;
++// CraftBukkit end
++
+ public class Creeper extends Monster implements PowerableMob {
++
+ private static final EntityDataAccessor<Integer> DATA_SWELL_DIR = SynchedEntityData.defineId(Creeper.class, EntityDataSerializers.INT);
+ private static final EntityDataAccessor<Boolean> DATA_IS_POWERED = SynchedEntityData.defineId(Creeper.class, EntityDataSerializers.BOOLEAN);
+ private static final EntityDataAccessor<Boolean> DATA_IS_IGNITED = SynchedEntityData.defineId(Creeper.class, EntityDataSerializers.BOOLEAN);
+ private int oldSwell;
+- private int swell;
+- private int maxSwell = 30;
+- private int explosionRadius = 3;
++ public int swell;
++ public int maxSwell = 30;
++ public int explosionRadius = 3;
+ private int droppedSkulls;
+
+ public Creeper(EntityType<? extends Creeper> entityType, Level level) {
+@@ -59,29 +68,30 @@
+ protected void registerGoals() {
+ this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(2, new SwellGoal(this));
+- this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Ocelot.class, 6.0F, 1.0, 1.2));
+- this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Cat.class, 6.0F, 1.0, 1.2));
+- this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, false));
+- this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8));
++ this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Ocelot.class, 6.0F, 1.0D, 1.2D));
++ this.goalSelector.addGoal(3, new AvoidEntityGoal<>(this, Cat.class, 6.0F, 1.0D, 1.2D));
++ this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, false));
++ this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8D));
+ this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
+ this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
+ this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, true));
+- this.targetSelector.addGoal(2, new HurtByTargetGoal(this));
++ this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.25);
++ return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.25D);
+ }
+
+ @Override
+ public int getMaxFallDistance() {
+- return this.getTarget() == null ? 3 : 3 + (int)(this.getHealth() - 1.0F);
++ return this.getTarget() == null ? 3 : 3 + (int) (this.getHealth() - 1.0F);
+ }
+
+ @Override
+ public boolean causeFallDamage(float fallDistance, float multiplier, DamageSource source) {
+ boolean flag = super.causeFallDamage(fallDistance, multiplier, source);
+- this.swell += (int)(fallDistance * 1.5F);
++
++ this.swell += (int) (fallDistance * 1.5F);
+ if (this.swell > this.maxSwell - 5) {
+ this.swell = this.maxSwell - 5;
+ }
+@@ -92,27 +102,27 @@
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_SWELL_DIR, -1);
+- this.entityData.define(DATA_IS_POWERED, false);
+- this.entityData.define(DATA_IS_IGNITED, false);
++ this.entityData.define(Creeper.DATA_SWELL_DIR, -1);
++ this.entityData.define(Creeper.DATA_IS_POWERED, false);
++ this.entityData.define(Creeper.DATA_IS_IGNITED, false);
+ }
+
+ @Override
+ public void addAdditionalSaveData(CompoundTag compound) {
+ super.addAdditionalSaveData(compound);
+- if (this.entityData.get(DATA_IS_POWERED)) {
++ if ((Boolean) this.entityData.get(Creeper.DATA_IS_POWERED)) {
+ compound.putBoolean("powered", true);
+ }
+
+- compound.putShort("Fuse", (short)this.maxSwell);
+- compound.putByte("ExplosionRadius", (byte)this.explosionRadius);
++ compound.putShort("Fuse", (short) this.maxSwell);
++ compound.putByte("ExplosionRadius", (byte) this.explosionRadius);
+ compound.putBoolean("ignited", this.isIgnited());
+ }
+
+ @Override
+ public void readAdditionalSaveData(CompoundTag compound) {
+ super.readAdditionalSaveData(compound);
+- this.entityData.set(DATA_IS_POWERED, compound.getBoolean("powered"));
++ this.entityData.set(Creeper.DATA_IS_POWERED, compound.getBoolean("powered"));
+ if (compound.contains("Fuse", 99)) {
+ this.maxSwell = compound.getShort("Fuse");
+ }
+@@ -124,6 +134,7 @@
+ if (compound.getBoolean("ignited")) {
+ this.ignite();
+ }
++
+ }
+
+ @Override
+@@ -134,13 +145,14 @@
+ this.setSwellDir(1);
+ }
+
+- int swellDir = this.getSwellDir();
+- if (swellDir > 0 && this.swell == 0) {
++ int i = this.getSwellDir();
++
++ if (i > 0 && this.swell == 0) {
+ this.playSound(SoundEvents.CREEPER_PRIMED, 1.0F, 0.5F);
+ this.gameEvent(GameEvent.PRIME_FUSE);
+ }
+
+- this.swell += swellDir;
++ this.swell += i;
+ if (this.swell < 0) {
+ this.swell = 0;
+ }
+@@ -175,10 +187,16 @@
+ protected void dropCustomDeathLoot(DamageSource source, int looting, boolean recentlyHit) {
+ super.dropCustomDeathLoot(source, looting, recentlyHit);
+ Entity entity = source.getEntity();
+- if (entity != this && entity instanceof Creeper creeper && creeper.canDropMobsSkull()) {
+- creeper.increaseDroppedSkulls();
+- this.spawnAtLocation(Items.CREEPER_HEAD);
++
++ if (entity != this && entity instanceof Creeper) {
++ Creeper entitycreeper = (Creeper) entity;
++
++ if (entitycreeper.canDropMobsSkull()) {
++ entitycreeper.increaseDroppedSkulls();
++ this.spawnAtLocation((IMaterial) Items.CREEPER_HEAD);
++ }
+ }
++
+ }
+
+ @Override
+@@ -188,40 +206,54 @@
+
+ @Override
+ public boolean isPowered() {
+- return this.entityData.get(DATA_IS_POWERED);
++ return (Boolean) this.entityData.get(Creeper.DATA_IS_POWERED);
+ }
+
+ public float getSwelling(float partialTicks) {
+- return Mth.lerp(partialTicks, (float)this.oldSwell, (float)this.swell) / (float)(this.maxSwell - 2);
++ return Mth.lerp(partialTicks, (float) this.oldSwell, (float) this.swell) / (float) (this.maxSwell - 2);
+ }
+
+ public int getSwellDir() {
+- return this.entityData.get(DATA_SWELL_DIR);
++ return (Integer) this.entityData.get(Creeper.DATA_SWELL_DIR);
+ }
+
+ public void setSwellDir(int state) {
+- this.entityData.set(DATA_SWELL_DIR, state);
++ this.entityData.set(Creeper.DATA_SWELL_DIR, state);
+ }
+
+ @Override
+ public void thunderHit(ServerLevel level, LightningBolt lightning) {
+ super.thunderHit(level, lightning);
+- this.entityData.set(DATA_IS_POWERED, true);
++ // CraftBukkit start
++ if (CraftEventFactory.callCreeperPowerEvent(this, lightning, org.bukkit.event.entity.CreeperPowerEvent.PowerCause.LIGHTNING).isCancelled()) {
++ return;
++ }
++ // CraftBukkit end
++ this.entityData.set(Creeper.DATA_IS_POWERED, true);
+ }
+
++ // CraftBukkit start
++ public void setPowered(boolean powered) {
++ this.entityData.set(Creeper.DATA_IS_POWERED, powered);
++ }
++ // CraftBukkit end
++
+ @Override
+- protected InteractionResult mobInteract(Player player, InteractionHand hand) {
+- ItemStack itemInHand = player.getItemInHand(hand);
+- if (itemInHand.is(ItemTags.CREEPER_IGNITERS)) {
+- SoundEvent soundEvent = itemInHand.is(Items.FIRE_CHARGE) ? SoundEvents.FIRECHARGE_USE : SoundEvents.FLINTANDSTEEL_USE;
+- this.level()
+- .playSound(player, this.getX(), this.getY(), this.getZ(), soundEvent, this.getSoundSource(), 1.0F, this.random.nextFloat() * 0.4F + 0.8F);
++ protected InteractionResult mobInteract(Player player, EnumHand hand) {
++ ItemStack itemstack = player.getItemInHand(hand);
++
++ if (itemstack.is(ItemTags.CREEPER_IGNITERS)) {
++ SoundEvent soundeffect = itemstack.is(Items.FIRE_CHARGE) ? SoundEvents.FIRECHARGE_USE : SoundEvents.FLINTANDSTEEL_USE;
++
++ this.level().playSound(player, this.getX(), this.getY(), this.getZ(), soundeffect, this.getSoundSource(), 1.0F, this.random.nextFloat() * 0.4F + 0.8F);
+ if (!this.level().isClientSide) {
+ this.ignite();
+- if (!itemInHand.isDamageableItem()) {
+- itemInHand.shrink(1);
++ if (itemstack.getItem().getMaxDamage() == 0) { // CraftBukkit - fix MC-264285: unbreakable flint and steels are completely consumed when igniting a creeper
++ itemstack.shrink(1);
+ } else {
+- itemInHand.hurtAndBreak(1, player, entity -> entity.broadcastBreakEvent(hand));
++ itemstack.hurtAndBreak(1, player, (entityhuman1) -> {
++ entityhuman1.broadcastBreakEvent(hand);
++ });
+ }
+ }
+
+@@ -231,40 +263,58 @@
+ }
+ }
+
+- private void explodeCreeper() {
++ public void explodeCreeper() {
+ if (!this.level().isClientSide) {
+ float f = this.isPowered() ? 2.0F : 1.0F;
++
++ // CraftBukkit start
++ ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent(this, this.explosionRadius * f, false);
++ if (!event.isCancelled()) {
++ // CraftBukkit end
+ this.dead = true;
+- this.level().explode(this, this.getX(), this.getY(), this.getZ(), (float)this.explosionRadius * f, Level.ExplosionInteraction.MOB);
++ this.level().explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.a.MOB); // CraftBukkit
+ this.discard();
+ this.spawnLingeringCloud();
++ // CraftBukkit start
++ } else {
++ swell = 0;
++ }
++ // CraftBukkit end
+ }
++
+ }
+
+ private void spawnLingeringCloud() {
+- Collection<MobEffectInstance> activeEffects = this.getActiveEffects();
+- if (!activeEffects.isEmpty()) {
+- AreaEffectCloud areaEffectCloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ());
+- areaEffectCloud.setRadius(2.5F);
+- areaEffectCloud.setRadiusOnUse(-0.5F);
+- areaEffectCloud.setWaitTime(10);
+- areaEffectCloud.setDuration(areaEffectCloud.getDuration() / 2);
+- areaEffectCloud.setRadiusPerTick(-areaEffectCloud.getRadius() / (float)areaEffectCloud.getDuration());
++ Collection<MobEffectInstance> collection = this.getActiveEffects();
+
+- for (MobEffectInstance mobEffectInstance : activeEffects) {
+- areaEffectCloud.addEffect(new MobEffectInstance(mobEffectInstance));
++ if (!collection.isEmpty()) {
++ AreaEffectCloud entityareaeffectcloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ());
++
++ entityareaeffectcloud.setOwner(this); // CraftBukkit
++ entityareaeffectcloud.setRadius(2.5F);
++ entityareaeffectcloud.setRadiusOnUse(-0.5F);
++ entityareaeffectcloud.setWaitTime(10);
++ entityareaeffectcloud.setDuration(entityareaeffectcloud.getDuration() / 2);
++ entityareaeffectcloud.setRadiusPerTick(-entityareaeffectcloud.getRadius() / (float) entityareaeffectcloud.getDuration());
++ Iterator iterator = collection.iterator();
++
++ while (iterator.hasNext()) {
++ MobEffectInstance mobeffect = (MobEffectInstance) iterator.next();
++
++ entityareaeffectcloud.addEffect(new MobEffectInstance(mobeffect));
+ }
+
+- this.level().addFreshEntity(areaEffectCloud);
++ this.level().addFreshEntity(entityareaeffectcloud, CreatureSpawnEvent.SpawnReason.EXPLOSION); // CraftBukkit
+ }
++
+ }
+
+ public boolean isIgnited() {
+- return this.entityData.get(DATA_IS_IGNITED);
++ return (Boolean) this.entityData.get(Creeper.DATA_IS_IGNITED);
+ }
+
+ public void ignite() {
+- this.entityData.set(DATA_IS_IGNITED, true);
++ this.entityData.set(Creeper.DATA_IS_IGNITED, true);
+ }
+
+ public boolean canDropMobsSkull() {
+@@ -272,6 +322,6 @@
+ }
+
+ public void increaseDroppedSkulls() {
+- this.droppedSkulls++;
++ ++this.droppedSkulls;
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Drowned.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Drowned.java.patch
new file mode 100644
index 0000000000..e6905ed1f3
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Drowned.java.patch
@@ -0,0 +1,580 @@
+--- a/net/minecraft/world/entity/monster/Drowned.java
++++ b/net/minecraft/world/entity/monster/Drowned.java
+@@ -13,15 +13,15 @@
+ import net.minecraft.util.RandomSource;
+ import net.minecraft.world.Difficulty;
+ import net.minecraft.world.DifficultyInstance;
+-import net.minecraft.world.InteractionHand;
++import net.minecraft.world.EnumHand;
+ import net.minecraft.world.damagesource.DamageSource;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
++import net.minecraft.world.entity.EnumMoveType;
+ 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.MoverType;
+ import net.minecraft.world.entity.PathfinderMob;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+ import net.minecraft.world.entity.ai.control.MoveControl;
+ import net.minecraft.world.entity.ai.goal.Goal;
+@@ -53,10 +53,11 @@
+ import net.minecraft.world.phys.Vec3;
+
+ public class Drowned extends Zombie implements RangedAttackMob {
++
+ public static final float NAUTILUS_SHELL_CHANCE = 0.03F;
+ boolean searchingForLand;
+- protected final WaterBoundPathNavigation waterNavigation;
+- protected final GroundPathNavigation groundNavigation;
++ public final WaterBoundPathNavigation waterNavigation;
++ public final GroundPathNavigation groundNavigation;
+
+ public Drowned(EntityType<? extends Drowned> entityType, Level level) {
+ super(entityType, level);
+@@ -69,13 +70,13 @@
+
+ @Override
+ protected void addBehaviourGoals() {
+- this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0));
+- this.goalSelector.addGoal(2, new Drowned.DrownedTridentAttackGoal(this, 1.0, 40, 10.0F));
+- this.goalSelector.addGoal(2, new Drowned.DrownedAttackGoal(this, 1.0, false));
+- this.goalSelector.addGoal(5, new Drowned.DrownedGoToBeachGoal(this, 1.0));
+- this.goalSelector.addGoal(6, new Drowned.DrownedSwimUpGoal(this, 1.0, this.level().getSeaLevel()));
+- this.goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0));
+- this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Drowned.class).setAlertOthers(ZombifiedPiglin.class));
++ this.goalSelector.addGoal(1, new Drowned.DrownedGoToWaterGoal(this, 1.0D));
++ this.goalSelector.addGoal(2, new Drowned.DrownedTridentAttackGoal(this, 1.0D, 40, 10.0F));
++ this.goalSelector.addGoal(2, new Drowned.DrownedAttackGoal(this, 1.0D, false));
++ this.goalSelector.addGoal(5, new Drowned.DrownedGoToBeachGoal(this, 1.0D));
++ this.goalSelector.addGoal(6, new Drowned.DrownedSwimUpGoal(this, 1.0D, this.level().getSeaLevel()));
++ this.goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0D));
++ this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Drowned.class})).setAlertOthers(ZombifiedPiglin.class));
+ this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::okTarget));
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false));
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
+@@ -84,35 +85,24 @@
+ }
+
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
+- SpawnGroupData var6 = super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
++ spawnData = super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
+ if (this.getItemBySlot(EquipmentSlot.OFFHAND).isEmpty() && level.getRandom().nextFloat() < 0.03F) {
+ this.setItemSlot(EquipmentSlot.OFFHAND, new ItemStack(Items.NAUTILUS_SHELL));
+ this.setGuaranteedDrop(EquipmentSlot.OFFHAND);
+ }
+
+- return var6;
++ return spawnData;
+ }
+
+- public static boolean checkDrownedSpawnRules(
+- EntityType<Drowned> drowned, ServerLevelAccessor serverLevel, MobSpawnType mobSpawnType, BlockPos pos, RandomSource random
+- ) {
+- if (!serverLevel.getFluidState(pos.below()).is(FluidTags.WATER) && !MobSpawnType.isSpawner(mobSpawnType)) {
++ public static boolean checkDrownedSpawnRules(EntityType<Drowned> drowned, ServerLevelAccessor serverLevel, EnumMobSpawn mobSpawnType, BlockPos pos, RandomSource random) {
++ if (!serverLevel.getFluidState(pos.below()).is(FluidTags.WATER) && !EnumMobSpawn.isSpawner(mobSpawnType)) {
+ return false;
+ } else {
+- Holder<Biome> biome = serverLevel.getBiome(pos);
+- boolean flag = serverLevel.getDifficulty() != Difficulty.PEACEFUL
+- && (MobSpawnType.ignoresLightRequirements(mobSpawnType) || isDarkEnoughToSpawn(serverLevel, pos, random))
+- && (MobSpawnType.isSpawner(mobSpawnType) || serverLevel.getFluidState(pos).is(FluidTags.WATER));
+- if (flag && MobSpawnType.isSpawner(mobSpawnType)) {
+- return true;
+- } else {
+- return biome.is(BiomeTags.MORE_FREQUENT_DROWNED_SPAWNS)
+- ? random.nextInt(15) == 0 && flag
+- : random.nextInt(40) == 0 && isDeepEnoughToSpawn(serverLevel, pos) && flag;
+- }
++ Holder<Biome> holder = serverLevel.getBiome(pos);
++ boolean flag = serverLevel.getDifficulty() != Difficulty.PEACEFUL && (EnumMobSpawn.ignoresLightRequirements(mobSpawnType) || isDarkEnoughToSpawn(serverLevel, pos, random)) && (EnumMobSpawn.isSpawner(mobSpawnType) || serverLevel.getFluidState(pos).is(FluidTags.WATER));
++
++ return flag && EnumMobSpawn.isSpawner(mobSpawnType) ? true : (holder.is(BiomeTags.MORE_FREQUENT_DROWNED_SPAWNS) ? random.nextInt(15) == 0 && flag : random.nextInt(40) == 0 && isDeepEnoughToSpawn(serverLevel, pos) && flag);
+ }
+ }
+
+@@ -157,25 +147,21 @@
+
+ @Override
+ protected void populateDefaultEquipmentSlots(RandomSource random, DifficultyInstance difficulty) {
+- if ((double)random.nextFloat() > 0.9) {
+- int randomInt = random.nextInt(16);
+- if (randomInt < 10) {
++ if ((double) random.nextFloat() > 0.9D) {
++ int i = random.nextInt(16);
++
++ if (i < 10) {
+ this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.TRIDENT));
+ } else {
+ this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.FISHING_ROD));
+ }
+ }
++
+ }
+
+ @Override
+ protected boolean canReplaceCurrentItem(ItemStack candidate, ItemStack existing) {
+- if (existing.is(Items.NAUTILUS_SHELL)) {
+- return false;
+- } else {
+- return existing.is(Items.TRIDENT)
+- ? candidate.is(Items.TRIDENT) && candidate.getDamageValue() < existing.getDamageValue()
+- : candidate.is(Items.TRIDENT) || super.canReplaceCurrentItem(candidate, existing);
+- }
++ return existing.is(Items.NAUTILUS_SHELL) ? false : (existing.is(Items.TRIDENT) ? (candidate.is(Items.TRIDENT) ? candidate.getDamageValue() < existing.getDamageValue() : false) : (candidate.is(Items.TRIDENT) ? true : super.canReplaceCurrentItem(candidate, existing)));
+ }
+
+ @Override
+@@ -189,7 +175,7 @@
+ }
+
+ public boolean okTarget(@Nullable LivingEntity target) {
+- return target != null && (!this.level().isDay() || target.isInWater());
++ return target != null ? !this.level().isDay() || target.isInWater() : false;
+ }
+
+ @Override
+@@ -201,8 +187,9 @@
+ if (this.searchingForLand) {
+ return true;
+ } else {
+- LivingEntity target = this.getTarget();
+- return target != null && target.isInWater();
++ LivingEntity entityliving = this.getTarget();
++
++ return entityliving != null && entityliving.isInWater();
+ }
+ }
+
+@@ -210,11 +197,12 @@
+ public void travel(Vec3 travelVector) {
+ if (this.isControlledByLocalInstance() && this.isInWater() && this.wantsToSwim()) {
+ this.moveRelative(0.01F, travelVector);
+- this.move(MoverType.SELF, this.getDeltaMovement());
+- this.setDeltaMovement(this.getDeltaMovement().scale(0.9));
++ this.move(EnumMoveType.SELF, this.getDeltaMovement());
++ this.setDeltaMovement(this.getDeltaMovement().scale(0.9D));
+ } else {
+ super.travel(travelVector);
+ }
++
+ }
+
+ @Override
+@@ -228,6 +216,7 @@
+ this.setSwimming(false);
+ }
+ }
++
+ }
+
+ @Override
+@@ -236,12 +225,15 @@
+ }
+
+ protected boolean closeToNextPos() {
+- Path path = this.getNavigation().getPath();
+- if (path != null) {
+- BlockPos target = path.getTarget();
+- if (target != null) {
+- double d = this.distanceToSqr((double)target.getX(), (double)target.getY(), (double)target.getZ());
+- if (d < 4.0) {
++ Path pathentity = this.getNavigation().getPath();
++
++ if (pathentity != null) {
++ BlockPos blockposition = pathentity.getTarget();
++
++ if (blockposition != null) {
++ double d0 = this.distanceToSqr((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ());
++
++ if (d0 < 4.0D) {
+ return true;
+ }
+ }
+@@ -252,80 +244,72 @@
+
+ @Override
+ public void performRangedAttack(LivingEntity target, float distanceFactor) {
+- ThrownTrident thrownTrident = new ThrownTrident(this.level(), this, new ItemStack(Items.TRIDENT));
+- double d = target.getX() - this.getX();
+- double d1 = target.getY(0.3333333333333333) - thrownTrident.getY();
++ ThrownTrident entitythrowntrident = new ThrownTrident(this.level(), this, this.getItemInHand(net.minecraft.world.entity.projectile.ProjectileUtil.getWeaponHoldingHand(this, Items.TRIDENT))); // CraftBukkit - Use Trident in hand like skeletons (SPIGOT-7025)
++ double d0 = target.getX() - this.getX();
++ double d1 = target.getY(0.3333333333333333D) - entitythrowntrident.getY();
+ double d2 = target.getZ() - this.getZ();
+- double squareRoot = Math.sqrt(d * d + d2 * d2);
+- thrownTrident.shoot(d, d1 + squareRoot * 0.2F, d2, 1.6F, (float)(14 - this.level().getDifficulty().getId() * 4));
++ double d3 = Math.sqrt(d0 * d0 + d2 * d2);
++
++ entitythrowntrident.shoot(d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, (float) (14 - this.level().getDifficulty().getId() * 4));
+ this.playSound(SoundEvents.DROWNED_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F));
+- this.level().addFreshEntity(thrownTrident);
++ this.level().addFreshEntity(entitythrowntrident);
+ }
+
+ public void setSearchingForLand(boolean searchingForLand) {
+ this.searchingForLand = searchingForLand;
+ }
+
+- static class DrownedAttackGoal extends ZombieAttackGoal {
++ private static class DrownedMoveControl extends MoveControl {
++
+ private final Drowned drowned;
+
+- public DrownedAttackGoal(Drowned drowned, double speedModifier, boolean followingTargetEvenIfNotSeen) {
+- super(drowned, speedModifier, followingTargetEvenIfNotSeen);
++ public DrownedMoveControl(Drowned drowned) {
++ super(drowned);
+ this.drowned = drowned;
+ }
+
+ @Override
+- public boolean canUse() {
+- return super.canUse() && this.drowned.okTarget(this.drowned.getTarget());
+- }
++ public void tick() {
++ LivingEntity entityliving = this.drowned.getTarget();
+
+- @Override
+- public boolean canContinueToUse() {
+- return super.canContinueToUse() && this.drowned.okTarget(this.drowned.getTarget());
+- }
+- }
++ if (this.drowned.wantsToSwim() && this.drowned.isInWater()) {
++ if (entityliving != null && entityliving.getY() > this.drowned.getY() || this.drowned.searchingForLand) {
++ this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add(0.0D, 0.002D, 0.0D));
++ }
+
+- static class DrownedGoToBeachGoal extends MoveToBlockGoal {
+- private final Drowned drowned;
++ if (this.operation != MoveControl.Operation.MOVE_TO || this.drowned.getNavigation().isDone()) {
++ this.drowned.setSpeed(0.0F);
++ return;
++ }
+
+- public DrownedGoToBeachGoal(Drowned drowned, double speedModifier) {
+- super(drowned, speedModifier, 8, 2);
+- this.drowned = drowned;
+- }
++ double d0 = this.wantedX - this.drowned.getX();
++ double d1 = this.wantedY - this.drowned.getY();
++ double d2 = this.wantedZ - this.drowned.getZ();
++ double d3 = Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
+
+- @Override
+- public boolean canUse() {
+- return super.canUse()
+- && !this.drowned.level().isDay()
+- && this.drowned.isInWater()
+- && this.drowned.getY() >= (double)(this.drowned.level().getSeaLevel() - 3);
+- }
++ d1 /= d3;
++ float f = (float) (Mth.atan2(d2, d0) * 57.2957763671875D) - 90.0F;
+
+- @Override
+- public boolean canContinueToUse() {
+- return super.canContinueToUse();
+- }
++ this.drowned.setYRot(this.rotlerp(this.drowned.getYRot(), f, 90.0F));
++ this.drowned.yBodyRot = this.drowned.getYRot();
++ float f1 = (float) (this.speedModifier * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED));
++ float f2 = Mth.lerp(0.125F, this.drowned.getSpeed(), f1);
+
+- @Override
+- protected boolean isValidTarget(LevelReader level, BlockPos pos) {
+- BlockPos blockPos = pos.above();
+- return level.isEmptyBlock(blockPos) && level.isEmptyBlock(blockPos.above()) && level.getBlockState(pos).entityCanStandOn(level, pos, this.drowned);
+- }
++ this.drowned.setSpeed(f2);
++ this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add((double) f2 * d0 * 0.005D, (double) f2 * d1 * 0.1D, (double) f2 * d2 * 0.005D));
++ } else {
++ if (!this.drowned.onGround()) {
++ this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add(0.0D, -0.008D, 0.0D));
++ }
+
+- @Override
+- public void start() {
+- this.drowned.setSearchingForLand(false);
+- this.drowned.navigation = this.drowned.groundNavigation;
+- super.start();
+- }
++ super.tick();
++ }
+
+- @Override
+- public void stop() {
+- super.stop();
+ }
+ }
+
+- static class DrownedGoToWaterGoal extends Goal {
++ private static class DrownedGoToWaterGoal extends Goal {
++
+ private final PathfinderMob mob;
+ private double wantedX;
+ private double wantedY;
+@@ -337,7 +321,7 @@
+ this.mob = mob;
+ this.speedModifier = speedModifier;
+ this.level = mob.level();
+- this.setFlags(EnumSet.of(Goal.Flag.MOVE));
++ this.setFlags(EnumSet.of(Goal.Type.MOVE));
+ }
+
+ @Override
+@@ -347,13 +331,14 @@
+ } else if (this.mob.isInWater()) {
+ return false;
+ } else {
+- Vec3 waterPos = this.getWaterPos();
+- if (waterPos == null) {
++ Vec3 vec3d = this.getWaterPos();
++
++ if (vec3d == null) {
+ return false;
+ } else {
+- this.wantedX = waterPos.x;
+- this.wantedY = waterPos.y;
+- this.wantedZ = waterPos.z;
++ this.wantedX = vec3d.x;
++ this.wantedY = vec3d.y;
++ this.wantedZ = vec3d.z;
+ return true;
+ }
+ }
+@@ -371,13 +356,14 @@
+
+ @Nullable
+ private Vec3 getWaterPos() {
+- RandomSource random = this.mob.getRandom();
+- BlockPos blockPos = this.mob.blockPosition();
++ RandomSource randomsource = this.mob.getRandom();
++ BlockPos blockposition = this.mob.blockPosition();
+
+- for (int i = 0; i < 10; i++) {
+- BlockPos blockPos1 = blockPos.offset(random.nextInt(20) - 10, 2 - random.nextInt(8), random.nextInt(20) - 10);
+- if (this.level.getBlockState(blockPos1).is(Blocks.WATER)) {
+- return Vec3.atBottomCenterOf(blockPos1);
++ for (int i = 0; i < 10; ++i) {
++ BlockPos blockposition1 = blockposition.offset(randomsource.nextInt(20) - 10, 2 - randomsource.nextInt(8), randomsource.nextInt(20) - 10);
++
++ if (this.level.getBlockState(blockposition1).is(Blocks.WATER)) {
++ return Vec3.atBottomCenterOf(blockposition1);
+ }
+ }
+
+@@ -385,123 +371,141 @@
+ }
+ }
+
+- static class DrownedMoveControl extends MoveControl {
++ private static class DrownedTridentAttackGoal extends RangedAttackGoal {
++
+ private final Drowned drowned;
+
+- public DrownedMoveControl(Drowned mob) {
+- super(mob);
+- this.drowned = mob;
++ public DrownedTridentAttackGoal(RangedAttackMob rangedAttackMob, double speedModifier, int i, float attackInterval) {
++ super(rangedAttackMob, speedModifier, i, attackInterval);
++ this.drowned = (Drowned) rangedAttackMob;
+ }
+
+ @Override
+- public void tick() {
+- LivingEntity target = this.drowned.getTarget();
+- if (this.drowned.wantsToSwim() && this.drowned.isInWater()) {
+- if (target != null && target.getY() > this.drowned.getY() || this.drowned.searchingForLand) {
+- this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add(0.0, 0.002, 0.0));
+- }
++ public boolean canUse() {
++ return super.canUse() && this.drowned.getMainHandItem().is(Items.TRIDENT);
++ }
+
+- if (this.operation != MoveControl.Operation.MOVE_TO || this.drowned.getNavigation().isDone()) {
+- this.drowned.setSpeed(0.0F);
+- return;
+- }
++ @Override
++ public void start() {
++ super.start();
++ this.drowned.setAggressive(true);
++ this.drowned.startUsingItem(EnumHand.MAIN_HAND);
++ }
+
+- double d = this.wantedX - this.drowned.getX();
+- double d1 = this.wantedY - this.drowned.getY();
+- double d2 = this.wantedZ - this.drowned.getZ();
+- double squareRoot = Math.sqrt(d * d + d1 * d1 + d2 * d2);
+- d1 /= squareRoot;
+- float f = (float)(Mth.atan2(d2, d) * 180.0F / (float)Math.PI) - 90.0F;
+- this.drowned.setYRot(this.rotlerp(this.drowned.getYRot(), f, 90.0F));
+- this.drowned.yBodyRot = this.drowned.getYRot();
+- float f1 = (float)(this.speedModifier * this.drowned.getAttributeValue(Attributes.MOVEMENT_SPEED));
+- float f2 = Mth.lerp(0.125F, this.drowned.getSpeed(), f1);
+- this.drowned.setSpeed(f2);
+- this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add((double)f2 * d * 0.005, (double)f2 * d1 * 0.1, (double)f2 * d2 * 0.005));
+- } else {
+- if (!this.drowned.onGround()) {
+- this.drowned.setDeltaMovement(this.drowned.getDeltaMovement().add(0.0, -0.008, 0.0));
+- }
++ @Override
++ public void stop() {
++ super.stop();
++ this.drowned.stopUsingItem();
++ this.drowned.setAggressive(false);
++ }
++ }
+
+- super.tick();
+- }
++ private static class DrownedAttackGoal extends ZombieAttackGoal {
++
++ private final Drowned drowned;
++
++ public DrownedAttackGoal(Drowned drowned, double speedModifier, boolean flag) {
++ super((Zombie) drowned, speedModifier, flag);
++ this.drowned = drowned;
+ }
++
++ @Override
++ public boolean canUse() {
++ return super.canUse() && this.drowned.okTarget(this.drowned.getTarget());
++ }
++
++ @Override
++ public boolean canContinueToUse() {
++ return super.canContinueToUse() && this.drowned.okTarget(this.drowned.getTarget());
++ }
+ }
+
+- static class DrownedSwimUpGoal extends Goal {
++ private static class DrownedGoToBeachGoal extends MoveToBlockGoal {
++
+ private final Drowned drowned;
+- private final double speedModifier;
+- private final int seaLevel;
+- private boolean stuck;
+
+- public DrownedSwimUpGoal(Drowned drowned, double speedModifier, int seaLevel) {
++ public DrownedGoToBeachGoal(Drowned drowned, double speedModifier) {
++ super(drowned, speedModifier, 8, 2);
+ this.drowned = drowned;
+- this.speedModifier = speedModifier;
+- this.seaLevel = seaLevel;
+ }
+
+ @Override
+ public boolean canUse() {
+- return !this.drowned.level().isDay() && this.drowned.isInWater() && this.drowned.getY() < (double)(this.seaLevel - 2);
++ return super.canUse() && !this.drowned.level().isDay() && this.drowned.isInWater() && this.drowned.getY() >= (double) (this.drowned.level().getSeaLevel() - 3);
+ }
+
+ @Override
+ public boolean canContinueToUse() {
+- return this.canUse() && !this.stuck;
++ return super.canContinueToUse();
+ }
+
+ @Override
+- public void tick() {
+- if (this.drowned.getY() < (double)(this.seaLevel - 1) && (this.drowned.getNavigation().isDone() || this.drowned.closeToNextPos())) {
+- Vec3 posTowards = DefaultRandomPos.getPosTowards(
+- this.drowned, 4, 8, new Vec3(this.drowned.getX(), (double)(this.seaLevel - 1), this.drowned.getZ()), (float) (Math.PI / 2)
+- );
+- if (posTowards == null) {
+- this.stuck = true;
+- return;
+- }
++ protected boolean isValidTarget(LevelReader level, BlockPos pos) {
++ BlockPos blockposition1 = pos.above();
+
+- this.drowned.getNavigation().moveTo(posTowards.x, posTowards.y, posTowards.z, this.speedModifier);
+- }
++ return level.isEmptyBlock(blockposition1) && level.isEmptyBlock(blockposition1.above()) ? level.getBlockState(pos).entityCanStandOn(level, pos, this.drowned) : false;
+ }
+
+ @Override
+ public void start() {
+- this.drowned.setSearchingForLand(true);
+- this.stuck = false;
++ this.drowned.setSearchingForLand(false);
++ this.drowned.navigation = this.drowned.groundNavigation;
++ super.start();
+ }
+
+ @Override
+ public void stop() {
+- this.drowned.setSearchingForLand(false);
++ super.stop();
+ }
+ }
+
+- static class DrownedTridentAttackGoal extends RangedAttackGoal {
++ private static class DrownedSwimUpGoal extends Goal {
++
+ private final Drowned drowned;
++ private final double speedModifier;
++ private final int seaLevel;
++ private boolean stuck;
+
+- public DrownedTridentAttackGoal(RangedAttackMob rangedAttackMob, double speedModifier, int attackInterval, float attackRadius) {
+- super(rangedAttackMob, speedModifier, attackInterval, attackRadius);
+- this.drowned = (Drowned)rangedAttackMob;
++ public DrownedSwimUpGoal(Drowned drowned, double speedModifier, int i) {
++ this.drowned = drowned;
++ this.speedModifier = speedModifier;
++ this.seaLevel = i;
+ }
+
+ @Override
+ public boolean canUse() {
+- return super.canUse() && this.drowned.getMainHandItem().is(Items.TRIDENT);
++ return !this.drowned.level().isDay() && this.drowned.isInWater() && this.drowned.getY() < (double) (this.seaLevel - 2);
+ }
+
+ @Override
++ public boolean canContinueToUse() {
++ return this.canUse() && !this.stuck;
++ }
++
++ @Override
++ public void tick() {
++ if (this.drowned.getY() < (double) (this.seaLevel - 1) && (this.drowned.getNavigation().isDone() || this.drowned.closeToNextPos())) {
++ Vec3 vec3d = DefaultRandomPos.getPosTowards(this.drowned, 4, 8, new Vec3(this.drowned.getX(), (double) (this.seaLevel - 1), this.drowned.getZ()), 1.5707963705062866D);
++
++ if (vec3d == null) {
++ this.stuck = true;
++ return;
++ }
++
++ this.drowned.getNavigation().moveTo(vec3d.x, vec3d.y, vec3d.z, this.speedModifier);
++ }
++
++ }
++
++ @Override
+ public void start() {
+- super.start();
+- this.drowned.setAggressive(true);
+- this.drowned.startUsingItem(InteractionHand.MAIN_HAND);
++ this.drowned.setSearchingForLand(true);
++ this.stuck = false;
+ }
+
+ @Override
+ public void stop() {
+- super.stop();
+- this.drowned.stopUsingItem();
+- this.drowned.setAggressive(false);
++ this.drowned.setSearchingForLand(false);
+ }
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/ElderGuardian.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/ElderGuardian.java.patch
new file mode 100644
index 0000000000..52bedd93b6
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/ElderGuardian.java.patch
@@ -0,0 +1,54 @@
+--- a/net/minecraft/world/entity/monster/ElderGuardian.java
++++ b/net/minecraft/world/entity/monster/ElderGuardian.java
+@@ -19,6 +19,7 @@
+ import org.joml.Vector3f;
+
+ public class ElderGuardian extends Guardian {
++
+ public static final float ELDER_SIZE_SCALE = EntityType.ELDER_GUARDIAN.getWidth() / EntityType.GUARDIAN.getWidth();
+ private static final int EFFECT_INTERVAL = 1200;
+ private static final int EFFECT_RADIUS = 50;
+@@ -32,10 +33,11 @@
+ if (this.randomStrollGoal != null) {
+ this.randomStrollGoal.setInterval(400);
+ }
++
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.3F).add(Attributes.ATTACK_DAMAGE, 8.0).add(Attributes.MAX_HEALTH, 80.0);
++ return Guardian.createAttributes().add(Attributes.MOVEMENT_SPEED, 0.30000001192092896D).add(Attributes.ATTACK_DAMAGE, 8.0D).add(Attributes.MAX_HEALTH, 80.0D);
+ }
+
+ @Override
+@@ -67,21 +69,22 @@
+ protected void customServerAiStep() {
+ super.customServerAiStep();
+ if ((this.tickCount + this.getId()) % 1200 == 0) {
+- MobEffectInstance mobEffectInstance = new MobEffectInstance(MobEffects.DIG_SLOWDOWN, 6000, 2);
+- List<ServerPlayer> list = MobEffectUtil.addEffectToPlayersAround((ServerLevel)this.level(), this, this.position(), 50.0, mobEffectInstance, 1200);
+- list.forEach(
+- serverPlayer -> serverPlayer.connection
+- .send(new ClientboundGameEventPacket(ClientboundGameEventPacket.GUARDIAN_ELDER_EFFECT, this.isSilent() ? 0.0F : 1.0F))
+- );
++ MobEffectInstance mobeffect = new MobEffectInstance(MobEffects.DIG_SLOWDOWN, 6000, 2);
++ List<ServerPlayer> list = MobEffectUtil.addEffectToPlayersAround((ServerLevel) this.level(), this, this.position(), 50.0D, mobeffect, 1200, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit
++
++ list.forEach((entityplayer) -> {
++ entityplayer.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.GUARDIAN_ELDER_EFFECT, this.isSilent() ? 0.0F : 1.0F));
++ });
+ }
+
+ if (!this.hasRestriction()) {
+ this.restrictTo(this.blockPosition(), 16);
+ }
++
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height + 0.353125F * f, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height + 0.353125F * f, 0.0F);
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/EnderMan.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/EnderMan.java.patch
new file mode 100644
index 0000000000..56412da320
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/EnderMan.java.patch
@@ -0,0 +1,692 @@
+--- a/net/minecraft/world/entity/monster/EnderMan.java
++++ b/net/minecraft/world/entity/monster/EnderMan.java
+@@ -1,6 +1,7 @@
+ package net.minecraft.world.entity.monster;
+
+ import java.util.EnumSet;
++import java.util.Iterator;
+ import java.util.List;
+ import java.util.Optional;
+ import java.util.UUID;
+@@ -29,10 +30,10 @@
+ import net.minecraft.world.effect.MobEffectInstance;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
++import net.minecraft.world.entity.EntityPose;
+ import net.minecraft.world.entity.EntityType;
+ import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.NeutralMob;
+-import net.minecraft.world.entity.Pose;
+ import net.minecraft.world.entity.ai.attributes.AttributeInstance;
+ import net.minecraft.world.entity.ai.attributes.AttributeModifier;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+@@ -60,7 +61,7 @@
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.level.block.Block;
+ import net.minecraft.world.level.block.Blocks;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import net.minecraft.world.level.gameevent.GameEvent;
+ import net.minecraft.world.level.pathfinder.BlockPathTypes;
+ import net.minecraft.world.level.storage.loot.LootParams;
+@@ -70,16 +71,18 @@
+ import net.minecraft.world.phys.Vec3;
+ import org.joml.Vector3f;
+
++// CraftBukkit start;
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++import org.bukkit.event.entity.EntityTargetEvent;
++// CraftBukkit end
++
+ public class EnderMan extends Monster implements NeutralMob {
++
+ private static final UUID SPEED_MODIFIER_ATTACKING_UUID = UUID.fromString("020E0DFB-87AE-4653-9556-831010E291A0");
+- private static final AttributeModifier SPEED_MODIFIER_ATTACKING = new AttributeModifier(
+- SPEED_MODIFIER_ATTACKING_UUID, "Attacking speed boost", 0.15F, AttributeModifier.Operation.ADDITION
+- );
++ private static final AttributeModifier SPEED_MODIFIER_ATTACKING = new AttributeModifier(EnderMan.SPEED_MODIFIER_ATTACKING_UUID, "Attacking speed boost", 0.15000000596046448D, AttributeModifier.Operation.ADDITION);
+ private static final int DELAY_BETWEEN_CREEPY_STARE_SOUND = 400;
+ private static final int MIN_DEAGGRESSION_TIME = 600;
+- private static final EntityDataAccessor<Optional<BlockState>> DATA_CARRY_STATE = SynchedEntityData.defineId(
+- EnderMan.class, EntityDataSerializers.OPTIONAL_BLOCK_STATE
+- );
++ private static final EntityDataAccessor<Optional<IBlockData>> DATA_CARRY_STATE = SynchedEntityData.defineId(EnderMan.class, EntityDataSerializers.OPTIONAL_BLOCK_STATE);
+ private static final EntityDataAccessor<Boolean> DATA_CREEPY = SynchedEntityData.defineId(EnderMan.class, EntityDataSerializers.BOOLEAN);
+ private static final EntityDataAccessor<Boolean> DATA_STARED_AT = SynchedEntityData.defineId(EnderMan.class, EntityDataSerializers.BOOLEAN);
+ private int lastStareSound = Integer.MIN_VALUE;
+@@ -99,55 +102,64 @@
+ protected void registerGoals() {
+ this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new EnderMan.EndermanFreezeWhenLookedAt(this));
+- this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0, false));
+- this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0, 0.0F));
++ this.goalSelector.addGoal(2, new MeleeAttackGoal(this, 1.0D, false));
++ this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D, 0.0F));
+ this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F));
+ this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
+ this.goalSelector.addGoal(10, new EnderMan.EndermanLeaveBlockGoal(this));
+ this.goalSelector.addGoal(11, new EnderMan.EndermanTakeBlockGoal(this));
+ this.targetSelector.addGoal(1, new EnderMan.EndermanLookForPlayerGoal(this, this::isAngryAt));
+- this.targetSelector.addGoal(2, new HurtByTargetGoal(this));
++ this.targetSelector.addGoal(2, new HurtByTargetGoal(this, new Class[0]));
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Endermite.class, true, false));
+ this.targetSelector.addGoal(4, new ResetUniversalAngerTargetGoal<>(this, false));
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes()
+- .add(Attributes.MAX_HEALTH, 40.0)
+- .add(Attributes.MOVEMENT_SPEED, 0.3F)
+- .add(Attributes.ATTACK_DAMAGE, 7.0)
+- .add(Attributes.FOLLOW_RANGE, 64.0);
++ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 40.0D).add(Attributes.MOVEMENT_SPEED, 0.30000001192092896D).add(Attributes.ATTACK_DAMAGE, 7.0D).add(Attributes.FOLLOW_RANGE, 64.0D);
+ }
+
+ @Override
+ public void setTarget(@Nullable LivingEntity livingEntity) {
+- super.setTarget(livingEntity);
+- AttributeInstance attribute = this.getAttribute(Attributes.MOVEMENT_SPEED);
+- if (livingEntity == null) {
++ // CraftBukkit start - fire event
++ setTarget(livingEntity, EntityTargetEvent.TargetReason.UNKNOWN, true);
++ }
++
++ @Override
++ public boolean setTarget(LivingEntity entityliving, EntityTargetEvent.TargetReason reason, boolean fireEvent) {
++ if (!super.setTarget(entityliving, reason, fireEvent)) {
++ return false;
++ }
++ entityliving = getTarget();
++ // CraftBukkit end
++ AttributeInstance attributemodifiable = this.getAttribute(Attributes.MOVEMENT_SPEED);
++
++ if (entityliving == null) {
+ this.targetChangeTime = 0;
+- this.entityData.set(DATA_CREEPY, false);
+- this.entityData.set(DATA_STARED_AT, false);
+- attribute.removeModifier(SPEED_MODIFIER_ATTACKING.getId());
++ this.entityData.set(EnderMan.DATA_CREEPY, false);
++ this.entityData.set(EnderMan.DATA_STARED_AT, false);
++ attributemodifiable.removeModifier(EnderMan.SPEED_MODIFIER_ATTACKING.getId());
+ } else {
+ this.targetChangeTime = this.tickCount;
+- this.entityData.set(DATA_CREEPY, true);
+- if (!attribute.hasModifier(SPEED_MODIFIER_ATTACKING)) {
+- attribute.addTransientModifier(SPEED_MODIFIER_ATTACKING);
++ this.entityData.set(EnderMan.DATA_CREEPY, true);
++ if (!attributemodifiable.hasModifier(EnderMan.SPEED_MODIFIER_ATTACKING)) {
++ attributemodifiable.addTransientModifier(EnderMan.SPEED_MODIFIER_ATTACKING);
+ }
+ }
++ return true;
++
+ }
+
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_CARRY_STATE, Optional.empty());
+- this.entityData.define(DATA_CREEPY, false);
+- this.entityData.define(DATA_STARED_AT, false);
++ this.entityData.define(EnderMan.DATA_CARRY_STATE, Optional.empty());
++ this.entityData.define(EnderMan.DATA_CREEPY, false);
++ this.entityData.define(EnderMan.DATA_STARED_AT, false);
+ }
+
+ @Override
+ public void startPersistentAngerTimer() {
+- this.setRemainingPersistentAngerTime(PERSISTENT_ANGER_TIME.sample(this.random));
++ this.setRemainingPersistentAngerTime(EnderMan.PERSISTENT_ANGER_TIME.sample(this.random));
+ }
+
+ @Override
+@@ -178,11 +190,12 @@
+ this.level().playLocalSound(this.getX(), this.getEyeY(), this.getZ(), SoundEvents.ENDERMAN_STARE, this.getSoundSource(), 2.5F, 1.0F, false);
+ }
+ }
++
+ }
+
+ @Override
+ public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
+- if (DATA_CREEPY.equals(key) && this.hasBeenStaredAt() && this.level().isClientSide) {
++ if (EnderMan.DATA_CREEPY.equals(key) && this.hasBeenStaredAt() && this.level().isClientSide) {
+ this.playStareSound();
+ }
+
+@@ -192,9 +205,10 @@
+ @Override
+ public void addAdditionalSaveData(CompoundTag compound) {
+ super.addAdditionalSaveData(compound);
+- BlockState carriedBlock = this.getCarriedBlock();
+- if (carriedBlock != null) {
+- compound.put("carriedBlockState", NbtUtils.writeBlockState(carriedBlock));
++ IBlockData iblockdata = this.getCarriedBlock();
++
++ if (iblockdata != null) {
++ compound.put("carriedBlockState", NbtUtils.writeBlockState(iblockdata));
+ }
+
+ this.addPersistentAngerSaveData(compound);
+@@ -203,62 +217,57 @@
+ @Override
+ public void readAdditionalSaveData(CompoundTag compound) {
+ super.readAdditionalSaveData(compound);
+- BlockState blockState = null;
++ IBlockData iblockdata = null;
++
+ if (compound.contains("carriedBlockState", 10)) {
+- blockState = NbtUtils.readBlockState(this.level().holderLookup(Registries.BLOCK), compound.getCompound("carriedBlockState"));
+- if (blockState.isAir()) {
+- blockState = null;
++ iblockdata = NbtUtils.readBlockState(this.level().holderLookup(Registries.BLOCK), compound.getCompound("carriedBlockState"));
++ if (iblockdata.isAir()) {
++ iblockdata = null;
+ }
+ }
+
+- this.setCarriedBlock(blockState);
++ this.setCarriedBlock(iblockdata);
+ this.readPersistentAngerSaveData(this.level(), compound);
+ }
+
+ boolean isLookingAtMe(Player player) {
+- ItemStack itemStack = player.getInventory().armor.get(3);
+- if (itemStack.is(Blocks.CARVED_PUMPKIN.asItem())) {
++ ItemStack itemstack = (ItemStack) player.getInventory().armor.get(3);
++
++ if (itemstack.is(Blocks.CARVED_PUMPKIN.asItem())) {
+ return false;
+ } else {
+- Vec3 vec3 = player.getViewVector(1.0F).normalize();
+- Vec3 vec31 = new Vec3(this.getX() - player.getX(), this.getEyeY() - player.getEyeY(), this.getZ() - player.getZ());
+- double len = vec31.length();
+- vec31 = vec31.normalize();
+- double d = vec3.dot(vec31);
+- return d > 1.0 - 0.025 / len && player.hasLineOfSight(this);
++ Vec3 vec3d = player.getViewVector(1.0F).normalize();
++ Vec3 vec3d1 = new Vec3(this.getX() - player.getX(), this.getEyeY() - player.getEyeY(), this.getZ() - player.getZ());
++ double d0 = vec3d1.length();
++
++ vec3d1 = vec3d1.normalize();
++ double d1 = vec3d.dot(vec3d1);
++
++ return d1 > 1.0D - 0.025D / d0 ? player.hasLineOfSight(this) : false;
+ }
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return 2.55F;
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height - 0.09375F * f, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height - 0.09375F * f, 0.0F);
+ }
+
+ @Override
+ public void aiStep() {
+ if (this.level().isClientSide) {
+- for (int i = 0; i < 2; i++) {
+- this.level()
+- .addParticle(
+- ParticleTypes.PORTAL,
+- this.getRandomX(0.5),
+- this.getRandomY() - 0.25,
+- this.getRandomZ(0.5),
+- (this.random.nextDouble() - 0.5) * 2.0,
+- -this.random.nextDouble(),
+- (this.random.nextDouble() - 0.5) * 2.0
+- );
++ for (int i = 0; i < 2; ++i) {
++ this.level().addParticle(ParticleTypes.PORTAL, this.getRandomX(0.5D), this.getRandomY() - 0.25D, this.getRandomZ(0.5D), (this.random.nextDouble() - 0.5D) * 2.0D, -this.random.nextDouble(), (this.random.nextDouble() - 0.5D) * 2.0D);
+ }
+ }
+
+ this.jumping = false;
+ if (!this.level().isClientSide) {
+- this.updatePersistentAnger((ServerLevel)this.level(), true);
++ this.updatePersistentAnger((ServerLevel) this.level(), true);
+ }
+
+ super.aiStep();
+@@ -272,11 +281,10 @@
+ @Override
+ protected void customServerAiStep() {
+ if (this.level().isDay() && this.tickCount >= this.targetChangeTime + 600) {
+- float lightLevelDependentMagicValue = this.getLightLevelDependentMagicValue();
+- if (lightLevelDependentMagicValue > 0.5F
+- && this.level().canSeeSky(this.blockPosition())
+- && this.random.nextFloat() * 30.0F < (lightLevelDependentMagicValue - 0.4F) * 2.0F) {
+- this.setTarget(null);
++ float f = this.getLightLevelDependentMagicValue();
++
++ if (f > 0.5F && this.level().canSeeSky(this.blockPosition()) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F) {
++ this.setTarget((LivingEntity) null);
+ this.teleport();
+ }
+ }
+@@ -284,49 +292,54 @@
+ super.customServerAiStep();
+ }
+
+- protected boolean teleport() {
++ public boolean teleport() {
+ if (!this.level().isClientSide() && this.isAlive()) {
+- double d = this.getX() + (this.random.nextDouble() - 0.5) * 64.0;
+- double d1 = this.getY() + (double)(this.random.nextInt(64) - 32);
+- double d2 = this.getZ() + (this.random.nextDouble() - 0.5) * 64.0;
+- return this.teleport(d, d1, d2);
++ double d0 = this.getX() + (this.random.nextDouble() - 0.5D) * 64.0D;
++ double d1 = this.getY() + (double) (this.random.nextInt(64) - 32);
++ double d2 = this.getZ() + (this.random.nextDouble() - 0.5D) * 64.0D;
++
++ return this.teleport(d0, d1, d2);
+ } else {
+ return false;
+ }
+ }
+
+- boolean teleportTowards(Entity target) {
+- Vec3 vec3 = new Vec3(this.getX() - target.getX(), this.getY(0.5) - target.getEyeY(), this.getZ() - target.getZ());
+- vec3 = vec3.normalize();
+- double d = 16.0;
+- double d1 = this.getX() + (this.random.nextDouble() - 0.5) * 8.0 - vec3.x * 16.0;
+- double d2 = this.getY() + (double)(this.random.nextInt(16) - 8) - vec3.y * 16.0;
+- double d3 = this.getZ() + (this.random.nextDouble() - 0.5) * 8.0 - vec3.z * 16.0;
++ public boolean teleportTowards(Entity target) {
++ Vec3 vec3d = new Vec3(this.getX() - target.getX(), this.getY(0.5D) - target.getEyeY(), this.getZ() - target.getZ());
++
++ vec3d = vec3d.normalize();
++ double d0 = 16.0D;
++ double d1 = this.getX() + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.x * 16.0D;
++ double d2 = this.getY() + (double) (this.random.nextInt(16) - 8) - vec3d.y * 16.0D;
++ double d3 = this.getZ() + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.z * 16.0D;
++
+ return this.teleport(d1, d2, d3);
+ }
+
+- private boolean teleport(double x, double y, double z) {
+- BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(x, y, z);
++ private boolean teleport(double x, double d1, double y) {
++ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(x, d1, y);
+
+- while (mutableBlockPos.getY() > this.level().getMinBuildHeight() && !this.level().getBlockState(mutableBlockPos).blocksMotion()) {
+- mutableBlockPos.move(Direction.DOWN);
++ while (blockposition_mutableblockposition.getY() > this.level().getMinBuildHeight() && !this.level().getBlockState(blockposition_mutableblockposition).blocksMotion()) {
++ blockposition_mutableblockposition.move(Direction.DOWN);
+ }
+
+- BlockState blockState = this.level().getBlockState(mutableBlockPos);
+- boolean flag = blockState.blocksMotion();
+- boolean isWater = blockState.getFluidState().is(FluidTags.WATER);
+- if (flag && !isWater) {
+- Vec3 vec3 = this.position();
+- boolean flag1 = this.randomTeleport(x, y, z, true);
+- if (flag1) {
+- this.level().gameEvent(GameEvent.TELEPORT, vec3, GameEvent.Context.of(this));
++ IBlockData iblockdata = this.level().getBlockState(blockposition_mutableblockposition);
++ boolean flag = iblockdata.blocksMotion();
++ boolean flag1 = iblockdata.getFluidState().is(FluidTags.WATER);
++
++ if (flag && !flag1) {
++ Vec3 vec3d = this.position();
++ boolean flag2 = this.randomTeleport(x, d1, y, true);
++
++ if (flag2) {
++ this.level().gameEvent(GameEvent.TELEPORT, vec3d, GameEvent.Context.of((Entity) this));
+ if (!this.isSilent()) {
+- this.level().playSound(null, this.xo, this.yo, this.zo, SoundEvents.ENDERMAN_TELEPORT, this.getSoundSource(), 1.0F, 1.0F);
++ this.level().playSound((Player) null, this.xo, this.yo, this.zo, SoundEvents.ENDERMAN_TELEPORT, this.getSoundSource(), 1.0F, 1.0F);
+ this.playSound(SoundEvents.ENDERMAN_TELEPORT, 1.0F, 1.0F);
+ }
+ }
+
+- return flag1;
++ return flag2;
+ } else {
+ return false;
+ }
+@@ -350,28 +363,32 @@
+ @Override
+ protected void dropCustomDeathLoot(DamageSource source, int looting, boolean recentlyHit) {
+ super.dropCustomDeathLoot(source, looting, recentlyHit);
+- BlockState carriedBlock = this.getCarriedBlock();
+- if (carriedBlock != null) {
+- ItemStack itemStack = new ItemStack(Items.DIAMOND_AXE);
+- itemStack.enchant(Enchantments.SILK_TOUCH, 1);
+- LootParams.Builder builder = new LootParams.Builder((ServerLevel)this.level())
+- .withParameter(LootContextParams.ORIGIN, this.position())
+- .withParameter(LootContextParams.TOOL, itemStack)
+- .withOptionalParameter(LootContextParams.THIS_ENTITY, this);
++ IBlockData iblockdata = this.getCarriedBlock();
+
+- for (ItemStack itemStack1 : carriedBlock.getDrops(builder)) {
+- this.spawnAtLocation(itemStack1);
++ if (iblockdata != null) {
++ ItemStack itemstack = new ItemStack(Items.DIAMOND_AXE);
++
++ itemstack.enchant(Enchantments.SILK_TOUCH, 1);
++ LootParams.Builder lootparams_a = (new LootParams.Builder((ServerLevel) this.level())).withParameter(LootContextParams.ORIGIN, this.position()).withParameter(LootContextParams.TOOL, itemstack).withOptionalParameter(LootContextParams.THIS_ENTITY, this);
++ List<ItemStack> list = iblockdata.getDrops(lootparams_a);
++ Iterator iterator = list.iterator();
++
++ while (iterator.hasNext()) {
++ ItemStack itemstack1 = (ItemStack) iterator.next();
++
++ this.spawnAtLocation(itemstack1);
+ }
+ }
++
+ }
+
+- public void setCarriedBlock(@Nullable BlockState state) {
+- this.entityData.set(DATA_CARRY_STATE, Optional.ofNullable(state));
++ public void setCarriedBlock(@Nullable IBlockData state) {
++ this.entityData.set(EnderMan.DATA_CARRY_STATE, Optional.ofNullable(state));
+ }
+
+ @Nullable
+- public BlockState getCarriedBlock() {
+- return this.entityData.get(DATA_CARRY_STATE).orElse(null);
++ public IBlockData getCarriedBlock() {
++ return (IBlockData) ((Optional) this.entityData.get(EnderMan.DATA_CARRY_STATE)).orElse((Object) null);
+ }
+
+ @Override
+@@ -380,17 +397,19 @@
+ return false;
+ } else {
+ boolean flag = source.getDirectEntity() instanceof ThrownPotion;
++ boolean flag1;
++
+ if (!source.is(DamageTypeTags.IS_PROJECTILE) && !flag) {
+- boolean flag1 = super.hurt(source, amount);
++ flag1 = super.hurt(source, amount);
+ if (!this.level().isClientSide() && !(source.getEntity() instanceof LivingEntity) && this.random.nextInt(10) != 0) {
+ this.teleport();
+ }
+
+ return flag1;
+ } else {
+- boolean flag1 = flag && this.hurtWithCleanWater(source, (ThrownPotion)source.getDirectEntity(), amount);
++ flag1 = flag && this.hurtWithCleanWater(source, (ThrownPotion) source.getDirectEntity(), amount);
+
+- for (int i = 0; i < 64; i++) {
++ for (int i = 0; i < 64; ++i) {
+ if (this.teleport()) {
+ return true;
+ }
+@@ -402,23 +421,24 @@
+ }
+
+ private boolean hurtWithCleanWater(DamageSource source, ThrownPotion potion, float amount) {
+- ItemStack item = potion.getItem();
+- Potion potion1 = PotionUtils.getPotion(item);
+- List<MobEffectInstance> mobEffects = PotionUtils.getMobEffects(item);
+- boolean flag = potion1 == Potions.WATER && mobEffects.isEmpty();
+- return flag && super.hurt(source, amount);
++ ItemStack itemstack = potion.getItem();
++ Potion potionregistry = PotionUtils.getPotion(itemstack);
++ List<MobEffectInstance> list = PotionUtils.getMobEffects(itemstack);
++ boolean flag = potionregistry == Potions.WATER && list.isEmpty();
++
++ return flag ? super.hurt(source, amount) : false;
+ }
+
+ public boolean isCreepy() {
+- return this.entityData.get(DATA_CREEPY);
++ return (Boolean) this.entityData.get(EnderMan.DATA_CREEPY);
+ }
+
+ public boolean hasBeenStaredAt() {
+- return this.entityData.get(DATA_STARED_AT);
++ return (Boolean) this.entityData.get(EnderMan.DATA_STARED_AT);
+ }
+
+ public void setBeingStaredAt() {
+- this.entityData.set(DATA_STARED_AT, true);
++ this.entityData.set(EnderMan.DATA_STARED_AT, true);
+ }
+
+ @Override
+@@ -426,14 +446,15 @@
+ return super.requiresCustomPersistence() || this.getCarriedBlock() != null;
+ }
+
+- static class EndermanFreezeWhenLookedAt extends Goal {
++ private static class EndermanFreezeWhenLookedAt extends Goal {
++
+ private final EnderMan enderman;
+ @Nullable
+ private LivingEntity target;
+
+ public EndermanFreezeWhenLookedAt(EnderMan enderman) {
+ this.enderman = enderman;
+- this.setFlags(EnumSet.of(Goal.Flag.JUMP, Goal.Flag.MOVE));
++ this.setFlags(EnumSet.of(Goal.Type.JUMP, Goal.Type.MOVE));
+ }
+
+ @Override
+@@ -442,8 +463,9 @@
+ if (!(this.target instanceof Player)) {
+ return false;
+ } else {
+- double d = this.target.distanceToSqr(this.enderman);
+- return !(d > 256.0) && this.enderman.isLookingAtMe((Player)this.target);
++ double d0 = this.target.distanceToSqr((Entity) this.enderman);
++
++ return d0 > 256.0D ? false : this.enderman.isLookingAtMe((Player) this.target);
+ }
+ }
+
+@@ -458,7 +480,8 @@
+ }
+ }
+
+- static class EndermanLeaveBlockGoal extends Goal {
++ private static class EndermanLeaveBlockGoal extends Goal {
++
+ private final EnderMan enderman;
+
+ public EndermanLeaveBlockGoal(EnderMan enderman) {
+@@ -467,52 +490,81 @@
+
+ @Override
+ public boolean canUse() {
+- return this.enderman.getCarriedBlock() != null
+- && this.enderman.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)
+- && this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0;
++ return this.enderman.getCarriedBlock() == null ? false : (!this.enderman.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(2000)) == 0);
+ }
+
+ @Override
+ public void tick() {
+- RandomSource random = this.enderman.getRandom();
+- Level level = this.enderman.level();
+- int floor = Mth.floor(this.enderman.getX() - 1.0 + random.nextDouble() * 2.0);
+- int floor1 = Mth.floor(this.enderman.getY() + random.nextDouble() * 2.0);
+- int floor2 = Mth.floor(this.enderman.getZ() - 1.0 + random.nextDouble() * 2.0);
+- BlockPos blockPos = new BlockPos(floor, floor1, floor2);
+- BlockState blockState = level.getBlockState(blockPos);
+- BlockPos blockPos1 = blockPos.below();
+- BlockState blockState1 = level.getBlockState(blockPos1);
+- BlockState carriedBlock = this.enderman.getCarriedBlock();
+- if (carriedBlock != null) {
+- BlockState var11 = Block.updateFromNeighbourShapes(carriedBlock, this.enderman.level(), blockPos);
+- if (this.canPlaceBlock(level, blockPos, var11, blockState, blockState1, blockPos1)) {
+- level.setBlock(blockPos, var11, 3);
+- level.gameEvent(GameEvent.BLOCK_PLACE, blockPos, GameEvent.Context.of(this.enderman, var11));
+- this.enderman.setCarriedBlock(null);
++ RandomSource randomsource = this.enderman.getRandom();
++ Level world = this.enderman.level();
++ int i = Mth.floor(this.enderman.getX() - 1.0D + randomsource.nextDouble() * 2.0D);
++ int j = Mth.floor(this.enderman.getY() + randomsource.nextDouble() * 2.0D);
++ int k = Mth.floor(this.enderman.getZ() - 1.0D + randomsource.nextDouble() * 2.0D);
++ BlockPos blockposition = new BlockPos(i, j, k);
++ IBlockData iblockdata = world.getBlockState(blockposition);
++ BlockPos blockposition1 = blockposition.below();
++ IBlockData iblockdata1 = world.getBlockState(blockposition1);
++ IBlockData iblockdata2 = this.enderman.getCarriedBlock();
++
++ if (iblockdata2 != null) {
++ iblockdata2 = Block.updateFromNeighbourShapes(iblockdata2, this.enderman.level(), blockposition);
++ if (this.canPlaceBlock(world, blockposition, iblockdata2, iblockdata, iblockdata1, blockposition1)) {
++ if (CraftEventFactory.callEntityChangeBlockEvent(this.enderman, blockposition, iblockdata2)) { // CraftBukkit - Place event
++ world.setBlock(blockposition, iblockdata2, 3);
++ world.gameEvent(GameEvent.BLOCK_PLACE, blockposition, GameEvent.Context.of(this.enderman, iblockdata2));
++ this.enderman.setCarriedBlock((IBlockData) null);
++ } // CraftBukkit
+ }
++
+ }
+ }
+
+- private boolean canPlaceBlock(
+- Level level,
+- BlockPos destinationPos,
+- BlockState carriedState,
+- BlockState destinationState,
+- BlockState belowDestinationState,
+- BlockPos belowDestinationPos
+- ) {
+- return destinationState.isAir()
+- && !belowDestinationState.isAir()
+- && !belowDestinationState.is(Blocks.BEDROCK)
+- && belowDestinationState.isCollisionShapeFullBlock(level, belowDestinationPos)
+- && carriedState.canSurvive(level, destinationPos)
+- && level.getEntities(this.enderman, AABB.unitCubeFromLowerCorner(Vec3.atLowerCornerOf(destinationPos))).isEmpty();
++ private boolean canPlaceBlock(Level level, BlockPos destinationPos, IBlockData carriedState, IBlockData destinationState, IBlockData belowDestinationState, BlockPos belowDestinationPos) {
++ return destinationState.isAir() && !belowDestinationState.isAir() && !belowDestinationState.is(Blocks.BEDROCK) && belowDestinationState.isCollisionShapeFullBlock(level, belowDestinationPos) && carriedState.canSurvive(level, destinationPos) && level.getEntities(this.enderman, AABB.unitCubeFromLowerCorner(Vec3.atLowerCornerOf(destinationPos))).isEmpty();
+ }
+ }
+
+- static class EndermanLookForPlayerGoal extends NearestAttackableTargetGoal<Player> {
++ private static class EndermanTakeBlockGoal extends Goal {
++
+ private final EnderMan enderman;
++
++ public EndermanTakeBlockGoal(EnderMan enderman) {
++ this.enderman = enderman;
++ }
++
++ @Override
++ public boolean canUse() {
++ return this.enderman.getCarriedBlock() != null ? false : (!this.enderman.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) ? false : this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0);
++ }
++
++ @Override
++ public void tick() {
++ RandomSource randomsource = this.enderman.getRandom();
++ Level world = this.enderman.level();
++ int i = Mth.floor(this.enderman.getX() - 2.0D + randomsource.nextDouble() * 4.0D);
++ int j = Mth.floor(this.enderman.getY() + randomsource.nextDouble() * 3.0D);
++ int k = Mth.floor(this.enderman.getZ() - 2.0D + randomsource.nextDouble() * 4.0D);
++ BlockPos blockposition = new BlockPos(i, j, k);
++ IBlockData iblockdata = world.getBlockState(blockposition);
++ Vec3 vec3d = new Vec3((double) this.enderman.getBlockX() + 0.5D, (double) j + 0.5D, (double) this.enderman.getBlockZ() + 0.5D);
++ Vec3 vec3d1 = new Vec3((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D);
++ BlockHitResult movingobjectpositionblock = world.clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.enderman));
++ boolean flag = movingobjectpositionblock.getBlockPos().equals(blockposition);
++
++ if (iblockdata.is(BlockTags.ENDERMAN_HOLDABLE) && flag) {
++ if (CraftEventFactory.callEntityChangeBlockEvent(this.enderman, blockposition, Blocks.AIR.defaultBlockState())) { // CraftBukkit - Place event
++ world.removeBlock(blockposition, false);
++ world.gameEvent(GameEvent.BLOCK_DESTROY, blockposition, GameEvent.Context.of(this.enderman, iblockdata));
++ this.enderman.setCarriedBlock(iblockdata.getBlock().defaultBlockState());
++ } // CraftBukkit
++ }
++
++ }
++ }
++
++ private static class EndermanLookForPlayerGoal extends NearestAttackableTargetGoal<Player> {
++
++ private final EnderMan enderman;
+ @Nullable
+ private Player pendingTarget;
+ private int aggroTime;
+@@ -524,7 +576,9 @@
+ public EndermanLookForPlayerGoal(EnderMan enderman, @Nullable Predicate<LivingEntity> selectionPredicate) {
+ super(enderman, Player.class, 10, false, false, selectionPredicate);
+ this.enderman = enderman;
+- this.isAngerInducing = entity -> (enderman.isLookingAtMe((Player)entity) || enderman.isAngryAt(entity)) && !enderman.hasIndirectPassenger(entity);
++ this.isAngerInducing = (entityliving) -> {
++ return (enderman.isLookingAtMe((Player) entityliving) || enderman.isAngryAt(entityliving)) && !enderman.hasIndirectPassenger(entityliving);
++ };
+ this.startAggroTargetConditions = TargetingConditions.forCombat().range(this.getFollowDistance()).selector(this.isAngerInducing);
+ }
+
+@@ -574,7 +628,7 @@
+ @Override
+ public void tick() {
+ if (this.enderman.getTarget() == null) {
+- super.setTarget(null);
++ super.setTarget((LivingEntity) null);
+ }
+
+ if (this.pendingTarget != null) {
+@@ -585,56 +639,20 @@
+ }
+ } else {
+ if (this.target != null && !this.enderman.isPassenger()) {
+- if (this.enderman.isLookingAtMe((Player)this.target)) {
+- if (this.target.distanceToSqr(this.enderman) < 16.0) {
++ if (this.enderman.isLookingAtMe((Player) this.target)) {
++ if (this.target.distanceToSqr((Entity) this.enderman) < 16.0D) {
+ this.enderman.teleport();
+ }
+
+ this.teleportTime = 0;
+- } else if (this.target.distanceToSqr(this.enderman) > 256.0
+- && this.teleportTime++ >= this.adjustedTickDelay(30)
+- && this.enderman.teleportTowards(this.target)) {
++ } else if (this.target.distanceToSqr((Entity) this.enderman) > 256.0D && this.teleportTime++ >= this.adjustedTickDelay(30) && this.enderman.teleportTowards(this.target)) {
+ this.teleportTime = 0;
+ }
+ }
+
+ super.tick();
+ }
+- }
+- }
+
+- static class EndermanTakeBlockGoal extends Goal {
+- private final EnderMan enderman;
+-
+- public EndermanTakeBlockGoal(EnderMan enderman) {
+- this.enderman = enderman;
+ }
+-
+- @Override
+- public boolean canUse() {
+- return this.enderman.getCarriedBlock() == null
+- && this.enderman.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)
+- && this.enderman.getRandom().nextInt(reducedTickDelay(20)) == 0;
+- }
+-
+- @Override
+- public void tick() {
+- RandomSource random = this.enderman.getRandom();
+- Level level = this.enderman.level();
+- int floor = Mth.floor(this.enderman.getX() - 2.0 + random.nextDouble() * 4.0);
+- int floor1 = Mth.floor(this.enderman.getY() + random.nextDouble() * 3.0);
+- int floor2 = Mth.floor(this.enderman.getZ() - 2.0 + random.nextDouble() * 4.0);
+- BlockPos blockPos = new BlockPos(floor, floor1, floor2);
+- BlockState blockState = level.getBlockState(blockPos);
+- Vec3 vec3 = new Vec3((double)this.enderman.getBlockX() + 0.5, (double)floor1 + 0.5, (double)this.enderman.getBlockZ() + 0.5);
+- Vec3 vec31 = new Vec3((double)floor + 0.5, (double)floor1 + 0.5, (double)floor2 + 0.5);
+- BlockHitResult blockHitResult = level.clip(new ClipContext(vec3, vec31, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.enderman));
+- boolean flag = blockHitResult.getBlockPos().equals(blockPos);
+- if (blockState.is(BlockTags.ENDERMAN_HOLDABLE) && flag) {
+- level.removeBlock(blockPos, false);
+- level.gameEvent(GameEvent.BLOCK_DESTROY, blockPos, GameEvent.Context.of(this.enderman, blockState));
+- this.enderman.setCarriedBlock(blockState.getBlock().defaultBlockState());
+- }
+- }
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Evoker.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Evoker.java.patch
new file mode 100644
index 0000000000..fd571c6fc7
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Evoker.java.patch
@@ -0,0 +1,410 @@
+--- a/net/minecraft/world/entity/monster/Evoker.java
++++ b/net/minecraft/world/entity/monster/Evoker.java
+@@ -12,10 +12,11 @@
+ import net.minecraft.world.damagesource.DamageSource;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
++import net.minecraft.world.entity.EnumMonsterType;
++import net.minecraft.world.entity.GroupDataEntity;
+ import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.Mob;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.MobType;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+ import net.minecraft.world.entity.ai.goal.AvoidEntityGoal;
+@@ -34,13 +35,14 @@
+ import net.minecraft.world.item.DyeColor;
+ import net.minecraft.world.level.GameRules;
+ import net.minecraft.world.level.Level;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import net.minecraft.world.level.gameevent.GameEvent;
+ import net.minecraft.world.phys.Vec3;
+ import net.minecraft.world.phys.shapes.VoxelShape;
+ import net.minecraft.world.scores.PlayerTeam;
+
+ public class Evoker extends SpellcasterIllager {
++
+ @Nullable
+ private Sheep wololoTarget;
+
+@@ -54,21 +56,21 @@
+ super.registerGoals();
+ this.goalSelector.addGoal(0, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new Evoker.EvokerCastingSpellGoal());
+- this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 0.6, 1.0));
++ this.goalSelector.addGoal(2, new AvoidEntityGoal<>(this, Player.class, 8.0F, 0.6D, 1.0D));
+ this.goalSelector.addGoal(4, new Evoker.EvokerSummonSpellGoal());
+ this.goalSelector.addGoal(5, new Evoker.EvokerAttackSpellGoal());
+ this.goalSelector.addGoal(6, new Evoker.EvokerWololoSpellGoal());
+- this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6));
++ this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D));
+ this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
+ this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
+- this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class).setAlertOthers());
+- this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true).setUnseenMemoryTicks(300));
+- this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false).setUnseenMemoryTicks(300));
++ this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
++ this.targetSelector.addGoal(2, (new NearestAttackableTargetGoal<>(this, Player.class, true)).setUnseenMemoryTicks(300));
++ this.targetSelector.addGoal(3, (new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)).setUnseenMemoryTicks(300));
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, false));
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.5).add(Attributes.FOLLOW_RANGE, 12.0).add(Attributes.MAX_HEALTH, 24.0);
++ return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.5D).add(Attributes.FOLLOW_RANGE, 12.0D).add(Attributes.MAX_HEALTH, 24.0D);
+ }
+
+ @Override
+@@ -98,20 +100,7 @@
+
+ @Override
+ public boolean isAlliedTo(Entity entity) {
+- if (entity == null) {
+- return false;
+- } else if (entity == this) {
+- return true;
+- } else if (super.isAlliedTo(entity)) {
+- return true;
+- } else {
+- return entity instanceof Vex
+- ? this.isAlliedTo(((Vex)entity).getOwner())
+- : entity instanceof LivingEntity
+- && ((LivingEntity)entity).getMobType() == MobType.ILLAGER
+- && this.getTeam() == null
+- && entity.getTeam() == null;
+- }
++ return entity == null ? false : (entity == this ? true : (super.isAlliedTo(entity) ? true : (entity instanceof Vex ? this.isAlliedTo((Entity) ((Vex) entity).getOwner()) : (entity instanceof LivingEntity && ((LivingEntity) entity).getMobType() == EnumMonsterType.ILLAGER ? this.getTeam() == null && entity.getTeam() == null : false))));
+ }
+
+ @Override
+@@ -144,162 +133,192 @@
+ }
+
+ @Override
+- public void applyRaidBuffs(int wave, boolean unusedFalse) {
++ public void applyRaidBuffs(int wave, boolean unusedFalse) {}
++
++ private class EvokerCastingSpellGoal extends SpellcasterIllager.SpellcasterCastingSpellGoal {
++
++ EvokerCastingSpellGoal() {
++ super();
++ }
++
++ @Override
++ public void tick() {
++ if (Evoker.this.getTarget() != null) {
++ Evoker.this.getLookControl().setLookAt(Evoker.this.getTarget(), (float) Evoker.this.getMaxHeadYRot(), (float) Evoker.this.getMaxHeadXRot());
++ } else if (Evoker.this.getWololoTarget() != null) {
++ Evoker.this.getLookControl().setLookAt(Evoker.this.getWololoTarget(), (float) Evoker.this.getMaxHeadYRot(), (float) Evoker.this.getMaxHeadXRot());
++ }
++
++ }
+ }
+
+- class EvokerAttackSpellGoal extends SpellcasterIllager.SpellcasterUseSpellGoal {
++ private class EvokerSummonSpellGoal extends SpellcasterIllager.SpellcasterUseSpellGoal {
++
++ private final TargetingConditions vexCountTargeting = TargetingConditions.forNonCombat().range(16.0D).ignoreLineOfSight().ignoreInvisibilityTesting();
++
++ EvokerSummonSpellGoal() {
++ super();
++ }
++
+ @Override
++ public boolean canUse() {
++ if (!super.canUse()) {
++ return false;
++ } else {
++ int i = Evoker.this.level().getNearbyEntities(Vex.class, this.vexCountTargeting, Evoker.this, Evoker.this.getBoundingBox().inflate(16.0D)).size();
++
++ return Evoker.this.random.nextInt(8) + 1 > i;
++ }
++ }
++
++ @Override
+ protected int getCastingTime() {
+- return 40;
++ return 100;
+ }
+
+ @Override
+ protected int getCastingInterval() {
+- return 100;
++ return 340;
+ }
+
+ @Override
+ protected void performSpellCasting() {
+- LivingEntity target = Evoker.this.getTarget();
+- double min = Math.min(target.getY(), Evoker.this.getY());
+- double d = Math.max(target.getY(), Evoker.this.getY()) + 1.0;
+- float f = (float)Mth.atan2(target.getZ() - Evoker.this.getZ(), target.getX() - Evoker.this.getX());
+- if (Evoker.this.distanceToSqr(target) < 9.0) {
+- for (int i = 0; i < 5; i++) {
+- float f1 = f + (float)i * (float) Math.PI * 0.4F;
+- this.createSpellEntity(Evoker.this.getX() + (double)Mth.cos(f1) * 1.5, Evoker.this.getZ() + (double)Mth.sin(f1) * 1.5, min, d, f1, 0);
+- }
++ ServerLevel worldserver = (ServerLevel) Evoker.this.level();
++ PlayerTeam scoreboardteam = Evoker.this.getTeam();
+
+- for (int i = 0; i < 8; i++) {
+- float f1 = f + (float)i * (float) Math.PI * 2.0F / 8.0F + (float) (Math.PI * 2.0 / 5.0);
+- this.createSpellEntity(Evoker.this.getX() + (double)Mth.cos(f1) * 2.5, Evoker.this.getZ() + (double)Mth.sin(f1) * 2.5, min, d, f1, 3);
+- }
+- } else {
+- for (int i = 0; i < 16; i++) {
+- double d1 = 1.25 * (double)(i + 1);
+- int i1 = 1 * i;
+- this.createSpellEntity(Evoker.this.getX() + (double)Mth.cos(f) * d1, Evoker.this.getZ() + (double)Mth.sin(f) * d1, min, d, f, i1);
+- }
+- }
+- }
++ for (int i = 0; i < 3; ++i) {
++ BlockPos blockposition = Evoker.this.blockPosition().offset(-2 + Evoker.this.random.nextInt(5), 1, -2 + Evoker.this.random.nextInt(5));
++ Vex entityvex = (Vex) EntityType.VEX.create(Evoker.this.level());
+
+- private void createSpellEntity(double x, double z, double minY, double maxY, float yRot, int warmupDelay) {
+- BlockPos blockPos = BlockPos.containing(x, maxY, z);
+- boolean flag = false;
+- double d = 0.0;
+-
+- do {
+- BlockPos blockPos1 = blockPos.below();
+- BlockState blockState = Evoker.this.level().getBlockState(blockPos1);
+- if (blockState.isFaceSturdy(Evoker.this.level(), blockPos1, Direction.UP)) {
+- if (!Evoker.this.level().isEmptyBlock(blockPos)) {
+- BlockState blockState1 = Evoker.this.level().getBlockState(blockPos);
+- VoxelShape collisionShape = blockState1.getCollisionShape(Evoker.this.level(), blockPos);
+- if (!collisionShape.isEmpty()) {
+- d = collisionShape.max(Direction.Axis.Y);
+- }
++ if (entityvex != null) {
++ entityvex.moveTo(blockposition, 0.0F, 0.0F);
++ entityvex.finalizeSpawn(worldserver, Evoker.this.level().getCurrentDifficultyAt(blockposition), EnumMobSpawn.MOB_SUMMONED, (GroupDataEntity) null, (CompoundTag) null);
++ entityvex.setOwner(Evoker.this);
++ entityvex.setBoundOrigin(blockposition);
++ entityvex.setLimitedLife(20 * (30 + Evoker.this.random.nextInt(90)));
++ if (scoreboardteam != null) {
++ worldserver.getScoreboard().addPlayerToTeam(entityvex.getScoreboardName(), scoreboardteam);
+ }
+
+- flag = true;
+- break;
++ worldserver.addFreshEntityWithPassengers(entityvex, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPELL); // CraftBukkit - Add SpawnReason
++ worldserver.gameEvent(GameEvent.ENTITY_PLACE, blockposition, GameEvent.Context.of((Entity) Evoker.this));
+ }
+-
+- blockPos = blockPos.below();
+- } while (blockPos.getY() >= Mth.floor(minY) - 1);
+-
+- if (flag) {
+- Evoker.this.level().addFreshEntity(new EvokerFangs(Evoker.this.level(), x, (double)blockPos.getY() + d, z, yRot, warmupDelay, Evoker.this));
+- Evoker.this.level().gameEvent(GameEvent.ENTITY_PLACE, new Vec3(x, (double)blockPos.getY() + d, z), GameEvent.Context.of(Evoker.this));
+ }
++
+ }
+
+ @Override
+ protected SoundEvent getSpellPrepareSound() {
+- return SoundEvents.EVOKER_PREPARE_ATTACK;
++ return SoundEvents.EVOKER_PREPARE_SUMMON;
+ }
+
+ @Override
+ protected SpellcasterIllager.IllagerSpell getSpell() {
+- return SpellcasterIllager.IllagerSpell.FANGS;
++ return SpellcasterIllager.IllagerSpell.SUMMON_VEX;
+ }
+ }
+
+- class EvokerCastingSpellGoal extends SpellcasterIllager.SpellcasterCastingSpellGoal {
+- @Override
+- public void tick() {
+- if (Evoker.this.getTarget() != null) {
+- Evoker.this.getLookControl().setLookAt(Evoker.this.getTarget(), (float)Evoker.this.getMaxHeadYRot(), (float)Evoker.this.getMaxHeadXRot());
+- } else if (Evoker.this.getWololoTarget() != null) {
+- Evoker.this.getLookControl().setLookAt(Evoker.this.getWololoTarget(), (float)Evoker.this.getMaxHeadYRot(), (float)Evoker.this.getMaxHeadXRot());
+- }
+- }
+- }
++ private class EvokerAttackSpellGoal extends SpellcasterIllager.SpellcasterUseSpellGoal {
+
+- class EvokerSummonSpellGoal extends SpellcasterIllager.SpellcasterUseSpellGoal {
+- private final TargetingConditions vexCountTargeting = TargetingConditions.forNonCombat().range(16.0).ignoreLineOfSight().ignoreInvisibilityTesting();
+-
+- @Override
+- public boolean canUse() {
+- if (!super.canUse()) {
+- return false;
+- } else {
+- int size = Evoker.this.level()
+- .getNearbyEntities(Vex.class, this.vexCountTargeting, Evoker.this, Evoker.this.getBoundingBox().inflate(16.0))
+- .size();
+- return Evoker.this.random.nextInt(8) + 1 > size;
+- }
++ EvokerAttackSpellGoal() {
++ super();
+ }
+
+ @Override
+ protected int getCastingTime() {
+- return 100;
++ return 40;
+ }
+
+ @Override
+ protected int getCastingInterval() {
+- return 340;
++ return 100;
+ }
+
+ @Override
+ protected void performSpellCasting() {
+- ServerLevel serverLevel = (ServerLevel)Evoker.this.level();
+- PlayerTeam team = Evoker.this.getTeam();
++ LivingEntity entityliving = Evoker.this.getTarget();
++ double d0 = Math.min(entityliving.getY(), Evoker.this.getY());
++ double d1 = Math.max(entityliving.getY(), Evoker.this.getY()) + 1.0D;
++ float f = (float) Mth.atan2(entityliving.getZ() - Evoker.this.getZ(), entityliving.getX() - Evoker.this.getX());
++ int i;
+
+- for (int i = 0; i < 3; i++) {
+- BlockPos blockPos = Evoker.this.blockPosition().offset(-2 + Evoker.this.random.nextInt(5), 1, -2 + Evoker.this.random.nextInt(5));
+- Vex vex = EntityType.VEX.create(Evoker.this.level());
+- if (vex != null) {
+- vex.moveTo(blockPos, 0.0F, 0.0F);
+- vex.finalizeSpawn(serverLevel, Evoker.this.level().getCurrentDifficultyAt(blockPos), MobSpawnType.MOB_SUMMONED, null, null);
+- vex.setOwner(Evoker.this);
+- vex.setBoundOrigin(blockPos);
+- vex.setLimitedLife(20 * (30 + Evoker.this.random.nextInt(90)));
+- if (team != null) {
+- serverLevel.getScoreboard().addPlayerToTeam(vex.getScoreboardName(), team);
++ if (Evoker.this.distanceToSqr((Entity) entityliving) < 9.0D) {
++ float f1;
++
++ for (i = 0; i < 5; ++i) {
++ f1 = f + (float) i * 3.1415927F * 0.4F;
++ this.createSpellEntity(Evoker.this.getX() + (double) Mth.cos(f1) * 1.5D, Evoker.this.getZ() + (double) Mth.sin(f1) * 1.5D, d0, d1, f1, 0);
++ }
++
++ for (i = 0; i < 8; ++i) {
++ f1 = f + (float) i * 3.1415927F * 2.0F / 8.0F + 1.2566371F;
++ this.createSpellEntity(Evoker.this.getX() + (double) Mth.cos(f1) * 2.5D, Evoker.this.getZ() + (double) Mth.sin(f1) * 2.5D, d0, d1, f1, 3);
++ }
++ } else {
++ for (i = 0; i < 16; ++i) {
++ double d2 = 1.25D * (double) (i + 1);
++ int j = 1 * i;
++
++ this.createSpellEntity(Evoker.this.getX() + (double) Mth.cos(f) * d2, Evoker.this.getZ() + (double) Mth.sin(f) * d2, d0, d1, f, j);
++ }
++ }
++
++ }
++
++ private void createSpellEntity(double x, double d1, double z, double d3, float minY, int i) {
++ BlockPos blockposition = BlockPos.containing(x, d3, d1);
++ boolean flag = false;
++ double d4 = 0.0D;
++
++ do {
++ BlockPos blockposition1 = blockposition.below();
++ IBlockData iblockdata = Evoker.this.level().getBlockState(blockposition1);
++
++ if (iblockdata.isFaceSturdy(Evoker.this.level(), blockposition1, Direction.UP)) {
++ if (!Evoker.this.level().isEmptyBlock(blockposition)) {
++ IBlockData iblockdata1 = Evoker.this.level().getBlockState(blockposition);
++ VoxelShape voxelshape = iblockdata1.getCollisionShape(Evoker.this.level(), blockposition);
++
++ if (!voxelshape.isEmpty()) {
++ d4 = voxelshape.max(Direction.Axis.Y);
++ }
+ }
+
+- serverLevel.addFreshEntityWithPassengers(vex);
+- serverLevel.gameEvent(GameEvent.ENTITY_PLACE, blockPos, GameEvent.Context.of(Evoker.this));
++ flag = true;
++ break;
+ }
++
++ blockposition = blockposition.below();
++ } while (blockposition.getY() >= Mth.floor(z) - 1);
++
++ if (flag) {
++ Evoker.this.level().addFreshEntity(new EvokerFangs(Evoker.this.level(), x, (double) blockposition.getY() + d4, d1, minY, i, Evoker.this));
++ Evoker.this.level().gameEvent(GameEvent.ENTITY_PLACE, new Vec3(x, (double) blockposition.getY() + d4, d1), GameEvent.Context.of((Entity) Evoker.this));
+ }
++
+ }
+
+ @Override
+ protected SoundEvent getSpellPrepareSound() {
+- return SoundEvents.EVOKER_PREPARE_SUMMON;
++ return SoundEvents.EVOKER_PREPARE_ATTACK;
+ }
+
+ @Override
+ protected SpellcasterIllager.IllagerSpell getSpell() {
+- return SpellcasterIllager.IllagerSpell.SUMMON_VEX;
++ return SpellcasterIllager.IllagerSpell.FANGS;
+ }
+ }
+
+ public class EvokerWololoSpellGoal extends SpellcasterIllager.SpellcasterUseSpellGoal {
+- private final TargetingConditions wololoTargeting = TargetingConditions.forNonCombat()
+- .range(16.0)
+- .selector(entity -> ((Sheep)entity).getColor() == DyeColor.BLUE);
+
++ private final TargetingConditions wololoTargeting = TargetingConditions.forNonCombat().range(16.0D).selector((entityliving) -> {
++ return ((Sheep) entityliving).getColor() == DyeColor.BLUE;
++ });
++
++ public EvokerWololoSpellGoal() {
++ super();
++ }
++
+ @Override
+ public boolean canUse() {
+ if (Evoker.this.getTarget() != null) {
+@@ -311,12 +330,12 @@
+ } else if (!Evoker.this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
+ return false;
+ } else {
+- List<Sheep> nearbyEntities = Evoker.this.level()
+- .getNearbyEntities(Sheep.class, this.wololoTargeting, Evoker.this, Evoker.this.getBoundingBox().inflate(16.0, 4.0, 16.0));
+- if (nearbyEntities.isEmpty()) {
++ List<Sheep> list = Evoker.this.level().getNearbyEntities(Sheep.class, this.wololoTargeting, Evoker.this, Evoker.this.getBoundingBox().inflate(16.0D, 4.0D, 16.0D));
++
++ if (list.isEmpty()) {
+ return false;
+ } else {
+- Evoker.this.setWololoTarget(nearbyEntities.get(Evoker.this.random.nextInt(nearbyEntities.size())));
++ Evoker.this.setWololoTarget((Sheep) list.get(Evoker.this.random.nextInt(list.size())));
+ return true;
+ }
+ }
+@@ -330,15 +349,17 @@
+ @Override
+ public void stop() {
+ super.stop();
+- Evoker.this.setWololoTarget(null);
++ Evoker.this.setWololoTarget((Sheep) null);
+ }
+
+ @Override
+ protected void performSpellCasting() {
+- Sheep wololoTarget = Evoker.this.getWololoTarget();
+- if (wololoTarget != null && wololoTarget.isAlive()) {
+- wololoTarget.setColor(DyeColor.RED);
++ Sheep entitysheep = Evoker.this.getWololoTarget();
++
++ if (entitysheep != null && entitysheep.isAlive()) {
++ entitysheep.setColor(DyeColor.RED);
+ }
++
+ }
+
+ @Override
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Ghast.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Ghast.java.patch
new file mode 100644
index 0000000000..883eb23c05
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Ghast.java.patch
@@ -0,0 +1,392 @@
+--- a/net/minecraft/world/entity/monster/Ghast.java
++++ b/net/minecraft/world/entity/monster/Ghast.java
+@@ -15,12 +15,12 @@
+ 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.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
+ import net.minecraft.world.entity.FlyingMob;
+ import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.Mob;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.Pose;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+ import net.minecraft.world.entity.ai.control.MoveControl;
+@@ -34,7 +34,8 @@
+ import net.minecraft.world.phys.Vec3;
+ import org.joml.Vector3f;
+
+-public class Ghast extends FlyingMob implements Enemy {
++public class Ghast extends FlyingMob implements IMonster {
++
+ private static final EntityDataAccessor<Boolean> DATA_IS_CHARGING = SynchedEntityData.defineId(Ghast.class, EntityDataSerializers.BOOLEAN);
+ private int explosionPower = 1;
+
+@@ -49,18 +50,17 @@
+ this.goalSelector.addGoal(5, new Ghast.RandomFloatAroundGoal(this));
+ this.goalSelector.addGoal(7, new Ghast.GhastLookGoal(this));
+ this.goalSelector.addGoal(7, new Ghast.GhastShootFireballGoal(this));
+- this.targetSelector
+- .addGoal(
+- 1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, livingEntity -> Math.abs(livingEntity.getY() - this.getY()) <= 4.0)
+- );
++ this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entityliving) -> {
++ return Math.abs(entityliving.getY() - this.getY()) <= 4.0D;
++ }));
+ }
+
+ public boolean isCharging() {
+- return this.entityData.get(DATA_IS_CHARGING);
++ return (Boolean) this.entityData.get(Ghast.DATA_IS_CHARGING);
+ }
+
+ public void setCharging(boolean charging) {
+- this.entityData.set(DATA_IS_CHARGING, charging);
++ this.entityData.set(Ghast.DATA_IS_CHARGING, charging);
+ }
+
+ public int getExplosionPower() {
+@@ -87,18 +87,18 @@
+ super.hurt(source, 1000.0F);
+ return true;
+ } else {
+- return !this.isInvulnerableTo(source) && super.hurt(source, amount);
++ return this.isInvulnerableTo(source) ? false : super.hurt(source, amount);
+ }
+ }
+
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_IS_CHARGING, false);
++ this.entityData.define(Ghast.DATA_IS_CHARGING, false);
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0).add(Attributes.FOLLOW_RANGE, 100.0);
++ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 10.0D).add(Attributes.FOLLOW_RANGE, 100.0D);
+ }
+
+ @Override
+@@ -126,7 +126,7 @@
+ return 5.0F;
+ }
+
+- public static boolean checkGhastSpawnRules(EntityType<Ghast> ghast, LevelAccessor level, MobSpawnType spawnType, BlockPos pos, RandomSource random) {
++ public static boolean checkGhastSpawnRules(EntityType<Ghast> ghast, LevelAccessor level, EnumMobSpawn spawnType, BlockPos pos, RandomSource random) {
+ return level.getDifficulty() != Difficulty.PEACEFUL && random.nextInt(20) == 0 && checkMobSpawnRules(ghast, level, spawnType, pos, random);
+ }
+
+@@ -136,8 +136,8 @@
+ }
+
+ @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
+@@ -148,7 +148,7 @@
+ @Override
+ public void addAdditionalSaveData(CompoundTag compound) {
+ super.addAdditionalSaveData(compound);
+- compound.putByte("ExplosionPower", (byte)this.explosionPower);
++ compound.putByte("ExplosionPower", (byte) this.explosionPower);
+ }
+
+ @Override
+@@ -157,91 +157,142 @@
+ if (compound.contains("ExplosionPower", 99)) {
+ this.explosionPower = compound.getByte("ExplosionPower");
+ }
++
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return 2.6F;
+ }
+
+- static class GhastLookGoal extends Goal {
++ private static class GhastMoveControl extends MoveControl {
++
+ private final Ghast ghast;
++ private int floatDuration;
+
+- public GhastLookGoal(Ghast ghast) {
++ public GhastMoveControl(Ghast ghast) {
++ super(ghast);
+ this.ghast = ghast;
+- this.setFlags(EnumSet.of(Goal.Flag.LOOK));
+ }
+
+ @Override
+- public boolean canUse() {
+- return true;
++ public void tick() {
++ if (this.operation == MoveControl.Operation.MOVE_TO) {
++ if (this.floatDuration-- <= 0) {
++ this.floatDuration += this.ghast.getRandom().nextInt(5) + 2;
++ Vec3 vec3d = new Vec3(this.wantedX - this.ghast.getX(), this.wantedY - this.ghast.getY(), this.wantedZ - this.ghast.getZ());
++ double d0 = vec3d.length();
++
++ vec3d = vec3d.normalize();
++ if (this.canReach(vec3d, Mth.ceil(d0))) {
++ this.ghast.setDeltaMovement(this.ghast.getDeltaMovement().add(vec3d.scale(0.1D)));
++ } else {
++ this.operation = MoveControl.Operation.WAIT;
++ }
++ }
++
++ }
+ }
+
+- @Override
+- public boolean requiresUpdateEveryTick() {
++ private boolean canReach(Vec3 pos, int length) {
++ AABB axisalignedbb = this.ghast.getBoundingBox();
++
++ for (int j = 1; j < length; ++j) {
++ axisalignedbb = axisalignedbb.move(pos);
++ if (!this.ghast.level().noCollision(this.ghast, axisalignedbb)) {
++ return false;
++ }
++ }
++
+ return true;
+ }
++ }
+
++ private static class RandomFloatAroundGoal extends Goal {
++
++ private final Ghast ghast;
++
++ public RandomFloatAroundGoal(Ghast ghast) {
++ this.ghast = ghast;
++ this.setFlags(EnumSet.of(Goal.Type.MOVE));
++ }
++
+ @Override
+- public void tick() {
+- if (this.ghast.getTarget() == null) {
+- Vec3 deltaMovement = this.ghast.getDeltaMovement();
+- this.ghast.setYRot(-((float)Mth.atan2(deltaMovement.x, deltaMovement.z)) * (180.0F / (float)Math.PI));
+- this.ghast.yBodyRot = this.ghast.getYRot();
++ public boolean canUse() {
++ MoveControl controllermove = this.ghast.getMoveControl();
++
++ if (!controllermove.hasWanted()) {
++ return true;
+ } else {
+- LivingEntity target = this.ghast.getTarget();
+- double d = 64.0;
+- if (target.distanceToSqr(this.ghast) < 4096.0) {
+- double d1 = target.getX() - this.ghast.getX();
+- double d2 = target.getZ() - this.ghast.getZ();
+- this.ghast.setYRot(-((float)Mth.atan2(d1, d2)) * (180.0F / (float)Math.PI));
+- this.ghast.yBodyRot = this.ghast.getYRot();
+- }
++ double d0 = controllermove.getWantedX() - this.ghast.getX();
++ double d1 = controllermove.getWantedY() - this.ghast.getY();
++ double d2 = controllermove.getWantedZ() - this.ghast.getZ();
++ double d3 = d0 * d0 + d1 * d1 + d2 * d2;
++
++ return d3 < 1.0D || d3 > 3600.0D;
+ }
+ }
++
++ @Override
++ public boolean canContinueToUse() {
++ return false;
++ }
++
++ @Override
++ public void start() {
++ RandomSource randomsource = this.ghast.getRandom();
++ double d0 = this.ghast.getX() + (double) ((randomsource.nextFloat() * 2.0F - 1.0F) * 16.0F);
++ double d1 = this.ghast.getY() + (double) ((randomsource.nextFloat() * 2.0F - 1.0F) * 16.0F);
++ double d2 = this.ghast.getZ() + (double) ((randomsource.nextFloat() * 2.0F - 1.0F) * 16.0F);
++
++ this.ghast.getMoveControl().setWantedPosition(d0, d1, d2, 1.0D);
++ }
+ }
+
+- static class GhastMoveControl extends MoveControl {
++ private static class GhastLookGoal extends Goal {
++
+ private final Ghast ghast;
+- private int floatDuration;
+
+- public GhastMoveControl(Ghast mob) {
+- super(mob);
+- this.ghast = mob;
++ public GhastLookGoal(Ghast ghast) {
++ this.ghast = ghast;
++ this.setFlags(EnumSet.of(Goal.Type.LOOK));
+ }
+
+ @Override
+- public void tick() {
+- if (this.operation == MoveControl.Operation.MOVE_TO) {
+- if (this.floatDuration-- <= 0) {
+- this.floatDuration = this.floatDuration + this.ghast.getRandom().nextInt(5) + 2;
+- Vec3 vec3 = new Vec3(this.wantedX - this.ghast.getX(), this.wantedY - this.ghast.getY(), this.wantedZ - this.ghast.getZ());
+- double len = vec3.length();
+- Vec3 var4 = vec3.normalize();
+- if (this.canReach(var4, Mth.ceil(len))) {
+- this.ghast.setDeltaMovement(this.ghast.getDeltaMovement().add(var4.scale(0.1)));
+- } else {
+- this.operation = MoveControl.Operation.WAIT;
+- }
+- }
+- }
++ public boolean canUse() {
++ return true;
+ }
+
+- private boolean canReach(Vec3 pos, int length) {
+- AABB boundingBox = this.ghast.getBoundingBox();
++ @Override
++ public boolean requiresUpdateEveryTick() {
++ return true;
++ }
+
+- for (int i = 1; i < length; i++) {
+- boundingBox = boundingBox.move(pos);
+- if (!this.ghast.level().noCollision(this.ghast, boundingBox)) {
+- return false;
++ @Override
++ public void tick() {
++ if (this.ghast.getTarget() == null) {
++ Vec3 vec3d = this.ghast.getDeltaMovement();
++
++ this.ghast.setYRot(-((float) Mth.atan2(vec3d.x, vec3d.z)) * 57.295776F);
++ this.ghast.yBodyRot = this.ghast.getYRot();
++ } else {
++ LivingEntity entityliving = this.ghast.getTarget();
++ double d0 = 64.0D;
++
++ if (entityliving.distanceToSqr((Entity) this.ghast) < 4096.0D) {
++ double d1 = entityliving.getX() - this.ghast.getX();
++ double d2 = entityliving.getZ() - this.ghast.getZ();
++
++ this.ghast.setYRot(-((float) Mth.atan2(d1, d2)) * 57.295776F);
++ this.ghast.yBodyRot = this.ghast.getYRot();
+ }
+ }
+
+- return true;
+ }
+ }
+
+- static class GhastShootFireballGoal extends Goal {
++ private static class GhastShootFireballGoal extends Goal {
++
+ private final Ghast ghast;
+ public int chargeTime;
+
+@@ -271,74 +322,44 @@
+
+ @Override
+ public void tick() {
+- LivingEntity target = this.ghast.getTarget();
+- if (target != null) {
+- double d = 64.0;
+- if (target.distanceToSqr(this.ghast) < 4096.0 && this.ghast.hasLineOfSight(target)) {
+- Level level = this.ghast.level();
+- this.chargeTime++;
++ LivingEntity entityliving = this.ghast.getTarget();
++
++ if (entityliving != null) {
++ double d0 = 64.0D;
++
++ if (entityliving.distanceToSqr((Entity) this.ghast) < 4096.0D && this.ghast.hasLineOfSight(entityliving)) {
++ Level world = this.ghast.level();
++
++ ++this.chargeTime;
+ if (this.chargeTime == 10 && !this.ghast.isSilent()) {
+- level.levelEvent(null, 1015, this.ghast.blockPosition(), 0);
++ world.levelEvent((Player) null, 1015, this.ghast.blockPosition(), 0);
+ }
+
+ if (this.chargeTime == 20) {
+- double d1 = 4.0;
+- Vec3 viewVector = this.ghast.getViewVector(1.0F);
+- double d2 = target.getX() - (this.ghast.getX() + viewVector.x * 4.0);
+- double d3 = target.getY(0.5) - (0.5 + this.ghast.getY(0.5));
+- double d4 = target.getZ() - (this.ghast.getZ() + viewVector.z * 4.0);
++ double d1 = 4.0D;
++ Vec3 vec3d = this.ghast.getViewVector(1.0F);
++ double d2 = entityliving.getX() - (this.ghast.getX() + vec3d.x * 4.0D);
++ double d3 = entityliving.getY(0.5D) - (0.5D + this.ghast.getY(0.5D));
++ double d4 = entityliving.getZ() - (this.ghast.getZ() + vec3d.z * 4.0D);
++
+ if (!this.ghast.isSilent()) {
+- level.levelEvent(null, 1016, this.ghast.blockPosition(), 0);
++ world.levelEvent((Player) null, 1016, this.ghast.blockPosition(), 0);
+ }
+
+- LargeFireball largeFireball = new LargeFireball(level, this.ghast, d2, d3, d4, this.ghast.getExplosionPower());
+- largeFireball.setPos(this.ghast.getX() + viewVector.x * 4.0, this.ghast.getY(0.5) + 0.5, largeFireball.getZ() + viewVector.z * 4.0);
+- level.addFreshEntity(largeFireball);
++ LargeFireball entitylargefireball = new LargeFireball(world, this.ghast, d2, d3, d4, this.ghast.getExplosionPower());
++
++ // CraftBukkit - set bukkitYield when setting explosionpower
++ entitylargefireball.bukkitYield = entitylargefireball.explosionPower = this.ghast.getExplosionPower();
++ entitylargefireball.setPos(this.ghast.getX() + vec3d.x * 4.0D, this.ghast.getY(0.5D) + 0.5D, entitylargefireball.getZ() + vec3d.z * 4.0D);
++ world.addFreshEntity(entitylargefireball);
+ this.chargeTime = -40;
+ }
+ } else if (this.chargeTime > 0) {
+- this.chargeTime--;
++ --this.chargeTime;
+ }
+
+ this.ghast.setCharging(this.chargeTime > 10);
+ }
+ }
+ }
+-
+- static class RandomFloatAroundGoal extends Goal {
+- private final Ghast ghast;
+-
+- public RandomFloatAroundGoal(Ghast ghast) {
+- this.ghast = ghast;
+- this.setFlags(EnumSet.of(Goal.Flag.MOVE));
+- }
+-
+- @Override
+- public boolean canUse() {
+- MoveControl moveControl = this.ghast.getMoveControl();
+- if (!moveControl.hasWanted()) {
+- return true;
+- } else {
+- double d = moveControl.getWantedX() - this.ghast.getX();
+- double d1 = moveControl.getWantedY() - this.ghast.getY();
+- double d2 = moveControl.getWantedZ() - this.ghast.getZ();
+- double d3 = d * d + d1 * d1 + d2 * d2;
+- return d3 < 1.0 || d3 > 3600.0;
+- }
+- }
+-
+- @Override
+- public boolean canContinueToUse() {
+- return false;
+- }
+-
+- @Override
+- public void start() {
+- RandomSource random = this.ghast.getRandom();
+- double d = this.ghast.getX() + (double)((random.nextFloat() * 2.0F - 1.0F) * 16.0F);
+- double d1 = this.ghast.getY() + (double)((random.nextFloat() * 2.0F - 1.0F) * 16.0F);
+- double d2 = this.ghast.getZ() + (double)((random.nextFloat() * 2.0F - 1.0F) * 16.0F);
+- this.ghast.getMoveControl().setWantedPosition(d, d1, d2, 1.0);
+- }
+- }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Guardian.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Guardian.java.patch
new file mode 100644
index 0000000000..b2e7e561b7
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Guardian.java.patch
@@ -0,0 +1,574 @@
+--- a/net/minecraft/world/entity/monster/Guardian.java
++++ b/net/minecraft/world/entity/monster/Guardian.java
+@@ -19,12 +19,12 @@
+ import net.minecraft.world.damagesource.DamageTypes;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
++import net.minecraft.world.entity.EntityPose;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
++import net.minecraft.world.entity.EnumMonsterType;
++import net.minecraft.world.entity.EnumMoveType;
+ import net.minecraft.world.entity.LivingEntity;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.MobType;
+-import net.minecraft.world.entity.MoverType;
+-import net.minecraft.world.entity.Pose;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+ import net.minecraft.world.entity.ai.control.LookControl;
+@@ -48,6 +48,7 @@
+ import org.joml.Vector3f;
+
+ public class Guardian extends Monster {
++
+ protected static final int ATTACK_TIME = 80;
+ private static final EntityDataAccessor<Boolean> DATA_ID_MOVING = SynchedEntityData.defineId(Guardian.class, EntityDataSerializers.BOOLEAN);
+ private static final EntityDataAccessor<Integer> DATA_ID_ATTACK_TARGET = SynchedEntityData.defineId(Guardian.class, EntityDataSerializers.INT);
+@@ -61,7 +62,8 @@
+ private int clientSideAttackTime;
+ private boolean clientSideTouchedGround;
+ @Nullable
+- protected RandomStrollGoal randomStrollGoal;
++ public RandomStrollGoal randomStrollGoal;
++ public Guardian.GuardianAttackGoal guardianAttackGoal; // CraftBukkit - add field
+
+ public Guardian(EntityType<? extends Guardian> entityType, Level level) {
+ super(entityType, level);
+@@ -74,25 +76,22 @@
+
+ @Override
+ protected void registerGoals() {
+- MoveTowardsRestrictionGoal moveTowardsRestrictionGoal = new MoveTowardsRestrictionGoal(this, 1.0);
+- this.randomStrollGoal = new RandomStrollGoal(this, 1.0, 80);
+- this.goalSelector.addGoal(4, new Guardian.GuardianAttackGoal(this));
+- this.goalSelector.addGoal(5, moveTowardsRestrictionGoal);
++ MoveTowardsRestrictionGoal pathfindergoalmovetowardsrestriction = new MoveTowardsRestrictionGoal(this, 1.0D);
++
++ this.randomStrollGoal = new RandomStrollGoal(this, 1.0D, 80);
++ this.goalSelector.addGoal(4, guardianAttackGoal = new Guardian.GuardianAttackGoal(this)); // CraftBukkit - assign field
++ this.goalSelector.addGoal(5, pathfindergoalmovetowardsrestriction);
+ this.goalSelector.addGoal(7, this.randomStrollGoal);
+ this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F));
+ this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Guardian.class, 12.0F, 0.01F));
+ this.goalSelector.addGoal(9, new RandomLookAroundGoal(this));
+- this.randomStrollGoal.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
+- moveTowardsRestrictionGoal.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
++ this.randomStrollGoal.setFlags(EnumSet.of(Goal.Type.MOVE, Goal.Type.LOOK));
++ pathfindergoalmovetowardsrestriction.setFlags(EnumSet.of(Goal.Type.MOVE, Goal.Type.LOOK));
+ this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, LivingEntity.class, 10, true, false, new Guardian.GuardianAttackSelector(this)));
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes()
+- .add(Attributes.ATTACK_DAMAGE, 6.0)
+- .add(Attributes.MOVEMENT_SPEED, 0.5)
+- .add(Attributes.FOLLOW_RANGE, 16.0)
+- .add(Attributes.MAX_HEALTH, 30.0);
++ return Monster.createMonsterAttributes().add(Attributes.ATTACK_DAMAGE, 6.0D).add(Attributes.MOVEMENT_SPEED, 0.5D).add(Attributes.FOLLOW_RANGE, 16.0D).add(Attributes.MAX_HEALTH, 30.0D);
+ }
+
+ @Override
+@@ -103,33 +102,33 @@
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_ID_MOVING, false);
+- this.entityData.define(DATA_ID_ATTACK_TARGET, 0);
++ this.entityData.define(Guardian.DATA_ID_MOVING, false);
++ this.entityData.define(Guardian.DATA_ID_ATTACK_TARGET, 0);
+ }
+
+ @Override
+- public MobType getMobType() {
+- return MobType.WATER;
++ public EnumMonsterType getMobType() {
++ return EnumMonsterType.WATER;
+ }
+
+ public boolean isMoving() {
+- return this.entityData.get(DATA_ID_MOVING);
++ return (Boolean) this.entityData.get(Guardian.DATA_ID_MOVING);
+ }
+
+ void setMoving(boolean moving) {
+- this.entityData.set(DATA_ID_MOVING, moving);
++ this.entityData.set(Guardian.DATA_ID_MOVING, moving);
+ }
+
+ public int getAttackDuration() {
+ return 80;
+ }
+
+- void setActiveAttackTarget(int activeAttackTargetId) {
+- this.entityData.set(DATA_ID_ATTACK_TARGET, activeAttackTargetId);
++ public void setActiveAttackTarget(int activeAttackTargetId) {
++ this.entityData.set(Guardian.DATA_ID_ATTACK_TARGET, activeAttackTargetId);
+ }
+
+ public boolean hasActiveAttackTarget() {
+- return this.entityData.get(DATA_ID_ATTACK_TARGET) != 0;
++ return (Integer) this.entityData.get(Guardian.DATA_ID_ATTACK_TARGET) != 0;
+ }
+
+ @Nullable
+@@ -140,9 +139,10 @@
+ if (this.clientSideCachedAttackTarget != null) {
+ return this.clientSideCachedAttackTarget;
+ } else {
+- Entity entity = this.level().getEntity(this.entityData.get(DATA_ID_ATTACK_TARGET));
++ Entity entity = this.level().getEntity((Integer) this.entityData.get(Guardian.DATA_ID_ATTACK_TARGET));
++
+ if (entity instanceof LivingEntity) {
+- this.clientSideCachedAttackTarget = (LivingEntity)entity;
++ this.clientSideCachedAttackTarget = (LivingEntity) entity;
+ return this.clientSideCachedAttackTarget;
+ } else {
+ return null;
+@@ -156,10 +156,11 @@
+ @Override
+ public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
+ super.onSyncedDataUpdated(key);
+- if (DATA_ID_ATTACK_TARGET.equals(key)) {
++ if (Guardian.DATA_ID_ATTACK_TARGET.equals(key)) {
+ this.clientSideAttackTime = 0;
+ this.clientSideCachedAttackTarget = null;
+ }
++
+ }
+
+ @Override
+@@ -188,7 +189,7 @@
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return size.height * 0.5F;
+ }
+
+@@ -202,82 +203,68 @@
+ if (this.isAlive()) {
+ if (this.level().isClientSide) {
+ this.clientSideTailAnimationO = this.clientSideTailAnimation;
++ Vec3 vec3d;
++
+ if (!this.isInWater()) {
+ this.clientSideTailAnimationSpeed = 2.0F;
+- Vec3 deltaMovement = this.getDeltaMovement();
+- if (deltaMovement.y > 0.0 && this.clientSideTouchedGround && !this.isSilent()) {
++ vec3d = this.getDeltaMovement();
++ if (vec3d.y > 0.0D && this.clientSideTouchedGround && !this.isSilent()) {
+ this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), this.getFlopSound(), this.getSoundSource(), 1.0F, 1.0F, false);
+ }
+
+- this.clientSideTouchedGround = deltaMovement.y < 0.0 && this.level().loadedAndEntityCanStandOn(this.blockPosition().below(), this);
++ this.clientSideTouchedGround = vec3d.y < 0.0D && this.level().loadedAndEntityCanStandOn(this.blockPosition().below(), this);
+ } else if (this.isMoving()) {
+ if (this.clientSideTailAnimationSpeed < 0.5F) {
+ this.clientSideTailAnimationSpeed = 4.0F;
+ } else {
+- this.clientSideTailAnimationSpeed = this.clientSideTailAnimationSpeed + (0.5F - this.clientSideTailAnimationSpeed) * 0.1F;
++ this.clientSideTailAnimationSpeed += (0.5F - this.clientSideTailAnimationSpeed) * 0.1F;
+ }
+ } else {
+- this.clientSideTailAnimationSpeed = this.clientSideTailAnimationSpeed + (0.125F - this.clientSideTailAnimationSpeed) * 0.2F;
++ this.clientSideTailAnimationSpeed += (0.125F - this.clientSideTailAnimationSpeed) * 0.2F;
+ }
+
+- this.clientSideTailAnimation = this.clientSideTailAnimation + this.clientSideTailAnimationSpeed;
++ this.clientSideTailAnimation += this.clientSideTailAnimationSpeed;
+ this.clientSideSpikesAnimationO = this.clientSideSpikesAnimation;
+ if (!this.isInWaterOrBubble()) {
+ this.clientSideSpikesAnimation = this.random.nextFloat();
+ } else if (this.isMoving()) {
+- this.clientSideSpikesAnimation = this.clientSideSpikesAnimation + (0.0F - this.clientSideSpikesAnimation) * 0.25F;
++ this.clientSideSpikesAnimation += (0.0F - this.clientSideSpikesAnimation) * 0.25F;
+ } else {
+- this.clientSideSpikesAnimation = this.clientSideSpikesAnimation + (1.0F - this.clientSideSpikesAnimation) * 0.06F;
++ this.clientSideSpikesAnimation += (1.0F - this.clientSideSpikesAnimation) * 0.06F;
+ }
+
+ if (this.isMoving() && this.isInWater()) {
+- Vec3 deltaMovement = this.getViewVector(0.0F);
++ vec3d = this.getViewVector(0.0F);
+
+- for (int i = 0; i < 2; i++) {
+- this.level()
+- .addParticle(
+- ParticleTypes.BUBBLE,
+- this.getRandomX(0.5) - deltaMovement.x * 1.5,
+- this.getRandomY() - deltaMovement.y * 1.5,
+- this.getRandomZ(0.5) - deltaMovement.z * 1.5,
+- 0.0,
+- 0.0,
+- 0.0
+- );
++ for (int i = 0; i < 2; ++i) {
++ this.level().addParticle(ParticleTypes.BUBBLE, this.getRandomX(0.5D) - vec3d.x * 1.5D, this.getRandomY() - vec3d.y * 1.5D, this.getRandomZ(0.5D) - vec3d.z * 1.5D, 0.0D, 0.0D, 0.0D);
+ }
+ }
+
+ if (this.hasActiveAttackTarget()) {
+ if (this.clientSideAttackTime < this.getAttackDuration()) {
+- this.clientSideAttackTime++;
++ ++this.clientSideAttackTime;
+ }
+
+- LivingEntity activeAttackTarget = this.getActiveAttackTarget();
+- if (activeAttackTarget != null) {
+- this.getLookControl().setLookAt(activeAttackTarget, 90.0F, 90.0F);
++ LivingEntity entityliving = this.getActiveAttackTarget();
++
++ if (entityliving != null) {
++ this.getLookControl().setLookAt(entityliving, 90.0F, 90.0F);
+ this.getLookControl().tick();
+- double d = (double)this.getAttackAnimationScale(0.0F);
+- double d1 = activeAttackTarget.getX() - this.getX();
+- double d2 = activeAttackTarget.getY(0.5) - this.getEyeY();
+- double d3 = activeAttackTarget.getZ() - this.getZ();
+- double squareRoot = Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3);
+- double var17 = d1 / squareRoot;
+- double var18 = d2 / squareRoot;
+- double var19 = d3 / squareRoot;
+- double randomDouble = this.random.nextDouble();
++ double d0 = (double) this.getAttackAnimationScale(0.0F);
++ double d1 = entityliving.getX() - this.getX();
++ double d2 = entityliving.getY(0.5D) - this.getEyeY();
++ double d3 = entityliving.getZ() - this.getZ();
++ double d4 = Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3);
+
+- while (randomDouble < squareRoot) {
+- randomDouble += 1.8 - d + this.random.nextDouble() * (1.7 - d);
+- this.level()
+- .addParticle(
+- ParticleTypes.BUBBLE,
+- this.getX() + var17 * randomDouble,
+- this.getEyeY() + var18 * randomDouble,
+- this.getZ() + var19 * randomDouble,
+- 0.0,
+- 0.0,
+- 0.0
+- );
++ d1 /= d4;
++ d2 /= d4;
++ d3 /= d4;
++ double d5 = this.random.nextDouble();
++
++ while (d5 < d4) {
++ d5 += 1.8D - d0 + this.random.nextDouble() * (1.7D - d0);
++ this.level().addParticle(ParticleTypes.BUBBLE, this.getX() + d1 * d5, this.getEyeY() + d2 * d5, this.getZ() + d3 * d5, 0.0D, 0.0D, 0.0D);
+ }
+ }
+ }
+@@ -286,10 +273,7 @@
+ if (this.isInWaterOrBubble()) {
+ this.setAirSupply(300);
+ } else if (this.onGround()) {
+- this.setDeltaMovement(
+- this.getDeltaMovement()
+- .add((double)((this.random.nextFloat() * 2.0F - 1.0F) * 0.4F), 0.5, (double)((this.random.nextFloat() * 2.0F - 1.0F) * 0.4F))
+- );
++ this.setDeltaMovement(this.getDeltaMovement().add((double) ((this.random.nextFloat() * 2.0F - 1.0F) * 0.4F), 0.5D, (double) ((this.random.nextFloat() * 2.0F - 1.0F) * 0.4F)));
+ this.setYRot(this.random.nextFloat() * 360.0F);
+ this.setOnGround(false);
+ this.hasImpulse = true;
+@@ -316,11 +300,11 @@
+ }
+
+ public float getAttackAnimationScale(float partialTick) {
+- return ((float)this.clientSideAttackTime + partialTick) / (float)this.getAttackDuration();
++ return ((float) this.clientSideAttackTime + partialTick) / (float) this.getAttackDuration();
+ }
+
+ public float getClientSideAttackTime() {
+- return (float)this.clientSideAttackTime;
++ return (float) this.clientSideAttackTime;
+ }
+
+ @Override
+@@ -328,13 +312,8 @@
+ return level.isUnobstructed(this);
+ }
+
+- public static boolean checkGuardianSpawnRules(
+- EntityType<? extends Guardian> guardian, LevelAccessor level, MobSpawnType spawnType, BlockPos pos, RandomSource random
+- ) {
+- return (random.nextInt(20) == 0 || !level.canSeeSkyFromBelowWater(pos))
+- && level.getDifficulty() != Difficulty.PEACEFUL
+- && (MobSpawnType.isSpawner(spawnType) || level.getFluidState(pos).is(FluidTags.WATER))
+- && level.getFluidState(pos.below()).is(FluidTags.WATER);
++ public static boolean checkGuardianSpawnRules(EntityType<? extends Guardian> guardian, LevelAccessor level, EnumMobSpawn spawnType, BlockPos pos, RandomSource random) {
++ return (random.nextInt(20) == 0 || !level.canSeeSkyFromBelowWater(pos)) && level.getDifficulty() != Difficulty.PEACEFUL && (EnumMobSpawn.isSpawner(spawnType) || level.getFluidState(pos).is(FluidTags.WATER)) && level.getFluidState(pos.below()).is(FluidTags.WATER);
+ }
+
+ @Override
+@@ -342,11 +321,14 @@
+ if (this.level().isClientSide) {
+ return false;
+ } else {
+- if (!this.isMoving()
+- && !source.is(DamageTypeTags.AVOIDS_GUARDIAN_THORNS)
+- && !source.is(DamageTypes.THORNS)
+- && source.getDirectEntity() instanceof LivingEntity livingEntity) {
+- livingEntity.hurt(this.damageSources().thorns(this), 2.0F);
++ if (!this.isMoving() && !source.is(DamageTypeTags.AVOIDS_GUARDIAN_THORNS) && !source.is(DamageTypes.THORNS)) {
++ Entity entity = source.getDirectEntity();
++
++ if (entity instanceof LivingEntity) {
++ LivingEntity entityliving = (LivingEntity) entity;
++
++ entityliving.hurt(this.damageSources().thorns(this), 2.0F);
++ }
+ }
+
+ if (this.randomStrollGoal != null) {
+@@ -366,51 +348,108 @@
+ public void travel(Vec3 travelVector) {
+ if (this.isControlledByLocalInstance() && this.isInWater()) {
+ this.moveRelative(0.1F, travelVector);
+- this.move(MoverType.SELF, this.getDeltaMovement());
+- this.setDeltaMovement(this.getDeltaMovement().scale(0.9));
++ this.move(EnumMoveType.SELF, this.getDeltaMovement());
++ this.setDeltaMovement(this.getDeltaMovement().scale(0.9D));
+ if (!this.isMoving() && this.getTarget() == null) {
+- this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.005, 0.0));
++ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.005D, 0.0D));
+ }
+ } else {
+ super.travel(travelVector);
+ }
++
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height + 0.125F * f, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height + 0.125F * f, 0.0F);
+ }
+
+- static class GuardianAttackGoal extends Goal {
++ private static class GuardianMoveControl extends MoveControl {
++
+ private final Guardian guardian;
+- private int attackTime;
++
++ public GuardianMoveControl(Guardian guardian) {
++ super(guardian);
++ this.guardian = guardian;
++ }
++
++ @Override
++ public void tick() {
++ if (this.operation == MoveControl.Operation.MOVE_TO && !this.guardian.getNavigation().isDone()) {
++ Vec3 vec3d = new Vec3(this.wantedX - this.guardian.getX(), this.wantedY - this.guardian.getY(), this.wantedZ - this.guardian.getZ());
++ double d0 = vec3d.length();
++ double d1 = vec3d.x / d0;
++ double d2 = vec3d.y / d0;
++ double d3 = vec3d.z / d0;
++ float f = (float) (Mth.atan2(vec3d.z, vec3d.x) * 57.2957763671875D) - 90.0F;
++
++ this.guardian.setYRot(this.rotlerp(this.guardian.getYRot(), f, 90.0F));
++ this.guardian.yBodyRot = this.guardian.getYRot();
++ float f1 = (float) (this.speedModifier * this.guardian.getAttributeValue(Attributes.MOVEMENT_SPEED));
++ float f2 = Mth.lerp(0.125F, this.guardian.getSpeed(), f1);
++
++ this.guardian.setSpeed(f2);
++ double d4 = Math.sin((double) (this.guardian.tickCount + this.guardian.getId()) * 0.5D) * 0.05D;
++ double d5 = Math.cos((double) (this.guardian.getYRot() * 0.017453292F));
++ double d6 = Math.sin((double) (this.guardian.getYRot() * 0.017453292F));
++ double d7 = Math.sin((double) (this.guardian.tickCount + this.guardian.getId()) * 0.75D) * 0.05D;
++
++ this.guardian.setDeltaMovement(this.guardian.getDeltaMovement().add(d4 * d5, d7 * (d6 + d5) * 0.25D + (double) f2 * d2 * 0.1D, d4 * d6));
++ LookControl controllerlook = this.guardian.getLookControl();
++ double d8 = this.guardian.getX() + d1 * 2.0D;
++ double d9 = this.guardian.getEyeY() + d2 / d0;
++ double d10 = this.guardian.getZ() + d3 * 2.0D;
++ double d11 = controllerlook.getWantedX();
++ double d12 = controllerlook.getWantedY();
++ double d13 = controllerlook.getWantedZ();
++
++ if (!controllerlook.isLookingAtTarget()) {
++ d11 = d8;
++ d12 = d9;
++ d13 = d10;
++ }
++
++ this.guardian.getLookControl().setLookAt(Mth.lerp(0.125D, d11, d8), Mth.lerp(0.125D, d12, d9), Mth.lerp(0.125D, d13, d10), 10.0F, 40.0F);
++ this.guardian.setMoving(true);
++ } else {
++ this.guardian.setSpeed(0.0F);
++ this.guardian.setMoving(false);
++ }
++ }
++ }
++
++ public static class GuardianAttackGoal extends Goal {
++
++ private final Guardian guardian;
++ public int attackTime;
+ private final boolean elder;
+
+ public GuardianAttackGoal(Guardian guardian) {
+ this.guardian = guardian;
+ this.elder = guardian instanceof ElderGuardian;
+- this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
++ this.setFlags(EnumSet.of(Goal.Type.MOVE, Goal.Type.LOOK));
+ }
+
+ @Override
+ public boolean canUse() {
+- LivingEntity target = this.guardian.getTarget();
+- return target != null && target.isAlive();
++ LivingEntity entityliving = this.guardian.getTarget();
++
++ return entityliving != null && entityliving.isAlive();
+ }
+
+ @Override
+ public boolean canContinueToUse() {
+- return super.canContinueToUse()
+- && (this.elder || this.guardian.getTarget() != null && this.guardian.distanceToSqr(this.guardian.getTarget()) > 9.0);
++ return super.canContinueToUse() && (this.elder || this.guardian.getTarget() != null && this.guardian.distanceToSqr((Entity) this.guardian.getTarget()) > 9.0D);
+ }
+
+ @Override
+ public void start() {
+ this.attackTime = -10;
+ this.guardian.getNavigation().stop();
+- LivingEntity target = this.guardian.getTarget();
+- if (target != null) {
+- this.guardian.getLookControl().setLookAt(target, 90.0F, 90.0F);
++ LivingEntity entityliving = this.guardian.getTarget();
++
++ if (entityliving != null) {
++ this.guardian.getLookControl().setLookAt(entityliving, 90.0F, 90.0F);
+ }
+
+ this.guardian.hasImpulse = true;
+@@ -419,7 +458,7 @@
+ @Override
+ public void stop() {
+ this.guardian.setActiveAttackTarget(0);
+- this.guardian.setTarget(null);
++ this.guardian.setTarget((LivingEntity) null);
+ this.guardian.randomStrollGoal.trigger();
+ }
+
+@@ -430,21 +469,23 @@
+
+ @Override
+ public void tick() {
+- LivingEntity target = this.guardian.getTarget();
+- if (target != null) {
++ LivingEntity entityliving = this.guardian.getTarget();
++
++ if (entityliving != null) {
+ this.guardian.getNavigation().stop();
+- this.guardian.getLookControl().setLookAt(target, 90.0F, 90.0F);
+- if (!this.guardian.hasLineOfSight(target)) {
+- this.guardian.setTarget(null);
++ this.guardian.getLookControl().setLookAt(entityliving, 90.0F, 90.0F);
++ if (!this.guardian.hasLineOfSight(entityliving)) {
++ this.guardian.setTarget((LivingEntity) null);
+ } else {
+- this.attackTime++;
++ ++this.attackTime;
+ if (this.attackTime == 0) {
+- this.guardian.setActiveAttackTarget(target.getId());
++ this.guardian.setActiveAttackTarget(entityliving.getId());
+ if (!this.guardian.isSilent()) {
+- this.guardian.level().broadcastEntityEvent(this.guardian, (byte)21);
++ this.guardian.level().broadcastEntityEvent(this.guardian, (byte) 21);
+ }
+ } else if (this.attackTime >= this.guardian.getAttackDuration()) {
+ float f = 1.0F;
++
+ if (this.guardian.level().getDifficulty() == Difficulty.HARD) {
+ f += 2.0F;
+ }
+@@ -453,9 +494,9 @@
+ f += 2.0F;
+ }
+
+- target.hurt(this.guardian.damageSources().indirectMagic(this.guardian, this.guardian), f);
+- target.hurt(this.guardian.damageSources().mobAttack(this.guardian), (float)this.guardian.getAttributeValue(Attributes.ATTACK_DAMAGE));
+- this.guardian.setTarget(null);
++ entityliving.hurt(this.guardian.damageSources().indirectMagic(this.guardian, this.guardian), f);
++ entityliving.hurt(this.guardian.damageSources().mobAttack(this.guardian), (float) this.guardian.getAttributeValue(Attributes.ATTACK_DAMAGE));
++ this.guardian.setTarget((LivingEntity) null);
+ }
+
+ super.tick();
+@@ -464,67 +505,16 @@
+ }
+ }
+
+- static class GuardianAttackSelector implements Predicate<LivingEntity> {
++ private static class GuardianAttackSelector implements Predicate<LivingEntity> {
++
+ private final Guardian guardian;
+
+ public GuardianAttackSelector(Guardian guardian) {
+ this.guardian = guardian;
+ }
+
+- @Override
+ public boolean test(@Nullable LivingEntity entity) {
+- return (entity instanceof Player || entity instanceof Squid || entity instanceof Axolotl) && entity.distanceToSqr(this.guardian) > 9.0;
++ return (entity instanceof Player || entity instanceof Squid || entity instanceof Axolotl) && entity.distanceToSqr((Entity) this.guardian) > 9.0D;
+ }
+ }
+-
+- static class GuardianMoveControl extends MoveControl {
+- private final Guardian guardian;
+-
+- public GuardianMoveControl(Guardian mob) {
+- super(mob);
+- this.guardian = mob;
+- }
+-
+- @Override
+- public void tick() {
+- if (this.operation == MoveControl.Operation.MOVE_TO && !this.guardian.getNavigation().isDone()) {
+- Vec3 vec3 = new Vec3(this.wantedX - this.guardian.getX(), this.wantedY - this.guardian.getY(), this.wantedZ - this.guardian.getZ());
+- double len = vec3.length();
+- double d = vec3.x / len;
+- double d1 = vec3.y / len;
+- double d2 = vec3.z / len;
+- float f = (float)(Mth.atan2(vec3.z, vec3.x) * 180.0F / (float)Math.PI) - 90.0F;
+- this.guardian.setYRot(this.rotlerp(this.guardian.getYRot(), f, 90.0F));
+- this.guardian.yBodyRot = this.guardian.getYRot();
+- float f1 = (float)(this.speedModifier * this.guardian.getAttributeValue(Attributes.MOVEMENT_SPEED));
+- float f2 = Mth.lerp(0.125F, this.guardian.getSpeed(), f1);
+- this.guardian.setSpeed(f2);
+- double d3 = Math.sin((double)(this.guardian.tickCount + this.guardian.getId()) * 0.5) * 0.05;
+- double cos = Math.cos((double)(this.guardian.getYRot() * (float) (Math.PI / 180.0)));
+- double sin = Math.sin((double)(this.guardian.getYRot() * (float) (Math.PI / 180.0)));
+- double d4 = Math.sin((double)(this.guardian.tickCount + this.guardian.getId()) * 0.75) * 0.05;
+- this.guardian.setDeltaMovement(this.guardian.getDeltaMovement().add(d3 * cos, d4 * (sin + cos) * 0.25 + (double)f2 * d1 * 0.1, d3 * sin));
+- LookControl lookControl = this.guardian.getLookControl();
+- double d5 = this.guardian.getX() + d * 2.0;
+- double d6 = this.guardian.getEyeY() + d1 / len;
+- double d7 = this.guardian.getZ() + d2 * 2.0;
+- double wantedX = lookControl.getWantedX();
+- double wantedY = lookControl.getWantedY();
+- double wantedZ = lookControl.getWantedZ();
+- if (!lookControl.isLookingAtTarget()) {
+- wantedX = d5;
+- wantedY = d6;
+- wantedZ = d7;
+- }
+-
+- this.guardian
+- .getLookControl()
+- .setLookAt(Mth.lerp(0.125, wantedX, d5), Mth.lerp(0.125, wantedY, d6), Mth.lerp(0.125, wantedZ, d7), 10.0F, 40.0F);
+- this.guardian.setMoving(true);
+- } else {
+- this.guardian.setSpeed(0.0F);
+- this.guardian.setMoving(false);
+- }
+- }
+- }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Husk.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Husk.java.patch
new file mode 100644
index 0000000000..d48c4adfd2
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Husk.java.patch
@@ -0,0 +1,63 @@
+--- a/net/minecraft/world/entity/monster/Husk.java
++++ b/net/minecraft/world/entity/monster/Husk.java
+@@ -10,20 +10,22 @@
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
+ import net.minecraft.world.entity.LivingEntity;
+-import net.minecraft.world.entity.MobSpawnType;
++import net.minecraft.world.entity.player.Player;
+ import net.minecraft.world.item.ItemStack;
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.level.ServerLevelAccessor;
+ import org.joml.Vector3f;
+
+ public class Husk extends Zombie {
++
+ public Husk(EntityType<? extends Husk> entityType, Level level) {
+ super(entityType, level);
+ }
+
+- public static boolean checkHuskSpawnRules(EntityType<Husk> husk, ServerLevelAccessor level, MobSpawnType spawnType, BlockPos pos, RandomSource random) {
+- return checkMonsterSpawnRules(husk, level, spawnType, pos, random) && (MobSpawnType.isSpawner(spawnType) || level.canSeeSky(pos));
++ public static boolean checkHuskSpawnRules(EntityType<Husk> husk, ServerLevelAccessor level, EnumMobSpawn spawnType, BlockPos pos, RandomSource random) {
++ return checkMonsterSpawnRules(husk, level, spawnType, pos, random) && (EnumMobSpawn.isSpawner(spawnType) || level.canSeeSky(pos));
+ }
+
+ @Override
+@@ -54,9 +56,11 @@
+ @Override
+ public boolean doHurtTarget(Entity entity) {
+ boolean flag = super.doHurtTarget(entity);
++
+ if (flag && this.getMainHandItem().isEmpty() && entity instanceof LivingEntity) {
+- float effectiveDifficulty = this.level().getCurrentDifficultyAt(this.blockPosition()).getEffectiveDifficulty();
+- ((LivingEntity)entity).addEffect(new MobEffectInstance(MobEffects.HUNGER, 140 * (int)effectiveDifficulty), this);
++ float f = this.level().getCurrentDifficultyAt(this.blockPosition()).getEffectiveDifficulty();
++
++ ((LivingEntity) entity).addEffect(new MobEffectInstance(MobEffects.HUNGER, 140 * (int) f), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit
+ }
+
+ return flag;
+@@ -71,8 +75,9 @@
+ protected void doUnderWaterConversion() {
+ this.convertToZombieType(EntityType.ZOMBIE);
+ if (!this.isSilent()) {
+- this.level().levelEvent(null, 1041, this.blockPosition(), 0);
++ this.level().levelEvent((Player) null, 1041, this.blockPosition(), 0);
+ }
++
+ }
+
+ @Override
+@@ -81,7 +86,7 @@
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height + 0.125F * f, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height + 0.125F * f, 0.0F);
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Illusioner.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Illusioner.java.patch
new file mode 100644
index 0000000000..2c34f0888c
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Illusioner.java.patch
@@ -0,0 +1,324 @@
+--- a/net/minecraft/world/entity/monster/Illusioner.java
++++ b/net/minecraft/world/entity/monster/Illusioner.java
+@@ -12,12 +12,12 @@
+ import net.minecraft.world.effect.MobEffects;
+ import net.minecraft.world.entity.Entity;
+ 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.Mob;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.MobType;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+ import net.minecraft.world.entity.ai.goal.FloatGoal;
+@@ -40,6 +40,7 @@
+ import net.minecraft.world.phys.Vec3;
+
+ public class Illusioner extends SpellcasterIllager implements RangedAttackMob {
++
+ private static final int NUM_ILLUSIONS = 4;
+ private static final int ILLUSION_TRANSITION_TICKS = 3;
+ private static final int ILLUSION_SPREAD = 3;
+@@ -51,10 +52,11 @@
+ this.xpReward = 5;
+ this.clientSideIllusionOffsets = new Vec3[2][4];
+
+- for (int i = 0; i < 4; i++) {
++ for (int i = 0; i < 4; ++i) {
+ this.clientSideIllusionOffsets[0][i] = Vec3.ZERO;
+ this.clientSideIllusionOffsets[1][i] = Vec3.ZERO;
+ }
++
+ }
+
+ @Override
+@@ -64,24 +66,22 @@
+ this.goalSelector.addGoal(1, new SpellcasterIllager.SpellcasterCastingSpellGoal());
+ this.goalSelector.addGoal(4, new Illusioner.IllusionerMirrorSpellGoal());
+ this.goalSelector.addGoal(5, new Illusioner.IllusionerBlindnessSpellGoal());
+- this.goalSelector.addGoal(6, new RangedBowAttackGoal<>(this, 0.5, 20, 15.0F));
+- this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6));
++ this.goalSelector.addGoal(6, new RangedBowAttackGoal<>(this, 0.5D, 20, 15.0F));
++ this.goalSelector.addGoal(8, new RandomStrollGoal(this, 0.6D));
+ this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
+ this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
+- this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class).setAlertOthers());
+- this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true).setUnseenMemoryTicks(300));
+- this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false).setUnseenMemoryTicks(300));
+- this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, false).setUnseenMemoryTicks(300));
++ this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
++ this.targetSelector.addGoal(2, (new NearestAttackableTargetGoal<>(this, Player.class, true)).setUnseenMemoryTicks(300));
++ this.targetSelector.addGoal(3, (new NearestAttackableTargetGoal<>(this, AbstractVillager.class, false)).setUnseenMemoryTicks(300));
++ this.targetSelector.addGoal(3, (new NearestAttackableTargetGoal<>(this, IronGolem.class, false)).setUnseenMemoryTicks(300));
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.5).add(Attributes.FOLLOW_RANGE, 18.0).add(Attributes.MAX_HEALTH, 32.0);
++ return Monster.createMonsterAttributes().add(Attributes.MOVEMENT_SPEED, 0.5D).add(Attributes.FOLLOW_RANGE, 18.0D).add(Attributes.MAX_HEALTH, 32.0D);
+ }
+
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
+ this.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.BOW));
+ return super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
+ }
+@@ -93,47 +93,47 @@
+
+ @Override
+ public AABB getBoundingBoxForCulling() {
+- return this.getBoundingBox().inflate(3.0, 0.0, 3.0);
++ return this.getBoundingBox().inflate(3.0D, 0.0D, 3.0D);
+ }
+
+ @Override
+ public void aiStep() {
+ super.aiStep();
+ if (this.level().isClientSide && this.isInvisible()) {
+- this.clientSideIllusionTicks--;
++ --this.clientSideIllusionTicks;
+ if (this.clientSideIllusionTicks < 0) {
+ this.clientSideIllusionTicks = 0;
+ }
+
+- if (this.hurtTime == 1 || this.tickCount % 1200 == 0) {
++ if (this.hurtTime != 1 && this.tickCount % 1200 != 0) {
++ if (this.hurtTime == this.hurtDuration - 1) {
++ this.clientSideIllusionTicks = 3;
++
++ for (int i = 0; i < 4; ++i) {
++ this.clientSideIllusionOffsets[0][i] = this.clientSideIllusionOffsets[1][i];
++ this.clientSideIllusionOffsets[1][i] = new Vec3(0.0D, 0.0D, 0.0D);
++ }
++ }
++ } else {
+ this.clientSideIllusionTicks = 3;
+ float f = -6.0F;
+- int i = 13;
++ boolean flag = true;
+
+- for (int i1 = 0; i1 < 4; i1++) {
+- this.clientSideIllusionOffsets[0][i1] = this.clientSideIllusionOffsets[1][i1];
+- this.clientSideIllusionOffsets[1][i1] = new Vec3(
+- (double)(-6.0F + (float)this.random.nextInt(13)) * 0.5,
+- (double)Math.max(0, this.random.nextInt(6) - 4),
+- (double)(-6.0F + (float)this.random.nextInt(13)) * 0.5
+- );
+- }
++ int j;
+
+- for (int i1 = 0; i1 < 16; i1++) {
+- this.level().addParticle(ParticleTypes.CLOUD, this.getRandomX(0.5), this.getRandomY(), this.getZ(0.5), 0.0, 0.0, 0.0);
++ for (j = 0; j < 4; ++j) {
++ this.clientSideIllusionOffsets[0][j] = this.clientSideIllusionOffsets[1][j];
++ this.clientSideIllusionOffsets[1][j] = new Vec3((double) (-6.0F + (float) this.random.nextInt(13)) * 0.5D, (double) Math.max(0, this.random.nextInt(6) - 4), (double) (-6.0F + (float) this.random.nextInt(13)) * 0.5D);
+ }
+
+- this.level()
+- .playLocalSound(this.getX(), this.getY(), this.getZ(), SoundEvents.ILLUSIONER_MIRROR_MOVE, this.getSoundSource(), 1.0F, 1.0F, false);
+- } else if (this.hurtTime == this.hurtDuration - 1) {
+- this.clientSideIllusionTicks = 3;
+-
+- for (int i2 = 0; i2 < 4; i2++) {
+- this.clientSideIllusionOffsets[0][i2] = this.clientSideIllusionOffsets[1][i2];
+- this.clientSideIllusionOffsets[1][i2] = new Vec3(0.0, 0.0, 0.0);
++ for (j = 0; j < 16; ++j) {
++ this.level().addParticle(ParticleTypes.CLOUD, this.getRandomX(0.5D), this.getRandomY(), this.getZ(0.5D), 0.0D, 0.0D, 0.0D);
+ }
++
++ this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), SoundEvents.ILLUSIONER_MIRROR_MOVE, this.getSoundSource(), 1.0F, 1.0F, false);
+ }
+ }
++
+ }
+
+ @Override
+@@ -145,22 +145,22 @@
+ if (this.clientSideIllusionTicks <= 0) {
+ return this.clientSideIllusionOffsets[1];
+ } else {
+- double d = (double)(((float)this.clientSideIllusionTicks - partialTick) / 3.0F);
+- double var6 = Math.pow(d, 0.25);
+- Vec3[] vec3s = new Vec3[4];
++ double d0 = (double) (((float) this.clientSideIllusionTicks - partialTick) / 3.0F);
+
+- for (int i = 0; i < 4; i++) {
+- vec3s[i] = this.clientSideIllusionOffsets[1][i].scale(1.0 - var6).add(this.clientSideIllusionOffsets[0][i].scale(var6));
++ d0 = Math.pow(d0, 0.25D);
++ Vec3[] avec3d = new Vec3[4];
++
++ for (int i = 0; i < 4; ++i) {
++ avec3d[i] = this.clientSideIllusionOffsets[1][i].scale(1.0D - d0).add(this.clientSideIllusionOffsets[0][i].scale(d0));
+ }
+
+- return vec3s;
++ return avec3d;
+ }
+ }
+
+ @Override
+ public boolean isAlliedTo(Entity entity) {
+- return super.isAlliedTo(entity)
+- || entity instanceof LivingEntity && ((LivingEntity)entity).getMobType() == MobType.ILLAGER && this.getTeam() == null && entity.getTeam() == null;
++ return super.isAlliedTo(entity) ? true : (entity instanceof LivingEntity && ((LivingEntity) entity).getMobType() == EnumMonsterType.ILLAGER ? this.getTeam() == null && entity.getTeam() == null : false);
+ }
+
+ @Override
+@@ -184,49 +184,36 @@
+ }
+
+ @Override
+- public void applyRaidBuffs(int wave, boolean unusedFalse) {
+- }
++ public void applyRaidBuffs(int wave, boolean unusedFalse) {}
+
+ @Override
+ public void performRangedAttack(LivingEntity target, float distanceFactor) {
+- ItemStack projectile = this.getProjectile(this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW)));
+- AbstractArrow mobArrow = ProjectileUtil.getMobArrow(this, projectile, distanceFactor);
+- double d = target.getX() - this.getX();
+- double d1 = target.getY(0.3333333333333333) - mobArrow.getY();
++ ItemStack itemstack = this.getProjectile(this.getItemInHand(ProjectileUtil.getWeaponHoldingHand(this, Items.BOW)));
++ AbstractArrow entityarrow = ProjectileUtil.getMobArrow(this, itemstack, distanceFactor);
++ double d0 = target.getX() - this.getX();
++ double d1 = target.getY(0.3333333333333333D) - entityarrow.getY();
+ double d2 = target.getZ() - this.getZ();
+- double squareRoot = Math.sqrt(d * d + d2 * d2);
+- mobArrow.shoot(d, d1 + squareRoot * 0.2F, d2, 1.6F, (float)(14 - this.level().getDifficulty().getId() * 4));
++ double d3 = Math.sqrt(d0 * d0 + d2 * d2);
++
++ entityarrow.shoot(d0, d1 + d3 * 0.20000000298023224D, d2, 1.6F, (float) (14 - this.level().getDifficulty().getId() * 4));
+ this.playSound(SoundEvents.SKELETON_SHOOT, 1.0F, 1.0F / (this.getRandom().nextFloat() * 0.4F + 0.8F));
+- this.level().addFreshEntity(mobArrow);
++ this.level().addFreshEntity(entityarrow);
+ }
+
+ @Override
+- public AbstractIllager.IllagerArmPose getArmPose() {
+- if (this.isCastingSpell()) {
+- return AbstractIllager.IllagerArmPose.SPELLCASTING;
+- } else {
+- return this.isAggressive() ? AbstractIllager.IllagerArmPose.BOW_AND_ARROW : AbstractIllager.IllagerArmPose.CROSSED;
+- }
++ public AbstractIllager.a getArmPose() {
++ return this.isCastingSpell() ? AbstractIllager.a.SPELLCASTING : (this.isAggressive() ? AbstractIllager.a.BOW_AND_ARROW : AbstractIllager.a.CROSSED);
+ }
+
+- class IllusionerBlindnessSpellGoal extends SpellcasterIllager.SpellcasterUseSpellGoal {
+- private int lastTargetId;
++ private class IllusionerMirrorSpellGoal extends SpellcasterIllager.SpellcasterUseSpellGoal {
+
+- @Override
+- public boolean canUse() {
+- return super.canUse()
+- && Illusioner.this.getTarget() != null
+- && Illusioner.this.getTarget().getId() != this.lastTargetId
+- && Illusioner.this.level().getCurrentDifficultyAt(Illusioner.this.blockPosition()).isHarderThan((float)Difficulty.NORMAL.ordinal());
++ IllusionerMirrorSpellGoal() {
++ super();
+ }
+
+ @Override
+- public void start() {
+- super.start();
+- LivingEntity target = Illusioner.this.getTarget();
+- if (target != null) {
+- this.lastTargetId = target.getId();
+- }
++ public boolean canUse() {
++ return !super.canUse() ? false : !Illusioner.this.hasEffect(MobEffects.INVISIBILITY);
+ }
+
+ @Override
+@@ -236,55 +223,73 @@
+
+ @Override
+ protected int getCastingInterval() {
+- return 180;
++ return 340;
+ }
+
+ @Override
+ protected void performSpellCasting() {
+- Illusioner.this.getTarget().addEffect(new MobEffectInstance(MobEffects.BLINDNESS, 400), Illusioner.this);
++ Illusioner.this.addEffect(new MobEffectInstance(MobEffects.INVISIBILITY, 1200), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ILLUSION); // CraftBukkit
+ }
+
++ @Nullable
+ @Override
+ protected SoundEvent getSpellPrepareSound() {
+- return SoundEvents.ILLUSIONER_PREPARE_BLINDNESS;
++ return SoundEvents.ILLUSIONER_PREPARE_MIRROR;
+ }
+
+ @Override
+ protected SpellcasterIllager.IllagerSpell getSpell() {
+- return SpellcasterIllager.IllagerSpell.BLINDNESS;
++ return SpellcasterIllager.IllagerSpell.DISAPPEAR;
+ }
+ }
+
+- class IllusionerMirrorSpellGoal extends SpellcasterIllager.SpellcasterUseSpellGoal {
++ private class IllusionerBlindnessSpellGoal extends SpellcasterIllager.SpellcasterUseSpellGoal {
++
++ private int lastTargetId;
++
++ IllusionerBlindnessSpellGoal() {
++ super();
++ }
++
+ @Override
+ public boolean canUse() {
+- return super.canUse() && !Illusioner.this.hasEffect(MobEffects.INVISIBILITY);
++ return !super.canUse() ? false : (Illusioner.this.getTarget() == null ? false : (Illusioner.this.getTarget().getId() == this.lastTargetId ? false : Illusioner.this.level().getCurrentDifficultyAt(Illusioner.this.blockPosition()).isHarderThan((float) Difficulty.NORMAL.ordinal())));
+ }
+
+ @Override
++ public void start() {
++ super.start();
++ LivingEntity entityliving = Illusioner.this.getTarget();
++
++ if (entityliving != null) {
++ this.lastTargetId = entityliving.getId();
++ }
++
++ }
++
++ @Override
+ protected int getCastingTime() {
+ return 20;
+ }
+
+ @Override
+ protected int getCastingInterval() {
+- return 340;
++ return 180;
+ }
+
+ @Override
+ protected void performSpellCasting() {
+- Illusioner.this.addEffect(new MobEffectInstance(MobEffects.INVISIBILITY, 1200));
++ Illusioner.this.getTarget().addEffect(new MobEffectInstance(MobEffects.BLINDNESS, 400), Illusioner.this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit
+ }
+
+- @Nullable
+ @Override
+ protected SoundEvent getSpellPrepareSound() {
+- return SoundEvents.ILLUSIONER_PREPARE_MIRROR;
++ return SoundEvents.ILLUSIONER_PREPARE_BLINDNESS;
+ }
+
+ @Override
+ protected SpellcasterIllager.IllagerSpell getSpell() {
+- return SpellcasterIllager.IllagerSpell.DISAPPEAR;
++ return SpellcasterIllager.IllagerSpell.BLINDNESS;
+ }
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Phantom.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Phantom.java.patch
new file mode 100644
index 0000000000..cf3ac8546b
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Phantom.java.patch
@@ -0,0 +1,668 @@
+--- a/net/minecraft/world/entity/monster/Phantom.java
++++ b/net/minecraft/world/entity/monster/Phantom.java
+@@ -2,6 +2,7 @@
+
+ import java.util.Comparator;
+ import java.util.EnumSet;
++import java.util.Iterator;
+ import java.util.List;
+ import javax.annotation.Nullable;
+ import net.minecraft.core.BlockPos;
+@@ -18,15 +19,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.FlyingMob;
++import net.minecraft.world.entity.GroupDataEntity;
+ import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.Mob;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.MobType;
+-import net.minecraft.world.entity.Pose;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+ import net.minecraft.world.entity.ai.control.BodyRotationControl;
+ import net.minecraft.world.entity.ai.control.LookControl;
+@@ -41,16 +42,20 @@
+ import net.minecraft.world.phys.Vec3;
+ import org.joml.Vector3f;
+
+-public class Phantom extends FlyingMob implements Enemy {
++public class Phantom extends FlyingMob implements IMonster {
++
+ public static final float FLAP_DEGREES_PER_TICK = 7.448451F;
+ public static final int TICKS_PER_FLAP = Mth.ceil(24.166098F);
+ private static final EntityDataAccessor<Integer> ID_SIZE = SynchedEntityData.defineId(Phantom.class, EntityDataSerializers.INT);
+- Vec3 moveTargetPoint = Vec3.ZERO;
+- BlockPos anchorPoint = BlockPos.ZERO;
+- Phantom.AttackPhase attackPhase = Phantom.AttackPhase.CIRCLE;
++ Vec3 moveTargetPoint;
++ BlockPos anchorPoint;
++ Phantom.AttackPhase attackPhase;
+
+ public Phantom(EntityType<? extends Phantom> entityType, Level level) {
+ super(entityType, level);
++ this.moveTargetPoint = Vec3.ZERO;
++ this.anchorPoint = BlockPos.ZERO;
++ this.attackPhase = Phantom.AttackPhase.CIRCLE;
+ this.xpReward = 5;
+ this.moveControl = new Phantom.PhantomMoveControl(this);
+ this.lookControl = new Phantom.PhantomLookControl(this);
+@@ -58,7 +63,7 @@
+
+ @Override
+ public boolean isFlapping() {
+- return (this.getUniqueFlapTickOffset() + this.tickCount) % TICKS_PER_FLAP == 0;
++ return (this.getUniqueFlapTickOffset() + this.tickCount) % Phantom.TICKS_PER_FLAP == 0;
+ }
+
+ @Override
+@@ -77,30 +82,30 @@
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(ID_SIZE, 0);
++ this.entityData.define(Phantom.ID_SIZE, 0);
+ }
+
+ public void setPhantomSize(int phantomSize) {
+- this.entityData.set(ID_SIZE, Mth.clamp(phantomSize, 0, 64));
++ this.entityData.set(Phantom.ID_SIZE, Mth.clamp(phantomSize, 0, 64));
+ }
+
+ private void updatePhantomSizeInfo() {
+ this.refreshDimensions();
+- this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue((double)(6 + this.getPhantomSize()));
++ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue((double) (6 + this.getPhantomSize()));
+ }
+
+ public int getPhantomSize() {
+- return this.entityData.get(ID_SIZE);
++ return (Integer) this.entityData.get(Phantom.ID_SIZE);
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return size.height * 0.35F;
+ }
+
+ @Override
+ public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
+- if (ID_SIZE.equals(key)) {
++ if (Phantom.ID_SIZE.equals(key)) {
+ this.updatePhantomSizeInfo();
+ }
+
+@@ -120,29 +125,22 @@
+ public void tick() {
+ super.tick();
+ if (this.level().isClientSide) {
+- float cos = Mth.cos((float)(this.getUniqueFlapTickOffset() + this.tickCount) * 7.448451F * (float) (Math.PI / 180.0) + (float) Math.PI);
+- float cos1 = Mth.cos((float)(this.getUniqueFlapTickOffset() + this.tickCount + 1) * 7.448451F * (float) (Math.PI / 180.0) + (float) Math.PI);
+- if (cos > 0.0F && cos1 <= 0.0F) {
+- this.level()
+- .playLocalSound(
+- this.getX(),
+- this.getY(),
+- this.getZ(),
+- SoundEvents.PHANTOM_FLAP,
+- this.getSoundSource(),
+- 0.95F + this.random.nextFloat() * 0.05F,
+- 0.95F + this.random.nextFloat() * 0.05F,
+- false
+- );
++ float f = Mth.cos((float) (this.getUniqueFlapTickOffset() + this.tickCount) * 7.448451F * 0.017453292F + 3.1415927F);
++ float f1 = Mth.cos((float) (this.getUniqueFlapTickOffset() + this.tickCount + 1) * 7.448451F * 0.017453292F + 3.1415927F);
++
++ if (f > 0.0F && f1 <= 0.0F) {
++ this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), SoundEvents.PHANTOM_FLAP, this.getSoundSource(), 0.95F + this.random.nextFloat() * 0.05F, 0.95F + this.random.nextFloat() * 0.05F, false);
+ }
+
+- int phantomSize = this.getPhantomSize();
+- float f = Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)) * (1.3F + 0.21F * (float)phantomSize);
+- float f1 = Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)) * (1.3F + 0.21F * (float)phantomSize);
+- float f2 = (0.3F + cos * 0.45F) * ((float)phantomSize * 0.2F + 1.0F);
+- this.level().addParticle(ParticleTypes.MYCELIUM, this.getX() + (double)f, this.getY() + (double)f2, this.getZ() + (double)f1, 0.0, 0.0, 0.0);
+- this.level().addParticle(ParticleTypes.MYCELIUM, this.getX() - (double)f, this.getY() + (double)f2, this.getZ() - (double)f1, 0.0, 0.0, 0.0);
++ int i = this.getPhantomSize();
++ float f2 = Mth.cos(this.getYRot() * 0.017453292F) * (1.3F + 0.21F * (float) i);
++ float f3 = Mth.sin(this.getYRot() * 0.017453292F) * (1.3F + 0.21F * (float) i);
++ float f4 = (0.3F + f * 0.45F) * ((float) i * 0.2F + 1.0F);
++
++ this.level().addParticle(ParticleTypes.MYCELIUM, this.getX() + (double) f2, this.getY() + (double) f4, this.getZ() + (double) f3, 0.0D, 0.0D, 0.0D);
++ this.level().addParticle(ParticleTypes.MYCELIUM, this.getX() - (double) f2, this.getY() + (double) f4, this.getZ() - (double) f3, 0.0D, 0.0D, 0.0D);
+ }
++
+ }
+
+ @Override
+@@ -160,9 +158,7 @@
+ }
+
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
+ this.anchorPoint = this.blockPosition().above(5);
+ this.setPhantomSize(0);
+ return super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
+@@ -213,8 +209,8 @@
+ }
+
+ @Override
+- public MobType getMobType() {
+- return MobType.UNDEAD;
++ public EnumMonsterType getMobType() {
++ return EnumMonsterType.UNDEAD;
+ }
+
+ @Override
+@@ -228,15 +224,16 @@
+ }
+
+ @Override
+- public EntityDimensions getDimensions(Pose pose) {
+- int phantomSize = this.getPhantomSize();
+- EntityDimensions entityDimensions = super.getDimensions(pose);
+- return entityDimensions.scale(1.0F + 0.15F * (float)phantomSize);
++ public EntityDimensions getDimensions(EntityPose pose) {
++ int i = this.getPhantomSize();
++ EntityDimensions entitysize = super.getDimensions(pose);
++
++ return entitysize.scale(1.0F + 0.15F * (float) i);
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height * 0.675F, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height * 0.675F, 0.0F);
+ }
+
+ @Override
+@@ -244,53 +241,102 @@
+ return -0.125F;
+ }
+
+- static enum AttackPhase {
+- CIRCLE,
+- SWOOP;
++ private static enum AttackPhase {
++
++ CIRCLE, SWOOP;
++
++ private AttackPhase() {}
+ }
+
+- class PhantomAttackPlayerTargetGoal extends Goal {
+- private final TargetingConditions attackTargeting = TargetingConditions.forCombat().range(64.0);
+- private int nextScanTick = reducedTickDelay(20);
++ private class PhantomMoveControl extends MoveControl {
+
++ private float speed = 0.1F;
++
++ public PhantomMoveControl(Mob mob) {
++ super(mob);
++ }
++
+ @Override
+- public boolean canUse() {
+- if (this.nextScanTick > 0) {
+- this.nextScanTick--;
+- return false;
+- } else {
+- this.nextScanTick = reducedTickDelay(60);
+- List<Player> nearbyPlayers = Phantom.this.level()
+- .getNearbyPlayers(this.attackTargeting, Phantom.this, Phantom.this.getBoundingBox().inflate(16.0, 64.0, 16.0));
+- if (!nearbyPlayers.isEmpty()) {
+- nearbyPlayers.sort(Comparator.<Player, Double>comparing(Entity::getY).reversed());
++ public void tick() {
++ if (Phantom.this.horizontalCollision) {
++ Phantom.this.setYRot(Phantom.this.getYRot() + 180.0F);
++ this.speed = 0.1F;
++ }
+
+- for (Player player : nearbyPlayers) {
+- if (Phantom.this.canAttack(player, TargetingConditions.DEFAULT)) {
+- Phantom.this.setTarget(player);
+- return true;
+- }
+- }
++ double d0 = Phantom.this.moveTargetPoint.x - Phantom.this.getX();
++ double d1 = Phantom.this.moveTargetPoint.y - Phantom.this.getY();
++ double d2 = Phantom.this.moveTargetPoint.z - Phantom.this.getZ();
++ double d3 = Math.sqrt(d0 * d0 + d2 * d2);
++
++ if (Math.abs(d3) > 9.999999747378752E-6D) {
++ double d4 = 1.0D - Math.abs(d1 * 0.699999988079071D) / d3;
++
++ d0 *= d4;
++ d2 *= d4;
++ d3 = Math.sqrt(d0 * d0 + d2 * d2);
++ double d5 = Math.sqrt(d0 * d0 + d2 * d2 + d1 * d1);
++ float f = Phantom.this.getYRot();
++ float f1 = (float) Mth.atan2(d2, d0);
++ float f2 = Mth.wrapDegrees(Phantom.this.getYRot() + 90.0F);
++ float f3 = Mth.wrapDegrees(f1 * 57.295776F);
++
++ Phantom.this.setYRot(Mth.approachDegrees(f2, f3, 4.0F) - 90.0F);
++ Phantom.this.yBodyRot = Phantom.this.getYRot();
++ if (Mth.degreesDifferenceAbs(f, Phantom.this.getYRot()) < 3.0F) {
++ this.speed = Mth.approach(this.speed, 1.8F, 0.005F * (1.8F / this.speed));
++ } else {
++ this.speed = Mth.approach(this.speed, 0.2F, 0.025F);
+ }
+
+- return false;
++ float f4 = (float) (-(Mth.atan2(-d1, d3) * 57.2957763671875D));
++
++ Phantom.this.setXRot(f4);
++ float f5 = Phantom.this.getYRot() + 90.0F;
++ double d6 = (double) (this.speed * Mth.cos(f5 * 0.017453292F)) * Math.abs(d0 / d5);
++ double d7 = (double) (this.speed * Mth.sin(f5 * 0.017453292F)) * Math.abs(d2 / d5);
++ double d8 = (double) (this.speed * Mth.sin(f4 * 0.017453292F)) * Math.abs(d1 / d5);
++ Vec3 vec3d = Phantom.this.getDeltaMovement();
++
++ Phantom.this.setDeltaMovement(vec3d.add((new Vec3(d6, d8, d7)).subtract(vec3d).scale(0.2D)));
+ }
++
+ }
++ }
+
++ private class PhantomLookControl extends LookControl {
++
++ public PhantomLookControl(Mob mob) {
++ super(mob);
++ }
++
+ @Override
+- public boolean canContinueToUse() {
+- LivingEntity target = Phantom.this.getTarget();
+- return target != null && Phantom.this.canAttack(target, TargetingConditions.DEFAULT);
++ public void tick() {}
++ }
++
++ private class PhantomBodyRotationControl extends BodyRotationControl {
++
++ public PhantomBodyRotationControl(Mob mob) {
++ super(mob);
+ }
++
++ @Override
++ public void clientTick() {
++ Phantom.this.yHeadRot = Phantom.this.yBodyRot;
++ Phantom.this.yBodyRot = Phantom.this.getYRot();
++ }
+ }
+
+- class PhantomAttackStrategyGoal extends Goal {
++ private class PhantomAttackStrategyGoal extends Goal {
++
+ private int nextSweepTick;
+
++ PhantomAttackStrategyGoal() {}
++
+ @Override
+ public boolean canUse() {
+- LivingEntity target = Phantom.this.getTarget();
+- return target != null && Phantom.this.canAttack(target, TargetingConditions.DEFAULT);
++ LivingEntity entityliving = Phantom.this.getTarget();
++
++ return entityliving != null ? Phantom.this.canAttack(entityliving, TargetingConditions.DEFAULT) : false;
+ }
+
+ @Override
+@@ -302,15 +348,13 @@
+
+ @Override
+ public void stop() {
+- Phantom.this.anchorPoint = Phantom.this.level()
+- .getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, Phantom.this.anchorPoint)
+- .above(10 + Phantom.this.random.nextInt(20));
++ Phantom.this.anchorPoint = Phantom.this.level().getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, Phantom.this.anchorPoint).above(10 + Phantom.this.random.nextInt(20));
+ }
+
+ @Override
+ public void tick() {
+ if (Phantom.this.attackPhase == Phantom.AttackPhase.CIRCLE) {
+- this.nextSweepTick--;
++ --this.nextSweepTick;
+ if (this.nextSweepTick <= 0) {
+ Phantom.this.attackPhase = Phantom.AttackPhase.SWOOP;
+ this.setAnchorAboveTarget();
+@@ -318,36 +362,112 @@
+ Phantom.this.playSound(SoundEvents.PHANTOM_SWOOP, 10.0F, 0.95F + Phantom.this.random.nextFloat() * 0.1F);
+ }
+ }
++
+ }
+
+ private void setAnchorAboveTarget() {
+ Phantom.this.anchorPoint = Phantom.this.getTarget().blockPosition().above(20 + Phantom.this.random.nextInt(20));
+ if (Phantom.this.anchorPoint.getY() < Phantom.this.level().getSeaLevel()) {
+- Phantom.this.anchorPoint = new BlockPos(
+- Phantom.this.anchorPoint.getX(), Phantom.this.level().getSeaLevel() + 1, Phantom.this.anchorPoint.getZ()
+- );
++ Phantom.this.anchorPoint = new BlockPos(Phantom.this.anchorPoint.getX(), Phantom.this.level().getSeaLevel() + 1, Phantom.this.anchorPoint.getZ());
+ }
++
+ }
+ }
+
+- class PhantomBodyRotationControl extends BodyRotationControl {
+- public PhantomBodyRotationControl(Mob mob) {
+- super(mob);
++ private class PhantomSweepAttackGoal extends Phantom.h {
++
++ private static final int CAT_SEARCH_TICK_DELAY = 20;
++ private boolean isScaredOfCat;
++ private int catSearchTick;
++
++ PhantomSweepAttackGoal() {
++ super();
+ }
+
+ @Override
+- public void clientTick() {
+- Phantom.this.yHeadRot = Phantom.this.yBodyRot;
+- Phantom.this.yBodyRot = Phantom.this.getYRot();
++ public boolean canUse() {
++ return Phantom.this.getTarget() != null && Phantom.this.attackPhase == Phantom.AttackPhase.SWOOP;
+ }
++
++ @Override
++ public boolean canContinueToUse() {
++ LivingEntity entityliving = Phantom.this.getTarget();
++
++ if (entityliving == null) {
++ return false;
++ } else if (!entityliving.isAlive()) {
++ return false;
++ } else {
++ if (entityliving instanceof Player) {
++ Player entityhuman = (Player) entityliving;
++
++ if (entityliving.isSpectator() || entityhuman.isCreative()) {
++ return false;
++ }
++ }
++
++ if (!this.canUse()) {
++ return false;
++ } else {
++ if (Phantom.this.tickCount > this.catSearchTick) {
++ this.catSearchTick = Phantom.this.tickCount + 20;
++ List<Cat> list = Phantom.this.level().getEntitiesOfClass(Cat.class, Phantom.this.getBoundingBox().inflate(16.0D), EntitySelector.ENTITY_STILL_ALIVE);
++ Iterator iterator = list.iterator();
++
++ while (iterator.hasNext()) {
++ Cat entitycat = (Cat) iterator.next();
++
++ entitycat.hiss();
++ }
++
++ this.isScaredOfCat = !list.isEmpty();
++ }
++
++ return !this.isScaredOfCat;
++ }
++ }
++ }
++
++ @Override
++ public void start() {}
++
++ @Override
++ public void stop() {
++ Phantom.this.setTarget((LivingEntity) null);
++ Phantom.this.attackPhase = Phantom.AttackPhase.CIRCLE;
++ }
++
++ @Override
++ public void tick() {
++ LivingEntity entityliving = Phantom.this.getTarget();
++
++ if (entityliving != null) {
++ Phantom.this.moveTargetPoint = new Vec3(entityliving.getX(), entityliving.getY(0.5D), entityliving.getZ());
++ if (Phantom.this.getBoundingBox().inflate(0.20000000298023224D).intersects(entityliving.getBoundingBox())) {
++ Phantom.this.doHurtTarget(entityliving);
++ Phantom.this.attackPhase = Phantom.AttackPhase.CIRCLE;
++ if (!Phantom.this.isSilent()) {
++ Phantom.this.level().levelEvent(1039, Phantom.this.blockPosition(), 0);
++ }
++ } else if (Phantom.this.horizontalCollision || Phantom.this.hurtTime > 0) {
++ Phantom.this.attackPhase = Phantom.AttackPhase.CIRCLE;
++ }
++
++ }
++ }
+ }
+
+- class PhantomCircleAroundAnchorGoal extends Phantom.PhantomMoveTargetGoal {
++ private class PhantomCircleAroundAnchorGoal extends Phantom.h {
++
+ private float angle;
+ private float distance;
+ private float height;
+ private float clockwise;
+
++ PhantomCircleAroundAnchorGoal() {
++ super();
++ }
++
+ @Override
+ public boolean canUse() {
+ return Phantom.this.getTarget() == null || Phantom.this.attackPhase == Phantom.AttackPhase.CIRCLE;
+@@ -368,7 +488,7 @@
+ }
+
+ if (Phantom.this.random.nextInt(this.adjustedTickDelay(250)) == 0) {
+- this.distance++;
++ ++this.distance;
+ if (this.distance > 15.0F) {
+ this.distance = 5.0F;
+ this.clockwise = -this.clockwise;
+@@ -376,7 +496,7 @@
+ }
+
+ if (Phantom.this.random.nextInt(this.adjustedTickDelay(450)) == 0) {
+- this.angle = Phantom.this.random.nextFloat() * 2.0F * (float) Math.PI;
++ this.angle = Phantom.this.random.nextFloat() * 2.0F * 3.1415927F;
+ this.selectNext();
+ }
+
+@@ -393,6 +513,7 @@
+ this.height = Math.min(-1.0F, this.height);
+ this.selectNext();
+ }
++
+ }
+
+ private void selectNext() {
+@@ -400,147 +521,61 @@
+ Phantom.this.anchorPoint = Phantom.this.blockPosition();
+ }
+
+- this.angle = this.angle + this.clockwise * 15.0F * (float) (Math.PI / 180.0);
+- Phantom.this.moveTargetPoint = Vec3.atLowerCornerOf(Phantom.this.anchorPoint)
+- .add((double)(this.distance * Mth.cos(this.angle)), (double)(-4.0F + this.height), (double)(this.distance * Mth.sin(this.angle)));
++ this.angle += this.clockwise * 15.0F * 0.017453292F;
++ Phantom.this.moveTargetPoint = Vec3.atLowerCornerOf(Phantom.this.anchorPoint).add((double) (this.distance * Mth.cos(this.angle)), (double) (-4.0F + this.height), (double) (this.distance * Mth.sin(this.angle)));
+ }
+ }
+
+- class PhantomLookControl extends LookControl {
+- public PhantomLookControl(Mob mob) {
+- super(mob);
+- }
++ private class PhantomAttackPlayerTargetGoal extends Goal {
+
+- @Override
+- public void tick() {
+- }
+- }
++ private final TargetingConditions attackTargeting = TargetingConditions.forCombat().range(64.0D);
++ private int nextScanTick = reducedTickDelay(20);
+
+- class PhantomMoveControl extends MoveControl {
+- private float speed = 0.1F;
++ PhantomAttackPlayerTargetGoal() {}
+
+- public PhantomMoveControl(Mob mob) {
+- super(mob);
+- }
+-
+ @Override
+- public void tick() {
+- if (Phantom.this.horizontalCollision) {
+- Phantom.this.setYRot(Phantom.this.getYRot() + 180.0F);
+- this.speed = 0.1F;
+- }
+-
+- double d = Phantom.this.moveTargetPoint.x - Phantom.this.getX();
+- double d1 = Phantom.this.moveTargetPoint.y - Phantom.this.getY();
+- double d2 = Phantom.this.moveTargetPoint.z - Phantom.this.getZ();
+- double squareRoot = Math.sqrt(d * d + d2 * d2);
+- if (Math.abs(squareRoot) > 1.0E-5F) {
+- double d3 = 1.0 - Math.abs(d1 * 0.7F) / squareRoot;
+- double var26 = d * d3;
+- double var27 = d2 * d3;
+- squareRoot = Math.sqrt(var26 * var26 + var27 * var27);
+- double squareRoot1 = Math.sqrt(var26 * var26 + var27 * var27 + d1 * d1);
+- float yRot = Phantom.this.getYRot();
+- float f = (float)Mth.atan2(var27, var26);
+- float f1 = Mth.wrapDegrees(Phantom.this.getYRot() + 90.0F);
+- float f2 = Mth.wrapDegrees(f * (180.0F / (float)Math.PI));
+- Phantom.this.setYRot(Mth.approachDegrees(f1, f2, 4.0F) - 90.0F);
+- Phantom.this.yBodyRot = Phantom.this.getYRot();
+- if (Mth.degreesDifferenceAbs(yRot, Phantom.this.getYRot()) < 3.0F) {
+- this.speed = Mth.approach(this.speed, 1.8F, 0.005F * (1.8F / this.speed));
+- } else {
+- this.speed = Mth.approach(this.speed, 0.2F, 0.025F);
+- }
+-
+- float f3 = (float)(-(Mth.atan2(-d1, squareRoot) * 180.0F / (float)Math.PI));
+- Phantom.this.setXRot(f3);
+- float f4 = Phantom.this.getYRot() + 90.0F;
+- double d4 = (double)(this.speed * Mth.cos(f4 * (float) (Math.PI / 180.0))) * Math.abs(var26 / squareRoot1);
+- double d5 = (double)(this.speed * Mth.sin(f4 * (float) (Math.PI / 180.0))) * Math.abs(var27 / squareRoot1);
+- double d6 = (double)(this.speed * Mth.sin(f3 * (float) (Math.PI / 180.0))) * Math.abs(d1 / squareRoot1);
+- Vec3 deltaMovement = Phantom.this.getDeltaMovement();
+- Phantom.this.setDeltaMovement(deltaMovement.add(new Vec3(d4, d6, d5).subtract(deltaMovement).scale(0.2)));
+- }
+- }
+- }
+-
+- abstract class PhantomMoveTargetGoal extends Goal {
+- public PhantomMoveTargetGoal() {
+- this.setFlags(EnumSet.of(Goal.Flag.MOVE));
+- }
+-
+- protected boolean touchingTarget() {
+- return Phantom.this.moveTargetPoint.distanceToSqr(Phantom.this.getX(), Phantom.this.getY(), Phantom.this.getZ()) < 4.0;
+- }
+- }
+-
+- class PhantomSweepAttackGoal extends Phantom.PhantomMoveTargetGoal {
+- private static final int CAT_SEARCH_TICK_DELAY = 20;
+- private boolean isScaredOfCat;
+- private int catSearchTick;
+-
+- @Override
+ public boolean canUse() {
+- return Phantom.this.getTarget() != null && Phantom.this.attackPhase == Phantom.AttackPhase.SWOOP;
+- }
+-
+- @Override
+- public boolean canContinueToUse() {
+- LivingEntity target = Phantom.this.getTarget();
+- if (target == null) {
++ if (this.nextScanTick > 0) {
++ --this.nextScanTick;
+ return false;
+- } else if (!target.isAlive()) {
+- return false;
+ } else {
+- if (target instanceof Player player && (target.isSpectator() || player.isCreative())) {
+- return false;
+- }
++ this.nextScanTick = reducedTickDelay(60);
++ List<Player> list = Phantom.this.level().getNearbyPlayers(this.attackTargeting, Phantom.this, Phantom.this.getBoundingBox().inflate(16.0D, 64.0D, 16.0D));
+
+- if (!this.canUse()) {
+- return false;
+- } else {
+- if (Phantom.this.tickCount > this.catSearchTick) {
+- this.catSearchTick = Phantom.this.tickCount + 20;
+- List<Cat> entitiesOfClass = Phantom.this.level()
+- .getEntitiesOfClass(Cat.class, Phantom.this.getBoundingBox().inflate(16.0), EntitySelector.ENTITY_STILL_ALIVE);
++ if (!list.isEmpty()) {
++ list.sort(Comparator.comparing((Entity e) -> { return e.getY(); }).reversed()); // CraftBukkit - decompile error
++ Iterator iterator = list.iterator();
+
+- for (Cat cat : entitiesOfClass) {
+- cat.hiss();
+- }
++ while (iterator.hasNext()) {
++ Player entityhuman = (Player) iterator.next();
+
+- this.isScaredOfCat = !entitiesOfClass.isEmpty();
++ if (Phantom.this.canAttack(entityhuman, TargetingConditions.DEFAULT)) {
++ Phantom.this.setTarget(entityhuman, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER, true); // CraftBukkit - reason
++ return true;
++ }
+ }
+-
+- return !this.isScaredOfCat;
+ }
++
++ return false;
+ }
+ }
+
+ @Override
+- public void start() {
++ public boolean canContinueToUse() {
++ LivingEntity entityliving = Phantom.this.getTarget();
++
++ return entityliving != null ? Phantom.this.canAttack(entityliving, TargetingConditions.DEFAULT) : false;
+ }
++ }
+
+- @Override
+- public void stop() {
+- Phantom.this.setTarget(null);
+- Phantom.this.attackPhase = Phantom.AttackPhase.CIRCLE;
++ private abstract class h extends Goal {
++
++ public h() {
++ this.setFlags(EnumSet.of(Goal.Type.MOVE));
+ }
+
+- @Override
+- public void tick() {
+- LivingEntity target = Phantom.this.getTarget();
+- if (target != null) {
+- Phantom.this.moveTargetPoint = new Vec3(target.getX(), target.getY(0.5), target.getZ());
+- if (Phantom.this.getBoundingBox().inflate(0.2F).intersects(target.getBoundingBox())) {
+- Phantom.this.doHurtTarget(target);
+- Phantom.this.attackPhase = Phantom.AttackPhase.CIRCLE;
+- if (!Phantom.this.isSilent()) {
+- Phantom.this.level().levelEvent(1039, Phantom.this.blockPosition(), 0);
+- }
+- } else if (Phantom.this.horizontalCollision || Phantom.this.hurtTime > 0) {
+- Phantom.this.attackPhase = Phantom.AttackPhase.CIRCLE;
+- }
+- }
++ protected boolean touchingTarget() {
++ return Phantom.this.moveTargetPoint.distanceToSqr(Phantom.this.getX(), Phantom.this.getY(), Phantom.this.getZ()) < 4.0D;
+ }
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Ravager.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Ravager.java.patch
new file mode 100644
index 0000000000..2819d9219e
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Ravager.java.patch
@@ -0,0 +1,313 @@
+--- a/net/minecraft/world/entity/monster/Ravager.java
++++ b/net/minecraft/world/entity/monster/Ravager.java
+@@ -1,5 +1,7 @@
+ package net.minecraft.world.entity.monster;
+
++import java.util.Iterator;
++import java.util.List;
+ import java.util.function.Predicate;
+ import javax.annotation.Nullable;
+ import net.minecraft.core.BlockPos;
+@@ -34,21 +36,28 @@
+ import net.minecraft.world.level.LevelReader;
+ import net.minecraft.world.level.block.Block;
+ import net.minecraft.world.level.block.LeavesBlock;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import net.minecraft.world.level.gameevent.GameEvent;
+ import net.minecraft.world.level.pathfinder.BlockPathTypes;
+ import net.minecraft.world.phys.AABB;
+ import net.minecraft.world.phys.Vec3;
+ import org.joml.Vector3f;
+
++// CraftBukkit start
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++// CraftBukkit end
++
+ public class Ravager extends Raider {
+- private static final Predicate<Entity> NO_RAVAGER_AND_ALIVE = entity -> entity.isAlive() && !(entity instanceof Ravager);
+- private static final double BASE_MOVEMENT_SPEED = 0.3;
+- private static final double ATTACK_MOVEMENT_SPEED = 0.35;
++
++ private static final Predicate<Entity> NO_RAVAGER_AND_ALIVE = (entity) -> {
++ return entity.isAlive() && !(entity instanceof Ravager);
++ };
++ private static final double BASE_MOVEMENT_SPEED = 0.3D;
++ private static final double ATTACK_MOVEMENT_SPEED = 0.35D;
+ private static final int STUNNED_COLOR = 8356754;
+- private static final double STUNNED_COLOR_BLUE = 0.5725490196078431;
+- private static final double STUNNED_COLOR_GREEN = 0.5137254901960784;
+- private static final double STUNNED_COLOR_RED = 0.4980392156862745;
++ private static final double STUNNED_COLOR_BLUE = 0.5725490196078431D;
++ private static final double STUNNED_COLOR_GREEN = 0.5137254901960784D;
++ private static final double STUNNED_COLOR_RED = 0.4980392156862745D;
+ private static final int ATTACK_DURATION = 10;
+ public static final int STUN_DURATION = 40;
+ private int attackTick;
+@@ -66,13 +75,15 @@
+ protected void registerGoals() {
+ super.registerGoals();
+ this.goalSelector.addGoal(0, new FloatGoal(this));
+- this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, true));
+- this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.4));
++ this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, true));
++ this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.4D));
+ this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 6.0F));
+ this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
+- this.targetSelector.addGoal(2, new HurtByTargetGoal(this, Raider.class).setAlertOthers());
++ this.targetSelector.addGoal(2, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true));
+- this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true, entity -> !entity.isBaby()));
++ this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, AbstractVillager.class, true, (entityliving) -> {
++ return !entityliving.isBaby();
++ }));
+ this.targetSelector.addGoal(4, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
+ }
+
+@@ -80,20 +91,15 @@
+ protected void updateControlFlags() {
+ boolean flag = !(this.getControllingPassenger() instanceof Mob) || this.getControllingPassenger().getType().is(EntityTypeTags.RAIDERS);
+ boolean flag1 = !(this.getVehicle() instanceof Boat);
+- this.goalSelector.setControlFlag(Goal.Flag.MOVE, flag);
+- this.goalSelector.setControlFlag(Goal.Flag.JUMP, flag && flag1);
+- this.goalSelector.setControlFlag(Goal.Flag.LOOK, flag);
+- this.goalSelector.setControlFlag(Goal.Flag.TARGET, flag);
++
++ this.goalSelector.setControlFlag(Goal.Type.MOVE, flag);
++ this.goalSelector.setControlFlag(Goal.Type.JUMP, flag && flag1);
++ this.goalSelector.setControlFlag(Goal.Type.LOOK, flag);
++ this.goalSelector.setControlFlag(Goal.Type.TARGET, flag);
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes()
+- .add(Attributes.MAX_HEALTH, 100.0)
+- .add(Attributes.MOVEMENT_SPEED, 0.3)
+- .add(Attributes.KNOCKBACK_RESISTANCE, 0.75)
+- .add(Attributes.ATTACK_DAMAGE, 12.0)
+- .add(Attributes.ATTACK_KNOCKBACK, 1.5)
+- .add(Attributes.FOLLOW_RANGE, 32.0);
++ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 100.0D).add(Attributes.MOVEMENT_SPEED, 0.3D).add(Attributes.KNOCKBACK_RESISTANCE, 0.75D).add(Attributes.ATTACK_DAMAGE, 12.0D).add(Attributes.ATTACK_KNOCKBACK, 1.5D).add(Attributes.FOLLOW_RANGE, 32.0D);
+ }
+
+ @Override
+@@ -123,8 +129,8 @@
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height + 0.0625F * f, -0.0625F * f);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height + 0.0625F * f, -0.0625F * f);
+ }
+
+ @Override
+@@ -132,24 +138,31 @@
+ super.aiStep();
+ if (this.isAlive()) {
+ if (this.isImmobile()) {
+- this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.0);
++ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(0.0D);
+ } else {
+- double d = this.getTarget() != null ? 0.35 : 0.3;
+- double baseValue = this.getAttribute(Attributes.MOVEMENT_SPEED).getBaseValue();
+- this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(Mth.lerp(0.1, baseValue, d));
++ double d0 = this.getTarget() != null ? 0.35D : 0.3D;
++ double d1 = this.getAttribute(Attributes.MOVEMENT_SPEED).getBaseValue();
++
++ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(Mth.lerp(0.1D, d1, d0));
+ }
+
+ if (this.horizontalCollision && this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
+ boolean flag = false;
+- AABB aABB = this.getBoundingBox().inflate(0.2);
++ AABB axisalignedbb = this.getBoundingBox().inflate(0.2D);
++ Iterator iterator = BlockPos.betweenClosed(Mth.floor(axisalignedbb.minX), Mth.floor(axisalignedbb.minY), Mth.floor(axisalignedbb.minZ), Mth.floor(axisalignedbb.maxX), Mth.floor(axisalignedbb.maxY), Mth.floor(axisalignedbb.maxZ)).iterator();
+
+- for (BlockPos blockPos : BlockPos.betweenClosed(
+- Mth.floor(aABB.minX), Mth.floor(aABB.minY), Mth.floor(aABB.minZ), Mth.floor(aABB.maxX), Mth.floor(aABB.maxY), Mth.floor(aABB.maxZ)
+- )) {
+- BlockState blockState = this.level().getBlockState(blockPos);
+- Block block = blockState.getBlock();
++ while (iterator.hasNext()) {
++ BlockPos blockposition = (BlockPos) iterator.next();
++ IBlockData iblockdata = this.level().getBlockState(blockposition);
++ Block block = iblockdata.getBlock();
++
+ if (block instanceof LeavesBlock) {
+- flag = this.level().destroyBlock(blockPos, true, this) || flag;
++ // CraftBukkit start
++ if (!CraftEventFactory.callEntityChangeBlockEvent(this, blockposition, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState())) {
++ continue;
++ }
++ // CraftBukkit end
++ flag = this.level().destroyBlock(blockposition, true, this) || flag;
+ }
+ }
+
+@@ -159,38 +172,37 @@
+ }
+
+ if (this.roarTick > 0) {
+- this.roarTick--;
++ --this.roarTick;
+ if (this.roarTick == 10) {
+ this.roar();
+ }
+ }
+
+ if (this.attackTick > 0) {
+- this.attackTick--;
++ --this.attackTick;
+ }
+
+ if (this.stunnedTick > 0) {
+- this.stunnedTick--;
++ --this.stunnedTick;
+ this.stunEffect();
+ if (this.stunnedTick == 0) {
+ this.playSound(SoundEvents.RAVAGER_ROAR, 1.0F, 1.0F);
+ this.roarTick = 20;
+ }
+ }
++
+ }
+ }
+
+ private void stunEffect() {
+ if (this.random.nextInt(6) == 0) {
+- double d = this.getX()
+- - (double)this.getBbWidth() * Math.sin((double)(this.yBodyRot * (float) (Math.PI / 180.0)))
+- + (this.random.nextDouble() * 0.6 - 0.3);
+- double d1 = this.getY() + (double)this.getBbHeight() - 0.3;
+- double d2 = this.getZ()
+- + (double)this.getBbWidth() * Math.cos((double)(this.yBodyRot * (float) (Math.PI / 180.0)))
+- + (this.random.nextDouble() * 0.6 - 0.3);
+- this.level().addParticle(ParticleTypes.ENTITY_EFFECT, d, d1, d2, 0.4980392156862745, 0.5137254901960784, 0.5725490196078431);
++ double d0 = this.getX() - (double) this.getBbWidth() * Math.sin((double) (this.yBodyRot * 0.017453292F)) + (this.random.nextDouble() * 0.6D - 0.3D);
++ double d1 = this.getY() + (double) this.getBbHeight() - 0.3D;
++ double d2 = this.getZ() + (double) this.getBbWidth() * Math.cos((double) (this.yBodyRot * 0.017453292F)) + (this.random.nextDouble() * 0.6D - 0.3D);
++
++ this.level().addParticle(ParticleTypes.ENTITY_EFFECT, d0, d1, d2, 0.4980392156862745D, 0.5137254901960784D, 0.5725490196078431D);
+ }
++
+ }
+
+ @Override
+@@ -200,16 +212,16 @@
+
+ @Override
+ public boolean hasLineOfSight(Entity entity) {
+- return this.stunnedTick <= 0 && this.roarTick <= 0 && super.hasLineOfSight(entity);
++ return this.stunnedTick <= 0 && this.roarTick <= 0 ? super.hasLineOfSight(entity) : false;
+ }
+
+ @Override
+ protected void blockedByShield(LivingEntity entity) {
+ if (this.roarTick == 0) {
+- if (this.random.nextDouble() < 0.5) {
++ if (this.random.nextDouble() < 0.5D) {
+ this.stunnedTick = 40;
+ this.playSound(SoundEvents.RAVAGER_STUNNED, 1.0F, 1.0F);
+- this.level().broadcastEntityEvent(this, (byte)39);
++ this.level().broadcastEntityEvent(this, (byte) 39);
+ entity.push(this);
+ } else {
+ this.strongKnockback(entity);
+@@ -217,36 +229,43 @@
+
+ entity.hurtMarked = true;
+ }
++
+ }
+
+ private void roar() {
+ if (this.isAlive()) {
+- for (LivingEntity livingEntity : this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox().inflate(4.0), NO_RAVAGER_AND_ALIVE)) {
+- if (!(livingEntity instanceof AbstractIllager)) {
+- livingEntity.hurt(this.damageSources().mobAttack(this), 6.0F);
+- }
++ List<? extends LivingEntity> list = this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox().inflate(4.0D), Ravager.NO_RAVAGER_AND_ALIVE);
+
+- this.strongKnockback(livingEntity);
++ LivingEntity entityliving;
++
++ for (Iterator iterator = list.iterator(); iterator.hasNext(); this.strongKnockback(entityliving)) {
++ entityliving = (LivingEntity) iterator.next();
++ if (!(entityliving instanceof AbstractIllager)) {
++ entityliving.hurt(this.damageSources().mobAttack(this), 6.0F);
++ }
+ }
+
+- Vec3 center = this.getBoundingBox().getCenter();
++ Vec3 vec3d = this.getBoundingBox().getCenter();
+
+- for (int i = 0; i < 40; i++) {
+- double d = this.random.nextGaussian() * 0.2;
+- double d1 = this.random.nextGaussian() * 0.2;
+- double d2 = this.random.nextGaussian() * 0.2;
+- this.level().addParticle(ParticleTypes.POOF, center.x, center.y, center.z, d, d1, d2);
++ for (int i = 0; i < 40; ++i) {
++ double d0 = this.random.nextGaussian() * 0.2D;
++ double d1 = this.random.nextGaussian() * 0.2D;
++ double d2 = this.random.nextGaussian() * 0.2D;
++
++ this.level().addParticle(ParticleTypes.POOF, vec3d.x, vec3d.y, vec3d.z, d0, d1, d2);
+ }
+
+ this.gameEvent(GameEvent.ENTITY_ACTION);
+ }
++
+ }
+
+ private void strongKnockback(Entity entity) {
+- double d = entity.getX() - this.getX();
++ double d0 = entity.getX() - this.getX();
+ double d1 = entity.getZ() - this.getZ();
+- double max = Math.max(d * d + d1 * d1, 0.001);
+- entity.push(d / max * 4.0, 0.2, d1 / max * 4.0);
++ double d2 = Math.max(d0 * d0 + d1 * d1, 0.001D);
++
++ entity.push(d0 / d2 * 4.0D, 0.2D, d1 / d2 * 4.0D);
+ }
+
+ @Override
+@@ -276,7 +295,7 @@
+ @Override
+ public boolean doHurtTarget(Entity entity) {
+ this.attackTick = 10;
+- this.level().broadcastEntityEvent(this, (byte)4);
++ this.level().broadcastEntityEvent(this, (byte) 4);
+ this.playSound(SoundEvents.RAVAGER_ATTACK, 1.0F, 1.0F);
+ return super.doHurtTarget(entity);
+ }
+@@ -298,7 +317,7 @@
+ }
+
+ @Override
+- protected void playStepSound(BlockPos pos, BlockState block) {
++ protected void playStepSound(BlockPos pos, IBlockData block) {
+ this.playSound(SoundEvents.RAVAGER_STEP, 0.15F, 1.0F);
+ }
+
+@@ -308,8 +327,7 @@
+ }
+
+ @Override
+- public void applyRaidBuffs(int wave, boolean unusedFalse) {
+- }
++ public void applyRaidBuffs(int wave, boolean unusedFalse) {}
+
+ @Override
+ public boolean canBeLeader() {
+@@ -318,7 +336,8 @@
+
+ @Override
+ protected AABB getAttackBoundingBox() {
+- AABB aABB = super.getAttackBoundingBox();
+- return aABB.deflate(0.05, 0.0, 0.05);
++ AABB axisalignedbb = super.getAttackBoundingBox();
++
++ return axisalignedbb.deflate(0.05D, 0.0D, 0.05D);
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Shulker.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Shulker.java.patch
new file mode 100644
index 0000000000..109f273887
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Shulker.java.patch
@@ -0,0 +1,853 @@
+--- a/net/minecraft/world/entity/monster/Shulker.java
++++ b/net/minecraft/world/entity/monster/Shulker.java
+@@ -1,6 +1,8 @@
+ package net.minecraft.world.entity.monster;
+
+ import java.util.EnumSet;
++import java.util.Iterator;
++import java.util.List;
+ import java.util.Optional;
+ import java.util.UUID;
+ import javax.annotation.Nullable;
+@@ -23,14 +25,14 @@
+ 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.EnumMoveType;
++import net.minecraft.world.entity.GroupDataEntity;
+ import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.Mob;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.MoverType;
+-import net.minecraft.world.entity.Pose;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.VariantHolder;
+ import net.minecraft.world.entity.ai.attributes.AttributeModifier;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+@@ -50,20 +52,26 @@
+ import net.minecraft.world.level.Level;
+ 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 net.minecraft.world.level.entity.EntityTypeTest;
+ import net.minecraft.world.level.gameevent.GameEvent;
+ import net.minecraft.world.phys.AABB;
+ import net.minecraft.world.phys.Vec3;
+ import org.joml.Vector3f;
+
+-public class Shulker extends AbstractGolem implements VariantHolder<Optional<DyeColor>>, Enemy {
++// CraftBukkit start
++import org.bukkit.craftbukkit.util.CraftLocation;
++import org.bukkit.event.entity.EntityTeleportEvent;
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++// CraftBukkit end
++
++public class Shulker extends AbstractGolem implements VariantHolder<Optional<DyeColor>>, IMonster {
++
+ private static final UUID COVERED_ARMOR_MODIFIER_UUID = UUID.fromString("7E0292F2-9434-48D5-A29F-9583AF7DF27F");
+- private static final AttributeModifier COVERED_ARMOR_MODIFIER = new AttributeModifier(
+- COVERED_ARMOR_MODIFIER_UUID, "Covered armor bonus", 20.0, AttributeModifier.Operation.ADDITION
+- );
++ private static final AttributeModifier COVERED_ARMOR_MODIFIER = new AttributeModifier(Shulker.COVERED_ARMOR_MODIFIER_UUID, "Covered armor bonus", 20.0D, AttributeModifier.Operation.ADDITION);
+ protected static final EntityDataAccessor<Direction> DATA_ATTACH_FACE_ID = SynchedEntityData.defineId(Shulker.class, EntityDataSerializers.DIRECTION);
+ protected static final EntityDataAccessor<Byte> DATA_PEEK_ID = SynchedEntityData.defineId(Shulker.class, EntityDataSerializers.BYTE);
+- protected static final EntityDataAccessor<Byte> DATA_COLOR_ID = SynchedEntityData.defineId(Shulker.class, EntityDataSerializers.BYTE);
++ public static final EntityDataAccessor<Byte> DATA_COLOR_ID = SynchedEntityData.defineId(Shulker.class, EntityDataSerializers.BYTE);
+ private static final int TELEPORT_STEPS = 6;
+ private static final byte NO_COLOR = 16;
+ private static final byte DEFAULT_COLOR = 16;
+@@ -71,9 +79,10 @@
+ private static final int OTHER_SHULKER_SCAN_RADIUS = 8;
+ private static final int OTHER_SHULKER_LIMIT = 5;
+ private static final float PEEK_PER_TICK = 0.05F;
+- static final Vector3f FORWARD = Util.make(() -> {
+- Vec3i normal = Direction.SOUTH.getNormal();
+- return new Vector3f((float)normal.getX(), (float)normal.getY(), (float)normal.getZ());
++ static final Vector3f FORWARD = (Vector3f) Util.make(() -> {
++ Vec3i baseblockposition = Direction.SOUTH.getNormal();
++
++ return new Vector3f((float) baseblockposition.getX(), (float) baseblockposition.getY(), (float) baseblockposition.getZ());
+ });
+ private float currentPeekAmountO;
+ private float currentPeekAmount;
+@@ -94,7 +103,7 @@
+ this.goalSelector.addGoal(4, new Shulker.ShulkerAttackGoal());
+ this.goalSelector.addGoal(7, new Shulker.ShulkerPeekGoal());
+ this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
+- this.targetSelector.addGoal(1, new HurtByTargetGoal(this, this.getClass()).setAlertOthers());
++ this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{this.getClass()})).setAlertOthers());
+ this.targetSelector.addGoal(2, new Shulker.ShulkerNearestAttackGoal(this));
+ this.targetSelector.addGoal(3, new Shulker.ShulkerDefenseAttackGoal(this));
+ }
+@@ -119,6 +128,7 @@
+ if (!this.isClosed()) {
+ super.playAmbientSound();
+ }
++
+ }
+
+ @Override
+@@ -134,13 +144,13 @@
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_ATTACH_FACE_ID, Direction.DOWN);
+- this.entityData.define(DATA_PEEK_ID, (byte)0);
+- this.entityData.define(DATA_COLOR_ID, (byte)16);
++ this.entityData.define(Shulker.DATA_ATTACH_FACE_ID, Direction.DOWN);
++ this.entityData.define(Shulker.DATA_PEEK_ID, (byte) 0);
++ this.entityData.define(Shulker.DATA_COLOR_ID, (byte) 16);
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 30.0);
++ return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 30.0D);
+ }
+
+ @Override
+@@ -152,18 +162,19 @@
+ public void readAdditionalSaveData(CompoundTag compound) {
+ super.readAdditionalSaveData(compound);
+ this.setAttachFace(Direction.from3DDataValue(compound.getByte("AttachFace")));
+- this.entityData.set(DATA_PEEK_ID, compound.getByte("Peek"));
++ this.entityData.set(Shulker.DATA_PEEK_ID, compound.getByte("Peek"));
+ if (compound.contains("Color", 99)) {
+- this.entityData.set(DATA_COLOR_ID, compound.getByte("Color"));
++ this.entityData.set(Shulker.DATA_COLOR_ID, compound.getByte("Color"));
+ }
++
+ }
+
+ @Override
+ public void addAdditionalSaveData(CompoundTag compound) {
+ super.addAdditionalSaveData(compound);
+- compound.putByte("AttachFace", (byte)this.getAttachFace().get3DDataValue());
+- compound.putByte("Peek", this.entityData.get(DATA_PEEK_ID));
+- compound.putByte("Color", this.entityData.get(DATA_COLOR_ID));
++ compound.putByte("AttachFace", (byte) this.getAttachFace().get3DDataValue());
++ compound.putByte("Peek", (Byte) this.entityData.get(Shulker.DATA_PEEK_ID));
++ compound.putByte("Color", (Byte) this.entityData.get(Shulker.DATA_COLOR_ID));
+ }
+
+ @Override
+@@ -179,37 +190,42 @@
+
+ if (this.level().isClientSide) {
+ if (this.clientSideTeleportInterpolation > 0) {
+- this.clientSideTeleportInterpolation--;
++ --this.clientSideTeleportInterpolation;
+ } else {
+ this.clientOldAttachPosition = null;
+ }
+ }
++
+ }
+
+ private void findNewAttachment() {
+- Direction direction = this.findAttachableSurface(this.blockPosition());
+- if (direction != null) {
+- this.setAttachFace(direction);
++ Direction enumdirection = this.findAttachableSurface(this.blockPosition());
++
++ if (enumdirection != null) {
++ this.setAttachFace(enumdirection);
+ } else {
+ this.teleportSomewhere();
+ }
++
+ }
+
+ @Override
+ protected AABB makeBoundingBox() {
+- float physicalPeek = getPhysicalPeek(this.currentPeekAmount);
+- Direction opposite = this.getAttachFace().getOpposite();
+- float f = this.getType().getWidth() / 2.0F;
+- return getProgressAabb(opposite, physicalPeek).move(this.getX() - (double)f, this.getY(), this.getZ() - (double)f);
++ float f = getPhysicalPeek(this.currentPeekAmount);
++ Direction enumdirection = this.getAttachFace().getOpposite();
++ float f1 = this.getType().getWidth() / 2.0F;
++
++ return getProgressAabb(enumdirection, f).move(this.getX() - (double) f1, this.getY(), this.getZ() - (double) f1);
+ }
+
+ private static float getPhysicalPeek(float peek) {
+- return 0.5F - Mth.sin((0.5F + peek) * (float) Math.PI) * 0.5F;
++ return 0.5F - Mth.sin((0.5F + peek) * 3.1415927F) * 0.5F;
+ }
+
+ private boolean updatePeekAmount() {
+ this.currentPeekAmountO = this.currentPeekAmount;
+- float f = (float)this.getRawPeekAmount() * 0.01F;
++ float f = (float) this.getRawPeekAmount() * 0.01F;
++
+ if (this.currentPeekAmount == f) {
+ return false;
+ } else {
+@@ -225,24 +241,25 @@
+
+ private void onPeekAmountChange() {
+ this.reapplyPosition();
+- float physicalPeek = getPhysicalPeek(this.currentPeekAmount);
+- float physicalPeek1 = getPhysicalPeek(this.currentPeekAmountO);
+- Direction opposite = this.getAttachFace().getOpposite();
+- float f = physicalPeek - physicalPeek1;
+- if (!(f <= 0.0F)) {
+- for (Entity entity : this.level()
+- .getEntities(
+- this,
+- getProgressDeltaAabb(opposite, physicalPeek1, physicalPeek).move(this.getX() - 0.5, this.getY(), this.getZ() - 0.5),
+- EntitySelector.NO_SPECTATORS.and(entity1 -> !entity1.isPassengerOfSameVehicle(this))
+- )) {
++ float f = getPhysicalPeek(this.currentPeekAmount);
++ float f1 = getPhysicalPeek(this.currentPeekAmountO);
++ Direction enumdirection = this.getAttachFace().getOpposite();
++ float f2 = f - f1;
++
++ if (f2 > 0.0F) {
++ List<Entity> list = this.level().getEntities((Entity) this, getProgressDeltaAabb(enumdirection, f1, f).move(this.getX() - 0.5D, this.getY(), this.getZ() - 0.5D), EntitySelector.NO_SPECTATORS.and((entity) -> {
++ return !entity.isPassengerOfSameVehicle(this);
++ }));
++ Iterator iterator = list.iterator();
++
++ while (iterator.hasNext()) {
++ Entity entity = (Entity) iterator.next();
++
+ if (!(entity instanceof Shulker) && !entity.noPhysics) {
+- entity.move(
+- MoverType.SHULKER,
+- new Vec3((double)(f * (float)opposite.getStepX()), (double)(f * (float)opposite.getStepY()), (double)(f * (float)opposite.getStepZ()))
+- );
++ entity.move(EnumMoveType.SHULKER, new Vec3((double) (f2 * (float) enumdirection.getStepX()), (double) (f2 * (float) enumdirection.getStepY()), (double) (f2 * (float) enumdirection.getStepZ())));
+ }
+ }
++
+ }
+ }
+
+@@ -251,11 +268,10 @@
+ }
+
+ public static AABB getProgressDeltaAabb(Direction direction, float delta, float deltaO) {
+- double d = (double)Math.max(delta, deltaO);
+- double d1 = (double)Math.min(delta, deltaO);
+- return new AABB(BlockPos.ZERO)
+- .expandTowards((double)direction.getStepX() * d, (double)direction.getStepY() * d, (double)direction.getStepZ() * d)
+- .contract((double)(-direction.getStepX()) * (1.0 + d1), (double)(-direction.getStepY()) * (1.0 + d1), (double)(-direction.getStepZ()) * (1.0 + d1));
++ double d0 = (double) Math.max(delta, deltaO);
++ double d1 = (double) Math.min(delta, deltaO);
++
++ return (new AABB(BlockPos.ZERO)).expandTowards((double) direction.getStepX() * d0, (double) direction.getStepY() * d0, (double) direction.getStepZ() * d0).contract((double) (-direction.getStepX()) * (1.0D + d1), (double) (-direction.getStepY()) * (1.0D + d1), (double) (-direction.getStepZ()) * (1.0D + d1));
+ }
+
+ @Override
+@@ -282,9 +298,7 @@
+
+ @Nullable
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
+ this.setYRot(0.0F);
+ this.yHeadRot = this.getYRot();
+ this.setOldPosAndRot();
+@@ -292,12 +306,13 @@
+ }
+
+ @Override
+- public void move(MoverType type, Vec3 pos) {
+- if (type == MoverType.SHULKER_BOX) {
++ public void move(EnumMoveType type, Vec3 pos) {
++ if (type == EnumMoveType.SHULKER_BOX) {
+ this.teleportSomewhere();
+ } else {
+ super.move(type, pos);
+ }
++
+ }
+
+ @Override
+@@ -306,39 +321,46 @@
+ }
+
+ @Override
+- public void setDeltaMovement(Vec3 deltaMovement) {
+- }
++ public void setDeltaMovement(Vec3 deltaMovement) {}
+
+ @Override
+- public void setPos(double x, double y, double z) {
+- BlockPos blockPos = this.blockPosition();
++ public void setPos(double x, double d1, double y) {
++ BlockPos blockposition = this.blockPosition();
++
+ if (this.isPassenger()) {
+- super.setPos(x, y, z);
++ super.setPos(x, d1, y);
+ } else {
+- super.setPos((double)Mth.floor(x) + 0.5, (double)Mth.floor(y + 0.5), (double)Mth.floor(z) + 0.5);
++ super.setPos((double) Mth.floor(x) + 0.5D, (double) Mth.floor(d1 + 0.5D), (double) Mth.floor(y) + 0.5D);
+ }
+
+ if (this.tickCount != 0) {
+- BlockPos blockPos1 = this.blockPosition();
+- if (!blockPos1.equals(blockPos)) {
+- this.entityData.set(DATA_PEEK_ID, (byte)0);
++ BlockPos blockposition1 = this.blockPosition();
++
++ if (!blockposition1.equals(blockposition)) {
++ this.entityData.set(Shulker.DATA_PEEK_ID, (byte) 0);
+ this.hasImpulse = true;
+- if (this.level().isClientSide && !this.isPassenger() && !blockPos1.equals(this.clientOldAttachPosition)) {
+- this.clientOldAttachPosition = blockPos;
++ if (this.level().isClientSide && !this.isPassenger() && !blockposition1.equals(this.clientOldAttachPosition)) {
++ this.clientOldAttachPosition = blockposition;
+ this.clientSideTeleportInterpolation = 6;
+ this.xOld = this.getX();
+ this.yOld = this.getY();
+ this.zOld = this.getZ();
+ }
+ }
++
+ }
+ }
+
+ @Nullable
+ protected Direction findAttachableSurface(BlockPos pos) {
+- for (Direction direction : Direction.values()) {
+- if (this.canStayAt(pos, direction)) {
+- return direction;
++ Direction[] aenumdirection = Direction.values();
++ int i = aenumdirection.length;
++
++ for (int j = 0; j < i; ++j) {
++ Direction enumdirection = aenumdirection[j];
++
++ if (this.canStayAt(pos, enumdirection)) {
++ return enumdirection;
+ }
+ }
+
+@@ -349,49 +371,56 @@
+ if (this.isPositionBlocked(pos)) {
+ return false;
+ } else {
+- Direction opposite = facing.getOpposite();
+- if (!this.level().loadedAndEntityCanStandOnFace(pos.relative(facing), this, opposite)) {
++ Direction enumdirection1 = facing.getOpposite();
++
++ if (!this.level().loadedAndEntityCanStandOnFace(pos.relative(facing), this, enumdirection1)) {
+ return false;
+ } else {
+- AABB aABB = getProgressAabb(opposite, 1.0F).move(pos).deflate(1.0E-6);
+- return this.level().noCollision(this, aABB);
++ AABB axisalignedbb = getProgressAabb(enumdirection1, 1.0F).move(pos).deflate(1.0E-6D);
++
++ return this.level().noCollision(this, axisalignedbb);
+ }
+ }
+ }
+
+ private boolean isPositionBlocked(BlockPos pos) {
+- BlockState blockState = this.level().getBlockState(pos);
+- if (blockState.isAir()) {
++ IBlockData iblockdata = this.level().getBlockState(pos);
++
++ if (iblockdata.isAir()) {
+ return false;
+ } else {
+- boolean flag = blockState.is(Blocks.MOVING_PISTON) && pos.equals(this.blockPosition());
++ boolean flag = iblockdata.is(Blocks.MOVING_PISTON) && pos.equals(this.blockPosition());
++
+ return !flag;
+ }
+ }
+
+ protected boolean teleportSomewhere() {
+ if (!this.isNoAi() && this.isAlive()) {
+- BlockPos blockPos = this.blockPosition();
++ BlockPos blockposition = this.blockPosition();
+
+- for (int i = 0; i < 5; i++) {
+- BlockPos blockPos1 = blockPos.offset(
+- Mth.randomBetweenInclusive(this.random, -8, 8),
+- Mth.randomBetweenInclusive(this.random, -8, 8),
+- Mth.randomBetweenInclusive(this.random, -8, 8)
+- );
+- if (blockPos1.getY() > this.level().getMinBuildHeight()
+- && this.level().isEmptyBlock(blockPos1)
+- && this.level().getWorldBorder().isWithinBounds(blockPos1)
+- && this.level().noCollision(this, new AABB(blockPos1).deflate(1.0E-6))) {
+- Direction direction = this.findAttachableSurface(blockPos1);
+- if (direction != null) {
++ for (int i = 0; i < 5; ++i) {
++ BlockPos blockposition1 = blockposition.offset(Mth.randomBetweenInclusive(this.random, -8, 8), Mth.randomBetweenInclusive(this.random, -8, 8), Mth.randomBetweenInclusive(this.random, -8, 8));
++
++ if (blockposition1.getY() > this.level().getMinBuildHeight() && this.level().isEmptyBlock(blockposition1) && this.level().getWorldBorder().isWithinBounds(blockposition1) && this.level().noCollision(this, (new AABB(blockposition1)).deflate(1.0E-6D))) {
++ Direction enumdirection = this.findAttachableSurface(blockposition1);
++
++ if (enumdirection != null) {
++ // CraftBukkit start
++ EntityTeleportEvent teleportEvent = CraftEventFactory.callEntityTeleportEvent(this, blockposition1.getX(), blockposition1.getY(), blockposition1.getZ());
++ if (teleportEvent.isCancelled()) {
++ return false;
++ } else {
++ blockposition1 = CraftLocation.toBlockPosition(teleportEvent.getTo());
++ }
++ // CraftBukkit end
+ this.unRide();
+- this.setAttachFace(direction);
++ this.setAttachFace(enumdirection);
+ this.playSound(SoundEvents.SHULKER_TELEPORT, 1.0F, 1.0F);
+- this.setPos((double)blockPos1.getX() + 0.5, (double)blockPos1.getY(), (double)blockPos1.getZ() + 0.5);
+- this.level().gameEvent(GameEvent.TELEPORT, blockPos, GameEvent.Context.of(this));
+- this.entityData.set(DATA_PEEK_ID, (byte)0);
+- this.setTarget(null);
++ this.setPos((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY(), (double) blockposition1.getZ() + 0.5D);
++ this.level().gameEvent(GameEvent.TELEPORT, blockposition, GameEvent.Context.of((Entity) this));
++ this.entityData.set(Shulker.DATA_PEEK_ID, (byte) 0);
++ this.setTarget((LivingEntity) null);
+ return true;
+ }
+ }
+@@ -404,17 +433,19 @@
+ }
+
+ @Override
+- public void lerpTo(double d, double d1, double d2, float f, float f1, int i) {
++ public void lerpTo(double d0, double d1, double d2, float f, float f1, int i) {
+ this.lerpSteps = 0;
+- this.setPos(d, d1, d2);
++ this.setPos(d0, d1, d2);
+ this.setRot(f, f1);
+ }
+
+ @Override
+ public boolean hurt(DamageSource source, float amount) {
++ Entity entity;
++
+ if (this.isClosed()) {
+- Entity directEntity = source.getDirectEntity();
+- if (directEntity instanceof AbstractArrow) {
++ entity = source.getDirectEntity();
++ if (entity instanceof AbstractArrow) {
+ return false;
+ }
+ }
+@@ -422,11 +453,11 @@
+ if (!super.hurt(source, amount)) {
+ return false;
+ } else {
+- if ((double)this.getHealth() < (double)this.getMaxHealth() * 0.5 && this.random.nextInt(4) == 0) {
++ if ((double) this.getHealth() < (double) this.getMaxHealth() * 0.5D && this.random.nextInt(4) == 0) {
+ this.teleportSomewhere();
+ } else if (source.is(DamageTypeTags.IS_PROJECTILE)) {
+- Entity directEntity = source.getDirectEntity();
+- if (directEntity != null && directEntity.getType() == EntityType.SHULKER_BULLET) {
++ entity = source.getDirectEntity();
++ if (entity != null && entity.getType() == EntityType.SHULKER_BULLET) {
+ this.hitByShulkerBullet();
+ }
+ }
+@@ -440,18 +471,22 @@
+ }
+
+ private void hitByShulkerBullet() {
+- Vec3 vec3 = this.position();
+- AABB boundingBox = this.getBoundingBox();
++ Vec3 vec3d = this.position();
++ AABB axisalignedbb = this.getBoundingBox();
++
+ if (!this.isClosed() && this.teleportSomewhere()) {
+- int size = this.level().getEntities(EntityType.SHULKER, boundingBox.inflate(8.0), Entity::isAlive).size();
+- float f = (float)(size - 1) / 5.0F;
+- if (!(this.level().random.nextFloat() < f)) {
+- Shulker shulker = EntityType.SHULKER.create(this.level());
+- if (shulker != null) {
+- shulker.setVariant(this.getVariant());
+- shulker.moveTo(vec3);
+- this.level().addFreshEntity(shulker);
++ int i = this.level().getEntities((EntityTypeTest) EntityType.SHULKER, axisalignedbb.inflate(8.0D), Entity::isAlive).size();
++ float f = (float) (i - 1) / 5.0F;
++
++ if (this.level().random.nextFloat() >= f) {
++ Shulker entityshulker = (Shulker) EntityType.SHULKER.create(this.level());
++
++ if (entityshulker != null) {
++ entityshulker.setVariant(this.getVariant());
++ entityshulker.moveTo(vec3d);
++ this.level().addFreshEntity(entityshulker, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - the mysteries of life
+ }
++
+ }
+ }
+ }
+@@ -462,31 +497,31 @@
+ }
+
+ public Direction getAttachFace() {
+- return this.entityData.get(DATA_ATTACH_FACE_ID);
++ return (Direction) this.entityData.get(Shulker.DATA_ATTACH_FACE_ID);
+ }
+
+- private void setAttachFace(Direction attachFace) {
+- this.entityData.set(DATA_ATTACH_FACE_ID, attachFace);
++ public void setAttachFace(Direction attachFace) {
++ this.entityData.set(Shulker.DATA_ATTACH_FACE_ID, attachFace);
+ }
+
+ @Override
+ public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
+- if (DATA_ATTACH_FACE_ID.equals(key)) {
++ if (Shulker.DATA_ATTACH_FACE_ID.equals(key)) {
+ this.setBoundingBox(this.makeBoundingBox());
+ }
+
+ super.onSyncedDataUpdated(key);
+ }
+
+- private int getRawPeekAmount() {
+- return this.entityData.get(DATA_PEEK_ID);
++ public int getRawPeekAmount() {
++ return (Byte) this.entityData.get(Shulker.DATA_PEEK_ID);
+ }
+
+- void setRawPeekAmount(int peekAmount) {
++ public void setRawPeekAmount(int peekAmount) {
+ if (!this.level().isClientSide) {
+- this.getAttribute(Attributes.ARMOR).removeModifier(COVERED_ARMOR_MODIFIER.getId());
++ this.getAttribute(Attributes.ARMOR).removeModifier(Shulker.COVERED_ARMOR_MODIFIER.getId());
+ if (peekAmount == 0) {
+- this.getAttribute(Attributes.ARMOR).addPermanentModifier(COVERED_ARMOR_MODIFIER);
++ this.getAttribute(Attributes.ARMOR).addPermanentModifier(Shulker.COVERED_ARMOR_MODIFIER);
+ this.playSound(SoundEvents.SHULKER_CLOSE, 1.0F, 1.0F);
+ this.gameEvent(GameEvent.CONTAINER_CLOSE);
+ } else {
+@@ -495,7 +530,7 @@
+ }
+ }
+
+- this.entityData.set(DATA_PEEK_ID, (byte)peekAmount);
++ this.entityData.set(Shulker.DATA_PEEK_ID, (byte) peekAmount);
+ }
+
+ public float getClientPeekAmount(float partialTick) {
+@@ -503,7 +538,7 @@
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return 0.5F;
+ }
+
+@@ -525,26 +560,28 @@
+ }
+
+ @Override
+- public void push(Entity entity) {
+- }
++ public void push(Entity entity) {}
+
+ public Optional<Vec3> getRenderPosition(float partial) {
+ if (this.clientOldAttachPosition != null && this.clientSideTeleportInterpolation > 0) {
+- double d = (double)((float)this.clientSideTeleportInterpolation - partial) / 6.0;
+- d *= d;
+- BlockPos blockPos = this.blockPosition();
+- double d1 = (double)(blockPos.getX() - this.clientOldAttachPosition.getX()) * d;
+- double d2 = (double)(blockPos.getY() - this.clientOldAttachPosition.getY()) * d;
+- double d3 = (double)(blockPos.getZ() - this.clientOldAttachPosition.getZ()) * d;
++ double d0 = (double) ((float) this.clientSideTeleportInterpolation - partial) / 6.0D;
++
++ d0 *= d0;
++ BlockPos blockposition = this.blockPosition();
++ double d1 = (double) (blockposition.getX() - this.clientOldAttachPosition.getX()) * d0;
++ double d2 = (double) (blockposition.getY() - this.clientOldAttachPosition.getY()) * d0;
++ double d3 = (double) (blockposition.getZ() - this.clientOldAttachPosition.getZ()) * d0;
++
+ return Optional.of(new Vec3(-d1, -d2, -d3));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+- @Override
+ public void setVariant(Optional<DyeColor> variant) {
+- this.entityData.set(DATA_COLOR_ID, variant.<Byte>map(color -> (byte)color.getId()).orElse((byte)16));
++ this.entityData.set(Shulker.DATA_COLOR_ID, (Byte) variant.map((enumcolor) -> {
++ return (byte) enumcolor.getId();
++ }).orElse((byte) 16));
+ }
+
+ @Override
+@@ -554,21 +591,57 @@
+
+ @Nullable
+ public DyeColor getColor() {
+- byte b = this.entityData.get(DATA_COLOR_ID);
+- return b != 16 && b <= 15 ? DyeColor.byId(b) : null;
++ byte b0 = (Byte) this.entityData.get(Shulker.DATA_COLOR_ID);
++
++ return b0 != 16 && b0 <= 15 ? DyeColor.byId(b0) : null;
+ }
+
+- class ShulkerAttackGoal extends Goal {
++ private class ShulkerLookControl extends LookControl {
++
++ public ShulkerLookControl(Mob mob) {
++ super(mob);
++ }
++
++ @Override
++ protected void clampHeadRotationToBody() {}
++
++ @Override
++ protected Optional<Float> getYRotD() {
++ Direction enumdirection = Shulker.this.getAttachFace().getOpposite();
++ Vector3f vector3f = enumdirection.getRotation().transform(new Vector3f(Shulker.FORWARD));
++ Vec3i baseblockposition = enumdirection.getNormal();
++ Vector3f vector3f1 = new Vector3f((float) baseblockposition.getX(), (float) baseblockposition.getY(), (float) baseblockposition.getZ());
++
++ vector3f1.cross(vector3f);
++ double d0 = this.wantedX - this.mob.getX();
++ double d1 = this.wantedY - this.mob.getEyeY();
++ double d2 = this.wantedZ - this.mob.getZ();
++ Vector3f vector3f2 = new Vector3f((float) d0, (float) d1, (float) d2);
++ float f = vector3f1.dot(vector3f2);
++ float f1 = vector3f.dot(vector3f2);
++
++ return Math.abs(f) <= 1.0E-5F && Math.abs(f1) <= 1.0E-5F ? Optional.empty() : Optional.of((float) (Mth.atan2((double) (-f), (double) f1) * 57.2957763671875D));
++ }
++
++ @Override
++ protected Optional<Float> getXRotD() {
++ return Optional.of(0.0F);
++ }
++ }
++
++ private class ShulkerAttackGoal extends Goal {
++
+ private int attackTime;
+
+ public ShulkerAttackGoal() {
+- this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
++ this.setFlags(EnumSet.of(Goal.Type.MOVE, Goal.Type.LOOK));
+ }
+
+ @Override
+ public boolean canUse() {
+- LivingEntity target = Shulker.this.getTarget();
+- return target != null && target.isAlive() && Shulker.this.level().getDifficulty() != Difficulty.PEACEFUL;
++ LivingEntity entityliving = Shulker.this.getTarget();
++
++ return entityliving != null && entityliving.isAlive() ? Shulker.this.level().getDifficulty() != Difficulty.PEACEFUL : false;
+ }
+
+ @Override
+@@ -590,22 +663,21 @@
+ @Override
+ public void tick() {
+ if (Shulker.this.level().getDifficulty() != Difficulty.PEACEFUL) {
+- this.attackTime--;
+- LivingEntity target = Shulker.this.getTarget();
+- if (target != null) {
+- Shulker.this.getLookControl().setLookAt(target, 180.0F, 180.0F);
+- double d = Shulker.this.distanceToSqr(target);
+- if (d < 400.0) {
++ --this.attackTime;
++ LivingEntity entityliving = Shulker.this.getTarget();
++
++ if (entityliving != null) {
++ Shulker.this.getLookControl().setLookAt(entityliving, 180.0F, 180.0F);
++ double d0 = Shulker.this.distanceToSqr((Entity) entityliving);
++
++ if (d0 < 400.0D) {
+ if (this.attackTime <= 0) {
+ this.attackTime = 20 + Shulker.this.random.nextInt(10) * 20 / 2;
+- Shulker.this.level()
+- .addFreshEntity(new ShulkerBullet(Shulker.this.level(), Shulker.this, target, Shulker.this.getAttachFace().getAxis()));
+- Shulker.this.playSound(
+- SoundEvents.SHULKER_SHOOT, 2.0F, (Shulker.this.random.nextFloat() - Shulker.this.random.nextFloat()) * 0.2F + 1.0F
+- );
++ Shulker.this.level().addFreshEntity(new ShulkerBullet(Shulker.this.level(), Shulker.this, entityliving, Shulker.this.getAttachFace().getAxis()));
++ Shulker.this.playSound(SoundEvents.SHULKER_SHOOT, 2.0F, (Shulker.this.random.nextFloat() - Shulker.this.random.nextFloat()) * 0.2F + 1.0F);
+ }
+ } else {
+- Shulker.this.setTarget(null);
++ Shulker.this.setTarget((LivingEntity) null);
+ }
+
+ super.tick();
+@@ -614,126 +686,89 @@
+ }
+ }
+
+- static class ShulkerBodyRotationControl extends BodyRotationControl {
+- public ShulkerBodyRotationControl(Mob mob) {
+- super(mob);
+- }
++ private class ShulkerPeekGoal extends Goal {
+
+- @Override
+- public void clientTick() {
+- }
+- }
++ private int peekTime;
+
+- static class ShulkerDefenseAttackGoal extends NearestAttackableTargetGoal<LivingEntity> {
+- public ShulkerDefenseAttackGoal(Shulker shulker) {
+- super(shulker, LivingEntity.class, 10, true, false, entity -> entity instanceof Enemy);
+- }
++ ShulkerPeekGoal() {}
+
+ @Override
+ public boolean canUse() {
+- return this.mob.getTeam() != null && super.canUse();
++ return Shulker.this.getTarget() == null && Shulker.this.random.nextInt(reducedTickDelay(40)) == 0 && Shulker.this.canStayAt(Shulker.this.blockPosition(), Shulker.this.getAttachFace());
+ }
+
+ @Override
+- protected AABB getTargetSearchArea(double targetDistance) {
+- Direction attachFace = ((Shulker)this.mob).getAttachFace();
+- if (attachFace.getAxis() == Direction.Axis.X) {
+- return this.mob.getBoundingBox().inflate(4.0, targetDistance, targetDistance);
+- } else {
+- return attachFace.getAxis() == Direction.Axis.Z
+- ? this.mob.getBoundingBox().inflate(targetDistance, targetDistance, 4.0)
+- : this.mob.getBoundingBox().inflate(targetDistance, 4.0, targetDistance);
+- }
++ public boolean canContinueToUse() {
++ return Shulker.this.getTarget() == null && this.peekTime > 0;
+ }
+- }
+
+- class ShulkerLookControl extends LookControl {
+- public ShulkerLookControl(Mob mob) {
+- super(mob);
+- }
+-
+ @Override
+- protected void clampHeadRotationToBody() {
++ public void start() {
++ this.peekTime = this.adjustedTickDelay(20 * (1 + Shulker.this.random.nextInt(3)));
++ Shulker.this.setRawPeekAmount(30);
+ }
+
+ @Override
+- protected Optional<Float> getYRotD() {
+- Direction opposite = Shulker.this.getAttachFace().getOpposite();
+- Vector3f vector3f = opposite.getRotation().transform(new Vector3f(Shulker.FORWARD));
+- Vec3i normal = opposite.getNormal();
+- Vector3f vector3f1 = new Vector3f((float)normal.getX(), (float)normal.getY(), (float)normal.getZ());
+- vector3f1.cross(vector3f);
+- double d = this.wantedX - this.mob.getX();
+- double d1 = this.wantedY - this.mob.getEyeY();
+- double d2 = this.wantedZ - this.mob.getZ();
+- Vector3f vector3f2 = new Vector3f((float)d, (float)d1, (float)d2);
+- float f = vector3f1.dot(vector3f2);
+- float f1 = vector3f.dot(vector3f2);
+- return !(Math.abs(f) > 1.0E-5F) && !(Math.abs(f1) > 1.0E-5F)
+- ? Optional.empty()
+- : Optional.of((float)(Mth.atan2((double)(-f), (double)f1) * 180.0F / (float)Math.PI));
++ public void stop() {
++ if (Shulker.this.getTarget() == null) {
++ Shulker.this.setRawPeekAmount(0);
++ }
++
+ }
+
+ @Override
+- protected Optional<Float> getXRotD() {
+- return Optional.of(0.0F);
++ public void tick() {
++ --this.peekTime;
+ }
+ }
+
+- class ShulkerNearestAttackGoal extends NearestAttackableTargetGoal<Player> {
+- public ShulkerNearestAttackGoal(Shulker shulker) {
+- super(shulker, Player.class, true);
++ private class ShulkerNearestAttackGoal extends NearestAttackableTargetGoal<Player> {
++
++ public ShulkerNearestAttackGoal(Shulker entityshulker) {
++ super(entityshulker, Player.class, true);
+ }
+
+ @Override
+ public boolean canUse() {
+- return Shulker.this.level().getDifficulty() != Difficulty.PEACEFUL && super.canUse();
++ return Shulker.this.level().getDifficulty() == Difficulty.PEACEFUL ? false : super.canUse();
+ }
+
+ @Override
+ protected AABB getTargetSearchArea(double targetDistance) {
+- Direction attachFace = ((Shulker)this.mob).getAttachFace();
+- if (attachFace.getAxis() == Direction.Axis.X) {
+- return this.mob.getBoundingBox().inflate(4.0, targetDistance, targetDistance);
+- } else {
+- return attachFace.getAxis() == Direction.Axis.Z
+- ? this.mob.getBoundingBox().inflate(targetDistance, targetDistance, 4.0)
+- : this.mob.getBoundingBox().inflate(targetDistance, 4.0, targetDistance);
+- }
++ Direction enumdirection = ((Shulker) this.mob).getAttachFace();
++
++ return enumdirection.getAxis() == Direction.Axis.X ? this.mob.getBoundingBox().inflate(4.0D, targetDistance, targetDistance) : (enumdirection.getAxis() == Direction.Axis.Z ? this.mob.getBoundingBox().inflate(targetDistance, targetDistance, 4.0D) : this.mob.getBoundingBox().inflate(targetDistance, 4.0D, targetDistance));
+ }
+ }
+
+- class ShulkerPeekGoal extends Goal {
+- private int peekTime;
++ private static class ShulkerDefenseAttackGoal extends NearestAttackableTargetGoal<LivingEntity> {
+
+- @Override
+- public boolean canUse() {
+- return Shulker.this.getTarget() == null
+- && Shulker.this.random.nextInt(reducedTickDelay(40)) == 0
+- && Shulker.this.canStayAt(Shulker.this.blockPosition(), Shulker.this.getAttachFace());
++ public ShulkerDefenseAttackGoal(Shulker shulker) {
++ super(shulker, LivingEntity.class, 10, true, false, (entityliving) -> {
++ return entityliving instanceof IMonster;
++ });
+ }
+
+ @Override
+- public boolean canContinueToUse() {
+- return Shulker.this.getTarget() == null && this.peekTime > 0;
++ public boolean canUse() {
++ return this.mob.getTeam() == null ? false : super.canUse();
+ }
+
+ @Override
+- public void start() {
+- this.peekTime = this.adjustedTickDelay(20 * (1 + Shulker.this.random.nextInt(3)));
+- Shulker.this.setRawPeekAmount(30);
++ protected AABB getTargetSearchArea(double targetDistance) {
++ Direction enumdirection = ((Shulker) this.mob).getAttachFace();
++
++ return enumdirection.getAxis() == Direction.Axis.X ? this.mob.getBoundingBox().inflate(4.0D, targetDistance, targetDistance) : (enumdirection.getAxis() == Direction.Axis.Z ? this.mob.getBoundingBox().inflate(targetDistance, targetDistance, 4.0D) : this.mob.getBoundingBox().inflate(targetDistance, 4.0D, targetDistance));
+ }
++ }
+
+- @Override
+- public void stop() {
+- if (Shulker.this.getTarget() == null) {
+- Shulker.this.setRawPeekAmount(0);
+- }
++ private static class ShulkerBodyRotationControl extends BodyRotationControl {
++
++ public ShulkerBodyRotationControl(Mob mob) {
++ super(mob);
+ }
+
+ @Override
+- public void tick() {
+- this.peekTime--;
+- }
++ public void clientTick() {}
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Silverfish.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Silverfish.java.patch
new file mode 100644
index 0000000000..1e5713e0bb
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Silverfish.java.patch
@@ -0,0 +1,282 @@
+--- a/net/minecraft/world/entity/monster/Silverfish.java
++++ b/net/minecraft/world/entity/monster/Silverfish.java
+@@ -11,10 +11,10 @@
+ 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.EntityType;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.MobType;
+-import net.minecraft.world.entity.Pose;
++import net.minecraft.world.entity.EnumMobSpawn;
++import net.minecraft.world.entity.EnumMonsterType;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+ import net.minecraft.world.entity.ai.goal.ClimbOnTopOfPowderSnowGoal;
+@@ -31,10 +31,15 @@
+ import net.minecraft.world.level.LevelReader;
+ import net.minecraft.world.level.block.Block;
+ import net.minecraft.world.level.block.InfestedBlock;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import org.joml.Vector3f;
+
++// CraftBukkit start
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++// CraftBukkit end
++
+ public class Silverfish extends Monster {
++
+ @Nullable
+ private Silverfish.SilverfishWakeUpFriendsGoal friendsGoal;
+
+@@ -48,19 +53,19 @@
+ this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(1, new ClimbOnTopOfPowderSnowGoal(this, this.level()));
+ this.goalSelector.addGoal(3, this.friendsGoal);
+- this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0, false));
++ this.goalSelector.addGoal(4, new MeleeAttackGoal(this, 1.0D, false));
+ this.goalSelector.addGoal(5, new Silverfish.SilverfishMergeWithStoneGoal(this));
+- this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers());
++ this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers());
+ this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, true));
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return 0.13F;
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 8.0).add(Attributes.MOVEMENT_SPEED, 0.25).add(Attributes.ATTACK_DAMAGE, 1.0);
++ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 8.0D).add(Attributes.MOVEMENT_SPEED, 0.25D).add(Attributes.ATTACK_DAMAGE, 1.0D);
+ }
+
+ @Override
+@@ -84,7 +89,7 @@
+ }
+
+ @Override
+- protected void playStepSound(BlockPos pos, BlockState block) {
++ protected void playStepSound(BlockPos pos, IBlockData block) {
+ this.playSound(SoundEvents.SILVERFISH_STEP, 0.15F, 1.0F);
+ }
+
+@@ -118,35 +123,95 @@
+ return InfestedBlock.isCompatibleHostBlock(level.getBlockState(pos.below())) ? 10.0F : super.getWalkTargetValue(pos, level);
+ }
+
+- public static boolean checkSilverfishSpawnRules(
+- EntityType<Silverfish> silverfish, LevelAccessor level, MobSpawnType spawnType, BlockPos pos, RandomSource random
+- ) {
++ public static boolean checkSilverfishSpawnRules(EntityType<Silverfish> silverfish, LevelAccessor level, EnumMobSpawn spawnType, BlockPos pos, RandomSource random) {
+ if (checkAnyLightMonsterSpawnRules(silverfish, level, spawnType, pos, random)) {
+- Player nearestPlayer = level.getNearestPlayer((double)pos.getX() + 0.5, (double)pos.getY() + 0.5, (double)pos.getZ() + 0.5, 5.0, true);
+- return nearestPlayer == null;
++ Player entityhuman = level.getNearestPlayer((double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D, 5.0D, true);
++
++ return entityhuman == null;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+- public MobType getMobType() {
+- return MobType.ARTHROPOD;
++ public EnumMonsterType getMobType() {
++ return EnumMonsterType.ARTHROPOD;
+ }
+
+ @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);
+ }
+
+- static class SilverfishMergeWithStoneGoal extends RandomStrollGoal {
++ private static class SilverfishWakeUpFriendsGoal extends Goal {
++
++ private final Silverfish silverfish;
++ private int lookForFriends;
++
++ public SilverfishWakeUpFriendsGoal(Silverfish silverfish) {
++ this.silverfish = silverfish;
++ }
++
++ public void notifyHurt() {
++ if (this.lookForFriends == 0) {
++ this.lookForFriends = this.adjustedTickDelay(20);
++ }
++
++ }
++
++ @Override
++ public boolean canUse() {
++ return this.lookForFriends > 0;
++ }
++
++ @Override
++ public void tick() {
++ --this.lookForFriends;
++ if (this.lookForFriends <= 0) {
++ Level world = this.silverfish.level();
++ RandomSource randomsource = this.silverfish.getRandom();
++ BlockPos blockposition = this.silverfish.blockPosition();
++
++ for (int i = 0; i <= 5 && i >= -5; i = (i <= 0 ? 1 : 0) - i) {
++ for (int j = 0; j <= 10 && j >= -10; j = (j <= 0 ? 1 : 0) - j) {
++ for (int k = 0; k <= 10 && k >= -10; k = (k <= 0 ? 1 : 0) - k) {
++ BlockPos blockposition1 = blockposition.offset(j, i, k);
++ IBlockData iblockdata = world.getBlockState(blockposition1);
++ Block block = iblockdata.getBlock();
++
++ if (block instanceof InfestedBlock) {
++ // CraftBukkit start
++ if (!CraftEventFactory.callEntityChangeBlockEvent(this.silverfish, blockposition1, net.minecraft.world.level.block.Blocks.AIR.defaultBlockState())) {
++ continue;
++ }
++ // CraftBukkit end
++ if (world.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
++ world.destroyBlock(blockposition1, true, this.silverfish);
++ } else {
++ world.setBlock(blockposition1, ((InfestedBlock) block).hostStateByInfested(world.getBlockState(blockposition1)), 3);
++ }
++
++ if (randomsource.nextBoolean()) {
++ return;
++ }
++ }
++ }
++ }
++ }
++ }
++
++ }
++ }
++
++ private static class SilverfishMergeWithStoneGoal extends RandomStrollGoal {
++
+ @Nullable
+ private Direction selectedDirection;
+ private boolean doMerge;
+
+ public SilverfishMergeWithStoneGoal(Silverfish silverfish) {
+- super(silverfish, 1.0, 10);
+- this.setFlags(EnumSet.of(Goal.Flag.MOVE));
++ super(silverfish, 1.0D, 10);
++ this.setFlags(EnumSet.of(Goal.Type.MOVE));
+ }
+
+ @Override
+@@ -156,12 +221,14 @@
+ } else if (!this.mob.getNavigation().isDone()) {
+ return false;
+ } else {
+- RandomSource random = this.mob.getRandom();
+- if (this.mob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && random.nextInt(reducedTickDelay(10)) == 0) {
+- this.selectedDirection = Direction.getRandom(random);
+- BlockPos blockPos = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5, this.mob.getZ()).relative(this.selectedDirection);
+- BlockState blockState = this.mob.level().getBlockState(blockPos);
+- if (InfestedBlock.isCompatibleHostBlock(blockState)) {
++ RandomSource randomsource = this.mob.getRandom();
++
++ if (this.mob.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) && randomsource.nextInt(reducedTickDelay(10)) == 0) {
++ this.selectedDirection = Direction.getRandom(randomsource);
++ BlockPos blockposition = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5D, this.mob.getZ()).relative(this.selectedDirection);
++ IBlockData iblockdata = this.mob.level().getBlockState(blockposition);
++
++ if (InfestedBlock.isCompatibleHostBlock(iblockdata)) {
+ this.doMerge = true;
+ return true;
+ }
+@@ -174,7 +241,7 @@
+
+ @Override
+ public boolean canContinueToUse() {
+- return !this.doMerge && super.canContinueToUse();
++ return this.doMerge ? false : super.canContinueToUse();
+ }
+
+ @Override
+@@ -182,66 +249,22 @@
+ if (!this.doMerge) {
+ super.start();
+ } else {
+- LevelAccessor levelAccessor = this.mob.level();
+- BlockPos blockPos = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5, this.mob.getZ()).relative(this.selectedDirection);
+- BlockState blockState = levelAccessor.getBlockState(blockPos);
+- if (InfestedBlock.isCompatibleHostBlock(blockState)) {
+- levelAccessor.setBlock(blockPos, InfestedBlock.infestedStateByHost(blockState), 3);
++ Level world = this.mob.level();
++ BlockPos blockposition = BlockPos.containing(this.mob.getX(), this.mob.getY() + 0.5D, this.mob.getZ()).relative(this.selectedDirection);
++ IBlockData iblockdata = world.getBlockState(blockposition);
++
++ if (InfestedBlock.isCompatibleHostBlock(iblockdata)) {
++ // CraftBukkit start
++ if (!CraftEventFactory.callEntityChangeBlockEvent(this.mob, blockposition, InfestedBlock.infestedStateByHost(iblockdata))) {
++ return;
++ }
++ // CraftBukkit end
++ world.setBlock(blockposition, InfestedBlock.infestedStateByHost(iblockdata), 3);
+ this.mob.spawnAnim();
+ this.mob.discard();
+ }
+- }
+- }
+- }
+
+- static class SilverfishWakeUpFriendsGoal extends Goal {
+- private final Silverfish silverfish;
+- private int lookForFriends;
+-
+- public SilverfishWakeUpFriendsGoal(Silverfish silverfish) {
+- this.silverfish = silverfish;
+- }
+-
+- public void notifyHurt() {
+- if (this.lookForFriends == 0) {
+- this.lookForFriends = this.adjustedTickDelay(20);
+ }
+ }
+-
+- @Override
+- public boolean canUse() {
+- return this.lookForFriends > 0;
+- }
+-
+- @Override
+- public void tick() {
+- this.lookForFriends--;
+- if (this.lookForFriends <= 0) {
+- Level level = this.silverfish.level();
+- RandomSource random = this.silverfish.getRandom();
+- BlockPos blockPos = this.silverfish.blockPosition();
+-
+- for (int i = 0; i <= 5 && i >= -5; i = (i <= 0 ? 1 : 0) - i) {
+- for (int i1 = 0; i1 <= 10 && i1 >= -10; i1 = (i1 <= 0 ? 1 : 0) - i1) {
+- for (int i2 = 0; i2 <= 10 && i2 >= -10; i2 = (i2 <= 0 ? 1 : 0) - i2) {
+- BlockPos blockPos1 = blockPos.offset(i1, i, i2);
+- BlockState blockState = level.getBlockState(blockPos1);
+- Block block = blockState.getBlock();
+- if (block instanceof InfestedBlock) {
+- if (level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
+- level.destroyBlock(blockPos1, true, this.silverfish);
+- } else {
+- level.setBlock(blockPos1, ((InfestedBlock)block).hostStateByInfested(level.getBlockState(blockPos1)), 3);
+- }
+-
+- if (random.nextBoolean()) {
+- return;
+- }
+- }
+- }
+- }
+- }
+- }
+- }
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Skeleton.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Skeleton.java.patch
new file mode 100644
index 0000000000..ede8670387
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Skeleton.java.patch
@@ -0,0 +1,103 @@
+--- a/net/minecraft/world/entity/monster/Skeleton.java
++++ b/net/minecraft/world/entity/monster/Skeleton.java
+@@ -7,16 +7,20 @@
+ import net.minecraft.sounds.SoundEvent;
+ import net.minecraft.sounds.SoundEvents;
+ import net.minecraft.world.damagesource.DamageSource;
++import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.player.Player;
+ import net.minecraft.world.item.Items;
++import net.minecraft.world.level.IMaterial;
+ import net.minecraft.world.level.Level;
+
+ public class Skeleton extends AbstractSkeleton {
++
+ private static final int TOTAL_CONVERSION_TIME = 300;
+- private static final EntityDataAccessor<Boolean> DATA_STRAY_CONVERSION_ID = SynchedEntityData.defineId(Skeleton.class, EntityDataSerializers.BOOLEAN);
++ public static final EntityDataAccessor<Boolean> DATA_STRAY_CONVERSION_ID = SynchedEntityData.defineId(Skeleton.class, EntityDataSerializers.BOOLEAN);
+ public static final String CONVERSION_TAG = "StrayConversionTime";
+ private int inPowderSnowTime;
+- private int conversionTime;
++ public int conversionTime;
+
+ public Skeleton(EntityType<? extends Skeleton> entityType, Level level) {
+ super(entityType, level);
+@@ -25,15 +29,15 @@
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.getEntityData().define(DATA_STRAY_CONVERSION_ID, false);
++ this.getEntityData().define(Skeleton.DATA_STRAY_CONVERSION_ID, false);
+ }
+
+ public boolean isFreezeConverting() {
+- return this.getEntityData().get(DATA_STRAY_CONVERSION_ID);
++ return (Boolean) this.getEntityData().get(Skeleton.DATA_STRAY_CONVERSION_ID);
+ }
+
+ public void setFreezeConverting(boolean isFrozen) {
+- this.entityData.set(DATA_STRAY_CONVERSION_ID, isFrozen);
++ this.entityData.set(Skeleton.DATA_STRAY_CONVERSION_ID, isFrozen);
+ }
+
+ @Override
+@@ -46,12 +50,12 @@
+ if (!this.level().isClientSide && this.isAlive() && !this.isNoAi()) {
+ if (this.isInPowderSnow) {
+ if (this.isFreezeConverting()) {
+- this.conversionTime--;
++ --this.conversionTime;
+ if (this.conversionTime < 0) {
+ this.doFreezeConversion();
+ }
+ } else {
+- this.inPowderSnowTime++;
++ ++this.inPowderSnowTime;
+ if (this.inPowderSnowTime >= 140) {
+ this.startFreezeConversion(300);
+ }
+@@ -77,18 +81,20 @@
+ if (compound.contains("StrayConversionTime", 99) && compound.getInt("StrayConversionTime") > -1) {
+ this.startFreezeConversion(compound.getInt("StrayConversionTime"));
+ }
++
+ }
+
+- private void startFreezeConversion(int conversionTime) {
++ public void startFreezeConversion(int conversionTime) {
+ this.conversionTime = conversionTime;
+ this.setFreezeConverting(true);
+ }
+
+ protected void doFreezeConversion() {
+- this.convertTo(EntityType.STRAY, true);
++ this.convertTo(EntityType.STRAY, true, org.bukkit.event.entity.EntityTransformEvent.TransformReason.FROZEN, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.FROZEN); // CraftBukkit - add spawn and transform reasons
+ if (!this.isSilent()) {
+- this.level().levelEvent(null, 1048, this.blockPosition(), 0);
++ this.level().levelEvent((Player) null, 1048, this.blockPosition(), 0);
+ }
++
+ }
+
+ @Override
+@@ -119,9 +125,16 @@
+ @Override
+ protected void dropCustomDeathLoot(DamageSource source, int looting, boolean recentlyHit) {
+ super.dropCustomDeathLoot(source, looting, recentlyHit);
+- if (source.getEntity() instanceof Creeper creeper && creeper.canDropMobsSkull()) {
+- creeper.increaseDroppedSkulls();
+- this.spawnAtLocation(Items.SKELETON_SKULL);
++ Entity entity = source.getEntity();
++
++ if (entity instanceof Creeper) {
++ Creeper entitycreeper = (Creeper) entity;
++
++ if (entitycreeper.canDropMobsSkull()) {
++ entitycreeper.increaseDroppedSkulls();
++ this.spawnAtLocation((IMaterial) Items.SKELETON_SKULL);
++ }
+ }
++
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Slime.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Slime.java.patch
new file mode 100644
index 0000000000..7808f88a35
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Slime.java.patch
@@ -0,0 +1,665 @@
+--- a/net/minecraft/world/entity/monster/Slime.java
++++ b/net/minecraft/world/entity/monster/Slime.java
+@@ -23,12 +23,12 @@
+ import net.minecraft.world.effect.MobEffects;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
++import net.minecraft.world.entity.EntityPose;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
++import net.minecraft.world.entity.GroupDataEntity;
+ import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.Mob;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.Pose;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+ import net.minecraft.world.entity.ai.control.MoveControl;
+ import net.minecraft.world.entity.ai.goal.Goal;
+@@ -44,7 +44,16 @@
+ import net.minecraft.world.phys.Vec3;
+ import org.joml.Vector3f;
+
+-public class Slime extends Mob implements Enemy {
++// CraftBukkit start
++import java.util.ArrayList;
++import java.util.List;
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++import org.bukkit.event.entity.EntityTransformEvent;
++import org.bukkit.event.entity.SlimeSplitEvent;
++// CraftBukkit end
++
++public class Slime extends Mob implements IMonster {
++
+ private static final EntityDataAccessor<Integer> ID_SIZE = SynchedEntityData.defineId(Slime.class, EntityDataSerializers.INT);
+ public static final int MIN_SIZE = 1;
+ public static final int MAX_SIZE = 127;
+@@ -65,10 +74,9 @@
+ this.goalSelector.addGoal(2, new Slime.SlimeAttackGoal(this));
+ this.goalSelector.addGoal(3, new Slime.SlimeRandomDirectionGoal(this));
+ this.goalSelector.addGoal(5, new Slime.SlimeKeepOnJumpingGoal(this));
+- this.targetSelector
+- .addGoal(
+- 1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, livingEntity -> Math.abs(livingEntity.getY() - this.getY()) <= 4.0)
+- );
++ this.targetSelector.addGoal(1, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, (entityliving) -> {
++ return Math.abs(entityliving.getY() - this.getY()) <= 4.0D;
++ }));
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, IronGolem.class, true));
+ }
+
+@@ -80,27 +88,28 @@
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(ID_SIZE, 1);
++ this.entityData.define(Slime.ID_SIZE, 1);
+ }
+
+ @VisibleForTesting
+ public void setSize(int size, boolean resetHealth) {
+- int i = Mth.clamp(size, 1, 127);
+- this.entityData.set(ID_SIZE, i);
++ int j = Mth.clamp(size, 1, 127);
++
++ this.entityData.set(Slime.ID_SIZE, j);
+ this.reapplyPosition();
+ this.refreshDimensions();
+- this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double)(i * i));
+- this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue((double)(0.2F + 0.1F * (float)i));
+- this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue((double)i);
++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double) (j * j));
++ this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue((double) (0.2F + 0.1F * (float) j));
++ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue((double) j);
+ if (resetHealth) {
+ this.setHealth(this.getMaxHealth());
+ }
+
+- this.xpReward = i;
++ this.xpReward = j;
+ }
+
+ public int getSize() {
+- return this.entityData.get(ID_SIZE);
++ return (Integer) this.entityData.get(Slime.ID_SIZE);
+ }
+
+ @Override
+@@ -132,18 +141,19 @@
+
+ @Override
+ public void tick() {
+- this.squish = this.squish + (this.targetSquish - this.squish) * 0.5F;
++ this.squish += (this.targetSquish - this.squish) * 0.5F;
+ this.oSquish = this.squish;
+ super.tick();
+ if (this.onGround() && !this.wasOnGround) {
+- int size = this.getSize();
++ int i = this.getSize();
+
+- for (int i = 0; i < size * 8; i++) {
+- float f = this.random.nextFloat() * (float) (Math.PI * 2);
++ for (int j = 0; j < i * 8; ++j) {
++ float f = this.random.nextFloat() * 6.2831855F;
+ float f1 = this.random.nextFloat() * 0.5F + 0.5F;
+- float f2 = Mth.sin(f) * (float)size * 0.5F * f1;
+- float f3 = Mth.cos(f) * (float)size * 0.5F * f1;
+- this.level().addParticle(this.getParticleType(), this.getX() + (double)f2, this.getY(), this.getZ() + (double)f3, 0.0, 0.0, 0.0);
++ float f2 = Mth.sin(f) * (float) i * 0.5F * f1;
++ float f3 = Mth.cos(f) * (float) i * 0.5F * f1;
++
++ this.level().addParticle(this.getParticleType(), this.getX() + (double) f2, this.getY(), this.getZ() + (double) f3, 0.0D, 0.0D, 0.0D);
+ }
+
+ this.playSound(this.getSquishSound(), this.getSoundVolume(), ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F) / 0.8F);
+@@ -166,16 +176,17 @@
+
+ @Override
+ public void refreshDimensions() {
+- double x = this.getX();
+- double y = this.getY();
+- double z = this.getZ();
++ double d0 = this.getX();
++ double d1 = this.getY();
++ double d2 = this.getZ();
++
+ super.refreshDimensions();
+- this.setPos(x, y, z);
++ this.setPos(d0, d1, d2);
+ }
+
+ @Override
+ public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
+- if (ID_SIZE.equals(key)) {
++ if (Slime.ID_SIZE.equals(key)) {
+ this.refreshDimensions();
+ this.setYRot(this.yHeadRot);
+ this.yBodyRot = this.yHeadRot;
+@@ -189,36 +200,60 @@
+
+ @Override
+ public EntityType<? extends Slime> getType() {
+- return (EntityType<? extends Slime>)super.getType();
++ return (EntityType<? extends Slime>) super.getType(); // CraftBukkit - decompile error
+ }
+
+ @Override
+ public void remove(Entity.RemovalReason reason) {
+- int size = this.getSize();
+- if (!this.level().isClientSide && size > 1 && this.isDeadOrDying()) {
+- Component customName = this.getCustomName();
+- boolean isNoAi = this.isNoAi();
+- float f = (float)size / 4.0F;
+- int i = size / 2;
+- int i1 = 2 + this.random.nextInt(3);
++ int i = this.getSize();
+
+- for (int i2 = 0; i2 < i1; i2++) {
+- float f1 = ((float)(i2 % 2) - 0.5F) * f;
+- float f2 = ((float)(i2 / 2) - 0.5F) * f;
+- Slime slime = this.getType().create(this.level());
+- if (slime != null) {
++ if (!this.level().isClientSide && i > 1 && this.isDeadOrDying()) {
++ Component ichatbasecomponent = this.getCustomName();
++ boolean flag = this.isNoAi();
++ float f = (float) i / 4.0F;
++ int j = i / 2;
++ int k = 2 + this.random.nextInt(3);
++
++ // CraftBukkit start
++ SlimeSplitEvent event = new SlimeSplitEvent((org.bukkit.entity.Slime) this.getBukkitEntity(), k);
++ this.level().getCraftServer().getPluginManager().callEvent(event);
++
++ if (!event.isCancelled() && event.getCount() > 0) {
++ k = event.getCount();
++ } else {
++ super.remove(reason);
++ return;
++ }
++ List<LivingEntity> slimes = new ArrayList<>(j);
++ // CraftBukkit end
++
++ for (int l = 0; l < k; ++l) {
++ float f1 = ((float) (l % 2) - 0.5F) * f;
++ float f2 = ((float) (l / 2) - 0.5F) * f;
++ Slime entityslime = (Slime) this.getType().create(this.level());
++
++ if (entityslime != null) {
+ if (this.isPersistenceRequired()) {
+- slime.setPersistenceRequired();
++ entityslime.setPersistenceRequired();
+ }
+
+- slime.setCustomName(customName);
+- slime.setNoAi(isNoAi);
+- slime.setInvulnerable(this.isInvulnerable());
+- slime.setSize(i, true);
+- slime.moveTo(this.getX() + (double)f1, this.getY() + 0.5, this.getZ() + (double)f2, this.random.nextFloat() * 360.0F, 0.0F);
+- this.level().addFreshEntity(slime);
++ entityslime.setCustomName(ichatbasecomponent);
++ entityslime.setNoAi(flag);
++ entityslime.setInvulnerable(this.isInvulnerable());
++ entityslime.setSize(j, true);
++ entityslime.moveTo(this.getX() + (double) f1, this.getY() + 0.5D, this.getZ() + (double) f2, this.random.nextFloat() * 360.0F, 0.0F);
++ slimes.add(entityslime); // CraftBukkit
+ }
+ }
++ // CraftBukkit start
++ if (CraftEventFactory.callEntityTransformEvent(this, slimes, EntityTransformEvent.TransformReason.SPLIT).isCancelled()) {
++ super.remove(reason);
++ return;
++ }
++ for (LivingEntity living : slimes) {
++ this.level().addFreshEntity(living, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SLIME_SPLIT); // CraftBukkit - SpawnReason
++ }
++ // CraftBukkit end
+ }
+
+ super.remove(reason);
+@@ -228,8 +263,9 @@
+ public void push(Entity entity) {
+ super.push(entity);
+ if (entity instanceof IronGolem && this.isDealsDamage()) {
+- this.dealDamage((LivingEntity)entity);
++ this.dealDamage((LivingEntity) entity);
+ }
++
+ }
+
+ @Override
+@@ -237,28 +273,29 @@
+ if (this.isDealsDamage()) {
+ this.dealDamage(entity);
+ }
++
+ }
+
+ protected void dealDamage(LivingEntity livingEntity) {
+ if (this.isAlive()) {
+- int size = this.getSize();
+- if (this.distanceToSqr(livingEntity) < 0.6 * (double)size * 0.6 * (double)size
+- && this.hasLineOfSight(livingEntity)
+- && livingEntity.hurt(this.damageSources().mobAttack(this), this.getAttackDamage())) {
++ int i = this.getSize();
++
++ if (this.distanceToSqr((Entity) livingEntity) < 0.6D * (double) i * 0.6D * (double) i && this.hasLineOfSight(livingEntity) && livingEntity.hurt(this.damageSources().mobAttack(this), this.getAttackDamage())) {
+ this.playSound(SoundEvents.SLIME_ATTACK, 1.0F, (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F);
+ this.doEnchantDamageEffects(this, livingEntity);
+ }
+ }
++
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return 0.625F * size.height;
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height - 0.015625F * (float)this.getSize() * f, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height - 0.015625F * (float) this.getSize() * f, 0.0F);
+ }
+
+ protected boolean isDealsDamage() {
+@@ -266,7 +303,7 @@
+ }
+
+ protected float getAttackDamage() {
+- return (float)this.getAttributeValue(Attributes.ATTACK_DAMAGE);
++ return (float) this.getAttributeValue(Attributes.ATTACK_DAMAGE);
+ }
+
+ @Override
+@@ -283,21 +320,16 @@
+ return this.isTiny() ? SoundEvents.SLIME_SQUISH_SMALL : SoundEvents.SLIME_SQUISH;
+ }
+
+- public static boolean checkSlimeSpawnRules(EntityType<Slime> slime, LevelAccessor level, MobSpawnType spawnType, BlockPos pos, RandomSource random) {
+- if (MobSpawnType.isSpawner(spawnType)) {
++ public static boolean checkSlimeSpawnRules(EntityType<Slime> slime, LevelAccessor level, EnumMobSpawn spawnType, BlockPos pos, RandomSource random) {
++ if (EnumMobSpawn.isSpawner(spawnType)) {
+ return checkMobSpawnRules(slime, level, spawnType, pos, random);
+ } else {
+ if (level.getDifficulty() != Difficulty.PEACEFUL) {
+- if (spawnType == MobSpawnType.SPAWNER) {
++ if (spawnType == EnumMobSpawn.SPAWNER) {
+ return checkMobSpawnRules(slime, level, spawnType, pos, random);
+ }
+
+- if (level.getBiome(pos).is(BiomeTags.ALLOWS_SURFACE_SLIME_SPAWNS)
+- && pos.getY() > 50
+- && pos.getY() < 70
+- && random.nextFloat() < 0.5F
+- && random.nextFloat() < level.getMoonBrightness()
+- && level.getMaxLocalRawBrightness(pos) <= random.nextInt(8)) {
++ if (level.getBiome(pos).is(BiomeTags.ALLOWS_SURFACE_SLIME_SPAWNS) && pos.getY() > 50 && pos.getY() < 70 && random.nextFloat() < 0.5F && random.nextFloat() < level.getMoonBrightness() && level.getMaxLocalRawBrightness(pos) <= random.nextInt(8)) {
+ return checkMobSpawnRules(slime, level, spawnType, pos, random);
+ }
+
+@@ -305,8 +337,9 @@
+ return false;
+ }
+
+- ChunkPos chunkPos = new ChunkPos(pos);
+- boolean flag = WorldgenRandom.seedSlimeChunk(chunkPos.x, chunkPos.z, ((WorldGenLevel)level).getSeed(), 987234911L).nextInt(10) == 0;
++ ChunkPos chunkcoordintpair = new ChunkPos(pos);
++ boolean flag = WorldgenRandom.seedSlimeChunk(chunkcoordintpair.x, chunkcoordintpair.z, ((WorldGenLevel) level).getSeed(), 987234911L).nextInt(10) == 0;
++
+ if (random.nextInt(10) == 0 && flag && pos.getY() < 40) {
+ return checkMobSpawnRules(slime, level, spawnType, pos, random);
+ }
+@@ -318,7 +351,7 @@
+
+ @Override
+ protected float getSoundVolume() {
+- return 0.4F * (float)this.getSize();
++ return 0.4F * (float) this.getSize();
+ }
+
+ @Override
+@@ -332,29 +365,31 @@
+
+ @Override
+ protected void jumpFromGround() {
+- Vec3 deltaMovement = this.getDeltaMovement();
+- this.setDeltaMovement(deltaMovement.x, (double)this.getJumpPower(), deltaMovement.z);
++ Vec3 vec3d = this.getDeltaMovement();
++
++ this.setDeltaMovement(vec3d.x, (double) this.getJumpPower(), vec3d.z);
+ this.hasImpulse = true;
+ }
+
+ @Nullable
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
+- RandomSource random = level.getRandom();
+- int randomInt = random.nextInt(3);
+- if (randomInt < 2 && random.nextFloat() < 0.5F * difficulty.getSpecialMultiplier()) {
+- randomInt++;
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
++ RandomSource randomsource = level.getRandom();
++ int i = randomsource.nextInt(3);
++
++ if (i < 2 && randomsource.nextFloat() < 0.5F * difficulty.getSpecialMultiplier()) {
++ ++i;
+ }
+
+- int i = 1 << randomInt;
+- this.setSize(i, true);
++ int j = 1 << i;
++
++ this.setSize(j, true);
+ return super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
+ }
+
+ float getSoundPitch() {
+ float f = this.isTiny() ? 1.4F : 0.8F;
++
+ return ((this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F) * f;
+ }
+
+@@ -363,61 +398,74 @@
+ }
+
+ @Override
+- public EntityDimensions getDimensions(Pose pose) {
+- return super.getDimensions(pose).scale(0.255F * (float)this.getSize());
++ public EntityDimensions getDimensions(EntityPose pose) {
++ return super.getDimensions(pose).scale(0.255F * (float) this.getSize());
+ }
+
+- static class SlimeAttackGoal extends Goal {
++ private static class SlimeMoveControl extends MoveControl {
++
++ private float yRot;
++ private int jumpDelay;
+ private final Slime slime;
+- private int growTiredTimer;
++ private boolean isAggressive;
+
+- public SlimeAttackGoal(Slime slime) {
++ public SlimeMoveControl(Slime slime) {
++ super(slime);
+ this.slime = slime;
+- this.setFlags(EnumSet.of(Goal.Flag.LOOK));
++ this.yRot = 180.0F * slime.getYRot() / 3.1415927F;
+ }
+
+- @Override
+- public boolean canUse() {
+- LivingEntity target = this.slime.getTarget();
+- return target != null && this.slime.canAttack(target) && this.slime.getMoveControl() instanceof Slime.SlimeMoveControl;
++ public void setDirection(float yRot, boolean aggressive) {
++ this.yRot = yRot;
++ this.isAggressive = aggressive;
+ }
+
+- @Override
+- public void start() {
+- this.growTiredTimer = reducedTickDelay(300);
+- super.start();
++ public void setWantedMovement(double speed) {
++ this.speedModifier = speed;
++ this.operation = MoveControl.Operation.MOVE_TO;
+ }
+
+ @Override
+- public boolean canContinueToUse() {
+- LivingEntity target = this.slime.getTarget();
+- return target != null && this.slime.canAttack(target) && --this.growTiredTimer > 0;
+- }
++ public void tick() {
++ this.mob.setYRot(this.rotlerp(this.mob.getYRot(), this.yRot, 90.0F));
++ this.mob.yHeadRot = this.mob.getYRot();
++ this.mob.yBodyRot = this.mob.getYRot();
++ if (this.operation != MoveControl.Operation.MOVE_TO) {
++ this.mob.setZza(0.0F);
++ } else {
++ this.operation = MoveControl.Operation.WAIT;
++ if (this.mob.onGround()) {
++ this.mob.setSpeed((float) (this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)));
++ if (this.jumpDelay-- <= 0) {
++ this.jumpDelay = this.slime.getJumpDelay();
++ if (this.isAggressive) {
++ this.jumpDelay /= 3;
++ }
+
+- @Override
+- public boolean requiresUpdateEveryTick() {
+- return true;
+- }
++ this.slime.getJumpControl().jump();
++ if (this.slime.doPlayJumpSound()) {
++ this.slime.playSound(this.slime.getJumpSound(), this.slime.getSoundVolume(), this.slime.getSoundPitch());
++ }
++ } else {
++ this.slime.xxa = 0.0F;
++ this.slime.zza = 0.0F;
++ this.mob.setSpeed(0.0F);
++ }
++ } else {
++ this.mob.setSpeed((float) (this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)));
++ }
+
+- @Override
+- public void tick() {
+- LivingEntity target = this.slime.getTarget();
+- if (target != null) {
+- this.slime.lookAt(target, 10.0F, 10.0F);
+ }
+-
+- if (this.slime.getMoveControl() instanceof Slime.SlimeMoveControl slimeMoveControl) {
+- slimeMoveControl.setDirection(this.slime.getYRot(), this.slime.isDealsDamage());
+- }
+ }
+ }
+
+- static class SlimeFloatGoal extends Goal {
++ private static class SlimeFloatGoal extends Goal {
++
+ private final Slime slime;
+
+ public SlimeFloatGoal(Slime slime) {
+ this.slime = slime;
+- this.setFlags(EnumSet.of(Goal.Flag.JUMP, Goal.Flag.MOVE));
++ this.setFlags(EnumSet.of(Goal.Type.JUMP, Goal.Type.MOVE));
+ slime.getNavigation().setCanFloat(true);
+ }
+
+@@ -437,115 +485,129 @@
+ this.slime.getJumpControl().jump();
+ }
+
+- if (this.slime.getMoveControl() instanceof Slime.SlimeMoveControl slimeMoveControl) {
+- slimeMoveControl.setWantedMovement(1.2);
++ MoveControl controllermove = this.slime.getMoveControl();
++
++ if (controllermove instanceof Slime.SlimeMoveControl) {
++ Slime.SlimeMoveControl entityslime_controllermoveslime = (Slime.SlimeMoveControl) controllermove;
++
++ entityslime_controllermoveslime.setWantedMovement(1.2D);
+ }
++
+ }
+ }
+
+- static class SlimeKeepOnJumpingGoal extends Goal {
++ private static class SlimeAttackGoal extends Goal {
++
+ private final Slime slime;
++ private int growTiredTimer;
+
+- public SlimeKeepOnJumpingGoal(Slime slime) {
++ public SlimeAttackGoal(Slime slime) {
+ this.slime = slime;
+- this.setFlags(EnumSet.of(Goal.Flag.JUMP, Goal.Flag.MOVE));
++ this.setFlags(EnumSet.of(Goal.Type.LOOK));
+ }
+
+ @Override
+ public boolean canUse() {
+- return !this.slime.isPassenger();
++ LivingEntity entityliving = this.slime.getTarget();
++
++ return entityliving == null ? false : (!this.slime.canAttack(entityliving) ? false : this.slime.getMoveControl() instanceof Slime.SlimeMoveControl);
+ }
+
+ @Override
+- public void tick() {
+- if (this.slime.getMoveControl() instanceof Slime.SlimeMoveControl slimeMoveControl) {
+- slimeMoveControl.setWantedMovement(1.0);
+- }
++ public void start() {
++ this.growTiredTimer = reducedTickDelay(300);
++ super.start();
+ }
+- }
+
+- static class SlimeMoveControl extends MoveControl {
+- private float yRot;
+- private int jumpDelay;
+- private final Slime slime;
+- private boolean isAggressive;
++ @Override
++ public boolean canContinueToUse() {
++ LivingEntity entityliving = this.slime.getTarget();
+
+- public SlimeMoveControl(Slime mob) {
+- super(mob);
+- this.slime = mob;
+- this.yRot = 180.0F * mob.getYRot() / (float) Math.PI;
++ return entityliving == null ? false : (!this.slime.canAttack(entityliving) ? false : --this.growTiredTimer > 0);
+ }
+
+- public void setDirection(float yRot, boolean aggressive) {
+- this.yRot = yRot;
+- this.isAggressive = aggressive;
++ @Override
++ public boolean requiresUpdateEveryTick() {
++ return true;
+ }
+
+- public void setWantedMovement(double speed) {
+- this.speedModifier = speed;
+- this.operation = MoveControl.Operation.MOVE_TO;
+- }
+-
+ @Override
+ public void tick() {
+- this.mob.setYRot(this.rotlerp(this.mob.getYRot(), this.yRot, 90.0F));
+- this.mob.yHeadRot = this.mob.getYRot();
+- this.mob.yBodyRot = this.mob.getYRot();
+- if (this.operation != MoveControl.Operation.MOVE_TO) {
+- this.mob.setZza(0.0F);
+- } else {
+- this.operation = MoveControl.Operation.WAIT;
+- if (this.mob.onGround()) {
+- this.mob.setSpeed((float)(this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)));
+- if (this.jumpDelay-- <= 0) {
+- this.jumpDelay = this.slime.getJumpDelay();
+- if (this.isAggressive) {
+- this.jumpDelay /= 3;
+- }
++ LivingEntity entityliving = this.slime.getTarget();
+
+- this.slime.getJumpControl().jump();
+- if (this.slime.doPlayJumpSound()) {
+- this.slime.playSound(this.slime.getJumpSound(), this.slime.getSoundVolume(), this.slime.getSoundPitch());
+- }
+- } else {
+- this.slime.xxa = 0.0F;
+- this.slime.zza = 0.0F;
+- this.mob.setSpeed(0.0F);
+- }
+- } else {
+- this.mob.setSpeed((float)(this.speedModifier * this.mob.getAttributeValue(Attributes.MOVEMENT_SPEED)));
+- }
++ if (entityliving != null) {
++ this.slime.lookAt(entityliving, 10.0F, 10.0F);
+ }
++
++ MoveControl controllermove = this.slime.getMoveControl();
++
++ if (controllermove instanceof Slime.SlimeMoveControl) {
++ Slime.SlimeMoveControl entityslime_controllermoveslime = (Slime.SlimeMoveControl) controllermove;
++
++ entityslime_controllermoveslime.setDirection(this.slime.getYRot(), this.slime.isDealsDamage());
++ }
++
+ }
+ }
+
+- static class SlimeRandomDirectionGoal extends Goal {
++ private static class SlimeRandomDirectionGoal extends Goal {
++
+ private final Slime slime;
+ private float chosenDegrees;
+ private int nextRandomizeTime;
+
+ public SlimeRandomDirectionGoal(Slime slime) {
+ this.slime = slime;
+- this.setFlags(EnumSet.of(Goal.Flag.LOOK));
++ this.setFlags(EnumSet.of(Goal.Type.LOOK));
+ }
+
+ @Override
+ public boolean canUse() {
+- return this.slime.getTarget() == null
+- && (this.slime.onGround() || this.slime.isInWater() || this.slime.isInLava() || this.slime.hasEffect(MobEffects.LEVITATION))
+- && this.slime.getMoveControl() instanceof Slime.SlimeMoveControl;
++ return this.slime.getTarget() == null && (this.slime.onGround() || this.slime.isInWater() || this.slime.isInLava() || this.slime.hasEffect(MobEffects.LEVITATION)) && this.slime.getMoveControl() instanceof Slime.SlimeMoveControl;
+ }
+
+ @Override
+ public void tick() {
+ if (--this.nextRandomizeTime <= 0) {
+ this.nextRandomizeTime = this.adjustedTickDelay(40 + this.slime.getRandom().nextInt(60));
+- this.chosenDegrees = (float)this.slime.getRandom().nextInt(360);
++ this.chosenDegrees = (float) this.slime.getRandom().nextInt(360);
+ }
+
+- if (this.slime.getMoveControl() instanceof Slime.SlimeMoveControl slimeMoveControl) {
+- slimeMoveControl.setDirection(this.chosenDegrees, false);
++ MoveControl controllermove = this.slime.getMoveControl();
++
++ if (controllermove instanceof Slime.SlimeMoveControl) {
++ Slime.SlimeMoveControl entityslime_controllermoveslime = (Slime.SlimeMoveControl) controllermove;
++
++ entityslime_controllermoveslime.setDirection(this.chosenDegrees, false);
+ }
++
+ }
+ }
++
++ private static class SlimeKeepOnJumpingGoal extends Goal {
++
++ private final Slime slime;
++
++ public SlimeKeepOnJumpingGoal(Slime slime) {
++ this.slime = slime;
++ this.setFlags(EnumSet.of(Goal.Type.JUMP, Goal.Type.MOVE));
++ }
++
++ @Override
++ public boolean canUse() {
++ return !this.slime.isPassenger();
++ }
++
++ @Override
++ public void tick() {
++ MoveControl controllermove = this.slime.getMoveControl();
++
++ if (controllermove instanceof Slime.SlimeMoveControl) {
++ Slime.SlimeMoveControl entityslime_controllermoveslime = (Slime.SlimeMoveControl) controllermove;
++
++ entityslime_controllermoveslime.setWantedMovement(1.0D);
++ }
++
++ }
++ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/SpellcasterIllager.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/SpellcasterIllager.java.patch
new file mode 100644
index 0000000000..c726efdf25
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/SpellcasterIllager.java.patch
@@ -0,0 +1,271 @@
+--- a/net/minecraft/world/entity/monster/SpellcasterIllager.java
++++ b/net/minecraft/world/entity/monster/SpellcasterIllager.java
+@@ -15,20 +15,25 @@
+ import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.ai.goal.Goal;
+ import net.minecraft.world.level.Level;
++// CraftBukkit start
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++// CraftBukkit end
+
+ public abstract class SpellcasterIllager extends AbstractIllager {
++
+ private static final EntityDataAccessor<Byte> DATA_SPELL_CASTING_ID = SynchedEntityData.defineId(SpellcasterIllager.class, EntityDataSerializers.BYTE);
+ protected int spellCastingTickCount;
+- private SpellcasterIllager.IllagerSpell currentSpell = SpellcasterIllager.IllagerSpell.NONE;
++ private SpellcasterIllager.IllagerSpell currentSpell;
+
+ protected SpellcasterIllager(EntityType<? extends SpellcasterIllager> entityType, Level level) {
+ super(entityType, level);
++ this.currentSpell = SpellcasterIllager.IllagerSpell.NONE;
+ }
+
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_SPELL_CASTING_ID, (byte)0);
++ this.entityData.define(SpellcasterIllager.DATA_SPELL_CASTING_ID, (byte) 0);
+ }
+
+ @Override
+@@ -44,51 +49,48 @@
+ }
+
+ @Override
+- public AbstractIllager.IllagerArmPose getArmPose() {
+- if (this.isCastingSpell()) {
+- return AbstractIllager.IllagerArmPose.SPELLCASTING;
+- } else {
+- return this.isCelebrating() ? AbstractIllager.IllagerArmPose.CELEBRATING : AbstractIllager.IllagerArmPose.CROSSED;
+- }
++ public AbstractIllager.a getArmPose() {
++ return this.isCastingSpell() ? AbstractIllager.a.SPELLCASTING : (this.isCelebrating() ? AbstractIllager.a.CELEBRATING : AbstractIllager.a.CROSSED);
+ }
+
+ public boolean isCastingSpell() {
+- return this.level().isClientSide ? this.entityData.get(DATA_SPELL_CASTING_ID) > 0 : this.spellCastingTickCount > 0;
++ return this.level().isClientSide ? (Byte) this.entityData.get(SpellcasterIllager.DATA_SPELL_CASTING_ID) > 0 : this.spellCastingTickCount > 0;
+ }
+
+ public void setIsCastingSpell(SpellcasterIllager.IllagerSpell currentSpell) {
+ this.currentSpell = currentSpell;
+- this.entityData.set(DATA_SPELL_CASTING_ID, (byte)currentSpell.id);
++ this.entityData.set(SpellcasterIllager.DATA_SPELL_CASTING_ID, (byte) currentSpell.id);
+ }
+
+- protected SpellcasterIllager.IllagerSpell getCurrentSpell() {
+- return !this.level().isClientSide ? this.currentSpell : SpellcasterIllager.IllagerSpell.byId(this.entityData.get(DATA_SPELL_CASTING_ID));
++ public SpellcasterIllager.IllagerSpell getCurrentSpell() {
++ return !this.level().isClientSide ? this.currentSpell : SpellcasterIllager.IllagerSpell.byId((Byte) this.entityData.get(SpellcasterIllager.DATA_SPELL_CASTING_ID));
+ }
+
+ @Override
+ protected void customServerAiStep() {
+ super.customServerAiStep();
+ if (this.spellCastingTickCount > 0) {
+- this.spellCastingTickCount--;
++ --this.spellCastingTickCount;
+ }
++
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (this.level().isClientSide && this.isCastingSpell()) {
+- SpellcasterIllager.IllagerSpell currentSpell = this.getCurrentSpell();
+- double d = currentSpell.spellColor[0];
+- double d1 = currentSpell.spellColor[1];
+- double d2 = currentSpell.spellColor[2];
+- float f = this.yBodyRot * (float) (Math.PI / 180.0) + Mth.cos((float)this.tickCount * 0.6662F) * 0.25F;
+- float cos = Mth.cos(f);
+- float sin = Mth.sin(f);
+- this.level()
+- .addParticle(ParticleTypes.ENTITY_EFFECT, this.getX() + (double)cos * 0.6, this.getY() + 1.8, this.getZ() + (double)sin * 0.6, d, d1, d2);
+- this.level()
+- .addParticle(ParticleTypes.ENTITY_EFFECT, this.getX() - (double)cos * 0.6, this.getY() + 1.8, this.getZ() - (double)sin * 0.6, d, d1, d2);
++ SpellcasterIllager.IllagerSpell entityillagerwizard_spell = this.getCurrentSpell();
++ double d0 = entityillagerwizard_spell.spellColor[0];
++ double d1 = entityillagerwizard_spell.spellColor[1];
++ double d2 = entityillagerwizard_spell.spellColor[2];
++ float f = this.yBodyRot * 0.017453292F + Mth.cos((float) this.tickCount * 0.6662F) * 0.25F;
++ float f1 = Mth.cos(f);
++ float f2 = Mth.sin(f);
++
++ this.level().addParticle(ParticleTypes.ENTITY_EFFECT, this.getX() + (double) f1 * 0.6D, this.getY() + 1.8D, this.getZ() + (double) f2 * 0.6D, d0, d1, d2);
++ this.level().addParticle(ParticleTypes.ENTITY_EFFECT, this.getX() - (double) f1 * 0.6D, this.getY() + 1.8D, this.getZ() - (double) f2 * 0.6D, d0, d1, d2);
+ }
++
+ }
+
+ protected int getSpellCastingTime() {
+@@ -97,80 +99,45 @@
+
+ protected abstract SoundEvent getCastingSoundEvent();
+
+- protected static enum IllagerSpell {
+- NONE(0, 0.0, 0.0, 0.0),
+- SUMMON_VEX(1, 0.7, 0.7, 0.8),
+- FANGS(2, 0.4, 0.3, 0.35),
+- WOLOLO(3, 0.7, 0.5, 0.2),
+- DISAPPEAR(4, 0.3, 0.3, 0.8),
+- BLINDNESS(5, 0.1, 0.1, 0.2);
++ public static enum IllagerSpell {
+
+- private static final IntFunction<SpellcasterIllager.IllagerSpell> BY_ID = ByIdMap.continuous(
+- illagerSpell -> illagerSpell.id, values(), ByIdMap.OutOfBoundsStrategy.ZERO
+- );
++ NONE(0, 0.0D, 0.0D, 0.0D), SUMMON_VEX(1, 0.7D, 0.7D, 0.8D), FANGS(2, 0.4D, 0.3D, 0.35D), WOLOLO(3, 0.7D, 0.5D, 0.2D), DISAPPEAR(4, 0.3D, 0.3D, 0.8D), BLINDNESS(5, 0.1D, 0.1D, 0.2D);
++
++ private static final IntFunction<SpellcasterIllager.IllagerSpell> BY_ID = ByIdMap.continuous((entityillagerwizard_spell) -> {
++ return entityillagerwizard_spell.id;
++ }, values(), ByIdMap.a.ZERO);
+ final int id;
+ final double[] spellColor;
+
+- private IllagerSpell(int id, double red, double green, double blue) {
+- this.id = id;
+- this.spellColor = new double[]{red, green, blue};
++ private IllagerSpell(int i, double d0, double d1, double d2) {
++ this.id = i;
++ this.spellColor = new double[]{d0, d1, d2};
+ }
+
+ public static SpellcasterIllager.IllagerSpell byId(int id) {
+- return BY_ID.apply(id);
++ return (SpellcasterIllager.IllagerSpell) SpellcasterIllager.IllagerSpell.BY_ID.apply(id);
+ }
+ }
+
+- protected class SpellcasterCastingSpellGoal extends Goal {
+- public SpellcasterCastingSpellGoal() {
+- this.setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK));
+- }
+-
+- @Override
+- public boolean canUse() {
+- return SpellcasterIllager.this.getSpellCastingTime() > 0;
+- }
+-
+- @Override
+- public void start() {
+- super.start();
+- SpellcasterIllager.this.navigation.stop();
+- }
+-
+- @Override
+- public void stop() {
+- super.stop();
+- SpellcasterIllager.this.setIsCastingSpell(SpellcasterIllager.IllagerSpell.NONE);
+- }
+-
+- @Override
+- public void tick() {
+- if (SpellcasterIllager.this.getTarget() != null) {
+- SpellcasterIllager.this.getLookControl()
+- .setLookAt(
+- SpellcasterIllager.this.getTarget(), (float)SpellcasterIllager.this.getMaxHeadYRot(), (float)SpellcasterIllager.this.getMaxHeadXRot()
+- );
+- }
+- }
+- }
+-
+ protected abstract class SpellcasterUseSpellGoal extends Goal {
++
+ protected int attackWarmupDelay;
+ protected int nextAttackTickCount;
+
++ protected SpellcasterUseSpellGoal() {}
++
+ @Override
+ public boolean canUse() {
+- LivingEntity target = SpellcasterIllager.this.getTarget();
+- return target != null
+- && target.isAlive()
+- && !SpellcasterIllager.this.isCastingSpell()
+- && SpellcasterIllager.this.tickCount >= this.nextAttackTickCount;
++ LivingEntity entityliving = SpellcasterIllager.this.getTarget();
++
++ return entityliving != null && entityliving.isAlive() ? (SpellcasterIllager.this.isCastingSpell() ? false : SpellcasterIllager.this.tickCount >= this.nextAttackTickCount) : false;
+ }
+
+ @Override
+ public boolean canContinueToUse() {
+- LivingEntity target = SpellcasterIllager.this.getTarget();
+- return target != null && target.isAlive() && this.attackWarmupDelay > 0;
++ LivingEntity entityliving = SpellcasterIllager.this.getTarget();
++
++ return entityliving != null && entityliving.isAlive() && this.attackWarmupDelay > 0;
+ }
+
+ @Override
+@@ -178,9 +145,10 @@
+ this.attackWarmupDelay = this.adjustedTickDelay(this.getCastWarmupTime());
+ SpellcasterIllager.this.spellCastingTickCount = this.getCastingTime();
+ this.nextAttackTickCount = SpellcasterIllager.this.tickCount + this.getCastingInterval();
+- SoundEvent spellPrepareSound = this.getSpellPrepareSound();
+- if (spellPrepareSound != null) {
+- SpellcasterIllager.this.playSound(spellPrepareSound, 1.0F, 1.0F);
++ SoundEvent soundeffect = this.getSpellPrepareSound();
++
++ if (soundeffect != null) {
++ SpellcasterIllager.this.playSound(soundeffect, 1.0F, 1.0F);
+ }
+
+ SpellcasterIllager.this.setIsCastingSpell(this.getSpell());
+@@ -188,11 +156,17 @@
+
+ @Override
+ public void tick() {
+- this.attackWarmupDelay--;
++ --this.attackWarmupDelay;
+ if (this.attackWarmupDelay == 0) {
++ // CraftBukkit start
++ if (!CraftEventFactory.handleEntitySpellCastEvent(SpellcasterIllager.this, this.getSpell())) {
++ return;
++ }
++ // CraftBukkit end
+ this.performSpellCasting();
+ SpellcasterIllager.this.playSound(SpellcasterIllager.this.getCastingSoundEvent(), 1.0F, 1.0F);
+ }
++
+ }
+
+ protected abstract void performSpellCasting();
+@@ -210,4 +184,36 @@
+
+ protected abstract SpellcasterIllager.IllagerSpell getSpell();
+ }
++
++ protected class SpellcasterCastingSpellGoal extends Goal {
++
++ public SpellcasterCastingSpellGoal() {
++ this.setFlags(EnumSet.of(Goal.Type.MOVE, Goal.Type.LOOK));
++ }
++
++ @Override
++ public boolean canUse() {
++ return SpellcasterIllager.this.getSpellCastingTime() > 0;
++ }
++
++ @Override
++ public void start() {
++ super.start();
++ SpellcasterIllager.this.navigation.stop();
++ }
++
++ @Override
++ public void stop() {
++ super.stop();
++ SpellcasterIllager.this.setIsCastingSpell(SpellcasterIllager.IllagerSpell.NONE);
++ }
++
++ @Override
++ public void tick() {
++ if (SpellcasterIllager.this.getTarget() != null) {
++ SpellcasterIllager.this.getLookControl().setLookAt(SpellcasterIllager.this.getTarget(), (float) SpellcasterIllager.this.getMaxHeadYRot(), (float) SpellcasterIllager.this.getMaxHeadXRot());
++ }
++
++ }
++ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Spider.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Spider.java.patch
new file mode 100644
index 0000000000..8019035f12
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Spider.java.patch
@@ -0,0 +1,278 @@
+--- a/net/minecraft/world/entity/monster/Spider.java
++++ b/net/minecraft/world/entity/monster/Spider.java
+@@ -17,12 +17,12 @@
+ import net.minecraft.world.effect.MobEffects;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
++import net.minecraft.world.entity.EntityPose;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
++import net.minecraft.world.entity.EnumMonsterType;
++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.Pose;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+ import net.minecraft.world.entity.ai.goal.FloatGoal;
+@@ -40,11 +40,12 @@
+ import net.minecraft.world.level.Level;
+ 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 net.minecraft.world.phys.Vec3;
+ import org.joml.Vector3f;
+
+ public class Spider extends Monster {
++
+ private static final EntityDataAccessor<Byte> DATA_FLAGS_ID = SynchedEntityData.defineId(Spider.class, EntityDataSerializers.BYTE);
+ private static final float SPIDER_SPECIAL_EFFECT_CHANCE = 0.1F;
+
+@@ -57,17 +58,17 @@
+ this.goalSelector.addGoal(1, new FloatGoal(this));
+ this.goalSelector.addGoal(3, new LeapAtTargetGoal(this, 0.4F));
+ this.goalSelector.addGoal(4, new Spider.SpiderAttackGoal(this));
+- this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8));
++ this.goalSelector.addGoal(5, new WaterAvoidingRandomStrollGoal(this, 0.8D));
+ this.goalSelector.addGoal(6, new LookAtPlayerGoal(this, Player.class, 8.0F));
+ this.goalSelector.addGoal(6, new RandomLookAroundGoal(this));
+- this.targetSelector.addGoal(1, new HurtByTargetGoal(this));
++ this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[0]));
+ this.targetSelector.addGoal(2, new Spider.SpiderTargetGoal<>(this, Player.class));
+ this.targetSelector.addGoal(3, new Spider.SpiderTargetGoal<>(this, IronGolem.class));
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height * 0.85F, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height * 0.85F, 0.0F);
+ }
+
+ @Override
+@@ -78,7 +79,7 @@
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_FLAGS_ID, (byte)0);
++ this.entityData.define(Spider.DATA_FLAGS_ID, (byte) 0);
+ }
+
+ @Override
+@@ -87,10 +88,11 @@
+ if (!this.level().isClientSide) {
+ this.setClimbing(this.horizontalCollision);
+ }
++
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 16.0).add(Attributes.MOVEMENT_SPEED, 0.3F);
++ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 16.0D).add(Attributes.MOVEMENT_SPEED, 0.30000001192092896D);
+ }
+
+ @Override
+@@ -109,7 +111,7 @@
+ }
+
+ @Override
+- protected void playStepSound(BlockPos pos, BlockState block) {
++ protected void playStepSound(BlockPos pos, IBlockData block) {
+ this.playSound(SoundEvents.SPIDER_STEP, 0.15F, 1.0F);
+ }
+
+@@ -119,72 +121,76 @@
+ }
+
+ @Override
+- public void makeStuckInBlock(BlockState state, Vec3 motionMultiplier) {
++ public void makeStuckInBlock(IBlockData state, Vec3 motionMultiplier) {
+ if (!state.is(Blocks.COBWEB)) {
+ super.makeStuckInBlock(state, motionMultiplier);
+ }
++
+ }
+
+ @Override
+- public MobType getMobType() {
+- return MobType.ARTHROPOD;
++ public EnumMonsterType getMobType() {
++ return EnumMonsterType.ARTHROPOD;
+ }
+
+ @Override
+ public boolean canBeAffected(MobEffectInstance potioneffect) {
+- return potioneffect.getEffect() != MobEffects.POISON && super.canBeAffected(potioneffect);
++ return potioneffect.getEffect() == MobEffects.POISON ? false : super.canBeAffected(potioneffect);
+ }
+
+ public boolean isClimbing() {
+- return (this.entityData.get(DATA_FLAGS_ID) & 1) != 0;
++ return ((Byte) this.entityData.get(Spider.DATA_FLAGS_ID) & 1) != 0;
+ }
+
+ public void setClimbing(boolean climbing) {
+- byte b = this.entityData.get(DATA_FLAGS_ID);
++ byte b0 = (Byte) this.entityData.get(Spider.DATA_FLAGS_ID);
++
+ if (climbing) {
+- b = (byte)(b | 1);
++ b0 = (byte) (b0 | 1);
+ } else {
+- b = (byte)(b & -2);
++ b0 &= -2;
+ }
+
+- this.entityData.set(DATA_FLAGS_ID, b);
++ this.entityData.set(Spider.DATA_FLAGS_ID, b0);
+ }
+
+ @Nullable
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
+- SpawnGroupData var9 = super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
+- RandomSource random = level.getRandom();
+- if (random.nextInt(100) == 0) {
+- Skeleton skeleton = EntityType.SKELETON.create(this.level());
+- if (skeleton != null) {
+- skeleton.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F);
+- skeleton.finalizeSpawn(level, difficulty, reason, null, null);
+- skeleton.startRiding(this);
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
++ Object object = super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
++ RandomSource randomsource = level.getRandom();
++
++ if (randomsource.nextInt(100) == 0) {
++ Skeleton entityskeleton = (Skeleton) EntityType.SKELETON.create(this.level());
++
++ if (entityskeleton != null) {
++ entityskeleton.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F);
++ entityskeleton.finalizeSpawn(level, difficulty, reason, (GroupDataEntity) null, (CompoundTag) null);
++ entityskeleton.startRiding(this);
+ }
+ }
+
+- if (var9 == null) {
+- var9 = new Spider.SpiderEffectsGroupData();
+- if (level.getDifficulty() == Difficulty.HARD && random.nextFloat() < 0.1F * difficulty.getSpecialMultiplier()) {
+- ((Spider.SpiderEffectsGroupData)var9).setRandomEffect(random);
++ if (object == null) {
++ object = new Spider.SpiderEffectsGroupData();
++ if (level.getDifficulty() == Difficulty.HARD && randomsource.nextFloat() < 0.1F * difficulty.getSpecialMultiplier()) {
++ ((Spider.SpiderEffectsGroupData) object).setRandomEffect(randomsource);
+ }
+ }
+
+- if (var9 instanceof Spider.SpiderEffectsGroupData spiderEffectsGroupData) {
+- MobEffect mobEffect = spiderEffectsGroupData.effect;
+- if (mobEffect != null) {
+- this.addEffect(new MobEffectInstance(mobEffect, -1));
++ if (object instanceof Spider.SpiderEffectsGroupData) {
++ Spider.SpiderEffectsGroupData entityspider_groupdataspider = (Spider.SpiderEffectsGroupData) object;
++ MobEffect mobeffectlist = entityspider_groupdataspider.effect;
++
++ if (mobeffectlist != null) {
++ this.addEffect(new MobEffectInstance(mobeffectlist, -1), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.SPIDER_SPAWN); // CraftBukkit
+ }
+ }
+
+- return (SpawnGroupData)var9;
++ return (GroupDataEntity) object;
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return 0.65F;
+ }
+
+@@ -193,9 +199,10 @@
+ return entity.getBbWidth() <= this.getBbWidth() ? -0.3125F : 0.0F;
+ }
+
+- static class SpiderAttackGoal extends MeleeAttackGoal {
++ private static class SpiderAttackGoal extends MeleeAttackGoal {
++
+ public SpiderAttackGoal(Spider spider) {
+- super(spider, 1.0, true);
++ super(spider, 1.0D, true);
+ }
+
+ @Override
+@@ -205,9 +212,10 @@
+
+ @Override
+ public boolean canContinueToUse() {
+- float lightLevelDependentMagicValue = this.mob.getLightLevelDependentMagicValue();
+- if (lightLevelDependentMagicValue >= 0.5F && this.mob.getRandom().nextInt(100) == 0) {
+- this.mob.setTarget(null);
++ float f = this.mob.getLightLevelDependentMagicValue();
++
++ if (f >= 0.5F && this.mob.getRandom().nextInt(100) == 0) {
++ this.mob.setTarget((LivingEntity) null);
+ return false;
+ } else {
+ return super.canContinueToUse();
+@@ -215,33 +223,40 @@
+ }
+ }
+
+- public static class SpiderEffectsGroupData implements SpawnGroupData {
++ private static class SpiderTargetGoal<T extends LivingEntity> extends NearestAttackableTargetGoal<T> {
++
++ public SpiderTargetGoal(Spider spider, Class<T> entityTypeToTarget) {
++ super(spider, entityTypeToTarget, true);
++ }
++
++ @Override
++ public boolean canUse() {
++ float f = this.mob.getLightLevelDependentMagicValue();
++
++ return f >= 0.5F ? false : super.canUse();
++ }
++ }
++
++ public static class SpiderEffectsGroupData implements GroupDataEntity {
++
+ @Nullable
+ public MobEffect effect;
+
++ public SpiderEffectsGroupData() {}
++
+ public void setRandomEffect(RandomSource random) {
+- int randomInt = random.nextInt(5);
+- if (randomInt <= 1) {
++ int i = random.nextInt(5);
++
++ if (i <= 1) {
+ this.effect = MobEffects.MOVEMENT_SPEED;
+- } else if (randomInt <= 2) {
++ } else if (i <= 2) {
+ this.effect = MobEffects.DAMAGE_BOOST;
+- } else if (randomInt <= 3) {
++ } else if (i <= 3) {
+ this.effect = MobEffects.REGENERATION;
+- } else if (randomInt <= 4) {
++ } else if (i <= 4) {
+ this.effect = MobEffects.INVISIBILITY;
+ }
+- }
+- }
+
+- static class SpiderTargetGoal<T extends LivingEntity> extends NearestAttackableTargetGoal<T> {
+- public SpiderTargetGoal(Spider spider, Class<T> entityTypeToTarget) {
+- super(spider, entityTypeToTarget, true);
+ }
+-
+- @Override
+- public boolean canUse() {
+- float lightLevelDependentMagicValue = this.mob.getLightLevelDependentMagicValue();
+- return !(lightLevelDependentMagicValue >= 0.5F) && super.canUse();
+- }
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Strider.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Strider.java.patch
new file mode 100644
index 0000000000..23517c9040
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Strider.java.patch
@@ -0,0 +1,583 @@
+--- a/net/minecraft/world/entity/monster/Strider.java
++++ b/net/minecraft/world/entity/monster/Strider.java
+@@ -1,6 +1,8 @@
+ package net.minecraft.world.entity.monster;
+
+ import com.google.common.collect.Sets;
++import com.google.common.collect.UnmodifiableIterator;
++import java.util.Iterator;
+ import java.util.Set;
+ import java.util.UUID;
+ import javax.annotation.Nullable;
+@@ -19,22 +21,22 @@
+ import net.minecraft.util.Mth;
+ import net.minecraft.util.RandomSource;
+ import net.minecraft.world.DifficultyInstance;
+-import net.minecraft.world.InteractionHand;
++import net.minecraft.world.EnumHand;
+ import net.minecraft.world.InteractionResult;
+ import net.minecraft.world.damagesource.DamageSource;
+ import net.minecraft.world.entity.AgeableMob;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
++import net.minecraft.world.entity.EntityPose;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
+ import net.minecraft.world.entity.EquipmentSlot;
++import net.minecraft.world.entity.GroupDataEntity;
++import net.minecraft.world.entity.ISteerable;
+ import net.minecraft.world.entity.ItemBasedSteering;
+-import net.minecraft.world.entity.ItemSteerable;
+ import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.Mob;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.Pose;
+ import net.minecraft.world.entity.Saddleable;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.ai.attributes.AttributeInstance;
+ import net.minecraft.world.entity.ai.attributes.AttributeModifier;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+@@ -55,28 +57,28 @@
+ import net.minecraft.world.item.ItemStack;
+ import net.minecraft.world.item.Items;
+ import net.minecraft.world.item.crafting.Ingredient;
++import net.minecraft.world.level.IMaterial;
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.level.LevelAccessor;
+ import net.minecraft.world.level.LevelReader;
+ import net.minecraft.world.level.ServerLevelAccessor;
+ import net.minecraft.world.level.block.Blocks;
+ import net.minecraft.world.level.block.LiquidBlock;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import net.minecraft.world.level.material.FluidState;
+ import net.minecraft.world.level.pathfinder.BlockPathTypes;
+-import net.minecraft.world.level.pathfinder.PathComputationType;
+ import net.minecraft.world.level.pathfinder.PathFinder;
++import net.minecraft.world.level.pathfinder.PathMode;
+ import net.minecraft.world.level.pathfinder.WalkNodeEvaluator;
+ import net.minecraft.world.phys.AABB;
+ import net.minecraft.world.phys.Vec3;
+ import net.minecraft.world.phys.shapes.CollisionContext;
+ import org.joml.Vector3f;
+
+-public class Strider extends Animal implements ItemSteerable, Saddleable {
++public class Strider extends Animal implements ISteerable, Saddleable {
++
+ private static final UUID SUFFOCATING_MODIFIER_UUID = UUID.fromString("9e362924-01de-4ddd-a2b2-d0f7a405a174");
+- private static final AttributeModifier SUFFOCATING_MODIFIER = new AttributeModifier(
+- SUFFOCATING_MODIFIER_UUID, "Strider suffocating modifier", -0.34F, AttributeModifier.Operation.MULTIPLY_BASE
+- );
++ private static final AttributeModifier SUFFOCATING_MODIFIER = new AttributeModifier(Strider.SUFFOCATING_MODIFIER_UUID, "Strider suffocating modifier", -0.3400000035762787D, AttributeModifier.Operation.MULTIPLY_BASE);
+ private static final float SUFFOCATE_STEERING_MODIFIER = 0.35F;
+ private static final float STEERING_MODIFIER = 0.55F;
+ private static final Ingredient FOOD_ITEMS = Ingredient.of(Items.WARPED_FUNGUS);
+@@ -84,12 +86,13 @@
+ private static final EntityDataAccessor<Integer> DATA_BOOST_TIME = SynchedEntityData.defineId(Strider.class, EntityDataSerializers.INT);
+ private static final EntityDataAccessor<Boolean> DATA_SUFFOCATING = SynchedEntityData.defineId(Strider.class, EntityDataSerializers.BOOLEAN);
+ private static final EntityDataAccessor<Boolean> DATA_SADDLE_ID = SynchedEntityData.defineId(Strider.class, EntityDataSerializers.BOOLEAN);
+- private final ItemBasedSteering steering = new ItemBasedSteering(this.entityData, DATA_BOOST_TIME, DATA_SADDLE_ID);
++ public final ItemBasedSteering steering;
+ @Nullable
+ private TemptGoal temptGoal;
+
+ public Strider(EntityType<? extends Strider> entityType, Level level) {
+ super(entityType, level);
++ this.steering = new ItemBasedSteering(this.entityData, Strider.DATA_BOOST_TIME, Strider.DATA_SADDLE_ID);
+ this.blocksBuilding = true;
+ this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F);
+ this.setPathfindingMalus(BlockPathTypes.LAVA, 0.0F);
+@@ -97,19 +100,19 @@
+ this.setPathfindingMalus(BlockPathTypes.DAMAGE_FIRE, 0.0F);
+ }
+
+- public static boolean checkStriderSpawnRules(EntityType<Strider> strider, LevelAccessor level, MobSpawnType spawnType, BlockPos pos, RandomSource random) {
+- BlockPos.MutableBlockPos mutableBlockPos = pos.mutable();
++ public static boolean checkStriderSpawnRules(EntityType<Strider> strider, LevelAccessor level, EnumMobSpawn spawnType, BlockPos pos, RandomSource random) {
++ BlockPos.MutableBlockPos blockposition_mutableblockposition = pos.mutable();
+
+ do {
+- mutableBlockPos.move(Direction.UP);
+- } while (level.getFluidState(mutableBlockPos).is(FluidTags.LAVA));
++ blockposition_mutableblockposition.move(Direction.UP);
++ } while (level.getFluidState(blockposition_mutableblockposition).is(FluidTags.LAVA));
+
+- return level.getBlockState(mutableBlockPos).isAir();
++ return level.getBlockState(blockposition_mutableblockposition).isAir();
+ }
+
+ @Override
+ public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
+- if (DATA_BOOST_TIME.equals(key) && this.level().isClientSide) {
++ if (Strider.DATA_BOOST_TIME.equals(key) && this.level().isClientSide) {
+ this.steering.onSynced();
+ }
+
+@@ -119,9 +122,9 @@
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_BOOST_TIME, 0);
+- this.entityData.define(DATA_SUFFOCATING, false);
+- this.entityData.define(DATA_SADDLE_ID, false);
++ this.entityData.define(Strider.DATA_BOOST_TIME, 0);
++ this.entityData.define(Strider.DATA_SUFFOCATING, false);
++ this.entityData.define(Strider.DATA_SADDLE_ID, false);
+ }
+
+ @Override
+@@ -150,37 +153,40 @@
+ public void equipSaddle(@Nullable SoundSource source) {
+ this.steering.setSaddle(true);
+ if (source != null) {
+- this.level().playSound(null, this, SoundEvents.STRIDER_SADDLE, source, 0.5F, 1.0F);
++ this.level().playSound((Player) null, (Entity) this, SoundEvents.STRIDER_SADDLE, source, 0.5F, 1.0F);
+ }
++
+ }
+
+ @Override
+ protected void registerGoals() {
+- this.goalSelector.addGoal(1, new PanicGoal(this, 1.65));
+- this.goalSelector.addGoal(2, new BreedGoal(this, 1.0));
+- this.temptGoal = new TemptGoal(this, 1.4, TEMPT_ITEMS, false);
++ this.goalSelector.addGoal(1, new PanicGoal(this, 1.65D));
++ this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D));
++ this.temptGoal = new TemptGoal(this, 1.4D, Strider.TEMPT_ITEMS, false);
+ this.goalSelector.addGoal(3, this.temptGoal);
+- this.goalSelector.addGoal(4, new Strider.StriderGoToLavaGoal(this, 1.0));
+- this.goalSelector.addGoal(5, new FollowParentGoal(this, 1.0));
+- this.goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0, 60));
++ this.goalSelector.addGoal(4, new Strider.StriderGoToLavaGoal(this, 1.0D));
++ this.goalSelector.addGoal(5, new FollowParentGoal(this, 1.0D));
++ this.goalSelector.addGoal(7, new RandomStrollGoal(this, 1.0D, 60));
+ this.goalSelector.addGoal(8, new LookAtPlayerGoal(this, Player.class, 8.0F));
+ this.goalSelector.addGoal(8, new RandomLookAroundGoal(this));
+ this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Strider.class, 8.0F));
+ }
+
+ public void setSuffocating(boolean suffocating) {
+- this.entityData.set(DATA_SUFFOCATING, suffocating);
+- AttributeInstance attribute = this.getAttribute(Attributes.MOVEMENT_SPEED);
+- if (attribute != null) {
+- attribute.removeModifier(SUFFOCATING_MODIFIER_UUID);
++ this.entityData.set(Strider.DATA_SUFFOCATING, suffocating);
++ AttributeInstance attributemodifiable = this.getAttribute(Attributes.MOVEMENT_SPEED);
++
++ if (attributemodifiable != null) {
++ attributemodifiable.removeModifier(Strider.SUFFOCATING_MODIFIER_UUID);
+ if (suffocating) {
+- attribute.addTransientModifier(SUFFOCATING_MODIFIER);
++ attributemodifiable.addTransientModifier(Strider.SUFFOCATING_MODIFIER);
+ }
+ }
++
+ }
+
+ public boolean isSuffocating() {
+- return this.entityData.get(DATA_SUFFOCATING);
++ return (Boolean) this.entityData.get(Strider.DATA_SUFFOCATING);
+ }
+
+ @Override
+@@ -189,11 +195,12 @@
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- float min = Math.min(0.25F, this.walkAnimation.speed());
+- float f1 = this.walkAnimation.position();
+- float f2 = 0.12F * Mth.cos(f1 * 1.5F) * 2.0F * min;
+- return new Vector3f(0.0F, entityDimensions.height + f2 * f, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ float f1 = Math.min(0.25F, this.walkAnimation.speed());
++ float f2 = this.walkAnimation.position();
++ float f3 = 0.12F * Mth.cos(f2 * 1.5F) * 2.0F * f1;
++
++ return new Vector3f(0.0F, entitysize.height + f3 * f, 0.0F);
+ }
+
+ @Override
+@@ -204,45 +211,61 @@
+ @Nullable
+ @Override
+ public LivingEntity getControllingPassenger() {
+- return (LivingEntity)(this.isSaddled() && this.getFirstPassenger() instanceof Player player && player.isHolding(Items.WARPED_FUNGUS_ON_A_STICK)
+- ? player
+- : super.getControllingPassenger());
++ if (this.isSaddled()) {
++ Entity entity = this.getFirstPassenger();
++
++ if (entity instanceof Player) {
++ Player entityhuman = (Player) entity;
++
++ if (entityhuman.isHolding(Items.WARPED_FUNGUS_ON_A_STICK)) {
++ return entityhuman;
++ }
++ }
++ }
++
++ return super.getControllingPassenger();
+ }
+
+ @Override
+ public Vec3 getDismountLocationForPassenger(LivingEntity livingEntity) {
+- Vec3[] vec3s = new Vec3[]{
+- getCollisionHorizontalEscapeVector((double)this.getBbWidth(), (double)livingEntity.getBbWidth(), livingEntity.getYRot()),
+- getCollisionHorizontalEscapeVector((double)this.getBbWidth(), (double)livingEntity.getBbWidth(), livingEntity.getYRot() - 22.5F),
+- getCollisionHorizontalEscapeVector((double)this.getBbWidth(), (double)livingEntity.getBbWidth(), livingEntity.getYRot() + 22.5F),
+- getCollisionHorizontalEscapeVector((double)this.getBbWidth(), (double)livingEntity.getBbWidth(), livingEntity.getYRot() - 45.0F),
+- getCollisionHorizontalEscapeVector((double)this.getBbWidth(), (double)livingEntity.getBbWidth(), livingEntity.getYRot() + 45.0F)
+- };
++ Vec3[] avec3d = new Vec3[]{getCollisionHorizontalEscapeVector((double) this.getBbWidth(), (double) livingEntity.getBbWidth(), livingEntity.getYRot()), getCollisionHorizontalEscapeVector((double) this.getBbWidth(), (double) livingEntity.getBbWidth(), livingEntity.getYRot() - 22.5F), getCollisionHorizontalEscapeVector((double) this.getBbWidth(), (double) livingEntity.getBbWidth(), livingEntity.getYRot() + 22.5F), getCollisionHorizontalEscapeVector((double) this.getBbWidth(), (double) livingEntity.getBbWidth(), livingEntity.getYRot() - 45.0F), getCollisionHorizontalEscapeVector((double) this.getBbWidth(), (double) livingEntity.getBbWidth(), livingEntity.getYRot() + 45.0F)};
+ Set<BlockPos> set = Sets.newLinkedHashSet();
+- double d = this.getBoundingBox().maxY;
+- double d1 = this.getBoundingBox().minY - 0.5;
+- BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
++ double d0 = this.getBoundingBox().maxY;
++ double d1 = this.getBoundingBox().minY - 0.5D;
++ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
++ Vec3[] avec3d1 = avec3d;
++ int i = avec3d.length;
+
+- for (Vec3 vec3 : vec3s) {
+- mutableBlockPos.set(this.getX() + vec3.x, d, this.getZ() + vec3.z);
++ for (int j = 0; j < i; ++j) {
++ Vec3 vec3d = avec3d1[j];
+
+- for (double d2 = d; d2 > d1; d2--) {
+- set.add(mutableBlockPos.immutable());
+- mutableBlockPos.move(Direction.DOWN);
++ blockposition_mutableblockposition.set(this.getX() + vec3d.x, d0, this.getZ() + vec3d.z);
++
++ for (double d2 = d0; d2 > d1; --d2) {
++ set.add(blockposition_mutableblockposition.immutable());
++ blockposition_mutableblockposition.move(Direction.DOWN);
+ }
+ }
+
+- for (BlockPos blockPos : set) {
+- if (!this.level().getFluidState(blockPos).is(FluidTags.LAVA)) {
+- double blockFloorHeight = this.level().getBlockFloorHeight(blockPos);
+- if (DismountHelper.isBlockFloorValid(blockFloorHeight)) {
+- Vec3 vec31 = Vec3.upFromBottomCenterOf(blockPos, blockFloorHeight);
++ Iterator iterator = set.iterator();
+
+- for (Pose pose : livingEntity.getDismountPoses()) {
+- AABB localBoundsForPose = livingEntity.getLocalBoundsForPose(pose);
+- if (DismountHelper.canDismountTo(this.level(), livingEntity, localBoundsForPose.move(vec31))) {
+- livingEntity.setPose(pose);
+- return vec31;
++ while (iterator.hasNext()) {
++ BlockPos blockposition = (BlockPos) iterator.next();
++
++ if (!this.level().getFluidState(blockposition).is(FluidTags.LAVA)) {
++ double d3 = this.level().getBlockFloorHeight(blockposition);
++
++ if (DismountHelper.isBlockFloorValid(d3)) {
++ Vec3 vec3d1 = Vec3.upFromBottomCenterOf(blockposition, d3);
++ UnmodifiableIterator unmodifiableiterator = livingEntity.getDismountPoses().iterator();
++
++ while (unmodifiableiterator.hasNext()) {
++ EntityPose entitypose = (EntityPose) unmodifiableiterator.next();
++ AABB axisalignedbb = livingEntity.getLocalBoundsForPose(entitypose);
++
++ if (DismountHelper.canDismountTo(this.level(), livingEntity, axisalignedbb.move(vec3d1))) {
++ livingEntity.setPose(entitypose);
++ return vec3d1;
+ }
+ }
+ }
+@@ -262,12 +285,12 @@
+
+ @Override
+ protected Vec3 getRiddenInput(Player player, Vec3 travelVector) {
+- return new Vec3(0.0, 0.0, 1.0);
++ return new Vec3(0.0D, 0.0D, 1.0D);
+ }
+
+ @Override
+ protected float getRiddenSpeed(Player player) {
+- return (float)(this.getAttributeValue(Attributes.MOVEMENT_SPEED) * (double)(this.isSuffocating() ? 0.35F : 0.55F) * (double)this.steering.boostFactor());
++ return (float) (this.getAttributeValue(Attributes.MOVEMENT_SPEED) * (double) (this.isSuffocating() ? 0.35F : 0.55F) * (double) this.steering.boostFactor());
+ }
+
+ @Override
+@@ -276,7 +299,7 @@
+ }
+
+ @Override
+- protected void playStepSound(BlockPos pos, BlockState block) {
++ protected void playStepSound(BlockPos pos, IBlockData block) {
+ this.playSound(this.isInLava() ? SoundEvents.STRIDER_STEP_LAVA : SoundEvents.STRIDER_STEP, 1.0F, 1.0F);
+ }
+
+@@ -286,12 +309,12 @@
+ }
+
+ @Override
+- protected void checkFallDamage(double y, boolean onGround, BlockState state, BlockPos pos) {
++ protected void checkFallDamage(double y, boolean flag, IBlockData onGround, BlockPos state) {
+ this.checkInsideBlocks();
+ if (this.isInLava()) {
+ this.resetFallDistance();
+ } else {
+- super.checkFallDamage(y, onGround, state, pos);
++ super.checkFallDamage(y, flag, onGround, state);
+ }
+ }
+
+@@ -305,23 +328,37 @@
+
+ if (!this.isNoAi()) {
+ boolean flag;
+- boolean var10000;
+- label36: {
+- BlockState blockState = this.level().getBlockState(this.blockPosition());
+- BlockState blockStateOnLegacy = this.getBlockStateOnLegacy();
+- flag = blockState.is(BlockTags.STRIDER_WARM_BLOCKS)
+- || blockStateOnLegacy.is(BlockTags.STRIDER_WARM_BLOCKS)
+- || this.getFluidHeight(FluidTags.LAVA) > 0.0;
+- if (this.getVehicle() instanceof Strider strider && strider.isSuffocating()) {
+- var10000 = true;
+- break label36;
++ boolean flag1;
++ label36:
++ {
++ IBlockData iblockdata = this.level().getBlockState(this.blockPosition());
++ IBlockData iblockdata1 = this.getBlockStateOnLegacy();
++
++ flag = iblockdata.is(BlockTags.STRIDER_WARM_BLOCKS) || iblockdata1.is(BlockTags.STRIDER_WARM_BLOCKS) || this.getFluidHeight(FluidTags.LAVA) > 0.0D;
++ Entity entity = this.getVehicle();
++
++ if (entity instanceof Strider) {
++ Strider entitystrider = (Strider) entity;
++
++ if (entitystrider.isSuffocating()) {
++ flag1 = true;
++ break label36;
++ }
+ }
+
+- var10000 = false;
++ flag1 = false;
+ }
+
+- boolean flag1 = var10000;
+- this.setSuffocating(!flag || flag1);
++ boolean flag2 = flag1;
++
++ // CraftBukkit start
++ boolean suffocating = !flag || flag2;
++ if (suffocating ^ this.isSuffocating()) {
++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callStriderTemperatureChangeEvent(this, suffocating)) {
++ this.setSuffocating(suffocating);
++ }
++ }
++ // CraftBukkit end
+ }
+
+ super.tick();
+@@ -340,18 +377,19 @@
+
+ private void floatStrider() {
+ if (this.isInLava()) {
+- CollisionContext collisionContext = CollisionContext.of(this);
+- if (collisionContext.isAbove(LiquidBlock.STABLE_SHAPE, this.blockPosition(), true)
+- && !this.level().getFluidState(this.blockPosition().above()).is(FluidTags.LAVA)) {
++ CollisionContext voxelshapecollision = CollisionContext.of(this);
++
++ if (voxelshapecollision.isAbove(LiquidBlock.STABLE_SHAPE, this.blockPosition(), true) && !this.level().getFluidState(this.blockPosition().above()).is(FluidTags.LAVA)) {
+ this.setOnGround(true);
+ } else {
+- this.setDeltaMovement(this.getDeltaMovement().scale(0.5).add(0.0, 0.05, 0.0));
++ this.setDeltaMovement(this.getDeltaMovement().scale(0.5D).add(0.0D, 0.05D, 0.0D));
+ }
+ }
++
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.175F).add(Attributes.FOLLOW_RANGE, 16.0);
++ return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 0.17499999701976776D).add(Attributes.FOLLOW_RANGE, 16.0D);
+ }
+
+ @Override
+@@ -391,109 +429,101 @@
+
+ @Override
+ public float getWalkTargetValue(BlockPos pos, LevelReader level) {
+- if (level.getBlockState(pos).getFluidState().is(FluidTags.LAVA)) {
+- return 10.0F;
+- } else {
+- return this.isInLava() ? Float.NEGATIVE_INFINITY : 0.0F;
+- }
++ return level.getBlockState(pos).getFluidState().is(FluidTags.LAVA) ? 10.0F : (this.isInLava() ? Float.NEGATIVE_INFINITY : 0.0F);
+ }
+
+ @Nullable
+ @Override
+ public Strider getBreedOffspring(ServerLevel level, AgeableMob otherParent) {
+- return EntityType.STRIDER.create(level);
++ return (Strider) EntityType.STRIDER.create(level);
+ }
+
+ @Override
+ public boolean isFood(ItemStack stack) {
+- return FOOD_ITEMS.test(stack);
++ return Strider.FOOD_ITEMS.test(stack);
+ }
+
+ @Override
+ protected void dropEquipment() {
+ super.dropEquipment();
+ if (this.isSaddled()) {
+- this.spawnAtLocation(Items.SADDLE);
++ this.spawnAtLocation((IMaterial) Items.SADDLE);
+ }
++
+ }
+
+ @Override
+- public InteractionResult mobInteract(Player player, InteractionHand hand) {
+- boolean isFood = this.isFood(player.getItemInHand(hand));
+- if (!isFood && this.isSaddled() && !this.isVehicle() && !player.isSecondaryUseActive()) {
++ public InteractionResult mobInteract(Player player, EnumHand hand) {
++ boolean flag = this.isFood(player.getItemInHand(hand));
++
++ if (!flag && this.isSaddled() && !this.isVehicle() && !player.isSecondaryUseActive()) {
+ if (!this.level().isClientSide) {
+ player.startRiding(this);
+ }
+
+ return InteractionResult.sidedSuccess(this.level().isClientSide);
+ } else {
+- InteractionResult interactionResult = super.mobInteract(player, hand);
+- if (!interactionResult.consumesAction()) {
+- ItemStack itemInHand = player.getItemInHand(hand);
+- return itemInHand.is(Items.SADDLE) ? itemInHand.interactLivingEntity(player, this, hand) : InteractionResult.PASS;
++ InteractionResult enuminteractionresult = super.mobInteract(player, hand);
++
++ if (!enuminteractionresult.consumesAction()) {
++ ItemStack itemstack = player.getItemInHand(hand);
++
++ return itemstack.is(Items.SADDLE) ? itemstack.interactLivingEntity(player, this, hand) : InteractionResult.PASS;
+ } else {
+- if (isFood && !this.isSilent()) {
+- this.level()
+- .playSound(
+- null,
+- this.getX(),
+- this.getY(),
+- this.getZ(),
+- SoundEvents.STRIDER_EAT,
+- this.getSoundSource(),
+- 1.0F,
+- 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F
+- );
++ if (flag && !this.isSilent()) {
++ this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.STRIDER_EAT, this.getSoundSource(), 1.0F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.2F);
+ }
+
+- return interactionResult;
++ return enuminteractionresult;
+ }
+ }
+ }
+
+ @Override
+ public Vec3 getLeashOffset() {
+- return new Vec3(0.0, (double)(0.6F * this.getEyeHeight()), (double)(this.getBbWidth() * 0.4F));
++ return new Vec3(0.0D, (double) (0.6F * this.getEyeHeight()), (double) (this.getBbWidth() * 0.4F));
+ }
+
+ @Nullable
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
+ if (this.isBaby()) {
+- return super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
++ return super.finalizeSpawn(level, difficulty, reason, (GroupDataEntity) spawnData, dataTag);
+ } else {
+- RandomSource random = level.getRandom();
+- if (random.nextInt(30) == 0) {
+- Mob mob = EntityType.ZOMBIFIED_PIGLIN.create(level.getLevel());
+- if (mob != null) {
+- spawnData = this.spawnJockey(level, difficulty, mob, new Zombie.ZombieGroupData(Zombie.getSpawnAsBabyOdds(random), false));
+- mob.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.WARPED_FUNGUS_ON_A_STICK));
+- this.equipSaddle(null);
++ RandomSource randomsource = level.getRandom();
++
++ if (randomsource.nextInt(30) == 0) {
++ Mob entityinsentient = (Mob) EntityType.ZOMBIFIED_PIGLIN.create(level.getLevel());
++
++ if (entityinsentient != null) {
++ spawnData = this.spawnJockey(level, difficulty, entityinsentient, new Zombie.ZombieGroupData(Zombie.getSpawnAsBabyOdds(randomsource), false));
++ entityinsentient.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.WARPED_FUNGUS_ON_A_STICK));
++ this.equipSaddle((SoundSource) null);
+ }
+- } else if (random.nextInt(10) == 0) {
+- AgeableMob ageableMob = EntityType.STRIDER.create(level.getLevel());
+- if (ageableMob != null) {
+- ageableMob.setAge(-24000);
+- spawnData = this.spawnJockey(level, difficulty, ageableMob, null);
++ } else if (randomsource.nextInt(10) == 0) {
++ AgeableMob entityageable = (AgeableMob) EntityType.STRIDER.create(level.getLevel());
++
++ if (entityageable != null) {
++ entityageable.setAge(-24000);
++ spawnData = this.spawnJockey(level, difficulty, entityageable, (GroupDataEntity) null);
+ }
+ } else {
+ spawnData = new AgeableMob.AgeableMobGroupData(0.5F);
+ }
+
+- return super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
++ return super.finalizeSpawn(level, difficulty, reason, (GroupDataEntity) spawnData, dataTag);
+ }
+ }
+
+- private SpawnGroupData spawnJockey(ServerLevelAccessor serverLevel, DifficultyInstance difficulty, Mob jockey, @Nullable SpawnGroupData spawnData) {
++ private GroupDataEntity spawnJockey(ServerLevelAccessor serverLevel, DifficultyInstance difficulty, Mob jockey, @Nullable GroupDataEntity spawnData) {
+ jockey.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F);
+- jockey.finalizeSpawn(serverLevel, difficulty, MobSpawnType.JOCKEY, spawnData, null);
++ jockey.finalizeSpawn(serverLevel, difficulty, EnumMobSpawn.JOCKEY, spawnData, (CompoundTag) null);
+ jockey.startRiding(this, true);
+ return new AgeableMob.AgeableMobGroupData(0.0F);
+ }
+
+- static class StriderGoToLavaGoal extends MoveToBlockGoal {
++ private static class StriderGoToLavaGoal extends MoveToBlockGoal {
++
+ private final Strider strider;
+
+ StriderGoToLavaGoal(Strider strider, double speedModifier) {
+@@ -523,11 +553,12 @@
+
+ @Override
+ protected boolean isValidTarget(LevelReader level, BlockPos pos) {
+- return level.getBlockState(pos).is(Blocks.LAVA) && level.getBlockState(pos.above()).isPathfindable(level, pos, PathComputationType.LAND);
++ return level.getBlockState(pos).is(Blocks.LAVA) && level.getBlockState(pos.above()).isPathfindable(level, pos, PathMode.LAND);
+ }
+ }
+
+- static class StriderPathNavigation extends GroundPathNavigation {
++ private static class StriderPathNavigation extends GroundPathNavigation {
++
+ StriderPathNavigation(Strider strider, Level level) {
+ super(strider, level);
+ }
+@@ -541,10 +572,7 @@
+
+ @Override
+ protected boolean hasValidPathType(BlockPathTypes pathType) {
+- return pathType == BlockPathTypes.LAVA
+- || pathType == BlockPathTypes.DAMAGE_FIRE
+- || pathType == BlockPathTypes.DANGER_FIRE
+- || super.hasValidPathType(pathType);
++ return pathType != BlockPathTypes.LAVA && pathType != BlockPathTypes.DAMAGE_FIRE && pathType != BlockPathTypes.DANGER_FIRE ? super.hasValidPathType(pathType) : true;
+ }
+
+ @Override
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Vex.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Vex.java.patch
new file mode 100644
index 0000000000..07677de98b
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Vex.java.patch
@@ -0,0 +1,402 @@
+--- a/net/minecraft/world/entity/monster/Vex.java
++++ b/net/minecraft/world/entity/monster/Vex.java
+@@ -15,15 +15,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.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
++import net.minecraft.world.entity.EnumMoveType;
+ import net.minecraft.world.entity.EquipmentSlot;
++import net.minecraft.world.entity.GroupDataEntity;
+ import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.Mob;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.MoverType;
+ import net.minecraft.world.entity.PathfinderMob;
+-import net.minecraft.world.entity.Pose;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.TraceableEntity;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+@@ -45,16 +45,17 @@
+ import org.joml.Vector3f;
+
+ public class Vex extends Monster implements TraceableEntity {
++
+ public static final float FLAP_DEGREES_PER_TICK = 45.836624F;
+- public static final int TICKS_PER_FLAP = Mth.ceil((float) (Math.PI * 5.0 / 4.0));
++ public static final int TICKS_PER_FLAP = Mth.ceil(3.9269907F);
+ protected static final EntityDataAccessor<Byte> DATA_FLAGS_ID = SynchedEntityData.defineId(Vex.class, EntityDataSerializers.BYTE);
+ private static final int FLAG_IS_CHARGING = 1;
+ @Nullable
+ Mob owner;
+ @Nullable
+ private BlockPos boundOrigin;
+- private boolean hasLimitedLife;
+- private int limitedLifeTicks;
++ public boolean hasLimitedLife;
++ public int limitedLifeTicks;
+
+ public Vex(EntityType<? extends Vex> entityType, Level level) {
+ super(entityType, level);
+@@ -63,17 +64,17 @@
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions dimensions) {
+ return dimensions.height - 0.28125F;
+ }
+
+ @Override
+ public boolean isFlapping() {
+- return this.tickCount % TICKS_PER_FLAP == 0;
++ return this.tickCount % Vex.TICKS_PER_FLAP == 0;
+ }
+
+ @Override
+- public void move(MoverType type, Vec3 pos) {
++ public void move(EnumMoveType type, Vec3 pos) {
+ super.move(type, pos);
+ this.checkInsideBlocks();
+ }
+@@ -88,6 +89,7 @@
+ this.limitedLifeTicks = 20;
+ this.hurt(this.damageSources().starve(), 1.0F);
+ }
++
+ }
+
+ @Override
+@@ -98,19 +100,19 @@
+ this.goalSelector.addGoal(8, new Vex.VexRandomMoveGoal());
+ this.goalSelector.addGoal(9, new LookAtPlayerGoal(this, Player.class, 3.0F, 1.0F));
+ this.goalSelector.addGoal(10, new LookAtPlayerGoal(this, Mob.class, 8.0F));
+- this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class).setAlertOthers());
++ this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[]{Raider.class})).setAlertOthers());
+ this.targetSelector.addGoal(2, new Vex.VexCopyOwnerTargetGoal(this));
+ this.targetSelector.addGoal(3, new NearestAttackableTargetGoal<>(this, Player.class, true));
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0).add(Attributes.ATTACK_DAMAGE, 4.0);
++ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 14.0D).add(Attributes.ATTACK_DAMAGE, 4.0D);
+ }
+
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_FLAGS_ID, (byte)0);
++ this.entityData.define(Vex.DATA_FLAGS_ID, (byte) 0);
+ }
+
+ @Override
+@@ -123,14 +125,18 @@
+ if (compound.contains("LifeTicks")) {
+ this.setLimitedLife(compound.getInt("LifeTicks"));
+ }
++
+ }
+
+ @Override
+ public void restoreFrom(Entity entity) {
+ super.restoreFrom(entity);
+- if (entity instanceof Vex vex) {
+- this.owner = vex.getOwner();
++ if (entity instanceof Vex) {
++ Vex entityvex = (Vex) entity;
++
++ this.owner = entityvex.getOwner();
+ }
++
+ }
+
+ @Override
+@@ -145,6 +151,7 @@
+ if (this.hasLimitedLife) {
+ compound.putInt("LifeTicks", this.limitedLifeTicks);
+ }
++
+ }
+
+ @Nullable
+@@ -163,19 +170,22 @@
+ }
+
+ private boolean getVexFlag(int mask) {
+- int i = this.entityData.get(DATA_FLAGS_ID);
+- return (i & mask) != 0;
++ byte b0 = (Byte) this.entityData.get(Vex.DATA_FLAGS_ID);
++
++ return (b0 & mask) != 0;
+ }
+
+ private void setVexFlag(int mask, boolean value) {
+- int i = this.entityData.get(DATA_FLAGS_ID);
++ byte b0 = (Byte) this.entityData.get(Vex.DATA_FLAGS_ID);
++ int j;
++
+ if (value) {
+- i |= mask;
++ j = b0 | mask;
+ } else {
+- i &= ~mask;
++ j = b0 & ~mask;
+ }
+
+- this.entityData.set(DATA_FLAGS_ID, (byte)(i & 0xFF));
++ this.entityData.set(Vex.DATA_FLAGS_ID, (byte) (j & 255));
+ }
+
+ public boolean isCharging() {
+@@ -217,12 +227,11 @@
+
+ @Nullable
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
+- RandomSource random = level.getRandom();
+- this.populateDefaultEquipmentSlots(random, difficulty);
+- this.populateDefaultEquipmentEnchantments(random, difficulty);
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
++ RandomSource randomsource = level.getRandom();
++
++ this.populateDefaultEquipmentSlots(randomsource, difficulty);
++ this.populateDefaultEquipmentEnchantments(randomsource, difficulty);
+ return super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
+ }
+
+@@ -238,23 +247,56 @@
+ }
+
+ @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);
+ }
+
+- class VexChargeAttackGoal extends Goal {
++ private class VexMoveControl extends MoveControl {
++
++ public VexMoveControl(Vex entityvex) {
++ super(entityvex);
++ }
++
++ @Override
++ public void tick() {
++ if (this.operation == MoveControl.Operation.MOVE_TO) {
++ Vec3 vec3d = new Vec3(this.wantedX - Vex.this.getX(), this.wantedY - Vex.this.getY(), this.wantedZ - Vex.this.getZ());
++ double d0 = vec3d.length();
++
++ if (d0 < Vex.this.getBoundingBox().getSize()) {
++ this.operation = MoveControl.Operation.WAIT;
++ Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().scale(0.5D));
++ } else {
++ Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(vec3d.scale(this.speedModifier * 0.05D / d0)));
++ if (Vex.this.getTarget() == null) {
++ Vec3 vec3d1 = Vex.this.getDeltaMovement();
++
++ Vex.this.setYRot(-((float) Mth.atan2(vec3d1.x, vec3d1.z)) * 57.295776F);
++ Vex.this.yBodyRot = Vex.this.getYRot();
++ } else {
++ double d1 = Vex.this.getTarget().getX() - Vex.this.getX();
++ double d2 = Vex.this.getTarget().getZ() - Vex.this.getZ();
++
++ Vex.this.setYRot(-((float) Mth.atan2(d1, d2)) * 57.295776F);
++ Vex.this.yBodyRot = Vex.this.getYRot();
++ }
++ }
++
++ }
++ }
++ }
++
++ private class VexChargeAttackGoal extends Goal {
++
+ public VexChargeAttackGoal() {
+- this.setFlags(EnumSet.of(Goal.Flag.MOVE));
++ this.setFlags(EnumSet.of(Goal.Type.MOVE));
+ }
+
+ @Override
+ public boolean canUse() {
+- LivingEntity target = Vex.this.getTarget();
+- return target != null
+- && target.isAlive()
+- && !Vex.this.getMoveControl().hasWanted()
+- && Vex.this.random.nextInt(reducedTickDelay(7)) == 0
+- && Vex.this.distanceToSqr(target) > 4.0;
++ LivingEntity entityliving = Vex.this.getTarget();
++
++ return entityliving != null && entityliving.isAlive() && !Vex.this.getMoveControl().hasWanted() && Vex.this.random.nextInt(reducedTickDelay(7)) == 0 ? Vex.this.distanceToSqr((Entity) entityliving) > 4.0D : false;
+ }
+
+ @Override
+@@ -264,10 +306,12 @@
+
+ @Override
+ public void start() {
+- LivingEntity target = Vex.this.getTarget();
+- if (target != null) {
+- Vec3 eyePosition = target.getEyePosition();
+- Vex.this.moveControl.setWantedPosition(eyePosition.x, eyePosition.y, eyePosition.z, 1.0);
++ LivingEntity entityliving = Vex.this.getTarget();
++
++ if (entityliving != null) {
++ Vec3 vec3d = entityliving.getEyePosition();
++
++ Vex.this.moveControl.setWantedPosition(vec3d.x, vec3d.y, vec3d.z, 1.0D);
+ }
+
+ Vex.this.setIsCharging(true);
+@@ -286,104 +330,82 @@
+
+ @Override
+ public void tick() {
+- LivingEntity target = Vex.this.getTarget();
+- if (target != null) {
+- if (Vex.this.getBoundingBox().intersects(target.getBoundingBox())) {
+- Vex.this.doHurtTarget(target);
++ LivingEntity entityliving = Vex.this.getTarget();
++
++ if (entityliving != null) {
++ if (Vex.this.getBoundingBox().intersects(entityliving.getBoundingBox())) {
++ Vex.this.doHurtTarget(entityliving);
+ Vex.this.setIsCharging(false);
+ } else {
+- double d = Vex.this.distanceToSqr(target);
+- if (d < 9.0) {
+- Vec3 eyePosition = target.getEyePosition();
+- Vex.this.moveControl.setWantedPosition(eyePosition.x, eyePosition.y, eyePosition.z, 1.0);
++ double d0 = Vex.this.distanceToSqr((Entity) entityliving);
++
++ if (d0 < 9.0D) {
++ Vec3 vec3d = entityliving.getEyePosition();
++
++ Vex.this.moveControl.setWantedPosition(vec3d.x, vec3d.y, vec3d.z, 1.0D);
+ }
+ }
++
+ }
+ }
+ }
+
+- class VexCopyOwnerTargetGoal extends TargetGoal {
+- private final TargetingConditions copyOwnerTargeting = TargetingConditions.forNonCombat().ignoreLineOfSight().ignoreInvisibilityTesting();
++ private class VexRandomMoveGoal extends Goal {
+
+- public VexCopyOwnerTargetGoal(PathfinderMob mob) {
+- super(mob, false);
++ public VexRandomMoveGoal() {
++ this.setFlags(EnumSet.of(Goal.Type.MOVE));
+ }
+
+ @Override
+ public boolean canUse() {
+- return Vex.this.owner != null && Vex.this.owner.getTarget() != null && this.canAttack(Vex.this.owner.getTarget(), this.copyOwnerTargeting);
++ return !Vex.this.getMoveControl().hasWanted() && Vex.this.random.nextInt(reducedTickDelay(7)) == 0;
+ }
+
+ @Override
+- public void start() {
+- Vex.this.setTarget(Vex.this.owner.getTarget());
+- super.start();
++ public boolean canContinueToUse() {
++ return false;
+ }
+- }
+
+- class VexMoveControl extends MoveControl {
+- public VexMoveControl(Vex mob) {
+- super(mob);
+- }
+-
+ @Override
+ public void tick() {
+- if (this.operation == MoveControl.Operation.MOVE_TO) {
+- Vec3 vec3 = new Vec3(this.wantedX - Vex.this.getX(), this.wantedY - Vex.this.getY(), this.wantedZ - Vex.this.getZ());
+- double len = vec3.length();
+- if (len < Vex.this.getBoundingBox().getSize()) {
+- this.operation = MoveControl.Operation.WAIT;
+- Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().scale(0.5));
+- } else {
+- Vex.this.setDeltaMovement(Vex.this.getDeltaMovement().add(vec3.scale(this.speedModifier * 0.05 / len)));
++ BlockPos blockposition = Vex.this.getBoundOrigin();
++
++ if (blockposition == null) {
++ blockposition = Vex.this.blockPosition();
++ }
++
++ for (int i = 0; i < 3; ++i) {
++ BlockPos blockposition1 = blockposition.offset(Vex.this.random.nextInt(15) - 7, Vex.this.random.nextInt(11) - 5, Vex.this.random.nextInt(15) - 7);
++
++ if (Vex.this.level().isEmptyBlock(blockposition1)) {
++ Vex.this.moveControl.setWantedPosition((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.5D, (double) blockposition1.getZ() + 0.5D, 0.25D);
+ if (Vex.this.getTarget() == null) {
+- Vec3 deltaMovement = Vex.this.getDeltaMovement();
+- Vex.this.setYRot(-((float)Mth.atan2(deltaMovement.x, deltaMovement.z)) * (180.0F / (float)Math.PI));
+- Vex.this.yBodyRot = Vex.this.getYRot();
+- } else {
+- double d = Vex.this.getTarget().getX() - Vex.this.getX();
+- double d1 = Vex.this.getTarget().getZ() - Vex.this.getZ();
+- Vex.this.setYRot(-((float)Mth.atan2(d, d1)) * (180.0F / (float)Math.PI));
+- Vex.this.yBodyRot = Vex.this.getYRot();
++ Vex.this.getLookControl().setLookAt((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.5D, (double) blockposition1.getZ() + 0.5D, 180.0F, 20.0F);
+ }
++ break;
+ }
+ }
++
+ }
+ }
+
+- class VexRandomMoveGoal extends Goal {
+- public VexRandomMoveGoal() {
+- this.setFlags(EnumSet.of(Goal.Flag.MOVE));
++ private class VexCopyOwnerTargetGoal extends TargetGoal {
++
++ private final TargetingConditions copyOwnerTargeting = TargetingConditions.forNonCombat().ignoreLineOfSight().ignoreInvisibilityTesting();
++
++ public VexCopyOwnerTargetGoal(PathfinderMob entitycreature) {
++ super(entitycreature, false);
+ }
+
+ @Override
+ public boolean canUse() {
+- return !Vex.this.getMoveControl().hasWanted() && Vex.this.random.nextInt(reducedTickDelay(7)) == 0;
++ return Vex.this.owner != null && Vex.this.owner.getTarget() != null && this.canAttack(Vex.this.owner.getTarget(), this.copyOwnerTargeting);
+ }
+
+ @Override
+- public boolean canContinueToUse() {
+- return false;
++ public void start() {
++ Vex.this.setTarget(Vex.this.owner.getTarget(), org.bukkit.event.entity.EntityTargetEvent.TargetReason.OWNER_ATTACKED_TARGET, true); // CraftBukkit
++ super.start();
+ }
+-
+- @Override
+- public void tick() {
+- BlockPos boundOrigin = Vex.this.getBoundOrigin();
+- if (boundOrigin == null) {
+- boundOrigin = Vex.this.blockPosition();
+- }
+-
+- for (int i = 0; i < 3; i++) {
+- BlockPos blockPos = boundOrigin.offset(Vex.this.random.nextInt(15) - 7, Vex.this.random.nextInt(11) - 5, Vex.this.random.nextInt(15) - 7);
+- if (Vex.this.level().isEmptyBlock(blockPos)) {
+- Vex.this.moveControl.setWantedPosition((double)blockPos.getX() + 0.5, (double)blockPos.getY() + 0.5, (double)blockPos.getZ() + 0.5, 0.25);
+- if (Vex.this.getTarget() == null) {
+- Vex.this.getLookControl()
+- .setLookAt((double)blockPos.getX() + 0.5, (double)blockPos.getY() + 0.5, (double)blockPos.getZ() + 0.5, 180.0F, 20.0F);
+- }
+- break;
+- }
+- }
+- }
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Witch.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Witch.java.patch
new file mode 100644
index 0000000000..64cc833019
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/Witch.java.patch
@@ -0,0 +1,313 @@
+--- a/net/minecraft/world/entity/monster/Witch.java
++++ b/net/minecraft/world/entity/monster/Witch.java
+@@ -1,7 +1,9 @@
+ package net.minecraft.world.entity.monster;
+
++import java.util.Iterator;
+ import java.util.List;
+ import java.util.UUID;
++import java.util.function.Predicate;
+ import net.minecraft.core.particles.ParticleTypes;
+ import net.minecraft.network.syncher.EntityDataAccessor;
+ import net.minecraft.network.syncher.EntityDataSerializers;
+@@ -15,10 +17,10 @@
+ import net.minecraft.world.effect.MobEffects;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
++import net.minecraft.world.entity.EntityPose;
+ import net.minecraft.world.entity.EntityType;
+ import net.minecraft.world.entity.EquipmentSlot;
+ import net.minecraft.world.entity.LivingEntity;
+-import net.minecraft.world.entity.Pose;
+ import net.minecraft.world.entity.ai.attributes.AttributeInstance;
+ import net.minecraft.world.entity.ai.attributes.AttributeModifier;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+@@ -45,10 +47,9 @@
+ import org.joml.Vector3f;
+
+ public class Witch extends Raider implements RangedAttackMob {
++
+ private static final UUID SPEED_MODIFIER_DRINKING_UUID = UUID.fromString("5CD17E52-A79A-43D3-A529-90FDE04B181E");
+- private static final AttributeModifier SPEED_MODIFIER_DRINKING = new AttributeModifier(
+- SPEED_MODIFIER_DRINKING_UUID, "Drinking speed penalty", -0.25, AttributeModifier.Operation.ADDITION
+- );
++ private static final AttributeModifier SPEED_MODIFIER_DRINKING = new AttributeModifier(Witch.SPEED_MODIFIER_DRINKING_UUID, "Drinking speed penalty", -0.25D, AttributeModifier.Operation.ADDITION);
+ private static final EntityDataAccessor<Boolean> DATA_USING_ITEM = SynchedEntityData.defineId(Witch.class, EntityDataSerializers.BOOLEAN);
+ private int usingTime;
+ private NearestHealableRaiderTargetGoal<Raider> healRaidersGoal;
+@@ -61,16 +62,16 @@
+ @Override
+ protected void registerGoals() {
+ super.registerGoals();
+- this.healRaidersGoal = new NearestHealableRaiderTargetGoal<>(
+- this, Raider.class, true, livingEntity -> livingEntity != null && this.hasActiveRaid() && livingEntity.getType() != EntityType.WITCH
+- );
+- this.attackPlayersGoal = new NearestAttackableWitchTargetGoal<>(this, Player.class, 10, true, false, null);
++ this.healRaidersGoal = new NearestHealableRaiderTargetGoal<>(this, Raider.class, true, (entityliving) -> {
++ return entityliving != null && this.hasActiveRaid() && entityliving.getType() != EntityType.WITCH;
++ });
++ this.attackPlayersGoal = new NearestAttackableWitchTargetGoal<>(this, Player.class, 10, true, false, (Predicate) null);
+ this.goalSelector.addGoal(1, new FloatGoal(this));
+- this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0, 60, 10.0F));
+- this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0));
++ this.goalSelector.addGoal(2, new RangedAttackGoal(this, 1.0D, 60, 10.0F));
++ this.goalSelector.addGoal(2, new WaterAvoidingRandomStrollGoal(this, 1.0D));
+ this.goalSelector.addGoal(3, new LookAtPlayerGoal(this, Player.class, 8.0F));
+ this.goalSelector.addGoal(3, new RandomLookAroundGoal(this));
+- this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class));
++ this.targetSelector.addGoal(1, new HurtByTargetGoal(this, new Class[]{Raider.class}));
+ this.targetSelector.addGoal(2, this.healRaidersGoal);
+ this.targetSelector.addGoal(3, this.attackPlayersGoal);
+ }
+@@ -78,7 +79,7 @@
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.getEntityData().define(DATA_USING_ITEM, false);
++ this.getEntityData().define(Witch.DATA_USING_ITEM, false);
+ }
+
+ @Override
+@@ -97,15 +98,15 @@
+ }
+
+ public void setUsingItem(boolean usingItem) {
+- this.getEntityData().set(DATA_USING_ITEM, usingItem);
++ this.getEntityData().set(Witch.DATA_USING_ITEM, usingItem);
+ }
+
+ public boolean isDrinkingPotion() {
+- return this.getEntityData().get(DATA_USING_ITEM);
++ return (Boolean) this.getEntityData().get(Witch.DATA_USING_ITEM);
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 26.0).add(Attributes.MOVEMENT_SPEED, 0.25);
++ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 26.0D).add(Attributes.MOVEMENT_SPEED, 0.25D);
+ }
+
+ @Override
+@@ -121,63 +122,56 @@
+ if (this.isDrinkingPotion()) {
+ if (this.usingTime-- <= 0) {
+ this.setUsingItem(false);
+- ItemStack mainHandItem = this.getMainHandItem();
++ ItemStack itemstack = this.getMainHandItem();
++
+ this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY);
+- if (mainHandItem.is(Items.POTION)) {
+- List<MobEffectInstance> mobEffects = PotionUtils.getMobEffects(mainHandItem);
+- if (mobEffects != null) {
+- for (MobEffectInstance mobEffectInstance : mobEffects) {
+- this.addEffect(new MobEffectInstance(mobEffectInstance));
++ if (itemstack.is(Items.POTION)) {
++ List<MobEffectInstance> list = PotionUtils.getMobEffects(itemstack);
++
++ if (list != null) {
++ Iterator iterator = list.iterator();
++
++ while (iterator.hasNext()) {
++ MobEffectInstance mobeffect = (MobEffectInstance) iterator.next();
++
++ this.addEffect(new MobEffectInstance(mobeffect), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit
+ }
+ }
+ }
+
+ this.gameEvent(GameEvent.DRINK);
+- this.getAttribute(Attributes.MOVEMENT_SPEED).removeModifier(SPEED_MODIFIER_DRINKING.getId());
++ this.getAttribute(Attributes.MOVEMENT_SPEED).removeModifier(Witch.SPEED_MODIFIER_DRINKING.getId());
+ }
+ } else {
+- Potion potion = null;
++ Potion potionregistry = null;
++
+ if (this.random.nextFloat() < 0.15F && this.isEyeInFluid(FluidTags.WATER) && !this.hasEffect(MobEffects.WATER_BREATHING)) {
+- potion = Potions.WATER_BREATHING;
+- } else if (this.random.nextFloat() < 0.15F
+- && (this.isOnFire() || this.getLastDamageSource() != null && this.getLastDamageSource().is(DamageTypeTags.IS_FIRE))
+- && !this.hasEffect(MobEffects.FIRE_RESISTANCE)) {
+- potion = Potions.FIRE_RESISTANCE;
++ potionregistry = Potions.WATER_BREATHING;
++ } else if (this.random.nextFloat() < 0.15F && (this.isOnFire() || this.getLastDamageSource() != null && this.getLastDamageSource().is(DamageTypeTags.IS_FIRE)) && !this.hasEffect(MobEffects.FIRE_RESISTANCE)) {
++ potionregistry = Potions.FIRE_RESISTANCE;
+ } else if (this.random.nextFloat() < 0.05F && this.getHealth() < this.getMaxHealth()) {
+- potion = Potions.HEALING;
+- } else if (this.random.nextFloat() < 0.5F
+- && this.getTarget() != null
+- && !this.hasEffect(MobEffects.MOVEMENT_SPEED)
+- && this.getTarget().distanceToSqr(this) > 121.0) {
+- potion = Potions.SWIFTNESS;
++ potionregistry = Potions.HEALING;
++ } else if (this.random.nextFloat() < 0.5F && this.getTarget() != null && !this.hasEffect(MobEffects.MOVEMENT_SPEED) && this.getTarget().distanceToSqr((Entity) this) > 121.0D) {
++ potionregistry = Potions.SWIFTNESS;
+ }
+
+- if (potion != null) {
+- this.setItemSlot(EquipmentSlot.MAINHAND, PotionUtils.setPotion(new ItemStack(Items.POTION), potion));
++ if (potionregistry != null) {
++ this.setItemSlot(EquipmentSlot.MAINHAND, PotionUtils.setPotion(new ItemStack(Items.POTION), potionregistry));
+ this.usingTime = this.getMainHandItem().getUseDuration();
+ this.setUsingItem(true);
+ if (!this.isSilent()) {
+- this.level()
+- .playSound(
+- null,
+- this.getX(),
+- this.getY(),
+- this.getZ(),
+- SoundEvents.WITCH_DRINK,
+- this.getSoundSource(),
+- 1.0F,
+- 0.8F + this.random.nextFloat() * 0.4F
+- );
++ this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.WITCH_DRINK, this.getSoundSource(), 1.0F, 0.8F + this.random.nextFloat() * 0.4F);
+ }
+
+- AttributeInstance attribute = this.getAttribute(Attributes.MOVEMENT_SPEED);
+- attribute.removeModifier(SPEED_MODIFIER_DRINKING.getId());
+- attribute.addTransientModifier(SPEED_MODIFIER_DRINKING);
++ AttributeInstance attributemodifiable = this.getAttribute(Attributes.MOVEMENT_SPEED);
++
++ attributemodifiable.removeModifier(Witch.SPEED_MODIFIER_DRINKING.getId());
++ attributemodifiable.addTransientModifier(Witch.SPEED_MODIFIER_DRINKING);
+ }
+ }
+
+ if (this.random.nextFloat() < 7.5E-4F) {
+- this.level().broadcastEntityEvent(this, (byte)15);
++ this.level().broadcastEntityEvent(this, (byte) 15);
+ }
+ }
+
+@@ -192,97 +186,80 @@
+ @Override
+ public void handleEntityEvent(byte id) {
+ if (id == 15) {
+- for (int i = 0; i < this.random.nextInt(35) + 10; i++) {
+- this.level()
+- .addParticle(
+- ParticleTypes.WITCH,
+- this.getX() + this.random.nextGaussian() * 0.13F,
+- this.getBoundingBox().maxY + 0.5 + this.random.nextGaussian() * 0.13F,
+- this.getZ() + this.random.nextGaussian() * 0.13F,
+- 0.0,
+- 0.0,
+- 0.0
+- );
++ for (int i = 0; i < this.random.nextInt(35) + 10; ++i) {
++ this.level().addParticle(ParticleTypes.WITCH, this.getX() + this.random.nextGaussian() * 0.12999999523162842D, this.getBoundingBox().maxY + 0.5D + this.random.nextGaussian() * 0.12999999523162842D, this.getZ() + this.random.nextGaussian() * 0.12999999523162842D, 0.0D, 0.0D, 0.0D);
+ }
+ } else {
+ super.handleEntityEvent(id);
+ }
++
+ }
+
+ @Override
+ protected float getDamageAfterMagicAbsorb(DamageSource source, float damage) {
+- float var3 = super.getDamageAfterMagicAbsorb(source, damage);
++ damage = super.getDamageAfterMagicAbsorb(source, damage);
+ if (source.getEntity() == this) {
+- var3 = 0.0F;
++ damage = 0.0F;
+ }
+
+ if (source.is(DamageTypeTags.WITCH_RESISTANT_TO)) {
+- var3 *= 0.15F;
++ damage *= 0.15F;
+ }
+
+- return var3;
++ return damage;
+ }
+
+ @Override
+ public void performRangedAttack(LivingEntity target, float distanceFactor) {
+ if (!this.isDrinkingPotion()) {
+- Vec3 deltaMovement = target.getDeltaMovement();
+- double d = target.getX() + deltaMovement.x - this.getX();
+- double d1 = target.getEyeY() - 1.1F - this.getY();
+- double d2 = target.getZ() + deltaMovement.z - this.getZ();
+- double squareRoot = Math.sqrt(d * d + d2 * d2);
+- Potion potion = Potions.HARMING;
++ Vec3 vec3d = target.getDeltaMovement();
++ double d0 = target.getX() + vec3d.x - this.getX();
++ double d1 = target.getEyeY() - 1.100000023841858D - this.getY();
++ double d2 = target.getZ() + vec3d.z - this.getZ();
++ double d3 = Math.sqrt(d0 * d0 + d2 * d2);
++ Potion potionregistry = Potions.HARMING;
++
+ if (target instanceof Raider) {
+ if (target.getHealth() <= 4.0F) {
+- potion = Potions.HEALING;
++ potionregistry = Potions.HEALING;
+ } else {
+- potion = Potions.REGENERATION;
++ potionregistry = Potions.REGENERATION;
+ }
+
+- this.setTarget(null);
+- } else if (squareRoot >= 8.0 && !target.hasEffect(MobEffects.MOVEMENT_SLOWDOWN)) {
+- potion = Potions.SLOWNESS;
++ this.setTarget((LivingEntity) null);
++ } else if (d3 >= 8.0D && !target.hasEffect(MobEffects.MOVEMENT_SLOWDOWN)) {
++ potionregistry = Potions.SLOWNESS;
+ } else if (target.getHealth() >= 8.0F && !target.hasEffect(MobEffects.POISON)) {
+- potion = Potions.POISON;
+- } else if (squareRoot <= 3.0 && !target.hasEffect(MobEffects.WEAKNESS) && this.random.nextFloat() < 0.25F) {
+- potion = Potions.WEAKNESS;
++ potionregistry = Potions.POISON;
++ } else if (d3 <= 3.0D && !target.hasEffect(MobEffects.WEAKNESS) && this.random.nextFloat() < 0.25F) {
++ potionregistry = Potions.WEAKNESS;
+ }
+
+- ThrownPotion thrownPotion = new ThrownPotion(this.level(), this);
+- thrownPotion.setItem(PotionUtils.setPotion(new ItemStack(Items.SPLASH_POTION), potion));
+- thrownPotion.setXRot(thrownPotion.getXRot() - -20.0F);
+- thrownPotion.shoot(d, d1 + squareRoot * 0.2, d2, 0.75F, 8.0F);
++ ThrownPotion entitypotion = new ThrownPotion(this.level(), this);
++
++ entitypotion.setItem(PotionUtils.setPotion(new ItemStack(Items.SPLASH_POTION), potionregistry));
++ entitypotion.setXRot(entitypotion.getXRot() - -20.0F);
++ entitypotion.shoot(d0, d1 + d3 * 0.2D, d2, 0.75F, 8.0F);
+ if (!this.isSilent()) {
+- this.level()
+- .playSound(
+- null,
+- this.getX(),
+- this.getY(),
+- this.getZ(),
+- SoundEvents.WITCH_THROW,
+- this.getSoundSource(),
+- 1.0F,
+- 0.8F + this.random.nextFloat() * 0.4F
+- );
++ this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.WITCH_THROW, this.getSoundSource(), 1.0F, 0.8F + this.random.nextFloat() * 0.4F);
+ }
+
+- this.level().addFreshEntity(thrownPotion);
++ this.level().addFreshEntity(entitypotion);
+ }
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return 1.62F;
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height + 0.3125F * f, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height + 0.3125F * f, 0.0F);
+ }
+
+ @Override
+- public void applyRaidBuffs(int wave, boolean unusedFalse) {
+- }
++ public void applyRaidBuffs(int wave, boolean unusedFalse) {}
+
+ @Override
+ public boolean canBeLeader() {
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/WitherSkeleton.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/WitherSkeleton.java.patch
new file mode 100644
index 0000000000..7ece9183c9
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/WitherSkeleton.java.patch
@@ -0,0 +1,110 @@
+--- a/net/minecraft/world/entity/monster/WitherSkeleton.java
++++ b/net/minecraft/world/entity/monster/WitherSkeleton.java
+@@ -11,23 +11,25 @@
+ import net.minecraft.world.effect.MobEffects;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
++import net.minecraft.world.entity.EntityPose;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
+ 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.Pose;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+ import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
+ import net.minecraft.world.entity.monster.piglin.AbstractPiglin;
+ import net.minecraft.world.entity.projectile.AbstractArrow;
+ import net.minecraft.world.item.ItemStack;
+ import net.minecraft.world.item.Items;
++import net.minecraft.world.level.IMaterial;
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.level.ServerLevelAccessor;
+ import net.minecraft.world.level.pathfinder.BlockPathTypes;
+
+ public class WitherSkeleton extends AbstractSkeleton {
++
+ public WitherSkeleton(EntityType<? extends WitherSkeleton> entityType, Level level) {
+ super(entityType, level);
+ this.setPathfindingMalus(BlockPathTypes.LAVA, 8.0F);
+@@ -62,10 +64,17 @@
+ @Override
+ protected void dropCustomDeathLoot(DamageSource source, int looting, boolean recentlyHit) {
+ super.dropCustomDeathLoot(source, looting, recentlyHit);
+- if (source.getEntity() instanceof Creeper creeper && creeper.canDropMobsSkull()) {
+- creeper.increaseDroppedSkulls();
+- this.spawnAtLocation(Items.WITHER_SKELETON_SKULL);
++ Entity entity = source.getEntity();
++
++ if (entity instanceof Creeper) {
++ Creeper entitycreeper = (Creeper) entity;
++
++ if (entitycreeper.canDropMobsSkull()) {
++ entitycreeper.increaseDroppedSkulls();
++ this.spawnAtLocation((IMaterial) Items.WITHER_SKELETON_SKULL);
++ }
+ }
++
+ }
+
+ @Override
+@@ -74,22 +83,20 @@
+ }
+
+ @Override
+- protected void populateDefaultEquipmentEnchantments(RandomSource random, DifficultyInstance difficulty) {
+- }
++ protected void populateDefaultEquipmentEnchantments(RandomSource random, DifficultyInstance difficulty) {}
+
+ @Nullable
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
+- SpawnGroupData spawnGroupData = super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
+- this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(4.0);
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
++ GroupDataEntity groupdataentity1 = super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
++
++ this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(4.0D);
+ this.reassessWeaponGoal();
+- return spawnGroupData;
++ return groupdataentity1;
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return 2.1F;
+ }
+
+@@ -104,7 +111,7 @@
+ return false;
+ } else {
+ if (entity instanceof LivingEntity) {
+- ((LivingEntity)entity).addEffect(new MobEffectInstance(MobEffects.WITHER, 200), this);
++ ((LivingEntity) entity).addEffect(new MobEffectInstance(MobEffects.WITHER, 200), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit
+ }
+
+ return true;
+@@ -113,13 +120,14 @@
+
+ @Override
+ protected AbstractArrow getArrow(ItemStack arrowStack, float distanceFactor) {
+- AbstractArrow abstractArrow = super.getArrow(arrowStack, distanceFactor);
+- abstractArrow.setSecondsOnFire(100);
+- return abstractArrow;
++ AbstractArrow entityarrow = super.getArrow(arrowStack, distanceFactor);
++
++ entityarrow.setSecondsOnFire(100);
++ return entityarrow;
+ }
+
+ @Override
+ public boolean canBeAffected(MobEffectInstance potioneffect) {
+- return potioneffect.getEffect() != MobEffects.WITHER && super.canBeAffected(potioneffect);
++ return potioneffect.getEffect() == MobEffects.WITHER ? false : super.canBeAffected(potioneffect);
+ }
+ }
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;
+
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/ZombieVillager.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/ZombieVillager.java.patch
new file mode 100644
index 0000000000..e2bfe9f30e
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/ZombieVillager.java.patch
@@ -0,0 +1,389 @@
+--- a/net/minecraft/world/entity/monster/ZombieVillager.java
++++ b/net/minecraft/world/entity/monster/ZombieVillager.java
+@@ -3,11 +3,11 @@
+ import com.mojang.logging.LogUtils;
+ import com.mojang.serialization.DataResult;
+ import com.mojang.serialization.Dynamic;
++import java.util.Objects;
+ import java.util.UUID;
+ import javax.annotation.Nullable;
+ import net.minecraft.advancements.CriteriaTriggers;
+ import net.minecraft.core.BlockPos;
+-import net.minecraft.core.Holder;
+ import net.minecraft.core.registries.BuiltInRegistries;
+ import net.minecraft.nbt.CompoundTag;
+ import net.minecraft.nbt.NbtOps;
+@@ -15,12 +15,8 @@
+ 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.EnumHand;
+ import net.minecraft.world.InteractionResult;
+ import net.minecraft.world.damagesource.DamageSource;
+ import net.minecraft.world.effect.MobEffectInstance;
+@@ -28,9 +24,9 @@
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
+ import net.minecraft.world.entity.EquipmentSlot;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.SpawnGroupData;
++import net.minecraft.world.entity.GroupDataEntity;
+ import net.minecraft.world.entity.ai.village.ReputationEventType;
+ import net.minecraft.world.entity.npc.Villager;
+ import net.minecraft.world.entity.npc.VillagerData;
+@@ -46,50 +42,63 @@
+ import net.minecraft.world.level.ServerLevelAccessor;
+ import net.minecraft.world.level.block.BedBlock;
+ 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 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();
+- private 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
+- );
++ 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 int VILLAGER_CONVERSION_WAIT_MIN = 3600;
+ private static final int VILLAGER_CONVERSION_WAIT_MAX = 6000;
+ private static final int MAX_SPECIAL_BLOCKS_COUNT = 14;
+ private static final int SPECIAL_BLOCK_RADIUS = 4;
+- private int villagerConversionTime;
++ public int villagerConversionTime;
+ @Nullable
+- private UUID conversionStarter;
++ public UUID conversionStarter;
+ @Nullable
+ private Tag gossips;
+ @Nullable
+ private CompoundTag tradeOffers;
+ private int villagerXp;
++ private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field
+
+ public ZombieVillager(EntityType<? extends ZombieVillager> entityType, Level level) {
+ super(entityType, level);
+- 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() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_CONVERTING_ID, false);
+- this.entityData.define(DATA_VILLAGER_DATA, new VillagerData(VillagerType.PLAINS, VillagerProfession.NONE, 1));
++ this.entityData.define(ZombieVillager.DATA_CONVERTING_ID, false);
++ this.entityData.define(ZombieVillager.DATA_VILLAGER_DATA, new VillagerData(VillagerType.PLAINS, VillagerProfession.NONE, 1));
+ }
+
+ @Override
+ public void addAdditionalSaveData(CompoundTag compound) {
+ super.addAdditionalSaveData(compound);
+- VillagerData.CODEC
+- .encodeStart(NbtOps.INSTANCE, this.getVillagerData())
+- .resultOrPartial(LOGGER::error)
+- .ifPresent(tag -> compound.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) -> {
++ compound.put("VillagerData", nbtbase);
++ });
+ if (this.tradeOffers != null) {
+ compound.put("Offers", this.tradeOffers);
+ }
+@@ -110,8 +119,11 @@
+ public void readAdditionalSaveData(CompoundTag compound) {
+ super.readAdditionalSaveData(compound);
+ if (compound.contains("VillagerData", 10)) {
+- DataResult<VillagerData> dataResult = VillagerData.CODEC.parse(new Dynamic<>(NbtOps.INSTANCE, compound.get("VillagerData")));
+- dataResult.resultOrPartial(LOGGER::error).ifPresent(this::setVillagerData);
++ DataResult<VillagerData> dataresult = VillagerData.CODEC.parse(new Dynamic(NbtOps.INSTANCE, compound.get("VillagerData")));
++ Logger logger = ZombieVillager.LOGGER;
++
++ Objects.requireNonNull(logger);
++ dataresult.resultOrPartial(logger::error).ifPresent(this::setVillagerData);
+ }
+
+ if (compound.contains("Offers", 10)) {
+@@ -129,28 +141,36 @@
+ if (compound.contains("Xp", 3)) {
+ this.villagerXp = compound.getInt("Xp");
+ }
++
+ }
+
+ @Override
+ public void tick() {
+ if (!this.level().isClientSide && this.isAlive() && this.isConverting()) {
+- int conversionProgress = this.getConversionProgress();
+- this.villagerConversionTime -= conversionProgress;
++ 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 itemInHand = player.getItemInHand(hand);
+- if (itemInHand.is(Items.GOLDEN_APPLE)) {
++ public InteractionResult mobInteract(Player player, EnumHand hand) {
++ ItemStack itemstack = player.getItemInHand(hand);
++
++ if (itemstack.is(Items.GOLDEN_APPLE)) {
+ if (this.hasEffect(MobEffects.WEAKNESS)) {
+ if (!player.getAbilities().instabuild) {
+- itemInHand.shrink(1);
++ itemstack.shrink(1);
+ }
+
+ if (!this.level().isClientSide) {
+@@ -177,98 +197,108 @@
+ }
+
+ public boolean isConverting() {
+- return this.getEntityData().get(DATA_CONVERTING_ID);
++ return (Boolean) this.getEntityData().get(ZombieVillager.DATA_CONVERTING_ID);
+ }
+
+- private void startConverting(@Nullable UUID conversionStarter, int villagerConversionTime) {
++ public void startConverting(@Nullable UUID conversionStarter, int villagerConversionTime) {
+ this.conversionStarter = conversionStarter;
+ this.villagerConversionTime = villagerConversionTime;
+- this.getEntityData().set(DATA_CONVERTING_ID, true);
+- this.removeEffect(MobEffects.WEAKNESS);
+- this.addEffect(new MobEffectInstance(MobEffects.DAMAGE_BOOST, villagerConversionTime, 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, villagerConversionTime, Math.min(this.level().getDifficulty().getId() - 1, 0)), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.CONVERSION);
++ // CraftBukkit end
++ this.level().broadcastEntityEvent(this, (byte) 16);
+ }
+
+ @Override
+ public void handleEntityEvent(byte id) {
+ if (id == 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(id);
+ }
+ }
+
+ private void finishConversion(ServerLevel serverLevel) {
+- Villager villager = this.convertTo(EntityType.VILLAGER, false);
++ // CraftBukkit start
++ Villager entityvillager = (Villager) this.convertTo(EntityType.VILLAGER, false, EntityTransformEvent.TransformReason.CURED, CreatureSpawnEvent.SpawnReason.CURED);
++ if (entityvillager == null) {
++ ((org.bukkit.entity.ZombieVillager) getBukkitEntity()).setConversionTime(-1); // SPIGOT-5208: End conversion to stop event spam
++ return;
++ }
++ // CraftBukkit end
++ EquipmentSlot[] aenumitemslot = EquipmentSlot.values();
++ int i = aenumitemslot.length;
+
+- for (EquipmentSlot equipmentSlot : EquipmentSlot.values()) {
+- ItemStack itemBySlot = this.getItemBySlot(equipmentSlot);
+- if (!itemBySlot.isEmpty()) {
+- if (EnchantmentHelper.hasBindingCurse(itemBySlot)) {
+- villager.getSlot(equipmentSlot.getIndex() + 300).set(itemBySlot);
++ for (int j = 0; j < i; ++j) {
++ EquipmentSlot enumitemslot = aenumitemslot[j];
++ ItemStack itemstack = this.getItemBySlot(enumitemslot);
++
++ if (!itemstack.isEmpty()) {
++ if (EnchantmentHelper.hasBindingCurse(itemstack)) {
++ entityvillager.getSlot(enumitemslot.getIndex() + 300).set(itemstack);
+ } else {
+- double d = (double)this.getEquipmentDropChance(equipmentSlot);
+- if (d > 1.0) {
+- this.spawnAtLocation(itemBySlot);
++ double d0 = (double) this.getEquipmentDropChance(enumitemslot);
++
++ if (d0 > 1.0D) {
++ this.forceDrops = true; // CraftBukkit
++ this.spawnAtLocation(itemstack);
++ this.forceDrops = false; // CraftBukkit
+ }
+ }
+ }
+ }
+
+- 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(new MerchantOffers(this.tradeOffers));
++ entityvillager.setOffers(new MerchantOffers(this.tradeOffers));
+ }
+
+- villager.setVillagerXp(this.villagerXp);
+- villager.finalizeSpawn(serverLevel, serverLevel.getCurrentDifficultyAt(villager.blockPosition()), MobSpawnType.CONVERSION, null, null);
+- villager.refreshBrain(serverLevel);
++ entityvillager.setVillagerXp(this.villagerXp);
++ entityvillager.finalizeSpawn(serverLevel, serverLevel.getCurrentDifficultyAt(entityvillager.blockPosition()), EnumMobSpawn.CONVERSION, (GroupDataEntity) null, (CompoundTag) null);
++ entityvillager.refreshBrain(serverLevel);
+ if (this.conversionStarter != null) {
+- Player playerByUUID = serverLevel.getPlayerByUUID(this.conversionStarter);
+- if (playerByUUID instanceof ServerPlayer) {
+- CriteriaTriggers.CURED_ZOMBIE_VILLAGER.trigger((ServerPlayer)playerByUUID, this, villager);
+- serverLevel.onReputationEvent(ReputationEventType.ZOMBIE_VILLAGER_CURED, playerByUUID, villager);
++ Player entityhuman = serverLevel.getPlayerByUUID(this.conversionStarter);
++
++ if (entityhuman instanceof ServerPlayer) {
++ CriteriaTriggers.CURED_ZOMBIE_VILLAGER.trigger((ServerPlayer) entityhuman, this, entityvillager);
++ serverLevel.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()) {
+- serverLevel.levelEvent(null, 1027, this.blockPosition(), 0);
++ serverLevel.levelEvent((Player) null, 1027, this.blockPosition(), 0);
+ }
++
+ }
+
+ private int getConversionProgress() {
+ int i = 1;
++
+ if (this.random.nextFloat() < 0.01F) {
+- int i1 = 0;
+- BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
++ int j = 0;
++ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
+
+- for (int i2 = (int)this.getX() - 4; i2 < (int)this.getX() + 4 && i1 < 14; i2++) {
+- for (int i3 = (int)this.getY() - 4; i3 < (int)this.getY() + 4 && i1 < 14; i3++) {
+- for (int i4 = (int)this.getZ() - 4; i4 < (int)this.getZ() + 4 && i1 < 14; i4++) {
+- BlockState blockState = this.level().getBlockState(mutableBlockPos.set(i2, i3, i4));
+- 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) {
++ IBlockData 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;
+ }
+
+- i1++;
++ ++j;
+ }
+ }
+ }
+@@ -280,9 +310,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
+@@ -320,26 +348,25 @@
+
+ @Nullable
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
+ this.setVillagerData(this.getVillagerData().setType(VillagerType.byBiome(level.getBiome(this.blockPosition()))));
+ return super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
+ }
+
+ @Override
+ public void setVillagerData(VillagerData data) {
+- VillagerData villagerData = this.getVillagerData();
+- if (villagerData.getProfession() != data.getProfession()) {
++ VillagerData villagerdata1 = this.getVillagerData();
++
++ if (villagerdata1.getProfession() != data.getProfession()) {
+ this.tradeOffers = null;
+ }
+
+- this.entityData.set(DATA_VILLAGER_DATA, data);
++ this.entityData.set(ZombieVillager.DATA_VILLAGER_DATA, data);
+ }
+
+ @Override
+ public VillagerData getVillagerData() {
+- return this.entityData.get(DATA_VILLAGER_DATA);
++ return (VillagerData) this.entityData.get(ZombieVillager.DATA_VILLAGER_DATA);
+ }
+
+ public int getVillagerXp() {
+@@ -351,7 +378,7 @@
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height + 0.175F * f, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height + 0.175F * f, 0.0F);
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/ZombifiedPiglin.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/ZombifiedPiglin.java.patch
new file mode 100644
index 0000000000..e8ce6c3e73
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/ZombifiedPiglin.java.patch
@@ -0,0 +1,202 @@
+--- a/net/minecraft/world/entity/monster/ZombifiedPiglin.java
++++ b/net/minecraft/world/entity/monster/ZombifiedPiglin.java
+@@ -15,13 +15,13 @@
+ 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.EquipmentSlot;
+ import net.minecraft.world.entity.LivingEntity;
+-import net.minecraft.world.entity.MobSpawnType;
+ import net.minecraft.world.entity.NeutralMob;
+-import net.minecraft.world.entity.Pose;
+ import net.minecraft.world.entity.ai.attributes.AttributeInstance;
+ import net.minecraft.world.entity.ai.attributes.AttributeModifier;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+@@ -43,10 +43,9 @@
+ import org.joml.Vector3f;
+
+ public class ZombifiedPiglin extends Zombie implements NeutralMob {
++
+ private static final UUID SPEED_MODIFIER_ATTACKING_UUID = UUID.fromString("49455A49-7EC5-45BA-B886-3B90B23A1718");
+- private static final AttributeModifier SPEED_MODIFIER_ATTACKING = new AttributeModifier(
+- SPEED_MODIFIER_ATTACKING_UUID, "Attacking speed boost", 0.05, AttributeModifier.Operation.ADDITION
+- );
++ private static final AttributeModifier SPEED_MODIFIER_ATTACKING = new AttributeModifier(ZombifiedPiglin.SPEED_MODIFIER_ATTACKING_UUID, "Attacking speed boost", 0.05D, AttributeModifier.Operation.ADDITION);
+ private static final UniformInt FIRST_ANGER_SOUND_DELAY = TimeUtil.rangeOfSeconds(0, 1);
+ private int playFirstAngerSoundIn;
+ private static final UniformInt PERSISTENT_ANGER_TIME = TimeUtil.rangeOfSeconds(20, 39);
+@@ -71,22 +70,19 @@
+
+ @Override
+ protected void addBehaviourGoals() {
+- this.goalSelector.addGoal(2, new ZombieAttackGoal(this, 1.0, false));
+- this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0));
+- this.targetSelector.addGoal(1, new HurtByTargetGoal(this).setAlertOthers());
++ this.goalSelector.addGoal(2, new ZombieAttackGoal(this, 1.0D, false));
++ this.goalSelector.addGoal(7, new WaterAvoidingRandomStrollGoal(this, 1.0D));
++ this.targetSelector.addGoal(1, (new HurtByTargetGoal(this, new Class[0])).setAlertOthers());
+ this.targetSelector.addGoal(2, new NearestAttackableTargetGoal<>(this, Player.class, 10, true, false, this::isAngryAt));
+ this.targetSelector.addGoal(3, new ResetUniversalAngerTargetGoal<>(this, true));
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Zombie.createAttributes()
+- .add(Attributes.SPAWN_REINFORCEMENTS_CHANCE, 0.0)
+- .add(Attributes.MOVEMENT_SPEED, 0.23F)
+- .add(Attributes.ATTACK_DAMAGE, 5.0);
++ return Zombie.createAttributes().add(Attributes.SPAWN_REINFORCEMENTS_CHANCE, 0.0D).add(Attributes.MOVEMENT_SPEED, 0.23000000417232513D).add(Attributes.ATTACK_DAMAGE, 5.0D);
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ return this.isBaby() ? 0.96999997F : 1.79F;
+ }
+
+@@ -97,18 +93,19 @@
+
+ @Override
+ protected void customServerAiStep() {
+- AttributeInstance attribute = this.getAttribute(Attributes.MOVEMENT_SPEED);
++ AttributeInstance attributemodifiable = this.getAttribute(Attributes.MOVEMENT_SPEED);
++
+ if (this.isAngry()) {
+- if (!this.isBaby() && !attribute.hasModifier(SPEED_MODIFIER_ATTACKING)) {
+- attribute.addTransientModifier(SPEED_MODIFIER_ATTACKING);
++ if (!this.isBaby() && !attributemodifiable.hasModifier(ZombifiedPiglin.SPEED_MODIFIER_ATTACKING)) {
++ attributemodifiable.addTransientModifier(ZombifiedPiglin.SPEED_MODIFIER_ATTACKING);
+ }
+
+ this.maybePlayFirstAngerSound();
+- } else if (attribute.hasModifier(SPEED_MODIFIER_ATTACKING)) {
+- attribute.removeModifier(SPEED_MODIFIER_ATTACKING.getId());
++ } else if (attributemodifiable.hasModifier(ZombifiedPiglin.SPEED_MODIFIER_ATTACKING)) {
++ attributemodifiable.removeModifier(ZombifiedPiglin.SPEED_MODIFIER_ATTACKING.getId());
+ }
+
+- this.updatePersistentAnger((ServerLevel)this.level(), true);
++ this.updatePersistentAnger((ServerLevel) this.level(), true);
+ if (this.getTarget() != null) {
+ this.maybeAlertOthers();
+ }
+@@ -122,35 +119,39 @@
+
+ private void maybePlayFirstAngerSound() {
+ if (this.playFirstAngerSoundIn > 0) {
+- this.playFirstAngerSoundIn--;
++ --this.playFirstAngerSoundIn;
+ if (this.playFirstAngerSoundIn == 0) {
+ this.playAngerSound();
+ }
+ }
++
+ }
+
+ private void maybeAlertOthers() {
+ if (this.ticksUntilNextAlert > 0) {
+- this.ticksUntilNextAlert--;
++ --this.ticksUntilNextAlert;
+ } else {
+ if (this.getSensing().hasLineOfSight(this.getTarget())) {
+ this.alertOthers();
+ }
+
+- this.ticksUntilNextAlert = ALERT_INTERVAL.sample(this.random);
++ this.ticksUntilNextAlert = ZombifiedPiglin.ALERT_INTERVAL.sample(this.random);
+ }
+ }
+
+ private void alertOthers() {
+- double attributeValue = this.getAttributeValue(Attributes.FOLLOW_RANGE);
+- AABB aABB = AABB.unitCubeFromLowerCorner(this.position()).inflate(attributeValue, 10.0, attributeValue);
+- this.level()
+- .getEntitiesOfClass(ZombifiedPiglin.class, aABB, EntitySelector.NO_SPECTATORS)
+- .stream()
+- .filter(zombifiedPiglin -> zombifiedPiglin != this)
+- .filter(zombifiedPiglin -> zombifiedPiglin.getTarget() == null)
+- .filter(zombifiedPiglin -> !zombifiedPiglin.isAlliedTo(this.getTarget()))
+- .forEach(zombifiedPiglin -> zombifiedPiglin.setTarget(this.getTarget()));
++ double d0 = this.getAttributeValue(Attributes.FOLLOW_RANGE);
++ AABB axisalignedbb = AABB.unitCubeFromLowerCorner(this.position()).inflate(d0, 10.0D, d0);
++
++ this.level().getEntitiesOfClass(ZombifiedPiglin.class, axisalignedbb, EntitySelector.NO_SPECTATORS).stream().filter((entitypigzombie) -> {
++ return entitypigzombie != this;
++ }).filter((entitypigzombie) -> {
++ return entitypigzombie.getTarget() == null;
++ }).filter((entitypigzombie) -> {
++ return !entitypigzombie.isAlliedTo((Entity) this.getTarget());
++ }).forEach((entitypigzombie) -> {
++ entitypigzombie.setTarget(this.getTarget(), org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY, true); // CraftBukkit
++ });
+ }
+
+ private void playAngerSound() {
+@@ -158,27 +159,34 @@
+ }
+
+ @Override
+- public void setTarget(@Nullable LivingEntity livingEntity) {
+- if (this.getTarget() == null && livingEntity != null) {
+- this.playFirstAngerSoundIn = FIRST_ANGER_SOUND_DELAY.sample(this.random);
+- this.ticksUntilNextAlert = ALERT_INTERVAL.sample(this.random);
++ public boolean setTarget(@Nullable LivingEntity entityliving, org.bukkit.event.entity.EntityTargetEvent.TargetReason reason, boolean fireEvent) { // CraftBukkit - signature
++ if (this.getTarget() == null && entityliving != null) {
++ this.playFirstAngerSoundIn = ZombifiedPiglin.FIRST_ANGER_SOUND_DELAY.sample(this.random);
++ this.ticksUntilNextAlert = ZombifiedPiglin.ALERT_INTERVAL.sample(this.random);
+ }
+
+- if (livingEntity instanceof Player) {
+- this.setLastHurtByPlayer((Player)livingEntity);
++ if (entityliving instanceof Player) {
++ this.setLastHurtByPlayer((Player) entityliving);
+ }
+
+- super.setTarget(livingEntity);
++ return super.setTarget(entityliving, reason, fireEvent); // CraftBukkit
+ }
+
+ @Override
+ public void startPersistentAngerTimer() {
+- this.setRemainingPersistentAngerTime(PERSISTENT_ANGER_TIME.sample(this.random));
++ // CraftBukkit start
++ Entity entity = ((ServerLevel) this.level()).getEntity(getPersistentAngerTarget());
++ org.bukkit.event.entity.PigZombieAngerEvent event = new org.bukkit.event.entity.PigZombieAngerEvent((org.bukkit.entity.PigZombie) this.getBukkitEntity(), (entity == null) ? null : entity.getBukkitEntity(), ZombifiedPiglin.PERSISTENT_ANGER_TIME.sample(this.random));
++ this.level().getCraftServer().getPluginManager().callEvent(event);
++ if (event.isCancelled()) {
++ this.setPersistentAngerTarget(null);
++ return;
++ }
++ this.setRemainingPersistentAngerTime(event.getNewAnger());
++ // CraftBukkit end
+ }
+
+- public static boolean checkZombifiedPiglinSpawnRules(
+- EntityType<ZombifiedPiglin> zombifiedPiglin, LevelAccessor level, MobSpawnType spawnType, BlockPos pos, RandomSource random
+- ) {
++ public static boolean checkZombifiedPiglinSpawnRules(EntityType<ZombifiedPiglin> zombifiedPiglin, LevelAccessor level, EnumMobSpawn spawnType, BlockPos pos, RandomSource random) {
+ return level.getDifficulty() != Difficulty.PEACEFUL && !level.getBlockState(pos.below()).is(Blocks.NETHER_WART_BLOCK);
+ }
+
+@@ -236,7 +244,7 @@
+
+ @Override
+ protected void randomizeReinforcementsChance() {
+- this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(0.0);
++ this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(0.0D);
+ }
+
+ @Nullable
+@@ -256,7 +264,7 @@
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height + 0.05F * f, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height + 0.05F * f, 0.0F);
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java.patch
new file mode 100644
index 0000000000..4b8d2d70f8
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java.patch
@@ -0,0 +1,133 @@
+--- a/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java
++++ b/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java
+@@ -11,9 +11,9 @@
+ import net.minecraft.world.effect.MobEffects;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
++import net.minecraft.world.entity.EntityPose;
+ import net.minecraft.world.entity.EntityType;
+ import net.minecraft.world.entity.LivingEntity;
+-import net.minecraft.world.entity.Pose;
+ import net.minecraft.world.entity.ai.memory.MemoryModuleType;
+ import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
+ import net.minecraft.world.entity.ai.util.GoalUtils;
+@@ -25,12 +25,11 @@
+ import org.joml.Vector3f;
+
+ public abstract class AbstractPiglin extends Monster {
+- protected static final EntityDataAccessor<Boolean> DATA_IMMUNE_TO_ZOMBIFICATION = SynchedEntityData.defineId(
+- AbstractPiglin.class, EntityDataSerializers.BOOLEAN
+- );
++
++ protected static final EntityDataAccessor<Boolean> DATA_IMMUNE_TO_ZOMBIFICATION = SynchedEntityData.defineId(AbstractPiglin.class, EntityDataSerializers.BOOLEAN);
+ protected static final int CONVERSION_TIME = 300;
+ protected static final float PIGLIN_EYE_HEIGHT = 1.79F;
+- protected int timeInOverworld;
++ public int timeInOverworld;
+
+ public AbstractPiglin(EntityType<? extends AbstractPiglin> entityType, Level level) {
+ super(entityType, level);
+@@ -42,12 +41,13 @@
+
+ private void applyOpenDoorsAbility() {
+ if (GoalUtils.hasGroundPathNavigation(this)) {
+- ((GroundPathNavigation)this.getNavigation()).setCanOpenDoors(true);
++ ((GroundPathNavigation) this.getNavigation()).setCanOpenDoors(true);
+ }
++
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions dimensions) {
+ return 1.79F;
+ }
+
+@@ -57,24 +57,24 @@
+ }
+
+ @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);
+ }
+
+ protected abstract boolean canHunt();
+
+ public void setImmuneToZombification(boolean immuneToZombification) {
+- this.getEntityData().set(DATA_IMMUNE_TO_ZOMBIFICATION, immuneToZombification);
++ this.getEntityData().set(AbstractPiglin.DATA_IMMUNE_TO_ZOMBIFICATION, immuneToZombification);
+ }
+
+- protected boolean isImmuneToZombification() {
+- return this.getEntityData().get(DATA_IMMUNE_TO_ZOMBIFICATION);
++ public boolean isImmuneToZombification() {
++ return (Boolean) this.getEntityData().get(AbstractPiglin.DATA_IMMUNE_TO_ZOMBIFICATION);
+ }
+
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_IMMUNE_TO_ZOMBIFICATION, false);
++ this.entityData.define(AbstractPiglin.DATA_IMMUNE_TO_ZOMBIFICATION, false);
+ }
+
+ @Override
+@@ -98,15 +98,16 @@
+ protected void customServerAiStep() {
+ super.customServerAiStep();
+ if (this.isConverting()) {
+- this.timeInOverworld++;
++ ++this.timeInOverworld;
+ } else {
+ this.timeInOverworld = 0;
+ }
+
+ if (this.timeInOverworld > 300) {
+ this.playConvertedSound();
+- this.finishConversion((ServerLevel)this.level());
++ this.finishConversion((ServerLevel) this.level());
+ }
++
+ }
+
+ public boolean isConverting() {
+@@ -114,22 +115,24 @@
+ }
+
+ protected void finishConversion(ServerLevel serverLevel) {
+- ZombifiedPiglin zombifiedPiglin = this.convertTo(EntityType.ZOMBIFIED_PIGLIN, true);
+- if (zombifiedPiglin != null) {
+- zombifiedPiglin.addEffect(new MobEffectInstance(MobEffects.CONFUSION, 200, 0));
++ ZombifiedPiglin entitypigzombie = (ZombifiedPiglin) this.convertTo(EntityType.ZOMBIFIED_PIGLIN, true, org.bukkit.event.entity.EntityTransformEvent.TransformReason.PIGLIN_ZOMBIFIED, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.PIGLIN_ZOMBIFIED); // CraftBukkit - add spawn and transform reasons
++
++ if (entitypigzombie != null) {
++ entitypigzombie.addEffect(new MobEffectInstance(MobEffects.CONFUSION, 200, 0));
+ }
++
+ }
+
+ public boolean isAdult() {
+ return !this.isBaby();
+ }
+
+- public abstract PiglinArmPose getArmPose();
++ public abstract EntityPiglinArmPose getArmPose();
+
+ @Nullable
+ @Override
+ public LivingEntity getTarget() {
+- return this.brain.getMemory(MemoryModuleType.ATTACK_TARGET).orElse(null);
++ return (LivingEntity) this.brain.getMemory(MemoryModuleType.ATTACK_TARGET).orElse(null); // CraftBukkit - decompile error
+ }
+
+ protected boolean isHoldingMeleeWeapon() {
+@@ -141,6 +144,7 @@
+ if (PiglinAi.isIdle(this)) {
+ super.playAmbientSound();
+ }
++
+ }
+
+ @Override
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/piglin/Piglin.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/piglin/Piglin.java.patch
new file mode 100644
index 0000000000..7bda1083f5
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/piglin/Piglin.java.patch
@@ -0,0 +1,474 @@
+--- a/net/minecraft/world/entity/monster/piglin/Piglin.java
++++ b/net/minecraft/world/entity/monster/piglin/Piglin.java
+@@ -5,30 +5,22 @@
+ import java.util.List;
+ import java.util.UUID;
+ import javax.annotation.Nullable;
+-import net.minecraft.core.BlockPos;
+-import net.minecraft.nbt.CompoundTag;
+-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.util.RandomSource;
+ import net.minecraft.util.VisibleForDebug;
+ import net.minecraft.world.DifficultyInstance;
+-import net.minecraft.world.InteractionHand;
++import net.minecraft.world.EnumHand;
+ import net.minecraft.world.InteractionResult;
+ import net.minecraft.world.SimpleContainer;
+ 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.EntityType;
++import net.minecraft.world.entity.EnumMobSpawn;
+ import net.minecraft.world.entity.EquipmentSlot;
++import net.minecraft.world.entity.GroupDataEntity;
+ import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.Mob;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.Pose;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.ai.Brain;
+ import net.minecraft.world.entity.ai.attributes.AttributeInstance;
+ import net.minecraft.world.entity.ai.attributes.AttributeModifier;
+@@ -53,16 +45,35 @@
+ import net.minecraft.world.level.LevelAccessor;
+ 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;
+
++// CraftBukkit start
++import java.util.stream.Collectors;
++import java.util.HashSet;
++import java.util.Set;
++import net.minecraft.core.BlockPos;
++import net.minecraft.core.registries.BuiltInRegistries;
++import net.minecraft.nbt.CompoundTag;
++import net.minecraft.nbt.ListTag;
++import net.minecraft.nbt.StringTag;
++import net.minecraft.nbt.Tag;
++import net.minecraft.network.syncher.EntityDataAccessor;
++import net.minecraft.network.syncher.EntityDataSerializers;
++import net.minecraft.network.syncher.SynchedEntityData;
++import net.minecraft.resources.ResourceLocation;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.sounds.SoundEvent;
++import net.minecraft.sounds.SoundEvents;
++import net.minecraft.world.item.Item;
++// CraftBukkit end
++
+ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, InventoryCarrier {
++
+ private static final EntityDataAccessor<Boolean> DATA_BABY_ID = SynchedEntityData.defineId(Piglin.class, EntityDataSerializers.BOOLEAN);
+ private static final EntityDataAccessor<Boolean> DATA_IS_CHARGING_CROSSBOW = SynchedEntityData.defineId(Piglin.class, EntityDataSerializers.BOOLEAN);
+ private static final EntityDataAccessor<Boolean> DATA_IS_DANCING = SynchedEntityData.defineId(Piglin.class, EntityDataSerializers.BOOLEAN);
+ private static final UUID SPEED_MODIFIER_BABY_UUID = UUID.fromString("766bfa64-11f3-11ea-8d71-362b9e155667");
+- private static final AttributeModifier SPEED_MODIFIER_BABY = new AttributeModifier(
+- SPEED_MODIFIER_BABY_UUID, "Baby speed boost", 0.2F, AttributeModifier.Operation.MULTIPLY_BASE
+- );
++ private static final AttributeModifier SPEED_MODIFIER_BABY = new AttributeModifier(Piglin.SPEED_MODIFIER_BABY_UUID, "Baby speed boost", 0.20000000298023224D, AttributeModifier.Operation.MULTIPLY_BASE);
+ private static final int MAX_HEALTH = 16;
+ private static final float MOVEMENT_SPEED_WHEN_FIGHTING = 0.35F;
+ private static final int ATTACK_DAMAGE = 5;
+@@ -71,53 +82,15 @@
+ private static final int MAX_PASSENGERS_ON_ONE_HOGLIN = 3;
+ private static final float PROBABILITY_OF_SPAWNING_AS_BABY = 0.2F;
+ private static final float BABY_EYE_HEIGHT_ADJUSTMENT = 0.82F;
+- private static final double PROBABILITY_OF_SPAWNING_WITH_CROSSBOW_INSTEAD_OF_SWORD = 0.5;
+- private final SimpleContainer inventory = new SimpleContainer(8);
+- private boolean cannotHunt;
+- protected static final ImmutableList<SensorType<? extends Sensor<? super Piglin>>> SENSOR_TYPES = ImmutableList.of(
+- SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.NEAREST_ITEMS, SensorType.HURT_BY, SensorType.PIGLIN_SPECIFIC_SENSOR
+- );
+- protected static final ImmutableList<MemoryModuleType<?>> MEMORY_TYPES = ImmutableList.of(
+- MemoryModuleType.LOOK_TARGET,
+- MemoryModuleType.DOORS_TO_CLOSE,
+- MemoryModuleType.NEAREST_LIVING_ENTITIES,
+- MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES,
+- MemoryModuleType.NEAREST_VISIBLE_PLAYER,
+- MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER,
+- MemoryModuleType.NEAREST_VISIBLE_ADULT_PIGLINS,
+- MemoryModuleType.NEARBY_ADULT_PIGLINS,
+- MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM,
+- MemoryModuleType.ITEM_PICKUP_COOLDOWN_TICKS,
+- MemoryModuleType.HURT_BY,
+- MemoryModuleType.HURT_BY_ENTITY,
+- MemoryModuleType.WALK_TARGET,
+- MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE,
+- MemoryModuleType.ATTACK_TARGET,
+- MemoryModuleType.ATTACK_COOLING_DOWN,
+- MemoryModuleType.INTERACTION_TARGET,
+- MemoryModuleType.PATH,
+- MemoryModuleType.ANGRY_AT,
+- MemoryModuleType.UNIVERSAL_ANGER,
+- MemoryModuleType.AVOID_TARGET,
+- MemoryModuleType.ADMIRING_ITEM,
+- MemoryModuleType.TIME_TRYING_TO_REACH_ADMIRE_ITEM,
+- MemoryModuleType.ADMIRING_DISABLED,
+- MemoryModuleType.DISABLE_WALK_TO_ADMIRE_ITEM,
+- MemoryModuleType.CELEBRATE_LOCATION,
+- MemoryModuleType.DANCING,
+- MemoryModuleType.HUNTED_RECENTLY,
+- MemoryModuleType.NEAREST_VISIBLE_BABY_HOGLIN,
+- MemoryModuleType.NEAREST_VISIBLE_NEMESIS,
+- MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED,
+- MemoryModuleType.RIDE_TARGET,
+- MemoryModuleType.VISIBLE_ADULT_PIGLIN_COUNT,
+- MemoryModuleType.VISIBLE_ADULT_HOGLIN_COUNT,
+- MemoryModuleType.NEAREST_VISIBLE_HUNTABLE_HOGLIN,
+- MemoryModuleType.NEAREST_TARGETABLE_PLAYER_NOT_WEARING_GOLD,
+- MemoryModuleType.NEAREST_PLAYER_HOLDING_WANTED_ITEM,
+- MemoryModuleType.ATE_RECENTLY,
+- MemoryModuleType.NEAREST_REPELLENT
+- );
++ private static final double PROBABILITY_OF_SPAWNING_WITH_CROSSBOW_INSTEAD_OF_SWORD = 0.5D;
++ public final SimpleContainer inventory = new SimpleContainer(8);
++ public boolean cannotHunt;
++ protected static final ImmutableList<SensorType<? extends Sensor<? super Piglin>>> SENSOR_TYPES = ImmutableList.of(SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.NEAREST_ITEMS, SensorType.HURT_BY, SensorType.PIGLIN_SPECIFIC_SENSOR);
++ protected static final ImmutableList<MemoryModuleType<?>> MEMORY_TYPES = ImmutableList.of(MemoryModuleType.LOOK_TARGET, MemoryModuleType.DOORS_TO_CLOSE, MemoryModuleType.NEAREST_LIVING_ENTITIES, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, MemoryModuleType.NEAREST_VISIBLE_PLAYER, MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER, MemoryModuleType.NEAREST_VISIBLE_ADULT_PIGLINS, MemoryModuleType.NEARBY_ADULT_PIGLINS, MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM, MemoryModuleType.ITEM_PICKUP_COOLDOWN_TICKS, MemoryModuleType.HURT_BY, MemoryModuleType.HURT_BY_ENTITY, new MemoryModuleType[]{MemoryModuleType.WALK_TARGET, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.ATTACK_TARGET, MemoryModuleType.ATTACK_COOLING_DOWN, MemoryModuleType.INTERACTION_TARGET, MemoryModuleType.PATH, MemoryModuleType.ANGRY_AT, MemoryModuleType.UNIVERSAL_ANGER, MemoryModuleType.AVOID_TARGET, MemoryModuleType.ADMIRING_ITEM, MemoryModuleType.TIME_TRYING_TO_REACH_ADMIRE_ITEM, MemoryModuleType.ADMIRING_DISABLED, MemoryModuleType.DISABLE_WALK_TO_ADMIRE_ITEM, MemoryModuleType.CELEBRATE_LOCATION, MemoryModuleType.DANCING, MemoryModuleType.HUNTED_RECENTLY, MemoryModuleType.NEAREST_VISIBLE_BABY_HOGLIN, MemoryModuleType.NEAREST_VISIBLE_NEMESIS, MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED, MemoryModuleType.RIDE_TARGET, MemoryModuleType.VISIBLE_ADULT_PIGLIN_COUNT, MemoryModuleType.VISIBLE_ADULT_HOGLIN_COUNT, MemoryModuleType.NEAREST_VISIBLE_HUNTABLE_HOGLIN, MemoryModuleType.NEAREST_TARGETABLE_PLAYER_NOT_WEARING_GOLD, MemoryModuleType.NEAREST_PLAYER_HOLDING_WANTED_ITEM, MemoryModuleType.ATE_RECENTLY, MemoryModuleType.NEAREST_REPELLENT});
++ // CraftBukkit start - Custom bartering and interest list
++ public Set<Item> allowedBarterItems = new HashSet<>();
++ public Set<Item> interestItems = new HashSet<>();
++ // CraftBukkit end
+
+ public Piglin(EntityType<? extends AbstractPiglin> entityType, Level level) {
+ super(entityType, level);
+@@ -136,6 +109,14 @@
+ }
+
+ this.writeInventoryToTag(compound);
++ // CraftBukkit start
++ ListTag barterList = new ListTag();
++ allowedBarterItems.stream().map(BuiltInRegistries.ITEM::getKey).map(ResourceLocation::toString).map(StringTag::valueOf).forEach(barterList::add);
++ compound.put("Bukkit.BarterList", barterList);
++ ListTag interestList = new ListTag();
++ interestItems.stream().map(BuiltInRegistries.ITEM::getKey).map(ResourceLocation::toString).map(StringTag::valueOf).forEach(interestList::add);
++ compound.put("Bukkit.InterestList", interestList);
++ // CraftBukkit end
+ }
+
+ @Override
+@@ -144,6 +125,10 @@
+ this.setBaby(compound.getBoolean("IsBaby"));
+ this.setCannotHunt(compound.getBoolean("CannotHunt"));
+ this.readInventoryFromTag(compound);
++ // CraftBukkit start
++ this.allowedBarterItems = compound.getList("Bukkit.BarterList", 8).stream().map(Tag::getAsString).map(ResourceLocation::tryParse).map(BuiltInRegistries.ITEM::get).collect(Collectors.toCollection(HashSet::new));
++ this.interestItems = compound.getList("Bukkit.InterestList", 8).stream().map(Tag::getAsString).map(ResourceLocation::tryParse).map(BuiltInRegistries.ITEM::get).collect(Collectors.toCollection(HashSet::new));
++ // CraftBukkit end
+ }
+
+ @VisibleForDebug
+@@ -155,10 +140,17 @@
+ @Override
+ protected void dropCustomDeathLoot(DamageSource source, int looting, boolean recentlyHit) {
+ super.dropCustomDeathLoot(source, looting, recentlyHit);
+- if (source.getEntity() instanceof Creeper creeper && creeper.canDropMobsSkull()) {
+- ItemStack itemStack = new ItemStack(Items.PIGLIN_HEAD);
+- creeper.increaseDroppedSkulls();
+- this.spawnAtLocation(itemStack);
++ Entity entity = source.getEntity();
++
++ if (entity instanceof Creeper) {
++ Creeper entitycreeper = (Creeper) entity;
++
++ if (entitycreeper.canDropMobsSkull()) {
++ ItemStack itemstack = new ItemStack(Items.PIGLIN_HEAD);
++
++ entitycreeper.increaseDroppedSkulls();
++ this.spawnAtLocation(itemstack);
++ }
+ }
+
+ this.inventory.removeAllItems().forEach(this::spawnAtLocation);
+@@ -175,35 +167,35 @@
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_BABY_ID, false);
+- this.entityData.define(DATA_IS_CHARGING_CROSSBOW, false);
+- this.entityData.define(DATA_IS_DANCING, false);
++ this.entityData.define(Piglin.DATA_BABY_ID, false);
++ this.entityData.define(Piglin.DATA_IS_CHARGING_CROSSBOW, false);
++ this.entityData.define(Piglin.DATA_IS_DANCING, false);
+ }
+
+ @Override
+ public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
+ super.onSyncedDataUpdated(key);
+- if (DATA_BABY_ID.equals(key)) {
++ if (Piglin.DATA_BABY_ID.equals(key)) {
+ this.refreshDimensions();
+ }
++
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 16.0).add(Attributes.MOVEMENT_SPEED, 0.35F).add(Attributes.ATTACK_DAMAGE, 5.0);
++ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 16.0D).add(Attributes.MOVEMENT_SPEED, 0.3499999940395355D).add(Attributes.ATTACK_DAMAGE, 5.0D);
+ }
+
+- public static boolean checkPiglinSpawnRules(EntityType<Piglin> piglin, LevelAccessor level, MobSpawnType spawnType, BlockPos pos, RandomSource random) {
++ public static boolean checkPiglinSpawnRules(EntityType<Piglin> piglin, LevelAccessor level, EnumMobSpawn spawnType, BlockPos pos, RandomSource random) {
+ return !level.getBlockState(pos.below()).is(Blocks.NETHER_WART_BLOCK);
+ }
+
+ @Nullable
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
+- RandomSource random = level.getRandom();
+- if (reason != MobSpawnType.STRUCTURE) {
+- if (random.nextFloat() < 0.2F) {
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
++ RandomSource randomsource = level.getRandom();
++
++ if (reason != EnumMobSpawn.STRUCTURE) {
++ if (randomsource.nextFloat() < 0.2F) {
+ this.setBaby(true);
+ } else if (this.isAdult()) {
+ this.setItemSlot(EquipmentSlot.MAINHAND, this.createSpawnWeapon());
+@@ -211,8 +203,8 @@
+ }
+
+ PiglinAi.initMemories(this, level.getRandom());
+- this.populateDefaultEquipmentSlots(random, difficulty);
+- this.populateDefaultEquipmentEnchantments(random, difficulty);
++ this.populateDefaultEquipmentSlots(randomsource, difficulty);
++ this.populateDefaultEquipmentEnchantments(randomsource, difficulty);
+ return super.finalizeSpawn(level, difficulty, reason, spawnData, dataTag);
+ }
+
+@@ -234,17 +226,19 @@
+ this.maybeWearArmor(EquipmentSlot.LEGS, new ItemStack(Items.GOLDEN_LEGGINGS), random);
+ this.maybeWearArmor(EquipmentSlot.FEET, new ItemStack(Items.GOLDEN_BOOTS), random);
+ }
++
+ }
+
+ private void maybeWearArmor(EquipmentSlot slot, ItemStack stack, RandomSource random) {
+ if (random.nextFloat() < 0.1F) {
+ this.setItemSlot(slot, stack);
+ }
++
+ }
+
+ @Override
+ protected Brain.Provider<Piglin> brainProvider() {
+- return Brain.provider(MEMORY_TYPES, SENSOR_TYPES);
++ return Brain.provider(Piglin.MEMORY_TYPES, Piglin.SENSOR_TYPES);
+ }
+
+ @Override
+@@ -254,43 +248,48 @@
+
+ @Override
+ public Brain<Piglin> getBrain() {
+- return (Brain<Piglin>)super.getBrain();
++ return (Brain<Piglin>) super.getBrain(); // CraftBukkit - Decompile error
+ }
+
+ @Override
+- public InteractionResult mobInteract(Player player, InteractionHand hand) {
+- InteractionResult interactionResult = super.mobInteract(player, hand);
+- if (interactionResult.consumesAction()) {
+- return interactionResult;
++ public InteractionResult mobInteract(Player player, EnumHand hand) {
++ InteractionResult enuminteractionresult = super.mobInteract(player, hand);
++
++ if (enuminteractionresult.consumesAction()) {
++ return enuminteractionresult;
+ } else if (!this.level().isClientSide) {
+ return PiglinAi.mobInteract(this, player, hand);
+ } else {
+- boolean flag = PiglinAi.canAdmire(this, player.getItemInHand(hand)) && this.getArmPose() != PiglinArmPose.ADMIRING_ITEM;
++ boolean flag = PiglinAi.canAdmire(this, player.getItemInHand(hand)) && this.getArmPose() != EntityPiglinArmPose.ADMIRING_ITEM;
++
+ return flag ? InteractionResult.SUCCESS : InteractionResult.PASS;
+ }
+ }
+
+ @Override
+- protected float getStandingEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getStandingEyeHeight(EntityPose pose, EntityDimensions size) {
+ float f = super.getStandingEyeHeight(pose, size);
++
+ return this.isBaby() ? f - 0.82F : f;
+ }
+
+ @Override
+ public void setBaby(boolean childZombie) {
+- this.getEntityData().set(DATA_BABY_ID, childZombie);
++ this.getEntityData().set(Piglin.DATA_BABY_ID, childZombie);
+ if (!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(Piglin.SPEED_MODIFIER_BABY.getId());
+ if (childZombie) {
+- attribute.addTransientModifier(SPEED_MODIFIER_BABY);
++ attributemodifiable.addTransientModifier(Piglin.SPEED_MODIFIER_BABY);
+ }
+ }
++
+ }
+
+ @Override
+ public boolean isBaby() {
+- return this.getEntityData().get(DATA_BABY_ID);
++ return (Boolean) this.getEntityData().get(Piglin.DATA_BABY_ID);
+ }
+
+ private void setCannotHunt(boolean cannotHunt) {
+@@ -305,7 +304,7 @@
+ @Override
+ protected void customServerAiStep() {
+ this.level().getProfiler().push("piglinBrain");
+- this.getBrain().tick((ServerLevel)this.level(), this);
++ this.getBrain().tick((ServerLevel) this.level(), this);
+ this.level().getProfiler().pop();
+ PiglinAi.updateActivity(this);
+ super.customServerAiStep();
+@@ -324,16 +323,16 @@
+ }
+
+ private ItemStack createSpawnWeapon() {
+- return (double)this.random.nextFloat() < 0.5 ? new ItemStack(Items.CROSSBOW) : new ItemStack(Items.GOLDEN_SWORD);
++ return (double) this.random.nextFloat() < 0.5D ? new ItemStack(Items.CROSSBOW) : new ItemStack(Items.GOLDEN_SWORD);
+ }
+
+ private boolean isChargingCrossbow() {
+- return this.entityData.get(DATA_IS_CHARGING_CROSSBOW);
++ return (Boolean) this.entityData.get(Piglin.DATA_IS_CHARGING_CROSSBOW);
+ }
+
+ @Override
+ public void setChargingCrossbow(boolean isCharging) {
+- this.entityData.set(DATA_IS_CHARGING_CROSSBOW, isCharging);
++ this.entityData.set(Piglin.DATA_IS_CHARGING_CROSSBOW, isCharging);
+ }
+
+ @Override
+@@ -342,36 +341,27 @@
+ }
+
+ @Override
+- public PiglinArmPose getArmPose() {
+- if (this.isDancing()) {
+- return PiglinArmPose.DANCING;
+- } else if (PiglinAi.isLovedItem(this.getOffhandItem())) {
+- return PiglinArmPose.ADMIRING_ITEM;
+- } else if (this.isAggressive() && this.isHoldingMeleeWeapon()) {
+- return PiglinArmPose.ATTACKING_WITH_MELEE_WEAPON;
+- } else if (this.isChargingCrossbow()) {
+- return PiglinArmPose.CROSSBOW_CHARGE;
+- } else {
+- return this.isAggressive() && this.isHolding(Items.CROSSBOW) ? PiglinArmPose.CROSSBOW_HOLD : PiglinArmPose.DEFAULT;
+- }
++ public EntityPiglinArmPose getArmPose() {
++ return this.isDancing() ? EntityPiglinArmPose.DANCING : (PiglinAi.isLovedItem(this.getOffhandItem()) ? EntityPiglinArmPose.ADMIRING_ITEM : (this.isAggressive() && this.isHoldingMeleeWeapon() ? EntityPiglinArmPose.ATTACKING_WITH_MELEE_WEAPON : (this.isChargingCrossbow() ? EntityPiglinArmPose.CROSSBOW_CHARGE : (this.isAggressive() && this.isHolding(Items.CROSSBOW) ? EntityPiglinArmPose.CROSSBOW_HOLD : EntityPiglinArmPose.DEFAULT))));
+ }
+
+ public boolean isDancing() {
+- return this.entityData.get(DATA_IS_DANCING);
++ return (Boolean) this.entityData.get(Piglin.DATA_IS_DANCING);
+ }
+
+ public void setDancing(boolean dancing) {
+- this.entityData.set(DATA_IS_DANCING, dancing);
++ this.entityData.set(Piglin.DATA_IS_DANCING, dancing);
+ }
+
+ @Override
+ public boolean hurt(DamageSource source, float amount) {
+ boolean flag = super.hurt(source, amount);
++
+ if (this.level().isClientSide) {
+ return false;
+ } else {
+ if (flag && source.getEntity() instanceof LivingEntity) {
+- PiglinAi.wasHurtBy(this, (LivingEntity)source.getEntity());
++ PiglinAi.wasHurtBy(this, (LivingEntity) source.getEntity());
+ }
+
+ return flag;
+@@ -398,12 +388,13 @@
+ }
+
+ protected void holdInOffHand(ItemStack stack) {
+- if (stack.is(PiglinAi.BARTERING_ITEM)) {
++ if (stack.is(PiglinAi.BARTERING_ITEM) || allowedBarterItems.contains(stack.getItem())) { // CraftBukkit - Changes to accept custom payment items
+ this.setItemSlot(EquipmentSlot.OFFHAND, stack);
+ this.setGuaranteedDrop(EquipmentSlot.OFFHAND);
+ } else {
+ this.setItemSlotAndDropWhenKilled(EquipmentSlot.OFFHAND, stack);
+ }
++
+ }
+
+ @Override
+@@ -412,9 +403,10 @@
+ }
+
+ protected boolean canReplaceCurrentItem(ItemStack candidate) {
+- EquipmentSlot equipmentSlotForItem = Mob.getEquipmentSlotForItem(candidate);
+- ItemStack itemBySlot = this.getItemBySlot(equipmentSlotForItem);
+- return this.canReplaceCurrentItem(candidate, itemBySlot);
++ EquipmentSlot enumitemslot = Mob.getEquipmentSlotForItem(candidate);
++ ItemStack itemstack1 = this.getItemBySlot(enumitemslot);
++
++ return this.canReplaceCurrentItem(candidate, itemstack1);
+ }
+
+ @Override
+@@ -422,12 +414,10 @@
+ if (EnchantmentHelper.hasBindingCurse(existing)) {
+ return false;
+ } else {
+- boolean flag = PiglinAi.isLovedItem(candidate) || candidate.is(Items.CROSSBOW);
+- boolean flag1 = PiglinAi.isLovedItem(existing) || existing.is(Items.CROSSBOW);
+- return flag && !flag1
+- || (flag || !flag1)
+- && (!this.isAdult() || candidate.is(Items.CROSSBOW) || !existing.is(Items.CROSSBOW))
+- && super.canReplaceCurrentItem(candidate, existing);
++ boolean flag = PiglinAi.isLovedItem(candidate, this) || candidate.is(Items.CROSSBOW); // CraftBukkit
++ boolean flag1 = PiglinAi.isLovedItem(existing, this) || existing.is(Items.CROSSBOW); // CraftBukkit
++
++ return flag && !flag1 ? true : (!flag && flag1 ? false : (this.isAdult() && !candidate.is(Items.CROSSBOW) && existing.is(Items.CROSSBOW) ? false : super.canReplaceCurrentItem(candidate, existing)));
+ }
+ }
+
+@@ -447,13 +437,14 @@
+ }
+
+ private Entity getTopPassenger(Entity vehicle, int maxPosition) {
+- List<Entity> passengers = vehicle.getPassengers();
+- return maxPosition != 1 && !passengers.isEmpty() ? this.getTopPassenger(passengers.get(0), maxPosition - 1) : vehicle;
++ List<Entity> list = vehicle.getPassengers();
++
++ return maxPosition != 1 && !list.isEmpty() ? this.getTopPassenger((Entity) list.get(0), maxPosition - 1) : vehicle;
+ }
+
+ @Override
+ protected SoundEvent getAmbientSound() {
+- return this.level().isClientSide ? null : PiglinAi.getSoundForCurrentActivity(this).orElse(null);
++ return this.level().isClientSide ? null : (SoundEvent) PiglinAi.getSoundForCurrentActivity(this).orElse(null); // CraftBukkit - Decompile error
+ }
+
+ @Override
+@@ -467,7 +458,7 @@
+ }
+
+ @Override
+- protected void playStepSound(BlockPos pos, BlockState block) {
++ protected void playStepSound(BlockPos pos, IBlockData block) {
+ this.playSound(SoundEvents.PIGLIN_STEP, 0.15F, 1.0F);
+ }
+
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/piglin/PiglinAi.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/piglin/PiglinAi.java.patch
new file mode 100644
index 0000000000..da1bd1d9a0
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/piglin/PiglinAi.java.patch
@@ -0,0 +1,967 @@
+--- a/net/minecraft/world/entity/monster/piglin/PiglinAi.java
++++ b/net/minecraft/world/entity/monster/piglin/PiglinAi.java
+@@ -4,7 +4,9 @@
+ import com.google.common.collect.ImmutableSet;
+ import com.mojang.datafixers.util.Pair;
+ import java.util.Collections;
++import java.util.Iterator;
+ import java.util.List;
++import java.util.Objects;
+ import java.util.Optional;
+ import net.minecraft.server.level.ServerLevel;
+ import net.minecraft.sounds.SoundEvent;
+@@ -13,7 +15,7 @@
+ import net.minecraft.util.RandomSource;
+ import net.minecraft.util.TimeUtil;
+ import net.minecraft.util.valueproviders.UniformInt;
+-import net.minecraft.world.InteractionHand;
++import net.minecraft.world.EnumHand;
+ import net.minecraft.world.InteractionResult;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityType;
+@@ -52,7 +54,6 @@
+ import net.minecraft.world.entity.ai.behavior.StopBeingAngryIfTargetDead;
+ import net.minecraft.world.entity.ai.behavior.TriggerGate;
+ import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder;
+-import net.minecraft.world.entity.ai.behavior.declarative.Trigger;
+ import net.minecraft.world.entity.ai.memory.MemoryModuleType;
+ import net.minecraft.world.entity.ai.sensing.Sensor;
+ import net.minecraft.world.entity.ai.util.LandRandomPos;
+@@ -72,8 +73,15 @@
+ import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
+ import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
+ import net.minecraft.world.phys.Vec3;
++// CraftBukkit start
++import java.util.stream.Collectors;
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++import org.bukkit.craftbukkit.inventory.CraftItemStack;
++import org.bukkit.event.entity.PiglinBarterEvent;
++// CraftBukkit end
+
+ public class PiglinAi {
++
+ public static final int REPELLENT_DETECTION_RANGE_HORIZONTAL = 8;
+ public static final int REPELLENT_DETECTION_RANGE_VERTICAL = 4;
+ public static final Item BARTERING_ITEM = Items.GOLD_INGOT;
+@@ -111,6 +119,8 @@
+ private static final float SPEED_MULTIPLIER_WHEN_DANCING = 0.6F;
+ private static final float SPEED_MULTIPLIER_WHEN_IDLING = 0.6F;
+
++ public PiglinAi() {}
++
+ protected static Brain<?> makeBrain(Piglin piglin, Brain<Piglin> brain) {
+ initCoreActivity(brain);
+ initIdleActivity(brain);
+@@ -126,159 +136,56 @@
+ }
+
+ protected static void initMemories(Piglin piglin, RandomSource random) {
+- int i = TIME_BETWEEN_HUNTS.sample(random);
+- piglin.getBrain().setMemoryWithExpiry(MemoryModuleType.HUNTED_RECENTLY, true, (long)i);
++ int i = PiglinAi.TIME_BETWEEN_HUNTS.sample(random);
++
++ piglin.getBrain().setMemoryWithExpiry(MemoryModuleType.HUNTED_RECENTLY, true, (long) i);
+ }
+
+ private static void initCoreActivity(Brain<Piglin> brain) {
+- brain.addActivity(
+- Activity.CORE,
+- 0,
+- ImmutableList.of(
+- new LookAtTargetSink(45, 90),
+- new MoveToTargetSink(),
+- InteractWithDoor.create(),
+- babyAvoidNemesis(),
+- avoidZombified(),
+- StopHoldingItemIfNoLongerAdmiring.create(),
+- StartAdmiringItemIfSeen.create(119),
+- StartCelebratingIfTargetDead.create(300, PiglinAi::wantsToDance),
+- StopBeingAngryIfTargetDead.create()
+- )
+- );
++ brain.addActivity(Activity.CORE, 0, ImmutableList.of(new LookAtTargetSink(45, 90), new MoveToTargetSink(), InteractWithDoor.create(), babyAvoidNemesis(), avoidZombified(), StopHoldingItemIfNoLongerAdmiring.create(), StartAdmiringItemIfSeen.create(119), StartCelebratingIfTargetDead.create(300, PiglinAi::wantsToDance), StopBeingAngryIfTargetDead.create()));
+ }
+
+ private static void initIdleActivity(Brain<Piglin> brain) {
+- brain.addActivity(
+- Activity.IDLE,
+- 10,
+- ImmutableList.of(
+- SetEntityLookTarget.create(PiglinAi::isPlayerHoldingLovedItem, 14.0F),
+- StartAttacking.<Piglin>create(AbstractPiglin::isAdult, PiglinAi::findNearestValidAttackTarget),
+- BehaviorBuilder.triggerIf(Piglin::canHunt, StartHuntingHoglin.create()),
+- avoidRepellent(),
+- babySometimesRideBabyHoglin(),
+- createIdleLookBehaviors(),
+- createIdleMovementBehaviors(),
+- SetLookAndInteract.create(EntityType.PLAYER, 4)
+- )
+- );
++ brain.addActivity(Activity.IDLE, 10, ImmutableList.of(SetEntityLookTarget.create(PiglinAi::isPlayerHoldingLovedItem, 14.0F), StartAttacking.create(AbstractPiglin::isAdult, PiglinAi::findNearestValidAttackTarget), BehaviorBuilder.triggerIf(Piglin::canHunt, StartHuntingHoglin.create()), avoidRepellent(), babySometimesRideBabyHoglin(), createIdleLookBehaviors(), createIdleMovementBehaviors(), SetLookAndInteract.create(EntityType.PLAYER, 4)));
+ }
+
+ private static void initFightActivity(Piglin piglin, Brain<Piglin> brain) {
+- brain.addActivityAndRemoveMemoryWhenStopped(
+- Activity.FIGHT,
+- 10,
+- ImmutableList.<BehaviorControl<? super Piglin>>of(
+- StopAttackingIfTargetInvalid.create(entity -> !isNearestValidAttackTarget(piglin, entity)),
+- BehaviorBuilder.triggerIf(PiglinAi::hasCrossbow, BackUpIfTooClose.create(5, 0.75F)),
+- SetWalkTargetFromAttackTargetIfTargetOutOfReach.create(1.0F),
+- MeleeAttack.create(20),
+- new CrossbowAttack(),
+- RememberIfHoglinWasKilled.create(),
+- EraseMemoryIf.create(PiglinAi::isNearZombified, MemoryModuleType.ATTACK_TARGET)
+- ),
+- MemoryModuleType.ATTACK_TARGET
+- );
++ brain.addActivityAndRemoveMemoryWhenStopped(Activity.FIGHT, 10, ImmutableList.of(StopAttackingIfTargetInvalid.create((entityliving) -> {
++ return !isNearestValidAttackTarget(piglin, entityliving);
++ }), BehaviorBuilder.triggerIf(PiglinAi::hasCrossbow, BackUpIfTooClose.create(5, 0.75F)), SetWalkTargetFromAttackTargetIfTargetOutOfReach.create(1.0F), MeleeAttack.create(20), new CrossbowAttack<>(), RememberIfHoglinWasKilled.create(), EraseMemoryIf.create(PiglinAi::isNearZombified, MemoryModuleType.ATTACK_TARGET)), MemoryModuleType.ATTACK_TARGET);
+ }
+
+ private static void initCelebrateActivity(Brain<Piglin> brain) {
+- brain.addActivityAndRemoveMemoryWhenStopped(
+- Activity.CELEBRATE,
+- 10,
+- ImmutableList.of(
+- avoidRepellent(),
+- SetEntityLookTarget.create(PiglinAi::isPlayerHoldingLovedItem, 14.0F),
+- StartAttacking.<Piglin>create(AbstractPiglin::isAdult, PiglinAi::findNearestValidAttackTarget),
+- BehaviorBuilder.triggerIf(piglin -> !piglin.isDancing(), GoToTargetLocation.create(MemoryModuleType.CELEBRATE_LOCATION, 2, 1.0F)),
+- BehaviorBuilder.triggerIf(Piglin::isDancing, GoToTargetLocation.create(MemoryModuleType.CELEBRATE_LOCATION, 4, 0.6F)),
+- new RunOne<>(
+- ImmutableList.of(
+- Pair.of(SetEntityLookTarget.create(EntityType.PIGLIN, 8.0F), 1),
+- Pair.of(RandomStroll.stroll(0.6F, 2, 1), 1),
+- Pair.of(new DoNothing(10, 20), 1)
+- )
+- )
+- ),
+- MemoryModuleType.CELEBRATE_LOCATION
+- );
++ brain.addActivityAndRemoveMemoryWhenStopped(Activity.CELEBRATE, 10, ImmutableList.of(avoidRepellent(), SetEntityLookTarget.create(PiglinAi::isPlayerHoldingLovedItem, 14.0F), StartAttacking.create(AbstractPiglin::isAdult, PiglinAi::findNearestValidAttackTarget), BehaviorBuilder.triggerIf((entitypiglin) -> {
++ return !entitypiglin.isDancing();
++ }, GoToTargetLocation.create(MemoryModuleType.CELEBRATE_LOCATION, 2, 1.0F)), BehaviorBuilder.triggerIf(Piglin::isDancing, GoToTargetLocation.create(MemoryModuleType.CELEBRATE_LOCATION, 4, 0.6F)), new RunOne<>(ImmutableList.of(Pair.of(SetEntityLookTarget.create(EntityType.PIGLIN, 8.0F), 1), Pair.of(RandomStroll.stroll(0.6F, 2, 1), 1), Pair.of(new DoNothing(10, 20), 1)))), MemoryModuleType.CELEBRATE_LOCATION);
+ }
+
+ private static void initAdmireItemActivity(Brain<Piglin> brain) {
+- brain.addActivityAndRemoveMemoryWhenStopped(
+- Activity.ADMIRE_ITEM,
+- 10,
+- ImmutableList.of(
+- GoToWantedItem.create(PiglinAi::isNotHoldingLovedItemInOffHand, 1.0F, true, 9),
+- StopAdmiringIfItemTooFarAway.create(9),
+- StopAdmiringIfTiredOfTryingToReachItem.create(200, 200)
+- ),
+- MemoryModuleType.ADMIRING_ITEM
+- );
++ brain.addActivityAndRemoveMemoryWhenStopped(Activity.ADMIRE_ITEM, 10, ImmutableList.of(GoToWantedItem.create(PiglinAi::isNotHoldingLovedItemInOffHand, 1.0F, true, 9), StopAdmiringIfItemTooFarAway.create(9), StopAdmiringIfTiredOfTryingToReachItem.create(200, 200)), MemoryModuleType.ADMIRING_ITEM);
+ }
+
+ private static void initRetreatActivity(Brain<Piglin> brain) {
+- brain.addActivityAndRemoveMemoryWhenStopped(
+- Activity.AVOID,
+- 10,
+- ImmutableList.of(
+- SetWalkTargetAwayFrom.entity(MemoryModuleType.AVOID_TARGET, 1.0F, 12, true),
+- createIdleLookBehaviors(),
+- createIdleMovementBehaviors(),
+- EraseMemoryIf.create(PiglinAi::wantsToStopFleeing, MemoryModuleType.AVOID_TARGET)
+- ),
+- MemoryModuleType.AVOID_TARGET
+- );
++ brain.addActivityAndRemoveMemoryWhenStopped(Activity.AVOID, 10, ImmutableList.of(SetWalkTargetAwayFrom.entity(MemoryModuleType.AVOID_TARGET, 1.0F, 12, true), createIdleLookBehaviors(), createIdleMovementBehaviors(), EraseMemoryIf.create(PiglinAi::wantsToStopFleeing, MemoryModuleType.AVOID_TARGET)), MemoryModuleType.AVOID_TARGET);
+ }
+
+ private static void initRideHoglinActivity(Brain<Piglin> brain) {
+- brain.addActivityAndRemoveMemoryWhenStopped(
+- Activity.RIDE,
+- 10,
+- ImmutableList.of(
+- Mount.create(0.8F),
+- SetEntityLookTarget.create(PiglinAi::isPlayerHoldingLovedItem, 8.0F),
+- BehaviorBuilder.sequence(
+- BehaviorBuilder.triggerIf(Entity::isPassenger),
+- TriggerGate.triggerOneShuffled(
+- ImmutableList.<Pair<? extends Trigger<? super LivingEntity>, Integer>>builder()
+- .addAll(createLookBehaviors())
+- .add(Pair.of(BehaviorBuilder.triggerIf(piglin -> true), 1))
+- .build()
+- )
+- ),
+- DismountOrSkipMounting.create(8, PiglinAi::wantsToStopRiding)
+- ),
+- MemoryModuleType.RIDE_TARGET
+- );
++ // CraftBukkit - decompile error
++ brain.addActivityAndRemoveMemoryWhenStopped(Activity.RIDE, 10, ImmutableList.of(Mount.create(0.8F), SetEntityLookTarget.create(PiglinAi::isPlayerHoldingLovedItem, 8.0F), BehaviorBuilder.sequence(BehaviorBuilder.triggerIf(Entity::isPassenger), TriggerGate.triggerOneShuffled(ImmutableList.<Pair<? extends net.minecraft.world.entity.ai.behavior.declarative.Trigger<? super LivingEntity>, Integer>>builder().addAll(createLookBehaviors()).add(Pair.of(BehaviorBuilder.triggerIf((entitypiglin) -> {
++ return true;
++ }), 1)).build())), DismountOrSkipMounting.create(8, PiglinAi::wantsToStopRiding)), MemoryModuleType.RIDE_TARGET);
+ }
+
+ private static ImmutableList<Pair<OneShot<LivingEntity>, Integer>> createLookBehaviors() {
+- return ImmutableList.of(
+- Pair.of(SetEntityLookTarget.create(EntityType.PLAYER, 8.0F), 1),
+- Pair.of(SetEntityLookTarget.create(EntityType.PIGLIN, 8.0F), 1),
+- Pair.of(SetEntityLookTarget.create(8.0F), 1)
+- );
++ return ImmutableList.of(Pair.of(SetEntityLookTarget.create(EntityType.PLAYER, 8.0F), 1), Pair.of(SetEntityLookTarget.create(EntityType.PIGLIN, 8.0F), 1), Pair.of(SetEntityLookTarget.create(8.0F), 1));
+ }
+
+ private static RunOne<LivingEntity> createIdleLookBehaviors() {
+- return new RunOne<>(
+- ImmutableList.<Pair<? extends BehaviorControl<? super LivingEntity>, Integer>>builder()
+- .addAll(createLookBehaviors())
+- .add(Pair.of(new DoNothing(30, 60), 1))
+- .build()
+- );
++ return new RunOne<>(ImmutableList.<Pair<? extends BehaviorControl<? super LivingEntity>, Integer>>builder().addAll(createLookBehaviors()).add(Pair.of(new DoNothing(30, 60), 1)).build()); // CraftBukkit - decompile error
+ }
+
+ private static RunOne<Piglin> createIdleMovementBehaviors() {
+- return new RunOne<>(
+- ImmutableList.of(
+- Pair.of(RandomStroll.stroll(0.6F), 2),
+- Pair.of(InteractWith.of(EntityType.PIGLIN, 8, MemoryModuleType.INTERACTION_TARGET, 0.6F, 2), 2),
+- Pair.of(BehaviorBuilder.triggerIf(PiglinAi::doesntSeeAnyPlayerHoldingLovedItem, SetWalkTargetFromLookTarget.create(0.6F, 3)), 2),
+- Pair.of(new DoNothing(30, 60), 1)
+- )
+- );
++ return new RunOne<>(ImmutableList.of(Pair.of(RandomStroll.stroll(0.6F), 2), Pair.of(InteractWith.of(EntityType.PIGLIN, 8, MemoryModuleType.INTERACTION_TARGET, 0.6F, 2), 2), Pair.of(BehaviorBuilder.triggerIf(PiglinAi::doesntSeeAnyPlayerHoldingLovedItem, SetWalkTargetFromLookTarget.create(0.6F, 3)), 2), Pair.of(new DoNothing(30, 60), 1)));
+ }
+
+ private static BehaviorControl<PathfinderMob> avoidRepellent() {
+@@ -286,140 +193,164 @@
+ }
+
+ private static BehaviorControl<Piglin> babyAvoidNemesis() {
+- return CopyMemoryWithExpiry.create(Piglin::isBaby, MemoryModuleType.NEAREST_VISIBLE_NEMESIS, MemoryModuleType.AVOID_TARGET, BABY_AVOID_NEMESIS_DURATION);
++ return CopyMemoryWithExpiry.create(Piglin::isBaby, MemoryModuleType.NEAREST_VISIBLE_NEMESIS, MemoryModuleType.AVOID_TARGET, PiglinAi.BABY_AVOID_NEMESIS_DURATION);
+ }
+
+ private static BehaviorControl<Piglin> avoidZombified() {
+- return CopyMemoryWithExpiry.create(
+- PiglinAi::isNearZombified, MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED, MemoryModuleType.AVOID_TARGET, AVOID_ZOMBIFIED_DURATION
+- );
++ return CopyMemoryWithExpiry.create(PiglinAi::isNearZombified, MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED, MemoryModuleType.AVOID_TARGET, PiglinAi.AVOID_ZOMBIFIED_DURATION);
+ }
+
+ protected static void updateActivity(Piglin piglin) {
+- Brain<Piglin> brain = piglin.getBrain();
+- Activity activity = brain.getActiveNonCoreActivity().orElse(null);
+- brain.setActiveActivityToFirstValid(
+- ImmutableList.of(Activity.ADMIRE_ITEM, Activity.FIGHT, Activity.AVOID, Activity.CELEBRATE, Activity.RIDE, Activity.IDLE)
+- );
+- Activity activity1 = brain.getActiveNonCoreActivity().orElse(null);
++ Brain<Piglin> behaviorcontroller = piglin.getBrain();
++ Activity activity = (Activity) behaviorcontroller.getActiveNonCoreActivity().orElse(null); // CraftBukkit - decompile error
++
++ behaviorcontroller.setActiveActivityToFirstValid(ImmutableList.of(Activity.ADMIRE_ITEM, Activity.FIGHT, Activity.AVOID, Activity.CELEBRATE, Activity.RIDE, Activity.IDLE));
++ Activity activity1 = (Activity) behaviorcontroller.getActiveNonCoreActivity().orElse(null); // CraftBukkit - decompile error
++
+ if (activity != activity1) {
+- getSoundForCurrentActivity(piglin).ifPresent(piglin::playSoundEvent);
++ Optional<SoundEvent> optional = getSoundForCurrentActivity(piglin); // CraftBukkit - decompile error
++
++ Objects.requireNonNull(piglin);
++ optional.ifPresent(piglin::playSoundEvent);
+ }
+
+- piglin.setAggressive(brain.hasMemoryValue(MemoryModuleType.ATTACK_TARGET));
+- if (!brain.hasMemoryValue(MemoryModuleType.RIDE_TARGET) && isBabyRidingBaby(piglin)) {
++ piglin.setAggressive(behaviorcontroller.hasMemoryValue(MemoryModuleType.ATTACK_TARGET));
++ if (!behaviorcontroller.hasMemoryValue(MemoryModuleType.RIDE_TARGET) && isBabyRidingBaby(piglin)) {
+ piglin.stopRiding();
+ }
+
+- if (!brain.hasMemoryValue(MemoryModuleType.CELEBRATE_LOCATION)) {
+- brain.eraseMemory(MemoryModuleType.DANCING);
++ if (!behaviorcontroller.hasMemoryValue(MemoryModuleType.CELEBRATE_LOCATION)) {
++ behaviorcontroller.eraseMemory(MemoryModuleType.DANCING);
+ }
+
+- piglin.setDancing(brain.hasMemoryValue(MemoryModuleType.DANCING));
++ piglin.setDancing(behaviorcontroller.hasMemoryValue(MemoryModuleType.DANCING));
+ }
+
+ private static boolean isBabyRidingBaby(Piglin passenger) {
+ if (!passenger.isBaby()) {
+ return false;
+ } else {
+- Entity vehicle = passenger.getVehicle();
+- return vehicle instanceof Piglin && ((Piglin)vehicle).isBaby() || vehicle instanceof Hoglin && ((Hoglin)vehicle).isBaby();
++ Entity entity = passenger.getVehicle();
++
++ return entity instanceof Piglin && ((Piglin) entity).isBaby() || entity instanceof Hoglin && ((Hoglin) entity).isBaby();
+ }
+ }
+
+ protected static void pickUpItem(Piglin piglin, ItemEntity itemEntity) {
+ stopWalking(piglin);
+- ItemStack item;
+- if (itemEntity.getItem().is(Items.GOLD_NUGGET)) {
++ ItemStack itemstack;
++
++ // CraftBukkit start
++ if (itemEntity.getItem().is(Items.GOLD_NUGGET) && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(piglin, itemEntity, 0, false).isCancelled()) {
+ piglin.take(itemEntity, itemEntity.getItem().getCount());
+- item = itemEntity.getItem();
++ itemstack = itemEntity.getItem();
+ itemEntity.discard();
+- } else {
++ } else if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(piglin, itemEntity, itemEntity.getItem().getCount() - 1, false).isCancelled()) {
+ piglin.take(itemEntity, 1);
+- item = removeOneItemFromItemEntity(itemEntity);
++ itemstack = removeOneItemFromItemEntity(itemEntity);
++ } else {
++ return;
+ }
++ // CraftBukkit end
+
+- if (isLovedItem(item)) {
++ if (isLovedItem(itemstack, piglin)) { // CraftBukkit - Changes to allow for custom payment in bartering
+ piglin.getBrain().eraseMemory(MemoryModuleType.TIME_TRYING_TO_REACH_ADMIRE_ITEM);
+- holdInOffhand(piglin, item);
++ holdInOffhand(piglin, itemstack);
+ admireGoldItem(piglin);
+- } else if (isFood(item) && !hasEatenRecently(piglin)) {
++ } else if (isFood(itemstack) && !hasEatenRecently(piglin)) {
+ eat(piglin);
+ } else {
+- boolean flag = !piglin.equipItemIfPossible(item).equals(ItemStack.EMPTY);
++ boolean flag = !piglin.equipItemIfPossible(itemstack, itemEntity).equals(ItemStack.EMPTY); // CraftBukkit
++
+ if (!flag) {
+- putInInventory(piglin, item);
++ putInInventory(piglin, itemstack);
+ }
+ }
+ }
+
+ private static void holdInOffhand(Piglin piglin, ItemStack stack) {
+ if (isHoldingItemInOffHand(piglin)) {
+- piglin.spawnAtLocation(piglin.getItemInHand(InteractionHand.OFF_HAND));
++ piglin.spawnAtLocation(piglin.getItemInHand(EnumHand.OFF_HAND));
+ }
+
+ piglin.holdInOffHand(stack);
+ }
+
+ private static ItemStack removeOneItemFromItemEntity(ItemEntity itemEntity) {
+- ItemStack item = itemEntity.getItem();
+- ItemStack itemStack = item.split(1);
+- if (item.isEmpty()) {
++ ItemStack itemstack = itemEntity.getItem();
++ ItemStack itemstack1 = itemstack.split(1);
++
++ if (itemstack.isEmpty()) {
+ itemEntity.discard();
+ } else {
+- itemEntity.setItem(item);
++ itemEntity.setItem(itemstack);
+ }
+
+- return itemStack;
++ return itemstack1;
+ }
+
+ protected static void stopHoldingOffHandItem(Piglin piglin, boolean shouldBarter) {
+- ItemStack itemInHand = piglin.getItemInHand(InteractionHand.OFF_HAND);
+- piglin.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY);
++ ItemStack itemstack = piglin.getItemInHand(EnumHand.OFF_HAND);
++
++ piglin.setItemInHand(EnumHand.OFF_HAND, ItemStack.EMPTY);
++ boolean flag1;
++
+ if (piglin.isAdult()) {
+- boolean isBarterCurrency = isBarterCurrency(itemInHand);
+- if (shouldBarter && isBarterCurrency) {
+- throwItems(piglin, getBarterResponseItems(piglin));
+- } else if (!isBarterCurrency) {
+- boolean flag = !piglin.equipItemIfPossible(itemInHand).isEmpty();
+- if (!flag) {
+- putInInventory(piglin, itemInHand);
++ flag1 = isBarterCurrency(itemstack, piglin); // CraftBukkit - Changes to allow custom payment for bartering
++ if (shouldBarter && flag1) {
++ // CraftBukkit start
++ PiglinBarterEvent event = CraftEventFactory.callPiglinBarterEvent(piglin, getBarterResponseItems(piglin), itemstack);
++ if (!event.isCancelled()) {
++ throwItems(piglin, event.getOutcome().stream().map(CraftItemStack::asNMSCopy).collect(Collectors.toList()));
+ }
++ // CraftBukkit end
++ } else if (!flag1) {
++ boolean flag2 = !piglin.equipItemIfPossible(itemstack).isEmpty();
++
++ if (!flag2) {
++ putInInventory(piglin, itemstack);
++ }
+ }
+ } else {
+- boolean isBarterCurrency = !piglin.equipItemIfPossible(itemInHand).isEmpty();
+- if (!isBarterCurrency) {
+- ItemStack mainHandItem = piglin.getMainHandItem();
+- if (isLovedItem(mainHandItem)) {
+- putInInventory(piglin, mainHandItem);
++ flag1 = !piglin.equipItemIfPossible(itemstack).isEmpty();
++ if (!flag1) {
++ ItemStack itemstack1 = piglin.getMainHandItem();
++
++ if (isLovedItem(itemstack1, piglin)) { // CraftBukkit - Changes to allow for custom payment in bartering
++ putInInventory(piglin, itemstack1);
+ } else {
+- throwItems(piglin, Collections.singletonList(mainHandItem));
++ throwItems(piglin, Collections.singletonList(itemstack1));
+ }
+
+- piglin.holdInMainHand(itemInHand);
++ piglin.holdInMainHand(itemstack);
+ }
+ }
++
+ }
+
+ protected static void cancelAdmiring(Piglin piglin) {
+ if (isAdmiringItem(piglin) && !piglin.getOffhandItem().isEmpty()) {
+ piglin.spawnAtLocation(piglin.getOffhandItem());
+- piglin.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY);
++ piglin.setItemInHand(EnumHand.OFF_HAND, ItemStack.EMPTY);
+ }
++
+ }
+
+ private static void putInInventory(Piglin piglin, ItemStack stack) {
+- ItemStack itemStack = piglin.addToInventory(stack);
+- throwItemsTowardRandomPos(piglin, Collections.singletonList(itemStack));
++ ItemStack itemstack1 = piglin.addToInventory(stack);
++
++ throwItemsTowardRandomPos(piglin, Collections.singletonList(itemstack1));
+ }
+
+ private static void throwItems(Piglin pilgin, List<ItemStack> stacks) {
+- Optional<Player> memory = pilgin.getBrain().getMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER);
+- if (memory.isPresent()) {
+- throwItemsTowardPlayer(pilgin, memory.get(), stacks);
++ Optional<Player> optional = pilgin.getBrain().getMemory(MemoryModuleType.NEAREST_VISIBLE_PLAYER);
++
++ if (optional.isPresent()) {
++ throwItemsTowardPlayer(pilgin, (Player) optional.get(), stacks);
+ } else {
+ throwItemsTowardRandomPos(pilgin, stacks);
+ }
++
+ }
+
+ private static void throwItemsTowardRandomPos(Piglin piglin, List<ItemStack> stacks) {
+@@ -432,24 +363,27 @@
+
+ private static void throwItemsTowardPos(Piglin piglin, List<ItemStack> stacks, Vec3 pos) {
+ if (!stacks.isEmpty()) {
+- piglin.swing(InteractionHand.OFF_HAND);
++ piglin.swing(EnumHand.OFF_HAND);
++ Iterator iterator = stacks.iterator();
+
+- for (ItemStack itemStack : stacks) {
+- BehaviorUtils.throwItem(piglin, itemStack, pos.add(0.0, 1.0, 0.0));
++ while (iterator.hasNext()) {
++ ItemStack itemstack = (ItemStack) iterator.next();
++
++ BehaviorUtils.throwItem(piglin, itemstack, pos.add(0.0D, 1.0D, 0.0D));
+ }
+ }
++
+ }
+
+ private static List<ItemStack> getBarterResponseItems(Piglin piglin) {
+- LootTable lootTable = piglin.level().getServer().getLootData().getLootTable(BuiltInLootTables.PIGLIN_BARTERING);
+- List<ItemStack> randomItems = lootTable.getRandomItems(
+- new LootParams.Builder((ServerLevel)piglin.level()).withParameter(LootContextParams.THIS_ENTITY, piglin).create(LootContextParamSets.PIGLIN_BARTER)
+- );
+- return randomItems;
++ LootTable loottable = piglin.level().getServer().getLootData().getLootTable(BuiltInLootTables.PIGLIN_BARTERING);
++ List<ItemStack> list = loottable.getRandomItems((new LootParams.Builder((ServerLevel) piglin.level())).withParameter(LootContextParams.THIS_ENTITY, piglin).create(LootContextParamSets.PIGLIN_BARTER));
++
++ return list;
+ }
+
+ private static boolean wantsToDance(LivingEntity piglin, LivingEntity target) {
+- return target.getType() == EntityType.HOGLIN && RandomSource.create(piglin.level().getGameTime()).nextFloat() < 0.1F;
++ return target.getType() != EntityType.HOGLIN ? false : RandomSource.create(piglin.level().getGameTime()).nextFloat() < 0.1F;
+ }
+
+ protected static boolean wantsToPickup(Piglin piglin, ItemStack stack) {
+@@ -459,86 +393,107 @@
+ return false;
+ } else if (isAdmiringDisabled(piglin) && piglin.getBrain().hasMemoryValue(MemoryModuleType.ATTACK_TARGET)) {
+ return false;
+- } else if (isBarterCurrency(stack)) {
++ } else if (isBarterCurrency(stack, piglin)) { // CraftBukkit
+ return isNotHoldingLovedItemInOffHand(piglin);
+ } else {
+- boolean canAddToInventory = piglin.canAddToInventory(stack);
+- if (stack.is(Items.GOLD_NUGGET)) {
+- return canAddToInventory;
+- } else if (isFood(stack)) {
+- return !hasEatenRecently(piglin) && canAddToInventory;
+- } else {
+- return !isLovedItem(stack) ? piglin.canReplaceCurrentItem(stack) : isNotHoldingLovedItemInOffHand(piglin) && canAddToInventory;
+- }
++ boolean flag = piglin.canAddToInventory(stack);
++
++ return stack.is(Items.GOLD_NUGGET) ? flag : (isFood(stack) ? !hasEatenRecently(piglin) && flag : (!isLovedItem(stack) ? piglin.canReplaceCurrentItem(stack) : isNotHoldingLovedItemInOffHand(piglin) && flag));
+ }
+ }
+
++ // CraftBukkit start - Added method to allow checking for custom payment items
++ protected static boolean isLovedItem(ItemStack itemstack, Piglin piglin) {
++ return isLovedItem(itemstack) || (piglin.interestItems.contains(itemstack.getItem()) || piglin.allowedBarterItems.contains(itemstack.getItem()));
++ }
++ // CraftBukkit end
++
+ protected static boolean isLovedItem(ItemStack item) {
+ return item.is(ItemTags.PIGLIN_LOVED);
+ }
+
+ private static boolean wantsToStopRiding(Piglin piglin, Entity vehicle) {
+- return vehicle instanceof Mob mob
+- && (!mob.isBaby() || !mob.isAlive() || wasHurtRecently(piglin) || wasHurtRecently(mob) || mob instanceof Piglin && mob.getVehicle() == null);
++ if (!(vehicle instanceof Mob)) {
++ return false;
++ } else {
++ Mob entityinsentient = (Mob) vehicle;
++
++ return !entityinsentient.isBaby() || !entityinsentient.isAlive() || wasHurtRecently(piglin) || wasHurtRecently(entityinsentient) || entityinsentient instanceof Piglin && entityinsentient.getVehicle() == null;
++ }
+ }
+
+ private static boolean isNearestValidAttackTarget(Piglin piglin, LivingEntity target) {
+- return findNearestValidAttackTarget(piglin).filter(entity -> entity == target).isPresent();
++ return findNearestValidAttackTarget(piglin).filter((entityliving1) -> {
++ return entityliving1 == target;
++ }).isPresent();
+ }
+
+ private static boolean isNearZombified(Piglin piglin) {
+- Brain<Piglin> brain = piglin.getBrain();
+- if (brain.hasMemoryValue(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED)) {
+- LivingEntity livingEntity = brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED).get();
+- return piglin.closerThan(livingEntity, 6.0);
++ Brain<Piglin> behaviorcontroller = piglin.getBrain();
++
++ if (behaviorcontroller.hasMemoryValue(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED)) {
++ LivingEntity entityliving = (LivingEntity) behaviorcontroller.getMemory(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED).get();
++
++ return piglin.closerThan(entityliving, 6.0D);
+ } else {
+ return false;
+ }
+ }
+
+ private static Optional<? extends LivingEntity> findNearestValidAttackTarget(Piglin piglin) {
+- Brain<Piglin> brain = piglin.getBrain();
++ Brain<Piglin> behaviorcontroller = piglin.getBrain();
++
+ if (isNearZombified(piglin)) {
+ return Optional.empty();
+ } else {
+- Optional<LivingEntity> livingEntityFromUUIDMemory = BehaviorUtils.getLivingEntityFromUUIDMemory(piglin, MemoryModuleType.ANGRY_AT);
+- if (livingEntityFromUUIDMemory.isPresent() && Sensor.isEntityAttackableIgnoringLineOfSight(piglin, livingEntityFromUUIDMemory.get())) {
+- return livingEntityFromUUIDMemory;
++ Optional<LivingEntity> optional = BehaviorUtils.getLivingEntityFromUUIDMemory(piglin, MemoryModuleType.ANGRY_AT);
++
++ if (optional.isPresent() && Sensor.isEntityAttackableIgnoringLineOfSight(piglin, (LivingEntity) optional.get())) {
++ return optional;
+ } else {
+- if (brain.hasMemoryValue(MemoryModuleType.UNIVERSAL_ANGER)) {
+- Optional<Player> memory = brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER);
+- if (memory.isPresent()) {
+- return memory;
++ Optional optional1;
++
++ if (behaviorcontroller.hasMemoryValue(MemoryModuleType.UNIVERSAL_ANGER)) {
++ optional1 = behaviorcontroller.getMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER);
++ if (optional1.isPresent()) {
++ return optional1;
+ }
+ }
+
+- Optional<Mob> memory = brain.getMemory(MemoryModuleType.NEAREST_VISIBLE_NEMESIS);
+- if (memory.isPresent()) {
+- return memory;
++ optional1 = behaviorcontroller.getMemory(MemoryModuleType.NEAREST_VISIBLE_NEMESIS);
++ if (optional1.isPresent()) {
++ return optional1;
+ } else {
+- Optional<Player> memory1 = brain.getMemory(MemoryModuleType.NEAREST_TARGETABLE_PLAYER_NOT_WEARING_GOLD);
+- return memory1.isPresent() && Sensor.isEntityAttackable(piglin, memory1.get()) ? memory1 : Optional.empty();
++ Optional<Player> optional2 = behaviorcontroller.getMemory(MemoryModuleType.NEAREST_TARGETABLE_PLAYER_NOT_WEARING_GOLD);
++
++ return optional2.isPresent() && Sensor.isEntityAttackable(piglin, (LivingEntity) optional2.get()) ? optional2 : Optional.empty();
+ }
+ }
+ }
+ }
+
+ public static void angerNearbyPiglins(Player player, boolean angerOnlyIfCanSee) {
+- List<Piglin> entitiesOfClass = player.level().getEntitiesOfClass(Piglin.class, player.getBoundingBox().inflate(16.0));
+- entitiesOfClass.stream().filter(PiglinAi::isIdle).filter(piglin -> !angerOnlyIfCanSee || BehaviorUtils.canSee(piglin, player)).forEach(piglin -> {
+- if (piglin.level().getGameRules().getBoolean(GameRules.RULE_UNIVERSAL_ANGER)) {
+- setAngerTargetToNearestTargetablePlayerIfFound(piglin, player);
++ List<Piglin> list = player.level().getEntitiesOfClass(Piglin.class, player.getBoundingBox().inflate(16.0D));
++
++ list.stream().filter(PiglinAi::isIdle).filter((entitypiglin) -> {
++ return !angerOnlyIfCanSee || BehaviorUtils.canSee(entitypiglin, player);
++ }).forEach((entitypiglin) -> {
++ if (entitypiglin.level().getGameRules().getBoolean(GameRules.RULE_UNIVERSAL_ANGER)) {
++ setAngerTargetToNearestTargetablePlayerIfFound(entitypiglin, player);
+ } else {
+- setAngerTarget(piglin, player);
++ setAngerTarget(entitypiglin, player);
+ }
++
+ });
+ }
+
+- public static InteractionResult mobInteract(Piglin piglin, Player player, InteractionHand hand) {
+- ItemStack itemInHand = player.getItemInHand(hand);
+- if (canAdmire(piglin, itemInHand)) {
+- ItemStack itemStack = itemInHand.split(1);
+- holdInOffhand(piglin, itemStack);
++ public static InteractionResult mobInteract(Piglin piglin, Player player, EnumHand hand) {
++ ItemStack itemstack = player.getItemInHand(hand);
++
++ if (canAdmire(piglin, itemstack)) {
++ ItemStack itemstack1 = itemstack.split(1);
++
++ holdInOffhand(piglin, itemstack1);
+ admireGoldItem(piglin);
+ stopWalking(piglin);
+ return InteractionResult.CONSUME;
+@@ -548,7 +503,7 @@
+ }
+
+ protected static boolean canAdmire(Piglin piglin, ItemStack stack) {
+- return !isAdmiringDisabled(piglin) && !isAdmiringItem(piglin) && piglin.isAdult() && isBarterCurrency(stack);
++ return !isAdmiringDisabled(piglin) && !isAdmiringItem(piglin) && piglin.isAdult() && isBarterCurrency(stack, piglin); // CraftBukkit
+ }
+
+ protected static void wasHurtBy(Piglin piglin, LivingEntity target) {
+@@ -557,24 +512,27 @@
+ stopHoldingOffHandItem(piglin, false);
+ }
+
+- Brain<Piglin> brain = piglin.getBrain();
+- brain.eraseMemory(MemoryModuleType.CELEBRATE_LOCATION);
+- brain.eraseMemory(MemoryModuleType.DANCING);
+- brain.eraseMemory(MemoryModuleType.ADMIRING_ITEM);
++ Brain<Piglin> behaviorcontroller = piglin.getBrain();
++
++ behaviorcontroller.eraseMemory(MemoryModuleType.CELEBRATE_LOCATION);
++ behaviorcontroller.eraseMemory(MemoryModuleType.DANCING);
++ behaviorcontroller.eraseMemory(MemoryModuleType.ADMIRING_ITEM);
+ if (target instanceof Player) {
+- brain.setMemoryWithExpiry(MemoryModuleType.ADMIRING_DISABLED, true, 400L);
++ behaviorcontroller.setMemoryWithExpiry(MemoryModuleType.ADMIRING_DISABLED, true, 400L);
+ }
+
+- getAvoidTarget(piglin).ifPresent(entity -> {
+- if (entity.getType() != target.getType()) {
+- brain.eraseMemory(MemoryModuleType.AVOID_TARGET);
++ getAvoidTarget(piglin).ifPresent((entityliving1) -> {
++ if (entityliving1.getType() != target.getType()) {
++ behaviorcontroller.eraseMemory(MemoryModuleType.AVOID_TARGET);
+ }
++
+ });
+ if (piglin.isBaby()) {
+- brain.setMemoryWithExpiry(MemoryModuleType.AVOID_TARGET, target, 100L);
++ behaviorcontroller.setMemoryWithExpiry(MemoryModuleType.AVOID_TARGET, target, 100L);
+ if (Sensor.isEntityAttackableIgnoringLineOfSight(piglin, target)) {
+ broadcastAngerTarget(piglin, target);
+ }
++
+ } else if (target.getType() == EntityType.HOGLIN && hoglinsOutnumberPiglins(piglin)) {
+ setAvoidTargetAndDontHuntForAWhile(piglin, target);
+ broadcastRetreat(piglin, target);
+@@ -587,7 +545,7 @@
+ protected static void maybeRetaliate(AbstractPiglin piglin, LivingEntity target) {
+ if (!piglin.getBrain().isActive(Activity.AVOID)) {
+ if (Sensor.isEntityAttackableIgnoringLineOfSight(piglin, target)) {
+- if (!BehaviorUtils.isOtherTargetMuchFurtherAwayThanCurrentAttackTarget(piglin, target, 4.0)) {
++ if (!BehaviorUtils.isOtherTargetMuchFurtherAwayThanCurrentAttackTarget(piglin, target, 4.0D)) {
+ if (target.getType() == EntityType.PLAYER && piglin.level().getGameRules().getBoolean(GameRules.RULE_UNIVERSAL_ANGER)) {
+ setAngerTargetToNearestTargetablePlayerIfFound(piglin, target);
+ broadcastUniversalAnger(piglin);
+@@ -595,55 +553,53 @@
+ setAngerTarget(piglin, target);
+ broadcastAngerTarget(piglin, target);
+ }
++
+ }
+ }
+ }
+ }
+
+ public static Optional<SoundEvent> getSoundForCurrentActivity(Piglin piglin) {
+- return piglin.getBrain().getActiveNonCoreActivity().map(activity -> getSoundForActivity(piglin, activity));
++ return piglin.getBrain().getActiveNonCoreActivity().map((activity) -> {
++ return getSoundForActivity(piglin, activity);
++ });
+ }
+
+ private static SoundEvent getSoundForActivity(Piglin piglin, Activity activity) {
+- if (activity == Activity.FIGHT) {
+- return SoundEvents.PIGLIN_ANGRY;
+- } else if (piglin.isConverting()) {
+- return SoundEvents.PIGLIN_RETREAT;
+- } else if (activity == Activity.AVOID && isNearAvoidTarget(piglin)) {
+- return SoundEvents.PIGLIN_RETREAT;
+- } else if (activity == Activity.ADMIRE_ITEM) {
+- return SoundEvents.PIGLIN_ADMIRING_ITEM;
+- } else if (activity == Activity.CELEBRATE) {
+- return SoundEvents.PIGLIN_CELEBRATE;
+- } else if (seesPlayerHoldingLovedItem(piglin)) {
+- return SoundEvents.PIGLIN_JEALOUS;
+- } else {
+- return isNearRepellent(piglin) ? SoundEvents.PIGLIN_RETREAT : SoundEvents.PIGLIN_AMBIENT;
+- }
++ return activity == Activity.FIGHT ? SoundEvents.PIGLIN_ANGRY : (piglin.isConverting() ? SoundEvents.PIGLIN_RETREAT : (activity == Activity.AVOID && isNearAvoidTarget(piglin) ? SoundEvents.PIGLIN_RETREAT : (activity == Activity.ADMIRE_ITEM ? SoundEvents.PIGLIN_ADMIRING_ITEM : (activity == Activity.CELEBRATE ? SoundEvents.PIGLIN_CELEBRATE : (seesPlayerHoldingLovedItem(piglin) ? SoundEvents.PIGLIN_JEALOUS : (isNearRepellent(piglin) ? SoundEvents.PIGLIN_RETREAT : SoundEvents.PIGLIN_AMBIENT))))));
+ }
+
+ private static boolean isNearAvoidTarget(Piglin piglin) {
+- Brain<Piglin> brain = piglin.getBrain();
+- return brain.hasMemoryValue(MemoryModuleType.AVOID_TARGET) && brain.getMemory(MemoryModuleType.AVOID_TARGET).get().closerThan(piglin, 12.0);
++ Brain<Piglin> behaviorcontroller = piglin.getBrain();
++
++ return !behaviorcontroller.hasMemoryValue(MemoryModuleType.AVOID_TARGET) ? false : ((LivingEntity) behaviorcontroller.getMemory(MemoryModuleType.AVOID_TARGET).get()).closerThan(piglin, 12.0D);
+ }
+
+ protected static List<AbstractPiglin> getVisibleAdultPiglins(Piglin piglin) {
+- return piglin.getBrain().getMemory(MemoryModuleType.NEAREST_VISIBLE_ADULT_PIGLINS).orElse(ImmutableList.of());
++ return (List) piglin.getBrain().getMemory(MemoryModuleType.NEAREST_VISIBLE_ADULT_PIGLINS).orElse(ImmutableList.of());
+ }
+
+ private static List<AbstractPiglin> getAdultPiglins(AbstractPiglin piglin) {
+- return piglin.getBrain().getMemory(MemoryModuleType.NEARBY_ADULT_PIGLINS).orElse(ImmutableList.of());
++ return (List) piglin.getBrain().getMemory(MemoryModuleType.NEARBY_ADULT_PIGLINS).orElse(ImmutableList.of());
+ }
+
+ public static boolean isWearingGold(LivingEntity livingEntity) {
+- for (ItemStack itemStack : livingEntity.getArmorSlots()) {
+- Item item = itemStack.getItem();
+- if (item instanceof ArmorItem && ((ArmorItem)item).getMaterial() == ArmorMaterials.GOLD) {
+- return true;
++ Iterable<ItemStack> iterable = livingEntity.getArmorSlots();
++ Iterator iterator = iterable.iterator();
++
++ Item item;
++
++ do {
++ if (!iterator.hasNext()) {
++ return false;
+ }
+- }
+
+- return false;
++ ItemStack itemstack = (ItemStack) iterator.next();
++
++ item = itemstack.getItem();
++ } while (!(item instanceof ArmorItem) || ((ArmorItem) item).getMaterial() != ArmorMaterials.GOLD);
++
++ return true;
+ }
+
+ private static void stopWalking(Piglin piglin) {
+@@ -652,25 +608,27 @@
+ }
+
+ private static BehaviorControl<LivingEntity> babySometimesRideBabyHoglin() {
+- SetEntityLookTargetSometimes.Ticker ticker = new SetEntityLookTargetSometimes.Ticker(RIDE_START_INTERVAL);
+- return CopyMemoryWithExpiry.create(
+- livingEntity -> livingEntity.isBaby() && ticker.tickDownAndCheck(livingEntity.level().random),
+- MemoryModuleType.NEAREST_VISIBLE_BABY_HOGLIN,
+- MemoryModuleType.RIDE_TARGET,
+- RIDE_DURATION
+- );
++ SetEntityLookTargetSometimes.Ticker setentitylooktargetsometimes_a = new SetEntityLookTargetSometimes.Ticker(PiglinAi.RIDE_START_INTERVAL);
++
++ return CopyMemoryWithExpiry.create((entityliving) -> {
++ return entityliving.isBaby() && setentitylooktargetsometimes_a.tickDownAndCheck(entityliving.level().random);
++ }, MemoryModuleType.NEAREST_VISIBLE_BABY_HOGLIN, MemoryModuleType.RIDE_TARGET, PiglinAi.RIDE_DURATION);
+ }
+
+ protected static void broadcastAngerTarget(AbstractPiglin piglin, LivingEntity target) {
+- getAdultPiglins(piglin).forEach(adultPiglin -> {
+- if (target.getType() != EntityType.HOGLIN || adultPiglin.canHunt() && ((Hoglin)target).canBeHunted()) {
+- setAngerTargetIfCloserThanCurrent(adultPiglin, target);
++ getAdultPiglins(piglin).forEach((entitypiglinabstract1) -> {
++ if (target.getType() != EntityType.HOGLIN || entitypiglinabstract1.canHunt() && ((Hoglin) target).canBeHunted()) {
++ setAngerTargetIfCloserThanCurrent(entitypiglinabstract1, target);
+ }
+ });
+ }
+
+ protected static void broadcastUniversalAnger(AbstractPiglin piglin) {
+- getAdultPiglins(piglin).forEach(adultPiglin -> getNearestVisibleTargetablePlayer(adultPiglin).ifPresent(player -> setAngerTarget(adultPiglin, player)));
++ getAdultPiglins(piglin).forEach((entitypiglinabstract1) -> {
++ getNearestVisibleTargetablePlayer(entitypiglinabstract1).ifPresent((entityhuman) -> {
++ setAngerTarget(entitypiglinabstract1, entityhuman);
++ });
++ });
+ }
+
+ protected static void setAngerTarget(AbstractPiglin piglin, LivingEntity target) {
+@@ -684,23 +642,27 @@
+ if (target.getType() == EntityType.PLAYER && piglin.level().getGameRules().getBoolean(GameRules.RULE_UNIVERSAL_ANGER)) {
+ piglin.getBrain().setMemoryWithExpiry(MemoryModuleType.UNIVERSAL_ANGER, true, 600L);
+ }
++
+ }
+ }
+
+ private static void setAngerTargetToNearestTargetablePlayerIfFound(AbstractPiglin piglin, LivingEntity currentTarget) {
+- Optional<Player> nearestVisibleTargetablePlayer = getNearestVisibleTargetablePlayer(piglin);
+- if (nearestVisibleTargetablePlayer.isPresent()) {
+- setAngerTarget(piglin, nearestVisibleTargetablePlayer.get());
++ Optional<Player> optional = getNearestVisibleTargetablePlayer(piglin);
++
++ if (optional.isPresent()) {
++ setAngerTarget(piglin, (LivingEntity) optional.get());
+ } else {
+ setAngerTarget(piglin, currentTarget);
+ }
++
+ }
+
+ private static void setAngerTargetIfCloserThanCurrent(AbstractPiglin piglin, LivingEntity currentTarget) {
+- Optional<LivingEntity> angerTarget = getAngerTarget(piglin);
+- LivingEntity nearestTarget = BehaviorUtils.getNearestTarget(piglin, angerTarget, currentTarget);
+- if (!angerTarget.isPresent() || angerTarget.get() != nearestTarget) {
+- setAngerTarget(piglin, nearestTarget);
++ Optional<LivingEntity> optional = getAngerTarget(piglin);
++ LivingEntity entityliving1 = BehaviorUtils.getNearestTarget(piglin, optional, currentTarget);
++
++ if (!optional.isPresent() || optional.get() != entityliving1) {
++ setAngerTarget(piglin, entityliving1);
+ }
+ }
+
+@@ -713,35 +675,35 @@
+ }
+
+ public static Optional<Player> getNearestVisibleTargetablePlayer(AbstractPiglin piglin) {
+- return piglin.getBrain().hasMemoryValue(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER)
+- ? piglin.getBrain().getMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER)
+- : Optional.empty();
++ return piglin.getBrain().hasMemoryValue(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER) ? piglin.getBrain().getMemory(MemoryModuleType.NEAREST_VISIBLE_ATTACKABLE_PLAYER) : Optional.empty();
+ }
+
+ private static void broadcastRetreat(Piglin piglin, LivingEntity target) {
+- getVisibleAdultPiglins(piglin)
+- .stream()
+- .filter(adultPiglin -> adultPiglin instanceof Piglin)
+- .forEach(adultPiglin -> retreatFromNearestTarget((Piglin)adultPiglin, target));
++ getVisibleAdultPiglins(piglin).stream().filter((entitypiglinabstract) -> {
++ return entitypiglinabstract instanceof Piglin;
++ }).forEach((entitypiglinabstract) -> {
++ retreatFromNearestTarget((Piglin) entitypiglinabstract, target);
++ });
+ }
+
+ private static void retreatFromNearestTarget(Piglin piglin, LivingEntity target) {
+- Brain<Piglin> brain = piglin.getBrain();
+- LivingEntity livingEntity = BehaviorUtils.getNearestTarget(piglin, brain.getMemory(MemoryModuleType.AVOID_TARGET), target);
+- livingEntity = BehaviorUtils.getNearestTarget(piglin, brain.getMemory(MemoryModuleType.ATTACK_TARGET), livingEntity);
+- setAvoidTargetAndDontHuntForAWhile(piglin, livingEntity);
++ Brain<Piglin> behaviorcontroller = piglin.getBrain();
++ LivingEntity entityliving1 = BehaviorUtils.getNearestTarget(piglin, behaviorcontroller.getMemory(MemoryModuleType.AVOID_TARGET), target);
++
++ entityliving1 = BehaviorUtils.getNearestTarget(piglin, behaviorcontroller.getMemory(MemoryModuleType.ATTACK_TARGET), entityliving1);
++ setAvoidTargetAndDontHuntForAWhile(piglin, entityliving1);
+ }
+
+ private static boolean wantsToStopFleeing(Piglin piglin) {
+- Brain<Piglin> brain = piglin.getBrain();
+- if (!brain.hasMemoryValue(MemoryModuleType.AVOID_TARGET)) {
++ Brain<Piglin> behaviorcontroller = piglin.getBrain();
++
++ if (!behaviorcontroller.hasMemoryValue(MemoryModuleType.AVOID_TARGET)) {
+ return true;
+ } else {
+- LivingEntity livingEntity = brain.getMemory(MemoryModuleType.AVOID_TARGET).get();
+- EntityType<?> type = livingEntity.getType();
+- return type == EntityType.HOGLIN
+- ? piglinsEqualOrOutnumberHoglins(piglin)
+- : isZombified(type) && !brain.isMemoryValue(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED, livingEntity);
++ LivingEntity entityliving = (LivingEntity) behaviorcontroller.getMemory(MemoryModuleType.AVOID_TARGET).get();
++ EntityType<?> entitytypes = entityliving.getType();
++
++ return entitytypes == EntityType.HOGLIN ? piglinsEqualOrOutnumberHoglins(piglin) : (isZombified(entitytypes) ? !behaviorcontroller.isMemoryValue(MemoryModuleType.NEAREST_VISIBLE_ZOMBIFIED, entityliving) : false);
+ }
+ }
+
+@@ -750,21 +712,22 @@
+ }
+
+ private static boolean hoglinsOutnumberPiglins(Piglin piglin) {
+- int i = piglin.getBrain().getMemory(MemoryModuleType.VISIBLE_ADULT_PIGLIN_COUNT).orElse(0) + 1;
+- int i1 = piglin.getBrain().getMemory(MemoryModuleType.VISIBLE_ADULT_HOGLIN_COUNT).orElse(0);
+- return i1 > i;
++ int i = (Integer) piglin.getBrain().getMemory(MemoryModuleType.VISIBLE_ADULT_PIGLIN_COUNT).orElse(0) + 1;
++ int j = (Integer) piglin.getBrain().getMemory(MemoryModuleType.VISIBLE_ADULT_HOGLIN_COUNT).orElse(0);
++
++ return j > i;
+ }
+
+ private static void setAvoidTargetAndDontHuntForAWhile(Piglin piglin, LivingEntity target) {
+ piglin.getBrain().eraseMemory(MemoryModuleType.ANGRY_AT);
+ piglin.getBrain().eraseMemory(MemoryModuleType.ATTACK_TARGET);
+ piglin.getBrain().eraseMemory(MemoryModuleType.WALK_TARGET);
+- piglin.getBrain().setMemoryWithExpiry(MemoryModuleType.AVOID_TARGET, target, (long)RETREAT_DURATION.sample(piglin.level().random));
++ piglin.getBrain().setMemoryWithExpiry(MemoryModuleType.AVOID_TARGET, target, (long) PiglinAi.RETREAT_DURATION.sample(piglin.level().random));
+ dontKillAnyMoreHoglinsForAWhile(piglin);
+ }
+
+ protected static void dontKillAnyMoreHoglinsForAWhile(AbstractPiglin piglin) {
+- piglin.getBrain().setMemoryWithExpiry(MemoryModuleType.HUNTED_RECENTLY, true, (long)TIME_BETWEEN_HUNTS.sample(piglin.level().random));
++ piglin.getBrain().setMemoryWithExpiry(MemoryModuleType.HUNTED_RECENTLY, true, (long) PiglinAi.TIME_BETWEEN_HUNTS.sample(piglin.level().random));
+ }
+
+ private static void eat(Piglin piglin) {
+@@ -772,8 +735,9 @@
+ }
+
+ private static Vec3 getRandomNearbyPos(Piglin piglin) {
+- Vec3 pos = LandRandomPos.getPos(piglin, 4, 2);
+- return pos == null ? piglin.position() : pos;
++ Vec3 vec3d = LandRandomPos.getPos(piglin, 4, 2);
++
++ return vec3d == null ? piglin.position() : vec3d;
+ }
+
+ private static boolean hasEatenRecently(Piglin piglin) {
+@@ -796,8 +760,14 @@
+ return piglin.getBrain().hasMemoryValue(MemoryModuleType.ADMIRING_ITEM);
+ }
+
++ // CraftBukkit start - Changes to allow custom payment for bartering
++ private static boolean isBarterCurrency(ItemStack itemstack, Piglin piglin) {
++ return isBarterCurrency(itemstack) || piglin.allowedBarterItems.contains(itemstack.getItem());
++ }
++ // CraftBukkit end
++
+ private static boolean isBarterCurrency(ItemStack stack) {
+- return stack.is(BARTERING_ITEM);
++ return stack.is(PiglinAi.BARTERING_ITEM);
+ }
+
+ private static boolean isFood(ItemStack stack) {
+@@ -833,7 +803,7 @@
+ }
+
+ private static boolean isNotHoldingLovedItemInOffHand(Piglin piglin) {
+- return piglin.getOffhandItem().isEmpty() || !isLovedItem(piglin.getOffhandItem());
++ return piglin.getOffhandItem().isEmpty() || !isLovedItem(piglin.getOffhandItem(), piglin); // CraftBukkit - Changes to allow custom payment for bartering
+ }
+
+ public static boolean isZombified(EntityType<?> entityType) {
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/warden/Warden.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/warden/Warden.java.patch
new file mode 100644
index 0000000000..7cb6a96030
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/monster/warden/Warden.java.patch
@@ -0,0 +1,615 @@
+--- a/net/minecraft/world/entity/monster/warden/Warden.java
++++ b/net/minecraft/world/entity/monster/warden/Warden.java
+@@ -2,8 +2,10 @@
+
+ import com.google.common.annotations.VisibleForTesting;
+ import com.mojang.logging.LogUtils;
++import com.mojang.serialization.DataResult;
+ import com.mojang.serialization.Dynamic;
+ import java.util.Collections;
++import java.util.Objects;
+ import java.util.Optional;
+ import java.util.function.BiConsumer;
+ import javax.annotation.Nullable;
+@@ -12,7 +14,6 @@
+ import net.minecraft.core.particles.ParticleTypes;
+ import net.minecraft.nbt.CompoundTag;
+ import net.minecraft.nbt.NbtOps;
+-import net.minecraft.nbt.Tag;
+ import net.minecraft.network.protocol.Packet;
+ import net.minecraft.network.protocol.game.ClientGamePacketListener;
+ import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
+@@ -37,12 +38,12 @@
+ import net.minecraft.world.entity.AnimationState;
+ 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.GroupDataEntity;
+ import net.minecraft.world.entity.LivingEntity;
+-import net.minecraft.world.entity.MobSpawnType;
+-import net.minecraft.world.entity.Pose;
+-import net.minecraft.world.entity.SpawnGroupData;
+ import net.minecraft.world.entity.ai.Brain;
+ import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
+@@ -56,8 +57,8 @@
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.level.LevelReader;
+ import net.minecraft.world.level.ServerLevelAccessor;
+-import net.minecraft.world.level.block.RenderShape;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.EnumRenderType;
++import net.minecraft.world.level.block.state.IBlockData;
+ import net.minecraft.world.level.gameevent.DynamicGameEventListener;
+ import net.minecraft.world.level.gameevent.EntityPositionSource;
+ import net.minecraft.world.level.gameevent.GameEvent;
+@@ -73,6 +74,7 @@
+ import org.slf4j.Logger;
+
+ public class Warden extends Monster implements VibrationSystem {
++
+ private static final Logger LOGGER = LogUtils.getLogger();
+ private static final int VIBRATION_COOLDOWN_TICKS = 40;
+ private static final int TIME_TO_USE_MELEE_UNTIL_SONIC_BOOM = 200;
+@@ -106,16 +108,13 @@
+ public AnimationState diggingAnimationState = new AnimationState();
+ public AnimationState attackAnimationState = new AnimationState();
+ public AnimationState sonicBoomAnimationState = new AnimationState();
+- private final DynamicGameEventListener<VibrationSystem.Listener> dynamicGameEventListener;
+- private final VibrationSystem.User vibrationUser;
+- private VibrationSystem.Data vibrationData;
++ private final DynamicGameEventListener<VibrationSystem.Listener> dynamicGameEventListener = new DynamicGameEventListener<>(new VibrationSystem.Listener(this));
++ private final VibrationSystem.User vibrationUser = new Warden.a();
++ private VibrationSystem.Data vibrationData = new VibrationSystem.Data();
+ AngerManagement angerManagement = new AngerManagement(this::canTargetEntity, Collections.emptyList());
+
+ public Warden(EntityType<? extends Monster> entityType, Level level) {
+ super(entityType, level);
+- this.vibrationUser = new Warden.VibrationUser();
+- this.vibrationData = new VibrationSystem.Data();
+- this.dynamicGameEventListener = new DynamicGameEventListener<>(new VibrationSystem.Listener(this));
+ this.xpReward = 5;
+ this.getNavigation().setCanFloat(true);
+ this.setPathfindingMalus(BlockPathTypes.UNPASSABLE_RAIL, 0.0F);
+@@ -128,15 +127,16 @@
+
+ @Override
+ public Packet<ClientGamePacketListener> getAddEntityPacket() {
+- return new ClientboundAddEntityPacket(this, this.hasPose(Pose.EMERGING) ? 1 : 0);
++ return new ClientboundAddEntityPacket(this, this.hasPose(EntityPose.EMERGING) ? 1 : 0);
+ }
+
+ @Override
+ public void recreateFromPacket(ClientboundAddEntityPacket packet) {
+ super.recreateFromPacket(packet);
+ if (packet.getData() == 1) {
+- this.setPose(Pose.EMERGING);
++ this.setPose(EntityPose.EMERGING);
+ }
++
+ }
+
+ @Override
+@@ -151,11 +151,11 @@
+
+ @Override
+ public boolean isInvulnerableTo(DamageSource source) {
+- return this.isDiggingOrEmerging() && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY) || super.isInvulnerableTo(source);
++ return this.isDiggingOrEmerging() && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY) ? true : super.isInvulnerableTo(source);
+ }
+
+ boolean isDiggingOrEmerging() {
+- return this.hasPose(Pose.DIGGING) || this.hasPose(Pose.EMERGING);
++ return this.hasPose(EntityPose.DIGGING) || this.hasPose(EntityPose.EMERGING);
+ }
+
+ @Override
+@@ -174,12 +174,7 @@
+ }
+
+ public static AttributeSupplier.Builder createAttributes() {
+- return Monster.createMonsterAttributes()
+- .add(Attributes.MAX_HEALTH, 500.0)
+- .add(Attributes.MOVEMENT_SPEED, 0.3F)
+- .add(Attributes.KNOCKBACK_RESISTANCE, 1.0)
+- .add(Attributes.ATTACK_KNOCKBACK, 1.5)
+- .add(Attributes.ATTACK_DAMAGE, 30.0);
++ return Monster.createMonsterAttributes().add(Attributes.MAX_HEALTH, 500.0D).add(Attributes.MOVEMENT_SPEED, 0.30000001192092896D).add(Attributes.KNOCKBACK_RESISTANCE, 1.0D).add(Attributes.ATTACK_KNOCKBACK, 1.5D).add(Attributes.ATTACK_DAMAGE, 30.0D);
+ }
+
+ @Override
+@@ -195,7 +190,7 @@
+ @Nullable
+ @Override
+ protected SoundEvent getAmbientSound() {
+- return !this.hasPose(Pose.ROARING) && !this.isDiggingOrEmerging() ? this.getAngerLevel().getAmbientSound() : null;
++ return !this.hasPose(EntityPose.ROARING) && !this.isDiggingOrEmerging() ? this.getAngerLevel().getAmbientSound() : null;
+ }
+
+ @Override
+@@ -209,36 +204,40 @@
+ }
+
+ @Override
+- protected void playStepSound(BlockPos pos, BlockState state) {
++ protected void playStepSound(BlockPos pos, IBlockData state) {
+ this.playSound(SoundEvents.WARDEN_STEP, 10.0F, 1.0F);
+ }
+
+ @Override
+- public boolean doHurtTarget(Entity entity) {
+- this.level().broadcastEntityEvent(this, (byte)4);
++ public boolean doHurtTarget(Entity target) {
++ this.level().broadcastEntityEvent(this, (byte) 4);
+ this.playSound(SoundEvents.WARDEN_ATTACK_IMPACT, 10.0F, this.getVoicePitch());
+ SonicBoom.setCooldown(this, 40);
+- return super.doHurtTarget(entity);
++ return super.doHurtTarget(target);
+ }
+
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(CLIENT_ANGER_LEVEL, 0);
++ this.entityData.define(Warden.CLIENT_ANGER_LEVEL, 0);
+ }
+
+ public int getClientAngerLevel() {
+- return this.entityData.get(CLIENT_ANGER_LEVEL);
++ return (Integer) this.entityData.get(Warden.CLIENT_ANGER_LEVEL);
+ }
+
+ private void syncClientAngerLevel() {
+- this.entityData.set(CLIENT_ANGER_LEVEL, this.getActiveAnger());
++ this.entityData.set(Warden.CLIENT_ANGER_LEVEL, this.getActiveAnger());
+ }
+
+ @Override
+ public void tick() {
+- if (this.level() instanceof ServerLevel serverLevel) {
+- VibrationSystem.Ticker.tick(serverLevel, this.vibrationData, this.vibrationUser);
++ Level world = this.level();
++
++ if (world instanceof ServerLevel) {
++ ServerLevel worldserver = (ServerLevel) world;
++
++ VibrationSystem.Ticker.tick(worldserver, this.vibrationData, this.vibrationUser);
+ if (this.isPersistenceRequired() || this.requiresCustomPersistence()) {
+ WardenAi.setDigCooldown(this);
+ }
+@@ -249,21 +248,18 @@
+ if (this.tickCount % this.getHeartBeatDelay() == 0) {
+ this.heartAnimation = 10;
+ if (!this.isSilent()) {
+- this.level()
+- .playLocalSound(
+- this.getX(), this.getY(), this.getZ(), SoundEvents.WARDEN_HEARTBEAT, this.getSoundSource(), 5.0F, this.getVoicePitch(), false
+- );
++ this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), SoundEvents.WARDEN_HEARTBEAT, this.getSoundSource(), 5.0F, this.getVoicePitch(), false);
+ }
+ }
+
+ this.tendrilAnimationO = this.tendrilAnimation;
+ if (this.tendrilAnimation > 0) {
+- this.tendrilAnimation--;
++ --this.tendrilAnimation;
+ }
+
+ this.heartAnimationO = this.heartAnimation;
+ if (this.heartAnimation > 0) {
+- this.heartAnimation--;
++ --this.heartAnimation;
+ }
+
+ switch (this.getPose()) {
+@@ -274,21 +270,23 @@
+ this.clientDiggingParticles(this.diggingAnimationState);
+ }
+ }
++
+ }
+
+ @Override
+ protected void customServerAiStep() {
+- ServerLevel serverLevel = (ServerLevel)this.level();
+- serverLevel.getProfiler().push("wardenBrain");
+- this.getBrain().tick(serverLevel, this);
++ ServerLevel worldserver = (ServerLevel) this.level();
++
++ worldserver.getProfiler().push("wardenBrain");
++ this.getBrain().tick(worldserver, this);
+ this.level().getProfiler().pop();
+ super.customServerAiStep();
+ if ((this.tickCount + this.getId()) % 120 == 0) {
+- applyDarknessAround(serverLevel, this.position(), this, 20);
++ applyDarknessAround(worldserver, this.position(), this, 20);
+ }
+
+ if (this.tickCount % 20 == 0) {
+- this.angerManagement.tick(serverLevel, this::canTargetEntity);
++ this.angerManagement.tick(worldserver, this::canTargetEntity);
+ this.syncClientAngerLevel();
+ }
+
+@@ -307,39 +305,44 @@
+ } else {
+ super.handleEntityEvent(id);
+ }
++
+ }
+
+ private int getHeartBeatDelay() {
+- float f = (float)this.getClientAngerLevel() / (float)AngerLevel.ANGRY.getMinimumAnger();
++ float f = (float) this.getClientAngerLevel() / (float) AngerLevel.ANGRY.getMinimumAnger();
++
+ return 40 - Mth.floor(Mth.clamp(f, 0.0F, 1.0F) * 30.0F);
+ }
+
+ public float getTendrilAnimation(float partialTick) {
+- return Mth.lerp(partialTick, (float)this.tendrilAnimationO, (float)this.tendrilAnimation) / 10.0F;
++ return Mth.lerp(partialTick, (float) this.tendrilAnimationO, (float) this.tendrilAnimation) / 10.0F;
+ }
+
+ public float getHeartAnimation(float partialTick) {
+- return Mth.lerp(partialTick, (float)this.heartAnimationO, (float)this.heartAnimation) / 10.0F;
++ return Mth.lerp(partialTick, (float) this.heartAnimationO, (float) this.heartAnimation) / 10.0F;
+ }
+
+ private void clientDiggingParticles(AnimationState animationState) {
+- if ((float)animationState.getAccumulatedTime() < 4500.0F) {
+- RandomSource random = this.getRandom();
+- BlockState blockStateOn = this.getBlockStateOn();
+- if (blockStateOn.getRenderShape() != RenderShape.INVISIBLE) {
+- for (int i = 0; i < 30; i++) {
+- double d = this.getX() + (double)Mth.randomBetween(random, -0.7F, 0.7F);
+- double y = this.getY();
+- double d1 = this.getZ() + (double)Mth.randomBetween(random, -0.7F, 0.7F);
+- this.level().addParticle(new BlockParticleOption(ParticleTypes.BLOCK, blockStateOn), d, y, d1, 0.0, 0.0, 0.0);
++ if ((float) animationState.getAccumulatedTime() < 4500.0F) {
++ RandomSource randomsource = this.getRandom();
++ IBlockData iblockdata = this.getBlockStateOn();
++
++ if (iblockdata.getRenderShape() != EnumRenderType.INVISIBLE) {
++ for (int i = 0; i < 30; ++i) {
++ double d0 = this.getX() + (double) Mth.randomBetween(randomsource, -0.7F, 0.7F);
++ double d1 = this.getY();
++ double d2 = this.getZ() + (double) Mth.randomBetween(randomsource, -0.7F, 0.7F);
++
++ this.level().addParticle(new BlockParticleOption(ParticleTypes.BLOCK, iblockdata), d0, d1, d2, 0.0D, 0.0D, 0.0D);
+ }
+ }
+ }
++
+ }
+
+ @Override
+ public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
+- if (DATA_POSE.equals(key)) {
++ if (Warden.DATA_POSE.equals(key)) {
+ switch (this.getPose()) {
+ case EMERGING:
+ this.emergeAnimationState.start(this.tickCount);
+@@ -370,7 +373,7 @@
+
+ @Override
+ public Brain<Warden> getBrain() {
+- return (Brain<Warden>)super.getBrain();
++ return (Brain<Warden>) super.getBrain(); // CraftBukkit - decompile error
+ }
+
+ @Override
+@@ -381,69 +384,89 @@
+
+ @Override
+ public void updateDynamicGameEventListener(BiConsumer<DynamicGameEventListener<?>, ServerLevel> listenerConsumer) {
+- if (this.level() instanceof ServerLevel serverLevel) {
+- listenerConsumer.accept(this.dynamicGameEventListener, serverLevel);
++ Level world = this.level();
++
++ if (world instanceof ServerLevel) {
++ ServerLevel worldserver = (ServerLevel) world;
++
++ listenerConsumer.accept(this.dynamicGameEventListener, worldserver);
+ }
++
+ }
+
+ @Contract("null->false")
+ public boolean canTargetEntity(@Nullable Entity entity) {
+- if (entity instanceof LivingEntity livingEntity
+- && this.level() == entity.level()
+- && EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(entity)
+- && !this.isAlliedTo(entity)
+- && livingEntity.getType() != EntityType.ARMOR_STAND
+- && livingEntity.getType() != EntityType.WARDEN
+- && !livingEntity.isInvulnerable()
+- && !livingEntity.isDeadOrDying()
+- && this.level().getWorldBorder().isWithinBounds(livingEntity.getBoundingBox())) {
+- return true;
++ boolean flag;
++
++ if (entity instanceof LivingEntity) {
++ LivingEntity entityliving = (LivingEntity) entity;
++
++ if (this.level() == entity.level() && EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(entity) && !this.isAlliedTo(entity) && entityliving.getType() != EntityType.ARMOR_STAND && entityliving.getType() != EntityType.WARDEN && !entityliving.isInvulnerable() && !entityliving.isDeadOrDying() && this.level().getWorldBorder().isWithinBounds(entityliving.getBoundingBox())) {
++ flag = true;
++ return flag;
++ }
+ }
+
+- return false;
++ flag = false;
++ return flag;
+ }
+
+ public static void applyDarknessAround(ServerLevel level, Vec3 pos, @Nullable Entity source, int radius) {
+- MobEffectInstance mobEffectInstance = new MobEffectInstance(MobEffects.DARKNESS, 260, 0, false, false);
+- MobEffectUtil.addEffectToPlayersAround(level, source, pos, (double)radius, mobEffectInstance, 200);
++ MobEffectInstance mobeffect = new MobEffectInstance(MobEffects.DARKNESS, 260, 0, false, false);
++
++ MobEffectUtil.addEffectToPlayersAround(level, source, pos, radius, mobeffect, 200, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.WARDEN); // CraftBukkit - Add EntityPotionEffectEvent.Cause
+ }
+
+ @Override
+ public void addAdditionalSaveData(CompoundTag compound) {
+ super.addAdditionalSaveData(compound);
+- AngerManagement.codec(this::canTargetEntity)
+- .encodeStart(NbtOps.INSTANCE, this.angerManagement)
+- .resultOrPartial(LOGGER::error)
+- .ifPresent(tag -> compound.put("anger", tag));
+- VibrationSystem.Data.CODEC
+- .encodeStart(NbtOps.INSTANCE, this.vibrationData)
+- .resultOrPartial(LOGGER::error)
+- .ifPresent(tag -> compound.put("listener", tag));
++ DataResult<net.minecraft.nbt.Tag> dataresult = AngerManagement.codec(this::canTargetEntity).encodeStart(NbtOps.INSTANCE, this.angerManagement); // CraftBukkit - decompile error
++ Logger logger = Warden.LOGGER;
++
++ Objects.requireNonNull(logger);
++ dataresult.resultOrPartial(logger::error).ifPresent((nbtbase) -> {
++ compound.put("anger", nbtbase);
++ });
++ dataresult = VibrationSystem.Data.CODEC.encodeStart(NbtOps.INSTANCE, this.vibrationData);
++ logger = Warden.LOGGER;
++ Objects.requireNonNull(logger);
++ dataresult.resultOrPartial(logger::error).ifPresent((nbtbase) -> {
++ compound.put("listener", nbtbase);
++ });
+ }
+
+ @Override
+ public void readAdditionalSaveData(CompoundTag compound) {
+ super.readAdditionalSaveData(compound);
++ DataResult dataresult;
++ Logger logger;
++
+ if (compound.contains("anger")) {
+- AngerManagement.codec(this::canTargetEntity)
+- .parse(new Dynamic<>(NbtOps.INSTANCE, compound.get("anger")))
+- .resultOrPartial(LOGGER::error)
+- .ifPresent(angerManagement -> this.angerManagement = angerManagement);
++ dataresult = AngerManagement.codec(this::canTargetEntity).parse(new Dynamic(NbtOps.INSTANCE, compound.get("anger")));
++ logger = Warden.LOGGER;
++ Objects.requireNonNull(logger);
++ ((DataResult<AngerManagement>) dataresult).resultOrPartial(logger::error).ifPresent((angermanagement) -> { // CraftBukkit - decompile error
++ this.angerManagement = angermanagement;
++ });
+ this.syncClientAngerLevel();
+ }
+
+ if (compound.contains("listener", 10)) {
+- VibrationSystem.Data.CODEC
+- .parse(new Dynamic<>(NbtOps.INSTANCE, compound.getCompound("listener")))
+- .resultOrPartial(LOGGER::error)
+- .ifPresent(vibrationData -> this.vibrationData = vibrationData);
++ dataresult = VibrationSystem.Data.CODEC.parse(new Dynamic(NbtOps.INSTANCE, compound.getCompound("listener")));
++ logger = Warden.LOGGER;
++ Objects.requireNonNull(logger);
++ ((DataResult<VibrationSystem.Data>) dataresult).resultOrPartial(logger::error).ifPresent((vibrationsystem_a) -> { // CraftBukkit - decompile error
++ this.vibrationData = vibrationsystem_a;
++ });
+ }
++
+ }
+
+ private void playListeningSound() {
+- if (!this.hasPose(Pose.ROARING)) {
++ if (!this.hasPose(EntityPose.ROARING)) {
+ this.playSound(this.getAngerLevel().getListeningSound(), 10.0F, this.getVoicePitch());
+ }
++
+ }
+
+ public AngerLevel getAngerLevel() {
+@@ -466,9 +489,10 @@
+ public void increaseAngerAt(@Nullable Entity entity, int offset, boolean playListeningSound) {
+ if (!this.isNoAi() && this.canTargetEntity(entity)) {
+ WardenAi.setDigCooldown(this);
+- boolean flag = !(this.getBrain().getMemory(MemoryModuleType.ATTACK_TARGET).orElse(null) instanceof Player);
+- int i = this.angerManagement.increaseAnger(entity, offset);
+- if (entity instanceof Player && flag && AngerLevel.byAnger(i).isAngry()) {
++ boolean flag1 = !(this.getBrain().getMemory(MemoryModuleType.ATTACK_TARGET).orElse(null) instanceof Player); // CraftBukkit - decompile error
++ int j = this.angerManagement.increaseAnger(entity, offset);
++
++ if (entity instanceof Player && flag1 && AngerLevel.byAnger(j).isAngry()) {
+ this.getBrain().eraseMemory(MemoryModuleType.ATTACK_TARGET);
+ }
+
+@@ -476,6 +500,7 @@
+ this.playListeningSound();
+ }
+ }
++
+ }
+
+ public Optional<LivingEntity> getEntityAngryAt() {
+@@ -485,7 +510,7 @@
+ @Nullable
+ @Override
+ public LivingEntity getTarget() {
+- return this.getBrain().getMemory(MemoryModuleType.ATTACK_TARGET).orElse(null);
++ return (LivingEntity) this.getBrain().getMemory(MemoryModuleType.ATTACK_TARGET).orElse(null); // CraftBukkit - decompile error
+ }
+
+ @Override
+@@ -495,13 +520,11 @@
+
+ @Nullable
+ @Override
+- public SpawnGroupData finalizeSpawn(
+- ServerLevelAccessor level, DifficultyInstance difficulty, MobSpawnType reason, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag dataTag
+- ) {
++ public GroupDataEntity finalizeSpawn(ServerLevelAccessor level, DifficultyInstance difficulty, EnumMobSpawn reason, @Nullable GroupDataEntity spawnData, @Nullable CompoundTag dataTag) {
+ this.getBrain().setMemoryWithExpiry(MemoryModuleType.DIG_COOLDOWN, Unit.INSTANCE, 1200L);
+- if (reason == MobSpawnType.TRIGGERED) {
+- this.setPose(Pose.EMERGING);
+- this.getBrain().setMemoryWithExpiry(MemoryModuleType.IS_EMERGING, Unit.INSTANCE, (long)WardenAi.EMERGE_DURATION);
++ if (reason == EnumMobSpawn.TRIGGERED) {
++ this.setPose(EntityPose.EMERGING);
++ this.getBrain().setMemoryWithExpiry(MemoryModuleType.IS_EMERGING, Unit.INSTANCE, (long) WardenAi.EMERGE_DURATION);
+ this.playSound(SoundEvents.WARDEN_AGITATED, 5.0F, 1.0F);
+ }
+
+@@ -511,13 +534,17 @@
+ @Override
+ public boolean hurt(DamageSource source, float amount) {
+ boolean flag = super.hurt(source, amount);
++
+ if (!this.level().isClientSide && !this.isNoAi() && !this.isDiggingOrEmerging()) {
+ Entity entity = source.getEntity();
++
+ this.increaseAngerAt(entity, AngerLevel.ANGRY.getMinimumAnger() + 20, false);
+- if (this.brain.getMemory(MemoryModuleType.ATTACK_TARGET).isEmpty()
+- && entity instanceof LivingEntity livingEntity
+- && (!source.isIndirect() || this.closerThan(livingEntity, 5.0))) {
+- this.setAttackTarget(livingEntity);
++ if (this.brain.getMemory(MemoryModuleType.ATTACK_TARGET).isEmpty() && entity instanceof LivingEntity) {
++ LivingEntity entityliving = (LivingEntity) entity;
++
++ if (!source.isIndirect() || this.closerThan(entityliving, 5.0D)) {
++ this.setAttackTarget(entityliving);
++ }
+ }
+ }
+
+@@ -526,15 +553,16 @@
+
+ public void setAttackTarget(LivingEntity attackTarget) {
+ this.getBrain().eraseMemory(MemoryModuleType.ROAR_TARGET);
+- this.getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, attackTarget);
++ this.getBrain().setMemory(MemoryModuleType.ATTACK_TARGET, attackTarget); // CraftBukkit - decompile error
+ this.getBrain().eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
+ SonicBoom.setCooldown(this, 200);
+ }
+
+ @Override
+- public EntityDimensions getDimensions(Pose pose) {
+- EntityDimensions entityDimensions = super.getDimensions(pose);
+- return this.isDiggingOrEmerging() ? EntityDimensions.fixed(entityDimensions.width, 1.0F) : entityDimensions;
++ public EntityDimensions getDimensions(EntityPose pose) {
++ EntityDimensions entitysize = super.getDimensions(pose);
++
++ return this.isDiggingOrEmerging() ? EntityDimensions.fixed(entitysize.width, 1.0F) : entitysize;
+ }
+
+ @Override
+@@ -576,8 +604,8 @@
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
+- return new Vector3f(0.0F, entityDimensions.height + 0.25F * f, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height + 0.25F * f, 0.0F);
+ }
+
+ @Override
+@@ -590,10 +618,13 @@
+ return this.vibrationUser;
+ }
+
+- class VibrationUser implements VibrationSystem.User {
++ private class a implements VibrationSystem.User {
++
+ private static final int GAME_EVENT_LISTENER_RANGE = 16;
+ private final PositionSource positionSource = new EntityPositionSource(Warden.this, Warden.this.getEyeHeight());
+
++ a() {}
++
+ @Override
+ public int getListenerRadius() {
+ return 16;
+@@ -616,35 +647,39 @@
+
+ @Override
+ public boolean canReceiveVibration(ServerLevel level, BlockPos pos, GameEvent gameEvent, GameEvent.Context context) {
+- if (!Warden.this.isNoAi()
+- && !Warden.this.isDeadOrDying()
+- && !Warden.this.getBrain().hasMemoryValue(MemoryModuleType.VIBRATION_COOLDOWN)
+- && !Warden.this.isDiggingOrEmerging()
+- && level.getWorldBorder().isWithinBounds(pos)) {
+- if (context.sourceEntity() instanceof LivingEntity livingEntity && !Warden.this.canTargetEntity(livingEntity)) {
+- return false;
++ if (!Warden.this.isNoAi() && !Warden.this.isDeadOrDying() && !Warden.this.getBrain().hasMemoryValue(MemoryModuleType.VIBRATION_COOLDOWN) && !Warden.this.isDiggingOrEmerging() && level.getWorldBorder().isWithinBounds(pos)) {
++ Entity entity = context.sourceEntity();
++ boolean flag;
++
++ if (entity instanceof LivingEntity) {
++ LivingEntity entityliving = (LivingEntity) entity;
++
++ if (!Warden.this.canTargetEntity(entityliving)) {
++ flag = false;
++ return flag;
++ }
+ }
+
+- return true;
++ flag = true;
++ return flag;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+- public void onReceiveVibration(
+- ServerLevel level, BlockPos pos, GameEvent gameEvent, @Nullable Entity entity, @Nullable Entity playerEntity, float distance
+- ) {
++ public void onReceiveVibration(ServerLevel level, BlockPos pos, GameEvent gameEvent, @Nullable Entity entity, @Nullable Entity playerEntity, float distance) {
+ if (!Warden.this.isDeadOrDying()) {
+ Warden.this.brain.setMemoryWithExpiry(MemoryModuleType.VIBRATION_COOLDOWN, Unit.INSTANCE, 40L);
+- level.broadcastEntityEvent(Warden.this, (byte)61);
++ level.broadcastEntityEvent(Warden.this, (byte) 61);
+ Warden.this.playSound(SoundEvents.WARDEN_TENDRIL_CLICKS, 5.0F, Warden.this.getVoicePitch());
+- BlockPos blockPos = pos;
++ BlockPos blockposition1 = pos;
++
+ if (playerEntity != null) {
+- if (Warden.this.closerThan(playerEntity, 30.0)) {
++ if (Warden.this.closerThan(playerEntity, 30.0D)) {
+ if (Warden.this.getBrain().hasMemoryValue(MemoryModuleType.RECENT_PROJECTILE)) {
+ if (Warden.this.canTargetEntity(playerEntity)) {
+- blockPos = playerEntity.blockPosition();
++ blockposition1 = playerEntity.blockPosition();
+ }
+
+ Warden.this.increaseAngerAt(playerEntity);
+@@ -659,11 +694,13 @@
+ }
+
+ if (!Warden.this.getAngerLevel().isAngry()) {
+- Optional<LivingEntity> activeEntity = Warden.this.angerManagement.getActiveEntity();
+- if (playerEntity != null || activeEntity.isEmpty() || activeEntity.get() == entity) {
+- WardenAi.setDisturbanceLocation(Warden.this, blockPos);
++ Optional<LivingEntity> optional = Warden.this.angerManagement.getActiveEntity();
++
++ if (playerEntity != null || optional.isEmpty() || optional.get() == entity) {
++ WardenAi.setDisturbanceLocation(Warden.this, blockposition1);
+ }
+ }
++
+ }
+ }
+ }