aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHelfull <admin@helfull.de>2023-10-17 12:30:23 +0200
committerGitHub <noreply@github.com>2023-10-17 12:30:23 +0200
commit48a41d5dd03a7fa591827f3bcb0f9d6f2dc27e9a (patch)
tree5374eb81ed19d929187eb98a0a88433329e32b88 /src
parent8116fb8434a724b67be01e1369ef902b865d5c49 (diff)
downloadskyhanni-48a41d5dd03a7fa591827f3bcb0f9d6f2dc27e9a.tar.gz
skyhanni-48a41d5dd03a7fa591827f3bcb0f9d6f2dc27e9a.tar.bz2
skyhanni-48a41d5dd03a7fa591827f3bcb0f9d6f2dc27e9a.zip
Internal Changes: Refactor VisitorAPI to event based interaction (#561)
Split up GardenVisitorFeatures into multiple useful events #561
Diffstat (limited to 'src')
-rw-r--r--src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/VisitorAcceptEvent.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/VisitorArrivalEvent.kt5
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorAcceptedEvent.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorArrivalEvent.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorLeftEvent.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorOpenEvent.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorRefusedEvent.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorRenderEvent.kt9
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorToolTipEvent.kt7
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/AnitaMedalProfit.kt3
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorFeatures.kt376
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorTimer.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorAPI.kt140
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorListener.kt212
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorTooltipParser.kt42
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/inventory/HideNotClickableItems.kt3
-rw-r--r--src/test/java/at/hannibal2/skyhanni/test/garden/VisitorAPITest.kt51
-rw-r--r--src/test/java/at/hannibal2/skyhanni/test/garden/VisitorListenerTest.kt83
-rw-r--r--src/test/java/at/hannibal2/skyhanni/test/garden/VisitorToolTipParserTest.kt65
20 files changed, 705 insertions, 329 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
index ce7ad3490..cd2c458c8 100644
--- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
+++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
@@ -142,6 +142,7 @@ import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorColorNames
import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorDropStatistics
import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorFeatures
import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorTimer
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorListener
import at.hannibal2.skyhanni.features.inventory.AuctionsHighlighter
import at.hannibal2.skyhanni.features.inventory.HideNotClickableItems
import at.hannibal2.skyhanni.features.inventory.HighlightBonzoMasks
@@ -345,6 +346,7 @@ class SkyHanniMod {
loadModule(RenderData())
loadModule(GardenCropMilestones)
loadModule(GardenCropUpgrades())
+ loadModule(VisitorListener())
loadModule(OwnInventoryData())
loadModule(ToolTipData())
loadModule(GuiEditManager())
diff --git a/src/main/java/at/hannibal2/skyhanni/events/VisitorAcceptEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/VisitorAcceptEvent.kt
index 620694a22..af6fa275a 100644
--- a/src/main/java/at/hannibal2/skyhanni/events/VisitorAcceptEvent.kt
+++ b/src/main/java/at/hannibal2/skyhanni/events/VisitorAcceptEvent.kt
@@ -1,5 +1,5 @@
package at.hannibal2.skyhanni.events
-import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorFeatures
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI
-class VisitorAcceptEvent(val visitor: GardenVisitorFeatures.Visitor) : LorenzEvent() \ No newline at end of file
+class VisitorAcceptEvent(val visitor: VisitorAPI.Visitor) : LorenzEvent() \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/events/VisitorArrivalEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/VisitorArrivalEvent.kt
deleted file mode 100644
index 91be1bbe5..000000000
--- a/src/main/java/at/hannibal2/skyhanni/events/VisitorArrivalEvent.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package at.hannibal2.skyhanni.events
-
-import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorFeatures.Visitor
-
-class VisitorArrivalEvent(val visitor: Visitor) : LorenzEvent() \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorAcceptedEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorAcceptedEvent.kt
new file mode 100644
index 000000000..e3bffed8b
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorAcceptedEvent.kt
@@ -0,0 +1,6 @@
+package at.hannibal2.skyhanni.events.garden.visitor
+
+import at.hannibal2.skyhanni.events.LorenzEvent
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI
+
+class VisitorAcceptedEvent(val visitor: VisitorAPI.Visitor) : LorenzEvent() \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorArrivalEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorArrivalEvent.kt
new file mode 100644
index 000000000..673f45e20
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorArrivalEvent.kt
@@ -0,0 +1,6 @@
+package at.hannibal2.skyhanni.events.garden.visitor
+
+import at.hannibal2.skyhanni.events.LorenzEvent
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI.Visitor
+
+class VisitorArrivalEvent(val visitor: Visitor) : LorenzEvent() \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorLeftEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorLeftEvent.kt
new file mode 100644
index 000000000..f906364ce
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorLeftEvent.kt
@@ -0,0 +1,6 @@
+package at.hannibal2.skyhanni.events.garden.visitor
+
+import at.hannibal2.skyhanni.events.LorenzEvent
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI
+
+class VisitorLeftEvent(val visitor: VisitorAPI.Visitor) : LorenzEvent() \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorOpenEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorOpenEvent.kt
new file mode 100644
index 000000000..6abec9865
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorOpenEvent.kt
@@ -0,0 +1,6 @@
+package at.hannibal2.skyhanni.events.garden.visitor
+
+import at.hannibal2.skyhanni.events.LorenzEvent
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI
+
+class VisitorOpenEvent(val visitor: VisitorAPI.Visitor) : LorenzEvent() \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorRefusedEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorRefusedEvent.kt
new file mode 100644
index 000000000..fea24fba9
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorRefusedEvent.kt
@@ -0,0 +1,6 @@
+package at.hannibal2.skyhanni.events.garden.visitor
+
+import at.hannibal2.skyhanni.events.LorenzEvent
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI
+
+class VisitorRefusedEvent(val visitor: VisitorAPI.Visitor) : LorenzEvent() \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorRenderEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorRenderEvent.kt
new file mode 100644
index 000000000..f050d53a5
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorRenderEvent.kt
@@ -0,0 +1,9 @@
+package at.hannibal2.skyhanni.events.garden.visitor
+
+import at.hannibal2.skyhanni.events.LorenzEvent
+import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI
+import at.hannibal2.skyhanni.utils.LorenzVec
+import net.minecraftforge.client.event.RenderWorldLastEvent
+
+class VisitorRenderEvent(val visitor: VisitorAPI.Visitor, val location: LorenzVec, val parent: LorenzRenderWorldEvent) : LorenzEvent() \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorToolTipEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorToolTipEvent.kt
new file mode 100644
index 000000000..daf1439f3
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/garden/visitor/VisitorToolTipEvent.kt
@@ -0,0 +1,7 @@
+package at.hannibal2.skyhanni.events.garden.visitor
+
+import at.hannibal2.skyhanni.events.LorenzEvent
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI
+import net.minecraft.item.ItemStack
+
+class VisitorToolTipEvent(val visitor: VisitorAPI.Visitor, val itemStack: ItemStack, val toolTip: MutableList<String>) : LorenzEvent() \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/AnitaMedalProfit.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/AnitaMedalProfit.kt
index b0d0013a1..fd98cd437 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/AnitaMedalProfit.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/AnitaMedalProfit.kt
@@ -6,6 +6,7 @@ import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.InventoryCloseEvent
import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorFeatures
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI
import at.hannibal2.skyhanni.utils.ItemUtils
import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
@@ -47,7 +48,7 @@ class AnitaMedalProfit {
fun onInventoryOpen(event: InventoryFullyOpenedEvent) {
if (!config.medalProfitEnabled) return
if (event.inventoryName != "Anita") return
- if (GardenVisitorFeatures.inVisitorInventory) return
+ if (VisitorAPI.inVisitorInventory) return
inInventory = 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 8c7ceba3a..378b72e35 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
@@ -3,27 +3,24 @@ package at.hannibal2.skyhanni.features.garden.visitor
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
import at.hannibal2.skyhanni.data.IslandType
-import at.hannibal2.skyhanni.events.CheckRenderEntityEvent
import at.hannibal2.skyhanni.events.GuiRenderEvent
-import at.hannibal2.skyhanni.events.InventoryCloseEvent
-import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
import at.hannibal2.skyhanni.events.LorenzChatEvent
-import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent
import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.events.OwnInventoryItemUpdateEvent
-import at.hannibal2.skyhanni.events.PacketEvent
import at.hannibal2.skyhanni.events.PreProfileSwitchEvent
import at.hannibal2.skyhanni.events.TabListLineRenderEvent
-import at.hannibal2.skyhanni.events.TabListUpdateEvent
import at.hannibal2.skyhanni.events.VisitorAcceptEvent
-import at.hannibal2.skyhanni.events.VisitorArrivalEvent
-import at.hannibal2.skyhanni.events.withAlpha
+import at.hannibal2.skyhanni.events.garden.visitor.VisitorAcceptedEvent
+import at.hannibal2.skyhanni.events.garden.visitor.VisitorArrivalEvent
+import at.hannibal2.skyhanni.events.garden.visitor.VisitorOpenEvent
+import at.hannibal2.skyhanni.events.garden.visitor.VisitorRefusedEvent
+import at.hannibal2.skyhanni.events.garden.visitor.VisitorRenderEvent
+import at.hannibal2.skyhanni.events.garden.visitor.VisitorToolTipEvent
import at.hannibal2.skyhanni.features.bazaar.BazaarApi
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.mixins.hooks.RenderLivingEntityHelper
-import at.hannibal2.skyhanni.test.command.ErrorManager
import at.hannibal2.skyhanni.utils.EntityUtils
import at.hannibal2.skyhanni.utils.InventoryUtils
import at.hannibal2.skyhanni.utils.ItemBlink
@@ -33,13 +30,9 @@ import at.hannibal2.skyhanni.utils.ItemUtils.getItemName
import at.hannibal2.skyhanni.utils.ItemUtils.getItemNameOrNull
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
import at.hannibal2.skyhanni.utils.ItemUtils.name
-import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyHeld
-import at.hannibal2.skyhanni.utils.LocationUtils.distanceToPlayer
-import at.hannibal2.skyhanni.utils.LorenzColor
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.editCopy
import at.hannibal2.skyhanni.utils.NEUInternalName
import at.hannibal2.skyhanni.utils.NEUItems
import at.hannibal2.skyhanni.utils.NEUItems.getItemStack
@@ -47,37 +40,29 @@ import at.hannibal2.skyhanni.utils.NEUItems.getPrice
import at.hannibal2.skyhanni.utils.NumberUtil
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.RenderUtils.drawString
-import at.hannibal2.skyhanni.utils.RenderUtils.exactLocation
import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAndItems
import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import at.hannibal2.skyhanni.utils.TimeUtils
import at.hannibal2.skyhanni.utils.getLorenzVec
import at.hannibal2.skyhanni.utils.renderables.Renderable
-import io.github.moulberry.notenoughupdates.events.SlotClickEvent
import io.github.moulberry.notenoughupdates.util.MinecraftExecutor
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.inventory.GuiEditSign
-import net.minecraft.entity.Entity
import net.minecraft.entity.EntityLivingBase
import net.minecraft.entity.item.EntityArmorStand
import net.minecraft.item.ItemStack
-import net.minecraft.network.play.client.C02PacketUseEntity
import net.minecraftforge.client.event.GuiScreenEvent.DrawScreenEvent
import net.minecraftforge.client.event.RenderLivingEvent
-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
import kotlin.math.round
import kotlin.time.Duration.Companion.seconds
-private val config get() = SkyHanniMod.feature.garden.visitors
+private val config get() = VisitorAPI.config
class GardenVisitorFeatures {
- private var visitors = mapOf<String, Visitor>()
private var display = emptyList<List<Any>>()
- private var lastClickedNpc = 0
private val newVisitorArrivedMessage = ".* §r§ehas arrived on your §r§bGarden§r§e!".toPattern()
private val copperPattern = " §8\\+§c(?<amount>.*) Copper".toPattern()
private val gardenExperiencePattern = " §8\\+§2(?<amount>.*) §7Garden Experience".toPattern()
@@ -86,45 +71,16 @@ class GardenVisitorFeatures {
private val logger = LorenzLogger("garden/visitors")
private var lastFullPrice = 0.0
- companion object {
- var inVisitorInventory = false
- }
-
@SubscribeEvent
fun onPreProfileSwitch(event: PreProfileSwitchEvent) {
display = emptyList()
- visitors = emptyMap()
}
@SubscribeEvent
- fun onInventoryOpen(event: InventoryFullyOpenedEvent) {
- if (!GardenAPI.inGarden()) return
- val npcItem = event.inventoryItems[13] ?: return
- val lore = npcItem.getLore()
- var isVisitor = false
- if (lore.size == 4) {
- val line = lore[3]
- if (line.startsWith("§7Offers Accepted: §a")) {
- isVisitor = true
- }
- }
- if (!isVisitor) return
-
- val offerItem = event.inventoryItems[29] ?: return
- if (offerItem.name != "§aAccept Offer") return
-
- inVisitorInventory = true
-
- if (!config.needs.display && config.highlightStatus == 3) return
-
- var name = npcItem.name ?: return
- if (name.length == name.removeColor().length + 4) {
- name = name.substring(2)
- }
-
- val visitor = getOrCreateVisitor(name) ?: return
+ fun onVisitorOpen(event: VisitorOpenEvent) {
+ val visitor = event.visitor
+ val offerItem = visitor.offer!!.offerItem
- visitor.entityId = lastClickedNpc
for (line in offerItem.getLore()) {
if (line == "§7Items Required:") continue
if (line.isEmpty()) break
@@ -138,54 +94,22 @@ class GardenVisitorFeatures {
val internalName = NEUInternalName.fromItemName(itemName)
visitor.items[internalName] = amount
}
- readToolTip(visitor, event.inventoryItems[29])
- if (visitor.status == VisitorStatus.NEW) {
- val alreadyReady = event.inventoryItems[29]?.getLore()?.any { it == "§eClick to give!" } == true
+ readToolTip(visitor, offerItem)
+
+ if (visitor.status == VisitorAPI.VisitorStatus.NEW) {
+ val alreadyReady = offerItem.getLore().any { it == "§eClick to give!" } == true
if (alreadyReady) {
- changeStatus(visitor, VisitorStatus.READY, "inSacks")
+ VisitorAPI.changeStatus(visitor, VisitorAPI.VisitorStatus.READY, "inSacks")
visitor.inSacks = true
update()
} else {
- val waiting = VisitorStatus.WAITING
- changeStatus(visitor, waiting, "firstContact")
+ VisitorAPI.changeStatus(visitor, VisitorAPI.VisitorStatus.WAITING, "firstContact")
}
update()
}
}
- private fun getOrCreateVisitor(name: String): Visitor? {
- var visitor = visitors[name]
- if (visitor == null) {
- // workaround if the tab list has not yet updated when opening the visitor
- addVisitor(name)
- LorenzUtils.debug("Found visitor from npc that is not in tab list. Adding it still.")
- updateDisplay()
- visitor = visitors[name]
- }
-
- if (visitor != null) return visitor
-
- println("visitors: $visitors")
- println("name: $name")
- ErrorManager.logErrorState(
- "Error finding the visitor `$name§c`. Try to reopen the inventory",
- "visitor is null! name='$name', visitors=`$visitors`"
- )
- return null
- }
-
- private fun readReward(offerItem: ItemStack): VisitorReward? {
- for (line in offerItem.getLore()) {
- for (reward in VisitorReward.entries) {
- if (line.contains(reward.displayName)) {
- return reward
- }
- }
- }
- return null
- }
-
private fun updateDisplay() {
display = drawDisplay()
}
@@ -196,8 +120,8 @@ class GardenVisitorFeatures {
val requiredItems = mutableMapOf<NEUInternalName, Int>()
val newVisitors = mutableListOf<String>()
- for ((visitorName, visitor) in visitors) {
- if (visitor.status == VisitorStatus.ACCEPTED || visitor.status == VisitorStatus.REFUSED) continue
+ for ((visitorName, visitor) in VisitorAPI.getVisitorsMap()) {
+ if (visitor.status == VisitorAPI.VisitorStatus.ACCEPTED || visitor.status == VisitorAPI.VisitorStatus.REFUSED) continue
val items = visitor.items
if (items.isEmpty()) {
@@ -294,102 +218,42 @@ class GardenVisitorFeatures {
}
}
- @SubscribeEvent(priority = EventPriority.HIGH)
- fun onStackClick(event: SlotClickEvent) {
- if (!inVisitorInventory) return
- if (event.clickType != 0) return
-
- val visitor = getVisitor(lastClickedNpc) ?: return
-
- if (event.slotId == 33) {
- if (event.slot.stack?.name != "§cRefuse Offer") return
-
- visitor.hasReward()?.let {
- if (config.rewardWarning.preventRefusing) {
- if (config.rewardWarning.bypassKey.isKeyHeld()) {
- LorenzUtils.chat("§e[SkyHanni] §cBypassed blocking refusal of visitor ${visitor.visitorName} §7(${it.displayName}§7)")
- return
- }
- event.isCanceled = true
- LorenzUtils.chat("§e[SkyHanni] §cBlocked refusing visitor ${visitor.visitorName} §7(${it.displayName}§7)")
- if (config.rewardWarning.bypassKey == Keyboard.KEY_NONE) {
- LorenzUtils.clickableChat(
- "§eIf you want to deny this visitor, set a keybind in §e/sh bypass",
- "sh bypass"
- )
- }
- Minecraft.getMinecraft().thePlayer.closeScreen()
- return
- }
- }
-
- changeStatus(visitor, VisitorStatus.REFUSED, "refused")
- update()
- GardenVisitorDropStatistics.deniedVisitors += 1
- GardenVisitorDropStatistics.saveAndUpdate()
- return
- }
- if (event.slotId == 29 && event.slot.stack?.getLore()?.any { it == "§eClick to give!" } == true) {
- changeStatus(visitor, VisitorStatus.ACCEPTED, "accepted")
- acceptVisitor(visitor)
- update()
- GardenVisitorDropStatistics.coinsSpent += round(lastFullPrice).toLong()
- GardenVisitorDropStatistics.lastAccept = System.currentTimeMillis()
- return
- }
- }
-
- private fun acceptVisitor(visitor: Visitor) {
- VisitorAcceptEvent(visitor).postAndCatch()
+ @SubscribeEvent
+ fun onVisitorRefused(event: VisitorRefusedEvent) {
+ update()
+ GardenVisitorDropStatistics.deniedVisitors += 1
+ GardenVisitorDropStatistics.saveAndUpdate()
}
- private fun getVisitor(id: Int) = visitors.map { it.value }.find { it.entityId == id }
-
@SubscribeEvent
- fun onCheckRender(event: CheckRenderEntityEvent<*>) {
- if (!GardenAPI.inGarden()) return
- if (!GardenAPI.onBarnPlot) return
- if (config.highlightStatus != 1 && config.highlightStatus != 2) return
-
- val entity = event.entity
- if (entity is EntityArmorStand && entity.name == "§e§lCLICK") {
- event.isCanceled = true
- }
+ fun onVisitorAccepted(event: VisitorAcceptedEvent) {
+ VisitorAcceptEvent(event.visitor).postAndCatch()
+ update()
+ GardenVisitorDropStatistics.coinsSpent += round(lastFullPrice).toLong()
+ GardenVisitorDropStatistics.lastAccept = System.currentTimeMillis()
}
@SubscribeEvent
- fun onRenderWorld(event: LorenzRenderWorldEvent) {
- if (!GardenAPI.inGarden()) return
- if (!GardenAPI.onBarnPlot) return
- if (config.highlightStatus != 1 && config.highlightStatus != 2) return
-
- for (visitor in visitors.values) {
- visitor.getNameTagEntity()?.let {
- val location = event.exactLocation(it)
- if (it.distanceToPlayer() < 15) {
- val text = visitor.status.displayName
- event.drawString(location.add(0.0, 2.23, 0.0), text)
- if (config.rewardWarning.showOverName) {
- visitor.hasReward()?.let { reward ->
- val name = reward.displayName
-
- event.drawString(location.add(0.0, 2.73, 0.0), "§c!$name§c!")
- }
- }
- }
+ fun onVisitorRender(event: VisitorRenderEvent) {
+ val visitor = event.visitor
+ val text = visitor.status.displayName
+ val location = event.location
+ event.parent.drawString(location.add(0.0, 2.23, 0.0), text)
+ if (config.rewardWarning.showOverName) {
+ visitor.hasReward()?.let { reward ->
+ val name = reward.displayName
+
+ event.parent.drawString(location.add(0.0, 2.73, 0.0), "§c!$name§c!")
}
}
}
@SubscribeEvent(priority = EventPriority.HIGH)
- fun onTooltip(event: ItemTooltipEvent) {
- if (!GardenAPI.onBarnPlot) return
- if (!inVisitorInventory) return
+ fun onVisitorTooltip(event: VisitorToolTipEvent) {
if (event.itemStack.name != "§aAccept Offer") return
- val visitor = getVisitor(lastClickedNpc) ?: return
-
- val toolTip = event.toolTip ?: return
+ val visitor = event.visitor
+ val toolTip = event.toolTip
toolTip.clear()
if (visitor.lastLore.isEmpty()) {
@@ -400,7 +264,7 @@ class GardenVisitorFeatures {
toolTip.addAll(visitor.lastLore)
}
- private fun readToolTip(visitor: Visitor, itemStack: ItemStack?) {
+ private fun readToolTip(visitor: VisitorAPI.Visitor, itemStack: ItemStack?) {
val stack = itemStack ?: error("Accept offer item not found for visitor ${visitor.visitorName}")
var totalPrice = 0.0
var timeRequired = -1L
@@ -512,74 +376,11 @@ class GardenVisitorFeatures {
}
@SubscribeEvent
- fun onInventoryClose(event: InventoryCloseEvent) {
- inVisitorInventory = false
- }
-
- @SubscribeEvent
- fun onTabListUpdate(event: TabListUpdateEvent) {
- if (!GardenAPI.inGarden()) return
- var found = false
- val visitorsInTab = mutableListOf<String>()
- for (line in event.tabList) {
- if (line.startsWith("§b§lVisitors:")) {
- found = true
- continue
- }
- if (found) {
- if (line.isEmpty()) {
- found = false
- continue
- }
- val name = fromHypixelName(line)
-
- // Hide hypixel watchdog entries
- if (name.contains("§c") && !name.contains("Spaceman") && !name.contains("Grandma Wolf")) {
- logger.log("Ignore wrong red name: '$name'")
- continue
- }
-
- //hide own player name
- if (name.contains(LorenzUtils.getPlayerName())) {
- logger.log("Ignore wrong own name: '$name'")
- continue
- }
-
- visitorsInTab.add(name)
- }
- }
- val removedVisitors = mutableListOf<String>()
- visitors.forEach {
- val name = it.key
- val time = System.currentTimeMillis() - LorenzUtils.lastWorldSwitch
- val removed = name !in visitorsInTab && time > 2_000
- if (removed) {
- logger.log("Removed old visitor: '$name'")
- removedVisitors.add(name)
- }
- }
- var dirty = false
- if (removedVisitors.isNotEmpty()) {
- visitors = visitors.editCopy {
- keys.removeIf { it in removedVisitors }
- }
- dirty = true
- }
- for (name in visitorsInTab) {
- if (!visitors.containsKey(name)) {
- addVisitor(name)
- dirty = true
- }
- }
- if (dirty) {
- updateDisplay()
- }
- }
+ fun onVisitorArrival(event: VisitorArrivalEvent) {
+ val visitor = event.visitor
+ val name = visitor.visitorName
- private fun addVisitor(name: String) {
- val visitor = Visitor(name, status = VisitorStatus.NEW)
- visitors = visitors.editCopy { this[name] = visitor }
- VisitorArrivalEvent(visitor).postAndCatch()
+ update()
logger.log("New visitor detected: '$name'")
@@ -603,21 +404,13 @@ class GardenVisitorFeatures {
}
}
- private fun fromHypixelName(line: String): String {
- var name = line.trim().replace("§r", "").trim()
- if (!name.contains("§")) {
- name = "§f$name"
- }
- return name
- }
-
@SubscribeEvent
fun onTabListText(event: TabListLineRenderEvent) {
if (!GardenAPI.inGarden()) return
if (!config.coloredName) return
val text = event.text
- val replace = fromHypixelName(text)
- val visitor = visitors[replace]
+ val replace = VisitorAPI.fromHypixelName(text)
+ val visitor = VisitorAPI.getVisitor(replace)
visitor?.let {
event.text = " " + GardenVisitorColorNames.getColoredName(it.visitorName)
}
@@ -639,7 +432,7 @@ class GardenVisitorFeatures {
if (name == "Spaceman") return false
if (name == "Beth") return false
- return visitors.keys.any { it.removeColor() == name }
+ return VisitorAPI.getVisitorsMap().keys.any { it.removeColor() == name }
} ?: false
private fun update() {
@@ -648,7 +441,8 @@ class GardenVisitorFeatures {
}
private fun checkVisitorsReady() {
- for ((visitorName, visitor) in visitors) {
+ for (visitor in VisitorAPI.getVisitors()) {
+ val visitorName = visitor.visitorName
val entity = visitor.getEntity()
if (entity == null) {
findNametag(visitorName.removeColor())?.let {
@@ -658,9 +452,9 @@ class GardenVisitorFeatures {
if (!visitor.inSacks) {
val status = visitor.status
- if (status == VisitorStatus.WAITING || status == VisitorStatus.READY) {
- val newStatus = if (hasItemsInInventory(visitor)) VisitorStatus.READY else VisitorStatus.WAITING
- changeStatus(visitor, newStatus, "hasItemsInInventory")
+ if (status == VisitorAPI.VisitorStatus.WAITING || status == VisitorAPI.VisitorStatus.READY) {
+ val newStatus = if (hasItemsInInventory(visitor)) VisitorAPI.VisitorStatus.READY else VisitorAPI.VisitorStatus.WAITING
+ VisitorAPI.changeStatus(visitor, newStatus, "hasItemsInInventory")
}
}
@@ -678,14 +472,7 @@ class GardenVisitorFeatures {
}
}
- private fun changeStatus(visitor: Visitor, newStatus: VisitorStatus, reason: String) {
- val old = visitor.status
- if (old == newStatus) return
- visitor.status = newStatus
- logger.log("Visitor status change for '${visitor.visitorName}': $old -> $newStatus ($reason)")
- }
-
- private fun findEntity(nameTag: EntityArmorStand, visitor: Visitor) {
+ private fun findEntity(nameTag: EntityArmorStand, visitor: VisitorAPI.Visitor) {
for (entity in EntityUtils.getAllEntities()) {
if (entity is EntityArmorStand) continue
if (entity.getLorenzVec().distanceIgnoreY(nameTag.getLorenzVec()) != 0.0) continue
@@ -722,7 +509,7 @@ class GardenVisitorFeatures {
return foundVisitorNameTags[0]
}
- private fun hasItemsInInventory(visitor: Visitor): Boolean {
+ private fun hasItemsInInventory(visitor: VisitorAPI.Visitor): Boolean {
var ready = true
for ((internalName, need) in visitor.items) {
val having = InventoryUtils.countItemsInLowerInventory { it.getInternalName() == internalName }
@@ -733,19 +520,6 @@ class GardenVisitorFeatures {
return ready
}
- // TODO make event
- @SubscribeEvent
- fun onSendEvent(event: PacketEvent.SendEvent) {
- val packet = event.packet
- if (packet !is C02PacketUseEntity) return
-
- val theWorld = Minecraft.getMinecraft().theWorld
- val entity = packet.getEntityFromWorld(theWorld) ?: return
- val entityId = entity.entityId
-
- lastClickedNpc = entityId
- }
-
@SubscribeEvent
fun onRenderInSigns(event: DrawScreenEvent.Post) {
if (!GardenAPI.inGarden()) return
@@ -787,49 +561,13 @@ class GardenVisitorFeatures {
if (!config.coloredName) return
val entity = event.entity
val entityId = entity.entityId
- for (visitor in visitors.values) {
+ for (visitor in VisitorAPI.getVisitors()) {
if (visitor.nameTagEntityId == entityId) {
entity.customNameTag = GardenVisitorColorNames.getColoredName(entity.name)
}
}
}
- class Visitor(
- val visitorName: String,
- var entityId: Int = -1,
- var nameTagEntityId: Int = -1,
- var status: VisitorStatus,
- var inSacks: Boolean = false,
- val items: MutableMap<NEUInternalName, Int> = mutableMapOf(),
- ) {
-
- var allRewards = listOf<NEUInternalName>()
- var lastLore = listOf<String>()
- fun getEntity(): Entity? = Minecraft.getMinecraft().theWorld.getEntityByID(entityId)
-
- fun getNameTagEntity(): Entity? = Minecraft.getMinecraft().theWorld.getEntityByID(nameTagEntityId)
-
- fun hasReward(): VisitorReward? {
- for (internalName in allRewards) {
- val reward = VisitorReward.getByInternalName(internalName) ?: continue
-
- if (config.rewardWarning.drops.contains(reward.ordinal)) {
- return reward
- }
- }
-
- return null
- }
- }
-
- enum class VisitorStatus(val displayName: String, val color: Int) {
- NEW("§eNew", LorenzColor.YELLOW.toColor().withAlpha(100)),
- WAITING("Waiting", -1),
- READY("§aItems Ready", LorenzColor.GREEN.toColor().withAlpha(80)),
- ACCEPTED("§7Accepted", LorenzColor.DARK_GRAY.toColor().withAlpha(80)),
- REFUSED("§cRefused", LorenzColor.RED.toColor().withAlpha(60)),
- }
-
@SubscribeEvent
fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) {
event.move(3, "garden.visitorNeedsDisplay", "garden.visitors.needs.display")
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorTimer.kt
index dedd034fa..a4b86e03c 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorTimer.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/GardenVisitorTimer.kt
@@ -6,7 +6,7 @@ import at.hannibal2.skyhanni.events.CropClickEvent
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
import at.hannibal2.skyhanni.events.PreProfileSwitchEvent
-import at.hannibal2.skyhanni.events.VisitorArrivalEvent
+import at.hannibal2.skyhanni.events.garden.visitor.VisitorArrivalEvent
import at.hannibal2.skyhanni.features.garden.GardenAPI
import at.hannibal2.skyhanni.test.command.ErrorManager
import at.hannibal2.skyhanni.utils.LorenzUtils
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
new file mode 100644
index 000000000..4f9bbb23d
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorAPI.kt
@@ -0,0 +1,140 @@
+package at.hannibal2.skyhanni.features.garden.visitor
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.garden.visitor.VisitorAcceptedEvent
+import at.hannibal2.skyhanni.events.garden.visitor.VisitorArrivalEvent
+import at.hannibal2.skyhanni.events.garden.visitor.VisitorLeftEvent
+import at.hannibal2.skyhanni.events.garden.visitor.VisitorRefusedEvent
+import at.hannibal2.skyhanni.events.withAlpha
+import at.hannibal2.skyhanni.test.command.ErrorManager
+import at.hannibal2.skyhanni.utils.LorenzColor
+import at.hannibal2.skyhanni.utils.LorenzLogger
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.editCopy
+import at.hannibal2.skyhanni.utils.NEUInternalName
+import net.minecraft.client.Minecraft
+import net.minecraft.entity.Entity
+import net.minecraft.item.ItemStack
+
+object VisitorAPI {
+ private var visitors = mapOf<String, Visitor>()
+ var inVisitorInventory = false
+ val config get() = SkyHanniMod.feature.garden.visitors
+ private val logger = LorenzLogger("garden/visitors/api")
+
+ fun getVisitorsMap() = visitors
+ fun getVisitors() = visitors.values
+ fun getVisitor(id: Int) = visitors.map { it.value }.find { it.entityId == id }
+ fun getVisitor(name: String) = visitors[name]
+
+ fun reset() {
+ visitors = emptyMap()
+ }
+
+ fun changeStatus(visitor: Visitor, newStatus: VisitorStatus, reason: String) {
+ val old = visitor.status
+ if (old == newStatus) return
+ visitor.status = newStatus
+ logger.log("Visitor status change for '${visitor.visitorName}': $old -> $newStatus ($reason)")
+
+ when (newStatus) {
+ VisitorStatus.ACCEPTED -> {
+ VisitorAcceptedEvent(visitor).postAndCatch()
+ }
+ VisitorStatus.REFUSED -> {
+ VisitorRefusedEvent(visitor).postAndCatch()
+ }
+ else -> {}
+ }
+ }
+
+ fun getOrCreateVisitor(name: String): Visitor? {
+ var visitor = visitors[name]
+ if (visitor == null) {
+ // workaround if the tab list has not yet updated when opening the visitor
+ addVisitor(name)
+ LorenzUtils.debug("Found visitor from npc that is not in tab list. Adding it still.")
+ visitor = visitors[name]
+ }
+
+ if (visitor != null) return visitor
+
+ println("visitors: $visitors")
+ println("name: $name")
+ ErrorManager.logErrorState(
+ "Error finding the visitor `$name§c`. Try to reopen the inventory",
+ "visitor is null! name='$name', visitors=`$visitors`"
+ )
+ return null
+ }
+
+ fun removeVisitor(name: String): Boolean {
+ if (!visitors.containsKey(name)) return false
+ val visitor = visitors[name] ?: return false
+ visitors = visitors.editCopy { remove(name) }
+ VisitorLeftEvent(visitor).postAndCatch()
+ return true
+ }
+
+ fun addVisitor(name: String): Boolean {
+ if (visitors.containsKey(name)) return false
+ val visitor = Visitor(name, status = VisitorStatus.NEW)
+ visitors = visitors.editCopy { this[name] = visitor }
+ VisitorArrivalEvent(visitor).postAndCatch()
+ return true
+ }
+
+ fun fromHypixelName(line: String): String {
+ var name = line.trim().replace("§r", "").trim()
+ if (!name.contains("§")) {
+ name = "§f$name"
+ }
+ return name
+ }
+
+ fun isVisitorInfo(lore: List<String>): Boolean {
+ if (lore.size != 4) return false
+ return lore[3].startsWith("§7Offers Accepted: §a")
+ }
+
+ class VisitorOffer(
+ val offerItem: ItemStack
+ )
+
+ class Visitor(
+ val visitorName: String,
+ var entityId: Int = -1,
+ var nameTagEntityId: Int = -1,
+ var status: VisitorStatus,
+ var inSacks: Boolean = false,
+ val items: MutableMap<NEUInternalName, Int> = mutableMapOf(),
+ var offer: VisitorOffer? = null,
+ ) {
+ var lore: List<String> = emptyList()
+ var allRewards = listOf<NEUInternalName>()
+ var lastLore = listOf<String>()
+
+ fun getEntity(): Entity? = Minecraft.getMinecraft().theWorld.getEntityByID(entityId)
+ fun getNameTagEntity(): Entity? = Minecraft.getMinecraft().theWorld.getEntityByID(nameTagEntityId)
+
+ fun hasReward(): VisitorReward? {
+ for (internalName in allRewards) {
+ val reward = VisitorReward.getByInternalName(internalName) ?: continue
+
+ if (config.rewardWarning.drops.contains(reward.ordinal)) {
+ return reward
+ }
+ }
+
+ return null
+ }
+ }
+
+ enum class VisitorStatus(val displayName: String, val color: Int) {
+ NEW("§eNew", LorenzColor.YELLOW.toColor().withAlpha(100)),
+ WAITING("Waiting", -1),
+ READY("§aItems Ready", LorenzColor.GREEN.toColor().withAlpha(80)),
+ ACCEPTED("§7Accepted", LorenzColor.DARK_GRAY.toColor().withAlpha(80)),
+ REFUSED("§cRefused", LorenzColor.RED.toColor().withAlpha(60)),
+ }
+} \ No newline at end of file
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
new file mode 100644
index 000000000..c03d666f9
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorListener.kt
@@ -0,0 +1,212 @@
+package at.hannibal2.skyhanni.features.garden.visitor
+
+import at.hannibal2.skyhanni.events.CheckRenderEntityEvent
+import at.hannibal2.skyhanni.events.InventoryCloseEvent
+import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
+import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent
+import at.hannibal2.skyhanni.events.PacketEvent
+import at.hannibal2.skyhanni.events.PreProfileSwitchEvent
+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.events.garden.visitor.VisitorToolTipEvent
+import at.hannibal2.skyhanni.features.garden.GardenAPI
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI.VisitorStatus
+import at.hannibal2.skyhanni.utils.ItemUtils.getLore
+import at.hannibal2.skyhanni.utils.ItemUtils.name
+import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyHeld
+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.removeColor
+import io.github.moulberry.notenoughupdates.events.SlotClickEvent
+import net.minecraft.client.Minecraft
+import net.minecraft.entity.item.EntityArmorStand
+import net.minecraft.network.play.client.C02PacketUseEntity
+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
+
+private val config get() = VisitorAPI.config
+
+class VisitorListener {
+ 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
+ private val VISITOR_REFUSE_ITEM_SLOT = 33
+ }
+
+ @SubscribeEvent
+ fun onPreProfileSwitch(event: PreProfileSwitchEvent) {
+ VisitorAPI.reset()
+ }
+
+ // TODO make event
+ @SubscribeEvent
+ fun onSendEvent(event: PacketEvent.SendEvent) {
+ val packet = event.packet
+ if (packet !is C02PacketUseEntity) return
+
+ val theWorld = Minecraft.getMinecraft().theWorld
+ val entity = packet.getEntityFromWorld(theWorld) ?: return
+ val entityId = entity.entityId
+
+ lastClickedNpc = entityId
+ }
+
+ @SubscribeEvent
+ fun onTabListUpdate(event: TabListUpdateEvent) {
+ if (!GardenAPI.inGarden()) return
+ var found = false
+ val visitorsInTab = mutableListOf<String>()
+ for (line in event.tabList) {
+ if (line.startsWith("§b§lVisitors:")) {
+ found = true
+ continue
+ }
+ if (!found) continue
+
+ if (line.isEmpty()) {
+ found = false
+ continue
+ }
+ val name = VisitorAPI.fromHypixelName(line)
+
+ // Hide hypixel watchdog entries
+ if (name.contains("§c") && !name.contains("Spaceman") && !name.contains("Grandma Wolf")) {
+ logger.log("Ignore wrong red name: '$name'")
+ continue
+ }
+
+ //hide own player name
+ if (name.contains(LorenzUtils.getPlayerName())) {
+ logger.log("Ignore wrong own name: '$name'")
+ continue
+ }
+
+ visitorsInTab.add(name)
+ }
+
+ VisitorAPI.getVisitors().forEach {
+ val name = it.visitorName
+ val time = System.currentTimeMillis() - LorenzUtils.lastWorldSwitch
+ val removed = name !in visitorsInTab && time > 2_000
+ if (removed) {
+ logger.log("Removed old visitor: '$name'")
+ VisitorAPI.removeVisitor(name)
+ }
+ }
+
+ for (name in visitorsInTab) {
+ VisitorAPI.addVisitor(name)
+ }
+ }
+
+ @SubscribeEvent
+ fun onInventoryOpen(event: InventoryFullyOpenedEvent) {
+ if (!GardenAPI.inGarden()) return
+ val npcItem = event.inventoryItems[VISITOR_INFO_ITEM_SLOT] ?: return
+ val lore = npcItem.getLore()
+ if(!VisitorAPI.isVisitorInfo(lore)) return
+
+ val offerItem = event.inventoryItems[VISITOR_ACCEPT_ITEM_SLOT] ?: return
+ if (offerItem.name != "§aAccept Offer") return
+
+ VisitorAPI.inVisitorInventory = true
+
+ val visitorOffer = VisitorAPI.VisitorOffer(offerItem)
+
+ var name = npcItem.name ?: return
+ if (name.length == name.removeColor().length + 4) {
+ name = name.substring(2)
+ }
+
+ val visitor = VisitorAPI.getOrCreateVisitor(name) ?: return
+
+ visitor.entityId = lastClickedNpc
+ visitor.offer = visitorOffer
+ VisitorOpenEvent(visitor).postAndCatch()
+ }
+
+ @SubscribeEvent
+ fun onInventoryClose(event: InventoryCloseEvent) {
+ VisitorAPI.inVisitorInventory = false
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGH)
+ fun onStackClick(event: SlotClickEvent) {
+ if (!VisitorAPI.inVisitorInventory) return
+ if (event.clickType != 0) return
+
+ val visitor = VisitorAPI.getVisitor(lastClickedNpc) ?: return
+
+ if (event.slotId == VISITOR_REFUSE_ITEM_SLOT) {
+ if (event.slot.stack?.name != "§cRefuse Offer") return
+
+ visitor.hasReward()?.let {
+ if (config.rewardWarning.preventRefusing) {
+ if (config.rewardWarning.bypassKey.isKeyHeld()) {
+ LorenzUtils.chat("§e[SkyHanni] §cBypassed blocking refusal of visitor ${visitor.visitorName} §7(${it.displayName}§7)")
+ return
+ }
+ event.isCanceled = true
+ LorenzUtils.chat("§e[SkyHanni] §cBlocked refusing visitor ${visitor.visitorName} §7(${it.displayName}§7)")
+ if (config.rewardWarning.bypassKey == Keyboard.KEY_NONE) {
+ LorenzUtils.clickableChat(
+ "§eIf you want to deny this visitor, set a keybind in §e/sh bypass",
+ "sh bypass"
+ )
+ }
+ Minecraft.getMinecraft().thePlayer.closeScreen()
+ return
+ }
+ }
+
+ VisitorAPI.changeStatus(visitor, VisitorStatus.REFUSED, "refused")
+ return
+ }
+ if (event.slotId == VISITOR_ACCEPT_ITEM_SLOT && event.slot.stack?.getLore()?.any { it == "§eClick to give!" } == true) {
+ VisitorAPI.changeStatus(visitor, VisitorStatus.ACCEPTED, "accepted")
+ return
+ }
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGH)
+ fun onTooltip(event: ItemTooltipEvent) {
+ if (!GardenAPI.onBarnPlot) return
+ if (!VisitorAPI.inVisitorInventory) return
+ val visitor = VisitorAPI.getVisitor(lastClickedNpc) ?: return
+ VisitorToolTipEvent(visitor, event.itemStack, event.toolTip).postAndCatch()
+ }
+
+ @SubscribeEvent
+ fun onCheckRender(event: CheckRenderEntityEvent<*>) {
+ if (!GardenAPI.inGarden()) return
+ if (!GardenAPI.onBarnPlot) return
+ if (config.highlightStatus != 1 && config.highlightStatus != 2) return
+
+ val entity = event.entity
+ if (entity is EntityArmorStand && entity.name == "§e§lCLICK") {
+ event.isCanceled = true
+ }
+ }
+
+ @SubscribeEvent
+ fun onRenderWorld(event: LorenzRenderWorldEvent) {
+ if (!GardenAPI.inGarden()) return
+ if (!GardenAPI.onBarnPlot) return
+ if (config.highlightStatus != 1 && config.highlightStatus != 2) return
+
+ for (visitor in VisitorAPI.getVisitors()) {
+ visitor.getNameTagEntity()?.let {
+ if (it.distanceToPlayer() > 15) return@let
+ VisitorRenderEvent(visitor, event.exactLocation(it), event).postAndCatch()
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorTooltipParser.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorTooltipParser.kt
new file mode 100644
index 000000000..5d6e7ee26
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/visitor/VisitorTooltipParser.kt
@@ -0,0 +1,42 @@
+package at.hannibal2.skyhanni.features.garden.visitor
+
+import at.hannibal2.skyhanni.config.features.GardenConfig
+import at.hannibal2.skyhanni.utils.ItemUtils
+
+class VisitorTooltipParser {
+ class ParsedTooltip(
+ val itemsNeeded: MutableMap<String, Int>,
+ val rewards: MutableMap<String, Int>,
+ val config: GardenConfig,
+ )
+
+ enum class ParsingSection {
+ ITEMS_NEEDED,
+ REWARDS
+
+ }
+
+ companion object {
+ fun parse(lore: List<String>, config: GardenConfig?): ParsedTooltip {
+ var section = ParsingSection.ITEMS_NEEDED
+ val parsedData = ParsedTooltip(mutableMapOf(), mutableMapOf(), config ?: GardenConfig())
+ for (line in lore) {
+ if (line.isBlank()) continue
+ val isRewardSection = line.contains("Rewards:")
+ if (isRewardSection) {
+ section = ParsingSection.REWARDS
+ continue
+ }
+
+ val (itemName, amount) = ItemUtils.readItemAmount(line) ?: continue
+
+ when (section) {
+ ParsingSection.ITEMS_NEEDED -> parsedData.itemsNeeded[itemName] = amount
+ ParsingSection.REWARDS -> parsedData.rewards[itemName] = amount
+ }
+ }
+
+ return parsedData;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/at/hannibal2/skyhanni/features/inventory/HideNotClickableItems.kt b/src/main/java/at/hannibal2/skyhanni/features/inventory/HideNotClickableItems.kt
index 94c20c2ed..7e015b09c 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/inventory/HideNotClickableItems.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/inventory/HideNotClickableItems.kt
@@ -9,6 +9,7 @@ import at.hannibal2.skyhanni.events.RepositoryReloadEvent
import at.hannibal2.skyhanni.features.bazaar.BazaarApi
import at.hannibal2.skyhanni.features.garden.composter.ComposterOverlay
import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorFeatures
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI
import at.hannibal2.skyhanni.features.rift.RiftAPI
import at.hannibal2.skyhanni.features.rift.RiftAPI.motesNpcPrice
import at.hannibal2.skyhanni.utils.InventoryUtils
@@ -385,7 +386,7 @@ class HideNotClickableItems {
private fun hideNpcSell(chestName: String, stack: ItemStack): Boolean {
if (!tradeNpcFilter.match(chestName)) return false
- if (GardenVisitorFeatures.inVisitorInventory) return false
+ if (VisitorAPI.inVisitorInventory) return false
reverseColor = true
var name = stack.cleanName()
diff --git a/src/test/java/at/hannibal2/skyhanni/test/garden/VisitorAPITest.kt b/src/test/java/at/hannibal2/skyhanni/test/garden/VisitorAPITest.kt
new file mode 100644
index 000000000..74baf866a
--- /dev/null
+++ b/src/test/java/at/hannibal2/skyhanni/test/garden/VisitorAPITest.kt
@@ -0,0 +1,51 @@
+package at.hannibal2.skyhanni.test.garden
+
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI
+import org.junit.jupiter.api.Test
+
+class VisitorAPITest {
+
+ @Test
+ fun testFromHypixelName() {
+ assert(VisitorAPI.fromHypixelName(" §r Jacob") == "§fJacob")
+ assert(VisitorAPI.fromHypixelName("§r Jacob") == "§fJacob")
+ assert(VisitorAPI.fromHypixelName("§rJacob") == "§fJacob")
+ assert(VisitorAPI.fromHypixelName("Jacob") == "§fJacob")
+ assert(VisitorAPI.fromHypixelName(" Jacob ") == "§fJacob")
+ assert(VisitorAPI.fromHypixelName("§cSpaceman") == "§cSpaceman")
+ assert(VisitorAPI.fromHypixelName("§cGrandma Wolf") == "§cGrandma Wolf")
+ }
+
+ @Test
+ fun testIsVisitorInfo() {
+
+ // To short
+ assert(
+ VisitorAPI.isVisitorInfo(mutableListOf(
+ "§a§lVisitor Info",
+ "§7§oClick to view info about this visitor."
+ )) == false
+ )
+
+ // To long
+ assert(
+ VisitorAPI.isVisitorInfo(mutableListOf(
+ "§a§lVisitor Info",
+ "§7§oClick to view info about this visitor.",
+ "§7§oClick to view info about this visitor.",
+ "§7§oClick to view info about this visitor.",
+ "§7§oClick to view info about this visitor.",
+ )) == false
+ )
+
+ // Third line is §7Offers Accepted: §a
+ assert(
+ VisitorAPI.isVisitorInfo(mutableListOf(
+ "§a§lVisitor Info",
+ "§7§oClick to view info about this visitor.",
+ "§7§oClick to view info about this visitor.",
+ "§7Offers Accepted: §a",
+ )) == true
+ )
+ }
+} \ No newline at end of file
diff --git a/src/test/java/at/hannibal2/skyhanni/test/garden/VisitorListenerTest.kt b/src/test/java/at/hannibal2/skyhanni/test/garden/VisitorListenerTest.kt
new file mode 100644
index 000000000..13e96a019
--- /dev/null
+++ b/src/test/java/at/hannibal2/skyhanni/test/garden/VisitorListenerTest.kt
@@ -0,0 +1,83 @@
+package at.hannibal2.skyhanni.test.garden
+
+import at.hannibal2.skyhanni.events.TabListUpdateEvent
+import at.hannibal2.skyhanni.features.garden.GardenAPI
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorAPI
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorListener
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.mockkObject
+import io.mockk.verify
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+
+
+class VisitorListenerTest {
+ private lateinit var listener: VisitorListener
+ @BeforeEach
+ fun setUp() {
+ mockkObject(GardenAPI)
+ every { GardenAPI.inGarden() } returns true
+
+ mockkObject(LorenzUtils)
+ every { LorenzUtils.getPlayerName() } returns "ThePlayerName"
+
+ mockkObject(VisitorAPI)
+ every { VisitorAPI.addVisitor(any()) } returns true
+
+ listener = VisitorListener()
+ }
+
+ @Test
+ fun `onTablistUpdate it should add new visitors to the list`() {
+ listener.onTabListUpdate(
+ TabListUpdateEvent(mutableListOf(
+ "§b§lVisitors:",
+ "§cSpaceman",
+ "§cGrandma Wolf",
+ "ThePlayerName",
+ "Jacob",
+ "",
+ ))
+ )
+
+ verify { VisitorAPI.addVisitor("§fJacob") }
+ verify { VisitorAPI.addVisitor("§cSpaceman") }
+ verify { VisitorAPI.addVisitor("§cGrandma Wolf") }
+ }
+
+ @Test
+ fun `onTablistUpdate it should remove visitors from the list`() {
+ every { VisitorAPI.getVisitors() } returns listOf(
+ mockk { every { visitorName } returns "§fJacob" },
+ )
+
+ listener.onTabListUpdate(
+ TabListUpdateEvent(mutableListOf(
+ "§b§lVisitors:",
+ "",
+ ))
+ )
+
+ verify { VisitorAPI.removeVisitor("§fJacob") }
+ }
+
+ @Test
+ fun `onTablistUpdate it should not remove visitors if the timeout is not hit`() {
+ every { VisitorAPI.getVisitors() } returns listOf(
+ mockk { every { visitorName } returns "§fJacob" },
+ )
+
+ every { LorenzUtils.lastWorldSwitch } returns System.currentTimeMillis()
+
+ listener.onTabListUpdate(
+ TabListUpdateEvent(mutableListOf(
+ "§b§lVisitors:",
+ "",
+ ))
+ )
+
+ verify(exactly = 0) { VisitorAPI.removeVisitor("§fJacob") }
+ }
+} \ No newline at end of file
diff --git a/src/test/java/at/hannibal2/skyhanni/test/garden/VisitorToolTipParserTest.kt b/src/test/java/at/hannibal2/skyhanni/test/garden/VisitorToolTipParserTest.kt
new file mode 100644
index 000000000..9418b3db8
--- /dev/null
+++ b/src/test/java/at/hannibal2/skyhanni/test/garden/VisitorToolTipParserTest.kt
@@ -0,0 +1,65 @@
+package at.hannibal2.skyhanni.test.garden
+
+import at.hannibal2.skyhanni.config.features.GardenConfig
+import at.hannibal2.skyhanni.features.garden.visitor.VisitorTooltipParser
+import org.junit.jupiter.api.Test
+
+class VisitorToolTipParserTest {
+ private val lore = mutableListOf(
+ "§7Items Required:",
+ " §aEnchanted Hay Bale §8x28",
+ "",
+ "§7Rewards:",
+ " §8+§37.2k §7Farming XP",
+ " §8+§215 §7Garden Experience",
+ " §8+§c23 Copper",
+ " §8+§b10 Bits",
+ " §aJacob's Ticket",
+ " §9Flowering Bouquet",
+ "",
+ "§eClick to give!"
+ )
+
+ @Test
+ fun testParseItemsNeeded() {
+ val parsedData = VisitorTooltipParser.parse(lore, GardenConfig())
+ assert(parsedData.itemsNeeded.isNotEmpty()) {
+ "Visitor items needed is ${parsedData.itemsNeeded.count()} instead of 1"
+ }
+ assert(parsedData.itemsNeeded.get("§aEnchanted Hay Bale") == 28) {
+ "Visitor items needed does not contain '§aEnchanted Hay Bale'"
+ }
+ }
+
+ @Test
+ fun testParseRewards() {
+ val parsedData = VisitorTooltipParser.parse(lore, GardenConfig())
+ assert(parsedData.rewards.isNotEmpty()) {
+ "Visitor rewards is ${parsedData.rewards.count()} instead of 6"
+ }
+
+ val assertions = mutableMapOf(
+ "§7Farming XP" to 7200,
+ "§7Garden Experience" to 15,
+ "Copper" to 23,
+ "Bits" to 10,
+ "§aJacob's Ticket" to 1,
+ "§9Flowering Bouquet" to 1
+ )
+
+ for ((itemName, amount) in assertions) {
+ assert(parsedData.rewards.get(itemName) == amount) {
+ "Visitor rewards does not contain '$itemName' with amount '$amount'"
+ }
+ }
+ }
+
+ @Test
+ fun testParseCopper() {
+ val parsedData = VisitorTooltipParser.parse(lore, GardenConfig())
+ val copper = parsedData.rewards.get("Copper")
+ assert(copper == 23) {
+ "Visitor rewards does not contain 'Copper' with amount '23'"
+ }
+ }
+} \ No newline at end of file