diff --git a/src/main/java/net/runelite/client/plugins/microbot/animatedarmour/AnimatedArmourPlugin.java b/src/main/java/net/runelite/client/plugins/microbot/animatedarmour/AnimatedArmourPlugin.java index 7d01a5c0a7..644aed7d3e 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/animatedarmour/AnimatedArmourPlugin.java +++ b/src/main/java/net/runelite/client/plugins/microbot/animatedarmour/AnimatedArmourPlugin.java @@ -26,7 +26,7 @@ @Slf4j public class AnimatedArmourPlugin extends Plugin { - static final String version = "1.0.1"; + static final String version = "1.0.2"; @Inject private AnimatedArmourConfig config; diff --git a/src/main/java/net/runelite/client/plugins/microbot/animatedarmour/AnimatedArmourScript.java b/src/main/java/net/runelite/client/plugins/microbot/animatedarmour/AnimatedArmourScript.java index a1944a0ccb..ae10637d16 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/animatedarmour/AnimatedArmourScript.java +++ b/src/main/java/net/runelite/client/plugins/microbot/animatedarmour/AnimatedArmourScript.java @@ -1,11 +1,13 @@ package net.runelite.client.plugins.microbot.animatedarmour; +import net.runelite.api.Skill; import net.runelite.api.coords.WorldPoint; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.Script; import net.runelite.client.plugins.microbot.api.tileobject.models.Rs2TileObjectModel; import net.runelite.client.plugins.microbot.util.bank.Rs2Bank; import net.runelite.client.plugins.microbot.util.bank.enums.BankLocation; +import net.runelite.client.plugins.microbot.util.gameobject.Rs2GameObject; import net.runelite.client.plugins.microbot.util.grounditem.LootingParameters; import net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem; import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; @@ -20,6 +22,16 @@ public class AnimatedArmourScript extends Script { public static boolean test = false; + // Armoury entrance double-door (plane 0). From the shortest-path transport data: + // 2855 3546 0 <-> 2855 3545 0 (Open;Door;24309) + // 2854 3546 0 <-> 2854 3545 0 (Open;Door;24306) + // These are already in the walker's transport graph, so pathfinding opens them + // automatically; the explicit interact below is only a fallback when the walker + // stalls on the wrong side of the door. + private static final int[] ARMOURY_DOOR_IDS = {24309, 24306}; + // Armour stand object tile (unchanged from the original plugin). + private static final WorldPoint ARMOUR_STAND_TILE = new WorldPoint(2851, 3536, 0); + public boolean run(AnimatedArmourConfig config) { Microbot.enableAutoRunOn = false; @@ -32,17 +44,25 @@ public boolean run(AnimatedArmourConfig config) { boolean hasArmorPieces = Rs2Inventory.contains(item -> item.getName().contains("platebody")) && Rs2Inventory.contains(item -> item.getName().contains("platelegs")) && Rs2Inventory.contains(item -> item.getName().contains("full helm")); - if (Rs2Inventory.getInventoryFood().isEmpty() && config.foodAmount() > 0) { - Rs2Bank.walkToBank(BankLocation.WARRIORS_GUILD); - Rs2Bank.openBank(); - Rs2Bank.withdrawX(config.food().getId(), config.foodAmount()); - Rs2Walker.walkTo(2855, 3540, 0); - } + if (hasArmorPieces) { - animateArmor(); + // Never bank mid-kill. Only restock food before animating the next + // suit, when fewer than two food remain. + if (Rs2Inventory.getInventoryFood().size() < 2 && config.foodAmount() > 0) { + bankForFood(config); + } else { + animateArmor(config); + } } else { - Rs2Player.eatAt(Rs2Random.randomGaussian(30, 3)); - loot(); + Rs2Player.eatAt(Rs2Random.randomGaussian(60, 5)); + // Emergency only: if we ran dry mid-fight and are about to die, + // abandon the kill and bank rather than dying. + if (Rs2Inventory.getInventoryFood().isEmpty() && config.foodAmount() > 0 + && Rs2Player.getHealthPercentage() <= Rs2Random.randomGaussian(20, 3)) { + bankForFood(config); + } else { + loot(); + } } long endTime = System.currentTimeMillis(); @@ -50,22 +70,69 @@ public boolean run(AnimatedArmourConfig config) { System.out.println("Total time for loop " + totalTime); } catch (Exception ex) { + // A stop/restart can interrupt a client-thread call mid-flight (e.g. the + // door-open fallback). Restore the flag and exit quietly instead of logging + // a misleading ERROR stack trace. + if (ex instanceof InterruptedException || ex.getCause() instanceof InterruptedException) { + Thread.currentThread().interrupt(); + return; + } Microbot.logStackTrace(this.getClass().getSimpleName(), ex); } }, 0, 1000, TimeUnit.MILLISECONDS); return true; } - public void animateArmor() { - WorldPoint armorStandLocation = new WorldPoint(2851, 3536, 0); - Rs2TileObjectModel armorStand = Microbot.getRs2TileObjectCache().query().within(armorStandLocation, 0).nearest(); + private void bankForFood(AnimatedArmourConfig config) { + Rs2Bank.walkToBank(BankLocation.WARRIORS_GUILD); + Rs2Bank.openBank(); + Rs2Bank.withdrawX(config.food().getId(), config.foodAmount()); + Rs2Bank.closeBank(); + walkToArmourRoom(); + } + + private void walkToArmourRoom() { + // Walk with a distance tolerance instead of demanding the exact tile -- the + // exact-tile invocation is what made the short hop slow and prone to re-pathing. + Rs2Walker.walkTo(ARMOUR_STAND_TILE, 4); + // Fallback: if we stalled on the far side of the door, click it directly. The + // door is located by id once it has loaded, so no approach tile is hardcoded. + WorldPoint loc = Rs2Player.getWorldLocation(); + if (loc != null && loc.distanceTo(ARMOUR_STAND_TILE) > 4) { + if (Rs2GameObject.interact(ARMOURY_DOOR_IDS, "Open")) { + sleepUntil(() -> !Rs2Player.isMoving(), 3000); + Rs2Walker.walkTo(ARMOUR_STAND_TILE, 4); + } + } + } + + public void animateArmor(AnimatedArmourConfig config) { + // Make sure we actually reached the stand before trying to target it. + WorldPoint loc = Rs2Player.getWorldLocation(); + if (loc == null || loc.distanceTo(ARMOUR_STAND_TILE) > 8) { + walkToArmourRoom(); + return; + } + Rs2TileObjectModel armorStand = Microbot.getRs2TileObjectCache().query().within(ARMOUR_STAND_TILE, 0).nearest(); if (armorStand != null) { armorStand.click(); + // Eat one tick later, never on the same tick as the animate click (which + // would cancel it). This uses the idle animate delay to top up HP, but + // only when it won't be wasted (missing HP >= the food's heal amount). + sleepUntilNextTick(); + eatDuringAnimateIfEfficient(config); Rs2Player.waitForAnimation(); } - } + private void eatDuringAnimateIfEfficient(AnimatedArmourConfig config) { + if (Rs2Inventory.getInventoryFood().isEmpty()) return; + int missing = Rs2Player.getRealSkillLevel(Skill.HITPOINTS) + - Rs2Player.getBoostedSkillLevel(Skill.HITPOINTS); + if (missing >= config.food().getHeal()) { + Rs2Player.useFood(); + } + } public void loot() { LootingParameters valueParams = new LootingParameters( @@ -86,4 +153,4 @@ public void loot() { public void shutdown() { super.shutdown(); } -} \ No newline at end of file +} diff --git a/src/main/java/net/runelite/client/plugins/microbot/natewinemaker/WinePlugin.java b/src/main/java/net/runelite/client/plugins/microbot/natewinemaker/WinePlugin.java index 1d1408b20d..8903cc1c87 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/natewinemaker/WinePlugin.java +++ b/src/main/java/net/runelite/client/plugins/microbot/natewinemaker/WinePlugin.java @@ -30,7 +30,7 @@ ) @Slf4j public class WinePlugin extends Plugin { - public final static String version = "1.1.1"; + public final static String version = "1.2.0"; @Inject private Client client; diff --git a/src/main/java/net/runelite/client/plugins/microbot/natewinemaker/WineScript.java b/src/main/java/net/runelite/client/plugins/microbot/natewinemaker/WineScript.java index 95ce4c16ad..833c94151d 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/natewinemaker/WineScript.java +++ b/src/main/java/net/runelite/client/plugins/microbot/natewinemaker/WineScript.java @@ -3,6 +3,8 @@ import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.Script; +import net.runelite.client.plugins.microbot.util.antiban.Rs2Antiban; +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; import net.runelite.client.plugins.microbot.util.bank.Rs2Bank; import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; import net.runelite.client.plugins.microbot.util.keyboard.Rs2Keyboard; @@ -14,14 +16,30 @@ public class WineScript extends Script { public boolean run(WineConfig config) { + // Apply the cooking template as a baseline, then overlay the user's saved + // antiban panel settings so anything toggled there wins over the template. + Rs2Antiban.resetAntibanSettings(); + Rs2Antiban.antibanSetupTemplates.applyCookingSetup(); + Rs2AntibanSettings.moveMouseRandomly = true; + Rs2AntibanSettings.loadFromProfile(); mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { try { if (!super.run()) return; if (!Microbot.isLoggedIn()) return; + if (Rs2AntibanSettings.actionCooldownActive) return; + if (Rs2AntibanSettings.microBreakActive) return; if (Rs2Inventory.count("grapes") > 0 && (Rs2Inventory.count("jug of water") > 0)) { Rs2Inventory.combine("jug of water", "grapes"); sleepUntil(() -> Rs2Widget.getWidget(17694734) != null); Rs2Keyboard.keyPress('1'); + // Trigger the cooldown (incl. mouse off screen / random moves) as soon as + // the batch starts crafting, like a human looking away mid-batch, rather + // than after the last wine finishes. + Rs2Inventory.waitForInventoryChanges(3000); + Rs2Antiban.actionCooldown(); + if (Rs2AntibanSettings.takeMicroBreaks) { + Rs2Antiban.takeMicroBreakByChance(); + } sleepUntil(() -> !Rs2Inventory.hasItem("jug of water"),25000); } else { bank(); @@ -37,21 +55,30 @@ private void bank(){ Rs2Bank.openBank(); if(Rs2Bank.isOpen()){ Rs2Bank.depositAll(); - if(Rs2Bank.hasBankItem("jug of water",14) && Rs2Bank.hasBankItem("grapes",14)) { - Rs2Bank.withdrawDeficit("jug of water", 14); + int jugsInBank = Rs2Bank.count("jug of water"); + int grapesInBank = Rs2Bank.count("grapes"); + if(jugsInBank > 0 && grapesInBank > 0) { + // Withdraw up to 14 of each, or whatever is left for a final partial batch + int amount = Math.min(14, Math.min(jugsInBank, grapesInBank)); + Rs2Bank.withdrawDeficit("jug of water", amount); sleepUntil(() -> Rs2Inventory.hasItem("jug of water")); - Rs2Bank.withdrawDeficit("grapes", 14); + Rs2Bank.withdrawDeficit("grapes", amount); sleepUntil(() -> Rs2Inventory.hasItem("grapes")); } else { + // Out of grapes or jugs of water: log out and stop the plugin Microbot.getNotifier().notify("Run out of Materials"); + Microbot.status = "[Shutting down] - Reason: out of grapes or jugs of water."; Rs2Bank.closeBank(); + sleepUntil(() -> !Rs2Bank.isOpen()); Rs2Player.logout(); + sleepUntil(() -> !Microbot.isLoggedIn(), 10000); Plugin wineMakerPlugin = Microbot.getPluginManager().getPlugins().stream() .filter(x -> x.getClass().getName().equals(WinePlugin.class.getName())) .findFirst() .orElse(null); Microbot.stopPlugin(wineMakerPlugin); shutdown(); + return; } } Rs2Bank.closeBank(); @@ -60,6 +87,7 @@ private void bank(){ @Override public void shutdown() { + Rs2Antiban.resetAntibanSettings(); super.shutdown(); } } diff --git a/src/main/java/net/runelite/client/plugins/microbot/slayer/SlayerPlugin.java b/src/main/java/net/runelite/client/plugins/microbot/slayer/SlayerPlugin.java index 40d93f5093..cc1fe0928a 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/slayer/SlayerPlugin.java +++ b/src/main/java/net/runelite/client/plugins/microbot/slayer/SlayerPlugin.java @@ -42,7 +42,7 @@ @Slf4j public class SlayerPlugin extends Plugin { - static final String version = "1.0.2"; + static final String version = "1.1.0"; @Inject private SlayerConfig config; diff --git a/src/main/java/net/runelite/client/plugins/microbot/slayer/SlayerScript.java b/src/main/java/net/runelite/client/plugins/microbot/slayer/SlayerScript.java index ce2bfe254f..51fb7654a6 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/slayer/SlayerScript.java +++ b/src/main/java/net/runelite/client/plugins/microbot/slayer/SlayerScript.java @@ -3,8 +3,13 @@ import lombok.extern.slf4j.Slf4j; import net.runelite.api.Actor; import net.runelite.api.GameState; +import net.runelite.api.Player; import net.runelite.api.Skill; import net.runelite.api.coords.WorldPoint; +import net.runelite.api.gameval.ItemID; +import static net.runelite.api.gameval.VarbitID.SLAYER_AUTOKILL_DESERTLIZARDS; +import static net.runelite.api.gameval.VarbitID.SLAYER_AUTOKILL_GARGOYLES; +import static net.runelite.api.gameval.VarbitID.SLAYER_AUTOKILL_ROCKSLUGS; import net.runelite.api.widgets.Widget; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.Script; @@ -1385,7 +1390,8 @@ private void handleBankingState() { // Check if already matching if (inventorySetup.doesEquipmentMatch() && inventorySetup.doesInventoryMatch()) { - log.info("Inventory setup matches, closing bank"); + log.info("Inventory setup matches"); + withdrawFinishingItems(); Rs2Bank.closeBank(); sleepUntil(() -> !Rs2Bank.isOpen(), 2000); initialSetupDone = true; @@ -1408,6 +1414,7 @@ private void handleBankingState() { if (equipmentLoaded && inventoryLoaded) { log.info("Inventory setup loaded successfully"); + withdrawFinishingItems(); Rs2Bank.closeBank(); sleepUntil(() -> !Rs2Bank.isOpen(), 2000); initialSetupDone = true; @@ -1426,6 +1433,29 @@ private void handleBankingState() { } } + private void withdrawFinishingItems() { + int finishingItemId = getRequiredFinishingItemId(); + if (finishingItemId == -1) return; + + int alreadyHave = Rs2Inventory.itemQuantity(finishingItemId); + int remaining = Rs2Slayer.getSlayerTaskSize(); + int needed = remaining - alreadyHave; + if (needed <= 0) return; + + if (alreadyHave == 0 && Rs2Inventory.isFull()) { + log.warn("Inventory is full, cannot withdraw finishing items — add a free slot to your inventory setup"); + return; + } + + if (Rs2Bank.hasBankItem(finishingItemId, 1)) { + log.info("Withdrawing {} finishing items (have {}, task remaining {})", needed, alreadyHave, remaining); + Rs2Bank.withdrawX(finishingItemId, needed); + sleep(300, 600); + } else { + log.warn("Finishing item (ID {}) not found in bank", finishingItemId); + } + } + // Spellbook integer values from InventorySetup // 0=Standard, 1=Ancient, 2=Lunar, 3=Arceuus, 4=None private static final int SPELLBOOK_STANDARD = 0; @@ -2018,6 +2048,9 @@ private void handleFightingState(boolean hasTask, int remaining) { // Handle fungicide spray refill (for zygomite tasks) handleFungicideRefill(); + // Handle slayer finishing items (ice cooler, bag of salt, rock hammer) + handleSlayerFinishingItems(); + // Handle eating food BEFORE POH check // This ensures we eat food first, only use POH if still low HP after eating boolean ateFood = handleEating(); @@ -2264,6 +2297,57 @@ private int parseFungicideCharges(String name) { } } + private static final List LIZARD_VARIANTS = Arrays.asList("Lizard", "Desert Lizard", "Small Lizard"); + + /** + * Returns the finishing item ID required for the current slayer task, or -1 if none needed. + * Checks the vanilla autokill varbit — if the player already has auto-finish enabled in-game + * for that monster, we don't need to carry the item. + */ + private int getRequiredFinishingItemId() { + String task = Rs2Slayer.getSlayerTask(); + if (task == null) return -1; + String taskLower = task.toLowerCase(); + if (taskLower.contains("lizard") && !taskLower.contains("lizardm") + && Microbot.getVarbitValue(SLAYER_AUTOKILL_DESERTLIZARDS) == 0) { + return ItemID.SLAYER_ICY_WATER; + } else if (taskLower.contains("rockslug") + && Microbot.getVarbitValue(SLAYER_AUTOKILL_ROCKSLUGS) == 0) { + return ItemID.SLAYER_BAG_OF_SALT; + } else if (taskLower.contains("gargoyle") + && Microbot.getVarbitValue(SLAYER_AUTOKILL_GARGOYLES) == 0) { + return ItemID.SLAYER_ROCK_HAMMER; + } + return -1; + } + + private void handleSlayerFinishingItems() { + final Player localPlayer = Microbot.getClient().getLocalPlayer(); + if (localPlayer == null) return; + + Rs2NpcModel npc = Microbot.getRs2NpcCache().query() + .where(n -> n.isDead() && java.util.Objects.equals(n.getInteracting(), localPlayer)) + .first(); + if (npc == null) return; + + net.runelite.client.plugins.microbot.util.npc.Rs2NpcModel legacyNpc = + new net.runelite.client.plugins.microbot.util.npc.Rs2NpcModel(npc.getNpc()); + + if (Microbot.getVarbitValue(SLAYER_AUTOKILL_DESERTLIZARDS) == 0 + && LIZARD_VARIANTS.contains(npc.getName()) && npc.getHealthRatio() < 5) { + Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ICY_WATER, legacyNpc); + Rs2Player.waitForAnimation(); + } else if (Microbot.getVarbitValue(SLAYER_AUTOKILL_ROCKSLUGS) == 0 + && "Rockslug".equalsIgnoreCase(npc.getName()) && npc.getHealthRatio() < 5) { + Rs2Inventory.useItemOnNpc(ItemID.SLAYER_BAG_OF_SALT, legacyNpc); + Rs2Player.waitForAnimation(); + } else if (Microbot.getVarbitValue(SLAYER_AUTOKILL_GARGOYLES) == 0 + && "Gargoyle".equalsIgnoreCase(npc.getName()) && npc.getHealthRatio() < 3) { + Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ROCK_HAMMER, legacyNpc); + Rs2Player.waitForAnimation(); + } + } + /** * Varbit for antifire protection. * 0 = no protection, >0 = protected (ticks remaining) @@ -3265,6 +3349,13 @@ private boolean needsBanking() { } } + // Check finishing items (ice cooler, bag of salt, rock hammer) + int finishingItemId = getRequiredFinishingItemId(); + if (finishingItemId != -1 && Rs2Inventory.itemQuantity(finishingItemId) == 0) { + log.info("Out of finishing items (item ID {}), need to bank", finishingItemId); + return true; + } + return false; }