aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt1
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/Storage.java15
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/MiscConfig.java55
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/EnderNode.kt30
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/EnderNodeTracker.kt199
5 files changed, 300 insertions, 0 deletions
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;
@@ -264,6 +265,20 @@ public class Storage {
}
@Expose
+ public EnderNodeTracker enderNodeTracker = new EnderNodeTracker();
+
+ public static class EnderNodeTracker {
+ @Expose
+ public int totalNodesMined = 0;
+
+ @Expose
+ public int totalEndermiteNests = 0;
+
+ @Expose
+ public Map<EnderNode, Integer> lootCount = new HashMap<>();
+ }
+
+ @Expose
public RiftStorage rift = new RiftStorage();
public static class 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
@@ -567,6 +567,61 @@ public class MiscConfig {
}
@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<Integer> 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
public TextBox textBox = new TextBox();
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<List<Any>>()
+ private var lootCount = mapOf<EnderNode, Int>()
+ private var lootProfit = mapOf<EnderNode, Double>()
+
+ 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<EnderNode, Double> {
+ val newProfit = mutableMapOf<EnderNode, Double>()
+ 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<List<Any>> {
+ 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<Any>>): List<List<Any>> {
+ val newList = mutableListOf<List<Any>>()
+ for (index in config.textFormat) {
+ newList.add(map[index])
+ }
+ return newList
+ }
+}