From 01054a05d2179df53080922a7b00023bfd44d165 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 8 Mar 2025 11:06:35 +0700 Subject: [PATCH] Add BooleanProvider.BiasedLinear --- .../ru/dbotthepony/mc/otm/datagen/WorldGen.kt | 7 +- .../mc/otm/data/world/BooleanProvider.kt | 65 +++++++++++++++++-- 2 files changed, 59 insertions(+), 13 deletions(-) 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 2ffdb3d61..3ea6124b7 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt @@ -7,9 +7,6 @@ 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.UniformFloat import net.minecraft.util.valueproviders.UniformInt import net.minecraft.world.level.levelgen.GenerationStep @@ -23,8 +20,6 @@ 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 @@ -130,7 +125,7 @@ fun registerPlacedFeatures(context: BootstrapContext) { ) ), wormLength = UniformInt.of(40, 200), - wormTurnChance = BooleanProvider.Uniform(4), + wormTurnChance = BooleanProvider.BiasedLinear(0.6f, 4), wormTurnXY = WormPlacement.normalDistributedTurnRate(60f), wormTurnXZ = WormPlacement.normalDistributedTurnRate(60f), ) 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 index f19fb7c00..365276ec5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/data/world/BooleanProvider.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/data/world/BooleanProvider.kt @@ -7,7 +7,7 @@ import net.minecraft.util.RandomSource 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.minRange +import ru.dbotthepony.mc.otm.data.codec.inRange import ru.dbotthepony.mc.otm.registry.MDeferredRegister import ru.dbotthepony.mc.otm.registry.RegistryDelegate @@ -23,7 +23,12 @@ interface BooleanProvider { fun sample(random: RandomSource): Boolean } - class Uniform(val chance: Int) : BooleanProvider, Instance { + /** + * 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 } @@ -32,12 +37,57 @@ interface BooleanProvider { get() = Companion override fun sample(random: RandomSource): Boolean { - return random.nextInt(chance) == 0 + return random.nextFloat() <= chance } - companion object : Type { - override val codec: MapCodec = RecordCodecBuilder.mapCodec { - it.group(Codec.INT.minRange(1).fieldOf("once_every").forGetter(Uniform::chance)).apply(it, ::Uniform) + companion object : Type { + override val codec: MapCodec = 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 { + override val codec: MapCodec = RecordCodecBuilder.mapCodec { + it.group( + Codec.FLOAT.inRange(0f, 1f).fieldOf("base_chance").forGetter(BiasedLinear::baseChance), + ).apply(it, ::BiasedLinear) } } } @@ -53,7 +103,8 @@ interface BooleanProvider { private val registrar = MDeferredRegister(registryKey) init { - registrar.register("uniform") { Uniform.Companion } + registrar.register("unbiased") { Unbiased.Companion } + registrar.register("linear_bias") { BiasedLinear.Companion } } val CODEC: Codec by lazy {