From b4a847e4b58407466c3306e2c73991a538999aa4 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Tue, 26 Mar 2024 17:09:36 +0000
Subject: [PATCH 01/11] Update loom

---
 build.gradle                                                   | 2 +-
 gradle/wrapper/gradle-wrapper.properties                       | 2 +-
 .../unicopia/client/render/shader/ViewportShader.java          | 3 +--
 .../com/minelittlepony/unicopia/item/EnchantedStaffItem.java   | 2 +-
 4 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/build.gradle b/build.gradle
index 51a392c2..285777fc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
 }
 plugins {
     id 'java-library'
-    id 'fabric-loom' version '0.12-SNAPSHOT'
+    id 'fabric-loom' version '1.5-SNAPSHOT'
     id 'com.modrinth.minotaur' version '2.+'
     id 'org.ajoberstar.reckon' version '0.13.0'
 }
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index e750102e..db9a6b82 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/shader/ViewportShader.java b/src/main/java/com/minelittlepony/unicopia/client/render/shader/ViewportShader.java
index ebef6c6a..21df38bc 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/shader/ViewportShader.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/shader/ViewportShader.java
@@ -2,8 +2,7 @@ package com.minelittlepony.unicopia.client.render.shader;
 
 import java.io.IOException;
 
-import javax.annotation.Nullable;
-
+import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 
 import com.google.common.collect.*;
diff --git a/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java b/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java
index b37d4cab..08798972 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java
@@ -5,7 +5,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.stream.Stream;
 
-import javax.annotation.Nullable;
+import org.jetbrains.annotations.Nullable;
 
 import com.minelittlepony.unicopia.USounds;
 import com.minelittlepony.unicopia.ability.magic.Caster;

From 692db5a0707c3731aaddf1af5daa0996f3a6084c Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Tue, 26 Mar 2024 17:11:39 +0000
Subject: [PATCH 02/11] Fix fall damage, ladders, hitbox calculations,
 clipping, camera offset, and unicorn's teleportation when in inverted gravity

---
 .../ability/UnicornTeleportAbility.java       | 13 ++--
 .../client/render/WorldRenderDelegate.java    | 36 ++++-----
 .../unicopia/command/GravityCommand.java      | 75 ++++++++++---------
 .../unicopia/entity/EntityPhysics.java        | 35 +--------
 .../unicopia/entity/Living.java               |  6 --
 .../unicopia/entity/Physics.java              |  2 -
 .../entity/player/PlayerDimensions.java       |  6 +-
 .../unicopia/entity/player/PlayerPhysics.java |  4 -
 .../unicopia/entity/player/Pony.java          |  5 +-
 .../unicopia/mixin/MixinLivingEntity.java     | 22 ------
 .../mixin/client/MixinKeyboardInput.java      |  6 --
 .../unicopia/mixin/gravity/MixinEntity.java   | 72 ++++++++++++++----
 .../mixin/gravity/MixinLivingEntity.java      | 23 ++++++
 .../MixinServerPlayNetworkHandler.java        | 29 +++++++
 .../gravity/MixinServerPlayerEntity.java      | 24 ++++++
 .../resources/assets/unicopia/lang/en_us.json |  1 +
 src/main/resources/unicopia.mixin.json        |  2 +
 17 files changed, 203 insertions(+), 158 deletions(-)
 create mode 100644 src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinServerPlayNetworkHandler.java
 create mode 100644 src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinServerPlayerEntity.java

diff --git a/src/main/java/com/minelittlepony/unicopia/ability/UnicornTeleportAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/UnicornTeleportAbility.java
index 3401632f..7f75d688 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/UnicornTeleportAbility.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/UnicornTeleportAbility.java
@@ -27,6 +27,7 @@ import net.minecraft.sound.SoundCategory;
 import net.minecraft.text.Text;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Direction;
 import net.minecraft.util.math.Vec3d;
 import net.minecraft.util.shape.VoxelShape;
 import net.minecraft.world.World;
@@ -89,8 +90,10 @@ public class UnicornTeleportAbility implements Ability<Pos> {
         return trace.getBlockOrEntityPos().map(pos -> {
             final BlockPos originalPos = pos;
 
+            Direction globalUp = player.getPhysics().isGravityNegative() ? Direction.DOWN : Direction.UP;
+
             boolean originalPosHasSupport = exception(w, pos, player.asEntity());
-            boolean originalPosValid = enterable(w, pos.up()) && enterable(w, pos.up(2));
+            boolean originalPosValid = enterable(w, pos.offset(globalUp)) && enterable(w, pos.offset(globalUp, 2));
 
             if (w.getBlockState(pos).isOf(Blocks.POWDER_SNOW) && !PowderSnowBlock.canWalkOnPowderSnow(player.asEntity())) {
                 return null;
@@ -111,11 +114,11 @@ public class UnicornTeleportAbility implements Ability<Pos> {
             if (pos.getX() != originalPos.getX() || pos.getZ() != originalPos.getZ()) {
                 // check support
                 int steps = 0;
-                while (enterable(w, pos.down())) {
-                    pos = pos.down();
+                while (enterable(w, pos.offset(globalUp.getOpposite()))) {
+                    pos = pos.offset(globalUp.getOpposite());
                     if (++steps > 2) {
                         if (originalPosValid) {
-                            pos = originalPos.up();
+                            pos = originalPos.offset(globalUp);
                             break;
                         } else {
                             return null;
@@ -125,7 +128,7 @@ public class UnicornTeleportAbility implements Ability<Pos> {
             }
 
             if ((!enterable(w, pos) && exception(w, pos, player.asEntity()))
-             || (!enterable(w, pos.up()) && exception(w, pos.up(), player.asEntity()))) {
+             || (!enterable(w, pos.offset(globalUp)) && exception(w, pos.offset(globalUp), player.asEntity()))) {
                 return null;
             }
 
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 ac9c3cdb..17594ba0 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/WorldRenderDelegate.java
@@ -175,43 +175,39 @@ public class WorldRenderDelegate {
 
         boolean negative = pony.getPhysics().isGravityNegative();
 
-        float roll = negative ? 180 : 0;
-
-        roll = pony instanceof Pony ? ((Pony)pony).getInterpolator().interpolate("g_roll", roll, 15) : roll;
-
         matrices.translate(x, y + owner.getHeight() / 2, z);
 
-        matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(roll));
-        matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(roll));
-
         if (pony instanceof Pony p) {
-            roll = p.getCamera().calculateRoll();
-            if (negative) {
-                roll -= 180;
-            }
+            float sidewaysRoll = p.getCamera().calculateRoll();
 
             if (p.getAcrobatics().isFloppy()) {
                 matrices.translate(0, -0.5, 0);
                 p.asEntity().setBodyYaw(0);
                 p.asEntity().setYaw(0);
-                matrices.multiply(RotationAxis.NEGATIVE_X.rotationDegrees(90));
+                sidewaysRoll += 90;
             }
 
-            matrices.multiply(RotationAxis.NEGATIVE_Y.rotationDegrees(yaw));
-            matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(roll));
+            matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(90));
+            matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(sidewaysRoll));
+            matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-90));
 
-            float diveAngle = p.getInterpolator().interpolate("g_kdive", p.getMotion().isDiving() ? 80 : 0, 15);
+            float forwardPitch = p.getInterpolator().interpolate("g_kdive", p.getMotion().isDiving() ? 80 : 0, 15);
 
-            matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(diveAngle));
-            matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw));
+            matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(forwardPitch));
 
             if (p.getCompositeRace().includes(Race.SEAPONY)
                     && pony.asEntity().isSubmergedInWater()
                     && MineLPDelegate.getInstance().getPlayerPonyRace(p.asEntity()) != Race.SEAPONY) {
                 ModelPartHooks.startCollecting();
             }
-        } else if (pony instanceof Creature creature && smittenEyesRenderer.isSmitten(creature)) {
-            ModelPartHooks.startCollecting();
+        } else {
+            float roll = negative ? 180 : 0;
+
+            matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(roll));
+
+            if (pony instanceof Creature creature && smittenEyesRenderer.isSmitten(creature)) {
+                ModelPartHooks.startCollecting();
+            }
         }
 
         matrices.translate(-x, -y - owner.getHeight() / 2, -z);
@@ -224,7 +220,7 @@ public class WorldRenderDelegate {
     }
 
     private void flipAngles(Entity entity) {
-        if (entity instanceof PlayerEntity) {
+        if (entity instanceof PlayerEntity player) {
             entity.prevYaw *= -1;
             entity.setYaw(entity.getYaw() * -1);
 
diff --git a/src/main/java/com/minelittlepony/unicopia/command/GravityCommand.java b/src/main/java/com/minelittlepony/unicopia/command/GravityCommand.java
index c99aa75b..c5249dcb 100644
--- a/src/main/java/com/minelittlepony/unicopia/command/GravityCommand.java
+++ b/src/main/java/com/minelittlepony/unicopia/command/GravityCommand.java
@@ -1,15 +1,15 @@
 package com.minelittlepony.unicopia.command;
 
-import java.util.Arrays;
-import java.util.stream.Stream;
-
-import com.google.common.collect.Streams;
-import com.minelittlepony.unicopia.entity.player.Pony;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import com.minelittlepony.unicopia.entity.Living;
 import com.mojang.brigadier.arguments.FloatArgumentType;
 import com.mojang.brigadier.builder.LiteralArgumentBuilder;
 import com.mojang.brigadier.exceptions.CommandSyntaxException;
 
 import net.minecraft.command.argument.EntityArgumentType;
+import net.minecraft.entity.Entity;
 import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.server.command.CommandManager;
 import net.minecraft.server.command.ServerCommandSource;
@@ -22,45 +22,48 @@ class GravityCommand {
         return CommandManager.literal("gravity").requires(s -> s.hasPermissionLevel(2))
             .then(CommandManager.literal("get")
                             .executes(context -> get(context.getSource(), context.getSource().getPlayer(), true))
-                   .then(CommandManager.argument("target", EntityArgumentType.player())
-                            .executes(context -> get(context.getSource(), EntityArgumentType.getPlayer(context, "target"), false))
+                   .then(CommandManager.argument("target", EntityArgumentType.entity())
+                            .executes(context -> get(context.getSource(), EntityArgumentType.getEntity(context, "target"), false))
                     ))
             .then(CommandManager.literal("set")
                    .then(CommandManager.argument("gravity", FloatArgumentType.floatArg(-99, 99))
-                               .executes(context -> set(context.getSource(), context.getSource().getPlayer(), FloatArgumentType.getFloat(context, "gravity"), true))
-                   .then(CommandManager.argument("target", EntityArgumentType.player())
-                               .executes(context -> set(context.getSource(), EntityArgumentType.getPlayer(context, "target"), FloatArgumentType.getFloat(context, "gravity"), false))
+                               .executes(context -> set(context.getSource(), List.of(context.getSource().getPlayer()), FloatArgumentType.getFloat(context, "gravity"), true))
+                   .then(CommandManager.argument("target", EntityArgumentType.entities())
+                               .executes(context -> set(context.getSource(), EntityArgumentType.getEntities(context, "target"), FloatArgumentType.getFloat(context, "gravity"), false))
                    )));
     }
 
-    static int get(ServerCommandSource source, PlayerEntity player, boolean isSelf) throws CommandSyntaxException {
-        sendFeedback(source, player, "get", false, Pony.of(player).getPhysics().getGravityModifier());
-        return 0;
-    }
+    static int get(ServerCommandSource source, Entity target, boolean isSelf) throws CommandSyntaxException {
+        Living<?> l = Living.living(target);
 
-    static int set(ServerCommandSource source, PlayerEntity player, float gravity, boolean isSelf) {
-
-        Pony iplayer = Pony.of(player);
-
-        iplayer.getPhysics().setBaseGravityModifier(gravity);
-        iplayer.setDirty();
-
-        sendFeedback(source, player, "set", true, gravity);
-        return 0;
-    }
-
-
-    static void sendFeedback(ServerCommandSource source, PlayerEntity player, String key, boolean notifyTarget, Object...arguments) {
-        String translationKey = "commands.gravity." + key;
-
-        if (source.getEntity() == player) {
-            source.sendFeedback(() -> Text.translatable(translationKey + ".self", arguments), true);
+        float gravity = l == null ? 1 : l.getPhysics().getGravityModifier();
+        if (source.getEntity() == target) {
+            source.sendFeedback(() -> Text.translatable("commands.gravity.get.self", gravity), true);
         } else {
-            if (notifyTarget && source.getWorld().getGameRules().getBoolean(GameRules.SEND_COMMAND_FEEDBACK)) {
-                player.sendMessage(Text.translatable(translationKey, arguments));
-            }
-
-            source.sendFeedback(() -> Text.translatable(translationKey + ".other", Streams.concat(Stream.of(player.getDisplayName()), Arrays.stream(arguments)).toArray()), true);
+            source.sendFeedback(() -> Text.translatable("commands.gravity.get.other", target.getDisplayName(), gravity), true);
         }
+        return 0;
+    }
+
+    static int set(ServerCommandSource source, Collection<? extends Entity> targets, float gravity, boolean isSelf) {
+        List<Entity> affected = targets.stream().map(Living::living).filter(Objects::nonNull).map(l -> {
+            l.getPhysics().setBaseGravityModifier(gravity);
+            l.setDirty();
+            if (l.asEntity() instanceof PlayerEntity player) {
+                if (source.getEntity() == player) {
+                    player.sendMessage(Text.translatable("commands.gravity.set.self", gravity));
+                } else if (source.getWorld().getGameRules().getBoolean(GameRules.SEND_COMMAND_FEEDBACK)) {
+                    player.sendMessage(Text.translatable("commands.gravity.set.other", l.asEntity().getDisplayName(), gravity));
+                }
+            }
+            return (Entity)l.asEntity();
+        }).toList();
+
+        if (affected.size() == 1) {
+            source.sendFeedback(() -> Text.translatable("commands.gravity.set.other", affected.get(0).getDisplayName()), true);
+        } else {
+            source.sendFeedback(() -> Text.translatable("commands.gravity.set.multiple", affected.size()), true);
+        }
+        return 0;
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/EntityPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/EntityPhysics.java
index 538e7e4a..749ee3cf 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/EntityPhysics.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/EntityPhysics.java
@@ -4,18 +4,13 @@ import com.minelittlepony.unicopia.entity.mob.UEntityAttributes;
 import com.minelittlepony.unicopia.util.Copyable;
 import com.minelittlepony.unicopia.util.Tickable;
 
-import net.minecraft.block.BlockRenderType;
 import net.minecraft.block.BlockState;
 import net.minecraft.block.FenceGateBlock;
 import net.minecraft.entity.Entity;
-import net.minecraft.entity.EntityPose;
 import net.minecraft.entity.LivingEntity;
-import net.minecraft.entity.MovementType;
 import net.minecraft.entity.data.TrackedData;
 import net.minecraft.entity.mob.MobEntity;
 import net.minecraft.nbt.NbtCompound;
-import net.minecraft.particle.BlockStateParticleEffect;
-import net.minecraft.particle.ParticleTypes;
 import net.minecraft.registry.tag.BlockTags;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.math.MathHelper;
@@ -37,22 +32,12 @@ public class EntityPhysics<T extends Entity> implements Physics, Copyable<Entity
     @Override
     public void tick() {
         if (isGravityNegative()) {
-            if (isGravityNegative() && !entity.isSneaking() && entity.isInSneakingPose()) {
-                float currentHeight = entity.getDimensions(entity.getPose()).height;
-                float sneakingHeight = entity.getDimensions(EntityPose.STANDING).height;
-
-                entity.move(MovementType.SELF, new Vec3d(0, -(currentHeight - sneakingHeight), 0));
-                entity.setPose(EntityPose.STANDING);
-            }
-
             if (entity.getY() > entity.getWorld().getHeight() + 64) {
                 entity.damage(entity.getDamageSources().outOfWorld(), 4.0F);
             }
-
-            entity.setOnGround(entity.verticalCollision && entity.getVelocity().getY() > 0);
         }
 
-        float gravity = this.getGravityModifier();
+        float gravity = getGravityModifier();
         if (gravity != lastGravity) {
             lastGravity = gravity;
 
@@ -87,8 +72,6 @@ public class EntityPhysics<T extends Entity> implements Physics, Copyable<Entity
     @Override
     public BlockPos getHeadPosition() {
 
-        entity.setOnGround(false);
-
         BlockPos pos = new BlockPos(
                 MathHelper.floor(entity.getX()),
                 MathHelper.floor(entity.getY() + entity.getHeight() + 0.20000000298023224D),
@@ -99,29 +82,13 @@ public class EntityPhysics<T extends Entity> implements Physics, Copyable<Entity
             BlockPos below = pos.down();
             BlockState block = entity.getWorld().getBlockState(below);
             if (block.isIn(BlockTags.FENCES) || block.isIn(BlockTags.WALLS) || block.getBlock() instanceof FenceGateBlock) {
-                entity.setOnGround(true);
                 return below;
             }
-        } else {
-            entity.setOnGround(true);
         }
 
         return pos;
     }
 
-    @Override
-    public void spawnSprintingParticles() {
-        BlockState state = entity.getWorld().getBlockState(getHeadPosition());
-        if (state.getRenderType() != BlockRenderType.INVISIBLE) {
-            Vec3d vel = entity.getVelocity();
-            entity.getWorld().addParticle(new BlockStateParticleEffect(ParticleTypes.BLOCK, state),
-                    entity.getX() + (entity.getWorld().random.nextFloat() - 0.5D) * entity.getWidth(),
-                    entity.getY() + entity.getHeight() - 0.1D,
-                    entity.getZ() + (entity.getWorld().random.nextFloat() - 0.5D) * entity.getWidth(),
-                    vel.x * -4, -1.5D, vel.z * -4);
-        }
-    }
-
     @Override
     public void setBaseGravityModifier(float constant) {
         entity.getDataTracker().set(gravity, constant);
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Living.java b/src/main/java/com/minelittlepony/unicopia/entity/Living.java
index 947a33be..84dce958 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/Living.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/Living.java
@@ -476,12 +476,6 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
         return false;
     }
 
-    public void onJump() {
-        if (getPhysics().isGravityNegative()) {
-            entity.setVelocity(entity.getVelocity().multiply(1, -1, 1));
-        }
-    }
-
     @Nullable
     public final Caster<?> getAttacker() {
         return attacker;
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Physics.java b/src/main/java/com/minelittlepony/unicopia/entity/Physics.java
index c7aff817..bbc2d1ce 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/Physics.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/Physics.java
@@ -21,8 +21,6 @@ public interface Physics extends NbtSerialisable {
 
     BlockPos getHeadPosition();
 
-    void spawnSprintingParticles();
-
     default boolean isGravityNegative() {
         return getGravityModifier() < 0;
     }
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerDimensions.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerDimensions.java
index 70eee553..a2e5a329 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerDimensions.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerDimensions.java
@@ -25,11 +25,7 @@ public final class PlayerDimensions {
                 .or(() -> physics.isFlyingSurvival ? FLYING_EYE_HEIGHT : physics.isGravityNegative() ? Optional.of(dimensions.height) : Optional.empty())
                 .map(h -> {
                     if (physics.isGravityNegative()) {
-                        if (pony.asEntity().isSneaking()) {
-                            h += 0.2F;
-                        }
-
-                        return dimensions.height - h;
+                        return dimensions.height - h + 0.1F;
                     }
                     return h;
                 });
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java
index f6c82b63..8ac41371 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java
@@ -301,10 +301,6 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
             if (entity.isOnGround() || (!creative && entity.horizontalCollision)) {
                 cancelFlight(false);
             }
-
-            if (entity.isClimbing() && (entity.horizontalCollision || ((LivingEntityDuck)entity).isJumping())) {
-                velocity.y = -0.2F;
-            }
         }
 
         isFlyingSurvival = entity.getAbilities().flying && !creative;
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 3cb7f3cf..0c78666f 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java
@@ -631,7 +631,7 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
             float max = 0.6F;
             return Optional.of(new Vec3d(
                     MathHelper.clamp(speed.x * factor, -max, max),
-                    speed.y * ((speed.y * getPhysics().getGravitySignum()) > 0 ? 1.2 : 1.101),
+                    speed.y * (speed.y > 0 ? 1.2 : 1.101),
                     MathHelper.clamp(speed.z * factor, -max, max)
             ));
         }
@@ -715,11 +715,8 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
     }
 
     public Optional<Float> onImpact(float distance, float damageMultiplier, DamageSource cause) {
-
         float originalDistance = distance;
 
-        distance *= gravity.getGravityModifier();
-
         boolean extraProtection = getSpellSlot().get(SpellType.SHIELD, false).isPresent();
 
         if (!entity.isCreative() && !entity.isSpectator()) {
diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java
index b1e811b0..7d41f099 100644
--- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java
+++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLivingEntity.java
@@ -128,11 +128,6 @@ abstract class MixinLivingEntity extends Entity implements LivingEntityDuck, Equ
         get().adjustMovementSpeedInWater(info.getReturnValue()).ifPresent(info::setReturnValue);
     }
 
-    @Inject(method = "jump()V", at = @At("RETURN"))
-    private void onJump(CallbackInfo info) {
-        get().onJump();
-    }
-
     @Inject(method = "tick()V", at = @At("HEAD"), cancellable = true)
     private void beforeTick(CallbackInfo info) {
         if (get().beforeUpdate()) {
@@ -186,21 +181,4 @@ abstract class MixinLivingEntity extends Entity implements LivingEntityDuck, Equ
             setLivingFlag(2, hand == Hand.OFF_HAND);
         }
     }
-
-    @Override
-    public BlockPos getBlockPos() {
-        if (get().getPhysics().isGravityNegative()) {
-            return get().getPhysics().getHeadPosition();
-        }
-        return super.getBlockPos();
-    }
-
-    @Override
-    protected void spawnSprintingParticles() {
-        if (get().getPhysics().isGravityNegative()) {
-            get().getPhysics().spawnSprintingParticles();
-        } else {
-            super.spawnSprintingParticles();
-        }
-    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinKeyboardInput.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinKeyboardInput.java
index 55c8a70b..6b4240d3 100644
--- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinKeyboardInput.java
+++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinKeyboardInput.java
@@ -27,12 +27,6 @@ abstract class MixinKeyboardInput extends Input {
                 pressingRight = tmp;
 
                 movementSideways = -movementSideways;
-
-                /*if (player.asEntity().getAbilities().flying && !player.getPhysics().isFlying()) {
-                    tmp = jumping;
-                    jumping = sneaking;
-                    sneaking = tmp;
-                }*/
             }
 
             if (EffectUtils.getAmplifier(MinecraftClient.getInstance().player, UEffects.PARALYSIS) > 1) {
diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinEntity.java
index c0725d94..fe2eef70 100644
--- a/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinEntity.java
+++ b/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinEntity.java
@@ -5,6 +5,7 @@ import org.spongepowered.asm.mixin.Mixin;
 import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.At.Shift;
 import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.ModifyArg;
 import org.spongepowered.asm.mixin.injection.ModifyVariable;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -12,7 +13,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
 import com.minelittlepony.unicopia.entity.Equine;
 
 import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityPose;
 import net.minecraft.entity.MovementType;
+import net.minecraft.util.math.Box;
 import net.minecraft.util.math.Vec3d;
 
 @Mixin(value = Entity.class, priority = 29000)
@@ -21,7 +24,7 @@ abstract class MixinEntity {
     // we invert y when moving
     @ModifyVariable(method = "move", at = @At("HEAD"), argsOnly = true)
     private Vec3d modifyMovement(Vec3d movement) {
-        if (this instanceof Equine.Container eq && eq.get().getPhysics().isGravityNegative()) {
+        if (unicopiaIsGravityInverted()) {
             return movement.multiply(1, -1, 1);
         }
         return movement;
@@ -35,14 +38,6 @@ abstract class MixinEntity {
         }
     }
 
-    // invert jumping velocity so we can jump
-    @Inject(method = "getJumpVelocityMultiplier", at = @At("RETURN"), cancellable = true)
-    private void onGetJumpVelocityMultiplier(CallbackInfoReturnable<Float> info) {
-        if (this instanceof Equine.Container eq && eq.get().getPhysics().isGravityNegative()) {
-            info.setReturnValue(-info.getReturnValue());
-        }
-    }
-
     // invert offsets so it can properly find the block we're walking on
     @ModifyVariable(method = "getPosWithYOffset", at = @At("HEAD"), argsOnly = true)
     private float onGetPosWithYOffset(float offset) {
@@ -53,12 +48,27 @@ abstract class MixinEntity {
     }
 
     // fix sprinting particles
-    @Inject(method = "spawnSprintingParticles", at = @At("HEAD"), cancellable = true)
-    protected void spawnSprintingParticles(CallbackInfo info) {
+    @ModifyArg(method = "spawnSprintingParticles",
+            at = @At(value = "INVOKE",
+                target = "net/minecraft/world/World.addParticle(Lnet/minecraft/particle/ParticleEffect;DDDDDD)V"),
+            index = 2)
+    private double modifyParticleY(double y) {
         if (this instanceof Equine.Container eq && eq.get().getPhysics().isGravityNegative()) {
-            eq.get().getPhysics().spawnSprintingParticles();
-            info.cancel();
+            Entity self = eq.get().asEntity();
+            return self.getHeight() - y + (self.getY() * 2);
         }
+        return y;
+    }
+
+    // fix fall damage
+    @ModifyArg(
+            method = "move",
+            at = @At(value = "INVOKE", target = "net/minecraft/entity/Entity.fall(DZLnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;)V"))
+    private double modifyFallDistance(double heightDifference) {
+        if (unicopiaIsGravityInverted()) {
+            return -heightDifference;
+        }
+        return heightDifference;
     }
 
     // invert check for walking up a step
@@ -68,9 +78,43 @@ abstract class MixinEntity {
             argsOnly = true)
 
     private static Vec3d modifyMovementForStepheight(Vec3d movement, @Nullable Entity entity) {
-        if (entity != null && movement.getY() == entity.getStepHeight()) {
+        if (entity instanceof Equine.Container eq && eq.get().getPhysics().isGravityNegative() && movement.getY() == entity.getStepHeight()) {
             return movement.multiply(1, -1, 1);
         }
         return movement;
     }
+
+    @Inject(method = {"calculateBoundsForPose"}, at = @At("RETURN"), cancellable = true)
+    private void adjustPoseBoxForGravity(EntityPose pos, CallbackInfoReturnable<Box> info) {
+        unicopiaReAnchorBoundingBox(info);
+    }
+
+    @Inject(method = {"calculateBoundingBox"}, at = @At("RETURN"), cancellable = true)
+    private void adjustPoseBoxForGravity(CallbackInfoReturnable<Box> info) {
+        if (unicopiaReAnchorBoundingBox(info)) {
+            Entity self = (Entity)(Object)this;
+            self.setPos(self.getX(), info.getReturnValue().minY, self.getZ());
+        }
+    }
+
+    private boolean unicopiaReAnchorBoundingBox(CallbackInfoReturnable<Box> info) {
+        if (this instanceof Equine.Container eq && eq.get().getPhysics().isGravityNegative()) {
+            Entity self = eq.get().asEntity();
+            Box box = info.getReturnValue();
+            Box oldBox = self.getBoundingBox();
+            double newHeight = box.getYLength();
+            if (newHeight > oldBox.getYLength()) {
+                double targetMaxY = oldBox.maxY;
+                Vec3d min = new Vec3d(box.minX, targetMaxY - newHeight, box.minZ);
+                Vec3d max = new Vec3d(box.maxX, targetMaxY, box.maxZ);
+                info.setReturnValue(new Box(min, max));
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean unicopiaIsGravityInverted() {
+        return this instanceof Equine.Container eq && eq.get().getPhysics().isGravityNegative();
+    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinLivingEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinLivingEntity.java
index 2d9dfe6c..368eb0d8 100644
--- a/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinLivingEntity.java
+++ b/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinLivingEntity.java
@@ -1,11 +1,15 @@
 package com.minelittlepony.unicopia.mixin.gravity;
 
 import org.spongepowered.asm.mixin.*;
+import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Constant;
+import org.spongepowered.asm.mixin.injection.ModifyArg;
 import org.spongepowered.asm.mixin.injection.ModifyConstant;
 import com.minelittlepony.unicopia.entity.*;
+
 import net.minecraft.entity.Entity;
 import net.minecraft.entity.LivingEntity;
+import net.minecraft.util.math.BlockPos;
 
 @Mixin(LivingEntity.class)
 abstract class MixinLivingEntity extends Entity implements Equine.Container<Living<?>> {
@@ -19,4 +23,23 @@ abstract class MixinLivingEntity extends Entity implements Equine.Container<Livi
     private double modifyGravity(double initial) {
         return Math.abs(get().getPhysics().calcGravity(initial));
     }
+
+    @ModifyArg(method = "fall",
+            at = @At(value = "INVOKE",
+                target = "net/minecraft/server/world/ServerWorld.spawnParticles(Lnet/minecraft/particle/ParticleEffect;DDDIDDDD)I"),
+            index = 2)
+    private double modifyParticleY(double y) {
+        if (get().getPhysics().isGravityNegative()) {
+            return y + getHeight();
+        }
+        return y;
+    }
+
+    @Override
+    public BlockPos getBlockPos() {
+        if (get().getPhysics().isGravityNegative()) {
+            return get().getPhysics().getHeadPosition();
+        }
+        return super.getBlockPos();
+    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinServerPlayNetworkHandler.java b/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinServerPlayNetworkHandler.java
new file mode 100644
index 00000000..5c3b5452
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinServerPlayNetworkHandler.java
@@ -0,0 +1,29 @@
+package com.minelittlepony.unicopia.mixin.gravity;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+import com.minelittlepony.unicopia.entity.player.Pony;
+
+import net.minecraft.network.listener.ServerPlayPacketListener;
+import net.minecraft.network.listener.TickablePacketListener;
+import net.minecraft.server.network.ServerPlayNetworkHandler;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.server.world.EntityTrackingListener;
+
+@Mixin(ServerPlayNetworkHandler.class)
+abstract class MixinServerPlayNetworkHandler
+implements EntityTrackingListener,
+TickablePacketListener,
+ServerPlayPacketListener {
+    @Shadow public ServerPlayerEntity player;
+
+    @ModifyVariable(method = "onPlayerMove", at = @At("STORE"), ordinal = 0)
+    private boolean flipLandingFlag(boolean value) {
+        if (Pony.of(this.player).getPhysics().isGravityNegative()) {
+            return !value;
+        }
+        return value;
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinServerPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinServerPlayerEntity.java
new file mode 100644
index 00000000..77783d4b
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinServerPlayerEntity.java
@@ -0,0 +1,24 @@
+package com.minelittlepony.unicopia.mixin.gravity;
+
+import org.spongepowered.asm.mixin.*;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+
+import com.minelittlepony.unicopia.entity.Equine;
+
+import net.minecraft.server.network.ServerPlayerEntity;
+
+@Mixin(ServerPlayerEntity.class)
+abstract class MixinServerPlayerEntity {
+    @ModifyVariable(
+            method = "handleFall(DDDZ)V",
+            at = @At("HEAD"),
+            ordinal = 1,
+            argsOnly = true)
+    private double modifyFallDistance(double value) {
+        if (this instanceof Equine.Container eq && eq.get().getPhysics().isGravityNegative()) {
+            return -value;
+        }
+        return value;
+    }
+}
diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json
index a83cc748..0941798f 100644
--- a/src/main/resources/assets/unicopia/lang/en_us.json
+++ b/src/main/resources/assets/unicopia/lang/en_us.json
@@ -1383,6 +1383,7 @@
   "commands.gravity.set": "Your gravity has been updated to %f",
   "commands.gravity.set.self": "Set own gravity to %f",
   "commands.gravity.set.other": "Set %s's gravity to %f",
+  "commands.gravity.set.multiple": "Updated %s entities",
 
   "unicopia.options.title": "Unicopia Options",
   "unicopia.options.ignore_mine_lp": "Ignore Mine Little Pony",
diff --git a/src/main/resources/unicopia.mixin.json b/src/main/resources/unicopia.mixin.json
index 0d34a3bf..4f3227b4 100644
--- a/src/main/resources/unicopia.mixin.json
+++ b/src/main/resources/unicopia.mixin.json
@@ -59,6 +59,8 @@
     "gravity.MixinMobEntity",
     "gravity.MixinWorld",
     "gravity.MixinServerWorld",
+    "gravity.MixinServerPlayerEntity",
+    "gravity.MixinServerPlayNetworkHandler",
     "gravity.MixinWorldChunk",
     "trinkets.MixinTrinketSurvivalSlot",
     "trinkets.MixinTrinketItem",

From 5cfebba12e9e67bc3d9b5f0b6d1a27ea174aadb9 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Tue, 26 Mar 2024 19:05:57 +0000
Subject: [PATCH 03/11] Fix quilt crash

---
 src/main/java/com/minelittlepony/unicopia/block/UBlocks.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java
index 32e48568..c5779abd 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/UBlocks.java
@@ -330,7 +330,7 @@ public interface UBlocks {
         FlammableBlockRegistry.getDefaultInstance().add(BANANAS, 5, 20);
         FlammableBlockRegistry.getDefaultInstance().add(CURING_JOKE, 60, 100);
 
-        CompostingChanceRegistry.INSTANCE.add(WORM_BLOCK, 8F);
+        CompostingChanceRegistry.INSTANCE.add(WORM_BLOCK, 1F);
 
         UBlockEntities.bootstrap();
         EdibleBlock.bootstrap();

From 4a630dfcf911afeffeea1864102ebb6edccee9a7 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Tue, 26 Mar 2024 20:21:12 +0000
Subject: [PATCH 04/11] Fix slime blocks in the upside down

---
 .../mixin/gravity/MixinPistonBlockEntity.java | 46 +++++++++++++++++++
 src/main/resources/unicopia.mixin.json        |  1 +
 2 files changed, 47 insertions(+)
 create mode 100644 src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinPistonBlockEntity.java

diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinPistonBlockEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinPistonBlockEntity.java
new file mode 100644
index 00000000..279047c7
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinPistonBlockEntity.java
@@ -0,0 +1,46 @@
+package com.minelittlepony.unicopia.mixin.gravity;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import net.minecraft.block.entity.PistonBlockEntity;
+import net.minecraft.entity.Entity;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Box;
+import net.minecraft.util.math.Direction;
+import net.minecraft.world.World;
+
+@Mixin(PistonBlockEntity.class)
+abstract class MixinPistonBlockEntity {
+    @Shadow
+    private static Box offsetHeadBox(BlockPos pos, Box box, PistonBlockEntity blockEntity) {
+        return box;
+    }
+
+    @Shadow
+    private static boolean canMoveEntity(Box box, Entity entity, BlockPos pos) {
+        return false;
+    }
+
+    @Shadow
+    private static void moveEntity(Direction direction, Entity entity, double distance, Direction movementDirection) {
+
+    }
+
+    @Inject(method = "moveEntitiesInHoneyBlock", at = @At("TAIL"))
+    private static void moveEntitiesInHoneyBlock(World world, BlockPos pos, float ticks, PistonBlockEntity tile, CallbackInfo info) {
+        Direction direction = tile.getMovementDirection();
+        if (!direction.getAxis().isHorizontal()) {
+            return;
+        }
+        double blockY = tile.getPushedBlock().getCollisionShape(world, pos).getMin(Direction.Axis.Y);
+        Box box = offsetHeadBox(pos, new Box(0, blockY - 1.5000010000000001F, 0, 1, blockY, 1), tile);
+        double distance = ticks - tile.getProgress(1);
+        for (Entity entity2 : world.getOtherEntities(null, box, entity -> canMoveEntity(box, entity, pos))) {
+            moveEntity(direction, entity2, distance, direction);
+        }
+    }
+}
diff --git a/src/main/resources/unicopia.mixin.json b/src/main/resources/unicopia.mixin.json
index 4f3227b4..530d0226 100644
--- a/src/main/resources/unicopia.mixin.json
+++ b/src/main/resources/unicopia.mixin.json
@@ -58,6 +58,7 @@
     "gravity.MixinLivingEntity",
     "gravity.MixinMobEntity",
     "gravity.MixinWorld",
+    "gravity.MixinPistonBlockEntity",
     "gravity.MixinServerWorld",
     "gravity.MixinServerPlayerEntity",
     "gravity.MixinServerPlayNetworkHandler",

From dd8bf650d55d7a49c7e298bc41c2deac5c815b38 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Tue, 26 Mar 2024 20:46:19 +0000
Subject: [PATCH 05/11] #168 Add speed boosts when performing stunts and add a
 stunt for flying at low altitude.

---
 .../entity/player/FlightStuntUtil.java        | 19 +++++
 .../unicopia/entity/player/PlayerPhysics.java | 73 ++++++++++++-------
 .../unicopia/util/MutableVector.java          | 18 +++++
 3 files changed, 85 insertions(+), 25 deletions(-)
 create mode 100644 src/main/java/com/minelittlepony/unicopia/entity/player/FlightStuntUtil.java

diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/FlightStuntUtil.java b/src/main/java/com/minelittlepony/unicopia/entity/player/FlightStuntUtil.java
new file mode 100644
index 00000000..c5975dbe
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/entity/player/FlightStuntUtil.java
@@ -0,0 +1,19 @@
+package com.minelittlepony.unicopia.entity.player;
+
+import com.minelittlepony.unicopia.entity.mob.StormCloudEntity;
+import com.minelittlepony.unicopia.util.MutableVector;
+
+import net.minecraft.util.math.BlockPos;
+
+public class FlightStuntUtil {
+    public static boolean isPerformingDive(Pony pony, MutableVector velocity) {
+        double horizontalSpeed = velocity.horizontalLengthSquared();
+        double verticalSpeed = velocity.y;
+        return horizontalSpeed != 0 && verticalSpeed < -0.3F && (verticalSpeed / horizontalSpeed) < -0.3F;
+    }
+
+    public static boolean isFlyingLow(Pony pony, MutableVector velocity) {
+        BlockPos pos = pony.asEntity().getBlockPos();
+        return velocity.horizontalLengthSquared() > 0.005F && (pos.getY() - StormCloudEntity.findSurfaceBelow(pony.asWorld(), pos).getY()) < 6;
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java
index 8ac41371..b4db56d0 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java
@@ -60,6 +60,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
     private int ticksInAir;
     private int ticksToGlide;
     private int ticksDiving;
+    private int ticksFlyingLow;
 
     private float thrustScale = 0;
     private float prevThrustScale;
@@ -335,28 +336,9 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
                     velocity.y /= 2F;
                 }
 
-                double horizontalSpeed = this.getHorizontalMotion();
-                double verticalSpeed = velocity.y;
-
-                if (Abilities.RAINBOOM.canUse(pony.getCompositeRace()) && horizontalSpeed != 0 && verticalSpeed < -0.3F && (verticalSpeed / horizontalSpeed) < -0.3F) {
-                    ticksDiving++;
-                } else {
-                    ticksDiving = 0;
-                }
-
-                if (ticksDiving > 0 && ticksDiving % 25 == 0) {
-                    pony.getMagicalReserves().getCharge().addPercent(12.5F);
-                }
+                tickStunts(velocity);
             } else {
-                prevStrafe = 0;
-                strafe = 0;
-                ticksInAir = 0;
-                wallHitCooldown = MAX_WALL_HIT_CALLDOWN;
-                soundPlaying = false;
-                descentRate = 0;
-                ticksDiving = 0;
-                updraft.update(0, 100);
-                windStrength.update(0, 100);
+                tickGrounded();
 
                 if (Abilities.RAINBOOM.canUse(pony.getCompositeRace()) && entity.isOnGround()) {
                     pony.getMagicalReserves().getCharge().set(0);
@@ -367,10 +349,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
                 }
             }
         } else {
-            descentRate = 0;
-            soundPlaying = false;
-            updraft.update(0, 100);
-            windStrength.update(0, 100);
+            tickGrounded();
         }
 
         if (!entity.isOnGround()) {
@@ -379,6 +358,10 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
             velocity.z /= heavyness;
         }
 
+        float maximum = 1.5F;
+        velocity.x = MathHelper.clamp(velocity.x, -maximum, maximum);
+        velocity.y = MathHelper.clamp(velocity.y, -maximum, maximum);
+        velocity.z = MathHelper.clamp(velocity.z, -maximum, maximum);
         entity.setVelocity(velocity.toImmutable());
 
         if (isFlying() && !entity.isFallFlying() && !pony.getAcrobatics().isHanging() && pony.isClient()) {
@@ -397,6 +380,46 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
         }
     }
 
+    private void tickGrounded() {
+        prevStrafe = 0;
+        strafe = 0;
+        ticksInAir = 0;
+        wallHitCooldown = MAX_WALL_HIT_CALLDOWN;
+        soundPlaying = false;
+        descentRate = 0;
+        ticksDiving = 0;
+        ticksFlyingLow = 0;
+        updraft.update(0, 0);
+        windStrength.update(0, 0);
+    }
+
+    private void tickStunts(MutableVector velocity) {
+        boolean canPerformStunts = Abilities.RAINBOOM.canUse(pony.getCompositeRace());
+        ticksDiving = canPerformStunts && FlightStuntUtil.isPerformingDive(pony, velocity) ? (ticksDiving + 1) : 0;
+        ticksFlyingLow = canPerformStunts && FlightStuntUtil.isFlyingLow(pony, velocity) ? (ticksFlyingLow + 1) : 0;
+
+        if (ticksDiving > 0) {
+            float horDiveScale = MathHelper.clamp(ticksDiving / 100F, 0, 10);
+            float verDiveScale = MathHelper.clamp(ticksDiving / 90F, 0, 5);
+            velocity.multiply(1 + horDiveScale, 1 + verDiveScale, 1 + horDiveScale);
+        }
+
+        if (ticksFlyingLow > 0) {
+            float horDiveScale = MathHelper.clamp(ticksFlyingLow / 1000F, 0, 0.9F);
+            float verDiveScale = MathHelper.clamp(ticksFlyingLow / 900F, 0, 0.5F);
+            velocity.multiply(1 + horDiveScale, 1 + verDiveScale, 1 + horDiveScale);
+        }
+
+        if (pony.asEntity().age % 2 == 0) {
+            if (ticksDiving > 0) {
+                pony.getMagicalReserves().getCharge().addPercent(1F);
+            }
+            if (ticksFlyingLow > 0) {
+                pony.getMagicalReserves().getCharge().addPercent(ticksFlyingLow / 200F);
+            }
+        }
+    }
+
     private void tickFlight(FlightType type, MutableVector velocity) {
         if (type.isArtifical()) {
             tickArtificialFlight(velocity);
diff --git a/src/main/java/com/minelittlepony/unicopia/util/MutableVector.java b/src/main/java/com/minelittlepony/unicopia/util/MutableVector.java
index c76e0209..8ea4aac6 100644
--- a/src/main/java/com/minelittlepony/unicopia/util/MutableVector.java
+++ b/src/main/java/com/minelittlepony/unicopia/util/MutableVector.java
@@ -20,6 +20,12 @@ public class MutableVector {
         z = vec.z;
     }
 
+    public void multiply(double x, double y, double z) {
+        this.x *= x;
+        this.y *= y;
+        this.z *= z;
+    }
+
     public void add(Vec3d vector) {
         add(vector.x, vector.y, vector.z);
     }
@@ -38,6 +44,18 @@ public class MutableVector {
         this.z += z;
     }
 
+    public double horizontalLengthSquared() {
+        return x * x + z * z;
+    }
+
+    public double verticalLengthSquared() {
+        return y * y;
+    }
+
+    public double lengthSquared() {
+        return verticalLengthSquared() + horizontalLengthSquared();
+    }
+
     public Vec3d toImmutable() {
         return new Vec3d(x, y, z);
     }

From 2ae28c72d3f222487407a12acaf65096382dba10 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Tue, 26 Mar 2024 22:00:43 +0000
Subject: [PATCH 06/11] Fix edge sneaking when upside down

---
 .../mixin/gravity/MixinPlayerEntity.java      | 43 +++++++++++++++++++
 src/main/resources/unicopia.mixin.json        |  1 +
 2 files changed, 44 insertions(+)
 create mode 100644 src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinPlayerEntity.java

diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinPlayerEntity.java
new file mode 100644
index 00000000..e6b8b24b
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/mixin/gravity/MixinPlayerEntity.java
@@ -0,0 +1,43 @@
+package com.minelittlepony.unicopia.mixin.gravity;
+
+import org.spongepowered.asm.mixin.*;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.ModifyArg;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+import com.minelittlepony.unicopia.entity.Equine;
+
+import net.minecraft.entity.MovementType;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.util.math.Vec3d;
+
+@Mixin(PlayerEntity.class)
+abstract class MixinPlayerEntity {
+    @ModifyVariable(method = "adjustMovementForSneaking", at = @At("HEAD"), argsOnly = true)
+    private Vec3d flipMovementForSneaking(Vec3d movement) {
+        if (this instanceof Equine.Container eq && eq.get().getPhysics().isGravityNegative()) {
+            return movement.multiply(1, -1, 1);
+        }
+        return movement;
+    }
+
+    @Inject(method = "adjustMovementForSneaking", at = @At("RETURN"), cancellable = true)
+    private void unflipMovementForSneaking(Vec3d movement, MovementType type, CallbackInfoReturnable<Vec3d> info) {
+        if (this instanceof Equine.Container eq && eq.get().getPhysics().isGravityNegative()) {
+            info.setReturnValue(info.getReturnValue().multiply(1, -1, 1));
+        }
+    }
+
+    @ModifyArg(method = { "adjustMovementForSneaking", "method_30263" }, at = @At(
+            value = "INVOKE",
+            target = "net/minecraft/util/math/Box.offset(DDD)Lnet/minecraft/util/math/Box;"),
+            index = 1)
+    private double invertStepHeight(double stepHeight) {
+        if (this instanceof Equine.Container eq && eq.get().getPhysics().isGravityNegative()) {
+            return -stepHeight;
+        }
+        return stepHeight;
+    }
+}
diff --git a/src/main/resources/unicopia.mixin.json b/src/main/resources/unicopia.mixin.json
index 530d0226..0a82eae8 100644
--- a/src/main/resources/unicopia.mixin.json
+++ b/src/main/resources/unicopia.mixin.json
@@ -58,6 +58,7 @@
     "gravity.MixinLivingEntity",
     "gravity.MixinMobEntity",
     "gravity.MixinWorld",
+    "gravity.MixinPlayerEntity",
     "gravity.MixinPistonBlockEntity",
     "gravity.MixinServerWorld",
     "gravity.MixinServerPlayerEntity",

From f84f45c47cf7624b6112be5993521907ecd2e640 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Wed, 27 Mar 2024 19:20:33 +0000
Subject: [PATCH 07/11] Run datagen before publish

---
 .github/workflows/gradle-publish.yml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/.github/workflows/gradle-publish.yml b/.github/workflows/gradle-publish.yml
index e93b8b24..ab6a77f9 100644
--- a/.github/workflows/gradle-publish.yml
+++ b/.github/workflows/gradle-publish.yml
@@ -17,6 +17,10 @@ jobs:
         uses: actions/setup-java@v1
         with:
           java-version: 17
+      - name: Prepare Datagen
+        uses: eskatos/gradle-command-action@v1
+        with:
+          arguments: rundatagen
       - name: Publish Modrinth Jar
         env:
           MODRINTH_KEY: ${{ secrets.MODRINTH_KEY }}

From e02c6f57a08d04e0095b8965b2efc3fa80b1fe3d Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Wed, 27 Mar 2024 19:22:11 +0000
Subject: [PATCH 08/11] Bump blockus

---
 BlockusAddon | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/BlockusAddon b/BlockusAddon
index ec6ea81e..7170edad 160000
--- a/BlockusAddon
+++ b/BlockusAddon
@@ -1 +1 @@
-Subproject commit ec6ea81ed5f0862ab2dfa6201c66504287e3ef76
+Subproject commit 7170edad67426756e2383bd9464a8615e9bb4b3a

From e931be3388b34154ac70d2a3fa49caca725cb85a Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Wed, 27 Mar 2024 19:23:16 +0000
Subject: [PATCH 09/11] You can now harvest pinecones and acorns from spruce
 and oak leaves using a hoe

---
 .../unicopia/InteractionManager.java          |  6 ++
 .../client/ClientInteractionManager.java      |  7 ++
 .../unicopia/item/ForageableItem.java         | 93 +++++++++++++++++++
 .../minelittlepony/unicopia/item/UItems.java  |  5 +-
 4 files changed, 109 insertions(+), 2 deletions(-)
 create mode 100644 src/main/java/com/minelittlepony/unicopia/item/ForageableItem.java

diff --git a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java
index 7c60d0d5..e0848e1a 100644
--- a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java
+++ b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java
@@ -14,6 +14,8 @@ import net.minecraft.entity.Entity;
 import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.network.PacketByteBuf;
 import net.minecraft.util.Identifier;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Direction;
 import net.minecraft.world.World;
 
 public class InteractionManager {
@@ -90,4 +92,8 @@ public class InteractionManager {
     public void sendPlayerLookAngles(PlayerEntity player) {
 
     }
+
+    public void addBlockBreakingParticles(BlockPos pos, Direction direction) {
+
+    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java
index 837b65fd..13352054 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java
@@ -41,6 +41,8 @@ import net.minecraft.network.PacketByteBuf;
 import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;
 import net.minecraft.sound.SoundCategory;
 import net.minecraft.util.Identifier;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Direction;
 import net.minecraft.util.math.random.Random;
 import net.minecraft.world.World;
 
@@ -161,4 +163,9 @@ public class ClientInteractionManager extends InteractionManager {
             c.networkHandler.sendPacket(new PlayerMoveC2SPacket.LookAndOnGround(player.getYaw(), player.getPitch(), player.isOnGround()));
         }
     }
+
+    @Override
+    public void addBlockBreakingParticles(BlockPos pos, Direction direction) {
+        client.particleManager.addBlockBreakingParticles(pos, direction);
+    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/item/ForageableItem.java b/src/main/java/com/minelittlepony/unicopia/item/ForageableItem.java
new file mode 100644
index 00000000..bb129cf6
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/item/ForageableItem.java
@@ -0,0 +1,93 @@
+package com.minelittlepony.unicopia.item;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+import com.minelittlepony.unicopia.InteractionManager;
+import com.minelittlepony.unicopia.item.enchantment.EnchantmentUtil;
+import com.minelittlepony.unicopia.server.world.BlockDestructionManager;
+
+import net.fabricmc.fabric.api.event.player.UseBlockCallback;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.enchantment.EnchantmentHelper;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.HoeItem;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.ToolMaterials;
+import net.minecraft.registry.tag.BlockTags;
+import net.minecraft.registry.tag.ItemTags;
+import net.minecraft.sound.SoundCategory;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.Hand;
+import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.world.World;
+import net.minecraft.world.WorldEvents;
+
+public class ForageableItem extends Item {
+    private static final List<ForageableItem> REGISTRY = new ArrayList<>();
+    static {
+        UseBlockCallback.EVENT.register((PlayerEntity player, World world, Hand hand, BlockHitResult hitResult) -> {
+            if (player.shouldCancelInteraction()) {
+                return ActionResult.PASS;
+            }
+
+            ItemStack stack = player.getStackInHand(hand);
+            if (!stack.isIn(ItemTags.HOES)) {
+                return ActionResult.PASS;
+            }
+
+            BlockPos pos = hitResult.getBlockPos();
+            BlockState state = world.getBlockState(pos);
+
+            ActionResult result = ActionResult.PASS;
+
+            if (state.isIn(BlockTags.LEAVES)) {
+                player.swingHand(hand);
+                world.playSound(player, pos, state.getSoundGroup().getHitSound(), SoundCategory.BLOCKS);
+                InteractionManager.instance().addBlockBreakingParticles(pos, hitResult.getSide());
+
+                int miningLevel = (stack.getItem() instanceof HoeItem hoe ? hoe.getMaterial().getMiningLevel() : 59);
+
+                for (ForageableItem item : REGISTRY) {
+                    if ((result = item.onTryForage(world, pos, state, stack, player, miningLevel)).isAccepted()) {
+                        stack.damage(1, player, p -> p.sendToolBreakStatus(hand));
+                        return result;
+                    }
+                }
+            }
+
+            return result.isAccepted() ? ActionResult.SUCCESS : ActionResult.PASS;
+        });
+    }
+
+    private final Supplier<Block> targetBlock;
+
+    public ForageableItem(Settings settings, Supplier<Block> targetBlock) {
+        super(settings);
+        this.targetBlock = targetBlock;
+        REGISTRY.add(this);
+    }
+
+    public ActionResult onTryForage(World world, BlockPos pos, BlockState state, ItemStack stack, PlayerEntity player, int miningLevel) {
+        if (state.isOf(targetBlock.get())) {
+            int spawnChance = (int)((1F - MathHelper.clamp(miningLevel / (float)ToolMaterials.NETHERITE.getMiningLevel(), 0, 1)) * 32);
+            spawnChance -= EnchantmentUtil.getLuck(1, player);
+
+            if (spawnChance <= 0 || world.random.nextInt(spawnChance) == 0) {
+                Block.dropStack(world, pos, new ItemStack(this, 1 + EnchantmentHelper.getLooting(player)));
+                world.syncWorldEvent(WorldEvents.BLOCK_BROKEN, pos, Block.getRawIdFromState(state));
+                if (BlockDestructionManager.of(world).damageBlock(pos, world.getRandom().nextBetween(3, 7)) >= BlockDestructionManager.MAX_DAMAGE) {
+                    world.breakBlock(pos, true);
+                }
+                return ActionResult.SUCCESS;
+            }
+            return ActionResult.FAIL;
+        }
+        return ActionResult.PASS;
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
index b75f9a6f..0c8fdaab 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
@@ -16,6 +16,7 @@ import com.terraformersmc.terraform.boat.api.TerraformBoatType;
 import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry;
 import com.terraformersmc.terraform.boat.api.item.TerraformBoatItemHelper;
 
+import net.minecraft.block.Blocks;
 import net.minecraft.entity.attribute.EntityAttributeModifier;
 import net.minecraft.entity.attribute.EntityAttributes;
 import net.minecraft.entity.vehicle.BoatEntity;
@@ -85,8 +86,8 @@ public interface UItems {
 
     Item WHEAT_WORMS = register("wheat_worms", new Item(new Item.Settings().maxCount(16).food(UFoodComponents.WORMS)), ItemGroups.NATURAL);
     Item MUFFIN = register("muffin", new MuffinItem(new Item.Settings().maxCount(32).food(FoodComponents.BREAD), 0), ItemGroups.FOOD_AND_DRINK);
-    Item PINECONE = register("pinecone", new Item(new Item.Settings().food(UFoodComponents.PINECONE).maxCount(3)), ItemGroups.FOOD_AND_DRINK);
-    Item ACORN = register("acorn", new Item(new Item.Settings().food(UFoodComponents.ACORN).maxCount(16)), ItemGroups.FOOD_AND_DRINK);
+    Item PINECONE = register("pinecone", new ForageableItem(new Item.Settings().food(UFoodComponents.PINECONE).maxCount(16), () -> Blocks.SPRUCE_LEAVES), ItemGroups.FOOD_AND_DRINK);
+    Item ACORN = register("acorn", new ForageableItem(new Item.Settings().food(UFoodComponents.ACORN).maxCount(16), () -> Blocks.OAK_LEAVES), ItemGroups.FOOD_AND_DRINK);
     Item MANGO = register("mango", new Item(new Item.Settings().food(UFoodComponents.MANGO)), ItemGroups.FOOD_AND_DRINK);
     Item BANANA = register("banana", new Item(new Item.Settings().food(UFoodComponents.BANANA)), ItemGroups.FOOD_AND_DRINK);
     Item CURING_JOKE = register("curing_joke", new CuringJokeItem(UBlocks.CURING_JOKE, new Item.Settings().food(UFoodComponents.POISON_JOKE)), ItemGroups.NATURAL);

From c0a64a80f11ea961693711e82b590334a3125624 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Wed, 27 Mar 2024 19:23:39 +0000
Subject: [PATCH 10/11] Fixed eating rock stew also consuming the bowl

---
 src/main/java/com/minelittlepony/unicopia/item/UItems.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
index 0c8fdaab..1f036829 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
@@ -102,7 +102,7 @@ public interface UItems {
     Item TOM = register("tom", new BluntWeaponItem(new Item.Settings(), ImmutableMultimap.of(
             EntityAttributes.GENERIC_KNOCKBACK_RESISTANCE, new EntityAttributeModifier(BluntWeaponItem.KNOCKBACK_MODIFIER_ID, "Weapon modifier", 0.9, EntityAttributeModifier.Operation.ADDITION)
     )), ItemGroups.NATURAL);
-    Item ROCK_STEW = register("rock_stew", new Item(new Item.Settings().food(FoodComponents.MUSHROOM_STEW)), ItemGroups.FOOD_AND_DRINK);
+    Item ROCK_STEW = register("rock_stew", new StewItem(new Item.Settings().food(FoodComponents.MUSHROOM_STEW).recipeRemainder(Items.BOWL)), ItemGroups.FOOD_AND_DRINK);
     Item ROCK_CANDY = register("rock_candy", new Item(new Item.Settings().food(UFoodComponents.CANDY).maxCount(16)), ItemGroups.FOOD_AND_DRINK);
     Item SALT_CUBE = register("salt_cube", new Item(new Item.Settings().food(UFoodComponents.SALT_CUBE)), ItemGroups.FOOD_AND_DRINK);
 

From 7cf32cce521699ed37c03c5cdf5636fc425fcfbd Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Wed, 27 Mar 2024 19:24:06 +0000
Subject: [PATCH 11/11] Added bowl of nuts, oatmeal cookie, and pinecone cookie

---
 .../datagen/providers/UModelProvider.java     |   6 ++---
 .../UBlockAdditionsLootTableProvider.java     |  22 +++++++++++++-----
 .../providers/recipe/URecipeProvider.java     |  11 +++++++++
 .../minelittlepony/unicopia/item/UItems.java  |   4 ++++
 .../resources/assets/unicopia/lang/en_us.json |   3 +++
 .../unicopia/textures/item/bowl_of_nuts.png   | Bin 0 -> 8620 bytes
 .../unicopia/textures/item/oatmeal_cookie.png | Bin 0 -> 9087 bytes
 .../textures/item/pinecone_cookie.png         | Bin 0 -> 9118 bytes
 .../tags/items/food_types/baked_goods.json    |   3 +++
 .../tags/items/groups/earth_pony.json         |   3 +++
 10 files changed, 43 insertions(+), 9 deletions(-)
 create mode 100644 src/main/resources/assets/unicopia/textures/item/bowl_of_nuts.png
 create mode 100644 src/main/resources/assets/unicopia/textures/item/oatmeal_cookie.png
 create mode 100644 src/main/resources/assets/unicopia/textures/item/pinecone_cookie.png

diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UModelProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UModelProvider.java
index 64bbc4be..1c58e4f7 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UModelProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UModelProvider.java
@@ -57,7 +57,7 @@ public class UModelProvider extends FabricModelProvider {
     public void generateItemModels(ItemModelGenerator itemModelGenerator) {
         ItemModels.register(itemModelGenerator,
                 UItems.ACORN, UItems.APPLE_PIE_HOOF, UItems.APPLE_PIE_SLICE, UItems.APPLE_PIE,
-                UItems.BANANA, UItems.BOTCHED_GEM, UItems.BROKEN_SUNGLASSES, UItems.BURNED_JUICE, UItems.BURNED_TOAST,
+                UItems.BANANA, UItems.BOTCHED_GEM, UItems.BOWL_OF_NUTS, UItems.BROKEN_SUNGLASSES, UItems.BURNED_JUICE, UItems.BURNED_TOAST,
                 UItems.CARAPACE, UItems.CLAM_SHELL, UItems.COOKED_ZAP_APPLE, UItems.CLOUD_LUMP, UItems.CRISPY_HAY_FRIES, UItems.CRYSTAL_HEART, UItems.CRYSTAL_SHARD,
                 UItems.DAFFODIL_DAISY_SANDWICH, UItems.DRAGON_BREATH_SCROLL,
                 UItems.EMPTY_JAR,
@@ -69,8 +69,8 @@ public class UModelProvider extends FabricModelProvider {
                 UItems.JAM_TOAST, UItems.JUICE,
                 UItems.LIGHTNING_JAR,
                 UItems.MANGO, UItems.MUFFIN,
-                UItems.OATMEAL,
-                UItems.PEBBLES, UItems.PEGASUS_FEATHER, UItems.PINECONE, UItems.PINEAPPLE_CROWN,
+                UItems.OATMEAL, UItems.OATMEAL_COOKIE,
+                UItems.PEBBLES, UItems.PEGASUS_FEATHER, UItems.PINECONE, UItems.PINECONE_COOKIE, UItems.PINEAPPLE_CROWN,
                 UItems.RAIN_CLOUD_JAR, UItems.ROCK_STEW, UItems.ROCK, UItems.ROTTEN_APPLE,
                 UItems.SALT_CUBE, UItems.SCALLOP_SHELL, UItems.SHELLY, UItems.SOUR_APPLE_SEEDS, UItems.SOUR_APPLE, UItems.SPELLBOOK, UItems.STORM_CLOUD_JAR,
                     UItems.SWEET_APPLE_SEEDS, UItems.SWEET_APPLE,
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockAdditionsLootTableProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockAdditionsLootTableProvider.java
index 9788e234..4663bd66 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockAdditionsLootTableProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockAdditionsLootTableProvider.java
@@ -11,6 +11,7 @@ import net.minecraft.block.Block;
 import net.minecraft.block.Blocks;
 import net.minecraft.data.server.loottable.BlockLootTableGenerator;
 import net.minecraft.enchantment.Enchantments;
+import net.minecraft.item.ItemConvertible;
 import net.minecraft.loot.LootPool;
 import net.minecraft.loot.LootTable;
 import net.minecraft.loot.condition.LootCondition;
@@ -54,18 +55,29 @@ public class UBlockAdditionsLootTableProvider extends FabricBlockLootTableProvid
         addVanillaDrop(Blocks.PODZOL, block -> wormDrops(block, 4, 0.06F, 0.062222223F, 0.065F, 0.077777776F, 0.2F));
         addVanillaDrop(Blocks.DIAMOND_ORE, this::crystalShardDrops);
         addVanillaDrop(Blocks.DEEPSLATE_DIAMOND_ORE, this::crystalShardDrops);
+        addVanillaDrop(Blocks.OAK_LEAVES, block -> chanceDropWithShears(block, UItems.ACORN, GEMSTONES_FORTUNE_CHANCE));
+        addVanillaDrop(Blocks.SPRUCE_LEAVES, block -> chanceDropWithShears(block, UItems.PINECONE, GEMSTONES_FORTUNE_CHANCE));
     }
 
     private void addVanillaDrop(Block block, Function<Block, LootTable.Builder> lootTableFunction) {
         lootTables.put(new Identifier("unicopiamc", block.getLootTableId().getPath()), lootTableFunction.apply(block));
     }
 
+    public LootTable.Builder chanceDropWithShears(Block block, ItemConvertible drop, float...chance) {
+        return LootTable.builder()
+                .pool(LootPool.builder()
+                        .rolls(ConstantLootNumberProvider.create(1))
+                        .conditionally(WITHOUT_SILK_TOUCH.and(WITH_SHEARS))
+                        .with(chanceDrops(block, drop, 1, chance))
+                );
+    }
+
     public LootTable.Builder wormDrops(Block block, int max, float...chance) {
         return LootTable.builder()
                 .pool(LootPool.builder()
                         .rolls(ConstantLootNumberProvider.create(1))
                         .conditionally(WITHOUT_SILK_TOUCH)
-                        .with(wheatwormDrops(block, max, chance))
+                        .with(chanceDrops(block, UItems.WHEAT_WORMS, max, chance))
                 );
     }
 
@@ -75,7 +87,7 @@ public class UBlockAdditionsLootTableProvider extends FabricBlockLootTableProvid
                         .rolls(ConstantLootNumberProvider.create(1))
                         .conditionally(WITHOUT_SILK_TOUCH)
                         .with(gemstoneDrops(block, 0.1F))
-                        .with(wheatwormDrops(block, max, chance))
+                        .with(chanceDrops(block, UItems.WHEAT_WORMS, max, chance))
                 );
     }
 
@@ -111,16 +123,14 @@ public class UBlockAdditionsLootTableProvider extends FabricBlockLootTableProvid
             .conditionally(TableBonusLootCondition.builder(Enchantments.FORTUNE, GEMSTONES_FORTUNE_CHANCE));
     }
 
-    public LootPoolEntry.Builder<?> wheatwormDrops(Block block, int max, float...chance) {
-        return applyExplosionDecay(block, ItemEntry.builder(UItems.WHEAT_WORMS)
+    public LootPoolEntry.Builder<?> chanceDrops(Block block, ItemConvertible drop, int max, float...chance) {
+        return applyExplosionDecay(block, ItemEntry.builder(drop)
                 .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, max)))
             )
             .conditionally(TableBonusLootCondition.builder(Enchantments.FORTUNE, chance));
     }
 
-
     public static LootTable.Builder dropsWithGemfinding(Block drop, LootPoolEntry.Builder<?> child) {
         return BlockLootTableGenerator.drops(drop, WITHOUT_SILK_TOUCH_AND_GEM_FINDER, child);
     }
-
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/URecipeProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/URecipeProvider.java
index ed82fb66..57340e26 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/URecipeProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/URecipeProvider.java
@@ -315,6 +315,17 @@ public class URecipeProvider extends FabricRecipeProvider {
             .input(UItems.ROCK, 3).criterion(hasItem(UItems.ROCK), conditionsFromItem(UItems.ROCK))
             .input(Items.BOWL)
             .offerTo(exporter);
+        ShapelessRecipeJsonBuilder.create(RecipeCategory.FOOD, UItems.BOWL_OF_NUTS)
+            .input(UItems.ACORN, 3).criterion(hasItem(UItems.ACORN), conditionsFromItem(UItems.ACORN))
+            .input(Items.BOWL)
+            .offerTo(exporter);
+        ShapelessRecipeJsonBuilder.create(RecipeCategory.FOOD, UItems.OATMEAL_COOKIE)
+            .input(UItems.OATS, 3).criterion(hasItem(UItems.OATS), conditionsFromItem(UItems.OATS))
+            .offerTo(exporter);
+        ShapelessRecipeJsonBuilder.create(RecipeCategory.FOOD, UItems.PINECONE_COOKIE)
+            .input(UItems.PINECONE).criterion(hasItem(UItems.PINECONE), conditionsFromItem(UItems.PINECONE))
+            .input(Items.WHEAT, 2)
+            .offerTo(exporter);
         ShapelessRecipeJsonBuilder.create(RecipeCategory.FOOD, UItems.ROCK_CANDY, 3)
             .input(Items.SUGAR, 6).criterion(hasItem(Items.SUGAR), conditionsFromItem(Items.SUGAR))
             .input(UItems.PEBBLES, 3)
diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
index 1f036829..ce60d14b 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
@@ -75,6 +75,10 @@ public interface UItems {
     Item IMPORTED_OATS = register("imported_oats", new Item(new Item.Settings().food(UFoodComponents.IMPORTED_OATS)), ItemGroups.FOOD_AND_DRINK);
     Item OATMEAL = register("oatmeal", new OatmealItem(new Item.Settings().recipeRemainder(Items.BOWL).maxCount(1).food(UFoodComponents.OATMEAL)), ItemGroups.FOOD_AND_DRINK);
 
+    Item OATMEAL_COOKIE = register("oatmeal_cookie", new Item(new Item.Settings().food(FoodComponents.COOKIE)), ItemGroups.FOOD_AND_DRINK);
+    Item PINECONE_COOKIE = register("pinecone_cookie", new Item(new Item.Settings().food(FoodComponents.COOKIE)), ItemGroups.FOOD_AND_DRINK);
+    Item BOWL_OF_NUTS = register("bowl_of_nuts", new StewItem(new Item.Settings().food(FoodComponents.BAKED_POTATO).recipeRemainder(Items.BOWL)), ItemGroups.FOOD_AND_DRINK);
+
     Item DAFFODIL_DAISY_SANDWICH = register("daffodil_daisy_sandwich", new Item(new Item.Settings().food(UFoodComponents.DAFODIL_DAISY_SANDWICH)), ItemGroups.FOOD_AND_DRINK);
     Item HAY_BURGER = register("hay_burger", new Item(new Item.Settings().maxCount(1).food(UFoodComponents.HAY_BURGER)), ItemGroups.FOOD_AND_DRINK);
     Item HAY_FRIES = register("hay_fries", new Item(new Item.Settings().maxCount(16).food(UFoodComponents.HAY_FRIES)), ItemGroups.FOOD_AND_DRINK);
diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json
index 0941798f..8558c7b3 100644
--- a/src/main/resources/assets/unicopia/lang/en_us.json
+++ b/src/main/resources/assets/unicopia/lang/en_us.json
@@ -146,6 +146,9 @@
   "item.unicopia.horse_shoe_fries": "Horse Shoe Fries",
   "item.unicopia.wheat_worms": "Wheat Worms",
   "item.unicopia.muffin": "Muffin",
+  "item.unicopia.oatmeal_cookie": "Oatmeal Cookie",
+  "item.unicopia.pinecone_cookie": "Pinecone Cookie",
+  "item.unicopia.bowl_of_nuts": "Bowl of Nuts",
 
   "item.unicopia.pegasus_amulet": "Wings of Icarus",
   "item.unicopia.pegasus_amulet.lore": "Grants temporary flight to whoever wears it",
diff --git a/src/main/resources/assets/unicopia/textures/item/bowl_of_nuts.png b/src/main/resources/assets/unicopia/textures/item/bowl_of_nuts.png
new file mode 100644
index 0000000000000000000000000000000000000000..8e684446c117429bfb1a6b04ce495bbffd916317
GIT binary patch
literal 8620
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
Z1Z)@0k=!EON)o-*I*~Tm725d6{S$aW#=ign

literal 0
HcmV?d00001

diff --git a/src/main/resources/assets/unicopia/textures/item/oatmeal_cookie.png b/src/main/resources/assets/unicopia/textures/item/oatmeal_cookie.png
new file mode 100644
index 0000000000000000000000000000000000000000..daa46c80f62cfae0c47d488bae33a9d8a0f1d036
GIT binary patch
literal 9087
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
R271I`OI>eSw$?lT-vGR4fU^Jq

literal 0
HcmV?d00001

diff --git a/src/main/resources/assets/unicopia/textures/item/pinecone_cookie.png b/src/main/resources/assets/unicopia/textures/item/pinecone_cookie.png
new file mode 100644
index 0000000000000000000000000000000000000000..79fb1ed76ed999e863ea7a0f4878139271f7d9f6
GIT binary patch
literal 9118
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
KbD?8!{NDhhiccW`

literal 0
HcmV?d00001

diff --git a/src/main/resources/data/unicopia/tags/items/food_types/baked_goods.json b/src/main/resources/data/unicopia/tags/items/food_types/baked_goods.json
index 02828b2e..b28e7389 100644
--- a/src/main/resources/data/unicopia/tags/items/food_types/baked_goods.json
+++ b/src/main/resources/data/unicopia/tags/items/food_types/baked_goods.json
@@ -17,6 +17,9 @@
     "unicopia:hay_fries",
     "unicopia:crispy_hay_fries",
     "unicopia:horse_shoe_fries",
+    "unicopia:oatmeal_cookie",
+    "unicopia:pinecone_cookie",
+    "unicopia:bowl_of_nuts",
     { "id": "farmersdelight:wheat_dough", "required": false },
     { "id": "farmersdelight:raw_pasta", "required": false },
     { "id": "farmersdelight:pie_crust", "required": false },
diff --git a/src/main/resources/data/unicopia/tags/items/groups/earth_pony.json b/src/main/resources/data/unicopia/tags/items/groups/earth_pony.json
index d88b92b7..e3ec6fc3 100644
--- a/src/main/resources/data/unicopia/tags/items/groups/earth_pony.json
+++ b/src/main/resources/data/unicopia/tags/items/groups/earth_pony.json
@@ -51,6 +51,7 @@
     "unicopia:oats",
     "unicopia:imported_oats",
     "unicopia:oatmeal",
+    "unicopia:oatmeal_cookie",
     "unicopia:daffodil_daisy_sandwich",
     "unicopia:hay_burger",
     "unicopia:hay_fries",
@@ -61,6 +62,8 @@
     "unicopia:muffin",
     "unicopia:acorn",
     "unicopia:pinecone",
+    "unicopia:pinecone_cookie",
+    "unicopia:bowl_of_nuts",
     "unicopia:crystal_shard",
     "unicopia:pebbles",
     "unicopia:rock",