summaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/dungeon
diff options
context:
space:
mode:
authorLorenz <ESs95s3P5z8Pheb>2022-07-14 12:06:07 +0200
committerLorenz <ESs95s3P5z8Pheb>2022-07-14 12:06:07 +0200
commita5c540d977a3510812cac7fac340fe17e7d10983 (patch)
treedbbe5b208e6871378a10868d1206d1d78beeb950 /src/main/java/at/hannibal2/skyhanni/dungeon
parentd6c99ed30a2b1cb228b2fdc3d3178cf1f369dc53 (diff)
downloadskyhanni-a5c540d977a3510812cac7fac340fe17e7d10983.tar.gz
skyhanni-a5c540d977a3510812cac7fac340fe17e7d10983.tar.bz2
skyhanni-a5c540d977a3510812cac7fac340fe17e7d10983.zip
renamed mod to SkyHanni
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/dungeon')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/dungeon/DungeonBossMessages.kt51
-rw-r--r--src/main/java/at/hannibal2/skyhanni/dungeon/DungeonChatFilter.kt224
-rw-r--r--src/main/java/at/hannibal2/skyhanni/dungeon/DungeonCleanEnd.kt130
-rw-r--r--src/main/java/at/hannibal2/skyhanni/dungeon/DungeonData.kt46
-rw-r--r--src/main/java/at/hannibal2/skyhanni/dungeon/DungeonDeathCounter.kt97
-rw-r--r--src/main/java/at/hannibal2/skyhanni/dungeon/DungeonHighlightClickedBlocks.kt99
-rw-r--r--src/main/java/at/hannibal2/skyhanni/dungeon/DungeonMilestoneDisplay.kt96
-rw-r--r--src/main/java/at/hannibal2/skyhanni/dungeon/damageindicator/DungeonBossDamageIndicator.kt181
-rw-r--r--src/main/java/at/hannibal2/skyhanni/dungeon/damageindicator/DungeonBossFinder.kt381
-rw-r--r--src/main/java/at/hannibal2/skyhanni/dungeon/damageindicator/EntityData.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/dungeon/damageindicator/EntityResult.kt3
11 files changed, 1314 insertions, 0 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonBossMessages.kt b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonBossMessages.kt
new file mode 100644
index 000000000..0dbfbd732
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonBossMessages.kt
@@ -0,0 +1,51 @@
+package at.hannibal2.skyhanni.dungeon
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.matchRegex
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class DungeonBossMessages {
+
+ @SubscribeEvent
+ fun onChatMessage(event: LorenzChatEvent) {
+ if (!LorenzUtils.inSkyblock) return
+
+ if (!SkyHanniMod.feature.chat.dungeonBossMessages) return
+
+ if (isBoss(event.message)) {
+ event.blockedReason = "dungeon_boss"
+ }
+ }
+
+ private fun isBoss(message: String): Boolean {
+ when {
+ message.matchRegex("§([cd4])\\[BOSS] (.*)") -> {
+ when {
+ message.contains(" The Watcher§r§f: ") -> return true
+ message.contains(" Bonzo§r§f: ") -> return true
+ message.contains(" Scarf§r§f:") -> return true
+ message.contains("Professor§r§f") -> return true
+ message.contains(" Livid§r§f: ") || message.contains(" Enderman§r§f: ") -> return true
+ message.contains(" Thorn§r§f: ") -> return true
+ message.contains(" Sadan§r§f: ") -> return true
+ message.contains(" Maxor§r§c: ") -> return true
+ message.contains(" Storm§r§c: ") -> return true
+ message.contains(" Goldor§r§c: ") -> return true
+ message.contains(" Necron§r§c: ") -> return true
+ message.contains(" §r§4§kWither King§r§c:") -> return true
+
+ message.endsWith(" Necron§r§c: That is enough, fool!") -> return true
+ message.endsWith(" Necron§r§c: Adventurers! Be careful of who you are messing with..") -> return true
+ message.endsWith(" Necron§r§c: Before I have to deal with you myself.") -> return true
+ }
+ }
+
+ //M7 - Dragons
+ message == "§cThe Crystal withers your soul as you hold it in your hands!" -> return true
+ message == "§cIt doesn't seem like that is supposed to go there." -> return true
+ }
+ return false
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonChatFilter.kt b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonChatFilter.kt
new file mode 100644
index 000000000..7f1f2d938
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonChatFilter.kt
@@ -0,0 +1,224 @@
+package at.hannibal2.skyhanni.dungeon
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.matchRegex
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class DungeonChatFilter {
+
+ @SubscribeEvent
+ fun onChatMessage(event: LorenzChatEvent) {
+ if (!LorenzUtils.inSkyblock) return
+
+ if (!SkyHanniMod.feature.chat.dungeonMessages) return
+
+ val blockReason = block(event.message)
+ if (blockReason != "") {
+ event.blockedReason = "dungeon_$blockReason"
+ }
+ }
+
+ private fun block(message: String): String {
+ when {
+ isPrepare(message) -> return "prepare"
+ isStart(message) -> return "start"
+ }
+
+ if (!LorenzUtils.inDungeons) return ""
+
+ return when {
+ isKey(message) -> "key"
+ isDoor(message) -> "door"
+ isPickup(message) -> "pickup"
+ isReminder(message) -> "reminder"
+ isBuff(message) -> "buff"
+ isNotPossible(message) -> "not_possible"
+ isDamage(message) -> "damage"
+ isAbility(message) -> "ability"
+ isPuzzle(message) -> "puzzle"
+ isEnd(message) -> "end"
+
+ else -> ""
+ }
+ }
+
+ private fun isDoor(message: String): Boolean = message == "§cThe §r§c§lBLOOD DOOR§r§c has been opened!"
+
+ private fun isEnd(message: String): Boolean = when {
+ message.matchRegex("(.*) §r§eunlocked §r§d(.*) Essence §r§8x(.*)§r§e!") -> true
+ message.matchRegex(" §r§d(.*) Essence §r§8x(.*)") -> true
+ message.endsWith(" Experience §r§b(Team Bonus)") -> true
+ else -> false
+ }
+
+ private fun isAbility(message: String): Boolean = when {
+ message == "§a§r§6Guided Sheep §r§ais now available!" -> true
+ message.matchRegex("§7Your Guided Sheep hit §r§c(.*) §r§7enemy for §r§c(.*) §r§7damage.") -> true
+ message == "§6Rapid Fire§r§a is ready to use! Press §r§6§lDROP§r§a to activate it!" -> true
+ message == "§6Castle of Stone§r§a is ready to use! Press §r§6§lDROP§r§a to activate it!" -> true
+
+
+ message.matchRegex("§a§lBUFF! §fYou were splashed by (.*) §fwith §r§cHealing VIII§r§f!") -> true
+ message.matchRegex("§aYou were healed for (.*) health by (.*)§a!") -> true
+ message.matchRegex("§aYou gained (.*) HP worth of absorption for 3s from §r(.*)§r§a!") -> true
+ message.matchRegex("§c(.*) §r§epicked up your (.*) Orb!") -> true
+ message.matchRegex("§cThis ability is on cooldown for (.*)s.") -> true
+ message.matchRegex("§a§l(.*) healed you for (.*) health!") -> true
+ message.matchRegex("§eYour bone plating reduced the damage you took by §r§c(.*)§r§e!") -> true
+ message.matchRegex("(.*) §r§eformed a tether with you!") -> true
+ message.matchRegex("§eYour tether with (.*) §r§ehealed you for §r§a(.*) §r§ehealth.") -> true
+ message.matchRegex("§7Your Implosion hit §r§c(.*) §r§7enemy for §r§c(.*) §r§7damage.") -> true
+
+ message.matchRegex("§eYour §r§6Spirit Pet §r§ehealed (.*) §r§efor §r§a(.*) §r§ehealth!") -> true
+ message.matchRegex("§eYour §r§6Spirit Pet §r§ehit (.*) enemy for §r§c(.*) §r§edamage.") -> true
+
+ message == "§dCreeper Veil §r§aActivated!" -> true
+ message == "§dCreeper Veil §r§cDe-activated!" -> true
+ message.matchRegex("§cYou need at least (.*) mana to activate this!") -> true
+
+ message.matchRegex(
+ "§eYou were healed for §r§a(.*)§r§e health by §r(.*)§r§e's §r§9Healing Bow§r§e and " + "gained §r§c\\+(.*) Strength§r§e for 10 seconds."
+ ) -> true
+ message.matchRegex("(.*)§r§a granted you §r§c(.*) §r§astrength for §r§e20 §r§aseconds!") -> true
+
+ message.matchRegex("§eYour fairy healed §r§ayourself §r§efor §r§a(.*) §r§ehealth!") -> true
+ message.matchRegex("§eYour fairy healed §r(.*) §r§efor §r§a(.*) §r§ehealth!") -> true
+ message.matchRegex("(.*) fairy healed you for §r§a(.*) §r§ehealth!") -> true
+
+ else -> false
+ }
+
+ private fun isDamage(message: String): Boolean = when {
+ message == "§cMute silenced you!" -> true
+ message.matchRegex("(.*) §r§aused §r(.*) §r§aon you!") -> true
+ message.matchRegex("§cThe (.*)§r§c struck you for (.*) damage!") -> true
+ message.matchRegex("§cThe (.*) hit you for (.*) damage!") -> true
+ message.matchRegex("§7(.*) struck you for §r§c(.*)§r§7 damage.") -> true
+ message.matchRegex("(.*) hit you for §r§c(.*)§r§7 damage.") -> true
+ message.matchRegex("(.*) hit you for §r§c(.*)§r§7 true damage.") -> true
+ message.matchRegex("§7(.*) exploded, hitting you for §r§c(.*)§r§7 damage.") -> true
+ message.matchRegex("(.*)§r§c hit you with §r(.*) §r§cfor (.*) damage!") -> true
+ message.matchRegex("(.*)§r§a struck you for §r§c(.*)§r§a damage!") -> true
+ message.matchRegex("(.*)§r§c struck you for (.*)!") -> true
+
+ message.matchRegex("§7The Mage's Magma burnt you for §r§c(.*)§r§7 true damage.") -> true
+
+ message.matchRegex("§7Your (.*) hit §r§c(.*) §r§7(enemy|enemies) for §r§c(.*) §r§7damage.") -> true
+ else -> false
+ }
+
+ private fun isNotPossible(message: String): Boolean = when (message) {
+ "§cYou cannot hit the silverfish while it's moving!",
+ "§cYou cannot move the silverfish in that direction!",
+ "§cThere are blocks in the way!",
+ "§cThis chest has already been searched!",
+ "§cThis lever has already been used.",
+ "§cYou cannot do that in this room!",
+ "§cYou do not have the key for this door!",
+ "§cYou have already opened this dungeon chest!",
+ "§cYou cannot use abilities in this room!",
+ "§cA mystical force in this room prevents you from using that ability!" -> true
+
+ else -> false
+ }
+
+ private fun isBuff(message: String): Boolean = when {
+ message.matchRegex("§6§lDUNGEON BUFF! (.*) §r§ffound a §r§dBlessing of (.*)§r§f!(.*)") -> true
+ message.matchRegex("§6§lDUNGEON BUFF! §r§fYou found a §r§dBlessing of (.*)§r§f!(.*)") -> true
+ message.matchRegex("§6§lDUNGEON BUFF! §r§fA §r§dBlessing of (.*)§r§f was found! (.*)") -> true
+ message.matchRegex("§eA §r§a§r§dBlessing of (.*)§r§e was picked up!") -> true
+ message.matchRegex("(.*) §r§ehas obtained §r§a§r§dBlessing of (.*)§r§e!") -> true
+ message.matchRegex(" §r§7(Grants|Granted) you §r§a(.*) Strength §r§7and §r§a(.*) Crit Damage§r§7.") -> true
+ message.matchRegex(" §r§7(Grants|Granted) you §r§a(.*) Defense §r§7and §r§a+(.*) Damage§r§7.") -> true
+ message.matchRegex(" §r§7(Grants|Granted) you §r§a(.*) HP §r§7and §r§a+(.*)% §r§7health regeneration.") -> true
+ message.matchRegex(" §r§7(Grants|Granted) you §r§a(.*) Intelligence §r§7and §r§a+(.*)? Speed§r§7.") -> true
+ message.matchRegex(" §r§7Granted you §r§a+(.*) HP§r§7, §r§a(.*) Defense§r§7, §r§a(.*) Intelligence§r§7, and §r§a(.*) Strength§r§7.") -> true
+ message == "§a§lBUFF! §fYou have gained §r§cHealing V§r§f!" -> true
+ else -> false
+ }
+
+ private fun isPuzzle(message: String): Boolean = when {
+ message.matchRegex("§a§lPUZZLE SOLVED! (.*) §r§ewasn't fooled by §r§c(.*)§r§e! §r§4G§r§co§r§6o§r§ed§r§a §r§2j§r§bo§r§3b§r§5!") -> true
+ message.matchRegex("§a§lPUZZLE SOLVED! (.*) §r§etied Tic Tac Toe! §r§4G§r§co§r§6o§r§ed§r§a §r§2j§r§bo§r§3b§r§5!") -> true
+ message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fThough I sit stationary in this prison that is §r§cThe Catacombs§r§f, my knowledge knows no bounds." -> true
+ message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fProve your knowledge by answering 3 questions and I shall reward you in ways that transcend time!" -> true
+ message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fAnswer incorrectly, and your moment of ineptitude will live on for generations." -> true
+
+// message == "§4[STATUE] Oruo the Omniscient§r§f: §r§f2 questions §r§fleft...and§r§f you will have proven your worth to me!" -> true
+ message == "§4[STATUE] Oruo the Omniscient§r§f: §r§f2 questions left... Then you will have proven your worth to me!" -> true
+
+ message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fOne more question!" -> true
+ message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fI bestow upon you all the power of a hundred years!" -> true
+ message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fYou've already proven enough to me! No need to press more of my buttons!" -> true
+ message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fI've had enough of you and your party fiddling with my buttons. Scram!" -> true
+ message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fEnough! My buttons are not to be pressed with such lack of grace!" -> true
+ message.matchRegex("§4\\[STATUE] Oruo the Omniscient§r§f: §r(.*) §r§fthinks the answer is §r§6 . §r(.*)§r§f! §r§fLock in your party's answer in my Chamber!") -> true
+ else -> false
+ }
+
+ private fun isKey(message: String): Boolean = when {
+ message.matchRegex("(.*) §r§ehas obtained §r§a§r§6§r§8Wither Key§r§e!") -> true
+ message.matchRegex("(.*) opened a §r§8§lWITHER §r§adoor!") -> true
+ message.matchRegex("(.*) §r§ehas obtained §r§a§r§c§r§cBlood Key§r§e!") -> true
+ message.matchRegex("(.*) §r§ehas obtained §r§a§r§9Beating Heart§r§e!") -> true
+ message == "§5A shiver runs down your spine..." -> true
+ message == "§eA §r§a§r§6§r§8Wither Key§r§e was picked up!" -> true
+ message == "§eA §r§a§r§c§r§cBlood Key§r§e was picked up!" -> true
+
+ else -> false
+ }
+
+ private fun isReminder(message: String): Boolean = when (message) {
+ "§e§lRIGHT CLICK §r§7on §r§7a §r§8WITHER §r§7door§r§7 to open it. This key can only be used to open §r§a1§r§7 door!",
+ "§e§lRIGHT CLICK §r§7on §r§7the §r§cBLOOD DOOR§r§7 to open it. This key can only be used to open §r§a1§r§7 door!" -> true
+
+ else -> false
+ }
+
+ private fun isPickup(message: String): Boolean = when {
+ message.matchRegex("(.*) §r§ehas obtained §r§a§r§9Superboom TNT§r§e!") -> true
+ message.matchRegex("(.*) §r§ehas obtained §r§a§r§9Superboom TNT §r§8x2§r§e!") -> true
+ message.matchRegex("§6§lRARE DROP! §r§9Hunk of Blue Ice §r§b\\(+(.*)% Magic Find!\\)") -> true
+ message.matchRegex("(.*) §r§ehas obtained §r§a§r§6Revive Stone§r§e!") -> true
+ message.matchRegex("(.*) §r§ffound a §r§dWither Essence§r§f! Everyone gains an extra essence!") -> true
+ message == "§fYou found a §r§dWither Essence§r§f! Everyone gains an extra essence!" -> true
+ message.matchRegex("§d(.*) the Fairy§r§f: You killed me! Take this §r§6Revive Stone §r§fso that my death is not in vain!") -> true
+ message.matchRegex("§d(.*) the Fairy§r§f: You killed me! I'll revive you so that my death is not in vain!") -> true
+ message.matchRegex("§d(.*) the Fairy§r§f: You killed me! I'll revive your friend §r(.*) §r§fso that my death is not in vain!") -> true
+ message.matchRegex("§d(.*) the Fairy§r§f: Have a great life!") -> true
+ message.matchRegex(
+ "§c(.*) §r§eYou picked up a Ability Damage Orb from (.*) §r§ehealing you for §r§c(.*) §r§eand granting you +§r§c(.*)% §r§eAbility Damage for §r§b10 §r§eseconds."
+ ) -> true
+ message.matchRegex(
+ "§c(.*) §r§eYou picked up a Damage Orb from (.*) §r§ehealing you for §r§c(.*) §r§eand granting you +§r§c(.*)% §r§eDamage for §r§b10 §r§eseconds."
+ ) -> true
+ message.matchRegex("(.*) §r§ehas obtained §r§a§r§9Premium Flesh§r§e!") -> true
+ message.matchRegex("§6§lRARE DROP! §r§9Beating Heart §r§b(.*)") -> true
+ else -> false
+ }
+
+ private fun isStart(message: String): Boolean = when {
+ message == "§e[NPC] §bMort§f: §rHere, I found this map when I first entered the dungeon." -> true
+ message == "§e[NPC] §bMort§f: §rYou should find it useful if you get lost." -> true
+ message == "§e[NPC] §bMort§f: §rGood luck." -> true
+ message == "§e[NPC] §bMort§f: §rTalk to me to change your class and ready up." -> true
+
+ //§a[Berserk] §r§fMelee Damage §r§c48%§r§f -> §r§a88%
+ //§a[Berserk] §r§fWalk Speed §r§c38§r§f -> §r§a68
+ message.matchRegex("§a(.*) §r§f(.*) §r§c(.*)§r§f -> §r§a(.*)") -> true
+ else -> false
+ }
+
+ private fun isPrepare(message: String): Boolean = when {
+ message == "§aYour active Potion Effects have been paused and stored. They will be restored when you leave Dungeons! You are not allowed to use existing Potion Effects while in Dungeons." -> true
+ message.matchRegex("(.*) has started the dungeon countdown. The dungeon will begin in 1 minute.") -> true
+ message.matchRegex("§e[NPC] §bMort§f: §rTalk to me to change your class and ready up.") -> true
+ message.matchRegex("(.*) §a is now ready!") -> true
+ message.matchRegex("§aDungeon starts in (.*) seconds.") -> true
+ message == "§aDungeon starts in 1 second." -> true
+ message == "§aYou can no longer consume or splash any potions during the remainder of this Dungeon run!" -> true
+ else -> false
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonCleanEnd.kt b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonCleanEnd.kt
new file mode 100644
index 000000000..f668fc22d
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonCleanEnd.kt
@@ -0,0 +1,130 @@
+package at.hannibal2.skyhanni.dungeon
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.CheckRenderEntityEvent
+import at.hannibal2.skyhanni.events.DamageIndicatorFinalBossEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.events.PacketEvent
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.matchRegex
+import net.minecraft.client.Minecraft
+import net.minecraft.client.entity.EntityOtherPlayerMP
+import net.minecraft.entity.item.EntityArmorStand
+import net.minecraft.entity.monster.EntityGuardian
+import net.minecraft.network.play.server.S1CPacketEntityMetadata
+import net.minecraft.network.play.server.S2APacketParticles
+import net.minecraftforge.event.world.WorldEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class DungeonCleanEnd {
+
+ private var bossDone = false
+ private var chestsSpawned = false
+ private var lastBossId: Int = -1
+
+ @SubscribeEvent
+ fun onChatMessage(event: LorenzChatEvent) {
+ if (!LorenzUtils.inDungeons) return
+ if (!SkyHanniMod.feature.dungeon.cleanEnd) return
+
+ val message = event.message
+
+ if (message.matchRegex("([ ]*)§r§c(The|Master Mode) Catacombs §r§8- §r§eFloor (.*)")) {
+ chestsSpawned = true
+ }
+ }
+
+ private fun shouldBlock(): Boolean {
+ if (!LorenzUtils.inDungeons) return false
+ if (!SkyHanniMod.feature.dungeon.cleanEnd) return false
+
+ if (!bossDone) return false
+
+ return true
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: WorldEvent.Load) {
+ bossDone = false
+ chestsSpawned = false
+ lastBossId = -1
+ }
+
+ @SubscribeEvent
+ fun onBossDead(event: DamageIndicatorFinalBossEvent) {
+ if (!LorenzUtils.inDungeons) return
+ if (bossDone) return
+
+ if (lastBossId == -1) {
+ lastBossId = event.id
+ }
+ }
+
+ @SubscribeEvent
+ fun onHealthUpdatePacket(event: PacketEvent.ReceiveEvent) {
+ if (!LorenzUtils.inDungeons) return
+ if (!SkyHanniMod.feature.dungeon.cleanEnd) return
+
+ if (bossDone) return
+ if (lastBossId == -1) return
+
+ val packet = event.packet
+ if (packet !is S1CPacketEntityMetadata) return
+ if (packet.entityId != lastBossId) return
+
+ for (watchableObject in packet.func_149376_c()) {
+ if (watchableObject.dataValueId == 6) {
+ val health = watchableObject.`object` as Float
+ if (health < 1) {
+ val dungeonFloor = DungeonData.dungeonFloor
+ LorenzUtils.chat("§eFloor $dungeonFloor done!")
+ bossDone = true
+ }
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onCheckRender(event: CheckRenderEntityEvent<*>) {
+ if (!shouldBlock()) return
+
+ val entity = event.entity
+
+ if (entity == Minecraft.getMinecraft().thePlayer) return
+
+ if (SkyHanniMod.feature.dungeon.cleanEndF3IgnoreGuardians) {
+ if (DungeonData.isOneOf("F3", "M3")) {
+ if (entity is EntityGuardian) {
+ if (entity.entityId != lastBossId) {
+ if (Minecraft.getMinecraft().thePlayer.isSneaking) {
+ return
+ }
+ }
+ }
+ }
+ }
+
+ if (chestsSpawned) {
+ if (entity is EntityArmorStand) {
+ if (!entity.hasCustomName()) {
+ return
+ }
+ }
+ if (entity is EntityOtherPlayerMP) {
+ return
+ }
+ }
+
+ event.isCanceled = true
+ }
+
+ @SubscribeEvent
+ fun onReceivePacket(event: PacketEvent.ReceiveEvent) {
+ if (!shouldBlock()) return
+
+
+ if (event.packet is S2APacketParticles) {
+ event.isCanceled = true
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonData.kt b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonData.kt
new file mode 100644
index 000000000..eedf664a3
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonData.kt
@@ -0,0 +1,46 @@
+package at.hannibal2.skyhanni.dungeon
+
+import at.hannibal2.skyhanni.events.DungeonEnterEvent
+import at.hannibal2.skyhanni.misc.ScoreboardData
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import net.minecraftforge.event.world.WorldEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+
+class DungeonData {
+
+ companion object {
+ var dungeonFloor: String? = null
+
+ fun isOneOf(vararg floors: String): Boolean {
+ for (floor in floors) {
+ if (dungeonFloor == floor) {
+ return true
+ }
+ }
+
+ return false
+ }
+ }
+
+ @SubscribeEvent
+ fun onTick(event: TickEvent.ClientTickEvent) {
+ if (event.phase != TickEvent.Phase.START) return
+ if (LorenzUtils.inDungeons) {
+ if (dungeonFloor == null) {
+ for (line in ScoreboardData.sidebarLines) {
+ if (line.contains("The Catacombs (")) {
+ dungeonFloor = line.substringAfter("(").substringBefore(")")
+ DungeonEnterEvent(dungeonFloor!!).postAndCatch()
+ break
+ }
+ }
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: WorldEvent.Load) {
+ dungeonFloor = null
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonDeathCounter.kt b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonDeathCounter.kt
new file mode 100644
index 000000000..521dbeb6d
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonDeathCounter.kt
@@ -0,0 +1,97 @@
+package at.hannibal2.skyhanni.dungeon
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.DungeonEnterEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.utils.GuiRender.renderString
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.matchRegex
+import net.minecraftforge.client.event.RenderGameOverlayEvent
+import net.minecraftforge.event.world.WorldEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class DungeonDeathCounter {
+
+ private var textToRender = ""
+ private var deaths = 0
+
+ private fun isDeathMessage(message: String): Boolean = when {
+ message.matchRegex("§c ☠ §r§7You were killed by (.*)§r§7 and became a ghost§r§7.") -> true
+ message.matchRegex("§c ☠ §r§7(.*) was killed by (.*) and became a ghost§r§7.") -> true
+
+ message.matchRegex("§c ☠ §r§7You were crushed and became a ghost§r§7.") -> true
+ message.matchRegex("§c ☠ §r§7§r(.*)§r§7 was crushed and became a ghost§r§7.") -> true
+
+ message.matchRegex("§c ☠ §r§7You died to a trap and became a ghost§r§7.") -> true
+ message.matchRegex("§c ☠ §r(.*)§r§7 died to a trap and became a ghost§r§7.") -> true
+
+ message.matchRegex("§c ☠ §r§7You burnt to death and became a ghost§r§7.") -> true
+ message.matchRegex("§c ☠ §r(.*)§r§7 burnt to death and became a ghost§r§7.") -> true
+
+ message.matchRegex("§c ☠ §r§7You died and became a ghost§r§7.") -> true
+ message.matchRegex("§c ☠ §r(.*)§r§7 died and became a ghost§r§7.") -> true
+
+ message.matchRegex("§c ☠ §r§7You suffocated and became a ghost§r§7.") -> true
+ message.matchRegex("§c ☠ §r§7§r(.*)§r§7 suffocated and became a ghost§r§7.") -> true
+
+ message.matchRegex("§c ☠ §r§7You died to a mob and became a ghost§r§7.") -> true
+ message.matchRegex("§c ☠ §r(.*)§7 died to a mob and became a ghost§r§7.") -> true
+
+ message.matchRegex("§c ☠ §r§7You fell into a deep hole and became a ghost§r§7.") -> true
+ message.matchRegex("§c ☠ §r(.*)§r§7 fell into a deep hole and became a ghost§r§7.") -> true
+
+ message.matchRegex("§c ☠ §r§(.*)§r§7 disconnected from the Dungeon and became a ghost§r§7.") -> true
+
+ message.matchRegex("§c ☠ §r§7(.*)§r§7 fell to their death with help from §r(.*)§r§7 and became a ghost§r§7.") -> true
+
+ else -> false
+ }
+
+ @SubscribeEvent(receiveCanceled = true)
+ fun onChatPacket(event: LorenzChatEvent) {
+ if (!isEnabled()) return
+
+ if (isDeathMessage(event.message)) {
+ deaths++
+ LorenzUtils.chat("§c§l$deaths. DEATH!")
+ update()
+ }
+ }
+
+ private fun update() {
+ if (deaths == 0) {
+ textToRender = ""
+ return
+ }
+
+ val color = when (deaths) {
+ 1, 2 -> "§e"
+ 3 -> "§c"
+ else -> "§4"
+ }
+ textToRender = color + "Deaths: $deaths"
+ }
+
+ @SubscribeEvent
+ fun onDungeonStart(event: DungeonEnterEvent) {
+ deaths = 0
+ update()
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: WorldEvent.Load) {
+ deaths = 0
+ update()
+ }
+
+ @SubscribeEvent
+ fun renderOverlay(event: RenderGameOverlayEvent.Post) {
+ if (!isEnabled()) return
+
+ SkyHanniMod.feature.dungeon.deathCounterDisplay.renderString(DungeonMilestoneDisplay.color + textToRender)
+ }
+
+ private fun isEnabled(): Boolean {
+ return LorenzUtils.inDungeons && SkyHanniMod.feature.dungeon.deathCounter
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonHighlightClickedBlocks.kt b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonHighlightClickedBlocks.kt
new file mode 100644
index 000000000..ff0ca6931
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonHighlightClickedBlocks.kt
@@ -0,0 +1,99 @@
+package at.hannibal2.skyhanni.dungeon
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.events.PacketEvent
+import at.hannibal2.skyhanni.utils.*
+import at.hannibal2.skyhanni.utils.BlockUtils.getBlockAt
+import at.hannibal2.skyhanni.utils.RenderUtils.drawColor
+import at.hannibal2.skyhanni.utils.RenderUtils.drawString
+import net.minecraft.init.Blocks
+import net.minecraft.network.play.client.C08PacketPlayerBlockPlacement
+import net.minecraftforge.client.event.RenderWorldLastEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class DungeonHighlightClickedBlocks {
+
+ private val blocks = mutableListOf<ClickedBlock>()
+ private var colorIndex = 0
+ private val colors = listOf(LorenzColor.YELLOW, LorenzColor.AQUA, LorenzColor.GREEN, LorenzColor.LIGHT_PURPLE)
+
+ private fun getNextColor(): LorenzColor {
+ var id = colorIndex + 1
+ if (id == colors.size) id = 0
+ colorIndex = id
+ return colors[colorIndex]
+ }
+
+ @SubscribeEvent
+ fun onChatMessage(event: LorenzChatEvent) {
+ if (!SkyHanniMod.feature.dungeon.highlightClickedBlocks) return
+ if (!LorenzUtils.inDungeons) return
+
+ if (event.message == "§cYou hear the sound of something opening...") {
+ event.blockedReason = "dungeon_highlight_clicked_block"
+ }
+ }
+
+ @SubscribeEvent
+ fun onSendPacket(event: PacketEvent.SendEvent) {
+ if (!SkyHanniMod.feature.dungeon.highlightClickedBlocks) return
+ if (!LorenzUtils.inDungeons) return
+// TODO add
+// if (DungeonAPI.inBossRoom) return
+ if (event.packet !is C08PacketPlayerBlockPlacement || event.packet.stack == null) return
+
+ val position = event.packet.position.toLorenzVec()
+
+ if (blocks.any { it.position == position }) return
+
+ val type: ClickedBlockType = when (position.getBlockAt()) {
+ Blocks.chest, Blocks.trapped_chest -> ClickedBlockType.CHEST
+ Blocks.lever -> ClickedBlockType.LEVER
+ Blocks.skull -> ClickedBlockType.WITHER_ESSENCE
+ else -> return
+ }
+
+ if (type == ClickedBlockType.WITHER_ESSENCE) {
+ val text = BlockUtils.getSkinFromSkull(position.toBlocPos())
+ if (text != "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQ" +
+ "ubmV0L3RleHR1cmUvYzRkYjRhZGZhOWJmNDhmZjVkNDE3M" +
+ "DdhZTM0ZWE3OGJkMjM3MTY1OWZjZDhjZDg5MzQ3NDlhZjRjY2U5YiJ9fX0="
+ ) {
+ return
+ }
+ }
+
+// if (nearWaterRoom() && type == ClickedBlockType.LEVER) return
+
+ val color = getNextColor()
+ val displayText = color.getChatColor() + "Clicked " + type.display
+ blocks.add(ClickedBlock(position, displayText, color, System.currentTimeMillis()))
+ }
+
+ @SubscribeEvent
+ fun onWorldRender(event: RenderWorldLastEvent) {
+ if (!SkyHanniMod.feature.dungeon.highlightClickedBlocks) return
+ if (!LorenzUtils.inDungeons) return
+
+ blocks.removeAll { System.currentTimeMillis() > it.time + 3000 }
+ blocks.forEach {
+ event.drawColor(it.position, it.color)
+ event.drawString(it.position.add(0.5, 0.5, 0.5), it.displayText, true)
+ }
+ }
+
+ class ClickedBlock(val position: LorenzVec, val displayText: String, val color: LorenzColor, val time: Long)
+
+ enum class ClickedBlockType(val display: String) {
+ LEVER("Lever"),
+ CHEST("Chest"),
+ WITHER_ESSENCE("Wither Essence"),
+ }
+
+// private fun nearWaterRoom(): Boolean {
+// val playerLoc =
+// LocationUtils.getPlayerLocation().add(LocationUtils.getPlayerLookingAtDirection().multiply(2)).add(0, 2, 0)
+// return WaterBoardSolver.waterRoomDisplays.any { it.distance(playerLoc) < 3 }
+// }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonMilestoneDisplay.kt b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonMilestoneDisplay.kt
new file mode 100644
index 000000000..96bfba678
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/dungeon/DungeonMilestoneDisplay.kt
@@ -0,0 +1,96 @@
+package at.hannibal2.skyhanni.dungeon
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.DungeonEnterEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.utils.GuiRender.renderString
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.matchRegex
+import net.minecraftforge.client.event.RenderGameOverlayEvent
+import net.minecraftforge.event.world.WorldEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.concurrent.fixedRateTimer
+
+class DungeonMilestoneDisplay {
+
+
+ companion object {
+ private var textToRender = ""
+ var color = ""
+ var currentMilestone = 0
+ var timeReached = 0L
+
+ fun isMilestoneMessage(message: String): Boolean = when {
+ message.matchRegex("§e§l(.*) Milestone §r§e.§r§7: You have dealt §r§c(.*)§r§7 Total Damage so far! §r§a(.*)") -> true
+ message.matchRegex("§e§lArcher Milestone §r§e.§r§7: You have dealt §r§c(.*)§r§7 Ranged Damage so far! §r§a(.*)") -> true
+ message.matchRegex("§e§lHealer Milestone §r§e.§r§7: You have healed §r§a(.*)§r§7 Damage so far! §r§a(.*)") -> true
+ message.matchRegex("§e§lTank Milestone §r§e.§r§7: You have tanked and dealt §r§c(.*)§r§7 Total Damage so far! §r§a(.*)s") -> true
+
+ else -> false
+ }
+ }
+
+ init {
+ fixedRateTimer(name = "dungeon-milestone-display", period = 200) {
+ if (!isEnabled()) return@fixedRateTimer
+ checkVisibility()
+ }
+ }
+
+ private fun checkVisibility() {
+ if (currentMilestone >= 3) {
+ if (System.currentTimeMillis() > timeReached + 3_000)
+ if (textToRender != "") {
+ textToRender = textToRender.substring(1)
+ }
+ }
+ }
+
+ @SubscribeEvent(receiveCanceled = true)
+ fun onChatPacket(event: LorenzChatEvent) {
+ if (!isEnabled()) return
+
+ if (isMilestoneMessage(event.message)) {
+ event.blockedReason = "dungeon_milestone"
+ currentMilestone++
+