Skip to content

Commit

Permalink
Complete backend for skills & refund, add starting ui for primaries
Browse files Browse the repository at this point in the history
  • Loading branch information
bibi-reden committed Jul 23, 2024
1 parent e33f61d commit 01ce8b2
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 46 deletions.
111 changes: 101 additions & 10 deletions src/client/kotlin/com/bibireden/playerex/ui/PlayerEXScreen.kt
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -29,35 +41,46 @@ class PlayerEXScreen : BaseUIModelScreen<FlowLayout>(FlowLayout::class.java, Dat
private var currentPage = 0

// TODO: Make registry based?
private val pages: List<List<Component>> = listOf(testLayout(), testLayout2()) // Temp just to help myself make code - prob will change
private val pages: MutableList<List<Component>> = 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() {
Expand All @@ -79,6 +102,58 @@ class PlayerEXScreen : BaseUIModelScreen<FlowLayout>(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<Component> = 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

Expand All @@ -95,6 +170,8 @@ class PlayerEXScreen : BaseUIModelScreen<FlowLayout>(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()
Expand Down Expand Up @@ -145,4 +222,18 @@ class PlayerEXScreen : BaseUIModelScreen<FlowLayout>(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
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,7 @@

<!-- content -->
<flow-layout direction="horizontal" id="content">
<children>
<stack-layout>
<children>

</children>
</stack-layout>
</children>
<children/>

<sizing><vertical method="fill">85</vertical><horizontal method="fill">100</horizontal></sizing>
<positioning type="relative">0,50</positioning>
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/bibireden/playerex/PlayerEXCommands.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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)
Expand All @@ -80,10 +80,11 @@ object PlayerEXAttributes {
}

val PRIMARY_ATTRIBUTE_IDS: Set<Identifier> = 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,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,17 @@ 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

this.sync { buf, player -> buf.writeNbt(toPacketNbt())}
}

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) {
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -218,6 +218,9 @@ class PlayerDataComponent(
return true
}

val modifiers: Map<Identifier, Double>
get() = this._modifiers

override val skillPoints: Int
get() = this._skillPoints

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions src/main/resources/assets/playerex/lang/en_us.json
Original file line number Diff line number Diff line change
@@ -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"},
Expand All @@ -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",
Expand Down

0 comments on commit 01ce8b2

Please sign in to comment.