[WIP] Remove LibGUI

This commit is contained in:
Linnea Gräf
2024-07-01 03:42:51 +02:00
parent dff1f9c0e2
commit 5ee4b8d925
44 changed files with 439 additions and 1141 deletions

View File

@@ -122,11 +122,9 @@ dependencies {
modImplementation(libs.fabric.loader) modImplementation(libs.fabric.loader)
modImplementation(libs.fabric.kotlin) modImplementation(libs.fabric.kotlin)
modImplementation(libs.modmenu) modImplementation(libs.modmenu)
modImplementation(libs.libgui)
modImplementation(libs.moulconfig) modImplementation(libs.moulconfig)
modImplementation(libs.manninghamMills) modImplementation(libs.manninghamMills)
modCompileOnly(libs.explosiveenhancement) modCompileOnly(libs.explosiveenhancement)
include(libs.libgui)
include(libs.manninghamMills) include(libs.manninghamMills)
include(libs.moulconfig) include(libs.moulconfig)

View File

@@ -44,7 +44,6 @@ moulconfig = { module = "org.notenoughupdates.moulconfig:modern", version.ref =
repoparser = { module = "moe.nea:neurepoparser", version.ref = "neurepoparser" } repoparser = { module = "moe.nea:neurepoparser", version.ref = "neurepoparser" }
dbus_java_core = { module = "com.github.hypfvieh:dbus-java-core", version.ref = "dbus_java" } dbus_java_core = { module = "com.github.hypfvieh:dbus-java-core", version.ref = "dbus_java" }
dbus_java_unixsocket = { module = "com.github.hypfvieh:dbus-java-transport-native-unixsocket", version.ref = "dbus_java" } dbus_java_unixsocket = { module = "com.github.hypfvieh:dbus-java-transport-native-unixsocket", version.ref = "dbus_java" }
libgui = { module = "io.github.cottonmc:LibGui", version.ref = "libgui" }
mixinextras = { module = "io.github.llamalad7:mixinextras-fabric", version.ref = "mixinextras" } mixinextras = { module = "io.github.llamalad7:mixinextras-fabric", version.ref = "mixinextras" }
jarvis_api = { module = "moe.nea.jarvis:jarvis-api", version.ref = "jarvis" } jarvis_api = { module = "moe.nea.jarvis:jarvis-api", version.ref = "jarvis" }
jarvis_fabric = { module = "moe.nea.jarvis:jarvis-fabric", version.ref = "jarvis" } jarvis_fabric = { module = "moe.nea.jarvis:jarvis-fabric", version.ref = "jarvis" }

View File

@@ -20,7 +20,6 @@ import moe.nea.firmament.features.world.FairySouls
import moe.nea.firmament.gui.config.AllConfigsGui import moe.nea.firmament.gui.config.AllConfigsGui
import moe.nea.firmament.gui.config.BooleanHandler import moe.nea.firmament.gui.config.BooleanHandler
import moe.nea.firmament.gui.config.ManagedOption import moe.nea.firmament.gui.config.ManagedOption
import moe.nea.firmament.gui.profileviewer.ProfileViewer
import moe.nea.firmament.repo.HypixelStaticData import moe.nea.firmament.repo.HypixelStaticData
import moe.nea.firmament.repo.RepoManager import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.FirmFormatters import moe.nea.firmament.util.FirmFormatters
@@ -28,7 +27,6 @@ import moe.nea.firmament.util.MC
import moe.nea.firmament.util.SBData import moe.nea.firmament.util.SBData
import moe.nea.firmament.util.ScreenUtil import moe.nea.firmament.util.ScreenUtil
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.unformattedString
fun firmamentCommand() = literal("firmament") { fun firmamentCommand() = literal("firmament") {
@@ -127,19 +125,6 @@ fun firmamentCommand() = literal("firmament") {
} }
} }
} }
thenLiteral("pv") {
thenExecute {
ProfileViewer.onCommand(source, MC.player!!.name.unformattedString)
}
thenArgument("name", string()) { name ->
suggestsList {
MC.world?.players?.filter { it.uuid?.version() == 4 }?.map { it.name.unformattedString } ?: listOf()
}
thenExecute {
ProfileViewer.onCommand(source, get(name))
}
}
}
thenLiteral("price") { thenLiteral("price") {
thenArgument("item", string()) { item -> thenArgument("item", string()) { item ->
suggestsList { RepoManager.neuRepo.items.items.keys } suggestsList { RepoManager.neuRepo.items.items.keys }
@@ -155,7 +140,7 @@ fun firmamentCommand() = literal("firmament") {
source.sendFeedback( source.sendFeedback(
Text.stringifiedTranslatable( Text.stringifiedTranslatable(
"firmament.price.bazaar.buy.price", "firmament.price.bazaar.buy.price",
FirmFormatters.formatCurrency(bazaarData.quickStatus.buyPrice, 1) FirmFormatters.formatCommas(bazaarData.quickStatus.buyPrice, 1)
) )
) )
source.sendFeedback( source.sendFeedback(
@@ -167,7 +152,7 @@ fun firmamentCommand() = literal("firmament") {
source.sendFeedback( source.sendFeedback(
Text.stringifiedTranslatable( Text.stringifiedTranslatable(
"firmament.price.bazaar.sell.price", "firmament.price.bazaar.sell.price",
FirmFormatters.formatCurrency(bazaarData.quickStatus.sellPrice, 1) FirmFormatters.formatCommas(bazaarData.quickStatus.sellPrice, 1)
) )
) )
source.sendFeedback( source.sendFeedback(
@@ -182,7 +167,7 @@ fun firmamentCommand() = literal("firmament") {
source.sendFeedback( source.sendFeedback(
Text.stringifiedTranslatable( Text.stringifiedTranslatable(
"firmament.price.lowestbin", "firmament.price.lowestbin",
FirmFormatters.formatCurrency(lowestBin, 1) FirmFormatters.formatCommas(lowestBin, 1)
) )
) )
} }

View File

@@ -6,12 +6,6 @@
package moe.nea.firmament.features.debug package moe.nea.firmament.features.debug
import io.github.cottonmc.cotton.gui.client.CottonHud
import io.github.cottonmc.cotton.gui.widget.WBox
import io.github.cottonmc.cotton.gui.widget.WDynamicLabel
import io.github.cottonmc.cotton.gui.widget.data.Axis
import java.util.stream.Collectors
import kotlin.time.Duration.Companion.seconds
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.events.TickEvent import moe.nea.firmament.events.TickEvent
import moe.nea.firmament.features.FirmamentFeature import moe.nea.firmament.features.FirmamentFeature
@@ -36,24 +30,8 @@ object DebugView : FirmamentFeature {
} }
fun recalculateDebugWidget() { fun recalculateDebugWidget() {
storedVariables.entries.removeIf { it.value.timer.passedTime() > 1.seconds }
debugWidget.streamChildren().collect(Collectors.toList()).forEach {
debugWidget.remove(it)
}
storedVariables.entries.forEach {
debugWidget.add(WDynamicLabel({ "${it.key}: ${it.value.obj}" }))
}
debugWidget.layout()
if (storedVariables.isNotEmpty()) {
CottonHud.add(debugWidget, 20, 20)
} else {
CottonHud.remove(debugWidget)
}
} }
val debugWidget = WBox(Axis.VERTICAL)
override fun onLoad() { override fun onLoad() {
TickEvent.subscribe { TickEvent.subscribe {
synchronized(this) { synchronized(this) {

View File

@@ -1,58 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.features.debug
import io.github.cottonmc.cotton.gui.widget.WBox
import io.github.cottonmc.cotton.gui.widget.WLabel
import io.github.cottonmc.cotton.gui.widget.WWidget
import io.github.cottonmc.cotton.gui.widget.data.Axis
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlin.reflect.KProperty1
import net.minecraft.text.Text
import moe.nea.firmament.gui.WSpacer
class ObjectRenderer(val box: WBox) {
var indent = 0
fun beginObject() {
indent++
}
fun endObject() {
indent--
}
fun emit(label: String, widget: WWidget) {
WSpacer(WBox(Axis.VERTICAL).also {
it.add(WWidget())
it.add(widget)
}, indent * 18)
}
fun <T : Any?> getDebuggingView(label: String, obj: T) {
if (obj == null) {
emit(label, WLabel(Text.literal("§cnull")))
return
}
if (obj is String) {
emit(label, WLabel(Text.literal(Json.encodeToString(obj))))
}
getObject(label, obj)
}
fun <T : Any> getObject(label: String, obj: T) {
emit(label, WLabel(Text.literal(obj::class.simpleName ?: "<unknown>")))
beginObject()
for (prop in obj::class.members.filterIsInstance<KProperty1<T, *>>()) {
val child = prop.get(obj)
getDebuggingView(prop.name, child)
}
endObject()
}
}

View File

@@ -39,17 +39,17 @@ object PriceData : FirmamentFeature {
it.lines.add(Text.literal("")) it.lines.add(Text.literal(""))
it.lines.add( it.lines.add(
Text.stringifiedTranslatable("firmament.tooltip.bazaar.sell-order", Text.stringifiedTranslatable("firmament.tooltip.bazaar.sell-order",
FirmFormatters.formatCurrency(bazaarData.quickStatus.sellPrice, 1)) FirmFormatters.formatCommas(bazaarData.quickStatus.sellPrice, 1))
) )
it.lines.add( it.lines.add(
Text.stringifiedTranslatable("firmament.tooltip.bazaar.buy-order", Text.stringifiedTranslatable("firmament.tooltip.bazaar.buy-order",
FirmFormatters.formatCurrency(bazaarData.quickStatus.buyPrice, 1)) FirmFormatters.formatCommas(bazaarData.quickStatus.buyPrice, 1))
) )
} else if (lowestBin != null) { } else if (lowestBin != null) {
it.lines.add(Text.literal("")) it.lines.add(Text.literal(""))
it.lines.add( it.lines.add(
Text.stringifiedTranslatable("firmament.tooltip.ah.lowestbin", Text.stringifiedTranslatable("firmament.tooltip.ah.lowestbin",
FirmFormatters.formatCurrency(lowestBin, 1)) FirmFormatters.formatCommas(lowestBin, 1))
) )
} }
} }

View File

@@ -18,7 +18,7 @@ import moe.nea.firmament.features.FirmamentFeature
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.gui.hud.MoulConfigHud import moe.nea.firmament.gui.hud.MoulConfigHud
import moe.nea.firmament.util.BazaarPriceStrategy import moe.nea.firmament.util.BazaarPriceStrategy
import moe.nea.firmament.util.FirmFormatters.formatCurrency import moe.nea.firmament.util.FirmFormatters.formatCommas
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
import moe.nea.firmament.util.data.ProfileSpecificDataHolder import moe.nea.firmament.util.data.ProfileSpecificDataHolder
import moe.nea.firmament.util.formattedString import moe.nea.firmament.util.formattedString
@@ -99,11 +99,11 @@ object PristineProfitTracker : FirmamentFeature {
if (collectionPerSecond == null || moneyPerSecond == null) return if (collectionPerSecond == null || moneyPerSecond == null) return
ProfitHud.collectionCurrent = collectionPerSecond ProfitHud.collectionCurrent = collectionPerSecond
ProfitHud.collectionText = Text.stringifiedTranslatable("firmament.pristine-profit.collection", ProfitHud.collectionText = Text.stringifiedTranslatable("firmament.pristine-profit.collection",
formatCurrency(collectionPerSecond * SECONDS_PER_HOUR, formatCommas(collectionPerSecond * SECONDS_PER_HOUR,
1)).formattedString() 1)).formattedString()
ProfitHud.moneyCurrent = moneyPerSecond ProfitHud.moneyCurrent = moneyPerSecond
ProfitHud.moneyText = Text.stringifiedTranslatable("firmament.pristine-profit.money", ProfitHud.moneyText = Text.stringifiedTranslatable("firmament.pristine-profit.money",
formatCurrency(moneyPerSecond * SECONDS_PER_HOUR, 1)) formatCommas(moneyPerSecond * SECONDS_PER_HOUR, 1))
.formattedString() .formattedString()
val data = DConfig.data val data = DConfig.data
if (data != null) { if (data != null) {

View File

@@ -7,8 +7,6 @@
package moe.nea.firmament.gui package moe.nea.firmament.gui
import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.systems.RenderSystem
import io.github.cottonmc.cotton.gui.client.ScreenDrawing
import io.github.cottonmc.cotton.gui.widget.data.Texture
import io.github.notenoughupdates.moulconfig.common.MyResourceLocation import io.github.notenoughupdates.moulconfig.common.MyResourceLocation
import io.github.notenoughupdates.moulconfig.common.RenderContext import io.github.notenoughupdates.moulconfig.common.RenderContext
import io.github.notenoughupdates.moulconfig.gui.GuiComponent import io.github.notenoughupdates.moulconfig.gui.GuiComponent
@@ -33,6 +31,24 @@ class BarComponent(
return 8 return 8
} }
data class Texture(
val identifier: Identifier,
val u1: Float, val v1: Float,
val u2: Float, val v2: Float,
) {
fun draw(context: DrawContext, x: Int, y: Int, width: Int, height: Int, color: Color) {
context.drawTexturedQuad(
identifier,
x, y, x + width, x + height, 0,
u1, u2, v1, v2,
color.red / 255F,
color.green / 255F,
color.blue / 255F,
color.alpha / 255F,
)
}
}
companion object { companion object {
val resource = Firmament.identifier("textures/gui/bar.png") val resource = Firmament.identifier("textures/gui/bar.png")
val left = Texture(resource, 0 / 64F, 0 / 64F, 4 / 64F, 8 / 64F) val left = Texture(resource, 0 / 64F, 0 / 64F, 4 / 64F, 8 / 64F)
@@ -51,20 +67,21 @@ class BarComponent(
sectionEnd: Double sectionEnd: Double
) { ) {
if (sectionEnd < progress.get() && width == 4) { if (sectionEnd < progress.get() && width == 4) {
ScreenDrawing.texturedRect(context, x, y, 4, 8, texture, fillColor.color) texture.draw(context, x, y, 4, 8, fillColor)
return return
} }
if (sectionStart > progress.get() && width == 4) { if (sectionStart > progress.get() && width == 4) {
ScreenDrawing.texturedRect(context, x, y, 4, 8, texture, emptyColor.color) texture.draw(context, x, y, 4, 8, emptyColor)
return return
} }
val increasePerPixel = (sectionEnd - sectionStart) / width val increasePerPixel = (sectionEnd - sectionStart) / width
var valueAtPixel = sectionStart var valueAtPixel = sectionStart
for (i in (0 until width)) { for (i in (0 until width)) {
ScreenDrawing.texturedRect( val newTex =
Texture(texture.identifier, texture.u1 + i / 64F, texture.v1, texture.u1 + (i + 1) / 64F, texture.v2)
newTex.draw(
context, x + i, y, 1, 8, context, x + i, y, 1, 8,
texture.image, texture.u1 + i / 64F, texture.v1, texture.u1 + (i + 1) / 64F, texture.v2, if (valueAtPixel < progress.get()) fillColor else emptyColor
if (valueAtPixel < progress.get()) fillColor.color else emptyColor.color
) )
valueAtPixel += increasePerPixel valueAtPixel += increasePerPixel
} }

View File

@@ -15,24 +15,31 @@ import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent
import io.github.notenoughupdates.moulconfig.observer.GetSetter import io.github.notenoughupdates.moulconfig.observer.GetSetter
class FirmButtonComponent( open class FirmButtonComponent(
child: GuiComponent, child: GuiComponent,
val isEnabled: GetSetter<Boolean> = GetSetter.constant(true),
val action: Runnable, val action: Runnable,
val isEnabled: GetSetter<Boolean> = GetSetter.constant(true)
) : PanelComponent(child) { ) : PanelComponent(child) {
/* TODO: make use of vanillas built in nine slicer */ /* TODO: make use of vanillas built in nine slicer */
val hoveredBg = NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button_highlighted.png")) val hoveredBg =
.cornerSize(5) NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button_highlighted.png"))
.cornerUv(5 / 200F, 5 / 20F) .cornerSize(5)
.mode(NinePatch.Mode.STRETCHING) .cornerUv(5 / 200F, 5 / 20F)
.build() .mode(NinePatch.Mode.STRETCHING)
.build()
val unhoveredBg = NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button.png")) val unhoveredBg = NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button.png"))
.cornerSize(5) .cornerSize(5)
.cornerUv(5 / 200F, 5 / 20F) .cornerUv(5 / 200F, 5 / 20F)
.mode(NinePatch.Mode.STRETCHING) .mode(NinePatch.Mode.STRETCHING)
.build() .build()
val disabledBg = NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button_disabled.png")) val disabledBg =
NinePatch.builder(MyResourceLocation("minecraft", "textures/gui/sprites/widget/button_disabled.png"))
.cornerSize(5)
.cornerUv(5 / 200F, 5 / 20F)
.mode(NinePatch.Mode.STRETCHING)
.build()
val activeBg = NinePatch.builder(MyResourceLocation("firmament", "textures/gui/sprites/widget/button_active.png"))
.cornerSize(5) .cornerSize(5)
.cornerUv(5 / 200F, 5 / 20F) .cornerUv(5 / 200F, 5 / 20F)
.mode(NinePatch.Mode.STRETCHING) .mode(NinePatch.Mode.STRETCHING)
@@ -59,12 +66,15 @@ class FirmButtonComponent(
return false return false
} }
open fun getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> =
if (!isEnabled.get()) disabledBg
else if (context.isHovered || isClicking) hoveredBg
else unhoveredBg
override fun render(context: GuiImmediateContext) { override fun render(context: GuiImmediateContext) {
context.renderContext.pushMatrix() context.renderContext.pushMatrix()
context.renderContext.drawNinePatch( context.renderContext.drawNinePatch(
if (!isEnabled.get()) disabledBg getBackground(context),
else if (context.isHovered || isClicking) hoveredBg
else unhoveredBg,
0f, 0f, context.width, context.height 0f, 0f, context.width, context.height
) )
context.renderContext.translate(insets.toFloat(), insets.toFloat(), 0f) context.renderContext.translate(insets.toFloat(), insets.toFloat(), 0f)

View File

@@ -14,20 +14,20 @@ import io.github.notenoughupdates.moulconfig.observer.GetSetter
import java.util.function.BiFunction import java.util.function.BiFunction
class FixedComponent( class FixedComponent(
val fixedWidth: GetSetter<Int>, val fixedWidth: GetSetter<Int>?,
val fixedHeight: GetSetter<Int>, val fixedHeight: GetSetter<Int>?,
val component: GuiComponent, val component: GuiComponent,
) : GuiComponent() { ) : GuiComponent() {
override fun getWidth(): Int = fixedWidth.get() override fun getWidth(): Int = fixedWidth?.get() ?: component.width
override fun getHeight(): Int = fixedHeight.get() override fun getHeight(): Int = fixedHeight?.get() ?: component.height
override fun <T : Any?> foldChildren(initial: T, visitor: BiFunction<GuiComponent, T, T>): T { override fun <T : Any?> foldChildren(initial: T, visitor: BiFunction<GuiComponent, T, T>): T {
return visitor.apply(component, initial) return visitor.apply(component, initial)
} }
fun fixContext(context: GuiImmediateContext): GuiImmediateContext = fun fixContext(context: GuiImmediateContext): GuiImmediateContext =
context.translated(0, 0, fixedWidth.get(), fixedHeight.get()) context.translated(0, 0, width, height)
override fun render(context: GuiImmediateContext) { override fun render(context: GuiImmediateContext) {
component.render(fixContext(context)) component.render(fixContext(context))

View File

@@ -1,79 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.gui
import com.mojang.blaze3d.systems.RenderSystem
import io.github.cottonmc.cotton.gui.client.ScreenDrawing
import io.github.cottonmc.cotton.gui.widget.WWidget
import io.github.cottonmc.cotton.gui.widget.data.Texture
import me.shedaniel.math.Color
import net.minecraft.client.gui.DrawContext
import moe.nea.firmament.Firmament
open class WBar(
var progress: Double,
var total: Double,
val fillColor: Color,
val emptyColor: Color = fillColor.darker(2.0),
) : WWidget() {
companion object {
val resource = Firmament.identifier("textures/gui/bar.png")
val left = Texture(resource, 0 / 64F, 0 / 64F, 4 / 64F, 8 / 64F)
val middle = Texture(resource, 4 / 64F, 0 / 64F, 8 / 64F, 8 / 64F)
val right = Texture(resource, 8 / 64F, 0 / 64F, 12 / 64F, 8 / 64F)
val segmentOverlay = Texture(resource, 12 / 64F, 0 / 64F, 15 / 64F, 8 / 64F)
}
override fun canResize(): Boolean {
return true
}
private fun drawSection(
context: DrawContext,
texture: Texture,
x: Int,
y: Int,
width: Int,
sectionStart: Double,
sectionEnd: Double
) {
if (sectionEnd < progress && width == 4) {
ScreenDrawing.texturedRect(context, x, y, 4, 8, texture, fillColor.color)
return
}
if (sectionStart > progress && width == 4) {
ScreenDrawing.texturedRect(context, x, y, 4, 8, texture, emptyColor.color)
return
}
val increasePerPixel = (sectionEnd - sectionStart / 4)
var valueAtPixel = sectionStart
for (i in (0 until width)) {
ScreenDrawing.texturedRect(
context, x + i, y, 1, 8,
texture.image, texture.u1 + i / 64F, texture.v1, texture.u1 + (i + 1) / 64F, texture.v2,
if (valueAtPixel < progress) fillColor.color else emptyColor.color
)
valueAtPixel += increasePerPixel
}
}
override fun paint(context: DrawContext, x: Int, y: Int, mouseX: Int, mouseY: Int) {
var i = 0
while (i < width - 4) {
drawSection(
context,
if (i == 0) left else middle,
x + i, y,
(width - (i + 4)).coerceAtMost(4),
i * total / width, (i + 4) * total / width
)
i += 4
}
drawSection(context, right, x + width - 4, y, 4, (width - 4) * total / width, total)
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
}
}

View File

@@ -1,40 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.gui
import io.github.cottonmc.cotton.gui.widget.WPanel
import io.github.cottonmc.cotton.gui.widget.WWidget
import io.github.cottonmc.cotton.gui.widget.data.Axis
data class WCenteringPanel(
val child: WWidget,
val axis: Axis,
) : WPanel() {
init {
child.parent = this
}
override fun setSize(x: Int, y: Int) {
super.setSize(x, y)
if (!child.canResize()) return
if (axis == Axis.HORIZONTAL) {
child.setSize(child.width, y)
} else {
child.setSize(x, child.height)
}
}
override fun layout() {
super.layout()
child.setLocation(
axis.choose((child.width + width) / 2, child.x),
axis.choose(child.y, (child.height + height) / 2),
)
}
}

View File

@@ -1,32 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.gui
import io.github.cottonmc.cotton.gui.widget.WPanel
import io.github.cottonmc.cotton.gui.widget.WWidget
class WFixedPanel() : WPanel() {
var child: WWidget
set(value) {
children.clear()
setSize(0, 0)
value.parent = this
children.add(value)
}
get() = children.single()
constructor(child: WWidget) : this() {
this.child = child
}
override fun layout() {
setSize(0, 0)
super.layout()
}
override fun canResize(): Boolean = false
}

View File

@@ -1,25 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.gui
import io.github.cottonmc.cotton.gui.widget.WPanel
import io.github.cottonmc.cotton.gui.widget.WWidget
class WSpacer(val child: WWidget, val spaceLeft: Int) : WPanel() {
init {
children.add(child)
child.setLocation(spaceLeft, 0)
}
override fun getWidth(): Int {
return child.width + spaceLeft
}
override fun getHeight(): Int {
return child.height
}
}

View File

@@ -1,29 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.gui
import io.github.cottonmc.cotton.gui.widget.WPanel
import io.github.cottonmc.cotton.gui.widget.WPanelWithInsets
import io.github.cottonmc.cotton.gui.widget.WWidget
class WSplitPanel(val left: WWidget, val right: WWidget) : WPanelWithInsets() {
init {
left.parent = this
right.parent = this
children.add(left)
children.add(right)
}
override fun layout() {
expandToFit(left, insets)
expandToFit(right, insets)
(left as? WPanel)?.layout()
(right as? WPanel)?.layout()
left.setLocation(insets.left, insets.top)
right.setLocation(width - insets.right - right.width, insets.top)
}
}

View File

@@ -1,18 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.gui
import io.github.cottonmc.cotton.gui.widget.WPanel
import io.github.cottonmc.cotton.gui.widget.WScrollPanel
import io.github.cottonmc.cotton.gui.widget.WWidget
class WTightScrollPanel(val widget: WWidget, val margin: Int = 3) : WScrollPanel(widget) {
override fun setSize(x: Int, y: Int) {
(widget as? WPanel)?.layout()
super.setSize(widget.width + 8 + margin, y)
}
}

View File

@@ -1,39 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.gui
import com.mojang.blaze3d.systems.RenderSystem
import io.github.cottonmc.cotton.gui.client.BackgroundPainter
import io.github.cottonmc.cotton.gui.widget.TooltipBuilder
import io.github.cottonmc.cotton.gui.widget.WWidget
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.item.TooltipType
import net.minecraft.item.ItemStack
import net.minecraft.text.Text
import moe.nea.firmament.util.MC
open class WTitledItem(var stack: ItemStack, val countString: Text = Text.empty()) : WWidget() {
var backgroundPainter: BackgroundPainter = BackgroundPainter.SLOT
override fun canResize(): Boolean = true
override fun paint(context: DrawContext, x: Int, y: Int, mouseX: Int, mouseY: Int) {
backgroundPainter.paintBackground(context, x, y, this)
context.matrices.push()
context.matrices.translate(x.toFloat(), y.toFloat(), 0F)
context.matrices.scale(width / 18F, height / 18F, 1F)
RenderSystem.enableDepthTest()
context.drawItemWithoutEntity(stack, 18 / 2 - 8, 18 / 2 - 8)
context.matrices.translate(0F, 0F, 200F)
context.drawText(MC.font, countString, 19 - 2 - MC.font.getWidth(countString), 6 + 3, 0xFFFFFF, true)
context.matrices.pop()
}
override fun addTooltip(tooltip: TooltipBuilder) {
tooltip.add(*stack.getTooltip(null, null, TooltipType.BASIC).toTypedArray())
}
}

View File

@@ -6,7 +6,9 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import io.github.cottonmc.cotton.gui.widget.WToggleButton import io.github.notenoughupdates.moulconfig.gui.component.CenterComponent
import io.github.notenoughupdates.moulconfig.gui.component.SwitchComponent
import io.github.notenoughupdates.moulconfig.observer.GetSetter
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.boolean import kotlinx.serialization.json.boolean
@@ -24,13 +26,16 @@ class BooleanHandler(val config: ManagedConfig) : ManagedConfig.OptionHandler<Bo
override fun emitGuiElements(opt: ManagedOption<Boolean>, guiAppender: GuiAppender) { override fun emitGuiElements(opt: ManagedOption<Boolean>, guiAppender: GuiAppender) {
guiAppender.appendLabeledRow( guiAppender.appendLabeledRow(
opt.labelText, opt.labelText,
WToggleButton(opt.labelText).apply { CenterComponent(SwitchComponent(object : GetSetter<Boolean> {
guiAppender.onReload { toggle = opt.value } override fun get(): Boolean {
setOnToggle { return opt.get()
opt.value = it }
override fun set(newValue: Boolean) {
opt.set(newValue)
config.save() config.save()
} }
} }, 200)
) ))
} }
} }

View File

@@ -6,9 +6,9 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import io.github.cottonmc.cotton.gui.widget.WButton import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import net.minecraft.text.Text import moe.nea.firmament.gui.FirmButtonComponent
class ClickHandler(val config: ManagedConfig, val runnable: () -> Unit) : ManagedConfig.OptionHandler<Unit> { class ClickHandler(val config: ManagedConfig, val runnable: () -> Unit) : ManagedConfig.OptionHandler<Unit> {
override fun toJson(element: Unit): JsonElement? { override fun toJson(element: Unit): JsonElement? {
@@ -19,12 +19,10 @@ class ClickHandler(val config: ManagedConfig, val runnable: () -> Unit) : Manage
override fun emitGuiElements(opt: ManagedOption<Unit>, guiAppender: GuiAppender) { override fun emitGuiElements(opt: ManagedOption<Unit>, guiAppender: GuiAppender) {
guiAppender.appendLabeledRow( guiAppender.appendLabeledRow(
Text.translatable("firmament.config.${config.name}.${opt.propertyName}"), opt.labelText,
WButton(Text.translatable("firmament.config.${config.name}.${opt.propertyName}")).apply { FirmButtonComponent(
setOnClick { TextComponent(opt.labelText.string),
runnable() action = runnable),
}
},
) )
} }
} }

View File

@@ -6,18 +6,16 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import io.github.cottonmc.cotton.gui.widget.WBox import io.github.notenoughupdates.moulconfig.common.IMinecraft
import io.github.cottonmc.cotton.gui.widget.WLabel import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
import io.github.cottonmc.cotton.gui.widget.WSlider import io.github.notenoughupdates.moulconfig.gui.component.SliderComponent
import io.github.cottonmc.cotton.gui.widget.data.Axis import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment import io.github.notenoughupdates.moulconfig.observer.GetSetter
import java.util.function.IntConsumer
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.long import kotlinx.serialization.json.long
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.DurationUnit import kotlin.time.DurationUnit
import kotlin.time.toDuration import kotlin.time.toDuration
import net.minecraft.text.Text import net.minecraft.text.Text
@@ -34,22 +32,31 @@ class DurationHandler(val config: ManagedConfig, val min: Duration, val max: Dur
} }
override fun emitGuiElements(opt: ManagedOption<Duration>, guiAppender: GuiAppender) { override fun emitGuiElements(opt: ManagedOption<Duration>, guiAppender: GuiAppender) {
val label = guiAppender.appendLabeledRow(
WLabel(Text.literal(FirmFormatters.formatTimespan(opt.value))).setVerticalAlignment(VerticalAlignment.CENTER) opt.labelText,
guiAppender.appendLabeledRow(opt.labelText, WBox(Axis.HORIZONTAL).also { RowComponent(
it.add(label, 40, 18) TextComponent(IMinecraft.instance.defaultFontRenderer,
it.add(WSlider(min.inWholeMilliseconds.toInt(), max.inWholeMilliseconds.toInt(), Axis.HORIZONTAL).apply { { FirmFormatters.formatTimespan(opt.value) },
valueChangeListener = IntConsumer { 40,
opt.value = it.milliseconds TextComponent.TextAlignment.CENTER,
label.text = Text.literal(FirmFormatters.formatTimespan(opt.value)) true,
config.save() false),
} SliderComponent(
guiAppender.onReload { object : GetSetter<Float> {
value = opt.value.inWholeMilliseconds.toInt() override fun get(): Float {
label.text = Text.literal(FirmFormatters.formatTimespan(opt.value)) return opt.value.toDouble(DurationUnit.SECONDS).toFloat()
} }
}, 130, 18)
}) override fun set(newValue: Float) {
opt.value = newValue.toDouble().toDuration(DurationUnit.SECONDS)
}
},
min.toDouble(DurationUnit.SECONDS).toFloat(),
max.toDouble(DurationUnit.SECONDS).toFloat(),
0.1F,
130
)
))
} }
} }

View File

@@ -6,37 +6,39 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import io.github.cottonmc.cotton.gui.widget.WBox import io.github.notenoughupdates.moulconfig.gui.GuiComponent
import io.github.cottonmc.cotton.gui.widget.WLabel import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
import io.github.cottonmc.cotton.gui.widget.WWidget import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import io.github.cottonmc.cotton.gui.widget.data.Axis import io.github.notenoughupdates.moulconfig.observer.GetSetter
import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment
import net.minecraft.client.gui.screen.Screen import net.minecraft.client.gui.screen.Screen
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.gui.WSplitPanel import moe.nea.firmament.gui.FixedComponent
class GuiAppender(val width: Int, val screenAccessor: () -> Screen) { class GuiAppender(val width: Int, val screenAccessor: () -> Screen) {
val panel = WBox(Axis.VERTICAL).also { val panel = mutableListOf<GuiComponent>()
it.setSize(width, 200)
}
internal val reloadables = mutableListOf<(() -> Unit)>() internal val reloadables = mutableListOf<(() -> Unit)>()
fun onReload(reloadable: () -> Unit) { fun onReload(reloadable: () -> Unit) {
reloadables.add(reloadable) reloadables.add(reloadable)
} }
fun appendLabeledRow(label: Text, right: WWidget) { fun appendLabeledRow(label: Text, right: GuiComponent) {
appendSplitRow( appendSplitRow(
WLabel(label).setVerticalAlignment(VerticalAlignment.CENTER), TextComponent(label.string),
right right
) )
} }
fun appendSplitRow(left: WWidget, right: WWidget) { fun appendSplitRow(left: GuiComponent, right: GuiComponent) {
appendFullRow(WSplitPanel(left.also { it.setSize(width / 2, 18) }, right.also { it.setSize(width / 2, 18) })) // TODO: make this more dynamic
// i could just make a component that allows for using half the available size
appendFullRow(RowComponent(
FixedComponent(GetSetter.constant(width / 2), null, left),
FixedComponent(GetSetter.constant(width / 2), null, right),
))
} }
fun appendFullRow(widget: WWidget) { fun appendFullRow(widget: GuiComponent) {
panel.add(widget, width, 18) panel.add(widget)
} }
} }

View File

@@ -6,13 +6,14 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import io.github.cottonmc.cotton.gui.widget.WButton import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.json.encodeToJsonElement
import net.minecraft.text.MutableText import net.minecraft.text.MutableText
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.gui.FirmButtonComponent
import moe.nea.firmament.jarvis.JarvisIntegration import moe.nea.firmament.jarvis.JarvisIntegration
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
@@ -27,14 +28,16 @@ class HudMetaHandler(val config: ManagedConfig, val label: MutableText, val widt
} }
override fun emitGuiElements(opt: ManagedOption<HudMeta>, guiAppender: GuiAppender) { override fun emitGuiElements(opt: ManagedOption<HudMeta>, guiAppender: GuiAppender) {
guiAppender.appendLabeledRow(opt.labelText, WButton(Text.stringifiedTranslatable("firmament.hud.edit", label)) guiAppender.appendLabeledRow(
.also { opt.labelText,
it.setOnClick { FirmButtonComponent(
MC.screen = JarvisIntegration.jarvis.getHudEditor( TextComponent(
guiAppender.screenAccessor.invoke(), Text.stringifiedTranslatable("firmament.hud.edit", label).string),
listOf(opt.value) ) {
) MC.screen = JarvisIntegration.jarvis.getHudEditor(
} guiAppender.screenAccessor.invoke(),
listOf(opt.value)
)
}) })
} }
} }

View File

@@ -6,17 +6,16 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import io.github.cottonmc.cotton.gui.widget.WBox import io.github.notenoughupdates.moulconfig.common.IMinecraft
import io.github.cottonmc.cotton.gui.widget.WLabel import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
import io.github.cottonmc.cotton.gui.widget.WSlider import io.github.notenoughupdates.moulconfig.gui.component.SliderComponent
import io.github.cottonmc.cotton.gui.widget.data.Axis import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment import io.github.notenoughupdates.moulconfig.observer.GetSetter
import java.util.function.IntConsumer
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.int import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
import net.minecraft.text.Text import moe.nea.firmament.util.FirmFormatters
class IntegerHandler(val config: ManagedConfig, val min: Int, val max: Int) : ManagedConfig.OptionHandler<Int> { class IntegerHandler(val config: ManagedConfig, val min: Int, val max: Int) : ManagedConfig.OptionHandler<Int> {
override fun toJson(element: Int): JsonElement? { override fun toJson(element: Int): JsonElement? {
@@ -28,22 +27,32 @@ class IntegerHandler(val config: ManagedConfig, val min: Int, val max: Int) : Ma
} }
override fun emitGuiElements(opt: ManagedOption<Int>, guiAppender: GuiAppender) { override fun emitGuiElements(opt: ManagedOption<Int>, guiAppender: GuiAppender) {
val label = guiAppender.appendLabeledRow(
WLabel(Text.literal(opt.value.toString())).setVerticalAlignment(VerticalAlignment.CENTER) opt.labelText,
guiAppender.appendLabeledRow(opt.labelText, WBox(Axis.HORIZONTAL).also { RowComponent(
it.add(label, 40, 18) TextComponent(IMinecraft.instance.defaultFontRenderer,
it.add(WSlider(min, max, Axis.HORIZONTAL).apply { { FirmFormatters.formatCommas(opt.value, 0) },
valueChangeListener = IntConsumer { 40,
opt.value = it TextComponent.TextAlignment.CENTER,
label.text = Text.literal(opt.value.toString()) true,
config.save() false),
} SliderComponent(
guiAppender.onReload { object : GetSetter<Float> {
value = opt.value override fun get(): Float {
label.text = Text.literal(opt.value.toString()) return opt.value.toFloat()
} }
}, 130, 18)
}) override fun set(newValue: Float) {
opt.value = newValue.toInt()
}
},
min.toFloat(),
max.toFloat(),
0.1F,
130
)
))
} }
} }

View File

@@ -6,8 +6,12 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import io.github.cottonmc.cotton.gui.widget.WButton import io.github.notenoughupdates.moulconfig.common.IMinecraft
import io.github.cottonmc.cotton.gui.widget.data.InputResult import io.github.notenoughupdates.moulconfig.common.MyResourceLocation
import io.github.notenoughupdates.moulconfig.deps.libninepatch.NinePatch
import io.github.notenoughupdates.moulconfig.gui.GuiImmediateContext
import io.github.notenoughupdates.moulconfig.gui.KeyboardEvent
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import org.lwjgl.glfw.GLFW import org.lwjgl.glfw.GLFW
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
@@ -16,6 +20,7 @@ import kotlinx.serialization.json.encodeToJsonElement
import net.minecraft.client.util.InputUtil import net.minecraft.client.util.InputUtil
import net.minecraft.text.Text import net.minecraft.text.Text
import net.minecraft.util.Formatting import net.minecraft.util.Formatting
import moe.nea.firmament.gui.FirmButtonComponent
import moe.nea.firmament.keybindings.FirmamentKeyBindings import moe.nea.firmament.keybindings.FirmamentKeyBindings
import moe.nea.firmament.keybindings.SavedKeyBinding import moe.nea.firmament.keybindings.SavedKeyBinding
@@ -36,61 +41,8 @@ class KeyBindingHandler(name: String, val managedConfig: ManagedConfig) : Manage
var editing = false var editing = false
var lastPressed = 0 var lastPressed = 0
var lastPressedNonModifier = 0 var lastPressedNonModifier = 0
var updateButton: (() -> Unit)? = null var label: String = ""
val button = object : WButton() { var button: FirmButtonComponent? = null
override fun onKeyPressed(ch: Int, key: Int, modifiers: Int): InputResult {
if (!editing) {
return super.onKeyPressed(ch, key, modifiers)
}
if (ch == GLFW.GLFW_KEY_ESCAPE) {
lastPressedNonModifier = 0
editing = false
lastPressed = 0
opt.value = SavedKeyBinding(GLFW.GLFW_KEY_UNKNOWN)
updateButton!!()
return InputResult.PROCESSED
}
if (ch == GLFW.GLFW_KEY_LEFT_SHIFT || ch == GLFW.GLFW_KEY_RIGHT_SHIFT
|| ch == GLFW.GLFW_KEY_LEFT_ALT || ch == GLFW.GLFW_KEY_RIGHT_ALT
|| ch == GLFW.GLFW_KEY_LEFT_CONTROL || ch == GLFW.GLFW_KEY_RIGHT_CONTROL
) {
lastPressed = ch
} else {
opt.value = SavedKeyBinding(
ch, modifiers
)
editing = false
lastPressed = 0
lastPressedNonModifier = 0
}
updateButton!!()
return InputResult.PROCESSED
}
override fun onFocusLost() {
super.onFocusLost()
lastPressedNonModifier = 0
editing = false
lastPressed = 0
updateButton!!()
}
override fun onKeyReleased(ch: Int, key: Int, modifiers: Int): InputResult {
if (!editing)
return super.onKeyReleased(ch, key, modifiers)
if (lastPressedNonModifier == ch || (lastPressedNonModifier == 0 && ch == lastPressed)) {
opt.value = SavedKeyBinding(
ch, modifiers
)
editing = false
lastPressed = 0
lastPressedNonModifier = 0
}
updateButton!!()
return InputResult.PROCESSED
}
}
fun updateLabel() { fun updateLabel() {
val stroke = Text.literal("") val stroke = Text.literal("")
if (opt.value.shift) { if (opt.value.shift) {
@@ -105,16 +57,89 @@ class KeyBindingHandler(name: String, val managedConfig: ManagedConfig) : Manage
stroke.append(InputUtil.Type.KEYSYM.createFromCode(opt.value.keyCode).localizedText) stroke.append(InputUtil.Type.KEYSYM.createFromCode(opt.value.keyCode).localizedText)
if (editing) if (editing)
stroke.styled { it.withColor(Formatting.YELLOW) } stroke.styled { it.withColor(Formatting.YELLOW) }
button.setLabel(stroke) label = (stroke).string
managedConfig.save() managedConfig.save()
} }
updateButton = ::updateLabel button = object : FirmButtonComponent(
updateButton() TextComponent(
button.setOnClick { IMinecraft.instance.defaultFontRenderer,
editing = true { label },
button.requestFocus() 130,
updateButton() TextComponent.TextAlignment.LEFT,
false,
false
), action = {
if (editing) {
button!!.blur()
} else {
editing = true
button!!.requestFocus()
updateLabel()
}
}) {
override fun keyboardEvent(event: KeyboardEvent, context: GuiImmediateContext): Boolean {
if (event is KeyboardEvent.KeyPressed) {
if (event.pressed) onKeyPressed(event.keycode, SavedKeyBinding.getModInt())
else onKeyReleased(event.keycode, SavedKeyBinding.getModInt())
}
return super.keyboardEvent(event, context)
}
override fun getBackground(context: GuiImmediateContext): NinePatch<MyResourceLocation> {
if (editing) return activeBg
return super.getBackground(context)
}
fun onKeyPressed(ch: Int, modifiers: Int): Boolean {
if (!editing) {
return false
}
if (ch == GLFW.GLFW_KEY_ESCAPE) {
lastPressedNonModifier = 0
editing = false
lastPressed = 0
opt.value = SavedKeyBinding(GLFW.GLFW_KEY_UNKNOWN)
updateLabel()
return true
}
if (ch == GLFW.GLFW_KEY_LEFT_SHIFT || ch == GLFW.GLFW_KEY_RIGHT_SHIFT
|| ch == GLFW.GLFW_KEY_LEFT_ALT || ch == GLFW.GLFW_KEY_RIGHT_ALT
|| ch == GLFW.GLFW_KEY_LEFT_CONTROL || ch == GLFW.GLFW_KEY_RIGHT_CONTROL
) {
lastPressed = ch
} else {
opt.value = SavedKeyBinding(
ch, modifiers
)
editing = false
lastPressed = 0
lastPressedNonModifier = 0
}
updateLabel()
return true
}
override fun onLostFocus() {
lastPressedNonModifier = 0
editing = false
lastPressed = 0
updateLabel()
}
fun onKeyReleased(ch: Int, modifiers: Int): Boolean {
if (!editing)
return false
if (lastPressedNonModifier == ch || (lastPressedNonModifier == 0 && ch == lastPressed)) {
opt.value = SavedKeyBinding(ch, modifiers)
editing = false
lastPressed = 0
lastPressedNonModifier = 0
}
updateLabel()
return true
}
} }
updateLabel()
guiAppender.appendLabeledRow(opt.labelText, button) guiAppender.appendLabeledRow(opt.labelText, button)
} }

View File

@@ -6,17 +6,17 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import io.github.cottonmc.cotton.gui.client.CottonClientScreen import io.github.notenoughupdates.moulconfig.gui.CloseEventListener
import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper
import io.github.cottonmc.cotton.gui.widget.WBox import io.github.notenoughupdates.moulconfig.gui.GuiContext
import io.github.cottonmc.cotton.gui.widget.WButton import io.github.notenoughupdates.moulconfig.gui.component.CenterComponent
import io.github.cottonmc.cotton.gui.widget.WLabel import io.github.notenoughupdates.moulconfig.gui.component.ColumnComponent
import io.github.cottonmc.cotton.gui.widget.data.Axis import io.github.notenoughupdates.moulconfig.gui.component.PanelComponent
import io.github.cottonmc.cotton.gui.widget.data.Insets import io.github.notenoughupdates.moulconfig.gui.component.RowComponent
import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment import io.github.notenoughupdates.moulconfig.gui.component.ScrollPanelComponent
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import moe.nea.jarvis.api.Point import moe.nea.jarvis.api.Point
import org.lwjgl.glfw.GLFW import org.lwjgl.glfw.GLFW
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
@@ -27,9 +27,8 @@ import kotlin.time.Duration
import net.minecraft.client.gui.screen.Screen import net.minecraft.client.gui.screen.Screen
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.gui.WTightScrollPanel import moe.nea.firmament.gui.FirmButtonComponent
import moe.nea.firmament.keybindings.SavedKeyBinding import moe.nea.firmament.keybindings.SavedKeyBinding
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.ScreenUtil.setScreenLater import moe.nea.firmament.util.ScreenUtil.setScreenLater
abstract class ManagedConfig(override val name: String) : ManagedConfigElement() { abstract class ManagedConfig(override val name: String) : ManagedConfigElement() {
@@ -150,45 +149,30 @@ abstract class ManagedConfig(override val name: String) : ManagedConfigElement()
val labelText = Text.translatable("firmament.config.${name}") val labelText = Text.translatable("firmament.config.${name}")
fun getConfigEditor(parent: Screen? = null): CottonClientScreen { fun getConfigEditor(parent: Screen? = null): Screen {
val lwgd = LightweightGuiDescription()
var screen: Screen? = null var screen: Screen? = null
val guiapp = GuiAppender(400) { requireNotNull(screen) { "Screen Accessor called too early" } } val guiapp = GuiAppender(400) { requireNotNull(screen) { "Screen Accessor called too early" } }
latestGuiAppender = guiapp latestGuiAppender = guiapp
guiapp.panel.insets = Insets.ROOT_PANEL guiapp.appendFullRow(RowComponent(
guiapp.appendFullRow(WBox(Axis.HORIZONTAL).also { FirmButtonComponent(TextComponent("")) {
it.add(WButton(Text.literal("")).also { if (parent != null) {
it.setOnClick {
if (parent != null) {
save()
setScreenLater(parent)
} else {
AllConfigsGui.showAllGuis()
}
}
})
it.add(WLabel(labelText).also {
it.verticalAlignment = VerticalAlignment.CENTER
})
})
sortedOptions.forEach { it.appendToGui(guiapp) }
guiapp.reloadables.forEach { it() }
lwgd.setRootPanel(WTightScrollPanel(guiapp.panel).also {
it.setSize(400, 300)
})
screen =
object : CottonClientScreen(lwgd) {
override fun init() {
latestGuiAppender = guiapp
super.init()
}
override fun close() {
latestGuiAppender = null
save() save()
MC.screen = parent setScreenLater(parent)
} else {
AllConfigsGui.showAllGuis()
} }
} }
))
sortedOptions.forEach { it.appendToGui(guiapp) }
guiapp.reloadables.forEach { it() }
val component = CenterComponent(PanelComponent(ScrollPanelComponent(400, 300, ColumnComponent(guiapp.panel)), 10, PanelComponent.DefaultBackgroundRenderer.VANILLA))
screen = object : GuiComponentWrapper(GuiContext(component)) {
override fun close() {
if (context.onBeforeClose() == CloseEventListener.CloseAction.NO_OBJECTIONS_TO_CLOSE) {
client!!.setScreen(parent)
}
}
}
return screen return screen
} }

View File

@@ -6,6 +6,7 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import io.github.notenoughupdates.moulconfig.observer.GetSetter
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
@@ -18,7 +19,14 @@ class ManagedOption<T : Any>(
val propertyName: String, val propertyName: String,
val default: () -> T, val default: () -> T,
val handler: ManagedConfig.OptionHandler<T> val handler: ManagedConfig.OptionHandler<T>
) : ReadWriteProperty<Any?, T> { ) : ReadWriteProperty<Any?, T>, GetSetter<T> {
override fun set(newValue: T) {
this.value = newValue
}
override fun get(): T {
return this.value
}
val rawLabelText = "firmament.config.${element.name}.${propertyName}" val rawLabelText = "firmament.config.${element.name}.${propertyName}"
val labelText = Text.translatable(rawLabelText) val labelText = Text.translatable(rawLabelText)

View File

@@ -6,7 +6,8 @@
package moe.nea.firmament.gui.config package moe.nea.firmament.gui.config
import io.github.cottonmc.cotton.gui.widget.WTextField import io.github.notenoughupdates.moulconfig.gui.component.TextFieldComponent
import io.github.notenoughupdates.moulconfig.observer.GetSetter
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
@@ -24,15 +25,16 @@ class StringHandler(val config: ManagedConfig) : ManagedConfig.OptionHandler<Str
override fun emitGuiElements(opt: ManagedOption<String>, guiAppender: GuiAppender) { override fun emitGuiElements(opt: ManagedOption<String>, guiAppender: GuiAppender) {
guiAppender.appendLabeledRow( guiAppender.appendLabeledRow(
opt.labelText, opt.labelText,
WTextField(opt.labelText).apply { TextFieldComponent(
maxLength = 1000 object : GetSetter<String> by opt {
suggestion = Text.translatableWithFallback(opt.rawLabelText + ".hint", "") override fun set(newValue: String) {
guiAppender.onReload { text = opt.value } opt.set(newValue)
setChangedListener { config.save()
opt.value = it }
config.save() },
} 130,
} suggestion = Text.translatableWithFallback(opt.rawLabelText + ".hint", "").string
),
) )
} }
} }

View File

@@ -6,11 +6,12 @@
package moe.nea.firmament.gui.hud package moe.nea.firmament.gui.hud
import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper
import io.github.notenoughupdates.moulconfig.gui.GuiContext import io.github.notenoughupdates.moulconfig.gui.GuiContext
import io.github.notenoughupdates.moulconfig.gui.component.TextComponent import io.github.notenoughupdates.moulconfig.gui.component.TextComponent
import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper
import net.minecraft.resource.ResourceManager import net.minecraft.resource.ResourceManager
import net.minecraft.resource.SynchronousResourceReloader import net.minecraft.resource.SynchronousResourceReloader
import moe.nea.firmament.events.FinalizeResourceManagerEvent
import moe.nea.firmament.events.HudRenderEvent import moe.nea.firmament.events.HudRenderEvent
import moe.nea.firmament.gui.config.HudMeta import moe.nea.firmament.gui.config.HudMeta
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
@@ -21,9 +22,11 @@ abstract class MoulConfigHud(
val hudMeta: HudMeta, val hudMeta: HudMeta,
) { ) {
companion object { companion object {
private val componentWrapper = object : GuiComponentWrapper(GuiContext(TextComponent("§cERROR"))) { private val componentWrapper by lazy {
init { object : GuiComponentWrapper(GuiContext(TextComponent("§cERROR"))) {
this.client = MC.instance init {
this.client = MC.instance
}
} }
} }
} }
@@ -31,7 +34,6 @@ abstract class MoulConfigHud(
private var fragment: GuiContext? = null private var fragment: GuiContext? = null
fun forceInit() { fun forceInit() {
} }
open fun shouldRender(): Boolean { open fun shouldRender(): Boolean {
@@ -53,11 +55,13 @@ abstract class MoulConfigHud(
fragment!!.root.render(renderContextTranslated) fragment!!.root.render(renderContextTranslated)
it.context.matrices.pop() it.context.matrices.pop()
} }
MC.resourceManager.registerReloader(object : SynchronousResourceReloader { FinalizeResourceManagerEvent.subscribe {
override fun reload(manager: ResourceManager?) { MC.resourceManager.registerReloader(object : SynchronousResourceReloader {
fragment = null override fun reload(manager: ResourceManager?) {
} fragment = null
}) }
})
}
} }
fun loadFragment() { fun loadFragment() {

View File

@@ -1,110 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.gui.profileviewer
import io.github.cottonmc.cotton.gui.client.BackgroundPainter
import io.github.cottonmc.cotton.gui.widget.WBox
import io.github.cottonmc.cotton.gui.widget.WGridPanel
import io.github.cottonmc.cotton.gui.widget.WText
import io.github.cottonmc.cotton.gui.widget.WWidget
import io.github.cottonmc.cotton.gui.widget.data.Axis
import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment
import io.github.cottonmc.cotton.gui.widget.data.InputResult
import io.github.cottonmc.cotton.gui.widget.data.Insets
import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment
import io.github.cottonmc.cotton.gui.widget.icon.Icon
import io.github.cottonmc.cotton.gui.widget.icon.ItemIcon
import io.github.moulberry.repo.data.Rarity
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.text.Text
import net.minecraft.util.Formatting
import moe.nea.firmament.gui.WTightScrollPanel
import moe.nea.firmament.gui.WTitledItem
import moe.nea.firmament.rei.PetData
import moe.nea.firmament.rei.SBItemStack
import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.FirmFormatters
object PetsPage : ProfilePage {
private fun petOverview(profileViewer: ProfileViewer, choosePet: (ItemStack) -> Unit) = WGridPanel().also { panel ->
panel.insets = Insets.ROOT_PANEL
panel.add(WText(Text.literal(profileViewer.account.getDisplayName(profileViewer.primaryName))), 0, 0, 6, 1)
panel.add((WTightScrollPanel(WGridPanel().also { it ->
it.setGaps(8, 8)
for ((i, pet) in profileViewer.member.pets.map {
SBItemStack(it.itemId, PetData(it.tier, it.type.name, it.exp))
}.sortedWith(
Comparator.comparing<SBItemStack?, Rarity?> { it.petData!!.rarity }.reversed()
.thenDescending(Comparator.comparing { it.petData!!.levelData.currentLevel })
.thenDescending(Comparator.comparing { it.petData!!.petId })
).withIndex()) {
val stack = pet.asItemStack()
it.add(object : WTitledItem(stack) {
override fun onClick(x: Int, y: Int, button: Int): InputResult {
choosePet(stack)
return InputResult.PROCESSED
}
}, i % 9, i / 9, 1, 1)
}
it.layout()
})), 0, 1, 12, 8)
petStats(profileViewer).withIndex().forEach { (i, it) ->
panel.add(it, 0, 10 + i, 8, 1)
}
}
private fun petStats(profileViewer: ProfileViewer): List<WWidget> {
val petScore = profileViewer.member.pets.groupBy { it.type }
.map { it.value.maxBy { it.tier } }
.sumOf { RepoManager.neuRepo.constants.bonuses.getPetValue(it.tier) }
return listOf(
WText(
Text.literal("Pet Score: ").styled { it.withColor(Formatting.AQUA) }
.append(Text.literal("$petScore").styled { it.withColor(Formatting.GOLD) })
),
WText(
Text.literal("Magic Find: ").styled { it.withColor(Formatting.AQUA) }
.append(
Text.literal(
FirmFormatters.formatCurrency(
RepoManager.neuRepo.constants.bonuses.getPetRewards(
petScore
)["magic_find"] ?: 0.0F, 1
)
)
.styled { it.withColor(Formatting.GOLD) })
)
)
}
override fun getElements(profileViewer: ProfileViewer): WWidget {
return WBox(Axis.HORIZONTAL).also {
it.insets = Insets.ROOT_PANEL
val item = WTitledItem(ItemStack.EMPTY)
item.backgroundPainter = BackgroundPainter.VANILLA
it.add(WBox(Axis.VERTICAL).also { box ->
box.add(petOverview(profileViewer) { item.stack = it })
})
val b = WBox(Axis.VERTICAL).also { box ->
box.verticalAlignment = VerticalAlignment.CENTER
box.horizontalAlignment = HorizontalAlignment.CENTER
box.add(item, 128, 128)
}
it.add(b)
it.layout()
b.setSize(b.width + 20, it.height)
}
}
override val icon: Icon
get() = ItemIcon(Items.BONE)
override val text: Text
get() = Text.translatable("firmament.pv.pets")
}

View File

@@ -1,17 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.gui.profileviewer
import io.github.cottonmc.cotton.gui.widget.WWidget
import io.github.cottonmc.cotton.gui.widget.icon.Icon
import net.minecraft.text.Text
interface ProfilePage {
fun getElements(profileViewer: ProfileViewer): WWidget
val icon: Icon
val text: Text
}

View File

@@ -1,77 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.gui.profileviewer
import io.github.cottonmc.cotton.gui.client.CottonClientScreen
import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription
import io.github.cottonmc.cotton.gui.widget.WTabPanel
import moe.nea.firmament.Firmament
import moe.nea.firmament.apis.Member
import moe.nea.firmament.apis.PlayerData
import moe.nea.firmament.apis.Profile
import moe.nea.firmament.apis.Routes
import moe.nea.firmament.util.ScreenUtil
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource
import net.minecraft.text.Text
import java.util.*
class ProfileViewer(
val primaryPlayer: UUID,
val playerNames: Map<UUID, String>,
val accountData: Map<UUID, PlayerData>,
val profile: Profile,
) : LightweightGuiDescription() {
val member: Member = profile.members[primaryPlayer] ?: error("Primary player not in profile")
val primaryName: String = playerNames[primaryPlayer] ?: error("Primary player has no name")
val account: PlayerData = accountData[primaryPlayer] ?: error("Primary player has no data")
init {
val panel = WTabPanel().also { rootPanel = it }
panel.backgroundPainter
listOf<ProfilePage>(SkillPage, PetsPage)
.forEach { page ->
panel.add(page.getElements(this)) {
it.icon(page.icon)
it.tooltip(page.text)
}
}
}
companion object {
suspend fun onCommand(source: FabricClientCommandSource, name: String) {
source.sendFeedback(Text.stringifiedTranslatable("firmament.pv.lookingup", name))
try {
val uuid = Routes.getUUIDForPlayerName(name)
if (uuid == null) {
source.sendError(Text.stringifiedTranslatable("firmament.pv.noplayer", name))
return
}
val name = Routes.getPlayerNameForUUID(uuid) ?: name
val names = mapOf(uuid to (name))
val data = Routes.getAccountData(uuid)
if (data == null) {
source.sendError(Text.stringifiedTranslatable("firmament.pv.noprofile", name))
return
}
val accountData = mapOf(data.uuid to data)
val profiles = Routes.getProfiles(uuid)
val profile = profiles?.profiles?.find { it.selected }
if (profile == null) {
source.sendFeedback(Text.stringifiedTranslatable("firmament.pv.noprofile", name))
return
}
ScreenUtil.setScreenLater(CottonClientScreen(ProfileViewer(uuid, names, accountData, profile)))
} catch (e: Exception) {
Firmament.logger.error("Error loading profile data for $name", e)
source.sendError(Text.stringifiedTranslatable("firmament.pv.badprofile", name, e.message))
}
}
}
}

View File

@@ -1,71 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.gui.profileviewer
import com.mojang.brigadier.StringReader
import io.github.cottonmc.cotton.gui.client.CottonClientScreen
import io.github.cottonmc.cotton.gui.client.LightweightGuiDescription
import io.github.cottonmc.cotton.gui.widget.WGridPanel
import io.github.cottonmc.cotton.gui.widget.WText
import io.github.cottonmc.cotton.gui.widget.data.VerticalAlignment
import moe.nea.lisp.LispData
import moe.nea.lisp.LispExecutionContext
import moe.nea.lisp.LispParser
import moe.nea.lisp.bind.AutoBinder
import moe.nea.lisp.bind.LispBinding
import moe.nea.lisp.bind.UnmapForeignObject
import net.minecraft.command.argument.ItemStringReader
import net.minecraft.item.ItemStack
import net.minecraft.text.Text
import moe.nea.firmament.gui.WTitledItem
import moe.nea.firmament.util.MC
import moe.nea.firmament.util.ScreenUtil
import moe.nea.firmament.util.item.setCustomName
import moe.nea.firmament.util.modifyLore
class ProfileViewerLibrary {
@LispBinding("mk-item")
fun makeItem(itemType: String, title: String, vararg lore: String): LispData.ForeignObject<ItemStack> {
val item = ItemStringReader(MC.defaultRegistries).consume(StringReader(itemType))
val itemStack = ItemStack(item.item.value())
itemStack.applyComponentsFrom(item.components)
itemStack.modifyLore { lore.map { Text.literal(it) } }
itemStack.setCustomName(Text.literal(title))
return LispData.ForeignObject(itemStack)
}
@LispBinding("def-page")
fun defPage(name: String, @UnmapForeignObject icon: ItemStack) {
pages.add(Pair(name, icon))
}
val pages = mutableListOf<Pair<String, ItemStack>>()
val coreEnvironment = LispExecutionContext()
fun run() {
val t = coreEnvironment.genBindings()
val ab = AutoBinder()
ab.bindTo(this, t)
val prog = LispParser.parse(
"testfile.lisp", """
(def-page "Test" (mk-item "minecraft:tnt" "§aThis is a test page" "§aMore text"))
(def-page "Skills" (mk-item "minecraft:diamond_sword" "§aThis is a test page" "§aMore text"))
""".trimIndent()
)
coreEnvironment.executeProgram(t, prog)
val light = LightweightGuiDescription()
val root = light.rootPanel as WGridPanel
root.setGaps(8, 8)
pages.forEachIndexed { i, (name, item) ->
root.add(WTitledItem(item), 0, i)
root.add(WText(Text.literal(name)).also { it.verticalAlignment = VerticalAlignment.CENTER }, 1, i, 6, 1)
}
ScreenUtil.setScreenLater(CottonClientScreen(light))
}
}

View File

@@ -1,146 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.gui.profileviewer
import io.github.cottonmc.cotton.gui.widget.*
import io.github.cottonmc.cotton.gui.widget.data.Axis
import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment
import io.github.cottonmc.cotton.gui.widget.data.Insets
import io.github.cottonmc.cotton.gui.widget.icon.Icon
import io.github.cottonmc.cotton.gui.widget.icon.ItemIcon
import moe.nea.firmament.apis.*
import moe.nea.firmament.gui.WBar
import moe.nea.firmament.gui.WFixedPanel
import moe.nea.firmament.gui.WTitledItem
import moe.nea.firmament.hud.horizontal
import moe.nea.firmament.rei.FirmamentReiPlugin.Companion.asItemEntry
import moe.nea.firmament.rei.SBItemEntryDefinition
import moe.nea.firmament.repo.HypixelStaticData
import moe.nea.firmament.repo.ItemCache.asItemStack
import moe.nea.firmament.repo.RepoManager
import moe.nea.firmament.util.FirmFormatters
import moe.nea.firmament.util.modifyLore
import moe.nea.firmament.util.toShedaniel
import moe.nea.firmament.util.toTextColor
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.text.Style
import net.minecraft.text.Text
import net.minecraft.util.DyeColor
import net.minecraft.util.Formatting
import moe.nea.firmament.util.item.setCustomName
object SkillPage : ProfilePage {
private fun skillBar(profileViewer: ProfileViewer, skill: Skill): WBar {
val leveling = RepoManager.neuRepo.constants.leveling
val exp = skill.accessor.get(profileViewer.member)
val maxLevel = skill.getMaximumLevel(leveling)
val level = skill.getLadder(leveling)
.runningFold(0.0) { a, b -> a + b }
.filter { it <= exp }.size
.coerceAtMost(maxLevel)
return object : WBar(
level.toDouble(), maxLevel.toDouble(),
skill.color.toShedaniel(), skill.color.toShedaniel().darker(2.0)
) {
override fun addTooltip(tooltip: TooltipBuilder) {
tooltip.add(Text.literal("$level/$maxLevel"))
tooltip.add(Text.stringifiedTranslatable("firmament.pv.skills.total", FirmFormatters.formatCurrency(exp, 1)))
}
}
}
private fun collectionItem(type: CollectionType, info: CollectionInfo, color: DyeColor, profile: Profile): WWidget {
val collectionCount = profile.members.values.sumOf { it.collection[type] ?: 0 }
val unlockedTiers = info.tiers.count { it.amountRequired <= collectionCount }
return WTitledItem(
SBItemEntryDefinition.getEntry(type.skyblockId).asItemEntry().value.copy()
.also {
it.setCustomName(
Text.literal(info.name).fillStyle(
Style.EMPTY.withItalic(false).withBold(true)
.withColor(color.toTextColor())
)
)
it.modifyLore { old ->
listOf(
Text.literal("${info.name} Collection: $collectionCount / ${info.tiers.last().amountRequired}"),
Text.literal("Tiers unlocked: $unlockedTiers / ${info.tiers.size}")
).map {
it.fillStyle(
Style.EMPTY.withItalic(false).withColor(Formatting.GRAY)
)
}
}
}, countString = Text.literal("$unlockedTiers").styled {
if (unlockedTiers == info.maxTiers)
it.withColor(Formatting.YELLOW)
else it
}
)
}
private fun collectionPanel(profileViewer: ProfileViewer): WTabPanel {
return WTabPanel().also {
val data = HypixelStaticData.collectionData
val panels = mutableListOf<WPanel>()
for ((collectionKind, collections) in data.entries) {
val skillT = CollectionCategory.values().find { it.name == collectionKind }
val color = skillT?.color ?: DyeColor.WHITE
val icon = skillT?.icon?.let { RepoManager.getNEUItem(it).asItemStack() } ?: ItemStack(Items.ITEM_FRAME)
val panel = WBox(Axis.HORIZONTAL).also {
it.horizontalAlignment = HorizontalAlignment.CENTER
it.add(WFixedPanel(WGridPanel().also {
it.insets = Insets.ROOT_PANEL
it.setGaps(2, 2)
var x = 0
var y = 0
for (item in collections.items) {
it.add(collectionItem(item.key, item.value, color, profileViewer.profile), x, y, 1, 1)
x++
if (x == 5) {
x = 0
y++
}
}
}))
}
panels.add(panel)
it.add(panel) {
it.tooltip(
Text.translatable("firmament.pv.skills.${collectionKind.lowercase()}")
.styled { it.withColor(color.toTextColor()) })
it.icon(ItemIcon(icon))
}
}
it.layout()
val tabWidth = it.width
panels.forEach { it.setSize(tabWidth - Insets.ROOT_PANEL.horizontal, it.height) }
}
}
override fun getElements(profileViewer: ProfileViewer): WWidget {
return WBox(Axis.HORIZONTAL).also {
it.insets = Insets.ROOT_PANEL
it.add(WGridPanel().also {
it.add(WText(Text.literal(profileViewer.account.getDisplayName(profileViewer.primaryName))), 0, 0, 8, 1)
for ((i, skill) in Skill.values().withIndex()) {
it.add(WText(Text.translatable("firmament.pv.skills.${skill.name.lowercase()}")), 0, i + 1, 4, 1)
it.add(skillBar(profileViewer, skill), 4, i + 1, 4, 1)
}
})
it.add(collectionPanel(profileViewer))
}
}
override val icon: Icon
get() = ItemIcon(ItemStack(Items.IRON_SWORD))
override val text: Text
get() = Text.translatable("firmament.pv.skills")
}

View File

@@ -1,69 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package moe.nea.firmament.hud
import io.github.cottonmc.cotton.gui.client.ScreenDrawing
import io.github.cottonmc.cotton.gui.widget.WWidget
import io.github.cottonmc.cotton.gui.widget.data.HorizontalAlignment
import io.github.cottonmc.cotton.gui.widget.data.Insets
import kotlin.math.roundToInt
import kotlin.math.sin
import net.minecraft.client.gui.DrawContext
val Insets.vertical get() = bottom + top
val Insets.horizontal get() = left + right
class ProgressBar(
var label: String,
var total: Int?, // If total is null, then make it a bouncy rectangle
var progress: Int = 0,
) : WWidget() {
var insets: Insets = Insets(7)
override fun canResize(): Boolean = true
fun reportProgress(label: String, progress: Int, total: Int?) {
synchronized(this) {
this.label = label
this.progress = progress
this.total = total
}
}
override fun paint(context: DrawContext, x: Int, y: Int, mouseX: Int, mouseY: Int) {
ScreenDrawing.coloredRect(context, x, y, width, height, 0xFF808080.toInt())
val (l, prog) = synchronized(this) {
label to (progress to total)
}
val (p, t) = prog
if (t == null) {
ScreenDrawing.coloredRect(
context,
(x + (1 + sin(System.currentTimeMillis().toDouble() / 1000)) * width * 3 / 4 / 2).roundToInt(),
y,
width / 4,
height,
0xFF00FF00.toInt()
)
} else {
ScreenDrawing.coloredRect(context, x, y, width * p / t, height, 0xFF00FF00.toInt())
}
ScreenDrawing.drawString(
context,
if (t != null) "$l ($p/$t)" else l,
HorizontalAlignment.CENTER,
x + insets.left,
y + insets.top,
width - insets.horizontal,
height - insets.vertical,
)
}
}

View File

@@ -6,11 +6,11 @@
package moe.nea.firmament.keybindings package moe.nea.firmament.keybindings
import org.lwjgl.glfw.GLFW
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import moe.nea.firmament.util.MC
import net.minecraft.client.MinecraftClient import net.minecraft.client.MinecraftClient
import net.minecraft.client.util.InputUtil import net.minecraft.client.util.InputUtil
import org.lwjgl.glfw.GLFW import moe.nea.firmament.util.MC
@Serializable @Serializable
data class SavedKeyBinding( data class SavedKeyBinding(
@@ -38,6 +38,24 @@ data class SavedKeyBinding(
modifiers and GLFW.GLFW_MOD_ALT != 0 modifiers and GLFW.GLFW_MOD_ALT != 0
) )
} }
fun getModInt(): Int {
val h = MC.window.handle
val ctrl = if (MinecraftClient.IS_SYSTEM_MAC) {
InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SUPER)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SUPER)
} else InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_CONTROL)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_CONTROL)
val shift = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SHIFT)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SHIFT)
val alt = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_ALT)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_ALT)
var mods = 0
if (ctrl) mods = mods or GLFW.GLFW_MOD_CONTROL
if (shift) mods = mods or GLFW.GLFW_MOD_SHIFT
if (alt) mods = mods or GLFW.GLFW_MOD_ALT
return mods
}
} }
fun isPressed(atLeast: Boolean = false): Boolean { fun isPressed(atLeast: Boolean = false): Boolean {
@@ -47,21 +65,21 @@ data class SavedKeyBinding(
val ctrl = if (MinecraftClient.IS_SYSTEM_MAC) { val ctrl = if (MinecraftClient.IS_SYSTEM_MAC) {
InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SUPER) InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SUPER)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SUPER) || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SUPER)
} else InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_CONTROL) } else InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_CONTROL)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_CONTROL) || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_CONTROL)
val shift = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SHIFT) val shift = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_SHIFT)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SHIFT) || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_SHIFT)
val alt = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_ALT) val alt = InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_LEFT_ALT)
|| InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_ALT) || InputUtil.isKeyPressed(h, GLFW.GLFW_KEY_RIGHT_ALT)
if (atLeast) if (atLeast)
return (ctrl >= this.ctrl) && return (ctrl >= this.ctrl) &&
(alt >= this.alt) && (alt >= this.alt) &&
(shift >= this.shift) (shift >= this.shift)
return (ctrl == this.ctrl) && return (ctrl == this.ctrl) &&
(alt == this.alt) && (alt == this.alt) &&
(shift == this.shift) (shift == this.shift)
} }
override fun matches(keyCode: Int, scanCode: Int, modifiers: Int): Boolean { override fun matches(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {

View File

@@ -79,10 +79,10 @@ data class SBItemStack(
) { ) {
val stats = petInfo.interpolatedStatsAtLevel(level) ?: return val stats = petInfo.interpolatedStatsAtLevel(level) ?: return
stats.otherNumbers.forEachIndexed { index, it -> stats.otherNumbers.forEachIndexed { index, it ->
replacementData[index.toString()] = FirmFormatters.formatCurrency(it, 1) replacementData[index.toString()] = FirmFormatters.formatCommas(it, 1)
} }
stats.statNumbers.forEach { (t, u) -> stats.statNumbers.forEach { (t, u) ->
replacementData[t] = FirmFormatters.formatCurrency(u, 1) replacementData[t] = FirmFormatters.formatCommas(u, 1)
} }
} }

View File

@@ -8,12 +8,12 @@
package moe.nea.firmament.repo package moe.nea.firmament.repo
import com.mojang.serialization.Dynamic import com.mojang.serialization.Dynamic
import io.github.cottonmc.cotton.gui.client.CottonHud
import io.github.moulberry.repo.IReloadable import io.github.moulberry.repo.IReloadable
import io.github.moulberry.repo.NEURepository import io.github.moulberry.repo.NEURepository
import io.github.moulberry.repo.data.NEUItem import io.github.moulberry.repo.data.NEUItem
import io.github.notenoughupdates.moulconfig.xml.Bind
import java.text.NumberFormat import java.text.NumberFormat
import java.util.* import java.util.UUID
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -32,6 +32,9 @@ import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtOps import net.minecraft.nbt.NbtOps
import net.minecraft.text.Text import net.minecraft.text.Text
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.gui.config.HudMeta
import moe.nea.firmament.gui.config.HudPosition
import moe.nea.firmament.gui.hud.MoulConfigHud
import moe.nea.firmament.util.LegacyTagParser import moe.nea.firmament.util.LegacyTagParser
import moe.nea.firmament.util.MC import moe.nea.firmament.util.MC
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
@@ -135,6 +138,30 @@ object ItemCache : IReloadable {
fun NEUItem.getIdentifier() = skyblockId.identifier fun NEUItem.getIdentifier() = skyblockId.identifier
var job: Job? = null var job: Job? = null
object ReloadProgressHud : MoulConfigHud(
"repo_reload", HudMeta(HudPosition(0.0, 0.0, 1F), Text.literal("Repo Reload"), 180, 18)) {
var isEnabled = false
override fun shouldRender(): Boolean {
return isEnabled
}
@get:Bind("current")
var current: Double = 0.0
@get:Bind("label")
var label: String = ""
@get:Bind("max")
var max: Double = 0.0
fun reportProgress(label: String, current: Int, max: Int) {
this.label = label
this.current = current.toDouble()
this.max = max.toDouble()
}
}
override fun reload(repository: NEURepository) { override fun reload(repository: NEURepository) {
val j = job val j = job
@@ -147,18 +174,18 @@ object ItemCache : IReloadable {
job = Firmament.coroutineScope.launch { job = Firmament.coroutineScope.launch {
val items = repository.items?.items val items = repository.items?.items
if (items == null) { if (items == null) {
CottonHud.remove(RepoManager.progressBar) ReloadProgressHud.isEnabled = false
return@launch return@launch
} }
val recacheItems = I18n.translate("firmament.repo.cache") val recacheItems = I18n.translate("firmament.repo.cache")
RepoManager.progressBar.reportProgress(recacheItems, 0, items.size) ReloadProgressHud.reportProgress(recacheItems, 0, items.size)
CottonHud.add(RepoManager.progressBar) ReloadProgressHud.isEnabled = true
var i = 0 var i = 0
items.values.forEach { items.values.forEach {
it.asItemStack() // Rebuild cache it.asItemStack() // Rebuild cache
RepoManager.progressBar.reportProgress(recacheItems, i++, items.size) ReloadProgressHud.reportProgress(recacheItems, i++, items.size)
} }
CottonHud.remove(RepoManager.progressBar) ReloadProgressHud.isEnabled = false
} }
} }

View File

@@ -6,7 +6,6 @@
package moe.nea.firmament.repo package moe.nea.firmament.repo
import io.github.cottonmc.cotton.gui.client.CottonHud
import io.github.moulberry.repo.NEURecipeCache import io.github.moulberry.repo.NEURecipeCache
import io.github.moulberry.repo.NEURepository import io.github.moulberry.repo.NEURepository
import io.github.moulberry.repo.NEURepositoryException import io.github.moulberry.repo.NEURepositoryException
@@ -21,7 +20,6 @@ import net.minecraft.text.Text
import moe.nea.firmament.Firmament import moe.nea.firmament.Firmament
import moe.nea.firmament.Firmament.logger import moe.nea.firmament.Firmament.logger
import moe.nea.firmament.gui.config.ManagedConfig import moe.nea.firmament.gui.config.ManagedConfig
import moe.nea.firmament.hud.ProgressBar
import moe.nea.firmament.rei.PetData import moe.nea.firmament.rei.PetData
import moe.nea.firmament.util.MinecraftDispatcher import moe.nea.firmament.util.MinecraftDispatcher
import moe.nea.firmament.util.SkyblockId import moe.nea.firmament.util.SkyblockId
@@ -54,12 +52,6 @@ object RepoManager {
var recentlyFailedToUpdateItemList = false var recentlyFailedToUpdateItemList = false
val progressBar by lazy {
ProgressBar("", null, 0).also {
it.setSize(180, 22)
}
}
val neuRepo: NEURepository = NEURepository.of(RepoDownloadManager.repoSavedLocation).apply { val neuRepo: NEURepository = NEURepository.of(RepoDownloadManager.repoSavedLocation).apply {
registerReloadListener(ItemCache) registerReloadListener(ItemCache)
registerReloadListener(ExpLadders) registerReloadListener(ExpLadders)
@@ -98,14 +90,13 @@ object RepoManager {
fun launchAsyncUpdate(force: Boolean = false) { fun launchAsyncUpdate(force: Boolean = false) {
Firmament.coroutineScope.launch { Firmament.coroutineScope.launch {
progressBar.reportProgress("Downloading", 0, null) ItemCache.ReloadProgressHud.reportProgress("Downloading", 0, -1) // TODO: replace with a proper boundy bar
CottonHud.add(progressBar) ItemCache.ReloadProgressHud.isEnabled = true
try { try {
RepoDownloadManager.downloadUpdate(force) RepoDownloadManager.downloadUpdate(force)
progressBar.reportProgress("Download complete", 1, 1) ItemCache.ReloadProgressHud.reportProgress("Download complete", 1, 1)
} finally { } finally {
CottonHud.remove(progressBar) ItemCache.ReloadProgressHud.isEnabled = false
} }
reload() reload()
} }
@@ -113,14 +104,14 @@ object RepoManager {
fun reload() { fun reload() {
try { try {
progressBar.reportProgress("Reloading from Disk", 0, null) ItemCache.ReloadProgressHud.reportProgress("Reloading from Disk", 0, -1) // TODO: replace with a proper boundy bar
CottonHud.add(progressBar) ItemCache.ReloadProgressHud.isEnabled = true
neuRepo.reload() neuRepo.reload()
} catch (exc: NEURepositoryException) { } catch (exc: NEURepositoryException) {
MinecraftClient.getInstance().player?.sendMessage( MinecraftClient.getInstance().player?.sendMessage(
Text.literal("Failed to reload repository. This will result in some mod features not working.") Text.literal("Failed to reload repository. This will result in some mod features not working.")
) )
CottonHud.remove(progressBar) ItemCache.ReloadProgressHud.isEnabled = false
exc.printStackTrace() exc.printStackTrace()
} }
} }

View File

@@ -11,21 +11,22 @@ import kotlin.math.absoluteValue
import kotlin.time.Duration import kotlin.time.Duration
object FirmFormatters { object FirmFormatters {
fun formatCurrency(long: Long, segments: Int = 3): String { fun formatCommas(int: Int, segments: Int = 3): String = formatCommas(int.toLong(), segments)
fun formatCommas(long: Long, segments: Int = 3): String {
val α = long / 1000 val α = long / 1000
if (α != 0L) { if (α != 0L) {
return formatCurrency(α, segments) + "," + (long - α * 1000).toString().padStart(3, '0') return formatCommas(α, segments) + "," + (long - α * 1000).toString().padStart(3, '0')
} }
return long.toString() return long.toString()
} }
fun formatCurrency(float: Float, fractionalDigits: Int): String = formatCurrency(float.toDouble(), fractionalDigits) fun formatCommas(float: Float, fractionalDigits: Int): String = formatCommas(float.toDouble(), fractionalDigits)
fun formatCurrency(double: Double, fractionalDigits: Int): String { fun formatCommas(double: Double, fractionalDigits: Int): String {
val long = double.toLong() val long = double.toLong()
val δ = (double - long).absoluteValue val δ = (double - long).absoluteValue
val μ = pow(10, fractionalDigits) val μ = pow(10, fractionalDigits)
val digits = (μ * δ).toInt().toString().padStart(fractionalDigits, '0').trimEnd('0') val digits = (μ * δ).toInt().toString().padStart(fractionalDigits, '0').trimEnd('0')
return formatCurrency(long) + (if (digits.isEmpty()) "" else ".$digits") return formatCommas(long) + (if (digits.isEmpty()) "" else ".$digits")
} }
fun formatDistance(distance: Double): String { fun formatDistance(distance: Double): String {
@@ -34,8 +35,29 @@ object FirmFormatters {
return "%dm".format(distance.toInt()) return "%dm".format(distance.toInt())
} }
fun formatTimespan(duration: Duration): String { fun formatTimespan(duration: Duration, millis: Boolean = false): String {
return duration.toString() if (duration.isInfinite()) {
return if (duration.isPositive()) ""
else "-∞"
}
val sb = StringBuilder()
if (duration.isNegative()) sb.append("-")
duration.toComponents { days, hours, minutes, seconds, nanoseconds ->
if (days > 0) {
sb.append(days).append("d")
}
if (hours > 0) {
sb.append(hours).append("h")
}
if (minutes > 0) {
sb.append(minutes).append("m")
}
sb.append(seconds).append("s")
if (millis) {
sb.append(nanoseconds / 1_000_000).append("ms")
}
}
return sb.toString()
} }
} }

View File

@@ -77,9 +77,9 @@ object MoulConfigUtils {
override fun createInstance(context: XMLContext<*>, element: Element): FirmButtonComponent { override fun createInstance(context: XMLContext<*>, element: Element): FirmButtonComponent {
return FirmButtonComponent( return FirmButtonComponent(
context.getChildFragment(element), context.getChildFragment(element),
context.getMethodFromAttribute(element, QName("onClick")),
context.getPropertyFromAttribute(element, QName("enabled"), Boolean::class.java) context.getPropertyFromAttribute(element, QName("enabled"), Boolean::class.java)
?: GetSetter.constant(true) ?: GetSetter.constant(true),
context.getMethodFromAttribute(element, QName("onClick")),
) )
} }

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
SPDX-FileCopyrightText: 2023 Linnea Gräf <nea@nea.moe>
SPDX-License-Identifier: GPL-3.0-or-later
-->
<Root xmlns="http://notenoughupdates.org/moulconfig" xmlns:firm="http://firmament.nea.moe/moulconfig">
<Row>
<firm:Bar progress="@current" total="@max" emptyColor="#30ff30" fillColor="#30aa30"/>
<Text text="@label"/>
</Row>
</Root>

View File

@@ -97,6 +97,7 @@
"firmament.config.waypoints": "Waypoints", "firmament.config.waypoints": "Waypoints",
"firmament.config.waypoints.temp-waypoint-duration": "Temporary Waypoint Duration", "firmament.config.waypoints.temp-waypoint-duration": "Temporary Waypoint Duration",
"firmament.config.waypoints.show-index": "Show ordered waypoint indexes", "firmament.config.waypoints.show-index": "Show ordered waypoint indexes",
"firmament.config.waypoints.skip-to-nearest": "Allow skipping waypoints",
"firmament.recipe.forge.time": "Forging Time: %s", "firmament.recipe.forge.time": "Forging Time: %s",
"firmament.recipe.mobs.drops": "§e§lDrop Chance: %s", "firmament.recipe.mobs.drops": "§e§lDrop Chance: %s",
"firmament.recipe.mobs.name": "§8[§7Lv %d§8] §c%s", "firmament.recipe.mobs.name": "§8[§7Lv %d§8] §c%s",

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -15,3 +15,4 @@ accessible field net/minecraft/entity/passive/AbstractHorseEntity items Lnet/min
accessible field net/minecraft/entity/passive/AbstractHorseEntity SADDLED_FLAG I accessible field net/minecraft/entity/passive/AbstractHorseEntity SADDLED_FLAG I
accessible field net/minecraft/entity/passive/AbstractHorseEntity HORSE_FLAGS Lnet/minecraft/entity/data/TrackedData; accessible field net/minecraft/entity/passive/AbstractHorseEntity HORSE_FLAGS Lnet/minecraft/entity/data/TrackedData;
accessible method net/minecraft/resource/NamespaceResourceManager loadMetadata (Lnet/minecraft/resource/InputSupplier;)Lnet/minecraft/resource/metadata/ResourceMetadata; accessible method net/minecraft/resource/NamespaceResourceManager loadMetadata (Lnet/minecraft/resource/InputSupplier;)Lnet/minecraft/resource/metadata/ResourceMetadata;
accessible method net/minecraft/client/gui/DrawContext drawTexturedQuad (Lnet/minecraft/util/Identifier;IIIIIFFFFFFFF)V