[Feature] New Bonemeal API

This commit is contained in:
Frank 2022-11-12 15:15:38 +01:00
parent 41b1d9eb68
commit 5b99e0be53
7 changed files with 328 additions and 1 deletions

View file

@ -0,0 +1,103 @@
package org.betterx.bclib.api.v3.bonemeal;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.BonemealableBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.feature.configurations.RandomPatchConfiguration;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import java.util.List;
public interface BonemealGrassLike extends BonemealableBlock {
BlockState growableCoverState(); //Blocks.GRASS.defaultBlockState();
Block hostBlock(); //this
Holder<PlacedFeature> coverFeature(); //VegetationPlacements.GRASS_BONEMEAL
List<ConfiguredFeature<?, ?>> flowerFeatures(); /*serverLevel.getBiome(currentPos)
.value()
.getGenerationSettings()
.getFlowerFeatures();*/
default boolean canGrowFlower(RandomSource random) {
return random.nextInt(8) == 0;
}
default boolean canGrowCover(RandomSource random) {
return random.nextInt(10) == 0;
}
default boolean isValidBonemealTarget(
BlockGetter blockGetter,
BlockPos blockPos,
BlockState blockState,
boolean bl
) {
return blockGetter.getBlockState(blockPos.above()).isAir();
}
default boolean isBonemealSuccess(
Level level,
RandomSource randomSource,
BlockPos blockPos,
BlockState blockState
) {
return true;
}
default void performBonemeal(ServerLevel serverLevel, RandomSource random, BlockPos pos, BlockState state) {
final BlockPos above = pos.above();
final BlockState growableState = growableCoverState();
outerLoop:
for (int bonemealAttempt = 0; bonemealAttempt < 128; ++bonemealAttempt) {
BlockPos currentPos = above;
for (int j = 0; j < bonemealAttempt / 16; ++j) {
currentPos = currentPos.offset(
random.nextInt(3) - 1,
(random.nextInt(3) - 1) * random.nextInt(3) / 2,
random.nextInt(3) - 1
);
if (!serverLevel.getBlockState(currentPos.below()).is(hostBlock())
|| serverLevel.getBlockState(currentPos)
.isCollisionShapeFullBlock(serverLevel, currentPos)) {
continue outerLoop;
}
}
BlockState currentState = serverLevel.getBlockState(currentPos);
if (currentState.is(growableState.getBlock()) && canGrowCover(random)) {
((BonemealableBlock) growableState.getBlock()).performBonemeal(
serverLevel,
random,
currentPos,
currentState
);
}
if (currentState.isAir()) {
Holder<PlacedFeature> boneFeature;
if (canGrowFlower(random)) {
List<ConfiguredFeature<?, ?>> list = flowerFeatures();
if (list.isEmpty()) {
continue;
}
boneFeature = ((RandomPatchConfiguration) list.get(0).config()).feature();
} else {
boneFeature = coverFeature();
}
boneFeature.value()
.place(serverLevel, serverLevel.getChunkSource().getGenerator(), random, currentPos);
}
}
}
}

View file

@ -0,0 +1,52 @@
package org.betterx.bclib.api.v3.bonemeal;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.BonemealableBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
public interface BonemealNyliumLike extends BonemealableBlock {
Block hostBlock(); //this
Holder<PlacedFeature> coverFeature();
default boolean isValidBonemealTarget(
BlockGetter blockGetter,
BlockPos blockPos,
BlockState blockState,
boolean bl
) {
return blockGetter.getBlockState(blockPos.above()).isAir();
}
default boolean isBonemealSuccess(
Level level,
RandomSource randomSource,
BlockPos blockPos,
BlockState blockState
) {
return true;
}
default void performBonemeal(
ServerLevel serverLevel,
RandomSource randomSource,
BlockPos blockPos,
BlockState blockState
) {
final BlockState currentState = serverLevel.getBlockState(blockPos);
final BlockPos above = blockPos.above();
final ChunkGenerator generator = serverLevel.getChunkSource().getGenerator();
if (currentState.is(hostBlock())) {
coverFeature()
.value()
.place(serverLevel, generator, randomSource, above);
}
}
}

View file

@ -0,0 +1,20 @@
package org.betterx.bclib.api.v3.bonemeal;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockState;
public interface BonemealSpreader {
boolean isValidBonemealSpreadTarget(BlockGetter blockGetter, BlockPos blockPos, BlockState blockState, boolean bl);
boolean performBonemealSpread(
ServerLevel serverLevel,
RandomSource randomSource,
BlockPos blockPos,
BlockState blockState
);
}

View file

@ -0,0 +1,31 @@
package org.betterx.bclib.api.v3.bonemeal;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockState;
public class EndStoneSpreader implements BonemealSpreader {
public static final EndStoneSpreader INSTANCE = new EndStoneSpreader();
@Override
public boolean isValidBonemealSpreadTarget(
BlockGetter blockGetter,
BlockPos blockPos,
BlockState blockState,
boolean bl
) {
return false;
}
@Override
public boolean performBonemealSpread(
ServerLevel serverLevel,
RandomSource randomSource,
BlockPos blockPos,
BlockState blockState
) {
return false;
}
}

View file

@ -0,0 +1,81 @@
package org.betterx.bclib.api.v3.bonemeal;
import org.betterx.bclib.util.WeightedList;
import org.betterx.worlds.together.tag.v3.CommonBlockTags;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import java.util.HashMap;
import java.util.Map;
public class NetherrackSpreader implements BonemealSpreader {
public static final NetherrackSpreader INSTANCE = new NetherrackSpreader();
public boolean isValidBonemealSpreadTarget(
BlockGetter blockGetter,
BlockPos blockPos,
BlockState blockState,
boolean bl
) {
if (!blockGetter.getBlockState(blockPos.above()).propagatesSkylightDown(blockGetter, blockPos)) {
return false;
} else {
for (BlockPos testPos : BlockPos.betweenClosed(
blockPos.offset(-1, -1, -1),
blockPos.offset(1, 1, 1)
)) {
if (blockGetter.getBlockState(testPos).is(CommonBlockTags.NETHERRACK_SPREADABLE))
return true;
}
return false;
}
}
public boolean performBonemealSpread(
ServerLevel serverLevel,
RandomSource randomSource,
BlockPos blockPos,
BlockState blockState
) {
Map<Block, Integer> sourceBlocks = new HashMap<>();
boolean hasNonVanilla = false;
for (BlockPos testPos : BlockPos.betweenClosed(
blockPos.offset(-1, -1, -1),
blockPos.offset(1, 1, 1)
)) {
BlockState state = serverLevel.getBlockState(testPos);
if (serverLevel.getBlockState(testPos).is(CommonBlockTags.NETHERRACK_SPREADABLE)) {
sourceBlocks.compute(state.getBlock(), (k, v) -> {
if (v == null) return 1;
return v + 1;
});
}
if (!state.is(Blocks.WARPED_NYLIUM) && state.is(Blocks.CRIMSON_NYLIUM)) {
hasNonVanilla = true;
}
}
if (hasNonVanilla) {
WeightedList<Block> list = new WeightedList<>();
for (Map.Entry<Block, Integer> e : sourceBlocks.entrySet()) {
list.add(e.getKey(), e.getValue());
}
Block bl = list.get(randomSource);
if (bl != null) {
serverLevel.setBlock(blockPos, bl.defaultBlockState(), 3);
return true;
}
}
return false;
}
}

View file

@ -2,14 +2,19 @@ package org.betterx.bclib.mixin.common;
import org.betterx.bclib.api.v2.BonemealAPI;
import org.betterx.bclib.api.v2.levelgen.biomes.BiomeAPI;
import org.betterx.bclib.api.v3.bonemeal.BonemealSpreader;
import org.betterx.bclib.api.v3.bonemeal.EndStoneSpreader;
import org.betterx.bclib.api.v3.bonemeal.NetherrackSpreader;
import org.betterx.bclib.util.BlocksHelper;
import org.betterx.bclib.util.MHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.item.BoneMealItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
@ -33,7 +38,7 @@ public class BoneMealItemMixin {
@Inject(method = "useOn", at = @At("HEAD"), cancellable = true)
private void bclib_onUse(UseOnContext context, CallbackInfoReturnable<InteractionResult> info) {
Level world = context.getLevel();
BlockPos blockPos = context.getClickedPos();
final BlockPos blockPos = context.getClickedPos();
if (!world.isClientSide()) {
if (BonemealAPI.isTerrain(world.getBlockState(blockPos).getBlock())) {
boolean consume = false;
@ -65,6 +70,36 @@ public class BoneMealItemMixin {
}
}
@Inject(method = "growCrop", at = @At("HEAD"), cancellable = true)
private static void growCrop(
ItemStack itemStack,
Level level,
BlockPos blockPos,
CallbackInfoReturnable<Boolean> cir
) {
BlockState blockState = level.getBlockState(blockPos);
BonemealSpreader spreader = null;
if (blockState.is(Blocks.NETHERRACK)) {
spreader = NetherrackSpreader.INSTANCE;
} else if (blockState.is(Blocks.END_STONE)) {
spreader = EndStoneSpreader.INSTANCE;
} else if (blockState.getBlock() instanceof BonemealSpreader s) {
spreader = s;
}
if (spreader != null) {
if (spreader.isValidBonemealSpreadTarget(level, blockPos, blockState, level.isClientSide)) {
if (level instanceof ServerLevel) {
if (spreader.performBonemealSpread((ServerLevel) level, level.random, blockPos, blockState)) {
itemStack.shrink(1);
cir.setReturnValue(true);
}
}
}
}
}
@Unique
private boolean bclib_growLandGrass(Level level, BlockPos pos) {
int y1 = pos.getY() + 3;

View file

@ -35,7 +35,12 @@ public class CommonBlockTags {
public static final TagKey<Block> TERRAIN = TagManager.BLOCKS.makeCommonTag("terrain");
public static final TagKey<Block> NETHER_TERRAIN = TagManager.BLOCKS.makeCommonTag("nether_terrain");
public static final TagKey<Block> NETHERRACK_SPREADABLE = TagManager.BLOCKS.makeCommonTag(
"netherrack_spreadable");
static void prepareTags() {
TagManager.BLOCKS.add(NETHERRACK_SPREADABLE, Blocks.WARPED_NYLIUM, Blocks.CRIMSON_NYLIUM);
TagManager.BLOCKS.add(SCULK_LIKE, Blocks.SCULK);
TagManager.BLOCKS.addOtherTags(DRAGON_IMMUNE, BlockTags.DRAGON_IMMUNE);