From 2d9844426886cd488703d05ee58558e05d4c3987 Mon Sep 17 00:00:00 2001 From: hannibal2 <24389977+hannibal002@users.noreply.github.com> Date: Wed, 8 May 2024 21:13:21 +0200 Subject: [PATCH] Feature: Profit per corpse loot (#1734) Co-authored-by: CalMWolfs <94038482+CalMWolfs@users.noreply.github.com> Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com> --- .../java/at/hannibal2/skyhanni/SkyHanniMod.kt | 4 + .../mining/FossilExcavatorConfig.java | 4 +- .../features/mining/MineshaftConfig.java | 18 ++++ .../config/features/mining/MiningConfig.java | 5 ++ .../events/mining/CorpseLootedEvent.kt | 6 ++ .../fossilexcavator/FossilExcavatorAPI.kt | 2 +- .../fossilexcavator/ProfitPerExcavation.kt | 2 +- .../features/mining/mineshaft/CorpeType.kt | 13 +++ .../features/mining/mineshaft/CorpseAPI.kt | 85 +++++++++++++++++++ .../mineshaft/MineshaftCorpseProfitPer.kt | 54 ++++++++++++ .../skyhanni/test/command/TestChatCommand.kt | 33 ++++--- 11 files changed, 211 insertions(+), 15 deletions(-) create mode 100644 src/main/java/at/hannibal2/skyhanni/config/features/mining/MineshaftConfig.java create mode 100644 src/main/java/at/hannibal2/skyhanni/events/mining/CorpseLootedEvent.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/mining/mineshaft/CorpeType.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/mining/mineshaft/CorpseAPI.kt create mode 100644 src/main/java/at/hannibal2/skyhanni/features/mining/mineshaft/MineshaftCorpseProfitPer.kt diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index 068a441c59a7..f04a7de4602c 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -309,6 +309,8 @@ import at.hannibal2.skyhanni.features.mining.fossilexcavator.FossilExcavatorAPI import at.hannibal2.skyhanni.features.mining.fossilexcavator.GlacitePowderFeatures import at.hannibal2.skyhanni.features.mining.fossilexcavator.ProfitPerExcavation import at.hannibal2.skyhanni.features.mining.fossilexcavator.solver.FossilSolverDisplay +import at.hannibal2.skyhanni.features.mining.mineshaft.CorpseAPI +import at.hannibal2.skyhanni.features.mining.mineshaft.MineshaftCorpseProfitPer import at.hannibal2.skyhanni.features.mining.powdertracker.PowderTracker import at.hannibal2.skyhanni.features.minion.InfernoMinionFeatures import at.hannibal2.skyhanni.features.minion.MinionCollectLogic @@ -746,6 +748,8 @@ class SkyHanniMod { loadModule(ExcavatorProfitTracker()) loadModule(ProfitPerExcavation()) loadModule(GlacitePowderFeatures()) + loadModule(MineshaftCorpseProfitPer()) + loadModule(CorpseAPI()) loadModule(GardenOptimalSpeed()) loadModule(GardenLevelDisplay()) loadModule(FarmingWeightDisplay()) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java index c8eddb16573d..9252c45f9983 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/FossilExcavatorConfig.java @@ -20,8 +20,8 @@ public class FossilExcavatorConfig { @Expose @ConfigOption( - name = "Profit Per", - desc = "Show profit/loss in chat after each excavation. Also include breakdown information on hover." + name = "Profit Per Excavation", + desc = "Show profit/loss in chat after each excavation. Also includes breakdown information on hover." ) @ConfigEditorBoolean @FeatureToggle diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MineshaftConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MineshaftConfig.java new file mode 100644 index 000000000000..c0a6e3e50859 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MineshaftConfig.java @@ -0,0 +1,18 @@ +package at.hannibal2.skyhanni.config.features.mining; + +import at.hannibal2.skyhanni.config.FeatureToggle; +import com.google.gson.annotations.Expose; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; +import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; + +public class MineshaftConfig { + + @Expose + @ConfigOption( + name = "Profit Per Corpse", + desc = "Show profit/loss in chat after each looted corpse in the Mineshaft. Also includes breakdown information on hover." + ) + @ConfigEditorBoolean + @FeatureToggle + public boolean profitPerCorpseLoot = true; +} diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java index 6d8bd06ab3bc..9c4af3882f17 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java @@ -55,6 +55,11 @@ public class MiningConfig { @Accordion public CommissionsBlocksColorConfig commissionsBlocksColor = new CommissionsBlocksColorConfig(); + @Expose + @ConfigOption(name = "Mineshaft", desc = "") + @Accordion + public MineshaftConfig mineshaft = new MineshaftConfig(); + @Expose @ConfigOption(name = "Highlight Commission Mobs", desc = "Highlight Mobs that are part of active commissions.") @ConfigEditorBoolean diff --git a/src/main/java/at/hannibal2/skyhanni/events/mining/CorpseLootedEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/mining/CorpseLootedEvent.kt new file mode 100644 index 000000000000..e73357296106 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/mining/CorpseLootedEvent.kt @@ -0,0 +1,6 @@ +package at.hannibal2.skyhanni.events.mining + +import at.hannibal2.skyhanni.events.LorenzEvent +import at.hannibal2.skyhanni.features.mining.mineshaft.CorpeType + +class CorpseLootedEvent(val corpseType: CorpeType, val loot: List>) : LorenzEvent() diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilExcavatorAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilExcavatorAPI.kt index def47812c180..56b3242b938c 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilExcavatorAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/FossilExcavatorAPI.kt @@ -113,7 +113,7 @@ object FossilExcavatorAPI { } ?: return // Workaround: If it is a enchanted book, we assume it is a paleontologist I book if (pair.first.let { it == "§fEnchanted" || it == "§fEnchanted Book" }) { - pair = "Paleontologist I" to pair.second + pair = "§9Paleontologist I" to pair.second } loot.add(pair) } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/ProfitPerExcavation.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/ProfitPerExcavation.kt index 52b2f2447cc8..74fb3be55baa 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/ProfitPerExcavation.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/fossilexcavator/ProfitPerExcavation.kt @@ -27,7 +27,7 @@ class ProfitPerExcavation { val pricePer = it.getPrice() if (pricePer == -1.0) continue val profit = amount * pricePer - val text = "Found $name §8${amount.addSeparators()}x §7(§6${NumberUtil.format(profit)}§7)" + val text = "§eFound $name §8${amount.addSeparators()}x §7(§6${NumberUtil.format(profit)}§7)" map[text] = profit totalProfit += profit } diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/mineshaft/CorpeType.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/mineshaft/CorpeType.kt new file mode 100644 index 000000000000..e2d385de5700 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/mineshaft/CorpeType.kt @@ -0,0 +1,13 @@ +package at.hannibal2.skyhanni.features.mining.mineshaft + +import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName + +enum class CorpeType(val displayName: String, private val keyName: String? = null) { + LAPIS("§9Lapis"), + TUNGSTEN("§7Tungsten", "TUNGSTEN_KEY"), + UMBER("§6Umber", "UMBER_KEY"), + VANGUARD("§fVanguard", "SKELETON_KEY"), + ; + + val key by lazy { keyName?.asInternalName() } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/mineshaft/CorpseAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/mineshaft/CorpseAPI.kt new file mode 100644 index 000000000000..db16e4d58214 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/mineshaft/CorpseAPI.kt @@ -0,0 +1,85 @@ +package at.hannibal2.skyhanni.features.mining.mineshaft + +import at.hannibal2.skyhanni.data.IslandType +import at.hannibal2.skyhanni.events.LorenzChatEvent +import at.hannibal2.skyhanni.events.mining.CorpseLootedEvent +import at.hannibal2.skyhanni.utils.ItemUtils +import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland +import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher +import at.hannibal2.skyhanni.utils.StringUtils.matches +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class CorpseAPI { + + private val patternGroup = RepoPattern.group("mining.mineshaft") + private val chatPatternGroup = patternGroup.group("chat") + + /** + * REGEX-TEST: §r§b§l§r§9§lLAPIS §r§b§lCORPSE LOOT! + * REGEX-TEST: §r§b§l§r§7§lTUNGSTEN §r§b§lCORPSE LOOT! + * REGEX-TEST: §r§b§l§r§6§lUMBER §r§b§lCORPSE LOOT! + * REGEX-TEST: §r§b§l§r§f§lVANGUARD §r§b§lCORPSE LOOT! + */ + private val startPattern by chatPatternGroup.pattern( + "start", + " {2}§r§b§l§r§(?.)§l(?.*) §r§b§lCORPSE LOOT! ?" + ) + + /** + * REGEX-TEST: §a§l▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ + */ + private val endPattern by chatPatternGroup.pattern("end", "§a§l▬{64}") + + /** + * REGEX-TEST: §r§9☠ Fine Onyx Gemstone §r§8x2 + */ + private val itemPattern by chatPatternGroup.pattern("item", " {4}§r(?.+)") + + private var inLoot = false + private val loot = mutableListOf>() + + private var corpeType: CorpeType? = null + + @SubscribeEvent + fun onChat(event: LorenzChatEvent) { + if (!IslandType.MINESHAFT.isInIsland()) return + + val message = event.message + + startPattern.matchMatcher(message) { + inLoot = true + val name = group("name") + corpeType = CorpeType.valueOf(name) + return + } + + if (!inLoot) return + + if (endPattern.matches(message)) { + corpeType?.let { + CorpseLootedEvent(it, loot.toList()).postAndCatch() + } + corpeType = null + loot.clear() + inLoot = false + return + } + var pair = itemPattern.matchMatcher(message) { + /** + * TODO fix the bug that readItemAmount produces two different outputs: + * §r§fEnchanted Book -> §fEnchanted + * §fEnchanted Book §r§8x -> §fEnchanted Book + * + * also maybe this is no bug, as enchanted book is no real item? + */ + ItemUtils.readItemAmount(group("item")) + } ?: return + // Workaround: If it is a enchanted book, we assume it is a paleontologist I book + if (pair.first.let { it == "§fEnchanted" || it == "§fEnchanted Book" }) { +// pair = "Paleontologist I" to pair.second + pair = "§9Ice Cold I" to pair.second + } + loot.add(pair) + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/features/mining/mineshaft/MineshaftCorpseProfitPer.kt b/src/main/java/at/hannibal2/skyhanni/features/mining/mineshaft/MineshaftCorpseProfitPer.kt new file mode 100644 index 000000000000..d8dbe9e28fa4 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/mining/mineshaft/MineshaftCorpseProfitPer.kt @@ -0,0 +1,54 @@ +package at.hannibal2.skyhanni.features.mining.mineshaft + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.mining.CorpseLootedEvent +import at.hannibal2.skyhanni.utils.ChatUtils +import at.hannibal2.skyhanni.utils.CollectionUtils.sortedDesc +import at.hannibal2.skyhanni.utils.ItemUtils.itemName +import at.hannibal2.skyhanni.utils.NEUInternalName +import at.hannibal2.skyhanni.utils.NEUItems.getPrice +import at.hannibal2.skyhanni.utils.NumberUtil +import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class MineshaftCorpseProfitPer { + private val config get() = SkyHanniMod.feature.mining.mineshaft + + @SubscribeEvent + fun onFossilExcavation(event: CorpseLootedEvent) { + if (!config.profitPerCorpseLoot) return + val loot = event.loot + + var totalProfit = 0.0 + val map = mutableMapOf() + for ((name, amount) in loot) { + if (name == "§bGlacite Powder") continue + NEUInternalName.fromItemNameOrNull(name)?.let { + val pricePer = it.getPrice() + if (pricePer == -1.0) continue + val profit = amount * pricePer + val text = "§eFound $name §8${amount.addSeparators()}x §7(§6${NumberUtil.format(profit)}§7)" + map[text] = profit + totalProfit += profit + } + } + + val corpseType = event.corpseType + val name = corpseType.displayName + + corpseType.key?.let { + val keyName = it.itemName + val price = it.getPrice() + + map["$keyName: §c-${NumberUtil.format(price)}"] = -price + totalProfit -= price + } + + val hover = map.sortedDesc().keys.toMutableList() + val profitPrefix = if (totalProfit < 0) "§c" else "§6" + val totalMessage = "Profit for $name Corpse§e: $profitPrefix${NumberUtil.format(totalProfit)}" + hover.add("") + hover.add("§e$totalMessage") + ChatUtils.hoverableChat(totalMessage, hover) + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/test/command/TestChatCommand.kt b/src/main/java/at/hannibal2/skyhanni/test/command/TestChatCommand.kt index 2007e5b99ee6..6d67a9ad0394 100644 --- a/src/main/java/at/hannibal2/skyhanni/test/command/TestChatCommand.kt +++ b/src/main/java/at/hannibal2/skyhanni/test/command/TestChatCommand.kt @@ -21,24 +21,35 @@ object TestChatCommand { val isComplex = mutArgs.remove("-complex") val isClipboard = mutArgs.remove("-clipboard") val isHidden = mutArgs.remove("-s") + val multiLines = mutArgs.remove("-lines") val text = if (isClipboard) { OSUtils.readFromClipboard() ?: return@launchCoroutine ChatUtils.userError("Clipboard does not contain a string!") } else mutArgs.joinToString(" ") - val component = - if (isComplex) - try { - IChatComponent.Serializer.jsonToComponent(text) - } catch (ex: Exception) { - ChatUtils.userError("Please provide a valid JSON chat component (either in the command or via -clipboard)") - return@launchCoroutine - } - else ChatComponentText(text.replace("&", "§")) - if (!isHidden) ChatUtils.chat("Testing message: §7${component.formattedText}", prefixColor = "§a") - test(component) + if (multiLines) { + for (line in text.split("\n")) { + extracted(isComplex, line, isHidden) + } + } else { + extracted(isComplex, text, isHidden) + } } } + private fun extracted(isComplex: Boolean, text: String, isHidden: Boolean) { + val component = + if (isComplex) + try { + IChatComponent.Serializer.jsonToComponent(text) + } catch (ex: Exception) { + ChatUtils.userError("Please provide a valid JSON chat component (either in the command or via -clipboard)") + return + } + else ChatComponentText(text.replace("&", "§")) + if (!isHidden) ChatUtils.chat("Testing message: §7${component.formattedText}", prefixColor = "§a") + test(component) + } + private fun test(componentText: IChatComponent) { val message = componentText.formattedText.stripHypixelMessage() val event = LorenzChatEvent(message, componentText)