aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/features/misc
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features/misc')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/ButtonOnPause.kt51
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/CollectionCounter.kt194
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/CompactBingoChat.kt112
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/CompactSplashPotionMessage.kt35
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/CorruptedMobHighlight.kt61
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/CurrentPetDisplay.kt48
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/ExpBottleOnGroundHider.kt20
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/HideArmor.kt216
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/HideDamageSplash.kt22
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/MarkedPlayerManager.kt120
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/NonGodPotEffectDisplay.kt208
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/RealTime.kt25
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/ThunderSparksHighlight.kt67
13 files changed, 1179 insertions, 0 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/ButtonOnPause.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/ButtonOnPause.kt
new file mode 100644
index 000000000..b54cff9da
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/ButtonOnPause.kt
@@ -0,0 +1,51 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.config.ConfigEditor
+import at.hannibal2.skyhanni.config.core.GuiScreenElementWrapper
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import net.minecraft.client.gui.GuiButton
+import net.minecraft.client.gui.GuiIngameMenu
+import net.minecraftforge.client.event.GuiScreenEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class ButtonOnPause {
+ private val buttonId = System.nanoTime().toInt()
+
+ @SubscribeEvent
+ fun onGuiAction(event: GuiScreenEvent.ActionPerformedEvent.Post) {
+ if (!LorenzUtils.isOnHypixel) return
+
+ if (SkyHanniMod.feature.misc.configButtonOnPause && event.gui is GuiIngameMenu && event.button.id == buttonId) {
+ SkyHanniMod.screenToOpen = GuiScreenElementWrapper(
+ ConfigEditor(
+ SkyHanniMod.feature
+ )
+ )
+ }
+ }
+
+ @SubscribeEvent
+ fun onGuiInitPost(event: GuiScreenEvent.InitGuiEvent.Post) {
+ if (!LorenzUtils.isOnHypixel) return
+
+ if (SkyHanniMod.feature.misc.configButtonOnPause && event.gui is GuiIngameMenu) {
+ val x = event.gui.width - 105
+ val x2 = x + 100
+ var y = event.gui.height - 22
+ var y2 = y + 20
+ val sorted = event.buttonList.sortedWith { a, b -> b.yPosition + b.height - a.yPosition + a.height }
+ for (button in sorted) {
+ val otherX = button.xPosition
+ val otherX2 = button.xPosition + button.width
+ val otherY = button.yPosition
+ val otherY2 = button.yPosition + button.height
+ if (otherX2 > x && otherX < x2 && otherY2 > y && otherY < y2) {
+ y = otherY - 20 - 2
+ y2 = y + 20
+ }
+ }
+ event.buttonList.add(GuiButton(buttonId, x, 0.coerceAtLeast(y), 100, 20, "SkyHanni"))
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/CollectionCounter.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/CollectionCounter.kt
new file mode 100644
index 000000000..277ecafb6
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/CollectionCounter.kt
@@ -0,0 +1,194 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.ProfileApiDataLoadedEvent
+import at.hannibal2.skyhanni.features.bazaar.BazaarApi
+import at.hannibal2.skyhanni.features.bazaar.BazaarData
+import at.hannibal2.skyhanni.test.GriffinJavaUtils
+import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.RenderUtils.renderString
+import net.minecraft.client.Minecraft
+import net.minecraftforge.client.event.RenderGameOverlayEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+
+class CollectionCounter {
+
+ private val RECENT_GAIN_TIME = 1_500
+
+ companion object {
+
+ private var display = ""
+
+ private var itemName = ""
+ private var itemApiName = ""
+ private var itemAmount = -1
+
+ private var lastAmountInInventory = -1
+
+ private var recentGain = 0
+ private var lastGainTime = -1L
+
+ private val apiCollectionData = mutableMapOf<String, Int>()
+
+ fun command(args: Array<String>) {
+ if (args.isEmpty()) {
+ if (itemName == "") {
+ LorenzUtils.chat("§c/shtrackcollection <item name>")
+ return
+ }
+ LorenzUtils.chat("§e[SkyHanni] Stopped collection tracker.")
+ apiCollectionData[itemApiName] = itemAmount
+ resetData()
+ return
+ }
+
+ var name = args.joinToString(" ")
+
+ var data: BazaarData? = null
+ for (bazaarData in BazaarApi.bazaarMap.values) {
+ if (bazaarData.itemName.equals(name, ignoreCase = true)) {
+ data = bazaarData
+ break
+ }
+ }
+
+ if (data == null) {
+ LorenzUtils.chat("§c[SkyHanni] Item '$name' not found!")
+ return
+ }
+ name = data.itemName
+
+ val apiName = data.apiName
+ if (!apiCollectionData.contains(apiName)) {
+ LorenzUtils.chat("§c[SkyHanni] Item $name not in collection data!")
+ return
+ }
+
+ if (itemAmount != -1) {
+ resetData()
+ }
+
+ itemName = name
+ itemApiName = apiName
+ itemAmount = apiCollectionData[apiName]!!
+
+ lastAmountInInventory = countCurrentlyInInventory()
+ updateDisplay()
+ LorenzUtils.chat("§e[SkyHanni] Started tracking $itemName collection.")
+ }
+
+ private fun resetData() {
+ itemAmount = -1
+ itemName = ""
+ itemApiName = ""
+
+ lastAmountInInventory = -1
+ display = ""
+
+ recentGain = 0
+ }
+
+ private fun updateDisplay() {
+ val format = GriffinJavaUtils.formatInteger(itemAmount)
+
+ var gainText = ""
+ if (recentGain != 0) {
+ gainText = "§a+" + GriffinJavaUtils.formatInteger(recentGain)
+ }
+
+ display = "$itemName collection: §e$format $gainText"
+ }
+
+ private fun countCurrentlyInInventory(): Int {
+ var currentlyInInventory = 0
+ val player = Minecraft.getMinecraft().thePlayer
+ for (stack in player.inventory.mainInventory) {
+ if (stack == null) continue
+ val internalName = stack.getInternalName()
+ if (internalName == itemApiName) {
+ currentlyInInventory += stack.stackSize
+ }
+ }
+ return currentlyInInventory
+ }
+ }
+
+ @SubscribeEvent
+ fun onTick(event: TickEvent.ClientTickEvent) {
+ val thePlayer = Minecraft.getMinecraft().thePlayer ?: return
+ thePlayer.worldObj ?: return
+
+ compareInventory()
+ updateGain()
+ }
+
+ private fun compareInventory() {
+ if (lastAmountInInventory == -1) return
+ if (Minecraft.getMinecraft().currentScreen != null) return
+
+ val currentlyInInventory = countCurrentlyInInventory()
+ val diff = currentlyInInventory - lastAmountInInventory
+ if (diff != 0) {
+ if (diff > 0) {
+ gainItems(diff)
+ } else {
+ LorenzUtils.debug("Collection counter! Negative collection change: $diff")
+ }
+ }
+
+ lastAmountInInventory = currentlyInInventory
+ }
+
+ private fun updateGain() {
+ if (recentGain != 0) {
+ if (System.currentTimeMillis() > lastGainTime + RECENT_GAIN_TIME) {
+ recentGain = 0
+ updateDisplay()
+ }
+ }
+ }
+
+ private fun gainItems(amount: Int) {
+ itemAmount += amount
+
+ if (System.currentTimeMillis() > lastGainTime + RECENT_GAIN_TIME) {
+ recentGain = 0
+ }
+ lastGainTime = System.currentTimeMillis()
+ recentGain += amount
+
+ updateDisplay()
+ }
+
+ @SubscribeEvent
+ fun onProfileDataLoad(event: ProfileApiDataLoadedEvent) {
+ val profileData = event.profileData
+ val collection = profileData["collection"].asJsonObject
+
+ apiCollectionData.clear()
+ for (entry in collection.entrySet()) {
+ val name = entry.key
+ val value = entry.value.asInt
+ apiCollectionData[name] = value
+ if (name == itemApiName) {
+ val diff = value - itemAmount
+ if (diff != 0) {
+ LorenzUtils.debug("Collection counter was wrong by $diff items. (Compared against API data)")
+ }
+ itemAmount = value
+ recentGain = 0
+ updateDisplay()
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: RenderGameOverlayEvent.Post) {
+ if (event.type != RenderGameOverlayEvent.ElementType.ALL) return
+ if (!LorenzUtils.inSkyblock) return
+
+ SkyHanniMod.feature.misc.collectionCounterPos.renderString(display)
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/CompactBingoChat.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/CompactBingoChat.kt
new file mode 100644
index 000000000..a4becd8c4
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/CompactBingoChat.kt
@@ -0,0 +1,112 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class CompactBingoChat {
+
+ private var blockedSkillLevelUp = false
+ private var blockedCollectionLevelUp = false
+ private var collectionLevelUpLastLine: String? = null
+ private var newArea = 0//0 = nothing, 1 = after first message, 2 = after second message
+ private var blockedBestiarity = false
+
+ @SubscribeEvent
+ fun onChatMessage(event: LorenzChatEvent) {
+ if (!LorenzUtils.isBingoProfile) return
+ if (!SkyHanniMod.feature.bingo.compactChatMessages) return
+
+ onSkillLevelUp(event)
+ onCollectionLevelUp(event)
+ onNewAreaDiscovered(event)
+ onBestiarityUpgrade(event)
+ }
+
+ private fun onSkillLevelUp(event: LorenzChatEvent) {
+ val message = event.message
+ if (message.startsWith(" §r§b§lSKILL LEVEL UP ")) {
+ blockedSkillLevelUp = true
+ return
+ }
+ if (message == "§3§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬") {
+ blockedSkillLevelUp = false
+ return
+ }
+
+ if (blockedSkillLevelUp) {
+ if (!message.contains("Access to") && !message.endsWith(" Enchantment")) {
+ event.blockedReason = "compact skill level up"
+ }
+ }
+ }
+
+ private fun onCollectionLevelUp(event: LorenzChatEvent) {
+ val message = event.message
+ if (message.startsWith(" §r§6§lCOLLECTION LEVEL UP ")) {
+ blockedCollectionLevelUp = true
+ return
+ }
+ if (message == "§e§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬") {
+ blockedCollectionLevelUp = false
+ return
+ }
+
+ if (blockedCollectionLevelUp) {
+ if (message.contains("Trade") || message.contains("Recipe")) {
+ var text = message.removeColor().replace(" ", "")
+ if (text == "Trade" || text == "Recipe") {
+ collectionLevelUpLastLine?.let { LorenzUtils.chat(it) }
+ }
+ } else {
+ event.blockedReason = "compact collection level up"
+ collectionLevelUpLastLine = message
+ }
+ }
+ }
+
+ private fun onNewAreaDiscovered(event: LorenzChatEvent) {
+ var message = event.message
+
+ if (message == " §r§6§lNEW AREA DISCOVERED!") {
+ newArea = 1
+ println("new area $newArea $message")
+ return
+ }
+
+ if (message != "") {
+ if (newArea == 1) {
+ newArea = 2
+ println("new area $newArea $message")
+ return
+ }
+
+ if (newArea == 2) {
+ if (message.startsWith("§7 ■ §r") || message.startsWith(" §r")) {
+ event.blockedReason = "compact new area discovered"
+ } else {
+ newArea = 0
+ println("new area $newArea $message")
+ }
+ }
+ }
+ }
+
+ private fun onBestiarityUpgrade(event: LorenzChatEvent) {
+ val message = event.message
+ if (message.startsWith(" §r§3§lBESTIARY §b§l")) {
+ blockedBestiarity = true
+ return
+ }
+ if (message == "§3§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬") {
+ blockedBestiarity = false
+ return
+ }
+
+ if (blockedBestiarity) {
+ event.blockedReason = "compact bestiarity upgrade"
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/CompactSplashPotionMessage.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/CompactSplashPotionMessage.kt
new file mode 100644
index 000000000..2f7bc254c
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/CompactSplashPotionMessage.kt
@@ -0,0 +1,35 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import net.minecraft.util.ChatComponentText
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.regex.Pattern
+
+class CompactSplashPotionMessage {
+
+ private val POTION_EFFECT_PATTERN =
+ Pattern.compile("§a§lBUFF! §fYou have gained §r(.*)§r§f! Press TAB or type /effects to view your active effects!")
+
+ private val POTION_EFFECT_OTHERS_PATTERN =
+ Pattern.compile("§a§lBUFF! §fYou were splashed by (.*) §fwith §r(.*)§r§f! Press TAB or type /effects to view your active effects!")
+
+ @SubscribeEvent
+ fun onChatMessage(event: LorenzChatEvent) {
+ if (!LorenzUtils.inSkyblock || !SkyHanniMod.feature.chat.compactPotionMessage) return
+
+ var matcher = POTION_EFFECT_PATTERN.matcher(event.message)
+ if (matcher.matches()) {
+ val name = matcher.group(1)
+ event.chatComponent = ChatComponentText("§a§lPotion Effect! §r$name")
+ }
+
+ matcher = POTION_EFFECT_OTHERS_PATTERN.matcher(event.message)
+ if (matcher.matches()) {
+ val playerName = matcher.group(1)
+ val effectName = matcher.group(2)
+ event.chatComponent = ChatComponentText("§a§lPotion Effect! §r$effectName §7(by $playerName§7)")
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/CorruptedMobHighlight.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/CorruptedMobHighlight.kt
new file mode 100644
index 000000000..b831bc927
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/CorruptedMobHighlight.kt
@@ -0,0 +1,61 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.EntityHealthUpdateEvent
+import at.hannibal2.skyhanni.events.RenderMobColoredEvent
+import at.hannibal2.skyhanni.events.ResetEntityHurtEvent
+import at.hannibal2.skyhanni.events.withAlpha
+import at.hannibal2.skyhanni.utils.LorenzColor
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.baseMaxHealth
+import net.minecraft.entity.EntityLivingBase
+import net.minecraftforge.event.world.WorldEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class CorruptedMobHighlight {
+
+ private val corruptedMobs = mutableListOf<EntityLivingBase>()
+
+ @SubscribeEvent
+ fun onEntityHealthUpdate(event: EntityHealthUpdateEvent) {
+ if (!LorenzUtils.inSkyblock) return
+
+ val entity = event.entity
+ if (entity in corruptedMobs) return
+
+ val baseMaxHealth = entity.baseMaxHealth.toFloat()
+ if (event.health == baseMaxHealth * 3) {
+ corruptedMobs.add(entity)
+ }
+ }
+
+ @SubscribeEvent
+ fun onRenderMobColored(event: RenderMobColoredEvent) {
+ if (!isEnabled()) return
+ val entity = event.entity
+
+ if (entity in corruptedMobs) {
+ event.color = LorenzColor.DARK_PURPLE.toColor().withAlpha(127)
+ }
+ }
+
+ @SubscribeEvent
+ fun onResetEntityHurtTime(event: ResetEntityHurtEvent) {
+ if (!isEnabled()) return
+ val entity = event.entity
+
+ if (entity in corruptedMobs) {
+ event.shouldReset = true
+ }
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: WorldEvent.Load) {
+ corruptedMobs.clear()
+ }
+
+ private fun isEnabled(): Boolean {
+ return LorenzUtils.inSkyblock && SkyHanniMod.feature.misc.corruptedMobHighlight &&
+ LorenzUtils.skyBlockIsland != "Private Island"
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/CurrentPetDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/CurrentPetDisplay.kt
new file mode 100644
index 000000000..53f56c37c
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/CurrentPetDisplay.kt
@@ -0,0 +1,48 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.between
+import at.hannibal2.skyhanni.utils.LorenzUtils.matchRegex
+import at.hannibal2.skyhanni.utils.RenderUtils.renderString
+import net.minecraftforge.client.event.RenderGameOverlayEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class CurrentPetDisplay {
+
+ @SubscribeEvent
+ fun onChatMessage(event: LorenzChatEvent) {
+ if (!LorenzUtils.inSkyblock) return
+
+ var blocked = false
+
+ val message = event.message
+ if (message.matchRegex("§aYou summoned your §r(.*)§r§a!")) {
+ SkyHanniMod.feature.hidden.currentPet = message.between("your §r", "§r§a")
+ blocked = true
+ }
+ if (message.matchRegex("§cAutopet §eequipped your §7(.*)§e! §a§lVIEW RULE")) {
+ SkyHanniMod.feature.hidden.currentPet = message.between("] ", "§e!")
+ blocked = true
+ }
+ if (message.matchRegex("§aYou despawned your §r(.*)§r§a!")) {
+ SkyHanniMod.feature.hidden.currentPet = ""
+ blocked = true
+ }
+
+ if (blocked && SkyHanniMod.feature.misc.petDisplay) {
+ event.blockedReason = "pets"
+ }
+ }
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: RenderGameOverlayEvent.Post) {
+ if (event.type != RenderGameOverlayEvent.ElementType.ALL) return
+ if (!LorenzUtils.inSkyblock) return
+
+ if (!SkyHanniMod.feature.misc.petDisplay) return
+
+ SkyHanniMod.feature.misc.petDisplayPos.renderString(SkyHanniMod.feature.hidden.currentPet)
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/ExpBottleOnGroundHider.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/ExpBottleOnGroundHider.kt
new file mode 100644
index 000000000..99ccb4cea
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/ExpBottleOnGroundHider.kt
@@ -0,0 +1,20 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.CheckRenderEntityEvent
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import net.minecraft.entity.item.EntityXPOrb
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class ExpBottleOnGroundHider {
+
+ @SubscribeEvent
+ fun onCheckRender(event: CheckRenderEntityEvent<*>) {
+ if (!LorenzUtils.inSkyblock) return
+ if (!SkyHanniMod.feature.misc.hideExpBottles) return
+
+ if (event.entity is EntityXPOrb) {
+ event.isCanceled = true
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/HideArmor.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/HideArmor.kt
new file mode 100644
index 000000000..b6623331d
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/HideArmor.kt
@@ -0,0 +1,216 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.PacketEvent
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import net.minecraft.client.Minecraft
+import net.minecraft.client.entity.EntityOtherPlayerMP
+import net.minecraft.client.entity.EntityPlayerSP
+import net.minecraft.client.gui.inventory.GuiInventory
+import net.minecraft.entity.player.EntityPlayer
+import net.minecraft.item.ItemStack
+import net.minecraft.network.play.server.S04PacketEntityEquipment
+import net.minecraft.network.play.server.S2FPacketSetSlot
+import net.minecraft.network.play.server.S30PacketWindowItems
+import net.minecraftforge.event.world.WorldEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+
+class HideArmor {
+
+ private var invOpen = false
+ private val laterCheck = mutableListOf<Int>()
+
+ @SubscribeEvent
+ fun onGuiInventoryToggle(event: TickEvent.ClientTickEvent) {
+ if (!LorenzUtils.inSkyblock) return // TODO test this
+
+ fixOtherArmor()
+
+ if (!SkyHanniMod.feature.misc.hideArmorEnabled) return
+
+ val currentScreen = Minecraft.getMinecraft().currentScreen
+ if (currentScreen == null || currentScreen !is GuiInventory) {
+ if (invOpen) {
+ invOpen = false
+ changeArmor(Minecraft.getMinecraft().thePlayer, null)
+ }
+ } else {
+ if (!invOpen) {
+ invOpen = true
+ val thePlayer = Minecraft.getMinecraft().thePlayer
+ val entityId = thePlayer.entityId
+ changeArmor(thePlayer, getCachedArmor(entityId))
+ }
+ }
+ }
+
+ // Since S04PacketEntityEquipment gets sent before the entity is fully loaded, I need to remove the armor later
+ private fun fixOtherArmor() {
+ for (entity in Minecraft.getMinecraft().theWorld.loadedEntityList) {
+ if (entity !is EntityOtherPlayerMP) continue
+
+ val entityId = entity.entityId
+ if (entityId !in laterCheck) continue
+
+ laterCheck.remove(entityId)
+ if (SkyHanniMod.feature.misc.hideArmorEnabled) {
+ val armorInventory = entity.inventory.armorInventory
+ for ((equipmentSlot, _) in armorInventory.withIndex()) {
+ if (!SkyHanniMod.feature.misc.hideArmorOnlyHelmet || equipmentSlot == 3) {
+ armorInventory[equipmentSlot] = null
+ }
+ }
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onPacketReceive(event: PacketEvent.ReceiveEvent) {
+ val packet = event.packet
+
+ //own player world switch
+ if (packet is S30PacketWindowItems) {
+
+ // check window id
+ if (packet.func_148911_c() != 0) return
+
+ for ((slot, itemStack) in packet.itemStacks.withIndex()) {
+
+ if (slot !in 5..8) continue
+
+ val armorSlot = (slot - 5) * -1 + 3
+ val armor = getCachedArmor(Minecraft.getMinecraft().thePlayer.entityId)
+ armor[armorSlot] = itemStack
+
+ val currentScreen = Minecraft.getMinecraft().currentScreen
+ if (currentScreen == null || currentScreen !is GuiInventory) {
+ if (SkyHanniMod.feature.misc.hideArmorEnabled) {
+ if (SkyHanniMod.feature.misc.hideArmorOwn) {
+ if (!SkyHanniMod.feature.misc.hideArmorOnlyHelmet || armorSlot == 3) {
+ packet.itemStacks[slot] = null
+ }
+ }
+ }
+ }
+ }
+ return
+ }
+
+
+ //own player armor change
+ if (packet is S2FPacketSetSlot) {
+ val slot = packet.func_149173_d()
+
+ // check window id
+ if (packet.func_149175_c() != 0) return
+ if (slot !in 5..8) return
+
+ val armorSlot = (slot - 5) * -1 + 3
+ val armor = getCachedArmor(Minecraft.getMinecraft().thePlayer.entityId)
+ // set item in cache
+ armor[armorSlot] = packet.func_149174_e()
+
+ val currentScreen = Minecraft.getMinecraft().currentScreen
+ if (currentScreen == null || currentScreen !is GuiInventory) {
+ if (SkyHanniMod.feature.misc.hideArmorEnabled) {
+ if (SkyHanniMod.feature.misc.hideArmorOwn) {
+ if (!SkyHanniMod.feature.misc.hideArmorOnlyHelmet || armorSlot == 3) {
+ event.isCanceled = true
+ }
+ }
+ }
+ }
+ return
+ }
+
+
+ //other player armor switch
+ if (packet is S04PacketEntityEquipment) {
+ val entityID = packet.entityID
+ val equipmentSlot = packet.equipmentSlot - 1
+ if (equipmentSlot == -1) return
+
+ val entity = Minecraft.getMinecraft().theWorld?.getEntityByID(entityID)
+ if (entity == null) {
+ laterCheck.add(entityID)
+ return
+ }
+
+ if (entity !is EntityOtherPlayerMP) return
+
+ val armor = getCachedArmor(entityID)
+
+ // set item in cache
+ armor[equipmentSlot] = packet.itemStack
+
+ if (SkyHanniMod.feature.misc.hideArmorEnabled) {
+ if (!SkyHanniMod.feature.misc.hideArmorOnlyHelmet || equipmentSlot == 3) {
+ event.isCanceled = true
+ }
+ }
+ }
+ }
+
+ private fun getCachedArmor(entityID: Int): Array<ItemStack?> {
+ val armor: Array<ItemStack?> = if (armorCache.containsKey(entityID)) {
+ armorCache[entityID]!!
+ } else {
+ val new = arrayOf<ItemStack?>(null, null, null, null)
+ armorCache[entityID] = new
+ new
+ }
+ return armor
+ }
+
+ companion object {
+ var armorCache: MutableMap<Int, Array<ItemStack?>> = mutableMapOf()
+
+ fun updateArmor() {
+ for (entity in Minecraft.getMinecraft().theWorld.loadedEntityList) {
+ if (entity !is EntityPlayer) continue
+
+ val entityId = entity.entityId
+ armorCache[entityId]?.let {
+ changeArmor(entity, it)
+ }
+
+ if (SkyHanniMod.feature.misc.hideArmorEnabled) {
+ changeArmor(entity, null)
+ }
+ }
+ }
+
+ private fun changeArmor(entity: EntityPlayer, new: Array<ItemStack?>?) {
+ if (!LorenzUtils.inSkyblock) return
+
+ val current = entity.inventory.armorInventory
+ if (new != null) {
+ current[0] = new[0]
+ current[1] = new[1]
+ current[2] = new[2]
+ current[3] = new[3]
+ return
+ }
+
+ if (!SkyHanniMod.feature.misc.hideArmorOwn) {
+ if (entity is EntityPlayerSP) {
+ return
+ }
+ }
+
+ if (!SkyHanniMod.feature.misc.hideArmorOnlyHelmet) {
+ current[0] = null
+ current[1] = null
+ current[2] = null
+ }
+ current[3] = null
+ }
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: WorldEvent.Load) {
+ armorCache.clear()
+ laterCheck.clear()
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/HideDamageSplash.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/HideDamageSplash.kt
new file mode 100644
index 000000000..8c4a0fcac
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/HideDamageSplash.kt
@@ -0,0 +1,22 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.features.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<EntityLivingBase>) {
+ if (!LorenzUtils.inSkyblock) return
+ if (!SkyHanniMod.feature.misc.hideDamageSplash) return
+
+ if (DamageIndicatorManager.isDamageSplash(event.entity)) {
+ event.isCanceled = true
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/MarkedPlayerManager.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/MarkedPlayerManager.kt
new file mode 100644
index 000000000..38e3babbc
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/MarkedPlayerManager.kt
@@ -0,0 +1,120 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.RenderMobColoredEvent
+import at.hannibal2.skyhanni.events.ResetEntityHurtEvent
+import at.hannibal2.skyhanni.events.withAlpha
+import at.hannibal2.skyhanni.utils.LorenzColor
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import net.minecraft.client.Minecraft
+import net.minecraft.client.entity.EntityOtherPlayerMP
+import net.minecraftforge.event.world.WorldEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+
+class MarkedPlayerManager {
+
+ companion object {
+ val playerNamesToMark = mutableListOf<String>()
+ private val markedPlayers = mutableMapOf<String, EntityOtherPlayerMP>()
+
+ fun command(args: Array<String>) {
+ if (args.size != 1) {
+ LorenzUtils.chat("§cUsage: /shmarkplayer <name>")
+ return
+ }
+
+ val displayName = args[0]
+ val name = displayName.lowercase()
+
+
+ if (name == Minecraft.getMinecraft().thePlayer.name.lowercase()) {
+ LorenzUtils.chat("§c[SkyHanni] You can't add or remove yourself this way! Go to the settings and toggle 'Mark your own name'.")
+ return
+ }
+
+ if (name !in playerNamesToMark) {
+ playerNamesToMark.add(name)
+ findPlayers()
+ LorenzUtils.chat("§e[SkyHanni] §aMarked §eplayer §b$displayName§e!")
+ } else {
+ playerNamesToMark.remove(name)
+ markedPlayers.remove(name)
+ LorenzUtils.chat("§e[SkyHanni] §cUnmarked §eplayer §b$displayName§e!")
+ }
+ }
+
+ private fun findPlayers() {
+ for (entity in Minecraft.getMinecraft().theWorld.loadedEntityList) {
+ if (entity is EntityOtherPlayerMP) {
+ if (entity in markedPlayers.values) continue
+
+ val name = entity.name.lowercase()
+ if (name in playerNamesToMark) {
+ markedPlayers[name] = entity
+ }
+ }
+ }
+ }
+
+ fun isMarkedPlayer(player: String): Boolean = player.lowercase() in playerNamesToMark
+
+ fun toggleOwn() {
+ val ownName = SkyHanniMod.feature.markedPlayers.markOwnName
+ val name = Minecraft.getMinecraft().thePlayer.name
+ if (ownName) {
+ if (!playerNamesToMark.contains(name)) {
+ playerNamesToMark.add(name)
+ }
+ } else {
+ playerNamesToMark.remove(name)
+ }
+ }
+ }
+
+ var tick = 0
+
+ @SubscribeEvent
+ fun onTick(event: TickEvent.ClientTickEvent) {
+ if (!LorenzUtils.inSkyblock) return
+
+ if (tick++ % 20 == 0) {
+ findPlayers()
+ }
+ }
+
+ @SubscribeEvent
+ fun onRenderMobColored(event: RenderMobColoredEvent) {
+ if (!LorenzUtils.inSkyblock) return
+ if (!SkyHanniMod.feature.markedPlayers.highlightInWorld) return
+
+ val entity = event.entity
+ if (entity in markedPlayers.values) {
+ event.color = LorenzColor.YELLOW.toColor().withAlpha(127)
+ }
+ }
+
+ @SubscribeEvent
+ fun onResetEntityHurtTime(event: ResetEntityHurtEvent) {
+ if (!LorenzUtils.inSkyblock) return
+ if (!SkyHanniMod.feature.markedPlayers.highlightInWorld) return
+
+ val entity = event.entity
+ if (entity in markedPlayers.values) {
+ event.shouldReset = true
+ }
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: WorldEvent.Load) {
+ if (Minecraft.getMinecraft().thePlayer == null) return
+
+ markedPlayers.clear()
+ if (SkyHanniMod.feature.markedPlayers.markOwnName) {
+ val name = Minecraft.getMinecraft().thePlayer.name
+ if (!playerNamesToMark.contains(name)) {
+ playerNamesToMark.add(name)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/NonGodPotEffectDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/NonGodPotEffectDisplay.kt
new file mode 100644
index 000000000..9f91e1c82
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/NonGodPotEffectDisplay.kt
@@ -0,0 +1,208 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.events.PacketEvent
+import at.hannibal2.skyhanni.events.ProfileApiDataLoadedEvent
+import at.hannibal2.skyhanni.test.GriffinJavaUtils
+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.RenderUtils.renderStrings
+import at.hannibal2.skyhanni.utils.StringUtils
+import net.minecraft.network.play.server.S30PacketWindowItems
+import net.minecraft.network.play.server.S47PacketPlayerListHeaderFooter
+import net.minecraftforge.client.event.RenderGameOverlayEvent
+import net.minecraftforge.event.world.WorldEvent
+import net.minecraftforge.fml.common.eventhandler.EventPriority
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+import java.util.regex.Pattern
+
+class NonGodPotEffectDisplay {
+
+ private var checkFooter = false
+ private val activeEffects = mutableMapOf<String, Long>()
+ private val textToRender = mutableListOf<String>()
+ private var lastTick = 0L
+
+ private var nonGodPotEffects = mapOf(
+ "smoldering_polarization" to "§aSmoldering Polarization I",
+ "mushed_glowy_tonic" to "§2Mushed Glowy Tonic I",
+ "wisp_ice" to "§bWisp's Ice-Flavored Water I",
+ )
+
+ private var patternEffectsCount = Pattern.compile("§7You have §e(\\d+) §7non-god effects\\.")
+ private var totalEffectsCount = 0
+
+ @SubscribeEvent
+ fun onChatMessage(event: LorenzChatEvent) {
+ if (event.message == "§aYou ate a §r§aRe-heated Gummy Polar Bear§r§a!") {
+ checkFooter = true
+ activeEffects["§aSmoldering Polarization I"] = System.currentTimeMillis() + 1000 * 60 * 60
+ format()
+ }
+
+ if (event.message == "§a§lBUFF! §fYou have gained §r§2Mushed Glowy Tonic I§r§f! Press TAB or type /effects to view your active effects!") {
+ checkFooter = true
+ activeEffects["§2Mushed Glowy Tonic I"] = System.currentTimeMillis() + 1000 * 60 * 60
+ format()
+ }
+
+ if (event.message == "§a§lBUFF! §fYou splashed yourself with §r§bWisp's Ice-Flavored Water I§r§f! Press TAB or type /effects to view your active effects!") {
+ checkFooter = true
+ activeEffects["§bWisp's Ice-Flavored Water I"] = System.currentTimeMillis() + 1000 * 60 * 5
+ format()
+ }
+ }
+
+ private fun format() {
+ val now = System.currentTimeMillis()
+ textToRender.clear()
+ if (activeEffects.values.removeIf { now > it }) {
+ //to fetch the real amount of active pots
+ totalEffectsCount = 0
+ checkFooter = true
+ }
+ for (effect in GriffinJavaUtils.sortByValue(activeEffects)) {
+ val label = effect.key
+ val until = effect.value
+ val seconds = (until - now) / 1000
+ val format = StringUtils.formatDuration(seconds)
+
+ val color = colorForTime(seconds)
+
+ textToRender.add("$label $color$format")
+ }
+ val diff = totalEffectsCount - activeEffects.size
+ if (diff > 0) {
+ textToRender.add("§eOpen the /effects inventory")
+ textToRender.add("§eto show the missing $diff effects!")
+ checkFooter = true
+ }
+ }
+
+ private fun colorForTime(seconds: Long): String {
+ return if (seconds <= 60) {
+ "§c"
+ } else if (seconds <= 60 * 3) {
+ "§6"
+ } else if (seconds <= 60 * 10) {
+ "§e"
+ } else {
+ "§f"
+ }
+ }
+
+ @SubscribeEvent
+ fun onTick(event: TickEvent.ClientTickEvent) {
+ if (!isEnabled()) return
+ if (lastTick + 1_000 > System.currentTimeMillis()) return
+ lastTick = System.currentTimeMillis()
+
+ format()
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: WorldEvent.Load) {
+ checkFooter = true
+ }
+
+ @SubscribeEvent(priority = EventPriority.LOW, receiveCanceled = true)
+ fun onChatPacket(event: PacketEvent.ReceiveEvent) {
+ val packet = event.packet
+ if (packet is S30PacketWindowItems) {
+ for (stack in packet.itemStacks) {
+ val name = stack?.name ?: continue
+ if (name in nonGodPotEffects.values) {
+ for (line in stack.getLore()) {
+ if (line.contains("Remaining")) {
+ val duration = readDuration(line.split("§f")[1])
+ activeEffects[name] = System.currentTimeMillis() + duration
+ format()
+ }
+ }
+ }
+ }
+ }
+
+ if (!checkFooter) return
+ if (packet is S47PacketPlayerListHeaderFooter) {
+ val formattedText = packet.footer.formattedText
+ val lines = formattedText.replace("§r", "").split("\n")
+
+ if (!lines.any { it.contains("§a§lActive Effects") }) return
+ checkFooter = false
+
+ var effectsCount = 0
+ for (line in lines) {
+ if (line.startsWith("§2Mushed Glowy Tonic I")) {
+ val duration = readDuration(line.split("§f")[1])
+ activeEffects["§2Mushed Glowy Tonic I"] = System.currentTimeMillis() + duration
+ format()
+ }
+ val matcher = patternEffectsCount.matcher(line)
+ if (matcher.matches()) {
+ val group = matcher.group(1)
+ effectsCount = group.toInt()
+ }
+ }
+ totalEffectsCount = effectsCount
+ }
+ }
+
+ private fun readDuration(text: String): Int {
+ val split = text.split(":")
+ return when (split.size) {
+ 3 -> {
+ val hours = split[0].toInt() * 1000 * 60 * 60
+ val minutes = split[1].toInt() * 1000 * 60
+ val seconds = split[2].toInt() * 1000
+ seconds + minutes + hours
+ }
+
+ 2 -> {
+ val minutes = split[0].toInt() * 1000 * 60
+ val seconds = split[1].toInt() * 1000
+ seconds + minutes
+ }
+
+ 1 -> {
+ split[0].toInt() * 1000
+ }
+
+ else -> {
+ throw RuntimeException("Invalid format: '$text'")
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: RenderGameOverlayEvent.Post) {
+ if (event.type != RenderGameOverlayEvent.ElementType.ALL) return
+ if (!isEnabled()) return
+
+ SkyHanniMod.feature.misc.nonGodPotEffectPos.renderStrings(textToRender)
+ }
+
+ @SubscribeEvent
+ fun onProfileDataLoad(event: ProfileApiDataLoadedEvent) {
+ val profileData = event.profileData
+ val effects = profileData["active_effects"]?.asJsonArray ?: return
+ for (element in effects) {
+ val effect = element.asJsonObject
+ val name = effect["effect"].asString
+ val label = nonGodPotEffects[name] ?: continue
+
+ val time = effect["ticks_remaining"].asLong / 20
+ val newValue = System.currentTimeMillis() + time * 1000
+ val old = activeEffects.getOrDefault(label, 0)
+ val diff = newValue - old
+ activeEffects[label] = newValue
+ }
+ }
+
+ private fun isEnabled(): Boolean {
+ return LorenzUtils.inSkyblock && SkyHanniMod.feature.misc.nonGodPotEffectDisplay && !LorenzUtils.inDungeons && !LorenzUtils.inKuudraFight
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/RealTime.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/RealTime.kt
new file mode 100644
index 000000000..8d7fafb1f
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/RealTime.kt
@@ -0,0 +1,25 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.RenderUtils.renderString
+import net.minecraftforge.client.event.RenderGameOverlayEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.text.SimpleDateFormat
+
+class RealTime {
+
+ private val format = SimpleDateFormat("HH:mm:ss")
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: RenderGameOverlayEvent.Post) {
+ if (event.type != RenderGameOverlayEvent.ElementType.ALL) return
+ if (!isEnabled()) return
+
+ SkyHanniMod.feature.misc.realTimePos.renderString(format.format(System.currentTimeMillis()))
+ }
+
+ private fun isEnabled(): Boolean {
+ return LorenzUtils.inSkyblock && SkyHanniMod.feature.misc.realTime
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/ThunderSparksHighlight.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/ThunderSparksHighlight.kt
new file mode 100644
index 000000000..c7432ffa9
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/misc/ThunderSparksHighlight.kt
@@ -0,0 +1,67 @@
+package at.hannibal2.skyhanni.features.misc
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.test.GriffinUtils.drawWaypointFilled
+import at.hannibal2.skyhanni.utils.BlockUtils.getBlockAt
+import at.hannibal2.skyhanni.utils.EntityUtils.hasSkullTexture
+import at.hannibal2.skyhanni.utils.LocationUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.RenderUtils.drawString
+import at.hannibal2.skyhanni.utils.SpecialColour
+import at.hannibal2.skyhanni.utils.getLorenzVec
+import net.minecraft.client.Minecraft
+import net.minecraft.entity.item.EntityArmorStand
+import net.minecraft.init.Blocks
+import net.minecraftforge.client.event.RenderWorldLastEvent
+import net.minecraftforge.event.world.WorldEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+import java.awt.Color
+
+class ThunderSparksHighlight {
+
+ private val texture =
+ "ewogICJ0aW1lc3RhbXAiIDogMTY0MzUwNDM3MjI1NiwKICAicHJvZmlsZUlkIiA6ICI2MzMyMDgwZTY3YTI0Y2MxYjE3ZGJhNzZmM2MwMGYxZCIsCiAgInByb2ZpbGVOYW1lIiA6ICJUZWFtSHlkcmEiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2IzMzI4ZDNlOWQ3MTA0MjAzMjI1NTViMTcyMzkzMDdmMTIyNzBhZGY4MWJmNjNhZmM1MGZhYTA0YjVjMDZlMSIsCiAgICAgICJtZXRhZGF0YSIgOiB7CiAgICAgICAgIm1vZGVsIiA6ICJzbGltIgogICAgICB9CiAgICB9CiAgfQp9"
+ private val sparks = mutableListOf<EntityArmorStand>()
+
+ @SubscribeEvent
+ fun onTick(event: TickEvent.ClientTickEvent) {
+ if (!isEnabled()) return
+
+ Minecraft.getMinecraft().theWorld.loadedEntityList.filter {
+ it is EntityArmorStand && it !in sparks && it.hasSkullTexture(texture)
+ }.forEach { sparks.add(it as EntityArmorStand) }
+ }
+
+ @SubscribeEvent
+ fun onRenderWorld(event: RenderWorldLastEvent) {
+ if (!isEnabled()) return
+
+ val special = SkyHanniMod.feature.fishing.thunderSparkColor
+ val color = Color(SpecialColour.specialToChromaRGB(special), true)
+
+ val playerLocation = LocationUtils.playerLocation()
+ for (spark in sparks) {
+ if (spark.isDead) continue
+ val sparkLocation = spark.getLorenzVec()
+ val block = sparkLocation.getBlockAt()
+ val seeThroughBlocks =
+ sparkLocation.distance(LocationUtils.playerLocation()) < 6 && (block == Blocks.flowing_lava || block == Blocks.lava)
+ event.drawWaypointFilled(
+ sparkLocation.add(-0.5, 0.0, -0.5), color, extraSize = -0.25, seeThroughBlocks = seeThroughBlocks
+ )
+ if (sparkLocation.distance(playerLocation) < 10) {
+ event.drawString(sparkLocation.add(0.0, 1.5, 0.0), "Thunder Spark", seeThroughBlocks = seeThroughBlocks)
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: WorldEvent.Load) {
+ sparks.clear()
+ }
+
+ private fun isEnabled(): Boolean {
+ return LorenzUtils.inSkyblock && SkyHanniMod.feature.fishing.thunderSparkHighlight
+ }
+} \ No newline at end of file