From 98955f7e5d2366855ccde5081508368355993ff1 Mon Sep 17 00:00:00 2001 From: ByteDice Date: Sat, 23 Nov 2024 02:07:23 +0100 Subject: [PATCH] fixed bugs, added new command --- .../bytedice/bde_particles/Bde_particles.kt | 7 +-- .../kotlin/com/bytedice/bde_particles/Math.kt | 57 ++++++++++++++----- .../bde_particles/commands/KillAllEmitters.kt | 29 ++++++++++ .../particleIdRegister/EmitterParams.kt | 8 +-- .../particleIdRegister/Particle.kt | 6 +- .../particleIdRegister/ParticleEmitter.kt | 23 ++++---- .../particleIdRegister/ParticleIdRegister.kt | 9 +-- .../particleIdRegister/ParticleParams.kt | 20 ++++--- 8 files changed, 112 insertions(+), 47 deletions(-) create mode 100644 src/main/kotlin/com/bytedice/bde_particles/commands/KillAllEmitters.kt diff --git a/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt b/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt index 546c103..bdebe5f 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt @@ -1,6 +1,7 @@ package com.bytedice.bde_particles import com.bytedice.bde_particles.commands.GiveEmitterTool +import com.bytedice.bde_particles.commands.KillAllEmitters import com.bytedice.bde_particles.commands.ManageEmitters import com.bytedice.bde_particles.items.ParticleEmitterTool import com.bytedice.bde_particles.particleIdRegister.* @@ -22,15 +23,12 @@ import net.minecraft.world.World // TODO: finish the particle ticking // TODO: save particle emitters in world file, or kill them on server restart (so they don't linger forever) // TODO: make custom commands to create particles easier (only temporarily saved) -// TODO: add interpolation curves (easing). Also remake the Array's to Triple -// TODO: kill all particles command var ALL_PARTICLE_EMITTERS: Array = emptyArray() class Bde_particles : ModInitializer { - override fun onInitialize() { ServerLifecycleEvents.SERVER_STARTED.register { _ -> init() @@ -42,7 +40,8 @@ class Bde_particles : ModInitializer { CommandRegistrationCallback.EVENT.register { dispatcher, registryAccess, _ -> GiveEmitterTool.register(dispatcher, registryAccess) - ManageEmitters .register(dispatcher) + ManageEmitters .register(dispatcher) + KillAllEmitters.register(dispatcher) } UseItemCallback.EVENT.register(UseItemCallback { player: PlayerEntity, world: World, hand: Hand -> diff --git a/src/main/kotlin/com/bytedice/bde_particles/Math.kt b/src/main/kotlin/com/bytedice/bde_particles/Math.kt index 0481339..b1e00d9 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/Math.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/Math.kt @@ -12,6 +12,42 @@ import kotlin.math.* import kotlin.random.Random +class InterpolationCurves(val function: (Float) -> Float) { + companion object { + val LINEAR = InterpolationCurves { y -> y } + val SQRT = InterpolationCurves { y -> sqrt(y) } + val EXPONENT = InterpolationCurves { y -> y.pow(2.0f) } + val CUBIC = InterpolationCurves { y -> y.pow(3.0f) } + val SINE = InterpolationCurves { y -> sin(y * PI.toFloat() / 2) } + val COSINE = InterpolationCurves { y -> 1 - cos(y * PI.toFloat() / 2) } + val INVERSE = InterpolationCurves { y -> 1 - y } + val LOG = InterpolationCurves { y -> if (y > 0) ln(y + 1) else 0f } + val EXP = InterpolationCurves { y -> exp(y) - 1 } + val BOUNCE = InterpolationCurves { y -> + val n1 = 7.5625f + val d1 = 2.75f + when { + y < 1 / d1 -> n1 * y * y + y < 2 / d1 -> { + val t = y - 1.5f / d1 + n1 * t * t + 0.75f + } + y < 2.5 / d1 -> { + val t = y - 2.25f / d1 + n1 * t * t + 0.9375f + } + else -> { + val t = y - 2.625f / d1 + n1 * t * t + 0.984375f + } + } + } + + fun custom(equation: (Float) -> Float) = InterpolationCurves(equation) + } +} + + fun raycastFromPlayer(player: ServerPlayerEntity, maxDistance: Double): HitResult? { val world: World = player.world val eyePos: Vec3d = player.getCameraPosVec(1.0f) @@ -173,23 +209,18 @@ fun lerp(x: Float, y: Float, t: Float) : Float { } -fun interpolateCurve(curve: Array, t: Float): Vector3f { - if (curve.size < 2) { return curve[0] } - +fun interpolateCurve(start: Vector3f, end: Vector3f, t: Float, curve: InterpolationCurves): Vector3f { val clampedT = t.coerceIn(0.0f, 1.0f) - val scaledT = clampedT * (curve.size - 1) - val segmentIndex = scaledT.toInt() - val segmentT = scaledT - segmentIndex + val curvedT = curve.function(clampedT) - val start = curve[segmentIndex] - val end = curve[(segmentIndex + 1).coerceAtMost(curve.size - 1)] // Ensure bounds - - return Vector3f( - start.x + (end.x - start.x) * segmentT, - start.y + (end.y - start.y) * segmentT, - start.z + (end.z - start.z) * segmentT + val interpolatedVec = Vector3f( + lerp(start.x, end.x, curvedT), + lerp(start.y, end.y, curvedT), + lerp(start.z, end.z, curvedT) ) + + return interpolatedVec } diff --git a/src/main/kotlin/com/bytedice/bde_particles/commands/KillAllEmitters.kt b/src/main/kotlin/com/bytedice/bde_particles/commands/KillAllEmitters.kt new file mode 100644 index 0000000..786833c --- /dev/null +++ b/src/main/kotlin/com/bytedice/bde_particles/commands/KillAllEmitters.kt @@ -0,0 +1,29 @@ +package com.bytedice.bde_particles.commands + +import com.bytedice.bde_particles.ALL_PARTICLE_EMITTERS +import com.mojang.brigadier.Command +import com.mojang.brigadier.CommandDispatcher +import net.minecraft.server.command.CommandManager +import net.minecraft.server.command.ServerCommandSource +import net.minecraft.text.Text + +object KillAllEmitters { + fun register(dispatcher: CommandDispatcher) { + val command = CommandManager.literal("KillAllEmitters") + .requires { source -> source.hasPermissionLevel(4) } + + dispatcher.register( + command + .executes { context -> + for (emitter in ALL_PARTICLE_EMITTERS) { + emitter.kill() + } + + val feedback = Text.literal("BPS - Killed all living Emitters.") + + context.source.sendFeedback({ feedback }, false) + Command.SINGLE_SUCCESS + } + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/EmitterParams.kt b/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/EmitterParams.kt index 9e228f3..bd736f9 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/EmitterParams.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/EmitterParams.kt @@ -7,16 +7,16 @@ package com.bytedice.bde_particles.particleIdRegister * @param maxCount The maximum number of particles that can exist. If set to 0 or below, no particles will spawn. * @param spawnsPerTick The number of particles spawned per tick. If set to 0 or below, no particles will spawn. * @param loopDur The duration for which the loop runs, in ticks. A value 0 or below means the loop is infinite, and `loopDelay` and `loopCount` will be ignored. - * @param loopDelay The delay between each loop cycle, in ticks. This only takes effect if `loop` is true. Negative values are treated as 0, meaning no delay. + * @param loopDelay The delay between each loop cycle, in ticks. Negative values are treated as 0, meaning no delay. * @param loopCount The number of times the loop will repeat. 0 means it will shoot a single burst of particles. A value below 0 means it will repeat indefinitely. * @param particleTypes An array of `ParticleParams` defining the types of particles to spawn. If empty, no particles will spawn. */ data class EmitterParams ( var maxCount: Int = 200, - var spawnsPerTick: Int = 2, + var spawnsPerTick: Int = 1, var loopDur: Int = 25, - var loopDelay: Int = 10, - var loopCount: Int = 3, + var loopDelay: Int = 0, + var loopCount: Int = 0, var particleTypes: Array = arrayOf(ParticleParams()), ) { diff --git a/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/Particle.kt b/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/Particle.kt index 06ab492..46194d6 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/Particle.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/Particle.kt @@ -135,8 +135,9 @@ class Particle(private val particleParams: ParticleParams) { fun calcRot(timeAliveClamped: Float) : Vector3f { val newRot = Vector3f(rot) val newVel = Vector3f(rotVel) + val rotCurve = particleParams.rotVelCurve - newVel.mul(interpolateCurve(particleParams.sizeCurve, timeAliveClamped)) + newVel.mul(interpolateCurve(rotCurve.first, rotCurve.second, timeAliveClamped, rotCurve.third)) newRot.add(newVel) return newRot } @@ -157,7 +158,8 @@ class Particle(private val particleParams: ParticleParams) { fun calcSize(timeAliveClamped: Float) : Vector3f { val scale = Vector3f(scale) - scale.mul(interpolateCurve(particleParams.sizeCurve, timeAliveClamped)) + val sizeCurve = particleParams.sizeCurve + scale.mul(interpolateCurve(sizeCurve.first, sizeCurve.second, timeAliveClamped, sizeCurve.third)) return scale } diff --git a/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/ParticleEmitter.kt b/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/ParticleEmitter.kt index ca009e6..39a847e 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/ParticleEmitter.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/ParticleEmitter.kt @@ -13,14 +13,15 @@ class ParticleEmitter(private val emitterPos: Vec3d, private val emitterWorld: S private var loopDur = 0 private var loopDelayActive = false - private var isCounting = true + private var isTicking = true var isDead = false fun tick() { - if (isCounting) { count(); addParticle() } - if (!isCounting && allParticles.isEmpty()) { isDead = true } + if (isTicking) { count() } + if (isTicking) { addParticle() } + if (!isTicking && allParticles.isEmpty()) { isDead = true } for (particle in allParticles) { if (particle.isDead) { allParticles = allParticles.toMutableList().apply { remove(particle) }.toTypedArray() } @@ -29,7 +30,7 @@ class ParticleEmitter(private val emitterPos: Vec3d, private val emitterWorld: S } - private fun addParticle() { + fun addParticle() { if (loopDelayActive) { return } for (i in emitterParams.particleTypes.indices) { @@ -46,7 +47,7 @@ class ParticleEmitter(private val emitterPos: Vec3d, private val emitterWorld: S } - private fun count() { + fun count() { // this is the only section I have comments // because im so damn good at cooking spaghetti @@ -56,9 +57,9 @@ class ParticleEmitter(private val emitterPos: Vec3d, private val emitterWorld: S // if the duration is greater than max duration, add 1 loopCount // return if max loopCount is 0 // if the delay is greater than 0 then enable it - if (loopDur > emitterParams.loopDur) { + if (loopDur >= emitterParams.loopDur) { if (loopCount == 0) { - isCounting = false + isTicking = false return } loopCount += 1 @@ -67,15 +68,15 @@ class ParticleEmitter(private val emitterPos: Vec3d, private val emitterWorld: S } // if the delay is greater than max delay, disable the delay - else if (loopDelay > emitterParams.loopDelay) { + else if (loopDelay >= emitterParams.loopDelay) { loopDelay = 0 loopDelayActive = false } // if loopCount is greater than max loopCount then stop immediately // if max loopCount is below 0 then don't do anything, loop infinitely - if (loopCount > 0 && emitterParams.loopCount >= loopCount) { - isCounting = false + if (loopCount > 0 && loopCount >= emitterParams.loopCount) { + isTicking = false return } @@ -93,7 +94,7 @@ class ParticleEmitter(private val emitterPos: Vec3d, private val emitterWorld: S particle.kill() allParticles = allParticles.toMutableList().apply { remove(particle) }.toTypedArray() } - isCounting = false + isTicking = false isDead = true } } \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/ParticleIdRegister.kt b/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/ParticleIdRegister.kt index d660f19..0b576db 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/ParticleIdRegister.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/ParticleIdRegister.kt @@ -2,6 +2,7 @@ package com.bytedice.bde_particles.particleIdRegister +import com.bytedice.bde_particles.InterpolationCurves import org.joml.Vector3f @@ -97,16 +98,16 @@ fun updateSingleParticleParam(params: ParticleParams, paramName: String, paramVa "blockCurve" -> paramValue as Array "rotRandom" -> paramValue as Pair "rotVelRandom" -> paramValue as Pair - "rotVelCurve" -> paramValue as Array "sizeRandom" -> paramValue as Pair "uniformSize" -> paramValue as Boolean - "sizeCurve" -> paramValue as Array "velRandom" -> paramValue as Pair "forceFields" -> paramValue as Array "gravity" -> paramValue as Vector3f "drag" -> paramValue as Float "minVel" -> paramValue as Float "lifeTime" -> paramValue as Pair + "rotVelCurve" -> paramValue as Triple + "sizeCurve" -> paramValue as Triple else -> null } @@ -119,16 +120,16 @@ fun updateSingleParticleParam(params: ParticleParams, paramName: String, paramVa "blockCurve" -> newParams.blockCurve = value as Array "rotRandom" -> newParams.rotRandom = value as Pair "rotVelRandom" -> newParams.rotVelRandom = value as Pair - "rotVelCurve" -> newParams.rotVelCurve = value as Array "sizeRandom" -> newParams.sizeRandom = value as Pair "uniformSize" -> newParams.uniformSize = value as Boolean - "sizeCurve" -> newParams.sizeCurve = value as Array "velRandom" -> newParams.velRandom = value as Pair "forceFields" -> newParams.forceFields = value as Array "gravity" -> newParams.gravity = value as Vector3f "drag" -> newParams.drag = value as Float "minVel" -> newParams.minVel = value as Float "lifeTime" -> newParams.lifeTime = value as Pair + "rotVelCurve" -> newParams.rotVelCurve = value as Triple + "sizeCurve" -> newParams.sizeCurve = value as Triple else -> return Pair(params, false) } diff --git a/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/ParticleParams.kt b/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/ParticleParams.kt index 0fe179f..e085cb5 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/ParticleParams.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particleIdRegister/ParticleParams.kt @@ -1,6 +1,8 @@ package com.bytedice.bde_particles.particleIdRegister +import com.bytedice.bde_particles.InterpolationCurves import org.joml.Vector3f +import kotlin.math.pow /** * ParticleParams defines the parameters for particle behavior, including shape, size, velocity, and other physics properties. @@ -9,32 +11,32 @@ import org.joml.Vector3f * @param blockCurve An array of block names that gets cycled through during the particle's lifetime. Empty defaults to "minecraft:air". * @param rotRandom The random ***initial*** rotation range for the particle's rotation in X, Y, and Z axes. * @param rotVelRandom The random velocity for particle rotation in the X, Y, and Z axes. - * @param rotVelCurve A curve which the rotation velocity gets multiplied by during the particle's lifetime. * @param sizeRandom The random ***initial*** size range for the particle. If `uniformSize` is true, this will be uniform (cubic). * @param uniformSize If true, particles will have a uniform (cubic) size. Otherwise, their sizes on different axis can vary. - * @param sizeCurve A curve which the size gets multiplied by during the particle's lifetime. * @param velRandom The random ***initial*** velocity range for the particle in the X, Y, and Z axes. * @param forceFields An array of force fields applied to the particle. * @param gravity A constant force applied to the particle every tick. It's often used as gravity. * @param drag A drag force that affects the speed overtime. The formula is (velocity * (1.0 - drag)) * @param minVel If particles travel slower than this their speed is automatically 0. Values less than or equal to 0 mean the particle can travel at any speed. * @param lifeTime A range for the particle's lifetime in ticks. A value below 1 will result in the particle not spawning. + * @param rotVelCurve A curve which the rotation velocity gets multiplied by during the particle's lifetime. + * @param sizeCurve A curve which the size gets multiplied by during the particle's lifetime. */ data class ParticleParams ( var shape: SpawningShape? = SpawningShape.Circle(3.0f), var blockCurve: Array = arrayOf("minecraft:shroomlight", "minecraft:orange_concrete", "minecraft:orange_stained_glass", "minecraft:gray_stained_glass", "minecraft:light_gray_stained_glass"), var rotRandom: Pair = Pair(Vector3f(0.0f, 0.0f, 0.0f), Vector3f(360.0f, 360.0f, 360.0f)), var rotVelRandom: Pair = Pair(Vector3f(-0.2f, -0.2f, -0.2f), Vector3f(0.2f, 0.2f, 0.2f)), - var rotVelCurve: Array = arrayOf(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f)), var sizeRandom: Pair = Pair(Vector3f(0.5f, 0.5f, 0.5f), Vector3f(1.0f, 1.0f, 1.0f)), var uniformSize: Boolean = true, - var sizeCurve: Array = arrayOf(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.5f, 0.5f, 0.5f)), var velRandom: Pair = Pair(Vector3f(-0.1f, 0.4f, -0.1f), Vector3f(0.1f, 0.8f, 0.1f)), - var forceFields: Array = arrayOf(ForceField()), // emptyArray(), + var forceFields: Array = emptyArray(), var gravity: Vector3f = Vector3f(0.0f, -0.01f, 0.0f), var drag: Float = 0.075f, var minVel: Float = 0.0f, - var lifeTime: Pair = Pair(15, 45) + var lifeTime: Pair = Pair(15, 45), + var rotVelCurve: Triple = Triple(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f), InterpolationCurves.SQRT), + var sizeCurve: Triple = Triple(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.5f, 0.5f, 0.5f), InterpolationCurves.LINEAR), ) { companion object Presets { val DEFAULT = ParticleParams() @@ -43,16 +45,16 @@ data class ParticleParams ( arrayOf("minecraft:shroomlight", "minecraft:orange_concrete", "minecraft:orange_stained_glass", "minecraft:gray_stained_glass", "minecraft:light_gray_stained_glass"), Pair(Vector3f(0.0f, 0.0f, 0.0f), Vector3f(360.0f, 360.0f, 360.0f)), Pair(Vector3f(-0.2f, -0.2f, -0.2f), Vector3f(0.2f, 0.2f, 0.2f)), - arrayOf(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f)), Pair(Vector3f(0.5f, 0.5f, 0.5f), Vector3f(1.0f, 1.0f, 1.0f)), true, - arrayOf(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.5f, 0.5f, 0.5f)), Pair(Vector3f(-0.1f, 0.4f, -0.1f), Vector3f(0.1f, 0.8f, 0.1f)), emptyArray(), Vector3f(0.0f, -0.01f, 0.0f), 0.075f, 0.0f, - Pair(15, 45) + Pair(15, 45), + Triple(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f), InterpolationCurves.SQRT), + Triple(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.5f, 0.5f, 0.5f), InterpolationCurves.SQRT) ) } } \ No newline at end of file