Skip to content

Commit 3a2ba23

Browse files
Merge pull request #39 from CodeMonkeysMods/feat/furnace-workbench-model-hookup
feat make model line up with bounding box
2 parents 43cf082 + 12e2904 commit 3a2ba23

17 files changed

Lines changed: 315 additions & 564 deletions

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

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,14 @@
44
import java.util.List;
55
import java.util.Map;
66
import java.util.Optional;
7-
import java.util.stream.Collectors;
87

98
import com.tcm.MineTale.MineTale;
10-
import com.tcm.MineTale.block.workbenches.entity.AbstractWorkbenchEntity;
119
import com.tcm.MineTale.block.workbenches.menu.AbstractWorkbenchContainerMenu;
1210
import com.tcm.MineTale.block.workbenches.menu.WorkbenchWorkbenchMenu;
1311
import com.tcm.MineTale.mixin.client.ClientRecipeBookAccessor;
1412
import com.tcm.MineTale.mixin.client.RecipeBookComponentAccessor;
1513
import com.tcm.MineTale.network.CraftRequestPayload;
1614
import com.tcm.MineTale.recipe.MineTaleRecipeBookComponent;
17-
import com.tcm.MineTale.recipe.WorkbenchRecipe;
1815
import com.tcm.MineTale.registry.ModBlocks;
1916
import com.tcm.MineTale.registry.ModRecipeDisplay;
2017
import com.tcm.MineTale.registry.ModRecipes;
@@ -28,9 +25,8 @@
2825
import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent;
2926
import net.minecraft.client.gui.screens.recipebook.RecipeCollection;
3027
import net.minecraft.client.renderer.RenderPipelines;
31-
import net.minecraft.core.HolderSet;
28+
import net.minecraft.core.Holder;
3229
import net.minecraft.resources.Identifier;
33-
import net.minecraft.server.level.ServerPlayer;
3430
import net.minecraft.world.entity.player.Inventory;
3531
import net.minecraft.world.entity.player.Player;
3632
import net.minecraft.world.item.Item;
@@ -211,37 +207,45 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
211207
renderTooltip(graphics, mouseX, mouseY);
212208
}
213209

210+
/**
211+
* Determines whether the player has enough ingredients to craft the given recipe the specified number of times.
212+
*
213+
* @param player the player whose inventory (and networked nearby items) will be checked; may be null
214+
* @param entry the recipe display entry providing crafting requirements; may be null
215+
* @param craftCount the multiplier for required ingredient quantities (e.g., 1, 10, or -1 is not specially handled here)
216+
* @return `true` if the player has at least the required quantity of each ingredient multiplied by `craftCount`, `false` otherwise (also returns `false` if `player` or `entry` is null or the recipe has no requirements)
217+
*/
214218
private boolean canCraft(Player player, RecipeDisplayEntry entry, int craftCount) {
215219
if (player == null || entry == null) return false;
216220

217221
Optional<List<Ingredient>> reqs = entry.craftingRequirements();
218222
if (reqs.isEmpty()) return false;
219223

220-
// 1. Group ingredients by their underlying Item HolderSet.
221-
// Since Ingredient doesn't override hashCode, we use the values field directly
222-
// or use a List of Holders as the key for stable hashing.
223-
Map<HolderSet<Item>, Integer> aggregatedRequirements = new HashMap<>();
224-
225-
// Helper map to get back to an Ingredient object for the final check
226-
Map<HolderSet<Item>, Ingredient> holderToIngredient = new HashMap<>();
224+
// 1. Group ingredients by their underlying Item Holders.
225+
// Using List<Holder<Item>> as the key ensures structural equality (content-based hashing).
226+
Map<List<Holder<Item>>, Integer> aggregatedRequirements = new HashMap<>();
227+
Map<List<Holder<Item>>, Ingredient> holderToIngredient = new HashMap<>();
227228

228229
for (Ingredient ing : reqs.get()) {
229-
// Accessing the 'values' via a custom accessor or reflection if private,
230-
// but based on your source, we can use the Ingredient object itself
231-
// IF we use a helper that handles the hashing correctly.
232-
233-
// Strategy: Use the stream of holders as a List key (Lists have stable hashcodes)
234-
HolderSet<Item> key = ing.items().collect(Collectors.collectingAndThen(Collectors.toList(), HolderSet::direct));
235-
230+
// Collect holders into a List to get a stable hashCode() and equals()
231+
@SuppressWarnings("deprecation")
232+
List<Holder<Item>> key = ing.items().toList();
233+
234+
// Aggregate the counts (how many of this specific ingredient set are required)
236235
aggregatedRequirements.put(key, aggregatedRequirements.getOrDefault(key, 0) + 1);
236+
237+
// Map the list back to the original ingredient for use in hasIngredientAmount
237238
holderToIngredient.putIfAbsent(key, ing);
238239
}
239240

240-
// 2. Check the player's inventory
241+
// 2. Check the player's inventory against the aggregated totals
241242
Inventory inv = player.getInventory();
242-
for (Map.Entry<HolderSet<Item>, Integer> entryReq : aggregatedRequirements.entrySet()) {
243+
for (Map.Entry<List<Holder<Item>>, Integer> entryReq : aggregatedRequirements.entrySet()) {
244+
List<Holder<Item>> key = entryReq.getKey();
243245
int totalNeeded = entryReq.getValue() * craftCount;
244-
Ingredient originalIng = holderToIngredient.get(entryReq.getKey());
246+
247+
// Retrieve the original Ingredient object associated with this list of holders
248+
Ingredient originalIng = holderToIngredient.get(key);
245249

246250
if (!hasIngredientAmount(inv, originalIng, totalNeeded)) {
247251
return false;

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

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
package com.tcm.MineTale.datagen;
22

3+
import com.tcm.MineTale.MineTale;
34
import com.tcm.MineTale.registry.ModBlocks;
45
import net.fabricmc.fabric.api.client.datagen.v1.provider.FabricModelProvider;
56
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
67
import net.minecraft.client.data.models.BlockModelGenerators;
78
import net.minecraft.client.data.models.ItemModelGenerators;
89
import net.minecraft.client.data.models.blockstates.MultiVariantGenerator;
910
import net.minecraft.client.data.models.blockstates.PropertyDispatch;
10-
import net.minecraft.client.data.models.model.ModelLocationUtils;
1111
import net.minecraft.client.renderer.block.model.VariantMutator;
1212
import net.minecraft.core.Direction;
1313
import net.minecraft.resources.Identifier;
1414
import net.minecraft.world.level.block.Block;
1515
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
16-
import net.minecraft.world.level.block.state.properties.ChestType;
1716
import net.minecraft.world.level.block.state.properties.DoubleBlockHalf;
1817

1918
public class ModModelProvider extends FabricModelProvider {
@@ -32,12 +31,12 @@ public class ModModelProvider extends FabricModelProvider {
3231
.select(Direction.WEST, BlockModelGenerators.Y_ROT_270);
3332

3433
/**
35-
* Registers block state and model definitions for the mod's log blocks.
34+
* Registers block state and model definitions for the mod's custom log blocks and furnace workbenches.
3635
*
37-
* This configures horizontal and vertical log models for each custom log block and, for
38-
* WILD_WISTERIA_LOG, also registers the corresponding wood model (WILD_WISTERIA_WOOD).
36+
* Configures horizontal and vertical variants for each custom log block and registers the wood model for
37+
* WILD_WISTERIA_LOG; registers blockstate variants and item models for the mod's furnace workbench blocks.
3938
*
40-
* @param blockStateModelGenerator the generator used to create block state and model entries
39+
* @param blockStateModelGenerator generator used to create block state and model entries
4140
*/
4241
@Override
4342
public void generateBlockStateModels(BlockModelGenerators blockStateModelGenerator) {
@@ -72,38 +71,41 @@ public void generateBlockStateModels(BlockModelGenerators blockStateModelGenerat
7271
blockStateModelGenerator.woodProvider(ModBlocks.WINDWILLOW_LOG).logWithHorizontal(ModBlocks.WINDWILLOW_LOG);
7372
blockStateModelGenerator.woodProvider(ModBlocks.WILD_WISTERIA_LOG).logWithHorizontal(ModBlocks.WILD_WISTERIA_LOG).wood(ModBlocks.WILD_WISTERIA_WOOD);
7473

75-
registerLargeWorkbench(blockStateModelGenerator, ModBlocks.FURNACE_WORKBENCH_BLOCK_T1);
76-
registerLargeWorkbench(blockStateModelGenerator, ModBlocks.FURNACE_WORKBENCH_BLOCK_T2);
74+
registerFurnaceWorkbench(blockStateModelGenerator, ModBlocks.FURNACE_WORKBENCH_BLOCK_T1);
75+
registerFurnaceWorkbench(blockStateModelGenerator, ModBlocks.FURNACE_WORKBENCH_BLOCK_T2);
7776
}
7877

79-
private void registerLargeWorkbench(BlockModelGenerators generator, Block block) {
80-
// 1. Get the base identifier (e.g., minetale:block/furnace_workbench_block_t1)
81-
Identifier blockId = ModelLocationUtils.getModelLocation(block);
82-
83-
// 2. Build the references to your manual JSON files
84-
// .withSuffix() creates: minetale:block/furnace_workbench_block_t1_bottom_left, etc.
85-
Identifier bottomLeft = blockId.withSuffix("_bottom_left");
86-
Identifier bottomRight = blockId.withSuffix("_bottom_right");
87-
Identifier topLeft = blockId.withSuffix("_top_left");
88-
Identifier topRight = blockId.withSuffix("_top_right");
89-
Identifier inventory = blockId.withSuffix("_inventory");
78+
/**
79+
* Registers block state variants and the item model for a two-block furnace workbench.
80+
*
81+
* Uses explicit shared model identifiers for the top, bottom, and inventory models, dispatches
82+
* the block state by `DOUBLE_BLOCK_HALF` to select the top or bottom model, applies
83+
* `WORKBENCH_ROTATION` for horizontal orientation, and registers the simple item model.
84+
*
85+
* @param generator the BlockModelGenerators instance used to emit blockstate and item model data
86+
* @param block the furnace workbench block to register models for
87+
*/
88+
private void registerFurnaceWorkbench(BlockModelGenerators generator, Block block) {
89+
Identifier topModel = Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "block/bench/furnace_top");
90+
Identifier bottomModel = Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "block/bench/furnace_bottom");
91+
Identifier inventoryModel = Identifier.fromNamespaceAndPath(MineTale.MOD_ID, "block/bench/furnace_inventory");
9092

91-
// 3. Dispatch to Blockstate (Tells the game which model to show for each state)
9293
generator.blockStateOutput.accept(MultiVariantGenerator.dispatch(block)
93-
.with(PropertyDispatch.initial(BlockStateProperties.DOUBLE_BLOCK_HALF, BlockStateProperties.CHEST_TYPE)
94-
.select(DoubleBlockHalf.LOWER, ChestType.LEFT, BlockModelGenerators.plainVariant(bottomLeft))
95-
.select(DoubleBlockHalf.LOWER, ChestType.RIGHT, BlockModelGenerators.plainVariant(bottomRight))
96-
.select(DoubleBlockHalf.UPPER, ChestType.LEFT, BlockModelGenerators.plainVariant(topLeft))
97-
.select(DoubleBlockHalf.UPPER, ChestType.RIGHT, BlockModelGenerators.plainVariant(topRight))
98-
// Support the 'SINGLE' state as a fallback
99-
.select(DoubleBlockHalf.LOWER, ChestType.SINGLE, BlockModelGenerators.plainVariant(bottomLeft))
100-
.select(DoubleBlockHalf.UPPER, ChestType.SINGLE, BlockModelGenerators.plainVariant(topLeft))
94+
.with(PropertyDispatch.initial(
95+
BlockStateProperties.DOUBLE_BLOCK_HALF,
96+
BlockStateProperties.CHEST_TYPE,
97+
BlockStateProperties.LIT
98+
)
99+
.generate((half, type, lit) -> {
100+
return half == DoubleBlockHalf.UPPER
101+
? BlockModelGenerators.plainVariant(topModel)
102+
: BlockModelGenerators.plainVariant(bottomModel);
103+
})
101104
)
102105
.with(WORKBENCH_ROTATION)
103106
);
104107

105-
// 4. Map the Item in your hand to the inventory JSON
106-
generator.registerSimpleItemModel(block, inventory);
108+
generator.registerSimpleItemModel(block, inventoryModel);
107109
}
108110

109111
/**

src/main/java/com/tcm/MineTale/MineTale.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import static com.tcm.MineTale.item.ModCreativeTab.MINETALE_CREATIVE_TAB;
3434
import static com.tcm.MineTale.item.ModCreativeTab.MINETALE_CREATIVE_TAB_KEY;
3535

36-
import java.util.List;
3736
import java.util.Optional;
3837

3938
public class MineTale implements ModInitializer {

0 commit comments

Comments
 (0)