diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/minion/InfernoMinionProfitTrackerConfig.kt b/src/main/java/at/hannibal2/skyhanni/config/features/minion/InfernoMinionProfitTrackerConfig.kt new file mode 100644 index 000000000000..835764087c7f --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/minion/InfernoMinionProfitTrackerConfig.kt @@ -0,0 +1,35 @@ +package at.hannibal2.skyhanni.config.features.minion + +import at.hannibal2.skyhanni.config.FeatureToggle +import at.hannibal2.skyhanni.config.core.config.Position +import at.hannibal2.skyhanni.config.features.misc.tracker.individual.IndividualItemTrackerConfig +import com.google.gson.annotations.Expose +import io.github.notenoughupdates.moulconfig.annotations.Accordion +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean +import io.github.notenoughupdates.moulconfig.annotations.ConfigLink +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption + +class InfernoMinionProfitTrackerConfig { + @Expose + @ConfigOption(name = "Enabled", desc = "Track items collected from Inferno Minions.") + @ConfigEditorBoolean + @FeatureToggle + var enabled: Boolean = true + + @Expose + @ConfigLink(owner = InfernoMinionProfitTrackerConfig::class, field = "enabled") + val position: Position = Position(250, 250) + + @Expose + @ConfigOption( + name = "Show After Collection", + desc = "Show the tracker for a few seconds after collecting from an Inferno Minion.", + ) + @ConfigEditorBoolean + var showAfterCollection: Boolean = true + + @Expose + @ConfigOption(name = "Tracker Settings", desc = "") + @Accordion + val perTrackerConfig: IndividualItemTrackerConfig = IndividualItemTrackerConfig() +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/minion/MinionsConfig.kt b/src/main/java/at/hannibal2/skyhanni/config/features/minion/MinionsConfig.kt index 174c6c1f8f57..d9589496ae13 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/minion/MinionsConfig.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/features/minion/MinionsConfig.kt @@ -69,4 +69,9 @@ class MinionsConfig { @ConfigEditorBoolean @FeatureToggle var infernoFuelBlocker: Boolean = false + + @Expose + @ConfigOption(name = "Inferno Minion Profit Tracker", desc = "") + @Accordion + val infernoMinionProfitTracker: InfernoMinionProfitTrackerConfig = InfernoMinionProfitTrackerConfig() } diff --git a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.kt b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.kt index b32133830717..c0244e6762fb 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.kt @@ -45,6 +45,7 @@ import at.hannibal2.skyhanni.features.garden.tracker.PestProfitTracker import at.hannibal2.skyhanni.features.garden.visitor.VisitorReward import at.hannibal2.skyhanni.features.gifting.GiftProfitTracker import at.hannibal2.skyhanni.features.hunting.HuntingProfitTracker +import at.hannibal2.skyhanni.features.minion.InfernoMinionProfitTracker import at.hannibal2.skyhanni.features.inventory.EquipmentApi import at.hannibal2.skyhanni.features.inventory.chocolatefactory.stray.CFStrayTracker import at.hannibal2.skyhanni.features.inventory.experimentationtable.ExperimentsProfitTracker @@ -830,6 +831,9 @@ class ProfileSpecificStorage( } } + @Expose + var infernoMinionProfitTracker: InfernoMinionProfitTracker.Data = InfernoMinionProfitTracker.Data() + // - misc @Expose var trapperData: TrapperData = TrapperData() diff --git a/src/main/java/at/hannibal2/skyhanni/features/minion/InfernoMinionFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/minion/InfernoMinionFeatures.kt index 0f24df896f0b..cd1fc89a3753 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/minion/InfernoMinionFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/minion/InfernoMinionFeatures.kt @@ -24,17 +24,18 @@ object InfernoMinionFeatures { * REGEX-TEST: Inferno Minion II * REGEX-TEST: Inferno Minion IX */ - private val infernoMinionTitlePattern by RepoPattern.pattern( + val infernoMinionTitlePattern by RepoPattern.pattern( "minion.infernominiontitle", "Inferno Minion .*", ) - private var fuelItemIds = listOf() + var fuelItemIds = setOf() + private set private var inInventory = false @HandleEvent fun onRepoReload(event: RepositoryReloadEvent) { val data = event.getConstant("InfernoMinionFuels") - fuelItemIds = data.minionFuels + fuelItemIds = data.minionFuels.toSet() } @HandleEvent diff --git a/src/main/java/at/hannibal2/skyhanni/features/minion/InfernoMinionProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/minion/InfernoMinionProfitTracker.kt new file mode 100644 index 000000000000..25cff780eb2f --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/minion/InfernoMinionProfitTracker.kt @@ -0,0 +1,184 @@ +package at.hannibal2.skyhanni.features.minion + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.api.event.HandleEvent +import at.hannibal2.skyhanni.config.commands.CommandCategory +import at.hannibal2.skyhanni.config.commands.CommandRegistrationEvent +import at.hannibal2.skyhanni.events.InventoryUpdatedEvent +import at.hannibal2.skyhanni.events.MinionCloseEvent +import at.hannibal2.skyhanni.events.MinionOpenEvent +import at.hannibal2.skyhanni.events.SackChangeEvent +import at.hannibal2.skyhanni.events.entity.ItemAddInInventoryEvent +import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule +import at.hannibal2.skyhanni.utils.InventoryDetector +import at.hannibal2.skyhanni.utils.ItemPriceUtils.getPrice +import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull +import at.hannibal2.skyhanni.utils.NeuInternalName +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat +import at.hannibal2.skyhanni.utils.RegexUtils.matches +import at.hannibal2.skyhanni.utils.RenderDisplayHelper +import at.hannibal2.skyhanni.utils.SimpleTimeMark +import at.hannibal2.skyhanni.utils.SkyBlockUtils +import at.hannibal2.skyhanni.utils.collection.RenderableCollectionUtils.addSearchString +import at.hannibal2.skyhanni.utils.renderables.Renderable +import at.hannibal2.skyhanni.utils.renderables.Searchable +import at.hannibal2.skyhanni.utils.renderables.toSearchable +import at.hannibal2.skyhanni.utils.tracker.ItemTrackerData +import at.hannibal2.skyhanni.utils.tracker.SessionUptime +import at.hannibal2.skyhanni.utils.tracker.SkyHanniItemTracker +import com.google.gson.annotations.Expose +import net.minecraft.world.item.ItemStack +import kotlin.time.Duration.Companion.seconds + +@SkyHanniModule +object InfernoMinionProfitTracker { + + private val config get() = SkyHanniMod.feature.misc.minions.infernoMinionProfitTracker + + private val infernoMinionInventory = InventoryDetector { name -> + InfernoMinionFeatures.infernoMinionTitlePattern.matches(name) + } + + private var isInfernoMinion = false + private var lastFuelItem: NeuInternalName? = null + private var lastCollectionTime = SimpleTimeMark.farPast() + private var itemsCollected = false + + private val tracker = SkyHanniItemTracker( + "Inferno Minion Profit Tracker", + { Data() }, + { it.infernoMinionProfitTracker }, + trackerConfig = { config.perTrackerConfig }, + ) { drawDisplay(it) } + + data class Data( + @Expose var totalFuelCost: Double = 0.0, + ) : ItemTrackerData(SessionUptime.Normal::class) { + + override fun getDescription(timesGained: Long): List { + val totalItems = items.values.sumOf { it.timesGained } + val percentage = if (totalItems > 0) timesGained.toDouble() / totalItems else 0.0 + val dropRate = "%.1f%%".format(percentage * 100) + return listOf( + "§7Dropped §e${timesGained.addSeparators()} §7times.", + "§7Drop chance: §c$dropRate", + ) + } + + override fun getCoinName(item: TrackedItem) = "" + + override fun getCoinDescription(item: TrackedItem) = listOf() + } + + init { + RenderDisplayHelper( + inventory = infernoMinionInventory, + outsideInventory = true, + condition = { config.enabled && SkyBlockUtils.inSkyBlock && (infernoMinionInventory.isInside() || isRecentCollection()) }, + onRender = { tracker.renderDisplay(config.position) }, + ) + } + + private fun drawDisplay(data: Data): List = buildList { + addSearchString("§e§lInferno Minion Profit Tracker") + + var profit = tracker.drawItems(data, { true }, this) + profit = addFuelCost(data, profit) + + val totalCollections = data.items.values.sumOf { it.timesGained } + add( + Renderable.hoverTips( + "§7Total collections: §e${totalCollections.addSeparators()}", + listOf("§7You've collected from Inferno Minions §e${totalCollections.addSeparators()} §7times."), + ).toSearchable(), + ) + + val duration = data.getTotalUptime() + addAll(tracker.addTotalProfit(profit, totalCollections, "collection", duration, "Collections")) + + tracker.addPriceFromButton(this) + } + + private fun MutableList.addFuelCost(data: Data, profit: Double): Double { + val fuelCost = data.totalFuelCost + if (fuelCost <= 0) return profit + add( + Renderable.hoverTips( + "§7Fuel cost: §c-${fuelCost.shortFormat()}", + listOf( + "§7Total spent on fuel items.", + "§7This is subtracted from your profit.", + ), + ).toSearchable("Fuel Cost"), + ) + return profit - fuelCost + } + + @HandleEvent + fun onMinionOpen(event: MinionOpenEvent) { + val firstOpen = !isInfernoMinion + isInfernoMinion = InfernoMinionFeatures.infernoMinionTitlePattern.matches(event.inventoryName) + if (!isInfernoMinion) return + if (firstOpen) { + lastFuelItem = getFuelFromInventory(event.inventoryItems) + } + } + + @HandleEvent + fun onInventoryUpdated(event: InventoryUpdatedEvent) { + if (!isInfernoMinion) return + if (!config.enabled) return + val newFuel = getFuelFromInventory(event.inventoryItems) + if (newFuel != null && newFuel != lastFuelItem) { + tracker.modify { it.totalFuelCost += newFuel.getPrice() } + } + lastFuelItem = newFuel + } + + @HandleEvent(onlyOnSkyblock = true) + fun onItemAddInInventory(event: ItemAddInInventoryEvent) { + if (!isInfernoMinion) return + if (!config.enabled) return + tracker.addItem(event.internalName, event.amount, command = false) + itemsCollected = true + } + + @HandleEvent(onlyOnSkyblock = true) + fun onSackChange(event: SackChangeEvent) { + if (!config.enabled) return + if (lastCollectionTime.passedSince() > 5.seconds) return + for (sackChange in event.sackChanges) { + if (sackChange.delta > 0) { + tracker.addItem(sackChange.internalName, sackChange.delta, command = false) + } + } + } + + @HandleEvent + fun onMinionClose(event: MinionCloseEvent) { + if (!isInfernoMinion) return + if (!config.enabled) return + lastCollectionTime = SimpleTimeMark.now() + lastFuelItem = null + isInfernoMinion = false + itemsCollected = false + } + + private fun getFuelFromInventory(inventoryItems: Map): NeuInternalName? { + val fuelStack = inventoryItems[19] ?: return null + val name = fuelStack.getInternalNameOrNull() ?: return null + return if (name in InfernoMinionFeatures.fuelItemIds) name else null + } + + private fun isRecentCollection() = config.showAfterCollection && lastCollectionTime.passedSince() < 10.seconds + + @HandleEvent + fun onCommandRegistration(event: CommandRegistrationEvent) { + event.registerBrigadier("shresetinfernominiontracker") { + description = "Resets the Inferno Minion Profit Tracker" + category = CommandCategory.USERS_RESET + simpleCallback { tracker.resetCommand() } + } + } +}