diff --git a/src/main/java/org/betterx/bclib/api/v2/generator/BCLibNetherBiomeSource.java b/src/main/java/org/betterx/bclib/api/v2/generator/BCLibNetherBiomeSource.java index 5e7abdf0..07d10b26 100644 --- a/src/main/java/org/betterx/bclib/api/v2/generator/BCLibNetherBiomeSource.java +++ b/src/main/java/org/betterx/bclib/api/v2/generator/BCLibNetherBiomeSource.java @@ -16,7 +16,6 @@ import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.resources.RegistryOps; import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.BiomeTags; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.biome.Climate; @@ -148,9 +147,10 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource { private static boolean isValidNetherBiome(Holder biome, ResourceLocation location) { - return NetherBiomes.canGenerateInNether(biome.unwrapKey().get()) || - biome.is(BiomeTags.IS_NETHER) || - BiomeAPI.wasRegisteredAsNetherBiome(location); + return biome.unwrapKey().get().location().toString().equals("betternether:bone_reef"); +// return NetherBiomes.canGenerateInNether(biome.unwrapKey().get()) || +// biome.is(BiomeTags.IS_NETHER) || +// BiomeAPI.wasRegisteredAsNetherBiome(location); } private static boolean isValidNonVanillaNetherBiome(Holder biome, ResourceLocation location) { diff --git a/src/main/java/org/betterx/bclib/api/v2/levelgen/biomes/BCLBiomeBuilder.java b/src/main/java/org/betterx/bclib/api/v2/levelgen/biomes/BCLBiomeBuilder.java index 25b26be5..f44a281a 100644 --- a/src/main/java/org/betterx/bclib/api/v2/levelgen/biomes/BCLBiomeBuilder.java +++ b/src/main/java/org/betterx/bclib/api/v2/levelgen/biomes/BCLBiomeBuilder.java @@ -595,6 +595,16 @@ public class BCLBiomeBuilder { return feature(feature.getDecoration(), feature.getPlacedFeature()); } + /** + * Adds new feature to the biome. + * + * @param feature {@link BCLFeature}. + * @return same {@link BCLBiomeBuilder} instance. + */ + public BCLBiomeBuilder feature(org.betterx.bclib.api.v3.levelgen.features.BCLFeature feature) { + return feature(feature.decoration, feature.placedFeature); + } + /** * Adds new structure feature into the biome. * diff --git a/src/main/java/org/betterx/bclib/api/v2/levelgen/features/FastFeatures.java b/src/main/java/org/betterx/bclib/api/v2/levelgen/features/FastFeatures.java index 8eb495b9..09c4774b 100644 --- a/src/main/java/org/betterx/bclib/api/v2/levelgen/features/FastFeatures.java +++ b/src/main/java/org/betterx/bclib/api/v2/levelgen/features/FastFeatures.java @@ -4,6 +4,8 @@ import org.betterx.bclib.api.v2.levelgen.features.config.PlaceFacingBlockConfig; import org.betterx.bclib.api.v2.levelgen.features.config.ScatterFeatureConfig; import org.betterx.bclib.api.v2.levelgen.features.features.ScatterFeature; +import net.minecraft.data.worldgen.features.FeatureUtils; +import net.minecraft.data.worldgen.placement.PlacementUtils; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.valueproviders.UniformInt; import net.minecraft.world.level.block.Block; @@ -19,6 +21,12 @@ import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvi import net.minecraft.world.level.levelgen.feature.stateproviders.RandomizedIntStateProvider; public class FastFeatures { + public static RandomPatchConfiguration grassPatch(BlockStateProvider stateProvider, int tries) { + return FeatureUtils.simpleRandomPatchConfiguration( + tries, + PlacementUtils.onlyWhenEmpty(Feature.SIMPLE_BLOCK, new SimpleBlockConfiguration(stateProvider)) + ); + } public static BCLFeature, ScatterFeatureConfig.OnSolid> vine( diff --git a/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLConfigureFeature.java b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLConfigureFeature.java new file mode 100644 index 00000000..b3932c62 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLConfigureFeature.java @@ -0,0 +1,49 @@ +package org.betterx.bclib.api.v3.levelgen.features; + +import net.minecraft.core.Holder; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; + +import java.util.HashMap; +import java.util.Map; + +public class BCLConfigureFeature, FC extends FeatureConfiguration> { + private static final Map>, BCLConfigureFeature> KNOWN = new HashMap<>(); + + public final ResourceLocation id; + public final Holder> configuredFeature; + public final boolean registered; + + BCLConfigureFeature(ResourceLocation id, Holder> configuredFeature, boolean registered) { + this.id = id; + this.configuredFeature = configuredFeature; + this.registered = registered; + } + + public F getFeature() { + return configuredFeature.value().feature(); + } + + public FC getConfiguration() { + return configuredFeature.value().config(); + } + + + public BCLPlacedFeatureBuilder place() { + return place(this.id); + } + + BCLPlacedFeatureBuilder place(ResourceLocation id) { + return BCLPlacedFeatureBuilder.place(id, this); + } + + static , FC extends FeatureConfiguration> BCLConfigureFeature create(Holder> registeredFeature) { + return (BCLConfigureFeature) KNOWN.computeIfAbsent( + (Holder>) (Object) registeredFeature, + holder -> new BCLConfigureFeature<>(holder.unwrapKey().orElseThrow() + .location(), registeredFeature, false) + ); + } +} diff --git a/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLFeature.java b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLFeature.java new file mode 100644 index 00000000..ad8af16c --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLFeature.java @@ -0,0 +1,36 @@ +package org.betterx.bclib.api.v3.levelgen.features; + +import org.betterx.bclib.api.v2.levelgen.features.config.*; + +import net.minecraft.core.Holder; +import net.minecraft.world.level.levelgen.GenerationStep; +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.NoneFeatureConfiguration; +import net.minecraft.world.level.levelgen.feature.configurations.RandomFeatureConfiguration; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; + +public class BCLFeature, FC extends FeatureConfiguration> { + public static final Feature PLACE_BLOCK = org.betterx.bclib.api.v2.levelgen.features.BCLFeature.PLACE_BLOCK; + public static final Feature SCATTER_ON_SOLID = org.betterx.bclib.api.v2.levelgen.features.BCLFeature.SCATTER_ON_SOLID; + public static final Feature SCATTER_EXTEND_TOP = org.betterx.bclib.api.v2.levelgen.features.BCLFeature.SCATTER_EXTEND_TOP; + public static final Feature SCATTER_EXTEND_BOTTOM = org.betterx.bclib.api.v2.levelgen.features.BCLFeature.SCATTER_EXTEND_BOTTOM; + public static final Feature RANDOM_SELECTOR = org.betterx.bclib.api.v2.levelgen.features.BCLFeature.RANDOM_SELECTOR; + public static final Feature TEMPLATE = org.betterx.bclib.api.v2.levelgen.features.BCLFeature.TEMPLATE; + public static final Feature MARK_POSTPROCESSING = org.betterx.bclib.api.v2.levelgen.features.BCLFeature.MARK_POSTPROCESSING; + public static final Feature SEQUENCE = org.betterx.bclib.api.v2.levelgen.features.BCLFeature.SEQUENCE; + public static final Feature CONDITION = org.betterx.bclib.api.v2.levelgen.features.BCLFeature.CONDITION; + public final BCLConfigureFeature configuredFeature; + public final Holder placedFeature; + public final GenerationStep.Decoration decoration; + + BCLFeature( + BCLConfigureFeature configuredFeature, + Holder placed, + GenerationStep.Decoration decoration + ) { + this.configuredFeature = configuredFeature; + this.placedFeature = placed; + this.decoration = decoration; + } +} diff --git a/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLFeatureBuilder.java b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLFeatureBuilder.java new file mode 100644 index 00000000..068153f9 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLFeatureBuilder.java @@ -0,0 +1,289 @@ +package org.betterx.bclib.api.v3.levelgen.features; + +import org.betterx.bclib.api.v2.levelgen.features.BCLFeature; +import org.betterx.bclib.api.v2.poi.BCLPoiType; + +import net.minecraft.core.Holder; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.random.SimpleWeightedRandomList; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.configurations.*; +import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider; +import net.minecraft.world.level.levelgen.feature.stateproviders.WeightedStateProvider; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; + +import java.util.Collection; +import java.util.Set; +import java.util.function.BiFunction; +import org.jetbrains.annotations.NotNull; + +public abstract class BCLFeatureBuilder, FC extends FeatureConfiguration> { + public static class NetherForrestVegetation> extends BCLFeatureBuilder { + private SimpleWeightedRandomList.Builder blocks; + private WeightedStateProvider stateProvider; + private int spreadWidth = 8; + private int spreadHeight = 4; + + private NetherForrestVegetation(ResourceLocation featureID, FF feature) { + super(featureID, feature); + } + + public NetherForrestVegetation spreadWidth(int v) { + spreadWidth = v; + return this; + } + + public NetherForrestVegetation spreadHeight(int v) { + spreadHeight = v; + return this; + } + + public NetherForrestVegetation addAllStates(Block block, int weight) { + Set states = BCLPoiType.getBlockStates(block); + states.forEach(s -> add(block.defaultBlockState(), Math.max(1, weight / states.size()))); + return this; + } + + public NetherForrestVegetation addAllStatesFor(IntegerProperty prop, Block block, int weight) { + Collection values = prop.getPossibleValues(); + values.forEach(s -> add(block.defaultBlockState().setValue(prop, s), Math.max(1, weight / values.size()))); + return this; + } + + public NetherForrestVegetation add(Block block, int weight) { + return add(block.defaultBlockState(), weight); + } + + public NetherForrestVegetation add(BlockState state, int weight) { + if (stateProvider != null) { + throw new IllegalStateException("You can not add new state once a WeightedStateProvider was built. (" + state + ", " + weight + ")"); + } + if (blocks == null) { + blocks = SimpleWeightedRandomList.builder(); + } + blocks.add(state, weight); + return this; + } + + public NetherForrestVegetation provider(WeightedStateProvider provider) { + if (blocks != null) { + throw new IllegalStateException( + "You can not set a WeightedStateProvider after states were added manually."); + } + stateProvider = provider; + return this; + } + + @Override + public NetherForestVegetationConfig createConfiguration() { + if (stateProvider == null && blocks == null) { + throw new IllegalStateException("NetherForestVegetationConfig needs at least one BlockState"); + } + if (stateProvider == null) stateProvider = new WeightedStateProvider(blocks.build()); + return new NetherForestVegetationConfig(stateProvider, spreadWidth, spreadHeight); + } + } + + public static class RandomPatch> extends BCLFeatureBuilder { + private int tries = 96; + private int xzSpread = 7; + private int ySpread = 3; + private final Holder featureToPlace; + + private RandomPatch( + @NotNull ResourceLocation featureID, + @NotNull FF feature, + @NotNull Holder featureToPlace + ) { + super(featureID, feature); + this.featureToPlace = featureToPlace; + } + + public RandomPatch tries(int v) { + tries = v; + return this; + } + + public RandomPatch spreadXZ(int v) { + xzSpread = v; + return this; + } + + public RandomPatch spreadY(int v) { + ySpread = v; + return this; + } + + + @Override + public RandomPatchConfiguration createConfiguration() { + return new RandomPatchConfiguration(tries, xzSpread, ySpread, featureToPlace); + } + } + + public static class WithConfiguration, FC extends FeatureConfiguration> extends BCLFeatureBuilder { + private FC configuration; + + private WithConfiguration(@NotNull ResourceLocation featureID, @NotNull F feature) { + super(featureID, feature); + } + + public WithConfiguration configuration(FC config) { + this.configuration = config; + return this; + } + + + @Override + public FC createConfiguration() { + if (configuration == null) return (FC) NoneFeatureConfiguration.NONE; + return configuration; + } + } + + public static class ForSimpleBlock> extends BCLFeatureBuilder { + private final BlockStateProvider provider; + + private ForSimpleBlock( + @NotNull ResourceLocation featureID, + @NotNull FF feature, + @NotNull BlockStateProvider provider + ) { + super(featureID, feature); + this.provider = provider; + } + + @Override + public SimpleBlockConfiguration createConfiguration() { + return new SimpleBlockConfiguration(provider); + } + } + + private final ResourceLocation featureID; + private final F feature; + + private BCLFeatureBuilder(ResourceLocation featureID, F feature) { + this.featureID = featureID; + this.feature = feature; + } + + /** + * Starts a new {@link BCLFeature} builder. + * + * @param featureID {@link ResourceLocation} feature identifier. + * @param feature {@link Feature} to construct. + * @return {@link org.betterx.bclib.api.v2.levelgen.features.BCLFeatureBuilder} instance. + */ + public static , FC extends FeatureConfiguration> WithConfiguration start( + ResourceLocation featureID, + F feature + ) { + return new WithConfiguration(featureID, feature); + } + + public static ForSimpleBlock start( + ResourceLocation featureID, + Block block + ) { + return start(featureID, BlockStateProvider.simple(block)); + } + + public static ForSimpleBlock start( + ResourceLocation featureID, + BlockState state + ) { + return start(featureID, BlockStateProvider.simple(state)); + } + + public static ForSimpleBlock start( + ResourceLocation featureID, + BlockStateProvider provider + ) { + ForSimpleBlock builder = new ForSimpleBlock( + featureID, + Feature.SIMPLE_BLOCK, + provider + ); + return builder; + } + + public static RandomPatch startRandomPatch( + ResourceLocation featureID, + Holder featureToPlace + ) { + RandomPatch builder = new RandomPatch( + featureID, + Feature.RANDOM_PATCH, + featureToPlace + ); + return builder; + } + + public static NetherForrestVegetation startNetherVegetation( + ResourceLocation featureID + ) { + NetherForrestVegetation builder = new NetherForrestVegetation( + featureID, + Feature.NETHER_FOREST_VEGETATION + ); + return builder; + } + + public abstract FC createConfiguration(); + + protected BCLConfigureFeature buildAndRegister(BiFunction, Holder>> holderBuilder) { + FC config = createConfiguration(); + if (config == null) { + throw new IllegalStateException("Feature configuration for " + featureID + " can not be null!"); + } + ConfiguredFeature cFeature = new ConfiguredFeature(feature, config); + Holder> holder = holderBuilder.apply(featureID, cFeature); + return new BCLConfigureFeature<>(featureID, holder, true); + } + + public BCLConfigureFeature buildAndRegister() { + return buildAndRegister(BCLFeatureBuilder::register); + } + + public BCLConfigureFeature build() { + return buildAndRegister((id, cFeature) -> Holder.direct(cFeature)); + } + + public BCLInlinePlacedBuilder inlinePlace() { + BCLConfigureFeature f = build(); + return BCLInlinePlacedBuilder.place(f); + } + + public Holder inlinePlace(BCLInlinePlacedBuilder placer) { + BCLConfigureFeature f = build(); + return placer.build(f); + } + + /** + * Internally used by the builder. Normally you should not have to call this method directly as it is + * handled by {@link #buildAndRegister()} + * + * @param id The ID to register this feature with + * @param cFeature The configured Feature + * @param The Feature Class + * @param The FeatureConfiguration Class + * @return The Holder for the new Feature + */ + public static , FC extends FeatureConfiguration> Holder> register( + ResourceLocation id, + ConfiguredFeature cFeature + ) { + return (Holder>) (Object) BuiltinRegistries.register( + BuiltinRegistries.CONFIGURED_FEATURE, + id, + cFeature + ); + } +} + + diff --git a/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLInlinePlacedBuilder.java b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLInlinePlacedBuilder.java new file mode 100644 index 00000000..cc54cae4 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLInlinePlacedBuilder.java @@ -0,0 +1,83 @@ +package org.betterx.bclib.api.v3.levelgen.features; + +import net.minecraft.core.Holder; +import net.minecraft.data.worldgen.placement.PlacementUtils; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import net.minecraft.world.level.levelgen.placement.PlacementModifier; + +public class BCLInlinePlacedBuilder, FC extends FeatureConfiguration> extends CommonPlacedFeatureBuilder> { + private final BCLConfigureFeature cFeature; + + private BCLInlinePlacedBuilder(BCLConfigureFeature cFeature) { + this.cFeature = cFeature; + } + + /** + * Starts a new {@link BCLFeature} builder. + * + * @param holder {@link Feature} the configured Feature to start from. + * @return {@link CommonPlacedFeatureBuilder} instance. + */ + public static , FC extends FeatureConfiguration> BCLInlinePlacedBuilder place( + ResourceLocation featureID, + Holder> holder + ) { + return place(BCLConfigureFeature.create(holder)); + } + + + /** + * Starts a new {@link BCLFeature} builder. + * + * @param cFeature {@link Feature} the configured Feature to start from. + * @return {@link CommonPlacedFeatureBuilder} instance. + */ + static , FC extends FeatureConfiguration> BCLInlinePlacedBuilder place( + BCLConfigureFeature cFeature + ) { + return new BCLInlinePlacedBuilder(cFeature); + } + + /** + * Builds a new inline (not registered) {@link PlacedFeature}. + * + * @return created {@link PlacedFeature} instance. + */ + public Holder build() { + return build(cFeature); + } + + /** + * Builds a new inline (not registered) {@link PlacedFeature}. + * + * @return created {@link PlacedFeature} instance. + */ + public Holder build(BCLConfigureFeature feature) { + return build(feature.configuredFeature); + } + + /** + * Builds a new inline (not registered) {@link PlacedFeature}. + * + * @return created {@link PlacedFeature} instance. + */ + public Holder build(Holder> feature) { + PlacementModifier[] modifiers = modifications.toArray(new PlacementModifier[modifications.size()]); + return PlacementUtils.inlinePlaced(feature, modifiers); + } + + /** + * Builds a new inline (not registered) {@link PlacedFeature}. + * + * @return created {@link PlacedFeature} instance. + */ + public Holder build(F feature, FC configuration) { + PlacementModifier[] modifiers = modifications.toArray(new PlacementModifier[modifications.size()]); + return PlacementUtils.inlinePlaced(feature, configuration, modifiers); + } + +} diff --git a/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLPlacedFeatureBuilder.java b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLPlacedFeatureBuilder.java new file mode 100644 index 00000000..d0e8df93 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/BCLPlacedFeatureBuilder.java @@ -0,0 +1,88 @@ +package org.betterx.bclib.api.v3.levelgen.features; + +import net.minecraft.core.Holder; +import net.minecraft.data.worldgen.placement.PlacementUtils; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; + +public class BCLPlacedFeatureBuilder, FC extends FeatureConfiguration> extends CommonPlacedFeatureBuilder> { + private final ResourceLocation featureID; + private GenerationStep.Decoration decoration = GenerationStep.Decoration.VEGETAL_DECORATION; + private final BCLConfigureFeature cFeature; + + private BCLPlacedFeatureBuilder(ResourceLocation featureID, BCLConfigureFeature cFeature) { + this.featureID = featureID; + this.cFeature = cFeature; + } + + + /** + * Set generation step for the feature. Default is {@code VEGETAL_DECORATION}. + * + * @param decoration {@link GenerationStep.Decoration} step. + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + public BCLPlacedFeatureBuilder decoration(GenerationStep.Decoration decoration) { + this.decoration = decoration; + return this; + } + + /** + * Starts a new {@link BCLFeature} builder. + * + * @param featureID {@link ResourceLocation} feature identifier. + * @param holder {@link Feature} the configured Feature to start from. + * @return {@link CommonPlacedFeatureBuilder} instance. + */ + public static , FC extends FeatureConfiguration> BCLPlacedFeatureBuilder place( + ResourceLocation featureID, + Holder> holder + ) { + return place(featureID, BCLConfigureFeature.create(holder)); + } + + + /** + * Starts a new {@link BCLFeature} builder. + * + * @param featureID {@link ResourceLocation} feature identifier. + * @param cFeature {@link Feature} the configured Feature to start from. + * @return {@link CommonPlacedFeatureBuilder} instance. + */ + static , FC extends FeatureConfiguration> BCLPlacedFeatureBuilder place( + ResourceLocation featureID, + BCLConfigureFeature cFeature + ) { + return new BCLPlacedFeatureBuilder(featureID, cFeature); + } + + /** + * Builds a new {@link BCLFeature} instance. + * + * @return created {@link BCLFeature} instance. + */ + protected Holder build() { + Holder p = PlacementUtils.register( + featureID.toString(), + cFeature.configuredFeature, + modifications + ); + return p; + } + + + /** + * Builds a new {@link BCLFeature} instance. + * Features will be registered during this process. + * + * @return created {@link BCLFeature} instance. + */ + public BCLFeature buildAndRegister() { + Holder p = build(); + return new BCLFeature(cFeature, p, decoration); + } +} diff --git a/src/main/java/org/betterx/bclib/api/v3/levelgen/features/CommonPlacedFeatureBuilder.java b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/CommonPlacedFeatureBuilder.java new file mode 100644 index 00000000..e7bbe8d0 --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/CommonPlacedFeatureBuilder.java @@ -0,0 +1,375 @@ +package org.betterx.bclib.api.v3.levelgen.features; + +import org.betterx.bclib.api.v2.levelgen.features.config.PlaceFacingBlockConfig; +import org.betterx.bclib.api.v2.levelgen.features.placement.*; +import org.betterx.bclib.api.v2.tag.CommonBlockTags; + +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.Vec3i; +import net.minecraft.data.worldgen.placement.PlacementUtils; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.valueproviders.IntProvider; +import net.minecraft.util.valueproviders.UniformInt; +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 java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +abstract class CommonPlacedFeatureBuilder, FC extends FeatureConfiguration, T extends CommonPlacedFeatureBuilder> { + protected final List modifications = new LinkedList<>(); + + + /** + * Add feature placement modifier. Used as a condition for feature how to generate. + * + * @param modifiers {@link PlacementModifier}s to add. + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + public T modifier(PlacementModifier... modifiers) { + for (var m : modifiers) + modifications.add(m); + return (T) this; + } + + /** + * Add feature placement modifier. Used as a condition for feature how to generate. + * + * @param modifiers {@link PlacementModifier}s to add. + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + public T modifier(List modifiers) { + modifications.addAll(modifiers); + return (T) this; + } + + /** + * Generate feature in certain iterations (per chunk). + * + * @param count how many times feature will be generated in chunk. + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + public T count(int count) { + return modifier(CountPlacement.of(count)); + } + + /** + * Generate feature in certain iterations (per chunk). Count can be between 0 and max value. + * + * @param count maximum amount of iterations per chunk. + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + public T countMax(int count) { + return modifier(CountPlacement.of(UniformInt.of(0, count))); + } + + public T countRange(int min, int max) { + return modifier(CountPlacement.of(UniformInt.of(min, max))); + } + + /** + * Generate points in a stencil pattern + * + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + public T stencil() { + return modifier(Stencil.all()); + } + + /** + * Generate points in a stencil pattern and selecting (on average) only every 4th + * + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + public T stencilOneIn4() { + return modifier(Stencil.oneIn4()); + } + + /** + * Generate points for every xz-Coordinate in a chunk. Be carefuller, this is quite expensive! + * + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + public T all() { + return modifier(All.simple()); + } + + /** + * Generate feature in certain iterations (per chunk). + * Feature will be generated on all layers (example - Nether plants). + * + * @param count how many times feature will be generated in chunk layers. + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + @SuppressWarnings("deprecation") + public T onEveryLayer(int count) { + return modifier(CountOnEveryLayerPlacement.of(count)); + } + + /** + * Generate feature in certain iterations (per chunk). Count can be between 0 and max value. + * Feature will be generated on all layers (example - Nether plants). + * + * @param count maximum amount of iterations per chunk layers. + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + @SuppressWarnings("deprecation") + public T onEveryLayerMax(int count) { + return modifier(CountOnEveryLayerPlacement.of(UniformInt.of(0, count))); + } + + public T onEveryLayer() { + return modifier(OnEveryLayer.simple()); + } + + public T onEveryLayerMin4() { + return modifier(OnEveryLayer.min4()); + } + + public T underEveryLayer() { + return modifier(UnderEveryLayer.simple()); + } + + public T underEveryLayerMin4() { + return modifier(UnderEveryLayer.min4()); + } + + /** + * Will place feature once every n-th attempts (in average). + * + * @param n amount of attempts. + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + public T onceEvery(int n) { + return modifier(RarityFilter.onAverageOnceEvery(n)); + } + + /** + * Restricts feature generation only to biome where feature was added. + * + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + public T onlyInBiome() { + return modifier(BiomeFilter.biome()); + } + + /** + * Randomize the xz-Coordinates + * + * @return same {@link CommonPlacedFeatureBuilder} instance. + */ + public T squarePlacement() { + return modifier(InSquarePlacement.spread()); + } + + + /** + * Select random height that is 10 above min Build height and 10 below max generation height + * + * @return The instance it was called on + */ + public T randomHeight10FromFloorCeil() { + return modifier(PlacementUtils.RANGE_10_10); + } + + /** + * Select random height that is 4 above min Build height and 10 below max generation height + * + * @return The instance it was called on + */ + public T 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 T 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 T randomHeight() { + return modifier(PlacementUtils.FULL_RANGE); + } + + public T isEmptyAbove4() { + return modifier(IsEmptyAboveSampledFilter.emptyAbove4()); + } + + public T isEmptyAbove2() { + return modifier(IsEmptyAboveSampledFilter.emptyAbove2()); + } + + public T isEmptyAbove() { + return modifier(IsEmptyAboveSampledFilter.emptyAbove()); + } + + public T isEmptyBelow4() { + return modifier(IsEmptyAboveSampledFilter.emptyBelow4()); + } + + public T isEmptyBelow2() { + return modifier(IsEmptyAboveSampledFilter.emptyBelow2()); + } + + public T isEmptyBelow() { + return modifier(IsEmptyAboveSampledFilter.emptyBelow()); + } + + public T isEmptyAbove(int d1, int d2) { + return modifier(new IsEmptyAboveSampledFilter(d1, d2)); + } + + public T spreadHorizontal(IntProvider p) { + return modifier(RandomOffsetPlacement.horizontal(p)); + } + + public T spreadVertical(IntProvider p) { + return modifier(RandomOffsetPlacement.horizontal(p)); + } + + public T spread(IntProvider horizontal, IntProvider vertical) { + return modifier(RandomOffsetPlacement.of(horizontal, vertical)); + } + + public T offset(Direction dir) { + return modifier(Offset.inDirection(dir)); + } + + public T offset(Vec3i dir) { + return modifier(new Offset(dir)); + } + + /** + * 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 T findSolidFloor(int distance) { + return modifier(FindSolidInDirection.down(distance)); + } + + public T noiseBasedCount(float noiseLevel, int belowNoiseCount, int aboveNoiseCount) { + return modifier(NoiseThresholdCountPlacement.of(noiseLevel, belowNoiseCount, aboveNoiseCount)); + } + + public T extendDown(int min, int max) { + return modifier(new Extend(Direction.DOWN, UniformInt.of(min, max))); + } + + public T inBasinOf(BlockPredicate... predicates) { + return modifier(new IsBasin(BlockPredicate.anyOf(predicates))); + } + + public T inOpenBasinOf(BlockPredicate... predicates) { + return modifier(IsBasin.openTop(BlockPredicate.anyOf(predicates))); + } + + public T is(BlockPredicate... predicates) { + return modifier(new Is(BlockPredicate.anyOf(predicates), Optional.empty())); + } + + public T isAbove(BlockPredicate... predicates) { + return modifier(new Is(BlockPredicate.anyOf(predicates), Optional.of(Direction.DOWN.getNormal()))); + } + + public T isUnder(BlockPredicate... predicates) { + return modifier(new Is(BlockPredicate.anyOf(predicates), Optional.of(Direction.UP.getNormal()))); + } + + public T findSolidCeil(int distance) { + return modifier(FindSolidInDirection.up(distance)); + } + + public T hasMinimumDownwardSpace() { + return modifier(MinEmptyFilter.down()); + } + + public T hasMinimumUpwardSpace() { + return modifier(MinEmptyFilter.up()); + } + + + /** + * 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 block with the + * {@link CommonBlockTags#TERRAIN}-tag + * + * @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 T findSolidSurface(Direction dir, int distance) { + return modifier(new FindSolidInDirection(dir, distance)); + } + + public T findSolidSurface(List dir, int distance, boolean randomSelect) { + return modifier(new FindSolidInDirection(dir, distance, randomSelect)); + } + + public T heightmap() { + return modifier(PlacementUtils.HEIGHTMAP); + } + + public T heightmapTopSolid() { + return modifier(PlacementUtils.HEIGHTMAP_TOP_SOLID); + } + + public T heightmapWorldSurface() { + return modifier(PlacementUtils.HEIGHTMAP_WORLD_SURFACE); + } + + public T onlyWhenEmpty() { + return modifier(BlockPredicateFilter.forPredicate(BlockPredicate.ONLY_IN_AIR_PREDICATE)); + } + + public T filtered(BlockPredicate predicate) { + return modifier(BlockPredicateFilter.forPredicate(predicate)); + } + + public T vanillaNetherGround(int countPerLayer) { + return (T) this.onEveryLayer(countPerLayer).onlyInBiome(); + } + + public T betterNetherGround(int countPerLayer) { + return (T) this.count(countPerLayer).squarePlacement().onEveryLayerMin4().onlyInBiome(); + } + + public T betterNetherCeiling(int countPerLayer) { + return (T) this.count(countPerLayer).squarePlacement().underEveryLayerMin4().onlyInBiome(); + } + + public T betterNetherWall(int countPerLayer) { + return (T) this.count(countPerLayer) + .squarePlacement() + .randomHeight4FromFloorCeil() + .modifier(new FindSolidInDirection(PlaceFacingBlockConfig.HORIZONTAL, 12, false)) + .onlyInBiome(); + } + + /** + * Builds a new inline (not registered) {@link PlacedFeature}. + * + * @return created {@link PlacedFeature} instance. + */ + abstract Holder build(); + + public BCLFeatureBuilder.RandomPatch inRandomPatch(ResourceLocation id) { + return BCLFeatureBuilder.startRandomPatch(id, build()); + } +} diff --git a/src/main/java/org/betterx/bclib/api/v3/levelgen/features/UserGrowableFeature.java b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/UserGrowableFeature.java new file mode 100644 index 00000000..874bc07c --- /dev/null +++ b/src/main/java/org/betterx/bclib/api/v3/levelgen/features/UserGrowableFeature.java @@ -0,0 +1,6 @@ +package org.betterx.bclib.api.v3.levelgen.features; + +import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration; + +public interface UserGrowableFeature extends org.betterx.bclib.api.v2.levelgen.features.UserGrowableFeature { +}