diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt index df638c7d2..3d754847c 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt @@ -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 ) 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 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) { 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) { 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 diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index 04120682f..7f7f770c1 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -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) +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/EuclidMath.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/EuclidMath.kt index 77d910401..7dca62760 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/EuclidMath.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/EuclidMath.kt @@ -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) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/data/world/BooleanProvider.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/data/world/BooleanProvider.kt new file mode 100644 index 000000000..7d5d4bdc6 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/world/BooleanProvider.kt @@ -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 = 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) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MPlacementModifiers.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MPlacementModifiers.kt index 57dabc1e9..3211c2667 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MPlacementModifiers.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MPlacementModifiers.kt @@ -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 } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MWorldGenFeatures.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MWorldGenFeatures.kt index 04d3ce732..eb82e1c04 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MWorldGenFeatures.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/data/MWorldGenFeatures.kt @@ -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 } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/feature/DebugPlacerFeature.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/feature/DebugPlacerFeature.kt new file mode 100644 index 000000000..aa5e38d67 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/feature/DebugPlacerFeature.kt @@ -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(BlockState.CODEC.xmap(::Config, Config::blockState)) { + data class Config(val blockState: BlockState) : FeatureConfiguration + + override fun place(context: FeaturePlaceContext): Boolean { + return context.level().setBlock(context.origin(), context.config().blockState, Block.UPDATE_CLIENTS) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/WormPlacement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/WormPlacement.kt new file mode 100644 index 000000000..c1094c2d1 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/WormPlacement.kt @@ -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 { + 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() + 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 = 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) + } + } +}