diff options
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features')
12 files changed, 764 insertions, 36 deletions
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 == "You |
