Unify placement modifier and feature under single "placement" interface, greatly simplifying and empowering things at the same time

This commit is contained in:
DBotThePony 2025-03-26 11:03:48 +07:00
parent 2e104dcbce
commit e2369b3a24
Signed by: DBot
GPG Key ID: DCC23B5715498507
14 changed files with 156 additions and 281 deletions

View File

@ -34,22 +34,20 @@ import ru.dbotthepony.mc.otm.registry.game.MBlocks
import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacedFeature
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature
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
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.EnhancedPlacement
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")
val TRITANIUM_ORE_SMALL = key("tritanium_ore_small")
val DILITHIUM = ekey("dilithium")
val DILITHIUM = key("dilithium")
val BLACK_HOLE = key("black_hole")
private fun key(name: String): ResourceKey<ConfiguredFeature<*, *>> {
@ -62,17 +60,6 @@ private object ConfiguredFeatures {
}
fun registerEnhancedConfiguredFeatures(context: BootstrapContext<EnhancedFeature.Configured<*, *>>) {
val stone = TagMatchTest(BlockTags.STONE_ORE_REPLACEABLES)
val deepslate = TagMatchTest(BlockTags.DEEPSLATE_ORE_REPLACEABLES)
run {
val target = listOf(
OreConfiguration.target(stone, MBlocks.DILITHIUM_ORE.defaultBlockState()),
OreConfiguration.target(deepslate, MBlocks.DEEPSLATE_DILITHIUM_ORE.defaultBlockState()),
)
context.register(ConfiguredFeatures.DILITHIUM, EnhancedFeature.Wrapper.configure(ConfiguredFeature(Feature.REPLACE_SINGLE_BLOCK, ReplaceBlockConfiguration(target))))
}
}
fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>>) {
@ -90,6 +77,15 @@ fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>
//context.register(ConfiguredFeatures.TRITANIUM_ORE_SMALL, ConfiguredFeature(MWorldGenFeatures.DEBUG_PLACEMENT, DebugPlacerFeature.Config(MBlocks.TRITANIUM_ORE.defaultBlockState())))
}
run {
val target = listOf(
OreConfiguration.target(stone, MBlocks.DILITHIUM_ORE.defaultBlockState()),
OreConfiguration.target(deepslate, MBlocks.DEEPSLATE_DILITHIUM_ORE.defaultBlockState()),
)
context.register(ConfiguredFeatures.DILITHIUM, ConfiguredFeature(Feature.REPLACE_SINGLE_BLOCK, ReplaceBlockConfiguration(target)))
}
context.register(ConfiguredFeatures.BLACK_HOLE, ConfiguredFeature(
MWorldGenFeatures.BLACK_HOLE_PLACER,
BlackHolePlacerFeature.Config(Decimal("0.25"), Decimal(1))))
@ -134,11 +130,16 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
context.register(
PlacedFeatures.WORM_TRITANIUM,
EnhancedPlacedFeature.Builder(RarityFilter.onAverageOnceEvery(140))
.then(InSquarePlacement.spread())
.then(HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(-40), 15.0)))
.then(
EnhancedPlacedFeature.configure(
chunkScanRange = 24,
seedMix = 9284343575495L,
root = EnhancedChainPlacement(
RarityFilter.onAverageOnceEvery(140).wrap(),
InSquarePlacement.spread().wrap(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(-40), 15.0)).wrap(),
EnhancedSplitPlacement(
EnhancedSplitPlacement.Mode.COMBINE,
// "heart"
EllipsoidPlacement(
count = UniformInt.of(600, 900),
@ -174,18 +175,16 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
z = ClampedNormalFloat.of(0f, 0.2f, -1f, 1f),
)
)
)
)
.then(configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE_SMALL))
.build(
chunkScanRange = 24,
seedMix = 9284343575495L,
),
configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE_SMALL).placement()
)
)
)
}
run {
val ore = econfigured.getOrThrow(ConfiguredFeatures.DILITHIUM)
val ore = configured.getOrThrow(ConfiguredFeatures.DILITHIUM)
val ringularity = OneOfFloatProvider.of(
ClampedNormalFloat.of(0.4f, 0.2f, -2f, 2f),
@ -194,10 +193,13 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
context.register(
PlacedFeatures.DILITHIUM,
EnhancedPlacedFeature.Builder(RarityFilter.onAverageOnceEvery(120))
.then(InSquarePlacement.spread())
.then(HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)))
.then(
EnhancedPlacedFeature.configure(
chunkScanRange = 6,
seedMix = 237483209523709234L,
root = EnhancedChainPlacement(
RarityFilter.onAverageOnceEvery(120).wrap(),
InSquarePlacement.spread().wrap(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)).wrap(),
EllipsoidPlacement(
x = ringularity,
y = ringularity,
@ -206,13 +208,10 @@ 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(
chunkScanRange = 6,
seedMix = 237483209523709234L,
),
ore.placement()
)
)
)
}

View File

@ -6,10 +6,8 @@ import net.neoforged.bus.api.EventPriority
import net.neoforged.fml.common.Mod
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent
import ru.dbotthepony.mc.otm.player.android.AndroidResearchDescription
import ru.dbotthepony.mc.otm.player.android.AndroidResearchDescriptions
import ru.dbotthepony.mc.otm.player.android.AndroidResearchManager
import ru.dbotthepony.mc.otm.player.android.AndroidResearchResult
import ru.dbotthepony.mc.otm.player.android.AndroidResearchResults
import ru.dbotthepony.mc.otm.player.android.feature.EnderTeleporterFeature
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
@ -93,7 +91,7 @@ import ru.dbotthepony.mc.otm.server.MCommands
import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.triggers.KillAsAndroidTrigger
import ru.dbotthepony.mc.otm.worldgen.feature.EnhancedFeature
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacementModifier
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacement
import thedarkcolour.kotlinforforge.neoforge.forge.DIST
import thedarkcolour.kotlinforforge.neoforge.forge.FORGE_BUS
import thedarkcolour.kotlinforforge.neoforge.forge.LOADING_CONTEXT
@ -142,7 +140,7 @@ object OverdriveThatMatters {
DecimalProvider.register(MOD_BUS)
BooleanProvider.register(MOD_BUS)
EnhancedFeature.register(MOD_BUS)
EnhancedPlacementModifier.register(MOD_BUS)
EnhancedPlacement.register(MOD_BUS)
AndroidResearchDescriptions.register(MOD_BUS)
AndroidResearchResults.register(MOD_BUS)

View File

@ -13,9 +13,8 @@ import ru.dbotthepony.mc.otm.player.android.AndroidFeatureType
import ru.dbotthepony.mc.otm.player.android.AndroidResearchDescription
import ru.dbotthepony.mc.otm.player.android.AndroidResearchResult
import ru.dbotthepony.mc.otm.storage.StorageStack
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacedFeature
import ru.dbotthepony.mc.otm.worldgen.feature.EnhancedFeature
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacementModifier
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacement
object MRegistries {
private fun <T> k(name: String): ResourceKey<Registry<T>> {
@ -33,5 +32,5 @@ object MRegistries {
val BOOLEAN_PROVIDER = k<BooleanProvider.Type<*>>("boolean_provider")
val FEATURE = k<EnhancedFeature<*>>("feature")
val CONFIGURED_FEATURE = k<EnhancedFeature.Configured<*, *>>("configured_feature")
val PLACEMENT_MODIFIER = k<EnhancedPlacementModifier.Type<*>>("placement_modifier")
val PLACEMENT_MODIFIER = k<EnhancedPlacement.Type<*>>("placement_modifier")
}

View File

@ -5,6 +5,7 @@ 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.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
@ -28,6 +29,7 @@ object MPlacementModifiers {
val CHAIN by registry.register("chain") { PlacementModifierType { ChainPlacement.CODEC } }
init {
enhancedRegistry.register("feature") { EnhancedFeature.ConfiguredWrapper.Companion }
enhancedRegistry.register("ellipsoid") { EllipsoidPlacement.Companion }
enhancedRegistry.register("worm") { WormPlacement.Companion }
enhancedRegistry.register("split") { EnhancedSplitPlacement.Companion }

View File

@ -10,7 +10,6 @@ import com.mojang.serialization.DataResult
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.codecs.RecordCodecBuilder
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet
import net.minecraft.Util
import net.minecraft.core.BlockPos
@ -26,122 +25,38 @@ import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfigur
import net.minecraft.world.level.levelgen.placement.PlacedFeature
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import ru.dbotthepony.kommons.util.XXHash64
import ru.dbotthepony.mc.otm.core.collect.BlockPosSet
import ru.dbotthepony.mc.otm.core.collect.Vec3iHashStrategy
import ru.dbotthepony.mc.otm.core.util.GJRAND64RandomSource
import ru.dbotthepony.mc.otm.data.codec.minRange
import ru.dbotthepony.mc.otm.worldgen.feature.EnhancedFeature
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacementModifier
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacement
import java.io.DataOutputStream
import java.time.Duration
import java.util.LinkedList
import kotlin.math.sqrt
private object NodeCodec : Codec<EnhancedPlacedFeature.Node> {
private val list = Codec.list(this)
override fun <T : Any?> encode(input: EnhancedPlacedFeature.Node, ops: DynamicOps<T>, prefix: T): DataResult<T> {
val result = HashMap<T, T>()
if (input.children.isNotEmpty()) {
val re = list.encodeStart(ops, input.children)
if (re.isError)
return re
result[ops.createString("children")] = re.resultOrPartial().get()
}
if (input.contents.left().isPresent) {
val re = EnhancedPlacementModifier.CODEC.encodeStart(ops, input.contents.left().get())
if (re.isError)
return DataResult.error { "Failed to serialize placement modifier: ${re.error().get().message()}" }
result[ops.createString("modifier")] = re.resultOrPartial().get()
} else {
val re = EnhancedFeature.CODEC.encodeStart(ops, input.contents.right().get())
if (re.isError)
return DataResult.error { "Failed to serialize feature: ${re.error().get().message()}" }
result[ops.createString("feature")] = re.resultOrPartial().get()
}
return ops.mergeToMap(prefix, result)
}
override fun <T : Any?> decode(ops: DynamicOps<T>, input: T): DataResult<Pair<EnhancedPlacedFeature.Node, T>> {
return ops.getMap(input).flatMap {
val children = it.get("children")
val modifier = it.get("modifier")
val feature = it.get("feature")
if (modifier != null && feature != null) {
return@flatMap DataResult.error { "Enhanced feature placement node should have either modifier or feature, but both are present" }
} else if (modifier == null && feature == null) {
return@flatMap DataResult.error { "Enhanced feature placement node contains no modifier and no feature" }
}
val deserializeChildren: List<EnhancedPlacedFeature.Node>
if (children != null) {
val result = list.decode(ops, children)
if (result.isError)
return@flatMap DataResult.error { result.error().get().message() }
deserializeChildren = result.map { it.first }.getOrThrow()
} else {
deserializeChildren = emptyList()
}
if (modifier != null) {
return@flatMap EnhancedPlacementModifier.CODEC.decode(ops, modifier).map { Pair(EnhancedPlacedFeature.Node(deserializeChildren, Either.left(it.first)), ops.empty()) }
} else {
return@flatMap EnhancedFeature.CODEC.decode(ops, feature).map { Pair(EnhancedPlacedFeature.Node(deserializeChildren, Either.right(it.first)), ops.empty()) }
}
}
}
}
object EnhancedPlacedFeature : Feature<EnhancedPlacedFeature.Config>(
RecordCodecBuilder.create {
it.group(
Codec.INT.minRange(0).fieldOf("chunk_scan_range").forGetter(Config::chunkScanRange),
Codec.LONG.fieldOf("seed_mix").forGetter(Config::seedMix),
NodeCodec.fieldOf("root").forGetter(Config::root),
EnhancedPlacement.CODEC.fieldOf("root").forGetter(Config::root),
).apply(it, ::Config)
}
) {
class Node(val children: List<Node>, val contents: Either<EnhancedPlacementModifier, Holder<EnhancedFeature.Configured<*, *>>>){
fun evaluate(context: EnhancedPlacementContext) {
evaluate(context, listOf(PlacementPos(BlockPos(context.origin.minBlockX, 0, context.origin.minBlockZ), PlacementVariableMap())))
}
private fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>) {
val actualPositions = if (contents.left().isPresent) {
contents.left().get().evaluate(context, positions)
} else {
positions
}
if (contents.right().isPresent) {
context.place(actualPositions, contents.right().get().value())
}
children.forEach {
it.evaluate(context.push(), actualPositions)
}
}
}
private val emptyVariableMap = PlacementVariableMap()
fun configure(
chunkScanRange: Int,
seedMix: Long,
root: EnhancedPlacement
): PlacedFeature {
return PlacedFeature(Holder.direct(ConfiguredFeature(EnhancedPlacedFeature, Config(chunkScanRange, seedMix, root))), listOf())
}
class Config(
val chunkScanRange: Int,
val seedMix: Long,
val root: Node,
val root: EnhancedPlacement,
) : FeatureConfiguration {
private class GeneratedChunk : EnhancedPlacementContext.Placer {
private data class Placement(val context: EnhancedPlacementContext, val positions: ObjectRBTreeSet<PlacementPos>, val feature: EnhancedFeature.Configured<*, *>)
@ -215,7 +130,7 @@ object EnhancedPlacedFeature : Feature<EnhancedPlacedFeature.Config>(
val chunk = GeneratedChunk()
val enhancedContext = EnhancedPlacementContext(context.level(), context.chunkGenerator(), random, chunkPos, chunk)
root.evaluate(enhancedContext)
root.evaluate(enhancedContext, listOf(PlacementPos(BlockPos(chunkPos.minBlockX, 0, chunkPos.minBlockZ), PlacementVariableMap())))
return chunk
}
@ -245,91 +160,4 @@ object EnhancedPlacedFeature : Feature<EnhancedPlacedFeature.Config>(
override fun place(context: FeaturePlaceContext<Config>): Boolean {
return context.config().place(context)
}
class Builder {
private val root: Builder?
private val children = ArrayList<Builder>()
private val contents: Either<EnhancedPlacementModifier, Holder<EnhancedFeature.Configured<*, *>>>
constructor() : this(EnhancedPlacementModifier.Passthrough)
constructor(root: EnhancedPlacementModifier) {
this.root = null
this.contents = Either.left(root)
}
constructor(root: PlacementModifier) : this(EnhancedPlacementModifier.Wrapper(root))
constructor(root: Holder<EnhancedFeature.Configured<*, *>>) {
this.root = null
this.contents = Either.right(root)
}
constructor(root: EnhancedFeature.Configured<*, *>) {
this.root = null
this.contents = Either.right(Holder.direct(root))
}
private constructor(parent: Builder, root: PlacementModifier) : this(parent, EnhancedPlacementModifier.Wrapper(root))
private constructor(parent: Builder, root: EnhancedPlacementModifier) {
this.root = parent
this.contents = Either.left(root)
}
private constructor(parent: Builder, root: Holder<EnhancedFeature.Configured<*, *>>) {
this.root = parent
this.contents = Either.right(root)
}
private constructor(parent: Builder, root: EnhancedFeature.Configured<*, *>) {
this.root = parent
this.contents = Either.right(Holder.direct(root))
}
fun then(action: PlacementModifier): Builder {
return Builder(root ?: this, action).also(children::add)
}
fun then(action: EnhancedPlacementModifier): Builder {
return Builder(root ?: this, action).also(children::add)
}
fun then(action: EnhancedFeature.Configured<*, *>): Builder {
return Builder(root ?: this, action).also(children::add)
}
fun then(action: Holder<EnhancedFeature.Configured<*, *>>): Builder {
return Builder(root ?: this, action).also(children::add)
}
@JvmName("thenVanilla")
fun then(action: ConfiguredFeature<*, *>): Builder {
return then(EnhancedFeature.Wrapper.configure(action))
}
@JvmName("thenVanilla")
fun then(action: Holder<ConfiguredFeature<*, *>>): Builder {
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)
}
fun build(
chunkScanRange: Int,
seedMix: Long,
): PlacedFeature {
if (root != null)
return root.build(chunkScanRange, seedMix)
return PlacedFeature(Holder.direct(ConfiguredFeature(EnhancedPlacedFeature, Config(chunkScanRange, seedMix, buildNode()))), listOf())
}
}
}

View File

@ -1,6 +1,5 @@
package ru.dbotthepony.mc.otm.worldgen
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.world.level.ChunkPos
@ -9,10 +8,8 @@ import net.minecraft.world.level.chunk.ChunkGenerator
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration
import net.minecraft.world.level.levelgen.placement.PlacementContext
import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.mc.otm.worldgen.feature.EnhancedFeature
import java.util.*
import kotlin.collections.HashMap
class EnhancedPlacementContext {
fun interface Placer {

View File

@ -1,12 +1,15 @@
package ru.dbotthepony.mc.otm.worldgen
import net.minecraft.core.BlockPos
import net.minecraft.core.Holder
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import ru.dbotthepony.mc.otm.core.collect.Vec3iHashStrategy
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacementModifier
import ru.dbotthepony.mc.otm.worldgen.feature.EnhancedFeature
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacement
fun PlacementModifier.wrap(): EnhancedPlacementModifier {
return EnhancedPlacementModifier.Wrapper(this)
fun PlacementModifier.wrap(): EnhancedPlacement {
return EnhancedPlacement.Wrapper(this)
}
data class PlacementPos(val pos: BlockPos, val variables: PlacementVariableMap) : Comparable<PlacementPos> {
@ -14,3 +17,9 @@ data class PlacementPos(val pos: BlockPos, val variables: PlacementVariableMap)
return Vec3iHashStrategy.compare(pos, other.pos)
}
}
@JvmName("placement")
fun Holder<EnhancedFeature.Configured<*, *>>.placement() = EnhancedFeature.ConfiguredWrapper(this)
@JvmName("vanillaPlacement")
fun Holder<ConfiguredFeature<*, *>>.placement() = EnhancedFeature.Wrapper.configure(this)
fun ConfiguredFeature<*, *>.placement() = EnhancedFeature.Wrapper.configure(this)

View File

@ -13,6 +13,7 @@ import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.MRegistries
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
import ru.dbotthepony.mc.otm.worldgen.PlacementPos
import ru.dbotthepony.mc.otm.worldgen.placement.EnhancedPlacement
abstract class EnhancedFeature<FC>(codec: Codec<FC>) {
abstract fun place(context: EnhancedPlacementContext, config: FC, positions: Set<PlacementPos>, allPositions: Set<PlacementPos>): Boolean
@ -21,10 +22,30 @@ abstract class EnhancedFeature<FC>(codec: Codec<FC>) {
fun place(context: EnhancedPlacementContext, positions: Set<PlacementPos>, allPositions: Set<PlacementPos>): Boolean {
return feature.place(context, config, positions, allPositions)
}
fun placement(): ConfiguredWrapper {
return ConfiguredWrapper(Holder.direct(this))
}
}
data class ConfiguredWrapper(val configured: Holder<Configured<*, *>>) : EnhancedPlacement {
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
context.place(positions, configured.value())
return positions
}
override val type: EnhancedPlacement.Type<*>
get() = Companion
companion object : EnhancedPlacement.Type<ConfiguredWrapper> {
override val codec: MapCodec<ConfiguredWrapper> by lazy {
CODEC.xmap(::ConfiguredWrapper, ConfiguredWrapper::configured).fieldOf("feature")
}
}
}
val codec: MapCodec<Configured<*, FC>> = codec.fieldOf("config").xmap({ Configured(this, it) }, { it.config })
fun configure(config: FC) = Configured(this, config)
fun configure(config: FC) = Configured(this, config).placement()
object Wrapper : EnhancedFeature<Holder<ConfiguredFeature<*, *>>>(ConfiguredFeature.CODEC) {
override fun place(

View File

@ -14,7 +14,6 @@ import ru.dbotthepony.mc.otm.core.collect.Vec3iHashStrategy
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
import ru.dbotthepony.mc.otm.worldgen.PlacementPos
import ru.dbotthepony.mc.otm.worldgen.PlacementVariableMap
import java.util.stream.Stream
/**
@ -55,7 +54,7 @@ data class EllipsoidPlacement(
* Ellipsoid size sampler on Y axis
*/
val yLength: FloatProvider,
) : PlacementModifier(), EnhancedPlacementModifier {
) : PlacementModifier(), EnhancedPlacement {
init {
require(xLength.minValue >= 1f) { "Bad ellipsoid x minimal size: $xLength" }
require(zLength.minValue >= 1f) { "Bad ellipsoid z minimal size: $zLength" }
@ -113,14 +112,14 @@ data class EllipsoidPlacement(
return result
}
override val type: EnhancedPlacementModifier.Type<*>
override val type: EnhancedPlacement.Type<*>
get() = Companion
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.ELLIPSOID_PLACEMENT
}
companion object : EnhancedPlacementModifier.Type<EllipsoidPlacement> {
companion object : EnhancedPlacement.Type<EllipsoidPlacement> {
val CODEC: MapCodec<EllipsoidPlacement> by lazy {
RecordCodecBuilder.mapCodec {
it.group(

View File

@ -3,31 +3,37 @@ 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
import ru.dbotthepony.mc.otm.worldgen.PlacementPos
/**
* Daisy-chaining placements. Required only when using placement modifiers which operate on children list
* Chains placements, feeding results from one placement into next
*
* Each placement gets its own, new [EnhancedPlacementContext], so variables can be pushed safely into it
*/
class EnhancedChainPlacement(
val children: List<EnhancedPlacementModifier>
) : EnhancedPlacementModifier {
constructor(vararg children: EnhancedPlacementModifier) : this(ImmutableList.copyOf(children))
val children: List<EnhancedPlacement>
) : EnhancedPlacement {
constructor(vararg children: EnhancedPlacement) : this(ImmutableList.copyOf(children))
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
var currentContext = context
var current = positions
children.forEach { current = it.evaluate(context, current) }
children.forEach {
current = it.evaluate(context, current)
currentContext = currentContext.push()
}
return current
}
override val type: EnhancedPlacementModifier.Type<*>
override val type: EnhancedPlacement.Type<*>
get() = Companion
companion object : EnhancedPlacementModifier.Type<EnhancedChainPlacement> {
companion object : EnhancedPlacement.Type<EnhancedChainPlacement> {
override val codec: MapCodec<EnhancedChainPlacement> by lazy {
Codec.list(EnhancedPlacementModifier.CODEC).xmap(::EnhancedChainPlacement, EnhancedChainPlacement::children).fieldOf("children")
Codec.list(EnhancedPlacement.CODEC).xmap(::EnhancedChainPlacement, EnhancedChainPlacement::children).fieldOf("children")
}
}
}

View File

@ -5,7 +5,7 @@ import net.minecraft.util.valueproviders.IntProvider
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
import ru.dbotthepony.mc.otm.worldgen.PlacementPos
class EnhancedCountPlacement(val provider: IntProvider) : EnhancedPlacementModifier {
class EnhancedCountPlacement(val provider: IntProvider) : EnhancedPlacement {
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
val count = provider.sample(context.random)
@ -21,10 +21,10 @@ class EnhancedCountPlacement(val provider: IntProvider) : EnhancedPlacementModif
}
}
override val type: EnhancedPlacementModifier.Type<*>
override val type: EnhancedPlacement.Type<*>
get() = Companion
companion object : EnhancedPlacementModifier.Type<EnhancedCountPlacement> {
companion object : EnhancedPlacement.Type<EnhancedCountPlacement> {
override val codec: MapCodec<EnhancedCountPlacement> = IntProvider.CODEC.xmap(::EnhancedCountPlacement, EnhancedCountPlacement::provider).fieldOf("count")
}
}

View File

@ -2,8 +2,6 @@ package ru.dbotthepony.mc.otm.worldgen.placement
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.world.level.levelgen.placement.PlacementContext
import net.minecraft.world.level.levelgen.placement.PlacementModifier
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.registry.MBuiltInRegistries
@ -11,18 +9,17 @@ import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.registry.MRegistries
import ru.dbotthepony.mc.otm.worldgen.EnhancedPlacementContext
import ru.dbotthepony.mc.otm.worldgen.PlacementPos
import ru.dbotthepony.mc.otm.worldgen.PlacementVariableMap
import java.util.stream.Collectors
interface EnhancedPlacementModifier {
interface Type<T : EnhancedPlacementModifier> {
interface EnhancedPlacement {
interface Type<T : EnhancedPlacement> {
val codec: MapCodec<T>
}
fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos>
val type: Type<*>
class Wrapper(val parent: PlacementModifier) : EnhancedPlacementModifier {
class Wrapper(val parent: PlacementModifier) : EnhancedPlacement {
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
return positions.stream()
.flatMap { (pos, vars) -> parent.getPositions(context.vanillaContext, context.random, pos).map { PlacementPos(it, vars) } }
@ -37,7 +34,7 @@ interface EnhancedPlacementModifier {
}
}
object Passthrough : EnhancedPlacementModifier, Type<Passthrough> {
object Passthrough : EnhancedPlacement, Type<Passthrough> {
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
return positions
}
@ -56,7 +53,7 @@ interface EnhancedPlacementModifier {
registrar.register("passthrough") { Passthrough }
}
val CODEC: Codec<EnhancedPlacementModifier> by lazy {
val CODEC: Codec<EnhancedPlacement> by lazy {
MBuiltInRegistries.PLACEMENT_MODIFIER.byNameCodec().dispatch({ it.type }, { it.codec })
}

View File

@ -3,16 +3,11 @@ 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 com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.util.StringRepresentable
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 ru.dbotthepony.mc.otm.worldgen.PlacementPos
import java.util.stream.Collectors
import java.util.stream.Stream
/**
* Or "shard" placement, if you will.
@ -20,22 +15,48 @@ import java.util.stream.Stream
* 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))
val mode: Mode,
val children: List<EnhancedPlacement>,
) : EnhancedPlacement {
constructor(mode: Mode, vararg children: EnhancedPlacement) : this(mode, ImmutableList.copyOf(children))
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
val result = ArrayList<PlacementPos>()
children.forEach { result.addAll(it.evaluate(context, positions)) }
return result
enum class Mode : StringRepresentable {
COMBINE,
FORK;
private val lname = name.lowercase()
override fun getSerializedName(): String {
return lname
}
companion object {
val CODEC: Codec<Mode> = StringRepresentable.fromEnum(::values)
}
}
override val type: EnhancedPlacementModifier.Type<*>
override fun evaluate(context: EnhancedPlacementContext, positions: List<PlacementPos>): List<PlacementPos> {
if (mode == Mode.COMBINE) {
val result = ArrayList<PlacementPos>()
children.forEach { result.addAll(it.evaluate(context.push(), positions)) }
return result
} else {
children.forEach { it.evaluate(context.push(), positions) }
return positions
}
}
override val type: EnhancedPlacement.Type<*>
get() = Companion
companion object : EnhancedPlacementModifier.Type<EnhancedSplitPlacement> {
companion object : EnhancedPlacement.Type<EnhancedSplitPlacement> {
override val codec: MapCodec<EnhancedSplitPlacement> by lazy {
Codec.list(EnhancedPlacementModifier.CODEC).xmap(::EnhancedSplitPlacement, EnhancedSplitPlacement::children).fieldOf("children")
RecordCodecBuilder.mapCodec {
it.group(
Mode.CODEC.fieldOf("mode").forGetter(EnhancedSplitPlacement::mode),
Codec.list(EnhancedPlacement.CODEC).fieldOf("children").forGetter(EnhancedSplitPlacement::children),
).apply(it, ::EnhancedSplitPlacement)
}
}
}
}

View File

@ -13,7 +13,6 @@ 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
@ -46,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(), EnhancedPlacementModifier {
) : PlacementModifier(), EnhancedPlacement {
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)
@ -165,7 +164,7 @@ class WormPlacement(
}
}
override val type: EnhancedPlacementModifier.Type<*>
override val type: EnhancedPlacement.Type<*>
get() = Companion
override fun getPositions(context: PlacementContext, random: RandomSource, center: BlockPos): Stream<BlockPos> {
@ -176,7 +175,7 @@ class WormPlacement(
return MPlacementModifiers.WORM_PLACEMENT
}
companion object : EnhancedPlacementModifier.Type<WormPlacement> {
companion object : EnhancedPlacement.Type<WormPlacement> {
private fun increment(value: Float): Float {
var i = 1f