From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
Date: Thu, 10 Dec 2020 16:44:54 -0600
Subject: [PATCH] Configurable entity base attributes


diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 20b29275c5b0f1e1c9d97af274abdc2e1ca3d847..034f53ddce67a2f1a8efca09ca20f175cd2fa399 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -150,7 +150,7 @@ import org.bukkit.plugin.PluginManager;
 // CraftBukkit end
 
 public abstract class Entity implements Nameable, EntityAccess, CommandSource, net.minecraft.server.KeyedObject { // Paper
-
+    protected static javax.script.ScriptEngine scriptEngine = new javax.script.ScriptEngineManager().getEngineByName("rhino"); // Purpur
     // CraftBukkit start
     private static final int CURRENT_LEVEL = 2;
     public boolean preserveMotion = true; // Paper - keep initial motion on first setPositionRotation
diff --git a/src/main/java/net/minecraft/world/entity/GlowSquid.java b/src/main/java/net/minecraft/world/entity/GlowSquid.java
index 8f8bc29d847801938e251904b8334b4b31bd21c5..87d01bebbb179eec53323e9e23db011a791660ed 100644
--- a/src/main/java/net/minecraft/world/entity/GlowSquid.java
+++ b/src/main/java/net/minecraft/world/entity/GlowSquid.java
@@ -29,6 +29,11 @@ public class GlowSquid extends Squid {
     public boolean rideableUnderWater() {
         return true;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.glowSquidMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index f508bf68172cd5e86c3f0c01f573514b8e302c02..63a59b0ca02503a677bd12509eee4e4b86799fb4 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -287,6 +287,7 @@ public abstract class LivingEntity extends Entity {
         this.useItem = ItemStack.EMPTY;
         this.lastClimbablePos = Optional.empty();
         this.attributes = new AttributeMap(DefaultAttributes.getSupplier(type));
+        this.initAttributes(); // Purpur
         this.craftAttributes = new CraftAttributeMap(this.attributes); // CraftBukkit
         // CraftBukkit - setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor
         this.entityData.set(LivingEntity.DATA_HEALTH_ID, (float) this.getAttribute(Attributes.MAX_HEALTH).getValue());
@@ -302,6 +303,8 @@ public abstract class LivingEntity extends Entity {
         this.brain = this.makeBrain(new Dynamic(dynamicopsnbt, (net.minecraft.nbt.Tag) dynamicopsnbt.createMap((Map) ImmutableMap.of(dynamicopsnbt.createString("memories"), (net.minecraft.nbt.Tag) dynamicopsnbt.emptyMap()))));
     }
 
+    protected void initAttributes() {}// Purpur
+
     public Brain<?> getBrain() {
         return this.brain;
     }
diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java
index e0a55fdff847180d08db0bc6bc7e19281c594a0c..e749f0f3a35783cada224e4b0c12f294c3245210 100644
--- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java
+++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java
@@ -83,6 +83,18 @@ public class Bat extends AmbientCreature {
             setDeltaMovement(mot.scale(0.9D));
         }
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.batMaxHealth);
+        this.getAttribute(Attributes.FOLLOW_RANGE).setBaseValue(this.level.purpurConfig.batFollowRange);
+        this.getAttribute(Attributes.KNOCKBACK_RESISTANCE).setBaseValue(this.level.purpurConfig.batKnockbackResistance);
+        this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level.purpurConfig.batMovementSpeed);
+        this.getAttribute(Attributes.FLYING_SPEED).setBaseValue(this.level.purpurConfig.batFlyingSpeed);
+        this.getAttribute(Attributes.ARMOR).setBaseValue(this.level.purpurConfig.batArmor);
+        this.getAttribute(Attributes.ARMOR_TOUGHNESS).setBaseValue(this.level.purpurConfig.batArmorToughness);
+        this.getAttribute(Attributes.ATTACK_KNOCKBACK).setBaseValue(this.level.purpurConfig.batAttackKnockback);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java
index 8632a4047776723088b9b9fa27c6e8093fb17801..56f55c4520bec4b4baaa606e47ecf6c0288a104f 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Bee.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java
@@ -209,6 +209,11 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
             setDeltaMovement(mot.scale(0.9D));
         }
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.beeMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java
index 14822b20971b63290a7022f8efe65693c41664d2..1cbbc29ef19c8f1b8f96882213b662cfcb193033 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Cat.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java
@@ -139,6 +139,11 @@ public class Cat extends TamableAnimal {
         setLying(false);
         setRelaxStateOne(false);
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.catMaxHealth);
+    }
     // Purpur end
 
     public ResourceLocation getResourceLocation() {
diff --git a/src/main/java/net/minecraft/world/entity/animal/Chicken.java b/src/main/java/net/minecraft/world/entity/animal/Chicken.java
index 7a5119e007d69adc9a77fbd167a8d19a9c71c6ff..9fe68301b9a6efd703fb9d2215a9cd3b2f017a4e 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Chicken.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Chicken.java
@@ -62,6 +62,11 @@ public class Chicken extends Animal {
     public boolean rideableUnderWater() {
         return level.purpurConfig.chickenRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.chickenMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/Cod.java b/src/main/java/net/minecraft/world/entity/animal/Cod.java
index 5275e4ed3d552d1e164ef580caf6c247ec5fd8c2..b84aa3aa391aab6c7bcb804ec3cad67264a1e3d5 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Cod.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Cod.java
@@ -23,6 +23,11 @@ public class Cod extends AbstractSchoolingFish {
     public boolean rideableUnderWater() {
         return true;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.codMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/Cow.java b/src/main/java/net/minecraft/world/entity/animal/Cow.java
index 1cb5342d63011605b567a4fe16d07de831f04216..bee82d5bf600cbeacfcede600e5606529af1435e 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Cow.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Cow.java
@@ -50,6 +50,11 @@ public class Cow extends Animal {
     public boolean rideableUnderWater() {
         return level.purpurConfig.cowRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.cowMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
index 2300abc4e80449e6b92992f6fb8cfe8e99dea351..c372d47a929e06c8cfb0df86cf4e9bfee4d4b300 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Dolphin.java
@@ -158,6 +158,11 @@ public class Dolphin extends WaterAnimal {
         }
         return false;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.dolphinMaxHealth);
+    }
     // Purpur end
 
     @Nullable
diff --git a/src/main/java/net/minecraft/world/entity/animal/Fox.java b/src/main/java/net/minecraft/world/entity/animal/Fox.java
index ee9dfc609cc9e4a94dca7803e51bb456c1e63389..80749df5d00df415a1b9e7c0e8586625b6453ffb 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Fox.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Fox.java
@@ -169,6 +169,11 @@ public class Fox extends Animal {
         super.onDismount(rider);
         setCanPickUpLoot(true);
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.foxMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
index 63b739c86d2ea22aa9a610796ad2f208b0db289e..f4e983da7206923ee0b0f984e65a6c2b3a6a8aeb 100644
--- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
@@ -80,6 +80,11 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
     public boolean rideableUnderWater() {
         return level.purpurConfig.ironGolemRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.ironGolemMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
index 81a393f29643a0cf3478ca8ff640c21153d1514b..b9aeb196f61582801416dab21c12d50e2e68d39e 100644
--- a/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
+++ b/src/main/java/net/minecraft/world/entity/animal/MushroomCow.java
@@ -71,6 +71,11 @@ public class MushroomCow extends Cow implements Shearable {
     public boolean rideableUnderWater() {
         return level.purpurConfig.mooshroomRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.mooshroomMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
index 2a8f6d920370b4087895e9f0098a0f41d9b1e152..de0247899f0bf785a94e39e170c8e2fc8a58b590 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Ocelot.java
@@ -78,6 +78,11 @@ public class Ocelot extends Animal {
     public boolean rideableUnderWater() {
         return level.purpurConfig.ocelotRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.ocelotMaxHealth);
+    }
     // Purpur end
 
     public boolean isTrusting() {
diff --git a/src/main/java/net/minecraft/world/entity/animal/Panda.java b/src/main/java/net/minecraft/world/entity/animal/Panda.java
index fa9dbb22dd13c650ef08af6030d60c5fc8ee8044..d0c10a6d465e2ac9a1bec04080a23dfb081e9f3e 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Panda.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Panda.java
@@ -127,6 +127,12 @@ public class Panda extends Animal {
         eat(false);
         setOnBack(false);
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.pandaMaxHealth);
+        setAttributes();
+    }
     // Purpur end
 
     @Override
@@ -617,7 +623,10 @@ public class Panda extends Animal {
 
     public void setAttributes() {
         if (this.isWeak()) {
-            this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(10.0D);
+            // Purpur start
+            net.minecraft.world.entity.ai.attributes.AttributeInstance maxHealth = this.getAttribute(Attributes.MAX_HEALTH);
+            maxHealth.setBaseValue(maxHealth.getValue() / 2);
+            // Purpur end
         }
 
         if (this.isLazy()) {
diff --git a/src/main/java/net/minecraft/world/entity/animal/Parrot.java b/src/main/java/net/minecraft/world/entity/animal/Parrot.java
index b881f6e948b21ba851913d9f244d74d886f2cc3a..1e38578a9a1531433866e2de7aa0ce5237da3b6b 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Parrot.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Parrot.java
@@ -176,6 +176,11 @@ public class Parrot extends ShoulderRidingEntity implements FlyingAnimal {
             setDeltaMovement(mot.scale(0.9D));
         }
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.parrotMaxHealth);
+    }
     // Purpur end
 
     @Nullable
diff --git a/src/main/java/net/minecraft/world/entity/animal/Pig.java b/src/main/java/net/minecraft/world/entity/animal/Pig.java
index 80c8ceda47bf8b3ff370f89a61aa01869d606ee6..f4a099e691dce3c57069e76d67859161b459098e 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Pig.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Pig.java
@@ -75,6 +75,11 @@ public class Pig extends Animal implements ItemSteerable, Saddleable {
     public boolean rideableUnderWater() {
         return level.purpurConfig.pigRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.pigMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
index dc6bc8f10d147cb1d0e5c69b6f6df63b0e8d4531..18780fb268cabb47bb0deb84c44520831c1a762b 100644
--- a/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
+++ b/src/main/java/net/minecraft/world/entity/animal/PolarBear.java
@@ -88,6 +88,11 @@ public class PolarBear extends Animal implements NeutralMob {
         }
         return false;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.polarBearMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
index 42446fd8ba1d78cd5992bc6c2e5259da361eb716..bb1a2d79e0034ca3d8b18cb578049deecd234a05 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Pufferfish.java
@@ -55,6 +55,11 @@ public class Pufferfish extends AbstractFish {
     public boolean rideableUnderWater() {
         return true;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.pufferfishMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
index 29551a3b87f06c5876de5fda80615acf6c1f4764..587feda351efae19407cb9f23c6c1d42d5ed0cc9 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Rabbit.java
@@ -128,6 +128,11 @@ public class Rabbit extends Animal {
         }
         wasOnGround = onGround;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.rabbitMaxHealth);
+    }
     // Purpur end
 
     // CraftBukkit start - code from constructor
diff --git a/src/main/java/net/minecraft/world/entity/animal/Salmon.java b/src/main/java/net/minecraft/world/entity/animal/Salmon.java
index 02a9ce0b98bab47d57b51cdfb2194f0e72a9f705..002564221cc53b872001bdb8a76d9ca059212401 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Salmon.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Salmon.java
@@ -23,6 +23,11 @@ public class Salmon extends AbstractSchoolingFish {
     public boolean rideableUnderWater() {
         return true;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.salmonMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/Sheep.java b/src/main/java/net/minecraft/world/entity/animal/Sheep.java
index 5890bf8aa714dc219059bca4950a1b5b1882dd3a..497be3182c72b5a0f3bc42088c4168702119b527 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Sheep.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Sheep.java
@@ -126,6 +126,11 @@ public class Sheep extends Animal implements Shearable {
     public boolean rideableUnderWater() {
         return level.purpurConfig.sheepRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.sheepMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
index 229b3cc63d2ec0bc5e5797d45efd9ca45f3ea82a..fcf3092ef1863f62d9e76a77c0299ce1495ca1b7 100644
--- a/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
+++ b/src/main/java/net/minecraft/world/entity/animal/SnowGolem.java
@@ -63,6 +63,11 @@ public class SnowGolem extends AbstractGolem implements Shearable, RangedAttackM
     public boolean rideableUnderWater() {
         return level.purpurConfig.snowGolemRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.snowGolemMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/Squid.java b/src/main/java/net/minecraft/world/entity/animal/Squid.java
index 151bb142c553d733367a92b39b430b536e569480..f96def2ebdf114823c322c2d4318d039e20eab97 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Squid.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Squid.java
@@ -74,6 +74,11 @@ public class Squid extends WaterAnimal {
         vector.setX(cos * x - sine * z);
         vector.setZ(sine * x + cos * z);
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.squidMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java
index 5f61fcffebf4d853711a38d1f315f3def25e31a7..fcd3166a7c818ef089ccf2a687596bef51ad14a6 100644
--- a/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java
+++ b/src/main/java/net/minecraft/world/entity/animal/TropicalFish.java
@@ -53,6 +53,11 @@ public class TropicalFish extends AbstractSchoolingFish {
     public boolean rideableUnderWater() {
         return true;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.tropicalFishMaxHealth);
+    }
     // Purpur end
 
     public static String getPredefinedName(int variant) {
diff --git a/src/main/java/net/minecraft/world/entity/animal/Turtle.java b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
index 631539a752a038926355c23aeb160af64f363a61..66c01b8300bc09ace27e4d1a30ee9274c69fcc9a 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Turtle.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Turtle.java
@@ -100,6 +100,11 @@ public class Turtle extends Animal {
     public boolean rideableUnderWater() {
         return level.purpurConfig.turtleRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.turtleMaxHealth);
+    }
     // Purpur end
 
     public void setHomePos(BlockPos pos) {
diff --git a/src/main/java/net/minecraft/world/entity/animal/Wolf.java b/src/main/java/net/minecraft/world/entity/animal/Wolf.java
index bca3300e06d6eb0c6acdfb11d715a1e8447c9198..ae416b70109c959980b3115da6e97df1610996ca 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Wolf.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Wolf.java
@@ -108,6 +108,11 @@ public class Wolf extends TamableAnimal implements NeutralMob {
         super.onMount(rider);
         setInSittingPose(false);
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.wolfMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java
index f0f1a286011bded016897cc8a0783a8a49a43b33..2542ed327e03c37a3225fac10d4a6659c35512ca 100644
--- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java
+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java
@@ -105,6 +105,11 @@ public class Axolotl extends Animal implements LerpingModel, Bucketable {
     protected void registerGoals() {
         this.goalSelector.addGoal(0, new net.pl3x.purpur.entity.ai.HasRider(this)); // Purpur
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.axolotlMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
index cb3b6c1866a4c5c05bc6c7f0d4ac03cd747271fe..0ee42f45a9877ffd6df80e602c3cb5affd43d285 100644
--- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
@@ -82,6 +82,11 @@ public class Goat extends Animal {
     public boolean rideableUnderWater() {
         return level.purpurConfig.goatRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.goatMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java
index fbd96e516ac34f874b0cca2da9076120e29254fb..54401524a2be27e18d5e616e9bb8481007c7af88 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/AbstractHorse.java
@@ -124,6 +124,32 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
     public boolean isRidable() {
         return false; // vanilla handles
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.generateRandomMaxHealth());
+        this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.generateRandomSpeed());
+        this.getAttribute(Attributes.JUMP_STRENGTH).setBaseValue(this.generateRandomJumpStrength());
+    }
+
+    protected double generateRandomMaxHealth(double min, double max) {
+        if (min == max) return min;
+        int diff = Mth.floor(max - min);
+        double base = max - diff;
+        int first = Mth.floor((double) diff / 2);
+        int rest = diff - first;
+        return base + random.nextInt(first + 1) + random.nextInt(rest + 1);
+    }
+
+    protected double generateRandomJumpStrength(double min, double max) {
+        if (min == max) return min;
+        return min + (max - min) * this.random.nextDouble();
+    }
+
+    protected double generateRandomSpeed(double min, double max) {
+        if (min == max) return min;
+        return min + (max - min) * this.random.nextDouble();
+    }
     // Purpur end
 
     @Override
@@ -1134,7 +1160,7 @@ public abstract class AbstractHorse extends Animal implements ContainerListener,
             entityData = new AgeableMob.AgeableMobGroupData(0.2F);
         }
 
-        this.randomizeAttributes();
+        //this.randomizeAttributes(); // Purpur - replaced by initAttributes()
         return super.finalizeSpawn(world, difficulty, spawnReason, (SpawnGroupData) entityData, entityNbt);
     }
 
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java
index e5b13558f45966bf70593931922ca73f4a66c66a..b4944ab2191241dc604ad21670360e5204bc111b 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Donkey.java
@@ -20,6 +20,21 @@ public class Donkey extends AbstractChestedHorse {
     public boolean rideableUnderWater() {
         return level.purpurConfig.donkeyRidableInWater;
     }
+
+    @Override
+    public float generateRandomMaxHealth() {
+        return (float) generateRandomMaxHealth(this.level.purpurConfig.donkeyMaxHealthMin, this.level.purpurConfig.donkeyMaxHealthMax);
+    }
+
+    @Override
+    public double generateRandomJumpStrength() {
+        return generateRandomJumpStrength(this.level.purpurConfig.donkeyJumpStrengthMin, this.level.purpurConfig.donkeyJumpStrengthMax);
+    }
+
+    @Override
+    public double generateRandomSpeed() {
+        return generateRandomSpeed(this.level.purpurConfig.donkeyMovementSpeedMin, this.level.purpurConfig.donkeyMovementSpeedMax);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java
index c7b0d6a987644e0b589c143b0b6b68053be04f37..6c6d2ac7551ae24dec88bd9e6bce7aeafd900e8c 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Horse.java
@@ -44,6 +44,21 @@ public class Horse extends AbstractHorse {
     public boolean rideableUnderWater() {
         return level.purpurConfig.horseRidableInWater;
     }
+
+    @Override
+    public float generateRandomMaxHealth() {
+        return (float) generateRandomMaxHealth(this.level.purpurConfig.horseMaxHealthMin, this.level.purpurConfig.horseMaxHealthMax);
+    }
+
+    @Override
+    public double generateRandomJumpStrength() {
+        return generateRandomJumpStrength(this.level.purpurConfig.horseJumpStrengthMin, this.level.purpurConfig.horseJumpStrengthMax);
+    }
+
+    @Override
+    public double generateRandomSpeed() {
+        return generateRandomSpeed(this.level.purpurConfig.horseMovementSpeedMin, this.level.purpurConfig.horseMovementSpeedMax);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java
index d336e2c63b39b22f9f3a7a84ee059c2d9d71239c..d664be25c2c2c527a44fa939e68e84d60effc144 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Llama.java
@@ -107,6 +107,21 @@ public class Llama extends AbstractChestedHorse implements RangedAttackMob {
     public boolean isSaddled() {
         return super.isSaddled() || (isTamed() && getSwag() != null);
     }
+
+    @Override
+    public float generateRandomMaxHealth() {
+        return (float) generateRandomMaxHealth(this.level.purpurConfig.llamaMaxHealthMin, this.level.purpurConfig.llamaMaxHealthMax);
+    }
+
+    @Override
+    public double generateRandomJumpStrength() {
+        return generateRandomJumpStrength(this.level.purpurConfig.llamaJumpStrengthMin, this.level.purpurConfig.llamaJumpStrengthMax);
+    }
+
+    @Override
+    public double generateRandomSpeed() {
+        return generateRandomSpeed(this.level.purpurConfig.llamaMovementSpeedMin, this.level.purpurConfig.llamaMovementSpeedMax);
+    }
     // Purpur end
 
     public boolean isTraderLlama() {
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java b/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java
index 40cc7e20913938d6e30e6bca9f4ad70fb5e9435b..da3d0b431c14f1a0794b9445236af1f548139013 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/Mule.java
@@ -19,6 +19,21 @@ public class Mule extends AbstractChestedHorse {
     public boolean rideableUnderWater() {
         return level.purpurConfig.muleRidableInWater;
     }
+
+    @Override
+    public float generateRandomMaxHealth() {
+        return (float) generateRandomMaxHealth(this.level.purpurConfig.muleMaxHealthMin, this.level.purpurConfig.muleMaxHealthMax);
+    }
+
+    @Override
+    public double generateRandomJumpStrength() {
+        return generateRandomJumpStrength(this.level.purpurConfig.muleJumpStrengthMin, this.level.purpurConfig.muleJumpStrengthMax);
+    }
+
+    @Override
+    public double generateRandomSpeed() {
+        return generateRandomSpeed(this.level.purpurConfig.muleMovementSpeedMin, this.level.purpurConfig.muleMovementSpeedMax);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java
index d4057c300d39eca4ff2e11791ce5ba7993d9b66e..ffc36728e0fc90c4e37051adc643a2b8af8077ff 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/SkeletonHorse.java
@@ -34,6 +34,21 @@ public class SkeletonHorse extends AbstractHorse {
     public boolean isTamed() {
         return true;
     }
+
+    @Override
+    public float generateRandomMaxHealth() {
+        return (float) generateRandomMaxHealth(this.level.purpurConfig.skeletonHorseMaxHealthMin, this.level.purpurConfig.skeletonHorseMaxHealthMax);
+    }
+
+    @Override
+    public double generateRandomJumpStrength() {
+        return generateRandomJumpStrength(this.level.purpurConfig.skeletonHorseJumpStrengthMin, this.level.purpurConfig.skeletonHorseJumpStrengthMax);
+    }
+
+    @Override
+    public double generateRandomSpeed() {
+        return generateRandomSpeed(this.level.purpurConfig.skeletonHorseMovementSpeedMin, this.level.purpurConfig.skeletonHorseMovementSpeedMax);
+    }
     // Purpur end
 
     public static AttributeSupplier.Builder createAttributes() {
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java b/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java
index ff252d9ca75b90ab7606f63aa5f89b6230e33a36..c4eb99466c00c6cf1d60c7fa387f8b65efdac912 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/TraderLlama.java
@@ -42,6 +42,21 @@ public class TraderLlama extends Llama {
     public boolean isSaddled() {
         return super.isSaddled() || isTamed();
     }
+
+    @Override
+    public float generateRandomMaxHealth() {
+        return (float) generateRandomMaxHealth(this.level.purpurConfig.traderLlamaMaxHealthMin, this.level.purpurConfig.traderLlamaMaxHealthMax);
+    }
+
+    @Override
+    public double generateRandomJumpStrength() {
+        return generateRandomJumpStrength(this.level.purpurConfig.traderLlamaJumpStrengthMin, this.level.purpurConfig.traderLlamaJumpStrengthMax);
+    }
+
+    @Override
+    public double generateRandomSpeed() {
+        return generateRandomSpeed(this.level.purpurConfig.traderLlamaMovementSpeedMin, this.level.purpurConfig.traderLlamaMovementSpeedMax);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java b/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java
index d328e36015b6b7d6a9e093fbe232eb5ecda46d96..6ca7b168a1ea26102922d9377e52662f16c1e725 100644
--- a/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java
+++ b/src/main/java/net/minecraft/world/entity/animal/horse/ZombieHorse.java
@@ -32,6 +32,21 @@ public class ZombieHorse extends AbstractHorse {
     public boolean isTamed() {
         return true;
     }
+
+    @Override
+    public float generateRandomMaxHealth() {
+        return (float) generateRandomMaxHealth(this.level.purpurConfig.zombieHorseMaxHealthMin, this.level.purpurConfig.zombieHorseMaxHealthMax);
+    }
+
+    @Override
+    public double generateRandomJumpStrength() {
+        return generateRandomJumpStrength(this.level.purpurConfig.zombieHorseJumpStrengthMin, this.level.purpurConfig.zombieHorseJumpStrengthMax);
+    }
+
+    @Override
+    public double generateRandomSpeed() {
+        return generateRandomSpeed(this.level.purpurConfig.zombieHorseMovementSpeedMin, this.level.purpurConfig.zombieHorseMovementSpeedMax);
+    }
     // Purpur end
 
     public static AttributeSupplier.Builder createAttributes() {
diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
index 5df112f87fba042f13f615a22a5c6f850b779bf7..d3caa18f5de2d96eae691655dd13e83f82c61e0c 100644
--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
@@ -154,6 +154,11 @@ public class EnderDragon extends Mob implements Enemy {
     public double getMaxY() {
         return level.purpurConfig.enderDragonMaxY;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.enderDragonMaxHealth);
+    }
     // Purpur end
 
     public static AttributeSupplier.Builder createAttributes() {
diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
index fdc9068de403e98e18af11d4d6c5b708d2cc3c52..f52420f27f51d9dbf214f96a0530c0f17f2bc5fc 100644
--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
@@ -196,6 +196,11 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
         skull.setPosRaw(headX, headY, headZ);
         level.addFreshEntity(skull);
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.witherMaxHealth);
+    }
     // Purpur end
 
     @Override
@@ -401,7 +406,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
 
             this.setInvulnerableTicks(i);
             if (this.tickCount % 10 == 0) {
-                this.heal(10.0F, EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit
+                this.heal(this.getMaxHealth() / 33, EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit // Purpur
             }
 
         } else {
diff --git a/src/main/java/net/minecraft/world/entity/monster/Blaze.java b/src/main/java/net/minecraft/world/entity/monster/Blaze.java
index a5294012d432d5d1fce570a1eb1b1716a24522de..83f9a1139a501135a89a758993c7ff209b5b92d5 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Blaze.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Blaze.java
@@ -67,6 +67,11 @@ public class Blaze extends Monster {
             setDeltaMovement(mot.scale(0.9D));
         }
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.blazeMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java
index dd05c5f35d78d1385f793f4e34333ff242d136e1..45b53f11b52cc161775fcaa18567e4a74c5e71b8 100644
--- a/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java
+++ b/src/main/java/net/minecraft/world/entity/monster/CaveSpider.java
@@ -38,6 +38,11 @@ public class CaveSpider extends Spider {
     public boolean rideableUnderWater() {
         return level.purpurConfig.caveSpiderRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.caveSpiderMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Creeper.java b/src/main/java/net/minecraft/world/entity/monster/Creeper.java
index 1bf65519883585569a1acc780554505dc5d6c30b..b71a6af3c2d1ca2a946b23efb11b0e794840cf25 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Creeper.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Creeper.java
@@ -129,6 +129,11 @@ public class Creeper extends Monster implements PowerableMob {
         }
         return getForwardMot() == 0 && getStrafeMot() == 0; // do not jump if standing still
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.creeperMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Drowned.java b/src/main/java/net/minecraft/world/entity/monster/Drowned.java
index ace909fab2334105eabe0593aba47736e3f67451..591dfcd091c1c4611aeb17dfc5ee493be8131b50 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java
@@ -79,6 +79,16 @@ public class Drowned extends Zombie implements RangedAttackMob {
     public boolean rideableUnderWater() {
         return level.purpurConfig.drownedRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.drownedMaxHealth);
+    }
+
+    @Override
+    protected void randomizeReinforcementsChance() {
+        this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level.purpurConfig.drownedSpawnReinforcements);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java
index 5f8233c1be6f92bcf58c5c5db360b2660c9439ad..55f67061aa4a63105059bfecfb153a579472adc0 100644
--- a/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java
+++ b/src/main/java/net/minecraft/world/entity/monster/ElderGuardian.java
@@ -40,6 +40,11 @@ public class ElderGuardian extends Guardian {
     public boolean rideableUnderWater() {
         return true;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.elderGuardianMaxHealth);
+    }
     // Purpur end
 
     public static AttributeSupplier.Builder createAttributes() {
diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
index f505cc4fc7945b2b462bc24ee3ecc76995072e14..b92a83cfd15cedf7cd68c26193e65c5f24d9a621 100644
--- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
+++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java
@@ -93,6 +93,11 @@ public class EnderMan extends Monster implements NeutralMob {
     public boolean rideableUnderWater() {
         return level.purpurConfig.endermanRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.endermanMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Endermite.java b/src/main/java/net/minecraft/world/entity/monster/Endermite.java
index 1f315cc57f6365a782ca4e645b0dcfbe5485138a..d9e2bc9d37968430910c8de33390004ba5db22a6 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Endermite.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Endermite.java
@@ -46,6 +46,11 @@ public class Endermite extends Monster {
     public boolean rideableUnderWater() {
         return level.purpurConfig.endermiteRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.endermiteMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Evoker.java b/src/main/java/net/minecraft/world/entity/monster/Evoker.java
index 053374baf3ec7730bcddf072ddf98e3176656435..0ae0a49da681c6603d6f1504ee58f99b8f345e44 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Evoker.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Evoker.java
@@ -56,6 +56,11 @@ public class Evoker extends SpellcasterIllager {
     public boolean rideableUnderWater() {
         return level.purpurConfig.evokerRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.evokerMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Ghast.java b/src/main/java/net/minecraft/world/entity/monster/Ghast.java
index bb1a6f6847d0e459eb26a029d8a1f5646168a422..e3f479bcc3bd9e28fc4a6ef2f390f79a8e361a3f 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Ghast.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Ghast.java
@@ -71,6 +71,11 @@ public class Ghast extends FlyingMob implements Enemy {
             setDeltaMovement(mot.scale(0.9D));
         }
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.ghastMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Giant.java b/src/main/java/net/minecraft/world/entity/monster/Giant.java
index d95b110a025d48e7ab18f172e455593740e45252..c67f33f2e2e0ff5c2a85782185103325a6bf4535 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Giant.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Giant.java
@@ -30,6 +30,13 @@ public class Giant extends Monster {
         this.goalSelector.addGoal(0, new net.pl3x.purpur.entity.ai.HasRider(this));
         this.targetSelector.addGoal(0, new net.pl3x.purpur.entity.ai.HasRider(this));
     }
+
+    @Override
+    protected void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.giantMaxHealth);
+        this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level.purpurConfig.giantMovementSpeed);
+        this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue(this.level.purpurConfig.giantAttackDamage);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Guardian.java b/src/main/java/net/minecraft/world/entity/monster/Guardian.java
index 20b7104b7fd38b3c6c05a68a1472c6006814cb81..2a25dbf9449ebc63587f24346ee92bd3115ad4d6 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Guardian.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Guardian.java
@@ -87,6 +87,11 @@ public class Guardian extends Monster {
     public boolean rideableUnderWater() {
         return true;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.guardianMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Husk.java b/src/main/java/net/minecraft/world/entity/monster/Husk.java
index 75397a8e1ae8d48bf07f5c0409536acd54851c48..464e062bba03a17c9281b3dfb8fa9193c1b81150 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Husk.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Husk.java
@@ -32,6 +32,16 @@ public class Husk extends Zombie {
     public boolean rideableUnderWater() {
         return level.purpurConfig.huskRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.huskMaxHealth);
+    }
+
+    @Override
+    protected void randomizeReinforcementsChance() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level.purpurConfig.huskSpawnReinforcements);
+    }
     // Purpur end
 
     public static boolean checkHuskSpawnRules(EntityType<Husk> type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) {
diff --git a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java
index 5f57c14a7ba03af9432a839e6caed47286638b2c..c4c239ab51c48a2b9c12e547dc7e125955911794 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Illusioner.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Illusioner.java
@@ -69,6 +69,13 @@ public class Illusioner extends SpellcasterIllager implements RangedAttackMob {
     public boolean rideableUnderWater() {
         return level.purpurConfig.illusionerRidableInWater;
     }
+
+    @Override
+    protected void initAttributes() {
+        this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.level.purpurConfig.illusionerMovementSpeed);
+        this.getAttribute(Attributes.FOLLOW_RANGE).setBaseValue(this.level.purpurConfig.illusionerFollowRange);
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.illusionerMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java
index 7d39f25734f2c52b11931e141524acd2c244c2c5..87aae8fa18704e58f2b1fc0abd1a98987715123f 100644
--- a/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java
+++ b/src/main/java/net/minecraft/world/entity/monster/MagmaCube.java
@@ -42,6 +42,16 @@ public class MagmaCube extends Slime {
     public float getJumpPower() {
         return 0.42F * this.getBlockJumpFactor(); // from EntityLiving
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.generateMaxHealth());
+    }
+
+    @Override
+    protected String getMaxHealthEquation() {
+        return level.purpurConfig.magmaCubeMaxHealth;
+    }
     // Purpur end
 
     public static AttributeSupplier.Builder createAttributes() {
diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java
index 61aff98a7b56eab6a43ddc9f07618cbbedb6b77d..5e069ac0c57d2a3a23f6e4483d12ce298d172691 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java
@@ -112,6 +112,11 @@ public class Phantom extends FlyingMob implements Enemy {
         level.addFreshEntity(flames);
         return true;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.phantomMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Pillager.java b/src/main/java/net/minecraft/world/entity/monster/Pillager.java
index 84bd5e7df663f315fd099742195e85508ea25adf..48a1bd4c9c7e36e42797937ea1af3d213f418da4 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Pillager.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Pillager.java
@@ -76,6 +76,11 @@ public class Pillager extends AbstractIllager implements CrossbowAttackMob, Inve
     public boolean rideableUnderWater() {
         return level.purpurConfig.pillagerRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.pillagerMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java
index 32ef769b2b3e3ab42c7a50bf4c8ec7ec548d5e7b..b8c753a574487cdc045ea334229aafd257d6e223 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java
@@ -86,6 +86,11 @@ public class Ravager extends Raider {
         super.onMount(rider);
         getNavigation().stop();
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.ravagerMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Shulker.java b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
index 42635be6183d86978df3f174c74a71691f2bfcdd..d6fdaa9f9033d8b1e8aa9ef0aace387e286ebce9 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Shulker.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Shulker.java
@@ -102,6 +102,11 @@ public class Shulker extends AbstractGolem implements Enemy {
     public boolean rideableUnderWater() {
         return level.purpurConfig.shulkerRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.shulkerMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java
index e66cc79dc61721b31ffb743f68f4388cc499a92d..0d60d6352294fadc1a26579a712b01cf0da681af 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Silverfish.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Silverfish.java
@@ -49,6 +49,11 @@ public class Silverfish extends Monster {
     public boolean rideableUnderWater() {
         return level.purpurConfig.silverfishRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.silverfishMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
index 059a62da29b7ec11e2ff9baf18dde788bec4923c..5a2759e636717920a6b34ddbda34a1106a88714b 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
@@ -35,6 +35,11 @@ public class Skeleton extends AbstractSkeleton {
     public boolean rideableUnderWater() {
         return level.purpurConfig.skeletonRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.skeletonMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java
index 25ce1910a03947ce070b318f57379f0da5ac5db8..7a9654a365a968592d706a13fb46615effcc2399 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Slime.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java
@@ -101,6 +101,30 @@ public class Slime extends Mob implements Enemy {
         }
         return true; // do not jump() in wasd controller, let vanilla controller handle
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.generateMaxHealth());
+    }
+
+    protected String getMaxHealthEquation() {
+        return level.purpurConfig.slimeMaxHealth;
+    }
+
+    protected double generateMaxHealth() {
+        int size = getSize();
+        Double maxHealth = level.purpurConfig.slimeMaxHealthCache.get(size);
+        if (maxHealth == null) {
+            try {
+                scriptEngine.eval("size = " + size);
+                maxHealth = (double) scriptEngine.eval(getMaxHealthEquation());
+            } catch (Exception e) {
+                maxHealth = (double) size * size;
+            }
+            level.purpurConfig.slimeMaxHealthCache.put(size, maxHealth);
+        }
+        return maxHealth;
+    }
     // Purpur end
 
     @Override
@@ -129,7 +153,7 @@ public class Slime extends Mob implements Enemy {
         this.entityData.set(Slime.ID_SIZE, j);
         this.reapplyPosition();
         this.refreshDimensions();
-        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue((double) (j * j));
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(generateMaxHealth()); // Purpur
         this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue((double) (0.2F + 0.1F * (float) j));
         this.getAttribute(Attributes.ATTACK_DAMAGE).setBaseValue((double) j);
         if (heal) {
diff --git a/src/main/java/net/minecraft/world/entity/monster/Spider.java b/src/main/java/net/minecraft/world/entity/monster/Spider.java
index 5874d86ca34200e9ce7d41cf7a80eb0f1f13410c..7ed31ccc9fbb3960f9de24d5893f7fa1f86ab4b8 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Spider.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Spider.java
@@ -61,6 +61,11 @@ public class Spider extends Monster {
     public boolean rideableUnderWater() {
         return level.purpurConfig.spiderRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.spiderMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Stray.java b/src/main/java/net/minecraft/world/entity/monster/Stray.java
index 6a8a0cd09e0bf17c7ecb6e55342b645f111dac22..d2eb323c8957ac4ccdf2ab73d83dbbdeefd6ae53 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Stray.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Stray.java
@@ -31,6 +31,11 @@ public class Stray extends AbstractSkeleton {
     public boolean rideableUnderWater() {
         return level.purpurConfig.strayRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.strayMaxHealth);
+    }
     // Purpur end
 
     public static boolean checkStraySpawnRules(EntityType<Stray> type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) {
diff --git a/src/main/java/net/minecraft/world/entity/monster/Strider.java b/src/main/java/net/minecraft/world/entity/monster/Strider.java
index 6c0af80f47c53e4573efb0c50412d289c0bb5540..2d7b83ce2e0e26ef3976514ec8921a718ccc28bd 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Strider.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java
@@ -107,6 +107,11 @@ public class Strider extends Animal implements ItemSteerable, Saddleable {
     public boolean rideableUnderWater() {
         return level.purpurConfig.striderRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.striderMaxHealth);
+    }
     // Purpur end
 
     public static boolean checkStriderSpawnRules(EntityType<Strider> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) {
diff --git a/src/main/java/net/minecraft/world/entity/monster/Vex.java b/src/main/java/net/minecraft/world/entity/monster/Vex.java
index c914cfc75cc0426c0333a6bb30aab7b9e4c52971..18d650befe5f257b9e8f269e605de70d4201dd5c 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Vex.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Vex.java
@@ -94,6 +94,11 @@ public class Vex extends Monster {
     public boolean causeFallDamage(float fallDistance, float damageMultiplier, DamageSource damageSource) {
         return false; //  no fall damage please
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.vexMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java
index a3b1332a92824255b807adc9a5a1d29569e7073b..76e6ea34db3942e9dd7646ad7ca1259f4387a4d8 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java
@@ -67,6 +67,11 @@ public class Vindicator extends AbstractIllager {
     public boolean rideableUnderWater() {
         return level.purpurConfig.vindicatorRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.vindicatorMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Witch.java b/src/main/java/net/minecraft/world/entity/monster/Witch.java
index e70fb661c62add74cdbe9fc9ef1e3143e96333b2..0876224d15b7b08954ab4348f942d2aaf91aa078 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Witch.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Witch.java
@@ -70,6 +70,11 @@ public class Witch extends Raider implements RangedAttackMob {
     public boolean rideableUnderWater() {
         return level.purpurConfig.witchRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.witchMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
index 4cea4ba127dc036ce5d585323a25bbcf06bd46a6..16d7266c32f5aa5cb980bf07d9d92c41c6405a2f 100644
--- a/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
+++ b/src/main/java/net/minecraft/world/entity/monster/WitherSkeleton.java
@@ -44,6 +44,11 @@ public class WitherSkeleton extends AbstractSkeleton {
     public boolean rideableUnderWater() {
         return level.purpurConfig.witherSkeletonRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.witherSkeletonMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java
index 8fd4e26ebe0527fd8a69b15095dd4091fcdde206..7afc9b83e385d711096db5ea9c6c259fd2387075 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Zoglin.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Zoglin.java
@@ -77,6 +77,11 @@ public class Zoglin extends Monster implements Enemy, HoglinBase {
     public boolean rideableUnderWater() {
         return level.purpurConfig.zoglinRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.zoglinMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
index 6dee348b4e1faec208e5144527ca3df0dab4f96e..c3288e6a5be2c0805067fe14496315afd6156110 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
@@ -117,6 +117,11 @@ public class Zombie extends Monster {
     public boolean rideableUnderWater() {
         return level.purpurConfig.zombieRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.drownedMaxHealth);
+    }
     // Purpur end
 
     @Override
@@ -589,7 +594,7 @@ public class Zombie extends Monster {
     }
 
     protected void randomizeReinforcementsChance() {
-        this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * 0.10000000149011612D);
+        this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level.purpurConfig.zombieSpawnReinforcements); // Purpur
     }
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java
index d487b4cb2da18eac12ec3dc71997cdc1217a85f6..9c60e3b3e62a29d6898c82a4110ffd4d1c7cb5f2 100644
--- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java
+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java
@@ -84,6 +84,16 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder {
     public boolean rideableUnderWater() {
         return level.purpurConfig.zombieVillagerRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.zombieVillagerMaxHealth);
+    }
+
+    @Override
+    protected void randomizeReinforcementsChance() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level.purpurConfig.zombieVillagerSpawnReinforcements);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java
index 708ba2c64a0736a30e477017b46dc711948cfc3d..8464026df1b46ad30301fed4944aa1d3cd39bd9e 100644
--- a/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java
+++ b/src/main/java/net/minecraft/world/entity/monster/ZombifiedPiglin.java
@@ -68,6 +68,11 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
     public boolean rideableUnderWater() {
         return level.purpurConfig.zombifiedPiglinRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.zombifiedPiglinMaxHealth);
+    }
     // Purpur end
 
     @Override
@@ -252,7 +257,7 @@ public class ZombifiedPiglin extends Zombie implements NeutralMob {
 
     @Override
     protected void randomizeReinforcementsChance() {
-        this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(0.0D);
+        this.getAttribute(Attributes.SPAWN_REINFORCEMENTS_CHANCE).setBaseValue(this.random.nextDouble() * this.level.purpurConfig.zombifiedPiglinSpawnReinforcements); // Purpur
     }
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
index 060676e094c425ab11889ee221808e5814d34f43..fb0aa2854f8be22682ee65298080b668c722f447 100644
--- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
@@ -77,6 +77,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
     public boolean rideableUnderWater() {
         return level.purpurConfig.hoglinRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.hoglinMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java
index ace6891fd0e14af5268249b60e14a99c17a2f435..79f075e7d7e5d8312f5a9ed47bea2a558b9491d5 100644
--- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java
+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java
@@ -107,6 +107,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento
     public boolean rideableUnderWater() {
         return level.purpurConfig.piglinRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.piglinMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
index d01a8fe6dd5c84ea5b7370a90c0d57130b27e447..9e66f8f2a92cb7b32cdb2cf749ca273759efa6eb 100644
--- a/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/PiglinBrute.java
@@ -50,6 +50,11 @@ public class PiglinBrute extends AbstractPiglin {
     public boolean rideableUnderWater() {
         return level.purpurConfig.piglinBruteRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.piglinBruteMaxHealth);
+    }
     // Purpur end
 
     public static AttributeSupplier.Builder createAttributes() {
diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java
index 8a37e6b42a50da4822389004123e3b72b33f50b4..682feb6f3c109b0b3034f3942f7dfa73721d359b 100644
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
@@ -172,6 +172,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
     protected void registerGoals() {
         this.goalSelector.addGoal(0, new net.pl3x.purpur.entity.ai.HasRider(this));
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.villagerMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
index d6c31596e21041a124a263054ccb6447829eccdd..d2588a91b55aebdecf8e1644498111ccf84a9dc9 100644
--- a/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
+++ b/src/main/java/net/minecraft/world/entity/npc/WanderingTrader.java
@@ -77,6 +77,11 @@ public class WanderingTrader extends net.minecraft.world.entity.npc.AbstractVill
     public boolean rideableUnderWater() {
         return level.purpurConfig.wanderingTraderRidableInWater;
     }
+
+    @Override
+    public void initAttributes() {
+        this.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.MAX_HEALTH).setBaseValue(this.level.purpurConfig.wanderingTraderMaxHealth);
+    }
     // Purpur end
 
     @Override
diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
index 3f91e7f49a1f00ff21fd65ea443af02729b7cc10..3627b50dd3af5da225004cd3f8158fcbd30d8ea3 100644
--- a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
@@ -103,257 +103,573 @@ public class PurpurWorldConfig {
     }
 
     public boolean axolotlRidable = false;
+    public double axolotlMaxHealth = 14.0D;
     private void axolotlSettings() {
         axolotlRidable = getBoolean("mobs.axolotl.ridable", axolotlRidable);
+        axolotlMaxHealth = getDouble("mobs.axolotl.attributes.max_health", axolotlMaxHealth);
     }
 
     public boolean batRidable = false;
     public boolean batRidableInWater = false;
     public double batMaxY = 256D;
+    public double batMaxHealth = 6.0D;
+    public double batFollowRange = 16.0D;
+    public double batKnockbackResistance = 0.0D;
+    public double batMovementSpeed = 0.6D;
+    public double batFlyingSpeed = 0.6D;
+    public double batArmor = 0.0D;
+    public double batArmorToughness = 0.0D;
+    public double batAttackKnockback = 0.0D;
     private void batSettings() {
         batRidable = getBoolean("mobs.bat.ridable", batRidable);
         batRidableInWater = getBoolean("mobs.bat.ridable-in-water", batRidableInWater);
         batMaxY = getDouble("mobs.bat.ridable-max-y", batMaxY);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.bat.attributes.max-health", batMaxHealth);
+            set("mobs.bat.attributes.max-health", null);
+            set("mobs.bat.attributes.max_health", oldValue);
+        }
+        batMaxHealth = getDouble("mobs.bat.attributes.max_health", batMaxHealth);
     }
 
     public boolean beeRidable = false;
     public boolean beeRidableInWater = false;
     public double beeMaxY = 256D;
+    public double beeMaxHealth = 10.0D;
     private void beeSettings() {
         beeRidable = getBoolean("mobs.bee.ridable", beeRidable);
         beeRidableInWater = getBoolean("mobs.bee.ridable-in-water", beeRidableInWater);
         beeMaxY = getDouble("mobs.bee.ridable-max-y", beeMaxY);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.bee.attributes.max-health", beeMaxHealth);
+            set("mobs.bee.attributes.max-health", null);
+            set("mobs.bee.attributes.max_health", oldValue);
+        }
+        beeMaxHealth = getDouble("mobs.bee.attributes.max_health", beeMaxHealth);
     }
 
     public boolean blazeRidable = false;
     public boolean blazeRidableInWater = false;
     public double blazeMaxY = 256D;
+    public double blazeMaxHealth = 20.0D;
     private void blazeSettings() {
         blazeRidable = getBoolean("mobs.blaze.ridable", blazeRidable);
         blazeRidableInWater = getBoolean("mobs.blaze.ridable-in-water", blazeRidableInWater);
         blazeMaxY = getDouble("mobs.blaze.ridable-max-y", blazeMaxY);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.blaze.attributes.max-health", blazeMaxHealth);
+            set("mobs.blaze.attributes.max-health", null);
+            set("mobs.blaze.attributes.max_health", oldValue);
+        }
+        blazeMaxHealth = getDouble("mobs.blaze.attributes.max_health", blazeMaxHealth);
     }
 
     public boolean catRidable = false;
     public boolean catRidableInWater = false;
+    public double catMaxHealth = 10.0D;
     private void catSettings() {
         catRidable = getBoolean("mobs.cat.ridable", catRidable);
         catRidableInWater = getBoolean("mobs.cat.ridable-in-water", catRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.cat.attributes.max-health", catMaxHealth);
+            set("mobs.cat.attributes.max-health", null);
+            set("mobs.cat.attributes.max_health", oldValue);
+        }
+        catMaxHealth = getDouble("mobs.cat.attributes.max_health", catMaxHealth);
     }
 
     public boolean caveSpiderRidable = false;
     public boolean caveSpiderRidableInWater = false;
+    public double caveSpiderMaxHealth = 12.0D;
     private void caveSpiderSettings() {
         caveSpiderRidable = getBoolean("mobs.cave_spider.ridable", caveSpiderRidable);
         caveSpiderRidableInWater = getBoolean("mobs.cave_spider.ridable-in-water", caveSpiderRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.cave_spider.attributes.max-health", caveSpiderMaxHealth);
+            set("mobs.cave_spider.attributes.max-health", null);
+            set("mobs.cave_spider.attributes.max_health", oldValue);
+        }
+        caveSpiderMaxHealth = getDouble("mobs.cave_spider.attributes.max_health", caveSpiderMaxHealth);
     }
 
     public boolean chickenRidable = false;
     public boolean chickenRidableInWater = false;
+    public double chickenMaxHealth = 4.0D;
     private void chickenSettings() {
         chickenRidable = getBoolean("mobs.chicken.ridable", chickenRidable);
         chickenRidableInWater = getBoolean("mobs.chicken.ridable-in-water", chickenRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.chicken.attributes.max-health", chickenMaxHealth);
+            set("mobs.chicken.attributes.max-health", null);
+            set("mobs.chicken.attributes.max_health", oldValue);
+        }
+        chickenMaxHealth = getDouble("mobs.chicken.attributes.max_health", chickenMaxHealth);
     }
 
     public boolean codRidable = false;
+    public double codMaxHealth = 3.0D;
     private void codSettings() {
         codRidable = getBoolean("mobs.cod.ridable", codRidable);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.cod.attributes.max-health", codMaxHealth);
+            set("mobs.cod.attributes.max-health", null);
+            set("mobs.cod.attributes.max_health", oldValue);
+        }
+        codMaxHealth = getDouble("mobs.cod.attributes.max_health", codMaxHealth);
     }
 
     public boolean cowRidable = false;
     public boolean cowRidableInWater = false;
+    public double cowMaxHealth = 10.0D;
     private void cowSettings() {
         cowRidable = getBoolean("mobs.cow.ridable", cowRidable);
         cowRidableInWater = getBoolean("mobs.cow.ridable-in-water", cowRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.cow.attributes.max-health", cowMaxHealth);
+            set("mobs.cow.attributes.max-health", null);
+            set("mobs.cow.attributes.max_health", oldValue);
+        }
+        cowMaxHealth = getDouble("mobs.cow.attributes.max_health", cowMaxHealth);
     }
 
     public boolean creeperRidable = false;
     public boolean creeperRidableInWater = false;
+    public double creeperMaxHealth = 20.0D;
     private void creeperSettings() {
         creeperRidable = getBoolean("mobs.creeper.ridable", creeperRidable);
         creeperRidableInWater = getBoolean("mobs.creeper.ridable-in-water", creeperRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.creeper.attributes.max-health", creeperMaxHealth);
+            set("mobs.creeper.attributes.max-health", null);
+            set("mobs.creeper.attributes.max_health", oldValue);
+        }
+        creeperMaxHealth = getDouble("mobs.creeper.attributes.max_health", creeperMaxHealth);
     }
 
     public boolean dolphinRidable = false;
     public int dolphinSpitCooldown = 20;
     public float dolphinSpitSpeed = 1.0F;
     public float dolphinSpitDamage = 2.0F;
+    public double dolphinMaxHealth = 10.0D;
     private void dolphinSettings() {
         dolphinRidable = getBoolean("mobs.dolphin.ridable", dolphinRidable);
         dolphinSpitCooldown = getInt("mobs.dolphin.spit.cooldown", dolphinSpitCooldown);
         dolphinSpitSpeed = (float) getDouble("mobs.dolphin.spit.speed", dolphinSpitSpeed);
         dolphinSpitDamage = (float) getDouble("mobs.dolphin.spit.damage", dolphinSpitDamage);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.dolphin.attributes.max-health", dolphinMaxHealth);
+            set("mobs.dolphin.attributes.max-health", null);
+            set("mobs.dolphin.attributes.max_health", oldValue);
+        }
+        dolphinMaxHealth = getDouble("mobs.dolphin.attributes.max_health", dolphinMaxHealth);
     }
 
     public boolean donkeyRidableInWater = false;
+    public double donkeyMaxHealthMin = 15.0D;
+    public double donkeyMaxHealthMax = 30.0D;
+    public double donkeyJumpStrengthMin = 0.5D;
+    public double donkeyJumpStrengthMax = 0.5D;
+    public double donkeyMovementSpeedMin = 0.175D;
+    public double donkeyMovementSpeedMax = 0.175D;
     private void donkeySettings() {
         donkeyRidableInWater = getBoolean("mobs.donkey.ridable-in-water", donkeyRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldMin = getDouble("mobs.donkey.attributes.max-health.min", donkeyMaxHealthMin);
+            double oldMax = getDouble("mobs.donkey.attributes.max-health.max", donkeyMaxHealthMax);
+            set("mobs.donkey.attributes.max-health", null);
+            set("mobs.donkey.attributes.max_health.min", oldMin);
+            set("mobs.donkey.attributes.max_health.max", oldMax);
+        }
+        donkeyMaxHealthMin = getDouble("mobs.donkey.attributes.max_health.min", donkeyMaxHealthMin);
+        donkeyMaxHealthMax = getDouble("mobs.donkey.attributes.max_health.max", donkeyMaxHealthMax);
+        donkeyJumpStrengthMin = getDouble("mobs.donkey.attributes.jump_strength.min", donkeyJumpStrengthMin);
+        donkeyJumpStrengthMax = getDouble("mobs.donkey.attributes.jump_strength.max", donkeyJumpStrengthMax);
+        donkeyMovementSpeedMin = getDouble("mobs.donkey.attributes.movement_speed.min", donkeyMovementSpeedMin);
+        donkeyMovementSpeedMax = getDouble("mobs.donkey.attributes.movement_speed.max", donkeyMovementSpeedMax);
     }
 
     public boolean drownedRidable = false;
     public boolean drownedRidableInWater = false;
+    public double drownedMaxHealth = 20.0D;
+    public double drownedSpawnReinforcements = 0.1D;
     private void drownedSettings() {
         drownedRidable = getBoolean("mobs.drowned.ridable", drownedRidable);
         drownedRidableInWater = getBoolean("mobs.drowned.ridable-in-water", drownedRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.drowned.attributes.max-health", drownedMaxHealth);
+            set("mobs.drowned.attributes.max-health", null);
+            set("mobs.drowned.attributes.max_health", oldValue);
+        }
+        drownedMaxHealth = getDouble("mobs.drowned.attributes.max_health", drownedMaxHealth);
+        drownedSpawnReinforcements = getDouble("mobs.drowned.attributes.spawn_reinforcements", drownedSpawnReinforcements);
     }
 
     public boolean elderGuardianRidable = false;
+    public double elderGuardianMaxHealth = 80.0D;
     private void elderGuardianSettings() {
         elderGuardianRidable = getBoolean("mobs.elder_guardian.ridable", elderGuardianRidable);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.elder_guardian.attributes.max-health", elderGuardianMaxHealth);
+            set("mobs.elder_guardian.attributes.max-health", null);
+            set("mobs.elder_guardian.attributes.max_health", oldValue);
+        }
+        elderGuardianMaxHealth = getDouble("mobs.elder_guardian.attributes.max_health", elderGuardianMaxHealth);
     }
 
     public boolean enderDragonRidable = false;
     public boolean enderDragonRidableInWater = false;
     public double enderDragonMaxY = 256D;
+    public double enderDragonMaxHealth = 200.0D;
     private void enderDragonSettings() {
         enderDragonRidable = getBoolean("mobs.ender_dragon.ridable", enderDragonRidable);
         enderDragonRidableInWater = getBoolean("mobs.ender_dragon.ridable-in-water", enderDragonRidableInWater);
         enderDragonMaxY = getDouble("mobs.ender_dragon.ridable-max-y", enderDragonMaxY);
+        if (PurpurConfig.version < 8) {
+            double oldValue = getDouble("mobs.ender_dragon.max-health", enderDragonMaxHealth);
+            set("mobs.ender_dragon.max-health", null);
+            set("mobs.ender_dragon.attributes.max_health", oldValue);
+        } else if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.ender_dragon.attributes.max-health", enderDragonMaxHealth);
+            set("mobs.ender_dragon.attributes.max-health", null);
+            set("mobs.ender_dragon.attributes.max_health", oldValue);
+        }
+        enderDragonMaxHealth = getDouble("mobs.ender_dragon.attributes.max_health", enderDragonMaxHealth);
     }
 
     public boolean endermanRidable = false;
     public boolean endermanRidableInWater = false;
+    public double endermanMaxHealth = 40.0D;
     private void endermanSettings() {
         endermanRidable = getBoolean("mobs.enderman.ridable", endermanRidable);
         endermanRidableInWater = getBoolean("mobs.enderman.ridable-in-water", endermanRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.enderman.attributes.max-health", endermanMaxHealth);
+            set("mobs.enderman.attributes.max-health", null);
+            set("mobs.enderman.attributes.max_health", oldValue);
+        }
+        endermanMaxHealth = getDouble("mobs.enderman.attributes.max_health", endermanMaxHealth);
     }
 
     public boolean endermiteRidable = false;
     public boolean endermiteRidableInWater = false;
+    public double endermiteMaxHealth = 8.0D;
     private void endermiteSettings() {
         endermiteRidable = getBoolean("mobs.endermite.ridable", endermiteRidable);
         endermiteRidableInWater = getBoolean("mobs.endermite.ridable-in-water", endermiteRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.endermite.attributes.max-health", endermiteMaxHealth);
+            set("mobs.endermite.attributes.max-health", null);
+            set("mobs.endermite.attributes.max_health", oldValue);
+        }
+        endermiteMaxHealth = getDouble("mobs.endermite.attributes.max_health", endermiteMaxHealth);
     }
 
     public boolean evokerRidable = false;
     public boolean evokerRidableInWater = false;
+    public double evokerMaxHealth = 24.0D;
     private void evokerSettings() {
         evokerRidable = getBoolean("mobs.evoker.ridable", evokerRidable);
         evokerRidableInWater = getBoolean("mobs.evoker.ridable-in-water", evokerRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.evoker.attributes.max-health", evokerMaxHealth);
+            set("mobs.evoker.attributes.max-health", null);
+            set("mobs.evoker.attributes.max_health", oldValue);
+        }
+        evokerMaxHealth = getDouble("mobs.evoker.attributes.max_health", evokerMaxHealth);
     }
 
     public boolean foxRidable = false;
     public boolean foxRidableInWater = false;
+    public double foxMaxHealth = 10.0D;
     private void foxSettings() {
         foxRidable = getBoolean("mobs.fox.ridable", foxRidable);
         foxRidableInWater = getBoolean("mobs.fox.ridable-in-water", foxRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.fox.attributes.max-health", foxMaxHealth);
+            set("mobs.fox.attributes.max-health", null);
+            set("mobs.fox.attributes.max_health", oldValue);
+        }
+        foxMaxHealth = getDouble("mobs.fox.attributes.max_health", foxMaxHealth);
     }
 
     public boolean ghastRidable = false;
     public boolean ghastRidableInWater = false;
     public double ghastMaxY = 256D;
+    public double ghastMaxHealth = 10.0D;
     private void ghastSettings() {
         ghastRidable = getBoolean("mobs.ghast.ridable", ghastRidable);
         ghastRidableInWater = getBoolean("mobs.ghast.ridable-in-water", ghastRidableInWater);
         ghastMaxY = getDouble("mobs.ghast.ridable-max-y", ghastMaxY);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.ghast.attributes.max-health", ghastMaxHealth);
+            set("mobs.ghast.attributes.max-health", null);
+            set("mobs.ghast.attributes.max_health", oldValue);
+        }
+        ghastMaxHealth = getDouble("mobs.ghast.attributes.max_health", ghastMaxHealth);
     }
 
     public boolean giantRidable = false;
     public boolean giantRidableInWater = false;
+    public double giantMovementSpeed = 0.5D;
+    public double giantAttackDamage = 50.0D;
+    public double giantMaxHealth = 100.0D;
     private void giantSettings() {
         giantRidable = getBoolean("mobs.giant.ridable", giantRidable);
         giantRidableInWater = getBoolean("mobs.giant.ridable-in-water", giantRidableInWater);
+        giantMovementSpeed = getDouble("mobs.giant.movement-speed", giantMovementSpeed);
+        giantAttackDamage = getDouble("mobs.giant.attack-damage", giantAttackDamage);
+        if (PurpurConfig.version < 8) {
+            double oldValue = getDouble("mobs.giant.max-health", giantMaxHealth);
+            set("mobs.giant.max-health", null);
+            set("mobs.giant.attributes.max_health", oldValue);
+        } else if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.giant.attributes.max-health", giantMaxHealth);
+            set("mobs.giant.attributes.max-health", null);
+            set("mobs.giant.attributes.max_health", oldValue);
+        }
+        giantMaxHealth = getDouble("mobs.giant.attributes.max_health", giantMaxHealth);
     }
 
     public boolean glowSquidRidable = false;
+    public double glowSquidMaxHealth = 10.0D;
     private void glowSquidSettings() {
         glowSquidRidable = getBoolean("mobs.glow_squid.ridable", glowSquidRidable);
+        glowSquidMaxHealth = getDouble("mobs.glow_squid.attributes.max_health", glowSquidMaxHealth);
     }
 
     public boolean goatRidable = false;
     public boolean goatRidableInWater = false;
+    public double goatMaxHealth = 10.0D;
     private void goatSettings() {
         goatRidable = getBoolean("mobs.goat.ridable", goatRidable);
         goatRidableInWater = getBoolean("mobs.goat.ridable-in-water", goatRidableInWater);
+        goatMaxHealth = getDouble("mobs.goat.attributes.max_health", goatMaxHealth);
     }
 
     public boolean guardianRidable = false;
+    public double guardianMaxHealth = 30.0D;
     private void guardianSettings() {
         guardianRidable = getBoolean("mobs.guardian.ridable", guardianRidable);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.guardian.attributes.max-health", guardianMaxHealth);
+            set("mobs.guardian.attributes.max-health", null);
+            set("mobs.guardian.attributes.max_health", oldValue);
+        }
+        guardianMaxHealth = getDouble("mobs.guardian.attributes.max_health", guardianMaxHealth);
     }
 
     public boolean hoglinRidable = false;
     public boolean hoglinRidableInWater = false;
+    public double hoglinMaxHealth = 40.0D;
     private void hoglinSettings() {
         hoglinRidable = getBoolean("mobs.hoglin.ridable", hoglinRidable);
         hoglinRidableInWater = getBoolean("mobs.hoglin.ridable-in-water", hoglinRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.hoglin.attributes.max-health", hoglinMaxHealth);
+            set("mobs.hoglin.attributes.max-health", null);
+            set("mobs.hoglin.attributes.max_health", oldValue);
+        }
+        hoglinMaxHealth = getDouble("mobs.hoglin.attributes.max_health", hoglinMaxHealth);
     }
 
     public boolean horseRidableInWater = false;
+    public double horseMaxHealthMin = 15.0D;
+    public double horseMaxHealthMax = 30.0D;
+    public double horseJumpStrengthMin = 0.4D;
+    public double horseJumpStrengthMax = 1.0D;
+    public double horseMovementSpeedMin = 0.1125D;
+    public double horseMovementSpeedMax = 0.3375D;
     private void horseSettings() {
         horseRidableInWater = getBoolean("mobs.horse.ridable-in-water", horseRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldMin = getDouble("mobs.horse.attributes.max-health.min", horseMaxHealthMin);
+            double oldMax = getDouble("mobs.horse.attributes.max-health.max", horseMaxHealthMax);
+            set("mobs.horse.attributes.max-health", null);
+            set("mobs.horse.attributes.max_health.min", oldMin);
+            set("mobs.horse.attributes.max_health.max", oldMax);
+        }
+        horseMaxHealthMin = getDouble("mobs.horse.attributes.max_health.min", horseMaxHealthMin);
+        horseMaxHealthMax = getDouble("mobs.horse.attributes.max_health.max", horseMaxHealthMax);
+        horseJumpStrengthMin = getDouble("mobs.horse.attributes.jump_strength.min", horseJumpStrengthMin);
+        horseJumpStrengthMax = getDouble("mobs.horse.attributes.jump_strength.max", horseJumpStrengthMax);
+        horseMovementSpeedMin = getDouble("mobs.horse.attributes.movement_speed.min", horseMovementSpeedMin);
+        horseMovementSpeedMax = getDouble("mobs.horse.attributes.movement_speed.max", horseMovementSpeedMax);
     }
 
     public boolean huskRidable = false;
     public boolean huskRidableInWater = false;
+    public double huskMaxHealth = 20.0D;
+    public double huskSpawnReinforcements = 0.1D;
     private void huskSettings() {
         huskRidable = getBoolean("mobs.husk.ridable", huskRidable);
         huskRidableInWater = getBoolean("mobs.husk.ridable-in-water", huskRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.husk.attributes.max-health", huskMaxHealth);
+            set("mobs.husk.attributes.max-health", null);
+            set("mobs.husk.attributes.max_health", oldValue);
+        }
+        huskMaxHealth = getDouble("mobs.husk.attributes.max_health", huskMaxHealth);
+        huskSpawnReinforcements = getDouble("mobs.husk.attributes.spawn_reinforcements", huskSpawnReinforcements);
     }
 
     public boolean illusionerRidable = false;
     public boolean illusionerRidableInWater = false;
+    public double illusionerMovementSpeed = 0.5D;
+    public double illusionerFollowRange = 18.0D;
+    public double illusionerMaxHealth = 32.0D;
     private void illusionerSettings() {
         illusionerRidable = getBoolean("mobs.illusioner.ridable", illusionerRidable);
         illusionerRidableInWater = getBoolean("mobs.illusioner.ridable-in-water", illusionerRidableInWater);
+        illusionerMovementSpeed = getDouble("mobs.illusioner.movement-speed", illusionerMovementSpeed);
+        illusionerFollowRange = getDouble("mobs.illusioner.follow-range", illusionerFollowRange);
+        if (PurpurConfig.version < 8) {
+            double oldValue = getDouble("mobs.illusioner.max-health", illusionerMaxHealth);
+            set("mobs.illusioner.max-health", null);
+            set("mobs.illusioner.attributes.max_health", oldValue);
+        } else if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.illusioner.attributes.max-health", illusionerMaxHealth);
+            set("mobs.illusioner.attributes.max-health", null);
+            set("mobs.illusioner.attributes.max_health", oldValue);
+        }
+        illusionerMaxHealth = getDouble("mobs.illusioner.attributes.max_health", illusionerMaxHealth);
     }
 
     public boolean ironGolemRidable = false;
     public boolean ironGolemRidableInWater = false;
     public boolean ironGolemCanSwim = false;
+    public double ironGolemMaxHealth = 100.0D;
     private void ironGolemSettings() {
         ironGolemRidable = getBoolean("mobs.iron_golem.ridable", ironGolemRidable);
         ironGolemRidableInWater = getBoolean("mobs.iron_golem.ridable-in-water", ironGolemRidableInWater);
         ironGolemCanSwim = getBoolean("mobs.iron_golem.can-swim", ironGolemCanSwim);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.iron_golem.attributes.max-health", ironGolemMaxHealth);
+            set("mobs.iron_golem.attributes.max-health", null);
+            set("mobs.iron_golem.attributes.max_health", oldValue);
+        }
+        ironGolemMaxHealth = getDouble("mobs.iron_golem.attributes.max_health", ironGolemMaxHealth);
     }
 
     public boolean llamaRidable = false;
     public boolean llamaRidableInWater = false;
+    public double llamaMaxHealthMin = 15.0D;
+    public double llamaMaxHealthMax = 30.0D;
+    public double llamaJumpStrengthMin = 0.5D;
+    public double llamaJumpStrengthMax = 0.5D;
+    public double llamaMovementSpeedMin = 0.175D;
+    public double llamaMovementSpeedMax = 0.175D;
     private void llamaSettings() {
         llamaRidable = getBoolean("mobs.llama.ridable", llamaRidable);
         llamaRidableInWater = getBoolean("mobs.llama.ridable-in-water", llamaRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldMin = getDouble("mobs.llama.attributes.max-health.min", llamaMaxHealthMin);
+            double oldMax = getDouble("mobs.llama.attributes.max-health.max", llamaMaxHealthMax);
+            set("mobs.llama.attributes.max-health", null);
+            set("mobs.llama.attributes.max_health.min", oldMin);
+            set("mobs.llama.attributes.max_health.max", oldMax);
+        }
+        llamaMaxHealthMin = getDouble("mobs.llama.attributes.max_health.min", llamaMaxHealthMin);
+        llamaMaxHealthMax = getDouble("mobs.llama.attributes.max_health.max", llamaMaxHealthMax);
+        llamaJumpStrengthMin = getDouble("mobs.llama.attributes.jump_strength.min", llamaJumpStrengthMin);
+        llamaJumpStrengthMax = getDouble("mobs.llama.attributes.jump_strength.max", llamaJumpStrengthMax);
+        llamaMovementSpeedMin = getDouble("mobs.llama.attributes.movement_speed.min", llamaMovementSpeedMin);
+        llamaMovementSpeedMax = getDouble("mobs.llama.attributes.movement_speed.max", llamaMovementSpeedMax);
     }
 
     public boolean magmaCubeRidable = false;
     public boolean magmaCubeRidableInWater = false;
+    public String magmaCubeMaxHealth = "size * size";
     private void magmaCubeSettings() {
         magmaCubeRidable = getBoolean("mobs.magma_cube.ridable", magmaCubeRidable);
         magmaCubeRidableInWater = getBoolean("mobs.magma_cube.ridable-in-water", magmaCubeRidableInWater);
+        if (PurpurConfig.version < 10) {
+            String oldValue = getString("mobs.magma_cube.attributes.max-health", magmaCubeMaxHealth);
+            set("mobs.magma_cube.attributes.max-health", null);
+            set("mobs.magma_cube.attributes.max_health", oldValue);
+        }
+        magmaCubeMaxHealth = getString("mobs.magma_cube.attributes.max_health", magmaCubeMaxHealth);
     }
 
     public boolean mooshroomRidable = false;
     public boolean mooshroomRidableInWater = false;
+    public double mooshroomMaxHealth = 10.0D;
     private void mooshroomSettings() {
         mooshroomRidable = getBoolean("mobs.mooshroom.ridable", mooshroomRidable);
         mooshroomRidableInWater = getBoolean("mobs.mooshroom.ridable-in-water", mooshroomRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.mooshroom.attributes.max-health", mooshroomMaxHealth);
+            set("mobs.mooshroom.attributes.max-health", null);
+            set("mobs.mooshroom.attributes.max_health", oldValue);
+        }
+        mooshroomMaxHealth = getDouble("mobs.mooshroom.attributes.max_health", mooshroomMaxHealth);
     }
 
     public boolean muleRidableInWater = false;
+    public double muleMaxHealthMin = 15.0D;
+    public double muleMaxHealthMax = 30.0D;
+    public double muleJumpStrengthMin = 0.5D;
+    public double muleJumpStrengthMax = 0.5D;
+    public double muleMovementSpeedMin = 0.175D;
+    public double muleMovementSpeedMax = 0.175D;
     private void muleSettings() {
         muleRidableInWater = getBoolean("mobs.mule.ridable-in-water", muleRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldMin = getDouble("mobs.mule.attributes.max-health.min", muleMaxHealthMin);
+            double oldMax = getDouble("mobs.mule.attributes.max-health.max", muleMaxHealthMax);
+            set("mobs.mule.attributes.max-health", null);
+            set("mobs.mule.attributes.max_health.min", oldMin);
+            set("mobs.mule.attributes.max_health.max", oldMax);
+        }
+        muleMaxHealthMin = getDouble("mobs.mule.attributes.max_health.min", muleMaxHealthMin);
+        muleMaxHealthMax = getDouble("mobs.mule.attributes.max_health.max", muleMaxHealthMax);
+        muleJumpStrengthMin = getDouble("mobs.mule.attributes.jump_strength.min", muleJumpStrengthMin);
+        muleJumpStrengthMax = getDouble("mobs.mule.attributes.jump_strength.max", muleJumpStrengthMax);
+        muleMovementSpeedMin = getDouble("mobs.mule.attributes.movement_speed.min", muleMovementSpeedMin);
+        muleMovementSpeedMax = getDouble("mobs.mule.attributes.movement_speed.max", muleMovementSpeedMax);
     }
 
     public boolean ocelotRidable = false;
     public boolean ocelotRidableInWater = false;
+    public double ocelotMaxHealth = 10.0D;
     private void ocelotSettings() {
         ocelotRidable = getBoolean("mobs.ocelot.ridable", ocelotRidable);
         ocelotRidableInWater = getBoolean("mobs.ocelot.ridable-in-water", ocelotRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.ocelot.attributes.max-health", ocelotMaxHealth);
+            set("mobs.ocelot.attributes.max-health", null);
+            set("mobs.ocelot.attributes.max_health", oldValue);
+        }
+        ocelotMaxHealth = getDouble("mobs.ocelot.attributes.max_health", ocelotMaxHealth);
     }
 
     public boolean pandaRidable = false;
     public boolean pandaRidableInWater = false;
+    public double pandaMaxHealth = 20.0D;
     private void pandaSettings() {
         pandaRidable = getBoolean("mobs.panda.ridable", pandaRidable);
         pandaRidableInWater = getBoolean("mobs.panda.ridable-in-water", pandaRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.panda.attributes.max-health", pandaMaxHealth);
+            set("mobs.panda.attributes.max-health", null);
+            set("mobs.panda.attributes.max_health", oldValue);
+        }
+        pandaMaxHealth = getDouble("mobs.panda.attributes.max_health", pandaMaxHealth);
     }
 
     public boolean parrotRidable = false;
     public boolean parrotRidableInWater = false;
     public double parrotMaxY = 256D;
+    public double parrotMaxHealth = 6.0D;
     private void parrotSettings() {
         parrotRidable = getBoolean("mobs.parrot.ridable", parrotRidable);
         parrotRidableInWater = getBoolean("mobs.parrot.ridable-in-water", parrotRidableInWater);
         parrotMaxY = getDouble("mobs.parrot.ridable-max-y", parrotMaxY);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.parrot.attributes.max-health", parrotMaxHealth);
+            set("mobs.parrot.attributes.max-health", null);
+            set("mobs.parrot.attributes.max_health", oldValue);
+        }
+        parrotMaxHealth = getDouble("mobs.parrot.attributes.max_health", parrotMaxHealth);
     }
 
     public boolean phantomRidable = false;
@@ -362,6 +678,7 @@ public class PurpurWorldConfig {
     public float phantomFlameDamage = 1.0F;
     public int phantomFlameFireTime = 8;
     public boolean phantomAllowGriefing = false;
+    public double phantomMaxHealth = 20.0D;
     private void phantomSettings() {
         phantomRidable = getBoolean("mobs.phantom.ridable", phantomRidable);
         phantomRidableInWater = getBoolean("mobs.phantom.ridable-in-water", phantomRidableInWater);
@@ -369,255 +686,559 @@ public class PurpurWorldConfig {
         phantomFlameDamage = (float) getDouble("mobs.phantom.flames.damage", phantomFlameDamage);
         phantomFlameFireTime = getInt("mobs.phantom.flames.fire-time", phantomFlameFireTime);
         phantomAllowGriefing = getBoolean("mobs.phantom.allow-griefing", phantomAllowGriefing);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.phantom.attributes.max-health", phantomMaxHealth);
+            set("mobs.phantom.attributes.max-health", null);
+            set("mobs.phantom.attributes.max_health", oldValue);
+        }
+        phantomMaxHealth = getDouble("mobs.phantom.attributes.max_health", phantomMaxHealth);
     }
 
     public boolean pigRidable = false;
     public boolean pigRidableInWater = false;
+    public double pigMaxHealth = 10.0D;
     private void pigSettings() {
         pigRidable = getBoolean("mobs.pig.ridable", pigRidable);
         pigRidableInWater = getBoolean("mobs.pig.ridable-in-water", pigRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.pig.attributes.max-health", pigMaxHealth);
+            set("mobs.pig.attributes.max-health", null);
+            set("mobs.pig.attributes.max_health", oldValue);
+        }
+        pigMaxHealth = getDouble("mobs.pig.attributes.max_health", pigMaxHealth);
     }
 
     public boolean piglinRidable = false;
     public boolean piglinRidableInWater = false;
+    public double piglinMaxHealth = 16.0D;
     private void piglinSettings() {
         piglinRidable = getBoolean("mobs.piglin.ridable", piglinRidable);
         piglinRidableInWater = getBoolean("mobs.piglin.ridable-in-water", piglinRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.piglin.attributes.max-health", piglinMaxHealth);
+            set("mobs.piglin.attributes.max-health", null);
+            set("mobs.piglin.attributes.max_health", oldValue);
+        }
+        piglinMaxHealth = getDouble("mobs.piglin.attributes.max_health", piglinMaxHealth);
     }
 
     public boolean piglinBruteRidable = false;
     public boolean piglinBruteRidableInWater = false;
+    public double piglinBruteMaxHealth = 50.0D;
     private void piglinBruteSettings() {
         piglinBruteRidable = getBoolean("mobs.piglin_brute.ridable", piglinBruteRidable);
         piglinBruteRidableInWater = getBoolean("mobs.piglin_brute.ridable-in-water", piglinBruteRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.piglin_brute.attributes.max-health", piglinBruteMaxHealth);
+            set("mobs.piglin_brute.attributes.max-health", null);
+            set("mobs.piglin_brute.attributes.max_health", oldValue);
+        }
+        piglinBruteMaxHealth = getDouble("mobs.piglin_brute.attributes.max_health", piglinBruteMaxHealth);
     }
 
     public boolean pillagerRidable = false;
     public boolean pillagerRidableInWater = false;
+    public double pillagerMaxHealth = 24.0D;
     private void pillagerSettings() {
         pillagerRidable = getBoolean("mobs.pillager.ridable", pillagerRidable);
         pillagerRidableInWater = getBoolean("mobs.pillager.ridable-in-water", pillagerRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.pillager.attributes.max-health", pillagerMaxHealth);
+            set("mobs.pillager.attributes.max-health", null);
+            set("mobs.pillager.attributes.max_health", oldValue);
+        }
+        pillagerMaxHealth = getDouble("mobs.pillager.attributes.max_health", pillagerMaxHealth);
     }
 
     public boolean polarBearRidable = false;
     public boolean polarBearRidableInWater = false;
+    public double polarBearMaxHealth = 30.0D;
     private void polarBearSettings() {
         polarBearRidable = getBoolean("mobs.polar_bear.ridable", polarBearRidable);
         polarBearRidableInWater = getBoolean("mobs.polar_bear.ridable-in-water", polarBearRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.polar_bear.attributes.max-health", polarBearMaxHealth);
+            set("mobs.polar_bear.attributes.max-health", null);
+            set("mobs.polar_bear.attributes.max_health", oldValue);
+        }
+        polarBearMaxHealth = getDouble("mobs.polar_bear.attributes.max_health", polarBearMaxHealth);
     }
 
     public boolean pufferfishRidable = false;
+    public double pufferfishMaxHealth = 3.0D;
     private void pufferfishSettings() {
         pufferfishRidable = getBoolean("mobs.pufferfish.ridable", pufferfishRidable);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.pufferfish.attributes.max-health", pufferfishMaxHealth);
+            set("mobs.pufferfish.attributes.max-health", null);
+            set("mobs.pufferfish.attributes.max_health", oldValue);
+        }
+        pufferfishMaxHealth = getDouble("mobs.pufferfish.attributes.max_health", pufferfishMaxHealth);
     }
 
     public boolean rabbitRidable = false;
     public boolean rabbitRidableInWater = false;
+    public double rabbitMaxHealth = 3.0D;
     private void rabbitSettings() {
         rabbitRidable = getBoolean("mobs.rabbit.ridable", rabbitRidable);
         rabbitRidableInWater = getBoolean("mobs.rabbit.ridable-in-water", rabbitRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.rabbit.attributes.max-health", rabbitMaxHealth);
+            set("mobs.rabbit.attributes.max-health", null);
+            set("mobs.rabbit.attributes.max_health", oldValue);
+        }
+        rabbitMaxHealth = getDouble("mobs.rabbit.attributes.max_health", rabbitMaxHealth);
     }
 
     public boolean ravagerRidable = false;
     public boolean ravagerRidableInWater = false;
+    public double ravagerMaxHealth = 100.0D;
     private void ravagerSettings() {
         ravagerRidable = getBoolean("mobs.ravager.ridable", ravagerRidable);
         ravagerRidableInWater = getBoolean("mobs.ravager.ridable-in-water", ravagerRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.ravager.attributes.max-health", ravagerMaxHealth);
+            set("mobs.ravager.attributes.max-health", null);
+            set("mobs.ravager.attributes.max_health", oldValue);
+        }
+        ravagerMaxHealth = getDouble("mobs.ravager.attributes.max_health", ravagerMaxHealth);
     }
 
     public boolean salmonRidable = false;
+    public double salmonMaxHealth = 3.0D;
     private void salmonSettings() {
         salmonRidable = getBoolean("mobs.salmon.ridable", salmonRidable);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.salmon.attributes.max-health", salmonMaxHealth);
+            set("mobs.salmon.attributes.max-health", null);
+            set("mobs.salmon.attributes.max_health", oldValue);
+        }
+        salmonMaxHealth = getDouble("mobs.salmon.attributes.max_health", salmonMaxHealth);
     }
 
     public boolean sheepRidable = false;
     public boolean sheepRidableInWater = false;
+    public double sheepMaxHealth = 8.0D;
     private void sheepSettings() {
         sheepRidable = getBoolean("mobs.sheep.ridable", sheepRidable);
         sheepRidableInWater = getBoolean("mobs.sheep.ridable-in-water", sheepRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.sheep.attributes.max-health", sheepMaxHealth);
+            set("mobs.sheep.attributes.max-health", null);
+            set("mobs.sheep.attributes.max_health", oldValue);
+        }
+        sheepMaxHealth = getDouble("mobs.sheep.attributes.max_health", sheepMaxHealth);
     }
 
     public boolean shulkerRidable = false;
     public boolean shulkerRidableInWater = false;
+    public double shulkerMaxHealth = 30.0D;
     private void shulkerSettings() {
         shulkerRidable = getBoolean("mobs.shulker.ridable", shulkerRidable);
         shulkerRidableInWater = getBoolean("mobs.shulker.ridable-in-water", shulkerRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.shulker.attributes.max-health", shulkerMaxHealth);
+            set("mobs.shulker.attributes.max-health", null);
+            set("mobs.shulker.attributes.max_health", oldValue);
+        }
+        shulkerMaxHealth = getDouble("mobs.shulker.attributes.max_health", shulkerMaxHealth);
     }
 
     public boolean silverfishRidable = false;
     public boolean silverfishRidableInWater = false;
+    public double silverfishMaxHealth = 8.0D;
     private void silverfishSettings() {
         silverfishRidable = getBoolean("mobs.silverfish.ridable", silverfishRidable);
         silverfishRidableInWater = getBoolean("mobs.silverfish.ridable-in-water", silverfishRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.silverfish.attributes.max-health", silverfishMaxHealth);
+            set("mobs.silverfish.attributes.max-health", null);
+            set("mobs.silverfish.attributes.max_health", oldValue);
+        }
+        silverfishMaxHealth = getDouble("mobs.silverfish.attributes.max_health", silverfishMaxHealth);
     }
 
     public boolean skeletonRidable = false;
     public boolean skeletonRidableInWater = false;
+    public double skeletonMaxHealth = 20.0D;
     private void skeletonSettings() {
         skeletonRidable = getBoolean("mobs.skeleton.ridable", skeletonRidable);
         skeletonRidableInWater = getBoolean("mobs.skeleton.ridable-in-water", skeletonRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.skeleton.attributes.max-health", skeletonMaxHealth);
+            set("mobs.skeleton.attributes.max-health", null);
+            set("mobs.skeleton.attributes.max_health", oldValue);
+        }
+        skeletonMaxHealth = getDouble("mobs.skeleton.attributes.max_health", skeletonMaxHealth);
     }
 
     public boolean skeletonHorseRidableInWater = false;
     public boolean skeletonHorseCanSwim = false;
+    public double skeletonHorseMaxHealthMin = 15.0D;
+    public double skeletonHorseMaxHealthMax = 15.0D;
+    public double skeletonHorseJumpStrengthMin = 0.4D;
+    public double skeletonHorseJumpStrengthMax = 1.0D;
+    public double skeletonHorseMovementSpeedMin = 0.2D;
+    public double skeletonHorseMovementSpeedMax = 0.2D;
     private void skeletonHorseSettings() {
         skeletonHorseRidableInWater = getBoolean("mobs.skeleton_horse.ridable-in-water", skeletonHorseRidableInWater);
         skeletonHorseCanSwim = getBoolean("mobs.skeleton_horse.can-swim", skeletonHorseCanSwim);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.skeleton_horse.attributes.max-health", skeletonHorseMaxHealthMin);
+            set("mobs.skeleton_horse.attributes.max-health", null);
+            set("mobs.skeleton_horse.attributes.max_health.min", oldValue);
+            set("mobs.skeleton_horse.attributes.max_health.max", oldValue);
+        }
+        skeletonHorseMaxHealthMin = getDouble("mobs.skeleton_horse.attributes.max_health.min", skeletonHorseMaxHealthMin);
+        skeletonHorseMaxHealthMax = getDouble("mobs.skeleton_horse.attributes.max_health.max", skeletonHorseMaxHealthMax);
+        skeletonHorseJumpStrengthMin = getDouble("mobs.skeleton_horse.attributes.jump_strength.min", skeletonHorseJumpStrengthMin);
+        skeletonHorseJumpStrengthMax = getDouble("mobs.skeleton_horse.attributes.jump_strength.max", skeletonHorseJumpStrengthMax);
+        skeletonHorseMovementSpeedMin = getDouble("mobs.skeleton_horse.attributes.movement_speed.min", skeletonHorseMovementSpeedMin);
+        skeletonHorseMovementSpeedMax = getDouble("mobs.skeleton_horse.attributes.movement_speed.max", skeletonHorseMovementSpeedMax);
     }
 
     public boolean slimeRidable = false;
     public boolean slimeRidableInWater = false;
+    public String slimeMaxHealth = "size * size";
+    public Map<Integer, Double> slimeMaxHealthCache = new HashMap<>();
     private void slimeSettings() {
         slimeRidable = getBoolean("mobs.slime.ridable", slimeRidable);
         slimeRidableInWater = getBoolean("mobs.slime.ridable-in-water", slimeRidableInWater);
+        if (PurpurConfig.version < 10) {
+            String oldValue = getString("mobs.slime.attributes.max-health", slimeMaxHealth);
+            set("mobs.slime.attributes.max-health", null);
+            set("mobs.slime.attributes.max_health", oldValue);
+        }
+        slimeMaxHealth = getString("mobs.slime.attributes.max_health", slimeMaxHealth);
+        slimeMaxHealthCache.clear();
     }
 
     public boolean snowGolemRidable = false;
     public boolean snowGolemRidableInWater = false;
     public boolean snowGolemLeaveTrailWhenRidden = false;
+    public double snowGolemMaxHealth = 4.0D;
     private void snowGolemSettings() {
         snowGolemRidable = getBoolean("mobs.snow_golem.ridable", snowGolemRidable);
         snowGolemRidableInWater = getBoolean("mobs.snow_golem.ridable-in-water", snowGolemRidableInWater);
         snowGolemLeaveTrailWhenRidden = getBoolean("mobs.snow_golem.leave-trail-when-ridden", snowGolemLeaveTrailWhenRidden);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.snow_golem.attributes.max-health", snowGolemMaxHealth);
+            set("mobs.snow_golem.attributes.max-health", null);
+            set("mobs.snow_golem.attributes.max_health", oldValue);
+        }
+        snowGolemMaxHealth = getDouble("mobs.snow_golem.attributes.max_health", snowGolemMaxHealth);
     }
 
     public boolean squidRidable = false;
+    public double squidMaxHealth = 10.0D;
     private void squidSettings() {
         squidRidable = getBoolean("mobs.squid.ridable", squidRidable);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.squid.attributes.max-health", squidMaxHealth);
+            set("mobs.squid.attributes.max-health", null);
+            set("mobs.squid.attributes.max_health", oldValue);
+        }
+        squidMaxHealth = getDouble("mobs.squid.attributes.max_health", squidMaxHealth);
     }
 
     public boolean spiderRidable = false;
     public boolean spiderRidableInWater = false;
+    public double spiderMaxHealth = 16.0D;
     private void spiderSettings() {
         spiderRidable = getBoolean("mobs.spider.ridable", spiderRidable);
         spiderRidableInWater = getBoolean("mobs.spider.ridable-in-water", spiderRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.spider.attributes.max-health", spiderMaxHealth);
+            set("mobs.spider.attributes.max-health", null);
+            set("mobs.spider.attributes.max_health", oldValue);
+        }
+        spiderMaxHealth = getDouble("mobs.spider.attributes.max_health", spiderMaxHealth);
     }
 
     public boolean strayRidable = false;
     public boolean strayRidableInWater = false;
+    public double strayMaxHealth = 20.0D;
     private void straySettings() {
         strayRidable = getBoolean("mobs.stray.ridable", strayRidable);
         strayRidableInWater = getBoolean("mobs.stray.ridable-in-water", strayRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.stray.attributes.max-health", strayMaxHealth);
+            set("mobs.stray.attributes.max-health", null);
+            set("mobs.stray.attributes.max_health", oldValue);
+        }
+        strayMaxHealth = getDouble("mobs.stray.attributes.max_health", strayMaxHealth);
     }
 
     public boolean striderRidable = false;
     public boolean striderRidableInWater = false;
+    public double striderMaxHealth = 20.0D;
     private void striderSettings() {
         striderRidable = getBoolean("mobs.strider.ridable", striderRidable);
         striderRidableInWater = getBoolean("mobs.strider.ridable-in-water", striderRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.strider.attributes.max-health", striderMaxHealth);
+            set("mobs.strider.attributes.max-health", null);
+            set("mobs.strider.attributes.max_health", oldValue);
+        }
+        striderMaxHealth = getDouble("mobs.strider.attributes.max_health", striderMaxHealth);
     }
 
     public boolean traderLlamaRidable = false;
     public boolean traderLlamaRidableInWater = false;
+    public double traderLlamaMaxHealthMin = 15.0D;
+    public double traderLlamaMaxHealthMax = 30.0D;
+    public double traderLlamaJumpStrengthMin = 0.5D;
+    public double traderLlamaJumpStrengthMax = 0.5D;
+    public double traderLlamaMovementSpeedMin = 0.175D;
+    public double traderLlamaMovementSpeedMax = 0.175D;
     private void traderLlamaSettings() {
         traderLlamaRidable = getBoolean("mobs.trader_llama.ridable", traderLlamaRidable);
         traderLlamaRidableInWater = getBoolean("mobs.trader_llama.ridable-in-water", traderLlamaRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldMin = getDouble("mobs.trader_llama.attributes.max-health.min", traderLlamaMaxHealthMin);
+            double oldMax = getDouble("mobs.trader_llama.attributes.max-health.max", traderLlamaMaxHealthMax);
+            set("mobs.trader_llama.attributes.max-health", null);
+            set("mobs.trader_llama.attributes.max_health.min", oldMin);
+            set("mobs.trader_llama.attributes.max_health.max", oldMax);
+        }
+        traderLlamaMaxHealthMin = getDouble("mobs.trader_llama.attributes.max_health.min", traderLlamaMaxHealthMin);
+        traderLlamaMaxHealthMax = getDouble("mobs.trader_llama.attributes.max_health.max", traderLlamaMaxHealthMax);
+        traderLlamaJumpStrengthMin = getDouble("mobs.trader_llama.attributes.jump_strength.min", traderLlamaJumpStrengthMin);
+        traderLlamaJumpStrengthMax = getDouble("mobs.trader_llama.attributes.jump_strength.max", traderLlamaJumpStrengthMax);
+        traderLlamaMovementSpeedMin = getDouble("mobs.trader_llama.attributes.movement_speed.min", traderLlamaMovementSpeedMin);
+        traderLlamaMovementSpeedMax = getDouble("mobs.trader_llama.attributes.movement_speed.max", traderLlamaMovementSpeedMax);
     }
 
     public boolean tropicalFishRidable = false;
+    public double tropicalFishMaxHealth = 3.0D;
     private void tropicalFishSettings() {
         tropicalFishRidable = getBoolean("mobs.tropical_fish.ridable", tropicalFishRidable);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.tropical_fish.attributes.max-health", tropicalFishMaxHealth);
+            set("mobs.tropical_fish.attributes.max-health", null);
+            set("mobs.tropical_fish.attributes.max_health", oldValue);
+        }
+        tropicalFishMaxHealth = getDouble("mobs.tropical_fish.attributes.max_health", tropicalFishMaxHealth);
     }
 
     public boolean turtleRidable = false;
     public boolean turtleRidableInWater = false;
+    public double turtleMaxHealth = 30.0D;
     private void turtleSettings() {
         turtleRidable = getBoolean("mobs.turtle.ridable", turtleRidable);
         turtleRidableInWater = getBoolean("mobs.turtle.ridable-in-water", turtleRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.turtle.attributes.max-health", turtleMaxHealth);
+            set("mobs.turtle.attributes.max-health", null);
+            set("mobs.turtle.attributes.max_health", oldValue);
+        }
+        turtleMaxHealth = getDouble("mobs.turtle.attributes.max_health", turtleMaxHealth);
     }
 
     public boolean vexRidable = false;
     public boolean vexRidableInWater = false;
     public double vexMaxY = 256D;
+    public double vexMaxHealth = 14.0D;
     private void vexSettings() {
         vexRidable = getBoolean("mobs.vex.ridable", vexRidable);
         vexRidableInWater = getBoolean("mobs.vex.ridable-in-water", vexRidableInWater);
         vexMaxY = getDouble("mobs.vex.ridable-max-y", vexMaxY);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.vex.attributes.max-health", vexMaxHealth);
+            set("mobs.vex.attributes.max-health", null);
+            set("mobs.vex.attributes.max_health", oldValue);
+        }
+        vexMaxHealth = getDouble("mobs.vex.attributes.max_health", vexMaxHealth);
     }
 
     public boolean villagerRidable = false;
     public boolean villagerRidableInWater = false;
+    public double villagerMaxHealth = 20.0D;
     private void villagerSettings() {
         villagerRidable = getBoolean("mobs.villager.ridable", villagerRidable);
         villagerRidableInWater = getBoolean("mobs.villager.ridable-in-water", villagerRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.villager.attributes.max-health", villagerMaxHealth);
+            set("mobs.villager.attributes.max-health", null);
+            set("mobs.villager.attributes.max_health", oldValue);
+        }
+        villagerMaxHealth = getDouble("mobs.villager.attributes.max_health", villagerMaxHealth);
     }
 
     public boolean vindicatorRidable = false;
     public boolean vindicatorRidableInWater = false;
+    public double vindicatorMaxHealth = 24.0D;
     private void vindicatorSettings() {
         vindicatorRidable = getBoolean("mobs.vindicator.ridable", vindicatorRidable);
         vindicatorRidableInWater = getBoolean("mobs.vindicator.ridable-in-water", vindicatorRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.vindicator.attributes.max-health", vindicatorMaxHealth);
+            set("mobs.vindicator.attributes.max-health", null);
+            set("mobs.vindicator.attributes.max_health", oldValue);
+        }
+        vindicatorMaxHealth = getDouble("mobs.vindicator.attributes.max_health", vindicatorMaxHealth);
     }
 
     public boolean wanderingTraderRidable = false;
     public boolean wanderingTraderRidableInWater = false;
+    public double wanderingTraderMaxHealth = 20.0D;
     private void wanderingTraderSettings() {
         wanderingTraderRidable = getBoolean("mobs.wandering_trader.ridable", wanderingTraderRidable);
         wanderingTraderRidableInWater = getBoolean("mobs.wandering_trader.ridable-in-water", wanderingTraderRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.wandering_trader.attributes.max-health", wanderingTraderMaxHealth);
+            set("mobs.wandering_trader.attributes.max-health", null);
+            set("mobs.wandering_trader.attributes.max_health", oldValue);
+        }
+        wanderingTraderMaxHealth = getDouble("mobs.wandering_trader.attributes.max_health", wanderingTraderMaxHealth);
     }
 
     public boolean witchRidable = false;
     public boolean witchRidableInWater = false;
+    public double witchMaxHealth = 26.0D;
     private void witchSettings() {
         witchRidable = getBoolean("mobs.witch.ridable", witchRidable);
         witchRidableInWater = getBoolean("mobs.witch.ridable-in-water", witchRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.witch.attributes.max-health", witchMaxHealth);
+            set("mobs.witch.attributes.max-health", null);
+            set("mobs.witch.attributes.max_health", oldValue);
+        }
+        witchMaxHealth = getDouble("mobs.witch.attributes.max_health", witchMaxHealth);
     }
 
     public boolean witherRidable = false;
     public boolean witherRidableInWater = false;
     public double witherMaxY = 256D;
+    public double witherMaxHealth = 300.0D;
     private void witherSettings() {
         witherRidable = getBoolean("mobs.wither.ridable", witherRidable);
         witherRidableInWater = getBoolean("mobs.wither.ridable-in-water", witherRidableInWater);
         witherMaxY = getDouble("mobs.wither.ridable-max-y", witherMaxY);
+        if (PurpurConfig.version < 8) {
+            double oldValue = getDouble("mobs.wither.max-health", witherMaxHealth);
+            set("mobs.wither.max_health", null);
+            set("mobs.wither.attributes.max-health", oldValue);
+        } else if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.wither.attributes.max-health", witherMaxHealth);
+            set("mobs.wither.attributes.max-health", null);
+            set("mobs.wither.attributes.max_health", oldValue);
+        }
+        witherMaxHealth = getDouble("mobs.wither.attributes.max_health", witherMaxHealth);
     }
 
     public boolean witherSkeletonRidable = false;
     public boolean witherSkeletonRidableInWater = false;
+    public double witherSkeletonMaxHealth = 20.0D;
     private void witherSkeletonSettings() {
         witherSkeletonRidable = getBoolean("mobs.wither_skeleton.ridable", witherSkeletonRidable);
         witherSkeletonRidableInWater = getBoolean("mobs.wither_skeleton.ridable-in-water", witherSkeletonRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.wither_skeleton.attributes.max-health", witherSkeletonMaxHealth);
+            set("mobs.wither_skeleton.attributes.max-health", null);
+            set("mobs.wither_skeleton.attributes.max_health", oldValue);
+        }
+        witherSkeletonMaxHealth = getDouble("mobs.wither_skeleton.attributes.max_health", witherSkeletonMaxHealth);
     }
 
     public boolean wolfRidable = false;
     public boolean wolfRidableInWater = false;
+    public double wolfMaxHealth = 8.0D;
     private void wolfSettings() {
         wolfRidable = getBoolean("mobs.wolf.ridable", wolfRidable);
         wolfRidableInWater = getBoolean("mobs.wolf.ridable-in-water", wolfRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.wolf.attributes.max-health", wolfMaxHealth);
+            set("mobs.wolf.attributes.max-health", null);
+            set("mobs.wolf.attributes.max_health", oldValue);
+        }
+        wolfMaxHealth = getDouble("mobs.wolf.attributes.max_health", wolfMaxHealth);
     }
 
     public boolean zoglinRidable = false;
     public boolean zoglinRidableInWater = false;
+    public double zoglinMaxHealth = 40.0D;
     private void zoglinSettings() {
         zoglinRidable = getBoolean("mobs.zoglin.ridable", zoglinRidable);
         zoglinRidableInWater = getBoolean("mobs.zoglin.ridable-in-water", zoglinRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.zoglin.attributes.max-health", zoglinMaxHealth);
+            set("mobs.zoglin.attributes.max-health", null);
+            set("mobs.zoglin.attributes.max_health", oldValue);
+        }
+        zoglinMaxHealth = getDouble("mobs.zoglin.attributes.max_health", zoglinMaxHealth);
     }
 
     public boolean zombieRidable = false;
     public boolean zombieRidableInWater = false;
+    public double zombieMaxHealth = 20.0D;
+    public double zombieSpawnReinforcements = 0.1D;
     private void zombieSettings() {
         zombieRidable = getBoolean("mobs.zombie.ridable", zombieRidable);
         zombieRidableInWater = getBoolean("mobs.zombie.ridable-in-water", zombieRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.zombie.attributes.max-health", zombieMaxHealth);
+            set("mobs.zombie.attributes.max-health", null);
+            set("mobs.zombie.attributes.max_health", oldValue);
+        }
+        zombieMaxHealth = getDouble("mobs.zombie.attributes.max_health", zombieMaxHealth);
+        zombieSpawnReinforcements = getDouble("mobs.zombie.attributes.spawn_reinforcements", zombieSpawnReinforcements);
     }
 
     public boolean zombieHorseRidableInWater = false;
     public boolean zombieHorseCanSwim = false;
+    public double zombieHorseMaxHealthMin = 15.0D;
+    public double zombieHorseMaxHealthMax = 15.0D;
+    public double zombieHorseJumpStrengthMin = 0.4D;
+    public double zombieHorseJumpStrengthMax = 1.0D;
+    public double zombieHorseMovementSpeedMin = 0.2D;
+    public double zombieHorseMovementSpeedMax = 0.2D;
     private void zombieHorseSettings() {
         zombieHorseRidableInWater = getBoolean("mobs.zombie_horse.ridable-in-water", zombieHorseRidableInWater);
         zombieHorseCanSwim = getBoolean("mobs.zombie_horse.can-swim", zombieHorseCanSwim);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.zombie_horse.attributes.max-health", zombieHorseMaxHealthMin);
+            set("mobs.zombie_horse.attributes.max-health", null);
+            set("mobs.zombie_horse.attributes.max_health.min", oldValue);
+            set("mobs.zombie_horse.attributes.max_health.max", oldValue);
+        }
+        zombieHorseMaxHealthMin = getDouble("mobs.zombie_horse.attributes.max_health.min", zombieHorseMaxHealthMin);
+        zombieHorseMaxHealthMax = getDouble("mobs.zombie_horse.attributes.max_health.max", zombieHorseMaxHealthMax);
+        zombieHorseJumpStrengthMin = getDouble("mobs.zombie_horse.attributes.jump_strength.min", zombieHorseJumpStrengthMin);
+        zombieHorseJumpStrengthMax = getDouble("mobs.zombie_horse.attributes.jump_strength.max", zombieHorseJumpStrengthMax);
+        zombieHorseMovementSpeedMin = getDouble("mobs.zombie_horse.attributes.movement_speed.min", zombieHorseMovementSpeedMin);
+        zombieHorseMovementSpeedMax = getDouble("mobs.zombie_horse.attributes.movement_speed.max", zombieHorseMovementSpeedMax);
     }
 
     public boolean zombieVillagerRidable = false;
     public boolean zombieVillagerRidableInWater = false;
+    public double zombieVillagerMaxHealth = 20.0D;
+    public double zombieVillagerSpawnReinforcements = 0.1D;
     private void zombieVillagerSettings() {
         zombieVillagerRidable = getBoolean("mobs.zombie_villager.ridable", zombieVillagerRidable);
         zombieVillagerRidableInWater = getBoolean("mobs.zombie_villager.ridable-in-water", zombieVillagerRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.zombie_villager.attributes.max-health", zombieVillagerMaxHealth);
+            set("mobs.zombie_villager.attributes.max-health", null);
+            set("mobs.zombie_villager.attributes.max_health", oldValue);
+        }
+        zombieVillagerMaxHealth = getDouble("mobs.zombie_villager.attributes.max_health", zombieVillagerMaxHealth);
+        zombieVillagerSpawnReinforcements = getDouble("mobs.zombie_villager.attributes.spawn_reinforcements", zombieVillagerSpawnReinforcements);
     }
 
     public boolean zombifiedPiglinRidable = false;
     public boolean zombifiedPiglinRidableInWater = false;
+    public double zombifiedPiglinMaxHealth = 20.0D;
+    public double zombifiedPiglinSpawnReinforcements = 0.0D;
     private void zombifiedPiglinSettings() {
         zombifiedPiglinRidable = getBoolean("mobs.zombified_piglin.ridable", zombifiedPiglinRidable);
         zombifiedPiglinRidableInWater = getBoolean("mobs.zombified_piglin.ridable-in-water", zombifiedPiglinRidableInWater);
+        if (PurpurConfig.version < 10) {
+            double oldValue = getDouble("mobs.zombified_piglin.attributes.max-health", zombifiedPiglinMaxHealth);
+            set("mobs.zombified_piglin.attributes.max-health", null);
+            set("mobs.zombified_piglin.attributes.max_health", oldValue);
+        }
+        zombifiedPiglinMaxHealth = getDouble("mobs.zombified_piglin.attributes.max_health", zombifiedPiglinMaxHealth);
+        zombifiedPiglinSpawnReinforcements = getDouble("mobs.zombified_piglin.attributes.spawn_reinforcements", zombifiedPiglinSpawnReinforcements);
     }
 }
