From 95a4273511e2b3fccc7e022e7123923b9ff41ec0 Mon Sep 17 00:00:00 2001 From: Gage307 Date: Sun, 31 Aug 2025 13:10:52 -0400 Subject: [PATCH 1/4] fix(Construction2): Added firstRun so we know if we need to summon the butler after a break or for the first loop --- .../microbot/GeoffPlugins/construction2/Construction2Plugin.java | 1 + 1 file changed, 1 insertion(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Plugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Plugin.java index 741f9ba5f1e..32b9ea28e81 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Plugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Plugin.java @@ -46,6 +46,7 @@ protected void startUp() throws AWTException { if (overlayManager != null) { overlayManager.add(construction2Overlay); } + Construction2Script.firstRun = true; construction2Script.run(config); } From 5f781adee5e71f7494a1413aca4da4e942e0dba8 Mon Sep 17 00:00:00 2001 From: Gage307 Date: Sun, 31 Aug 2025 13:13:29 -0400 Subject: [PATCH 2/4] fix(Construction2): Added support for tileObjects and gameObjects as we need both for this script. Minor changes to butler, added enter dungeon to returnToTheHouse if we're building oak dungeon doors --- .../construction2/Construction2Script.java | 277 +++++++++++++----- 1 file changed, 203 insertions(+), 74 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Script.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Script.java index 11fc88d3e1f..80b71cf6212 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Script.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Script.java @@ -2,6 +2,7 @@ import net.runelite.api.*; import net.runelite.api.coords.WorldPoint; +import net.runelite.api.gameval.InterfaceID; import net.runelite.api.widgets.Widget; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.Script; @@ -16,6 +17,7 @@ import net.runelite.client.plugins.microbot.util.npc.Rs2NpcModel; import net.runelite.client.plugins.microbot.util.player.Rs2Player; import net.runelite.client.plugins.microbot.util.tabs.Rs2Tab; +import net.runelite.client.plugins.microbot.util.tile.Rs2Tile; import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; import net.runelite.client.plugins.microbot.util.widget.Rs2Widget; @@ -35,11 +37,15 @@ public class Construction2Script extends Script { private static final List MAHOGANY_TABLE = List.of(13298, 15298); private static final List MYTHICAL_CAPE_MOUNT = List.of(15394, 31986); - public GameObject getClosestTile(List objIDs) { + private static List objectIDs = List.of(0); + + public static boolean firstRun = false; + + public GameObject getClosestTileGameObject(List objIDs) { List objects = Rs2GameObject.getGameObjects(); GameObject closest = null; WorldPoint playerLocation = Rs2Player.getWorldLocation(); - + for (GameObject obj : objects) { if (objIDs.contains(obj.getId())) { if (closest == null || Rs2Walker.getDistanceBetween(playerLocation, obj.getWorldLocation()) < Rs2Walker.getDistanceBetween(playerLocation, closest.getWorldLocation())) { @@ -50,6 +56,21 @@ public GameObject getClosestTile(List objIDs) { return closest; } + public TileObject getClosestTileTileObject(List objIDs) { + List tileObjects = Rs2GameObject.getTileObjects(); + TileObject closest = null; + WorldPoint playerLocation = Rs2Player.getWorldLocation(); + + for (TileObject obj : tileObjects) { + if (objIDs.contains(obj.getId())) { + if (closest == null || Rs2Walker.getDistanceBetween(playerLocation, obj.getWorldLocation()) < Rs2Walker.getDistanceBetween(playerLocation, closest.getWorldLocation())) { + closest = obj; + } + } + } + return closest; + } + public Rs2NpcModel getButler() { return Rs2Npc.getNpc("Demon butler"); } @@ -133,17 +154,21 @@ public void grabPlanksWhileWeBuild(Construction2Config config, int actionDelay){ if(getButler() != null) { sleepUntil(()-> getButler() != null && getButler().isInteractingWithPlayer(), Rs2Random.between(750,1500)); if(!getButler().isInteractingWithPlayer()){ - if (Rs2Inventory.count(config.selectedMode().getPlankItemId()) <= Rs2Random.between(0, 18)) butler(config, actionDelay); + if (Rs2Inventory.count(config.selectedMode().getPlankItemId()) <= Rs2Random.between(10, 18)) butler(config, actionDelay); } else { butler(config, actionDelay); } + } else { + if(firstRun){ + callTheButler(); + if(getButler() != null) firstRun = false; + } } } private void calculateState(Construction2Config config) { boolean hasRequiredPlanks; NPC butler = getButler(); - List objectIDs = List.of(0); switch (config.selectedMode()) { case OAK_DUNGEON_DOOR: objectIDs = OAK_DUNGEON_DOOR; @@ -162,31 +187,62 @@ private void calculateState(Construction2Config config) { } if (workingTile == null) { - workingTile = getClosestTile(objectIDs).getWorldLocation(); + if(!objectIDs.equals(OAK_DUNGEON_DOOR)){ + workingTile = getClosestTileGameObject(objectIDs).getWorldLocation(); + } else { + workingTile = getClosestTileTileObject(objectIDs).getWorldLocation(); + } } - GameObject objOnWorkingTile = Rs2GameObject.getGameObject(workingTile); - if (objOnWorkingTile == null || !objectIDs.contains(objOnWorkingTile.getId())) { - // Find new working tile - workingTile = getClosestTile(objectIDs).getWorldLocation(); + GameObject objOnWorkingTile = null; + TileObject tileObjOnWorkingTile = null; + if(!objectIDs.equals(OAK_DUNGEON_DOOR)){ objOnWorkingTile = Rs2GameObject.getGameObject(workingTile); + } else { + tileObjOnWorkingTile = Rs2GameObject.getTileObject(workingTile); } - if (objOnWorkingTile.getId() == objectIDs.get(0)) { - state = Construction2State.Remove; - } else if (objOnWorkingTile.getId() == objectIDs.get(1) && hasRequiredPlanks) { - state = Construction2State.Build; - } else if (objOnWorkingTile.getId() == objectIDs.get(1) && butler != null) { - state = Construction2State.Butler; - } else if (!objectIDs.contains(objOnWorkingTile.getId())) { - state = Construction2State.Idle; - Microbot.getNotifier().notify("Looks like we are no longer in our house."); - returnToTheHouse(); + if (objOnWorkingTile == null && tileObjOnWorkingTile == null) { + // Find new working tile + if(!objectIDs.equals(OAK_DUNGEON_DOOR)){ + workingTile = getClosestTileGameObject(objectIDs).getWorldLocation(); + objOnWorkingTile = Rs2GameObject.getGameObject(workingTile); + } else { + workingTile = getClosestTileTileObject(objectIDs).getWorldLocation(); + tileObjOnWorkingTile = Rs2GameObject.getTileObject(workingTile); + } + } + + if(!objectIDs.equals(OAK_DUNGEON_DOOR)){ + if (objOnWorkingTile.getId() == objectIDs.get(0)) { + state = Construction2State.Remove; + } else if (objOnWorkingTile.getId() == objectIDs.get(1) && hasRequiredPlanks) { + state = Construction2State.Build; + } else if (objOnWorkingTile.getId() == objectIDs.get(1)) { + state = Construction2State.Butler; + } else if (!objectIDs.contains(objOnWorkingTile.getId())) { + state = Construction2State.Idle; + Microbot.getNotifier().notify("Looks like we are no longer in our house."); + returnToTheHouse(objectIDs); + } + } else { + if (tileObjOnWorkingTile.getId() == objectIDs.get(0)) { + state = Construction2State.Remove; + } else if (tileObjOnWorkingTile.getId() == objectIDs.get(1) && hasRequiredPlanks) { + state = Construction2State.Build; + } else if (tileObjOnWorkingTile.getId() == objectIDs.get(1)) { + state = Construction2State.Butler; + } else if (!objectIDs.contains(tileObjOnWorkingTile.getId())) { + state = Construction2State.Idle; + Microbot.getNotifier().notify("Looks like we are no longer in our house."); + returnToTheHouse(objectIDs); + } } } - private void returnToTheHouse(){ + private void returnToTheHouse(List objIDs){ GameObject housePortal = Rs2GameObject.getGameObject("Portal"); + firstRun = true; //we're returning from break we'll need to recall the butler if(housePortal != null){ if(Rs2GameObject.interact(housePortal, "Build mode")){ sleepUntil(()-> Rs2Player.getWorldLocation() != null @@ -194,6 +250,15 @@ private void returnToTheHouse(){ && Rs2Player.getWorldLocation().getRegionY() == 89, Rs2Random.between(10000,20000)); sleep(2000,5000); } + if(objIDs.equals(OAK_DUNGEON_DOOR)){ + // We need to enter the dungeon + if(Rs2GameObject.getGameObject("Dungeon entrance", true) !=null){ + if(Rs2GameObject.interact("Dungeon entrance", "Enter", true)){ + sleepUntil(()-> Rs2Player.isMoving(), Rs2Random.between(500,1000)); + sleepUntil(()-> !Rs2Player.isMoving(), Rs2Random.between(10000,15000)); + } + } + } } else { Microbot.getNotifier().notify("Can't find the house portal!"); shutdown(); @@ -201,75 +266,123 @@ private void returnToTheHouse(){ } private void buildSpace(Construction2Config config, int actionDelay) { - GameObject space = Rs2GameObject.getGameObject(workingTile); - int spaceId = space != null ? space.getId() : -1; - char buildKey = '1'; - - switch (config.selectedMode()) { - case OAK_DUNGEON_DOOR: - buildKey = '1'; - break; - case OAK_LARDER: - buildKey = '2'; - break; - case MAHOGANY_TABLE: - buildKey = '6'; - break; - // case MYTHICAL_CAPE: - // buildKey = '4'; - // break; - default: - return; - } + GameObject gameObjSpace = Rs2GameObject.getGameObject(workingTile); + TileObject tileObjSpace = Rs2GameObject.getTileObject(workingTile); + if(!objectIDs.equals(OAK_DUNGEON_DOOR)){ + GameObject space = gameObjSpace; + int spaceId = space != null ? space.getId() : -1; + char buildKey = '1'; + + switch (config.selectedMode()) { + case OAK_DUNGEON_DOOR: + buildKey = '1'; + break; + case OAK_LARDER: + buildKey = '2'; + break; + case MAHOGANY_TABLE: + buildKey = '6'; + break; + // case MYTHICAL_CAPE: + // buildKey = '4'; + // break; + default: + return; + } - if (space == null) return; - if (Rs2GameObject.interact(space, "Build")) { - System.out.println("Interacted with build space: " + space.getId()); - sleepUntilOnClientThread(this::hasFurnitureInterfaceOpen, 2500); - System.out.println("Pressing key: " + buildKey); - Rs2Keyboard.keyPress(buildKey); // Ensure this is the correct key for the selected build option - sleepUntilOnClientThread(() -> spaceId != space.getId(), 2500); - System.out.println("Built object: " + config.selectedMode()); + if (space == null) return; + if (Rs2GameObject.interact(space, "Build")) { + System.out.println("Interacted with build space: " + space.getId()); + sleepUntilOnClientThread(this::hasFurnitureInterfaceOpen, 2500); + System.out.println("Pressing key: " + buildKey); + Rs2Keyboard.keyPress(buildKey); // Ensure this is the correct key for the selected build option + sleepUntilOnClientThread(() -> spaceId != space.getId(), 2500); + System.out.println("Built object: " + config.selectedMode()); + } else { + System.out.println("Failed to interact with build space: " + space.getId()); + } } else { - System.out.println("Failed to interact with build space: " + space.getId()); + TileObject space = tileObjSpace; + int spaceId = space != null ? space.getId() : -1; + char buildKey = '1'; + + switch (config.selectedMode()) { + case OAK_DUNGEON_DOOR: + buildKey = '1'; + break; + case OAK_LARDER: + buildKey = '2'; + break; + case MAHOGANY_TABLE: + buildKey = '6'; + break; + // case MYTHICAL_CAPE: + // buildKey = '4'; + // break; + default: + return; + } + + if (space == null) return; + if (Rs2GameObject.interact(space, "Build")) { + System.out.println("Interacted with build space: " + space.getId()); + sleepUntilOnClientThread(this::hasFurnitureInterfaceOpen, 2500); + System.out.println("Pressing key: " + buildKey); + Rs2Keyboard.keyPress(buildKey); // Ensure this is the correct key for the selected build option + sleepUntilOnClientThread(() -> spaceId != space.getId(), 2500); + System.out.println("Built object: " + config.selectedMode()); + } else { + System.out.println("Failed to interact with build space: " + space.getId()); + } } } private void removeSpace(Construction2Config config, int actionDelay) { - GameObject builtObject = Rs2GameObject.getGameObject(workingTile); - int spaceId = builtObject != null ? builtObject.getId() : -1; - - if (builtObject == null) return; - if(builtObject.getId() == 15328 || builtObject.getId() == 15403 || builtObject.getId() == 15298 || builtObject.getId() == 31986) return; - - if (Rs2GameObject.interact(builtObject, "Remove")) { - System.out.println("Interacted with remove option: " + builtObject.getId()); - sleepUntilOnClientThread(() -> hasRemoveInterfaceOpen(config), 2500); - Rs2Keyboard.keyPress('1'); - sleepUntilOnClientThread(() -> spaceId != builtObject.getId(), 2500); - System.out.println("Removed object: " + config.selectedMode()); + GameObject gameObjSpace = Rs2GameObject.getGameObject(workingTile); + TileObject tileObjSpace = Rs2GameObject.getTileObject(workingTile); + + if(!objectIDs.equals(OAK_DUNGEON_DOOR)){ + GameObject builtObject = gameObjSpace; + int spaceId = builtObject != null ? builtObject.getId() : -1; + + if (builtObject == null) return; + if(builtObject.getId() == 15328 || builtObject.getId() == 15403 || builtObject.getId() == 15298 || builtObject.getId() == 31986) return; + + if (Rs2GameObject.interact(builtObject, "Remove")) { + System.out.println("Interacted with remove option: " + builtObject.getId()); + sleepUntilOnClientThread(() -> hasRemoveInterfaceOpen(config), 2500); + Rs2Keyboard.keyPress('1'); + sleepUntilOnClientThread(() -> spaceId != builtObject.getId(), 2500); + System.out.println("Removed object: " + config.selectedMode()); + } else { + System.out.println("Failed to interact with remove option: " + builtObject.getId()); + } } else { - System.out.println("Failed to interact with remove option: " + builtObject.getId()); + TileObject builtObject = tileObjSpace; + int spaceId = builtObject != null ? builtObject.getId() : -1; + + if (builtObject == null) return; + if(builtObject.getId() == 15328 || builtObject.getId() == 15403 || builtObject.getId() == 15298 || builtObject.getId() == 31986) return; + + if (Rs2GameObject.interact(builtObject, "Remove")) { + System.out.println("Interacted with remove option: " + builtObject.getId()); + sleepUntilOnClientThread(() -> hasRemoveInterfaceOpen(config), 2500); + Rs2Keyboard.keyPress('1'); + sleepUntilOnClientThread(() -> spaceId != builtObject.getId(), 2500); + System.out.println("Removed object: " + config.selectedMode()); + } else { + System.out.println("Failed to interact with remove option: " + builtObject.getId()); + } } } private void butler(Construction2Config config, int actionDelay) { var butler = getButler(); - if (butler == null) return; + if(butler == null) callTheButler(); boolean butlerIsInteracting = butler.isInteractingWithPlayer(); if (!butlerIsInteracting) { - Rs2Tab.switchTo(InterfaceTab.SETTINGS); - - Widget houseOptionWidget = Rs2Widget.getWidget(7602207); - sleepUntil(()-> houseOptionWidget != null, Rs2Random.between(2000,5000)); - if (houseOptionWidget != null) Rs2Widget.clickWidget(houseOptionWidget); - - Widget callServantWidget = Rs2Widget.getWidget(24248342); - sleepUntil(()-> callServantWidget != null, Rs2Random.between(2000,5000)); - if (callServantWidget != null) Rs2Widget.clickWidget(callServantWidget); - - sleepUntil(()-> Rs2Dialogue.isInDialogue(), Rs2Random.between(2000,5000)); + callTheButler(); } if (Rs2Dialogue.isInDialogue() || Rs2Npc.interact(butler, "Talk-to")) { @@ -300,6 +413,22 @@ private void butler(Construction2Config config, int actionDelay) { } } + public void callTheButler(){ + Rs2Tab.switchTo(InterfaceTab.SETTINGS); + + Widget houseOptionWidget = Rs2Widget.getWidget(7602207); + sleepUntil(()-> houseOptionWidget != null, Rs2Random.between(2000,5000)); + if (houseOptionWidget != null) Rs2Widget.clickWidget(houseOptionWidget); + + sleep(1000,2000); // allow the client to load the widget + + Widget callServantWidget = Rs2Widget.getWidget(370,22); + sleepUntil(()-> callServantWidget != null, Rs2Random.between(2000,5000)); + if (callServantWidget != null) Rs2Widget.clickWidget(callServantWidget); + + sleepUntil(()-> Rs2Dialogue.isInDialogue(), Rs2Random.between(2000,5000)); + } + private boolean hasRemoveInterfaceOpen(Construction2Config config) { switch (config.selectedMode()) { case OAK_DUNGEON_DOOR: From 56b03de91303077d467e9431baba66b45794924d Mon Sep 17 00:00:00 2001 From: Gage307 Date: Wed, 24 Sep 2025 12:55:10 -0400 Subject: [PATCH 3/4] fix(Rs2GroundItem): Prevents Rs2GroundItem from spam clicking an item on the ground if our inventory is full. --- .../client/plugins/microbot/util/grounditem/Rs2GroundItem.java | 1 + 1 file changed, 1 insertion(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/grounditem/Rs2GroundItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/grounditem/Rs2GroundItem.java index 10f66852f6e..b23ec7eec98 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/grounditem/Rs2GroundItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/grounditem/Rs2GroundItem.java @@ -274,6 +274,7 @@ public static boolean coreLoot(GroundItem groundItem) { final int quantFinal = quantity; return runWhilePaused(() -> { for (int i = 0; i < quantFinal; i++) { + if(Rs2Inventory.isFull()) break; waitForGroundItemDespawn(() -> interact(groundItem), groundItem); } return true; From b7f0bdc63c121ca44bd4a9e602752b1d497fd91a Mon Sep 17 00:00:00 2001 From: Gage307 Date: Wed, 24 Sep 2025 12:59:11 -0400 Subject: [PATCH 4/4] fix(Rs2GrandExchange): Added a small randomized sleep. This will allow some time for the widget to load. This is needed because if we have previously searched the item the widget is all ready visible. Causing the script to misclick cyclically. --- .../plugins/microbot/util/grandexchange/Rs2GrandExchange.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/grandexchange/Rs2GrandExchange.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/grandexchange/Rs2GrandExchange.java index 67008400692..ba2dff69df9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/grandexchange/Rs2GrandExchange.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/grandexchange/Rs2GrandExchange.java @@ -235,6 +235,8 @@ public static boolean processOffer(GrandExchangeRequest request) { } Rs2Keyboard.typeString(request.getItemName()); + sleep(500,800); //allow the widget to load + if (!Rs2Widget.sleepUntilHasWidgetText(searchName, 162, 43, false, 5000)) break; sleepUntil(() -> getSearchResultWidget(request.getItemName(), request.isExact()) != null, 2200);