From 7824c8c5ed0f3626cfa4d4f51c64607f4b676d8c Mon Sep 17 00:00:00 2001 From: TheRealWormbo Date: Mon, 6 May 2024 23:27:05 +0200 Subject: [PATCH] Pasture seeds work more like random grass spread Whether a block can be a source or target of spreading are determined the same way as random grass or mycelium spread. Spread is no longer restricted to one Y-level and considers water or covered blocks properly. (fixes #4251) Additionally, the replaceable blocks are now defined by a block tag (`#botania:pasture_seed_replaceable`), which also includes mycelium by default now. --- .../bfa01a6ca2555c100103725bf5c9e6da285f29c3 | 1 + .../tags/blocks/pasture_seed_replaceable.json | 8 +++ .../botania/common/item/GrassSeedsItem.java | 72 ++++++++++++------- .../botania/common/lib/BotaniaTags.java | 5 ++ .../vazkii/botania/data/BlockTagProvider.java | 2 + 5 files changed, 64 insertions(+), 24 deletions(-) create mode 100644 Xplat/src/generated/resources/data/botania/tags/blocks/pasture_seed_replaceable.json diff --git a/Xplat/src/generated/resources/.cache/bfa01a6ca2555c100103725bf5c9e6da285f29c3 b/Xplat/src/generated/resources/.cache/bfa01a6ca2555c100103725bf5c9e6da285f29c3 index 33a2733c80..be8e99e4c5 100644 --- a/Xplat/src/generated/resources/.cache/bfa01a6ca2555c100103725bf5c9e6da285f29c3 +++ b/Xplat/src/generated/resources/.cache/bfa01a6ca2555c100103725bf5c9e6da285f29c3 @@ -28,6 +28,7 @@ fdccf413f94f9bc54ddb3cd8681d976fcba945d0 data/botania/tags/blocks/marimorphosis_ 9d560acc5024d61903ea8afc12ab496a7fb0d708 data/botania/tags/blocks/misc_special_flowers.json 482e0e7e492741201a883a48ee6806b5a4d4e51f data/botania/tags/blocks/mundane_floating_flowers.json d7dcc983bfdaf90fe776e2be93411250c03b9e7e data/botania/tags/blocks/mystical_flowers.json +5f23216e4bb6dd5bd6ac046b1d203d8f002a4d86 data/botania/tags/blocks/pasture_seed_replaceable.json 6164f1b08110e8a36397260f3afecc09a6ced65a data/botania/tags/blocks/shiny_flowers.json 278b6fb5ddf99d4fc9626494295fa7c5a11fc906 data/botania/tags/blocks/special_floating_flowers.json 7131d151b6322c92982ca3cdff678eb194b8d38a data/botania/tags/blocks/special_flowers.json diff --git a/Xplat/src/generated/resources/data/botania/tags/blocks/pasture_seed_replaceable.json b/Xplat/src/generated/resources/data/botania/tags/blocks/pasture_seed_replaceable.json new file mode 100644 index 0000000000..a2e8277e61 --- /dev/null +++ b/Xplat/src/generated/resources/data/botania/tags/blocks/pasture_seed_replaceable.json @@ -0,0 +1,8 @@ +{ + "replace": false, + "values": [ + "minecraft:dirt", + "minecraft:grass_block", + "minecraft:mycelium" + ] +} \ No newline at end of file diff --git a/Xplat/src/main/java/vazkii/botania/common/item/GrassSeedsItem.java b/Xplat/src/main/java/vazkii/botania/common/item/GrassSeedsItem.java index e4aaec57c6..62846f6dcf 100644 --- a/Xplat/src/main/java/vazkii/botania/common/item/GrassSeedsItem.java +++ b/Xplat/src/main/java/vazkii/botania/common/item/GrassSeedsItem.java @@ -11,21 +11,26 @@ import com.google.common.collect.ImmutableMap; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.FluidTags; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.SnowLayerBlock; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.lighting.LightEngine; import org.jetbrains.annotations.NotNull; import vazkii.botania.api.block.FloatingFlower.IslandType; import vazkii.botania.client.fx.WispParticleData; import vazkii.botania.common.block.BotaniaBlocks; +import vazkii.botania.common.lib.BotaniaTags; import java.util.*; @@ -67,7 +72,7 @@ public InteractionResult useOn(UseOnContext ctx) { public InteractionResult applySeeds(Level world, BlockPos pos, ItemStack stack) { BlockState state = world.getBlockState(pos); - if (state.is(Blocks.DIRT) || state.is(Blocks.GRASS_BLOCK) && type != IslandType.GRASS) { + if (state.is(BotaniaTags.Blocks.PASTURE_SEED_REPLACEABLE) && state != stateForType(type)) { if (!world.isClientSide) { BlockSwapper swapper = addBlockSwapper(world, pos, type); world.setBlockAndUpdate(pos, swapper.stateToSet); @@ -161,9 +166,14 @@ private static class BlockSwapper { public static final int RANGE = 3; /** - * The range around which a block can spread in a single tick. + * The horizontal range around which a block can spread in a single tick. */ - public static final int TICK_RANGE = 1; + public static final int TICK_RANGE_HORIZONTAL = 1; + + /** + * The vertical range around which a block can spread in a single tick. + */ + public static final int TICK_RANGE_VERTICAL = 2; private final Level world; private final Random rand; @@ -197,12 +207,14 @@ public BlockSwapper(Level world, BlockPos coords, BlockState state) { */ public boolean tick() { if (++ticksExisted % 20 == 0) { - for (BlockPos pos : BlockPos.betweenClosed(startCoords.offset(-RANGE, 0, -RANGE), - startCoords.offset(RANGE, 0, RANGE))) { - if (world.getBlockState(pos) == stateToSet) { - tickBlock(pos); + var tickPositions = new ArrayList(); + for (BlockPos pos : BlockPos.betweenClosed(startCoords.offset(-RANGE, -RANGE, -RANGE), + startCoords.offset(RANGE, RANGE, RANGE))) { + if (world.getBlockState(pos) == stateToSet && canPropagate(pos)) { + tickPositions.add(pos.immutable()); } } + tickPositions.forEach(this::tickBlock); } // This swapper should exist for 80 ticks @@ -219,16 +231,15 @@ public void tickBlock(BlockPos pos) { List validCoords = new ArrayList<>(); // Go around this block and aggregate valid blocks. - for (int xOffset = -TICK_RANGE; xOffset <= TICK_RANGE; xOffset++) { - for (int zOffset = -TICK_RANGE; zOffset <= TICK_RANGE; zOffset++) { - // Skip the current block - if (xOffset == 0 && zOffset == 0) { - continue; - } + for (BlockPos targetPos : BlockPos.betweenClosed(pos.offset(-TICK_RANGE_HORIZONTAL, -TICK_RANGE_VERTICAL, -TICK_RANGE_HORIZONTAL), + pos.offset(TICK_RANGE_HORIZONTAL, TICK_RANGE_VERTICAL, TICK_RANGE_HORIZONTAL))) { + // Skip the current block, and any blocks that are already converted + if (targetPos.equals(pos) || world.getBlockState(targetPos) == stateToSet) { + continue; + } - if (isValidSwapPosition(pos.offset(xOffset, 0, zOffset))) { - validCoords.add(pos.offset(xOffset, 0, zOffset)); - } + if (isValidSwapPosition(targetPos)) { + validCoords.add(targetPos.immutable()); } } @@ -251,16 +262,29 @@ public void tickBlock(BlockPos pos) { */ public boolean isValidSwapPosition(BlockPos pos) { BlockState state = world.getBlockState(pos); + return state.is(BotaniaTags.Blocks.PASTURE_SEED_REPLACEABLE) && canBeGrass(pos, state); + } - // Valid blocks to spread to are either dirt or grass, and do not - // have blocks which block grass growth. - - // See http://minecraft.gamepedia.com/Grass_Block - // The major rule is that a block which reduces light - // levels by 2 or more blocks grass growth. + // [VanillaCopy] net.minecraft.world.level.block.SpreadingSnowyDirtBlock#canBeGrass + private boolean canBeGrass(BlockPos pos, BlockState state) { + BlockPos abovePos = pos.above(); + BlockState aboveState = world.getBlockState(abovePos); + if (aboveState.is(Blocks.SNOW) && aboveState.getValue(SnowLayerBlock.LAYERS) == 1) { + // single snow layer, okay to spread below that + return true; + } + if (aboveState.getFluidState().getAmount() == 8) { + // full-height liquid, don't spread + return false; + } + int lightLevel = LightEngine.getLightBlockInto(world, state, pos, aboveState, abovePos, Direction.UP, aboveState.getLightBlock(world, abovePos)); + return lightLevel < world.getMaxLightLevel(); + } - return (state.is(Blocks.DIRT) || state.is(Blocks.GRASS_BLOCK)) - && world.getBlockState(pos.above()).getLightBlock(world, pos.above()) <= 1; + // [VanillaCopy] net.minecraft.world.level.block.SpreadingSnowyDirtBlock#canPropagate + private boolean canPropagate(BlockPos pos) { + BlockPos abovePos = pos.above(); + return canBeGrass(pos, stateToSet) && !world.getFluidState(abovePos).is(FluidTags.WATER); } } diff --git a/Xplat/src/main/java/vazkii/botania/common/lib/BotaniaTags.java b/Xplat/src/main/java/vazkii/botania/common/lib/BotaniaTags.java index f4034f757d..71bef6bb23 100644 --- a/Xplat/src/main/java/vazkii/botania/common/lib/BotaniaTags.java +++ b/Xplat/src/main/java/vazkii/botania/common/lib/BotaniaTags.java @@ -258,6 +258,11 @@ public static class Blocks { */ public static final TagKey UNWANDABLE = tag("unwandable"); + /** + * Blocks in this tag can be replaced by the spreading effect of Pasture Seeds and related items. + */ + public static final TagKey PASTURE_SEED_REPLACEABLE = tag("pasture_seed_replaceable"); + private static TagKey tag(String name) { return TagKey.create(Registries.BLOCK, prefix(name)); } diff --git a/Xplat/src/main/java/vazkii/botania/data/BlockTagProvider.java b/Xplat/src/main/java/vazkii/botania/data/BlockTagProvider.java index 3d175b1dc6..daf31834b2 100644 --- a/Xplat/src/main/java/vazkii/botania/data/BlockTagProvider.java +++ b/Xplat/src/main/java/vazkii/botania/data/BlockTagProvider.java @@ -212,6 +212,8 @@ protected void addTags(HolderLookup.Provider provider) { tag(BotaniaTags.Blocks.UNWANDABLE).addTag(BlockTags.FIRE) .add(Blocks.CHORUS_PLANT, Blocks.SCULK_VEIN, Blocks.VINE, Blocks.REDSTONE_WIRE, Blocks.NETHER_PORTAL, BotaniaBlocks.solidVines); + tag(BotaniaTags.Blocks.PASTURE_SEED_REPLACEABLE).add(Blocks.DIRT, Blocks.GRASS_BLOCK, Blocks.MYCELIUM); + tag(BlockTags.FLOWER_POTS) .add(Arrays.stream(DyeColor.values()) .map(BotaniaBlocks::getPottedFlower)