--- a/net/minecraft/world/entity/animal/frog/Tadpole.java
+++ b/net/minecraft/world/entity/animal/frog/Tadpole.java
@@ -19,6 +19,7 @@
 import net.minecraft.world.entity.EntityType;
 import net.minecraft.world.entity.Mob;
 import net.minecraft.world.entity.MobSpawnType;
+import net.minecraft.world.entity.SpawnGroupData;
 import net.minecraft.world.entity.ai.Brain;
 import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
 import net.minecraft.world.entity.ai.attributes.Attributes;
@@ -36,29 +37,20 @@
 import net.minecraft.world.item.Items;
 import net.minecraft.world.item.component.CustomData;
 import net.minecraft.world.level.Level;
+// CraftBukkit start
+import org.bukkit.event.entity.EntityRemoveEvent;
+// CraftBukkit end
 
 public class Tadpole extends AbstractFish {
+
     @VisibleForTesting
     public static int ticksToBeFrog = Math.abs(-24000);
     public static final float HITBOX_WIDTH = 0.4F;
     public static final float HITBOX_HEIGHT = 0.3F;
     public int age;
-    protected static final ImmutableList<SensorType<? extends Sensor<? super Tadpole>>> SENSOR_TYPES = ImmutableList.of(
-        SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.HURT_BY, SensorType.FROG_TEMPTATIONS
-    );
-    protected static final ImmutableList<MemoryModuleType<?>> MEMORY_TYPES = ImmutableList.of(
-        MemoryModuleType.LOOK_TARGET,
-        MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES,
-        MemoryModuleType.WALK_TARGET,
-        MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE,
-        MemoryModuleType.PATH,
-        MemoryModuleType.NEAREST_VISIBLE_ADULT,
-        MemoryModuleType.TEMPTATION_COOLDOWN_TICKS,
-        MemoryModuleType.IS_TEMPTED,
-        MemoryModuleType.TEMPTING_PLAYER,
-        MemoryModuleType.BREED_TARGET,
-        MemoryModuleType.IS_PANICKING
-    );
+    protected static final ImmutableList<SensorType<? extends Sensor<? super Tadpole>>> SENSOR_TYPES = ImmutableList.of(SensorType.NEAREST_LIVING_ENTITIES, SensorType.NEAREST_PLAYERS, SensorType.HURT_BY, SensorType.FROG_TEMPTATIONS);
+    protected static final ImmutableList<MemoryModuleType<?>> MEMORY_TYPES = ImmutableList.of(MemoryModuleType.LOOK_TARGET, MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES, MemoryModuleType.WALK_TARGET, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.PATH, MemoryModuleType.NEAREST_VISIBLE_ADULT, MemoryModuleType.TEMPTATION_COOLDOWN_TICKS, MemoryModuleType.IS_TEMPTED, MemoryModuleType.TEMPTING_PLAYER, MemoryModuleType.BREED_TARGET, MemoryModuleType.IS_PANICKING);
+    public boolean ageLocked; // Paper
 
     public Tadpole(EntityType<? extends AbstractFish> type, Level world) {
         super(type, world);
@@ -73,7 +65,7 @@
 
     @Override
     protected Brain.Provider<Tadpole> brainProvider() {
-        return Brain.provider(MEMORY_TYPES, SENSOR_TYPES);
+        return Brain.provider(Tadpole.MEMORY_TYPES, Tadpole.SENSOR_TYPES);
     }
 
     @Override
@@ -83,7 +75,7 @@
 
     @Override
     public Brain<Tadpole> getBrain() {
-        return (Brain<Tadpole>)super.getBrain();
+        return (Brain<Tadpole>) super.getBrain(); // CraftBukkit - decompile error
     }
 
     @Override
@@ -94,7 +86,7 @@
     @Override
     protected void customServerAiStep() {
         this.level().getProfiler().push("tadpoleBrain");
-        this.getBrain().tick((ServerLevel)this.level(), this);
+        this.getBrain().tick((ServerLevel) this.level(), this);
         this.level().getProfiler().pop();
         this.level().getProfiler().push("tadpoleActivityUpdate");
         TadpoleAi.updateActivity(this);
@@ -103,27 +95,30 @@
     }
 
     public static AttributeSupplier.Builder createAttributes() {
-        return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 1.0).add(Attributes.MAX_HEALTH, 6.0);
+        return Mob.createMobAttributes().add(Attributes.MOVEMENT_SPEED, 1.0D).add(Attributes.MAX_HEALTH, 6.0D);
     }
 
     @Override
     public void aiStep() {
         super.aiStep();
-        if (!this.level().isClientSide) {
+        if (!this.level().isClientSide && !this.ageLocked) { // Paper
             this.setAge(this.age + 1);
         }
+
     }
 
     @Override
     public void addAdditionalSaveData(CompoundTag nbt) {
         super.addAdditionalSaveData(nbt);
         nbt.putInt("Age", this.age);
+        nbt.putBoolean("AgeLocked", this.ageLocked); // Paper
     }
 
     @Override
     public void readAdditionalSaveData(CompoundTag nbt) {
         super.readAdditionalSaveData(nbt);
         this.setAge(nbt.getInt("Age"));
+        this.ageLocked = nbt.getBoolean("AgeLocked"); // Paper
     }
 
     @Nullable
@@ -146,12 +141,13 @@
 
     @Override
     public InteractionResult mobInteract(Player player, InteractionHand hand) {
-        ItemStack itemStack = player.getItemInHand(hand);
-        if (this.isFood(itemStack)) {
-            this.feed(player, itemStack);
+        ItemStack itemstack = player.getItemInHand(hand);
+
+        if (this.isFood(itemstack)) {
+            this.feed(player, itemstack);
             return InteractionResult.sidedSuccess(this.level().isClientSide);
         } else {
-            return Bucketable.bucketMobPickup(player, hand, this).orElse(super.mobInteract(player, hand));
+            return (InteractionResult) Bucketable.bucketMobPickup(player, hand, this).orElse(super.mobInteract(player, hand));
         }
     }
 
@@ -167,13 +163,15 @@
     }
 
     @Override
-    public void setFromBucket(boolean fromBucket) {
-    }
+    public void setFromBucket(boolean fromBucket) {}
 
     @Override
     public void saveToBucketTag(ItemStack stack) {
         Bucketable.saveDefaultDataToBucketTag(this, stack);
-        CustomData.update(DataComponents.BUCKET_ENTITY_DATA, stack, compoundTag -> compoundTag.putInt("Age", this.getAge()));
+        CustomData.update(DataComponents.BUCKET_ENTITY_DATA, stack, (nbttagcompound) -> {
+            nbttagcompound.putInt("Age", this.getAge());
+            nbttagcompound.putBoolean("AgeLocked", this.ageLocked); // Paper
+        });
     }
 
     @Override
@@ -182,6 +180,8 @@
         if (nbt.contains("Age")) {
             this.setAge(nbt.getInt("Age"));
         }
+
+        this.ageLocked = nbt.getBoolean("AgeLocked"); // Paper
     }
 
     @Override
@@ -201,7 +201,7 @@
     private void feed(Player player, ItemStack stack) {
         this.usePlayerItem(player, stack);
         this.ageUp(AgeableMob.getSpeedUpSecondsWhenFeeding(this.getTicksLeftUntilAdult()));
-        this.level().addParticle(ParticleTypes.HAPPY_VILLAGER, this.getRandomX(1.0), this.getRandomY() + 0.5, this.getRandomZ(1.0), 0.0, 0.0, 0.0);
+        this.level().addParticle(ParticleTypes.HAPPY_VILLAGER, this.getRandomX(1.0D), this.getRandomY() + 0.5D, this.getRandomZ(1.0D), 0.0D, 0.0D, 0.0D);
     }
 
     private void usePlayerItem(Player player, ItemStack stack) {
@@ -213,22 +213,27 @@
     }
 
     private void ageUp(int seconds) {
+        if (this.ageLocked) return; // Paper
         this.setAge(this.age + seconds * 20);
     }
 
     private void setAge(int tadpoleAge) {
         this.age = tadpoleAge;
-        if (this.age >= ticksToBeFrog) {
+        if (this.age >= Tadpole.ticksToBeFrog) {
             this.ageUp();
         }
+
     }
 
     private void ageUp() {
-        if (this.level() instanceof ServerLevel serverLevel) {
-            Frog frog = EntityType.FROG.create(this.level());
+        Level world = this.level();
+
+        if (world instanceof ServerLevel worldserver) {
+            Frog frog = (Frog) EntityType.FROG.create(this.level());
+
             if (frog != null) {
                 frog.moveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
-                frog.finalizeSpawn(serverLevel, this.level().getCurrentDifficultyAt(frog.blockPosition()), MobSpawnType.CONVERSION, null);
+                frog.finalizeSpawn(worldserver, this.level().getCurrentDifficultyAt(frog.blockPosition()), MobSpawnType.CONVERSION, (SpawnGroupData) null);
                 frog.setNoAi(this.isNoAi());
                 if (this.hasCustomName()) {
                     frog.setCustomName(this.getCustomName());
@@ -236,16 +241,23 @@
                 }
 
                 frog.setPersistenceRequired();
+                // CraftBukkit start
+                if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTransformEvent(this, frog, org.bukkit.event.entity.EntityTransformEvent.TransformReason.METAMORPHOSIS).isCancelled()) {
+                    this.setAge(0); // Sets the age to 0 for avoid a loop if the event is canceled
+                    return;
+                }
+                // CraftBukkit end
                 frog.fudgePositionAfterSizeChange(this.getDimensions(this.getPose()));
                 this.playSound(SoundEvents.TADPOLE_GROW_UP, 0.15F, 1.0F);
-                serverLevel.addFreshEntityWithPassengers(frog);
-                this.discard();
+                worldserver.addFreshEntityWithPassengers(frog, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.METAMORPHOSIS); // CraftBukkit - add SpawnReason
+                this.discard(EntityRemoveEvent.Cause.TRANSFORMATION); // CraftBukkit - add Bukkit remove cause
             }
         }
+
     }
 
     private int getTicksLeftUntilAdult() {
-        return Math.max(0, ticksToBeFrog - this.age);
+        return Math.max(0, Tadpole.ticksToBeFrog - this.age);
     }
 
     @Override
