Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
import net.runelite.client.plugins.microbot.util.gameobject.Rs2GameObject;
import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory;
import net.runelite.client.plugins.microbot.util.npc.Rs2Npc;
import net.runelite.client.plugins.microbot.util.npc.Rs2NpcModel;
import net.runelite.client.plugins.microbot.util.magic.Rs2Magic;
import net.runelite.client.plugins.skillcalculator.skills.MagicAction;
import net.runelite.client.plugins.microbot.util.player.Rs2Player;
import net.runelite.client.plugins.microbot.util.walker.Rs2Walker;
import net.runelite.client.plugins.microbot.agility.courses.PyramidObstacleData.ObstacleArea;
Expand Down Expand Up @@ -1157,7 +1160,7 @@ private boolean handlePyramidTurnIn() {
}
} else {
// Not in dialogue, use pyramid top on Simon
boolean used = Rs2Inventory.useItemOnNpc(ItemID.PYRAMID_TOP, simon);
boolean used = Rs2Inventory.useItemOnNpc(ItemID.PYRAMID_TOP, new Rs2NpcModel(simon));
if (used) {
log.debug("Successfully used pyramid top on Simon");
Global.sleepUntil(() -> Rs2Dialogue.isInDialogue(), 3000);
Expand Down Expand Up @@ -1197,19 +1200,35 @@ private boolean handlePyramidTurnIn() {
return false;
}
}

/**
* Checks for empty waterskins in inventory and drops them
* @return true if waterskins were dropped, false otherwise
*/


private boolean handleEmptyWaterskins() {
if (Rs2Inventory.contains(ItemID.WATERSKIN0)) {
log.debug("Found empty waterskin(s), dropping them");
Rs2Inventory.drop(ItemID.WATERSKIN0);
Global.sleep(300, 500);
final boolean hasEmpty = Rs2Inventory.contains(ItemID.WATERSKIN0);
if (!hasEmpty) return false;

final boolean hasFilled = Rs2Inventory.contains(
ItemID.WATERSKIN1, ItemID.WATERSKIN2, ItemID.WATERSKIN3, ItemID.WATERSKIN4
);

if (!hasFilled && Rs2Magic.canCast(MagicAction.HUMIDIFY)) {
log.debug("All waterskins are empty; casting Humidify");
if (Rs2Magic.cast(MagicAction.HUMIDIFY)) {
Global.sleepUntil(Rs2Player::isAnimating, 1500);
Global.sleepUntil(() -> !Rs2Player.isAnimating() && !Rs2Inventory.contains(ItemID.WATERSKIN0), 3500);
Global.sleep(200, 400);
}
return true;
}
return false;

if (hasFilled && Rs2Magic.canCast(MagicAction.HUMIDIFY)) {
log.debug("Have filled waterskin(s); not casting Humidify because not all Waterskins are empty");
return false;
}

log.debug("Cannot cast Humidify; dropping empty waterskin(s)");
Rs2Inventory.drop(ItemID.WATERSKIN0);
Global.sleep(300, 500);
return true;
}
Comment on lines 1205 to 1232
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Guard Humidify cast failures to avoid crashes or stalls.

Two risks:

  • Rs2Magic.cast may throw (e.g., unconfigured MagicAction widget), crashing the plugin.
  • If cast() returns false, the method still returns true, causing repeated no-op loops.

Wrap the cast with try/catch and add a fallback to drop empties when casting fails.

Apply this diff:

-        if (!hasFilled && Rs2Magic.canCast(MagicAction.HUMIDIFY)) {
-            log.debug("All waterskins are empty; casting Humidify");
-            if (Rs2Magic.cast(MagicAction.HUMIDIFY)) {
-                Global.sleepUntil(Rs2Player::isAnimating, 1500);
-                Global.sleepUntil(() -> !Rs2Player.isAnimating() && !Rs2Inventory.contains(ItemID.WATERSKIN0), 3500);
-                Global.sleep(200, 400);
-            }
-            return true;
-        }
+        if (!hasFilled && Rs2Magic.canCast(MagicAction.HUMIDIFY)) {
+            log.debug("All waterskins are empty; casting Humidify");
+            try {
+                if (Rs2Magic.cast(MagicAction.HUMIDIFY)) {
+                    Global.sleepUntil(Rs2Player::isAnimating, 1500);
+                    Global.sleepUntil(() -> !Rs2Player.isAnimating() && !Rs2Inventory.contains(ItemID.WATERSKIN0), 3500);
+                    Global.sleep(200, 400);
+                } else {
+                    log.warn("Humidify cast returned false; dropping empty waterskin(s) as fallback");
+                    Rs2Inventory.drop(ItemID.WATERSKIN0);
+                    Global.sleep(300, 500);
+                }
+            } catch (RuntimeException e) {
+                log.warn("Humidify cast threw; dropping empty waterskin(s) as fallback", e);
+                Rs2Inventory.drop(ItemID.WATERSKIN0);
+                Global.sleep(300, 500);
+            }
+            return true;
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private boolean handleEmptyWaterskins() {
if (Rs2Inventory.contains(ItemID.WATERSKIN0)) {
log.debug("Found empty waterskin(s), dropping them");
Rs2Inventory.drop(ItemID.WATERSKIN0);
Global.sleep(300, 500);
final boolean hasEmpty = Rs2Inventory.contains(ItemID.WATERSKIN0);
if (!hasEmpty) return false;
final boolean hasFilled = Rs2Inventory.contains(
ItemID.WATERSKIN1, ItemID.WATERSKIN2, ItemID.WATERSKIN3, ItemID.WATERSKIN4
);
if (!hasFilled && Rs2Magic.canCast(MagicAction.HUMIDIFY)) {
log.debug("All waterskins are empty; casting Humidify");
if (Rs2Magic.cast(MagicAction.HUMIDIFY)) {
Global.sleepUntil(Rs2Player::isAnimating, 1500);
Global.sleepUntil(() -> !Rs2Player.isAnimating() && !Rs2Inventory.contains(ItemID.WATERSKIN0), 3500);
Global.sleep(200, 400);
}
return true;
}
return false;
if (hasFilled && Rs2Magic.canCast(MagicAction.HUMIDIFY)) {
log.debug("Have filled waterskin(s); not casting Humidify because not all Waterskins are empty");
return false;
}
log.debug("Cannot cast Humidify; dropping empty waterskin(s)");
Rs2Inventory.drop(ItemID.WATERSKIN0);
Global.sleep(300, 500);
return true;
}
private boolean handleEmptyWaterskins() {
final boolean hasEmpty = Rs2Inventory.contains(ItemID.WATERSKIN0);
if (!hasEmpty) return false;
final boolean hasFilled = Rs2Inventory.contains(
ItemID.WATERSKIN1, ItemID.WATERSKIN2, ItemID.WATERSKIN3, ItemID.WATERSKIN4
);
if (!hasFilled && Rs2Magic.canCast(MagicAction.HUMIDIFY)) {
log.debug("All waterskins are empty; casting Humidify");
try {
if (Rs2Magic.cast(MagicAction.HUMIDIFY)) {
Global.sleepUntil(Rs2Player::isAnimating, 1500);
Global.sleepUntil(() -> !Rs2Player.isAnimating() && !Rs2Inventory.contains(ItemID.WATERSKIN0), 3500);
Global.sleep(200, 400);
} else {
log.warn("Humidify cast returned false; dropping empty waterskin(s) as fallback");
Rs2Inventory.drop(ItemID.WATERSKIN0);
Global.sleep(300, 500);
}
} catch (RuntimeException e) {
log.warn("Humidify cast threw; dropping empty waterskin(s) as fallback", e);
Rs2Inventory.drop(ItemID.WATERSKIN0);
Global.sleep(300, 500);
}
return true;
}
if (hasFilled && Rs2Magic.canCast(MagicAction.HUMIDIFY)) {
log.debug("Have filled waterskin(s); not casting Humidify because not all Waterskins are empty");
return false;
}
log.debug("Cannot cast Humidify; dropping empty waterskin(s)");
Rs2Inventory.drop(ItemID.WATERSKIN0);
Global.sleep(300, 500);
return true;
}
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/agility/courses/PyramidCourse.java
around lines 1205-1232, wrap the Rs2Magic.cast(MagicAction.HUMIDIFY) call in a
try/catch and handle a false return: if cast throws or returns false, log the
failure, fall back to dropping empty waterskins (call
Rs2Inventory.drop(ItemID.WATERSKIN0) and a short sleep) and return true to
indicate the empties were handled; keep the existing successful-cast behavior
(sleep until animating/finished) and the early-return when there are filled
waterskins.


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public boolean run() {
Rs2Inventory.waitForInventoryChanges(1800);
}

if (plugin.getJewelry().getGem() != null) {
if (plugin.getJewelry().getGem() != Gem.NONE) {
Rs2Bank.withdrawX(plugin.getJewelry().getGem().getCutItemID(), withdrawAmount);
Rs2Inventory.waitForInventoryChanges(1800);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public enum Jewelry {
GOLD_NECKLACE("gold necklace", ItemID.GOLD_NECKLACE, Gem.NONE, ItemID.NECKLACE_MOULD, JewelryType.GOLD, null, 6),
GOLD_BRACELET("gold bracelet", ItemID.GOLD_BRACELET, Gem.NONE, ItemID.BRACELET_MOULD, JewelryType.GOLD, null, 7),
GOLD_AMULET("gold amulet", ItemID.GOLD_AMULET_U, Gem.NONE, ItemID.AMULET_MOULD, JewelryType.GOLD, null, 8),
TIARA("tiara", ItemID.TIARA, Gem.NONE, ItemID.TIARA_MOULD, JewelryType.SILVER, null, 23),
UNSTRUNG_SYMBOL("holy symbol", ItemID.UNSTRUNG_SYMBOL, Gem.NONE, ItemID.HOLY_MOULD, JewelryType.SILVER, null, 16),
Comment on lines +18 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Widget label mismatch: use “unstrung symbol” for crafting UI.

The furnace widget lists “Unstrung symbol”. Using “holy symbol” will likely fail Rs2Widget.clickWidget(itemName).

Apply:

-    UNSTRUNG_SYMBOL("holy symbol", ItemID.UNSTRUNG_SYMBOL, Gem.NONE, ItemID.HOLY_MOULD, JewelryType.SILVER, null, 16),
+    UNSTRUNG_SYMBOL("unstrung symbol", ItemID.UNSTRUNG_SYMBOL, Gem.NONE, ItemID.HOLY_MOULD, JewelryType.SILVER, null, 16),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
TIARA("tiara", ItemID.TIARA, Gem.NONE, ItemID.TIARA_MOULD, JewelryType.SILVER, null, 23),
UNSTRUNG_SYMBOL("holy symbol", ItemID.UNSTRUNG_SYMBOL, Gem.NONE, ItemID.HOLY_MOULD, JewelryType.SILVER, null, 16),
TIARA("tiara", ItemID.TIARA, Gem.NONE, ItemID.TIARA_MOULD, JewelryType.SILVER, null, 23),
UNSTRUNG_SYMBOL("unstrung symbol", ItemID.UNSTRUNG_SYMBOL, Gem.NONE, ItemID.HOLY_MOULD, JewelryType.SILVER, null, 16),
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/crafting/jewelry/enums/Jewelry.java
around lines 18-19, the enum entry uses the display name "holy symbol" which
doesn't match the furnace widget label and will break
Rs2Widget.clickWidget(itemName); change the display string to "unstrung symbol"
(matching the crafting UI label) for the UNSTRUNG_SYMBOL enum entry so clicks
target the correct widget.

OPAL_RING("opal ring", ItemID.OPAL_RING, Gem.OPAL, ItemID.RING_MOULD, JewelryType.SILVER, EnchantSpell.LEVEL_1, 1),
OPAL_NECKLACE("opal necklace", ItemID.OPAL_NECKLACE, Gem.OPAL, ItemID.NECKLACE_MOULD, JewelryType.SILVER, EnchantSpell.LEVEL_1, 16),
OPAL_BRACELET("opal bracelet", ItemID.OPAL_BRACELET, Gem.OPAL, ItemID.BRACELET_MOULD, JewelryType.SILVER, EnchantSpell.LEVEL_1, 22),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,19 @@ AIOMagicConfig provideConfig(ConfigManager configManager) {
private SuperHeatScript superHeatScript;

@Inject
private TeleportScript teleportScript;
private TeleportScript teleportScript;

@Inject
private TeleAlchScript teleAlchScript;
private TeleAlchScript teleAlchScript;

@Inject
private StunAlchScript stunAlchScript;
private StunAlchScript stunAlchScript;

@Inject
private StunTeleAlchScript stunTeleAlchScript; // NEW
private StunTeleAlchScript stunTeleAlchScript; // NEW

@Inject
private SpinFlaxScript spinFlaxScript;

public static String version = "1.2.0"; // bumped

Expand Down Expand Up @@ -108,41 +111,45 @@ protected void startUp() throws AWTException {
overlayManager.add(aioMagicOverlay);
}

switch (config.magicActivity()) {
case SPLASHING:
splashScript.run();
break;
case ALCHING:
alchScript.run();
break;
case SUPERHEAT:
superHeatScript.run();
break;
case TELEPORT:
teleportScript.run();
break;
case TELEALCH:
teleAlchScript.run();
break;
case STUNALCH:
stunAlchScript.run();
break;
case STUNTELEALCH: // NEW
stunTeleAlchScript.run();
break;
}
}

protected void shutDown() {
splashScript.shutdown();
alchScript.shutdown();
superHeatScript.shutdown();
teleportScript.shutdown();
teleAlchScript.shutdown();
stunAlchScript.shutdown();
if (stunTeleAlchScript != null) stunTeleAlchScript.shutdown(); // NEW
overlayManager.remove(aioMagicOverlay);
}
switch (config.magicActivity()) {
case SPLASHING:
splashScript.run();
break;
case ALCHING:
alchScript.run();
break;
case SUPERHEAT:
superHeatScript.run();
break;
case TELEPORT:
teleportScript.run();
break;
case TELEALCH:
teleAlchScript.run();
break;
case STUNALCH:
stunAlchScript.run();
break;
case STUNTELEALCH: // NEW
stunTeleAlchScript.run();
break;
case SPINFLAX:
spinFlaxScript.run();
break;
}
}

protected void shutDown() {
splashScript.shutdown();
alchScript.shutdown();
superHeatScript.shutdown();
teleportScript.shutdown();
teleAlchScript.shutdown();
stunAlchScript.shutdown();
if (stunTeleAlchScript != null) stunTeleAlchScript.shutdown(); // NEW
if (spinFlaxScript != null) spinFlaxScript.shutdown();
overlayManager.remove(aioMagicOverlay);
}
Comment on lines +151 to +152
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid NPE on shutdown when OverlayManager is null

Null-check before removal to prevent a shutdown crash.

Apply this diff:

-        overlayManager.remove(aioMagicOverlay);
+        if (overlayManager != null) {
+            overlayManager.remove(aioMagicOverlay);
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
overlayManager.remove(aioMagicOverlay);
}
if (overlayManager != null) {
overlayManager.remove(aioMagicOverlay);
}
}
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/magic/aiomagic/AIOMagicPlugin.java
around lines 151-152, the call overlayManager.remove(aioMagicOverlay) can throw
an NPE if overlayManager is null during shutdown; add a null-check to ensure
overlayManager is not null (and optionally aioMagicOverlay is not null) before
calling remove, e.g. wrap the removal in an if (overlayManager != null) {
overlayManager.remove(aioMagicOverlay); } to avoid the crash.


@Subscribe
public void onConfigChanged(ConfigChanged event) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public enum MagicActivity {
TELEALCH,
STUNALCH,
STUNTELEALCH, // NEW
SPINFLAX,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package net.runelite.client.plugins.microbot.magic.aiomagic.scripts;

import net.runelite.api.Skill;
import net.runelite.api.gameval.ItemID;
import net.runelite.client.plugins.microbot.Microbot;
Comment on lines +3 to +5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect ItemID import (compile error)

The package should be net.runelite.api.ItemID.

-import net.runelite.api.gameval.ItemID;
+import net.runelite.api.ItemID;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import net.runelite.api.Skill;
import net.runelite.api.gameval.ItemID;
import net.runelite.client.plugins.microbot.Microbot;
import net.runelite.api.Skill;
import net.runelite.api.ItemID;
import net.runelite.client.plugins.microbot.Microbot;
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/magic/aiomagic/scripts/SpinFlaxScript.java
around lines 3 to 5, the import for ItemID is incorrect
(net.runelite.api.gameval.ItemID) causing a compile error; replace that import
with net.runelite.api.ItemID and remove the wrong one so the correct ItemID
class from net.runelite.api is used.

import net.runelite.client.plugins.microbot.Script;
import net.runelite.client.plugins.microbot.magic.aiomagic.AIOMagicPlugin;
import net.runelite.client.plugins.microbot.magic.aiomagic.enums.MagicState;
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.antiban.enums.Activity;
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.magic.Rs2Magic;
import net.runelite.client.plugins.microbot.util.magic.Rs2Spells;
import net.runelite.client.plugins.microbot.util.magic.Runes;
import net.runelite.client.plugins.microbot.util.player.Rs2Player;

import javax.inject.Inject;
import java.util.concurrent.TimeUnit;

public class SpinFlaxScript extends Script {

private MagicState state;
private int castsDone;

private final AIOMagicPlugin plugin;

@Inject
public SpinFlaxScript(AIOMagicPlugin plugin) {
this.plugin = plugin;
}

public boolean run() {
Microbot.enableAutoRunOn = false;
Rs2Antiban.resetAntibanSettings();
Rs2Antiban.antibanSetupTemplates.applyGeneralBasicSetup();
Rs2AntibanSettings.simulateAttentionSpan = true;
Rs2AntibanSettings.nonLinearIntervals = true;
Rs2AntibanSettings.contextualVariability = true;
Rs2AntibanSettings.usePlayStyle = true;
Rs2Antiban.setActivity(Activity.CASTING_SPIN_FLAX);

mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> {
try {
if (!Microbot.isLoggedIn()) return;
if (!super.run()) return;

if (!Rs2Player.getSkillRequirement(Skill.MAGIC, 76)) {
Microbot.showMessage("Spin Flax requires 76 Magic.");
shutdown();
return;
}

if (state == null) {
state = MagicState.BANKING;
castsDone = 0;
}

switch (state) {
case BANKING:
Microbot.status = "Banking: prepare 25 flax";
boolean isBankOpen = Rs2Bank.isNearBank(15) ? Rs2Bank.useBank() : Rs2Bank.walkToBankAndUseBank();
if (!isBankOpen || !Rs2Bank.isOpen()) return;

Rs2Bank.depositAll(ItemID.BOW_STRING);
Rs2Inventory.waitForInventoryChanges(1200);

boolean hasAstralInv = Rs2Inventory.hasItem(Runes.ASTRAL.getItemId());
boolean hasNatureInv = Rs2Inventory.hasItem(Runes.NATURE.getItemId());
boolean hasAirInv = Rs2Inventory.hasItem(Runes.AIR.getItemId());

boolean hasAstralBank = Rs2Bank.hasItem(Runes.ASTRAL.getItemId());
boolean hasNatureBank = Rs2Bank.hasItem(Runes.NATURE.getItemId());
Comment on lines +66 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Banking loop can soft-lock when carrying flax

If a cast fails and you return to bank with flax in inventory, the empty-slot check will never pass. Deposit flax before the slot check.

                         Rs2Bank.depositAll(ItemID.BOW_STRING);
                         Rs2Inventory.waitForInventoryChanges(1200);
+                        // Ensure we don't get stuck with flax in inventory
+                        if (Rs2Inventory.hasItem(ItemID.FLAX)) {
+                            Rs2Bank.depositAll(ItemID.FLAX);
+                            Rs2Inventory.waitForInventoryChanges(1200);
+                        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Rs2Bank.depositAll(ItemID.BOW_STRING);
Rs2Inventory.waitForInventoryChanges(1200);
boolean hasAstralInv = Rs2Inventory.hasItem(Runes.ASTRAL.getItemId());
boolean hasNatureInv = Rs2Inventory.hasItem(Runes.NATURE.getItemId());
boolean hasAirInv = Rs2Inventory.hasItem(Runes.AIR.getItemId());
boolean hasAstralBank = Rs2Bank.hasItem(Runes.ASTRAL.getItemId());
boolean hasNatureBank = Rs2Bank.hasItem(Runes.NATURE.getItemId());
Rs2Bank.depositAll(ItemID.BOW_STRING);
Rs2Inventory.waitForInventoryChanges(1200);
// Ensure we don't get stuck with flax in inventory
if (Rs2Inventory.hasItem(ItemID.FLAX)) {
Rs2Bank.depositAll(ItemID.FLAX);
Rs2Inventory.waitForInventoryChanges(1200);
}
boolean hasAstralInv = Rs2Inventory.hasItem(Runes.ASTRAL.getItemId());
boolean hasNatureInv = Rs2Inventory.hasItem(Runes.NATURE.getItemId());
boolean hasAirInv = Rs2Inventory.hasItem(Runes.AIR.getItemId());
boolean hasAstralBank = Rs2Bank.hasItem(Runes.ASTRAL.getItemId());
boolean hasNatureBank = Rs2Bank.hasItem(Runes.NATURE.getItemId());
🤖 Prompt for AI Agents
In
runelite-client/src/main/java/net/runelite/client/plugins/microbot/magic/aiomagic/scripts/SpinFlaxScript.java
around lines 66-74, the banking loop can soft-lock if the player returns to bank
still carrying flax because the later empty-slot check never passes; modify the
sequence so you deposit any flax (bow strings or raw flax) before performing the
empty-slot/inventory checks, i.e., call deposit for flax items first, wait for
inventory change, then evaluate hasAstral/hasNature/hasAir and bank item
presence.

boolean hasAirBank = Rs2Bank.hasItem(Runes.AIR.getItemId());

if (!hasAstralBank && !hasAstralInv) { Microbot.showMessage("Astral runes not found in bank."); shutdown(); return; }
if (!hasNatureBank && !hasNatureInv) { Microbot.showMessage("Nature runes not found in bank."); shutdown(); return; }
if (!hasAirBank && !hasAirInv) { Microbot.showMessage("Air runes not found in bank."); shutdown(); return; }

if (hasAstralBank) { if (!Rs2Bank.withdrawAll(Runes.ASTRAL.getItemId())) return; Rs2Inventory.waitForInventoryChanges(1200); }
if (hasNatureBank) { if (!Rs2Bank.withdrawAll(Runes.NATURE.getItemId())) return; Rs2Inventory.waitForInventoryChanges(1200); }
if (hasAirBank) { if (!Rs2Bank.withdrawAll(Runes.AIR.getItemId())) return; Rs2Inventory.waitForInventoryChanges(1200); }

if (!Rs2Bank.hasBankItem(ItemID.FLAX, 1)) {
Microbot.showMessage("No flax in bank.");
shutdown();
return;
}

int empty = Rs2Inventory.emptySlotCount();
if (empty < 25) {
Rs2Bank.depositAll(ItemID.BOW_STRING);
Rs2Inventory.waitForInventoryChanges(1200);
empty = Rs2Inventory.emptySlotCount();
if (empty < 25) return; // wait next tick
}

if (!Rs2Bank.withdrawX(ItemID.FLAX, 25)) return;
Rs2Inventory.waitForInventoryChanges(1200);

Rs2Bank.closeBank();
castsDone = 0;
state = MagicState.CASTING;
break;

case CASTING:
Microbot.status = "Casting: Spin Flax (" + castsDone + "/5)";

if (!Rs2Inventory.hasItem(ItemID.FLAX) || !Rs2Magic.hasRequiredRunes(Rs2Spells.SPIN_FLAX)) {
state = MagicState.BANKING;
break;
}

if (castsDone >= 5) {
state = MagicState.BANKING;
break;
}

if (!Rs2Magic.cast(Rs2Spells.SPIN_FLAX)) {
Microbot.log("Unable to cast Spin Flax");
state = MagicState.BANKING;
break;
}
Rs2Player.waitForXpDrop(Skill.MAGIC, 10000, false);
castsDone++;
break;
}

} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}, 0, 1000, TimeUnit.MILLISECONDS);
return true;
}

@Override
public void shutdown() {
Rs2Antiban.resetAntibanSettings();
super.shutdown();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public boolean run(StallThievingConfig config) {

long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
System.out.println("Total time for loop " + totalTime);

} catch (Exception ex) {
Microbot.logStackTrace(this.getClass().getSimpleName(), ex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public enum ThievingSpot {
VARROCK_TEA_STALL,
ARDY_BAKER,
ARDY_SILK,
HOSIDIUS_FRUIT
HOSIDIUS_FRUIT,
FORTIS_GEM_STALL
;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public IStallThievingSpot getThievingSpot(final ThievingSpot thievingSpot)
ThievingSpot.VARROCK_TEA_STALL, varrockTeaStallThievingSpot,
ThievingSpot.ARDY_BAKER, ardyBakerThievingSpot,
ThievingSpot.ARDY_SILK, ardySilkThievingSpot,
ThievingSpot.HOSIDIUS_FRUIT, hosidiusFruitThievingSpot
ThievingSpot.HOSIDIUS_FRUIT, hosidiusFruitThievingSpot,
ThievingSpot.FORTIS_GEM_STALL, fortisGemStallThievingSpot
);

return map.get(thievingSpot);
Expand All @@ -25,4 +26,5 @@ public IStallThievingSpot getThievingSpot(final ThievingSpot thievingSpot)
private ArdyBakerThievingSpot ardyBakerThievingSpot;
private ArdySilkThievingSpot ardySilkThievingSpot;
private HosidiusFruitThievingSpot hosidiusFruitThievingSpot;
private FortisGemStallThievingSpot fortisGemStallThievingSpot;
}
Loading