From 2784627adcfdfd261a1012c650d9c187262a5933 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Sat, 30 Mar 2024 21:22:36 +0000
Subject: [PATCH 01/21] Fix bugs and allow for storing bucketed entities in
 jars

---
 .../unicopia/block/ItemJarBlock.java          | 325 ++++++++++++++----
 .../unicopia/block/JarBlock.java              |  38 +-
 .../unicopia/client/render/RenderUtil.java    |  72 ++++
 .../entity/ItemJarBlockEntityRenderer.java    |  93 ++++-
 .../unicopia/mixin/MixinEntityBucketItem.java |  16 +
 .../unicopia/util/NbtSerialisable.java        |   3 +
 .../unicopia/util/PosHelper.java              |   2 +-
 src/main/resources/unicopia.mixin.json        |   1 +
 8 files changed, 474 insertions(+), 76 deletions(-)
 create mode 100644 src/main/java/com/minelittlepony/unicopia/mixin/MixinEntityBucketItem.java

diff --git a/src/main/java/com/minelittlepony/unicopia/block/ItemJarBlock.java b/src/main/java/com/minelittlepony/unicopia/block/ItemJarBlock.java
index 68c9b9ba..1c726165 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/ItemJarBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/ItemJarBlock.java
@@ -2,18 +2,38 @@ package com.minelittlepony.unicopia.block;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
+import org.jetbrains.annotations.Nullable;
+
+import com.minelittlepony.unicopia.mixin.MixinEntityBucketItem;
+import com.minelittlepony.unicopia.util.NbtSerialisable;
+
 import net.minecraft.block.BlockEntityProvider;
 import net.minecraft.block.BlockRenderType;
 import net.minecraft.block.BlockState;
+import net.minecraft.block.Blocks;
 import net.minecraft.block.InventoryProvider;
 import net.minecraft.block.entity.BlockEntity;
+import net.minecraft.entity.Bucketable;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityType;
 import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.inventory.SidedInventory;
 import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.nbt.NbtElement;
+import net.minecraft.network.listener.ClientPlayPacketListener;
+import net.minecraft.network.packet.Packet;
+import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
+import net.minecraft.registry.Registries;
+import net.minecraft.server.world.ServerWorld;
 import net.minecraft.util.ActionResult;
 import net.minecraft.util.Hand;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.TypedActionResult;
 import net.minecraft.util.hit.BlockHitResult;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.math.Direction;
@@ -36,14 +56,7 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
         if (hand == Hand.OFF_HAND) {
             return ActionResult.PASS;
         }
-        return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR).map(data -> {
-            ItemStack stack = player.getStackInHand(hand);
-            if (stack.isEmpty()) {
-                return data.removeItem(world, pos);
-            }
-
-            return data.insertItem(world, pos, player.isCreative() ? stack.copyWithCount(1) : stack.split(1));
-        }).orElse(ActionResult.PASS);
+        return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR).map(data -> data.interact(player, hand)).orElse(ActionResult.PASS);
     }
 
     @Deprecated
@@ -51,9 +64,7 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
     public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
         if (!moved && !state.isOf(newState.getBlock())) {
             world.getBlockEntity(pos, UBlockEntities.ITEM_JAR).ifPresent(data -> {
-                data.getStacks().forEach(stack -> {
-                    dropStack(world, pos, stack);
-                });
+                data.getContents().onDestroyed();
             });
         }
         super.onStateReplaced(state, world, pos, newState, moved);
@@ -66,7 +77,10 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
 
     @Override
     public int getComparatorOutput(BlockState state, World world, BlockPos pos) {
-        return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR).map(data -> Math.min(16, data.getStacks().size())).orElse(0);
+        return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR)
+                .map(TileData::getItems)
+                .map(data -> Math.min(16, data.getStacks().size()))
+                .orElse(0);
     }
 
     @Deprecated
@@ -85,34 +99,210 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
 
     @Override
     public SidedInventory getInventory(BlockState state, WorldAccess world, BlockPos pos) {
-        return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR).orElse(null);
+        return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR).map(TileData::getItems).orElse(null);
     }
 
-    public static class TileData extends BlockEntity implements SidedInventory {
-        private static final int[] SLOTS = IntStream.range(0, 16).toArray();
-        private final List<ItemStack> stacks = new ArrayList<>();
+    public static class TileData extends BlockEntity {
+
+        private JarContents contents = new ItemsJarContents(this);
 
         public TileData(BlockPos pos, BlockState state) {
             super(UBlockEntities.ITEM_JAR, pos, state);
         }
 
-        public ActionResult insertItem(World world, BlockPos pos, ItemStack stack) {
-            if (stacks.size() >= size()) {
-                return ActionResult.FAIL;
-            }
-            stacks.add(stack);
-            markDirty();
-
-            return ActionResult.SUCCESS;
+        public ActionResult interact(PlayerEntity player, Hand hand) {
+            TypedActionResult<JarContents> result = contents.interact(player, hand);
+            contents = result.getValue();
+            return result.getResult();
         }
 
-        public ActionResult removeItem(World world, BlockPos pos) {
-            if (stacks.isEmpty()) {
-                return ActionResult.FAIL;
+        public JarContents getContents() {
+            return contents;
+        }
+
+        @Nullable
+        public ItemsJarContents getItems() {
+            return getContents() instanceof ItemsJarContents c ? c : null;
+        }
+
+        @Nullable
+        public EntityJarContents getEntity() {
+            return getContents() instanceof EntityJarContents c ? c : null;
+        }
+
+        @Override
+        public Packet<ClientPlayPacketListener> toUpdatePacket() {
+            return BlockEntityUpdateS2CPacket.create(this);
+        }
+
+        @Override
+        public NbtCompound toInitialChunkDataNbt() {
+            return createNbt();
+        }
+
+        @Override
+        public void markDirty() {
+            super.markDirty();
+            if (getWorld() instanceof ServerWorld sw) {
+                sw.getChunkManager().markForUpdate(getPos());
             }
-            dropStack(world, pos, stacks.remove(0));
+        }
+
+        @Override
+        public void readNbt(NbtCompound nbt) {
+            if (nbt.contains("items", NbtElement.COMPOUND_TYPE)) {
+                contents = new ItemsJarContents(this);
+                contents.fromNBT(nbt.getCompound("items"));
+            } else if (nbt.contains("entity", NbtElement.COMPOUND_TYPE)) {
+                contents = new EntityJarContents(this);
+                contents.fromNBT(nbt.getCompound("entity"));
+            }
+        }
+
+        @Override
+        protected void writeNbt(NbtCompound nbt) {
+            var items = getItems();
+            if (items != null) {
+                nbt.put("items", items.toNBT());
+            } else if (getEntity() != null) {
+                nbt.put("entity", getEntity().toNBT());
+            }
+        }
+    }
+
+
+    public interface JarContents extends NbtSerialisable {
+        TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand);
+
+        void onDestroyed();
+    }
+
+    public static class EntityJarContents implements JarContents {
+        @Nullable
+        private EntityType<?> entityType;
+        @Nullable
+        private Entity renderEntity;
+
+        private final TileData tile;
+
+        public EntityJarContents(TileData tile) {
+            this(tile, null);
+        }
+
+        public EntityJarContents(TileData tile, EntityType<?> entityType) {
+            this.tile = tile;
+            this.entityType = entityType;
+        }
+
+        @Nullable
+        public Entity getOrCreateEntity() {
+            if (entityType == null && tile.getWorld() != null) {
+                return null;
+            }
+
+            if (renderEntity == null || renderEntity.getType() != entityType) {
+                renderEntity = entityType.create(tile.getWorld());
+            }
+            return renderEntity;
+        }
+
+        @Override
+        public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
+            ItemStack stack = player.getStackInHand(hand);
+            if (stack.isOf(Items.BUCKET)) {
+                if (getOrCreateEntity() instanceof Bucketable bucketable) {
+                    if (!player.isCreative()) {
+                        stack.decrement(1);
+                        if (stack.isEmpty()) {
+                            player.setStackInHand(hand, bucketable.getBucketItem());
+                        } else {
+                            player.giveItemStack(bucketable.getBucketItem());
+                        }
+                    }
+                    player.playSound(bucketable.getBucketFillSound(), 1, 1);
+                }
+                tile.markDirty();
+                return TypedActionResult.success(new ItemsJarContents(tile));
+            }
+            return TypedActionResult.pass(this);
+        }
+
+        @Override
+        public void onDestroyed() {
+            tile.getWorld().setBlockState(tile.getPos(), Blocks.WATER.getDefaultState());
+            Entity entity = getOrCreateEntity();
+            if (entity != null) {
+                entity.refreshPositionAfterTeleport(tile.getPos().toCenterPos());
+                tile.getWorld().spawnEntity(entity);
+            }
+        }
+
+        @Override
+        public void toNBT(NbtCompound compound) {
+            compound.putString("entity", EntityType.getId(entityType).toString());
+        }
+
+        @Override
+        public void fromNBT(NbtCompound compound) {
+            entityType = Registries.ENTITY_TYPE.getOrEmpty(Identifier.tryParse(compound.getString("entity"))).orElse(null);
+        }
+
+    }
+
+    public static class ItemsJarContents implements JarContents, SidedInventory {
+        private static final int[] SLOTS = IntStream.range(0, 16).toArray();
+
+        private final TileData tile;
+        private List<ItemStack> stacks = new ArrayList<>();
+
+        public ItemsJarContents(TileData tile) {
+            this.tile = tile;
+        }
+
+        @Override
+        public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
+            ItemStack handStack = player.getStackInHand(hand);
+
+            if (handStack.isEmpty()) {
+                if (stacks.isEmpty()) {
+                    return TypedActionResult.fail(this);
+                }
+                dropStack(tile.getWorld(), tile.getPos(), stacks.remove(0));
+                markDirty();
+                return TypedActionResult.success(this);
+            }
+
+            if (stacks.isEmpty()) {
+                if (handStack.getItem() instanceof MixinEntityBucketItem bucket) {
+                    if (!player.isCreative()) {
+                        handStack.decrement(1);
+                        if (handStack.isEmpty()) {
+                            player.setStackInHand(hand, Items.BUCKET.getDefaultStack());
+                        } else {
+                            player.giveItemStack(Items.BUCKET.getDefaultStack());
+                        }
+                    }
+
+                    player.playSound(bucket.getEmptyingSound(), 1, 1);
+                    markDirty();
+                    return TypedActionResult.success(new EntityJarContents(tile, bucket.getEntityType()));
+                }
+            }
+
+            if (stacks.size() >= size()) {
+                return TypedActionResult.fail(this);
+            }
+            stacks.add(player.isCreative() ? handStack.copyWithCount(1) : handStack.split(1));
             markDirty();
-            return ActionResult.SUCCESS;
+
+            return TypedActionResult.success(this);
+        }
+
+        @Override
+        public void onDestroyed() {
+            stacks.forEach(stack -> {
+                dropStack(tile.getWorld(), tile.getPos(), stack);
+            });
         }
 
         public List<ItemStack> getStacks() {
@@ -137,51 +327,42 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
         @Override
         public ItemStack removeStack(int slot, int amount) {
             if (slot < 0 || slot >= stacks.size()) {
-                try {
-                    ItemStack stack = stacks.get(slot);
-                    ItemStack removed = stack.split(1);
-                    if (stack.isEmpty()) {
-                        stacks.remove(slot);
-                    }
-                    return removed;
-                } finally {
-                    markDirty();
-                }
+                return ItemStack.EMPTY;
+            }
+
+            try {
+                ItemStack stack = stacks.get(slot);
+                ItemStack removed = stack.split(1);
+                if (stack.isEmpty()) {
+                    stacks.remove(slot);
+                }
+                return removed;
+            } finally {
+                markDirty();
             }
-            return ItemStack.EMPTY;
         }
 
         @Override
         public ItemStack removeStack(int slot) {
             if (slot < 0 || slot >= stacks.size()) {
-                try {
-                    return stacks.remove(slot);
-                } finally {
-                    markDirty();
-                }
+                return ItemStack.EMPTY;
+            }
+
+            try {
+                return stacks.remove(slot);
+            } finally {
+                markDirty();
             }
-            return ItemStack.EMPTY;
         }
 
         @Override
         public void setStack(int slot, ItemStack stack) {
             if (slot >= stacks.size()) {
-                if (stacks.size() >= size()) {
-                    dropStack(getWorld(), getPos(), stack);
-                } else {
-                    stacks.add(stack);
-                }
+                stacks.add(stack);
             } else {
-                ItemStack existing = stacks.get(slot);
-                if (!ItemStack.canCombine(existing, stack)) {
-                    dropStack(getWorld(), getPos(), stack);
-                } else {
-                    existing.setCount(existing.getCount() + stack.split(Math.max(0, existing.getMaxCount() - existing.getCount())).getCount());
-                    if (!stack.isEmpty()) {
-                        dropStack(getWorld(), getPos(), stack);
-                    }
-                }
+                stacks.set(slot, stack);
             }
+            markDirty();
         }
 
         @Override
@@ -202,15 +383,29 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
 
         @Override
         public boolean canInsert(int slot, ItemStack stack, Direction dir) {
-            return (slot >= 0 && slot < size()) && (slot >= stacks.size() || (
-                    ItemStack.canCombine(stacks.get(slot), stack)
-                    && (stacks.get(slot).getCount() + stack.getCount()) <= Math.min(stacks.get(slot).getMaxCount(), stack.getMaxCount())
-            ));
+            return slot >= 0 && slot < size() && slot >= stacks.size();
         }
 
         @Override
         public boolean canExtract(int slot, ItemStack stack, Direction dir) {
-            return true;
+            return slot >= 0 && slot < size() && slot < stacks.size();
+        }
+
+        @Override
+        public void toNBT(NbtCompound compound) {
+            compound.put("items", NbtSerialisable.ITEM_STACK.writeAll(stacks));
+        }
+
+        @Override
+        public void fromNBT(NbtCompound compound) {
+            stacks = NbtSerialisable.ITEM_STACK.readAll(compound.getList("items", NbtElement.COMPOUND_TYPE))
+                    .limit(size())
+                    .collect(Collectors.toList());
+        }
+
+        @Override
+        public void markDirty() {
+            tile.markDirty();
         }
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/block/JarBlock.java b/src/main/java/com/minelittlepony/unicopia/block/JarBlock.java
index 80f24c7e..2bf5777b 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/JarBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/JarBlock.java
@@ -11,10 +11,17 @@ import net.minecraft.block.AbstractGlassBlock;
 import net.minecraft.block.Block;
 import net.minecraft.block.BlockState;
 import net.minecraft.block.ShapeContext;
+import net.minecraft.block.Waterloggable;
 import net.minecraft.block.entity.BlockEntity;
 import net.minecraft.enchantment.EnchantmentHelper;
 import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.fluid.FluidState;
+import net.minecraft.fluid.Fluids;
+import net.minecraft.item.ItemPlacementContext;
 import net.minecraft.item.ItemStack;
+import net.minecraft.state.StateManager;
+import net.minecraft.state.property.BooleanProperty;
+import net.minecraft.state.property.Properties;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.math.Direction;
 import net.minecraft.util.math.random.Random;
@@ -22,17 +29,25 @@ import net.minecraft.util.shape.VoxelShape;
 import net.minecraft.util.shape.VoxelShapes;
 import net.minecraft.world.BlockView;
 import net.minecraft.world.World;
+import net.minecraft.world.WorldAccess;
 import net.minecraft.world.explosion.Explosion;
 
-public class JarBlock extends AbstractGlassBlock {
+public class JarBlock extends AbstractGlassBlock implements Waterloggable {
     private static final VoxelShape SHAPE = VoxelShapes.union(
             Block.createCuboidShape(4, 0, 4, 12, 12, 12),
             Block.createCuboidShape(6, 12, 6, 10, 16, 10),
             Block.createCuboidShape(5, 13, 5, 11, 14, 11)
     );
+    private static final BooleanProperty WATERLOGGED = Properties.WATERLOGGED;
 
     public JarBlock(Settings settings) {
         super(settings);
+        setDefaultState(getDefaultState().with(WATERLOGGED, false));
+    }
+
+    @Override
+    protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
+        builder.add(WATERLOGGED);
     }
 
     @Override
@@ -40,9 +55,26 @@ public class JarBlock extends AbstractGlassBlock {
         return SHAPE;
     }
 
+    @Deprecated
     @Override
-    public boolean isSideInvisible(BlockState state, BlockState stateFrom, Direction direction) {
-        return super.isSideInvisible(state, stateFrom, direction);
+    public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighborState, WorldAccess world, BlockPos pos, BlockPos neighborPos) {
+        if (state.get(WATERLOGGED)) {
+            world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
+        }
+
+        return super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos);
+    }
+
+    @Nullable
+    @Override
+    public BlockState getPlacementState(ItemPlacementContext ctx) {
+        return getDefaultState().with(WATERLOGGED, ctx.getWorld().getFluidState(ctx.getBlockPos()).getFluid() == Fluids.WATER);
+    }
+
+    @Deprecated
+    @Override
+    public FluidState getFluidState(BlockState state) {
+        return state.get(WATERLOGGED) ? Fluids.WATER.getStill(false) : super.getFluidState(state);
     }
 
     @Override
diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java
index cb43a128..c2795b11 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java
@@ -5,10 +5,16 @@ import org.joml.Vector3f;
 import org.joml.Vector4f;
 
 import net.minecraft.client.render.BufferBuilder;
+import net.minecraft.client.render.RenderLayer;
 import net.minecraft.client.render.Tessellator;
+import net.minecraft.client.render.VertexConsumer;
+import net.minecraft.client.render.VertexConsumerProvider;
 import net.minecraft.client.render.VertexFormat;
 import net.minecraft.client.render.VertexFormats;
+import net.minecraft.client.texture.Sprite;
 import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.util.math.ColorHelper;
+import net.minecraft.util.math.Direction;
 
 public class RenderUtil {
     public static final Vector4f TEMP_VECTOR = new Vector4f();
@@ -25,6 +31,72 @@ public class RenderUtil {
             new Vertex(1, 0, 0, 1, 1),
             new Vertex(0, 0, 0, 0, 1)
     };
+    private static final Vertex[][] CUBE_VERTICES = {
+            new Vertex[] { // down
+                    new Vertex(0, 0, 0, 0, 0),
+                    new Vertex(1, 0, 0, 1, 0),
+                    new Vertex(1, 0, 1, 1, 1),
+                    new Vertex(0, 0, 1, 0, 1)
+            },
+            new Vertex[] { //up
+                    new Vertex(0, 1, 0, 0, 0),
+                    new Vertex(0, 1, 1, 0, 1),
+                    new Vertex(1, 1, 1, 1, 1),
+                    new Vertex(1, 1, 0, 1, 0)
+            },
+            new Vertex[] { //north
+                    new Vertex(0, 0, 0, 0, 0),
+                    new Vertex(0, 1, 0, 0, 1),
+                    new Vertex(1, 1, 0, 1, 1),
+                    new Vertex(1, 0, 0, 1, 0)
+            },
+            new Vertex[] { //south
+                    new Vertex(0, 0, 1, 0, 0),
+                    new Vertex(1, 0, 1, 1, 0),
+                    new Vertex(1, 1, 1, 1, 1),
+                    new Vertex(0, 1, 1, 0, 1)
+            },
+            new Vertex[] { //west
+                    new Vertex(0, 0, 0, 0, 0),
+                    new Vertex(0, 0, 1, 1, 0),
+                    new Vertex(0, 1, 1, 1, 1),
+                    new Vertex(0, 1, 0, 0, 1)
+            },
+            new Vertex[] { //east
+                    new Vertex(1, 0, 0, 0, 0),
+                    new Vertex(1, 1, 0, 1, 0),
+                    new Vertex(1, 1, 1, 1, 1),
+                    new Vertex(1, 0, 1, 0, 1)
+            }
+    };
+
+    public static void renderSpriteCubeFaces(MatrixStack matrices, VertexConsumerProvider provider, Sprite sprite,
+            float width, float height, float length,
+            int color, int light, int overlay,
+            Direction... directions) {
+        float r = ColorHelper.Abgr.getRed(color),
+            g = ColorHelper.Abgr.getGreen(color),
+            b = ColorHelper.Abgr.getBlue(color),
+            a = ColorHelper.Abgr.getAlpha(color);
+        float u0 = sprite.getMinU(), uDelta = sprite.getMaxU() - u0;
+        float v0 = sprite.getMinV(), vDelta = sprite.getMaxV() - v0;
+        RenderLayer layer = RenderLayer.getEntitySolid(sprite.getAtlasId());
+        VertexConsumer buffer = provider.getBuffer(layer);
+        Matrix4f position = matrices.peek().getPositionMatrix();
+        for (Direction direction : directions) {
+            for (Vertex vertex : CUBE_VERTICES[direction.ordinal()]) {
+                Vector4f pos = position.transform(TEMP_VECTOR.set(vertex.position(), 1).mul(width, height, length, 1));
+                buffer.vertex(
+                        pos.x, pos.y, pos.z,
+                        r, g, b, a,
+                        u0 + vertex.texture().x * uDelta,
+                        v0 + vertex.texture().y * vDelta,
+                        overlay, light,
+                        direction.getOffsetX(), direction.getOffsetY(), direction.getOffsetZ()
+                );
+            }
+        }
+    }
 
     public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) {
         renderFace(matrices, te, buffer, r, g, b, a, light, 1, 1);
diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/ItemJarBlockEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/ItemJarBlockEntityRenderer.java
index 85bec08a..e0b4883c 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/ItemJarBlockEntityRenderer.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/ItemJarBlockEntityRenderer.java
@@ -1,29 +1,61 @@
 package com.minelittlepony.unicopia.client.render.entity;
 
-import java.util.List;
-
 import com.minelittlepony.unicopia.block.ItemJarBlock;
+import com.minelittlepony.unicopia.block.ItemJarBlock.EntityJarContents;
+import com.minelittlepony.unicopia.block.ItemJarBlock.ItemsJarContents;
+import com.minelittlepony.unicopia.client.render.RenderUtil;
+import com.minelittlepony.unicopia.util.PosHelper;
 
+import net.minecraft.block.Blocks;
 import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.color.world.BiomeColors;
 import net.minecraft.client.render.VertexConsumerProvider;
 import net.minecraft.client.render.block.entity.BlockEntityRenderer;
 import net.minecraft.client.render.block.entity.BlockEntityRendererFactory;
+import net.minecraft.client.render.entity.EntityRenderDispatcher;
+import net.minecraft.client.render.item.ItemRenderer;
 import net.minecraft.client.render.model.json.ModelTransformationMode;
+import net.minecraft.client.texture.Sprite;
 import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.command.argument.EntityAnchorArgumentType.EntityAnchor;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.item.ItemStack;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.MathHelper;
 import net.minecraft.util.math.RotationAxis;
+import net.minecraft.util.math.Vec3d;
 import net.minecraft.util.math.random.Random;
+import net.minecraft.world.World;
 
 public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBlock.TileData> {
 
+    private final ItemRenderer itemRenderer;
+    private final EntityRenderDispatcher dispatcher;
+
+    private final Sprite waterSprite;
+
     public ItemJarBlockEntityRenderer(BlockEntityRendererFactory.Context ctx) {
+        itemRenderer = ctx.getItemRenderer();
+        dispatcher = ctx.getEntityRenderDispatcher();
+        waterSprite = MinecraftClient.getInstance().getBakedModelManager().getBlockModels().getModel(Blocks.WATER.getDefaultState()).getParticleSprite();
     }
 
     @Override
-    public void render(ItemJarBlock.TileData entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
+    public void render(ItemJarBlock.TileData data, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
 
-        List<ItemStack> stacks = entity.getStacks();
+        ItemsJarContents items = data.getItems();
+        if (items != null) {
+            renderItemStacks(data, items, tickDelta, matrices, vertices, light, overlay);
+        }
 
+        EntityJarContents entity = data.getEntity();
+        if (entity != null) {
+            renderEntity(data, entity, tickDelta, matrices, vertices, light, overlay);
+        }
+    }
+
+    private void renderItemStacks(ItemJarBlock.TileData data, ItemsJarContents items, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
         float itemScale = 0.35F;
 
         matrices.push();
@@ -31,19 +63,66 @@ public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBl
         matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));
         matrices.scale(itemScale, itemScale, itemScale);
 
-        Random rng = Random.create(entity.getPos().asLong());
+        Random rng = Random.create(data.getPos().asLong());
 
         float y = 0;
-        for (ItemStack stack : stacks) {
+        for (ItemStack stack : items.getStacks()) {
             matrices.push();
 
             matrices.translate((rng.nextFloat() - 0.5F) * 0.5F, (rng.nextFloat() - 0.5F) * 0.8F, -0.05 + y);
             matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees((rng.nextFloat() * 360) - 180));
             matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees((rng.nextFloat() * 360) - 180));
             y -= 0.1F;
-            MinecraftClient.getInstance().getItemRenderer().renderItem(stack, ModelTransformationMode.FIXED, light, overlay, matrices, vertices, entity.getWorld(), 0);
+            itemRenderer.renderItem(stack, ModelTransformationMode.FIXED, light, overlay, matrices, vertices, data.getWorld(), 0);
             matrices.pop();
         }
         matrices.pop();
     }
+
+    private void renderEntity(ItemJarBlock.TileData data, EntityJarContents entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
+        Entity e = entity.getOrCreateEntity();
+        if (e != null) {
+
+
+            PlayerEntity player = MinecraftClient.getInstance().player;
+            int age = player == null ? 0 : player.age;
+
+            float fullTick = age + tickDelta;
+
+            float size = Math.max(e.getWidth(), e.getHeight());
+            float desiredSize = 0.25F;
+            float scale = desiredSize / size;
+            float eyePos = (e.getEyeHeight(e.getPose())) * scale;
+
+            float yaw = 0;
+            if (player != null) {
+                Vec3d center = data.getPos().toCenterPos();
+                Vec3d observerPos = MinecraftClient.getInstance().gameRenderer.getCamera().getPos();
+                e.setPosition(center);
+                e.lookAt(EntityAnchor.FEET, observerPos);
+            }
+
+            matrices.push();
+            matrices.translate(0.5, 0.48 + MathHelper.sin(fullTick / 19F) * 0.02F - eyePos, 0.5);
+            matrices.scale(scale, scale, scale);
+            matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(10 * MathHelper.sin(fullTick / 19F)));
+            dispatcher.render(e, 0, 0, 0, yaw * MathHelper.RADIANS_PER_DEGREE, tickDelta, matrices, vertices, light);
+            matrices.pop();
+        }
+        renderFluid(data.getWorld(), data.getPos(), tickDelta, matrices, vertices, light, overlay);
+    }
+
+    private void renderFluid(World world, BlockPos pos, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
+        matrices.push();
+        matrices.translate(0.3F, 0, 0.3F);
+        RenderUtil.renderSpriteCubeFaces(
+                matrices,
+                vertices,
+                waterSprite,
+                0.4F, 0.4F, 0.4F,
+                BiomeColors.getWaterColor(world, pos),
+                light, overlay, PosHelper.ALL
+        );
+        matrices.pop();
+    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntityBucketItem.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntityBucketItem.java
new file mode 100644
index 00000000..978c2636
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinEntityBucketItem.java
@@ -0,0 +1,16 @@
+package com.minelittlepony.unicopia.mixin;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import net.minecraft.entity.EntityType;
+import net.minecraft.item.EntityBucketItem;
+import net.minecraft.sound.SoundEvent;
+
+@Mixin(EntityBucketItem.class)
+public interface MixinEntityBucketItem {
+    @Accessor
+    EntityType<?> getEntityType();
+    @Accessor
+    SoundEvent getEmptyingSound();
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java b/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java
index 819153e5..54093f51 100644
--- a/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java
+++ b/src/main/java/com/minelittlepony/unicopia/util/NbtSerialisable.java
@@ -8,12 +8,14 @@ import java.util.stream.Stream;
 import com.mojang.datafixers.util.Pair;
 import com.mojang.serialization.Codec;
 
+import net.minecraft.item.ItemStack;
 import net.minecraft.nbt.*;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.math.Vec3d;
 
 public interface NbtSerialisable {
     Serializer<BlockPos> BLOCK_POS = Serializer.of(NbtHelper::toBlockPos, NbtHelper::fromBlockPos);
+    Serializer<ItemStack> ITEM_STACK = Serializer.of(ItemStack::fromNbt, stack -> stack.writeNbt(new NbtCompound()));
 
     /**
      * Called to save this to nbt to persist state on file or to transmit over the network
@@ -44,6 +46,7 @@ public interface NbtSerialisable {
     }
 
     static Vec3d readVector(NbtList list) {
+
         return new Vec3d(list.getDouble(0), list.getDouble(1), list.getDouble(2));
     }
 
diff --git a/src/main/java/com/minelittlepony/unicopia/util/PosHelper.java b/src/main/java/com/minelittlepony/unicopia/util/PosHelper.java
index 7e203abd..2cb644e4 100644
--- a/src/main/java/com/minelittlepony/unicopia/util/PosHelper.java
+++ b/src/main/java/com/minelittlepony/unicopia/util/PosHelper.java
@@ -21,7 +21,7 @@ import net.minecraft.world.BlockView;
 import net.minecraft.world.World;
 
 public interface PosHelper {
-
+    Direction[] ALL = Direction.values();
     Direction[] HORIZONTAL = Arrays.stream(Direction.values()).filter(d -> d.getAxis().isHorizontal()).toArray(Direction[]::new);
 
     static Vec3d offset(Vec3d a, Vec3i b) {
diff --git a/src/main/resources/unicopia.mixin.json b/src/main/resources/unicopia.mixin.json
index 0a82eae8..855a75b7 100644
--- a/src/main/resources/unicopia.mixin.json
+++ b/src/main/resources/unicopia.mixin.json
@@ -19,6 +19,7 @@
     "MixinEnchantmentHelper",
     "MixinFallLocation",
     "MixinEntity",
+    "MixinEntityBucketItem",
     "MixinEntityView",
     "MixinEntityShapeContext",
     "MixinFallingBlock",

From 1503cb57735be8af8411592d01d8144dbd2ee50d Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Sun, 31 Mar 2024 02:38:59 +0100
Subject: [PATCH 02/21] Set butterfly spawn conditions to include more blocks

---
 src/main/java/com/minelittlepony/unicopia/UTags.java           | 2 ++
 .../unicopia/datagen/providers/UBlockTagProvider.java          | 1 +
 .../minelittlepony/unicopia/entity/mob/ButterflyEntity.java    | 3 ++-
 3 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/main/java/com/minelittlepony/unicopia/UTags.java b/src/main/java/com/minelittlepony/unicopia/UTags.java
index 71120481..687d389d 100644
--- a/src/main/java/com/minelittlepony/unicopia/UTags.java
+++ b/src/main/java/com/minelittlepony/unicopia/UTags.java
@@ -83,6 +83,8 @@ public interface UTags {
         TagKey<Block> CLOUD_STAIRS = block("cloud_stairs");
         TagKey<Block> CLOUD_BLOCKS = block("cloud_blocks");
         TagKey<Block> CHITIN_BLOCKS = block("chitin_blocks");
+
+        TagKey<Block> BUTTERFLIES_SPAWNABLE_ON = block("butterflies_spawn_on");
     }
 
     static TagKey<Item> item(String name) {
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockTagProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockTagProvider.java
index 50a752c3..d30b68de 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockTagProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockTagProvider.java
@@ -40,6 +40,7 @@ public class UBlockTagProvider extends FabricTagProvider.BlockTagProvider {
         };
 
         getOrCreateTagBuilder(UTags.CATAPULT_IMMUNE).add(Blocks.BEDROCK).forceAddTag(BlockTags.DOORS).forceAddTag(BlockTags.TRAPDOORS);
+        getOrCreateTagBuilder(UTags.Blocks.BUTTERFLIES_SPAWNABLE_ON).forceAddTag(BlockTags.ANIMALS_SPAWNABLE_ON).forceAddTag(BlockTags.LEAVES).forceAddTag(BlockTags.FLOWERS).forceAddTag(BlockTags.FLOWER_POTS);
         getOrCreateTagBuilder(UTags.JARS).add(UBlocks.JAR, UBlocks.CLOUD_JAR, UBlocks.STORM_JAR, UBlocks.LIGHTNING_JAR, UBlocks.ZAP_JAR);
         getOrCreateTagBuilder(BlockTags.CROPS).add(crops);
         getOrCreateTagBuilder(BlockTags.BEE_GROWABLES).add(crops);
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/ButterflyEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/ButterflyEntity.java
index d1a43978..6a0731b4 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/mob/ButterflyEntity.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/ButterflyEntity.java
@@ -11,6 +11,7 @@ import java.util.stream.Collectors;
 import org.jetbrains.annotations.Nullable;
 
 import com.minelittlepony.unicopia.USounds;
+import com.minelittlepony.unicopia.UTags;
 import com.minelittlepony.unicopia.Unicopia;
 import com.minelittlepony.unicopia.item.ButterflyItem;
 import com.minelittlepony.unicopia.util.NbtSerialisable;
@@ -73,7 +74,7 @@ public class ButterflyEntity extends AmbientEntity {
     }
 
     public static boolean canSpawn(EntityType<? extends ButterflyEntity> type, WorldAccess world, SpawnReason spawnReason, BlockPos pos, Random random) {
-        return world.getBlockState(pos.down()).isIn(BlockTags.ANIMALS_SPAWNABLE_ON);
+        return world.getBlockState(pos.down()).isIn(UTags.Blocks.BUTTERFLIES_SPAWNABLE_ON);
     }
 
     @Override

From ea9c3bfaf96cdf1389212d9daa3b1ec10c93ab40 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Sun, 31 Mar 2024 14:01:52 +0100
Subject: [PATCH 03/21] Move more tags to datagen

---
 .../unicopia/UConventionalTags.java           | 10 +++
 .../unicopia/datagen/Datagen.java             | 20 ++---
 .../{ => tag}/UBlockTagProvider.java          | 12 ++-
 .../providers/tag/UDamageTypeProvider.java    | 30 +++++++
 .../providers/{ => tag}/UItemTagProvider.java |  0
 .../unicopia/entity/damage/UDamageTypes.java  | 22 +-----
 .../util/registry/DynamicRegistry.java        | 45 +++++++++++
 .../data/c/tags/blocks/concrete_powders.json  | 21 -----
 .../data/c/tags/blocks/glass_blocks.json      | 24 ------
 .../data/c/tags/blocks/glass_panes.json       | 22 ------
 .../resources/data/c/tags/blocks/ores.json    | 78 -------------------
 .../data/c/tags/items/concrete_powders.json   | 21 -----
 .../tags/damage_type/breaks_sunglasses.json   |  7 --
 .../tags/damage_type/from_horseshoes.json     |  6 --
 .../unicopia/tags/damage_type/from_rocks.json |  6 --
 .../tags/damage_type/spellbook_immune_to.json | 16 ----
 16 files changed, 110 insertions(+), 230 deletions(-)
 rename src/main/java/com/minelittlepony/unicopia/datagen/providers/{ => tag}/UBlockTagProvider.java (94%)
 create mode 100644 src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UDamageTypeProvider.java
 rename src/main/java/com/minelittlepony/unicopia/datagen/providers/{ => tag}/UItemTagProvider.java (100%)
 create mode 100644 src/main/java/com/minelittlepony/unicopia/util/registry/DynamicRegistry.java
 delete mode 100644 src/main/resources/data/c/tags/blocks/concrete_powders.json
 delete mode 100644 src/main/resources/data/c/tags/blocks/glass_blocks.json
 delete mode 100644 src/main/resources/data/c/tags/blocks/glass_panes.json
 delete mode 100644 src/main/resources/data/c/tags/blocks/ores.json
 delete mode 100644 src/main/resources/data/c/tags/items/concrete_powders.json
 delete mode 100644 src/main/resources/data/unicopia/tags/damage_type/breaks_sunglasses.json
 delete mode 100644 src/main/resources/data/unicopia/tags/damage_type/from_horseshoes.json
 delete mode 100644 src/main/resources/data/unicopia/tags/damage_type/from_rocks.json
 delete mode 100644 src/main/resources/data/unicopia/tags/damage_type/spellbook_immune_to.json

diff --git a/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java b/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
index 44e8571e..603c900e 100644
--- a/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
+++ b/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
@@ -35,4 +35,14 @@ public interface UConventionalTags {
     static TagKey<Block> block(String name) {
         return TagKey.of(RegistryKeys.BLOCK, new Identifier("c", name));
     }
+
+    interface Blocks {
+        TagKey<Block> CONCRETE_POWDERS = block("concrete_powders");
+        TagKey<Block> CONCRETES = block("concretes");
+    }
+
+    interface Items {
+        TagKey<Item> CONCRETE_POWDERS = item("concrete_powders");
+        TagKey<Item> CONCRETES = item("concretes");
+    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/Datagen.java b/src/main/java/com/minelittlepony/unicopia/datagen/Datagen.java
index 972699d1..f8d103a6 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/Datagen.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/Datagen.java
@@ -5,33 +5,32 @@ import org.apache.logging.log4j.Logger;
 
 import com.minelittlepony.unicopia.datagen.providers.SeasonsGrowthRatesProvider;
 import com.minelittlepony.unicopia.datagen.providers.UAdvancementsProvider;
-import com.minelittlepony.unicopia.datagen.providers.UBlockTagProvider;
-import com.minelittlepony.unicopia.datagen.providers.UItemTagProvider;
 import com.minelittlepony.unicopia.datagen.providers.UModelProvider;
 import com.minelittlepony.unicopia.datagen.providers.loot.UBlockAdditionsLootTableProvider;
 import com.minelittlepony.unicopia.datagen.providers.loot.UBlockLootTableProvider;
 import com.minelittlepony.unicopia.datagen.providers.loot.UChestAdditionsLootTableProvider;
 import com.minelittlepony.unicopia.datagen.providers.recipe.URecipeProvider;
+import com.minelittlepony.unicopia.datagen.providers.tag.UBlockTagProvider;
+import com.minelittlepony.unicopia.datagen.providers.tag.UDamageTypeProvider;
+import com.minelittlepony.unicopia.datagen.providers.tag.UItemTagProvider;
+import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
 import com.minelittlepony.unicopia.server.world.UWorldGen;
 
 import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
 import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
 import net.minecraft.registry.RegistryBuilder;
-import net.minecraft.registry.RegistryEntryLookup;
 import net.minecraft.registry.RegistryKeys;
 import net.minecraft.world.biome.OverworldBiomeCreator;
-import net.minecraft.world.gen.carver.ConfiguredCarver;
-import net.minecraft.world.gen.feature.PlacedFeature;
 
 public class Datagen implements DataGeneratorEntrypoint {
     public static final Logger LOGGER = LogManager.getLogger();
 
     @Override
     public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) {
-        final FabricDataGenerator.Pack pack = fabricDataGenerator.createPack();
-
-        UBlockTagProvider blockTags = pack.addProvider(UBlockTagProvider::new);
+        final var pack = fabricDataGenerator.createPack();
+        final var blockTags = pack.addProvider(UBlockTagProvider::new);
         pack.addProvider((output, registries) -> new UItemTagProvider(output, registries, blockTags));
+        pack.addProvider(UDamageTypeProvider::new);
 
         pack.addProvider(UModelProvider::new);
         pack.addProvider(URecipeProvider::new);
@@ -45,9 +44,10 @@ public class Datagen implements DataGeneratorEntrypoint {
     @Override
     public void buildRegistry(RegistryBuilder builder) {
         builder.addRegistry(RegistryKeys.BIOME, registerable -> {
-            RegistryEntryLookup<PlacedFeature> placedFeatureLookup = registerable.getRegistryLookup(RegistryKeys.PLACED_FEATURE);
-            RegistryEntryLookup<ConfiguredCarver<?>> carverLookup = registerable.getRegistryLookup(RegistryKeys.CONFIGURED_CARVER);
+            final var placedFeatureLookup = registerable.getRegistryLookup(RegistryKeys.PLACED_FEATURE);
+            final var carverLookup = registerable.getRegistryLookup(RegistryKeys.CONFIGURED_CARVER);
             registerable.register(UWorldGen.SWEET_APPLE_ORCHARD, OverworldBiomeCreator.createNormalForest(placedFeatureLookup, carverLookup, false, false, false));
         });
+        builder.addRegistry(RegistryKeys.DAMAGE_TYPE, UDamageTypes.REGISTRY);
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockTagProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java
similarity index 94%
rename from src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockTagProvider.java
rename to src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java
index d30b68de..f4c583a4 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockTagProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java
@@ -1,7 +1,9 @@
-package com.minelittlepony.unicopia.datagen.providers;
+package com.minelittlepony.unicopia.datagen.providers.tag;
 
+import java.util.Arrays;
 import java.util.concurrent.CompletableFuture;
 
+import com.minelittlepony.unicopia.UConventionalTags;
 import com.minelittlepony.unicopia.UTags;
 import com.minelittlepony.unicopia.Unicopia;
 import com.minelittlepony.unicopia.block.UBlocks;
@@ -12,12 +14,14 @@ import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
 import net.fabricmc.fabric.api.tag.convention.v1.ConventionalBlockTags;
 import net.minecraft.block.Block;
 import net.minecraft.block.Blocks;
+import net.minecraft.registry.Registries;
 import net.minecraft.registry.RegistryKeys;
 import net.minecraft.registry.RegistryWrapper;
 import net.minecraft.registry.RegistryWrapper.WrapperLookup;
 import net.minecraft.registry.tag.BlockTags;
 import net.minecraft.registry.tag.TagBuilder;
 import net.minecraft.registry.tag.TagKey;
+import net.minecraft.util.DyeColor;
 import net.minecraft.util.Identifier;
 
 public class UBlockTagProvider extends FabricTagProvider.BlockTagProvider {
@@ -32,6 +36,7 @@ public class UBlockTagProvider extends FabricTagProvider.BlockTagProvider {
 
     @Override
     protected void configure(WrapperLookup registries) {
+        populateConventionalTags();
         Block[] crops = {
                 UBlocks.OATS, UBlocks.OATS_STEM, UBlocks.OATS_CROWN,
                 UBlocks.ROCKS, UBlocks.PINEAPPLE,
@@ -205,4 +210,9 @@ public class UBlockTagProvider extends FabricTagProvider.BlockTagProvider {
         getOrCreateTagBuilder(BlockTags.CEILING_HANGING_SIGNS).add(hanging);
         getOrCreateTagBuilder(BlockTags.WALL_HANGING_SIGNS).add(wallHanging);
     }
+
+    private void populateConventionalTags() {
+        getOrCreateTagBuilder(UConventionalTags.Blocks.CONCRETES).add(Arrays.stream(DyeColor.values()).map(i -> Registries.BLOCK.get(new Identifier(i.getName() + "_concrete"))).toArray(Block[]::new));
+        getOrCreateTagBuilder(UConventionalTags.Blocks.CONCRETE_POWDERS).add(Arrays.stream(DyeColor.values()).map(i -> Registries.BLOCK.get(new Identifier(i.getName() + "_concrete_powder"))).toArray(Block[]::new));
+    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UDamageTypeProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UDamageTypeProvider.java
new file mode 100644
index 00000000..e4170071
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UDamageTypeProvider.java
@@ -0,0 +1,30 @@
+package com.minelittlepony.unicopia.datagen.providers.tag;
+
+import java.util.concurrent.CompletableFuture;
+
+import com.minelittlepony.unicopia.UTags;
+import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
+
+import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
+import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
+import net.minecraft.entity.damage.DamageType;
+import net.minecraft.registry.RegistryKeys;
+import net.minecraft.registry.RegistryWrapper.WrapperLookup;
+import net.minecraft.registry.tag.DamageTypeTags;
+
+public class UDamageTypeProvider extends FabricTagProvider<DamageType> {
+    public UDamageTypeProvider(FabricDataOutput output, CompletableFuture<WrapperLookup> registriesFuture) {
+        super(output, RegistryKeys.DAMAGE_TYPE, registriesFuture);
+    }
+
+    @Override
+    protected void configure(WrapperLookup lookup) {
+        getOrCreateTagBuilder(UTags.SPELLBOOK_IMMUNE_TO).add(
+                UDamageTypes.ZAP_APPLE, UDamageTypes.LOVE_DRAINING, UDamageTypes.LIFE_DRAINING,
+                UDamageTypes.RAINBOOM, UDamageTypes.SUN, UDamageTypes.SUNLIGHT, UDamageTypes.SMASH
+        ).forceAddTag(DamageTypeTags.IS_FALL).forceAddTag(DamageTypeTags.IS_FREEZING).forceAddTag(DamageTypeTags.IS_LIGHTNING).forceAddTag(DamageTypeTags.IS_PROJECTILE);
+        getOrCreateTagBuilder(UTags.FROM_ROCKS).add(UDamageTypes.ROCK);
+        getOrCreateTagBuilder(UTags.FROM_HORSESHOES).add(UDamageTypes.HORSESHOE);
+        getOrCreateTagBuilder(UTags.BREAKS_SUNGLASSES).add(UDamageTypes.BAT_SCREECH, UDamageTypes.RAINBOOM);
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UItemTagProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
similarity index 100%
rename from src/main/java/com/minelittlepony/unicopia/datagen/providers/UItemTagProvider.java
rename to src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/damage/UDamageTypes.java b/src/main/java/com/minelittlepony/unicopia/entity/damage/UDamageTypes.java
index eae9bb47..3bdc4a8f 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/damage/UDamageTypes.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/damage/UDamageTypes.java
@@ -1,18 +1,14 @@
 package com.minelittlepony.unicopia.entity.damage;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import com.minelittlepony.unicopia.Unicopia;
+import com.minelittlepony.unicopia.util.registry.DynamicRegistry;
 
-import net.fabricmc.fabric.api.event.registry.DynamicRegistrySetupCallback;
 import net.minecraft.entity.damage.DamageType;
-import net.minecraft.registry.Registry;
 import net.minecraft.registry.RegistryKey;
 import net.minecraft.registry.RegistryKeys;
 
 public interface UDamageTypes {
-    List<RegistryKey<DamageType>> REGISTRY = new ArrayList<>();
+    DynamicRegistry<DamageType> REGISTRY = new DynamicRegistry<>(RegistryKeys.DAMAGE_TYPE, key -> new DamageType(key.getValue().getNamespace() + "." + key.getValue().getPath(), 0));
 
     RegistryKey<DamageType> EXHAUSTION = register("magical_exhaustion");
     RegistryKey<DamageType> ALICORN_AMULET = register("alicorn_amulet");
@@ -34,18 +30,8 @@ public interface UDamageTypes {
     RegistryKey<DamageType> SPIKES = register("spikes");
 
     private static RegistryKey<DamageType> register(String name) {
-        var key = RegistryKey.of(RegistryKeys.DAMAGE_TYPE, Unicopia.id(name));
-        REGISTRY.add(key);
-        return key;
+        return REGISTRY.register(Unicopia.id(name));
     }
 
-    static void bootstrap() {
-        DynamicRegistrySetupCallback.EVENT.register(registries -> {
-            registries.getOptional(RegistryKeys.DAMAGE_TYPE).ifPresent(registry -> {
-                REGISTRY.forEach(key -> {
-                    Registry.register(registry, key.getValue(), new DamageType(key.getValue().getNamespace() + "." + key.getValue().getPath(), 0));
-                });
-            });
-        });
-    }
+    static void bootstrap() {}
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/util/registry/DynamicRegistry.java b/src/main/java/com/minelittlepony/unicopia/util/registry/DynamicRegistry.java
new file mode 100644
index 00000000..0dd2949a
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/util/registry/DynamicRegistry.java
@@ -0,0 +1,45 @@
+package com.minelittlepony.unicopia.util.registry;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+import net.fabricmc.fabric.api.event.registry.DynamicRegistrySetupCallback;
+import net.minecraft.registry.Registerable;
+import net.minecraft.registry.Registry;
+import net.minecraft.registry.RegistryBuilder;
+import net.minecraft.registry.RegistryKey;
+import net.minecraft.util.Identifier;
+
+public class DynamicRegistry<T> implements RegistryBuilder.BootstrapFunction<T> {
+    private final RegistryKey<Registry<T>> registry;
+    private final Map<RegistryKey<T>, Entry<T>> keys = new HashMap<>();
+    private final Function<RegistryKey<T>, T> valueFactory;
+
+    public DynamicRegistry(RegistryKey<Registry<T>> registry, Function<RegistryKey<T>, T> valueFactory) {
+        this.registry = registry;
+        this.valueFactory = valueFactory;
+
+        DynamicRegistrySetupCallback.EVENT.register(registries -> {
+            registries.getOptional(registry).ifPresent(r -> {
+                keys.forEach((key, entry)-> Registry.register(r, key.getValue(), entry.factory().apply(key)));
+            });
+        });
+    }
+
+    @Override
+    public void run(Registerable<T> registerable) {
+        keys.forEach((key, entry) -> registerable.register(key, entry.factory().apply(key)));
+    }
+
+    public RegistryKey<T> register(Identifier id) {
+        return register(id, valueFactory);
+    }
+
+    public RegistryKey<T> register(Identifier id, Function<RegistryKey<T>, T> valueFactory) {
+        return keys.computeIfAbsent(RegistryKey.of(registry, id), k -> new Entry<>(k, valueFactory)).key();
+    }
+
+    record Entry<T>(RegistryKey<T> key, Function<RegistryKey<T>, T> factory) {}
+
+}
diff --git a/src/main/resources/data/c/tags/blocks/concrete_powders.json b/src/main/resources/data/c/tags/blocks/concrete_powders.json
deleted file mode 100644
index e3142856..00000000
--- a/src/main/resources/data/c/tags/blocks/concrete_powders.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:white_concrete_powder",
-    "minecraft:orange_concrete_powder",
-    "minecraft:magenta_concrete_powder",
-    "minecraft:light_blue_concrete_powder",
-    "minecraft:yellow_concrete_powder",
-    "minecraft:lime_concrete_powder",
-    "minecraft:pink_concrete_powder",
-    "minecraft:gray_concrete_powder",
-    "minecraft:light_gray_concrete_powder",
-    "minecraft:cyan_concrete_powder",
-    "minecraft:purple_concrete_powder",
-    "minecraft:blue_concrete_powder",
-    "minecraft:brown_concrete_powder",
-    "minecraft:green_concrete_powder",
-    "minecraft:red_concrete_powder",
-    "minecraft:black_concrete_powder"
-  ]
-}
diff --git a/src/main/resources/data/c/tags/blocks/glass_blocks.json b/src/main/resources/data/c/tags/blocks/glass_blocks.json
deleted file mode 100644
index f55e9719..00000000
--- a/src/main/resources/data/c/tags/blocks/glass_blocks.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:glass",
-    "minecraft:white_stained_glass",
-    "minecraft:orange_stained_glass",
-    "minecraft:magenta_stained_glass",
-    "minecraft:light_blue_stained_glass",
-    "minecraft:yellow_stained_glass",
-    "minecraft:lime_stained_glass",
-    "minecraft:pink_stained_glass",
-    "minecraft:gray_stained_glass",
-    "minecraft:light_gray_stained_glass",
-    "minecraft:cyan_stained_glass",
-    "minecraft:purple_stained_glass",
-    "minecraft:blue_stained_glass",
-    "minecraft:brown_stained_glass",
-    "minecraft:green_stained_glass",
-    "minecraft:red_stained_glass",
-    "minecraft:black_stained_glass",
-    { "id": "#c:glass", "required": false },
-    { "id": "#c:glass_blocks", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/c/tags/blocks/glass_panes.json b/src/main/resources/data/c/tags/blocks/glass_panes.json
deleted file mode 100644
index ff9176f7..00000000
--- a/src/main/resources/data/c/tags/blocks/glass_panes.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:glass_pane",
-    "minecraft:white_stained_glass_pane",
-    "minecraft:orange_stained_glass_pane",
-    "minecraft:magenta_stained_glass_pane",
-    "minecraft:light_blue_stained_glass_pane",
-    "minecraft:yellow_stained_glass_pane",
-    "minecraft:lime_stained_glass_pane",
-    "minecraft:pink_stained_glass_pane",
-    "minecraft:gray_stained_glass_pane",
-    "minecraft:light_gray_stained_glass_pane",
-    "minecraft:cyan_stained_glass_pane",
-    "minecraft:purple_stained_glass_pane",
-    "minecraft:blue_stained_glass_pane",
-    "minecraft:brown_stained_glass_pane",
-    "minecraft:green_stained_glass_pane",
-    "minecraft:red_stained_glass_pane",
-    "minecraft:black_stained_glass_pane"
-  ]
-}
diff --git a/src/main/resources/data/c/tags/blocks/ores.json b/src/main/resources/data/c/tags/blocks/ores.json
deleted file mode 100644
index 1a53e7a3..00000000
--- a/src/main/resources/data/c/tags/blocks/ores.json
+++ /dev/null
@@ -1,78 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:coal_ore",
-    "minecraft:iron_ore",
-    "minecraft:gold_ore",
-    "minecraft:emerald_ore",
-    "minecraft:lapis_ore",
-    "minecraft:copper_ore",
-    "minecraft:diamond_ore",
-    "minecraft:nether_gold_ore",
-    "minecraft:nether_quartz_ore",
-    { "id": "#c:iron_ores", "required": false },
-    { "id": "#c:gold_ores", "required": false },
-    { "id": "#c:diamond_ores", "required": false },
-    { "id": "#c:emerald_ores", "required": false },
-    { "id": "#c:copper_ores", "required": false },
-    { "id": "#c:quartz_ores", "required": false },
-    { "id": "#c:lapis_ores", "required": false },
-    { "id": "#c:aluminium_ores", "required": false },
-    { "id": "#c:methyst_ores", "required": false },
-    { "id": "#c:antimony_ores", "required": false },
-    { "id": "#c:aquarium_ores", "required": false },
-    { "id": "#c:asterite_ores", "required": false },
-    { "id": "#c:banglum_ores", "required": false },
-    { "id": "#c:bauxite_ores", "required": false },
-    { "id": "#c:carmot_ores", "required": false },
-    { "id": "#c:certus_quartz_ores", "required": false },
-    { "id": "#c:cinnabar_ores", "required": false },
-    { "id": "#c:cobalt_ores", "required": false },
-    { "id": "#c:galaxium_ores", "required": false },
-    { "id": "#c:galena_ores", "required": false },
-    { "id": "#c:iridium_ores", "required": false },
-    { "id": "#c:kyber_ores", "required": false },
-    { "id": "#c:lunum_ores", "required": false },
-    { "id": "#c:lutetium_ores", "required": false },
-    { "id": "#c:menganese_ores", "required": false },
-    { "id": "#c:metite_ores", "required": false },
-    { "id": "#c:midas_gold_ores", "required": false },
-    { "id": "#c:moon_lunum_ores", "required": false },
-    { "id": "#c:mozanite_ores", "required": false },
-    { "id": "#c:mythril_ores", "required": false },
-    { "id": "#c:nickle_ores", "required": false },
-    { "id": "#c:nikolite_ores", "required": false },
-    { "id": "#c:orichalcum_ores", "required": false },
-    { "id": "#c:osmium_ores", "required": false },
-    { "id": "#c:palladium_ores", "required": false },
-    { "id": "#c:peridot_ores", "required": false },
-    { "id": "#c:platinum_ores", "required": false },
-    { "id": "#c:plutonium_blocks", "required": false },
-    { "id": "#c:prometheum_ores", "required": false },
-    { "id": "#c:pyrite_ores", "required": false },
-    { "id": "#c:quadrillium_ores", "required": false },
-    { "id": "#c:ruby_ores", "required": false },
-    { "id": "#c:runite_ores", "required": false },
-    { "id": "#c:sapphire_ores", "required": false },
-    { "id": "#c:sheldonite_ores", "required": false },
-    { "id": "#c:silver_ores", "required": false },
-    { "id": "#c:sodalite_ores", "required": false },
-    { "id": "#c:sphalerite_ores", "required": false },
-    { "id": "#c:starrite_ores", "required": false },
-    { "id": "#c:stellum_ores", "required": false },
-    { "id": "#c:stormyx_ores", "required": false },
-    { "id": "#c:sulfur_ores", "required": false },
-    { "id": "#c:tantalite_ores", "required": false },
-    { "id": "#c:tin_ore", "required": false },
-    { "id": "#c:tin_ores", "required": false },
-    { "id": "#c:titanium_ores", "required": false },
-    { "id": "#c:topaz_ores", "required": false },
-    { "id": "#c:truesilver_ores", "required": false },
-    { "id": "#c:tungsten_ores", "required": false },
-    { "id": "#c:unobtainium_ores", "required": false },
-    { "id": "#c:ur_ores", "required": false },
-    { "id": "#c:uranium_ores", "required": false },
-    { "id": "#c:vermiculite_ores", "required": false },
-    { "id": "#c:zinc_ores", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/concrete_powders.json b/src/main/resources/data/c/tags/items/concrete_powders.json
deleted file mode 100644
index e3142856..00000000
--- a/src/main/resources/data/c/tags/items/concrete_powders.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:white_concrete_powder",
-    "minecraft:orange_concrete_powder",
-    "minecraft:magenta_concrete_powder",
-    "minecraft:light_blue_concrete_powder",
-    "minecraft:yellow_concrete_powder",
-    "minecraft:lime_concrete_powder",
-    "minecraft:pink_concrete_powder",
-    "minecraft:gray_concrete_powder",
-    "minecraft:light_gray_concrete_powder",
-    "minecraft:cyan_concrete_powder",
-    "minecraft:purple_concrete_powder",
-    "minecraft:blue_concrete_powder",
-    "minecraft:brown_concrete_powder",
-    "minecraft:green_concrete_powder",
-    "minecraft:red_concrete_powder",
-    "minecraft:black_concrete_powder"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/damage_type/breaks_sunglasses.json b/src/main/resources/data/unicopia/tags/damage_type/breaks_sunglasses.json
deleted file mode 100644
index 3c832cc1..00000000
--- a/src/main/resources/data/unicopia/tags/damage_type/breaks_sunglasses.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "unicopia:bat_screech",
-    "unicopia:rainboom"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/damage_type/from_horseshoes.json b/src/main/resources/data/unicopia/tags/damage_type/from_horseshoes.json
deleted file mode 100644
index 276d2073..00000000
--- a/src/main/resources/data/unicopia/tags/damage_type/from_horseshoes.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "unicopia:horseshoe"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/damage_type/from_rocks.json b/src/main/resources/data/unicopia/tags/damage_type/from_rocks.json
deleted file mode 100644
index 9ab866dd..00000000
--- a/src/main/resources/data/unicopia/tags/damage_type/from_rocks.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "unicopia:rock"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/damage_type/spellbook_immune_to.json b/src/main/resources/data/unicopia/tags/damage_type/spellbook_immune_to.json
deleted file mode 100644
index f270bd7e..00000000
--- a/src/main/resources/data/unicopia/tags/damage_type/spellbook_immune_to.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "#minecraft:is_fall",
-    "#minecraft:is_freezing",
-    "#minecraft:is_lightning",
-    "#minecraft:is_projectile",
-    "unicopia:zap",
-    "unicopia:love_draining",
-    "unicopia:life_draining",
-    "unicopia:rainboom",
-    "unicopia:sun",
-    "unicopia:sunlight",
-    "unicopia:smash"
-  ]
-}

From ec9a12bdf9547a331abae8876c36d43cdfff26f6 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Sun, 31 Mar 2024 14:35:04 +0100
Subject: [PATCH 04/21] Clean up tags and fix some recipe unlock conditions

---
 .../com/minelittlepony/unicopia/Debug.java    |   2 +-
 .../unicopia/UConventionalTags.java           |  58 +++----
 .../com/minelittlepony/unicopia/UTags.java    | 150 ++++++++++--------
 .../ability/EarthPonyGrowAbility.java         |   2 +-
 .../ability/EarthPonyStompAbility.java        |   4 +-
 .../unicopia/ability/ScreechAbility.java      |   2 +-
 .../magic/spell/RainboomAbilitySpell.java     |   2 +-
 .../magic/spell/effect/CatapultSpell.java     |   2 +-
 .../spell/effect/TransformationSpell.java     |   2 +-
 .../unicopia/block/PieBlock.java              |   4 +-
 .../unicopia/client/render/PlayerPoser.java   |   4 +-
 .../providers/UAdvancementsProvider.java      |  14 +-
 .../UChestAdditionsLootTableProvider.java     |  22 +--
 .../recipe/BedSheetPatternRecipeBuilder.java  |   2 +-
 .../providers/recipe/URecipeProvider.java     |  43 +++--
 .../providers/tag/UBlockTagProvider.java      |  22 +--
 .../providers/tag/UDamageTypeProvider.java    |   8 +-
 .../providers/tag/UItemTagProvider.java       |  94 +++++------
 .../unicopia/entity/ItemImpl.java             |   4 +-
 .../unicopia/entity/Living.java               |   4 +-
 .../effect/SunBlindnessStatusEffect.java      |   4 +-
 .../unicopia/entity/mob/SpellbookEntity.java  |   2 +-
 .../unicopia/entity/mob/UTradeOffers.java     |   4 +-
 .../unicopia/entity/player/PlayerPhysics.java |   4 +-
 .../unicopia/entity/player/Pony.java          |   4 +-
 .../unicopia/item/CrystalHeartItem.java       |   4 +-
 .../unicopia/item/PineappleItem.java          |   2 +-
 .../unicopia/item/PolearmItem.java            |   2 +-
 .../minelittlepony/unicopia/item/UItems.java  |   2 +-
 .../unicopia/item/ZapAppleItem.java           |   2 +-
 .../enchantment/GemFindingEnchantment.java    |   2 +-
 .../item/group/ItemGroupRegistry.java         |   6 +-
 .../unicopia/item/group/UItemGroups.java      |  15 +-
 .../PhysicsBodyProjectileEntity.java          |   8 +-
 34 files changed, 267 insertions(+), 239 deletions(-)

diff --git a/src/main/java/com/minelittlepony/unicopia/Debug.java b/src/main/java/com/minelittlepony/unicopia/Debug.java
index 8cc8847e..2e3533df 100644
--- a/src/main/java/com/minelittlepony/unicopia/Debug.java
+++ b/src/main/java/com/minelittlepony/unicopia/Debug.java
@@ -51,7 +51,7 @@ public interface Debug {
         )).forEach((namespace, entries) -> {
             @SuppressWarnings("deprecation")
             var unregistered = entries.stream()
-                .filter(entry -> !entry.getValue().getRegistryEntry().isIn(UTags.HAS_NO_TRAITS) && SpellTraits.of(entry.getValue()).isEmpty())
+                .filter(entry -> !entry.getValue().getRegistryEntry().isIn(UTags.Items.HAS_NO_TRAITS) && SpellTraits.of(entry.getValue()).isEmpty())
                 .map(entry -> {
                     String id = entry.getKey().getValue().toString();
 
diff --git a/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java b/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
index 603c900e..bb55a662 100644
--- a/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
+++ b/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
@@ -7,42 +7,42 @@ import net.minecraft.registry.tag.TagKey;
 import net.minecraft.util.Identifier;
 
 public interface UConventionalTags {
-    TagKey<Item> APPLES = item("apples");
-    TagKey<Item> ACORNS = item("acorns");
-    TagKey<Item> PINECONES = item("pinecones");
-    TagKey<Item> PINEAPPLES = item("pineapples");
-    TagKey<Item> BANANAS = item("bananas");
-    TagKey<Item> STICKS = item("sticks");
-    TagKey<Item> SEEDS = item("seeds");
-    TagKey<Item> GRAIN = item("grain");
-    TagKey<Item> NUTS = item("nuts");
-    TagKey<Item> MUSHROOMS = item("mushrooms");
-    TagKey<Item> MUFFINS = item("muffins");
-    TagKey<Item> MANGOES = item("mangoes");
-    TagKey<Item> OATMEALS = item("oatmeals");
-
-    TagKey<Item> FRUITS = item("fruits");
-
-    TagKey<Item> COOKED_FISH = item("cooked_fish");
-
-    TagKey<Item> CROPS_PEANUTS = item("crops/peanuts");
-    TagKey<Item> TOOL_KNIVES = item("tools/knives");
-
-    static TagKey<Item> item(String name) {
-        return TagKey.of(RegistryKeys.ITEM, new Identifier("c", name));
-    }
-
-    static TagKey<Block> block(String name) {
-        return TagKey.of(RegistryKeys.BLOCK, new Identifier("c", name));
-    }
-
     interface Blocks {
         TagKey<Block> CONCRETE_POWDERS = block("concrete_powders");
         TagKey<Block> CONCRETES = block("concretes");
+
+        private static TagKey<Block> block(String name) {
+            return TagKey.of(RegistryKeys.BLOCK, new Identifier("c", name));
+        }
     }
 
     interface Items {
         TagKey<Item> CONCRETE_POWDERS = item("concrete_powders");
         TagKey<Item> CONCRETES = item("concretes");
+
+        TagKey<Item> APPLES = item("apples");
+        TagKey<Item> ACORNS = item("acorns");
+        TagKey<Item> PINECONES = item("pinecones");
+        TagKey<Item> PINEAPPLES = item("pineapples");
+        TagKey<Item> BANANAS = item("bananas");
+        TagKey<Item> STICKS = item("sticks");
+        TagKey<Item> SEEDS = item("seeds");
+        TagKey<Item> GRAIN = item("grain");
+        TagKey<Item> NUTS = item("nuts");
+        TagKey<Item> MUSHROOMS = item("mushrooms");
+        TagKey<Item> MUFFINS = item("muffins");
+        TagKey<Item> MANGOES = item("mangoes");
+        TagKey<Item> OATMEALS = item("oatmeals");
+
+        TagKey<Item> FRUITS = item("fruits");
+
+        TagKey<Item> COOKED_FISH = item("cooked_fish");
+
+        TagKey<Item> CROPS_PEANUTS = item("crops/peanuts");
+        TagKey<Item> TOOL_KNIVES = item("tools/knives");
+
+        private static TagKey<Item> item(String name) {
+            return TagKey.of(RegistryKeys.ITEM, new Identifier("c", name));
+        }
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/UTags.java b/src/main/java/com/minelittlepony/unicopia/UTags.java
index 687d389d..82bd5417 100644
--- a/src/main/java/com/minelittlepony/unicopia/UTags.java
+++ b/src/main/java/com/minelittlepony/unicopia/UTags.java
@@ -11,58 +11,6 @@ import net.minecraft.util.Identifier;
 import net.minecraft.world.dimension.DimensionType;
 
 public interface UTags {
-    TagKey<Item> FRESH_APPLES = item("fresh_apples");
-
-    TagKey<Item> FALLS_SLOWLY = item("falls_slowly");
-    TagKey<Item> PIES = item("pies");
-    TagKey<Item> CAN_CUT_PIE = item("can_cut_pie");
-
-    TagKey<Item> MAGIC_FEATHERS = item("magic_feathers");
-
-    TagKey<Item> SHADES = item("shades");
-    TagKey<Item> CHANGELING_EDIBLE = item("food_types/changeling_edible");
-    TagKey<Item> SPOOKED_MOB_DROPS = item("spooked_mob_drops");
-    TagKey<Item> HAS_NO_TRAITS = item("has_no_traits");
-    TagKey<Item> IS_DELIVERED_AGGRESSIVELY = item("is_delivered_aggressively");
-    TagKey<Item> FLOATS_ON_CLOUDS = item("floats_on_clouds");
-    TagKey<Item> COOLS_OFF_KIRINS = item("cools_off_kirins");
-    TagKey<Item> LOOT_BUG_HIGH_VALUE_DROPS = item("loot_bug_high_value_drops");
-
-    TagKey<Item> SHELLS = item("food_types/shells");
-
-    TagKey<Item> POLEARMS = item("polearms");
-    TagKey<Item> HORSE_SHOES = item("horse_shoes");
-    TagKey<Item> APPLE_SEEDS = item("apple_seeds");
-
-    TagKey<Item> BASKETS = item("baskets");
-    TagKey<Item> BADGES = item("badges");
-    TagKey<Item> WOOL_BED_SHEETS = item("wool_bed_sheets");
-    TagKey<Item> BED_SHEETS = item("bed_sheets");
-    TagKey<Item> CLOUD_JARS = item("cloud_jars");
-
-    TagKey<Block> FRAGILE = block("fragile");
-    TagKey<Block> INTERESTING = block("interesting");
-    TagKey<Block> CATAPULT_IMMUNE = block("catapult_immune");
-    TagKey<Block> JARS = block("jars");
-
-    TagKey<Block> CRYSTAL_HEART_BASE = block("crystal_heart_base");
-    TagKey<Block> CRYSTAL_HEART_ORNAMENT = block("crystal_heart_ornament");
-    TagKey<Block> UNAFFECTED_BY_GROW_ABILITY = block("unaffected_by_grow_ability");
-    TagKey<Block> KICKS_UP_DUST = block("kicks_up_dust");
-
-    TagKey<Block> POLEARM_MINEABLE = block("mineable/polearm");
-
-    TagKey<EntityType<?>> TRANSFORMABLE_ENTITIES = entity("transformable");
-
-    TagKey<StatusEffect> PINEAPPLE_EFFECTS = effect("pineapple_effects");
-
-    TagKey<DamageType> BREAKS_SUNGLASSES = damage("breaks_sunglasses");
-    TagKey<DamageType> SPELLBOOK_IMMUNE_TO = damage("spellbook_immune_to");
-    TagKey<DamageType> FROM_ROCKS = damage("from_rocks");
-    TagKey<DamageType> FROM_HORSESHOES = damage("from_horseshoes");
-
-    TagKey<DimensionType> HAS_NO_ATMOSPHERE = dimension("has_no_atmosphere");
-
     interface Items {
         TagKey<Item> ZAP_LOGS = item("zap_logs");
         TagKey<Item> WAXED_ZAP_LOGS = item("waxed_zap_logs");
@@ -72,6 +20,47 @@ public interface UTags {
         TagKey<Item> CLOUD_STAIRS = item("cloud_stairs");
         TagKey<Item> CLOUD_BLOCKS = item("cloud_blocks");
         TagKey<Item> CHITIN_BLOCKS = item("chitin_blocks");
+
+        TagKey<Item> FRESH_APPLES = item("fresh_apples");
+
+        TagKey<Item> FALLS_SLOWLY = item("falls_slowly");
+        TagKey<Item> PIES = item("pies");
+        TagKey<Item> CAN_CUT_PIE = item("can_cut_pie");
+
+        TagKey<Item> MAGIC_FEATHERS = item("magic_feathers");
+
+        TagKey<Item> SHADES = item("shades");
+        TagKey<Item> CHANGELING_EDIBLE = item("food_types/changeling_edible");
+        TagKey<Item> SPOOKED_MOB_DROPS = item("spooked_mob_drops");
+        TagKey<Item> HAS_NO_TRAITS = item("has_no_traits");
+        TagKey<Item> IS_DELIVERED_AGGRESSIVELY = item("is_delivered_aggressively");
+        TagKey<Item> FLOATS_ON_CLOUDS = item("floats_on_clouds");
+        TagKey<Item> COOLS_OFF_KIRINS = item("cools_off_kirins");
+        TagKey<Item> LOOT_BUG_HIGH_VALUE_DROPS = item("loot_bug_high_value_drops");
+
+        TagKey<Item> SHELLS = item("food_types/shells");
+
+        TagKey<Item> POLEARMS = item("polearms");
+        TagKey<Item> HORSE_SHOES = item("horse_shoes");
+        TagKey<Item> APPLE_SEEDS = item("apple_seeds");
+
+        TagKey<Item> BASKETS = item("baskets");
+        TagKey<Item> BADGES = item("badges");
+        TagKey<Item> WOOL_BED_SHEETS = item("wool_bed_sheets");
+        TagKey<Item> BED_SHEETS = item("bed_sheets");
+        TagKey<Item> CLOUD_JARS = item("cloud_jars");
+
+        TagKey<Item> GROUP_FORAGING = item("groups/foraging");
+        TagKey<Item> GROUP_EARTH_PONY = item("groups/earth_pony");
+        TagKey<Item> GROUP_UNICORN = item("groups/unicorn");
+        TagKey<Item> GROUP_PEGASUS = item("groups/pegasus");
+        TagKey<Item> GROUP_BAT_PONY = item("groups/bat_pony");
+        TagKey<Item> GROUP_SEA_PONY = item("groups/sea_pony");
+        TagKey<Item> GROUP_CHANGELING = item("groups/changeling");
+
+        private static TagKey<Item> item(String name) {
+            return TagKey.of(RegistryKeys.ITEM, Unicopia.id(name));
+        }
     }
 
     interface Blocks {
@@ -84,30 +73,57 @@ public interface UTags {
         TagKey<Block> CLOUD_BLOCKS = block("cloud_blocks");
         TagKey<Block> CHITIN_BLOCKS = block("chitin_blocks");
 
+        TagKey<Block> FRAGILE = block("fragile");
+        TagKey<Block> INTERESTING = block("interesting");
+        TagKey<Block> CATAPULT_IMMUNE = block("catapult_immune");
+        TagKey<Block> JARS = block("jars");
+
+        TagKey<Block> CRYSTAL_HEART_BASE = block("crystal_heart_base");
+        TagKey<Block> CRYSTAL_HEART_ORNAMENT = block("crystal_heart_ornament");
+        TagKey<Block> UNAFFECTED_BY_GROW_ABILITY = block("unaffected_by_grow_ability");
+        TagKey<Block> KICKS_UP_DUST = block("kicks_up_dust");
+
+        TagKey<Block> POLEARM_MINEABLE = block("mineable/polearm");
+
         TagKey<Block> BUTTERFLIES_SPAWNABLE_ON = block("butterflies_spawn_on");
+
+        private static TagKey<Block> block(String name) {
+            return TagKey.of(RegistryKeys.BLOCK, Unicopia.id(name));
+        }
     }
 
-    static TagKey<Item> item(String name) {
-        return TagKey.of(RegistryKeys.ITEM, Unicopia.id(name));
+    interface Entities {
+        TagKey<EntityType<?>> TRANSFORMABLE = entity("transformable");
+
+        private static TagKey<EntityType<?>> entity(String name) {
+            return TagKey.of(RegistryKeys.ENTITY_TYPE, Unicopia.id(name));
+        }
     }
 
-    static TagKey<Block> block(String name) {
-        return TagKey.of(RegistryKeys.BLOCK, Unicopia.id(name));
+    interface DamageTypes {
+        TagKey<DamageType> BREAKS_SUNGLASSES = damage("breaks_sunglasses");
+        TagKey<DamageType> SPELLBOOK_IMMUNE_TO = damage("spellbook_immune_to");
+        TagKey<DamageType> FROM_ROCKS = damage("from_rocks");
+        TagKey<DamageType> FROM_HORSESHOES = damage("from_horseshoes");
+
+        private static TagKey<DamageType> damage(String name) {
+            return TagKey.of(RegistryKeys.DAMAGE_TYPE, Unicopia.id(name));
+        }
     }
 
-    static TagKey<EntityType<?>> entity(String name) {
-        return TagKey.of(RegistryKeys.ENTITY_TYPE, Unicopia.id(name));
+    interface DimensionTypes {
+        TagKey<DimensionType> HAS_NO_ATMOSPHERE = dimension("has_no_atmosphere");
+
+        private static TagKey<DimensionType> dimension(String name) {
+            return TagKey.of(RegistryKeys.DIMENSION_TYPE, new Identifier("c", name));
+        }
     }
 
-    static TagKey<StatusEffect> effect(String name) {
-        return TagKey.of(RegistryKeys.STATUS_EFFECT, Unicopia.id(name));
-    }
+    interface StatusEffects {
+        TagKey<StatusEffect> PINEAPPLE_EFFECTS = effect("pineapple_effects");
 
-    static TagKey<DamageType> damage(String name) {
-        return TagKey.of(RegistryKeys.DAMAGE_TYPE, Unicopia.id(name));
-    }
-
-    static TagKey<DimensionType> dimension(String name) {
-        return TagKey.of(RegistryKeys.DIMENSION_TYPE, new Identifier("c", name));
+        private static TagKey<StatusEffect> effect(String name) {
+            return TagKey.of(RegistryKeys.STATUS_EFFECT, Unicopia.id(name));
+        }
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java
index 61ab54d5..75cd435f 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java
@@ -108,7 +108,7 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
             }
         }
 
-        if (w.getBlockState(pos).isIn(UTags.UNAFFECTED_BY_GROW_ABILITY)) {
+        if (w.getBlockState(pos).isIn(UTags.Blocks.UNAFFECTED_BY_GROW_ABILITY)) {
             return 0;
         }
 
diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java
index b702400f..285266c9 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyStompAbility.java
@@ -162,7 +162,7 @@ public class EarthPonyStompAbility implements Ability<Hit> {
 
             ParticleUtils.spawnParticle(player.getWorld(), UParticles.GROUND_POUND, player.getX(), player.getY() - 1, player.getZ(), 0, 0, 0);
             BlockState steppingState = player.getSteppingBlockState();
-            if (steppingState.isIn(UTags.KICKS_UP_DUST)) {
+            if (steppingState.isIn(UTags.Blocks.KICKS_UP_DUST)) {
                 ParticleUtils.spawnParticle(player.getWorld(), new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), player.getBlockPos().down().toCenterPos(), Vec3d.ZERO);
             }
 
@@ -227,7 +227,7 @@ public class EarthPonyStompAbility implements Ability<Hit> {
             w.syncWorldEvent(WorldEvents.BLOCK_BROKEN, pos, Block.getRawIdFromState(state));
         }
 
-        if (state.isIn(UTags.KICKS_UP_DUST)) {
+        if (state.isIn(UTags.Blocks.KICKS_UP_DUST)) {
             if (w.random.nextInt(4) == 0 && w.isAir(pos.up()) && w.getFluidState(pos.up()).isEmpty()) {
                 ParticleUtils.spawnParticle(w, new BlockStateParticleEffect(UParticles.DUST_CLOUD, state), pos.up().toCenterPos(), VecHelper.supply(() -> w.random.nextTriangular(0, 0.1F)));
             }
diff --git a/src/main/java/com/minelittlepony/unicopia/ability/ScreechAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/ScreechAbility.java
index ac244ea0..60319ba1 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/ScreechAbility.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/ScreechAbility.java
@@ -110,7 +110,7 @@ public class ScreechAbility implements Ability<Numeric> {
 
 
             if (living.getWorld().random.nextInt(MOB_SPOOK_PROBABILITY) == 0) {
-                RegistryUtils.pickRandom(living.getWorld(), UTags.SPOOKED_MOB_DROPS).ifPresent(drop -> {
+                RegistryUtils.pickRandom(living.getWorld(), UTags.Items.SPOOKED_MOB_DROPS).ifPresent(drop -> {
                     living.dropStack(drop.getDefaultStack());
                     living.playSound(USounds.Vanilla.ENTITY_ITEM_PICKUP, 1, 0.1F);
                     UCriteria.SPOOK_MOB.trigger(player.asEntity());
diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java
index a3339212..13445ea2 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/RainboomAbilitySpell.java
@@ -64,7 +64,7 @@ public class RainboomAbilitySpell extends AbstractSpell {
         });
         EFFECT_RANGE.translate(source.getOrigin()).getBlockPositions().forEach(pos -> {
             BlockState state = source.asWorld().getBlockState(pos);
-            if (state.isIn(UTags.FRAGILE) && source.canModifyAt(pos, ModificationType.PHYSICAL)) {
+            if (state.isIn(UTags.Blocks.FRAGILE) && source.canModifyAt(pos, ModificationType.PHYSICAL)) {
                 source.asWorld().breakBlock(pos, true);
             }
         });
diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/CatapultSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/CatapultSpell.java
index ec5cbf3e..ce440570 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/CatapultSpell.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/CatapultSpell.java
@@ -86,7 +86,7 @@ public class CatapultSpell extends AbstractSpell implements ProjectileDelegate.B
         }
 
         BlockState state = world.getBlockState(bpos);
-        if (state.isIn(UTags.CATAPULT_IMMUNE)) {
+        if (state.isIn(UTags.Blocks.CATAPULT_IMMUNE)) {
             return;
         }
 
diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TransformationSpell.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TransformationSpell.java
index e26152f0..4d507d1d 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TransformationSpell.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/effect/TransformationSpell.java
@@ -66,7 +66,7 @@ public class TransformationSpell extends AbstractSpell implements ProjectileDele
 
     @SuppressWarnings("unchecked")
     private <T extends MobEntity> Optional<EntityType<T>> pickType(EntityType<?> except, World world) {
-        Set<EntityType<?>> options = RegistryUtils.valuesForTag(world, UTags.TRANSFORMABLE_ENTITIES).collect(Collectors.toSet());
+        Set<EntityType<?>> options = RegistryUtils.valuesForTag(world, UTags.Entities.TRANSFORMABLE).collect(Collectors.toSet());
         if (except.getSpawnGroup() == SpawnGroup.MONSTER) {
             options.removeIf(t -> t.getSpawnGroup() == SpawnGroup.MONSTER);
         } else {
diff --git a/src/main/java/com/minelittlepony/unicopia/block/PieBlock.java b/src/main/java/com/minelittlepony/unicopia/block/PieBlock.java
index b7c2be5b..33c85df0 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/PieBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/PieBlock.java
@@ -71,7 +71,7 @@ public class PieBlock extends Block implements Waterloggable {
 
         if (world.isClient) {
 
-            if (itemStack.isIn(UTags.CAN_CUT_PIE)) {
+            if (itemStack.isIn(UTags.Items.CAN_CUT_PIE)) {
                 return ActionResult.SUCCESS;
             }
 
@@ -84,7 +84,7 @@ public class PieBlock extends Block implements Waterloggable {
             }
         }
 
-        if (itemStack.isIn(UTags.CAN_CUT_PIE)) {
+        if (itemStack.isIn(UTags.Items.CAN_CUT_PIE)) {
             SoundEmitter.playSoundAt(player, USounds.BLOCK_PIE_SLICE, SoundCategory.NEUTRAL, 1, 1);
             removeSlice(world, pos, state, player);
             itemStack.damage(1, player, p -> p.sendToolBreakStatus(hand));
diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/PlayerPoser.java b/src/main/java/com/minelittlepony/unicopia/client/render/PlayerPoser.java
index ec3b17bb..2402dfab 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/PlayerPoser.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/PlayerPoser.java
@@ -52,7 +52,7 @@ public class PlayerPoser {
             float pitchChange = -0.5F;
             float yawChange = 0.8F;
 
-            if (player.getStackInHand(rightHand).isIn(UTags.POLEARMS) && (!ponyRace.isEquine() || model.rightArm.pitch != 0)) {
+            if (player.getStackInHand(rightHand).isIn(UTags.Items.POLEARMS) && (!ponyRace.isEquine() || model.rightArm.pitch != 0)) {
                 model.rightArm.pitch += pitchChange;
                 model.rightArm.yaw += yawChange;
                 if (player.handSwingTicks > 0 && rightHand == Hand.MAIN_HAND) {
@@ -61,7 +61,7 @@ public class PlayerPoser {
                 }
             }
 
-            if (player.getStackInHand(leftHand).isIn(UTags.POLEARMS) && (!ponyRace.isEquine() || model.leftArm.pitch != 0)) {
+            if (player.getStackInHand(leftHand).isIn(UTags.Items.POLEARMS) && (!ponyRace.isEquine() || model.leftArm.pitch != 0)) {
                 model.leftArm.pitch += pitchChange;
                 model.leftArm.yaw -= yawChange;
                 if (player.handSwingTicks > 0 && leftHand == Hand.MAIN_HAND) {
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UAdvancementsProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UAdvancementsProvider.java
index e0c2c349..a46d1306 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UAdvancementsProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UAdvancementsProvider.java
@@ -73,27 +73,27 @@ public class UAdvancementsProvider extends FabricAdvancementProvider {
 
     private void generateEarthTribeAdvancementsTree(Consumer<Advancement> consumer, AdvancementDisplayBuilder.Parent parent) {
         parent.child(UItems.ROCK).criterion("has_rock", hasItems(UItems.ROCK)).build(consumer, "born_on_a_rock_farm").children(p -> {
-            p.child(UItems.PEBBLES).criterion("killed_entity_with_rock", killWithItems(UTags.FROM_ROCKS)).build(consumer, "sticks_and_stones");
+            p.child(UItems.PEBBLES).criterion("killed_entity_with_rock", killWithItems(UTags.DamageTypes.FROM_ROCKS)).build(consumer, "sticks_and_stones");
             p.child(UItems.WEIRD_ROCK).hidden().criterion("has_rock", hasItems(UItems.WEIRD_ROCK)).build(consumer, "thats_unusual");
         });
 
         parent.child(UItems.OATS).criterion("has_oats", hasItems(UItems.OATS)).build(consumer, "oats_so_easy");
         parent.child(Items.HAY_BLOCK).criterion("eat_hay", ConsumeItemCriterion.Conditions.item(Items.HAY_BLOCK)).build(consumer, "what_the_hay");
-        parent.child(UItems.COPPER_HORSE_SHOE).criterion("has_horseshoe", hasItems(UTags.HORSE_SHOES)).build(consumer, "blacksmith").children(p -> {
+        parent.child(UItems.COPPER_HORSE_SHOE).criterion("has_horseshoe", hasItems(UTags.Items.HORSE_SHOES)).build(consumer, "blacksmith").children(p -> {
             p.child(UItems.IRON_HORSE_SHOE).criterion("has_iron_horseshoe", hasItems(UItems.IRON_HORSE_SHOE)).build(consumer, "change_of_shoes")
              .child(UItems.GOLDEN_HORSE_SHOE).criterion("has_gold_horseshoe", hasItems(UItems.GOLDEN_HORSE_SHOE)).build(consumer, "fashionably_expensive")
              .child(UItems.NETHERITE_HORSE_SHOE).criterion("has_netherite_horseshoe", hasItems(UItems.NETHERITE_HORSE_SHOE)).build(consumer, "overkill");
-            p.child(UItems.IRON_HORSE_SHOE).hidden().frame(AdvancementFrame.CHALLENGE).criterion("killed_entity_with_horseshoe", killWithItems(UTags.FROM_HORSESHOES)).build(consumer, "dead_ringer");
+            p.child(UItems.IRON_HORSE_SHOE).hidden().frame(AdvancementFrame.CHALLENGE).criterion("killed_entity_with_horseshoe", killWithItems(UTags.DamageTypes.FROM_HORSESHOES)).build(consumer, "dead_ringer");
         });
         parent.child(UItems.PINECONE).frame(AdvancementFrame.CHALLENGE).criterion("eat_pinecone", ConsumeItemCriterion.Conditions.item(UItems.PINECONE)).build(consumer, "eat_pinecone");
-        parent.child(UItems.OAK_BASKET).doNotAnnounce().criterion("has_basket", hasItems(UTags.BASKETS)).build(consumer, "basket_case")
+        parent.child(UItems.OAK_BASKET).doNotAnnounce().criterion("has_basket", hasItems(UTags.Items.BASKETS)).build(consumer, "basket_case")
             .child(Items.LANTERN).criterion("construct_balloon", CustomEventCriterion.create("construct_balloon")).build(consumer, "aeronaut")
             .child(UItems.GIANT_BALLOON).announce().frame(AdvancementFrame.CHALLENGE).criterion("ride_balloon", CustomEventCriterion.create("ride_balloon")).build(consumer, "travelling_in_style");
         parent.child(UItems.MUFFIN).hidden().criterion("has_muffin", hasItems(UItems.MUFFIN)).build(consumer, "baked_bads");
         parent.child(UItems.HORSE_SHOE_FRIES).criterion("has_horse_shoe_fries", hasItems(UItems.HORSE_SHOE_FRIES)).build(consumer, "lucky");
         parent.child(UItems.TOAST).criterion("has_toast", hasItems(UItems.TOAST)).build(consumer, "toast")
             .child(UItems.BURNED_TOAST).hidden().criterion("has_burned_toast", hasItems(UItems.BURNED_TOAST)).build(consumer, "burn_toast");
-        parent.child(UItems.GREEN_APPLE).criterion("has_apple", hasItems(UTags.FRESH_APPLES)).build(consumer, "apple_route").children(p -> {
+        parent.child(UItems.GREEN_APPLE).criterion("has_apple", hasItems(UTags.Items.FRESH_APPLES)).build(consumer, "apple_route").children(p -> {
             p.child(UItems.SWEET_APPLE).criterion("has_all_apples", hasItems(Items.APPLE, UItems.GREEN_APPLE, UItems.SWEET_APPLE, UItems.SOUR_APPLE, UItems.ROTTEN_APPLE, UItems.ZAP_APPLE, UItems.COOKED_ZAP_APPLE, Items.GOLDEN_APPLE)).build(consumer, "sweet_apple_acres");
             p.child(UItems.ZAP_BULB).criterion("has_zap_apple", hasItems(UItems.ZAP_APPLE)).build(consumer, "trick_apple").children(pp -> {
                 pp.child(UItems.ZAP_APPLE).hidden().criterion("eat_trick_apple", CustomEventCriterion.createFlying("eat_trick_apple")).build(consumer, "eat_trick_apple");
@@ -110,7 +110,7 @@ public class UAdvancementsProvider extends FabricAdvancementProvider {
         parent.child(Items.GLASS_PANE).criterion("break_window", CustomEventCriterion.createFlying("break_window")).rewards(AdvancementRewards.Builder.experience(10)).build(consumer, "rainbow_crash");
         parent.child(UItems.PEGASUS_BADGE).criterion("fly_through_the_pain", CustomEventCriterion.createFlying("second_wind")).rewards(AdvancementRewards.Builder.experience(10)).build(consumer, "second_wind");
         parent.child(UItems.EMPTY_JAR).criterion("has_empty_jar", hasItems(UItems.EMPTY_JAR)).build(consumer, "jar")
-            .child(UItems.RAIN_CLOUD_JAR).criterion("has_cloud_jar", hasItems(UTags.CLOUD_JARS)).rewards(AdvancementRewards.Builder.experience(55)).build(consumer, "gotcha");
+            .child(UItems.RAIN_CLOUD_JAR).criterion("has_cloud_jar", hasItems(UTags.Items.CLOUD_JARS)).rewards(AdvancementRewards.Builder.experience(55)).build(consumer, "gotcha");
         parent.child(UItems.LIGHTNING_JAR).frame(AdvancementFrame.CHALLENGE).criterion("lightning_strike", CustomEventCriterion.createFlying("lightning_strike")).rewards(AdvancementRewards.Builder.experience(30)).build(consumer, "mid_flight_interruption").children(p -> {
             p.child(UItems.LIGHTNING_JAR).hidden().frame(AdvancementFrame.CHALLENGE).apply(d -> applyLightningBugCriterions(d, RacePredicate.of(Set.of(Race.CHANGELING), Set.of()), 10, 90)).build(consumer, "lightning_bug");
             p.child(UItems.LIGHTNING_JAR).hidden().frame(AdvancementFrame.CHALLENGE).apply(d -> applyLightningBugCriterions(d, RacePredicate.of(Set.of(), Set.of(Race.CHANGELING)), 10, 90)).build(consumer, "wonder_bolt");
@@ -255,7 +255,7 @@ public class UAdvancementsProvider extends FabricAdvancementProvider {
     static CriterionConditions dingCelestia(Set<Race> includeTribes, Set<Race> excludeTribes) {
         return new SendViaDragonBreathScrollCriterion.Conditions(
                 LootContextPredicate.EMPTY,
-                ItemPredicate.Builder.create().tag(UTags.IS_DELIVERED_AGGRESSIVELY).build(), false,
+                ItemPredicate.Builder.create().tag(UTags.Items.IS_DELIVERED_AGGRESSIVELY).build(), false,
                 Optional.of("princess celestia"),
                 TriState.FALSE,
                 Optional.of("dings_on_celestias_head"),
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestAdditionsLootTableProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestAdditionsLootTableProvider.java
index 72ad8a91..266c73a7 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestAdditionsLootTableProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestAdditionsLootTableProvider.java
@@ -39,7 +39,7 @@ public class UChestAdditionsLootTableProvider extends SimpleFabricLootTableProvi
                 .rolls(UniformLootNumberProvider.create(2, 4))
                 .with(ItemEntry.builder(UItems.GRYPHON_FEATHER).weight(10).apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 7))))
                 .with(ItemEntry.builder(UItems.GOLDEN_WING).weight(1).apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 2))))
-                .with(TagEntry.expandBuilder(UTags.FRESH_APPLES).weight(1).apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(2, 5))))
+                .with(TagEntry.expandBuilder(UTags.Items.FRESH_APPLES).weight(1).apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(2, 5))))
         ));
         exporter.accept(LootTables.VILLAGE_FLETCHER_CHEST, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(2, 4))
@@ -48,8 +48,8 @@ public class UChestAdditionsLootTableProvider extends SimpleFabricLootTableProvi
         ));
         exporter.accept(LootTables.VILLAGE_PLAINS_CHEST, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(3, 4))
-                .with(TagEntry.expandBuilder(UTags.FRESH_APPLES).weight(1))
-                .with(TagEntry.expandBuilder(UTags.APPLE_SEEDS).weight(1))
+                .with(TagEntry.expandBuilder(UTags.Items.FRESH_APPLES).weight(1))
+                .with(TagEntry.expandBuilder(UTags.Items.APPLE_SEEDS).weight(1))
         ));
 
         exporter.accept(LootTables.ANCIENT_CITY_CHEST, LootTable.builder().pool(LootPool.builder()
@@ -59,26 +59,26 @@ public class UChestAdditionsLootTableProvider extends SimpleFabricLootTableProvi
         exporter.accept(LootTables.BURIED_TREASURE_CHEST, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 4))
                 .with(ItemEntry.builder(UItems.PEARL_NECKLACE).weight(1))
-                .with(TagEntry.expandBuilder(UTags.item("food_types/shells")).weight(3))
+                .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(3))
         ));
         exporter.accept(LootTables.SHIPWRECK_SUPPLY_CHEST, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 6))
-                .with(TagEntry.expandBuilder(UTags.SHELLS).weight(3))
+                .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(3))
         ));
         exporter.accept(LootTables.SHIPWRECK_TREASURE_CHEST, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 4))
                 .with(ItemEntry.builder(UItems.PEARL_NECKLACE).weight(1))
-                .with(TagEntry.expandBuilder(UTags.SHELLS).weight(3))
+                .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(3))
         ));
         exporter.accept(LootTables.UNDERWATER_RUIN_BIG_CHEST, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 2))
                 .with(ItemEntry.builder(UItems.PEARL_NECKLACE).weight(1))
                 .with(ItemEntry.builder(UItems.SHELLY).weight(4))
-                .with(TagEntry.expandBuilder(UTags.SHELLS).weight(8))
+                .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(8))
         ));
         exporter.accept(LootTables.UNDERWATER_RUIN_SMALL_CHEST, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 4))
-                .with(TagEntry.expandBuilder(UTags.SHELLS).weight(1))
+                .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(1))
         ));
 
         exporter.accept(LootTables.DESERT_WELL_ARCHAEOLOGY, LootTable.builder().pool(LootPool.builder()
@@ -89,7 +89,7 @@ public class UChestAdditionsLootTableProvider extends SimpleFabricLootTableProvi
                 .with(ItemEntry.builder(UItems.ROCK_STEW).weight(1))
                 .with(ItemEntry.builder(UItems.PEBBLES).weight(1))
                 .with(ItemEntry.builder(UItems.SHELLY).weight(1))
-                .with(TagEntry.expandBuilder(UTags.SHELLS).weight(1))
+                .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(1))
                 .with(ItemEntry.builder(UItems.PEARL_NECKLACE).weight(1))
         ));
         exporter.accept(LootTables.TRAIL_RUINS_COMMON_ARCHAEOLOGY, LootTable.builder().pool(LootPool.builder()
@@ -106,13 +106,13 @@ public class UChestAdditionsLootTableProvider extends SimpleFabricLootTableProvi
         ));
         exporter.accept(LootTables.OCEAN_RUIN_WARM_ARCHAEOLOGY, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 4))
-                .with(TagEntry.expandBuilder(UTags.SHELLS).weight(1))
+                .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(1))
                 .with(ItemEntry.builder(UItems.PEARL_NECKLACE).weight(1))
         ));
 
         exporter.accept(LootTables.FISHING_GAMEPLAY, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 4))
-                .with(TagEntry.expandBuilder(UTags.SHELLS).weight(2))
+                .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(2))
         ));
 
         exporter.accept(LootTables.FISHING_JUNK_GAMEPLAY, LootTable.builder().pool(LootPool.builder()
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/BedSheetPatternRecipeBuilder.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/BedSheetPatternRecipeBuilder.java
index 0cd69ce6..2e067830 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/BedSheetPatternRecipeBuilder.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/BedSheetPatternRecipeBuilder.java
@@ -73,7 +73,7 @@ public class BedSheetPatternRecipeBuilder {
 
     private static void offerBedSheetConversionRecipe(Consumer<RecipeJsonProvider> exporter, ItemConvertible output, Stream<ItemConvertible> wools) {
         var builder = ShapelessRecipeJsonBuilder.create(RecipeCategory.DECORATIONS, output)
-            .input(UTags.WOOL_BED_SHEETS).criterion("has_bed_sheet", RecipeProvider.conditionsFromTag(UTags.WOOL_BED_SHEETS));
+            .input(UTags.Items.WOOL_BED_SHEETS).criterion("has_bed_sheet", RecipeProvider.conditionsFromTag(UTags.Items.WOOL_BED_SHEETS));
         wools.forEach(builder::input);
         builder
             .group("bed_sheet")
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 3a95e32e..3e27692b 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
@@ -80,8 +80,19 @@ public class URecipeProvider extends FabricRecipeProvider {
     }
 
     private void generateVanillaRecipeExtensions(Consumer<RecipeJsonProvider> exporter) {
-        ShapelessRecipeJsonBuilder.create(RecipeCategory.MISC, Items.WRITABLE_BOOK).input(Items.BOOK).input(Items.INK_SAC).input(UTags.MAGIC_FEATHERS).criterion("has_book", conditionsFromItem(Items.BOOK)).offerTo(exporter);
-        ShapedRecipeJsonBuilder.create(RecipeCategory.COMBAT, Items.ARROW, 4).input('#', UConventionalTags.STICKS).input('X', Items.FLINT).input('Y', UTags.MAGIC_FEATHERS).pattern("X").pattern("#").pattern("Y").criterion("has_feather", conditionsFromTag(UTags.MAGIC_FEATHERS)).criterion("has_flint", conditionsFromItem(Items.FLINT)).offerTo(exporter);
+        ShapelessRecipeJsonBuilder.create(RecipeCategory.MISC, Items.WRITABLE_BOOK)
+            .input(Items.BOOK).criterion("has_book", conditionsFromItem(Items.BOOK))
+            .input(Items.INK_SAC)
+            .input(UTags.Items.MAGIC_FEATHERS)
+            .offerTo(exporter);
+        ShapedRecipeJsonBuilder.create(RecipeCategory.COMBAT, Items.ARROW, 4)
+            .input('#', UConventionalTags.Items.STICKS)
+            .input('X', Items.FLINT).criterion("has_flint", conditionsFromItem(Items.FLINT))
+            .input('Y', UTags.Items.MAGIC_FEATHERS).criterion("has_feather", conditionsFromTag(UTags.Items.MAGIC_FEATHERS))
+            .pattern("X")
+            .pattern("#")
+            .pattern("Y")
+            .offerTo(exporter);
     }
 
     private void offerJarRecipes(Consumer<RecipeJsonProvider> exporter) {
@@ -97,7 +108,7 @@ public class URecipeProvider extends FabricRecipeProvider {
     }
 
     private void offerCloudRecipes(Consumer<RecipeJsonProvider> exporter) {
-        offerShapelessRecipe(exporter, UItems.CLOUD_LUMP, UTags.CLOUD_JARS, "cloud", 4);
+        offerShapelessRecipe(exporter, UItems.CLOUD_LUMP, UTags.Items.CLOUD_JARS, "cloud", 4);
         generateFamily(exporter, UBlockFamilies.CLOUD);
         offer2x3Recipe(exporter, UBlocks.CLOUD_PILLAR, UBlocks.CLOUD, "pillar");
         offer2x2CompactingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, UBlocks.CLOUD, UItems.CLOUD_LUMP);
@@ -200,7 +211,7 @@ public class URecipeProvider extends FabricRecipeProvider {
         // magic staff
         ShapedRecipeJsonBuilder.create(RecipeCategory.TOOLS, UItems.MAGIC_STAFF)
             .input('*', UItems.GEMSTONE).criterion("has_gemstone", conditionsFromItem(UItems.GEMSTONE))
-            .input('#', UConventionalTags.STICKS).criterion("has_stick", conditionsFromTag(UConventionalTags.STICKS))
+            .input('#', UConventionalTags.Items.STICKS).criterion("has_stick", conditionsFromTag(UConventionalTags.Items.STICKS))
             .pattern("  *")
             .pattern(" # ")
             .pattern("#  ")
@@ -217,7 +228,7 @@ public class URecipeProvider extends FabricRecipeProvider {
         // pegasus amulet
         ShapedRecipeJsonBuilder.create(RecipeCategory.MISC, UItems.GOLDEN_FEATHER)
             .input('*', Items.GOLD_NUGGET).criterion("has_nugget", conditionsFromItem(Items.GOLD_NUGGET))
-            .input('#', UTags.MAGIC_FEATHERS).criterion("has_feather", conditionsFromTag(UTags.MAGIC_FEATHERS))
+            .input('#', UTags.Items.MAGIC_FEATHERS).criterion("has_feather", conditionsFromTag(UTags.Items.MAGIC_FEATHERS))
             .pattern("***")
             .pattern("*#*")
             .pattern("***")
@@ -232,7 +243,7 @@ public class URecipeProvider extends FabricRecipeProvider {
         // friendship bracelet
         ShapedRecipeJsonBuilder.create(RecipeCategory.TOOLS, UItems.FRIENDSHIP_BRACELET)
             .input('*', Items.STRING)
-            .input('#', Items.LEATHER).criterion(hasItem(Items.LEATHER), conditionsFromTag(UTags.MAGIC_FEATHERS))
+            .input('#', Items.LEATHER).criterion(hasItem(Items.LEATHER), conditionsFromItem(Items.LEATHER))
             .pattern("*#*")
             .pattern("# #")
             .pattern("*#*")
@@ -242,7 +253,7 @@ public class URecipeProvider extends FabricRecipeProvider {
         // meadowbrook's staff
         ShapedRecipeJsonBuilder.create(RecipeCategory.TOOLS, UItems.MEADOWBROOKS_STAFF)
             .input('*', UItems.GEMSTONE).criterion(hasItem(UItems.GEMSTONE), conditionsFromItem(UItems.GEMSTONE))
-            .input('/', UConventionalTags.STICKS).criterion(hasItem(Items.STICK), conditionsFromTag(UConventionalTags.STICKS))
+            .input('/', UConventionalTags.Items.STICKS).criterion(hasItem(Items.STICK), conditionsFromTag(UConventionalTags.Items.STICKS))
             .pattern("  *")
             .pattern(" / ")
             .pattern("/  ")
@@ -310,7 +321,7 @@ public class URecipeProvider extends FabricRecipeProvider {
         offerShapelessRecipe(exporter, UItems.SOUR_APPLE_SEEDS, UItems.SOUR_APPLE, "seeds", 3);
         offerShapelessRecipe(exporter, UItems.GREEN_APPLE_SEEDS, UItems.GREEN_APPLE, "seeds", 3);
         offerShapelessRecipe(exporter, UItems.GOLDEN_OAK_SEEDS, Items.GOLDEN_APPLE, "seeds", 1);
-        offerPieRecipe(exporter, UItems.APPLE_PIE, UItems.APPLE_PIE_SLICE, Items.WHEAT, UTags.FRESH_APPLES);
+        offerPieRecipe(exporter, UItems.APPLE_PIE, UItems.APPLE_PIE_SLICE, Items.WHEAT, UTags.Items.FRESH_APPLES);
 
         ShapelessRecipeJsonBuilder.create(RecipeCategory.FOOD, UItems.ROCK_STEW)
             .input(UItems.ROCK, 3).criterion(hasItem(UItems.ROCK), conditionsFromItem(UItems.ROCK))
@@ -344,20 +355,20 @@ public class URecipeProvider extends FabricRecipeProvider {
             .pattern("###")
             .offerTo(exporter, convertBetween(UItems.OATS, Items.WHEAT));
         ShapelessRecipeJsonBuilder.create(RecipeCategory.FOOD, UItems.JUICE)
-            .input(Ingredient.fromTag(UTags.FRESH_APPLES), 6).criterion(hasItem(Items.APPLE), conditionsFromTag(UTags.FRESH_APPLES))
+            .input(Ingredient.fromTag(UTags.Items.FRESH_APPLES), 6).criterion(hasItem(Items.APPLE), conditionsFromTag(UTags.Items.FRESH_APPLES))
             .input(Items.GLASS_BOTTLE)
             .group("juice")
             .offerTo(exporter);
         appendIngredients(ShapelessRecipeJsonBuilder.create(RecipeCategory.FOOD, UItems.MUFFIN), Items.SUGAR, Items.EGG, Items.POTATO, UItems.JUICE, UItems.WHEAT_WORMS).offerTo(exporter);
         ShapedRecipeJsonBuilder.create(RecipeCategory.MISC, UItems.MUG)
             .input('*', Items.IRON_NUGGET).criterion(hasItem(Items.IRON_NUGGET), conditionsFromItem(Items.IRON_NUGGET))
-            .input('#', UConventionalTags.STICKS).criterion(hasItem(Items.STICK), conditionsFromTag(UConventionalTags.STICKS))
+            .input('#', UConventionalTags.Items.STICKS).criterion(hasItem(Items.STICK), conditionsFromTag(UConventionalTags.Items.STICKS))
             .pattern("# #")
             .pattern("* *")
             .pattern(" # ")
             .offerTo(exporter);
         appendIngredients(ShapelessRecipeJsonBuilder.create(RecipeCategory.FOOD, UItems.CIDER), UItems.BURNED_JUICE, UItems.MUG)
-            .input(Ingredient.fromTag(UTags.FRESH_APPLES)).criterion(hasItem(Items.APPLE), conditionsFromTag(UTags.FRESH_APPLES))
+            .input(Ingredient.fromTag(UTags.Items.FRESH_APPLES)).criterion(hasItem(Items.APPLE), conditionsFromTag(UTags.Items.FRESH_APPLES))
             .offerTo(exporter);
 
         ShapedRecipeJsonBuilder.create(RecipeCategory.FOOD, UItems.HAY_FRIES)
@@ -410,8 +421,8 @@ public class URecipeProvider extends FabricRecipeProvider {
             .input(UItems.TOAST, 8)
             .offerTo(exporter);
         ShapelessRecipeJsonBuilder.create(RecipeCategory.FOOD, UItems.CANDIED_APPLE)
-            .input(UConventionalTags.STICKS)
-            .input(UTags.FRESH_APPLES).criterion(hasItem(UItems.ZAP_APPLE_JAM_JAR), conditionsFromItem(UItems.ZAP_APPLE_JAM_JAR))
+            .input(UConventionalTags.Items.STICKS)
+            .input(UTags.Items.FRESH_APPLES).criterion(hasItem(Items.APPLE), conditionsFromTag(UTags.Items.FRESH_APPLES))
             .input(Items.SUGAR, 4)
             .offerTo(exporter);
 
@@ -445,7 +456,7 @@ public class URecipeProvider extends FabricRecipeProvider {
             .pattern(" C ")
             .offerTo(exporter);
         ShapedRecipeJsonBuilder.create(RecipeCategory.COMBAT, UItems.PEARL_NECKLACE)
-            .input('#', UTags.SHELLS).criterion("has_shell", conditionsFromTag(UTags.SHELLS))
+            .input('#', UTags.Items.SHELLS).criterion("has_shell", conditionsFromTag(UTags.Items.SHELLS))
             .input('~', Items.STRING)
             .pattern("# #")
             .pattern("# #")
@@ -492,7 +503,7 @@ public class URecipeProvider extends FabricRecipeProvider {
             .pattern("#*")
             .offerTo(exporter, convertBetween(Items.DIRT, UItems.WHEAT_WORMS));
 
-        offerShapelessRecipe(exporter, Items.BONE_MEAL, UTags.SHELLS, "bonemeal", 3);
+        offerShapelessRecipe(exporter, Items.BONE_MEAL, UTags.Items.SHELLS, "bonemeal", 3);
 
         // pegasus feathers for non pegasi
         ShapedRecipeJsonBuilder.create(RecipeCategory.MISC, UItems.PEGASUS_FEATHER)
@@ -578,7 +589,7 @@ public class URecipeProvider extends FabricRecipeProvider {
     public static void offerPolearmRecipe(Consumer<RecipeJsonProvider> exporter, ItemConvertible output, Either<ItemConvertible, TagKey<Item>> input) {
         CraftingMaterialHelper
             .input(ShapedRecipeJsonBuilder.create(RecipeCategory.TOOLS, output), 'o', input).criterion(CraftingMaterialHelper.hasEither(input), CraftingMaterialHelper.conditionsFromEither(input))
-            .input('#', UConventionalTags.STICKS)
+            .input('#', UConventionalTags.Items.STICKS)
             .pattern("  o")
             .pattern(" # ")
             .pattern("#  ")
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java
index f4c583a4..6198be9f 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java
@@ -44,14 +44,14 @@ public class UBlockTagProvider extends FabricTagProvider.BlockTagProvider {
                 UBlocks.GOLDEN_OAK_SPROUT
         };
 
-        getOrCreateTagBuilder(UTags.CATAPULT_IMMUNE).add(Blocks.BEDROCK).forceAddTag(BlockTags.DOORS).forceAddTag(BlockTags.TRAPDOORS);
+        getOrCreateTagBuilder(UTags.Blocks.CATAPULT_IMMUNE).add(Blocks.BEDROCK).forceAddTag(BlockTags.DOORS).forceAddTag(BlockTags.TRAPDOORS);
         getOrCreateTagBuilder(UTags.Blocks.BUTTERFLIES_SPAWNABLE_ON).forceAddTag(BlockTags.ANIMALS_SPAWNABLE_ON).forceAddTag(BlockTags.LEAVES).forceAddTag(BlockTags.FLOWERS).forceAddTag(BlockTags.FLOWER_POTS);
-        getOrCreateTagBuilder(UTags.JARS).add(UBlocks.JAR, UBlocks.CLOUD_JAR, UBlocks.STORM_JAR, UBlocks.LIGHTNING_JAR, UBlocks.ZAP_JAR);
+        getOrCreateTagBuilder(UTags.Blocks.JARS).add(UBlocks.JAR, UBlocks.CLOUD_JAR, UBlocks.STORM_JAR, UBlocks.LIGHTNING_JAR, UBlocks.ZAP_JAR);
         getOrCreateTagBuilder(BlockTags.CROPS).add(crops);
         getOrCreateTagBuilder(BlockTags.BEE_GROWABLES).add(crops);
         getOrCreateTagBuilder(BlockTags.MAINTAINS_FARMLAND).add(crops);
         getOrCreateTagBuilder(BlockTags.NEEDS_DIAMOND_TOOL).add(UBlocks.FROSTED_OBSIDIAN);
-        getOrCreateTagBuilder(BlockTags.PICKAXE_MINEABLE).add(UBlocks.ROCKS, UBlocks.FROSTED_OBSIDIAN, UBlocks.WEATHER_VANE).forceAddTag(UTags.JARS);
+        getOrCreateTagBuilder(BlockTags.PICKAXE_MINEABLE).add(UBlocks.ROCKS, UBlocks.FROSTED_OBSIDIAN, UBlocks.WEATHER_VANE).forceAddTag(UTags.Blocks.JARS);
         getOrCreateTagBuilder(BlockTags.DRAGON_IMMUNE).add(UBlocks.FROSTED_OBSIDIAN, UBlocks.GOLDEN_OAK_LOG, UBlocks.GOLDEN_OAK_LEAVES);
         getOrCreateTagBuilder(BlockTags.FIRE).add(UBlocks.SPECTRAL_FIRE);
         getOrCreateTagBuilder(BlockTags.HOE_MINEABLE).add(UBlocks.HAY_BLOCK).addOptional(Unicopia.id("rice_block")).addOptional(Unicopia.id("straw_block"));
@@ -63,31 +63,31 @@ public class UBlockTagProvider extends FabricTagProvider.BlockTagProvider {
         addChitinBlocksets();
         addFruitTrees();
 
-        getOrCreateTagBuilder(UTags.CRYSTAL_HEART_BASE).add(
+        getOrCreateTagBuilder(UTags.Blocks.CRYSTAL_HEART_BASE).add(
                 Blocks.DIAMOND_BLOCK,
                 Blocks.QUARTZ_BLOCK, Blocks.QUARTZ_BRICKS, Blocks.QUARTZ_SLAB, Blocks.QUARTZ_STAIRS, Blocks.QUARTZ_PILLAR,
                 Blocks.SMOOTH_QUARTZ, Blocks.SMOOTH_QUARTZ_SLAB, Blocks.SMOOTH_QUARTZ_STAIRS, Blocks.CHISELED_QUARTZ_BLOCK,
                 Blocks.AMETHYST_BLOCK, Blocks.NETHERITE_BLOCK, Blocks.EMERALD_BLOCK
         );
-        getOrCreateTagBuilder(UTags.CRYSTAL_HEART_ORNAMENT).add(Blocks.END_ROD);
+        getOrCreateTagBuilder(UTags.Blocks.CRYSTAL_HEART_ORNAMENT).add(Blocks.END_ROD);
 
-        getOrCreateTagBuilder(UTags.FRAGILE)
+        getOrCreateTagBuilder(UTags.Blocks.FRAGILE)
             .forceAddTag(ConventionalBlockTags.GLASS_BLOCKS)
             .forceAddTag(ConventionalBlockTags.GLASS_PANES)
-            .forceAddTag(UTags.JARS)
+            .forceAddTag(UTags.Blocks.JARS)
             .add(Blocks.VINE, Blocks.LILY_PAD);
 
-        getOrCreateTagBuilder(UTags.INTERESTING).add(
+        getOrCreateTagBuilder(UTags.Blocks.INTERESTING).add(
                 Blocks.SEA_LANTERN, Blocks.ENDER_CHEST, Blocks.END_PORTAL_FRAME,
                 Blocks.JUKEBOX, Blocks.SPAWNER
         ).forceAddTag(ConventionalBlockTags.ORES);
 
-        getOrCreateTagBuilder(UTags.KICKS_UP_DUST).forceAddTag(BlockTags.SAND).add(
+        getOrCreateTagBuilder(UTags.Blocks.KICKS_UP_DUST).forceAddTag(BlockTags.SAND).add(
                 Blocks.SUSPICIOUS_SAND,
                 Blocks.GRAVEL, Blocks.SUSPICIOUS_GRAVEL
         ).forceAddTag(TagKey.of(RegistryKeys.BLOCK, new Identifier("c", "concrete_powders")));
 
-        getOrCreateTagBuilder(UTags.UNAFFECTED_BY_GROW_ABILITY).add(Blocks.GRASS_BLOCK);
+        getOrCreateTagBuilder(UTags.Blocks.UNAFFECTED_BY_GROW_ABILITY).add(Blocks.GRASS_BLOCK);
     }
 
     private void addFruitTrees() {
@@ -112,7 +112,7 @@ public class UBlockTagProvider extends FabricTagProvider.BlockTagProvider {
 
     private void addZapWoodset() {
         getOrCreateTagBuilder(BlockTags.LEAVES).add(UBlocks.ZAP_LEAVES, UBlocks.FLOWERING_ZAP_LEAVES);
-        getOrCreateTagBuilder(UTags.POLEARM_MINEABLE).add(
+        getOrCreateTagBuilder(UTags.Blocks.POLEARM_MINEABLE).add(
                 UBlocks.ZAP_LEAVES, UBlocks.FLOWERING_ZAP_LEAVES,
                 UBlocks.ZAP_PLANKS,
                 UBlocks.ZAP_LOG, UBlocks.ZAP_WOOD, UBlocks.STRIPPED_ZAP_LOG, UBlocks.STRIPPED_ZAP_WOOD,
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UDamageTypeProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UDamageTypeProvider.java
index e4170071..d0b265b1 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UDamageTypeProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UDamageTypeProvider.java
@@ -19,12 +19,12 @@ public class UDamageTypeProvider extends FabricTagProvider<DamageType> {
 
     @Override
     protected void configure(WrapperLookup lookup) {
-        getOrCreateTagBuilder(UTags.SPELLBOOK_IMMUNE_TO).add(
+        getOrCreateTagBuilder(UTags.DamageTypes.SPELLBOOK_IMMUNE_TO).add(
                 UDamageTypes.ZAP_APPLE, UDamageTypes.LOVE_DRAINING, UDamageTypes.LIFE_DRAINING,
                 UDamageTypes.RAINBOOM, UDamageTypes.SUN, UDamageTypes.SUNLIGHT, UDamageTypes.SMASH
         ).forceAddTag(DamageTypeTags.IS_FALL).forceAddTag(DamageTypeTags.IS_FREEZING).forceAddTag(DamageTypeTags.IS_LIGHTNING).forceAddTag(DamageTypeTags.IS_PROJECTILE);
-        getOrCreateTagBuilder(UTags.FROM_ROCKS).add(UDamageTypes.ROCK);
-        getOrCreateTagBuilder(UTags.FROM_HORSESHOES).add(UDamageTypes.HORSESHOE);
-        getOrCreateTagBuilder(UTags.BREAKS_SUNGLASSES).add(UDamageTypes.BAT_SCREECH, UDamageTypes.RAINBOOM);
+        getOrCreateTagBuilder(UTags.DamageTypes.FROM_ROCKS).add(UDamageTypes.ROCK);
+        getOrCreateTagBuilder(UTags.DamageTypes.FROM_HORSESHOES).add(UDamageTypes.HORSESHOE);
+        getOrCreateTagBuilder(UTags.DamageTypes.BREAKS_SUNGLASSES).add(UDamageTypes.BAT_SCREECH, UDamageTypes.RAINBOOM);
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
index 65c5d92a..30b0c9ca 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
@@ -1,4 +1,4 @@
-package com.minelittlepony.unicopia.datagen.providers;
+package com.minelittlepony.unicopia.datagen.providers.tag;
 
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
@@ -66,50 +66,50 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
         getOrCreateTagBuilder(ItemTags.SIGNS).add(UBlocks.PALM_SIGN.asItem());
         getOrCreateTagBuilder(ItemTags.HANGING_SIGNS).add(UBlocks.PALM_HANGING_SIGN.asItem());
 
-        getOrCreateTagBuilder(UTags.HORSE_SHOES).add(ItemFamilies.HORSE_SHOES);
-        getOrCreateTagBuilder(UTags.POLEARMS).add(ItemFamilies.POLEARMS);
+        getOrCreateTagBuilder(UTags.Items.HORSE_SHOES).add(ItemFamilies.HORSE_SHOES);
+        getOrCreateTagBuilder(UTags.Items.POLEARMS).add(ItemFamilies.POLEARMS);
 
-        getOrCreateTagBuilder(ItemTags.TOOLS).addTag(UTags.HORSE_SHOES).addTag(UTags.POLEARMS);
+        getOrCreateTagBuilder(ItemTags.TOOLS).addTag(UTags.Items.HORSE_SHOES).addTag(UTags.Items.POLEARMS);
 
-        getOrCreateTagBuilder(UTags.BASKETS).add(ItemFamilies.BASKETS);
-        getOrCreateTagBuilder(UTags.BADGES).add(Race.REGISTRY.stream()
+        getOrCreateTagBuilder(UTags.Items.BASKETS).add(ItemFamilies.BASKETS);
+        getOrCreateTagBuilder(UTags.Items.BADGES).add(Race.REGISTRY.stream()
                 .map(race -> race.getId().withPath(p -> p + "_badge"))
                 .flatMap(id -> Registries.ITEM.getOrEmpty(id).stream())
                 .toArray(Item[]::new));
-        getOrCreateTagBuilder(UTags.WOOL_BED_SHEETS).add(BedsheetsItem.ITEMS.values().stream().filter(sheet -> sheet != UItems.KELP_BED_SHEETS).toArray(Item[]::new));
-        getOrCreateTagBuilder(UTags.BED_SHEETS).forceAddTag(UTags.WOOL_BED_SHEETS).add(UItems.KELP_BED_SHEETS);
-        getOrCreateTagBuilder(UTags.APPLE_SEEDS).add(UItems.GREEN_APPLE_SEEDS, UItems.SWEET_APPLE_SEEDS, UItems.SOUR_APPLE_SEEDS);
-        getOrCreateTagBuilder(UTags.MAGIC_FEATHERS).add(UItems.PEGASUS_FEATHER, UItems.GRYPHON_FEATHER);
-        getOrCreateTagBuilder(UTags.FRESH_APPLES).add(Items.APPLE, UItems.GREEN_APPLE, UItems.SWEET_APPLE, UItems.SOUR_APPLE);
-        getOrCreateTagBuilder(UTags.CLOUD_JARS).add(UItems.RAIN_CLOUD_JAR, UItems.STORM_CLOUD_JAR);
-        getOrCreateTagBuilder(UTags.PIES).add(UItems.APPLE_PIE, UItems.APPLE_PIE_HOOF);
+        getOrCreateTagBuilder(UTags.Items.WOOL_BED_SHEETS).add(BedsheetsItem.ITEMS.values().stream().filter(sheet -> sheet != UItems.KELP_BED_SHEETS).toArray(Item[]::new));
+        getOrCreateTagBuilder(UTags.Items.BED_SHEETS).forceAddTag(UTags.Items.WOOL_BED_SHEETS).add(UItems.KELP_BED_SHEETS);
+        getOrCreateTagBuilder(UTags.Items.APPLE_SEEDS).add(UItems.GREEN_APPLE_SEEDS, UItems.SWEET_APPLE_SEEDS, UItems.SOUR_APPLE_SEEDS);
+        getOrCreateTagBuilder(UTags.Items.MAGIC_FEATHERS).add(UItems.PEGASUS_FEATHER, UItems.GRYPHON_FEATHER);
+        getOrCreateTagBuilder(UTags.Items.FRESH_APPLES).add(Items.APPLE, UItems.GREEN_APPLE, UItems.SWEET_APPLE, UItems.SOUR_APPLE);
+        getOrCreateTagBuilder(UTags.Items.CLOUD_JARS).add(UItems.RAIN_CLOUD_JAR, UItems.STORM_CLOUD_JAR);
+        getOrCreateTagBuilder(UTags.Items.PIES).add(UItems.APPLE_PIE, UItems.APPLE_PIE_HOOF);
 
         // technical tags
-        getOrCreateTagBuilder(ItemTags.VILLAGER_PLANTABLE_SEEDS).addTag(UTags.APPLE_SEEDS);
-        getOrCreateTagBuilder(UTags.CAN_CUT_PIE).forceAddTag(ConventionalItemTags.SHEARS).addOptionalTag(UConventionalTags.TOOL_KNIVES);
-        getOrCreateTagBuilder(UTags.COOLS_OFF_KIRINS).add(Items.MELON_SLICE, UItems.JUICE).forceAddTag(ConventionalItemTags.WATER_BUCKETS);
-        getOrCreateTagBuilder(UTags.FALLS_SLOWLY).add(Items.FEATHER, UItems.CLOUD_LUMP).forceAddTag(UTags.MAGIC_FEATHERS);
-        getOrCreateTagBuilder(UTags.IS_DELIVERED_AGGRESSIVELY).forceAddTag(ItemTags.ANVIL);
-        getOrCreateTagBuilder(UTags.SPOOKED_MOB_DROPS).add(Items.BRICK);
-        getOrCreateTagBuilder(UTags.SHADES).add(
+        getOrCreateTagBuilder(ItemTags.VILLAGER_PLANTABLE_SEEDS).addTag(UTags.Items.APPLE_SEEDS);
+        getOrCreateTagBuilder(UTags.Items.CAN_CUT_PIE).forceAddTag(ConventionalItemTags.SHEARS).addOptionalTag(UConventionalTags.Items.TOOL_KNIVES);
+        getOrCreateTagBuilder(UTags.Items.COOLS_OFF_KIRINS).add(Items.MELON_SLICE, UItems.JUICE).forceAddTag(ConventionalItemTags.WATER_BUCKETS);
+        getOrCreateTagBuilder(UTags.Items.FALLS_SLOWLY).add(Items.FEATHER, UItems.CLOUD_LUMP).forceAddTag(UTags.Items.MAGIC_FEATHERS);
+        getOrCreateTagBuilder(UTags.Items.IS_DELIVERED_AGGRESSIVELY).forceAddTag(ItemTags.ANVIL);
+        getOrCreateTagBuilder(UTags.Items.SPOOKED_MOB_DROPS).add(Items.BRICK);
+        getOrCreateTagBuilder(UTags.Items.SHADES).add(
                 Items.CARVED_PUMPKIN, Items.SKELETON_SKULL, Items.WITHER_SKELETON_SKULL, Items.PLAYER_HEAD,
                 Items.ZOMBIE_HEAD, Items.CREEPER_HEAD, Items.DRAGON_HEAD, Items.PIGLIN_HEAD,
                 UItems.SUNGLASSES
         );
-        getOrCreateTagBuilder(UTags.FLOATS_ON_CLOUDS)
+        getOrCreateTagBuilder(UTags.Items.FLOATS_ON_CLOUDS)
             .forceAddTag(UTags.Items.CLOUD_BEDS)
             .forceAddTag(UTags.Items.CLOUD_SLABS)
             .forceAddTag(UTags.Items.CLOUD_STAIRS)
             .forceAddTag(UTags.Items.CLOUD_BLOCKS)
             .add(UItems.CLOUD_LUMP);
-        getOrCreateTagBuilder(UTags.HAS_NO_TRAITS).add(
+        getOrCreateTagBuilder(UTags.Items.HAS_NO_TRAITS).add(
                 Items.AIR, Items.SPAWNER, Items.STRUCTURE_VOID, Items.STRUCTURE_BLOCK,
                 Items.COMMAND_BLOCK, Items.CHAIN_COMMAND_BLOCK, Items.REPEATING_COMMAND_BLOCK,
                 Items.LIGHT, Items.JIGSAW, Items.BARRIER, Items.BEDROCK, Items.END_PORTAL_FRAME,
                 Items.DEBUG_STICK, Items.COMMAND_BLOCK_MINECART,
                 UItems.PLUNDER_VINE
-        ).forceAddTag(UTags.BADGES);
-        getOrCreateTagBuilder(UTags.LOOT_BUG_HIGH_VALUE_DROPS).add(
+        ).forceAddTag(UTags.Items.BADGES);
+        getOrCreateTagBuilder(UTags.Items.LOOT_BUG_HIGH_VALUE_DROPS).add(
                     Items.DIAMOND, Items.GOLDEN_APPLE, Items.GOLDEN_CARROT,
                     Items.GOLDEN_HELMET, Items.GOLDEN_BOOTS, Items.GOLDEN_LEGGINGS, Items.GOLDEN_CHESTPLATE,
                     Items.GOLDEN_HORSE_ARMOR,
@@ -152,36 +152,38 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
     }
 
     private void exportConventionalTags() {
-        getOrCreateTagBuilder(UConventionalTags.ACORNS).add(UItems.ACORN);
-        getOrCreateTagBuilder(UConventionalTags.APPLES)
+        copy(UConventionalTags.Blocks.CONCRETES, UConventionalTags.Items.CONCRETES);
+        copy(UConventionalTags.Blocks.CONCRETE_POWDERS, UConventionalTags.Items.CONCRETE_POWDERS);
+        getOrCreateTagBuilder(UConventionalTags.Items.ACORNS).add(UItems.ACORN);
+        getOrCreateTagBuilder(UConventionalTags.Items.APPLES)
             .add(Items.APPLE, Items.GOLDEN_APPLE, Items.ENCHANTED_GOLDEN_APPLE, UItems.ROTTEN_APPLE)
-            .forceAddTag(UTags.FRESH_APPLES)
+            .forceAddTag(UTags.Items.FRESH_APPLES)
             .addOptionalTag(new Identifier("c", "pyrite_apples")) // no idea which mod add pyrite apples
         ;
-        getOrCreateTagBuilder(UConventionalTags.BANANAS).add(UItems.BANANA);
-        getOrCreateTagBuilder(UConventionalTags.COOKED_FISH).add(Items.COOKED_COD, Items.COOKED_SALMON);
-        getOrCreateTagBuilder(UConventionalTags.STICKS).add(Items.STICK);
-        getOrCreateTagBuilder(UConventionalTags.PINECONES).add(UItems.PINECONE);
-        getOrCreateTagBuilder(UConventionalTags.PINEAPPLES).add(UItems.PINEAPPLE);
-        getOrCreateTagBuilder(UConventionalTags.MANGOES).add(UItems.MANGO);
-        getOrCreateTagBuilder(UConventionalTags.MUSHROOMS).add(Items.RED_MUSHROOM, Items.BROWN_MUSHROOM);
-        getOrCreateTagBuilder(UConventionalTags.MUFFINS).add(UItems.MUFFIN);
-        getOrCreateTagBuilder(UConventionalTags.SEEDS).add(Items.BEETROOT_SEEDS, Items.MELON_SEEDS, Items.PUMPKIN_SEEDS, Items.TORCHFLOWER_SEEDS, Items.WHEAT_SEEDS)
+        getOrCreateTagBuilder(UConventionalTags.Items.BANANAS).add(UItems.BANANA);
+        getOrCreateTagBuilder(UConventionalTags.Items.COOKED_FISH).add(Items.COOKED_COD, Items.COOKED_SALMON);
+        getOrCreateTagBuilder(UConventionalTags.Items.STICKS).add(Items.STICK);
+        getOrCreateTagBuilder(UConventionalTags.Items.PINECONES).add(UItems.PINECONE);
+        getOrCreateTagBuilder(UConventionalTags.Items.PINEAPPLES).add(UItems.PINEAPPLE);
+        getOrCreateTagBuilder(UConventionalTags.Items.MANGOES).add(UItems.MANGO);
+        getOrCreateTagBuilder(UConventionalTags.Items.MUSHROOMS).add(Items.RED_MUSHROOM, Items.BROWN_MUSHROOM);
+        getOrCreateTagBuilder(UConventionalTags.Items.MUFFINS).add(UItems.MUFFIN);
+        getOrCreateTagBuilder(UConventionalTags.Items.SEEDS).add(Items.BEETROOT_SEEDS, Items.MELON_SEEDS, Items.PUMPKIN_SEEDS, Items.TORCHFLOWER_SEEDS, Items.WHEAT_SEEDS)
             .add(UItems.OAT_SEEDS)
-            .forceAddTag(UTags.APPLE_SEEDS);
-        getOrCreateTagBuilder(UConventionalTags.OATMEALS).add(UItems.OATMEAL);
-        getOrCreateTagBuilder(UConventionalTags.GRAIN).add(Items.WHEAT, UItems.OATS);
-        getOrCreateTagBuilder(UConventionalTags.NUTS).addOptionalTag(UConventionalTags.CROPS_PEANUTS);
+            .forceAddTag(UTags.Items.APPLE_SEEDS);
+        getOrCreateTagBuilder(UConventionalTags.Items.OATMEALS).add(UItems.OATMEAL);
+        getOrCreateTagBuilder(UConventionalTags.Items.GRAIN).add(Items.WHEAT, UItems.OATS);
+        getOrCreateTagBuilder(UConventionalTags.Items.NUTS).addOptionalTag(UConventionalTags.Items.CROPS_PEANUTS);
 
-        getOrCreateTagBuilder(UConventionalTags.FRUITS)
-            .forceAddTag(UConventionalTags.MANGOES)
-            .forceAddTag(UConventionalTags.PINEAPPLES)
-            .forceAddTag(UConventionalTags.APPLES)
-            .forceAddTag(UConventionalTags.BANANAS);
+        getOrCreateTagBuilder(UConventionalTags.Items.FRUITS)
+            .forceAddTag(UConventionalTags.Items.MANGOES)
+            .forceAddTag(UConventionalTags.Items.PINEAPPLES)
+            .forceAddTag(UConventionalTags.Items.APPLES)
+            .forceAddTag(UConventionalTags.Items.BANANAS);
     }
 
     private void exportFarmersDelightItems() {
-        getOrCreateTagBuilder(UTags.COOLS_OFF_KIRINS)
+        getOrCreateTagBuilder(UTags.Items.COOLS_OFF_KIRINS)
             .addOptional(new Identifier("farmersdelight:melon_popsicle"))
             .addOptional(new Identifier("farmersdelight:melon_juice"));
         getOrCreateTagBuilder(TagKey.of(RegistryKeys.ITEM, new Identifier("farmersdelight:cabbage_roll_ingredients"))).add(UItems.OATS, UItems.ROCK, UItems.WHEAT_WORMS);
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/ItemImpl.java b/src/main/java/com/minelittlepony/unicopia/entity/ItemImpl.java
index 59d84ddb..27e8ca7f 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/ItemImpl.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/ItemImpl.java
@@ -111,7 +111,7 @@ public class ItemImpl implements Equine<ItemEntity> {
                     });
             }
 
-            if (stack.isIn(UTags.FALLS_SLOWLY)) {
+            if (stack.isIn(UTags.Items.FALLS_SLOWLY)) {
                 if (!entity.isOnGround() && Math.signum(entity.getVelocity().y) != getPhysics().getGravitySignum()) {
                     double ticks = ((Entity)entity).age;
                     double shift = Math.sin(ticks / 9D) / 9D;
@@ -160,7 +160,7 @@ public class ItemImpl implements Equine<ItemEntity> {
 
     @Override
     public boolean collidesWithClouds() {
-        return entity.getStack().isIn(UTags.FLOATS_ON_CLOUDS) || getSpecies().hasPersistentWeatherMagic();
+        return entity.getStack().isIn(UTags.Items.FLOATS_ON_CLOUDS) || getSpecies().hasPersistentWeatherMagic();
     }
 
     @Override
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Living.java b/src/main/java/com/minelittlepony/unicopia/entity/Living.java
index fecfe073..1696abef 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/Living.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/Living.java
@@ -310,7 +310,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
                     ItemStack payload = stack.payload();
                     Item item = payload.getItem();
 
-                    boolean deliverAggressively = payload.isIn(UTags.IS_DELIVERED_AGGRESSIVELY);
+                    boolean deliverAggressively = payload.isIn(UTags.Items.IS_DELIVERED_AGGRESSIVELY);
 
                     Vec3d randomPos = deliverAggressively ? targetPos.add(0, 2, 0) : targetPos.add(VecHelper.supply(() -> entity.getRandom().nextTriangular(0.1, 0.5)));
 
@@ -400,7 +400,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
                 this.attacker = attacker;
             }
 
-            if (magical.isIn(UTags.BREAKS_SUNGLASSES)) {
+            if (magical.isIn(UTags.DamageTypes.BREAKS_SUNGLASSES)) {
                 ItemStack glasses = GlassesItem.getForEntity(entity);
                 if (glasses.getItem() == UItems.SUNGLASSES) {
                     ItemStack broken = UItems.BROKEN_SUNGLASSES.getDefaultStack();
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/effect/SunBlindnessStatusEffect.java b/src/main/java/com/minelittlepony/unicopia/entity/effect/SunBlindnessStatusEffect.java
index c9f679c6..10e607a9 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/effect/SunBlindnessStatusEffect.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/effect/SunBlindnessStatusEffect.java
@@ -72,8 +72,8 @@ public class SunBlindnessStatusEffect extends StatusEffect {
             return true;
         }
 
-        if (entity.getEquippedStack(EquipmentSlot.HEAD).isIn(UTags.SHADES)
-            || TrinketsDelegate.getInstance(entity).getEquipped(entity, TrinketsDelegate.FACE).anyMatch(i -> i.isIn(UTags.SHADES))
+        if (entity.getEquippedStack(EquipmentSlot.HEAD).isIn(UTags.Items.SHADES)
+            || TrinketsDelegate.getInstance(entity).getEquipped(entity, TrinketsDelegate.FACE).anyMatch(i -> i.isIn(UTags.Items.SHADES))
             || entity.isSubmergedInWater()) {
             return false;
         }
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java
index da18363f..5808aea4 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/SpellbookEntity.java
@@ -373,7 +373,7 @@ public class SpellbookEntity extends MobEntity implements MagicImmune {
 
     @Override
     public boolean isInvulnerableTo(DamageSource damageSource) {
-        return super.isInvulnerableTo(damageSource) || damageSource.isIn(UTags.SPELLBOOK_IMMUNE_TO);
+        return super.isInvulnerableTo(damageSource) || damageSource.isIn(UTags.DamageTypes.SPELLBOOK_IMMUNE_TO);
     }
 
     @Override
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/mob/UTradeOffers.java b/src/main/java/com/minelittlepony/unicopia/entity/mob/UTradeOffers.java
index ff73a2f8..74e38429 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/mob/UTradeOffers.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/mob/UTradeOffers.java
@@ -35,7 +35,7 @@ public interface UTradeOffers {
             factories.add(buyForEmeralds(UItems.GEMSTONE, 3, 1, 20, 1, 0.05F));
         });
         TradeOfferHelper.registerVillagerOffers(VillagerProfession.FARMER, 2, factories -> {
-            factories.add(buy(Items.EMERALD, 4, UTags.APPLE_SEEDS, 2, 20, 1, 0.05F));
+            factories.add(buy(Items.EMERALD, 4, UTags.Items.APPLE_SEEDS, 2, 20, 1, 0.05F));
             factories.add(buy(Items.EMERALD, 8, UItems.MANGO, 1, 15, 1, 0.025F));
         });
 
@@ -47,7 +47,7 @@ public interface UTradeOffers {
             factories.add(buy(ItemTags.SMALL_FLOWERS, 2, UItems.DAFFODIL_DAISY_SANDWICH, 1, 10, 6, 0.08F));
             factories.add(buy(UItems.ZAP_APPLE, 45, UItems.ZAP_APPLE_JAM_JAR, 5, 50, 3, 0.07F));
             factories.add(buy(UItems.CIDER, 1, UItems.FRIENDSHIP_BRACELET, 1, 6, 1, 0.05F));
-            factories.add(buy(UItems.GEMSTONE, 5, UTags.FRESH_APPLES, 2, 12, 3, 0.05F));
+            factories.add(buy(UItems.GEMSTONE, 5, UTags.Items.FRESH_APPLES, 2, 12, 3, 0.05F));
             factories.add(buy(Items.EMERALD, 4, UItems.MANGO, 1, 35, 1, 0.025F));
             factories.add(new JarredItemTradeOfferFactory());
         });
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 b4db56d0..fc53f9b0 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java
@@ -194,7 +194,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
     private FlightType recalculateFlightType() {
         DimensionType dimension = entity.getWorld().getDimension();
 
-        if ((RegistryUtils.isIn(entity.getWorld(), dimension, RegistryKeys.DIMENSION_TYPE, UTags.HAS_NO_ATMOSPHERE)
+        if ((RegistryUtils.isIn(entity.getWorld(), dimension, RegistryKeys.DIMENSION_TYPE, UTags.DimensionTypes.HAS_NO_ATMOSPHERE)
                 || Unicopia.getConfig().dimensionsWithoutAtmosphere.get().contains(RegistryUtils.getId(entity.getWorld(), dimension, RegistryKeys.DIMENSION_TYPE).toString()))
                 && !OxygenUtils.API.hasOxygen(entity)) {
             return FlightType.NONE;
@@ -582,7 +582,7 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
 
         if (entity.isOnGround() || !force) {
             BlockState steppingState = pony.asEntity().getSteppingBlockState();
-            if (steppingState.isIn(UTags.KICKS_UP_DUST)) {
+            if (steppingState.isIn(UTags.Blocks.KICKS_UP_DUST)) {
                 pony.addParticle(new BlockStateParticleEffect(UParticles.DUST_CLOUD, steppingState), pony.getOrigin().toCenterPos(), Vec3d.ZERO);
             } else {
                 Supplier<Vec3d> pos = VecHelper.sphere(pony.asWorld().getRandom(), 0.5D);
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 10efd22d..fad4a95d 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java
@@ -551,7 +551,7 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
         }
 
         if (getObservedSpecies() == Race.BAT && !entity.hasPortalCooldown()) {
-            boolean hasShades = TrinketsDelegate.getInstance(entity).getEquipped(entity, TrinketsDelegate.FACE).anyMatch(s -> s.isIn(UTags.SHADES));
+            boolean hasShades = TrinketsDelegate.getInstance(entity).getEquipped(entity, TrinketsDelegate.FACE).anyMatch(s -> s.isIn(UTags.Items.SHADES));
             if (!this.hasShades && hasShades && getObservedSpecies() == Race.BAT) {
                 UCriteria.WEAR_SHADES.trigger(entity);
             }
@@ -754,7 +754,7 @@ public class Pony extends Living<PlayerEntity> implements Copyable<Pony>, Update
         }
 
         if (getObservedSpecies() == Race.KIRIN
-                && (stack.isIn(UTags.COOLS_OFF_KIRINS) || PotionUtil.getPotion(stack) == Potions.WATER)) {
+                && (stack.isIn(UTags.Items.COOLS_OFF_KIRINS) || PotionUtil.getPotion(stack) == Potions.WATER)) {
             getMagicalReserves().getCharge().multiply(0.5F);
             getSpellSlot().get(SpellType.RAGE, false).ifPresent(RageAbilitySpell::setExtenguishing);
         }
diff --git a/src/main/java/com/minelittlepony/unicopia/item/CrystalHeartItem.java b/src/main/java/com/minelittlepony/unicopia/item/CrystalHeartItem.java
index d4f9607c..c334124f 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/CrystalHeartItem.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/CrystalHeartItem.java
@@ -188,7 +188,7 @@ public class CrystalHeartItem extends Item implements FloatingArtefactEntity.Art
 
         BlockPos tip = entity.getBlockPos().offset(direction);
         BlockState tipState = entity.getWorld().getBlockState(tip);
-        if (!tipState.isIn(UTags.CRYSTAL_HEART_ORNAMENT) || (!tipState.contains(EndRodBlock.FACING)|| tipState.get(EndRodBlock.FACING) != direction.getOpposite())) {
+        if (!tipState.isIn(UTags.Blocks.CRYSTAL_HEART_ORNAMENT) || (!tipState.contains(EndRodBlock.FACING)|| tipState.get(EndRodBlock.FACING) != direction.getOpposite())) {
             return false;
         }
 
@@ -207,6 +207,6 @@ public class CrystalHeartItem extends Item implements FloatingArtefactEntity.Art
     }
 
     private boolean isDiamond(BlockState state) {
-        return state.isIn(UTags.CRYSTAL_HEART_BASE);
+        return state.isIn(UTags.Blocks.CRYSTAL_HEART_BASE);
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/item/PineappleItem.java b/src/main/java/com/minelittlepony/unicopia/item/PineappleItem.java
index 7098e7a4..9ff3941f 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/PineappleItem.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/PineappleItem.java
@@ -29,7 +29,7 @@ public class PineappleItem extends Item {
                 });
 
                 if (world.random.nextInt(20) == 0) {
-                    RegistryUtils.pickRandom(world, UTags.PINEAPPLE_EFFECTS, e -> !user.hasStatusEffect(e)).ifPresent(effect -> {
+                    RegistryUtils.pickRandom(world, UTags.StatusEffects.PINEAPPLE_EFFECTS, e -> !user.hasStatusEffect(e)).ifPresent(effect -> {
                         user.addStatusEffect(new StatusEffectInstance(effect, 10, 1));
                     });
                 }
diff --git a/src/main/java/com/minelittlepony/unicopia/item/PolearmItem.java b/src/main/java/com/minelittlepony/unicopia/item/PolearmItem.java
index a7f430c4..5dc90908 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/PolearmItem.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/PolearmItem.java
@@ -41,7 +41,7 @@ public class PolearmItem extends SwordItem {
 
     @Override
     public boolean isSuitableFor(BlockState state) {
-        return state.isIn(UTags.POLEARM_MINEABLE);
+        return state.isIn(UTags.Blocks.POLEARM_MINEABLE);
     }
 
     @Override
diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
index be2973db..30ff595f 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
@@ -272,7 +272,7 @@ public interface UItems {
         FuelRegistry.INSTANCE.add(SPELLBOOK, 9000);
         FuelRegistry.INSTANCE.add(MEADOWBROOKS_STAFF, 800);
         FuelRegistry.INSTANCE.add(BURNED_TOAST, 1600);
-        FuelRegistry.INSTANCE.add(UTags.BASKETS, 1700);
+        FuelRegistry.INSTANCE.add(UTags.Items.BASKETS, 1700);
 
         CompostingChanceRegistry.INSTANCE.add(GREEN_APPLE, 0.65F);
         CompostingChanceRegistry.INSTANCE.add(SWEET_APPLE, 0.65F);
diff --git a/src/main/java/com/minelittlepony/unicopia/item/ZapAppleItem.java b/src/main/java/com/minelittlepony/unicopia/item/ZapAppleItem.java
index 63cfb026..f8fe1ee3 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/ZapAppleItem.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/ZapAppleItem.java
@@ -103,7 +103,7 @@ public class ZapAppleItem extends Item implements ChameleonItem, MultiItem {
     public List<ItemStack> getDefaultStacks() {
         return Unicopia.SIDE.getPony().map(Pony::asWorld)
                 .stream()
-                .flatMap(world -> RegistryUtils.valuesForTag(world, UConventionalTags.APPLES))
+                .flatMap(world -> RegistryUtils.valuesForTag(world, UConventionalTags.Items.APPLES))
                 .filter(a -> a != this).map(item -> {
             ItemStack stack = new ItemStack(this);
             stack.getOrCreateNbt().putString("appearance", Registries.ITEM.getId(item).toString());
diff --git a/src/main/java/com/minelittlepony/unicopia/item/enchantment/GemFindingEnchantment.java b/src/main/java/com/minelittlepony/unicopia/item/enchantment/GemFindingEnchantment.java
index 0010a097..d07383cc 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/enchantment/GemFindingEnchantment.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/enchantment/GemFindingEnchantment.java
@@ -21,7 +21,7 @@ public class GemFindingEnchantment extends SimpleEnchantment {
 
         BlockPos origin = user.getOrigin();
 
-        double volume = BlockPos.findClosest(origin, radius, radius, pos -> user.asWorld().getBlockState(pos).isIn(UTags.INTERESTING))
+        double volume = BlockPos.findClosest(origin, radius, radius, pos -> user.asWorld().getBlockState(pos).isIn(UTags.Blocks.INTERESTING))
             .map(p -> user.getOriginVector().squaredDistanceTo(p.getX(), p.getY(), p.getZ()))
             .map(find -> (1 - (Math.sqrt(find) / radius)))
             .orElse(-1D);
diff --git a/src/main/java/com/minelittlepony/unicopia/item/group/ItemGroupRegistry.java b/src/main/java/com/minelittlepony/unicopia/item/group/ItemGroupRegistry.java
index 3998f13b..c276d2c6 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/group/ItemGroupRegistry.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/group/ItemGroupRegistry.java
@@ -4,7 +4,6 @@ import java.util.*;
 import java.util.function.Supplier;
 import java.util.stream.Stream;
 
-import com.minelittlepony.unicopia.UTags;
 import com.minelittlepony.unicopia.Unicopia;
 
 import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
@@ -57,10 +56,9 @@ public interface ItemGroupRegistry {
         return key;
     }
 
-    static RegistryKey<ItemGroup> createGroupFromTag(String name, Supplier<ItemStack> icon) {
-        TagKey<Item> key = UTags.item("groups/" + name);
+    static RegistryKey<ItemGroup> createGroupFromTag(String name, TagKey<Item> tag, Supplier<ItemStack> icon) {
         return createDynamic(name, icon, () -> {
-            return Registries.ITEM.getEntryList(key)
+            return Registries.ITEM.getEntryList(tag)
                     .stream()
                     .flatMap(named -> named.stream())
                     .map(entry -> entry.value());
diff --git a/src/main/java/com/minelittlepony/unicopia/item/group/UItemGroups.java b/src/main/java/com/minelittlepony/unicopia/item/group/UItemGroups.java
index a1f0efae..a479fa90 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/group/UItemGroups.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/group/UItemGroups.java
@@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.item.group;
 
 import java.util.stream.Stream;
 
+import com.minelittlepony.unicopia.UTags;
 import com.minelittlepony.unicopia.item.ChameleonItem;
 import com.minelittlepony.unicopia.item.UItems;
 import net.minecraft.item.*;
@@ -12,13 +13,13 @@ public interface UItemGroups {
         return Stream.concat(Stream.of(Items.APPLE), ItemGroupRegistry.ITEMS.stream()
                 .filter(item -> !(item instanceof ChameleonItem) || ((ChameleonItem)item).isFullyDisguised()));
     });
-    RegistryKey<ItemGroup> FORAGING_ITEMS = ItemGroupRegistry.createGroupFromTag("foraging", Items.HAY_BLOCK::getDefaultStack);
-    RegistryKey<ItemGroup> EARTH_PONY_ITEMS = ItemGroupRegistry.createGroupFromTag("earth_pony", UItems.EARTH_BADGE::getDefaultStack);
-    RegistryKey<ItemGroup> UNICORN_ITEMS = ItemGroupRegistry.createGroupFromTag("unicorn", UItems.UNICORN_BADGE::getDefaultStack);
-    RegistryKey<ItemGroup> PEGASUS_ITEMS = ItemGroupRegistry.createGroupFromTag("pegasus", UItems.PEGASUS_BADGE::getDefaultStack);
-    RegistryKey<ItemGroup> BAT_PONY_ITEMS = ItemGroupRegistry.createGroupFromTag("bat_pony", UItems.BAT_BADGE::getDefaultStack);
-    RegistryKey<ItemGroup> SEA_PON_ITEMS = ItemGroupRegistry.createGroupFromTag("sea_pony", UItems.PEARL_NECKLACE::getDefaultStack);
-    RegistryKey<ItemGroup> CHANGELING_ITEMS = ItemGroupRegistry.createGroupFromTag("changeling", UItems.CHANGELING_BADGE::getDefaultStack);
+    RegistryKey<ItemGroup> FORAGING_ITEMS = ItemGroupRegistry.createGroupFromTag("foraging", UTags.Items.GROUP_FORAGING, Items.HAY_BLOCK::getDefaultStack);
+    RegistryKey<ItemGroup> EARTH_PONY_ITEMS = ItemGroupRegistry.createGroupFromTag("earth_pony", UTags.Items.GROUP_EARTH_PONY, UItems.EARTH_BADGE::getDefaultStack);
+    RegistryKey<ItemGroup> UNICORN_ITEMS = ItemGroupRegistry.createGroupFromTag("unicorn", UTags.Items.GROUP_UNICORN, UItems.UNICORN_BADGE::getDefaultStack);
+    RegistryKey<ItemGroup> PEGASUS_ITEMS = ItemGroupRegistry.createGroupFromTag("pegasus", UTags.Items.GROUP_PEGASUS, UItems.PEGASUS_BADGE::getDefaultStack);
+    RegistryKey<ItemGroup> BAT_PONY_ITEMS = ItemGroupRegistry.createGroupFromTag("bat_pony", UTags.Items.GROUP_BAT_PONY, UItems.BAT_BADGE::getDefaultStack);
+    RegistryKey<ItemGroup> SEA_PON_ITEMS = ItemGroupRegistry.createGroupFromTag("sea_pony", UTags.Items.GROUP_SEA_PONY, UItems.PEARL_NECKLACE::getDefaultStack);
+    RegistryKey<ItemGroup> CHANGELING_ITEMS = ItemGroupRegistry.createGroupFromTag("changeling", UTags.Items.GROUP_CHANGELING, UItems.CHANGELING_BADGE::getDefaultStack);
 
     static void bootstrap() {
         ItemGroupRegistry.bootstrap();
diff --git a/src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java b/src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java
index 531f670e..de74181e 100644
--- a/src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java
+++ b/src/main/java/com/minelittlepony/unicopia/projectile/PhysicsBodyProjectileEntity.java
@@ -145,7 +145,7 @@ public class PhysicsBodyProjectileEntity extends PersistentProjectileEntity impl
             return;
         } else {
             ItemStack stack = asItemStack();
-            if (stack.isIn(UTags.HORSE_SHOES)) {
+            if (stack.isIn(UTags.Items.HORSE_SHOES)) {
                 if (stack.damage(1 + random.nextInt(10), random, null)) {
                     playSound(USounds.Vanilla.ENTITY_ITEM_BREAK, 1, 1);
                 } else {
@@ -205,7 +205,7 @@ public class PhysicsBodyProjectileEntity extends PersistentProjectileEntity impl
             boolean ownerCanModify = !getWorld().isClient && Caster.of(getOwner()).filter(pony -> pony.canModifyAt(hit.getBlockPos())).isPresent();
 
             if (ownerCanModify && getWorld().getGameRules().getBoolean(GameRules.DO_MOB_GRIEFING)) {
-                if ((!isBouncy() || getWorld().random.nextInt(200) == 0) && state.isIn(UTags.FRAGILE)) {
+                if ((!isBouncy() || getWorld().random.nextInt(200) == 0) && state.isIn(UTags.Blocks.FRAGILE)) {
                     getWorld().breakBlock(hit.getBlockPos(), true);
                 }
             }
@@ -246,7 +246,7 @@ public class PhysicsBodyProjectileEntity extends PersistentProjectileEntity impl
         emitGameEvent(GameEvent.STEP);
 
         if (!isBouncy()) {
-            if (stack.isIn(UTags.HORSE_SHOES)) {
+            if (stack.isIn(UTags.Items.HORSE_SHOES)) {
                 if (stack.damage(1 + random.nextInt(10), random, null)) {
                     playSound(USounds.Vanilla.ENTITY_ITEM_BREAK, 1, 1);
                     discard();
@@ -262,7 +262,7 @@ public class PhysicsBodyProjectileEntity extends PersistentProjectileEntity impl
 
     @Override
     protected SoundEvent getHitSound() {
-        if (getStack().isIn(UTags.HORSE_SHOES)) {
+        if (getStack().isIn(UTags.Items.HORSE_SHOES)) {
             return USounds.Vanilla.ITEM_TRIDENT_HIT_GROUND;
         }
         return isBouncy() ? USounds.ITEM_MUFFIN_BOUNCE.value() : USounds.ITEM_ROCK_LAND;

From a81336ef88b0692615675a36728795a51250f120 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Sun, 31 Mar 2024 16:47:40 +0100
Subject: [PATCH 05/21] Switch support for farmer's delight to farmer's delight
 refabricated

---
 build.gradle                                       | 14 +++++++++++---
 gradle.properties                                  |  8 ++++----
 .../unicopia/server/world/UTreeGen.java            |  2 +-
 3 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/build.gradle b/build.gradle
index 69922b16..58465b32 100644
--- a/build.gradle
+++ b/build.gradle
@@ -44,13 +44,15 @@ reckon {
 repositories {
     mavenLocal()
     flatDir { dirs 'lib' }
-    maven { name 'entity-reach-attributes'; url 'https://maven.jamieswhiteshirt.com/libs-release' }
+    maven { name 'entity-reach-attributes'; url 'https://maven.jamieswhiteshirt.com/libs-release'; content { includeGroup "com.jamieswhiteshirt" } }
     maven { name 'trinkets'; url 'https://maven.ladysnake.org/releases' }
     maven { name 'mod-menu'; url 'https://maven.terraformersmc.com/' }
     maven { name 'minelp-snapshot'; url 'https://repo.minelittlepony-mod.com/maven/snapshot' }
     maven { name 'minelp-releases'; url 'https://repo.minelittlepony-mod.com/maven/release' }
     maven { name 'TerraformersMC'; url 'https://maven.terraformersmc.com/' }
     maven { name 'Nodium'; url 'https://maven.cafeteria.dev/releases/' }
+    maven { name 'Greenhouse Maven For Farmers delight'; url 'https://maven.greenhouseteam.dev/releases/' }
+    maven { name 'Porting Lib For Farmers delight'; url = 'https://mvn.devos.one/releases/' }
     maven { name 'Modrinth'; url 'https://api.modrinth.com/maven' }
     maven { name 'JitPack'; url 'https://jitpack.io'; content { includeGroup "com.github.Virtuoel" } }
 }
@@ -80,11 +82,13 @@ dependencies {
 
     modImplementation "com.terraformersmc.terraform-api:terraform-wood-api-v1:${project.terraformer_api_version}"
     include "com.terraformersmc.terraform-api:terraform-wood-api-v1:${project.terraformer_api_version}"
-    
+
     modImplementation "me.luligabi:NoIndium:${project.nodium_version}"
     include "me.luligabi:NoIndium:${project.nodium_version}"
 
-    modCompileOnly "maven.modrinth:farmers-delight-fabric:${project.farmers_delight_version}", { exclude group: "net.fabricmc.fabric-api" }
+    modImplementation "vectorwing:FarmersDelight-Refabricated:${project.farmers_delight_version}", {
+      exclude group: "net.fabricmc"
+    }
     if (project.use_pehkui == '1') {
       modCompileOnly "maven.modrinth:pehkui:${project.pehkui_version}", { exclude group: "net.fabricmc.fabric-api" }
       modCompileOnly "com.github.Virtuoel:KanosConfig:0.4.1", { exclude group: "net.fabricmc.fabric-api" }
@@ -103,6 +107,10 @@ dependencies {
     }
 }
 
+remapJar {
+    addNestedDependencies = true
+}
+
 processResources {
     inputs.property "version", project.version.toString()
 
diff --git a/gradle.properties b/gradle.properties
index f56cd269..e821a843 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -5,7 +5,7 @@ org.gradle.daemon=false
   # check these on https://fabricmc.net/develop
   minecraft_version=1.20.1
   yarn_mappings=1.20.1+build.10
-  loader_version=0.15.3
+  loader_version=0.15.7
   fabric_version=0.91.0+1.20.1
 
 # Mod Properties
@@ -22,8 +22,8 @@ org.gradle.daemon=false
 # Dependencies
   fabwork_version=1.2.0
   modmenu_version=7.0.0-beta.2
-  minelp_version=4.10.4+1.20.1
-  kirin_version=1.15.4+1.20
+  minelp_version=4.10.6+1.20.1
+  kirin_version=1.15.5-beta.1+1.20.1
   reach_attributes_version=2.3.4
   trinkets_version=3.7.1
   terraformer_api_version=7.0.0-beta.1
@@ -33,7 +33,7 @@ org.gradle.daemon=false
   use_pehkui=0
   use_sodium=1
 
-  farmers_delight_version=1.4.3
+  farmers_delight_version=1.20.1-2.0.9
   pehkui_version=3.7.8+1.14.4-1.20.1
   iris_version=1.6.17+1.20.1
   sodium_version=mc1.20.1-0.5.8
diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java b/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java
index 143db033..77f43a34 100644
--- a/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java
+++ b/src/main/java/com/minelittlepony/unicopia/server/world/UTreeGen.java
@@ -69,7 +69,7 @@ public interface UTreeGen {
             .sapling(Unicopia.id("palm_sapling")).sapling((generator, settings) -> {
                 return new SaplingBlock(generator, settings) {
                     @Override
-                    protected boolean canPlantOnTop(BlockState floor, BlockView world, BlockPos pos) {
+                    public boolean canPlantOnTop(BlockState floor, BlockView world, BlockPos pos) {
                         return floor.isIn(BlockTags.SAND);
                     }
                 };

From 7d63797d4dc5fdc7287b9b0e6a49c0f6b1a52024 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Sun, 31 Mar 2024 22:34:31 +0100
Subject: [PATCH 06/21] Jars can now hold fluids

---
 .../unicopia/block/ItemJarBlock.java          | 295 +++---------------
 .../unicopia/block/jar/EntityJarContents.java |  80 +++++
 .../block/jar/FakeFluidJarContents.java       |  79 +++++
 .../block/jar/FluidOnlyJarContents.java       |  55 ++++
 .../unicopia/block/jar/ItemsJarContents.java  | 208 ++++++++++++
 .../unicopia/client/render/RenderUtil.java    |  76 +----
 .../entity/ItemJarBlockEntityRenderer.java    |  99 ++++--
 .../client/render/model/CubeModel.java        |  77 +++++
 .../loot/UBlockLootTableProvider.java         |  29 ++
 .../unicopia/util/FluidHelper.java            |  69 ++++
 .../unicopia/util/PsyFluidHelper.java         |  51 +++
 11 files changed, 772 insertions(+), 346 deletions(-)
 create mode 100644 src/main/java/com/minelittlepony/unicopia/block/jar/EntityJarContents.java
 create mode 100644 src/main/java/com/minelittlepony/unicopia/block/jar/FakeFluidJarContents.java
 create mode 100644 src/main/java/com/minelittlepony/unicopia/block/jar/FluidOnlyJarContents.java
 create mode 100644 src/main/java/com/minelittlepony/unicopia/block/jar/ItemsJarContents.java
 create mode 100644 src/main/java/com/minelittlepony/unicopia/client/render/model/CubeModel.java
 create mode 100644 src/main/java/com/minelittlepony/unicopia/util/FluidHelper.java
 create mode 100644 src/main/java/com/minelittlepony/unicopia/util/PsyFluidHelper.java

diff --git a/src/main/java/com/minelittlepony/unicopia/block/ItemJarBlock.java b/src/main/java/com/minelittlepony/unicopia/block/ItemJarBlock.java
index 1c726165..ef84849d 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/ItemJarBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/ItemJarBlock.java
@@ -1,42 +1,34 @@
 package com.minelittlepony.unicopia.block;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
 import org.jetbrains.annotations.Nullable;
 
-import com.minelittlepony.unicopia.mixin.MixinEntityBucketItem;
-import com.minelittlepony.unicopia.util.NbtSerialisable;
+import com.minelittlepony.unicopia.block.jar.EntityJarContents;
+import com.minelittlepony.unicopia.block.jar.FluidOnlyJarContents;
+import com.minelittlepony.unicopia.block.jar.ItemsJarContents;
+import com.minelittlepony.unicopia.block.jar.FakeFluidJarContents;
 
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
 import net.minecraft.block.BlockEntityProvider;
 import net.minecraft.block.BlockRenderType;
 import net.minecraft.block.BlockState;
-import net.minecraft.block.Blocks;
 import net.minecraft.block.InventoryProvider;
 import net.minecraft.block.entity.BlockEntity;
-import net.minecraft.entity.Bucketable;
-import net.minecraft.entity.Entity;
-import net.minecraft.entity.EntityType;
 import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.inventory.SidedInventory;
 import net.minecraft.item.ItemStack;
-import net.minecraft.item.Items;
+import net.minecraft.item.ItemUsage;
 import net.minecraft.nbt.NbtCompound;
 import net.minecraft.nbt.NbtElement;
 import net.minecraft.network.listener.ClientPlayPacketListener;
 import net.minecraft.network.packet.Packet;
 import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
-import net.minecraft.registry.Registries;
 import net.minecraft.server.world.ServerWorld;
 import net.minecraft.util.ActionResult;
 import net.minecraft.util.Hand;
-import net.minecraft.util.Identifier;
 import net.minecraft.util.TypedActionResult;
 import net.minecraft.util.hit.BlockHitResult;
 import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.Direction;
 import net.minecraft.world.World;
 import net.minecraft.world.WorldAccess;
 
@@ -79,7 +71,7 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
     public int getComparatorOutput(BlockState state, World world, BlockPos pos) {
         return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR)
                 .map(TileData::getItems)
-                .map(data -> Math.min(16, data.getStacks().size()))
+                .map(data -> Math.min(16, data.stacks().size()))
                 .orElse(0);
     }
 
@@ -96,7 +88,7 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
         return new TileData(pos, state);
     }
 
-
+    @Nullable
     @Override
     public SidedInventory getInventory(BlockState state, WorldAccess world, BlockPos pos) {
         return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR).map(TileData::getItems).orElse(null);
@@ -130,6 +122,16 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
             return getContents() instanceof EntityJarContents c ? c : null;
         }
 
+        @Nullable
+        public FluidJarContents getFluid() {
+            return getContents() instanceof FluidJarContents c ? c : null;
+        }
+
+        @Nullable
+        public FakeFluidJarContents getFakeFluid() {
+            return getContents() instanceof FakeFluidJarContents c ? c : null;
+        }
+
         @Override
         public Packet<ClientPlayPacketListener> toUpdatePacket() {
             return BlockEntityUpdateS2CPacket.create(this);
@@ -151,11 +153,13 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
         @Override
         public void readNbt(NbtCompound nbt) {
             if (nbt.contains("items", NbtElement.COMPOUND_TYPE)) {
-                contents = new ItemsJarContents(this);
-                contents.fromNBT(nbt.getCompound("items"));
+                contents = new ItemsJarContents(this, nbt.getCompound("items"));
             } else if (nbt.contains("entity", NbtElement.COMPOUND_TYPE)) {
-                contents = new EntityJarContents(this);
-                contents.fromNBT(nbt.getCompound("entity"));
+                contents = new EntityJarContents(this, nbt.getCompound("entity"));
+            } else if (nbt.contains("fluid", NbtElement.COMPOUND_TYPE)) {
+                contents = new FluidOnlyJarContents(this, nbt.getCompound("fluid"));
+            } else if (nbt.contains("fakeFluid", NbtElement.COMPOUND_TYPE)) {
+                contents = new FakeFluidJarContents(this, nbt.getCompound("fakeFluid"));
             }
         }
 
@@ -163,249 +167,34 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
         protected void writeNbt(NbtCompound nbt) {
             var items = getItems();
             if (items != null) {
-                nbt.put("items", items.toNBT());
+                nbt.put("items", items.toNBT(new NbtCompound()));
             } else if (getEntity() != null) {
-                nbt.put("entity", getEntity().toNBT());
+                nbt.put("entity", getEntity().toNBT(new NbtCompound()));
+            } else if (getFluid() != null) {
+                nbt.put("fluid", getFluid().toNBT(new NbtCompound()));
+            } else if (getFakeFluid() != null) {
+                nbt.put("fakeFluid", getFakeFluid().toNBT(new NbtCompound()));
             }
         }
     }
 
-
-    public interface JarContents extends NbtSerialisable {
+    public interface JarContents {
         TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand);
 
         void onDestroyed();
+
+        NbtCompound toNBT(NbtCompound compound);
+
+        default void consumeAndSwap(PlayerEntity player, Hand hand, ItemStack output) {
+            player.setStackInHand(hand, ItemUsage.exchangeStack(player.getStackInHand(hand), player, output.copy()));
+        }
     }
 
-    public static class EntityJarContents implements JarContents {
-        @Nullable
-        private EntityType<?> entityType;
-        @Nullable
-        private Entity renderEntity;
+    public interface FluidJarContents extends JarContents {
+        FluidVariant fluid();
 
-        private final TileData tile;
-
-        public EntityJarContents(TileData tile) {
-            this(tile, null);
-        }
-
-        public EntityJarContents(TileData tile, EntityType<?> entityType) {
-            this.tile = tile;
-            this.entityType = entityType;
-        }
-
-        @Nullable
-        public Entity getOrCreateEntity() {
-            if (entityType == null && tile.getWorld() != null) {
-                return null;
-            }
-
-            if (renderEntity == null || renderEntity.getType() != entityType) {
-                renderEntity = entityType.create(tile.getWorld());
-            }
-            return renderEntity;
-        }
-
-        @Override
-        public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
-            ItemStack stack = player.getStackInHand(hand);
-            if (stack.isOf(Items.BUCKET)) {
-                if (getOrCreateEntity() instanceof Bucketable bucketable) {
-                    if (!player.isCreative()) {
-                        stack.decrement(1);
-                        if (stack.isEmpty()) {
-                            player.setStackInHand(hand, bucketable.getBucketItem());
-                        } else {
-                            player.giveItemStack(bucketable.getBucketItem());
-                        }
-                    }
-                    player.playSound(bucketable.getBucketFillSound(), 1, 1);
-                }
-                tile.markDirty();
-                return TypedActionResult.success(new ItemsJarContents(tile));
-            }
-            return TypedActionResult.pass(this);
-        }
-
-        @Override
-        public void onDestroyed() {
-            tile.getWorld().setBlockState(tile.getPos(), Blocks.WATER.getDefaultState());
-            Entity entity = getOrCreateEntity();
-            if (entity != null) {
-                entity.refreshPositionAfterTeleport(tile.getPos().toCenterPos());
-                tile.getWorld().spawnEntity(entity);
-            }
-        }
-
-        @Override
-        public void toNBT(NbtCompound compound) {
-            compound.putString("entity", EntityType.getId(entityType).toString());
-        }
-
-        @Override
-        public void fromNBT(NbtCompound compound) {
-            entityType = Registries.ENTITY_TYPE.getOrEmpty(Identifier.tryParse(compound.getString("entity"))).orElse(null);
-        }
-
-    }
-
-    public static class ItemsJarContents implements JarContents, SidedInventory {
-        private static final int[] SLOTS = IntStream.range(0, 16).toArray();
-
-        private final TileData tile;
-        private List<ItemStack> stacks = new ArrayList<>();
-
-        public ItemsJarContents(TileData tile) {
-            this.tile = tile;
-        }
-
-        @Override
-        public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
-            ItemStack handStack = player.getStackInHand(hand);
-
-            if (handStack.isEmpty()) {
-                if (stacks.isEmpty()) {
-                    return TypedActionResult.fail(this);
-                }
-                dropStack(tile.getWorld(), tile.getPos(), stacks.remove(0));
-                markDirty();
-                return TypedActionResult.success(this);
-            }
-
-            if (stacks.isEmpty()) {
-                if (handStack.getItem() instanceof MixinEntityBucketItem bucket) {
-                    if (!player.isCreative()) {
-                        handStack.decrement(1);
-                        if (handStack.isEmpty()) {
-                            player.setStackInHand(hand, Items.BUCKET.getDefaultStack());
-                        } else {
-                            player.giveItemStack(Items.BUCKET.getDefaultStack());
-                        }
-                    }
-
-                    player.playSound(bucket.getEmptyingSound(), 1, 1);
-                    markDirty();
-                    return TypedActionResult.success(new EntityJarContents(tile, bucket.getEntityType()));
-                }
-            }
-
-            if (stacks.size() >= size()) {
-                return TypedActionResult.fail(this);
-            }
-            stacks.add(player.isCreative() ? handStack.copyWithCount(1) : handStack.split(1));
-            markDirty();
-
-            return TypedActionResult.success(this);
-        }
-
-        @Override
-        public void onDestroyed() {
-            stacks.forEach(stack -> {
-                dropStack(tile.getWorld(), tile.getPos(), stack);
-            });
-        }
-
-        public List<ItemStack> getStacks() {
-            return stacks;
-        }
-
-        @Override
-        public int size() {
-            return 15;
-        }
-
-        @Override
-        public boolean isEmpty() {
-            return stacks.isEmpty();
-        }
-
-        @Override
-        public ItemStack getStack(int slot) {
-            return slot < 0 || slot >= stacks.size() ? ItemStack.EMPTY : stacks.get(slot);
-        }
-
-        @Override
-        public ItemStack removeStack(int slot, int amount) {
-            if (slot < 0 || slot >= stacks.size()) {
-                return ItemStack.EMPTY;
-            }
-
-            try {
-                ItemStack stack = stacks.get(slot);
-                ItemStack removed = stack.split(1);
-                if (stack.isEmpty()) {
-                    stacks.remove(slot);
-                }
-                return removed;
-            } finally {
-                markDirty();
-            }
-        }
-
-        @Override
-        public ItemStack removeStack(int slot) {
-            if (slot < 0 || slot >= stacks.size()) {
-                return ItemStack.EMPTY;
-            }
-
-            try {
-                return stacks.remove(slot);
-            } finally {
-                markDirty();
-            }
-        }
-
-        @Override
-        public void setStack(int slot, ItemStack stack) {
-            if (slot >= stacks.size()) {
-                stacks.add(stack);
-            } else {
-                stacks.set(slot, stack);
-            }
-            markDirty();
-        }
-
-        @Override
-        public boolean canPlayerUse(PlayerEntity player) {
-            return false;
-        }
-
-        @Override
-        public void clear() {
-            stacks.clear();
-            markDirty();
-        }
-
-        @Override
-        public int[] getAvailableSlots(Direction side) {
-            return SLOTS;
-        }
-
-        @Override
-        public boolean canInsert(int slot, ItemStack stack, Direction dir) {
-            return slot >= 0 && slot < size() && slot >= stacks.size();
-        }
-
-        @Override
-        public boolean canExtract(int slot, ItemStack stack, Direction dir) {
-            return slot >= 0 && slot < size() && slot < stacks.size();
-        }
-
-        @Override
-        public void toNBT(NbtCompound compound) {
-            compound.put("items", NbtSerialisable.ITEM_STACK.writeAll(stacks));
-        }
-
-        @Override
-        public void fromNBT(NbtCompound compound) {
-            stacks = NbtSerialisable.ITEM_STACK.readAll(compound.getList("items", NbtElement.COMPOUND_TYPE))
-                    .limit(size())
-                    .collect(Collectors.toList());
-        }
-
-        @Override
-        public void markDirty() {
-            tile.markDirty();
+        default long amount() {
+            return FluidConstants.BUCKET;
         }
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/block/jar/EntityJarContents.java b/src/main/java/com/minelittlepony/unicopia/block/jar/EntityJarContents.java
new file mode 100644
index 00000000..967807c1
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/block/jar/EntityJarContents.java
@@ -0,0 +1,80 @@
+package com.minelittlepony.unicopia.block.jar;
+
+import java.util.function.Supplier;
+
+import org.jetbrains.annotations.Nullable;
+
+import com.google.common.base.Suppliers;
+import com.minelittlepony.unicopia.block.ItemJarBlock.FluidJarContents;
+import com.minelittlepony.unicopia.block.ItemJarBlock.JarContents;
+import com.minelittlepony.unicopia.block.ItemJarBlock.TileData;
+
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
+import net.minecraft.block.Blocks;
+import net.minecraft.entity.Bucketable;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.fluid.Fluids;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.registry.Registries;
+import net.minecraft.util.Hand;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.TypedActionResult;
+
+public record EntityJarContents (
+        TileData tile,
+        @Nullable EntityType<?> entityType,
+        Supplier<@Nullable Entity> entity
+) implements FluidJarContents {
+    public EntityJarContents(TileData tile, NbtCompound compound) {
+        this(tile, Registries.ENTITY_TYPE.getOrEmpty(Identifier.tryParse(compound.getString("entity"))).orElse(null));
+    }
+
+    public EntityJarContents(TileData tile) {
+        this(tile, (EntityType<?>)null);
+    }
+
+    public EntityJarContents(TileData tile, EntityType<?> entityType) {
+        this(tile, entityType, Suppliers.memoize(() -> {
+            return entityType == null ? null : entityType.create(tile.getWorld());
+        }));
+    }
+
+    @Override
+    public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
+        ItemStack stack = player.getStackInHand(hand);
+        if (stack.isOf(Items.BUCKET)) {
+            if (entity().get() instanceof Bucketable bucketable) {
+                consumeAndSwap(player, hand, bucketable.getBucketItem());
+                player.playSound(bucketable.getBucketFillSound(), 1, 1);
+            }
+            tile.markDirty();
+            return TypedActionResult.success(new ItemsJarContents(tile));
+        }
+        return TypedActionResult.pass(this);
+    }
+
+    @Override
+    public void onDestroyed() {
+        tile.getWorld().setBlockState(tile.getPos(), Blocks.WATER.getDefaultState());
+        Entity entity = entity().get();
+        if (entity != null) {
+            entity.refreshPositionAfterTeleport(tile.getPos().toCenterPos());
+            tile.getWorld().spawnEntity(entity);
+        }
+    }
+
+    @Override
+    public NbtCompound toNBT(NbtCompound compound) {
+        compound.putString("entity", EntityType.getId(entityType).toString());
+        return compound;
+    }
+
+    @Override
+    public FluidVariant fluid() {
+        return FluidVariant.of(Fluids.WATER);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/minelittlepony/unicopia/block/jar/FakeFluidJarContents.java b/src/main/java/com/minelittlepony/unicopia/block/jar/FakeFluidJarContents.java
new file mode 100644
index 00000000..e49d648c
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/block/jar/FakeFluidJarContents.java
@@ -0,0 +1,79 @@
+package com.minelittlepony.unicopia.block.jar;
+
+import java.util.Optional;
+
+import com.minelittlepony.unicopia.USounds;
+import com.minelittlepony.unicopia.block.ItemJarBlock.JarContents;
+import com.minelittlepony.unicopia.block.ItemJarBlock.TileData;
+import com.minelittlepony.unicopia.util.FluidHelper;
+
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.fluid.Fluid;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.registry.Registries;
+import net.minecraft.util.Hand;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.TypedActionResult;
+
+public record FakeFluidJarContents (
+        TileData tile,
+        String fluid,
+        int color,
+        Item empty,
+        Item filled
+) implements JarContents {
+    public FakeFluidJarContents(TileData tile, NbtCompound compound) {
+        this(tile, compound.getString("fluid"), compound.getInt("color"),
+                Registries.ITEM.get(new Identifier(compound.getString("empty"))),
+                Registries.ITEM.get(new Identifier(compound.getString("filled"))));
+    }
+
+    @Override
+    public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
+        ItemStack stack = player.getStackInHand(hand);
+
+        tile.markDirty();
+        return getRealFluid().map(FluidVariant::of).<TypedActionResult<JarContents>>map(fluid -> {
+            long remainder = FluidHelper.deposit(stack, player, hand, fluid, FluidConstants.BUCKET);
+            fluid.getFluid().getBucketFillSound().ifPresent(sound -> player.playSound(sound, 1, 1));
+            if (remainder > 0) {
+                return TypedActionResult.success(new FluidOnlyJarContents(tile, remainder, fluid));
+            }
+            return TypedActionResult.success(new ItemsJarContents(tile));
+        }).orElseGet(() -> {
+            if (!stack.isOf(empty)) {
+                return TypedActionResult.pass(this);
+            }
+            consumeAndSwap(player, hand, filled.getDefaultStack());
+            player.playSound("powder_snow".equalsIgnoreCase(fluid) ? USounds.Vanilla.ITEM_BUCKET_FILL_POWDER_SNOW : USounds.Vanilla.ITEM_BUCKET_FILL, 1, 1);
+            return TypedActionResult.success(new ItemsJarContents(tile));
+        });
+    }
+
+    @Override
+    public void onDestroyed() {
+        getRealFluid().ifPresent(fluid -> {
+            tile.getWorld().setBlockState(tile.getPos(), FluidHelper.getFullFluidState(FluidVariant.of(fluid)).getBlockState());
+        });
+    }
+
+    @Override
+    public NbtCompound toNBT(NbtCompound compound) {
+        compound.putString("fluid", fluid);
+        compound.putInt("color", color);
+        compound.putString("empty", Registries.ITEM.getId(empty).toString());
+        compound.putString("filled", Registries.ITEM.getId(filled).toString());
+        return compound;
+    }
+
+    private Optional<Fluid> getRealFluid() {
+        return Registries.FLUID.getIds().stream()
+                .filter(id -> id.getPath().equalsIgnoreCase(fluid))
+                .findFirst()
+                .map(Registries.FLUID::get);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/minelittlepony/unicopia/block/jar/FluidOnlyJarContents.java b/src/main/java/com/minelittlepony/unicopia/block/jar/FluidOnlyJarContents.java
new file mode 100644
index 00000000..0cdcab4b
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/block/jar/FluidOnlyJarContents.java
@@ -0,0 +1,55 @@
+package com.minelittlepony.unicopia.block.jar;
+
+import com.minelittlepony.unicopia.block.ItemJarBlock.FluidJarContents;
+import com.minelittlepony.unicopia.block.ItemJarBlock.JarContents;
+import com.minelittlepony.unicopia.block.ItemJarBlock.TileData;
+import com.minelittlepony.unicopia.util.FluidHelper;
+
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.util.Hand;
+import net.minecraft.util.TypedActionResult;
+
+public record FluidOnlyJarContents (
+        TileData tile,
+        long amount,
+        FluidVariant fluid
+) implements FluidJarContents {
+
+    public FluidOnlyJarContents(TileData tile, NbtCompound compound) {
+        this(tile, compound.getLong("amount"), FluidVariant.fromNbt(compound.getCompound("fluid")));
+    }
+
+    @Override
+    public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
+        ItemStack stack = player.getStackInHand(hand);
+        if (stack.isOf(Items.BUCKET)) {
+            long remainder = FluidHelper.deposit(stack, player, hand, fluid, amount);
+            tile.markDirty();
+            fluid.getFluid().getBucketFillSound().ifPresent(sound -> player.playSound(sound, 1, 1));
+            if (remainder > 0) {
+                return TypedActionResult.success(new FluidOnlyJarContents(tile, remainder, fluid));
+            }
+            return TypedActionResult.success(new ItemsJarContents(tile));
+        }
+        return TypedActionResult.pass(this);
+    }
+
+    @Override
+    public void onDestroyed() {
+        if (amount >= FluidConstants.BUCKET) {
+            tile.getWorld().setBlockState(tile.getPos(), FluidHelper.getFullFluidState(fluid).getBlockState());
+        }
+    }
+
+    @Override
+    public NbtCompound toNBT(NbtCompound compound) {
+        compound.put("fluid", fluid.toNbt());
+        compound.putLong("amount", amount);
+        return compound;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/minelittlepony/unicopia/block/jar/ItemsJarContents.java b/src/main/java/com/minelittlepony/unicopia/block/jar/ItemsJarContents.java
new file mode 100644
index 00000000..a406c3dd
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/block/jar/ItemsJarContents.java
@@ -0,0 +1,208 @@
+package com.minelittlepony.unicopia.block.jar;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import com.minelittlepony.unicopia.USounds;
+import com.minelittlepony.unicopia.block.ItemJarBlock.JarContents;
+import com.minelittlepony.unicopia.block.ItemJarBlock.TileData;
+import com.minelittlepony.unicopia.item.UItems;
+import com.minelittlepony.unicopia.mixin.MixinEntityBucketItem;
+import com.minelittlepony.unicopia.util.FluidHelper;
+import com.minelittlepony.unicopia.util.NbtSerialisable;
+
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.inventory.SidedInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.nbt.NbtElement;
+import net.minecraft.util.Hand;
+import net.minecraft.util.Pair;
+import net.minecraft.util.TypedActionResult;
+import net.minecraft.util.math.Direction;
+
+public record ItemsJarContents (
+        TileData tile,
+        List<ItemStack> stacks
+    ) implements JarContents, SidedInventory {
+    private static final int[] SLOTS = IntStream.range(0, 16).toArray();
+
+    public ItemsJarContents(TileData tile) {
+        this(tile, new ArrayList<>());
+    }
+
+    public ItemsJarContents(TileData tile, NbtCompound compound) {
+        this(tile, NbtSerialisable.ITEM_STACK.readAll(compound.getList("items", NbtElement.COMPOUND_TYPE))
+                .limit(15)
+                .collect(Collectors.toList()));
+    }
+
+    @Override
+    public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
+        ItemStack handStack = player.getStackInHand(hand);
+
+        if (handStack.isEmpty()) {
+            if (stacks.isEmpty()) {
+                return TypedActionResult.fail(this);
+            }
+            Block.dropStack(tile.getWorld(), tile.getPos(), stacks.remove(0));
+            markDirty();
+            return TypedActionResult.success(this);
+        }
+
+        if (stacks.isEmpty()) {
+            if (handStack.getItem() instanceof MixinEntityBucketItem bucket) {
+                consumeAndSwap(player, hand, Items.BUCKET.getDefaultStack());
+                player.playSound(bucket.getEmptyingSound(), 1, 1);
+                markDirty();
+                return TypedActionResult.success(new EntityJarContents(tile, bucket.getEntityType()));
+            }
+
+            Pair<Long, FluidVariant> fluid = FluidHelper.extract(handStack, player, hand).orElse(null);
+            if (fluid != null) {
+                fluid.getRight().getFluid().getBucketFillSound().ifPresent(sound -> player.playSound(sound, 1, 1));
+                markDirty();
+                return TypedActionResult.success(new FluidOnlyJarContents(tile, fluid.getLeft(), fluid.getRight()));
+            }
+
+            if (handStack.isOf(Items.MILK_BUCKET)) {
+                consumeAndSwap(player, hand, handStack.getRecipeRemainder());
+                player.playSound(USounds.Vanilla.ITEM_BUCKET_EMPTY, 1, 1);
+                markDirty();
+                return TypedActionResult.success(new FakeFluidJarContents(tile, "milk", 0xFFFFFFFF, Items.BUCKET, Items.MILK_BUCKET));
+            }
+
+            if (handStack.isOf(Items.POWDER_SNOW_BUCKET)) {
+                consumeAndSwap(player, hand, Items.BUCKET.getDefaultStack());
+                player.playSound(USounds.Vanilla.ITEM_BUCKET_EMPTY_POWDER_SNOW, 1, 1);
+                markDirty();
+                return TypedActionResult.success(new FakeFluidJarContents(tile, "powder_snow", 0xFFFFFFFF, Items.BUCKET, Items.POWDER_SNOW_BUCKET));
+            }
+
+            if (handStack.isOf(UItems.LOVE_BUCKET)) {
+                consumeAndSwap(player, hand, handStack.getRecipeRemainder());
+                player.playSound(USounds.Vanilla.ITEM_BUCKET_EMPTY, 1, 1);
+                markDirty();
+                return TypedActionResult.success(new FakeFluidJarContents(tile, "love", 0xFF3030, Items.BUCKET, UItems.LOVE_BUCKET));
+            }
+
+            if (handStack.isOf(UItems.JUICE)) {
+                consumeAndSwap(player, hand, handStack.getRecipeRemainder());
+                player.playSound(USounds.Vanilla.ITEM_BUCKET_EMPTY, 1, 1);
+                markDirty();
+                return TypedActionResult.success(new FakeFluidJarContents(tile, "apple_juice", 0x30FF30, Items.GLASS_BOTTLE, UItems.JUICE));
+            }
+        }
+
+        if (stacks.size() >= size()) {
+            return TypedActionResult.fail(this);
+        }
+        stacks.add(player.isCreative() ? handStack.copyWithCount(1) : handStack.split(1));
+        markDirty();
+
+        return TypedActionResult.success(this);
+    }
+
+    @Override
+    public void onDestroyed() {
+        stacks.forEach(stack -> Block.dropStack(tile.getWorld(), tile.getPos(), stack));
+    }
+
+    @Override
+    public int size() {
+        return 15;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return stacks.isEmpty();
+    }
+
+    @Override
+    public ItemStack getStack(int slot) {
+        return slot < 0 || slot >= stacks.size() ? ItemStack.EMPTY : stacks.get(slot);
+    }
+
+    @Override
+    public ItemStack removeStack(int slot, int amount) {
+        if (slot < 0 || slot >= stacks.size()) {
+            return ItemStack.EMPTY;
+        }
+
+        try {
+            ItemStack stack = stacks.get(slot);
+            ItemStack removed = stack.split(1);
+            if (stack.isEmpty()) {
+                stacks.remove(slot);
+            }
+            return removed;
+        } finally {
+            markDirty();
+        }
+    }
+
+    @Override
+    public ItemStack removeStack(int slot) {
+        if (slot < 0 || slot >= stacks.size()) {
+            return ItemStack.EMPTY;
+        }
+
+        try {
+            return stacks.remove(slot);
+        } finally {
+            markDirty();
+        }
+    }
+
+    @Override
+    public void setStack(int slot, ItemStack stack) {
+        if (slot >= stacks.size()) {
+            stacks.add(stack);
+        } else {
+            stacks.set(slot, stack);
+        }
+        markDirty();
+    }
+
+    @Override
+    public boolean canPlayerUse(PlayerEntity player) {
+        return false;
+    }
+
+    @Override
+    public void clear() {
+        stacks.clear();
+        markDirty();
+    }
+
+    @Override
+    public int[] getAvailableSlots(Direction side) {
+        return SLOTS;
+    }
+
+    @Override
+    public boolean canInsert(int slot, ItemStack stack, Direction dir) {
+        return slot >= 0 && slot < size() && slot >= stacks.size();
+    }
+
+    @Override
+    public boolean canExtract(int slot, ItemStack stack, Direction dir) {
+        return slot >= 0 && slot < size() && slot < stacks.size();
+    }
+
+    @Override
+    public NbtCompound toNBT(NbtCompound compound) {
+        compound.put("items", NbtSerialisable.ITEM_STACK.writeAll(stacks));
+        return compound;
+    }
+
+    @Override
+    public void markDirty() {
+        tile.markDirty();
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java
index c2795b11..9b92256d 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/RenderUtil.java
@@ -5,20 +5,15 @@ import org.joml.Vector3f;
 import org.joml.Vector4f;
 
 import net.minecraft.client.render.BufferBuilder;
-import net.minecraft.client.render.RenderLayer;
 import net.minecraft.client.render.Tessellator;
-import net.minecraft.client.render.VertexConsumer;
-import net.minecraft.client.render.VertexConsumerProvider;
 import net.minecraft.client.render.VertexFormat;
 import net.minecraft.client.render.VertexFormats;
-import net.minecraft.client.texture.Sprite;
 import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.util.math.ColorHelper;
-import net.minecraft.util.math.Direction;
 
 public class RenderUtil {
     public static final Vector4f TEMP_VECTOR = new Vector4f();
-    private static final Vector4f TEMP_UV_VECTOR = new Vector4f();
+    public static final Vector4f TEMP_UV_VECTOR = new Vector4f();
+    public static final Vector3f TEMP_NORMAL_VECTOR = new Vector3f();
     public static final Vertex[] UNIT_FACE = new Vertex[] {
             new Vertex(0, 0, 0, 1, 1),
             new Vertex(0, 1, 0, 1, 0),
@@ -31,72 +26,9 @@ public class RenderUtil {
             new Vertex(1, 0, 0, 1, 1),
             new Vertex(0, 0, 0, 0, 1)
     };
-    private static final Vertex[][] CUBE_VERTICES = {
-            new Vertex[] { // down
-                    new Vertex(0, 0, 0, 0, 0),
-                    new Vertex(1, 0, 0, 1, 0),
-                    new Vertex(1, 0, 1, 1, 1),
-                    new Vertex(0, 0, 1, 0, 1)
-            },
-            new Vertex[] { //up
-                    new Vertex(0, 1, 0, 0, 0),
-                    new Vertex(0, 1, 1, 0, 1),
-                    new Vertex(1, 1, 1, 1, 1),
-                    new Vertex(1, 1, 0, 1, 0)
-            },
-            new Vertex[] { //north
-                    new Vertex(0, 0, 0, 0, 0),
-                    new Vertex(0, 1, 0, 0, 1),
-                    new Vertex(1, 1, 0, 1, 1),
-                    new Vertex(1, 0, 0, 1, 0)
-            },
-            new Vertex[] { //south
-                    new Vertex(0, 0, 1, 0, 0),
-                    new Vertex(1, 0, 1, 1, 0),
-                    new Vertex(1, 1, 1, 1, 1),
-                    new Vertex(0, 1, 1, 0, 1)
-            },
-            new Vertex[] { //west
-                    new Vertex(0, 0, 0, 0, 0),
-                    new Vertex(0, 0, 1, 1, 0),
-                    new Vertex(0, 1, 1, 1, 1),
-                    new Vertex(0, 1, 0, 0, 1)
-            },
-            new Vertex[] { //east
-                    new Vertex(1, 0, 0, 0, 0),
-                    new Vertex(1, 1, 0, 1, 0),
-                    new Vertex(1, 1, 1, 1, 1),
-                    new Vertex(1, 0, 1, 0, 1)
-            }
-    };
 
-    public static void renderSpriteCubeFaces(MatrixStack matrices, VertexConsumerProvider provider, Sprite sprite,
-            float width, float height, float length,
-            int color, int light, int overlay,
-            Direction... directions) {
-        float r = ColorHelper.Abgr.getRed(color),
-            g = ColorHelper.Abgr.getGreen(color),
-            b = ColorHelper.Abgr.getBlue(color),
-            a = ColorHelper.Abgr.getAlpha(color);
-        float u0 = sprite.getMinU(), uDelta = sprite.getMaxU() - u0;
-        float v0 = sprite.getMinV(), vDelta = sprite.getMaxV() - v0;
-        RenderLayer layer = RenderLayer.getEntitySolid(sprite.getAtlasId());
-        VertexConsumer buffer = provider.getBuffer(layer);
-        Matrix4f position = matrices.peek().getPositionMatrix();
-        for (Direction direction : directions) {
-            for (Vertex vertex : CUBE_VERTICES[direction.ordinal()]) {
-                Vector4f pos = position.transform(TEMP_VECTOR.set(vertex.position(), 1).mul(width, height, length, 1));
-                buffer.vertex(
-                        pos.x, pos.y, pos.z,
-                        r, g, b, a,
-                        u0 + vertex.texture().x * uDelta,
-                        v0 + vertex.texture().y * vDelta,
-                        overlay, light,
-                        direction.getOffsetX(), direction.getOffsetY(), direction.getOffsetZ()
-                );
-            }
-        }
-    }
+
+
 
     public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) {
         renderFace(matrices, te, buffer, r, g, b, a, light, 1, 1);
diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/ItemJarBlockEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/ItemJarBlockEntityRenderer.java
index e0b4883c..472a8335 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/ItemJarBlockEntityRenderer.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/ItemJarBlockEntityRenderer.java
@@ -1,14 +1,24 @@
 package com.minelittlepony.unicopia.client.render.entity;
 
+import java.util.Arrays;
+
+import org.jetbrains.annotations.Nullable;
+
+import com.minelittlepony.common.util.Color;
 import com.minelittlepony.unicopia.block.ItemJarBlock;
-import com.minelittlepony.unicopia.block.ItemJarBlock.EntityJarContents;
-import com.minelittlepony.unicopia.block.ItemJarBlock.ItemsJarContents;
-import com.minelittlepony.unicopia.client.render.RenderUtil;
+import com.minelittlepony.unicopia.block.ItemJarBlock.FluidJarContents;
+import com.minelittlepony.unicopia.block.jar.ItemsJarContents;
+import com.minelittlepony.unicopia.block.jar.FakeFluidJarContents;
+import com.minelittlepony.unicopia.block.jar.EntityJarContents;
+import com.minelittlepony.unicopia.client.render.model.CubeModel;
+import com.minelittlepony.unicopia.util.FluidHelper;
 import com.minelittlepony.unicopia.util.PosHelper;
 
-import net.minecraft.block.Blocks;
+import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
+import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
 import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.color.world.BiomeColors;
+import net.minecraft.client.render.RenderLayer;
 import net.minecraft.client.render.VertexConsumerProvider;
 import net.minecraft.client.render.block.entity.BlockEntityRenderer;
 import net.minecraft.client.render.block.entity.BlockEntityRendererFactory;
@@ -20,8 +30,12 @@ import net.minecraft.client.util.math.MatrixStack;
 import net.minecraft.command.argument.EntityAnchorArgumentType.EntityAnchor;
 import net.minecraft.entity.Entity;
 import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.fluid.Fluid;
+import net.minecraft.fluid.FluidState;
+import net.minecraft.fluid.Fluids;
 import net.minecraft.item.ItemStack;
 import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Direction;
 import net.minecraft.util.math.MathHelper;
 import net.minecraft.util.math.RotationAxis;
 import net.minecraft.util.math.Vec3d;
@@ -29,16 +43,13 @@ import net.minecraft.util.math.random.Random;
 import net.minecraft.world.World;
 
 public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBlock.TileData> {
-
+    private static final Direction[] GLASS_SIDES = Arrays.stream(PosHelper.ALL).filter(i -> i != Direction.UP).toArray(Direction[]::new);
     private final ItemRenderer itemRenderer;
     private final EntityRenderDispatcher dispatcher;
 
-    private final Sprite waterSprite;
-
     public ItemJarBlockEntityRenderer(BlockEntityRendererFactory.Context ctx) {
         itemRenderer = ctx.getItemRenderer();
         dispatcher = ctx.getEntityRenderDispatcher();
-        waterSprite = MinecraftClient.getInstance().getBakedModelManager().getBlockModels().getModel(Blocks.WATER.getDefaultState()).getParticleSprite();
     }
 
     @Override
@@ -53,6 +64,16 @@ public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBl
         if (entity != null) {
             renderEntity(data, entity, tickDelta, matrices, vertices, light, overlay);
         }
+
+        FluidJarContents fluid = data.getFluid();
+        if (fluid != null) {
+            renderFluid(data, fluid, tickDelta, matrices, vertices, light, overlay);
+        }
+
+        FakeFluidJarContents milk = data.getFakeFluid();
+        if (milk != null) {
+            renderFluid(data, Fluids.WATER.getDefaultState(), milk.color(), FluidConstants.BUCKET, tickDelta, matrices, vertices, light, overlay);
+        }
     }
 
     private void renderItemStacks(ItemJarBlock.TileData data, ItemsJarContents items, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
@@ -66,7 +87,7 @@ public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBl
         Random rng = Random.create(data.getPos().asLong());
 
         float y = 0;
-        for (ItemStack stack : items.getStacks()) {
+        for (ItemStack stack : items.stacks()) {
             matrices.push();
 
             matrices.translate((rng.nextFloat() - 0.5F) * 0.5F, (rng.nextFloat() - 0.5F) * 0.8F, -0.05 + y);
@@ -80,10 +101,9 @@ public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBl
     }
 
     private void renderEntity(ItemJarBlock.TileData data, EntityJarContents entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
-        Entity e = entity.getOrCreateEntity();
+        Entity e = entity.entity().get();
         if (e != null) {
 
-
             PlayerEntity player = MinecraftClient.getInstance().player;
             int age = player == null ? 0 : player.age;
 
@@ -109,20 +129,57 @@ public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBl
             dispatcher.render(e, 0, 0, 0, yaw * MathHelper.RADIANS_PER_DEGREE, tickDelta, matrices, vertices, light);
             matrices.pop();
         }
-        renderFluid(data.getWorld(), data.getPos(), tickDelta, matrices, vertices, light, overlay);
     }
 
-    private void renderFluid(World world, BlockPos pos, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
+    private void renderFluid(ItemJarBlock.TileData data, FluidJarContents fluid, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
+        FluidState state = FluidHelper.getFullFluidState(fluid.fluid());
+        int color = getFluidColor(data.getWorld(), data.getPos(), state);
+        renderFluid(data, state, color, fluid.amount(), tickDelta, matrices, vertices, light, overlay);
+    }
+
+    private void renderFluid(ItemJarBlock.TileData data, FluidState state, int color, long amount, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
+        Sprite[] sprite = getFluidSprite(data.getWorld(), data.getPos(), state);
         matrices.push();
-        matrices.translate(0.3F, 0, 0.3F);
-        RenderUtil.renderSpriteCubeFaces(
+        Sprite topSprite = sprite[0];
+        float height = 0.6F * (amount / (float)FluidConstants.BUCKET);
+        boolean opaque = Color.a(color) >= 1;
+        CubeModel.render(
                 matrices,
-                vertices,
-                waterSprite,
-                0.4F, 0.4F, 0.4F,
-                BiomeColors.getWaterColor(world, pos),
-                light, overlay, PosHelper.ALL
+                vertices.getBuffer(opaque ? RenderLayer.getEntitySolid(topSprite.getAtlasId()) : RenderLayer.getEntityTranslucent(topSprite.getAtlasId())),
+                topSprite.getMinU(), topSprite.getMinV(),
+                topSprite.getMaxU(), topSprite.getMaxV(),
+                0.28F, 0.01F, 0.28F,
+                0.73F, 0.01F + height, 0.73F,
+                color,
+                light, overlay, Direction.UP
+        );
+        Sprite sideSprite = sprite[sprite.length - 1];
+        CubeModel.render(
+                matrices,
+                vertices.getBuffer(opaque ? RenderLayer.getEntitySolid(sideSprite.getAtlasId()) : RenderLayer.getEntityTranslucent(sideSprite.getAtlasId())),
+                sideSprite.getMinU(), sideSprite.getMinV(),
+                sideSprite.getMaxU(), sideSprite.getMaxV(),
+                0.28F, 0.01F, 0.28F,
+                0.73F, 0.01F + height, 0.73F,
+                color,
+                light, overlay, GLASS_SIDES
         );
         matrices.pop();
     }
+
+    private int getFluidColor(World world, BlockPos pos, FluidState state) {
+        return getFluidHandler(state.getFluid()).getFluidColor(world, pos, state);
+    }
+
+    private Sprite[] getFluidSprite(@Nullable World world, BlockPos pos, FluidState state) {
+        return getFluidHandler(state.getFluid()).getFluidSprites(world, pos, state);
+    }
+
+    private FluidRenderHandler getFluidHandler(Fluid fluid) {
+        FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(fluid);
+        if (handler == null) {
+            return FluidRenderHandlerRegistry.INSTANCE.get(Fluids.WATER);
+        }
+        return handler;
+    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/model/CubeModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/model/CubeModel.java
new file mode 100644
index 00000000..6aaadb33
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/model/CubeModel.java
@@ -0,0 +1,77 @@
+package com.minelittlepony.unicopia.client.render.model;
+
+import org.joml.Matrix3f;
+import org.joml.Matrix4f;
+import org.joml.Vector2f;
+import org.joml.Vector3f;
+import org.joml.Vector4f;
+
+import com.minelittlepony.common.util.Color;
+import com.minelittlepony.unicopia.client.render.RenderUtil;
+import com.minelittlepony.unicopia.client.render.RenderUtil.Vertex;
+
+import net.minecraft.client.render.VertexConsumer;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.util.math.Direction;
+
+public class CubeModel {
+    private static final Vector2f TEMP_UV_VECTOR = new Vector2f();
+    private static final Vertex[][] CUBE_VERTICES = {
+            new Vertex[] {
+                    new Vertex(0, 0, 0, 0, 0),
+                    new Vertex(1, 0, 0, 1, 0),
+                    new Vertex(1, 0, 1, 1, 1),
+                    new Vertex(0, 0, 1, 0, 1)
+            },
+            new Vertex[] {
+                    new Vertex(0, 1, 0, 0, 0),
+                    new Vertex(0, 1, 1, 0, 1),
+                    new Vertex(1, 1, 1, 1, 1),
+                    new Vertex(1, 1, 0, 1, 0)
+            },
+            new Vertex[] {
+                    new Vertex(0, 0, 0, 0, 0),
+                    new Vertex(0, 1, 0, 0, 1),
+                    new Vertex(1, 1, 0, 1, 1),
+                    new Vertex(1, 0, 0, 1, 0)
+            },
+            new Vertex[] {
+                    new Vertex(0, 0, 1, 0, 0),
+                    new Vertex(1, 0, 1, 1, 0),
+                    new Vertex(1, 1, 1, 1, 1),
+                    new Vertex(0, 1, 1, 0, 1)
+            },
+            new Vertex[] {
+                    new Vertex(0, 0, 0, 0, 0),
+                    new Vertex(0, 0, 1, 1, 0),
+                    new Vertex(0, 1, 1, 1, 1),
+                    new Vertex(0, 1, 0, 0, 1)
+            },
+            new Vertex[] {
+                    new Vertex(1, 0, 0, 0, 0),
+                    new Vertex(1, 1, 0, 1, 0),
+                    new Vertex(1, 1, 1, 1, 1),
+                    new Vertex(1, 0, 1, 0, 1)
+            }
+    };
+
+    public static void render(MatrixStack matrices, VertexConsumer buffer,
+            float u0, float v0, float u1, float v1,
+            float x0, float y0, float z0, float x1, float y1, float z1,
+            int color, int light, int overlay,
+            Direction... directions) {
+        float r = Color.r(color), g = Color.g(color), b = Color.b(color);
+        float du = u1 - u0, dv = v1 - v0;
+        float dx = x1 - x0, dy = y1 - y0, dz = z1 - z0;
+        Matrix4f position = matrices.peek().getPositionMatrix();
+        Matrix3f normal = matrices.peek().getNormalMatrix();
+        for (Direction direction : directions) {
+            for (Vertex vertex : CUBE_VERTICES[direction.ordinal()]) {
+                Vector4f pos = position.transform(RenderUtil.TEMP_VECTOR.set(vertex.position(), 1).mul(dx, dy, dz, 1).add(x0, y0, z0, 0));
+                Vector2f tex = TEMP_UV_VECTOR.set(vertex.texture().x, vertex.texture().y).mul(du, dv).add(u0, v0);
+                Vector3f norm = normal.transform(RenderUtil.TEMP_NORMAL_VECTOR.set(direction.getOffsetX(), direction.getOffsetY(), direction.getOffsetZ()));
+                buffer.vertex(pos.x, pos.y, pos.z, r, g, b, 1, tex.x, tex.y, overlay, light, norm.x, norm.y, norm.z);
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockLootTableProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockLootTableProvider.java
index 77b0d94e..d7ed00b1 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockLootTableProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockLootTableProvider.java
@@ -1,6 +1,8 @@
 package com.minelittlepony.unicopia.datagen.providers.loot;
 
 import java.util.List;
+
+import com.minelittlepony.unicopia.block.PieBlock;
 import com.minelittlepony.unicopia.block.UBlocks;
 import com.minelittlepony.unicopia.datagen.providers.UModelProvider;
 import com.minelittlepony.unicopia.item.UItems;
@@ -14,14 +16,21 @@ import net.minecraft.block.Block;
 import net.minecraft.block.Blocks;
 import net.minecraft.block.enums.BedPart;
 import net.minecraft.enchantment.Enchantments;
+import net.minecraft.item.Item;
 import net.minecraft.item.Items;
 import net.minecraft.loot.LootPool;
 import net.minecraft.loot.LootTable;
+import net.minecraft.loot.condition.BlockStatePropertyLootCondition;
+import net.minecraft.loot.condition.LootConditionConsumingBuilder;
 import net.minecraft.loot.condition.TableBonusLootCondition;
 import net.minecraft.loot.entry.ItemEntry;
 import net.minecraft.loot.function.SetCountLootFunction;
 import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
 import net.minecraft.loot.provider.number.UniformLootNumberProvider;
+import net.minecraft.predicate.StatePredicate;
+import net.minecraft.state.property.BooleanProperty;
+import net.minecraft.state.property.Property;
+import net.minecraft.util.StringIdentifiable;
 
 public class UBlockLootTableProvider extends FabricBlockLootTableProvider {
 
@@ -116,6 +125,7 @@ public class UBlockLootTableProvider extends FabricBlockLootTableProvider {
         addDrop(UBlocks.CLOUD_PILLAR, drops(UBlocks.CLOUD_PILLAR, UBlocks.CLOUD, ConstantLootNumberProvider.create(2)));
 
         addDrop(UBlocks.FROSTED_OBSIDIAN, Blocks.OBSIDIAN);
+        addDrop(UBlocks.APPLE_PIE, pieDrops(UBlocks.APPLE_PIE, UItems.APPLE_PIE, UItems.APPLE_PIE_HOOF));
     }
 
     private LootTable.Builder fruitLeavesDrops(Block leaves) {
@@ -136,4 +146,23 @@ public class UBlockLootTableProvider extends FabricBlockLootTableProvider {
             );
     }
 
+    private LootTable.Builder pieDrops(Block block, Item drop, Item stomped) {
+        return LootTable.builder().pool(LootPool.builder()
+            .rolls(ConstantLootNumberProvider.create(1)).conditionally(WITH_SILK_TOUCH)
+            .with(addStateCondition(block, PieBlock.STOMPED, false, applyExplosionDecay(block, ItemEntry.builder(drop))))
+            .with(addStateCondition(block, PieBlock.STOMPED, true, applyExplosionDecay(block, ItemEntry.builder(stomped))))
+        );
+    }
+
+    public static <T extends LootConditionConsumingBuilder<T>, P extends Comparable<P> & StringIdentifiable> T addStateCondition(Block block,
+            Property<P> property, P value,
+            LootConditionConsumingBuilder<T> builder) {
+        return builder.conditionally(BlockStatePropertyLootCondition.builder(block).properties(StatePredicate.Builder.create().exactMatch(property, value)));
+    }
+
+    public static <T extends LootConditionConsumingBuilder<T>> T addStateCondition(Block block,
+            BooleanProperty property, boolean value,
+            LootConditionConsumingBuilder<T> builder) {
+        return builder.conditionally(BlockStatePropertyLootCondition.builder(block).properties(StatePredicate.Builder.create().exactMatch(property, value)));
+    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/util/FluidHelper.java b/src/main/java/com/minelittlepony/unicopia/util/FluidHelper.java
new file mode 100644
index 00000000..48508815
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/util/FluidHelper.java
@@ -0,0 +1,69 @@
+package com.minelittlepony.unicopia.util;
+
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.function.Function;
+
+import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext;
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage;
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
+import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
+import net.fabricmc.fabric.api.transfer.v1.storage.StorageView;
+import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.fluid.FluidState;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.Hand;
+import net.minecraft.util.Pair;
+
+/**
+ * Here be dragons
+ */
+public interface FluidHelper {
+    static FluidState getFullFluidState(FluidVariant variant) {
+        return PsyFluidHelper.getFullFluidState(variant);
+    }
+
+    static Optional<Pair<Long, FluidVariant>> extract(ItemStack stack, PlayerEntity player, Hand hand) {
+        return getAsContainer(stack, ContainerItemContext.forPlayerInteraction(player, hand))
+                .filter(c -> !c.isResourceBlank())
+                .map(container -> applyTransaction(t -> {
+                    FluidVariant type = container.getResource();
+                    long amountExtracted = container.extract(type, FluidConstants.BUCKET, t);
+                    if (amountExtracted > 0) {
+                        return new Pair<>(amountExtracted, type);
+                    }
+                    return null;
+                }));
+    }
+
+    static long deposit(ItemStack stack, PlayerEntity player, Hand hand, FluidVariant variant, long amount) {
+        return amount - getAsStorage(stack, ContainerItemContext.forPlayerInteraction(player, hand))
+                .map(storage -> applyTransaction(t -> storage.insert(variant, amount, t)))
+                .orElse(0L);
+    }
+
+    private static Optional<Storage<FluidVariant>> getAsStorage(ItemStack stack, ContainerItemContext context) {
+        return Optional.ofNullable(FluidStorage.ITEM.find(stack, context));
+    }
+
+    private static Optional<StorageView<FluidVariant>> getAsContainer(ItemStack stack, ContainerItemContext context) {
+        return getAsStorage(stack, context).map(storage -> {
+            Iterator<StorageView<FluidVariant>> iter = storage.iterator();
+            return iter.hasNext() ? iter.next() : null;
+        });
+    }
+
+    private static <T> T applyTransaction(Function<Transaction, T> action) {
+        try (@SuppressWarnings("deprecation") var transaction = Transaction.isOpen()
+                ? Transaction.getCurrentUnsafe().openNested()
+                : Transaction.openOuter()) {
+            try {
+                return action.apply(transaction);
+            } finally {
+                transaction.commit();
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/util/PsyFluidHelper.java b/src/main/java/com/minelittlepony/unicopia/util/PsyFluidHelper.java
new file mode 100644
index 00000000..f83e44df
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/util/PsyFluidHelper.java
@@ -0,0 +1,51 @@
+package com.minelittlepony.unicopia.util;
+
+import java.lang.reflect.Method;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import com.google.common.base.Suppliers;
+
+import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
+import net.minecraft.fluid.FluidState;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.registry.Registries;
+
+final class PsyFluidHelper {
+    private static final Supplier<Optional<Class<?>>> SIMPLE_FLUID_CLASS = Suppliers.memoize(() -> {
+        try {
+            return Optional.ofNullable(Class.forName("ivorius.psychedelicraft.fluid.SimpleFluid"));
+        } catch (Throwable t) {
+            return Optional.empty();
+        }
+    });
+    private static final Function<FluidVariant, FluidState> FALLBACK_METHOD = fluid -> fluid.getFluid().getDefaultState();
+    private static final Supplier<Function<FluidVariant, FluidState>> GET_FULL_FLUID_STATE = Suppliers.memoize(() -> SIMPLE_FLUID_CLASS.get().<Function<FluidVariant, FluidState>>map(type -> {
+            try {
+                final Method method = type.getDeclaredMethod("getFluidState", ItemStack.class);
+                if (method != null) {
+                    return fluid -> {
+                        try {
+                            ItemStack stack = Items.STONE.getDefaultStack();
+                            NbtCompound fluidTag = stack.getOrCreateSubNbt("fluid");
+                            fluidTag.putString("id", Registries.FLUID.getId(fluid.getFluid()).toString());
+                            fluidTag.put("attributes", fluid.getNbt());
+                            return FluidState.class.cast(method.invoke(type.cast(fluid), stack));
+                        } catch (Throwable tt) {}
+                        return FALLBACK_METHOD.apply(fluid);
+                    };
+                }
+            } catch (Throwable t) {}
+            return FALLBACK_METHOD;
+        }).orElse(FALLBACK_METHOD));
+
+    static FluidState getFullFluidState(FluidVariant variant) {
+        return SIMPLE_FLUID_CLASS.get()
+                .filter(type -> type.isAssignableFrom(variant.getFluid().getClass()))
+                .map(type -> GET_FULL_FLUID_STATE.get().apply(variant))
+                .orElseGet(() -> variant.getFluid().getDefaultState());
+    }
+}

From 77d1d624949c6d2887fad6cefd827bf231a4abbc Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Sun, 31 Mar 2024 22:47:15 +0100
Subject: [PATCH 07/21] Fix build

---
 .../unicopia/block/PineappleCropBlock.java    |  2 +-
 .../unicopia/block/SegmentedCropBlock.java    |  2 +-
 .../loot_tables/blocks/apple_pie.json         | 47 -------------------
 3 files changed, 2 insertions(+), 49 deletions(-)
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/apple_pie.json

diff --git a/src/main/java/com/minelittlepony/unicopia/block/PineappleCropBlock.java b/src/main/java/com/minelittlepony/unicopia/block/PineappleCropBlock.java
index 2530a8d9..431597c5 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/PineappleCropBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/PineappleCropBlock.java
@@ -41,7 +41,7 @@ public class PineappleCropBlock extends CropBlock {
     }
 
     @Override
-    protected boolean canPlantOnTop(BlockState floor, BlockView world, BlockPos pos) {
+    public boolean canPlantOnTop(BlockState floor, BlockView world, BlockPos pos) {
         return floor.isOf(this) || super.canPlantOnTop(floor, world, pos);
     }
 
diff --git a/src/main/java/com/minelittlepony/unicopia/block/SegmentedCropBlock.java b/src/main/java/com/minelittlepony/unicopia/block/SegmentedCropBlock.java
index 25f3539c..83decf55 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/SegmentedCropBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/SegmentedCropBlock.java
@@ -97,7 +97,7 @@ public class SegmentedCropBlock extends CropBlock implements SegmentedBlock {
     }
 
     @Override
-    protected boolean canPlantOnTop(BlockState state, BlockView view, BlockPos pos) {
+    public boolean canPlantOnTop(BlockState state, BlockView view, BlockPos pos) {
         return (state.getBlock() instanceof SegmentedCropBlock o && o.canSupportBlock(this, state, view, pos)) || super.canPlantOnTop(state, view, pos);
     }
 
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/apple_pie.json b/src/main/resources/data/unicopia/loot_tables/blocks/apple_pie.json
deleted file mode 100644
index 263cdd20..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/apple_pie.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
-  "type": "minecraft:block",
-  "functions": [
-    {
-      "function": "minecraft:explosion_decay"
-    }
-  ],
-  "pools": [
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "condition": "minecraft:all_of",
-          "terms": [
-            {
-              "block": "unicopia:apple_pie",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "stomped": true
-              }
-            },
-            {
-              "condition": "minecraft:match_tool",
-              "predicate": {
-                "enchantments": [
-                  {
-                    "enchantment": "minecraft:silk_touch",
-                    "levels": {
-                      "min": 1
-                    }
-                  }
-                ]
-              }
-            }
-          ]
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:apple_pie_hoof"
-        }
-      ],
-      "rolls": 1.0
-    }
-  ]
-}
\ No newline at end of file

From 3bc1628fd4c27d40bd225c89826788f44b39092f Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Mon, 1 Apr 2024 23:39:21 +0100
Subject: [PATCH 08/21] Datagen loot tables and partially rewrite food

---
 .../unicopia/UConventionalTags.java           |  18 ++
 .../com/minelittlepony/unicopia/UTags.java    |   5 +-
 .../unicopia/block/EnchantedFruitBlock.java   |   2 +-
 .../unicopia/block/SegmentedCropBlock.java    |   4 +
 .../unicopia/datagen/Datagen.java             |  22 ++
 .../providers/UBlockStateModelGenerator.java  |   6 +-
 .../loot/UBlockLootTableProvider.java         | 240 +++++++++++++++---
 .../UChestAdditionsLootTableProvider.java     |   4 +
 .../loot/UChestLootTableProvider.java         |  56 ++++
 .../loot/UEntityLootTableProvider.java        |  63 +++++
 .../providers/tag/UBlockTagProvider.java      |   4 +
 .../providers/tag/UItemTagProvider.java       |  61 ++++-
 .../unicopia/diet/DietProfile.java            |  40 ++-
 .../unicopia/diet/DietsLoader.java            |  65 ++---
 .../minelittlepony/unicopia/diet/Effect.java  |  22 +-
 .../unicopia/diet/FoodGroupKey.java           |  49 ++++
 .../unicopia/diet/PonyDiets.java              |  18 +-
 .../resources/assets/unicopia/lang/en_us.json |  12 +-
 .../resources/data/c/tags/items/concrete.json |  21 --
 .../data/c/tags/items/cooked_insects.json     |   7 -
 .../data/c/tags/items/cooked_meats.json       |  22 --
 .../data/c/tags/items/coral_blocks.json       |  10 -
 .../data/c/tags/items/coral_fans.json         |  10 -
 .../resources/data/c/tags/items/corals.json   |  10 -
 .../data/c/tags/items/glazed_terracotta.json  |  21 --
 .../resources/data/c/tags/items/love.json     |   8 -
 .../resources/data/c/tags/items/raw_fish.json |  10 -
 .../data/c/tags/items/raw_insects.json        |   9 -
 .../data/c/tags/items/raw_meats.json          |  17 --
 .../resources/data/c/tags/items/rocks.json    |   6 -
 .../data/c/tags/items/rotten_meats.json       |   6 -
 .../resources/data/c/tags/items/worms.json    |   6 -
 .../conglomerate_materials_from_ground.json   |   4 +-
 .../items/overworld/edible_cooked_meat.json   |   2 -
 .../items/overworld/edible_raw_meat.json      |   2 +-
 .../baked_goods.json                          |   0
 .../diets/food_groups/bat_ponys_delight.json  |  10 +
 .../{food_effects => food_groups}/candy.json  |   0
 .../desserts.json                             |   0
 .../fish/cooked.json                          |   2 +-
 .../fish/raw.json                             |   2 +-
 .../fish/rotten.json                          |   2 +-
 .../foraging/blinding.json                    |   0
 .../foraging/dangerous.json                   |   0
 .../foraging/edible.json                      |   0
 .../foraging/edible_filling.json              |   0
 .../foraging/leafy_greens.json                |   0
 .../foraging/moderate.json                    |   0
 .../foraging/nauseating.json                  |   0
 .../foraging/prickly.json                     |   0
 .../foraging/radioactive.json                 |   0
 .../foraging/risky.json                       |   0
 .../foraging/severely_nauseating.json         |   0
 .../foraging/severely_prickly.json            |   0
 .../foraging/strengthening.json               |   0
 .../{food_effects => food_groups}/fruit.json  |   0
 .../insect/cooked.json                        |   2 +-
 .../insect/raw.json                           |   2 +-
 .../data/unicopia/diets/food_groups/love.json |  21 ++
 .../meat/cooked.json                          |   0
 .../meat/raw.json                             |   0
 .../meat/rotten.json                          |   2 +-
 .../nuts_and_seeds.json                       |   0
 .../pinecone.json                             |   0
 .../{food_effects => food_groups}/rocks.json  |   0
 .../food_groups/sea_vegetable/cooked.json     |  11 +
 .../diets/food_groups/sea_vegetable/raw.json  |  11 +
 .../unicopia/diets/food_groups/shells.json    |  10 +
 .../unicopia/diets/food_groups/shelly.json    |  10 +
 .../data/unicopia/diets/races/alicorn.json    |  24 +-
 .../data/unicopia/diets/races/bat.json        |  32 +--
 .../data/unicopia/diets/races/changeling.json |  36 +--
 .../data/unicopia/diets/races/earth.json      |  32 +--
 .../data/unicopia/diets/races/hippogriff.json |  32 +--
 .../data/unicopia/diets/races/human.json      |  14 +-
 .../data/unicopia/diets/races/kirin.json      |  38 +--
 .../data/unicopia/diets/races/pegasus.json    |  24 +-
 .../data/unicopia/diets/races/seapony.json    |  21 +-
 .../data/unicopia/diets/races/unicorn.json    |  20 +-
 .../unicopia/loot_tables/blocks/bananas.json  |  40 ---
 .../blocks/chiselled_chitin_hull.json         |  49 ----
 .../loot_tables/blocks/clam_shell.json        |  96 -------
 .../loot_tables/blocks/cloud_slab.json        |  41 ---
 .../loot_tables/blocks/dense_cloud_slab.json  |  41 ---
 .../loot_tables/blocks/etched_cloud_slab.json |  41 ---
 .../loot_tables/blocks/gold_root.json         |  51 ----
 .../loot_tables/blocks/golden_apple.json      |  49 ----
 .../loot_tables/blocks/hay_block.json         | 157 ------------
 .../loot_tables/blocks/mysterious_egg.json    |  64 -----
 .../unicopia/loot_tables/blocks/oats.json     |  55 ----
 .../loot_tables/blocks/oats_crown.json        |  29 ---
 .../loot_tables/blocks/oats_stem.json         |  29 ---
 .../loot_tables/blocks/pineapple.json         |  35 ---
 .../loot_tables/blocks/plunder_vine.json      |  42 ---
 .../loot_tables/blocks/rice_block.json        | 165 ------------
 .../unicopia/loot_tables/blocks/rocks.json    |  85 -------
 .../loot_tables/blocks/scallop_shell.json     |  96 -------
 .../loot_tables/blocks/slime_pustule.json     |  55 ----
 .../loot_tables/blocks/soggy_cloud_slab.json  |  27 --
 .../loot_tables/blocks/straw_block.json       | 165 ------------
 .../loot_tables/blocks/turret_shell.json      |  96 -------
 .../chests/changeling_hive_trap.json          | 204 ---------------
 .../loot_tables/entities/butterfly.json       |  25 --
 .../loot_tables/entities/loot_bug.json        |  87 -------
 .../loot_tables/entities/storm_cloud.json     |  34 ---
 .../tags/items/food_types/cooked_fish.json    |   9 -
 .../tags/items/food_types/cooked_insect.json  |   6 -
 .../tags/items/food_types/cooked_meat.json    |   2 +-
 .../unicopia/tags/items/food_types/love.json  |   2 +-
 .../tags/items/food_types/raw_fish.json       |  10 -
 .../tags/items/food_types/raw_insect.json     |   6 -
 .../tags/items/food_types/raw_meat.json       |   2 +-
 .../tags/items/food_types/rotten_meat.json    |   6 -
 .../unicopia/tags/items/groups/bat_pony.json  |   2 +-
 .../tags/items/groups/changeling.json         |   8 +-
 .../unicopia/tags/items/groups/pegasus.json   |   4 +-
 .../resources/data/unicopia/traits/love.json  |   2 +-
 117 files changed, 809 insertions(+), 2328 deletions(-)
 create mode 100644 src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestLootTableProvider.java
 create mode 100644 src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UEntityLootTableProvider.java
 create mode 100644 src/main/java/com/minelittlepony/unicopia/diet/FoodGroupKey.java
 delete mode 100644 src/main/resources/data/c/tags/items/concrete.json
 delete mode 100644 src/main/resources/data/c/tags/items/cooked_insects.json
 delete mode 100644 src/main/resources/data/c/tags/items/cooked_meats.json
 delete mode 100644 src/main/resources/data/c/tags/items/coral_blocks.json
 delete mode 100644 src/main/resources/data/c/tags/items/coral_fans.json
 delete mode 100644 src/main/resources/data/c/tags/items/corals.json
 delete mode 100644 src/main/resources/data/c/tags/items/glazed_terracotta.json
 delete mode 100644 src/main/resources/data/c/tags/items/love.json
 delete mode 100644 src/main/resources/data/c/tags/items/raw_fish.json
 delete mode 100644 src/main/resources/data/c/tags/items/raw_insects.json
 delete mode 100644 src/main/resources/data/c/tags/items/raw_meats.json
 delete mode 100644 src/main/resources/data/c/tags/items/rocks.json
 delete mode 100644 src/main/resources/data/c/tags/items/rotten_meats.json
 delete mode 100644 src/main/resources/data/c/tags/items/worms.json
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/baked_goods.json (100%)
 create mode 100644 src/main/resources/data/unicopia/diets/food_groups/bat_ponys_delight.json
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/candy.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/desserts.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/fish/cooked.json (81%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/fish/raw.json (87%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/fish/rotten.json (87%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/blinding.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/dangerous.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/edible.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/edible_filling.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/leafy_greens.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/moderate.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/nauseating.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/prickly.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/radioactive.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/risky.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/severely_nauseating.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/severely_prickly.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/foraging/strengthening.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/fruit.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/insect/cooked.json (80%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/insect/raw.json (81%)
 create mode 100644 src/main/resources/data/unicopia/diets/food_groups/love.json
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/meat/cooked.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/meat/raw.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/meat/rotten.json (87%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/nuts_and_seeds.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/pinecone.json (100%)
 rename src/main/resources/data/unicopia/diets/{food_effects => food_groups}/rocks.json (100%)
 create mode 100644 src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/cooked.json
 create mode 100644 src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/raw.json
 create mode 100644 src/main/resources/data/unicopia/diets/food_groups/shells.json
 create mode 100644 src/main/resources/data/unicopia/diets/food_groups/shelly.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/bananas.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/chiselled_chitin_hull.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/clam_shell.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/cloud_slab.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/dense_cloud_slab.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/etched_cloud_slab.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/gold_root.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/golden_apple.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/mysterious_egg.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/oats.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/oats_crown.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/oats_stem.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/pineapple.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/plunder_vine.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/rice_block.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/rocks.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/scallop_shell.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/slime_pustule.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/soggy_cloud_slab.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/straw_block.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/blocks/turret_shell.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/chests/changeling_hive_trap.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/entities/butterfly.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/entities/loot_bug.json
 delete mode 100644 src/main/resources/data/unicopia/loot_tables/entities/storm_cloud.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/cooked_fish.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/cooked_insect.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/raw_fish.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/raw_insect.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/rotten_meat.json

diff --git a/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java b/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
index bb55a662..a4be14e6 100644
--- a/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
+++ b/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
@@ -10,6 +10,10 @@ public interface UConventionalTags {
     interface Blocks {
         TagKey<Block> CONCRETE_POWDERS = block("concrete_powders");
         TagKey<Block> CONCRETES = block("concretes");
+        TagKey<Block> GLAZED_TERRACOTTAS = block("glazed_terracottas");
+        TagKey<Block> CORAL_BLOCKS = block("coral_blocks");
+        TagKey<Block> CORAL_FANS = block("coral_fans");
+        TagKey<Block> CORALS = block("corals");
 
         private static TagKey<Block> block(String name) {
             return TagKey.of(RegistryKeys.BLOCK, new Identifier("c", name));
@@ -19,6 +23,10 @@ public interface UConventionalTags {
     interface Items {
         TagKey<Item> CONCRETE_POWDERS = item("concrete_powders");
         TagKey<Item> CONCRETES = item("concretes");
+        TagKey<Item> GLAZED_TERRACOTTAS = item("glazed_terracottas");
+        TagKey<Item> CORAL_BLOCKS = item("coral_blocks");
+        TagKey<Item> CORAL_FANS = item("coral_fans");
+        TagKey<Item> CORALS = item("corals");
 
         TagKey<Item> APPLES = item("apples");
         TagKey<Item> ACORNS = item("acorns");
@@ -35,8 +43,18 @@ public interface UConventionalTags {
         TagKey<Item> OATMEALS = item("oatmeals");
 
         TagKey<Item> FRUITS = item("fruits");
+        TagKey<Item> WORMS = item("worms");
+        TagKey<Item> ROCKS = item("rocks");
 
+        TagKey<Item> RAW_INSECT = item("raw_insect");
+        TagKey<Item> COOKED_INSECT = item("cooked_insect");
+
+        TagKey<Item> RAW_FISH = item("raw_fish");
         TagKey<Item> COOKED_FISH = item("cooked_fish");
+        TagKey<Item> ROTTEN_FISH = item("rotten_fish");
+        TagKey<Item> RAW_MEAT = item("raw_meat");
+        TagKey<Item> COOKED_MEAT = item("cooked_meat");
+        TagKey<Item> ROTTEN_MEAT = item("rotten_meat");
 
         TagKey<Item> CROPS_PEANUTS = item("crops/peanuts");
         TagKey<Item> TOOL_KNIVES = item("tools/knives");
diff --git a/src/main/java/com/minelittlepony/unicopia/UTags.java b/src/main/java/com/minelittlepony/unicopia/UTags.java
index 82bd5417..cb045541 100644
--- a/src/main/java/com/minelittlepony/unicopia/UTags.java
+++ b/src/main/java/com/minelittlepony/unicopia/UTags.java
@@ -34,9 +34,12 @@ public interface UTags {
         TagKey<Item> SPOOKED_MOB_DROPS = item("spooked_mob_drops");
         TagKey<Item> HAS_NO_TRAITS = item("has_no_traits");
         TagKey<Item> IS_DELIVERED_AGGRESSIVELY = item("is_delivered_aggressively");
+        TagKey<Item> CONTAINER_WITH_LOVE = item("container_with_love");
         TagKey<Item> FLOATS_ON_CLOUDS = item("floats_on_clouds");
         TagKey<Item> COOLS_OFF_KIRINS = item("cools_off_kirins");
-        TagKey<Item> LOOT_BUG_HIGH_VALUE_DROPS = item("loot_bug_high_value_drops");
+        TagKey<Item> LOOT_BUG_COMMON_DROPS = item("loot_bug_common_drops");
+        TagKey<Item> LOOT_BUG_RARE_DROPS = item("loot_bug_rare_drops");
+        TagKey<Item> LOOT_BUG_EPIC_DROPS = item("loot_bug_epic_drops");
 
         TagKey<Item> SHELLS = item("food_types/shells");
 
diff --git a/src/main/java/com/minelittlepony/unicopia/block/EnchantedFruitBlock.java b/src/main/java/com/minelittlepony/unicopia/block/EnchantedFruitBlock.java
index c6af9166..fc72ef95 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/EnchantedFruitBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/EnchantedFruitBlock.java
@@ -8,7 +8,7 @@ import net.minecraft.util.math.Direction;
 import net.minecraft.util.shape.VoxelShape;
 
 public class EnchantedFruitBlock extends FruitBlock {
-    static final BooleanProperty ENCHANTED = BooleanProperty.of("enchanted");
+    public static final BooleanProperty ENCHANTED = BooleanProperty.of("enchanted");
 
     public EnchantedFruitBlock(Settings settings, Direction attachmentFace, Block stem, VoxelShape shape) {
         super(settings, attachmentFace, stem, shape);
diff --git a/src/main/java/com/minelittlepony/unicopia/block/SegmentedCropBlock.java b/src/main/java/com/minelittlepony/unicopia/block/SegmentedCropBlock.java
index 83decf55..1f16f7c3 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/SegmentedCropBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/SegmentedCropBlock.java
@@ -204,4 +204,8 @@ public class SegmentedCropBlock extends CropBlock implements SegmentedBlock {
         return state.getBlock() == this || (nextSegmentSupplier != null && nextSegmentSupplier.get().isNext(state));
     }
 
+    @Nullable
+    public SegmentedCropBlock getNext() {
+        return nextSegmentSupplier == null ? null : nextSegmentSupplier.get();
+    }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/Datagen.java b/src/main/java/com/minelittlepony/unicopia/datagen/Datagen.java
index f8d103a6..667d6173 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/Datagen.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/Datagen.java
@@ -3,12 +3,15 @@ package com.minelittlepony.unicopia.datagen;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
+import com.minelittlepony.unicopia.block.EdibleBlock;
 import com.minelittlepony.unicopia.datagen.providers.SeasonsGrowthRatesProvider;
 import com.minelittlepony.unicopia.datagen.providers.UAdvancementsProvider;
 import com.minelittlepony.unicopia.datagen.providers.UModelProvider;
 import com.minelittlepony.unicopia.datagen.providers.loot.UBlockAdditionsLootTableProvider;
 import com.minelittlepony.unicopia.datagen.providers.loot.UBlockLootTableProvider;
 import com.minelittlepony.unicopia.datagen.providers.loot.UChestAdditionsLootTableProvider;
+import com.minelittlepony.unicopia.datagen.providers.loot.UChestLootTableProvider;
+import com.minelittlepony.unicopia.datagen.providers.loot.UEntityLootTableProvider;
 import com.minelittlepony.unicopia.datagen.providers.recipe.URecipeProvider;
 import com.minelittlepony.unicopia.datagen.providers.tag.UBlockTagProvider;
 import com.minelittlepony.unicopia.datagen.providers.tag.UDamageTypeProvider;
@@ -18,13 +21,30 @@ import com.minelittlepony.unicopia.server.world.UWorldGen;
 
 import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
 import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
+import net.minecraft.block.Block;
+import net.minecraft.item.Item;
+import net.minecraft.registry.Registries;
+import net.minecraft.registry.Registry;
 import net.minecraft.registry.RegistryBuilder;
 import net.minecraft.registry.RegistryKeys;
+import net.minecraft.util.Identifier;
 import net.minecraft.world.biome.OverworldBiomeCreator;
 
 public class Datagen implements DataGeneratorEntrypoint {
     public static final Logger LOGGER = LogManager.getLogger();
 
+    public static Block getOrCreateBaleBlock(Identifier id) {
+        return Registries.BLOCK.getOrEmpty(id).orElseGet(() -> {
+            return Registry.register(Registries.BLOCK, id, new EdibleBlock(id, id, false));
+        });
+    }
+
+    public static Item getOrCreateItem(Identifier id) {
+        return Registries.ITEM.getOrEmpty(id).orElseGet(() -> {
+            return Registry.register(Registries.ITEM, id, new Item(new Item.Settings()));
+        });
+    }
+
     @Override
     public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) {
         final var pack = fabricDataGenerator.createPack();
@@ -35,6 +55,8 @@ public class Datagen implements DataGeneratorEntrypoint {
         pack.addProvider(UModelProvider::new);
         pack.addProvider(URecipeProvider::new);
         pack.addProvider(UBlockLootTableProvider::new);
+        pack.addProvider(UEntityLootTableProvider::new);
+        pack.addProvider(UChestLootTableProvider::new);
         pack.addProvider(UBlockAdditionsLootTableProvider::new);
         pack.addProvider(UChestAdditionsLootTableProvider::new);
         pack.addProvider(SeasonsGrowthRatesProvider::new);
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockStateModelGenerator.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockStateModelGenerator.java
index 048c3ad6..d016daae 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockStateModelGenerator.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockStateModelGenerator.java
@@ -16,6 +16,7 @@ import com.minelittlepony.unicopia.block.ShellsBlock;
 import com.minelittlepony.unicopia.block.SlimePustuleBlock;
 import com.minelittlepony.unicopia.block.UBlocks;
 import com.minelittlepony.unicopia.block.zap.ZapAppleLeavesBlock;
+import com.minelittlepony.unicopia.datagen.Datagen;
 import com.minelittlepony.unicopia.datagen.UBlockFamilies;
 import com.minelittlepony.unicopia.server.world.Tree;
 
@@ -41,7 +42,6 @@ import net.minecraft.data.client.When;
 import net.minecraft.item.Item;
 import net.minecraft.item.Items;
 import net.minecraft.registry.Registries;
-import net.minecraft.registry.Registry;
 import net.minecraft.state.property.BooleanProperty;
 import net.minecraft.state.property.EnumProperty;
 import net.minecraft.state.property.Properties;
@@ -429,9 +429,7 @@ public class UBlockStateModelGenerator extends BlockStateModelGenerator {
         Identifier side = baseBlockId.withPath(p -> "block/" + p + "_side");
         TextureMap textures = new TextureMap().put(TOP, top).put(SIDE, side);
 
-        MultipartBlockStateSupplier supplier = MultipartBlockStateSupplier.create(Registries.BLOCK.getOrEmpty(blockId).orElseGet(() -> {
-            return Registry.register(Registries.BLOCK, blockId, new EdibleBlock(blockId, blockId, false));
-        }));
+        MultipartBlockStateSupplier supplier = MultipartBlockStateSupplier.create(Datagen.getOrCreateBaleBlock(blockId));
         Map<Integer, Identifier> uploadedModels = new HashMap<>();
 
         for (Direction.Axis axis : Direction.Axis.VALUES) {
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockLootTableProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockLootTableProvider.java
index d7ed00b1..9c625a1d 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockLootTableProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UBlockLootTableProvider.java
@@ -2,8 +2,16 @@ package com.minelittlepony.unicopia.datagen.providers.loot;
 
 import java.util.List;
 
+import com.minelittlepony.unicopia.Unicopia;
+import com.minelittlepony.unicopia.block.EdibleBlock;
+import com.minelittlepony.unicopia.block.EnchantedFruitBlock;
 import com.minelittlepony.unicopia.block.PieBlock;
+import com.minelittlepony.unicopia.block.PileBlock;
+import com.minelittlepony.unicopia.block.SegmentedCropBlock;
+import com.minelittlepony.unicopia.block.ShellsBlock;
+import com.minelittlepony.unicopia.block.SlimePustuleBlock;
 import com.minelittlepony.unicopia.block.UBlocks;
+import com.minelittlepony.unicopia.datagen.Datagen;
 import com.minelittlepony.unicopia.datagen.providers.UModelProvider;
 import com.minelittlepony.unicopia.item.UItems;
 import com.minelittlepony.unicopia.server.world.Tree;
@@ -11,29 +19,44 @@ import com.minelittlepony.unicopia.server.world.UTreeGen;
 
 import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
 import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider;
+import net.fabricmc.fabric.api.resource.conditions.v1.DefaultResourceConditions;
 import net.minecraft.block.BedBlock;
 import net.minecraft.block.Block;
 import net.minecraft.block.Blocks;
+import net.minecraft.block.CarrotsBlock;
+import net.minecraft.block.SlabBlock;
 import net.minecraft.block.enums.BedPart;
+import net.minecraft.block.enums.BlockHalf;
+import net.minecraft.block.enums.SlabType;
 import net.minecraft.enchantment.Enchantments;
 import net.minecraft.item.Item;
+import net.minecraft.item.ItemConvertible;
 import net.minecraft.item.Items;
 import net.minecraft.loot.LootPool;
 import net.minecraft.loot.LootTable;
 import net.minecraft.loot.condition.BlockStatePropertyLootCondition;
 import net.minecraft.loot.condition.LootConditionConsumingBuilder;
+import net.minecraft.loot.condition.RandomChanceLootCondition;
 import net.minecraft.loot.condition.TableBonusLootCondition;
 import net.minecraft.loot.entry.ItemEntry;
+import net.minecraft.loot.function.ApplyBonusLootFunction;
+import net.minecraft.loot.function.ConditionalLootFunction;
 import net.minecraft.loot.function.SetCountLootFunction;
 import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
+import net.minecraft.loot.provider.number.LootNumberProvider;
 import net.minecraft.loot.provider.number.UniformLootNumberProvider;
 import net.minecraft.predicate.StatePredicate;
 import net.minecraft.state.property.BooleanProperty;
+import net.minecraft.state.property.IntProperty;
+import net.minecraft.state.property.Properties;
 import net.minecraft.state.property.Property;
+import net.minecraft.util.Identifier;
 import net.minecraft.util.StringIdentifiable;
 
 public class UBlockLootTableProvider extends FabricBlockLootTableProvider {
 
+    private static final ConditionalLootFunction.Builder<?> FORTUNE_BONUS = ApplyBonusLootFunction.binomialWithBonusCount(Enchantments.FORTUNE, 0.5714286F, 3);
+
     public UBlockLootTableProvider(FabricDataOutput output) {
         super(output);
     }
@@ -72,13 +95,22 @@ public class UBlockLootTableProvider extends FabricBlockLootTableProvider {
                 UBlocks.CHISELLED_CHITIN_SLAB, UBlocks.CLOUD_BRICK_SLAB,
                 UBlocks.CLOUD_PLANK_SLAB, UBlocks.PALM_SLAB, UBlocks.ZAP_SLAB, UBlocks.WAXED_ZAP_SLAB
         ).forEach(slab -> addDrop(slab, this::slabDrops));
+        addDrop(UBlocks.CLOUD_SLAB, slab -> decomposingSlabDrops(slab, UItems.CLOUD_LUMP, 2));
+        addDrop(UBlocks.SOGGY_CLOUD_SLAB, slab -> decomposingSlabDrops(slab, UItems.CLOUD_LUMP, 2));
+        addDrop(UBlocks.DENSE_CLOUD_SLAB, slab -> decomposingSlabDrops(slab, UItems.CLOUD_LUMP, 4));
+        addDrop(UBlocks.ETCHED_CLOUD_SLAB, slab -> decomposingSlabDrops(slab, UItems.CLOUD_LUMP, 4));
 
         // fruit
         UModelProvider.FRUITS.forEach((block, drop) -> {
             if (block != UBlocks.GOLDEN_APPLE) {
-                addDrop(block, drop);
+                addDrop(block, fortuneBonusDrops(drop));
             }
         });
+        addDrop(UBlocks.GOLDEN_APPLE, LootTable.builder().pool(LootPool.builder()
+            .rolls(exactly(1))
+            .with(applyStateCondition(UBlocks.GOLDEN_APPLE, EnchantedFruitBlock.ENCHANTED, false, applyExplosionDecay(UBlocks.GOLDEN_APPLE, ItemEntry.builder(Items.GOLDEN_APPLE))).apply(FORTUNE_BONUS))
+            .with(applyStateCondition(UBlocks.GOLDEN_APPLE, EnchantedFruitBlock.ENCHANTED, true, applyExplosionDecay(UBlocks.GOLDEN_APPLE, ItemEntry.builder(Items.ENCHANTED_GOLDEN_APPLE))).apply(FORTUNE_BONUS))
+        ));
         List.of(UBlocks.GREEN_APPLE_LEAVES, UBlocks.SOUR_APPLE_LEAVES, UBlocks.SWEET_APPLE_LEAVES, UBlocks.GOLDEN_OAK_LEAVES).forEach(block -> addDrop(block, this::fruitLeavesDrops));
         addDrop(UBlocks.MANGO_LEAVES, block -> leavesDrops(block, UTreeGen.MANGO_TREE.sapling().get(), 0.025F, 0.027777778F, 0.03125F, 0.041666668F, 0.1F)); // same chance as jungle
         addDrop(UBlocks.ZAP_LEAVES, block -> leavesDrops(block, UTreeGen.ZAP_APPLE_TREE.sapling().get(), SAPLING_DROP_CHANCE));
@@ -106,63 +138,197 @@ public class UBlockLootTableProvider extends FabricBlockLootTableProvider {
             UBlocks.CLOUD_BED, UBlocks.CLOTH_BED
         ).forEach(bed -> addDrop(bed, b -> dropsWithProperty(b, BedBlock.PART, BedPart.HEAD)));
 
-        addDrop(UBlocks.CHITIN_SPIKES, drops(UBlocks.CHITIN_SPIKES, UItems.CARAPACE, ConstantLootNumberProvider.create(6)));
-        addDrop(UBlocks.CHITIN, drops(UBlocks.CHITIN, UItems.CARAPACE, ConstantLootNumberProvider.create(9)));
-        addDrop(UBlocks.SURFACE_CHITIN, drops(UBlocks.SURFACE_CHITIN, UItems.CARAPACE, ConstantLootNumberProvider.create(9)));
+        addDrop(UBlocks.CHITIN_SPIKES, drops(UBlocks.CHITIN_SPIKES, UItems.CARAPACE, exactly(6)));
+        addDrop(UBlocks.CHITIN, drops(UBlocks.CHITIN, UItems.CARAPACE, exactly(9)));
+        addDrop(UBlocks.SURFACE_CHITIN, drops(UBlocks.SURFACE_CHITIN, UItems.CARAPACE, exactly(9)));
+        addDrop(UBlocks.CHISELLED_CHITIN_HULL, hullDrops(UBlocks.CHISELLED_CHITIN_HULL, UBlocks.CHITIN, UBlocks.CHISELLED_CHITIN));
 
-        addDrop(UBlocks.CLOUD, drops(UBlocks.CLOUD, UItems.CLOUD_LUMP, ConstantLootNumberProvider.create(4)));
-        addDrop(UBlocks.CLOUD_STAIRS, drops(UBlocks.CLOUD_STAIRS, UItems.CLOUD_LUMP, ConstantLootNumberProvider.create(6)));
+        addDrop(UBlocks.SLIME_PUSTULE, LootTable.builder()
+                .pool(applyStateCondition(UBlocks.SLIME_PUSTULE, SlimePustuleBlock.SHAPE, SlimePustuleBlock.Shape.POD,
+                        addSurvivesExplosionCondition(UBlocks.SLIME_PUSTULE, LootPool.builder()
+                    .rolls(exactly(1))
+                    .with(ItemEntry.builder(UBlocks.SLIME_PUSTULE)).conditionally(WITH_SILK_TOUCH_OR_SHEARS))
+        )));
+        addDrop(UBlocks.MYSTERIOUS_EGG, LootTable.builder()
+            .pool(addSurvivesExplosionCondition(UBlocks.MYSTERIOUS_EGG, LootPool.builder()
+                .rolls(exactly(1))
+                .with(ItemEntry.builder(UBlocks.MYSTERIOUS_EGG))
+                    .apply(PileBlock.COUNT.getValues(), count -> applyStateCondition(UBlocks.MYSTERIOUS_EGG, PileBlock.COUNT, count, SetCountLootFunction.builder(exactly(count)))))));
 
-        addDrop(UBlocks.SOGGY_CLOUD, drops(UBlocks.CLOUD, UItems.CLOUD_LUMP, ConstantLootNumberProvider.create(4)));
-        addDrop(UBlocks.SOGGY_CLOUD_STAIRS, drops(UBlocks.CLOUD_STAIRS, UItems.CLOUD_LUMP, ConstantLootNumberProvider.create(6)));
+        addDrop(UBlocks.CLOUD, drops(UBlocks.CLOUD, UItems.CLOUD_LUMP, exactly(4)));
+        addDrop(UBlocks.CLOUD_STAIRS, drops(UBlocks.CLOUD_STAIRS, UItems.CLOUD_LUMP, exactly(6)));
 
-        addDrop(UBlocks.DENSE_CLOUD, drops(UBlocks.DENSE_CLOUD, UItems.CLOUD_LUMP, ConstantLootNumberProvider.create(9)));
-        addDrop(UBlocks.DENSE_CLOUD_STAIRS, drops(UBlocks.DENSE_CLOUD_STAIRS, UItems.CLOUD_LUMP, ConstantLootNumberProvider.create(13)));
-        addDrop(UBlocks.ETCHED_CLOUD, drops(UBlocks.ETCHED_CLOUD, UItems.CLOUD_LUMP, ConstantLootNumberProvider.create(9)));
-        addDrop(UBlocks.ETCHED_CLOUD_STAIRS, drops(UBlocks.ETCHED_CLOUD_STAIRS, UItems.CLOUD_LUMP, ConstantLootNumberProvider.create(13)));
+        addDrop(UBlocks.SOGGY_CLOUD, drops(UBlocks.CLOUD, UItems.CLOUD_LUMP, exactly(4)));
+        addDrop(UBlocks.SOGGY_CLOUD_STAIRS, drops(UBlocks.CLOUD_STAIRS, UItems.CLOUD_LUMP, exactly(6)));
+
+        addDrop(UBlocks.DENSE_CLOUD, drops(UBlocks.DENSE_CLOUD, UItems.CLOUD_LUMP, exactly(9)));
+        addDrop(UBlocks.DENSE_CLOUD_STAIRS, drops(UBlocks.DENSE_CLOUD_STAIRS, UItems.CLOUD_LUMP, exactly(13)));
+        addDrop(UBlocks.ETCHED_CLOUD, drops(UBlocks.ETCHED_CLOUD, UItems.CLOUD_LUMP, exactly(9)));
+        addDrop(UBlocks.ETCHED_CLOUD_STAIRS, drops(UBlocks.ETCHED_CLOUD_STAIRS, UItems.CLOUD_LUMP, exactly(13)));
 
         // recipe produces: 6 blocks -> 3 pillars means: 6/3 = 2
-        addDrop(UBlocks.CLOUD_PILLAR, drops(UBlocks.CLOUD_PILLAR, UBlocks.CLOUD, ConstantLootNumberProvider.create(2)));
+        addDrop(UBlocks.CLOUD_PILLAR, drops(UBlocks.CLOUD_PILLAR, UBlocks.CLOUD, exactly(2)));
 
         addDrop(UBlocks.FROSTED_OBSIDIAN, Blocks.OBSIDIAN);
         addDrop(UBlocks.APPLE_PIE, pieDrops(UBlocks.APPLE_PIE, UItems.APPLE_PIE, UItems.APPLE_PIE_HOOF));
+
+        // crops
+        addTallCropDrops(UBlocks.OATS, UItems.OATS);
+        addDrop(UBlocks.BANANAS, LootTable.builder()
+            .pool(addSurvivesExplosionCondition(UBlocks.BANANAS, LootPool.builder()
+                .rolls(exactly(1))
+                .with(item(UItems.BANANA, between(6, 12F)).apply(FORTUNE_BONUS))
+            )));
+        addDrop(UBlocks.PINEAPPLE, LootTable.builder()
+            .pool(addSurvivesExplosionCondition(UBlocks.PINEAPPLE, LootPool.builder()
+                .rolls(exactly(1))
+                .with(item(UItems.PINEAPPLE, between(6, 12F))
+                    .apply(FORTUNE_BONUS)
+                    .conditionally(BlockStatePropertyLootCondition.builder(UBlocks.PINEAPPLE).properties(StatePredicate.Builder.create()
+                        .exactMatch(Properties.BLOCK_HALF, BlockHalf.TOP)
+                        .exactMatch(Properties.AGE_7, Properties.AGE_7_MAX))))
+            )));
+        addDrop(UBlocks.ROCKS, applyExplosionDecay(UBlocks.ROCKS, LootTable.builder()
+                .pool(applyStateCondition(UBlocks.ROCKS, Properties.AGE_7, Properties.AGE_7_MAX, LootPool.builder()
+                    .rolls(exactly(1))
+                    .with(ItemEntry.builder(UItems.WEIRD_ROCK).conditionally(RandomChanceLootCondition.builder(0.25F)).apply(FORTUNE_BONUS))
+                    .with(ItemEntry.builder(UItems.ROCK).apply(FORTUNE_BONUS))))
+                .pool(LootPool.builder()
+                    .rolls(exactly(1))
+                    .with(ItemEntry.builder(UItems.PEBBLES)))
+        ));
+        addDrop(UBlocks.GOLD_ROOT, applyExplosionDecay(UBlocks.GOLD_ROOT, LootTable.builder()
+            .pool(LootPool.builder().with(ItemEntry.builder(Items.GOLDEN_CARROT)))
+            .pool(applyStateCondition(UBlocks.GOLD_ROOT, CarrotsBlock.AGE, 7, LootPool.builder())
+                    .with(ItemEntry.builder(Items.GOLDEN_CARROT).apply(FORTUNE_BONUS)))));
+        addDrop(UBlocks.PLUNDER_VINE, applyExplosionDecay(UBlocks.PLUNDER_VINE, LootTable.builder()
+                .pool(LootPool.builder().rolls(exactly(4))
+                    .with(ItemEntry.builder(Items.STICK))
+                    .with(ItemEntry.builder(Items.DEAD_BUSH)))
+                .pool(LootPool.builder().rolls(exactly(1))
+                        .with(ItemEntry.builder(Items.STICK))
+                        .with(ItemEntry.builder(Items.DEAD_BUSH))
+                        .with(ItemEntry.builder(UItems.GRYPHON_FEATHER)))
+        ));
+
+        // hay
+        addDrop(UBlocks.HAY_BLOCK, b -> edibleBlockDrops(b, Items.WHEAT));
+
+        // shells
+        addDrop(UBlocks.CLAM_SHELL, shellDrops(UBlocks.CLAM_SHELL, UItems.CLAM_SHELL));
+        addDrop(UBlocks.SCALLOP_SHELL, shellDrops(UBlocks.SCALLOP_SHELL, UItems.SCALLOP_SHELL));
+        addDrop(UBlocks.TURRET_SHELL, shellDrops(UBlocks.TURRET_SHELL, UItems.TURRET_SHELL));
+
+        var farmersDelightGenerator = withConditions(DefaultResourceConditions.allModsLoaded("farmersdelight"));
+        farmersDelightGenerator.addDrop(Datagen.getOrCreateBaleBlock(Unicopia.id("rice_block")), b -> edibleBlockDrops(b, Datagen.getOrCreateItem(new Identifier("farmersdelight", "rice_panicle"))));
+        farmersDelightGenerator.addDrop(Datagen.getOrCreateBaleBlock(Unicopia.id("straw_block")), b -> edibleBlockDrops(b, Datagen.getOrCreateItem(new Identifier("farmersdelight", "straw"))));
+    }
+
+    private void addTallCropDrops(SegmentedCropBlock baseCrop, ItemConvertible crop) {
+        addDrop(baseCrop, applyExplosionDecay(baseCrop, LootTable.builder()
+            .pool(LootPool.builder()
+                .rolls(exactly(1))
+                .with(ItemEntry.builder(baseCrop.getSeedsItem()))))
+            .pool(applyStateCondition(baseCrop, baseCrop.getAgeProperty(), baseCrop.getMaxAge(), LootPool.builder()
+                .rolls(exactly(1))
+                .with(ItemEntry.builder(baseCrop.getSeedsItem()).apply(FORTUNE_BONUS)))));
+
+        SegmentedCropBlock stage = baseCrop;
+        while ((stage = stage.getNext()) != null) {
+            addDrop(stage, applyExplosionDecay(stage, LootTable.builder()
+                .pool(LootPool.builder()
+                    .rolls(exactly(1))
+                    .with(applyStateCondition(stage, stage.getAgeProperty(), stage.getMaxAge(), ItemEntry.builder(crop))))));
+        }
+    }
+
+    private LootTable.Builder decomposingSlabDrops(Block slab, ItemConvertible drop, int count) {
+        return LootTable.builder()
+            .pool(applyExplosionDecay(slab, LootPool.builder()
+                .rolls(exactly(1))
+                .with(item(drop, exactly(count))
+                        .apply(applyStateCondition(slab, SlabBlock.TYPE, SlabType.DOUBLE, SetCountLootFunction.builder(exactly(count * 2)))))));
     }
 
     private LootTable.Builder fruitLeavesDrops(Block leaves) {
         return LootTable.builder()
             .pool(LootPool.builder()
-                    .rolls(ConstantLootNumberProvider.create(1))
-                    .with(ItemEntry.builder(leaves).conditionally(WITH_SILK_TOUCH_OR_SHEARS))
-            )
-            .pool(LootPool.builder()
-                    .rolls(ConstantLootNumberProvider.create(1))
-                    .conditionally(WITHOUT_SILK_TOUCH_NOR_SHEARS)
-                    .with(
-                            applyExplosionDecay(leaves, ItemEntry.builder(Items.STICK)
-                                .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 2)))
-                            )
-                            .conditionally(TableBonusLootCondition.builder(Enchantments.FORTUNE, LEAVES_STICK_DROP_CHANCE))
-                    )
-            );
+                .rolls(exactly(1))
+                .with(ItemEntry.builder(leaves).conditionally(WITH_SILK_TOUCH_OR_SHEARS)))
+            .pool(applyExplosionDecay(leaves, LootPool.builder()
+                .rolls(exactly(1))
+                .conditionally(WITHOUT_SILK_TOUCH_NOR_SHEARS)
+                .with(item(Items.STICK, between(1, 2)).conditionally(TableBonusLootCondition.builder(Enchantments.FORTUNE, LEAVES_STICK_DROP_CHANCE)))));
+    }
+
+    private LootTable.Builder hullDrops(Block hull, ItemConvertible inner, ItemConvertible outer) {
+        return LootTable.builder()
+            .pool(addSurvivesExplosionCondition(hull, LootPool.builder()
+                .rolls(exactly(1))
+                .with(item(hull, exactly(2)).conditionally(WITHOUT_SILK_TOUCH))
+                .with(item(inner, exactly(2)).conditionally(WITHOUT_SILK_TOUCH))
+                .with(item(outer, exactly(2)).conditionally(WITH_SILK_TOUCH))));
+    }
+
+    private LootTable.Builder edibleBlockDrops(Block block, ItemConvertible drop) {
+        LootTable.Builder builder = LootTable.builder();
+        for (BooleanProperty segment : EdibleBlock.SEGMENTS) {
+            builder
+                .pool(addSurvivesExplosionCondition(block, LootPool.builder()
+                    .rolls(exactly(1))
+                        .with(applyStateCondition(block, segment, true, ItemEntry.builder(drop)))));
+        }
+        return builder;
     }
 
     private LootTable.Builder pieDrops(Block block, Item drop, Item stomped) {
-        return LootTable.builder().pool(LootPool.builder()
-            .rolls(ConstantLootNumberProvider.create(1)).conditionally(WITH_SILK_TOUCH)
-            .with(addStateCondition(block, PieBlock.STOMPED, false, applyExplosionDecay(block, ItemEntry.builder(drop))))
-            .with(addStateCondition(block, PieBlock.STOMPED, true, applyExplosionDecay(block, ItemEntry.builder(stomped))))
-        );
+        return LootTable.builder()
+            .pool(applyExplosionDecay(block, LootPool.builder()
+                .rolls(exactly(1)).conditionally(WITH_SILK_TOUCH)
+                .with(applyStateCondition(block, PieBlock.STOMPED, false, ItemEntry.builder(drop)))
+                .with(applyStateCondition(block, PieBlock.STOMPED, true, ItemEntry.builder(stomped)))));
     }
 
-    public static <T extends LootConditionConsumingBuilder<T>, P extends Comparable<P> & StringIdentifiable> T addStateCondition(Block block,
-            Property<P> property, P value,
-            LootConditionConsumingBuilder<T> builder) {
+    private LootTable.Builder shellDrops(Block block, Item shell) {
+        return LootTable.builder()
+            .pool(applyExplosionDecay(block, LootPool.builder()
+                .rolls(exactly(1))
+                .with(ItemEntry.builder(shell))
+                    .apply(ShellsBlock.COUNT.getValues(), count -> applyStateCondition(block, ShellsBlock.COUNT, count, SetCountLootFunction.builder(exactly(count))))
+                    .apply(FORTUNE_BONUS)));
+    }
+
+
+    public LootTable.Builder fortuneBonusDrops(ItemConvertible drop) {
+        return LootTable.builder().pool(addSurvivesExplosionCondition(drop, LootPool.builder()
+                .rolls(exactly(1))
+                .with(ItemEntry.builder(drop).apply(FORTUNE_BONUS))));
+    }
+
+    public static ConstantLootNumberProvider exactly(float n) {
+        return ConstantLootNumberProvider.create(n);
+    }
+
+    public static UniformLootNumberProvider between(float from, float to) {
+        return UniformLootNumberProvider.create(from, to);
+    }
+
+    public static ItemEntry.Builder<?> item(ItemConvertible item, LootNumberProvider count) {
+        return ItemEntry.builder(item).apply(SetCountLootFunction.builder(count));
+    }
+
+    public static <T extends LootConditionConsumingBuilder<T>, P extends Comparable<P> & StringIdentifiable> T applyStateCondition(Block block,
+            Property<P> property, P value, LootConditionConsumingBuilder<T> builder) {
         return builder.conditionally(BlockStatePropertyLootCondition.builder(block).properties(StatePredicate.Builder.create().exactMatch(property, value)));
     }
 
-    public static <T extends LootConditionConsumingBuilder<T>> T addStateCondition(Block block,
-            BooleanProperty property, boolean value,
-            LootConditionConsumingBuilder<T> builder) {
+    public static <T extends LootConditionConsumingBuilder<T>> T applyStateCondition(Block block,
+            BooleanProperty property, boolean value, LootConditionConsumingBuilder<T> builder) {
+        return builder.conditionally(BlockStatePropertyLootCondition.builder(block).properties(StatePredicate.Builder.create().exactMatch(property, value)));
+    }
+
+    public static <T extends LootConditionConsumingBuilder<T>> T applyStateCondition(Block block,
+            IntProperty property, int value, LootConditionConsumingBuilder<T> builder) {
         return builder.conditionally(BlockStatePropertyLootCondition.builder(block).properties(StatePredicate.Builder.create().exactMatch(property, value)));
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestAdditionsLootTableProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestAdditionsLootTableProvider.java
index 266c73a7..051875e7 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestAdditionsLootTableProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestAdditionsLootTableProvider.java
@@ -24,6 +24,10 @@ public class UChestAdditionsLootTableProvider extends SimpleFabricLootTableProvi
         super(dataOutput, LootContextTypes.CHEST);
     }
 
+    @Override
+    public String getName() {
+        return super.getName() + " Additions";
+    }
 
     @Override
     public void accept(BiConsumer<Identifier, Builder> exporter) {
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestLootTableProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestLootTableProvider.java
new file mode 100644
index 00000000..0ea207e9
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestLootTableProvider.java
@@ -0,0 +1,56 @@
+package com.minelittlepony.unicopia.datagen.providers.loot;
+
+import java.util.function.BiConsumer;
+
+import com.minelittlepony.unicopia.Unicopia;
+import com.minelittlepony.unicopia.entity.effect.UPotions;
+
+import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
+import net.fabricmc.fabric.api.datagen.v1.provider.SimpleFabricLootTableProvider;
+import net.minecraft.item.Items;
+import net.minecraft.loot.LootPool;
+import net.minecraft.loot.LootTable;
+import net.minecraft.loot.LootTable.Builder;
+import net.minecraft.loot.context.LootContextTypes;
+import net.minecraft.loot.entry.ItemEntry;
+import net.minecraft.loot.function.SetCountLootFunction;
+import net.minecraft.loot.function.SetPotionLootFunction;
+import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
+import net.minecraft.loot.provider.number.UniformLootNumberProvider;
+import net.minecraft.potion.Potion;
+import net.minecraft.util.Identifier;
+
+public class UChestLootTableProvider extends SimpleFabricLootTableProvider {
+    public UChestLootTableProvider(FabricDataOutput output) {
+        super(output, LootContextTypes.CHEST);
+    }
+
+    @Override
+    public void accept(BiConsumer<Identifier, Builder> exporter) {
+        exporter.accept(Unicopia.id("chests/changeling_hive_trap"), LootTable.builder()
+                .pool(LootPool.builder()
+                .rolls(ConstantLootNumberProvider.create(6))
+
+                .with(createTippedArrowEntry(UPotions.MORPH_EARTH_PONY.shortEffect(), 3))
+                .with(createTippedArrowEntry(UPotions.MORPH_UNICORN.shortEffect(), 1))
+                .with(createTippedArrowEntry(UPotions.MORPH_PEGASUS.shortEffect(), 1))
+                .with(createTippedArrowEntry(UPotions.MORPH_BAT.shortEffect(), 1))
+                .with(createTippedArrowEntry(UPotions.MORPH_KIRIN.shortEffect(), 1))
+                .with(createTippedArrowEntry(UPotions.MORPH_HIPPOGRIFF.shortEffect(), 1))
+
+                .with(createTippedArrowEntry(UPotions.MORPH_EARTH_PONY.longEffect(), 5))
+                .with(createTippedArrowEntry(UPotions.MORPH_UNICORN.longEffect(), 2))
+                .with(createTippedArrowEntry(UPotions.MORPH_PEGASUS.longEffect(), 2))
+                .with(createTippedArrowEntry(UPotions.MORPH_BAT.longEffect(), 2))
+                .with(createTippedArrowEntry(UPotions.MORPH_KIRIN.longEffect(), 2))
+                .with(createTippedArrowEntry(UPotions.MORPH_HIPPOGRIFF.longEffect(), 2))
+        ));
+    }
+
+    private static ItemEntry.Builder<?> createTippedArrowEntry(Potion potion, int weight) {
+        return ItemEntry.builder(Items.TIPPED_ARROW)
+                .weight(weight)
+                .apply(SetPotionLootFunction.builder(potion))
+                .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(3, 9)));
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UEntityLootTableProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UEntityLootTableProvider.java
new file mode 100644
index 00000000..bdcc6942
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UEntityLootTableProvider.java
@@ -0,0 +1,63 @@
+package com.minelittlepony.unicopia.datagen.providers.loot;
+
+import java.util.function.BiConsumer;
+
+import com.minelittlepony.unicopia.UTags;
+import com.minelittlepony.unicopia.entity.mob.UEntities;
+import com.minelittlepony.unicopia.item.UItems;
+
+import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
+import net.fabricmc.fabric.api.datagen.v1.provider.SimpleFabricLootTableProvider;
+import net.minecraft.entity.EntityType;
+import net.minecraft.loot.LootPool;
+import net.minecraft.loot.LootTable;
+import net.minecraft.loot.LootTable.Builder;
+import net.minecraft.loot.condition.RandomChanceLootCondition;
+import net.minecraft.loot.context.LootContextTypes;
+import net.minecraft.loot.entry.ItemEntry;
+import net.minecraft.loot.entry.TagEntry;
+import net.minecraft.loot.function.LootingEnchantLootFunction;
+import net.minecraft.loot.function.SetCountLootFunction;
+import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
+import net.minecraft.loot.provider.number.UniformLootNumberProvider;
+import net.minecraft.util.Identifier;
+
+public class UEntityLootTableProvider extends SimpleFabricLootTableProvider {
+    public UEntityLootTableProvider(FabricDataOutput output) {
+        super(output, LootContextTypes.ENTITY);
+    }
+
+    @Override
+    public void accept(BiConsumer<Identifier, Builder> exporter) {
+        generate((type, builder) -> exporter.accept(EntityType.getId(type).withPrefixedPath("entities/"), builder));
+    }
+
+    protected void generate(BiConsumer<EntityType<?>, Builder> exporter) {
+        exporter.accept(UEntities.BUTTERFLY, LootTable.builder()
+                .pool(LootPool.builder()
+                .rolls(ConstantLootNumberProvider.create(1))
+                .with(ItemEntry.builder(UItems.BUTTERFLY)
+                    .apply(LootingEnchantLootFunction.builder(UniformLootNumberProvider.create(0, 1))))));
+        exporter.accept(UEntities.STORM_CLOUD, LootTable.builder()
+                .pool(LootPool.builder()
+                .rolls(ConstantLootNumberProvider.create(1))
+                .with(ItemEntry.builder(UItems.CLOUD_LUMP)
+                    .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(6, 12)))
+                    .apply(LootingEnchantLootFunction.builder(UniformLootNumberProvider.create(0, 1))))));
+        exporter.accept(UEntities.LOOT_BUG, LootTable.builder()
+                .pool(LootPool.builder()
+                .rolls(ConstantLootNumberProvider.create(1))
+                .with(TagEntry.builder(UTags.Items.LOOT_BUG_COMMON_DROPS)
+                        .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(6, 12)))
+                        .apply(LootingEnchantLootFunction.builder(UniformLootNumberProvider.create(0, 3))))
+                .with(TagEntry.builder(UTags.Items.LOOT_BUG_RARE_DROPS)
+                        .conditionally(RandomChanceLootCondition.builder(0.25F))
+                        .apply(SetCountLootFunction.builder(UniformLootNumberProvider.create(1, 3)))
+                        .apply(LootingEnchantLootFunction.builder(UniformLootNumberProvider.create(0, 6))))
+                .with(TagEntry.builder(UTags.Items.LOOT_BUG_EPIC_DROPS)
+                        .conditionally(RandomChanceLootCondition.builder(0.1F))
+                        .apply(SetCountLootFunction.builder(ConstantLootNumberProvider.create(1)))
+                        .apply(LootingEnchantLootFunction.builder(UniformLootNumberProvider.create(0, 2))))
+        ));
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java
index 6198be9f..a576582d 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UBlockTagProvider.java
@@ -214,5 +214,9 @@ public class UBlockTagProvider extends FabricTagProvider.BlockTagProvider {
     private void populateConventionalTags() {
         getOrCreateTagBuilder(UConventionalTags.Blocks.CONCRETES).add(Arrays.stream(DyeColor.values()).map(i -> Registries.BLOCK.get(new Identifier(i.getName() + "_concrete"))).toArray(Block[]::new));
         getOrCreateTagBuilder(UConventionalTags.Blocks.CONCRETE_POWDERS).add(Arrays.stream(DyeColor.values()).map(i -> Registries.BLOCK.get(new Identifier(i.getName() + "_concrete_powder"))).toArray(Block[]::new));
+        getOrCreateTagBuilder(UConventionalTags.Blocks.GLAZED_TERRACOTTAS).add(Arrays.stream(DyeColor.values()).map(i -> Registries.BLOCK.get(new Identifier(i.getName() + "_glazed_terracotta"))).toArray(Block[]::new));
+        getOrCreateTagBuilder(UConventionalTags.Blocks.CORAL_BLOCKS).add(Blocks.TUBE_CORAL_BLOCK, Blocks.BRAIN_CORAL_BLOCK, Blocks.BUBBLE_CORAL_BLOCK, Blocks.FIRE_CORAL_BLOCK, Blocks.HORN_CORAL_BLOCK);
+        getOrCreateTagBuilder(UConventionalTags.Blocks.CORAL_FANS).add(Blocks.TUBE_CORAL_FAN, Blocks.BRAIN_CORAL_FAN, Blocks.BUBBLE_CORAL_FAN, Blocks.FIRE_CORAL_FAN, Blocks.HORN_CORAL_FAN);
+        getOrCreateTagBuilder(UConventionalTags.Blocks.CORALS).add(Blocks.TUBE_CORAL, Blocks.BRAIN_CORAL, Blocks.BUBBLE_CORAL, Blocks.FIRE_CORAL, Blocks.HORN_CORAL);
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
index 30b0c9ca..f54b7751 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
@@ -102,6 +102,7 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
             .forceAddTag(UTags.Items.CLOUD_STAIRS)
             .forceAddTag(UTags.Items.CLOUD_BLOCKS)
             .add(UItems.CLOUD_LUMP);
+        getOrCreateTagBuilder(UTags.Items.CONTAINER_WITH_LOVE).add(UItems.LOVE_BOTTLE, UItems.LOVE_BUCKET, UItems.LOVE_MUG);
         getOrCreateTagBuilder(UTags.Items.HAS_NO_TRAITS).add(
                 Items.AIR, Items.SPAWNER, Items.STRUCTURE_VOID, Items.STRUCTURE_BLOCK,
                 Items.COMMAND_BLOCK, Items.CHAIN_COMMAND_BLOCK, Items.REPEATING_COMMAND_BLOCK,
@@ -109,16 +110,23 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
                 Items.DEBUG_STICK, Items.COMMAND_BLOCK_MINECART,
                 UItems.PLUNDER_VINE
         ).forceAddTag(UTags.Items.BADGES);
-        getOrCreateTagBuilder(UTags.Items.LOOT_BUG_HIGH_VALUE_DROPS).add(
+        getOrCreateTagBuilder(UTags.Items.LOOT_BUG_COMMON_DROPS).forceAddTag(ConventionalItemTags.NUGGETS);
+        getOrCreateTagBuilder(UTags.Items.LOOT_BUG_RARE_DROPS).add(
                     Items.DIAMOND, Items.GOLDEN_APPLE, Items.GOLDEN_CARROT,
                     Items.GOLDEN_HELMET, Items.GOLDEN_BOOTS, Items.GOLDEN_LEGGINGS, Items.GOLDEN_CHESTPLATE,
                     Items.GOLDEN_HORSE_ARMOR,
                     Items.GOLDEN_PICKAXE, Items.GOLDEN_SHOVEL, Items.GOLDEN_AXE, Items.GOLDEN_SWORD, Items.GOLDEN_HOE,
                     UItems.GOLDEN_HORSE_SHOE, UItems.GOLDEN_POLEARM, UItems.GOLDEN_FEATHER, UItems.GOLDEN_WING,
                     UItems.GOLDEN_OAK_SEEDS
-            ).forceAddTag(ConventionalItemTags.NUGGETS)
-            .forceAddTag(ConventionalItemTags.GOLD_INGOTS).forceAddTag(ConventionalItemTags.RAW_GOLD_ORES).forceAddTag(ConventionalItemTags.RAW_GOLD_BLOCKS)
+            ).forceAddTag(ConventionalItemTags.GOLD_INGOTS).forceAddTag(ConventionalItemTags.RAW_GOLD_ORES).forceAddTag(ConventionalItemTags.RAW_GOLD_BLOCKS)
             .addOptionalTag(new Identifier("farmersdelight:golden_knife"));
+        getOrCreateTagBuilder(UTags.Items.LOOT_BUG_EPIC_DROPS).add(
+                Items.DIAMOND_BLOCK,
+                Items.DIAMOND_HELMET, Items.DIAMOND_BOOTS, Items.DIAMOND_LEGGINGS, Items.DIAMOND_CHESTPLATE,
+                Items.DIAMOND_HORSE_ARMOR,
+                Items.DIAMOND_PICKAXE, Items.DIAMOND_SHOVEL, Items.DIAMOND_AXE, Items.DIAMOND_SWORD, Items.DIAMOND_HOE,
+                UItems.DIAMOND_POLEARM
+        ).forceAddTag(UTags.Items.BADGES).forceAddTag(ConventionalItemTags.GOLD_INGOTS);
 
         exportFarmersDelightItems();
     }
@@ -154,6 +162,10 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
     private void exportConventionalTags() {
         copy(UConventionalTags.Blocks.CONCRETES, UConventionalTags.Items.CONCRETES);
         copy(UConventionalTags.Blocks.CONCRETE_POWDERS, UConventionalTags.Items.CONCRETE_POWDERS);
+        copy(UConventionalTags.Blocks.GLAZED_TERRACOTTAS, UConventionalTags.Items.GLAZED_TERRACOTTAS);
+        copy(UConventionalTags.Blocks.CORAL_BLOCKS, UConventionalTags.Items.CORAL_BLOCKS);
+        copy(UConventionalTags.Blocks.CORAL_FANS, UConventionalTags.Items.CORAL_FANS);
+        copy(UConventionalTags.Blocks.CORALS, UConventionalTags.Items.CORALS);
         getOrCreateTagBuilder(UConventionalTags.Items.ACORNS).add(UItems.ACORN);
         getOrCreateTagBuilder(UConventionalTags.Items.APPLES)
             .add(Items.APPLE, Items.GOLDEN_APPLE, Items.ENCHANTED_GOLDEN_APPLE, UItems.ROTTEN_APPLE)
@@ -161,8 +173,34 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
             .addOptionalTag(new Identifier("c", "pyrite_apples")) // no idea which mod add pyrite apples
         ;
         getOrCreateTagBuilder(UConventionalTags.Items.BANANAS).add(UItems.BANANA);
+        getOrCreateTagBuilder(UConventionalTags.Items.RAW_FISH).add(Items.COD, Items.SALMON, Items.PUFFERFISH, Items.TROPICAL_FISH).addOptionalTag(new Identifier("c", "mollusks"));
         getOrCreateTagBuilder(UConventionalTags.Items.COOKED_FISH).add(Items.COOKED_COD, Items.COOKED_SALMON);
+        getOrCreateTagBuilder(UConventionalTags.Items.COOKED_MEAT)
+            .add(Items.COOKED_PORKCHOP, Items.COOKED_BEEF, Items.COOKED_MUTTON, Items.COOKED_RABBIT, Items.COOKED_CHICKEN, Items.RABBIT_STEW)
+            .addOptionalTag(new Identifier("c", "cooked_bacon"))
+            .addOptionalTag(new Identifier("c", "cooked_beef"))
+            .addOptionalTag(new Identifier("c", "cooked_chicken"))
+            .addOptionalTag(new Identifier("c", "cooked_mutton"))
+            .addOptionalTag(new Identifier("c", "cooked_pork"))
+            .addOptionalTag(new Identifier("c", "fried_chickens"))
+            .addOptionalTag(new Identifier("c", "hamburgers"))
+            .addOptionalTag(new Identifier("c", "pork_and_beans"))
+            .addOptionalTag(new Identifier("c", "pork_jerkies"))
+            .addOptionalTag(new Identifier("c", "protien"));
+        getOrCreateTagBuilder(UConventionalTags.Items.RAW_MEAT)
+            .add(Items.PORKCHOP, Items.BEEF, Items.MUTTON, Items.RABBIT, Items.CHICKEN)
+            .addOptionalTag(new Identifier("c", "raw_bacon"))
+            .addOptionalTag(new Identifier("c", "raw_beef"))
+            .addOptionalTag(new Identifier("c", "raw_chicken"))
+            .addOptionalTag(new Identifier("c", "raw_mutton"))
+            .addOptionalTag(new Identifier("c", "raw_pork"))
+            .addOptionalTag(new Identifier("c", "lemon_chickens"));
+        getOrCreateTagBuilder(UConventionalTags.Items.ROTTEN_MEAT).add(Items.ROTTEN_FLESH);
+        getOrCreateTagBuilder(UConventionalTags.Items.COOKED_INSECT).add(Items.FERMENTED_SPIDER_EYE);
+        getOrCreateTagBuilder(UConventionalTags.Items.RAW_INSECT).add(Items.SPIDER_EYE, UItems.BUTTERFLY, UItems.WHEAT_WORMS, UBlocks.WORM_BLOCK.asItem());
+        getOrCreateTagBuilder(UConventionalTags.Items.WORMS).add(UItems.WHEAT_WORMS);
         getOrCreateTagBuilder(UConventionalTags.Items.STICKS).add(Items.STICK);
+        getOrCreateTagBuilder(UConventionalTags.Items.ROCKS).add(UItems.ROCK);
         getOrCreateTagBuilder(UConventionalTags.Items.PINECONES).add(UItems.PINECONE);
         getOrCreateTagBuilder(UConventionalTags.Items.PINEAPPLES).add(UItems.PINEAPPLE);
         getOrCreateTagBuilder(UConventionalTags.Items.MANGOES).add(UItems.MANGO);
@@ -184,9 +222,18 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
 
     private void exportFarmersDelightItems() {
         getOrCreateTagBuilder(UTags.Items.COOLS_OFF_KIRINS)
-            .addOptional(new Identifier("farmersdelight:melon_popsicle"))
-            .addOptional(new Identifier("farmersdelight:melon_juice"));
-        getOrCreateTagBuilder(TagKey.of(RegistryKeys.ITEM, new Identifier("farmersdelight:cabbage_roll_ingredients"))).add(UItems.OATS, UItems.ROCK, UItems.WHEAT_WORMS);
-        getOrCreateTagBuilder(TagKey.of(RegistryKeys.ITEM, new Identifier("farmersdelight:comfort_foods"))).add(UItems.OATMEAL, UItems.ROCK_STEW, UItems.MUFFIN);
+            .addOptional(new Identifier("farmersdelight", "melon_popsicle"))
+            .addOptional(new Identifier("farmersdelight", "melon_juice"));
+        getOrCreateTagBuilder(TagKey.of(RegistryKeys.ITEM, new Identifier("farmersdelight", "cabbage_roll_ingredients"))).add(UItems.OATS, UItems.ROCK, UItems.WHEAT_WORMS);
+        getOrCreateTagBuilder(TagKey.of(RegistryKeys.ITEM, new Identifier("farmersdelight", "comfort_foods"))).add(UItems.OATMEAL, UItems.ROCK_STEW, UItems.MUFFIN);
+        getOrCreateTagBuilder(UConventionalTags.Items.RAW_FISH)
+            .addOptional(new Identifier("farmersdelight", "cod_roll"))
+            .addOptional(new Identifier("farmersdelight", "salmon_roll"))
+            .addOptional(new Identifier("farmersdelight", "cod_slice"))
+            .addOptional(new Identifier("farmersdelight", "salmon_slice"));
+        getOrCreateTagBuilder(UConventionalTags.Items.COOKED_FISH)
+            .addOptional(new Identifier("farmersdelight", "fish_stew"))
+            .addOptional(new Identifier("farmersdelight", "baked_cod_stew"))
+            .addOptional(new Identifier("farmersdelight", "grilled_salmon"));
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java b/src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java
index a71c8499..4638ed04 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java
@@ -5,11 +5,11 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import org.jetbrains.annotations.Nullable;
-
 import com.minelittlepony.unicopia.entity.player.Pony;
 import com.minelittlepony.unicopia.item.ItemDuck;
 import com.mojang.datafixers.util.Pair;
@@ -19,13 +19,11 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
 import net.minecraft.client.item.TooltipContext;
 import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.item.FoodComponent;
-import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
 import net.minecraft.network.PacketByteBuf;
-import net.minecraft.registry.RegistryKeys;
-import net.minecraft.registry.tag.TagKey;
 import net.minecraft.text.Text;
 import net.minecraft.util.Formatting;
+import net.minecraft.util.Identifier;
 import net.minecraft.util.UseAction;
 
 public record DietProfile(
@@ -40,12 +38,34 @@ public record DietProfile(
                 Codec.FLOAT.fieldOf("default_multiplier").forGetter(DietProfile::defaultMultiplier),
                 Codec.FLOAT.fieldOf("foraging_multiplier").forGetter(DietProfile::foragingMultiplier),
                 Codec.list(Multiplier.CODEC).fieldOf("multipliers").forGetter(DietProfile::multipliers),
-                Codec.list(Effect.CODEC).fieldOf("effects").forGetter(DietProfile::effects),
+                Codec.list(Effect.PROFILE_CODEC).fieldOf("effects").forGetter(DietProfile::effects),
                 Effect.CODEC.optionalFieldOf("default_effect").forGetter(DietProfile::defaultEffect)
     ).apply(instance, DietProfile::new));
 
     public DietProfile(PacketByteBuf buffer) {
-        this(buffer.readFloat(), buffer.readFloat(), buffer.readList(Multiplier::new), buffer.readList(Effect::new), buffer.readOptional(Effect::new));
+        this(buffer.readFloat(), buffer.readFloat(),
+                buffer.readList(Multiplier::new),
+                buffer.readList(b -> new Effect(b, FoodGroupKey.LOOKUP)),
+                buffer.readOptional(b -> new Effect(b, FoodGroupKey.LOOKUP))
+        );
+    }
+
+    public void validate(Consumer<String> issues, Predicate<Identifier> foodGroupExists) {
+        multipliers.stream().flatMap(i -> i.tags().stream()).forEach(key -> {
+            if (!foodGroupExists.test(key.id())) {
+                issues.accept("Multiplier referenced unknown food group: " + key.id());
+            }
+        });
+        effects.stream().flatMap(i -> i.tags().stream()).forEach(key -> {
+            if (!foodGroupExists.test(key.id())) {
+                issues.accept("Override defined for unknown food group: " + key.id());
+            }
+        });
+        defaultEffect.stream().flatMap(i -> i.tags().stream()).forEach(key -> {
+            if (!foodGroupExists.test(key.id())) {
+                issues.accept("Default override defined for unknown food group: " + key.id());
+            }
+        });
     }
 
     public void toBuffer(PacketByteBuf buffer) {
@@ -145,11 +165,11 @@ public record DietProfile(
     }
 
     public record Multiplier(
-            Set<TagKey<Item>> tags,
+            Set<FoodGroupKey> tags,
             float hunger,
             float saturation
     ) implements Predicate<ItemStack> {
-        public static final Codec<Set<TagKey<Item>>> TAGS_CODEC = Codec.list(TagKey.unprefixedCodec(RegistryKeys.ITEM)).xmap(
+        public static final Codec<Set<FoodGroupKey>> TAGS_CODEC = FoodGroupKey.CODEC.listOf().xmap(
                 l -> l.stream().distinct().collect(Collectors.toSet()),
                 set -> new ArrayList<>(set)
         );
@@ -160,12 +180,12 @@ public record DietProfile(
         ).apply(instance, Multiplier::new));
 
         public Multiplier(PacketByteBuf buffer) {
-            this(buffer.readCollection(HashSet::new, p -> TagKey.of(RegistryKeys.ITEM, p.readIdentifier())), buffer.readFloat(), buffer.readFloat());
+            this(buffer.readCollection(HashSet::new, p -> FoodGroupKey.LOOKUP.apply(p.readIdentifier())), buffer.readFloat(), buffer.readFloat());
         }
 
         @Override
         public boolean test(ItemStack stack) {
-            return tags.stream().anyMatch(tag -> stack.isIn(tag));
+            return tags.stream().anyMatch(tag -> tag.contains(stack));
         }
 
         public void toBuffer(PacketByteBuf buffer) {
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/DietsLoader.java b/src/main/java/com/minelittlepony/unicopia/diet/DietsLoader.java
index 963efb77..f517c7a2 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/DietsLoader.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/DietsLoader.java
@@ -1,12 +1,9 @@
 package com.minelittlepony.unicopia.diet;
 
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
-
 import org.slf4j.Logger;
 
 import com.google.gson.JsonElement;
@@ -36,9 +33,24 @@ public class DietsLoader implements IdentifiableResourceReloadListener {
             Profiler prepareProfiler, Profiler applyProfiler,
             Executor prepareExecutor, Executor applyExecutor) {
 
-        var dietsLoadTask = loadData(manager, prepareExecutor, "diets/races").thenApplyAsync(data -> {
+        CompletableFuture<Map<Identifier, Effect>> foodGroupsFuture = CompletableFuture.supplyAsync(() -> {
+            Map<Identifier, Effect> foodGroups = new HashMap<>();
+            for (var group : loadData(manager, prepareExecutor, "diets/food_groups").entrySet()) {
+                try {
+                    Effect.CODEC.parse(JsonOps.INSTANCE, group.getValue())
+                        .resultOrPartial(error -> LOGGER.error("Could not load food group {}: {}", group.getKey(), error))
+                        .ifPresent(value -> {
+                            foodGroups.put(group.getKey(), value);
+                        });
+                } catch (Throwable t) {
+                    LOGGER.error("Could not load food effects {}", group.getKey(), t);
+                }
+            }
+            return foodGroups;
+        }, prepareExecutor);
+        CompletableFuture<Map<Race, DietProfile>> profilesFuture = CompletableFuture.supplyAsync(() -> {
             Map<Race, DietProfile> profiles = new HashMap<>();
-            for (var entry : data.entrySet()) {
+            for (var entry : loadData(manager, prepareExecutor, "diets/races").entrySet()) {
                 Identifier id = entry.getKey();
                 try {
                     Race.REGISTRY.getOrEmpty(id).ifPresentOrElse(race -> {
@@ -53,33 +65,26 @@ public class DietsLoader implements IdentifiableResourceReloadListener {
             return profiles;
         }, prepareExecutor);
 
-        var effectsLoadTask = loadData(manager, prepareExecutor, "diets/food_effects").thenApplyAsync(data -> data.entrySet().stream()
-                    .map(entry -> {
-                        try {
-                        return Effect.CODEC.parse(JsonOps.INSTANCE, entry.getValue())
-                                .resultOrPartial(error -> LOGGER.error("Could not load food effect {}: {}", entry.getKey(), error));
-                        } catch (Throwable t) {
-                            LOGGER.error("Could not load food effects {}", entry.getKey(), t);
-                        }
-                        return Optional.<Effect>empty();
-                    })
-                            .filter(Optional::isPresent)
-                            .map(Optional::get)
-                            .toList(), prepareExecutor);
-
-        return CompletableFuture.allOf(dietsLoadTask, effectsLoadTask).thenCompose(sync::whenPrepared).thenRunAsync(() -> {
-            PonyDiets.load(new PonyDiets(
-                    dietsLoadTask.getNow(Map.of()),
-                    effectsLoadTask.getNow(List.of())
-            ));
+        return CompletableFuture.allOf(foodGroupsFuture, profilesFuture).thenCompose(sync::whenPrepared).thenAcceptAsync(v -> {
+            var profiles = profilesFuture.getNow(Map.of());
+            var foodGroups = foodGroupsFuture.getNow(Map.of());
+            profiles.entrySet().removeIf(entry -> {
+                StringBuilder issueList = new StringBuilder();
+                entry.getValue().validate(issue -> {
+                    issueList.append(System.lineSeparator()).append(issue);
+                }, foodGroups::containsKey);
+                if (!issueList.isEmpty()) {
+                    LOGGER.error("Could not load diet profile {}. Caused by {}", entry.getKey(), issueList.toString());
+                }
+                return issueList.isEmpty();
+            });
+            PonyDiets.load(new PonyDiets(profiles, foodGroups));
         }, applyExecutor);
     }
 
-    private static CompletableFuture<Map<Identifier, JsonElement>> loadData(ResourceManager manager, Executor prepareExecutor, String path) {
-        return CompletableFuture.supplyAsync(() -> {
-            Map<Identifier, JsonElement> results = new HashMap<>();
-            JsonDataLoader.load(manager, path, Resources.GSON, results);
-            return results;
-        });
+    private static Map<Identifier, JsonElement> loadData(ResourceManager manager, Executor prepareExecutor, String path) {
+        Map<Identifier, JsonElement> results = new HashMap<>();
+        JsonDataLoader.load(manager, path, Resources.GSON, results);
+        return results;
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/Effect.java b/src/main/java/com/minelittlepony/unicopia/diet/Effect.java
index 9b7ba05d..b8ce1338 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/Effect.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/Effect.java
@@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.diet;
 
 import java.util.List;
 import java.util.Optional;
+import java.util.function.Function;
 import java.util.function.Predicate;
 
 import com.minelittlepony.unicopia.entity.player.Pony;
@@ -10,30 +11,33 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
 
 import net.minecraft.client.item.TooltipContext;
 import net.minecraft.item.FoodComponent;
-import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
 import net.minecraft.network.PacketByteBuf;
-import net.minecraft.registry.RegistryKeys;
-import net.minecraft.registry.tag.TagKey;
 import net.minecraft.text.Text;
 import net.minecraft.util.Formatting;
+import net.minecraft.util.Identifier;
 import net.minecraft.util.UseAction;
 import net.minecraft.util.Util;
 
 public record Effect(
-        List<TagKey<Item>> tags,
+        List<FoodGroupKey> tags,
         Optional<FoodComponent> foodComponent,
         Ailment ailment
 ) implements Predicate<ItemStack> {
     public static final Effect EMPTY = new Effect(List.of(), Optional.empty(), Ailment.EMPTY);
     public static final Codec<Effect> CODEC = RecordCodecBuilder.create(instance -> instance.group(
-            TagKey.unprefixedCodec(RegistryKeys.ITEM).listOf().fieldOf("tags").forGetter(Effect::tags),
+            FoodGroupKey.TAG_CODEC.listOf().fieldOf("tags").forGetter(Effect::tags),
+            FoodAttributes.CODEC.optionalFieldOf("food_component").forGetter(Effect::foodComponent),
+            Ailment.CODEC.fieldOf("ailment").forGetter(Effect::ailment)
+    ).apply(instance, Effect::new));
+    public static final Codec<Effect> PROFILE_CODEC = RecordCodecBuilder.create(instance -> instance.group(
+            FoodGroupKey.CODEC.listOf().fieldOf("tags").forGetter(Effect::tags),
             FoodAttributes.CODEC.optionalFieldOf("food_component").forGetter(Effect::foodComponent),
             Ailment.CODEC.fieldOf("ailment").forGetter(Effect::ailment)
     ).apply(instance, Effect::new));
 
-    public Effect(PacketByteBuf buffer) {
-        this(buffer.readList(b -> TagKey.of(RegistryKeys.ITEM, b.readIdentifier())), buffer.readOptional(FoodAttributes::read), new Ailment(buffer));
+    public Effect(PacketByteBuf buffer, Function<Identifier, FoodGroupKey> lookup) {
+        this(buffer.readList(b -> lookup.apply(b.readIdentifier())), buffer.readOptional(FoodAttributes::read), new Ailment(buffer));
     }
 
     public void afflict(Pony pony, ItemStack stack) {
@@ -43,7 +47,7 @@ public record Effect(
     public void appendTooltip(ItemStack stack, List<Text> tooltip, TooltipContext context) {
         int size = tooltip.size();
         tags.forEach(tag -> {
-            if (stack.isIn(tag)) {
+            if (tag.contains(stack)) {
                 tooltip.add(Text.literal(" ").append(Text.translatable(Util.createTranslationKey("tag", tag.id()))).formatted(Formatting.GRAY));
             }
         });
@@ -71,6 +75,6 @@ public record Effect(
 
     @Override
     public boolean test(ItemStack stack) {
-        return tags.stream().anyMatch(stack::isIn);
+        return tags.stream().anyMatch(tag -> tag.contains(stack));
     }
 }
\ No newline at end of file
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupKey.java b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupKey.java
new file mode 100644
index 00000000..efa49837
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupKey.java
@@ -0,0 +1,49 @@
+package com.minelittlepony.unicopia.diet;
+
+import java.util.function.Function;
+
+import com.mojang.serialization.Codec;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.registry.RegistryKeys;
+import net.minecraft.registry.tag.TagKey;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.Util;
+
+public interface FoodGroupKey {
+    Function<Identifier, FoodGroupKey> LOOKUP = Util.memoize(id -> {
+        return new FoodGroupKey() {
+            @Override
+            public Identifier id() {
+                return id;
+            }
+
+            @Override
+            public boolean contains(ItemStack stack) {
+                var group = PonyDiets.getEffect(id);
+                return group != null && group.test(stack);
+            }
+        };
+    });
+    Function<TagKey<Item>, FoodGroupKey> TAG_LOOKUP = Util.memoize(tag -> {
+        return new FoodGroupKey() {
+            @Override
+            public Identifier id() {
+                return tag.id();
+            }
+
+            @Override
+            public boolean contains(ItemStack stack) {
+                return stack.isIn(tag);
+            }
+        };
+    });
+    Function<Identifier, FoodGroupKey> TAG_ID_LOOKUP = id -> TAG_LOOKUP.apply(TagKey.of(RegistryKeys.ITEM, id));
+    Codec<FoodGroupKey> CODEC = Identifier.CODEC.xmap(LOOKUP, FoodGroupKey::id);
+    Codec<FoodGroupKey> TAG_CODEC = TagKey.unprefixedCodec(RegistryKeys.ITEM).xmap(TAG_LOOKUP, k -> TagKey.of(RegistryKeys.ITEM, k.id()));
+
+    Identifier id();
+
+    boolean contains(ItemStack stack);
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/PonyDiets.java b/src/main/java/com/minelittlepony/unicopia/diet/PonyDiets.java
index 3566bf06..e233871d 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/PonyDiets.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/PonyDiets.java
@@ -18,35 +18,41 @@ import net.minecraft.network.PacketByteBuf;
 import net.minecraft.text.Text;
 import net.minecraft.util.Formatting;
 import net.minecraft.util.Hand;
+import net.minecraft.util.Identifier;
 import net.minecraft.util.TypedActionResult;
 import net.minecraft.world.World;
 
 public class PonyDiets implements DietView {
     private final Map<Race, DietProfile> diets;
-    private final List<Effect> effects;
+    private final Map<Identifier, Effect> effects;
 
-    private static PonyDiets INSTANCE = new PonyDiets(Map.of(), List.of());
+    private static PonyDiets INSTANCE = new PonyDiets(Map.of(), Map.of());
 
     public static PonyDiets getInstance() {
         return INSTANCE;
     }
 
+    @Nullable
+    static Effect getEffect(Identifier id) {
+        return INSTANCE.effects.get(id);
+    }
+
     public static void load(PonyDiets diets) {
         INSTANCE = diets;
     }
 
-    PonyDiets(Map<Race, DietProfile> diets, List<Effect> effects) {
+    PonyDiets(Map<Race, DietProfile> diets, Map<Identifier, Effect> effects) {
         this.diets = diets;
         this.effects = effects;
     }
 
     public PonyDiets(PacketByteBuf buffer) {
-        this(buffer.readMap(b -> b.readRegistryValue(Race.REGISTRY), DietProfile::new), buffer.readList(Effect::new));
+        this(buffer.readMap(b -> b.readRegistryValue(Race.REGISTRY), DietProfile::new), buffer.readMap(PacketByteBuf::readIdentifier, b -> new Effect(b, FoodGroupKey.TAG_ID_LOOKUP)));
     }
 
     public void toBuffer(PacketByteBuf buffer) {
         buffer.writeMap(diets, (b, r) -> b.writeRegistryValue(Race.REGISTRY, r), (b, e) -> e.toBuffer(b));
-        buffer.writeCollection(effects, (b, e) -> e.toBuffer(b));
+        buffer.writeMap(effects, PacketByteBuf::writeIdentifier, (b, e) -> e.toBuffer(b));
     }
 
     private DietProfile getDiet(Pony pony) {
@@ -54,7 +60,7 @@ public class PonyDiets implements DietView {
     }
 
     Effect getEffects(ItemStack stack) {
-        return effects.stream().filter(effect -> effect.test(stack)).findFirst().orElse(Effect.EMPTY);
+        return effects.values().stream().filter(effect -> effect.test(stack)).findFirst().orElse(Effect.EMPTY);
     }
 
     private Effect getEffects(ItemStack stack, Pony pony) {
diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json
index 54133a8a..6f5978b0 100644
--- a/src/main/resources/assets/unicopia/lang/en_us.json
+++ b/src/main/resources/assets/unicopia/lang/en_us.json
@@ -622,17 +622,17 @@
   "tag.unicopia.food_types.rotten_meat": "Rotting Meat",
   "tag.unicopia.food_types.raw_meat": "Fresh Meat",
   "tag.unicopia.food_types.cooked_meat": "Prepared Meat",
-  "tag.unicopia.food_types.raw_fish": "Fresh Fish",
-  "tag.unicopia.food_types.cooked_fish": "Prepared Fish",
-  "tag.unicopia.food_types.raw_insect": "Bugs & Insects",
+  "tag.c.raw_fish": "Fresh Fish",
+  "tag.c.cooked_fish": "Prepared Fish",
+  "tag.c.raw_insect": "Bugs & Insects",
   "tag.unicopia.food_types.cooked_insect": "Cooked Bugs & Insects",
   "tag.unicopia.food_types.nuts_and_seeds": "Nuts & Seeds",
-  "tag.unicopia.food_types.love": "Love",
+  "tag.unicopia.container_with_love": "Love",
   "tag.unicopia.food_types.rocks": "Rocks",
   "tag.unicopia.food_types.pinecone": "Nuts & Seeds",
   "tag.unicopia.food_types.bat_ponys_delight": "Bat Pony Treats",
-  "tag.unicopia.food_types.cooked_sea_vegitables": "Prepared Fish Food",
-  "tag.unicopia.food_types.raw_sea_vegitables": "Fresh Fish Food",
+  "tag.unicopia.food_types.cooked_sea_vegitable": "Prepared Fish Food",
+  "tag.unicopia.food_types.raw_sea_vegitable": "Fresh Fish Food",
   "tag.unicopia.food_types.shells": "Sea Shells",
   "tag.unicopia.food_types.shelly": "Sea Shells",
   "tag.unicopia.food_types.candy": "Candy",
diff --git a/src/main/resources/data/c/tags/items/concrete.json b/src/main/resources/data/c/tags/items/concrete.json
deleted file mode 100644
index c09d67ba..00000000
--- a/src/main/resources/data/c/tags/items/concrete.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:white_concrete",
-    "minecraft:orange_concrete",
-    "minecraft:magenta_concrete",
-    "minecraft:light_blue_concrete",
-    "minecraft:yellow_concrete",
-    "minecraft:lime_concrete",
-    "minecraft:pink_concrete",
-    "minecraft:gray_concrete",
-    "minecraft:light_gray_concrete",
-    "minecraft:cyan_concrete",
-    "minecraft:purple_concrete",
-    "minecraft:blue_concrete",
-    "minecraft:brown_concrete",
-    "minecraft:green_concrete",
-    "minecraft:red_concrete",
-    "minecraft:black_concrete"
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/cooked_insects.json b/src/main/resources/data/c/tags/items/cooked_insects.json
deleted file mode 100644
index 1a241c36..00000000
--- a/src/main/resources/data/c/tags/items/cooked_insects.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:fermented_spider_eye",
-    { "id": "#c:cooked_insect", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/cooked_meats.json b/src/main/resources/data/c/tags/items/cooked_meats.json
deleted file mode 100644
index 52b592bc..00000000
--- a/src/main/resources/data/c/tags/items/cooked_meats.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:cooked_porkchop",
-    "minecraft:cooked_beef",
-    "minecraft:cooked_mutton",
-    "minecraft:cooked_rabbit",
-    "minecraft:rabbit_stew",
-    "minecraft:cooked_chicken",
-    { "id": "#c:cooked_meat", "required": false },
-    { "id": "#c:cooked_bacon", "required": false },
-    { "id": "#c:cooked_beef", "required": false },
-    { "id": "#c:cooked_chicken", "required": false },
-    { "id": "#c:cooked_mutton", "required": false },
-    { "id": "#c:cooked_pork", "required": false },
-    { "id": "#c:fried_chickens", "required": false },
-    { "id": "#c:hamburgers", "required": false },
-    { "id": "#c:pork_and_beans", "required": false },
-    { "id": "#c:pork_jerkies", "required": false },
-    { "id": "#c:protein", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/coral_blocks.json b/src/main/resources/data/c/tags/items/coral_blocks.json
deleted file mode 100644
index e907eedc..00000000
--- a/src/main/resources/data/c/tags/items/coral_blocks.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:tube_coral_block",
-    "minecraft:brain_coral_block",
-    "minecraft:bubble_coral_block",
-    "minecraft:fire_coral_block",
-    "minecraft:horn_coral_block"
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/coral_fans.json b/src/main/resources/data/c/tags/items/coral_fans.json
deleted file mode 100644
index 531d24c9..00000000
--- a/src/main/resources/data/c/tags/items/coral_fans.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:tube_coral_fan",
-    "minecraft:brain_coral_fan",
-    "minecraft:bubble_coral_fan",
-    "minecraft:fire_coral_fan",
-    "minecraft:horn_coral_fan"
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/corals.json b/src/main/resources/data/c/tags/items/corals.json
deleted file mode 100644
index cec1b08e..00000000
--- a/src/main/resources/data/c/tags/items/corals.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:tube_coral",
-    "minecraft:brain_coral",
-    "minecraft:bubble_coral",
-    "minecraft:fire_coral",
-    "minecraft:horn_coral"
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/glazed_terracotta.json b/src/main/resources/data/c/tags/items/glazed_terracotta.json
deleted file mode 100644
index 02b98848..00000000
--- a/src/main/resources/data/c/tags/items/glazed_terracotta.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:white_glazed_terracotta",
-    "minecraft:orange_glazed_terracotta",
-    "minecraft:magenta_glazed_terracotta",
-    "minecraft:light_blue_glazed_terracotta",
-    "minecraft:yellow_glazed_terracotta",
-    "minecraft:lime_glazed_terracotta",
-    "minecraft:pink_glazed_terracotta",
-    "minecraft:gray_glazed_terracotta",
-    "minecraft:light_gray_glazed_terracotta",
-    "minecraft:cyan_glazed_terracotta",
-    "minecraft:purple_glazed_terracotta",
-    "minecraft:blue_glazed_terracotta",
-    "minecraft:brown_glazed_terracotta",
-    "minecraft:green_glazed_terracotta",
-    "minecraft:red_glazed_terracotta",
-    "minecraft:black_glazed_terracotta"
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/love.json b/src/main/resources/data/c/tags/items/love.json
deleted file mode 100644
index 213d70aa..00000000
--- a/src/main/resources/data/c/tags/items/love.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "unicopia:love_bottle",
-    "unicopia:love_bucket",
-    "unicopia:love_mug"
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/raw_fish.json b/src/main/resources/data/c/tags/items/raw_fish.json
deleted file mode 100644
index 902fad0f..00000000
--- a/src/main/resources/data/c/tags/items/raw_fish.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:pufferfish",
-    "minecraft:cod",
-    "minecraft:salmon",
-    "minecraft:tropical_fish",
-    { "id": "#c:mollusks", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/raw_insects.json b/src/main/resources/data/c/tags/items/raw_insects.json
deleted file mode 100644
index ad219e8b..00000000
--- a/src/main/resources/data/c/tags/items/raw_insects.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:spider_eye",
-    "unicopia:butterfly",
-    "unicopia:wheat_worms",
-    "unicopia:worm_block"
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/raw_meats.json b/src/main/resources/data/c/tags/items/raw_meats.json
deleted file mode 100644
index 5333f1f8..00000000
--- a/src/main/resources/data/c/tags/items/raw_meats.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:porkchop",
-    "minecraft:beef",
-    "minecraft:mutton",
-    "minecraft:rabbit",
-    "minecraft:chicken",
-    { "id": "#c:raw_meat", "required": false },
-    { "id": "#c:lemon_chickens", "required": false },
-    { "id": "#c:raw_bacon", "required": false },
-    { "id": "#c:raw_beef", "required": false },
-    { "id": "#c:raw_chicken", "required": false },
-    { "id": "#c:raw_mutton", "required": false },
-    { "id": "#c:raw_pork", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/rocks.json b/src/main/resources/data/c/tags/items/rocks.json
deleted file mode 100644
index 9ab866dd..00000000
--- a/src/main/resources/data/c/tags/items/rocks.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "unicopia:rock"
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/rotten_meats.json b/src/main/resources/data/c/tags/items/rotten_meats.json
deleted file mode 100644
index 3f3bc662..00000000
--- a/src/main/resources/data/c/tags/items/rotten_meats.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:rotten_flesh"
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/worms.json b/src/main/resources/data/c/tags/items/worms.json
deleted file mode 100644
index 9c8b8692..00000000
--- a/src/main/resources/data/c/tags/items/worms.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "unicopia:wheat_worms"
-  ]
-}
diff --git a/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_materials_from_ground.json b/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_materials_from_ground.json
index a19e4901..200f8b3f 100644
--- a/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_materials_from_ground.json
+++ b/src/main/resources/data/minecraft/traits/blocks/overworld/conglomerate_materials_from_ground.json
@@ -3,7 +3,7 @@
   "traits": "earth:1 order:1 knowledge:4",
   "items": [
     "#minecraft:terracotta",
-    "#c:concrete",
-    "#c:glazed_terracotta"
+    "#c:concretes",
+    "#c:glazed_terracottas"
   ]
 }
\ No newline at end of file
diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_cooked_meat.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_cooked_meat.json
index e65442d1..8dd2afb5 100644
--- a/src/main/resources/data/minecraft/traits/items/overworld/edible_cooked_meat.json
+++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_cooked_meat.json
@@ -2,8 +2,6 @@
   "replace": false,
   "traits": "famine:-0.5 life:-1 knowledge:2",
   "items": [
-    "#c:cooked_meats",
-    "#c:cooked_fish",
     "minecraft:fermented_spider_eye",
     "#unicopia:food_types/cooked_fish",
     "#unicopia:food_types/cooked_meat"
diff --git a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat.json b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat.json
index 07371361..19d29187 100644
--- a/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat.json
+++ b/src/main/resources/data/minecraft/traits/items/overworld/edible_raw_meat.json
@@ -2,6 +2,6 @@
   "replace": false,
   "traits": "blood:1 famine:-2",
   "items": [
-    "#c:raw_meats"
+    "#c:raw_meat"
   ]
 }
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_effects/baked_goods.json b/src/main/resources/data/unicopia/diets/food_groups/baked_goods.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/baked_goods.json
rename to src/main/resources/data/unicopia/diets/food_groups/baked_goods.json
diff --git a/src/main/resources/data/unicopia/diets/food_groups/bat_ponys_delight.json b/src/main/resources/data/unicopia/diets/food_groups/bat_ponys_delight.json
new file mode 100644
index 00000000..0fd1f3a0
--- /dev/null
+++ b/src/main/resources/data/unicopia/diets/food_groups/bat_ponys_delight.json
@@ -0,0 +1,10 @@
+{
+  "tags": [ "unicopia:food_types/bat_ponys_delight" ],
+  "food_component": {
+    "hunger": 1,
+    "saturation": 0.1
+  },
+  "ailment": {
+    "effects": []
+  }
+}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_effects/candy.json b/src/main/resources/data/unicopia/diets/food_groups/candy.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/candy.json
rename to src/main/resources/data/unicopia/diets/food_groups/candy.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/desserts.json b/src/main/resources/data/unicopia/diets/food_groups/desserts.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/desserts.json
rename to src/main/resources/data/unicopia/diets/food_groups/desserts.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/fish/cooked.json b/src/main/resources/data/unicopia/diets/food_groups/fish/cooked.json
similarity index 81%
rename from src/main/resources/data/unicopia/diets/food_effects/fish/cooked.json
rename to src/main/resources/data/unicopia/diets/food_groups/fish/cooked.json
index 423e578f..cddeb511 100644
--- a/src/main/resources/data/unicopia/diets/food_effects/fish/cooked.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/fish/cooked.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/cooked_fish" ],
+  "tags": [ "c:cooked_fish" ],
   "food_component": {
     "hunger": 1,
     "saturation": 0.1
diff --git a/src/main/resources/data/unicopia/diets/food_effects/fish/raw.json b/src/main/resources/data/unicopia/diets/food_groups/fish/raw.json
similarity index 87%
rename from src/main/resources/data/unicopia/diets/food_effects/fish/raw.json
rename to src/main/resources/data/unicopia/diets/food_groups/fish/raw.json
index 40368a26..d33785d3 100644
--- a/src/main/resources/data/unicopia/diets/food_effects/fish/raw.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/fish/raw.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/raw_fish" ],
+  "tags": [ "c:raw_fish" ],
   "food_component": {
     "hunger": 1,
     "saturation": 0.1
diff --git a/src/main/resources/data/unicopia/diets/food_effects/fish/rotten.json b/src/main/resources/data/unicopia/diets/food_groups/fish/rotten.json
similarity index 87%
rename from src/main/resources/data/unicopia/diets/food_effects/fish/rotten.json
rename to src/main/resources/data/unicopia/diets/food_groups/fish/rotten.json
index 4259828b..fc362289 100644
--- a/src/main/resources/data/unicopia/diets/food_effects/fish/rotten.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/fish/rotten.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/rotten_fish" ],
+  "tags": [ "c:rotten_fish" ],
   "food_component": {
     "hunger": 1,
     "saturation": 0.1
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/blinding.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/blinding.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/blinding.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/blinding.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/dangerous.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/dangerous.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/dangerous.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/dangerous.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/edible.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/edible.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/edible.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/edible.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/edible_filling.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/edible_filling.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/edible_filling.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/edible_filling.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/leafy_greens.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/leafy_greens.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/leafy_greens.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/leafy_greens.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/moderate.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/moderate.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/moderate.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/moderate.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/nauseating.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/nauseating.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/nauseating.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/nauseating.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/prickly.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/prickly.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/prickly.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/prickly.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/radioactive.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/radioactive.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/radioactive.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/radioactive.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/risky.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/risky.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/risky.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/risky.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/severely_nauseating.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/severely_nauseating.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/severely_nauseating.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/severely_nauseating.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/severely_prickly.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/severely_prickly.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/severely_prickly.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/severely_prickly.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/foraging/strengthening.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/strengthening.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/foraging/strengthening.json
rename to src/main/resources/data/unicopia/diets/food_groups/foraging/strengthening.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/fruit.json b/src/main/resources/data/unicopia/diets/food_groups/fruit.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/fruit.json
rename to src/main/resources/data/unicopia/diets/food_groups/fruit.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/insect/cooked.json b/src/main/resources/data/unicopia/diets/food_groups/insect/cooked.json
similarity index 80%
rename from src/main/resources/data/unicopia/diets/food_effects/insect/cooked.json
rename to src/main/resources/data/unicopia/diets/food_groups/insect/cooked.json
index 9dbdc566..7d35ae55 100644
--- a/src/main/resources/data/unicopia/diets/food_effects/insect/cooked.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/insect/cooked.json
@@ -1,5 +1,5 @@
 {
-  "tags": [  "unicopia:food_types/cooked_insect" ],
+  "tags": [  "c:cooked_insect" ],
   "food_component": {
     "hunger": 1,
     "saturation": 0.1
diff --git a/src/main/resources/data/unicopia/diets/food_effects/insect/raw.json b/src/main/resources/data/unicopia/diets/food_groups/insect/raw.json
similarity index 81%
rename from src/main/resources/data/unicopia/diets/food_effects/insect/raw.json
rename to src/main/resources/data/unicopia/diets/food_groups/insect/raw.json
index 01a176d0..37686f06 100644
--- a/src/main/resources/data/unicopia/diets/food_effects/insect/raw.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/insect/raw.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/raw_insect" ],
+  "tags": [ "c:raw_insect" ],
   "food_component": {
     "hunger": 6,
     "saturation": 0.3
diff --git a/src/main/resources/data/unicopia/diets/food_groups/love.json b/src/main/resources/data/unicopia/diets/food_groups/love.json
new file mode 100644
index 00000000..29837e42
--- /dev/null
+++ b/src/main/resources/data/unicopia/diets/food_groups/love.json
@@ -0,0 +1,21 @@
+{
+  "tags": [ "unicopia:container_with_love" ],
+  "food_component": {
+    "hunger": 2,
+    "saturation": 0.4
+  },
+  "ailment": {
+    "effects": [
+      {
+        "effect": "unicopia:food_poisoning",
+        "seconds": 50,
+        "amplifier": 2
+      },
+      {
+        "name": "unicopia.affliction.love_sickness",
+        "type": "unicopia:lose_hunger",
+        "multiplier": 0.5
+      }
+    ]
+  }
+}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_effects/meat/cooked.json b/src/main/resources/data/unicopia/diets/food_groups/meat/cooked.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/meat/cooked.json
rename to src/main/resources/data/unicopia/diets/food_groups/meat/cooked.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/meat/raw.json b/src/main/resources/data/unicopia/diets/food_groups/meat/raw.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/meat/raw.json
rename to src/main/resources/data/unicopia/diets/food_groups/meat/raw.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/meat/rotten.json b/src/main/resources/data/unicopia/diets/food_groups/meat/rotten.json
similarity index 87%
rename from src/main/resources/data/unicopia/diets/food_effects/meat/rotten.json
rename to src/main/resources/data/unicopia/diets/food_groups/meat/rotten.json
index 72faee0e..1af89970 100644
--- a/src/main/resources/data/unicopia/diets/food_effects/meat/rotten.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/meat/rotten.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/rotten_meat" ],
+  "tags": [ "c:rotten_meat" ],
   "food_component": {
     "hunger": 1,
     "saturation": 1
diff --git a/src/main/resources/data/unicopia/diets/food_effects/nuts_and_seeds.json b/src/main/resources/data/unicopia/diets/food_groups/nuts_and_seeds.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/nuts_and_seeds.json
rename to src/main/resources/data/unicopia/diets/food_groups/nuts_and_seeds.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/pinecone.json b/src/main/resources/data/unicopia/diets/food_groups/pinecone.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/pinecone.json
rename to src/main/resources/data/unicopia/diets/food_groups/pinecone.json
diff --git a/src/main/resources/data/unicopia/diets/food_effects/rocks.json b/src/main/resources/data/unicopia/diets/food_groups/rocks.json
similarity index 100%
rename from src/main/resources/data/unicopia/diets/food_effects/rocks.json
rename to src/main/resources/data/unicopia/diets/food_groups/rocks.json
diff --git a/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/cooked.json b/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/cooked.json
new file mode 100644
index 00000000..194273c0
--- /dev/null
+++ b/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/cooked.json
@@ -0,0 +1,11 @@
+{
+  "tags": [ "unicopia:food_types/cooked_sea_vegitable" ],
+  "food_component": {
+    "hunger": 0,
+    "saturation": 0
+  },
+  "ailment": {
+    "effects": [
+    ]
+  }
+}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/raw.json b/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/raw.json
new file mode 100644
index 00000000..9fdb8ebf
--- /dev/null
+++ b/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/raw.json
@@ -0,0 +1,11 @@
+{
+  "tags": [ "unicopia:food_types/raw_sea_vegitable" ],
+  "food_component": {
+    "hunger": 0,
+    "saturation": 0
+  },
+  "ailment": {
+    "effects": [
+    ]
+  }
+}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/shells.json b/src/main/resources/data/unicopia/diets/food_groups/shells.json
new file mode 100644
index 00000000..e4760a96
--- /dev/null
+++ b/src/main/resources/data/unicopia/diets/food_groups/shells.json
@@ -0,0 +1,10 @@
+{
+  "tags": [ "unicopia:food_types/shells", "unicopia:food_types/shelly" ],
+  "food_component": {
+    "hunger": 0,
+    "saturation": 0
+  },
+  "ailment": {
+    "effects": []
+  }
+}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/shelly.json b/src/main/resources/data/unicopia/diets/food_groups/shelly.json
new file mode 100644
index 00000000..e4760a96
--- /dev/null
+++ b/src/main/resources/data/unicopia/diets/food_groups/shelly.json
@@ -0,0 +1,10 @@
+{
+  "tags": [ "unicopia:food_types/shells", "unicopia:food_types/shelly" ],
+  "food_component": {
+    "hunger": 0,
+    "saturation": 0
+  },
+  "ailment": {
+    "effects": []
+  }
+}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/races/alicorn.json b/src/main/resources/data/unicopia/diets/races/alicorn.json
index caf175c1..8801183e 100644
--- a/src/main/resources/data/unicopia/diets/races/alicorn.json
+++ b/src/main/resources/data/unicopia/diets/races/alicorn.json
@@ -3,49 +3,49 @@
   "foraging_multiplier": 0.9,
   "multipliers": [
     {
-      "tags": [ "unicopia:food_types/cooked_fish" ],
+      "tags": [ "unicopia:fish/cooked" ],
       "hunger": 1.5,
       "saturation": 1.5
     },
     {
-      "tags": [ "unicopia:food_types/raw_fish" ],
+      "tags": [ "unicopia:fish/raw" ],
       "hunger": 0.5,
       "saturation": 0.6
     },
     {
       "tags": [
-        "unicopia:food_types/baked_goods"
+        "unicopia:baked_goods"
       ],
       "hunger": 1,
       "saturation": 1
     },
     {
       "tags": [
-        "unicopia:food_types/cooked_insect",
-        "unicopia:food_types/cooked_meat"
+        "unicopia:insect/cooked",
+        "unicopia:meat/cooked"
       ],
       "hunger": 0.1,
       "saturation": 0.1
     },
     {
       "tags": [
-        "unicopia:food_types/love",
-        "unicopia:food_types/raw_insect",
-        "unicopia:food_types/raw_meat",
-        "unicopia:food_types/rotten_meat"
+        "unicopia:love",
+        "unicopia:insect/raw",
+        "unicopia:meat/raw",
+        "unicopia:meat/rotten"
       ],
       "hunger": 0,
       "saturation": 0
     },
     {
-      "tags": [ "unicopia:food_types/pinecone" ],
+      "tags": [ "unicopia:pinecone" ],
       "hunger": 0.9,
       "saturation": 0.9
     }
   ],
   "effects": [
     {
-      "tags": [ "unicopia:food_types/cooked_fish" ],
+      "tags": [ "unicopia:fish/cooked" ],
       "food_component": {
         "hunger": 2,
         "saturation": 1
@@ -55,7 +55,7 @@
       }
     },
     {
-      "tags": [ "unicopia:food_types/raw_fish" ],
+      "tags": [ "unicopia:fish/raw" ],
       "ailment": {
         "effects": [ ]
       }
diff --git a/src/main/resources/data/unicopia/diets/races/bat.json b/src/main/resources/data/unicopia/diets/races/bat.json
index 283a8788..b5c17d1b 100644
--- a/src/main/resources/data/unicopia/diets/races/bat.json
+++ b/src/main/resources/data/unicopia/diets/races/bat.json
@@ -3,54 +3,54 @@
   "foraging_multiplier": 0.9,
   "multipliers": [
     {
-      "tags": [ "unicopia:food_types/cooked_fish" ],
+      "tags": [ "unicopia:fish/cooked" ],
       "hunger": 0.75,
       "saturation": 0.75
     },
     {
-      "tags": [ "unicopia:food_types/raw_fish" ],
+      "tags": [ "unicopia:fish/raw" ],
       "hunger": 0.5,
       "saturation": 0.6
     },
     {
-      "tags": [ "unicopia:food_types/cooked_insect" ],
+      "tags": [ "unicopia:insect/cooked" ],
       "hunger": 1.75,
       "saturation": 1.75
     },
     {
-      "tags": [ "unicopia:food_types/cooked_meat" ],
+      "tags": [ "unicopia:meat/cooked" ],
       "hunger": 1.15,
       "saturation": 1.15
     },
     {
-      "tags": [ "unicopia:food_types/raw_insect" ],
+      "tags": [ "unicopia:insect/raw" ],
       "hunger": 1,
       "saturation": 1
     },
     {
-      "tags": [ "unicopia:food_types/raw_meat" ],
+      "tags": [ "unicopia:meat/raw" ],
       "hunger": 0.25,
       "saturation": 0.25
     },
     {
-      "tags": [ "unicopia:food_types/rotten_meat" ],
+      "tags": [ "unicopia:meat/rotten" ],
       "hunger": 0.2,
       "saturation": 0.2
     },
     {
-      "tags": [ "unicopia:food_types/love" ],
+      "tags": [ "unicopia:love" ],
       "hunger": 0,
       "saturation": 0
     },
     {
-      "tags": [ "unicopia:food_types/pinecone" ],
+      "tags": [ "unicopia:pinecone" ],
       "hunger": 0.9,
       "saturation": 0.9
     }
   ],
   "effects": [
     {
-      "tags": [ "unicopia:food_types/rotten_fish" ],
+      "tags": [ "unicopia:fish/rotten" ],
       "ailment": {
         "effects": [
           {
@@ -64,15 +64,15 @@
     },
     {
       "tags": [
-        "unicopia:food_types/cooked_insect",
-        "unicopia:food_types/cooked_meat"
+        "unicopia:insect/cooked",
+        "unicopia:meat/cooked"
       ],
       "ailment": {
         "effects": [ ]
       }
     },
     {
-      "tags": [ "unicopia:food_types/raw_insect" ],
+      "tags": [ "unicopia:insect/raw" ],
       "ailment": {
         "effects": [
           {
@@ -85,8 +85,8 @@
     },
     {
       "tags": [
-        "unicopia:food_types/raw_meat",
-        "unicopia:food_types/rotten_meat"
+        "unicopia:meat/raw",
+        "unicopia:meat/rotten"
       ],
       "ailment": {
         "effects": [
@@ -100,7 +100,7 @@
       }
     },
     {
-      "tags": [ "unicopia:food_types/bat_ponys_delight" ],
+      "tags": [ "unicopia:bat_ponys_delight" ],
       "ailment": {
         "effects": [
           {
diff --git a/src/main/resources/data/unicopia/diets/races/changeling.json b/src/main/resources/data/unicopia/diets/races/changeling.json
index c20c7753..e0c79004 100644
--- a/src/main/resources/data/unicopia/diets/races/changeling.json
+++ b/src/main/resources/data/unicopia/diets/races/changeling.json
@@ -3,39 +3,39 @@
   "foraging_multiplier": 0.1,
   "multipliers": [
     {
-      "tags": [ "unicopia:food_types/cooked_insect" ],
+      "tags": [ "unicopia:insect/cooked" ],
       "hunger": 2.0,
       "saturation": 1.3
     },
     {
-      "tags": [ "unicopia:food_types/cooked_meat" ],
+      "tags": [ "unicopia:meat/cooked" ],
       "hunger": 1.9,
       "saturation": 1.2
     },
     {
-      "tags": [ "unicopia:food_types/raw_insect" ],
+      "tags": [ "unicopia:insect/raw" ],
       "hunger": 1,
       "saturation": 1
     },
     {
-      "tags": [ "unicopia:food_types/raw_meat" ],
+      "tags": [ "unicopia:meat/raw" ],
       "hunger": 1.25,
       "saturation": 1.25
     },
     {
-      "tags": [ "unicopia:food_types/rotten_meat" ],
+      "tags": [ "unicopia:meat/rotten" ],
       "hunger": 0.6,
       "saturation": 0.6
     },
     {
       "tags": [
-        "unicopia:food_types/baked_goods"
+        "unicopia:baked_goods"
       ],
       "hunger": 0.5,
       "saturation": 0.9
     },
     {
-      "tags": [ "unicopia:food_types/love" ],
+      "tags": [ "unicopia:love" ],
       "hunger": 5,
       "saturation": 3
     }
@@ -60,7 +60,7 @@
   },
   "effects": [
     {
-      "tags": [ "unicopia:food_types/love" ],
+      "tags": [ "unicopia:love" ],
       "food_component": {
         "hunger": 2,
         "saturation": 1
@@ -76,7 +76,7 @@
     },
     {
       "tags": [
-        "unicopia:food_types/raw_insect"
+        "unicopia:insect/raw"
       ],
       "food_component": {
         "hunger": 3,
@@ -88,8 +88,8 @@
     },
     {
       "tags": [
-        "unicopia:food_types/cooked_fish",
-        "unicopia:food_types/raw_fish"
+        "unicopia:fish/cooked",
+        "unicopia:fish/raw"
       ],
       "ailment": {
         "effects": [
@@ -108,11 +108,11 @@
     },
     {
       "tags": [
-        "unicopia:food_types/rotten_fish",
-        "unicopia:food_types/cooked_insect",
-        "unicopia:food_types/cooked_meat",
-        "unicopia:food_types/raw_meat",
-        "unicopia:food_types/rotten_meat"
+        "unicopia:fish/rotten",
+        "unicopia:insect/cooked",
+        "unicopia:meat/cooked",
+        "unicopia:meat/raw",
+        "unicopia:meat/rotten"
       ],
       "food_component": {
         "hunger": 6,
@@ -129,8 +129,8 @@
     },
     {
       "tags": [
-        "unicopia:food_types/forage_edible",
-        "unicopia:food_types/forage_edible_filling"
+        "unicopia:foraging/edible",
+        "unicopia:foraging/edible_filling"
       ],
       "food_component": {
         "hunger": 18,
diff --git a/src/main/resources/data/unicopia/diets/races/earth.json b/src/main/resources/data/unicopia/diets/races/earth.json
index e06a0fd5..75cd8f16 100644
--- a/src/main/resources/data/unicopia/diets/races/earth.json
+++ b/src/main/resources/data/unicopia/diets/races/earth.json
@@ -4,46 +4,46 @@
   "multipliers": [
     {
       "tags": [
-        "unicopia:food_types/candy",
-        "unicopia:food_types/desserts",
-        "unicopia:food_types/rocks"
+        "unicopia:candy",
+        "unicopia:desserts",
+        "unicopia:rocks"
       ],
       "hunger": 2.5,
       "saturation": 1.7
     },
     {
       "tags": [
-        "unicopia:food_types/baked_goods"
+        "unicopia:baked_goods"
       ],
       "hunger": 1.2,
       "saturation": 2
     },
     {
-      "tags": [ "unicopia:food_types/cooked_fish" ],
+      "tags": [ "unicopia:fish/cooked" ],
       "hunger": 0.2,
       "saturation": 0.2
     },
     {
       "tags": [
-        "unicopia:food_types/cooked_insect",
-        "unicopia:food_types/cooked_meat"
+        "unicopia:insect/cooked",
+        "unicopia:meat/cooked"
       ],
       "hunger": 0.1,
       "saturation": 0.1
     },
     {
       "tags": [
-        "unicopia:food_types/love",
-        "unicopia:food_types/raw_fish",
-        "unicopia:food_types/raw_insect",
-        "unicopia:food_types/raw_meat",
-        "unicopia:food_types/rotten_meat"
+        "unicopia:love",
+        "unicopia:fish/raw",
+        "unicopia:insect/raw",
+        "unicopia:meat/raw",
+        "unicopia:meat/rotten"
       ],
       "hunger": 0,
       "saturation": 0
     },
     {
-      "tags": [ "unicopia:food_types/pinecone" ],
+      "tags": [ "unicopia:pinecone" ],
       "hunger": 1,
       "saturation": 1
     }
@@ -51,8 +51,8 @@
   "effects": [
     {
       "tags": [
-        "unicopia:food_types/candy",
-        "unicopia:food_types/rocks"
+        "unicopia:candy",
+        "unicopia:rocks"
       ],
       "food_component": {
         "hunger": 5,
@@ -65,7 +65,7 @@
     },
     {
       "tags": [
-        "unicopia:food_types/desserts"
+        "unicopia:desserts"
       ],
       "food_component": {
         "hunger": 12,
diff --git a/src/main/resources/data/unicopia/diets/races/hippogriff.json b/src/main/resources/data/unicopia/diets/races/hippogriff.json
index c91d791e..22c21e1f 100644
--- a/src/main/resources/data/unicopia/diets/races/hippogriff.json
+++ b/src/main/resources/data/unicopia/diets/races/hippogriff.json
@@ -4,57 +4,57 @@
   "multipliers": [
     {
       "tags": [
-        "unicopia:food_types/cooked_meat",
-        "unicopia:food_types/cooked_fish"
+        "unicopia:meat/cooked",
+        "unicopia:fish/cooked"
        ],
       "hunger": 1.6,
       "saturation": 1.6
     },
     {
       "tags": [
-        "unicopia:food_types/nuts_and_seeds"
+        "unicopia:nuts_and_seeds"
        ],
       "hunger": 1.4,
       "saturation": 1.4
     },
     {
       "tags": [
-        "unicopia:food_types/baked_goods"
+        "unicopia:baked_goods"
       ],
       "hunger": 1,
       "saturation": 1
     },
     {
       "tags": [
-        "unicopia:food_types/raw_meat",
-        "unicopia:food_types/raw_fish"
+        "unicopia:meat/raw",
+        "unicopia:fish/raw"
       ],
       "hunger": 0.6,
       "saturation": 0.6
     },
     {
-      "tags": [ "unicopia:food_types/rotten_meat" ],
+      "tags": [ "unicopia:meat/rotten" ],
       "hunger": 0.3,
       "saturation": 0.3
     },
     {
       "tags": [
-        "unicopia:food_types/love",
-        "unicopia:food_types/raw_insect",
-        "unicopia:food_types/cooked_insect"
+        "unicopia:love",
+        "unicopia:insect/raw",
+        "unicopia:insect/cooked"
       ],
       "hunger": 0,
       "saturation": 0
     },
     {
-      "tags": [ "unicopia:food_types/pinecone" ],
+      "tags": [ "unicopia:pinecone" ],
       "hunger": 1,
       "saturation": 1
     }
   ],
   "effects": [
     {
-      "tags": [ "unicopia:food_types/cooked_fish" ],
+      "tags": [ "unicopia:fish/cooked" ],
       "food_component": {
         "hunger": 2,
         "saturation": 1
@@ -64,15 +64,15 @@
       }
     },
     {
-      "tags": [ "unicopia:food_types/raw_fish" ],
+      "tags": [ "unicopia:fish/raw" ],
       "ailment": {
         "effects": [ ]
       }
     },
     {
       "tags": [
-        "unicopia:food_types/forage_prickly",
-        "unicopia:food_types/forage_severely_prickly"
+        "unicopia:foraging/prickly",
+        "unicopia:foraging/severely_prickly"
       ],
       "food_component": {
         "hunger": 2,
@@ -83,7 +83,7 @@
       }
     },
     {
-      "tags": [ "unicopia:food_types/pinecone" ],
+      "tags": [ "unicopia:pinecone" ],
       "ailment": {
         "effects": [
           {
diff --git a/src/main/resources/data/unicopia/diets/races/human.json b/src/main/resources/data/unicopia/diets/races/human.json
index ddf024b3..67db4d5b 100644
--- a/src/main/resources/data/unicopia/diets/races/human.json
+++ b/src/main/resources/data/unicopia/diets/races/human.json
@@ -5,13 +5,13 @@
   "effects": [
     {
       "tags": [
-        "unicopia:food_types/cooked_fish",
-        "unicopia:food_types/raw_fish",
-        "unicopia:food_types/rotten_fish",
-        "unicopia:food_types/cooked_meat",
-        "unicopia:food_types/raw_meat",
-        "unicopia:food_types/rotten_meat",
-        "unicopia:food_types/pinecone"
+        "unicopia:fish/cooked",
+        "unicopia:fish/raw",
+        "unicopia:fish/rotten",
+        "unicopia:meat/cooked",
+        "unicopia:meat/raw",
+        "unicopia:meat/rotten",
+        "unicopia:pinecone"
       ],
       "ailment": {
         "effects": [ ]
diff --git a/src/main/resources/data/unicopia/diets/races/kirin.json b/src/main/resources/data/unicopia/diets/races/kirin.json
index 42b41a67..5a9a5919 100644
--- a/src/main/resources/data/unicopia/diets/races/kirin.json
+++ b/src/main/resources/data/unicopia/diets/races/kirin.json
@@ -3,42 +3,42 @@
   "foraging_multiplier": 0.9,
   "multipliers": [
     {
-      "tags": [ "unicopia:food_types/cooked_meat" ],
+      "tags": [ "unicopia:meat/cooked" ],
       "hunger": 1.5,
       "saturation": 1.5
     },
     {
-      "tags": [ "unicopia:food_types/raw_meat" ],
+      "tags": [ "unicopia:meat/raw" ],
       "hunger": 0.5,
       "saturation": 0.6
     },
     {
       "tags": [
-        "unicopia:food_types/baked_goods"
+        "unicopia:baked_goods"
       ],
       "hunger": 1,
       "saturation": 1
     },
     {
       "tags": [
-        "unicopia:food_types/cooked_insect",
-        "unicopia:food_types/cooked_fish"
+        "unicopia:insect/cooked",
+        "unicopia:fish/cooked"
       ],
       "hunger": 0.1,
       "saturation": 0.1
     },
     {
       "tags": [
-        "unicopia:food_types/love",
-        "unicopia:food_types/raw_insect",
-        "unicopia:food_types/raw_fish",
-        "unicopia:food_types/rotten_meat"
+        "unicopia:love",
+        "unicopia:insect/raw",
+        "unicopia:fish/raw",
+        "unicopia:meat/rotten"
       ],
       "hunger": 0,
       "saturation": 0
     },
     {
-      "tags": [ "unicopia:food_types/pinecone" ],
+      "tags": [ "unicopia:pinecone" ],
       "hunger": 0.9,
       "saturation": 0.9
     }
@@ -46,15 +46,15 @@
   "effects": [
     {
       "tags": [
-        "unicopia:food_types/rotten_fish",
-        "unicopia:food_types/cooked_insect",
-        "unicopia:food_types/cooked_meat",
-        "unicopia:food_types/raw_meat",
-        "unicopia:food_types/rotten_meat",
-        "unicopia:food_types/forage_blinding",
-        "unicopia:food_types/forage_prickly",
-        "unicopia:food_types/forage_severely_prickly",
-        "unicopia:food_types/forage_strengthening"
+        "unicopia:fish/rotten",
+        "unicopia:insect/cooked",
+        "unicopia:meat/cooked",
+        "unicopia:meat/raw",
+        "unicopia:meat/rotten",
+        "unicopia:foraging/blinding",
+        "unicopia:foraging/prickly",
+        "unicopia:foraging/severely_prickly",
+        "unicopia:foraging/strengthening"
       ],
       "food_component": {
         "hunger": 2,
diff --git a/src/main/resources/data/unicopia/diets/races/pegasus.json b/src/main/resources/data/unicopia/diets/races/pegasus.json
index d1208202..7cbd683a 100644
--- a/src/main/resources/data/unicopia/diets/races/pegasus.json
+++ b/src/main/resources/data/unicopia/diets/races/pegasus.json
@@ -3,55 +3,55 @@
   "foraging_multiplier": 1,
   "multipliers": [
     {
-      "tags": [ "unicopia:food_types/cooked_fish" ],
+      "tags": [ "unicopia:fish/cooked" ],
       "hunger": 1.5,
       "saturation": 1.5
     },
     {
-      "tags": [ "unicopia:food_types/raw_fish" ],
+      "tags": [ "unicopia:fish/raw" ],
       "hunger": 0.5,
       "saturation": 0.6
     },
     {
       "tags": [
-        "unicopia:food_types/baked_goods"
+        "unicopia:baked_goods"
       ],
       "hunger": 1,
       "saturation": 1
     },
     {
       "tags": [
-        "unicopia:food_types/cooked_insect",
-        "unicopia:food_types/cooked_meat"
+        "unicopia:insect/cooked",
+        "unicopia:meat/cooked"
       ],
       "hunger": 0.1,
       "saturation": 0.1
     },
     {
       "tags": [
-        "unicopia:food_types/love",
-        "unicopia:food_types/raw_insect",
-        "unicopia:food_types/raw_meat",
-        "unicopia:food_types/rotten_meat"
+        "unicopia:love",
+        "unicopia:insect/raw",
+        "unicopia:meat/raw",
+        "unicopia:meat/rotten"
       ],
       "hunger": 0,
       "saturation": 0
     },
     {
-      "tags": [ "unicopia:food_types/pinecone" ],
+      "tags": [ "unicopia:pinecone" ],
       "hunger": 0.9,
       "saturation": 0.9
     }
   ],
   "effects": [
     {
-      "tags": [ "unicopia:food_types/cooked_fish" ],
+      "tags": [ "unicopia:fish/cooked" ],
       "ailment": {
         "effects": [ ]
       }
     },
     {
-      "tags": [ "unicopia:food_types/raw_fish" ],
+      "tags": [ "unicopia:fish/raw" ],
       "ailment": {
         "effects": [
           {
diff --git a/src/main/resources/data/unicopia/diets/races/seapony.json b/src/main/resources/data/unicopia/diets/races/seapony.json
index 6558338a..c67aa9f7 100644
--- a/src/main/resources/data/unicopia/diets/races/seapony.json
+++ b/src/main/resources/data/unicopia/diets/races/seapony.json
@@ -3,14 +3,13 @@
   "foraging_multiplier": 0.7,
   "multipliers": [
     {
-      "tags": [ "unicopia:food_types/raw_sea_vegitable" ],
+      "tags": [ "unicopia:sea_vegetable/raw" ],
       "hunger": 1,
       "saturation": 1
     },
     {
       "tags": [
-        "unicopia:food_types/shells",
-        "unicopia:food_types/shelly"
+        "unicopia:shells", "unicopia:shelly"
       ],
       "hunger": 1,
       "saturation": 1
@@ -27,21 +26,21 @@
   },
   "effects": [
     {
-      "tags": [ "unicopia:food_types/cooked_fish" ],
+      "tags": [ "unicopia:fish/cooked" ],
       "ailment": {
         "effects": [ ]
       }
     },
     {
-      "tags": [ "unicopia:food_types/raw_fish" ],
+      "tags": [ "unicopia:fish/raw" ],
       "ailment": {
         "effects": [ ]
       }
     },
     {
       "tags": [
-        "unicopia:food_types/forage_edible",
-        "unicopia:food_types/forage_edible_filling"
+        "unicopia:foraging/edible",
+        "unicopia:foraging/edible_filling"
       ],
       "food_component": {
         "hunger": 18,
@@ -58,7 +57,7 @@
       }
     },
     {
-      "tags": [ "unicopia:food_types/raw_sea_vegitable" ],
+      "tags": [ "unicopia:sea_vegetable/raw" ],
       "food_component": {
         "hunger": 2,
         "saturation": 1
@@ -68,7 +67,7 @@
       }
     },
     {
-      "tags": [ "unicopia:food_types/cooked_sea_vegitable" ],
+      "tags": [ "unicopia:sea_vegetable/cooked" ],
       "food_component": {
         "hunger": 6,
         "saturation": 2
@@ -78,7 +77,7 @@
       }
     },
     {
-      "tags": [ "unicopia:food_types/shells" ],
+      "tags": [ "unicopia:shells" ],
       "food_component": {
         "hunger": 3,
         "saturation": 5
@@ -88,7 +87,7 @@
       }
     },
     {
-      "tags": [ "unicopia:food_types/shelly" ],
+      "tags": [ "unicopia:shelly" ],
       "food_component": {
         "hunger": 6,
         "saturation": 7
diff --git a/src/main/resources/data/unicopia/diets/races/unicorn.json b/src/main/resources/data/unicopia/diets/races/unicorn.json
index be679a08..3213c82b 100644
--- a/src/main/resources/data/unicopia/diets/races/unicorn.json
+++ b/src/main/resources/data/unicopia/diets/races/unicorn.json
@@ -4,33 +4,33 @@
   "multipliers": [
     {
       "tags": [
-        "unicopia:food_types/cooked_insect",
-        "unicopia:food_types/cooked_meat",
-        "unicopia:food_types/cooked_fish"
+        "unicopia:insect/cooked",
+        "unicopia:meat/cooked",
+        "unicopia:fish/cooked"
       ],
       "hunger": 0.1,
       "saturation": 0.1
     },
     {
       "tags": [
-        "unicopia:food_types/baked_goods"
+        "unicopia:baked_goods"
       ],
       "hunger": 1,
       "saturation": 1
     },
     {
       "tags": [
-        "unicopia:food_types/love",
-        "unicopia:food_types/raw_insect",
-        "unicopia:food_types/raw_meat",
-        "unicopia:food_types/raw_fish",
-        "unicopia:food_types/rotten_meat"
+        "unicopia:love",
+        "unicopia:insect/raw",
+        "unicopia:meat/raw",
+        "unicopia:fish/raw",
+        "unicopia:meat/rotten"
       ],
       "hunger": 0,
       "saturation": 0
     },
     {
-      "tags": [ "unicopia:food_types/pinecone" ],
+      "tags": [ "unicopia:pinecone" ],
       "hunger": 0.9,
       "saturation": 0.9
     }
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/bananas.json b/src/main/resources/data/unicopia/loot_tables/blocks/bananas.json
deleted file mode 100644
index 7e5ce591..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/bananas.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "functions": [
-            {
-              "add": false,
-              "count": {
-                "type": "minecraft:uniform",
-                "max": 12.0,
-                "min": 6.0
-              },
-              "function": "minecraft:set_count"
-            },
-            {
-              "enchantment": "minecraft:fortune",
-              "formula": "minecraft:binomial_with_bonus_count",
-              "function": "minecraft:apply_bonus",
-              "parameters": {
-                "extra": 3,
-                "probability": 0.5714286
-              }
-            }
-          ],
-          "name": "unicopia:banana"
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/chiselled_chitin_hull.json b/src/main/resources/data/unicopia/loot_tables/blocks/chiselled_chitin_hull.json
deleted file mode 100644
index 5af0f74d..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/chiselled_chitin_hull.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:chitin",
-          "functions": [
-            {
-              "add": false,
-              "count": 2,
-              "function": "minecraft:set_count"
-            }
-          ]
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        }
-      ]
-    },
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:chiselled_chitin",
-          "functions": [
-            {
-              "add": false,
-              "count": 2,
-              "function": "minecraft:set_count"
-            }
-          ]
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/clam_shell.json b/src/main/resources/data/unicopia/loot_tables/blocks/clam_shell.json
deleted file mode 100644
index f21c580e..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/clam_shell.json
+++ /dev/null
@@ -1,96 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:clam_shell",
-          "conditions": [
-            {
-              "block": "unicopia:clam_shell",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "count": "1"
-              }
-            }
-          ],
-          "functions": [
-            {
-              "add": false,
-              "count": 1,
-              "function": "minecraft:set_count"
-            }
-          ]
-        },
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:clam_shell",
-          "conditions": [
-            {
-              "block": "unicopia:clam_shell",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "count": "2"
-              }
-            }
-          ],
-          "functions": [
-            {
-              "add": false,
-              "count": 2,
-              "function": "minecraft:set_count"
-            }
-          ]
-        },
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:clam_shell",
-          "conditions": [
-            {
-              "block": "unicopia:clam_shell",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "count": "3"
-              }
-            }
-          ],
-          "functions": [
-            {
-              "add": false,
-              "count": 3,
-              "function": "minecraft:set_count"
-            }
-          ]
-        },
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:clam_shell",
-          "conditions": [
-            {
-              "block": "unicopia:clam_shell",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "count": "4"
-              }
-            }
-          ],
-          "functions": [
-            {
-              "add": false,
-              "count": 4,
-              "function": "minecraft:set_count"
-            }
-          ]
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/cloud_slab.json b/src/main/resources/data/unicopia/loot_tables/blocks/cloud_slab.json
deleted file mode 100644
index 6926e7fe..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/cloud_slab.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:cloud_lump",
-          "functions": [
-            {
-              "add": false,
-              "count": 2,
-              "function": "minecraft:set_count"
-            },
-            {
-              "add": false,
-              "count": 4,
-              "function": "minecraft:set_count",
-              "conditions": [
-                {
-                  "block": "unicopia:cloud_slab",
-                  "condition": "minecraft:block_state_property",
-                  "properties": {
-                    "type": "double"
-                  }
-                }
-              ]
-            }
-          ]
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/dense_cloud_slab.json b/src/main/resources/data/unicopia/loot_tables/blocks/dense_cloud_slab.json
deleted file mode 100644
index e9d1f0e2..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/dense_cloud_slab.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:cloud_lump",
-          "functions": [
-            {
-              "add": false,
-              "count": 4,
-              "function": "minecraft:set_count"
-            },
-            {
-              "add": false,
-              "count": 8,
-              "function": "minecraft:set_count",
-              "conditions": [
-                {
-                  "block": "unicopia:dense_cloud_slab",
-                  "condition": "minecraft:block_state_property",
-                  "properties": {
-                    "type": "double"
-                  }
-                }
-              ]
-            }
-          ]
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/etched_cloud_slab.json b/src/main/resources/data/unicopia/loot_tables/blocks/etched_cloud_slab.json
deleted file mode 100644
index 4465e094..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/etched_cloud_slab.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:cloud_lump",
-          "functions": [
-            {
-              "add": false,
-              "count": 4,
-              "function": "minecraft:set_count"
-            },
-            {
-              "add": false,
-              "count": 8,
-              "function": "minecraft:set_count",
-              "conditions": [
-                {
-                  "block": "unicopia:etched_cloud_slab",
-                  "condition": "minecraft:block_state_property",
-                  "properties": {
-                    "type": "double"
-                  }
-                }
-              ]
-            }
-          ]
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/gold_root.json b/src/main/resources/data/unicopia/loot_tables/blocks/gold_root.json
deleted file mode 100644
index 6bfe9e77..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/gold_root.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
-  "type": "minecraft:block",
-  "functions": [
-    {
-      "function": "minecraft:explosion_decay"
-    }
-  ],
-  "pools": [
-    {
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:golden_carrot"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:gold_root",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "age": "7"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "functions": [
-            {
-              "enchantment": "minecraft:fortune",
-              "formula": "minecraft:binomial_with_bonus_count",
-              "function": "minecraft:apply_bonus",
-              "parameters": {
-                "extra": 3,
-                "probability": 0.5714286
-              }
-            }
-          ],
-          "name": "minecraft:golden_carrot"
-        }
-      ],
-      "rolls": 1.0
-    }
-  ],
-  "random_sequence": "minecraft:blocks/carrots"
-}
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/golden_apple.json b/src/main/resources/data/unicopia/loot_tables/blocks/golden_apple.json
deleted file mode 100644
index 7bd87a25..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/golden_apple.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:golden_apple"
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        },
-        {
-          "block": "unicopia:golden_apple",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "enchanted": "false"
-          }
-        }
-      ]
-    },
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:enchanted_golden_apple"
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        },
-        {
-          "block": "unicopia:golden_apple",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "enchanted": "true"
-          }
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json b/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json
deleted file mode 100644
index 1101857f..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/hay_block.json
+++ /dev/null
@@ -1,157 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:hay_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "top_north_east": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:wheat"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:hay_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "top_north_west": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:wheat"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:hay_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "top_south_east": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:wheat"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:hay_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "top_south_west": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:wheat"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:hay_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "bottom_north_east": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:wheat"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:hay_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "bottom_north_west": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:wheat"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:hay_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "bottom_south_east": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:wheat"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:hay_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "bottom_south_west": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:wheat"
-        }
-      ],
-      "rolls": 1.0
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/mysterious_egg.json b/src/main/resources/data/unicopia/loot_tables/blocks/mysterious_egg.json
deleted file mode 100644
index 915f5152..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/mysterious_egg.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:mysterious_egg",
-          "functions": [
-            {
-              "add": false,
-              "count": 1,
-              "function": "minecraft:set_count",
-              "conditions": [
-                {
-                  "block": "unicopia:mysterious_egg",
-                  "condition": "minecraft:block_state_property",
-                  "properties": {
-                    "count": "1"
-                  }
-                }
-              ]
-            },
-            {
-              "add": false,
-              "count": 2,
-              "function": "minecraft:set_count",
-              "conditions": [
-                {
-                  "block": "unicopia:mysterious_egg",
-                  "condition": "minecraft:block_state_property",
-                  "properties": {
-                    "count": "2"
-                  }
-                }
-              ]
-            },
-            {
-              "add": false,
-              "count": 3,
-              "function": "minecraft:set_count",
-              "conditions": [
-                {
-                  "block": "unicopia:mysterious_egg",
-                  "condition": "minecraft:block_state_property",
-                  "properties": {
-                    "count": "3"
-                  }
-                }
-              ]
-            }
-          ]
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/oats.json b/src/main/resources/data/unicopia/loot_tables/blocks/oats.json
deleted file mode 100644
index 17b0d17a..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/oats.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
-  "type": "minecraft:block",
-  "functions": [
-    {
-      "function": "minecraft:explosion_decay"
-    }
-  ],
-  "pools": [
-    {
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:alternatives",
-          "children": [
-            {
-              "type": "minecraft:item",
-              "name": "unicopia:oat_seeds"
-            }
-          ]
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:oats",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "age": "11"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "functions": [
-            {
-              "enchantment": "minecraft:fortune",
-              "formula": "minecraft:binomial_with_bonus_count",
-              "function": "minecraft:apply_bonus",
-              "parameters": {
-                "extra": 3,
-                "probability": 0.5714286
-              }
-            }
-          ],
-          "name": "unicopia:oat_seeds"
-        }
-      ],
-      "rolls": 1.0
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/oats_crown.json b/src/main/resources/data/unicopia/loot_tables/blocks/oats_crown.json
deleted file mode 100644
index fe5807ed..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/oats_crown.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  "type": "minecraft:block",
-  "functions": [
-    {
-      "function": "minecraft:explosion_decay"
-    }
-  ],
-  "pools": [
-    {
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "conditions": [
-            {
-              "block": "unicopia:oats_crown",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "age": "1"
-              }
-            }
-          ],
-          "name": "unicopia:oats"
-        }
-      ],
-      "rolls": 1.0
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/oats_stem.json b/src/main/resources/data/unicopia/loot_tables/blocks/oats_stem.json
deleted file mode 100644
index 72ea00c4..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/oats_stem.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  "type": "minecraft:block",
-  "functions": [
-    {
-      "function": "minecraft:explosion_decay"
-    }
-  ],
-  "pools": [
-    {
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "conditions": [
-            {
-              "block": "unicopia:oats_stem",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "age": "6"
-              }
-            }
-          ],
-          "name": "unicopia:oats"
-        }
-      ],
-      "rolls": 1.0
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/pineapple.json b/src/main/resources/data/unicopia/loot_tables/blocks/pineapple.json
deleted file mode 100644
index aced6181..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/pineapple.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:alternatives",
-          "children": [
-            {
-              "type": "minecraft:item",
-              "conditions": [
-                {
-                  "condition": "minecraft:block_state_property",
-                  "block": "unicopia:pineapple",
-                  "properties": {
-                    "age": "7",
-                    "half": "top"
-                  }
-                }
-              ],
-              "name": "unicopia:pineapple"
-            }
-          ]
-        }
-      ]
-    }
-  ],
-  "functions": [
-    {
-      "function": "minecraft:explosion_decay"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/plunder_vine.json b/src/main/resources/data/unicopia/loot_tables/blocks/plunder_vine.json
deleted file mode 100644
index 857d1f4c..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/plunder_vine.json
+++ /dev/null
@@ -1,42 +0,0 @@
-{
-  "type": "minecraft:block",
-  "functions": [
-    {
-      "function": "minecraft:explosion_decay"
-    }
-  ],
-  "pools": [
-    {
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:stick"
-        },
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:dead_bush"
-        }
-      ],
-      "rolls": 4.0
-    },
-        {
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:stick"
-        },
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:dead_bush"
-        },
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:gryphon_feather"
-        }
-      ],
-      "rolls": 1.0
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/rice_block.json b/src/main/resources/data/unicopia/loot_tables/blocks/rice_block.json
deleted file mode 100644
index e79c51d7..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/rice_block.json
+++ /dev/null
@@ -1,165 +0,0 @@
-{
-  "type": "minecraft:block",
-  "fabric:load_conditions": [
-    {
-      "condition": "fabric:all_mods_loaded",
-      "values": [
-        "farmersdelight"
-      ]
-    }
-  ],
-  "pools": [
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:rice_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "top_north_east": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:rice_panicle"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:rice_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "top_north_west": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:rice_panicle"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:rice_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "top_south_east": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:rice_panicle"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:rice_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "top_south_west": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:rice_panicle"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:rice_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "bottom_north_east": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:rice_panicle"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:rice_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "bottom_north_west": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:rice_panicle"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:rice_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "bottom_south_east": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:rice_panicle"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:rice_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "bottom_south_west": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:rice_panicle"
-        }
-      ],
-      "rolls": 1.0
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/rocks.json b/src/main/resources/data/unicopia/loot_tables/blocks/rocks.json
deleted file mode 100644
index 83eda199..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/rocks.json
+++ /dev/null
@@ -1,85 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:alternatives",
-          "children": [
-            {
-              "type": "minecraft:item",
-              "conditions": [
-                {
-                  "condition": "minecraft:block_state_property",
-                  "block": "unicopia:rocks",
-                  "properties": {
-                    "age": "7"
-                  }
-                },
-	            {
-	              "condition": "minecraft:random_chance",
-	              "chance": 0.25
-	            }
-              ],
-              "name": "unicopia:weird_rock"
-            },
-            {
-              "type": "minecraft:item",
-              "conditions": [
-                {
-                  "condition": "minecraft:block_state_property",
-                  "block": "unicopia:rocks",
-                  "properties": {
-                    "age": "7"
-                  }
-                }
-              ],
-              "name": "unicopia:rock"
-            },
-            {
-              "type": "minecraft:item",
-              "name": "unicopia:pebbles"
-            }
-          ]
-        }
-      ]
-    },
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "functions": [
-            {
-              "function": "minecraft:apply_bonus",
-              "enchantment": "minecraft:fortune",
-              "formula": "minecraft:binomial_with_bonus_count",
-              "parameters": {
-                "extra": 3,
-                "probability": 0.5714286
-              }
-            }
-          ],
-          "name": "unicopia:pebbles"
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:block_state_property",
-          "block": "unicopia:rocks",
-          "properties": {
-            "age": "7"
-          }
-        }
-      ]
-    }
-  ],
-  "functions": [
-    {
-      "function": "minecraft:explosion_decay"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/scallop_shell.json b/src/main/resources/data/unicopia/loot_tables/blocks/scallop_shell.json
deleted file mode 100644
index c1e002b1..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/scallop_shell.json
+++ /dev/null
@@ -1,96 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:scallop_shell",
-          "conditions": [
-            {
-              "block": "unicopia:scallop_shell",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "count": "1"
-              }
-            }
-          ],
-          "functions": [
-            {
-              "add": false,
-              "count": 1,
-              "function": "minecraft:set_count"
-            }
-          ]
-        },
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:scallop_shell",
-          "conditions": [
-            {
-              "block": "unicopia:scallop_shell",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "count": "2"
-              }
-            }
-          ],
-          "functions": [
-            {
-              "add": false,
-              "count": 2,
-              "function": "minecraft:set_count"
-            }
-          ]
-        },
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:scallop_shell",
-          "conditions": [
-            {
-              "block": "unicopia:scallop_shell",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "count": "3"
-              }
-            }
-          ],
-          "functions": [
-            {
-              "add": false,
-              "count": 3,
-              "function": "minecraft:set_count"
-            }
-          ]
-        },
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:scallop_shell",
-          "conditions": [
-            {
-              "block": "unicopia:scallop_shell",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "count": "4"
-              }
-            }
-          ],
-          "functions": [
-            {
-              "add": false,
-              "count": 4,
-              "function": "minecraft:set_count"
-            }
-          ]
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/slime_pustule.json b/src/main/resources/data/unicopia/loot_tables/blocks/slime_pustule.json
deleted file mode 100644
index 3945bf2f..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/slime_pustule.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:slime_pustule"
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:all_of",
-          "terms": [
-            {
-              "block": "unicopia:slime_pustule",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "shape": "pod"
-              }
-            },
-            {
-              "condition": "minecraft:any_of",
-              "terms": [
-                {
-                  "condition": "minecraft:match_tool",
-                  "predicate": {
-                    "items": [
-                      "minecraft:shears"
-                    ]
-                  }
-                },
-                {
-                  "condition": "minecraft:match_tool",
-                  "predicate": {
-                    "enchantments": [
-                      {
-                        "enchantment": "minecraft:silk_touch",
-                        "levels": {
-                          "min": 1
-                        }
-                      }
-                    ]
-                  }
-                }
-              ]
-            }
-          ]
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/soggy_cloud_slab.json b/src/main/resources/data/unicopia/loot_tables/blocks/soggy_cloud_slab.json
deleted file mode 100644
index b805f097..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/soggy_cloud_slab.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:cloud",
-          "functions": [
-            {
-              "add": false,
-              "count": 3,
-              "function": "minecraft:set_count"
-            }
-          ]
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/straw_block.json b/src/main/resources/data/unicopia/loot_tables/blocks/straw_block.json
deleted file mode 100644
index ca46ba50..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/straw_block.json
+++ /dev/null
@@ -1,165 +0,0 @@
-{
-  "type": "minecraft:block",
-  "fabric:load_conditions": [
-    {
-      "condition": "fabric:all_mods_loaded",
-      "values": [
-        "farmersdelight"
-      ]
-    }
-  ],
-  "pools": [
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:straw_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "top_north_east": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:straw"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:straw_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "top_north_west": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:straw"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:straw_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "top_south_east": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:straw"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:straw_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "top_south_west": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:straw"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:straw_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "bottom_north_east": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:straw"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:straw_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "bottom_north_west": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:straw"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:straw_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "bottom_south_east": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:straw"
-        }
-      ],
-      "rolls": 1.0
-    },
-    {
-      "bonus_rolls": 0.0,
-      "conditions": [
-        {
-          "block": "unicopia:straw_block",
-          "condition": "minecraft:block_state_property",
-          "properties": {
-            "bottom_south_west": "true"
-          }
-        }
-      ],
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "farmersdelight:straw"
-        }
-      ],
-      "rolls": 1.0
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/blocks/turret_shell.json b/src/main/resources/data/unicopia/loot_tables/blocks/turret_shell.json
deleted file mode 100644
index b300a2d4..00000000
--- a/src/main/resources/data/unicopia/loot_tables/blocks/turret_shell.json
+++ /dev/null
@@ -1,96 +0,0 @@
-{
-  "type": "minecraft:block",
-  "pools": [
-    {
-      "rolls": 1.0,
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:turret_shell",
-          "conditions": [
-            {
-              "block": "unicopia:turret_shell",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "count": "1"
-              }
-            }
-          ],
-          "functions": [
-            {
-              "add": false,
-              "count": 1,
-              "function": "minecraft:set_count"
-            }
-          ]
-        },
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:turret_shell",
-          "conditions": [
-            {
-              "block": "unicopia:turret_shell",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "count": "2"
-              }
-            }
-          ],
-          "functions": [
-            {
-              "add": false,
-              "count": 2,
-              "function": "minecraft:set_count"
-            }
-          ]
-        },
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:turret_shell",
-          "conditions": [
-            {
-              "block": "unicopia:turret_shell",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "count": "3"
-              }
-            }
-          ],
-          "functions": [
-            {
-              "add": false,
-              "count": 3,
-              "function": "minecraft:set_count"
-            }
-          ]
-        },
-        {
-          "type": "minecraft:item",
-          "name": "unicopia:turret_shell",
-          "conditions": [
-            {
-              "block": "unicopia:turret_shell",
-              "condition": "minecraft:block_state_property",
-              "properties": {
-                "count": "4"
-              }
-            }
-          ],
-          "functions": [
-            {
-              "add": false,
-              "count": 4,
-              "function": "minecraft:set_count"
-            }
-          ]
-        }
-      ],
-      "conditions": [
-        {
-          "condition": "minecraft:survives_explosion"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/chests/changeling_hive_trap.json b/src/main/resources/data/unicopia/loot_tables/chests/changeling_hive_trap.json
deleted file mode 100644
index 2dac1a86..00000000
--- a/src/main/resources/data/unicopia/loot_tables/chests/changeling_hive_trap.json
+++ /dev/null
@@ -1,204 +0,0 @@
-{
-  "type": "minecraft:chest",
-  "pools": [
-    {
-      "bonus_rolls": 0.0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:tipped_arrow",
-          "functions": [
-            { "function": "minecraft:set_potion", "id": "unicopia:short_morph_earth" },
-            {
-              "function": "minecraft:set_count",
-              "count": {
-                "min": 3.0,
-                "max": 9.0,
-                "type": "minecraft:uniform"
-              }
-            }
-          ],
-          "weight": 3
-        },
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:tipped_arrow",
-          "functions": [
-            { "function": "minecraft:set_potion", "id": "unicopia:short_morph_unicorn" },
-            {
-              "function": "minecraft:set_count",
-              "count": {
-                "min": 3.0,
-                "max": 9.0,
-                "type": "minecraft:uniform"
-              }
-            }
-          ],
-          "weight": 1
-        },
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:tipped_arrow",
-          "functions": [
-            { "function": "minecraft:set_potion", "id": "unicopia:short_morph_pegasus" },
-            {
-              "function": "minecraft:set_count",
-              "count": {
-                "min": 3.0,
-                "max": 9.0,
-                "type": "minecraft:uniform"
-              }
-            }
-          ],
-          "weight": 1
-        },
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:tipped_arrow",
-          "functions": [
-            { "function": "minecraft:set_potion", "id": "unicopia:short_morph_bat" },
-            {
-              "function": "minecraft:set_count",
-              "count": {
-                "min": 3.0,
-                "max": 9.0,
-                "type": "minecraft:uniform"
-              }
-            }
-          ],
-          "weight": 1
-        },
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:tipped_arrow",
-          "functions": [
-            { "function": "minecraft:set_potion", "id": "unicopia:short_morph_kirin" },
-            {
-              "function": "minecraft:set_count",
-              "count": {
-                "min": 3.0,
-                "max": 9.0,
-                "type": "minecraft:uniform"
-              }
-            }
-          ],
-          "weight": 1
-        },
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:tipped_arrow",
-          "functions": [
-            { "function": "minecraft:set_potion", "id": "unicopia:short_morph_hippogriff" },
-            {
-              "function": "minecraft:set_count",
-              "count": {
-                "min": 3.0,
-                "max": 9.0,
-                "type": "minecraft:uniform"
-              }
-            }
-          ],
-          "weight": 1
-        },
-        
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:tipped_arrow",
-          "functions": [
-            { "function": "minecraft:set_potion", "id": "unicopia:long_morph_earth" },
-            {
-              "function": "minecraft:set_count",
-              "count": {
-                "min": 3.0,
-                "max": 9.0,
-                "type": "minecraft:uniform"
-              }
-            }
-          ],
-          "weight": 5
-        },
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:tipped_arrow",
-          "functions": [
-            { "function": "minecraft:set_potion", "id": "unicopia:long_morph_unicorn" },
-            {
-              "function": "minecraft:set_count",
-              "count": {
-                "min": 3.0,
-                "max": 9.0,
-                "type": "minecraft:uniform"
-              }
-            }
-          ],
-          "weight": 2
-        },
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:tipped_arrow",
-          "functions": [
-            { "function": "minecraft:set_potion", "id": "unicopia:long_morph_pegasus" },
-            {
-              "function": "minecraft:set_count",
-              "count": {
-                "min": 3.0,
-                "max": 9.0,
-                "type": "minecraft:uniform"
-              }
-            }
-          ],
-          "weight": 2
-        },
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:tipped_arrow",
-          "functions": [
-            { "function": "minecraft:set_potion", "id": "unicopia:long_morph_bat" },
-            {
-              "function": "minecraft:set_count",
-              "count": {
-                "min": 3.0,
-                "max": 9.0,
-                "type": "minecraft:uniform"
-              }
-            }
-          ],
-          "weight": 2
-        },
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:tipped_arrow",
-          "functions": [
-            { "function": "minecraft:set_potion", "id": "unicopia:long_morph_kirin" },
-            {
-              "function": "minecraft:set_count",
-              "count": {
-                "min": 3.0,
-                "max": 9.0,
-                "type": "minecraft:uniform"
-              }
-            }
-          ],
-          "weight": 2
-        },
-        {
-          "type": "minecraft:item",
-          "name": "minecraft:tipped_arrow",
-          "functions": [
-            { "function": "minecraft:set_potion", "id": "unicopia:long_morph_hippogriff" },
-            {
-              "function": "minecraft:set_count",
-              "count": {
-                "min": 3.0,
-                "max": 9.0,
-                "type": "minecraft:uniform"
-              }
-            }
-          ],
-          "weight": 2
-        }
-      ],
-      "rolls": 6.0
-    }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/loot_tables/entities/butterfly.json b/src/main/resources/data/unicopia/loot_tables/entities/butterfly.json
deleted file mode 100644
index 9992d4bd..00000000
--- a/src/main/resources/data/unicopia/loot_tables/entities/butterfly.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-  "type": "minecraft:entity",
-  "pools": [
-    {
-      "rolls": 1,
-      "bonus_rolls": 0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "functions": [
-            {
-              "function": "minecraft:looting_enchant",
-              "count": {
-                "type": "minecraft:uniform",
-                "min": 0.0,
-                "max": 1.0
-              }
-            }
-          ],
-          "name": "unicopia:butterfly"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/entities/loot_bug.json b/src/main/resources/data/unicopia/loot_tables/entities/loot_bug.json
deleted file mode 100644
index 7e8ec0ae..00000000
--- a/src/main/resources/data/unicopia/loot_tables/entities/loot_bug.json
+++ /dev/null
@@ -1,87 +0,0 @@
-{
-  "type": "minecraft:entity",
-  "pools": [
-    {
-      "rolls": 11,
-      "bonus_rolls": 0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "functions": [
-            {
-              "add": false,
-              "count": {
-                "type": "minecraft:uniform",
-                "max": 12.0,
-                "min": 6.0
-              },
-              "function": "minecraft:set_count"
-            },
-            {
-              "function": "minecraft:looting_enchant",
-              "count": {
-                "type": "minecraft:uniform",
-                "min": 0.0,
-                "max": 3.0
-              }
-            }
-          ],
-          "name": "minecraft:gold_nugget"
-        },
-        {
-          "type": "minecraft:item",
-          "functions": [
-            {
-              "add": false,
-              "count": {
-                "type": "minecraft:uniform",
-                "max": 12.0,
-                "min": 6.0
-              },
-              "function": "minecraft:set_count"
-            },
-            {
-              "function": "minecraft:looting_enchant",
-              "count": {
-                "type": "minecraft:uniform",
-                "min": 0.0,
-                "max": 3.0
-              }
-            }
-          ],
-          "name": "minecraft:iron_nugget"
-        }
-      ]
-    },
-    {
-      "rolls": 1,
-      "bonus_rolls": 0,
-      "entries": [
-        {
-          "type": "minecraft:tag",
-          "functions": [
-            {
-              "add": false,
-              "count": {
-                "type": "minecraft:uniform",
-                "max": 3.0,
-                "min": 1.0
-              },
-              "function": "minecraft:set_count"
-            },
-            {
-              "function": "minecraft:looting_enchant",
-              "count": {
-                "type": "minecraft:uniform",
-                "min": 0.0,
-                "max": 6.0
-              }
-            }
-          ],
-          "name": "unicopia:loot_bug_high_value_drops",
-          "expand": true
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/loot_tables/entities/storm_cloud.json b/src/main/resources/data/unicopia/loot_tables/entities/storm_cloud.json
deleted file mode 100644
index 8d599d1a..00000000
--- a/src/main/resources/data/unicopia/loot_tables/entities/storm_cloud.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-  "type": "minecraft:entity",
-  "pools": [
-    {
-      "rolls": 1,
-      "bonus_rolls": 0,
-      "entries": [
-        {
-          "type": "minecraft:item",
-          "functions": [
-            {
-              "add": false,
-              "count": {
-                "type": "minecraft:uniform",
-                "max": 12.0,
-                "min": 6.0
-              },
-              "function": "minecraft:set_count"
-            },
-            {
-              "function": "minecraft:looting_enchant",
-              "count": {
-                "type": "minecraft:uniform",
-                "min": 0.0,
-                "max": 1.0
-              }
-            }
-          ],
-          "name": "unicopia:cloud_lump"
-        }
-      ]
-    }
-  ]
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/cooked_fish.json b/src/main/resources/data/unicopia/tags/items/food_types/cooked_fish.json
deleted file mode 100644
index 216c3f5d..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/cooked_fish.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "#c:cooked_fish",
-    { "id": "farmersdelight:fish_stew", "required": false },
-    { "id": "farmersdelight:baked_cod_stew", "required": false },
-    { "id": "farmersdelight:grilled_salmon", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/cooked_insect.json b/src/main/resources/data/unicopia/tags/items/food_types/cooked_insect.json
deleted file mode 100644
index 8e23aa37..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/cooked_insect.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "#c:cooked_insects"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/cooked_meat.json b/src/main/resources/data/unicopia/tags/items/food_types/cooked_meat.json
index 9860573f..f3e131a6 100644
--- a/src/main/resources/data/unicopia/tags/items/food_types/cooked_meat.json
+++ b/src/main/resources/data/unicopia/tags/items/food_types/cooked_meat.json
@@ -1,7 +1,7 @@
 {
   "replace": false,
   "values": [
-    "#c:cooked_meats",
+    "#c:cooked_meat",
     { "id": "farmersdelight:chicken_soup", "required": false },
     { "id": "farmersdelight:bacon_and_eggs", "required": false },
     { "id": "farmersdelight:pasta_with_meatballs", "required": false },
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/love.json b/src/main/resources/data/unicopia/tags/items/food_types/love.json
index 72d0be9d..d9127025 100644
--- a/src/main/resources/data/unicopia/tags/items/food_types/love.json
+++ b/src/main/resources/data/unicopia/tags/items/food_types/love.json
@@ -1,6 +1,6 @@
 {
   "replace": false,
   "values": [
-    "#c:love"
+    "#unicopia:container_with_love"
   ]
 }
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/raw_fish.json b/src/main/resources/data/unicopia/tags/items/food_types/raw_fish.json
deleted file mode 100644
index 1fb0b027..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/raw_fish.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "#c:raw_fish",
-    { "id": "farmersdelight:cod_roll", "required": false },
-    { "id": "farmersdelight:salmon_roll", "required": false },
-    { "id": "farmersdelight:salmon_slice", "required": false },
-    { "id": "farmersdelight:cod_slice", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/raw_insect.json b/src/main/resources/data/unicopia/tags/items/food_types/raw_insect.json
deleted file mode 100644
index 24b2cff4..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/raw_insect.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "#c:raw_insects"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/raw_meat.json b/src/main/resources/data/unicopia/tags/items/food_types/raw_meat.json
index dcf23921..e48e50ee 100644
--- a/src/main/resources/data/unicopia/tags/items/food_types/raw_meat.json
+++ b/src/main/resources/data/unicopia/tags/items/food_types/raw_meat.json
@@ -1,7 +1,7 @@
 {
   "replace": false,
   "values": [
-    "#c:raw_meats",
+    "#c:raw_meat",
     { "id": "farmersdelight:ham", "required": false }
   ]
 }
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/rotten_meat.json b/src/main/resources/data/unicopia/tags/items/food_types/rotten_meat.json
deleted file mode 100644
index 4dee31d1..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/rotten_meat.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "#c:rotten_meats"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/groups/bat_pony.json b/src/main/resources/data/unicopia/tags/items/groups/bat_pony.json
index 8c547253..d8ace57f 100644
--- a/src/main/resources/data/unicopia/tags/items/groups/bat_pony.json
+++ b/src/main/resources/data/unicopia/tags/items/groups/bat_pony.json
@@ -1,7 +1,7 @@
 {
   "replace": false,
   "values": [
-    "#unicopia:food_types/raw_insect",
+    "#c:raw_insect",
     "#unicopia:polearms",
     "unicopia:mango_leaves",
     "unicopia:mango_sapling",
diff --git a/src/main/resources/data/unicopia/tags/items/groups/changeling.json b/src/main/resources/data/unicopia/tags/items/groups/changeling.json
index 3cc31831..8c094993 100644
--- a/src/main/resources/data/unicopia/tags/items/groups/changeling.json
+++ b/src/main/resources/data/unicopia/tags/items/groups/changeling.json
@@ -12,10 +12,10 @@
     "unicopia:mysterious_egg",
     "unicopia:hive",
     "unicopia:dark_oak_stable_door",
-    "#unicopia:food_types/cooked_meat",
+    "#c:cooked_meat",
     "#unicopia:food_types/raw_meat",
-    "#unicopia:food_types/raw_insect",
-    "#unicopia:food_types/rotten_meat",
-    "#unicopia:food_types/love"
+    "#c:raw_insect",
+    "#c:rotten_meat",
+    "#unicopia:container_with_love"
   ]
 }
diff --git a/src/main/resources/data/unicopia/tags/items/groups/pegasus.json b/src/main/resources/data/unicopia/tags/items/groups/pegasus.json
index b76a934d..59b825f5 100644
--- a/src/main/resources/data/unicopia/tags/items/groups/pegasus.json
+++ b/src/main/resources/data/unicopia/tags/items/groups/pegasus.json
@@ -24,8 +24,8 @@
     "unicopia:cloud_door",
     "unicopia:cloud_bed",
     "#unicopia:bed_sheets",
-    "#unicopia:food_types/raw_fish",
-    "#unicopia:food_types/cooked_fish",
+    "#c:raw_fish",
+    "#c:cooked_fish",
     "unicopia:rain_cloud_jar",
     "unicopia:storm_cloud_jar",
     "unicopia:lightning_jar",
diff --git a/src/main/resources/data/unicopia/traits/love.json b/src/main/resources/data/unicopia/traits/love.json
index 534e5a40..af8df4e3 100644
--- a/src/main/resources/data/unicopia/traits/love.json
+++ b/src/main/resources/data/unicopia/traits/love.json
@@ -2,7 +2,7 @@
   "replace": false,
   "traits": "happiness:10 kindness:10",
   "items": [
-    "#c:love",
+    "#unicopia:container_with_love",
     "#unicopia:clouds"
   ]
 }
\ No newline at end of file

From a87257de83076da07a612dbc334a8fb93c6c2061 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Tue, 2 Apr 2024 08:30:42 +0100
Subject: [PATCH 09/21] Use the id of the food group as the name rather than
 the tag

---
 .../unicopia/diet/DietProfile.java            | 14 ++--
 .../unicopia/diet/DietsLoader.java            |  4 +-
 .../minelittlepony/unicopia/diet/Effect.java  | 63 +++++-----------
 .../unicopia/diet/FoodGroup.java              | 48 +++++++++++++
 .../unicopia/diet/FoodGroupEffects.java       | 42 +++++++++++
 .../unicopia/diet/PonyDiets.java              | 12 +++-
 .../resources/assets/unicopia/lang/en_us.json | 71 +++++++++----------
 7 files changed, 160 insertions(+), 94 deletions(-)
 create mode 100644 src/main/java/com/minelittlepony/unicopia/diet/FoodGroup.java
 create mode 100644 src/main/java/com/minelittlepony/unicopia/diet/FoodGroupEffects.java

diff --git a/src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java b/src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java
index 4638ed04..18ceaefb 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java
@@ -30,23 +30,23 @@ public record DietProfile(
         float defaultMultiplier,
         float foragingMultiplier,
         List<Multiplier> multipliers,
-        List<Effect> effects,
-        Optional<Effect> defaultEffect
+        List<FoodGroupEffects> effects,
+        Optional<FoodGroupEffects> defaultEffect
     ) {
     public static final DietProfile EMPTY = new DietProfile(1, 1, List.of(), List.of(), Optional.empty());
     public static final Codec<DietProfile> CODEC = RecordCodecBuilder.create(instance -> instance.group(
                 Codec.FLOAT.fieldOf("default_multiplier").forGetter(DietProfile::defaultMultiplier),
                 Codec.FLOAT.fieldOf("foraging_multiplier").forGetter(DietProfile::foragingMultiplier),
                 Codec.list(Multiplier.CODEC).fieldOf("multipliers").forGetter(DietProfile::multipliers),
-                Codec.list(Effect.PROFILE_CODEC).fieldOf("effects").forGetter(DietProfile::effects),
-                Effect.CODEC.optionalFieldOf("default_effect").forGetter(DietProfile::defaultEffect)
+                Codec.list(FoodGroupEffects.CODEC).fieldOf("effects").forGetter(DietProfile::effects),
+                FoodGroupEffects.CODEC.optionalFieldOf("default_effect").forGetter(DietProfile::defaultEffect)
     ).apply(instance, DietProfile::new));
 
     public DietProfile(PacketByteBuf buffer) {
         this(buffer.readFloat(), buffer.readFloat(),
                 buffer.readList(Multiplier::new),
-                buffer.readList(b -> new Effect(b, FoodGroupKey.LOOKUP)),
-                buffer.readOptional(b -> new Effect(b, FoodGroupKey.LOOKUP))
+                buffer.readList(b -> new FoodGroupEffects(b, FoodGroupKey.LOOKUP)),
+                buffer.readOptional(b -> new FoodGroupEffects(b, FoodGroupKey.LOOKUP))
         );
     }
 
@@ -81,7 +81,7 @@ public record DietProfile(
     }
 
     public Optional<Effect> findEffect(ItemStack stack) {
-        return effects.stream().filter(m -> m.test(stack)).findFirst().or(this::defaultEffect);
+        return effects.stream().filter(m -> m.test(stack)).findFirst().or(this::defaultEffect).map(Effect.class::cast);
     }
 
     static boolean isForaged(ItemStack stack) {
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/DietsLoader.java b/src/main/java/com/minelittlepony/unicopia/diet/DietsLoader.java
index f517c7a2..7da493ea 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/DietsLoader.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/DietsLoader.java
@@ -37,10 +37,10 @@ public class DietsLoader implements IdentifiableResourceReloadListener {
             Map<Identifier, Effect> foodGroups = new HashMap<>();
             for (var group : loadData(manager, prepareExecutor, "diets/food_groups").entrySet()) {
                 try {
-                    Effect.CODEC.parse(JsonOps.INSTANCE, group.getValue())
+                    FoodGroup.CODEC.parse(JsonOps.INSTANCE, group.getValue())
                         .resultOrPartial(error -> LOGGER.error("Could not load food group {}: {}", group.getKey(), error))
                         .ifPresent(value -> {
-                            foodGroups.put(group.getKey(), value);
+                            foodGroups.put(group.getKey(), new FoodGroup(group.getKey(), value));
                         });
                 } catch (Throwable t) {
                     LOGGER.error("Could not load food effects {}", group.getKey(), t);
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/Effect.java b/src/main/java/com/minelittlepony/unicopia/diet/Effect.java
index b8ce1338..af9ad3d9 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/Effect.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/Effect.java
@@ -2,60 +2,31 @@ package com.minelittlepony.unicopia.diet;
 
 import java.util.List;
 import java.util.Optional;
-import java.util.function.Function;
 import java.util.function.Predicate;
 
-import com.minelittlepony.unicopia.entity.player.Pony;
-import com.mojang.serialization.Codec;
-import com.mojang.serialization.codecs.RecordCodecBuilder;
-
 import net.minecraft.client.item.TooltipContext;
 import net.minecraft.item.FoodComponent;
 import net.minecraft.item.ItemStack;
 import net.minecraft.network.PacketByteBuf;
 import net.minecraft.text.Text;
 import net.minecraft.util.Formatting;
-import net.minecraft.util.Identifier;
 import net.minecraft.util.UseAction;
-import net.minecraft.util.Util;
 
-public record Effect(
-        List<FoodGroupKey> tags,
-        Optional<FoodComponent> foodComponent,
-        Ailment ailment
-) implements Predicate<ItemStack> {
-    public static final Effect EMPTY = new Effect(List.of(), Optional.empty(), Ailment.EMPTY);
-    public static final Codec<Effect> CODEC = RecordCodecBuilder.create(instance -> instance.group(
-            FoodGroupKey.TAG_CODEC.listOf().fieldOf("tags").forGetter(Effect::tags),
-            FoodAttributes.CODEC.optionalFieldOf("food_component").forGetter(Effect::foodComponent),
-            Ailment.CODEC.fieldOf("ailment").forGetter(Effect::ailment)
-    ).apply(instance, Effect::new));
-    public static final Codec<Effect> PROFILE_CODEC = RecordCodecBuilder.create(instance -> instance.group(
-            FoodGroupKey.CODEC.listOf().fieldOf("tags").forGetter(Effect::tags),
-            FoodAttributes.CODEC.optionalFieldOf("food_component").forGetter(Effect::foodComponent),
-            Ailment.CODEC.fieldOf("ailment").forGetter(Effect::ailment)
-    ).apply(instance, Effect::new));
+public interface Effect extends Predicate<ItemStack> {
+    Effect EMPTY = new FoodGroupEffects(List.of(), Optional.empty(), Ailment.EMPTY);
 
-    public Effect(PacketByteBuf buffer, Function<Identifier, FoodGroupKey> lookup) {
-        this(buffer.readList(b -> lookup.apply(b.readIdentifier())), buffer.readOptional(FoodAttributes::read), new Ailment(buffer));
-    }
+    List<FoodGroupKey> tags();
 
-    public void afflict(Pony pony, ItemStack stack) {
-        ailment().effects().afflict(pony.asEntity(), stack);
-    }
+    Optional<FoodComponent> foodComponent();
 
-    public void appendTooltip(ItemStack stack, List<Text> tooltip, TooltipContext context) {
-        int size = tooltip.size();
-        tags.forEach(tag -> {
-            if (tag.contains(stack)) {
-                tooltip.add(Text.literal(" ").append(Text.translatable(Util.createTranslationKey("tag", tag.id()))).formatted(Formatting.GRAY));
-            }
-        });
-        if (tooltip.size() == size) {
+    Ailment ailment();
+
+    default void appendTooltip(ItemStack stack, List<Text> tooltip, TooltipContext context) {
+        if (!test(stack)) {
             if (stack.isFood()) {
-                tooltip.add(Text.literal(" ").append(Text.translatable("tag.unicopia.food_types.misc")).formatted(Formatting.GRAY));
+                tooltip.add(Text.literal(" ").append(Text.translatable("food_group.unicopia.misc")).formatted(Formatting.GRAY));
             } else if (stack.getUseAction() == UseAction.DRINK) {
-                tooltip.add(Text.literal(" ").append(Text.translatable("tag.unicopia.food_types.drinks")).formatted(Formatting.GRAY));
+                tooltip.add(Text.literal(" ").append(Text.translatable("food_group.unicopia.drinks")).formatted(Formatting.GRAY));
             }
         }
 
@@ -67,14 +38,14 @@ public record Effect(
         }
     }
 
-    public void toBuffer(PacketByteBuf buffer) {
-        buffer.writeCollection(tags, (b, t) -> b.writeIdentifier(t.id()));
-        buffer.writeOptional(foodComponent, FoodAttributes::write);
-        ailment.toBuffer(buffer);
+    default void toBuffer(PacketByteBuf buffer) {
+        buffer.writeCollection(tags(), (b, t) -> b.writeIdentifier(t.id()));
+        buffer.writeOptional(foodComponent(), FoodAttributes::write);
+        ailment().toBuffer(buffer);
     }
 
     @Override
-    public boolean test(ItemStack stack) {
-        return tags.stream().anyMatch(tag -> tag.contains(stack));
+    default boolean test(ItemStack stack) {
+        return tags().stream().anyMatch(tag -> tag.contains(stack));
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/FoodGroup.java b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroup.java
new file mode 100644
index 00000000..9c6279b5
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroup.java
@@ -0,0 +1,48 @@
+package com.minelittlepony.unicopia.diet;
+
+import java.util.List;
+import java.util.Optional;
+
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.codecs.RecordCodecBuilder;
+
+import net.minecraft.client.item.TooltipContext;
+import net.minecraft.item.FoodComponent;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.Util;
+
+public record FoodGroup(
+        Identifier id,
+        List<FoodGroupKey> tags,
+        Optional<FoodComponent> foodComponent,
+        Ailment ailment) implements Effect {
+    public static final Codec<FoodGroupEffects> CODEC = RecordCodecBuilder.create(instance -> instance.group(
+            FoodGroupKey.TAG_CODEC.listOf().fieldOf("tags").forGetter(FoodGroupEffects::tags),
+            FoodAttributes.CODEC.optionalFieldOf("food_component").forGetter(FoodGroupEffects::foodComponent),
+            Ailment.CODEC.fieldOf("ailment").forGetter(FoodGroupEffects::ailment)
+    ).apply(instance, FoodGroupEffects::new));
+
+    public FoodGroup(Identifier id, Effect effect) {
+        this(id, effect.tags(), effect.foodComponent(), effect.ailment());
+    }
+
+    public FoodGroup(PacketByteBuf buffer) {
+        this(buffer.readIdentifier(), new FoodGroupEffects(buffer, FoodGroupKey.TAG_ID_LOOKUP));
+    }
+
+    @Override
+    public void appendTooltip(ItemStack stack, List<Text> tooltip, TooltipContext context) {
+        tooltip.add(Text.literal(" ").append(Text.translatable(Util.createTranslationKey("food_group", id()))).formatted(Formatting.GRAY));
+        Effect.super.appendTooltip(stack, tooltip, context);
+    }
+
+    @Override
+    public void toBuffer(PacketByteBuf buffer) {
+        buffer.writeIdentifier(id());
+        Effect.super.toBuffer(buffer);
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupEffects.java b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupEffects.java
new file mode 100644
index 00000000..e92f2995
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupEffects.java
@@ -0,0 +1,42 @@
+package com.minelittlepony.unicopia.diet;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.codecs.RecordCodecBuilder;
+
+import net.minecraft.client.item.TooltipContext;
+import net.minecraft.item.FoodComponent;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.PacketByteBuf;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.Util;
+
+public record FoodGroupEffects(
+        List<FoodGroupKey> tags,
+        Optional<FoodComponent> foodComponent,
+        Ailment ailment
+) implements Effect {
+    public static final Codec<FoodGroupEffects> CODEC = RecordCodecBuilder.create(instance -> instance.group(
+            FoodGroupKey.CODEC.listOf().fieldOf("tags").forGetter(FoodGroupEffects::tags),
+            FoodAttributes.CODEC.optionalFieldOf("food_component").forGetter(FoodGroupEffects::foodComponent),
+            Ailment.CODEC.fieldOf("ailment").forGetter(FoodGroupEffects::ailment)
+    ).apply(instance, FoodGroupEffects::new));
+
+    public FoodGroupEffects(PacketByteBuf buffer, Function<Identifier, FoodGroupKey> lookup) {
+        this(buffer.readList(b -> lookup.apply(b.readIdentifier())), buffer.readOptional(FoodAttributes::read), new Ailment(buffer));
+    }
+
+    @Override
+    public void appendTooltip(ItemStack stack, List<Text> tooltip, TooltipContext context) {
+        tags.forEach(tag -> {
+            if (tag.contains(stack)) {
+                tooltip.add(Text.literal(" ").append(Text.translatable(Util.createTranslationKey("tag", tag.id()))).formatted(Formatting.GRAY));
+            }
+        });
+        Effect.super.appendTooltip(stack, tooltip, context);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/PonyDiets.java b/src/main/java/com/minelittlepony/unicopia/diet/PonyDiets.java
index e233871d..54990fee 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/PonyDiets.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/PonyDiets.java
@@ -1,8 +1,11 @@
 package com.minelittlepony.unicopia.diet;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
 
 import org.jetbrains.annotations.Nullable;
 
@@ -47,12 +50,15 @@ public class PonyDiets implements DietView {
     }
 
     public PonyDiets(PacketByteBuf buffer) {
-        this(buffer.readMap(b -> b.readRegistryValue(Race.REGISTRY), DietProfile::new), buffer.readMap(PacketByteBuf::readIdentifier, b -> new Effect(b, FoodGroupKey.TAG_ID_LOOKUP)));
+        this(
+                buffer.readMap(b -> b.readRegistryValue(Race.REGISTRY), DietProfile::new),
+                buffer.readCollection(ArrayList::new, FoodGroup::new).stream().collect(Collectors.toMap(FoodGroup::id, Function.identity()))
+        );
     }
 
     public void toBuffer(PacketByteBuf buffer) {
         buffer.writeMap(diets, (b, r) -> b.writeRegistryValue(Race.REGISTRY, r), (b, e) -> e.toBuffer(b));
-        buffer.writeMap(effects, PacketByteBuf::writeIdentifier, (b, e) -> e.toBuffer(b));
+        buffer.writeCollection(effects.values(), (b, e) -> e.toBuffer(b));
     }
 
     private DietProfile getDiet(Pony pony) {
@@ -77,7 +83,7 @@ public class PonyDiets implements DietView {
     @Override
     public void finishUsing(ItemStack stack, World world, LivingEntity entity) {
         if (initEdibility(stack, entity)) {
-            Pony.of(entity).ifPresent(pony -> getEffects(stack, pony).afflict(pony, stack));
+            Pony.of(entity).ifPresent(pony -> getEffects(stack, pony).ailment().effects().afflict(pony.asEntity(), stack));
         }
     }
 
diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json
index 6f5978b0..8552f3f2 100644
--- a/src/main/resources/assets/unicopia/lang/en_us.json
+++ b/src/main/resources/assets/unicopia/lang/en_us.json
@@ -619,42 +619,41 @@
   "unicopia.diet.hunger": "Hunger Ratio: %s%%",
   "unicopia.diet.saturation": "Saturation Ratio: %s%%",
   
-  "tag.unicopia.food_types.rotten_meat": "Rotting Meat",
-  "tag.unicopia.food_types.raw_meat": "Fresh Meat",
-  "tag.unicopia.food_types.cooked_meat": "Prepared Meat",
-  "tag.c.raw_fish": "Fresh Fish",
-  "tag.c.cooked_fish": "Prepared Fish",
-  "tag.c.raw_insect": "Bugs & Insects",
-  "tag.unicopia.food_types.cooked_insect": "Cooked Bugs & Insects",
-  "tag.unicopia.food_types.nuts_and_seeds": "Nuts & Seeds",
-  "tag.unicopia.container_with_love": "Love",
-  "tag.unicopia.food_types.rocks": "Rocks",
-  "tag.unicopia.food_types.pinecone": "Nuts & Seeds",
-  "tag.unicopia.food_types.bat_ponys_delight": "Bat Pony Treats",
-  "tag.unicopia.food_types.cooked_sea_vegitable": "Prepared Fish Food",
-  "tag.unicopia.food_types.raw_sea_vegitable": "Fresh Fish Food",
-  "tag.unicopia.food_types.shells": "Sea Shells",
-  "tag.unicopia.food_types.shelly": "Sea Shells",
-  "tag.unicopia.food_types.candy": "Candy",
-  "tag.unicopia.food_types.desserts": "Desserts",
-  "tag.unicopia.food_types.fruit": "Fruit",
-  "tag.unicopia.food_types.baked_goods": "Baked Goods",
-  "tag.unicopia.food_types.misc": "Misc",
-  "tag.unicopia.food_types.fruits_and_vegetables": "Fruits & Vegetables",
-  "tag.unicopia.food_types.drinks": "Drinks",
-  "tag.minecraft.leaves": "Leaves",
-
-  "tag.unicopia.food_types.forage_edible_filling": "Bulky Plant Matter",
-  "tag.unicopia.food_types.forage_edible": "Plant Matter",
-  "tag.unicopia.food_types.forage_nauseating": "Nauseating",
-  "tag.unicopia.food_types.forage_prickly": "Prickly",
-  "tag.unicopia.food_types.forage_risky": "Unsafe",
-  "tag.unicopia.food_types.forage_strengthening": "Strength Enhancing",
-  "tag.unicopia.food_types.forage_severely_prickly": "Very Prickly",
-  "tag.unicopia.food_types.forage_severely_nauseating": "Sickening",
-  "tag.unicopia.food_types.forage_radioactive": "Glowy",
-  "tag.unicopia.food_types.forage_dangerous": "Dangerous",
-  "tag.unicopia.food_types.forage_blinding": "Toxic",
+  "food_group.unicopia.meat.rotten": "Rotting Meat",
+  "food_group.unicopia.meat.raw": "Fresh Meat",
+  "food_group.unicopia.meat.cooked": "Prepared Meat",
+  "food_group.unicopia.fish.raw": "Fresh Fish",
+  "food_group.unicopia.fish.cooked": "Prepared Fish",
+  "food_group.unicopia.insect.raw": "Bugs & Insects",
+  "food_group.unicopia.fish.cooked": "Cooked Bugs & Insects",
+  "food_group.unicopia.nuts_and_seeds": "Nuts & Seeds",
+  "food_group.unicopia.love": "Love",
+  "food_group.unicopia.rocks": "Rocks",
+  "food_group.unicopia.pinecone": "Nuts & Seeds",
+  "food_group.unicopia.bat_ponys_delight": "Bat Pony Treats",
+  "food_group.unicopia.sea_vegitable.cooked": "Prepared Fish Food",
+  "food_group.unicopia.sea_vegitable.raw": "Fresh Fish Food",
+  "food_group.unicopia.shells": "Sea Shells",
+  "food_group.unicopia.shelly": "Sea Shells",
+  "food_group.unicopia.candy": "Candy",
+  "food_group.unicopia.desserts": "Desserts",
+  "food_group.unicopia.fruit": "Fruit",
+  "food_group.unicopia.baked_goods": "Baked Goods",
+  "food_group.unicopia.misc": "Misc",
+  "food_group.unicopia.fruits_and_vegetables": "Fruits & Vegetables",
+  "food_group.unicopia.drinks": "Drinks",
+  "food_group.unicopia.foraging.edible_filling": "Bulky Plant Matter",
+  "food_group.unicopia.foraging.edible": "Plant Matter",
+  "food_group.unicopia.foraging.nauseating": "Nauseating",
+  "food_group.unicopia.foraging.prickly": "Prickly",
+  "food_group.unicopia.foraging.risky": "Unsafe",
+  "food_group.unicopia.foraging.strengthening": "Strength Enhancing",
+  "food_group.unicopia.foraging.severely_prickly": "Very Prickly",
+  "food_group.unicopia.foraging.severely_nauseating": "Sickening",
+  "food_group.unicopia.foraging.radioactive": "Glowy",
+  "food_group.unicopia.foraging.dangerous": "Dangerous",
+  "food_group.unicopia.foraging.blinding": "Toxic",
+  "food_group.unicopia.foraging.leafy_greens": "Leafy Greens",
 
   "toxicity.safe.name": "Safe",
   "toxicity.mild.name": "Mildly Toxic",

From 4f613ebb910604abb0e997dd9cf0115d0b6a34ab Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Tue, 2 Apr 2024 11:27:38 +0100
Subject: [PATCH 10/21] Adjust food categorizations and move some more tags to
 datagen

---
 .../unicopia/UConventionalTags.java           |  3 +
 .../com/minelittlepony/unicopia/UTags.java    |  5 +-
 .../providers/tag/UItemTagProvider.java       | 66 ++++++++++++++++++-
 .../unicopia/diet/FoodGroupKey.java           | 11 ++++
 .../diets/food_groups/baked_goods.json        |  2 +-
 .../diets/food_groups/bat_ponys_delight.json  |  2 +-
 .../unicopia/diets/food_groups/candy.json     |  2 +-
 .../unicopia/diets/food_groups/desserts.json  |  2 +-
 .../unicopia/diets/food_groups/fruit.json     |  2 +-
 .../diets/food_groups/meat/cooked.json        |  2 +-
 .../unicopia/diets/food_groups/meat/raw.json  |  2 +-
 .../diets/food_groups/nuts_and_seeds.json     |  2 +-
 .../unicopia/diets/food_groups/pinecone.json  |  2 +-
 .../unicopia/diets/food_groups/rocks.json     |  2 +-
 .../food_groups/sea_vegetable/cooked.json     |  2 +-
 .../diets/food_groups/sea_vegetable/raw.json  |  2 +-
 .../unicopia/diets/food_groups/shells.json    |  2 +-
 .../unicopia/diets/food_groups/shelly.json    |  2 +-
 .../tags/items/food_types/baked_goods.json    | 32 ---------
 .../items/food_types/bat_ponys_delight.json   |  8 ---
 .../unicopia/tags/items/food_types/candy.json | 10 ---
 .../tags/items/food_types/cooked_meat.json    | 24 -------
 .../food_types/cooked_sea_vegitable.json      |  3 +-
 .../tags/items/food_types/desserts.json       | 12 ----
 .../unicopia/tags/items/food_types/fruit.json | 19 ------
 .../unicopia/tags/items/food_types/love.json  |  6 --
 .../tags/items/food_types/nuts_and_seeds.json | 11 ----
 .../tags/items/food_types/pinecone.json       |  6 --
 .../tags/items/food_types/raw_meat.json       |  7 --
 .../items/food_types/raw_sea_vegitable.json   |  3 -
 .../unicopia/tags/items/food_types/rocks.json |  6 --
 .../tags/items/food_types/shells.json         | 11 ----
 .../tags/items/food_types/shelly.json         |  6 --
 .../tags/items/groups/changeling.json         |  2 +-
 .../tags/items/groups/earth_pony.json         |  2 +
 .../unicopia/tags/items/groups/sea_pony.json  |  5 +-
 36 files changed, 101 insertions(+), 185 deletions(-)
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/baked_goods.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/bat_ponys_delight.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/candy.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/cooked_meat.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/desserts.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/fruit.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/love.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/nuts_and_seeds.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/pinecone.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/raw_meat.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/rocks.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/shells.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/shelly.json

diff --git a/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java b/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
index a4be14e6..0b1942ce 100644
--- a/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
+++ b/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
@@ -41,6 +41,7 @@ public interface UConventionalTags {
         TagKey<Item> MUFFINS = item("muffins");
         TagKey<Item> MANGOES = item("mangoes");
         TagKey<Item> OATMEALS = item("oatmeals");
+        TagKey<Item> COOKIES = item("cookies");
 
         TagKey<Item> FRUITS = item("fruits");
         TagKey<Item> WORMS = item("worms");
@@ -55,6 +56,8 @@ public interface UConventionalTags {
         TagKey<Item> RAW_MEAT = item("raw_meat");
         TagKey<Item> COOKED_MEAT = item("cooked_meat");
         TagKey<Item> ROTTEN_MEAT = item("rotten_meat");
+        TagKey<Item> DESSERTS = item("desserts");
+        TagKey<Item> CANDY = item("candy");
 
         TagKey<Item> CROPS_PEANUTS = item("crops/peanuts");
         TagKey<Item> TOOL_KNIVES = item("tools/knives");
diff --git a/src/main/java/com/minelittlepony/unicopia/UTags.java b/src/main/java/com/minelittlepony/unicopia/UTags.java
index cb045541..24fd3a3f 100644
--- a/src/main/java/com/minelittlepony/unicopia/UTags.java
+++ b/src/main/java/com/minelittlepony/unicopia/UTags.java
@@ -41,7 +41,10 @@ public interface UTags {
         TagKey<Item> LOOT_BUG_RARE_DROPS = item("loot_bug_rare_drops");
         TagKey<Item> LOOT_BUG_EPIC_DROPS = item("loot_bug_epic_drops");
 
-        TagKey<Item> SHELLS = item("food_types/shells");
+        TagKey<Item> SHELLS = item("shells");
+        TagKey<Item> SPECIAL_SHELLS = item("special_shells");
+        TagKey<Item> ROCK_STEWS = item("rock_stews");
+        TagKey<Item> BAKED_GOODS = item("baked_goods");
 
         TagKey<Item> POLEARMS = item("polearms");
         TagKey<Item> HORSE_SHOES = item("horse_shoes");
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
index f54b7751..7187116a 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
@@ -127,6 +127,9 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
                 Items.DIAMOND_PICKAXE, Items.DIAMOND_SHOVEL, Items.DIAMOND_AXE, Items.DIAMOND_SWORD, Items.DIAMOND_HOE,
                 UItems.DIAMOND_POLEARM
         ).forceAddTag(UTags.Items.BADGES).forceAddTag(ConventionalItemTags.GOLD_INGOTS);
+        getOrCreateTagBuilder(UTags.Items.SHELLS).add(Items.NAUTILUS_SHELL, UItems.CLAM_SHELL, UItems.SCALLOP_SHELL, UItems.TURRET_SHELL);
+        getOrCreateTagBuilder(UTags.Items.SPECIAL_SHELLS).add(UItems.SHELLY);
+        getOrCreateTagBuilder(UTags.Items.ROCK_STEWS).add(UItems.ROCK_STEW);
 
         exportFarmersDelightItems();
     }
@@ -209,15 +212,31 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
         getOrCreateTagBuilder(UConventionalTags.Items.SEEDS).add(Items.BEETROOT_SEEDS, Items.MELON_SEEDS, Items.PUMPKIN_SEEDS, Items.TORCHFLOWER_SEEDS, Items.WHEAT_SEEDS)
             .add(UItems.OAT_SEEDS)
             .forceAddTag(UTags.Items.APPLE_SEEDS);
+        getOrCreateTagBuilder(UConventionalTags.Items.COOKIES).add(Items.COOKIE, UItems.OATMEAL_COOKIE, UItems.CHOCOLATE_OATMEAL_COOKIE, UItems.PINECONE_COOKIE);
         getOrCreateTagBuilder(UConventionalTags.Items.OATMEALS).add(UItems.OATMEAL);
         getOrCreateTagBuilder(UConventionalTags.Items.GRAIN).add(Items.WHEAT, UItems.OATS);
-        getOrCreateTagBuilder(UConventionalTags.Items.NUTS).addOptionalTag(UConventionalTags.Items.CROPS_PEANUTS);
-
+        getOrCreateTagBuilder(UConventionalTags.Items.NUTS).add(UItems.BOWL_OF_NUTS)
+            .addOptionalTag(UConventionalTags.Items.CROPS_PEANUTS)
+            .forceAddTag(UConventionalTags.Items.ACORNS)
+            .addOptional(new Identifier("garnished", "nuts"))
+            .addOptional(new Identifier("garnished", "nut_mix"))
+            .addOptional(new Identifier("garnished", "neverable_delecacies"));
         getOrCreateTagBuilder(UConventionalTags.Items.FRUITS)
+            .add(Items.MELON_SLICE, Items.SWEET_BERRIES, Items.GLOW_BERRIES, Items.CHORUS_FRUIT)
+            .add(UItems.JUICE, UItems.ZAP_APPLE, UItems.ZAP_BULB)
             .forceAddTag(UConventionalTags.Items.MANGOES)
             .forceAddTag(UConventionalTags.Items.PINEAPPLES)
             .forceAddTag(UConventionalTags.Items.APPLES)
-            .forceAddTag(UConventionalTags.Items.BANANAS);
+            .forceAddTag(UConventionalTags.Items.BANANAS)
+            .addOptionalTag(new Identifier("garnished", "berries"));
+        getOrCreateTagBuilder(UConventionalTags.Items.DESSERTS).add(Items.CAKE, UItems.APPLE_PIE_SLICE).forceAddTag(UTags.Items.PIES);
+        getOrCreateTagBuilder(UConventionalTags.Items.CANDY).add(Items.SUGAR, UItems.ROCK_CANDY, UItems.CANDIED_APPLE);
+        getOrCreateTagBuilder(UTags.Items.BAKED_GOODS).add(
+                Items.BREAD, Items.COOKIE, Items.PUMPKIN_PIE,
+                UItems.MUFFIN, UItems.SCONE, UItems.COOKED_ZAP_APPLE, UItems.TOAST, UItems.BURNED_TOAST, UItems.JAM_TOAST, UItems.IMPORTED_OATS,
+                UItems.HAY_FRIES, UItems.CRISPY_HAY_FRIES, UItems.HORSE_SHOE_FRIES)
+            .forceAddTag(UConventionalTags.Items.OATMEALS)
+            .forceAddTag(UConventionalTags.Items.COOKIES);
     }
 
     private void exportFarmersDelightItems() {
@@ -235,5 +254,46 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
             .addOptional(new Identifier("farmersdelight", "fish_stew"))
             .addOptional(new Identifier("farmersdelight", "baked_cod_stew"))
             .addOptional(new Identifier("farmersdelight", "grilled_salmon"));
+        getOrCreateTagBuilder(UConventionalTags.Items.RAW_MEAT)
+            .addOptional(new Identifier("farmersdelight", "ham"));
+        getOrCreateTagBuilder(UConventionalTags.Items.COOKED_MEAT)
+            .addOptional(new Identifier("farmersdelight", "chicken_soup"))
+            .addOptional(new Identifier("farmersdelight", "bacon_and_eggs"))
+            .addOptional(new Identifier("farmersdelight", "pasta_with_meatballs"))
+            .addOptional(new Identifier("farmersdelight", "beef_stew"))
+            .addOptional(new Identifier("farmersdelight", "bone_broth"))
+            .addOptional(new Identifier("farmersdelight", "mutton_wrap"))
+            .addOptional(new Identifier("farmersdelight", "bacon_sandwich"))
+            .addOptional(new Identifier("farmersdelight", "hamburger"))
+            .addOptional(new Identifier("farmersdelight", "chicken_sandwich"))
+            .addOptional(new Identifier("farmersdelight", "barbecue_stick"))
+            .addOptional(new Identifier("farmersdelight", "smoked_ham"))
+            .addOptional(new Identifier("farmersdelight", "honey_glazed_ham"))
+            .addOptional(new Identifier("farmersdelight", "honey_glazed_ham_block"))
+            .addOptional(new Identifier("farmersdelight", "roast_chicken"))
+            .addOptional(new Identifier("farmersdelight", "roast_chicken_block"))
+            .addOptional(new Identifier("farmersdelight", "steak_and_potatoes"))
+            .addOptional(new Identifier("farmersdelight", "roasted_mutton_chops"))
+            .addOptional(new Identifier("farmersdelight", "pasta_with_mutton_chop"));
+        getOrCreateTagBuilder(UConventionalTags.Items.FRUITS)
+            .addOptional(new Identifier("farmersdelight", "pumpkin_slice"))
+            .addOptional(new Identifier("farmersdelight", "tomato"))
+            .addOptional(new Identifier("farmersdelight", "melon_juice"))
+            .addOptional(new Identifier("farmersdelight", "fruit_salad"));
+        getOrCreateTagBuilder(UConventionalTags.Items.DESSERTS)
+            .addOptional(new Identifier("farmersdelight", "sweet_berry_cheesecake"))
+            .addOptional(new Identifier("farmersdelight", "sweet_berry_cheesecake_slice"))
+            .addOptional(new Identifier("farmersdelight", "chocolate_pie_slice"))
+            .addOptional(new Identifier("farmersdelight", "cake_slice"))
+            .addOptional(new Identifier("farmersdelight", "apple_pie_slice"))
+            .addOptional(new Identifier("farmersdelight", "glow_berry_custard"));
+        getOrCreateTagBuilder(UConventionalTags.Items.COOKIES)
+            .addOptional(new Identifier("farmersdelight", "sweet_berry_cookie"))
+            .addOptional(new Identifier("farmersdelight", "honey_cookie"));
+        getOrCreateTagBuilder(UTags.Items.BAKED_GOODS)
+            .addOptional(new Identifier("farmersdelight", "wheat_dough"))
+            .addOptional(new Identifier("farmersdelight", "raw_pasta"))
+            .addOptional(new Identifier("farmersdelight", "pie_crust"))
+            .addOptional(new Identifier("farmersdelight", "egg_sandwich"));
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupKey.java b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupKey.java
index efa49837..ac59d145 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupKey.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupKey.java
@@ -2,10 +2,13 @@ package com.minelittlepony.unicopia.diet;
 
 import java.util.function.Function;
 
+import com.minelittlepony.unicopia.Debug;
+import com.minelittlepony.unicopia.Unicopia;
 import com.mojang.serialization.Codec;
 
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
+import net.minecraft.registry.Registries;
 import net.minecraft.registry.RegistryKeys;
 import net.minecraft.registry.tag.TagKey;
 import net.minecraft.util.Identifier;
@@ -28,6 +31,7 @@ public interface FoodGroupKey {
     });
     Function<TagKey<Item>, FoodGroupKey> TAG_LOOKUP = Util.memoize(tag -> {
         return new FoodGroupKey() {
+            private boolean check;
             @Override
             public Identifier id() {
                 return tag.id();
@@ -35,6 +39,13 @@ public interface FoodGroupKey {
 
             @Override
             public boolean contains(ItemStack stack) {
+                if (Debug.CHECK_GAME_VALUES && !check) {
+                    check = true;
+                    if (Registries.ITEM.getEntryList(tag).isEmpty()) {
+                        Unicopia.LOGGER.info("Tag is empty: " + tag.id());
+                    }
+                }
+
                 return stack.isIn(tag);
             }
         };
diff --git a/src/main/resources/data/unicopia/diets/food_groups/baked_goods.json b/src/main/resources/data/unicopia/diets/food_groups/baked_goods.json
index 31306e54..12d8e949 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/baked_goods.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/baked_goods.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/baked_goods" ],
+  "tags": [ "unicopia:baked_goods" ],
   "food_component": {
     "hunger": 1,
     "saturation": 1
diff --git a/src/main/resources/data/unicopia/diets/food_groups/bat_ponys_delight.json b/src/main/resources/data/unicopia/diets/food_groups/bat_ponys_delight.json
index 0fd1f3a0..73e2acf8 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/bat_ponys_delight.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/bat_ponys_delight.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/bat_ponys_delight" ],
+  "tags": [ "c:mangoes" ],
   "food_component": {
     "hunger": 1,
     "saturation": 0.1
diff --git a/src/main/resources/data/unicopia/diets/food_groups/candy.json b/src/main/resources/data/unicopia/diets/food_groups/candy.json
index a7d7d64b..e5df4d14 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/candy.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/candy.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/candy" ],
+  "tags": [ "c:candy" ],
   "food_component": {
     "hunger": 1,
     "saturation": 1
diff --git a/src/main/resources/data/unicopia/diets/food_groups/desserts.json b/src/main/resources/data/unicopia/diets/food_groups/desserts.json
index 63913e02..4892ee6b 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/desserts.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/desserts.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/desserts" ],
+  "tags": [ "c:desserts" ],
   "food_component": {
     "hunger": 1,
     "saturation": 1
diff --git a/src/main/resources/data/unicopia/diets/food_groups/fruit.json b/src/main/resources/data/unicopia/diets/food_groups/fruit.json
index 20793232..c8710f92 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/fruit.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/fruit.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/fruit" ],
+  "tags": [ "c:fruits" ],
   "food_component": {
     "hunger": 1,
     "saturation": 1
diff --git a/src/main/resources/data/unicopia/diets/food_groups/meat/cooked.json b/src/main/resources/data/unicopia/diets/food_groups/meat/cooked.json
index 80b1e542..49207c8b 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/meat/cooked.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/meat/cooked.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/cooked_meat" ],
+  "tags": [ "c:cooked_meat" ],
   "food_component": {
     "hunger": 12,
     "saturation": 1.2
diff --git a/src/main/resources/data/unicopia/diets/food_groups/meat/raw.json b/src/main/resources/data/unicopia/diets/food_groups/meat/raw.json
index f2c547d9..18879268 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/meat/raw.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/meat/raw.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/raw_meat" ],
+  "tags": [ "c:raw_meat" ],
   "food_component": {
     "hunger": 1,
     "saturation": 1
diff --git a/src/main/resources/data/unicopia/diets/food_groups/nuts_and_seeds.json b/src/main/resources/data/unicopia/diets/food_groups/nuts_and_seeds.json
index 30f205a2..eee07b2d 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/nuts_and_seeds.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/nuts_and_seeds.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/nuts_and_seeds" ],
+  "tags": [ "c:grain", "c:nuts", "c:seeds" ],
   "food_component": {
     "hunger": 2,
     "saturation": 2.5
diff --git a/src/main/resources/data/unicopia/diets/food_groups/pinecone.json b/src/main/resources/data/unicopia/diets/food_groups/pinecone.json
index 75f92718..735bff30 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/pinecone.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/pinecone.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/pinecone" ],
+  "tags": [ "c:pinecones" ],
   "food_component": {
     "hunger": 1,
     "saturation": 0.1
diff --git a/src/main/resources/data/unicopia/diets/food_groups/rocks.json b/src/main/resources/data/unicopia/diets/food_groups/rocks.json
index ebe7b986..ead61325 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/rocks.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/rocks.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/rocks" ],
+  "tags": [ "c:rocks", "unicopia:rock_stews" ],
   "food_component": {
     "hunger": 1,
     "saturation": 0.1
diff --git a/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/cooked.json b/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/cooked.json
index 194273c0..06d46526 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/cooked.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/cooked.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/cooked_sea_vegitable" ],
+  "tags": [ "unicopia:food_types/cooked_sea_vegitable", "c:coral_blocks" ],
   "food_component": {
     "hunger": 0,
     "saturation": 0
diff --git a/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/raw.json b/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/raw.json
index 9fdb8ebf..94bea223 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/raw.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/raw.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/raw_sea_vegitable" ],
+  "tags": [ "unicopia:food_types/raw_sea_vegitable", "c:corals", "c:coral_fans" ],
   "food_component": {
     "hunger": 0,
     "saturation": 0
diff --git a/src/main/resources/data/unicopia/diets/food_groups/shells.json b/src/main/resources/data/unicopia/diets/food_groups/shells.json
index e4760a96..f154f859 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/shells.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/shells.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/shells", "unicopia:food_types/shelly" ],
+  "tags": [ "unicopia:shells" ],
   "food_component": {
     "hunger": 0,
     "saturation": 0
diff --git a/src/main/resources/data/unicopia/diets/food_groups/shelly.json b/src/main/resources/data/unicopia/diets/food_groups/shelly.json
index e4760a96..791711a0 100644
--- a/src/main/resources/data/unicopia/diets/food_groups/shelly.json
+++ b/src/main/resources/data/unicopia/diets/food_groups/shelly.json
@@ -1,5 +1,5 @@
 {
-  "tags": [ "unicopia:food_types/shells", "unicopia:food_types/shelly" ],
+  "tags": [ "unicopia:special_shells" ],
   "food_component": {
     "hunger": 0,
     "saturation": 0
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
deleted file mode 100644
index e9ac11e8..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/baked_goods.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:bread",
-    "minecraft:cookie",
-    "unicopia:muffin",
-    "unicopia:scone",
-    "#c:grain",
-    "unicopia:cooked_zap_apple",
-    "minecraft:pumpkin_pie",
-    "#unicopia:pies",
-    "unicopia:apple_pie_slice",
-    "unicopia:toast",
-    "unicopia:burned_toast",
-    "unicopia:jam_toast",
-    "unicopia:imported_oats",
-    "unicopia:oatmeal",
-    "unicopia:hay_fries",
-    "unicopia:crispy_hay_fries",
-    "unicopia:horse_shoe_fries",
-    "unicopia:oatmeal_cookie",
-    "unicopia:chocolate_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 },
-    { "id": "farmersdelight:sweet_berry_cookie", "required": false },
-    { "id": "farmersdelight:honey_cookie", "required": false },
-    { "id": "farmersdelight:egg_sandwich", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/bat_ponys_delight.json b/src/main/resources/data/unicopia/tags/items/food_types/bat_ponys_delight.json
deleted file mode 100644
index 8309c0f0..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/bat_ponys_delight.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "unicopia:mango",
-    { "id": "#c:mango", "required": false },
-    { "id": "#c:mangoes", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/candy.json b/src/main/resources/data/unicopia/tags/items/food_types/candy.json
deleted file mode 100644
index 8a152f67..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/candy.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "unicopia:rock_candy",
-    "unicopia:candied_apple",
-    "minecraft:sugar",
-    { "id": "bakersdelight:sweet_berry_cheesecake_slice", "required": false },
-    { "id": "bakersdelight:cake_slice", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/cooked_meat.json b/src/main/resources/data/unicopia/tags/items/food_types/cooked_meat.json
deleted file mode 100644
index f3e131a6..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/cooked_meat.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "#c:cooked_meat",
-    { "id": "farmersdelight:chicken_soup", "required": false },
-    { "id": "farmersdelight:bacon_and_eggs", "required": false },
-    { "id": "farmersdelight:pasta_with_meatballs", "required": false },
-    { "id": "farmersdelight:beef_stew", "required": false },
-    { "id": "farmersdelight:bone_broth", "required": false },
-    { "id": "farmersdelight:mutton_wrap", "required": false },
-    { "id": "farmersdelight:bacon_sandwich", "required": false },
-    { "id": "farmersdelight:hamburger", "required": false },
-    { "id": "farmersdelight:chicken_sandwich", "required": false },
-    { "id": "farmersdelight:barbecue_stick", "required": false },
-    { "id": "farmersdelight:smoked_ham", "required": false },
-    { "id": "farmersdelight:honey_glazed_ham", "required": false },
-    { "id": "farmersdelight:honey_glazed_ham_block", "required": false },
-    { "id": "farmersdelight:roast_chicken", "required": false },
-    { "id": "farmersdelight:roast_chicken_block", "required": false },
-    { "id": "farmersdelight:steak_and_potatoes", "required": false },
-    { "id": "farmersdelight:roasted_mutton_chops", "required": false },
-    { "id": "farmersdelight:pasta_with_mutton_chop", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/cooked_sea_vegitable.json b/src/main/resources/data/unicopia/tags/items/food_types/cooked_sea_vegitable.json
index 30ffdfce..bde823d2 100644
--- a/src/main/resources/data/unicopia/tags/items/food_types/cooked_sea_vegitable.json
+++ b/src/main/resources/data/unicopia/tags/items/food_types/cooked_sea_vegitable.json
@@ -2,7 +2,6 @@
   "replace": false,
   "values": [
     "minecraft:dried_kelp_block",
-    "minecraft:glow_lichen",
-    "#c:coral_blocks"
+    "minecraft:glow_lichen"
   ]
 }
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/desserts.json b/src/main/resources/data/unicopia/tags/items/food_types/desserts.json
deleted file mode 100644
index c88f3f26..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/desserts.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:cake",
-    { "id": "farmersdelight:sweet_berry_cheesecake", "required": false },
-    { "id": "farmersdelight:sweet_berry_cheesecake_slice", "required": false },
-    { "id": "farmersdelight:chocolate_pie_slice", "required": false },
-    { "id": "farmersdelight:cake_slice", "required": false },
-    { "id": "farmersdelight:apple_pie_slice", "required": false },
-    { "id": "farmersdelight:glow_berry_custard", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/fruit.json b/src/main/resources/data/unicopia/tags/items/food_types/fruit.json
deleted file mode 100644
index 39c40cdc..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/fruit.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "#c:fruits",
-    "unicopia:zap_apple",
-    "unicopia:zap_bulb",
-    "unicopia:rotten_apple",
-    "minecraft:melon_slice",
-    "minecraft:sweet_berries",
-    "minecraft:glow_berries",
-    "minecraft:chorus_fruit",
-    "unicopia:juice",
-    { "id": "farmersdelight:pumpkin_slice", "required": false },
-    { "id": "farmersdelight:tomato", "required": false },
-    { "id": "farmersdelight:melon_juice", "required": false },
-    { "id": "farmersdelight:fruit_salad", "required": false },
-    { "id": "#garnished:berries", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/love.json b/src/main/resources/data/unicopia/tags/items/food_types/love.json
deleted file mode 100644
index d9127025..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/love.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "#unicopia:container_with_love"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/nuts_and_seeds.json b/src/main/resources/data/unicopia/tags/items/food_types/nuts_and_seeds.json
deleted file mode 100644
index 937e7578..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/nuts_and_seeds.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "#c:seeds",
-    "#c:acorns",
-    "#c:nuts",
-    { "id": "#garnished:nuts", "required": false },
-    { "id": "#garnished:nut_mix", "required": false },
-    { "id": "#garnished:neverable_delecacies", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/pinecone.json b/src/main/resources/data/unicopia/tags/items/food_types/pinecone.json
deleted file mode 100644
index 9ad60d14..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/pinecone.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "#c:pinecones"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/raw_meat.json b/src/main/resources/data/unicopia/tags/items/food_types/raw_meat.json
deleted file mode 100644
index e48e50ee..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/raw_meat.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "#c:raw_meat",
-    { "id": "farmersdelight:ham", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/raw_sea_vegitable.json b/src/main/resources/data/unicopia/tags/items/food_types/raw_sea_vegitable.json
index c570e4a4..cc815d78 100644
--- a/src/main/resources/data/unicopia/tags/items/food_types/raw_sea_vegitable.json
+++ b/src/main/resources/data/unicopia/tags/items/food_types/raw_sea_vegitable.json
@@ -5,9 +5,6 @@
     "minecraft:dried_kelp",
     "minecraft:seagrass",
     "minecraft:sea_pickle",
-    "#c:corals",
-    "#c:coral_fans",
-    { "id": "farmersdelight:melon_popsicle", "required": false },
     { "id": "farmersdelight:kelp_roll", "required": false },
     { "id": "farmersdelight:kelp_roll_slice", "required": false }
   ]
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/rocks.json b/src/main/resources/data/unicopia/tags/items/food_types/rocks.json
deleted file mode 100644
index 229f317a..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/rocks.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "unicopia:rock_stew"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/shells.json b/src/main/resources/data/unicopia/tags/items/food_types/shells.json
deleted file mode 100644
index d62b7052..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/shells.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:nautilus_shell",
-    "unicopia:clam_shell",
-    "unicopia:scallop_shell",
-    "unicopia:turret_shell",
-    "minecraft:seagrass",
-    "minecraft:sea_pickle"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/shelly.json b/src/main/resources/data/unicopia/tags/items/food_types/shelly.json
deleted file mode 100644
index f8790589..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/shelly.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "unicopia:shelly"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/groups/changeling.json b/src/main/resources/data/unicopia/tags/items/groups/changeling.json
index 8c094993..f25692d7 100644
--- a/src/main/resources/data/unicopia/tags/items/groups/changeling.json
+++ b/src/main/resources/data/unicopia/tags/items/groups/changeling.json
@@ -13,7 +13,7 @@
     "unicopia:hive",
     "unicopia:dark_oak_stable_door",
     "#c:cooked_meat",
-    "#unicopia:food_types/raw_meat",
+    "#c:raw_meat",
     "#c:raw_insect",
     "#c:rotten_meat",
     "#unicopia:container_with_love"
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 51b9f951..c75fb733 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
@@ -52,6 +52,7 @@
     "unicopia:imported_oats",
     "unicopia:oatmeal",
     "unicopia:oatmeal_cookie",
+    "unicopia:chocolate_oatmeal_cookie",
     "unicopia:daffodil_daisy_sandwich",
     "unicopia:hay_burger",
     "unicopia:hay_fries",
@@ -61,6 +62,7 @@
     "unicopia:baited_fishing_rod",
     "unicopia:worm_block",
     "unicopia:muffin",
+    "unicopia:scone",
     "unicopia:acorn",
     "unicopia:pinecone",
     "unicopia:pinecone_cookie",
diff --git a/src/main/resources/data/unicopia/tags/items/groups/sea_pony.json b/src/main/resources/data/unicopia/tags/items/groups/sea_pony.json
index a9259060..5d2c981b 100644
--- a/src/main/resources/data/unicopia/tags/items/groups/sea_pony.json
+++ b/src/main/resources/data/unicopia/tags/items/groups/sea_pony.json
@@ -2,9 +2,8 @@
   "replace": false,
   "values": [
     "unicopia:pearl_necklace",
-    "minecraft:nautilus_shell",
-    "#unicopia:food_types/shells",
-    "#unicopia:food_types/shelly",
+    "#unicopia:shells",
+    "#unicopia:special_shells",
     "#unicopia:food_types/raw_sea_vegitable",
     "#unicopia:food_types/cooked_sea_vegitable"
   ]

From 734e75f8c878495141c38cc2706dc3029032f883 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Tue, 2 Apr 2024 16:25:28 +0100
Subject: [PATCH 11/21] Add proper item models and textures for the slime
 pustule and mysterious egg

---
 .../providers/UBlockStateModelGenerator.java    |   4 ++--
 .../unicopia/models/item/mysterious_egg.json    |  16 ----------------
 .../unicopia/textures/item/mysterious_egg.png   | Bin 0 -> 6922 bytes
 .../unicopia/textures/item/slime_pustule.png    | Bin 0 -> 6682 bytes
 4 files changed, 2 insertions(+), 18 deletions(-)
 delete mode 100644 src/main/resources/assets/unicopia/models/item/mysterious_egg.json
 create mode 100644 src/main/resources/assets/unicopia/textures/item/mysterious_egg.png
 create mode 100644 src/main/resources/assets/unicopia/textures/item/slime_pustule.png

diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockStateModelGenerator.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockStateModelGenerator.java
index d016daae..40fe766c 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockStateModelGenerator.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UBlockStateModelGenerator.java
@@ -113,7 +113,7 @@ public class UBlockStateModelGenerator extends BlockStateModelGenerator {
         registerHiveBlock(UBlocks.HIVE);
         registerRotated(UBlocks.CHITIN_SPIKES, BlockModels.SPIKES);
         registerHull(UBlocks.CHISELLED_CHITIN_HULL, UBlocks.CHITIN, UBlocks.CHISELLED_CHITIN);
-        registerParentedItemModel(UBlocks.SLIME_PUSTULE, ModelIds.getBlockSubModelId(UBlocks.SLIME_PUSTULE, "_pod"));
+        registerItemModel(UBlocks.SLIME_PUSTULE.asItem());
         blockStateCollector.accept(VariantsBlockStateSupplier.create(UBlocks.SLIME_PUSTULE)
                 .coordinate(BlockStateVariantMap.create(SlimePustuleBlock.SHAPE)
                 .register(state -> BlockStateVariant.create().put(MODEL, ModelIds.getBlockSubModelId(UBlocks.SLIME_PUSTULE, "_" + state.asString())))));
@@ -176,7 +176,7 @@ public class UBlockStateModelGenerator extends BlockStateModelGenerator {
         registerWithStages(UBlocks.FROSTED_OBSIDIAN, Properties.AGE_3, BlockModels.CUBE_ALL, 0, 1, 2, 3);
         registerWithStagesBuiltinModels(UBlocks.ROCKS, Properties.AGE_7, 0, 1, 2, 3, 4, 5, 6, 7);
         registerWithStagesBuiltinModels(UBlocks.MYSTERIOUS_EGG, PileBlock.COUNT, 1, 2, 3);
-        excludeFromSimpleItemModelGeneration(UBlocks.MYSTERIOUS_EGG);
+        registerItemModel(UBlocks.MYSTERIOUS_EGG.asItem());
         FireModels.registerSoulFire(this, UBlocks.SPECTRAL_FIRE, Blocks.SOUL_FIRE);
 
         blockStateCollector.accept(createSingletonBlockState(UBlocks.JAR, BlockModels.TEMPLATE_JAR));
diff --git a/src/main/resources/assets/unicopia/models/item/mysterious_egg.json b/src/main/resources/assets/unicopia/models/item/mysterious_egg.json
deleted file mode 100644
index 7c9548c9..00000000
--- a/src/main/resources/assets/unicopia/models/item/mysterious_egg.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-    "parent": "unicopia:block/mysterious_egg_stage1",
-    "gui_light": "side",
-    "display": {
-        "gui": {
-            "rotation": [ 30, 225, 0 ],
-            "translation": [ 0, 0, 0],
-            "scale":[ 0.625, 0.625, 0.625 ]
-        },
-        "fixed": {
-            "rotation": [ 0, 0, 0 ],
-            "translation": [ 0, 0, 0],
-            "scale":[ 0.5, 0.5, 0.5 ]
-        }
-    }
-}
diff --git a/src/main/resources/assets/unicopia/textures/item/mysterious_egg.png b/src/main/resources/assets/unicopia/textures/item/mysterious_egg.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc1d9a42e705931098bcdc4d2c6f427fe60a6d93
GIT binary patch
literal 6922
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
G=lu)2_dk{Z

literal 0
HcmV?d00001

diff --git a/src/main/resources/assets/unicopia/textures/item/slime_pustule.png b/src/main/resources/assets/unicopia/textures/item/slime_pustule.png
new file mode 100644
index 0000000000000000000000000000000000000000..e3111b434f00de6bf4716182c864187e29c52490
GIT binary patch
literal 6682
zcmeHKd010d7Jng-giS?51qv8~)_`UYAqn6DM2Q+DfNNXzC3%4;5J<vihY>_;jSHhP
zRuC(VxGxoJwXP!yZiu6TYaPdmf^}y^tvbS-8wjGcU+a9+@0&kNa_>9u-gAEE{LZ=e
z<=uI)qlde@__$yg=B|i|j)Q9t)Mymw`#fsHF^sl@n4r)p6u}q`#z2$gh><$<NL^$H
z5*Z`I_~+1if{b)f^C3Cet_wi^utP2anbo1=05YHCfVso96q*2JI$SqG)6Z@9bpYhk
z&}g=A3`hvsejTn*#xevPK93_5gFR0mlJVdtVDQB<o=_IT2W?W>tBAl9bH-Y+c5H-X
z=Y(MGoRDni6bUl9BVLplN*CF)Lq^<54(*sR$hJeCPDKvvvA11pyN2}`F-$QG93dLe
zwf%raVKfSbN^zo5sZ?iYnu{CV-Obh2&AVr>?sPw&KK_0_zP^mUoc;{vz(8MLR%p;b
z9$zF9_31B-kO;y#LXiN0ker>J-CW(g+}*td0loo(cmA{;#5`yyCNhbMIe3uB9;CJ^
ztPjNLM6!L_sUV_cM<)uE=Ir7M3g7iWnIbzllAW9!9bq;X<}pVPr=9`)p_E=KoXSk6
z3-T9#O$&^ysPv3G^RTZ_ZJg!o@<DGeZ=Zf2^=AbIi$cVq5~(aIT0U%ejABH5!q{=+
zCrq5A(Gt4EB>gm#IU_SGJ7-40>^Yy${bHVF$<m^~F8gwMafx+x`I@!s)^FIj{o5Vi
z?cDYK?mdSNS5+T5dhGa#vvu|7&R@8A>GF@)8*bceY`XQ+?dE?yYI*$R>CexeBVLH7
z{nO6u4PG9=%fZo+>_|ntNDf(uxQC-t0H4xxsEUfG_hJh2Y4pg&UsqH*2MXgJda8|Q
zTt4V4`mtX#qGn_E`-si@ub6cbd&TQA=0+w#@yH(7VC-3|g;nP8f6}^Zt(F0&!Vg+4
zEb@_CR!hs6QBOv&tos^Hy0ENy_7<LMVSN+batMO%J`4;NG=w)=SX&QoV_7X&*Ve^W
zcAI70*7rt|)pBKZ{d%h<vcJ_5)MM%yx#v8mV3xIb!!Zzd4|AuhCaqY>rmH4;m7YVg
zze!FXk6)y#<e0sE){m~5oZjd}R}DeUzgA99@b(`nr;lBM?mZy_YvD+UtEiC=o^<(g
zdMuSMr;oe&tB?q~1J?grfGhs$V}SGd>LpmsF1<-t#Uw5|Bd5;~QpxEjWHOeuf-eR)
zCx;h9)ECWP{W8d|h->8ZIFH66m0VDm3t>gdQF?2Wtd<6T2=t5n$0LsCkx3SGCf}p|
zz;9zy2=r@@mLeCKcD6Rg$3ed|XYK}wW_FH}t{Rs#V+&msr_+M!DYXFV;y#0AEg!y2
z1*REuASr%h>8c6d#XD5;*hONfRYCcq5|(vuH&kh?qmeHGrALLlJL|2MgG(1eq~-N3
zm!UR}5R|dl6{V4i0y~ldE~ibjT7pN_h*a``-aukjx8H)T2YqLhwfxdty6RI3DvJX!
zz#u4FZS@fC;$k2;PL*k4t!Fd<>@C?CDFm|Zu6^y<uMtaN4N4xlk6>BLmfVB_(ID!n
z!vRz9n68SYpb(iWp_>3MNVNdHp`r<b&GbWlNnILsJ4`nD;}MXi8i-{m1s2}Kd<daj
zn{HOg&3;|Aogg7)1t>XXKfPtuw@hPMzdyx81zrO>fxSwgkDrR}w}VvLXpbLVC4jZ3
z+h5Qm+7b<tLN@4ZyZ07cNE`MF+g`D*SMn)H*WIv=4H*{9vhF^mDFZyAu*Np?D~aqH
z(3|q0<zRm_T+<E^iT3;3tMR7c@3|k`b_r`FhxLR%qXK<YRIDN@ijihC==8}1hGm!L
zE{=#fJjAzpO5F(g#%`HC(vQWiylai~o$hIg4XAh7B3u5+b0?*n!ZLsLwMl6YH5Iq7
zUEjQ^U=xG(=R8T6Bxp~}yta`pY<0Ng{?Wak74FT-JH44->(Y;;+*O+_>(}TyK>f34
z0L^QN171Aw$mYJpwXg&CJ+9sPv^4SAsu!k~P4f99(qhSRx4Mre_aB{nnbMSdowey$
zaI>^|wxW06Ki}AMz4^nY?KKX6Ur@69u5WtLgn?NVXR9}>r&ZNe@yFH$O*!`2X5-cC
zo6p_eoI9rVP>gEI<m<9pRe438Jf_FjWs8ERZSz<+wtscxvM2F_T0Jgs7mo}W@43MD
z(ZxK<rN;$rl@C(4oUJealCB^{a2Gw|Q)Xx7k2{=xpwMq!hJ)j(`~mwL{+Z+;TvZ+_
zn{=1vb%ny&8dkEM`pJFl@R-k&Pi&=^2NjJt_`*9kp5(R1>G`<*S1-Si<YLD9v5gdo
z#PLW?(f-Cq_pjc++8R-J{{c0LX0(nCcAF2cnc4b;3FZmPQ8JAog@bDiYJ!uUl4kq+
z4IiAHhHIu1W`>$b)Tc%;AME`blcCo}Fvo`|dCIgXB1s=J!$`!>7@eS*F<m3oG6xTG
z3D1@RKnh{T8QCexsU}%=1k;8q0~rl-nGBnXd3pqM0=z0l8H@x&#1V0LY<admQ@|YL
z!U#8Nb+WkVVI2@~7r{(2o6}@mZdO(nCrijN7!$dCsZ`443Ah3Q8#LIaoK!QO%}zB1
zB8Ya3Xu_m1>eI}6Ln;Gd;%Y;NIfBWAd4@f{lr*KX6F$|{kp;*HHycmm@;N+iN(#5D
zg~=?>1dxt^-fCe=$Vnr(afHc`Vbl=vOd{1B*cC#n>9kMFFecmRXf<3SnMeUu6L{sn
z<`TVBcUqtnB<fSrY*vu$*EG#~-K%8178{ze(dilpn0Mm7rfuhLQwA-iQWkB{WT5aA
z(Gg7KU#2x^^jeus6i9F(U(MsOi4Z|3TcqX_Y$*>{vw2#fL?jh!LNsDY7br!l$&9CJ
z2m}S-96jLZq#}u!5aVnSE`?626SF0_TFcgn#bR}+R;v+)2)jUxG3sGe;>lg3LQq<O
zQj2l5G*ruHi!?eOTSV}rY&=vN!WQX7T3js<@<XNVP+E;_n8BEWLpk*+cp||~OHH)-
zKna%(ja5W21svY1k=SJ1tOJ7x<|ut?M)s?T1bqq-Z^ltJ`JsFPFO&yf#6q4pRP;(|
z9APxUN<^5p<B@F!l|=?*Ah9@FrvP9Z2QIQGBY~R@#sq^QIfB`Fu<D#v!gkW)W;_}<
z69C$I)Jm`)wFdC`GG2T52u^9x>UBB4rHwWZBOLUh<S}{^_|LHoweP5SBE9{t{We)|
z+e!?EZBxi_O*;h>o=IqJaROHRlqLyJO(fvy(Xm|Z<NCKO1wp7#8mi%o*`ZRkm@VS-
z)offR3}uHBLP3Za7l=c6A+MvG3_5cbZX|{#LLMPkusm&tPzK9ZsG!%ivyuo@1xOg1
z2S34^3FCGU=AvhYopCt#7dZ*HDRg<rfL;3-JiOpp$nAU>cH|5_cK*OmM=kz94*>n%
z$vfrueY)PK>zy+2PRj3B*ZXw6QwH8i`TgqppQX#?)p3eQg}0zAI4sq!ZYY7H7DYXB
zcr@0AzGeH@Erk*1w3vw|40G*;8VM^Y^@2gFS)r6uZ#g=2Ckvn4VcmtpbiE>aXo94s
zdP&9fUbxqhnNv=#+3%{~L{rnw#`+B`Dlis!#|4Z$f4(5KvQnOAtjoJz_OgFfwW2aF
zBR1?}fak2Z)n~k7TK0Y6LtSe1=7b!7wmZ9dt2f>EPwO7MJn%I4z|)*n)YdH8%q6vn
z*o`BX*B)yWo^9J%u`>vBi~sV6ITkPS!;+@nOY2XFYZ_ewl$Fo_T~fIyYD;q6hOC%@
zRnLCjIEUKvYFONs<fUO*C1Do~q|<lqFPW_5ci*<WYU@si$1nC3d`Fl>EB6_Lcb(Y3
z!ih8az%XX|>}faqE$nZgDTAXmOB4E>YmTQqV;v(*bEvy6>`wbB7`xfMP+C0F;|z1h
z3cu6zp04R`@qO+kE{ObQZ_T$&{jSd3)UbyW*LwBl)18N_T4Txv-5BHZ#1(sfs)tfl
zd{$lLGRFG}<%?})kMGwkU(o6teJB27|G|rI{P>RytlTkxu{k)p`^N826%Kwr<dm<)
zW1-yp$y_%oM_3npZzbpDH{`s$+4;`fpDjoT?1haQvw=kw`Bd+c_$9VfPHqjv4%e?q
z?78V;mG$_Qv&m(Z!D3R(Dg|l56|b^~BmEz6rrx|4Dfe2-X<DY6*C*`c`Gs+LC#F<C
kdy%*XzkT*W!OTUg)->--8tS<G5ITe_<fEgvMoyjeAHRt!BLDyZ

literal 0
HcmV?d00001


From 171c786dec0933b86ce90b70a88a7cc4b78d9c34 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Thu, 4 Apr 2024 20:22:10 +0100
Subject: [PATCH 12/21] Datagen food groups+ - add more variety in the foraging
 groups - rebalance the probability of negative effects/food poisoning when
 eating the foraged things - add groups for rotten insect - moved fermented
 spider eye to the rotten insect category - removed unused foraging_moderate
 category - fixed incorrect tag reference breaking sapling eating

---
 .../unicopia/UConventionalTags.java           |   1 +
 .../com/minelittlepony/unicopia/UTags.java    |  16 ++-
 .../unicopia/datagen/DataCollector.java       |   4 +
 .../unicopia/datagen/Datagen.java             |   4 +-
 .../providers/DietProfileGenerator.java       |  12 ++
 .../datagen/providers/DietsProvider.java      |  83 ++++++++++++
 .../providers/FoodGroupsGenerator.java        | 123 ++++++++++++++++++
 .../providers/SeasonsGrowthRatesProvider.java |   6 +-
 .../providers/tag/UItemTagProvider.java       |  47 ++++++-
 .../unicopia/diet/FoodGroup.java              |  22 +++-
 .../unicopia/diet/FoodGroupEffects.java       |  44 +++++++
 .../unicopia/diet/FoodGroupKey.java           |  20 +++
 .../unicopia/diet/affliction/Affliction.java  |   2 +-
 .../diet/affliction/CompoundAffliction.java   |   2 +-
 .../diet/affliction/HealingAffliction.java    |   2 +-
 .../unicopia/diet/affliction/Range.java       |   6 +-
 .../affliction/StatusEffectAffliction.java    |  52 ++++----
 .../unicopia/item/UFoodComponents.java        |   6 -
 .../data/c/tags/items/foraging/edibles.json   |  13 --
 .../tags/items/foraging/edibles_filling.json  |   8 --
 .../data/c/tags/items/foraging/risky.json     |   8 --
 .../diets/food_groups/baked_goods.json        |  11 --
 .../diets/food_groups/bat_ponys_delight.json  |  10 --
 .../unicopia/diets/food_groups/candy.json     |  11 --
 .../unicopia/diets/food_groups/desserts.json  |  11 --
 .../diets/food_groups/fish/cooked.json        |  16 ---
 .../unicopia/diets/food_groups/fish/raw.json  |  22 ----
 .../diets/food_groups/fish/rotten.json        |  23 ----
 .../diets/food_groups/foraging/blinding.json  |  21 ---
 .../diets/food_groups/foraging/dangerous.json |  17 ---
 .../diets/food_groups/foraging/edible.json    |  10 --
 .../food_groups/foraging/edible_filling.json  |  10 --
 .../food_groups/foraging/leafy_greens.json    |  10 --
 .../diets/food_groups/foraging/moderate.json  |  17 ---
 .../food_groups/foraging/nauseating.json      |  22 ----
 .../diets/food_groups/foraging/prickly.json   |  17 ---
 .../food_groups/foraging/radioactive.json     |  22 ----
 .../diets/food_groups/foraging/risky.json     |  17 ---
 .../foraging/severely_nauseating.json         |  21 ---
 .../foraging/severely_prickly.json            |  21 ---
 .../food_groups/foraging/strengthening.json   |  22 ----
 .../unicopia/diets/food_groups/fruit.json     |  11 --
 .../diets/food_groups/insect/cooked.json      |  16 ---
 .../diets/food_groups/insect/raw.json         |  16 ---
 .../data/unicopia/diets/food_groups/love.json |  21 ---
 .../diets/food_groups/meat/cooked.json        |  16 ---
 .../unicopia/diets/food_groups/meat/raw.json  |  23 ----
 .../diets/food_groups/meat/rotten.json        |  23 ----
 .../diets/food_groups/nuts_and_seeds.json     |  11 --
 .../unicopia/diets/food_groups/pinecone.json  |  15 ---
 .../unicopia/diets/food_groups/rocks.json     |  10 --
 .../food_groups/sea_vegetable/cooked.json     |  11 --
 .../diets/food_groups/sea_vegetable/raw.json  |  11 --
 .../unicopia/diets/food_groups/shells.json    |  10 --
 .../unicopia/diets/food_groups/shelly.json    |  10 --
 .../food_types/cooked_sea_vegitable.json      |   7 -
 .../items/food_types/forage_blinding.json     |   6 -
 .../items/food_types/forage_dangerous.json    |   7 -
 .../tags/items/food_types/forage_edible.json  |  19 ---
 .../food_types/forage_edible_filling.json     |   7 -
 .../items/food_types/forage_nauseating.json   |   8 --
 .../tags/items/food_types/forage_prickly.json |   7 -
 .../items/food_types/forage_radioactive.json  |   7 -
 .../tags/items/food_types/forage_risky.json   |   9 --
 .../forage_severely_nauseating.json           |   6 -
 .../food_types/forage_severely_prickly.json   |   6 -
 .../food_types/forage_strengthening.json      |   6 -
 .../items/food_types/raw_sea_vegitable.json   |  11 --
 68 files changed, 400 insertions(+), 722 deletions(-)
 create mode 100644 src/main/java/com/minelittlepony/unicopia/datagen/providers/DietProfileGenerator.java
 create mode 100644 src/main/java/com/minelittlepony/unicopia/datagen/providers/DietsProvider.java
 create mode 100644 src/main/java/com/minelittlepony/unicopia/datagen/providers/FoodGroupsGenerator.java
 delete mode 100644 src/main/resources/data/c/tags/items/foraging/edibles.json
 delete mode 100644 src/main/resources/data/c/tags/items/foraging/edibles_filling.json
 delete mode 100644 src/main/resources/data/c/tags/items/foraging/risky.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/baked_goods.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/bat_ponys_delight.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/candy.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/desserts.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/fish/cooked.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/fish/raw.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/fish/rotten.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/blinding.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/dangerous.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/edible.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/edible_filling.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/leafy_greens.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/moderate.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/nauseating.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/prickly.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/radioactive.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/risky.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/severely_nauseating.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/severely_prickly.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/foraging/strengthening.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/fruit.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/insect/cooked.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/insect/raw.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/love.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/meat/cooked.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/meat/raw.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/meat/rotten.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/nuts_and_seeds.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/pinecone.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/rocks.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/cooked.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/raw.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/shells.json
 delete mode 100644 src/main/resources/data/unicopia/diets/food_groups/shelly.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/cooked_sea_vegitable.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/forage_blinding.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/forage_dangerous.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/forage_edible.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/forage_edible_filling.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/forage_nauseating.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/forage_prickly.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/forage_radioactive.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/forage_risky.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/forage_severely_nauseating.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/forage_severely_prickly.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/forage_strengthening.json
 delete mode 100644 src/main/resources/data/unicopia/tags/items/food_types/raw_sea_vegitable.json

diff --git a/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java b/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
index 0b1942ce..6b54e607 100644
--- a/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
+++ b/src/main/java/com/minelittlepony/unicopia/UConventionalTags.java
@@ -49,6 +49,7 @@ public interface UConventionalTags {
 
         TagKey<Item> RAW_INSECT = item("raw_insect");
         TagKey<Item> COOKED_INSECT = item("cooked_insect");
+        TagKey<Item> ROTTEN_INSECT = item("rotten_insect");
 
         TagKey<Item> RAW_FISH = item("raw_fish");
         TagKey<Item> COOKED_FISH = item("cooked_fish");
diff --git a/src/main/java/com/minelittlepony/unicopia/UTags.java b/src/main/java/com/minelittlepony/unicopia/UTags.java
index 24fd3a3f..83e9cb37 100644
--- a/src/main/java/com/minelittlepony/unicopia/UTags.java
+++ b/src/main/java/com/minelittlepony/unicopia/UTags.java
@@ -30,7 +30,6 @@ public interface UTags {
         TagKey<Item> MAGIC_FEATHERS = item("magic_feathers");
 
         TagKey<Item> SHADES = item("shades");
-        TagKey<Item> CHANGELING_EDIBLE = item("food_types/changeling_edible");
         TagKey<Item> SPOOKED_MOB_DROPS = item("spooked_mob_drops");
         TagKey<Item> HAS_NO_TRAITS = item("has_no_traits");
         TagKey<Item> IS_DELIVERED_AGGRESSIVELY = item("is_delivered_aggressively");
@@ -46,6 +45,9 @@ public interface UTags {
         TagKey<Item> ROCK_STEWS = item("rock_stews");
         TagKey<Item> BAKED_GOODS = item("baked_goods");
 
+        TagKey<Item> HIGH_QUALITY_SEA_VEGETABLES = item("food_types/high_quality_sea_vegetables");
+        TagKey<Item> LOW_QUALITY_SEA_VEGETABLES = item("food_types/low_quality_sea_vegetables");
+
         TagKey<Item> POLEARMS = item("polearms");
         TagKey<Item> HORSE_SHOES = item("horse_shoes");
         TagKey<Item> APPLE_SEEDS = item("apple_seeds");
@@ -64,6 +66,18 @@ public interface UTags {
         TagKey<Item> GROUP_SEA_PONY = item("groups/sea_pony");
         TagKey<Item> GROUP_CHANGELING = item("groups/changeling");
 
+        TagKey<Item> FORAGE_BLINDING = item("forage/blinding");
+        TagKey<Item> FORAGE_DANGEROUS = item("forage/dangerous");
+        TagKey<Item> FORAGE_FILLING = item("forage/filling");
+        TagKey<Item> FORAGE_SAFE = item("forage/safe");
+        TagKey<Item> FORAGE_NAUSEATING = item("forage/nauseating");
+        TagKey<Item> FORAGE_PRICKLY = item("forage/prickly");
+        TagKey<Item> FORAGE_GLOWING = item("forage/glowing");
+        TagKey<Item> FORAGE_RISKY = item("forage/risky");
+        TagKey<Item> FORAGE_STRENGHENING = item("forage/strenghtening");
+        TagKey<Item> FORAGE_SEVERE_NAUSEATING = item("forage/severe/nauseating");
+        TagKey<Item> FORAGE_SEVERE_PRICKLY = item("forage/severe/prickly");
+
         private static TagKey<Item> item(String name) {
             return TagKey.of(RegistryKeys.ITEM, Unicopia.id(name));
         }
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/DataCollector.java b/src/main/java/com/minelittlepony/unicopia/datagen/DataCollector.java
index 46b27642..2dabd5cd 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/DataCollector.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/DataCollector.java
@@ -22,6 +22,10 @@ public class DataCollector {
         this.resolver = resolver;
     }
 
+    public boolean isDefined(Identifier id) {
+        return values.containsKey(id);
+    }
+
     public BiConsumer<Identifier, Supplier<JsonElement>> prime() {
         values.clear();
         return (Identifier id, Supplier<JsonElement> value) ->
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/Datagen.java b/src/main/java/com/minelittlepony/unicopia/datagen/Datagen.java
index 667d6173..517a21e4 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/Datagen.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/Datagen.java
@@ -4,6 +4,7 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import com.minelittlepony.unicopia.block.EdibleBlock;
+import com.minelittlepony.unicopia.datagen.providers.DietsProvider;
 import com.minelittlepony.unicopia.datagen.providers.SeasonsGrowthRatesProvider;
 import com.minelittlepony.unicopia.datagen.providers.UAdvancementsProvider;
 import com.minelittlepony.unicopia.datagen.providers.UModelProvider;
@@ -49,7 +50,8 @@ public class Datagen implements DataGeneratorEntrypoint {
     public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) {
         final var pack = fabricDataGenerator.createPack();
         final var blockTags = pack.addProvider(UBlockTagProvider::new);
-        pack.addProvider((output, registries) -> new UItemTagProvider(output, registries, blockTags));
+        final var itemTags = pack.addProvider((output, registries) -> new UItemTagProvider(output, registries, blockTags));
+        pack.addProvider((output, registries) -> new DietsProvider(output, itemTags));
         pack.addProvider(UDamageTypeProvider::new);
 
         pack.addProvider(UModelProvider::new);
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/DietProfileGenerator.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/DietProfileGenerator.java
new file mode 100644
index 00000000..6afe54f9
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/DietProfileGenerator.java
@@ -0,0 +1,12 @@
+package com.minelittlepony.unicopia.datagen.providers;
+
+import java.util.function.BiConsumer;
+import com.minelittlepony.unicopia.Race;
+import com.minelittlepony.unicopia.diet.DietProfile;
+
+public class DietProfileGenerator {
+
+    public void generate(BiConsumer<Race, DietProfile> exporter) {
+
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/DietsProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/DietsProvider.java
new file mode 100644
index 00000000..5b14e3dd
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/DietsProvider.java
@@ -0,0 +1,83 @@
+package com.minelittlepony.unicopia.datagen.providers;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import com.google.gson.JsonObject;
+import com.minelittlepony.unicopia.Race;
+import com.minelittlepony.unicopia.datagen.DataCollector;
+import com.minelittlepony.unicopia.diet.DietProfile;
+import com.minelittlepony.unicopia.diet.FoodGroup;
+import com.mojang.serialization.JsonOps;
+
+import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
+import net.minecraft.data.DataOutput;
+import net.minecraft.data.DataProvider;
+import net.minecraft.data.DataWriter;
+import net.minecraft.data.server.tag.TagProvider;
+import net.minecraft.data.server.tag.TagProvider.TagLookup;
+import net.minecraft.item.Item;
+import net.minecraft.registry.RegistryKeys;
+import net.minecraft.registry.tag.TagKey;
+import net.minecraft.util.Identifier;
+
+public class DietsProvider implements DataProvider {
+    private final DataCollector dietsCollector;
+    private final DataCollector categoriesCollector;
+
+    private final CompletableFuture<TagLookup<Item>> itemTagLookup;
+
+    public DietsProvider(FabricDataOutput output, TagProvider<Item> tagProvider) {
+        this.dietsCollector = new DataCollector(output.getResolver(DataOutput.OutputType.DATA_PACK, "diet/races"));
+        this.categoriesCollector = new DataCollector(output.getResolver(DataOutput.OutputType.DATA_PACK, "diet/food_groups"));
+        itemTagLookup = tagProvider.getTagLookupFuture();
+    }
+
+    @Override
+    public String getName() {
+        return "Diets";
+    }
+
+    @Override
+    public CompletableFuture<?> run(DataWriter writer) {
+        return itemTagLookup.thenCompose(tagLookup -> {
+            var diets = categoriesCollector.prime();
+            Map<Identifier, Set<Identifier>> keyToGroupId = new HashMap<>();
+            new FoodGroupsGenerator().generate((id, builder) -> {
+                var attributes = builder.build();
+                attributes.tags().forEach(key -> {
+                    if (!tagLookup.contains(TagKey.of(RegistryKeys.ITEM, key.id()))) {
+                        throw new IllegalArgumentException("Food group " + id + " references unknown item tag " + key.id());
+                    }
+                    keyToGroupId.computeIfAbsent(key.id(), i -> new HashSet<>()).add(id);
+                });
+                diets.accept(id, () -> FoodGroup.CODEC.encode(attributes, JsonOps.INSTANCE, new JsonObject()).result().get());
+            });
+            var profiles = dietsCollector.prime();
+            new DietProfileGenerator().generate((race, profile) -> {
+                Identifier id = Race.REGISTRY.getId(race);
+                StringBuilder issues = new StringBuilder();
+                profile.validate(issue -> {
+                    issues.append(System.lineSeparator()).append(issue);
+                }, categoriesCollector::isDefined);
+                if (!issues.isEmpty()) {
+                    throw new IllegalArgumentException("Diet profile " + id + " failed validation: " + issues.toString());
+                }
+                profiles.accept(id, () -> DietProfile.CODEC.encode(profile, JsonOps.INSTANCE, new JsonObject()).result().get());
+            });
+            keyToGroupId.forEach((tag, groups) -> {
+               if (groups.size() > 1) {
+                   throw new IllegalArgumentException("Multiple groups referenced the same tag " + tag + " held by "
+                           + groups.stream().map(Identifier::toString).collect(Collectors.joining())
+                   );
+               }
+            });
+
+            return CompletableFuture.allOf(categoriesCollector.upload(writer), dietsCollector.upload(writer));
+        });
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/FoodGroupsGenerator.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/FoodGroupsGenerator.java
new file mode 100644
index 00000000..c1e4bf75
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/FoodGroupsGenerator.java
@@ -0,0 +1,123 @@
+package com.minelittlepony.unicopia.datagen.providers;
+
+import java.util.List;
+import java.util.function.BiConsumer;
+import com.minelittlepony.unicopia.UConventionalTags;
+import com.minelittlepony.unicopia.UTags;
+import com.minelittlepony.unicopia.Unicopia;
+import com.minelittlepony.unicopia.diet.FoodGroupEffects;
+import com.minelittlepony.unicopia.diet.affliction.Affliction;
+import com.minelittlepony.unicopia.diet.affliction.CompoundAffliction;
+import com.minelittlepony.unicopia.diet.affliction.HealingAffliction;
+import com.minelittlepony.unicopia.diet.affliction.LoseHungerAffliction;
+import com.minelittlepony.unicopia.diet.affliction.Range;
+import com.minelittlepony.unicopia.diet.affliction.StatusEffectAffliction;
+import com.minelittlepony.unicopia.entity.effect.UEffects;
+import com.minelittlepony.unicopia.item.UFoodComponents;
+
+import net.minecraft.entity.effect.StatusEffects;
+import net.minecraft.item.FoodComponent;
+import net.minecraft.item.FoodComponents;
+import net.minecraft.item.Item;
+import net.minecraft.registry.tag.ItemTags;
+import net.minecraft.registry.tag.TagKey;
+import net.minecraft.util.Identifier;
+
+public class FoodGroupsGenerator {
+    public void generate(BiConsumer<Identifier, FoodGroupEffects.Builder> exporter) {
+        exporter.accept(Unicopia.id("baked_goods"), new FoodGroupEffects.Builder().tag(UTags.Items.BAKED_GOODS).food(FoodComponents.BREAD));
+        exporter.accept(Unicopia.id("bat_ponys_delight"), new FoodGroupEffects.Builder().tag(UConventionalTags.Items.MANGOES).food(UFoodComponents.MANGO));
+        exporter.accept(Unicopia.id("candy"), new FoodGroupEffects.Builder().tag(UConventionalTags.Items.CANDY).food(UFoodComponents.CANDY));
+        exporter.accept(Unicopia.id("desserts"), new FoodGroupEffects.Builder().tag(UConventionalTags.Items.DESSERTS).food(FoodComponents.COOKIE));
+        exporter.accept(Unicopia.id("fruit"), new FoodGroupEffects.Builder().tag(UConventionalTags.Items.FRUITS).food(UFoodComponents.BANANA));
+        exporter.accept(Unicopia.id("rocks"), new FoodGroupEffects.Builder().tag(UConventionalTags.Items.ROCKS).tag(UTags.Items.ROCK_STEWS).food(FoodComponents.MUSHROOM_STEW));
+        exporter.accept(Unicopia.id("shells"), new FoodGroupEffects.Builder().tag(UTags.Items.SHELLS).food(UFoodComponents.SHELL));
+        exporter.accept(Unicopia.id("special_shells"), new FoodGroupEffects.Builder().tag(UTags.Items.SPECIAL_SHELLS).food(UFoodComponents.SHELLY));
+        exporter.accept(Unicopia.id("love"), new FoodGroupEffects.Builder().tag(UTags.Items.CONTAINER_WITH_LOVE).food(UFoodComponents.LOVE_MUG).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(50), Range.of(2), 0),
+                new LoseHungerAffliction(0.5F)
+        ))));
+        exporter.accept(Unicopia.id("nuts_and_seeds"), new FoodGroupEffects.Builder()
+                .tag(UConventionalTags.Items.GRAIN).tag(UConventionalTags.Items.NUTS).tag(UConventionalTags.Items.SEEDS)
+                .food(UFoodComponents.BANANA)
+        );
+        exporter.accept(Unicopia.id("pinecone"), new FoodGroupEffects.Builder().tag(UConventionalTags.Items.PINECONES).food(UFoodComponents.PINECONE).ailment(new CompoundAffliction(List.<Affliction>of(
+                new HealingAffliction(1)
+        ))));
+
+        provideMeatCategory("fish",
+                UConventionalTags.Items.COOKED_FISH, UConventionalTags.Items.RAW_FISH, UConventionalTags.Items.ROTTEN_FISH,
+                FoodComponents.COOKED_COD, FoodComponents.COD, FoodComponents.ROTTEN_FLESH, exporter);
+        provideMeatCategory("meat",
+                UConventionalTags.Items.COOKED_MEAT, UConventionalTags.Items.RAW_MEAT, UConventionalTags.Items.ROTTEN_MEAT,
+                FoodComponents.COOKED_BEEF, FoodComponents.BEEF, FoodComponents.ROTTEN_FLESH, exporter);
+        provideMeatCategory("insect",
+                UConventionalTags.Items.COOKED_INSECT, UConventionalTags.Items.RAW_INSECT, UConventionalTags.Items.ROTTEN_INSECT,
+                FoodComponents.COOKED_BEEF, FoodComponents.BEEF, FoodComponents.ROTTEN_FLESH, exporter);
+        provideVegetableCategory("sea_vegetable",
+                UTags.Items.HIGH_QUALITY_SEA_VEGETABLES, UTags.Items.LOW_QUALITY_SEA_VEGETABLES,
+                FoodComponents.COOKED_BEEF, FoodComponents.BEEF, exporter);
+
+        exporter.accept(Unicopia.id("foraging/blinding"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_BLINDING).food(4, 0.2F).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(StatusEffects.BLINDNESS, Range.of(30), Range.of(0), 50),
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 12)
+        ))));
+        exporter.accept(Unicopia.id("foraging/dangerous"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_DANGEROUS).food(3, 0.3F).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(250), Range.of(2), 4)
+        ))));
+        exporter.accept(Unicopia.id("foraging/edible_filling"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_FILLING).food(17, 0.6F));
+        exporter.accept(Unicopia.id("foraging/edible"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_SAFE).food(2, 1));
+        exporter.accept(Unicopia.id("foraging/leafy_greens"), new FoodGroupEffects.Builder().tag(ItemTags.LEAVES).food(1, 1.4F));
+        exporter.accept(Unicopia.id("foraging/nauseating"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_NAUSEATING).food(5, 0.5F).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(StatusEffects.WEAKNESS, Range.of(200), Range.of(1), 30),
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(200), Range.of(2), 0)
+        ))));
+        exporter.accept(Unicopia.id("foraging/prickly"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_PRICKLY).food(0, 1.5F).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(StatusEffects.INSTANT_DAMAGE, Range.of(1), Range.of(0), 30)
+        ))));
+        exporter.accept(Unicopia.id("foraging/glowing"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_GLOWING).food(1, 1.6F).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(StatusEffects.GLOWING, Range.of(30), Range.of(0), 30),
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 0)
+        ))));
+        exporter.accept(Unicopia.id("foraging/risky"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_RISKY).food(9, 1.1F).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 80)
+        ))));
+        exporter.accept(Unicopia.id("foraging/severely_nauseating"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_SEVERE_NAUSEATING).food(3, 0.9F).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(StatusEffects.WEAKNESS, Range.of(200), Range.of(1), 0),
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 80)
+        ))));
+        exporter.accept(Unicopia.id("foraging/severely_prickly"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_SEVERE_PRICKLY).food(2, 0.9F).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(StatusEffects.INSTANT_DAMAGE, Range.of(1), Range.of(0), 0),
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 80)
+        ))));
+        exporter.accept(Unicopia.id("foraging/strengthening"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_STRENGHENING).food(4, 0.2F).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(StatusEffects.STRENGTH, Range.of(1300), Range.of(0), 0),
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 70)
+        ))));
+    }
+
+    private void provideMeatCategory(String name,
+            TagKey<Item> cookedTag, TagKey<Item> rawTag, TagKey<Item> rottenTag,
+            FoodComponent cooked, FoodComponent raw, FoodComponent rotten,
+            BiConsumer<Identifier, FoodGroupEffects.Builder> exporter) {
+        exporter.accept(Unicopia.id(name + "/cooked"), new FoodGroupEffects.Builder().tag(cookedTag).food(cooked).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 25)
+        ))));
+        exporter.accept(Unicopia.id(name + "/raw"), new FoodGroupEffects.Builder().tag(rawTag).food(raw).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(StatusEffects.POISON, Range.of(45), Range.of(2), 80),
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 65)
+        ))));
+        exporter.accept(Unicopia.id(name + "/rotten"), new FoodGroupEffects.Builder().tag(rottenTag).food(rotten).ailment(new CompoundAffliction(List.<Affliction>of(
+                new StatusEffectAffliction(StatusEffects.POISON, Range.of(45), Range.of(2), 80),
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 95)
+        ))));
+    }
+
+    private void provideVegetableCategory(String name,
+            TagKey<Item> cookedTag, TagKey<Item> rawTag,
+            FoodComponent cooked, FoodComponent raw,
+            BiConsumer<Identifier, FoodGroupEffects.Builder> exporter) {
+        exporter.accept(Unicopia.id(name + "/cooked"), new FoodGroupEffects.Builder().tag(cookedTag).food(cooked));
+        exporter.accept(Unicopia.id(name + "/raw"), new FoodGroupEffects.Builder().tag(rawTag).food(raw));
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/SeasonsGrowthRatesProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/SeasonsGrowthRatesProvider.java
index 186c0685..d802bbc7 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/SeasonsGrowthRatesProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/SeasonsGrowthRatesProvider.java
@@ -12,17 +12,16 @@ import com.minelittlepony.unicopia.server.world.UTreeGen;
 import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
 import net.minecraft.block.Block;
 import net.minecraft.data.DataOutput;
-import net.minecraft.data.DataOutput.PathResolver;
 import net.minecraft.registry.Registries;
 import net.minecraft.data.DataProvider;
 import net.minecraft.data.DataWriter;
 
 public class SeasonsGrowthRatesProvider implements DataProvider {
 
-    private final PathResolver pathResolver;
+    private final DataCollector collectedData;
 
     public SeasonsGrowthRatesProvider(FabricDataOutput output) {
-        this.pathResolver = output.getResolver(DataOutput.OutputType.DATA_PACK, "seasons/crop");
+        this.collectedData = new DataCollector(output.getResolver(DataOutput.OutputType.DATA_PACK, "seasons/crop"));
     }
 
     @Override
@@ -32,7 +31,6 @@ public class SeasonsGrowthRatesProvider implements DataProvider {
 
     @Override
     public CompletableFuture<?> run(DataWriter writer) {
-        DataCollector collectedData = new DataCollector(pathResolver);
         var exporter = collectedData.prime();
         generate((block, crop) -> {
             exporter.accept(Registries.BLOCK.getId(block), crop::toJson);
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
index 7187116a..bc248a61 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
@@ -130,7 +130,14 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
         getOrCreateTagBuilder(UTags.Items.SHELLS).add(Items.NAUTILUS_SHELL, UItems.CLAM_SHELL, UItems.SCALLOP_SHELL, UItems.TURRET_SHELL);
         getOrCreateTagBuilder(UTags.Items.SPECIAL_SHELLS).add(UItems.SHELLY);
         getOrCreateTagBuilder(UTags.Items.ROCK_STEWS).add(UItems.ROCK_STEW);
+        getOrCreateTagBuilder(UTags.Items.HIGH_QUALITY_SEA_VEGETABLES)
+            .add(Items.DRIED_KELP_BLOCK, Items.GLOW_LICHEN)
+            .forceAddTag(UConventionalTags.Items.CORAL_BLOCKS);
+        getOrCreateTagBuilder(UTags.Items.LOW_QUALITY_SEA_VEGETABLES)
+            .add(Items.KELP, Items.DRIED_KELP, Items.SEAGRASS, Items.SEA_PICKLE)
+            .forceAddTag(UConventionalTags.Items.CORALS).forceAddTag(UConventionalTags.Items.CORAL_FANS);
 
+        exportForagingTags();
         exportFarmersDelightItems();
     }
 
@@ -162,6 +169,23 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
         copy(UTags.Blocks.CHITIN_BLOCKS, UTags.Items.CHITIN_BLOCKS);
     }
 
+    private void exportForagingTags() {
+        getOrCreateTagBuilder(UTags.Items.FORAGE_BLINDING).add(Items.OXEYE_DAISY);
+        getOrCreateTagBuilder(UTags.Items.FORAGE_DANGEROUS).add(Items.POPPY, Items.LILY_OF_THE_VALLEY);
+        getOrCreateTagBuilder(UTags.Items.FORAGE_FILLING).add(Items.HAY_BLOCK);
+        getOrCreateTagBuilder(UTags.Items.FORAGE_SAFE).add(Items.BLUE_ORCHID, Items.RED_TULIP, Items.ORANGE_TULIP,
+                Items.PINK_TULIP, Items.CORNFLOWER, Items.PEONY, Items.SUNFLOWER, Items.DANDELION, Items.LILAC, Items.TALL_GRASS,
+                Items.DEAD_BUSH, Items.PINK_PETALS
+        ).forceAddTag(UConventionalTags.Items.MUSHROOMS).forceAddTag(ItemTags.SAPLINGS);
+        getOrCreateTagBuilder(UTags.Items.FORAGE_NAUSEATING).add(Items.GRASS, UItems.CIDER);
+        getOrCreateTagBuilder(UTags.Items.FORAGE_PRICKLY).add(Items.ROSE_BUSH).forceAddTag(ItemTags.SAPLINGS);
+        getOrCreateTagBuilder(UTags.Items.FORAGE_GLOWING).add(Items.AZURE_BLUET, Items.TORCHFLOWER);
+        getOrCreateTagBuilder(UTags.Items.FORAGE_RISKY).add(Items.ALLIUM, Items.WHITE_TULIP, UItems.BURNED_JUICE);
+        getOrCreateTagBuilder(UTags.Items.FORAGE_STRENGHENING).add(Items.FERN);
+        getOrCreateTagBuilder(UTags.Items.FORAGE_SEVERE_NAUSEATING).add(Items.PITCHER_PLANT);
+        getOrCreateTagBuilder(UTags.Items.FORAGE_SEVERE_PRICKLY).add(Items.LARGE_FERN);
+    }
+
     private void exportConventionalTags() {
         copy(UConventionalTags.Blocks.CONCRETES, UConventionalTags.Items.CONCRETES);
         copy(UConventionalTags.Blocks.CONCRETE_POWDERS, UConventionalTags.Items.CONCRETE_POWDERS);
@@ -199,7 +223,9 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
             .addOptionalTag(new Identifier("c", "raw_pork"))
             .addOptionalTag(new Identifier("c", "lemon_chickens"));
         getOrCreateTagBuilder(UConventionalTags.Items.ROTTEN_MEAT).add(Items.ROTTEN_FLESH);
-        getOrCreateTagBuilder(UConventionalTags.Items.COOKED_INSECT).add(Items.FERMENTED_SPIDER_EYE);
+        getOrCreateTagBuilder(UConventionalTags.Items.ROTTEN_FISH);//.add(Items.ROTTEN_FLESH); TODO:
+        getOrCreateTagBuilder(UConventionalTags.Items.ROTTEN_INSECT).add(Items.FERMENTED_SPIDER_EYE);
+        getOrCreateTagBuilder(UConventionalTags.Items.COOKED_INSECT);//.add(Items.FERMENTED_SPIDER_EYE);
         getOrCreateTagBuilder(UConventionalTags.Items.RAW_INSECT).add(Items.SPIDER_EYE, UItems.BUTTERFLY, UItems.WHEAT_WORMS, UBlocks.WORM_BLOCK.asItem());
         getOrCreateTagBuilder(UConventionalTags.Items.WORMS).add(UItems.WHEAT_WORMS);
         getOrCreateTagBuilder(UConventionalTags.Items.STICKS).add(Items.STICK);
@@ -295,5 +321,24 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
             .addOptional(new Identifier("farmersdelight", "raw_pasta"))
             .addOptional(new Identifier("farmersdelight", "pie_crust"))
             .addOptional(new Identifier("farmersdelight", "egg_sandwich"));
+        getOrCreateTagBuilder(UTags.Items.HIGH_QUALITY_SEA_VEGETABLES)
+            .addOptional(new Identifier("farmersdelight", "kelp_roll"));
+        getOrCreateTagBuilder(UTags.Items.LOW_QUALITY_SEA_VEGETABLES)
+            .addOptional(new Identifier("farmersdelight", "kelp_roll_slice"));
+        getOrCreateTagBuilder(UTags.Items.FORAGE_FILLING)
+            .addOptional(new Identifier("farmersdelight", "horse_feed"))
+            .addOptional(new Identifier("farmersdelight", "rice_bale"))
+            .addOptional(new Identifier("farmersdelight", "straw_bale"));
+        getOrCreateTagBuilder(UTags.Items.FORAGE_SAFE)
+            .addOptional(new Identifier("farmersdelight", "sandy_shrub"))
+            .addOptional(new Identifier("farmersdelight", "wild_cabbages"))
+            .addOptional(new Identifier("farmersdelight", "wild_onions"))
+            .addOptional(new Identifier("farmersdelight", "wild_carrots"))
+            .addOptional(new Identifier("farmersdelight", "wild_beetroots"))
+            .addOptional(new Identifier("farmersdelight", "wild_rice"));
+        getOrCreateTagBuilder(UTags.Items.FORAGE_RISKY)
+            .addOptional(new Identifier("farmersdelight", "wild_tomatoes"))
+            .addOptional(new Identifier("farmersdelight", "wild_potatoes"))
+            .addOptionalTag(new Identifier("c", "meads"));
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/FoodGroup.java b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroup.java
index 9c6279b5..173b1fef 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/FoodGroup.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroup.java
@@ -17,23 +17,31 @@ import net.minecraft.util.Util;
 
 public record FoodGroup(
         Identifier id,
-        List<FoodGroupKey> tags,
-        Optional<FoodComponent> foodComponent,
-        Ailment ailment) implements Effect {
+        FoodGroupEffects attributes) implements Effect {
     public static final Codec<FoodGroupEffects> CODEC = RecordCodecBuilder.create(instance -> instance.group(
             FoodGroupKey.TAG_CODEC.listOf().fieldOf("tags").forGetter(FoodGroupEffects::tags),
             FoodAttributes.CODEC.optionalFieldOf("food_component").forGetter(FoodGroupEffects::foodComponent),
             Ailment.CODEC.fieldOf("ailment").forGetter(FoodGroupEffects::ailment)
     ).apply(instance, FoodGroupEffects::new));
 
-    public FoodGroup(Identifier id, Effect effect) {
-        this(id, effect.tags(), effect.foodComponent(), effect.ailment());
-    }
-
     public FoodGroup(PacketByteBuf buffer) {
         this(buffer.readIdentifier(), new FoodGroupEffects(buffer, FoodGroupKey.TAG_ID_LOOKUP));
     }
 
+    @Override
+    public List<FoodGroupKey> tags() {
+        return attributes.tags();
+    }
+
+    @Override
+    public Optional<FoodComponent> foodComponent() {
+        return attributes.foodComponent();
+    }
+
+    @Override
+    public Ailment ailment() {
+        return attributes.ailment();
+    }
     @Override
     public void appendTooltip(ItemStack stack, List<Text> tooltip, TooltipContext context) {
         tooltip.add(Text.literal(" ").append(Text.translatable(Util.createTranslationKey("food_group", id()))).formatted(Formatting.GRAY));
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupEffects.java b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupEffects.java
index e92f2995..ad207ed1 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupEffects.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupEffects.java
@@ -1,15 +1,22 @@
 package com.minelittlepony.unicopia.diet;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 import java.util.function.Function;
+
+import com.minelittlepony.unicopia.diet.affliction.Affliction;
+import com.minelittlepony.unicopia.item.UFoodComponents;
 import com.mojang.serialization.Codec;
 import com.mojang.serialization.codecs.RecordCodecBuilder;
 
 import net.minecraft.client.item.TooltipContext;
 import net.minecraft.item.FoodComponent;
+import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
 import net.minecraft.network.PacketByteBuf;
+import net.minecraft.registry.RegistryKeys;
+import net.minecraft.registry.tag.TagKey;
 import net.minecraft.text.Text;
 import net.minecraft.util.Formatting;
 import net.minecraft.util.Identifier;
@@ -39,4 +46,41 @@ public record FoodGroupEffects(
         });
         Effect.super.appendTooltip(stack, tooltip, context);
     }
+
+    public static final class Builder {
+        private final List<FoodGroupKey> tags = new ArrayList<>();
+        private Optional<FoodComponent> foodComponent = Optional.empty();
+        private Ailment ailment = new Ailment(Affliction.EMPTY);
+
+        public Builder tag(Identifier tag) {
+            return tag(TagKey.of(RegistryKeys.ITEM, tag));
+        }
+
+        public Builder tag(TagKey<Item> tag) {
+            tags.add(FoodGroupKey.TAG_LOOKUP.apply(tag));
+            return this;
+        }
+
+        public Builder ailment(Affliction affliction) {
+            ailment = new Ailment(affliction);
+            return this;
+        }
+
+        public Builder food(int hunger, float saturation) {
+            return food(UFoodComponents.builder(hunger, saturation));
+        }
+
+        public Builder food(FoodComponent.Builder food) {
+            return food(food.build());
+        }
+
+        public Builder food(FoodComponent food) {
+            foodComponent = Optional.of(food);
+            return this;
+        }
+
+        public FoodGroupEffects build() {
+            return new FoodGroupEffects(tags, foodComponent, ailment);
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupKey.java b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupKey.java
index ac59d145..567c4a36 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupKey.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/FoodGroupKey.java
@@ -27,6 +27,16 @@ public interface FoodGroupKey {
                 var group = PonyDiets.getEffect(id);
                 return group != null && group.test(stack);
             }
+
+            @Override
+            public boolean equals(Object o) {
+                return o == this && (o instanceof FoodGroupKey k && k.id().equals(id()));
+            }
+
+            @Override
+            public int hashCode() {
+                return id().hashCode();
+            }
         };
     });
     Function<TagKey<Item>, FoodGroupKey> TAG_LOOKUP = Util.memoize(tag -> {
@@ -48,6 +58,16 @@ public interface FoodGroupKey {
 
                 return stack.isIn(tag);
             }
+
+            @Override
+            public boolean equals(Object o) {
+                return o == this && (o instanceof FoodGroupKey k && k.id().equals(id()));
+            }
+
+            @Override
+            public int hashCode() {
+                return id().hashCode();
+            }
         };
     });
     Function<Identifier, FoodGroupKey> TAG_ID_LOOKUP = id -> TAG_LOOKUP.apply(TagKey.of(RegistryKeys.ITEM, id));
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/affliction/Affliction.java b/src/main/java/com/minelittlepony/unicopia/diet/affliction/Affliction.java
index 013d3f51..ff228b31 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/affliction/Affliction.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/affliction/Affliction.java
@@ -37,7 +37,7 @@ public interface Affliction {
             affliction -> ((CompoundAffliction)affliction).afflictions
     )).xmap(
             either -> either.left().or(either::right).get(),
-            affliction -> affliction instanceof CompoundAffliction ? Either.left(affliction) : Either.right(affliction)
+            affliction -> affliction instanceof CompoundAffliction ? Either.right(affliction) : Either.left(affliction)
     );
 
     void afflict(PlayerEntity player, ItemStack stack);
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/affliction/CompoundAffliction.java b/src/main/java/com/minelittlepony/unicopia/diet/affliction/CompoundAffliction.java
index d41cf4ad..0d561985 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/affliction/CompoundAffliction.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/affliction/CompoundAffliction.java
@@ -7,7 +7,7 @@ import net.minecraft.item.ItemStack;
 import net.minecraft.network.PacketByteBuf;
 import net.minecraft.text.Text;
 
-class CompoundAffliction implements Affliction {
+public class CompoundAffliction implements Affliction {
     public final List<Affliction> afflictions;
     private final Text name;
 
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/affliction/HealingAffliction.java b/src/main/java/com/minelittlepony/unicopia/diet/affliction/HealingAffliction.java
index 71590879..8e6bc53d 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/affliction/HealingAffliction.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/affliction/HealingAffliction.java
@@ -8,7 +8,7 @@ import net.minecraft.item.ItemStack;
 import net.minecraft.network.PacketByteBuf;
 import net.minecraft.text.Text;
 
-record HealingAffliction(float health) implements Affliction {
+public record HealingAffliction(float health) implements Affliction {
     public static final Codec<HealingAffliction> CODEC = RecordCodecBuilder.create(instance -> instance.group(
             Codec.FLOAT.fieldOf("health").forGetter(HealingAffliction::health)
     ).apply(instance, HealingAffliction::new));
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/affliction/Range.java b/src/main/java/com/minelittlepony/unicopia/diet/affliction/Range.java
index ed84ad97..78c3abe1 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/affliction/Range.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/affliction/Range.java
@@ -7,7 +7,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
 import net.minecraft.network.PacketByteBuf;
 import net.minecraft.util.dynamic.Codecs;
 
-record Range(int min, int max) {
+public record Range(int min, int max) {
     public static final Codec<Range> CODEC = Codecs.xor(
             Codec.INT.xmap(value -> Range.of(value, -1), range -> range.min()),
             RecordCodecBuilder.<Range>create(instance -> instance.group(
@@ -20,6 +20,10 @@ record Range(int min, int max) {
         return new Range(min, max);
     }
 
+    public static Range of(int exact) {
+        return of(exact, exact);
+    }
+
     public static Range of(PacketByteBuf buffer) {
         return of(buffer.readInt(), buffer.readInt());
     }
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/affliction/StatusEffectAffliction.java b/src/main/java/com/minelittlepony/unicopia/diet/affliction/StatusEffectAffliction.java
index d0c0b91c..9bc7adff 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/affliction/StatusEffectAffliction.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/affliction/StatusEffectAffliction.java
@@ -4,6 +4,7 @@ import com.mojang.serialization.Codec;
 import com.mojang.serialization.codecs.RecordCodecBuilder;
 
 import net.minecraft.entity.attribute.EntityAttributes;
+import net.minecraft.entity.effect.StatusEffect;
 import net.minecraft.entity.effect.StatusEffectInstance;
 import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.item.ItemStack;
@@ -11,25 +12,24 @@ import net.minecraft.network.PacketByteBuf;
 import net.minecraft.registry.Registries;
 import net.minecraft.text.MutableText;
 import net.minecraft.text.Text;
-import net.minecraft.util.Identifier;
 import net.minecraft.util.StringHelper;
 import net.minecraft.util.math.MathHelper;
 
-record StatusEffectAffliction(Identifier effect, Range seconds, Range amplifier, int chance) implements Affliction {
+public record StatusEffectAffliction(StatusEffect effect, Range seconds, Range amplifier, int chance) implements Affliction {
     public static final Codec<StatusEffectAffliction> CODEC = RecordCodecBuilder.create(instance -> instance.group(
-            Identifier.CODEC.fieldOf("effect").forGetter(StatusEffectAffliction::effect),
+            Registries.STATUS_EFFECT.getCodec().fieldOf("effect").forGetter(StatusEffectAffliction::effect),
             Range.CODEC.fieldOf("seconds").forGetter(StatusEffectAffliction::seconds),
             Range.CODEC.optionalFieldOf("amplifier", Range.of(0, -1)).forGetter(StatusEffectAffliction::amplifier),
             Codec.INT.optionalFieldOf("chance", 0).forGetter(StatusEffectAffliction::chance)
     ).apply(instance, StatusEffectAffliction::new));
 
     public StatusEffectAffliction(PacketByteBuf buffer) {
-        this(buffer.readIdentifier(), Range.of(buffer), Range.of(buffer), buffer.readInt());
+        this(Registries.STATUS_EFFECT.get(buffer.readIdentifier()), Range.of(buffer), Range.of(buffer), buffer.readInt());
     }
 
     @Override
     public void toBuffer(PacketByteBuf buffer) {
-        buffer.writeIdentifier(effect);
+        buffer.writeIdentifier(Registries.STATUS_EFFECT.getId(effect));
         seconds.toBuffer(buffer);
         amplifier.toBuffer(buffer);
         buffer.writeInt(chance);
@@ -45,35 +45,31 @@ record StatusEffectAffliction(Identifier effect, Range seconds, Range amplifier,
         if (chance > 0 && player.getWorld().random.nextInt(chance) > 0) {
             return;
         }
-        Registries.STATUS_EFFECT.getOrEmpty(effect).ifPresent(effect -> {
-            float health = player.getHealth();
-            StatusEffectInstance current = player.getStatusEffect(effect);
-            player.addStatusEffect(new StatusEffectInstance(effect,
-                    seconds.getClamped(current == null ? 0 : current.getDuration(), 20),
-                    amplifier.getClamped(current == null ? 0 : current.getAmplifier(), 1)
-            ));
-            // keep original health
-            if (effect.getAttributeModifiers().containsKey(EntityAttributes.GENERIC_MAX_HEALTH)) {
-                player.setHealth(MathHelper.clamp(health, 0, player.getMaxHealth()));
-            }
-        });
+        float health = player.getHealth();
+        StatusEffectInstance current = player.getStatusEffect(effect);
+        player.addStatusEffect(new StatusEffectInstance(effect,
+                seconds.getClamped(current == null ? 0 : current.getDuration(), 20),
+                amplifier.getClamped(current == null ? 0 : current.getAmplifier(), 1)
+        ));
+        // keep original health
+        if (effect.getAttributeModifiers().containsKey(EntityAttributes.GENERIC_MAX_HEALTH)) {
+            player.setHealth(MathHelper.clamp(health, 0, player.getMaxHealth()));
+        }
     }
 
     @Override
     public Text getName() {
-        return Registries.STATUS_EFFECT.getOrEmpty(effect).map(effect -> {
-            MutableText text = effect.getName().copy();
+        MutableText text = effect.getName().copy();
 
-            if (amplifier.min() > 0) {
-                text = Text.translatable("potion.withAmplifier", text, Text.translatable("potion.potency." + (amplifier.min())));
-            }
+        if (amplifier.min() > 0) {
+            text = Text.translatable("potion.withAmplifier", text, Text.translatable("potion.potency." + (amplifier.min())));
+        }
 
-            text = Text.translatable("potion.withDuration", text, StringHelper.formatTicks(seconds.min() * 20));
+        text = Text.translatable("potion.withDuration", text, StringHelper.formatTicks(seconds.min() * 20));
 
-            if (chance > 0) {
-                text = Text.translatable("potion.withChance", chance, text);
-            }
-            return (Text)text;
-        }).orElse(EMPTY.getName());
+        if (chance > 0) {
+            text = Text.translatable("potion.withChance", chance, text);
+        }
+        return text;
     }
 }
\ No newline at end of file
diff --git a/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java b/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java
index ee093b08..8217a4c1 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java
@@ -30,10 +30,6 @@ public interface UFoodComponents {
     FoodComponent CHOCOLATE_OATMEAL_COOKIE = builder(3, 0.4F).build();
     FoodComponent SCONE = builder(2, 0.2F).build();
 
-    @Deprecated
-    FoodComponent RANDOM_FOLIAGE = builder(2, 1).build();
-    @Deprecated
-    FoodComponent RANDOM_FOLIAGE_FILLING = builder(18, 1).build();
     FoodComponent WORMS = builder(1, 1.5F).alwaysEdible().meat().build();
     FoodComponent INSECTS = builder(1, 0).alwaysEdible().build();
 
@@ -62,9 +58,7 @@ public interface UFoodComponents {
 
     FoodComponent POISON_JOKE = builder(0, 0F).alwaysEdible().snack().build();
 
-    @Deprecated
     FoodComponent SHELL = builder(3, 5).build();
-    @Deprecated
     FoodComponent SHELLY = builder(6, 7).build();
 
     static FoodComponent.Builder builder(int hunger, float saturation) {
diff --git a/src/main/resources/data/c/tags/items/foraging/edibles.json b/src/main/resources/data/c/tags/items/foraging/edibles.json
deleted file mode 100644
index a0432ed7..00000000
--- a/src/main/resources/data/c/tags/items/foraging/edibles.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    { "id": "#c:mushrooms", "required": false },
-    { "id": "#c:saplings", "required": false },
-    { "id": "farmersdelight:sandy_shrub", "required": false },
-    { "id": "farmersdelight:wild_cabbages", "required": false },
-    { "id": "farmersdelight:wild_onions", "required": false },
-    { "id": "farmersdelight:wild_carrots", "required": false },
-    { "id": "farmersdelight:wild_beetroots", "required": false },
-    { "id": "farmersdelight:wild_rice", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/foraging/edibles_filling.json b/src/main/resources/data/c/tags/items/foraging/edibles_filling.json
deleted file mode 100644
index bc799386..00000000
--- a/src/main/resources/data/c/tags/items/foraging/edibles_filling.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    { "id": "farmersdelight:horse_feed", "required": false },
-    { "id": "farmersdelight:rice_bale", "required": false },
-    { "id": "farmersdelight:straw_bale", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/c/tags/items/foraging/risky.json b/src/main/resources/data/c/tags/items/foraging/risky.json
deleted file mode 100644
index aa54573a..00000000
--- a/src/main/resources/data/c/tags/items/foraging/risky.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    { "id": "#c:meads", "required": false },
-    { "id": "farmersdelight:wild_tomatoes", "required": false },
-    { "id": "farmersdelight:wild_potatoes", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/diets/food_groups/baked_goods.json b/src/main/resources/data/unicopia/diets/food_groups/baked_goods.json
deleted file mode 100644
index 12d8e949..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/baked_goods.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "tags": [ "unicopia:baked_goods" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/bat_ponys_delight.json b/src/main/resources/data/unicopia/diets/food_groups/bat_ponys_delight.json
deleted file mode 100644
index 73e2acf8..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/bat_ponys_delight.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "tags": [ "c:mangoes" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 0.1
-  },
-  "ailment": {
-    "effects": []
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/candy.json b/src/main/resources/data/unicopia/diets/food_groups/candy.json
deleted file mode 100644
index e5df4d14..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/candy.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "tags": [ "c:candy" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/desserts.json b/src/main/resources/data/unicopia/diets/food_groups/desserts.json
deleted file mode 100644
index 4892ee6b..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/desserts.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "tags": [ "c:desserts" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/fish/cooked.json b/src/main/resources/data/unicopia/diets/food_groups/fish/cooked.json
deleted file mode 100644
index cddeb511..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/fish/cooked.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "tags": [ "c:cooked_fish" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 0.1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/fish/raw.json b/src/main/resources/data/unicopia/diets/food_groups/fish/raw.json
deleted file mode 100644
index d33785d3..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/fish/raw.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "tags": [ "c:raw_fish" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 0.1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "minecraft:poison",
-        "seconds": 45,
-        "amplifier": 2,
-        "chance": 80
-      },
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/fish/rotten.json b/src/main/resources/data/unicopia/diets/food_groups/fish/rotten.json
deleted file mode 100644
index fc362289..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/fish/rotten.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "tags": [ "c:rotten_fish" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 0.1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "minecraft:poison",
-        "seconds": 45,
-        "amplifier": 2,
-        "chance": 80
-      },
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 400,
-        "amplifier": 3,
-        "chance": 5
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/blinding.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/blinding.json
deleted file mode 100644
index ca392e74..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/blinding.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/forage_blinding" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "minecraft:blindness",
-        "seconds": 30,
-        "amplifier": 0
-      },
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/dangerous.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/dangerous.json
deleted file mode 100644
index 372abede..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/dangerous.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/forage_dangerous" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 250,
-        "amplifier": 2,
-        "chance": 4
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/edible.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/edible.json
deleted file mode 100644
index 33887410..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/edible.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/forage_edible" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": []
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/edible_filling.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/edible_filling.json
deleted file mode 100644
index b6721b3f..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/edible_filling.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/forage_edible_filling" ],
-  "food_component": {
-    "hunger": 18,
-    "saturation": 9
-  },
-  "ailment": {
-    "effects": []
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/leafy_greens.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/leafy_greens.json
deleted file mode 100644
index 0c81ee64..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/leafy_greens.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "tags": [ "minecraft:leaves" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 1.5
-  },
-  "ailment": {
-    "effects": []
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/moderate.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/moderate.json
deleted file mode 100644
index 4d7a238a..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/moderate.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/forage_moderate" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2,
-        "chance": 40
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/nauseating.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/nauseating.json
deleted file mode 100644
index e79628a0..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/nauseating.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/forage_nauseating" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "minecraft:weakness",
-        "seconds": 200,
-        "amplifier": 1,
-        "chance": 30
-      },
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/prickly.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/prickly.json
deleted file mode 100644
index d3c61180..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/prickly.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/forage_prickly" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "minecraft:instant_damage",
-        "seconds": 1,
-        "amplifier": 0,
-        "chance": 30
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/radioactive.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/radioactive.json
deleted file mode 100644
index 7a023788..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/radioactive.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/forage_radioactive" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "minecraft:glowing",
-        "seconds": 15,
-        "amplifier": 0,
-        "chance": 30
-      },
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/risky.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/risky.json
deleted file mode 100644
index 9ee5d1dc..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/risky.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/forage_risky" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2,
-        "chance": 80
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/severely_nauseating.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/severely_nauseating.json
deleted file mode 100644
index 7f4ef36e..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/severely_nauseating.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/forage_severely_nauseating" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "minecraft:weakness",
-        "seconds": 200,
-        "amplifier": 1
-      },
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/severely_prickly.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/severely_prickly.json
deleted file mode 100644
index 3b5a1cd6..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/severely_prickly.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/forage_severely_prickly" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "minecraft:instant_damage",
-        "seconds": 1,
-        "amplifier": 0
-      },
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/foraging/strengthening.json b/src/main/resources/data/unicopia/diets/food_groups/foraging/strengthening.json
deleted file mode 100644
index 11477833..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/foraging/strengthening.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/forage_strengthening" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "minecraft:strength",
-        "seconds": 30,
-        "amplifier": 0
-      },
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2,
-        "chance": 10
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/fruit.json b/src/main/resources/data/unicopia/diets/food_groups/fruit.json
deleted file mode 100644
index c8710f92..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/fruit.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "tags": [ "c:fruits" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/insect/cooked.json b/src/main/resources/data/unicopia/diets/food_groups/insect/cooked.json
deleted file mode 100644
index 7d35ae55..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/insect/cooked.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "tags": [  "c:cooked_insect" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 0.1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/insect/raw.json b/src/main/resources/data/unicopia/diets/food_groups/insect/raw.json
deleted file mode 100644
index 37686f06..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/insect/raw.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "tags": [ "c:raw_insect" ],
-  "food_component": {
-    "hunger": 6,
-    "saturation": 0.3
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/love.json b/src/main/resources/data/unicopia/diets/food_groups/love.json
deleted file mode 100644
index 29837e42..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/love.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "tags": [ "unicopia:container_with_love" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 0.4
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 50,
-        "amplifier": 2
-      },
-      {
-        "name": "unicopia.affliction.love_sickness",
-        "type": "unicopia:lose_hunger",
-        "multiplier": 0.5
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/meat/cooked.json b/src/main/resources/data/unicopia/diets/food_groups/meat/cooked.json
deleted file mode 100644
index 49207c8b..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/meat/cooked.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "tags": [ "c:cooked_meat" ],
-  "food_component": {
-    "hunger": 12,
-    "saturation": 1.2
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/meat/raw.json b/src/main/resources/data/unicopia/diets/food_groups/meat/raw.json
deleted file mode 100644
index 18879268..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/meat/raw.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "tags": [ "c:raw_meat" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "minecraft:poison",
-        "seconds": 45,
-        "amplifier": 2,
-        "chance": 80
-      },
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2,
-        "chance": 5
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/meat/rotten.json b/src/main/resources/data/unicopia/diets/food_groups/meat/rotten.json
deleted file mode 100644
index 1af89970..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/meat/rotten.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "tags": [ "c:rotten_meat" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "effect": "minecraft:poison",
-        "seconds": 45,
-        "amplifier": 2,
-        "chance": 80
-      },
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 400,
-        "amplifier": 3,
-        "chance": 5
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/nuts_and_seeds.json b/src/main/resources/data/unicopia/diets/food_groups/nuts_and_seeds.json
deleted file mode 100644
index eee07b2d..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/nuts_and_seeds.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "tags": [ "c:grain", "c:nuts", "c:seeds" ],
-  "food_component": {
-    "hunger": 2,
-    "saturation": 2.5
-  },
-  "ailment": {
-    "effects": [
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/pinecone.json b/src/main/resources/data/unicopia/diets/food_groups/pinecone.json
deleted file mode 100644
index 735bff30..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/pinecone.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "tags": [ "c:pinecones" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 0.1
-  },
-  "ailment": {
-    "effects": [
-      {
-        "type": "unicopia:healing",
-        "health": 1
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/rocks.json b/src/main/resources/data/unicopia/diets/food_groups/rocks.json
deleted file mode 100644
index ead61325..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/rocks.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "tags": [ "c:rocks", "unicopia:rock_stews" ],
-  "food_component": {
-    "hunger": 1,
-    "saturation": 0.1
-  },
-  "ailment": {
-    "effects": []
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/cooked.json b/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/cooked.json
deleted file mode 100644
index 06d46526..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/cooked.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/cooked_sea_vegitable", "c:coral_blocks" ],
-  "food_component": {
-    "hunger": 0,
-    "saturation": 0
-  },
-  "ailment": {
-    "effects": [
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/raw.json b/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/raw.json
deleted file mode 100644
index 94bea223..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/sea_vegetable/raw.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "tags": [ "unicopia:food_types/raw_sea_vegitable", "c:corals", "c:coral_fans" ],
-  "food_component": {
-    "hunger": 0,
-    "saturation": 0
-  },
-  "ailment": {
-    "effects": [
-    ]
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/shells.json b/src/main/resources/data/unicopia/diets/food_groups/shells.json
deleted file mode 100644
index f154f859..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/shells.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "tags": [ "unicopia:shells" ],
-  "food_component": {
-    "hunger": 0,
-    "saturation": 0
-  },
-  "ailment": {
-    "effects": []
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/diets/food_groups/shelly.json b/src/main/resources/data/unicopia/diets/food_groups/shelly.json
deleted file mode 100644
index 791711a0..00000000
--- a/src/main/resources/data/unicopia/diets/food_groups/shelly.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  "tags": [ "unicopia:special_shells" ],
-  "food_component": {
-    "hunger": 0,
-    "saturation": 0
-  },
-  "ailment": {
-    "effects": []
-  }
-}
\ No newline at end of file
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/cooked_sea_vegitable.json b/src/main/resources/data/unicopia/tags/items/food_types/cooked_sea_vegitable.json
deleted file mode 100644
index bde823d2..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/cooked_sea_vegitable.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:dried_kelp_block",
-    "minecraft:glow_lichen"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_blinding.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_blinding.json
deleted file mode 100644
index 2734f9e9..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/forage_blinding.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:oxeye_daisy"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_dangerous.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_dangerous.json
deleted file mode 100644
index 40636af9..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/forage_dangerous.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:poppy",
-    "minecraft:lily_of_the_valley"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_edible.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_edible.json
deleted file mode 100644
index ee20b95a..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/forage_edible.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:blue_orchid",
-    "minecraft:red_tulip",
-    "minecraft:orange_tulip",
-    "minecraft:pink_tulip",
-    "minecraft:cornflower",
-    "minecraft:peony",
-    "minecraft:sunflower",
-    "minecraft:dandelion",
-    "minecraft:lilac",
-    "minecraft:tall_grass",
-    "minecraft:wheat",
-    "minecraft:dead_bush",
-    "minecraft:pink_petals",
-    "#c:foraging/edibles"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_edible_filling.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_edible_filling.json
deleted file mode 100644
index c3245df5..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/forage_edible_filling.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:hay_block",
-    "#c:foraging/edibles_filling"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_nauseating.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_nauseating.json
deleted file mode 100644
index 9b5ae248..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/forage_nauseating.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:grass",
-    "unicopia:cider",
-    { "id": "farmersdelight:rotten_tomato", "required": false }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_prickly.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_prickly.json
deleted file mode 100644
index 3fbb59d5..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/forage_prickly.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:rose_bush",
-    "#minecraft:saplings"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_radioactive.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_radioactive.json
deleted file mode 100644
index f274ebe5..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/forage_radioactive.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:azure_bluet",
-    "minecraft:torchflower"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_risky.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_risky.json
deleted file mode 100644
index 533231e1..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/forage_risky.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:allium",
-    "minecraft:white_tulip",
-    "unicopia:burned_juice",
-    "#c:foraging/risky"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_severely_nauseating.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_severely_nauseating.json
deleted file mode 100644
index ae67591c..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/forage_severely_nauseating.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:pitcher_plant"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_severely_prickly.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_severely_prickly.json
deleted file mode 100644
index 4ff8aebc..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/forage_severely_prickly.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:large_fern"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/forage_strengthening.json b/src/main/resources/data/unicopia/tags/items/food_types/forage_strengthening.json
deleted file mode 100644
index 97038fab..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/forage_strengthening.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:fern"
-  ]
-}
diff --git a/src/main/resources/data/unicopia/tags/items/food_types/raw_sea_vegitable.json b/src/main/resources/data/unicopia/tags/items/food_types/raw_sea_vegitable.json
deleted file mode 100644
index cc815d78..00000000
--- a/src/main/resources/data/unicopia/tags/items/food_types/raw_sea_vegitable.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "replace": false,
-  "values": [
-    "minecraft:kelp",
-    "minecraft:dried_kelp",
-    "minecraft:seagrass",
-    "minecraft:sea_pickle",
-    { "id": "farmersdelight:kelp_roll", "required": false },
-    { "id": "farmersdelight:kelp_roll_slice", "required": false }
-  ]
-}

From eeded18c36d1a65ef96da6d717704282d7037db7 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Fri, 5 Apr 2024 14:06:29 +0100
Subject: [PATCH 13/21] Move pony diets to datagen. Summary of diets:

Unicorns:
 - have a general even preference of foods
 - Improved benefits from cooking their food before eating it
 - Can still eat raw and rotten but at a reduced yield

Earth Ponies:
 - Are vegans
 - They get the most from foraging
 - Pastries are their passion
 - If they must eat meat, they have to cook it and not let it spoil.
 - They have a sweet tooth and prefer candy, desserts, and rocks
 - Candy and rocks gives them a massive saturation boost. Maybe too much?

Pegasus
 - prefer fish over other food sources
 - Cannot eat love, or raw/rotten meat
 - Can eat raw and rotten fish but still prefers if they are cooked
 - Can safely eat fresh and cooked fish with no ill effects
 - Is less affected when eating rotten fish

Bat Ponies:
 - prefer cooked foods over raw, and meat/insects over fish
 - Doesn't like baked goods but really likes meats, fish, and insects
 - Gets food poisoning from eating rotten and raw meat
 - Can eat cooked meat and insects without negative effects
 - Becomes hyper when eating mangoes

Kirins:
 - Much like Earth Ponies, Kirins must cook their meat before they eat it
 - Cannot eat love, or raw/rotten meats and fish
 - Can eat cooked meat and insect without negative effects
 - Can eat blinding, prickly, strengthening, and glowing foraged foods without negative effects

Changelings:
 - like meat and fish but really prefer feasting on ponies' love directly from the tap
 - Doesn't like baked goods but really likes meats, fish, and insects
 - Can eat fish, meat, insects, and love without negative effects
 - Gets sick when eating foraged plants and vegetables

Hippogriffs:
 - like fish, nuts, and seeds
 - Can eat fish and prickly foods without negative effect
 - Gains more health from pinecones

Seaponies:
 - can eat seaweed, kelp, shells, and other undersea foods
 - Can eat fish without negative effect
 - Gains more health from pinecones
---
 .../providers/DietProfileGenerator.java       | 263 ++++++++++++++++++
 .../providers/FoodGroupsGenerator.java        |  52 ++--
 .../unicopia/diet/DietProfile.java            |  25 ++
 .../ClearLoveSicknessAffliction.java          |   3 +-
 .../diet/affliction/CompoundAffliction.java   |   4 +
 .../data/unicopia/diets/races/alicorn.json    |  64 -----
 .../data/unicopia/diets/races/bat.json        | 130 ---------
 .../data/unicopia/diets/races/changeling.json | 159 -----------
 .../data/unicopia/diets/races/earth.json      |  81 ------
 .../data/unicopia/diets/races/hippogriff.json |  97 -------
 .../data/unicopia/diets/races/human.json      |  21 --
 .../data/unicopia/diets/races/kirin.json      |  68 -----
 .../data/unicopia/diets/races/pegasus.json    |  66 -----
 .../data/unicopia/diets/races/seapony.json    | 100 -------
 .../data/unicopia/diets/races/unicorn.json    |  39 ---
 15 files changed, 316 insertions(+), 856 deletions(-)
 delete mode 100644 src/main/resources/data/unicopia/diets/races/alicorn.json
 delete mode 100644 src/main/resources/data/unicopia/diets/races/bat.json
 delete mode 100644 src/main/resources/data/unicopia/diets/races/changeling.json
 delete mode 100644 src/main/resources/data/unicopia/diets/races/earth.json
 delete mode 100644 src/main/resources/data/unicopia/diets/races/hippogriff.json
 delete mode 100644 src/main/resources/data/unicopia/diets/races/human.json
 delete mode 100644 src/main/resources/data/unicopia/diets/races/kirin.json
 delete mode 100644 src/main/resources/data/unicopia/diets/races/pegasus.json
 delete mode 100644 src/main/resources/data/unicopia/diets/races/seapony.json
 delete mode 100644 src/main/resources/data/unicopia/diets/races/unicorn.json

diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/DietProfileGenerator.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/DietProfileGenerator.java
index 6afe54f9..4034021f 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/DietProfileGenerator.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/DietProfileGenerator.java
@@ -1,12 +1,275 @@
 package com.minelittlepony.unicopia.datagen.providers;
 
+import java.util.List;
+import java.util.Optional;
 import java.util.function.BiConsumer;
 import com.minelittlepony.unicopia.Race;
+import com.minelittlepony.unicopia.Unicopia;
 import com.minelittlepony.unicopia.diet.DietProfile;
+import com.minelittlepony.unicopia.diet.DietProfile.Multiplier;
+import com.minelittlepony.unicopia.diet.FoodGroupEffects;
+import com.minelittlepony.unicopia.diet.affliction.ClearLoveSicknessAffliction;
+import com.minelittlepony.unicopia.diet.affliction.CompoundAffliction;
+import com.minelittlepony.unicopia.diet.affliction.HealingAffliction;
+import com.minelittlepony.unicopia.diet.affliction.LoseHungerAffliction;
+import com.minelittlepony.unicopia.diet.affliction.Range;
+import com.minelittlepony.unicopia.diet.affliction.StatusEffectAffliction;
+import com.minelittlepony.unicopia.entity.effect.UEffects;
+import com.minelittlepony.unicopia.item.UFoodComponents;
+
+import net.minecraft.entity.effect.StatusEffects;
+import net.minecraft.item.FoodComponents;
 
 public class DietProfileGenerator {
 
     public void generate(BiConsumer<Race, DietProfile> exporter) {
+        // Pinecones are for everypony
+        var pineconeMultiplier = new Multiplier.Builder().tag(Unicopia.id("pinecone")).hunger(0.9F).saturation(0.9F).build();
+        var bakedGoodPreference = new Multiplier.Builder().tag(Unicopia.id("baked_goods")).build();
+        var bakedGoodExtremePreference = new Multiplier.Builder().tag(Unicopia.id("baked_goods")).hunger(1.2F).saturation(2).build();
+        var bakedGoodNonPreference = new Multiplier.Builder().tag(Unicopia.id("baked_goods")).hunger(0.4F).saturation(0.2F).build();
+        var properMeatStandards = new Multiplier.Builder().tag(Unicopia.id("love"))
+                .tag(Unicopia.id("meat/raw")).tag(Unicopia.id("insect/raw")).tag(Unicopia.id("fish/raw"))
+                .tag(Unicopia.id("meat/rotten")).tag(Unicopia.id("insect/rotten")).tag(Unicopia.id("fish/rotten"))
+                .hunger(0).saturation(0).build();
+        var avianMeatStandards = new Multiplier.Builder().tag(Unicopia.id("love"))
+                .tag(Unicopia.id("meat/raw")).tag(Unicopia.id("insect/raw"))
+                .tag(Unicopia.id("meat/rotten")).tag(Unicopia.id("insect/rotten"))
+                .hunger(0).saturation(0).build();
+        var loveSicknessEffects = CompoundAffliction.of(
+                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 95),
+                new StatusEffectAffliction(StatusEffects.WEAKNESS, Range.of(200), Range.of(1), 0),
+                new LoseHungerAffliction(0.5F));
+        var seaFoodExclusions = new Multiplier.Builder()
+                .tag(Unicopia.id("sea_vegetable/raw")).tag(Unicopia.id("sea_vegetable/cooked"))
+                .tag(Unicopia.id("shells")).tag(Unicopia.id("special_shells"))
+                .hunger(0).saturation(0).build();
 
+        exporter.accept(Race.HUMAN, new DietProfile(1, 0, List.of(), List.of(
+                new FoodGroupEffects.Builder()
+                .tag(Unicopia.id("fish/cooked")).tag(Unicopia.id("fish/raw")).tag(Unicopia.id("fish/rotten"))
+                .tag(Unicopia.id("meat/cooked")).tag(Unicopia.id("meat/raw")).tag(Unicopia.id("meat/rotten"))
+                .tag(Unicopia.id("sea_vegetable/cooked")).tag(Unicopia.id("sea_vegetable/raw"))
+                .tag(Unicopia.id("pinecone"))
+                .build()
+        ), Optional.empty()));
+        // Alicorns are a mashup of unicorn, pegasus, and earth pony eating habits
+        exporter.accept(Race.ALICORN, new DietProfile(0.9F, 1, List.of(
+                // Pastries are their passion
+                bakedGoodExtremePreference, pineconeMultiplier, avianMeatStandards, seaFoodExclusions,
+                // They have a more of a sweet tooth than earth ponies
+                new Multiplier.Builder().tag(Unicopia.id("desserts")).hunger(2.5F).saturation(1.7F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("candy")).tag(Unicopia.id("rocks")).hunger(1.5F).saturation(1.3F).build(),
+
+                // Cannot eat love, or raw/rotten meats and fish
+                // Can eat raw and rotten fish but still prefers if they are cooked
+                new Multiplier.Builder().tag(Unicopia.id("fish/cooked")).hunger(1.5F).saturation(1.5F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/cooked")).hunger(0.25F).saturation(0.16F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/cooked")).hunger(0.1F).saturation(0.7F).build(),
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/raw")).hunger(0.5F).saturation(0.8F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("fish/rotten")).hunger(0.25F).saturation(0.5F).build()
+        ), List.of(
+                // Can safely eat fresh and cooked fish with no ill effects
+                new FoodGroupEffects.Builder().tag(Unicopia.id("fish/cooked")).tag(Unicopia.id("fish/raw")).build(),
+                // Is less affected when eating rotten fish
+                new FoodGroupEffects.Builder().tag(Unicopia.id("fish/rotten")).ailment(new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(50), Range.of(2), 95)).build()
+        ), Optional.empty()));
+        // Unicorns have a general even preference of foods
+        exporter.accept(Race.UNICORN, new DietProfile(1.1F, 1, List.of(
+                bakedGoodPreference, pineconeMultiplier, seaFoodExclusions,
+                new Multiplier.Builder().tag(Unicopia.id("love")).hunger(0).saturation(0).build(),
+
+                // Improved benefits from cooking their food
+                new Multiplier.Builder().tag(Unicopia.id("fish/cooked")).hunger(0.3F).saturation(0.2F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/cooked")).hunger(0.4F).saturation(0.4F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/cooked")).hunger(0.1F).saturation(0.1F).build(),
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/raw")).hunger(0.25F).saturation(0.1F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/raw")).hunger(0.3F).saturation(0.1F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/raw")).hunger(0.15F).saturation(0.1F).build(),
+
+                // Can still eat raw and rotten but at a reduced yield
+                new Multiplier.Builder().tag(Unicopia.id("fish/rotten")).hunger(0.1F).saturation(0.1F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/rotten")).hunger(0.1F).saturation(0.1F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/rotten")).hunger(0).saturation(0.1F).build()
+        ), List.of(), Optional.empty()));
+        // Bats prefer cooked foods over raw, and meat/insects over fish
+        exporter.accept(Race.BAT, new DietProfile(0.7F, 0.9F, List.of(
+                pineconeMultiplier, seaFoodExclusions,
+                // Doesn't like baked goods but really likes meats, fish, and insects
+                bakedGoodNonPreference,
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/cooked")).hunger(0.75F).saturation(0.75F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/cooked")).hunger(1.15F).saturation(1.16F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/cooked")).hunger(1.75F).saturation(1.74F).build(),
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/raw")).hunger(0.5F).saturation(0.6F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/raw")).hunger(0.25F).saturation(0.25F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/raw")).hunger(1).saturation(1).build(),
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/rotten")).hunger(0.24F).saturation(0.25F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/rotten")).hunger(0.2F).saturation(0.2F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/rotten")).hunger(0.9F).saturation(0.9F).build()
+        ), List.of(
+                // Gets food poisoning from eating rotten and raw meat
+                new FoodGroupEffects.Builder().tag(Unicopia.id("fish/rotten")).tag(Unicopia.id("meat/raw")).tag(Unicopia.id("meat/rotten")).ailment(new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 5)).build(),
+                new FoodGroupEffects.Builder().tag(Unicopia.id("insect/rotten")).ailment(new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(50), Range.of(1), 15)).build(),
+                // Can eat cooked meat and insects without negative effects
+                new FoodGroupEffects.Builder().tag(Unicopia.id("insect/cooked")).tag(Unicopia.id("meat/cooked")).build(),
+                // Becomes hyper when eating mangoes
+                new FoodGroupEffects.Builder().tag(Unicopia.id("bat_ponys_delight")).ailment(CompoundAffliction.of(
+                        new StatusEffectAffliction(StatusEffects.HEALTH_BOOST, Range.of(30, 60), Range.of(2, 6), 0),
+                        new StatusEffectAffliction(StatusEffects.JUMP_BOOST, Range.of(30, 60), Range.of(1, 6), 0),
+                        new StatusEffectAffliction(StatusEffects.REGENERATION, Range.of(3, 30), Range.of(3, 6), 0)
+                )).build()
+        ), Optional.empty()));
+        // Much like Earth Ponies, Kirins must cook their meat before they eat it
+        exporter.accept(Race.KIRIN, new DietProfile(0.6F, 0.9F, List.of(
+                // Cannot eat love, or raw/rotten meats and fish
+                bakedGoodPreference, properMeatStandards, pineconeMultiplier, seaFoodExclusions,
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/cooked")).hunger(0.75F).saturation(0.35F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/cooked")).hunger(1.5F).saturation(1.6F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/cooked")).hunger(0.25F).saturation(0.74F).build()
+        ), List.of(
+                // can eat these without negative effects
+                new FoodGroupEffects.Builder().tag(Unicopia.id("insect/cooked")).tag(Unicopia.id("meat/cooked")).food(FoodComponents.COOKED_BEEF).build(),
+                new FoodGroupEffects.Builder().tag(Unicopia.id("foraging/blinding")).food(4, 0.2F).build(),
+                new FoodGroupEffects.Builder().tag(Unicopia.id("foraging/prickly")).food(0, 1.5F).build(),
+                new FoodGroupEffects.Builder().tag(Unicopia.id("foraging/severely_prickly")).food(2, 0.9F).build(),
+                new FoodGroupEffects.Builder().tag(Unicopia.id("foraging/strengthening")).food(4, 0.2F).ailment(new StatusEffectAffliction(StatusEffects.STRENGTH, Range.of(1300), Range.of(0), 0)).build(),
+                new FoodGroupEffects.Builder().tag(Unicopia.id("foraging/glowing")).food(1, 1.6F).ailment(new StatusEffectAffliction(StatusEffects.GLOWING, Range.of(30), Range.of(0), 30)).build()
+        ), Optional.empty()));
+        // Earth Ponies are vegans. They get the most from foraging
+        exporter.accept(Race.EARTH, new DietProfile(0.7F, 1.2F, List.of(
+                // Pastries are their passion
+                // If they must eat meat, they have to cook it and not let it spoil.
+                bakedGoodExtremePreference, pineconeMultiplier, properMeatStandards, seaFoodExclusions,
+                // They have a sweet tooth
+                new Multiplier.Builder().tag(Unicopia.id("candy")).tag(Unicopia.id("desserts")).tag(Unicopia.id("rocks")).hunger(2.5F).saturation(1.7F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("fish/cooked")).hunger(0.2F).saturation(0.3F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/cooked")).hunger(0.1F).saturation(0.1F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/cooked")).hunger(0.1F).saturation(0.1F).build()
+        ), List.of(
+                // Candy and rocks gives them a massive saturation boost. Maybe too much?
+                new FoodGroupEffects.Builder().tag(Unicopia.id("candy")).tag(Unicopia.id("rocks")).food(UFoodComponents.builder(5, 12).alwaysEdible()).build(),
+                new FoodGroupEffects.Builder().tag(Unicopia.id("desserts")).food(UFoodComponents.builder(12, 32).snack().alwaysEdible()).build()
+        ), Optional.empty()));
+        // Pegasi prefer fish over other food sources
+        exporter.accept(Race.PEGASUS, new DietProfile(0.9F, 1, List.of(
+                bakedGoodPreference, pineconeMultiplier, avianMeatStandards, seaFoodExclusions,
+                // Cannot eat love, or raw/rotten meat
+                // Can eat raw and rotten fish but still prefers if they are cooked
+                new Multiplier.Builder().tag(Unicopia.id("fish/cooked")).hunger(1.5F).saturation(1.5F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/cooked")).hunger(0.25F).saturation(0.16F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/cooked")).hunger(0.1F).saturation(0.7F).build(),
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/raw")).hunger(0.5F).saturation(0.8F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("fish/rotten")).hunger(0.25F).saturation(0.5F).build()
+        ), List.of(
+                // Can safely eat fresh and cooked fish with no ill effects
+                new FoodGroupEffects.Builder().tag(Unicopia.id("fish/cooked")).tag(Unicopia.id("fish/raw")).build(),
+                // Is less affected when eating rotten fish
+                new FoodGroupEffects.Builder().tag(Unicopia.id("fish/rotten")).ailment(new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(50), Range.of(2), 95)).build()
+        ), Optional.empty()));
+        // Changelings like meat and fish but really prefer feasting on ponies' love directly from the tap
+        exporter.accept(Race.CHANGELING, new DietProfile(0.15F, 0.1F, List.of(
+                // Doesn't like baked goods but really likes meats, fish, and insects
+                bakedGoodNonPreference, pineconeMultiplier, seaFoodExclusions,
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/cooked")).hunger(0.5F).saturation(1.2F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/cooked")).hunger(0.9F).saturation(1.2F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/cooked")).hunger(1.2F).saturation(1.3F).build(),
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/raw")).hunger(0.15F).saturation(0.25F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/raw")).hunger(1.25F).saturation(1.25F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/raw")).hunger(1).saturation(1).build(),
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/rotten")).hunger(0.24F).saturation(0.25F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/rotten")).hunger(0.2F).saturation(0.2F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("insect/rotten")).hunger(0.9F).saturation(0.9F).build(),
+
+                new Multiplier.Builder().tag(Unicopia.id("love")).hunger(1).saturation(1.5F).build()
+        ), List.of(
+                // Can eat fish, meat, insects, and love without negative effects
+                new FoodGroupEffects.Builder()
+                    .tag(Unicopia.id("fish/cooked")).tag(Unicopia.id("meat/cooked")).tag(Unicopia.id("insect/cooked"))
+                    .tag(Unicopia.id("fish/rotten")).tag(Unicopia.id("meat/rotten")).tag(Unicopia.id("insect/rotten"))
+                    .tag(Unicopia.id("fish/raw")).tag(Unicopia.id("meat/raw")).tag(Unicopia.id("insect/raw"))
+                    .tag(Unicopia.id("baked_goods")).tag(Unicopia.id("pinecone"))
+                    .tag(Unicopia.id("love")).ailment(ClearLoveSicknessAffliction.INSTANCE).build(),
+
+                new FoodGroupEffects.Builder()
+                    .tag(Unicopia.id("foraging/blinding")).tag(Unicopia.id("foraging/dangerous")).tag(Unicopia.id("foraging/edible_filling"))
+                    .tag(Unicopia.id("foraging/edible")).tag(Unicopia.id("foraging/leafy_greens")).tag(Unicopia.id("foraging/nauseating"))
+                    .tag(Unicopia.id("foraging/prickly")).tag(Unicopia.id("foraging/glowing")).tag(Unicopia.id("foraging/risky"))
+                    .tag(Unicopia.id("foraging/severely_nauseating")).tag(Unicopia.id("foraging/severely_prickly"))
+                    .tag(Unicopia.id("foraging/strengthening"))
+                    .ailment(loveSicknessEffects)
+                    .build()
+        ), Optional.empty()));
+        // Hippogriffs like fish, nuts, and seeds
+        exporter.accept(Race.HIPPOGRIFF, new DietProfile(0.5F, 0.8F, List.of(
+                bakedGoodPreference, pineconeMultiplier, seaFoodExclusions,
+
+                new Multiplier.Builder().tag(Unicopia.id("love"))
+                    .tag(Unicopia.id("insect/cooked")).tag(Unicopia.id("insect/raw")).tag(Unicopia.id("insect/rotten"))
+                    .hunger(0).saturation(0).build(),
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/cooked")).hunger(1.5F).saturation(1.2F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/cooked")).hunger(1.9F).saturation(1.2F).build(),
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/raw")).hunger(0.85F).saturation(0.95F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/raw")).hunger(0.75F).saturation(0.75F).build(),
+
+                new Multiplier.Builder().tag(Unicopia.id("fish/rotten")).hunger(0.24F).saturation(0.25F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("meat/rotten")).hunger(0.3F).saturation(0.5F).build(),
+
+                new Multiplier.Builder().tag(Unicopia.id("nuts_and_seeds")).hunger(1.4F).saturation(1.4F).build()
+        ), List.of(
+                // Can eat fish and prickly foods without negative effect
+                new FoodGroupEffects.Builder()
+                    .tag(Unicopia.id("fish/cooked")).tag(Unicopia.id("fish/raw")).tag(Unicopia.id("fish/rotten"))
+                    .tag(Unicopia.id("foraging/prickly")).tag(Unicopia.id("foraging/severely_prickly"))
+                    .build(),
+                // Gains more health from pinecones
+                new FoodGroupEffects.Builder().tag(Unicopia.id("pinecone")).ailment(new HealingAffliction(3)).build()
+        ), Optional.empty()));
+        // Seaponies can eat seaweed, kelp, shells, and other undersea foods
+        exporter.accept(Race.SEAPONY, new DietProfile(0.5F, 0.8F, List.of(
+                new Multiplier.Builder().tag(Unicopia.id("fish/cooked")).hunger(1.5F).saturation(1.2F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("fish/raw")).hunger(0.85F).saturation(0.95F).build(),
+                new Multiplier.Builder().tag(Unicopia.id("fish/rotten")).hunger(0.24F).saturation(0.25F).build(),
+                new Multiplier.Builder()
+                    .tag(Unicopia.id("sea_vegetable/raw"))
+                    .tag(Unicopia.id("sea_vegetable/cooked"))
+                    .tag(Unicopia.id("shells")).tag(Unicopia.id("special_shells"))
+                    .hunger(1).saturation(1).build()
+        ), List.of(
+                // Can eat fish without negative effect
+                new FoodGroupEffects.Builder()
+                    .tag(Unicopia.id("fish/cooked")).tag(Unicopia.id("fish/raw")).tag(Unicopia.id("fish/rotten"))
+                    .build(),
+                // Gains more health from pinecones
+                new FoodGroupEffects.Builder().tag(Unicopia.id("pinecone")).ailment(new HealingAffliction(3)).build()
+        ), Optional.empty()));
     }
 }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/FoodGroupsGenerator.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/FoodGroupsGenerator.java
index c1e4bf75..3959da20 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/FoodGroupsGenerator.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/FoodGroupsGenerator.java
@@ -41,9 +41,7 @@ public class FoodGroupsGenerator {
                 .tag(UConventionalTags.Items.GRAIN).tag(UConventionalTags.Items.NUTS).tag(UConventionalTags.Items.SEEDS)
                 .food(UFoodComponents.BANANA)
         );
-        exporter.accept(Unicopia.id("pinecone"), new FoodGroupEffects.Builder().tag(UConventionalTags.Items.PINECONES).food(UFoodComponents.PINECONE).ailment(new CompoundAffliction(List.<Affliction>of(
-                new HealingAffliction(1)
-        ))));
+        exporter.accept(Unicopia.id("pinecone"), new FoodGroupEffects.Builder().tag(UConventionalTags.Items.PINECONES).food(UFoodComponents.PINECONE).ailment(new HealingAffliction(1)));
 
         provideMeatCategory("fish",
                 UConventionalTags.Items.COOKED_FISH, UConventionalTags.Items.RAW_FISH, UConventionalTags.Items.ROTTEN_FISH,
@@ -58,59 +56,53 @@ public class FoodGroupsGenerator {
                 UTags.Items.HIGH_QUALITY_SEA_VEGETABLES, UTags.Items.LOW_QUALITY_SEA_VEGETABLES,
                 FoodComponents.COOKED_BEEF, FoodComponents.BEEF, exporter);
 
-        exporter.accept(Unicopia.id("foraging/blinding"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_BLINDING).food(4, 0.2F).ailment(new CompoundAffliction(List.<Affliction>of(
+        exporter.accept(Unicopia.id("foraging/blinding"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_BLINDING).food(4, 0.2F).ailment(CompoundAffliction.of(
                 new StatusEffectAffliction(StatusEffects.BLINDNESS, Range.of(30), Range.of(0), 50),
                 new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 12)
-        ))));
-        exporter.accept(Unicopia.id("foraging/dangerous"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_DANGEROUS).food(3, 0.3F).ailment(new CompoundAffliction(List.<Affliction>of(
-                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(250), Range.of(2), 4)
-        ))));
+        )));
+        exporter.accept(Unicopia.id("foraging/dangerous"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_DANGEROUS).food(3, 0.3F).ailment(new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(250), Range.of(2), 4)));
         exporter.accept(Unicopia.id("foraging/edible_filling"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_FILLING).food(17, 0.6F));
         exporter.accept(Unicopia.id("foraging/edible"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_SAFE).food(2, 1));
         exporter.accept(Unicopia.id("foraging/leafy_greens"), new FoodGroupEffects.Builder().tag(ItemTags.LEAVES).food(1, 1.4F));
-        exporter.accept(Unicopia.id("foraging/nauseating"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_NAUSEATING).food(5, 0.5F).ailment(new CompoundAffliction(List.<Affliction>of(
+        exporter.accept(Unicopia.id("foraging/nauseating"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_NAUSEATING).food(5, 0.5F).ailment(CompoundAffliction.of(
                 new StatusEffectAffliction(StatusEffects.WEAKNESS, Range.of(200), Range.of(1), 30),
                 new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(200), Range.of(2), 0)
-        ))));
-        exporter.accept(Unicopia.id("foraging/prickly"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_PRICKLY).food(0, 1.5F).ailment(new CompoundAffliction(List.<Affliction>of(
+        )));
+        exporter.accept(Unicopia.id("foraging/prickly"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_PRICKLY).food(0, 1.5F).ailment(CompoundAffliction.of(
                 new StatusEffectAffliction(StatusEffects.INSTANT_DAMAGE, Range.of(1), Range.of(0), 30)
-        ))));
-        exporter.accept(Unicopia.id("foraging/glowing"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_GLOWING).food(1, 1.6F).ailment(new CompoundAffliction(List.<Affliction>of(
+        )));
+        exporter.accept(Unicopia.id("foraging/glowing"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_GLOWING).food(1, 1.6F).ailment(CompoundAffliction.of(
                 new StatusEffectAffliction(StatusEffects.GLOWING, Range.of(30), Range.of(0), 30),
                 new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 0)
-        ))));
-        exporter.accept(Unicopia.id("foraging/risky"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_RISKY).food(9, 1.1F).ailment(new CompoundAffliction(List.<Affliction>of(
-                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 80)
-        ))));
-        exporter.accept(Unicopia.id("foraging/severely_nauseating"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_SEVERE_NAUSEATING).food(3, 0.9F).ailment(new CompoundAffliction(List.<Affliction>of(
+        )));
+        exporter.accept(Unicopia.id("foraging/risky"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_RISKY).food(9, 1.1F).ailment(new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 80)));
+        exporter.accept(Unicopia.id("foraging/severely_nauseating"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_SEVERE_NAUSEATING).food(3, 0.9F).ailment(CompoundAffliction.of(
                 new StatusEffectAffliction(StatusEffects.WEAKNESS, Range.of(200), Range.of(1), 0),
                 new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 80)
-        ))));
-        exporter.accept(Unicopia.id("foraging/severely_prickly"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_SEVERE_PRICKLY).food(2, 0.9F).ailment(new CompoundAffliction(List.<Affliction>of(
+        )));
+        exporter.accept(Unicopia.id("foraging/severely_prickly"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_SEVERE_PRICKLY).food(2, 0.9F).ailment(CompoundAffliction.of(
                 new StatusEffectAffliction(StatusEffects.INSTANT_DAMAGE, Range.of(1), Range.of(0), 0),
                 new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 80)
-        ))));
-        exporter.accept(Unicopia.id("foraging/strengthening"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_STRENGHENING).food(4, 0.2F).ailment(new CompoundAffliction(List.<Affliction>of(
+        )));
+        exporter.accept(Unicopia.id("foraging/strengthening"), new FoodGroupEffects.Builder().tag(UTags.Items.FORAGE_STRENGHENING).food(4, 0.2F).ailment(CompoundAffliction.of(
                 new StatusEffectAffliction(StatusEffects.STRENGTH, Range.of(1300), Range.of(0), 0),
                 new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 70)
-        ))));
+        )));
     }
 
     private void provideMeatCategory(String name,
             TagKey<Item> cookedTag, TagKey<Item> rawTag, TagKey<Item> rottenTag,
             FoodComponent cooked, FoodComponent raw, FoodComponent rotten,
             BiConsumer<Identifier, FoodGroupEffects.Builder> exporter) {
-        exporter.accept(Unicopia.id(name + "/cooked"), new FoodGroupEffects.Builder().tag(cookedTag).food(cooked).ailment(new CompoundAffliction(List.<Affliction>of(
-                new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 25)
-        ))));
-        exporter.accept(Unicopia.id(name + "/raw"), new FoodGroupEffects.Builder().tag(rawTag).food(raw).ailment(new CompoundAffliction(List.<Affliction>of(
+        exporter.accept(Unicopia.id(name + "/cooked"), new FoodGroupEffects.Builder().tag(cookedTag).food(cooked).ailment(new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 25)));
+        exporter.accept(Unicopia.id(name + "/raw"), new FoodGroupEffects.Builder().tag(rawTag).food(raw).ailment(CompoundAffliction.of(
                 new StatusEffectAffliction(StatusEffects.POISON, Range.of(45), Range.of(2), 80),
                 new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 65)
-        ))));
-        exporter.accept(Unicopia.id(name + "/rotten"), new FoodGroupEffects.Builder().tag(rottenTag).food(rotten).ailment(new CompoundAffliction(List.<Affliction>of(
+        )));
+        exporter.accept(Unicopia.id(name + "/rotten"), new FoodGroupEffects.Builder().tag(rottenTag).food(rotten).ailment(CompoundAffliction.of(
                 new StatusEffectAffliction(StatusEffects.POISON, Range.of(45), Range.of(2), 80),
                 new StatusEffectAffliction(UEffects.FOOD_POISONING, Range.of(100), Range.of(2), 95)
-        ))));
+        )));
     }
 
     private void provideVegetableCategory(String name,
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java b/src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java
index 18ceaefb..b10a8143 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/DietProfile.java
@@ -193,5 +193,30 @@ public record DietProfile(
             buffer.writeFloat(hunger);
             buffer.writeFloat(saturation);
         }
+
+        public static final class Builder {
+            private Set<FoodGroupKey> tags = new HashSet<>();
+            private float hunger = 1;
+            private float saturation = 1;
+
+            public Builder tag(Identifier tag) {
+                tags.add(FoodGroupKey.LOOKUP.apply(tag));
+                return this;
+            }
+
+            public Builder hunger(float hunger) {
+                this.hunger = hunger;
+                return this;
+            }
+
+            public Builder saturation(float saturation) {
+                this.saturation = saturation;
+                return this;
+            }
+
+            public Multiplier build() {
+                return new Multiplier(tags, hunger, saturation);
+            }
+        }
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/affliction/ClearLoveSicknessAffliction.java b/src/main/java/com/minelittlepony/unicopia/diet/affliction/ClearLoveSicknessAffliction.java
index 79343432..ab8786a4 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/affliction/ClearLoveSicknessAffliction.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/affliction/ClearLoveSicknessAffliction.java
@@ -7,7 +7,7 @@ import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.item.ItemStack;
 import net.minecraft.network.PacketByteBuf;
 
-final class ClearLoveSicknessAffliction implements Affliction {
+public final class ClearLoveSicknessAffliction implements Affliction {
     public static final ClearLoveSicknessAffliction INSTANCE = new ClearLoveSicknessAffliction();
     public static final Codec<ClearLoveSicknessAffliction> CODEC = Codec.unit(INSTANCE);
 
@@ -21,6 +21,7 @@ final class ClearLoveSicknessAffliction implements Affliction {
         player.heal(stack.isFood() ? stack.getItem().getFoodComponent().getHunger() : 1);
         player.removeStatusEffect(StatusEffects.NAUSEA);
         player.removeStatusEffect(UEffects.FOOD_POISONING);
+        player.removeStatusEffect(StatusEffects.WEAKNESS);
     }
 
     @Override
diff --git a/src/main/java/com/minelittlepony/unicopia/diet/affliction/CompoundAffliction.java b/src/main/java/com/minelittlepony/unicopia/diet/affliction/CompoundAffliction.java
index 0d561985..ff77dcbd 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/affliction/CompoundAffliction.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/affliction/CompoundAffliction.java
@@ -18,6 +18,10 @@ public class CompoundAffliction implements Affliction {
         });
     }
 
+    public static CompoundAffliction of(Affliction...afflictions) {
+        return new CompoundAffliction(List.of(afflictions));
+    }
+
     public CompoundAffliction(PacketByteBuf buffer) {
         this(buffer.readList(Affliction::read));
     }
diff --git a/src/main/resources/data/unicopia/diets/races/alicorn.json b/src/main/resources/data/unicopia/diets/races/alicorn.json
deleted file mode 100644
index 8801183e..00000000
--- a/src/main/resources/data/unicopia/diets/races/alicorn.json
+++ /dev/null
@@ -1,64 +0,0 @@
-{
-  "default_multiplier": 1.4,
-  "foraging_multiplier": 0.9,
-  "multipliers": [
-    {
-      "tags": [ "unicopia:fish/cooked" ],
-      "hunger": 1.5,
-      "saturation": 1.5
-    },
-    {
-      "tags": [ "unicopia:fish/raw" ],
-      "hunger": 0.5,
-      "saturation": 0.6
-    },
-    {
-      "tags": [
-        "unicopia:baked_goods"
-      ],
-      "hunger": 1,
-      "saturation": 1
-    },
-    {
-      "tags": [
-        "unicopia:insect/cooked",
-        "unicopia:meat/cooked"
-      ],
-      "hunger": 0.1,
-      "saturation": 0.1
-    },
-    {
-      "tags": [
-        "unicopia:love",
-        "unicopia:insect/raw",
-        "unicopia:meat/raw",
-        "unicopia:meat/rotten"
-      ],
-      "hunger": 0,
-      "saturation": 0
-    },
-    {
-      "tags": [ "unicopia:pinecone" ],
-      "hunger": 0.9,
-      "saturation": 0.9
-    }
-  ],
-  "effects": [
-    {
-      "tags": [ "unicopia:fish/cooked" ],
-      "food_component": {
-        "hunger": 2,
-        "saturation": 1
-      },
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [ "unicopia:fish/raw" ],
-      "ailment": {
-        "effects": [ ]
-      }
-    }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/diets/races/bat.json b/src/main/resources/data/unicopia/diets/races/bat.json
deleted file mode 100644
index b5c17d1b..00000000
--- a/src/main/resources/data/unicopia/diets/races/bat.json
+++ /dev/null
@@ -1,130 +0,0 @@
-{
-  "default_multiplier": 0.6,
-  "foraging_multiplier": 0.9,
-  "multipliers": [
-    {
-      "tags": [ "unicopia:fish/cooked" ],
-      "hunger": 0.75,
-      "saturation": 0.75
-    },
-    {
-      "tags": [ "unicopia:fish/raw" ],
-      "hunger": 0.5,
-      "saturation": 0.6
-    },
-    {
-      "tags": [ "unicopia:insect/cooked" ],
-      "hunger": 1.75,
-      "saturation": 1.75
-    },
-    {
-      "tags": [ "unicopia:meat/cooked" ],
-      "hunger": 1.15,
-      "saturation": 1.15
-    },
-    {
-      "tags": [ "unicopia:insect/raw" ],
-      "hunger": 1,
-      "saturation": 1
-    },
-    {
-      "tags": [ "unicopia:meat/raw" ],
-      "hunger": 0.25,
-      "saturation": 0.25
-    },
-    {
-      "tags": [ "unicopia:meat/rotten" ],
-      "hunger": 0.2,
-      "saturation": 0.2
-    },
-    {
-      "tags": [ "unicopia:love" ],
-      "hunger": 0,
-      "saturation": 0
-    },
-    {
-      "tags": [ "unicopia:pinecone" ],
-      "hunger": 0.9,
-      "saturation": 0.9
-    }
-  ],
-  "effects": [
-    {
-      "tags": [ "unicopia:fish/rotten" ],
-      "ailment": {
-        "effects": [
-          {
-            "effect": "unicopia:food_poisoning",
-            "seconds": 100,
-            "amplifier": 2,
-            "chance": 5
-          }
-        ]
-      }
-    },
-    {
-      "tags": [
-        "unicopia:insect/cooked",
-        "unicopia:meat/cooked"
-      ],
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [ "unicopia:insect/raw" ],
-      "ailment": {
-        "effects": [
-          {
-            "effect": "unicopia:food_poisoning",
-            "seconds": 50,
-            "amplifier": 1
-          }
-        ]
-      }
-    },
-    {
-      "tags": [
-        "unicopia:meat/raw",
-        "unicopia:meat/rotten"
-      ],
-      "ailment": {
-        "effects": [
-          {
-            "effect": "unicopia:food_poisoning",
-            "seconds": 100,
-            "amplifier": 2,
-            "chance": 5
-          }
-        ]
-      }
-    },
-    {
-      "tags": [ "unicopia:bat_ponys_delight" ],
-      "ailment": {
-        "effects": [
-          {
-            "effect": "minecraft:health_boost",
-            "seconds": { "min": 30, "max": 60 },
-            "amplifier": { "min": 2, "max": 6 }
-          },
-          {
-            "effect": "minecraft:jump_boost",
-            "seconds": { "min": 30, "max": 60 },
-            "amplifier": { "min": 1, "max": 6 }
-          },
-          {
-            "effect": "minecraft:health_boost",
-            "seconds": 30,
-            "amplifier": { "min": 1, "max": 6 }
-          },
-          {
-            "effect": "minecraft:regeneration",
-            "seconds": { "min": 3, "max": 30 },
-            "amplifier": { "min": 3, "max": 6 }
-          }
-        ]
-      }
-    }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/diets/races/changeling.json b/src/main/resources/data/unicopia/diets/races/changeling.json
deleted file mode 100644
index e0c79004..00000000
--- a/src/main/resources/data/unicopia/diets/races/changeling.json
+++ /dev/null
@@ -1,159 +0,0 @@
-{
-  "default_multiplier": 0.15,
-  "foraging_multiplier": 0.1,
-  "multipliers": [
-    {
-      "tags": [ "unicopia:insect/cooked" ],
-      "hunger": 2.0,
-      "saturation": 1.3
-    },
-    {
-      "tags": [ "unicopia:meat/cooked" ],
-      "hunger": 1.9,
-      "saturation": 1.2
-    },
-    {
-      "tags": [ "unicopia:insect/raw" ],
-      "hunger": 1,
-      "saturation": 1
-    },
-    {
-      "tags": [ "unicopia:meat/raw" ],
-      "hunger": 1.25,
-      "saturation": 1.25
-    },
-    {
-      "tags": [ "unicopia:meat/rotten" ],
-      "hunger": 0.6,
-      "saturation": 0.6
-    },
-    {
-      "tags": [
-        "unicopia:baked_goods"
-      ],
-      "hunger": 0.5,
-      "saturation": 0.9
-    },
-    {
-      "tags": [ "unicopia:love" ],
-      "hunger": 5,
-      "saturation": 3
-    }
-  ],
-  "default_effects": {
-    "effects": [
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2
-      },
-      {
-        "effect": "minecraft:weakness",
-        "seconds": 200,
-        "amplifier": 1
-      },
-      {
-        "type": "unicopia:lose_hunger",
-        "multiplier": 0.5
-      }
-    ]
-  },
-  "effects": [
-    {
-      "tags": [ "unicopia:love" ],
-      "food_component": {
-        "hunger": 2,
-        "saturation": 1
-      },
-      "ailment": {
-        "effects": [
-          {
-            "name": "Love Consumption",
-            "type": "unicopia:cure_love_sickness"
-          }
-        ]
-      }
-    },
-    {
-      "tags": [
-        "unicopia:insect/raw"
-      ],
-      "food_component": {
-        "hunger": 3,
-        "saturation": 2
-      },
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [
-        "unicopia:fish/cooked",
-        "unicopia:fish/raw"
-      ],
-      "ailment": {
-        "effects": [
-          {
-            "effect": "unicopia:food_poisoning",
-            "seconds": 50,
-            "amplifier": 2
-          },
-          {
-            "name": "unicopia.affliction.love_sickness",
-            "type": "unicopia:lose_hunger",
-            "multiplier": 0.5
-          }
-        ]
-      }
-    },
-    {
-      "tags": [
-        "unicopia:fish/rotten",
-        "unicopia:insect/cooked",
-        "unicopia:meat/cooked",
-        "unicopia:meat/raw",
-        "unicopia:meat/rotten"
-      ],
-      "food_component": {
-        "hunger": 6,
-        "saturation": 9
-      },
-      "ailment": {
-        "effects": [
-          {
-            "name": "Love Consumption",
-            "type": "unicopia:cure_love_sickness"
-          }
-        ]
-      }
-    },
-    {
-      "tags": [
-        "unicopia:foraging/edible",
-        "unicopia:foraging/edible_filling"
-      ],
-      "food_component": {
-        "hunger": 18,
-        "saturation": 9
-      },
-      "ailment": {
-        "effects": [
-          {
-            "effect": "unicopia:food_poisoning",
-            "seconds": 10,
-            "amplifier": 2
-          },
-          {
-            "effect": "minecraft:weakness",
-            "seconds": 2,
-            "amplifier": 1
-          },
-          {
-            "type": "unicopia:lose_hunger",
-            "multiplier": 0.5
-          }
-        ]
-      }
-    }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/diets/races/earth.json b/src/main/resources/data/unicopia/diets/races/earth.json
deleted file mode 100644
index 75cd8f16..00000000
--- a/src/main/resources/data/unicopia/diets/races/earth.json
+++ /dev/null
@@ -1,81 +0,0 @@
-{
-  "default_multiplier": 0.7,
-  "foraging_multiplier": 1,
-  "multipliers": [
-    {
-      "tags": [
-        "unicopia:candy",
-        "unicopia:desserts",
-        "unicopia:rocks"
-      ],
-      "hunger": 2.5,
-      "saturation": 1.7
-    },
-    {
-      "tags": [
-        "unicopia:baked_goods"
-      ],
-      "hunger": 1.2,
-      "saturation": 2
-    },
-    {
-      "tags": [ "unicopia:fish/cooked" ],
-      "hunger": 0.2,
-      "saturation": 0.2
-    },
-    {
-      "tags": [
-        "unicopia:insect/cooked",
-        "unicopia:meat/cooked"
-      ],
-      "hunger": 0.1,
-      "saturation": 0.1
-    },
-    {
-      "tags": [
-        "unicopia:love",
-        "unicopia:fish/raw",
-        "unicopia:insect/raw",
-        "unicopia:meat/raw",
-        "unicopia:meat/rotten"
-      ],
-      "hunger": 0,
-      "saturation": 0
-    },
-    {
-      "tags": [ "unicopia:pinecone" ],
-      "hunger": 1,
-      "saturation": 1
-    }
-  ],
-  "effects": [
-    {
-      "tags": [
-        "unicopia:candy",
-        "unicopia:rocks"
-      ],
-      "food_component": {
-        "hunger": 5,
-        "saturation": 12,
-        "fastFood": true
-      },
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [
-        "unicopia:desserts"
-      ],
-      "food_component": {
-        "hunger": 12,
-        "saturation": 32,
-        "eatenQuickly": true,
-        "fastFood": true
-      },
-      "ailment": {
-        "effects": [ ]
-      }
-    }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/diets/races/hippogriff.json b/src/main/resources/data/unicopia/diets/races/hippogriff.json
deleted file mode 100644
index 22c21e1f..00000000
--- a/src/main/resources/data/unicopia/diets/races/hippogriff.json
+++ /dev/null
@@ -1,97 +0,0 @@
-{
-  "default_multiplier": 0.5,
-  "foraging_multiplier": 0.8,
-  "multipliers": [
-    {
-      "tags": [
-        "unicopia:meat/cooked",
-        "unicopia:fish/cooked"
-       ],
-      "hunger": 1.6,
-      "saturation": 1.6
-    },
-    {
-      "tags": [
-        "unicopia:nuts_and_seeds"
-       ],
-      "hunger": 1.4,
-      "saturation": 1.4
-    },
-    {
-      "tags": [
-        "unicopia:baked_goods"
-      ],
-      "hunger": 1,
-      "saturation": 1
-    },
-    {
-      "tags": [
-        "unicopia:meat/raw",
-        "unicopia:fish/raw"
-      ],
-      "hunger": 0.6,
-      "saturation": 0.6
-    },
-    {
-      "tags": [ "unicopia:meat/rotten" ],
-      "hunger": 0.3,
-      "saturation": 0.3
-    },
-    {
-      "tags": [
-        "unicopia:love",
-        "unicopia:insect/raw",
-        "unicopia:insect/cooked"
-      ],
-      "hunger": 0,
-      "saturation": 0
-    },
-    {
-      "tags": [ "unicopia:pinecone" ],
-      "hunger": 1,
-      "saturation": 1
-    }
-  ],
-  "effects": [
-    {
-      "tags": [ "unicopia:fish/cooked" ],
-      "food_component": {
-        "hunger": 2,
-        "saturation": 1
-      },
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [ "unicopia:fish/raw" ],
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [
-        "unicopia:foraging/prickly",
-        "unicopia:foraging/severely_prickly"
-      ],
-      "food_component": {
-        "hunger": 2,
-        "saturation": 1
-      },
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [ "unicopia:pinecone" ],
-      "ailment": {
-        "effects": [
-          {
-            "type": "unicopia:healing",
-            "health": 3
-          }
-        ]
-      }
-    }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/diets/races/human.json b/src/main/resources/data/unicopia/diets/races/human.json
deleted file mode 100644
index 67db4d5b..00000000
--- a/src/main/resources/data/unicopia/diets/races/human.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-  "default_multiplier": 1,
-  "foraging_multiplier": 0,
-  "multipliers": [ ],
-  "effects": [
-    {
-      "tags": [
-        "unicopia:fish/cooked",
-        "unicopia:fish/raw",
-        "unicopia:fish/rotten",
-        "unicopia:meat/cooked",
-        "unicopia:meat/raw",
-        "unicopia:meat/rotten",
-        "unicopia:pinecone"
-      ],
-      "ailment": {
-        "effects": [ ]
-      }
-    }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/diets/races/kirin.json b/src/main/resources/data/unicopia/diets/races/kirin.json
deleted file mode 100644
index 5a9a5919..00000000
--- a/src/main/resources/data/unicopia/diets/races/kirin.json
+++ /dev/null
@@ -1,68 +0,0 @@
-{
-  "default_multiplier": 0.25,
-  "foraging_multiplier": 0.9,
-  "multipliers": [
-    {
-      "tags": [ "unicopia:meat/cooked" ],
-      "hunger": 1.5,
-      "saturation": 1.5
-    },
-    {
-      "tags": [ "unicopia:meat/raw" ],
-      "hunger": 0.5,
-      "saturation": 0.6
-    },
-    {
-      "tags": [
-        "unicopia:baked_goods"
-      ],
-      "hunger": 1,
-      "saturation": 1
-    },
-    {
-      "tags": [
-        "unicopia:insect/cooked",
-        "unicopia:fish/cooked"
-      ],
-      "hunger": 0.1,
-      "saturation": 0.1
-    },
-    {
-      "tags": [
-        "unicopia:love",
-        "unicopia:insect/raw",
-        "unicopia:fish/raw",
-        "unicopia:meat/rotten"
-      ],
-      "hunger": 0,
-      "saturation": 0
-    },
-    {
-      "tags": [ "unicopia:pinecone" ],
-      "hunger": 0.9,
-      "saturation": 0.9
-    }
-  ],
-  "effects": [
-    {
-      "tags": [
-        "unicopia:fish/rotten",
-        "unicopia:insect/cooked",
-        "unicopia:meat/cooked",
-        "unicopia:meat/raw",
-        "unicopia:meat/rotten",
-        "unicopia:foraging/blinding",
-        "unicopia:foraging/prickly",
-        "unicopia:foraging/severely_prickly",
-        "unicopia:foraging/strengthening"
-      ],
-      "food_component": {
-        "hunger": 2,
-        "saturation": 1
-      },
-      "ailment": {
-        "effects": [ ]
-      }
-    }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/diets/races/pegasus.json b/src/main/resources/data/unicopia/diets/races/pegasus.json
deleted file mode 100644
index 7cbd683a..00000000
--- a/src/main/resources/data/unicopia/diets/races/pegasus.json
+++ /dev/null
@@ -1,66 +0,0 @@
-{
-  "default_multiplier": 0.9,
-  "foraging_multiplier": 1,
-  "multipliers": [
-    {
-      "tags": [ "unicopia:fish/cooked" ],
-      "hunger": 1.5,
-      "saturation": 1.5
-    },
-    {
-      "tags": [ "unicopia:fish/raw" ],
-      "hunger": 0.5,
-      "saturation": 0.6
-    },
-    {
-      "tags": [
-        "unicopia:baked_goods"
-      ],
-      "hunger": 1,
-      "saturation": 1
-    },
-    {
-      "tags": [
-        "unicopia:insect/cooked",
-        "unicopia:meat/cooked"
-      ],
-      "hunger": 0.1,
-      "saturation": 0.1
-    },
-    {
-      "tags": [
-        "unicopia:love",
-        "unicopia:insect/raw",
-        "unicopia:meat/raw",
-        "unicopia:meat/rotten"
-      ],
-      "hunger": 0,
-      "saturation": 0
-    },
-    {
-      "tags": [ "unicopia:pinecone" ],
-      "hunger": 0.9,
-      "saturation": 0.9
-    }
-  ],
-  "effects": [
-    {
-      "tags": [ "unicopia:fish/cooked" ],
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [ "unicopia:fish/raw" ],
-      "ailment": {
-        "effects": [
-          {
-            "effect": "unicopia:food_poisoning",
-            "seconds": 50,
-            "amplifier": 2
-          }
-        ]
-      }
-    }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/diets/races/seapony.json b/src/main/resources/data/unicopia/diets/races/seapony.json
deleted file mode 100644
index c67aa9f7..00000000
--- a/src/main/resources/data/unicopia/diets/races/seapony.json
+++ /dev/null
@@ -1,100 +0,0 @@
-{
-  "default_multiplier": 0.4,
-  "foraging_multiplier": 0.7,
-  "multipliers": [
-    {
-      "tags": [ "unicopia:sea_vegetable/raw" ],
-      "hunger": 1,
-      "saturation": 1
-    },
-    {
-      "tags": [
-        "unicopia:shells", "unicopia:shelly"
-      ],
-      "hunger": 1,
-      "saturation": 1
-    }
-  ],
-  "default_effects": {
-    "effects": [
-      {
-        "effect": "unicopia:food_poisoning",
-        "seconds": 100,
-        "amplifier": 2
-      }
-    ]
-  },
-  "effects": [
-    {
-      "tags": [ "unicopia:fish/cooked" ],
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [ "unicopia:fish/raw" ],
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [
-        "unicopia:foraging/edible",
-        "unicopia:foraging/edible_filling"
-      ],
-      "food_component": {
-        "hunger": 18,
-        "saturation": 9
-      },
-      "ailment": {
-        "effects": [
-          {
-            "effect": "unicopia:food_poisoning",
-            "seconds": 100,
-            "amplifier": 2
-          }
-        ]
-      }
-    },
-    {
-      "tags": [ "unicopia:sea_vegetable/raw" ],
-      "food_component": {
-        "hunger": 2,
-        "saturation": 1
-      },
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [ "unicopia:sea_vegetable/cooked" ],
-      "food_component": {
-        "hunger": 6,
-        "saturation": 2
-      },
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [ "unicopia:shells" ],
-      "food_component": {
-        "hunger": 3,
-        "saturation": 5
-      },
-      "ailment": {
-        "effects": [ ]
-      }
-    },
-    {
-      "tags": [ "unicopia:shelly" ],
-      "food_component": {
-        "hunger": 6,
-        "saturation": 7
-      },
-      "ailment": {
-        "effects": [ ]
-      }
-    }
-  ]
-}
diff --git a/src/main/resources/data/unicopia/diets/races/unicorn.json b/src/main/resources/data/unicopia/diets/races/unicorn.json
deleted file mode 100644
index 3213c82b..00000000
--- a/src/main/resources/data/unicopia/diets/races/unicorn.json
+++ /dev/null
@@ -1,39 +0,0 @@
-{
-  "default_multiplier": 1.2,
-  "foraging_multiplier": 1,
-  "multipliers": [
-    {
-      "tags": [
-        "unicopia:insect/cooked",
-        "unicopia:meat/cooked",
-        "unicopia:fish/cooked"
-      ],
-      "hunger": 0.1,
-      "saturation": 0.1
-    },
-    {
-      "tags": [
-        "unicopia:baked_goods"
-      ],
-      "hunger": 1,
-      "saturation": 1
-    },
-    {
-      "tags": [
-        "unicopia:love",
-        "unicopia:insect/raw",
-        "unicopia:meat/raw",
-        "unicopia:fish/raw",
-        "unicopia:meat/rotten"
-      ],
-      "hunger": 0,
-      "saturation": 0
-    },
-    {
-      "tags": [ "unicopia:pinecone" ],
-      "hunger": 0.9,
-      "saturation": 0.9
-    }
-  ],
-  "effects": []
-}

From 56e91629b1114255c39de9752787c19598b04c39 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Fri, 5 Apr 2024 15:07:07 +0100
Subject: [PATCH 14/21] Fix some tag references and fix diets not loading

---
 .../unicopia/diet/DietsLoader.java            |  4 ++--
 .../unicopia/tags/items/groups/foraging.json  | 22 +++++++++----------
 .../unicopia/tags/items/groups/sea_pony.json  |  4 ++--
 3 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/src/main/java/com/minelittlepony/unicopia/diet/DietsLoader.java b/src/main/java/com/minelittlepony/unicopia/diet/DietsLoader.java
index 7da493ea..0c4eb7f5 100644
--- a/src/main/java/com/minelittlepony/unicopia/diet/DietsLoader.java
+++ b/src/main/java/com/minelittlepony/unicopia/diet/DietsLoader.java
@@ -35,7 +35,7 @@ public class DietsLoader implements IdentifiableResourceReloadListener {
 
         CompletableFuture<Map<Identifier, Effect>> foodGroupsFuture = CompletableFuture.supplyAsync(() -> {
             Map<Identifier, Effect> foodGroups = new HashMap<>();
-            for (var group : loadData(manager, prepareExecutor, "diets/food_groups").entrySet()) {
+            for (var group : loadData(manager, prepareExecutor, "diet/food_groups").entrySet()) {
                 try {
                     FoodGroup.CODEC.parse(JsonOps.INSTANCE, group.getValue())
                         .resultOrPartial(error -> LOGGER.error("Could not load food group {}: {}", group.getKey(), error))
@@ -50,7 +50,7 @@ public class DietsLoader implements IdentifiableResourceReloadListener {
         }, prepareExecutor);
         CompletableFuture<Map<Race, DietProfile>> profilesFuture = CompletableFuture.supplyAsync(() -> {
             Map<Race, DietProfile> profiles = new HashMap<>();
-            for (var entry : loadData(manager, prepareExecutor, "diets/races").entrySet()) {
+            for (var entry : loadData(manager, prepareExecutor, "diet/races").entrySet()) {
                 Identifier id = entry.getKey();
                 try {
                     Race.REGISTRY.getOrEmpty(id).ifPresentOrElse(race -> {
diff --git a/src/main/resources/data/unicopia/tags/items/groups/foraging.json b/src/main/resources/data/unicopia/tags/items/groups/foraging.json
index f50de618..92eb5eee 100644
--- a/src/main/resources/data/unicopia/tags/items/groups/foraging.json
+++ b/src/main/resources/data/unicopia/tags/items/groups/foraging.json
@@ -1,16 +1,16 @@
 {
   "replace": false,
   "values": [
-    "#unicopia:food_types/forage_dangerous",
-    "#unicopia:food_types/forage_edible_filling",
-    "#unicopia:food_types/forage_edible",
-    "#unicopia:food_types/forage_nauseating",
-    "#unicopia:food_types/forage_blinding",
-    "#unicopia:food_types/forage_prickly",
-    "#unicopia:food_types/forage_radioactive",
-    "#unicopia:food_types/forage_risky",
-    "#unicopia:food_types/forage_severely_nauseating",
-    "#unicopia:food_types/forage_severely_prickly",
-    "#unicopia:food_types/forage_strengthening"
+    "#unicopia:forage/dangerous",
+    "#unicopia:forage/filling",
+    "#unicopia:forage/safe",
+    "#unicopia:forage/nauseating",
+    "#unicopia:forage/blinding",
+    "#unicopia:forage/prickly",
+    "#unicopia:forage/glowing",
+    "#unicopia:forage/risky",
+    "#unicopia:forage/severe/nauseating",
+    "#unicopia:forage/severe/prickly",
+    "#unicopia:forage/strenghtening"
   ]
 }
diff --git a/src/main/resources/data/unicopia/tags/items/groups/sea_pony.json b/src/main/resources/data/unicopia/tags/items/groups/sea_pony.json
index 5d2c981b..9aeefc68 100644
--- a/src/main/resources/data/unicopia/tags/items/groups/sea_pony.json
+++ b/src/main/resources/data/unicopia/tags/items/groups/sea_pony.json
@@ -4,7 +4,7 @@
     "unicopia:pearl_necklace",
     "#unicopia:shells",
     "#unicopia:special_shells",
-    "#unicopia:food_types/raw_sea_vegitable",
-    "#unicopia:food_types/cooked_sea_vegitable"
+    "#unicopia:food_types/low_quality_sea_vegetables",
+    "#unicopia:food_types/high_quality_sea_vegetables"
   ]
 }

From d6e1a6c3b896ddf11a25e08ae912231947c4fa4b Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Fri, 5 Apr 2024 15:07:21 +0100
Subject: [PATCH 15/21] Fixed slime pustule not triggering a redstone update
 above it

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

diff --git a/src/main/java/com/minelittlepony/unicopia/block/SlimePustuleBlock.java b/src/main/java/com/minelittlepony/unicopia/block/SlimePustuleBlock.java
index 4d7d58e8..72b1a814 100644
--- a/src/main/java/com/minelittlepony/unicopia/block/SlimePustuleBlock.java
+++ b/src/main/java/com/minelittlepony/unicopia/block/SlimePustuleBlock.java
@@ -199,7 +199,7 @@ public class SlimePustuleBlock extends Block {
     public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
         super.onStateReplaced(state, world, pos, newState, moved);
         if (state.isOf(this) && newState.isOf(this) && state.get(POWERED) != newState.get(POWERED)) {
-            world.updateNeighbor(pos.up(), this, pos);
+            world.updateNeighborsAlways(pos.up(), this);
         }
     }
 

From 926b8088cf23190d011c5bdd8cbb2d753c06fa19 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Fri, 5 Apr 2024 15:15:14 +0100
Subject: [PATCH 16/21] Fix some food group names

---
 src/main/resources/assets/unicopia/lang/en_us.json | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json
index 8552f3f2..5033eda6 100644
--- a/src/main/resources/assets/unicopia/lang/en_us.json
+++ b/src/main/resources/assets/unicopia/lang/en_us.json
@@ -622,19 +622,21 @@
   "food_group.unicopia.meat.rotten": "Rotting Meat",
   "food_group.unicopia.meat.raw": "Fresh Meat",
   "food_group.unicopia.meat.cooked": "Prepared Meat",
+  "food_group.unicopia.fish.rotten": "Rotten Fish",
   "food_group.unicopia.fish.raw": "Fresh Fish",
   "food_group.unicopia.fish.cooked": "Prepared Fish",
+  "food_group.unicopia.insect.rotten": "Rotted Bugs & Insects",
   "food_group.unicopia.insect.raw": "Bugs & Insects",
-  "food_group.unicopia.fish.cooked": "Cooked Bugs & Insects",
+  "food_group.unicopia.insect.cooked": "Cooked Bugs & Insects",
   "food_group.unicopia.nuts_and_seeds": "Nuts & Seeds",
   "food_group.unicopia.love": "Love",
   "food_group.unicopia.rocks": "Rocks",
   "food_group.unicopia.pinecone": "Nuts & Seeds",
   "food_group.unicopia.bat_ponys_delight": "Bat Pony Treats",
-  "food_group.unicopia.sea_vegitable.cooked": "Prepared Fish Food",
-  "food_group.unicopia.sea_vegitable.raw": "Fresh Fish Food",
+  "food_group.unicopia.sea_vegetable.cooked": "Prepared Shells & Coral",
+  "food_group.unicopia.sea_vegetable.raw": "Shells & Coral",
   "food_group.unicopia.shells": "Sea Shells",
-  "food_group.unicopia.shelly": "Sea Shells",
+  "food_group.unicopia.special_shells": "Companions",
   "food_group.unicopia.candy": "Candy",
   "food_group.unicopia.desserts": "Desserts",
   "food_group.unicopia.fruit": "Fruit",

From 8c8364700478947f53670e9fafb57273892334e3 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Fri, 5 Apr 2024 17:10:39 +0100
Subject: [PATCH 17/21] Add cooked and rotten variants of all the fish

---
 .../providers/UAdvancementsProvider.java      |   1 +
 .../datagen/providers/UModelProvider.java     |  14 ++++++++---
 .../UBlockAdditionsLootTableProvider.java     |  22 +++++++++++++++++
 .../UChestAdditionsLootTableProvider.java     |   9 +++++++
 .../providers/recipe/URecipeProvider.java     |   4 +++
 .../providers/tag/UItemTagProvider.java       |   7 +++++-
 ...DrinkableItem.java => ConsumableItem.java} |  10 +++++---
 .../unicopia/item/UFoodComponents.java        |   1 +
 .../minelittlepony/unicopia/item/UItems.java  |  23 +++++++++++++-----
 .../resources/assets/unicopia/lang/en_us.json |  10 ++++++++
 .../textures/item/cooked_pufferfish.png       | Bin 0 -> 6980 bytes
 .../textures/item/cooked_tropical_fish.png    | Bin 0 -> 6795 bytes
 .../unicopia/textures/item/fried_axolotl.png  | Bin 0 -> 6561 bytes
 .../unicopia/textures/item/fried_egg.png      | Bin 0 -> 6662 bytes
 .../textures/item/green_fried_egg.png         | Bin 0 -> 6709 bytes
 .../unicopia/textures/item/rotten_cod.png     | Bin 0 -> 7008 bytes
 .../textures/item/rotten_pufferfish.png       | Bin 0 -> 6927 bytes
 .../unicopia/textures/item/rotten_salmon.png  | Bin 0 -> 6986 bytes
 .../textures/item/rotten_tropical_fish.png    | Bin 0 -> 6649 bytes
 .../tags/items/groups/changeling.json         |   1 +
 .../unicopia/tags/items/groups/pegasus.json   |   1 +
 21 files changed, 89 insertions(+), 14 deletions(-)
 rename src/main/java/com/minelittlepony/unicopia/item/{DrinkableItem.java => ConsumableItem.java} (84%)
 create mode 100644 src/main/resources/assets/unicopia/textures/item/cooked_pufferfish.png
 create mode 100644 src/main/resources/assets/unicopia/textures/item/cooked_tropical_fish.png
 create mode 100644 src/main/resources/assets/unicopia/textures/item/fried_axolotl.png
 create mode 100644 src/main/resources/assets/unicopia/textures/item/fried_egg.png
 create mode 100644 src/main/resources/assets/unicopia/textures/item/green_fried_egg.png
 create mode 100644 src/main/resources/assets/unicopia/textures/item/rotten_cod.png
 create mode 100644 src/main/resources/assets/unicopia/textures/item/rotten_pufferfish.png
 create mode 100644 src/main/resources/assets/unicopia/textures/item/rotten_salmon.png
 create mode 100644 src/main/resources/assets/unicopia/textures/item/rotten_tropical_fish.png

diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UAdvancementsProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UAdvancementsProvider.java
index a46d1306..a540572c 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UAdvancementsProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UAdvancementsProvider.java
@@ -77,6 +77,7 @@ public class UAdvancementsProvider extends FabricAdvancementProvider {
             p.child(UItems.WEIRD_ROCK).hidden().criterion("has_rock", hasItems(UItems.WEIRD_ROCK)).build(consumer, "thats_unusual");
         });
 
+        parent.child(UItems.FRIED_AXOLOTL).criterion("eaten_axolotl", ConsumeItemCriterion.Conditions.item(UItems.FRIED_AXOLOTL)).build(consumer, "tastes_like_chicken");
         parent.child(UItems.OATS).criterion("has_oats", hasItems(UItems.OATS)).build(consumer, "oats_so_easy");
         parent.child(Items.HAY_BLOCK).criterion("eat_hay", ConsumeItemCriterion.Conditions.item(Items.HAY_BLOCK)).build(consumer, "what_the_hay");
         parent.child(UItems.COPPER_HORSE_SHOE).criterion("has_horseshoe", hasItems(UTags.Items.HORSE_SHOES)).build(consumer, "blacksmith").children(p -> {
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 8dd5d9c5..cc317eb5 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UModelProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UModelProvider.java
@@ -59,12 +59,14 @@ public class UModelProvider extends FabricModelProvider {
         ItemModels.register(itemModelGenerator,
                 UItems.ACORN, UItems.APPLE_PIE_HOOF, UItems.APPLE_PIE_SLICE, UItems.APPLE_PIE,
                 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.CHOCOLATE_OATMEAL_COOKIE, UItems.CLOUD_LUMP, UItems.CRISPY_HAY_FRIES, UItems.CRYSTAL_HEART, UItems.CRYSTAL_SHARD,
+                UItems.CARAPACE, UItems.CLAM_SHELL, UItems.COOKED_ZAP_APPLE, UItems.CHOCOLATE_OATMEAL_COOKIE,
+                    UItems.CLOUD_LUMP, UItems.CRISPY_HAY_FRIES, UItems.CRYSTAL_HEART, UItems.CRYSTAL_SHARD,
+                    UItems.COOKED_TROPICAL_FISH, UItems.COOKED_PUFFERFISH, UItems.FRIED_AXOLOTL,
                 UItems.DAFFODIL_DAISY_SANDWICH, UItems.DRAGON_BREATH_SCROLL,
                 UItems.EMPTY_JAR,
                 UItems.FRIENDSHIP_BRACELET,
                 UItems.GIANT_BALLOON, UItems.GOLDEN_FEATHER, UItems.GOLDEN_OAK_SEEDS, UItems.GOLDEN_WING, UItems.GREEN_APPLE_SEEDS, UItems.GREEN_APPLE, UItems.GROGARS_BELL,
-                    UItems.GRYPHON_FEATHER,
+                    UItems.GRYPHON_FEATHER, UItems.GREEN_FRIED_EGG,
                 UItems.HAY_BURGER, UItems.HAY_FRIES, UItems.HORSE_SHOE_FRIES,
                 UItems.IMPORTED_OATS,
                 UItems.JAM_TOAST, UItems.JUICE,
@@ -72,7 +74,8 @@ public class UModelProvider extends FabricModelProvider {
                 UItems.MANGO, UItems.MUFFIN,
                 UItems.OATMEAL, UItems.OATMEAL_COOKIE, UItems.SCONE,
                 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.RAIN_CLOUD_JAR, UItems.ROCK_STEW, UItems.ROCK,
+                    UItems.ROTTEN_APPLE, UItems.ROTTEN_COD, UItems.ROTTEN_TROPICAL_FISH, UItems.ROTTEN_SALMON, UItems.ROTTEN_PUFFERFISH,
                 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,
                 UItems.TOAST, UItems.TOM, UItems.TURRET_SHELL,
@@ -106,7 +109,10 @@ public class UModelProvider extends FabricModelProvider {
                 .put(TextureKey.LAYER1, ModelIds.getItemSubModelId(UItems.MAGIC_STAFF, "_magic")), itemModelGenerator.writer);
 
         // polearms
-        List.of(UItems.DIAMOND_POLEARM, UItems.GOLDEN_POLEARM, UItems.NETHERITE_POLEARM, UItems.STONE_POLEARM, UItems.WOODEN_POLEARM, UItems.IRON_POLEARM).forEach(item -> ItemModels.registerPolearm(itemModelGenerator, item));
+        List.of(
+                UItems.DIAMOND_POLEARM, UItems.GOLDEN_POLEARM, UItems.NETHERITE_POLEARM,
+                UItems.STONE_POLEARM, UItems.WOODEN_POLEARM, UItems.IRON_POLEARM
+        ).forEach(item -> ItemModels.registerPolearm(itemModelGenerator, item));
         // sheets
         ItemModels.register(itemModelGenerator, BedsheetsItem.ITEMS.values().stream().toArray(Item[]::new));
         // badges
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 4663bd66..cf6ed6d5 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
@@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.datagen.providers.loot;
 
 import java.util.function.Function;
 
+import com.minelittlepony.unicopia.UTags;
 import com.minelittlepony.unicopia.item.UItems;
 import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
 
@@ -14,20 +15,24 @@ 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.LocationCheckLootCondition;
 import net.minecraft.loot.condition.LootCondition;
 import net.minecraft.loot.condition.MatchToolLootCondition;
 import net.minecraft.loot.condition.RandomChanceLootCondition;
 import net.minecraft.loot.condition.TableBonusLootCondition;
 import net.minecraft.loot.entry.ItemEntry;
 import net.minecraft.loot.entry.LootPoolEntry;
+import net.minecraft.loot.entry.TagEntry;
 import net.minecraft.loot.function.ApplyBonusLootFunction;
 import net.minecraft.loot.function.SetCountLootFunction;
 import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
 import net.minecraft.loot.provider.number.UniformLootNumberProvider;
 import net.minecraft.predicate.NumberRange;
+import net.minecraft.predicate.entity.LocationPredicate;
 import net.minecraft.predicate.item.EnchantmentPredicate;
 import net.minecraft.predicate.item.ItemPredicate;
 import net.minecraft.util.Identifier;
+import net.minecraft.world.biome.BiomeKeys;
 
 public class UBlockAdditionsLootTableProvider extends FabricBlockLootTableProvider {
     public static final LootCondition.Builder WITH_GEM_FINDER = MatchToolLootCondition.builder(ItemPredicate.Builder.create().enchantment(new EnchantmentPredicate(UEnchantments.GEM_FINDER, NumberRange.IntRange.atLeast(1))));
@@ -35,6 +40,10 @@ public class UBlockAdditionsLootTableProvider extends FabricBlockLootTableProvid
     public static final LootCondition.Builder WITHOUT_SILK_TOUCH_AND_GEM_FINDER = WITHOUT_SILK_TOUCH.and(WITH_GEM_FINDER);
     public static final float[] GEMSTONES_FORTUNE_CHANCE = { 0.1F, 0.14285715F, 0.25F, 1F };
 
+    public static final LootCondition.Builder NEEDS_OCEAN_OR_BEACH_BIOME =
+            LocationCheckLootCondition.builder(LocationPredicate.Builder.create().biome(BiomeKeys.OCEAN))
+        .or(LocationCheckLootCondition.builder(LocationPredicate.Builder.create().biome(BiomeKeys.BEACH)));
+
     public UBlockAdditionsLootTableProvider(FabricDataOutput dataOutput) {
         super(dataOutput);
     }
@@ -57,12 +66,25 @@ public class UBlockAdditionsLootTableProvider extends FabricBlockLootTableProvid
         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));
+        addVanillaDrop(Blocks.GRAVEL, this::shellDrops);
+        addVanillaDrop(Blocks.SUSPICIOUS_GRAVEL, this::shellDrops);
     }
 
     private void addVanillaDrop(Block block, Function<Block, LootTable.Builder> lootTableFunction) {
         lootTables.put(new Identifier("unicopiamc", block.getLootTableId().getPath()), lootTableFunction.apply(block));
     }
 
+    public LootTable.Builder shellDrops(Block block) {
+        return LootTable.builder().pool(LootPool.builder()
+                .rolls(ConstantLootNumberProvider.create(1))
+                .conditionally(WITHOUT_SILK_TOUCH.and(NEEDS_OCEAN_OR_BEACH_BIOME))
+                .with(applyExplosionDecay(block, TagEntry.builder(UTags.Items.SHELLS)
+                        .apply(SetCountLootFunction.builder(ConstantLootNumberProvider.create(1)))
+                    )
+                    .conditionally(TableBonusLootCondition.builder(Enchantments.FORTUNE, GEMSTONES_FORTUNE_CHANCE)))
+                );
+    }
+
     public LootTable.Builder chanceDropWithShears(Block block, ItemConvertible drop, float...chance) {
         return LootTable.builder()
                 .pool(LootPool.builder()
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestAdditionsLootTableProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestAdditionsLootTableProvider.java
index 051875e7..0d6beac9 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestAdditionsLootTableProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/loot/UChestAdditionsLootTableProvider.java
@@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.datagen.providers.loot;
 
 import java.util.function.BiConsumer;
 
+import com.minelittlepony.unicopia.UConventionalTags;
 import com.minelittlepony.unicopia.UTags;
 import com.minelittlepony.unicopia.item.UItems;
 
@@ -60,6 +61,7 @@ public class UChestAdditionsLootTableProvider extends SimpleFabricLootTableProvi
                 .rolls(UniformLootNumberProvider.create(0, 1))
                 .with(ItemEntry.builder(UItems.GROGARS_BELL).weight(1))
         ));
+
         exporter.accept(LootTables.BURIED_TREASURE_CHEST, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 4))
                 .with(ItemEntry.builder(UItems.PEARL_NECKLACE).weight(1))
@@ -68,21 +70,25 @@ public class UChestAdditionsLootTableProvider extends SimpleFabricLootTableProvi
         exporter.accept(LootTables.SHIPWRECK_SUPPLY_CHEST, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 6))
                 .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(3))
+                .with(TagEntry.expandBuilder(UConventionalTags.Items.ROTTEN_FISH).weight(1))
         ));
         exporter.accept(LootTables.SHIPWRECK_TREASURE_CHEST, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 4))
                 .with(ItemEntry.builder(UItems.PEARL_NECKLACE).weight(1))
                 .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(3))
+                .with(TagEntry.expandBuilder(UConventionalTags.Items.ROTTEN_FISH).weight(1))
         ));
         exporter.accept(LootTables.UNDERWATER_RUIN_BIG_CHEST, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 2))
                 .with(ItemEntry.builder(UItems.PEARL_NECKLACE).weight(1))
                 .with(ItemEntry.builder(UItems.SHELLY).weight(4))
                 .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(8))
+                .with(TagEntry.expandBuilder(UConventionalTags.Items.ROTTEN_FISH).weight(1))
         ));
         exporter.accept(LootTables.UNDERWATER_RUIN_SMALL_CHEST, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 4))
                 .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(1))
+                .with(TagEntry.expandBuilder(UConventionalTags.Items.ROTTEN_FISH).weight(1))
         ));
 
         exporter.accept(LootTables.DESERT_WELL_ARCHAEOLOGY, LootTable.builder().pool(LootPool.builder()
@@ -112,17 +118,20 @@ public class UChestAdditionsLootTableProvider extends SimpleFabricLootTableProvi
                 .rolls(UniformLootNumberProvider.create(1, 4))
                 .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(1))
                 .with(ItemEntry.builder(UItems.PEARL_NECKLACE).weight(1))
+                .with(TagEntry.expandBuilder(UConventionalTags.Items.ROTTEN_FISH).weight(1))
         ));
 
         exporter.accept(LootTables.FISHING_GAMEPLAY, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 4))
                 .with(TagEntry.expandBuilder(UTags.Items.SHELLS).weight(2))
+                .with(TagEntry.expandBuilder(UConventionalTags.Items.ROTTEN_FISH).weight(1))
         ));
 
         exporter.accept(LootTables.FISHING_JUNK_GAMEPLAY, LootTable.builder().pool(LootPool.builder()
                 .rolls(UniformLootNumberProvider.create(1, 4))
                 .with(ItemEntry.builder(UItems.BROKEN_SUNGLASSES).weight(2))
                 .with(ItemEntry.builder(UItems.WHEAT_WORMS).weight(2))
+                .with(TagEntry.expandBuilder(UConventionalTags.Items.ROTTEN_FISH).weight(1))
                 .with(ItemEntry.builder(UItems.BOTCHED_GEM).weight(4))
         ));
 
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 3e27692b..aad4a126 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
@@ -411,6 +411,10 @@ public class URecipeProvider extends FabricRecipeProvider {
         offerSmelting(exporter, List.of(UItems.BURNED_JUICE, UItems.BURNED_TOAST), RecipeCategory.FOOD, Items.CHARCOAL, 1, 20, "coal");
         offerSmelting(exporter, List.of(UItems.HAY_FRIES), RecipeCategory.FOOD, UItems.CRISPY_HAY_FRIES, 1F, 25, "hay_fries");
         offerSmelting(exporter, List.of(UItems.ZAP_APPLE), RecipeCategory.FOOD, UItems.COOKED_ZAP_APPLE, 1.2F, 430, "zap_apple");
+        offerSmelting(exporter, List.of(Items.TROPICAL_FISH), RecipeCategory.FOOD, UItems.COOKED_TROPICAL_FISH, 1.2F, 230, "fish");
+        offerSmelting(exporter, List.of(Items.PUFFERFISH), RecipeCategory.FOOD, UItems.COOKED_PUFFERFISH, 1.2F, 230, "fish");
+        offerSmelting(exporter, List.of(Items.AXOLOTL_BUCKET), RecipeCategory.FOOD, UItems.FRIED_AXOLOTL, 2.2F, 230, "fried_axolotl");
+        offerSmelting(exporter, List.of(UBlocks.MYSTERIOUS_EGG.asItem()), RecipeCategory.FOOD, UItems.GREEN_FRIED_EGG, 3.8F, 630, "fried_egg");
 
         ShapelessRecipeJsonBuilder.create(RecipeCategory.FOOD, UItems.ZAP_APPLE_JAM_JAR)
             .input(UItems.COOKED_ZAP_APPLE, 6).criterion(hasItem(UItems.COOKED_ZAP_APPLE), conditionsFromItem(UItems.COOKED_ZAP_APPLE))
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
index bc248a61..922e19fa 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
@@ -201,7 +201,12 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
         ;
         getOrCreateTagBuilder(UConventionalTags.Items.BANANAS).add(UItems.BANANA);
         getOrCreateTagBuilder(UConventionalTags.Items.RAW_FISH).add(Items.COD, Items.SALMON, Items.PUFFERFISH, Items.TROPICAL_FISH).addOptionalTag(new Identifier("c", "mollusks"));
-        getOrCreateTagBuilder(UConventionalTags.Items.COOKED_FISH).add(Items.COOKED_COD, Items.COOKED_SALMON);
+        getOrCreateTagBuilder(UConventionalTags.Items.COOKED_FISH).add(Items.COOKED_COD, Items.COOKED_SALMON, UItems.COOKED_TROPICAL_FISH, UItems.COOKED_PUFFERFISH, UItems.FRIED_AXOLOTL);
+        getOrCreateTagBuilder(UConventionalTags.Items.ROTTEN_FISH).add(UItems.ROTTEN_COD, UItems.ROTTEN_TROPICAL_FISH, UItems.ROTTEN_SALMON, UItems.ROTTEN_PUFFERFISH);
+        getOrCreateTagBuilder(ItemTags.FISHES).add(
+                UItems.COOKED_TROPICAL_FISH, UItems.COOKED_PUFFERFISH, UItems.FRIED_AXOLOTL,
+                UItems.ROTTEN_COD, UItems.ROTTEN_TROPICAL_FISH, UItems.ROTTEN_SALMON, UItems.ROTTEN_PUFFERFISH
+        );
         getOrCreateTagBuilder(UConventionalTags.Items.COOKED_MEAT)
             .add(Items.COOKED_PORKCHOP, Items.COOKED_BEEF, Items.COOKED_MUTTON, Items.COOKED_RABBIT, Items.COOKED_CHICKEN, Items.RABBIT_STEW)
             .addOptionalTag(new Identifier("c", "cooked_bacon"))
diff --git a/src/main/java/com/minelittlepony/unicopia/item/DrinkableItem.java b/src/main/java/com/minelittlepony/unicopia/item/ConsumableItem.java
similarity index 84%
rename from src/main/java/com/minelittlepony/unicopia/item/DrinkableItem.java
rename to src/main/java/com/minelittlepony/unicopia/item/ConsumableItem.java
index 23fa0dd1..dba7218d 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/DrinkableItem.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/ConsumableItem.java
@@ -11,9 +11,13 @@ import net.minecraft.stat.Stats;
 import net.minecraft.util.UseAction;
 import net.minecraft.world.World;
 
-public class DrinkableItem extends Item {
-    public DrinkableItem(Item.Settings settings) {
+public class ConsumableItem extends Item {
+
+    private final UseAction action;
+
+    public ConsumableItem(Item.Settings settings, UseAction action) {
         super(settings);
+        this.action = action;
     }
 
     @Override
@@ -30,6 +34,6 @@ public class DrinkableItem extends Item {
 
     @Override
     public UseAction getUseAction(ItemStack stack) {
-        return UseAction.DRINK;
+        return action;
     }
 }
diff --git a/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java b/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java
index 8217a4c1..80a2e9a5 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/UFoodComponents.java
@@ -29,6 +29,7 @@ public interface UFoodComponents {
     FoodComponent OATMEAL_COOKIE = FoodComponents.COOKIE; //builder(2, 0.1F).build();
     FoodComponent CHOCOLATE_OATMEAL_COOKIE = builder(3, 0.4F).build();
     FoodComponent SCONE = builder(2, 0.2F).build();
+    FoodComponent FRIED_EGG = builder(4, 0.4F).build();
 
     FoodComponent WORMS = builder(1, 1.5F).alwaysEdible().meat().build();
     FoodComponent INSECTS = builder(1, 0).alwaysEdible().build();
diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
index 30ff595f..894b6d2d 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
@@ -27,6 +27,7 @@ import net.fabricmc.fabric.api.registry.CompostingChanceRegistry;
 import net.fabricmc.fabric.api.registry.FuelRegistry;
 import net.minecraft.sound.SoundEvent;
 import net.minecraft.util.Rarity;
+import net.minecraft.util.UseAction;
 import net.minecraft.registry.Registry;
 import net.minecraft.registry.RegistryKey;
 import net.minecraft.registry.Registries;
@@ -119,17 +120,17 @@ public interface UItems {
     Item GOLDEN_OAK_SEEDS = register("golden_oak_seeds", new AliasedBlockItem(UBlocks.GOLDEN_OAK_SPROUT, new Item.Settings()), ItemGroups.NATURAL);
 
     Item MUG = register("mug", new Item(new Settings()), ItemGroups.TOOLS);
-    Item CIDER = register("cider", new DrinkableItem(new Item.Settings().food(UFoodComponents.CIDER).maxCount(16).recipeRemainder(MUG)), ItemGroups.FOOD_AND_DRINK);
-    Item JUICE = register("juice", new DrinkableItem(new Item.Settings().recipeRemainder(Items.GLASS_BOTTLE).maxCount(16).food(UFoodComponents.JUICE)), ItemGroups.FOOD_AND_DRINK);
-    Item BURNED_JUICE = register("burned_juice", new DrinkableItem(new Item.Settings().recipeRemainder(Items.GLASS_BOTTLE).maxCount(16).food(UFoodComponents.BURNED_JUICE)), ItemGroups.FOOD_AND_DRINK);
+    Item CIDER = register("cider", new ConsumableItem(new Item.Settings().food(UFoodComponents.CIDER).maxCount(16).recipeRemainder(MUG), UseAction.DRINK), ItemGroups.FOOD_AND_DRINK);
+    Item JUICE = register("juice", new ConsumableItem(new Item.Settings().recipeRemainder(Items.GLASS_BOTTLE).maxCount(16).food(UFoodComponents.JUICE), UseAction.DRINK), ItemGroups.FOOD_AND_DRINK);
+    Item BURNED_JUICE = register("burned_juice", new ConsumableItem(new Item.Settings().recipeRemainder(Items.GLASS_BOTTLE).maxCount(16).food(UFoodComponents.BURNED_JUICE), UseAction.DRINK), ItemGroups.FOOD_AND_DRINK);
     Item APPLE_PIE = register("apple_pie", new BlockItem(UBlocks.APPLE_PIE, new Item.Settings().maxCount(1)), ItemGroups.FOOD_AND_DRINK);
     Item APPLE_PIE_HOOF = register("apple_pie_hoof", new AliasedBlockItem(UBlocks.APPLE_PIE, new Item.Settings().maxCount(1)), ItemGroups.FOOD_AND_DRINK);
     Item APPLE_PIE_SLICE = register("apple_pie_slice", new Item(new Item.Settings().maxCount(16).food(UFoodComponents.PIE)), ItemGroups.FOOD_AND_DRINK);
     Item CANDIED_APPLE = register("candied_apple", new StagedFoodItem(new Item.Settings().food(UFoodComponents.CANDY).maxDamage(3), () -> Items.STICK), ItemGroups.FOOD_AND_DRINK);
 
-    Item LOVE_BOTTLE = register("love_bottle", new DrinkableItem(new Item.Settings().food(UFoodComponents.LOVE_BOTTLE).maxCount(1).recipeRemainder(Items.GLASS_BOTTLE)), ItemGroups.FOOD_AND_DRINK);
-    Item LOVE_BUCKET = register("love_bucket", new DrinkableItem(new Item.Settings().food(UFoodComponents.LOVE_BUCKET).recipeRemainder(Items.BUCKET)), ItemGroups.FOOD_AND_DRINK);
-    Item LOVE_MUG = register("love_mug", new DrinkableItem(new Item.Settings().food(UFoodComponents.LOVE_MUG).recipeRemainder(MUG)), ItemGroups.FOOD_AND_DRINK);
+    Item LOVE_BOTTLE = register("love_bottle", new ConsumableItem(new Item.Settings().food(UFoodComponents.LOVE_BOTTLE).maxCount(1).recipeRemainder(Items.GLASS_BOTTLE), UseAction.DRINK), ItemGroups.FOOD_AND_DRINK);
+    Item LOVE_BUCKET = register("love_bucket", new ConsumableItem(new Item.Settings().food(UFoodComponents.LOVE_BUCKET).recipeRemainder(Items.BUCKET), UseAction.DRINK), ItemGroups.FOOD_AND_DRINK);
+    Item LOVE_MUG = register("love_mug", new ConsumableItem(new Item.Settings().food(UFoodComponents.LOVE_MUG).recipeRemainder(MUG), UseAction.DRINK), ItemGroups.FOOD_AND_DRINK);
 
     Item GOLDEN_FEATHER = register("golden_feather", new Item(new Item.Settings().rarity(Rarity.UNCOMMON)), ItemGroups.NATURAL);
     Item GOLDEN_WING = register("golden_wing", new Item(new Item.Settings().rarity(Rarity.UNCOMMON)), ItemGroups.NATURAL);
@@ -226,6 +227,16 @@ public interface UItems {
     Item TURRET_SHELL = register("turret_shell", new Item(new Item.Settings()), ItemGroups.INGREDIENTS);
     Item SHELLY = register("shelly", new Item(new Item.Settings()), ItemGroups.INGREDIENTS);
 
+    Item ROTTEN_COD = register("rotten_cod", new Item(new Item.Settings().food(FoodComponents.ROTTEN_FLESH)), ItemGroups.FOOD_AND_DRINK);
+    Item ROTTEN_SALMON = register("rotten_salmon", new Item(new Item.Settings().food(FoodComponents.ROTTEN_FLESH)), ItemGroups.FOOD_AND_DRINK);
+    Item ROTTEN_TROPICAL_FISH = register("rotten_tropical_fish", new Item(new Item.Settings().food(FoodComponents.ROTTEN_FLESH)), ItemGroups.FOOD_AND_DRINK);
+    Item ROTTEN_PUFFERFISH = register("rotten_pufferfish", new Item(new Item.Settings().food(FoodComponents.ROTTEN_FLESH)), ItemGroups.FOOD_AND_DRINK);
+
+    Item COOKED_TROPICAL_FISH = register("cooked_tropical_fish", new Item(new Item.Settings().food(FoodComponents.COOKED_COD)), ItemGroups.FOOD_AND_DRINK);
+    Item COOKED_PUFFERFISH = register("cooked_pufferfish", new Item(new Item.Settings().food(FoodComponents.COOKED_COD)), ItemGroups.FOOD_AND_DRINK);
+    Item FRIED_AXOLOTL = register("fried_axolotl", new ConsumableItem(new Item.Settings().food(FoodComponents.COOKED_CHICKEN).recipeRemainder(Items.BUCKET), UseAction.EAT), ItemGroups.FOOD_AND_DRINK);
+    Item GREEN_FRIED_EGG = register("green_fried_egg", new Item(new Item.Settings().food(UFoodComponents.FRIED_EGG)), ItemGroups.FOOD_AND_DRINK);
+
     Item CARAPACE = register("carapace", new Item(new Item.Settings()), ItemGroups.INGREDIENTS);
     Item CLOTH_BED = register("cloth_bed", new FancyBedItem(UBlocks.CLOTH_BED, new Item.Settings().maxCount(1)), ItemGroups.FUNCTIONAL);
     Item CLOUD_BED = register("cloud_bed", new CloudBedItem(UBlocks.CLOUD_BED, new Item.Settings().maxCount(1)), ItemGroups.FUNCTIONAL);
diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json
index 5033eda6..df8a8f2e 100644
--- a/src/main/resources/assets/unicopia/lang/en_us.json
+++ b/src/main/resources/assets/unicopia/lang/en_us.json
@@ -74,6 +74,14 @@
   "item.unicopia.cooked_zap_apple": "Cooked Zap Apple",
   "item.unicopia.zap_apple": "Zap Apple",
   "item.unicopia.zap_bulb": "Unripened Zap Apple",
+  "item.unicopia.rotten_cod": "Rotten Cod",
+  "item.unicopia.rotten_salmon": "Rotten Salmon",
+  "item.unicopia.rotten_tropical_fish": "Rotten Tropical Fish",
+  "item.unicopia.rotten_pufferfish": "Rotten Pufferfish",
+  "item.unicopia.cooked_tropical_fish": "Cooked Tropical Fish",
+  "item.unicopia.cooked_pufferfish": "Cooked Pufferfish",
+  "item.unicopia.fried_axolotl": "Fried Axolotl",
+  "item.unicopia.green_fried_egg": "Green Fried Egg",
 
   "item.unicopia.love_bottle": "Bottle o' Love",
   "item.unicopia.love_bucket": "Love Bucket",
@@ -1639,6 +1647,8 @@
   "advancements.unicopia.eat_trick_apple.description": "Bite into a zap apple",
   "advancements.unicopia.eat_pinecone.title": "Desperation",
   "advancements.unicopia.eat_pinecone.description": "Eat a pinecone",
+  "advancements.unicopia.tastes_like_chicken.title": "Tastes Like Chicken",
+  "advancements.unicopia.tastes_like_chicken.description": "Fry and eat an axolotl",
   "advancements.unicopia.what_the_hay.title": "What The Hay",
   "advancements.unicopia.what_the_hay.description": "Eat an entire block of hay",
   "advancements.unicopia.oats_so_easy.title": "Oats So Easy",
diff --git a/src/main/resources/assets/unicopia/textures/item/cooked_pufferfish.png b/src/main/resources/assets/unicopia/textures/item/cooked_pufferfish.png
new file mode 100644
index 0000000000000000000000000000000000000000..eef6293b98036298983ac171eba86986ac7de86c
GIT binary patch
literal 6980
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

literal 0
HcmV?d00001

diff --git a/src/main/resources/assets/unicopia/textures/item/cooked_tropical_fish.png b/src/main/resources/assets/unicopia/textures/item/cooked_tropical_fish.png
new file mode 100644
index 0000000000000000000000000000000000000000..93747e0f5b39dc50d0dd2a7a073c5852c3863bad
GIT binary patch
literal 6795
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
Okx9o&J{vK8{=Wg*s#(YY

literal 0
HcmV?d00001

diff --git a/src/main/resources/assets/unicopia/textures/item/fried_axolotl.png b/src/main/resources/assets/unicopia/textures/item/fried_axolotl.png
new file mode 100644
index 0000000000000000000000000000000000000000..c1f00289233c492d62ddab8ac5969a87b2b1dd10
GIT binary patch
literal 6561
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
M%DAYHBW4!-8%(>9`Tzg`

literal 0
HcmV?d00001

diff --git a/src/main/resources/assets/unicopia/textures/item/fried_egg.png b/src/main/resources/assets/unicopia/textures/item/fried_egg.png
new file mode 100644
index 0000000000000000000000000000000000000000..55d4a4ac62e72c8b52a52f2abb8f69d635576dd9
GIT binary patch
literal 6662
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
F_uuBk?>ztj

literal 0
HcmV?d00001

diff --git a/src/main/resources/assets/unicopia/textures/item/green_fried_egg.png b/src/main/resources/assets/unicopia/textures/item/green_fried_egg.png
new file mode 100644
index 0000000000000000000000000000000000000000..46e85b593525b07d9d255eac13c3f56847339f20
GIT binary patch
literal 6709
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

literal 0
HcmV?d00001

diff --git a/src/main/resources/assets/unicopia/textures/item/rotten_cod.png b/src/main/resources/assets/unicopia/textures/item/rotten_cod.png
new file mode 100644
index 0000000000000000000000000000000000000000..9cd70e51624ed975676e165d8cb3490fbd73b7dc
GIT binary patch
literal 7008
zcmeHLX;@R&);?hhqku+4L<j*jpb0ri2uUQOK!hM5QBd@1HNyc?Aqhzst*wG+jTVdG
z0CF7yC|9k$iq@&r2}DIy1T0dgI$<4(h)UHecbyOv``zdHKELPQe+G8W-fOS*?)AQV
z?US6Wh>Zz%uywOV5X3<g5jG93PI$Anf_KmEUI~KOxM2yRM3Kl3u?8D>5-bpc$&WCg
zZ$>aj%)!14+F<D8K6rB@SQ@XZpg+RYFNQwV<YNYXlwgK9z_kn-6Z#~$?trFP-nZ6I
z(7y=H+V~d%Jsi3J8ZL^CB{S(LgU;eWJcG&RGvGgfjB@xWmyfc*o3Q15Mi7eFAa9XA
zY#gbd6As(Y3GerF3W2`4DPLR}s{#F?rasP{VAh8jfybKqO+<Ww{@i=pdT%05k&{J}
zVIiCby!HNqgd)~fRzxdHYa)?oV`FV=Pjaxgv$G#Qc!(ouq?@P5NH=$PvJZU>nKI7X
z-JKffJC1>}*=)}-JOP&(L}#&?I0(VU#>U>x-qpdumFeZ~#r)`3?=i&L8qdj`Ktar$
z3Fgj(-de;H^0XuvUwu??qUIKsRzzzXTRU*r?}RI5Zf0R_X=z~rvx{IJv2eB=?1fIU
z8X}euDQP6;lESUl-XVLA51rQZ#D^u-<l5M}409dsHfr=3s;?hAfD_2&@k7IelfxrK
zk<;ViXUt5PHCraf5|fe@pKEpL8JStxIeAO-mo5K#g?>#@@!ECkH<Xkb$}1|jZLj*r
zj=k0U_Wy9;;Gx4OPS)0)sy}_^?4{<G*2_P)U1|UI*6llY?{(b&?LpVm?q|<m^!)Mi
z70wIi+5hTe_7`5xz{||S!rX$0^CFmK;>4XTEWJ>x!IQ*9N!kz!bBQ%6q;Tt=<2K%`
zX-|eqHBGiIKI~sdb>Y;E%>Hl0a{ph<-VuAxs~xd7C&1>JJ0ro!%eQ*!7PG%JJCaz4
z%rxk!h^Y};z`z;cMHCB3NITWA*i%S~HBUC^kw_T0fQLnY2yR?sGMMpU))v}?#RmP@
zGZU&FRvGj!Pu@L0TTlHy;&hWi@3)c%aEBeH+JnP2<|sfhb2A0ufdkvf{xx6ifN-a-
zS_s*2wbh{a^*DBxYWQ}w4;Y%pe^W{|94ZJse&3+)DUi+v5HCO_^d~!^8oWAIoq<fc
z2c7Vv8g`Wzfn}dPgdMg3vb*yx3jAP!psvV$VqtQobCYoBtrGWdNaD{9-F5|2hG$sT
zHGI@z2N{{+l0YLouOo@)agrwqN%3VJ0YcJDv|R{&_lh{NFhwc_l$M1S#bRNiG-oJ8
zlh4P%B2isuAkT9Tn2_Eh77O|<&mG+%pP#uQ19W%XO~r*{=|M63^FaSQy*D_8Ug73Y
z4S99r`BcM?X&WH7^pU4pN#Z%%MfI@WgR+Viu`r?_1N<j01Gw6!?Nmd}F<eyR1_CJZ
zDi_k&NInB_-uoVbrFw`Nh`?%sz3_FC08G17DHaYrk+nS#NM(l)$KM}~(20ff)7Aj-
z5u_@Ee(wcb{B~YGF7-30nuYPeari|g#1~G6epKxuk~reopi8it>n$GaSIv+i+aacr
zKM1desMF_QP*J@W-nZ<%OA=4>K6~Gwe-Seu5b=66)*=WRO9YxjZ@X6>rW&dR-b%>T
zyqN>8cY^XZLGZ!0hcIaAfHbFbd|~;!PHIqL;pAnY`V3LIkTgTS2{viwPx5d;BMfN7
z132>E?}D1UT57<Ni2eeWTkiR=R*lB4SS*z5im6aI_62T`v)q`iRtEX+IzJ6cspMkz
zNT7Y?`~=ehAV97N374Z1ietj8?EsqelMbxe;kjvXkQSmV77NuLOTK}OB)_`0z!rWT
zNi|gUM8PhV+$;ryG{YklW)FJ7%E;`2dzVfUPxR=7xeIQapcZFdEb)wjgr5?3Y4(YQ
zIop;m0Ha>(E`X%YEPeo`(#YFEHS8CJxIwY64m%3dzQ?j*XFl6=0?*mXRLlq@)t7fZ
z%sIDZkRW5?npseOQxo)h>JDy9BnS}Kf)lErl?NgwPRGT6)~JV4>~07uh3S-gz~O@<
zjE|vEsOFcd4EnBuq9YKqI7}xNinmUNB0X0C+qskcJ%BDNhh4n-h@FgY?CsNV>9L0c
z-&b7tEy-fwzzhtI^8l=FynPN3bOrieF0e*z2b*fp7lrtNq(yM5cUK?m28Ws<4TB*B
z55t=c_#n8LPEBx_A=b!!6c`5KtKfzY)B%+U_$=HSNXP>d#}TGVGKXc6-YZD>VaF%%
zk+)P48X7AK4JE5JszgN!h9FsGiwXr1Cnvhs%{v-fF|BqGWzg^)^bE0c{p@^7ob~>Y
zor6ZN|8i2%GM~CSv6{yHV^2c5I_+}n@pvbT;E81;%Sdx8YI{fJyPzKKJ-4sb)L$Ea
z?8Yu;tNmyszIs)ZpYNTgJ3}sv&9(N*C9IlquB9Ukb-2)#?&vwM^Hxy9>#Li~=lO21
ze_~T>vu%V+msQufZI#;VV+!`}t#H~rWU8xf(yj2_4fLaFbl#(m60!Hjk}(B??JDW(
zj%--#ur{ldyRFLY?rSZ5Y}o<s&Q|_{8%0MMb1u$UcPK_{;X6)hL$NqrRi5iNu2#_+
zffjCkQ?dN3wyu?T)|`nac-NbK9_z*X@=5R?*S?!oR;nvHAtFDM{^D6a@dEwg=WQ=~
z_M{$&S$vG8u1x=P|7gZ*QBHYzUBTkh9Cg~YraOY08lr2?rBR`p3MBfgJ=Q;NZVJ4+
z<oMsVs2U%CSF^gANS-pcQnffQbpEceD-S(7e}DY@;~T8UZ@)yanVPM6nT@yM+iaF1
zE<u+N9mSWaQt1-8N{Z35Qq{&!`Jmt|wM4c6(~+fEl0qq<bRIcMAuHqp%FKXhMzlH<
zOIAeWXt3!yF>$h-1u~wT5<I~+D2opOshCbe&Pq*DYWZ0LiV>F&ecbF%AsbzE3j~w|
z_(mG4(qLpZolR%ZgjtFVCS`&xIY=W<<WCEmY=VH1fRe1!srml?nVFgNOcq_GN%BW|
zJf1&;>Ca@+z=NjER_Y{KG^N%Xhv>ry!?ZGuLakG%lw=%JB2}g91QZI)ll${aRYym^
zgI8)zDu5pTSrWBBN@w_|ruq+z(CUO40Afn$-$rQTvelUXG)${X*T^to2By?`4}_4*
z-o>laH7Q0qa+yDtf~A717FI<+SQ7v4{w@NSAW4y`Hb#NiA9(5%iSNbwAUAx*NM~Ro
z5dIGL1MhzBMrZJfj^>A{Wa)T%qA&pkU!N~m$rN(Fu`A&)<x*aNgvLUp92%R2Vl*Cx
z2GA1afif_ms8qrk03}jtbrPiv!=V72t^gdA$3eLaOhyZo%UCqFgv+H#17XPI%DFO*
zoXwT9Spy)ZY7|hFl9Yj2;ZSmbVzPO1sf5L*C32VnG&VON5wZygq@g@+01M+LG9{7#
zBa~dmpRCfPN?<z`sgfkjU#(0sPT+#`C&h{c6egYV-V&Q4(IrBFfD)xprf0pMh*P9u
z({&PDO*9Z?vRDizkHuwi*bL@-rx}<=3zdjt8lS?9Gx)affeZ*M!Rr(NjCSC{57l52
zok|m@Ql$te@1Eb@O-I9Vl1p@wFo_NW(05OCas5wpV;Lx)(U(3B7p;;j60`p|Z~X9(
zgTNoQJVK#`^|Otpz7sVaOY0l;4O0}xqeLbf4+UQ$>!YBRWMFb*o`BUiB}<kllQ6h@
zn98-^uJ|{lz(X-sB9keju>(-3CN30`B!J1FF=ePUP#%CXP!8`ybge2;mnqR;lafG3
z&<e`a_^eB&8aK-KL+{LF4BrJ1jK+ZffWHdnZzAlE-!u9d2l@XGO@fRL18y=9*Jp#9
z7u*Z|-`xyNn&G#d|KiKE7yrc-0Q%D+AEoanxjxDDQ3`yN^QY?iB-ckN@KMg6s_Q={
zm+kw<DNG69f->P@sqXvjkKj?uN;)Mx3|S99-LKDHuB`%#jXGkM7D4QW;EjNkmbrqF
zs1rpCiT5om9S1wSTuT2Pemk!gg-wd%Hl!9^STHA^R3F@Pb$p3v^ckNPksnI^F0x!&
zn^E|K-Oy?3&Jd@xQ3uE6-VBaCoH5JVHeXrsIC8z8w6?AN)DNSZM#ogWM7u|4l$Exf
z+nC*X{kHUrW|xJWo=tVUj)gzn=+3VX+ZvnYGpw-w-sRb=P^<V+3kj=GZbxkVCAQ_w
zBaYV%FO_puf3At8uZ>QvJYG^AOgS`X(3Y8HZ;E`pmLkVW@>d^xah*ZDX1{$~Qrk;u
zIB}QxF-$l-x1rnfX~mJe)%lg1H@1|!5GTyNck%V~(#zA|`hJEzbf~X&OpTAfy~gLa
z=r=iU*$(mRT)b<>o~`*}^YG8Bl*2li`wyJ1t7_bU-g&#UR_EMs@?wv6-NUhV?ZN5A
zKQEtpDMhFIb7VtpcSv$h<OmN>%Ui9fUryP1&1(2Y<T>@0x}H%oY<5@AkBbr_zOQ^#
z<1?rI(1lme-M(UHG~Q1>v#MdsOI3Q_{&lOT9zEM!L~KiWTRC<A&5}Ucfy>3R@Np}r
z_%3Wc_Mp+P?pH$hm5#N<HT;f<=Hf_?9Zdqkthlq=-5(wZYW8;z<%eg#;5|uQJ=X1M
z`P>sVJz1TFt7_Nx{vOUL9$V<e*|cY73?un<vWxOb^B42qWGpRSdFsZnOYFMYE58o<
zHmzsHqQBFIDjG8mGB-bMzY|YCV7{Mrx3093>EqsY_E7guWU15PXyzcRUD%692S_s{
z(vx0!-}bgPC#}8vI6841F<NqDVZ!<Jj-1Z49r_-RX8SLmwx&gmBRTrs7?)+cVpPo`
z(ILL{)^D5+`Yyt=^G7mQoaZTT{3A_@t%-E6>a_HGIO6nf#$`A3b^Z8rjc=~AVyy}B
zkX(bSAi)i_o2OL#Q(s_pYWec0tDax~Jow(d%Q>HQUB6ux8g^gDpDS90_E!IPvheXZ
g%Ibu_h5a$C=v74alC;N*@ZT6j!kDn#A@g(p1;)GXy8r+H

literal 0
HcmV?d00001

diff --git a/src/main/resources/assets/unicopia/textures/item/rotten_pufferfish.png b/src/main/resources/assets/unicopia/textures/item/rotten_pufferfish.png
new file mode 100644
index 0000000000000000000000000000000000000000..24ade9a54c2cdf2b865d97718727ba77f2611cd3
GIT binary patch
literal 6927
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
WUl_}q<JXP7)=MSvQQIO?i~a>Ll%Y2O

literal 0
HcmV?d00001

diff --git a/src/main/resources/assets/unicopia/textures/item/rotten_salmon.png b/src/main/resources/assets/unicopia/textures/item/rotten_salmon.png
new file mode 100644
index 0000000000000000000000000000000000000000..fc4d53c097708b72ca81d1d05f7fcac6a97cb451
GIT binary patch
literal 6986
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
Q{MQD#Y>M>Y`1x7?0B_61KmY&$

literal 0
HcmV?d00001

diff --git a/src/main/resources/assets/unicopia/textures/item/rotten_tropical_fish.png b/src/main/resources/assets/unicopia/textures/item/rotten_tropical_fish.png
new file mode 100644
index 0000000000000000000000000000000000000000..7c0354025831fcac81c4f6ff32566feb81a16d94
GIT binary patch
literal 6649
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
F{{ncV0p0)r

literal 0
HcmV?d00001

diff --git a/src/main/resources/data/unicopia/tags/items/groups/changeling.json b/src/main/resources/data/unicopia/tags/items/groups/changeling.json
index f25692d7..8f87e169 100644
--- a/src/main/resources/data/unicopia/tags/items/groups/changeling.json
+++ b/src/main/resources/data/unicopia/tags/items/groups/changeling.json
@@ -10,6 +10,7 @@
     "unicopia:chiselled_chitin_hull",
     "unicopia:chitin_spikes",
     "unicopia:mysterious_egg",
+    "unicopia:green_fried_egg",
     "unicopia:hive",
     "unicopia:dark_oak_stable_door",
     "#c:cooked_meat",
diff --git a/src/main/resources/data/unicopia/tags/items/groups/pegasus.json b/src/main/resources/data/unicopia/tags/items/groups/pegasus.json
index 59b825f5..269e51b9 100644
--- a/src/main/resources/data/unicopia/tags/items/groups/pegasus.json
+++ b/src/main/resources/data/unicopia/tags/items/groups/pegasus.json
@@ -26,6 +26,7 @@
     "#unicopia:bed_sheets",
     "#c:raw_fish",
     "#c:cooked_fish",
+    "#c:rotten_fish",
     "unicopia:rain_cloud_jar",
     "unicopia:storm_cloud_jar",
     "unicopia:lightning_jar",

From 5529382e7112e3721fa0a24f94f650bf5a017483 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Fri, 5 Apr 2024 18:33:24 +0100
Subject: [PATCH 18/21] Move recipes to their own package

---
 .../unicopia/ability/EarthPonyGrowAbility.java               | 4 ++--
 .../ability/magic/spell/crafting/SpellCraftingRecipe.java    | 2 +-
 .../ability/magic/spell/crafting/SpellDuplicatingRecipe.java | 1 +
 .../ability/magic/spell/crafting/SpellEnhancingRecipe.java   | 1 +
 .../magic/spell/crafting/SpellShapedCraftingRecipe.java      | 2 +-
 .../ability/magic/spell/crafting/SpellbookRecipe.java        | 2 +-
 .../client/gui/spellbook/SpellbookCraftingPageContent.java   | 2 +-
 .../java/com/minelittlepony/unicopia/compat/emi/Main.java    | 4 ++--
 .../unicopia/container/ShapingBenchScreenHandler.java        | 2 +-
 .../unicopia/container/SpellbookScreenHandler.java           | 2 +-
 .../unicopia/datagen/providers/UAdvancementsProvider.java    | 1 +
 .../datagen/providers/recipe/CraftingMaterialHelper.java     | 2 +-
 .../datagen/providers/recipe/GrowingRecipeJsonBuilder.java   | 2 +-
 .../providers/recipe/SpellcraftingRecipeJsonBuilder.java     | 3 ++-
 .../providers/recipe/TrickCraftingRecipeJsonBuilder.java     | 2 +-
 .../unicopia/datagen/providers/recipe/URecipeProvider.java   | 2 +-
 .../unicopia/datagen/providers/tag/UItemTagProvider.java     | 3 +--
 src/main/java/com/minelittlepony/unicopia/item/UItems.java   | 1 +
 .../unicopia/{item/cloud => recipe}/CloudShapingRecipe.java  | 3 +--
 .../unicopia/{item => recipe}/GlowingRecipe.java             | 4 +++-
 .../unicopia/{item => recipe}/ItemCombinationRecipe.java     | 2 +-
 .../unicopia/{item => recipe}/JarExtractRecipe.java          | 4 +++-
 .../unicopia/{item => recipe}/JarInsertRecipe.java           | 5 ++++-
 .../unicopia/{item => recipe}/TransformCropsRecipe.java      | 2 +-
 .../minelittlepony/unicopia/{item => recipe}/URecipes.java   | 3 +--
 .../unicopia/{item => recipe}/ZapAppleRecipe.java            | 3 ++-
 26 files changed, 37 insertions(+), 27 deletions(-)
 rename src/main/java/com/minelittlepony/unicopia/{item/cloud => recipe}/CloudShapingRecipe.java (90%)
 rename src/main/java/com/minelittlepony/unicopia/{item => recipe}/GlowingRecipe.java (93%)
 rename src/main/java/com/minelittlepony/unicopia/{item => recipe}/ItemCombinationRecipe.java (97%)
 rename src/main/java/com/minelittlepony/unicopia/{item => recipe}/JarExtractRecipe.java (94%)
 rename src/main/java/com/minelittlepony/unicopia/{item => recipe}/JarInsertRecipe.java (91%)
 rename src/main/java/com/minelittlepony/unicopia/{item => recipe}/TransformCropsRecipe.java (99%)
 rename src/main/java/com/minelittlepony/unicopia/{item => recipe}/URecipes.java (97%)
 rename src/main/java/com/minelittlepony/unicopia/{item => recipe}/ZapAppleRecipe.java (96%)

diff --git a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java
index 75cd435f..0454681e 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/EarthPonyGrowAbility.java
@@ -10,10 +10,10 @@ import com.minelittlepony.unicopia.ability.data.Hit;
 import com.minelittlepony.unicopia.ability.data.Pos;
 import com.minelittlepony.unicopia.block.UBlocks;
 import com.minelittlepony.unicopia.entity.player.Pony;
-import com.minelittlepony.unicopia.item.TransformCropsRecipe;
-import com.minelittlepony.unicopia.item.URecipes;
 import com.minelittlepony.unicopia.particle.MagicParticleEffect;
 import com.minelittlepony.unicopia.particle.ParticleUtils;
+import com.minelittlepony.unicopia.recipe.TransformCropsRecipe;
+import com.minelittlepony.unicopia.recipe.URecipes;
 import com.minelittlepony.unicopia.server.world.BlockDestructionManager;
 import com.minelittlepony.unicopia.util.TraceHelper;
 import com.minelittlepony.unicopia.util.VecHelper;
diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellCraftingRecipe.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellCraftingRecipe.java
index 99528460..3badf6d4 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellCraftingRecipe.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellCraftingRecipe.java
@@ -9,7 +9,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
 import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
 import com.minelittlepony.unicopia.container.inventory.SpellbookInventory;
 import com.minelittlepony.unicopia.item.EnchantableItem;
-import com.minelittlepony.unicopia.item.URecipes;
+import com.minelittlepony.unicopia.recipe.URecipes;
 import com.minelittlepony.unicopia.util.InventoryUtil;
 import com.mojang.datafixers.util.Pair;
 
diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellDuplicatingRecipe.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellDuplicatingRecipe.java
index 0b29c917..8d228a2c 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellDuplicatingRecipe.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellDuplicatingRecipe.java
@@ -4,6 +4,7 @@ import com.google.gson.JsonObject;
 import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
 import com.minelittlepony.unicopia.container.inventory.SpellbookInventory;
 import com.minelittlepony.unicopia.item.*;
+import com.minelittlepony.unicopia.recipe.URecipes;
 import com.minelittlepony.unicopia.util.InventoryUtil;
 
 import net.minecraft.item.ItemStack;
diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellEnhancingRecipe.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellEnhancingRecipe.java
index 7aebd7e5..d63a2e86 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellEnhancingRecipe.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellEnhancingRecipe.java
@@ -4,6 +4,7 @@ import com.google.gson.JsonObject;
 import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
 import com.minelittlepony.unicopia.container.inventory.SpellbookInventory;
 import com.minelittlepony.unicopia.item.*;
+import com.minelittlepony.unicopia.recipe.URecipes;
 
 import net.minecraft.item.ItemStack;
 import net.minecraft.network.PacketByteBuf;
diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellShapedCraftingRecipe.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellShapedCraftingRecipe.java
index cc0dcc0f..e07e1ab3 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellShapedCraftingRecipe.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellShapedCraftingRecipe.java
@@ -2,7 +2,7 @@ package com.minelittlepony.unicopia.ability.magic.spell.crafting;
 
 import com.google.gson.JsonObject;
 import com.minelittlepony.unicopia.item.EnchantableItem;
-import com.minelittlepony.unicopia.item.URecipes;
+import com.minelittlepony.unicopia.recipe.URecipes;
 import com.minelittlepony.unicopia.util.InventoryUtil;
 
 import net.minecraft.inventory.RecipeInputInventory;
diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellbookRecipe.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellbookRecipe.java
index 89043ce8..49b781aa 100644
--- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellbookRecipe.java
+++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellbookRecipe.java
@@ -5,7 +5,7 @@ import java.util.List;
 import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
 import com.minelittlepony.unicopia.container.inventory.SpellbookInventory;
 import com.minelittlepony.unicopia.item.UItems;
-import com.minelittlepony.unicopia.item.URecipes;
+import com.minelittlepony.unicopia.recipe.URecipes;
 
 import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.Recipe;
diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookCraftingPageContent.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookCraftingPageContent.java
index 6350bc98..a6fde947 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookCraftingPageContent.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookCraftingPageContent.java
@@ -7,7 +7,7 @@ import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe;
 import com.minelittlepony.unicopia.client.gui.DrawableUtil;
 import com.minelittlepony.unicopia.client.gui.MagicText;
 import com.minelittlepony.unicopia.container.SpellbookState;
-import com.minelittlepony.unicopia.item.URecipes;
+import com.minelittlepony.unicopia.recipe.URecipes;
 import com.mojang.blaze3d.systems.RenderSystem;
 
 import net.minecraft.client.gui.DrawContext;
diff --git a/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java b/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java
index 9523cce2..4924981f 100644
--- a/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java
+++ b/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java
@@ -15,10 +15,10 @@ import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
 import com.minelittlepony.unicopia.block.UBlocks;
 import com.minelittlepony.unicopia.block.state.Schematic;
 import com.minelittlepony.unicopia.item.EnchantableItem;
-import com.minelittlepony.unicopia.item.TransformCropsRecipe;
 import com.minelittlepony.unicopia.item.UItems;
-import com.minelittlepony.unicopia.item.URecipes;
 import com.minelittlepony.unicopia.item.group.MultiItem;
+import com.minelittlepony.unicopia.recipe.TransformCropsRecipe;
+import com.minelittlepony.unicopia.recipe.URecipes;
 
 import dev.emi.emi.api.EmiPlugin;
 import dev.emi.emi.api.EmiRegistry;
diff --git a/src/main/java/com/minelittlepony/unicopia/container/ShapingBenchScreenHandler.java b/src/main/java/com/minelittlepony/unicopia/container/ShapingBenchScreenHandler.java
index 3ff1ac4a..2758a373 100644
--- a/src/main/java/com/minelittlepony/unicopia/container/ShapingBenchScreenHandler.java
+++ b/src/main/java/com/minelittlepony/unicopia/container/ShapingBenchScreenHandler.java
@@ -1,7 +1,7 @@
 package com.minelittlepony.unicopia.container;
 
 import com.minelittlepony.unicopia.block.UBlocks;
-import com.minelittlepony.unicopia.item.URecipes;
+import com.minelittlepony.unicopia.recipe.URecipes;
 
 import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.entity.player.PlayerInventory;
diff --git a/src/main/java/com/minelittlepony/unicopia/container/SpellbookScreenHandler.java b/src/main/java/com/minelittlepony/unicopia/container/SpellbookScreenHandler.java
index 5f675f85..47a088f1 100644
--- a/src/main/java/com/minelittlepony/unicopia/container/SpellbookScreenHandler.java
+++ b/src/main/java/com/minelittlepony/unicopia/container/SpellbookScreenHandler.java
@@ -10,7 +10,7 @@ import com.minelittlepony.unicopia.compat.trinkets.TrinketsDelegate;
 import com.minelittlepony.unicopia.container.inventory.*;
 import com.minelittlepony.unicopia.entity.player.Pony;
 import com.minelittlepony.unicopia.item.UItems;
-import com.minelittlepony.unicopia.item.URecipes;
+import com.minelittlepony.unicopia.recipe.URecipes;
 import com.mojang.datafixers.util.Pair;
 
 import net.minecraft.enchantment.EnchantmentHelper;
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UAdvancementsProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UAdvancementsProvider.java
index a540572c..de390bfb 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/UAdvancementsProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/UAdvancementsProvider.java
@@ -146,6 +146,7 @@ public class UAdvancementsProvider extends FabricAdvancementProvider {
 
     private void generateUnicornTribeAdvancementsTree(Consumer<Advancement> consumer, AdvancementDisplayBuilder.Parent parent) {
         parent.child(UItems.SPELLBOOK).criterion("has_spellbook", hasItems(UItems.SPELLBOOK)).build(consumer, "books").children(p -> {
+            // TODO:
             //ItemPredicate bookPredicate = ItemPredicate.Builder.create().tag(ItemTags.BOOKSHELF_BOOKS).build();
             //p.child(Items.BOOK).hidden().frame(AdvancementFrame.CHALLENGE).criterion("has_books", InventoryChangedCriterion.Conditions.items(IntStream.range(0, 9 * 4).mapToObj(i -> bookPredicate).toArray(ItemPredicate[]::new))).build(consumer, "books_books_books");
             p.child(UItems.CRYSTAL_SHARD).criterion("has_shard", hasItems(UItems.CRYSTAL_SHARD)).build(consumer, "crystaline").children(pp -> {
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/CraftingMaterialHelper.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/CraftingMaterialHelper.java
index 25118dd3..a32f4245 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/CraftingMaterialHelper.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/CraftingMaterialHelper.java
@@ -4,7 +4,7 @@ import java.util.Map;
 import java.util.NoSuchElementException;
 
 import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
-import com.minelittlepony.unicopia.item.URecipes;
+import com.minelittlepony.unicopia.recipe.URecipes;
 import com.mojang.datafixers.util.Either;
 
 import net.fabricmc.fabric.api.tag.convention.v1.ConventionalItemTags;
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/GrowingRecipeJsonBuilder.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/GrowingRecipeJsonBuilder.java
index 1daab591..53d44888 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/GrowingRecipeJsonBuilder.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/GrowingRecipeJsonBuilder.java
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.Nullable;
 import org.spongepowered.include.com.google.common.base.Preconditions;
 
 import com.google.gson.JsonObject;
-import com.minelittlepony.unicopia.item.URecipes;
+import com.minelittlepony.unicopia.recipe.URecipes;
 import com.mojang.serialization.JsonOps;
 
 import net.minecraft.advancement.Advancement;
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/SpellcraftingRecipeJsonBuilder.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/SpellcraftingRecipeJsonBuilder.java
index ca8d7b58..c8ddf638 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/SpellcraftingRecipeJsonBuilder.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/SpellcraftingRecipeJsonBuilder.java
@@ -15,7 +15,8 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
 import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
 import com.minelittlepony.unicopia.advancement.TraitDiscoveredCriterion;
 import com.minelittlepony.unicopia.item.UItems;
-import com.minelittlepony.unicopia.item.URecipes;
+import com.minelittlepony.unicopia.recipe.URecipes;
+
 import net.minecraft.advancement.Advancement;
 import net.minecraft.advancement.AdvancementRewards;
 import net.minecraft.advancement.CriterionMerger;
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/TrickCraftingRecipeJsonBuilder.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/TrickCraftingRecipeJsonBuilder.java
index 22868e0c..ccccdc12 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/TrickCraftingRecipeJsonBuilder.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/recipe/TrickCraftingRecipeJsonBuilder.java
@@ -8,7 +8,7 @@ import org.spongepowered.include.com.google.common.base.Preconditions;
 
 import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
-import com.minelittlepony.unicopia.item.URecipes;
+import com.minelittlepony.unicopia.recipe.URecipes;
 
 import net.minecraft.advancement.Advancement;
 import net.minecraft.advancement.AdvancementRewards;
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 aad4a126..0597b525 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
@@ -17,7 +17,7 @@ import com.minelittlepony.unicopia.datagen.ItemFamilies;
 import com.minelittlepony.unicopia.datagen.UBlockFamilies;
 import com.minelittlepony.unicopia.datagen.providers.recipe.BedSheetPatternRecipeBuilder.PatternTemplate;
 import com.minelittlepony.unicopia.item.UItems;
-import com.minelittlepony.unicopia.item.URecipes;
+import com.minelittlepony.unicopia.recipe.URecipes;
 import com.minelittlepony.unicopia.server.world.UTreeGen;
 import com.mojang.datafixers.util.Either;
 
diff --git a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
index 922e19fa..42365d56 100644
--- a/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
+++ b/src/main/java/com/minelittlepony/unicopia/datagen/providers/tag/UItemTagProvider.java
@@ -228,9 +228,8 @@ public class UItemTagProvider extends FabricTagProvider.ItemTagProvider {
             .addOptionalTag(new Identifier("c", "raw_pork"))
             .addOptionalTag(new Identifier("c", "lemon_chickens"));
         getOrCreateTagBuilder(UConventionalTags.Items.ROTTEN_MEAT).add(Items.ROTTEN_FLESH);
-        getOrCreateTagBuilder(UConventionalTags.Items.ROTTEN_FISH);//.add(Items.ROTTEN_FLESH); TODO:
         getOrCreateTagBuilder(UConventionalTags.Items.ROTTEN_INSECT).add(Items.FERMENTED_SPIDER_EYE);
-        getOrCreateTagBuilder(UConventionalTags.Items.COOKED_INSECT);//.add(Items.FERMENTED_SPIDER_EYE);
+        getOrCreateTagBuilder(UConventionalTags.Items.COOKED_INSECT);//.add(Items.FERMENTED_SPIDER_EYE); TODO
         getOrCreateTagBuilder(UConventionalTags.Items.RAW_INSECT).add(Items.SPIDER_EYE, UItems.BUTTERFLY, UItems.WHEAT_WORMS, UBlocks.WORM_BLOCK.asItem());
         getOrCreateTagBuilder(UConventionalTags.Items.WORMS).add(UItems.WHEAT_WORMS);
         getOrCreateTagBuilder(UConventionalTags.Items.STICKS).add(Items.STICK);
diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
index 894b6d2d..5e69e0d6 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
@@ -12,6 +12,7 @@ import com.minelittlepony.unicopia.item.cloud.CloudBedItem;
 import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
 import com.minelittlepony.unicopia.item.group.ItemGroupRegistry;
 import com.minelittlepony.unicopia.item.group.UItemGroups;
+import com.minelittlepony.unicopia.recipe.URecipes;
 import com.terraformersmc.terraform.boat.api.TerraformBoatType;
 import com.terraformersmc.terraform.boat.api.TerraformBoatTypeRegistry;
 import com.terraformersmc.terraform.boat.api.item.TerraformBoatItemHelper;
diff --git a/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudShapingRecipe.java b/src/main/java/com/minelittlepony/unicopia/recipe/CloudShapingRecipe.java
similarity index 90%
rename from src/main/java/com/minelittlepony/unicopia/item/cloud/CloudShapingRecipe.java
rename to src/main/java/com/minelittlepony/unicopia/recipe/CloudShapingRecipe.java
index 4f12f7db..063130fd 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/cloud/CloudShapingRecipe.java
+++ b/src/main/java/com/minelittlepony/unicopia/recipe/CloudShapingRecipe.java
@@ -1,7 +1,6 @@
-package com.minelittlepony.unicopia.item.cloud;
+package com.minelittlepony.unicopia.recipe;
 
 import com.minelittlepony.unicopia.block.UBlocks;
-import com.minelittlepony.unicopia.item.URecipes;
 
 import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.Ingredient;
diff --git a/src/main/java/com/minelittlepony/unicopia/item/GlowingRecipe.java b/src/main/java/com/minelittlepony/unicopia/recipe/GlowingRecipe.java
similarity index 93%
rename from src/main/java/com/minelittlepony/unicopia/item/GlowingRecipe.java
rename to src/main/java/com/minelittlepony/unicopia/recipe/GlowingRecipe.java
index 663dd8a5..32fdfc37 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/GlowingRecipe.java
+++ b/src/main/java/com/minelittlepony/unicopia/recipe/GlowingRecipe.java
@@ -1,4 +1,6 @@
-package com.minelittlepony.unicopia.item;
+package com.minelittlepony.unicopia.recipe;
+
+import com.minelittlepony.unicopia.item.GlowableItem;
 
 import net.minecraft.inventory.RecipeInputInventory;
 import net.minecraft.item.ItemStack;
diff --git a/src/main/java/com/minelittlepony/unicopia/item/ItemCombinationRecipe.java b/src/main/java/com/minelittlepony/unicopia/recipe/ItemCombinationRecipe.java
similarity index 97%
rename from src/main/java/com/minelittlepony/unicopia/item/ItemCombinationRecipe.java
rename to src/main/java/com/minelittlepony/unicopia/recipe/ItemCombinationRecipe.java
index ccdaa038..7a3b998e 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/ItemCombinationRecipe.java
+++ b/src/main/java/com/minelittlepony/unicopia/recipe/ItemCombinationRecipe.java
@@ -1,4 +1,4 @@
-package com.minelittlepony.unicopia.item;
+package com.minelittlepony.unicopia.recipe;
 
 import net.minecraft.inventory.RecipeInputInventory;
 import net.minecraft.item.ItemStack;
diff --git a/src/main/java/com/minelittlepony/unicopia/item/JarExtractRecipe.java b/src/main/java/com/minelittlepony/unicopia/recipe/JarExtractRecipe.java
similarity index 94%
rename from src/main/java/com/minelittlepony/unicopia/item/JarExtractRecipe.java
rename to src/main/java/com/minelittlepony/unicopia/recipe/JarExtractRecipe.java
index b480d05e..5d28fd91 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/JarExtractRecipe.java
+++ b/src/main/java/com/minelittlepony/unicopia/recipe/JarExtractRecipe.java
@@ -1,7 +1,9 @@
-package com.minelittlepony.unicopia.item;
+package com.minelittlepony.unicopia.recipe;
 
 import org.jetbrains.annotations.Nullable;
 
+import com.minelittlepony.unicopia.item.UItems;
+
 import net.minecraft.inventory.RecipeInputInventory;
 import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.RecipeSerializer;
diff --git a/src/main/java/com/minelittlepony/unicopia/item/JarInsertRecipe.java b/src/main/java/com/minelittlepony/unicopia/recipe/JarInsertRecipe.java
similarity index 91%
rename from src/main/java/com/minelittlepony/unicopia/item/JarInsertRecipe.java
rename to src/main/java/com/minelittlepony/unicopia/recipe/JarInsertRecipe.java
index 776a8bc3..694c347a 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/JarInsertRecipe.java
+++ b/src/main/java/com/minelittlepony/unicopia/recipe/JarInsertRecipe.java
@@ -1,4 +1,7 @@
-package com.minelittlepony.unicopia.item;
+package com.minelittlepony.unicopia.recipe;
+
+import com.minelittlepony.unicopia.item.EmptyJarItem;
+import com.minelittlepony.unicopia.item.UItems;
 
 import net.minecraft.inventory.RecipeInputInventory;
 import net.minecraft.item.ItemStack;
diff --git a/src/main/java/com/minelittlepony/unicopia/item/TransformCropsRecipe.java b/src/main/java/com/minelittlepony/unicopia/recipe/TransformCropsRecipe.java
similarity index 99%
rename from src/main/java/com/minelittlepony/unicopia/item/TransformCropsRecipe.java
rename to src/main/java/com/minelittlepony/unicopia/recipe/TransformCropsRecipe.java
index edc9924a..1d914706 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/TransformCropsRecipe.java
+++ b/src/main/java/com/minelittlepony/unicopia/recipe/TransformCropsRecipe.java
@@ -1,4 +1,4 @@
-package com.minelittlepony.unicopia.item;
+package com.minelittlepony.unicopia.recipe;
 
 import java.util.HashSet;
 import java.util.Set;
diff --git a/src/main/java/com/minelittlepony/unicopia/item/URecipes.java b/src/main/java/com/minelittlepony/unicopia/recipe/URecipes.java
similarity index 97%
rename from src/main/java/com/minelittlepony/unicopia/item/URecipes.java
rename to src/main/java/com/minelittlepony/unicopia/recipe/URecipes.java
index 52aff325..d45af14c 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/URecipes.java
+++ b/src/main/java/com/minelittlepony/unicopia/recipe/URecipes.java
@@ -1,10 +1,9 @@
-package com.minelittlepony.unicopia.item;
+package com.minelittlepony.unicopia.recipe;
 
 import java.util.List;
 
 import com.google.gson.JsonArray;
 import com.minelittlepony.unicopia.ability.magic.spell.crafting.*;
-import com.minelittlepony.unicopia.item.cloud.CloudShapingRecipe;
 
 import net.fabricmc.fabric.api.loot.v2.LootTableEvents;
 import net.minecraft.loot.LootTable;
diff --git a/src/main/java/com/minelittlepony/unicopia/item/ZapAppleRecipe.java b/src/main/java/com/minelittlepony/unicopia/recipe/ZapAppleRecipe.java
similarity index 96%
rename from src/main/java/com/minelittlepony/unicopia/item/ZapAppleRecipe.java
rename to src/main/java/com/minelittlepony/unicopia/recipe/ZapAppleRecipe.java
index 82c1d162..a92b956a 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/ZapAppleRecipe.java
+++ b/src/main/java/com/minelittlepony/unicopia/recipe/ZapAppleRecipe.java
@@ -1,8 +1,9 @@
-package com.minelittlepony.unicopia.item;
+package com.minelittlepony.unicopia.recipe;
 
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParseException;
 import com.google.gson.JsonSyntaxException;
+import com.minelittlepony.unicopia.item.UItems;
 
 import net.minecraft.item.ItemStack;
 import net.minecraft.network.PacketByteBuf;

From 34bdd7f135bb54fcdaf841a31ba30a889b61f571 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Fri, 5 Apr 2024 23:32:14 +0100
Subject: [PATCH 19/21] Set rock stew's max count to 1

---
 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 5e69e0d6..b9495246 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
@@ -111,7 +111,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 StewItem(new Item.Settings().food(FoodComponents.MUSHROOM_STEW).recipeRemainder(Items.BOWL)), ItemGroups.FOOD_AND_DRINK);
+    Item ROCK_STEW = register("rock_stew", new StewItem(new Item.Settings().food(FoodComponents.MUSHROOM_STEW).maxCount(1).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 7140ce6d0ee2a6fb7da86f5a8a2a74fcb32354a1 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Fri, 5 Apr 2024 23:41:32 +0100
Subject: [PATCH 20/21] Return the empty container when eating stacked food
 with containers

---
 .../unicopia/item/ConsumableItem.java               | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/main/java/com/minelittlepony/unicopia/item/ConsumableItem.java b/src/main/java/com/minelittlepony/unicopia/item/ConsumableItem.java
index dba7218d..747115e1 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/ConsumableItem.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/ConsumableItem.java
@@ -4,8 +4,10 @@ import java.util.Optional;
 
 import net.minecraft.advancement.criterion.Criteria;
 import net.minecraft.entity.LivingEntity;
+import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
+import net.minecraft.item.ItemUsage;
 import net.minecraft.server.network.ServerPlayerEntity;
 import net.minecraft.stat.Stats;
 import net.minecraft.util.UseAction;
@@ -29,7 +31,16 @@ public class ConsumableItem extends Item {
             serverPlayerEntity.incrementStat(Stats.USED.getOrCreateStat(this));
         }
 
-        return stack.isEmpty() ? Optional.ofNullable(getRecipeRemainder()).map(Item::getDefaultStack).orElse(ItemStack.EMPTY) : stack;
+        if (stack.isEmpty()) {
+            return stack.isEmpty() ? Optional.ofNullable(getRecipeRemainder()).map(Item::getDefaultStack).orElse(ItemStack.EMPTY) : stack;
+        }
+
+        if (user instanceof PlayerEntity player) {
+            return Optional.ofNullable(getRecipeRemainder()).map(Item::getDefaultStack).map(remainder -> {
+                return ItemUsage.exchangeStack(stack, player, remainder);
+            }).orElse(stack);
+        }
+        return stack;
     }
 
     @Override

From ca2be7ae8504ec2f281eee10385f9f50e44c5249 Mon Sep 17 00:00:00 2001
From: Sollace <sollacea@gmail.com>
Date: Fri, 5 Apr 2024 23:41:51 +0100
Subject: [PATCH 21/21] Set the max count for fried axolotl to 1 (same as other
 bucket foods)

---
 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 b9495246..9ec070b3 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
@@ -235,7 +235,7 @@ public interface UItems {
 
     Item COOKED_TROPICAL_FISH = register("cooked_tropical_fish", new Item(new Item.Settings().food(FoodComponents.COOKED_COD)), ItemGroups.FOOD_AND_DRINK);
     Item COOKED_PUFFERFISH = register("cooked_pufferfish", new Item(new Item.Settings().food(FoodComponents.COOKED_COD)), ItemGroups.FOOD_AND_DRINK);
-    Item FRIED_AXOLOTL = register("fried_axolotl", new ConsumableItem(new Item.Settings().food(FoodComponents.COOKED_CHICKEN).recipeRemainder(Items.BUCKET), UseAction.EAT), ItemGroups.FOOD_AND_DRINK);
+    Item FRIED_AXOLOTL = register("fried_axolotl", new ConsumableItem(new Item.Settings().food(FoodComponents.COOKED_CHICKEN).maxCount(1).recipeRemainder(Items.BUCKET), UseAction.EAT), ItemGroups.FOOD_AND_DRINK);
     Item GREEN_FRIED_EGG = register("green_fried_egg", new Item(new Item.Settings().food(UFoodComponents.FRIED_EGG)), ItemGroups.FOOD_AND_DRINK);
 
     Item CARAPACE = register("carapace", new Item(new Item.Settings()), ItemGroups.INGREDIENTS);