Compare commits
21 Commits
70ac0f2c7f
...
e179756995
Author | SHA1 | Date | |
---|---|---|---|
e179756995 | |||
df949a6861 | |||
88c3fe24b0 | |||
25a312b56f | |||
be97560f01 | |||
85aecaf79b | |||
65d076f634 | |||
d06c32aec2 | |||
3ea9a7f386 | |||
7251760800 | |||
3bcfd55154 | |||
1ca2348adf | |||
430bc70c7e | |||
8b0edc5d5f | |||
19e405d4f4 | |||
e1d3b36dab | |||
322d89f2a2 | |||
157d2c5498 | |||
ebdff6811d | |||
01054a05d2 | |||
a89371007f |
@ -22,7 +22,7 @@ mixin_version=0.8.5
|
||||
neogradle.subsystems.parchment.minecraftVersion=1.21.1
|
||||
neogradle.subsystems.parchment.mappingsVersion=2024.11.17
|
||||
|
||||
kommons_version=3.1.3
|
||||
kommons_version=3.3.2
|
||||
caffeine_cache_version=3.1.5
|
||||
|
||||
jei_version=19.16.4.171
|
||||
|
@ -7,9 +7,8 @@ import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.tags.BiomeTags
|
||||
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.ConstantInt
|
||||
import net.minecraft.util.valueproviders.UniformFloat
|
||||
import net.minecraft.util.valueproviders.UniformInt
|
||||
import net.minecraft.world.level.levelgen.GenerationStep
|
||||
@ -17,14 +16,13 @@ import net.minecraft.world.level.levelgen.VerticalAnchor
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature
|
||||
import net.minecraft.world.level.levelgen.feature.Feature
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.OreConfiguration
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.ReplaceBlockConfiguration
|
||||
import net.minecraft.world.level.levelgen.heightproviders.VeryBiasedToBottomHeight
|
||||
import net.minecraft.world.level.levelgen.placement.CountPlacement
|
||||
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
|
||||
@ -37,6 +35,7 @@ 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.EllipsoidPlacement
|
||||
import ru.dbotthepony.mc.otm.worldgen.placement.EnormousEllipsoidPlacement
|
||||
import ru.dbotthepony.mc.otm.worldgen.placement.WormPlacement
|
||||
|
||||
@ -62,8 +61,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())))
|
||||
context.register(ConfiguredFeatures.TRITANIUM_ORE_SMALL, ConfiguredFeature(Feature.REPLACE_SINGLE_BLOCK, ReplaceBlockConfiguration(target)))
|
||||
//context.register(ConfiguredFeatures.TRITANIUM_ORE_SMALL, ConfiguredFeature(MWorldGenFeatures.DEBUG_PLACEMENT, DebugPlacerFeature.Config(MBlocks.TRITANIUM_ORE.defaultBlockState())))
|
||||
}
|
||||
|
||||
run {
|
||||
@ -72,7 +71,7 @@ fun registerConfiguredFeatures(context: BootstrapContext<ConfiguredFeature<*, *>
|
||||
OreConfiguration.target(deepslate, MBlocks.DEEPSLATE_DILITHIUM_ORE.defaultBlockState()),
|
||||
)
|
||||
|
||||
context.register(ConfiguredFeatures.DILITHIUM, ConfiguredFeature(Feature.ORE, OreConfiguration(target, 3)))
|
||||
context.register(ConfiguredFeatures.DILITHIUM, ConfiguredFeature(Feature.REPLACE_SINGLE_BLOCK, ReplaceBlockConfiguration(target)))
|
||||
}
|
||||
|
||||
context.register(ConfiguredFeatures.BLACK_HOLE, ConfiguredFeature(
|
||||
@ -121,18 +120,34 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
|
||||
listOf(
|
||||
WormPlacement(
|
||||
parameters = AbstractEnormousPlacement.Parameters(
|
||||
chunkScanRange = 12,
|
||||
chunkScanRange = 24,
|
||||
seedMix = 9284343575495L,
|
||||
placementModifiers = listOf(
|
||||
RarityFilter.onAverageOnceEvery(10),
|
||||
prePlacementModifiers = listOf(
|
||||
RarityFilter.onAverageOnceEvery(240),
|
||||
InSquarePlacement.spread(),
|
||||
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(120), 15.0)),
|
||||
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(-40), 15.0)),
|
||||
),
|
||||
postPlacementModifiers = listOf(
|
||||
EllipsoidPlacement(
|
||||
count = UniformInt.of(15, 40),
|
||||
xLength = ConstantFloat.of(14f),
|
||||
yLength = ConstantFloat.of(14f),
|
||||
zLength = ConstantFloat.of(14f),
|
||||
x = ClampedNormalFloat.of(0f, 0.4f, -1f, 1f),
|
||||
y = ClampedNormalFloat.of(0f, 0.4f, -1f, 1f),
|
||||
z = ClampedNormalFloat.of(0f, 0.4f, -1f, 1f),
|
||||
)
|
||||
)
|
||||
),
|
||||
wormLength = UniformInt.of(40, 200),
|
||||
wormTurnChance = BooleanProvider.onceEvery(4),
|
||||
wormTurnXY = WormPlacement.normalDistributedTurnRate(60f),
|
||||
wormTurnXZ = WormPlacement.normalDistributedTurnRate(60f),
|
||||
length = UniformInt.of(120, 400),
|
||||
turnRateXY = WormPlacement.normalDistributedTurnRate(10f),
|
||||
turnRateXZ = WormPlacement.normalDistributedTurnRate(60f),
|
||||
turnSpeedXZ = WormPlacement.constantTurnRate(10f),
|
||||
turnSpeedXY = WormPlacement.constantTurnRate(4f),
|
||||
turnChanceXY = BooleanProvider.Unbiased(6),
|
||||
turnChanceXZ = BooleanProvider.BiasedLinear(0.6f, 5),
|
||||
maxTravelUp = 16,
|
||||
maxTravelDown = 16,
|
||||
)
|
||||
)
|
||||
))
|
||||
@ -153,7 +168,7 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
|
||||
parameters = AbstractEnormousPlacement.Parameters(
|
||||
chunkScanRange = 5,
|
||||
seedMix = 237483209523709234L,
|
||||
placementModifiers = listOf(
|
||||
prePlacementModifiers = listOf(
|
||||
RarityFilter.onAverageOnceEvery(120),
|
||||
InSquarePlacement.spread(),
|
||||
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(0), 15.0)),
|
||||
|
@ -5,11 +5,11 @@ import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import ru.dbotthepony.mc.otm.core.IMatteryLevel;
|
||||
import ru.dbotthepony.mc.otm.core.util.Xoshiro256SSRandom;
|
||||
import ru.dbotthepony.mc.otm.core.util.PCG32RandomSource;
|
||||
|
||||
@Mixin(Level.class)
|
||||
public abstract class LevelMixin implements IMatteryLevel {
|
||||
public final RandomSource otm_random = new Xoshiro256SSRandom();
|
||||
public final RandomSource otm_random = new PCG32RandomSource();
|
||||
|
||||
@Override
|
||||
public @NotNull RandomSource getOtmRandom() {
|
||||
|
@ -53,6 +53,7 @@ import ru.dbotthepony.mc.otm.config.MachinesConfig
|
||||
import ru.dbotthepony.mc.otm.config.ServerConfig
|
||||
import ru.dbotthepony.mc.otm.config.ToolsConfig
|
||||
import ru.dbotthepony.mc.otm.data.FlywheelMaterials
|
||||
import ru.dbotthepony.mc.otm.data.world.BooleanProvider
|
||||
import ru.dbotthepony.mc.otm.data.world.DecimalProvider
|
||||
import ru.dbotthepony.mc.otm.entity.WitheredSkeletonSpawnHandler
|
||||
import ru.dbotthepony.mc.otm.item.ChestUpgraderItem
|
||||
@ -136,6 +137,7 @@ object OverdriveThatMatters {
|
||||
MOD_BUS.addListener(::registerNetworkPackets)
|
||||
|
||||
DecimalProvider.register(MOD_BUS)
|
||||
BooleanProvider.register(MOD_BUS)
|
||||
AndroidResearchDescription.register(MOD_BUS)
|
||||
AndroidResearchDescriptions.register(MOD_BUS)
|
||||
AndroidResearchResult.register(MOD_BUS)
|
||||
|
@ -660,12 +660,18 @@ fun RandomSource.nextNormalDouble(stddev: Double, mean: Double): Double {
|
||||
}
|
||||
|
||||
fun RandomSource.nextFloat(min: Float, max: Float): Float {
|
||||
if (this is RandomGenerator)
|
||||
return nextFloat(min, max)
|
||||
|
||||
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 {
|
||||
if (this is RandomGenerator)
|
||||
return nextDouble(min, max)
|
||||
|
||||
require(max >= min) { "Min is bigger than max: $min vs $max" }
|
||||
if (min == max) return min
|
||||
return min + nextDouble() * (max - min)
|
||||
|
@ -240,18 +240,31 @@ sealed class Decimal : Number(), Comparable<Decimal> {
|
||||
return mag.signum()
|
||||
}
|
||||
|
||||
private var intCache = 0
|
||||
private var intComputed = false
|
||||
|
||||
override fun toInt(): Int {
|
||||
return if (whole > BI_INT_MAX) {
|
||||
if (!intComputed) {
|
||||
intCache = if (whole > BI_INT_MAX) {
|
||||
Int.MAX_VALUE
|
||||
} else if (whole < BI_INT_MIN) {
|
||||
Int.MIN_VALUE
|
||||
} else {
|
||||
whole.toInt()
|
||||
}
|
||||
|
||||
intComputed = true
|
||||
}
|
||||
|
||||
return intCache
|
||||
}
|
||||
|
||||
private var longCache = 0L
|
||||
private var longComputed = false
|
||||
|
||||
override fun toLong(): Long {
|
||||
return if (whole > BI_LONG_MAX) {
|
||||
if (!longComputed) {
|
||||
longCache = if (whole > BI_LONG_MAX) {
|
||||
Long.MAX_VALUE
|
||||
} else if (whole < BI_LONG_MIN) {
|
||||
Long.MIN_VALUE
|
||||
@ -260,6 +273,9 @@ sealed class Decimal : Number(), Comparable<Decimal> {
|
||||
}
|
||||
}
|
||||
|
||||
return longCache
|
||||
}
|
||||
|
||||
override fun compareTo(other: Decimal): Int {
|
||||
return if (other === Zero)
|
||||
signum()
|
||||
|
@ -5,6 +5,7 @@ import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian
|
||||
import net.minecraft.world.level.levelgen.PositionalRandomFactory
|
||||
import net.minecraft.world.level.levelgen.RandomSupport
|
||||
import ru.dbotthepony.kommons.random.LCG64Random
|
||||
import java.lang.StringBuilder
|
||||
import java.util.random.RandomGenerator
|
||||
|
||||
|
@ -0,0 +1,30 @@
|
||||
package ru.dbotthepony.mc.otm.core.util
|
||||
|
||||
import net.minecraft.util.RandomSource
|
||||
import java.util.random.RandomGenerator
|
||||
|
||||
interface IRandomSourceGenerator : RandomSource, RandomGenerator {
|
||||
override fun nextInt(): Int
|
||||
|
||||
override fun nextInt(bound: Int): Int {
|
||||
return super<RandomGenerator>.nextInt(bound)
|
||||
}
|
||||
|
||||
override fun nextInt(origin: Int, bound: Int): Int {
|
||||
return super<RandomGenerator>.nextInt(origin, bound)
|
||||
}
|
||||
|
||||
override fun nextBoolean(): Boolean {
|
||||
return super.nextBoolean()
|
||||
}
|
||||
|
||||
override fun nextFloat(): Float {
|
||||
return super.nextFloat()
|
||||
}
|
||||
|
||||
override fun nextDouble(): Double {
|
||||
return super.nextDouble()
|
||||
}
|
||||
|
||||
override fun nextGaussian(): Double
|
||||
}
|
@ -6,8 +6,8 @@ import net.minecraft.world.level.levelgen.LegacyRandomSource
|
||||
import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian
|
||||
import net.minecraft.world.level.levelgen.PositionalRandomFactory
|
||||
import net.minecraft.world.level.levelgen.RandomSupport
|
||||
import ru.dbotthepony.kommons.random.LCG64Random
|
||||
import java.lang.StringBuilder
|
||||
import java.util.random.RandomGenerator
|
||||
|
||||
/**
|
||||
* Simple and insanely fast random number generator, which can be used for seeding (initializing internal state) of other number generators.
|
||||
@ -19,7 +19,7 @@ import java.util.random.RandomGenerator
|
||||
* * Always use upper 32 bits instead of implementing BitRandomSource and sampling some upper bits
|
||||
* * Uses all bits from provided seed
|
||||
*/
|
||||
class LCG64Random(private var seed: Long = RandomSupport.generateUniqueSeed()) : RandomGenerator, RandomSource {
|
||||
class LCG64RandomSource(seed: Long = RandomSupport.generateUniqueSeed()) : LCG64Random(seed), IRandomSourceGenerator {
|
||||
private val gaussian = MarsagliaPolarGaussian(this)
|
||||
|
||||
override fun setSeed(seed: Long) {
|
||||
@ -27,43 +27,12 @@ class LCG64Random(private var seed: Long = RandomSupport.generateUniqueSeed()) :
|
||||
gaussian.reset()
|
||||
}
|
||||
|
||||
override fun nextInt(): Int {
|
||||
this.seed = MULTIPLIER * this.seed + INCREMENT
|
||||
return this.seed.ushr(32).toInt()
|
||||
}
|
||||
|
||||
override fun nextLong(): Long {
|
||||
val a = nextInt().toLong() and 0xFFFFFFFFL
|
||||
val b = nextInt().toLong() and 0xFFFFFFFFL
|
||||
return a.shl(32) or b
|
||||
}
|
||||
|
||||
override fun nextInt(bound: Int): Int {
|
||||
return super<RandomGenerator>.nextInt(bound)
|
||||
}
|
||||
|
||||
override fun nextInt(origin: Int, bound: Int): Int {
|
||||
return super<RandomGenerator>.nextInt(origin, bound)
|
||||
}
|
||||
|
||||
override fun nextBoolean(): Boolean {
|
||||
return super.nextBoolean()
|
||||
}
|
||||
|
||||
override fun nextFloat(): Float {
|
||||
return super.nextFloat()
|
||||
}
|
||||
|
||||
override fun nextDouble(): Double {
|
||||
return super.nextDouble()
|
||||
}
|
||||
|
||||
override fun nextGaussian(): Double {
|
||||
return gaussian.nextGaussian()
|
||||
}
|
||||
|
||||
override fun fork(): RandomSource {
|
||||
return LCG64Random(nextLong())
|
||||
return LCG64RandomSource(nextLong())
|
||||
}
|
||||
|
||||
override fun forkPositional(): PositionalRandomFactory {
|
||||
@ -72,24 +41,19 @@ class LCG64Random(private var seed: Long = RandomSupport.generateUniqueSeed()) :
|
||||
|
||||
class Positional(val seed: Long) : PositionalRandomFactory {
|
||||
override fun at(x: Int, y: Int, z: Int): RandomSource {
|
||||
return LCG64Random(Mth.getSeed(x, y, z).xor(seed))
|
||||
return LCG64RandomSource(Mth.getSeed(x, y, z).xor(seed))
|
||||
}
|
||||
|
||||
override fun fromHashOf(name: String): RandomSource {
|
||||
return LCG64Random(name.hashCode().toLong().xor(seed))
|
||||
return LCG64RandomSource(name.hashCode().toLong().xor(seed))
|
||||
}
|
||||
|
||||
override fun fromSeed(seed: Long): RandomSource {
|
||||
return LCG64Random(seed)
|
||||
return LCG64RandomSource(seed)
|
||||
}
|
||||
|
||||
override fun parityConfigString(builder: StringBuilder) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MULTIPLIER = 6364136223846793005
|
||||
const val INCREMENT = 1442695040888963407
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package ru.dbotthepony.mc.otm.core.util
|
||||
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.level.levelgen.LegacyRandomSource
|
||||
import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian
|
||||
import net.minecraft.world.level.levelgen.PositionalRandomFactory
|
||||
import net.minecraft.world.level.levelgen.RandomSupport
|
||||
import ru.dbotthepony.kommons.random.LCG64Random
|
||||
import ru.dbotthepony.kommons.random.PCG32Random
|
||||
import java.lang.StringBuilder
|
||||
|
||||
/**
|
||||
* @see PCG32Random
|
||||
*/
|
||||
class PCG32RandomSource(seed: Long = RandomSupport.generateUniqueSeed()) : PCG32Random(seed), IRandomSourceGenerator {
|
||||
private val gaussian = MarsagliaPolarGaussian(this)
|
||||
|
||||
override fun setSeed(seed: Long) {
|
||||
this.seed = seed
|
||||
gaussian.reset()
|
||||
}
|
||||
|
||||
override fun nextGaussian(): Double {
|
||||
return gaussian.nextGaussian()
|
||||
}
|
||||
|
||||
override fun fork(): RandomSource {
|
||||
return PCG32RandomSource(nextLong())
|
||||
}
|
||||
|
||||
override fun forkPositional(): PositionalRandomFactory {
|
||||
return Positional(nextLong())
|
||||
}
|
||||
|
||||
class Positional(val seed: Long) : PositionalRandomFactory {
|
||||
override fun at(x: Int, y: Int, z: Int): RandomSource {
|
||||
return PCG32RandomSource(Mth.getSeed(x, y, z).xor(seed))
|
||||
}
|
||||
|
||||
override fun fromHashOf(name: String): RandomSource {
|
||||
return PCG32RandomSource(name.hashCode().toLong().xor(seed))
|
||||
}
|
||||
|
||||
override fun fromSeed(seed: Long): RandomSource {
|
||||
return PCG32RandomSource(seed)
|
||||
}
|
||||
|
||||
override fun parityConfigString(builder: StringBuilder) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package ru.dbotthepony.mc.otm.core.util
|
||||
|
||||
import it.unimi.dsi.fastutil.HashCommon
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian
|
||||
import net.minecraft.world.level.levelgen.PositionalRandomFactory
|
||||
import net.minecraft.world.level.levelgen.RandomSupport
|
||||
import ru.dbotthepony.kommons.random.LCG64Random
|
||||
import ru.dbotthepony.kommons.random.Xoshiro256StarStarRandom
|
||||
|
||||
/**
|
||||
* Excellent number generator with guaranteed period of 2^255
|
||||
*/
|
||||
class Xoshiro256Random : Xoshiro256StarStarRandom, IRandomSourceGenerator {
|
||||
private val gaussian = MarsagliaPolarGaussian(this)
|
||||
|
||||
// raw
|
||||
private constructor(s0: Long, s1: Long, s2: Long, s3: Long, marker: Nothing?): super(s0, s1, s2, s3, null)
|
||||
// normal
|
||||
constructor(s0: Long, s1: Long, s2: Long, s3: Long) : super(s0, s1, s2, s3)
|
||||
|
||||
// 64-bit seeded
|
||||
constructor(seed: Long) : super(1L, 2L, 3L, 4L, null) {
|
||||
setSeed(seed)
|
||||
}
|
||||
|
||||
// completely random
|
||||
constructor() : super(RandomSupport.generateUniqueSeed(), RandomSupport.generateUniqueSeed(), RandomSupport.generateUniqueSeed(), RandomSupport.generateUniqueSeed(), null)
|
||||
|
||||
override fun setSeed(seed: Long) {
|
||||
val rng = LCG64Random(seed)
|
||||
s0 = rng.nextLong()
|
||||
s1 = rng.nextLong()
|
||||
s2 = rng.nextLong()
|
||||
s3 = rng.nextLong()
|
||||
gaussian.reset()
|
||||
}
|
||||
|
||||
override fun nextInt(): Int {
|
||||
// sample upper bits
|
||||
return nextLong().ushr(32).toInt()
|
||||
}
|
||||
|
||||
override fun nextGaussian(): Double {
|
||||
return gaussian.nextGaussian()
|
||||
}
|
||||
|
||||
override fun fork(): RandomSource {
|
||||
return Xoshiro256Random(nextLong(), nextLong(), nextLong(), nextLong(), null)
|
||||
}
|
||||
|
||||
override fun forkPositional(): PositionalRandomFactory {
|
||||
return Positional(nextLong(), nextLong(), nextLong(), nextLong())
|
||||
}
|
||||
|
||||
class Positional(
|
||||
val s0: Long,
|
||||
val s1: Long,
|
||||
val s2: Long,
|
||||
val s3: Long,
|
||||
) : PositionalRandomFactory {
|
||||
override fun at(x: Int, y: Int, z: Int): RandomSource {
|
||||
val rng = LCG64RandomSource(Mth.getSeed(x, y, z))
|
||||
|
||||
return Xoshiro256Random(
|
||||
s0.rotateLeft(11).xor(rng.nextLong()),
|
||||
s1.rotateLeft(22).xor(rng.nextLong()),
|
||||
s2.rotateLeft(33).xor(rng.nextLong()),
|
||||
s3.rotateLeft(44).xor(rng.nextLong()),
|
||||
)
|
||||
}
|
||||
|
||||
override fun fromHashOf(name: String): RandomSource {
|
||||
return Xoshiro256Random(s0, HashCommon.murmurHash3(name.hashCode().toLong()).xor(s1), s2, s3)
|
||||
}
|
||||
|
||||
override fun fromSeed(seed: Long): RandomSource {
|
||||
return Xoshiro256Random(seed)
|
||||
}
|
||||
|
||||
override fun parityConfigString(builder: StringBuilder) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun raw(s0: Long, s1: Long, s2: Long, s3: Long): Xoshiro256Random {
|
||||
return Xoshiro256Random(s0, s1, s2, s3, null)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
package ru.dbotthepony.mc.otm.core.util
|
||||
|
||||
import it.unimi.dsi.fastutil.HashCommon
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.level.levelgen.MarsagliaPolarGaussian
|
||||
import net.minecraft.world.level.levelgen.PositionalRandomFactory
|
||||
import net.minecraft.world.level.levelgen.RandomSupport
|
||||
import java.util.random.RandomGenerator
|
||||
|
||||
/**
|
||||
* Excellent number generator with guaranteed period of 2^255
|
||||
*/
|
||||
class Xoshiro256SSRandom private constructor(
|
||||
private var s0: Long,
|
||||
private var s1: Long,
|
||||
private var s2: Long,
|
||||
private var s3: Long,
|
||||
marker: Nothing?
|
||||
) : RandomGenerator, RandomSource {
|
||||
private val gaussian = MarsagliaPolarGaussian(this)
|
||||
|
||||
init {
|
||||
if (s0 or s1 or s2 or s3 == 0L) {
|
||||
s0 = 0x73CF3D83FFF44FF3L
|
||||
s1 = 0x6412312B70F3CD37L
|
||||
s2 = -0X6BB4C4E1327BFDCFL
|
||||
s3 = -0X4BE0F5BB5F3F5240L
|
||||
}
|
||||
}
|
||||
|
||||
constructor(s0: Long, s1: Long, s2: Long, s3: Long) : this(s0, s1, s2, s3, null) {
|
||||
// discard some values so generator can get going if provided seed was "weak"
|
||||
for (i in 0 until 32)
|
||||
nextLong()
|
||||
}
|
||||
|
||||
constructor(seed: Long) : this(1L, 2L, 3L, 4L, null) {
|
||||
setSeed(seed)
|
||||
}
|
||||
|
||||
constructor() : this(RandomSupport.generateUniqueSeed(), RandomSupport.generateUniqueSeed(), RandomSupport.generateUniqueSeed(), RandomSupport.generateUniqueSeed(), null)
|
||||
|
||||
override fun setSeed(seed: Long) {
|
||||
val rng = LCG64Random(seed)
|
||||
s0 = rng.nextLong()
|
||||
s1 = rng.nextLong()
|
||||
s2 = rng.nextLong()
|
||||
s3 = rng.nextLong()
|
||||
gaussian.reset()
|
||||
}
|
||||
|
||||
override fun nextInt(): Int {
|
||||
// sample upper bits
|
||||
return nextLong().ushr(32).toInt()
|
||||
}
|
||||
|
||||
override fun nextLong(): Long {
|
||||
val result = (s1 * 5).rotateLeft(7) * 9
|
||||
val t = s1.shl(17)
|
||||
s2 = s2.xor(s0)
|
||||
s3 = s3.xor(s1)
|
||||
s1 = s1.xor(s2)
|
||||
s0 = s0.xor(s3)
|
||||
s2 = s2.xor(t)
|
||||
s3 = s3.rotateLeft(45)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun nextInt(bound: Int): Int {
|
||||
return super<RandomGenerator>.nextInt(bound)
|
||||
}
|
||||
|
||||
override fun nextInt(origin: Int, bound: Int): Int {
|
||||
return super<RandomGenerator>.nextInt(origin, bound)
|
||||
}
|
||||
|
||||
override fun nextBoolean(): Boolean {
|
||||
return super.nextBoolean()
|
||||
}
|
||||
|
||||
override fun nextFloat(): Float {
|
||||
return super.nextFloat()
|
||||
}
|
||||
|
||||
override fun nextDouble(): Double {
|
||||
return super.nextDouble()
|
||||
}
|
||||
|
||||
override fun nextGaussian(): Double {
|
||||
return gaussian.nextGaussian()
|
||||
}
|
||||
|
||||
override fun fork(): RandomSource {
|
||||
return Xoshiro256SSRandom(nextLong(), nextLong(), nextLong(), nextLong())
|
||||
}
|
||||
|
||||
override fun forkPositional(): PositionalRandomFactory {
|
||||
return Positional(nextLong(), nextLong(), nextLong(), nextLong())
|
||||
}
|
||||
|
||||
class Positional(
|
||||
val s0: Long,
|
||||
val s1: Long,
|
||||
val s2: Long,
|
||||
val s3: Long,
|
||||
) : PositionalRandomFactory {
|
||||
override fun at(x: Int, y: Int, z: Int): RandomSource {
|
||||
val rng = LCG64Random(Mth.getSeed(x, y, z))
|
||||
|
||||
return Xoshiro256SSRandom(
|
||||
s0.rotateLeft(11).xor(rng.nextLong()),
|
||||
s1.rotateLeft(22).xor(rng.nextLong()),
|
||||
s2.rotateLeft(33).xor(rng.nextLong()),
|
||||
s3.rotateLeft(44).xor(rng.nextLong()),
|
||||
)
|
||||
}
|
||||
|
||||
override fun fromHashOf(name: String): RandomSource {
|
||||
return Xoshiro256SSRandom(s0, HashCommon.murmurHash3(name.hashCode().toLong()).xor(s1), s2, s3)
|
||||
}
|
||||
|
||||
override fun fromSeed(seed: Long): RandomSource {
|
||||
return Xoshiro256SSRandom(seed)
|
||||
}
|
||||
|
||||
override fun parityConfigString(builder: StringBuilder) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun raw(s0: Long, s1: Long, s2: Long, s3: Long): Xoshiro256SSRandom {
|
||||
return Xoshiro256SSRandom(s0, s1, s2, s3, null)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +1,119 @@
|
||||
package ru.dbotthepony.mc.otm.data.world
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.util.RandomSource
|
||||
import net.minecraft.util.valueproviders.FloatProvider
|
||||
import net.minecraft.util.valueproviders.UniformFloat
|
||||
import net.neoforged.bus.api.IEventBus
|
||||
import ru.dbotthepony.mc.otm.OverdriveThatMatters
|
||||
import ru.dbotthepony.mc.otm.core.ResourceLocation
|
||||
import ru.dbotthepony.mc.otm.data.codec.inRange
|
||||
import ru.dbotthepony.mc.otm.registry.MDeferredRegister
|
||||
import ru.dbotthepony.mc.otm.registry.RegistryDelegate
|
||||
|
||||
class BooleanProvider(val sampler: FloatProvider, val threshold: Float, val lessThan: Boolean = true) {
|
||||
fun sample(random: RandomSource): Boolean {
|
||||
val sampled = sampler.sample(random)
|
||||
interface BooleanProvider {
|
||||
interface Type<T : BooleanProvider> {
|
||||
val codec: MapCodec<T>
|
||||
}
|
||||
|
||||
if (lessThan) {
|
||||
return sampled < threshold
|
||||
} else {
|
||||
return sampled >= threshold
|
||||
fun instance(): Instance
|
||||
val type: Type<*>
|
||||
|
||||
fun interface Instance {
|
||||
fun sample(random: RandomSource): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Each time boolean is sampled, there is a fixed chance for it to test true
|
||||
*/
|
||||
class Unbiased(val chance: Float) : BooleanProvider, Instance {
|
||||
constructor(chance: Int) : this(1f / chance)
|
||||
|
||||
override fun instance(): Instance {
|
||||
return this
|
||||
}
|
||||
|
||||
override val type: Type<*>
|
||||
get() = Companion
|
||||
|
||||
override fun sample(random: RandomSource): Boolean {
|
||||
return random.nextFloat() <= chance
|
||||
}
|
||||
|
||||
companion object : Type<Unbiased> {
|
||||
override val codec: MapCodec<Unbiased> = RecordCodecBuilder.mapCodec {
|
||||
it.group(Codec.FLOAT.inRange(0f, 1f).fieldOf("chance").forGetter(Unbiased::chance)).apply(it, ::Unbiased)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Successful roll chance is specified as follows:
|
||||
* ```
|
||||
* base_chance * failures
|
||||
* ```
|
||||
*
|
||||
* `failures` starts at 1, and increases each time roll failed, eventually reaching 100%
|
||||
* if no successful rolls have been made.
|
||||
*
|
||||
* Once roll is successful, `failures` is reset back to 1.
|
||||
*/
|
||||
class BiasedLinear(val baseChance: Float) : BooleanProvider {
|
||||
constructor(middle: Float, at: Int) : this(middle / at)
|
||||
|
||||
private class I(private val baseChance: Float) : Instance {
|
||||
var lastSuccess = 1f
|
||||
|
||||
override fun sample(random: RandomSource): Boolean {
|
||||
val success = random.nextFloat() <= lastSuccess * baseChance
|
||||
|
||||
if (success)
|
||||
lastSuccess = 1f
|
||||
else
|
||||
lastSuccess += 1f
|
||||
|
||||
return success
|
||||
}
|
||||
}
|
||||
|
||||
override fun instance(): Instance {
|
||||
return I(baseChance)
|
||||
}
|
||||
|
||||
override val type: Type<*>
|
||||
get() = Companion
|
||||
|
||||
companion object : Type<BiasedLinear> {
|
||||
override val codec: MapCodec<BiasedLinear> = RecordCodecBuilder.mapCodec {
|
||||
it.group(
|
||||
Codec.FLOAT.inRange(0f, 1f).fieldOf("base_chance").forGetter(BiasedLinear::baseChance),
|
||||
).apply(it, ::BiasedLinear)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun onceEvery(samples: Int): BooleanProvider {
|
||||
return BooleanProvider(UniformFloat.of(0f, samples.toFloat()), 1f, lessThan = true)
|
||||
private val registryHolder = RegistryDelegate<Type<*>>("boolean_provider") {
|
||||
defaultKey(ResourceLocation(OverdriveThatMatters.MOD_ID, "zero"))
|
||||
}
|
||||
|
||||
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)
|
||||
val registry by registryHolder
|
||||
val registryKey get() = registryHolder.key
|
||||
|
||||
private val registrar = MDeferredRegister(registryKey)
|
||||
|
||||
init {
|
||||
registrar.register("unbiased") { Unbiased.Companion }
|
||||
registrar.register("linear_bias") { BiasedLinear.Companion }
|
||||
}
|
||||
|
||||
val CODEC: Codec<BooleanProvider> by lazy {
|
||||
registry.byNameCodec().dispatch({ it.type }, { it.codec })
|
||||
}
|
||||
|
||||
fun register(bus: IEventBus) {
|
||||
bus.addListener(registryHolder::build)
|
||||
registrar.register(bus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,9 @@ package ru.dbotthepony.mc.otm.worldgen.placement
|
||||
import com.github.benmanes.caffeine.cache.Caffeine
|
||||
import com.github.benmanes.caffeine.cache.Scheduler
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DataResult
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import it.unimi.dsi.fastutil.HashCommon
|
||||
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream
|
||||
import net.minecraft.Util
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.SectionPos
|
||||
@ -14,10 +13,11 @@ import net.minecraft.util.RandomSource
|
||||
import net.minecraft.world.level.ChunkPos
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementContext
|
||||
import net.minecraft.world.level.levelgen.placement.PlacementModifier
|
||||
import ru.dbotthepony.mc.otm.core.util.CMWCRandom
|
||||
import ru.dbotthepony.mc.otm.core.util.Xoshiro256SSRandom
|
||||
import ru.dbotthepony.mc.otm.data.codec.inRange
|
||||
import ru.dbotthepony.kommons.util.XXHash64
|
||||
import ru.dbotthepony.mc.otm.core.util.PCG32RandomSource
|
||||
import ru.dbotthepony.mc.otm.data.codec.minRange
|
||||
import ru.dbotthepony.mc.otm.worldgen.placement.AbstractEnormousPlacement.Parameters
|
||||
import java.io.DataOutputStream
|
||||
import java.time.Duration
|
||||
import java.util.stream.Stream
|
||||
import kotlin.math.sqrt
|
||||
@ -26,8 +26,10 @@ import kotlin.math.sqrt
|
||||
* Enormous placement base, which allows it to span over several chunks without issues.
|
||||
*
|
||||
* MUST come as first placement modifier, other placement modifiers (such as rarity and
|
||||
* shuffle of center point within chunks) must be provided inside [Parameters.placementModifiers] list, in same order as if they were
|
||||
* shuffle of center point within chunks) must be provided inside [Parameters.prePlacementModifiers] list, in same order as if they were
|
||||
* *before* this placement
|
||||
*
|
||||
* If "post-processing" placements are required, better provide them as [Parameters.postPlacementModifiers].
|
||||
*/
|
||||
abstract class AbstractEnormousPlacement(val parameters: Parameters) : PlacementModifier() {
|
||||
data class Parameters(
|
||||
@ -42,7 +44,14 @@ abstract class AbstractEnormousPlacement(val parameters: Parameters) : Placement
|
||||
/**
|
||||
* Baseline placement modifiers, dictating how to appear in chunk
|
||||
*/
|
||||
val placementModifiers: List<PlacementModifier>,
|
||||
val prePlacementModifiers: List<PlacementModifier> = listOf(),
|
||||
|
||||
/**
|
||||
* Post placement modifiers, operating on positions returned by this placement
|
||||
*
|
||||
* Generally, using this will yield better results
|
||||
*/
|
||||
val postPlacementModifiers: List<PlacementModifier> = listOf(),
|
||||
)
|
||||
|
||||
private class GeneratedChunk(positions: Stream<BlockPos>) {
|
||||
@ -73,10 +82,23 @@ abstract class AbstractEnormousPlacement(val parameters: Parameters) : Placement
|
||||
.build<ChunkPos, GeneratedChunk>()
|
||||
|
||||
private fun computeChunk(context: PlacementContext, pos: ChunkPos): GeneratedChunk {
|
||||
val random = Xoshiro256SSRandom(context.level.seed, parameters.seedMix, pos.x.toLong(), pos.z.toLong())
|
||||
val bytes = FastByteArrayOutputStream()
|
||||
val dataStream = DataOutputStream(bytes)
|
||||
|
||||
dataStream.writeLong(context.level.seed)
|
||||
dataStream.writeLong(parameters.seedMix)
|
||||
dataStream.writeInt(pos.x)
|
||||
dataStream.writeInt(pos.z)
|
||||
|
||||
val hash = XXHash64()
|
||||
hash.update(bytes.array, 0, bytes.length)
|
||||
|
||||
val random = PCG32RandomSource(hash.digestAsLong())
|
||||
var stream = Stream.of(BlockPos(pos.minBlockX, 0, pos.minBlockZ))
|
||||
parameters.placementModifiers.forEach { modifier -> stream = stream.flatMap { modifier.getPositions(context, random, it).sequential() } }
|
||||
return GeneratedChunk(stream.flatMap { getPositions(it, random) })
|
||||
parameters.prePlacementModifiers.forEach { modifier -> stream = stream.flatMap { modifier.getPositions(context, random, it).sequential() } }
|
||||
stream = stream.flatMap { getPositions(it, random) }
|
||||
parameters.postPlacementModifiers.forEach { modifier -> stream = stream.flatMap { modifier.getPositions(context, random, it).sequential() } }
|
||||
return GeneratedChunk(stream)
|
||||
}
|
||||
|
||||
final override fun getPositions(context: PlacementContext, random: RandomSource, pos: BlockPos): Stream<BlockPos> {
|
||||
@ -104,7 +126,8 @@ abstract class AbstractEnormousPlacement(val parameters: Parameters) : Placement
|
||||
it.group(
|
||||
Codec.INT.minRange(0).fieldOf("chunk_scan_range").forGetter(Parameters::chunkScanRange),
|
||||
Codec.LONG.fieldOf("seed_mix").forGetter(Parameters::seedMix),
|
||||
CODEC.listOf().fieldOf("placement").forGetter(Parameters::placementModifiers),
|
||||
CODEC.listOf().optionalFieldOf("placement", listOf()).forGetter(Parameters::prePlacementModifiers),
|
||||
CODEC.listOf().optionalFieldOf("post_placement", listOf()).forGetter(Parameters::postPlacementModifiers),
|
||||
).apply(it, ::Parameters)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package ru.dbotthepony.mc.otm.worldgen.placement
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
@ -11,29 +12,44 @@ 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.angleDifference
|
||||
import ru.dbotthepony.mc.otm.core.math.normalizeAngle
|
||||
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.codec.minRange
|
||||
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.absoluteValue
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sin
|
||||
|
||||
class WormPlacement(
|
||||
parameters: Parameters,
|
||||
val wormLength: IntProvider,
|
||||
val wormTurnChance: BooleanProvider,
|
||||
val wormTurnXZ: FloatProvider,
|
||||
val wormTurnXY: FloatProvider,
|
||||
val length: IntProvider,
|
||||
val turnChanceXZ: BooleanProvider,
|
||||
val turnChanceXY: BooleanProvider,
|
||||
val turnSpeedXZ: FloatProvider,
|
||||
val turnSpeedXY: FloatProvider,
|
||||
val turnRateXZ: FloatProvider,
|
||||
val turnRateXY: FloatProvider,
|
||||
val initialAngleXY: FloatProvider = DEFAULT_INITIAL_ANGLE_XY,
|
||||
val maxTravelDown: Int = Int.MAX_VALUE,
|
||||
val maxTravelUp: Int = Int.MAX_VALUE,
|
||||
) : AbstractEnormousPlacement(parameters) {
|
||||
override fun getPositions(center: BlockPos, random: RandomSource): Stream<BlockPos> {
|
||||
var position = Vector.ZERO
|
||||
val maxDistance = wormLength.sample(random)
|
||||
val maxDistance = length.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 xyRotation = normalizeAngle(initialAngleXY.sample(random).toDouble())
|
||||
|
||||
// determine initial "turn to" angle
|
||||
var xzTargetRotation = random.nextDouble(-PI / 2.0, PI / 2.0)
|
||||
var xyTargetRotation = normalizeAngle(initialAngleXY.sample(random).toDouble())
|
||||
|
||||
var xzSin = sin(xzRotation)
|
||||
var xzCos = cos(xzRotation)
|
||||
@ -43,21 +59,64 @@ class WormPlacement(
|
||||
var prevPos = position.toBlockPos()
|
||||
positions.add(prevPos)
|
||||
|
||||
val turnChanceXZ = turnChanceXZ.instance()
|
||||
val turnChanceXY = turnChanceXY.instance()
|
||||
|
||||
for (traveledDistance in 0 .. maxDistance) {
|
||||
// wormy turn
|
||||
if (wormTurnChance.sample(random)) {
|
||||
if (turnChanceXZ.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)
|
||||
xzTargetRotation += turnRateXZ.sample(random)
|
||||
xzTargetRotation = normalizeAngle(xzTargetRotation)
|
||||
}
|
||||
|
||||
// wormy-o!
|
||||
if (turnChanceXY.sample(random)) {
|
||||
// worm?
|
||||
xyTargetRotation += turnRateXY.sample(random)
|
||||
xzTargetRotation = normalizeAngle(xzTargetRotation)
|
||||
}
|
||||
|
||||
if (xzTargetRotation != xzRotation) {
|
||||
val diff = angleDifference(xzTargetRotation, xzRotation)
|
||||
val abs = diff.absoluteValue
|
||||
val speed = turnSpeedXZ.sample(random).toDouble()
|
||||
|
||||
if (abs <= speed)
|
||||
xzRotation = xzTargetRotation
|
||||
else
|
||||
xzRotation += speed * diff.sign
|
||||
|
||||
xzSin = sin(xzRotation)
|
||||
xzCos = cos(xzRotation)
|
||||
}
|
||||
|
||||
if (xyTargetRotation != xyRotation) {
|
||||
val diff = angleDifference(xyTargetRotation, xyRotation)
|
||||
val abs = diff.absoluteValue
|
||||
val speed = turnSpeedXZ.sample(random).toDouble()
|
||||
|
||||
if (abs <= speed)
|
||||
xyRotation = xyTargetRotation
|
||||
else
|
||||
xyRotation += speed * diff.sign
|
||||
|
||||
xySin = sin(xyRotation)
|
||||
}
|
||||
|
||||
// advance worm
|
||||
position += Vector(xzCos, xySin, xzSin)
|
||||
|
||||
if (position.y > maxTravelUp) {
|
||||
xyTargetRotation = 0.0
|
||||
xySin = 0.0
|
||||
position = Vector(position.x, maxTravelUp.toDouble(), position.z)
|
||||
} else if (position.y < -maxTravelDown) {
|
||||
xyTargetRotation = 0.0
|
||||
xySin = 0.0
|
||||
position = Vector(position.x, -maxTravelDown.toDouble(), position.z)
|
||||
}
|
||||
|
||||
val calc = position.toBlockPos()
|
||||
|
||||
if (calc != prevPos) {
|
||||
@ -85,11 +144,21 @@ class WormPlacement(
|
||||
if (sum != value) {
|
||||
return sum
|
||||
} else {
|
||||
i += 1f
|
||||
i *= 2f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val DEFAULT_INITIAL_ANGLE_XY: UniformFloat = UniformFloat.of(-PI.toFloat() / 16f, increment(PI.toFloat() / 16f))
|
||||
|
||||
fun constantTurnRate(degrees: Float): FloatProvider {
|
||||
return constantTurnRateRadians(degrees * DEGREES_TO_RADIANS)
|
||||
}
|
||||
|
||||
fun constantTurnRateRadians(rad: Float): FloatProvider {
|
||||
return ConstantFloat.of(rad)
|
||||
}
|
||||
|
||||
fun uniformTurnRate(degrees: Float): FloatProvider {
|
||||
return uniformTurnRateRadians(degrees * DEGREES_TO_RADIANS)
|
||||
}
|
||||
@ -115,10 +184,16 @@ class WormPlacement(
|
||||
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),
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user