fixed bugs, added new command

This commit is contained in:
2024-11-23 02:07:23 +01:00
parent 71654bd491
commit 98955f7e5d
8 changed files with 112 additions and 47 deletions
@@ -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<Vector3f>'s to Triple<V3f, V3f, EASING>
// TODO: kill all particles command
var ALL_PARTICLE_EMITTERS: Array<ParticleEmitter> = 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 ->
@@ -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<Vector3f>, 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
}
@@ -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<ServerCommandSource>) {
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
}
)
}
}
@@ -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<ParticleParams> = arrayOf(ParticleParams()),
)
{
@@ -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
}
@@ -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
}
}
@@ -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<String>
"rotRandom" -> paramValue as Pair<Vector3f, Vector3f>
"rotVelRandom" -> paramValue as Pair<Vector3f, Vector3f>
"rotVelCurve" -> paramValue as Array<Vector3f>
"sizeRandom" -> paramValue as Pair<Vector3f, Vector3f>
"uniformSize" -> paramValue as Boolean
"sizeCurve" -> paramValue as Array<Vector3f>
"velRandom" -> paramValue as Pair<Vector3f, Vector3f>
"forceFields" -> paramValue as Array<ForceField>
"gravity" -> paramValue as Vector3f
"drag" -> paramValue as Float
"minVel" -> paramValue as Float
"lifeTime" -> paramValue as Pair<Int, Int>
"rotVelCurve" -> paramValue as Triple<Vector3f, Vector3f, InterpolationCurves>
"sizeCurve" -> paramValue as Triple<Vector3f, Vector3f, InterpolationCurves>
else -> null
}
@@ -119,16 +120,16 @@ fun updateSingleParticleParam(params: ParticleParams, paramName: String, paramVa
"blockCurve" -> newParams.blockCurve = value as Array<String>
"rotRandom" -> newParams.rotRandom = value as Pair<Vector3f, Vector3f>
"rotVelRandom" -> newParams.rotVelRandom = value as Pair<Vector3f, Vector3f>
"rotVelCurve" -> newParams.rotVelCurve = value as Array<Vector3f>
"sizeRandom" -> newParams.sizeRandom = value as Pair<Vector3f, Vector3f>
"uniformSize" -> newParams.uniformSize = value as Boolean
"sizeCurve" -> newParams.sizeCurve = value as Array<Vector3f>
"velRandom" -> newParams.velRandom = value as Pair<Vector3f, Vector3f>
"forceFields" -> newParams.forceFields = value as Array<ForceField>
"gravity" -> newParams.gravity = value as Vector3f
"drag" -> newParams.drag = value as Float
"minVel" -> newParams.minVel = value as Float
"lifeTime" -> newParams.lifeTime = value as Pair<Int, Int>
"rotVelCurve" -> newParams.rotVelCurve = value as Triple<Vector3f, Vector3f, InterpolationCurves>
"sizeCurve" -> newParams.sizeCurve = value as Triple<Vector3f, Vector3f, InterpolationCurves>
else -> return Pair(params, false)
}
@@ -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<String> = arrayOf("minecraft:shroomlight", "minecraft:orange_concrete", "minecraft:orange_stained_glass", "minecraft:gray_stained_glass", "minecraft:light_gray_stained_glass"),
var rotRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(0.0f, 0.0f, 0.0f), Vector3f(360.0f, 360.0f, 360.0f)),
var rotVelRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(-0.2f, -0.2f, -0.2f), Vector3f(0.2f, 0.2f, 0.2f)),
var rotVelCurve: Array<Vector3f> = arrayOf(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f)),
var sizeRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(0.5f, 0.5f, 0.5f), Vector3f(1.0f, 1.0f, 1.0f)),
var uniformSize: Boolean = true,
var sizeCurve: Array<Vector3f> = arrayOf(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.5f, 0.5f, 0.5f)),
var velRandom: Pair<Vector3f, Vector3f> = Pair(Vector3f(-0.1f, 0.4f, -0.1f), Vector3f(0.1f, 0.8f, 0.1f)),
var forceFields: Array<ForceField> = arrayOf(ForceField()), // emptyArray(),
var forceFields: Array<ForceField> = emptyArray(),
var gravity: Vector3f = Vector3f(0.0f, -0.01f, 0.0f),
var drag: Float = 0.075f,
var minVel: Float = 0.0f,
var lifeTime: Pair<Int, Int> = Pair(15, 45)
var lifeTime: Pair<Int, Int> = Pair(15, 45),
var rotVelCurve: Triple<Vector3f, Vector3f, InterpolationCurves> = Triple(Vector3f(1.0f, 1.0f, 1.0f), Vector3f(0.0f, 0.0f, 0.0f), InterpolationCurves.SQRT),
var sizeCurve: Triple<Vector3f, Vector3f, InterpolationCurves> = 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)
)
}
}