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

View File

@ -4,20 +4,34 @@ import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.world.level.levelgen.placement.PlacementModifierType import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import net.neoforged.bus.api.IEventBus import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.registry.MDeferredRegister 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.ChainPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EllipsoidPlacement 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.SplitPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement
object MPlacementModifiers { object MPlacementModifiers {
private val registry = MDeferredRegister(BuiltInRegistries.PLACEMENT_MODIFIER_TYPE) private val registry = MDeferredRegister(BuiltInRegistries.PLACEMENT_MODIFIER_TYPE)
private val enhancedRegistry = MDeferredRegister(MRegistries.PLACEMENT_MODIFIER)
internal fun register(bus: IEventBus) { internal fun register(bus: IEventBus) {
registry.register(bus) registry.register(bus)
enhancedRegistry.register(bus)
} }
val ELLIPSOID_PLACEMENT by registry.register("ellipsoid") { PlacementModifierType { EllipsoidPlacement.CODEC } } val ELLIPSOID_PLACEMENT by registry.register("ellipsoid") { PlacementModifierType { EllipsoidPlacement.CODEC } }
val WORM_PLACEMENT by registry.register("worm") { PlacementModifierType { WormPlacement.CODEC } } val WORM_PLACEMENT by registry.register("worm") { PlacementModifierType { WormPlacement.CODEC } }
val SPLIT by registry.register("split") { PlacementModifierType { SplitPlacement.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)) return then(EnhancedFeature.Wrapper.configure(action))
} }
inline fun fork(configurator: Builder.() -> Unit): Builder {
configurator(this)
return this
}
private fun buildNode(): Node { private fun buildNode(): Node {
return Node(children.map { it.buildNode() }, contents) 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.minus
import ru.dbotthepony.mc.otm.core.math.plus import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
import java.util.stream.Stream import java.util.stream.Stream
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -57,22 +58,18 @@ data class EllipsoidPlacement(
* Ellipsoid size sampler on Y axis * Ellipsoid size sampler on Y axis
*/ */
val yLength: FloatProvider, val yLength: FloatProvider,
) : PlacementModifier() { ) : PlacementModifier(), EnhancedPlacementModifier {
init { init {
require(xLength.minValue >= 1f) { "Bad ellipsoid x minimal size: $xLength" } require(xLength.minValue >= 1f) { "Bad ellipsoid x minimal size: $xLength" }
require(zLength.minValue >= 1f) { "Bad ellipsoid z minimal size: $zLength" } require(zLength.minValue >= 1f) { "Bad ellipsoid z minimal size: $zLength" }
require(yLength.minValue >= 1f) { "Bad ellipsoid y minimal size: $yLength" } require(yLength.minValue >= 1f) { "Bad ellipsoid y minimal size: $yLength" }
} }
override fun getPositions( private fun evaluate(random: RandomSource, position: BlockPos, result: ArrayList<BlockPos>) {
context: PlacementContext,
random: RandomSource,
position: BlockPos
): Stream<BlockPos> {
val count = count.sample(random) val count = count.sample(random)
if (count <= 0) if (count <= 0)
return Stream.empty() return
val xLength = xLength.sample(random) val xLength = xLength.sample(random)
val zLength = zLength.sample(random) val zLength = zLength.sample(random)
@ -82,7 +79,9 @@ data class EllipsoidPlacement(
val zPow = zLength * zLength val zPow = zLength * zLength
val yPow = yLength * yLength val yPow = yLength * yLength
return Stream.generate { result.ensureCapacity(result.size + count + 1)
Stream.generate {
val x = this.x.sample(random) * xLength val x = this.x.sample(random) * xLength
val y = this.y.sample(random) * yLength val y = this.y.sample(random) * yLength
val z = this.z.sample(random) * zLength val z = this.z.sample(random) * zLength
@ -99,13 +98,33 @@ data class EllipsoidPlacement(
.distinct() .distinct()
.limit(count.toLong()) .limit(count.toLong())
.map { it + position } .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<*> { override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.ELLIPSOID_PLACEMENT return MPlacementModifiers.ELLIPSOID_PLACEMENT
} }
companion object { companion object : EnhancedPlacementModifier.Type<EllipsoidPlacement> {
val CODEC: MapCodec<EllipsoidPlacement> by lazy { val CODEC: MapCodec<EllipsoidPlacement> by lazy {
RecordCodecBuilder.mapCodec { RecordCodecBuilder.mapCodec {
it.group( it.group(
@ -119,5 +138,8 @@ data class EllipsoidPlacement(
).apply(it, ::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.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 ru.dbotthepony.mc.otm.core.addAll
import ru.dbotthepony.mc.otm.core.math.Vector import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.math.angleDifference import ru.dbotthepony.mc.otm.core.math.angleDifference
import ru.dbotthepony.mc.otm.core.math.normalizeAngle 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.codec.minRange
import ru.dbotthepony.mc.otm.data.world.BooleanProvider import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
import java.util.stream.Stream import java.util.stream.Stream
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@ -43,7 +45,7 @@ class WormPlacement(
val initialAngleXY: FloatProvider = DEFAULT_INITIAL_ANGLE_XY, val initialAngleXY: FloatProvider = DEFAULT_INITIAL_ANGLE_XY,
val maxTravelDown: Int = Int.MAX_VALUE, val maxTravelDown: Int = Int.MAX_VALUE,
val maxTravelUp: 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 inner class Worm(private val random: RandomSource, private var position: Vector) {
private var remainingDistance = length.sample(random) private var remainingDistance = length.sample(random)
private var xzRotation = random.nextDouble(-PI, PI) 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>() val worms = ArrayList<Worm>()
worms.add(Worm(random, Vector.ZERO)) worms.add(Worm(random, Vector.ZERO))
val positions = ArrayList<BlockPos>() results.add(position)
positions.add(BlockPos.ZERO)
while (worms.isNotEmpty()) { while (worms.isNotEmpty()) {
worms.removeIf { worms.removeIf {
val pos = it.follow() val pos = it.follow()
if (pos != null) positions.add(pos) if (pos != null) results.add(pos + position)
it.hasFinished 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<*> { override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.WORM_PLACEMENT return MPlacementModifiers.WORM_PLACEMENT
} }
companion object { companion object : EnhancedPlacementModifier.Type<WormPlacement> {
private fun increment(value: Float): Float { private fun increment(value: Float): Float {
var i = 1f var i = 1f
@ -202,19 +220,24 @@ class WormPlacement(
private const val DEGREES_TO_RADIANS = PI.toFloat() / 180f private const val DEGREES_TO_RADIANS = PI.toFloat() / 180f
val CODEC: MapCodec<WormPlacement> = RecordCodecBuilder.mapCodec { val CODEC: MapCodec<WormPlacement> by lazy {
it.group( RecordCodecBuilder.mapCodec {
IntProvider.CODEC.fieldOf("length").forGetter(WormPlacement::length), it.group(
BooleanProvider.CODEC.fieldOf("turn_chance_xz").forGetter(WormPlacement::turnChanceXZ), IntProvider.CODEC.fieldOf("length").forGetter(WormPlacement::length),
BooleanProvider.CODEC.fieldOf("turn_chance_xy").forGetter(WormPlacement::turnChanceXY), BooleanProvider.CODEC.fieldOf("turn_chance_xz").forGetter(WormPlacement::turnChanceXZ),
FloatProvider.CODEC.fieldOf("turn_speed_xz").forGetter(WormPlacement::turnSpeedXZ), BooleanProvider.CODEC.fieldOf("turn_chance_xy").forGetter(WormPlacement::turnChanceXY),
FloatProvider.CODEC.fieldOf("turn_speed_xy").forGetter(WormPlacement::turnSpeedXY), FloatProvider.CODEC.fieldOf("turn_speed_xz").forGetter(WormPlacement::turnSpeedXZ),
FloatProvider.CODEC.fieldOf("turn_rate_xz").forGetter(WormPlacement::turnRateXZ), FloatProvider.CODEC.fieldOf("turn_speed_xy").forGetter(WormPlacement::turnSpeedXY),
FloatProvider.CODEC.fieldOf("turn_rate_xy").forGetter(WormPlacement::turnRateXY), FloatProvider.CODEC.fieldOf("turn_rate_xz").forGetter(WormPlacement::turnRateXZ),
FloatProvider.CODEC.optionalFieldOf("initial_angle_xy", DEFAULT_INITIAL_ANGLE_XY).forGetter(WormPlacement::initialAngleXY), FloatProvider.CODEC.fieldOf("turn_rate_xy").forGetter(WormPlacement::turnRateXY),
Codec.INT.minRange(1).optionalFieldOf("max_travel_down", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelDown), FloatProvider.CODEC.optionalFieldOf("initial_angle_xy", DEFAULT_INITIAL_ANGLE_XY).forGetter(WormPlacement::initialAngleXY),
Codec.INT.minRange(1).optionalFieldOf("max_travel_up", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelUp), Codec.INT.minRange(1).optionalFieldOf("max_travel_down", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelDown),
).apply(it, ::WormPlacement) Codec.INT.minRange(1).optionalFieldOf("max_travel_up", Int.MAX_VALUE).forGetter(WormPlacement::maxTravelUp),
).apply(it, ::WormPlacement)
}
} }
override val codec: MapCodec<WormPlacement>
get() = CODEC
} }
} }