Add Pristine Profit Tracker
This commit is contained in:
@@ -26,7 +26,7 @@ mixinextras = "0.2.0-rc.5"
|
|||||||
jarvis = "1.1.1"
|
jarvis = "1.1.1"
|
||||||
nealisp = "1.0.0"
|
nealisp = "1.0.0"
|
||||||
explosiveenhancement = "1.2.1-1.20.x"
|
explosiveenhancement = "1.2.1-1.20.x"
|
||||||
moulconfig = "2.4.0"
|
moulconfig = "9999.9999.9999"
|
||||||
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import moe.nea.firmament.features.inventory.SaveCursorPosition
|
|||||||
import moe.nea.firmament.features.inventory.SlotLocking
|
import moe.nea.firmament.features.inventory.SlotLocking
|
||||||
import moe.nea.firmament.features.inventory.buttons.InventoryButtons
|
import moe.nea.firmament.features.inventory.buttons.InventoryButtons
|
||||||
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlay
|
import moe.nea.firmament.features.inventory.storageoverlay.StorageOverlay
|
||||||
|
import moe.nea.firmament.features.mining.PristineProfitTracker
|
||||||
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures
|
import moe.nea.firmament.features.texturepack.CustomSkyBlockTextures
|
||||||
import moe.nea.firmament.features.world.FairySouls
|
import moe.nea.firmament.features.world.FairySouls
|
||||||
import moe.nea.firmament.features.world.Waypoints
|
import moe.nea.firmament.features.world.Waypoints
|
||||||
@@ -55,6 +56,7 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature
|
|||||||
// TODO: loadFeature(FishingWarning)
|
// TODO: loadFeature(FishingWarning)
|
||||||
loadFeature(SlotLocking)
|
loadFeature(SlotLocking)
|
||||||
loadFeature(StorageOverlay)
|
loadFeature(StorageOverlay)
|
||||||
|
loadFeature(PristineProfitTracker)
|
||||||
loadFeature(CraftingOverlay)
|
loadFeature(CraftingOverlay)
|
||||||
loadFeature(PowerUserTools)
|
loadFeature(PowerUserTools)
|
||||||
loadFeature(Waypoints)
|
loadFeature(Waypoints)
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.features.mining
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import moe.nea.firmament.util.TimeMark
|
||||||
|
|
||||||
|
class Histogram<T>(
|
||||||
|
val maxSize: Int,
|
||||||
|
val maxDuration: Duration,
|
||||||
|
) {
|
||||||
|
|
||||||
|
data class OrderedTimestamp(val timestamp: TimeMark, val order: Int) : Comparable<OrderedTimestamp> {
|
||||||
|
override fun compareTo(other: OrderedTimestamp): Int {
|
||||||
|
val o = timestamp.compareTo(other.timestamp)
|
||||||
|
if (o != 0) return o
|
||||||
|
return order.compareTo(other.order)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val size: Int get() = dataPoints.size
|
||||||
|
private val dataPoints: NavigableMap<OrderedTimestamp, T> = TreeMap()
|
||||||
|
|
||||||
|
private var order = Int.MIN_VALUE
|
||||||
|
|
||||||
|
fun record(entry: T, timestamp: TimeMark = TimeMark.now()) {
|
||||||
|
dataPoints[OrderedTimestamp(timestamp, order++)] = entry
|
||||||
|
trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun oldestUpdate(): TimeMark {
|
||||||
|
trim()
|
||||||
|
return if (dataPoints.isEmpty()) TimeMark.now() else dataPoints.firstKey().timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
fun latestUpdate(): TimeMark {
|
||||||
|
trim()
|
||||||
|
return if (dataPoints.isEmpty()) TimeMark.farPast() else dataPoints.lastKey().timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
fun averagePer(valueExtractor: (T) -> Double, perDuration: Duration): Double? {
|
||||||
|
return aggregate(
|
||||||
|
seed = 0.0,
|
||||||
|
operator = { accumulator, entry, _ -> accumulator + valueExtractor(entry) },
|
||||||
|
finish = { sum, beginning, end ->
|
||||||
|
val timespan = end - beginning
|
||||||
|
if (timespan > perDuration)
|
||||||
|
sum / (timespan / perDuration)
|
||||||
|
else null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <V, R> aggregate(
|
||||||
|
seed: V,
|
||||||
|
operator: (V, T, TimeMark) -> V,
|
||||||
|
finish: (V, TimeMark, TimeMark) -> R
|
||||||
|
): R? {
|
||||||
|
trim()
|
||||||
|
var accumulator = seed
|
||||||
|
var min: TimeMark? = null
|
||||||
|
var max: TimeMark? = null
|
||||||
|
dataPoints.forEach { (key, value) ->
|
||||||
|
max = key.timestamp
|
||||||
|
if (min == null)
|
||||||
|
min = key.timestamp
|
||||||
|
accumulator = operator(accumulator, value, key.timestamp)
|
||||||
|
}
|
||||||
|
if (min == null)
|
||||||
|
return null
|
||||||
|
return finish(accumulator, min!!, max!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun trim() {
|
||||||
|
while (maxSize < dataPoints.size) {
|
||||||
|
dataPoints.pollFirstEntry()
|
||||||
|
}
|
||||||
|
dataPoints.headMap(OrderedTimestamp(TimeMark.ago(maxDuration), Int.MAX_VALUE)).clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.features.mining
|
||||||
|
|
||||||
|
import io.github.moulberry.moulconfig.xml.Bind
|
||||||
|
import moe.nea.jarvis.api.Point
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.serializer
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
import net.minecraft.text.Text
|
||||||
|
import moe.nea.firmament.events.ProcessChatEvent
|
||||||
|
import moe.nea.firmament.features.FirmamentFeature
|
||||||
|
import moe.nea.firmament.gui.config.ManagedConfig
|
||||||
|
import moe.nea.firmament.gui.hud.MoulConfigHud
|
||||||
|
import moe.nea.firmament.util.BazaarPriceStrategy
|
||||||
|
import moe.nea.firmament.util.FirmFormatters.formatCurrency
|
||||||
|
import moe.nea.firmament.util.SkyblockId
|
||||||
|
import moe.nea.firmament.util.data.ProfileSpecificDataHolder
|
||||||
|
import moe.nea.firmament.util.formattedString
|
||||||
|
import moe.nea.firmament.util.parseIntWithComma
|
||||||
|
import moe.nea.firmament.util.useMatch
|
||||||
|
|
||||||
|
object PristineProfitTracker : FirmamentFeature {
|
||||||
|
override val identifier: String
|
||||||
|
get() = "pristine-profit"
|
||||||
|
|
||||||
|
enum class GemstoneKind(
|
||||||
|
val label: String,
|
||||||
|
val flawedId: SkyblockId,
|
||||||
|
) {
|
||||||
|
SAPPHIRE("Sapphire", SkyblockId("FLAWED_SAPPHIRE_GEM")),
|
||||||
|
RUBY("Ruby", SkyblockId("FLAWED_RUBY_GEM")),
|
||||||
|
AMETHYST("Amethyst", SkyblockId("FLAWED_AMETHYST_GEM")),
|
||||||
|
AMBER("Amber", SkyblockId("FLAWED_AMBER_GEM")),
|
||||||
|
TOPAZ("Topaz", SkyblockId("FLAWED_TOPAZ_GEM")),
|
||||||
|
JADE("Jade", SkyblockId("FLAWED_JADE_GEM")),
|
||||||
|
JASPER("Jasper", SkyblockId("FLAWED_JASPER_GEM")),
|
||||||
|
OPAL("Opal", SkyblockId("FLAWED_OPAL_GEM")),
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Data(
|
||||||
|
var maxMoneyPerSecond: Double = 1.0,
|
||||||
|
var maxCollectionPerSecond: Double = 1.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
object DConfig : ProfileSpecificDataHolder<Data>(serializer(), identifier, ::Data)
|
||||||
|
|
||||||
|
override val config: ManagedConfig?
|
||||||
|
get() = TConfig
|
||||||
|
|
||||||
|
object TConfig : ManagedConfig(identifier) {
|
||||||
|
val timeout by duration("timeout", 0.seconds, 120.seconds) { 30.seconds }
|
||||||
|
val gui by position("position", 80, 30) { Point(0.05, 0.2) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val sellingStrategy = BazaarPriceStrategy.SELL_ORDER
|
||||||
|
|
||||||
|
val pristineRegex =
|
||||||
|
"PRISTINE! You found . Flawed (?<kind>${
|
||||||
|
GemstoneKind.values().joinToString("|") { it.label }
|
||||||
|
}) Gemstone x(?<count>[0-9,]+)!".toPattern()
|
||||||
|
|
||||||
|
val collectionHistogram = Histogram<Double>(10000, 180.seconds)
|
||||||
|
val moneyHistogram = Histogram<Double>(10000, 180.seconds)
|
||||||
|
|
||||||
|
object ProfitHud : MoulConfigHud("pristine_profit", TConfig.gui) {
|
||||||
|
@field:Bind
|
||||||
|
var moneyCurrent: Double = 0.0
|
||||||
|
|
||||||
|
@field:Bind
|
||||||
|
var moneyMax: Double = 1.0
|
||||||
|
|
||||||
|
@field:Bind
|
||||||
|
var moneyText = ""
|
||||||
|
|
||||||
|
@field:Bind
|
||||||
|
var collectionCurrent = 0.0
|
||||||
|
|
||||||
|
@field:Bind
|
||||||
|
var collectionMax = 1.0
|
||||||
|
|
||||||
|
@field:Bind
|
||||||
|
var collectionText = ""
|
||||||
|
override fun shouldRender(): Boolean = collectionHistogram.latestUpdate().passedTime() < TConfig.timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
val SECONDS_PER_HOUR = 3600
|
||||||
|
val ROUGHS_PER_FLAWED = 80
|
||||||
|
|
||||||
|
fun updateUi() {
|
||||||
|
val collectionPerSecond = collectionHistogram.averagePer({ it }, 1.seconds)
|
||||||
|
val moneyPerSecond = moneyHistogram.averagePer({ it }, 1.seconds)
|
||||||
|
if (collectionPerSecond == null || moneyPerSecond == null) return
|
||||||
|
ProfitHud.collectionCurrent = collectionPerSecond
|
||||||
|
ProfitHud.collectionText = Text.translatable(
|
||||||
|
"firmament.pristine-profit.collection",
|
||||||
|
formatCurrency(collectionPerSecond * SECONDS_PER_HOUR, 1)
|
||||||
|
).formattedString()
|
||||||
|
ProfitHud.moneyCurrent = moneyPerSecond
|
||||||
|
ProfitHud.moneyText = Text.translatable(
|
||||||
|
"firmament.pristine-profit.money",
|
||||||
|
formatCurrency(moneyPerSecond * SECONDS_PER_HOUR, 1)
|
||||||
|
).formattedString()
|
||||||
|
val data = DConfig.data
|
||||||
|
if (data != null) {
|
||||||
|
if (data.maxCollectionPerSecond < collectionPerSecond && collectionHistogram.oldestUpdate()
|
||||||
|
.passedTime() > 30.seconds
|
||||||
|
) {
|
||||||
|
data.maxCollectionPerSecond = collectionPerSecond
|
||||||
|
DConfig.markDirty()
|
||||||
|
}
|
||||||
|
if (data.maxMoneyPerSecond < moneyPerSecond && moneyHistogram.oldestUpdate().passedTime() > 30.seconds) {
|
||||||
|
data.maxMoneyPerSecond = moneyPerSecond
|
||||||
|
DConfig.markDirty()
|
||||||
|
}
|
||||||
|
ProfitHud.collectionMax = maxOf(data.maxCollectionPerSecond, collectionPerSecond)
|
||||||
|
ProfitHud.moneyMax = maxOf(data.maxMoneyPerSecond, moneyPerSecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onLoad() {
|
||||||
|
ProcessChatEvent.subscribe {
|
||||||
|
pristineRegex.useMatch(it.unformattedString) {
|
||||||
|
val gemstoneKind = GemstoneKind.valueOf(group("kind").uppercase())
|
||||||
|
val flawedCount = parseIntWithComma(group("count"))
|
||||||
|
val moneyAmount = sellingStrategy.getSellPrice(gemstoneKind.flawedId) * flawedCount
|
||||||
|
moneyHistogram.record(moneyAmount)
|
||||||
|
val collectionAmount = flawedCount * ROUGHS_PER_FLAWED
|
||||||
|
collectionHistogram.record(collectionAmount.toDouble())
|
||||||
|
updateUi()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
src/main/kotlin/moe/nea/firmament/gui/BarComponent.kt
Normal file
113
src/main/kotlin/moe/nea/firmament/gui/BarComponent.kt
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* 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.ScreenDrawing
|
||||||
|
import io.github.cottonmc.cotton.gui.widget.data.Texture
|
||||||
|
import io.github.moulberry.moulconfig.common.MyResourceLocation
|
||||||
|
import io.github.moulberry.moulconfig.common.RenderContext
|
||||||
|
import io.github.moulberry.moulconfig.gui.GuiComponent
|
||||||
|
import io.github.moulberry.moulconfig.gui.GuiImmediateContext
|
||||||
|
import io.github.moulberry.moulconfig.observer.GetSetter
|
||||||
|
import io.github.notenoughupdates.moulconfig.platform.ModernRenderContext
|
||||||
|
import me.shedaniel.math.Color
|
||||||
|
import net.minecraft.client.gui.DrawContext
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import moe.nea.firmament.Firmament
|
||||||
|
|
||||||
|
class BarComponent(
|
||||||
|
val progress: GetSetter<Double>, val total: GetSetter<Double>,
|
||||||
|
val fillColor: Color,
|
||||||
|
val emptyColor: Color,
|
||||||
|
) : GuiComponent() {
|
||||||
|
override fun getWidth(): Int {
|
||||||
|
return 80
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getHeight(): Int {
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawSection(
|
||||||
|
context: DrawContext,
|
||||||
|
texture: Texture,
|
||||||
|
x: Int,
|
||||||
|
y: Int,
|
||||||
|
width: Int,
|
||||||
|
sectionStart: Double,
|
||||||
|
sectionEnd: Double
|
||||||
|
) {
|
||||||
|
if (sectionEnd < progress.get() && width == 4) {
|
||||||
|
ScreenDrawing.texturedRect(context, x, y, 4, 8, texture, fillColor.color)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (sectionStart > progress.get() && 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.get()) fillColor.color else emptyColor.color
|
||||||
|
)
|
||||||
|
valueAtPixel += increasePerPixel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun render(context: GuiImmediateContext) {
|
||||||
|
val renderContext = (context.renderContext as ModernRenderContext).drawContext
|
||||||
|
var i = 0
|
||||||
|
val x = 0
|
||||||
|
val y = 0
|
||||||
|
while (i < context.width - 4) {
|
||||||
|
drawSection(
|
||||||
|
renderContext,
|
||||||
|
if (i == 0) left else middle,
|
||||||
|
x + i, y,
|
||||||
|
(context.width - (i + 4)).coerceAtMost(4),
|
||||||
|
i * total.get() / context.width, (i + 4) * total.get() / context.width
|
||||||
|
)
|
||||||
|
i += 4
|
||||||
|
}
|
||||||
|
drawSection(
|
||||||
|
renderContext,
|
||||||
|
right,
|
||||||
|
x + context.width - 4,
|
||||||
|
y,
|
||||||
|
4,
|
||||||
|
(context.width - 4) * total.get() / context.width,
|
||||||
|
total.get()
|
||||||
|
)
|
||||||
|
RenderSystem.setShaderColor(1F, 1F, 1F, 1F)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Identifier.toMoulConfig(): MyResourceLocation {
|
||||||
|
return MyResourceLocation(this.namespace, this.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RenderContext.color(color: Color) {
|
||||||
|
color(color.red, color.green, color.blue, color.alpha)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RenderContext.color(red: Int, green: Int, blue: Int, alpha: Int) {
|
||||||
|
color(red / 255f, green / 255f, blue / 255f, alpha / 255f)
|
||||||
|
}
|
||||||
@@ -16,9 +16,9 @@ import moe.nea.firmament.Firmament
|
|||||||
|
|
||||||
open class WBar(
|
open class WBar(
|
||||||
var progress: Double,
|
var progress: Double,
|
||||||
val total: Double,
|
var total: Double,
|
||||||
val fillColor: Color,
|
val fillColor: Color,
|
||||||
val emptyColor: Color,
|
val emptyColor: Color = fillColor.darker(2.0),
|
||||||
) : WWidget() {
|
) : WWidget() {
|
||||||
companion object {
|
companion object {
|
||||||
val resource = Firmament.identifier("textures/gui/bar.png")
|
val resource = Firmament.identifier("textures/gui/bar.png")
|
||||||
|
|||||||
62
src/main/kotlin/moe/nea/firmament/gui/hud/MoulConfigHud.kt
Normal file
62
src/main/kotlin/moe/nea/firmament/gui/hud/MoulConfigHud.kt
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.gui.hud
|
||||||
|
|
||||||
|
import io.github.moulberry.moulconfig.gui.GuiContext
|
||||||
|
import io.github.moulberry.moulconfig.gui.component.TextComponent
|
||||||
|
import io.github.notenoughupdates.moulconfig.gui.GuiComponentWrapper
|
||||||
|
import net.minecraft.resource.ResourceManager
|
||||||
|
import net.minecraft.resource.SynchronousResourceReloader
|
||||||
|
import moe.nea.firmament.events.HudRenderEvent
|
||||||
|
import moe.nea.firmament.gui.config.HudMeta
|
||||||
|
import moe.nea.firmament.util.MC
|
||||||
|
import moe.nea.firmament.util.MoulConfigUtils
|
||||||
|
|
||||||
|
abstract class MoulConfigHud(
|
||||||
|
val name: String,
|
||||||
|
val hudMeta: HudMeta,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private val componentWrapper = object : GuiComponentWrapper(GuiContext(TextComponent("§cERROR"))) {
|
||||||
|
init {
|
||||||
|
this.client = MC.instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var fragment: GuiContext? = null
|
||||||
|
|
||||||
|
open fun shouldRender(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
HudRenderEvent.subscribe {
|
||||||
|
if (!shouldRender()) return@subscribe
|
||||||
|
val renderContext = componentWrapper.createContext(it.context)
|
||||||
|
if (fragment == null)
|
||||||
|
loadFragment()
|
||||||
|
it.context.matrices.push()
|
||||||
|
hudMeta.applyTransformations(it.context.matrices)
|
||||||
|
val renderContextTranslated =
|
||||||
|
renderContext.translated(hudMeta.absoluteX, hudMeta.absoluteY, hudMeta.width, hudMeta.height)
|
||||||
|
.scaled(hudMeta.scale)
|
||||||
|
fragment!!.root.render(renderContextTranslated)
|
||||||
|
it.context.matrices.pop()
|
||||||
|
}
|
||||||
|
MC.resourceManager.registerReloader(object : SynchronousResourceReloader {
|
||||||
|
override fun reload(manager: ResourceManager?) {
|
||||||
|
fragment = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadFragment() {
|
||||||
|
fragment = MoulConfigUtils.loadGui(name, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.util
|
||||||
|
|
||||||
|
import moe.nea.firmament.repo.HypixelStaticData
|
||||||
|
|
||||||
|
enum class BazaarPriceStrategy {
|
||||||
|
BUY_ORDER,
|
||||||
|
SELL_ORDER,
|
||||||
|
NPC_SELL;
|
||||||
|
|
||||||
|
fun getSellPrice(skyblockId: SkyblockId): Double {
|
||||||
|
val bazaarEntry = HypixelStaticData.bazaarData[skyblockId] ?: return 0.0
|
||||||
|
return when (this) {
|
||||||
|
BUY_ORDER -> bazaarEntry.quickStatus.sellPrice
|
||||||
|
SELL_ORDER -> bazaarEntry.quickStatus.buyPrice
|
||||||
|
NPC_SELL -> TODO()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -91,7 +91,7 @@ abstract class FragmentGuiScreen(
|
|||||||
verticalAmount: Double
|
verticalAmount: Double
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return ifPopup {
|
return ifPopup {
|
||||||
it.mouseScrolled(mouseX, mouseY, verticalAmount)
|
it.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import net.minecraft.client.gui.screen.ingame.HandledScreen
|
|||||||
import net.minecraft.network.message.ArgumentSignatureDataMap
|
import net.minecraft.network.message.ArgumentSignatureDataMap
|
||||||
import net.minecraft.network.message.LastSeenMessagesCollector.LastSeenMessages
|
import net.minecraft.network.message.LastSeenMessagesCollector.LastSeenMessages
|
||||||
import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket
|
import net.minecraft.network.packet.c2s.play.CommandExecutionC2SPacket
|
||||||
|
import net.minecraft.resource.ReloadableResourceManagerImpl
|
||||||
import net.minecraft.text.Text
|
import net.minecraft.text.Text
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
import moe.nea.firmament.events.TickEvent
|
import moe.nea.firmament.events.TickEvent
|
||||||
@@ -59,6 +60,7 @@ object MC {
|
|||||||
player?.networkHandler?.sendCommand(command)
|
player?.networkHandler?.sendCommand(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline val resourceManager get() = (MinecraftClient.getInstance().resourceManager as ReloadableResourceManagerImpl)
|
||||||
inline val networkHandler get() = player?.networkHandler
|
inline val networkHandler get() = player?.networkHandler
|
||||||
inline val instance get() = MinecraftClient.getInstance()
|
inline val instance get() = MinecraftClient.getInstance()
|
||||||
inline val keyboard get() = MinecraftClient.getInstance().keyboard
|
inline val keyboard get() = MinecraftClient.getInstance().keyboard
|
||||||
|
|||||||
@@ -8,10 +8,60 @@ package moe.nea.firmament.util
|
|||||||
|
|
||||||
import io.github.moulberry.moulconfig.common.MyResourceLocation
|
import io.github.moulberry.moulconfig.common.MyResourceLocation
|
||||||
import io.github.moulberry.moulconfig.gui.GuiContext
|
import io.github.moulberry.moulconfig.gui.GuiContext
|
||||||
|
import io.github.moulberry.moulconfig.xml.ChildCount
|
||||||
|
import io.github.moulberry.moulconfig.xml.XMLContext
|
||||||
|
import io.github.moulberry.moulconfig.xml.XMLGuiLoader
|
||||||
import io.github.moulberry.moulconfig.xml.XMLUniverse
|
import io.github.moulberry.moulconfig.xml.XMLUniverse
|
||||||
|
import javax.xml.namespace.QName
|
||||||
|
import me.shedaniel.math.Color
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
import moe.nea.firmament.gui.BarComponent
|
||||||
|
|
||||||
object MoulConfigUtils {
|
object MoulConfigUtils {
|
||||||
val universe = XMLUniverse.getDefaultUniverse()
|
val firmUrl = "http://nea.moe/Firmament"
|
||||||
|
val universe = XMLUniverse.getDefaultUniverse().also { uni ->
|
||||||
|
uni.registerMapper(java.awt.Color::class.java) {
|
||||||
|
if (it.startsWith("#")) {
|
||||||
|
val hexString = it.substring(1)
|
||||||
|
val hex = hexString.toInt(16)
|
||||||
|
if (hexString.length == 6) {
|
||||||
|
return@registerMapper java.awt.Color(hex)
|
||||||
|
}
|
||||||
|
if (hexString.length == 8) {
|
||||||
|
return@registerMapper java.awt.Color(hex, true)
|
||||||
|
}
|
||||||
|
error("Hexcolor $it needs to be exactly 6 or 8 hex digits long")
|
||||||
|
}
|
||||||
|
return@registerMapper java.awt.Color(it.toInt(), true)
|
||||||
|
}
|
||||||
|
uni.registerMapper(Color::class.java) {
|
||||||
|
val color = uni.mapXMLObject(it, java.awt.Color::class.java)
|
||||||
|
Color.ofRGBA(color.red, color.green, color.blue, color.alpha)
|
||||||
|
}
|
||||||
|
uni.registerLoader(object : XMLGuiLoader<BarComponent> {
|
||||||
|
override fun getName(): QName {
|
||||||
|
return QName(firmUrl, "Bar")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createInstance(context: XMLContext<*>, element: Element): BarComponent {
|
||||||
|
return BarComponent(
|
||||||
|
context.getPropertyFromAttribute(element, QName("progress"), Double::class.java)!!,
|
||||||
|
context.getPropertyFromAttribute(element, QName("total"), Double::class.java)!!,
|
||||||
|
context.getPropertyFromAttribute(element, QName("fillColor"), Color::class.java)!!.get(),
|
||||||
|
context.getPropertyFromAttribute(element, QName("emptyColor"), Color::class.java)!!.get(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getChildCount(): ChildCount {
|
||||||
|
return ChildCount.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAttributeNames(): Map<String, Boolean> {
|
||||||
|
return mapOf("progress" to true, "total" to true, "emptyColor" to true, "fillColor" to true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fun loadGui(name: String, bindTo: Any): GuiContext {
|
fun loadGui(name: String, bindTo: Any): GuiContext {
|
||||||
return GuiContext(universe.load(bindTo, MyResourceLocation("firmament", "gui/$name.xml")))
|
return GuiContext(universe.load(bindTo, MyResourceLocation("firmament", "gui/$name.xml")))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,22 +7,42 @@
|
|||||||
package moe.nea.firmament.util
|
package moe.nea.firmament.util
|
||||||
|
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlin.time.TimeSource
|
|
||||||
|
|
||||||
@OptIn(ExperimentalTime::class)
|
class TimeMark private constructor(private val timeMark: Long) : Comparable<TimeMark> {
|
||||||
class TimeMark private constructor(private val timeMark: TimeSource.Monotonic.ValueTimeMark?) : Comparable<TimeMark> {
|
fun passedTime() = if (timeMark == 0L) Duration.INFINITE else (System.currentTimeMillis() - timeMark).milliseconds
|
||||||
fun passedTime() = timeMark?.elapsedNow() ?: Duration.INFINITE
|
|
||||||
|
operator fun minus(other: TimeMark): Duration {
|
||||||
|
if (other.timeMark == timeMark)
|
||||||
|
return 0.milliseconds
|
||||||
|
if (other.timeMark == 0L)
|
||||||
|
return Duration.INFINITE
|
||||||
|
if (timeMark == 0L)
|
||||||
|
return -Duration.INFINITE
|
||||||
|
return (timeMark - other.timeMark).milliseconds
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun now() = TimeMark(TimeSource.Monotonic.markNow())
|
fun now() = TimeMark(System.currentTimeMillis())
|
||||||
fun farPast() = TimeMark(null)
|
fun farPast() = TimeMark(0L)
|
||||||
|
fun ago(timeDelta: Duration): TimeMark {
|
||||||
|
if (timeDelta.isFinite()) {
|
||||||
|
return TimeMark(System.currentTimeMillis() - timeDelta.inWholeMilliseconds)
|
||||||
|
}
|
||||||
|
require(timeDelta.isPositive())
|
||||||
|
return farPast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return timeMark.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
return other is TimeMark && other.timeMark == timeMark
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun compareTo(other: TimeMark): Int {
|
override fun compareTo(other: TimeMark): Int {
|
||||||
if (this.timeMark == other.timeMark) return 0
|
|
||||||
if (this.timeMark == null) return -1
|
|
||||||
if (other.timeMark == null) return -1
|
|
||||||
return this.timeMark.compareTo(other.timeMark)
|
return this.timeMark.compareTo(other.timeMark)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,13 @@
|
|||||||
|
|
||||||
package moe.nea.firmament.util
|
package moe.nea.firmament.util
|
||||||
|
|
||||||
|
import java.util.regex.Matcher
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
inline fun <T> String.ifMatches(regex: Regex, block: (MatchResult) -> T): T? =
|
inline fun <T> String.ifMatches(regex: Regex, block: (MatchResult) -> T): T? =
|
||||||
regex.matchEntire(this)?.let(block)
|
regex.matchEntire(this)?.let(block)
|
||||||
|
|
||||||
|
inline fun <T> Pattern.useMatch(string: String, block: Matcher.() -> T): T? =
|
||||||
|
matcher(string)
|
||||||
|
.takeIf(Matcher::matches)
|
||||||
|
?.let(block)
|
||||||
|
|||||||
11
src/main/kotlin/moe/nea/firmament/util/stringutil.kt
Normal file
11
src/main/kotlin/moe/nea/firmament/util/stringutil.kt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Linnea Gräf <nea@nea.moe>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package moe.nea.firmament.util
|
||||||
|
|
||||||
|
fun parseIntWithComma(string: String): Int {
|
||||||
|
return string.replace(",", "").toInt()
|
||||||
|
}
|
||||||
22
src/main/resources/assets/firmament/gui/pristine_profit.xml
Normal file
22
src/main/resources/assets/firmament/gui/pristine_profit.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?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:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:firm="http://nea.moe/Firmament"
|
||||||
|
xsi:schemaLocation="http://notenoughupdates.org/moulconfig https://raw.githubusercontent.com/NotEnoughUpdates/MoulConfig/master/MoulConfig.xsd">
|
||||||
|
<Column>
|
||||||
|
<Row>
|
||||||
|
<firm:Bar progress="@moneyCurrent" total="@moneyMax" fillColor="#bbbb00" emptyColor="#909000"/>
|
||||||
|
<Text text="@moneyText"/>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<firm:Bar progress="@collectionCurrent" total="@collectionMax" fillColor="#33ff33" emptyColor="#00a000"/>
|
||||||
|
<Text text="@collectionText"/>
|
||||||
|
</Row>
|
||||||
|
</Column>
|
||||||
|
</Root>
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
"firmament.command.toggle.no-property-found": "Could not find property %s",
|
"firmament.command.toggle.no-property-found": "Could not find property %s",
|
||||||
"firmament.command.toggle.not-a-toggle": "Property %s is not a toggle",
|
"firmament.command.toggle.not-a-toggle": "Property %s is not a toggle",
|
||||||
"firmament.command.toggle.toggled": "Toggled %s / %s %s",
|
"firmament.command.toggle.toggled": "Toggled %s / %s %s",
|
||||||
|
"firmament.pristine-profit.collection": "Collection: %s/h",
|
||||||
|
"firmament.pristine-profit.money": "Money: %s/h",
|
||||||
"firmament.toggle.true": "On",
|
"firmament.toggle.true": "On",
|
||||||
"firmament.toggle.false": "Off",
|
"firmament.toggle.false": "Off",
|
||||||
"firmament.config.developer": "Developer Settings",
|
"firmament.config.developer": "Developer Settings",
|
||||||
|
|||||||
Reference in New Issue
Block a user