diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/EntityReplacementRenderManager.java b/src/main/java/com/minelittlepony/unicopia/client/render/EntityReplacementRenderManager.java
new file mode 100644
index 00000000..8e9e6113
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/EntityReplacementRenderManager.java
@@ -0,0 +1,80 @@
+package com.minelittlepony.unicopia.client.render;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.jetbrains.annotations.Nullable;
+
+import com.minelittlepony.unicopia.EquinePredicates;
+import com.minelittlepony.unicopia.ability.magic.Caster;
+import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
+import com.minelittlepony.unicopia.entity.behaviour.Disguise;
+import com.minelittlepony.unicopia.entity.behaviour.EntityAppearance;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.passive.PassiveEntity;
+import net.minecraft.entity.player.PlayerEntity;
+
+class EntityReplacementRenderManager implements Disguise {
+    public static final EntityReplacementRenderManager INSTANCE = new EntityReplacementRenderManager();
+
+    @Nullable
+    private EntityType<?> currentType;
+    private final EntityAppearance disguise = new EntityAppearance();
+    private final List<EntityType<?>> defaultTypes = List.of(
+            EntityType.ZOMBIE,
+            EntityType.CREEPER,
+            EntityType.HUSK,
+            EntityType.SKELETON,
+            EntityType.BLAZE,
+            EntityType.DROWNED
+    );
+    private final Map<EntityType<?>, List<EntityType<?>>> pools = Map.of(
+            EntityType.VILLAGER, List.of(EntityType.PILLAGER),
+            EntityType.PLAYER, List.of(EntityType.ZOMBIE),
+            EntityType.PIGLIN, List.of(EntityType.ZOMBIFIED_PIGLIN, EntityType.PIGLIN_BRUTE),
+            EntityType.SQUID, List.of(EntityType.DROWNED),
+            EntityType.DOLPHIN, List.of(EntityType.DROWNED),
+            EntityType.ENDER_DRAGON, List.of(EntityType.CHICKEN)
+    );
+
+    public Optional<Disguise> getAppearanceFor(Caster<?> caster) {
+
+        if (isRageApplicable(caster.asEntity()) && EquinePredicates.RAGING.test(MinecraftClient.getInstance().player)) {
+            List<EntityType<?>> mobTypes = getMobTypePool(caster.asEntity().getType());
+            EntityType<?> type = mobTypes.get(Math.abs((int)caster.asEntity().getUuid().getMostSignificantBits()) % mobTypes.size());
+            if (type != currentType) {
+                currentType = type;
+                disguise.setAppearance(type.create(caster.asWorld()));
+            }
+            return Optional.of(this);
+        }
+
+        return caster.getSpellSlot().get(SpellPredicate.IS_DISGUISE, false).map(Disguise.class::cast);
+    }
+
+    private List<EntityType<?>> getMobTypePool(EntityType<?> type) {
+        return pools.getOrDefault(type, defaultTypes);
+    }
+
+    private boolean isRageApplicable(Entity entity) {
+        return entity instanceof PassiveEntity || entity instanceof PlayerEntity || pools.containsKey(entity.getType());
+    }
+
+    @Override
+    public EntityAppearance getDisguise() {
+        return disguise;
+    }
+
+    @Override
+    public void setDirty() {
+    }
+
+    @Override
+    public boolean isDead() {
+        return false;
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java b/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java
index 3288ec9c..b0a28a6e 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java
@@ -4,8 +4,6 @@ import org.jetbrains.annotations.Nullable;
 
 import com.minelittlepony.client.util.render.RenderLayerUtil;
 import com.minelittlepony.unicopia.Unicopia;
-import com.minelittlepony.unicopia.ability.magic.Caster;
-import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
 import com.minelittlepony.unicopia.compat.pehkui.PehkUtil;
 import com.minelittlepony.unicopia.entity.Creature;
 import com.minelittlepony.unicopia.entity.Equine;
@@ -157,39 +155,35 @@ public class WorldRenderDelegate {
             flipAngles(owner);
         }
 
-        if (pony instanceof Caster<?>) {
-            int fireTicks = owner.doesRenderOnFire() ? 1 : 0;
+        int fireTicks = owner.doesRenderOnFire() ? 1 : 0;
 
-            return ((Caster<?>)pony).getSpellSlot().get(SpellPredicate.IS_DISGUISE, false).map(effect -> {
-                effect.update(pony, false);
+        return EntityReplacementRenderManager.INSTANCE.getAppearanceFor(pony).map(effect -> {
+            effect.update(pony, false);
 
-                EntityAppearance ve = effect.getDisguise();
-                Entity e = ve.getAppearance();
+            EntityAppearance ve = effect.getDisguise();
+            Entity e = ve.getAppearance();
 
-                if (e != null) {
-                    PehkUtil.copyScale(pony.asEntity(), e);
+            if (e != null) {
+                PehkUtil.copyScale(pony.asEntity(), e);
 
-                    if (dispatcher.shouldRenderHitboxes()) {
-                        e.setBoundingBox(pony.asEntity().getBoundingBox());
-                    }
-
-                    renderDisguise(dispatcher, ve, e, x, y, z, fireTicks, tickDelta, matrices, vertexConsumers, light);
-                    ve.getAttachments().forEach(ee -> {
-                        PehkUtil.copyScale(pony.asEntity(), ee);
-                        Vec3d difference = ee.getPos().subtract(e.getPos());
-                        renderDisguise(dispatcher, ve, ee, x + difference.x, y + difference.y, z + difference.z, fireTicks, tickDelta, matrices, vertexConsumers, light);
-                        PehkUtil.clearScale(ee);
-                    });
-
-                    afterEntityRender(pony, matrices, light);
-                    PehkUtil.clearScale(e);
-                    return true;
+                if (dispatcher.shouldRenderHitboxes()) {
+                    e.setBoundingBox(pony.asEntity().getBoundingBox());
                 }
-                return false;
-            }).orElse(false);
-        }
 
-        return false;
+                renderDisguise(dispatcher, ve, e, x, y, z, fireTicks, tickDelta, matrices, vertexConsumers, light);
+                ve.getAttachments().forEach(ee -> {
+                    PehkUtil.copyScale(pony.asEntity(), ee);
+                    Vec3d difference = ee.getPos().subtract(e.getPos());
+                    renderDisguise(dispatcher, ve, ee, x + difference.x, y + difference.y, z + difference.z, fireTicks, tickDelta, matrices, vertexConsumers, light);
+                    PehkUtil.clearScale(ee);
+                });
+
+                afterEntityRender(pony, matrices, light);
+                PehkUtil.clearScale(e);
+                return true;
+            }
+            return false;
+        }).orElse(false);
     }
 
     public void afterEntityRender(Equine<?> pony, MatrixStack matrices, int light) {
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Living.java b/src/main/java/com/minelittlepony/unicopia/entity/Living.java
index 72bb95a7..ed657348 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/Living.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/Living.java
@@ -386,6 +386,10 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
             .isEmpty();
     }
 
+    public Optional<Vec3d> adjustMovementSpeedInWater(Vec3d speed) {
+        return Optional.empty();
+    }
+
     private void updateDragonBreath() {
         if (!entity.getWorld().isClient && (entity instanceof PlayerEntity || entity.hasCustomName())) {
 
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java
index 356d099f..5451ed02 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java
@@ -638,6 +638,14 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
         return super.canBeSeenBy(entity);
     }
 
+    @Override
+    public Optional<Vec3d> adjustMovementSpeedInWater(Vec3d speed) {
+        if (getObservedSpecies() == Race.KIRIN) {
+            return Optional.of(speed.multiply(0.5, 1, 0.5));
+        }
+        return Optional.empty();
+    }
+
     public Optional<Living<?>> getEntityInArms() {
         return Living.getOrEmpty(entity.getFirstPassenger()).filter(Living::isBeingCarried);
     }
diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java
index 917c401c..27d286c6 100644
--- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java
+++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java
@@ -26,6 +26,7 @@ import net.minecraft.item.ItemStack;
 import net.minecraft.nbt.NbtCompound;
 import net.minecraft.util.Hand;
 import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
 
 @Mixin(LivingEntity.class)
 abstract class MixinLivingEntity extends Entity implements LivingEntityDuck, Equine.Container<Living<?>> {
@@ -123,6 +124,11 @@ abstract class MixinLivingEntity extends Entity implements LivingEntityDuck, Equ
         }
     }
 
+    @Inject(method = "applyFluidMovingSpeed", at = @At("RETURN"), cancellable = true)
+    private void applyFluidMovingSpeed(double gravity, boolean falling, Vec3d motion, CallbackInfoReturnable<Vec3d> info) {
+        get().adjustMovementSpeedInWater(info.getReturnValue()).ifPresent(info::setReturnValue);
+    }
+
     @Inject(method = "jump()V", at = @At("RETURN"))
     private void onJump(CallbackInfo info) {
         get().onJump();