diff --git a/src/client/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreen.java b/src/client/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreen.java index 1cfed57..80791e3 100644 --- a/src/client/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreen.java +++ b/src/client/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreen.java @@ -8,7 +8,7 @@ import net.minecraft.text.Text; import net.minecraft.util.Identifier; public class MagicalCraftingScreen extends HandledScreen { - private static final Identifier TEXTURE = Identifier.of("textures/gui/container/anvil.png"); + private static final Identifier TEXTURE = Identifier.of("textures/gui/container/dispenser.png"); public MagicalCraftingScreen(MagicalCraftingScreenHandler handler, PlayerInventory inventory, Text title) { super(handler, inventory, title); diff --git a/src/main/java/com/uncreativebunch/magicmod/MagicMod.java b/src/main/java/com/uncreativebunch/magicmod/MagicMod.java index 787bf10..c2c1efb 100644 --- a/src/main/java/com/uncreativebunch/magicmod/MagicMod.java +++ b/src/main/java/com/uncreativebunch/magicmod/MagicMod.java @@ -1,8 +1,10 @@ package com.uncreativebunch.magicmod; import com.uncreativebunch.magicmod.block.ModBlocks; +import com.uncreativebunch.magicmod.block.entity.ModBlockEntityTypes; import com.uncreativebunch.magicmod.item.ModItemGroups; import com.uncreativebunch.magicmod.item.ModItems; +import com.uncreativebunch.magicmod.recipe.ModRecipeTypes; import com.uncreativebunch.magicmod.screen.ModScreenHandlers; import net.fabricmc.api.ModInitializer; @@ -31,6 +33,8 @@ public class MagicMod implements ModInitializer { ModItems.init(); ModItemGroups.init(); ModScreenHandlers.init(); + ModRecipeTypes.init(); + ModBlockEntityTypes.init(); LOGGER.info("Finished Magic Mod initialization!"); } } \ No newline at end of file diff --git a/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java b/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java index af5da16..a36744a 100644 --- a/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java +++ b/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java @@ -1,10 +1,12 @@ package com.uncreativebunch.magicmod.block.crafting; +import com.mojang.serialization.MapCodec; import com.uncreativebunch.magicmod.MagicMod; +import com.uncreativebunch.magicmod.block.entity.MagicalCrafterBlockEntity; import com.uncreativebunch.magicmod.screen.MagicalCraftingScreenHandler; -import net.minecraft.block.AbstractBlock; -import net.minecraft.block.BlockState; -import net.minecraft.block.CraftingTableBlock; +import net.minecraft.block.*; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.enums.SlabType; import net.minecraft.entity.EntityType; import net.minecraft.entity.LightningEntity; import net.minecraft.entity.player.PlayerEntity; @@ -12,19 +14,34 @@ import net.minecraft.screen.NamedScreenHandlerFactory; import net.minecraft.screen.ScreenHandlerContext; import net.minecraft.screen.SimpleNamedScreenHandlerFactory; import net.minecraft.stat.Stats; +import net.minecraft.state.StateManager; import net.minecraft.text.Text; import net.minecraft.util.ActionResult; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -public class MagicalCrafterBlock extends CraftingTableBlock { +public class MagicalCrafterBlock extends BlockWithEntity { + + private static final MapCodec CODEC = null; /** * Creates a new MagicalCrafterBlock. * @param settings The settings of the block. */ - public MagicalCrafterBlock(AbstractBlock.Settings settings) { super(settings); } + public MagicalCrafterBlock(AbstractBlock.Settings settings) { + super(settings); + this.setDefaultState( + this.stateManager.getDefaultState() + .with(SlabBlock.TYPE, SlabType.BOTTOM) + .with(SlabBlock.WATERLOGGED, false) + ); + } + + @Override + protected MapCodec getCodec() { + return CODEC; + } /** * Occurs whenever this block is used. @@ -71,4 +88,18 @@ public class MagicalCrafterBlock extends CraftingTableBlock { Text.translatable("container.magical_crafting_table") ); } + + @Override + public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { + return new MagicalCrafterBlockEntity(pos, state); + } + + /** + * Adds properties to the block state. This crafting table is a slab, so it has a type and waterlogged property. + * @param builder The {@link StateManager.Builder} to add properties to. + */ + @Override + protected void appendProperties(StateManager.Builder builder) { + builder.add(SlabBlock.TYPE, SlabBlock.WATERLOGGED); + } } diff --git a/src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java b/src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java new file mode 100644 index 0000000..2c2c75d --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java @@ -0,0 +1,21 @@ +package com.uncreativebunch.magicmod.block.entity; + +import com.uncreativebunch.magicmod.util.ImplementedInventory; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.collection.DefaultedList; +import net.minecraft.util.math.BlockPos; + +public class MagicalCrafterBlockEntity extends BlockEntity implements ImplementedInventory { + protected DefaultedList items = DefaultedList.ofSize(3, ItemStack.EMPTY); + + @Override + public DefaultedList getItems() { + return items; + } + + public MagicalCrafterBlockEntity(BlockPos pos, BlockState state) { + super(ModBlockEntityTypes.MAGICAL_CRAFTING_TABLE, pos, state); + } +} diff --git a/src/main/java/com/uncreativebunch/magicmod/block/entity/ModBlockEntityTypes.java b/src/main/java/com/uncreativebunch/magicmod/block/entity/ModBlockEntityTypes.java new file mode 100644 index 0000000..7cd0e70 --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/block/entity/ModBlockEntityTypes.java @@ -0,0 +1,22 @@ +package com.uncreativebunch.magicmod.block.entity; + +import com.uncreativebunch.magicmod.block.ModBlocks; +import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; + +public class ModBlockEntityTypes { + + public static final BlockEntityType MAGICAL_CRAFTING_TABLE = register( + "magical_crafting_table", + FabricBlockEntityTypeBuilder.create(MagicalCrafterBlockEntity::new, ModBlocks.MAGICAL_CRAFTING_TABLE).build() + ); + + public static BlockEntityType register(String id, BlockEntityType type) { + return Registry.register(Registries.BLOCK_ENTITY_TYPE, id, type); + } + + public static void init() {} +} diff --git a/src/main/java/com/uncreativebunch/magicmod/datagen/StickTagRecipeProvider.java b/src/main/java/com/uncreativebunch/magicmod/datagen/StickTagRecipeProvider.java index 7c3da6d..7689ba8 100644 --- a/src/main/java/com/uncreativebunch/magicmod/datagen/StickTagRecipeProvider.java +++ b/src/main/java/com/uncreativebunch/magicmod/datagen/StickTagRecipeProvider.java @@ -21,6 +21,16 @@ public class StickTagRecipeProvider extends FabricRecipeProvider { public void generate() { MagicMod.LOGGER.info("Hello from the recipe provider!"); MagicMod.LOGGER.info("This cannot scan recipes - apparently they don't exist in this context."); + + // Create a builder for our custom recipe types. This will allow for type-safe creation of recipes. + // This builder should likely be defined as a static class or function within the main Recipe class. + // RecipeBuilder.create( + // Ingredient.fromTag(ItemTags.MAGIC_MATERIALS), + // Ingredient.fromTag(ItemTags.MAGIC_CORES), + // Ingredient.fromTag(ItemTags.MAGIC_GRIPS), + // ModItems.BASIC_WAND.getDefaultStack(), + // new Identifier("magic_mod", "basic_wand_crafting") + // ); } }; } diff --git a/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedInput.java b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedInput.java new file mode 100644 index 0000000..0ec7ada --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedInput.java @@ -0,0 +1,23 @@ +package com.uncreativebunch.magicmod.recipe; + +import net.minecraft.item.ItemStack; +import net.minecraft.recipe.input.RecipeInput; + +import java.util.List; + +public record MagicShapedInput(List stacks) implements RecipeInput { + @Override + public ItemStack getStackInSlot(int slot) { + return stacks.get(slot); + } + + @Override + public int size() { + return stacks.size(); + } + + @Override + public boolean isEmpty() { + return RecipeInput.super.isEmpty(); + } +} diff --git a/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java new file mode 100644 index 0000000..f829c3f --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java @@ -0,0 +1,158 @@ +package com.uncreativebunch.magicmod.recipe; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.item.ItemStack; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.network.codec.PacketCodecs; +import net.minecraft.recipe.*; +import net.minecraft.recipe.book.RecipeBookCategory; +import net.minecraft.recipe.display.RecipeDisplay; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.util.Identifier; +import net.minecraft.world.World; + +import java.util.ArrayList; +import java.util.List; + +public class MagicShapedRecipe implements Recipe { + private final Ingredient inputA; + private final Ingredient inputB; + private final ItemStack output; + private final Identifier id; + + public MagicShapedRecipe(Ingredient inputA, Ingredient inputB, ItemStack output, Identifier id) { + this.inputA = inputA; + this.inputB = inputB; + this.output = output; + this.id = id; + } + + public Ingredient getInputA() { + return this.inputA; + } + + public Ingredient getInputB() { + return inputB; + } + + public ItemStack getOutput() { + return this.output; + } + + public Identifier getId() { + return this.id; + } + + public boolean fits(int width, int height) { + return true; + } + + @Override + public boolean matches(MagicShapedInput input, World world) { + if (input.stacks().size() < 2) return false; + return inputA.test(input.stacks().getFirst()) && inputB.test(input.stacks().get(1)); + } + + @Override + public ItemStack craft(MagicShapedInput input, RegistryWrapper.WrapperLookup registries) { + return this.output.copy(); + } + + @Override + public boolean isIgnoredInRecipeBook() { + return Recipe.super.isIgnoredInRecipeBook(); + } + + @Override + public boolean showNotification() { + return Recipe.super.showNotification(); + } + + @Override + public String getGroup() { + return Recipe.super.getGroup(); + } + + @Override + public RecipeSerializer> getSerializer() { + return null; + } + + // Implement the recipe's type. + public static class Type implements RecipeType { + private Type() { } + public static final Type INSTANCE = new Type(); + public static final String ID = "magic_crafting"; + } + + /** + * {@return the type of this recipe} + * + *

The {@code type} in the recipe JSON format is the {@linkplain + * #getSerializer() serializer} instead. + */ + @Override + public RecipeType> getType() { + return ModRecipeTypes.MAGIC_SHAPED; + } + + @Override + public IngredientPlacement getIngredientPlacement() { + return null; + } + + @Override + public List getDisplays() { + return Recipe.super.getDisplays(); + } + + @Override + public RecipeBookCategory getRecipeBookCategory() { + return null; + } + + // Serializer definition + public static class Serializer implements RecipeSerializer { + public static final Serializer INSTANCE = new Serializer(); + + private static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + // This function essentially creates a way to deserialize from a JSON using the constructor's + // parameters, as defined above. + instance -> instance.group( + Ingredient.CODEC.fieldOf("inputA").forGetter(MagicShapedRecipe::getInputA), + Ingredient.CODEC.fieldOf("inputB").forGetter(MagicShapedRecipe::getInputB), + ItemStack.CODEC.fieldOf("output").forGetter(MagicShapedRecipe::getOutput), + Identifier.CODEC.fieldOf("id").forGetter(MagicShapedRecipe::getId) + ).apply(instance, MagicShapedRecipe::new) + ); + + private static final PacketCodec PACKET_CODEC = PacketCodec.tuple( + Ingredient.PACKET_CODEC, MagicShapedRecipe::getInputA, + Ingredient.PACKET_CODEC, MagicShapedRecipe::getInputB, + ItemStack.PACKET_CODEC, MagicShapedRecipe::getOutput, + Identifier.PACKET_CODEC, MagicShapedRecipe::getId, + MagicShapedRecipe::new + ); + + /** + * Gets the codec for this serializer. This determines how the recipe is parsed from JSON. + * @return The codec for this serializer. + */ + @Override + public MapCodec codec() { + return CODEC; + } + + /** + * Gets the packet codec for this serializer. This determines how the recipe is parsed from packets. + * @return The packet codec for this serializer. + */ + @Override + public PacketCodec packetCodec() { + return PACKET_CODEC; + } + } +} diff --git a/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeSerializers.java b/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeSerializers.java new file mode 100644 index 0000000..564ff2f --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeSerializers.java @@ -0,0 +1,16 @@ +package com.uncreativebunch.magicmod.recipe; + +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.RecipeSerializer; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; + +public class ModRecipeSerializers { + public static RecipeSerializer MAGIC_SHAPED = new MagicShapedRecipe.Serializer(); + + public static > RecipeSerializer register(String id, RecipeSerializer type) { + return Registry.register(Registries.RECIPE_SERIALIZER, id, type); + } + + public static void init() {} +} diff --git a/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeTypes.java b/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeTypes.java new file mode 100644 index 0000000..15fc56e --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeTypes.java @@ -0,0 +1,16 @@ +package com.uncreativebunch.magicmod.recipe; + +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.RecipeType; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; + +public class ModRecipeTypes { + public static final RecipeType MAGIC_SHAPED = register("magic_shaped", MagicShapedRecipe.Type.INSTANCE); + + public static > RecipeType register(String id, RecipeType type) { + return Registry.register(Registries.RECIPE_TYPE, id, type); + } + + public static void init() {} +} diff --git a/src/main/java/com/uncreativebunch/magicmod/util/ImplementedInventory.java b/src/main/java/com/uncreativebunch/magicmod/util/ImplementedInventory.java new file mode 100644 index 0000000..b594e5a --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/util/ImplementedInventory.java @@ -0,0 +1,77 @@ +package com.uncreativebunch.magicmod.util; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventories; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.collection.DefaultedList; + +public interface ImplementedInventory extends Inventory { + DefaultedList getItems(); + + static ImplementedInventory of(DefaultedList items) { + return () -> items; + } + + static ImplementedInventory ofSize(int size) { + return of(DefaultedList.ofSize(size, ItemStack.EMPTY)); + } + + @Override + default int size() { + return getItems().size(); + } + + @Override + default boolean isEmpty() { + for (int i = 0; i < size(); i++) { + ItemStack stack = getStack(i); + if (!stack.isEmpty()) { + return false; + } + } + return true; + } + + @Override + default ItemStack getStack(int slot) { + return getItems().get(slot); + } + + @Override + default ItemStack removeStack(int slot, int count) { + ItemStack result = Inventories.splitStack(getItems(), slot, count); + if (!result.isEmpty()) { + markDirty(); + } + return result; + } + + @Override + default ItemStack removeStack(int slot) { + return Inventories.removeStack(getItems(), slot); + } + + @Override + default void setStack(int slot, ItemStack stack) { + getItems().set(slot, stack); + if (stack.getCount() > stack.getMaxCount()) { + stack.setCount(stack.getMaxCount()); + } + } + + @Override + default void clear() { + getItems().clear(); + } + + @Override + default void markDirty() { + // Doesn't need to be overridden in this case. + } + + @Override + default boolean canPlayerUse(PlayerEntity player) { + return true; + } +}