From 25a312b56f72f499fd5c6f8bad46dfddda6c64e1 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 8 Mar 2025 21:59:29 +0700 Subject: [PATCH] Separate turn rate for each axis, smooth turning with specifable turn speed --- .../ru/dbotthepony/mc/otm/datagen/WorldGen.kt | 11 ++- .../otm/worldgen/placement/WormPlacement.kt | 88 +++++++++++++++---- 2 files changed, 79 insertions(+), 20 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 3ea6124b7..38ab0ce69 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/WorldGen.kt @@ -124,10 +124,13 @@ fun registerPlacedFeatures(context: BootstrapContext) { HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(120), 15.0)), ) ), - wormLength = UniformInt.of(40, 200), - wormTurnChance = BooleanProvider.BiasedLinear(0.6f, 4), - wormTurnXY = WormPlacement.normalDistributedTurnRate(60f), - wormTurnXZ = WormPlacement.normalDistributedTurnRate(60f), + length = UniformInt.of(40, 200), + 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) ) ) )) 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 index bd6d8cc3b..55079c771 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/WormPlacement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/worldgen/placement/WormPlacement.kt @@ -11,6 +11,8 @@ 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 @@ -18,22 +20,32 @@ 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, ) : AbstractEnormousPlacement(parameters) { override fun getPositions(center: BlockPos, random: RandomSource): Stream { 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,18 +55,48 @@ class WormPlacement( var prevPos = position.toBlockPos() positions.add(prevPos) - val wormTurnChance = wormTurnChance.instance() + 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) } @@ -87,11 +129,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) } @@ -117,10 +169,14 @@ class WormPlacement( 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), + 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), ).apply(it, ::WormPlacement) } }