diff options
author | Obsidian <108832807+Obsidianninja11@users.noreply.github.com> | 2024-04-10 00:51:20 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-10 10:51:20 +0200 |
commit | f541212f286940b7260df85ab419a02228daf1ea (patch) | |
tree | aba4f1dac12cd0b1993800dc05cecda4d42277a5 | |
parent | 8e4a4a156b659fcbb26dfec8ff619777771d0c59 (diff) | |
download | skyhanni-f541212f286940b7260df85ab419a02228daf1ea.tar.gz skyhanni-f541212f286940b7260df85ab419a02228daf1ea.tar.bz2 skyhanni-f541212f286940b7260df85ab419a02228daf1ea.zip |
Improvement: Better Visitor Reward Warning (#1417)
* Price per copper based visitor blocking + improvements
* Fixed merge conflicts + minor bug
* Fixed merge conflicts + Fixed Accept offer title not rendering in tooltip
* Messed something up during merge conflicts. fixed now
* fixing merge conflicts
* Code cleanup + more features + fixes
* Code cleanup + improvements
* optimized imports
* Cache tooltip
* Use GuiContainerEvent.SlotClickEvent
* Lots of code cleanup + merge conflicts + fixes
* fix
* fix merge conflicts
---------
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
7 files changed, 227 insertions, 20 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt index a2a99eb20..02db25131 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt @@ -226,6 +226,7 @@ import at.hannibal2.skyhanni.features.garden.visitor.HighlightVisitorsOutsideOfG import at.hannibal2.skyhanni.features.garden.visitor.NPCVisitorFix import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI import at.hannibal2.skyhanni.features.garden.visitor.VisitorListener +import at.hannibal2.skyhanni.features.garden.visitor.VisitorRewardWarning import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboard import at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardPattern import at.hannibal2.skyhanni.features.inventory.AuctionsHighlighter @@ -469,6 +470,7 @@ class SkyHanniMod { loadModule(GardenCropMilestonesCommunityFix) loadModule(GardenCropUpgrades()) loadModule(VisitorListener()) + loadModule(VisitorRewardWarning()) loadModule(OwnInventoryData()) loadModule(ToolTipData()) loadModule(HighlightVisitorsOutsideOfGarden()) diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/garden/visitor/RewardWarningConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/garden/visitor/RewardWarningConfig.java index 230085347..2ea448148 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/features/garden/visitor/RewardWarningConfig.java +++ b/src/main/java/at/hannibal2/skyhanni/config/features/garden/visitor/RewardWarningConfig.java @@ -6,6 +6,7 @@ import com.google.gson.annotations.Expose; import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean; import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList; import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorKeybind; +import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider; import io.github.notenoughupdates.moulconfig.annotations.ConfigOption; import org.lwjgl.input.Keyboard; @@ -28,15 +29,15 @@ public class RewardWarningConfig { public boolean showOverName = true; @Expose - @ConfigOption(name = "Prevent Refusing", desc = "Prevent the refusal of a visitor with reward.") + @ConfigOption(name = "Block Refusing Reward", desc = "Prevent the refusal of a visitor with reward.") @ConfigEditorBoolean @FeatureToggle public boolean preventRefusing = true; @Expose - @ConfigOption(name = "Bypass Key", desc = "Hold that key to bypass the Prevent Refusing feature.") - @ConfigEditorKeybind(defaultKey = Keyboard.KEY_NONE) - public int bypassKey = Keyboard.KEY_NONE; + @ConfigOption(name = "Bypass Key", desc = "Hold this key to bypass the Prevent Refusing feature.") + @ConfigEditorKeybind(defaultKey = Keyboard.KEY_LCONTROL) + public int bypassKey = Keyboard.KEY_LCONTROL; @Expose @@ -55,4 +56,47 @@ public class RewardWarningConfig { VisitorReward.REPLENISH )); + @Expose + @ConfigOption( + name = "Coins Per Copper", + desc = "The price to use for the below options.\n" + + "Requires one of the below options to be on." + ) + @ConfigEditorSlider(minValue = 1, maxValue = 50_000, minStep = 250) + public int coinsPerCopperPrice = 6_000; + + @Expose + @ConfigOption(name = "Block Refusing Copper", desc = "Prevent refusing a visitor with a coins per copper lower than the set value.") + @ConfigEditorBoolean + @FeatureToggle + public boolean preventRefusingCopper = false; + + @Expose + @ConfigOption(name = "Block Accepting Copper", desc = "Prevent accepting a visitor with a coins per copper higher than the set value.") + @ConfigEditorBoolean + @FeatureToggle + public boolean preventAcceptingCopper = false; + + @Expose + @ConfigOption(name = "Block Refusing New Visitors", desc = "Prevents refusing a visitor you've never completed an offer with.") + @ConfigEditorBoolean + @FeatureToggle + public boolean preventRefusingNew = true; + + @Expose + @ConfigOption( + name = "Opacity", + desc = "How strong should the offer buttons be grayed out when blocked?" + ) + @ConfigEditorSlider( + minValue = 0, + maxValue = 255, + minStep = 5 + ) + public int opacity = 180; + + @Expose + @ConfigOption(name = "Outline", desc = "Adds a red/green line around the best offer button.") + @ConfigEditorBoolean + public boolean optionOutline = true; } diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt index 47785eba9..479569961 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt @@ -21,6 +21,7 @@ import at.hannibal2.skyhanni.events.garden.visitor.VisitorRenderEvent import at.hannibal2.skyhanni.features.garden.CropType.Companion.getByNameOrNull import at.hannibal2.skyhanni.features.garden.GardenAPI import at.hannibal2.skyhanni.features.garden.farming.GardenCropSpeed.getSpeed +import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI.blockReason import at.hannibal2.skyhanni.features.inventory.bazaar.BazaarApi import at.hannibal2.skyhanni.mixins.hooks.RenderLivingEntityHelper import at.hannibal2.skyhanni.test.command.ErrorManager @@ -128,7 +129,9 @@ object GardenVisitorFeatures { visitor.shoppingList[internalName] = amount } - readToolTip(visitor, offerItem) + visitor.lastLore = listOf() + visitor.blockedLore = listOf() + visitor.blockReason = visitor.blockReason() val alreadyReady = offerItem.getLore().any { it == "§eClick to give!" } if (alreadyReady) { @@ -297,16 +300,14 @@ object GardenVisitorFeatures { fun onTooltip(visitor: VisitorAPI.Visitor, itemStack: ItemStack, toolTip: MutableList<String>) { if (itemStack.name != "§aAccept Offer") return - toolTip.clear() - if (visitor.lastLore.isEmpty()) { - readToolTip(visitor, itemStack) + readToolTip(visitor, itemStack, toolTip) } - + toolTip.clear() toolTip.addAll(visitor.lastLore) } - private fun readToolTip(visitor: VisitorAPI.Visitor, itemStack: ItemStack?) { + private fun readToolTip(visitor: VisitorAPI.Visitor, itemStack: ItemStack?, toolTip: MutableList<String>) { val stack = itemStack ?: error("Accept offer item not found for visitor ${visitor.visitorName}") var totalPrice = 0.0 var farmingTimeRequired = 0.seconds @@ -351,7 +352,7 @@ object GardenVisitorFeatures { } readingShoppingList = true - val finalList = stack.getLore().toMutableList() + val finalList = toolTip.map { it.removePrefix("§5§o")}.toMutableList() var offset = 0 for ((i, formattedLine) in finalList.toMutableList().withIndex()) { val index = i + offset @@ -366,6 +367,7 @@ object GardenVisitorFeatures { copperPattern.matchMatcher(formattedLine) { val copper = group("amount").formatInt() val pricePerCopper = NumberUtil.format((totalPrice / copper).toInt()) + visitor.pricePerCopper = (totalPrice / copper).toInt() val timePerCopper = (farmingTimeRequired / copper).format() var copperLine = formattedLine if (config.inventory.copperPrice) copperLine += " §7(§6$pricePerCopper §7per)" @@ -378,7 +380,6 @@ object GardenVisitorFeatures { if (formattedLine.contains("Rewards")) { readingShoppingList = false } - val (itemName, amount) = ItemUtils.readItemAmount(formattedLine) ?: continue val internalName = NEUInternalName.fromItemNameOrNull(itemName)?.replace("◆_", "") ?: continue diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorAPI.kt index dab1d40e7..295e3c08e 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorAPI.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorAPI.kt @@ -22,9 +22,14 @@ object VisitorAPI { private var visitors = mapOf<String, Visitor>() var inInventory = false + var lastClickedNpc = 0 val config get() = GardenAPI.config.visitors private val logger = LorenzLogger("garden/visitors/api") + const val INFO_SLOT = 13 + const val ACCEPT_SLOT = 29 + const val REFUSE_SLOT = 33 + val patternGroup = RepoPattern.group("garden.visitor.api") val visitorCountPattern by patternGroup.pattern( "visitor.count", @@ -124,10 +129,13 @@ object VisitorAPI { val shoppingList: MutableMap<NEUInternalName, Int> = mutableMapOf(), var offer: VisitorOffer? = null, ) { - + var offersAccepted: Int? = null + var pricePerCopper: Int? = null var lore: List<String> = emptyList() var allRewards = listOf<NEUInternalName>() var lastLore = listOf<String>() + var blockedLore = listOf<String>() + var blockReason: VisitorBlockReason? = null fun getEntity() = EntityUtils.getEntityByID(entityId) fun getNameTagEntity() = EntityUtils.getEntityByID(nameTagEntityId) @@ -184,4 +192,30 @@ object VisitorAPI { } return visitorsInTab } + + fun Visitor.blockReason(): VisitorBlockReason? { + + val visitorHasReward = config.rewardWarning.preventRefusing && this.hasReward() != null + if (visitorHasReward) { + return VisitorBlockReason.RARE_REWARD + } + else if (config.rewardWarning.preventRefusingNew && this.offersAccepted == 0) { + return VisitorBlockReason.NEVER_ACCEPTED + } + val pricePerCopper = this.pricePerCopper ?: return VisitorBlockReason.EXPENSIVE_COPPER + return if (config.rewardWarning.preventRefusingCopper && pricePerCopper <= config.rewardWarning.coinsPerCopperPrice) { + VisitorBlockReason.CHEAP_COPPER + } + else if (config.rewardWarning.preventAcceptingCopper && pricePerCopper > config.rewardWarning.coinsPerCopperPrice) { + VisitorBlockReason.EXPENSIVE_COPPER + } + else null + } + + enum class VisitorBlockReason(val description: String, val blockRefusing: Boolean) { + NEVER_ACCEPTED("§cNever accepted", true), + RARE_REWARD("§aRare visitor reward found", true), + CHEAP_COPPER("§aCheap copper", true), + EXPENSIVE_COPPER("§cExpensive copper", false) + } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorListener.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorListener.kt index a1a3c859d..22d7f4784 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorListener.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorListener.kt @@ -13,7 +13,9 @@ import at.hannibal2.skyhanni.events.TabListUpdateEvent import at.hannibal2.skyhanni.events.garden.visitor.VisitorOpenEvent import at.hannibal2.skyhanni.events.garden.visitor.VisitorRenderEvent import at.hannibal2.skyhanni.features.garden.GardenAPI -import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI.VisitorStatus +import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI.ACCEPT_SLOT +import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI.INFO_SLOT +import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI.lastClickedNpc import at.hannibal2.skyhanni.mixins.transformers.gui.AccessorGuiContainer import at.hannibal2.skyhanni.utils.ChatUtils import at.hannibal2.skyhanni.utils.DelayedRun @@ -24,8 +26,10 @@ import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer import at.hannibal2.skyhanni.utils.LorenzLogger import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.RenderUtils.exactLocation +import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher import at.hannibal2.skyhanni.utils.StringUtils.matches import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern import net.minecraft.client.Minecraft import net.minecraft.client.gui.inventory.GuiContainer import net.minecraft.entity.item.EntityArmorStand @@ -37,12 +41,16 @@ import org.lwjgl.input.Keyboard import kotlin.time.Duration.Companion.seconds class VisitorListener { + private val offersAcceptedPattern by RepoPattern.pattern( + "garden.visitor.offersaccepted", + "§7Offers Accepted: §a(?<offersAccepted>\\d+)" + ) private val config get() = VisitorAPI.config - private var lastClickedNpc = 0 private val logger = LorenzLogger("garden/visitors/listener") + companion object { private val VISITOR_INFO_ITEM_SLOT = 13 private val VISITOR_ACCEPT_ITEM_SLOT = 29 @@ -75,6 +83,7 @@ class VisitorListener { if (!hasVisitorInfo) return val visitorsInTab = VisitorAPI.visitorsInTabList(event.tabList) + if (LorenzUtils.lastWorldSwitch.passedSince() > 2.seconds) { VisitorAPI.getVisitors().forEach { val name = it.visitorName @@ -94,11 +103,11 @@ class VisitorListener { @SubscribeEvent fun onInventoryOpen(event: InventoryFullyOpenedEvent) { if (!GardenAPI.inGarden()) return - val npcItem = event.inventoryItems[VISITOR_INFO_ITEM_SLOT] ?: return + val npcItem = event.inventoryItems[INFO_SLOT] ?: return val lore = npcItem.getLore() if (!VisitorAPI.isVisitorInfo(lore)) return - val offerItem = event.inventoryItems[VISITOR_ACCEPT_ITEM_SLOT] ?: return + val offerItem = event.inventoryItems[ACCEPT_SLOT] ?: return if (offerItem.name != "§aAccept Offer") return VisitorAPI.inInventory = true @@ -112,6 +121,7 @@ class VisitorListener { val visitor = VisitorAPI.getOrCreateVisitor(name) ?: return + visitor.offersAccepted = offersAcceptedPattern.matchMatcher(lore[3]) { group("offersAccepted").toInt() } visitor.entityId = lastClickedNpc visitor.offer = visitorOffer VisitorOpenEvent(visitor).postAndCatch() @@ -161,7 +171,7 @@ class VisitorListener { } } - VisitorAPI.changeStatus(visitor, VisitorStatus.REFUSED, "refused") + VisitorAPI.changeStatus(visitor, VisitorAPI.VisitorStatus.REFUSED, "refused") // fallback if tab list is disabled DelayedRun.runDelayed(10.seconds) { VisitorAPI.removeVisitor(visitor.visitorName) @@ -171,7 +181,7 @@ class VisitorListener { if (event.slotId == VISITOR_ACCEPT_ITEM_SLOT && event.slot?.stack?.getLore() ?.any { it == "§eClick to give!" } == true ) { - VisitorAPI.changeStatus(visitor, VisitorStatus.ACCEPTED, "accepted") + VisitorAPI.changeStatus(visitor, VisitorAPI.VisitorStatus.ACCEPTED, "accepted") return } } diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorRewardWarning.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorRewardWarning.kt new file mode 100644 index 000000000..641794ce7 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorRewardWarning.kt @@ -0,0 +1,116 @@ +package at.hannibal2.skyhanni.features.garden.visitor + +import at.hannibal2.skyhanni.data.ItemRenderBackground.Companion.background +import at.hannibal2.skyhanni.data.ItemRenderBackground.Companion.borderLine +import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.features.garden.GardenAPI +import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI.ACCEPT_SLOT +import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI.REFUSE_SLOT +import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI.VisitorBlockReason +import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI.lastClickedNpc +import at.hannibal2.skyhanni.utils.ItemUtils.name +import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyHeld +import at.hannibal2.skyhanni.utils.LorenzColor +import at.hannibal2.skyhanni.utils.NumberUtil +import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import net.minecraft.item.ItemStack +import net.minecraftforge.event.entity.player.ItemTooltipEvent +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import org.lwjgl.input.Keyboard + + +class VisitorRewardWarning { + private val config get() = VisitorAPI.config.rewardWarning + + @SubscribeEvent + fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { + if (!VisitorAPI.inInventory) return + if (!config.preventRefusing && !config.preventRefusingCopper && !config.preventAcceptingCopper) return + + val visitor = VisitorAPI.getVisitor(lastClickedNpc) ?: return + val refuseOfferStack = event.gui.inventorySlots.getSlot(REFUSE_SLOT).stack + val acceptOfferStack = event.gui.inventorySlots.getSlot(ACCEPT_SLOT).stack + val blockReason = visitor.blockReason ?: return + + if (blockReason.blockRefusing) { + renderColor(refuseOfferStack, acceptOfferStack, LorenzColor.GREEN) + } else { + renderColor(acceptOfferStack, refuseOfferStack, LorenzColor.RED) + } + } + + private fun renderColor(backgroundStack: ItemStack?, outlineStack: ItemStack?, outlineColor: LorenzColor) { + if (!config.bypassKey.isKeyHeld()) backgroundStack?.background = LorenzColor.DARK_GRAY.addOpacity(config.opacity).rgb + if (config.optionOutline) outlineStack?.borderLine = outlineColor.addOpacity(200).rgb + } + + @SubscribeEvent(priority = EventPriority.HIGH) + fun onStackClick(event: GuiContainerEvent.SlotClickEvent) { + if (!VisitorAPI.inInventory) return + val slot = event.slot ?: return + + val visitor = VisitorAPI.getVisitor(lastClickedNpc) ?: return + val blockReason = visitor.blockReason + + val isRefuseSlot = slot.stack.name == "§cRefuse Offer" + val isAcceptSlot = slot.stack.name == "§aAccept Offer" + + if (blockReason != null && !config.bypassKey.isKeyHeld() && + ((blockReason.blockRefusing && isRefuseSlot) || !blockReason.blockRefusing && isAcceptSlot)) { + event.isCanceled = true + return + } + + if (isRefuseSlot) { + VisitorAPI.changeStatus(visitor, VisitorAPI.VisitorStatus.REFUSED, "refused") + return + } + if (isAcceptSlot) { + if (slot.stack?.name != "§eClick to give!") return + VisitorAPI.changeStatus(visitor, VisitorAPI.VisitorStatus.ACCEPTED, "accepted") + return + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + fun onTooltip(event: ItemTooltipEvent) { + if (!GardenAPI.onBarnPlot) return + if (!VisitorAPI.inInventory) return + val visitor = VisitorAPI.getVisitor(lastClickedNpc) ?: return + if (config.bypassKey.isKeyHeld()) return + + val isRefuseSlot = event.itemStack.name == "§cRefuse Offer" + val isAcceptSlot = event.itemStack.name == "§aAccept Offer" + + val blockReason = visitor.blockReason?: return + if (blockReason.blockRefusing && !isRefuseSlot) return + if (!blockReason.blockRefusing && !isAcceptSlot) return + + if (visitor.blockedLore.isEmpty()) { + val copiedTooltip = event.toolTip.toList() + val blockedToolTip = mutableListOf<String>() + + for (line in copiedTooltip) { + if (line.contains("§aAccept Offer§r")) { + blockedToolTip.add(line.replace("§aAccept Offer§r", "§7Accept Offer§8")) + } else if (line.contains("§cRefuse Offer§r")) { + blockedToolTip.add(line.replace("§cRefuse Offer§r", "§7Refuse Offer§8")) + } else if (!line.contains("minecraft:") && !line.contains("NBT:")) { + blockedToolTip.add("§8" + line.removeColor()) + } + } + blockedToolTip.add("") + val pricePerCopper = visitor.pricePerCopper?.let { NumberUtil.format(it) } + blockedToolTip.add( + if (blockReason == VisitorBlockReason.CHEAP_COPPER || blockReason == VisitorBlockReason.EXPENSIVE_COPPER) + "${blockReason.description} §7(§6$pricePerCopper §7per)" else blockReason.description + ) + blockedToolTip.add(" §7(Bypass by holding ${Keyboard.getKeyName(config.bypassKey)})") + + visitor.blockedLore = blockedToolTip + } + event.toolTip.clear() + event.toolTip.addAll(visitor.blockedLore) + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/UtilsPatterns.kt b/src/main/java/at/hannibal2/skyhanni/utils/UtilsPatterns.kt index b6781bde0..97a4ab51c 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/UtilsPatterns.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/UtilsPatterns.kt @@ -49,7 +49,7 @@ object UtilsPatterns { ) val readAmountBeforePattern by patternGroup.pattern( "item.amount.front", - "(?: *§8(\\+§\\w)?(?<amount>[\\d.km,]+)(x )?)?(?<name>.*)" + "(?: +§8(?:\\+§.)?(?<amount>[\\d.,]+[km]?)x? )?(?<name>.*)" ) val readAmountAfterPattern by patternGroup.pattern( "item.amount.behind", |