aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/hannibal2/skyhanni/features
diff options
context:
space:
mode:
authorhannibal2 <24389977+hannibal002@users.noreply.github.com>2023-11-22 14:26:49 +0100
committerGitHub <noreply@github.com>2023-11-22 14:26:49 +0100
commitdd2a77f37504f81e09806e1c277451957ea455ac (patch)
tree9453880145ce14e14ccc2a9adcb09f7fbcf6b332 /src/main/java/at/hannibal2/skyhanni/features
parentcebe991e0f997035e9e0be0f49fd460be20c5a20 (diff)
downloadskyhanni-dd2a77f37504f81e09806e1c277451957ea455ac.tar.gz
skyhanni-dd2a77f37504f81e09806e1c277451957ea455ac.tar.bz2
skyhanni-dd2a77f37504f81e09806e1c277451957ea455ac.zip
fishing profit tracker (#703)
Fishing Tracker #703
Diffstat (limited to 'src/main/java/at/hannibal2/skyhanni/features')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt14
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/fishing/FishingProfitTracker.kt322
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerProfitTracker.kt5
3 files changed, 337 insertions, 4 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt
index 550388893..035525c01 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingAPI.kt
@@ -1,10 +1,14 @@
package at.hannibal2.skyhanni.features.fishing
import at.hannibal2.skyhanni.events.FishingBobberCastEvent
+import at.hannibal2.skyhanni.features.fishing.trophy.TrophyFishManager
+import at.hannibal2.skyhanni.features.fishing.trophy.TrophyFishManager.getFilletValue
+import at.hannibal2.skyhanni.features.fishing.trophy.TrophyRarity
import at.hannibal2.skyhanni.utils.InventoryUtils
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
import at.hannibal2.skyhanni.utils.ItemUtils.name
import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.NEUInternalName
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import net.minecraft.client.Minecraft
@@ -42,4 +46,14 @@ object FishingAPI {
fun getAllowedBlocks() = if (isLavaRod()) lavaBlocks else waterBlocks
+ fun getFilletPerTrophy(internalName: NEUInternalName): Int {
+ val internal = internalName.asString()
+ val trophyFishName = internal.substringBeforeLast("_")
+ .replace("_", "").lowercase()
+ val trophyRarityName = internal.substringAfterLast("_")
+ val info = TrophyFishManager.getInfo(trophyFishName)
+ val rarity = TrophyRarity.getByName(trophyRarityName) ?: TrophyRarity.BRONZE
+ return info?.getFilletValue(rarity) ?: 0
+ }
+
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingProfitTracker.kt
new file mode 100644
index 000000000..50a9e6e1f
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingProfitTracker.kt
@@ -0,0 +1,322 @@
+package at.hannibal2.skyhanni.features.fishing
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.EntityMoveEvent
+import at.hannibal2.skyhanni.events.FishingBobberCastEvent
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
+import at.hannibal2.skyhanni.events.RepositoryReloadEvent
+import at.hannibal2.skyhanni.events.SackChangeEvent
+import at.hannibal2.skyhanni.events.entity.ItemAddInInventoryEvent
+import at.hannibal2.skyhanni.features.bazaar.BazaarApi.Companion.getBazaarData
+import at.hannibal2.skyhanni.test.PriceSource
+import at.hannibal2.skyhanni.utils.ItemUtils.getItemName
+import at.hannibal2.skyhanni.utils.KeyboardManager
+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.NEUInternalName.Companion.asInternalName
+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.NumberUtil.formatNumber
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import at.hannibal2.skyhanni.utils.jsonobjects.FishingProfitItemsJson
+import at.hannibal2.skyhanni.utils.renderables.Renderable
+import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker
+import at.hannibal2.skyhanni.utils.tracker.TrackerData
+import com.google.gson.annotations.Expose
+import net.minecraft.client.Minecraft
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.time.Duration.Companion.seconds
+
+object FishingProfitTracker {
+ private val config get() = SkyHanniMod.feature.fishing.fishingProfitTracker
+
+ private val coinsChatPattern = ".* CATCH! §r§bYou found §r§6(?<coins>.*) Coins§r§b\\.".toPattern()
+ private var lastClickDelay = 0L
+
+ private val tracker =
+ SkyHanniTracker("Fishing Profit Tracker", { Data() }, { it.fishing.fishingProfitTracker }) { drawDisplay(it) }
+
+ class Data : TrackerData() {
+ override fun reset() {
+ items.clear()
+ totalCatchAmount = 0
+ }
+
+ @Expose
+ var items = mutableMapOf<NEUInternalName, FishingItem>()
+
+ @Expose
+ var totalCatchAmount = 0L
+
+ class FishingItem {
+ @Expose
+ var internalName: NEUInternalName? = null
+
+ @Expose
+ var timesCaught: Long = 0
+
+ @Expose
+ var totalAmount: Long = 0
+
+ @Expose
+ var hidden = false
+
+ override fun toString() = "FishingItem{" +
+ "internalName='" + internalName + '\'' +
+ ", timesDropped=" + timesCaught +
+ ", totalAmount=" + totalAmount +
+ ", hidden=" + hidden +
+ '}'
+
+ var lastTimeUpdated = SimpleTimeMark.farPast()
+ }
+ }
+
+ private val SKYBLOCK_COIN by lazy { "SKYBLOCK_COIN".asInternalName() }
+ private val MAGMA_FISH by lazy { "MAGMA_FISH".asInternalName() }
+
+ private fun drawDisplay(data: Data): List<List<Any>> = buildList {
+ addAsSingletonList("§e§lFishing Profit Tracker")
+
+ var profit = 0.0
+ val map = mutableMapOf<Renderable, Long>()
+ for ((internalName, itemProfit) in data.items) {
+ val amount = itemProfit.totalAmount
+
+ var pricePer = if (internalName == SKYBLOCK_COIN) 1.0 else getPrice(internalName)
+ if (pricePer == 0.0) {
+ pricePer = getPrice(MAGMA_FISH) * FishingAPI.getFilletPerTrophy(internalName)
+ }
+
+ val price = (pricePer * amount).toLong()
+ val displayAmount = if (internalName == SKYBLOCK_COIN) {
+ itemProfit.timesCaught
+ } else amount
+
+ val cleanName = if (internalName == SKYBLOCK_COIN) "§6Coins" else internalName.getItemName()
+ var name = cleanName
+ val priceFormat = NumberUtil.format(price)
+ val hidden = itemProfit.hidden
+
+ val newDrop = itemProfit.lastTimeUpdated.passedSince() < 10.seconds && config.showRecentDropss
+ val numberColor = if (newDrop) "§a§l" else "§7"
+
+ if (hidden) {
+ name = "§8§m" + name.removeColor(keepFormatting = true).replace("§r", "")
+ }
+
+ val text = " $numberColor${displayAmount.addSeparators()}x $name§7: §6$priceFormat"
+
+ val timesCaught = itemProfit.timesCaught
+ val percentage = timesCaught.toDouble() / data.totalCatchAmount
+ val catchRate = LorenzUtils.formatPercentage(percentage.coerceAtMost(1.0))
+
+ val renderable = if (tracker.isInventoryOpen()) Renderable.clickAndHover(
+ text,
+ buildLore(timesCaught, catchRate, hidden, newDrop)
+ ) {
+ if (System.currentTimeMillis() > lastClickDelay + 150) {
+
+ if (KeyboardManager.isControlKeyDown()) {
+ data.items.remove(internalName)
+ LorenzUtils.chat("§e[SkyHanni] Removed $cleanName §efrom Fishing Frofit Tracker.")
+ lastClickDelay = System.currentTimeMillis() + 500
+ } else {
+ itemProfit.hidden = !hidden
+ lastClickDelay = System.currentTimeMillis()
+ }
+ tracker.update()
+ }
+ } else Renderable.string(text)
+ if (tracker.isInventoryOpen() || !hidden) {
+ map[renderable] = price
+ }
+ profit += price
+ }
+
+ for (text in map.sortedDesc().keys) {
+ addAsSingletonList(text)
+ }
+
+ val fishedCount = data.totalCatchAmount
+ addAsSingletonList(
+ Renderable.hoverTips(
+ "§7Times fished: §e${fishedCount.addSeparators()}",
+ listOf("§7You catched §e${fishedCount.addSeparators()} §7times something.")
+ )
+ )
+
+ val profitFormat = NumberUtil.format(profit)
+ val profitPrefix = if (profit < 0) "§c" else "§6"
+
+ val profitPerCatch = profit / data.totalCatchAmount
+ val profitPerCatchFormat = NumberUtil.format(profitPerCatch)
+
+ val text = "§eTotal Profit: $profitPrefix$profitFormat"
+ addAsSingletonList(Renderable.hoverTips(text, listOf("§7Profit per catch: $profitPrefix$profitPerCatchFormat")))
+
+ if (tracker.isInventoryOpen()) {
+ addSelector<PriceSource>(
+ "",
+ getName = { type -> type.displayName },
+ isCurrent = { it.ordinal == config.priceFrom },
+ onChange = {
+ config.priceFrom = it.ordinal
+ tracker.update()
+ }
+ )
+ }
+ }
+
+ private fun buildLore(
+ timesCaught: Long,
+ catchRate: String,
+ hidden: Boolean,
+ newDrop: Boolean
+ ) = buildList {
+ add("§7Caught §e${timesCaught.addSeparators()} §7times.")
+ add("§7Your catch rate: §c$catchRate")
+ add("")
+ if (newDrop) {
+ add("§aYou caught this item recently.")
+ add("")
+ }
+ add("§eClick to " + (if (hidden) "show" else "hide") + "!")
+ add("§eControl + Click to remove this item!")
+ }
+
+ @SubscribeEvent
+ fun onSackChange(event: SackChangeEvent) {
+ if (!isEnabled()) return
+
+ for (sackChange in event.sackChanges) {
+ val change = sackChange.delta
+ if (change > 0) {
+ val internalName = sackChange.internalName
+ maybeAddItem(internalName, change)
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onItemAdd(event: ItemAddInInventoryEvent) {
+ if (!isEnabled()) return
+
+ maybeAddItem(event.internalName, event.amount)
+ }
+
+ @SubscribeEvent
+ fun onChat(event: LorenzChatEvent) {
+ coinsChatPattern.matchMatcher(event.message) {
+ val coins = group("coins").formatNumber()
+ addItem(SKYBLOCK_COIN, coins.toInt())
+ }
+ }
+
+ private fun addItem(internalName: NEUInternalName, stackSize: Int) {
+ tracker.modify {
+ it.totalCatchAmount++
+
+ val fishingItem = it.items.getOrPut(internalName) { Data.FishingItem() }
+
+ fishingItem.timesCaught++
+ fishingItem.totalAmount += stackSize
+ fishingItem.lastTimeUpdated = SimpleTimeMark.now()
+ }
+ }
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: GuiRenderEvent) {
+ if (!isEnabled()) return
+ if (!FishingAPI.hasFishingRodInHand()) return
+ if (isMoving && config.hideMoving) return
+
+ tracker.renderDisplay(config.position)
+ }
+
+ private fun maybeAddItem(internalName: NEUInternalName, amount: Int) {
+ if (!isAllowedItem(internalName)) {
+ LorenzUtils.debug("Ignored non-fishing item pickup: $internalName'")
+ return
+ }
+
+ addItem(internalName, amount)
+ }
+
+ private var itemCategories = mutableMapOf<String, List<NEUInternalName>>()
+
+ @SubscribeEvent
+ fun onRepoReload(event: RepositoryReloadEvent) {
+ itemCategories = event.getConstant<FishingProfitItemsJson>("FishingProfitItems").categories
+ }
+
+ private fun isAllowedItem(internalName: NEUInternalName): Boolean {
+ for ((name, items) in itemCategories) {
+ if (internalName in items) {
+ return true
+ }
+ }
+
+ return false
+ }
+
+ 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
+ }
+
+ /// <editor-fold desc="isMoving">
+
+ private val lastSteps = mutableListOf<Double>()
+ private var isMoving = true
+
+ @SubscribeEvent
+ fun onEntityMove(event: EntityMoveEvent) {
+ if (!isEnabled() || !config.hideMoving) return
+ if (event.entity != Minecraft.getMinecraft().thePlayer) return
+
+ val distance = event.newLocation.distanceIgnoreY(event.oldLocation)
+ if (distance < 0.1) {
+ lastSteps.clear()
+ return
+ }
+ lastSteps.add(distance)
+ if (lastSteps.size > 20) {
+ lastSteps.removeAt(0)
+ }
+ val total = lastSteps.sum()
+ if (total > 3) {
+ isMoving = true
+ }
+ }
+
+ @SubscribeEvent
+ fun onBobberThrow(event: FishingBobberCastEvent) {
+ if (!isEnabled() || !config.hideMoving) return
+ isMoving = false
+ tracker.firstUpdate()
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: LorenzWorldChangeEvent) {
+ isMoving = true
+ }
+ /// </editor-fold>
+
+ fun resetCommand(args: Array<String>) {
+ tracker.resetCommand(args, "shresetfishingtracker")
+ }
+
+ fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerProfitTracker.kt
index c05839ba6..6ea38924b 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerProfitTracker.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/slayer/SlayerProfitTracker.kt
@@ -251,10 +251,7 @@ object SlayerProfitTracker {
val priceFormat = NumberUtil.format(price)
val hidden = itemProfit.hidden
if (hidden) {
- while (name.startsWith("§f")) {
- name = name.substring(2)
- }
- name = StringUtils.addFormat(name, "§m")
+ name = "§8§m" + name.removeColor(keepFormatting = true).replace("§r", "")
}
val text = " §7${amount.addSeparators()}x $name§7: §6$priceFormat"