From 661dd7ed0141774e5be8348cafb3fd240733c50b Mon Sep 17 00:00:00 2001 From: BlayTheNinth <1933180+blay09@users.noreply.github.com> Date: Sun, 26 May 2024 17:50:26 +0200 Subject: [PATCH] feat: Add crafting table functionality to cutting boards #387 --- .../block/CuttingBoardBlock.java | 54 +++++ .../menu/CuttingBoardMenu.java | 185 ++++++++++++++++++ .../cookingforblockheads/menu/ModMenus.java | 15 ++ .../cookingforblockheads/lang/en_us.json | 1 + 4 files changed, 255 insertions(+) create mode 100644 common/src/main/java/net/blay09/mods/cookingforblockheads/menu/CuttingBoardMenu.java diff --git a/common/src/main/java/net/blay09/mods/cookingforblockheads/block/CuttingBoardBlock.java b/common/src/main/java/net/blay09/mods/cookingforblockheads/block/CuttingBoardBlock.java index a4caf0df..f52262be 100644 --- a/common/src/main/java/net/blay09/mods/cookingforblockheads/block/CuttingBoardBlock.java +++ b/common/src/main/java/net/blay09/mods/cookingforblockheads/block/CuttingBoardBlock.java @@ -1,15 +1,32 @@ package net.blay09.mods.cookingforblockheads.block; import com.mojang.serialization.MapCodec; +import net.blay09.mods.balm.api.menu.BalmMenuProvider; import net.blay09.mods.cookingforblockheads.block.entity.CuttingBoardBlockEntity; +import net.blay09.mods.cookingforblockheads.menu.CuttingBoardMenu; import net.minecraft.core.BlockPos; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.stats.Stats; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.SimpleMenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ContainerLevelAccess; +import net.minecraft.world.inventory.CraftingMenu; import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; import org.jetbrains.annotations.Nullable; @@ -17,6 +34,7 @@ public class CuttingBoardBlock extends BaseKitchenBlock { public static final MapCodec CODEC = simpleCodec(CuttingBoardBlock::new); + private static final Component CONTAINER_TITLE = Component.translatable("container.cookingforblockheads.cutting_board"); private static final VoxelShape SHAPE = Block.box(2, 0, 2, 14, 1.6, 14); @@ -44,4 +62,40 @@ public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { protected MapCodec codec() { return CODEC; } + + @Override + protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult blockHitResult) { + if (level.isClientSide) { + return InteractionResult.SUCCESS; + } else { + player.openMenu(state.getMenuProvider(level, pos)); + player.awardStat(Stats.INTERACT_WITH_CRAFTING_TABLE); + return InteractionResult.CONSUME; + } + } + + @Override + protected MenuProvider getMenuProvider(BlockState state, Level level, BlockPos pos) { + return new BalmMenuProvider() { + @Override + public Component getDisplayName() { + return CONTAINER_TITLE; + } + + @Override + public AbstractContainerMenu createMenu(int windowId, Inventory inventory, Player player) { + return new CuttingBoardMenu(windowId, inventory, ContainerLevelAccess.create(level, pos)); + } + + @Override + public BlockPos getScreenOpeningData(ServerPlayer serverPlayer) { + return pos; + } + + @Override + public StreamCodec getScreenStreamCodec() { + return BlockPos.STREAM_CODEC.cast(); + } + }; + } } diff --git a/common/src/main/java/net/blay09/mods/cookingforblockheads/menu/CuttingBoardMenu.java b/common/src/main/java/net/blay09/mods/cookingforblockheads/menu/CuttingBoardMenu.java new file mode 100644 index 00000000..8015fbe7 --- /dev/null +++ b/common/src/main/java/net/blay09/mods/cookingforblockheads/menu/CuttingBoardMenu.java @@ -0,0 +1,185 @@ +package net.blay09.mods.cookingforblockheads.menu; + +import net.blay09.mods.cookingforblockheads.block.ModBlocks; +import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.Container; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.inventory.*; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.Level; + +public class CuttingBoardMenu extends RecipeBookMenu { + private final CraftingContainer craftSlots = new TransientCraftingContainer(this, 3, 3); + private final ResultContainer resultSlots = new ResultContainer(); + private final ContainerLevelAccess access; + private final Player player; + + public CuttingBoardMenu(int windowId, Inventory inventory) { + this(windowId, inventory, ContainerLevelAccess.NULL); + } + + public CuttingBoardMenu(int windowId, Inventory inventory, ContainerLevelAccess access) { + super(MenuType.CRAFTING, windowId); + this.access = access; + this.player = inventory.player; + addSlot(new ResultSlot(inventory.player, craftSlots, resultSlots, 0, 124, 35)); + + for (int x = 0; x < 3; x++) { + for (int y = 0; y < 3; y++) { + addSlot(new Slot(craftSlots, y + x * 3, 30 + y * 18, 17 + x * 18)); + } + } + + for (int x = 0; x < 3; x++) { + for (int y = 0; y < 9; y++) { + addSlot(new Slot(inventory, y + x * 9 + 9, 8 + y * 18, 84 + x * 18)); + } + } + + for (int i = 0; i < 9; i++) { + addSlot(new Slot(inventory, i, 8 + i * 18, 142)); + } + } + + protected static void slotChangedCraftingGrid(AbstractContainerMenu menu, Level level, Player player, CraftingContainer craftingContainer, ResultContainer resultContainer) { + if (!level.isClientSide) { + final var serverPlayer = (ServerPlayer) player; + var itemStack = ItemStack.EMPTY; + final var optionalRecipe = level.getServer().getRecipeManager().getRecipeFor(RecipeType.CRAFTING, craftingContainer, level); + if (optionalRecipe.isPresent()) { + final var recipeHolder = optionalRecipe.get(); + final var recipe = recipeHolder.value(); + if (resultContainer.setRecipeUsed(level, serverPlayer, recipeHolder)) { + final var assembledStack = recipe.assemble(craftingContainer, level.registryAccess()); + if (assembledStack.isItemEnabled(level.enabledFeatures())) { + itemStack = assembledStack; + } + } + } + + resultContainer.setItem(0, itemStack); + menu.setRemoteSlot(0, itemStack); + serverPlayer.connection.send(new ClientboundContainerSetSlotPacket(menu.containerId, menu.incrementStateId(), 0, itemStack)); + } + } + + @Override + public void slotsChanged(Container container) { + access.execute((level, pos) -> slotChangedCraftingGrid(this, level, player, craftSlots, resultSlots)); + } + + @Override + public void fillCraftSlotsStackedContents(StackedContents stackedContents) { + craftSlots.fillStackedContents(stackedContents); + } + + @Override + public void clearCraftingContent() { + craftSlots.clearContent(); + resultSlots.clearContent(); + } + + @Override + public boolean recipeMatches(RecipeHolder> recipe) { + return recipe.value().matches(craftSlots, player.level()); + } + + @Override + public void removed(Player player) { + super.removed(player); + access.execute((level, pos) -> clearContainer(player, craftSlots)); + } + + @Override + public boolean stillValid(Player player) { + return stillValid(access, player, ModBlocks.cuttingBoard); + } + + @Override + public ItemStack quickMoveStack(Player player, int slotId) { + var itemStack = ItemStack.EMPTY; + final var slot = slots.get(slotId); + if (slot != null && slot.hasItem()) { + final var slotStack = slot.getItem(); + itemStack = slotStack.copy(); + if (slotId == 0) { + this.access.execute(($$2x, $$3x) -> slotStack.getItem().onCraftedBy(slotStack, $$2x, player)); + if (!moveItemStackTo(slotStack, 10, 46, true)) { + return ItemStack.EMPTY; + } + + slot.onQuickCraft(slotStack, itemStack); + } else if (slotId >= 10 && slotId < 46) { + if (!moveItemStackTo(slotStack, 1, 10, false)) { + if (slotId < 37) { + if (!moveItemStackTo(slotStack, 37, 46, false)) { + return ItemStack.EMPTY; + } + } else if (!moveItemStackTo(slotStack, 10, 37, false)) { + return ItemStack.EMPTY; + } + } + } else if (!moveItemStackTo(slotStack, 10, 46, false)) { + return ItemStack.EMPTY; + } + + if (slotStack.isEmpty()) { + slot.setByPlayer(ItemStack.EMPTY); + } else { + slot.setChanged(); + } + + if (slotStack.getCount() == itemStack.getCount()) { + return ItemStack.EMPTY; + } + + slot.onTake(player, slotStack); + if (slotId == 0) { + player.drop(slotStack, false); + } + } + + return itemStack; + } + + @Override + public boolean canTakeItemForPickAll(ItemStack itemStack, Slot slot) { + return slot.container != resultSlots && super.canTakeItemForPickAll(itemStack, slot); + } + + @Override + public int getResultSlotIndex() { + return 0; + } + + @Override + public int getGridWidth() { + return craftSlots.getWidth(); + } + + @Override + public int getGridHeight() { + return craftSlots.getHeight(); + } + + @Override + public int getSize() { + return 10; + } + + @Override + public RecipeBookType getRecipeBookType() { + return RecipeBookType.CRAFTING; + } + + @Override + public boolean shouldMoveToInventory(int slot) { + return slot != getResultSlotIndex(); + } +} diff --git a/common/src/main/java/net/blay09/mods/cookingforblockheads/menu/ModMenus.java b/common/src/main/java/net/blay09/mods/cookingforblockheads/menu/ModMenus.java index 510ede9f..54fb11df 100644 --- a/common/src/main/java/net/blay09/mods/cookingforblockheads/menu/ModMenus.java +++ b/common/src/main/java/net/blay09/mods/cookingforblockheads/menu/ModMenus.java @@ -13,7 +13,9 @@ import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.ExtraCodecs; +import net.minecraft.util.Unit; import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.ContainerLevelAccess; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntity; @@ -32,6 +34,7 @@ public class ModMenus { public static DeferredObject> cookingTable; public static DeferredObject> noFilterBook; public static DeferredObject> craftingBook; + public static DeferredObject> cuttingBoard; public static void initialize(BalmMenus menus) { counter = menus.registerMenu(id("counter"), new BalmMenuFactory() { @@ -151,6 +154,18 @@ public StreamCodec getStreamCodec() { return ItemStack.STREAM_CODEC.cast(); } }); + + cuttingBoard = menus.registerMenu(id("cutting_board"), new BalmMenuFactory() { + @Override + public CuttingBoardMenu create(int windowId, Inventory inventory, BlockPos pos) { + return new CuttingBoardMenu(windowId, inventory, ContainerLevelAccess.create(inventory.player.level(), pos)); + } + + @Override + public StreamCodec getStreamCodec() { + return BlockPos.STREAM_CODEC.cast(); + } + }); } @NotNull diff --git a/common/src/main/resources/assets/cookingforblockheads/lang/en_us.json b/common/src/main/resources/assets/cookingforblockheads/lang/en_us.json index a935dd30..f8a69e48 100644 --- a/common/src/main/resources/assets/cookingforblockheads/lang/en_us.json +++ b/common/src/main/resources/assets/cookingforblockheads/lang/en_us.json @@ -202,6 +202,7 @@ "container.cookingforblockheads.cow_jar_compressed": "Compressed Cow in a Jar", "container.cookingforblockheads.cow_jar": "Cow in a Jar", "container.cookingforblockheads.cow_jar_custom": "%s in a Jar", + "container.cookingforblockheads.cutting_board": "Cutting Board", "itemGroup.cookingforblockheads.cookingforblockheads": "Cooking for Blockheads", "waila.cookingforblockheads.water_stored": "Water Stored: %d/%d", "waila.cookingforblockheads.milk_stored": "Milk Stored: %d/%d",