fix entity attribute stuff

This commit is contained in:
Bixilon 2021-10-24 16:35:33 +02:00
parent 2008dcba18
commit 34f15d2baf
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
8 changed files with 71 additions and 43 deletions

View File

@ -26,8 +26,8 @@ import de.bixilon.minosoft.data.registries.AABB
import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.blocks.types.FluidBlock import de.bixilon.minosoft.data.registries.blocks.types.FluidBlock
import de.bixilon.minosoft.data.registries.effects.StatusEffect import de.bixilon.minosoft.data.registries.effects.StatusEffect
import de.bixilon.minosoft.data.registries.effects.attributes.StatusEffectAttribute import de.bixilon.minosoft.data.registries.effects.attributes.EntityAttribute
import de.bixilon.minosoft.data.registries.effects.attributes.StatusEffectAttributeInstance import de.bixilon.minosoft.data.registries.effects.attributes.EntityAttributeModifier
import de.bixilon.minosoft.data.registries.effects.attributes.StatusEffectOperations import de.bixilon.minosoft.data.registries.effects.attributes.StatusEffectOperations
import de.bixilon.minosoft.data.registries.enchantment.Enchantment import de.bixilon.minosoft.data.registries.enchantment.Enchantment
import de.bixilon.minosoft.data.registries.entities.EntityType import de.bixilon.minosoft.data.registries.entities.EntityType
@ -72,7 +72,7 @@ abstract class Entity(
protected val random = Random protected val random = Random
open val equipment: MutableMap<EquipmentSlots, ItemStack> = mutableMapOf() open val equipment: MutableMap<EquipmentSlots, ItemStack> = mutableMapOf()
val activeStatusEffects: MutableMap<StatusEffect, StatusEffectInstance> = synchronizedMapOf() val activeStatusEffects: MutableMap<StatusEffect, StatusEffectInstance> = synchronizedMapOf()
val attributes: MutableMap<ResourceLocation, MutableMap<UUID, StatusEffectAttributeInstance>> = synchronizedMapOf() val attributes: MutableMap<ResourceLocation, EntityAttribute> = synchronizedMapOf()
val id: Int? val id: Int?
get() = connection.world.entities.getId(this) get() = connection.world.entities.getId(this)
@ -154,28 +154,30 @@ abstract class Entity(
activeStatusEffects.remove(effect) activeStatusEffects.remove(effect)
} }
fun getAttributeValue(attribute: ResourceLocation, baseValue: Double = entityType.attributes[attribute] ?: 1.0): Double { fun getAttributeValue(name: ResourceLocation, baseValue: Double? = null): Double {
// ToDo: Check order and verify value // ToDo: Check order and verify value
var ret = baseValue val attribute = attributes[name]
val realBaseValue = baseValue ?: attribute?.baseValue ?: 1.0
var ret = realBaseValue
fun addToValue(statusEffectAttribute: StatusEffectAttribute, amplifier: Int) { fun addToValue(modifier: EntityAttributeModifier, amplifier: Int) {
val instanceValue = statusEffectAttribute.amount * amplifier val instanceValue = modifier.amount * amplifier
when (statusEffectAttribute.operation) { when (modifier.operation) {
StatusEffectOperations.MULTIPLY_TOTAL -> ret *= 1.0 + instanceValue StatusEffectOperations.MULTIPLY_TOTAL -> ret *= 1.0 + instanceValue
StatusEffectOperations.ADDITION -> ret += instanceValue StatusEffectOperations.ADDITION -> ret += instanceValue
StatusEffectOperations.MULTIPLY_BASE -> ret += baseValue * (instanceValue + 1.0) StatusEffectOperations.MULTIPLY_BASE -> ret += realBaseValue * (instanceValue + 1.0)
} }
} }
attributes[attribute]?.let { attribute?.let {
for (instance in it.values) { for (instance in it.modifiers.values) {
addToValue(instance.statusEffectAttribute, instance.amplifier) addToValue(instance, 1)
} }
} }
for (statusEffect in activeStatusEffects.values) { for (statusEffect in activeStatusEffects.values) {
for ((instanceResourceLocation, instance) in statusEffect.statusEffect.attributes) { for ((instanceResourceLocation, instance) in statusEffect.statusEffect.attributes) {
if (instanceResourceLocation != attribute) { if (instanceResourceLocation != name) {
continue continue
} }
addToValue(instance, statusEffect.amplifier) addToValue(instance, statusEffect.amplifier)

View File

@ -30,7 +30,7 @@ import de.bixilon.minosoft.data.registries.blocks.types.Block
import de.bixilon.minosoft.data.registries.effects.DefaultStatusEffects import de.bixilon.minosoft.data.registries.effects.DefaultStatusEffects
import de.bixilon.minosoft.data.registries.effects.attributes.DefaultStatusEffectAttributeNames import de.bixilon.minosoft.data.registries.effects.attributes.DefaultStatusEffectAttributeNames
import de.bixilon.minosoft.data.registries.effects.attributes.DefaultStatusEffectAttributes import de.bixilon.minosoft.data.registries.effects.attributes.DefaultStatusEffectAttributes
import de.bixilon.minosoft.data.registries.effects.attributes.StatusEffectAttributeInstance import de.bixilon.minosoft.data.registries.effects.attributes.EntityAttribute
import de.bixilon.minosoft.data.registries.enchantment.DefaultEnchantments import de.bixilon.minosoft.data.registries.enchantment.DefaultEnchantments
import de.bixilon.minosoft.data.registries.items.DefaultItems import de.bixilon.minosoft.data.registries.items.DefaultItems
import de.bixilon.minosoft.data.registries.items.Item import de.bixilon.minosoft.data.registries.items.Item
@ -133,10 +133,10 @@ class LocalPlayerEntity(
if (value == field) { if (value == field) {
return return
} }
attributes[DefaultStatusEffectAttributeNames.GENERIC_MOVEMENT_SPEED]?.remove(DefaultStatusEffectAttributes.SPRINT_SPEED_BOOST.uuid) attributes[DefaultStatusEffectAttributeNames.GENERIC_MOVEMENT_SPEED]?.modifiers?.remove(DefaultStatusEffectAttributes.SPRINT_SPEED_BOOST.uuid)
if (value) { if (value) {
attributes.getOrPut(DefaultStatusEffectAttributeNames.GENERIC_MOVEMENT_SPEED) { synchronizedMapOf() }[DefaultStatusEffectAttributes.SPRINT_SPEED_BOOST.uuid] = StatusEffectAttributeInstance(DefaultStatusEffectAttributes.SPRINT_SPEED_BOOST, 1) attributes.getOrPut(DefaultStatusEffectAttributeNames.GENERIC_MOVEMENT_SPEED) { EntityAttribute() }.modifiers[DefaultStatusEffectAttributes.SPRINT_SPEED_BOOST.uuid] = DefaultStatusEffectAttributes.SPRINT_SPEED_BOOST
} }
field = value field = value
} }

View File

@ -13,7 +13,7 @@
package de.bixilon.minosoft.data.registries.effects package de.bixilon.minosoft.data.registries.effects
import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.effects.attributes.StatusEffectAttribute import de.bixilon.minosoft.data.registries.effects.attributes.EntityAttributeModifier
import de.bixilon.minosoft.data.registries.registries.Registries import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.data.registries.registries.registry.RegistryItem import de.bixilon.minosoft.data.registries.registries.registry.RegistryItem
import de.bixilon.minosoft.data.registries.registries.registry.ResourceLocationDeserializer import de.bixilon.minosoft.data.registries.registries.registry.ResourceLocationDeserializer
@ -32,8 +32,8 @@ data class StatusEffect(
val category: StatusEffectCategories, val category: StatusEffectCategories,
override val translationKey: ResourceLocation?, override val translationKey: ResourceLocation?,
val color: RGBColor, val color: RGBColor,
val attributes: Map<ResourceLocation, StatusEffectAttribute>, val attributes: Map<ResourceLocation, EntityAttributeModifier>,
val uuidAttributes: Map<UUID, StatusEffectAttribute>, val uuidAttributes: Map<UUID, EntityAttributeModifier>,
) : RegistryItem(), Translatable { ) : RegistryItem(), Translatable {
override fun toString(): String { override fun toString(): String {
@ -42,12 +42,12 @@ data class StatusEffect(
companion object : ResourceLocationDeserializer<StatusEffect> { companion object : ResourceLocationDeserializer<StatusEffect> {
override fun deserialize(registries: Registries?, resourceLocation: ResourceLocation, data: Map<String, Any>): StatusEffect { override fun deserialize(registries: Registries?, resourceLocation: ResourceLocation, data: Map<String, Any>): StatusEffect {
val attributes: MutableMap<ResourceLocation, StatusEffectAttribute> = mutableMapOf() val attributes: MutableMap<ResourceLocation, EntityAttributeModifier> = mutableMapOf()
val uuidAttributes: MutableMap<UUID, StatusEffectAttribute> = mutableMapOf() val uuidAttributes: MutableMap<UUID, EntityAttributeModifier> = mutableMapOf()
data["attributes"]?.compoundCast()?.let { data["attributes"]?.compoundCast()?.let {
for ((key, value) in it) { for ((key, value) in it) {
val attribute = StatusEffectAttribute.deserialize(value.asCompound()) val attribute = EntityAttributeModifier.deserialize(value.asCompound())
attributes[ResourceLocation.getResourceLocation(key).fix()] = attribute attributes[ResourceLocation.getResourceLocation(key).fix()] = attribute
uuidAttributes[attribute.uuid] = attribute uuidAttributes[attribute.uuid] = attribute
} }

View File

@ -16,5 +16,5 @@ package de.bixilon.minosoft.data.registries.effects.attributes
import de.bixilon.minosoft.util.KUtil.asUUID import de.bixilon.minosoft.util.KUtil.asUUID
object DefaultStatusEffectAttributes { object DefaultStatusEffectAttributes {
val SPRINT_SPEED_BOOST = StatusEffectAttribute("Sprinting speed boost", "662A6B8D-DA3E-4C1C-8813-96EA6097278D".asUUID(), 0.30000001192092896, StatusEffectOperations.MULTIPLY_TOTAL) val SPRINT_SPEED_BOOST = EntityAttributeModifier("Sprinting speed boost", "662A6B8D-DA3E-4C1C-8813-96EA6097278D".asUUID(), 0.30000001192092896, StatusEffectOperations.MULTIPLY_TOTAL)
} }

View File

@ -13,7 +13,17 @@
package de.bixilon.minosoft.data.registries.effects.attributes package de.bixilon.minosoft.data.registries.effects.attributes
class StatusEffectAttributeInstance( import de.bixilon.minosoft.util.KUtil.synchronizedMapOf
val statusEffectAttribute: StatusEffectAttribute, import java.util.*
val amplifier: Int,
) data class EntityAttribute(
var baseValue: Double = 1.0,
val modifiers: MutableMap<UUID, EntityAttributeModifier> = synchronizedMapOf(),
) {
fun merge(other: EntityAttribute) {
baseValue = other.baseValue
for ((key, modifier) in other.modifiers) {
modifiers[key] = modifier
}
}
}

View File

@ -16,7 +16,7 @@ import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.Util import de.bixilon.minosoft.util.Util
import java.util.* import java.util.*
data class StatusEffectAttribute( data class EntityAttributeModifier(
val name: String, val name: String,
val uuid: UUID, val uuid: UUID,
val amount: Double, val amount: Double,
@ -27,8 +27,8 @@ data class StatusEffectAttribute(
} }
companion object { companion object {
fun deserialize(data: Map<String, Any>): StatusEffectAttribute { fun deserialize(data: Map<String, Any>): EntityAttributeModifier {
return StatusEffectAttribute( return EntityAttributeModifier(
name = data["name"].unsafeCast(), name = data["name"].unsafeCast(),
uuid = Util.getUUIDFromString(data["uuid"].unsafeCast()), uuid = Util.getUUIDFromString(data["uuid"].unsafeCast()),
amount = data["amount"].unsafeCast(), amount = data["amount"].unsafeCast(),

View File

@ -26,6 +26,8 @@ import de.bixilon.minosoft.util.KUtil.decide
import de.bixilon.minosoft.util.KUtil.unsafeCast import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.MMath.ceil import de.bixilon.minosoft.util.MMath.ceil
import glm_.vec2.Vec2i import glm_.vec2.Vec2i
import java.lang.Float.max
import java.lang.Float.min
class HotbarHealthElement(hudRenderer: HUDRenderer) : Element(hudRenderer), Pollable { class HotbarHealthElement(hudRenderer: HUDRenderer) : Element(hudRenderer), Pollable {
private val witherStatusEffect = hudRenderer.connection.registries.statusEffectRegistry[DefaultStatusEffects.WITHER] private val witherStatusEffect = hudRenderer.connection.registries.statusEffectRegistry[DefaultStatusEffects.WITHER]
@ -234,13 +236,15 @@ class HotbarHealthElement(hudRenderer: HUDRenderer) : Element(hudRenderer), Poll
val wither = witherStatusEffect?.let { player.activeStatusEffects[it] != null } ?: false val wither = witherStatusEffect?.let { player.activeStatusEffects[it] != null } ?: false
val frozen = player.ticksFrozen > 0 val frozen = player.ticksFrozen > 0
val absorptionsAmount = max(0.0f, player.playerAbsorptionHearts) // ToDo: This is (probably) calculated as effect instance
val maxHealth = max(0.0f, player.getAttributeValue(DefaultStatusEffectAttributeNames.GENERIC_MAX_HEALTH).toFloat())
var health = player.healthCondition.hp var health = player.healthCondition.hp
if (health > 0.0f && health < 0.5f) { if (health > 0.0f && health < 0.5f) {
health = 0.5f health = 0.5f
} }
val absorptionsAmount = player.playerAbsorptionHearts // ToDo: This is (probably) calculated as effect instance health = min(health, maxHealth)
val maxHealth = player.getAttributeValue(DefaultStatusEffectAttributeNames.GENERIC_MAX_HEALTH).toFloat()
if (this.hardcode == hardcode && this.poison == poison && this.wither == wither && this.frozen == frozen && this.health == health && this.absorptionsAmount == absorptionsAmount && this.maxHealth == maxHealth) { if (this.hardcode == hardcode && this.poison == poison && this.wither == wither && this.frozen == frozen && this.health == health && this.absorptionsAmount == absorptionsAmount && this.maxHealth == maxHealth) {
return false return false

View File

@ -13,8 +13,10 @@
package de.bixilon.minosoft.protocol.packets.s2c.play package de.bixilon.minosoft.protocol.packets.s2c.play
import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.effects.attributes.StatusEffectAttribute import de.bixilon.minosoft.data.registries.effects.attributes.EntityAttribute
import de.bixilon.minosoft.data.registries.effects.attributes.EntityAttributeModifier
import de.bixilon.minosoft.data.registries.effects.attributes.StatusEffectOperations import de.bixilon.minosoft.data.registries.effects.attributes.StatusEffectOperations
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_14W04A import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_14W04A
@ -26,34 +28,44 @@ import java.util.*
class EntityEffectAttributesS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() { class EntityEffectAttributesS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
val entityId: Int = buffer.readEntityId() val entityId: Int = buffer.readEntityId()
val attributes: Map<ResourceLocation, StatusEffectAttribute> val attributes: Map<ResourceLocation, EntityAttribute>
init { init {
val attributes: MutableMap<ResourceLocation, StatusEffectAttribute> = mutableMapOf() val attributes: MutableMap<ResourceLocation, EntityAttribute> = mutableMapOf()
val count: Int = if (buffer.versionId < V_21W08A) { val attributeCount: Int = if (buffer.versionId < V_21W08A) {
buffer.readInt() buffer.readInt()
} else { } else {
buffer.readVarInt() buffer.readVarInt()
} }
for (i in 0 until count) { for (i in 0 until attributeCount) {
val key: ResourceLocation = buffer.readResourceLocation() val key: ResourceLocation = buffer.readResourceLocation()
val value: Double = buffer.readDouble() // ToDo: For what is this? val baseValue: Double = buffer.readDouble()
val listLength: Int = if (buffer.versionId < V_14W04A) { val attribute = EntityAttribute(baseValue = baseValue)
val modifierCount: Int = if (buffer.versionId < V_14W04A) {
buffer.readUnsignedShort() buffer.readUnsignedShort()
} else { } else {
buffer.readVarInt() buffer.readVarInt()
} }
for (ii in 0 until listLength) { for (ii in 0 until modifierCount) {
val uuid: UUID = buffer.readUUID() val uuid: UUID = buffer.readUUID()
val amount: Double = buffer.readDouble() val amount: Double = buffer.readDouble()
val operation = StatusEffectOperations[buffer.readUnsignedByte()] val operation = StatusEffectOperations[buffer.readUnsignedByte()]
// ToDo: modifiers attribute.modifiers[uuid] = EntityAttributeModifier(key.toString(), uuid, amount, operation)
attributes[key] = StatusEffectAttribute("", uuid, amount, operation)
} }
attributes[key] = attribute
} }
this.attributes = attributes.toMap() this.attributes = attributes.toMap()
} }
override fun handle(connection: PlayConnection) {
connection.world.entities[entityId]?.let {
for ((key, attribute) in this.attributes) {
it.attributes.getOrPut(key) { EntityAttribute(baseValue = it.entityType.attributes[key] ?: 1.0) }.merge(attribute)
}
}
}
override fun log() { override fun log() {
Log.log(LogMessageType.NETWORK_PACKETS_IN, LogLevels.VERBOSE) { "Entity effect attributes (entityId=$entityId, attributes=$attributes)" } Log.log(LogMessageType.NETWORK_PACKETS_IN, LogLevels.VERBOSE) { "Entity effect attributes (entityId=$entityId, attributes=$attributes)" }
} }