diff --git a/src/client/kotlin/com/bibireden/playerex/ui/PlayerEXScreen.kt b/src/client/kotlin/com/bibireden/playerex/ui/PlayerEXScreen.kt index 041164d2..3b5c537f 100644 --- a/src/client/kotlin/com/bibireden/playerex/ui/PlayerEXScreen.kt +++ b/src/client/kotlin/com/bibireden/playerex/ui/PlayerEXScreen.kt @@ -1,23 +1,35 @@ package com.bibireden.playerex.ui +import com.bibireden.data_attributes.api.DataAttributesAPI +import com.bibireden.data_attributes.api.attribute.EntityAttributeSupplier +import com.bibireden.data_attributes.api.attribute.IEntityAttribute import com.bibireden.playerex.PlayerEX import com.bibireden.playerex.PlayerEXClient import com.bibireden.playerex.api.attribute.PlayerEXAttributes import com.bibireden.playerex.components.PlayerEXComponents +import com.bibireden.playerex.ext.id import com.bibireden.playerex.ext.level import com.bibireden.playerex.networking.NetworkingChannels import com.bibireden.playerex.networking.NetworkingPackets +import com.bibireden.playerex.networking.types.UpdatePacketType import com.bibireden.playerex.util.PlayerEXUtil import io.wispforest.owo.ui.base.BaseUIModelScreen import io.wispforest.owo.ui.component.ButtonComponent import io.wispforest.owo.ui.component.Components import io.wispforest.owo.ui.component.LabelComponent import io.wispforest.owo.ui.component.TextBoxComponent +import io.wispforest.owo.ui.container.Containers import io.wispforest.owo.ui.container.FlowLayout import io.wispforest.owo.ui.core.Component import io.wispforest.owo.ui.core.ParentComponent +import io.wispforest.owo.ui.core.Positioning +import io.wispforest.owo.ui.core.Sizing +import net.minecraft.client.network.ClientPlayerEntity +import net.minecraft.entity.attribute.EntityAttribute import net.minecraft.entity.player.PlayerEntity +import net.minecraft.registry.Registries import net.minecraft.text.Text +import net.minecraft.util.Colors import net.minecraft.util.Formatting import kotlin.reflect.KClass @@ -29,35 +41,46 @@ class PlayerEXScreen : BaseUIModelScreen(FlowLayout::class.java, Dat private var currentPage = 0 // TODO: Make registry based? - private val pages: List> = listOf(testLayout(), testLayout2()) // Temp just to help myself make code - prob will change + private val pages: MutableList> = mutableListOf() // Temp just to help myself make code - prob will change + + private val playerComponent by lazy { PlayerEXComponents.PLAYER_DATA.get(this.client?.player!!) } override fun shouldPause(): Boolean = false /** Whenever the level attribute gets modified, and on initialization of the screen, this will be called. */ fun onLevelUpdated() { val player = client?.player ?: return - val data = PlayerEXComponents.PLAYER_DATA.get(player) val root = this.uiAdapter.rootComponent root.childById(LabelComponent::class, "level:current")?.apply { text(Text.translatable("playerex.ui.current_level", player.level.toInt(), PlayerEXUtil.getRequiredXpForNextLevel(player))) } - root.childById(LabelComponent::class, "points_available")?.apply { - text(Text.literal(data.skillPoints.toString()) - .formatted(when (data.skillPoints) { - 0 -> Formatting.WHITE - else -> Formatting.YELLOW - } - )) - } + updatePointsAvailable() updateLevelUpButton(player, root.childById(TextBoxComponent::class, "level:amount")!!.text, root.childById(ButtonComponent::class, "level:button")!!) } /** Whenever any attribute is updated, this will be called. */ // todo: this is subject to change... and needs to be done first fun onAttributesUpdated() { + PlayerEXAttributes.PRIMARY_ATTRIBUTE_IDS.forEach { + val component = this.uiAdapter.rootComponent.childById(TextBoxComponent::class, "entry:${it}") + this.uiAdapter.rootComponent.childById(LabelComponent::class, "${it}:current_level")?.apply { + text(EntityAttributeSupplier(it).get()?.let { attribute -> attributeLabel(attribute, client?.player!!) }) + } + } + updatePointsAvailable() + } + private fun updatePointsAvailable() { + this.uiAdapter.rootComponent.childById(LabelComponent::class, "points_available")?.apply { + text(Text.literal(playerComponent.skillPoints.toString()) + .formatted(when (playerComponent.skillPoints) { + 0 -> Formatting.WHITE + else -> Formatting.YELLOW + } + )) + } } private fun onPagesUpdated() { @@ -79,6 +102,58 @@ class PlayerEXScreen : BaseUIModelScreen(FlowLayout::class.java, Dat levelUpButton.tooltip(Text.translatable("playerex.ui.level_button", PlayerEXUtil.getRequiredXpForLevel(player, result), amount, player.experienceLevel)) } + private fun attributeButtonComponent(attribute: EntityAttribute, type: AttributeButtonComponentType): Component { + val player = this.client?.player ?: return Components.label(Text.of("ohno")) + return Components.button(Text.of(type.symbol)) { + it.parent()?.childById(TextBoxComponent::class, "entry:${attribute.id}")?.let { ta -> + val amount = ta.text.toDoubleOrNull() ?: return@let + val points = if (type == AttributeButtonComponentType.Add) playerComponent.skillPoints else playerComponent.refundablePoints + + if (points < amount) return@let // invalid, not enough points. + + DataAttributesAPI.getValue(attribute, player).ifPresent { NetworkingChannels.MODIFY.clientHandle().send(NetworkingPackets.Update(type.packet, attribute.id, amount.toInt())) } + } + } + .renderer(ButtonComponent.Renderer.flat(Colors.BLACK, Colors.BLACK, Colors.BLACK)) + .sizing(Sizing.fixed(12), Sizing.fixed(12)) + } + + // todo: migrate + private fun createAttributeComponent(attribute: EntityAttribute): Component { + return Containers.horizontalFlow(Sizing.fill(100), Sizing.fixed(18)).also { + it.child(Components.label(Text.translatable(attribute.translationKey)).sizing(Sizing.content(), Sizing.fill(100))) + it.child(Components.label(attributeLabel(attribute, this.client?.player!!)).id("${attribute.id}:current_level")) + it.child( + Containers.horizontalFlow(Sizing.fill(50), Sizing.fill(100)).also { + it.child(attributeButtonComponent(attribute, AttributeButtonComponentType.Remove)) + it.child(attributeButtonComponent(attribute, AttributeButtonComponentType.Add)) + it.child( + Components.textBox(Sizing.fixed(27)) + .text("1") + .verticalSizing(Sizing.fixed(12)) + .id("entry:${attribute.id}") + ) + it.gap(4) + }.positioning(Positioning.relative(100, 0)) + ) + it.gap(3) + } + } + + private fun attributeLabel(attribute: EntityAttribute, player: ClientPlayerEntity): Text? { + return Text.literal("(").append(Text.literal("${DataAttributesAPI.getValue(attribute, player).map(Double::toInt).orElse(0)}").formatted(Formatting.GOLD)).append("/${(attribute as IEntityAttribute).`data_attributes$max`().toInt()})") + } + + // todo: migrate to Registry once completed + private fun temporarySupplyAttributePage(): List = listOf( + Containers.verticalFlow(Sizing.fill(75), Sizing.content()).also { + it.child(Components.label(Text.translatable("playerex.ui.category.primary_attributes"))) + it.child(Components.box(Sizing.fill(60), Sizing.fixed(2))) + it.children(PlayerEXAttributes.PRIMARY_ATTRIBUTE_IDS.mapNotNull(Registries.ATTRIBUTE::get).map(::createAttributeComponent)) + it.gap(5) + }.positioning(Positioning.relative(10, 25)) + ) + override fun build(rootComponent: FlowLayout) { val player = client?.player ?: return @@ -95,6 +170,8 @@ class PlayerEXScreen : BaseUIModelScreen(FlowLayout::class.java, Dat val content = rootComponent.childById(FlowLayout::class, "content")!! val footer = rootComponent.childById(FlowLayout::class, "footer")!! + pages.addAll(listOf(temporarySupplyAttributePage(), testLayout2())) + this.onLevelUpdated() this.onAttributesUpdated() this.onPagesUpdated() @@ -145,4 +222,18 @@ class PlayerEXScreen : BaseUIModelScreen(FlowLayout::class.java, Dat } ) } + + enum class AttributeButtonComponentType { + Add, + Remove; + + val symbol: String + get() = if (this == Add) "+" else "-" + + val packet: UpdatePacketType + get() = when (this) { + Add -> UpdatePacketType.Skill + Remove -> UpdatePacketType.Refund + } + } } \ No newline at end of file diff --git a/src/client/resources/assets/playerex/owo_ui/main_ui_model.xml b/src/client/resources/assets/playerex/owo_ui/main_ui_model.xml index 8299a55b..54223da4 100644 --- a/src/client/resources/assets/playerex/owo_ui/main_ui_model.xml +++ b/src/client/resources/assets/playerex/owo_ui/main_ui_model.xml @@ -84,13 +84,7 @@ - - - - - - - + 85100 0,50 diff --git a/src/main/kotlin/com/bibireden/playerex/PlayerEXCommands.kt b/src/main/kotlin/com/bibireden/playerex/PlayerEXCommands.kt index fe6fc3bf..2769d381 100644 --- a/src/main/kotlin/com/bibireden/playerex/PlayerEXCommands.kt +++ b/src/main/kotlin/com/bibireden/playerex/PlayerEXCommands.kt @@ -120,7 +120,7 @@ object PlayerEXCommands { else { val result = it - amount component.addSkillPoints(amount) - component.set(attribute, result) + component.set(attribute, result.toInt()) ctx.source.sendFeedback({ Text.translatable("playerex.command.refunded", amount, Text.translatable(attribute.translationKey), player.name) }, false) ctx.source.sendFeedback(updatedValueText(attribute, result), false) 1 diff --git a/src/main/kotlin/com/bibireden/playerex/api/attribute/PlayerEXAttributes.kt b/src/main/kotlin/com/bibireden/playerex/api/attribute/PlayerEXAttributes.kt index bd41b40f..9662bc1b 100644 --- a/src/main/kotlin/com/bibireden/playerex/api/attribute/PlayerEXAttributes.kt +++ b/src/main/kotlin/com/bibireden/playerex/api/attribute/PlayerEXAttributes.kt @@ -12,19 +12,22 @@ object PlayerEXAttributes { val LEVEL = register("level", 0.0, 0.0, 100.0) @JvmField - val CONSTITUTION = register("constitution", 0.0, 0.0, 1000000.0); + val CONSTITUTION = register("constitution", 0.0, 0.0, 100.0); @JvmField - val STRENGTH = register("strength", 0.0, 0.0, 1000000.0); + val STRENGTH = register("strength", 0.0, 0.0, 100.0); @JvmField - val DEXTERITY = register("dexterity", 0.0, 0.0, 1000000.0); + val DEXTERITY = register("dexterity", 0.0, 0.0, 100.0); @JvmField - val INTELLIGENCE = register("intelligence", 0.0, 0.0, 1000000.0); + val INTELLIGENCE = register("intelligence", 0.0, 0.0, 100.0); @JvmField - val LUCKINESS = register("luckiness", 0.0, 0.0, 1000000.0); + val LUCKINESS = register("luckiness", 0.0, 0.0, 100.0); + + @JvmField + val FOCUS = register("focus", 0.0, 0.0, 100.0) @JvmField val HEALTH_REGENERATION = register("health_regeneration", 0.0, 0.0, 100.0); @@ -36,10 +39,10 @@ object PlayerEXAttributes { val LIFESTEAL = register("lifesteal", 0.0, 0.0, 100.0); @JvmField - val MELEE_CRIT_DAMAGE = register("melee_crit_damage", 0.0, 0.0, 100.0); + val MELEE_CRIT_DAMAGE = register("melee_crit_damage", 0.0, 0.0, 1.0); @JvmField - val MELEE_CRIT_CHANCE = register("melee_crit_chance", 0.0, 0.0, 100.0); + val MELEE_CRIT_CHANCE = register("melee_crit_chance", 0.0, 0.0, 1.0); @JvmField val BREAKING_SPEED = register("breaking_speed", 0.0, 0.0, 100.0); @@ -62,14 +65,11 @@ object PlayerEXAttributes { @JvmField val EVASION = register("evasion", 0.0, 0.0, 100.0); - @JvmField - val FOCUS = register("focus", 0.0, 0.0, 1_000_000.0) - @JvmField val RANGED_DAMAGE = register("ranged_damage", 0.0, 0.0, 1000000.0) @JvmField - val RANGED_CRITICAL_CHANCE = register("ranged_crit_chance", 0.0, 0.0, 100.0) + val RANGED_CRITICAL_CHANCE = register("ranged_crit_chance", 0.0, 0.0, 1.0) @JvmField val RANGED_CRITICAL_DAMAGE = register("ranged_crit_damage", 0.0, 0.0, 1000000.0) @@ -80,10 +80,11 @@ object PlayerEXAttributes { } val PRIMARY_ATTRIBUTE_IDS: Set = setOf( - PlayerEXAttributes.CONSTITUTION.id, - PlayerEXAttributes.STRENGTH.id, - PlayerEXAttributes.DEXTERITY.id, - PlayerEXAttributes.INTELLIGENCE.id, - PlayerEXAttributes.LUCKINESS.id + CONSTITUTION.id, + STRENGTH.id, + DEXTERITY.id, + INTELLIGENCE.id, + LUCKINESS.id, + FOCUS.id, ) } \ No newline at end of file diff --git a/src/main/kotlin/com/bibireden/playerex/components/player/IPlayerDataComponent.kt b/src/main/kotlin/com/bibireden/playerex/components/player/IPlayerDataComponent.kt index 180901be..39f68403 100644 --- a/src/main/kotlin/com/bibireden/playerex/components/player/IPlayerDataComponent.kt +++ b/src/main/kotlin/com/bibireden/playerex/components/player/IPlayerDataComponent.kt @@ -13,7 +13,7 @@ interface IPlayerDataComponent : Component { /** Gets the cached [EntityAttribute] modifier value, or provides `0` if it does not exist. */ fun get(attribute: EntityAttribute): Double /** Applies the provided modifier value to the [EntityAttribute], and creates it if it does not exist. */ - fun set(attribute: EntityAttribute, value: Double) + fun set(attribute: EntityAttribute, value: Int) /** Removes the [EntityAttribute] modifier if it exists. */ fun remove(attribute: EntityAttribute) /** Adds the current [EntityAttribute]'s value with the provided value together. */ diff --git a/src/main/kotlin/com/bibireden/playerex/components/player/PlayerDataComponent.kt b/src/main/kotlin/com/bibireden/playerex/components/player/PlayerDataComponent.kt index 5384d984..1426b289 100644 --- a/src/main/kotlin/com/bibireden/playerex/components/player/PlayerDataComponent.kt +++ b/src/main/kotlin/com/bibireden/playerex/components/player/PlayerDataComponent.kt @@ -107,9 +107,9 @@ class PlayerDataComponent( return this._modifiers.getOrDefault(attribute.id, 0.0) } - override fun set(attribute: EntityAttribute, value: Double) { + override fun set(attribute: EntityAttribute, value: Int) { val identifier = attribute.id - val attributeValue = attribute.clamp(value) + val attributeValue = attribute.clamp(value.toDouble()) if (!this.trySet(identifier, attributeValue)) return @@ -117,7 +117,7 @@ class PlayerDataComponent( } override fun add(attribute: EntityAttribute, value: Double) { - this.set(attribute, value + this.get(attribute)) + this.set(attribute, (value + this.get(attribute)).toInt()) } override fun remove(attribute: EntityAttribute) { @@ -131,7 +131,7 @@ class PlayerDataComponent( val partition = if (percent == 0) 0.0 else percent / 100.0 - for ((id, value) in this._modifiers) { + for ((id, value) in this.modifiers) { if (partition == 0.0) { this.tryRemove(id) this._modifiers.remove(id) @@ -186,7 +186,7 @@ class PlayerDataComponent( player.addExperienceLevels(-required) component.addSkillPoints(skillPoints) - component.set(PlayerEXAttributes.LEVEL, expectedLevel) + component.set(PlayerEXAttributes.LEVEL, expectedLevel.toInt()) } return@map isEnoughExperience }.orElse(false) @@ -204,7 +204,7 @@ class PlayerDataComponent( if (skillPoints < amount) return@map false this._skillPoints -= amount } - this.set(skill, expected) + this.set(skill, expected.toInt()) // signal to a client that an increase has occurred... NetworkingChannels.NOTIFICATIONS.serverHandle(player).send(NetworkingPackets.Notify(NotificationType.Spent)) return@map true @@ -218,6 +218,9 @@ class PlayerDataComponent( return true } + val modifiers: Map + get() = this._modifiers + override val skillPoints: Int get() = this._skillPoints diff --git a/src/main/kotlin/com/bibireden/playerex/networking/NetworkingPackets.kt b/src/main/kotlin/com/bibireden/playerex/networking/NetworkingPackets.kt index ebf89934..64810d03 100644 --- a/src/main/kotlin/com/bibireden/playerex/networking/NetworkingPackets.kt +++ b/src/main/kotlin/com/bibireden/playerex/networking/NetworkingPackets.kt @@ -15,9 +15,6 @@ object NetworkingPackets { /** * Updates the provided attribute with an associated [Double] value. * Possibility will be dictated based on the [UpdatePacketType] - * - * todo: make it add the amounts given, not set. - * todo: also, ensure they SHOULD be able to do this, based on the amount of skill points. */ @JvmRecord data class Update(val type: UpdatePacketType, val id: Identifier, val amount: Int) diff --git a/src/main/resources/assets/playerex/lang/en_us.json b/src/main/resources/assets/playerex/lang/en_us.json index 29d10f81..38b0b448 100644 --- a/src/main/resources/assets/playerex/lang/en_us.json +++ b/src/main/resources/assets/playerex/lang/en_us.json @@ -1,6 +1,8 @@ { "playerex.key.main_screen": "Open Main Screen", + "playerex.ui.category.primary_attributes": "Attributes", + "playerex.ui.current_level": [ {"text": "❤ ", "color": "#F6CB3B"}, {"text": "Level: ", "color": "white"}, @@ -16,9 +18,9 @@ {"index": 0}, {"text": " XP in order to level by ", "color": "white"}, {"index": 1}, - {"text": " points.", "color": "white"}, "\n", + {"text": " point(s).", "color": "white"}, "\n", {"text": "★ ", "color": "#62D16A"}, - {"text": " Current XP: ", "color": "white"}, + {"text": "Current XP: ", "color": "white"}, {"index": 2} ], "playerex.ui.text.nameplate": "lvl %s",