feat: build crafting type and block entity, pc change

This commit is contained in:
alphafetus committed 2025-09-05 07:31:39 -04:00
1 parent 6e224e1fdf
commit ce1bd9052f
11 files changed
+384 -6

No files matched your search

@@ -8,7 +8,7 @@ import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
public class MagicalCraftingScreen extends HandledScreen<MagicalCraftingScreenHandler> {
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);
@@ -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!");
}
}
@@ -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<MagicalCrafterBlock> 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<? extends BlockWithEntity> 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<Block, BlockState> builder) {
builder.add(SlabBlock.TYPE, SlabBlock.WATERLOGGED);
}
}
@@ -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<ItemStack> items = DefaultedList.ofSize(3, ItemStack.EMPTY);
@Override
public DefaultedList<ItemStack> getItems() {
return items;
}
public MagicalCrafterBlockEntity(BlockPos pos, BlockState state) {
super(ModBlockEntityTypes.MAGICAL_CRAFTING_TABLE, pos, state);
}
}
@@ -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<MagicalCrafterBlockEntity> MAGICAL_CRAFTING_TABLE = register(
"magical_crafting_table",
FabricBlockEntityTypeBuilder.create(MagicalCrafterBlockEntity::new, ModBlocks.MAGICAL_CRAFTING_TABLE).build()
);
public static <T extends BlockEntity> BlockEntityType<T> register(String id, BlockEntityType<T> type) {
return Registry.register(Registries.BLOCK_ENTITY_TYPE, id, type);
}
public static void init() {}
}
@@ -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")
// );
}
};
}
@@ -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<ItemStack> 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();
}
}
@@ -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<MagicShapedInput> {
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<? extends Recipe<MagicShapedInput>> getSerializer() {
return null;
}
// Implement the recipe's type.
public static class Type implements RecipeType<MagicShapedRecipe> {
private Type() { }
public static final Type INSTANCE = new Type();
public static final String ID = "magic_crafting";
}
/**
* {@return the type of this recipe}
*
* <p>The {@code type} in the recipe JSON format is the {@linkplain
* #getSerializer() serializer} instead.
*/
@Override
public RecipeType<? extends Recipe<MagicShapedInput>> getType() {
return ModRecipeTypes.MAGIC_SHAPED;
}
@Override
public IngredientPlacement getIngredientPlacement() {
return null;
}
@Override
public List<RecipeDisplay> getDisplays() {
return Recipe.super.getDisplays();
}
@Override
public RecipeBookCategory getRecipeBookCategory() {
return null;
}
// Serializer definition
public static class Serializer implements RecipeSerializer<MagicShapedRecipe> {
public static final Serializer INSTANCE = new Serializer();
private static final MapCodec<MagicShapedRecipe> 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<RegistryByteBuf, MagicShapedRecipe> 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<MagicShapedRecipe> 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<RegistryByteBuf, MagicShapedRecipe> packetCodec() {
return PACKET_CODEC;
}
}
}
@@ -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<MagicShapedRecipe> MAGIC_SHAPED = new MagicShapedRecipe.Serializer();
public static <T extends Recipe<?>> RecipeSerializer<T> register(String id, RecipeSerializer<T> type) {
return Registry.register(Registries.RECIPE_SERIALIZER, id, type);
}
public static void init() {}
}
@@ -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<MagicShapedRecipe> MAGIC_SHAPED = register("magic_shaped", MagicShapedRecipe.Type.INSTANCE);
public static <T extends Recipe<?>> RecipeType<T> register(String id, RecipeType<T> type) {
return Registry.register(Registries.RECIPE_TYPE, id, type);
}
public static void init() {}
}
@@ -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<ItemStack> getItems();
static ImplementedInventory of(DefaultedList<ItemStack> 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;
}
}