Some Feature related fixes

This commit is contained in:
Frank 2022-06-03 00:08:26 +02:00
parent 6adf6486ac
commit 7b9936af05
16 changed files with 310 additions and 57 deletions

View file

@ -1,19 +1,20 @@
package org.betterx.bclib.api.features; package org.betterx.bclib.api.features;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
import net.minecraft.data.worldgen.placement.PlacementUtils; import net.minecraft.data.worldgen.placement.PlacementUtils;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.valueproviders.IntProvider;
import net.minecraft.util.valueproviders.UniformInt; import net.minecraft.util.valueproviders.UniformInt;
import net.minecraft.world.level.levelgen.GenerationStep.Decoration; import net.minecraft.world.level.levelgen.GenerationStep.Decoration;
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
import net.minecraft.world.level.levelgen.feature.Feature; import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import net.minecraft.world.level.levelgen.placement.*; import net.minecraft.world.level.levelgen.placement.*;
import net.minecraft.world.level.material.Material; import net.minecraft.world.level.material.Material;
import org.betterx.bclib.world.features.BCLFeature; import org.betterx.bclib.world.features.BCLFeature;
import org.betterx.bclib.world.features.placement.FindSolidInDirection; import org.betterx.bclib.world.features.placement.*;
import org.betterx.bclib.world.features.placement.IsEmptyAboveSampledFilter;
import org.betterx.bclib.world.features.placement.MinEmptyFilter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -137,6 +138,14 @@ public class BCLFeatureBuilder<FC extends FeatureConfiguration, F extends Featur
return modifier(InSquarePlacement.spread()); return modifier(InSquarePlacement.spread());
} }
public BCLFeatureBuilder stencil() {
return modifier(Stencil.all());
}
public BCLFeatureBuilder stencilOneIn4() {
return modifier(Stencil.oneIn4());
}
/** /**
* Select random height that is 10 above min Build height and 10 below max generation height * Select random height that is 10 above min Build height and 10 below max generation height
* *
@ -177,10 +186,38 @@ public class BCLFeatureBuilder<FC extends FeatureConfiguration, F extends Featur
return modifier(IsEmptyAboveSampledFilter.emptyAbove4()); return modifier(IsEmptyAboveSampledFilter.emptyAbove4());
} }
public BCLFeatureBuilder isEmptyAbove() {
return modifier(IsEmptyAboveSampledFilter.emptyAbove());
}
public BCLFeatureBuilder isEmptyAbove(int d1, int d2) { public BCLFeatureBuilder isEmptyAbove(int d1, int d2) {
return modifier(new IsEmptyAboveSampledFilter(d1, d2)); return modifier(new IsEmptyAboveSampledFilter(d1, d2));
} }
public BCLFeatureBuilder onEveryLayer() {
return modifier(OnEveryLayer.simple());
}
public BCLFeatureBuilder spreadHorizontal(IntProvider p) {
return modifier(RandomOffsetPlacement.horizontal(p));
}
public BCLFeatureBuilder spreadVertical(IntProvider p) {
return modifier(RandomOffsetPlacement.horizontal(p));
}
public BCLFeatureBuilder spread(IntProvider horizontal, IntProvider vertical) {
return modifier(RandomOffsetPlacement.of(horizontal, vertical));
}
public BCLFeatureBuilder offset(Direction dir) {
return modifier(Offset.inDirection(dir));
}
public BCLFeatureBuilder offset(Vec3i dir) {
return modifier(new Offset(dir));
}
/** /**
* Cast a downward ray with max {@code distance} length to find the next solid Block. * Cast a downward ray with max {@code distance} length to find the next solid Block.
* *
@ -192,6 +229,22 @@ public class BCLFeatureBuilder<FC extends FeatureConfiguration, F extends Featur
return modifier(FindSolidInDirection.down(distance)); return modifier(FindSolidInDirection.down(distance));
} }
public BCLFeatureBuilder noiseBasedCount(float noiseLevel, int belowNoiseCount, int aboveNoiseCount) {
return modifier(NoiseThresholdCountPlacement.of(noiseLevel, belowNoiseCount, aboveNoiseCount));
}
public BCLFeatureBuilder extendDown(int min, int max) {
return modifier(new Extend(Direction.DOWN, UniformInt.of(min, max)));
}
public BCLFeatureBuilder inBasinOf(BlockPredicate... predicates) {
return modifier(new IsBasin(BlockPredicate.anyOf(predicates)));
}
public BCLFeatureBuilder is(BlockPredicate... predicates) {
return modifier(new Is(BlockPredicate.anyOf(predicates)));
}
public BCLFeatureBuilder findSolidCeil(int distance) { public BCLFeatureBuilder findSolidCeil(int distance) {
return modifier(FindSolidInDirection.up(distance)); return modifier(FindSolidInDirection.up(distance));
} }

View file

@ -46,7 +46,8 @@ public class CommonBlockTags {
TagAPI.BLOCKS.add(SOUL_GROUND, Blocks.SOUL_SAND, Blocks.SOUL_SOIL); TagAPI.BLOCKS.add(SOUL_GROUND, Blocks.SOUL_SAND, Blocks.SOUL_SOIL);
TagAPI.BLOCKS.add(IS_OBSIDIAN, Blocks.OBSIDIAN, Blocks.CRYING_OBSIDIAN); TagAPI.BLOCKS.add(IS_OBSIDIAN, Blocks.OBSIDIAN, Blocks.CRYING_OBSIDIAN);
TagAPI.BLOCKS.add(TERRAIN, Blocks.MAGMA_BLOCK);
TagAPI.BLOCKS.addOtherTags(TERRAIN, TagAPI.BLOCKS.addOtherTags(TERRAIN,
BlockTags.DRIPSTONE_REPLACEABLE, BlockTags.DRIPSTONE_REPLACEABLE,
BlockTags.BASE_STONE_OVERWORLD, BlockTags.BASE_STONE_OVERWORLD,

View file

@ -28,11 +28,11 @@ public abstract class BiomeSourceMixin implements BiomeSourceAccessor {
@Inject(method = "<init>(Ljava/util/List;)V", at = @At("TAIL")) @Inject(method = "<init>(Ljava/util/List;)V", at = @At("TAIL"))
public void bcl_init(List list, CallbackInfo ci) { public void bcl_init(List list, CallbackInfo ci) {
System.out.println("new BiomeSource (" + Integer.toHexString(hashCode()) + ", biomes=" + possibleBiomes().size() + ")"); // System.out.println("new BiomeSource (" + Integer.toHexString(hashCode()) + ", biomes=" + possibleBiomes().size() + ")");
if (possibleBiomes().size() == 27) { // if (possibleBiomes().size() == 27) {
System.out.println("Nether????"); // System.out.println("Nether????");
} else if (possibleBiomes().size() == 2) { // } else if (possibleBiomes().size() == 2) {
System.out.println("Datapack Nether???"); // System.out.println("Datapack Nether???");
} // }
} }
} }

View file

@ -229,13 +229,39 @@ public class BlocksHelper {
return false; return false;
} }
public static boolean findSurroundingSurface(LevelAccessor level,
MutableBlockPos startPos,
Direction dir,
int length,
Predicate<BlockState> surface) {
for (int len = 0; len < length; len++) {
if (surface.test(level.getBlockState(startPos))) {
if (len == 0) { //we started inside of the surface
for (int lenUp = 0; lenUp < length; lenUp++) {
startPos.move(dir, -1);
if (!surface.test(level.getBlockState(startPos))) {
startPos.move(dir, 1);
return true;
}
}
return false;
}
return true;
}
startPos.move(dir, 1);
}
return false;
}
public static boolean isFreeSpace(LevelAccessor level, public static boolean isFreeSpace(LevelAccessor level,
BlockPos startPos, BlockPos startPos,
Direction dir, Direction dir,
int length, int length,
Predicate<BlockState> freeSurface) { Predicate<BlockState> freeSurface) {
MutableBlockPos POS = startPos.mutable(); MutableBlockPos POS = startPos.mutable();
for (int len = 1; len < length; len++) { for (int len = 0; len < length; len++) {
POS.move(dir, 1); POS.move(dir, 1);
if (!freeSurface.test(level.getBlockState(POS))) { if (!freeSurface.test(level.getBlockState(POS))) {
return false; return false;

View file

@ -110,13 +110,13 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
final double distNormalizer = (config.maxSpread * Math.sqrt(2)); final double distNormalizer = (config.maxSpread * Math.sqrt(2));
final int tryCount = (int) Math.min(16, 4 * config.maxSpread * config.maxSpread); final int tryCount = config.spreadCount.sample(random);
for (int i = 0; i < tryCount; i++) { for (int i = 0; i < tryCount; i++) {
int x = origin.getX() + (int) (random.nextGaussian() * config.maxSpread); int x = origin.getX() + (int) (random.nextGaussian() * config.maxSpread);
int z = origin.getZ() + (int) (random.nextGaussian() * config.maxSpread); int z = origin.getZ() + (int) (random.nextGaussian() * config.maxSpread);
POS.set(x, origin.getY(), z); POS.set(x, origin.getY(), z);
if (BlocksHelper.findSurface(level, POS, surfaceDirection, 4, config::isValidBase)) { if (BlocksHelper.findSurroundingSurface(level, POS, surfaceDirection, 4, config::isValidBase)) {
int myHeight; int myHeight;
if (config.growWhileFree) { if (config.growWhileFree) {
myHeight = BlocksHelper.blockCount(level, myHeight = BlocksHelper.blockCount(level,

View file

@ -1,11 +1,14 @@
package org.betterx.bclib.world.features; package org.betterx.bclib.world.features;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
import net.minecraft.util.valueproviders.ConstantInt;
import net.minecraft.util.valueproviders.IntProvider;
import net.minecraft.util.valueproviders.UniformInt;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import com.mojang.datafixers.util.Function14; import com.mojang.datafixers.util.Function15;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder;
import org.betterx.bclib.BCLib; import org.betterx.bclib.BCLib;
@ -14,7 +17,7 @@ import org.betterx.bclib.api.tag.CommonBlockTags;
import java.util.Optional; import java.util.Optional;
public abstract class ScatterFeatureConfig implements FeatureConfiguration { public abstract class ScatterFeatureConfig implements FeatureConfiguration {
public interface Instancer<T extends ScatterFeatureConfig> extends Function14<BlockState, BlockState, BlockState, Optional<BlockState>, Float, Float, Float, Float, Integer, Integer, Float, Float, Float, Boolean, T> { public interface Instancer<T extends ScatterFeatureConfig> extends Function15<BlockState, BlockState, BlockState, Optional<BlockState>, Float, Float, Float, Float, Integer, Integer, Float, Float, Float, Boolean, IntProvider, T> {
} }
public final BlockState clusterBlock; public final BlockState clusterBlock;
@ -31,6 +34,8 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
public final float sizeVariation; public final float sizeVariation;
public final float floorChance; public final float floorChance;
public final IntProvider spreadCount;
public final boolean growWhileFree; public final boolean growWhileFree;
public ScatterFeatureConfig(BlockState clusterBlock, public ScatterFeatureConfig(BlockState clusterBlock,
@ -46,7 +51,8 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
float maxSpread, float maxSpread,
float sizeVariation, float sizeVariation,
float floorChance, float floorChance,
boolean growWhileFree) { boolean growWhileFree,
IntProvider spreadCount) {
this.clusterBlock = clusterBlock; this.clusterBlock = clusterBlock;
this.tipBlock = tipBlock == null ? clusterBlock : tipBlock; this.tipBlock = tipBlock == null ? clusterBlock : tipBlock;
this.bottomBlock = bottomBlock == null ? clusterBlock : bottomBlock; this.bottomBlock = bottomBlock == null ? clusterBlock : bottomBlock;
@ -61,6 +67,7 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
this.sizeVariation = sizeVariation; this.sizeVariation = sizeVariation;
this.floorChance = floorChance; this.floorChance = floorChance;
this.growWhileFree = growWhileFree; this.growWhileFree = growWhileFree;
this.spreadCount = spreadCount;
} }
@ -137,7 +144,11 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
.BOOL .BOOL
.fieldOf("grow_while_empty") .fieldOf("grow_while_empty")
.orElse(false) .orElse(false)
.forGetter((T cfg) -> cfg.growWhileFree) .forGetter((T cfg) -> cfg.growWhileFree),
IntProvider.codec(0, 64)
.fieldOf("length")
.orElse(UniformInt.of(0, 3))
.forGetter(cfg -> cfg.spreadCount)
) )
.apply(instance, instancer) .apply(instance, instancer)
); );
@ -158,6 +169,7 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
private float sizeVariation = 0; private float sizeVariation = 0;
private float floorChance = 0.5f; private float floorChance = 0.5f;
private boolean growWhileFree = false; private boolean growWhileFree = false;
public IntProvider spreadCount = ConstantInt.of(0);
private final Instancer<T> instancer; private final Instancer<T> instancer;
public Builder(Instancer<T> instancer) { public Builder(Instancer<T> instancer) {
@ -251,6 +263,11 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
} }
public Builder<T> spread(float maxSpread, float sizeVariation) { public Builder<T> spread(float maxSpread, float sizeVariation) {
return spread(maxSpread, sizeVariation, ConstantInt.of((int) Math.min(16, 4 * maxSpread * maxSpread)));
}
public Builder<T> spread(float maxSpread, float sizeVariation, IntProvider spreadCount) {
this.spreadCount = spreadCount; //
this.maxSpread = maxSpread; this.maxSpread = maxSpread;
this.sizeVariation = sizeVariation; this.sizeVariation = sizeVariation;
return this; return this;
@ -286,7 +303,8 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
this.maxSpread, this.maxSpread,
this.sizeVariation, this.sizeVariation,
this.floorChance, this.floorChance,
this.growWhileFree this.growWhileFree,
this.spreadCount
); );
} }
} }
@ -307,7 +325,8 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
float maxSpread, float maxSpread,
float sizeVariation, float sizeVariation,
float floorChance, float floorChance,
boolean growWhileFree) { boolean growWhileFree,
IntProvider spreadCount) {
super(clusterBlock, super(clusterBlock,
tipBlock, tipBlock,
bottomBlock, bottomBlock,
@ -321,9 +340,11 @@ public abstract class ScatterFeatureConfig implements FeatureConfiguration {
maxSpread, maxSpread,
sizeVariation, sizeVariation,
floorChance, floorChance,
growWhileFree); growWhileFree,
spreadCount);
} }
public static Builder<OnSolid> startOnSolid() { public static Builder<OnSolid> startOnSolid() {
return Builder.start(OnSolid::new); return Builder.start(OnSolid::new);
} }

View file

@ -2,7 +2,6 @@ package org.betterx.bclib.world.features;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.feature.Feature; import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext; import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
@ -37,14 +36,6 @@ public abstract class SurfaceFeature<T extends FeatureConfiguration> extends Fea
minHeight(ctx), minHeight(ctx),
this::isValidSurface); this::isValidSurface);
if (pos.isPresent()) { if (pos.isPresent()) {
int y2 = ctx.level().getHeight(Heightmap.Types.WORLD_SURFACE_WG, ctx.origin().getX(), ctx.origin().getZ());
int y3 = ctx.level().getHeight(Heightmap.Types.WORLD_SURFACE, ctx.origin().getX(), ctx.origin().getZ());
int y4 = ctx.level().getHeight(Heightmap.Types.OCEAN_FLOOR, ctx.origin().getX(), ctx.origin().getZ());
int y5 = ctx.level().getHeight(Heightmap.Types.OCEAN_FLOOR_WG, ctx.origin().getX(), ctx.origin().getZ());
System.out.println("Surfaces:" + pos
.get()
.getY() + ", " + y2 + ", " + y3 + ", " + y4 + ", " + y5 + ", " + ctx.origin().getY());
generate(pos.get(), ctx); generate(pos.get(), ctx);
return true; return true;
} }

View file

@ -12,8 +12,6 @@ import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
public class Extend extends PlacementModifier { public class Extend extends PlacementModifier {
@ -42,12 +40,13 @@ public class Extend extends PlacementModifier {
public Stream<BlockPos> getPositions(PlacementContext placementContext, public Stream<BlockPos> getPositions(PlacementContext placementContext,
RandomSource random, RandomSource random,
BlockPos blockPos) { BlockPos blockPos) {
var builder = Stream.<BlockPos>builder();
final int count = length.sample(random); final int count = length.sample(random);
List<BlockPos> pos = new ArrayList<>(count); builder.add(blockPos);
for (int y = 0; y < count; y++) { for (int y = 1; y < count + 1; y++) {
pos.add(blockPos.relative(direction, y + 1)); builder.add(blockPos.relative(direction, y));
} }
return pos.stream(); return builder.build();
} }
@Override @Override

View file

@ -16,8 +16,8 @@ import java.util.stream.Stream;
public class FindSolidInDirection extends PlacementModifier { public class FindSolidInDirection extends PlacementModifier {
protected static final FindSolidInDirection DOWN = new FindSolidInDirection(Direction.DOWN, 12); protected static final FindSolidInDirection DOWN = new FindSolidInDirection(Direction.DOWN, 6);
protected static final FindSolidInDirection UP = new FindSolidInDirection(Direction.UP, 12); protected static final FindSolidInDirection UP = new FindSolidInDirection(Direction.UP, 6);
public static final Codec<FindSolidInDirection> CODEC = RecordCodecBuilder.create((instance) -> instance public static final Codec<FindSolidInDirection> CODEC = RecordCodecBuilder.create((instance) -> instance
.group( .group(
Direction.CODEC.fieldOf("dir").orElse(Direction.DOWN).forGetter((p) -> p.direction), Direction.CODEC.fieldOf("dir").orElse(Direction.DOWN).forGetter((p) -> p.direction),
@ -54,7 +54,7 @@ public class FindSolidInDirection extends PlacementModifier {
RandomSource randomSource, RandomSource randomSource,
BlockPos blockPos) { BlockPos blockPos) {
BlockPos.MutableBlockPos POS = blockPos.mutable(); BlockPos.MutableBlockPos POS = blockPos.mutable();
if (BlocksHelper.findSurface(placementContext.getLevel(), if (BlocksHelper.findSurroundingSurface(placementContext.getLevel(),
POS, POS,
direction, direction,
maxSearchDistance, maxSearchDistance,

View file

@ -0,0 +1,43 @@
package org.betterx.bclib.world.features.placement;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
import net.minecraft.world.level.levelgen.placement.PlacementContext;
import net.minecraft.world.level.levelgen.placement.PlacementFilter;
import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
public class Is extends PlacementFilter {
public static final Codec<Is> CODEC = RecordCodecBuilder.create((instance) -> instance
.group(
BlockPredicate.CODEC
.fieldOf("predicate")
.forGetter(cfg -> cfg.predicate)
)
.apply(instance, Is::new));
private final BlockPredicate predicate;
public Is(BlockPredicate predicate) {
this.predicate = predicate;
}
public static Is simple(BlockPredicate predicate) {
return new Is(predicate);
}
@Override
protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) {
WorldGenLevel level = ctx.getLevel();
return predicate.test(level, pos);
}
@Override
public PlacementModifierType<Is> type() {
return PlacementModifiers.IS;
}
}

View file

@ -1,9 +1,9 @@
package org.betterx.bclib.world.features.placement; package org.betterx.bclib.world.features.placement;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
import net.minecraft.world.level.levelgen.placement.PlacementContext; import net.minecraft.world.level.levelgen.placement.PlacementContext;
import net.minecraft.world.level.levelgen.placement.PlacementFilter; import net.minecraft.world.level.levelgen.placement.PlacementFilter;
import net.minecraft.world.level.levelgen.placement.PlacementModifierType; import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
@ -11,27 +11,34 @@ import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.List;
public class IsBasin extends PlacementFilter { public class IsBasin extends PlacementFilter {
public static final Codec<IsBasin> CODEC = RecordCodecBuilder.create((instance) -> instance public static final Codec<IsBasin> CODEC = RecordCodecBuilder.create((instance) -> instance
.group( .group(
ExtraCodecs.nonEmptyList(BlockState.CODEC.listOf()) BlockPredicate.CODEC
.fieldOf("blocks") .fieldOf("predicate")
.forGetter(cfg -> cfg.blocks) .forGetter(cfg -> cfg.predicate)
) )
.apply(instance, IsBasin::new)); .apply(instance, IsBasin::new));
private final List<BlockState> blocks; private final BlockPredicate predicate;
public IsBasin(List<BlockState> blocks) { public IsBasin(BlockPredicate predicate) {
this.blocks = blocks; this.predicate = predicate;
}
public static IsBasin simple(BlockPredicate predicate) {
return new IsBasin(predicate);
} }
@Override @Override
protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) { protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) {
BlockState bs = ctx.getLevel().getBlockState(pos); WorldGenLevel level = ctx.getLevel();
return blocks.stream().map(b -> b.getBlock()).anyMatch(b -> bs.is(b)); return predicate.test(level, pos.below())
&& predicate.test(level, pos.west())
&& predicate.test(level, pos.east())
&& predicate.test(level, pos.north())
&& predicate.test(level, pos.south());
} }
@Override @Override

View file

@ -15,6 +15,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
*/ */
public class IsEmptyAboveSampledFilter extends PlacementFilter { public class IsEmptyAboveSampledFilter extends PlacementFilter {
private static final IsEmptyAboveSampledFilter DEFAULT = new IsEmptyAboveSampledFilter(4, 2); private static final IsEmptyAboveSampledFilter DEFAULT = new IsEmptyAboveSampledFilter(4, 2);
private static final IsEmptyAboveSampledFilter DEFAULT1 = new IsEmptyAboveSampledFilter(1, 1);
public static final Codec<IsEmptyAboveSampledFilter> CODEC = RecordCodecBuilder.create((instance) -> instance public static final Codec<IsEmptyAboveSampledFilter> CODEC = RecordCodecBuilder.create((instance) -> instance
.group( .group(
Codec.intRange(1, 32).fieldOf("d1").orElse(2).forGetter((p) -> p.distance1), Codec.intRange(1, 32).fieldOf("d1").orElse(2).forGetter((p) -> p.distance1),
@ -26,6 +27,10 @@ public class IsEmptyAboveSampledFilter extends PlacementFilter {
return DEFAULT; return DEFAULT;
} }
public static PlacementFilter emptyAbove() {
return DEFAULT1;
}
public IsEmptyAboveSampledFilter(int d1, int d2) { public IsEmptyAboveSampledFilter(int d1, int d2) {
this.distance1 = d1; this.distance1 = d1;
this.distance2 = d2; this.distance2 = d2;
@ -38,7 +43,8 @@ public class IsEmptyAboveSampledFilter extends PlacementFilter {
@Override @Override
protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) { protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) {
WorldGenLevel level = ctx.getLevel(); WorldGenLevel level = ctx.getLevel();
if (level.isEmptyBlock(pos.above(distance1)) && level.isEmptyBlock(pos.above(distance2))) { if (level.isEmptyBlock(pos.above(distance1))
&& (distance1 == distance2 || level.isEmptyBlock(pos.above(distance2)))) {
return true; return true;
} }
return false; return false;

View file

@ -1,18 +1,22 @@
package org.betterx.bclib.world.features.placement; package org.betterx.bclib.world.features.placement;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
import net.minecraft.util.RandomSource; import net.minecraft.util.RandomSource;
import net.minecraft.world.level.levelgen.placement.PlacementContext; import net.minecraft.world.level.levelgen.placement.PlacementContext;
import net.minecraft.world.level.levelgen.placement.PlacementModifier; import net.minecraft.world.level.levelgen.placement.PlacementModifier;
import net.minecraft.world.level.levelgen.placement.PlacementModifierType; import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
import com.google.common.collect.Maps;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.Map;
import java.util.stream.Stream; import java.util.stream.Stream;
public class Offset extends PlacementModifier { public class Offset extends PlacementModifier {
private static Map<Direction, Offset> DIRECTIONS = Maps.newHashMap();
public static final Codec<Offset> CODEC = RecordCodecBuilder.create((instance) -> instance public static final Codec<Offset> CODEC = RecordCodecBuilder.create((instance) -> instance
.group( .group(
Vec3i.CODEC Vec3i.CODEC
@ -27,6 +31,10 @@ public class Offset extends PlacementModifier {
this.offset = offset; this.offset = offset;
} }
public static Offset inDirection(Direction dir) {
return DIRECTIONS.get(dir);
}
@Override @Override
public Stream<BlockPos> getPositions(PlacementContext placementContext, public Stream<BlockPos> getPositions(PlacementContext placementContext,
RandomSource randomSource, RandomSource randomSource,
@ -38,4 +46,9 @@ public class Offset extends PlacementModifier {
public PlacementModifierType<?> type() { public PlacementModifierType<?> type() {
return PlacementModifiers.OFFSET; return PlacementModifiers.OFFSET;
} }
static {
for (Direction d : Direction.values())
DIRECTIONS.put(d, new Offset(d.getNormal()));
}
} }

View file

@ -0,0 +1,74 @@
package org.betterx.bclib.world.features.placement;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.placement.PlacementContext;
import net.minecraft.world.level.levelgen.placement.PlacementModifier;
import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
import com.mojang.serialization.Codec;
import java.util.stream.Stream;
public class OnEveryLayer
extends PlacementModifier {
private static OnEveryLayer INSTANCE = new OnEveryLayer();
public static final Codec<OnEveryLayer> CODEC = Codec.unit(() -> INSTANCE);
private OnEveryLayer() {
}
public static OnEveryLayer simple() {
return INSTANCE;
}
@Override
public Stream<BlockPos> getPositions(PlacementContext ctx,
RandomSource random,
BlockPos pos) {
Stream.Builder<BlockPos> builder = Stream.builder();
final int z = pos.getZ();
final int x = pos.getX();
int y = ctx.getHeight(Heightmap.Types.MOTION_BLOCKING, x, z);
int layerY;
do {
layerY = OnEveryLayer.findOnGroundYPosition(ctx, x, y, z);
if (layerY != Integer.MAX_VALUE) {
builder.add(new BlockPos(x, layerY, z));
y = layerY - 1;
}
} while (layerY != Integer.MAX_VALUE);
return builder.build();
}
@Override
public PlacementModifierType<OnEveryLayer> type() {
return PlacementModifiers.ON_EVERY_LAYER;
}
private static int findOnGroundYPosition(PlacementContext ctx, int x, int startY, int z) {
BlockPos.MutableBlockPos mPos = new BlockPos.MutableBlockPos(x, startY, z);
BlockState nowState = ctx.getBlockState(mPos);
for (int y = startY; y >= ctx.getMinBuildHeight() + 1; --y) {
mPos.setY(y - 1);
BlockState belowState = ctx.getBlockState(mPos);
if (!OnEveryLayer.isEmpty(belowState) && OnEveryLayer.isEmpty(nowState) && !belowState.is(Blocks.BEDROCK)) {
return mPos.getY() + 1;
}
nowState = belowState;
}
return Integer.MAX_VALUE;
}
private static boolean isEmpty(BlockState blockState) {
return blockState.isAir() || blockState.is(Blocks.WATER) || blockState.is(Blocks.LAVA);
}
}

View file

@ -29,6 +29,10 @@ public class PlacementModifiers {
"is_basin", "is_basin",
IsBasin.CODEC); IsBasin.CODEC);
public static final PlacementModifierType<Is> IS = register(
"is",
Is.CODEC);
public static final PlacementModifierType<Offset> OFFSET = register( public static final PlacementModifierType<Offset> OFFSET = register(
"offset", "offset",
Offset.CODEC); Offset.CODEC);
@ -37,6 +41,10 @@ public class PlacementModifiers {
"extend", "extend",
Extend.CODEC); Extend.CODEC);
public static final PlacementModifierType<OnEveryLayer> ON_EVERY_LAYER = register(
"on_every_layer",
OnEveryLayer.CODEC);
private static <P extends PlacementModifier> PlacementModifierType<P> register(String path, Codec<P> codec) { private static <P extends PlacementModifier> PlacementModifierType<P> register(String path, Codec<P> codec) {
return register(BCLib.makeID(path), codec); return register(BCLib.makeID(path), codec);

View file

@ -20,23 +20,29 @@ public class Stencil extends PlacementModifier {
private static final Boolean[] BN_STENCIL; private static final Boolean[] BN_STENCIL;
private final List<Boolean> stencil; private final List<Boolean> stencil;
private static final Stencil DEFAULT; private static final Stencil DEFAULT;
private static final Stencil DEFAULT4;
private final int selectOneIn;
private static List<Boolean> convert(Boolean[] s) { private static List<Boolean> convert(Boolean[] s) {
return Arrays.stream(s).toList(); return Arrays.stream(s).toList();
} }
public Stencil(Boolean[] stencil) { public Stencil(Boolean[] stencil, int selectOneIn) {
this(convert(stencil)); this(convert(stencil), selectOneIn);
} }
public Stencil(List<Boolean> stencil) { public Stencil(List<Boolean> stencil, int selectOneIn) {
this.stencil = stencil; this.stencil = stencil;
this.selectOneIn = selectOneIn;
} }
public static Stencil basic() { public static Stencil all() {
return DEFAULT; return DEFAULT;
} }
public static Stencil oneIn4() {
return DEFAULT4;
}
@Override @Override
public Stream<BlockPos> getPositions(PlacementContext placementContext, public Stream<BlockPos> getPositions(PlacementContext placementContext,
@ -319,13 +325,18 @@ public class Stencil extends PlacementModifier {
false false
}; };
DEFAULT = new Stencil(BN_STENCIL); DEFAULT = new Stencil(BN_STENCIL, 1);
DEFAULT4 = new Stencil(BN_STENCIL, 4);
CODEC = RecordCodecBuilder.create((instance) -> instance CODEC = RecordCodecBuilder.create((instance) -> instance
.group( .group(
ExtraCodecs.nonEmptyList(Codec.BOOL.listOf()) ExtraCodecs.nonEmptyList(Codec.BOOL.listOf())
.fieldOf("structures") .fieldOf("structures")
.orElse(convert(BN_STENCIL)) .orElse(convert(BN_STENCIL))
.forGetter((Stencil a) -> a.stencil) .forGetter((Stencil a) -> a.stencil),
Codec.INT
.fieldOf("one_in")
.orElse(1)
.forGetter((Stencil a) -> a.selectOneIn)
) )
.apply(instance, Stencil::new) .apply(instance, Stencil::new)
); );