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.
// 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<ServerCommandSource, String> {
fun emitterListArg(argName: String) : RequiredArgumentBuilder<ServerCommandSource, String> {
return CommandManager.argument(argName, StringArgumentType.string())
.suggests { _, builder ->
idRegister.keys.forEach { key ->
@@ -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) {
ALL_PARTICLE_EMITTERS += emitter
}
@@ -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) }
@@ -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<ArgumentBuilder<ServerCommandSource, *>>
@@ -28,7 +30,11 @@ typealias CommandArgumentBuilder = KFunction<ArgumentBuilder<ServerCommandSource
// 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 ->
var configArg = CommandManager.literal(arg)
@@ -36,11 +42,17 @@ fun listConfigArgs(configArgs: Array<String>, rootArg: RequiredArgumentBuilder<S
val func = parseArgTypeToFunc(argAccess.returnType)
if (func != null) {
if (func.parameters.lastIndex == 0) { configArg.then(func.call(argAccess)) }
else { configArg = func.call(argAccess, configArg) as LiteralArgumentBuilder<ServerCommandSource>? }
if (func.parameters.lastIndex == 0) {
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)
}
@@ -60,26 +72,23 @@ fun parseArgNameToAccess(name: String) : KProperty1<EmitterParams, *>? {
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<EmitterParams, ParamClasses.Duration>,
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<EmitterParams, ParamClasses.Duration>,
.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<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 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<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> {
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<EmitterParams, SpawningShape>,
.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<EmitterParams, SpawningShape>,
.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<EmitterParams, SpawningShape>,
.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<EmitterParams, SpawningShape>,
.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<EmitterParams, SpawningShape>, context: CommandContext<ServerCommandSource>) {
fun shapeArgExec(paramName: String, access: KProperty1<EmitterParams, SpawningShape>, context: CommandContext<ServerCommandSource>) {
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<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.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<Array<String>, LerpCurves>,
object ManageEmitters {
fun register(dispatcher: CommandDispatcher<ServerCommandSource>) {
fun register(dispatcher: CommandDispatcher<ServerCommandSource>, 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<ServerCommandSource, String> {
private fun config(registryAccess: CommandRegistryAccess) : RequiredArgumentBuilder<ServerCommandSource, String> {
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
}
@@ -30,7 +30,7 @@ data class EmitterParams (
var offsetCurve: ParamClasses.LerpVal,
var rotVelCurve: ParamClasses.LerpVal,
var scaleCurve: ParamClasses.LerpVal,
var modelCurve: Pair<Array<String>, 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
)
)
}
@@ -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<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(
pos = entityPos,
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),
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<Any>, 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)))
@@ -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 }
@@ -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<EmitterParams, *>, paramValu
fun updateParams(params: EmitterParams, paramAccess: KProperty1<EmitterParams, Any>, paramValue: Any): Pair<EmitterParams, Boolean> {
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.set(newParams, paramValue)
return Pair(newParams, true)