aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2
diff options
context:
space:
mode:
authorhannibal2 <24389977+hannibal00212@users.noreply.github.com>2023-06-09 22:36:52 +0200
committerhannibal2 <24389977+hannibal00212@users.noreply.github.com>2023-06-09 22:36:52 +0200
commit50905075a70f4419f6772a99ae854d49d105351e (patch)
tree39ae9d8011168a65039caad66caa343e69893beb /src/main/java/at/hannibal2
parent1647e8c472ba950e122b89c7c4b2889dd254d1ea (diff)
downloadskyhanni-50905075a70f4419f6772a99ae854d49d105351e.tar.gz
skyhanni-50905075a70f4419f6772a99ae854d49d105351e.tar.bz2
skyhanni-50905075a70f4419f6772a99ae854d49d105351e.zip
Added Item Profit Tracker, Items on Ground and Broken Hyperion Warning
Diffstat (limited to 'src/main/java/at/hannibal2')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt10
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/features/Slayer.java51
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt61
-rw-r--r--src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt110
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/PurseChangeEvent.kt12
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/SlayerChangeEvent.kt3
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/slayer/DetectBrokenHyperion.kt52
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerItemProfitTracker.kt184
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerItemsOnGround.kt54
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerType.kt5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt17
11 files changed, 554 insertions, 5 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
index b0a96be99..b4a9ebe75 100644
--- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
+++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
@@ -58,10 +58,7 @@ import at.hannibal2.skyhanni.features.mobs.AshfangMinisNametagHider
import at.hannibal2.skyhanni.features.mobs.MobHighlight
import at.hannibal2.skyhanni.features.nether.ashfang.*
import at.hannibal2.skyhanni.features.nether.reputationhelper.CrimsonIsleReputationHelper
-import at.hannibal2.skyhanni.features.slayer.EndermanSlayerBeacon
-import at.hannibal2.skyhanni.features.slayer.HideMobNames
-import at.hannibal2.skyhanni.features.slayer.HighlightSlayerMiniBoss
-import at.hannibal2.skyhanni.features.slayer.SlayerQuestWarning
+import at.hannibal2.skyhanni.features.slayer.*
import at.hannibal2.skyhanni.features.slayer.blaze.BlazeSlayerClearView
import at.hannibal2.skyhanni.features.slayer.blaze.BlazeSlayerDaggerHelper
import at.hannibal2.skyhanni.features.slayer.blaze.BlazeSlayerFirePitsWarning
@@ -146,6 +143,8 @@ class SkyHanniMod {
loadModule(FarmingContestAPI)
loadModule(FriendAPI())
loadModule(PartyAPI())
+ loadModule(SlayerAPI)
+ loadModule(PurseAPI())
// features
loadModule(BazaarOrderHelper())
@@ -293,6 +292,9 @@ class SkyHanniMod {
loadModule(ShowFishingItemName())
loadModule(WarpTabComplete)
loadModule(PlayerTabComplete)
+ loadModule(SlayerItemProfitTracker())
+ loadModule(SlayerItemsOnGround())
+ loadModule(DetectBrokenHyperion())
init()
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/Slayer.java b/src/main/java/at/hannibal2/skyhanni/config/features/Slayer.java
index eb9bd078a..e243fcc81 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/Slayer.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/Slayer.java
@@ -82,6 +82,57 @@ public class Slayer {
public boolean blazeClearView = false;
@Expose
+ @ConfigOption(name = "Item Profit Tracker", desc = "")
+ @Accordion
+ public ItemProfitTracker itemProfitTracker = new ItemProfitTracker();
+
+ public static class ItemProfitTracker {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Count all items you pick up while doing slayer, " +
+ "keep track of how much you pay for starting slayers and calculating the overall profit.")
+ @ConfigEditorBoolean
+ public boolean enabled = true;
+
+ @Expose
+ @ConfigOption(name = "Price in Chat", desc = "Show an extra chat message when you pick up an item. " +
+ "(This contains name, amount and price)")
+ @ConfigEditorBoolean
+ public boolean priceInChat = false;
+
+ @Expose
+ @ConfigOption(name = "Minimum Price", desc = "Items below this price will not show up in chat.")
+ @ConfigEditorSlider(minValue = 1, maxValue = 5_000_000, minStep = 1)
+ public int minimumPrice = 100_000;
+ }
+
+ @Expose
+ @ConfigOption(name = "Items on Ground", desc = "")
+ @Accordion
+ public ItemsOnGround itemsOnGround = new ItemsOnGround();
+
+ public static class ItemsOnGround {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Show the name and price of items laying on the ground. §cOnly in slayer areas!")
+ @ConfigEditorBoolean
+ public boolean enabled = true;
+
+ @Expose
+ @ConfigOption(name = "Minimum Price", desc = "Items below this price will be ignored.")
+ @ConfigEditorSlider(minValue = 1, maxValue = 1_000_000, minStep = 1)
+ public int minimumPrice = 50_000;
+ }
+
+ @Expose
+ @ConfigOption(name = "Broken Wither Impact",
+ desc = "Warns when right-clicking with a Wither Impact weapon (e.g. Hyperion) no longer gains combat exp. " +
+ "Kill a mob with melee-hits to fix this hypixel bug. §cOnly works while doing slayers!"
+ )
+ @ConfigEditorBoolean
+ public boolean brokenHyperion = true;
+
+ @Expose
@ConfigOption(name = "Miniboss Highlight", desc = "Highlight slayer miniboss in blue color.")
@ConfigEditorBoolean
public boolean slayerMinibossHighlight = false;
diff --git a/src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt
new file mode 100644
index 000000000..750e23794
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/PurseAPI.kt
@@ -0,0 +1,61 @@
+package at.hannibal2.skyhanni.data
+
+import at.hannibal2.skyhanni.events.InventoryCloseEvent
+import at.hannibal2.skyhanni.events.PurseChangeCause
+import at.hannibal2.skyhanni.events.PurseChangeEvent
+import at.hannibal2.skyhanni.utils.NumberUtil.formatNumber
+import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
+import net.minecraft.client.Minecraft
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+
+class PurseAPI {
+ private val pattern = "(Piggy|Purse): §6(?<coins>[\\d,]*).*".toPattern()
+ private var currentPurse = 0.0
+ private var inventoryCloseTime = 0L
+
+ @SubscribeEvent
+ fun onInventoryClose(event: InventoryCloseEvent) {
+ inventoryCloseTime = System.currentTimeMillis()
+ }
+
+ @SubscribeEvent
+ fun onTick(event: TickEvent.ClientTickEvent) {
+ if (event.phase != TickEvent.Phase.START) return
+
+ for (line in ScoreboardData.sidebarLinesFormatted) {
+ val newPurse = pattern.matchMatcher(line) {
+ group("coins").formatNumber().toDouble()
+ } ?: continue
+ val diff = newPurse - currentPurse
+ if (diff == 0.0) continue
+ currentPurse = newPurse
+
+ PurseChangeEvent(diff, getCause(diff)).postAndCatch()
+ }
+ }
+
+ // TODO add more causes in the future (e.g. ah/bz/bank)
+ private fun getCause(diff: Double): PurseChangeCause {
+ if (diff > 0) {
+ if (diff == 1.0) {
+ return PurseChangeCause.GAIN_TALISMAN_OF_COINS
+ }
+ if (Minecraft.getMinecraft().currentScreen == null) {
+ val timeDiff = System.currentTimeMillis() - inventoryCloseTime
+ if (timeDiff > 2_000) {
+ return PurseChangeCause.GAIN_MOB_KILL
+ }
+
+ }
+ return PurseChangeCause.GAIN_UNKNOWN
+ } else {
+ val timeDiff = System.currentTimeMillis() - SlayerAPI.questStartTime
+ if (timeDiff < 1500) {
+ return PurseChangeCause.LOSE_SLAYER_QUEST_STARTED
+ }
+
+ return PurseChangeCause.LOSE_UNKNOWN
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt
new file mode 100644
index 000000000..6b4adc54c
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt
@@ -0,0 +1,110 @@
+package at.hannibal2.skyhanni.data
+
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.events.SlayerChangeEvent
+import at.hannibal2.skyhanni.features.bazaar.BazaarApi
+import at.hannibal2.skyhanni.features.slayer.SlayerType
+import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
+import at.hannibal2.skyhanni.utils.ItemUtils.nameWithEnchantment
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.nextAfter
+import at.hannibal2.skyhanni.utils.NEUItems
+import at.hannibal2.skyhanni.utils.NumberUtil
+import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import com.google.common.cache.CacheBuilder
+import net.minecraft.item.ItemStack
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import net.minecraftforge.fml.common.gameevent.TickEvent
+import java.util.concurrent.TimeUnit
+
+object SlayerAPI {
+
+ var tick = 0
+
+ private var nameCache =
+ CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build<Pair<String, Int>, Pair<String, Double>>()
+
+ var questStartTime = 0L
+ var isInSlayerArea = false
+ private var latestSlayerCategory = ""
+ private var latestProgressChangeTime = 0L
+ private var latestSlayerProgress = ""
+
+ fun hasActiveSlayerQuest() = latestSlayerCategory != ""
+
+ fun getLatestProgressChangeTime() = if (latestSlayerProgress == "§eSlay the boss!") {
+ System.currentTimeMillis()
+ } else latestProgressChangeTime
+
+
+ // TODO use repo
+ fun ignoreSlayerDrop(name: String) = when (name.removeColor()) {
+ // maybe everywhere?
+ "Stone" -> true
+ "Head" -> true
+
+ // Spider
+ "Cobweb" -> true
+ "String" -> true
+ "Spider Eye" -> true
+ "Bone" -> true
+
+ // Blaze
+ "Water Bottle" -> true
+
+ else -> false
+ }
+
+ fun getItemNameAndPrice(stack: ItemStack): Pair<String, Double> {
+ val internalName = stack.getInternalName()
+ val amount = stack.stackSize
+ val key = internalName to amount
+ nameCache.getIfPresent(key)?.let {
+ return it
+ }
+
+ val amountFormat = if (amount != 1) "§7${amount}x §r" else ""
+ val displayName = NEUItems.getItemStack(internalName).nameWithEnchantment
+
+ val price = NEUItems.getPrice(internalName)
+ val npcPrice = BazaarApi.getBazaarDataByInternalName(internalName)?.npcPrice ?: 0.0
+ val maxPrice = npcPrice.coerceAtLeast(price)
+ val totalPrice = maxPrice * amount
+
+ val format = NumberUtil.format(totalPrice)
+ val priceFormat = " §7(§6$format coins§7)"
+
+ val result = "$amountFormat$displayName$priceFormat" to totalPrice
+ nameCache.put(key, result)
+ return result
+ }
+
+ @SubscribeEvent
+ fun onChat(event: LorenzChatEvent) {
+ if (event.message.contains("§r§5§lSLAYER QUEST STARTED!")) {
+ questStartTime = System.currentTimeMillis()
+ }
+ }
+
+ @SubscribeEvent
+ fun onTick(event: TickEvent.ClientTickEvent) {
+ if (event.phase != TickEvent.Phase.START) return
+ if (!LorenzUtils.inSkyBlock) return
+
+ val slayerQuest = ScoreboardData.sidebarLinesFormatted.nextAfter("Slayer Quest") ?: ""
+ if (slayerQuest != latestSlayerCategory) {
+ SlayerChangeEvent(latestSlayerCategory, slayerQuest).postAndCatch()
+ latestSlayerCategory = slayerQuest
+ }
+
+ val slayerProgress = ScoreboardData.sidebarLinesFormatted.nextAfter("Slayer Quest", 2) ?: ""
+ if (latestSlayerProgress != slayerProgress) {
+ latestSlayerProgress = slayerProgress
+ latestProgressChangeTime = System.currentTimeMillis()
+ }
+
+ if (tick++ % 5 == 0) {
+ isInSlayerArea = SlayerType.getByArea(LorenzUtils.skyBlockArea) != null
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/events/PurseChangeEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/PurseChangeEvent.kt
new file mode 100644
index 000000000..50343a7d9
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/PurseChangeEvent.kt
@@ -0,0 +1,12 @@
+package at.hannibal2.skyhanni.events
+
+class PurseChangeEvent(val coins: Double, val reason: PurseChangeCause) : LorenzEvent()
+
+enum class PurseChangeCause {
+ GAIN_MOB_KILL,
+ GAIN_TALISMAN_OF_COINS,
+ GAIN_UNKNOWN,
+
+ LOSE_SLAYER_QUEST_STARTED,
+ LOSE_UNKNOWN,
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/events/SlayerChangeEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/SlayerChangeEvent.kt
new file mode 100644
index 000000000..9bbf1672a
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/SlayerChangeEvent.kt
@@ -0,0 +1,3 @@
+package at.hannibal2.skyhanni.events
+
+class SlayerChangeEvent(val oldSlayer: String, val newSlayer: String): LorenzEvent() \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/slayer/DetectBrokenHyperion.kt b/src/main/java/at/hannibal2/skyhanni/features/slayer/DetectBrokenHyperion.kt
new file mode 100644
index 000000000..53baff2f1
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/slayer/DetectBrokenHyperion.kt
@@ -0,0 +1,52 @@
+package at.hannibal2.skyhanni.features.slayer
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.SlayerAPI
+import at.hannibal2.skyhanni.data.TitleUtils
+import at.hannibal2.skyhanni.events.PurseChangeCause
+import at.hannibal2.skyhanni.events.PurseChangeEvent
+import at.hannibal2.skyhanni.utils.LorenzLogger
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+class DetectBrokenHyperion {
+ private val config get() = SkyHanniMod.feature.slayer
+ private var brokenInRow = 0
+ private val logger = LorenzLogger("detect_broken_hyperion")
+
+ @SubscribeEvent
+ fun onPurseChange(event: PurseChangeEvent) {
+ if (!isEnabled()) return
+ if (event.reason != PurseChangeCause.GAIN_MOB_KILL) return
+ if (!SlayerAPI.hasActiveSlayerQuest()) return
+ if (!SlayerAPI.isInSlayerArea) return
+
+ val diff = System.currentTimeMillis() - SlayerAPI.getLatestProgressChangeTime()
+ logger.log("diff: $diff")
+
+ if (diff < 2_500) {
+ if (brokenInRow != 0) {
+
+ brokenInRow = 0
+ logger.log(" reset to 0")
+ }
+ return
+ }
+
+ brokenInRow++
+ logger.log(" add: $brokenInRow")
+
+ if (brokenInRow > 5) {
+ logger.log(" send warning!")
+ TitleUtils.sendTitle("§eBroken Hyperion!", 3_000)
+ LorenzUtils.chat(
+ "§e[SkyHanni] Your Hyperion is broken! It no longer collects combat exp. " +
+ "Kill a mob with meele-hits to fix this hypixel bug"
+ )
+ }
+
+ LorenzUtils.debug("diff: $diff")
+ }
+
+ fun isEnabled() = LorenzUtils.inSkyBlock && config.brokenHyperion
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerItemProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerItemProfitTracker.kt
new file mode 100644
index 000000000..33f9f3170
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerItemProfitTracker.kt
@@ -0,0 +1,184 @@
+package at.hannibal2.skyhanni.features.slayer
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.SlayerAPI
+import at.hannibal2.skyhanni.events.*
+import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
+import at.hannibal2.skyhanni.utils.ItemUtils.name
+import at.hannibal2.skyhanni.utils.ItemUtils.nameWithEnchantment
+import at.hannibal2.skyhanni.utils.LorenzLogger
+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.LorenzUtils.sortedDesc
+import at.hannibal2.skyhanni.utils.NEUItems
+import at.hannibal2.skyhanni.utils.NumberUtil
+import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
+import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems
+import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import com.google.common.cache.CacheBuilder
+import net.minecraft.client.Minecraft
+import net.minecraft.entity.item.EntityItem
+import net.minecraft.network.play.server.S0DPacketCollectItem
+import net.minecraftforge.fml.common.eventhandler.EventPriority
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.concurrent.TimeUnit
+
+class SlayerItemProfitTracker {
+ private val config get() = SkyHanniMod.feature.slayer.itemProfitTracker
+ private var collectedCache = CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS).build<Int, Unit>()
+
+ private var itemLogCategory = ""
+ private var display = listOf<List<Any>>()
+ private var itemLogs = mapOf<String, ItemLog>()
+ private val logger = LorenzLogger("slayer_item_profit_tracker")
+
+ private fun addSlayerCosts(price: Double) {
+ val itemLog = currentLog() ?: return
+
+ val name = "Slayer Spawn Costs"
+ itemLog.items = itemLog.items.editCopy {
+ val (oldCoins, oldAmount) = getOrDefault(name, 0.0 to 0)
+ this[name] = oldCoins + price to oldAmount + 1
+ }
+ update()
+ }
+
+ @SubscribeEvent
+ fun onPurseChange(event: PurseChangeEvent) {
+ if (!isEnabled()) return
+ val coins = event.coins
+ if (event.reason == PurseChangeCause.GAIN_MOB_KILL) {
+ if (SlayerAPI.isInSlayerArea) {
+ logger.log("Coins gained for killing mobs: ${coins.addSeparators()}")
+ addMobKillCoins(coins)
+ }
+ }
+ if (event.reason == PurseChangeCause.LOSE_SLAYER_QUEST_STARTED) {
+ logger.log("Coins paid for starting slayer quest: ${coins.addSeparators()}")
+ addSlayerCosts(coins)
+ }
+ }
+
+ @SubscribeEvent
+ fun onSlayerChange(event: SlayerChangeEvent) {
+ val newSlayer = event.newSlayer
+ itemLogCategory = if (newSlayer == "") {
+ ""
+ } else {
+ newSlayer.split(" ").dropLast(1).joinToString(" ")
+ }
+ update()
+ }
+
+ private fun addMobKillCoins(coins: Double) {
+ val itemLog = currentLog() ?: return
+
+ itemLog.mobKillCoins += coins
+ update()
+ }
+
+ private fun addItemPickup(totalPrice: Double, displayName: String, stackSize: Int) {
+ val itemLog = currentLog() ?: return
+
+ itemLog.items = itemLog.items.editCopy {
+ val (oldCoins, oldAmount) = getOrDefault(displayName, 0.0 to 0)
+ this[displayName] = oldCoins + totalPrice to oldAmount + stackSize
+ }
+ update()
+ }
+
+ private fun currentLog(): ItemLog? {
+ if (itemLogCategory == "") return null
+
+ itemLogs[itemLogCategory]?.let {
+ return it
+ }
+
+ val itemLog = ItemLog(itemLogCategory)
+ itemLogs = itemLogs.editCopy { this[itemLogCategory] = itemLog }
+
+ return itemLog
+ }
+
+ @SubscribeEvent(priority = EventPriority.LOW, receiveCanceled = true)
+ fun onChatPacket(event: PacketEvent.ReceiveEvent) {
+ if (!isEnabled()) return
+ if (!SlayerAPI.isInSlayerArea) return
+
+ val packet = event.packet
+ if (packet !is S0DPacketCollectItem) return
+
+ val entityID = packet.collectedItemEntityID
+ val item = Minecraft.getMinecraft().theWorld.getEntityByID(entityID) ?: return
+ if (item !is EntityItem) return
+
+ if (collectedCache.getIfPresent(entityID) != null) return
+ collectedCache.put(entityID, Unit)
+
+ val itemStack = item.entityItem
+ val name = itemStack.name ?: return
+ if (SlayerAPI.ignoreSlayerDrop(name)) return
+ val internalName = itemStack.getInternalName()
+ if (internalName == "") return
+
+ val (itemName, price) = SlayerAPI.getItemNameAndPrice(itemStack)
+ val displayName = NEUItems.getItemStack(internalName).nameWithEnchantment ?: "internalName"
+ addItemPickup(price, displayName, itemStack.stackSize)
+ logger.log("Coins gained for picking up an item ($itemName) ${price.addSeparators()}")
+ if (config.priceInChat) {
+ if (config.minimumPrice < price) {
+ LorenzUtils.chat("§e[SkyHanni] §a+Slayer Drop§7: §r$itemName")
+ }
+ }
+ }
+
+ fun update() {
+ display = drawDisplay()
+ }
+
+ private fun drawDisplay() = buildList<List<Any>> {
+ val itemLog = currentLog() ?: return@buildList
+
+ val displayName = itemLog.displayName.removeColor()
+ addAsSingletonList("§e§l$displayName Profit Tracker")
+ var profit = 0.0
+ val map = mutableMapOf<String, Double>()
+ for ((name, value) in itemLog.items) {
+ val (price, amount) = value
+ val profitPrefix = if (price < 0) "§c" else "§6"
+ val priceFormat = NumberUtil.format(price)
+ map["§7${amount.addSeparators()}x $name§7: $profitPrefix$priceFormat"] = price
+ profit += price
+ }
+ val mobKillCoins = itemLog.mobKillCoins
+ if (mobKillCoins != 0.0) {
+ val mobKillCoinsFormat = NumberUtil.format(mobKillCoins)
+ map["§7Mob kill coins: §6$mobKillCoinsFormat"] = mobKillCoins
+ }
+
+ for (text in map.sortedDesc().keys) {
+ addAsSingletonList(" $text")
+ }
+
+ profit += mobKillCoins
+ val profitFormat = NumberUtil.format(profit)
+ val profitPrefix = if (profit < 0) "§c" else "§6"
+ addAsSingletonList("§eTotal Profit: $profitPrefix$profitFormat")
+ }
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: GuiRenderEvent) {
+ if (!isEnabled()) return
+ if (!SlayerAPI.isInSlayerArea) return
+ SkyHanniMod.feature.dev.debugPos.renderStringsAndItems(display, posLabel = "Slayer Item Profit Tracker")
+ }
+
+ fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled
+
+ class ItemLog(val displayName: String) {
+ // display name -> (totalCoins, amount)
+ var items = mapOf<String, Pair<Double, Int>>()
+ var mobKillCoins = 0.0
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerItemsOnGround.kt b/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerItemsOnGround.kt
new file mode 100644
index 000000000..64b5e9fce
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerItemsOnGround.kt
@@ -0,0 +1,54 @@
+package at.hannibal2.skyhanni.features.slayer
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.SlayerAPI
+import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
+import at.hannibal2.skyhanni.utils.ItemUtils.name
+import at.hannibal2.skyhanni.utils.LocationUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzVec
+import at.hannibal2.skyhanni.utils.RenderUtils.drawString
+import at.hannibal2.skyhanni.utils.RenderUtils.exactLocation
+import com.google.common.cache.CacheBuilder
+import net.minecraft.client.Minecraft
+import net.minecraft.entity.item.EntityItem
+import net.minecraft.init.Items
+import net.minecraftforge.client.event.RenderWorldLastEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.util.concurrent.TimeUnit
+
+class SlayerItemsOnGround {
+ private val config get() = SkyHanniMod.feature.slayer.itemsOnGround
+
+ private var itemsOnGround =
+ CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS)
+ .build<EntityItem, Pair<LorenzVec, String>>()
+
+ @SubscribeEvent
+ fun onRenderWorld(event: RenderWorldLastEvent) {
+ if (!LorenzUtils.inSkyBlock) return
+ if (!config.enabled) return
+ if (!SlayerAPI.isInSlayerArea) return
+
+ for (entityItem in Minecraft.getMinecraft().theWorld.loadedEntityList.filterIsInstance<EntityItem>()) {
+ val location = event.exactLocation(entityItem).add(0.0, 0.8, 0.0)
+ if (location.distance(LocationUtils.playerLocation()) > 15) continue
+
+ val itemStack = entityItem.entityItem
+ val name = itemStack.name ?: continue
+ if (SlayerAPI.ignoreSlayerDrop(name)) continue
+ // happens in spiders den sometimes
+ if (itemStack.item == Items.spawn_egg) continue
+ if (itemStack.getInternalName() == "") continue
+
+ val (itemName, price) = SlayerAPI.getItemNameAndPrice(itemStack)
+ if (config.minimumPrice > price) continue
+
+ itemsOnGround.put(entityItem, location to itemName)
+ }
+
+ for ((location, text) in itemsOnGround.asMap().values) {
+ event.drawString(location, text)
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerType.kt b/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerType.kt
index 37a879106..73069c586 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerType.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerType.kt
@@ -21,7 +21,9 @@ enum class SlayerType(val displayName: String, val clazz: Class<*>) {
"Coal Mine",
-> REVENANT
- "Spiders Den",
+ "Spider Mound",
+ "Arachne's Burrow",
+ "Arachne's Sanctuary",
-> TARANTULA
"Ruins",
@@ -30,6 +32,7 @@ enum class SlayerType(val displayName: String, val clazz: Class<*>) {
"The End",
"Void Sepulture",
+ "Zealot Bruiser Hideout",
-> VOID
"Stronghold",
diff --git a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
index 556eb59a0..656030c97 100644
--- a/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/utils/LorenzUtils.kt
@@ -346,4 +346,21 @@ object LorenzUtils {
child.set(rootObj, value)
}
}
+
+ fun List<String>.nextAfter(after: String, skip: Int = 1): String? {
+ var missing = -1
+ for (line in this) {
+ if (line == after) {
+ missing = skip - 1
+ continue
+ }
+ if (missing == 0) {
+ return line
+ }
+ if (missing != -1) {
+ missing--
+ }
+ }
+ return null
+ }
} \ No newline at end of file