aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile
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/projectile
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/projectile')
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/AbstractArrow.java.patch784
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/AbstractHurtingProjectile.java.patch276
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/Arrow.java.patch306
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/EvokerFangs.java.patch192
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/EyeOfEnder.java.patch245
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/Fireball.java.patch78
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch445
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FishingHook.java.patch854
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/LargeFireball.java.patch86
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/LlamaSpit.java.patch119
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/Projectile.java.patch300
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ShulkerBullet.java.patch357
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/SmallFireball.java.patch93
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/SpectralArrow.java.patch57
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrowableItemProjectile.java.patch86
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrowableProjectile.java.patch144
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownEgg.java.patch135
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch140
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownExperienceBottle.java.patch45
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownPotion.java.patch381
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownTrident.java.patch215
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/WindCharge.java.patch134
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/WitherSkull.java.patch130
23 files changed, 5602 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/AbstractArrow.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/AbstractArrow.java.patch
new file mode 100644
index 0000000000..d546fcc7e3
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/AbstractArrow.java.patch
@@ -0,0 +1,784 @@
+--- a/net/minecraft/world/entity/projectile/AbstractArrow.java
++++ b/net/minecraft/world/entity/projectile/AbstractArrow.java
+@@ -3,6 +3,8 @@
+ import com.google.common.collect.Lists;
+ import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
+ import java.util.Arrays;
++import java.util.Collection;
++import java.util.Iterator;
+ import java.util.List;
+ import javax.annotation.Nullable;
+ import net.minecraft.advancements.CriteriaTriggers;
+@@ -25,11 +27,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.EnumMoveType;
+ import net.minecraft.world.entity.LivingEntity;
+-import net.minecraft.world.entity.MoverType;
+-import net.minecraft.world.entity.Pose;
+ import net.minecraft.world.entity.ai.attributes.Attributes;
++import net.minecraft.world.entity.item.ItemEntity;
+ import net.minecraft.world.entity.player.Player;
+ import net.minecraft.world.item.ItemStack;
+ import net.minecraft.world.item.enchantment.EnchantmentHelper;
+@@ -37,56 +40,65 @@
+ import net.minecraft.world.level.ClipContext;
+ import net.minecraft.world.level.Level;
+ 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.AABB;
+ import net.minecraft.world.phys.BlockHitResult;
+ import net.minecraft.world.phys.EntityHitResult;
+ import net.minecraft.world.phys.HitResult;
+ import net.minecraft.world.phys.Vec3;
+ import net.minecraft.world.phys.shapes.VoxelShape;
++import org.bukkit.event.entity.EntityCombustByEntityEvent;
++import org.bukkit.event.player.PlayerPickupArrowEvent;
++// CraftBukkit end
+
+ public abstract class AbstractArrow extends Projectile {
+- private static final double ARROW_BASE_DAMAGE = 2.0;
++
++ private static final double ARROW_BASE_DAMAGE = 2.0D;
+ private static final EntityDataAccessor<Byte> ID_FLAGS = SynchedEntityData.defineId(AbstractArrow.class, EntityDataSerializers.BYTE);
+ private static final EntityDataAccessor<Byte> PIERCE_LEVEL = SynchedEntityData.defineId(AbstractArrow.class, EntityDataSerializers.BYTE);
+ private static final int FLAG_CRIT = 1;
+ private static final int FLAG_NOPHYSICS = 2;
+ private static final int FLAG_CROSSBOW = 4;
+ @Nullable
+- private BlockState lastState;
+- protected boolean inGround;
++ private IBlockData lastState;
++ public boolean inGround;
+ protected int inGroundTime;
+- public AbstractArrow.Pickup pickup = AbstractArrow.Pickup.DISALLOWED;
++ public AbstractArrow.Pickup pickup;
+ public int shakeTime;
+- private int life;
+- private double baseDamage = 2.0;
+- private int knockback;
+- private SoundEvent soundEvent = this.getDefaultHitGroundSoundEvent();
++ public int life;
++ private double baseDamage;
++ public int knockback;
++ private SoundEvent soundEvent;
+ @Nullable
+ private IntOpenHashSet piercingIgnoreEntityIds;
+ @Nullable
+ private List<Entity> piercedAndKilledEntities;
+- private ItemStack pickupItemStack;
++ public ItemStack pickupItemStack;
+
+- protected AbstractArrow(EntityType<? extends AbstractArrow> entityType, Level level, ItemStack itemStack) {
+- super(entityType, level);
+- this.pickupItemStack = itemStack.copy();
+- if (itemStack.hasCustomHoverName()) {
+- this.setCustomName(itemStack.getHoverName());
++ protected AbstractArrow(EntityType<? extends AbstractArrow> entitytypes, Level world, ItemStack itemstack) {
++ super(entitytypes, world);
++ this.pickup = AbstractArrow.Pickup.DISALLOWED;
++ this.baseDamage = 2.0D;
++ this.soundEvent = this.getDefaultHitGroundSoundEvent();
++ this.pickupItemStack = itemstack.copy();
++ if (itemstack.hasCustomHoverName()) {
++ this.setCustomName(itemstack.getHoverName());
+ }
++
+ }
+
+- protected AbstractArrow(EntityType<? extends AbstractArrow> entityType, double d, double d1, double d2, Level level, ItemStack itemStack) {
+- this(entityType, level, itemStack);
+- this.setPos(d, d1, d2);
++ protected AbstractArrow(EntityType<? extends AbstractArrow> entitytypes, double d0, double d1, double d2, Level world, ItemStack itemstack) {
++ this(entitytypes, world, itemstack);
++ this.setPos(d0, d1, d2);
+ }
+
+- protected AbstractArrow(EntityType<? extends AbstractArrow> entityType, LivingEntity livingEntity, Level level, ItemStack itemStack) {
+- this(entityType, livingEntity.getX(), livingEntity.getEyeY() - 0.1F, livingEntity.getZ(), level, itemStack);
+- this.setOwner(livingEntity);
+- if (livingEntity instanceof Player) {
++ protected AbstractArrow(EntityType<? extends AbstractArrow> entitytypes, LivingEntity entityliving, Level world, ItemStack itemstack) {
++ this(entitytypes, entityliving.getX(), entityliving.getEyeY() - 0.10000000149011612D, entityliving.getZ(), world, itemstack);
++ this.setOwner(entityliving);
++ if (entityliving instanceof Player) {
+ this.pickup = AbstractArrow.Pickup.ALLOWED;
+ }
++
+ }
+
+ public void setSoundEvent(SoundEvent soundEvent) {
+@@ -95,61 +107,70 @@
+
+ @Override
+ public boolean shouldRenderAtSqrDistance(double distance) {
+- double d = this.getBoundingBox().getSize() * 10.0;
+- if (Double.isNaN(d)) {
+- d = 1.0;
++ double d1 = this.getBoundingBox().getSize() * 10.0D;
++
++ if (Double.isNaN(d1)) {
++ d1 = 1.0D;
+ }
+
+- d *= 64.0 * getViewScale();
+- return distance < d * d;
++ d1 *= 64.0D * getViewScale();
++ return distance < d1 * d1;
+ }
+
+ @Override
+ protected void defineSynchedData() {
+- this.entityData.define(ID_FLAGS, (byte)0);
+- this.entityData.define(PIERCE_LEVEL, (byte)0);
++ this.entityData.define(AbstractArrow.ID_FLAGS, (byte) 0);
++ this.entityData.define(AbstractArrow.PIERCE_LEVEL, (byte) 0);
+ }
+
+ @Override
+- public void shoot(double x, double y, double z, float velocity, float inaccuracy) {
+- super.shoot(x, y, z, velocity, inaccuracy);
++ public void shoot(double x, double d1, double y, float f, float z) {
++ super.shoot(x, d1, y, f, z);
+ this.life = 0;
+ }
+
+ @Override
+- public void lerpTo(double d, double d1, double d2, float f, float f1, int i) {
+- this.setPos(d, d1, d2);
++ public void lerpTo(double d0, double d1, double d2, float f, float f1, int i) {
++ this.setPos(d0, d1, d2);
+ this.setRot(f, f1);
+ }
+
+ @Override
+- public void lerpMotion(double x, double y, double z) {
+- super.lerpMotion(x, y, z);
++ public void lerpMotion(double x, double d1, double y) {
++ super.lerpMotion(x, d1, y);
+ this.life = 0;
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+- boolean isNoPhysics = this.isNoPhysics();
+- Vec3 deltaMovement = this.getDeltaMovement();
++ boolean flag = this.isNoPhysics();
++ Vec3 vec3d = this.getDeltaMovement();
++
+ if (this.xRotO == 0.0F && this.yRotO == 0.0F) {
+- double d = deltaMovement.horizontalDistance();
+- this.setYRot((float)(Mth.atan2(deltaMovement.x, deltaMovement.z) * 180.0F / (float)Math.PI));
+- this.setXRot((float)(Mth.atan2(deltaMovement.y, d) * 180.0F / (float)Math.PI));
++ double d0 = vec3d.horizontalDistance();
++
++ this.setYRot((float) (Mth.atan2(vec3d.x, vec3d.z) * 57.2957763671875D));
++ this.setXRot((float) (Mth.atan2(vec3d.y, d0) * 57.2957763671875D));
+ this.yRotO = this.getYRot();
+ this.xRotO = this.getXRot();
+ }
+
+- BlockPos blockPos = this.blockPosition();
+- BlockState blockState = this.level().getBlockState(blockPos);
+- if (!blockState.isAir() && !isNoPhysics) {
+- VoxelShape collisionShape = blockState.getCollisionShape(this.level(), blockPos);
+- if (!collisionShape.isEmpty()) {
+- Vec3 vec3 = this.position();
++ BlockPos blockposition = this.blockPosition();
++ IBlockData iblockdata = this.level().getBlockState(blockposition);
++ Vec3 vec3d1;
+
+- for (AABB aABB : collisionShape.toAabbs()) {
+- if (aABB.move(blockPos).contains(vec3)) {
++ if (!iblockdata.isAir() && !flag) {
++ VoxelShape voxelshape = iblockdata.getCollisionShape(this.level(), blockposition);
++
++ if (!voxelshape.isEmpty()) {
++ vec3d1 = this.position();
++ Iterator iterator = voxelshape.toAabbs().iterator();
++
++ while (iterator.hasNext()) {
++ AABB axisalignedbb = (AABB) iterator.next();
++
++ if (axisalignedbb.move(blockposition).contains(vec3d1)) {
+ this.inGround = true;
+ break;
+ }
+@@ -158,104 +179,104 @@
+ }
+
+ if (this.shakeTime > 0) {
+- this.shakeTime--;
++ --this.shakeTime;
+ }
+
+- if (this.isInWaterOrRain() || blockState.is(Blocks.POWDER_SNOW)) {
++ if (this.isInWaterOrRain() || iblockdata.is(Blocks.POWDER_SNOW)) {
+ this.clearFire();
+ }
+
+- if (this.inGround && !isNoPhysics) {
+- if (this.lastState != blockState && this.shouldFall()) {
++ if (this.inGround && !flag) {
++ if (this.lastState != iblockdata && this.shouldFall()) {
+ this.startFalling();
+ } else if (!this.level().isClientSide) {
+ this.tickDespawn();
+ }
+
+- this.inGroundTime++;
++ ++this.inGroundTime;
+ } else {
+ this.inGroundTime = 0;
+- Vec3 vec31 = this.position();
+- Vec3 vec3 = vec31.add(deltaMovement);
+- HitResult hitResult = this.level().clip(new ClipContext(vec31, vec3, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this));
+- if (hitResult.getType() != HitResult.Type.MISS) {
+- vec3 = hitResult.getLocation();
++ Vec3 vec3d2 = this.position();
++
++ vec3d1 = vec3d2.add(vec3d);
++ Object object = this.level().clip(new ClipContext(vec3d2, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this));
++
++ if (((HitResult) object).getType() != HitResult.EnumMovingObjectType.MISS) {
++ vec3d1 = ((HitResult) object).getLocation();
+ }
+
+ while (!this.isRemoved()) {
+- EntityHitResult entityHitResult = this.findHitEntity(vec31, vec3);
+- if (entityHitResult != null) {
+- hitResult = entityHitResult;
++ EntityHitResult movingobjectpositionentity = this.findHitEntity(vec3d2, vec3d1);
++
++ if (movingobjectpositionentity != null) {
++ object = movingobjectpositionentity;
+ }
+
+- if (hitResult != null && hitResult.getType() == HitResult.Type.ENTITY) {
+- Entity entity = ((EntityHitResult)hitResult).getEntity();
+- Entity owner = this.getOwner();
+- if (entity instanceof Player && owner instanceof Player && !((Player)owner).canHarmPlayer((Player)entity)) {
+- hitResult = null;
+- entityHitResult = null;
++ if (object != null && ((HitResult) object).getType() == HitResult.EnumMovingObjectType.ENTITY) {
++ Entity entity = ((EntityHitResult) object).getEntity();
++ Entity entity1 = this.getOwner();
++
++ if (entity instanceof Player && entity1 instanceof Player && !((Player) entity1).canHarmPlayer((Player) entity)) {
++ object = null;
++ movingobjectpositionentity = null;
+ }
+ }
+
+- if (hitResult != null && !isNoPhysics) {
+- this.onHit(hitResult);
++ if (object != null && !flag) {
++ this.preOnHit((HitResult) object); // CraftBukkit - projectile hit event
+ this.hasImpulse = true;
+ }
+
+- if (entityHitResult == null || this.getPierceLevel() <= 0) {
++ if (movingobjectpositionentity == null || this.getPierceLevel() <= 0) {
+ break;
+ }
+
+- hitResult = null;
++ object = null;
+ }
+
+- deltaMovement = this.getDeltaMovement();
+- double d1 = deltaMovement.x;
+- double d2 = deltaMovement.y;
+- double d3 = deltaMovement.z;
++ vec3d = this.getDeltaMovement();
++ double d1 = vec3d.x;
++ double d2 = vec3d.y;
++ double d3 = vec3d.z;
++
+ if (this.isCritArrow()) {
+- for (int i = 0; i < 4; i++) {
+- this.level()
+- .addParticle(
+- ParticleTypes.CRIT,
+- this.getX() + d1 * (double)i / 4.0,
+- this.getY() + d2 * (double)i / 4.0,
+- this.getZ() + d3 * (double)i / 4.0,
+- -d1,
+- -d2 + 0.2,
+- -d3
+- );
++ for (int i = 0; i < 4; ++i) {
++ this.level().addParticle(ParticleTypes.CRIT, this.getX() + d1 * (double) i / 4.0D, this.getY() + d2 * (double) i / 4.0D, this.getZ() + d3 * (double) i / 4.0D, -d1, -d2 + 0.2D, -d3);
+ }
+ }
+
+ double d4 = this.getX() + d1;
+ double d5 = this.getY() + d2;
+ double d6 = this.getZ() + d3;
+- double d7 = deltaMovement.horizontalDistance();
+- if (isNoPhysics) {
+- this.setYRot((float)(Mth.atan2(-d1, -d3) * 180.0F / (float)Math.PI));
++ double d7 = vec3d.horizontalDistance();
++
++ if (flag) {
++ this.setYRot((float) (Mth.atan2(-d1, -d3) * 57.2957763671875D));
+ } else {
+- this.setYRot((float)(Mth.atan2(d1, d3) * 180.0F / (float)Math.PI));
++ this.setYRot((float) (Mth.atan2(d1, d3) * 57.2957763671875D));
+ }
+
+- this.setXRot((float)(Mth.atan2(d2, d7) * 180.0F / (float)Math.PI));
++ this.setXRot((float) (Mth.atan2(d2, d7) * 57.2957763671875D));
+ this.setXRot(lerpRotation(this.xRotO, this.getXRot()));
+ this.setYRot(lerpRotation(this.yRotO, this.getYRot()));
+ float f = 0.99F;
+ float f1 = 0.05F;
++
+ if (this.isInWater()) {
+- for (int i1 = 0; i1 < 4; i1++) {
++ for (int j = 0; j < 4; ++j) {
+ float f2 = 0.25F;
+- this.level().addParticle(ParticleTypes.BUBBLE, d4 - d1 * 0.25, d5 - d2 * 0.25, d6 - d3 * 0.25, d1, d2, d3);
++
++ this.level().addParticle(ParticleTypes.BUBBLE, d4 - d1 * 0.25D, d5 - d2 * 0.25D, d6 - d3 * 0.25D, d1, d2, d3);
+ }
+
+ f = this.getWaterInertia();
+ }
+
+- this.setDeltaMovement(deltaMovement.scale((double)f));
+- if (!this.isNoGravity() && !isNoPhysics) {
+- Vec3 deltaMovement1 = this.getDeltaMovement();
+- this.setDeltaMovement(deltaMovement1.x, deltaMovement1.y - 0.05F, deltaMovement1.z);
++ this.setDeltaMovement(vec3d.scale((double) f));
++ if (!this.isNoGravity() && !flag) {
++ Vec3 vec3d3 = this.getDeltaMovement();
++
++ this.setDeltaMovement(vec3d3.x, vec3d3.y - 0.05000000074505806D, vec3d3.z);
+ }
+
+ this.setPos(d4, d5, d6);
+@@ -264,31 +285,32 @@
+ }
+
+ private boolean shouldFall() {
+- return this.inGround && this.level().noCollision(new AABB(this.position(), this.position()).inflate(0.06));
++ return this.inGround && this.level().noCollision((new AABB(this.position(), this.position())).inflate(0.06D));
+ }
+
+ private void startFalling() {
+ this.inGround = false;
+- Vec3 deltaMovement = this.getDeltaMovement();
+- this.setDeltaMovement(
+- deltaMovement.multiply((double)(this.random.nextFloat() * 0.2F), (double)(this.random.nextFloat() * 0.2F), (double)(this.random.nextFloat() * 0.2F))
+- );
++ Vec3 vec3d = this.getDeltaMovement();
++
++ this.setDeltaMovement(vec3d.multiply((double) (this.random.nextFloat() * 0.2F), (double) (this.random.nextFloat() * 0.2F), (double) (this.random.nextFloat() * 0.2F)));
+ this.life = 0;
+ }
+
+ @Override
+- public void move(MoverType type, Vec3 pos) {
++ public void move(EnumMoveType type, Vec3 pos) {
+ super.move(type, pos);
+- if (type != MoverType.SELF && this.shouldFall()) {
++ if (type != EnumMoveType.SELF && this.shouldFall()) {
+ this.startFalling();
+ }
++
+ }
+
+ protected void tickDespawn() {
+- this.life++;
++ ++this.life;
+ if (this.life >= 1200) {
+ this.discard();
+ }
++
+ }
+
+ private void resetPiercedEntities() {
+@@ -299,14 +321,16 @@
+ if (this.piercingIgnoreEntityIds != null) {
+ this.piercingIgnoreEntityIds.clear();
+ }
++
+ }
+
+ @Override
+ protected void onHitEntity(EntityHitResult result) {
+ super.onHitEntity(result);
+ Entity entity = result.getEntity();
+- float f = (float)this.getDeltaMovement().length();
+- int ceil = Mth.ceil(Mth.clamp((double)f * this.baseDamage, 0.0, 2.147483647E9));
++ float f = (float) this.getDeltaMovement().length();
++ int i = Mth.ceil(Mth.clamp((double) f * this.baseDamage, 0.0D, 2.147483647E9D));
++
+ if (this.getPierceLevel() > 0) {
+ if (this.piercingIgnoreEntityIds == null) {
+ this.piercingIgnoreEntityIds = new IntOpenHashSet(5);
+@@ -325,65 +349,79 @@
+ }
+
+ if (this.isCritArrow()) {
+- long l = (long)this.random.nextInt(ceil / 2 + 2);
+- ceil = (int)Math.min(l + (long)ceil, 2147483647L);
++ long j = (long) this.random.nextInt(i / 2 + 2);
++
++ i = (int) Math.min(j + (long) i, 2147483647L);
+ }
+
+- Entity owner = this.getOwner();
+- DamageSource damageSource;
+- if (owner == null) {
+- damageSource = this.damageSources().arrow(this, this);
++ Entity entity1 = this.getOwner();
++ DamageSource damagesource;
++
++ if (entity1 == null) {
++ damagesource = this.damageSources().arrow(this, this);
+ } else {
+- damageSource = this.damageSources().arrow(this, owner);
+- if (owner instanceof LivingEntity) {
+- ((LivingEntity)owner).setLastHurtMob(entity);
++ damagesource = this.damageSources().arrow(this, entity1);
++ if (entity1 instanceof LivingEntity) {
++ ((LivingEntity) entity1).setLastHurtMob(entity);
+ }
+ }
+
+ boolean flag = entity.getType() == EntityType.ENDERMAN;
+- int remainingFireTicks = entity.getRemainingFireTicks();
+- boolean isDeflectsArrows = entity.getType().is(EntityTypeTags.DEFLECTS_ARROWS);
+- if (this.isOnFire() && !flag && !isDeflectsArrows) {
+- entity.setSecondsOnFire(5);
++ int k = entity.getRemainingFireTicks();
++ boolean flag1 = entity.getType().is(EntityTypeTags.DEFLECTS_ARROWS);
++
++ if (this.isOnFire() && !flag && !flag1) {
++ // CraftBukkit start
++ EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 5);
++ org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent);
++ if (!combustEvent.isCancelled()) {
++ entity.setSecondsOnFire(combustEvent.getDuration(), false);
++ }
++ // CraftBukkit end
+ }
+
+- if (entity.hurt(damageSource, (float)ceil)) {
++ if (entity.hurt(damagesource, (float) i)) {
+ if (flag) {
+ return;
+ }
+
+- if (entity instanceof LivingEntity livingEntity) {
++ if (entity instanceof LivingEntity) {
++ LivingEntity entityliving = (LivingEntity) entity;
++
+ if (!this.level().isClientSide && this.getPierceLevel() <= 0) {
+- livingEntity.setArrowCount(livingEntity.getArrowCount() + 1);
++ entityliving.setArrowCount(entityliving.getArrowCount() + 1);
+ }
+
+ if (this.knockback > 0) {
+- double max = Math.max(0.0, 1.0 - livingEntity.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE));
+- Vec3 vec3 = this.getDeltaMovement().multiply(1.0, 0.0, 1.0).normalize().scale((double)this.knockback * 0.6 * max);
+- if (vec3.lengthSqr() > 0.0) {
+- livingEntity.push(vec3.x, 0.1, vec3.z);
++ double d0 = Math.max(0.0D, 1.0D - entityliving.getAttributeValue(Attributes.KNOCKBACK_RESISTANCE));
++ Vec3 vec3d = this.getDeltaMovement().multiply(1.0D, 0.0D, 1.0D).normalize().scale((double) this.knockback * 0.6D * d0);
++
++ if (vec3d.lengthSqr() > 0.0D) {
++ entityliving.push(vec3d.x, 0.1D, vec3d.z);
+ }
+ }
+
+- if (!this.level().isClientSide && owner instanceof LivingEntity) {
+- EnchantmentHelper.doPostHurtEffects(livingEntity, owner);
+- EnchantmentHelper.doPostDamageEffects((LivingEntity)owner, livingEntity);
++ if (!this.level().isClientSide && entity1 instanceof LivingEntity) {
++ EnchantmentHelper.doPostHurtEffects(entityliving, entity1);
++ EnchantmentHelper.doPostDamageEffects((LivingEntity) entity1, entityliving);
+ }
+
+- this.doPostHurtEffects(livingEntity);
+- if (owner != null && livingEntity != owner && livingEntity instanceof Player && owner instanceof ServerPlayer && !this.isSilent()) {
+- ((ServerPlayer)owner).connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.ARROW_HIT_PLAYER, 0.0F));
++ this.doPostHurtEffects(entityliving);
++ if (entity1 != null && entityliving != entity1 && entityliving instanceof Player && entity1 instanceof ServerPlayer && !this.isSilent()) {
++ ((ServerPlayer) entity1).connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.ARROW_HIT_PLAYER, 0.0F));
+ }
+
+ if (!entity.isAlive() && this.piercedAndKilledEntities != null) {
+- this.piercedAndKilledEntities.add(livingEntity);
++ this.piercedAndKilledEntities.add(entityliving);
+ }
+
+- if (!this.level().isClientSide && owner instanceof ServerPlayer serverPlayer) {
++ if (!this.level().isClientSide && entity1 instanceof ServerPlayer) {
++ ServerPlayer entityplayer = (ServerPlayer) entity1;
++
+ if (this.piercedAndKilledEntities != null && this.shotFromCrossbow()) {
+- CriteriaTriggers.KILLED_BY_CROSSBOW.trigger(serverPlayer, this.piercedAndKilledEntities);
++ CriteriaTriggers.KILLED_BY_CROSSBOW.trigger(entityplayer, (Collection) this.piercedAndKilledEntities);
+ } else if (!entity.isAlive() && this.shotFromCrossbow()) {
+- CriteriaTriggers.KILLED_BY_CROSSBOW.trigger(serverPlayer, Arrays.asList(entity));
++ CriteriaTriggers.KILLED_BY_CROSSBOW.trigger(entityplayer, (Collection) Arrays.asList(entity));
+ }
+ }
+ }
+@@ -392,14 +430,14 @@
+ if (this.getPierceLevel() <= 0) {
+ this.discard();
+ }
+- } else if (isDeflectsArrows) {
++ } else if (flag1) {
+ this.deflect();
+ } else {
+- entity.setRemainingFireTicks(remainingFireTicks);
+- this.setDeltaMovement(this.getDeltaMovement().scale(-0.1));
++ entity.setRemainingFireTicks(k);
++ this.setDeltaMovement(this.getDeltaMovement().scale(-0.1D));
+ this.setYRot(this.getYRot() + 180.0F);
+ this.yRotO += 180.0F;
+- if (!this.level().isClientSide && this.getDeltaMovement().lengthSqr() < 1.0E-7) {
++ if (!this.level().isClientSide && this.getDeltaMovement().lengthSqr() < 1.0E-7D) {
+ if (this.pickup == AbstractArrow.Pickup.ALLOWED) {
+ this.spawnAtLocation(this.getPickupItem(), 0.1F);
+ }
+@@ -407,11 +445,13 @@
+ this.discard();
+ }
+ }
++
+ }
+
+ public void deflect() {
+ float f = this.random.nextFloat() * 360.0F;
+- this.setDeltaMovement(this.getDeltaMovement().yRot(f * (float) (Math.PI / 180.0)).scale(0.5));
++
++ this.setDeltaMovement(this.getDeltaMovement().yRot(f * 0.017453292F).scale(0.5D));
+ this.setYRot(this.getYRot() + f);
+ this.yRotO += f;
+ }
+@@ -420,15 +460,17 @@
+ protected void onHitBlock(BlockHitResult result) {
+ this.lastState = this.level().getBlockState(result.getBlockPos());
+ super.onHitBlock(result);
+- Vec3 vec3 = result.getLocation().subtract(this.getX(), this.getY(), this.getZ());
+- this.setDeltaMovement(vec3);
+- Vec3 vec31 = vec3.normalize().scale(0.05F);
+- this.setPosRaw(this.getX() - vec31.x, this.getY() - vec31.y, this.getZ() - vec31.z);
++ Vec3 vec3d = result.getLocation().subtract(this.getX(), this.getY(), this.getZ());
++
++ this.setDeltaMovement(vec3d);
++ Vec3 vec3d1 = vec3d.normalize().scale(0.05000000074505806D);
++
++ this.setPosRaw(this.getX() - vec3d1.x, this.getY() - vec3d1.y, this.getZ() - vec3d1.z);
+ this.playSound(this.getHitGroundSoundEvent(), 1.0F, 1.2F / (this.random.nextFloat() * 0.2F + 0.9F));
+ this.inGround = true;
+ this.shakeTime = 7;
+ this.setCritArrow(false);
+- this.setPierceLevel((byte)0);
++ this.setPierceLevel((byte) 0);
+ this.setSoundEvent(SoundEvents.ARROW_HIT);
+ this.setShotFromCrossbow(false);
+ this.resetPiercedEntities();
+@@ -442,14 +484,11 @@
+ return this.soundEvent;
+ }
+
+- protected void doPostHurtEffects(LivingEntity target) {
+- }
++ protected void doPostHurtEffects(LivingEntity target) {}
+
+ @Nullable
+ protected EntityHitResult findHitEntity(Vec3 startVec, Vec3 endVec) {
+- return ProjectileUtil.getEntityHitResult(
+- this.level(), this, startVec, endVec, this.getBoundingBox().expandTowards(this.getDeltaMovement()).inflate(1.0), this::canHitEntity
+- );
++ return ProjectileUtil.getEntityHitResult(this.level(), this, startVec, endVec, this.getBoundingBox().expandTowards(this.getDeltaMovement()).inflate(1.0D), this::canHitEntity);
+ }
+
+ @Override
+@@ -460,14 +499,14 @@
+ @Override
+ public void addAdditionalSaveData(CompoundTag compound) {
+ super.addAdditionalSaveData(compound);
+- compound.putShort("life", (short)this.life);
++ compound.putShort("life", (short) this.life);
+ if (this.lastState != null) {
+ compound.put("inBlockState", NbtUtils.writeBlockState(this.lastState));
+ }
+
+- compound.putByte("shake", (byte)this.shakeTime);
++ compound.putByte("shake", (byte) this.shakeTime);
+ compound.putBoolean("inGround", this.inGround);
+- compound.putByte("pickup", (byte)this.pickup.ordinal());
++ compound.putByte("pickup", (byte) this.pickup.ordinal());
+ compound.putDouble("damage", this.baseDamage);
+ compound.putBoolean("crit", this.isCritArrow());
+ compound.putByte("PierceLevel", this.getPierceLevel());
+@@ -494,32 +533,48 @@
+ this.setCritArrow(compound.getBoolean("crit"));
+ this.setPierceLevel(compound.getByte("PierceLevel"));
+ if (compound.contains("SoundEvent", 8)) {
+- this.soundEvent = BuiltInRegistries.SOUND_EVENT
+- .getOptional(new ResourceLocation(compound.getString("SoundEvent")))
+- .orElse(this.getDefaultHitGroundSoundEvent());
++ this.soundEvent = (SoundEvent) BuiltInRegistries.SOUND_EVENT.getOptional(new ResourceLocation(compound.getString("SoundEvent"))).orElse(this.getDefaultHitGroundSoundEvent());
+ }
+
+ this.setShotFromCrossbow(compound.getBoolean("ShotFromCrossbow"));
+ if (compound.contains("item", 10)) {
+ this.pickupItemStack = ItemStack.of(compound.getCompound("item"));
+ }
++
+ }
+
+ @Override
+ public void setOwner(@Nullable Entity entity) {
+ super.setOwner(entity);
+ if (entity instanceof Player) {
+- this.pickup = ((Player)entity).getAbilities().instabuild ? AbstractArrow.Pickup.CREATIVE_ONLY : AbstractArrow.Pickup.ALLOWED;
++ this.pickup = ((Player) entity).getAbilities().instabuild ? AbstractArrow.Pickup.CREATIVE_ONLY : AbstractArrow.Pickup.ALLOWED;
+ }
++
+ }
+
+ @Override
+ public void playerTouch(Player entity) {
+ if (!this.level().isClientSide && (this.inGround || this.isNoPhysics()) && this.shakeTime <= 0) {
+- if (this.tryPickup(entity)) {
++ // CraftBukkit start
++ ItemStack itemstack = this.getPickupItem();
++ if (this.pickup == Pickup.ALLOWED && !itemstack.isEmpty() && entity.getInventory().canHold(itemstack) > 0) {
++ ItemEntity item = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemstack);
++ PlayerPickupArrowEvent event = new PlayerPickupArrowEvent((org.bukkit.entity.Player) entity.getBukkitEntity(), new org.bukkit.craftbukkit.entity.CraftItem(this.level().getCraftServer(), item), (org.bukkit.entity.AbstractArrow) this.getBukkitEntity());
++ // event.setCancelled(!entityhuman.canPickUpLoot); TODO
++ this.level().getCraftServer().getPluginManager().callEvent(event);
++
++ if (event.isCancelled()) {
++ return;
++ }
++ itemstack = item.getItem();
++ }
++
++ if ((this.pickup == AbstractArrow.Pickup.ALLOWED && entity.getInventory().add(itemstack)) || (this.pickup == AbstractArrow.Pickup.CREATIVE_ONLY && entity.getAbilities().instabuild)) {
++ // CraftBukkit end
+ entity.take(this, 1);
+ this.discard();
+ }
++
+ }
+ }
+
+@@ -569,7 +624,7 @@
+ }
+
+ @Override
+- protected float getEyeHeight(Pose pose, EntityDimensions size) {
++ protected float getEyeHeight(EntityPose pose, EntityDimensions size) {
+ return 0.13F;
+ }
+
+@@ -578,47 +633,53 @@
+ }
+
+ public void setPierceLevel(byte pierceLevel) {
+- this.entityData.set(PIERCE_LEVEL, pierceLevel);
++ this.entityData.set(AbstractArrow.PIERCE_LEVEL, pierceLevel);
+ }
+
+ private void setFlag(int id, boolean value) {
+- byte b = this.entityData.get(ID_FLAGS);
++ byte b0 = (Byte) this.entityData.get(AbstractArrow.ID_FLAGS);
++
+ if (value) {
+- this.entityData.set(ID_FLAGS, (byte)(b | id));
++ this.entityData.set(AbstractArrow.ID_FLAGS, (byte) (b0 | id));
+ } else {
+- this.entityData.set(ID_FLAGS, (byte)(b & ~id));
++ this.entityData.set(AbstractArrow.ID_FLAGS, (byte) (b0 & ~id));
+ }
++
+ }
+
+ public boolean isCritArrow() {
+- byte b = this.entityData.get(ID_FLAGS);
+- return (b & 1) != 0;
++ byte b0 = (Byte) this.entityData.get(AbstractArrow.ID_FLAGS);
++
++ return (b0 & 1) != 0;
+ }
+
+ public boolean shotFromCrossbow() {
+- byte b = this.entityData.get(ID_FLAGS);
+- return (b & 4) != 0;
++ byte b0 = (Byte) this.entityData.get(AbstractArrow.ID_FLAGS);
++
++ return (b0 & 4) != 0;
+ }
+
+ public byte getPierceLevel() {
+- return this.entityData.get(PIERCE_LEVEL);
++ return (Byte) this.entityData.get(AbstractArrow.PIERCE_LEVEL);
+ }
+
+ public void setEnchantmentEffectsFromEntity(LivingEntity shooter, float velocity) {
+- int enchantmentLevel = EnchantmentHelper.getEnchantmentLevel(Enchantments.POWER_ARROWS, shooter);
+- int enchantmentLevel1 = EnchantmentHelper.getEnchantmentLevel(Enchantments.PUNCH_ARROWS, shooter);
+- this.setBaseDamage((double)(velocity * 2.0F) + this.random.triangle((double)this.level().getDifficulty().getId() * 0.11, 0.57425));
+- if (enchantmentLevel > 0) {
+- this.setBaseDamage(this.getBaseDamage() + (double)enchantmentLevel * 0.5 + 0.5);
++ int i = EnchantmentHelper.getEnchantmentLevel(Enchantments.POWER_ARROWS, shooter);
++ int j = EnchantmentHelper.getEnchantmentLevel(Enchantments.PUNCH_ARROWS, shooter);
++
++ this.setBaseDamage((double) (velocity * 2.0F) + this.random.triangle((double) this.level().getDifficulty().getId() * 0.11D, 0.57425D));
++ if (i > 0) {
++ this.setBaseDamage(this.getBaseDamage() + (double) i * 0.5D + 0.5D);
+ }
+
+- if (enchantmentLevel1 > 0) {
+- this.setKnockback(enchantmentLevel1);
++ if (j > 0) {
++ this.setKnockback(j);
+ }
+
+ if (EnchantmentHelper.getEnchantmentLevel(Enchantments.FLAMING_ARROWS, shooter) > 0) {
+ this.setSecondsOnFire(100);
+ }
++
+ }
+
+ protected float getWaterInertia() {
+@@ -631,7 +692,7 @@
+ }
+
+ public boolean isNoPhysics() {
+- return !this.level().isClientSide ? this.noPhysics : (this.entityData.get(ID_FLAGS) & 2) != 0;
++ return !this.level().isClientSide ? this.noPhysics : ((Byte) this.entityData.get(AbstractArrow.ID_FLAGS) & 2) != 0;
+ }
+
+ public void setShotFromCrossbow(boolean shotFromCrossbow) {
+@@ -639,10 +700,11 @@
+ }
+
+ public static enum Pickup {
+- DISALLOWED,
+- ALLOWED,
+- CREATIVE_ONLY;
+
++ DISALLOWED, ALLOWED, CREATIVE_ONLY;
++
++ private Pickup() {}
++
+ public static AbstractArrow.Pickup byOrdinal(int ordinal) {
+ if (ordinal < 0 || ordinal > values().length) {
+ ordinal = 0;
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/AbstractHurtingProjectile.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/AbstractHurtingProjectile.java.patch
new file mode 100644
index 0000000000..d1dd2b274f
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/AbstractHurtingProjectile.java.patch
@@ -0,0 +1,276 @@
+--- a/net/minecraft/world/entity/projectile/AbstractHurtingProjectile.java
++++ b/net/minecraft/world/entity/projectile/AbstractHurtingProjectile.java
+@@ -16,56 +16,64 @@
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.phys.HitResult;
+ import net.minecraft.world.phys.Vec3;
++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
+
+ public abstract class AbstractHurtingProjectile extends Projectile {
++
+ public double xPower;
+ public double yPower;
+ public double zPower;
++ public float bukkitYield = 1; // CraftBukkit
++ public boolean isIncendiary = true; // CraftBukkit
+
+ protected AbstractHurtingProjectile(EntityType<? extends AbstractHurtingProjectile> entityType, Level level) {
+ super(entityType, level);
+ }
+
+- protected AbstractHurtingProjectile(EntityType<? extends AbstractHurtingProjectile> entityType, double d, double d1, double d2, Level level) {
+- this(entityType, level);
+- this.setPos(d, d1, d2);
++ protected AbstractHurtingProjectile(EntityType<? extends AbstractHurtingProjectile> entitytypes, double d0, double d1, double d2, Level world) {
++ this(entitytypes, world);
++ this.setPos(d0, d1, d2);
+ }
+
+- public AbstractHurtingProjectile(
+- EntityType<? extends AbstractHurtingProjectile> entityType, double x, double y, double z, double offsetX, double offsetY, double offsetZ, Level level
+- ) {
+- this(entityType, level);
+- this.moveTo(x, y, z, this.getYRot(), this.getXRot());
++ public AbstractHurtingProjectile(EntityType<? extends AbstractHurtingProjectile> entityType, double x, double d1, double y, double d3, double z, double d5, Level offsetX) {
++ this(entityType, offsetX);
++ this.moveTo(x, d1, y, this.getYRot(), this.getXRot());
+ this.reapplyPosition();
+- double squareRoot = Math.sqrt(offsetX * offsetX + offsetY * offsetY + offsetZ * offsetZ);
+- if (squareRoot != 0.0) {
+- this.xPower = offsetX / squareRoot * 0.1;
+- this.yPower = offsetY / squareRoot * 0.1;
+- this.zPower = offsetZ / squareRoot * 0.1;
++ // CraftBukkit start - Added setDirection method
++ this.setDirection(d3, z, d5);
++ }
++
++ public void setDirection(double d3, double d4, double d5) {
++ // CraftBukkit end
++ double d6 = Math.sqrt(d3 * d3 + d4 * d4 + d5 * d5);
++
++ if (d6 != 0.0D) {
++ this.xPower = d3 / d6 * 0.1D;
++ this.yPower = d4 / d6 * 0.1D;
++ this.zPower = d5 / d6 * 0.1D;
+ }
++
+ }
+
+- public AbstractHurtingProjectile(
+- EntityType<? extends AbstractHurtingProjectile> entityType, LivingEntity shooter, double offsetX, double offsetY, double offsetZ, Level level
+- ) {
+- this(entityType, shooter.getX(), shooter.getY(), shooter.getZ(), offsetX, offsetY, offsetZ, level);
++ public AbstractHurtingProjectile(EntityType<? extends AbstractHurtingProjectile> entityType, LivingEntity shooter, double offsetX, double d1, double offsetY, Level world) {
++ this(entityType, shooter.getX(), shooter.getY(), shooter.getZ(), offsetX, d1, offsetY, world);
+ this.setOwner(shooter);
+ this.setRot(shooter.getYRot(), shooter.getXRot());
+ }
+
+ @Override
+- protected void defineSynchedData() {
+- }
++ protected void defineSynchedData() {}
+
+ @Override
+ public boolean shouldRenderAtSqrDistance(double distance) {
+- double d = this.getBoundingBox().getSize() * 4.0;
+- if (Double.isNaN(d)) {
+- d = 4.0;
++ double d1 = this.getBoundingBox().getSize() * 4.0D;
++
++ if (Double.isNaN(d1)) {
++ d1 = 4.0D;
+ }
+
+- d *= 64.0;
+- return distance < d * d;
++ d1 *= 64.0D;
++ return distance < d1 * d1;
+ }
+
+ protected ClipContext.Block getClipType() {
+@@ -74,54 +82,57 @@
+
+ @Override
+ public void tick() {
+- Entity owner = this.getOwner();
+- if (this.level().isClientSide || (owner == null || !owner.isRemoved()) && this.level().hasChunkAt(this.blockPosition())) {
++ Entity entity = this.getOwner();
++
++ if (!this.level().isClientSide && (entity != null && entity.isRemoved() || !this.level().hasChunkAt(this.blockPosition()))) {
++ this.discard();
++ } else {
+ super.tick();
+ if (this.shouldBurn()) {
+ this.setSecondsOnFire(1);
+ }
+
+- HitResult hitResultOnMoveVector = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity, this.getClipType());
+- if (hitResultOnMoveVector.getType() != HitResult.Type.MISS) {
+- this.onHit(hitResultOnMoveVector);
++ HitResult movingobjectposition = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity, this.getClipType());
++
++ if (movingobjectposition.getType() != HitResult.EnumMovingObjectType.MISS) {
++ this.preOnHit(movingobjectposition); // CraftBukkit - projectile hit event
++
++ // CraftBukkit start - Fire ProjectileHitEvent
++ if (this.isRemoved()) {
++ CraftEventFactory.callProjectileHitEvent(this, movingobjectposition);
++ }
++ // CraftBukkit end
+ }
+
+ this.checkInsideBlocks();
+- Vec3 deltaMovement = this.getDeltaMovement();
+- double d = this.getX() + deltaMovement.x;
+- double d1 = this.getY() + deltaMovement.y;
+- double d2 = this.getZ() + deltaMovement.z;
++ Vec3 vec3d = this.getDeltaMovement();
++ double d0 = this.getX() + vec3d.x;
++ double d1 = this.getY() + vec3d.y;
++ double d2 = this.getZ() + vec3d.z;
++
+ ProjectileUtil.rotateTowardsMovement(this, 0.2F);
+- float liquidInertia;
++ float f;
++
+ if (this.isInWater()) {
+- for (int i = 0; i < 4; i++) {
+- float f = 0.25F;
+- this.level()
+- .addParticle(
+- ParticleTypes.BUBBLE,
+- d - deltaMovement.x * 0.25,
+- d1 - deltaMovement.y * 0.25,
+- d2 - deltaMovement.z * 0.25,
+- deltaMovement.x,
+- deltaMovement.y,
+- deltaMovement.z
+- );
++ for (int i = 0; i < 4; ++i) {
++ float f1 = 0.25F;
++
++ this.level().addParticle(ParticleTypes.BUBBLE, d0 - vec3d.x * 0.25D, d1 - vec3d.y * 0.25D, d2 - vec3d.z * 0.25D, vec3d.x, vec3d.y, vec3d.z);
+ }
+
+- liquidInertia = this.getLiquidInertia();
++ f = this.getLiquidInertia();
+ } else {
+- liquidInertia = this.getInertia();
++ f = this.getInertia();
+ }
+
+- this.setDeltaMovement(deltaMovement.add(this.xPower, this.yPower, this.zPower).scale((double)liquidInertia));
+- ParticleOptions trailParticle = this.getTrailParticle();
+- if (trailParticle != null) {
+- this.level().addParticle(trailParticle, d, d1 + 0.5, d2, 0.0, 0.0, 0.0);
++ this.setDeltaMovement(vec3d.add(this.xPower, this.yPower, this.zPower).scale((double) f));
++ ParticleOptions particleparam = this.getTrailParticle();
++
++ if (particleparam != null) {
++ this.level().addParticle(particleparam, d0, d1 + 0.5D, d2, 0.0D, 0.0D, 0.0D);
+ }
+
+- this.setPos(d, d1, d2);
+- } else {
+- this.discard();
++ this.setPos(d0, d1, d2);
+ }
+ }
+
+@@ -157,13 +168,15 @@
+ public void readAdditionalSaveData(CompoundTag compound) {
+ super.readAdditionalSaveData(compound);
+ if (compound.contains("power", 9)) {
+- ListTag list = compound.getList("power", 6);
+- if (list.size() == 3) {
+- this.xPower = list.getDouble(0);
+- this.yPower = list.getDouble(1);
+- this.zPower = list.getDouble(2);
++ ListTag nbttaglist = compound.getList("power", 6);
++
++ if (nbttaglist.size() == 3) {
++ this.xPower = nbttaglist.getDouble(0);
++ this.yPower = nbttaglist.getDouble(1);
++ this.zPower = nbttaglist.getDouble(2);
+ }
+ }
++
+ }
+
+ @Override
+@@ -183,13 +196,20 @@
+ } else {
+ this.markHurt();
+ Entity entity = source.getEntity();
++
+ if (entity != null) {
+ if (!this.level().isClientSide) {
+- Vec3 lookAngle = entity.getLookAngle();
+- this.setDeltaMovement(lookAngle);
+- this.xPower = lookAngle.x * 0.1;
+- this.yPower = lookAngle.y * 0.1;
+- this.zPower = lookAngle.z * 0.1;
++ // CraftBukkit start
++ if (CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, amount, false)) {
++ return false;
++ }
++ // CraftBukkit end
++ Vec3 vec3d = entity.getLookAngle();
++
++ this.setDeltaMovement(vec3d);
++ this.xPower = vec3d.x * 0.1D;
++ this.yPower = vec3d.y * 0.1D;
++ this.zPower = vec3d.z * 0.1D;
+ this.setOwner(entity);
+ }
+
+@@ -207,34 +227,25 @@
+
+ @Override
+ public Packet<ClientGamePacketListener> getAddEntityPacket() {
+- Entity owner = this.getOwner();
+- int i = owner == null ? 0 : owner.getId();
+- return new ClientboundAddEntityPacket(
+- this.getId(),
+- this.getUUID(),
+- this.getX(),
+- this.getY(),
+- this.getZ(),
+- this.getXRot(),
+- this.getYRot(),
+- this.getType(),
+- i,
+- new Vec3(this.xPower, this.yPower, this.zPower),
+- 0.0
+- );
++ Entity entity = this.getOwner();
++ int i = entity == null ? 0 : entity.getId();
++
++ return new ClientboundAddEntityPacket(this.getId(), this.getUUID(), this.getX(), this.getY(), this.getZ(), this.getXRot(), this.getYRot(), this.getType(), i, new Vec3(this.xPower, this.yPower, this.zPower), 0.0D);
+ }
+
+ @Override
+ public void recreateFromPacket(ClientboundAddEntityPacket packet) {
+ super.recreateFromPacket(packet);
+- double xa = packet.getXa();
+- double ya = packet.getYa();
+- double za = packet.getZa();
+- double squareRoot = Math.sqrt(xa * xa + ya * ya + za * za);
+- if (squareRoot != 0.0) {
+- this.xPower = xa / squareRoot * 0.1;
+- this.yPower = ya / squareRoot * 0.1;
+- this.zPower = za / squareRoot * 0.1;
++ double d0 = packet.getXa();
++ double d1 = packet.getYa();
++ double d2 = packet.getZa();
++ double d3 = Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
++
++ if (d3 != 0.0D) {
++ this.xPower = d0 / d3 * 0.1D;
++ this.yPower = d1 / d3 * 0.1D;
++ this.zPower = d2 / d3 * 0.1D;
+ }
++
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/Arrow.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/Arrow.java.patch
new file mode 100644
index 0000000000..4ee848deb3
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/Arrow.java.patch
@@ -0,0 +1,306 @@
+--- a/net/minecraft/world/entity/projectile/Arrow.java
++++ b/net/minecraft/world/entity/projectile/Arrow.java
+@@ -2,6 +2,7 @@
+
+ import com.google.common.collect.Sets;
+ import java.util.Collection;
++import java.util.Iterator;
+ import java.util.Set;
+ import net.minecraft.core.particles.ParticleTypes;
+ import net.minecraft.core.registries.BuiltInRegistries;
+@@ -22,73 +23,89 @@
+ import net.minecraft.world.level.Level;
+
+ public class Arrow extends AbstractArrow {
++
+ private static final int EXPOSED_POTION_DECAY_TIME = 600;
+ private static final int NO_EFFECT_COLOR = -1;
+ private static final EntityDataAccessor<Integer> ID_EFFECT_COLOR = SynchedEntityData.defineId(Arrow.class, EntityDataSerializers.INT);
+ private static final byte EVENT_POTION_PUFF = 0;
+ private static final ItemStack DEFAULT_ARROW_STACK = new ItemStack(Items.ARROW);
+- private Potion potion = Potions.EMPTY;
+- private final Set<MobEffectInstance> effects = Sets.newHashSet();
++ public Potion potion;
++ public final Set<MobEffectInstance> effects;
+ private boolean fixedColor;
+
+ public Arrow(EntityType<? extends Arrow> entityType, Level level) {
+- super(entityType, level, DEFAULT_ARROW_STACK);
++ super(entityType, level, Arrow.DEFAULT_ARROW_STACK);
++ this.potion = Potions.EMPTY;
++ this.effects = Sets.newHashSet();
+ }
+
+- public Arrow(Level level, double d, double d1, double d2, ItemStack itemStack) {
+- super(EntityType.ARROW, d, d1, d2, level, itemStack);
++ public Arrow(Level world, double d0, double d1, double d2, ItemStack itemstack) {
++ super(EntityType.ARROW, d0, d1, d2, world, itemstack);
++ this.potion = Potions.EMPTY;
++ this.effects = Sets.newHashSet();
+ }
+
+- public Arrow(Level level, LivingEntity livingEntity, ItemStack itemStack) {
+- super(EntityType.ARROW, livingEntity, level, itemStack);
++ public Arrow(Level world, LivingEntity entityliving, ItemStack itemstack) {
++ super(EntityType.ARROW, entityliving, world, itemstack);
++ this.potion = Potions.EMPTY;
++ this.effects = Sets.newHashSet();
+ }
+
+ public void setEffectsFromItem(ItemStack stack) {
+ if (stack.is(Items.TIPPED_ARROW)) {
+ this.potion = PotionUtils.getPotion(stack);
+- Collection<MobEffectInstance> customEffects = PotionUtils.getCustomEffects(stack);
+- if (!customEffects.isEmpty()) {
+- for (MobEffectInstance mobEffectInstance : customEffects) {
+- this.effects.add(new MobEffectInstance(mobEffectInstance));
++ Collection<MobEffectInstance> collection = PotionUtils.getCustomEffects(stack);
++
++ if (!collection.isEmpty()) {
++ Iterator iterator = collection.iterator();
++
++ while (iterator.hasNext()) {
++ MobEffectInstance mobeffect = (MobEffectInstance) iterator.next();
++
++ this.effects.add(new MobEffectInstance(mobeffect));
+ }
+ }
+
+- int customColor = getCustomColor(stack);
+- if (customColor == -1) {
++ int i = getCustomColor(stack);
++
++ if (i == -1) {
+ this.updateColor();
+ } else {
+- this.setFixedColor(customColor);
++ this.setFixedColor(i);
+ }
+ } else if (stack.is(Items.ARROW)) {
+ this.potion = Potions.EMPTY;
+ this.effects.clear();
+- this.entityData.set(ID_EFFECT_COLOR, -1);
++ this.entityData.set(Arrow.ID_EFFECT_COLOR, -1);
+ }
++
+ }
+
+ public static int getCustomColor(ItemStack stack) {
+- CompoundTag tag = stack.getTag();
+- return tag != null && tag.contains("CustomPotionColor", 99) ? tag.getInt("CustomPotionColor") : -1;
++ CompoundTag nbttagcompound = stack.getTag();
++
++ return nbttagcompound != null && nbttagcompound.contains("CustomPotionColor", 99) ? nbttagcompound.getInt("CustomPotionColor") : -1;
+ }
+
+- private void updateColor() {
++ public void updateColor() {
+ this.fixedColor = false;
+ if (this.potion == Potions.EMPTY && this.effects.isEmpty()) {
+- this.entityData.set(ID_EFFECT_COLOR, -1);
++ this.entityData.set(Arrow.ID_EFFECT_COLOR, -1);
+ } else {
+- this.entityData.set(ID_EFFECT_COLOR, PotionUtils.getColor(PotionUtils.getAllEffects(this.potion, this.effects)));
++ this.entityData.set(Arrow.ID_EFFECT_COLOR, PotionUtils.getColor((Collection) PotionUtils.getAllEffects(this.potion, this.effects)));
+ }
++
+ }
+
+ public void addEffect(MobEffectInstance effectInstance) {
+ this.effects.add(effectInstance);
+- this.getEntityData().set(ID_EFFECT_COLOR, PotionUtils.getColor(PotionUtils.getAllEffects(this.potion, this.effects)));
++ this.getEntityData().set(Arrow.ID_EFFECT_COLOR, PotionUtils.getColor((Collection) PotionUtils.getAllEffects(this.potion, this.effects)));
+ }
+
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(ID_EFFECT_COLOR, -1);
++ this.entityData.define(Arrow.ID_EFFECT_COLOR, -1);
+ }
+
+ @Override
+@@ -103,33 +120,36 @@
+ this.makeParticle(2);
+ }
+ } else if (this.inGround && this.inGroundTime != 0 && !this.effects.isEmpty() && this.inGroundTime >= 600) {
+- this.level().broadcastEntityEvent(this, (byte)0);
++ this.level().broadcastEntityEvent(this, (byte) 0);
+ this.potion = Potions.EMPTY;
+ this.effects.clear();
+- this.entityData.set(ID_EFFECT_COLOR, -1);
++ this.entityData.set(Arrow.ID_EFFECT_COLOR, -1);
+ }
++
+ }
+
+ private void makeParticle(int particleAmount) {
+- int color = this.getColor();
+- if (color != -1 && particleAmount > 0) {
+- double d = (double)(color >> 16 & 0xFF) / 255.0;
+- double d1 = (double)(color >> 8 & 0xFF) / 255.0;
+- double d2 = (double)(color >> 0 & 0xFF) / 255.0;
++ int j = this.getColor();
+
+- for (int i = 0; i < particleAmount; i++) {
+- this.level().addParticle(ParticleTypes.ENTITY_EFFECT, this.getRandomX(0.5), this.getRandomY(), this.getRandomZ(0.5), d, d1, d2);
++ if (j != -1 && particleAmount > 0) {
++ double d0 = (double) (j >> 16 & 255) / 255.0D;
++ double d1 = (double) (j >> 8 & 255) / 255.0D;
++ double d2 = (double) (j >> 0 & 255) / 255.0D;
++
++ for (int k = 0; k < particleAmount; ++k) {
++ this.level().addParticle(ParticleTypes.ENTITY_EFFECT, this.getRandomX(0.5D), this.getRandomY(), this.getRandomZ(0.5D), d0, d1, d2);
+ }
++
+ }
+ }
+
+ public int getColor() {
+- return this.entityData.get(ID_EFFECT_COLOR);
++ return (Integer) this.entityData.get(Arrow.ID_EFFECT_COLOR);
+ }
+
+- private void setFixedColor(int fixedColor) {
++ public void setFixedColor(int fixedColor) {
+ this.fixedColor = true;
+- this.entityData.set(ID_EFFECT_COLOR, fixedColor);
++ this.entityData.set(Arrow.ID_EFFECT_COLOR, fixedColor);
+ }
+
+ @Override
+@@ -144,14 +164,18 @@
+ }
+
+ if (!this.effects.isEmpty()) {
+- ListTag list = new ListTag();
++ ListTag nbttaglist = new ListTag();
++ Iterator iterator = this.effects.iterator();
+
+- for (MobEffectInstance mobEffectInstance : this.effects) {
+- list.add(mobEffectInstance.save(new CompoundTag()));
++ while (iterator.hasNext()) {
++ MobEffectInstance mobeffect = (MobEffectInstance) iterator.next();
++
++ nbttaglist.add(mobeffect.save(new CompoundTag()));
+ }
+
+- compound.put("custom_potion_effects", list);
++ compound.put("custom_potion_effects", nbttaglist);
+ }
++
+ }
+
+ @Override
+@@ -161,8 +185,12 @@
+ this.potion = PotionUtils.getPotion(compound);
+ }
+
+- for (MobEffectInstance mobEffectInstance : PotionUtils.getCustomEffects(compound)) {
+- this.addEffect(mobEffectInstance);
++ Iterator iterator = PotionUtils.getCustomEffects(compound).iterator();
++
++ while (iterator.hasNext()) {
++ MobEffectInstance mobeffect = (MobEffectInstance) iterator.next();
++
++ this.addEffect(mobeffect);
+ }
+
+ if (compound.contains("Color", 99)) {
+@@ -170,64 +198,69 @@
+ } else {
+ this.updateColor();
+ }
++
+ }
+
+ @Override
+ protected void doPostHurtEffects(LivingEntity living) {
+ super.doPostHurtEffects(living);
+- Entity effectSource = this.getEffectSource();
++ Entity entity = this.getEffectSource();
++ Iterator iterator = this.potion.getEffects().iterator();
+
+- for (MobEffectInstance mobEffectInstance : this.potion.getEffects()) {
+- living.addEffect(
+- new MobEffectInstance(
+- mobEffectInstance.getEffect(),
+- Math.max(mobEffectInstance.mapDuration(i -> i / 8), 1),
+- mobEffectInstance.getAmplifier(),
+- mobEffectInstance.isAmbient(),
+- mobEffectInstance.isVisible()
+- ),
+- effectSource
+- );
++ MobEffectInstance mobeffect;
++
++ while (iterator.hasNext()) {
++ mobeffect = (MobEffectInstance) iterator.next();
++ living.addEffect(new MobEffectInstance(mobeffect.getEffect(), Math.max(mobeffect.mapDuration((i) -> {
++ return i / 8;
++ }), 1), mobeffect.getAmplifier(), mobeffect.isAmbient(), mobeffect.isVisible()), entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ARROW); // CraftBukkit
+ }
+
+ if (!this.effects.isEmpty()) {
+- for (MobEffectInstance mobEffectInstance : this.effects) {
+- living.addEffect(mobEffectInstance, effectSource);
++ iterator = this.effects.iterator();
++
++ while (iterator.hasNext()) {
++ mobeffect = (MobEffectInstance) iterator.next();
++ living.addEffect(mobeffect, entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ARROW); // CraftBukkit
+ }
+ }
++
+ }
+
+ @Override
+ protected ItemStack getPickupItem() {
+- ItemStack itemStack = super.getPickupItem();
++ ItemStack itemstack = super.getPickupItem();
++
+ if (this.effects.isEmpty() && this.potion == Potions.EMPTY) {
+- return itemStack;
++ return itemstack;
+ } else {
+- PotionUtils.setPotion(itemStack, this.potion);
+- PotionUtils.setCustomEffects(itemStack, this.effects);
++ PotionUtils.setPotion(itemstack, this.potion);
++ PotionUtils.setCustomEffects(itemstack, this.effects);
+ if (this.fixedColor) {
+- itemStack.getOrCreateTag().putInt("CustomPotionColor", this.getColor());
++ itemstack.getOrCreateTag().putInt("CustomPotionColor", this.getColor());
+ }
+
+- return itemStack;
++ return itemstack;
+ }
+ }
+
+ @Override
+ public void handleEntityEvent(byte id) {
+ if (id == 0) {
+- int color = this.getColor();
+- if (color != -1) {
+- double d = (double)(color >> 16 & 0xFF) / 255.0;
+- double d1 = (double)(color >> 8 & 0xFF) / 255.0;
+- double d2 = (double)(color >> 0 & 0xFF) / 255.0;
++ int i = this.getColor();
+
+- for (int i = 0; i < 20; i++) {
+- this.level().addParticle(ParticleTypes.ENTITY_EFFECT, this.getRandomX(0.5), this.getRandomY(), this.getRandomZ(0.5), d, d1, d2);
++ if (i != -1) {
++ double d0 = (double) (i >> 16 & 255) / 255.0D;
++ double d1 = (double) (i >> 8 & 255) / 255.0D;
++ double d2 = (double) (i >> 0 & 255) / 255.0D;
++
++ for (int j = 0; j < 20; ++j) {
++ this.level().addParticle(ParticleTypes.ENTITY_EFFECT, this.getRandomX(0.5D), this.getRandomY(), this.getRandomZ(0.5D), d0, d1, d2);
+ }
+ }
+ } else {
+ super.handleEntityEvent(id);
+ }
++
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/EvokerFangs.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/EvokerFangs.java.patch
new file mode 100644
index 0000000000..904fa91739
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/EvokerFangs.java.patch
@@ -0,0 +1,192 @@
+--- a/net/minecraft/world/entity/projectile/EvokerFangs.java
++++ b/net/minecraft/world/entity/projectile/EvokerFangs.java
+@@ -1,5 +1,7 @@
+ package net.minecraft.world.entity.projectile;
+
++import java.util.Iterator;
++import java.util.List;
+ import java.util.UUID;
+ import javax.annotation.Nullable;
+ import net.minecraft.core.particles.ParticleTypes;
+@@ -13,12 +15,13 @@
+ import net.minecraft.world.level.Level;
+
+ public class EvokerFangs extends Entity implements TraceableEntity {
++
+ public static final int ATTACK_DURATION = 20;
+ public static final int LIFE_OFFSET = 2;
+ public static final int ATTACK_TRIGGER_TICKS = 14;
+- private int warmupDelayTicks;
++ public int warmupDelayTicks;
+ private boolean sentSpikeEvent;
+- private int lifeTicks = 22;
++ private int lifeTicks;
+ private boolean clientSideAttackStarted;
+ @Nullable
+ private LivingEntity owner;
+@@ -27,19 +30,19 @@
+
+ public EvokerFangs(EntityType<? extends EvokerFangs> entityType, Level level) {
+ super(entityType, level);
++ this.lifeTicks = 22;
+ }
+
+- public EvokerFangs(Level level, double x, double y, double z, float yRot, int warmupDelay, LivingEntity owner) {
++ public EvokerFangs(Level level, double x, double d1, double y, float f, int z, LivingEntity entityliving) {
+ this(EntityType.EVOKER_FANGS, level);
+- this.warmupDelayTicks = warmupDelay;
+- this.setOwner(owner);
+- this.setYRot(yRot * (180.0F / (float)Math.PI));
+- this.setPos(x, y, z);
++ this.warmupDelayTicks = z;
++ this.setOwner(entityliving);
++ this.setYRot(f * 57.295776F);
++ this.setPos(x, d1, y);
+ }
+
+ @Override
+- protected void defineSynchedData() {
+- }
++ protected void defineSynchedData() {}
+
+ public void setOwner(@Nullable LivingEntity owner) {
+ this.owner = owner;
+@@ -50,9 +53,10 @@
+ @Override
+ public LivingEntity getOwner() {
+ if (this.owner == null && this.ownerUUID != null && this.level() instanceof ServerLevel) {
+- Entity entity = ((ServerLevel)this.level()).getEntity(this.ownerUUID);
++ Entity entity = ((ServerLevel) this.level()).getEntity(this.ownerUUID);
++
+ if (entity instanceof LivingEntity) {
+- this.owner = (LivingEntity)entity;
++ this.owner = (LivingEntity) entity;
+ }
+ }
+
+@@ -65,6 +69,7 @@
+ if (compound.hasUUID("Owner")) {
+ this.ownerUUID = compound.getUUID("Owner");
+ }
++
+ }
+
+ @Override
+@@ -73,6 +78,7 @@
+ if (this.ownerUUID != null) {
+ compound.putUUID("Owner", this.ownerUUID);
+ }
++
+ }
+
+ @Override
+@@ -80,28 +86,34 @@
+ super.tick();
+ if (this.level().isClientSide) {
+ if (this.clientSideAttackStarted) {
+- this.lifeTicks--;
++ --this.lifeTicks;
+ if (this.lifeTicks == 14) {
+- for (int i = 0; i < 12; i++) {
+- double d = this.getX() + (this.random.nextDouble() * 2.0 - 1.0) * (double)this.getBbWidth() * 0.5;
+- double d1 = this.getY() + 0.05 + this.random.nextDouble();
+- double d2 = this.getZ() + (this.random.nextDouble() * 2.0 - 1.0) * (double)this.getBbWidth() * 0.5;
+- double d3 = (this.random.nextDouble() * 2.0 - 1.0) * 0.3;
+- double d4 = 0.3 + this.random.nextDouble() * 0.3;
+- double d5 = (this.random.nextDouble() * 2.0 - 1.0) * 0.3;
+- this.level().addParticle(ParticleTypes.CRIT, d, d1 + 1.0, d2, d3, d4, d5);
++ for (int i = 0; i < 12; ++i) {
++ double d0 = this.getX() + (this.random.nextDouble() * 2.0D - 1.0D) * (double) this.getBbWidth() * 0.5D;
++ double d1 = this.getY() + 0.05D + this.random.nextDouble();
++ double d2 = this.getZ() + (this.random.nextDouble() * 2.0D - 1.0D) * (double) this.getBbWidth() * 0.5D;
++ double d3 = (this.random.nextDouble() * 2.0D - 1.0D) * 0.3D;
++ double d4 = 0.3D + this.random.nextDouble() * 0.3D;
++ double d5 = (this.random.nextDouble() * 2.0D - 1.0D) * 0.3D;
++
++ this.level().addParticle(ParticleTypes.CRIT, d0, d1 + 1.0D, d2, d3, d4, d5);
+ }
+ }
+ }
+ } else if (--this.warmupDelayTicks < 0) {
+ if (this.warmupDelayTicks == -8) {
+- for (LivingEntity livingEntity : this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox().inflate(0.2, 0.0, 0.2))) {
+- this.dealDamageTo(livingEntity);
++ List<LivingEntity> list = this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox().inflate(0.2D, 0.0D, 0.2D));
++ Iterator iterator = list.iterator();
++
++ while (iterator.hasNext()) {
++ LivingEntity entityliving = (LivingEntity) iterator.next();
++
++ this.dealDamageTo(entityliving);
+ }
+ }
+
+ if (!this.sentSpikeEvent) {
+- this.level().broadcastEntityEvent(this, (byte)4);
++ this.level().broadcastEntityEvent(this, (byte) 4);
+ this.sentSpikeEvent = true;
+ }
+
+@@ -109,20 +121,25 @@
+ this.discard();
+ }
+ }
++
+ }
+
+ private void dealDamageTo(LivingEntity target) {
+- LivingEntity owner = this.getOwner();
+- if (target.isAlive() && !target.isInvulnerable() && target != owner) {
+- if (owner == null) {
++ LivingEntity entityliving1 = this.getOwner();
++
++ if (target.isAlive() && !target.isInvulnerable() && target != entityliving1) {
++ if (entityliving1 == null) {
++ org.bukkit.craftbukkit.event.CraftEventFactory.entityDamage = this; // CraftBukkit
+ target.hurt(this.damageSources().magic(), 6.0F);
++ org.bukkit.craftbukkit.event.CraftEventFactory.entityDamage = null; // CraftBukkit
+ } else {
+- if (owner.isAlliedTo(target)) {
++ if (entityliving1.isAlliedTo((Entity) target)) {
+ return;
+ }
+
+- target.hurt(this.damageSources().indirectMagic(this, owner), 6.0F);
++ target.hurt(this.damageSources().indirectMagic(this, entityliving1), 6.0F);
+ }
++
+ }
+ }
+
+@@ -132,19 +149,10 @@
+ if (id == 4) {
+ this.clientSideAttackStarted = true;
+ if (!this.isSilent()) {
+- this.level()
+- .playLocalSound(
+- this.getX(),
+- this.getY(),
+- this.getZ(),
+- SoundEvents.EVOKER_FANGS_ATTACK,
+- this.getSoundSource(),
+- 1.0F,
+- this.random.nextFloat() * 0.2F + 0.85F,
+- false
+- );
++ this.level().playLocalSound(this.getX(), this.getY(), this.getZ(), SoundEvents.EVOKER_FANGS_ATTACK, this.getSoundSource(), 1.0F, this.random.nextFloat() * 0.2F + 0.85F, false);
+ }
+ }
++
+ }
+
+ public float getAnimationProgress(float partialTicks) {
+@@ -152,7 +160,8 @@
+ return 0.0F;
+ } else {
+ int i = this.lifeTicks - 2;
+- return i <= 0 ? 1.0F : 1.0F - ((float)i - partialTicks) / 20.0F;
++
++ return i <= 0 ? 1.0F : 1.0F - ((float) i - partialTicks) / 20.0F;
+ }
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/EyeOfEnder.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/EyeOfEnder.java.patch
new file mode 100644
index 0000000000..c01a4e5a26
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/EyeOfEnder.java.patch
@@ -0,0 +1,245 @@
+--- a/net/minecraft/world/entity/projectile/EyeOfEnder.java
++++ b/net/minecraft/world/entity/projectile/EyeOfEnder.java
+@@ -17,68 +17,73 @@
+ import net.minecraft.world.phys.Vec3;
+
+ public class EyeOfEnder extends Entity implements ItemSupplier {
++
+ private static final EntityDataAccessor<ItemStack> DATA_ITEM_STACK = SynchedEntityData.defineId(EyeOfEnder.class, EntityDataSerializers.ITEM_STACK);
+- private double tx;
+- private double ty;
+- private double tz;
+- private int life;
+- private boolean surviveAfterDeath;
++ public double tx;
++ public double ty;
++ public double tz;
++ public int life;
++ public boolean surviveAfterDeath;
+
+ public EyeOfEnder(EntityType<? extends EyeOfEnder> entityType, Level level) {
+ super(entityType, level);
+ }
+
+- public EyeOfEnder(Level level, double x, double y, double z) {
++ public EyeOfEnder(Level level, double x, double d1, double y) {
+ this(EntityType.EYE_OF_ENDER, level);
+- this.setPos(x, y, z);
++ this.setPos(x, d1, y);
+ }
+
+ public void setItem(ItemStack stack) {
+- if (!stack.is(Items.ENDER_EYE) || stack.hasTag()) {
+- this.getEntityData().set(DATA_ITEM_STACK, stack.copyWithCount(1));
++ if (true || !stack.is(Items.ENDER_EYE) || stack.hasTag()) { // CraftBukkit - always allow item changing
++ this.getEntityData().set(EyeOfEnder.DATA_ITEM_STACK, stack.copyWithCount(1));
+ }
++
+ }
+
+ private ItemStack getItemRaw() {
+- return this.getEntityData().get(DATA_ITEM_STACK);
++ return (ItemStack) this.getEntityData().get(EyeOfEnder.DATA_ITEM_STACK);
+ }
+
+ @Override
+ public ItemStack getItem() {
+- ItemStack itemRaw = this.getItemRaw();
+- return itemRaw.isEmpty() ? new ItemStack(Items.ENDER_EYE) : itemRaw;
++ ItemStack itemstack = this.getItemRaw();
++
++ return itemstack.isEmpty() ? new ItemStack(Items.ENDER_EYE) : itemstack;
+ }
+
+ @Override
+ protected void defineSynchedData() {
+- this.getEntityData().define(DATA_ITEM_STACK, ItemStack.EMPTY);
++ this.getEntityData().define(EyeOfEnder.DATA_ITEM_STACK, ItemStack.EMPTY);
+ }
+
+ @Override
+ public boolean shouldRenderAtSqrDistance(double distance) {
+- double d = this.getBoundingBox().getSize() * 4.0;
+- if (Double.isNaN(d)) {
+- d = 4.0;
++ double d1 = this.getBoundingBox().getSize() * 4.0D;
++
++ if (Double.isNaN(d1)) {
++ d1 = 4.0D;
+ }
+
+- d *= 64.0;
+- return distance < d * d;
++ d1 *= 64.0D;
++ return distance < d1 * d1;
+ }
+
+ public void signalTo(BlockPos pos) {
+- double d = (double)pos.getX();
+- int y = pos.getY();
+- double d1 = (double)pos.getZ();
+- double d2 = d - this.getX();
++ double d0 = (double) pos.getX();
++ int i = pos.getY();
++ double d1 = (double) pos.getZ();
++ double d2 = d0 - this.getX();
+ double d3 = d1 - this.getZ();
+- double squareRoot = Math.sqrt(d2 * d2 + d3 * d3);
+- if (squareRoot > 12.0) {
+- this.tx = this.getX() + d2 / squareRoot * 12.0;
+- this.tz = this.getZ() + d3 / squareRoot * 12.0;
+- this.ty = this.getY() + 8.0;
++ double d4 = Math.sqrt(d2 * d2 + d3 * d3);
++
++ if (d4 > 12.0D) {
++ this.tx = this.getX() + d2 / d4 * 12.0D;
++ this.tz = this.getZ() + d3 / d4 * 12.0D;
++ this.ty = this.getY() + 8.0D;
+ } else {
+- this.tx = d;
+- this.ty = (double)y;
++ this.tx = d0;
++ this.ty = (double) i;
+ this.tz = d1;
+ }
+
+@@ -87,74 +92,62 @@
+ }
+
+ @Override
+- public void lerpMotion(double x, double y, double z) {
+- this.setDeltaMovement(x, y, z);
++ public void lerpMotion(double x, double d1, double y) {
++ this.setDeltaMovement(x, d1, y);
+ if (this.xRotO == 0.0F && this.yRotO == 0.0F) {
+- double squareRoot = Math.sqrt(x * x + z * z);
+- this.setYRot((float)(Mth.atan2(x, z) * 180.0F / (float)Math.PI));
+- this.setXRot((float)(Mth.atan2(y, squareRoot) * 180.0F / (float)Math.PI));
++ double d3 = Math.sqrt(x * x + y * y);
++
++ this.setYRot((float) (Mth.atan2(x, y) * 57.2957763671875D));
++ this.setXRot((float) (Mth.atan2(d1, d3) * 57.2957763671875D));
+ this.yRotO = this.getYRot();
+ this.xRotO = this.getXRot();
+ }
++
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+- Vec3 deltaMovement = this.getDeltaMovement();
+- double d = this.getX() + deltaMovement.x;
+- double d1 = this.getY() + deltaMovement.y;
+- double d2 = this.getZ() + deltaMovement.z;
+- double d3 = deltaMovement.horizontalDistance();
+- this.setXRot(Projectile.lerpRotation(this.xRotO, (float)(Mth.atan2(deltaMovement.y, d3) * 180.0F / (float)Math.PI)));
+- this.setYRot(Projectile.lerpRotation(this.yRotO, (float)(Mth.atan2(deltaMovement.x, deltaMovement.z) * 180.0F / (float)Math.PI)));
++ Vec3 vec3d = this.getDeltaMovement();
++ double d0 = this.getX() + vec3d.x;
++ double d1 = this.getY() + vec3d.y;
++ double d2 = this.getZ() + vec3d.z;
++ double d3 = vec3d.horizontalDistance();
++
++ this.setXRot(Projectile.lerpRotation(this.xRotO, (float) (Mth.atan2(vec3d.y, d3) * 57.2957763671875D)));
++ this.setYRot(Projectile.lerpRotation(this.yRotO, (float) (Mth.atan2(vec3d.x, vec3d.z) * 57.2957763671875D)));
+ if (!this.level().isClientSide) {
+- double d4 = this.tx - d;
++ double d4 = this.tx - d0;
+ double d5 = this.tz - d2;
+- float f = (float)Math.sqrt(d4 * d4 + d5 * d5);
+- float f1 = (float)Mth.atan2(d5, d4);
+- double d6 = Mth.lerp(0.0025, d3, (double)f);
+- double d7 = deltaMovement.y;
++ float f = (float) Math.sqrt(d4 * d4 + d5 * d5);
++ float f1 = (float) Mth.atan2(d5, d4);
++ double d6 = Mth.lerp(0.0025D, d3, (double) f);
++ double d7 = vec3d.y;
++
+ if (f < 1.0F) {
+- d6 *= 0.8;
+- d7 *= 0.8;
++ d6 *= 0.8D;
++ d7 *= 0.8D;
+ }
+
+ int i = this.getY() < this.ty ? 1 : -1;
+- deltaMovement = new Vec3(Math.cos((double)f1) * d6, d7 + ((double)i - d7) * 0.015F, Math.sin((double)f1) * d6);
+- this.setDeltaMovement(deltaMovement);
++
++ vec3d = new Vec3(Math.cos((double) f1) * d6, d7 + ((double) i - d7) * 0.014999999664723873D, Math.sin((double) f1) * d6);
++ this.setDeltaMovement(vec3d);
+ }
+
+ float f2 = 0.25F;
++
+ if (this.isInWater()) {
+- for (int i1 = 0; i1 < 4; i1++) {
+- this.level()
+- .addParticle(
+- ParticleTypes.BUBBLE,
+- d - deltaMovement.x * 0.25,
+- d1 - deltaMovement.y * 0.25,
+- d2 - deltaMovement.z * 0.25,
+- deltaMovement.x,
+- deltaMovement.y,
+- deltaMovement.z
+- );
++ for (int j = 0; j < 4; ++j) {
++ this.level().addParticle(ParticleTypes.BUBBLE, d0 - vec3d.x * 0.25D, d1 - vec3d.y * 0.25D, d2 - vec3d.z * 0.25D, vec3d.x, vec3d.y, vec3d.z);
+ }
+ } else {
+- this.level()
+- .addParticle(
+- ParticleTypes.PORTAL,
+- d - deltaMovement.x * 0.25 + this.random.nextDouble() * 0.6 - 0.3,
+- d1 - deltaMovement.y * 0.25 - 0.5,
+- d2 - deltaMovement.z * 0.25 + this.random.nextDouble() * 0.6 - 0.3,
+- deltaMovement.x,
+- deltaMovement.y,
+- deltaMovement.z
+- );
++ this.level().addParticle(ParticleTypes.PORTAL, d0 - vec3d.x * 0.25D + this.random.nextDouble() * 0.6D - 0.3D, d1 - vec3d.y * 0.25D - 0.5D, d2 - vec3d.z * 0.25D + this.random.nextDouble() * 0.6D - 0.3D, vec3d.x, vec3d.y, vec3d.z);
+ }
+
+ if (!this.level().isClientSide) {
+- this.setPos(d, d1, d2);
+- this.life++;
++ this.setPos(d0, d1, d2);
++ ++this.life;
+ if (this.life > 80 && !this.level().isClientSide) {
+ this.playSound(SoundEvents.ENDER_EYE_DEATH, 1.0F, 1.0F);
+ this.discard();
+@@ -165,22 +158,26 @@
+ }
+ }
+ } else {
+- this.setPosRaw(d, d1, d2);
++ this.setPosRaw(d0, d1, d2);
+ }
++
+ }
+
+ @Override
+ public void addAdditionalSaveData(CompoundTag compound) {
+- ItemStack itemRaw = this.getItemRaw();
+- if (!itemRaw.isEmpty()) {
+- compound.put("Item", itemRaw.save(new CompoundTag()));
++ ItemStack itemstack = this.getItemRaw();
++
++ if (!itemstack.isEmpty()) {
++ compound.put("Item", itemstack.save(new CompoundTag()));
+ }
++
+ }
+
+ @Override
+ public void readAdditionalSaveData(CompoundTag compound) {
+- ItemStack itemStack = ItemStack.of(compound.getCompound("Item"));
+- this.setItem(itemStack);
++ ItemStack itemstack = ItemStack.of(compound.getCompound("Item"));
++
++ if (!itemstack.isEmpty()) this.setItem(itemstack); // CraftBukkit - SPIGOT-6103 summon, see also SPIGOT-5474
+ }
+
+ @Override
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/Fireball.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/Fireball.java.patch
new file mode 100644
index 0000000000..354ae6ed1d
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/Fireball.java.patch
@@ -0,0 +1,78 @@
+--- a/net/minecraft/world/entity/projectile/Fireball.java
++++ b/net/minecraft/world/entity/projectile/Fireball.java
+@@ -11,54 +11,60 @@
+ import net.minecraft.world.level.Level;
+
+ public abstract class Fireball extends AbstractHurtingProjectile implements ItemSupplier {
++
+ private static final EntityDataAccessor<ItemStack> DATA_ITEM_STACK = SynchedEntityData.defineId(Fireball.class, EntityDataSerializers.ITEM_STACK);
+
+ public Fireball(EntityType<? extends Fireball> entityType, Level level) {
+ super(entityType, level);
+ }
+
+- public Fireball(EntityType<? extends Fireball> entityType, double x, double y, double z, double offsetX, double offsetY, double offsetZ, Level level) {
+- super(entityType, x, y, z, offsetX, offsetY, offsetZ, level);
++ public Fireball(EntityType<? extends Fireball> entityType, double x, double d1, double y, double d3, double z, double d5, Level offsetX) {
++ super(entityType, x, d1, y, d3, z, d5, offsetX);
+ }
+
+- public Fireball(EntityType<? extends Fireball> entityType, LivingEntity shooter, double offsetX, double offsetY, double offsetZ, Level level) {
+- super(entityType, shooter, offsetX, offsetY, offsetZ, level);
++ public Fireball(EntityType<? extends Fireball> entityType, LivingEntity shooter, double offsetX, double d1, double offsetY, Level world) {
++ super(entityType, shooter, offsetX, d1, offsetY, world);
+ }
+
+ public void setItem(ItemStack stack) {
+ if (!stack.is(Items.FIRE_CHARGE) || stack.hasTag()) {
+- this.getEntityData().set(DATA_ITEM_STACK, stack.copyWithCount(1));
++ this.getEntityData().set(Fireball.DATA_ITEM_STACK, stack.copyWithCount(1));
+ }
++
+ }
+
+- protected ItemStack getItemRaw() {
+- return this.getEntityData().get(DATA_ITEM_STACK);
++ public ItemStack getItemRaw() {
++ return (ItemStack) this.getEntityData().get(Fireball.DATA_ITEM_STACK);
+ }
+
+ @Override
+ public ItemStack getItem() {
+- ItemStack itemRaw = this.getItemRaw();
+- return itemRaw.isEmpty() ? new ItemStack(Items.FIRE_CHARGE) : itemRaw;
++ ItemStack itemstack = this.getItemRaw();
++
++ return itemstack.isEmpty() ? new ItemStack(Items.FIRE_CHARGE) : itemstack;
+ }
+
+ @Override
+ protected void defineSynchedData() {
+- this.getEntityData().define(DATA_ITEM_STACK, ItemStack.EMPTY);
++ this.getEntityData().define(Fireball.DATA_ITEM_STACK, ItemStack.EMPTY);
+ }
+
+ @Override
+ public void addAdditionalSaveData(CompoundTag compound) {
+ super.addAdditionalSaveData(compound);
+- ItemStack itemRaw = this.getItemRaw();
+- if (!itemRaw.isEmpty()) {
+- compound.put("Item", itemRaw.save(new CompoundTag()));
++ ItemStack itemstack = this.getItemRaw();
++
++ if (!itemstack.isEmpty()) {
++ compound.put("Item", itemstack.save(new CompoundTag()));
+ }
++
+ }
+
+ @Override
+ public void readAdditionalSaveData(CompoundTag compound) {
+ super.readAdditionalSaveData(compound);
+- ItemStack itemStack = ItemStack.of(compound.getCompound("Item"));
+- this.setItem(itemStack);
++ ItemStack itemstack = ItemStack.of(compound.getCompound("Item"));
++
++ if (!itemstack.isEmpty()) this.setItem(itemstack); // CraftBukkit - SPIGOT-5474 probably came from bugged earlier versions
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch
new file mode 100644
index 0000000000..6a80061276
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch
@@ -0,0 +1,445 @@
+--- a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
++++ b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java
+@@ -1,5 +1,7 @@
+ package net.minecraft.world.entity.projectile;
+
++import java.util.Iterator;
++import java.util.List;
+ import java.util.OptionalInt;
+ import javax.annotation.Nullable;
+ import net.minecraft.core.BlockPos;
+@@ -13,8 +15,9 @@
+ import net.minecraft.sounds.SoundSource;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMoveType;
+ import net.minecraft.world.entity.LivingEntity;
+-import net.minecraft.world.entity.MoverType;
++import net.minecraft.world.entity.player.Player;
+ import net.minecraft.world.item.ItemStack;
+ import net.minecraft.world.item.Items;
+ import net.minecraft.world.level.ClipContext;
+@@ -24,159 +27,149 @@
+ import net.minecraft.world.phys.EntityHitResult;
+ import net.minecraft.world.phys.HitResult;
+ import net.minecraft.world.phys.Vec3;
++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
+
+ public class FireworkRocketEntity extends Projectile implements ItemSupplier {
+- private static final EntityDataAccessor<ItemStack> DATA_ID_FIREWORKS_ITEM = SynchedEntityData.defineId(
+- FireworkRocketEntity.class, EntityDataSerializers.ITEM_STACK
+- );
+- private static final EntityDataAccessor<OptionalInt> DATA_ATTACHED_TO_TARGET = SynchedEntityData.defineId(
+- FireworkRocketEntity.class, EntityDataSerializers.OPTIONAL_UNSIGNED_INT
+- );
+- private static final EntityDataAccessor<Boolean> DATA_SHOT_AT_ANGLE = SynchedEntityData.defineId(FireworkRocketEntity.class, EntityDataSerializers.BOOLEAN);
+- private int life;
+- private int lifetime;
++
++ public static final EntityDataAccessor<ItemStack> DATA_ID_FIREWORKS_ITEM = SynchedEntityData.defineId(FireworkRocketEntity.class, EntityDataSerializers.ITEM_STACK);
++ private static final EntityDataAccessor<OptionalInt> DATA_ATTACHED_TO_TARGET = SynchedEntityData.defineId(FireworkRocketEntity.class, EntityDataSerializers.OPTIONAL_UNSIGNED_INT);
++ public static final EntityDataAccessor<Boolean> DATA_SHOT_AT_ANGLE = SynchedEntityData.defineId(FireworkRocketEntity.class, EntityDataSerializers.BOOLEAN);
++ public int life;
++ public int lifetime;
+ @Nullable
+- private LivingEntity attachedToEntity;
++ public LivingEntity attachedToEntity;
+
+ public FireworkRocketEntity(EntityType<? extends FireworkRocketEntity> entityType, Level level) {
+ super(entityType, level);
+ }
+
+- public FireworkRocketEntity(Level level, double x, double y, double z, ItemStack stack) {
++ public FireworkRocketEntity(Level level, double x, double d1, double y, ItemStack itemstack) {
+ super(EntityType.FIREWORK_ROCKET, level);
+ this.life = 0;
+- this.setPos(x, y, z);
++ this.setPos(x, d1, y);
+ int i = 1;
+- if (!stack.isEmpty() && stack.hasTag()) {
+- this.entityData.set(DATA_ID_FIREWORKS_ITEM, stack.copy());
+- i += stack.getOrCreateTagElement("Fireworks").getByte("Flight");
++
++ if (!itemstack.isEmpty() && itemstack.hasTag()) {
++ this.entityData.set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, itemstack.copy());
++ i += itemstack.getOrCreateTagElement("Fireworks").getByte("Flight");
+ }
+
+- this.setDeltaMovement(this.random.triangle(0.0, 0.002297), 0.05, this.random.triangle(0.0, 0.002297));
++ this.setDeltaMovement(this.random.triangle(0.0D, 0.002297D), 0.05D, this.random.triangle(0.0D, 0.002297D));
+ this.lifetime = 10 * i + this.random.nextInt(6) + this.random.nextInt(7);
+ }
+
+- public FireworkRocketEntity(Level level, @Nullable Entity shooter, double x, double y, double z, ItemStack stack) {
+- this(level, x, y, z, stack);
++ public FireworkRocketEntity(Level level, @Nullable Entity shooter, double x, double d1, double y, ItemStack itemstack) {
++ this(level, x, d1, y, itemstack);
+ this.setOwner(shooter);
+ }
+
+ public FireworkRocketEntity(Level level, ItemStack stack, LivingEntity shooter) {
+ this(level, shooter, shooter.getX(), shooter.getY(), shooter.getZ(), stack);
+- this.entityData.set(DATA_ATTACHED_TO_TARGET, OptionalInt.of(shooter.getId()));
++ this.entityData.set(FireworkRocketEntity.DATA_ATTACHED_TO_TARGET, OptionalInt.of(shooter.getId()));
+ this.attachedToEntity = shooter;
+ }
+
+- public FireworkRocketEntity(Level level, ItemStack stack, double x, double y, double z, boolean shotAtAngle) {
+- this(level, x, y, z, stack);
+- this.entityData.set(DATA_SHOT_AT_ANGLE, shotAtAngle);
++ public FireworkRocketEntity(Level level, ItemStack stack, double x, double d1, double y, boolean flag) {
++ this(level, x, d1, y, stack);
++ this.entityData.set(FireworkRocketEntity.DATA_SHOT_AT_ANGLE, flag);
+ }
+
+- public FireworkRocketEntity(Level level, ItemStack stack, Entity shooter, double x, double y, double z, boolean shotAtAngle) {
+- this(level, stack, x, y, z, shotAtAngle);
++ public FireworkRocketEntity(Level level, ItemStack stack, Entity shooter, double x, double d1, double y, boolean flag) {
++ this(level, stack, x, d1, y, flag);
+ this.setOwner(shooter);
+ }
+
+ @Override
+ protected void defineSynchedData() {
+- this.entityData.define(DATA_ID_FIREWORKS_ITEM, ItemStack.EMPTY);
+- this.entityData.define(DATA_ATTACHED_TO_TARGET, OptionalInt.empty());
+- this.entityData.define(DATA_SHOT_AT_ANGLE, false);
++ this.entityData.define(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, ItemStack.EMPTY);
++ this.entityData.define(FireworkRocketEntity.DATA_ATTACHED_TO_TARGET, OptionalInt.empty());
++ this.entityData.define(FireworkRocketEntity.DATA_SHOT_AT_ANGLE, false);
+ }
+
+ @Override
+ public boolean shouldRenderAtSqrDistance(double distance) {
+- return distance < 4096.0 && !this.isAttachedToEntity();
++ return distance < 4096.0D && !this.isAttachedToEntity();
+ }
+
+ @Override
+- public boolean shouldRender(double x, double y, double z) {
+- return super.shouldRender(x, y, z) && !this.isAttachedToEntity();
++ public boolean shouldRender(double x, double d1, double y) {
++ return super.shouldRender(x, d1, y) && !this.isAttachedToEntity();
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
++ Vec3 vec3d;
++
+ if (this.isAttachedToEntity()) {
+ if (this.attachedToEntity == null) {
+- this.entityData.get(DATA_ATTACHED_TO_TARGET).ifPresent(target -> {
+- Entity entity = this.level().getEntity(target);
++ ((OptionalInt) this.entityData.get(FireworkRocketEntity.DATA_ATTACHED_TO_TARGET)).ifPresent((i) -> {
++ Entity entity = this.level().getEntity(i);
++
+ if (entity instanceof LivingEntity) {
+- this.attachedToEntity = (LivingEntity)entity;
++ this.attachedToEntity = (LivingEntity) entity;
+ }
++
+ });
+ }
+
+ if (this.attachedToEntity != null) {
+- Vec3 handHoldingItemAngle;
+ if (this.attachedToEntity.isFallFlying()) {
+- Vec3 lookAngle = this.attachedToEntity.getLookAngle();
+- double d = 1.5;
+- double d1 = 0.1;
+- Vec3 deltaMovement = this.attachedToEntity.getDeltaMovement();
+- this.attachedToEntity
+- .setDeltaMovement(
+- deltaMovement.add(
+- lookAngle.x * 0.1 + (lookAngle.x * 1.5 - deltaMovement.x) * 0.5,
+- lookAngle.y * 0.1 + (lookAngle.y * 1.5 - deltaMovement.y) * 0.5,
+- lookAngle.z * 0.1 + (lookAngle.z * 1.5 - deltaMovement.z) * 0.5
+- )
+- );
+- handHoldingItemAngle = this.attachedToEntity.getHandHoldingItemAngle(Items.FIREWORK_ROCKET);
++ Vec3 vec3d1 = this.attachedToEntity.getLookAngle();
++ double d0 = 1.5D;
++ double d1 = 0.1D;
++ Vec3 vec3d2 = this.attachedToEntity.getDeltaMovement();
++
++ this.attachedToEntity.setDeltaMovement(vec3d2.add(vec3d1.x * 0.1D + (vec3d1.x * 1.5D - vec3d2.x) * 0.5D, vec3d1.y * 0.1D + (vec3d1.y * 1.5D - vec3d2.y) * 0.5D, vec3d1.z * 0.1D + (vec3d1.z * 1.5D - vec3d2.z) * 0.5D));
++ vec3d = this.attachedToEntity.getHandHoldingItemAngle(Items.FIREWORK_ROCKET);
+ } else {
+- handHoldingItemAngle = Vec3.ZERO;
++ vec3d = Vec3.ZERO;
+ }
+
+- this.setPos(
+- this.attachedToEntity.getX() + handHoldingItemAngle.x,
+- this.attachedToEntity.getY() + handHoldingItemAngle.y,
+- this.attachedToEntity.getZ() + handHoldingItemAngle.z
+- );
++ this.setPos(this.attachedToEntity.getX() + vec3d.x, this.attachedToEntity.getY() + vec3d.y, this.attachedToEntity.getZ() + vec3d.z);
+ this.setDeltaMovement(this.attachedToEntity.getDeltaMovement());
+ }
+ } else {
+ if (!this.isShotAtAngle()) {
+- double d2 = this.horizontalCollision ? 1.0 : 1.15;
+- this.setDeltaMovement(this.getDeltaMovement().multiply(d2, 1.0, d2).add(0.0, 0.04, 0.0));
++ double d2 = this.horizontalCollision ? 1.0D : 1.15D;
++
++ this.setDeltaMovement(this.getDeltaMovement().multiply(d2, 1.0D, d2).add(0.0D, 0.04D, 0.0D));
+ }
+
+- Vec3 handHoldingItemAngle = this.getDeltaMovement();
+- this.move(MoverType.SELF, handHoldingItemAngle);
+- this.setDeltaMovement(handHoldingItemAngle);
++ vec3d = this.getDeltaMovement();
++ this.move(EnumMoveType.SELF, vec3d);
++ this.setDeltaMovement(vec3d);
+ }
+
+- HitResult hitResultOnMoveVector = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
++ HitResult movingobjectposition = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
++
+ if (!this.noPhysics) {
+- this.onHit(hitResultOnMoveVector);
++ this.preOnHit(movingobjectposition); // CraftBukkit - projectile hit event
+ this.hasImpulse = true;
+ }
+
+ this.updateRotation();
+ if (this.life == 0 && !this.isSilent()) {
+- this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.FIREWORK_ROCKET_LAUNCH, SoundSource.AMBIENT, 3.0F, 1.0F);
++ this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.FIREWORK_ROCKET_LAUNCH, SoundSource.AMBIENT, 3.0F, 1.0F);
+ }
+
+- this.life++;
++ ++this.life;
+ if (this.level().isClientSide && this.life % 2 < 2) {
+- this.level()
+- .addParticle(
+- ParticleTypes.FIREWORK,
+- this.getX(),
+- this.getY(),
+- this.getZ(),
+- this.random.nextGaussian() * 0.05,
+- -this.getDeltaMovement().y * 0.5,
+- this.random.nextGaussian() * 0.05
+- );
++ this.level().addParticle(ParticleTypes.FIREWORK, this.getX(), this.getY(), this.getZ(), this.random.nextGaussian() * 0.05D, -this.getDeltaMovement().y * 0.5D, this.random.nextGaussian() * 0.05D);
+ }
+
+ if (!this.level().isClientSide && this.life > this.lifetime) {
+- this.explode();
++ // CraftBukkit start
++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) {
++ this.explode();
++ }
++ // CraftBukkit end
+ }
++
+ }
+
+ private void explode() {
+- this.level().broadcastEntityEvent(this, (byte)17);
++ this.level().broadcastEntityEvent(this, (byte) 17);
+ this.gameEvent(GameEvent.EXPLODE, this.getOwner());
+ this.dealExplosionDamage();
+ this.discard();
+@@ -186,96 +179,110 @@
+ protected void onHitEntity(EntityHitResult result) {
+ super.onHitEntity(result);
+ if (!this.level().isClientSide) {
+- this.explode();
++ // CraftBukkit start
++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) {
++ this.explode();
++ }
++ // CraftBukkit end
+ }
+ }
+
+ @Override
+ protected void onHitBlock(BlockHitResult result) {
+- BlockPos blockPos = new BlockPos(result.getBlockPos());
+- this.level().getBlockState(blockPos).entityInside(this.level(), blockPos, this);
++ BlockPos blockposition = new BlockPos(result.getBlockPos());
++
++ this.level().getBlockState(blockposition).entityInside(this.level(), blockposition, this);
+ if (!this.level().isClientSide() && this.hasExplosion()) {
+- this.explode();
++ // CraftBukkit start
++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) {
++ this.explode();
++ }
++ // CraftBukkit end
+ }
+
+ super.onHitBlock(result);
+ }
+
+ private boolean hasExplosion() {
+- ItemStack itemStack = this.entityData.get(DATA_ID_FIREWORKS_ITEM);
+- CompoundTag compoundTag = itemStack.isEmpty() ? null : itemStack.getTagElement("Fireworks");
+- ListTag list = compoundTag != null ? compoundTag.getList("Explosions", 10) : null;
+- return list != null && !list.isEmpty();
++ ItemStack itemstack = (ItemStack) this.entityData.get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM);
++ CompoundTag nbttagcompound = itemstack.isEmpty() ? null : itemstack.getTagElement("Fireworks");
++ ListTag nbttaglist = nbttagcompound != null ? nbttagcompound.getList("Explosions", 10) : null;
++
++ return nbttaglist != null && !nbttaglist.isEmpty();
+ }
+
+ private void dealExplosionDamage() {
+ float f = 0.0F;
+- ItemStack itemStack = this.entityData.get(DATA_ID_FIREWORKS_ITEM);
+- CompoundTag compoundTag = itemStack.isEmpty() ? null : itemStack.getTagElement("Fireworks");
+- ListTag list = compoundTag != null ? compoundTag.getList("Explosions", 10) : null;
+- if (list != null && !list.isEmpty()) {
+- f = 5.0F + (float)(list.size() * 2);
++ ItemStack itemstack = (ItemStack) this.entityData.get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM);
++ CompoundTag nbttagcompound = itemstack.isEmpty() ? null : itemstack.getTagElement("Fireworks");
++ ListTag nbttaglist = nbttagcompound != null ? nbttagcompound.getList("Explosions", 10) : null;
++
++ if (nbttaglist != null && !nbttaglist.isEmpty()) {
++ f = 5.0F + (float) (nbttaglist.size() * 2);
+ }
+
+ if (f > 0.0F) {
+ if (this.attachedToEntity != null) {
+- this.attachedToEntity.hurt(this.damageSources().fireworks(this, this.getOwner()), 5.0F + (float)(list.size() * 2));
++ CraftEventFactory.entityDamage = this; // CraftBukkit
++ this.attachedToEntity.hurt(this.damageSources().fireworks(this, this.getOwner()), 5.0F + (float) (nbttaglist.size() * 2));
++ CraftEventFactory.entityDamage = null; // CraftBukkit
+ }
+
+- double d = 5.0;
+- Vec3 vec3 = this.position();
++ double d0 = 5.0D;
++ Vec3 vec3d = this.position();
++ List<LivingEntity> list = this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox().inflate(5.0D));
++ Iterator iterator = list.iterator();
+
+- for (LivingEntity livingEntity : this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox().inflate(5.0))) {
+- if (livingEntity != this.attachedToEntity && !(this.distanceToSqr(livingEntity) > 25.0)) {
++ while (iterator.hasNext()) {
++ LivingEntity entityliving = (LivingEntity) iterator.next();
++
++ if (entityliving != this.attachedToEntity && this.distanceToSqr((Entity) entityliving) <= 25.0D) {
+ boolean flag = false;
+
+- for (int i = 0; i < 2; i++) {
+- Vec3 vec31 = new Vec3(livingEntity.getX(), livingEntity.getY(0.5 * (double)i), livingEntity.getZ());
+- HitResult hitResult = this.level().clip(new ClipContext(vec3, vec31, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this));
+- if (hitResult.getType() == HitResult.Type.MISS) {
++ for (int i = 0; i < 2; ++i) {
++ Vec3 vec3d1 = new Vec3(entityliving.getX(), entityliving.getY(0.5D * (double) i), entityliving.getZ());
++ BlockHitResult movingobjectpositionblock = this.level().clip(new ClipContext(vec3d, vec3d1, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, this));
++
++ if (movingobjectpositionblock.getType() == HitResult.EnumMovingObjectType.MISS) {
+ flag = true;
+ break;
+ }
+ }
+
+ if (flag) {
+- float f1 = f * (float)Math.sqrt((5.0 - (double)this.distanceTo(livingEntity)) / 5.0);
+- livingEntity.hurt(this.damageSources().fireworks(this, this.getOwner()), f1);
++ float f1 = f * (float) Math.sqrt((5.0D - (double) this.distanceTo(entityliving)) / 5.0D);
++
++ CraftEventFactory.entityDamage = this; // CraftBukkit
++ entityliving.hurt(this.damageSources().fireworks(this, this.getOwner()), f1);
++ CraftEventFactory.entityDamage = null; // CraftBukkit
+ }
+ }
+ }
+ }
++
+ }
+
+ private boolean isAttachedToEntity() {
+- return this.entityData.get(DATA_ATTACHED_TO_TARGET).isPresent();
++ return ((OptionalInt) this.entityData.get(FireworkRocketEntity.DATA_ATTACHED_TO_TARGET)).isPresent();
+ }
+
+ public boolean isShotAtAngle() {
+- return this.entityData.get(DATA_SHOT_AT_ANGLE);
++ return (Boolean) this.entityData.get(FireworkRocketEntity.DATA_SHOT_AT_ANGLE);
+ }
+
+ @Override
+ public void handleEntityEvent(byte id) {
+ if (id == 17 && this.level().isClientSide) {
+ if (!this.hasExplosion()) {
+- for (int i = 0; i < this.random.nextInt(3) + 2; i++) {
+- this.level()
+- .addParticle(
+- ParticleTypes.POOF,
+- this.getX(),
+- this.getY(),
+- this.getZ(),
+- this.random.nextGaussian() * 0.05,
+- 0.005,
+- this.random.nextGaussian() * 0.05
+- );
++ for (int i = 0; i < this.random.nextInt(3) + 2; ++i) {
++ this.level().addParticle(ParticleTypes.POOF, this.getX(), this.getY(), this.getZ(), this.random.nextGaussian() * 0.05D, 0.005D, this.random.nextGaussian() * 0.05D);
+ }
+ } else {
+- ItemStack itemStack = this.entityData.get(DATA_ID_FIREWORKS_ITEM);
+- CompoundTag compoundTag = itemStack.isEmpty() ? null : itemStack.getTagElement("Fireworks");
+- Vec3 deltaMovement = this.getDeltaMovement();
+- this.level().createFireworks(this.getX(), this.getY(), this.getZ(), deltaMovement.x, deltaMovement.y, deltaMovement.z, compoundTag);
++ ItemStack itemstack = (ItemStack) this.entityData.get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM);
++ CompoundTag nbttagcompound = itemstack.isEmpty() ? null : itemstack.getTagElement("Fireworks");
++ Vec3 vec3d = this.getDeltaMovement();
++
++ this.level().createFireworks(this.getX(), this.getY(), this.getZ(), vec3d.x, vec3d.y, vec3d.z, nbttagcompound);
+ }
+ }
+
+@@ -287,12 +294,13 @@
+ super.addAdditionalSaveData(compound);
+ compound.putInt("Life", this.life);
+ compound.putInt("LifeTime", this.lifetime);
+- ItemStack itemStack = this.entityData.get(DATA_ID_FIREWORKS_ITEM);
+- if (!itemStack.isEmpty()) {
+- compound.put("FireworksItem", itemStack.save(new CompoundTag()));
++ ItemStack itemstack = (ItemStack) this.entityData.get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM);
++
++ if (!itemstack.isEmpty()) {
++ compound.put("FireworksItem", itemstack.save(new CompoundTag()));
+ }
+
+- compound.putBoolean("ShotAtAngle", this.entityData.get(DATA_SHOT_AT_ANGLE));
++ compound.putBoolean("ShotAtAngle", (Boolean) this.entityData.get(FireworkRocketEntity.DATA_SHOT_AT_ANGLE));
+ }
+
+ @Override
+@@ -300,20 +308,23 @@
+ super.readAdditionalSaveData(compound);
+ this.life = compound.getInt("Life");
+ this.lifetime = compound.getInt("LifeTime");
+- ItemStack itemStack = ItemStack.of(compound.getCompound("FireworksItem"));
+- if (!itemStack.isEmpty()) {
+- this.entityData.set(DATA_ID_FIREWORKS_ITEM, itemStack);
++ ItemStack itemstack = ItemStack.of(compound.getCompound("FireworksItem"));
++
++ if (!itemstack.isEmpty()) {
++ this.entityData.set(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM, itemstack);
+ }
+
+ if (compound.contains("ShotAtAngle")) {
+- this.entityData.set(DATA_SHOT_AT_ANGLE, compound.getBoolean("ShotAtAngle"));
++ this.entityData.set(FireworkRocketEntity.DATA_SHOT_AT_ANGLE, compound.getBoolean("ShotAtAngle"));
+ }
++
+ }
+
+ @Override
+ public ItemStack getItem() {
+- ItemStack itemStack = this.entityData.get(DATA_ID_FIREWORKS_ITEM);
+- return itemStack.isEmpty() ? new ItemStack(Items.FIREWORK_ROCKET) : itemStack;
++ ItemStack itemstack = (ItemStack) this.entityData.get(FireworkRocketEntity.DATA_ID_FIREWORKS_ITEM);
++
++ return itemstack.isEmpty() ? new ItemStack(Items.FIREWORK_ROCKET) : itemstack;
+ }
+
+ @Override
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FishingHook.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FishingHook.java.patch
new file mode 100644
index 0000000000..80a95da104
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/FishingHook.java.patch
@@ -0,0 +1,854 @@
+--- a/net/minecraft/world/entity/projectile/FishingHook.java
++++ b/net/minecraft/world/entity/projectile/FishingHook.java
+@@ -2,6 +2,7 @@
+
+ import com.mojang.logging.LogUtils;
+ import java.util.Collections;
++import java.util.Iterator;
+ import java.util.List;
+ import javax.annotation.Nullable;
+ import net.minecraft.advancements.CriteriaTriggers;
+@@ -24,15 +25,14 @@
+ import net.minecraft.util.RandomSource;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMoveType;
+ import net.minecraft.world.entity.ExperienceOrb;
+-import net.minecraft.world.entity.MoverType;
+ import net.minecraft.world.entity.item.ItemEntity;
+-import net.minecraft.world.entity.player.Player;
+ import net.minecraft.world.item.ItemStack;
+ import net.minecraft.world.item.Items;
+ import net.minecraft.world.level.Level;
+ 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.material.FluidState;
+ import net.minecraft.world.level.storage.loot.BuiltInLootTables;
+ import net.minecraft.world.level.storage.loot.LootParams;
+@@ -45,28 +45,50 @@
+ import net.minecraft.world.phys.Vec3;
+ import org.slf4j.Logger;
+
++// CraftBukkit start
++import org.bukkit.entity.Player;
++import org.bukkit.entity.FishHook;
++import org.bukkit.event.player.PlayerFishEvent;
++// CraftBukkit end
++
+ public class FishingHook extends Projectile {
++
+ private static final Logger LOGGER = LogUtils.getLogger();
+- private final RandomSource syncronizedRandom = RandomSource.create();
++ private final RandomSource syncronizedRandom;
+ private boolean biting;
+ private int outOfWaterTime;
+ private static final int MAX_OUT_OF_WATER_TIME = 10;
+- private static final EntityDataAccessor<Integer> DATA_HOOKED_ENTITY = SynchedEntityData.defineId(FishingHook.class, EntityDataSerializers.INT);
++ public static final EntityDataAccessor<Integer> DATA_HOOKED_ENTITY = SynchedEntityData.defineId(FishingHook.class, EntityDataSerializers.INT);
+ private static final EntityDataAccessor<Boolean> DATA_BITING = SynchedEntityData.defineId(FishingHook.class, EntityDataSerializers.BOOLEAN);
+ private int life;
+ private int nibble;
+ private int timeUntilLured;
+ private int timeUntilHooked;
+ private float fishAngle;
+- private boolean openWater = true;
++ private boolean openWater;
+ @Nullable
+- private Entity hookedIn;
+- private FishingHook.FishHookState currentState = FishingHook.FishHookState.FLYING;
++ public Entity hookedIn;
++ public FishingHook.HookState currentState;
+ private final int luck;
+ private final int lureSpeed;
+
++ // CraftBukkit start - Extra variables to enable modification of fishing wait time, values are minecraft defaults
++ public int minWaitTime = 100;
++ public int maxWaitTime = 600;
++ public int minLureTime = 20;
++ public int maxLureTime = 80;
++ public float minLureAngle = 0.0F;
++ public float maxLureAngle = 360.0F;
++ public boolean applyLure = true;
++ public boolean rainInfluenced = true;
++ public boolean skyInfluenced = true;
++ // CraftBukkit end
++
+ private FishingHook(EntityType<? extends FishingHook> entityType, Level level, int luck, int lureSpeed) {
+ super(entityType, level);
++ this.syncronizedRandom = RandomSource.create();
++ this.openWater = true;
++ this.currentState = FishingHook.HookState.FLYING;
+ this.noCulling = true;
+ this.luck = Math.max(0, luck);
+ this.lureSpeed = Math.max(0, lureSpeed);
+@@ -76,50 +98,49 @@
+ this(entityType, level, 0, 0);
+ }
+
+- public FishingHook(Player player, Level level, int luck, int lureSpeed) {
++ public FishingHook(net.minecraft.world.entity.player.Player player, Level level, int luck, int lureSpeed) {
+ this(EntityType.FISHING_BOBBER, level, luck, lureSpeed);
+ this.setOwner(player);
+- float xRot = player.getXRot();
+- float yRot = player.getYRot();
+- float cos = Mth.cos(-yRot * (float) (Math.PI / 180.0) - (float) Math.PI);
+- float sin = Mth.sin(-yRot * (float) (Math.PI / 180.0) - (float) Math.PI);
+- float f = -Mth.cos(-xRot * (float) (Math.PI / 180.0));
+- float sin1 = Mth.sin(-xRot * (float) (Math.PI / 180.0));
+- double d = player.getX() - (double)sin * 0.3;
+- double eyeY = player.getEyeY();
+- double d1 = player.getZ() - (double)cos * 0.3;
+- this.moveTo(d, eyeY, d1, yRot, xRot);
+- Vec3 vec3 = new Vec3((double)(-sin), (double)Mth.clamp(-(sin1 / f), -5.0F, 5.0F), (double)(-cos));
+- double len = vec3.length();
+- vec3 = vec3.multiply(
+- 0.6 / len + this.random.triangle(0.5, 0.0103365),
+- 0.6 / len + this.random.triangle(0.5, 0.0103365),
+- 0.6 / len + this.random.triangle(0.5, 0.0103365)
+- );
+- this.setDeltaMovement(vec3);
+- this.setYRot((float)(Mth.atan2(vec3.x, vec3.z) * 180.0F / (float)Math.PI));
+- this.setXRot((float)(Mth.atan2(vec3.y, vec3.horizontalDistance()) * 180.0F / (float)Math.PI));
++ float f = player.getXRot();
++ float f1 = player.getYRot();
++ float f2 = Mth.cos(-f1 * 0.017453292F - 3.1415927F);
++ float f3 = Mth.sin(-f1 * 0.017453292F - 3.1415927F);
++ float f4 = -Mth.cos(-f * 0.017453292F);
++ float f5 = Mth.sin(-f * 0.017453292F);
++ double d0 = player.getX() - (double) f3 * 0.3D;
++ double d1 = player.getEyeY();
++ double d2 = player.getZ() - (double) f2 * 0.3D;
++
++ this.moveTo(d0, d1, d2, f1, f);
++ Vec3 vec3d = new Vec3((double) (-f3), (double) Mth.clamp(-(f5 / f4), -5.0F, 5.0F), (double) (-f2));
++ double d3 = vec3d.length();
++
++ vec3d = vec3d.multiply(0.6D / d3 + this.random.triangle(0.5D, 0.0103365D), 0.6D / d3 + this.random.triangle(0.5D, 0.0103365D), 0.6D / d3 + this.random.triangle(0.5D, 0.0103365D));
++ this.setDeltaMovement(vec3d);
++ this.setYRot((float) (Mth.atan2(vec3d.x, vec3d.z) * 57.2957763671875D));
++ this.setXRot((float) (Mth.atan2(vec3d.y, vec3d.horizontalDistance()) * 57.2957763671875D));
+ this.yRotO = this.getYRot();
+ this.xRotO = this.getXRot();
+ }
+
+ @Override
+ protected void defineSynchedData() {
+- this.getEntityData().define(DATA_HOOKED_ENTITY, 0);
+- this.getEntityData().define(DATA_BITING, false);
++ this.getEntityData().define(FishingHook.DATA_HOOKED_ENTITY, 0);
++ this.getEntityData().define(FishingHook.DATA_BITING, false);
+ }
+
+ @Override
+ public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
+- if (DATA_HOOKED_ENTITY.equals(key)) {
+- int i = this.getEntityData().get(DATA_HOOKED_ENTITY);
++ if (FishingHook.DATA_HOOKED_ENTITY.equals(key)) {
++ int i = (Integer) this.getEntityData().get(FishingHook.DATA_HOOKED_ENTITY);
++
+ this.hookedIn = i > 0 ? this.level().getEntity(i - 1) : null;
+ }
+
+- if (DATA_BITING.equals(key)) {
+- this.biting = this.getEntityData().get(DATA_BITING);
++ if (FishingHook.DATA_BITING.equals(key)) {
++ this.biting = (Boolean) this.getEntityData().get(FishingHook.DATA_BITING);
+ if (this.biting) {
+- this.setDeltaMovement(this.getDeltaMovement().x, (double)(-0.4F * Mth.nextFloat(this.syncronizedRandom, 0.6F, 1.0F)), this.getDeltaMovement().z);
++ this.setDeltaMovement(this.getDeltaMovement().x, (double) (-0.4F * Mth.nextFloat(this.syncronizedRandom, 0.6F, 1.0F)), this.getDeltaMovement().z);
+ }
+ }
+
+@@ -128,24 +149,25 @@
+
+ @Override
+ public boolean shouldRenderAtSqrDistance(double distance) {
+- double d = 64.0;
+- return distance < 4096.0;
++ double d1 = 64.0D;
++
++ return distance < 4096.0D;
+ }
+
+ @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) {}
+
+ @Override
+ public void tick() {
+ this.syncronizedRandom.setSeed(this.getUUID().getLeastSignificantBits() ^ this.level().getGameTime());
+ super.tick();
+- Player playerOwner = this.getPlayerOwner();
+- if (playerOwner == null) {
++ net.minecraft.world.entity.player.Player entityhuman = this.getPlayerOwner();
++
++ if (entityhuman == null) {
+ this.discard();
+- } else if (this.level().isClientSide || !this.shouldStopFishing(playerOwner)) {
++ } else if (this.level().isClientSide || !this.shouldStopFishing(entityhuman)) {
+ if (this.onGround()) {
+- this.life++;
++ ++this.life;
+ if (this.life >= 1200) {
+ this.discard();
+ return;
+@@ -155,66 +177,66 @@
+ }
+
+ float f = 0.0F;
+- BlockPos blockPos = this.blockPosition();
+- FluidState fluidState = this.level().getFluidState(blockPos);
+- if (fluidState.is(FluidTags.WATER)) {
+- f = fluidState.getHeight(this.level(), blockPos);
++ BlockPos blockposition = this.blockPosition();
++ FluidState fluid = this.level().getFluidState(blockposition);
++
++ if (fluid.is(FluidTags.WATER)) {
++ f = fluid.getHeight(this.level(), blockposition);
+ }
+
+ boolean flag = f > 0.0F;
+- if (this.currentState == FishingHook.FishHookState.FLYING) {
++
++ if (this.currentState == FishingHook.HookState.FLYING) {
+ if (this.hookedIn != null) {
+ this.setDeltaMovement(Vec3.ZERO);
+- this.currentState = FishingHook.FishHookState.HOOKED_IN_ENTITY;
++ this.currentState = FishingHook.HookState.HOOKED_IN_ENTITY;
+ return;
+ }
+
+ if (flag) {
+- this.setDeltaMovement(this.getDeltaMovement().multiply(0.3, 0.2, 0.3));
+- this.currentState = FishingHook.FishHookState.BOBBING;
++ this.setDeltaMovement(this.getDeltaMovement().multiply(0.3D, 0.2D, 0.3D));
++ this.currentState = FishingHook.HookState.BOBBING;
+ return;
+ }
+
+ this.checkCollision();
+ } else {
+- if (this.currentState == FishingHook.FishHookState.HOOKED_IN_ENTITY) {
++ if (this.currentState == FishingHook.HookState.HOOKED_IN_ENTITY) {
+ if (this.hookedIn != null) {
+ if (!this.hookedIn.isRemoved() && this.hookedIn.level().dimension() == this.level().dimension()) {
+- this.setPos(this.hookedIn.getX(), this.hookedIn.getY(0.8), this.hookedIn.getZ());
++ this.setPos(this.hookedIn.getX(), this.hookedIn.getY(0.8D), this.hookedIn.getZ());
+ } else {
+- this.setHookedEntity(null);
+- this.currentState = FishingHook.FishHookState.FLYING;
++ this.setHookedEntity((Entity) null);
++ this.currentState = FishingHook.HookState.FLYING;
+ }
+ }
+
+ return;
+ }
+
+- if (this.currentState == FishingHook.FishHookState.BOBBING) {
+- Vec3 deltaMovement = this.getDeltaMovement();
+- double d = this.getY() + deltaMovement.y - (double)blockPos.getY() - (double)f;
+- if (Math.abs(d) < 0.01) {
+- d += Math.signum(d) * 0.1;
++ if (this.currentState == FishingHook.HookState.BOBBING) {
++ Vec3 vec3d = this.getDeltaMovement();
++ double d0 = this.getY() + vec3d.y - (double) blockposition.getY() - (double) f;
++
++ if (Math.abs(d0) < 0.01D) {
++ d0 += Math.signum(d0) * 0.1D;
+ }
+
+- this.setDeltaMovement(deltaMovement.x * 0.9, deltaMovement.y - d * (double)this.random.nextFloat() * 0.2, deltaMovement.z * 0.9);
++ this.setDeltaMovement(vec3d.x * 0.9D, vec3d.y - d0 * (double) this.random.nextFloat() * 0.2D, vec3d.z * 0.9D);
+ if (this.nibble <= 0 && this.timeUntilHooked <= 0) {
+ this.openWater = true;
+ } else {
+- this.openWater = this.openWater && this.outOfWaterTime < 10 && this.calculateOpenWater(blockPos);
++ this.openWater = this.openWater && this.outOfWaterTime < 10 && this.calculateOpenWater(blockposition);
+ }
+
+ if (flag) {
+ this.outOfWaterTime = Math.max(0, this.outOfWaterTime - 1);
+ if (this.biting) {
+- this.setDeltaMovement(
+- this.getDeltaMovement()
+- .add(0.0, -0.1 * (double)this.syncronizedRandom.nextFloat() * (double)this.syncronizedRandom.nextFloat(), 0.0)
+- );
++ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.1D * (double) this.syncronizedRandom.nextFloat() * (double) this.syncronizedRandom.nextFloat(), 0.0D));
+ }
+
+ if (!this.level().isClientSide) {
+- this.catchingFish(blockPos);
++ this.catchingFish(blockposition);
+ }
+ } else {
+ this.outOfWaterTime = Math.min(10, this.outOfWaterTime + 1);
+@@ -222,28 +244,30 @@
+ }
+ }
+
+- if (!fluidState.is(FluidTags.WATER)) {
+- this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.03, 0.0));
++ if (!fluid.is(FluidTags.WATER)) {
++ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.03D, 0.0D));
+ }
+
+- this.move(MoverType.SELF, this.getDeltaMovement());
++ this.move(EnumMoveType.SELF, this.getDeltaMovement());
+ this.updateRotation();
+- if (this.currentState == FishingHook.FishHookState.FLYING && (this.onGround() || this.horizontalCollision)) {
++ if (this.currentState == FishingHook.HookState.FLYING && (this.onGround() || this.horizontalCollision)) {
+ this.setDeltaMovement(Vec3.ZERO);
+ }
+
+- double d1 = 0.92;
+- this.setDeltaMovement(this.getDeltaMovement().scale(0.92));
++ double d1 = 0.92D;
++
++ this.setDeltaMovement(this.getDeltaMovement().scale(0.92D));
+ this.reapplyPosition();
+ }
+ }
+
+- private boolean shouldStopFishing(Player player) {
+- ItemStack mainHandItem = player.getMainHandItem();
+- ItemStack offhandItem = player.getOffhandItem();
+- boolean isFishingRod = mainHandItem.is(Items.FISHING_ROD);
+- boolean isFishingRod1 = offhandItem.is(Items.FISHING_ROD);
+- if (!player.isRemoved() && player.isAlive() && (isFishingRod || isFishingRod1) && !(this.distanceToSqr(player) > 1024.0)) {
++ private boolean shouldStopFishing(net.minecraft.world.entity.player.Player player) {
++ ItemStack itemstack = player.getMainHandItem();
++ ItemStack itemstack1 = player.getOffhandItem();
++ boolean flag = itemstack.is(Items.FISHING_ROD);
++ boolean flag1 = itemstack1.is(Items.FISHING_ROD);
++
++ if (!player.isRemoved() && player.isAlive() && (flag || flag1) && this.distanceToSqr((Entity) player) <= 1024.0D) {
+ return false;
+ } else {
+ this.discard();
+@@ -252,8 +276,9 @@
+ }
+
+ private void checkCollision() {
+- HitResult hitResultOnMoveVector = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
+- this.onHit(hitResultOnMoveVector);
++ HitResult movingobjectposition = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
++
++ this.preOnHit(movingobjectposition); // CraftBukkit - projectile hit event
+ }
+
+ @Override
+@@ -267,6 +292,7 @@
+ if (!this.level().isClientSide) {
+ this.setHookedEntity(result.getEntity());
+ }
++
+ }
+
+ @Override
+@@ -275,153 +301,162 @@
+ this.setDeltaMovement(this.getDeltaMovement().normalize().scale(result.distanceTo(this)));
+ }
+
+- private void setHookedEntity(@Nullable Entity hookedEntity) {
++ public void setHookedEntity(@Nullable Entity hookedEntity) {
+ this.hookedIn = hookedEntity;
+- this.getEntityData().set(DATA_HOOKED_ENTITY, hookedEntity == null ? 0 : hookedEntity.getId() + 1);
++ this.getEntityData().set(FishingHook.DATA_HOOKED_ENTITY, hookedEntity == null ? 0 : hookedEntity.getId() + 1);
+ }
+
+ private void catchingFish(BlockPos pos) {
+- ServerLevel serverLevel = (ServerLevel)this.level();
++ ServerLevel worldserver = (ServerLevel) this.level();
+ int i = 1;
+- BlockPos blockPos = pos.above();
+- if (this.random.nextFloat() < 0.25F && this.level().isRainingAt(blockPos)) {
+- i++;
++ BlockPos blockposition1 = pos.above();
++
++ if (this.rainInfluenced && this.random.nextFloat() < 0.25F && this.level().isRainingAt(blockposition1)) { // CraftBukkit
++ ++i;
+ }
+
+- if (this.random.nextFloat() < 0.5F && !this.level().canSeeSky(blockPos)) {
+- i--;
++ if (this.skyInfluenced && this.random.nextFloat() < 0.5F && !this.level().canSeeSky(blockposition1)) { // CraftBukkit
++ --i;
+ }
+
+ if (this.nibble > 0) {
+- this.nibble--;
++ --this.nibble;
+ if (this.nibble <= 0) {
+ this.timeUntilLured = 0;
+ this.timeUntilHooked = 0;
+- this.getEntityData().set(DATA_BITING, false);
++ this.getEntityData().set(FishingHook.DATA_BITING, false);
++ // CraftBukkit start
++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.getPlayerOwner().getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT);
++ this.level().getCraftServer().getPluginManager().callEvent(playerFishEvent);
++ // CraftBukkit end
+ }
+- } else if (this.timeUntilHooked > 0) {
+- this.timeUntilHooked -= i;
++ } else {
++ float f;
++ float f1;
++ float f2;
++ double d0;
++ double d1;
++ double d2;
++ IBlockData iblockdata;
++
+ if (this.timeUntilHooked > 0) {
+- this.fishAngle = this.fishAngle + (float)this.random.triangle(0.0, 9.188);
+- float f = this.fishAngle * (float) (Math.PI / 180.0);
+- float sin = Mth.sin(f);
+- float cos = Mth.cos(f);
+- double d = this.getX() + (double)(sin * (float)this.timeUntilHooked * 0.1F);
+- double d1 = (double)((float)Mth.floor(this.getY()) + 1.0F);
+- double d2 = this.getZ() + (double)(cos * (float)this.timeUntilHooked * 0.1F);
+- BlockState blockState = serverLevel.getBlockState(BlockPos.containing(d, d1 - 1.0, d2));
+- if (blockState.is(Blocks.WATER)) {
+- if (this.random.nextFloat() < 0.15F) {
+- serverLevel.sendParticles(ParticleTypes.BUBBLE, d, d1 - 0.1F, d2, 1, (double)sin, 0.1, (double)cos, 0.0);
++ this.timeUntilHooked -= i;
++ if (this.timeUntilHooked > 0) {
++ this.fishAngle += (float) this.random.triangle(0.0D, 9.188D);
++ f = this.fishAngle * 0.017453292F;
++ f1 = Mth.sin(f);
++ f2 = Mth.cos(f);
++ d0 = this.getX() + (double) (f1 * (float) this.timeUntilHooked * 0.1F);
++ d1 = (double) ((float) Mth.floor(this.getY()) + 1.0F);
++ d2 = this.getZ() + (double) (f2 * (float) this.timeUntilHooked * 0.1F);
++ iblockdata = worldserver.getBlockState(BlockPos.containing(d0, d1 - 1.0D, d2));
++ if (iblockdata.is(Blocks.WATER)) {
++ if (this.random.nextFloat() < 0.15F) {
++ worldserver.sendParticles(ParticleTypes.BUBBLE, d0, d1 - 0.10000000149011612D, d2, 1, (double) f1, 0.1D, (double) f2, 0.0D);
++ }
++
++ float f3 = f1 * 0.04F;
++ float f4 = f2 * 0.04F;
++
++ worldserver.sendParticles(ParticleTypes.FISHING, d0, d1, d2, 0, (double) f4, 0.01D, (double) (-f3), 1.0D);
++ worldserver.sendParticles(ParticleTypes.FISHING, d0, d1, d2, 0, (double) (-f4), 0.01D, (double) f3, 1.0D);
+ }
++ } else {
++ // CraftBukkit start
++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.getPlayerOwner().getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.BITE);
++ this.level().getCraftServer().getPluginManager().callEvent(playerFishEvent);
++ if (playerFishEvent.isCancelled()) {
++ return;
++ }
++ // CraftBukkit end
++ this.playSound(SoundEvents.FISHING_BOBBER_SPLASH, 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
++ double d3 = this.getY() + 0.5D;
+
+- float f1 = sin * 0.04F;
+- float f2 = cos * 0.04F;
+- serverLevel.sendParticles(ParticleTypes.FISHING, d, d1, d2, 0, (double)f2, 0.01, (double)(-f1), 1.0);
+- serverLevel.sendParticles(ParticleTypes.FISHING, d, d1, d2, 0, (double)(-f2), 0.01, (double)f1, 1.0);
++ worldserver.sendParticles(ParticleTypes.BUBBLE, this.getX(), d3, this.getZ(), (int) (1.0F + this.getBbWidth() * 20.0F), (double) this.getBbWidth(), 0.0D, (double) this.getBbWidth(), 0.20000000298023224D);
++ worldserver.sendParticles(ParticleTypes.FISHING, this.getX(), d3, this.getZ(), (int) (1.0F + this.getBbWidth() * 20.0F), (double) this.getBbWidth(), 0.0D, (double) this.getBbWidth(), 0.20000000298023224D);
++ this.nibble = Mth.nextInt(this.random, 20, 40);
++ this.getEntityData().set(FishingHook.DATA_BITING, true);
+ }
+- } else {
+- this.playSound(SoundEvents.FISHING_BOBBER_SPLASH, 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
+- double d3 = this.getY() + 0.5;
+- serverLevel.sendParticles(
+- ParticleTypes.BUBBLE,
+- this.getX(),
+- d3,
+- this.getZ(),
+- (int)(1.0F + this.getBbWidth() * 20.0F),
+- (double)this.getBbWidth(),
+- 0.0,
+- (double)this.getBbWidth(),
+- 0.2F
+- );
+- serverLevel.sendParticles(
+- ParticleTypes.FISHING,
+- this.getX(),
+- d3,
+- this.getZ(),
+- (int)(1.0F + this.getBbWidth() * 20.0F),
+- (double)this.getBbWidth(),
+- 0.0,
+- (double)this.getBbWidth(),
+- 0.2F
+- );
+- this.nibble = Mth.nextInt(this.random, 20, 40);
+- this.getEntityData().set(DATA_BITING, true);
+- }
+- } else if (this.timeUntilLured > 0) {
+- this.timeUntilLured -= i;
+- float f = 0.15F;
+- if (this.timeUntilLured < 20) {
+- f += (float)(20 - this.timeUntilLured) * 0.05F;
+- } else if (this.timeUntilLured < 40) {
+- f += (float)(40 - this.timeUntilLured) * 0.02F;
+- } else if (this.timeUntilLured < 60) {
+- f += (float)(60 - this.timeUntilLured) * 0.01F;
+- }
++ } else if (this.timeUntilLured > 0) {
++ this.timeUntilLured -= i;
++ f = 0.15F;
++ if (this.timeUntilLured < 20) {
++ f += (float) (20 - this.timeUntilLured) * 0.05F;
++ } else if (this.timeUntilLured < 40) {
++ f += (float) (40 - this.timeUntilLured) * 0.02F;
++ } else if (this.timeUntilLured < 60) {
++ f += (float) (60 - this.timeUntilLured) * 0.01F;
++ }
+
+- if (this.random.nextFloat() < f) {
+- float sin = Mth.nextFloat(this.random, 0.0F, 360.0F) * (float) (Math.PI / 180.0);
+- float cos = Mth.nextFloat(this.random, 25.0F, 60.0F);
+- double d = this.getX() + (double)(Mth.sin(sin) * cos) * 0.1;
+- double d1 = (double)((float)Mth.floor(this.getY()) + 1.0F);
+- double d2 = this.getZ() + (double)(Mth.cos(sin) * cos) * 0.1;
+- BlockState blockState = serverLevel.getBlockState(BlockPos.containing(d, d1 - 1.0, d2));
+- if (blockState.is(Blocks.WATER)) {
+- serverLevel.sendParticles(ParticleTypes.SPLASH, d, d1, d2, 2 + this.random.nextInt(2), 0.1F, 0.0, 0.1F, 0.0);
++ if (this.random.nextFloat() < f) {
++ f1 = Mth.nextFloat(this.random, 0.0F, 360.0F) * 0.017453292F;
++ f2 = Mth.nextFloat(this.random, 25.0F, 60.0F);
++ d0 = this.getX() + (double) (Mth.sin(f1) * f2) * 0.1D;
++ d1 = (double) ((float) Mth.floor(this.getY()) + 1.0F);
++ d2 = this.getZ() + (double) (Mth.cos(f1) * f2) * 0.1D;
++ iblockdata = worldserver.getBlockState(BlockPos.containing(d0, d1 - 1.0D, d2));
++ if (iblockdata.is(Blocks.WATER)) {
++ worldserver.sendParticles(ParticleTypes.SPLASH, d0, d1, d2, 2 + this.random.nextInt(2), 0.10000000149011612D, 0.0D, 0.10000000149011612D, 0.0D);
++ }
+ }
+- }
+
+- if (this.timeUntilLured <= 0) {
+- this.fishAngle = Mth.nextFloat(this.random, 0.0F, 360.0F);
+- this.timeUntilHooked = Mth.nextInt(this.random, 20, 80);
++ if (this.timeUntilLured <= 0) {
++ // CraftBukkit start - logic to modify fishing wait time, lure time, and lure angle
++ this.fishAngle = Mth.nextFloat(this.random, this.minLureAngle, this.maxLureAngle);
++ this.timeUntilHooked = Mth.nextInt(this.random, this.minLureTime, this.maxLureTime);
++ // CraftBukkit end
++ }
++ } else {
++ // CraftBukkit start - logic to modify fishing wait time
++ this.timeUntilLured = Mth.nextInt(this.random, this.minWaitTime, this.maxWaitTime);
++ this.timeUntilLured -= (this.applyLure) ? this.lureSpeed * 20 * 5 : 0;
++ // CraftBukkit end
+ }
+- } else {
+- this.timeUntilLured = Mth.nextInt(this.random, 100, 600);
+- this.timeUntilLured = this.timeUntilLured - this.lureSpeed * 20 * 5;
+ }
++
+ }
+
+ private boolean calculateOpenWater(BlockPos pos) {
+- FishingHook.OpenWaterType openWaterType = FishingHook.OpenWaterType.INVALID;
++ FishingHook.WaterPosition entityfishinghook_waterposition = FishingHook.WaterPosition.INVALID;
+
+- for (int i = -1; i <= 2; i++) {
+- FishingHook.OpenWaterType openWaterTypeForArea = this.getOpenWaterTypeForArea(pos.offset(-2, i, -2), pos.offset(2, i, 2));
+- switch (openWaterTypeForArea) {
++ for (int i = -1; i <= 2; ++i) {
++ FishingHook.WaterPosition entityfishinghook_waterposition1 = this.getOpenWaterTypeForArea(pos.offset(-2, i, -2), pos.offset(2, i, 2));
++
++ switch (entityfishinghook_waterposition1) {
+ case INVALID:
+ return false;
+ case ABOVE_WATER:
+- if (openWaterType == FishingHook.OpenWaterType.INVALID) {
++ if (entityfishinghook_waterposition == FishingHook.WaterPosition.INVALID) {
+ return false;
+ }
+ break;
+ case INSIDE_WATER:
+- if (openWaterType == FishingHook.OpenWaterType.ABOVE_WATER) {
++ if (entityfishinghook_waterposition == FishingHook.WaterPosition.ABOVE_WATER) {
+ return false;
+ }
+ }
+
+- openWaterType = openWaterTypeForArea;
++ entityfishinghook_waterposition = entityfishinghook_waterposition1;
+ }
+
+ return true;
+ }
+
+- private FishingHook.OpenWaterType getOpenWaterTypeForArea(BlockPos firstPos, BlockPos secondPos) {
+- return BlockPos.betweenClosedStream(firstPos, secondPos)
+- .map(this::getOpenWaterTypeForBlock)
+- .reduce((firstType, secondType) -> firstType == secondType ? firstType : FishingHook.OpenWaterType.INVALID)
+- .orElse(FishingHook.OpenWaterType.INVALID);
++ private FishingHook.WaterPosition getOpenWaterTypeForArea(BlockPos firstPos, BlockPos secondPos) {
++ return (FishingHook.WaterPosition) BlockPos.betweenClosedStream(firstPos, secondPos).map(this::getOpenWaterTypeForBlock).reduce((entityfishinghook_waterposition, entityfishinghook_waterposition1) -> {
++ return entityfishinghook_waterposition == entityfishinghook_waterposition1 ? entityfishinghook_waterposition : FishingHook.WaterPosition.INVALID;
++ }).orElse(FishingHook.WaterPosition.INVALID);
+ }
+
+- private FishingHook.OpenWaterType getOpenWaterTypeForBlock(BlockPos pos) {
+- BlockState blockState = this.level().getBlockState(pos);
+- if (!blockState.isAir() && !blockState.is(Blocks.LILY_PAD)) {
+- FluidState fluidState = blockState.getFluidState();
+- return fluidState.is(FluidTags.WATER) && fluidState.isSource() && blockState.getCollisionShape(this.level(), pos).isEmpty()
+- ? FishingHook.OpenWaterType.INSIDE_WATER
+- : FishingHook.OpenWaterType.INVALID;
++ private FishingHook.WaterPosition getOpenWaterTypeForBlock(BlockPos pos) {
++ IBlockData iblockdata = this.level().getBlockState(pos);
++
++ if (!iblockdata.isAir() && !iblockdata.is(Blocks.LILY_PAD)) {
++ FluidState fluid = iblockdata.getFluidState();
++
++ return fluid.is(FluidTags.WATER) && fluid.isSource() && iblockdata.getCollisionShape(this.level(), pos).isEmpty() ? FishingHook.WaterPosition.INSIDE_WATER : FishingHook.WaterPosition.INVALID;
+ } else {
+- return FishingHook.OpenWaterType.ABOVE_WATER;
++ return FishingHook.WaterPosition.ABOVE_WATER;
+ }
+ }
+
+@@ -430,58 +465,90 @@
+ }
+
+ @Override
+- public void addAdditionalSaveData(CompoundTag compound) {
+- }
++ public void addAdditionalSaveData(CompoundTag compound) {}
+
+ @Override
+- public void readAdditionalSaveData(CompoundTag compound) {
+- }
++ public void readAdditionalSaveData(CompoundTag compound) {}
+
+ public int retrieve(ItemStack stack) {
+- Player playerOwner = this.getPlayerOwner();
+- if (!this.level().isClientSide && playerOwner != null && !this.shouldStopFishing(playerOwner)) {
++ net.minecraft.world.entity.player.Player entityhuman = this.getPlayerOwner();
++
++ if (!this.level().isClientSide && entityhuman != null && !this.shouldStopFishing(entityhuman)) {
+ int i = 0;
++
+ if (this.hookedIn != null) {
++ // CraftBukkit start
++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) entityhuman.getBukkitEntity(), this.hookedIn.getBukkitEntity(), (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_ENTITY);
++ this.level().getCraftServer().getPluginManager().callEvent(playerFishEvent);
++
++ if (playerFishEvent.isCancelled()) {
++ return 0;
++ }
++ // CraftBukkit end
+ this.pullEntity(this.hookedIn);
+- CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer)playerOwner, stack, this, Collections.emptyList());
+- this.level().broadcastEntityEvent(this, (byte)31);
++ CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer) entityhuman, stack, this, Collections.emptyList());
++ this.level().broadcastEntityEvent(this, (byte) 31);
+ i = this.hookedIn instanceof ItemEntity ? 3 : 5;
+ } else if (this.nibble > 0) {
+- LootParams lootParams = new LootParams.Builder((ServerLevel)this.level())
+- .withParameter(LootContextParams.ORIGIN, this.position())
+- .withParameter(LootContextParams.TOOL, stack)
+- .withParameter(LootContextParams.THIS_ENTITY, this)
+- .withLuck((float)this.luck + playerOwner.getLuck())
+- .create(LootContextParamSets.FISHING);
+- LootTable lootTable = this.level().getServer().getLootData().getLootTable(BuiltInLootTables.FISHING);
+- List<ItemStack> randomItems = lootTable.getRandomItems(lootParams);
+- CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer)playerOwner, stack, this, randomItems);
++ LootParams lootparams = (new LootParams.Builder((ServerLevel) this.level())).withParameter(LootContextParams.ORIGIN, this.position()).withParameter(LootContextParams.TOOL, stack).withParameter(LootContextParams.THIS_ENTITY, this).withLuck((float) this.luck + entityhuman.getLuck()).create(LootContextParamSets.FISHING);
++ LootTable loottable = this.level().getServer().getLootData().getLootTable(BuiltInLootTables.FISHING);
++ List<ItemStack> list = loottable.getRandomItems(lootparams);
+
+- for (ItemStack itemStack : randomItems) {
+- ItemEntity itemEntity = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemStack);
+- double d = playerOwner.getX() - this.getX();
+- double d1 = playerOwner.getY() - this.getY();
+- double d2 = playerOwner.getZ() - this.getZ();
+- double d3 = 0.1;
+- itemEntity.setDeltaMovement(d * 0.1, d1 * 0.1 + Math.sqrt(Math.sqrt(d * d + d1 * d1 + d2 * d2)) * 0.08, d2 * 0.1);
+- this.level().addFreshEntity(itemEntity);
+- playerOwner.level()
+- .addFreshEntity(
+- new ExperienceOrb(
+- playerOwner.level(), playerOwner.getX(), playerOwner.getY() + 0.5, playerOwner.getZ() + 0.5, this.random.nextInt(6) + 1
+- )
+- );
+- if (itemStack.is(ItemTags.FISHES)) {
+- playerOwner.awardStat(Stats.FISH_CAUGHT, 1);
++ CriteriaTriggers.FISHING_ROD_HOOKED.trigger((ServerPlayer) entityhuman, stack, this, list);
++ Iterator iterator = list.iterator();
++
++ while (iterator.hasNext()) {
++ ItemStack itemstack1 = (ItemStack) iterator.next();
++ ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemstack1);
++ // CraftBukkit start
++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) entityhuman.getBukkitEntity(), entityitem.getBukkitEntity(), (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_FISH);
++ playerFishEvent.setExpToDrop(this.random.nextInt(6) + 1);
++ this.level().getCraftServer().getPluginManager().callEvent(playerFishEvent);
++
++ if (playerFishEvent.isCancelled()) {
++ return 0;
+ }
++ // CraftBukkit end
++ double d0 = entityhuman.getX() - this.getX();
++ double d1 = entityhuman.getY() - this.getY();
++ double d2 = entityhuman.getZ() - this.getZ();
++ double d3 = 0.1D;
++
++ entityitem.setDeltaMovement(d0 * 0.1D, d1 * 0.1D + Math.sqrt(Math.sqrt(d0 * d0 + d1 * d1 + d2 * d2)) * 0.08D, d2 * 0.1D);
++ this.level().addFreshEntity(entityitem);
++ // CraftBukkit start - this.random.nextInt(6) + 1 -> playerFishEvent.getExpToDrop()
++ if (playerFishEvent.getExpToDrop() > 0) {
++ entityhuman.level().addFreshEntity(new ExperienceOrb(entityhuman.level(), entityhuman.getX(), entityhuman.getY() + 0.5D, entityhuman.getZ() + 0.5D, playerFishEvent.getExpToDrop()));
++ }
++ // CraftBukkit end
++ if (itemstack1.is(ItemTags.FISHES)) {
++ entityhuman.awardStat(Stats.FISH_CAUGHT, 1);
++ }
+ }
+
+ i = 1;
+ }
+
+ if (this.onGround()) {
++ // CraftBukkit start
++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) entityhuman.getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.IN_GROUND);
++ this.level().getCraftServer().getPluginManager().callEvent(playerFishEvent);
++
++ if (playerFishEvent.isCancelled()) {
++ return 0;
++ }
++ // CraftBukkit end
+ i = 2;
+ }
++ // CraftBukkit start
++ if (i == 0) {
++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) entityhuman.getBukkitEntity(), null, (FishHook) this.getBukkitEntity(), PlayerFishEvent.State.REEL_IN);
++ this.level().getCraftServer().getPluginManager().callEvent(playerFishEvent);
++ if (playerFishEvent.isCancelled()) {
++ return 0;
++ }
++ }
++ // CraftBukkit end
+
+ this.discard();
+ return i;
+@@ -492,18 +559,20 @@
+
+ @Override
+ public void handleEntityEvent(byte id) {
+- if (id == 31 && this.level().isClientSide && this.hookedIn instanceof Player && ((Player)this.hookedIn).isLocalPlayer()) {
++ if (id == 31 && this.level().isClientSide && this.hookedIn instanceof net.minecraft.world.entity.player.Player && ((net.minecraft.world.entity.player.Player) this.hookedIn).isLocalPlayer()) {
+ this.pullEntity(this.hookedIn);
+ }
+
+ super.handleEntityEvent(id);
+ }
+
+- protected void pullEntity(Entity entity) {
+- Entity owner = this.getOwner();
+- if (owner != null) {
+- Vec3 vec3 = new Vec3(owner.getX() - this.getX(), owner.getY() - this.getY(), owner.getZ() - this.getZ()).scale(0.1);
+- entity.setDeltaMovement(entity.getDeltaMovement().add(vec3));
++ public void pullEntity(Entity entity) {
++ Entity entity1 = this.getOwner();
++
++ if (entity1 != null) {
++ Vec3 vec3d = (new Vec3(entity1.getX() - this.getX(), entity1.getY() - this.getY(), entity1.getZ() - this.getZ())).scale(0.1D);
++
++ entity.setDeltaMovement(entity.getDeltaMovement().add(vec3d));
+ }
+ }
+
+@@ -514,13 +583,13 @@
+
+ @Override
+ public void remove(Entity.RemovalReason reason) {
+- this.updateOwnerInfo(null);
++ this.updateOwnerInfo((FishingHook) null);
+ super.remove(reason);
+ }
+
+ @Override
+ public void onClientRemoval() {
+- this.updateOwnerInfo(null);
++ this.updateOwnerInfo((FishingHook) null);
+ }
+
+ @Override
+@@ -530,16 +599,19 @@
+ }
+
+ private void updateOwnerInfo(@Nullable FishingHook fishingHook) {
+- Player playerOwner = this.getPlayerOwner();
+- if (playerOwner != null) {
+- playerOwner.fishing = fishingHook;
++ net.minecraft.world.entity.player.Player entityhuman = this.getPlayerOwner();
++
++ if (entityhuman != null) {
++ entityhuman.fishing = fishingHook;
+ }
++
+ }
+
+ @Nullable
+- public Player getPlayerOwner() {
+- Entity owner = this.getOwner();
+- return owner instanceof Player ? (Player)owner : null;
++ public net.minecraft.world.entity.player.Player getPlayerOwner() {
++ Entity entity = this.getOwner();
++
++ return entity instanceof net.minecraft.world.entity.player.Player ? (net.minecraft.world.entity.player.Player) entity : null;
+ }
+
+ @Nullable
+@@ -554,29 +626,34 @@
+
+ @Override
+ public Packet<ClientGamePacketListener> getAddEntityPacket() {
+- Entity owner = this.getOwner();
+- return new ClientboundAddEntityPacket(this, owner == null ? this.getId() : owner.getId());
++ Entity entity = this.getOwner();
++
++ return new ClientboundAddEntityPacket(this, entity == null ? this.getId() : entity.getId());
+ }
+
+ @Override
+ public void recreateFromPacket(ClientboundAddEntityPacket packet) {
+ super.recreateFromPacket(packet);
+ if (this.getPlayerOwner() == null) {
+- int data = packet.getData();
+- LOGGER.error("Failed to recreate fishing hook on client. {} (id: {}) is not a valid owner.", this.level().getEntity(data), data);
++ int i = packet.getData();
++
++ FishingHook.LOGGER.error("Failed to recreate fishing hook on client. {} (id: {}) is not a valid owner.", this.level().getEntity(i), i);
+ this.kill();
+ }
++
+ }
+
+- static enum FishHookState {
+- FLYING,
+- HOOKED_IN_ENTITY,
+- BOBBING;
++ public static enum HookState {
++
++ FLYING, HOOKED_IN_ENTITY, BOBBING;
++
++ private HookState() {}
+ }
+
+- static enum OpenWaterType {
+- ABOVE_WATER,
+- INSIDE_WATER,
+- INVALID;
++ private static enum WaterPosition {
++
++ ABOVE_WATER, INSIDE_WATER, INVALID;
++
++ private WaterPosition() {}
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/LargeFireball.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/LargeFireball.java.patch
new file mode 100644
index 0000000000..badfa6564b
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/LargeFireball.java.patch
@@ -0,0 +1,86 @@
+--- a/net/minecraft/world/entity/projectile/LargeFireball.java
++++ b/net/minecraft/world/entity/projectile/LargeFireball.java
+@@ -8,27 +8,41 @@
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.phys.EntityHitResult;
+ import net.minecraft.world.phys.HitResult;
++import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit
+
+ public class LargeFireball extends Fireball {
+- private int explosionPower = 1;
+
++ public int explosionPower = 1;
++
+ public LargeFireball(EntityType<? extends LargeFireball> entityType, Level level) {
+ super(entityType, level);
++ isIncendiary = this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit
+ }
+
+- public LargeFireball(Level level, LivingEntity shooter, double offsetX, double offsetY, double offsetZ, int explosionPower) {
+- super(EntityType.FIREBALL, shooter, offsetX, offsetY, offsetZ, level);
+- this.explosionPower = explosionPower;
++ public LargeFireball(Level level, LivingEntity shooter, double offsetX, double d1, double offsetY, int i) {
++ super(EntityType.FIREBALL, shooter, offsetX, d1, offsetY, level);
++ this.explosionPower = i;
++ isIncendiary = this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); // CraftBukkit
+ }
+
+ @Override
+ protected void onHit(HitResult result) {
+ super.onHit(result);
+ if (!this.level().isClientSide) {
+- boolean _boolean = this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
+- this.level().explode(this, this.getX(), this.getY(), this.getZ(), (float)this.explosionPower, _boolean, Level.ExplosionInteraction.MOB);
++ boolean flag = this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
++
++ // CraftBukkit start - fire ExplosionPrimeEvent
++ ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) this.getBukkitEntity());
++ this.level().getCraftServer().getPluginManager().callEvent(event);
++
++ if (!event.isCancelled()) {
++ // give 'this' instead of (Entity) null so we know what causes the damage
++ this.level().explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.a.MOB);
++ }
++ // CraftBukkit end
+ this.discard();
+ }
++
+ }
+
+ @Override
+@@ -36,25 +50,29 @@
+ super.onHitEntity(result);
+ if (!this.level().isClientSide) {
+ Entity entity = result.getEntity();
+- Entity owner = this.getOwner();
+- entity.hurt(this.damageSources().fireball(this, owner), 6.0F);
+- if (owner instanceof LivingEntity) {
+- this.doEnchantDamageEffects((LivingEntity)owner, entity);
++ Entity entity1 = this.getOwner();
++
++ entity.hurt(this.damageSources().fireball(this, entity1), 6.0F);
++ if (entity1 instanceof LivingEntity) {
++ this.doEnchantDamageEffects((LivingEntity) entity1, entity);
+ }
++
+ }
+ }
+
+ @Override
+ public void addAdditionalSaveData(CompoundTag compound) {
+ super.addAdditionalSaveData(compound);
+- compound.putByte("ExplosionPower", (byte)this.explosionPower);
++ compound.putByte("ExplosionPower", (byte) this.explosionPower);
+ }
+
+ @Override
+ public void readAdditionalSaveData(CompoundTag compound) {
+ super.readAdditionalSaveData(compound);
+ if (compound.contains("ExplosionPower", 99)) {
+- this.explosionPower = compound.getByte("ExplosionPower");
++ // CraftBukkit - set bukkitYield when setting explosionpower
++ bukkitYield = this.explosionPower = compound.getByte("ExplosionPower");
+ }
++
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/LlamaSpit.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/LlamaSpit.java.patch
new file mode 100644
index 0000000000..ab781fd084
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/LlamaSpit.java.patch
@@ -0,0 +1,119 @@
+--- a/net/minecraft/world/entity/projectile/LlamaSpit.java
++++ b/net/minecraft/world/entity/projectile/LlamaSpit.java
+@@ -3,6 +3,7 @@
+ import net.minecraft.core.particles.ParticleTypes;
+ import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
+ import net.minecraft.util.Mth;
++import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityType;
+ import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.animal.horse.Llama;
+@@ -14,6 +15,7 @@
+ import net.minecraft.world.phys.Vec3;
+
+ public class LlamaSpit extends Projectile {
++
+ public LlamaSpit(EntityType<? extends LlamaSpit> entityType, Level level) {
+ super(entityType, level);
+ }
+@@ -21,45 +23,49 @@
+ public LlamaSpit(Level level, Llama spitter) {
+ this(EntityType.LLAMA_SPIT, level);
+ this.setOwner(spitter);
+- this.setPos(
+- spitter.getX() - (double)(spitter.getBbWidth() + 1.0F) * 0.5 * (double)Mth.sin(spitter.yBodyRot * (float) (Math.PI / 180.0)),
+- spitter.getEyeY() - 0.1F,
+- spitter.getZ() + (double)(spitter.getBbWidth() + 1.0F) * 0.5 * (double)Mth.cos(spitter.yBodyRot * (float) (Math.PI / 180.0))
+- );
++ this.setPos(spitter.getX() - (double) (spitter.getBbWidth() + 1.0F) * 0.5D * (double) Mth.sin(spitter.yBodyRot * 0.017453292F), spitter.getEyeY() - 0.10000000149011612D, spitter.getZ() + (double) (spitter.getBbWidth() + 1.0F) * 0.5D * (double) Mth.cos(spitter.yBodyRot * 0.017453292F));
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+- Vec3 deltaMovement = this.getDeltaMovement();
+- HitResult hitResultOnMoveVector = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
+- this.onHit(hitResultOnMoveVector);
+- double d = this.getX() + deltaMovement.x;
+- double d1 = this.getY() + deltaMovement.y;
+- double d2 = this.getZ() + deltaMovement.z;
++ Vec3 vec3d = this.getDeltaMovement();
++ HitResult movingobjectposition = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
++
++ this.preOnHit(movingobjectposition); // CraftBukkit - projectile hit event
++ double d0 = this.getX() + vec3d.x;
++ double d1 = this.getY() + vec3d.y;
++ double d2 = this.getZ() + vec3d.z;
++
+ this.updateRotation();
+ float f = 0.99F;
+ float f1 = 0.06F;
++
+ if (this.level().getBlockStates(this.getBoundingBox()).noneMatch(BlockBehaviour.BlockStateBase::isAir)) {
+ this.discard();
+ } else if (this.isInWaterOrBubble()) {
+ this.discard();
+ } else {
+- this.setDeltaMovement(deltaMovement.scale(0.99F));
++ this.setDeltaMovement(vec3d.scale(0.9900000095367432D));
+ if (!this.isNoGravity()) {
+- this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.06F, 0.0));
++ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.05999999865889549D, 0.0D));
+ }
+
+- this.setPos(d, d1, d2);
++ this.setPos(d0, d1, d2);
+ }
+ }
+
+ @Override
+ protected void onHitEntity(EntityHitResult result) {
+ super.onHitEntity(result);
+- if (this.getOwner() instanceof LivingEntity livingEntity) {
+- result.getEntity().hurt(this.damageSources().mobProjectile(this, livingEntity), 1.0F);
++ Entity entity = this.getOwner();
++
++ if (entity instanceof LivingEntity) {
++ LivingEntity entityliving = (LivingEntity) entity;
++
++ result.getEntity().hurt(this.damageSources().mobProjectile(this, entityliving), 1.0F);
+ }
++
+ }
+
+ @Override
+@@ -68,24 +74,25 @@
+ if (!this.level().isClientSide) {
+ this.discard();
+ }
++
+ }
+
+ @Override
+- protected void defineSynchedData() {
+- }
++ protected void defineSynchedData() {}
+
+ @Override
+ public void recreateFromPacket(ClientboundAddEntityPacket packet) {
+ super.recreateFromPacket(packet);
+- double xa = packet.getXa();
+- double ya = packet.getYa();
+- double za = packet.getZa();
++ double d0 = packet.getXa();
++ double d1 = packet.getYa();
++ double d2 = packet.getZa();
+
+- for (int i = 0; i < 7; i++) {
+- double d = 0.4 + 0.1 * (double)i;
+- this.level().addParticle(ParticleTypes.SPIT, this.getX(), this.getY(), this.getZ(), xa * d, ya, za * d);
++ for (int i = 0; i < 7; ++i) {
++ double d3 = 0.4D + 0.1D * (double) i;
++
++ this.level().addParticle(ParticleTypes.SPIT, this.getX(), this.getY(), this.getZ(), d0 * d3, d1, d2 * d3);
+ }
+
+- this.setDeltaMovement(xa, ya, za);
++ this.setDeltaMovement(d0, d1, d2);
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/Projectile.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/Projectile.java.patch
new file mode 100644
index 0000000000..2d066dcf81
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/Projectile.java.patch
@@ -0,0 +1,300 @@
+--- a/net/minecraft/world/entity/projectile/Projectile.java
++++ b/net/minecraft/world/entity/projectile/Projectile.java
+@@ -1,6 +1,7 @@
+ package net.minecraft.world.entity.projectile;
+
+ import com.google.common.base.MoreObjects;
++import java.util.Iterator;
+ import java.util.UUID;
+ import javax.annotation.Nullable;
+ import net.minecraft.core.BlockPos;
+@@ -17,14 +18,18 @@
+ import net.minecraft.world.entity.player.Player;
+ 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.BlockHitResult;
+ import net.minecraft.world.phys.EntityHitResult;
+ import net.minecraft.world.phys.HitResult;
+ import net.minecraft.world.phys.Vec3;
++// CraftBukkit start
++import org.bukkit.projectiles.ProjectileSource;
++// CraftBukkit end
+
+ public abstract class Projectile extends Entity implements TraceableEntity {
++
+ @Nullable
+ private UUID ownerUUID;
+ @Nullable
+@@ -32,6 +37,10 @@
+ private boolean leftOwner;
+ private boolean hasBeenShot;
+
++ // CraftBukkit start
++ private boolean hitCancelled = false;
++ // CraftBukkit end
++
+ Projectile(EntityType<? extends Projectile> entityType, Level level) {
+ super(entityType, level);
+ }
+@@ -41,6 +50,8 @@
+ this.ownerUUID = owner.getUUID();
+ this.cachedOwner = owner;
+ }
++ this.projectileSource = (owner != null && owner.getBukkitEntity() instanceof ProjectileSource) ? (ProjectileSource) owner.getBukkitEntity() : null; // CraftBukkit
++
+ }
+
+ @Nullable
+@@ -48,16 +59,24 @@
+ public Entity getOwner() {
+ if (this.cachedOwner != null && !this.cachedOwner.isRemoved()) {
+ return this.cachedOwner;
+- } else if (this.ownerUUID != null && this.level() instanceof ServerLevel serverLevel) {
+- this.cachedOwner = serverLevel.getEntity(this.ownerUUID);
+- return this.cachedOwner;
+ } else {
++ if (this.ownerUUID != null) {
++ Level world = this.level();
++
++ if (world instanceof ServerLevel) {
++ ServerLevel worldserver = (ServerLevel) world;
++
++ this.cachedOwner = worldserver.getEntity(this.ownerUUID);
++ return this.cachedOwner;
++ }
++ }
++
+ return null;
+ }
+ }
+
+ public Entity getEffectSource() {
+- return MoreObjects.firstNonNull(this.getOwner(), this);
++ return (Entity) MoreObjects.firstNonNull(this.getOwner(), this);
+ }
+
+ @Override
+@@ -91,9 +110,12 @@
+ @Override
+ public void restoreFrom(Entity entity) {
+ super.restoreFrom(entity);
+- if (entity instanceof Projectile projectile) {
+- this.cachedOwner = projectile.cachedOwner;
++ if (entity instanceof Projectile) {
++ Projectile iprojectile = (Projectile) entity;
++
++ this.cachedOwner = iprojectile.cachedOwner;
+ }
++
+ }
+
+ @Override
+@@ -111,13 +133,17 @@
+ }
+
+ private boolean checkLeftOwner() {
+- Entity owner = this.getOwner();
+- if (owner != null) {
+- for (Entity entity : this.level()
+- .getEntities(
+- this, this.getBoundingBox().expandTowards(this.getDeltaMovement()).inflate(1.0), entity1 -> !entity1.isSpectator() && entity1.isPickable()
+- )) {
+- if (entity.getRootVehicle() == owner.getRootVehicle()) {
++ Entity entity = this.getOwner();
++
++ if (entity != null) {
++ Iterator iterator = this.level().getEntities((Entity) this, this.getBoundingBox().expandTowards(this.getDeltaMovement()).inflate(1.0D), (entity1) -> {
++ return !entity1.isSpectator() && entity1.isPickable();
++ }).iterator();
++
++ while (iterator.hasNext()) {
++ Entity entity1 = (Entity) iterator.next();
++
++ if (entity1.getRootVehicle() == entity.getRootVehicle()) {
+ return false;
+ }
+ }
+@@ -126,80 +152,100 @@
+ return true;
+ }
+
+- public void shoot(double x, double y, double z, float velocity, float inaccuracy) {
+- Vec3 vec3 = new Vec3(x, y, z)
+- .normalize()
+- .add(
+- this.random.triangle(0.0, 0.0172275 * (double)inaccuracy),
+- this.random.triangle(0.0, 0.0172275 * (double)inaccuracy),
+- this.random.triangle(0.0, 0.0172275 * (double)inaccuracy)
+- )
+- .scale((double)velocity);
+- this.setDeltaMovement(vec3);
+- double d = vec3.horizontalDistance();
+- this.setYRot((float)(Mth.atan2(vec3.x, vec3.z) * 180.0F / (float)Math.PI));
+- this.setXRot((float)(Mth.atan2(vec3.y, d) * 180.0F / (float)Math.PI));
++ public void shoot(double x, double d1, double y, float f, float z) {
++ Vec3 vec3d = (new Vec3(x, d1, y)).normalize().add(this.random.triangle(0.0D, 0.0172275D * (double) z), this.random.triangle(0.0D, 0.0172275D * (double) z), this.random.triangle(0.0D, 0.0172275D * (double) z)).scale((double) f);
++
++ this.setDeltaMovement(vec3d);
++ double d3 = vec3d.horizontalDistance();
++
++ this.setYRot((float) (Mth.atan2(vec3d.x, vec3d.z) * 57.2957763671875D));
++ this.setXRot((float) (Mth.atan2(vec3d.y, d3) * 57.2957763671875D));
+ this.yRotO = this.getYRot();
+ this.xRotO = this.getXRot();
+ }
+
+ public void shootFromRotation(Entity shooter, float x, float y, float z, float velocity, float inaccuracy) {
+- float f = -Mth.sin(y * (float) (Math.PI / 180.0)) * Mth.cos(x * (float) (Math.PI / 180.0));
+- float f1 = -Mth.sin((x + z) * (float) (Math.PI / 180.0));
+- float f2 = Mth.cos(y * (float) (Math.PI / 180.0)) * Mth.cos(x * (float) (Math.PI / 180.0));
+- this.shoot((double)f, (double)f1, (double)f2, velocity, inaccuracy);
+- Vec3 deltaMovement = shooter.getDeltaMovement();
+- this.setDeltaMovement(this.getDeltaMovement().add(deltaMovement.x, shooter.onGround() ? 0.0 : deltaMovement.y, deltaMovement.z));
++ float f5 = -Mth.sin(y * 0.017453292F) * Mth.cos(x * 0.017453292F);
++ float f6 = -Mth.sin((x + z) * 0.017453292F);
++ float f7 = Mth.cos(y * 0.017453292F) * Mth.cos(x * 0.017453292F);
++
++ this.shoot((double) f5, (double) f6, (double) f7, velocity, inaccuracy);
++ Vec3 vec3d = shooter.getDeltaMovement();
++
++ this.setDeltaMovement(this.getDeltaMovement().add(vec3d.x, shooter.onGround() ? 0.0D : vec3d.y, vec3d.z));
+ }
+
+- protected void onHit(HitResult result) {
+- HitResult.Type type = result.getType();
+- if (type == HitResult.Type.ENTITY) {
+- this.onHitEntity((EntityHitResult)result);
+- this.level().gameEvent(GameEvent.PROJECTILE_LAND, result.getLocation(), GameEvent.Context.of(this, null));
+- } else if (type == HitResult.Type.BLOCK) {
+- BlockHitResult blockHitResult = (BlockHitResult)result;
+- this.onHitBlock(blockHitResult);
+- BlockPos blockPos = blockHitResult.getBlockPos();
+- this.level().gameEvent(GameEvent.PROJECTILE_LAND, blockPos, GameEvent.Context.of(this, this.level().getBlockState(blockPos)));
++ // CraftBukkit start - call projectile hit event
++ protected void preOnHit(HitResult movingobjectposition) {
++ org.bukkit.event.entity.ProjectileHitEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition);
++ this.hitCancelled = event != null && event.isCancelled();
++ if (movingobjectposition.getType() == HitResult.EnumMovingObjectType.BLOCK || !this.hitCancelled) {
++ this.onHit(movingobjectposition);
+ }
+ }
++ // CraftBukkit end
+
+- protected void onHitEntity(EntityHitResult result) {
++ protected void onHit(HitResult result) {
++ HitResult.EnumMovingObjectType movingobjectposition_enummovingobjecttype = result.getType();
++
++ if (movingobjectposition_enummovingobjecttype == HitResult.EnumMovingObjectType.ENTITY) {
++ this.onHitEntity((EntityHitResult) result);
++ this.level().gameEvent(GameEvent.PROJECTILE_LAND, result.getLocation(), GameEvent.Context.of(this, (IBlockData) null));
++ } else if (movingobjectposition_enummovingobjecttype == HitResult.EnumMovingObjectType.BLOCK) {
++ BlockHitResult movingobjectpositionblock = (BlockHitResult) result;
++
++ this.onHitBlock(movingobjectpositionblock);
++ BlockPos blockposition = movingobjectpositionblock.getBlockPos();
++
++ this.level().gameEvent(GameEvent.PROJECTILE_LAND, blockposition, GameEvent.Context.of(this, this.level().getBlockState(blockposition)));
++ }
++
+ }
+
++ protected void onHitEntity(EntityHitResult result) {}
++
+ protected void onHitBlock(BlockHitResult result) {
+- BlockState blockState = this.level().getBlockState(result.getBlockPos());
+- blockState.onProjectileHit(this.level(), blockState, result, this);
++ // CraftBukkit start - cancellable hit event
++ if (hitCancelled) {
++ return;
++ }
++ // CraftBukkit end
++ IBlockData iblockdata = this.level().getBlockState(result.getBlockPos());
++
++ iblockdata.onProjectileHit(this.level(), iblockdata, result, this);
+ }
+
+ @Override
+- public void lerpMotion(double x, double y, double z) {
+- this.setDeltaMovement(x, y, z);
++ public void lerpMotion(double x, double d1, double y) {
++ this.setDeltaMovement(x, d1, y);
+ if (this.xRotO == 0.0F && this.yRotO == 0.0F) {
+- double squareRoot = Math.sqrt(x * x + z * z);
+- this.setXRot((float)(Mth.atan2(y, squareRoot) * 180.0F / (float)Math.PI));
+- this.setYRot((float)(Mth.atan2(x, z) * 180.0F / (float)Math.PI));
++ double d3 = Math.sqrt(x * x + y * y);
++
++ this.setXRot((float) (Mth.atan2(d1, d3) * 57.2957763671875D));
++ this.setYRot((float) (Mth.atan2(x, y) * 57.2957763671875D));
+ this.xRotO = this.getXRot();
+ this.yRotO = this.getYRot();
+ this.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
+ }
++
+ }
+
+ protected boolean canHitEntity(Entity target) {
+ if (!target.canBeHitByProjectile()) {
+ return false;
+ } else {
+- Entity owner = this.getOwner();
+- return owner == null || this.leftOwner || !owner.isPassengerOfSameVehicle(target);
++ Entity entity1 = this.getOwner();
++
++ return entity1 == null || this.leftOwner || !entity1.isPassengerOfSameVehicle(target);
+ }
+ }
+
+ protected void updateRotation() {
+- Vec3 deltaMovement = this.getDeltaMovement();
+- double d = deltaMovement.horizontalDistance();
+- this.setXRot(lerpRotation(this.xRotO, (float)(Mth.atan2(deltaMovement.y, d) * 180.0F / (float)Math.PI)));
+- this.setYRot(lerpRotation(this.yRotO, (float)(Mth.atan2(deltaMovement.x, deltaMovement.z) * 180.0F / (float)Math.PI)));
++ Vec3 vec3d = this.getDeltaMovement();
++ double d0 = vec3d.horizontalDistance();
++
++ this.setXRot(lerpRotation(this.xRotO, (float) (Mth.atan2(vec3d.y, d0) * 57.2957763671875D)));
++ this.setYRot(lerpRotation(this.yRotO, (float) (Mth.atan2(vec3d.x, vec3d.z) * 57.2957763671875D)));
+ }
+
+ protected static float lerpRotation(float currentRotation, float targetRotation) {
+@@ -216,26 +262,30 @@
+
+ @Override
+ public Packet<ClientGamePacketListener> getAddEntityPacket() {
+- Entity owner = this.getOwner();
+- return new ClientboundAddEntityPacket(this, owner == null ? 0 : owner.getId());
++ Entity entity = this.getOwner();
++
++ return new ClientboundAddEntityPacket(this, entity == null ? 0 : entity.getId());
+ }
+
+ @Override
+ public void recreateFromPacket(ClientboundAddEntityPacket packet) {
+ super.recreateFromPacket(packet);
+ Entity entity = this.level().getEntity(packet.getData());
++
+ if (entity != null) {
+ this.setOwner(entity);
+ }
++
+ }
+
+ @Override
+ public boolean mayInteract(Level level, BlockPos pos) {
+- Entity owner = this.getOwner();
+- return owner instanceof Player ? owner.mayInteract(level, pos) : owner == null || level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
++ Entity entity = this.getOwner();
++
++ return entity instanceof Player ? entity.mayInteract(level, pos) : entity == null || level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
+ }
+
+- public boolean mayBreak(Level level) {
+- return this.getType().is(EntityTypeTags.IMPACT_PROJECTILES) && level.getGameRules().getBoolean(GameRules.RULE_PROJECTILESCANBREAKBLOCKS);
++ public boolean mayBreak(Level world) {
++ return this.getType().is(EntityTypeTags.IMPACT_PROJECTILES) && world.getGameRules().getBoolean(GameRules.RULE_PROJECTILESCANBREAKBLOCKS);
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ShulkerBullet.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ShulkerBullet.java.patch
new file mode 100644
index 0000000000..0242bf32b8
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ShulkerBullet.java.patch
@@ -0,0 +1,357 @@
+--- a/net/minecraft/world/entity/projectile/ShulkerBullet.java
++++ b/net/minecraft/world/entity/projectile/ShulkerBullet.java
+@@ -30,7 +30,8 @@
+ import net.minecraft.world.phys.Vec3;
+
+ public class ShulkerBullet extends Projectile {
+- private static final double SPEED = 0.15;
++
++ private static final double SPEED = 0.15D;
+ @Nullable
+ private Entity finalTarget;
+ @Nullable
+@@ -50,16 +51,30 @@
+ public ShulkerBullet(Level level, LivingEntity shooter, Entity finalTarget, Direction.Axis axis) {
+ this(EntityType.SHULKER_BULLET, level);
+ this.setOwner(shooter);
+- BlockPos blockPos = shooter.blockPosition();
+- double d = (double)blockPos.getX() + 0.5;
+- double d1 = (double)blockPos.getY() + 0.5;
+- double d2 = (double)blockPos.getZ() + 0.5;
+- this.moveTo(d, d1, d2, this.getYRot(), this.getXRot());
++ BlockPos blockposition = shooter.blockPosition();
++ double d0 = (double) blockposition.getX() + 0.5D;
++ double d1 = (double) blockposition.getY() + 0.5D;
++ double d2 = (double) blockposition.getZ() + 0.5D;
++
++ this.moveTo(d0, d1, d2, this.getYRot(), this.getXRot());
+ this.finalTarget = finalTarget;
+ this.currentMoveDirection = Direction.UP;
+ this.selectNextMoveDirection(axis);
++ projectileSource = (org.bukkit.entity.LivingEntity) shooter.getBukkitEntity(); // CraftBukkit
+ }
+
++ // CraftBukkit start
++ public Entity getTarget() {
++ return this.finalTarget;
++ }
++
++ public void setTarget(Entity e) {
++ this.finalTarget = e;
++ this.currentMoveDirection = Direction.UP;
++ this.selectNextMoveDirection(Direction.Axis.X);
++ }
++ // CraftBukkit end
++
+ @Override
+ public SoundSource getSoundSource() {
+ return SoundSource.HOSTILE;
+@@ -96,11 +111,11 @@
+ if (compound.hasUUID("Target")) {
+ this.targetId = compound.getUUID("Target");
+ }
++
+ }
+
+ @Override
+- protected void defineSynchedData() {
+- }
++ protected void defineSynchedData() {}
+
+ @Nullable
+ private Direction getMoveDirection() {
+@@ -112,73 +127,77 @@
+ }
+
+ private void selectNextMoveDirection(@Nullable Direction.Axis axis) {
+- double d = 0.5;
+- BlockPos blockPos;
++ double d0 = 0.5D;
++ BlockPos blockposition;
++
+ if (this.finalTarget == null) {
+- blockPos = this.blockPosition().below();
++ blockposition = this.blockPosition().below();
+ } else {
+- d = (double)this.finalTarget.getBbHeight() * 0.5;
+- blockPos = BlockPos.containing(this.finalTarget.getX(), this.finalTarget.getY() + d, this.finalTarget.getZ());
++ d0 = (double) this.finalTarget.getBbHeight() * 0.5D;
++ blockposition = BlockPos.containing(this.finalTarget.getX(), this.finalTarget.getY() + d0, this.finalTarget.getZ());
+ }
+
+- double d1 = (double)blockPos.getX() + 0.5;
+- double d2 = (double)blockPos.getY() + d;
+- double d3 = (double)blockPos.getZ() + 0.5;
+- Direction direction = null;
+- if (!blockPos.closerToCenterThan(this.position(), 2.0)) {
+- BlockPos blockPos1 = this.blockPosition();
++ double d1 = (double) blockposition.getX() + 0.5D;
++ double d2 = (double) blockposition.getY() + d0;
++ double d3 = (double) blockposition.getZ() + 0.5D;
++ Direction enumdirection = null;
++
++ if (!blockposition.closerToCenterThan(this.position(), 2.0D)) {
++ BlockPos blockposition1 = this.blockPosition();
+ List<Direction> list = Lists.newArrayList();
++
+ if (axis != Direction.Axis.X) {
+- if (blockPos1.getX() < blockPos.getX() && this.level().isEmptyBlock(blockPos1.east())) {
++ if (blockposition1.getX() < blockposition.getX() && this.level().isEmptyBlock(blockposition1.east())) {
+ list.add(Direction.EAST);
+- } else if (blockPos1.getX() > blockPos.getX() && this.level().isEmptyBlock(blockPos1.west())) {
++ } else if (blockposition1.getX() > blockposition.getX() && this.level().isEmptyBlock(blockposition1.west())) {
+ list.add(Direction.WEST);
+ }
+ }
+
+ if (axis != Direction.Axis.Y) {
+- if (blockPos1.getY() < blockPos.getY() && this.level().isEmptyBlock(blockPos1.above())) {
++ if (blockposition1.getY() < blockposition.getY() && this.level().isEmptyBlock(blockposition1.above())) {
+ list.add(Direction.UP);
+- } else if (blockPos1.getY() > blockPos.getY() && this.level().isEmptyBlock(blockPos1.below())) {
++ } else if (blockposition1.getY() > blockposition.getY() && this.level().isEmptyBlock(blockposition1.below())) {
+ list.add(Direction.DOWN);
+ }
+ }
+
+ if (axis != Direction.Axis.Z) {
+- if (blockPos1.getZ() < blockPos.getZ() && this.level().isEmptyBlock(blockPos1.south())) {
++ if (blockposition1.getZ() < blockposition.getZ() && this.level().isEmptyBlock(blockposition1.south())) {
+ list.add(Direction.SOUTH);
+- } else if (blockPos1.getZ() > blockPos.getZ() && this.level().isEmptyBlock(blockPos1.north())) {
++ } else if (blockposition1.getZ() > blockposition.getZ() && this.level().isEmptyBlock(blockposition1.north())) {
+ list.add(Direction.NORTH);
+ }
+ }
+
+- direction = Direction.getRandom(this.random);
++ enumdirection = Direction.getRandom(this.random);
+ if (list.isEmpty()) {
+- for (int i = 5; !this.level().isEmptyBlock(blockPos1.relative(direction)) && i > 0; i--) {
+- direction = Direction.getRandom(this.random);
++ for (int i = 5; !this.level().isEmptyBlock(blockposition1.relative(enumdirection)) && i > 0; --i) {
++ enumdirection = Direction.getRandom(this.random);
+ }
+ } else {
+- direction = list.get(this.random.nextInt(list.size()));
++ enumdirection = (Direction) list.get(this.random.nextInt(list.size()));
+ }
+
+- d1 = this.getX() + (double)direction.getStepX();
+- d2 = this.getY() + (double)direction.getStepY();
+- d3 = this.getZ() + (double)direction.getStepZ();
++ d1 = this.getX() + (double) enumdirection.getStepX();
++ d2 = this.getY() + (double) enumdirection.getStepY();
++ d3 = this.getZ() + (double) enumdirection.getStepZ();
+ }
+
+- this.setMoveDirection(direction);
++ this.setMoveDirection(enumdirection);
+ double d4 = d1 - this.getX();
+ double d5 = d2 - this.getY();
+ double d6 = d3 - this.getZ();
+- double squareRoot = Math.sqrt(d4 * d4 + d5 * d5 + d6 * d6);
+- if (squareRoot == 0.0) {
+- this.targetDeltaX = 0.0;
+- this.targetDeltaY = 0.0;
+- this.targetDeltaZ = 0.0;
++ double d7 = Math.sqrt(d4 * d4 + d5 * d5 + d6 * d6);
++
++ if (d7 == 0.0D) {
++ this.targetDeltaX = 0.0D;
++ this.targetDeltaY = 0.0D;
++ this.targetDeltaZ = 0.0D;
+ } else {
+- this.targetDeltaX = d4 / squareRoot * 0.15;
+- this.targetDeltaY = d5 / squareRoot * 0.15;
+- this.targetDeltaZ = d6 / squareRoot * 0.15;
++ this.targetDeltaX = d4 / d7 * 0.15D;
++ this.targetDeltaY = d5 / d7 * 0.15D;
++ this.targetDeltaZ = d6 / d7 * 0.15D;
+ }
+
+ this.hasImpulse = true;
+@@ -190,73 +209,69 @@
+ if (this.level().getDifficulty() == Difficulty.PEACEFUL) {
+ this.discard();
+ }
++
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
++ Vec3 vec3d;
++
+ if (!this.level().isClientSide) {
+ if (this.finalTarget == null && this.targetId != null) {
+- this.finalTarget = ((ServerLevel)this.level()).getEntity(this.targetId);
++ this.finalTarget = ((ServerLevel) this.level()).getEntity(this.targetId);
+ if (this.finalTarget == null) {
+ this.targetId = null;
+ }
+ }
+
+- if (this.finalTarget == null || !this.finalTarget.isAlive() || this.finalTarget instanceof Player && this.finalTarget.isSpectator()) {
+- if (!this.isNoGravity()) {
+- this.setDeltaMovement(this.getDeltaMovement().add(0.0, -0.04, 0.0));
+- }
+- } else {
+- this.targetDeltaX = Mth.clamp(this.targetDeltaX * 1.025, -1.0, 1.0);
+- this.targetDeltaY = Mth.clamp(this.targetDeltaY * 1.025, -1.0, 1.0);
+- this.targetDeltaZ = Mth.clamp(this.targetDeltaZ * 1.025, -1.0, 1.0);
+- Vec3 deltaMovement = this.getDeltaMovement();
+- this.setDeltaMovement(
+- deltaMovement.add(
+- (this.targetDeltaX - deltaMovement.x) * 0.2, (this.targetDeltaY - deltaMovement.y) * 0.2, (this.targetDeltaZ - deltaMovement.z) * 0.2
+- )
+- );
++ if (this.finalTarget != null && this.finalTarget.isAlive() && (!(this.finalTarget instanceof Player) || !this.finalTarget.isSpectator())) {
++ this.targetDeltaX = Mth.clamp(this.targetDeltaX * 1.025D, -1.0D, 1.0D);
++ this.targetDeltaY = Mth.clamp(this.targetDeltaY * 1.025D, -1.0D, 1.0D);
++ this.targetDeltaZ = Mth.clamp(this.targetDeltaZ * 1.025D, -1.0D, 1.0D);
++ vec3d = this.getDeltaMovement();
++ this.setDeltaMovement(vec3d.add((this.targetDeltaX - vec3d.x) * 0.2D, (this.targetDeltaY - vec3d.y) * 0.2D, (this.targetDeltaZ - vec3d.z) * 0.2D));
++ } else if (!this.isNoGravity()) {
++ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, -0.04D, 0.0D));
+ }
+
+- HitResult hitResultOnMoveVector = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
+- if (hitResultOnMoveVector.getType() != HitResult.Type.MISS) {
+- this.onHit(hitResultOnMoveVector);
++ HitResult movingobjectposition = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
++
++ if (movingobjectposition.getType() != HitResult.EnumMovingObjectType.MISS) {
++ this.preOnHit(movingobjectposition); // CraftBukkit - projectile hit event
+ }
+ }
+
+ this.checkInsideBlocks();
+- Vec3 deltaMovement = this.getDeltaMovement();
+- this.setPos(this.getX() + deltaMovement.x, this.getY() + deltaMovement.y, this.getZ() + deltaMovement.z);
++ vec3d = this.getDeltaMovement();
++ this.setPos(this.getX() + vec3d.x, this.getY() + vec3d.y, this.getZ() + vec3d.z);
+ ProjectileUtil.rotateTowardsMovement(this, 0.5F);
+ if (this.level().isClientSide) {
+- this.level()
+- .addParticle(
+- ParticleTypes.END_ROD, this.getX() - deltaMovement.x, this.getY() - deltaMovement.y + 0.15, this.getZ() - deltaMovement.z, 0.0, 0.0, 0.0
+- );
++ this.level().addParticle(ParticleTypes.END_ROD, this.getX() - vec3d.x, this.getY() - vec3d.y + 0.15D, this.getZ() - vec3d.z, 0.0D, 0.0D, 0.0D);
+ } else if (this.finalTarget != null && !this.finalTarget.isRemoved()) {
+ if (this.flightSteps > 0) {
+- this.flightSteps--;
++ --this.flightSteps;
+ if (this.flightSteps == 0) {
+ this.selectNextMoveDirection(this.currentMoveDirection == null ? null : this.currentMoveDirection.getAxis());
+ }
+ }
+
+ if (this.currentMoveDirection != null) {
+- BlockPos blockPos = this.blockPosition();
+- Direction.Axis axis = this.currentMoveDirection.getAxis();
+- if (this.level().loadedAndEntityCanStandOn(blockPos.relative(this.currentMoveDirection), this)) {
+- this.selectNextMoveDirection(axis);
++ BlockPos blockposition = this.blockPosition();
++ Direction.Axis enumdirection_enumaxis = this.currentMoveDirection.getAxis();
++
++ if (this.level().loadedAndEntityCanStandOn(blockposition.relative(this.currentMoveDirection), this)) {
++ this.selectNextMoveDirection(enumdirection_enumaxis);
+ } else {
+- BlockPos blockPos1 = this.finalTarget.blockPosition();
+- if (axis == Direction.Axis.X && blockPos.getX() == blockPos1.getX()
+- || axis == Direction.Axis.Z && blockPos.getZ() == blockPos1.getZ()
+- || axis == Direction.Axis.Y && blockPos.getY() == blockPos1.getY()) {
+- this.selectNextMoveDirection(axis);
++ BlockPos blockposition1 = this.finalTarget.blockPosition();
++
++ if (enumdirection_enumaxis == Direction.Axis.X && blockposition.getX() == blockposition1.getX() || enumdirection_enumaxis == Direction.Axis.Z && blockposition.getZ() == blockposition1.getZ() || enumdirection_enumaxis == Direction.Axis.Y && blockposition.getY() == blockposition1.getY()) {
++ this.selectNextMoveDirection(enumdirection_enumaxis);
+ }
+ }
+ }
+ }
++
+ }
+
+ @Override
+@@ -271,7 +286,7 @@
+
+ @Override
+ public boolean shouldRenderAtSqrDistance(double distance) {
+- return distance < 16384.0;
++ return distance < 16384.0D;
+ }
+
+ @Override
+@@ -283,27 +298,31 @@
+ protected void onHitEntity(EntityHitResult result) {
+ super.onHitEntity(result);
+ Entity entity = result.getEntity();
+- Entity owner = this.getOwner();
+- LivingEntity livingEntity = owner instanceof LivingEntity ? (LivingEntity)owner : null;
+- boolean flag = entity.hurt(this.damageSources().mobProjectile(this, livingEntity), 4.0F);
++ Entity entity1 = this.getOwner();
++ LivingEntity entityliving = entity1 instanceof LivingEntity ? (LivingEntity) entity1 : null;
++ boolean flag = entity.hurt(this.damageSources().mobProjectile(this, entityliving), 4.0F);
++
+ if (flag) {
+- this.doEnchantDamageEffects(livingEntity, entity);
+- if (entity instanceof LivingEntity livingEntity1) {
+- livingEntity1.addEffect(new MobEffectInstance(MobEffects.LEVITATION, 200), MoreObjects.firstNonNull(owner, this));
++ this.doEnchantDamageEffects(entityliving, entity);
++ if (entity instanceof LivingEntity) {
++ LivingEntity entityliving1 = (LivingEntity) entity;
++
++ entityliving1.addEffect(new MobEffectInstance(MobEffects.LEVITATION, 200), (Entity) MoreObjects.firstNonNull(entity1, this), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit
+ }
+ }
++
+ }
+
+ @Override
+ protected void onHitBlock(BlockHitResult result) {
+ super.onHitBlock(result);
+- ((ServerLevel)this.level()).sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(), this.getZ(), 2, 0.2, 0.2, 0.2, 0.0);
++ ((ServerLevel) this.level()).sendParticles(ParticleTypes.EXPLOSION, this.getX(), this.getY(), this.getZ(), 2, 0.2D, 0.2D, 0.2D, 0.0D);
+ this.playSound(SoundEvents.SHULKER_BULLET_HIT, 1.0F, 1.0F);
+ }
+
+ private void destroy() {
+ this.discard();
+- this.level().gameEvent(GameEvent.ENTITY_DAMAGE, this.position(), GameEvent.Context.of(this));
++ this.level().gameEvent(GameEvent.ENTITY_DAMAGE, this.position(), GameEvent.Context.of((Entity) this));
+ }
+
+ @Override
+@@ -319,9 +338,14 @@
+
+ @Override
+ public boolean hurt(DamageSource source, float amount) {
++ // CraftBukkit start
++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, source, amount, false)) {
++ return false;
++ }
++ // CraftBukkit end
+ if (!this.level().isClientSide) {
+ this.playSound(SoundEvents.SHULKER_BULLET_HURT, 1.0F, 1.0F);
+- ((ServerLevel)this.level()).sendParticles(ParticleTypes.CRIT, this.getX(), this.getY(), this.getZ(), 15, 0.2, 0.2, 0.2, 0.0);
++ ((ServerLevel) this.level()).sendParticles(ParticleTypes.CRIT, this.getX(), this.getY(), this.getZ(), 15, 0.2D, 0.2D, 0.2D, 0.0D);
+ this.destroy();
+ }
+
+@@ -331,9 +355,10 @@
+ @Override
+ public void recreateFromPacket(ClientboundAddEntityPacket packet) {
+ super.recreateFromPacket(packet);
+- double xa = packet.getXa();
+- double ya = packet.getYa();
+- double za = packet.getZa();
+- this.setDeltaMovement(xa, ya, za);
++ double d0 = packet.getXa();
++ double d1 = packet.getYa();
++ double d2 = packet.getZa();
++
++ this.setDeltaMovement(d0, d1, d2);
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/SmallFireball.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/SmallFireball.java.patch
new file mode 100644
index 0000000000..f62e624986
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/SmallFireball.java.patch
@@ -0,0 +1,93 @@
+--- a/net/minecraft/world/entity/projectile/SmallFireball.java
++++ b/net/minecraft/world/entity/projectile/SmallFireball.java
+@@ -12,18 +12,25 @@
+ import net.minecraft.world.phys.BlockHitResult;
+ import net.minecraft.world.phys.EntityHitResult;
+ import net.minecraft.world.phys.HitResult;
++import org.bukkit.event.entity.EntityCombustByEntityEvent; // CraftBukkit
+
+ public class SmallFireball extends Fireball {
++
+ public SmallFireball(EntityType<? extends SmallFireball> entityType, Level level) {
+ super(entityType, level);
+ }
+
+- public SmallFireball(Level level, LivingEntity shooter, double offsetX, double offsetY, double offsetZ) {
+- super(EntityType.SMALL_FIREBALL, shooter, offsetX, offsetY, offsetZ, level);
++ public SmallFireball(Level level, LivingEntity shooter, double offsetX, double d1, double offsetY) {
++ super(EntityType.SMALL_FIREBALL, shooter, offsetX, d1, offsetY, level);
++ // CraftBukkit start
++ if (this.getOwner() != null && this.getOwner() instanceof Mob) {
++ isIncendiary = this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
++ }
++ // CraftBukkit end
+ }
+
+- public SmallFireball(Level level, double x, double y, double z, double offsetX, double offsetY, double offsetZ) {
+- super(EntityType.SMALL_FIREBALL, x, y, z, offsetX, offsetY, offsetZ, level);
++ public SmallFireball(Level level, double x, double d1, double y, double d3, double z, double d5) {
++ super(EntityType.SMALL_FIREBALL, x, d1, y, d3, z, d5, level);
+ }
+
+ @Override
+@@ -31,14 +38,23 @@
+ super.onHitEntity(result);
+ if (!this.level().isClientSide) {
+ Entity entity = result.getEntity();
+- Entity owner = this.getOwner();
+- int remainingFireTicks = entity.getRemainingFireTicks();
+- entity.setSecondsOnFire(5);
+- if (!entity.hurt(this.damageSources().fireball(this, owner), 5.0F)) {
+- entity.setRemainingFireTicks(remainingFireTicks);
+- } else if (owner instanceof LivingEntity) {
+- this.doEnchantDamageEffects((LivingEntity)owner, entity);
++ Entity entity1 = this.getOwner();
++ int i = entity.getRemainingFireTicks();
++
++ // CraftBukkit start - Entity damage by entity event + combust event
++ EntityCombustByEntityEvent event = new EntityCombustByEntityEvent((org.bukkit.entity.Projectile) this.getBukkitEntity(), entity.getBukkitEntity(), 5);
++ entity.level().getCraftServer().getPluginManager().callEvent(event);
++
++ if (!event.isCancelled()) {
++ entity.setSecondsOnFire(event.getDuration(), false);
+ }
++ // CraftBukkit end
++ if (!entity.hurt(this.damageSources().fireball(this, entity1), 5.0F)) {
++ entity.setRemainingFireTicks(i);
++ } else if (entity1 instanceof LivingEntity) {
++ this.doEnchantDamageEffects((LivingEntity) entity1, entity);
++ }
++
+ }
+ }
+
+@@ -46,13 +62,16 @@
+ protected void onHitBlock(BlockHitResult result) {
+ super.onHitBlock(result);
+ if (!this.level().isClientSide) {
+- Entity owner = this.getOwner();
+- if (!(owner instanceof Mob) || this.level().getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) {
+- BlockPos blockPos = result.getBlockPos().relative(result.getDirection());
+- if (this.level().isEmptyBlock(blockPos)) {
+- this.level().setBlockAndUpdate(blockPos, BaseFireBlock.getState(this.level(), blockPos));
++ Entity entity = this.getOwner();
++
++ if (isIncendiary) { // CraftBukkit
++ BlockPos blockposition = result.getBlockPos().relative(result.getDirection());
++
++ if (this.level().isEmptyBlock(blockposition) && !org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.level(), blockposition, this).isCancelled()) { // CraftBukkit
++ this.level().setBlockAndUpdate(blockposition, BaseFireBlock.getState(this.level(), blockposition));
+ }
+ }
++
+ }
+ }
+
+@@ -62,6 +81,7 @@
+ if (!this.level().isClientSide) {
+ this.discard();
+ }
++
+ }
+
+ @Override
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/SpectralArrow.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/SpectralArrow.java.patch
new file mode 100644
index 0000000000..3de2d9a67c
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/SpectralArrow.java.patch
@@ -0,0 +1,57 @@
+--- a/net/minecraft/world/entity/projectile/SpectralArrow.java
++++ b/net/minecraft/world/entity/projectile/SpectralArrow.java
+@@ -11,34 +11,37 @@
+ import net.minecraft.world.level.Level;
+
+ public class SpectralArrow extends AbstractArrow {
++
+ private static final ItemStack DEFAULT_ARROW_STACK = new ItemStack(Items.SPECTRAL_ARROW);
+- private int duration = 200;
++ public int duration = 200;
+
+ public SpectralArrow(EntityType<? extends SpectralArrow> entityType, Level level) {
+- super(entityType, level, DEFAULT_ARROW_STACK);
++ super(entityType, level, SpectralArrow.DEFAULT_ARROW_STACK);
+ }
+
+- public SpectralArrow(Level level, LivingEntity livingEntity, ItemStack itemStack) {
+- super(EntityType.SPECTRAL_ARROW, livingEntity, level, itemStack);
++ public SpectralArrow(Level world, LivingEntity entityliving, ItemStack itemstack) {
++ super(EntityType.SPECTRAL_ARROW, entityliving, world, itemstack);
+ }
+
+- public SpectralArrow(Level level, double d, double d1, double d2, ItemStack itemStack) {
+- super(EntityType.SPECTRAL_ARROW, d, d1, d2, level, itemStack);
++ public SpectralArrow(Level world, double d0, double d1, double d2, ItemStack itemstack) {
++ super(EntityType.SPECTRAL_ARROW, d0, d1, d2, world, itemstack);
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+ if (this.level().isClientSide && !this.inGround) {
+- this.level().addParticle(ParticleTypes.INSTANT_EFFECT, this.getX(), this.getY(), this.getZ(), 0.0, 0.0, 0.0);
++ this.level().addParticle(ParticleTypes.INSTANT_EFFECT, this.getX(), this.getY(), this.getZ(), 0.0D, 0.0D, 0.0D);
+ }
++
+ }
+
+ @Override
+ protected void doPostHurtEffects(LivingEntity living) {
+ super.doPostHurtEffects(living);
+- MobEffectInstance mobEffectInstance = new MobEffectInstance(MobEffects.GLOWING, this.duration, 0);
+- living.addEffect(mobEffectInstance, this.getEffectSource());
++ MobEffectInstance mobeffect = new MobEffectInstance(MobEffects.GLOWING, this.duration, 0);
++
++ living.addEffect(mobeffect, this.getEffectSource(), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ARROW); // CraftBukkit
+ }
+
+ @Override
+@@ -47,6 +50,7 @@
+ if (compound.contains("Duration")) {
+ this.duration = compound.getInt("Duration");
+ }
++
+ }
+
+ @Override
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrowableItemProjectile.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrowableItemProjectile.java.patch
new file mode 100644
index 0000000000..e27c9b42d6
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrowableItemProjectile.java.patch
@@ -0,0 +1,86 @@
+--- a/net/minecraft/world/entity/projectile/ThrowableItemProjectile.java
++++ b/net/minecraft/world/entity/projectile/ThrowableItemProjectile.java
+@@ -11,16 +11,15 @@
+ import net.minecraft.world.level.Level;
+
+ public abstract class ThrowableItemProjectile extends ThrowableProjectile implements ItemSupplier {
+- private static final EntityDataAccessor<ItemStack> DATA_ITEM_STACK = SynchedEntityData.defineId(
+- ThrowableItemProjectile.class, EntityDataSerializers.ITEM_STACK
+- );
+
++ private static final EntityDataAccessor<ItemStack> DATA_ITEM_STACK = SynchedEntityData.defineId(ThrowableItemProjectile.class, EntityDataSerializers.ITEM_STACK);
++
+ public ThrowableItemProjectile(EntityType<? extends ThrowableItemProjectile> entityType, Level level) {
+ super(entityType, level);
+ }
+
+- public ThrowableItemProjectile(EntityType<? extends ThrowableItemProjectile> entityType, double x, double y, double z, Level level) {
+- super(entityType, x, y, z, level);
++ public ThrowableItemProjectile(EntityType<? extends ThrowableItemProjectile> entityType, double x, double d1, double y, Level world) {
++ super(entityType, x, d1, y, world);
+ }
+
+ public ThrowableItemProjectile(EntityType<? extends ThrowableItemProjectile> entityType, LivingEntity shooter, Level level) {
+@@ -29,40 +28,51 @@
+
+ public void setItem(ItemStack stack) {
+ if (!stack.is(this.getDefaultItem()) || stack.hasTag()) {
+- this.getEntityData().set(DATA_ITEM_STACK, stack.copyWithCount(1));
++ this.getEntityData().set(ThrowableItemProjectile.DATA_ITEM_STACK, stack.copyWithCount(1));
+ }
++
+ }
+
+ protected abstract Item getDefaultItem();
+
+- protected ItemStack getItemRaw() {
+- return this.getEntityData().get(DATA_ITEM_STACK);
++ // CraftBukkit start
++ public Item getDefaultItemPublic() {
++ return getDefaultItem();
+ }
++ // CraftBukkit end
+
++ public ItemStack getItemRaw() {
++ return (ItemStack) this.getEntityData().get(ThrowableItemProjectile.DATA_ITEM_STACK);
++ }
++
+ @Override
+ public ItemStack getItem() {
+- ItemStack itemRaw = this.getItemRaw();
+- return itemRaw.isEmpty() ? new ItemStack(this.getDefaultItem()) : itemRaw;
++ ItemStack itemstack = this.getItemRaw();
++
++ return itemstack.isEmpty() ? new ItemStack(this.getDefaultItem()) : itemstack;
+ }
+
+ @Override
+ protected void defineSynchedData() {
+- this.getEntityData().define(DATA_ITEM_STACK, ItemStack.EMPTY);
++ this.getEntityData().define(ThrowableItemProjectile.DATA_ITEM_STACK, ItemStack.EMPTY);
+ }
+
+ @Override
+ public void addAdditionalSaveData(CompoundTag compound) {
+ super.addAdditionalSaveData(compound);
+- ItemStack itemRaw = this.getItemRaw();
+- if (!itemRaw.isEmpty()) {
+- compound.put("Item", itemRaw.save(new CompoundTag()));
++ ItemStack itemstack = this.getItemRaw();
++
++ if (!itemstack.isEmpty()) {
++ compound.put("Item", itemstack.save(new CompoundTag()));
+ }
++
+ }
+
+ @Override
+ public void readAdditionalSaveData(CompoundTag compound) {
+ super.readAdditionalSaveData(compound);
+- ItemStack itemStack = ItemStack.of(compound.getCompound("Item"));
+- this.setItem(itemStack);
++ ItemStack itemstack = ItemStack.of(compound.getCompound("Item"));
++
++ this.setItem(itemstack);
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrowableProjectile.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrowableProjectile.java.patch
new file mode 100644
index 0000000000..68d2b040d1
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrowableProjectile.java.patch
@@ -0,0 +1,144 @@
+--- a/net/minecraft/world/entity/projectile/ThrowableProjectile.java
++++ b/net/minecraft/world/entity/projectile/ThrowableProjectile.java
+@@ -8,96 +8,96 @@
+ import net.minecraft.world.level.block.Blocks;
+ import net.minecraft.world.level.block.entity.BlockEntity;
+ import net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import net.minecraft.world.phys.BlockHitResult;
+ import net.minecraft.world.phys.HitResult;
+ import net.minecraft.world.phys.Vec3;
+
+ public abstract class ThrowableProjectile extends Projectile {
++
+ protected ThrowableProjectile(EntityType<? extends ThrowableProjectile> entityType, Level level) {
+ super(entityType, level);
+ }
+
+- protected ThrowableProjectile(EntityType<? extends ThrowableProjectile> entityType, double x, double y, double z, Level level) {
+- this(entityType, level);
+- this.setPos(x, y, z);
++ protected ThrowableProjectile(EntityType<? extends ThrowableProjectile> entityType, double x, double d1, double y, Level world) {
++ this(entityType, world);
++ this.setPos(x, d1, y);
+ }
+
+ protected ThrowableProjectile(EntityType<? extends ThrowableProjectile> entityType, LivingEntity shooter, Level level) {
+- this(entityType, shooter.getX(), shooter.getEyeY() - 0.1F, shooter.getZ(), level);
++ this(entityType, shooter.getX(), shooter.getEyeY() - 0.10000000149011612D, shooter.getZ(), level);
+ this.setOwner(shooter);
+ }
+
+ @Override
+ public boolean shouldRenderAtSqrDistance(double distance) {
+- double d = this.getBoundingBox().getSize() * 4.0;
+- if (Double.isNaN(d)) {
+- d = 4.0;
++ double d1 = this.getBoundingBox().getSize() * 4.0D;
++
++ if (Double.isNaN(d1)) {
++ d1 = 4.0D;
+ }
+
+- d *= 64.0;
+- return distance < d * d;
++ d1 *= 64.0D;
++ return distance < d1 * d1;
+ }
+
+ @Override
+ public void tick() {
+ super.tick();
+- HitResult hitResultOnMoveVector = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
++ HitResult movingobjectposition = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
+ boolean flag = false;
+- if (hitResultOnMoveVector.getType() == HitResult.Type.BLOCK) {
+- BlockPos blockPos = ((BlockHitResult)hitResultOnMoveVector).getBlockPos();
+- BlockState blockState = this.level().getBlockState(blockPos);
+- if (blockState.is(Blocks.NETHER_PORTAL)) {
+- this.handleInsidePortal(blockPos);
++
++ if (movingobjectposition.getType() == HitResult.EnumMovingObjectType.BLOCK) {
++ BlockPos blockposition = ((BlockHitResult) movingobjectposition).getBlockPos();
++ IBlockData iblockdata = this.level().getBlockState(blockposition);
++
++ if (iblockdata.is(Blocks.NETHER_PORTAL)) {
++ this.handleInsidePortal(blockposition);
+ flag = true;
+- } else if (blockState.is(Blocks.END_GATEWAY)) {
+- BlockEntity blockEntity = this.level().getBlockEntity(blockPos);
+- if (blockEntity instanceof TheEndGatewayBlockEntity && TheEndGatewayBlockEntity.canEntityTeleport(this)) {
+- TheEndGatewayBlockEntity.teleportEntity(this.level(), blockPos, blockState, this, (TheEndGatewayBlockEntity)blockEntity);
++ } else if (iblockdata.is(Blocks.END_GATEWAY)) {
++ BlockEntity tileentity = this.level().getBlockEntity(blockposition);
++
++ if (tileentity instanceof TheEndGatewayBlockEntity && TheEndGatewayBlockEntity.canEntityTeleport(this)) {
++ TheEndGatewayBlockEntity.teleportEntity(this.level(), blockposition, iblockdata, this, (TheEndGatewayBlockEntity) tileentity);
+ }
+
+ flag = true;
+ }
+ }
+
+- if (hitResultOnMoveVector.getType() != HitResult.Type.MISS && !flag) {
+- this.onHit(hitResultOnMoveVector);
++ if (movingobjectposition.getType() != HitResult.EnumMovingObjectType.MISS && !flag) {
++ this.preOnHit(movingobjectposition); // CraftBukkit - projectile hit event
+ }
+
+ this.checkInsideBlocks();
+- Vec3 deltaMovement = this.getDeltaMovement();
+- double d = this.getX() + deltaMovement.x;
+- double d1 = this.getY() + deltaMovement.y;
+- double d2 = this.getZ() + deltaMovement.z;
++ Vec3 vec3d = this.getDeltaMovement();
++ double d0 = this.getX() + vec3d.x;
++ double d1 = this.getY() + vec3d.y;
++ double d2 = this.getZ() + vec3d.z;
++
+ this.updateRotation();
+- float f1;
++ float f;
++
+ if (this.isInWater()) {
+- for (int i = 0; i < 4; i++) {
+- float f = 0.25F;
+- this.level()
+- .addParticle(
+- ParticleTypes.BUBBLE,
+- d - deltaMovement.x * 0.25,
+- d1 - deltaMovement.y * 0.25,
+- d2 - deltaMovement.z * 0.25,
+- deltaMovement.x,
+- deltaMovement.y,
+- deltaMovement.z
+- );
++ for (int i = 0; i < 4; ++i) {
++ float f1 = 0.25F;
++
++ this.level().addParticle(ParticleTypes.BUBBLE, d0 - vec3d.x * 0.25D, d1 - vec3d.y * 0.25D, d2 - vec3d.z * 0.25D, vec3d.x, vec3d.y, vec3d.z);
+ }
+
+- f1 = 0.8F;
++ f = 0.8F;
+ } else {
+- f1 = 0.99F;
++ f = 0.99F;
+ }
+
+- this.setDeltaMovement(deltaMovement.scale((double)f1));
++ this.setDeltaMovement(vec3d.scale((double) f));
+ if (!this.isNoGravity()) {
+- Vec3 deltaMovement1 = this.getDeltaMovement();
+- this.setDeltaMovement(deltaMovement1.x, deltaMovement1.y - (double)this.getGravity(), deltaMovement1.z);
++ Vec3 vec3d1 = this.getDeltaMovement();
++
++ this.setDeltaMovement(vec3d1.x, vec3d1.y - (double) this.getGravity(), vec3d1.z);
+ }
+
+- this.setPos(d, d1, d2);
++ this.setPos(d0, d1, d2);
+ }
+
+ protected float getGravity() {
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownEgg.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownEgg.java.patch
new file mode 100644
index 0000000000..cf9cf0021b
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownEgg.java.patch
@@ -0,0 +1,135 @@
+--- a/net/minecraft/world/entity/projectile/ThrownEgg.java
++++ b/net/minecraft/world/entity/projectile/ThrownEgg.java
+@@ -1,47 +1,45 @@
+ package net.minecraft.world.entity.projectile;
+
+-import net.minecraft.core.particles.ItemParticleOption;
+-import net.minecraft.core.particles.ParticleTypes;
+-import net.minecraft.world.entity.EntityType;
+-import net.minecraft.world.entity.LivingEntity;
+-import net.minecraft.world.entity.animal.Chicken;
+ import net.minecraft.world.item.Item;
+ import net.minecraft.world.item.Items;
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.phys.EntityHitResult;
+ import net.minecraft.world.phys.HitResult;
++import net.minecraft.core.particles.ItemParticleOption;
++import net.minecraft.core.particles.ParticleTypes;
++import net.minecraft.server.level.ServerPlayer;
++import net.minecraft.world.entity.Entity;
++import net.minecraft.world.entity.LivingEntity;
++import org.bukkit.entity.Ageable;
++import org.bukkit.entity.EntityType;
++import org.bukkit.entity.Player;
++import org.bukkit.event.player.PlayerEggThrowEvent;
++// CraftBukkit end
+
+ public class ThrownEgg extends ThrowableItemProjectile {
+- public ThrownEgg(EntityType<? extends ThrownEgg> entityType, Level level) {
++
++ public ThrownEgg(net.minecraft.world.entity.EntityType<? extends ThrownEgg> entityType, Level level) {
+ super(entityType, level);
+ }
+
+ public ThrownEgg(Level level, LivingEntity shooter) {
+- super(EntityType.EGG, shooter, level);
++ super(net.minecraft.world.entity.EntityType.EGG, shooter, level);
+ }
+
+- public ThrownEgg(Level level, double x, double y, double z) {
+- super(EntityType.EGG, x, y, z, level);
++ public ThrownEgg(Level level, double x, double d1, double y) {
++ super(net.minecraft.world.entity.EntityType.EGG, x, d1, y, level);
+ }
+
+ @Override
+ public void handleEntityEvent(byte id) {
+ if (id == 3) {
+- double d = 0.08;
++ double d0 = 0.08D;
+
+- for (int i = 0; i < 8; i++) {
+- this.level()
+- .addParticle(
+- new ItemParticleOption(ParticleTypes.ITEM, this.getItem()),
+- this.getX(),
+- this.getY(),
+- this.getZ(),
+- ((double)this.random.nextFloat() - 0.5) * 0.08,
+- ((double)this.random.nextFloat() - 0.5) * 0.08,
+- ((double)this.random.nextFloat() - 0.5) * 0.08
+- );
++ for (int i = 0; i < 8; ++i) {
++ this.level().addParticle(new ItemParticleOption(ParticleTypes.ITEM, this.getItem()), this.getX(), this.getY(), this.getZ(), ((double) this.random.nextFloat() - 0.5D) * 0.08D, ((double) this.random.nextFloat() - 0.5D) * 0.08D, ((double) this.random.nextFloat() - 0.5D) * 0.08D);
+ }
+ }
++
+ }
+
+ @Override
+@@ -54,25 +52,55 @@
+ protected void onHit(HitResult result) {
+ super.onHit(result);
+ if (!this.level().isClientSide) {
+- if (this.random.nextInt(8) == 0) {
+- int i = 1;
++ // CraftBukkit start
++ boolean hatching = this.random.nextInt(8) == 0;
++ if (true) {
++ // CraftBukkit end
++ byte b0 = 1;
++
+ if (this.random.nextInt(32) == 0) {
+- i = 4;
++ b0 = 4;
+ }
+
+- for (int i1 = 0; i1 < i; i1++) {
+- Chicken chicken = EntityType.CHICKEN.create(this.level());
+- if (chicken != null) {
+- chicken.setAge(-24000);
+- chicken.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F);
+- this.level().addFreshEntity(chicken);
++ // CraftBukkit start
++ EntityType hatchingType = EntityType.CHICKEN;
++
++ Entity shooter = this.getOwner();
++ if (!hatching) {
++ b0 = 0;
++ }
++ if (shooter instanceof ServerPlayer) {
++ PlayerEggThrowEvent event = new PlayerEggThrowEvent((Player) shooter.getBukkitEntity(), (org.bukkit.entity.Egg) this.getBukkitEntity(), hatching, b0, hatchingType);
++ this.level().getCraftServer().getPluginManager().callEvent(event);
++
++ b0 = event.getNumHatches();
++ hatching = event.isHatching();
++ hatchingType = event.getHatchingType();
++ // If hatching is set to false, ensure child count is 0
++ if (!hatching) {
++ b0 = 0;
+ }
+ }
++ // CraftBukkit end
++
++ for (int i = 0; i < b0; ++i) {
++ Entity entitychicken = this.level().getWorld().makeEntity(new org.bukkit.Location(this.level().getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F), hatchingType.getEntityClass()); // CraftBukkit
++
++ if (entitychicken != null) {
++ // CraftBukkit start
++ if (entitychicken.getBukkitEntity() instanceof Ageable) {
++ ((Ageable) entitychicken.getBukkitEntity()).setBaby();
++ }
++ this.level().addFreshEntity(entitychicken, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EGG);
++ // CraftBukkit end
++ }
++ }
+ }
+
+- this.level().broadcastEntityEvent(this, (byte)3);
++ this.level().broadcastEntityEvent(this, (byte) 3);
+ this.discard();
+ }
++
+ }
+
+ @Override
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch
new file mode 100644
index 0000000000..0bf0250ceb
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownEnderpearl.java.patch
@@ -0,0 +1,140 @@
+--- a/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
++++ b/net/minecraft/world/entity/projectile/ThrownEnderpearl.java
+@@ -10,14 +10,22 @@
+ import net.minecraft.world.entity.EntityType;
+ import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.monster.Endermite;
++import net.minecraft.world.entity.player.Player;
+ import net.minecraft.world.item.Item;
+ import net.minecraft.world.item.Items;
+ import net.minecraft.world.level.GameRules;
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.phys.EntityHitResult;
+ import net.minecraft.world.phys.HitResult;
++// CraftBukkit start
++import org.bukkit.Bukkit;
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++import org.bukkit.event.entity.CreatureSpawnEvent;
++import org.bukkit.event.player.PlayerTeleportEvent;
++// CraftBukkit end
+
+ public class ThrownEnderpearl extends ThrowableItemProjectile {
++
+ public ThrownEnderpearl(EntityType<? extends ThrownEnderpearl> entityType, Level level) {
+ super(entityType, level);
+ }
+@@ -41,66 +49,78 @@
+ protected void onHit(HitResult result) {
+ super.onHit(result);
+
+- for (int i = 0; i < 32; i++) {
+- this.level()
+- .addParticle(
+- ParticleTypes.PORTAL,
+- this.getX(),
+- this.getY() + this.random.nextDouble() * 2.0,
+- this.getZ(),
+- this.random.nextGaussian(),
+- 0.0,
+- this.random.nextGaussian()
+- );
++ for (int i = 0; i < 32; ++i) {
++ this.level().addParticle(ParticleTypes.PORTAL, this.getX(), this.getY() + this.random.nextDouble() * 2.0D, this.getZ(), this.random.nextGaussian(), 0.0D, this.random.nextGaussian());
+ }
+
+ if (!this.level().isClientSide && !this.isRemoved()) {
+- Entity owner = this.getOwner();
+- if (owner instanceof ServerPlayer serverPlayer) {
+- if (serverPlayer.connection.isAcceptingMessages() && serverPlayer.level() == this.level() && !serverPlayer.isSleeping()) {
+- if (this.random.nextFloat() < 0.05F && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
+- Endermite endermite = EntityType.ENDERMITE.create(this.level());
+- if (endermite != null) {
+- endermite.moveTo(owner.getX(), owner.getY(), owner.getZ(), owner.getYRot(), owner.getXRot());
+- this.level().addFreshEntity(endermite);
++ Entity entity = this.getOwner();
++
++ if (entity instanceof ServerPlayer) {
++ ServerPlayer entityplayer = (ServerPlayer) entity;
++
++ if (entityplayer.connection.isAcceptingMessages() && entityplayer.level() == this.level() && !entityplayer.isSleeping()) {
++ // CraftBukkit start - Fire PlayerTeleportEvent
++ org.bukkit.craftbukkit.entity.CraftPlayer player = entityplayer.getBukkitEntity();
++ org.bukkit.Location location = getBukkitEntity().getLocation();
++ location.setPitch(player.getLocation().getPitch());
++ location.setYaw(player.getLocation().getYaw());
++
++ PlayerTeleportEvent teleEvent = new PlayerTeleportEvent(player, player.getLocation(), location, PlayerTeleportEvent.TeleportCause.ENDER_PEARL);
++ Bukkit.getPluginManager().callEvent(teleEvent);
++
++ if (!teleEvent.isCancelled() && entityplayer.connection.isAcceptingMessages()) {
++ if (this.random.nextFloat() < 0.05F && this.level().getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING)) {
++ Endermite entityendermite = (Endermite) EntityType.ENDERMITE.create(this.level());
++
++ if (entityendermite != null) {
++ entityendermite.moveTo(entity.getX(), entity.getY(), entity.getZ(), entity.getYRot(), entity.getXRot());
++ this.level().addFreshEntity(entityendermite, CreatureSpawnEvent.SpawnReason.ENDER_PEARL);
++ }
+ }
+- }
+
+- if (owner.isPassenger()) {
+- serverPlayer.dismountTo(this.getX(), this.getY(), this.getZ());
+- } else {
+- owner.teleportTo(this.getX(), this.getY(), this.getZ());
+- }
++ if (entity.isPassenger()) {
++ entity.stopRiding();
++ }
+
+- owner.resetFallDistance();
+- owner.hurt(this.damageSources().fall(), 5.0F);
+- this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_TELEPORT, SoundSource.PLAYERS);
++ entityplayer.connection.teleport(teleEvent.getTo());
++ entity.resetFallDistance();
++ CraftEventFactory.entityDamage = this;
++ entity.hurt(this.damageSources().fall(), 5.0F);
++ CraftEventFactory.entityDamage = null;
++ }
++ // CraftBukkit end
++ this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_TELEPORT, SoundSource.PLAYERS);
+ }
+- } else if (owner != null) {
+- owner.teleportTo(this.getX(), this.getY(), this.getZ());
+- owner.resetFallDistance();
++ } else if (entity != null) {
++ entity.teleportTo(this.getX(), this.getY(), this.getZ());
++ entity.resetFallDistance();
+ }
+
+ this.discard();
+ }
++
+ }
+
+ @Override
+ public void tick() {
+- Entity owner = this.getOwner();
+- if (owner instanceof ServerPlayer && !owner.isAlive() && this.level().getGameRules().getBoolean(GameRules.RULE_ENDER_PEARLS_VANISH_ON_DEATH)) {
++ Entity entity = this.getOwner();
++
++ if (entity instanceof ServerPlayer && !entity.isAlive() && this.level().getGameRules().getBoolean(GameRules.RULE_ENDER_PEARLS_VANISH_ON_DEATH)) {
+ this.discard();
+ } else {
+ super.tick();
+ }
++
+ }
+
+ @Nullable
+ @Override
+ public Entity changeDimension(ServerLevel server) {
+- Entity owner = this.getOwner();
+- if (owner != null && owner.level().dimension() != server.dimension()) {
+- this.setOwner(null);
++ Entity entity = this.getOwner();
++
++ if (entity != null && server != null && entity.level().dimension() != server.dimension()) { // CraftBukkit - SPIGOT-6113
++ this.setOwner((Entity) null);
+ }
+
+ return super.changeDimension(server);
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownExperienceBottle.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownExperienceBottle.java.patch
new file mode 100644
index 0000000000..df7a925c0b
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownExperienceBottle.java.patch
@@ -0,0 +1,45 @@
+--- a/net/minecraft/world/entity/projectile/ThrownExperienceBottle.java
++++ b/net/minecraft/world/entity/projectile/ThrownExperienceBottle.java
+@@ -12,6 +12,7 @@
+ import net.minecraft.world.phys.HitResult;
+
+ public class ThrownExperienceBottle extends ThrowableItemProjectile {
++
+ public ThrownExperienceBottle(EntityType<? extends ThrownExperienceBottle> entityType, Level level) {
+ super(entityType, level);
+ }
+@@ -20,8 +21,8 @@
+ super(EntityType.EXPERIENCE_BOTTLE, shooter, level);
+ }
+
+- public ThrownExperienceBottle(Level level, double x, double y, double z) {
+- super(EntityType.EXPERIENCE_BOTTLE, x, y, z, level);
++ public ThrownExperienceBottle(Level level, double x, double d1, double y) {
++ super(EntityType.EXPERIENCE_BOTTLE, x, d1, y, level);
+ }
+
+ @Override
+@@ -38,10 +39,21 @@
+ protected void onHit(HitResult result) {
+ super.onHit(result);
+ if (this.level() instanceof ServerLevel) {
+- this.level().levelEvent(2002, this.blockPosition(), PotionUtils.getColor(Potions.WATER));
++ // CraftBukkit - moved to after event
++ // this.level().levelEvent(2002, this.blockPosition(), PotionUtil.getColor(Potions.WATER));
+ int i = 3 + this.level().random.nextInt(5) + this.level().random.nextInt(5);
+- ExperienceOrb.award((ServerLevel)this.level(), this.position(), i);
++
++ // CraftBukkit start
++ org.bukkit.event.entity.ExpBottleEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExpBottleEvent(this, result, i);
++ i = event.getExperience();
++ if (event.getShowEffect()) {
++ this.level().levelEvent(2002, this.blockPosition(), PotionUtils.getColor(Potions.WATER));
++ }
++ // CraftBukkit end
++
++ ExperienceOrb.award((ServerLevel) this.level(), this.position(), i);
+ this.discard();
+ }
++
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownPotion.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownPotion.java.patch
new file mode 100644
index 0000000000..b61b8d9855
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownPotion.java.patch
@@ -0,0 +1,381 @@
+--- a/net/minecraft/world/entity/projectile/ThrownPotion.java
++++ b/net/minecraft/world/entity/projectile/ThrownPotion.java
+@@ -1,19 +1,21 @@
+ package net.minecraft.world.entity.projectile;
+
++import java.util.Iterator;
+ import java.util.List;
+ import java.util.function.Predicate;
+ import javax.annotation.Nullable;
+ import net.minecraft.core.BlockPos;
+ import net.minecraft.core.Direction;
+ import net.minecraft.nbt.CompoundTag;
++import net.minecraft.server.level.ServerPlayer;
+ import net.minecraft.tags.BlockTags;
+ import net.minecraft.world.effect.MobEffect;
+ import net.minecraft.world.effect.MobEffectInstance;
+ import net.minecraft.world.entity.AreaEffectCloud;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityType;
+-import net.minecraft.world.entity.LivingEntity;
+ import net.minecraft.world.entity.animal.axolotl.Axolotl;
++import net.minecraft.world.entity.player.Player;
+ import net.minecraft.world.item.Item;
+ import net.minecraft.world.item.ItemStack;
+ import net.minecraft.world.item.Items;
+@@ -22,28 +24,40 @@
+ import net.minecraft.world.item.alchemy.Potions;
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.level.block.AbstractCandleBlock;
+-import net.minecraft.world.level.block.CampfireBlock;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import net.minecraft.world.phys.AABB;
+ import net.minecraft.world.phys.BlockHitResult;
+ import net.minecraft.world.phys.EntityHitResult;
+ import net.minecraft.world.phys.HitResult;
++// CraftBukkit start
++import java.util.HashMap;
++import java.util.Map;
++import net.minecraft.world.effect.MobEffects;
++import net.minecraft.world.level.block.Blocks;
++import net.minecraft.world.level.block.CampfireBlock;
++import org.bukkit.craftbukkit.entity.CraftLivingEntity;
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++import org.bukkit.entity.LivingEntity;
++// CraftBukkit end
+
+ public class ThrownPotion extends ThrowableItemProjectile implements ItemSupplier {
+- public static final double SPLASH_RANGE = 4.0;
+- private static final double SPLASH_RANGE_SQ = 16.0;
+- public static final Predicate<LivingEntity> WATER_SENSITIVE_OR_ON_FIRE = livingEntity -> livingEntity.isSensitiveToWater() || livingEntity.isOnFire();
+
++ public static final double SPLASH_RANGE = 4.0D;
++ private static final double SPLASH_RANGE_SQ = 16.0D;
++ public static final Predicate<net.minecraft.world.entity.LivingEntity> WATER_SENSITIVE_OR_ON_FIRE = (entityliving) -> {
++ return entityliving.isSensitiveToWater() || entityliving.isOnFire();
++ };
++
+ public ThrownPotion(EntityType<? extends ThrownPotion> entityType, Level level) {
+ super(entityType, level);
+ }
+
+- public ThrownPotion(Level level, LivingEntity shooter) {
++ public ThrownPotion(Level level, net.minecraft.world.entity.LivingEntity shooter) {
+ super(EntityType.POTION, shooter, level);
+ }
+
+- public ThrownPotion(Level level, double x, double y, double z) {
+- super(EntityType.POTION, x, y, z, level);
++ public ThrownPotion(Level level, double x, double d1, double y) {
++ super(EntityType.POTION, x, d1, y, level);
+ }
+
+ @Override
+@@ -60,21 +74,26 @@
+ protected void onHitBlock(BlockHitResult result) {
+ super.onHitBlock(result);
+ if (!this.level().isClientSide) {
+- ItemStack item = this.getItem();
+- Potion potion = PotionUtils.getPotion(item);
+- List<MobEffectInstance> mobEffects = PotionUtils.getMobEffects(item);
+- boolean flag = potion == Potions.WATER && mobEffects.isEmpty();
+- Direction direction = result.getDirection();
+- BlockPos blockPos = result.getBlockPos();
+- BlockPos blockPos1 = blockPos.relative(direction);
++ ItemStack itemstack = this.getItem();
++ Potion potionregistry = PotionUtils.getPotion(itemstack);
++ List<MobEffectInstance> list = PotionUtils.getMobEffects(itemstack);
++ boolean flag = potionregistry == Potions.WATER && list.isEmpty();
++ Direction enumdirection = result.getDirection();
++ BlockPos blockposition = result.getBlockPos();
++ BlockPos blockposition1 = blockposition.relative(enumdirection);
++
+ if (flag) {
+- this.dowseFire(blockPos1);
+- this.dowseFire(blockPos1.relative(direction.getOpposite()));
++ this.dowseFire(blockposition1);
++ this.dowseFire(blockposition1.relative(enumdirection.getOpposite()));
++ Iterator iterator = Direction.Plane.HORIZONTAL.iterator();
+
+- for (Direction direction1 : Direction.Plane.HORIZONTAL) {
+- this.dowseFire(blockPos1.relative(direction1));
++ while (iterator.hasNext()) {
++ Direction enumdirection1 = (Direction) iterator.next();
++
++ this.dowseFire(blockposition1.relative(enumdirection1));
+ }
+ }
++
+ }
+ }
+
+@@ -82,123 +101,199 @@
+ protected void onHit(HitResult result) {
+ super.onHit(result);
+ if (!this.level().isClientSide) {
+- ItemStack item = this.getItem();
+- Potion potion = PotionUtils.getPotion(item);
+- List<MobEffectInstance> mobEffects = PotionUtils.getMobEffects(item);
+- boolean flag = potion == Potions.WATER && mobEffects.isEmpty();
++ ItemStack itemstack = this.getItem();
++ Potion potionregistry = PotionUtils.getPotion(itemstack);
++ List<MobEffectInstance> list = PotionUtils.getMobEffects(itemstack);
++ boolean flag = potionregistry == Potions.WATER && list.isEmpty();
++
+ if (flag) {
+ this.applyWater();
+- } else if (!mobEffects.isEmpty()) {
++ } else if (true || !list.isEmpty()) { // CraftBukkit - Call event even if no effects to apply
+ if (this.isLingering()) {
+- this.makeAreaOfEffectCloud(item, potion);
++ this.makeAreaOfEffectCloud(itemstack, potionregistry, result); // CraftBukkit - Pass MovingObjectPosition
+ } else {
+- this.applySplash(mobEffects, result.getType() == HitResult.Type.ENTITY ? ((EntityHitResult)result).getEntity() : null);
++ this.applySplash(list, result.getType() == HitResult.EnumMovingObjectType.ENTITY ? ((EntityHitResult) result).getEntity() : null, result); // CraftBukkit - Pass MovingObjectPosition
+ }
+ }
+
+- int i = potion.hasInstantEffects() ? 2007 : 2002;
+- this.level().levelEvent(i, this.blockPosition(), PotionUtils.getColor(item));
++ int i = potionregistry.hasInstantEffects() ? 2007 : 2002;
++
++ this.level().levelEvent(i, this.blockPosition(), PotionUtils.getColor(itemstack));
+ this.discard();
+ }
+ }
+
+ private void applyWater() {
+- AABB aABB = this.getBoundingBox().inflate(4.0, 2.0, 4.0);
++ AABB axisalignedbb = this.getBoundingBox().inflate(4.0D, 2.0D, 4.0D);
++ List<net.minecraft.world.entity.LivingEntity> list = this.level().getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class, axisalignedbb, ThrownPotion.WATER_SENSITIVE_OR_ON_FIRE);
++ Iterator iterator = list.iterator();
+
+- for (LivingEntity livingEntity : this.level().getEntitiesOfClass(LivingEntity.class, aABB, WATER_SENSITIVE_OR_ON_FIRE)) {
+- double d = this.distanceToSqr(livingEntity);
+- if (d < 16.0) {
+- if (livingEntity.isSensitiveToWater()) {
+- livingEntity.hurt(this.damageSources().indirectMagic(this, this.getOwner()), 1.0F);
++ while (iterator.hasNext()) {
++ net.minecraft.world.entity.LivingEntity entityliving = (net.minecraft.world.entity.LivingEntity) iterator.next();
++ double d0 = this.distanceToSqr((Entity) entityliving);
++
++ if (d0 < 16.0D) {
++ if (entityliving.isSensitiveToWater()) {
++ entityliving.hurt(this.damageSources().indirectMagic(this, this.getOwner()), 1.0F);
+ }
+
+- if (livingEntity.isOnFire() && livingEntity.isAlive()) {
+- livingEntity.extinguishFire();
++ if (entityliving.isOnFire() && entityliving.isAlive()) {
++ entityliving.extinguishFire();
+ }
+ }
+ }
+
+- for (Axolotl axolotl : this.level().getEntitiesOfClass(Axolotl.class, aABB)) {
++ List<Axolotl> list1 = this.level().getEntitiesOfClass(Axolotl.class, axisalignedbb);
++ Iterator iterator1 = list1.iterator();
++
++ while (iterator1.hasNext()) {
++ Axolotl axolotl = (Axolotl) iterator1.next();
++
+ axolotl.rehydrate();
+ }
++
+ }
+
+- private void applySplash(List<MobEffectInstance> effectInstances, @Nullable Entity target) {
+- AABB aABB = this.getBoundingBox().inflate(4.0, 2.0, 4.0);
+- List<LivingEntity> entitiesOfClass = this.level().getEntitiesOfClass(LivingEntity.class, aABB);
+- if (!entitiesOfClass.isEmpty()) {
+- Entity effectSource = this.getEffectSource();
++ private void applySplash(List<MobEffectInstance> list, @Nullable Entity entity, HitResult position) { // CraftBukkit - Pass MovingObjectPosition
++ AABB axisalignedbb = this.getBoundingBox().inflate(4.0D, 2.0D, 4.0D);
++ List<net.minecraft.world.entity.LivingEntity> list1 = this.level().getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class, axisalignedbb);
++ Map<LivingEntity, Double> affected = new HashMap<LivingEntity, Double>(); // CraftBukkit
+
+- for (LivingEntity livingEntity : entitiesOfClass) {
+- if (livingEntity.isAffectedByPotions()) {
+- double d = this.distanceToSqr(livingEntity);
+- if (d < 16.0) {
++ if (!list1.isEmpty()) {
++ Entity entity1 = this.getEffectSource();
++ Iterator iterator = list1.iterator();
++
++ while (iterator.hasNext()) {
++ net.minecraft.world.entity.LivingEntity entityliving = (net.minecraft.world.entity.LivingEntity) iterator.next();
++
++ if (entityliving.isAffectedByPotions()) {
++ double d0 = this.distanceToSqr((Entity) entityliving);
++
++ if (d0 < 16.0D) {
+ double d1;
+- if (livingEntity == target) {
+- d1 = 1.0;
++
++ if (entityliving == entity) {
++ d1 = 1.0D;
+ } else {
+- d1 = 1.0 - Math.sqrt(d) / 4.0;
++ d1 = 1.0D - Math.sqrt(d0) / 4.0D;
+ }
+
+- for (MobEffectInstance mobEffectInstance : effectInstances) {
+- MobEffect effect = mobEffectInstance.getEffect();
+- if (effect.isInstantenous()) {
+- effect.applyInstantenousEffect(this, this.getOwner(), livingEntity, mobEffectInstance.getAmplifier(), d1);
+- } else {
+- int i = mobEffectInstance.mapDuration(i1 -> (int)(d1 * (double)i1 + 0.5));
+- MobEffectInstance mobEffectInstance1 = new MobEffectInstance(
+- effect, i, mobEffectInstance.getAmplifier(), mobEffectInstance.isAmbient(), mobEffectInstance.isVisible()
+- );
+- if (!mobEffectInstance1.endsWithin(20)) {
+- livingEntity.addEffect(mobEffectInstance1, effectSource);
+- }
+- }
++ // CraftBukkit start
++ affected.put((LivingEntity) entityliving.getBukkitEntity(), d1);
++ }
++ }
++ }
++ }
++
++ org.bukkit.event.entity.PotionSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPotionSplashEvent(this, position, affected);
++ if (!event.isCancelled() && list != null && !list.isEmpty()) { // do not process effects if there are no effects to process
++ Entity entity1 = this.getEffectSource();
++ for (LivingEntity victim : event.getAffectedEntities()) {
++ if (!(victim instanceof CraftLivingEntity)) {
++ continue;
++ }
++
++ net.minecraft.world.entity.LivingEntity entityliving = ((CraftLivingEntity) victim).getHandle();
++ double d1 = event.getIntensity(victim);
++ // CraftBukkit end
++
++ Iterator iterator1 = list.iterator();
++
++ while (iterator1.hasNext()) {
++ MobEffectInstance mobeffect = (MobEffectInstance) iterator1.next();
++ MobEffect mobeffectlist = mobeffect.getEffect();
++ // CraftBukkit start - Abide by PVP settings - for players only!
++ if (!this.level().pvpMode && this.getOwner() instanceof ServerPlayer && entityliving instanceof ServerPlayer && entityliving != this.getOwner()) {
++ if (mobeffectlist == MobEffects.MOVEMENT_SLOWDOWN || mobeffectlist == MobEffects.DIG_SLOWDOWN || mobeffectlist == MobEffects.HARM || mobeffectlist == MobEffects.BLINDNESS
++ || mobeffectlist == MobEffects.HUNGER || mobeffectlist == MobEffects.WEAKNESS || mobeffectlist == MobEffects.POISON) {
++ continue;
+ }
+ }
++ // CraftBukkit end
++
++ if (mobeffectlist.isInstantenous()) {
++ mobeffectlist.applyInstantenousEffect(this, this.getOwner(), entityliving, mobeffect.getAmplifier(), d1);
++ } else {
++ int i = mobeffect.mapDuration((j) -> {
++ return (int) (d1 * (double) j + 0.5D);
++ });
++ MobEffectInstance mobeffect1 = new MobEffectInstance(mobeffectlist, i, mobeffect.getAmplifier(), mobeffect.isAmbient(), mobeffect.isVisible());
++
++ if (!mobeffect1.endsWithin(20)) {
++ entityliving.addEffect(mobeffect1, entity1, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.POTION_SPLASH); // CraftBukkit
++ }
++ }
+ }
+ }
+ }
++
+ }
+
+- private void makeAreaOfEffectCloud(ItemStack stack, Potion potion) {
+- AreaEffectCloud areaEffectCloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ());
+- Entity owner = this.getOwner();
+- if (owner instanceof LivingEntity) {
+- areaEffectCloud.setOwner((LivingEntity)owner);
++ private void makeAreaOfEffectCloud(ItemStack itemstack, Potion potionregistry, HitResult position) { // CraftBukkit - Pass MovingObjectPosition
++ AreaEffectCloud entityareaeffectcloud = new AreaEffectCloud(this.level(), this.getX(), this.getY(), this.getZ());
++ Entity entity = this.getOwner();
++
++ if (entity instanceof net.minecraft.world.entity.LivingEntity) {
++ entityareaeffectcloud.setOwner((net.minecraft.world.entity.LivingEntity) entity);
+ }
+
+- areaEffectCloud.setRadius(3.0F);
+- areaEffectCloud.setRadiusOnUse(-0.5F);
+- areaEffectCloud.setWaitTime(10);
+- areaEffectCloud.setRadiusPerTick(-areaEffectCloud.getRadius() / (float)areaEffectCloud.getDuration());
+- areaEffectCloud.setPotion(potion);
++ entityareaeffectcloud.setRadius(3.0F);
++ entityareaeffectcloud.setRadiusOnUse(-0.5F);
++ entityareaeffectcloud.setWaitTime(10);
++ entityareaeffectcloud.setRadiusPerTick(-entityareaeffectcloud.getRadius() / (float) entityareaeffectcloud.getDuration());
++ entityareaeffectcloud.setPotion(potionregistry);
++ Iterator iterator = PotionUtils.getCustomEffects(itemstack).iterator();
+
+- for (MobEffectInstance mobEffectInstance : PotionUtils.getCustomEffects(stack)) {
+- areaEffectCloud.addEffect(new MobEffectInstance(mobEffectInstance));
++ while (iterator.hasNext()) {
++ MobEffectInstance mobeffect = (MobEffectInstance) iterator.next();
++
++ entityareaeffectcloud.addEffect(new MobEffectInstance(mobeffect));
+ }
+
+- CompoundTag tag = stack.getTag();
+- if (tag != null && tag.contains("CustomPotionColor", 99)) {
+- areaEffectCloud.setFixedColor(tag.getInt("CustomPotionColor"));
++ CompoundTag nbttagcompound = itemstack.getTag();
++
++ if (nbttagcompound != null && nbttagcompound.contains("CustomPotionColor", 99)) {
++ entityareaeffectcloud.setFixedColor(nbttagcompound.getInt("CustomPotionColor"));
+ }
+
+- this.level().addFreshEntity(areaEffectCloud);
++ // CraftBukkit start
++ org.bukkit.event.entity.LingeringPotionSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callLingeringPotionSplashEvent(this, position, entityareaeffectcloud);
++ if (!(event.isCancelled() || entityareaeffectcloud.isRemoved())) {
++ this.level().addFreshEntity(entityareaeffectcloud);
++ } else {
++ entityareaeffectcloud.discard();
++ }
++ // CraftBukkit end
+ }
+
+- private boolean isLingering() {
++ public boolean isLingering() {
+ return this.getItem().is(Items.LINGERING_POTION);
+ }
+
+ private void dowseFire(BlockPos pos) {
+- BlockState blockState = this.level().getBlockState(pos);
+- if (blockState.is(BlockTags.FIRE)) {
+- this.level().destroyBlock(pos, false, this);
+- } else if (AbstractCandleBlock.isLit(blockState)) {
+- AbstractCandleBlock.extinguish(null, blockState, this.level(), pos);
+- } else if (CampfireBlock.isLitCampfire(blockState)) {
+- this.level().levelEvent(null, 1009, pos, 0);
+- CampfireBlock.dowse(this.getOwner(), this.level(), pos, blockState);
+- this.level().setBlockAndUpdate(pos, blockState.setValue(CampfireBlock.LIT, Boolean.valueOf(false)));
++ IBlockData iblockdata = this.level().getBlockState(pos);
++
++ if (iblockdata.is(BlockTags.FIRE)) {
++ // CraftBukkit start
++ if (CraftEventFactory.callEntityChangeBlockEvent(this, pos, Blocks.AIR.defaultBlockState())) {
++ this.level().destroyBlock(pos, false, this);
++ }
++ // CraftBukkit end
++ } else if (AbstractCandleBlock.isLit(iblockdata)) {
++ // CraftBukkit start
++ if (CraftEventFactory.callEntityChangeBlockEvent(this, pos, iblockdata.setValue(AbstractCandleBlock.LIT, false))) {
++ AbstractCandleBlock.extinguish((Player) null, iblockdata, this.level(), pos);
++ }
++ // CraftBukkit end
++ } else if (CampfireBlock.isLitCampfire(iblockdata)) {
++ // CraftBukkit start
++ if (CraftEventFactory.callEntityChangeBlockEvent(this, pos, iblockdata.setValue(CampfireBlock.LIT, false))) {
++ this.level().levelEvent((Player) null, 1009, pos, 0);
++ CampfireBlock.dowse(this.getOwner(), this.level(), pos, iblockdata);
++ this.level().setBlockAndUpdate(pos, (IBlockData) iblockdata.setValue(CampfireBlock.LIT, false));
++ }
++ // CraftBukkit end
+ }
++
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownTrident.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownTrident.java.patch
new file mode 100644
index 0000000000..6f6538c6eb
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/ThrownTrident.java.patch
@@ -0,0 +1,215 @@
+--- a/net/minecraft/world/entity/projectile/ThrownTrident.java
++++ b/net/minecraft/world/entity/projectile/ThrownTrident.java
+@@ -25,6 +25,7 @@
+ import net.minecraft.world.phys.Vec3;
+
+ public class ThrownTrident extends AbstractArrow {
++
+ private static final EntityDataAccessor<Byte> ID_LOYALTY = SynchedEntityData.defineId(ThrownTrident.class, EntityDataSerializers.BYTE);
+ private static final EntityDataAccessor<Boolean> ID_FOIL = SynchedEntityData.defineId(ThrownTrident.class, EntityDataSerializers.BOOLEAN);
+ private static final ItemStack DEFAULT_ARROW_STACK = new ItemStack(Items.TRIDENT);
+@@ -32,20 +33,20 @@
+ public int clientSideReturnTridentTickCount;
+
+ public ThrownTrident(EntityType<? extends ThrownTrident> entityType, Level level) {
+- super(entityType, level, DEFAULT_ARROW_STACK);
++ super(entityType, level, ThrownTrident.DEFAULT_ARROW_STACK);
+ }
+
+ public ThrownTrident(Level level, LivingEntity shooter, ItemStack stack) {
+ super(EntityType.TRIDENT, shooter, level, stack);
+- this.entityData.set(ID_LOYALTY, (byte)EnchantmentHelper.getLoyalty(stack));
+- this.entityData.set(ID_FOIL, stack.hasFoil());
++ this.entityData.set(ThrownTrident.ID_LOYALTY, (byte) EnchantmentHelper.getLoyalty(stack));
++ this.entityData.set(ThrownTrident.ID_FOIL, stack.hasFoil());
+ }
+
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(ID_LOYALTY, (byte)0);
+- this.entityData.define(ID_FOIL, false);
++ this.entityData.define(ThrownTrident.ID_LOYALTY, (byte) 0);
++ this.entityData.define(ThrownTrident.ID_FOIL, false);
+ }
+
+ @Override
+@@ -54,9 +55,10 @@
+ this.dealtDamage = true;
+ }
+
+- Entity owner = this.getOwner();
+- int i = this.entityData.get(ID_LOYALTY);
+- if (i > 0 && (this.dealtDamage || this.isNoPhysics()) && owner != null) {
++ Entity entity = this.getOwner();
++ byte b0 = (Byte) this.entityData.get(ThrownTrident.ID_LOYALTY);
++
++ if (b0 > 0 && (this.dealtDamage || this.isNoPhysics()) && entity != null) {
+ if (!this.isAcceptibleReturnOwner()) {
+ if (!this.level().isClientSide && this.pickup == AbstractArrow.Pickup.ALLOWED) {
+ this.spawnAtLocation(this.getPickupItem(), 0.1F);
+@@ -65,19 +67,21 @@
+ this.discard();
+ } else {
+ this.setNoPhysics(true);
+- Vec3 vec3 = owner.getEyePosition().subtract(this.position());
+- this.setPosRaw(this.getX(), this.getY() + vec3.y * 0.015 * (double)i, this.getZ());
++ Vec3 vec3d = entity.getEyePosition().subtract(this.position());
++
++ this.setPosRaw(this.getX(), this.getY() + vec3d.y * 0.015D * (double) b0, this.getZ());
+ if (this.level().isClientSide) {
+ this.yOld = this.getY();
+ }
+
+- double d = 0.05 * (double)i;
+- this.setDeltaMovement(this.getDeltaMovement().scale(0.95).add(vec3.normalize().scale(d)));
++ double d0 = 0.05D * (double) b0;
++
++ this.setDeltaMovement(this.getDeltaMovement().scale(0.95D).add(vec3d.normalize().scale(d0)));
+ if (this.clientSideReturnTridentTickCount == 0) {
+ this.playSound(SoundEvents.TRIDENT_RETURN, 10.0F, 1.0F);
+ }
+
+- this.clientSideReturnTridentTickCount++;
++ ++this.clientSideReturnTridentTickCount;
+ }
+ }
+
+@@ -85,12 +89,13 @@
+ }
+
+ private boolean isAcceptibleReturnOwner() {
+- Entity owner = this.getOwner();
+- return owner != null && owner.isAlive() && (!(owner instanceof ServerPlayer) || !owner.isSpectator());
++ Entity entity = this.getOwner();
++
++ return entity != null && entity.isAlive() ? !(entity instanceof ServerPlayer) || !entity.isSpectator() : false;
+ }
+
+ public boolean isFoil() {
+- return this.entityData.get(ID_FOIL);
++ return (Boolean) this.entityData.get(ThrownTrident.ID_FOIL);
+ }
+
+ @Nullable
+@@ -103,49 +108,59 @@
+ protected void onHitEntity(EntityHitResult result) {
+ Entity entity = result.getEntity();
+ float f = 8.0F;
+- if (entity instanceof LivingEntity livingEntity) {
+- f += EnchantmentHelper.getDamageBonus(this.getPickupItemStackOrigin(), livingEntity.getMobType());
++
++ if (entity instanceof LivingEntity) {
++ LivingEntity entityliving = (LivingEntity) entity;
++
++ f += EnchantmentHelper.getDamageBonus(this.getPickupItemStackOrigin(), entityliving.getMobType());
+ }
+
+- Entity owner = this.getOwner();
+- DamageSource damageSource = this.damageSources().trident(this, (Entity)(owner == null ? this : owner));
++ Entity entity1 = this.getOwner();
++ DamageSource damagesource = this.damageSources().trident(this, (Entity) (entity1 == null ? this : entity1));
++
+ this.dealtDamage = true;
+- SoundEvent soundEvent = SoundEvents.TRIDENT_HIT;
+- if (entity.hurt(damageSource, f)) {
++ SoundEvent soundeffect = SoundEvents.TRIDENT_HIT;
++
++ if (entity.hurt(damagesource, f)) {
+ if (entity.getType() == EntityType.ENDERMAN) {
+ return;
+ }
+
+- if (entity instanceof LivingEntity livingEntity1) {
+- if (owner instanceof LivingEntity) {
+- EnchantmentHelper.doPostHurtEffects(livingEntity1, owner);
+- EnchantmentHelper.doPostDamageEffects((LivingEntity)owner, livingEntity1);
++ if (entity instanceof LivingEntity) {
++ LivingEntity entityliving1 = (LivingEntity) entity;
++
++ if (entity1 instanceof LivingEntity) {
++ EnchantmentHelper.doPostHurtEffects(entityliving1, entity1);
++ EnchantmentHelper.doPostDamageEffects((LivingEntity) entity1, entityliving1);
+ }
+
+- this.doPostHurtEffects(livingEntity1);
++ this.doPostHurtEffects(entityliving1);
+ }
+ } else if (entity.getType().is(EntityTypeTags.DEFLECTS_TRIDENTS)) {
+ this.deflect();
+ return;
+ }
+
+- this.setDeltaMovement(this.getDeltaMovement().multiply(-0.01, -0.1, -0.01));
++ this.setDeltaMovement(this.getDeltaMovement().multiply(-0.01D, -0.1D, -0.01D));
+ float f1 = 1.0F;
++
+ if (this.level() instanceof ServerLevel && this.level().isThundering() && this.isChanneling()) {
+- BlockPos blockPos = entity.blockPosition();
+- if (this.level().canSeeSky(blockPos)) {
+- LightningBolt lightningBolt = EntityType.LIGHTNING_BOLT.create(this.level());
+- if (lightningBolt != null) {
+- lightningBolt.moveTo(Vec3.atBottomCenterOf(blockPos));
+- lightningBolt.setCause(owner instanceof ServerPlayer ? (ServerPlayer)owner : null);
+- this.level().addFreshEntity(lightningBolt);
+- soundEvent = SoundEvents.TRIDENT_THUNDER;
++ BlockPos blockposition = entity.blockPosition();
++
++ if (this.level().canSeeSky(blockposition)) {
++ LightningBolt entitylightning = (LightningBolt) EntityType.LIGHTNING_BOLT.create(this.level());
++
++ if (entitylightning != null) {
++ entitylightning.moveTo(Vec3.atBottomCenterOf(blockposition));
++ entitylightning.setCause(entity1 instanceof ServerPlayer ? (ServerPlayer) entity1 : null);
++ ((ServerLevel) this.level()).strikeLightning(entitylightning, org.bukkit.event.weather.LightningStrikeEvent.Cause.TRIDENT); // CraftBukkit
++ soundeffect = SoundEvents.TRIDENT_THUNDER;
+ f1 = 5.0F;
+ }
+ }
+ }
+
+- this.playSound(soundEvent, f1, 1.0F);
++ this.playSound(soundeffect, f1, 1.0F);
+ }
+
+ public boolean isChanneling() {
+@@ -167,13 +182,14 @@
+ if (this.ownedBy(entity) || this.getOwner() == null) {
+ super.playerTouch(entity);
+ }
++
+ }
+
+ @Override
+ public void readAdditionalSaveData(CompoundTag compound) {
+ super.readAdditionalSaveData(compound);
+ this.dealtDamage = compound.getBoolean("DealtDamage");
+- this.entityData.set(ID_LOYALTY, (byte)EnchantmentHelper.getLoyalty(this.getPickupItemStackOrigin()));
++ this.entityData.set(ThrownTrident.ID_LOYALTY, (byte) EnchantmentHelper.getLoyalty(this.getPickupItemStackOrigin()));
+ }
+
+ @Override
+@@ -184,10 +200,12 @@
+
+ @Override
+ public void tickDespawn() {
+- int i = this.entityData.get(ID_LOYALTY);
+- if (this.pickup != AbstractArrow.Pickup.ALLOWED || i <= 0) {
++ byte b0 = (Byte) this.entityData.get(ThrownTrident.ID_LOYALTY);
++
++ if (this.pickup != AbstractArrow.Pickup.ALLOWED || b0 <= 0) {
+ super.tickDespawn();
+ }
++
+ }
+
+ @Override
+@@ -196,7 +214,7 @@
+ }
+
+ @Override
+- public boolean shouldRender(double x, double y, double z) {
++ public boolean shouldRender(double x, double d1, double y) {
+ return true;
+ }
+ }
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/WindCharge.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/WindCharge.java.patch
new file mode 100644
index 0000000000..38a6ddfa83
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/WindCharge.java.patch
@@ -0,0 +1,134 @@
+--- a/net/minecraft/world/entity/projectile/WindCharge.java
++++ b/net/minecraft/world/entity/projectile/WindCharge.java
+@@ -4,11 +4,13 @@
+ import net.minecraft.core.particles.ParticleOptions;
+ import net.minecraft.core.particles.ParticleTypes;
+ import net.minecraft.sounds.SoundEvents;
++import net.minecraft.world.damagesource.DamageSource;
++import net.minecraft.world.damagesource.DamageSources;
+ 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.monster.breeze.Breeze;
+ import net.minecraft.world.item.ItemStack;
+ import net.minecraft.world.level.ClipContext;
+@@ -21,14 +23,15 @@
+ import net.minecraft.world.phys.HitResult;
+
+ public class WindCharge extends AbstractHurtingProjectile implements ItemSupplier {
+- public static final WindCharge.WindChargeExplosionDamageCalculator EXPLOSION_DAMAGE_CALCULATOR = new WindCharge.WindChargeExplosionDamageCalculator();
+
++ public static final WindCharge.a EXPLOSION_DAMAGE_CALCULATOR = new WindCharge.a();
++
+ public WindCharge(EntityType<? extends WindCharge> entityType, Level level) {
+ super(entityType, level);
+ }
+
+- public WindCharge(EntityType<? extends WindCharge> entityType, Breeze breeze, Level level) {
+- super(entityType, breeze.getX(), breeze.getSnoutYPosition(), breeze.getZ(), level);
++ public WindCharge(EntityType<? extends WindCharge> entitytypes, Breeze breeze, Level world) {
++ super(entitytypes, breeze.getX(), breeze.getSnoutYPosition(), breeze.getZ(), world);
+ this.setOwner(breeze);
+ }
+
+@@ -37,56 +40,49 @@
+ float f = this.getType().getDimensions().width / 2.0F;
+ float f1 = this.getType().getDimensions().height;
+ float f2 = 0.15F;
+- return new AABB(
+- this.position().x - (double)f,
+- this.position().y - 0.15F,
+- this.position().z - (double)f,
+- this.position().x + (double)f,
+- this.position().y - 0.15F + (double)f1,
+- this.position().z + (double)f
+- );
++
++ return new AABB(this.position().x - (double) f, this.position().y - 0.15000000596046448D, this.position().z - (double) f, this.position().x + (double) f, this.position().y - 0.15000000596046448D + (double) f1, this.position().z + (double) f);
+ }
+
+ @Override
+- protected float getEyeHeight(Pose pose, EntityDimensions dimensions) {
++ protected float getEyeHeight(EntityPose pose, EntityDimensions dimensions) {
+ return 0.0F;
+ }
+
+ @Override
+ public boolean canCollideWith(Entity entity) {
+- return !(entity instanceof WindCharge) && super.canCollideWith(entity);
++ return entity instanceof WindCharge ? false : super.canCollideWith(entity);
+ }
+
+ @Override
+ protected boolean canHitEntity(Entity target) {
+- return !(target instanceof WindCharge) && super.canHitEntity(target);
++ return target instanceof WindCharge ? false : super.canHitEntity(target);
+ }
+
+ @Override
+ protected void onHitEntity(EntityHitResult result) {
+ super.onHitEntity(result);
+ if (!this.level().isClientSide) {
+- result.getEntity().hurt(this.damageSources().mobProjectile(this, this.getOwner() instanceof LivingEntity livingEntity ? livingEntity : null), 1.0F);
++ Entity entity = result.getEntity();
++ DamageSources damagesources = this.damageSources();
++ Entity entity1 = this.getOwner();
++ LivingEntity entityliving;
++
++ if (entity1 instanceof LivingEntity) {
++ LivingEntity entityliving1 = (LivingEntity) entity1;
++
++ entityliving = entityliving1;
++ } else {
++ entityliving = null;
++ }
++
++ entity.hurt(damagesources.mobProjectile(this, entityliving), 1.0F);
+ this.explode();
+ }
+ }
+
+- private void explode() {
+- this.level()
+- .explode(
+- this,
+- null,
+- EXPLOSION_DAMAGE_CALCULATOR,
+- this.getX(),
+- this.getY(),
+- this.getZ(),
+- (float)(3.0 + this.random.nextDouble()),
+- false,
+- Level.ExplosionInteraction.BLOW,
+- ParticleTypes.GUST,
+- ParticleTypes.GUST_EMITTER,
+- SoundEvents.WIND_BURST
+- );
++ public void explode() { // PAIL private -> public
++ this.level().explode(this, (DamageSource) null, WindCharge.EXPLOSION_DAMAGE_CALCULATOR, this.getX(), this.getY(), this.getZ(), (float) (3.0D + this.random.nextDouble()), false, Level.a.BLOW, ParticleTypes.GUST, ParticleTypes.GUST_EMITTER, SoundEvents.WIND_BURST);
+ }
+
+ @Override
+@@ -102,6 +98,7 @@
+ if (!this.level().isClientSide) {
+ this.discard();
+ }
++
+ }
+
+ @Override
+@@ -135,7 +132,10 @@
+ return ClipContext.Block.OUTLINE;
+ }
+
+- public static final class WindChargeExplosionDamageCalculator extends ExplosionDamageCalculator {
++ public static final class a extends ExplosionDamageCalculator {
++
++ public a() {}
++
+ @Override
+ public boolean shouldDamageEntity(Explosion explosion, Entity entity) {
+ return false;
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/WitherSkull.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/WitherSkull.java.patch
new file mode 100644
index 0000000000..1ffc1a5de3
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/projectile/WitherSkull.java.patch
@@ -0,0 +1,130 @@
+--- a/net/minecraft/world/entity/projectile/WitherSkull.java
++++ b/net/minecraft/world/entity/projectile/WitherSkull.java
+@@ -16,20 +16,24 @@
+ import net.minecraft.world.level.BlockGetter;
+ import net.minecraft.world.level.Explosion;
+ 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.material.FluidState;
+ import net.minecraft.world.phys.EntityHitResult;
+ import net.minecraft.world.phys.HitResult;
++// CraftBukkit start
++import org.bukkit.event.entity.ExplosionPrimeEvent;
++// CraftBukkit end
+
+ public class WitherSkull extends AbstractHurtingProjectile {
++
+ private static final EntityDataAccessor<Boolean> DATA_DANGEROUS = SynchedEntityData.defineId(WitherSkull.class, EntityDataSerializers.BOOLEAN);
+
+ public WitherSkull(EntityType<? extends WitherSkull> entityType, Level level) {
+ super(entityType, level);
+ }
+
+- public WitherSkull(Level level, LivingEntity shooter, double offsetX, double offsetY, double offsetZ) {
+- super(EntityType.WITHER_SKULL, shooter, offsetX, offsetY, offsetZ, level);
++ public WitherSkull(Level level, LivingEntity shooter, double offsetX, double d1, double offsetY) {
++ super(EntityType.WITHER_SKULL, shooter, offsetX, d1, offsetY, level);
+ }
+
+ @Override
+@@ -43,9 +47,7 @@
+ }
+
+ @Override
+- public float getBlockExplosionResistance(
+- Explosion explosion, BlockGetter level, BlockPos pos, BlockState blockState, FluidState fluidState, float explosionPower
+- ) {
++ public float getBlockExplosionResistance(Explosion explosion, BlockGetter level, BlockPos pos, IBlockData blockState, FluidState fluidState, float explosionPower) {
+ return this.isDangerous() && WitherBoss.canDestroy(blockState) ? Math.min(0.8F, explosionPower) : explosionPower;
+ }
+
+@@ -54,32 +56,39 @@
+ super.onHitEntity(result);
+ if (!this.level().isClientSide) {
+ Entity entity = result.getEntity();
++ Entity entity1 = this.getOwner();
++ LivingEntity entityliving;
+ boolean flag;
+- if (this.getOwner() instanceof LivingEntity livingEntity) {
+- flag = entity.hurt(this.damageSources().witherSkull(this, livingEntity), 8.0F);
++
++ if (entity1 instanceof LivingEntity) {
++ entityliving = (LivingEntity) entity1;
++ flag = entity.hurt(this.damageSources().witherSkull(this, entityliving), 8.0F);
+ if (flag) {
+ if (entity.isAlive()) {
+- this.doEnchantDamageEffects(livingEntity, entity);
++ this.doEnchantDamageEffects(entityliving, entity);
+ } else {
+- livingEntity.heal(5.0F);
++ entityliving.heal(5.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.WITHER); // CraftBukkit
+ }
+ }
+ } else {
+ flag = entity.hurt(this.damageSources().magic(), 5.0F);
+ }
+
+- if (flag && entity instanceof LivingEntity livingEntityx) {
+- int i = 0;
++ if (flag && entity instanceof LivingEntity) {
++ entityliving = (LivingEntity) entity;
++ byte b0 = 0;
++
+ if (this.level().getDifficulty() == Difficulty.NORMAL) {
+- i = 10;
++ b0 = 10;
+ } else if (this.level().getDifficulty() == Difficulty.HARD) {
+- i = 40;
++ b0 = 40;
+ }
+
+- if (i > 0) {
+- livingEntityx.addEffect(new MobEffectInstance(MobEffects.WITHER, 20 * i, 1), this.getEffectSource());
++ if (b0 > 0) {
++ entityliving.addEffect(new MobEffectInstance(MobEffects.WITHER, 20 * b0, 1), this.getEffectSource(), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.ATTACK); // CraftBukkit
+ }
+ }
++
+ }
+ }
+
+@@ -87,9 +96,18 @@
+ protected void onHit(HitResult result) {
+ super.onHit(result);
+ if (!this.level().isClientSide) {
+- this.level().explode(this, this.getX(), this.getY(), this.getZ(), 1.0F, false, Level.ExplosionInteraction.MOB);
++ // CraftBukkit start
++ // this.level().explode(this, this.getX(), this.getY(), this.getZ(), 1.0F, false, World.a.MOB);
++ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 1.0F, false);
++ this.level().getCraftServer().getPluginManager().callEvent(event);
++
++ if (!event.isCancelled()) {
++ this.level().explode(this, this.getX(), this.getY(), this.getZ(), event.getRadius(), event.getFire(), Level.a.MOB);
++ }
++ // CraftBukkit end
+ this.discard();
+ }
++
+ }
+
+ @Override
+@@ -104,15 +122,15 @@
+
+ @Override
+ protected void defineSynchedData() {
+- this.entityData.define(DATA_DANGEROUS, false);
++ this.entityData.define(WitherSkull.DATA_DANGEROUS, false);
+ }
+
+ public boolean isDangerous() {
+- return this.entityData.get(DATA_DANGEROUS);
++ return (Boolean) this.entityData.get(WitherSkull.DATA_DANGEROUS);
+ }
+
+ public void setDangerous(boolean invulnerable) {
+- this.entityData.set(DATA_DANGEROUS, invulnerable);
++ this.entityData.set(WitherSkull.DATA_DANGEROUS, invulnerable);
+ }
+
+ @Override