From a9a1af0f90cd277a581ff7c1962716de663c07b5 Mon Sep 17 00:00:00 2001 From: krulvis Date: Mon, 8 Sep 2025 22:18:40 +0200 Subject: [PATCH 1/4] fix(SpiritTree): Add and remove POH spirit tree from cache --- .../util/cache/Rs2SpiritTreeCache.java | 8 ++ .../farming/SpiritTreeUpdateStrategy.java | 82 +++++++------------ .../microbot/util/farming/SpiritTree.java | 17 +--- 3 files changed, 41 insertions(+), 66 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/Rs2SpiritTreeCache.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/Rs2SpiritTreeCache.java index ed2b294e416..128cac2318f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/Rs2SpiritTreeCache.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/Rs2SpiritTreeCache.java @@ -590,6 +590,14 @@ public void onGameObjectSpawned(net.runelite.api.events.GameObjectSpawned event) getInstance().handleEvent(event); } + /** + * Handle GameObjectSpawned event and delegate to update strategy. + */ + @Subscribe + public void onGameObjectDespawned(net.runelite.api.events.GameObjectDespawned event) { + getInstance().handleEvent(event); + } + /** * Handle game state changes for cache lifecycle management (unchanged). */ diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/strategy/farming/SpiritTreeUpdateStrategy.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/strategy/farming/SpiritTreeUpdateStrategy.java index c01222d71db..31c8bf1181f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/strategy/farming/SpiritTreeUpdateStrategy.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/strategy/farming/SpiritTreeUpdateStrategy.java @@ -6,16 +6,13 @@ import net.runelite.api.GameObject; import net.runelite.api.Skill; import net.runelite.api.coords.WorldPoint; -import net.runelite.api.events.GameStateChanged; -import net.runelite.api.events.VarbitChanged; -import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.events.*; import net.runelite.api.gameval.ObjectID; import net.runelite.api.gameval.VarbitID; import net.runelite.api.widgets.Widget; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.questhelper.helpers.mischelpers.farmruns.CropState; import net.runelite.client.plugins.microbot.util.cache.Rs2Cache; -import net.runelite.client.plugins.microbot.util.cache.Rs2ObjectCache; import net.runelite.client.plugins.microbot.util.cache.model.SpiritTreeData; import net.runelite.client.plugins.microbot.util.cache.strategy.CacheOperations; import net.runelite.client.plugins.microbot.util.cache.strategy.CacheUpdateStrategy; @@ -63,13 +60,18 @@ public class SpiritTreeUpdateStrategy implements CacheUpdateStrategy cache) { try { - if (event instanceof WidgetLoaded) { handleWidgetLoaded((WidgetLoaded) event, cache); } else if (event instanceof VarbitChanged) { handleVarbitChanged((VarbitChanged) event, cache); } else if (event instanceof GameStateChanged) { handleGameStateChanged((GameStateChanged) event, cache); + } else if (event instanceof GameObjectSpawned) { + GameObject go = ((GameObjectSpawned) event).getGameObject(); + handleGameObjectChange(go, true, cache); + } else if (event instanceof GameObjectDespawned) { + GameObject go = ((GameObjectDespawned) event).getGameObject(); + handleGameObjectChange(go, false, cache); } } catch (Exception e) { log.error("Error handling event in SpiritTreeUpdateStrategy: {}", e.getMessage(), e); @@ -78,7 +80,7 @@ public void handleEvent(Object event, CacheOperations[] getHandledEventTypes() { - return new Class[]{WidgetLoaded.class, VarbitChanged.class, GameStateChanged.class}; + return new Class[]{WidgetLoaded.class, VarbitChanged.class, GameStateChanged.class, GameObjectSpawned.class, GameObjectDespawned.class}; } /** * Handle widget loaded events to detect spirit tree widget opening @@ -127,13 +129,29 @@ private void handleVarbitChanged(VarbitChanged event, CacheOperations cache) { + if(gameObject == null){ + return; } - + Arrays.stream(SpiritTree.values()).filter(tree -> tree.getType() == SpiritTree.SpiritTreeType.POH).forEach(tree -> { + if (tree.getObjectId().contains(gameObject.getId())) { + log.info("Found spirit tree object {} for POH spirit tree {}, {} to cache", gameObject.getId(), tree.name(), spawned ? "added" : "removed"); + SpiritTreeData newData = new SpiritTreeData( + tree, + spawned ? CropState.HARVESTABLE: CropState.DEAD, + spawned, + gameObject.getWorldLocation(), + false, // Not detected via widget + true // Detected via nearby tree if present + ); + cache.put(tree, newData); + } + }); + } - + /** * Handle GameStateChanged events to detect POH region changes and validate spirit tree presence */ @@ -148,53 +166,11 @@ private void handleGameStateChanged(GameStateChanged event, CacheOperations cache) { - try { - WorldPoint playerLocation = getPlayerLocation(); - if (playerLocation == null|| !Rs2Cache.isInPOH()) { - log.warn("Cannot determine player location for POH spirit tree detection"); - return; - } - // Stream over all SpiritTree values, filter for POH type, and update cache for each - Arrays.stream(SpiritTree.values()) - .filter(tree -> tree.getType() == SpiritTree.SpiritTreeType.POH) - .forEach(tree -> { - // Check for POH spirit tree object nearby using the objectId defined in the enum - boolean pohSpiritTreePresent = Rs2ObjectCache.getGameObjects() - .anyMatch(obj -> tree.getObjectId().contains(obj.getId()) && - obj.getWorldLocation().distanceTo(playerLocation) <= 40); - - SpiritTreeData newData = new SpiritTreeData( - tree, - pohSpiritTreePresent ? CropState.HARVESTABLE : CropState.DEAD, - pohSpiritTreePresent, - playerLocation, - false, // Not detected via widget - pohSpiritTreePresent // Detected via nearby tree if present - ); - - cache.put(tree, newData); - log.debug("Updated POH spirit tree cache for {} - {}", tree.name(), pohSpiritTreePresent ? "tree present and available" : "no tree present"); - }); - } catch (Exception e) { - log.error("Error updating POH spirit tree cache: {}", e.getMessage(), e); - } - } - /** * Update cache from spirit tree widget data */ diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/farming/SpiritTree.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/farming/SpiritTree.java index b78c9700be7..8d263fd29e2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/farming/SpiritTree.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/farming/SpiritTree.java @@ -15,6 +15,7 @@ import net.runelite.client.plugins.microbot.questhelper.helpers.mischelpers.farmruns.CropState; import net.runelite.client.plugins.microbot.questhelper.helpers.mischelpers.farmruns.FarmingPatch; import net.runelite.client.plugins.microbot.util.cache.Rs2SpiritTreeCache; +import net.runelite.client.plugins.microbot.util.gameobject.Rs2GameObject; import net.runelite.client.plugins.microbot.util.misc.Rs2UiHelper; import net.runelite.client.plugins.microbot.util.player.Rs2Player; import net.runelite.client.plugins.microbot.util.poh.PohTeleports; @@ -187,7 +188,7 @@ public enum SpiritTree { null, // Location is dynamic based on player's house SpiritTreeType.POH, List.of(Quest.TREE_GNOME_VILLAGE, Quest.THE_GRAND_TREE), - 75, // Requires 75 Construction + 0, // Requires 75 Construction VarbitID.POH_SPIRIT_TREE_UPROOTED, //TODO Must be checked if correct POH Spirit Tree varbit List.of(ObjectID.POH_SPIRIT_TREE), // TODO Must be checked if correct - POH Spirit Tree object ID // we must update it. here are also variations for it.. leauge skins, christmas skins (), etc new int[] {-1}, // Region must be player's own house @@ -199,9 +200,9 @@ public enum SpiritTree { null, // Location is dynamic based on player's house SpiritTreeType.POH, List.of(Quest.TREE_GNOME_VILLAGE, Quest.THE_GRAND_TREE, Quest.FAIRYTALE_II__CURE_A_QUEEN), - 75, // Requires 75 Construction + 0, // Requires 75 Construction VarbitID.POH_SPIRIT_TREE_UPROOTED, // TODO Must be checkedPOH Spirit Tree varbit (combined tree/ring uses same varbit) - List.of(ObjectID.POH_SPIRIT_RING), // TODO Must be checked Spirit Ring object ID // we must update it. here are also variations for it.. leauge skins + Rs2GameObject.getObjectIdsByName("poh_spirit_ring"), new int[] {-1}, // Region must be player's own house "Your house" ); @@ -249,16 +250,6 @@ public boolean isAvailableForTravel() { return isPatchHealthyAndGrown(); } - // Check construction level for POH trees - if (type == SpiritTreeType.POH) { - if (Rs2Player.getRealSkillLevel(Skill.CONSTRUCTION) < requiredSkillLevel || !Rs2Farming.hasRequiredFarmingLevel(75)) { - return false; - } - - // For POH trees, check if they are built and player is in their house - return isPOHTreeAvailable(); - } - // Built-in trees are always available if quest requirements are met return true; } From 5dec5e4ddc9f0573460818a5d9265dc4f0487cb9 Mon Sep 17 00:00:00 2001 From: krulvis Date: Mon, 8 Sep 2025 22:36:08 +0200 Subject: [PATCH 2/4] chore(SpiritTree): Add docstring --- .../farming/SpiritTreeUpdateStrategy.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/strategy/farming/SpiritTreeUpdateStrategy.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/strategy/farming/SpiritTreeUpdateStrategy.java index 31c8bf1181f..0c47a9b9d82 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/strategy/farming/SpiritTreeUpdateStrategy.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/strategy/farming/SpiritTreeUpdateStrategy.java @@ -56,7 +56,12 @@ public class SpiritTreeUpdateStrategy implements CacheUpdateStrategy cache) { try { @@ -77,11 +82,15 @@ public void handleEvent(Object event, CacheOperations[] getHandledEventTypes() { return new Class[]{WidgetLoaded.class, VarbitChanged.class, GameStateChanged.class, GameObjectSpawned.class, GameObjectDespawned.class}; - } + } + /** * Handle widget loaded events to detect spirit tree widget opening */ @@ -131,6 +140,15 @@ private void handleVarbitChanged(VarbitChanged event, CacheOperations cache) { if(gameObject == null){ return; From c7f09d6261ae5cbc7e3d6f5e21ca573ed31e2d40 Mon Sep 17 00:00:00 2001 From: Krulvis Date: Tue, 9 Sep 2025 09:26:48 +0200 Subject: [PATCH 3/4] fix(WebWalker): Set requiredLevels for POH spirit trees to 75 and 95 respectively --- .../client/plugins/microbot/util/farming/SpiritTree.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/farming/SpiritTree.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/farming/SpiritTree.java index 8d263fd29e2..02cc212d506 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/farming/SpiritTree.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/farming/SpiritTree.java @@ -188,7 +188,7 @@ public enum SpiritTree { null, // Location is dynamic based on player's house SpiritTreeType.POH, List.of(Quest.TREE_GNOME_VILLAGE, Quest.THE_GRAND_TREE), - 0, // Requires 75 Construction + 75, // Requires 75 Construction VarbitID.POH_SPIRIT_TREE_UPROOTED, //TODO Must be checked if correct POH Spirit Tree varbit List.of(ObjectID.POH_SPIRIT_TREE), // TODO Must be checked if correct - POH Spirit Tree object ID // we must update it. here are also variations for it.. leauge skins, christmas skins (), etc new int[] {-1}, // Region must be player's own house @@ -200,7 +200,7 @@ public enum SpiritTree { null, // Location is dynamic based on player's house SpiritTreeType.POH, List.of(Quest.TREE_GNOME_VILLAGE, Quest.THE_GRAND_TREE, Quest.FAIRYTALE_II__CURE_A_QUEEN), - 0, // Requires 75 Construction + 95, // Requires 95 Construction VarbitID.POH_SPIRIT_TREE_UPROOTED, // TODO Must be checkedPOH Spirit Tree varbit (combined tree/ring uses same varbit) Rs2GameObject.getObjectIdsByName("poh_spirit_ring"), new int[] {-1}, // Region must be player's own house From 07a1f3c5cd041d3617add64342cf216a99051832 Mon Sep 17 00:00:00 2001 From: Krulvis Date: Tue, 9 Sep 2025 09:31:14 +0200 Subject: [PATCH 4/4] fix(WebWalker): Set default cache to check all requirements --- .../microbot/util/cache/Rs2SpiritTreeCache.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/Rs2SpiritTreeCache.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/Rs2SpiritTreeCache.java index 128cac2318f..01319ed81b4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/Rs2SpiritTreeCache.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/cache/Rs2SpiritTreeCache.java @@ -97,19 +97,14 @@ public static SpiritTreeData getSpiritTreeData(SpiritTree spiritTree) { return getInstance().get(spiritTree, () -> { try { // Determine initial state based on spiritTree type - CropState cropState = null; - boolean availableForTravel = false; + CropState cropState = CropState.HARVESTABLE; + boolean availableForTravel = spiritTree.hasQuestRequirements(); - if (spiritTree.getType() == SpiritTree.SpiritTreeType.BUILT_IN) { - // Built-in trees are available if quest requirements are met - availableForTravel = spiritTree.hasQuestRequirements(); - } else if (spiritTree.getType() == SpiritTree.SpiritTreeType.FARMABLE) { - // Farmable trees - get current farming state + if (spiritTree.getType() == SpiritTree.SpiritTreeType.FARMABLE) { cropState = spiritTree.getPatchState(); - availableForTravel = spiritTree.isAvailableForTravel(); - }else if (spiritTree.getType() == SpiritTree.SpiritTreeType.POH) { - // Other types (e.g. POH spirit trees) - use default availability - availableForTravel = spiritTree.isAvailableForTravel(); + availableForTravel &= spiritTree.isAvailableForTravel(); + } else if (spiritTree.getType() == SpiritTree.SpiritTreeType.POH) { + availableForTravel &= spiritTree.hasLevelRequirement(); } log.debug("Initial spirit tree data for {}: \n\tcropState={}, available={}",