Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
10b7940
fix(revkiller): avoid off-thread Actor.getName and empty equipment sl…
chsami Oct 14, 2026
47badaa
fix(motherloadmine): fall back to mining intensity when antiban activ…
chsami Oct 14, 2026
ad0992d
fix(nmz): guard prayer potion threshold against zero real prayer leve…
chsami Oct 14, 2026
87754de
fix(aiofighter): treat missing slayer task as no protective equipment…
chsami Oct 14, 2026
0782871
fix(stallthieving): ignore non-numeric always-drop entries instead of…
chsami Oct 14, 2026
9fd4d2a
fix(sulphurnagua): read getInteracting() once per null check (2.0.3) …
chsami Oct 14, 2026
15021bf
fix(jad): read NPC name once per null check (1.0.11) (#620)
chsami Oct 14, 2026
4f547da
fix(qol): fall back to unit bounds for recorded entries without widge…
chsami Oct 14, 2026
b5f8eb4
fix(aiofighter): snapshot target sort keys before sorting (2.2.5) (#622)
chsami Oct 14, 2026
d2a25c7
fix(bankorganizer): stop throwing on the client thread when the bank …
chsami Oct 14, 2026
ff5554e
fix(cluesolver): keep requirement banking off the client thread (1.0.…
chsami Oct 15, 2026
4578b67
fix(wildernessagility): skip null ladder re-query in pit recovery (1.…
chsami Oct 15, 2026
d33f195
fix(qol): read Wintertodt energy widget and parse it defensively (1.8…
chsami Oct 15, 2026
0613cfe
fix(aiofighter): null-safe state check in SafeSpot loop (2.2.6) (#627)
chsami Oct 15, 2026
fdf8738
fix(sandcrabs): guard null inventory setup before use (1.5.7) (#628)
chsami Oct 15, 2026
fd14702
fix(nmz): guard null widgets and local player (2.4.4) (#629)
chsami Oct 15, 2026
5561590
fix(woodcutting): guard null return point in overlay and walk-back (1…
chsami Oct 15, 2026
c72e51a
fix(mahoganyhomes): skip tick while player location is unknown (0.0.1…
chsami Oct 15, 2026
a075948
fix(aiofighter): skip attack style lookup when enum or struct is unav…
chsami Oct 15, 2026
99235d8
fix(aiofighter): skip slayer finishing item when dying NPC has no nam…
chsami Oct 15, 2026
2d5cd87
fix(cluesolver): skip typing when anagram has no answer (1.0.7) (#634)
chsami Oct 15, 2026
1c6caed
fix(scurrius): tolerate inverted eat/prayer ranges and null player lo…
chsami Oct 15, 2026
ba968d9
fix(gotr): guard altar renderable cast to DynamicObject (1.5.10) (#636)
chsami Oct 15, 2026
f20ac8b
fix(playermonitor): create click log dir before saving on stop (1.0.5…
chsami Oct 15, 2026
6f54d34
fix(aiofighter): guard null cache inventory setup after equipping sla…
chsami Oct 15, 2026
351284e
fix(FarmTreeRun): build bank withdrawal list locally to avoid CME (1.…
chsami Oct 15, 2026
f600bb2
fix(MageTrainingArena): guard buyReward against unloaded reward shop …
chsami Oct 15, 2026
0cab914
fix(Scurrius): read giant rat name once to avoid NPE in rat query (1.…
chsami Oct 15, 2026
dcfc5f0
fix(AmmoniteCrabs): guard null local player location during hop or lo…
chsami Oct 16, 2026
a8be3b6
fix(Thieving): guard null local player in setStartXp during overlay r…
chsami Oct 16, 2026
a213d1d
fix(TutorialIsland): walk to and open rat pen gate before melee attac…
chsami Oct 16, 2026
01117fe
fix(PVirewatchKiller): skip loop and overlay until fight area is set …
chsami Oct 16, 2026
293dc8e
fix(MoonsOfPeril): fish the river fishing spot during resupply (1.0.5…
chsami Oct 16, 2026
95d4b7d
fix(MahoganyHomes): guard null home and player location in walkToHome…
chsami Oct 17, 2026
1445d2a
fix(MahoganyHomes): skip hotspots beyond home object count in getFixa…
chsami Oct 17, 2026
79f3247
fix(FarmTreeRun): guard null object composition in isPatchEmpty and h…
chsami Oct 17, 2026
10db07a
fix(ShootingStar): close stop race on null panel and null state (1.4.…
chsami Oct 17, 2026
d7f5501
fix(TowerOfLifeCC): run summon query on client thread (1.1.5) (#653)
chsami Oct 18, 2026
c418633
Merge remote-tracking branch 'origin/development' into claude/release…
chsami Oct 18, 2026
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 @@ -28,7 +28,7 @@
@Slf4j
public class AmmoniteCrabPlugin extends Plugin {

public final static String version = "1.1.6";
public final static String version = "1.1.7";
@Inject
public AmmoniteCrabScript ammoniteCrabScript;
@Inject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ public boolean run(AmmoniteCrabConfig config) {

switch (ammoniteCrabState) {
case FIGHT:
if (!Microbot.getClient().getLocalPlayer().isInteracting() && !Rs2Combat.inCombat()) {
if (!Rs2Player.isInteracting() && !Rs2Combat.inCombat()) {
Rs2Combat.enableAutoRetialiate();
}

if (Rs2Player.getWorldLocation().distanceTo(config.crabLocation().getFightLocation()) > 0) {
WorldPoint fightPlayerLocation = Rs2Player.getWorldLocation();
if (fightPlayerLocation != null && fightPlayerLocation.distanceTo(config.crabLocation().getFightLocation()) > 0) {
Rs2Walker.walkFastCanvas(config.crabLocation().getFightLocation());
}

Expand Down Expand Up @@ -132,7 +133,8 @@ private void preStateChecks(AmmoniteCrabConfig config) {
ammoniteCrabState = AmmoniteCrabState.HOP_WORLD;
}

if (Microbot.getClientThread().invoke(() -> Microbot.getClient().getLocalPlayer().getWorldLocation()).distanceTo(config.crabLocation().getWorldhopLocation()) > 10 && (ammoniteCrabState != AmmoniteCrabState.RESET_AGGRO && ammoniteCrabState != AmmoniteCrabState.WALK_BACK && ammoniteCrabState != AmmoniteCrabState.BANK)) {
WorldPoint playerLocation = Rs2Player.getWorldLocation();
if (playerLocation != null && playerLocation.distanceTo(config.crabLocation().getWorldhopLocation()) > 10 && (ammoniteCrabState != AmmoniteCrabState.RESET_AGGRO && ammoniteCrabState != AmmoniteCrabState.WALK_BACK && ammoniteCrabState != AmmoniteCrabState.BANK)) {
ammoniteCrabState = AmmoniteCrabState.WALK_BACK;
resetAggro(config);
resetAfkTimer();
Expand Down Expand Up @@ -178,7 +180,11 @@ private void scanAmmoniteCrabLocation(AmmoniteCrabConfig config) {
ammoniteCrabState = AmmoniteCrabState.FIGHT;
return;
}
if (config.crabLocation().getWorldhopLocation().distanceTo(Microbot.getClientThread().invoke(() -> Microbot.getClient().getLocalPlayer().getWorldLocation())) > 2) {
WorldPoint playerLocation = Rs2Player.getWorldLocation();
if (playerLocation == null) {
return;
}
if (config.crabLocation().getWorldhopLocation().distanceTo(playerLocation) > 2) {
Rs2Walker.walkTo(config.crabLocation().getWorldhopLocation());
return;
}
Expand All @@ -188,7 +194,7 @@ private void scanAmmoniteCrabLocation(AmmoniteCrabConfig config) {
return;
} else {
Rs2Walker.walkTo(config.crabLocation().getFightLocation(), 0);
if (Rs2Player.getWorldLocation().distanceTo(config.crabLocation().getFightLocation()) <= 3) {
if (isWithin(Rs2Player.getWorldLocation(), config.crabLocation().getFightLocation(), 3)) {
resetAfkTimer();
ammoniteCrabState = AmmoniteCrabState.FIGHT;
} else {
Expand All @@ -208,7 +214,7 @@ private void usePotions(AmmoniteCrabConfig config) {

private void walkBack(AmmoniteCrabConfig config) {
Rs2Walker.walkTo(config.crabLocation().getFightLocation());
if (Microbot.getClientThread().invoke(() -> Microbot.getClient().getLocalPlayer().getWorldLocation()).distanceTo(config.crabLocation().getFightLocation()) <= 3) {
if (isWithin(Rs2Player.getWorldLocation(), config.crabLocation().getFightLocation(), 3)) {
attackScatteredCrabs(config);
resetAfkTimer();
ammoniteCrabState = AmmoniteCrabState.FIGHT;
Expand Down Expand Up @@ -256,7 +262,7 @@ private boolean isNpcAggressive() {

private void resetAggro(AmmoniteCrabConfig config) {
Rs2Walker.walkTo(config.crabLocation().getResetLocation());
if (Microbot.getClientThread().invoke(() -> Microbot.getClient().getLocalPlayer().getWorldLocation()).distanceTo(config.crabLocation().getResetLocation()) <= 3) {
if (isWithin(Rs2Player.getWorldLocation(), config.crabLocation().getResetLocation(), 3)) {
ammoniteCrabState = AmmoniteCrabState.WALK_BACK;
}
}
Expand All @@ -273,10 +279,17 @@ private void resetAfkTimer() {
}

private boolean otherPlayerDetected() {
return otherPlayerDetected(Microbot.getClientThread().invoke(() -> Microbot.getClient().getLocalPlayer().getWorldLocation()));
return otherPlayerDetected(Rs2Player.getWorldLocation());
}

private boolean otherPlayerDetected(WorldPoint worldPoint) {
static boolean isWithin(WorldPoint location, WorldPoint target, int distance) {
return location != null && location.distanceTo(target) <= distance;
}

static boolean otherPlayerDetected(WorldPoint worldPoint) {
if (worldPoint == null) {
return false;
}
for (Rs2PlayerModel player : Rs2Player.getPlayers(player -> true).collect(Collectors.toList())) {
if (player.getWorldLocation().distanceTo(worldPoint) > 3)
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
)
@Slf4j
public class AIOFighterPlugin extends Plugin {
public static final String version = "2.2.3";
public static final String version = "2.2.9";
public static boolean needShopping = false;
private static final String SET = "Set";
private static final String CENTER_TILE = ColorUtil.wrapWithColorTag("Center Tile", JagexColors.MENU_TARGET);
Expand Down Expand Up @@ -251,6 +251,10 @@ public static void setSlayerHasTaskWeakness(boolean slayerHasTaskWeakness) {
}

public static void setCurrentSlayerInventorySetup(InventorySetup currentInventorySetup) {
if (currentInventorySetup == null) {
Microbot.log("Inventory setup not found; keeping current inventory setup");
return;
}
Microbot.log("Setting current inventory setup to: " + currentInventorySetup.getName());
setConfig("currentInventorySetup", currentInventorySetup);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,17 @@ static boolean isSetupResolvable(Collection<String> setupNames, String setupName
.anyMatch(name -> name.equalsIgnoreCase(setupName) || name.equals("default"));
}

static InventorySetup findSetupByName(Collection<InventorySetup> setups, String setupName) {
if (setups == null || setupName == null) {
return null;
}
return setups.stream()
.filter(Objects::nonNull)
.filter(setup -> setupName.equalsIgnoreCase(setup.getName()))
.findFirst()
.orElse(null);
}

/**
* Checks if the given setup requires any variant of a specific potion but the player does not have it.
*/
Expand Down Expand Up @@ -323,7 +334,7 @@ public boolean hasSlayerItems() {
public boolean hasProtectiveSlayerEquipment() {
String needed = Rs2Slayer.getSlayerTaskProtectiveEquipment();

if ("None".equals(needed)) {
if (!requiresProtectiveEquipment(needed)) {
return true;
}

Expand All @@ -349,7 +360,14 @@ public boolean hasProtectiveSlayerEquipment() {
return result;
}

private boolean isBypassedBySlayerHelmet(String item) {
static boolean requiresProtectiveEquipment(String needed) {
return needed != null && !"None".equals(needed);
}

static boolean isBypassedBySlayerHelmet(String item) {
if (item == null) {
return false;
}
switch (item) {
case "Earmuffs":
case "Facemask":
Expand Down Expand Up @@ -472,7 +490,8 @@ public void withdrawUpkeepItems(AIOFighterConfig config) {
Rs2Bank.withdrawAndEquip(Rs2Slayer.getSlayerTaskProtectiveEquipment());
sleep(3000);
inventorySetupsPlugin.addInventorySetup("cache");
InventorySetup inventorySetup1 = MInventorySetupsPlugin.getInventorySetups().stream().filter(Objects::nonNull).filter(x -> x.getName().equalsIgnoreCase("cache")).findFirst().orElse(null);
sleepUntil(() -> findSetupByName(MInventorySetupsPlugin.getInventorySetups(), "cache") != null, 3000);
InventorySetup inventorySetup1 = findSetupByName(MInventorySetupsPlugin.getInventorySetups(), "cache");
AIOFighterPlugin.setCurrentSlayerInventorySetup(inventorySetup1);
} else {
ShopScript.shopItems.add(new ShopItem(Rs2Slayer.getSlayerTaskProtectiveEquipment(), 1, Microbot.getRs2ItemManager().getItemId(Rs2Slayer.getSlayerTaskProtectiveEquipment()), ShopType.SLAYER_SHOP));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.IntUnaryOperator;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;

import static net.runelite.api.gameval.VarbitID.*;
Expand Down Expand Up @@ -158,12 +160,10 @@ public void run(AIOFighterConfig config) {
String name = npc.getName();
return name != null && !npcsToAttack.isEmpty() && npcsToAttack.contains(name.toLowerCase());
})
.toList()
.stream()
.sorted(Comparator
.comparingInt((Rs2NpcModel npc) -> Objects.equals(npc.getInteracting(), localPlayer) ? 0 : 1)
.thenComparingInt(npc -> rs2PlayerPoint.distanceToPath(npcWorldLocation(npc, instanced))))
.collect(Collectors.toList());
.toList();
attackableNpcs = sortBySnapshotKeys(attackableNpcs,
npc -> Objects.equals(npc.getInteracting(), localPlayer) ? 0 : 1,
npc -> rs2PlayerPoint.distanceToPath(npcWorldLocation(npc, instanced)));

filteredAttackableNpcs.set(attackableNpcs);

Expand Down Expand Up @@ -296,6 +296,20 @@ public void run(AIOFighterConfig config) {
}


static <T> List<T> sortBySnapshotKeys(List<T> items, ToIntFunction<T> primary, ToIntFunction<T> secondary) {
List<int[]> keyed = new ArrayList<>(items.size());
for (int i = 0; i < items.size(); i++) {
T item = items.get(i);
keyed.add(new int[]{primary.applyAsInt(item), secondary.applyAsInt(item), i});
}
keyed.sort(Comparator.<int[]>comparingInt(k -> k[0]).thenComparingInt(k -> k[1]));
List<T> sorted = new ArrayList<>(keyed.size());
for (int[] k : keyed) {
sorted.add(items.get(k[2]));
}
return sorted;
}

static boolean shouldHoldAttack(boolean inCombat, boolean antibanEnabled, double actionCooldownChance, boolean actionCooldownActive) {
if (inCombat) return true;
return antibanEnabled && actionCooldownChance > 0 && actionCooldownActive;
Expand All @@ -310,20 +324,26 @@ private void handleItemOnNpcToKill(AIOFighterConfig config) {
.where(n -> n.isDead() && Objects.equals(n.getInteracting(), localPlayer))
.first();
if (npc == null) return;
List<String> lizardVariants = new ArrayList<>(Arrays.asList("Lizard", "Desert Lizard", "Small Lizard"));
int itemId = finishingItemFor(npc.getName(), npc.getHealthRatio(), Microbot::getVarbitValue);
if (itemId == -1) return;
// Rs2Inventory.useItemOnNpc only accepts the legacy util.npc.Rs2NpcModel — wrap our underlying NPC at the call site.
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 && lizardVariants.contains(npc.getName()) && npc.getHealthRatio() < 5) {
Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ICY_WATER, legacyNpc);
Rs2Player.waitForAnimation();
} else if (Microbot.getVarbitValue(SLAYER_AUTOKILL_ROCKSLUGS) == 0 && npc.getName().equalsIgnoreCase("rockslug") && npc.getHealthRatio() < 5) {
Rs2Inventory.useItemOnNpc(ItemID.SLAYER_BAG_OF_SALT, legacyNpc);
Rs2Player.waitForAnimation();
} else if (Microbot.getVarbitValue(SLAYER_AUTOKILL_GARGOYLES) == 0 && npc.getName().equalsIgnoreCase("gargoyle") && npc.getHealthRatio() < 3) {
Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ROCK_HAMMER, legacyNpc);
Rs2Player.waitForAnimation();
Rs2Inventory.useItemOnNpc(itemId, legacyNpc);
Rs2Player.waitForAnimation();
}

static int finishingItemFor(String name, int healthRatio, IntUnaryOperator varbits) {
if (name == null) return -1;
List<String> lizardVariants = Arrays.asList("Lizard", "Desert Lizard", "Small Lizard");
if (varbits.applyAsInt(SLAYER_AUTOKILL_DESERTLIZARDS) == 0 && lizardVariants.contains(name) && healthRatio < 5) {
return ItemID.SLAYER_ICY_WATER;
} else if (varbits.applyAsInt(SLAYER_AUTOKILL_ROCKSLUGS) == 0 && name.equalsIgnoreCase("rockslug") && healthRatio < 5) {
return ItemID.SLAYER_BAG_OF_SALT;
} else if (varbits.applyAsInt(SLAYER_AUTOKILL_GARGOYLES) == 0 && name.equalsIgnoreCase("gargoyle") && healthRatio < 3) {
return ItemID.SLAYER_ROCK_HAMMER;
}
return -1;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class SafeSpot extends Script {
public boolean run(AIOFighterConfig config) {
mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> {
try {
if (AIOFighterPlugin.getState().equals(State.BANKING) || AIOFighterPlugin.getState().equals(State.WALKING)) return;
if (isBlockedByState(AIOFighterPlugin.getState())) return;
if (!Microbot.isLoggedIn() || !super.run() || !config.toggleSafeSpot() || Rs2Player.isMoving()) return;

currentSafeSpot = config.safeSpot();
Expand Down Expand Up @@ -48,6 +48,10 @@ public boolean run(AIOFighterConfig config) {
return true;
}

static boolean isBlockedByState(State state) {
return state == State.BANKING || state == State.WALKING;
}

private boolean isDefaultSafeSpot(WorldPoint safeSpot) {
return safeSpot.getX() == 0 && safeSpot.getY() == 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,14 +197,25 @@ private void updateAttackStyle(int equippedWeaponType, int attackStyleIndex, int
}
}

private AttackStyle[] getWeaponTypeStyles(int weaponType) {
int weaponStyleEnum = Microbot.getEnum(EnumID.WEAPON_STYLES).getIntValue(weaponType);
int[] weaponStyleStructs = Microbot.getEnum(weaponStyleEnum).getIntVals();
static AttackStyle[] getWeaponTypeStyles(int weaponType) {
EnumComposition weaponStylesEnum = Microbot.getEnum(EnumID.WEAPON_STYLES);
if (weaponStylesEnum == null) {
return new AttackStyle[0];
}
EnumComposition weaponStyleEnum = Microbot.getEnum(weaponStylesEnum.getIntValue(weaponType));
if (weaponStyleEnum == null) {
return new AttackStyle[0];
}
int[] weaponStyleStructs = weaponStyleEnum.getIntVals();

AttackStyle[] styles = new AttackStyle[weaponStyleStructs.length];
int i = 0;
for (int style : weaponStyleStructs) {
String attackStyleName = Microbot.getStructComposition(style).getStringValue(ParamID.ATTACK_STYLE_NAME);
StructComposition struct = Microbot.getStructComposition(style);
String attackStyleName = struct == null ? null : struct.getStringValue(ParamID.ATTACK_STYLE_NAME);
if (attackStyleName == null) {
return new AttackStyle[0];
}
AttackStyle attackStyle = AttackStyle.valueOf(attackStyleName.toUpperCase());

if (attackStyle == AttackStyle.OTHER) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
@Slf4j
public class BankOrganizerPlugin extends Plugin
{
public static final String version = "1.0.0";
public static final String version = "1.0.1";

@Inject
private BankOrganizerConfig config;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.ItemComposition;
Expand Down Expand Up @@ -44,14 +45,15 @@ final class BankSnapshotReader
BankSnapshot read()
{
return Microbot.getClientThread().runOnClientThreadOptional(this::readOnClientThread)
.orElseThrow(() -> new IllegalStateException("Could not read bank snapshot on the client thread."));
.orElseThrow(() -> new IllegalStateException("Could not read bank snapshot on the client thread."))
.orElseThrow(() -> new IllegalStateException("Open your bank before running Bank Organizer."));
}

private BankSnapshot readOnClientThread()
private Optional<BankSnapshot> readOnClientThread()
{
if (!Rs2Bank.isOpen())
{
throw new IllegalStateException("Open your bank before running Bank Organizer.");
return Optional.empty();
}

List<Rs2ItemModel> bankItems = new ArrayList<>(Rs2Bank.bankItems());
Expand Down Expand Up @@ -83,7 +85,7 @@ private BankSnapshot readOnClientThread()
isEquipable(composition)));
}

return new BankSnapshot(stacks, tabCounts, client.getVarbitValue(Varbits.CURRENT_BANK_TAB));
return Optional.of(new BankSnapshot(stacks, tabCounts, client.getVarbitValue(Varbits.CURRENT_BANK_TAB)));
}

private int[] readTabCounts()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
@PluginDependency(ClueScrollPlugin.class)
public class ClueSolverPlugin extends Plugin {

final static String version = "1.0.5";
final static String version = "1.0.7";
@Inject
private ClueSolverScript clueSolverScript;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,11 @@ private boolean handleDialogue() {
Rs2Dialogue.clickContinue();
}

Rs2Keyboard.typeString(answer);
Rs2Keyboard.enter();
Rs2Dialogue.sleepUntilHasContinue();
if (answer != null && !answer.isBlank()) {
Rs2Keyboard.typeString(answer);
Rs2Keyboard.enter();
Rs2Dialogue.sleepUntilHasContinue();
}

if (Rs2Dialogue.hasContinue()) {
Rs2Dialogue.clickContinue();
Expand Down
Loading
Loading