aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/moe/nea/firmament/features/mining/PristineProfitTracker.kt
blob: 294c835c03dc33a70ad1be3c4c3e310347ec7df6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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()
            }
        }
    }
}