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
|
/*
* Copyright (C) 2023-2024 NotEnoughUpdates contributors
*
* This file is part of NotEnoughUpdates.
*
* NotEnoughUpdates is free software: you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* NotEnoughUpdates is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.moulberry.notenoughupdates.util
import com.google.gson.JsonArray
import io.github.moulberry.notenoughupdates.NotEnoughUpdates
import io.github.moulberry.notenoughupdates.miscfeatures.tablisttutorial.TablistAPI
import net.minecraft.util.EnumChatFormatting
import java.util.regex.Pattern
import kotlin.math.abs
object TabSkillInfoParser {
private val skillTabPattern: Pattern =
Pattern.compile("^§r (?<type>\\w+) (?<level>\\d+): §r§a(?<progress>.+)%§r\$")
private val maxSkillTabPattern: Pattern =
Pattern.compile("^§r (?<type>\\w+) (?<level>\\d+): §r§c§lMAX§r\$")
private var sentErrorOnce = false
private fun calculateLevelXp(levelingArray: JsonArray, level: Int): Double {
var totalXp = 0.0
for (i in 0 until level + 1) {
val xp = levelingArray[i].asDouble
totalXp += xp
}
return totalXp
}
private fun isWithinPercentageRange(xp: Double, existingXp: Double, percentage: Double): Boolean {
val diff = (abs(xp - existingXp) / existingXp) * 100
return diff <= percentage
}
private fun sendError(message: String) {
if (!sentErrorOnce) {
Utils.addChatMessage(message)
sentErrorOnce = true
}
}
private fun levelArray(skillType: String) =
if (skillType == "runecrafting") Utils.getElement(Constants.LEVELING, "runecrafting_xp").asJsonArray
else Utils.getElement(Constants.LEVELING, "leveling_xp").asJsonArray
@JvmStatic
fun parseSkillInfo() {
if (!NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) {
return
}
if (Constants.LEVELING == null) {
sendError("${EnumChatFormatting.RED}[NEU] There is an error with your repo, please report this in the discord at ${EnumChatFormatting.AQUA}discord.gg/moulberry")
return
}
for (s in TablistAPI.getOptionalWidgetLines(
TablistAPI.WidgetNames.SKILLS
)) {
val matcher = skillTabPattern.matcher(s)
val maxLevelMatcher = maxSkillTabPattern.matcher(s)
if (matcher.matches()) {
// All the groups are guaranteed to match
val name = matcher.group("type")!!.lowercase()
val level = matcher.group("level")!!.toInt()
val progress = matcher.group("progress")!!.toFloatOrNull()
if (progress == null) {
sendError("${EnumChatFormatting.RED}[NEU] Error while parsing skill level from tab list")
return
}
val levelingArray = levelArray(name)
val levelXp = calculateLevelXp(levelingArray, level - 1)
// This *should* not cause problems, since skills that are max Level won't be picked up
val nextLevelDiff = levelingArray[level].asDouble
val nextLevelProgress = nextLevelDiff * progress / 100
val totalXp = levelXp + nextLevelProgress
val existingLevel = XPInformation.getInstance().getSkillInfo(name, false) ?: XPInformation.SkillInfo()
// Only update if the numbers are substantially different
if (!isWithinPercentageRange(totalXp, existingLevel.totalXp.toDouble(), 1.0)) {
existingLevel.level = level
existingLevel.totalXp = totalXp
existingLevel.currentXp = nextLevelProgress
existingLevel.currentXpMax = nextLevelDiff
XPInformation.getInstance().skillInfoMap[name] = existingLevel
}
// There is only one skill at a time in the tab list
break
} else if (maxLevelMatcher.matches()) {
val name = maxLevelMatcher.group("type")!!.lowercase()
val level = maxLevelMatcher.group("level")!!.toInt()
val existingLevel = XPInformation.getInstance().getSkillInfo(name, false) ?: XPInformation.SkillInfo()
if (existingLevel.level != level) {
existingLevel.level = level
val levelingArray = levelArray(name)
val totalXp = calculateLevelXp(levelingArray, level - 1)
existingLevel.totalXp = totalXp
XPInformation.getInstance().skillInfoMap[name] = existingLevel
}
}
}
}
}
|