Skip to content

Commit 5f162d4

Browse files
feat: created custom button crafting
1 parent b8c1841 commit 5f162d4

18 files changed

Lines changed: 556 additions & 173 deletions
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
package com.tcm.MineTale;
22

33
import com.tcm.MineTale.block.workbenches.screen.FurnaceWorkbenchScreen;
4+
import com.tcm.MineTale.block.workbenches.screen.WorkbenchWorkbenchScreen;
45
import com.tcm.MineTale.block.workbenches.screen.CampfireWorkbenchScreen;
56
import com.tcm.MineTale.registry.ModMenuTypes;
67

78
import net.fabricmc.api.ClientModInitializer;
89
import net.minecraft.client.gui.screens.MenuScreens;
910

1011
public class MineTaleClient implements ClientModInitializer {
11-
12-
13-
1412
/**
1513
* Registers client-side screen factories for custom workbench menu types.
1614
*
@@ -21,5 +19,6 @@ public class MineTaleClient implements ClientModInitializer {
2119
public void onInitializeClient() {
2220
MenuScreens.register(ModMenuTypes.FURNACE_WORKBENCH_MENU, FurnaceWorkbenchScreen::new);
2321
MenuScreens.register(ModMenuTypes.CAMPFIRE_WORKBENCH_MENU, CampfireWorkbenchScreen::new);
22+
MenuScreens.register(ModMenuTypes.WORKBENCH_WORKBENCH_MENU, WorkbenchWorkbenchScreen::new);
2423
}
2524
}

src/client/java/com/tcm/MineTale/block/workbenches/screen/WorkbenchWorkbenchScreen.java

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,40 @@
44

55
import com.tcm.MineTale.MineTale;
66
import com.tcm.MineTale.block.workbenches.menu.WorkbenchWorkbenchMenu;
7+
import com.tcm.MineTale.mixin.client.RecipeBookComponentAccessor;
8+
import com.tcm.MineTale.network.CraftRequestPayload;
79
import com.tcm.MineTale.recipe.MineTaleRecipeBookComponent;
810
import com.tcm.MineTale.registry.ModBlocks;
911
import com.tcm.MineTale.registry.ModRecipeDisplay;
1012
import com.tcm.MineTale.registry.ModRecipes;
1113

14+
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
1215
import net.minecraft.client.gui.GuiGraphics;
16+
import net.minecraft.client.gui.components.Button;
1317
import net.minecraft.client.gui.navigation.ScreenPosition;
1418
import net.minecraft.client.gui.screens.inventory.AbstractRecipeBookScreen;
1519
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
20+
import net.minecraft.client.gui.screens.recipebook.RecipeBookPage;
21+
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
1622
import net.minecraft.client.renderer.RenderPipelines;
1723
import net.minecraft.resources.Identifier;
1824
import net.minecraft.world.entity.player.Inventory;
1925
import net.minecraft.world.item.ItemStack;
26+
import net.minecraft.world.item.crafting.display.RecipeDisplayEntry;
27+
import net.minecraft.world.item.crafting.display.RecipeDisplayId;
28+
import net.minecraft.world.item.crafting.display.SlotDisplayContext;
2029
import net.minecraft.network.chat.Component;
2130

2231
public class WorkbenchWorkbenchScreen extends AbstractRecipeBookScreen<WorkbenchWorkbenchMenu> {
2332
private static final Identifier TEXTURE =
2433
Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "textures/gui/container/furnace_workbench.png");
2534

35+
private final MineTaleRecipeBookComponent mineTaleRecipeBook;
36+
37+
private Button craftOneBtn;
38+
private Button craftThirtyBtn;
39+
private Button craftAllBtn;
40+
2641
/**
2742
* Initialize a workbench GUI screen using the provided container menu, player inventory, and title.
2843
*
@@ -31,7 +46,12 @@ public class WorkbenchWorkbenchScreen extends AbstractRecipeBookScreen<Workbench
3146
* @param title the title component shown at the top of the screen
3247
*/
3348
public WorkbenchWorkbenchScreen(WorkbenchWorkbenchMenu menu, Inventory inventory, Component title) {
34-
super(menu, createRecipeBookComponent(menu), inventory, title);
49+
this(menu, inventory, title, createRecipeBookComponent(menu));
50+
}
51+
52+
private WorkbenchWorkbenchScreen(WorkbenchWorkbenchMenu menu, Inventory inventory, Component title, MineTaleRecipeBookComponent recipeBook) {
53+
super(menu, recipeBook, inventory, title);
54+
this.mineTaleRecipeBook = recipeBook;
3555
}
3656

3757
/**
@@ -63,6 +83,109 @@ protected void init() {
6383
this.imageHeight = 166;
6484

6585
super.init();
86+
87+
this.craftOneBtn = addRenderableWidget(Button.builder(Component.literal("1"), (button) -> {
88+
handleCraftRequest(1);
89+
}).bounds(this.leftPos + 80, this.topPos + 20, 30, 20).build());
90+
91+
this.craftThirtyBtn = addRenderableWidget(Button.builder(Component.literal("30"), (button) -> {
92+
handleCraftRequest(30);
93+
}).bounds(this.leftPos + 112, this.topPos + 20, 30, 20).build());
94+
95+
this.craftAllBtn = addRenderableWidget(Button.builder(Component.literal("All"), (button) -> {
96+
handleCraftRequest(-1); // -1 represents "All" logic
97+
}).bounds(this.leftPos + 144, this.topPos + 20, 30, 20).build());
98+
}
99+
100+
// private void handleCraftRequest(int amount) {
101+
// RecipeBookPage page = ((RecipeBookComponentAccessor)this.mineTaleRecipeBook).getRecipeBookPage();
102+
// RecipeCollection collection = page.getLastClickedRecipeCollection();
103+
// RecipeDisplayId displayId = page.getLastClickedRecipe();
104+
105+
// if (collection != null && displayId != null) {
106+
// // 1. Find the specific entry that was clicked
107+
// for (RecipeDisplayEntry entry : collection.getSelectedRecipes(RecipeCollection.CraftableStatus.ANY)) {
108+
// if (entry.id().equals(displayId)) {
109+
// // 2. Resolve the visual result into an actual ItemStack
110+
// List<ItemStack> results = entry.resultItems(SlotDisplayContext.fromLevel(this.minecraft.level));
111+
112+
// if (!results.isEmpty()) {
113+
// ItemStack resultStack = results.get(0);
114+
// // 3. Send the item and amount to the server
115+
// // Note: Update your CraftRequestPayload to accept ItemStack instead of Identifier
116+
// ClientPlayNetworking.send(new CraftRequestPayload(resultStack, amount));
117+
// }
118+
// break;
119+
// }
120+
// }
121+
// }
122+
// }
123+
124+
// private void handleCraftRequest(int amount) {
125+
// // 1. Get the current page from the recipe book
126+
// // We use your mixin/accessor to get the internal page object
127+
// RecipeBookPage page = ((RecipeBookComponentAccessor)this.mineTaleRecipeBook).getRecipeBookPage();
128+
129+
// // 2. Identify WHAT was clicked
130+
// RecipeCollection collection = page.getLastClickedRecipeCollection();
131+
// RecipeDisplayId displayId = page.getLastClickedRecipe();
132+
133+
// if (collection != null && displayId != null) {
134+
// // 3. Find the display entry
135+
// for (RecipeDisplayEntry entry : collection.getSelectedRecipes(RecipeCollection.CraftableStatus.ANY)) {
136+
// if (entry.id().equals(displayId)) {
137+
// // 4. Get the result item (the Chest)
138+
// // 1.21.1 uses SlotDisplayContext to handle dynamic results
139+
// List<ItemStack> results = entry.resultItems(SlotDisplayContext.fromLevel(this.minecraft.level));
140+
141+
// if (!results.isEmpty()) {
142+
// ItemStack resultStack = results.get(0);
143+
144+
// // 5. Send the packet to the Server
145+
// // IMPORTANT: Ensure your CraftRequestPayload is registered to handle
146+
// // an ItemStack and an Int.
147+
// ClientPlayNetworking.send(new CraftRequestPayload(resultStack, amount));
148+
149+
// // Optional: Play a click sound so the player knows it worked
150+
// this.minecraft.getSoundManager().play(net.minecraft.client.resources.sounds.SimpleSoundInstance.forUI(
151+
// net.minecraft.sounds.SoundEvents.UI_BUTTON_CLICK, 1.0F));
152+
// }
153+
// break;
154+
// }
155+
// }
156+
// }
157+
// }
158+
159+
// RecipeBookComponent
160+
161+
private void handleCraftRequest(int amount) {
162+
// 1. Cast the book component to the Accessor to get the selected data
163+
RecipeBookComponentAccessor accessor = (RecipeBookComponentAccessor) this.mineTaleRecipeBook;
164+
165+
RecipeCollection collection = accessor.getLastRecipeCollection();
166+
RecipeDisplayId displayId = accessor.getLastRecipe();
167+
168+
if (collection != null && displayId != null) {
169+
// 2. Find the visual entry
170+
for (RecipeDisplayEntry entry : collection.getSelectedRecipes(RecipeCollection.CraftableStatus.ANY)) {
171+
if (entry.id().equals(displayId)) {
172+
// 3. Resolve result for the packet
173+
List<ItemStack> results = entry.resultItems(SlotDisplayContext.fromLevel(this.minecraft.level));
174+
175+
if (!results.isEmpty()) {
176+
ItemStack resultStack = results.get(0);
177+
178+
// 4. LOG FOR DEBUGGING: Does this print in your console?
179+
System.out.println("Sending craft request for: " + resultStack + " amount: " + amount);
180+
181+
ClientPlayNetworking.send(new CraftRequestPayload(resultStack, amount));
182+
}
183+
break;
184+
}
185+
}
186+
} else {
187+
System.out.println("Request failed: Collection or DisplayID is null!");
188+
}
66189
}
67190

68191
/**
@@ -95,6 +218,11 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
95218
// 3. Call super (this draws your slots and items)
96219
super.render(graphics, mouseX, mouseY, delta);
97220

221+
boolean hasSelection = this.mineTaleRecipeBook.getSelectedRecipeId() != null;
222+
this.craftOneBtn.active = hasSelection;
223+
this.craftThirtyBtn.active = hasSelection;
224+
this.craftAllBtn.active = hasSelection;
225+
98226
renderTooltip(graphics, mouseX, mouseY);
99227
}
100228

src/client/java/com/tcm/MineTale/datagen/ModRecipeProvider.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import net.minecraft.core.HolderLookup;
1313
import net.minecraft.data.recipes.RecipeOutput;
1414
import net.minecraft.data.recipes.RecipeProvider;
15-
import net.minecraft.resources.Identifier;
15+
import net.minecraft.tags.ItemTags;
1616
import net.minecraft.world.item.ItemStack;
1717
import net.minecraft.world.item.Items;
1818
import net.minecraft.world.item.crafting.CraftingBookCategory;
@@ -52,7 +52,7 @@ public void buildRecipes() {
5252
.unlockedBy("has_porkchop", has(Items.PORKCHOP))
5353
.category(CraftingBookCategory.MISC)
5454
.bookCategory(ModRecipeDisplay.CAMPFIRE_SEARCH)
55-
.save(exporter, Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "campfire_pork_cooking"));
55+
.save(exporter, "campfire_pork_cooking");
5656

5757
new WorkbenchRecipeBuilder(ModRecipes.FURNACE_T1_TYPE, ModRecipes.FURNACE_SERIALIZER)
5858
.input(Ingredient.of(Items.PORKCHOP))
@@ -61,7 +61,17 @@ public void buildRecipes() {
6161
.unlockedBy("has_porkchop", has(Items.PORKCHOP))
6262
.category(CraftingBookCategory.MISC)
6363
.bookCategory(ModRecipeDisplay.FURNACE_T1_SEARCH)
64-
.save(exporter, Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "furnace_pork_cooking"));
64+
.save(exporter, "furnace_pork_cooking");
65+
66+
new WorkbenchRecipeBuilder(ModRecipes.WORKBENCH_TYPE, ModRecipes.WORKBENCH_SERIALIZER)
67+
.input(ItemTags.LOGS, registryLookup, 5)
68+
.input(Items.STICK, 10)
69+
.output(new ItemStack(Items.CHEST))
70+
.time(50)
71+
.unlockedBy("has_logs", has(ItemTags.LOGS))
72+
.category(CraftingBookCategory.MISC)
73+
.bookCategory(ModRecipeDisplay.WORKBENCH_SEARCH)
74+
.save(exporter, "workbench_wood_chest");
6575
}
6676
};
6777
}

src/client/java/com/tcm/MineTale/datagen/builders/WorkbenchRecipeBuilder.java

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import org.jspecify.annotations.Nullable;
99

10+
import com.tcm.MineTale.MineTale;
1011
import com.tcm.MineTale.recipe.WorkbenchRecipe;
1112
import com.tcm.MineTale.registry.ModRecipeDisplay;
1213

@@ -15,12 +16,14 @@
1516
import net.minecraft.advancements.AdvancementRewards;
1617
import net.minecraft.advancements.Criterion;
1718
import net.minecraft.advancements.criterion.RecipeUnlockedTrigger;
19+
import net.minecraft.core.HolderLookup;
1820
import net.minecraft.core.registries.BuiltInRegistries;
1921
import net.minecraft.core.registries.Registries;
2022
import net.minecraft.data.recipes.RecipeBuilder;
2123
import net.minecraft.data.recipes.RecipeOutput;
2224
import net.minecraft.resources.Identifier;
2325
import net.minecraft.resources.ResourceKey;
26+
import net.minecraft.tags.TagKey;
2427
import net.minecraft.world.item.Item;
2528
import net.minecraft.world.item.ItemStack;
2629
import net.minecraft.world.item.Items;
@@ -30,6 +33,7 @@
3033
import net.minecraft.world.item.crafting.RecipeBookCategory;
3134
import net.minecraft.world.item.crafting.RecipeSerializer;
3235
import net.minecraft.world.item.crafting.RecipeType;
36+
import net.minecraft.world.level.ItemLike;
3337

3438
public class WorkbenchRecipeBuilder implements RecipeBuilder {
3539
private final RecipeType<WorkbenchRecipe> type;
@@ -43,29 +47,6 @@ public class WorkbenchRecipeBuilder implements RecipeBuilder {
4347
private int cookTime = 200;
4448
@Nullable private String group;
4549

46-
// /**
47-
// * Creates a MapCodec that serializes and deserializes WorkbenchRecipe instances bound to the given recipe type and serializer.
48-
// *
49-
// * The codec encodes the recipe's ingredients, results, and cookTime (default 200) and constructs a WorkbenchRecipe using the provided type and serializer.
50-
// *
51-
// * @param type the RecipeType associated with the encoded WorkbenchRecipe
52-
// * @param serializer the RecipeSerializer used to (de)serialize the WorkbenchRecipe
53-
// * @return a MapCodec for WorkbenchRecipe that reads/writes ingredients, results, and cookTime and produces WorkbenchRecipe instances tied to the given type and serializer
54-
// */
55-
// public static final MapCodec<WorkbenchRecipe> CODEC(RecipeType<WorkbenchRecipe> type, RecipeSerializer<WorkbenchRecipe> serializer) {
56-
// return RecordCodecBuilder.mapCodec(inst -> inst.group(
57-
// Ingredient.CODEC.listOf().fieldOf("ingredients").forGetter(WorkbenchRecipe::ingredients),
58-
// ItemStack.STRICT_CODEC.listOf().fieldOf("results").forGetter(WorkbenchRecipe::results),
59-
// Codec.INT.optionalFieldOf("cookTime", 200).forGetter(WorkbenchRecipe::cookTime),
60-
// // Updated to CraftingBookCategory codec
61-
// CraftingBookCategory.CODEC.optionalFieldOf("category", CraftingBookCategory.MISC).forGetter(WorkbenchRecipe::category),
62-
// Identifier.CODEC.fieldOf("book_category").forGetter(WorkbenchRecipe::bookCategory)
63-
// ).apply(inst, (ingredients, results, cookTime, category, bookCategory) ->
64-
// // 2. Pass the new bookCategory into the constructor
65-
// new WorkbenchRecipe(ingredients, results, cookTime, type, serializer, category, bookCategory)
66-
// ));
67-
// }
68-
6950
/**
7051
* Create a new WorkbenchRecipeBuilder configured for a specific recipe type and its serializer.
7152
*
@@ -87,6 +68,20 @@ public WorkbenchRecipeBuilder(RecipeType<WorkbenchRecipe> type, RecipeSerializer
8768
public static WorkbenchRecipeBuilder create(RecipeType<WorkbenchRecipe> type, RecipeSerializer<WorkbenchRecipe> serializer) {
8869
return new WorkbenchRecipeBuilder(type, serializer);
8970
}
71+
72+
/**
73+
* Adds an input ingredient multiple times to represent a required count.
74+
*
75+
* @param ingredient the ingredient to add
76+
* @param count how many of this ingredient are required
77+
* @return this builder instance
78+
*/
79+
public WorkbenchRecipeBuilder input(Ingredient ingredient, int count) {
80+
for (int i = 0; i < count; i++) {
81+
this.ingredients.add(ingredient);
82+
}
83+
return this;
84+
}
9085

9186
/**
9287
* Adds an input ingredient to the recipe being built.
@@ -99,6 +94,36 @@ public WorkbenchRecipeBuilder input(Ingredient ingredient) {
9994
return this;
10095
}
10196

97+
/**
98+
* Adds an item input with a specific count.
99+
*/
100+
public WorkbenchRecipeBuilder input(ItemLike item, int count) {
101+
Ingredient ingredient = Ingredient.of(item);
102+
for (int i = 0; i < count; i++) {
103+
this.ingredients.add(ingredient);
104+
}
105+
return this;
106+
}
107+
108+
/**
109+
* Adds a tag input with a specific count.
110+
* Requires a RegistryLookup (usually available in your RecipeProvider).
111+
*/
112+
public WorkbenchRecipeBuilder input(TagKey<Item> tag, HolderLookup.Provider registries, int count) {
113+
// 1. Get the lookup for the Item registry from the provider
114+
var itemLookup = registries.lookupOrThrow(Registries.ITEM);
115+
116+
// 2. Now you can use getOrThrow with the TagKey
117+
Ingredient ingredient = Ingredient.of(itemLookup.getOrThrow(tag));
118+
119+
for (int i = 0; i < count; i++) {
120+
this.ingredients.add(ingredient);
121+
}
122+
return this;
123+
}
124+
125+
126+
102127
public WorkbenchRecipeBuilder category(CraftingBookCategory category) {
103128
this.category = category;
104129
return this;
@@ -176,8 +201,8 @@ public Item getResult() {
176201
* @param exporter the RecipeOutput that will receive the recipe
177202
* @param id the identifier to use for the saved recipe
178203
*/
179-
public void save(RecipeOutput exporter, Identifier id) {
180-
this.save(exporter, ResourceKey.create(Registries.RECIPE, id));
204+
public void save(RecipeOutput exporter, String name) {
205+
this.save(exporter, ResourceKey.create(Registries.RECIPE, Identifier.fromNamespaceAndPath(MineTale.MOD_ID, name)));
181206
}
182207

183208
/**

src/client/java/com/tcm/MineTale/mixin/client/ClientRecipeBookMixin.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public abstract class ClientRecipeBookMixin {
2424
cir.setReturnValue(ModRecipeDisplay.CAMPFIRE_SEARCH);
2525
} else if (ModRecipes.FURNACE_T1_TYPE.equals(type)) {
2626
cir.setReturnValue(ModRecipeDisplay.FURNACE_T1_SEARCH);
27+
} else if (ModRecipes.WORKBENCH_TYPE.equals(type)) {
28+
cir.setReturnValue(ModRecipeDisplay.WORKBENCH_SEARCH);
2729
}
2830
}
2931
}

0 commit comments

Comments
 (0)