Initial worm placement code along with debug feature

This commit is contained in:
DBotThePony 2025-03-03 06:40:29 +07:00
parent 97d30d3a19
commit f6abe0f038
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 225 additions and 0 deletions

View File

@ -9,6 +9,7 @@ import net.minecraft.tags.BlockTags
import net.minecraft.util.valueproviders.ClampedNormalFloat
import net.minecraft.util.valueproviders.ClampedNormalInt
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.util.valueproviders.UniformFloat
import net.minecraft.util.valueproviders.UniformInt
import net.minecraft.world.level.levelgen.GenerationStep
@ -22,20 +23,26 @@ import net.minecraft.world.level.levelgen.placement.HeightRangePlacement
import net.minecraft.world.level.levelgen.placement.InSquarePlacement
import net.minecraft.world.level.levelgen.placement.PlacedFeature
import net.minecraft.world.level.levelgen.placement.RarityFilter
import net.minecraft.world.level.levelgen.structure.templatesystem.AlwaysTrueTest
import net.minecraft.world.level.levelgen.structure.templatesystem.BlockStateMatchTest
import net.minecraft.world.level.levelgen.structure.templatesystem.TagMatchTest
import net.neoforged.neoforge.common.world.BiomeModifier
import net.neoforged.neoforge.registries.NeoForgeRegistries
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.data.world.OneOfFloatProvider
import ru.dbotthepony.mc.otm.worldgen.placement.StandardDeviationHeightProvider
import ru.dbotthepony.mc.otm.registry.game.MBlocks
import ru.dbotthepony.mc.otm.registry.data.MWorldGenFeatures
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature
import ru.dbotthepony.mc.otm.worldgen.feature.DebugPlacerFeature
import ru.dbotthepony.mc.otm.worldgen.placement.AbstractEnormousPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnormousEllipsoidPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement
private object ConfiguredFeatures {
val TRITANIUM_ORE = key("tritanium_ore")
val TRITANIUM_ORE_SMALL = key("tritanium_ore_small")
val DILITHIUM = key("dilithium")
val BLACK_HOLE = key("black_hole")
@ -55,6 +62,8 @@ fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>
)
context.register(ConfiguredFeatures.TRITANIUM_ORE, ConfiguredFeature(Feature.ORE, OreConfiguration(target, 9)))
//context.register(ConfiguredFeatures.TRITANIUM_ORE_SMALL, ConfiguredFeature(Feature.ORE, OreConfiguration(target, 3)))
context.register(ConfiguredFeatures.TRITANIUM_ORE_SMALL, ConfiguredFeature(MWorldGenFeatures.DEBUG_PLACEMENT, DebugPlacerFeature.Config(MBlocks.TRITANIUM_ORE.defaultBlockState())))
}
run {
@ -74,6 +83,7 @@ fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>
private object PlacedFeatures {
val NORMAL_TRITANIUM = key("normal_tritanium")
val DEEP_TRITANIUM = key("deep_tritanium")
val WORM_TRITANIUM = key("worm_tritanium")
val DILITHIUM = key("dilithium")
val BLACK_HOLE = key("black_hole")
@ -105,6 +115,27 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
HeightRangePlacement.of(VeryBiasedToBottomHeight.of(VerticalAnchor.aboveBottom(4), VerticalAnchor.absolute(0), 16))
)
))
context.register(PlacedFeatures.WORM_TRITANIUM, PlacedFeature(
configured.getOrThrow(ConfiguredFeatures.TRITANIUM_ORE_SMALL),
listOf(
WormPlacement(
parameters = AbstractEnormousPlacement.Parameters(
chunkScanRange = 12,
seedMix = 9284343575495L,
placementModifiers = listOf(
RarityFilter.onAverageOnceEvery(10),
InSquarePlacement.spread(),
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(120), 15.0)),
)
),
wormLength = UniformInt.of(40, 200),
wormTurnChance = BooleanProvider.onceEvery(4),
wormTurnXY = WormPlacement.normalDistributedTurnRate(60f),
wormTurnXZ = WormPlacement.normalDistributedTurnRate(60f),
)
)
))
}
run {
@ -172,6 +203,7 @@ fun registerBiomeModifiers(context: BootstrapContext<BiomeModifier>) {
HolderSet.direct(
placed.getOrThrow(PlacedFeatures.NORMAL_TRITANIUM),
placed.getOrThrow(PlacedFeatures.DEEP_TRITANIUM),
placed.getOrThrow(PlacedFeatures.WORM_TRITANIUM),
placed.getOrThrow(PlacedFeatures.DILITHIUM),
),
GenerationStep.Decoration.UNDERGROUND_ORES

View File

@ -678,3 +678,15 @@ fun RandomSource.nextNormalDoubles(stddev: Double, mean: Double): DoublePair {
fun RandomSource.nextNormalDouble(stddev: Double, mean: Double): Double {
return nextGaussian() * stddev + mean
}
fun RandomSource.nextFloat(min: Float, max: Float): Float {
require(max >= min) { "Min is bigger than max: $min vs $max" }
if (min == max) return min
return min + nextFloat() * (max - min)
}
fun RandomSource.nextDouble(min: Double, max: Double): Double {
require(max >= min) { "Min is bigger than max: $min vs $max" }
if (min == max) return min
return min + nextDouble() * (max - min)
}

View File

@ -436,7 +436,9 @@ operator fun Direction.times(int: Int): Vec3i = this.normal.multiply(int)
operator fun Vec3i.times(double: Double): Vector = Vector(x * double, y * double, z * double)
fun Vec3.toIntVector() = Vec3i(x.toInt(), y.toInt(), z.toInt())
fun Vec3.toBlockPos() = BlockPos(x.toInt(), y.toInt(), z.toInt())
fun Vec3.roundToIntVector() = Vec3i(x.roundToInt(), y.roundToInt(), z.roundToInt())
fun Vec3.roundToBlockPos() = BlockPos(x.roundToInt(), y.roundToInt(), z.roundToInt())
fun BlockPos.asVector(): Vector {
return Vector(x + 0.5, y + 0.5, z + 0.5)

View File

@ -0,0 +1,33 @@
package ru.dbotthepony.mc.otm.data.world
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.util.valueproviders.UniformFloat
class BooleanProvider(val sampler: FloatProvider, val threshold: Float, val lessThan: Boolean = true) {
fun sample(random: RandomSource): Boolean {
val sampled = sampler.sample(random)
if (lessThan) {
return sampled < threshold
} else {
return sampled >= threshold
}
}
companion object {
fun onceEvery(samples: Int): BooleanProvider {
return BooleanProvider(UniformFloat.of(0f, samples.toFloat()), 1f, lessThan = true)
}
val CODEC: Codec<BooleanProvider> = RecordCodecBuilder.create {
it.group(
FloatProvider.CODEC.fieldOf("sampler").forGetter(BooleanProvider::sampler),
Codec.FLOAT.fieldOf("threshold").forGetter(BooleanProvider::threshold),
Codec.BOOL.optionalFieldOf("less_than", true).forGetter(BooleanProvider::lessThan)
).apply(it, ::BooleanProvider)
}
}
}

View File

@ -6,6 +6,7 @@ import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.worldgen.placement.EllipsoidPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.EnormousEllipsoidPlacement
import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement
object MPlacementModifiers {
private val registry = MDeferredRegister(BuiltInRegistries.PLACEMENT_MODIFIER_TYPE)
@ -16,4 +17,5 @@ object MPlacementModifiers {
val ELLIPSOID_PLACEMENT by registry.register("ellipsoid") { PlacementModifierType { EllipsoidPlacement.CODEC } }
val ENORMOUS_ELLIPSOID_PLACEMENT by registry.register("enormous_ellipsoid") { PlacementModifierType { EnormousEllipsoidPlacement.CODEC } }
val WORM_PLACEMENT by registry.register("worm") { PlacementModifierType { WormPlacement.CODEC } }
}

View File

@ -2,8 +2,10 @@ package ru.dbotthepony.mc.otm.registry.data
import net.minecraft.core.registries.BuiltInRegistries
import net.neoforged.bus.api.IEventBus
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
import ru.dbotthepony.mc.otm.worldgen.feature.BlackHolePlacerFeature
import ru.dbotthepony.mc.otm.worldgen.feature.DebugPlacerFeature
object MWorldGenFeatures {
private val registry = MDeferredRegister(BuiltInRegistries.FEATURE)
@ -13,4 +15,5 @@ object MWorldGenFeatures {
}
val BLACK_HOLE_PLACER by registry.register("black_hole_placer") { BlackHolePlacerFeature }
val DEBUG_PLACEMENT by registry.register("debug") { DebugPlacerFeature }
}

View File

@ -0,0 +1,16 @@
package ru.dbotthepony.mc.otm.worldgen.feature
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.levelgen.feature.Feature
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration
import ru.dbotthepony.mc.otm.registry.game.MBlocks
object DebugPlacerFeature : Feature<DebugPlacerFeature.Config>(BlockState.CODEC.xmap(::Config, Config::blockState)) {
data class Config(val blockState: BlockState) : FeatureConfiguration
override fun place(context: FeaturePlaceContext<Config>): Boolean {
return context.level().setBlock(context.origin(), context.config().blockState, Block.UPDATE_CLIENTS)
}
}

View File

@ -0,0 +1,125 @@
package ru.dbotthepony.mc.otm.worldgen.placement
import com.mojang.serialization.MapCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.util.RandomSource
import net.minecraft.util.valueproviders.ClampedNormalFloat
import net.minecraft.util.valueproviders.ConstantFloat
import net.minecraft.util.valueproviders.FloatProvider
import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.util.valueproviders.UniformFloat
import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import ru.dbotthepony.mc.otm.core.math.Vector
import ru.dbotthepony.mc.otm.core.math.plus
import ru.dbotthepony.mc.otm.core.math.toBlockPos
import ru.dbotthepony.mc.otm.core.nextDouble
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
import ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import java.util.stream.Stream
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
class WormPlacement(
parameters: Parameters,
val wormLength: IntProvider,
val wormTurnChance: BooleanProvider,
val wormTurnXZ: FloatProvider,
val wormTurnXY: FloatProvider,
) : AbstractEnormousPlacement(parameters) {
override fun getPositions(center: BlockPos, random: RandomSource): Stream<BlockPos> {
var position = Vector.ZERO
val maxDistance = wormLength.sample(random)
// determine initial worm facing angle
var xzRotation = random.nextDouble(-PI / 2.0, PI / 2.0)
var xyRotation = random.nextDouble(-PI / 16.0, PI / 16.0)
var xzSin = sin(xzRotation)
var xzCos = cos(xzRotation)
var xySin = sin(xyRotation)
val positions = ArrayList<BlockPos>()
var prevPos = position.toBlockPos()
positions.add(prevPos)
for (traveledDistance in 0 .. maxDistance) {
// wormy turn
if (wormTurnChance.sample(random)) {
// wormy angle
// TODO: smooth turning, instead of snapping to new angle make it gradually face new angle
xzRotation += wormTurnXZ.sample(random)
xyRotation += wormTurnXY.sample(random)
xzSin = sin(xzRotation)
xzCos = cos(xzRotation)
xySin = sin(xyRotation)
}
// advance worm
position += Vector(xzCos, xySin, xzSin)
val calc = position.toBlockPos()
if (calc != prevPos) {
// if worm made a wurm, add new position to set
prevPos = calc
positions.add(calc)
}
}
return positions.stream().map { it + center }
}
override fun type(): PlacementModifierType<*> {
return MPlacementModifiers.WORM_PLACEMENT
}
companion object {
private fun increment(value: Float): Float {
var i = 1f
while (true) {
val enlarge = Float.MIN_VALUE * i
val sum = value + enlarge
if (sum != value) {
return sum
} else {
i += 1f
}
}
}
fun uniformTurnRate(degrees: Float): FloatProvider {
return uniformTurnRateRadians(degrees * DEGREES_TO_RADIANS)
}
fun uniformTurnRateRadians(rad: Float): FloatProvider {
require(rad >= 0f) { "Provided value must be non-negative, $rad given" }
if (rad == 0f) return ConstantFloat.ZERO
return UniformFloat.of(-rad, increment(rad))
}
fun normalDistributedTurnRate(deviation: Float): FloatProvider {
return normalDistributedTurnRateRadians(deviation * DEGREES_TO_RADIANS)
}
fun normalDistributedTurnRateRadians(deviation: Float): FloatProvider {
require(deviation >= 0f) { "Provided value must be non-negative, $deviation given" }
if (deviation == 0f) return ConstantFloat.ZERO
return ClampedNormalFloat.of(0f, deviation, -PI.toFloat() * 2f, PI.toFloat() * 2f)
}
private const val DEGREES_TO_RADIANS = PI.toFloat() / 180f
val CODEC: MapCodec<WormPlacement> = RecordCodecBuilder.mapCodec {
it.group(
PARAMETERS_CODEC.forGetter(WormPlacement::parameters),
IntProvider.CODEC.fieldOf("length").forGetter(WormPlacement::wormLength),
BooleanProvider.CODEC.fieldOf("turn_chance").forGetter(WormPlacement::wormTurnChance),
FloatProvider.CODEC.fieldOf("turn_xz").forGetter(WormPlacement::wormTurnXZ),
FloatProvider.CODEC.fieldOf("turn_xy").forGetter(WormPlacement::wormTurnXY),
).apply(it, ::WormPlacement)
}
}
}