diff --git a/README.md b/README.md index be17b99..edead15 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,13 @@ BDE_ParticleSys is a highly customizable **server-side** particle system that uses `Block Display Entities` (BDEs) as particles. It is meant to be used as a library for other mods, but can be used as a standalone mod too. - -If BDE_ParticleSys is used as a standalone mod, you will only be able to use it with the custom in-game commands `/GiveEmitterTool`, `/KillAllEmitters` and `/ManageEmitters`. Any particles made with those commands are **temporary** and get removed once the server shuts down, as they are meant for quick testing. +If BDE_ParticleSys is used as a standalone mod, you will only be able to use it with the custom in-game commands `/GiveEmitterTool`, `/KillAllEmitters`, and `/ManageEmitters`. Any particles made, removed, or changed with those commands are **temporary** and get reverted once the server shuts down, as they are meant for quick testing. + +### Contents (in-game) +* `/GiveEmitterTool` +* `/KillAllEmitters` +* `/ManageEmitters` +* `GlobalMaxParticles` (GameRule) # Open-source - Copyright diff --git a/src/client/kotlin/com/bytedice/bde_particles/client/Bde_particlesClient.kt b/src/client/kotlin/com/bytedice/bde_particles/client/Bde_particlesClient.kt index f993ae6..9f6feb0 100644 --- a/src/client/kotlin/com/bytedice/bde_particles/client/Bde_particlesClient.kt +++ b/src/client/kotlin/com/bytedice/bde_particles/client/Bde_particlesClient.kt @@ -2,7 +2,7 @@ package com.bytedice.bde_particles.client import com.bytedice.bde_particles.commands.GiveEmitterTool import com.bytedice.bde_particles.commands.KillAllEmitters -import com.bytedice.bde_particles.commands.ManageEmitters +//import com.bytedice.bde_particles.commands.ManageEmitters import com.bytedice.bde_particles.init import com.bytedice.bde_particles.onRightClick import com.bytedice.bde_particles.tick @@ -38,7 +38,7 @@ class Bde_particlesClient : ClientModInitializer { CommandRegistrationCallback.EVENT.register { dispatcher, registryAccess, _ -> GiveEmitterTool.register(dispatcher, registryAccess) - ManageEmitters .register(dispatcher) + //ManageEmitters .register(dispatcher) KillAllEmitters.register(dispatcher) } 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 dcccf1f..3bf4fd3 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt @@ -2,7 +2,7 @@ package com.bytedice.bde_particles import com.bytedice.bde_particles.commands.GiveEmitterTool import com.bytedice.bde_particles.commands.KillAllEmitters -import com.bytedice.bde_particles.commands.ManageEmitters +//import com.bytedice.bde_particles.commands.ManageEmitters import com.bytedice.bde_particles.items.ParticleEmitterTool import com.bytedice.bde_particles.particles.* import kotlinx.coroutines.async @@ -47,23 +47,31 @@ import java.util.* // Custom curve equation command args (parse from strings) // Cylinder and cone force field shape. // Better particle performance. -// Global max particles variable. - // gamerule max particles // Emitter groups (multiple emitters for EmitterTool) // Private functions/classes +// Debug tools + // RenderParticleDebug gamerule + // render emitter origin + // render particle origin + // name-tag particle with its data + // render particle velocity direction var ALL_PARTICLE_EMITTERS: Array = emptyArray() +var LIVING_PARTICLE_COUNT: Int = 0 val SESSION_UUID: UUID = UUID.randomUUID() -val GLOBAL_MAX_PARTICLES = GameRuleRegistry.register( - "GlobalMaxParticles", - GameRules.Category.UPDATES, - GameRuleFactory.createIntRule(3000, 0) -) class Bde_particles : ModInitializer { + companion object { + val GLOBAL_MAX_PARTICLES: GameRules.Key = GameRuleRegistry.register( + "GlobalMaxParticles", + GameRules.Category.UPDATES, + GameRuleFactory.createIntRule(3000, 0) + ) + } + override fun onInitialize() { if (FabricLoader.getInstance().environmentType != EnvType.SERVER) { return @@ -71,17 +79,13 @@ class Bde_particles : ModInitializer { println("BPS - Initializing on ${FabricLoader.getInstance().environmentType}") - ServerLifecycleEvents.SERVER_STARTED.register { _ -> - init() - } - ServerTickEvents.START_SERVER_TICK.register { server -> tick(server) } CommandRegistrationCallback.EVENT.register { dispatcher, registryAccess, _ -> GiveEmitterTool.register(dispatcher, registryAccess) - ManageEmitters .register(dispatcher) + //ManageEmitters .register(dispatcher) KillAllEmitters.register(dispatcher) } @@ -93,6 +97,8 @@ class Bde_particles : ModInitializer { }) ServerLifecycleEvents.SERVER_STARTED.register(ServerLifecycleEvents.ServerStarted { _ -> + init() + println("BPS - Progress bar filled :3") println( "\n____________ _____ _____ \n" + diff --git a/src/main/kotlin/com/bytedice/bde_particles/DisplayEntity.kt b/src/main/kotlin/com/bytedice/bde_particles/DisplayEntity.kt index 32a227e..315919b 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/DisplayEntity.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/DisplayEntity.kt @@ -75,7 +75,7 @@ class DisplayEntity(private var properties: DisplayEntityProperties?) { fun kill() { - if (entity == null) { println("BPS - Particle Emitter entity is null!"); return } + if (entity == null) { println("BPS - Failed to kill BDE. Entity is null!"); return } entity?.kill() } } \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/Math.kt b/src/main/kotlin/com/bytedice/bde_particles/Math.kt index 23c43ab..ed9fe78 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/Math.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/Math.kt @@ -186,7 +186,6 @@ fun sdfSphere(pos: Vector3f, radius: Float, objectPos: Vector3f): Float { } - fun sdfCube(pos: Vector3f, size: Vector3f, objectPos: Vector3f): Float { val px = objectPos.x - pos.x val py = objectPos.y - pos.y @@ -215,4 +214,22 @@ fun sdfCube(pos: Vector3f, size: Vector3f, objectPos: Vector3f): Float { fun normalizeSdf(sdf: Float, radius: Float): Float { return if (sdf < 0) (sdf + radius) / radius else 0f +} + + +fun velToRot(velocity: Vector3f): Vector3f { + val normalizedVelocity = Vector3f(velocity).normalize() + + val yaw = Math.toDegrees(atan2(normalizedVelocity.z.toDouble(), normalizedVelocity.x.toDouble())).toFloat() + + val pitch = Math.toDegrees( + atan2( + -normalizedVelocity.y.toDouble(), + sqrt( + normalizedVelocity.x.toDouble().pow(2) + normalizedVelocity.z.toDouble().pow(2) + ) + ) + ).toFloat() + + return Vector3f(pitch, yaw, 0f) } \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/commands/KillAllEmitters.kt b/src/main/kotlin/com/bytedice/bde_particles/commands/KillAllEmitters.kt index e79115a..5a24f88 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/commands/KillAllEmitters.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/commands/KillAllEmitters.kt @@ -16,11 +16,13 @@ object KillAllEmitters { dispatcher.register( command.executes { context -> + var amountEmitters = 0 for (emitter in ALL_PARTICLE_EMITTERS) { emitter.kill() + amountEmitters += 1 } - val feedback = Text.literal("BPS - Killed all living Emitters.") + val feedback = Text.literal("BPS - Killed all living Emitters. [$amountEmitters]") .setStyle(Style.EMPTY.withColor(Color(0, 200, 0).rgb)) context.source.sendFeedback({ feedback }, false) diff --git a/src/main/kotlin/com/bytedice/bde_particles/commands/ManageConfigArgs.kt b/src/main/kotlin/com/bytedice/bde_particles/commands/ManageConfigArgs.kt index 517c374..7e8fa4f 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/commands/ManageConfigArgs.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/commands/ManageConfigArgs.kt @@ -22,7 +22,7 @@ import java.util.concurrent.CompletableFuture // TODO: completely redesign - +/* // CLOSE THIS FILE IMMEDIATELY, YOU ARE ENTERING EGYPTIAN (pyramid) AND ITALIAN (spaghetti) TERRITORY val curves = arrayOf("LINEAR", "SQRT", "EXPONENT", "CUBIC", "SINE", "COSINE", "INVERSE", "LOG", "EXP", "BOUNCE") @@ -602,4 +602,5 @@ fun forceFieldGenericParams(context: CommandContext, shape: Vector3f(posX, posY, posZ), shape ) -} \ No newline at end of file +} +*/ \ 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 fc45cd3..71345f6 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt @@ -1,6 +1,5 @@ package com.bytedice.bde_particles.commands -import com.bytedice.bde_particles.emitterParamsToJson import com.bytedice.bde_particles.particles.* import com.mojang.brigadier.Command import com.mojang.brigadier.CommandDispatcher @@ -81,7 +80,7 @@ blockCurve: Pair, LerpCurves>, // // output -> give player a command block with the particle params (should be paste-able in kotlin) - +/* object ManageEmitters { fun register(dispatcher: CommandDispatcher) { val command = CommandManager.literal("ManageEmitters") @@ -298,4 +297,5 @@ fun makeCommandBlock(emitterData: Map): ItemStack { i.set(DataComponentTypes.BLOCK_ENTITY_DATA, component) return i -} \ No newline at end of file +} +*/ \ No newline at end of file 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 bc1515e..87f5ca8 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/EmitterParams.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/EmitterParams.kt @@ -14,52 +14,50 @@ data class EmitterParams ( var lifeTime: ParamClasses.PairInt, var shape: SpawningShape, // init transforms - var offset: ParamClasses.PairVec3f, - var initRot: ParamClasses.PairVec3f, - var rotVel: ParamClasses.PairVec3f, - var rotWithVel: Boolean, - var initVel: ParamClasses.PairVec3f, - var initCenterVel: ParamClasses.PairFloat, - var initScale: ParamClasses.PairVec3f, - var scaleWithVel: Boolean, + var offset: ParamClasses.PairVec3f, + var initRot: ParamClasses.PairVec3f, + var rotVel: ParamClasses.PairVec3f, + var initVel: ParamClasses.PairVec3f, + var initCenterVel: ParamClasses.PairFloat, + var initScale: ParamClasses.PairVec3f, + var transformWithVel: ParamClasses.TransformWithVel, // velocity - var forceFields: Array, - var constVel: Vector3f, - var drag: Float, - var minVel: Float, + var forceFields: Array, + var constVel: Vector3f, + var drag: Float, + var minVel: Float, // curves - var offsetCurve: ParamClasses.LerpVal, - var rotVelCurve: ParamClasses.LerpVal, - var scaleCurve: ParamClasses.LerpVal, - var blockCurve: Pair, LerpCurves>, + var offsetCurve: ParamClasses.LerpVal, + var rotVelCurve: ParamClasses.LerpVal, + var scaleCurve: ParamClasses.LerpVal, + var blockCurve: Pair, LerpCurves>, ) { companion object Presets { val DEFAULT = EmitterParams( - maxCount = 200, - spawnRate = 1, + maxCount = 400, + spawnRate = 100, spawnChance = 1.0f, - spawnDuration = ParamClasses.Duration.SingleBurst(25), + spawnDuration = ParamClasses.Duration.SingleBurst(4), spawnPosOffset = Vector3f(0.0f, 0.0f, 0.0f), lifeTime = ParamClasses.PairInt(25, 40), - shape = SpawningShape.Circle(3.0f, false), + shape = SpawningShape.Sphere(1.0f, true), offset = ParamClasses.PairVec3f.Uniform(-0.5f, -0.5f), - initRot = ParamClasses.PairVec3f.Null, + initRot = ParamClasses.PairVec3f.NonUniform(0.0f, 0.0f, 0.0f, 360.0f, 360.0f, 360.0f), rotVel = ParamClasses.PairVec3f.NonUniform(-0.2f, -0.2f, -0.2f, 0.2f, 0.2f, 0.2f), - rotWithVel = true, initVel = ParamClasses.PairVec3f.Null, - initCenterVel = ParamClasses.PairFloat(0.1f, 0.3f), - initScale = ParamClasses.PairVec3f.Uniform(1.0f, 0.2f), - scaleWithVel = true, + initCenterVel = ParamClasses.PairFloat(0.4f, 0.5f), + initScale = ParamClasses.PairVec3f.Uniform(1.5f, 1.0f), + transformWithVel = ParamClasses.TransformWithVel.none, forceFields = emptyArray(), - constVel = Vector3f(0.0f, -0.01f, 0.0f), + constVel = Vector3f(0.0f, 0.0f, 0.0f), drag = 0.075f, minVel = 0.0f, - offsetCurve = ParamClasses.LerpVal.NoLerpVec3f(Vector3f(-0.5f, -0.5f, -0.5f)), + offsetCurve = ParamClasses.LerpVal.NoLerpVec3f, rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Sqrt), - scaleCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.5f, LerpCurves.Linear), + scaleCurve = ParamClasses.LerpVal.LerpUniform(0.0f, 2.0f, LerpCurves.Linear), blockCurve = Pair( - arrayOf("minecraft:purple_concrete", "minecraft:purple_stained_glass"), + arrayOf("shroomlight", "orange_concrete", "orange_stained_glass", "gray_wool", "gray_stained_glass", "light_gray_stained_glass"), LerpCurves.Sqrt ), ) 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 05a2ca2..a4dba63 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/ParamClasses.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/ParamClasses.kt @@ -90,7 +90,7 @@ class ParamClasses { return com.bytedice.bde_particles.lerp(from, to, t) * curve.function(t) } } - data class NoLerpVec3f(val value: Vector3f) : LerpVal() + data object NoLerpVec3f : LerpVal() fun lerpToVector3f(t: Float) : Vector3f { when (this) { @@ -100,7 +100,7 @@ class ParamClasses { val x = this.lerp(t) return Vector3f(x, x, x) } - is NoLerpVec3f -> return this.value + is NoLerpVec3f -> return Vector3f(1.0f, 1.0f, 1.0f) } } } @@ -118,4 +118,9 @@ class ParamClasses { val loopDelay: Int ) : Duration() } + enum class TransformWithVel { + rotOnly, + scaleAndRot, + none + } } \ 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 1603c0b..cfe3cf1 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/Particle.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/Particle.kt @@ -66,7 +66,8 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos: pos = newSpawnPos - val dirFromCenter = entityPos.subtract(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()).normalize() + val posDouble = Vec3d(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) + val dirFromCenter = posDouble.normalize() val forceFromCenter = dirFromCenter.multiply(emitterParams.initCenterVel.randomize().toDouble()) vel.add(forceFromCenter.x.toFloat(), forceFromCenter.y.toFloat(), forceFromCenter.z.toFloat()) @@ -89,7 +90,10 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos: fun spawn(world: ServerWorld) { - if (isInit) { blockDisplay.spawn(world) } + if (isInit) { + blockDisplay.spawn(world) + LIVING_PARTICLE_COUNT += 1 + } else { error("Particle is not initialized before being spawned. Please use Particle.init() to initialize it.") } } @@ -101,7 +105,7 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos: blockDisplay.updateProperties(newProperties) - if (timeAlive > lifeTime) { isDead = true; blockDisplay.kill() } + if (timeAlive > lifeTime) { kill() } timeAlive += 1 } @@ -144,7 +148,7 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos: val newProperties = DisplayEntityProperties( pos = entityPos, blockType = newBlock, - translation = combinedOffset, + translation = combinedOffset.add(newOriginOffset), leftRotation = quatRot, scale = newScale, tags = arrayOf("BPS_Particle", "BPS_UUID", SESSION_UUID.toString()) @@ -183,11 +187,17 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos: private fun calcRot(t: Float) : Vector3f { val newRot = Vector3f(rot) - val newVel = Vector3f(rotVel) - newVel.mul(emitterParams.rotVelCurve.lerpToVector3f(t)) - newRot.add(newVel) - return newRot + if (emitterParams.transformWithVel == ParamClasses.TransformWithVel.none) { + val newVel = Vector3f(rotVel) + + newVel.mul(emitterParams.rotVelCurve.lerpToVector3f(t)) + return newRot.add(newVel) + } + else { + val rotWithVel = velToRot(vel) + return newRot.add(rotWithVel) + } } @@ -214,6 +224,10 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos: fun kill() { - if (!isDead) { blockDisplay.kill() } + if (!isDead) { + blockDisplay.kill() + LIVING_PARTICLE_COUNT -= 1 + isDead = true + } } } \ No newline at end of file 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 7305cc4..c066e1e 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleEmitter.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleEmitter.kt @@ -1,5 +1,7 @@ package com.bytedice.bde_particles.particles +import com.bytedice.bde_particles.Bde_particles +import com.bytedice.bde_particles.LIVING_PARTICLE_COUNT import com.bytedice.bde_particles.randomFloatBetween import net.minecraft.server.world.ServerWorld import net.minecraft.util.math.Vec3d @@ -37,6 +39,9 @@ class ParticleEmitter(private val pos: Vec3d, private val rot: Vector2f, private repeat(params.spawnRate) { if (allParticles.size >= params.maxCount) { return } + val maxParticles = world.gameRules.getInt(Bde_particles.GLOBAL_MAX_PARTICLES) + if (LIVING_PARTICLE_COUNT >= maxParticles) { return } + val randomSpawnChance = randomFloatBetween(0.0f, params.spawnChance.coerceIn(0.0f, 1.0f)) if (randomSpawnChance > params.spawnChance) { return } 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 5bb2302..0fad576 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt @@ -13,6 +13,18 @@ fun addToRegister(id: String, params: EmitterParams) : Pair { return Pair(newId, false) } + val newBlocks: MutableList = mutableListOf() + + for (block in params.blockCurve.first) { + val blockModIdRegex = ".*:".toRegex() + if (blockModIdRegex.containsMatchIn(block)) { break } + else { + newBlocks.add("minecraft:$block") + } + } + + params.blockCurve = Pair(newBlocks.toTypedArray(), params.blockCurve.second) + idRegister[id] = params println("BPS - Registered Emitter ID \"$newId\".") return Pair(newId, true)