moved to ItemDisplays and added support for CustomModelData

This commit is contained in:
2024-11-30 17:22:20 +01:00
parent 8ad6cb9ffb
commit 828297aef2
7 changed files with 93 additions and 33 deletions
@@ -18,7 +18,7 @@ import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory
import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry
import net.fabricmc.loader.api.FabricLoader import net.fabricmc.loader.api.FabricLoader
import net.minecraft.entity.EntityType import net.minecraft.entity.EntityType
import net.minecraft.entity.decoration.DisplayEntity.BlockDisplayEntity import net.minecraft.entity.decoration.DisplayEntity.ItemDisplayEntity
import net.minecraft.entity.player.PlayerEntity import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemStack import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtCompound
@@ -38,7 +38,6 @@ import java.util.*
// Allowing the use of regular Minecraft particles instead of just BDEs. // Allowing the use of regular Minecraft particles instead of just BDEs.
// 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
// blockCurve array not needing "minecraft:"
// Parameters // Parameters
// rotWithVel / scaleWithVel (bool) // rotWithVel / scaleWithVel (bool)
@@ -70,6 +69,11 @@ class Bde_particles : ModInitializer {
GameRules.Category.UPDATES, GameRules.Category.UPDATES,
GameRuleFactory.createIntRule(3000, 0) GameRuleFactory.createIntRule(3000, 0)
) )
val SHOW_PARTICLE_DEBUG: GameRules.Key<GameRules.BooleanRule> = GameRuleRegistry.register(
"ShowParticleDebug",
GameRules.Category.UPDATES,
GameRuleFactory.createBooleanRule(false)
)
} }
override fun onInitialize() { override fun onInitialize() {
@@ -122,10 +126,10 @@ fun tick(server: MinecraftServer) {
runBlocking { runBlocking {
server.worlds.map { world -> server.worlds.map { world ->
async { async {
val blockDisplayEntities = world.iterateEntities() val displayEntities = world.iterateEntities()
.filter { it.type == EntityType.BLOCK_DISPLAY } .filter { it.type == EntityType.ITEM_DISPLAY }
for (entity in blockDisplayEntities) { for (entity in displayEntities) {
if (displayEntityContainsTag(entity as BlockDisplayEntity, "BPS_UUID") if (displayEntityContainsTag(entity as ItemDisplayEntity, "BPS_UUID")
&& !displayEntityContainsTag(entity, SESSION_UUID.toString())) { && !displayEntityContainsTag(entity, SESSION_UUID.toString())) {
entity.kill() entity.kill()
@@ -185,7 +189,7 @@ fun emitterParamsToJson(params: EmitterParams) : Map<String, Any> { // TODO: map
*/ */
fun displayEntityContainsTag(entity: BlockDisplayEntity, tag: String) : Boolean { fun displayEntityContainsTag(entity: ItemDisplayEntity, tag: String) : Boolean {
val nbt = NbtCompound() val nbt = NbtCompound()
entity.writeNbt(nbt) entity.writeNbt(nbt)
val tagsNbtList = nbt.getList("Tags", 8) val tagsNbtList = nbt.getList("Tags", 8)
@@ -1,21 +1,26 @@
package com.bytedice.bde_particles package com.bytedice.bde_particles
import net.minecraft.component.Component
import net.minecraft.component.ComponentType
import net.minecraft.entity.EntityType import net.minecraft.entity.EntityType
import net.minecraft.entity.decoration.DisplayEntity.BlockDisplayEntity import net.minecraft.entity.decoration.DisplayEntity.ItemDisplayEntity
import net.minecraft.item.Item
import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtFloat import net.minecraft.nbt.NbtFloat
import net.minecraft.nbt.NbtList import net.minecraft.nbt.NbtList
import net.minecraft.nbt.NbtString import net.minecraft.nbt.NbtString
import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.server.world.ServerWorld import net.minecraft.server.world.ServerWorld
class DisplayEntity(private var properties: DisplayEntityProperties?) { class DisplayEntity(private var properties: DisplayEntityProperties?) {
private var hasSpawned = false private var hasSpawned = false
private var entity: BlockDisplayEntity? = null private var entity: ItemDisplayEntity? = null
fun spawn(world: ServerWorld) { fun spawn(world: ServerWorld) {
if (hasSpawned || properties == null) { return } if (hasSpawned || properties == null) { return }
entity = BlockDisplayEntity(EntityType.BLOCK_DISPLAY, world) entity = ItemDisplayEntity(EntityType.ITEM_DISPLAY, world)
updateProperties(properties!!) updateProperties(properties!!)
world.spawnEntity(entity) world.spawnEntity(entity)
@@ -25,6 +30,21 @@ class DisplayEntity(private var properties: DisplayEntityProperties?) {
fun updateProperties(properties: DisplayEntityProperties) { fun updateProperties(properties: DisplayEntityProperties) {
val customModelRegex = "^([^/]+)(?:/([0-9]+))?$".toRegex()
val matchResult = customModelRegex.find(properties.model)
val modelName: String
val customModelNum: Int
if (matchResult != null) {
modelName = matchResult.groupValues[1]
customModelNum = matchResult.groupValues[2].toInt()
}
else {
modelName = properties.model
customModelNum = 0
}
val nbt = NbtCompound().apply { val nbt = NbtCompound().apply {
val tagList = NbtList() val tagList = NbtList()
@@ -34,7 +54,11 @@ class DisplayEntity(private var properties: DisplayEntityProperties?) {
put("Tags", tagList) put("Tags", tagList)
put("block_state", NbtCompound().apply { putString("Name", properties.blockType) }) put("item", NbtCompound().apply {
putString("id", modelName)
putInt("count", 1)
put("components", NbtCompound().apply { putInt("minecraft:custom_model_data", customModelNum) })
})
put("transformation", NbtCompound().apply { put("transformation", NbtCompound().apply {
val offsetList = NbtList().apply { val offsetList = NbtList().apply {
@@ -65,6 +89,9 @@ class DisplayEntity(private var properties: DisplayEntityProperties?) {
put("scale", scaleList) put("scale", scaleList)
put("right_rotation", rightRotList) put("right_rotation", rightRotList)
}) })
put("view_range", NbtCompound().apply { properties.viewRange })
} }
entity?.readNbt(nbt) entity?.readNbt(nbt)
@@ -9,11 +9,13 @@ data class DisplayEntityProperties (
val pos: Vec3d = Vec3d(0.0, 0.0, 0.0), val pos: Vec3d = Vec3d(0.0, 0.0, 0.0),
val rot: Vector2f = Vector2f(0.0f, 0.0f), val rot: Vector2f = Vector2f(0.0f, 0.0f),
val blockType: String = "minecraft:purple_concrete", val model: String = "minecraft:purple_concrete", // end with "/{num}" for custom model
val tags: Array<String> = emptyArray(), val tags: Array<String> = emptyArray(),
val translation: Vector3f = Vector3f(-0.5f, -0.5f, -0.5f), val translation: Vector3f = Vector3f(-0.5f, -0.5f, -0.5f),
val leftRotation: Vector4f = Vector4f(0.0f, 0.0f, 0.0f, 1.0f), val leftRotation: Vector4f = Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
val scale: Vector3f = Vector3f(1.0f, 1.0f, 1.0f), val scale: Vector3f = Vector3f(1.0f, 1.0f, 1.0f),
val rightRotation: Vector4f = Vector4f(0.0f, 0.0f, 0.0f, 1.0f) val rightRotation: Vector4f = Vector4f(0.0f, 0.0f, 0.0f, 1.0f),
val viewRange: Float = 2.0f
) )
@@ -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 blockCurve: Pair<Array<String>, LerpCurves>, var modelCurve: Pair<Array<String>, LerpCurves>,
) )
{ {
companion object Presets { companion object Presets {
@@ -42,7 +42,7 @@ data class EmitterParams (
spawnPosOffset = Vector3f(0.0f, 0.0f, 0.0f), spawnPosOffset = Vector3f(0.0f, 0.0f, 0.0f),
lifeTime = ParamClasses.PairInt(25, 40), lifeTime = ParamClasses.PairInt(25, 40),
shape = SpawningShape.Sphere(1.0f, true), shape = SpawningShape.Sphere(1.0f, true),
offset = ParamClasses.PairVec3f.Uniform(-0.5f, -0.5f), offset = ParamClasses.PairVec3f.Uniform(0.0f, 0.0f),
initRot = ParamClasses.PairVec3f.NonUniform(0.0f, 0.0f, 0.0f, 360.0f, 360.0f, 360.0f), 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), rotVel = ParamClasses.PairVec3f.NonUniform(-0.2f, -0.2f, -0.2f, 0.2f, 0.2f, 0.2f),
initVel = ParamClasses.PairVec3f.Null, initVel = ParamClasses.PairVec3f.Null,
@@ -56,8 +56,8 @@ data class EmitterParams (
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(0.0f, 2.0f, LerpCurves.Linear), scaleCurve = ParamClasses.LerpVal.LerpUniform(0.0f, 2.0f, LerpCurves.Linear),
blockCurve = Pair( modelCurve = Pair(
arrayOf("shroomlight", "orange_concrete", "orange_stained_glass", "gray_wool", "gray_stained_glass", "light_gray_stained_glass"), arrayOf("shroomlight", "orange_concrete", "orange_stained_glass", "gray_wool", "gray_stained_glass", "light_gray_stained_glass", "light_gray_stained_glass_pane"),
LerpCurves.Sqrt LerpCurves.Sqrt
), ),
) )
@@ -16,9 +16,13 @@ scaleWithVel: Boolean (NEW)
*/ */
class Particle(private val emitterParams: EmitterParams, private var entityPos: Vec3d, private val entityRot: Vector2f) { class Particle(
private val emitterParams: EmitterParams,
private var entityPos: Vec3d,
private val entityRot: Vector2f,
private var debug: Boolean
) {
private var pos = Vector3f(0.0f, 0.0f, 0.0f) private var pos = Vector3f(0.0f, 0.0f, 0.0f)
private var blockDisplay: DisplayEntity = DisplayEntity(DisplayEntityProperties())
private var timeAlive = 0 private var timeAlive = 0
private var isInit = false private var isInit = false
var isDead = false var isDead = false
@@ -56,6 +60,9 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos:
private var lifeTime = emitterParams.lifeTime.randomize() private var lifeTime = emitterParams.lifeTime.randomize()
private lateinit var particleEntity: DisplayEntity
private lateinit var debugOriginEntity: DisplayEntity
private lateinit var debugVelEntity: DisplayEntity
fun init() { fun init() {
val so = emitterParams.spawnPosOffset val so = emitterParams.spawnPosOffset
@@ -76,14 +83,14 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos:
val properties = DisplayEntityProperties( val properties = DisplayEntityProperties(
pos = entityPos, pos = entityPos,
rot = entityRot, rot = entityRot,
blockType = lerpArray(emitterParams.blockCurve.first as Array<Any>, 0.0f, emitterParams.blockCurve.second) as String, model = lerpArray(emitterParams.modelCurve.first as Array<Any>, 0.0f, emitterParams.modelCurve.second) as String,
translation = newPos.add(pos), translation = newPos.add(pos),
leftRotation = quatRot, leftRotation = quatRot,
scale = scale, scale = scale,
tags = arrayOf("BPS_Particle", "BPS_UUID", SESSION_UUID.toString()) tags = arrayOf("BPS_Particle", "BPS_UUID", SESSION_UUID.toString())
) )
blockDisplay = DisplayEntity(properties) particleEntity = DisplayEntity(properties)
isInit = true isInit = true
} }
@@ -91,7 +98,7 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos:
fun spawn(world: ServerWorld) { fun spawn(world: ServerWorld) {
if (isInit) { if (isInit) {
blockDisplay.spawn(world) particleEntity.spawn(world)
LIVING_PARTICLE_COUNT += 1 LIVING_PARTICLE_COUNT += 1
} }
else { error("Particle is not initialized before being spawned. Please use Particle.init() to initialize it.") } else { error("Particle is not initialized before being spawned. Please use Particle.init() to initialize it.") }
@@ -103,7 +110,7 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos:
val newProperties = calcNewProperties() val newProperties = calcNewProperties()
blockDisplay.updateProperties(newProperties) particleEntity.updateProperties(newProperties)
if (timeAlive > lifeTime) { kill() } if (timeAlive > lifeTime) { kill() }
@@ -127,7 +134,7 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos:
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 newBlock = lerpArray(emitterParams.blockCurve.first as Array<Any>, timeAliveClamped, emitterParams.blockCurve.second) as String val newModel = lerpArray(emitterParams.modelCurve.first as Array<Any>, timeAliveClamped, emitterParams.modelCurve.second) as String
val (newVel, newPos) = calcPos() val (newVel, newPos) = calcPos()
val newRot = calcRot(timeAliveClamped) val newRot = calcRot(timeAliveClamped)
val newScale = calcScale(timeAliveClamped) val newScale = calcScale(timeAliveClamped)
@@ -147,7 +154,7 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos:
val newProperties = DisplayEntityProperties( val newProperties = DisplayEntityProperties(
pos = entityPos, pos = entityPos,
blockType = newBlock, model = newModel,
translation = combinedOffset.add(newOriginOffset), translation = combinedOffset.add(newOriginOffset),
leftRotation = quatRot, leftRotation = quatRot,
scale = newScale, scale = newScale,
@@ -223,9 +230,28 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos:
} }
private fun spawnDebug() {
// spawn cube at particle origin
// show arrow pointing toward particle direction
// name-tag its data
}
private fun updateDebug() {
}
private fun calcDebug() {
}
fun kill() { fun kill() {
if (!isDead) { if (!isDead) {
blockDisplay.kill() particleEntity.kill()
LIVING_PARTICLE_COUNT -= 1 LIVING_PARTICLE_COUNT -= 1
isDead = true isDead = true
} }
@@ -45,7 +45,8 @@ class ParticleEmitter(private val pos: Vec3d, private val rot: Vector2f, private
val randomSpawnChance = randomFloatBetween(0.0f, params.spawnChance.coerceIn(0.0f, 1.0f)) val randomSpawnChance = randomFloatBetween(0.0f, params.spawnChance.coerceIn(0.0f, 1.0f))
if (randomSpawnChance > params.spawnChance) { return } if (randomSpawnChance > params.spawnChance) { return }
val particle = Particle(params, pos, rot) val debug = world.gameRules.getBoolean(Bde_particles.SHOW_PARTICLE_DEBUG)
val particle = Particle(params, pos, rot, debug)
particle.init() particle.init()
particle.spawn(world) particle.spawn(world)
@@ -13,17 +13,17 @@ fun addToRegister(id: String, params: EmitterParams) : Pair<String, Boolean> {
return Pair(newId, false) return Pair(newId, false)
} }
val newBlocks: MutableList<String> = mutableListOf() val newModels: MutableList<String> = mutableListOf()
for (block in params.blockCurve.first) { for (model in params.modelCurve.first) {
val blockModIdRegex = ".*:".toRegex() val modelModIdRegex = ".*:".toRegex()
if (blockModIdRegex.containsMatchIn(block)) { break } if (modelModIdRegex.containsMatchIn(model)) { break }
else { else {
newBlocks.add("minecraft:$block") newModels.add("minecraft:$model")
} }
} }
params.blockCurve = Pair(newBlocks.toTypedArray(), params.blockCurve.second) params.modelCurve = Pair(newModels.toTypedArray(), params.modelCurve.second)
idRegister[id] = params idRegister[id] = params
println("BPS - Registered Emitter ID \"$newId\".") println("BPS - Registered Emitter ID \"$newId\".")