From bd1c148c5cfeee0d2e5d7e3ed04498a496a435b6 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal00212@users.noreply.github.com> Date: Tue, 7 Nov 2023 21:51:46 +0100 Subject: renamed SlayerItemProfitTracker to SlayerProfitTracker --- src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt | 4 +- .../java/at/hannibal2/skyhanni/config/Storage.java | 4 +- .../hannibal2/skyhanni/config/commands/Commands.kt | 4 +- .../features/slayer/SlayerItemProfitTracker.kt | 387 --------------------- .../features/slayer/SlayerProfitTracker.kt | 387 +++++++++++++++++++++ 5 files changed, 393 insertions(+), 393 deletions(-) delete mode 100644 src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerItemProfitTracker.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerProfitTracker.kt diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index b31dbbc31..81a82904a 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -273,9 +273,9 @@ import at.hannibal2.skyhanni.features.rift.everywhere.motes.RiftMotesOrb import at.hannibal2.skyhanni.features.rift.everywhere.motes.ShowMotesNpcSellPrice import at.hannibal2.skyhanni.features.slayer.HideMobNames import at.hannibal2.skyhanni.features.slayer.SlayerBossSpawnSoon -import at.hannibal2.skyhanni.features.slayer.SlayerItemProfitTracker import at.hannibal2.skyhanni.features.slayer.SlayerItemsOnGround import at.hannibal2.skyhanni.features.slayer.SlayerMiniBossFeatures +import at.hannibal2.skyhanni.features.slayer.SlayerProfitTracker import at.hannibal2.skyhanni.features.slayer.SlayerQuestWarning import at.hannibal2.skyhanni.features.slayer.SlayerRngMeterDisplay import at.hannibal2.skyhanni.features.slayer.VampireSlayerFeatures @@ -554,7 +554,7 @@ class SkyHanniMod { loadModule(WarpTabComplete) loadModule(PlayerTabComplete) loadModule(GetFromSacksTabComplete) - loadModule(SlayerItemProfitTracker) + loadModule(SlayerProfitTracker) loadModule(SlayerItemsOnGround()) loadModule(RestorePieceOfWizardPortalLore()) loadModule(QuickModMenuSwitch) diff --git a/src/main/java/at/hannibal2/skyhanni/config/Storage.java b/src/main/java/at/hannibal2/skyhanni/config/Storage.java index edab10936..cf8bd827a 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/Storage.java +++ b/src/main/java/at/hannibal2/skyhanni/config/Storage.java @@ -16,7 +16,7 @@ import at.hannibal2.skyhanni.features.mining.powdertracker.PowderTracker; import at.hannibal2.skyhanni.features.misc.trevor.TrevorTracker; import at.hannibal2.skyhanni.features.misc.visualwords.VisualWord; import at.hannibal2.skyhanni.features.rift.area.westvillage.KloonTerminal; -import at.hannibal2.skyhanni.features.slayer.SlayerItemProfitTracker; +import at.hannibal2.skyhanni.features.slayer.SlayerProfitTracker; import at.hannibal2.skyhanni.utils.LorenzVec; import at.hannibal2.skyhanni.utils.NEUInternalName; import com.google.gson.annotations.Expose; @@ -345,7 +345,7 @@ public class Storage { } @Expose - public Map slayerProfitData = new HashMap<>(); + public Map slayerProfitData = new HashMap<>(); @Expose public Map slayerRngMeter = new HashMap<>(); diff --git a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt index c612a9742..47d267bbc 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt @@ -34,7 +34,7 @@ import at.hannibal2.skyhanni.features.misc.MarkedPlayerManager import at.hannibal2.skyhanni.features.misc.discordrpc.DiscordRPCManager import at.hannibal2.skyhanni.features.misc.massconfiguration.DefaultConfigFeatures import at.hannibal2.skyhanni.features.misc.visualwords.VisualWordGui -import at.hannibal2.skyhanni.features.slayer.SlayerItemProfitTracker +import at.hannibal2.skyhanni.features.slayer.SlayerProfitTracker import at.hannibal2.skyhanni.test.PacketTest import at.hannibal2.skyhanni.test.SkyHanniConfigSearchResetCommand import at.hannibal2.skyhanni.test.SkyHanniDebugsAndTests @@ -151,7 +151,7 @@ object Commands { registerCommand( "shclearslayerprofits", "Clearing the total slayer profit for the current slayer type" - ) { SlayerItemProfitTracker.clearProfitCommand(it) } + ) { SlayerProfitTracker.clearProfitCommand(it) } registerCommand( "shimportghostcounterdata", "Manually importing the ghost counter data from GhostCounterV3" diff --git a/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerItemProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerItemProfitTracker.kt deleted file mode 100644 index 7c5cb5b6c..000000000 --- a/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerItemProfitTracker.kt +++ /dev/null @@ -1,387 +0,0 @@ -package at.hannibal2.skyhanni.features.slayer - -import at.hannibal2.skyhanni.SkyHanniMod -import at.hannibal2.skyhanni.config.Storage -import at.hannibal2.skyhanni.data.SlayerAPI -import at.hannibal2.skyhanni.events.GuiRenderEvent -import at.hannibal2.skyhanni.events.PacketEvent -import at.hannibal2.skyhanni.events.PurseChangeCause -import at.hannibal2.skyhanni.events.PurseChangeEvent -import at.hannibal2.skyhanni.events.RepositoryReloadEvent -import at.hannibal2.skyhanni.events.SackChangeEvent -import at.hannibal2.skyhanni.events.SlayerChangeEvent -import at.hannibal2.skyhanni.events.SlayerQuestCompleteEvent -import at.hannibal2.skyhanni.features.bazaar.BazaarApi.Companion.getBazaarData -import at.hannibal2.skyhanni.test.PriceSource -import at.hannibal2.skyhanni.utils.EntityUtils -import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull -import at.hannibal2.skyhanni.utils.ItemUtils.name -import at.hannibal2.skyhanni.utils.KeyboardManager -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.addSelector -import at.hannibal2.skyhanni.utils.LorenzUtils.sortedDesc -import at.hannibal2.skyhanni.utils.NEUInternalName -import at.hannibal2.skyhanni.utils.NEUItems.getNpcPriceOrNull -import at.hannibal2.skyhanni.utils.NEUItems.getPriceOrNull -import at.hannibal2.skyhanni.utils.NumberUtil -import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators -import at.hannibal2.skyhanni.utils.StringUtils -import at.hannibal2.skyhanni.utils.StringUtils.removeColor -import at.hannibal2.skyhanni.utils.jsonobjects.SlayerProfitTrackerItemsJson -import at.hannibal2.skyhanni.utils.renderables.Renderable -import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker -import at.hannibal2.skyhanni.utils.tracker.TrackerData -import com.google.common.cache.CacheBuilder -import com.google.gson.annotations.Expose -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 -import kotlin.time.Duration.Companion.seconds - -object SlayerItemProfitTracker { - private val config get() = SkyHanniMod.feature.slayer.itemProfitTracker - private var collectedCache = CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS).build() - - private var itemLogCategory = "" - private var baseSlayerType = "" - private var display = emptyList>() - private val logger = LorenzLogger("slayer/profit_tracker") - private var lastClickDelay = 0L - private val trackers = mutableMapOf>() - - class Data : TrackerData() { - override fun reset() { - items.clear() - mobKillCoins = 0 - slayerSpawnCost = 0 - slayerCompletedCount = 0 - } - - @Expose - var items: MutableMap = HashMap() - - @Expose - var mobKillCoins: Long = 0 - - @Expose - var slayerSpawnCost: Long = 0 - - @Expose - var slayerCompletedCount = 0 - - class SlayerItemProfit { - @Expose - var internalName: NEUInternalName? = null - - @Expose - var timesDropped: Long = 0 - - @Expose - var totalAmount: Long = 0 - - @Expose - var hidden = false - - override fun toString() = "SlayerItemProfit{" + - "internalName='" + internalName + '\'' + - ", timesDropped=" + timesDropped + - ", totalAmount=" + totalAmount + - ", hidden=" + hidden + - '}' - } - - override fun toString() = "SlayerProfitList{" + - "items=" + items + - ", mobKillCoins=" + mobKillCoins + - ", slayerSpawnCost=" + slayerSpawnCost + - ", slayerCompletedCount=" + slayerCompletedCount + - '}' - } - - private fun addSlayerCosts(price: Int) { - getTracker()?.modify { - it.slayerSpawnCost += price - } - update() - } - - private var allowedItems = mapOf>() - - @SubscribeEvent - fun onRepoReload(event: RepositoryReloadEvent) { - allowedItems = event.getConstant("SlayerProfitTrackerItems").slayers - } - - @SubscribeEvent - fun onPurseChange(event: PurseChangeEvent) { - if (!isEnabled()) return - val coins = event.coins - if (event.reason == PurseChangeCause.GAIN_MOB_KILL && SlayerAPI.isInCorrectArea) { - logger.log("Coins gained for killing mobs: ${coins.addSeparators()}") - addMobKillCoins(coins.toInt()) - } - if (event.reason == PurseChangeCause.LOSE_SLAYER_QUEST_STARTED) { - logger.log("Coins paid for starting slayer quest: ${coins.addSeparators()}") - addSlayerCosts(coins.toInt()) - } - } - - @SubscribeEvent - fun onSlayerChange(event: SlayerChangeEvent) { - val newSlayer = event.newSlayer - itemLogCategory = newSlayer.removeColor() - baseSlayerType = itemLogCategory.substringBeforeLast(" ") - update() - } - - private fun addMobKillCoins(coins: Int) { - getTracker()?.modify { - it.mobKillCoins += coins - } - update() - } - - private fun addItemPickup(internalName: NEUInternalName, stackSize: Int) { - getTracker()?.modify { - val slayerItemProfit = it.items.getOrPut(internalName) { Data.SlayerItemProfit() } - - slayerItemProfit.timesDropped++ - slayerItemProfit.totalAmount += stackSize - } - - update() - } - - private fun getTracker(): SkyHanniTracker? { - if (itemLogCategory == "") return null - - return trackers.getOrPut(itemLogCategory) { - val getStorage: (Storage.ProfileSpecific) -> Data = { - it.slayerProfitData.getOrPut( - itemLogCategory - ) { Data() } - } - SkyHanniTracker("$itemLogCategory Profit Tracker", Data(), getStorage) { update() } - } - } - - @SubscribeEvent - fun onQuestComplete(event: SlayerQuestCompleteEvent) { - getTracker()?.modify { - it.slayerCompletedCount++ - } - - update() - } - - @SubscribeEvent - fun onSackChange(event: SackChangeEvent) { - if (!isEnabled()) return - if (!SlayerAPI.isInCorrectArea) return - if (!SlayerAPI.hasActiveSlayerQuest()) return - - for (sackChange in event.sackChanges) { - val change = sackChange.delta - if (change > 0) { - val internalName = sackChange.internalName - addItem(internalName, change) - } - } - } - - @SubscribeEvent(priority = EventPriority.LOW, receiveCanceled = true) - fun onChatPacket(event: PacketEvent.ReceiveEvent) { - if (!isEnabled()) return - if (!SlayerAPI.isInCorrectArea) return - if (!SlayerAPI.hasActiveSlayerQuest()) return - - val packet = event.packet - if (packet !is S0DPacketCollectItem) return - - val entityID = packet.collectedItemEntityID - val item = EntityUtils.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.getInternalNameOrNull() ?: return - addItem(internalName, itemStack.stackSize) - } - - private fun addItem(internalName: NEUInternalName, amount: Int) { - if (!isAllowedItem(internalName)) { - LorenzUtils.debug("Ignored non-slayer item pickup: '$internalName' '$itemLogCategory'") - return - } - - val (itemName, price) = SlayerAPI.getItemNameAndPrice(internalName, amount) - addItemPickup(internalName, amount) - logger.log("Coins gained for picking up an item ($itemName) ${price.addSeparators()}") - if (config.priceInChat && price > config.minimumPrice) { - LorenzUtils.chat("§e[SkyHanni] §a+Slayer Drop§7: §r$itemName") - } - if (config.titleWarning && price > config.minimumPriceWarning) { - LorenzUtils.sendTitle("§a+ $itemName", 5.seconds) - } - } - - private fun isAllowedItem(internalName: NEUInternalName): Boolean { - val allowedList = allowedItems[baseSlayerType] ?: return false - return internalName in allowedList - } - - fun update() { - val tracker = getTracker() ?: return - display = drawDisplay(tracker) - } - - private fun drawDisplay(tracker: SkyHanniTracker) = buildList> { - val itemLog = tracker.currentDisplay() ?: return@buildList - - addAsSingletonList("§e§l$itemLogCategory Profit Tracker") - tracker.addDisplayModeToggle(this) - - var profit = 0.0 - val map = mutableMapOf() - for ((internalName, itemProfit) in itemLog.items) { - val amount = itemProfit.totalAmount - - val price = (getPrice(internalName) * amount).toLong() - - val cleanName = SlayerAPI.getNameWithEnchantmentFor(internalName) - var name = cleanName - val priceFormat = NumberUtil.format(price) - val hidden = itemProfit.hidden - if (hidden) { - while (name.startsWith("§f")) { - name = name.substring(2) - } - name = StringUtils.addFormat(name, "§m") - } - val text = " §7${amount.addSeparators()}x $name§7: §6$priceFormat" - - val timesDropped = itemProfit.timesDropped - val percentage = timesDropped.toDouble() / itemLog.slayerCompletedCount - val perBoss = LorenzUtils.formatPercentage(percentage.coerceAtMost(1.0)) - - val renderable = if (tracker.isInventoryOpen()) Renderable.clickAndHover( - text, listOf( - "§7Dropped §e${timesDropped.addSeparators()} §7times.", - "§7Your drop rate: §c$perBoss", - "", - "§eClick to " + (if (hidden) "show" else "hide") + "!", - "§eControl + Click to remove this item!", - ) - ) { - if (System.currentTimeMillis() > lastClickDelay + 150) { - - if (KeyboardManager.isControlKeyDown()) { - itemLog.items.remove(internalName) - LorenzUtils.chat("§e[SkyHanni] Removed $cleanName §efrom slayer profit display.") - lastClickDelay = System.currentTimeMillis() + 500 - } else { - itemProfit.hidden = !hidden - lastClickDelay = System.currentTimeMillis() - } - update() - } - } else Renderable.string(text) - if (tracker.isInventoryOpen() || !hidden) { - map[renderable] = price - } - profit += price - } - val mobKillCoins = itemLog.mobKillCoins - if (mobKillCoins != 0L) { - val mobKillCoinsFormat = NumberUtil.format(mobKillCoins) - map[Renderable.hoverTips( - " §7Mob kill coins: §6$mobKillCoinsFormat", - listOf( - "§7Killing mobs gives you coins (more with scavenger)", - "§7You got §e$mobKillCoinsFormat §7coins in total this way" - ) - )] = mobKillCoins - profit += mobKillCoins - } - val slayerSpawnCost = itemLog.slayerSpawnCost - if (slayerSpawnCost != 0L) { - val mobKillCoinsFormat = NumberUtil.format(slayerSpawnCost) - map[Renderable.hoverTips( - " §7Slayer Spawn Costs: §c$mobKillCoinsFormat", - listOf("§7You paid §c$mobKillCoinsFormat §7in total", "§7for starting the slayer quests.") - )] = slayerSpawnCost - profit += slayerSpawnCost - } - - for (text in map.sortedDesc().keys) { - addAsSingletonList(text) - } - - val slayerCompletedCount = itemLog.slayerCompletedCount - addAsSingletonList( - Renderable.hoverTips( - "§7Bosses killed: §e${slayerCompletedCount.addSeparators()}", - listOf("§7You killed the $itemLogCategory boss", "§e${slayerCompletedCount.addSeparators()} §7times.") - ) - ) - - val profitFormat = NumberUtil.format(profit) - val profitPrefix = if (profit < 0) "§c" else "§6" - - val profitPerBoss = profit / itemLog.slayerCompletedCount - val profitPerBossFormat = NumberUtil.format(profitPerBoss) - - val text = "§eTotal Profit: $profitPrefix$profitFormat" - addAsSingletonList(Renderable.hoverTips(text, listOf("§7Profit per boss: $profitPrefix$profitPerBossFormat"))) - - if (tracker.isInventoryOpen()) { - addSelector( - "", - getName = { type -> type.displayName }, - isCurrent = { it.ordinal == config.priceFrom }, - onChange = { - config.priceFrom = it.ordinal - update() - } - ) - } - tracker.addSessionResetButton(this) - } - - private fun getPrice(internalName: NEUInternalName) = when (config.priceFrom) { - 0 -> internalName.getBazaarData()?.sellPrice ?: internalName.getPriceOrNull() ?: 0.0 - 1 -> internalName.getBazaarData()?.buyPrice ?: internalName.getPriceOrNull() ?: 0.0 - - else -> internalName.getNpcPriceOrNull() ?: 0.0 - } - - @SubscribeEvent - fun onRenderOverlay(event: GuiRenderEvent) { - if (!isEnabled()) return - if (!SlayerAPI.isInCorrectArea) return - - getTracker()?.renderDisplay(config.pos, display) - } - - fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled - - fun clearProfitCommand(args: Array) { - if (itemLogCategory == "") { - LorenzUtils.chat( - "§c[SkyHanni] No current slayer data found. " + - "Go to a slayer area and start the specific slayer type you want to reset the data of." - ) - return - } - - getTracker()?.resetCommand(args, "shclearslayerprofits") - } -} diff --git a/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerProfitTracker.kt new file mode 100644 index 000000000..b5848a2d6 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerProfitTracker.kt @@ -0,0 +1,387 @@ +package at.hannibal2.skyhanni.features.slayer + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.config.Storage +import at.hannibal2.skyhanni.data.SlayerAPI +import at.hannibal2.skyhanni.events.GuiRenderEvent +import at.hannibal2.skyhanni.events.PacketEvent +import at.hannibal2.skyhanni.events.PurseChangeCause +import at.hannibal2.skyhanni.events.PurseChangeEvent +import at.hannibal2.skyhanni.events.RepositoryReloadEvent +import at.hannibal2.skyhanni.events.SackChangeEvent +import at.hannibal2.skyhanni.events.SlayerChangeEvent +import at.hannibal2.skyhanni.events.SlayerQuestCompleteEvent +import at.hannibal2.skyhanni.features.bazaar.BazaarApi.Companion.getBazaarData +import at.hannibal2.skyhanni.test.PriceSource +import at.hannibal2.skyhanni.utils.EntityUtils +import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull +import at.hannibal2.skyhanni.utils.ItemUtils.name +import at.hannibal2.skyhanni.utils.KeyboardManager +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.addSelector +import at.hannibal2.skyhanni.utils.LorenzUtils.sortedDesc +import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.NEUItems.getNpcPriceOrNull +import at.hannibal2.skyhanni.utils.NEUItems.getPriceOrNull +import at.hannibal2.skyhanni.utils.NumberUtil +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.StringUtils +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import at.hannibal2.skyhanni.utils.jsonobjects.SlayerProfitTrackerItemsJson +import at.hannibal2.skyhanni.utils.renderables.Renderable +import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker +import at.hannibal2.skyhanni.utils.tracker.TrackerData +import com.google.common.cache.CacheBuilder +import com.google.gson.annotations.Expose +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 +import kotlin.time.Duration.Companion.seconds + +object SlayerProfitTracker { + private val config get() = SkyHanniMod.feature.slayer.itemProfitTracker + private var collectedCache = CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS).build() + + private var itemLogCategory = "" + private var baseSlayerType = "" + private var display = emptyList>() + private val logger = LorenzLogger("slayer/profit_tracker") + private var lastClickDelay = 0L + private val trackers = mutableMapOf>() + + class Data : TrackerData() { + override fun reset() { + items.clear() + mobKillCoins = 0 + slayerSpawnCost = 0 + slayerCompletedCount = 0 + } + + @Expose + var items: MutableMap = HashMap() + + @Expose + var mobKillCoins: Long = 0 + + @Expose + var slayerSpawnCost: Long = 0 + + @Expose + var slayerCompletedCount = 0 + + class SlayerItemProfit { + @Expose + var internalName: NEUInternalName? = null + + @Expose + var timesDropped: Long = 0 + + @Expose + var totalAmount: Long = 0 + + @Expose + var hidden = false + + override fun toString() = "SlayerItemProfit{" + + "internalName='" + internalName + '\'' + + ", timesDropped=" + timesDropped + + ", totalAmount=" + totalAmount + + ", hidden=" + hidden + + '}' + } + + override fun toString() = "SlayerProfitList{" + + "items=" + items + + ", mobKillCoins=" + mobKillCoins + + ", slayerSpawnCost=" + slayerSpawnCost + + ", slayerCompletedCount=" + slayerCompletedCount + + '}' + } + + private fun addSlayerCosts(price: Int) { + getTracker()?.modify { + it.slayerSpawnCost += price + } + update() + } + + private var allowedItems = mapOf>() + + @SubscribeEvent + fun onRepoReload(event: RepositoryReloadEvent) { + allowedItems = event.getConstant("SlayerProfitTrackerItems").slayers + } + + @SubscribeEvent + fun onPurseChange(event: PurseChangeEvent) { + if (!isEnabled()) return + val coins = event.coins + if (event.reason == PurseChangeCause.GAIN_MOB_KILL && SlayerAPI.isInCorrectArea) { + logger.log("Coins gained for killing mobs: ${coins.addSeparators()}") + addMobKillCoins(coins.toInt()) + } + if (event.reason == PurseChangeCause.LOSE_SLAYER_QUEST_STARTED) { + logger.log("Coins paid for starting slayer quest: ${coins.addSeparators()}") + addSlayerCosts(coins.toInt()) + } + } + + @SubscribeEvent + fun onSlayerChange(event: SlayerChangeEvent) { + val newSlayer = event.newSlayer + itemLogCategory = newSlayer.removeColor() + baseSlayerType = itemLogCategory.substringBeforeLast(" ") + update() + } + + private fun addMobKillCoins(coins: Int) { + getTracker()?.modify { + it.mobKillCoins += coins + } + update() + } + + private fun addItemPickup(internalName: NEUInternalName, stackSize: Int) { + getTracker()?.modify { + val slayerItemProfit = it.items.getOrPut(internalName) { Data.SlayerItemProfit() } + + slayerItemProfit.timesDropped++ + slayerItemProfit.totalAmount += stackSize + } + + update() + } + + private fun getTracker(): SkyHanniTracker? { + if (itemLogCategory == "") return null + + return trackers.getOrPut(itemLogCategory) { + val getStorage: (Storage.ProfileSpecific) -> Data = { + it.slayerProfitData.getOrPut( + itemLogCategory + ) { Data() } + } + SkyHanniTracker("$itemLogCategory Profit Tracker", Data(), getStorage) { update() } + } + } + + @SubscribeEvent + fun onQuestComplete(event: SlayerQuestCompleteEvent) { + getTracker()?.modify { + it.slayerCompletedCount++ + } + + update() + } + + @SubscribeEvent + fun onSackChange(event: SackChangeEvent) { + if (!isEnabled()) return + if (!SlayerAPI.isInCorrectArea) return + if (!SlayerAPI.hasActiveSlayerQuest()) return + + for (sackChange in event.sackChanges) { + val change = sackChange.delta + if (change > 0) { + val internalName = sackChange.internalName + addItem(internalName, change) + } + } + } + + @SubscribeEvent(priority = EventPriority.LOW, receiveCanceled = true) + fun onChatPacket(event: PacketEvent.ReceiveEvent) { + if (!isEnabled()) return + if (!SlayerAPI.isInCorrectArea) return + if (!SlayerAPI.hasActiveSlayerQuest()) return + + val packet = event.packet + if (packet !is S0DPacketCollectItem) return + + val entityID = packet.collectedItemEntityID + val item = EntityUtils.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.getInternalNameOrNull() ?: return + addItem(internalName, itemStack.stackSize) + } + + private fun addItem(internalName: NEUInternalName, amount: Int) { + if (!isAllowedItem(internalName)) { + LorenzUtils.debug("Ignored non-slayer item pickup: '$internalName' '$itemLogCategory'") + return + } + + val (itemName, price) = SlayerAPI.getItemNameAndPrice(internalName, amount) + addItemPickup(internalName, amount) + logger.log("Coins gained for picking up an item ($itemName) ${price.addSeparators()}") + if (config.priceInChat && price > config.minimumPrice) { + LorenzUtils.chat("§e[SkyHanni] §a+Slayer Drop§7: §r$itemName") + } + if (config.titleWarning && price > config.minimumPriceWarning) { + LorenzUtils.sendTitle("§a+ $itemName", 5.seconds) + } + } + + private fun isAllowedItem(internalName: NEUInternalName): Boolean { + val allowedList = allowedItems[baseSlayerType] ?: return false + return internalName in allowedList + } + + fun update() { + val tracker = getTracker() ?: return + display = drawDisplay(tracker) + } + + private fun drawDisplay(tracker: SkyHanniTracker) = buildList> { + val itemLog = tracker.currentDisplay() ?: return@buildList + + addAsSingletonList("§e§l$itemLogCategory Profit Tracker") + tracker.addDisplayModeToggle(this) + + var profit = 0.0 + val map = mutableMapOf() + for ((internalName, itemProfit) in itemLog.items) { + val amount = itemProfit.totalAmount + + val price = (getPrice(internalName) * amount).toLong() + + val cleanName = SlayerAPI.getNameWithEnchantmentFor(internalName) + var name = cleanName + val priceFormat = NumberUtil.format(price) + val hidden = itemProfit.hidden + if (hidden) { + while (name.startsWith("§f")) { + name = name.substring(2) + } + name = StringUtils.addFormat(name, "§m") + } + val text = " §7${amount.addSeparators()}x $name§7: §6$priceFormat" + + val timesDropped = itemProfit.timesDropped + val percentage = timesDropped.toDouble() / itemLog.slayerCompletedCount + val perBoss = LorenzUtils.formatPercentage(percentage.coerceAtMost(1.0)) + + val renderable = if (tracker.isInventoryOpen()) Renderable.clickAndHover( + text, listOf( + "§7Dropped §e${timesDropped.addSeparators()} §7times.", + "§7Your drop rate: §c$perBoss", + "", + "§eClick to " + (if (hidden) "show" else "hide") + "!", + "§eControl + Click to remove this item!", + ) + ) { + if (System.currentTimeMillis() > lastClickDelay + 150) { + + if (KeyboardManager.isControlKeyDown()) { + itemLog.items.remove(internalName) + LorenzUtils.chat("§e[SkyHanni] Removed $cleanName §efrom slayer profit display.") + lastClickDelay = System.currentTimeMillis() + 500 + } else { + itemProfit.hidden = !hidden + lastClickDelay = System.currentTimeMillis() + } + update() + } + } else Renderable.string(text) + if (tracker.isInventoryOpen() || !hidden) { + map[renderable] = price + } + profit += price + } + val mobKillCoins = itemLog.mobKillCoins + if (mobKillCoins != 0L) { + val mobKillCoinsFormat = NumberUtil.format(mobKillCoins) + map[Renderable.hoverTips( + " §7Mob kill coins: §6$mobKillCoinsFormat", + listOf( + "§7Killing mobs gives you coins (more with scavenger)", + "§7You got §e$mobKillCoinsFormat §7coins in total this way" + ) + )] = mobKillCoins + profit += mobKillCoins + } + val slayerSpawnCost = itemLog.slayerSpawnCost + if (slayerSpawnCost != 0L) { + val mobKillCoinsFormat = NumberUtil.format(slayerSpawnCost) + map[Renderable.hoverTips( + " §7Slayer Spawn Costs: §c$mobKillCoinsFormat", + listOf("§7You paid §c$mobKillCoinsFormat §7in total", "§7for starting the slayer quests.") + )] = slayerSpawnCost + profit += slayerSpawnCost + } + + for (text in map.sortedDesc().keys) { + addAsSingletonList(text) + } + + val slayerCompletedCount = itemLog.slayerCompletedCount + addAsSingletonList( + Renderable.hoverTips( + "§7Bosses killed: §e${slayerCompletedCount.addSeparators()}", + listOf("§7You killed the $itemLogCategory boss", "§e${slayerCompletedCount.addSeparators()} §7times.") + ) + ) + + val profitFormat = NumberUtil.format(profit) + val profitPrefix = if (profit < 0) "§c" else "§6" + + val profitPerBoss = profit / itemLog.slayerCompletedCount + val profitPerBossFormat = NumberUtil.format(profitPerBoss) + + val text = "§eTotal Profit: $profitPrefix$profitFormat" + addAsSingletonList(Renderable.hoverTips(text, listOf("§7Profit per boss: $profitPrefix$profitPerBossFormat"))) + + if (tracker.isInventoryOpen()) { + addSelector( + "", + getName = { type -> type.displayName }, + isCurrent = { it.ordinal == config.priceFrom }, + onChange = { + config.priceFrom = it.ordinal + update() + } + ) + } + tracker.addSessionResetButton(this) + } + + private fun getPrice(internalName: NEUInternalName) = when (config.priceFrom) { + 0 -> internalName.getBazaarData()?.sellPrice ?: internalName.getPriceOrNull() ?: 0.0 + 1 -> internalName.getBazaarData()?.buyPrice ?: internalName.getPriceOrNull() ?: 0.0 + + else -> internalName.getNpcPriceOrNull() ?: 0.0 + } + + @SubscribeEvent + fun onRenderOverlay(event: GuiRenderEvent) { + if (!isEnabled()) return + if (!SlayerAPI.isInCorrectArea) return + + getTracker()?.renderDisplay(config.pos, display) + } + + fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled + + fun clearProfitCommand(args: Array) { + if (itemLogCategory == "") { + LorenzUtils.chat( + "§c[SkyHanni] No current slayer data found. " + + "Go to a slayer area and start the specific slayer type you want to reset the data of." + ) + return + } + + getTracker()?.resetCommand(args, "shclearslayerprofits") + } +} -- cgit