Some placement filters
This commit is contained in:
parent
4321017c25
commit
aa4133fad2
12 changed files with 318 additions and 47 deletions
|
@ -22,6 +22,7 @@ import org.betterx.bclib.recipes.CraftingRecipes;
|
|||
import org.betterx.bclib.registry.BaseBlockEntities;
|
||||
import org.betterx.bclib.registry.BaseRegistry;
|
||||
import org.betterx.bclib.util.Logger;
|
||||
import org.betterx.bclib.world.features.placement.PlacementModifiers;
|
||||
import org.betterx.bclib.world.generator.BCLibEndBiomeSource;
|
||||
import org.betterx.bclib.world.generator.BCLibNetherBiomeSource;
|
||||
import org.betterx.bclib.world.generator.GeneratorOptions;
|
||||
|
@ -50,16 +51,17 @@ public class BCLib implements ModInitializer {
|
|||
AnvilRecipe.register();
|
||||
|
||||
DataExchangeAPI.registerDescriptors(List.of(
|
||||
HelloClient.DESCRIPTOR,
|
||||
HelloServer.DESCRIPTOR,
|
||||
RequestFiles.DESCRIPTOR,
|
||||
SendFiles.DESCRIPTOR,
|
||||
Chunker.DESCRIPTOR
|
||||
)
|
||||
);
|
||||
HelloClient.DESCRIPTOR,
|
||||
HelloServer.DESCRIPTOR,
|
||||
RequestFiles.DESCRIPTOR,
|
||||
SendFiles.DESCRIPTOR,
|
||||
Chunker.DESCRIPTOR
|
||||
)
|
||||
);
|
||||
|
||||
BCLibPatch.register();
|
||||
TemplatePiece.ensureStaticInitialization();
|
||||
PlacementModifiers.ensureStaticInitialization();
|
||||
Configs.save();
|
||||
if (isDevEnvironment()) {
|
||||
Biome.BiomeBuilder builder = new Biome.BiomeBuilder()
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
package org.betterx.bclib.api.features;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.data.worldgen.placement.PlacementUtils;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.valueproviders.UniformInt;
|
||||
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.configurations.FeatureConfiguration;
|
||||
import net.minecraft.world.level.levelgen.placement.*;
|
||||
import net.minecraft.world.level.material.Material;
|
||||
|
||||
import org.betterx.bclib.world.features.BCLFeature;
|
||||
import org.betterx.bclib.world.features.placement.IsEmptyAboveSampledFilter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -132,14 +136,79 @@ public class BCLFeatureBuilder<FC extends FeatureConfiguration, F extends Featur
|
|||
return modifier(InSquarePlacement.spread());
|
||||
}
|
||||
|
||||
public BCLFeatureBuilder distanceToTopAndBottom10() {
|
||||
/**
|
||||
* Select random height that is 10 above min Build height and 10 below max generation height
|
||||
*
|
||||
* @return The instance it was called on
|
||||
*/
|
||||
public BCLFeatureBuilder randomHeight10FromFloorCeil() {
|
||||
return modifier(PlacementUtils.RANGE_10_10);
|
||||
}
|
||||
|
||||
public BCLFeatureBuilder distanceToTopAndBottom4() {
|
||||
/**
|
||||
* Select random height that is 4 above min Build height and 10 below max generation height
|
||||
*
|
||||
* @return The instance it was called on
|
||||
*/
|
||||
public BCLFeatureBuilder randomHeight4FromFloorCeil() {
|
||||
return modifier(PlacementUtils.RANGE_4_4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select random height that is 8 above min Build height and 10 below max generation height
|
||||
*
|
||||
* @return The instance it was called on
|
||||
*/
|
||||
public BCLFeatureBuilder randomHeight8FromFloorCeil() {
|
||||
return modifier(PlacementUtils.RANGE_8_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select random height that is above min Build height and 10 below max generation height
|
||||
*
|
||||
* @return The instance it was called on
|
||||
*/
|
||||
public BCLFeatureBuilder randomHeight() {
|
||||
return modifier(PlacementUtils.FULL_RANGE);
|
||||
}
|
||||
|
||||
public BCLFeatureBuilder isEmptyAbove4() {
|
||||
return modifier(IsEmptyAboveSampledFilter.emptyAbove4());
|
||||
}
|
||||
|
||||
public BCLFeatureBuilder isEmptyAbove(int d1, int d2) {
|
||||
return modifier(new IsEmptyAboveSampledFilter(d1, d2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cast a downward ray with max {@code distance} length to find the next solid Block.
|
||||
*
|
||||
* @param distance The maximum search Distance
|
||||
* @return The instance it was called on
|
||||
* @see #findSolidSurface(Direction, int) for Details
|
||||
*/
|
||||
public BCLFeatureBuilder findSolidFloor(int distance) {
|
||||
return findSolidSurface(Direction.DOWN, distance);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cast a ray with max {@code distance} length to find the next solid Block. The ray will travel through replaceable
|
||||
* Blocks (see {@link Material#isReplaceable()}) and will be accepted if it hits a solid one
|
||||
* (see {@link Material#isSolid()} ()})
|
||||
*
|
||||
* @param dir The direction the ray is cast
|
||||
* @param distance The maximum search Distance
|
||||
* @return The instance it was called on
|
||||
* @see #findSolidSurface(Direction, int) for Details
|
||||
*/
|
||||
public BCLFeatureBuilder findSolidSurface(Direction dir, int distance) {
|
||||
return modifier(EnvironmentScanPlacement.scanningFor(dir,
|
||||
BlockPredicate.solid(),
|
||||
BlockPredicate.replaceable(),
|
||||
distance));
|
||||
}
|
||||
|
||||
public BCLFeatureBuilder heightmap() {
|
||||
return modifier(PlacementUtils.HEIGHTMAP);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package org.betterx.bclib.interfaces;
|
||||
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
|
||||
public interface BCLPlacementContext {
|
||||
Rotation bcl_getRotation();
|
||||
void bcl_setRotation(Rotation bcl_rotation);
|
||||
Mirror bcl_getMirror();
|
||||
void bcl_setMirror(Mirror bcl_mirror);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package org.betterx.bclib.mixin.common;
|
||||
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementContext;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
@Mixin(PlacementContext.class)
|
||||
public class PlacementContextMixin implements org.betterx.bclib.interfaces.BCLPlacementContext {
|
||||
private Rotation bcl_rotation = Rotation.NONE;
|
||||
private Mirror bcl_mirror = Mirror.NONE;
|
||||
|
||||
|
||||
@Override
|
||||
public Rotation bcl_getRotation() {
|
||||
return bcl_rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bcl_setRotation(Rotation bcl_rotation) {
|
||||
this.bcl_rotation = bcl_rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mirror bcl_getMirror() {
|
||||
return bcl_mirror;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bcl_setMirror(Mirror bcl_mirror) {
|
||||
this.bcl_mirror = bcl_mirror;
|
||||
}
|
||||
}
|
|
@ -245,6 +245,22 @@ public class BlocksHelper {
|
|||
return true;
|
||||
}
|
||||
|
||||
public static int blockCount(LevelAccessor level,
|
||||
BlockPos startPos,
|
||||
Direction dir,
|
||||
int length,
|
||||
Predicate<BlockState> freeSurface) {
|
||||
MutableBlockPos POS = startPos.mutable();
|
||||
for (int len = 1; len < length; len++) {
|
||||
POS.move(dir, 1);
|
||||
if (!freeSurface.test(level.getBlockState(POS))) {
|
||||
return len - 1;
|
||||
}
|
||||
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
public static boolean isLava(BlockState state) {
|
||||
return state.getFluidState().getType() instanceof LavaFluid;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.betterx.bclib.world.features;
|
|||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.data.worldgen.placement.PlacementUtils;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
@ -25,6 +26,8 @@ import org.betterx.bclib.api.features.BCLFeatureBuilder;
|
|||
import org.betterx.bclib.api.tag.CommonBlockTags;
|
||||
import org.betterx.bclib.util.BlocksHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
@ -35,28 +38,32 @@ public class ScatterFeature<FC extends ScatterFeatureConfig>
|
|||
int minPerChunk,
|
||||
int maxPerChunk,
|
||||
T cfg,
|
||||
Feature<T> inlineFeatures) {
|
||||
SimpleRandomFeatureConfiguration configuration = new SimpleRandomFeatureConfiguration(HolderSet.direct(
|
||||
PlacementUtils.inlinePlaced(inlineFeatures,
|
||||
cfg,
|
||||
EnvironmentScanPlacement.scanningFor(Direction.DOWN,
|
||||
BlockPredicate.solid(),
|
||||
BlockPredicate.ONLY_IN_AIR_PREDICATE,
|
||||
12),
|
||||
RandomOffsetPlacement.vertical(ConstantInt.of(1))),
|
||||
PlacementUtils.inlinePlaced(inlineFeatures,
|
||||
cfg,
|
||||
EnvironmentScanPlacement.scanningFor(Direction.UP,
|
||||
BlockPredicate.solid(),
|
||||
BlockPredicate.ONLY_IN_AIR_PREDICATE,
|
||||
12),
|
||||
RandomOffsetPlacement.vertical(ConstantInt.of(-1)))));
|
||||
Feature<T> inlineFeature) {
|
||||
List<Holder<PlacedFeature>> set = new ArrayList<>(2);
|
||||
if (cfg.floorChance > 0) set.add(PlacementUtils.inlinePlaced(inlineFeature,
|
||||
cfg,
|
||||
EnvironmentScanPlacement.scanningFor(Direction.DOWN,
|
||||
BlockPredicate.solid(),
|
||||
BlockPredicate.ONLY_IN_AIR_PREDICATE,
|
||||
12),
|
||||
RandomOffsetPlacement.vertical(ConstantInt.of(1))));
|
||||
|
||||
if (cfg.floorChance < 1) {
|
||||
set.add(PlacementUtils.inlinePlaced(inlineFeature,
|
||||
cfg,
|
||||
EnvironmentScanPlacement.scanningFor(Direction.UP,
|
||||
BlockPredicate.solid(),
|
||||
BlockPredicate.ONLY_IN_AIR_PREDICATE,
|
||||
12),
|
||||
RandomOffsetPlacement.vertical(ConstantInt.of(-1))));
|
||||
}
|
||||
SimpleRandomFeatureConfiguration configuration = new SimpleRandomFeatureConfiguration(HolderSet.direct(set));
|
||||
|
||||
return BCLFeatureBuilder.start(location, SIMPLE_RANDOM_SELECTOR)
|
||||
.decoration(GenerationStep.Decoration.VEGETAL_DECORATION)
|
||||
.modifier(CountPlacement.of(UniformInt.of(minPerChunk, maxPerChunk)))
|
||||
.modifier(InSquarePlacement.spread())
|
||||
.distanceToTopAndBottom4()
|
||||
.randomHeight4FromFloorCeil()
|
||||
.modifier(CountPlacement.of(UniformInt.of(2, 5)))
|
||||
.modifier(RandomOffsetPlacement.of(
|
||||
ClampedNormalInt.of(0.0f, 2.0f, -6, 6),
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
package org.betterx.bclib.world.features;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
||||
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
|
||||
import net.minecraft.world.level.levelgen.feature.Feature;
|
||||
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
|
||||
import net.minecraft.world.level.levelgen.placement.BiomeFilter;
|
||||
import net.minecraft.world.level.levelgen.placement.EnvironmentScanPlacement;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import org.betterx.bclib.api.features.BCLFeatureBuilder;
|
||||
|
@ -29,30 +24,28 @@ public class TemplateFeature<FC extends TemplateFeatureConfig> extends Feature<F
|
|||
return BCLFeatureBuilder
|
||||
.start(location, INSTANCE)
|
||||
.decoration(GenerationStep.Decoration.SURFACE_STRUCTURES)
|
||||
.squarePlacement()
|
||||
.distanceToTopAndBottom10()
|
||||
.modifier(EnvironmentScanPlacement.scanningFor(Direction.DOWN,
|
||||
BlockPredicate.solid(),
|
||||
BlockPredicate.matchesBlocks(Blocks.AIR,
|
||||
Blocks.WATER,
|
||||
Blocks.LAVA),
|
||||
12))
|
||||
.modifier(BiomeFilter.biome())
|
||||
.oncePerChunks(onceEveryChunk)
|
||||
.oncePerChunks(onceEveryChunk) //discard neighboring chunks
|
||||
.count(16) //try 16 placements in chunk
|
||||
.squarePlacement() //randomize x/z in chunk
|
||||
.randomHeight10FromFloorCeil() //randomize height 10 above and 10 below max vertical
|
||||
.findSolidFloor(12) //cast downward ray to find solid surface
|
||||
.isEmptyAbove4() //make sure we have 4 free blocks above
|
||||
.onlyInBiome() //ensure that we still are in the correct biome
|
||||
|
||||
.buildAndRegister(configuration);
|
||||
}
|
||||
|
||||
public static <T extends TemplateFeatureConfig> BCLFeature createAndRegister(ResourceLocation location,
|
||||
TemplateFeatureConfig configuration,
|
||||
int count) {
|
||||
|
||||
|
||||
return BCLFeatureBuilder
|
||||
.start(location, INSTANCE)
|
||||
.decoration(GenerationStep.Decoration.SURFACE_STRUCTURES)
|
||||
.count(count)
|
||||
.squarePlacement()
|
||||
.distanceToTopAndBottom10()
|
||||
.randomHeight10FromFloorCeil()
|
||||
.findSolidFloor(12) //cast downward ray to find solid surface
|
||||
.isEmptyAbove4()
|
||||
.onlyInBiome()
|
||||
.buildAndRegister(configuration);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
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.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;
|
||||
|
||||
/**
|
||||
* Tests if there is air at two locations above the tested block position
|
||||
*/
|
||||
public class IsEmptyAboveSampledFilter extends PlacementFilter {
|
||||
private static final IsEmptyAboveSampledFilter DEFAULT = new IsEmptyAboveSampledFilter(4, 2);
|
||||
public static final Codec<IsEmptyAboveSampledFilter> CODEC = RecordCodecBuilder.create((instance) -> instance
|
||||
.group(
|
||||
Codec.intRange(1, 32).fieldOf("d1").orElse(2).forGetter((p) -> p.distance1),
|
||||
Codec.intRange(1, 32).fieldOf("d2").orElse(4).forGetter((p) -> p.distance1)
|
||||
)
|
||||
.apply(instance, IsEmptyAboveSampledFilter::new));
|
||||
|
||||
public static PlacementFilter emptyAbove4() {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
public IsEmptyAboveSampledFilter(int d1, int d2) {
|
||||
this.distance1 = d1;
|
||||
this.distance2 = d2;
|
||||
}
|
||||
|
||||
private final int distance1;
|
||||
private final int distance2;
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) {
|
||||
WorldGenLevel level = ctx.getLevel();
|
||||
if (level.isEmptyBlock(pos.above(distance1)) && level.isEmptyBlock(pos.above(distance2))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlacementModifierType<?> type() {
|
||||
return PlacementModifiers.IS_EMPTY_ABOVE_SAMPLED_FILTER;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package org.betterx.bclib.world.features.placement;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementContext;
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementFilter;
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier;
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import org.betterx.bclib.util.BlocksHelper;
|
||||
|
||||
public class MinEmptyFilter extends PlacementFilter {
|
||||
private static MinEmptyFilter DOWN = new MinEmptyFilter(Direction.DOWN, 12);
|
||||
public static final Codec<MinEmptyFilter> CODEC = RecordCodecBuilder.create((instance) -> instance
|
||||
.group(
|
||||
Direction.CODEC.fieldOf("dir").orElse(Direction.DOWN).forGetter((p) -> p.direction),
|
||||
Codec.intRange(1, 32).fieldOf("dist").orElse(12).forGetter((p) -> p.maxSearchDistance)
|
||||
)
|
||||
.apply(instance, MinEmptyFilter::new));
|
||||
|
||||
private final Direction direction;
|
||||
private final int maxSearchDistance;
|
||||
|
||||
protected MinEmptyFilter(Direction direction, int maxSearchDistance) {
|
||||
this.direction = direction;
|
||||
this.maxSearchDistance = maxSearchDistance;
|
||||
}
|
||||
|
||||
public PlacementModifier down() {
|
||||
return DOWN;
|
||||
}
|
||||
|
||||
public PlacementModifier down(int dist) {
|
||||
return new MinEmptyFilter(Direction.DOWN, dist);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldPlace(PlacementContext ctx, RandomSource randomSource, BlockPos pos) {
|
||||
int h = BlocksHelper.blockCount(
|
||||
ctx.getLevel(),
|
||||
pos.relative(direction),
|
||||
direction,
|
||||
maxSearchDistance,
|
||||
state -> state.getMaterial().isReplaceable()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlacementModifierType<?> type() {
|
||||
return PlacementModifiers.MIN_EMPTY_FILTER;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package org.betterx.bclib.world.features.placement;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier;
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import org.betterx.bclib.BCLib;
|
||||
|
||||
public class PlacementModifiers {
|
||||
public static final PlacementModifierType<IsEmptyAboveSampledFilter> IS_EMPTY_ABOVE_SAMPLED_FILTER = register(
|
||||
"is_empty_above_sampled_filter",
|
||||
IsEmptyAboveSampledFilter.CODEC);
|
||||
|
||||
public static final PlacementModifierType<MinEmptyFilter> MIN_EMPTY_FILTER = register(
|
||||
"min_empty_filter",
|
||||
MinEmptyFilter.CODEC);
|
||||
|
||||
|
||||
private static <P extends PlacementModifier> PlacementModifierType<P> register(String path, Codec<P> codec) {
|
||||
return register(BCLib.makeID(path), codec);
|
||||
}
|
||||
|
||||
public static <P extends PlacementModifier> PlacementModifierType<P> register(ResourceLocation location,
|
||||
Codec<P> codec) {
|
||||
return Registry.register(Registry.PLACEMENT_MODIFIERS, location, () -> codec);
|
||||
}
|
||||
|
||||
public static void ensureStaticInitialization() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import net.minecraft.util.StringRepresentable;
|
|||
import com.mojang.serialization.Codec;
|
||||
|
||||
public enum StructurePlacementType implements StringRepresentable {
|
||||
FLOOR, WALL, CEIL, LAVA, UNDER, FLOOR_FREE_ABOVE;
|
||||
FLOOR, WALL, CEIL, LAVA, UNDER;
|
||||
|
||||
public static final Codec<StructurePlacementType> CODEC = StringRepresentable.fromEnum(StructurePlacementType::values);
|
||||
|
||||
|
|
|
@ -104,8 +104,6 @@ public class StructureWorldNBT extends StructureNBT {
|
|||
return canGenerateUnder(level, pos, rotation);
|
||||
else if (type == StructurePlacementType.CEIL)
|
||||
return canGenerateCeil(level, pos, rotation);
|
||||
else if (type == StructurePlacementType.FLOOR_FREE_ABOVE)
|
||||
return canGenerateFloorFreeAbove(level, pos, rotation);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue