aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/features/damageindicator
diff options
context:
space:
mode:
authorLorenz <lo.scherf@gmail.com>2022-08-17 03:05:34 +0200
committerLorenz <lo.scherf@gmail.com>2022-08-17 03:05:34 +0200
commitefbf06263d6fbf5de5a815fe5583ddd02710470f (patch)
treecb56d5969f8bebf586298475a61c521229663fda /src/main/java/at/hannibal2/skyhanni/features/damageindicator
parent9b8ff26e84dda348685fe1306abed7457b63f7e3 (diff)
downloadSkyHanni-efbf06263d6fbf5de5a815fe5583ddd02710470f.tar.gz
SkyHanni-efbf06263d6fbf5de5a815fe5583ddd02710470f.tar.bz2
SkyHanni-efbf06263d6fbf5de5a815fe5583ddd02710470f.zip
moving packets around
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features/damageindicator')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/damageindicator/BossFinder.kt569
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/damageindicator/BossType.kt70
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/damageindicator/DamageCounter.kt10
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/damageindicator/DamageIndicatorManager.kt544
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/damageindicator/EntityData.kt22
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/damageindicator/EntityResult.kt8
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/damageindicator/OldDamage.kt4
7 files changed, 1227 insertions, 0 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/features/damageindicator/BossFinder.kt b/src/main/java/at/hannibal2/skyhanni/features/damageindicator/BossFinder.kt
new file mode 100644
index 000000000..a0895c753
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/damageindicator/BossFinder.kt
@@ -0,0 +1,569 @@
+package at.hannibal2.skyhanni.features.damageindicator
+
+import at.hannibal2.skyhanni.features.dungeon.DungeonData
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.baseMaxHealth
+import at.hannibal2.skyhanni.utils.LorenzUtils.matchRegex
+import at.hannibal2.skyhanni.utils.LorenzVec
+import at.hannibal2.skyhanni.utils.getLorenzVec
+import net.minecraft.client.Minecraft
+import net.minecraft.client.entity.EntityOtherPlayerMP
+import net.minecraft.entity.Entity
+import net.minecraft.entity.EntityLiving
+import net.minecraft.entity.EntityLivingBase
+import net.minecraft.entity.boss.EntityDragon
+import net.minecraft.entity.boss.EntityWither
+import net.minecraft.entity.item.EntityArmorStand
+import net.minecraft.entity.monster.*
+import net.minecraft.entity.passive.EntityHorse
+import net.minecraft.entity.passive.EntityWolf
+import net.minecraft.util.AxisAlignedBB
+import java.util.*
+
+class BossFinder {
+
+ //F1
+ private var floor1bonzo1 = false
+ private var floor1bonzo1SpawnTime = 0L
+ private var floor1bonzo2 = false
+ private var floor1bonzo2SpawnTime = 0L
+
+ //F2
+ private var floor2summons1 = false
+ private var floor2summons1SpawnTime = 0L
+ private var floor2summonsDiedOnce = mutableListOf<EntityOtherPlayerMP>()
+ private var floor2secondPhase = false
+ private var floor2secondPhaseSpawnTime = 0L
+
+ //F3
+ private var floor3GuardianShield = false
+ private var floor3GuardianShieldSpawnTime = 0L
+ private var guardians = mutableListOf<EntityGuardian>()
+ private var floor3Professor = false
+ private var floor3ProfessorSpawnTime = 0L
+ private var floor3ProfessorGuardianPrepare = false
+ private var floor3ProfessorGuardianPrepareSpawnTime = 0L
+ private var floor3ProfessorGuardian = false
+ private var floor3ProfessorGuardianEntity: EntityGuardian? = null
+
+ //F5
+ private var floor5lividEntity: EntityOtherPlayerMP? = null
+ private var floor5lividEntitySpawnTime = 0L
+
+ //F6
+ private var floor6Giants = false
+ private var floor6GiantsSpawnTime = 0L
+ private var floor6GiantsSeparateDelay = mutableMapOf<UUID, Long>()
+ private var floor6Sadan = false
+ private var floor6SadanSpawnTime = 0L
+
+ internal fun tryAdd(entity: EntityLivingBase): EntityResult? {
+ if (LorenzUtils.inDungeons) {
+ if (DungeonData.isOneOf("F1", "M1")) {
+ if (floor1bonzo1) {
+ if (entity is EntityOtherPlayerMP) {
+ if (entity.name == "Bonzo ") {
+ return EntityResult(floor1bonzo1SpawnTime)
+ }
+ }
+ }
+ if (floor1bonzo2) {
+ if (entity is EntityOtherPlayerMP) {
+ if (entity.name == "Bonzo ") {
+ return EntityResult(floor1bonzo2SpawnTime, finalDungeonBoss = true)
+ }
+ }
+ }
+ }
+
+ if (DungeonData.isOneOf("F2", "M2")) {
+ if (entity.name == "Summon ") {
+ if (entity is EntityOtherPlayerMP) {
+ if (floor2summons1) {
+ if (!floor2summonsDiedOnce.contains(entity)) {
+ if (entity.health.toInt() != 0) {
+ return EntityResult(floor2summons1SpawnTime)
+ } else {
+ floor2summonsDiedOnce.add(entity)
+ }
+ }
+ }
+ if (floor2secondPhase) {
+ return EntityResult(floor2secondPhaseSpawnTime)
+ }
+ }
+ }
+
+ if (floor2secondPhase) {
+ if (entity is EntityOtherPlayerMP) {
+ //TODO only show scarf after (all/at least x) summons are dead?
+ val result = entity.name == "Scarf "
+ if (result) {
+ return EntityResult(floor2secondPhaseSpawnTime, finalDungeonBoss = true)
+ }
+ }
+ }
+ }
+
+ if (DungeonData.isOneOf("F3", "M3")) {
+ if (entity is EntityGuardian) {
+ if (floor3GuardianShield) {
+ if (guardians.size == 4) {
+ var totalHealth = 0
+ for (guardian in guardians) {
+ totalHealth += guardian.health.toInt()
+ }
+ if (totalHealth == 0) {
+ floor3GuardianShield = false
+ guardians.clear()
+ }
+ } else {
+ findGuardians()
+ }
+ if (guardians.contains(entity)) {
+ return EntityResult(floor3GuardianShieldSpawnTime, true)
+ }
+ }
+ }
+
+ if (floor3Professor) {
+ if (entity is EntityOtherPlayerMP) {
+ if (entity.name == "The Professor") {
+ return EntityResult(
+ floor3ProfessorSpawnTime,
+ floor3ProfessorSpawnTime + 1_000 > System.currentTimeMillis()
+ )
+ }
+ }
+ }
+ if (floor3ProfessorGuardianPrepare) {
+ if (entity is EntityOtherPlayerMP) {
+ if (entity.name == "The Professor") {
+ return EntityResult(floor3ProfessorGuardianPrepareSpawnTime, true)
+ }
+ }
+ }
+
+ if (entity is EntityGuardian) {
+ if (floor3ProfessorGuardian) {
+ if (entity == floor3ProfessorGuardianEntity) {
+ return EntityResult(finalDungeonBoss = true)
+ }
+ }
+ }
+ }
+
+ if (DungeonData.isOneOf("F4", "M4")) {
+ if (entity is EntityGhast) {
+ return EntityResult(bossType = BossType.DUNGEON_F4_THORN,
+ ignoreBlocks = true,
+ finalDungeonBoss = true)
+ }
+
+ }
+
+ if (DungeonData.isOneOf("F5", "M5")) {
+ if (entity is EntityOtherPlayerMP) {
+ if (entity == floor5lividEntity) {
+ return EntityResult(floor5lividEntitySpawnTime, true, finalDungeonBoss = true)
+ }
+ }
+ }
+
+ if (DungeonData.isOneOf("F6", "M6")) {
+ if (entity is EntityGiantZombie && !entity.isInvisible) {
+ if (floor6Giants && entity.posY > 68) {
+ val extraDelay = checkExtraF6GiantsDelay(entity)
+ return EntityResult(
+ floor6GiantsSpawnTime + extraDelay,
+ floor6GiantsSpawnTime + extraDelay + 1_000 > System.currentTimeMillis()
+ )
+ }
+
+ if (floor6Sadan) {
+ return EntityResult(floor6SadanSpawnTime, finalDungeonBoss = true)
+ }
+ }
+ }
+ } else {
+
+ val maxHealth = entity.baseMaxHealth.toInt()
+ if (entity is EntityBlaze) {
+ if (entity.name != "Dinnerbone") {
+ if (entity.hasNameTagWith(2, "§e﴾ §8[§7Lv200§8] §l§8§lAshfang§r ")) {
+ if (maxHealth == 50_000_000) {
+ return EntityResult(bossType = BossType.NETHER_ASHFANG)
+ }
+ //Derpy
+ if (maxHealth == 100_000_000) {
+ return EntityResult(bossType = BossType.NETHER_ASHFANG)
+ }
+ }
+ }
+ }
+ if (entity is EntitySkeleton) {
+ if (entity.hasNameTagWith(5, "§e﴾ §8[§7Lv200§8] §l§8§lBladesoul§r ")) {
+ return EntityResult(bossType = BossType.NETHER_BLADESOUL)
+ }
+ }
+ if (entity is EntityOtherPlayerMP) {
+ if (entity.name == "Mage Outlaw") {
+ return EntityResult(bossType = BossType.NETHER_MAGE_OUTLAW)
+ }
+ if (entity.name == "DukeBarb ") {
+ return EntityResult(bossType = BossType.NETHER_BARBARIAN_DUKE)
+ }
+ }
+ if (entity is EntityWither) {
+ if (entity.hasNameTagWith(4, "§8[§7Lv100§8] §c§5Vanquisher§r ")) {
+ return EntityResult(bossType = BossType.NETHER_VANQUISHER)
+ }
+ }
+ if (entity is EntityEnderman) {
+ if (entity.hasNameTagWith(3, "§c☠ §bVoidgloom Seraph ")) {
+ when (maxHealth) {
+ 300_000, 600_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_ENDERMAN_1)
+ }
+ 15_000_000, 30_000_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_ENDERMAN_2)
+ }
+ 66_666_666, 66_666_666 * 2 -> {
+ return EntityResult(bossType = BossType.SLAYER_ENDERMAN_3)
+ }
+ 300_000_000, 600_000_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_ENDERMAN_4)
+ }
+ }
+ }
+ }
+ if (entity is EntityDragon) {
+ //TODO testing and make right and so
+ return EntityResult(bossType = BossType.END_ENDER_DRAGON)
+ }
+ if (entity is EntityIronGolem) {
+ //TODO testing
+ if (entity.hasNameTagWith(3, "§e﴾ §8[§7Lv100§8] §lEndstone Protector§r ")) {
+ return EntityResult(bossType = BossType.END_ENDSTONE_PROTECTOR, ignoreBlocks = true)
+ }
+ }
+ if (entity is EntityZombie) {
+ if (entity.hasNameTagWith(2, "§c☠ §bRevenant Horror")) {
+ when (maxHealth) {
+ 500, 1_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_ZOMBIE_1)
+ }
+ 20_000, 40_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_ZOMBIE_2)
+ }
+ 400_000, 800_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_ZOMBIE_3)
+ }
+ 1_500_000, 3_000_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_ZOMBIE_4)
+ }
+ }
+ }
+ if (entity.hasNameTagWith(2, "§c☠ §fAtoned Horror ")) {
+ if (maxHealth == 10_000_000 || maxHealth == 20_000_000) {
+ return EntityResult(bossType = BossType.SLAYER_ZOMBIE_5)
+ }
+ }
+ }
+ if (entity is EntityMagmaCube) {
+ if (entity.hasNameTagWith(15, "§e﴾ §8[§7Lv500§8] §l§4§lMagma Boss§r ")) {
+ if (maxHealth == 200_000_000) {
+ return EntityResult(bossType = BossType.NETHER_MAGMA_BOSS, ignoreBlocks = true)
+ }
+ //Derpy
+ if (maxHealth == 400_000_000) {
+ return EntityResult(bossType = BossType.NETHER_MAGMA_BOSS, ignoreBlocks = true)
+ }
+ }
+ }
+ if (entity is EntityHorse) {
+ if (entity.hasNameTagWith(15, "§8[§7Lv100§8] §c§6Headless Horseman§r ")) {
+ if (maxHealth == 3_000_000) {
+ return EntityResult(bossType = BossType.HUB_HEADLESS_HORSEMAN)
+ }
+ //Derpy
+ if (maxHealth == 6_000_000) {
+ return EntityResult(bossType = BossType.HUB_HEADLESS_HORSEMAN)
+ }
+ }
+ }
+ if (entity is EntityBlaze) {
+ if (entity.hasNameTagWith(2, "§c☠ §bInferno Demonlord ")) {
+ when (maxHealth) {
+ 2_500_000, 5_000_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_BLAZE_1)
+ }
+ }
+ }
+ }
+ if (entity is EntitySpider) {
+ if (entity.hasNameTagWith(1, "§5☠ §4Tarantula Broodfather ")) {
+ when (maxHealth) {
+ 740, 1_500 -> {
+ return EntityResult(bossType = BossType.SLAYER_SPIDER_1)
+ }
+ 30_000, 60_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_SPIDER_2)
+ }
+ 900_000, 1_800_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_SPIDER_3)
+ }
+ 2_400_000, 4_800_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_SPIDER_4)
+ }
+ }
+ }
+ }
+ if (entity is EntityWolf) {
+ if (entity.hasNameTagWith(1, "§c☠ §fSven Packmaster ")) {
+ when (maxHealth) {
+ 2_000, 4_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_WOLF_1)
+ }
+ 40_000, 80_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_WOLF_2)
+ }
+ 750_000, 1_500_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_WOLF_3)
+ }
+ 2_000_000, 4_000_000 -> {
+ return EntityResult(bossType = BossType.SLAYER_WOLF_4)
+ }
+ }
+ }
+ }
+ }
+
+ return null
+ }
+
+ private fun checkExtraF6GiantsDelay(entity: EntityGiantZombie): Long {
+ val uuid = entity.uniqueID
+
+ if (floor6GiantsSeparateDelay.contains(uuid)) {
+ return floor6GiantsSeparateDelay[uuid]!!
+ }
+
+ val middle = LorenzVec(-8, 0, 56)
+
+ val loc = entity.getLorenzVec()
+
+ var pos = 0
+
+ //first
+ if (loc.x > middle.x && loc.z > middle.z) {
+ pos = 2
+ }
+
+ //second
+ if (loc.x > middle.x && loc.z < middle.z) {
+ pos = 3
+ }
+
+ //third
+ if (loc.x < middle.x && loc.z < middle.z) {
+ pos = 0
+ }
+
+ //fourth
+ if (loc.x < middle.x && loc.z > middle.z) {
+ pos = 1
+ }
+
+ val extraDelay = 900L * pos
+ floor6GiantsSeparateDelay[uuid] = extraDelay
+
+ return extraDelay
+ }
+
+ fun handleChat(message: String) {
+ if (LorenzUtils.inDungeons) {
+ when (message) {
+ //F1
+ "§c[BOSS] Bonzo§r§f: Gratz for making it this far, but I’m basically unbeatable." -> {
+ floor1bonzo1 = true
+ floor1bonzo1SpawnTime = System.currentTimeMillis() + 11_250
+ }
+
+ "§c[BOSS] Bonzo§r§f: Oh noes, you got me.. what ever will I do?!" -> {
+ floor1bonzo1 = false
+ }
+
+ "§c[BOSS] Bonzo§r§f: Oh I'm dead!" -> {
+ floor1bonzo2 = true
+ floor1bonzo2SpawnTime = System.currentTimeMillis() + 4_200
+ }
+
+ "§c[BOSS] Bonzo§r§f: Alright, maybe I'm just weak after all.." -> {
+ floor1bonzo2 = false
+ }
+
+ //F2
+ "§c[BOSS] Scarf§r§f: ARISE, MY CREATIONS!" -> {
+ floor2summons1 = true
+ floor2summons1SpawnTime = System.currentTimeMillis() + 3_500
+ }
+
+ "§c[BOSS] Scarf§r§f: Those toys are not strong enough I see." -> {
+ floor2summons1 = false
+ }
+
+ "§c[BOSS] Scarf§r§f: Don't get too excited though." -> {
+ floor2secondPhase = true
+ floor2secondPhaseSpawnTime = System.currentTimeMillis() + 6_300
+ }
+
+ "§c[BOSS] Scarf§r§f: Whatever..." -> {
+ floor2secondPhase = false
+ }
+
+ //F3
+ "§c[BOSS] The Professor§r§f: I was burdened with terrible news recently..." -> {
+ floor3GuardianShield = true
+ floor3GuardianShieldSpawnTime = System.currentTimeMillis() + 16_400
+ }
+
+ "§c[BOSS] The Professor§r§f: Even if you took my barrier down, I can still fight." -> {
+ floor3GuardianShield = false
+ }
+
+ "§c[BOSS] The Professor§r§f: Oh? You found my Guardians one weakness?" -> {
+ floor3Professor = true
+ floor3ProfessorSpawnTime = System.currentTimeMillis() + 10_300
+ }
+
+ "§c[BOSS] The Professor§r§f: I see. You have forced me to use my ultimate technique." -> {
+ floor3Professor = false
+
+ floor3ProfessorGuardianPrepare = true
+ floor3ProfessorGuardianPrepareSpawnTime = System.currentTimeMillis() + 10_500
+ }
+
+ "§c[BOSS] The Professor§r§f: The process is irreversible, but I'll be stronger than a Wither now!" -> {
+ floor3ProfessorGuardian = true
+ }
+
+ "§c[BOSS] The Professor§r§f: What?! My Guardian power is unbeatable!" -> {
+ floor3ProfessorGuardian = false
+ }
+
+
+ //F5
+ "§c[BOSS] Livid§r§f: This Orb you see, is Thorn, or what is left of him." -> {
+ floor5lividEntity = findLivid()
+ floor5lividEntitySpawnTime = System.currentTimeMillis() + 13_000
+ }
+
+ //F6
+ "§c[BOSS] Sadan§r§f: ENOUGH!" -> {
+ floor6Giants = true
+ floor6GiantsSpawnTime = System.currentTimeMillis() + 7_400
+ }
+
+ "§c[BOSS] Sadan§r§f: You did it. I understand now, you have earned my respect." -> {
+ floor6Giants = false
+ floor6Sadan = true
+ floor6SadanSpawnTime = System.currentTimeMillis() + 32_500
+ }
+
+ "§c[BOSS] Sadan§r§f: NOOOOOOOOO!!! THIS IS IMPOSSIBLE!!" -> {
+ floor6Sadan = false
+ }
+ }
+
+ if (message.matchRegex("§c\\[BOSS] (.*) Livid§r§f: Impossible! How did you figure out which one I was\\?!")) {
+ floor5lividEntity = null
+ }
+ }
+ }
+
+ fun handleNewEntity(entity: Entity) {
+ if (LorenzUtils.inDungeons) {
+ if (floor3ProfessorGuardian) {
+ if (entity is EntityGuardian) {
+ if (floor3ProfessorGuardianEntity == null) {
+ floor3ProfessorGuardianEntity = entity
+ floor3ProfessorGuardianPrepare = false
+ }
+ }
+ }
+ }
+ }
+
+ private fun findGuardians() {
+ guardians.clear()
+
+ for (entity in Minecraft.getMinecraft().theWorld.loadedEntityList) {
+ if (entity is EntityGuardian) {
+
+ val maxHealth = entity.baseMaxHealth.toInt()
+
+ //F3
+ if (maxHealth == 1_000_000 || maxHealth == 1_200_000) {
+ guardians.add(entity)
+ }
+
+ //F3 Derpy
+ if (maxHealth == 2_000_000 || maxHealth == 2_400_000) {
+ guardians.add(entity)
+ }
+
+ //M3
+ if (maxHealth == 240_000_000 || maxHealth == 280_000_000) {
+ guardians.add(entity)
+ }
+
+ //M3 Derpy
+ if (maxHealth == 120_000_000 || maxHealth == 140_000_000) {
+ guardians.add(entity)
+ }
+ }
+ }
+ }
+
+ private fun findLivid(): EntityOtherPlayerMP? {
+ for (entity in Minecraft.getMinecraft().theWorld.loadedEntityList) {
+ if (entity is EntityOtherPlayerMP) {
+ if (entity.name == "Livid ") {
+ return entity
+ }
+ }
+ }
+
+ return null
+ }
+}
+
+fun EntityLiving.hasNameTagWith(
+ y: Int,
+ contains: String,
+ debugRightEntity: Boolean = false,
+ consumer: (EntityArmorStand) -> Unit = {},
+ inaccuracy: Double = 1.6,
+ debugWrongEntity: Boolean = false,
+): Boolean {
+ val center = getLorenzVec().add(0, y, 0)
+ val a = center.add(-inaccuracy, -inaccuracy - 3, -inaccuracy).toBlocPos()
+ val b = center.add(inaccuracy, inaccuracy + 3, inaccuracy).toBlocPos()
+ val alignedBB = AxisAlignedBB(a, b)
+ val clazz = EntityArmorStand::class.java
+ val found = worldObj.getEntitiesWithinAABB(clazz, alignedBB)
+ return found.any {
+ val result = it.name.contains(contains)
+ if (debugWrongEntity && !result) {
+ println("wrong entity in aabb: '" + it.name + "'")
+ }
+ if (debugRightEntity && result) {
+ println("mob: " + center.printWithAccuracy(2))
+ println("nametag: " + it.getLorenzVec().printWithAccuracy(2))
+ println("accuracy: " + it.getLorenzVec().subtract(center).printWithAccuracy(3))
+ }
+ if (result) consumer(it)
+ result
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/damageindicator/BossType.kt b/src/main/java/at/hannibal2/skyhanni/features/damageindicator/BossType.kt
new file mode 100644
index 000000000..0bae4bbbd
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/damageindicator/BossType.kt
@@ -0,0 +1,70 @@
+package at.hannibal2.skyhanni.features.damageindicator
+
+enum class BossType(val fullName: String, val bossTypeToggle: Int, val shortName: String = fullName) {
+ 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"),
+ SLAYER_ZOMBIE_2("§eRevenant Horror 2", 5, "§eRev 2"),
+ SLAYER_ZOMBIE_3("§cRevenant Horror 3", 5, "§cRev 3"),
+ SLAYER_ZOMBIE_4("§4Revenant Horror 4", 5, "§4Rev 4"),
+ SLAYER_ZOMBIE_5("§5Revenant Horror 5", 5, "§5Rev 5"),
+
+ SLAYER_SPIDER_1("§aTarantula Broodfather 1", 6, "§aTara 1"),
+ SLAYER_SPIDER_2("§eTarantula Broodfather 2", 6, "§eTara 2"),
+ SLAYER_SPIDER_3("§cTarantula Broodfather 3", 6, "§cTara 3"),
+ SLAYER_SPIDER_4("§4Tarantula Broodfather 4", 6, "§4Tara 4"),
+
+ SLAYER_WOLF_1("§aSven Packmaster 1", 7, "§aSven 1"),
+ SLAYER_WOLF_2("§eSven Packmaster 2", 7, "§eSven 2"),
+ SLAYER_WOLF_3("§cSven Packmaster 3", 7, "§cSven 3"),
+ SLAYER_WOLF_4("§4Sven Packmaster 4", 7, "§4Sven 4"),
+
+ SLAYER_ENDERMAN_1("§aVoidgloom Seraph 1", 8, "§aVoid 1"),
+ SLAYER_ENDERMAN_2("§eVoidgloom Seraph 2", 8, "§eVoid 2"),
+ SLAYER_ENDERMAN_3("§cVoidgloom Seraph 3", 8, "§cVoid 3"),
+ SLAYER_ENDERMAN_4("§4Voidgloom Seraph 4", 8, "§4Void 4"),
+
+ SLAYER_BLAZE_1("§aInferno Demonlord 1", 9, "§aInferno 1"),
+
+ HUB_HEADLESS_HORSEMAN("§6Headless Horseman", 10),
+
+ DUNGEON_F1("", 11),
+ DUNGEON_F2("", 12),
+ DUNGEON_F3("", 13),
+ DUNGEON_F4_THORN("§cThorn", 14),
+ DUNGEON_F5("", 15),
+ DUNGEON_F("", 16),
+ DUNGEON_75("", 17),
+
+ //TODO arachne
+
+ //TODO corelone
+ //TODO bal
+
+
+ /**
+ * TODO dungeon mini bosses
+ * shadow assassin
+ * lost adventurer
+ * frozen adventurer
+ * king midas
+ * silverfish 2b one tap - deathmite outside trap
+ * 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/damageindicator/DamageCounter.kt b/src/main/java/at/hannibal2/skyhanni/features/damageindicator/DamageCounter.kt
new file mode 100644
index 000000000..84bc7d50a
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/damageindicator/DamageCounter.kt
@@ -0,0 +1,10 @@
+package at.hannibal2.skyhanni.features.damageindicator
+
+class DamageCounter {
+
+ var currentDamage = 0L
+ var currentHealing = 0L
+ var oldDamages = mutableListOf<OldDamage>()
+ var firstTick = 0L
+
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/damageindicator/DamageIndicatorManager.kt b/src/main/java/at/hannibal2/skyhanni/features/damageindicator/DamageIndicatorManager.kt
new file mode 100644
index 000000000..ddfaed4cf
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/damageindicator/DamageIndicatorManager.kt
@@ -0,0 +1,544 @@
+package at.hannibal2.skyhanni.features.damageindicator
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.ScoreboardData
+import at.hannibal2.skyhanni.events.DamageIndicatorFinalBossEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.features.dungeon.DungeonData
+import at.hannibal2.skyhanni.test.LorenzTest
+import at.hannibal2.skyhanni.utils.*
+import at.hannibal2.skyhanni.utils.LorenzUtils.baseMaxHealth
+import at.hannibal2.skyhanni.utils.LorenzUtils.between
+import at.hannibal2.skyhanni.utils.LorenzUtils.removeColor
+import net.minecraft.client.Minecraft
+import net.minecraft.client.renderer.GlStateManager
+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.client.event.RenderWorldLastEvent
+import net.minecraftforge.event.entity.EntityJoinWorldEvent
+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.text.DecimalFormat
+import java.util.*
+import java.util.regex.Pattern
+import kotlin.math.max
+
+class DamageIndicatorManager {
+
+ var data = mutableMapOf<UUID, EntityData>()
+ private var bossFinder: BossFinder? = null
+ private val decimalFormat = DecimalFormat("0.0")
+ private val maxHealth = mutableMapOf<UUID, Int>()
+ private val damagePattern = Pattern.compile("✧?(\\d+[⚔+✧❤♞☄✷ﬗ]*)")
+
+ @SubscribeEvent
+ fun onWorldLoad(event: WorldEvent.Load) {
+ bossFinder = BossFinder()
+ data.clear()
+ }
+
+ @SubscribeEvent(receiveCanceled = true)
+ fun onChatMessage(event: LorenzChatEvent) {
+ bossFinder?.handleChat(event.message)
+ }
+
+ @SubscribeEvent
+ fun onWorldRender(event: RenderWorldLastEvent) {
+ if (!SkyHanniMod.feature.damageIndicator.enabled) return
+
+ GlStateManager.disableDepth()
+ GlStateManager.disableCull()
+
+ val player = Minecraft.getMinecraft().thePlayer
+
+ //TODO config to define between 100ms and 5 sec
+ for (uuid in data.filter { System.currentTimeMillis() > it.value.timeLastTick + if (it.value.dead) 3_000 else 100 }
+ .map { it.key }) {
+ data.remove(uuid)
+ }
+
+ for (data in data.values) {
+ tickDamage(data.damageCounter)
+ if (!data.ignoreBlocks) {
+ if (!player.canEntityBeSeen(data.entity)) continue
+ }
+ if (data.bossType.bossTypeToggle !in SkyHanniMod.feature.damageIndicator.bossesToShow) continue
+
+ val entity = data.entity
+
+ var healthText = data.healthText
+ val delayedStart = data.delayedStart
+ if (delayedStart != -1L) {
+ if (delayedStart > System.currentTimeMillis()) {
+ val delay = delayedStart - System.currentTimeMillis()
+ healthText = formatDelay(delay)
+ }
+ }
+
+ val partialTicks = event.partialTicks
+
+ val location = if (data.dead && data.deathLocation != null) {
+ data.deathLocation!!
+ } else {
+ val loc = LorenzVec(
+ RenderUtils.interpolate(entity.posX, entity.lastTickPosX, partialTicks),
+ RenderUtils.interpolate(entity.posY, entity.lastTickPosY, partialTicks) + 0.5f,
+ RenderUtils.interpolate(entity.posZ, entity.lastTickPosZ, partialTicks)
+ )
+ if (data.dead) data.deathLocation = loc
+ loc
+ }
+
+ if (!data.healthLineHidden) {
+ RenderUtils.drawLabel(location, healthText, partialTicks, true, 6f)
+ }
+
+ var bossName = when (SkyHanniMod.feature.damageIndicator.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
+ }
+
+ RenderUtils.drawLabel(location, bossName, partialTicks, true, 3.9f, -9.0f)
+
+ if (SkyHanniMod.feature.damageIndicator.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"
+ }
+ RenderUtils.drawLabel(location, finalResult, partialTicks, true, 3.9f, diff)
+ 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"
+ }
+ RenderUtils.drawLabel(loc