Basic crafting recipe example #3
No files matched your search
+2
-1
@@ -6,7 +6,7 @@ org.gradle.parallel=true
|
||||
# check these on https://fabricmc.net/develop
|
||||
minecraft_version=1.21.5
|
||||
yarn_mappings=1.21.5+build.1
|
||||
loader_version=0.16.12
|
||||
loader_version=0.17.2
|
||||
loom_version=1.11-SNAPSHOT
|
||||
|
||||
# Mod Properties
|
||||
@@ -15,4 +15,5 @@ maven_group=com.uncreativebunch.magicmod
|
||||
archives_base_name=magic_mod
|
||||
|
||||
# Dependencies
|
||||
# Fabric API
|
||||
fabric_version=0.128.2+1.21.5
|
||||
@@ -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,11 @@
|
||||
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.ModRecipeSerializers;
|
||||
import com.uncreativebunch.magicmod.recipe.ModRecipeTypes;
|
||||
import com.uncreativebunch.magicmod.screen.ModScreenHandlers;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
|
||||
@@ -31,6 +34,9 @@ public class MagicMod implements ModInitializer {
|
||||
ModItems.init();
|
||||
ModItemGroups.init();
|
||||
ModScreenHandlers.init();
|
||||
ModRecipeSerializers.init();
|
||||
ModRecipeTypes.init();
|
||||
ModBlockEntityTypes.init();
|
||||
LOGGER.info("Finished Magic Mod initialization!");
|
||||
}
|
||||
}
|
||||
+50
-24
@@ -1,30 +1,44 @@
|
||||
package com.uncreativebunch.magicmod.block.crafting;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.uncreativebunch.magicmod.MagicMod;
|
||||
import com.uncreativebunch.magicmod.screen.MagicalCraftingScreenHandler;
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.CraftingTableBlock;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.LightningEntity;
|
||||
import com.uncreativebunch.magicmod.block.entity.MagicalCrafterBlockEntity;
|
||||
import com.uncreativebunch.magicmod.block.entity.ModBlockEntityTypes;
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.BlockEntityTicker;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.block.enums.SlabType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.screen.NamedScreenHandlerFactory;
|
||||
import net.minecraft.screen.ScreenHandlerContext;
|
||||
import net.minecraft.screen.SimpleNamedScreenHandlerFactory;
|
||||
import net.minecraft.stat.Stats;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.state.StateManager;
|
||||
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.
|
||||
@@ -44,13 +58,6 @@ public class MagicalCrafterBlock extends CraftingTableBlock {
|
||||
if (!world.isClient) {
|
||||
MagicMod.LOGGER.info("Block was used at location {}", pos.toShortString());
|
||||
|
||||
MagicMod.LOGGER.info("Electrocuting player...");
|
||||
BlockPos playerPos = player.getBlockPos();
|
||||
LightningEntity lightningEntity = new LightningEntity(EntityType.LIGHTNING_BOLT, world);
|
||||
lightningEntity.setPosition(playerPos.toCenterPos());
|
||||
world.spawnEntity(lightningEntity);
|
||||
MagicMod.LOGGER.info("Player electrocuted.");
|
||||
|
||||
NamedScreenHandlerFactory screenFactory = state.createScreenHandlerFactory(world, pos);
|
||||
if (screenFactory != null) {
|
||||
player.openHandledScreen(screenFactory);
|
||||
@@ -64,11 +71,30 @@ public class MagicalCrafterBlock extends CraftingTableBlock {
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedScreenHandlerFactory createScreenHandlerFactory(BlockState state, World world, BlockPos pos) {
|
||||
return new SimpleNamedScreenHandlerFactory(
|
||||
((syncId, playerInventory, player) ->
|
||||
new MagicalCraftingScreenHandler(syncId, playerInventory, ScreenHandlerContext.create(world, pos))),
|
||||
Text.translatable("container.magical_crafting_table")
|
||||
);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link BlockEntityTicker} that runs the specified "tick" function from the
|
||||
* given {@link BlockEntityType}.
|
||||
* @param world The {@link World} instance.
|
||||
* @param state The {@link BlockState} of the block.
|
||||
* @param type The {@link BlockEntityType} to run the tick function from.
|
||||
* @return A new {@link BlockEntityTicker} that runs the specified tick function.
|
||||
* @param <T> The {@link BlockEntity} to use.
|
||||
*/
|
||||
@Override
|
||||
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(World world, BlockState state, BlockEntityType<T> type) {
|
||||
return validateTicker(type, ModBlockEntityTypes.MAGICAL_CRAFTING_TABLE, MagicalCrafterBlockEntity::tick);
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
package com.uncreativebunch.magicmod.block.entity;
|
||||
|
||||
import com.uncreativebunch.magicmod.inventory.InputInventory;
|
||||
import com.uncreativebunch.magicmod.inventory.OutputInventory;
|
||||
import com.uncreativebunch.magicmod.recipe.MagicShapedInput;
|
||||
import com.uncreativebunch.magicmod.recipe.ModRecipeTypes;
|
||||
import com.uncreativebunch.magicmod.recipe.MagicShapedRecipe;
|
||||
import com.uncreativebunch.magicmod.screen.MagicalCraftingScreenHandler;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.recipe.RecipeEntry;
|
||||
import net.minecraft.recipe.ServerRecipeManager;
|
||||
import net.minecraft.screen.NamedScreenHandlerFactory;
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class MagicalCrafterBlockEntity extends BlockEntity implements NamedScreenHandlerFactory {
|
||||
private final ServerRecipeManager.MatchGetter<MagicShapedInput, ? extends MagicShapedRecipe> recipeGetter;
|
||||
|
||||
/**
|
||||
* The input inventory for crafting ingredients. It goes in the order core, material, and grip.
|
||||
*/
|
||||
private final InputInventory inputInventory = new InputInventory(3);
|
||||
/**
|
||||
* The output inventory for crafting ingredients.
|
||||
*/
|
||||
private final OutputInventory outputInventory = new OutputInventory(1);
|
||||
|
||||
public MagicalCrafterBlockEntity(BlockPos pos, BlockState state) {
|
||||
super(ModBlockEntityTypes.MAGICAL_CRAFTING_TABLE, pos, state);
|
||||
this.recipeGetter = ServerRecipeManager.createCachedMatchGetter(ModRecipeTypes.MAGIC_SHAPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the input inventory for this block entity.
|
||||
* @return A {@link InputInventory} that represents the input inventory.
|
||||
*/
|
||||
public InputInventory getInputInventory() { return inputInventory; }
|
||||
|
||||
/**
|
||||
* Gets the output inventory for this block entity.
|
||||
* @return An {@link OutputInventory} that represents the output slot.
|
||||
*/
|
||||
public OutputInventory getOutputInventory() { return outputInventory; }
|
||||
|
||||
/**
|
||||
* The ticking logic. This will check if the block's inventory contains the correct items to craft
|
||||
* a {@link MagicShapedRecipe}.
|
||||
* @param world The {@link World} instance.
|
||||
* @param pos The {@link BlockPos} positional data.
|
||||
* @param state The {@link BlockState} of the block.
|
||||
* @param blockEntity An instance of this type.
|
||||
*/
|
||||
public static void tick(World world, BlockPos pos, BlockState state, MagicalCrafterBlockEntity blockEntity) {
|
||||
// Check if the recipe within the crafting inventory matches any recipes. Must happen on server side.
|
||||
if (world instanceof ServerWorld serverWorld) {
|
||||
Optional<? extends RecipeEntry<? extends MagicShapedRecipe>> matched = blockEntity.recipeGetter
|
||||
.getFirstMatch(
|
||||
new MagicShapedInput(blockEntity.inputInventory.getHeldStacks()),
|
||||
serverWorld
|
||||
);
|
||||
|
||||
if (matched.isPresent()) {
|
||||
// Work with the RecipeEntry here
|
||||
MagicShapedRecipe entry = matched.get().value();
|
||||
blockEntity.outputInventory.heldStacks.set(0, entry.output());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player) {
|
||||
return new MagicalCraftingScreenHandler(syncId, playerInventory, inputInventory, outputInventory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Text getDisplayName() {
|
||||
return Text.translatable(getCachedState().getBlock().getTranslationKey());
|
||||
}
|
||||
}
|
||||
@@ -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() {}
|
||||
}
|
||||
@@ -13,6 +13,6 @@ public class DataGenerator implements DataGeneratorEntrypoint {
|
||||
MagicMod.LOGGER.info("Hello from the data generator!");
|
||||
|
||||
// Add providers to the pack here.
|
||||
pack.addProvider(StickTagRecipeProvider::new);
|
||||
pack.addProvider(MagicRecipeProvider::new);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.uncreativebunch.magicmod.datagen;
|
||||
|
||||
import com.uncreativebunch.magicmod.recipe.MagicShapedRecipe;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.data.recipe.RecipeExporter;
|
||||
import net.minecraft.data.recipe.RecipeGenerator;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.recipe.Ingredient;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.RegistryWrapper;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class MagicRecipeProvider extends FabricRecipeProvider {
|
||||
public MagicRecipeProvider(FabricDataOutput output, CompletableFuture<RegistryWrapper.WrapperLookup> registriesFuture) {
|
||||
super(output, registriesFuture);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecipeGenerator getRecipeGenerator(RegistryWrapper.WrapperLookup registryLookup, RecipeExporter exporter) {
|
||||
return new RecipeGenerator(registryLookup, exporter) {
|
||||
@Override
|
||||
public void generate() {
|
||||
RegistryWrapper.Impl<Item> itemLookup = registries.getOrThrow(RegistryKeys.ITEM);
|
||||
|
||||
// Recipes are added here. We only want to create MagicShapedRecipes here.
|
||||
MagicShapedRecipe.Builder.build(
|
||||
Ingredient.ofItem(Items.STICK),
|
||||
Ingredient.ofItem(Items.STICK),
|
||||
Ingredient.ofItem(Items.STICK),
|
||||
Items.REDSTONE.getDefaultStack()
|
||||
).offerTo(exporter, Identifier.of("magic_mod", "magic_testing_recipe"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "MagicRecipeProvider";
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.uncreativebunch.magicmod.datagen;
|
||||
|
||||
import com.uncreativebunch.magicmod.MagicMod;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.data.recipe.RecipeExporter;
|
||||
import net.minecraft.data.recipe.RecipeGenerator;
|
||||
import net.minecraft.registry.RegistryWrapper;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class StickTagRecipeProvider extends FabricRecipeProvider {
|
||||
public StickTagRecipeProvider(FabricDataOutput output, CompletableFuture<RegistryWrapper.WrapperLookup> registriesFuture) {
|
||||
super(output, registriesFuture);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecipeGenerator getRecipeGenerator(RegistryWrapper.WrapperLookup lookup, RecipeExporter exporter) {
|
||||
return new RecipeGenerator(lookup, exporter) {
|
||||
@Override
|
||||
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.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "StickTagRecipeProvider";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.uncreativebunch.magicmod.inventory;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.uncreativebunch.magicmod.inventory;
|
||||
|
||||
import net.minecraft.inventory.SimpleInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
public class InputInventory extends SimpleInventory {
|
||||
public InputInventory(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInsert(ItemStack stack) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes basic crafting logic and decrements all input stacks.
|
||||
*/
|
||||
public void onCrafted() {
|
||||
for (var stack : this) {
|
||||
stack.decrement(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.uncreativebunch.magicmod.inventory;
|
||||
|
||||
import net.minecraft.inventory.SimpleInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.collection.DefaultedList;
|
||||
|
||||
public class OutputInventory extends SimpleInventory {
|
||||
|
||||
public OutputInventory(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
public OutputInventory(ItemStack... items) {
|
||||
super(items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canInsert(ItemStack stack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canAddStack(ItemStack stack) {
|
||||
return super.canInsert(stack);
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ public class ModItems {
|
||||
Item::new,
|
||||
new Item.Settings()
|
||||
);
|
||||
|
||||
|
||||
public static RegistryKey<Item> keyOf(String id) {
|
||||
return RegistryKey.of(RegistryKeys.ITEM, Identifier.of(MagicMod.MOD_ID, id));
|
||||
}
|
||||
|
||||
@@ -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 index) {
|
||||
return stacks.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return stacks.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return RecipeInput.super.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package com.uncreativebunch.magicmod.recipe;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import com.uncreativebunch.magicmod.MagicMod;
|
||||
import net.minecraft.data.recipe.RecipeExporter;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.RegistryByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.recipe.*;
|
||||
import net.minecraft.recipe.book.RecipeBookCategory;
|
||||
import net.minecraft.recipe.display.RecipeDisplay;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.RegistryWrapper;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public record MagicShapedRecipe(Ingredient coreIngredient, Ingredient materialIngredient, Ingredient gripIngredient,
|
||||
ItemStack output, Identifier id) implements Recipe<MagicShapedInput> {
|
||||
@Override
|
||||
public boolean matches(MagicShapedInput input, World world) {
|
||||
if (input.stacks().size() < 2) return false;
|
||||
return coreIngredient.test(input.stacks().getFirst())
|
||||
&& materialIngredient.test(input.stacks().get(1))
|
||||
&& gripIngredient.test(input.stacks().get(2));
|
||||
}
|
||||
|
||||
@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 ModRecipeSerializers.MAGIC_SHAPED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeType<? extends Recipe<MagicShapedInput>> getType() {
|
||||
return ModRecipeTypes.MAGIC_SHAPED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RecipeDisplay> getDisplays() {
|
||||
return Recipe.super.getDisplays();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeBookCategory getRecipeBookCategory() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IngredientPlacement getIngredientPlacement() {
|
||||
List<Optional<Ingredient>> ingredients = List.of(
|
||||
Optional.of(coreIngredient),
|
||||
Optional.of(materialIngredient),
|
||||
Optional.of(gripIngredient)
|
||||
);
|
||||
|
||||
return IngredientPlacement.forMultipleSlots(ingredients);
|
||||
}
|
||||
|
||||
// Type definition
|
||||
public static class Type implements RecipeType<MagicShapedRecipe> {
|
||||
private Type() { }
|
||||
|
||||
public static final Type INSTANCE = new Type();
|
||||
public static final String ID = "magic_crafting";
|
||||
}
|
||||
|
||||
// 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("coreIngredient").forGetter(MagicShapedRecipe::coreIngredient),
|
||||
Ingredient.CODEC.fieldOf("materialIngredient").forGetter(MagicShapedRecipe::materialIngredient),
|
||||
Ingredient.CODEC.fieldOf("gripIngredient").forGetter(MagicShapedRecipe::gripIngredient),
|
||||
ItemStack.CODEC.fieldOf("output").forGetter(MagicShapedRecipe::output),
|
||||
Identifier.CODEC.fieldOf("id").forGetter(MagicShapedRecipe::id)
|
||||
).apply(instance, MagicShapedRecipe::new)
|
||||
);
|
||||
|
||||
private static final PacketCodec<RegistryByteBuf, MagicShapedRecipe> PACKET_CODEC = PacketCodec.tuple(
|
||||
Ingredient.PACKET_CODEC, MagicShapedRecipe::coreIngredient,
|
||||
Ingredient.PACKET_CODEC, MagicShapedRecipe::materialIngredient,
|
||||
Ingredient.PACKET_CODEC, MagicShapedRecipe::gripIngredient,
|
||||
ItemStack.PACKET_CODEC, MagicShapedRecipe::output,
|
||||
Identifier.PACKET_CODEC, MagicShapedRecipe::id,
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public PacketCodec<RegistryByteBuf, MagicShapedRecipe> packetCodec() {
|
||||
return PACKET_CODEC;
|
||||
}
|
||||
}
|
||||
|
||||
// Builder definition
|
||||
public record Builder(Ingredient core, Ingredient material, Ingredient grip, ItemStack output) {
|
||||
/**
|
||||
* Creates a new {@link Builder}.
|
||||
* @param core The core {@link Ingredient}.
|
||||
* @param material The material {@link Ingredient}.
|
||||
* @param grip The grip {@link Ingredient}.
|
||||
* @param output The output {@link ItemStack}
|
||||
* @return A new {@link Builder} that can be offered to a {@link RecipeExporter}.
|
||||
*/
|
||||
public static Builder build(
|
||||
Ingredient core,
|
||||
Ingredient material,
|
||||
Ingredient grip,
|
||||
ItemStack output
|
||||
) {
|
||||
return new Builder(core, material, grip, output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offers the contents of this builder to the given {@link RecipeExporter}.
|
||||
* @param exporter The {@link RecipeExporter} to use.
|
||||
* @param recipeId The {@link Identifier} of the recipe to create.
|
||||
*/
|
||||
public void offerTo(RecipeExporter exporter, Identifier recipeId) {
|
||||
RegistryKey<Recipe<?>> key = RegistryKey.of(RegistryKeys.RECIPE, recipeId);
|
||||
MagicShapedRecipe recipe = new MagicShapedRecipe(core, material, grip, output, recipeId);
|
||||
exporter.accept(key, recipe, null);
|
||||
MagicMod.LOGGER.info("Created recipe with id {}", recipeId.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.uncreativebunch.magicmod.recipe;
|
||||
|
||||
import com.uncreativebunch.magicmod.MagicMod;
|
||||
import net.minecraft.recipe.Recipe;
|
||||
import net.minecraft.recipe.RecipeSerializer;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class ModRecipeSerializers {
|
||||
public static RecipeSerializer<MagicShapedRecipe> MAGIC_SHAPED = register(
|
||||
"magic_shaped",
|
||||
MagicShapedRecipe.Serializer.INSTANCE
|
||||
);
|
||||
|
||||
/**
|
||||
* Registers a {@link RecipeSerializer} with Minecraft's internal registry.
|
||||
* @param id The {@link net.minecraft.util.Identifier} of the serializer.
|
||||
* @param type An instance of the {@link RecipeSerializer} to register.
|
||||
* @return The registered instance of the {@link RecipeSerializer}.
|
||||
* @param <T> The type of the {@link Recipe} that is serialized.
|
||||
*/
|
||||
public static <T extends Recipe<?>> RecipeSerializer<T> register(String id, RecipeSerializer<T> type) {
|
||||
return Registry.register(Registries.RECIPE_SERIALIZER, Identifier.of(MagicMod.MOD_ID, id), type);
|
||||
}
|
||||
|
||||
public static void init() {}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.uncreativebunch.magicmod.recipe;
|
||||
|
||||
import com.uncreativebunch.magicmod.MagicMod;
|
||||
import net.minecraft.recipe.Recipe;
|
||||
import net.minecraft.recipe.RecipeType;
|
||||
import net.minecraft.recipe.input.RecipeInput;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
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, Identifier.of(MagicMod.MOD_ID, id), type);
|
||||
}
|
||||
|
||||
public static void init() {}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.uncreativebunch.magicmod.screen;
|
||||
|
||||
import com.uncreativebunch.magicmod.inventory.InputInventory;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
|
||||
public class MagicResultSlot extends Slot {
|
||||
/**
|
||||
* The crafting input inventory. There is not a current need for custom logic, but this may come in the future.
|
||||
*/
|
||||
protected InputInventory inputInventory;
|
||||
|
||||
public MagicResultSlot(InputInventory inputInventory, Inventory outputInventory, int index, int x, int y) {
|
||||
super(outputInventory, index, x, y);
|
||||
this.inputInventory = inputInventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs when the player takes the item out of the slot.
|
||||
* This slot will run the crafting logic of the {@link InputInventory}.
|
||||
* @param player The player that took the item.
|
||||
* @param stack The item that was taken.
|
||||
*/
|
||||
@Override
|
||||
public void onTakeItem(PlayerEntity player, ItemStack stack) {
|
||||
this.inputInventory.onCrafted();
|
||||
|
||||
super.onTakeItem(player, stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false, as the result slot does not allow items to be inserted.
|
||||
* @param stack The item to insert.
|
||||
* @return False.
|
||||
*/
|
||||
@Override
|
||||
public boolean canInsert(ItemStack stack) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+26
-20
@@ -1,17 +1,17 @@
|
||||
package com.uncreativebunch.magicmod.screen;
|
||||
|
||||
import com.uncreativebunch.magicmod.inventory.InputInventory;
|
||||
import com.uncreativebunch.magicmod.inventory.OutputInventory;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.inventory.CraftingInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.screen.ScreenHandlerContext;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
|
||||
public class MagicalCraftingScreenHandler extends ScreenHandler {
|
||||
|
||||
private final ScreenHandlerContext context;
|
||||
private final CraftingInventory inventory;
|
||||
private final InputInventory inputInventory;
|
||||
private final OutputInventory outputInventory;
|
||||
|
||||
/**
|
||||
* Client-side constructor.
|
||||
@@ -19,7 +19,7 @@ public class MagicalCraftingScreenHandler extends ScreenHandler {
|
||||
* @param playerInventory The player's inventory.
|
||||
*/
|
||||
public MagicalCraftingScreenHandler(int syncId, PlayerInventory playerInventory) {
|
||||
this(syncId, playerInventory, ScreenHandlerContext.EMPTY);
|
||||
this(syncId, playerInventory, new InputInventory(3), new OutputInventory(1));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,30 +27,36 @@ public class MagicalCraftingScreenHandler extends ScreenHandler {
|
||||
* It handles creating the inventory slots and syncs all information from the server to the client.
|
||||
* @param syncId The sync ID that's sent from the server.
|
||||
* @param playerInventory The player's inventory.
|
||||
* @param screenHandlerContext The context of the screen handler. Not sure if this is needed anymore.
|
||||
* @param inputInventory The crafting inventory.
|
||||
* @param outputInventory The output inventory.
|
||||
*/
|
||||
public MagicalCraftingScreenHandler(int syncId, PlayerInventory playerInventory, ScreenHandlerContext screenHandlerContext) {
|
||||
public MagicalCraftingScreenHandler(int syncId, PlayerInventory playerInventory, InputInventory inputInventory,
|
||||
OutputInventory outputInventory) {
|
||||
super(ModScreenHandlers.MAGICAL_CRAFTING_SCREEN_HANDLER, syncId);
|
||||
this.context = screenHandlerContext;
|
||||
this.inventory = new CraftingInventory(this, 3, 3);
|
||||
this.inputInventory = inputInventory;
|
||||
this.outputInventory = outputInventory;
|
||||
|
||||
// Ensure that the crafting inventory is the correct size.
|
||||
checkSize(inventory, 9);
|
||||
checkSize(inputInventory, 3);
|
||||
checkSize(outputInventory, 1);
|
||||
|
||||
// Ensure that the inventory's custom logic runs (if any)
|
||||
inventory.onOpen(playerInventory.player);
|
||||
inputInventory.onOpen(playerInventory.player);
|
||||
outputInventory.onOpen(playerInventory.player);
|
||||
|
||||
// Place all inventory slots within the screen. This piece doesn't actually render the background of the slots,
|
||||
// as this is handled by the client-side application (the Screen), but it does tell the screen where to render.
|
||||
// We do assign locations - each slot is 18 units wide/tall.
|
||||
int m, l;
|
||||
for (m = 0; m < 3; m++) {
|
||||
for (l = 0; l < 3; l++) {
|
||||
// Create the crafting slots. This is good for a 3x3 grid of crafting slots. It registers them to a
|
||||
// specific inventory and location on the client's screen, relative to the screen's size and origin.
|
||||
this.addSlot(new Slot(inventory, l + m * 3, 62 + l * 18, 17 + m * 18));
|
||||
}
|
||||
for (l = 0; l < 3; l++) {
|
||||
// Create the crafting slots. This is good for a 3x3 grid of crafting slots. It registers them to a
|
||||
// specific inventory and location on the client's screen, relative to the screen's size and origin.
|
||||
this.addSlot(new Slot(inputInventory, l, 62 + l * 18, 17));
|
||||
}
|
||||
|
||||
// TODO I don't actually know where the output slot should be located. Get correct coords.
|
||||
this.addSlot(new MagicResultSlot(inputInventory, outputInventory, 0, 80, 35));
|
||||
|
||||
// Add all player inventory slots. Start with the main inventory, then the hotbar.
|
||||
for (m = 0; m < 3; m++) {
|
||||
for (l = 0; l < 9; l++) {
|
||||
@@ -68,7 +74,7 @@ public class MagicalCraftingScreenHandler extends ScreenHandler {
|
||||
* @return True if the slot index is within the crafting inventory, false otherwise.
|
||||
*/
|
||||
protected boolean isCraftingInventorySlot(int slotIndex) {
|
||||
return slotIndex < this.inventory.size();
|
||||
return slotIndex < this.inputInventory.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,7 +89,7 @@ public class MagicalCraftingScreenHandler extends ScreenHandler {
|
||||
newStack = stack.copy();
|
||||
if (isCraftingInventorySlot(slotIndex)) {
|
||||
// Attempt to move the stack into the player's inventory.
|
||||
if (!this.insertItem(stack, this.inventory.size(), this.slots.size(), true)) {
|
||||
if (!this.insertItem(stack, this.inputInventory.size() + this.outputInventory.size(), this.slots.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else {
|
||||
@@ -104,6 +110,6 @@ public class MagicalCraftingScreenHandler extends ScreenHandler {
|
||||
|
||||
@Override
|
||||
public boolean canUse(PlayerEntity player) {
|
||||
return inventory.canPlayerUse(player);
|
||||
return inputInventory.canPlayerUse(player);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user