From 68abc8ade546de62aee76e0b786993fc59fbc3cc Mon Sep 17 00:00:00 2001 From: Walker Selby Date: Sat, 21 Oct 2023 07:31:19 +0100 Subject: Internal Change: Misc Refactoring (#551) Internal Change: Misc Refactoring #551 --- .../hannibal2/skyhanni/features/chat/ChatPeek.kt | 30 + .../features/chat/CompactSplashPotionMessage.kt | 32 + .../skyhanni/features/combat/BestiaryData.kt | 489 ++++++++++++ .../skyhanni/features/combat/HideDamageSplash.kt | 28 + .../features/combat/damageindicator/BossType.kt | 111 +++ .../combat/damageindicator/DamageCounter.kt | 12 + .../damageindicator/DamageIndicatorManager.kt | 862 +++++++++++++++++++++ .../features/combat/damageindicator/EntityData.kt | 30 + .../combat/damageindicator/EntityResult.kt | 8 + .../features/combat/damageindicator/MobFinder.kt | 513 ++++++++++++ .../features/combat/damageindicator/OldDamage.kt | 3 + .../features/combat/endernodetracker/EnderNode.kt | 33 + .../combat/endernodetracker/EnderNodeTracker.kt | 239 ++++++ .../features/combat/ghostcounter/GhostCounter.kt | 494 ++++++++++++ .../features/combat/ghostcounter/GhostData.kt | 90 +++ .../combat/ghostcounter/GhostFormatting.kt | 182 +++++ .../features/combat/ghostcounter/GhostUtil.kt | 144 ++++ .../features/combat/mobs/AreaMiniBossFeatures.kt | 121 +++ .../combat/mobs/AshfangMinisNametagHider.kt | 28 + .../skyhanni/features/combat/mobs/MobHighlight.kt | 99 +++ .../skyhanni/features/combat/mobs/SpawnTimers.kt | 98 +++ .../tabcomplete/GetFromSacksTabComplete.kt | 45 ++ .../commands/tabcomplete/PlayerTabComplete.kt | 86 ++ .../features/commands/tabcomplete/TabComplete.kt | 38 + .../commands/tabcomplete/WarpTabComplete.kt | 26 + .../skyhanni/features/damageindicator/BossType.kt | 111 --- .../features/damageindicator/DamageCounter.kt | 12 - .../damageindicator/DamageIndicatorManager.kt | 862 --------------------- .../features/damageindicator/EntityData.kt | 30 - .../features/damageindicator/EntityResult.kt | 8 - .../skyhanni/features/damageindicator/MobFinder.kt | 513 ------------ .../skyhanni/features/damageindicator/OldDamage.kt | 3 - .../dungeon/DungeonBossHideDamageSplash.kt | 2 +- .../event/jerry/frozentreasure/FrozenTreasure.kt | 19 + .../jerry/frozentreasure/FrozenTreasureTracker.kt | 167 ++++ .../skyhanni/features/fishing/ChumBucketHider.kt | 83 ++ .../features/fishing/SeaCreatureFeatures.kt | 2 +- .../features/fishing/ThunderSparksHighlight.kt | 77 ++ .../skyhanni/features/inventory/ChestValue.kt | 277 +++++++ .../skyhanni/features/inventory/HarpFeatures.kt | 78 ++ .../skyhanni/features/inventory/tiarelay/Relay.kt | 48 ++ .../features/inventory/tiarelay/TiaRelayHelper.kt | 146 ++++ .../inventory/tiarelay/TiaRelayWaypoints.kt | 63 ++ .../features/itemabilities/ChickenHeadTimer.kt | 75 ++ .../mining/powdertracker/PowderChestReward.kt | 148 ++++ .../features/mining/powdertracker/PowderTracker.kt | 366 +++++++++ .../skyhanni/features/misc/BestiaryData.kt | 489 ------------ .../hannibal2/skyhanni/features/misc/ChatPeek.kt | 30 - .../hannibal2/skyhanni/features/misc/ChestValue.kt | 277 ------- .../skyhanni/features/misc/ChickenHeadTimer.kt | 75 -- .../skyhanni/features/misc/ChumBucketHider.kt | 83 -- .../features/misc/CompactSplashPotionMessage.kt | 32 - .../hannibal2/skyhanni/features/misc/EnderNode.kt | 33 - .../skyhanni/features/misc/EnderNodeTracker.kt | 239 ------ .../skyhanni/features/misc/FrozenTreasure.kt | 19 - .../features/misc/FrozenTreasureTracker.kt | 167 ---- .../skyhanni/features/misc/HarpFeatures.kt | 78 -- .../skyhanni/features/misc/HideDamageSplash.kt | 28 - .../features/misc/ThunderSparksHighlight.kt | 77 -- .../features/misc/ghostcounter/GhostCounter.kt | 494 ------------ .../features/misc/ghostcounter/GhostData.kt | 90 --- .../features/misc/ghostcounter/GhostFormatting.kt | 182 ----- .../features/misc/ghostcounter/GhostUtil.kt | 144 ---- .../misc/powdertracker/PowderChestReward.kt | 148 ---- .../features/misc/powdertracker/PowderTracker.kt | 366 --------- .../misc/tabcomplete/GetFromSacksTabComplete.kt | 45 -- .../features/misc/tabcomplete/PlayerTabComplete.kt | 86 -- .../features/misc/tabcomplete/TabComplete.kt | 38 - .../features/misc/tabcomplete/WarpTabComplete.kt | 26 - .../skyhanni/features/misc/tiarelay/Relay.kt | 48 -- .../features/misc/tiarelay/TiaRelayHelper.kt | 146 ---- .../features/misc/tiarelay/TiaRelayWaypoints.kt | 63 -- .../skyhanni/features/mobs/AreaMiniBossFeatures.kt | 121 --- .../features/mobs/AshfangMinisNametagHider.kt | 28 - .../skyhanni/features/mobs/MobHighlight.kt | 99 --- .../skyhanni/features/mobs/SpawnTimers.kt | 98 --- .../features/nether/ashfang/AshfangBlazes.kt | 4 +- .../features/nether/ashfang/AshfangBlazingSouls.kt | 4 +- .../nether/ashfang/AshfangFreezeCooldown.kt | 4 +- .../features/nether/ashfang/AshfangGravityOrbs.kt | 4 +- .../nether/ashfang/AshfangHideDamageIndicator.kt | 4 +- .../nether/ashfang/AshfangHideParticles.kt | 4 +- .../nether/ashfang/AshfangNextResetCooldown.kt | 4 +- .../miniboss/DailyMiniBossHelper.kt | 2 +- .../features/slayer/SlayerMiniBossFeatures.kt | 2 +- .../features/slayer/blaze/BlazeSlayerClearView.kt | 4 +- .../slayer/blaze/BlazeSlayerFirePitsWarning.kt | 4 +- 87 files changed, 5410 insertions(+), 5410 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/features/chat/ChatPeek.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/chat/CompactSplashPotionMessage.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/BestiaryData.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/HideDamageSplash.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/BossType.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageCounter.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageIndicatorManager.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/EntityData.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/EntityResult.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/MobFinder.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/OldDamage.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/endernodetracker/EnderNode.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/endernodetracker/EnderNodeTracker.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostCounter.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostData.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostFormatting.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/ghostcounter/GhostUtil.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/mobs/AreaMiniBossFeatures.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/mobs/AshfangMinisNametagHider.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/mobs/MobHighlight.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/combat/mobs/SpawnTimers.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/GetFromSacksTabComplete.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/PlayerTabComplete.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/TabComplete.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/commands/tabcomplete/WarpTabComplete.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/damageindicator/BossType.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/damageindicator/DamageCounter.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/damageindicator/DamageIndicatorManager.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/damageindicator/EntityData.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/damageindicator/EntityResult.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/damageindicator/MobFinder.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/damageindicator/OldDamage.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/event/jerry/frozentreasure/FrozenTreasure.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/event/jerry/frozentreasure/FrozenTreasureTracker.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/fishing/ChumBucketHider.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/fishing/ThunderSparksHighlight.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/ChestValue.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/HarpFeatures.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/tiarelay/Relay.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/tiarelay/TiaRelayHelper.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/inventory/tiarelay/TiaRelayWaypoints.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/itemabilities/ChickenHeadTimer.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/mining/powdertracker/PowderChestReward.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/mining/powdertracker/PowderTracker.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/BestiaryData.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/ChatPeek.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/ChestValue.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/ChickenHeadTimer.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/ChumBucketHider.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/CompactSplashPotionMessage.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/EnderNode.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/EnderNodeTracker.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/FrozenTreasure.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/FrozenTreasureTracker.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/HarpFeatures.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/HideDamageSplash.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/ThunderSparksHighlight.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/ghostcounter/GhostCounter.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/ghostcounter/GhostData.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/ghostcounter/GhostFormatting.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/ghostcounter/GhostUtil.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/powdertracker/PowderChestReward.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/powdertracker/PowderTracker.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/GetFromSacksTabComplete.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/PlayerTabComplete.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/TabComplete.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/tabcomplete/WarpTabComplete.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/tiarelay/Relay.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/tiarelay/TiaRelayHelper.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/tiarelay/TiaRelayWaypoints.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/mobs/AreaMiniBossFeatures.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/mobs/AshfangMinisNametagHider.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/mobs/MobHighlight.kt delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/mobs/SpawnTimers.kt (limited to 'src/main/java/at/hannibal2/skyhanni/features') diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/ChatPeek.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/ChatPeek.kt new file mode 100644 index 000000000..5d5580772 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/chat/ChatPeek.kt @@ -0,0 +1,30 @@ +package at.hannibal2.skyhanni.features.chat + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.GuiEditManager +import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyHeld +import at.hannibal2.skyhanni.features.garden.fortuneguide.FFGuideGUI +import at.hannibal2.skyhanni.features.misc.visualwords.VisualWordGui +import at.hannibal2.skyhanni.utils.NEUItems +import io.github.moulberry.moulconfig.gui.GuiScreenElementWrapper +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.inventory.GuiEditSign +import org.lwjgl.input.Keyboard + + +object ChatPeek { + @JvmStatic + fun peek(): Boolean { + val key = SkyHanniMod.feature.chat.peekChat + + if (Minecraft.getMinecraft().thePlayer == null) return false + if (key <= Keyboard.KEY_NONE) return false + if (Minecraft.getMinecraft().currentScreen is GuiEditSign) return false + if (Minecraft.getMinecraft().currentScreen is GuiScreenElementWrapper) return false + + if (NEUItems.neuHasFocus()) return false + if (GuiEditManager.isInGui() || FFGuideGUI.isInGui() || VisualWordGui.isInGui()) return false + + return key.isKeyHeld() + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/CompactSplashPotionMessage.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/CompactSplashPotionMessage.kt new file mode 100644 index 000000000..dfc0db3f9 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/chat/CompactSplashPotionMessage.kt @@ -0,0 +1,32 @@ +package at.hannibal2.skyhanni.features.chat + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import net.minecraft.util.ChatComponentText +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class CompactSplashPotionMessage { + private val potionEffectPattern = + "§a§lBUFF! §fYou have gained §r(?.*)§r§f! Press TAB or type /effects to view your active effects!".toPattern() + private val potionEffectOthersPattern = + "§a§lBUFF! §fYou were splashed by (?.*) §fwith §r(?.*)§r§f! Press TAB or type /effects to view your active effects!".toPattern() + + @SubscribeEvent + fun onChatMessage(event: LorenzChatEvent) { + if (!LorenzUtils.inSkyBlock || !SkyHanniMod.feature.chat.compactPotionMessage) return + + potionEffectPattern.matchMatcher(event.message) { + val name = group("name") + event.chatComponent = ChatComponentText("§a§lPotion Effect! §r$name") + } + + potionEffectOthersPattern.matchMatcher(event.message) { + val playerName = group("playerName").removeColor() + val effectName = group("effectName") + event.chatComponent = ChatComponentText("§a§lPotion Effect! §r$effectName by §b$playerName") + } + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/BestiaryData.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/BestiaryData.kt new file mode 100644 index 000000000..5f0a4125e --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/BestiaryData.kt @@ -0,0 +1,489 @@ +package at.hannibal2.skyhanni.features.combat + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator +import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.InventoryCloseEvent +import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent +import at.hannibal2.skyhanni.utils.InventoryUtils +import at.hannibal2.skyhanni.utils.ItemUtils.getLore +import at.hannibal2.skyhanni.utils.LorenzColor +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.addAsSingletonList +import at.hannibal2.skyhanni.utils.LorenzUtils.addButton +import at.hannibal2.skyhanni.utils.NumberUtil +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.formatNumber +import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimalIfNeeded +import at.hannibal2.skyhanni.utils.NumberUtil.roundToPrecision +import at.hannibal2.skyhanni.utils.NumberUtil.toRoman +import at.hannibal2.skyhanni.utils.RenderUtils.highlight +import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems +import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import at.hannibal2.skyhanni.utils.renderables.Renderable +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + + +object BestiaryData { + + private val config get() = SkyHanniMod.feature.combat.bestiary + private var display = emptyList>() + private val mobList = mutableListOf() + private val stackList = mutableMapOf() + private val catList = mutableListOf() + private val progressPattern = "(?[0-9kKmMbB,.]+)/(?[0-9kKmMbB,.]+$)".toPattern() + private val titlePattern = "^(?:\\(\\d+/\\d+\\) )?(Bestiary|.+) ➜ (.+)$".toPattern() + private var inInventory = false + private var isCategory = false + private var indexes = listOf( + 10, 11, 12, 13, 14, 15, 16, + 19, 20, 21, 22, 23, 24, 25, + 28, 29, 30, 31, 32, 33, 34, + 37, 38, 39, 40, 41, 42, 43 + ) + + @SubscribeEvent + fun onBackgroundDraw(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) { + if (!isEnabled()) return + if (inInventory) { + config.position.renderStringsAndItems( + display, + extraSpace = -1, + itemScale = 1.3, + posLabel = "Bestiary Data" + ) + } + } + + @SubscribeEvent + fun onRender(event: GuiContainerEvent.BackgroundDrawnEvent) { + if (!isEnabled()) return + if (inInventory) { + for (slot in InventoryUtils.getItemsInOpenChest()) { + val stack = slot.stack + val lore = stack.getLore() + if (lore.any { it == "§7Overall Progress: §b100% §7(§c§lMAX!§7)" || it == "§7Families Completed: §a100%" }) { + slot highlight LorenzColor.GREEN + } + } + } + } + + @SubscribeEvent + fun onInventoryOpen(event: InventoryFullyOpenedEvent) { + if (!isEnabled()) return + val inventoryName = event.inventoryName + val stack = event.inventoryItems[4] ?: return + if ((inventoryName == "Bestiary ➜ Fishing" || inventoryName == "Bestiary") || isBestiaryGui( + stack, + inventoryName + ) + ) { + isCategory = inventoryName == "Bestiary ➜ Fishing" || inventoryName == "Bestiary" + stackList.putAll(event.inventoryItems) + inInventory = true + update() + } + } + + @SubscribeEvent + fun onInventoryClose(event: InventoryCloseEvent) { + mobList.clear() + stackList.clear() + inInventory = false + } + + @SubscribeEvent + fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) { + event.move(2, "misc.bestiaryData", "combat.bestiary") + } + + private fun update() { + display = drawDisplay() + } + + private fun init() { + mobList.clear() + catList.clear() + if (isCategory) { + inCategory() + } else { + notInCategory() + } + } + + private fun inCategory() { + for ((index, stack) in stackList) { + if (stack.displayName == " ") continue + if (!indexes.contains(index)) continue + inInventory = true + val name = stack.displayName + var familiesFound: Long = 0 + var totalFamilies: Long = 0 + var familiesCompleted: Long = 0 + for ((lineIndex, loreLine) in stack.getLore().withIndex()) { + val line = loreLine.removeColor() + if (!line.startsWith(" ")) continue + val previousLine = stack.getLore()[lineIndex - 1] + val progress = line.substring(line.lastIndexOf(' ') + 1) + if (previousLine.contains("Families Found")) { + progressPattern.matchMatcher(progress) { + familiesFound = group("current").formatNumber() + totalFamilies = group("needed").formatNumber() + } + } else if (previousLine.contains("Families Completed")) { + progressPattern.matchMatcher(progress) { + familiesCompleted = group("current").formatNumber() + } + } + } + catList.add(Category(name, familiesFound, totalFamilies, familiesCompleted)) + } + } + + private fun notInCategory() { + for ((index, stack) in stackList) { + if (stack.displayName == " ") continue + if (!indexes.contains(index)) continue + inInventory = true + val name = " [IVX0-9]+$".toPattern().matcher(stack.displayName).replaceFirst("") + val level = " ([IVX0-9]+$)".toRegex().find(stack.displayName)?.groupValues?.get(1) ?: "0" + var totalKillToMax: Long = 0 + var currentTotalKill: Long = 0 + var totalKillToTier: Long = 0 + var currentKillToTier: Long = 0 + var actualRealTotalKill: Long = 0 + for ((lineIndex, line) in stack.getLore().withIndex()) { + val loreLine = line.removeColor() + if (loreLine.startsWith("Kills: ")) { + actualRealTotalKill = + "([0-9,.]+)".toRegex().find(loreLine)?.groupValues?.get(1)?.formatNumber() + ?: 0 + } + if (!loreLine.startsWith(" ")) continue + val previousLine = stack.getLore()[lineIndex - 1] + val progress = loreLine.substring(loreLine.lastIndexOf(' ') + 1) + if (previousLine.contains("Progress to Tier")) { + progressPattern.matchMatcher(progress) { + totalKillToTier = group("needed").formatNumber() + currentKillToTier = group("current").formatNumber() + } + } else if (previousLine.contains("Overall Progress")) { + progressPattern.matchMatcher(progress) { + totalKillToMax = group("needed").formatNumber() + currentTotalKill = group("current").formatNumber() + } + } + } + mobList.add( + BestiaryMob( + name, + level, + totalKillToMax, + currentTotalKill, + totalKillToTier, + currentKillToTier, + actualRealTotalKill + ) + ) + } + } + + private fun drawDisplay(): List> { + val newDisplay = mutableListOf>() + init() + + addCategories(newDisplay) + + if (mobList.isEmpty()) return newDisplay + + addList(newDisplay) + + addButtons(newDisplay) + + return newDisplay + } + + private fun sortMobList(): MutableList { + val sortedMobList = when (config.displayType) { + 0 -> mobList.sortedBy { it.percentToMax() } + 1 -> mobList.sortedBy { it.percentToTier() } + 2 -> mobList.sortedBy { it.totalKills } + 3 -> mobList.sortedByDescending { it.totalKills } + 4 -> mobList.sortedBy { it.killNeededToMax() } + 5 -> mobList.sortedByDescending { it.killNeededToMax() } + 6 -> mobList.sortedBy { it.killNeededToNextLevel() } + 7 -> mobList.sortedByDescending { it.killNeededToNextLevel() } + else -> mobList.sortedBy { it.totalKills } + }.toMutableList() + return sortedMobList + } + + private fun addList(newDisplay: MutableList>) { + val sortedMobList = sortMobList() + + newDisplay.addAsSingletonList("§7Bestiary Data") + for (mob in sortedMobList) { + val isUnlocked = mob.totalKills != 0.toLong() + val isMaxed = mob.percentToMax() >= 1 + if (!isUnlocked) { + newDisplay.add(buildList { + add(" §7- ") + add("${mob.name}: §cNot unlocked!") + }) + continue + } + if (isMaxed && config.hideMaxed) continue + val text = getMobLine(mob, isMaxed) + val tips = getMobHover(mob) + newDisplay.addAsSingletonList(Renderable.hoverTips(text, tips) { true }) + } + } + + private fun getMobHover(mob: BestiaryMob) = listOf( + "§6Name: §b${mob.name}", + "§6Level: §b${mob.level} ${if (!config.replaceRoman) "§7(${mob.level.romanToDecimalIfNeeded()})" else ""}", + "§6Total Kills: §b${mob.actualRealTotalKill.formatNumber()}", + "§6Kills needed to max: §b${mob.killNeededToMax().formatNumber()}", + "§6Kills needed to next lvl: §b${mob.killNeededToNextLevel().formatNumber()}", + "§6Current kill to next level: §b${mob.currentKillToNextLevel.formatNumber()}", + "§6Kill needed for next level: §b${mob.killNeededForNextLevel.formatNumber()}", + "§6Current kill to max: §b${mob.killToMax.formatNumber()}", + "§6Percent to max: §b${mob.percentToMaxFormatted()}", + "§6Percent to tier: §b${mob.percentToTierFormatted()}", + "", + "§7More info thing" + ) + + private fun getMobLine( + mob: BestiaryMob, + isMaxed: Boolean + ): String { + val displayType = config.displayType + var text = "" + text += " §7- " + text += "${mob.name} ${mob.level.romanOrInt()} " + text += if (isMaxed) { + "§c§lMAXED! §7(§b${mob.actualRealTotalKill.formatNumber()}§7 kills)" + } else { + when (displayType) { + 0, 1 -> { + val currentKill = when (displayType) { + 0 -> mob.totalKills + 1 -> mob.currentKillToNextLevel + else -> 0 + } + val killNeeded = when (displayType) { + 0 -> mob.killToMax + 1 -> mob.killNeededForNextLevel + else -> 0 + } + "§7(§b${currentKill.formatNumber()}§7/§b${killNeeded.formatNumber()}§7) §a${ + ((currentKill.toDouble() / killNeeded) * 100).roundToPrecision( + 2 + ) + }§6% ${if (displayType == 1) "§ato level ${mob.getNextLevel()}" else ""}" + } + + 2, 3 -> { + + "§6${mob.totalKills.formatNumber()} §7total kills" + } + + 4, 5 -> { + "§6${mob.killNeededToMax().formatNumber()} §7kills needed" + } + + 6, 7 -> { + "§6${mob.killNeededToNextLevel().formatNumber()} §7kills needed" + } + + else -> "§cYou are not supposed to see this, please report it to @HiZe on discord!" + } + } + return text + } + + private fun addButtons(newDisplay: MutableList>) { + newDisplay.addButton( + prefix = "§7Number Format: ", + getName = FormatType.entries[config.numberFormat].type, + onChange = { + config.numberFormat = (config.numberFormat + 1) % 2 + update() + }) + + newDisplay.addButton( + prefix = "§7Display Type: ", + getName = DisplayType.entries[config.displayType].type, + onChange = { + config.displayType = (config.displayType + 1) % 8 + update() + }) + + newDisplay.addButton( + prefix = "§7Number Type: ", + getName = NumberType.entries[if (config.replaceRoman) 0 else 1].type, + onChange = { + config.replaceRoman = !config.replaceRoman + update() + } + ) + newDisplay.addButton( + prefix = "§7Hide Maxed: ", + getName = HideMaxed.entries[if (config.hideMaxed) 1 else 0].type, + onChange = { + config.hideMaxed = !config.hideMaxed + update() + } + ) + } + + private fun addCategories(newDisplay: MutableList>) { + if (catList.isNotEmpty()) { + newDisplay.addAsSingletonList("§7Category") + for (cat in catList) { + newDisplay.add(buildList { + add(" §7- ${cat.name}§7: ") + val element = when { + cat.familiesCompleted == cat.totalFamilies -> "§c§lCompleted!" + cat.familiesFound == cat.totalFamilies -> "§b${cat.familiesCompleted}§7/§b${cat.totalFamilies} §7completed" + cat.familiesFound < cat.totalFamilies -> + "§b${cat.familiesFound}§7/§b${cat.totalFamilies} §7found, §b${cat.familiesCompleted}§7/§b${cat.totalFamilies} §7completed" + + else -> continue + } + add(element) + }) + } + } + } + + private fun isBestiaryGui(stack: ItemStack?, name: String): Boolean { + if (stack == null) return false + val bestiaryGuiTitleMatcher = titlePattern.matcher(name) + if (bestiaryGuiTitleMatcher.matches()) { + if ("Bestiary" != bestiaryGuiTitleMatcher.group(1)) { + var loreContainsFamiliesFound = false + for (line in stack.getLore()) { + if (line.removeColor().startsWith("Families Found")) { + loreContainsFamiliesFound = true + break + } + } + if (!loreContainsFamiliesFound) { + return false + } + } + return true + } else if (name == "Search Results") { + val loreList = stack.getLore() + if (loreList.size >= 2 && loreList[0].startsWith("§7Query: §a") + && loreList[1].startsWith("§7Results: §a") + ) { + return true + } + } + return false + } + + enum class FormatType(val type: String) { + SHORT("Short"), + LONG("Long") + } + + enum class NumberType(val type: String) { + INT("Normal (1, 2, 3)"), + ROMAN("Roman (I, II, III") + } + + enum class DisplayType(val type: String) { + GLOBAL_MAX("Global display (to max)"), + GLOBAL_TIER("Global display (to next tier)"), + LOWEST_TOTAL("Lowest total kills"), + HIGHEST_TOTAL("Highest total kills"), + LOWEST_NEEDED_MAX("Lowest kills needed to max"), + HIGHEST_NEEDED_MAX("Highest kills needed to max"), + LOWEST_NEEDED_TIER("Lowest kills needed to next tier"), + HIGHEST_NEEDED_TIER("Highest kills needed to next tier"), + } + + enum class HideMaxed(val type: String) { + NO("Show"), + YES("Hide") + } + + private fun Long.formatNumber(): String = when (config.numberFormat) { + 0 -> NumberUtil.format(this) + 1 -> this.addSeparators() + else -> "0" + } + + data class Category( + val name: String, + val familiesFound: Long, + val totalFamilies: Long, + val familiesCompleted: Long + ) + + data class BestiaryMob( + var name: String, + var level: String, + var killToMax: Long, + var totalKills: Long, + var killNeededForNextLevel: Long, + var currentKillToNextLevel: Long, + var actualRealTotalKill: Long + ) { + + fun killNeededToMax(): Long { + return 0L.coerceAtLeast(killToMax - totalKills) + } + + fun killNeededToNextLevel(): Long { + return 0L.coerceAtLeast(killNeededForNextLevel - currentKillToNextLevel) + } + + fun percentToMax() = totalKills.toDouble() / killToMax + + fun percentToMaxFormatted() = LorenzUtils.formatPercentage(percentToMax()) + + fun percentToTier() = currentKillToNextLevel.toDouble() / killNeededForNextLevel + + fun percentToTierFormatted() = LorenzUtils.formatPercentage(percentToTier()) + + fun getNextLevel() = level.getNextLevel() + } + + + private fun String.romanOrInt() = romanToDecimalIfNeeded().let { + if (config.replaceRoman || it == 0) it.toString() else it.toRoman() + } + + fun Any.getNextLevel(): String { + return when (this) { + is Int -> { + (this + 1).toString().romanOrInt() + } + + is String -> { + if (this == "0") { + "I".romanOrInt() + } else { + val intValue = romanToDecimalIfNeeded() + (intValue + 1).toRoman().romanOrInt() + } + } + + else -> { + "Unsupported type: ${this::class.simpleName}" + } + } + } + + private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled + +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/HideDamageSplash.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/HideDamageSplash.kt new file mode 100644 index 000000000..7b2684c40 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/HideDamageSplash.kt @@ -0,0 +1,28 @@ +package at.hannibal2.skyhanni.features.combat + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator +import at.hannibal2.skyhanni.features.combat.damageindicator.DamageIndicatorManager +import at.hannibal2.skyhanni.utils.LorenzUtils +import net.minecraft.entity.EntityLivingBase +import net.minecraftforge.client.event.RenderLivingEvent +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class HideDamageSplash { + + @SubscribeEvent(priority = EventPriority.HIGH) + fun onRenderDamage(event: RenderLivingEvent.Specials.Pre) { + if (!LorenzUtils.inSkyBlock) return + if (!SkyHanniMod.feature.combat.hideDamageSplash) return + + if (DamageIndicatorManager.isDamageSplash(event.entity)) { + event.isCanceled = true + } + } + + @SubscribeEvent + fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) { + event.move(2, "misc.hideDamageSplash", "combat.hideDamageSplash") + } +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/BossType.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/BossType.kt new file mode 100644 index 000000000..664aa63d4 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/BossType.kt @@ -0,0 +1,111 @@ +package at.hannibal2.skyhanni.features.combat.damageindicator + +enum class BossType( + val fullName: String, + val bossTypeToggle: Int, + val shortName: String = fullName, + val showDeathTime: Boolean = false +) { + GENERIC_DUNGEON_BOSS("Generic Dungeon boss", 0),//TODO split into different bosses + + //Nether Mini Bosses + NETHER_BLADESOUL("§8Bladesoul", 1), + NETHER_MAGMA_BOSS("§4Magma Boss", 1), + NETHER_ASHFANG("§cAshfang", 1), + NETHER_BARBARIAN_DUKE("§eBarbarian Duke", 1), + NETHER_MAGE_OUTLAW("§5Mage Outlaw", 1), + + NETHER_VANQUISHER("§5Vanquisher", 2), + + END_ENDSTONE_PROTECTOR("§cEndstone Protector", 3), + END_ENDER_DRAGON("Ender Dragon", 4),//TODO fix totally + + SLAYER_ZOMBIE_1("§aRevenant Horror 1", 5, "§aRev 1", showDeathTime = true), + SLAYER_ZOMBIE_2("§eRevenant Horror 2", 5, "§eRev 2", showDeathTime = true), + SLAYER_ZOMBIE_3("§cRevenant Horror 3", 5, "§cRev 3", showDeathTime = true), + SLAYER_ZOMBIE_4("§4Revenant Horror 4", 5, "§4Rev 4", showDeathTime = true), + SLAYER_ZOMBIE_5("§5Revenant Horror 5", 5, "§5Rev 5", showDeathTime = true), + + SLAYER_SPIDER_1("§aTarantula Broodfather 1", 6, "§aTara 1", showDeathTime = true), + SLAYER_SPIDER_2("§eTarantula Broodfather 2", 6, "§eTara 2", showDeathTime = true), + SLAYER_SPIDER_3("§cTarantula Broodfather 3", 6, "§cTara 3", showDeathTime = true), + SLAYER_SPIDER_4("§4Tarantula Broodfather 4", 6, "§4Tara 4", showDeathTime = true), + + SLAYER_WOLF_1("§aSven Packmaster 1", 7, "§aSven 1", showDeathTime = true), + SLAYER_WOLF_2("§eSven Packmaster 2", 7, "§eSven 2", showDeathTime = true), + SLAYER_WOLF_3("§cSven Packmaster 3", 7, "§cSven 3", showDeathTime = true), + SLAYER_WOLF_4("§4Sven Packmaster 4", 7, "§4Sven 4", showDeathTime = true), + + SLAYER_ENDERMAN_1("§aVoidgloom Seraph 1", 8, "§aVoid 1", showDeathTime = true), + SLAYER_ENDERMAN_2("§eVoidgloom Seraph 2", 8, "§eVoid 2", showDeathTime = true), + SLAYER_ENDERMAN_3("§cVoidgloom Seraph 3", 8, "§cVoid 3", showDeathTime = true), + SLAYER_ENDERMAN_4("§4Voidgloom Seraph 4", 8, "§4Void 4", showDeathTime = true), + + SLAYER_BLAZE_1("§aInferno Demonlord 1", 9, "§aInferno 1", showDeathTime = true), + SLAYER_BLAZE_2("§aInferno Demonlord 2", 9, "§aInferno 2", showDeathTime = true), + SLAYER_BLAZE_3("§aInferno Demonlord 3", 9, "§aInferno 3", showDeathTime = true), + SLAYER_BLAZE_4("§aInferno Demonlord 4", 9, "§aInferno 4", showDeathTime = true), + + SLAYER_BLAZE_TYPHOEUS_1("§aInferno Typhoeus 1", 9, "§aTyphoeus 1"), + SLAYER_BLAZE_TYPHOEUS_2("§eInferno Typhoeus 2", 9, "§eTyphoeus 2"), + SLAYER_BLAZE_TYPHOEUS_3("§cInferno Typhoeus 3", 9, "§cTyphoeus 3"), + SLAYER_BLAZE_TYPHOEUS_4("§cInferno Typhoeus 4", 9, "§cTyphoeus 4"), + + SLAYER_BLAZE_QUAZII_1("§aInferno Quazii 1", 9, "§aQuazii 1"), + SLAYER_BLAZE_QUAZII_2("§eInferno Quazii 2", 9, "§eQuazii 2"), + SLAYER_BLAZE_QUAZII_3("§cInferno Quazii 3", 9, "§cQuazii 3"), + SLAYER_BLAZE_QUAZII_4("§cInferno Quazii 4", 9, "§cQuazii 4"), + + SLAYER_BLOODFIEND_1("§aRiftstalker Bloodfiend 1", 23, "§aBlood 1", showDeathTime = true), + SLAYER_BLOODFIEND_2("§6Riftstalker Bloodfiend 2", 23, "§6Blood 2", showDeathTime = true), + SLAYER_BLOODFIEND_3("§cRiftstalker Bloodfiend 3", 23, "§cBlood 3", showDeathTime = true), + SLAYER_BLOODFIEND_4("§4Riftstalker Bloodfiend 4", 23, "§4Blood 4", showDeathTime = true), + SLAYER_BLOODFIEND_5("§5Riftstalker Bloodfiend 5", 23, "§5Blood 5", showDeathTime = true), + + HUB_HEADLESS_HORSEMAN("§6Headless Horseman", 10), + + DUNGEON_F1("", 11), + DUNGEON_F2("", 12), + DUNGEON_F3("", 13), + DUNGEON_F4_THORN("§cThorn", 14), + DUNGEON_F5("§dLivid", 15), + DUNGEON_F("", 16), + DUNGEON_75("", 17), + + MINOS_INQUISITOR("§5Minos Inquisitor", 18), + MINOS_CHAMPION("§2Minos Champion", 18), + GAIA_CONSTURUCT("§2Gaia Construct", 18), + MINOTAUR("§2Minotaur", 18), + + THUNDER("§cThunder", 19), + LORD_JAWBUS("§cLord Jawbus", 19), + + DUMMY("Dummy", 20), + ARACHNE_SMALL("§cSmall Arachne", 21), + ARACHNE_BIG("§4Big Arachne", 21), + + // The Rift + LEECH_SUPREME("§cLeech Supreme", 22), + BACTE("§aBacte", 22), + + WINTER_REINDRAKE("Reindrake", 24),//TODO fix totally + + //TODO arachne + + //TODO corelone + //TODO bal + + + /** + * TODO dungeon mini bosses + * shadow assassin + * lost adventurer + * frozen adventurer + * king midas + * in blood room: bonzo, scarf, ?? + * f7 blood room giants + * + */ + + //TODO diana mythological creatures +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageCounter.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageCounter.kt new file mode 100644 index 000000000..1595b728a --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageCounter.kt @@ -0,0 +1,12 @@ +package at.hannibal2.skyhanni.features.combat.damageindicator + +import java.util.LinkedList + +class DamageCounter { + + var currentDamage = 0L + var currentHealing = 0L + var oldDamages = LinkedList() + var firstTick = 0L + +} \ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageIndicatorManager.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageIndicatorManager.kt new file mode 100644 index 000000000..7735c5835 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/combat/damageindicator/DamageIndicatorManager.kt @@ -0,0 +1,862 @@ +package at.hannibal2.skyhanni.features.combat.damageindicator + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator +import at.hannibal2.skyhanni.data.ScoreboardData +import at.hannibal2.skyhanni.events.BossHealthChangeEvent +import at.hannibal2.skyhanni.events.DamageIndicatorDetectedEvent +import at.hannibal2.skyhanni.events.DamageIndicatorFinalBossEvent +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent +import at.hannibal2.skyhanni.events.LorenzTickEvent +import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent +import at.hannibal2.skyhanni.features.dungeon.DungeonAPI +import at.hannibal2.skyhanni.features.slayer.blaze.HellionShield +import at.hannibal2.skyhanni.features.slayer.blaze.setHellionShield +import at.hannibal2.skyhanni.utils.EntityUtils +import at.hannibal2.skyhanni.utils.EntityUtils.getNameTagWith +import at.hannibal2.skyhanni.utils.EntityUtils.hasNameTagWith +import at.hannibal2.skyhanni.utils.LocationUtils +import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer +import at.hannibal2.skyhanni.utils.LorenzColor +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.baseMaxHealth +import at.hannibal2.skyhanni.utils.LorenzUtils.between +import at.hannibal2.skyhanni.utils.LorenzUtils.editCopy +import at.hannibal2.skyhanni.utils.LorenzUtils.put +import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.LorenzVec +import at.hannibal2.skyhanni.utils.NumberUtil +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import at.hannibal2.skyhanni.utils.TimeUtils +import at.hannibal2.skyhanni.utils.getLorenzVec +import net.minecraft.client.Minecraft +import net.minecraft.client.entity.EntityOtherPlayerMP +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.entity.EntityLiving +import net.minecraft.entity.EntityLivingBase +import net.minecraft.entity.item.EntityArmorStand +import net.minecraft.entity.monster.EntityEnderman +import net.minecraft.entity.monster.EntityMagmaCube +import net.minecraft.entity.monster.EntityZombie +import net.minecraft.entity.passive.EntityWolf +import net.minecraftforge.client.event.RenderLivingEvent +import net.minecraftforge.event.entity.EntityJoinWorldEvent +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import java.util.UUID +import kotlin.math.max + +class DamageIndicatorManager { + + private var mobFinder: MobFinder? = null + private val maxHealth = mutableMapOf() + private val config get() = SkyHanniMod.feature.combat.damageIndicator + + companion object { + private var data = mapOf() + private val damagePattern = "[✧✯]?(\\d+[⚔+✧❤♞☄✷ﬗ✯]*)".toPattern() + + fun isBoss(entity: EntityLivingBase) = data.values.any { it.entity == entity } + + fun isDamageSplash(entity: EntityLivingBase): Boolean { + if (entity.ticksExisted > 300 || entity !is EntityArmorStand) return false + if (!entity.hasCustomName()) return false + if (entity.isDead) return false + val name = entity.customNameTag.removeColor().replace(",", "") + + return damagePattern.matcher(name).matches() + } + + fun isBossSpawned(type: BossType) = data.entries.find { it.value.bossType == type } != null + + fun isBossSpawned(vararg types: BossType) = types.any { isBossSpawned(it) } + + fun getDistanceTo(vararg types: BossType): Double { + val playerLocation = LocationUtils.playerLocation() + return data.values.filter { it.bossType in types } + .map { it.entity.getLorenzVec().distance(playerLocation) } + .let { list -> + if (list.isEmpty()) Double.MAX_VALUE else list.minOf { it } + } + } + + fun getNearestDistanceTo(location: LorenzVec): Double { + return data.values + .map { it.entity.getLorenzVec() } + .minOfOrNull { it.distance(location) } ?: Double.MAX_VALUE + } + } + + @SubscribeEvent + fun onWorldChange(event: LorenzWorldChangeEvent) { + mobFinder = MobFinder() + data = emptyMap() + } + + @SubscribeEvent(receiveCanceled = true) + fun onChatMessage(event: LorenzChatEvent) { + mobFinder?.handleChat(event.message) + } + + @SubscribeEvent + fun onWorldRender(event: LorenzRenderWorldEvent) { + if (!isEnabled()) return + + GlStateManager.disableDepth() + GlStateManager.disableCull() + + val player = Minecraft.getMinecraft().thePlayer + + //TODO config to define between 100ms and 5 sec + val filter = data.filter { + val waitForRemoval = if (it.value.dead && !noDeathDisplay(it.value.bossType)) 4_000 else 100 + (System.currentTimeMillis() > it.value.timeLastTick + waitForRemoval) || (it.value.dead && noDeathDisplay(it.value.bossType)) + } + if (filter.isNotEmpty()) { + data = data.editCopy { + for (entry in filter) { + remove(entry.key) + } + } + } + + val sizeHealth: Double + val sizeNameAbove: Double + val sizeBossName: Double + val sizeFinalResults: Double + val smallestDistanceVew: Double + val thirdPersonView = Minecraft.getMinecraft().gameSettings.thirdPersonView + // 0 == normal + // 1 == f3 behind + // 2 == selfie + if (thirdPersonView == 1) { + sizeHealth = 2.8 + sizeNameAbove = 2.2 + sizeBossName = 2.4 + sizeFinalResults = 1.8 + + smallestDistanceVew = 10.0 + } else { + sizeHealth = 1.9 + sizeNameAbove = 1.8 + sizeBossName = 2.1 + sizeFinalResults = 1.4 + + smallestDistanceVew = 6.0 + } + + for (data in data.values) { + + //TODO test end stone protector in hole? - maybe change eye pos +// data.ignoreBlocks = +// data.bossType == BossType.END_ENDSTONE_PROTECTOR && Minecraft.getMinecraft().thePlayer.isSneaking + + if (!data.ignoreBlocks && !player.canEntityBeSeen(data.entity)) continue + if (data.bossType.bossTypeToggle !in config.bossesToShow) continue + + val entity = data.entity + + var healthText = data.healthText + val delayedStart = data.delayedStart + if (delayedStart != -1L && delayedStart > System.currentTimeMillis()) { + val delay = delayedStart - System.currentTimeMillis() + healthText = formatDelay(delay) + } + + val location = if (data.dead && data.deathLocation != null) { + data.deathLocation!! + } else { + val loc = entity.getLorenzVec() + if (data.dead) data.deathLocation = loc + loc + }.add(-0.5, 0.0, -0.5) + + + event.drawDynamicText(location, healthText, sizeHealth, smallestDistanceVew = smallestDistanceVew) + + if (data.nameAbove.isNotEmpty()) { + event.drawDynamicText( + location, + data.nameAbove, + sizeNameAbove, + -18f, + smallestDistanceVew = smallestDistanceVew + ) + } + + var bossName = when (config.bossName) { + 0 -> "" + 1 -> data.bossType.fullName + 2 -> data.bossType.shortName + else -> data.bossType.fullName + } + + if (data.namePrefix.isNotEmpty()) { + bossName = data.namePrefix + bossName + } + if (data.nameSuffix.isNotEmpty()) { + bossName += data.nameSuffix + } + event.drawDynamicText(location, bossName, sizeBossName, -9f, smallestDistanceVew = smallestDistanceVew) + + if (config.showDamageOverTime) { + var diff = 13f + val currentDamage = data.damageCounter.currentDamage + val currentHealing = data.damageCounter.currentHealing + if (currentDamage != 0L || currentHealing != 0L) { + val formatDamage = "§c" + NumberUtil.format(currentDamage) + val formatHealing = "§a+" + NumberUtil.format(currentHealing) + val finalResult = if (currentHealing == 0L) { + formatDamage + } else if (currentDamage == 0L) { + formatHealing + } else { + "$formatDamage §7/ $formatHealing" + } + event.drawDynamicText( + location, + finalResult, + sizeFinalResults, + diff, + smallestDistanceVew = smallestDistanceVew + ) + diff += 9f + } + for (damage in data.damageCounter.oldDamages) { + val formatDamage = "§c" + NumberUtil.format(damage.damage) + "/s" + val formatHealing = "§a+" + NumberUtil.format(damage.healing) + "/s" + val finalResult = if (damage.healing == 0L) { + formatDamage + } else if (damage.damage == 0L) { + formatHealing + } else { + "$formatDamage §7/ $formatHealing" + } + event.drawDynamicText( + location, + finalResult, + sizeFinalResults, + diff, + smallestDistanceVew = smallestDistanceVew + ) + diff += 9f + } + } + + } + GlStateManager.enableDepth() + GlStateManager.enableCull() + } + + private fun noDeathDisplay(bossType: BossType): Boolean { + return when (bossType) { + BossType.SLAYER_BLAZE_TYPHOEUS_1, + BossType.SLAYER_BLAZE_TYPHOEUS_2, + BossType.SLAYER_BLAZE_TYPHOEUS_3, + BossType.SLAYER_BLAZE_TYPHOEUS_4, + BossType.SLAYER_BLAZE_QUAZII_1, + BossType.SLAYER_BLAZE_QUAZII_2, + BossType.SLAYER_BLAZE_QUAZII_3, + BossType.SLAYER_BLAZE_QUAZII_4, + + //TODO f3/m3 4 guardians, f2/m2 4 boss room fighters + -> true + + else -> false + } + } + + private fun tickDamage(damageCounter: DamageCounter) { + val now = System.currentTimeMillis() + if (damageCounter.currentDamage != 0L || damageCounter.currentHealing != 0L) { + if (damageCounter.firstTick == 0L) { + damageCounter.firstTick = now + } + + if (now > damageCounter.firstTick + 1_000) { + damageCounter.oldDamages.add( + 0, OldDamage(now, damageCounter.currentDamage, damageCounter.currentHealing) + ) + damageCounter.firstTick = 0L + damageCounter.currentDamage = 0 + damageCounter.currentHealing = 0 + } + } + damageCounter.oldDamages.removeIf { now > it.time + 5_000 } + } + + private fun formatDelay(delay: Long): String { + val color = when { + delay < 1_000 -> LorenzColor.DARK_PURPLE + delay < 3_000 -> LorenzColor.LIGHT_PURPLE + + else -> LorenzColor.WHITE + } + val format = TimeUtils.formatDuration(delay, showMilliSeconds = true) + return color.getChatColor() + format + } + + @SubscribeEvent + fun onTick(event: LorenzTickEvent) { + if (!isEnabled()) return + data = data.editCopy { + EntityUtils.getEntities().mapNotNull(::checkEntity).forEach { this put it } + } + } + + private fun checkEntity(entity: EntityLivingBase): Pair? { + try { + val entityData = grabData(entity) ?: return null + if (LorenzUtils.inDungeons) { + checkFinalBoss(entityData.finalDungeonBoss, entity.entityId) + } + + val health = entity.health.toLong() + val maxHealth: Long + val biggestHealth = getMaxHealthFor(entity) + if (biggestHealth == 0L) { + val currentMaxHealth = entity.baseMaxHealth.toLong() + maxHealth = max(currentMaxHealth, health) + setMaxHealth(entity, maxHealth) + } else { + maxHealth = biggestHealth + } + + entityData.namePrefix = "" + entityData.nameSuffix = "" + entityData.nameAbove = "" + val customHealthText = if (health == 0L) { + entityData.dead = true + if (entityData.bossType.showDeathTime && config.timeToKillSlayer) { + entityData.nameAbove = entityData.timeToKill + } + "§cDead" + } else { + getCustomHealth(entityData, health, entity, maxHealth) ?: return null + } + + if (data.containsKey(entity.uniqueID)) { + val lastHealth = data[entity.uniqueID]!!.lastHealth + checkDamage(entityData, health, lastHealth) + tickDamage(entityData.damageCounter) + + BossHealthChangeEvent(entityData, lastHealth, health, maxHealth).postAndCatch() + } + entityData.lastHealth = health + + if (customHealthText.isNotEmpty()) { + entityData.healthText = customHealthText + } else { + val color = NumberUtil.percentageColor(health, maxHealth) + entityData.healthText = color.getChatColor() + NumberUtil.format(health) + } + entityData.timeLastTick = System.currentTimeMillis() + return entity.uniqueID to entityData + } catch (e: Throwable) { + e.printStackTrace() + return null + } + } + + private fun getCustomHealth( + entityData: EntityData, + health: Long, + entity: EntityLivingBase, + maxHealth: Long, + ): String? { + + when (entityData.bossType) { + BossType.DUNGEON_F4_THORN -> { + val thorn = checkThorn(health, maxHealth) + if (thorn == null) { + val floor = DungeonAPI.dungeonFloor + LorenzUtils.error("problems with thorn detection! ($floor, $health/$maxHealth)") + } + return thorn + } + + BossType.SLAYER_ENDERMAN_1, + BossType.SLAYER_ENDERMAN_2, + BossType.SLAYER_ENDERMAN_3, + BossType.SLAYER_ENDERMAN_4, + -> return checkEnderSlayer(entity as EntityEnderman, entityData, health.toInt(), maxHealth.toInt()) + + BossType.SLAYER_BLOODFIEND_1, + BossType.SLAYER_BLOODFIEND_2, + BossType.SLAYER_BLOODFIEND_3, + BossType.SLAYER_BLOODFIEND_4, + -> return checkVampireSlayer(entity as EntityOtherPlayerMP, entityData, health.toInt(), maxHealth.toInt()) + + BossType.SLAYER_BLAZE_1, + BossType.SLAYER_BLAZE_2, + BossType.SLAYER_BLAZE_3, + BossType.SLAYER_BLAZE_4, + BossType.SLAYER_BLAZE_QUAZII_2, + BossType.SLAYER_BLAZE_QUAZII_3, + BossType.SLAYER_BLAZE_QUAZII_4, + BossType.SLAYER_BLAZE_TYPHOEUS_2, + BossType.SLAYER_BLAZE_TYPHOEUS_3, + BossType.SLAYER_BLAZE_TYPHOEUS_4, + -> return checkBlazeSlayer(entity as EntityLiving, entityData, health.toInt(), maxHealth.toInt()) + + BossType.NETHER_MAGMA_BOSS -> return checkMagmaCube( + entity as EntityMagmaCube, + entityData, + health.toInt(), + maxHealth.toInt() + ) + + BossType.SLAYER_ZOMBIE_5 -> { + if ((entity as EntityZombie).hasNameTagWith(3, "§fBoom!")) { + //TODO fix +// val ticksAlive = entity.ticksExisted % (20 * 5) +// val remainingTicks = (5 * 20).toLong() - ticksAlive +// val format = formatDelay(remainingTicks * 50) +// entityData.nameSuffix = " §f§lBOOM - $format" + entityData.nameSuffix = " §f§lBOOM!" + } + } + + BossType.SLAYER_WOLF_3, + BossType.SLAYER_WOLF_4, + -> { + if ((entity as EntityWolf).hasNameTagWith(2, "§bCalling the pups!")) { + return "Pups!" + } + } + + BossType.NETHER_BARBARIAN_DUKE, + -> { + val location = entity.getLorenzVec() + entityData.ignoreBlocks = location.y == 117.0 && location.distanceToPlayer() < 15 + } + + else -> return "" + } + return "" + } + + private fun checkBlazeSlayer(entity: EntityLiving, entityData: EntityData, health: Int, maxHealth: Int): String { + var found = false + for (shield in HellionShield.entries) { + val armorStand = entity.getNameTagWith(3, shield.name) + if (armorStand != null) { + val number = armorStand.name.split(" ♨")[1].substring(0, 1) + entity.setHellionShield(shield) + entityData.nameAbove = shield.formattedName + " $number" + found = true + break + } + } + if (!found) { + entity.setHellionShield(null) + } + + if (!SkyHanniMod.feature.slayer.blazes.phaseDisplay) return "" + + var calcHealth = health + val calcMaxHealth: Int + entityData.namePrefix = when (entityData.bossType) { + BossType.SLAYER_BLAZE_1, + BossType.SLAYER_BLAZE_2, + -> { + val step = maxHealth / 2 + calcMaxHealth = step + if (health > step) { + calcHealth -= step + "§c1/2 " + } else { + calcHealth = health + "§a2/2 " + } + } + + BossType.SLAYER_BLAZE_3, + BossType.SLAYER_BLAZE_4, + -> { + val step = maxHealth / 3 + calcMaxHealth = step + if (health > step * 2) { + calcHealth -= step * 2 + "§c1/3 " + } else if (health > step) { + calcHealth -= step + "§e2/3 " + } else { + calcHealth = health + "§a3/3 " + } + } + + else -> return "" + } + + return NumberUtil.percentageColor( + calcHealth.toLong(), calcMaxHealth.toLong() + ).getChatColor() + NumberUtil.format(calcHealth) + } + + private fun checkMagmaCube( + entity: EntityMagmaCube, + entityData: EntityData, + health: Int, + maxHealth: Int, + ): String? { + val slimeSize = entity.slimeSize + entityData.namePrefix = when (slimeSize) { + 24 -> "§c1/6" + 22 -> "§e2/6" + 20 -> "§e3/6" + 18 -> "§e4/6" + 16 -> "§e5/6" + else -> { + val color = NumberUtil.percentageColor(health.toLong(), 10_000_000) + entityData.namePrefix = "§a6/6" + return color.getChatColor() + NumberUtil.format(health) + } + } + " §f" + + //hide while in the middle +// val position = entity.getLorenzVec() + //TODO other logic or something +// entityData.healthLineHidden = position.x == -368.0 && position.z == -804.0 + + var calcHealth = -1 + for (line in ScoreboardData.sidebarLinesRaw) { + if (line.contains("▎")) { + val color: String + if (line.startsWith("§7")) { + color = "§7" + } else if (line.startsWith("§e")) { + color = "§e" + } else if (line.startsWith("§6") || line.startsWith("§a") || line.startsWith("§c")) { + calcHealth = 0 + break + } else { + LorenzUtils.error("unknown magma boss health sidebar format!") + break + } + + val text = line.replace("\uD83C\uDF81" + color, "") + val max = 25.0 + val length = text.split("§e", "§7")[1].length + val missing = (health.toDouble() / max) * length + calcHealth = (health - missing).toInt() + } + } + if (calcHealth == -1) return null + + val color = NumberUtil.percentageColor(calcHealth.toLong(), maxHealth.toLong()) + return color.getChatColor() + NumberUtil.format(calcHealth) + } + + private fun checkEnderSlayer( + entity: EntityEnderman, + entityData: EntityData, + health: Int, + maxHealth: Int, + ): String? { + var calcHealth = health + val calcMaxHealth: Int + entityData.namePrefix = when (entityData.bossType) { + BossType.SLAYER_ENDERMAN_1, + BossType.SLAYER_ENDE