@@ -146,10 +146,10 @@ private void handleCraftRequest(int amount) {
146146 }
147147
148148 /**
149- * Draws the workbench GUI background texture at the screen's top-left corner.
149+ * Render the workbench background texture at the screen's top-left corner.
150150 *
151151 * @param guiGraphics the graphics context used to draw GUI elements
152- * @param f partial tick time for interpolation
152+ * @param f partial tick time used for animation interpolation
153153 * @param i current mouse x coordinate relative to the window
154154 * @param j current mouse y coordinate relative to the window
155155 */
@@ -159,36 +159,51 @@ protected void renderBg(GuiGraphics guiGraphics, float f, int i, int j) {
159159 guiGraphics .blit (RenderPipelines .GUI_TEXTURED , TEXTURE , k , l , 0.0F , 0.0F , this .imageWidth , this .imageHeight , 256 , 256 );
160160 }
161161
162+ /**
163+ * Renders the builders workbench screen, updates recipe selection state, updates craft button availability
164+ * and, when a recipe is selected, renders its ingredient list and hover tooltips.
165+ *
166+ * The method:
167+ * - draws the background and base screen,
168+ * - updates {@code lastKnownSelectedId} from the recipe book component,
169+ * - looks up the corresponding {@code RecipeDisplayEntry} from the client's known recipes,
170+ * - enables or disables the craft buttons according to available ingredients for 1, 2 and 10 crafts,
171+ * - renders the aggregated ingredient list for the selected recipe (including hover tooltips) when present,
172+ * - renders the standard screen tooltip layer.
173+ *
174+ * @param graphics the GUI graphics context used for all rendering operations
175+ * @param mouseX current mouse X coordinate (screen space)
176+ * @param mouseY current mouse Y coordinate (screen space)
177+ * @param delta frame partial tick time used for animated displays
178+ */
162179 @ Override
163180 public void render (GuiGraphics graphics , int mouseX , int mouseY , float delta ) {
164181 renderBackground (graphics , mouseX , mouseY , delta );
165182 super .render (graphics , mouseX , mouseY , delta );
166183
167- // 1. Get the current selection from the book
168184 RecipeDisplayId currentId = this .mineTaleRecipeBook .getSelectedRecipeId ();
169-
170- // 2. If it's NOT null, remember it!
171185 if (currentId != null ) {
172186 this .lastKnownSelectedId = currentId ;
173187 }
174188
175- // 3. Use the remembered ID to find the entry for button activation
176189 RecipeDisplayEntry selectedEntry = null ;
177190 if (this .lastKnownSelectedId != null && this .minecraft .level != null ) {
178191 ClientRecipeBook book = this .minecraft .player .getRecipeBook ();
179192 selectedEntry = ((ClientRecipeBookAccessor ) book ).getKnown ().get (this .lastKnownSelectedId );
180193 }
181194
182- // 2. Button Activation Logic
183195 if (selectedEntry != null ) {
184- // We use the entry directly. It contains the 15 ingredients needed!
196+ // Existing Button Logic
185197 boolean canCraftOne = canCraft (this .minecraft .player , selectedEntry , 1 );
186198 boolean canCraftMoreThanOne = canCraft (this .minecraft .player , selectedEntry , 2 );
187199 boolean canCraftTen = canCraft (this .minecraft .player , selectedEntry , 10 );
188200
189201 this .craftOneBtn .active = canCraftOne ;
190202 this .craftTenBtn .active = canCraftTen ;
191203 this .craftAllBtn .active = canCraftMoreThanOne ;
204+
205+ // NEW: Render the Ingredients List
206+ renderIngredientList (graphics , selectedEntry , mouseX , mouseY );
192207 } else {
193208 this .craftOneBtn .active = false ;
194209 this .craftTenBtn .active = false ;
@@ -198,6 +213,94 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
198213 renderTooltip (graphics , mouseX , mouseY );
199214 }
200215
216+ /**
217+ * Render the aggregated ingredient list for a recipe, showing icons, availability and tooltips.
218+ *
219+ * <p>For the given recipe this draws one row per unique ingredient set (duplicates aggregated),
220+ * cycles through item variants for the icon, renders an availability string "available/needed"
221+ * (red when insufficient, white when sufficient) and shows an item tooltip when the mouse
222+ * hovers over the icon.
223+ *
224+ * @param graphics the GUI graphics context used for rendering
225+ * @param entry the recipe entry whose crafting requirements will be displayed
226+ * @param mouseX the current mouse X coordinate (used to detect tooltip hover)
227+ * @param mouseY the current mouse Y coordinate (used to detect tooltip hover)
228+ */
229+ private void renderIngredientList (GuiGraphics graphics , RecipeDisplayEntry entry , int mouseX , int mouseY ) {
230+ Optional <List <Ingredient >> reqs = entry .craftingRequirements ();
231+ if (reqs .isEmpty ()) return ;
232+
233+ // Group requirements to avoid duplicate rows for the same item type
234+ Map <List <Holder <Item >>, Integer > aggregated = new HashMap <>();
235+ Map <List <Holder <Item >>, Ingredient > holderToIng = new HashMap <>();
236+
237+ for (Ingredient ing : reqs .get ()) {
238+ List <Holder <Item >> key = ing .items ().toList ();
239+ aggregated .put (key , aggregated .getOrDefault (key , 0 ) + 1 );
240+ holderToIng .putIfAbsent (key , ing );
241+ }
242+
243+ int startX = this .leftPos + 8 ; // Adjust to fit your texture's empty space
244+ int startY = this .topPos + 20 ;
245+ int rowHeight = 20 ;
246+ int index = 0 ;
247+
248+ for (Map .Entry <List <Holder <Item >>, Integer > reqEntry : aggregated .entrySet ()) {
249+ Ingredient ing = holderToIng .get (reqEntry .getKey ());
250+ int amountNeeded = reqEntry .getValue ();
251+ int currentY = startY + (index * rowHeight );
252+
253+ // Calculate total available (Inv + Nearby)
254+ int available = getAvailableCount (ing );
255+
256+ // Draw Item Icon
257+ ItemStack [] variants = ing .items ().toArray (ItemStack []::new );
258+ if (variants .length > 0 ) {
259+ // Cycle through variants every second
260+ ItemStack displayStack = variants [(int ) (System .currentTimeMillis () / 1000 % variants .length )];
261+ graphics .renderFakeItem (displayStack , startX , currentY );
262+
263+ // Draw Text (Red if lacking, White if okay)
264+ int color = (available < amountNeeded ) ? 0xFF5555 : 0xFFFFFF ;
265+ String progress = available + "/" + amountNeeded ;
266+ graphics .drawString (this .font , progress , startX + 20 , currentY + 4 , color , true );
267+
268+ // Tooltip logic
269+ if (mouseX >= startX && mouseX < startX + 16 && mouseY >= currentY && mouseY < currentY + 16 ) {
270+ graphics .renderItemTooltip (this .font , displayStack , mouseX , mouseY );
271+ }
272+ }
273+ index ++;
274+ }
275+ }
276+
277+ /**
278+ * Count the total quantity of items that satisfy the given ingredient from the player inventory and nearby networked storage.
279+ *
280+ * @param ingredient the ingredient used to test ItemStacks
281+ * @return the sum of counts of all ItemStacks matching `ingredient` from the player inventory and any networked nearby items
282+ */
283+ private int getAvailableCount (Ingredient ingredient ) {
284+ int found = 0 ;
285+ // Check Player Inventory
286+ // Use getContainerSize() and getItem(i) for safe access
287+ Inventory inv = this .minecraft .player .getInventory ();
288+ for (int i = 0 ; i < inv .getContainerSize (); i ++) {
289+ ItemStack stack = inv .getItem (i );
290+ if (ingredient .test (stack )) {
291+ found += stack .getCount ();
292+ }
293+ }
294+
295+ // Check Networked Nearby Items
296+ if (this .menu instanceof AbstractWorkbenchContainerMenu workbenchMenu ) {
297+ for (ItemStack stack : workbenchMenu .getNetworkedNearbyItems ()) {
298+ if (ingredient .test (stack )) found += stack .getCount ();
299+ }
300+ }
301+ return found ;
302+ }
303+
201304 /**
202305 * Determines whether the player has enough ingredients to craft the given recipe the specified number of times.
203306 *
@@ -281,9 +384,9 @@ private boolean hasIngredientAmount(Inventory inventory, Ingredient ingredient,
281384 }
282385
283386 /**
284- * Computes the on-screen position for the recipe book toggle button for this GUI.
387+ * Compute the on-screen position for the recipe- book toggle button for this GUI.
285388 *
286- * @return the screen position placed 5 pixels from the GUI's left edge and 49 pixels above the GUI's vertical center
389+ * @return the screen position located 5 pixels from the GUI's left edge and 49 pixels above the GUI's vertical centre
287390 */
288391 @ Override
289392 protected ScreenPosition getRecipeBookButtonPosition () {
@@ -297,4 +400,4 @@ protected ScreenPosition getRecipeBookButtonPosition() {
297400 // Usually 5 pixels in from the left and 49 pixels up from the center
298401 return new ScreenPosition (guiLeft + 5 , guiTop + this .imageHeight / 2 - 49 );
299402 }
300- }
403+ }
0 commit comments