Skip to content

Commit 5e4f31c

Browse files
Merge pull request #25 from CodeMonkeysMods/feat/abstract-furnace-workbench-logic
Feat/abstract furnace workbench logic
2 parents 55c8057 + a62db58 commit 5e4f31c

29 files changed

Lines changed: 1032 additions & 577 deletions

src/client/java/com/tcm/MineTale/MineTaleClient.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import net.minecraft.client.gui.screens.MenuScreens;
99

1010
public class MineTaleClient implements ClientModInitializer {
11+
12+
13+
1114
/**
1215
* Registers client-side screen factories for custom workbench menu types.
1316
*

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

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
package com.tcm.MineTale.block.workbenches.screen;
22

3+
import java.util.List;
4+
35
import com.tcm.MineTale.MineTale;
46
import com.tcm.MineTale.block.workbenches.menu.CampfireWorkbenchMenu;
7+
import com.tcm.MineTale.recipe.MineTaleRecipeBookComponent;
8+
import com.tcm.MineTale.registry.ModBlocks;
9+
import com.tcm.MineTale.registry.ModRecipeDisplay;
510

611
import net.minecraft.client.gui.GuiGraphics;
7-
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
12+
import net.minecraft.client.gui.navigation.ScreenPosition;
13+
import net.minecraft.client.gui.screens.inventory.AbstractRecipeBookScreen;
14+
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
815
import net.minecraft.client.renderer.RenderPipelines;
916
import net.minecraft.resources.Identifier;
1017
import net.minecraft.world.entity.player.Inventory;
18+
import net.minecraft.world.item.ItemStack;
1119
import net.minecraft.network.chat.Component;
1220

13-
public class CampfireWorkbenchScreen extends AbstractContainerScreen<CampfireWorkbenchMenu> {
21+
public class CampfireWorkbenchScreen extends AbstractRecipeBookScreen<CampfireWorkbenchMenu> {
1422
private static final Identifier TEXTURE =
1523
Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "textures/gui/container/furnace_workbench.png");
24+
1625
/**
1726
* Creates a campfire workbench screen for the provided menu, player inventory, and title.
1827
*
@@ -21,16 +30,33 @@ public class CampfireWorkbenchScreen extends AbstractContainerScreen<CampfireWor
2130
* @param title the title component shown at the top of the screen
2231
*/
2332
public CampfireWorkbenchScreen(CampfireWorkbenchMenu menu, Inventory inventory, Component title) {
24-
super(menu, inventory, title);
33+
super(menu, createRecipeBookComponent(menu), inventory, title);
34+
}
35+
36+
/**
37+
* Static helper to build the component with the custom MineTale tabs
38+
* before the super constructor is called.
39+
*/
40+
private static MineTaleRecipeBookComponent createRecipeBookComponent(CampfireWorkbenchMenu menu) {
41+
ItemStack tabIcon = new ItemStack(ModBlocks.CAMPFIRE_WORKBENCH_BLOCK.asItem());
42+
43+
List<RecipeBookComponent.TabInfo> tabs = List.of(
44+
new RecipeBookComponent.TabInfo(tabIcon.getItem(), ModRecipeDisplay.CAMPFIRE_SEARCH)
45+
);
46+
47+
return new MineTaleRecipeBookComponent(menu, tabs);
2548
}
2649

2750
/**
2851
* Initializes the screen and centers the title horizontally by setting {@code titleLabelX}.
2952
*/
3053
@Override
3154
protected void init() {
55+
// Important: Set your GUI size before super.init()
56+
this.imageWidth = 176;
57+
this.imageHeight = 166;
58+
3259
super.init();
33-
this.titleLabelX = (this.imageWidth - this.font.width(this.title)) / 2;
3460
}
3561

3662
/**
@@ -57,8 +83,25 @@ protected void renderBg(GuiGraphics guiGraphics, float f, int i, int j) {
5783
*/
5884
@Override
5985
public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
86+
// 1. Always render the dark background tint first
6087
renderBackground(graphics, mouseX, mouseY, delta);
88+
89+
// 3. Call super (this draws your slots and items)
6190
super.render(graphics, mouseX, mouseY, delta);
91+
6292
renderTooltip(graphics, mouseX, mouseY);
6393
}
94+
95+
@Override
96+
protected ScreenPosition getRecipeBookButtonPosition() {
97+
// 1. Calculate the start (left) of your workbench GUI
98+
int guiLeft = (this.width - this.imageWidth) / 2;
99+
100+
// 2. Calculate the top of your workbench GUI
101+
int guiTop = (this.height - this.imageHeight) / 2;
102+
103+
// 3. Standard Vanilla positioning:
104+
// Usually 5 pixels in from the left and 49 pixels up from the center
105+
return new ScreenPosition(guiLeft + 5, guiTop + this.imageHeight / 2 - 49);
106+
}
64107
}
Lines changed: 70 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,104 @@
11
package com.tcm.MineTale.block.workbenches.screen;
22

3+
import java.util.List;
4+
35
import com.tcm.MineTale.MineTale;
46
import com.tcm.MineTale.block.workbenches.menu.FurnaceWorkbenchMenu;
7+
import com.tcm.MineTale.recipe.MineTaleRecipeBookComponent;
8+
import com.tcm.MineTale.registry.ModBlocks;
59

610
import net.minecraft.client.gui.GuiGraphics;
7-
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
11+
import net.minecraft.client.gui.navigation.ScreenPosition;
12+
import net.minecraft.client.gui.screens.inventory.AbstractRecipeBookScreen;
13+
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
814
import net.minecraft.client.renderer.RenderPipelines;
915
import net.minecraft.resources.Identifier;
1016
import net.minecraft.world.entity.player.Inventory;
17+
import net.minecraft.world.item.ItemStack;
18+
import net.minecraft.world.item.crafting.RecipeBookCategories;
1119
import net.minecraft.network.chat.Component;
1220

13-
public class FurnaceWorkbenchScreen extends AbstractContainerScreen<FurnaceWorkbenchMenu> {
21+
public class FurnaceWorkbenchScreen extends AbstractRecipeBookScreen<FurnaceWorkbenchMenu> {
1422
private static final Identifier TEXTURE =
1523
Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "textures/gui/container/furnace_workbench.png");
1624

1725
/**
18-
* Creates a new furnace workbench screen for the given menu, player inventory, and title.
19-
*
20-
* @param menu the container menu that provides slots and syncs state for this screen
21-
* @param inventory the player's inventory to display and interact with
22-
* @param title the title component shown at the top of the screen
26+
* Creates a new furnace workbench screen.
27+
* Note: recipeBookComponent is inherited from AbstractRecipeBookScreen.
2328
*/
2429
public FurnaceWorkbenchScreen(FurnaceWorkbenchMenu menu, Inventory inventory, Component title) {
25-
super(menu, inventory, title);
30+
super(menu, createRecipeBookComponent(menu), inventory, title);
2631
}
2732

2833
/**
29-
* Initializes the screen and centers the title horizontally by setting {@code titleLabelX}.
34+
* Static helper to build the component with the custom MineTale tabs.
35+
* This uses the FURNACE_WORKBENCH icon for this specific screen's tab.
3036
*/
37+
private static MineTaleRecipeBookComponent createRecipeBookComponent(FurnaceWorkbenchMenu menu) {
38+
ItemStack tabIcon = new ItemStack(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1.asItem());
39+
40+
List<RecipeBookComponent.TabInfo> tabs = List.of(
41+
new RecipeBookComponent.TabInfo(tabIcon.getItem(), RecipeBookCategories.CRAFTING_MISC)
42+
);
43+
44+
return new MineTaleRecipeBookComponent(menu, tabs);
45+
}
46+
3147
@Override
3248
protected void init() {
49+
this.imageWidth = 176;
50+
this.imageHeight = 166;
51+
3352
super.init();
34-
this.titleLabelX = (this.imageWidth - this.font.width(this.title)) / 2;
53+
54+
// // Initialize the inherited recipeBookComponent UI state
55+
// this.recipeBookComponent.init(this.width, this.height, this.minecraft, false);
56+
// this.leftPos = this.recipeBookComponent.updateScreenPosition(this.width, this.imageWidth);
57+
58+
// // The toggle button is managed via getRecipeBookButtonPosition() in 1.21.1
59+
// // but we add the ImageButton manually to match your CampfireWorkbenchScreen logic exactly.
60+
// this.addRenderableWidget(new ImageButton(
61+
// this.leftPos + 5,
62+
// this.height / 2 - 49,
63+
// 20, 18,
64+
// RecipeBookComponent.RECIPE_BUTTON_SPRITES,
65+
// (button) -> {
66+
// this.recipeBookComponent.toggleVisibility();
67+
// this.leftPos = this.recipeBookComponent.updateScreenPosition(this.width, this.imageWidth);
68+
// button.setPosition(this.leftPos + 5, this.height / 2 - 49);
69+
// }
70+
// ));
3571
}
3672

37-
/**
38-
* Draws the furnace workbench background texture onto the screen.
39-
*
40-
* @param guiGraphics the graphics context used for drawing
41-
* @param f partial tick time used for interpolation
42-
* @param i current mouse x position
43-
* @param j current mouse y position
44-
*/
45-
protected void renderBg(GuiGraphics guiGraphics, float f, int i, int j) {
46-
int k = this.leftPos;
47-
int l = this.topPos;
48-
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, k, l, 0.0F, 0.0F, this.imageWidth, this.imageHeight, 256, 256);
49-
}
73+
@Override
74+
protected void renderBg(GuiGraphics guiGraphics, float f, int i, int j) {
75+
int k = this.leftPos;
76+
int l = this.topPos;
77+
guiGraphics.blit(RenderPipelines.GUI_TEXTURED, TEXTURE, k, l, 0.0F, 0.0F, this.imageWidth, this.imageHeight, 256, 256);
78+
}
5079

51-
/**
52-
* Renders the furnace workbench screen, drawing its background, contents, and tooltips.
53-
*
54-
* @param graphics the graphics context used for rendering
55-
* @param mouseX the current mouse X coordinate
56-
* @param mouseY the current mouse Y coordinate
57-
* @param delta the frame time delta (partial tick) used for animated rendering
58-
*/
5980
@Override
6081
public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
6182
renderBackground(graphics, mouseX, mouseY, delta);
83+
84+
// if (this.recipeBookComponent != null) {
85+
// this.recipeBookComponent.render(graphics, mouseX, mouseY, delta);
86+
// }
87+
6288
super.render(graphics, mouseX, mouseY, delta);
89+
90+
// if (this.recipeBookComponent != null) {
91+
// this.recipeBookComponent.renderGhostRecipe(graphics, true);
92+
// this.recipeBookComponent.renderTooltip(graphics, this.leftPos, this.topPos, this.hoveredSlot);
93+
// }
94+
6395
renderTooltip(graphics, mouseX, mouseY);
6496
}
97+
98+
@Override
99+
protected ScreenPosition getRecipeBookButtonPosition() {
100+
int guiLeft = (this.width - this.imageWidth) / 2;
101+
int guiTop = (this.height - this.imageHeight) / 2;
102+
return new ScreenPosition(guiLeft + 5, guiTop + this.imageHeight / 2 - 49);
103+
}
65104
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.tcm.MineTale.mixin.client;
2+
3+
import org.spongepowered.asm.mixin.Mixin;
4+
import org.spongepowered.asm.mixin.injection.At;
5+
import org.spongepowered.asm.mixin.injection.Inject;
6+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
7+
8+
import com.tcm.MineTale.registry.ModRecipeDisplay;
9+
import com.tcm.MineTale.registry.ModRecipes;
10+
11+
import net.minecraft.client.ClientRecipeBook;
12+
import net.minecraft.world.item.crafting.RecipeBookCategory;
13+
import net.minecraft.world.item.crafting.RecipeHolder;
14+
15+
@Mixin(ClientRecipeBook.class)
16+
public abstract class ClientRecipeBookMixin {
17+
@Inject(method = "getCategory", at = @At("HEAD"), cancellable = true)
18+
private static void minetale$addCustomCategory(RecipeHolder<?> recipe, CallbackInfoReturnable<RecipeBookCategory> cir) {
19+
if (recipe.value().getType() == ModRecipes.FURNACE_SERIALIZER) {
20+
// This tells the search engine to put your recipes into your custom tab
21+
cir.setReturnValue(ModRecipeDisplay.CAMPFIRE_SEARCH);
22+
}
23+
}
24+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.tcm.MineTale.recipe;
2+
3+
import java.util.List;
4+
5+
import com.tcm.MineTale.registry.ModRecipeDisplay;
6+
import com.tcm.MineTale.util.Constants;
7+
8+
import net.minecraft.client.gui.components.WidgetSprites;
9+
import net.minecraft.client.gui.screens.recipebook.GhostSlots;
10+
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
11+
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
12+
import net.minecraft.network.chat.Component;
13+
import net.minecraft.resources.Identifier;
14+
import net.minecraft.util.context.ContextMap;
15+
import net.minecraft.world.entity.player.StackedItemContents;
16+
import net.minecraft.world.inventory.RecipeBookMenu;
17+
import net.minecraft.world.inventory.Slot;
18+
import net.minecraft.world.item.crafting.display.RecipeDisplay;
19+
20+
public class MineTaleRecipeBookComponent extends RecipeBookComponent<RecipeBookMenu> {
21+
22+
// Standard button sprites (the "Filter" checkmark button)
23+
protected static final WidgetSprites FILTER_BUTTON_SPRITES = new WidgetSprites(
24+
Identifier.withDefaultNamespace("recipe_book/filter_enabled"),
25+
Identifier.withDefaultNamespace("recipe_book/filter_disabled"),
26+
Identifier.withDefaultNamespace("recipe_book/filter_enabled_focused"),
27+
Identifier.withDefaultNamespace("recipe_book/filter_disabled_focused")
28+
);
29+
30+
public MineTaleRecipeBookComponent(RecipeBookMenu recipeBookMenu, List<TabInfo> list) {
31+
super(recipeBookMenu, list);
32+
}
33+
34+
@Override
35+
protected void selectMatchingRecipes(RecipeCollection recipeCollection, StackedItemContents stackedItemContents) {
36+
// Force everything to be "selected"
37+
// recipeCollection.selectRecipes(stackedItemContents, (recipeDisplay) -> true);
38+
39+
recipeCollection.selectRecipes(stackedItemContents, (recipeDisplay) -> {
40+
// Only allow recipes that use your custom Workbench display type
41+
// This effectively filters out vanilla CraftingRecipeDisplays (the boats)
42+
return recipeDisplay.type() == ModRecipeDisplay.WORKBENCH_TYPE;
43+
});
44+
}
45+
46+
47+
48+
@Override
49+
protected WidgetSprites getFilterButtonTextures() {
50+
// Returns the textures for the "Toggle craftable" button
51+
return FILTER_BUTTON_SPRITES;
52+
}
53+
54+
@Override
55+
protected boolean isCraftingSlot(Slot slot) {
56+
return slot.index == Constants.INPUT_START || slot.index == Constants.INPUT_START + 1;
57+
}
58+
59+
@Override
60+
protected Component getRecipeFilterName() {
61+
// The text shown when hovering over the filter button
62+
return Component.translatable("gui.recipebook.toggleRecipes.all");
63+
}
64+
65+
@Override
66+
protected void fillGhostRecipe(GhostSlots ghostSlots, RecipeDisplay recipeDisplay, ContextMap contextMap) {
67+
// This places the faint "ghost" items in the workbench slots when hovering a recipe
68+
// We use SlotDisplayContext.fromLevel(this.minecraft.level) to handle dynamic displays
69+
// ghostSlots.setRecipe(recipeDisplay);
70+
71+
// // We assume the first two slots of your menu are the inputs
72+
// // Your AbstractWorkbenchContainerMenu adds input slots first (index 0 and 1)
73+
// ghostSlots.addSlot(this.menu.slots.get(0), this.minecraft.level.registryAccess(), recipeDisplay.result());
74+
75+
// // If your custom recipe has specific inputs, you'd map them here.
76+
// // For a generic implementation, we use the display's suggested placement:
77+
// recipeDisplay.setupGhostSlots(ghostSlots, SlotDisplayContext.fromLevel(this.minecraft.level));
78+
}
79+
80+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package net.minecraft.client.gui.screens.recipebook;
2+
3+
import net.minecraft.world.inventory.Slot;
4+
import net.minecraft.util.context.ContextMap;
5+
import net.minecraft.world.item.crafting.display.SlotDisplay;
6+
7+
public class GhostSlotsProxy {
8+
public static void setInputProxy(GhostSlots ghostSlots, Slot slot, ContextMap contextMap, SlotDisplay display) {
9+
// Because this class is in the same package, it can see protected methods!
10+
ghostSlots.setInput(slot, contextMap, display);
11+
}
12+
13+
public static void setResultProxy(GhostSlots ghostSlots, Slot slot, ContextMap contextMap, SlotDisplay display) {
14+
ghostSlots.setResult(slot, contextMap, display);
15+
}
16+
}

0 commit comments

Comments
 (0)