From 5c1b1b07ce0718d71cfa955f1d0a49455950cad7 Mon Sep 17 00:00:00 2001 From: Byte Dice Date: Sat, 14 Dec 2024 13:11:23 +0100 Subject: [PATCH] added more args --- .../bytedice/bde_particles/Bde_particles.kt | 24 +- .../kotlin/com/bytedice/bde_particles/Math.kt | 1 - .../bde_particles/commands/ConfigArgs.kt | 333 ++++++++++++++---- .../bde_particles/commands/ManageEmitters.kt | 9 +- .../bde_particles/particles/EmitterParams.kt | 22 +- .../bde_particles/particles/ParamClasses.kt | 23 +- .../bde_particles/particles/Particle.kt | 4 +- .../particles/ParticleEmitter.kt | 13 +- .../particles/ParticleIdRegister.kt | 7 +- 9 files changed, 343 insertions(+), 93 deletions(-) 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 cfabaab..4a7def6 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt @@ -49,12 +49,11 @@ import java.util.concurrent.CompletableFuture // Allowing the use of regular Minecraft particles instead of just BDEs. // Cylinder and cone force field shape. // Copying particle params in-game +// Custom curve equation command args (parse from strings) // TODO: (project) // fix ManageEmitter command -// add SpawnEmitter command for spawning emitters via commands -// Custom curve equation command args (parse from strings) // Better command auto-completion. (and change names of args) // force field "config" option @@ -91,7 +90,7 @@ class Bde_particles : ModInitializer { CommandRegistrationCallback.EVENT.register { dispatcher, registryAccess, _ -> GiveEmitterTool.register(dispatcher, registryAccess) - ManageEmitters .register(dispatcher) + ManageEmitters .register(dispatcher, registryAccess) KillAllEmitters.register(dispatcher) SpawnEmitter .register(dispatcher) } @@ -196,7 +195,7 @@ fun displayEntityContainsTag(entity: ItemDisplayEntity, tag: String) : Boolean { } -fun emitterListArg (argName: String) : RequiredArgumentBuilder { +fun emitterListArg(argName: String) : RequiredArgumentBuilder { return CommandManager.argument(argName, StringArgumentType.string()) .suggests { _, builder -> idRegister.keys.forEach { key -> @@ -207,6 +206,23 @@ fun emitterListArg (argName: String) : RequiredArgumentBuilder { + return CommandManager.argument(argName, StringArgumentType.string()) + .suggests { _, builder -> + LerpCurves::class.nestedClasses.forEach { nestClass -> + builder.suggest(nestClass.simpleName) + } + CompletableFuture.completedFuture(builder.build()) + } +} + + +fun stringToCurve(curveName: String): LerpCurves? { + return LerpCurves::class.members + .filter { it.name == curveName }.firstNotNullOfOrNull { it.call(LerpCurves) as? LerpCurves } +} + + fun spawnEmitter(emitter: ParticleEmitter) { ALL_PARTICLE_EMITTERS += emitter } diff --git a/src/main/kotlin/com/bytedice/bde_particles/Math.kt b/src/main/kotlin/com/bytedice/bde_particles/Math.kt index b90c914..36fd467 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/Math.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/Math.kt @@ -15,7 +15,6 @@ import kotlin.random.Random class LerpCurves(val function: (Float) -> Float) { companion object { - val Constant = LerpCurves { _ -> 1.0f } val Linear = LerpCurves { y -> y } val Sqrt = LerpCurves { y -> sqrt(y) } val Exponent = LerpCurves { y -> y.pow(2.0f) } diff --git a/src/main/kotlin/com/bytedice/bde_particles/commands/ConfigArgs.kt b/src/main/kotlin/com/bytedice/bde_particles/commands/ConfigArgs.kt index 1f8a829..bb4aa6c 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/commands/ConfigArgs.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/commands/ConfigArgs.kt @@ -1,10 +1,10 @@ package com.bytedice.bde_particles.commands -import com.bytedice.bde_particles.particles.EmitterParams -import com.bytedice.bde_particles.particles.ParamClasses -import com.bytedice.bde_particles.particles.SpawningShape -import com.bytedice.bde_particles.particles.updateParam +import com.bytedice.bde_particles.curveListArg +import com.bytedice.bde_particles.negativeFeedback +import com.bytedice.bde_particles.particles.* import com.bytedice.bde_particles.positiveFeedback +import com.bytedice.bde_particles.stringToCurve import com.mojang.brigadier.Command import com.mojang.brigadier.arguments.BoolArgumentType import com.mojang.brigadier.arguments.FloatArgumentType @@ -14,13 +14,15 @@ import com.mojang.brigadier.builder.ArgumentBuilder import com.mojang.brigadier.builder.LiteralArgumentBuilder import com.mojang.brigadier.builder.RequiredArgumentBuilder import com.mojang.brigadier.context.CommandContext +import net.minecraft.command.CommandRegistryAccess +import net.minecraft.command.argument.ItemStackArgumentType import net.minecraft.server.command.CommandManager import net.minecraft.server.command.ServerCommandSource import org.joml.Vector2f import org.joml.Vector3f -import java.util.concurrent.CompletableFuture import kotlin.reflect.* import kotlin.reflect.full.memberProperties +import net.minecraft.server.command.GiveCommand typealias CommandArgumentBuilder = KFunction> @@ -28,7 +30,11 @@ typealias CommandArgumentBuilder = KFunction, rootArg: RequiredArgumentBuilder) : RequiredArgumentBuilder { +fun listConfigArgs(configArgs: Array, + rootArg: RequiredArgumentBuilder, + registryAccess: CommandRegistryAccess + ) : RequiredArgumentBuilder +{ configArgs.forEach { arg -> var configArg = CommandManager.literal(arg) @@ -36,11 +42,17 @@ fun listConfigArgs(configArgs: Array, rootArg: RequiredArgumentBuilder? } + if (func.parameters.lastIndex == 0) { + configArg.then(func.call(argAccess)) + } + else if (func.parameters.lastIndex == 1) { + configArg = func.call(argAccess, configArg) as LiteralArgumentBuilder? + } + else { + configArg = func.call(argAccess, configArg, registryAccess) as LiteralArgumentBuilder? + } } - // Add the configArg to the root command builder rootArg.then(configArg) } @@ -60,26 +72,23 @@ fun parseArgNameToAccess(name: String) : KProperty1? { return access } - +// TODO: add forceField & implement rest of args fun parseArgTypeToFunc(type: KType) : CommandArgumentBuilder? { - when (type.classifier) { - Int::class -> return ::intArg - Float::class -> return ::floatArg - String::class -> return ::stringArg - ParamClasses.Duration::class -> return ::durArg - Vector3f::class -> return ::vec3fArg - ParamClasses.PairInt::class -> return ::pairIntArg - SpawningShape::class -> return ::shapeArg - ParamClasses.PairVec3f::class -> println("Type is PairVec3f") - ParamClasses.PairFloat::class -> println("Type is PairFloat") - ParamClasses.TransformWithVel::class -> println("Type is TransformWithVel") - Array::class -> println("Type is Array") - ParamClasses.LerpVal::class -> println("Type is LerpVal") - Pair::class -> println("Type is Pair") - else -> return null + return when (type.classifier) { + Int::class -> ::intArg + Float::class -> ::floatArg + String::class -> ::stringArg + ParamClasses.Duration::class -> ::durArg + Vector3f::class -> ::vec3fArg + ParamClasses.PairInt::class -> ::pairIntArg + SpawningShape::class -> ::shapeArg + ParamClasses.PairVec3f::class -> ::pairVec3fArg + ParamClasses.PairFloat::class -> ::pairFloatArg + ParamClasses.TransformWithVel::class -> ::transformWithVelArg + ParamClasses.StringCurve::class -> ::stringCurveArg + ParamClasses.LerpVal::class -> null + else -> null } - - return null } @@ -134,14 +143,14 @@ fun durArg(access: KProperty1, return configArg .then(CommandManager.literal("SingleBurst") .then(CommandManager.argument("loopDur", IntegerArgumentType.integer()) - .executes { context -> durArgExec(access, context); Command.SINGLE_SUCCESS } + .executes { context -> durArgExec("SingleBurst", access, context); Command.SINGLE_SUCCESS } ) ) .then(CommandManager.literal("MultiBurst") .then(CommandManager.argument("loopDur", IntegerArgumentType.integer()) .then(CommandManager.argument("loopDelay", IntegerArgumentType.integer()) .then(CommandManager.argument("loopCount", IntegerArgumentType.integer()) - .executes { context -> durArgExec(access, context); Command.SINGLE_SUCCESS } + .executes { context -> durArgExec("MultiBurst", access, context); Command.SINGLE_SUCCESS } ) ) ) @@ -149,34 +158,33 @@ fun durArg(access: KProperty1, .then(CommandManager.literal("InfiniteLoop") .then(CommandManager.argument("loopDur", IntegerArgumentType.integer()) .then(CommandManager.argument("loopDelay", IntegerArgumentType.integer()) - .executes { context -> durArgExec(access, context); Command.SINGLE_SUCCESS } + .executes { context -> durArgExec("InfiniteLoop", access, context); Command.SINGLE_SUCCESS } ) ) ) } -fun durArgExec(access: KProperty1, context: CommandContext) { +fun durArgExec(paramName: String, access: KProperty1, context: CommandContext) { val id = StringArgumentType.getString(context, "Emitter ID") val loopDur = IntegerArgumentType.getInteger(context, "loopDur") - val eParams = EmitterParams.DEFAULT - when (access.get(eParams)) { - is ParamClasses.Duration.SingleBurst -> { + when (paramName) { + "SingleBurst" -> { updateParam(id, access, ParamClasses.Duration.SingleBurst(loopDur)) } - is ParamClasses.Duration.MultiBurst -> { + "MultiBurst" -> { val loopDelay = IntegerArgumentType.getInteger(context, "loopDelay") val loopCount = IntegerArgumentType.getInteger(context, "loopCount") updateParam(id, access, ParamClasses.Duration.MultiBurst(loopDur, loopDelay, loopCount)) } - is ParamClasses.Duration.InfiniteLoop -> { + "InfiniteLoop" -> { val loopDelay = IntegerArgumentType.getInteger(context, "loopDelay") updateParam(id, access, ParamClasses.Duration.InfiniteLoop(loopDur, loopDelay)) } } - successText(access, access.get(eParams)::class.simpleName ?: "UNKNOWN", id, context) + successText(access, access::class.simpleName ?: "UNKNOWN", id, context) } fun vec3fArg(access: KProperty1) : RequiredArgumentBuilder { @@ -199,12 +207,12 @@ fun vec3fArg(access: KProperty1) : RequiredArgumentBuil } fun pairIntArg(access: KProperty1) : RequiredArgumentBuilder { - return CommandManager.argument("First Int", IntegerArgumentType.integer()) - .then(CommandManager.argument("Second Int", IntegerArgumentType.integer()) + return CommandManager.argument("Min Int", IntegerArgumentType.integer()) + .then(CommandManager.argument("Max Int", IntegerArgumentType.integer()) .executes { context -> val id = StringArgumentType.getString(context, "Emitter ID") - val valueX = IntegerArgumentType.getInteger(context, "First Int") - val valueY = IntegerArgumentType.getInteger(context, "Second Int") + val valueX = IntegerArgumentType.getInteger(context, "Min Int") + val valueY = IntegerArgumentType.getInteger(context, "Max Int") updateParam(id, access, ParamClasses.PairInt(valueX, valueY)) successText(access, "[$valueX, $valueY]", id, context) @@ -221,7 +229,7 @@ fun shapeArg(access: KProperty1, .then(CommandManager.literal("Circle") .then(CommandManager.argument("Radius", FloatArgumentType.floatArg()) .then(CommandManager.argument("Spawn On Edge", BoolArgumentType.bool()) - .executes { context -> shapeArgExec(access, context); Command.SINGLE_SUCCESS } + .executes { context -> shapeArgExec("Circle", access, context); Command.SINGLE_SUCCESS } ) ) ) @@ -229,7 +237,7 @@ fun shapeArg(access: KProperty1, .then(CommandManager.argument("X", FloatArgumentType.floatArg()) .then(CommandManager.argument("Y", FloatArgumentType.floatArg()) .then(CommandManager.argument("Spawn On Edge", BoolArgumentType.bool()) - .executes { context -> shapeArgExec(access, context); Command.SINGLE_SUCCESS } + .executes { context -> shapeArgExec("Rect", access, context); Command.SINGLE_SUCCESS } ) ) ) @@ -239,7 +247,7 @@ fun shapeArg(access: KProperty1, .then(CommandManager.argument("Y", FloatArgumentType.floatArg()) .then(CommandManager.argument("Z", FloatArgumentType.floatArg()) .then(CommandManager.argument("Spawn On Edge", BoolArgumentType.bool()) - .executes { context -> shapeArgExec(access, context); Command.SINGLE_SUCCESS } + .executes { context -> shapeArgExec("Cube", access, context); Command.SINGLE_SUCCESS } ) ) ) @@ -248,51 +256,258 @@ fun shapeArg(access: KProperty1, .then(CommandManager.literal("Sphere") .then(CommandManager.argument("Radius", FloatArgumentType.floatArg()) .then(CommandManager.argument("Spawn On Edge", BoolArgumentType.bool()) - .executes { context -> shapeArgExec(access, context); Command.SINGLE_SUCCESS } + .executes { context -> shapeArgExec("Sphere", access, context); Command.SINGLE_SUCCESS } ) ) ) .then(CommandManager.literal("Point") - .executes { context -> shapeArgExec(access, context); Command.SINGLE_SUCCESS } + .executes { context -> shapeArgExec("Point", access, context); Command.SINGLE_SUCCESS } ) } -fun shapeArgExec(access: KProperty1, context: CommandContext) { +fun shapeArgExec(paramName: String, access: KProperty1, context: CommandContext) { val id = StringArgumentType.getString(context, "Emitter ID") - val eParams = EmitterParams.DEFAULT - - println(access.get(eParams)::class) - - when (access.get(eParams)) { - is SpawningShape.Circle -> { + when (paramName) { + "Circle" -> { val radius = FloatArgumentType.getFloat(context, "Radius") val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge") updateParam(id, access, SpawningShape.Circle(radius, onEdge)) } - is SpawningShape.Rect -> { + "Rect" -> { val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge") val x = FloatArgumentType.getFloat(context, "X") val y = FloatArgumentType.getFloat(context, "Y") updateParam(id, access, SpawningShape.Rect(Vector2f(x, y), onEdge)) } - is SpawningShape.Cube -> { + "Cube" -> { val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge") val x = FloatArgumentType.getFloat(context, "X") val y = FloatArgumentType.getFloat(context, "Y") val z = FloatArgumentType.getFloat(context, "Z") updateParam(id, access, SpawningShape.Cube(Vector3f(x, y, z), onEdge)) } - is SpawningShape.Sphere -> { + "Sphere" -> { val radius = FloatArgumentType.getFloat(context, "Radius") val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge") updateParam(id, access, SpawningShape.Sphere(radius, onEdge)) - println("$id, ${access.name}, $radius, $onEdge") } - is SpawningShape.Point -> { + "Point" -> { updateParam(id, access, SpawningShape.Point) } } - successText(access, access.get(eParams)::class.simpleName ?: "UNKNOWN", id, context) + successText(access, access::class.simpleName ?: "UNKNOWN", id, context) +} + +fun pairVec3fArg(access: KProperty1, + configArg: LiteralArgumentBuilder + ) : LiteralArgumentBuilder +{ + return configArg + .then(CommandManager.literal("NonUniform") + .then(CommandManager.argument("Min X", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Min Y", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Min Z", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Max X", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Max Y", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Max Z", FloatArgumentType.floatArg()) + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + val minX = FloatArgumentType.getFloat(context, "Min X") + val minY = FloatArgumentType.getFloat(context, "Min Y") + val minZ = FloatArgumentType.getFloat(context, "Min Z") + val maxX = FloatArgumentType.getFloat(context, "Max X") + val maxY = FloatArgumentType.getFloat(context, "Max Y") + val maxZ = FloatArgumentType.getFloat(context, "Max Z") + + updateParam(id, access, ParamClasses.PairVec3f.NonUniform(minX, minY, minZ, maxX, maxY, maxZ)) + + successText(access, "NonUniform", id, context) + Command.SINGLE_SUCCESS + } + ) + ) + ) + ) + ) + ) + ) + .then(CommandManager.literal("Uniform") + .then(CommandManager.argument("X", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Y", FloatArgumentType.floatArg()) + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + val min = FloatArgumentType.getFloat(context, "X") + val max = FloatArgumentType.getFloat(context, "Y") + + updateParam(id, access, ParamClasses.PairVec3f.Uniform(min, max)) + + successText(access, "Uniform", id, context) + Command.SINGLE_SUCCESS + } + ) + ) + ) + .then(CommandManager.literal("Null") + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + + updateParam(id, access, ParamClasses.PairVec3f.Null) + + successText(access, "Null", id, context) + Command.SINGLE_SUCCESS + } + ) +} + +fun pairFloatArg(access: KProperty1) : RequiredArgumentBuilder { + return CommandManager.argument("Min Float", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Max Float", FloatArgumentType.floatArg()) + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + val valueX = FloatArgumentType.getFloat(context, "Min Int") + val valueY = FloatArgumentType.getFloat(context, "Max Int") + + updateParam(id, access, ParamClasses.PairFloat(valueX, valueY)) + successText(access, "[$valueX, $valueY]", id, context) + Command.SINGLE_SUCCESS + } + ) +} + +fun transformWithVelArg(access: KProperty1, + configArg: LiteralArgumentBuilder + ) : LiteralArgumentBuilder +{ + return configArg + .then(CommandManager.literal("RotOnly") + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + + updateParam(id, access, ParamClasses.TransformWithVel.RotOnly) + successText(access, "RotOnly", id, context) + Command.SINGLE_SUCCESS + } + ) + .then(CommandManager.literal("ScaleAndRot") + .then(CommandManager.argument("Scale Multiplier", FloatArgumentType.floatArg()) + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + val scaleMul = FloatArgumentType.getFloat(context, "Scale Multiplier") + + updateParam(id, access, ParamClasses.TransformWithVel.ScaleAndRot(scaleMul)) + successText(access, "ScaleAndRot", id, context) + Command.SINGLE_SUCCESS + } + ) + ) + .then(CommandManager.literal("None") + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + + updateParam(id, access, ParamClasses.TransformWithVel.None) + successText(access, "None", id, context) + Command.SINGLE_SUCCESS + } + ) +} + +fun stringCurveArg(access: KProperty1, + configArg: LiteralArgumentBuilder, + registryAccess: CommandRegistryAccess + ) : LiteralArgumentBuilder +{ + return configArg + .then(CommandManager.literal("Add") + .then(CommandManager.argument("Item", ItemStackArgumentType.itemStack(registryAccess)) + .then(CommandManager.argument("Index", IntegerArgumentType.integer()) + .executes { context -> + val index = IntegerArgumentType.getInteger(context, "Index") + stringCurveAddArg(context, access, index) + Command.SINGLE_SUCCESS + } + ) + .executes { context -> + stringCurveAddArg(context, access, -1) + Command.SINGLE_SUCCESS + } + ) + ) + .then(CommandManager.literal("Remove") + .then(CommandManager.argument("Index", IntegerArgumentType.integer()) + .executes { context -> + val index = IntegerArgumentType.getInteger(context, "Index") + stringCurveRemoveArg(context, access, index) + Command.SINGLE_SUCCESS + } + ) + .executes { context -> + stringCurveRemoveArg(context, access, -1) + Command.SINGLE_SUCCESS + } + ) + .then(CommandManager.literal("Curve") + .then(curveListArg("Curve") + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + val curveName = StringArgumentType.getString(context, "Curve") + + val curve = stringToCurve(curveName) + val modelCurve = getEmitterDataById(id)?.modelCurve + + if (curve != null && modelCurve != null) { + modelCurve.curve = curve + updateParam(id, access, modelCurve) + successText(access, curveName, id, context) + Command.SINGLE_SUCCESS + } + else { + negativeFeedback("Failed to update param \"modelCurve.curve\"! The curve is most likely invalid!", context) + Command.SINGLE_SUCCESS + } + } + ) + ) +} + +fun stringCurveAddArg(context: CommandContext, + access: KProperty1, + index: Int +) { + val id = StringArgumentType.getString(context, "Emitter ID") + val item = ItemStackArgumentType.getItemStackArgument(context, "Item").item.name.string + + val modelCurve = getEmitterDataById(id)?.modelCurve + + if (index == -1 && modelCurve != null) { + modelCurve.array.toMutableList().apply { add(item) }.toTypedArray() + updateParam(id, access, modelCurve) + } + else if (modelCurve != null) { + modelCurve.array.toMutableList().apply { add(index.coerceIn(0, modelCurve.array.lastIndex), item) }.toTypedArray() + updateParam(id, access, modelCurve) + } + + successText(access, "++$item", id, context) +} + +fun stringCurveRemoveArg(context: CommandContext, + access: KProperty1, + index: Int +) { + val id = StringArgumentType.getString(context, "Emitter ID") + + val modelCurve = getEmitterDataById(id)?.modelCurve + + if (index == -1 && modelCurve != null) { + modelCurve.array.toMutableList().apply { removeLast() }.toTypedArray() + updateParam(id, access, modelCurve) + } + else if (modelCurve != null) { + modelCurve.array.toMutableList().apply { removeAt(index.coerceIn(0, modelCurve.array.lastIndex)) }.toTypedArray() + updateParam(id, access, modelCurve) + } + + successText(access, "--item at $index", id, context) } \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt b/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt index 879ae5a..da71609 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt @@ -9,6 +9,7 @@ import com.mojang.brigadier.CommandDispatcher import com.mojang.brigadier.arguments.StringArgumentType import com.mojang.brigadier.builder.RequiredArgumentBuilder import com.mojang.brigadier.context.CommandContext +import net.minecraft.command.CommandRegistryAccess import net.minecraft.server.command.CommandManager import net.minecraft.server.command.ServerCommandSource import net.minecraft.text.Style @@ -77,7 +78,7 @@ blockCurve: Pair, LerpCurves>, object ManageEmitters { - fun register(dispatcher: CommandDispatcher) { + fun register(dispatcher: CommandDispatcher, registryAccess: CommandRegistryAccess) { val command = CommandManager.literal("ManageEmitters") .requires { source -> source.hasPermissionLevel(2) } @@ -85,7 +86,7 @@ object ManageEmitters { command .then(CommandManager.literal("create").then(create())) .then(CommandManager.literal("remove").then(remove())) - .then(CommandManager.literal("config").then(config())) + .then(CommandManager.literal("config").then(config(registryAccess))) .then(CommandManager.literal("list") .executes { context -> val emitterIdList = idRegister.keys.sorted().joinToString(" ") @@ -173,10 +174,10 @@ object ManageEmitters { } } - private fun config() : RequiredArgumentBuilder { + private fun config(registryAccess: CommandRegistryAccess) : RequiredArgumentBuilder { var emitterList = emitterListArg("Emitter ID") - emitterList = listConfigArgs(dataClassToArray(EmitterParams::class), emitterList) as RequiredArgumentBuilder + emitterList = listConfigArgs(dataClassToArray(EmitterParams::class), emitterList, registryAccess) as RequiredArgumentBuilder return emitterList } diff --git a/src/main/kotlin/com/bytedice/bde_particles/particles/EmitterParams.kt b/src/main/kotlin/com/bytedice/bde_particles/particles/EmitterParams.kt index 1adcf8a..1afbeca 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/EmitterParams.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/EmitterParams.kt @@ -30,7 +30,7 @@ data class EmitterParams ( var offsetCurve: ParamClasses.LerpVal, var rotVelCurve: ParamClasses.LerpVal, var scaleCurve: ParamClasses.LerpVal, - var modelCurve: Pair, LerpCurves>, + var modelCurve: ParamClasses.StringCurve, ) { companion object Presets { @@ -55,8 +55,8 @@ data class EmitterParams ( minVel = 0.0f, offsetCurve = ParamClasses.LerpVal.NoLerpVec3f, rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Sqrt), - scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 2.5f, LerpCurves.Constant), - modelCurve = Pair( + scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 2.5f, LerpCurves.Linear), + modelCurve = ParamClasses.StringCurve( arrayOf( "shroomlight", "orange_concrete", @@ -88,9 +88,9 @@ data class EmitterParams ( drag = 0.075f, minVel = 0.0f, offsetCurve = ParamClasses.LerpVal.NoLerpVec3f, - rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Constant), - scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 1.0f, LerpCurves.Constant), - modelCurve = Pair( + rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Linear), + scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 1.0f, LerpCurves.Linear), + modelCurve = ParamClasses.StringCurve( arrayOf( "shroomlight", "orange_concrete", @@ -99,7 +99,7 @@ data class EmitterParams ( "gray_stained_glass", "light_gray_stained_glass" ), - LerpCurves.Constant + LerpCurves.Linear ) ) val STRESS_TEST = EmitterParams( @@ -126,9 +126,9 @@ data class EmitterParams ( drag = 0.075f, minVel = 0.0f, offsetCurve = ParamClasses.LerpVal.NoLerpVec3f, - rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Constant), - scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 1.5f, LerpCurves.Constant), - modelCurve = Pair( + rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Linear), + scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 1.5f, LerpCurves.Linear), + modelCurve = ParamClasses.StringCurve( arrayOf( "shroomlight", "orange_concrete", @@ -137,7 +137,7 @@ data class EmitterParams ( "gray_stained_glass", "light_gray_stained_glass" ), - LerpCurves.Constant + LerpCurves.Linear ) ) } diff --git a/src/main/kotlin/com/bytedice/bde_particles/particles/ParamClasses.kt b/src/main/kotlin/com/bytedice/bde_particles/particles/ParamClasses.kt index 5f5533d..ec18625 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/ParamClasses.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/ParamClasses.kt @@ -1,6 +1,7 @@ package com.bytedice.bde_particles.particles import com.bytedice.bde_particles.LerpCurves +import com.bytedice.bde_particles.lerpArray import com.bytedice.bde_particles.randomFloatBetween import com.bytedice.bde_particles.randomIntBetween import org.joml.Vector3f @@ -60,9 +61,9 @@ class ParamClasses { private val curve: LerpCurves, ) : LerpVal() { fun lerp(t: Float): Vector3f { - val x = com.bytedice.bde_particles.lerp(fromX, toX, t) * curve.function(t) - val y = com.bytedice.bde_particles.lerp(fromY, toY, t) * curve.function(t) - val z = com.bytedice.bde_particles.lerp(fromZ, toZ, t) * curve.function(t) + val x = com.bytedice.bde_particles.lerp(fromX, toX, curve.function(t)) + val y = com.bytedice.bde_particles.lerp(fromY, toY, curve.function(t)) + val z = com.bytedice.bde_particles.lerp(fromZ, toZ, curve.function(t)) return Vector3f(x, y, z) } } @@ -78,9 +79,9 @@ class ParamClasses { private val curveZ: LerpCurves ) : LerpVal() { fun lerp(t: Float): Vector3f { - val x = com.bytedice.bde_particles.lerp(fromX, toX, t) * curveX.function(t) - val y = com.bytedice.bde_particles.lerp(fromY, toY, t) * curveY.function(t) - val z = com.bytedice.bde_particles.lerp(fromZ, toZ, t) * curveZ.function(t) + val x = com.bytedice.bde_particles.lerp(fromX, toX, curveX.function(t)) + val y = com.bytedice.bde_particles.lerp(fromY, toY, curveY.function(t)) + val z = com.bytedice.bde_particles.lerp(fromZ, toZ, curveZ.function(t)) return Vector3f(x, y, z) } } @@ -89,8 +90,8 @@ class ParamClasses { private val to: Float, private val curve: LerpCurves, ) : LerpVal() { - fun lerp(t: Float): Float { - return com.bytedice.bde_particles.lerp(from, to, t) * curve.function(t) + fun lerp(t: Float) : Float { + return com.bytedice.bde_particles.lerp(from, to, curve.function(t)) } } data object NoLerpVec3f : LerpVal() @@ -155,4 +156,10 @@ class ParamClasses { } data object None : TransformWithVel() } + class StringCurve ( + var array: Array, + var curve: LerpCurves + ) { + fun lerp(t: Float) : String { return lerpArray(array as Array, t, curve) as String } + } } \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/particles/Particle.kt b/src/main/kotlin/com/bytedice/bde_particles/particles/Particle.kt index 807cff3..e9eeba3 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/Particle.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/Particle.kt @@ -75,7 +75,7 @@ class Particle( val properties = DisplayEntityProperties( pos = entityPos, rot = entityRot, - model = lerpArray(emitterParams.modelCurve.first as Array, 0.0f, emitterParams.modelCurve.second) as String, + model = emitterParams.modelCurve.lerp(0.0f), translation = newPos.add(pos), leftRotation = quatRot, scale = Vector3f(scale).mul(emitterParams.scaleCurve.lerpToVector3f(0.0f)), @@ -130,7 +130,7 @@ class Particle( private fun calcNewProperties() : DisplayEntityProperties { val timeAliveClamped = (timeAlive.toFloat() / lifeTime.toFloat()).coerceIn(0.0f, 1.0f) - val newModel = lerpArray(emitterParams.modelCurve.first as Array, timeAliveClamped, emitterParams.modelCurve.second) as String + val newModel = emitterParams.modelCurve.lerp(timeAliveClamped) val (newVel, newPos) = calcPos() val newOriginOffset = Vector3f(originOffset.mul(emitterParams.offsetCurve.lerpToVector3f(timeAliveClamped))) diff --git a/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleEmitter.kt b/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleEmitter.kt index 613d3c9..e662291 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleEmitter.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleEmitter.kt @@ -98,10 +98,17 @@ class ParticleEmitter( val loopDelay = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopDelay val loopCount = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopCount - if (timeAlive >= loopDur && timesLooped >= loopCount) { isTicking = false; return } + if (timeAlive >= loopDur && timesLooped + 1 >= loopCount) { isTicking = false; return } - if (timePaused >= loopDelay) { isPaused = false; timePaused = 0 } - if (timeAlive >= loopDur && loopDelay > 0) { isPaused = true; timeAlive = 0 } + if (timePaused >= loopDelay) { + isPaused = false + timePaused = 0 + timesLooped += 1 + } + if (timeAlive >= loopDur && loopDelay > 0) { + isPaused = true + timeAlive = 0 + } if (!isPaused) { timeAlive += 1 } else { timePaused += 1 } diff --git a/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt b/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt index 7caade6..d11d78b 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt @@ -1,7 +1,9 @@ package com.bytedice.bde_particles.particles +import kotlin.reflect.KClass import kotlin.reflect.KMutableProperty1 import kotlin.reflect.KProperty1 +import kotlin.reflect.full.isSubclassOf import kotlin.reflect.full.memberProperties import kotlin.reflect.jvm.isAccessible @@ -80,7 +82,10 @@ fun updateParam(id: String, paramAccess: KProperty1, paramValu fun updateParams(params: EmitterParams, paramAccess: KProperty1, paramValue: Any): Pair { val newParams = params.copy() - if (paramAccess.returnType.classifier == paramValue::class) { + if (paramAccess.returnType.classifier == paramValue::class + || (paramAccess.returnType.classifier as? KClass<*>)?.let { paramValue::class.isSubclassOf(it) } == true + ) + { paramAccess as KMutableProperty1 paramAccess.set(newParams, paramValue) return Pair(newParams, true)