aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/features/garden/GardenLevelDisplay.kt
blob: 1067312de45bd01a4b1cac7fd45ac7aa1fadfc96 (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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
package at.hannibal2.skyhanni.features.garden

import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
import at.hannibal2.skyhanni.data.ProfileStorageData
import at.hannibal2.skyhanni.events.ConfigLoadEvent
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.events.LorenzToolTipEvent
import at.hannibal2.skyhanni.events.ProfileJoinEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.ConditionalUtils
import at.hannibal2.skyhanni.utils.DelayedRun
import at.hannibal2.skyhanni.utils.HypixelCommands
import at.hannibal2.skyhanni.utils.InventoryUtils
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
import at.hannibal2.skyhanni.utils.ItemUtils.name
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.NumberUtil.format
import at.hannibal2.skyhanni.utils.NumberUtil.formatLong
import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNecessary
import at.hannibal2.skyhanni.utils.NumberUtil.toRoman
import at.hannibal2.skyhanni.utils.RegexUtils.groupOrNull
import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
import at.hannibal2.skyhanni.utils.RegexUtils.matches
import at.hannibal2.skyhanni.utils.RenderUtils.renderString
import at.hannibal2.skyhanni.utils.StringUtils
import at.hannibal2.skyhanni.utils.StringUtils.isRoman
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import kotlin.time.Duration.Companion.milliseconds

@SkyHanniModule
object GardenLevelDisplay {

    private val config get() = GardenAPI.config.gardenLevels
    private var useRomanNumerals: Boolean
        get() = ProfileStorageData.playerSpecific?.useRomanNumerals ?: true
        set(value) {
            ProfileStorageData.playerSpecific?.useRomanNumerals = value
        }

    private val patternGroup = RepoPattern.group("garden.level")
    private val expToNextLevelPattern by patternGroup.pattern(
        "inventory.nextxp",
        ".* §e(?<nextLevelExp>.*)§6/.*"
    )
    private val gardenItemNamePattern by patternGroup.pattern(
        "inventory.name",
        "Garden (?:Desk|Level (?<currentLevel>.*))"
    )
    private val overflowPattern by patternGroup.pattern(
        "inventory.overflow",
        ".*§r §6(?<overflow>.*)"
    )
    private val gardenLevelPattern by patternGroup.pattern(
        "inventory.levelprogress",
        "§7Progress to Level (?<currentLevel>[^:]*).*"
    )
    private val gardenMaxLevelPattern by patternGroup.pattern(
        "inventory.max",
        "§5§o§7§8Max level reached!"
    )
    private val visitorRewardPattern by patternGroup.pattern(
        "chat.increase",
        " {4}§r§8\\+§r§2(?<exp>.*) §r§7Garden Experience"
    )

    private var display = ""

    @SubscribeEvent
    fun onProfileJoin(event: ProfileJoinEvent) {
        update()
    }

    @SubscribeEvent(receiveCanceled = true)
    fun onChat(event: LorenzChatEvent) {
        if (!GardenAPI.inGarden()) return

        visitorRewardPattern.matchMatcher(event.message) {
            addExp(group("exp").toInt())
        }
    }

    private fun addExp(moreExp: Int) {
        val gardenExp = GardenAPI.gardenExp ?: return
        val oldLevel = GardenAPI.getGardenLevel()
        GardenAPI.gardenExp = gardenExp + moreExp
        update()

        if (!config.overflowChat) return
        val newLevel = GardenAPI.getGardenLevel()
        if (newLevel != oldLevel + 1 || newLevel <= 15) return
        DelayedRun.runDelayed(50.milliseconds) {
            // TODO utils function that is shared with Crop Milestone Display
            ChatUtils.clickableChat(
                " \n§b§lGARDEN LEVEL UP §8$oldLevel ➜ §b$newLevel\n" +
                    " §8+§aRespect from Elite Farmers and SkyHanni members :)\n ",
                onClick = {
                    HypixelCommands.gardenLevels()
                },
                prefix = false
            )

        }
    }

    @SubscribeEvent
    fun onInventoryOpen(event: InventoryFullyOpenedEvent) {
        if (!GardenAPI.inGarden()) return
        val item = when (event.inventoryName) {
            "Desk" -> event.inventoryItems[4] ?: return
            "SkyBlock Menu" -> event.inventoryItems[10] ?: return
            else -> return
        }
        gardenItemNamePattern.matchMatcher(item.name.removeColor()) {
            val level = groupOrNull("currentLevel")
            if (level != null) useRomanNumerals = level.isRoman()
        } ?: return
        var nextLevelExp = 0L
        var currentLevel = 0
        for (line in item.getLore()) {
            gardenLevelPattern.matchMatcher(line) {
                currentLevel = group("currentLevel").romanToDecimalIfNecessary() - 1
            }
            if (line == "§7§8Max level reached!") currentLevel = 15
            expToNextLevelPattern.matchMatcher(line) {
                nextLevelExp = group("nextLevelExp").formatLong()
            }
            overflowPattern.matchMatcher(line) {
                val overflow = group("overflow").formatLong()
                GardenAPI.gardenExp = overflow
                update()
                return
            }
        }
        val expForLevel = GardenAPI.getExpForLevel(currentLevel).toInt()
        GardenAPI.gardenExp = expForLevel + nextLevelExp
        update()
    }

    @SubscribeEvent
    fun onTooltip(event: LorenzToolTipEvent) {
        if (!GardenAPI.inGarden()) return
        if (!config.overflow.get()) return
        val slotIndex = event.slot.slotIndex
        val name = InventoryUtils.openInventoryName()
        if (!((name == "Desk" && slotIndex == 4) || (name == "SkyBlock Menu" && slotIndex == 10))) return

        val gardenExp = GardenAPI.gardenExp ?: return
        val currentLevel = GardenAPI.getGardenLevel()
        if (currentLevel < 15) return

        val needForLevel = GardenAPI.getExpForLevel(currentLevel).toInt()
        val overflow = (gardenExp - needForLevel).toDouble()
        val overflowTotal = (gardenExp - GardenAPI.getExpForLevel(15)).toInt()
        val needForNextLevel = GardenAPI.getExpForLevel(currentLevel + 1).toInt()
        val needForOnlyNextLvl = needForNextLevel - needForLevel

        val iterator = event.toolTip.listIterator()
        if (slotIndex == 4 && currentLevel > 15) {
            event.itemStack.name = "§aGarden Level ${currentLevel.toRomanIfNecessary()}"
        }
        var next = false
        for (line in iterator) {
            if (gardenMaxLevelPattern.matches(line)) {
                iterator.set("§7Progress to Level ${(currentLevel + 1).toRomanIfNecessary()}")
                next = true
                continue
            }
            if (next && line.contains("                    ")) {
                val progress = overflow / needForOnlyNextLvl
                val progressBar = StringUtils.progressBar(progress, 20)
                iterator.set("$progressBar §e${overflow.addSeparators()}§6/§e${format(needForOnlyNextLvl)}")
                iterator.add("")
                iterator.add("§b§lOVERFLOW XP:")
                iterator.add("§7▸ ${overflowTotal.addSeparators()}")
                return
            }
        }
    }

    private fun update() {
        display = drawDisplay()
    }

    private fun drawDisplay(): String {
        val gardenExp = GardenAPI.gardenExp ?: return "§aGarden Level ? §cOpen the desk!"
        val currentLevel = GardenAPI.getGardenLevel(overflow = config.overflow.get())
        val isMax = !config.overflow.get() && currentLevel == 15
        val needForLevel = GardenAPI.getExpForLevel(currentLevel).toInt()
        val overflow = gardenExp - needForLevel

        return "§aGarden level $currentLevel " + if (isMax) {
            "§7(§e${overflow.addSeparators()}§7)"
        } else {
            val needForNextLevel = GardenAPI.getExpForLevel(currentLevel + 1).toInt()
            val needForOnlyNextLevel = needForNextLevel - needForLevel
            "§7(§e${overflow.addSeparators()}§7/§e${needForOnlyNextLevel.addSeparators()}§7)"
        }
    }

    private fun Int.toRomanIfNecessary(): String {
        return if (useRomanNumerals) this.toRoman() else this.toString()
    }

    @SubscribeEvent
    fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) {
        if (!isEnabled()) return
        if (GardenAPI.hideExtraGuis()) return

        config.pos.renderString(display, posLabel = "Garden Level")
    }

    @SubscribeEvent
    fun onConfigLoad(event: ConfigLoadEvent) {
        ConditionalUtils.onToggle(config.overflow) { update() }
    }

    private fun isEnabled() = GardenAPI.inGarden() && config.display

    @SubscribeEvent
    fun onConfigFix(event: