package net.minecraft.data.loot; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.IntStream; import net.minecraft.advancements.critereon.BlockPredicate; import net.minecraft.advancements.critereon.EnchantmentPredicate; import net.minecraft.advancements.critereon.ItemEnchantmentsPredicate; import net.minecraft.advancements.critereon.ItemPredicate; import net.minecraft.advancements.critereon.ItemSubPredicates; import net.minecraft.advancements.critereon.MinMaxBounds.Ints; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.HolderLookup; import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import net.minecraft.util.StringRepresentable; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.item.enchantment.Enchantments; import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.BeehiveBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.CandleBlock; import net.minecraft.world.level.block.CaveVines; import net.minecraft.world.level.block.DoorBlock; import net.minecraft.world.level.block.DoublePlantBlock; import net.minecraft.world.level.block.FlowerPotBlock; import net.minecraft.world.level.block.MossyCarpetBlock; import net.minecraft.world.level.block.MultifaceBlock; import net.minecraft.world.level.block.PinkPetalsBlock; import net.minecraft.world.level.block.SlabBlock; import net.minecraft.world.level.block.StemBlock; import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.block.state.properties.SlabType; import net.minecraft.world.level.storage.loot.IntRange; 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.LootPoolEntryContainer.Builder; import net.minecraft.world.level.storage.loot.functions.ApplyBonusCount; import net.minecraft.world.level.storage.loot.functions.ApplyExplosionDecay; import net.minecraft.world.level.storage.loot.functions.CopyBlockState; import net.minecraft.world.level.storage.loot.functions.CopyComponentsFunction; import net.minecraft.world.level.storage.loot.functions.FunctionUserBuilder; import net.minecraft.world.level.storage.loot.functions.LimitCount; import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction; import net.minecraft.world.level.storage.loot.predicates.BonusLevelTableCondition; import net.minecraft.world.level.storage.loot.predicates.ConditionUserBuilder; import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition; import net.minecraft.world.level.storage.loot.predicates.LocationCheck; import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition; import net.minecraft.world.level.storage.loot.predicates.LootItemRandomChanceCondition; import net.minecraft.world.level.storage.loot.predicates.MatchTool; import net.minecraft.world.level.storage.loot.providers.number.BinomialDistributionGenerator; import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; import net.minecraft.world.level.storage.loot.providers.number.NumberProvider; import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator; public abstract class BlockLootSubProvider implements LootTableSubProvider { protected final HolderLookup.Provider registries; protected final Set explosionResistant; protected final FeatureFlagSet enabledFeatures; protected final Map, LootTable.Builder> map; protected static final float[] NORMAL_LEAVES_SAPLING_CHANCES = new float[]{0.05F, 0.0625F, 0.083333336F, 0.1F}; private static final float[] NORMAL_LEAVES_STICK_CHANCES = new float[]{0.02F, 0.022222223F, 0.025F, 0.033333335F, 0.1F}; protected net.minecraft.world.level.storage.loot.predicates.LootItemCondition.Builder hasSilkTouch() { HolderLookup.RegistryLookup registryLookup = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return MatchTool.toolMatches( ItemPredicate.Builder.item() .withSubPredicate( ItemSubPredicates.ENCHANTMENTS, ItemEnchantmentsPredicate.enchantments(List.of(new EnchantmentPredicate(registryLookup.getOrThrow(Enchantments.SILK_TOUCH), Ints.atLeast(1)))) ) ); } protected net.minecraft.world.level.storage.loot.predicates.LootItemCondition.Builder doesNotHaveSilkTouch() { return this.hasSilkTouch().invert(); } protected net.minecraft.world.level.storage.loot.predicates.LootItemCondition.Builder hasShears() { return MatchTool.toolMatches(ItemPredicate.Builder.item().of(this.registries.lookupOrThrow(Registries.ITEM), Items.SHEARS)); } private net.minecraft.world.level.storage.loot.predicates.LootItemCondition.Builder hasShearsOrSilkTouch() { return this.hasShears().or(this.hasSilkTouch()); } private net.minecraft.world.level.storage.loot.predicates.LootItemCondition.Builder doesNotHaveShearsOrSilkTouch() { return this.hasShearsOrSilkTouch().invert(); } protected BlockLootSubProvider(Set explosionResistant, FeatureFlagSet enabledFeatures, HolderLookup.Provider registries) { this(explosionResistant, enabledFeatures, new HashMap(), registries); } protected BlockLootSubProvider( Set explosionResistant, FeatureFlagSet enabledFeatures, Map, LootTable.Builder> map, HolderLookup.Provider registries ) { this.explosionResistant = explosionResistant; this.enabledFeatures = enabledFeatures; this.map = map; this.registries = registries; } protected > T applyExplosionDecay(ItemLike item, FunctionUserBuilder functionBuilder) { return !this.explosionResistant.contains(item.asItem()) ? functionBuilder.apply(ApplyExplosionDecay.explosionDecay()) : functionBuilder.unwrap(); } protected > T applyExplosionCondition(ItemLike item, ConditionUserBuilder conditionBuilder) { return !this.explosionResistant.contains(item.asItem()) ? conditionBuilder.when(ExplosionCondition.survivesExplosion()) : conditionBuilder.unwrap(); } public LootTable.Builder createSingleItemTable(ItemLike item) { return LootTable.lootTable() .withPool(this.applyExplosionCondition(item, LootPool.lootPool().setRolls(ConstantValue.exactly(1.0F)).add(LootItem.lootTableItem(item)))); } /** * If the condition from {@code conditionBuilder} succeeds, drops 1 {@code block}. * Otherwise, drops loot specified by {@code alternativeBuilder}. */ private static LootTable.Builder createSelfDropDispatchTable( Block block, net.minecraft.world.level.storage.loot.predicates.LootItemCondition.Builder conditionBuilder, Builder alternativeBuilder ) { return LootTable.lootTable() .withPool(LootPool.lootPool().setRolls(ConstantValue.exactly(1.0F)).add(LootItem.lootTableItem(block).when(conditionBuilder).otherwise(alternativeBuilder))); } /** * If the block is mined with Silk Touch, drops 1 {@code block}. * Otherwise, drops loot specified by {@code builder}. */ protected LootTable.Builder createSilkTouchDispatchTable(Block block, Builder builder) { return createSelfDropDispatchTable(block, this.hasSilkTouch(), builder); } /** * If the block is mined with Shears, drops 1 {@code block}. * Otherwise, drops loot specified by {@code builder}. */ protected LootTable.Builder createShearsDispatchTable(Block block, Builder builder) { return createSelfDropDispatchTable(block, this.hasShears(), builder); } /** * If the block is mined either with Silk Touch or Shears, drops 1 {@code block}. * Otherwise, drops loot specified by {@code builder}. */ protected LootTable.Builder createSilkTouchOrShearsDispatchTable(Block block, Builder builder) { return createSelfDropDispatchTable(block, this.hasShearsOrSilkTouch(), builder); } protected LootTable.Builder createSingleItemTableWithSilkTouch(Block block, ItemLike item) { return this.createSilkTouchDispatchTable(block, (Builder)this.applyExplosionCondition(block, LootItem.lootTableItem(item))); } protected LootTable.Builder createSingleItemTable(ItemLike item, NumberProvider count) { return LootTable.lootTable() .withPool( LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .add((Builder)this.applyExplosionDecay(item, LootItem.lootTableItem(item).apply(SetItemCountFunction.setCount(count)))) ); } protected LootTable.Builder createSingleItemTableWithSilkTouch(Block block, ItemLike item, NumberProvider count) { return this.createSilkTouchDispatchTable( block, (Builder)this.applyExplosionDecay(block, LootItem.lootTableItem(item).apply(SetItemCountFunction.setCount(count))) ); } private LootTable.Builder createSilkTouchOnlyTable(ItemLike item) { return LootTable.lootTable().withPool(LootPool.lootPool().when(this.hasSilkTouch()).setRolls(ConstantValue.exactly(1.0F)).add(LootItem.lootTableItem(item))); } private LootTable.Builder createPotFlowerItemTable(ItemLike item) { return LootTable.lootTable() .withPool( this.applyExplosionCondition(Blocks.FLOWER_POT, LootPool.lootPool().setRolls(ConstantValue.exactly(1.0F)).add(LootItem.lootTableItem(Blocks.FLOWER_POT))) ) .withPool(this.applyExplosionCondition(item, LootPool.lootPool().setRolls(ConstantValue.exactly(1.0F)).add(LootItem.lootTableItem(item)))); } protected LootTable.Builder createSlabItemTable(Block block) { return LootTable.lootTable() .withPool( LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .add( (Builder)this.applyExplosionDecay( block, LootItem.lootTableItem(block) .apply( SetItemCountFunction.setCount(ConstantValue.exactly(2.0F)) .when( LootItemBlockStatePropertyCondition.hasBlockStateProperties(block) .setProperties(net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties().hasProperty(SlabBlock.TYPE, SlabType.DOUBLE)) ) ) ) ) ); } protected & StringRepresentable> LootTable.Builder createSinglePropConditionTable(Block block, Property property, T value) { return LootTable.lootTable() .withPool( this.applyExplosionCondition( block, LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .add( LootItem.lootTableItem(block) .when( LootItemBlockStatePropertyCondition.hasBlockStateProperties(block) .setProperties(net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties().hasProperty(property, value)) ) ) ) ); } protected LootTable.Builder createNameableBlockEntityTable(Block block) { return LootTable.lootTable() .withPool( this.applyExplosionCondition( block, LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .add( LootItem.lootTableItem(block) .apply(CopyComponentsFunction.copyComponents(CopyComponentsFunction.Source.BLOCK_ENTITY).include(DataComponents.CUSTOM_NAME)) ) ) ); } protected LootTable.Builder createShulkerBoxDrop(Block block) { return LootTable.lootTable() .withPool( this.applyExplosionCondition( block, LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .add( LootItem.lootTableItem(block) .apply( CopyComponentsFunction.copyComponents(CopyComponentsFunction.Source.BLOCK_ENTITY) .include(DataComponents.CUSTOM_NAME) .include(DataComponents.CONTAINER) .include(DataComponents.LOCK) .include(DataComponents.CONTAINER_LOOT) ) ) ) ); } protected LootTable.Builder createCopperOreDrops(Block block) { HolderLookup.RegistryLookup registryLookup = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return this.createSilkTouchDispatchTable( block, (Builder)this.applyExplosionDecay( block, LootItem.lootTableItem(Items.RAW_COPPER) .apply(SetItemCountFunction.setCount(UniformGenerator.between(2.0F, 5.0F))) .apply(ApplyBonusCount.addOreBonusCount(registryLookup.getOrThrow(Enchantments.FORTUNE))) ) ); } protected LootTable.Builder createLapisOreDrops(Block block) { HolderLookup.RegistryLookup registryLookup = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return this.createSilkTouchDispatchTable( block, (Builder)this.applyExplosionDecay( block, LootItem.lootTableItem(Items.LAPIS_LAZULI) .apply(SetItemCountFunction.setCount(UniformGenerator.between(4.0F, 9.0F))) .apply(ApplyBonusCount.addOreBonusCount(registryLookup.getOrThrow(Enchantments.FORTUNE))) ) ); } protected LootTable.Builder createRedstoneOreDrops(Block block) { HolderLookup.RegistryLookup registryLookup = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return this.createSilkTouchDispatchTable( block, (Builder)this.applyExplosionDecay( block, LootItem.lootTableItem(Items.REDSTONE) .apply(SetItemCountFunction.setCount(UniformGenerator.between(4.0F, 5.0F))) .apply(ApplyBonusCount.addUniformBonusCount(registryLookup.getOrThrow(Enchantments.FORTUNE))) ) ); } protected LootTable.Builder createBannerDrop(Block block) { return LootTable.lootTable() .withPool( this.applyExplosionCondition( block, LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .add( LootItem.lootTableItem(block) .apply( CopyComponentsFunction.copyComponents(CopyComponentsFunction.Source.BLOCK_ENTITY) .include(DataComponents.CUSTOM_NAME) .include(DataComponents.ITEM_NAME) .include(DataComponents.HIDE_ADDITIONAL_TOOLTIP) .include(DataComponents.BANNER_PATTERNS) .include(DataComponents.RARITY) ) ) ) ); } protected LootTable.Builder createBeeNestDrop(Block block) { return LootTable.lootTable() .withPool( LootPool.lootPool() .when(this.hasSilkTouch()) .setRolls(ConstantValue.exactly(1.0F)) .add( LootItem.lootTableItem(block) .apply(CopyComponentsFunction.copyComponents(CopyComponentsFunction.Source.BLOCK_ENTITY).include(DataComponents.BEES)) .apply(CopyBlockState.copyState(block).copy(BeehiveBlock.HONEY_LEVEL)) ) ); } protected LootTable.Builder createBeeHiveDrop(Block block) { return LootTable.lootTable() .withPool( LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .add( LootItem.lootTableItem(block) .when(this.hasSilkTouch()) .apply(CopyComponentsFunction.copyComponents(CopyComponentsFunction.Source.BLOCK_ENTITY).include(DataComponents.BEES)) .apply(CopyBlockState.copyState(block).copy(BeehiveBlock.HONEY_LEVEL)) .otherwise(LootItem.lootTableItem(block)) ) ); } protected LootTable.Builder createCaveVinesDrop(Block block) { return LootTable.lootTable() .withPool( LootPool.lootPool() .add(LootItem.lootTableItem(Items.GLOW_BERRIES)) .when( LootItemBlockStatePropertyCondition.hasBlockStateProperties(block) .setProperties(net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties().hasProperty(CaveVines.BERRIES, true)) ) ); } protected LootTable.Builder createOreDrop(Block block, Item item) { HolderLookup.RegistryLookup registryLookup = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return this.createSilkTouchDispatchTable( block, (Builder)this.applyExplosionDecay( block, LootItem.lootTableItem(item).apply(ApplyBonusCount.addOreBonusCount(registryLookup.getOrThrow(Enchantments.FORTUNE))) ) ); } protected LootTable.Builder createMushroomBlockDrop(Block block, ItemLike item) { return this.createSilkTouchDispatchTable( block, (Builder)this.applyExplosionDecay( block, LootItem.lootTableItem(item) .apply(SetItemCountFunction.setCount(UniformGenerator.between(-6.0F, 2.0F))) .apply(LimitCount.limitCount(IntRange.lowerBound(0))) ) ); } protected LootTable.Builder createGrassDrops(Block block) { HolderLookup.RegistryLookup registryLookup = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return this.createShearsDispatchTable( block, (Builder)this.applyExplosionDecay( block, LootItem.lootTableItem(Items.WHEAT_SEEDS) .when(LootItemRandomChanceCondition.randomChance(0.125F)) .apply(ApplyBonusCount.addUniformBonusCount(registryLookup.getOrThrow(Enchantments.FORTUNE), 2)) ) ); } /** * Creates a builder that drops the given IItemProvider in amounts between 0 and 3, based on the AGE property. Only used in vanilla for pumpkin and melon stems. */ public LootTable.Builder createStemDrops(Block block, Item item) { return LootTable.lootTable() .withPool( this.applyExplosionDecay( block, LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .add( LootItem.lootTableItem(item) .apply( StemBlock.AGE.getPossibleValues(), integer -> SetItemCountFunction.setCount(BinomialDistributionGenerator.binomial(3, (integer + 1) / 15.0F)) .when( LootItemBlockStatePropertyCondition.hasBlockStateProperties(block) .setProperties(net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties().hasProperty(StemBlock.AGE, integer)) ) ) ) ) ); } public LootTable.Builder createAttachedStemDrops(Block block, Item item) { return LootTable.lootTable() .withPool( this.applyExplosionDecay( block, LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .add(LootItem.lootTableItem(item).apply(SetItemCountFunction.setCount(BinomialDistributionGenerator.binomial(3, 0.53333336F)))) ) ); } protected LootTable.Builder createShearsOnlyDrop(ItemLike item) { return LootTable.lootTable().withPool(LootPool.lootPool().setRolls(ConstantValue.exactly(1.0F)).when(this.hasShears()).add(LootItem.lootTableItem(item))); } protected LootTable.Builder createShearsOrSilkTouchOnlyDrop(ItemLike item) { return LootTable.lootTable() .withPool(LootPool.lootPool().setRolls(ConstantValue.exactly(1.0F)).when(this.hasShearsOrSilkTouch()).add(LootItem.lootTableItem(item))); } protected LootTable.Builder createMultifaceBlockDrops(Block block, net.minecraft.world.level.storage.loot.predicates.LootItemCondition.Builder builder) { return LootTable.lootTable() .withPool( LootPool.lootPool() .add( (Builder)this.applyExplosionDecay( block, LootItem.lootTableItem(block) .when(builder) .apply( Direction.values(), direction -> SetItemCountFunction.setCount(ConstantValue.exactly(1.0F), true) .when( LootItemBlockStatePropertyCondition.hasBlockStateProperties(block) .setProperties( net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties().hasProperty(MultifaceBlock.getFaceProperty(direction), true) ) ) ) .apply(SetItemCountFunction.setCount(ConstantValue.exactly(-1.0F), true)) ) ) ); } protected LootTable.Builder createMossyCarpetBlockDrops(Block block) { return LootTable.lootTable() .withPool( LootPool.lootPool() .add( (Builder)this.applyExplosionDecay( block, LootItem.lootTableItem(block) .when( LootItemBlockStatePropertyCondition.hasBlockStateProperties(block) .setProperties(net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties().hasProperty(MossyCarpetBlock.BASE, true)) ) ) ) ); } /** * Used for all leaves, drops self with silk touch, otherwise drops the second Block param with the passed chances for fortune levels, adding in sticks. */ protected LootTable.Builder createLeavesDrops(Block leavesBlock, Block saplingBlock, float... chances) { HolderLookup.RegistryLookup registryLookup = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return this.createSilkTouchOrShearsDispatchTable( leavesBlock, ((net.minecraft.world.level.storage.loot.entries.LootPoolSingletonContainer.Builder)this.applyExplosionCondition( leavesBlock, LootItem.lootTableItem(saplingBlock) )) .when(BonusLevelTableCondition.bonusLevelFlatChance(registryLookup.getOrThrow(Enchantments.FORTUNE), chances)) ) .withPool( LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .when(this.doesNotHaveShearsOrSilkTouch()) .add( ((net.minecraft.world.level.storage.loot.entries.LootPoolSingletonContainer.Builder)this.applyExplosionDecay( leavesBlock, LootItem.lootTableItem(Items.STICK).apply(SetItemCountFunction.setCount(UniformGenerator.between(1.0F, 2.0F))) )) .when(BonusLevelTableCondition.bonusLevelFlatChance(registryLookup.getOrThrow(Enchantments.FORTUNE), NORMAL_LEAVES_STICK_CHANCES)) ) ); } /** * Used for oak and dark oak, same as droppingWithChancesAndSticks but adding in apples. */ protected LootTable.Builder createOakLeavesDrops(Block oakLeavesBlock, Block saplingBlock, float... chances) { HolderLookup.RegistryLookup registryLookup = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return this.createLeavesDrops(oakLeavesBlock, saplingBlock, chances) .withPool( LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .when(this.doesNotHaveShearsOrSilkTouch()) .add( ((net.minecraft.world.level.storage.loot.entries.LootPoolSingletonContainer.Builder)this.applyExplosionCondition( oakLeavesBlock, LootItem.lootTableItem(Items.APPLE) )) .when( BonusLevelTableCondition.bonusLevelFlatChance(registryLookup.getOrThrow(Enchantments.FORTUNE), 0.005F, 0.0055555557F, 0.00625F, 0.008333334F, 0.025F) ) ) ); } protected LootTable.Builder createMangroveLeavesDrops(Block block) { HolderLookup.RegistryLookup registryLookup = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return this.createSilkTouchOrShearsDispatchTable( block, ((net.minecraft.world.level.storage.loot.entries.LootPoolSingletonContainer.Builder)this.applyExplosionDecay( Blocks.MANGROVE_LEAVES, LootItem.lootTableItem(Items.STICK).apply(SetItemCountFunction.setCount(UniformGenerator.between(1.0F, 2.0F))) )) .when(BonusLevelTableCondition.bonusLevelFlatChance(registryLookup.getOrThrow(Enchantments.FORTUNE), NORMAL_LEAVES_STICK_CHANCES)) ); } /** * If {@code dropGrownCropCondition} fails (i.e. crop is not ready), drops 1 {@code seedsItem}. * If {@code dropGrownCropCondition} succeeds (i.e. crop is ready), drops 1 {@code grownCropItem}, and 0-3 {@code seedsItem} with fortune applied. */ protected LootTable.Builder createCropDrops( Block cropBlock, Item grownCropItem, Item seedsItem, net.minecraft.world.level.storage.loot.predicates.LootItemCondition.Builder dropGrownCropCondition ) { HolderLookup.RegistryLookup registryLookup = this.registries.lookupOrThrow(Registries.ENCHANTMENT); return this.applyExplosionDecay( cropBlock, LootTable.lootTable() .withPool(LootPool.lootPool().add(LootItem.lootTableItem(grownCropItem).when(dropGrownCropCondition).otherwise(LootItem.lootTableItem(seedsItem)))) .withPool( LootPool.lootPool() .when(dropGrownCropCondition) .add( LootItem.lootTableItem(seedsItem) .apply(ApplyBonusCount.addBonusBinomialDistributionCount(registryLookup.getOrThrow(Enchantments.FORTUNE), 0.5714286F, 3)) ) ) ); } protected LootTable.Builder createDoublePlantShearsDrop(Block sheared) { return LootTable.lootTable() .withPool(LootPool.lootPool().when(this.hasShears()).add(LootItem.lootTableItem(sheared).apply(SetItemCountFunction.setCount(ConstantValue.exactly(2.0F))))); } protected LootTable.Builder createDoublePlantWithSeedDrops(Block block, Block sheared) { HolderLookup.RegistryLookup registryLookup = this.registries.lookupOrThrow(Registries.BLOCK); Builder builder = LootItem.lootTableItem(sheared) .apply(SetItemCountFunction.setCount(ConstantValue.exactly(2.0F))) .when(this.hasShears()) .otherwise( ((net.minecraft.world.level.storage.loot.entries.LootPoolSingletonContainer.Builder)this.applyExplosionCondition( block, LootItem.lootTableItem(Items.WHEAT_SEEDS) )) .when(LootItemRandomChanceCondition.randomChance(0.125F)) ); return LootTable.lootTable() .withPool( LootPool.lootPool() .add(builder) .when( LootItemBlockStatePropertyCondition.hasBlockStateProperties(block) .setProperties( net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties().hasProperty(DoublePlantBlock.HALF, DoubleBlockHalf.LOWER) ) ) .when( LocationCheck.checkLocation( net.minecraft.advancements.critereon.LocationPredicate.Builder.location() .setBlock( BlockPredicate.Builder.block() .of(registryLookup, block) .setProperties( net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties().hasProperty(DoublePlantBlock.HALF, DoubleBlockHalf.UPPER) ) ), new BlockPos(0, 1, 0) ) ) ) .withPool( LootPool.lootPool() .add(builder) .when( LootItemBlockStatePropertyCondition.hasBlockStateProperties(block) .setProperties( net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties().hasProperty(DoublePlantBlock.HALF, DoubleBlockHalf.UPPER) ) ) .when( LocationCheck.checkLocation( net.minecraft.advancements.critereon.LocationPredicate.Builder.location() .setBlock( BlockPredicate.Builder.block() .of(registryLookup, block) .setProperties( net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties().hasProperty(DoublePlantBlock.HALF, DoubleBlockHalf.LOWER) ) ), new BlockPos(0, -1, 0) ) ) ); } protected LootTable.Builder createCandleDrops(Block candleBlock) { return LootTable.lootTable() .withPool( LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .add( (Builder)this.applyExplosionDecay( candleBlock, LootItem.lootTableItem(candleBlock) .apply( List.of(2, 3, 4), integer -> SetItemCountFunction.setCount(ConstantValue.exactly(integer.intValue())) .when( LootItemBlockStatePropertyCondition.hasBlockStateProperties(candleBlock) .setProperties(net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties().hasProperty(CandleBlock.CANDLES, integer)) ) ) ) ) ); } protected LootTable.Builder createPetalsDrops(Block petalBlock) { return LootTable.lootTable() .withPool( LootPool.lootPool() .setRolls(ConstantValue.exactly(1.0F)) .add( (Builder)this.applyExplosionDecay( petalBlock, LootItem.lootTableItem(petalBlock) .apply( IntStream.rangeClosed(1, 4).boxed().toList(), integer -> SetItemCountFunction.setCount(ConstantValue.exactly(integer.intValue())) .when( LootItemBlockStatePropertyCondition.hasBlockStateProperties(petalBlock) .setProperties(net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties().hasProperty(PinkPetalsBlock.AMOUNT, integer)) ) ) ) ) ); } protected static LootTable.Builder createCandleCakeDrops(Block candleCakeBlock) { return LootTable.lootTable().withPool(LootPool.lootPool().setRolls(ConstantValue.exactly(1.0F)).add(LootItem.lootTableItem(candleCakeBlock))); } public static LootTable.Builder noDrop() { return LootTable.lootTable(); } protected abstract void generate(); @Override public void generate(BiConsumer, LootTable.Builder> biConsumer) { this.generate(); Set> set = new HashSet(); for (Block block : BuiltInRegistries.BLOCK) { if (block.isEnabled(this.enabledFeatures)) { block.getLootTable() .ifPresent( resourceKey -> { if (set.add(resourceKey)) { LootTable.Builder builder = (LootTable.Builder)this.map.remove(resourceKey); if (builder == null) { throw new IllegalStateException( String.format(Locale.ROOT, "Missing loottable '%s' for '%s'", resourceKey.location(), BuiltInRegistries.BLOCK.getKey(block)) ); } biConsumer.accept(resourceKey, builder); } } ); } } if (!this.map.isEmpty()) { throw new IllegalStateException("Created block loot tables for non-blocks: " + this.map.keySet()); } } protected void addNetherVinesDropTable(Block vines, Block plant) { HolderLookup.RegistryLookup registryLookup = this.registries.lookupOrThrow(Registries.ENCHANTMENT); LootTable.Builder builder = this.createSilkTouchOrShearsDispatchTable( vines, LootItem.lootTableItem(vines) .when(BonusLevelTableCondition.bonusLevelFlatChance(registryLookup.getOrThrow(Enchantments.FORTUNE), 0.33F, 0.55F, 0.77F, 1.0F)) ); this.add(vines, builder); this.add(plant, builder); } protected LootTable.Builder createDoorTable(Block doorBlock) { return this.createSinglePropConditionTable(doorBlock, DoorBlock.HALF, DoubleBlockHalf.LOWER); } protected void dropPottedContents(Block flowerPot) { this.add(flowerPot, block -> this.createPotFlowerItemTable(((FlowerPotBlock)block).getPotted())); } protected void otherWhenSilkTouch(Block block, Block other) { this.add(block, this.createSilkTouchOnlyTable(other)); } protected void dropOther(Block block, ItemLike item) { this.add(block, this.createSingleItemTable(item)); } protected void dropWhenSilkTouch(Block block) { this.otherWhenSilkTouch(block, block); } protected void dropSelf(Block block) { this.dropOther(block, block); } protected void add(Block block, Function factory) { this.add(block, (LootTable.Builder)factory.apply(block)); } protected void add(Block block, LootTable.Builder builder) { this.map.put((ResourceKey)block.getLootTable().orElseThrow(() -> new IllegalStateException("Block " + block + " does not have loot table")), builder); } }