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.loader.api.FabricLoader
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.item.ItemStack
import net.minecraft.nbt.NbtCompound
@@ -38,7 +38,6 @@ import java.util.*
// Allowing the use of regular Minecraft particles instead of just BDEs.
// Better command auto-completion. (and change names of args)
// force field "config" option
// blockCurve array not needing "minecraft:"
// Parameters
// rotWithVel / scaleWithVel (bool)
@@ -70,6 +69,11 @@ class Bde_particles : ModInitializer {
GameRules.Category.UPDATES,
GameRuleFactory.createIntRule(3000, 0)
)
val SHOW_PARTICLE_DEBUG: GameRules.Key<GameRules.BooleanRule> = GameRuleRegistry.register(
"ShowParticleDebug",
GameRules.Category.UPDATES,
GameRuleFactory.createBooleanRule(false)
)
}
override fun onInitialize() {
@@ -122,10 +126,10 @@ fun tick(server: MinecraftServer) {
runBlocking {
server.worlds.map { world ->
async {
val blockDisplayEntities = world.iterateEntities()
.filter { it.type == EntityType.BLOCK_DISPLAY }
for (entity in blockDisplayEntities) {
if (displayEntityContainsTag(entity as BlockDisplayEntity, "BPS_UUID")
val displayEntities = world.iterateEntities()
.filter { it.type == EntityType.ITEM_DISPLAY }
for (entity in displayEntities) {
if (displayEntityContainsTag(entity as ItemDisplayEntity, "BPS_UUID")
&& !displayEntityContainsTag(entity, SESSION_UUID.toString())) {
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()
entity.writeNbt(nbt)
val tagsNbtList = nbt.getList("Tags", 8)
@@ -1,21 +1,26 @@
package com.bytedice.bde_particles
import net.minecraft.component.Component
import net.minecraft.component.ComponentType
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.NbtFloat
import net.minecraft.nbt.NbtList
import net.minecraft.nbt.NbtString
import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.server.world.ServerWorld
class DisplayEntity(private var properties: DisplayEntityProperties?) {
private var hasSpawned = false
private var entity: BlockDisplayEntity? = null
private var entity: ItemDisplayEntity? = null
fun spawn(world: ServerWorld) {
if (hasSpawned || properties == null) { return }
entity = BlockDisplayEntity(EntityType.BLOCK_DISPLAY, world)
entity = ItemDisplayEntity(EntityType.ITEM_DISPLAY, world)
updateProperties(properties!!)
world.spawnEntity(entity)
@@ -25,6 +30,21 @@ class DisplayEntity(private var 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 tagList = NbtList()
@@ -34,7 +54,11 @@ class DisplayEntity(private var properties: DisplayEntityProperties?) {
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 {
val offsetList = NbtList().apply {
@@ -65,6 +89,9 @@ class DisplayEntity(private var properties: DisplayEntityProperties?) {
put("scale", scaleList)
put("right_rotation", rightRotList)
})
put("view_range", NbtCompound().apply { properties.viewRange })
}
entity?.readNbt(nbt)
@@ -9,11 +9,13 @@ data class DisplayEntityProperties (
val pos: Vec3d = Vec3d(0.0, 0.0, 0.0),
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 translation: Vector3f = Vector3f(-0.5f, -0.5f, -0.5f),
val leftRotation: Vector4f = Vector4f(0.0f, 0.0f, 0.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 rotVelCurve: ParamClasses.LerpVal,
var scaleCurve: ParamClasses.LerpVal,
var blockCurve: Pair<Array<String>, LerpCurves>,
var modelCurve: Pair<Array<String>, LerpCurves>,
)
{
companion object Presets {
@@ -42,7 +42,7 @@ data class EmitterParams (
spawnPosOffset = Vector3f(0.0f, 0.0f, 0.0f),
lifeTime = ParamClasses.PairInt(25, 40),
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),
rotVel = ParamClasses.PairVec3f.NonUniform(-0.2f, -0.2f, -0.2f, 0.2f, 0.2f, 0.2f),
initVel = ParamClasses.PairVec3f.Null,
@@ -56,8 +56,8 @@ data class EmitterParams (
offsetCurve = ParamClasses.LerpVal.NoLerpVec3f,
rotVelCurve = ParamClasses.LerpVal.LerpUniform(1.0f, 0.0f, LerpCurves.Sqrt),
scaleCurve = ParamClasses.LerpVal.LerpUniform(0.0f, 2.0f, LerpCurves.Linear),
blockCurve = Pair(
arrayOf("shroomlight", "orange_concrete", "orange_stained_glass", "gray_wool", "gray_stained_glass", "light_gray_stained_glass"),
modelCurve = Pair(
arrayOf("shroomlight", "orange_concrete", "orange_stained_glass", "gray_wool", "gray_stained_glass", "light_gray_stained_glass", "light_gray_stained_glass_pane"),
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 blockDisplay: DisplayEntity = DisplayEntity(DisplayEntityProperties())
private var timeAlive = 0
private var isInit = false
var isDead = false
@@ -56,6 +60,9 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos:
private var lifeTime = emitterParams.lifeTime.randomize()
private lateinit var particleEntity: DisplayEntity
private lateinit var debugOriginEntity: DisplayEntity
private lateinit var debugVelEntity: DisplayEntity
fun init() {
val so = emitterParams.spawnPosOffset
@@ -76,14 +83,14 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos:
val properties = DisplayEntityProperties(
pos = entityPos,
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),
leftRotation = quatRot,
scale = scale,
tags = arrayOf("BPS_Particle", "BPS_UUID", SESSION_UUID.toString())
)
blockDisplay = DisplayEntity(properties)
particleEntity = DisplayEntity(properties)
isInit = true
}
@@ -91,7 +98,7 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos:
fun spawn(world: ServerWorld) {
if (isInit) {
blockDisplay.spawn(world)
particleEntity.spawn(world)
LIVING_PARTICLE_COUNT += 1
}
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()
blockDisplay.updateProperties(newProperties)
particleEntity.updateProperties(newProperties)
if (timeAlive > lifeTime) { kill() }
@@ -127,7 +134,7 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos:
private fun calcNewProperties() : DisplayEntityProperties {
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 newRot = calcRot(timeAliveClamped)
val newScale = calcScale(timeAliveClamped)
@@ -147,7 +154,7 @@ class Particle(private val emitterParams: EmitterParams, private var entityPos:
val newProperties = DisplayEntityProperties(
pos = entityPos,
blockType = newBlock,
model = newModel,
translation = combinedOffset.add(newOriginOffset),
leftRotation = quatRot,
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() {
if (!isDead) {
blockDisplay.kill()
particleEntity.kill()
LIVING_PARTICLE_COUNT -= 1
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))
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.spawn(world)
@@ -13,17 +13,17 @@ fun addToRegister(id: String, params: EmitterParams) : Pair<String, Boolean> {
return Pair(newId, false)
}
val newBlocks: MutableList<String> = mutableListOf()
val newModels: MutableList<String> = mutableListOf()
for (block in params.blockCurve.first) {
val blockModIdRegex = ".*:".toRegex()
if (blockModIdRegex.containsMatchIn(block)) { break }
for (model in params.modelCurve.first) {
val modelModIdRegex = ".*:".toRegex()
if (modelModIdRegex.containsMatchIn(model)) { break }
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
println("BPS - Registered Emitter ID \"$newId\".")