Skip to content

Commit

Permalink
Made plant check of Agricarnation more flexible (#4659)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheRealWormbo authored Jul 2, 2024
1 parent 4b9d595 commit 3aae22a
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 27 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -37,7 +42,7 @@ public AgricarnationBlockEntity(BlockPos pos, BlockState state) {
public void tickFlower() {
super.tickFlower();

if (getLevel().isClientSide) {
if (!(getLevel() instanceof ServerLevel serverLevel)) {
return;
}

Expand All @@ -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;
}
Expand All @@ -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
Expand Down
15 changes: 15 additions & 0 deletions Xplat/src/main/java/vazkii/botania/common/lib/BotaniaTags.java
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,21 @@ public static class Blocks {
*/
public static final TagKey<Block> 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<Block> 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<Block> 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<Block> AGRICARNATION_APPLY_BONEMEAL = tag("agricarnation/apply_bonemeal");

/**
* Blocks in this tag can not have their state manipulated by a wand of the forest
*/
Expand Down
4 changes: 4 additions & 0 deletions Xplat/src/main/java/vazkii/botania/data/BlockTagProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<BlockPos> botania_getHeadPos(BlockGetter level, BlockPos pos, Block block);
}
1 change: 1 addition & 0 deletions Xplat/src/main/resources/botania_xplat.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"ExperienceOrbAccessor",
"FarmBlockMixin",
"FireBlockAccessor",
"GrowingPlantBodyBlockMixin",
"HopperBlockEntityAccessor",
"HurtByTargetGoalAccessor",
"InventoryAccessor",
Expand Down

0 comments on commit 3aae22a

Please sign in to comment.