Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce New PlayerEX UI #56

Merged
merged 3 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/audit-pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v2
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: setup jdk ${{ matrix.java }}
uses: actions/setup-java@v3
uses: actions/setup-java@v1
with:
java-version: ${{ matrix.java }}
- name: make gradle wrapper executable
Expand All @@ -29,4 +29,4 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: Artifacts
path: build/libs/
path: build/libs/
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
## Fixes 🌽
- Resolved a mixin target for Forge users (on Sinytra) that caused items to break immediately.
- Thank you **`AndyNoob` https://github.com/AndyNoob** for this contribution.
**This slates the first official release of the new PlayerEX. It had been stable for a while, although the UI was not fully completed.**

## Additions 🍎
- Finished the PlayerEX UI to the point where it is ready for release.
- There is now a component set on the right of the UI that has a list of current pages. Use it in order to navigate between multiple mods who use the registry.
8 changes: 4 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
id 'fabric-loom' version "1.7-SNAPSHOT"
id 'fabric-loom' version "1.9-SNAPSHOT"
id 'maven-publish'
id "org.jetbrains.kotlin.jvm" version "2.0.0"
id 'org.jetbrains.kotlin.plugin.serialization' version '2.0.0'
id("com.google.devtools.ksp") version "2.0.0-1.0.21"
id "org.jetbrains.kotlin.jvm" version "2.1.0"
id 'org.jetbrains.kotlin.plugin.serialization' version '2.1.0'
id "com.google.devtools.ksp" version "2.1.0-1.0.29"
}

version = project.mod_version
Expand Down
6 changes: 3 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
org.gradle.jvmargs=-Xmx4G
org.gradle.jvmargs=-Xmx8G
org.gradle.parallel=true

ksp_version=2.0.0-1.0.21
Expand All @@ -9,8 +9,8 @@ loader=fabric
# Fabric Properties
minecraft_version=1.20.1
loader_version=0.16.7
fabric_kotlin_version=1.12.3+kotlin.2.0.21
loom_version=1.8-SNAPSHOT
fabric_kotlin_version=1.13.0+kotlin.2.1.0
loom_version=1.9-SNAPSHOT

# Mappings
parchment_version=1.20.1:2023.09.03
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
package com.bibireden.playerex.mixin;

import com.bibireden.data_attributes.api.DataAttributesAPI;
import com.bibireden.playerex.ui.PlayerEXScreen;
import com.mojang.authlib.GameProfile;
import de.dafuqs.additionalentityattributes.AdditionalEntityAttributes;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
Expand All @@ -11,11 +21,33 @@
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(LocalPlayer.class)
abstract class LocalPlayerMixin {
abstract class LocalPlayerMixin extends LivingEntity {
@Shadow @Final protected Minecraft minecraft;

protected LocalPlayerMixin(EntityType<? extends LivingEntity> entityType, Level level) {
super(entityType, level);
}

@Inject(method = "setExperienceValues", at = @At("TAIL"))
private void playerex$setExperienceValues(CallbackInfo ci) {
if (minecraft.screen instanceof PlayerEXScreen screen) screen.onExperienceUpdated();
}

@Override
public void setHealth(float health) {
super.setHealth(health);
if (minecraft == null) return;
if (minecraft.screen instanceof PlayerEXScreen screen) {
screen.onAttributeUpdated(Attributes.MAX_HEALTH, getMaxHealth());
}
}

@Override
public void setAirSupply(int air) {
super.setAirSupply(air);
if (minecraft == null) return;
if (minecraft.screen instanceof PlayerEXScreen screen) {
screen.onAttributeUpdated(AdditionalEntityAttributes.LUNG_CAPACITY, DataAttributesAPI.getValue(AdditionalEntityAttributes.LUNG_CAPACITY, this).orElse(0.0));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static List<ResourceLocation> getIds() {
}

@NotNull
public static List<Class<? extends MenuComponent>> getDefs() {
public static List<Class<? extends MenuComponent>> getComponents() {
return ENTRIES.stream().map(Pair::component2).collect(Collectors.toUnmodifiableList());
}

Expand Down
1 change: 1 addition & 0 deletions src/client/kotlin/com/bibireden/playerex/PlayerEXClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import org.lwjgl.glfw.GLFW

object PlayerEXClient : ClientModInitializer {
val MAIN_UI_SCREEN_ID = PlayerEX.id("main_ui_model")
val MAIN_UI_SCREEN_ID_TEMP = PlayerEX.id("main_ui_model_v2")

val KEYBINDING_MAIN_SCREEN: KeyMapping = KeyBindingHelper.registerKeyBinding(KeyMapping("${PlayerEX.MOD_ID}.key.main_screen", InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_MINUS, "key.categories.${PlayerEX.MOD_ID}"))

Expand Down
129 changes: 79 additions & 50 deletions src/client/kotlin/com/bibireden/playerex/ui/PlayerEXScreen.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bibireden.playerex.ui

import com.bibireden.data_attributes.ui.renderers.ButtonRenderers
import com.bibireden.playerex.PlayerEXClient
import com.bibireden.playerex.components.player.IPlayerDataComponent
import com.bibireden.playerex.ext.component
Expand All @@ -14,28 +15,31 @@ import com.bibireden.playerex.ui.components.buttons.AttributeButtonComponent
import com.bibireden.playerex.ui.helper.InputHelper
import com.bibireden.playerex.ui.util.Colors
import com.bibireden.playerex.util.PlayerEXUtil
import io.wispforest.endec.impl.StructEndecBuilder
import io.wispforest.owo.ui.base.BaseUIModelScreen
import io.wispforest.owo.ui.component.*
import io.wispforest.owo.ui.container.Containers
import io.wispforest.owo.ui.container.FlowLayout
import io.wispforest.owo.ui.core.*
import io.wispforest.owo.ui.core.Component as OwoComponent
import io.wispforest.owo.ui.core.Easing
import io.wispforest.owo.ui.core.OwoUIDrawContext
import io.wispforest.owo.ui.core.ParentComponent
import io.wispforest.owo.ui.core.Sizing
import io.wispforest.owo.util.EventSource
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.Style
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.Mth
import net.minecraft.world.entity.ai.attributes.Attribute
import net.minecraft.network.chat.Component
import kotlin.reflect.KClass

// Transformers
fun <T : OwoComponent> ParentComponent.childById(clazz: KClass<T>, id: String) = this.childById(clazz.java, id)

/** Primary screen for the mod that brings everything intended together. */
class PlayerEXScreen : BaseUIModelScreen<FlowLayout>(FlowLayout::class.java, DataSource.asset(PlayerEXClient.MAIN_UI_SCREEN_ID)) {
private var listCollapsed = false

private var currentPage = 0

private val pages: MutableList<MenuComponent> = mutableListOf()
private val pages: MutableList<Pair<ResourceLocation, MenuComponent>> = mutableListOf()

private val player by lazy { this.minecraft!!.player!! }

Expand All @@ -46,6 +50,7 @@ class PlayerEXScreen : BaseUIModelScreen<FlowLayout>(FlowLayout::class.java, Dat
private val levelAmount by lazy { uiAdapter.rootComponent.childById(TextBoxComponent::class, "level:amount")!! }
private val levelButton by lazy { uiAdapter.rootComponent.childById(ButtonComponent::class, "level:button")!! }


private val onLevelUpdatedEvents = OnLevelUpdated.stream
private val onLevelUpdated: EventSource<OnLevelUpdated> = onLevelUpdatedEvents.source()

Expand Down Expand Up @@ -78,27 +83,27 @@ class PlayerEXScreen : BaseUIModelScreen<FlowLayout>(FlowLayout::class.java, Dat

private fun updatePointsAvailable() {
this.uiAdapter.rootComponent.childById(LabelComponent::class, "points_available")?.apply {
text(Component.translatable("playerex.ui.main.skill_points_header").append(": [").append(
text(Component.translatable("playerex.ui.main.skill_points_header").append(": ").append(
Component.literal("${player.component.skillPoints}").withStyle {
it.withColor(
when (player.component.skillPoints) {
0 -> Colors.GRAY
else -> Colors.SATURATED_BLUE
}
)
}).append("]")
})
)
}
}

private fun onPagesUpdated() {
/** changes the page based on the index. */
private fun switchPage(to: Int) {
val root = this.uiAdapter.rootComponent
val pageCounter = root.childById(LabelComponent::class, "counter")!!

val content = root.childById(FlowLayout::class, "content")!!

pageCounter.text(Component.nullToEmpty("${currentPage + 1}/${pages.size}"))
content.clearChildren()
content.child(pages[currentPage])
content.child(pages[to].second)
}

private fun updateLevelUpButton() {
Expand All @@ -116,64 +121,88 @@ class PlayerEXScreen : BaseUIModelScreen<FlowLayout>(FlowLayout::class.java, Dat
val required = PlayerEXUtil.getRequiredXpForNextLevel(player)
result = Mth.clamp((player.experienceLevel.toDouble() / required) * 100, 0.0, 100.0)
}
footer.childById(BoxComponent::class, "progress")!!
.horizontalSizing().animate(250, Easing.CUBIC, Sizing.fill(result.toInt())).forwards()
footer.childById(BoxComponent::class, "progress")?.horizontalSizing()?.animate(250, Easing.CUBIC, Sizing.fill(result.toInt()))?.forwards()
}

override fun build(rootComponent: FlowLayout) {
val player = minecraft?.player ?: return

val levelUpButton = rootComponent.childById(ButtonComponent::class, "level:button")!!
private fun updateCollapseState(content: FlowLayout, listLayout: FlowLayout, button: ButtonComponent) {
button.message = Component.literal(if (listCollapsed) "<" else ">").withStyle(Style.EMPTY.withBold(true))

updateLevelUpButton()

levelAmount.setFilter(InputHelper::isUIntInput)
levelAmount.onChanged().subscribe { updateLevelUpButton() }
content.horizontalSizing().animate(500, Easing.SINE, if (listCollapsed) Sizing.fill(97) else Sizing.fill(85)).forwards()
button.horizontalSizing().animate(500, Easing.SINE, if (listCollapsed) Sizing.fixed(10) else Sizing.fill(10)).forwards()
listLayout.horizontalSizing().animate(500, Easing.SINE, Sizing.fill(if (listCollapsed) 3 else 15)).forwards()
}

val previousPage = rootComponent.childById(ButtonComponent::class, "previous")!!
val pageCounter = rootComponent.childById(LabelComponent::class, "counter")!!
val nextPage = rootComponent.childById(ButtonComponent::class, "next")!!
val exit = rootComponent.childById(ButtonComponent::class, "exit")!!
override fun build(rootComponent: FlowLayout) {
val listLayout = rootComponent.childById(FlowLayout::class, "list")!!
val contentLayout = rootComponent.childById(FlowLayout::class, "content")!!

PlayerEXMenuRegistry.get().forEach { (_, clazz) ->
PlayerEXMenuRegistry.get().forEach { (resource, clazz) ->
val instance = clazz.getDeclaredConstructor().newInstance()
instance.init(minecraft!!, this, player.component)
instance.build(content)
pages.add(instance)
instance.build(contentLayout)
pages.add(Pair(resource, instance))
}

this.onLevelUpdated(player.level.toInt())
this.onPagesUpdated()
listLayout.child(
Components.button(Component.empty()) {
listCollapsed = !listCollapsed
updateCollapseState(contentLayout, listLayout, it)
}
.apply {
renderer(ButtonRenderers.STANDARD)
textShadow(false)
verticalSizing(Sizing.fill(100))

updateCollapseState(contentLayout, listLayout, this)
}
)

listLayout.child(
Containers.verticalScroll(Sizing.fill(90), Sizing.fill(100), Containers.verticalFlow(Sizing.content(), Sizing.content())
.apply {
gap(2)
padding(Insets.of(4))
verticalAlignment(VerticalAlignment.TOP)
horizontalAlignment(HorizontalAlignment.CENTER)
}
.also { vf ->
pages.forEachIndexed { index, (resource) ->
vf.child(
Components.button(Component.translatable("playerex.ui.menu.${resource.toLanguageKey()}")) { this.switchPage(index) }
.renderer(ButtonRenderers.STANDARD)
)
}
}
)
.apply {
positioning(Positioning.relative(100, 0))
}
.id("scroll")
)

pageCounter.text(Component.nullToEmpty("${currentPage + 1}/${pages.size}"))
val levelUpButton = rootComponent.childById(ButtonComponent::class, "level:button")!!
val exit = rootComponent.childById(ButtonComponent::class, "exit")!!

content.clearChildren()
content.child(pages[currentPage])

previousPage.onPress {
if (currentPage > 0) {
currentPage--
this.onPagesUpdated()
}
}
nextPage.onPress {
if (currentPage < pages.lastIndex) {
currentPage++
this.onPagesUpdated()
}
}
levelAmount.setFilter(InputHelper::isUIntInput)
levelAmount.onChanged().subscribe { updateLevelUpButton() }

levelUpButton.onPress {
levelAmount.value.toIntOrNull()?.let { NetworkingChannels.MODIFY.clientHandle().send(NetworkingPackets.Level(it)) }
}
onLevelUpdated(player.level.toInt())
onExperienceUpdated()

switchPage(0)

onLevelUpdated.subscribe { this.updateLevelUpButton() }
onLevelUpdated.subscribe { updateLevelUpButton() }

levelUpButton.onPress { levelAmount.value.toIntOrNull()?.let { NetworkingChannels.MODIFY.clientHandle().send(NetworkingPackets.Level(it)) } }
exit.onPress { this.onClose() }
}

/** Whenever the player's experience is changed, refreshing the current status of experience-tied ui elements. */
fun onExperienceUpdated() {
this.uiAdapter.rootComponent.childById(LabelComponent::class.java, "experience")!!.apply {
text(Component.literal(player.experienceLevel.toString()))
}
updateLevelUpButton()
updateProgressBar()
}
Expand Down
10 changes: 10 additions & 0 deletions src/client/kotlin/com/bibireden/playerex/ui/PlayerEXTextures.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.bibireden.playerex.ui

import com.bibireden.playerex.PlayerEX
import org.jetbrains.annotations.ApiStatus

@ApiStatus.Internal
object PlayerEXTextures {
@JvmField
val RHOMBUS = PlayerEX.id("textures/ui/rhombus.png")
}
Loading