aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/api/SkillAPI.kt449
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/Features.java5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/Storage.java5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt31
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/AllSkillDisplayConfig.java37
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/CustomGoalConfig.java39
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillETADisplayConfig.java44
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillOverflowConfig.java46
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillProgressBarConfig.java107
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillProgressConfig.java117
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/ActionBarData.kt7
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/ActionBarUpdateEvent.kt9
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/SkillOverflowLevelupEvent.kt5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShaderManager.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/chroma/StandardChromaShader.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/chroma/TexturedChromaShader.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/ItemDisplayOverlayFeatures.kt17
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/minion/MinionXp.kt34
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/RoundedRectangleShader.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillProgress.kt472
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillTooltip.kt85
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillType.kt36
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillUtil.kt140
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt10
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt47
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt15
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/Quad.kt31
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt113
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt8
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt10
36 files changed, 1863 insertions, 90 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
index de14fd4e4..ac02e25c1 100644
--- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
+++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
@@ -2,6 +2,7 @@ package at.hannibal2.skyhanni
import at.hannibal2.skyhanni.api.CollectionAPI
import at.hannibal2.skyhanni.api.DataWatcherAPI
+import at.hannibal2.skyhanni.api.SkillAPI
import at.hannibal2.skyhanni.api.GetFromSackAPI
import at.hannibal2.skyhanni.config.ConfigFileType
import at.hannibal2.skyhanni.config.ConfigManager
@@ -284,6 +285,8 @@ import at.hannibal2.skyhanni.features.misc.items.EstimatedItemValue
import at.hannibal2.skyhanni.features.misc.items.EstimatedWardrobePrice
import at.hannibal2.skyhanni.features.misc.items.GlowingDroppedItems
import at.hannibal2.skyhanni.features.misc.massconfiguration.DefaultConfigFeatures
+import at.hannibal2.skyhanni.features.skillprogress.SkillTooltip
+import at.hannibal2.skyhanni.features.skillprogress.SkillProgress
import at.hannibal2.skyhanni.features.misc.teleportpad.TeleportPadCompactName
import at.hannibal2.skyhanni.features.misc.teleportpad.TeleportPadInventoryNumber
import at.hannibal2.skyhanni.features.misc.trevor.TrevorFeatures
@@ -473,6 +476,7 @@ class SkyHanniMod {
loadModule(SackAPI)
loadModule(BingoAPI)
loadModule(FishingAPI)
+ loadModule(SkillAPI)
loadModule(IsFishingDetection)
loadModule(LorenzUtils)
loadModule(NEUItems)
@@ -747,6 +751,8 @@ class SkyHanniMod {
loadModule(SulphurSkitterBox())
loadModule(HighlightInquisitors())
loadModule(VerminTracker)
+ loadModule(SkillProgress)
+ loadModule(SkillTooltip())
loadModule(QuiverNotification)
init()
diff --git a/src/main/java/at/hannibal2/skyhanni/api/SkillAPI.kt b/src/main/java/at/hannibal2/skyhanni/api/SkillAPI.kt
new file mode 100644
index 000000000..63b9236de
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/api/SkillAPI.kt
@@ -0,0 +1,449 @@
+package at.hannibal2.skyhanni.api
+
+import at.hannibal2.skyhanni.data.ProfileStorageData
+import at.hannibal2.skyhanni.events.ActionBarUpdateEvent
+import at.hannibal2.skyhanni.events.ConfigLoadEvent
+import at.hannibal2.skyhanni.events.DebugDataCollectEvent
+import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
+import at.hannibal2.skyhanni.events.SkillOverflowLevelupEvent
+import at.hannibal2.skyhanni.features.skillprogress.SkillProgress
+import at.hannibal2.skyhanni.features.skillprogress.SkillType
+import at.hannibal2.skyhanni.features.skillprogress.SkillUtil.SPACE_SPLITTER
+import at.hannibal2.skyhanni.features.skillprogress.SkillUtil.XP_NEEDED_FOR_60
+import at.hannibal2.skyhanni.features.skillprogress.SkillUtil.calculateLevelXp
+import at.hannibal2.skyhanni.features.skillprogress.SkillUtil.calculateOverFlow
+import at.hannibal2.skyhanni.features.skillprogress.SkillUtil.getLevel
+import at.hannibal2.skyhanni.features.skillprogress.SkillUtil.getLevelExact
+import at.hannibal2.skyhanni.features.skillprogress.SkillUtil.getSkillInfo
+import at.hannibal2.skyhanni.features.skillprogress.SkillUtil.levelArray
+import at.hannibal2.skyhanni.features.skillprogress.SkillUtil.xpRequiredForLevel
+import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.ChatUtils.userError
+import at.hannibal2.skyhanni.utils.ItemUtils.cleanName
+import at.hannibal2.skyhanni.utils.ItemUtils.getLore
+import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
+import at.hannibal2.skyhanni.utils.NumberUtil.formatNumber
+import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNecessary
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import at.hannibal2.skyhanni.utils.TabListData
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
+import com.google.gson.GsonBuilder
+import com.google.gson.annotations.Expose
+import com.google.gson.reflect.TypeToken
+import io.github.moulberry.notenoughupdates.util.Constants
+import io.github.moulberry.notenoughupdates.util.Utils
+import net.minecraft.command.CommandBase
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.LinkedList
+import java.util.regex.Matcher
+import kotlin.concurrent.fixedRateTimer
+import kotlin.time.Duration.Companion.seconds
+
+object SkillAPI {
+ private val patternGroup = RepoPattern.group("api.skilldisplay")
+ private val skillPercentPattern by patternGroup.pattern(
+ "skill.percent",
+ "\\+(?<gained>[\\d.,]+) (?<skillName>.+) \\((?<progress>[\\d.]+)%\\)"
+ )
+ private val skillPattern by patternGroup.pattern(
+ "skill",
+ "\\+(?<gained>[\\d.,]+) (?<skillName>\\w+) \\((?<current>[\\d.,]+)/(?<needed>[\\d.,]+)\\)"
+ )
+ private val skillMultiplierPattern by patternGroup.pattern(
+ "skill.multiplier",
+ "\\+(?<gained>[\\d.,]+) (?<skillName>.+) \\((?<current>[\\d.,]+)/(?<needed>[\\d,.]+[kmb])\\)"
+ )
+ private val skillTabPattern by patternGroup.pattern(
+ "skill.tab",
+ "^§e§lSkills: §r§a(?<type>\\w+) (?<level>\\d+): §r§3(?<progress>.+)%\$"
+ )
+ private val maxSkillTabPattern by patternGroup.pattern(
+ "skill.tab.max",
+ "^§e§lSkills: §r§a(?<type>\\w+) (?<level>\\d+): §r§c§lMAX\$"
+ )
+
+ var skillXPInfoMap = mutableMapOf<SkillType, SkillXPInfo>()
+ var oldSkillInfoMap = mutableMapOf<SkillType?, SkillInfo?>()
+ val storage get() = ProfileStorageData.profileSpecific?.skillData
+ var exactLevelingMap = mapOf<Int, Int>()
+ var levelingMap = mapOf<Int, Int>()
+ var activeSkill: SkillType? = null
+
+ // TODO Use a map maxSkillLevel and move it into the repo
+ val excludedSkills = listOf(
+ SkillType.FORAGING,
+ SkillType.FISHING,
+ SkillType.ALCHEMY,
+ SkillType.CARPENTRY
+ )
+ var showDisplay = false
+ var lastUpdate = SimpleTimeMark.farPast()
+
+ @SubscribeEvent
+ fun onActionBar(event: ActionBarUpdateEvent) {
+ val actionBar = event.actionBar.removeColor()
+ val components = SPACE_SPLITTER.splitToList(actionBar)
+ for (component in components) {
+ val matcher = listOf(skillPattern, skillPercentPattern, skillMultiplierPattern)
+ .firstOrNull { it.matcher(component).matches() }
+ ?.matcher(component)
+
+ if (matcher?.matches() == true) {
+ val skillName = matcher.group("skillName")
+ val skillType = SkillType.getByNameOrNull(skillName) ?: return
+ val skillInfo = storage?.get(skillType) ?: SkillInfo()
+ val skillXp = skillXPInfoMap[skillType] ?: SkillXPInfo()
+ activeSkill = skillType
+ when (matcher.pattern()) {
+ skillPattern -> handleSkillPattern(matcher, skillType, skillInfo)
+ skillPercentPattern -> handleSkillPatternPercent(matcher, skillType)
+ skillMultiplierPattern -> handleSkillPatternMultiplier(matcher, skillType, skillInfo)
+ }
+ showDisplay = true
+ lastUpdate = SimpleTimeMark.now()
+ skillXp.lastUpdate = SimpleTimeMark.now()
+ skillXp.sessionTimerActive = true
+
+ if (skillXp.shouldStartTimer) {
+ runTimer(skillName, skillXp)
+ skillXp.shouldStartTimer = false
+ }
+ SkillProgress.updateDisplay()
+ SkillProgress.hideInActionBar = listOf(component)
+ return
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onConfigLoad(event: ConfigLoadEvent) {
+ val gson = GsonBuilder().create()
+ val xpList: List<Int> = gson.fromJson(
+ Utils.getElement(Constants.LEVELING, "leveling_xp").asJsonArray.toString(),
+ object : TypeToken<List<Int>>() {}.type
+ )
+ levelingMap = xpList.withIndex().associate { (index, xp) -> index to xp }
+ exactLevelingMap = xpList.withIndex().associate { (index, xp) -> xp to index }
+ }
+
+ @SubscribeEvent
+ fun onInventoryOpen(event: InventoryFullyOpenedEvent) {
+ val inventoryName = event.inventoryName
+ for (stack in event.inventoryItems.values) {
+ val lore = stack.getLore()
+ if (inventoryName == "Your Skills" &&
+ lore.any { it.contains("Click to view!") || it.contains("Not unlocked!") }
+ ) {
+ val cleanName = stack.cleanName()
+ val split = cleanName.split(" ")
+ val skillName = split.first()
+ val skill = SkillType.getByNameOrNull(skillName) ?: continue
+ val skillLevel = if (split.size > 1) split.last().romanToDecimalIfNecessary() else 0
+ val skillInfo = storage?.getOrPut(skill) { SkillInfo() }
+
+ for ((lineIndex, line) in lore.withIndex()) {
+ val cleanLine = line.removeColor()
+ if (!cleanLine.startsWith(" ")) continue
+ val previousLine = stack.getLore()[lineIndex - 1]
+ val progress = cleanLine.substring(cleanLine.lastIndexOf(' ') + 1)
+ if (previousLine == "§7§8Max Skill level reached!") {
+ var totalXp = progress.formatNumber()
+ val minus = if (skillLevel == 50) 4_000_000 else if (skillLevel == 60) 7_000_000 else 0
+ totalXp -= minus
+ val (overflowLevel, overflowCurrent, overflowNeeded, overflowTotal) = getSkillInfo(skillLevel, totalXp, 0L, totalXp)
+ skillInfo?.apply {
+ this.overflowLevel = overflowLevel
+ this.overflowCurrentXp = overflowCurrent
+ this.overflowCurrentXpMax = overflowNeeded
+ this.overflowTotalXp = overflowTotal
+
+ this.totalXp = totalXp
+ this.level = skillLevel
+ this.currentXp = totalXp
+ this.currentXpMax = 0L
+ }
+ } else {
+ val splitProgress = progress.split("/")
+ val currentXp = splitProgress.first().formatNumber()
+ val neededXp = splitProgress.last().formatNumber()
+ val levelingArray = levelArray()
+ val levelXp = calculateLevelXp(levelingArray, skillLevel - 1).toLong()
+
+ skillInfo?.apply {
+ this.currentXp = currentXp
+ this.level = skillLevel
+ this.currentXpMax = neededXp
+ this.totalXp = levelXp + currentXp
+
+ this.overflowCurrentXp = currentXp
+ this.overflowLevel = skillLevel
+ this.overflowCurrentXpMax = neededXp
+ this.overflowTotalXp = levelXp + currentXp
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onDebugDataCollect(event: DebugDataCollectEvent) {
+ event.title("Skills")
+ val storage = storage
+ if (storage == null) {
+ event.addIrrelevant("SkillMap is empty")
+ return
+ }
+ event.addData {
+ for ((skillName, skillInfo) in storage) {
+ add("- Name: $skillName")
+ add("- Level: ${skillInfo.level}")
+ add("- CurrentXp: ${skillInfo.currentXp}")
+ add("- CurrentXpMax: ${skillInfo.currentXpMax}")
+ add("- TotalXp: ${skillInfo.totalXp}")
+ add("- OverflowLevel: ${skillInfo.overflowLevel}")
+ add("- OverflowCurrentXp: ${skillInfo.overflowCurrentXp}")
+ add("- OverflowCurrentXpMax: ${skillInfo.overflowCurrentXpMax}")
+ add("- OverflowTotalXp: ${skillInfo.overflowTotalXp}")
+ add("- CustomGoalLevel: ${skillInfo.customGoalLevel}\n")
+ }
+
+ }
+ }
+
+ private fun runTimer(skillName: String, info: SkillXPInfo) {
+ fixedRateTimer(name = "skyhanni-skillprogress-timer-$skillName", initialDelay = 1_000L, period = 1_000L) {
+ if (info.shouldStartTimer) cancel()
+ val time = when (activeSkill) {
+ SkillType.FARMING -> SkillProgress.etaConfig.farmingPauseTime
+ SkillType.MINING -> SkillProgress.etaConfig.miningPauseTime
+ SkillType.COMBAT -> SkillProgress.etaConfig.combatPauseTime
+ SkillType.FORAGING -> SkillProgress.etaConfig.foragingPauseTime
+ SkillType.FISHING -> SkillProgress.etaConfig.fishingPauseTime
+ else -> 0
+ }
+ if (info.lastUpdate.passedSince() > time.seconds) {
+ info.sessionTimerActive = false
+ }
+ if (info.sessionTimerActive) {
+ info.timeActive++
+ }
+ }
+ }
+
+ private fun handleSkillPattern(matcher: Matcher, skillType: SkillType, skillInfo: SkillInfo) {
+ val currentXp = matcher.group("current").formatNumber()
+ val maxXp = matcher.group("needed").formatNumber()
+ val level = getLevelExact(maxXp)
+
+ val (levelOverflow, currentOverflow, currentMaxOverflow, totalOverflow) = getSkillInfo(level, currentXp, maxXp, currentXp)
+ if (skillInfo.overflowLevel != 0 && levelOverflow == skillInfo.overflowLevel + 1)
+ SkillOverflowLevelupEvent(skillType, skillInfo.overflowLevel, levelOverflow).postAndCatch()
+
+ skillInfo.apply {
+ this.level = level
+ this.currentXp = currentXp
+ this.currentXpMax = maxXp
+ this.totalXp = currentXp
+
+ this.overflowLevel = levelOverflow
+ this.overflowCurrentXp = currentOverflow
+ this.overflowCurrentXpMax = currentMaxOverflow
+ this.overflowTotalXp = totalOverflow
+
+ this.lastGain = matcher.group("gained")
+ }
+ storage?.set(skillType, skillInfo)
+ }
+
+ private fun handleSkillPatternPercent(matcher: Matcher, skillType: SkillType) {
+ var tablistLevel = 0
+ for (line in TabListData.getTabList()) {
+ var levelMatcher = skillTabPattern.matcher(line)
+ if (levelMatcher.matches()) {
+ tablistLevel = levelMatcher.group("level").toInt()
+ if (levelMatcher.group("type").lowercase() != activeSkill?.lowercaseName) tablistLevel = 0
+ } else {
+ levelMatcher = maxSkillTabPattern.matcher(line)
+ if (levelMatcher.matches()) {
+ tablistLevel = levelMatcher.group("level").toInt()
+ if (levelMatcher.group("type").lowercase() != activeSkill?.lowercaseName) tablistLevel = 0
+ }
+ }
+ }
+ val existingLevel = getSkillInfo(skillType) ?: SkillInfo()
+ val xpPercentageS = matcher.group("progress").replace(",", "")
+ val xpPercentage = xpPercentageS.toFloatOrNull() ?: return
+ val levelingArray = levelArray()
+ val levelXp = calculateLevelXp(levelingArray, existingLevel.level - 1)
+ val nextLevelDiff = levelingArray[tablistLevel]?.asDouble ?: 7_600_000.0
+ val nextLevelProgress = nextLevelDiff * xpPercentage / 100
+ val totalXp = levelXp + nextLevelProgress
+ val (_, currentOverflow, currentMaxOverflow, totalOverflow) = getSkillInfo(tablistLevel, nextLevelProgress.toLong(), nextLevelDiff.toLong(), totalXp.toLong())
+ existingLevel.apply {
+ this.totalXp = totalXp.toLong()
+ this.currentXp = nextLevelProgress.toLong()
+ this.currentXpMax = nextLevelDiff.toLong()
+ this.level = tablistLevel
+
+ this.overflowTotalXp = totalOverflow
+ this.overflowCurrentXp = currentOverflow
+ this.overflowCurrentXpMax = currentMaxOverflow
+ this.overflowLevel = tablistLevel
+
+ this.lastGain = matcher.group("gained")
+ }
+ storage?.set(skillType, existingLevel)
+ }
+
+ private fun handleSkillPatternMultiplier(matcher: Matcher, skillType: SkillType, skillInfo: SkillInfo) {
+ val currentXp = matcher.group("current").formatNumber()
+ val maxXp = matcher.group("needed").formatNumber()
+ val level = getLevelExact(maxXp)
+ val levelingArray = levelArray()
+ val levelXp = calculateLevelXp(levelingArray, level - 1).toLong() + currentXp
+ val (currentLevel, currentOverflow, currentMaxOverflow, totalOverflow) = getSkillInfo(level, currentXp, maxXp, levelXp)
+ skillInfo.apply {
+ this.overflowCurrentXp = currentOverflow
+ this.overflowCurrentXpMax = currentMaxOverflow
+ this.overflowTotalXp = totalOverflow
+ this.overflowLevel = currentLevel
+
+ this.currentXp = currentXp
+ this.currentXpMax = maxXp
+ this.totalXp = levelXp
+ this.level = level
+
+ this.lastGain = matcher.group("gained")
+ }
+ storage?.set(skillType, skillInfo)
+ }
+
+ fun onCommand(it: Array<String>) {
+ if (it.isEmpty()) {
+ commandHelp()
+ return
+ }
+
+ val first = it.first()
+ if (it.size == 1) {
+ when (first) {
+ "skillgoal" -> {
+ ChatUtils.chat("§bSkill Custom Goal Level")
+ if (storage?.isEmpty() == true) {
+ ChatUtils.chat("§cYou haven't set any custom goal yet!")
+ }
+ storage?.filter { it.value.customGoalLevel != 0 }?.forEach { (skill, data) ->
+ ChatUtils.chat("§e${skill.displayName}: §b${data.customGoalLevel}")
+ }
+ }
+ }
+ }
+
+ if (it.size == 2) {
+ val second = it[1]
+ when (first) {
+ "levelwithxp" -> {
+ val xp = second.toLong()
+ if (xp <= XP_NEEDED_FOR_60) {
+ val level = getLevel(xp)
+ ChatUtils.chat("With §b${xp.addSeparators()} §eXP you would be level §b$level")
+ } else {
+ val (overflowLevel, current, needed, _) = calculateOverFlow(second.toLong())
+ ChatUtils.chat("With §b${xp.addSeparators()} §eXP you would be level §b$overflowLevel " +
+ "§ewith progress (§b${current.addSeparators()}§e/§b${needed.addSeparators()}§e) XP")
+ }
+ }
+
+ "xpforlevel" -> {
+ val level = second.toInt()
+ if (level <= 60) {
+ val neededXp = levelingMap.filter { it.key < level }.values.sum().toLong()
+ ChatUtils.chat("You need §b${neededXp.addSeparators()} §eXP to be level §b${level.toDouble()}")
+ } else {
+ val base = levelingMap.values.sum().toLong()
+ val neededXP = xpRequiredForLevel(level.toDouble()) + base
+ ChatUtils.chat("You need §b${neededXP.addSeparators()} §eXP to be level §b${level.toDouble()}")
+ }
+
+ }
+ }
+ }
+ if (it.size == 3) {
+ when (first) {
+ "skillgoal" -> {
+ val rawSkill = it[1].lowercase()
+ val skillType = SkillType.getByNameOrNull(rawSkill)
+ if (skillType == null) {
+ userError("Unknown Skill type: $rawSkill")
+ return
+ }
+ val rawLevel = it[2]
+ val targetLevel = rawLevel.toIntOrNull()
+ if (targetLevel == null) {
+ userError("$rawLevel is not a valid number.")
+ return
+ }
+ val skill = storage?.get(skillType) ?: return
+
+ if (targetLevel <= skill.overflowLevel && targetLevel != 0) {
+ userError("Custom goal level ($targetLevel) must be greater than your current level (${skill.overflowLevel}).")
+ return
+ }
+
+ skill.customGoalLevel = targetLevel
+ ChatUtils.chat("Custom goal level for §b${skillType.displayName} §eset to §b$targetLevel")
+ }
+ }
+ }
+ }
+
+ fun onComplete(strings: Array<String>): List<String> {
+ return when (strings.size) {
+ 1 -> listOf("levelwithxp", "xpforlevel", "skillgoal")
+ 2 -> if (strings[0].lowercase() == "skillgoal") CommandBase.getListOfStringsMatchingLastWord(
+ strings,
+ SkillType.entries.map { it.displayName })
+ else
+ listOf()
+
+ else -> listOf()
+ }
+ }
+
+ private fun commandHelp() {
+ ChatUtils.chat("", false)
+ ChatUtils.chat("§6/shskills levelwithxp <currentXP> - §bGet a level with the given current XP.", false)
+ ChatUtils.chat("§6/shskills xpforlevel <desiredLevel> - §bGet how much XP you need for a desired level.", false)
+ ChatUtils.chat("§6/shskills skillgoal - §bView your current goal", false)
+ ChatUtils.chat("§6/shskills skillgoal <skill> <level> - §bDefine your goal for <skill>", false)
+ ChatUtils.chat("", false)
+ }
+
+ data class SkillInfo(
+ @Expose var level: Int = 0,
+ @Expose var totalXp: Long = 0,
+ @Expose var currentXp: Long = 0,
+ @Expose var currentXpMax: Long = 0,
+ @Expose var overflowLevel: Int = 0,
+ @Expose var overflowCurrentXp: Long = 0,
+ @Expose var overflowTotalXp: Long = 0,
+ @Expose var overflowCurrentXpMax: Long = 0,
+ @Expose var lastGain: String = "",
+ @Expose var customGoalLevel: Int = 0,
+ )
+
+ data class SkillXPInfo(
+ var lastTotalXp: Float = 0f,
+ var xpGainQueue: LinkedList<Float> = LinkedList(),
+ var xpGainHour: Float = 0f,
+ var xpGainLast: Float = 0f,
+ var timer: Int = 3,
+ var sessionTimerActive: Boolean = false,
+ var isActive: Boolean = false,
+ var lastUpdate: SimpleTimeMark = SimpleTimeMark.farPast(),
+ var timeActive: Long = 0L,
+ var shouldStartTimer: Boolean = true,
+ )
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/Features.java b/src/main/java/at/hannibal2/skyhanni/config/Features.java
index ded615520..af464f366 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/Features.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/Features.java
@@ -21,6 +21,7 @@ import at.hannibal2.skyhanni.config.features.mining.MiningConfig;
import at.hannibal2.skyhanni.config.features.minion.MinionsConfig;
import at.hannibal2.skyhanni.config.features.misc.MiscConfig;
import at.hannibal2.skyhanni.config.features.rift.RiftConfig;
+import at.hannibal2.skyhanni.config.features.skillprogress.SkillProgressConfig;
import at.hannibal2.skyhanni.config.features.slayer.SlayerConfig;
import at.hannibal2.skyhanni.config.features.stranded.StrandedConfig;
import com.google.gson.annotations.Expose;
@@ -137,6 +138,10 @@ public class Features extends Config {
public MiningConfig mining = new MiningConfig();
@Expose
+ @Category(name = "Skill Progress", desc = "Skill Progress related config options.")
+ public SkillProgressConfig skillProgress = new SkillProgressConfig();
+
+ @Expose
@Category(name = "Slayer", desc = "Slayer features.")
public SlayerConfig slayer = new SlayerConfig();
diff --git a/src/main/java/at/hannibal2/skyhanni/config/Storage.java b/src/main/java/at/hannibal2/skyhanni/config/Storage.java
index 77b92131b..fe303a17e 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/Storage.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/Storage.java
@@ -1,5 +1,6 @@
package at.hannibal2.skyhanni.config;
+import at.hannibal2.skyhanni.api.SkillAPI;
import at.hannibal2.skyhanni.data.model.ComposterUpgrade;
import at.hannibal2.skyhanni.features.bingo.card.goals.BingoGoal;
import at.hannibal2.skyhanni.features.combat.endernodetracker.EnderNodeTracker;
@@ -23,6 +24,7 @@ import at.hannibal2.skyhanni.features.misc.trevor.TrevorTracker;
import at.hannibal2.skyhanni.features.misc.visualwords.VisualWord;
import at.hannibal2.skyhanni.features.rift.area.westvillage.VerminTracker;
import at.hannibal2.skyhanni.features.rift.area.westvillage.kloon.KloonTerminal;
+import at.hannibal2.skyhanni.features.skillprogress.SkillType;
import at.hannibal2.skyhanni.features.slayer.SlayerProfitTracker;
import at.hannibal2.skyhanni.utils.LorenzVec;
import at.hannibal2.skyhanni.utils.NEUInternalName;
@@ -471,5 +473,8 @@ public class Storage {
// TODO renmae
public MythologicalCreatureTracker.Data mythologicalMobTracker = new MythologicalCreatureTracker.Data();
}
+
+ @Expose
+ public Map<SkillType, SkillAPI.SkillInfo> skillData = new HashMap<>();
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
index 680834317..ee89b5720 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
+++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
@@ -1,6 +1,7 @@
package at.hannibal2.skyhanni.config.commands
import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.api.SkillAPI
import at.hannibal2.skyhanni.config.ConfigFileType
import at.hannibal2.skyhanni.config.ConfigGuiManager
import at.hannibal2.skyhanni.data.ChatManager
@@ -14,6 +15,7 @@ import at.hannibal2.skyhanni.features.chat.Translator
import at.hannibal2.skyhanni.features.combat.endernodetracker.EnderNodeTracker
import at.hannibal2.skyhanni.features.combat.ghostcounter.GhostUtil
import at.hannibal2.skyhanni.features.commands.PartyCommands
+import at.hannibal2.skyhanni.features.commands.WikiManager
import at.hannibal2.skyhanni.features.event.diana.AllBurrowsList
import at.hannibal2.skyhanni.features.event.diana.BurrowWarpHelper
import at.hannibal2.skyhanni.features.event.diana.DianaProfitTracker
@@ -240,6 +242,33 @@ object Commands {
"shresetseacreaturetracker",
"Resets the Sea Creature Tracker"
) { SeaCreatureTracker.resetCommand(it) }
+ registerCommand(
+ "shfandomwiki",
+ "Searches the fandom wiki with SkyHanni's own method."
+ ) { WikiManager.otherWikiCommands(it, true) }
+ registerCommand(
+ "shfandomwikithis",
+ "Searches the fandom wiki with SkyHanni's own method."
+ ) { WikiManager.otherWikiCommands(it, true, true) }
+ registerCommand(
+ "shofficialwiki",
+ "Searches the official wiki with SkyHanni's own method."
+ ) { WikiManager.otherWikiCommands(it, false) }
+ registerCommand(
+ "shofficialwikithis",
+ "Searches the official wiki with SkyHanni's own method."
+ ) { WikiManager.otherWikiCommands(it, false, true) }
+ registerCommand0("shcalccrop", "Calculate how many crops need to be farmed between different crop milestones.", {
+ FarmingMilestoneCommand.onCommand(it.getOrNull(0), it.getOrNull(1), it.getOrNull(2), false)
+ }, FarmingMilestoneCommand::onComplete)
+ registerCommand0("shcalccroptime", "Calculate how long you need to farm crops between different crop milestones.", {
+ FarmingMilestoneCommand.onCommand(it.getOrNull(0), it.getOrNull(1), it.getOrNull(2), true)
+ }, FarmingMilestoneCommand::onComplete)
+ registerCommand0(
+ "shskills",
+ "Skills XP/Level related command",
+ { SkillAPI.onCommand(it) },
+ SkillAPI::onComplete)
registerCommand0(
"shcalccrop",
"Calculate how many crops need to be farmed between different crop milestones.",
@@ -549,4 +578,4 @@ object Commands {
if (args != null) function(args.asList().toTypedArray())
}
}
-}
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/AllSkillDisplayConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/AllSkillDisplayConfig.java
new file mode 100644
index 000000000..081a022db
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/AllSkillDisplayConfig.java
@@ -0,0 +1,37 @@
+package at.hannibal2.skyhanni.config.features.skillprogress;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import at.hannibal2.skyhanni.features.skillprogress.SkillType;
+import com.google.gson.annotations.Expose;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorDraggableList;
+import io.github.moulberry.moulconfig.annotations.ConfigOption;
+import io.github.moulberry.moulconfig.observer.Property;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class AllSkillDisplayConfig {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Show a display with all skills progress.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public Property<Boolean> enabled = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "Text", desc = "Choose what skills you want to see in the display.")
+ @ConfigEditorDraggableList
+ public List<SkillType> skillEntryList = new ArrayList<>(Arrays.asList(
+ SkillType.COMBAT,
+ SkillType.FARMING,
+ SkillType.FISHING,
+ SkillType.MINING,
+ SkillType.FORAGING,
+ SkillType.ENCHANTING,
+ SkillType.ALCHEMY,
+ SkillType.CARPENTRY,
+ SkillType.TAMING
+ ));
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/CustomGoalConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/CustomGoalConfig.java
new file mode 100644
index 000000000..9d645c8c7
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/CustomGoalConfig.java
@@ -0,0 +1,39 @@
+package at.hannibal2.skyhanni.config.features.skillprogress;
+
+import com.google.gson.annotations.Expose;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.moulberry.moulconfig.annotations.ConfigOption;
+
+public class CustomGoalConfig {
+
+ @Expose
+ @ConfigOption(name = "Display", desc = "Enable the custom goal in the progress display.")
+ @ConfigEditorBoolean
+ public boolean enableInDisplay = true;
+
+ @Expose
+ @ConfigOption(name = "All Skill Display", desc = "Enable the custom goal in the all skill display.")
+ @ConfigEditorBoolean
+ public boolean enableInAllDisplay = false;
+
+ @Expose
+ @ConfigOption(name = "ETA Display", desc = "Enable the custom goal in the ETA skill display.")
+ @ConfigEditorBoolean
+ public boolean enableInETADisplay = false;
+
+ @Expose
+ @ConfigOption(name = "Progress Bar", desc = "Enable the custom goal in the progress bar.")
+ @ConfigEditorBoolean
+ public boolean enableInProgressBar = true;
+
+ @Expose
+ @ConfigOption(name = "Skill Menu Tooltips", desc = "Enable the custom goal in the tooltip of items in skills menu.")
+ @ConfigEditorBoolean
+ public boolean enableInSkillMenuTooltip = false;
+
+ @Expose
+ @ConfigOption(name = "Chat", desc = "Send a message when you reach your goal.")
+ @ConfigEditorBoolean
+ public boolean enableInChat = false;
+
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillETADisplayConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillETADisplayConfig.java
new file mode 100644
index 000000000..37ea0430c
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillETADisplayConfig.java
@@ -0,0 +1,44 @@
+package at.hannibal2.skyhanni.config.features.skillprogress;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import com.google.gson.annotations.Expose;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorSlider;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorText;
+import io.github.moulberry.moulconfig.annotations.ConfigOption;
+import io.github.moulberry.moulconfig.observer.Property;
+
+public class SkillETADisplayConfig {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Show a display of your current active skill\n" +
+ "with the XP/hour rate, ETA to the next level and current session time.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public Property<Boolean> enabled = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "Farming", desc = "After how much seconds the Farming session timer should pause.")
+ @ConfigEditorSlider(minStep = 1, minValue = 3, maxValue = 60)
+ public int farmingPauseTime = 3;
+
+ @Expose
+ @ConfigOption(name = "Mining", desc = "After how much seconds the Mining session timer should pause.")
+ @ConfigEditorSlider(minStep = 1, minValue = 3, maxValue = 60)
+ public int miningPauseTime = 3;
+
+ @Expose
+ @ConfigOption(name = "Combat", desc = "After how much seconds the Combat session timer should pause.")
+ @ConfigEditorSlider(minStep = 1, minValue = 3, maxValue = 60)
+ public int combatPauseTime = 30;
+
+ @Expose
+ @ConfigOption(name = "Foraging", desc = "After how much seconds the Foraging session timer should pause.")
+ @ConfigEditorSlider(minStep = 1, minValue = 3, maxValue = 60)
+ public int foragingPauseTime = 3;
+
+ @Expose
+ @ConfigOption(name = "Fishing", desc = "After how much seconds the Fishing session timer should pause.")
+ @ConfigEditorSlider(minStep = 1, minValue = 3, maxValue = 60)
+ public int fishingPauseTime = 15;
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillOverflowConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillOverflowConfig.java
new file mode 100644
index 000000000..cf6118158
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillOverflowConfig.java
@@ -0,0 +1,46 @@
+package at.hannibal2.skyhanni.config.features.skillprogress;
+
+import com.google.gson.annotations.Expose;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.moulberry.moulconfig.annotations.ConfigOption;
+import io.github.moulberry.moulconfig.observer.Property;
+
+public class SkillOverflowConfig {
+
+ @Expose
+ @ConfigOption(name = "Display", desc = "Enable the overflow calculation in the progress display.")
+ @ConfigEditorBoolean
+ public Property<Boolean> enableInDisplay = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "All Skill Display", desc = "Enable the overflow calculation in the all skill progress display.")
+ @ConfigEditorBoolean
+ public Property<Boolean> enableInAllDisplay = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "ETA Display", desc = "Enable the overflow calculation in the ETA skill display.")
+ @ConfigEditorBoolean
+ public Property<Boolean> enableInEtaDisplay = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "Progress Bar", desc = "Enable the overflow calculation in the progress bar of the display.")
+ @ConfigEditorBoolean
+ public Property<Boolean> enableInProgressBar = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "Skill Menu Stack Size", desc = "Enable the overflow calculation when the 'Skill Level' Item Number is enabled.")
+ @ConfigEditorBoolean
+ public boolean enableInSkillMenuAsStackSize = false;
+
+ @Expose
+ @ConfigOption(name = "Skill Menu Tooltips", desc = "Enable the overflow calculation in the tooltip of items in skills menu.")
+ @ConfigEditorBoolean
+ public boolean enableInSkillMenuTooltip = false;
+
+ @Expose
+ @ConfigOption(name = "Chat", desc = "Enable the overflow level up message when you gain an overflow level.")
+ @ConfigEditorBoolean
+ public boolean enableInChat = false;
+
+
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillProgressBarConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillProgressBarConfig.java
new file mode 100644
index 000000000..80fe33148
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillProgressBarConfig.java
@@ -0,0 +1,107 @@
+package at.hannibal2.skyhanni.config.features.skillprogress;
+
+import at.hannibal2.skyhanni.SkyHanniMod;
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import com.google.gson.annotations.Expose;
+import io.github.moulberry.moulconfig.annotations.Accordion;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorColour;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorDropdown;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorSlider;
+import io.github.moulberry.moulconfig.annotations.ConfigOption;
+import io.github.moulberry.moulconfig.observer.Property;
+
+public class SkillProgressBarConfig {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Enable/Disable the progress bar.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public Property<Boolean> enabled = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "Textured Bar", desc = "Use a textured progress bar.\n§eCan be changed with a resource pack.")
+ @ConfigEditorBoolean
+ public Property<Boolean> useTexturedBar = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "Chroma", desc = "Use the SBA like chroma effect on the bar.\n§eIf enabled, ignore the Bar Color setting.")
+ @ConfigEditorBoolean
+ public Property<Boolean> useChroma = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "Bar Color", desc = "Color of the progress bar.\n§eIgnored if Chroma is enabled.")
+ @ConfigEditorColour
+ public String barStartColor = "0:255:255:0:0";
+
+ @Expose
+ @ConfigOption(name = "Textured Bar", desc = "")
+ @Accordion
+ public TexturedBar texturedBar = new TexturedBar();
+
+ public static class TexturedBar {
+
+ @Expose
+ @ConfigOption(name = "Used Texture", desc = "Choose what texture to use.")
+ @ConfigEditorDropdown
+ public Property<UsedTexture> usedTexture = Property.of(UsedTexture.MATCH_PACK);
+
+ public enum UsedTexture {
+ MATCH_PACK("Match Resource Pack", "minecraft:textures/gui/icons.png"),
+ CUSTOM_1("Texture 1", SkyHanniMod.MODID + ":bars/1.png"),
+ CUSTOM_2("Texture 2", SkyHanniMod.MODID + ":bars/2.png"),
+ CUSTOM_3("Texture 3", SkyHanniMod.MODID + ":bars/3.png"),
+ CUSTOM_4("Texture 4", SkyHanniMod.MODID + ":bars/4.png"),
+ CUSTOM_5("Texture 5", SkyHanniMod.MODID + ":bars/5.png"),
+ ;
+
+ private final String str;
+ private final String path;
+
+ UsedTexture(String str, String path) {
+ this.str = str;
+ this.path = path;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ @Override
+ public String toString() {
+ return str;
+ }
+ }
+
+ @Expose
+ @ConfigOption(name = "Width", desc = "Modify the width of the bar.\n" +
+ "§eDefault: 182\n" +
+ "§c!!Do not work for now!!")
+ @ConfigEditorSlider(minStep = 1, minValue = 16, maxValue = 1024)
+ public int width = 182;
+
+ @Expose
+ @ConfigOption(name = "Height", desc = "Modify the height of the bar.\n" +
+ "§eDefault: 5\n" +
+ "§c!!Do not work for now!!")
+ @ConfigEditorSlider(minStep = 1, minValue = 3, maxValue = 16)
+ public int height = 5;
+ }
+
+ @Expose
+ @ConfigOption(name = "Regular Bar", desc = "")
+ @Accordion
+ public RegularBar regularBar = new RegularBar();
+
+ public static class RegularBar {
+ @Expose
+ @ConfigOption(name = "Width", desc = "Modify the width of the bar.")
+ @ConfigEditorSlider(minStep = 1, minValue = 100, maxValue = 1000)
+ public int width = 182;
+
+ @Expose
+ @ConfigOption(name = "Height", desc = "Modify the height of the bar.")
+ @ConfigEditorSlider(minStep = 1, minValue = 3, maxValue = 15)
+ public int height = 6;
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillProgressConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillProgressConfig.java
new file mode 100644
index 000000000..77d459b6e
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/skillprogress/SkillProgressConfig.java
@@ -0,0 +1,117 @@
+package at.hannibal2.skyhanni.config.features.skillprogress;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import at.hannibal2.skyhanni.config.core.config.Position;
+import at.hannibal2.skyhanni.utils.RenderUtils;
+import com.google.gson.annotations.Expose;
+import io.github.moulberry.moulconfig.annotations.Category;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.moulberry.moulconfig.annotations.ConfigEditorDropdown;
+import io.github.moulberry.moulconfig.annotations.ConfigOption;
+import io.github.moulberry.moulconfig.observer.Property;
+
+public class SkillProgressConfig {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Show the Skill Progress Display.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public Property<Boolean> enabled = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "Text Alignment", desc = "Align the display text with the progress bar.")
+ @ConfigEditorDropdown
+ public Property<TextAlignment> textAlignmentProperty = Property.of(TextAlignment.CENTERED);
+
+ public enum TextAlignment {
+ NONE("None", null),
+ CENTERED("Centered", RenderUtils.HorizontalAlignment.CENTER),
+ LEFT("Left", RenderUtils.HorizontalAlignment.LEFT),
+ RIGHT("Right", RenderUtils.HorizontalAlignment.RIGHT),
+ ;
+
+ private final String str;
+ private final RenderUtils.HorizontalAlignment alignment;
+
+ TextAlignment(String str, RenderUtils.HorizontalAlignment alignment) {
+ this.str = str;
+ this.alignment = alignment;
+ }
+
+ public RenderUtils.HorizontalAlignment getAlignment() {
+ return alignment;
+ }
+
+ @Override
+ public String toString() {
+ return str;
+ }
+ }
+
+ @Expose
+ @ConfigOption(name = "Hide In Action Bar", desc = "Hide the skill progress in the Hypixel action bar.")
+ @ConfigEditorBoolean
+ public boolean hideInActionBar = false;
+
+ @Expose
+ @ConfigOption(name = "Always Show", desc = "Always show the skill progress.")
+ @ConfigEditorBoolean
+ public Property<Boolean> alwaysShow = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "Show Action left", desc = "Show action left until you reach the next level.")
+ @ConfigEditorBoolean
+ public Property<Boolean> showActionLeft = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "Use percentage", desc = "Use percentage instead of XP.")
+ @ConfigEditorBoolean
+ public Property<Boolean> usePercentage = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "Use Icon", desc = "Show the skill icon in the display.")
+ @ConfigEditorBoolean
+ public Property<Boolean> useIcon = Property.of(true);
+
+ @Expose
+ @ConfigOption(name = "Use Skill Name", desc = "Show the skill name in the display.")
+ @ConfigEditorBoolean
+ public Property<Boolean> useSkillName = Property.of(false);
+
+ @Expose
+ @ConfigOption(name = "Show Level", desc = "Show your current level in the display.")
+ @ConfigEditorBoolean
+ public Property<Boolean> showLevel = Property.of(true);
+
+ @Expose
+ @Category(name = "Progress Bar", desc = "Progress Bar Config.")
+ public SkillProgressBarConfig skillProgressBarConfig = new SkillProgressBarConfig();
+
+ @Expose
+ @Category(name = "Overflow", desc = "Overflow Config.")
+ public SkillOverflowConfig overflowConfig = new SkillOverflowConfig();
+
+ @Expose
+ @Category(name = "Custom Goal", desc = "Define a custom goal for each skills.")
+ public CustomGoalConfig customGoalConfig = new CustomGoalConfig();
+
+ @Expose
+ @Category(name = "All Skill Display", desc = "All Skill Display Config.")
+ public AllSkillDisplayConfig allSkillDisplayConfig = new AllSkillDisplayConfig();
+
+ @Expose
+ @Category(name = "ETA Display", desc = "ETA Display Config.")
+ public SkillETADisplayConfig skillETADisplayConfig = new SkillETADisplayConfig();
+
+ @Expose
+ public Position displayPosition = new Position(384, -105, false, true);
+
+ @Expose
+ public Position barPosition = new Position(384, -87, false, true);
+
+ @Expose
+ public Position allSkillPosition = new Position(5, 209, false, true);
+
+ @Expose
+ public Position etaPosition = new Position(5, 155, false, true);
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/ActionBarData.kt b/src/main/java/at/hannibal2/skyhanni/data/ActionBarData.kt
index aa72e9868..74617e9b3 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/ActionBarData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/ActionBarData.kt
@@ -22,8 +22,11 @@ object ActionBarData {
val original = event.message
val message = LorenzUtils.stripVanillaMessage(original.formattedText)
- if (message == actionBar) return
actionBar = message
- ActionBarUpdateEvent(actionBar).postAndCatch()
+ val actionBarEvent = ActionBarUpdateEvent(actionBar, event.message)
+ actionBarEvent.postAndCatch()
+ if (event.message.formattedText != actionBarEvent.chatComponent.formattedText) {
+ event.message = actionBarEvent.chatComponent
+ }
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/events/ActionBarUpdateEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/ActionBarUpdateEvent.kt
index 9b9c0f8dd..52ec909c0 100644
--- a/src/main/java/at/hannibal2/skyhanni/events/ActionBarUpdateEvent.kt
+++ b/src/main/java/at/hannibal2/skyhanni/events/ActionBarUpdateEvent.kt
@@ -1,3 +1,10 @@
package at.hannibal2.skyhanni.events
-class ActionBarUpdateEvent(val actionBar: String) : LorenzEvent()
+import net.minecraft.util.ChatComponentText
+import net.minecraft.util.IChatComponent
+
+class ActionBarUpdateEvent(var actionBar: String, var chatComponent: IChatComponent) : LorenzEvent() {
+ fun changeActionBar(newText: String) {
+ chatComponent = ChatComponentText(newText)
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/events/SkillOverflowLevelupEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/SkillOverflowLevelupEvent.kt
new file mode 100644
index 000000000..a4a25a314
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/SkillOverflowLevelupEvent.kt
@@ -0,0 +1,5 @@
+package at.hannibal2.skyhanni.events
+
+import at.hannibal2.skyhanni.features.skillprogress.SkillType
+
+class SkillOverflowLevelupEvent(val skill: SkillType, val oldLevel: Int, val newLevel: Int): LorenzEvent()
diff --git a/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShaderManager.kt b/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShaderManager.kt
index 98e1daa71..60b748d33 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShaderManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/chroma/ChromaShaderManager.kt
@@ -54,4 +54,4 @@ enum class ChromaType(val shaderName: String) {
* See [TexturedChromaShader]
*/
TEXTURED("textured_chroma")
-} \ No newline at end of file
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/chroma/StandardChromaShader.kt b/src/main/java/at/hannibal2/skyhanni/features/chroma/StandardChromaShader.kt
index 268b37b4c..e1f39aa84 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/chroma/StandardChromaShader.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/chroma/StandardChromaShader.kt
@@ -17,4 +17,4 @@ package at.hannibal2.skyhanni.features.chroma
object StandardChromaShader : ChromaShader("standard_chroma", "standard_chroma") {
val INSTANCE: StandardChromaShader
get() = this
-} \ No newline at end of file
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/chroma/TexturedChromaShader.kt b/src/main/java/at/hannibal2/skyhanni/features/chroma/TexturedChromaShader.kt
index 544d10184..26417d8dc 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/chroma/TexturedChromaShader.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/chroma/TexturedChromaShader.kt
@@ -17,4 +17,4 @@ package at.hannibal2.skyhanni.features.chroma
object TexturedChromaShader : ChromaShader("textured_chroma", "textured_chroma") {
val INSTANCE: TexturedChromaShader
get() = this
-} \ No newline at end of file
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt
index 733be8cd0..62eff052a 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt
@@ -182,7 +182,7 @@ object GhostCounter {
val bestiary = if (config.showMax) {
when (nextLevel) {
26 -> bestiaryFormatting.maxed.replace("%currentKill%", currentKill.addSeparators())
- in 1..25 -> {
+ in 1 .. 25 -> {
val sum = bestiaryData.filterKeys { it <= nextLevel - 1 }.values.sum()
val cKill = sum + currentKill
@@ -195,7 +195,7 @@ object GhostCounter {
} else {
when (nextLevel) {
26 -> bestiaryFormatting.maxed
- in 1..25 -> bestiaryFormatting.progress
+ in 1 .. 25 -> bestiaryFormatting.progress
else -> bestiaryFormatting.openMenu
}
}
@@ -301,7 +301,7 @@ object GhostCounter {
val res = current.formatNumber().toString()
gain = (res.toLong() - lastXp.toLong()).toDouble().roundToInt()
num = (gain.toDouble() / gained)
- if (gained in 150.0..450.0 && lastXp != "0" && num >= 0) {
+ if (gained in 150.0 .. 450.0 && lastXp != "0" && num >= 0) {
KILLS.add(num)
KILLS.add(num, true)
Option.GHOSTSINCESORROW.add(num)
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt
index b27d3e863..0ac715598 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt
@@ -147,7 +147,7 @@ object GardenCropMilestoneDisplay {
if (crop.isMaxed()) {
list.add("§7" + crop.cropName + " §eMAXED")
} else {
- list.add("§7" + crop.cropName + " $currentTier➜$nextTier")
+ list.add("§7" + crop.cropName + " §8$currentTier➜§3$nextTier")
}
lineMap[1] = list
diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/ItemDisplayOverlayFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/ItemDisplayOverlayFeatures.kt
index bc3f52ef2..35f0c6f4a 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/inventory/ItemDisplayOverlayFeatures.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/ItemDisplayOverlayFeatures.kt
@@ -2,6 +2,7 @@ package at.hannibal2.skyhanni.features.inventory
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.api.CollectionAPI
+import at.hannibal2.skyhanni.api.SkillAPI
import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
import at.hannibal2.skyhanni.config.features.inventory.InventoryConfig.ItemNumberEntry
import at.hannibal2.skyhanni.config.features.inventory.InventoryConfig.ItemNumberEntry.BINGO_GOAL_RANK
@@ -23,6 +24,7 @@ import at.hannibal2.skyhanni.config.features.inventory.InventoryConfig.ItemNumbe
import at.hannibal2.skyhanni.data.PetAPI
import at.hannibal2.skyhanni.events.RenderItemTipEvent
import at.hannibal2.skyhanni.features.garden.pests.PestAPI
+import at.hannibal2.skyhanni.features.skillprogress.SkillType
import at.hannibal2.skyhanni.utils.ConfigUtils
import at.hannibal2.skyhanni.utils.InventoryUtils
import at.hannibal2.skyhanni.utils.ItemCategory
@@ -158,9 +160,14 @@ object ItemDisplayOverlayFeatures {
if (CollectionAPI.isCollectionTier0(lore)) return "0"
val split = itemName.split(" ")
if (!itemName.contains("Dungeon")) {
+ val skillName = split.first()
val text = split.last()
if (split.size < 2) return "0"
- return "" + text.romanToDecimalIfNecessary()
+ val level = "" + text.romanToDecimalIfNecessary()
+ val skill = SkillType.getByNameOrNull(skillName) ?: return level
+ val skillInfo = SkillAPI.storage?.get(skill) ?: return level
+ return if (SkyHanniMod.feature.skillProgress.overflowConfig.enableInSkillMenuAsStackSize)
+ "" + skillInfo.overflowLevel else level
}
}
@@ -205,9 +212,9 @@ object ItemDisplayOverlayFeatures {
item.name?.let {
dungeonPotionPattern.matchMatcher(it.removeColor()) {
return when (val level = group("level").romanToDecimal()) {
- in 1..2 -> "§f$level"
- in 3..4 -> "§a$level"
- in 5..6 -> "§9$level"
+ in 1 .. 2 -> "§f$level"
+ in 3 .. 4 -> "§a$level"
+ in 5 .. 6 -> "§9$level"
else -> "§5$level"
}
}
@@ -279,4 +286,4 @@ object ItemDisplayOverlayFeatures {
}
fun ItemNumberEntry.isSelected() = config.itemNumberAsStackSize.contains(this)
-}
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/minion/MinionXp.kt b/src/main/java/at/hannibal2/skyhanni/features/minion/MinionXp.kt
index 4d5d54bae..22969de1c 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/minion/MinionXp.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/minion/MinionXp.kt
@@ -9,6 +9,7 @@ import at.hannibal2.skyhanni.events.MinionCloseEvent
import at.hannibal2.skyhanni.events.MinionOpenEvent
import at.hannibal2.skyhanni.events.MinionStorageOpenEvent
import at.hannibal2.skyhanni.events.RepositoryReloadEvent
+import at.hannibal2.skyhanni.features.skillprogress.SkillType
import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
import at.hannibal2.skyhanni.utils.LorenzUtils
@@ -38,28 +39,15 @@ class MinionXp {
private var xpInfoMap: Map<NEUInternalName, XpInfo> = hashMapOf()
- data class XpInfo(val type: XpType, val amount: Double)
-
- private data class MinionStorage(val position: LorenzVec, val xpList: EnumMap<XpType, Double>) {
+ data class XpInfo(val type: SkillType, val amount: Double)
+ private data class MinionStorage(val position: LorenzVec, val xpList: EnumMap<SkillType, Double>) {
val timestamp: SimpleTimeMark = SimpleTimeMark.now()
}
private fun toPrimitiveItemStack(itemStack: ItemStack) =
PrimitiveItemStack(itemStack.getInternalName(), itemStack.stackSize)
-
- // TODO use upper case names, created a function to get type by lowercase name
- // TODO maybe: rename to SkillType, move somewhere else
- enum class XpType {
-
- Farming,
- Mining,
- Combat,
- Foraging,
- Fishing,
- Alchemy
- }
-
+
@SubscribeEvent
fun onMinionOpen(event: MinionOpenEvent) {
if (!config.xpDisplay) return
@@ -84,7 +72,7 @@ class MinionXp {
private fun getStorageXpAndUpdateTotal(
minionPosition: LorenzVec,
- xpTotal: EnumMap<XpType, Double>,
+ xpTotal: EnumMap<SkillType, Double>,
): Boolean {
if (!getHasStorage(minionPosition)) return false
val storage = minionStorages.firstOrNull {
@@ -101,10 +89,10 @@ class MinionXp {
}
}
- private fun handleItems(inventoryItems: Map<Int, ItemStack>, isMinion: Boolean): EnumMap<XpType, Double> {
- val xpTotal = EnumMap<XpType, Double>(XpType::class.java)
+ private fun handleItems(inventoryItems: Map<Int, ItemStack>, isMinion: Boolean): EnumMap<SkillType, Double> {
+ val xpTotal = EnumMap<SkillType, Double>(SkillType::class.java)
inventoryItems.filter {
- it.value.getLore().isNotEmpty() && (!isMinion || it.key in listOf(21..26, 30..35, 39..44).flatten())
+ it.value.getLore().isNotEmpty() && (!isMinion || it.key in listOf(21 .. 26, 30 .. 35, 39 .. 44).flatten())
}.forEach { (_, itemStack) ->
val item = toPrimitiveItemStack(itemStack)
val name = item.internalName
@@ -133,8 +121,8 @@ class MinionXp {
minionStorages.add(MinionStorage(event.position, xpTotal))
}
- private fun collectMessage(type: XpType, amount: Double) =
- "§7Collect to get: §b${amount.addSeparators()} §e${type.name} XP"
+ private fun collectMessage(type: SkillType, amount: Double) =
+ "§7Collect to get: §b${amount.addSeparators()} §e${type.displayName} XP"
private fun getHasStorage(minionPosition: LorenzVec): Boolean {
val positionsToCheck = listOf(
@@ -192,7 +180,7 @@ class MinionXp {
@SubscribeEvent
fun onRepoReload(event: RepositoryReloadEvent) {
xpInfoMap = event.getConstant<MinionXPJson>("MinionXP").minion_xp.mapNotNull { xpType ->
- xpType.value.mapNotNull { it.key.asInternalName() to XpInfo(XpType.valueOf(xpType.key), it.value) }
+ xpType.value.mapNotNull { it.key.asInternalName() to XpInfo(SkillType.getByName(xpType.key), it.value) }
}.flatten().toMap()
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/RoundedRectangleShader.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/RoundedRectangleShader.kt
index 6b0ce5e63..9ce2d0ce7 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/misc/RoundedRectangleShader.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/RoundedRectangleShader.kt
@@ -25,4 +25,4 @@ object RoundedRectangleShader : Shader("rounded_rect", "rounded_rect") {
registerUniform(Uniform.UniformType.VEC2, "halfSize") { halfSize }
registerUniform(Uniform.UniformType.VEC2, "centerPos") { centerPos }
}
-} \ No newline at end of file
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillProgress.kt b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillProgress.kt
new file mode 100644
index 000000000..01f60ac87
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillProgress.kt
@@ -0,0 +1,472 @@
+package at.hannibal2.skyhanni.features.skillprogress
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.api.SkillAPI
+import at.hannibal2.skyhanni.api.SkillAPI.activeSkill
+import at.hannibal2.skyhanni.api.SkillAPI.lastUpdate
+import at.hannibal2.skyhanni.api.SkillAPI.oldSkillInfoMap
+import at.hannibal2.skyhanni.api.SkillAPI.showDisplay
+import at.hannibal2.skyhanni.api.SkillAPI.skillXPInfoMap
+import at.hannibal2.skyhanni.config.features.skillprogress.SkillProgressConfig
+import at.hannibal2.skyhanni.events.ActionBarUpdateEvent
+import at.hannibal2.skyhanni.events.ConfigLoadEvent
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.LorenzTickEvent
+import at.hannibal2.skyhanni.events.ProfileJoinEvent
+import at.hannibal2.skyhanni.events.SkillOverflowLevelupEvent
+import at.hannibal2.skyhanni.features.skillprogress.SkillUtil.XP_NEEDED_FOR_60
+import at.hannibal2.skyhanni.utils.ChatUtils.chat
+import at.hannibal2.skyhanni.utils.ConditionalUtils.onToggle
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
+import at.hannibal2.skyhanni.utils.NumberUtil.interpolate
+import at.hannibal2.skyhanni.utils.NumberUtil.roundToPrecision
+import at.hannibal2.skyhanni.utils.Quad
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables
+import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.SoundUtils
+import at.hannibal2.skyhanni.utils.SoundUtils.playSound
+import at.hannibal2.skyhanni.utils.SpecialColour
+import at.hannibal2.skyhanni.utils.TimeUnit
+import at.hannibal2.skyhanni.utils.TimeUtils.format
+import at.hannibal2.skyhanni.utils.renderables.Renderable
+import at.hannibal2.skyhanni.utils.renderables.Renderable.Companion.horizontalContainer
+import net.minecraftforge.fml.common.eventhandler.EventPriority
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.awt.Color
+import kotlin.math.ceil
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+
+object SkillProgress {
+
+ private val config get() = SkyHanniMod.feature.skillProgress
+ private val barConfig get() = config.skillProgressBarConfig
+ private val allSkillConfig get() = config.allSkillDisplayConfig
+ val etaConfig get() = config.skillETADisplayConfig
+ val customGoalConfig get() = config.customGoalConfig
+
+ private var skillExpPercentage = 0.0
+ private var display = emptyList<Renderable>()
+ private var allDisplay = emptyList<Renderable>()
+ private var etaDisplay = emptyList<Renderable>()
+ private var lastGainUpdate = SimpleTimeMark.farPast()
+ private var maxWidth = 0
+ var hideInActionBar = listOf<String>()
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) {
+ if (!isEnabled()) return
+ if (display.isEmpty()) return
+
+ if (showDisplay) {
+ renderDisplay()
+
+ if (barConfig.enabled.get()) {
+ renderBar()
+ }
+ }
+
+ if (etaConfig.enabled.get()) {
+ config.etaPosition.renderRenderables(etaDisplay, posLabel = "Skill ETA")
+ }
+ }
+
+ @SubscribeEvent
+ fun onGuiRender(event: GuiRenderEvent) {
+ if (!isEnabled()) return
+ if (display.isEmpty()) return
+
+ if (allSkillConfig.enabled.get()) {
+ config.allSkillPosition.renderRenderables(allDisplay, posLabel = "All Skills Display")
+ }
+ }
+
+ private fun renderDisplay() {
+ when (val textAlignment = config.textAlignmentProperty.get()) {
+ SkillProgressConfig.TextAlignment.NONE -> {
+ config.displayPosition.renderStringsAndItems(listOf(display), posLabel = "Skill Progress")
+ }
+
+ SkillProgressConfig.TextAlignment.CENTERED,
+ SkillProgressConfig.TextAlignment.LEFT,
+ SkillProgressConfig.TextAlignment.RIGHT,
+ -> {
+ config.displayPosition.renderRenderables(
+ listOf(Renderable.fixedSizeLine(horizontalContainer(display, horizontalAlign = textAlignment.alignment), maxWidth)),
+ posLabel = "Skill Progress")
+ }
+
+ else -> {}
+ }
+ }
+
+ private fun renderBar() {
+ val progress = if (barConfig.useTexturedBar.get()) {
+ val factor = (skillExpPercentage.toFloat().coerceAtMost(1f)) * 182
+ maxWidth = 182
+ Renderable.progressBar(
+ percent = factor.toDouble(),
+ startColor = Color(SpecialColour.specialToChromaRGB(barConfig.barStartColor)),
+ texture = barConfig.texturedBar.usedTexture.get(),
+ useChroma = barConfig.useChroma.get())
+
+ } else {
+ maxWidth = barConfig.regularBar.width
+ Renderable.progressBar(
+ percent = skillExpPercentage,
+ startColor = Color(SpecialColour.specialToChromaRGB(barConfig.barStartColor)),
+ endColor = Color(SpecialColour.specialToChromaRGB(barConfig.barStartColor)),
+ width = maxWidth,
+ height = barConfig.regularBar.height,
+ useChroma = barConfig.useChroma.get())
+ }
+
+ config.barPosition.renderRenderables(listOf(progress), posLabel = "Skill Progress Bar")
+ }
+
+ @SubscribeEvent
+ fun onProfileJoin(event: ProfileJoinEvent) {
+ display = emptyList()
+ allDisplay = emptyList()
+ etaDisplay = emptyList()
+ skillExpPercentage = 0.0
+ }
+
+ @SubscribeEvent
+ fun onTick(event: LorenzTickEvent) {
+ if (!isEnabled()) return
+ if (lastUpdate.passedSince() > 3.seconds) showDisplay = config.alwaysShow.get()
+
+ if (event.repeatSeconds(1)) {
+ allDisplay = formatAllDisplay(drawAllDisplay())
+ etaDisplay = drawETADisplay()
+ }
+
+ if (event.repeatSeconds(2)) {
+ update()
+ updateSkillInfo()
+ }
+ }
+
+ @SubscribeEvent
+ fun onLevelUp(event: SkillOverflowLevelupEvent) {
+ if (!isEnabled()) return
+ if (!config.overflowConfig.enableInChat) return
+ val skillName = event.skill.displayName
+ val oldLevel = event.oldLevel
+ val newLevel = event.newLevel
+ val skill = SkillAPI.storage?.get(event.skill) ?: return
+ val goalReached = newLevel == skill.customGoalLevel && customGoalConfig.enableInChat
+
+ val rewards = buildList {
+ add(" §r§7§8+§b1 Flexing Point")
+ if (newLevel % 5 == 0)
+ add(" §r§7§8+§d50 SkyHanni User Luck")
+ }
+ val messages = listOf(
+ "§3§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬",
+ " §r§b§lSKILL LEVEL UP §3$skillName §8$oldLevel➜§3$newLevel",
+ if (goalReached)
+ listOf(
+ "",
+ " §r§d§lGOAL REACHED!",
+ "",
+ ).joinToString("\n") else
+ "",
+ " §r§a§lREWARDS",
+ rewards.joinToString("\n"),
+ "§3§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬"
+ )
+
+ chat(messages.joinToString("\n"), false)
+
+ if (goalReached)
+ chat("§lYou have reached your goal level of §b§l${skill.customGoalLevel} §e§lin the §b§l$skillName §e§lskill!")
+
+ SoundUtils.createSound("random.levelup", 1f, 1f).playSound()
+ }
+
+ @SubscribeEvent
+ fun onConfigLoad(event: ConfigLoadEvent) {
+ onToggle(
+ config.enabled,
+ config.alwaysShow,
+ config.showActionLeft,
+ config.useIcon,
+ config.usePercentage,
+ config.useSkillName,
+ config.overflowConfig.enableInDisplay,
+ config.overflowConfig.enableInProgressBar,
+ config.overflowConfig.enableInEtaDisplay,
+ barConfig.enabled,
+ barConfig.useChroma,
+ barConfig.useTexturedBar,
+ allSkillConfig.enabled,
+ etaConfig.enabled
+ ) {
+ updateDisplay()
+ update()
+ }
+ }
+
+ @SubscribeEvent(priority = EventPriority.LOW)
+ fun onActionBar(event: ActionBarUpdateEvent) {
+ if (!config.hideInActionBar) return
+ if (event.isCanceled) return
+ var msg = event.actionBar
+ for (line in hideInActionBar) {
+ msg = msg.replace(Regex("\\s*" + Regex.escape(line)), "")
+ }
+ msg = msg.trim()
+
+ event.changeActionBar(msg)
+ }
+
+ fun updateDisplay() {
+ display = drawDisplay()
+ }
+
+ private fun update() {
+ lastGainUpdate = SimpleTimeMark.now()
+ skillXPInfoMap.forEach {
+ it.value.xpGainLast = it.value.xpGainHour
+ }
+ }
+
+ private fun formatAllDisplay(map: Map<SkillType, Renderable>): List<Renderable> {
+ val newList = mutableListOf<Renderable>()
+ if (map.isEmpty()) return newList
+ for (skillType in allSkillConfig.skillEntryList) {
+ map[skillType]?.let {
+ newList.add(it)
+ }
+ }
+ return newList
+ }
+
+ private fun drawAllDisplay() = buildMap {
+ val skillMap = SkillAPI.storage ?: return@buildMap
+ val sortedMap = SkillType.entries.filter { it.displayName.isNotEmpty() }.sortedBy { it.displayName.take(2) }
+
+ for (skill in sortedMap) {
+ val skillInfo = skillMap[skill] ?: SkillAPI.SkillInfo(level = -1, overflowLevel = -1)
+ val lockedLevels = skillInfo.overflowCurrentXp > skillInfo.overflowCurrentXpMax
+ val useCustomGoalLevel = skillInfo.customGoalLevel != 0 && skillInfo.customGoalLevel > skillInfo.overflowLevel && customGoalConfig.enableInAllDisplay
+ val targetLevel = skillInfo.customGoalLevel
+ var xp = skillInfo.overflowTotalXp
+ if (targetLevel in 50 .. 60 && skillInfo.overflowLevel >= 50) xp += SkillUtil.xpRequiredForLevel(50.0)
+ else if (targetLevel > 60 && skillInfo.overflowLevel >= 60) xp += SkillUtil.xpRequiredForLevel(60.0)
+
+ var have = skillInfo.overflowTotalXp
+ val need = SkillUtil.xpRequiredForLevel(targetLevel.toDouble())
+ if (targetLevel in 51 .. 59) have += SkillUtil.xpRequiredForLevel(50.0)
+ else if (targetLevel > 60) have += SkillUtil.xpRequiredForLevel(60.0)
+
+ val (level, currentXp, currentXpMax, totalXp) =
+ if (useCustomGoalLevel)
+ Quad(skillInfo.overflowLevel, have, need, xp)
+ else if (config.overflowConfig.enableInAllDisplay.get() && !lockedLevels)
+ Quad(skillInfo.overflowLevel, skillInfo.overflowCurrentXp, skillInfo.overflowCurrentXpMax, skillInfo.overflowTotalXp)
+ else
+ Quad(skillInfo.level, skillInfo.currentXp, skillInfo.currentXpMax, skillInfo.totalXp)
+
+ this[skill] = if (level == -1) {
+ Renderable.clickAndHover(
+ "§cOpen your skills menu !",
+ listOf("§eClick here to execute §6/skills"),
+ onClick = { LorenzUtils.sendCommandToServer("skills") }
+ )
+ } else {
+ val tips = buildList {
+ add("§6Level: §b${level}")
+ add("§6Current XP: §b${currentXp.addSeparators()}")
+ add("§6Needed XP: §b${currentXpMax.addSeparators()}")
+ add("§6Total XP: §b${totalXp.addSeparators()}")
+ }
+ val nameColor = if (skill == activeSkill) "§2" else "§a"
+ Renderable.hoverTips(buildString {
+ append("$nameColor${skill.displayName} $level ")
+ append("§7(")
+ append("§b${currentXp.addSeparators()}")
+ if (currentXpMax != 0L) {
+ append("§6/")
+ append("§b${currentXpMax.addSeparators()}")
+ }
+ append("§7)")
+ }, tips)
+ }
+ }
+ }
+
+ private fun drawETADisplay() = buildList {
+ val activeSkill = activeSkill ?: return@buildList
+ val skillInfo = SkillAPI.storage?.get(activeSkill) ?: return@buildList
+ val xpInfo = skillXPInfoMap[activeSkill] ?: return@buildList
+ val skillInfoLast = oldSkillInfoMap[activeSkill] ?: return@buildList
+ oldSkillInfoMap[activeSkill] = skillInfo
+ val level = if (config.overflowConfig.enableInEtaDisplay.get() || config.customGoalConfig.enableInETADisplay) skillInfo.overflowLevel else skillInfo.level
+
+ val useCustomGoalLevel = skillInfo.customGoalLevel != 0 && skillInfo.customGoalLevel > skillInfo.overflowLevel && customGoalConfig.enableInETADisplay
+ var targetLevel = if (useCustomGoalLevel) skillInfo.customGoalLevel else level + 1
+ if (targetLevel <= level || targetLevel > 400) targetLevel = (level + 1)
+ val currentLevelNeededXp = SkillUtil.xpRequiredForLevel(level.toDouble()) + skillInfo.overflowCurrentXp
+ val targetNeededXp = SkillUtil.xpRequiredForLevel(targetLevel.toDouble())
+ var remaining = if (useCustomGoalLevel) targetNeededXp - currentLevelNeededXp else skillInfo.overflowCurrentXpMax - skillInfo.overflowCurrentXp
+
+ if (!useCustomGoalLevel) {
+ if (skillInfo.overflowCurrentXpMax == skillInfoLast.overflowCurrentXpMax) {
+ remaining = interpolate(remaining.toFloat(), (skillInfoLast.overflowCurrentXpMax - skillInfoLast.overflowCurrentXp).toFloat(), lastGainUpdate.toMillis()).toLong()
+ }
+ }
+
+ add(Renderable.string("§6Skill: §a${activeSkill.displayName} §8$level➜§3$targetLevel"))
+
+ if (useCustomGoalLevel)
+ add(Renderable.string("§7Needed XP: §e${remaining.addSeparators()}"))
+
+ var xpInterp = xpInfo.xpGainHour
+ if (xpInfo.xpGainHour < 1000) {
+ add(Renderable.string("§7In §cN/A"))
+ } else {
+ val duration = ((remaining) * 1000 * 60 * 60 / xpInterp.toLong()).milliseconds
+ val format = duration.format(TimeUnit.DAY)
+ add(Renderable.string("§7In §b$format " +
+ if (xpInfo.isActive) "" else "§c(PAUSED)"))
+ }
+
+ if (xpInfo.xpGainLast == xpInfo.xpGainHour && xpInfo.xpGainHour <= 0) {
+ add(Renderable.string("§7XP/h: §cN/A"))
+ } else {
+ xpInterp = interpolate(xpInfo.xpGainHour, xpInfo.xpGainLast, lastGainUpdate.toMillis())
+ add(Renderable.string("§7XP/h: §e${xpInterp.toLong().addSeparators()} " +
+ if (xpInfo.isActive) "" else "§c(PAUSED)"))
+ }
+
+
+ val session = xpInfo.timeActive.seconds.format(TimeUnit.HOUR)
+ add(Renderable.clickAndHover("§7Session: §e$session ${if (xpInfo.sessionTimerActive) "" else "§c(PAUSED)"}",
+ listOf("§eClick to reset!")) {
+ xpInfo.sessionTimerActive = false
+ xpInfo.shouldStartTimer = true
+ xpInfo.timeActive = 0L
+ chat("Timer for §b${activeSkill.displayName} §ehas been reset!")
+ })
+ }
+
+ private fun drawDisplay() = buildList {
+ val activeSkill = activeSkill ?: return@buildList
+ val skillMap = SkillAPI.storage ?: return@buildList
+ val skill = skillMap[activeSkill] ?: return@buildList
+ val useCustomGoalLevel = skill.customGoalLevel != 0 && skill.customGoalLevel > skill.overflowLevel
+ val targetLevel = skill.customGoalLevel
+ val xp = skill.totalXp
+ val currentLevel = if (xp <= XP_NEEDED_FOR_60) {
+ SkillUtil.getLevel(xp)
+ } else {
+ SkillUtil.calculateOverFlow(xp).first
+ }
+ var have = skill.overflowTotalXp
+ val need = SkillUtil.xpRequiredForLevel(targetLevel.toDouble())
+ if (targetLevel in 51 .. 59) have += SkillUtil.xpRequiredForLevel(50.0)
+ else if (targetLevel > 60) have += SkillUtil.xpRequiredForLevel(60.0)
+
+
+ val (level, currentXp, currentXpMax, _) =
+ if (useCustomGoalLevel && customGoalConfig.enableInDisplay)
+ Quad(currentLevel, have, need, xp)
+ else if (config.overflowConfig.enableInDisplay.get())
+ Quad(skill.overflowLevel, skill.overflowCurrentXp, skill.overflowCurrentXpMax, skill.overflowTotalXp)
+ else
+ Quad(skill.level, skill.currentXp, skill.currentXpMax, skill.totalXp)
+
+ if (config.showLevel.get())
+ add(Renderable.string("§9[§d$level§9] "))
+
+ if (config.useIcon.get()) {
+ add(Renderable.itemStack(activeSkill.item, 1.2))
+ }
+
+ add(Renderable.string(buildString {
+ append("§b+${skill.lastGain} ")
+
+ if (config.useSkillName.get())
+ append("${activeSkill.displayName} ")
+
+ val (barCurrent, barMax) =
+ if (useCustomGoalLevel && customGoalConfig.enableInProgressBar)
+ Pair(have, need)
+ else if (config.overflowConfig.enableInProgressBar.get())
+ Pair(skill.overflowCurrentXp, skill.overflowCurrentXpMax)
+ else
+ Pair(skill.currentXp, skill.currentXpMax)
+
+ val barPercent = if (barMax == 0L) 100F else 100F * barCurrent / barMax
+ skillExpPercentage = (barPercent.toDouble() / 100)
+
+ val percent = if (currentXpMax == 0L) 100F else 100F * currentXp / currentXpMax
+
+ if (config.usePercentage.get())
+ append("§7(§6${percent.roundToPrecision(2)}%§7)")
+ else {
+ if (currentXpMax == 0L)
+ append("§7(§6${currentXp.addSeparators()}§7)")
+ else
+ append("§7(§6${currentXp.addSeparators()}§7/§6${currentXpMax.addSeparators()}§7)")
+ }
+
+ if (config.showActionLeft.get() && percent != 100f) {
+ append(" - ")
+ val gain = skill.lastGain.replace(",", "")
+ val actionLeft = (ceil(currentXpMax.toDouble() - currentXp) / gain.toDouble()).toLong().addSeparators()
+ if (skill.lastGain != "" && !actionLeft.contains("-")) {
+ append("§6$actionLeft Left")
+ } else {
+ append("§6∞ Left")
+ }
+ }
+ }))
+ }
+
+ private fun updateSkillInfo() {
+ val activeSkill = activeSkill ?: return
+ val xpInfo = skillXPInfoMap.getOrPut(activeSkill) { SkillAPI.SkillXPInfo() }
+ val skillInfo = SkillAPI.storage?.get(activeSkill) ?: return
+ oldSkillInfoMap[activeSkill] = skillInfo
+
+ val totalXp = skillInfo.currentXp
+
+ if (xpInfo.lastTotalXp > 0) {
+ val delta = totalXp - xpInfo.lastTotalXp
+ if (delta > 0 && delta < 1000) {
+ xpInfo.xpGainQueue.add(0, delta)
+
+ calculateXPHour(xpInfo)
+ } else if (xpInfo.timer > 0) {
+ xpInfo.timer--
+ xpInfo.xpGainQueue.add(0, 0f)
+
+ calculateXPHour(xpInfo)
+ } else if (delta <= 0) {
+ xpInfo.isActive = false
+ }
+ }
+ xpInfo.lastTotalXp = totalXp.toFloat()
+ }
+
+ private fun calculateXPHour(xpInfo: SkillAPI.SkillXPInfo) {
+ while (xpInfo.xpGainQueue.size > 30) {
+ xpInfo.xpGainQueue.removeLast()
+ }
+
+ var totalGain = 0f
+ for (f in xpInfo.xpGainQueue) totalGain += f
+
+ xpInfo.xpGainHour = totalGain * (60 * 60) / xpInfo.xpGainQueue.size
+
+ xpInfo.isActive = true
+ }
+
+ private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled.get()
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillTooltip.kt b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillTooltip.kt
new file mode 100644
index 000000000..c09100cbd
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillTooltip.kt
@@ -0,0 +1,85 @@
+package at.hannibal2.skyhanni.features.skillprogress
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.api.SkillAPI
+import at.hannibal2.skyhanni.events.LorenzToolTipEvent
+import at.hannibal2.skyhanni.utils.InventoryUtils
+import at.hannibal2.skyhanni.utils.ItemUtils.cleanName
+import at.hannibal2.skyhanni.utils.ItemUtils.getLore
+import at.hannibal2.skyhanni.utils.ItemUtils.name
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
+import at.hannibal2.skyhanni.utils.NumberUtil.roundToPrecision
+import at.hannibal2.skyhanni.utils.NumberUtil.toRoman
+import at.hannibal2.skyhanni.utils.StringUtils
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class SkillTooltip {
+
+ private val config get() = SkyHanniMod.feature.skillProgress
+ private val overflowConfig get() = config.overflowConfig
+ private val customGoalConfig get() = config.customGoalConfig
+
+ @SubscribeEvent
+ fun onTooltip(event: LorenzToolTipEvent) {
+ if (!LorenzUtils.inSkyBlock) return
+ val inventoryName = InventoryUtils.openInventoryName()
+ val stack = event.itemStack
+ if (inventoryName == "Your Skills" && stack.getLore().any { it.contains("Click to view!") }) {
+ val iterator = event.toolTip.listIterator()
+ val split = stack.cleanName().split(" ")
+ val skillName = split.first()
+ val skill = SkillType.getByNameOrNull(skillName) ?: return
+ val useRoman = split.last().toIntOrNull() == null
+ val skillInfo = SkillAPI.storage?.get(skill) ?: return
+ val showCustomGoal = skillInfo.customGoalLevel != 0 && customGoalConfig.enableInSkillMenuTooltip
+ var next = false
+ for (line in iterator) {
+ val maxReached = "§7§8Max Skill level reached!"
+ if (line.contains(maxReached) && overflowConfig.enableInSkillMenuTooltip) {
+ val progress = (skillInfo.overflowCurrentXp.toDouble() / skillInfo.overflowCurrentXpMax) * 100
+ val percent = "§e${progress.roundToPrecision(1)}%"
+ val currentLevel = skillInfo.overflowLevel
+
+ val level = if (useRoman) currentLevel.toRoman() else currentLevel
+ val nextLevel = if (useRoman) (currentLevel + 1).toRoman() else currentLevel + 1
+ iterator.set("§7Progress to Level $nextLevel: $percent")
+
+ event.itemStack.name = "§a${skill.displayName} $level"
+ next = true
+ continue
+ }
+ val bar = " "
+ if (next && overflowConfig.enableInSkillMenuTooltip) {
+ if (line.contains(bar)) {
+ val progress = (skillInfo.overflowCurrentXp.toDouble() / skillInfo.overflowCurrentXpMax)
+ val progressBar = StringUtils.progressBar(progress)
+ iterator.set("$progressBar §e${skillInfo.overflowCurrentXp.addSeparators()}§6/§e${skillInfo.overflowCurrentXpMax.addSeparators()}")
+ iterator.add("")
+ }
+ }
+ if ((line.contains(bar) || line.contains("/")) && showCustomGoal) {
+ val targetLevel = skillInfo.customGoalLevel
+ var have = skillInfo.overflowTotalXp
+ val need = SkillUtil.xpRequiredForLevel(targetLevel.toDouble())
+ if (targetLevel in 50 .. 60 && skillInfo.overflowLevel >= 50) have += SkillUtil.xpRequiredForLevel(50.0)
+ else if (targetLevel > 60 && skillInfo.overflowLevel >= 60) have += SkillUtil.xpRequiredForLevel(60.0)
+ val progress = have.toDouble() / need
+ val progressBar = StringUtils.progressBar(progress)
+ val nextLevel = if (useRoman) targetLevel.toRoman() else targetLevel
+ val percent = "§e${(progress * 100).roundToPrecision(1)}%"
+ iterator.add("")
+ iterator.add("§7Progress to Level $nextLevel: $percent")
+ iterator.add("$progressBar §e${have.addSeparators()}§6/§e${need.addSeparators()}")
+ iterator.add("")
+ }
+ if (next && overflowConfig.enableInSkillMenuTooltip) {
+ if (line.contains(bar)) {
+ iterator.add("§b§lOVERFLOW XP:")
+ iterator.add("§7▸ ${skillInfo.overflowTotalXp.addSeparators()}")
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillType.kt b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillType.kt
new file mode 100644
index 000000000..bd44a63f3
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillType.kt
@@ -0,0 +1,36 @@
+package at.hannibal2.skyhanni.features.skillprogress
+
+import io.github.moulberry.notenoughupdates.util.Utils
+import net.minecraft.block.Block
+import net.minecraft.init.Blocks
+import net.minecraft.init.Items
+import net.minecraft.item.Item
+import net.minecraft.item.ItemStack
+
+enum class SkillType(val displayName: String, icon: Item) {
+ COMBAT("Combat", Items.golden_sword),
+ FARMING("Farming", Items.golden_hoe),
+ FISHING("Fishing", Items.fishing_rod),
+ MINING("Mining", Items.golden_pickaxe),
+ FORAGING("Foraging", Items.golden_axe),
+ ENCHANTING("Enchanting", Blocks.enchanting_table),
+ ALCHEMY("Alchemy", Items.brewing_stand),
+ CARPENTRY("Carpentry", Blocks.crafting_table),
+ TAMING("Taming", Items.spawn_egg),
+ ;
+
+ constructor(displayName: String, block: Block) : this(displayName, Item.getItemFromBlock(block))
+
+ val item: ItemStack by lazy { Utils.createItemStack(icon, displayName) }
+ val lowercaseName = displayName.lowercase()
+ val uppercaseName = displayName.uppercase()
+
+ override fun toString(): String = "§b$displayName"
+
+ companion object {
+ fun getByName(name: String) = getByNameOrNull(name) ?: error("Unknown Skill Type: '$name'")
+
+ fun getByNameOrNull(name: String) =
+ entries.firstOrNull { it.displayName.lowercase() == name.lowercase() }
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillUtil.kt b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillUtil.kt
new file mode 100644
index 000000000..54ea4bdc9
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/skillprogress/SkillUtil.kt
@@ -0,0 +1,140 @@
+package at.hannibal2.skyhanni.features.skillprogress
+
+import at.hannibal2.skyhanni.api.SkillAPI
+import at.hannibal2.skyhanni.api.SkillAPI.activeSkill
+import at.hannibal2.skyhanni.api.SkillAPI.exactLevelingMap
+import at.hannibal2.skyhanni.api.SkillAPI.excludedSkills
+import at.hannibal2.skyhanni.api.SkillAPI.levelingMap
+import at.hannibal2.skyhanni.utils.Quad
+import com.google.common.base.Splitter
+import com.google.gson.JsonArray
+import io.github.moulberry.notenoughupdates.util.Constants
+import io.github.moulberry.notenoughupdates.util.Utils
+
+object SkillUtil {
+
+ val SPACE_SPLITTER = Splitter.on(" ").omitEmptyStrings().trimResults()
+ const val XP_NEEDED_FOR_60 = 111_672_425L
+
+ fun getSkillInfo(skill: SkillType): SkillAPI.SkillInfo? {
+ return SkillAPI.storage?.get(skill)
+ }
+
+ fun getSkillInfo(currentLevel: Int, currentXp: Long, neededXp: Long, totalXp: Long): Quad<Int, Long, Long, Long> {
+ return if (currentLevel == 50 && neededXp == 0L)
+ calculateOverFlow50(currentXp)
+ else if (currentLevel >= 60)
+ calculateOverFlow(currentXp)
+ else
+ Quad(currentLevel, currentXp, neededXp, totalXp)
+ }
+
+ /**
+ * @author Soopyboo32
+ */
+ fun calculateOverFlow(currentXp: Long): Quad<Int, Long, Long, Long> {
+ var xpCurrent = currentXp
+ var slope = 600000L
+ var xpForCurr = 7000000 + slope
+ var level = 60
+ var total = 0L
+ while (xpCurrent > xpForCurr) {
+ level++
+ xpCurrent -= xpForCurr
+ total += xpForCurr
+ xpForCurr += slope
+ if (level % 10 == 0) slope *= 2
+ }
+ total += xpCurrent
+ return Quad(level, xpCurrent, xpForCurr, total)
+ }
+
+ /**
+ * Calculate overflow starting at level 50
+ */
+ private fun calculateOverFlow50(currentXp: Long): Quad<Int, Long, Long, Long> {
+ var xpCurrent = currentXp
+ var level = 50
+ var total = 0L
+ var slope = 300000L
+ var xpForCurr = 4000000 + slope
+
+ while (level < 60 && xpCurrent > xpForCurr) {
+ level++
+ xpCurrent -= xpForCurr
+ total += xpForCurr
+ xpForCurr += slope
+ if (level % 10 == 0) slope *= 2
+ }
+
+ if (level >= 60) {
+ slope = 600000L
+ xpForCurr = 7000000 + slope
+ }
+
+ while (xpCurrent > xpForCurr) {
+ level++
+ xpCurrent -= xpForCurr
+ total += xpForCurr
+ xpForCurr += slope
+ if (level % 10 == 0) slope *= 2
+ }
+ total += xpCurrent
+
+ return Quad(level, xpCurrent, xpForCurr, total)
+ }
+
+ fun xpRequiredForLevel(levelWithProgress: Double): Long {
+ val level = levelWithProgress.toInt()
+ var slope = 600000L
+ var xpForCurr = 7000000 + slope
+ var totalXpRequired = 0L
+
+ for (i in 61 .. level) {
+ totalXpRequired += xpForCurr
+ xpForCurr += slope
+ if (i % 10 == 0) slope *= 2
+ }
+
+ val fractionalProgress = levelWithProgress - level
+ totalXpRequired += (xpForCurr * fractionalProgress).toLong()
+
+ val xp = if (level <= 60) {
+ levelingMap.filter { it.key < level }.values.sum().toLong()
+ } else {
+ totalXpRequired + levelingMap.values.sum()
+ }
+
+ return xp
+ }
+
+ fun getLevelExact(neededXp: Long): Int {
+ val defaultLevel = if (activeSkill in excludedSkills) 50 else 60
+ return exactLevelingMap.getOrDefault(neededXp.toInt(), defaultLevel)
+ }
+
+ fun getLevel(currentXp: Long): Int {
+ var level = 0
+ var remainingXp = currentXp
+ for ((i, v) in levelingMap) {
+ if (remainingXp >= v) {
+ remainingXp -= v
+ level++
+ }
+ }
+ return level
+ }
+
+ 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
+ }
+
+ fun levelArray(): JsonArray =
+ Utils.getElement(Constants.LEVELING, "leveling_xp").asJsonArray
+
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt
index cf851d65f..054e5e19f 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/ColorUtils.kt
@@ -1,6 +1,8 @@
package at.hannibal2.skyhanni.utils
import java.awt.Color
+import kotlin.math.max
+
object ColorUtils {
@@ -19,4 +21,12 @@ object ColorUtils {
(start.green * (1 - percent) + end.green * percent).toInt(),
(start.blue * (1 - percent) + end.blue * percent).toInt()
)
+
+ fun Color.darker(factor: Double): Color {
+ return Color(
+ max((red * factor).toInt(), 0),
+ max((green * factor).toInt(), 0),
+ max((blue * factor).toInt(), 0),
+ alpha)
+ }
}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt
index 1d0b42b92..af893d7dc 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/GuiRenderUtils.kt
@@ -1,5 +1,9 @@
package at.hannibal2.skyhanni.utils
+import at.hannibal2.skyhanni.config.features.skillprogress.SkillProgressBarConfig
+import at.hannibal2.skyhanni.features.chroma.ChromaShaderManager
+import at.hannibal2.skyhanni.features.chroma.ChromaType
+import io.github.moulberry.notenoughupdates.util.Utils
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.FontRenderer
import net.minecraft.client.gui.GuiScreen
@@ -9,6 +13,8 @@ import net.minecraft.item.ItemStack
import org.lwjgl.opengl.GL11
import java.awt.Color
import java.text.DecimalFormat
+import kotlin.math.ceil
+import kotlin.math.min
import kotlin.math.roundToInt
/**
@@ -294,4 +300,45 @@ object GuiRenderUtils {
renderItemStack(item, x, y)
GuiScreen.drawRect(x, y, x + 16, y + 16, colour)
}
+
+ // Taken and edited from NEU <- it's broken
+ fun renderTexturedBar(x: Float, y: Float, xSize: Float, completed: Float, color: Color, useChroma: Boolean, texture: SkillProgressBarConfig.TexturedBar.UsedTexture, height: Float) {
+ GlStateManager.pushMatrix()
+ GlStateManager.translate(x, y, 0f)
+ val w = xSize.toInt()
+ val w_2 = w / 2
+ val k = min(w.toDouble(), ceil((completed * w).toDouble())).toInt()
+ val vanilla = texture == SkillProgressBarConfig.TexturedBar.UsedTexture.MATCH_PACK
+ val vMinEmpty = if (vanilla) 64 / 256f else 0f
+ val vMaxEmpty = if (vanilla) 69 / 256f else .5f
+ val vMinFilled = if (vanilla) 69 / 256f else .5f
+ val vMaxFilled = if (vanilla) 74 / 256f else 1f
+
+ if (useChroma) {
+ ChromaShaderManager.begin(ChromaType.TEXTURED)
+ GlStateManager.color(Color.LIGHT_GRAY.darker().red / 255f, Color.LIGHT_GRAY.darker().green / 255f, Color.LIGHT_GRAY.darker().blue / 255f, 1f)
+ } else {
+ GlStateManager.color(color.darker().red / 255f, color.darker().green / 255f, color.darker().blue / 255f, 1f)
+ }
+
+ Utils.drawTexturedRect(x, y, w_2.toFloat(), height, 0f, w_2 / xSize, vMinEmpty, vMaxEmpty, GL11.GL_NEAREST)
+ Utils.drawTexturedRect(x + w_2, y, w_2.toFloat(), height, 1 - w_2 / xSize, 1f, vMinEmpty, vMaxEmpty, GL11.GL_NEAREST)
+
+ if (useChroma) {
+ GlStateManager.color(Color.WHITE.red / 255f, Color.WHITE.green / 255f, Color.WHITE.blue / 255f, 1f)
+ } else {
+ GlStateManager.color(color.red / 255f, color.green / 255f, color.blue / 255f, 1f)
+ }
+
+ if (k > 0) {
+ Utils.drawTexturedRect(x, y, w_2.coerceAtMost(k).toFloat(), height, 0f, w_2.toDouble().coerceAtMost(k.toDouble() / xSize).toFloat(), vMinFilled, vMaxFilled, GL11.GL_NEAREST)
+ if (completed > 0.5f) {
+ Utils.drawTexturedRect(x + w_2, y, (k - w_2).toFloat(), height, 1 - w_2 / xSize, 1 + (k - w) / xSize, vMinFilled, vMaxFilled, GL11.GL_NEAREST)
+ }
+ }
+ if (useChroma) {
+ ChromaShaderManager.end()
+ }
+ GlStateManager.popMatrix()
+ }
}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
index 77979a5aa..502cba763 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
@@ -27,12 +27,14 @@ import net.minecraft.entity.SharedMonsterAttributes
import net.minecraft.launchwrapper.Launch
import net.minecraft.util.ChatComponentText
import net.minecraftforge.fml.common.FMLCommonHandler
+import java.io.Serializable
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.Timer
import java.util.TimerTask
import java.util.regex.Matcher
import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
object LorenzUtils {
@@ -142,7 +144,7 @@ object LorenzUtils {
fun getSBMonthByName(month: String): Int {
var monthNr = 0
- for (i in 1..12) {
+ for (i in 1 .. 12) {
val monthName = SkyBlockTime.monthName(i)
if (month == monthName) {
monthNr = i
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt
index 60fb67213..4e00a85e7 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/NumberUtil.kt
@@ -3,6 +3,8 @@ package at.hannibal2.skyhanni.utils
import java.text.NumberFormat
import java.util.Locale
import java.util.TreeMap
+import kotlin.math.max
+import kotlin.math.min
import kotlin.math.pow
import kotlin.math.roundToInt
@@ -90,7 +92,7 @@ object NumberUtil {
fun Number.ordinal(): String {
val long = this.toLong()
- if (long % 100 in 11..13) return "th"
+ if (long % 100 in 11 .. 13) return "th"
return when (long % 10) {
1L -> "st"
2L -> "nd"
@@ -215,4 +217,15 @@ object NumberUtil {
fun Number.fractionOf(maxValue: Number) = maxValue.toDouble().takeIf { it != 0.0 }?.let { max ->
this.toDouble() / max
}?.coerceIn(0.0, 1.0) ?: 1.0
+
+ fun interpolate(now: Float, last: Float, lastUpdate: Long): Float {
+ var interp = now
+ if (last >= 0 && last != now) {
+ var factor: Float = (SimpleTimeMark.now().toMillis() - lastUpdate) / 1000f
+ factor = factor.coerceIn(0f, 1f)
+ interp = last + (now - last) * factor
+ }
+ return interp
+ }
+
}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/Quad.kt b/src/main/java/at/hannibal2/skyhanni/utils/Quad.kt
new file mode 100644
index 000000000..584ca99c8
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/utils/Quad.kt
@@ -0,0 +1,31 @@
+package at.hannibal2.skyhanni.utils
+
+import java.io.Serializable
+
+/**
+ * Represents a tetrad of values
+ *
+ * There is no meaning attached to values in this class, it can be used for any purpose.
+ * Quad exhibits value semantics, i.e. two quads are equal if all four components are equal.
+ *
+ * @param A type of the first value.
+ * @param B type of the second value.
+ * @param C type of the third value.
+ * @param D type of the fourth value.
+ * @property first First value.
+ * @property second Second value.
+ * @property third Third value.
+ * @property fourth Fourth value.
+ */
+data class Quad<out A, out B, out C, out D>(
+ val first: A,
+ val second: B,
+ val third: C,
+ val fourth: D
+) : Serializable {
+
+ /**
+ * Returns string representation of the [Quad] including its [first], [second], [third] and [fourth] values.
+ */
+ override fun toString(): String = "($first, $second, $third, $fourth)"
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt
index 193beda2a..914a906e5 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/RenderUtils.kt
@@ -549,7 +549,7 @@ object RenderUtils {
val z: Double =
entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * partialTicks - renderManager.viewerPosZ
val pix2 = Math.PI * 2.0
- for (i in 0..90) {
+ for (i in 0 .. 90) {
color.bindColor()
worldRenderer.pos(x + rad * cos(i * pix2 / 45.0), y + il, z + rad * sin(i * pix2 / 45.0)).endVertex()
}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt
index 5316b68cc..c1b382ba8 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/renderables/Renderable.kt
@@ -1,14 +1,18 @@
package at.hannibal2.skyhanni.utils.renderables
import at.hannibal2.skyhanni.config.core.config.gui.GuiPositionEditor
+import at.hannibal2.skyhanni.config.features.skillprogress.SkillProgressBarConfig
import at.hannibal2.skyhanni.data.ToolTipData
+import at.hannibal2.skyhanni.features.chroma.ChromaShaderManager
+import at.hannibal2.skyhanni.features.chroma.ChromaType
import at.hannibal2.skyhanni.utils.ColorUtils
+import at.hannibal2.skyhanni.utils.ColorUtils.darker
+import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.renderXAligned
+import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.renderYAligned
import at.hannibal2.skyhanni.utils.LorenzLogger
import at.hannibal2.skyhanni.utils.NEUItems.renderOnScreen
import at.hannibal2.skyhanni.utils.RenderUtils.HorizontalAlignment
import at.hannibal2.skyhanni.utils.RenderUtils.VerticalAlignment
-import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.renderXAligned
-import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.renderYAligned
import io.github.moulberry.moulconfig.gui.GuiScreenElementWrapper
import io.github.moulberry.notenoughupdates.util.Utils
import net.minecraft.client.Minecraft
@@ -16,6 +20,7 @@ import net.minecraft.client.gui.Gui
import net.minecraft.client.gui.inventory.GuiEditSign
import net.minecraft.client.renderer.GlStateManager
import net.minecraft.item.ItemStack
+import net.minecraft.util.ResourceLocation
import org.lwjgl.input.Mouse
import java.awt.Color
import java.util.Collections
@@ -29,8 +34,8 @@ interface Renderable {
val horizontalAlign: HorizontalAlignment
val verticalAlign: VerticalAlignment
fun isHovered(posX: Int, posY: Int) = currentRenderPassMousePosition?.let { (x, y) ->
- x in (posX..posX + width)
- && y in (posY..posY + height) // TODO: adjust for variable height?
+ x in (posX .. posX + width)
+ && y in (posY .. posY + height) // TODO: adjust for variable height?
} ?: false
/**
@@ -43,7 +48,6 @@ interface Renderable {
val logger = LorenzLogger("debug/renderable")
val list = mutableMapOf<Pair<Int, Int>, List<Int>>()
-
var currentRenderPassMousePosition: Pair<Int, Int>? = null
set
@@ -237,12 +241,12 @@ interface Renderable {
var isHovered = false
override fun render(posX: Int, posY: Int) {
- if (isHovered(posX, posY) && condition() && shouldAllowLink(true, bypassChecks)) {
+ isHovered = if (isHovered(posX, posY) && condition() && shouldAllowLink(true, bypassChecks)) {
hovered.render(posX, posY)
- isHovered = true
+ true
} else {
unhovered.render(posX, posY)
- isHovered = false
+ false
}
}
}
@@ -301,25 +305,82 @@ interface Renderable {
percent: Double,
startColor: Color = Color(255, 0, 0),
endColor: Color = Color(0, 255, 0),
- width: Int = 30,
- height: Int = 4,
+ useChroma: Boolean = false,
+ texture: SkillProgressBarConfig.TexturedBar.UsedTexture? = null,
+ width: Int = 182,
+ height: Int = 5,
horizontalAlign: HorizontalAlignment = HorizontalAlignment.LEFT,
- verticalAlign: VerticalAlignment = VerticalAlignment.TOP,
+ verticalAlign: VerticalAlignment = VerticalAlignment.TOP
) = object : Renderable {
override val width = width
override val height = height
override val horizontalAlign = horizontalAlign
override val verticalAlign = verticalAlign
- val progress = (1.0 + percent * (width - 2.0)).toInt()
- val color = ColorUtils.blendRGB(startColor, endColor, percent)
+ private val progress = if (texture == null) {
+ (1.0 + percent * (width - 2.0)).toInt()
+ } else {
+ percent.toInt()
+ }
+
+ private var color = if (texture == null) {
+ ColorUtils.blendRGB(startColor, endColor, percent)
+ } else {
+ startColor
+ }
override fun render(posX: Int, posY: Int) {
- Gui.drawRect(0, 0, width, height, 0xFF43464B.toInt())
- Gui.drawRect(1, 1, width - 1, height - 1, color.darker().rgb)
- Gui.drawRect(1, 1, progress, height - 1, color.rgb)
+ if (texture == null) {
+ Gui.drawRect(0, 0, width, height, 0xFF43464B.toInt())
+
+ if (useChroma) {
+ ChromaShaderManager.begin(ChromaType.STANDARD)
+ }
+
+ val factor = 0.2
+ val bgColor = if (useChroma) Color.GRAY.darker() else color
+ Gui.drawRect(1, 1, width - 1, height - 1, bgColor.darker(factor).rgb)
+ Gui.drawRect(1, 1, progress, height - 1, color.rgb)
+
+ if (useChroma) {
+ ChromaShaderManager.end()
+ }
+ } else {
+ val (textureX, textureY) = if (texture == SkillProgressBarConfig.TexturedBar.UsedTexture.MATCH_PACK) Pair(0, 64) else Pair(0, 0)
+
+ Minecraft.getMinecraft().renderEngine.bindTexture(ResourceLocation(texture.path))
+ Minecraft.getMinecraft().ingameGUI.drawTexturedModalRect(posX, posY, textureX, textureY, width, height)
+
+ if (useChroma) {
+ ChromaShaderManager.begin(ChromaType.TEXTURED)
+ GlStateManager.color(1f, 1f, 1f, 1f)
+ } else {
+ GlStateManager.color(color.red / 255f, color.green / 255f, color.blue / 255f, 1f)
+ }
+ Minecraft.getMinecraft().ingameGUI.drawTexturedModalRect(posX, posY, textureX, textureY + height, progress, height)
+
+ if (useChroma) {
+ ChromaShaderManager.end()
+ }
+ }
}
+ }
+
+ fun fixedSizeLine(
+ content: Renderable,
+ width: Int,
+ horizontalAlign: HorizontalAlignment = HorizontalAlignment.LEFT,
+ verticalAlign: VerticalAlignment = VerticalAlignment.TOP,
+ ) = object : Renderable {
+ val render = content
+ override val width = width
+ override val height = render.height
+ override val horizontalAlign = horizontalAlign
+ override val verticalAlign = verticalAlign
+ override fun render(posX: Int, posY: Int) {
+ render.renderXAligned(0, 0, width)
+ }
}
fun horizontalContainer(
@@ -345,23 +406,5 @@ interface Renderable {
GlStateManager.translate(-width.toFloat() - spacing.toFloat(), 0f, 0f)
}
}
-
- fun fixedSizeLine(
- content: Renderable,
- width: Int,
- horizontalAlign: HorizontalAlignment = HorizontalAlignment.LEFT,
- verticalAlign: VerticalAlignment = VerticalAlignment.TOP,
- ) = object : Renderable {
- val render = content
-
- override val width = width
- override val height = render.height
- override val horizontalAlign = horizontalAlign
- override val verticalAlign = verticalAlign
-
- override fun render(posX: Int, posY: Int) {
- render.renderXAligned(0, 0, width)
- }
- }
}
-}
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt b/src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt
index dedb60ef2..03b2fb3ac 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/shader/Shader.kt
@@ -69,14 +69,14 @@ abstract class Shader(val vertex: String, val fragment: String) {
if (ShaderHelper.glGetProgrami(shaderProgram, ShaderHelper.GL_LINK_STATUS) == GL11.GL_FALSE) {
val errorMessage = "Failed to link vertex shader $vertex and fragment shader $fragment. Features that " +
- "utilise this shader will not work correctly, if at all"
+ "utilise this shader will not work correctly, if at all"
val errorLog = StringUtils.trim(ShaderHelper.glGetShaderInfoLog(shaderProgram, 1024))
if (ShaderManager.inWorld()) {
ErrorManager.logErrorWithData(
- OpenGLException("Shader linking error."),
- errorMessage,
- "Link Error:\n" to errorLog
+ OpenGLException("Shader linking error."),
+ errorMessage,
+ "Link Error:\n" to errorLog
)
} else {
LorenzUtils.consoleLog("$errorMessage $errorLog")
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt b/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt
index 6e36285b1..fdc34f0ae 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderHelper.kt
@@ -139,9 +139,9 @@ class ShaderHelper {
fun glUniform2f(location: Int, v0: Float, v1: Float) {
if (USING_ARB_SHADERS) ARBShaderObjects.glUniform2fARB(location, v0, v1) else GL20.glUniform2f(
- location,
- v0,
- v1
+ location,
+ v0,
+ v1
)
}
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt b/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt
index 5621a5f87..645471087 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/shader/ShaderManager.kt
@@ -86,15 +86,15 @@ object ShaderManager {
if (ShaderHelper.glGetShaderi(shaderID, ShaderHelper.GL_COMPILE_STATUS) == 0) {
val errorMessage = "Failed to compile shader $fileName${type.extension}. Features that utilise this " +
- "shader will not work correctly, if at all"
+ "shader will not work correctly, if at all"
val errorLog = StringUtils.trim(ShaderHelper.glGetShaderInfoLog(shaderID, 1024))
if (inWorld()) {
ErrorManager.logErrorWithData(
- OpenGLException("Shader compilation error."),
- errorMessage,
- "GLSL Compilation Error:\n" to errorLog
- )
+ OpenGLException("Shader compilation error."),
+ errorMessage,
+ "GLSL Compilation Error:\n" to errorLog
+ )
} else {
LorenzUtils.consoleLog("$errorMessage $errorLog")
}