Separate turn rate for each axis, smooth turning with specifable turn speed

This commit is contained in:
DBotThePony 2025-03-08 21:59:29 +07:00
parent be97560f01
commit 25a312b56f
Signed by: DBot
GPG Key ID: DCC23B5715498507
2 changed files with 79 additions and 20 deletions

View File

@ -124,10 +124,13 @@ fun registerPlacedFeatures(context: BootstrapContext<PlacedFeature>) {
HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(120), 15.0)), HeightRangePlacement.of(StandardDeviationHeightProvider(VerticalAnchor.absolute(120), 15.0)),
) )
), ),
wormLength = UniformInt.of(40, 200), length = UniformInt.of(40, 200),
wormTurnChance = BooleanProvider.BiasedLinear(0.6f, 4), turnRateXY = WormPlacement.normalDistributedTurnRate(10f),
wormTurnXY = WormPlacement.normalDistributedTurnRate(60f), turnRateXZ = WormPlacement.normalDistributedTurnRate(60f),
wormTurnXZ = WormPlacement.normalDistributedTurnRate(60f), turnSpeedXZ = WormPlacement.constantTurnRate(10f),
turnSpeedXY = WormPlacement.constantTurnRate(4f),
turnChanceXY = BooleanProvider.Unbiased(6),
turnChanceXZ = BooleanProvider.BiasedLinear(0.6f, 5)
) )
) )
)) ))

View File

@ -11,6 +11,8 @@ import net.minecraft.util.valueproviders.IntProvider
import net.minecraft.util.valueproviders.UniformFloat import net.minecraft.util.valueproviders.UniformFloat
import net.minecraft.world.level.levelgen.placement.PlacementModifierType import net.minecraft.world.level.levelgen.placement.PlacementModifierType
import ru.dbotthepony.mc.otm.core.math.Vector 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.plus
import ru.dbotthepony.mc.otm.core.math.toBlockPos import ru.dbotthepony.mc.otm.core.math.toBlockPos
import ru.dbotthepony.mc.otm.core.nextDouble 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 ru.dbotthepony.mc.otm.registry.data.MPlacementModifiers
import java.util.stream.Stream import java.util.stream.Stream
import kotlin.math.PI import kotlin.math.PI
import kotlin.math.absoluteValue
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sign
import kotlin.math.sin import kotlin.math.sin
class WormPlacement( class WormPlacement(
parameters: Parameters, parameters: Parameters,
val wormLength: IntProvider, val length: IntProvider,
val wormTurnChance: BooleanProvider, val turnChanceXZ: BooleanProvider,
val wormTurnXZ: FloatProvider, val turnChanceXY: BooleanProvider,
val wormTurnXY: FloatProvider, val turnSpeedXZ: FloatProvider,
val turnSpeedXY: FloatProvider,
val turnRateXZ: FloatProvider,
val turnRateXY: FloatProvider,
val initialAngleXY: FloatProvider = DEFAULT_INITIAL_ANGLE_XY,
) : AbstractEnormousPlacement(parameters) { ) : AbstractEnormousPlacement(parameters) {
override fun getPositions(center: BlockPos, random: RandomSource): Stream<BlockPos> { override fun getPositions(center: BlockPos, random: RandomSource): Stream<BlockPos> {
var position = Vector.ZERO var position = Vector.ZERO
val maxDistance = wormLength.sample(random) val maxDistance = length.sample(random)
// determine initial worm facing angle // determine initial worm facing angle
var xzRotation = random.nextDouble(-PI / 2.0, PI / 2.0) 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 xzSin = sin(xzRotation)
var xzCos = cos(xzRotation) var xzCos = cos(xzRotation)
@ -43,18 +55,48 @@ class WormPlacement(
var prevPos = position.toBlockPos() var prevPos = position.toBlockPos()
positions.add(prevPos) positions.add(prevPos)
val wormTurnChance = wormTurnChance.instance() val turnChanceXZ = turnChanceXZ.instance()
val turnChanceXY = turnChanceXY.instance()
for (traveledDistance in 0 .. maxDistance) { for (traveledDistance in 0 .. maxDistance) {
// wormy turn // wormy turn
if (wormTurnChance.sample(random)) { if (turnChanceXZ.sample(random)) {
// wormy angle // wormy angle
// TODO: smooth turning, instead of snapping to new angle make it gradually face new angle xzTargetRotation += turnRateXZ.sample(random)
xzRotation += wormTurnXZ.sample(random) xzTargetRotation = normalizeAngle(xzTargetRotation)
xyRotation += wormTurnXY.sample(random) }
// 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) xzSin = sin(xzRotation)
xzCos = cos(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) xySin = sin(xyRotation)
} }
@ -87,11 +129,21 @@ class WormPlacement(
if (sum != value) { if (sum != value) {
return sum return sum
} else { } 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 { fun uniformTurnRate(degrees: Float): FloatProvider {
return uniformTurnRateRadians(degrees * DEGREES_TO_RADIANS) return uniformTurnRateRadians(degrees * DEGREES_TO_RADIANS)
} }
@ -117,10 +169,14 @@ class WormPlacement(
val CODEC: MapCodec<WormPlacement> = RecordCodecBuilder.mapCodec { val CODEC: MapCodec<WormPlacement> = RecordCodecBuilder.mapCodec {
it.group( it.group(
PARAMETERS_CODEC.forGetter(WormPlacement::parameters), PARAMETERS_CODEC.forGetter(WormPlacement::parameters),
IntProvider.CODEC.fieldOf("length").forGetter(WormPlacement::wormLength), IntProvider.CODEC.fieldOf("length").forGetter(WormPlacement::length),
BooleanProvider.CODEC.fieldOf("turn_chance").forGetter(WormPlacement::wormTurnChance), BooleanProvider.CODEC.fieldOf("turn_chance_xz").forGetter(WormPlacement::turnChanceXZ),
FloatProvider.CODEC.fieldOf("turn_xz").forGetter(WormPlacement::wormTurnXZ), BooleanProvider.CODEC.fieldOf("turn_chance_xy").forGetter(WormPlacement::turnChanceXY),
FloatProvider.CODEC.fieldOf("turn_xy").forGetter(WormPlacement::wormTurnXY), 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) ).apply(it, ::WormPlacement)
} }
} }