--- a/net/minecraft/world/entity/animal/Fox.java
+++ b/net/minecraft/world/entity/animal/Fox.java
@@ -104,10 +_,10 @@
     private static final int FLAG_SLEEPING = 32;
     private static final int FLAG_FACEPLANTED = 64;
     private static final int FLAG_DEFENDING = 128;
-    private static final EntityDataAccessor<Optional<EntityReference<LivingEntity>>> DATA_TRUSTED_ID_0 = SynchedEntityData.defineId(
+    public static final EntityDataAccessor<Optional<EntityReference<LivingEntity>>> DATA_TRUSTED_ID_0 = SynchedEntityData.defineId(
         Fox.class, EntityDataSerializers.OPTIONAL_LIVING_ENTITY_REFERENCE
     );
-    private static final EntityDataAccessor<Optional<EntityReference<LivingEntity>>> DATA_TRUSTED_ID_1 = SynchedEntityData.defineId(
+    public static final EntityDataAccessor<Optional<EntityReference<LivingEntity>>> DATA_TRUSTED_ID_1 = SynchedEntityData.defineId(
         Fox.class, EntityDataSerializers.OPTIONAL_LIVING_ENTITY_REFERENCE
     );
     static final Predicate<ItemEntity> ALLOWED_ITEMS = item -> !item.hasPickUpDelay() && item.isAlive();
@@ -370,7 +_,7 @@
         return Fox.Variant.byId(this.entityData.get(DATA_TYPE_ID));
     }
 
-    private void setVariant(Fox.Variant variant) {
+    public void setVariant(Fox.Variant variant) {
         this.entityData.set(DATA_TYPE_ID, variant.getId());
     }
 
@@ -429,7 +_,7 @@
         compound.read("Trusted", TRUSTED_LIST_CODEC).orElse(List.of()).forEach(this::addTrustedEntity);
         this.setSleeping(compound.getBooleanOr("Sleeping", false));
         this.setVariant(compound.read("Type", Fox.Variant.CODEC).orElse(Fox.Variant.DEFAULT));
-        this.setSitting(compound.getBooleanOr("Sitting", false));
+        this.setSitting(compound.getBooleanOr("Sitting", false), false); // Paper - Add EntityToggleSitEvent
         this.setIsCrouching(compound.getBooleanOr("Crouching", false));
         if (this.level() instanceof ServerLevel) {
             this.setTargetGoals();
@@ -446,6 +_,12 @@
     }
 
     public void setSitting(boolean sitting) {
+        // Paper start - Add EntityToggleSitEvent
+        this.setSitting(sitting, true);
+    }
+    public void setSitting(boolean sitting, boolean fireEvent) {
+        if (fireEvent && !new io.papermc.paper.event.entity.EntityToggleSitEvent(this.getBukkitEntity(), sitting).callEvent()) return;
+        // Paper end - Add EntityToggleSitEvent
         this.setFlag(1, sitting);
     }
 
@@ -453,15 +_,15 @@
         return this.getFlag(64);
     }
 
-    void setFaceplanted(boolean faceplanted) {
+    public void setFaceplanted(boolean faceplanted) {
         this.setFlag(64, faceplanted);
     }
 
-    boolean isDefending() {
+    public boolean isDefending() {
         return this.getFlag(128);
     }
 
-    void setDefending(boolean defending) {
+    public void setDefending(boolean defending) {
         this.setFlag(128, defending);
     }
 
@@ -470,7 +_,7 @@
         return this.getFlag(32);
     }
 
-    void setSleeping(boolean sleeping) {
+    public void setSleeping(boolean sleeping) {
         this.setFlag(32, sleeping);
     }
 
@@ -505,19 +_,20 @@
             itemEntity.setPickUpDelay(40);
             itemEntity.setThrower(this);
             this.playSound(SoundEvents.FOX_SPIT, 1.0F, 1.0F);
-            this.level().addFreshEntity(itemEntity);
+            this.spawnAtLocation((net.minecraft.server.level.ServerLevel) this.level(), itemEntity); // Paper - Call EntityDropItemEvent
         }
     }
 
     private void dropItemStack(ItemStack stack) {
         ItemEntity itemEntity = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), stack);
-        this.level().addFreshEntity(itemEntity);
+        this.spawnAtLocation((net.minecraft.server.level.ServerLevel) this.level(), itemEntity); // Paper - Call EntityDropItemEvent
     }
 
     @Override
     protected void pickUpItem(ServerLevel level, ItemEntity entity) {
         ItemStack item = entity.getItem();
-        if (this.canHoldItem(item)) {
+        if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityPickupItemEvent(this, entity, item.getCount() - 1, !this.canHoldItem(item)).isCancelled()) { // CraftBukkit - call EntityPickupItemEvent
+            item = entity.getItem(); // CraftBukkit - update item after event
             int count = item.getCount();
             if (count > 1) {
                 this.dropItemStack(item.split(count - 1));
@@ -528,7 +_,7 @@
             this.setItemSlot(EquipmentSlot.MAINHAND, item.split(1));
             this.setGuaranteedDrop(EquipmentSlot.MAINHAND);
             this.take(entity, item.getCount());
-            entity.discard();
+            entity.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause
             this.ticksSinceEaten = 0;
         }
     }
@@ -623,12 +_,12 @@
     }
 
     @Override
-    public void setTarget(@Nullable LivingEntity livingEntity) {
+    public boolean setTarget(@Nullable LivingEntity livingEntity, @Nullable org.bukkit.event.entity.EntityTargetEvent.TargetReason reason) { // CraftBukkit
         if (this.isDefending() && livingEntity == null) {
             this.setDefending(false);
         }
 
-        super.setTarget(livingEntity);
+        return super.setTarget(livingEntity, reason); // CraftBukkit
     }
 
     void wakeUp() {
@@ -660,7 +_,7 @@
 
     @Nullable
     @Override
-    protected SoundEvent getAmbientSound() {
+    public SoundEvent getAmbientSound() {
         if (this.isSleeping()) {
             return SoundEvents.FOX_SLEEP;
         } else {
@@ -678,13 +_,13 @@
 
     @Nullable
     @Override
-    protected SoundEvent getHurtSound(DamageSource damageSource) {
+    public SoundEvent getHurtSound(DamageSource damageSource) {
         return SoundEvents.FOX_HURT;
     }
 
     @Nullable
     @Override
-    protected SoundEvent getDeathSound() {
+    public SoundEvent getDeathSound() {
         return SoundEvents.FOX_DEATH;
     }
 
@@ -692,15 +_,33 @@
         return this.getTrustedEntities().anyMatch(entityReference -> entityReference.matches(entity));
     }
 
-    @Override
-    protected void dropAllDeathLoot(ServerLevel level, DamageSource damageSource) {
+    // Paper start - handle the bitten item separately like vanilla
+    @Override
+    protected boolean shouldSkipLoot(EquipmentSlot slot) {
+        return slot == EquipmentSlot.MAINHAND;
+    }
+    // Paper end
+
+    @Override
+    // Paper start - Cancellable death event
+    protected org.bukkit.event.entity.EntityDeathEvent dropAllDeathLoot(ServerLevel level, DamageSource damageSource) {
         ItemStack itemBySlot = this.getItemBySlot(EquipmentSlot.MAINHAND);
-        if (!itemBySlot.isEmpty()) {
+        boolean releaseMouth = false;
+        if (!itemBySlot.isEmpty() && level.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { // Fix MC-153010
             this.spawnAtLocation(level, itemBySlot);
+            releaseMouth = true;
+        }
+
+        org.bukkit.event.entity.EntityDeathEvent deathEvent = super.dropAllDeathLoot(level, damageSource);
+        // Below is code to drop
+        if (deathEvent == null || deathEvent.isCancelled()) return deathEvent;
+
+        if (releaseMouth) {
+            // Paper end
             this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY);
         }
 
-        super.dropAllDeathLoot(level, damageSource);
+        return deathEvent; // Paper - Cancellable death event
     }
 
     public static boolean isPathClear(Fox fox, LivingEntity livingEntity) {
@@ -876,6 +_,19 @@
                     fox.addTrustedEntity(loveCause1);
                 }
 
+                // CraftBukkit start - call EntityBreedEvent
+                fox.setAge(-24000);
+                fox.snapTo(this.animal.getX(), this.animal.getY(), this.animal.getZ(), 0.0F, 0.0F);
+                int experience = this.animal.getRandom().nextInt(7) + 1;
+                org.bukkit.event.entity.EntityBreedEvent entityBreedEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreedEvent(fox, this.animal, this.partner, loveCause, this.animal.breedItem, experience);
+                if (entityBreedEvent.isCancelled()) {
+                    this.animal.resetLove();
+                    this.partner.resetLove();
+                    return;
+                }
+                experience = entityBreedEvent.getExperience();
+                // CraftBukkit end - call EntityBreedEvent
+
                 if (serverPlayer != null) {
                     serverPlayer.awardStat(Stats.ANIMALS_BRED);
                     CriteriaTriggers.BRED_ANIMALS.trigger(serverPlayer, this.animal, this.partner, fox);
@@ -885,14 +_,12 @@
                 this.partner.setAge(6000);
                 this.animal.resetLove();
                 this.partner.resetLove();
-                fox.setAge(-24000);
-                fox.snapTo(this.animal.getX(), this.animal.getY(), this.animal.getZ(), 0.0F, 0.0F);
-                serverLevel.addFreshEntityWithPassengers(fox);
+                serverLevel.addFreshEntityWithPassengers(fox, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason
                 this.level.broadcastEntityEvent(this.animal, (byte)18);
-                if (serverLevel.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) {
+                if (experience > 0 && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) { // Paper - call EntityBreedEvent
                     this.level
                         .addFreshEntity(
-                            new ExperienceOrb(this.level, this.animal.getX(), this.animal.getY(), this.animal.getZ(), this.animal.getRandom().nextInt(7) + 1)
+                            new ExperienceOrb(this.level, this.animal.getX(), this.animal.getY(), this.animal.getZ(), experience, org.bukkit.entity.ExperienceOrb.SpawnReason.BREED, loveCause, fox) // Paper - call EntityBreedEvent, add spawn context
                         );
                 }
             }
@@ -956,6 +_,7 @@
         private void pickSweetBerries(BlockState state) {
             int ageValue = state.getValue(SweetBerryBushBlock.AGE);
             state.setValue(SweetBerryBushBlock.AGE, 1);
+            if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(Fox.this, this.blockPos, state.setValue(SweetBerryBushBlock.AGE, 1))) return; // CraftBukkit - call EntityChangeBlockEvent
             int i = 1 + Fox.this.level().random.nextInt(2) + (ageValue == 3 ? 1 : 0);
             ItemStack itemBySlot = Fox.this.getItemBySlot(EquipmentSlot.MAINHAND);
             if (itemBySlot.isEmpty()) {
