diff --git a/Xplat/src/generated/resources/.cache/bfa01a6ca2555c100103725bf5c9e6da285f29c3 b/Xplat/src/generated/resources/.cache/bfa01a6ca2555c100103725bf5c9e6da285f29c3 index 10a6e0b90a..753f09979c 100644 --- a/Xplat/src/generated/resources/.cache/bfa01a6ca2555c100103725bf5c9e6da285f29c3 +++ b/Xplat/src/generated/resources/.cache/bfa01a6ca2555c100103725bf5c9e6da285f29c3 @@ -1,4 +1,7 @@ // 1.20.1 Botania/Tags for minecraft:block +8865bb2eca4192a496bda05497a584aee6a3a409 data/botania/tags/blocks/agricarnation/apply_bonemeal.json +1f1a3978ea72d2be2545d91126d194b1b6639c49 data/botania/tags/blocks/agricarnation/growth_candidate.json +caa65214e42ad067ce1935d9b7d48e0a9feb9e73 data/botania/tags/blocks/agricarnation/growth_excluded.json 7c8a171ae6d6133a08aa4d939254a4290e6b44b6 data/botania/tags/blocks/corporea_spark_override.json e8a61a147b6a258b9c37788ac1bd4edf592c9dc9 data/botania/tags/blocks/double_mystical_flowers.json 070322f325bf48a093e7a873066fd5992202fd74 data/botania/tags/blocks/dreamwood_logs.json diff --git a/Xplat/src/generated/resources/data/botania/tags/blocks/agricarnation/apply_bonemeal.json b/Xplat/src/generated/resources/data/botania/tags/blocks/agricarnation/apply_bonemeal.json new file mode 100644 index 0000000000..40e647c35d --- /dev/null +++ b/Xplat/src/generated/resources/data/botania/tags/blocks/agricarnation/apply_bonemeal.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "minecraft:azalea", + "minecraft:flowering_azalea" + ] +} \ No newline at end of file diff --git a/Xplat/src/generated/resources/data/botania/tags/blocks/agricarnation/growth_candidate.json b/Xplat/src/generated/resources/data/botania/tags/blocks/agricarnation/growth_candidate.json new file mode 100644 index 0000000000..a0404e2dee --- /dev/null +++ b/Xplat/src/generated/resources/data/botania/tags/blocks/agricarnation/growth_candidate.json @@ -0,0 +1,6 @@ +{ + "replace": false, + "values": [ + "#botania:agricarnation/apply_bonemeal" + ] +} \ No newline at end of file diff --git a/Xplat/src/generated/resources/data/botania/tags/blocks/agricarnation/growth_excluded.json b/Xplat/src/generated/resources/data/botania/tags/blocks/agricarnation/growth_excluded.json new file mode 100644 index 0000000000..f25f9c37a5 --- /dev/null +++ b/Xplat/src/generated/resources/data/botania/tags/blocks/agricarnation/growth_excluded.json @@ -0,0 +1,7 @@ +{ + "replace": false, + "values": [ + "minecraft:red_mushroom", + "minecraft:brown_mushroom" + ] +} \ No newline at end of file diff --git a/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/AgricarnationBlockEntity.java b/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/AgricarnationBlockEntity.java index 032d9462bc..b875067992 100644 --- a/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/AgricarnationBlockEntity.java +++ b/Xplat/src/main/java/vazkii/botania/common/block/flower/functional/AgricarnationBlockEntity.java @@ -11,6 +11,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundSource; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -19,11 +20,15 @@ import vazkii.botania.api.block_entity.RadiusDescriptor; import vazkii.botania.common.block.BotaniaFlowerBlocks; import vazkii.botania.common.handler.BotaniaSounds; +import vazkii.botania.common.lib.BotaniaTags; +import vazkii.botania.mixin.GrowingPlantBodyBlockMixin; import vazkii.botania.xplat.BotaniaConfig; public class AgricarnationBlockEntity extends FunctionalFlowerBlockEntity { private static final int RANGE = 5; private static final int RANGE_MINI = 2; + private static final int MANA_COST = 5; + private static final float BONEMEAL_SUCCESS_CHANCE = 0.5f; protected AgricarnationBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); @@ -37,7 +42,7 @@ public AgricarnationBlockEntity(BlockPos pos, BlockState state) { public void tickFlower() { super.tickFlower(); - if (getLevel().isClientSide) { + if (!(getLevel() instanceof ServerLevel serverLevel)) { return; } @@ -47,24 +52,41 @@ public void tickFlower() { if (ticksExisted % 6 == 0 && redstoneSignal == 0) { int range = getRange(); - int x = getEffectivePos().getX() + getLevel().random.nextInt(range * 2 + 1) - range; - int z = getEffectivePos().getZ() + getLevel().random.nextInt(range * 2 + 1) - range; + int x = getEffectivePos().getX() + serverLevel.random.nextInt(range * 2 + 1) - range; + int z = getEffectivePos().getZ() + serverLevel.random.nextInt(range * 2 + 1) - range; for (int i = 4; i > -2; i--) { int y = getEffectivePos().getY() + i; BlockPos pos = new BlockPos(x, y, z); - if (getLevel().isEmptyBlock(pos)) { + BlockState state = serverLevel.getBlockState(pos); + if (state.isAir()) { continue; } - if (isPlant(pos) && getMana() > 5) { - BlockState state = getLevel().getBlockState(pos); - addMana(-5); - state.randomTick((ServerLevel) level, pos, level.random); + Block block = state.getBlock(); + if (block instanceof GrowingPlantBodyBlock) { + var headPos = ((GrowingPlantBodyBlockMixin) block).botania_getHeadPos(serverLevel, pos, block); + if (headPos.isPresent()) { + pos = headPos.get(); + } + } + + if (isPlant(serverLevel, pos, state, block) && getMana() > MANA_COST) { + addMana(-MANA_COST); + if (state.is(BotaniaTags.Blocks.AGRICARNATION_APPLY_BONEMEAL) + && block instanceof BonemealableBlock bonemealableBlock + && bonemealableBlock.isValidBonemealTarget(serverLevel, pos, state, false)) { + if (serverLevel.random.nextFloat() < BONEMEAL_SUCCESS_CHANCE + && bonemealableBlock.isBonemealSuccess(serverLevel, serverLevel.random, pos, state)) { + bonemealableBlock.performBonemeal(serverLevel, serverLevel.random, pos, state); + } + } else { + state.randomTick(serverLevel, pos, serverLevel.random); + } if (BotaniaConfig.common().blockBreakParticles()) { - getLevel().levelEvent(LevelEvent.PARTICLES_PLANT_GROWTH, pos, 6 + getLevel().random.nextInt(4)); + serverLevel.levelEvent(LevelEvent.PARTICLES_PLANT_GROWTH, pos, 6 + serverLevel.random.nextInt(4)); } - getLevel().playSound(null, x, y, z, BotaniaSounds.agricarnation, SoundSource.BLOCKS, 1F, 0.5F + (float) Math.random() * 0.5F); + serverLevel.playSound(null, x, y, z, BotaniaSounds.agricarnation, SoundSource.BLOCKS, 1F, 0.5F + (float) Math.random() * 0.5F); break; } @@ -78,28 +100,32 @@ public boolean acceptsRedstone() { } /** - * @return Whether the block at {@code pos} grows "naturally". That is, whether its IGrowable action is simply - * growing itself, instead of something like spreading around or creating flowers around, etc, and whether - * this - * action would have happened normally over time without bonemeal. + * @return Whether the agricarnation considers the given block a plant it can grow. By default, + * grass/mycelium/nylium-like spreading blocks are excluded. They can be excplicitly included by being added + * to the AGRICARNATION_GROWTH_CANDIDATE tag. Blocks in AGRICARNATION_GROWTH_EXCLUDED are always excluded. + * Potential included blocks are those that are bonemealable, instance of BushBlock, or in the + * AGRICARNATION_GROWTH_CANDIDATE tag. They are included only if they accept random ticks, or are + * bonemealable and have the AGRICARNATION_APPLY_BONEMEAL tag. */ - private boolean isPlant(BlockPos pos) { - BlockState state = getLevel().getBlockState(pos); - Block block = state.getBlock(); - - // Spreads when ticked - if (block instanceof SpreadingSnowyDirtBlock) { + private boolean isPlant(Level level, BlockPos pos, BlockState state, Block block) { + if (state.is(BotaniaTags.Blocks.AGRICARNATION_GROWTH_EXCLUDED) + // grass/mycelium/nylium-like spreading blocks are excluded unless tagged otherwise + || (block instanceof SpreadingSnowyDirtBlock || block instanceof NyliumBlock) + && !state.is(BotaniaTags.Blocks.AGRICARNATION_GROWTH_CANDIDATE)) { return false; } - // Exclude all BushBlock except known vanilla subclasses - if (block instanceof BushBlock && !(block instanceof CropBlock) && !(block instanceof StemBlock) - && !(block instanceof SaplingBlock) && !(block instanceof SweetBerryBushBlock)) { - return false; - } + boolean couldApplyBonemeal = block instanceof BonemealableBlock bonemealableBlock + && bonemealableBlock.isValidBonemealTarget(level, pos, state, level.isClientSide); + + boolean isTargetCandidate = couldApplyBonemeal + || block instanceof BushBlock + || state.is(BotaniaTags.Blocks.AGRICARNATION_GROWTH_CANDIDATE); + boolean acceptsGrowthBoost = state.isRandomlyTicking() + || couldApplyBonemeal && state.is(BotaniaTags.Blocks.AGRICARNATION_APPLY_BONEMEAL); + + return isTargetCandidate && acceptsGrowthBoost; - return block instanceof BonemealableBlock mealable - && mealable.isValidBonemealTarget(getLevel(), pos, state, getLevel().isClientSide); } @Override 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 fb9b5337a9..f4034f757d 100644 --- a/Xplat/src/main/java/vazkii/botania/common/lib/BotaniaTags.java +++ b/Xplat/src/main/java/vazkii/botania/common/lib/BotaniaTags.java @@ -238,6 +238,21 @@ public static class Blocks { */ public static final TagKey HORN_OF_THE_COVERING_BREAKABLE = tag("horn_of_the_covering_breakable"); + /** + * Blocks in this tag are candidates for the Agricarnation's growth boost, assuming they accept random ticks. + */ + public static final TagKey AGRICARNATION_GROWTH_CANDIDATE = tag("agricarnation/growth_candidate"); + /** + * Blocks in this tag are ignored by the Agricarnation, even if they look like they are growable plants. + */ + public static final TagKey AGRICARNATION_GROWTH_EXCLUDED = tag("agricarnation/growth_excluded"); + /** + * Blocks in this tag will have their growth boosted as if bonemeal was applied, instead of via random ticks. + * These plants need to pass the bonemeal success check twice to get a boost, but mana will be consumed even if + * that fails. + */ + public static final TagKey AGRICARNATION_APPLY_BONEMEAL = tag("agricarnation/apply_bonemeal"); + /** * Blocks in this tag can not have their state manipulated by a wand of the forest */ diff --git a/Xplat/src/main/java/vazkii/botania/data/BlockTagProvider.java b/Xplat/src/main/java/vazkii/botania/data/BlockTagProvider.java index 748afc50f9..4185c9ddf8 100644 --- a/Xplat/src/main/java/vazkii/botania/data/BlockTagProvider.java +++ b/Xplat/src/main/java/vazkii/botania/data/BlockTagProvider.java @@ -244,6 +244,10 @@ protected void addTags(HolderLookup.Provider provider) { tigerseyePotted, vinculotusPotted ); + tag(BotaniaTags.Blocks.AGRICARNATION_APPLY_BONEMEAL).add(Blocks.AZALEA, Blocks.FLOWERING_AZALEA); + tag(BotaniaTags.Blocks.AGRICARNATION_GROWTH_CANDIDATE).addTag(BotaniaTags.Blocks.AGRICARNATION_APPLY_BONEMEAL); + tag(BotaniaTags.Blocks.AGRICARNATION_GROWTH_EXCLUDED).add(Blocks.RED_MUSHROOM, Blocks.BROWN_MUSHROOM); + registerMiningTags(); } diff --git a/Xplat/src/main/java/vazkii/botania/mixin/GrowingPlantBodyBlockMixin.java b/Xplat/src/main/java/vazkii/botania/mixin/GrowingPlantBodyBlockMixin.java new file mode 100644 index 0000000000..098a30983b --- /dev/null +++ b/Xplat/src/main/java/vazkii/botania/mixin/GrowingPlantBodyBlockMixin.java @@ -0,0 +1,16 @@ +package vazkii.botania.mixin; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.Optional; + +@Mixin(net.minecraft.world.level.block.GrowingPlantBodyBlock.class) +public interface GrowingPlantBodyBlockMixin { + @Invoker("getHeadPos") + Optional botania_getHeadPos(BlockGetter level, BlockPos pos, Block block); +} diff --git a/Xplat/src/main/resources/botania_xplat.mixins.json b/Xplat/src/main/resources/botania_xplat.mixins.json index aa9c9d5e05..80174006a0 100644 --- a/Xplat/src/main/resources/botania_xplat.mixins.json +++ b/Xplat/src/main/resources/botania_xplat.mixins.json @@ -24,6 +24,7 @@ "ExperienceOrbAccessor", "FarmBlockMixin", "FireBlockAccessor", + "GrowingPlantBodyBlockMixin", "HopperBlockEntityAccessor", "HurtByTargetGoalAccessor", "InventoryAccessor",