Moved Classes
This commit is contained in:
parent
e05801277e
commit
4321017c25
2 changed files with 334 additions and 0 deletions
|
@ -0,0 +1,217 @@
|
|||
package org.betterx.bclib.world.features;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.data.worldgen.placement.PlacementUtils;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.util.valueproviders.ClampedNormalInt;
|
||||
import net.minecraft.util.valueproviders.ConstantInt;
|
||||
import net.minecraft.util.valueproviders.UniformInt;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
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.feature.configurations.SimpleRandomFeatureConfiguration;
|
||||
import net.minecraft.world.level.levelgen.placement.*;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import org.betterx.bclib.api.features.BCLFeatureBuilder;
|
||||
import org.betterx.bclib.api.tag.CommonBlockTags;
|
||||
import org.betterx.bclib.util.BlocksHelper;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ScatterFeature<FC extends ScatterFeatureConfig>
|
||||
extends Feature<FC> {
|
||||
|
||||
public static <T extends ScatterFeatureConfig> BCLFeature createAndRegister(ResourceLocation location,
|
||||
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)))));
|
||||
|
||||
return BCLFeatureBuilder.start(location, SIMPLE_RANDOM_SELECTOR)
|
||||
.decoration(GenerationStep.Decoration.VEGETAL_DECORATION)
|
||||
.modifier(CountPlacement.of(UniformInt.of(minPerChunk, maxPerChunk)))
|
||||
.modifier(InSquarePlacement.spread())
|
||||
.distanceToTopAndBottom4()
|
||||
.modifier(CountPlacement.of(UniformInt.of(2, 5)))
|
||||
.modifier(RandomOffsetPlacement.of(
|
||||
ClampedNormalInt.of(0.0f, 2.0f, -6, 6),
|
||||
ClampedNormalInt.of(0.0f, 0.6f, -2, 2)))
|
||||
.modifier(BiomeFilter.biome())
|
||||
.buildAndRegister(configuration);
|
||||
}
|
||||
|
||||
public ScatterFeature(Codec<FC> configCodec) {
|
||||
super(configCodec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<FC> featurePlaceContext) {
|
||||
final WorldGenLevel level = featurePlaceContext.level();
|
||||
final BlockPos origin = featurePlaceContext.origin();
|
||||
final RandomSource random = featurePlaceContext.random();
|
||||
|
||||
ScatterFeatureConfig config = featurePlaceContext.config();
|
||||
Optional<Direction> direction = getTipDirection(level, origin, random, config);
|
||||
if (direction.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
BlockPos basePos = origin.relative(direction.get().getOpposite());
|
||||
|
||||
int i = (int) (random.nextFloat() * (1 + config.maxHeight - config.minHeight) + config.minHeight);
|
||||
growCenterPillar(level, origin, basePos, direction.get(), i, config, random);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected void growCenterPillar(LevelAccessor level,
|
||||
BlockPos origin,
|
||||
BlockPos basePos,
|
||||
Direction direction,
|
||||
int centerHeight,
|
||||
ScatterFeatureConfig config,
|
||||
RandomSource random) {
|
||||
if (config.isValidBase(level.getBlockState(basePos))) {
|
||||
final Direction surfaceDirection = direction.getOpposite();
|
||||
BlockPos.MutableBlockPos POS = new BlockPos.MutableBlockPos();
|
||||
buildPillarWithBase(level, origin, basePos, direction, centerHeight, config, random);
|
||||
|
||||
|
||||
final double distNormalizer = (config.maxSpread * Math.sqrt(2));
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int x = origin.getX() + (int) (random.nextGaussian() * config.maxSpread);
|
||||
int z = origin.getZ() + (int) (random.nextGaussian() * config.maxSpread);
|
||||
POS.set(x, origin.getY(), z);
|
||||
|
||||
if (BlocksHelper.findSurface(level, POS, surfaceDirection, 4, config::isValidBase)) {
|
||||
POS.move(direction, 1);
|
||||
int dx = x - POS.getX();
|
||||
int dz = z - POS.getZ();
|
||||
float sizeFactor = (1 - (float) (Math.sqrt(dx * dx + dz * dz) / distNormalizer));
|
||||
sizeFactor = (1 - (random.nextFloat() * config.sizeVariation)) * sizeFactor;
|
||||
final int height = (int) Math.max(
|
||||
config.minHeight,
|
||||
config.minHeight + sizeFactor * (centerHeight - config.minHeight)
|
||||
);
|
||||
|
||||
buildPillarWithBase(level,
|
||||
POS,
|
||||
POS.relative(direction.getOpposite()),
|
||||
direction,
|
||||
height,
|
||||
config,
|
||||
random);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void buildPillarWithBase(LevelAccessor level,
|
||||
BlockPos origin,
|
||||
BlockPos basePos,
|
||||
Direction direction,
|
||||
int height,
|
||||
ScatterFeatureConfig config,
|
||||
RandomSource random) {
|
||||
if (BlocksHelper.isFreeSpace(level, origin, direction, height, (state) -> state.is(Blocks.AIR))) {
|
||||
createPatchOfBaseBlocks(level, random, basePos, config);
|
||||
buildPillar(level, origin, direction, height, config);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildPillar(LevelAccessor level,
|
||||
BlockPos origin,
|
||||
Direction direction,
|
||||
int height,
|
||||
ScatterFeatureConfig config) {
|
||||
|
||||
final BlockPos.MutableBlockPos POS = origin.mutable();
|
||||
buildBaseToTipColumn(height, (blockState) -> {
|
||||
BlocksHelper.setWithoutUpdate(level, POS, blockState);
|
||||
POS.move(direction);
|
||||
}, config);
|
||||
}
|
||||
|
||||
protected void buildBaseToTipColumn(int totalHeight, Consumer<BlockState> consumer, ScatterFeatureConfig config) {
|
||||
for (int size = totalHeight; size >= 0; size--) {
|
||||
consumer.accept(config.createBlock(size));
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<Direction> getTipDirection(LevelAccessor levelAccessor,
|
||||
BlockPos blockPos,
|
||||
RandomSource randomSource,
|
||||
ScatterFeatureConfig config) {
|
||||
boolean onCeil = config.floorChance < 1 && config.isValidBase(levelAccessor.getBlockState(blockPos.above()));
|
||||
boolean onFloor = config.floorChance > 0 && config.isValidBase(levelAccessor.getBlockState(blockPos.below()));
|
||||
|
||||
if (onCeil && onFloor) {
|
||||
return Optional.of(config.isFloor(randomSource) ? Direction.DOWN : Direction.UP);
|
||||
}
|
||||
if (onCeil) {
|
||||
return Optional.of(Direction.DOWN);
|
||||
}
|
||||
if (onFloor) {
|
||||
return Optional.of(Direction.UP);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private void createPatchOfBaseBlocks(LevelAccessor levelAccessor,
|
||||
RandomSource randomSource,
|
||||
BlockPos blockPos,
|
||||
ScatterFeatureConfig config) {
|
||||
if (config.baseState.isPresent() && config.baseReplaceChance > 0 && randomSource.nextFloat() < config.baseReplaceChance) {
|
||||
final BlockState baseState = config.baseState.get();
|
||||
BlockPos pos;
|
||||
for (Direction direction : Direction.Plane.HORIZONTAL) {
|
||||
if (randomSource.nextFloat() > config.chanceOfDirectionalSpread) continue;
|
||||
pos = blockPos.relative(direction);
|
||||
placeBaseBlockIfPossible(levelAccessor, pos, baseState);
|
||||
|
||||
if (randomSource.nextFloat() > config.chanceOfSpreadRadius2) continue;
|
||||
pos = pos.relative(Direction.getRandom(randomSource));
|
||||
placeBaseBlockIfPossible(levelAccessor, pos, baseState);
|
||||
|
||||
if (randomSource.nextFloat() > config.chanceOfSpreadRadius3) continue;
|
||||
pos = pos.relative(Direction.getRandom(randomSource));
|
||||
placeBaseBlockIfPossible(levelAccessor, pos, baseState);
|
||||
}
|
||||
placeBaseBlockIfPossible(levelAccessor, blockPos, baseState);
|
||||
}
|
||||
}
|
||||
|
||||
protected void placeBaseBlockIfPossible(LevelAccessor levelAccessor,
|
||||
BlockPos blockPos,
|
||||
BlockState baseState) {
|
||||
BlockState blockState = levelAccessor.getBlockState(blockPos);
|
||||
if (blockState.is(CommonBlockTags.STALAGMITE_BASE)) {
|
||||
levelAccessor.setBlock(blockPos, baseState, 2);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package org.betterx.bclib.world.features;
|
||||
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
|
||||
|
||||
import com.mojang.datafixers.util.Function11;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class ScatterFeatureConfig implements FeatureConfiguration {
|
||||
public final BlockState clusterBlock;
|
||||
public final Optional<BlockState> baseState;
|
||||
public final float baseReplaceChance;
|
||||
public final float chanceOfDirectionalSpread;
|
||||
public final float chanceOfSpreadRadius2;
|
||||
public final float chanceOfSpreadRadius3;
|
||||
public final int minHeight;
|
||||
public final int maxHeight;
|
||||
public final float maxSpread;
|
||||
public final float sizeVariation;
|
||||
public final float floorChance;
|
||||
|
||||
public ScatterFeatureConfig(BlockState clusterBlock,
|
||||
Optional<BlockState> baseState,
|
||||
float baseReplaceChance,
|
||||
float chanceOfDirectionalSpread,
|
||||
float chanceOfSpreadRadius2,
|
||||
float chanceOfSpreadRadius3,
|
||||
int minHeight,
|
||||
int maxHeight,
|
||||
float maxSpread,
|
||||
float sizeVariation,
|
||||
float floorChance) {
|
||||
this.clusterBlock = clusterBlock;
|
||||
this.baseState = baseState;
|
||||
this.baseReplaceChance = baseReplaceChance;
|
||||
this.chanceOfDirectionalSpread = chanceOfDirectionalSpread;
|
||||
this.chanceOfSpreadRadius2 = chanceOfSpreadRadius2;
|
||||
this.chanceOfSpreadRadius3 = chanceOfSpreadRadius3;
|
||||
this.minHeight = minHeight;
|
||||
this.maxHeight = maxHeight;
|
||||
this.maxSpread = maxSpread;
|
||||
this.sizeVariation = sizeVariation;
|
||||
this.floorChance = floorChance;
|
||||
}
|
||||
|
||||
|
||||
public boolean isFloor(RandomSource random) {
|
||||
return random.nextFloat() < floorChance;
|
||||
}
|
||||
|
||||
public abstract boolean isValidBase(BlockState state);
|
||||
|
||||
public abstract BlockState createBlock(int height);
|
||||
|
||||
public static <T extends ScatterFeatureConfig> Codec<T> buildCodec(Function11<BlockState, Optional<BlockState>, Float, Float, Float, Float, Integer, Integer, Float, Float, Float, T> instancer) {
|
||||
return RecordCodecBuilder.create((instance) -> instance
|
||||
.group(BlockState.CODEC
|
||||
.fieldOf("cluster_block")
|
||||
.forGetter((T cfg) -> cfg.clusterBlock),
|
||||
BlockState.CODEC
|
||||
.optionalFieldOf("base_state")
|
||||
.forGetter((T cfg) -> cfg.baseState),
|
||||
Codec
|
||||
.floatRange(0.0F, 1.0F)
|
||||
.fieldOf("baseReplaceChance")
|
||||
.orElse(1.0F)
|
||||
.forGetter((T cfg) -> cfg.baseReplaceChance),
|
||||
Codec
|
||||
.floatRange(0.0F, 1.0F)
|
||||
.fieldOf("chance_of_directional_spread")
|
||||
.orElse(0.7F)
|
||||
.forGetter((T cfg) -> cfg.chanceOfDirectionalSpread),
|
||||
Codec
|
||||
.floatRange(0.0F, 1.0F)
|
||||
.fieldOf("chance_of_spread_radius2")
|
||||
.orElse(0.5F)
|
||||
.forGetter((T cfg) -> cfg.chanceOfSpreadRadius2),
|
||||
Codec
|
||||
.floatRange(0.0F, 1.0F)
|
||||
.fieldOf("chance_of_spread_radius3")
|
||||
.orElse(0.5F)
|
||||
.forGetter((T cfg) -> cfg.chanceOfSpreadRadius3),
|
||||
Codec
|
||||
.intRange(1, 20)
|
||||
.fieldOf("min_height")
|
||||
.orElse(2)
|
||||
.forGetter((T cfg) -> cfg.minHeight),
|
||||
Codec
|
||||
.intRange(1, 20)
|
||||
.fieldOf("max_height")
|
||||
.orElse(7)
|
||||
.forGetter((T cfg) -> cfg.maxHeight),
|
||||
Codec
|
||||
.floatRange(0, 10)
|
||||
.fieldOf("max_spread")
|
||||
.orElse(2f)
|
||||
.forGetter((T cfg) -> cfg.maxSpread),
|
||||
Codec
|
||||
.floatRange(0, 1)
|
||||
.fieldOf("size_variation")
|
||||
.orElse(0.7f)
|
||||
.forGetter((T cfg) -> cfg.sizeVariation),
|
||||
Codec
|
||||
.floatRange(0, 1)
|
||||
.fieldOf("floor_chance")
|
||||
.orElse(0.5f)
|
||||
.forGetter((T cfg) -> cfg.floorChance)
|
||||
)
|
||||
.apply(instance, instancer)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue