Skip to content

Comments

feat(AIOFighter): Add wait-for-loot feature#1402

Merged
chsami merged 22 commits intochsami:developmentfrom
runsonmypc:aio-fighter-wait-for-loot
Sep 3, 2025
Merged

feat(AIOFighter): Add wait-for-loot feature#1402
chsami merged 22 commits intochsami:developmentfrom
runsonmypc:aio-fighter-wait-for-loot

Conversation

@runsonmypc
Copy link
Contributor

@runsonmypc runsonmypc commented Aug 19, 2025

Summary

This PR adds a configurable wait-for-loot feature to AIOFighter that pauses combat after killing an NPC to allow loot to appear before attacking the next target (useful for NPCs with long death animations such as dragons).

Features

  • Configurable toggle: Enable/disable via "Wait for Loot" option in the Loot section
  • Configurable timeout: Adjustable wait time from 1-10 seconds (default 6) via "Loot Wait Timeout" option
  • Smart detection: Continuously monitors NPC health during combat for reliable death detection
  • Loot prioritization: Prevents new combat from interrupting loot collection, even when attacked

Implementation Details

  1. Death detection:

    • Continuously tracks the NPC being fought throughout combat
    • Detects death when health reaches 0 or isDead() returns true
    • Avoids false positives from eating, walking, or running out of ammo
  2. Loot coordination:

    • Respects pauseAllScripts flag to prevent interrupting active looting
    • Clears wait state when loot is found or timeout expires
    • Prevents target switching while waiting to ensure loot priority
    • Status display shows countdown timer while waiting
  3. Files changed:

    • AIOFighterConfig.java: Added toggleWaitForLoot and configurable timeout options
    • AIOFighterPlugin.java: Added wait state management variables
    • AttackNpcScript.java: Implemented continuous NPC tracking and death detection
    • LootScript.java: Added wait state clearing when loot is found

Use Case

Essential for NPCs with:

  • Long death animations (e.g., dragons take 3-4 seconds)
  • Delayed loot drops
  • Valuable loot that justifies waiting

Summary by CodeRabbit

  • New Features

    • Option to wait for loot to appear before attacking the next NPC.
    • Configurable loot wait timeout (1–10 seconds).
  • Improvements

    • Combat pauses while waiting for loot and resumes on timeout or after first pickup.
    • Looting more robust: per-item pickup attempts, continue on failures, and ensure scripts resume.
    • Optional automatic food consumption when inventory is full.
    • State resets and clearer status/log messages across lifecycle.

Your Name and others added 3 commits July 23, 2025 17:17
Implements a configurable feature that makes the bot wait up to 6 seconds
after killing an NPC for loot to appear before attacking the next target.
This is useful for enemies with long death animations.

- Added toggleWaitForLoot config option in loot section
- Added tracking variables for NPC death time and waiting state
- Added ActorDeath event handler to detect when we kill an NPC
- Modified AttackNpcScript to pause combat while waiting for loot
- Shows countdown status while waiting
- 6-second hardcoded timeout to resume combat if no loot appears

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
  - Add check to respect pauseAllScripts flag during active looting
  - Improve NPC death detection by checking for lost target while in combat
  - Remove clearWaitingForLoot() calls that were causing premature combat resumption
  - Fix rapid clicking between loot and enemies during loot pickup
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 19, 2025

Walkthrough

Adds configurable loot-wait options and plugin-level wait state/timestamp, caches target NPC index in combat to detect death and enter a loot-wait mode with timeout, and updates looting to respect/clear that wait while handling space/eating and pause state.

Changes

Cohort / File(s) Summary of changes
Configuration: Loot wait options
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java
Adds toggleWaitForLoot() (boolean, default false) and lootWaitTimeout() (int, 1–10, default 6) config entries in the Loot section.
Plugin state & lifecycle
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java
Adds private static volatile lastNpcKilledTime and waitingForLoot with public getters/setters; adds clearWaitForLoot(String) to reset wait state and cached target; initializes/clears wait state on startup, scheduled init, and shutdown.
Combat flow: target tracking & loot wait
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java
Adds public static volatile int cachedTargetNpcIndex (init -1); early-return on global pause; caches interacting NPC index; detects cached target death to set waiting-for-loot and timestamp; when waiting, reports remaining time and resumes on timeout.
Loot flow: respect and clear wait
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java
Skips looting while in combat unless force-loot or waiting-for-loot; wraps looting pass to preserve/restore global pause state; per-item handling includes space/eat logic and pickup attempts; clears wait state on first successful pickup.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Combat as AttackNpcScript
  participant Plugin as AIOFighterPlugin
  participant Game as Game/NPC
  participant Loot as LootScript
  participant Items as Ground Items

  Combat->>Game: attack NPC
  Game-->>Combat: NPC reference (index)
  Note over Combat: cache target index

  Game-->>Combat: NPC dies
  Combat->>Plugin: setWaitingForLoot(true), setLastNpcKilledTime(now), clear cached index
  Note over Plugin: waiting-for-loot active

  alt Loot available before timeout
    Loot->>Plugin: isWaitingForLoot?
    Plugin-->>Loot: true
    Loot->>Items: attempt pickups (handle space/eat)
    Items-->>Loot: first pickup success
    Loot->>Plugin: clearWaitForLoot("First loot item picked up")
  else Timeout reached
    Combat->>Plugin: check elapsed >= lootWaitTimeout
    Plugin-->>Combat: timeout reached
    Combat->>Plugin: clearWaitForLoot("Timeout elapsed")
  end

  Combat->>Game: resume attack flow
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • g-mason0
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

private static long lastNpcKilledTime = 0;

@Getter @Setter
private static boolean waitingForLoot = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is there a waiting For Loot bool and a timer that you keep track of? It seems like just the time since kill should be enough to determine whether you're waiting for loot or not

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is to avoid the Fighter interrupting the loot.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, but you don't need both.
waitingForLoot can just be a function that checks if lastNpcKilledTime is within LOOT_WAIT_TIME

Actor currentInteracting = Rs2Player.getInteracting();

// If we're not interacting but were recently, the NPC probably just died
if (currentInteracting == null && Rs2Player.isInCombat()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a check that can trigger for a lot of reasons and should not be used to say that the npc died and thus start a waitforLoot timer.
You lose interacting when you're eating, walking, out of arrows. It generally doesn't work well with anyone that has auto retaliate off

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right this is a mistake, should just check if NPC hp hits 0.

// Handle wait for loot feature
if (config.toggleWaitForLoot()) {
NPC npc = npcDespawned.getNpc();
if (npc != null && npc.isDead()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like this PR can be reduced to:

  • cache npc were fighting
  • check if it dies through the NpcDespawned event
  • trigger a delay for the AttackNpcScript
  • end timeout early when loot is spawned

Looting script priority should automatically make sure that loot is picked up when the AttackNpcScript is delayed while no loot is spawned.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem is that the NPC takes too long to despawn, and in that time the Fighter will go attack the next NPC.

Copy link
Contributor

Choose a reason for hiding this comment

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

The problem is that the NPC takes too long to despawn, and in that time the Fighter will go attack the next NPC.

Out of curiosity, which npcs take 6 seconds to spawn loot?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It will stop waiting as soon as the loot spawns, 6 seconds is just a safeguard. Dragons take like 3 seconds idk, have not measured.

@Getter @Setter
private static boolean waitingForLoot = false;

public static final int LOOT_WAIT_TIMEOUT = 6000; // 6 seconds
Copy link
Contributor

Choose a reason for hiding this comment

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

6 seconds to wait for loot seems like a lot. Doesn't loot spawn immediately after the Npc is de spawned?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a timeout in case loot does not spawn. This is intended for use with NPCs with long death animations as per the PR desc

Copy link
Contributor

Choose a reason for hiding this comment

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

Let's say you're fighting an NPC that doesn't have a guaranteed drop (no bones or ashes). In this scenario you'll find yourself waiting 6 seconds whenever nothing spawns.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you can figure out the longest delay for something to die/loot to spawn I will set it to that. 6 seconds is to avoid missing edge cases. I did not foresee this being used on anything that does not have guaranteed drops (it is mainly intended for dragons and the like).

Pert added 2 commits August 21, 2025 09:43
… aio-fighter-wait-for-loot

# Conflicts:
#	runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java
  - Remove unreliable "lost target while in combat" detection that triggered on eating/walking
  - Track NPC continuously during combat instead of only on initial attack
  - Add configurable loot wait timeout (1-10 seconds, default 6)
  - Prevent target switching while waiting to ensure looting priority
  - Clear cached NPC properly when looting starts or timeout expires
@runsonmypc
Copy link
Contributor Author

Thanks for the feedback! I've addressed the concerns raised:

@Krulvis's suggestions addressed:

  1. "Just use timer, not boolean" - I kept both as they serve different purposes. The boolean provides clear state management while the timer tracks duration. Using only timestamps would require constant calculations throughout the code, making
    it less readable and more error-prone.
  2. Death detection improvements - Removed the problematic "lost target while in combat" check that was triggering falsely on eating/walking/running out of ammo. Now using continuous NPC tracking during combat for reliable detection.
  3. "6 seconds too long?" - Made the timeout configurable (1-10 seconds, default 6). Dragons need 3-4 seconds for death animation plus network delays, so 6 is a reasonable default that users can adjust.
  4. NpcDespawned not useful - Removed this approach entirely as it fires too late for NPCs with long death animations.

Key improvements:

  • Death detection now continuously tracks the NPC during combat rather than checking only on interaction loss
  • Added configurable timeout so users can adjust based on the NPCs they're fighting
  • Fixed issue where getting attacked while waiting would interrupt loot collection - now properly prioritizes looting
  • Cleaned up redundant code and simplified the overall implementation

  - Modified LootScript combat check to bypass restriction when waiting for loot
  - Ensures loot collection completes before engaging new targets
  - Fixes issue where wait timeout would expire without looting when attacked
if (!AIOFighterPlugin.isWaitingForLoot()) {
Actor currentInteracting = Rs2Player.getInteracting();
if (currentInteracting instanceof net.runelite.api.NPC) {
net.runelite.api.NPC npc = (net.runelite.api.NPC) currentInteracting;
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of using the NPC object, we should be comparing using Rs2NpcModel. You can compare an NPC & an Rs2NpcModel by using the getIndex() method on each of the objects.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (1)

69-91: pauseAllScripts must be released in a finally block. A thrown exception can freeze the bot.

If an exception occurs after pauseAllScripts is set to true, the catch path never resets it, leaving other scripts stuck. Ensure release in finally.

Apply this diff:

-                //Pause other scripts before looting
-                Microbot.pauseAllScripts.getAndSet(true);
-                for (GroundItem groundItem : groundItems) {
-                    ...
-                }
-                Microbot.log("Looting complete");
-                Microbot.pauseAllScripts.compareAndSet(true, false);
+                // Pause other scripts before looting and always release
+                Microbot.pauseAllScripts.getAndSet(true);
+                try {
+                    for (GroundItem groundItem : groundItems) {
+                        ...
+                    }
+                    Microbot.log("Looting complete");
+                } finally {
+                    Microbot.pauseAllScripts.compareAndSet(true, false);
+                }
♻️ Duplicate comments (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java (1)

72-77: waitingForLoot/lastNpcKilledTime are shared across scheduled tasks without visibility guarantees. Mark volatile (or use atomics).

These flags are read/written by multiple executor tasks (AttackNpcScript, LootScript). Without volatile/atomic semantics, you risk stale reads or reordering. Make the fields volatile (minimal change) or use AtomicBoolean/AtomicLong.

Apply this diff:

-    @Getter @Setter
-    private static long lastNpcKilledTime = 0;
+    @Getter @Setter
+    private static volatile long lastNpcKilledTime = 0;
@@
-    @Getter @Setter
-    private static boolean waitingForLoot = false;
+    @Getter @Setter
+    private static volatile boolean waitingForLoot = false;

Optional follow-ups:

  • Reset these on startup/shutdown to avoid stale state across reloads.
  • Centralize “clear wait” behavior to one place to avoid duplication.

Add this lifecycle reset (outside the changed hunk):

// In startUp(), near existing initialization:
AIOFighterPlugin.setWaitingForLoot(false);
AIOFighterPlugin.setLastNpcKilledTime(0L);

// In shutDown():
AIOFighterPlugin.setWaitingForLoot(false);
AIOFighterPlugin.setLastNpcKilledTime(0L);

Centralized helper (optional):

// Add to AIOFighterPlugin
public static void clearWaitForLoot(String reason) {
    setWaitingForLoot(false);
    setLastNpcKilledTime(0L);
    // Keep this method as the single point of truth; callers can clear combat cache if needed.
    Microbot.log("Clearing wait-for-loot state" + (reason != null ? ": " + reason : ""));
}

Krulvis previously suggested deriving “waiting” purely from time since kill. If you want to simplify state, you could drop waitingForLoot and keep only a deadline (e.g., waitingUntilEpochMs > now). Not mandatory, but it removes a second source of truth.

runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (1)

112-123: Bug: Wrong instanceof/cast for currentInteracting; should use Rs2NpcModel, not net.runelite.api.NPC

Rs2Player.getInteracting() wraps NPCs into Rs2NpcModel. Checking against net.runelite.api.NPC will never match, so the cache won’t update and death detection may never arm. This aligns with prior feedback to use Rs2NpcModel and compare via getIndex().

Apply:

-                if (!AIOFighterPlugin.isWaitingForLoot()) {
-                    Actor currentInteracting = Rs2Player.getInteracting();
-                    if (currentInteracting instanceof net.runelite.api.NPC) {
-                        net.runelite.api.NPC npc = (net.runelite.api.NPC) currentInteracting;
-                        // Update our cached target to who we're fighting
-                        if (npc.getHealthRatio() > 0 && !npc.isDead()) {
-                            cachedTargetNpcIndex = npc.getIndex();
-                        }
-                    }
-                }
+                if (!AIOFighterPlugin.isWaitingForLoot()) {
+                    Object currentInteracting = Rs2Player.getInteracting();
+                    if (currentInteracting instanceof Rs2NpcModel) {
+                        Rs2NpcModel npc = (Rs2NpcModel) currentInteracting;
+                        // Update our cached target to who we're fighting
+                        if (npc.getHealthRatio() > 0 && !npc.isDead()) {
+                            cachedTargetNpcIndex = npc.getIndex();
+                        }
+                    }
+                }

Note: If this change renders the net.runelite.api.Actor import unused, let the linter tidy it up separately (not blocking).

🧹 Nitpick comments (6)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java (1)

389-393: Depositing containers: ordering is fine; consider a small utility for maintainability.

The added Rs2Bank.depositLootingBag() call in this block is safe given the preceding isOpen() guard. As a readability/maintenance tweak, consider grouping these four calls behind a helper (e.g., Rs2Bank.emptyAllContainers()) to keep call sites concise and consistent across features that bank/empty containers.

runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java (1)

401-422: Config options look good; minor UX copy tweak.

Everything here wires cleanly. Optional: rename the label to “Loot Wait Timeout (s)” so units are obvious in the UI.

-            name = "Loot Wait Timeout",
+            name = "Loot Wait Timeout (s)",
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2Food.java (1)

67-70: Prefer ItemID constants over magic numbers for new blighted foods.

Using ItemID.* makes this resilient to cache changes and improves readability.

Apply this diff:

-    BLIGHTED_MANTA_RAY(24589, 22, "Blighted manta ray", 3),
-    BLIGHTED_ANGLERFISH(24592, 22, "Blighted anglerfish", 3),
-    BLIGHTED_KARAMBWAN(24595, 18, "Blighted karambwan", 3);
+    BLIGHTED_MANTA_RAY(ItemID.BLIGHTED_MANTA_RAY, 22, "Blighted manta ray", 3),
+    BLIGHTED_ANGLERFISH(ItemID.BLIGHTED_ANGLERFISH, 22, "Blighted anglerfish", 3),
+    BLIGHTED_KARAMBWAN(ItemID.BLIGHTED_KARAMBWAN, 18, "Blighted karambwan", 3);
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (1)

62-68: Clear-wait timing: move reset until after a successful loot interaction to avoid premature resumption.

Currently you clear the wait state as soon as any candidate loot is detected, before interacting. If an exception occurs or the list contains unrelated items, AttackNpcScript could resume one tick earlier than intended (especially if pauseAllScripts flips late).

Suggest clearing after the first successful pickup (or call a centralized helper). Example:

-                // Clear wait for loot state since we found loot
-                if (AIOFighterPlugin.isWaitingForLoot()) {
-                    AIOFighterPlugin.setWaitingForLoot(false);
-                    AIOFighterPlugin.setLastNpcKilledTime(0);
-                    AttackNpcScript.cachedTargetNpcIndex = -1; // Clear the cached NPC index
-                    Microbot.log("Loot found, clearing wait state");
-                }
+                // Defer clearing wait-for-loot until we successfully pick at least one item
                 //Pause other scripts before looting
                 Microbot.pauseAllScripts.getAndSet(true);
-                for (GroundItem groundItem : groundItems) {
+                boolean clearedWait = false;
+                for (GroundItem groundItem : groundItems) {
                     if (Rs2Inventory.emptySlotCount() <= minFreeSlots && !canStackItem(groundItem)) {
                         Microbot.log("Unable to pick loot: " + groundItem.getName() + " making space");
                         if (!config.eatFoodForSpace()) {
                             continue;
                         }
                         int emptySlots = Rs2Inventory.emptySlotCount();
                         if (Rs2Player.eatAt(100)) {
                             sleepUntil(() -> emptySlots < Rs2Inventory.emptySlotCount(), 1200);
                         }
                     }
                     Microbot.log("Picking up loot: " + groundItem.getName());
-                    if (!waitForGroundItemDespawn(() -> interact(groundItem), groundItem)) {
+                    if (!waitForGroundItemDespawn(() -> interact(groundItem), groundItem)) {
                         return;
                     }
+                    if (!clearedWait && AIOFighterPlugin.isWaitingForLoot()) {
+                        // Either call a centralized helper or inline the reset
+                        AIOFighterPlugin.setWaitingForLoot(false);
+                        AIOFighterPlugin.setLastNpcKilledTime(0);
+                        AttackNpcScript.cachedTargetNpcIndex = -1;
+                        Microbot.log("Loot picked up, clearing wait state");
+                        clearedWait = true;
+                    }
                 }

If you introduce AIOFighterPlugin.clearWaitForLoot(String), call it here instead of duplicating the reset.

runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (2)

124-136: Death detection logic looks solid; consider extracting a helper

Using Rs2Npc.getNpcByIndex and the combined isDead || (healthRatio==0 && healthScale>0) check is robust against “lost interaction” false-positives. Consider extracting this condition into a hasDied(Rs2NpcModel) helper to keep the run-loop lean and reuse elsewhere.


138-154: Loot wait reset confirmed; clamp countdown and consider monotonic timing

Verification shows that LootScript correctly clears both the waiting flag and the cached NPC index when loot is found:

  • LootScript.java (lines 63–69):
    • AIOFighterPlugin.setWaitingForLoot(false)
    • AIOFighterPlugin.setLastNpcKilledTime(0)
    • AttackNpcScript.cachedTargetNpcIndex = -1

Next steps:

• In AttackNpcScript.java, clamp the remaining-seconds calculation to avoid negative UI values. For example, around line 151:

-   int secondsLeft = (int)((timeoutMs - timeSinceKill) / 1000);
+   int secondsLeft = (int) Math.max(0, TimeUnit.MILLISECONDS.toSeconds(timeoutMs - timeSinceKill));

• (Optional) For true monotonic timing, migrate last-kill timestamps and comparisons to use System.nanoTime() (with appropriate conversions via TimeUnit).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c5ba4f9 and 91ac511.

📒 Files selected for processing (6)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (3 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (3 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2Food.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (1)
  • AttackNpcScript (43-256)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
  • Rs2Player (67-2062)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
  • Rs2Player (67-2062)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (3)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (1)

42-44: Good gating: allow looting while “waiting for loot,” otherwise respect in-combat block.

This matches the feature’s intent and avoids interrupting looting when a new attacker tags you during the wait.

runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (2)

107-111: Good guard: respect pauseAllScripts during active looting

This early return prevents combat from interrupting an ongoing loot action. Solid placement before target selection.


175-175: No-op formatting change

Blank line is fine and improves readability before NPC selection.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (1)

112-122: Tracking the currently-interacted NPC fixes prior false positives

Updating the cached target only while actively interacting and the target is alive aligns with earlier feedback to stop relying on “lost interaction.” This should remove spurious death detections during eating/walking/out-of-ammo.

🧹 Nitpick comments (5)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (2)

125-136: Set a status message immediately upon entering wait-for-loot

Minor UX nit: set Microbot.status as soon as we flip into waiting to avoid a one-tick gap before the countdown shows.

-                        AIOFighterPlugin.setWaitingForLoot(true);
-                        AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis());
-                        Microbot.log("NPC died, waiting for loot...");
+                        AIOFighterPlugin.setWaitingForLoot(true);
+                        AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis());
+                        Microbot.status = "Waiting for loot...";
+                        Microbot.log("NPC died, waiting for loot...");

138-154: Consolidate timeout cleanup via plugin helper; clamp countdown for nicer UX

  • Consider using AIOFighterPlugin.clearWaitForLoot("Timeout reached") for symmetry with LootScript and to centralize state resets.
  • Optional: display at least 1s while waiting to reduce 0s flicker at the end.
-                    int timeoutMs = config.lootWaitTimeout() * 1000;
+                    final int timeoutMs = config.lootWaitTimeout() * 1000;
                     if (timeSinceKill >= timeoutMs) {
-                        // Timeout reached, resume combat
-                        AIOFighterPlugin.setWaitingForLoot(false);
-                        AIOFighterPlugin.setLastNpcKilledTime(0);
-                        cachedTargetNpcIndex = -1; // Clear cached NPC on timeout
-                        Microbot.log("Loot wait timeout reached, resuming combat");
+                        // Timeout reached, resume combat
+                        AIOFighterPlugin.clearWaitForLoot("Loot wait timeout reached");
+                        cachedTargetNpcIndex = -1; // Clear cached NPC on timeout
+                        Microbot.log("Loot wait timeout reached, resuming combat");
                     } else {
                         // Still waiting for loot, don't attack
-                        int secondsLeft = (int) Math.max(0, TimeUnit.MILLISECONDS.toSeconds(timeoutMs - timeSinceKill));
+                        int secondsLeft = (int) Math.max(1, TimeUnit.MILLISECONDS.toSeconds(timeoutMs - timeSinceKill));
                         Microbot.status = "Waiting for loot... " + secondsLeft + "s";
                         return;
                     }
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (3)

9-9: Remove unused import AttackNpcScript

The class is not referenced in this file. Dropping the import avoids a warning and clarifies dependencies.

-import net.runelite.client.plugins.microbot.aiofighter.combat.AttackNpcScript;

62-91: try/finally around pauseAllScripts prevents stuck-pauses; small robustness tweaks

The always-release in finally prevents deadlocks if pickup fails mid-loop. A couple of small improvements to consider:

  • Gate the “Picked up” log to debug or reduce frequency to avoid log spam in high-drop scenarios.
  • If waitForGroundItemDespawn fails on one item, consider continuing to the next instead of returning early, unless there’s a known transient failure that requires a retry loop.
-                        Microbot.log("Picking up loot: " + groundItem.getName());
+                        // Consider lowering verbosity or guarding behind a debug flag
+                        Microbot.log("Picking up loot: " + groundItem.getName());
-                        if (!waitForGroundItemDespawn(() -> interact(groundItem), groundItem)) {
-                            return;
-                        }
+                        if (!waitForGroundItemDespawn(() -> interact(groundItem), groundItem)) {
+                            // Optional: skip this item and continue rather than aborting the whole pass
+                            continue;
+                        }

68-76: Space-making via eatAt(100) may consume food aggressively

Out of scope for this PR, but eating at 100% will consume any available food even at full HP. If that’s intended for “make space,” consider a separate helper (e.g., eatAnyFoodForSpace) to avoid conflating combat-healing thresholds with inventory management.

Would you like a follow-up PR to introduce a dedicated “make space” helper that prefers low-heal foods and avoids hitting food you’re trying to keep for combat?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 91ac511 and ee5d188.

📒 Files selected for processing (4)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java (3 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (3 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java
🧰 Additional context used
🧬 Code graph analysis (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (3)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (1)
  • AttackNpcScript (43-256)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
  • Rs2Player (67-2062)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java (1)
  • Rs2Inventory (60-2244)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
  • Rs2Player (67-2062)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (3)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (2)

107-111: Pause guard integrates cleanly with looting

Early-returning on Microbot.pauseAllScripts prevents new attacks during active looting. This is the right choke point and keeps the combat loop cheap while looting is in progress.


48-48: Thread-safety for cross-thread flags confirmed
I’ve verified that AIOFighterPlugin’s key fields are already marked volatile, so no further changes are needed here.

• In AIOFighterPlugin.java (73–76), private static volatile long lastNpcKilledTime and private static volatile boolean waitingForLoot are correctly declared volatile.
• In AttackNpcScript.java (48), public static volatile int cachedTargetNpcIndex remains volatile as intended.

runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (1)

42-44: In-combat looting now correctly respects wait-for-loot

Allowing looting to proceed when AIOFighterPlugin.isWaitingForLoot() is true ensures the first drop won’t be preempted by combat. Matches the feature’s intent.

@runsonmypc
Copy link
Contributor Author

Addressed the new coderabbit comments

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (1)

67-76: Re-check inventory after eating and use fast-food path to reduce latency.

Currently, you attempt pickup even if a slot wasn’t actually freed. Re-check space post-eat and skip the item if still blocked. Also, leverage the fastFood path to minimize time-to-free-slot while waiting for loot.

Apply this diff:

-                        if (Rs2Inventory.emptySlotCount() <= minFreeSlots && !canStackItem(groundItem)) {
+                        if (Rs2Inventory.emptySlotCount() <= minFreeSlots && !canStackItem(groundItem)) {
                             Microbot.log("Unable to pick loot: " + groundItem.getName() + " making space");
                             if (!config.eatFoodForSpace()) {
                                 continue;
                             }
                             int emptySlots = Rs2Inventory.emptySlotCount();
-                            if (Rs2Player.eatAt(100)) {
+                            if (Rs2Player.eatAt(100, true)) {
                                 sleepUntil(() -> emptySlots < Rs2Inventory.emptySlotCount(), 1200);
                             }
+                            // If we still don't have space and can't stack this item, skip it
+                            if (Rs2Inventory.emptySlotCount() <= minFreeSlots && !canStackItem(groundItem)) {
+                                continue;
+                            }
                         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ee5d188 and 93ec43b.

📒 Files selected for processing (2)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (3 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java
🧰 Additional context used
🧬 Code graph analysis (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
  • Rs2Player (67-2062)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java (1)
  • Rs2Inventory (60-2244)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (3)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (3)

41-43: Guard now correctly allows looting during wait-for-loot.

The added AIOFighterPlugin.isWaitingForLoot() exception to the in-combat early return aligns with the PR goal: continue looting while waiting and otherwise defer during combat unless force-loot is enabled. Looks good.


78-81: Good partial-failure handling during pickup.

Skipping a failed item and continuing the pass avoids aborting the entire looting cycle when a single item is contested or unreachable. This improves robustness.


83-86: Clear-on-first-pickup behavior is correct — volatile fields provide the needed atomicity/visibility.

Short verification: waitingForLoot and lastNpcKilledTime are declared volatile and accessed via Lombok-generated accessors; clearWaitForLoot writes those volatiles and clears the cached NPC index (also volatile). Single-variable reads/writes are atomic and visible across threads, so the clear-after-first-pickup call is thread-safe for these flags/timers.

Files/locations inspected:

  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java
    • private static volatile long lastNpcKilledTime = 0;
    • private static volatile boolean waitingForLoot = false; (@Getter/@Setter → isWaitingForLoot/setWaitingForLoot)
    • public static void clearWaitForLoot(String reason) { setWaitingForLoot(false); setLastNpcKilledTime(0L); AttackNpcScript.cachedTargetNpcIndex = -1; }
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java
    • public static volatile int cachedTargetNpcIndex = -1;
    • Reads AIOFighterPlugin.isWaitingForLoot() and getLastNpcKilledTime() when deciding timeouts/behavior.

Optional note (no blocker): if you need the three fields to change as one indivisible state (no chance of transient inconsistency across multiple variables), synchronize the update or use an atomic composite; otherwise the current volatile-based approach is sufficient.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (7)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (5)

107-110: Redundant pause guard (super.run already checks pauseAllScripts)

super.run() returns false when Microbot.pauseAllScripts is true, so this additional check is redundant.

Apply:

-                // Check if we should pause while looting is happening
-                if (Microbot.pauseAllScripts.get()) {
-                    return; // Don't attack while looting
-                }
+                // super.run() already returns false when pauseAllScripts is true

144-147: Avoid double-resets of cachedTargetNpcIndex

If AIOFighterPlugin.clearWaitForLoot(...) already resets AttackNpcScript.cachedTargetNpcIndex, the additional assignment is redundant.

Apply:

-                        AIOFighterPlugin.clearWaitForLoot("Loot wait timeout reached, resuming combat");
-                        cachedTargetNpcIndex = -1; // Clear cached NPC on timeout
+                        AIOFighterPlugin.clearWaitForLoot("Loot wait timeout reached, resuming combat");

139-153: Disable wait immediately if the user turns off the toggle at runtime

If the user disables “Wait for Loot” while already waiting, the script will continue waiting until timeout. Clear the wait state immediately to reflect the new configuration.

Apply:

-                // Check if we're waiting for loot
-                if (config.toggleWaitForLoot() && AIOFighterPlugin.isWaitingForLoot()) {
+                // Check if we're waiting for loot
+                if (AIOFighterPlugin.isWaitingForLoot()) {
+                    if (!config.toggleWaitForLoot()) {
+                        AIOFighterPlugin.clearWaitForLoot("Wait disabled by config");
+                        return;
+                    }
                     long timeSinceKill = System.currentTimeMillis() - AIOFighterPlugin.getLastNpcKilledTime();

149-151: Use ceil for countdown to avoid under-reporting remaining seconds

Math.max(1, toSeconds(...)) floors the value; showing 5s when 5.9s remain can feel jumpy. Prefer ceil.

Apply:

-                        int secondsLeft = (int) Math.max(1, TimeUnit.MILLISECONDS.toSeconds(timeoutMs - timeSinceKill));
+                        int secondsLeft = (int) Math.ceil((timeoutMs - timeSinceKill) / 1000.0);

45-55: Remove unused currentNpc

currentNpc is assigned only by skipNpc() and not read elsewhere in this class. If other components don’t use it, drop it to reduce surface area; otherwise document intended usage.

Apply:

-    public static Actor currentNpc = null;
-    public static void skipNpc() {
-        currentNpc = null;
-    }
+    // Removed unused currentNpc; reintroduce if another component requires it.
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (2)

61-65: Prefer using Rs2GroundItem.runWhilePaused to centralize pause/resume semantics

You already solved the pause restoration here; however, Rs2GroundItem exposes runWhilePaused helpers that encapsulate pausing behavior and reduce repetition.

Sketch:

Rs2GroundItem.runWhilePaused(() -> {
  boolean clearedWait = false;
  for (GroundItem groundItem : groundItems) {
    ...
    if (!clearedWait && AIOFighterPlugin.isWaitingForLoot()) {
      AIOFighterPlugin.clearWaitForLoot("First loot item picked up");
      clearedWait = true;
    }
  }
  Microbot.log("Looting complete");
  return true;
});

Also applies to: 81-91


135-137: Nit: remove leading space in " ashes"

The leading space is harmless due to trimming later, but it’s confusing at first glance.

Apply:

-            itemNamesToLoot.add(" ashes");
+            itemNamesToLoot.add("ashes");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 93ec43b and 285c006.

📒 Files selected for processing (3)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (1 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java
🧰 Additional context used
🧬 Code graph analysis (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (3)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java (1)
  • Rs2Inventory (60-2244)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/npc/Rs2NpcManager.java (1)
  • Rs2NpcManager (26-349)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
  • Rs2Player (67-2062)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java (1)
  • Rs2Inventory (60-2244)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
  • Rs2Player (67-2062)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (6)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (3)

125-132: Ensure Rs2Npc.getNpcByIndex(...) is called on the client thread

Depending on implementation, direct NPC table access must occur on the client thread to avoid race conditions and null/stale reads. Verify Rs2Npc.getNpcByIndex(...) internally marshals to client thread; otherwise wrap the lookup.

Apply (if needed):

-                    Rs2NpcModel cachedNpcModel = Rs2Npc.getNpcByIndex(cachedTargetNpcIndex);
+                    Rs2NpcModel cachedNpcModel = Microbot.getClientThread().invokeLater(() -> Rs2Npc.getNpcByIndex(cachedTargetNpcIndex)).join();

If invokeLater().join() isn’t available, provide a helper in Rs2Npc that guarantees client-thread execution.


139-147: Make waiting flags/time cross-thread safe

AIOFighterPlugin.isWaitingForLoot() and getLastNpcKilledTime() are accessed from multiple scheduled tasks. Ensure they’re volatile or use AtomicBoolean/AtomicLong. Otherwise, stale reads can break the wait/timeout logic.

Proposed changes in AIOFighterPlugin.java:

- private static boolean waitingForLoot;
- private static long lastNpcKilledTime;
+ private static volatile boolean waitingForLoot;
+ private static volatile long lastNpcKilledTime;

Optional: encapsulate both in a small immutable state object and swap via AtomicReference to keep them consistent.


166-170: Confirm interaction between antiban cooldown and wait-for-loot

When actionCooldownActive is set, this branch runs and returns even if “Wait for Loot” just triggered earlier in the tick (but before cooldown toggled). Given the earlier early-return while waiting, it’s probably fine, but please confirm intended priority is “wait > antiban cooldown work”.

If “wait” should always win, add a quick guard at the top of this branch:

-                if (Rs2AntibanSettings.actionCooldownActive) {
+                if (Rs2AntibanSettings.actionCooldownActive) {
+                    if (AIOFighterPlugin.isWaitingForLoot()) return;
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (3)

63-65: Good fix: restore pauseAllScripts to its prior state

Capturing the previous value and restoring it in finally prevents unintentionally unpausing other components. This addresses the earlier review note.

Also applies to: 94-95


41-43: Loot-in-combat guard correctly respects waiting-for-loot

The early return now allows looting during the wait window even if in combat. Matches the PR’s “loot prioritization” objective.


67-80: Space-creation loop: confirm we won’t thrash when minFreeSlots > 0

When minFreeSlots is configured and inventory is nearly full, the eat-for-space branch can run repeatedly per item. Consider short-circuiting once we cannot increase slots beyond minFreeSlots after a single eat attempt to avoid unnecessary retries across items in the same pass.

Optional tweak:

-                            if (Rs2Player.eatAt(100, true)) {
+                            if (Rs2Player.eatAt(100, true)) {
                                 sleepUntil(() -> emptySlots < Rs2Inventory.emptySlotCount(), 1200);
                             }
-                            // If we still don't have space and can't stack this item, skip it
+                            // If we still don't have space and can't stack this item, skip it and avoid reattempt in this pass
                             if (Rs2Inventory.emptySlotCount() <= minFreeSlots && !canStackItem(groundItem)) {
-                                continue;
+                                continue;
                             }

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (1)

63-65: Pause handling fixed; consider token-based pauser for multi-actor safety

Capturing and restoring the prior pause state avoids unpausing other components—good fix. If multiple systems can pause concurrently, a reference-counted or token-based pauser would make this bulletproof against interleavings where several actors overlap in time.

Example approach (conceptual):

  • Use an AtomicInteger counter; increment on pause request; decrement on release; scripts read paused = counter.get() > 0.
  • Or issue pause tokens that must be returned, ensuring balanced acquire/release across actors.

Also applies to: 93-95

🧹 Nitpick comments (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (2)

107-111: Maintain wait-for-loot timer even while paused to avoid stale state

When LootScript holds the global pause, AttackNpcScript returns early and won’t update/expire the wait timer. Optional: keep servicing the timer while paused to prevent edge cases where a long pause hides timeout progress.

Apply:

-                // Check if we should pause while looting is happening
-                if (Microbot.pauseAllScripts.get()) {
-                    return; // Don't attack while looting
-                }
+                // Check if we should pause while looting is happening
+                if (Microbot.pauseAllScripts.get()) {
+                    // While paused, still maintain/expire the wait-for-loot timer to avoid stale state
+                    if (config.toggleWaitForLoot() && AIOFighterPlugin.isWaitingForLoot()) {
+                        long timeSinceKill = System.currentTimeMillis() - AIOFighterPlugin.getLastNpcKilledTime();
+                        int timeoutMs = config.lootWaitTimeout() * 1000;
+                        if (timeSinceKill >= timeoutMs) {
+                            AIOFighterPlugin.clearWaitForLoot("Loot wait timeout reached (while paused)");
+                            cachedTargetNpcIndex = -1;
+                        } else {
+                            int secondsLeft = (int) Math.max(1, TimeUnit.MILLISECONDS.toSeconds(timeoutMs - timeSinceKill));
+                            Microbot.status = "Waiting for loot... " + secondsLeft + "s";
+                        }
+                    }
+                    return; // Don't attack while looting
+                }

139-153: Timeout/resume flow looks solid; consider earlier guards to save work

The countdown/status update and timeout-based resume are correct. Optional: move both the pause guard and the wait-for-loot handling ahead of the expensive NPC filtering/sorting (lines 79–103) to avoid doing heavy work when we’re known to be paused or waiting.

High-level idea:

  • Right after the top-level run guards, handle:
    • wait-for-loot timeout/status (return if still waiting),
    • then pause guard (return if paused),
    • then target-caching/death-detection,
    • only then build/scan attackable NPC lists.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 285c006 and 750385a.

📒 Files selected for processing (3)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java (3 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (3 hunks)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java
🧰 Additional context used
🧬 Code graph analysis (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (1)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
  • Rs2Player (67-2062)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (2)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/player/Rs2Player.java (1)
  • Rs2Player (67-2062)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java (1)
  • Rs2Inventory (60-2244)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (5)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java (2)

41-43: Combat guard correctly exempts wait-for-loot/force-loot

The early return now respects both Force Loot and the new wait-for-loot state, so looting can proceed immediately after a kill without being blocked by incidental combat state. This aligns with the PR's goal of prioritizing loot right after the NPC dies.


86-90: Clear wait state on first successful pickup — matches intended lifecycle

Clearing the wait-for-loot state after the first successful pickup prevents lingering “waiting…” status once loot begins. The local clearedWait flag correctly avoids multiple clears.

runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java (3)

48-48: Make cachedTargetNpcIndex volatile — correct for cross-thread visibility

Marking the shared NPC index as volatile is the right call given concurrent access from attack/loot scheduled tasks.


113-122: Continuous target caching during combat — avoids false positives

Caching by NPC index only while actively interacting and the target is alive eliminates prior false positives from eating/walking/ammo. Good alignment with reviewer feedback.


125-137: Death detection via cached index — robust against interaction loss

Checking the cached NPC by index and gating on isDead() or (healthRatio==0 && healthScale>0) provides reliable kill detection, especially for long death animations. Clearing the cache and entering wait state is correct.

return;
}
if (Rs2Player.isInCombat() && !config.toggleForceLoot()) {
if (Rs2Player.isInCombat() && !config.toggleForceLoot() && !AIOFighterPlugin.isWaitingForLoot()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

So, if we dont have toggleForceLoot enabled & we dont have isWaitingForLoot, we just aren't looting at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's:

  • Skip looting if: in combat AND force loot is off AND NOT waiting for loot
  • This means: Allow looting when waiting for loot, even if in combat

@chsami chsami merged commit 51d4777 into chsami:development Sep 3, 2025
2 checks passed
@chsami chsami mentioned this pull request Sep 3, 2025
@coderabbitai coderabbitai bot mentioned this pull request Sep 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants