--- a/net/minecraft/world/entity/animal/Animal.java
+++ b/net/minecraft/world/entity/animal/Animal.java
@@ -28,12 +28,17 @@
 import net.minecraft.world.level.LevelReader;
 import net.minecraft.world.level.block.Blocks;
 import net.minecraft.world.level.pathfinder.BlockPathTypes;
+import org.bukkit.craftbukkit.v1_19_R2.event.CraftEventFactory;
+import org.bukkit.event.entity.EntityEnterLoveModeEvent;
+// CraftBukkit end
 
 public abstract class Animal extends AgeableMob {
+
     protected static final int PARENT_AGE_AFTER_BREEDING = 6000;
     public int inLove;
     @Nullable
     public UUID loveCause;
+    public ItemStack breedItem; // CraftBukkit - Add breedItem variable
 
     protected Animal(EntityType<? extends Animal> type, Level world) {
         super(type, world);
@@ -60,24 +65,29 @@
         if (this.inLove > 0) {
             --this.inLove;
             if (this.inLove % 10 == 0) {
-                double d = this.random.nextGaussian() * 0.02D;
-                double e = this.random.nextGaussian() * 0.02D;
-                double f = this.random.nextGaussian() * 0.02D;
-                this.level.addParticle(ParticleTypes.HEART, this.getRandomX(1.0D), this.getRandomY() + 0.5D, this.getRandomZ(1.0D), d, e, f);
+                double d0 = this.random.nextGaussian() * 0.02D;
+                double d1 = this.random.nextGaussian() * 0.02D;
+                double d2 = this.random.nextGaussian() * 0.02D;
+
+                this.level.addParticle(ParticleTypes.HEART, this.getRandomX(1.0D), this.getRandomY() + 0.5D, this.getRandomZ(1.0D), d0, d1, d2);
             }
         }
 
     }
 
+    /* CraftBukkit start
+    // Function disabled as it has no special function anymore after
+    // setSitting is disabled.
     @Override
-    public boolean hurt(DamageSource source, float amount) {
-        if (this.isInvulnerableTo(source)) {
+    public boolean hurt(DamageSource damagesource, float f) {
+        if (this.isInvulnerableTo(damagesource)) {
             return false;
         } else {
             this.inLove = 0;
-            return super.hurt(source, amount);
+            return super.hurt(damagesource, f);
         }
     }
+    // CraftBukkit end */
 
     @Override
     public float getWalkTargetValue(BlockPos pos, LevelReader world) {
@@ -107,7 +117,7 @@
     }
 
     public static boolean checkAnimalSpawnRules(EntityType<? extends Animal> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, RandomSource random) {
-        return world.getBlockState(pos.below()).is(BlockTags.ANIMALS_SPAWNABLE_ON) && isBrightEnoughToSpawn(world, pos);
+        return world.getBlockState(pos.below()).is(BlockTags.ANIMALS_SPAWNABLE_ON) && Animal.isBrightEnoughToSpawn(world, pos);
     }
 
     protected static boolean isBrightEnoughToSpawn(BlockAndTintGetter world, BlockPos pos) {
@@ -135,17 +145,19 @@
 
     @Override
     public InteractionResult mobInteract(Player player, InteractionHand hand) {
-        ItemStack itemStack = player.getItemInHand(hand);
-        if (this.isFood(itemStack)) {
+        ItemStack itemstack = player.getItemInHand(hand);
+
+        if (this.isFood(itemstack)) {
             int i = this.getAge();
+
             if (!this.level.isClientSide && i == 0 && this.canFallInLove()) {
-                this.usePlayerItem(player, hand, itemStack);
+                this.usePlayerItem(player, hand, itemstack);
                 this.setInLove(player);
                 return InteractionResult.SUCCESS;
             }
 
             if (this.isBaby()) {
-                this.usePlayerItem(player, hand, itemStack);
+                this.usePlayerItem(player, hand, itemstack);
                 this.ageUp(getSpeedUpSecondsWhenFeeding(-i), true);
                 return InteractionResult.sidedSuccess(this.level.isClientSide);
             }
@@ -170,12 +182,19 @@
     }
 
     public void setInLove(@Nullable Player player) {
-        this.inLove = 600;
+        // CraftBukkit start
+        EntityEnterLoveModeEvent entityEnterLoveModeEvent = CraftEventFactory.callEntityEnterLoveModeEvent(player, this, 600);
+        if (entityEnterLoveModeEvent.isCancelled()) {
+            return;
+        }
+        this.inLove = entityEnterLoveModeEvent.getTicksInLove();
+        // CraftBukkit end
         if (player != null) {
             this.loveCause = player.getUUID();
         }
+        this.breedItem = player.getInventory().getSelected(); // CraftBukkit
 
-        this.level.broadcastEntityEvent(this, (byte)18);
+        this.level.broadcastEntityEvent(this, (byte) 18);
     }
 
     public void setInLoveTime(int loveTicks) {
@@ -191,8 +210,9 @@
         if (this.loveCause == null) {
             return null;
         } else {
-            Player player = this.level.getPlayerByUUID(this.loveCause);
-            return player instanceof ServerPlayer ? (ServerPlayer)player : null;
+            Player entityhuman = this.level.getGlobalPlayerByUUID(this.loveCause); // Paper - check all players
+
+            return entityhuman instanceof ServerPlayer ? (ServerPlayer) entityhuman : null;
         }
     }
 
@@ -205,38 +225,46 @@
     }
 
     public boolean canMate(Animal other) {
-        if (other == this) {
-            return false;
-        } else if (other.getClass() != this.getClass()) {
-            return false;
-        } else {
-            return this.isInLove() && other.isInLove();
-        }
+        return other == this ? false : (other.getClass() != this.getClass() ? false : this.isInLove() && other.isInLove());
     }
 
     public void spawnChildFromBreeding(ServerLevel world, Animal other) {
-        AgeableMob ageableMob = this.getBreedOffspring(world, other);
-        if (ageableMob != null) {
-            ServerPlayer serverPlayer = this.getLoveCause();
-            if (serverPlayer == null && other.getLoveCause() != null) {
-                serverPlayer = other.getLoveCause();
+        AgeableMob entityageable = this.getBreedOffspring(world, other);
+
+        if (entityageable != null) {
+            ServerPlayer entityplayer = this.getLoveCause();
+
+            if (entityplayer == null && other.getLoveCause() != null) {
+                entityplayer = other.getLoveCause();
+            }
+            // CraftBukkit start - call EntityBreedEvent
+            entityageable.setBaby(true);
+            entityageable.moveTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F);
+            int experience = this.getRandom().nextInt(7) + 1;
+            org.bukkit.event.entity.EntityBreedEvent entityBreedEvent = org.bukkit.craftbukkit.v1_19_R2.event.CraftEventFactory.callEntityBreedEvent(entityageable, this, other, entityplayer, this.breedItem, experience);
+            if (entityBreedEvent.isCancelled()) {
+                return;
             }
+            experience = entityBreedEvent.getExperience();
+            // CraftBukkit end
 
-            if (serverPlayer != null) {
-                serverPlayer.awardStat(Stats.ANIMALS_BRED);
-                CriteriaTriggers.BRED_ANIMALS.trigger(serverPlayer, this, other, ageableMob);
+            if (entityplayer != null) {
+                entityplayer.awardStat(Stats.ANIMALS_BRED);
+                CriteriaTriggers.BRED_ANIMALS.trigger(entityplayer, this, other, entityageable);
             }
 
             this.setAge(6000);
             other.setAge(6000);
             this.resetLove();
             other.resetLove();
-            ageableMob.setBaby(true);
-            ageableMob.moveTo(this.getX(), this.getY(), this.getZ(), 0.0F, 0.0F);
-            world.addFreshEntityWithPassengers(ageableMob);
-            world.broadcastEntityEvent(this, (byte)18);
+            world.addFreshEntityWithPassengers(entityageable, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason
+            world.broadcastEntityEvent(this, (byte) 18);
             if (world.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) {
-                world.addFreshEntity(new ExperienceOrb(world, this.getX(), this.getY(), this.getZ(), this.getRandom().nextInt(7) + 1));
+                // CraftBukkit start - use event experience
+                if (experience > 0) {
+                    world.addFreshEntity(new ExperienceOrb(world, this.getX(), this.getY(), this.getZ(), experience, org.bukkit.entity.ExperienceOrb.SpawnReason.BREED, entityplayer, entityageable)); // Paper
+                }
+                // CraftBukkit end
             }
 
         }
@@ -245,11 +273,12 @@
     @Override
     public void handleEntityEvent(byte status) {
         if (status == 18) {
-            for(int i = 0; i < 7; ++i) {
-                double d = this.random.nextGaussian() * 0.02D;
-                double e = this.random.nextGaussian() * 0.02D;
-                double f = this.random.nextGaussian() * 0.02D;
-                this.level.addParticle(ParticleTypes.HEART, this.getRandomX(1.0D), this.getRandomY() + 0.5D, this.getRandomZ(1.0D), d, e, f);
+            for (int i = 0; i < 7; ++i) {
+                double d0 = this.random.nextGaussian() * 0.02D;
+                double d1 = this.random.nextGaussian() * 0.02D;
+                double d2 = this.random.nextGaussian() * 0.02D;
+
+                this.level.addParticle(ParticleTypes.HEART, this.getRandomX(1.0D), this.getRandomY() + 0.5D, this.getRandomZ(1.0D), d0, d1, d2);
             }
         } else {
             super.handleEntityEvent(status);
