From ad2ac0fbff7a0936fb81b47fc5100a95118cf403 Mon Sep 17 00:00:00 2001 From: AnyaPizza Date: Thu, 19 Feb 2026 19:33:58 -0500 Subject: [PATCH 1/5] Added Loot Table datagen. Added sticks to the short/tall grass loot table. --- .../com/tcm/MineTale/MineTaleDataGen.java | 6 +- .../MineTale/datagen/ModBlockTagProvider.java | 9 +++ .../datagen/ModLootTableProvider.java | 65 +++++++++++++++++++ src/main/java/com/tcm/MineTale/MineTale.java | 5 +- .../com/tcm/MineTale/registry/ModBlocks.java | 11 +++- .../com/tcm/MineTale/registry/ModItems.java | 8 +++ .../MineTale/util/ModLootTableModifiers.java | 37 +++++++++++ 7 files changed, 133 insertions(+), 8 deletions(-) create mode 100644 src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java create mode 100644 src/main/java/com/tcm/MineTale/util/ModLootTableModifiers.java diff --git a/src/client/java/com/tcm/MineTale/MineTaleDataGen.java b/src/client/java/com/tcm/MineTale/MineTaleDataGen.java index e4bfe19..d1003a8 100644 --- a/src/client/java/com/tcm/MineTale/MineTaleDataGen.java +++ b/src/client/java/com/tcm/MineTale/MineTaleDataGen.java @@ -1,9 +1,6 @@ package com.tcm.MineTale; -import com.tcm.MineTale.datagen.ModBlockTagProvider; -import com.tcm.MineTale.datagen.ModLangProvider; -import com.tcm.MineTale.datagen.ModModelProvider; -import com.tcm.MineTale.datagen.ModRecipeProvider; +import com.tcm.MineTale.datagen.*; import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint; import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; @@ -26,5 +23,6 @@ public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { pack.addProvider(ModModelProvider::new); pack.addProvider(ModRecipeProvider::new); pack.addProvider(ModBlockTagProvider::new); + pack.addProvider(ModLootTableProvider::new); } } \ No newline at end of file diff --git a/src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java b/src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java index 21e9aec..c11025b 100644 --- a/src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java +++ b/src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java @@ -1,5 +1,6 @@ package com.tcm.MineTale.datagen; +import com.jcraft.jorbis.Block; import com.tcm.MineTale.registry.ModBlocks; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider; @@ -28,6 +29,14 @@ public ModBlockTagProvider(FabricDataOutput output, CompletableFuture registryLookup) { + super(dataOutput, registryLookup); + } + + @Override + public void generate() { + ///Block Drops Itself + //Bug!! Workbenches currently drop items based on the amount of blocks + //the multiblock is. For example the Furnaces currently drop 4 furnaces. + dropSelf(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1); + dropSelf(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1); + dropSelf(ModBlocks.ARMORERS_WORKBENCH_BLOCK); + dropSelf(ModBlocks.CAMPFIRE_WORKBENCH_BLOCK); + dropSelf(ModBlocks.WORKBENCH_WORKBENCH_BLOCK); + } + + +/// For Ore Drops +/** Ex: + * add(ModBlocks.IRON_ORE_SHALE, AverageOreDrops(ModBlocks.IRON_ORE_SHALE, Items.RAW_IRON)); + * **/ + public LootTable.Builder AverageOreDrops(Block drop, Item item) { + HolderLookup.RegistryLookup impl = this.registries.lookupOrThrow(Registries.ENCHANTMENT); + return this.createSilkTouchDispatchTable(drop, this.applyExplosionDecay(drop, ((LootPoolSingletonContainer.Builder) + LootItem.lootTableItem(item).apply(SetItemCountFunction.setCount(UniformGenerator.between(2, 5)))) + .apply(ApplyBonusCount.addOreBonusCount(impl.getOrThrow(Enchantments.FORTUNE))))); + } + + public LootTable.Builder SingleOreDrops(Block drop, Item item) { + HolderLookup.RegistryLookup impl = this.registries.lookupOrThrow(Registries.ENCHANTMENT); + return this.createSilkTouchDispatchTable(drop, this.applyExplosionDecay(drop, ((LootPoolSingletonContainer.Builder) + LootItem.lootTableItem(item).apply(SetItemCountFunction.setCount(ConstantValue.exactly(1)))) + .apply(ApplyBonusCount.addOreBonusCount(impl.getOrThrow(Enchantments.FORTUNE))))); + } + + public LootTable.Builder LightOreDrops(Block drop, Item item) { + HolderLookup.RegistryLookup impl = this.registries.lookupOrThrow(Registries.ENCHANTMENT); + return this.createSilkTouchDispatchTable(drop, this.applyExplosionDecay(drop, ((LootPoolSingletonContainer.Builder) + LootItem.lootTableItem(item).apply(SetItemCountFunction.setCount(UniformGenerator.between(1, 2)))) + .apply(ApplyBonusCount.addOreBonusCount(impl.getOrThrow(Enchantments.FORTUNE))))); + } +} diff --git a/src/main/java/com/tcm/MineTale/MineTale.java b/src/main/java/com/tcm/MineTale/MineTale.java index 6d3686d..645e62d 100644 --- a/src/main/java/com/tcm/MineTale/MineTale.java +++ b/src/main/java/com/tcm/MineTale/MineTale.java @@ -1,5 +1,6 @@ package com.tcm.MineTale; +import com.tcm.MineTale.util.ModLootTableModifiers; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; @@ -72,6 +73,8 @@ public void onInitialize() { RecipeSynchronization.synchronizeRecipeSerializer(ModRecipes.FURNACE_SERIALIZER); + ModLootTableModifiers.modifyLootTables(); + // Register the payload type and codec so the game knows how to handle it PayloadTypeRegistry.playC2S().register(CraftRequestPayload.TYPE, CraftRequestPayload.CODEC); @@ -129,7 +132,7 @@ public void onInitialize() { }); }); - LOGGER.info("Hello Fabric world!"); + LOGGER.info("MineTale Loaded!"); } private boolean hasIngredients(ServerPlayer player, WorkbenchRecipe recipe) { diff --git a/src/main/java/com/tcm/MineTale/registry/ModBlocks.java b/src/main/java/com/tcm/MineTale/registry/ModBlocks.java index 22ac439..f2096e6 100644 --- a/src/main/java/com/tcm/MineTale/registry/ModBlocks.java +++ b/src/main/java/com/tcm/MineTale/registry/ModBlocks.java @@ -28,6 +28,7 @@ import net.minecraft.world.level.material.MapColor; import net.minecraft.world.level.block.Blocks; +import static net.minecraft.world.level.block.Blocks.litBlockEmission; import static net.minecraft.world.level.block.Blocks.logProperties; public class ModBlocks { @@ -59,14 +60,18 @@ public class ModBlocks { public static final Block FURNACE_WORKBENCH_BLOCK_T1 = register( "furnace_workbench_block_t1", (props) -> new FurnaceWorkbench(props, ModTiers.TIER_1), - BlockBehaviour.Properties.of().sound(SoundType.STONE).noOcclusion(), - true + BlockBehaviour.Properties.of().sound(SoundType.STONE).noOcclusion() + .requiresCorrectToolForDrops().strength(3.5F) + .lightLevel(litBlockEmission(13)), + true ); public static final Block FURNACE_WORKBENCH_BLOCK_T2 = register( "furnace_workbench_block_t2", (props) -> new FurnaceWorkbench(props, ModTiers.TIER_2), - BlockBehaviour.Properties.of().sound(SoundType.STONE).noOcclusion(), + BlockBehaviour.Properties.of().sound(SoundType.STONE).noOcclusion() + .requiresCorrectToolForDrops().strength(3.5F) + .lightLevel(litBlockEmission(13)), true ); diff --git a/src/main/java/com/tcm/MineTale/registry/ModItems.java b/src/main/java/com/tcm/MineTale/registry/ModItems.java index 47aef66..7d7a95b 100644 --- a/src/main/java/com/tcm/MineTale/registry/ModItems.java +++ b/src/main/java/com/tcm/MineTale/registry/ModItems.java @@ -3,18 +3,26 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Function; +import java.util.function.UnaryOperator; import com.tcm.MineTale.MineTale; import com.tcm.MineTale.item.ModCreativeTab; import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents; import net.minecraft.core.Registry; +import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; import net.minecraft.world.food.FoodProperties; +import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.component.ItemContainerContents; +import net.minecraft.world.level.block.Block; + +import static net.minecraft.world.item.Items.registerBlock; public class ModItems { diff --git a/src/main/java/com/tcm/MineTale/util/ModLootTableModifiers.java b/src/main/java/com/tcm/MineTale/util/ModLootTableModifiers.java new file mode 100644 index 0000000..2b8d624 --- /dev/null +++ b/src/main/java/com/tcm/MineTale/util/ModLootTableModifiers.java @@ -0,0 +1,37 @@ +package com.tcm.MineTale.util; + +import net.fabricmc.fabric.api.loot.v3.LootTableEvents; +import net.minecraft.world.item.Items; +import net.minecraft.resources.Identifier; +import net.minecraft.world.level.storage.loot.LootPool; +import net.minecraft.world.level.storage.loot.entries.LootItem; +import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction; +import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; +import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator; + +public class ModLootTableModifiers { + private static final Identifier SHORT_GRASS_ID = Identifier.fromNamespaceAndPath("minecraft", "blocks/short_grass"); + private static final Identifier TALL_GRASS_ID = Identifier.fromNamespaceAndPath("minecraft", "blocks/tall_grass"); + + public static void modifyLootTables() { + LootTableEvents.MODIFY.register((key, tableBuilder, sources, registry) -> { + if (SHORT_GRASS_ID.equals(key.identifier())) { + LootPool.Builder poolBuilder = LootPool.lootPool() + .setRolls(ConstantValue.exactly(1)) + .add(LootItem.lootTableItem(Items.AIR) + .apply(SetItemCountFunction.setCount(UniformGenerator.between(1f, 1f)))) + .add(LootItem.lootTableItem(Items.STICK) + .apply(SetItemCountFunction.setCount(UniformGenerator.between(1f, 3f)))); + tableBuilder.pool(poolBuilder.build()); + } + + if (TALL_GRASS_ID.equals(key.identifier())) { + LootPool.Builder poolBuilder = LootPool.lootPool() + .setRolls(ConstantValue.exactly(1)) + .add(LootItem.lootTableItem(Items.STICK).setWeight(1) + .apply(SetItemCountFunction.setCount(UniformGenerator.between(1f, 4f)))); + tableBuilder.pool(poolBuilder.build()); + } + }); + } +} From 7e62858f4c7b08fd503f719215017fac5e87397e Mon Sep 17 00:00:00 2001 From: Andy Wilson Date: Fri, 20 Feb 2026 20:48:49 +0000 Subject: [PATCH 2/5] fix: block now drops one item --- .../datagen/ModLootTableProvider.java | 26 +++++++++- .../block/workbenches/AbstractWorkbench.java | 51 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java b/src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java index 45f6b5f..0078d46 100644 --- a/src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java +++ b/src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java @@ -1,20 +1,27 @@ package com.tcm.MineTale.datagen; +import com.tcm.MineTale.block.workbenches.AbstractWorkbench; import com.tcm.MineTale.registry.ModBlocks; import com.tcm.MineTale.registry.ModItems; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider; +import net.minecraft.advancements.criterion.StatePropertiesPredicate; import net.minecraft.core.HolderLookup; import net.minecraft.core.registries.Registries; import net.minecraft.world.item.Item; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.properties.ChestType; +import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; +import net.minecraft.world.level.storage.loot.LootPool; import net.minecraft.world.level.storage.loot.LootTable; import net.minecraft.world.level.storage.loot.entries.LootItem; import net.minecraft.world.level.storage.loot.entries.LootPoolSingletonContainer; import net.minecraft.world.level.storage.loot.functions.ApplyBonusCount; import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction; +import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition; +import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition; import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator; @@ -30,11 +37,26 @@ public void generate() { ///Block Drops Itself //Bug!! Workbenches currently drop items based on the amount of blocks //the multiblock is. For example the Furnaces currently drop 4 furnaces. - dropSelf(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1); - dropSelf(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1); + // dropSelf(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1); + // dropSelf(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1); dropSelf(ModBlocks.ARMORERS_WORKBENCH_BLOCK); dropSelf(ModBlocks.CAMPFIRE_WORKBENCH_BLOCK); dropSelf(ModBlocks.WORKBENCH_WORKBENCH_BLOCK); + + this.add(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1, + LootTable.lootTable() // Use the static factory method to start the builder + .withPool(LootPool.lootPool() + .setRolls(ConstantValue.exactly(1.0F)) + .add(LootItem.lootTableItem(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1)) + .when(LootItemBlockStatePropertyCondition.hasBlockStateProperties(ModBlocks.FURNACE_WORKBENCH_BLOCK_T1) + .setProperties(StatePropertiesPredicate.Builder.properties() + .hasProperty(AbstractWorkbench.HALF, DoubleBlockHalf.LOWER) + .hasProperty(AbstractWorkbench.TYPE, ChestType.LEFT) + ) + ) + .when(ExplosionCondition.survivesExplosion()) + ) + ); } diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/AbstractWorkbench.java b/src/main/java/com/tcm/MineTale/block/workbenches/AbstractWorkbench.java index fc9e7d6..13228e7 100644 --- a/src/main/java/com/tcm/MineTale/block/workbenches/AbstractWorkbench.java +++ b/src/main/java/com/tcm/MineTale/block/workbenches/AbstractWorkbench.java @@ -23,6 +23,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.*; +import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; @@ -33,6 +34,8 @@ import java.util.function.Supplier; +// DoorBlock + public abstract class AbstractWorkbench extends BaseEntityBlock { public static final EnumProperty FACING = HorizontalDirectionalBlock.FACING; public static final EnumProperty HALF = BlockStateProperties.DOUBLE_BLOCK_HALF; @@ -351,4 +354,52 @@ protected float getShadeBrightness(BlockState state, BlockGetter level, BlockPos // We want 1.0F to ensure the block doesn't cast a pitch-black shadow on itself. return 1.0F; } + + @Override + public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) { + if (!level.isClientSide()) { + BlockPos masterPos = getMasterPos(state, pos); + boolean isMaster = pos.equals(masterPos); + + // Iterate through the entire 2x2 or 1x2 structure + for (int y = 0; y <= (isTall ? 1 : 0); y++) { + for (int x = 0; x <= (isWide ? 1 : 0); x++) { + Direction facing = state.getValue(FACING); + BlockPos targetPos = masterPos.above(y).relative(facing.getClockWise(), x); + + // Skip the block the player is currently mining + if (targetPos.equals(pos)) continue; + + BlockState targetState = level.getBlockState(targetPos); + if (targetState.is(this) && getMasterPos(targetState, targetPos).equals(masterPos)) { + if (isMaster) { + // If we are mining the Master, destroy others SILENTLY + level.setBlock(targetPos, Blocks.AIR.defaultBlockState(), 35); + } else { + // If we are mining a Slave block, we need to handle the Master carefully. + // If the Master is at this targetPos, destroy it WITH drops. + if (targetPos.equals(masterPos)) { + level.destroyBlock(targetPos, !player.isCreative()); + } else { + // Otherwise, it's just another slave block, remove silently. + level.setBlock(targetPos, Blocks.AIR.defaultBlockState(), 35); + } + } + level.gameEvent(GameEvent.BLOCK_DESTROY, targetPos, GameEvent.Context.of(player, targetState)); + } + } + } + + // Final tweak: If the player is mining a SLAVE block, + // we must prevent the slave block itself from dropping. + if (!isMaster && !player.isCreative()) { + // This prevents the current block from dropping its loot table + // because we already triggered the Master's drop above. + state = state.setValue(BlockStateProperties.LIT, false); // Optional: change state to desync loot + level.setBlock(pos, Blocks.AIR.defaultBlockState(), 35); + } + } + + return super.playerWillDestroy(level, pos, state, player); + } } \ No newline at end of file From 8caa3d3309faeeb5e3a2514870709ef8d9e3e17b Mon Sep 17 00:00:00 2001 From: Andy Wilson Date: Fri, 20 Feb 2026 21:03:37 +0000 Subject: [PATCH 3/5] fix: add other workbench loot tables --- .../datagen/ModLootTableProvider.java | 66 +++++++++++++++++-- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java b/src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java index 0078d46..955079a 100644 --- a/src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java +++ b/src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java @@ -35,13 +35,52 @@ public ModLootTableProvider(FabricDataOutput dataOutput, CompletableFuture Date: Fri, 20 Feb 2026 21:03:52 +0000 Subject: [PATCH 4/5] fix: remove leftover drop self --- .../java/com/tcm/MineTale/datagen/ModLootTableProvider.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java b/src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java index 955079a..123c9ac 100644 --- a/src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java +++ b/src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java @@ -35,8 +35,6 @@ public ModLootTableProvider(FabricDataOutput dataOutput, CompletableFuture Date: Fri, 20 Feb 2026 21:32:48 +0000 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`Loo?= =?UTF-8?q?t-Table-Update`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @The-Code-Monkey. * https://github.com/CodeMonkeysMods/MineTale/pull/49#issuecomment-3930967545 The following files were modified: * `src/client/java/com/tcm/MineTale/MineTaleDataGen.java` * `src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java` * `src/client/java/com/tcm/MineTale/datagen/ModLootTableProvider.java` * `src/main/java/com/tcm/MineTale/MineTale.java` * `src/main/java/com/tcm/MineTale/block/workbenches/AbstractWorkbench.java` * `src/main/java/com/tcm/MineTale/util/ModLootTableModifiers.java` --- .../com/tcm/MineTale/MineTaleDataGen.java | 6 +-- .../MineTale/datagen/ModBlockTagProvider.java | 8 ++-- .../datagen/ModLootTableProvider.java | 46 +++++++++++++++++-- src/main/java/com/tcm/MineTale/MineTale.java | 11 ++++- .../block/workbenches/AbstractWorkbench.java | 18 ++++++++ .../MineTale/util/ModLootTableModifiers.java | 12 ++++- 6 files changed, 89 insertions(+), 12 deletions(-) diff --git a/src/client/java/com/tcm/MineTale/MineTaleDataGen.java b/src/client/java/com/tcm/MineTale/MineTaleDataGen.java index d1003a8..bc4ace5 100644 --- a/src/client/java/com/tcm/MineTale/MineTaleDataGen.java +++ b/src/client/java/com/tcm/MineTale/MineTaleDataGen.java @@ -8,10 +8,10 @@ public class MineTaleDataGen implements DataGeneratorEntrypoint { /** - * Initialize a data pack and register the mod's data providers. + * Initialize a data pack and register the mod's data providers for data generation. * - * Creates a data pack from the given Fabric data generator and adds the language - * and model providers so they will run during data generation. + * Registers language, model, recipe, block tag, and loot table providers so they + * will run as part of the Fabric data generation pack created from the given generator. * * @param fabricDataGenerator the Fabric data generator used to create the data pack */ diff --git a/src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java b/src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java index c11025b..6c306aa 100644 --- a/src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java +++ b/src/client/java/com/tcm/MineTale/datagen/ModBlockTagProvider.java @@ -21,11 +21,13 @@ public ModBlockTagProvider(FabricDataOutput output, CompletableFuture registryLookup) { super(dataOutput, registryLookup); } + /** + * Registers loot tables for the mod's workbench blocks. + * + * Each added loot table causes the block to drop itself (one item) only when the block's state + * matches the required DoubleBlockHalf (LOWER) and ChestType (LEFT or SINGLE) for that block, + * and the drop is subject to explosion survival/decay. + */ @Override public void generate() { ///Block Drops Itself @@ -113,9 +128,13 @@ public void generate() { /// For Ore Drops -/** Ex: - * add(ModBlocks.IRON_ORE_SHALE, AverageOreDrops(ModBlocks.IRON_ORE_SHALE, Items.RAW_IRON)); - * **/ +/** + * Create a loot table builder that drops the specified item in multiple quantities with Silk Touch, Fortune bonus, and explosion decay applied. + * + * @param drop the source block used for Silk Touch dispatch and explosion-decay context + * @param item the item to drop from the ore + * @return a LootTable.Builder that drops `item` in a base count between 2 and 5, augmented by the Fortune enchantment, with Silk Touch handling and explosion decay applied + */ public LootTable.Builder AverageOreDrops(Block drop, Item item) { HolderLookup.RegistryLookup impl = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return this.createSilkTouchDispatchTable(drop, this.applyExplosionDecay(drop, ((LootPoolSingletonContainer.Builder) @@ -123,6 +142,17 @@ public LootTable.Builder AverageOreDrops(Block drop, Item item) { .apply(ApplyBonusCount.addOreBonusCount(impl.getOrThrow(Enchantments.FORTUNE))))); } + /** + * Creates a loot table builder for an ore that yields the specified item with Silk Touch and Fortune handling. + * + * The table gives a base drop count of exactly 1 (before Fortune), increases the count with Fortune, returns the + * ore block when mined with Silk Touch, and applies explosion decay to the drop. + * + * @param drop the ore block (returned when Silk Touch is used) + * @param item the item to drop when the ore is mined without Silk Touch + * @return a LootTable.Builder configured to drop the specified item with Fortune bonuses, Silk Touch dispatch, + * and explosion decay + */ public LootTable.Builder SingleOreDrops(Block drop, Item item) { HolderLookup.RegistryLookup impl = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return this.createSilkTouchDispatchTable(drop, this.applyExplosionDecay(drop, ((LootPoolSingletonContainer.Builder) @@ -130,10 +160,18 @@ public LootTable.Builder SingleOreDrops(Block drop, Item item) { .apply(ApplyBonusCount.addOreBonusCount(impl.getOrThrow(Enchantments.FORTUNE))))); } + /** + * Builds a loot table for a "light" ore block that supports Silk Touch, Fortune bonuses, and explosion decay. + * + * @param drop the ore block whose loot table is being created + * @param item the item to drop from the ore when not Silk Touched + * @return a LootTable.Builder that drops {@code item} in quantities of 1–2 (before Fortune), applies Fortune bonus, + * dispatches to Silk Touch drops when applicable, and respects explosion decay + */ public LootTable.Builder LightOreDrops(Block drop, Item item) { HolderLookup.RegistryLookup impl = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return this.createSilkTouchDispatchTable(drop, this.applyExplosionDecay(drop, ((LootPoolSingletonContainer.Builder) LootItem.lootTableItem(item).apply(SetItemCountFunction.setCount(UniformGenerator.between(1, 2)))) .apply(ApplyBonusCount.addOreBonusCount(impl.getOrThrow(Enchantments.FORTUNE))))); } -} +} \ No newline at end of file diff --git a/src/main/java/com/tcm/MineTale/MineTale.java b/src/main/java/com/tcm/MineTale/MineTale.java index 645e62d..dac500e 100644 --- a/src/main/java/com/tcm/MineTale/MineTale.java +++ b/src/main/java/com/tcm/MineTale/MineTale.java @@ -135,7 +135,16 @@ public void onInitialize() { LOGGER.info("MineTale Loaded!"); } - private boolean hasIngredients(ServerPlayer player, WorkbenchRecipe recipe) { + /** + * Determines whether the player (and nearby pullable chests, if the workbench allows) collectively contain all item ingredients required by the given workbench recipe. + * + * This check requires the player's open container to be an AbstractWorkbenchContainerMenu; if it is not, the method returns `false`. It examines the player's non-equipment inventory and, when the workbench permits pulling, the contents of nearby inventories. Each recipe ingredient must be satisfied by a distinct matching item instance from those inventories. + * + * @param player the server player whose inventories are checked + * @param recipe the workbench recipe whose ingredient requirements are being validated + * @return `true` if all ingredients of the recipe can be satisfied from the player and allowed nearby inventories, `false` otherwise + */ + private boolean hasIngredients(ServerPlayer player, WorkbenchRecipe recipe) { if (!(player.containerMenu instanceof AbstractWorkbenchContainerMenu menu)) return false; AbstractWorkbenchEntity be = menu.getBlockEntity(); diff --git a/src/main/java/com/tcm/MineTale/block/workbenches/AbstractWorkbench.java b/src/main/java/com/tcm/MineTale/block/workbenches/AbstractWorkbench.java index 13228e7..6c6de09 100644 --- a/src/main/java/com/tcm/MineTale/block/workbenches/AbstractWorkbench.java +++ b/src/main/java/com/tcm/MineTale/block/workbenches/AbstractWorkbench.java @@ -355,6 +355,24 @@ protected float getShadeBrightness(BlockState state, BlockGetter level, BlockPos return 1.0F; } + /** + * Handle teardown of a multi-block workbench when a player destroys one of its parts, + * ensuring the master/slave parts are removed consistently and loot is produced exactly once. + * + *

Server-side behavior: + * - Computes the master (bottom-left) position for the workbench and whether the broken part is the master. + * - Iterates the workbench footprint (2x2 if wide and tall, or the corresponding subset) and removes other parts: + * - If the master is broken, other parts are removed silently (no drops). + * - If a slave is broken, the master is destroyed (producing drops unless the player is in creative) and other slaves are removed silently. + * - Emits GameEvent.BLOCK_DESTROY for each part that is removed. + * - If a slave was broken by a non-creative player, prevents the slave part itself from dropping to avoid duplicate loot. + * + * @param level the world where the destruction occurs + * @param pos the position of the part being destroyed + * @param state the block state of the part being destroyed (may be modified to suppress drops) + * @param player the player performing the destruction + * @return the BlockState returned by the superclass implementation after custom teardown handling + */ @Override public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) { if (!level.isClientSide()) { diff --git a/src/main/java/com/tcm/MineTale/util/ModLootTableModifiers.java b/src/main/java/com/tcm/MineTale/util/ModLootTableModifiers.java index 2b8d624..80557e4 100644 --- a/src/main/java/com/tcm/MineTale/util/ModLootTableModifiers.java +++ b/src/main/java/com/tcm/MineTale/util/ModLootTableModifiers.java @@ -13,6 +13,16 @@ public class ModLootTableModifiers { private static final Identifier SHORT_GRASS_ID = Identifier.fromNamespaceAndPath("minecraft", "blocks/short_grass"); private static final Identifier TALL_GRASS_ID = Identifier.fromNamespaceAndPath("minecraft", "blocks/tall_grass"); + /** + * Registers a listener that modifies the loot tables for short and tall grass. + * + *

When the listener sees the short grass loot table, it replaces its pools with a single-roll + * pool that can produce AIR (count exactly 1) and STICK (count between 1 and 3). When the listener + * sees the tall grass loot table, it replaces its pools with a single-roll pool that can produce + * STICK (weight 1, count between 1 and 4). + * + *

This method registers the modification via LootTableEvents.MODIFY. + */ public static void modifyLootTables() { LootTableEvents.MODIFY.register((key, tableBuilder, sources, registry) -> { if (SHORT_GRASS_ID.equals(key.identifier())) { @@ -34,4 +44,4 @@ public static void modifyLootTables() { } }); } -} +} \ No newline at end of file