diff options
Diffstat (limited to 'src')
43 files changed, 1871 insertions, 98 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") } diff --git a/src/main/resources/assets/skyhanni/bars/1.png b/src/main/resources/assets/skyhanni/bars/1.png Binary files differnew file mode 100644 index 000000000..063b624c0 --- /dev/null +++ b/src/main/resources/assets/skyhanni/bars/1.png diff --git a/src/main/resources/assets/skyhanni/bars/2.png b/src/main/resources/assets/skyhanni/bars/2.png Binary files differnew file mode 100644 index 000000000..ca09621ab --- /dev/null +++ b/src/main/resources/assets/skyhanni/bars/2.png diff --git a/src/main/resources/assets/skyhanni/bars/3.png b/src/main/resources/assets/skyhanni/bars/3.png Binary files differnew file mode 100644 index 000000000..7a8f10b1f --- /dev/null +++ b/src/main/resources/assets/skyhanni/bars/3.png diff --git a/src/main/resources/assets/skyhanni/bars/4.png b/src/main/resources/assets/skyhanni/bars/4.png Binary files differnew file mode 100644 index 000000000..cbc7c1845 --- /dev/null +++ b/src/main/resources/assets/skyhanni/bars/4.png diff --git a/src/main/resources/assets/skyhanni/bars/5.png b/src/main/resources/assets/skyhanni/bars/5.png Binary files differnew file mode 100644 index 000000000..17367ee37 --- /dev/null +++ b/src/main/resources/assets/skyhanni/bars/5.png diff --git a/src/main/resources/assets/skyhanni/shaders/textured_chroma.vsh b/src/main/resources/assets/skyhanni/shaders/textured_chroma.vsh index 87ca9fece..eebf95c82 100644 --- a/src/main/resources/assets/skyhanni/shaders/textured_chroma.vsh +++ b/src/main/resources/assets/skyhanni/shaders/textured_chroma.vsh @@ -12,4 +12,4 @@ void main() { // Pass the color & texture coords to the fragment shader outColor = gl_Color; outTextureCoords = gl_MultiTexCoord0.st; -} +}
\ No newline at end of file diff --git a/src/main/resources/mixins.skyhanni.json b/src/main/resources/mixins.skyhanni.json index 3b1770fe6..b7763efb1 100644 --- a/src/main/resources/mixins.skyhanni.json +++ b/src/main/resources/mixins.skyhanni.json @@ -1,9 +1,9 @@ { - "package": "at.hannibal2.skyhanni.mixins.transformers", - "refmap": "mixins.skyhanni.refmap.json", - "plugin": "at.hannibal2.skyhanni.mixins.init.SkyhanniMixinPlugin", - "compatibilityLevel": "JAVA_8", - "injectors": { - "maxShiftBy": 2 - } + "package": "at.hannibal2.skyhanni.mixins.transformers", + "refmap": "mixins.skyhanni.refmap.json", + "plugin": "at.hannibal2.skyhanni.mixins.init.SkyhanniMixinPlugin", + "compatibilityLevel": "JAVA_8", + "injectors": { + "maxShiftBy": 2 + } } |