From ab1f85b6184cae079a9cbf1d7a8fd840028e47fd Mon Sep 17 00:00:00 2001 From: Eric W <42985687+ericpretzel@users.noreply.github.com> Date: Thu, 3 Aug 2023 07:17:36 -0700 Subject: Merge pull request #357 * Implement ender node tracker feature --- src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt | 1 + .../java/at/hannibal2/skyhanni/config/Storage.java | 15 ++ .../skyhanni/config/features/MiscConfig.java | 55 ++++++ .../hannibal2/skyhanni/features/misc/EnderNode.kt | 30 ++++ .../skyhanni/features/misc/EnderNodeTracker.kt | 199 +++++++++++++++++++++ 5 files changed, 300 insertions(+) create mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/EnderNode.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/misc/EnderNodeTracker.kt (limited to 'src/main/java') diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index f96af1007..721941160 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -368,6 +368,7 @@ class SkyHanniMod { loadModule(GriffinPetWarning()) loadModule(KingTalismanHelper()) loadModule(HarpKeybinds()) + loadModule(EnderNodeTracker()) // diff --git a/src/main/java/at/hannibal2/skyhanni/config/Storage.java b/src/main/java/at/hannibal2/skyhanni/config/Storage.java index f6116a343..6cac70a95 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/Storage.java +++ b/src/main/java/at/hannibal2/skyhanni/config/Storage.java @@ -6,6 +6,7 @@ import at.hannibal2.skyhanni.features.garden.CropAccessory; import at.hannibal2.skyhanni.features.garden.CropType; import at.hannibal2.skyhanni.features.garden.fortuneguide.FarmingItems; import at.hannibal2.skyhanni.features.garden.visitor.VisitorReward; +import at.hannibal2.skyhanni.features.misc.EnderNode; import at.hannibal2.skyhanni.features.misc.FrozenTreasure; import at.hannibal2.skyhanni.features.misc.ghostcounter.GhostData; import at.hannibal2.skyhanni.features.rift.area.westvillage.KloonTerminal; @@ -263,6 +264,20 @@ public class Storage { public Map treasureCount = new HashMap<>(); } + @Expose + public EnderNodeTracker enderNodeTracker = new EnderNodeTracker(); + + public static class EnderNodeTracker { + @Expose + public int totalNodesMined = 0; + + @Expose + public int totalEndermiteNests = 0; + + @Expose + public Map lootCount = new HashMap<>(); + } + @Expose public RiftStorage rift = new RiftStorage(); diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java index dce5f2b3e..e04404ed8 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java @@ -566,6 +566,61 @@ public class MiscConfig { public Position position = new Position(10, 80, false, true); } + @Expose + @ConfigOption(name = "Ender Node Tracker", desc = "") + @Accordion + public EnderNodeTracker enderNodeTracker = new EnderNodeTracker(); + + public static class EnderNodeTracker { + @Expose + @ConfigOption( + name = "Enabled", + desc = "Tracks all of your drops from mining Ender Nodes in the End.\n" + + "Also tracks drops from Endermen." + ) + @ConfigEditorBoolean + public boolean enabled = false; + + @Expose + @ConfigOption( + name = "Text Format", + desc = "Drag text to change the appearance of the overlay." + ) + @ConfigEditorDraggableList( + exampleText = { + "§5§lEnder Node Tracker", + "§d1,303 Ender Nodes Mined", + "§615.3M Coins Made", + " ", + "§b123 §cEndermite Nest", + "§b832 §aEnchanted End Stone", + "§b230 §aEnchanted Obsidian", + "§b1630 §aEnchanted Ender Pearl", + "§b85 §aGrand Experience Bottle", + "§b4 §9Titanic Experience Bottle", + "§b15 §9End Stone Shulker", + "§b53 §9End Stone Geode", + "§b10 §d◆ Magical Rune I", + "§b24 §5Ender Gauntlet", + "§b357 §5Mite Gel", + "§b2 §cShrimp The Fish", + " ", + "§b200 §5Ender Armor", + "§b24 §5Ender Helmet", + "§b24 §5Ender Chestplate", + "§b24 §5Ender Leggings", + "§b24 §5Ender Boots", + "§b24 §5Ender Necklace", + "§f10§7-§a8§7-§93§7-§52§7-§61 §fEnderman Pet", + " " + } + ) + public List textFormat = new ArrayList<>(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 14, 15, 16, 17, 23)); + + @Expose + public Position position = new Position(10, 80, false, true); + } + @Expose @ConfigOption(name = "Custom Text box", desc = "") @Accordion diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/EnderNode.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/EnderNode.kt new file mode 100644 index 000000000..e9d374f91 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/EnderNode.kt @@ -0,0 +1,30 @@ +package at.hannibal2.skyhanni.features.misc + +enum class EnderNode( + val internalName: String, + val displayName: String, + ) { + + ENCHANTED_ENDSTONE("ENCHANTED_ENDSTONE","§aEnchanted End Stone"), + ENCHANTED_OBSIDIAN("ENCHANTED_OBSIDIAN","§aEnchanted Obsidian"), + ENCHANTED_ENDER_PEARL("ENCHANTED_ENDER_PEARL","§aEnchanted Ender Pearl"), + GRAND_EXP_BOTTLE("GRAND_EXP_BOTTLE","§aGrand Experience Bottle"), + TITANIC_EXP_BOTTLE("TITANIC_EXP_BOTTLE", "§9Titanic Experience Bottle"), + END_STONE_SHULKER("END_STONE_SHULKER","§9End Stone Shulker"), + ENDSTONE_GEODE("ENDSTONE_GEODE","§9End Stone Geode"), + MAGIC_RUNE("MAGIC_RUNE;1", "§d◆ Magical Rune I"), + ENDER_GAUNTLET("ENDER_GAUNTLET","§5Ender Gauntlet"), + MITE_GEL("MITE_GEL", "§5Mite Gel"), + SHRIMP_THE_FISH("SHRIMP_THE_FISH", "§cShrimp the Fish"), + + END_HELMET("END_HELMET", "§5Ender Helmet"), + END_CHESTPLATE("END_CHESTPLATE", "§5Ender Chestplate"), + END_LEGGINGS("END_LEGGINGS", "§5Ender Leggings"), + END_BOOTS("END_BOOTS", "§5Ender Boots"), + ENDER_NECKLACE("ENDER_NECKLACE", "§5Ender Necklace"), + COMMON_ENDERMAN_PET("ENDERMAN;0", "§fEnderman"), + UNCOMMON_ENDERMAN_PET("ENDERMAN;1", "§aEnderman"), + RARE_ENDERMAN_PET("ENDERMAN;2", "§9Enderman"), + EPIC_ENDERMAN_PET("ENDERMAN;3", "§5Enderman"), + LEGENDARY_ENDERMAN_PET("ENDERMAN;4", "§6Enderman") +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/EnderNodeTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/EnderNodeTracker.kt new file mode 100644 index 000000000..90c00bdad --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/EnderNodeTracker.kt @@ -0,0 +1,199 @@ +package at.hannibal2.skyhanni.features.misc + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.data.ProfileStorageData +import at.hannibal2.skyhanni.data.ScoreboardData +import at.hannibal2.skyhanni.events.ConfigLoadEvent +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.PlaySoundEvent +import at.hannibal2.skyhanni.features.bazaar.BazaarApi +import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.addAsSingletonList +import at.hannibal2.skyhanni.utils.LorenzUtils.editCopy +import at.hannibal2.skyhanni.utils.NEUItems +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.format +import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class EnderNodeTracker { + private val config get() = SkyHanniMod.feature.misc.enderNodeTracker; + + private var totalNodesMined = 0 + private var totalEndermiteNests = 0 + private var totalEnderArmor = 0 + private var display = emptyList>() + private var lootCount = mapOf() + private var lootProfit = mapOf() + + private val enderNodeRegex = Regex("""ENDER NODE!.+You found (\d+x )?§r(.+)§r§f!""") + private val endermanRegex = Regex("""(RARE|PET) DROP! §r(.+) §r§b\(""") + + private var lastEndermiteTime = 0L + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (!ProfileStorageData.loaded) return + if (!isInTheEnd()) return + + // don't call removeColor because we want to distinguish enderman pet rarity + val message = event.message.trim() + var item: String? = null + var amount = 1 + + // check whether the loot is from an ender node or an enderman + enderNodeRegex.find(message)?.let { + totalNodesMined++ + amount = it.groups[1]?.value?.substringBefore("x")?.toIntOrNull() ?: 1 + item = it.groups[2]?.value + } ?: endermanRegex.find(message)?.let { + amount = 1 + item = it.groups[2]?.value + } + + when { + item == null -> return + isEnderArmor(item) -> totalEnderArmor++ + item == "§cEndermite Nest" -> { + lastEndermiteTime = System.currentTimeMillis() + totalEndermiteNests++ + } + } + + // increment the count of the specific item found + EnderNode.entries.find { it.displayName == item }?.let { + val old = lootCount[it] ?: 0 + lootCount = lootCount.editCopy { + this[it] = old + amount + } + } + saveAndUpdate() + } + + @SubscribeEvent + fun onSoundPlay(event: PlaySoundEvent) { + if (!isInTheEnd()) return + if (event.soundName != "mob.silverfish.kill") return + if (event.distanceToPlayer > 15) return + if (System.currentTimeMillis() - lastEndermiteTime > 7500) return + + // listen for nearby endermite death sounds within 7.5s of mining an endermite nest + // this is a fairly accurate approximation for mite gel drops + val oldEndStone = lootCount[EnderNode.ENCHANTED_ENDSTONE] ?: 0 + val oldMiteGel = lootCount[EnderNode.MITE_GEL] ?: 0 + lootCount = lootCount.editCopy { + this[EnderNode.ENCHANTED_ENDSTONE] = oldEndStone + (1 + Math.random() * 2).toInt() + this[EnderNode.MITE_GEL] = oldMiteGel + (1 + Math.random() * 2).toInt() + } + saveAndUpdate() + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent.GameOverlayRenderEvent) { + if (!config.enabled) return + if (!isInTheEnd()) return + config.position.renderStringsAndItems(display, posLabel = "Ender Node Tracker") + } + + @SubscribeEvent + fun onConfigLoad(event: ConfigLoadEvent) { + val hidden = ProfileStorageData.profileSpecific?.enderNodeTracker ?: return + totalNodesMined = hidden.totalNodesMined + totalEndermiteNests = hidden.totalEndermiteNests + lootCount = hidden.lootCount + totalEnderArmor = hidden.lootCount.filter { isEnderArmor(it.key.displayName) }.map { it.value }.sum() + saveAndUpdate() + } + + private fun calculateProfit(): Map { + val newProfit = mutableMapOf() + lootCount.forEach { + val price = if (isEnderArmor(it.key.displayName)) { + 10_000.0 + } else { + val bzData = BazaarApi.getBazaarDataByInternalName(it.key.internalName) + if (LorenzUtils.noTradeMode) { + bzData?.npcPrice ?: georgePrice(it.key) ?: 0.0 + } else { + + bzData?.npcPrice + ?.coerceAtLeast(bzData.sellPrice) + ?.coerceAtLeast(georgePrice(it.key) ?: 0.0) + ?: NEUItems.getPrice(it.key.internalName) + + } + + } + newProfit[it.key] = price * (lootCount[it.key] ?: 0) + } + return newProfit + } + + private fun saveAndUpdate() { + val hidden = ProfileStorageData.profileSpecific?.enderNodeTracker ?: return + hidden.totalNodesMined = totalNodesMined + hidden.totalEndermiteNests = totalEndermiteNests + hidden.lootCount = lootCount + + lootProfit = calculateProfit() + display = formatDisplay(drawDisplay()) + } + + private fun isInTheEnd() = LorenzUtils.inIsland(IslandType.THE_END) + && ScoreboardData.sidebarLines.any { it.contains("The End") } + + private fun isEnderArmor(displayName: String?) = when (displayName) { + "§5Ender Helmet", + "§5Ender Chestplate", + "§5Ender Leggings", + "§5Ender Boots", + "§5Ender Necklace", + "§5Ender Gauntlet" -> true + else -> false + } + + private fun georgePrice(petRarity: EnderNode): Double? = when (petRarity) { + EnderNode.COMMON_ENDERMAN_PET -> 100.0 + EnderNode.UNCOMMON_ENDERMAN_PET -> 500.0 + EnderNode.RARE_ENDERMAN_PET -> 2_000.0 + EnderNode.EPIC_ENDERMAN_PET -> 10_000.0 + EnderNode.LEGENDARY_ENDERMAN_PET -> 1_000_000.0 + else -> null + } + + private fun drawDisplay() = buildList> { + addAsSingletonList("§5§lEnder Node Tracker") + addAsSingletonList("§d${totalNodesMined.addSeparators()} Ender Nodes Mined") + addAsSingletonList("§6${format(lootProfit.values.sum())} Coins Made") + addAsSingletonList(" ") + addAsSingletonList("§b${totalEndermiteNests.addSeparators()} §cEndermite Nest") + + for (item in EnderNode.entries.subList(0, 11)) { + val count = (lootCount[item] ?: 0).addSeparators() + val profit = format(lootProfit[item] ?: 0.0) + addAsSingletonList("§b$count ${item.displayName} §7(§6$profit§7)") + } + addAsSingletonList(" ") + addAsSingletonList("§b${totalEnderArmor.addSeparators()} §5Ender Armor " + + "§7(§6${format(totalEnderArmor * 10_000)}§7)") + for (item in EnderNode.entries.subList(11, 16)) { + val count = (lootCount[item] ?: 0).addSeparators() + val profit = format(lootProfit[item] ?: 0.0) + addAsSingletonList("§b$count ${item.displayName} §7(§6$profit§7)") + } + // enderman pet rarities + val (c, u, r, e, l) = EnderNode.entries.subList(16, 21).map { (lootCount[it] ?: 0).addSeparators() } + val profit = format(EnderNode.entries.subList(16, 21).sumOf { lootProfit[it] ?: 0.0 }) + addAsSingletonList("§f$c§7-§a$u§7-§9$r§7-§5$e§7-§6$l §fEnderman Pet §7(§6$profit§7)") + } + + private fun formatDisplay(map: List>): List> { + val newList = mutableListOf>() + for (index in config.textFormat) { + newList.add(map[index]) + } + return newList + } +} -- cgit