diff --git a/patches/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java.patch b/patches/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java.patch index e492a05b83..6980e8db13 100644 --- a/patches/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java.patch +++ b/patches/net/minecraft/world/entity/ai/behavior/HarvestFarmland.java.patch @@ -9,13 +9,31 @@ return false; } else if (p_23175_.getVillagerData().getProfession() != VillagerProfession.FARMER) { return false; +@@ -82,7 +_,7 @@ + BlockState blockstate = p_23182_.getBlockState(p_23181_); + Block block = blockstate.getBlock(); + Block block1 = p_23182_.getBlockState(p_23181_.below()).getBlock(); +- return block instanceof CropBlock && ((CropBlock)block).isMaxAge(blockstate) || blockstate.isAir() && block1 instanceof FarmBlock; ++ return block instanceof CropBlock && ((CropBlock)block).isMaxAge(blockstate) || blockstate.isAir() && (block1 instanceof FarmBlock || block1.builtInRegistryHolder().is(net.neoforged.neoforge.common.Tags.Blocks.VILLAGER_FARMLANDS)); + } + + protected void start(ServerLevel p_23177_, Villager p_23178_, long p_23179_) { +@@ -109,7 +_,7 @@ + p_23196_.destroyBlock(this.aboveFarmlandPos, true, p_23197_); + } + +- if (blockstate.isAir() && block1 instanceof FarmBlock && p_23197_.hasFarmSeeds()) { ++ if (blockstate.isAir() && (block1 instanceof FarmBlock || block1.builtInRegistryHolder().is(net.neoforged.neoforge.common.Tags.Blocks.VILLAGER_FARMLANDS)) && p_23197_.hasFarmSeeds()) { + SimpleContainer simplecontainer = p_23197_.getInventory(); + + for (int i = 0; i < simplecontainer.getContainerSize(); i++) { @@ -120,6 +_,11 @@ p_23196_.setBlockAndUpdate(this.aboveFarmlandPos, blockstate1); p_23196_.gameEvent(GameEvent.BLOCK_PLACE, this.aboveFarmlandPos, GameEvent.Context.of(p_23197_, blockstate1)); flag = true; -+ } else if (itemstack.getItem() instanceof net.neoforged.neoforge.common.IPlantable) { -+ if (((net.neoforged.neoforge.common.IPlantable)itemstack.getItem()).getPlantType(p_23196_, aboveFarmlandPos) == net.neoforged.neoforge.common.PlantType.CROP) { -+ p_23196_.setBlock(aboveFarmlandPos, ((net.neoforged.neoforge.common.IPlantable)itemstack.getItem()).getPlant(p_23196_, aboveFarmlandPos), 3); ++ } else if (itemstack.getItem() instanceof net.neoforged.neoforge.common.SpecialPlantable specialPlantable && specialPlantable.villagerCanPlantItem(p_23197_)) { ++ if (specialPlantable.canPlacePlantAtPosition(itemstack, p_23196_, aboveFarmlandPos, net.minecraft.core.Direction.DOWN)) { ++ specialPlantable.spawnPlantAtPosition(itemstack, p_23196_, aboveFarmlandPos, net.minecraft.core.Direction.DOWN); + flag = true; + } } diff --git a/patches/net/minecraft/world/entity/npc/Villager.java.patch b/patches/net/minecraft/world/entity/npc/Villager.java.patch index 130be2a382..df85d8ace4 100644 --- a/patches/net/minecraft/world/entity/npc/Villager.java.patch +++ b/patches/net/minecraft/world/entity/npc/Villager.java.patch @@ -37,3 +37,12 @@ p_35409_.addFreshEntityWithPassengers(witch); this.releaseAllPois(); this.discard(); +@@ -831,7 +_,7 @@ + @Override + public boolean wantsToPickUp(ItemStack p_35543_) { + Item item = p_35543_.getItem(); +- return (WANTED_ITEMS.contains(item) || this.getVillagerData().getProfession().requestedItems().contains(item)) ++ return (WANTED_ITEMS.contains(item) || this.getVillagerData().getProfession().requestedItems().contains(item) || (p_35543_.getItem() instanceof net.neoforged.neoforge.common.SpecialPlantable specialPlantable && specialPlantable.villagerCanPlantItem(this))) + && this.getInventory().canAddItem(p_35543_); + } + diff --git a/patches/net/minecraft/world/level/block/BambooSaplingBlock.java.patch b/patches/net/minecraft/world/level/block/BambooSaplingBlock.java.patch index 7505c61bc0..a097d379e0 100644 --- a/patches/net/minecraft/world/level/block/BambooSaplingBlock.java.patch +++ b/patches/net/minecraft/world/level/block/BambooSaplingBlock.java.patch @@ -1,5 +1,14 @@ --- a/net/minecraft/world/level/block/BambooSaplingBlock.java +++ b/net/minecraft/world/level/block/BambooSaplingBlock.java +@@ -50,6 +_,8 @@ + + @Override + protected boolean canSurvive(BlockState p_48986_, LevelReader p_48987_, BlockPos p_48988_) { ++ net.neoforged.neoforge.common.util.TriState soilDecision = p_48987_.getBlockState(p_48988_.below()).canSustainPlant(p_48987_, p_48988_.below(), net.minecraft.core.Direction.UP, p_48986_); ++ if (!soilDecision.isDefault()) return soilDecision.isTrue(); + return p_48987_.getBlockState(p_48988_.below()).is(BlockTags.BAMBOO_PLANTABLE_ON); + } + @@ -88,7 +_,7 @@ @Override diff --git a/patches/net/minecraft/world/level/block/BambooStalkBlock.java.patch b/patches/net/minecraft/world/level/block/BambooStalkBlock.java.patch index 6f3cef6059..55616552f9 100644 --- a/patches/net/minecraft/world/level/block/BambooStalkBlock.java.patch +++ b/patches/net/minecraft/world/level/block/BambooStalkBlock.java.patch @@ -1,14 +1,15 @@ --- a/net/minecraft/world/level/block/BambooStalkBlock.java +++ b/net/minecraft/world/level/block/BambooStalkBlock.java -@@ -27,7 +_,7 @@ - import net.minecraft.world.phys.shapes.CollisionContext; - import net.minecraft.world.phys.shapes.VoxelShape; - --public class BambooStalkBlock extends Block implements BonemealableBlock { -+public class BambooStalkBlock extends Block implements BonemealableBlock, net.neoforged.neoforge.common.IPlantable { - public static final MapCodec CODEC = simpleCodec(BambooStalkBlock::new); - protected static final float SMALL_LEAVES_AABB_OFFSET = 3.0F; - protected static final float LARGE_LEAVES_AABB_OFFSET = 5.0F; +@@ -97,7 +_,8 @@ + return null; + } else { + BlockState blockstate = p_261764_.getLevel().getBlockState(p_261764_.getClickedPos().below()); +- if (blockstate.is(BlockTags.BAMBOO_PLANTABLE_ON)) { ++ net.neoforged.neoforge.common.util.TriState soilDecision = blockstate.canSustainPlant(p_261764_.getLevel(), p_261764_.getClickedPos().below(), net.minecraft.core.Direction.UP, this.defaultBlockState()); ++ if (soilDecision.isDefault() ? blockstate.is(BlockTags.BAMBOO_PLANTABLE_ON) : soilDecision.isTrue()) { + if (blockstate.is(Blocks.BAMBOO_SAPLING)) { + return this.defaultBlockState().setValue(AGE, Integer.valueOf(0)); + } else if (blockstate.is(Blocks.BAMBOO)) { @@ -130,10 +_,11 @@ @Override protected void randomTick(BlockState p_261931_, ServerLevel p_261751_, BlockPos p_261616_, RandomSource p_261766_) { @@ -23,6 +24,15 @@ } } } +@@ -141,6 +_,8 @@ + + @Override + protected boolean canSurvive(BlockState p_261860_, LevelReader p_262154_, BlockPos p_261493_) { ++ net.neoforged.neoforge.common.util.TriState soilDecision = p_262154_.getBlockState(p_261493_.below()).canSustainPlant(p_262154_, p_261493_.below(), Direction.UP, p_261860_); ++ if (!soilDecision.isDefault()) return soilDecision.isTrue(); + return p_262154_.getBlockState(p_261493_.below()).is(BlockTags.BAMBOO_PLANTABLE_ON); + } + @@ -193,7 +_,7 @@ @Override @@ -32,16 +42,3 @@ } protected void growBamboo(BlockState p_261855_, Level p_262076_, BlockPos p_262109_, RandomSource p_261633_, int p_261759_) { -@@ -238,5 +_,12 @@ - } - - return i; -+ } -+ -+ @Override -+ public BlockState getPlant(BlockGetter world, BlockPos pos) { -+ BlockState state = world.getBlockState(pos); -+ if (state.getBlock() != this) return defaultBlockState(); -+ return state; - } - } diff --git a/patches/net/minecraft/world/level/block/BigDripleafBlock.java.patch b/patches/net/minecraft/world/level/block/BigDripleafBlock.java.patch new file mode 100644 index 0000000000..909353cec3 --- /dev/null +++ b/patches/net/minecraft/world/level/block/BigDripleafBlock.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/BigDripleafBlock.java ++++ b/net/minecraft/world/level/block/BigDripleafBlock.java +@@ -146,6 +_,8 @@ + protected boolean canSurvive(BlockState p_152289_, LevelReader p_152290_, BlockPos p_152291_) { + BlockPos blockpos = p_152291_.below(); + BlockState blockstate = p_152290_.getBlockState(blockpos); ++ net.neoforged.neoforge.common.util.TriState soilDecision = blockstate.canSustainPlant(p_152290_, blockpos, Direction.UP, p_152289_); ++ if (!soilDecision.isDefault()) return soilDecision.isTrue(); + return blockstate.is(this) || blockstate.is(Blocks.BIG_DRIPLEAF_STEM) || blockstate.is(BlockTags.BIG_DRIPLEAF_PLACEABLE); + } + diff --git a/patches/net/minecraft/world/level/block/Block.java.patch b/patches/net/minecraft/world/level/block/Block.java.patch index 2c32475972..c071202b83 100644 --- a/patches/net/minecraft/world/level/block/Block.java.patch +++ b/patches/net/minecraft/world/level/block/Block.java.patch @@ -123,7 +123,7 @@ public boolean dropFromExplosion(Explosion p_49826_) { return true; } -@@ -485,6 +_,104 @@ +@@ -485,6 +_,63 @@ return this.stateDefinition.getPossibleStates().stream().collect(ImmutableMap.toImmutableMap(Function.identity(), p_152459_)); } @@ -181,47 +181,6 @@ + public void initializeClient(java.util.function.Consumer consumer) { + } + -+ @Override -+ public boolean canSustainPlant(BlockState state, BlockGetter world, BlockPos pos, Direction facing, net.neoforged.neoforge.common.IPlantable plantable) { -+ BlockState plant = plantable.getPlant(world, pos.relative(facing)); -+ net.neoforged.neoforge.common.PlantType type = plantable.getPlantType(world, pos.relative(facing)); -+ -+ if (plant.getBlock() == Blocks.CACTUS) -+ return state.is(Blocks.CACTUS) || state.is(BlockTags.SAND); -+ -+ if (plant.getBlock() == Blocks.SUGAR_CANE && this == Blocks.SUGAR_CANE) -+ return true; -+ -+ if (plantable instanceof BushBlock && ((BushBlock)plantable).mayPlaceOn(state, world, pos)) -+ return true; -+ -+ if (net.neoforged.neoforge.common.PlantType.DESERT.equals(type)) { -+ return state.is(BlockTags.SAND) || state.is(BlockTags.TERRACOTTA); -+ } else if (net.neoforged.neoforge.common.PlantType.NETHER.equals(type)) { -+ return this == Blocks.SOUL_SAND; -+ } else if (net.neoforged.neoforge.common.PlantType.CROP.equals(type)) { -+ return state.is(Blocks.FARMLAND); -+ } else if (net.neoforged.neoforge.common.PlantType.CAVE.equals(type)) { -+ return state.isFaceSturdy(world, pos, Direction.UP); -+ } else if (net.neoforged.neoforge.common.PlantType.PLAINS.equals(type)) { -+ return state.is(BlockTags.DIRT) || this == Blocks.FARMLAND; -+ } else if (net.neoforged.neoforge.common.PlantType.WATER.equals(type)) { -+ return (state.is(Blocks.WATER) || state.getBlock() instanceof IceBlock) && world.getFluidState(pos.relative(facing)).isEmpty(); -+ } else if (net.neoforged.neoforge.common.PlantType.BEACH.equals(type)) { -+ boolean isBeach = state.is(BlockTags.DIRT) || state.is(BlockTags.SAND); -+ boolean hasWater = false; -+ for (Direction face : Direction.Plane.HORIZONTAL) { -+ BlockState adjacentBlockState = world.getBlockState(pos.relative(face)); -+ var adjacentFluidState = world.getFluidState(pos.relative(face)); -+ hasWater = hasWater || adjacentBlockState.is(Blocks.FROSTED_ICE) || adjacentFluidState.is(net.minecraft.tags.FluidTags.WATER); -+ if (hasWater) -+ break; //No point continuing. -+ } -+ return isBeach && hasWater; -+ } -+ return false; -+ } -+ + /* ========================================= FORGE END ======================================*/ + + /** @deprecated */ diff --git a/patches/net/minecraft/world/level/block/BushBlock.java.patch b/patches/net/minecraft/world/level/block/BushBlock.java.patch index 4e200e841c..165013111d 100644 --- a/patches/net/minecraft/world/level/block/BushBlock.java.patch +++ b/patches/net/minecraft/world/level/block/BushBlock.java.patch @@ -1,33 +1,14 @@ --- a/net/minecraft/world/level/block/BushBlock.java +++ b/net/minecraft/world/level/block/BushBlock.java -@@ -11,7 +_,7 @@ - import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.level.pathfinder.PathComputationType; - --public abstract class BushBlock extends Block { -+public abstract class BushBlock extends Block implements net.neoforged.neoforge.common.IPlantable { - protected BushBlock(BlockBehaviour.Properties p_51021_) { - super(p_51021_); - } -@@ -33,6 +_,8 @@ +@@ -33,7 +_,10 @@ @Override protected boolean canSurvive(BlockState p_51028_, LevelReader p_51029_, BlockPos p_51030_) { BlockPos blockpos = p_51030_.below(); -+ if (p_51028_.getBlock() == this) //Forge: This function is called during world gen and placement, before this block is set, so if we are not 'here' then assume it's the pre-check. -+ return p_51029_.getBlockState(blockpos).canSustainPlant(p_51029_, blockpos, Direction.UP, this); - return this.mayPlaceOn(p_51029_.getBlockState(blockpos), p_51029_, blockpos); +- return this.mayPlaceOn(p_51029_.getBlockState(blockpos), p_51029_, blockpos); ++ BlockState belowBlockState = p_51029_.getBlockState(blockpos); ++ net.neoforged.neoforge.common.util.TriState soilDecision = belowBlockState.canSustainPlant(p_51029_, blockpos, Direction.UP, p_51028_); ++ if (!soilDecision.isDefault()) return soilDecision.isTrue(); ++ return this.mayPlaceOn(belowBlockState, p_51029_, blockpos); } -@@ -44,5 +_,12 @@ @Override - protected boolean isPathfindable(BlockState p_51023_, PathComputationType p_51026_) { - return p_51026_ == PathComputationType.AIR && !this.hasCollision ? true : super.isPathfindable(p_51023_, p_51026_); -+ } -+ -+ @Override -+ public BlockState getPlant(BlockGetter world, BlockPos pos) { -+ BlockState state = world.getBlockState(pos); -+ if (state.getBlock() != this) return defaultBlockState(); -+ return state; - } - } diff --git a/patches/net/minecraft/world/level/block/CactusBlock.java.patch b/patches/net/minecraft/world/level/block/CactusBlock.java.patch index 13ecdb3020..037a99fb7c 100644 --- a/patches/net/minecraft/world/level/block/CactusBlock.java.patch +++ b/patches/net/minecraft/world/level/block/CactusBlock.java.patch @@ -1,14 +1,5 @@ --- a/net/minecraft/world/level/block/CactusBlock.java +++ b/net/minecraft/world/level/block/CactusBlock.java -@@ -21,7 +_,7 @@ - import net.minecraft.world.phys.shapes.CollisionContext; - import net.minecraft.world.phys.shapes.VoxelShape; - --public class CactusBlock extends Block { -+public class CactusBlock extends Block implements net.neoforged.neoforge.common.IPlantable { - public static final MapCodec CODEC = simpleCodec(CactusBlock::new); - public static final IntegerProperty AGE = BlockStateProperties.AGE_15; - public static final int MAX_AGE = 15; @@ -41,6 +_,7 @@ @Override @@ -34,28 +25,12 @@ } } } -@@ -99,7 +_,7 @@ +@@ -99,6 +_,8 @@ } BlockState blockstate1 = p_51154_.getBlockState(p_51155_.below()); -- return (blockstate1.is(Blocks.CACTUS) || blockstate1.is(BlockTags.SAND)) && !p_51154_.getBlockState(p_51155_.above()).liquid(); -+ return blockstate1.canSustainPlant(p_51154_, p_51155_, Direction.UP, this) && !p_51154_.getBlockState(p_51155_.above()).liquid(); ++ net.neoforged.neoforge.common.util.TriState soilDecision = blockstate1.canSustainPlant(p_51154_, p_51155_.below(), Direction.UP, p_51153_); ++ if (!soilDecision.isDefault()) return soilDecision.isTrue(); + return (blockstate1.is(Blocks.CACTUS) || blockstate1.is(BlockTags.SAND)) && !p_51154_.getBlockState(p_51155_.above()).liquid(); } - @Override -@@ -115,5 +_,15 @@ - @Override - protected boolean isPathfindable(BlockState p_51143_, PathComputationType p_51146_) { - return false; -+ } -+ -+ @Override -+ public net.neoforged.neoforge.common.PlantType getPlantType(BlockGetter world, BlockPos pos) { -+ return net.neoforged.neoforge.common.PlantType.DESERT; -+ } -+ -+ @Override -+ public BlockState getPlant(BlockGetter world, BlockPos pos) { -+ return defaultBlockState(); - } - } diff --git a/patches/net/minecraft/world/level/block/ChorusFlowerBlock.java.patch b/patches/net/minecraft/world/level/block/ChorusFlowerBlock.java.patch index 4209cc2f40..7f01cbf363 100644 --- a/patches/net/minecraft/world/level/block/ChorusFlowerBlock.java.patch +++ b/patches/net/minecraft/world/level/block/ChorusFlowerBlock.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/block/ChorusFlowerBlock.java +++ b/net/minecraft/world/level/block/ChorusFlowerBlock.java -@@ -65,7 +_,7 @@ +@@ -65,10 +_,13 @@ BlockPos blockpos = p_220982_.above(); if (p_220981_.isEmptyBlock(blockpos) && blockpos.getY() < p_220981_.getMaxBuildHeight()) { int i = p_220980_.getValue(AGE); @@ -9,6 +9,21 @@ boolean flag = false; boolean flag1 = false; BlockState blockstate = p_220981_.getBlockState(p_220982_.below()); ++ net.neoforged.neoforge.common.util.TriState soilDecision = blockstate.canSustainPlant(p_220981_, p_220982_.below(), Direction.UP, p_220980_); ++ if (!soilDecision.isDefault()) flag = soilDecision.isTrue(); ++ else + if (blockstate.is(Blocks.END_STONE)) { + flag = true; + } else if (blockstate.is(this.plant)) { +@@ -77,6 +_,8 @@ + for (int k = 0; k < 4; k++) { + BlockState blockstate1 = p_220981_.getBlockState(p_220982_.below(j + 1)); + if (!blockstate1.is(this.plant)) { ++ net.neoforged.neoforge.common.util.TriState soilDecision2 = blockstate1.canSustainPlant(p_220981_, p_220982_.below(j + 1), Direction.UP, p_220980_); ++ if (!soilDecision2.isDefault()) flag1 = soilDecision2.isTrue(); + if (blockstate1.is(Blocks.END_STONE)) { + flag1 = true; + } @@ -123,6 +_,7 @@ } else { this.placeDeadFlower(p_220981_, p_220982_); @@ -17,3 +32,12 @@ } } } +@@ -159,6 +_,8 @@ + @Override + protected boolean canSurvive(BlockState p_51683_, LevelReader p_51684_, BlockPos p_51685_) { + BlockState blockstate = p_51684_.getBlockState(p_51685_.below()); ++ net.neoforged.neoforge.common.util.TriState soilDecision = blockstate.canSustainPlant(p_51684_, p_51685_.below(), Direction.UP, p_51683_); ++ if (!soilDecision.isDefault()) return soilDecision.isTrue(); + if (!blockstate.is(this.plant) && !blockstate.is(Blocks.END_STONE)) { + if (!blockstate.isAir()) { + return false; diff --git a/patches/net/minecraft/world/level/block/ChorusPlantBlock.java.patch b/patches/net/minecraft/world/level/block/ChorusPlantBlock.java.patch new file mode 100644 index 0000000000..d01ed837a2 --- /dev/null +++ b/patches/net/minecraft/world/level/block/ChorusPlantBlock.java.patch @@ -0,0 +1,34 @@ +--- a/net/minecraft/world/level/block/ChorusPlantBlock.java ++++ b/net/minecraft/world/level/block/ChorusPlantBlock.java +@@ -49,7 +_,8 @@ + BlockState blockstate4 = p_51711_.getBlockState(p_51712_.south()); + BlockState blockstate5 = p_51711_.getBlockState(p_51712_.west()); + Block block = p_304771_.getBlock(); +- return p_304771_.trySetValue(DOWN, Boolean.valueOf(blockstate.is(block) || blockstate.is(Blocks.CHORUS_FLOWER) || blockstate.is(Blocks.END_STONE))) ++ net.neoforged.neoforge.common.util.TriState soilDecision = blockstate.canSustainPlant(p_51711_, p_51712_.below(), Direction.UP, p_304771_); ++ return p_304771_.trySetValue(DOWN, Boolean.valueOf(blockstate.is(block) || blockstate.is(Blocks.CHORUS_FLOWER) || blockstate.is(Blocks.END_STONE) || soilDecision.isTrue())) + .trySetValue(UP, Boolean.valueOf(blockstate1.is(block) || blockstate1.is(Blocks.CHORUS_FLOWER))) + .trySetValue(NORTH, Boolean.valueOf(blockstate2.is(block) || blockstate2.is(Blocks.CHORUS_FLOWER))) + .trySetValue(EAST, Boolean.valueOf(blockstate3.is(block) || blockstate3.is(Blocks.CHORUS_FLOWER))) +@@ -64,6 +_,12 @@ + return super.updateShape(p_51728_, p_51729_, p_51730_, p_51731_, p_51732_, p_51733_); + } else { + boolean flag = p_51730_.is(this) || p_51730_.is(Blocks.CHORUS_FLOWER) || p_51729_ == Direction.DOWN && p_51730_.is(Blocks.END_STONE); ++ if (p_51729_ == Direction.DOWN) { ++ net.neoforged.neoforge.common.util.TriState soilDecision = p_51730_.canSustainPlant(p_51731_, p_51733_.relative(p_51729_), p_51729_.getOpposite(), p_51728_); ++ if (!soilDecision.isDefault()) { ++ flag = soilDecision.isTrue(); ++ } ++ } + return p_51728_.setValue(PROPERTY_BY_DIRECTION.get(p_51729_), Boolean.valueOf(flag)); + } + } +@@ -95,6 +_,8 @@ + } + } + ++ net.neoforged.neoforge.common.util.TriState soilDecision = blockstate.canSustainPlant(p_51725_, p_51726_.below(), Direction.UP, p_51724_); ++ if (!soilDecision.isDefault()) return soilDecision.isTrue(); + return blockstate.is(this) || blockstate.is(Blocks.END_STONE); + } + diff --git a/patches/net/minecraft/world/level/block/CocoaBlock.java.patch b/patches/net/minecraft/world/level/block/CocoaBlock.java.patch index 9ba1d136c0..10b8be1eed 100644 --- a/patches/net/minecraft/world/level/block/CocoaBlock.java.patch +++ b/patches/net/minecraft/world/level/block/CocoaBlock.java.patch @@ -14,3 +14,12 @@ } } } +@@ -75,6 +_,8 @@ + @Override + protected boolean canSurvive(BlockState p_51767_, LevelReader p_51768_, BlockPos p_51769_) { + BlockState blockstate = p_51768_.getBlockState(p_51769_.relative(p_51767_.getValue(FACING))); ++ net.neoforged.neoforge.common.util.TriState soilDecision = blockstate.canSustainPlant(p_51768_, p_51769_.relative(p_51767_.getValue(FACING)), p_51767_.getValue(FACING).getOpposite(), p_51767_); ++ if (!soilDecision.isDefault()) return soilDecision.isTrue(); + return blockstate.is(BlockTags.JUNGLE_LOGS); + } + diff --git a/patches/net/minecraft/world/level/block/CropBlock.java.patch b/patches/net/minecraft/world/level/block/CropBlock.java.patch index 6f71741140..63e6616ed3 100644 --- a/patches/net/minecraft/world/level/block/CropBlock.java.patch +++ b/patches/net/minecraft/world/level/block/CropBlock.java.patch @@ -8,26 +8,47 @@ if (p_221051_.getRawBrightness(p_221052_, 0) >= 9) { int i = this.getAge(p_221050_); if (i < this.getMaxAge()) { - float f = getGrowthSpeed(this, p_221051_, p_221052_); +- float f = getGrowthSpeed(this, p_221051_, p_221052_); - if (p_221053_.nextInt((int)(25.0F / f) + 1) == 0) { ++ float f = getGrowthSpeed(p_221050_, p_221051_, p_221052_); + if (net.neoforged.neoforge.common.CommonHooks.canCropGrow(p_221051_, p_221052_, p_221050_, p_221053_.nextInt((int)(25.0F / f) + 1) == 0)) { p_221051_.setBlock(p_221052_, this.getStateForAge(i + 1), 2); + net.neoforged.neoforge.common.CommonHooks.fireCropGrowPost(p_221051_, p_221052_, p_221050_); } } } -@@ -117,9 +_,9 @@ +@@ -109,7 +_,8 @@ + return Mth.nextInt(p_52262_.random, 2, 5); + } + +- protected static float getGrowthSpeed(Block p_52273_, BlockGetter p_52274_, BlockPos p_52275_) { ++ protected static float getGrowthSpeed(BlockState blockState, BlockGetter p_52274_, BlockPos p_52275_) { ++ Block p_52273_ = blockState.getBlock(); + float f = 1.0F; + BlockPos blockpos = p_52275_.below(); + +@@ -117,9 +_,10 @@ for (int j = -1; j <= 1; j++) { float f1 = 0.0F; BlockState blockstate = p_52274_.getBlockState(blockpos.offset(i, 0, j)); - if (blockstate.is(Blocks.FARMLAND)) { -+ if (blockstate.canSustainPlant(p_52274_, blockpos.offset(i, 0, j), net.minecraft.core.Direction.UP, (net.neoforged.neoforge.common.IPlantable) p_52273_)) { ++ net.neoforged.neoforge.common.util.TriState soilDecision = blockstate.canSustainPlant(p_52274_, blockpos.offset(i, 0, j), net.minecraft.core.Direction.UP, blockState); ++ if (soilDecision.isDefault() ? blockstate.is(Blocks.FARMLAND) : soilDecision.isTrue()) { f1 = 1.0F; - if (blockstate.getValue(FarmBlock.MOISTURE) > 0) { + if (blockstate.isFertile(p_52274_, p_52275_.offset(i, 0, j))) { f1 = 3.0F; } } +@@ -155,6 +_,8 @@ + + @Override + protected boolean canSurvive(BlockState p_52282_, LevelReader p_52283_, BlockPos p_52284_) { ++ net.neoforged.neoforge.common.util.TriState soilDecision = p_52283_.getBlockState(p_52284_.below()).canSustainPlant(p_52283_, p_52284_.below(), net.minecraft.core.Direction.UP, p_52282_); ++ if (!soilDecision.isDefault()) return soilDecision.isTrue(); + return hasSufficientLight(p_52283_, p_52284_) && super.canSurvive(p_52282_, p_52283_, p_52284_); + } + @@ -164,7 +_,7 @@ @Override diff --git a/patches/net/minecraft/world/level/block/FarmBlock.java.patch b/patches/net/minecraft/world/level/block/FarmBlock.java.patch index 73a1ea523f..7b5c19bf38 100644 --- a/patches/net/minecraft/world/level/block/FarmBlock.java.patch +++ b/patches/net/minecraft/world/level/block/FarmBlock.java.patch @@ -12,14 +12,7 @@ turnToDirt(p_153230_, p_153228_, p_153227_, p_153229_); } -@@ -117,17 +_,20 @@ - } - - private static boolean shouldMaintainFarmland(BlockGetter p_279219_, BlockPos p_279209_) { -- return p_279219_.getBlockState(p_279209_.above()).is(BlockTags.MAINTAINS_FARMLAND); -+ BlockState plant = p_279219_.getBlockState(p_279209_.above()); -+ BlockState state = p_279219_.getBlockState(p_279209_); -+ return plant.getBlock() instanceof net.neoforged.neoforge.common.IPlantable && state.canSustainPlant(p_279219_, p_279209_, Direction.UP, (net.neoforged.neoforge.common.IPlantable)plant.getBlock()); +@@ -121,13 +_,14 @@ } private static boolean isNearWater(LevelReader p_53259_, BlockPos p_53260_) { diff --git a/patches/net/minecraft/world/level/block/MangrovePropaguleBlock.java.patch b/patches/net/minecraft/world/level/block/MangrovePropaguleBlock.java.patch new file mode 100644 index 0000000000..767257bd09 --- /dev/null +++ b/patches/net/minecraft/world/level/block/MangrovePropaguleBlock.java.patch @@ -0,0 +1,16 @@ +--- a/net/minecraft/world/level/block/MangrovePropaguleBlock.java ++++ b/net/minecraft/world/level/block/MangrovePropaguleBlock.java +@@ -93,7 +_,12 @@ + + @Override + protected boolean canSurvive(BlockState p_221473_, LevelReader p_221474_, BlockPos p_221475_) { +- return isHanging(p_221473_) ? p_221474_.getBlockState(p_221475_.above()).is(Blocks.MANGROVE_LEAVES) : super.canSurvive(p_221473_, p_221474_, p_221475_); ++ if (isHanging(p_221473_)) { ++ net.neoforged.neoforge.common.util.TriState soilDecision = p_221474_.getBlockState(p_221475_.above()).canSustainPlant(p_221474_, p_221475_.above(), Direction.DOWN, p_221473_); ++ if (!soilDecision.isDefault()) return soilDecision.isTrue(); ++ return p_221474_.getBlockState(p_221475_.above()).is(Blocks.MANGROVE_LEAVES); ++ } ++ return super.canSurvive(p_221473_, p_221474_, p_221475_); + } + + @Override diff --git a/patches/net/minecraft/world/level/block/MushroomBlock.java.patch b/patches/net/minecraft/world/level/block/MushroomBlock.java.patch index e39f3d2ee6..a977b18527 100644 --- a/patches/net/minecraft/world/level/block/MushroomBlock.java.patch +++ b/patches/net/minecraft/world/level/block/MushroomBlock.java.patch @@ -1,11 +1,14 @@ --- a/net/minecraft/world/level/block/MushroomBlock.java +++ b/net/minecraft/world/level/block/MushroomBlock.java -@@ -87,13 +_,21 @@ +@@ -85,15 +_,24 @@ + protected boolean canSurvive(BlockState p_54880_, LevelReader p_54881_, BlockPos p_54882_) { + BlockPos blockpos = p_54882_.below(); BlockState blockstate = p_54881_.getBlockState(blockpos); ++ net.neoforged.neoforge.common.util.TriState soilDecision = blockstate.canSustainPlant(p_54881_, blockpos, net.minecraft.core.Direction.UP, p_54880_); return blockstate.is(BlockTags.MUSHROOM_GROW_BLOCK) ? true - : p_54881_.getRawBrightness(p_54882_, 0) < 13 && this.mayPlaceOn(blockstate, p_54881_, blockpos); -+ : p_54881_.getRawBrightness(p_54882_, 0) < 13 && blockstate.canSustainPlant(p_54881_, blockpos, net.minecraft.core.Direction.UP, this); ++ : soilDecision.isDefault() ? (p_54881_.getRawBrightness(p_54882_, 0) < 13 && this.mayPlaceOn(blockstate, p_54881_, blockpos)) : soilDecision.isTrue(); } public boolean growMushroom(ServerLevel p_221774_, BlockPos p_221775_, BlockState p_221776_, RandomSource p_221777_) { diff --git a/patches/net/minecraft/world/level/block/PitcherCropBlock.java.patch b/patches/net/minecraft/world/level/block/PitcherCropBlock.java.patch new file mode 100644 index 0000000000..28cd5c9397 --- /dev/null +++ b/patches/net/minecraft/world/level/block/PitcherCropBlock.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/world/level/block/PitcherCropBlock.java ++++ b/net/minecraft/world/level/block/PitcherCropBlock.java +@@ -86,7 +_,8 @@ + + @Override + public boolean canSurvive(BlockState p_277671_, LevelReader p_277477_, BlockPos p_278085_) { +- return isLower(p_277671_) && !sufficientLight(p_277477_, p_278085_) ? false : super.canSurvive(p_277671_, p_277477_, p_278085_); ++ net.neoforged.neoforge.common.util.TriState soilDecision = p_277477_.getBlockState(p_278085_.below()).canSustainPlant(p_277477_, p_278085_.below(), Direction.UP, p_277671_); ++ return isLower(p_277671_) && !sufficientLight(p_277477_, p_278085_) ? soilDecision.isTrue() : super.canSurvive(p_277671_, p_277477_, p_278085_); + } + + @Override +@@ -125,7 +_,7 @@ + + @Override + public void randomTick(BlockState p_277950_, ServerLevel p_277589_, BlockPos p_277937_, RandomSource p_277887_) { +- float f = CropBlock.getGrowthSpeed(this, p_277589_, p_277937_); ++ float f = CropBlock.getGrowthSpeed(p_277950_, p_277589_, p_277937_); + boolean flag = p_277887_.nextInt((int)(25.0F / f) + 1) == 0; + if (flag) { + this.grow(p_277589_, p_277950_, p_277937_, 1); diff --git a/patches/net/minecraft/world/level/block/SmallDripleafBlock.java.patch b/patches/net/minecraft/world/level/block/SmallDripleafBlock.java.patch new file mode 100644 index 0000000000..18c886ec2f --- /dev/null +++ b/patches/net/minecraft/world/level/block/SmallDripleafBlock.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/SmallDripleafBlock.java ++++ b/net/minecraft/world/level/block/SmallDripleafBlock.java +@@ -90,6 +_,8 @@ + } else { + BlockPos blockpos = p_154617_.below(); + BlockState blockstate = p_154616_.getBlockState(blockpos); ++ net.neoforged.neoforge.common.util.TriState soilDecision = blockstate.canSustainPlant(p_154616_, blockpos, Direction.UP, p_154615_); ++ if (!soilDecision.isDefault()) return soilDecision.isTrue(); + return this.mayPlaceOn(blockstate, p_154616_, blockpos); + } + } diff --git a/patches/net/minecraft/world/level/block/StemBlock.java.patch b/patches/net/minecraft/world/level/block/StemBlock.java.patch index 5a00381295..2bc28f2beb 100644 --- a/patches/net/minecraft/world/level/block/StemBlock.java.patch +++ b/patches/net/minecraft/world/level/block/StemBlock.java.patch @@ -6,8 +6,9 @@ protected void randomTick(BlockState p_222538_, ServerLevel p_222539_, BlockPos p_222540_, RandomSource p_222541_) { + if (!p_222539_.isAreaLoaded(p_222540_, 1)) return; // Forge: prevent loading unloaded chunks when checking neighbor's light if (p_222539_.getRawBrightness(p_222540_, 0) >= 9) { - float f = CropBlock.getGrowthSpeed(this, p_222539_, p_222540_); +- float f = CropBlock.getGrowthSpeed(this, p_222539_, p_222540_); - if (p_222541_.nextInt((int)(25.0F / f) + 1) == 0) { ++ float f = CropBlock.getGrowthSpeed(p_222538_, p_222539_, p_222540_); + if (net.neoforged.neoforge.common.CommonHooks.canCropGrow(p_222539_, p_222540_, p_222538_, p_222541_.nextInt((int)(25.0F / f) + 1) == 0)) { int i = p_222538_.getValue(AGE); if (i < 7) { @@ -31,15 +32,3 @@ } } } -@@ -132,5 +_,11 @@ - @Override - protected void createBlockStateDefinition(StateDefinition.Builder p_57040_) { - p_57040_.add(AGE); -+ } -+ -+ //FORGE START -+ @Override -+ public net.neoforged.neoforge.common.PlantType getPlantType(BlockGetter world, BlockPos pos) { -+ return net.neoforged.neoforge.common.PlantType.CROP; - } - } diff --git a/patches/net/minecraft/world/level/block/SugarCaneBlock.java.patch b/patches/net/minecraft/world/level/block/SugarCaneBlock.java.patch index 770a46529a..55a9cd856c 100644 --- a/patches/net/minecraft/world/level/block/SugarCaneBlock.java.patch +++ b/patches/net/minecraft/world/level/block/SugarCaneBlock.java.patch @@ -1,14 +1,5 @@ --- a/net/minecraft/world/level/block/SugarCaneBlock.java +++ b/net/minecraft/world/level/block/SugarCaneBlock.java -@@ -19,7 +_,7 @@ - import net.minecraft.world.phys.shapes.CollisionContext; - import net.minecraft.world.phys.shapes.VoxelShape; - --public class SugarCaneBlock extends Block { -+public class SugarCaneBlock extends Block implements net.neoforged.neoforge.common.IPlantable { - public static final MapCodec CODEC = simpleCodec(SugarCaneBlock::new); - public static final IntegerProperty AGE = BlockStateProperties.AGE_15; - protected static final float AABB_OFFSET = 6.0F; @@ -58,12 +_,15 @@ if (i < 3) { @@ -25,16 +16,15 @@ } } } -@@ -79,6 +_,8 @@ - - @Override - protected boolean canSurvive(BlockState p_57175_, LevelReader p_57176_, BlockPos p_57177_) { -+ BlockState soil = p_57176_.getBlockState(p_57177_.below()); -+ if (soil.canSustainPlant(p_57176_, p_57177_.below(), Direction.UP, this)) return true; - BlockState blockstate = p_57176_.getBlockState(p_57177_.below()); +@@ -83,13 +_,15 @@ if (blockstate.is(this)) { return true; -@@ -89,7 +_,7 @@ + } else { ++ net.neoforged.neoforge.common.util.TriState soilDecision = blockstate.canSustainPlant(p_57176_, p_57177_.below(), Direction.UP, p_57175_); ++ if (!soilDecision.isDefault()) return soilDecision.isTrue(); + if (blockstate.is(BlockTags.DIRT) || blockstate.is(BlockTags.SAND)) { + BlockPos blockpos = p_57177_.below(); + for (Direction direction : Direction.Plane.HORIZONTAL) { BlockState blockstate1 = p_57176_.getBlockState(blockpos.relative(direction)); FluidState fluidstate = p_57176_.getFluidState(blockpos.relative(direction)); @@ -43,19 +33,3 @@ return true; } } -@@ -102,5 +_,15 @@ - @Override - protected void createBlockStateDefinition(StateDefinition.Builder p_57186_) { - p_57186_.add(AGE); -+ } -+ -+ @Override -+ public net.neoforged.neoforge.common.PlantType getPlantType(BlockGetter world, BlockPos pos) { -+ return net.neoforged.neoforge.common.PlantType.BEACH; -+ } -+ -+ @Override -+ public BlockState getPlant(BlockGetter world, BlockPos pos) { -+ return defaultBlockState(); - } - } diff --git a/src/generated/resources/assets/c/lang/en_us.json b/src/generated/resources/assets/c/lang/en_us.json index f577ba3f25..d2e4a073aa 100644 --- a/src/generated/resources/assets/c/lang/en_us.json +++ b/src/generated/resources/assets/c/lang/en_us.json @@ -110,6 +110,7 @@ "tag.block.neoforge.needs_gold_tool": "Needs Gold Tools", "tag.block.neoforge.needs_netherite_tool": "Needs Netherite Tools", "tag.block.neoforge.needs_wood_tool": "Needs Wooden Tools", + "tag.block.neoforge.villager_farmlands": "Villager Farmlands", "tag.enchantment.c.entity_auxiliary_movement_enhancements": "Entity Auxiliary Movement Enhancements", "tag.enchantment.c.entity_defense_enhancements": "Entity Defense Enhancements", "tag.enchantment.c.entity_speed_enhancements": "Entity Speed Enhancements", diff --git a/src/generated/resources/data/neoforge/tags/block/villager_farmlands.json b/src/generated/resources/data/neoforge/tags/block/villager_farmlands.json new file mode 100644 index 0000000000..ea3a8d61b0 --- /dev/null +++ b/src/generated/resources/data/neoforge/tags/block/villager_farmlands.json @@ -0,0 +1,5 @@ +{ + "values": [ + "minecraft:farmland" + ] +} \ No newline at end of file diff --git a/src/main/java/net/neoforged/neoforge/common/IPlantable.java b/src/main/java/net/neoforged/neoforge/common/IPlantable.java deleted file mode 100644 index 81e7c6d9db..0000000000 --- a/src/main/java/net/neoforged/neoforge/common/IPlantable.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.common; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.CropBlock; -import net.minecraft.world.level.block.FlowerBlock; -import net.minecraft.world.level.block.SaplingBlock; -import net.minecraft.world.level.block.state.BlockState; - -public interface IPlantable { - default PlantType getPlantType(BlockGetter level, BlockPos pos) { - if (this instanceof CropBlock || this == Blocks.PITCHER_CROP) return PlantType.CROP; - if (this instanceof SaplingBlock) return PlantType.PLAINS; - if (this instanceof FlowerBlock) return PlantType.PLAINS; - if (this == Blocks.DEAD_BUSH) return PlantType.DESERT; - if (this == Blocks.LILY_PAD) return PlantType.WATER; - if (this == Blocks.RED_MUSHROOM) return PlantType.CAVE; - if (this == Blocks.BROWN_MUSHROOM) return PlantType.CAVE; - if (this == Blocks.NETHER_WART) return PlantType.NETHER; - if (this == Blocks.TALL_GRASS) return PlantType.PLAINS; - return PlantType.PLAINS; - } - - BlockState getPlant(BlockGetter level, BlockPos pos); -} diff --git a/src/main/java/net/neoforged/neoforge/common/PlantType.java b/src/main/java/net/neoforged/neoforge/common/PlantType.java deleted file mode 100644 index 630ac66a09..0000000000 --- a/src/main/java/net/neoforged/neoforge/common/PlantType.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.common; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Pattern; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; - -public final class PlantType { - private static final Pattern INVALID_CHARACTERS = Pattern.compile("[^a-z_]"); //Only a-z and _ are allowed, meaning names must be lower case. And use _ to separate words. - private static final Map VALUES = new ConcurrentHashMap<>(); - - public static final PlantType PLAINS = get("plains"); - public static final PlantType DESERT = get("desert"); - public static final PlantType BEACH = get("beach"); - public static final PlantType CAVE = get("cave"); - public static final PlantType WATER = get("water"); - public static final PlantType NETHER = get("nether"); - public static final PlantType CROP = get("crop"); - - /** - * Getting a custom {@link PlantType}, or an existing one if it has the same name as that one. Your plant should implement {@link IPlantable} - * and return this custom type in {@link IPlantable#getPlantType(BlockGetter, BlockPos)}. - * - *

If your new plant grows on blocks like any one of them above, never create a new {@link PlantType}. - * This Type is only functioning in - * {@link Block#canSustainPlant(BlockState, BlockGetter, BlockPos, Direction, IPlantable)}, - * which you are supposed to override this function in your new block and create a new plant type to grow on that block. - * - * This method can be called during parallel loading - * - * @param name the name of the type of plant, you had better follow the style above - * @return the acquired {@link PlantType}, a new one if not found. - */ - public static PlantType get(String name) { - return VALUES.computeIfAbsent(name, e -> { - if (INVALID_CHARACTERS.matcher(e).find()) - throw new IllegalArgumentException("PlantType.get() called with invalid name: " + name); - return new PlantType(e); - }); - } - - private final String name; - - private PlantType(String name) { - this.name = name; - } - - public String getName() { - return name; - } -} diff --git a/src/main/java/net/neoforged/neoforge/common/SpecialPlantable.java b/src/main/java/net/neoforged/neoforge/common/SpecialPlantable.java new file mode 100644 index 0000000000..d607d9ac0f --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/SpecialPlantable.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.common; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.LevelReader; +import org.jetbrains.annotations.Nullable; + +/** + * Intended for mods to help mark if a modded item allows for placing modded plants of any kind or size. + * Also allows Villagers to properly plant items with this interface on and villagerCanPlantItem returning true. + *

+ * People trying to plant modded items should check if item implements this interface. + * Then check for true from canPlacePlantAtPosition first before calling spawnPlantAtPosition. + * Implementers of this interface would ideally call canSurvive on their plant block in canPlacePlantAtPosition. + *

+ * (Note: Vanilla plantable items are BlockItem where you can get their states directly and call canSurvive) + */ +public interface SpecialPlantable { + /** + * Checks location if this item can spawn a plant with the given direction attachment point. + * + * @return If plant can spawn + */ + boolean canPlacePlantAtPosition(ItemStack itemStack, LevelReader level, BlockPos pos, @Nullable Direction direction); + + /** + * Spawns the plant with the given direction attachment point at location. + * Ideally called after canPlacePlantAtPosition returns true. + */ + void spawnPlantAtPosition(ItemStack itemStack, LevelReader level, BlockPos pos, @Nullable Direction direction); + + /** + * Whether Villagers can pick up this item and plant it down on any block that extends FarmBlock. + */ + default boolean villagerCanPlantItem(Villager villager) { + return false; + }; +} diff --git a/src/main/java/net/neoforged/neoforge/common/Tags.java b/src/main/java/net/neoforged/neoforge/common/Tags.java index d54fa1e1a4..d065ddcc27 100644 --- a/src/main/java/net/neoforged/neoforge/common/Tags.java +++ b/src/main/java/net/neoforged/neoforge/common/Tags.java @@ -220,6 +220,11 @@ public static class Blocks { public static final TagKey STORAGE_BLOCKS_WHEAT = tag("storage_blocks/wheat"); public static final TagKey VILLAGER_JOB_SITES = tag("villager_job_sites"); + /** + * Blocks tagged here will be tracked by Farmer Villagers who will attempt to plant crops on top. + */ + public static final TagKey VILLAGER_FARMLANDS = neoforgeTag("villager_farmlands"); + private static TagKey tag(String name) { return BlockTags.create(ResourceLocation.fromNamespaceAndPath("c", name)); } diff --git a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeBlockTagsProvider.java b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeBlockTagsProvider.java index 87123ffb13..d37984dbbf 100644 --- a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeBlockTagsProvider.java +++ b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeBlockTagsProvider.java @@ -148,6 +148,8 @@ public void addTags(HolderLookup.Provider p_256380_) { Blocks.COMPOSTER, Blocks.FLETCHING_TABLE, Blocks.GRINDSTONE, Blocks.LECTERN, Blocks.LOOM, Blocks.SMITHING_TABLE, Blocks.SMOKER, Blocks.STONECUTTER); + tag(Tags.Blocks.VILLAGER_FARMLANDS).add(Blocks.FARMLAND); + // Backwards compat with pre-1.21 tags. Done after so optional tag is last for better readability. // TODO: Remove backwards compat tag entries in 1.22 tagWithOptionalLegacy(Tags.Blocks.BARRELS); diff --git a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeLanguageProvider.java b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeLanguageProvider.java index 39eeebdad1..be6f8d43f1 100644 --- a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeLanguageProvider.java +++ b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeLanguageProvider.java @@ -118,6 +118,7 @@ protected void addTranslations() { add(Tags.Blocks.STORAGE_BLOCKS_SLIME, "Slime Storage Blocks"); add(Tags.Blocks.STORAGE_BLOCKS_WHEAT, "Wheat Storage Blocks"); add(Tags.Blocks.VILLAGER_JOB_SITES, "Villager Job Sites"); + add(Tags.Blocks.VILLAGER_FARMLANDS, "Villager Farmlands"); // Items add(Tags.Items.BARRELS, "Barrels"); diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java index e424c0b868..982171cfec 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java @@ -73,10 +73,10 @@ import net.neoforged.neoforge.capabilities.BlockCapabilityCache; import net.neoforged.neoforge.client.ClientHooks; import net.neoforged.neoforge.client.model.data.ModelData; -import net.neoforged.neoforge.common.IPlantable; import net.neoforged.neoforge.common.ToolAction; import net.neoforged.neoforge.common.ToolActions; import net.neoforged.neoforge.common.enums.BubbleColumnDirection; +import net.neoforged.neoforge.common.util.TriState; import net.neoforged.neoforge.common.world.AuxiliaryLightManager; import net.neoforged.neoforge.event.EventHooks; import org.jetbrains.annotations.Nullable; @@ -360,24 +360,19 @@ default boolean addRunningEffects(BlockState state, Level level, BlockPos pos, E } /** - * Determines if this block can support the passed in plant, allowing it to be planted and grow. - * Some examples: - * Reeds check if its a reed, or if its sand/dirt/grass and adjacent to water - * Cacti checks if its a cacti, or if its sand - * Nether types check for soul sand - * Crops check for tilled soil - * Caves check if it's a solid surface - * Plains check if its grass or dirt - * Water check if its still water + * Determines if this block either force allow or force disallow a plant from being placed on it. (Or pass and let the plant's decision win) + * This will be called in plant's canSurvive method and/or mayPlace method. * - * @param state The Current state - * @param level The current level - * - * @param facing The direction relative to the given position the plant wants to be, typically its UP - * @param plantable The plant that wants to check - * @return True to allow the plant to be planted/stay. + * @param state The current state + * @param level The current level + * @param soilPosition The current position of the block that will sustain the plant + * @param facing The direction relative to the given position the plant wants to be, typically its UP + * @param plant The plant that is checking survivability + * @return {@link TriState#TRUE} to allow the plant to be planted/stay. {@link TriState#FALSE} to disallow the plant from placing. {@link TriState#DEFAULT} to allow the plant to make the decision to stay or not. */ - boolean canSustainPlant(BlockState state, BlockGetter level, BlockPos pos, Direction facing, IPlantable plantable); + default TriState canSustainPlant(BlockState state, BlockGetter level, BlockPos soilPosition, Direction facing, BlockState plant) { + return TriState.DEFAULT; + } /** * Called when a tree grows on top of this block and tries to set it to dirt by the trunk placer. diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java index 032f96a3f1..d359d99d4d 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java @@ -42,10 +42,10 @@ import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.capabilities.BlockCapabilityCache; -import net.neoforged.neoforge.common.IPlantable; import net.neoforged.neoforge.common.ToolAction; import net.neoforged.neoforge.common.ToolActions; import net.neoforged.neoforge.common.enums.BubbleColumnDirection; +import net.neoforged.neoforge.common.util.TriState; import net.neoforged.neoforge.common.world.AuxiliaryLightManager; import net.neoforged.neoforge.event.EventHooks; import org.jetbrains.annotations.Nullable; @@ -262,23 +262,17 @@ default boolean addRunningEffects(Level level, BlockPos pos, Entity entity) { } /** - * Determines if this block can support the passed in plant, allowing it to be planted and grow. - * Some examples: - * Reeds check if its a reed, or if its sand/dirt/grass and adjacent to water - * Cacti checks if its a cacti, or if its sand - * Nether types check for soul sand - * Crops check for tilled soil - * Caves check if it's a solid surface - * Plains check if its grass or dirt - * Water check if its still water + * Determines if this block either force allow or force disallow a plant from being placed on it. (Or pass and let the plant's decision win) + * This will be called in plant's canSurvive method and/or mayPlace method. * - * @param level The current level - * @param facing The direction relative to the given position the plant wants to be, typically its UP - * @param plantable The plant that wants to check - * @return True to allow the plant to be planted/stay. + * @param level The current level + * @param soilPosition The current position of the block that will sustain the plant + * @param facing The direction relative to the given position the plant wants to be, typically its UP + * @param plant The plant that is checking survivability + * @return {@link TriState#TRUE} to allow the plant to be planted/stay. {@link TriState#FALSE} to disallow the plant from placing. {@link TriState#DEFAULT} to allow the plant to make the decision to stay or not. */ - default boolean canSustainPlant(BlockGetter level, BlockPos pos, Direction facing, IPlantable plantable) { - return self().getBlock().canSustainPlant(self(), level, pos, facing, plantable); + default TriState canSustainPlant(BlockGetter level, BlockPos soilPosition, Direction facing, BlockState plant) { + return self().getBlock().canSustainPlant(self(), level, soilPosition, facing, plant); } /** diff --git a/tests/src/generated/resources/assets/neotests_survivability_bamboo_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_bamboo_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..36259fee5d --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_bamboo_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_bamboo_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_bamboo_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_bamboo_test/lang/en_us.json new file mode 100644 index 0000000000..746349a479 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_bamboo_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_bamboo_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_bamboo_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_bamboo_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_bamboo_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_bamboo_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_bamboo_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..72c7cad1f0 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_bamboo_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_bamboo_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_big_dripleaf_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_big_dripleaf_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..457799ee42 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_big_dripleaf_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_big_dripleaf_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_big_dripleaf_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_big_dripleaf_test/lang/en_us.json new file mode 100644 index 0000000000..f5cd7528ea --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_big_dripleaf_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_big_dripleaf_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_big_dripleaf_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_big_dripleaf_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_big_dripleaf_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_big_dripleaf_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_big_dripleaf_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..01f98ca4eb --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_big_dripleaf_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_big_dripleaf_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_cactus_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_cactus_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..8536ad1b5e --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_cactus_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_cactus_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_cactus_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_cactus_test/lang/en_us.json new file mode 100644 index 0000000000..f856289a13 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_cactus_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_cactus_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_cactus_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_cactus_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_cactus_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_cactus_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_cactus_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..aa8d5ca47b --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_cactus_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_cactus_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_chorus_flower_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_chorus_flower_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..5ce23a960f --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_chorus_flower_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_chorus_flower_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_chorus_flower_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_chorus_flower_test/lang/en_us.json new file mode 100644 index 0000000000..986ba06c68 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_chorus_flower_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_chorus_flower_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_chorus_flower_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_chorus_flower_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_chorus_flower_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_chorus_flower_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_chorus_flower_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..db2f7422a1 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_chorus_flower_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_chorus_flower_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_chorus_plant_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_chorus_plant_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..4d98b1aa2a --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_chorus_plant_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_chorus_plant_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_chorus_plant_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_chorus_plant_test/lang/en_us.json new file mode 100644 index 0000000000..659e5774e0 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_chorus_plant_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_chorus_plant_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_chorus_plant_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_chorus_plant_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_chorus_plant_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_chorus_plant_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_chorus_plant_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..cbd6c6af16 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_chorus_plant_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_chorus_plant_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_cocoa_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_cocoa_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..e5b41a332f --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_cocoa_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_cocoa_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_cocoa_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_cocoa_test/lang/en_us.json new file mode 100644 index 0000000000..55226b09b2 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_cocoa_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_cocoa_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_cocoa_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_cocoa_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_cocoa_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_cocoa_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_cocoa_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..92a48f775b --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_cocoa_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_cocoa_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_dead_bush_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_dead_bush_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..26c1a716c2 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_dead_bush_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_dead_bush_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_dead_bush_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_dead_bush_test/lang/en_us.json new file mode 100644 index 0000000000..b3f25e4103 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_dead_bush_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_dead_bush_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_dead_bush_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_dead_bush_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_dead_bush_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_dead_bush_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_dead_bush_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..bd6214082b --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_dead_bush_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_dead_bush_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_hanging_mangrove_propagule_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_hanging_mangrove_propagule_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..de725c6576 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_hanging_mangrove_propagule_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_hanging_mangrove_propagule_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_hanging_mangrove_propagule_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_hanging_mangrove_propagule_test/lang/en_us.json new file mode 100644 index 0000000000..503046561a --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_hanging_mangrove_propagule_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_hanging_mangrove_propagule_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_hanging_mangrove_propagule_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_hanging_mangrove_propagule_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_hanging_mangrove_propagule_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_hanging_mangrove_propagule_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_hanging_mangrove_propagule_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..8abc685e17 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_hanging_mangrove_propagule_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_hanging_mangrove_propagule_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_lily_pad_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_lily_pad_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..57903dc6b3 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_lily_pad_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_lily_pad_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_lily_pad_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_lily_pad_test/lang/en_us.json new file mode 100644 index 0000000000..3e10d30967 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_lily_pad_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_lily_pad_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_lily_pad_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_lily_pad_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_lily_pad_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_lily_pad_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_lily_pad_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..0cc8782201 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_lily_pad_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_lily_pad_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_oak_sapling_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_oak_sapling_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..37e3445281 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_oak_sapling_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_oak_sapling_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_oak_sapling_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_oak_sapling_test/lang/en_us.json new file mode 100644 index 0000000000..1f61b0dbdf --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_oak_sapling_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_oak_sapling_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_oak_sapling_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_oak_sapling_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_oak_sapling_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_oak_sapling_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_oak_sapling_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ee24f27c89 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_oak_sapling_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_oak_sapling_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_pitcher_crop_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_pitcher_crop_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..b452e7f3f4 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_pitcher_crop_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_pitcher_crop_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_pitcher_crop_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_pitcher_crop_test/lang/en_us.json new file mode 100644 index 0000000000..60bc7c3276 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_pitcher_crop_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_pitcher_crop_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_pitcher_crop_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_pitcher_crop_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_pitcher_crop_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_pitcher_crop_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_pitcher_crop_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..312d3d7b25 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_pitcher_crop_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_pitcher_crop_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_red_mushroom_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_red_mushroom_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..b8c9329049 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_red_mushroom_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_red_mushroom_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_red_mushroom_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_red_mushroom_test/lang/en_us.json new file mode 100644 index 0000000000..df8053bd24 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_red_mushroom_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_red_mushroom_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_red_mushroom_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_red_mushroom_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_red_mushroom_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_red_mushroom_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_red_mushroom_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..615e1e61e3 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_red_mushroom_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_red_mushroom_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_small_dripleaf_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_small_dripleaf_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..f327bbb6e4 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_small_dripleaf_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_small_dripleaf_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_small_dripleaf_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_small_dripleaf_test/lang/en_us.json new file mode 100644 index 0000000000..57f103921a --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_small_dripleaf_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_small_dripleaf_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_small_dripleaf_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_small_dripleaf_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_small_dripleaf_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_small_dripleaf_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_small_dripleaf_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..f80a657ba9 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_small_dripleaf_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_small_dripleaf_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_sugar_cane_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_sugar_cane_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..91c150d6e7 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_sugar_cane_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_sugar_cane_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_sugar_cane_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_sugar_cane_test/lang/en_us.json new file mode 100644 index 0000000000..356a918db0 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_sugar_cane_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_sugar_cane_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_sugar_cane_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_sugar_cane_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_sugar_cane_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_sugar_cane_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_sugar_cane_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..dc36b30022 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_sugar_cane_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_sugar_cane_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_wheat_test/blockstates/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_wheat_test/blockstates/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..935ca60212 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_wheat_test/blockstates/super_sustaining_sustaining_block.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "neotests_survivability_wheat_test:block/super_sustaining_sustaining_block" + } + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_wheat_test/lang/en_us.json b/tests/src/generated/resources/assets/neotests_survivability_wheat_test/lang/en_us.json new file mode 100644 index 0000000000..ad1d5cceeb --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_wheat_test/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.neotests_survivability_wheat_test.super_sustaining_sustaining_block": "Super Sustaining block" +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_wheat_test/models/block/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_wheat_test/models/block/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..ce4a458c61 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_wheat_test/models/block/super_sustaining_sustaining_block.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "testframework:block/white" + } +} \ No newline at end of file diff --git a/tests/src/generated/resources/assets/neotests_survivability_wheat_test/models/item/super_sustaining_sustaining_block.json b/tests/src/generated/resources/assets/neotests_survivability_wheat_test/models/item/super_sustaining_sustaining_block.json new file mode 100644 index 0000000000..5664b91975 --- /dev/null +++ b/tests/src/generated/resources/assets/neotests_survivability_wheat_test/models/item/super_sustaining_sustaining_block.json @@ -0,0 +1,3 @@ +{ + "parent": "neotests_survivability_wheat_test:block/super_sustaining_sustaining_block" +} \ No newline at end of file diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java index 2f6b2e0e2b..3ac8ee66ef 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java @@ -40,7 +40,6 @@ import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; import net.neoforged.testframework.gametest.EmptyTemplate; -import net.neoforged.testframework.gametest.ExtendedGameTestHelper; import net.neoforged.testframework.gametest.StructureTemplateBuilder; import net.neoforged.testframework.registration.RegistrationHelper; @@ -132,32 +131,6 @@ public Optional getRespawnPosition(BlockState stat .thenSucceed()); } - @GameTest - @EmptyTemplate(floor = true) - @TestHolder(description = { - "Dead bushes should be placeable on regular terracotta (colored or not), but not on glazed terracotta", - "(neoforged/NeoForge#306)" - }) - static void deadBushTerracottaTest(final ExtendedGameTestHelper helper) { - final BlockPos farmlandBlock = new BlockPos(1, 1, 1); - helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) - .thenExecute(() -> helper.setBlock(farmlandBlock, Blocks.TERRACOTTA)) - .thenExecute(player -> helper.useBlock(farmlandBlock, player, new ItemStack(Items.DEAD_BUSH), Direction.UP)) - .thenExecute(() -> helper.assertBlockPresent(Blocks.DEAD_BUSH, farmlandBlock.above())) - - .thenExecute(() -> helper.setBlock(farmlandBlock.above(), Blocks.AIR)) - .thenExecute(() -> helper.setBlock(farmlandBlock, Blocks.WHITE_TERRACOTTA)) - .thenExecute(player -> helper.useBlock(farmlandBlock, player, new ItemStack(Items.DEAD_BUSH), Direction.UP)) - .thenExecute(() -> helper.assertBlockPresent(Blocks.DEAD_BUSH, farmlandBlock.above())) - - .thenExecute(() -> helper.setBlock(farmlandBlock.above(), Blocks.AIR)) - .thenExecute(() -> helper.setBlock(farmlandBlock, Blocks.WHITE_GLAZED_TERRACOTTA)) - .thenExecute(player -> helper.useBlock(farmlandBlock, player, new ItemStack(Items.DEAD_BUSH), Direction.UP)) - .thenExecute(() -> helper.assertBlockNotPresent(Blocks.DEAD_BUSH, farmlandBlock.above())) - - .thenSucceed(); - } - @GameTest() @TestHolder(description = "Adds a block that can sustain Bubble Columns and verify it works") static void bubbleColumnTest(final DynamicTest test, final RegistrationHelper reg) { diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/block/CanSustainPlantTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/block/CanSustainPlantTests.java new file mode 100644 index 0000000000..8563305c31 --- /dev/null +++ b/tests/src/main/java/net/neoforged/neoforge/debug/block/CanSustainPlantTests.java @@ -0,0 +1,721 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.debug.block; + +import net.minecraft.commands.arguments.EntityAnchorArgument; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.GameType; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.ChorusPlantBlock; +import net.minecraft.world.level.block.MangrovePropaguleBlock; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.common.util.TriState; +import net.neoforged.testframework.DynamicTest; +import net.neoforged.testframework.annotation.ForEachTest; +import net.neoforged.testframework.annotation.TestHolder; +import net.neoforged.testframework.gametest.EmptyTemplate; +import net.neoforged.testframework.registration.RegistrationHelper; + +@ForEachTest(groups = { "level.block.survivability" }) +public class CanSustainPlantTests { + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Lily Pads should be placeable on water surface but not lava or land. And plantable on custom blocks that allow the plant." + }) + static void survivabilityLilyPadTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 1, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(player -> player.moveTo(helper.absolutePos(belowBlock).above().north().getCenter())) + .thenExecute(player -> player.lookAt(EntityAnchorArgument.Anchor.EYES, helper.absolutePos(belowBlock).getCenter())) + + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.WATER)) + .thenExecute(player -> player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(Items.LILY_PAD))) + .thenExecute(player -> Items.LILY_PAD.use(helper.getLevel(), player, InteractionHand.MAIN_HAND)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.LILY_PAD, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.LAVA)) + .thenExecute(player -> player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(Items.LILY_PAD))) + .thenExecute(player -> Items.LILY_PAD.use(helper.getLevel(), player, InteractionHand.MAIN_HAND)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.LILY_PAD, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.GRASS_BLOCK)) + .thenExecute(player -> player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(Items.LILY_PAD))) + .thenExecute(player -> Items.LILY_PAD.use(helper.getLevel(), player, InteractionHand.MAIN_HAND)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.LILY_PAD, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) + .thenExecute(player -> player.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(Items.LILY_PAD))) + .thenExecute(player -> Items.LILY_PAD.use(helper.getLevel(), player, InteractionHand.MAIN_HAND)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.LILY_PAD, belowBlock.above())) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Red Mushroom should be placeable on mycelium in any light and on other blocks when dark. Never on farmland. And plantable on custom blocks that allow the plant." + }) + static void survivabilityRedMushroomTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 1, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.MYCELIUM)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.RED_MUSHROOM), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.RED_MUSHROOM, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.STONE)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.RED_MUSHROOM), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.RED_MUSHROOM, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.RED_MUSHROOM), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.RED_MUSHROOM, belowBlock.above())) + + // Commented out due to issues with getting lighting engine to recalculate dark area in time. +// .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().north(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().south(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().east(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().west(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above(2), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock, Blocks.STONE)) +// .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.RED_MUSHROOM), Direction.UP)) +// .thenExecute(() -> helper.assertBlockPresent(Blocks.RED_MUSHROOM, belowBlock.above())) +// +// .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().north(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().south(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().east(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().west(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above(2), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock, Blocks.FARMLAND)) +// .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.RED_MUSHROOM), Direction.UP)) +// .thenExecute(() -> helper.assertBlockNotPresent(Blocks.RED_MUSHROOM, belowBlock.above())) +// +// .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().north(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().south(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().east(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().west(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above(2), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) +// .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.RED_MUSHROOM), Direction.UP)) +// .thenExecute(() -> helper.assertBlockPresent(Blocks.RED_MUSHROOM, belowBlock.above())) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Wheat should be placeable on farmland but not on dirt nor in too dark areas. And plantable on custom blocks that allow the plant." + }) + static void survivabilityWheatTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 1, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.FARMLAND)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.WHEAT_SEEDS), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.WHEAT, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.DIRT)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.WHEAT_SEEDS), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.WHEAT, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.WHEAT_SEEDS), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.WHEAT, belowBlock.above())) + + // Commented out due to issues with getting lighting engine to recalculate dark area in time. +// .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().north(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().south(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().east(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().west(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above(2), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock, Blocks.FARMLAND)) +// .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.WHEAT_SEEDS), Direction.UP)) +// .thenExecute(() -> helper.assertBlockNotPresent(Blocks.WHEAT, belowBlock.above())) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Pitcher Crop should be placeable on farmland but not on dirt nor in too dark areas. And plantable on custom blocks that allow the plant." + }) + static void survivabilityPitcherCropTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 1, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.FARMLAND)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.PITCHER_POD), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.PITCHER_CROP, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.DIRT)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.PITCHER_POD), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.PITCHER_CROP, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.PITCHER_POD), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.PITCHER_CROP, belowBlock.above())) + + // Commented out due to issues with getting lighting engine to recalculate dark area in time. +// .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().north(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().south(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().east(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above().west(), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock.above(2), Blocks.STONE)) +// .thenExecute(() -> helper.setBlock(belowBlock, Blocks.FARMLAND)) +// .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.PITCHER_POD), Direction.UP)) +// .thenExecute(() -> helper.assertBlockNotPresent(Blocks.PITCHER_CROP, belowBlock.above())) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Bamboo Stalk should be placeable on dirt, sand, gravel, other bamboo, but not on farmland. And plantable on custom blocks that allow the plant." + }) + static void survivabilityBambooTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 1, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.DIRT)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.BAMBOO), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.BAMBOO_SAPLING, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.BAMBOO)) + .thenExecute(player -> helper.useBlock(belowBlock.above(), player, new ItemStack(Items.BAMBOO), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.BAMBOO, belowBlock.above(2))) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock.above(2), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.SAND)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.BAMBOO), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.BAMBOO_SAPLING, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.GRAVEL)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.BAMBOO), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.BAMBOO_SAPLING, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.FARMLAND)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.BAMBOO), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.BAMBOO_SAPLING, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.BAMBOO), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.BAMBOO_SAPLING, belowBlock.above())) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Cactus should be placeable on sand, red sand, other cactus, but not on terracotta. And plantable on custom blocks that allow the plant." + }) + static void survivabilityCactusTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 1, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.SAND)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.CACTUS), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.CACTUS, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.CACTUS)) + .thenExecute(player -> helper.useBlock(belowBlock.above(), player, new ItemStack(Items.CACTUS), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.CACTUS, belowBlock.above(2))) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.RED_SAND)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.CACTUS), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.CACTUS, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.TERRACOTTA)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.CACTUS), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.CACTUS, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.CACTUS), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.CACTUS, belowBlock.above())) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Dead Bushes should be placeable on dirt, sand, and terracotta, but not on glazed terracotta nor farmland. And plantable on custom blocks that allow the plant.", + "(neoforged/NeoForge#306)" + }) + static void survivabilityDeadBushTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 1, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.TERRACOTTA)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.DEAD_BUSH), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.DEAD_BUSH, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.WHITE_TERRACOTTA)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.DEAD_BUSH), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.DEAD_BUSH, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.DIRT)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.DEAD_BUSH), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.DEAD_BUSH, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.SAND)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.DEAD_BUSH), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.DEAD_BUSH, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.WHITE_GLAZED_TERRACOTTA)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.DEAD_BUSH), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.DEAD_BUSH, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.FARMLAND)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.DEAD_BUSH), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.DEAD_BUSH, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.DEAD_BUSH), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.DEAD_BUSH, belowBlock.above())) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Oak Sapling should be placeable on dirt and farmland but not on sand. And plantable on custom blocks that allow the plant." + }) + static void survivabilityOakSaplingTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 1, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.DIRT)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.OAK_SAPLING), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.OAK_SAPLING, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.FARMLAND)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.OAK_SAPLING), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.OAK_SAPLING, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.SAND)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.OAK_SAPLING), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.OAK_SAPLING, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.OAK_SAPLING), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.OAK_SAPLING, belowBlock.above())) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Hanging Mangrove Propagule should be survivable on underside of Mangrove Leaves but not Stone. And survivable on custom blocks that allow the plant." + }) + static void survivabilityHangingMangrovePropaguleTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos aboveBlock = new BlockPos(1, 3, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(aboveBlock, Blocks.MANGROVE_LEAVES)) + .thenExecute(() -> helper.setBlock(aboveBlock.below(), Blocks.MANGROVE_PROPAGULE.defaultBlockState().setValue(MangrovePropaguleBlock.HANGING, true))) + .thenExecute(() -> helper.setBlock(aboveBlock.below().north(), Blocks.STONE)) // Trigger block update on neighbors + .thenExecute(() -> helper.assertBlockPresent(Blocks.MANGROVE_PROPAGULE, aboveBlock.below())) + + .thenExecute(() -> helper.setBlock(aboveBlock, Blocks.STONE)) + .thenExecute(() -> helper.setBlock(aboveBlock.below(), Blocks.MANGROVE_PROPAGULE.defaultBlockState().setValue(MangrovePropaguleBlock.HANGING, true))) + .thenExecute(() -> helper.setBlock(aboveBlock.below().north(), Blocks.DIRT)) // Trigger block update on neighbors + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.MANGROVE_PROPAGULE, aboveBlock.below())) + + .thenExecute(() -> helper.setBlock(aboveBlock, sustainingBlock.get())) + .thenExecute(() -> helper.setBlock(aboveBlock.below(), Blocks.MANGROVE_PROPAGULE.defaultBlockState().setValue(MangrovePropaguleBlock.HANGING, true))) + .thenExecute(() -> helper.setBlock(aboveBlock.below().north(), Blocks.STONE)) // Trigger block update on neighbors + .thenExecute(() -> helper.assertBlockPresent(Blocks.MANGROVE_PROPAGULE, aboveBlock.below())) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Sugar Cane should be placeable on dirt and sand next to water or frosted ice. And plantable on other sugar cane or custom blocks that allow the plant. Not plantable next to lava or on farmland." + }) + static void survivabilitySugarCaneTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 1, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.DIRT)) + .thenExecute(() -> helper.setBlock(belowBlock.north(), Blocks.WATER)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.SUGAR_CANE), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.SUGAR_CANE, belowBlock.above())) + + .thenExecute(player -> helper.useBlock(belowBlock.above(), player, new ItemStack(Items.SUGAR_CANE), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.SUGAR_CANE, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(2), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.SAND)) + .thenExecute(() -> helper.setBlock(belowBlock.north(), Blocks.WATER)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.SUGAR_CANE), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.SUGAR_CANE, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) + .thenExecute(() -> helper.setBlock(belowBlock.north(), Blocks.IRON_BLOCK)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.SUGAR_CANE), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.SUGAR_CANE, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.SAND)) + .thenExecute(() -> helper.setBlock(belowBlock.north(), Blocks.LAVA)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.SUGAR_CANE), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.SUGAR_CANE, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.FARMLAND)) + .thenExecute(() -> helper.setBlock(belowBlock.north(), Blocks.WATER)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.SUGAR_CANE), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.SUGAR_CANE, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.DIRT)) + .thenExecute(() -> helper.setBlock(belowBlock.north(), Blocks.IRON_BLOCK)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.SUGAR_CANE), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.SUGAR_CANE, belowBlock.above())) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Small Dripleaf should be placeable on any block in water or on dry clay and moss blocks. And plantable on custom blocks that allow the plant even outside of water." + }) + static void survivabilitySmallDripleafTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 2, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(belowBlock.north(), Blocks.DIRT)) + .thenExecute(() -> helper.setBlock(belowBlock.south(), Blocks.DIRT)) + .thenExecute(() -> helper.setBlock(belowBlock.east(), Blocks.DIRT)) + .thenExecute(() -> helper.setBlock(belowBlock.west(), Blocks.DIRT)) + + .thenExecute(() -> helper.setBlock(belowBlock.below(), Blocks.DIRT)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.WATER)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.SMALL_DRIPLEAF), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.SMALL_DRIPLEAF, belowBlock)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.SMALL_DRIPLEAF, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock.below(), sustainingBlock.get())) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.SMALL_DRIPLEAF), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.SMALL_DRIPLEAF, belowBlock)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.SMALL_DRIPLEAF, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.below(), Blocks.CLAY)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.SMALL_DRIPLEAF), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.SMALL_DRIPLEAF, belowBlock)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.SMALL_DRIPLEAF, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.below(), Blocks.MOSS_BLOCK)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.SMALL_DRIPLEAF), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.SMALL_DRIPLEAF, belowBlock)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.SMALL_DRIPLEAF, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.below(), Blocks.GRASS_BLOCK)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.SMALL_DRIPLEAF), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.SMALL_DRIPLEAF, belowBlock)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.SMALL_DRIPLEAF, belowBlock.above())) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Big Dripleaf should be placeable on dirt, farmland, clay, moss block, and big dripleaf but not on sand. And plantable on custom blocks that allow the plant." + }) + static void survivabilityBigDripleafTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 1, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.DIRT)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.BIG_DRIPLEAF), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.BIG_DRIPLEAF, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.FARMLAND)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.BIG_DRIPLEAF), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.BIG_DRIPLEAF, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.CLAY)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.BIG_DRIPLEAF), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.BIG_DRIPLEAF, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.MOSS_BLOCK)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.BIG_DRIPLEAF), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.BIG_DRIPLEAF, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.BIG_DRIPLEAF), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.BIG_DRIPLEAF, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.SAND)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.BIG_DRIPLEAF), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.BIG_DRIPLEAF, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.DIRT)) + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.BIG_DRIPLEAF)) + .thenExecute(player -> helper.useBlock(belowBlock.above(), player, new ItemStack(Items.BIG_DRIPLEAF), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.BIG_DRIPLEAF, belowBlock.above(2))) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Chorus Plant should be placeable on End Stone and itself but not on End Stone Bricks or Dirt. And plantable on custom blocks that allow the plant." + }) + static void survivabilityChorusPlantTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 1, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.END_STONE)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.CHORUS_PLANT), Direction.UP)) + .thenExecute(() -> helper.assertBlockState(belowBlock.above(), (state) -> state.getValue(ChorusPlantBlock.DOWN), () -> "Chorus Plant not found with down property")) + + .thenExecute(player -> helper.useBlock(belowBlock.above(), player, new ItemStack(Items.CHORUS_PLANT), Direction.UP)) + .thenExecute(() -> helper.assertBlockState(belowBlock.above(2), (state) -> state.getValue(ChorusPlantBlock.DOWN), () -> "Chorus Plant not found with down property")) + + .thenExecute(() -> helper.setBlock(belowBlock.above(2), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.END_STONE_BRICKS)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.CHORUS_PLANT), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.CHORUS_PLANT, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.DIRT)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.CHORUS_PLANT), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.CHORUS_PLANT, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.CHORUS_PLANT), Direction.UP)) + .thenExecute(() -> helper.assertBlockState(belowBlock.above(), (state) -> state.getValue(ChorusPlantBlock.DOWN), () -> "Chorus Plant not found with down property")) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Chorus Flower should be placeable on End Stone and Chorus Plant but not on End Stone Bricks or Dirt. And plantable on custom blocks that allow the plant." + }) + static void survivabilityChorusFlowerTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos belowBlock = new BlockPos(1, 1, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.END_STONE)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.CHORUS_FLOWER), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.CHORUS_FLOWER, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.CHORUS_PLANT)) + .thenExecute(player -> helper.useBlock(belowBlock.above(), player, new ItemStack(Items.CHORUS_FLOWER), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.CHORUS_FLOWER, belowBlock.above(2))) + + .thenExecute(() -> helper.setBlock(belowBlock.above(2), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.END_STONE_BRICKS)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.CHORUS_FLOWER), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.CHORUS_FLOWER, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, Blocks.DIRT)) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.CHORUS_FLOWER), Direction.UP)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.CHORUS_FLOWER, belowBlock.above())) + + .thenExecute(() -> helper.setBlock(belowBlock.above(), Blocks.AIR)) + .thenExecute(() -> helper.setBlock(belowBlock, sustainingBlock.get())) + .thenExecute(player -> helper.useBlock(belowBlock, player, new ItemStack(Items.CHORUS_FLOWER), Direction.UP)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.CHORUS_FLOWER, belowBlock.above())) + + .thenSucceed()); + } + + @GameTest + @EmptyTemplate(floor = true) + @TestHolder(description = { + "Cocoa should be placeable on Jungle Logs but not on Oak Logs. And plantable on custom blocks that allow the plant." + }) + static void survivabilityCocoaTest(final DynamicTest test, final RegistrationHelper reg) { + final var sustainingBlock = reg.blocks() + .registerBlock("super_sustaining_sustaining_block", CustomSuperSustainingBlock::new, BlockBehaviour.Properties.of()) + .withLang("Super Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + final BlockPos centerBlock = new BlockPos(1, 2, 1); + test.onGameTest(helper -> helper.startSequence(() -> helper.makeTickingMockServerPlayerInCorner(GameType.SURVIVAL)) + .thenExecute(() -> helper.setBlock(centerBlock.north(), Blocks.JUNGLE_LOG)) + .thenExecute(player -> helper.useBlock(centerBlock.north(), player, new ItemStack(Items.COCOA_BEANS), Direction.SOUTH)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.COCOA, centerBlock)) + + .thenExecute(() -> helper.setBlock(centerBlock, Blocks.AIR)) + .thenExecute(() -> helper.setBlock(centerBlock.north(), Blocks.OAK_LOG)) + .thenExecute(player -> helper.useBlock(centerBlock.north(), player, new ItemStack(Items.COCOA_BEANS), Direction.SOUTH)) + .thenExecute(() -> helper.assertBlockNotPresent(Blocks.COCOA, centerBlock)) + + .thenExecute(() -> helper.setBlock(centerBlock, Blocks.AIR)) + .thenExecute(() -> helper.setBlock(centerBlock.north(), sustainingBlock.get())) + .thenExecute(player -> helper.useBlock(centerBlock.north(), player, new ItemStack(Items.COCOA_BEANS), Direction.SOUTH)) + .thenExecute(() -> helper.assertBlockPresent(Blocks.COCOA, centerBlock)) + + .thenSucceed()); + } + + private static class CustomSuperSustainingBlock extends Block { + public CustomSuperSustainingBlock(Properties properties) { + super(properties); + } + + @Override + public TriState canSustainPlant(BlockState state, BlockGetter level, BlockPos soilPosition, Direction facing, BlockState plant) { + if (level.getBlockState(soilPosition).getBlock() != this) { + throw new RuntimeException("Incorrect soil position param passed in"); + } + + if (level.getBlockState(soilPosition.relative(facing)).canOcclude()) { + throw new RuntimeException("Incorrect direction param passed in"); + } + + return TriState.TRUE; + } + } +} diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/block/CustomPlantTypeTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/block/CustomPlantTypeTest.java index 3eda37e16e..a96de26e09 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/block/CustomPlantTypeTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/block/CustomPlantTypeTest.java @@ -15,6 +15,7 @@ import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.FlowerBlock; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.state.BlockState; @@ -22,8 +23,7 @@ import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.Mod; -import net.neoforged.neoforge.common.IPlantable; -import net.neoforged.neoforge.common.PlantType; +import net.neoforged.neoforge.common.util.TriState; import net.neoforged.neoforge.registries.DeferredBlock; import net.neoforged.neoforge.registries.RegisterEvent; @@ -59,36 +59,27 @@ public CustomBlock() { } @Override - public boolean canSustainPlant(BlockState state, BlockGetter level, BlockPos pos, Direction facing, IPlantable plantable) { - PlantType type = plantable.getPlantType(level, pos.relative(facing)); - if (type != null && type == CustomPlantBlock.pt) { - return true; + public TriState canSustainPlant(BlockState state, BlockGetter level, BlockPos pos, Direction facing, BlockState plantable) { + if (plantable.is(CUSTOM_PLANT)) { + return TriState.TRUE; } return super.canSustainPlant(state, level, pos, facing, plantable); } } - public static class CustomPlantBlock extends FlowerBlock implements IPlantable { - public static PlantType pt = PlantType.get("custom_plant_type"); - + public static class CustomPlantBlock extends FlowerBlock { public CustomPlantBlock() { super(MobEffects.WEAKNESS, 9, Properties.of().mapColor(MapColor.PLANT).noCollission().sound(SoundType.GRASS)); } - @Override - public PlantType getPlantType(BlockGetter level, BlockPos pos) { - return pt; - } - - @Override - public BlockState getPlant(BlockGetter level, BlockPos pos) { - return defaultBlockState(); - } - @Override public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { BlockState soil = world.getBlockState(pos.below()); - return soil.canSustainPlant(world, pos, Direction.UP, this); + TriState soilDecision = soil.canSustainPlant(world, pos.below(), Direction.UP, state); + if (soilDecision.isDefault()) { + return soil.is(Blocks.MAGMA_BLOCK); + } + return soilDecision.isTrue(); } @Override diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/block/OnTreeGrowBlockTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/block/OnTreeGrowBlockTest.java index 9ff444be07..2912f1e498 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/block/OnTreeGrowBlockTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/block/OnTreeGrowBlockTest.java @@ -21,8 +21,8 @@ import net.minecraft.world.level.material.MapColor; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.common.Mod; -import net.neoforged.neoforge.common.IPlantable; import net.neoforged.neoforge.common.extensions.IBlockStateExtension; +import net.neoforged.neoforge.common.util.TriState; import net.neoforged.neoforge.registries.DeferredItem; import net.neoforged.neoforge.registries.DeferredRegister; @@ -42,8 +42,8 @@ public class OnTreeGrowBlockTest { public static final Holder TEST_GRASS_BLOCK = BLOCKS.register("test_grass_block", () -> new Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).destroyTime(1.5f)) { @Override - public boolean canSustainPlant(BlockState state, BlockGetter world, BlockPos pos, Direction facing, IPlantable plantable) { - return plantable instanceof SaplingBlock; + public TriState canSustainPlant(BlockState state, BlockGetter world, BlockPos pos, Direction facing, BlockState plantable) { + return plantable.getBlock() instanceof SaplingBlock ? TriState.TRUE : TriState.DEFAULT; } @Override @@ -59,8 +59,8 @@ public boolean onTreeGrow(BlockState state, LevelReader level, BiConsumer TEST_DIRT = BLOCKS.register("test_dirt", () -> new Block(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_LIGHT_BLUE).destroyTime(1.5f)) { @Override - public boolean canSustainPlant(BlockState state, BlockGetter world, BlockPos pos, Direction facing, IPlantable plantable) { - return plantable instanceof SaplingBlock; + public TriState canSustainPlant(BlockState state, BlockGetter world, BlockPos pos, Direction facing, BlockState plantable) { + return plantable.getBlock() instanceof SaplingBlock ? TriState.TRUE : TriState.DEFAULT; } }); public static final DeferredItem TEST_GRASS_BLOCK_ITEM = ITEMS.registerSimpleBlockItem(TEST_GRASS_BLOCK);