--- a/net/minecraft/world/entity/AreaEffectCloud.java
+++ b/net/minecraft/world/entity/AreaEffectCloud.java
@@ -3,10 +3,12 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.mojang.logging.LogUtils;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 import javax.annotation.Nullable;
+import net.minecraft.core.Holder;
 import net.minecraft.core.particles.ColorParticleOption;
 import net.minecraft.core.particles.ParticleOptions;
 import net.minecraft.core.particles.ParticleTypes;
@@ -20,13 +22,22 @@
 import net.minecraft.server.level.ServerLevel;
 import net.minecraft.util.FastColor;
 import net.minecraft.util.Mth;
+import net.minecraft.world.effect.MobEffect;
 import net.minecraft.world.effect.MobEffectInstance;
+import net.minecraft.world.item.alchemy.Potion;
 import net.minecraft.world.item.alchemy.PotionContents;
 import net.minecraft.world.level.Level;
 import net.minecraft.world.level.material.PushReaction;
 import org.slf4j.Logger;
 
+// CraftBukkit start
+import org.bukkit.craftbukkit.entity.CraftLivingEntity;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.event.entity.EntityRemoveEvent;
+// CraftBukkit end
+
 public class AreaEffectCloud extends Entity implements TraceableEntity {
+
     private static final Logger LOGGER = LogUtils.getLogger();
     private static final int TIME_BETWEEN_APPLICATIONS = 5;
     private static final EntityDataAccessor<Float> DATA_RADIUS = SynchedEntityData.defineId(AreaEffectCloud.class, EntityDataSerializers.FLOAT);
@@ -37,21 +48,26 @@
     private static final float DEFAULT_RADIUS = 3.0F;
     public static final float DEFAULT_WIDTH = 6.0F;
     public static final float HEIGHT = 0.5F;
-    public PotionContents potionContents = PotionContents.EMPTY;
-    private final Map<Entity, Integer> victims = Maps.newHashMap();
-    private int duration = 600;
-    public int waitTime = 20;
-    public int reapplicationDelay = 20;
+    public PotionContents potionContents;
+    private final Map<Entity, Integer> victims;
+    private int duration;
+    public int waitTime;
+    public int reapplicationDelay;
     public int durationOnUse;
     public float radiusOnUse;
     public float radiusPerTick;
     @Nullable
-    private LivingEntity owner;
+    private net.minecraft.world.entity.LivingEntity owner;
     @Nullable
     public UUID ownerUUID;
 
     public AreaEffectCloud(EntityType<? extends AreaEffectCloud> type, Level world) {
         super(type, world);
+        this.potionContents = PotionContents.EMPTY;
+        this.victims = Maps.newHashMap();
+        this.duration = 600;
+        this.waitTime = 20;
+        this.reapplicationDelay = 20;
         this.noPhysics = true;
     }
 
@@ -62,28 +78,30 @@
 
     @Override
     protected void defineSynchedData(SynchedEntityData.Builder builder) {
-        builder.define(DATA_RADIUS, 3.0F);
-        builder.define(DATA_WAITING, false);
-        builder.define(DATA_PARTICLE, ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, -1));
+        builder.define(AreaEffectCloud.DATA_RADIUS, 3.0F);
+        builder.define(AreaEffectCloud.DATA_WAITING, false);
+        builder.define(AreaEffectCloud.DATA_PARTICLE, ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, -1));
     }
 
     public void setRadius(float radius) {
         if (!this.level().isClientSide) {
-            this.getEntityData().set(DATA_RADIUS, Mth.clamp(radius, 0.0F, 32.0F));
+            this.getEntityData().set(AreaEffectCloud.DATA_RADIUS, Mth.clamp(radius, 0.0F, 32.0F));
         }
+
     }
 
     @Override
     public void refreshDimensions() {
-        double d = this.getX();
-        double e = this.getY();
-        double f = this.getZ();
+        double d0 = this.getX();
+        double d1 = this.getY();
+        double d2 = this.getZ();
+
         super.refreshDimensions();
-        this.setPos(d, e, f);
+        this.setPos(d0, d1, d2);
     }
 
     public float getRadius() {
-        return this.getEntityData().get(DATA_RADIUS);
+        return (Float) this.getEntityData().get(AreaEffectCloud.DATA_RADIUS);
     }
 
     public void setPotionContents(PotionContents potionContentsComponent) {
@@ -92,11 +110,14 @@
     }
 
     public void updateColor() {
-        ParticleOptions particleOptions = this.entityData.get(DATA_PARTICLE);
-        if (particleOptions instanceof ColorParticleOption colorParticleOption) {
+        ParticleOptions particleparam = (ParticleOptions) this.entityData.get(AreaEffectCloud.DATA_PARTICLE);
+
+        if (particleparam instanceof ColorParticleOption colorparticleoption) {
             int i = this.potionContents.equals(PotionContents.EMPTY) ? 0 : this.potionContents.getColor();
-            this.entityData.set(DATA_PARTICLE, ColorParticleOption.create(colorParticleOption.getType(), FastColor.ARGB32.opaque(i)));
+
+            this.entityData.set(AreaEffectCloud.DATA_PARTICLE, ColorParticleOption.create(colorparticleoption.getType(), FastColor.ARGB32.opaque(i)));
         }
+
     }
 
     public void addEffect(MobEffectInstance effect) {
@@ -104,19 +125,19 @@
     }
 
     public ParticleOptions getParticle() {
-        return this.getEntityData().get(DATA_PARTICLE);
+        return (ParticleOptions) this.getEntityData().get(AreaEffectCloud.DATA_PARTICLE);
     }
 
     public void setParticle(ParticleOptions particle) {
-        this.getEntityData().set(DATA_PARTICLE, particle);
+        this.getEntityData().set(AreaEffectCloud.DATA_PARTICLE, particle);
     }
 
     protected void setWaiting(boolean waiting) {
-        this.getEntityData().set(DATA_WAITING, waiting);
+        this.getEntityData().set(AreaEffectCloud.DATA_WAITING, waiting);
     }
 
     public boolean isWaiting() {
-        return this.getEntityData().get(DATA_WAITING);
+        return (Boolean) this.getEntityData().get(AreaEffectCloud.DATA_WAITING);
     }
 
     public int getDuration() {
@@ -127,67 +148,80 @@
         this.duration = duration;
     }
 
+    // Spigot start - copied from below
+    @Override
+    public void inactiveTick() {
+        super.inactiveTick();
+
+        if (this.tickCount >= this.waitTime + this.duration) {
+            this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
+            return;
+        }
+    }
+    // Spigot end
+
     @Override
     public void tick() {
         super.tick();
-        boolean bl = this.isWaiting();
+        boolean flag = this.isWaiting();
         float f = this.getRadius();
+
         if (this.level().isClientSide) {
-            if (bl && this.random.nextBoolean()) {
+            if (flag && this.random.nextBoolean()) {
                 return;
             }
 
-            ParticleOptions particleOptions = this.getParticle();
+            ParticleOptions particleparam = this.getParticle();
             int i;
-            float g;
-            if (bl) {
+            float f1;
+
+            if (flag) {
                 i = 2;
-                g = 0.2F;
+                f1 = 0.2F;
             } else {
-                i = Mth.ceil((float) Math.PI * f * f);
-                g = f;
+                i = Mth.ceil(3.1415927F * f * f);
+                f1 = f;
             }
 
-            for (int k = 0; k < i; k++) {
-                float l = this.random.nextFloat() * (float) (Math.PI * 2);
-                float m = Mth.sqrt(this.random.nextFloat()) * g;
-                double d = this.getX() + (double)(Mth.cos(l) * m);
-                double e = this.getY();
-                double n = this.getZ() + (double)(Mth.sin(l) * m);
-                if (particleOptions.getType() == ParticleTypes.ENTITY_EFFECT) {
-                    if (bl && this.random.nextBoolean()) {
-                        this.level().addAlwaysVisibleParticle(ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, -1), d, e, n, 0.0, 0.0, 0.0);
+            for (int j = 0; j < i; ++j) {
+                float f2 = this.random.nextFloat() * 6.2831855F;
+                float f3 = Mth.sqrt(this.random.nextFloat()) * f1;
+                double d0 = this.getX() + (double) (Mth.cos(f2) * f3);
+                double d1 = this.getY();
+                double d2 = this.getZ() + (double) (Mth.sin(f2) * f3);
+
+                if (particleparam.getType() == ParticleTypes.ENTITY_EFFECT) {
+                    if (flag && this.random.nextBoolean()) {
+                        this.level().addAlwaysVisibleParticle(ColorParticleOption.create(ParticleTypes.ENTITY_EFFECT, -1), d0, d1, d2, 0.0D, 0.0D, 0.0D);
                     } else {
-                        this.level().addAlwaysVisibleParticle(particleOptions, d, e, n, 0.0, 0.0, 0.0);
+                        this.level().addAlwaysVisibleParticle(particleparam, d0, d1, d2, 0.0D, 0.0D, 0.0D);
                     }
-                } else if (bl) {
-                    this.level().addAlwaysVisibleParticle(particleOptions, d, e, n, 0.0, 0.0, 0.0);
+                } else if (flag) {
+                    this.level().addAlwaysVisibleParticle(particleparam, d0, d1, d2, 0.0D, 0.0D, 0.0D);
                 } else {
-                    this.level()
-                        .addAlwaysVisibleParticle(
-                            particleOptions, d, e, n, (0.5 - this.random.nextDouble()) * 0.15, 0.01F, (0.5 - this.random.nextDouble()) * 0.15
-                        );
+                    this.level().addAlwaysVisibleParticle(particleparam, d0, d1, d2, (0.5D - this.random.nextDouble()) * 0.15D, 0.009999999776482582D, (0.5D - this.random.nextDouble()) * 0.15D);
                 }
             }
         } else {
             if (this.tickCount >= this.waitTime + this.duration) {
-                this.discard();
+                this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
                 return;
             }
 
-            boolean bl2 = this.tickCount < this.waitTime;
-            if (bl != bl2) {
-                this.setWaiting(bl2);
+            boolean flag1 = this.tickCount < this.waitTime;
+
+            if (flag != flag1) {
+                this.setWaiting(flag1);
             }
 
-            if (bl2) {
+            if (flag1) {
                 return;
             }
 
             if (this.radiusPerTick != 0.0F) {
                 f += this.radiusPerTick;
                 if (f < 0.5F) {
-                    this.discard();
+                    this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
                     return;
                 }
 
@@ -195,50 +229,70 @@
             }
 
             if (this.tickCount % 5 == 0) {
-                this.victims.entrySet().removeIf(entry -> this.tickCount >= entry.getValue());
+                this.victims.entrySet().removeIf((entry) -> {
+                    return this.tickCount >= (Integer) entry.getValue();
+                });
                 if (!this.potionContents.hasEffects()) {
                     this.victims.clear();
                 } else {
                     List<MobEffectInstance> list = Lists.newArrayList();
+
                     if (this.potionContents.potion().isPresent()) {
-                        for (MobEffectInstance mobEffectInstance : this.potionContents.potion().get().value().getEffects()) {
-                            list.add(
-                                new MobEffectInstance(
-                                    mobEffectInstance.getEffect(),
-                                    mobEffectInstance.mapDuration(duration -> duration / 4),
-                                    mobEffectInstance.getAmplifier(),
-                                    mobEffectInstance.isAmbient(),
-                                    mobEffectInstance.isVisible()
-                                )
-                            );
+                        Iterator iterator = ((Potion) ((Holder) this.potionContents.potion().get()).value()).getEffects().iterator();
+
+                        while (iterator.hasNext()) {
+                            MobEffectInstance mobeffect = (MobEffectInstance) iterator.next();
+
+                            list.add(new MobEffectInstance(mobeffect.getEffect(), mobeffect.mapDuration((k) -> {
+                                return k / 4;
+                            }), mobeffect.getAmplifier(), mobeffect.isAmbient(), mobeffect.isVisible()));
                         }
                     }
 
                     list.addAll(this.potionContents.customEffects());
-                    List<LivingEntity> list2 = this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox());
-                    if (!list2.isEmpty()) {
-                        for (LivingEntity livingEntity : list2) {
-                            if (!this.victims.containsKey(livingEntity) && livingEntity.isAffectedByPotions()) {
-                                double o = livingEntity.getX() - this.getX();
-                                double p = livingEntity.getZ() - this.getZ();
-                                double q = o * o + p * p;
-                                if (q <= (double)(f * f)) {
-                                    this.victims.put(livingEntity, this.tickCount + this.reapplicationDelay);
+                    List<net.minecraft.world.entity.LivingEntity> list1 = this.level().getEntitiesOfClass(net.minecraft.world.entity.LivingEntity.class, this.getBoundingBox());
 
-                                    for (MobEffectInstance mobEffectInstance2 : list) {
-                                        if (mobEffectInstance2.getEffect().value().isInstantenous()) {
-                                            mobEffectInstance2.getEffect()
-                                                .value()
-                                                .applyInstantenousEffect(this, this.getOwner(), livingEntity, mobEffectInstance2.getAmplifier(), 0.5);
+                    if (!list1.isEmpty()) {
+                        Iterator iterator1 = list1.iterator();
+
+                        List<LivingEntity> entities = new java.util.ArrayList<LivingEntity>(); // CraftBukkit
+                        while (iterator1.hasNext()) {
+                            net.minecraft.world.entity.LivingEntity entityliving = (net.minecraft.world.entity.LivingEntity) iterator1.next();
+
+                            if (!this.victims.containsKey(entityliving) && entityliving.isAffectedByPotions()) {
+                                double d3 = entityliving.getX() - this.getX();
+                                double d4 = entityliving.getZ() - this.getZ();
+                                double d5 = d3 * d3 + d4 * d4;
+
+                                if (d5 <= (double) (f * f)) {
+                                    // CraftBukkit start
+                                    entities.add((LivingEntity) entityliving.getBukkitEntity());
+                                }
+                            }
+                        }
+                        org.bukkit.event.entity.AreaEffectCloudApplyEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callAreaEffectCloudApplyEvent(this, entities);
+                        if (!event.isCancelled()) {
+                            for (LivingEntity entity : event.getAffectedEntities()) {
+                                if (entity instanceof CraftLivingEntity) {
+                                    net.minecraft.world.entity.LivingEntity entityliving = ((CraftLivingEntity) entity).getHandle();
+                                    // CraftBukkit end
+                                    this.victims.put(entityliving, this.tickCount + this.reapplicationDelay);
+                                    Iterator iterator2 = list.iterator();
+
+                                    while (iterator2.hasNext()) {
+                                        MobEffectInstance mobeffect1 = (MobEffectInstance) iterator2.next();
+
+                                        if (((MobEffect) mobeffect1.getEffect().value()).isInstantenous()) {
+                                            ((MobEffect) mobeffect1.getEffect().value()).applyInstantenousEffect(this, this.getOwner(), entityliving, mobeffect1.getAmplifier(), 0.5D);
                                         } else {
-                                            livingEntity.addEffect(new MobEffectInstance(mobEffectInstance2), this);
+                                            entityliving.addEffect(new MobEffectInstance(mobeffect1), this, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.AREA_EFFECT_CLOUD); // CraftBukkit
                                         }
                                     }
 
                                     if (this.radiusOnUse != 0.0F) {
                                         f += this.radiusOnUse;
                                         if (f < 0.5F) {
-                                            this.discard();
+                                            this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
                                             return;
                                         }
 
@@ -246,9 +300,9 @@
                                     }
 
                                     if (this.durationOnUse != 0) {
-                                        this.duration = this.duration + this.durationOnUse;
+                                        this.duration += this.durationOnUse;
                                         if (this.duration <= 0) {
-                                            this.discard();
+                                            this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
                                             return;
                                         }
                                     }
@@ -259,6 +313,7 @@
                 }
             }
         }
+
     }
 
     public float getRadiusOnUse() {
@@ -293,18 +348,19 @@
         this.waitTime = waitTime;
     }
 
-    public void setOwner(@Nullable LivingEntity owner) {
+    public void setOwner(@Nullable net.minecraft.world.entity.LivingEntity owner) {
         this.owner = owner;
         this.ownerUUID = owner == null ? null : owner.getUUID();
     }
 
     @Nullable
     @Override
-    public LivingEntity getOwner() {
+    public net.minecraft.world.entity.LivingEntity getOwner() {
         if (this.owner == null && this.ownerUUID != null && this.level() instanceof ServerLevel) {
-            Entity entity = ((ServerLevel)this.level()).getEntity(this.ownerUUID);
-            if (entity instanceof LivingEntity) {
-                this.owner = (LivingEntity)entity;
+            Entity entity = ((ServerLevel) this.level()).getEntity(this.ownerUUID);
+
+            if (entity instanceof net.minecraft.world.entity.LivingEntity) {
+                this.owner = (net.minecraft.world.entity.LivingEntity) entity;
             }
         }
 
@@ -325,20 +381,20 @@
             this.ownerUUID = nbt.getUUID("Owner");
         }
 
-        RegistryOps<Tag> registryOps = this.registryAccess().createSerializationContext(NbtOps.INSTANCE);
+        RegistryOps<Tag> registryops = this.registryAccess().createSerializationContext(NbtOps.INSTANCE);
+
         if (nbt.contains("Particle", 10)) {
-            ParticleTypes.CODEC
-                .parse(registryOps, nbt.get("Particle"))
-                .resultOrPartial(string -> LOGGER.warn("Failed to parse area effect cloud particle options: '{}'", string))
-                .ifPresent(this::setParticle);
+            ParticleTypes.CODEC.parse(registryops, nbt.get("Particle")).resultOrPartial((s) -> {
+                AreaEffectCloud.LOGGER.warn("Failed to parse area effect cloud particle options: '{}'", s);
+            }).ifPresent(this::setParticle);
         }
 
         if (nbt.contains("potion_contents")) {
-            PotionContents.CODEC
-                .parse(registryOps, nbt.get("potion_contents"))
-                .resultOrPartial(string -> LOGGER.warn("Failed to parse area effect cloud potions: '{}'", string))
-                .ifPresent(this::setPotionContents);
+            PotionContents.CODEC.parse(registryops, nbt.get("potion_contents")).resultOrPartial((s) -> {
+                AreaEffectCloud.LOGGER.warn("Failed to parse area effect cloud potions: '{}'", s);
+            }).ifPresent(this::setPotionContents);
         }
+
     }
 
     @Override
@@ -351,21 +407,24 @@
         nbt.putFloat("RadiusOnUse", this.radiusOnUse);
         nbt.putFloat("RadiusPerTick", this.radiusPerTick);
         nbt.putFloat("Radius", this.getRadius());
-        RegistryOps<Tag> registryOps = this.registryAccess().createSerializationContext(NbtOps.INSTANCE);
-        nbt.put("Particle", ParticleTypes.CODEC.encodeStart(registryOps, this.getParticle()).getOrThrow());
+        RegistryOps<Tag> registryops = this.registryAccess().createSerializationContext(NbtOps.INSTANCE);
+
+        nbt.put("Particle", (Tag) ParticleTypes.CODEC.encodeStart(registryops, this.getParticle()).getOrThrow());
         if (this.ownerUUID != null) {
             nbt.putUUID("Owner", this.ownerUUID);
         }
 
         if (!this.potionContents.equals(PotionContents.EMPTY)) {
-            Tag tag = PotionContents.CODEC.encodeStart(registryOps, this.potionContents).getOrThrow();
-            nbt.put("potion_contents", tag);
+            Tag nbtbase = (Tag) PotionContents.CODEC.encodeStart(registryops, this.potionContents).getOrThrow();
+
+            nbt.put("potion_contents", nbtbase);
         }
+
     }
 
     @Override
     public void onSyncedDataUpdated(EntityDataAccessor<?> data) {
-        if (DATA_RADIUS.equals(data)) {
+        if (AreaEffectCloud.DATA_RADIUS.equals(data)) {
             this.refreshDimensions();
         }
 
