Add enhanced variants for placement modifiers

This commit is contained in:
DBotThePony 2025-03-25 00:07:55 +07:00
parent ba492e0cee
commit 07b295ce45
Signed by: DBot
GPG Key ID: DCC23B5715498507
9 changed files with 213 additions and 34 deletions

View File

@ -38,8 +38,13 @@ import ru.dbotthepony.mc.otm.worldgen.feature.DebugPlacerFeature
import ru.dbotthepony.mc.otm.worldgen.feature.EnhancedFeature
import ru.dbotthepony.mc.otm.worldgen.placement.ChainPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EllipsoidPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedChainPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedCountPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacementModifier
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedSplitPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.SplitPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement
import ru.dbotthepony.mc.otm.worldgen.wrap
private object ConfiguredFeatures {
val TRITANIUM_ORE = key("tritanium_ore")
@ -133,7 +138,7 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
.then(InSquarePlacement.spread())
.then(HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(-40), 15.0)))
.then(
SplitPlacement(
EnhancedSplitPlacement(
// "heart"
EllipsoidPlacement(
count = UniformInt.of(600, 900),
@ -145,8 +150,8 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
z = ClampedNormalFloat.of(0f, 0.4f, -1f, 1f),
),
// "branches"
ChainPlacement(
CountPlacement.of(UniformInt.of(2, 7)),
EnhancedChainPlacement(
EnhancedCountPlacement(UniformInt.of(2, 7)),
WormPlacement(
length = UniformInt.of(60, 120),
turnRateXY = WormPlacement.normalDistributedTurnRate(2f, 3f),
@ -201,7 +206,7 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
xLength = UniformFloat.of(30f, 70f),
yLength = UniformFloat.of(40f, 90f),
zLength = UniformFloat.of(30f, 70f),
)
) as EnhancedPlacementModifier
)
.then(ore)
.build(

View File

@ -4,20 +4,34 @@ import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.MRegistries
import ru.dbotthepony.mc.otm.worldgen.placement.ChainPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EllipsoidPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedChainPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedCountPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedSplitPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.SplitPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement
object MPlacementModifiers {
private val registry = MDeferredRegister(BuiltInRegistries.PLACEMENT_MODIFIER_TYPE)
private val enhancedRegistry = MDeferredRegister(MRegistries.PLACEMENT_MODIFIER)
internal fun register(bus: IEventBus) {
registry.register(bus)
enhancedRegistry.register(bus)
}
val ELLIPSOID_PLACEMENT by registry.register("ellipsoid") { PlacementModifierType { EllipsoidPlacement.CODEC } }
val WORM_PLACEMENT by registry.register("worm") { PlacementModifierType { WormPlacement.CODEC } }
val SPLIT by registry.register("split") { PlacementModifierType { SplitPlacement.CODEC} }
val CHAIN by registry.register("chain") { PlacementModifierType { ChainPlacement.CODEC} }
val CHAIN by registry.register("chain") { PlacementModifierType { ChainPlacement.CODEC } }
init {
enhancedRegistry.register("ellipsoid") { EllipsoidPlacement.Companion }
enhancedRegistry.register("worm") { WormPlacement.Companion }
enhancedRegistry.register("split") { EnhancedSplitPlacement.Companion }
enhancedRegistry.register("chain") { EnhancedChainPlacement.Companion }
enhancedRegistry.register("count") { EnhancedCountPlacement.Companion }
}
}

View File

@ -297,6 +297,11 @@ object EnhancedPlacedFeature : Feature<EnhancedPlacedFeature.Config>(
return then(EnhancedFeature.Wrapper.configure(action))
}
inline fun fork(configurator: Builder.() -> Unit): Builder {
configurator(this)
return this
}
private fun buildNode(): Node {
return Node(children.map { it.buildNode() }, contents)
}

View File

@ -0,0 +1,8 @@
package ru.dbotthepony.mc.otm.worldgen
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacementModifier
fun PlacementModifier.wrap(): EnhancedPlacementModifier {
return EnhancedPlacementModifier.Wrapper(this)
}

View File

@ -15,6 +15,7 @@ import ru.dbotthepony.mc.otm.core.math.component3
import ru.dbotthepony.mc.otm.core.math.minus
import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
import java.util.stream.Stream
import kotlin.math.PI
import kotlin.math.roundToInt
@ -57,22 +58,18 @@ data class EllipsoidPlacement(
* Ellipsoid size sampler on Y axis
*/
val yLength: FloatProvider,
) : PlacementModifier() {
) : PlacementModifier(), EnhancedPlacementModifier {
init {
require(xLength.minValue >= 1f) { "Bad ellipsoid x minimal size: $xLength" }
require(zLength.minValue >= 1f) { "Bad ellipsoid z minimal size: $zLength" }
require(yLength.minValue >= 1f) { "Bad ellipsoid y minimal size: $yLength" }
}
override fun getPositions(
context: PlacementContext,
random: RandomSource,
position: BlockPos
): Stream<BlockPos> {
private fun evaluate(random: RandomSource, position: BlockPos, result: ArrayList<BlockPos>) {
val count = count.sample(random)
if (count <= 0)
return Stream.empty()
return
val xLength = xLength.sample(random)
val zLength = zLength.sample(random)
@ -82,7 +79,9 @@ data class EllipsoidPlacement(
val zPow = zLength * zLength
val yPow = yLength * yLength
return Stream.generate {
result.ensureCapacity(result.size + count + 1)
Stream.generate {
val x = this.x.sample(random) * xLength
val y = this.y.sample(random) * yLength
val z = this.z.sample(random) * zLength
@ -99,13 +98,33 @@ data class EllipsoidPlacement(
.distinct()
.limit(count.toLong())
.map { it + position }
.forEach { result.add(it) }
}
override fun getPositions(
context: PlacementContext,
random: RandomSource,
position: BlockPos
): Stream<BlockPos> {
val result = ArrayList<BlockPos>()
evaluate(random, position, result)
return result.stream()
}
override fun evaluate(context: EnhancedPlacementContext, positions: List<BlockPos>): List<BlockPos> {
val result = ArrayList<BlockPos>()
positions.forEach { evaluate(context.random, it, result) }
return result
}
override val type: EnhancedPlacementModifier.Type<*>
get() = Companion
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.ELLIPSOID_PLACEMENT
}
companion object {
companion object : EnhancedPlacementModifier.Type<EllipsoidPlacement> {
val CODEC: MapCodec<EllipsoidPlacement> by lazy {
RecordCodecBuilder.mapCodec {
it.group(
@ -119,5 +138,8 @@ data class EllipsoidPlacement(
).apply(it, ::EllipsoidPlacement)
}
}
override val codec: MapCodec<EllipsoidPlacement>
get() = CODEC
}
}

View File

@ -0,0 +1,32 @@
package ru.dbotthepony.mc.otm.worldgen.placement
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
/**
* Daisy-chaining placements. Required only when using placement modifiers which operate on children list
*/
class EnhancedChainPlacement(
val children: List<EnhancedPlacementModifier>
) : EnhancedPlacementModifier {
constructor(vararg children: EnhancedPlacementModifier) : this(ImmutableList.copyOf(children))
override fun evaluate(context: EnhancedPlacementContext, positions: List<BlockPos>): List<BlockPos> {
var current = positions
children.forEach { current = it.evaluate(context, current) }
return current
}
override val type: EnhancedPlacementModifier.Type<*>
get() = Companion
companion object : EnhancedPlacementModifier.Type<EnhancedChainPlacement> {
override val codec: MapCodec<EnhancedChainPlacement> by lazy {
Codec.list(EnhancedPlacementModifier.CODEC).xmap(::EnhancedChainPlacement, EnhancedChainPlacement::children).fieldOf("children")
}
}
}

View File

@ -0,0 +1,30 @@
package ru.dbotthepony.mc.otm.worldgen.placement
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.util.valueproviders.IntProvider
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
class EnhancedCountPlacement(val provider: IntProvider) : EnhancedPlacementModifier {
override fun evaluate(context: EnhancedPlacementContext, positions: List<BlockPos>): List<BlockPos> {
val count = provider.sample(context.random)
if (count <= 0) {
return listOf()
} else if (count == 1) {
return positions
} else {
val result = ArrayList<BlockPos>()
result.ensureCapacity(positions.size * count)
for (i in 0 until count) result.addAll(positions)
return result
}
}
override val type: EnhancedPlacementModifier.Type<*>
get() = Companion
companion object : EnhancedPlacementModifier.Type<EnhancedCountPlacement> {
override val codec: MapCodec<EnhancedCountPlacement> = IntProvider.CODEC.xmap(::EnhancedCountPlacement, EnhancedCountPlacement::provider).fieldOf("count")
}
}

View File

@ -0,0 +1,40 @@
package ru.dbotthepony.mc.otm.worldgen.placement
import com.google.common.collect.ImmutableList
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
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 ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
import java.util.stream.Collectors
import java.util.stream.Stream
/**
* Or "shard" placement, if you will.
*
* Basically, allows multiple [PlacementModifier]s to split/branch off from provided point.
*/
class EnhancedSplitPlacement(
val children: List<EnhancedPlacementModifier>
) : EnhancedPlacementModifier {
constructor(vararg children: EnhancedPlacementModifier) : this(ImmutableList.copyOf(children))
override fun evaluate(context: EnhancedPlacementContext, positions: List<BlockPos>): List<BlockPos> {
val result = ArrayList<BlockPos>()
children.forEach { result.addAll(it.evaluate(context, positions)) }
return result
}
override val type: EnhancedPlacementModifier.Type<*>
get() = Companion
companion object : EnhancedPlacementModifier.Type<EnhancedSplitPlacement> {
override val codec: MapCodec<EnhancedSplitPlacement> by lazy {
Codec.list(EnhancedPlacementModifier.CODEC).xmap(::EnhancedSplitPlacement, EnhancedSplitPlacement::children).fieldOf("children")
}
}
}

View File

@ -13,6 +13,7 @@ import net.minecraft.util.valueproviders.UniformFloat
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 ru.dbotthepony.mc.otm.core.addAll
import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.math.angleDifference
import ru.dbotthepony.mc.otm.core.math.normalizeAngle
@ -22,6 +23,7 @@ import ru.dbotthepony.mc.otm.core.nextDouble
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
import java.util.stream.Stream
import kotlin.math.PI
import kotlin.math.absoluteValue
@ -43,7 +45,7 @@ class WormPlacement(
val initialAngleXY: FloatProvider = DEFAULT_INITIAL_ANGLE_XY,
val maxTravelDown: Int = Int.MAX_VALUE,
val maxTravelUp: Int = Int.MAX_VALUE,
) : PlacementModifier() {
) : PlacementModifier(), EnhancedPlacementModifier {
private inner class Worm(private val random: RandomSource, private var position: Vector) {
private var remainingDistance = length.sample(random)
private var xzRotation = random.nextDouble(-PI, PI)
@ -133,28 +135,44 @@ class WormPlacement(
}
}
override fun getPositions(context: PlacementContext, random: RandomSource, center: BlockPos): Stream<BlockPos> {
private fun evaluate(random: RandomSource, position: BlockPos, results: ArrayList<BlockPos>) {
val worms = ArrayList<Worm>()
worms.add(Worm(random, Vector.ZERO))
val positions = ArrayList<BlockPos>()
positions.add(BlockPos.ZERO)
results.add(position)
while (worms.isNotEmpty()) {
worms.removeIf {
val pos = it.follow()
if (pos != null) positions.add(pos)
if (pos != null) results.add(pos + position)
it.hasFinished
}
}
}
return positions.stream().map { it + center }
override fun evaluate(context: EnhancedPlacementContext, positions: List<BlockPos>): List<BlockPos> {
if (positions.isEmpty())
return positions
else {
val results = ArrayList<BlockPos>()
positions.forEach { evaluate(context.random, it, results) }
return results
}
}
override val type: EnhancedPlacementModifier.Type<*>
get() = Companion
override fun getPositions(context: PlacementContext, random: RandomSource, center: BlockPos): Stream<BlockPos> {
val results = ArrayList<BlockPos>()
evaluate(random, center, results)
return results.stream()
}
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.WORM_PLACEMENT
}
companion object {
companion object : EnhancedPlacementModifier.Type<WormPlacement> {
private fun increment(value: Float): Float {
var i = 1f
@ -202,19 +220,24 @@ class WormPlacement(
private const val DEGREES_TO_RADIANS = PI.toFloat() / 180f
val CODEC: MapCodec<WormPlacement> = RecordCodecBuilder.mapCodec {
it.group(
IntProvider.CODEC.fieldOf("length").forGetter(WormPlacement::length),
BooleanProvider.CODEC.fieldOf("turn_chance_xz").forGetter(WormPlacement::turnChanceXZ),
BooleanProvider.CODEC.fieldOf("turn_chance_xy").forGetter(WormPlacement::turnChanceXY),
FloatProvider.CODEC.fieldOf("turn_speed_xz").forGetter(WormPlacement::turnSpeedXZ),
FloatProvider.CODEC.fieldOf("turn_speed_xy").forGetter(WormPlacement::turnSpeedXY),
FloatProvider.CODEC.fieldOf("turn_rate_xz").forGetter(WormPlacement::turnRateXZ),
FloatProvider.CODEC.fieldOf("turn_rate_xy").forGetter(WormPlacement::turnRateXY),
FloatProvider.CODEC.optionalFieldOf("initial_angle_xy", DEFAULT_INITIAL_ANGLE_XY).forGetter(WormPlacement::initialAngleXY),
Codec.INT.minRange(1).optionalFieldOf("max_travel_down", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelDown),
Codec.INT.minRange(1).optionalFieldOf("max_travel_up", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelUp),
).apply(it, ::WormPlacement)
val CODEC: MapCodec<WormPlacement> by lazy {
RecordCodecBuilder.mapCodec {
it.group(
IntProvider.CODEC.fieldOf("length").forGetter(WormPlacement::length),
BooleanProvider.CODEC.fieldOf("turn_chance_xz").forGetter(WormPlacement::turnChanceXZ),
BooleanProvider.CODEC.fieldOf("turn_chance_xy").forGetter(WormPlacement::turnChanceXY),
FloatProvider.CODEC.fieldOf("turn_speed_xz").forGetter(WormPlacement::turnSpeedXZ),
FloatProvider.CODEC.fieldOf("turn_speed_xy").forGetter(WormPlacement::turnSpeedXY),
FloatProvider.CODEC.fieldOf("turn_rate_xz").forGetter(WormPlacement::turnRateXZ),
FloatProvider.CODEC.fieldOf("turn_rate_xy").forGetter(WormPlacement::turnRateXY),
FloatProvider.CODEC.optionalFieldOf("initial_angle_xy", DEFAULT_INITIAL_ANGLE_XY).forGetter(WormPlacement::initialAngleXY),
Codec.INT.minRange(1).optionalFieldOf("max_travel_down", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelDown),
Codec.INT.minRange(1).optionalFieldOf("max_travel_up", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelUp),
).apply(it, ::WormPlacement)
}
}
override val codec: MapCodec<WormPlacement>
get() = CODEC
}
}