added more args

This commit is contained in:
2024-12-14 13:11:23 +01:00
parent 46cc808889
commit 5c1b1b07ce
9 changed files with 343 additions and 93 deletions
@@ -49,12 +49,11 @@ import java.util.concurrent.CompletableFuture
// Allowing the use of regular Minecraft particles instead of just BDEs. // Allowing the use of regular Minecraft particles instead of just BDEs.
// Cylinder and cone force field shape. // Cylinder and cone force field shape.
// Copying particle params in-game // Copying particle params in-game
// Custom curve equation command args (parse from strings)
// TODO: (project) // TODO: (project)
// fix ManageEmitter command // 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) // Better command auto-completion. (and change names of args)
// force field "config" option // force field "config" option
@@ -91,7 +90,7 @@ class Bde_particles : ModInitializer {
CommandRegistrationCallback.EVENT.register { dispatcher, registryAccess, _ -> CommandRegistrationCallback.EVENT.register { dispatcher, registryAccess, _ ->
GiveEmitterTool.register(dispatcher, registryAccess) GiveEmitterTool.register(dispatcher, registryAccess)
ManageEmitters .register(dispatcher) ManageEmitters .register(dispatcher, registryAccess)
KillAllEmitters.register(dispatcher) KillAllEmitters.register(dispatcher)
SpawnEmitter .register(dispatcher) SpawnEmitter .register(dispatcher)
} }
@@ -207,6 +206,23 @@ fun emitterListArg (argName: String) : RequiredArgumentBuilder<ServerCommandSour
} }
fun curveListArg(argName: String) : RequiredArgumentBuilder<ServerCommandSource, String> {
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) { fun spawnEmitter(emitter: ParticleEmitter) {
ALL_PARTICLE_EMITTERS += emitter ALL_PARTICLE_EMITTERS += emitter
} }
@@ -15,7 +15,6 @@ import kotlin.random.Random
class LerpCurves(val function: (Float) -> Float) { class LerpCurves(val function: (Float) -> Float) {
companion object { companion object {
val Constant = LerpCurves { _ -> 1.0f }
val Linear = LerpCurves { y -> y } val Linear = LerpCurves { y -> y }
val Sqrt = LerpCurves { y -> sqrt(y) } val Sqrt = LerpCurves { y -> sqrt(y) }
val Exponent = LerpCurves { y -> y.pow(2.0f) } val Exponent = LerpCurves { y -> y.pow(2.0f) }
@@ -1,10 +1,10 @@
package com.bytedice.bde_particles.commands package com.bytedice.bde_particles.commands
import com.bytedice.bde_particles.particles.EmitterParams import com.bytedice.bde_particles.curveListArg
import com.bytedice.bde_particles.particles.ParamClasses import com.bytedice.bde_particles.negativeFeedback
import com.bytedice.bde_particles.particles.SpawningShape import com.bytedice.bde_particles.particles.*
import com.bytedice.bde_particles.particles.updateParam
import com.bytedice.bde_particles.positiveFeedback import com.bytedice.bde_particles.positiveFeedback
import com.bytedice.bde_particles.stringToCurve
import com.mojang.brigadier.Command import com.mojang.brigadier.Command
import com.mojang.brigadier.arguments.BoolArgumentType import com.mojang.brigadier.arguments.BoolArgumentType
import com.mojang.brigadier.arguments.FloatArgumentType 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.LiteralArgumentBuilder
import com.mojang.brigadier.builder.RequiredArgumentBuilder import com.mojang.brigadier.builder.RequiredArgumentBuilder
import com.mojang.brigadier.context.CommandContext 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.CommandManager
import net.minecraft.server.command.ServerCommandSource import net.minecraft.server.command.ServerCommandSource
import org.joml.Vector2f import org.joml.Vector2f
import org.joml.Vector3f import org.joml.Vector3f
import java.util.concurrent.CompletableFuture
import kotlin.reflect.* import kotlin.reflect.*
import kotlin.reflect.full.memberProperties import kotlin.reflect.full.memberProperties
import net.minecraft.server.command.GiveCommand
typealias CommandArgumentBuilder = KFunction<ArgumentBuilder<ServerCommandSource, *>> typealias CommandArgumentBuilder = KFunction<ArgumentBuilder<ServerCommandSource, *>>
@@ -28,7 +30,11 @@ typealias CommandArgumentBuilder = KFunction<ArgumentBuilder<ServerCommandSource
// TODO: doesn't apply shape & duration. Error applying shape other than circle // TODO: doesn't apply shape & duration. Error applying shape other than circle
fun listConfigArgs(configArgs: Array<String>, rootArg: RequiredArgumentBuilder<ServerCommandSource, *>) : RequiredArgumentBuilder<ServerCommandSource, *> { fun listConfigArgs(configArgs: Array<String>,
rootArg: RequiredArgumentBuilder<ServerCommandSource, *>,
registryAccess: CommandRegistryAccess
) : RequiredArgumentBuilder<ServerCommandSource, *>
{
configArgs.forEach { arg -> configArgs.forEach { arg ->
var configArg = CommandManager.literal(arg) var configArg = CommandManager.literal(arg)
@@ -36,11 +42,17 @@ fun listConfigArgs(configArgs: Array<String>, rootArg: RequiredArgumentBuilder<S
val func = parseArgTypeToFunc(argAccess.returnType) val func = parseArgTypeToFunc(argAccess.returnType)
if (func != null) { if (func != null) {
if (func.parameters.lastIndex == 0) { configArg.then(func.call(argAccess)) } if (func.parameters.lastIndex == 0) {
else { configArg = func.call(argAccess, configArg) as LiteralArgumentBuilder<ServerCommandSource>? } configArg.then(func.call(argAccess))
}
else if (func.parameters.lastIndex == 1) {
configArg = func.call(argAccess, configArg) as LiteralArgumentBuilder<ServerCommandSource>?
}
else {
configArg = func.call(argAccess, configArg, registryAccess) as LiteralArgumentBuilder<ServerCommandSource>?
}
} }
// Add the configArg to the root command builder
rootArg.then(configArg) rootArg.then(configArg)
} }
@@ -60,26 +72,23 @@ fun parseArgNameToAccess(name: String) : KProperty1<EmitterParams, *>? {
return access return access
} }
// TODO: add forceField & implement rest of args
fun parseArgTypeToFunc(type: KType) : CommandArgumentBuilder? { fun parseArgTypeToFunc(type: KType) : CommandArgumentBuilder? {
when (type.classifier) { return when (type.classifier) {
Int::class -> return ::intArg Int::class -> ::intArg
Float::class -> return ::floatArg Float::class -> ::floatArg
String::class -> return ::stringArg String::class -> ::stringArg
ParamClasses.Duration::class -> return ::durArg ParamClasses.Duration::class -> ::durArg
Vector3f::class -> return ::vec3fArg Vector3f::class -> ::vec3fArg
ParamClasses.PairInt::class -> return ::pairIntArg ParamClasses.PairInt::class -> ::pairIntArg
SpawningShape::class -> return ::shapeArg SpawningShape::class -> ::shapeArg
ParamClasses.PairVec3f::class -> println("Type is PairVec3f") ParamClasses.PairVec3f::class -> ::pairVec3fArg
ParamClasses.PairFloat::class -> println("Type is PairFloat") ParamClasses.PairFloat::class -> ::pairFloatArg
ParamClasses.TransformWithVel::class -> println("Type is TransformWithVel") ParamClasses.TransformWithVel::class -> ::transformWithVelArg
Array::class -> println("Type is Array") ParamClasses.StringCurve::class -> ::stringCurveArg
ParamClasses.LerpVal::class -> println("Type is LerpVal") ParamClasses.LerpVal::class -> null
Pair::class -> println("Type is Pair") else -> null
else -> return null
} }
return null
} }
@@ -134,14 +143,14 @@ fun durArg(access: KProperty1<EmitterParams, ParamClasses.Duration>,
return configArg return configArg
.then(CommandManager.literal("SingleBurst") .then(CommandManager.literal("SingleBurst")
.then(CommandManager.argument("loopDur", IntegerArgumentType.integer()) .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.literal("MultiBurst")
.then(CommandManager.argument("loopDur", IntegerArgumentType.integer()) .then(CommandManager.argument("loopDur", IntegerArgumentType.integer())
.then(CommandManager.argument("loopDelay", IntegerArgumentType.integer()) .then(CommandManager.argument("loopDelay", IntegerArgumentType.integer())
.then(CommandManager.argument("loopCount", 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<EmitterParams, ParamClasses.Duration>,
.then(CommandManager.literal("InfiniteLoop") .then(CommandManager.literal("InfiniteLoop")
.then(CommandManager.argument("loopDur", IntegerArgumentType.integer()) .then(CommandManager.argument("loopDur", IntegerArgumentType.integer())
.then(CommandManager.argument("loopDelay", 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<EmitterParams, ParamClasses.Duration>, context: CommandContext<ServerCommandSource>) { fun durArgExec(paramName: String, access: KProperty1<EmitterParams, ParamClasses.Duration>, context: CommandContext<ServerCommandSource>) {
val id = StringArgumentType.getString(context, "Emitter ID") val id = StringArgumentType.getString(context, "Emitter ID")
val loopDur = IntegerArgumentType.getInteger(context, "loopDur") val loopDur = IntegerArgumentType.getInteger(context, "loopDur")
val eParams = EmitterParams.DEFAULT
when (access.get(eParams)) { when (paramName) {
is ParamClasses.Duration.SingleBurst -> { "SingleBurst" -> {
updateParam(id, access, ParamClasses.Duration.SingleBurst(loopDur)) updateParam(id, access, ParamClasses.Duration.SingleBurst(loopDur))
} }
is ParamClasses.Duration.MultiBurst -> { "MultiBurst" -> {
val loopDelay = IntegerArgumentType.getInteger(context, "loopDelay") val loopDelay = IntegerArgumentType.getInteger(context, "loopDelay")
val loopCount = IntegerArgumentType.getInteger(context, "loopCount") val loopCount = IntegerArgumentType.getInteger(context, "loopCount")
updateParam(id, access, ParamClasses.Duration.MultiBurst(loopDur, loopDelay, loopCount)) updateParam(id, access, ParamClasses.Duration.MultiBurst(loopDur, loopDelay, loopCount))
} }
is ParamClasses.Duration.InfiniteLoop -> { "InfiniteLoop" -> {
val loopDelay = IntegerArgumentType.getInteger(context, "loopDelay") val loopDelay = IntegerArgumentType.getInteger(context, "loopDelay")
updateParam(id, access, ParamClasses.Duration.InfiniteLoop(loopDur, 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<EmitterParams, Vector3f>) : RequiredArgumentBuilder<ServerCommandSource, Float> { fun vec3fArg(access: KProperty1<EmitterParams, Vector3f>) : RequiredArgumentBuilder<ServerCommandSource, Float> {
@@ -199,12 +207,12 @@ fun vec3fArg(access: KProperty1<EmitterParams, Vector3f>) : RequiredArgumentBuil
} }
fun pairIntArg(access: KProperty1<EmitterParams, ParamClasses.PairInt>) : RequiredArgumentBuilder<ServerCommandSource, Int> { fun pairIntArg(access: KProperty1<EmitterParams, ParamClasses.PairInt>) : RequiredArgumentBuilder<ServerCommandSource, Int> {
return CommandManager.argument("First Int", IntegerArgumentType.integer()) return CommandManager.argument("Min Int", IntegerArgumentType.integer())
.then(CommandManager.argument("Second Int", IntegerArgumentType.integer()) .then(CommandManager.argument("Max Int", IntegerArgumentType.integer())
.executes { context -> .executes { context ->
val id = StringArgumentType.getString(context, "Emitter ID") val id = StringArgumentType.getString(context, "Emitter ID")
val valueX = IntegerArgumentType.getInteger(context, "First Int") val valueX = IntegerArgumentType.getInteger(context, "Min Int")
val valueY = IntegerArgumentType.getInteger(context, "Second Int") val valueY = IntegerArgumentType.getInteger(context, "Max Int")
updateParam(id, access, ParamClasses.PairInt(valueX, valueY)) updateParam(id, access, ParamClasses.PairInt(valueX, valueY))
successText(access, "[$valueX, $valueY]", id, context) successText(access, "[$valueX, $valueY]", id, context)
@@ -221,7 +229,7 @@ fun shapeArg(access: KProperty1<EmitterParams, SpawningShape>,
.then(CommandManager.literal("Circle") .then(CommandManager.literal("Circle")
.then(CommandManager.argument("Radius", FloatArgumentType.floatArg()) .then(CommandManager.argument("Radius", FloatArgumentType.floatArg())
.then(CommandManager.argument("Spawn On Edge", BoolArgumentType.bool()) .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<EmitterParams, SpawningShape>,
.then(CommandManager.argument("X", FloatArgumentType.floatArg()) .then(CommandManager.argument("X", FloatArgumentType.floatArg())
.then(CommandManager.argument("Y", FloatArgumentType.floatArg()) .then(CommandManager.argument("Y", FloatArgumentType.floatArg())
.then(CommandManager.argument("Spawn On Edge", BoolArgumentType.bool()) .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<EmitterParams, SpawningShape>,
.then(CommandManager.argument("Y", FloatArgumentType.floatArg()) .then(CommandManager.argument("Y", FloatArgumentType.floatArg())
.then(CommandManager.argument("Z", FloatArgumentType.floatArg()) .then(CommandManager.argument("Z", FloatArgumentType.floatArg())
.then(CommandManager.argument("Spawn On Edge", BoolArgumentType.bool()) .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<EmitterParams, SpawningShape>,
.then(CommandManager.literal("Sphere") .then(CommandManager.literal("Sphere")
.then(CommandManager.argument("Radius", FloatArgumentType.floatArg()) .then(CommandManager.argument("Radius", FloatArgumentType.floatArg())
.then(CommandManager.argument("Spawn On Edge", BoolArgumentType.bool()) .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") .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<EmitterParams, SpawningShape>, context: CommandContext<ServerCommandSource>) { fun shapeArgExec(paramName: String, access: KProperty1<EmitterParams, SpawningShape>, context: CommandContext<ServerCommandSource>) {
val id = StringArgumentType.getString(context, "Emitter ID") val id = StringArgumentType.getString(context, "Emitter ID")
val eParams = EmitterParams.DEFAULT when (paramName) {
"Circle" -> {
println(access.get(eParams)::class)
when (access.get(eParams)) {
is SpawningShape.Circle -> {
val radius = FloatArgumentType.getFloat(context, "Radius") val radius = FloatArgumentType.getFloat(context, "Radius")
val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge") val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge")
updateParam(id, access, SpawningShape.Circle(radius, onEdge)) updateParam(id, access, SpawningShape.Circle(radius, onEdge))
} }
is SpawningShape.Rect -> { "Rect" -> {
val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge") val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge")
val x = FloatArgumentType.getFloat(context, "X") val x = FloatArgumentType.getFloat(context, "X")
val y = FloatArgumentType.getFloat(context, "Y") val y = FloatArgumentType.getFloat(context, "Y")
updateParam(id, access, SpawningShape.Rect(Vector2f(x, y), onEdge)) updateParam(id, access, SpawningShape.Rect(Vector2f(x, y), onEdge))
} }
is SpawningShape.Cube -> { "Cube" -> {
val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge") val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge")
val x = FloatArgumentType.getFloat(context, "X") val x = FloatArgumentType.getFloat(context, "X")
val y = FloatArgumentType.getFloat(context, "Y") val y = FloatArgumentType.getFloat(context, "Y")
val z = FloatArgumentType.getFloat(context, "Z") val z = FloatArgumentType.getFloat(context, "Z")
updateParam(id, access, SpawningShape.Cube(Vector3f(x, y, z), onEdge)) updateParam(id, access, SpawningShape.Cube(Vector3f(x, y, z), onEdge))
} }
is SpawningShape.Sphere -> { "Sphere" -> {
val radius = FloatArgumentType.getFloat(context, "Radius") val radius = FloatArgumentType.getFloat(context, "Radius")
val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge") val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge")
updateParam(id, access, SpawningShape.Sphere(radius, onEdge)) updateParam(id, access, SpawningShape.Sphere(radius, onEdge))
println("$id, ${access.name}, $radius, $onEdge")
} }
is SpawningShape.Point -> { "Point" -> {
updateParam(id, access, SpawningShape.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<EmitterParams,ParamClasses.PairVec3f>,
configArg: LiteralArgumentBuilder<ServerCommandSource>
) : LiteralArgumentBuilder<ServerCommandSource>
{
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<EmitterParams, ParamClasses.PairInt>) : RequiredArgumentBuilder<ServerCommandSource, Float> {
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<EmitterParams, ParamClasses.TransformWithVel>,
configArg: LiteralArgumentBuilder<ServerCommandSource>
) : LiteralArgumentBuilder<ServerCommandSource>
{
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<EmitterParams, ParamClasses.StringCurve>,
configArg: LiteralArgumentBuilder<ServerCommandSource>,
registryAccess: CommandRegistryAccess
) : LiteralArgumentBuilder<ServerCommandSource>
{
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<ServerCommandSource>,
access: KProperty1<EmitterParams, ParamClasses.StringCurve>,
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<ServerCommandSource>,
access: KProperty1<EmitterParams, ParamClasses.StringCurve>,
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)
} }
@@ -9,6 +9,7 @@ import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.arguments.StringArgumentType import com.mojang.brigadier.arguments.StringArgumentType
import com.mojang.brigadier.builder.RequiredArgumentBuilder import com.mojang.brigadier.builder.RequiredArgumentBuilder
import com.mojang.brigadier.context.CommandContext import com.mojang.brigadier.context.CommandContext
import net.minecraft.command.CommandRegistryAccess
import net.minecraft.server.command.CommandManager import net.minecraft.server.command.CommandManager
import net.minecraft.server.command.ServerCommandSource import net.minecraft.server.command.ServerCommandSource
import net.minecraft.text.Style import net.minecraft.text.Style
@@ -77,7 +78,7 @@ blockCurve: Pair<Array<String>, LerpCurves>,
object ManageEmitters { object ManageEmitters {
fun register(dispatcher: CommandDispatcher<ServerCommandSource>) { fun register(dispatcher: CommandDispatcher<ServerCommandSource>, registryAccess: CommandRegistryAccess) {
val command = CommandManager.literal("ManageEmitters") val command = CommandManager.literal("ManageEmitters")
.requires { source -> source.hasPermissionLevel(2) } .requires { source -> source.hasPermissionLevel(2) }
@@ -85,7 +86,7 @@ object ManageEmitters {
command command
.then(CommandManager.literal("create").then(create())) .then(CommandManager.literal("create").then(create()))
.then(CommandManager.literal("remove").then(remove())) .then(CommandManager.literal("remove").then(remove()))
.then(CommandManager.literal("config").then(config())) .then(CommandManager.literal("config").then(config(registryAccess)))
.then(CommandManager.literal("list") .then(CommandManager.literal("list")
.executes { context -> .executes { context ->
val emitterIdList = idRegister.keys.sorted().joinToString(" ") val emitterIdList = idRegister.keys.sorted().joinToString(" ")
@@ -173,10 +174,10 @@ object ManageEmitters {
} }
} }
private fun config() : RequiredArgumentBuilder<ServerCommandSource, String> { private fun config(registryAccess: CommandRegistryAccess) : RequiredArgumentBuilder<ServerCommandSource, String> {
var emitterList = emitterListArg("Emitter ID") var emitterList = emitterListArg("Emitter ID")
emitterList = listConfigArgs(dataClassToArray(EmitterParams::class), emitterList) as RequiredArgumentBuilder<ServerCommandSource, String> emitterList = listConfigArgs(dataClassToArray(EmitterParams::class), emitterList, registryAccess) as RequiredArgumentBuilder<ServerCommandSource, String>
return emitterList return emitterList
} }
@@ -30,7 +30,7 @@ data class EmitterParams (
var offsetCurve: ParamClasses.LerpVal, var offsetCurve: ParamClasses.LerpVal,
var rotVelCurve: ParamClasses.LerpVal, var rotVelCurve: ParamClasses.LerpVal,
var scaleCurve: ParamClasses.LerpVal, var scaleCurve: ParamClasses.LerpVal,
var modelCurve: Pair<Array<String>, LerpCurves>, var modelCurve: ParamClasses.StringCurve,
) )
{ {
companion object Presets { companion object Presets {
@@ -55,8 +55,8 @@ data class EmitterParams (
minVel = 0.0f, minVel = 0.0f,
offsetCurve = ParamClasses.LerpVal.NoLerpVec3f, offsetCurve = ParamClasses.LerpVal.NoLerpVec3f,
rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Sqrt), rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Sqrt),
scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 2.5f, LerpCurves.Constant), scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 2.5f, LerpCurves.Linear),
modelCurve = Pair( modelCurve = ParamClasses.StringCurve(
arrayOf( arrayOf(
"shroomlight", "shroomlight",
"orange_concrete", "orange_concrete",
@@ -88,9 +88,9 @@ data class EmitterParams (
drag = 0.075f, drag = 0.075f,
minVel = 0.0f, minVel = 0.0f,
offsetCurve = ParamClasses.LerpVal.NoLerpVec3f, offsetCurve = ParamClasses.LerpVal.NoLerpVec3f,
rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Constant), rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Linear),
scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 1.0f, LerpCurves.Constant), scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 1.0f, LerpCurves.Linear),
modelCurve = Pair( modelCurve = ParamClasses.StringCurve(
arrayOf( arrayOf(
"shroomlight", "shroomlight",
"orange_concrete", "orange_concrete",
@@ -99,7 +99,7 @@ data class EmitterParams (
"gray_stained_glass", "gray_stained_glass",
"light_gray_stained_glass" "light_gray_stained_glass"
), ),
LerpCurves.Constant LerpCurves.Linear
) )
) )
val STRESS_TEST = EmitterParams( val STRESS_TEST = EmitterParams(
@@ -126,9 +126,9 @@ data class EmitterParams (
drag = 0.075f, drag = 0.075f,
minVel = 0.0f, minVel = 0.0f,
offsetCurve = ParamClasses.LerpVal.NoLerpVec3f, offsetCurve = ParamClasses.LerpVal.NoLerpVec3f,
rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Constant), rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Linear),
scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 1.5f, LerpCurves.Constant), scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 1.5f, LerpCurves.Linear),
modelCurve = Pair( modelCurve = ParamClasses.StringCurve(
arrayOf( arrayOf(
"shroomlight", "shroomlight",
"orange_concrete", "orange_concrete",
@@ -137,7 +137,7 @@ data class EmitterParams (
"gray_stained_glass", "gray_stained_glass",
"light_gray_stained_glass" "light_gray_stained_glass"
), ),
LerpCurves.Constant LerpCurves.Linear
) )
) )
} }
@@ -1,6 +1,7 @@
package com.bytedice.bde_particles.particles package com.bytedice.bde_particles.particles
import com.bytedice.bde_particles.LerpCurves import com.bytedice.bde_particles.LerpCurves
import com.bytedice.bde_particles.lerpArray
import com.bytedice.bde_particles.randomFloatBetween import com.bytedice.bde_particles.randomFloatBetween
import com.bytedice.bde_particles.randomIntBetween import com.bytedice.bde_particles.randomIntBetween
import org.joml.Vector3f import org.joml.Vector3f
@@ -60,9 +61,9 @@ class ParamClasses {
private val curve: LerpCurves, private val curve: LerpCurves,
) : LerpVal() { ) : LerpVal() {
fun lerp(t: Float): Vector3f { fun lerp(t: Float): Vector3f {
val x = com.bytedice.bde_particles.lerp(fromX, toX, 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, t) * curve.function(t) val y = com.bytedice.bde_particles.lerp(fromY, toY, curve.function(t))
val z = com.bytedice.bde_particles.lerp(fromZ, toZ, t) * curve.function(t) val z = com.bytedice.bde_particles.lerp(fromZ, toZ, curve.function(t))
return Vector3f(x, y, z) return Vector3f(x, y, z)
} }
} }
@@ -78,9 +79,9 @@ class ParamClasses {
private val curveZ: LerpCurves private val curveZ: LerpCurves
) : LerpVal() { ) : LerpVal() {
fun lerp(t: Float): Vector3f { fun lerp(t: Float): Vector3f {
val x = com.bytedice.bde_particles.lerp(fromX, toX, t) * curveX.function(t) val x = com.bytedice.bde_particles.lerp(fromX, toX, curveX.function(t))
val y = com.bytedice.bde_particles.lerp(fromY, toY, t) * curveY.function(t) val y = com.bytedice.bde_particles.lerp(fromY, toY, curveY.function(t))
val z = com.bytedice.bde_particles.lerp(fromZ, toZ, t) * curveZ.function(t) val z = com.bytedice.bde_particles.lerp(fromZ, toZ, curveZ.function(t))
return Vector3f(x, y, z) return Vector3f(x, y, z)
} }
} }
@@ -90,7 +91,7 @@ class ParamClasses {
private val curve: LerpCurves, private val curve: LerpCurves,
) : LerpVal() { ) : LerpVal() {
fun lerp(t: Float) : Float { fun lerp(t: Float) : Float {
return com.bytedice.bde_particles.lerp(from, to, t) * curve.function(t) return com.bytedice.bde_particles.lerp(from, to, curve.function(t))
} }
} }
data object NoLerpVec3f : LerpVal() data object NoLerpVec3f : LerpVal()
@@ -155,4 +156,10 @@ class ParamClasses {
} }
data object None : TransformWithVel() data object None : TransformWithVel()
} }
class StringCurve (
var array: Array<String>,
var curve: LerpCurves
) {
fun lerp(t: Float) : String { return lerpArray(array as Array<Any>, t, curve) as String }
}
} }
@@ -75,7 +75,7 @@ class Particle(
val properties = DisplayEntityProperties( val properties = DisplayEntityProperties(
pos = entityPos, pos = entityPos,
rot = entityRot, rot = entityRot,
model = lerpArray(emitterParams.modelCurve.first as Array<Any>, 0.0f, emitterParams.modelCurve.second) as String, model = emitterParams.modelCurve.lerp(0.0f),
translation = newPos.add(pos), translation = newPos.add(pos),
leftRotation = quatRot, leftRotation = quatRot,
scale = Vector3f(scale).mul(emitterParams.scaleCurve.lerpToVector3f(0.0f)), scale = Vector3f(scale).mul(emitterParams.scaleCurve.lerpToVector3f(0.0f)),
@@ -130,7 +130,7 @@ class Particle(
private fun calcNewProperties() : DisplayEntityProperties { private fun calcNewProperties() : DisplayEntityProperties {
val timeAliveClamped = (timeAlive.toFloat() / lifeTime.toFloat()).coerceIn(0.0f, 1.0f) val timeAliveClamped = (timeAlive.toFloat() / lifeTime.toFloat()).coerceIn(0.0f, 1.0f)
val newModel = lerpArray(emitterParams.modelCurve.first as Array<Any>, timeAliveClamped, emitterParams.modelCurve.second) as String val newModel = emitterParams.modelCurve.lerp(timeAliveClamped)
val (newVel, newPos) = calcPos() val (newVel, newPos) = calcPos()
val newOriginOffset = Vector3f(originOffset.mul(emitterParams.offsetCurve.lerpToVector3f(timeAliveClamped))) val newOriginOffset = Vector3f(originOffset.mul(emitterParams.offsetCurve.lerpToVector3f(timeAliveClamped)))
@@ -98,10 +98,17 @@ class ParticleEmitter(
val loopDelay = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopDelay val loopDelay = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopDelay
val loopCount = (params.spawnDuration as ParamClasses.Duration.MultiBurst).loopCount 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 (timePaused >= loopDelay) {
if (timeAlive >= loopDur && loopDelay > 0) { isPaused = true; timeAlive = 0 } isPaused = false
timePaused = 0
timesLooped += 1
}
if (timeAlive >= loopDur && loopDelay > 0) {
isPaused = true
timeAlive = 0
}
if (!isPaused) { timeAlive += 1 } if (!isPaused) { timeAlive += 1 }
else { timePaused += 1 } else { timePaused += 1 }
@@ -1,7 +1,9 @@
package com.bytedice.bde_particles.particles package com.bytedice.bde_particles.particles
import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1 import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty1 import kotlin.reflect.KProperty1
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.memberProperties import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible import kotlin.reflect.jvm.isAccessible
@@ -80,7 +82,10 @@ fun updateParam(id: String, paramAccess: KProperty1<EmitterParams, *>, paramValu
fun updateParams(params: EmitterParams, paramAccess: KProperty1<EmitterParams, Any>, paramValue: Any): Pair<EmitterParams, Boolean> { fun updateParams(params: EmitterParams, paramAccess: KProperty1<EmitterParams, Any>, paramValue: Any): Pair<EmitterParams, Boolean> {
val newParams = params.copy() 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<EmitterParams, Any> paramAccess as KMutableProperty1<EmitterParams, Any>
paramAccess.set(newParams, paramValue) paramAccess.set(newParams, paramValue)
return Pair(newParams, true) return Pair(newParams, true)