From 58b2a8474ea7a6b05c7b0009678d8256483023b1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 23 Jul 2025 17:17:10 -0400 Subject: [PATCH 01/16] feat(AIOFighter): Add Wait for Loot feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../microbot/aiofighter/AIOFighterConfig.java | 1995 +++++++++-------- .../microbot/aiofighter/AIOFighterPlugin.java | 908 ++++---- .../aiofighter/combat/AttackNpcScript.java | 321 +-- .../microbot/aiofighter/enums/State.java | 22 +- .../microbot/aiofighter/loot/LootScript.java | 400 ++-- 5 files changed, 1852 insertions(+), 1794 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java index 1d2b5dd5db6..447053c4e8c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java @@ -1,992 +1,1003 @@ -package net.runelite.client.plugins.microbot.aiofighter; - -import net.runelite.api.coords.WorldPoint; -import net.runelite.client.config.*; -import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.PlayStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.PrayerStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.State; -import net.runelite.client.plugins.microbot.inventorysetups.InventorySetup; -import net.runelite.client.plugins.microbot.util.magic.Rs2CombatSpells; -import net.runelite.client.plugins.microbot.util.slayer.enums.SlayerMaster; - -@ConfigGroup(AIOFighterConfig.GROUP) -@ConfigInformation("1. Make sure to place the cannon first before starting the plugin.
" + - "2. Use food also supports Guthan's healing, the shield weapon is default set to Dragon Defender.
" + - "3. Prayer, Combat, Ranging & AntiPoison potions are supported.
" + - "4. Items to loot based your requirements.
" + - "5. You can turn auto attack NPC off if you have a cannon.
" + - "6. PrayFlick in different styles.
" + - "7. SafeSpot you can Shift Right-click the ground to select the tile.
" + - "8. Right-click NPCs to add them to the attack list.
") -public interface AIOFighterConfig extends Config { - - String GROUP = "PlayerAssistant"; - - @ConfigSection( - name = "Combat", - description = "Combat", - position = 10, - closedByDefault = false - ) - String combatSection = "Combat"; - @ConfigSection( - name = "Slayer", - description = "Slayer", - position = 11, - closedByDefault = true - ) - String slayerSection = "Slayer"; - @ConfigSection( - name = "Banking", - description = "Banking settings", - position = 992, - closedByDefault = false - ) - String banking = "Banking"; - //Gear section - @ConfigSection( - name = "Gear", - description = "Gear", - position = 55, - closedByDefault = true - ) - String gearSection = "Gear"; - // Safety section - @ConfigSection( - name = "Safety", - description = "Safety", - position = 54, - closedByDefault = true - ) - String safetySection = "Safety"; - - @ConfigSection( - name = "Food & Potions", - description = "Food & Potions", - position = 20, - closedByDefault = false - ) - String foodAndPotionsSection = "Food & Potions"; - @ConfigSection( - name = "Loot", - description = "Loot", - position = 30, - closedByDefault = false - ) - String lootSection = "Loot"; - //Prayer section - @ConfigSection( - name = "Prayer", - description = "Prayer", - position = 40, - closedByDefault = false - ) - String prayerSection = "Prayer"; - //Skilling section - @ConfigSection( - name = "Skilling", - description = "Skilling", - position = 50, - closedByDefault = false - ) - String skillingSection = "Combat Skilling"; - - @ConfigItem( - keyName = "Combat", - name = "Auto attack npc", - description = "Attacks npc", - position = 0, - section = combatSection - ) - default boolean toggleCombat() { - return false; - } - - @ConfigItem( - keyName = "monster", - name = "Attackable npcs", - description = "List of attackable npcs", - position = 1, - section = combatSection - ) - default String attackableNpcs() { - return ""; - } - - @ConfigItem( - keyName = "Attack Radius", - name = "Attack Radius", - description = "The max radius to attack npcs", - position = 2, - section = combatSection - ) - default int attackRadius() { - return 10; - } - - @ConfigItem( - keyName = "Use special attack", - name = "Use special attack", - description = "Use special attack", - position = 3, - section = combatSection - ) - default boolean useSpecialAttack() { - return false; - } - - @ConfigItem( - keyName = "Cannon", - name = "Auto reload cannon", - description = "Automatically reloads cannon", - position = 4, - section = combatSection - ) - default boolean toggleCannon() { - return false; - } - - //safe spot - @ConfigItem( - keyName = "Safe Spot", - name = "Safe Spot", - description = "Shift Right-click the ground to select the safe spot tile", - position = 5, - section = combatSection - ) - default boolean toggleSafeSpot() { - return false; - } - - //PlayStyle - @ConfigItem( - keyName = "PlayStyle", - name = "Play Style", - description = "Play Style", - position = 6, - section = combatSection - ) - default PlayStyle playStyle() { - return PlayStyle.AGGRESSIVE; - } - - @ConfigItem( - keyName = "ReachableNpcs", - name = "Only attack reachable npcs", - description = "Only attack npcs that we can reach with melee", - position = 7, - section = combatSection - ) - default boolean attackReachableNpcs() { - return true; - } - - @ConfigItem( - keyName = "Food", - name = "Auto eat food", - description = "Automatically eats food", - position = 0, - section = foodAndPotionsSection - ) - default boolean toggleFood() { - return false; - } - - // Testing if full auto potion manager is preferred over individual potion toggles - -// @ConfigItem( -// keyName = "Auto Prayer Potion", -// name = "Auto prayer potion", -// description = "Automatically drinks prayer potions", -// position = 1, -// section = foodAndPotionsSection -// ) -// default boolean togglePrayerPotions() { -// return false; -// } -// -// @ConfigItem( -// keyName = "Combat potion", -// name = "Auto combat potion", -// description = "Automatically drinks combat potions", -// position = 2, -// section = foodAndPotionsSection -// ) -// default boolean toggleCombatPotion() { -// return false; -// } -// -// @ConfigItem( -// keyName = "Ranging/Bastion potion", -// name = "Auto Ranging/Bastion potion", -// description = "Automatically drinks Ranging/Bastion potions", -// position = 3, -// section = foodAndPotionsSection -// ) -// default boolean toggleRangingPotion() { -// return false; -// } -// -// @ConfigItem( -// keyName = "Magic/Battlemage potion", -// name = "Auto Magic/Battlemage potion", -// description = "Automatically drinks Magic/Battlemage potions", -// position = 4, -// section = foodAndPotionsSection -// ) -// default boolean toggleMagicPotion() { -// return false; -// } -// -// @ConfigItem( -// keyName = "Use AntiPoison", -// name = "Auto AntiPoison", -// description = "Use AntiPoison", -// position = 8, -// section = foodAndPotionsSection -// ) -// default boolean useAntiPoison() { -// return false; -// } -// -// // use antifire potion -// @ConfigItem( -// keyName = "useAntifirePotion", -// name = "Auto Antifire Potion", -// description = "Use Antifire Potion", -// position = 9, -// section = foodAndPotionsSection -// ) -// default boolean useAntifirePotion() { -// return false; -// } -// // Use goading potion -// @ConfigItem( -// keyName = "useGoadingPotion", -// name = "Auto Goading Potion", -// description = "Use Goading Potion", -// position = 10, -// section = foodAndPotionsSection -// ) -// default boolean useGoadingPotion() { -// return false; -// } - - @ConfigItem( - keyName = "Loot items", - name = "Auto loot items", - description = "Enable/disable loot items", - position = 0, - section = lootSection - ) - default boolean toggleLootItems() { - return true; - } - - @ConfigItem( - name = "Loot Style", - keyName = "lootStyle", - position = 1, - description = "Choose Looting Style", - section = lootSection - ) - default DefaultLooterStyle looterStyle() { - return DefaultLooterStyle.MIXED; - } - - @ConfigItem( - name = "List of Items", - keyName = "listOfItemsToLoot", - position = 2, - description = "List of items to loot", - section = lootSection - ) - default String listOfItemsToLoot() { - return "bones,ashes"; - } - - @ConfigItem( - keyName = "Min Price of items to loot", - name = "Min. Price of items to loot", - description = "Min. Price of items to loot", - position = 10, - section = lootSection - ) - default int minPriceOfItemsToLoot() { - return 5000; - } - - @ConfigItem( - keyName = "Max Price of items to loot", - name = "Max. Price of items to loot", - description = "Max. Price of items to loot default is set to 10M", - position = 11, - section = lootSection - ) - default int maxPriceOfItemsToLoot() { - return 10000000; - } - // toggle scatter - - @ConfigItem( - keyName = "Loot arrows", - name = "Auto loot arrows", - description = "Enable/disable loot arrows", - position = 20, - section = lootSection - ) - default boolean toggleLootArrows() { - return false; - } - - // toggle loot runes - @ConfigItem( - keyName = "Loot runes", - name = "Loot runes", - description = "Enable/disable loot runes", - position = 30, - section = lootSection - ) - default boolean toggleLootRunes() { - return false; - } - - // toggle loot coins - @ConfigItem( - keyName = "Loot coins", - name = "Loot coins", - description = "Enable/disable loot coins", - position = 40, - section = lootSection - ) - default boolean toggleLootCoins() { - return false; - } - - // toggle loot untreadables - @ConfigItem( - keyName = "Loot untradables", - name = "Loot untradables", - description = "Enable/disable loot untradables", - position = 50, - section = lootSection - ) - default boolean toggleLootUntradables() { - return false; - } - - @ConfigItem( - keyName = "Bury Bones", - name = "Bury Bones", - description = "Picks up and Bury Bones", - position = 96, - section = lootSection - ) - default boolean toggleBuryBones() { - return false; - } - - @ConfigItem( - keyName = "Scatter", - name = "Scatter", - description = "Picks up and Scatter ashes", - position = 97, - section = lootSection - ) - default boolean toggleScatter() { - return false; - } - - // delayed looting - @ConfigItem( - keyName = "delayedLooting", - name = "Delayed Looting", - description = "Lets the loot stay on the ground for a while before picking it up", - position = 98, - section = lootSection - ) - default boolean toggleDelayedLooting() { - return false; - } - - // only loot my items - @ConfigItem( - keyName = "onlyLootMyItems", - name = "Only Loot My Items", - description = "Only loot items that are dropped for/by you", - position = 99, - section = lootSection - ) - default boolean toggleOnlyLootMyItems() { - return false; - } - - //Force loot regardless if we are in combat or not - @ConfigItem( - keyName = "forceLoot", - name = "Force Loot", - description = "Force loot regardless if we are in combat or not", - position = 100, - section = lootSection - ) - default boolean toggleForceLoot() { - return false; - } - - //toggle High Alch profitable items - @ConfigItem( - keyName = "highAlchProfitable", - name = "High Alch Profitable", - description = "High Alch Profitable items", - position = 101, - section = lootSection - ) - default boolean toggleHighAlchProfitable() { - return false; - } - - @ConfigItem( - keyName = "eatFoodForSpace", - name = "Eat food for space", - description = "Eats food before looting if low on space", - position = 102, - section = lootSection - ) - default boolean eatFoodForSpace() { return false; } - - //set center tile manually - @ConfigItem( - keyName = "Center Tile", - name = "Manual Center Tile", - description = "Shift Right-click the ground to select the center tile", - position = 6, - section = combatSection - ) - default boolean toggleCenterTile() { - return false; - } - - //Use quick prayer - @ConfigItem( - keyName = "Use prayer", - name = "Use prayer", - description = "Use prayer", - position = 0, - section = prayerSection - ) - default boolean togglePrayer() { - return false; - } - - //Flick quick prayer - @ConfigItem( - keyName = "quickPrayer", - name = "Quick prayer", - description = "Use quick prayer", - position = 1, - section = prayerSection - ) - default boolean toggleQuickPray() { - return false; - } - - //Lazy flick - @ConfigItem( - keyName = "prayerStyle", - name = "Prayer Style", - description = "Select type of prayer style to use", - position = 2, - section = prayerSection - ) - default PrayerStyle prayerStyle() { - return PrayerStyle.LAZY_FLICK; - } - - //Prayer style guide - @ConfigItem( - keyName = "prayerStyleGuide", - name = "Prayer Style Guide", - description = "Prayer Style Guide", - position = 3, - section = prayerSection - ) - default String prayerStyleGuide() { - return "Lazy Flick: Flicks tick before hit\n" + - "Perfect Lazy Flick: Flicks on hit\n" + - "Continuous: Quick prayer is on when in combat\n" + - "Always On: Quick prayer is always on"; - } - - // Use Magic - @ConfigItem( - keyName = "useMagic", - name = "Use Magic", - description = "Use Magic", - position = 1, - section = skillingSection - ) - default boolean useMagic() { - return false; - } - // Magic spell - @ConfigItem( - keyName = "magicSpell", - name = "Auto Cast Spell", - description = "Magic Auto Cast Spell", - position = 2, - section = skillingSection - ) - default Rs2CombatSpells magicSpell() { - return Rs2CombatSpells.WIND_STRIKE; - } - - //Balance combat skills - @ConfigItem( - keyName = "balanceCombatSkills", - name = "Balance combat skills", - description = "Balance combat skills", - position = 10, - section = skillingSection - ) - default boolean toggleBalanceCombatSkills() { - return false; - } - - //Avoid Controlled attack style - @ConfigItem( - keyName = "avoidControlled", - name = "No Controlled Attack", - description = "Avoid Controlled attack style so you won't accidentally train unwanted combat skills", - position = 11, - section = skillingSection - ) - default boolean toggleAvoidControlled() { - return true; - } - - - //Attack style change delay (Seconds) - @ConfigItem( - keyName = "attackStyleChangeDelay", - name = "Change Delay", - description = "Attack Style Change Delay In Seconds", - position = 20, - section = skillingSection - ) - default int attackStyleChangeDelay() { - return 60 * 15; - } - // Disable on Max combat - @ConfigItem( - keyName = "disableOnMaxCombat", - name = "Disable on Max Combat", - description = "Disable on Max Combat", - position = 30, - section = skillingSection - ) - default boolean toggleDisableOnMaxCombat() { - return true; - } - //Attack skill target - @ConfigItem( - keyName = "attackSkillTarget", - name = "Attack Level Target", - description = "Attack level target", - position = 97, - section = skillingSection - ) - default int attackSkillTarget() { - return 99; - } - - //Strength skill target - @ConfigItem( - keyName = "strengthSkillTarget", - name = "Strength Level Target", - description = "Strength level target", - position = 98, - section = skillingSection - ) - default int strengthSkillTarget() { - return 99; - } - - //Defence skill target - @ConfigItem( - keyName = "defenceSkillTarget", - name = "Defence Level Target", - description = "Defence level target", - position = 99, - section = skillingSection - ) - default int defenceSkillTarget() { - return 99; - } - - - // Use Inventory Setup - @ConfigItem( - keyName = "useInventorySetup", - name = "Use Inventory Setup", - description = "Use Inventory Setup, make sure to select consumables used in the bank section", - position = 1, - section = gearSection - ) - default boolean useInventorySetup() { - return false; - } - - // Inventory setup selection TODO: Add inventory setup selection - @ConfigItem( - keyName = "InventorySetupName", - name = "Inventory setup name", - description = "Create an inventory setup in the inventory setup plugin and enter the name here", - position = 99, - section = gearSection - ) - default InventorySetup inventorySetup() { - return null; - } - - @ConfigItem( - keyName = "bank", - name = "Bank", - description = "If enabled, will bank items when inventory is full. If disabled, will just stop looting", - position = 0, - section = banking - ) - default boolean bank() { - return false; - } - - //Minimum free inventory slots to bank - @Range(max = 28) - @ConfigItem( - keyName = "minFreeSlots", - name = "Min. free slots", - description = "Minimum free inventory slots to bank, if less than this, will bank items", - position = 1, - section = banking - ) - default int minFreeSlots() { - return 5; - } - - // checkbox to use stamina potions when banking - @ConfigItem( - keyName = "useStamina", - name = "Use stamina potions", - description = "Use stamina potions when banking", - position = 2, - section = banking - ) - default boolean useStamina() { - return false; - } - - @ConfigItem( - keyName = "staminaValue", - name = "Stamina Potions", - description = "Amount of stamina potions to withdraw", - position = 2, - section = banking - ) - default int staminaValue() { - return 0; - } - - // checkbox to use food when banking - @ConfigItem( - keyName = "useFood", - name = "Use food", - description = "Use food when banking", - position = 3, - section = banking - ) - default boolean useFood() { - return false; - } - - @ConfigItem( - keyName = "foodValue", - name = "Food", - description = "Amount of food to withdraw", - position = 3, - section = banking - ) - default int foodValue() { - return 0; - } - - // checkbox to use restore potions when banking - @ConfigItem( - keyName = "useRestore", - name = "Use restore potions", - description = "Use restore potions when banking", - position = 4, - section = banking - ) - default boolean useRestore() { - return false; - } - - @ConfigItem( - keyName = "restoreValue", - name = "Restore Potions", - description = "Amount of restore potions to withdraw", - position = 4, - section = banking - ) - default int restoreValue() { - return 0; - } - - // checkbox to use prayer potions when banking - @ConfigItem( - keyName = "usePrayer", - name = "Use prayer potions", - description = "Use prayer potions when banking", - position = 5, - section = banking - ) - default boolean usePrayer() { - return false; - } - - @ConfigItem( - keyName = "prayerValue", - name = "Prayer Potions", - description = "Amount of prayer potions to withdraw", - position = 5, - section = banking - ) - default int prayerValue() { - return 0; - } - - // checkbox to use antipoison potions when banking - @ConfigItem( - keyName = "useAntipoison", - name = "Use antipoison potions", - description = "Use antipoison potions when banking", - position = 6, - section = banking - ) - default boolean useAntipoison() { - return false; - } - - @ConfigItem( - keyName = "antipoisonValue", - name = "Antipoison Potions", - description = "Amount of antipoison potions to withdraw", - position = 6, - section = banking - ) - default int antipoisonValue() { - return 0; - } - - // checkbox to use antifire potions when banking - @ConfigItem( - keyName = "useAntifire", - name = "Use antifire potions", - description = "Use antifire potions when banking", - position = 7, - section = banking - ) - default boolean useAntifire() { - return false; - } - - @ConfigItem( - keyName = "antifireValue", - name = "Antifire Potions", - description = "Amount of antifire potions to withdraw", - position = 7, - section = banking - ) - default int antifireValue() { - return 0; - } - - // checkbox to use combat potions when banking - @ConfigItem( - keyName = "useCombat", - name = "Use combat potions", - description = "Use combat potions when banking", - position = 8, - section = banking - ) - default boolean useCombat() { - return false; - } - - @ConfigItem( - keyName = "combatValue", - name = "Combat Potions", - description = "Amount of combat potions to withdraw", - position = 8, - section = banking - ) - default int combatValue() { - return 0; - } - - - // checkbox to use teleportation items when banking - @ConfigItem( - keyName = "ignoreTeleport", - name = "Ignore Teleport Items", - description = "ignore teleport items when banking", - position = 9, - section = banking - ) - default boolean ignoreTeleport() { - return true; - } - - // Safety section - @ConfigItem( - keyName = "useSafety", - name = "Use Safety", - description = "Use Safety", - position = 0, - section = safetySection - ) - default boolean useSafety() { - return false; - } - - // Missing runes - @ConfigItem( - keyName = "missingRunes", - name = "Missing Runes", - description = "Go to bank and logout if missing runes", - position = 1, - section = safetySection - ) - default boolean missingRunes() { - return true; - } - // Missing arrows - @ConfigItem( - keyName = "missingArrows", - name = "Missing Arrows", - description = "Go to bank and logout if missing arrows", - position = 2, - section = safetySection - ) - default boolean missingArrows() { - return true; - } - // Missing food - @ConfigItem( - keyName = "missingFood", - name = "Missing Food", - description = "Go to bank and logout if missing food and banking isn't enabled", - position = 3, - section = safetySection - ) - default boolean missingFood() { - return true; - } - - // Low health - @ConfigItem( - keyName = "lowHealth", - name = "Low Health", - description = "Go to bank and logout if low health", - position = 4, - section = safetySection - ) - default boolean lowHealth() { - return true; - } - // Health safety value - @ConfigItem( - keyName = "healthSafetyValue", - name = "Health Safety %", - description = "Health Safety %", - position = 5, - section = safetySection - ) - default int healthSafetyValue() { - return 25; - } - - // Slayer mode - @ConfigItem( - keyName = "slayerMode", - name = "Slayer Mode", - description = "Slayer Mode", - position = 0, - section = slayerSection, - hidden = true - ) - default boolean slayerMode() { - return false; - } - - // Slayer master - @ConfigItem( - keyName = "slayerMaster", - name = "Slayer Master", - description = "Slayer Master", - position = 1, - section = slayerSection, - hidden = true - ) - default SlayerMaster slayerMaster() { - return SlayerMaster.VANNAKA; - } - - - //hidden config item for state - @ConfigItem( - keyName = "state", - name = "State", - description = "State", - hidden = true - ) - default State state() { - return State.IDLE; - } - - // Hidden config item for inventory setup - @ConfigItem( - keyName = "inventorySetupHidden", - name = "inventorySetupHidden", - description = "inventorySetupHidden", - hidden = true - ) - default InventorySetup inventorySetupHidden() { - return null; - } - - //hidden config item for center location - @ConfigItem( - keyName = "centerLocation", - name = "Center Location", - description = "Center Location", - hidden = true - ) - default WorldPoint centerLocation() { - return new WorldPoint(0, 0, 0); - } - - //hidden config item for safe spot location - @ConfigItem( - keyName = "safeSpotLocation", - name = "Safe Spot Location", - description = "Safe Spot Location", - hidden = true - ) - default WorldPoint safeSpot() { - return new WorldPoint(0, 0, 0); - } - -} - - +package net.runelite.client.plugins.microbot.aiofighter; + +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.config.*; +import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.PlayStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.PrayerStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.State; +import net.runelite.client.plugins.microbot.inventorysetups.InventorySetup; +import net.runelite.client.plugins.microbot.util.magic.Rs2CombatSpells; +import net.runelite.client.plugins.microbot.util.slayer.enums.SlayerMaster; + +@ConfigGroup(AIOFighterConfig.GROUP) +@ConfigInformation("1. Make sure to place the cannon first before starting the plugin.
" + + "2. Use food also supports Guthan's healing, the shield weapon is default set to Dragon Defender.
" + + "3. Prayer, Combat, Ranging & AntiPoison potions are supported.
" + + "4. Items to loot based your requirements.
" + + "5. You can turn auto attack NPC off if you have a cannon.
" + + "6. PrayFlick in different styles.
" + + "7. SafeSpot you can Shift Right-click the ground to select the tile.
" + + "8. Right-click NPCs to add them to the attack list.
") +public interface AIOFighterConfig extends Config { + + String GROUP = "PlayerAssistant"; + + @ConfigSection( + name = "Combat", + description = "Combat", + position = 10, + closedByDefault = false + ) + String combatSection = "Combat"; + @ConfigSection( + name = "Slayer", + description = "Slayer", + position = 11, + closedByDefault = true + ) + String slayerSection = "Slayer"; + @ConfigSection( + name = "Banking", + description = "Banking settings", + position = 992, + closedByDefault = false + ) + String banking = "Banking"; + //Gear section + @ConfigSection( + name = "Gear", + description = "Gear", + position = 55, + closedByDefault = true + ) + String gearSection = "Gear"; + // Safety section + @ConfigSection( + name = "Safety", + description = "Safety", + position = 54, + closedByDefault = true + ) + String safetySection = "Safety"; + + @ConfigSection( + name = "Food & Potions", + description = "Food & Potions", + position = 20, + closedByDefault = false + ) + String foodAndPotionsSection = "Food & Potions"; + @ConfigSection( + name = "Loot", + description = "Loot", + position = 30, + closedByDefault = false + ) + String lootSection = "Loot"; + //Prayer section + @ConfigSection( + name = "Prayer", + description = "Prayer", + position = 40, + closedByDefault = false + ) + String prayerSection = "Prayer"; + //Skilling section + @ConfigSection( + name = "Skilling", + description = "Skilling", + position = 50, + closedByDefault = false + ) + String skillingSection = "Combat Skilling"; + + @ConfigItem( + keyName = "Combat", + name = "Auto attack npc", + description = "Attacks npc", + position = 0, + section = combatSection + ) + default boolean toggleCombat() { + return false; + } + + @ConfigItem( + keyName = "monster", + name = "Attackable npcs", + description = "List of attackable npcs", + position = 1, + section = combatSection + ) + default String attackableNpcs() { + return ""; + } + + @ConfigItem( + keyName = "Attack Radius", + name = "Attack Radius", + description = "The max radius to attack npcs", + position = 2, + section = combatSection + ) + default int attackRadius() { + return 10; + } + + @ConfigItem( + keyName = "Use special attack", + name = "Use special attack", + description = "Use special attack", + position = 3, + section = combatSection + ) + default boolean useSpecialAttack() { + return false; + } + + @ConfigItem( + keyName = "Cannon", + name = "Auto reload cannon", + description = "Automatically reloads cannon", + position = 4, + section = combatSection + ) + default boolean toggleCannon() { + return false; + } + + //safe spot + @ConfigItem( + keyName = "Safe Spot", + name = "Safe Spot", + description = "Shift Right-click the ground to select the safe spot tile", + position = 5, + section = combatSection + ) + default boolean toggleSafeSpot() { + return false; + } + + //PlayStyle + @ConfigItem( + keyName = "PlayStyle", + name = "Play Style", + description = "Play Style", + position = 6, + section = combatSection + ) + default PlayStyle playStyle() { + return PlayStyle.AGGRESSIVE; + } + + @ConfigItem( + keyName = "ReachableNpcs", + name = "Only attack reachable npcs", + description = "Only attack npcs that we can reach with melee", + position = 7, + section = combatSection + ) + default boolean attackReachableNpcs() { + return true; + } + + @ConfigItem( + keyName = "Food", + name = "Auto eat food", + description = "Automatically eats food", + position = 0, + section = foodAndPotionsSection + ) + default boolean toggleFood() { + return false; + } + + // Testing if full auto potion manager is preferred over individual potion toggles + +// @ConfigItem( +// keyName = "Auto Prayer Potion", +// name = "Auto prayer potion", +// description = "Automatically drinks prayer potions", +// position = 1, +// section = foodAndPotionsSection +// ) +// default boolean togglePrayerPotions() { +// return false; +// } +// +// @ConfigItem( +// keyName = "Combat potion", +// name = "Auto combat potion", +// description = "Automatically drinks combat potions", +// position = 2, +// section = foodAndPotionsSection +// ) +// default boolean toggleCombatPotion() { +// return false; +// } +// +// @ConfigItem( +// keyName = "Ranging/Bastion potion", +// name = "Auto Ranging/Bastion potion", +// description = "Automatically drinks Ranging/Bastion potions", +// position = 3, +// section = foodAndPotionsSection +// ) +// default boolean toggleRangingPotion() { +// return false; +// } +// +// @ConfigItem( +// keyName = "Magic/Battlemage potion", +// name = "Auto Magic/Battlemage potion", +// description = "Automatically drinks Magic/Battlemage potions", +// position = 4, +// section = foodAndPotionsSection +// ) +// default boolean toggleMagicPotion() { +// return false; +// } +// +// @ConfigItem( +// keyName = "Use AntiPoison", +// name = "Auto AntiPoison", +// description = "Use AntiPoison", +// position = 8, +// section = foodAndPotionsSection +// ) +// default boolean useAntiPoison() { +// return false; +// } +// +// // use antifire potion +// @ConfigItem( +// keyName = "useAntifirePotion", +// name = "Auto Antifire Potion", +// description = "Use Antifire Potion", +// position = 9, +// section = foodAndPotionsSection +// ) +// default boolean useAntifirePotion() { +// return false; +// } +// // Use goading potion +// @ConfigItem( +// keyName = "useGoadingPotion", +// name = "Auto Goading Potion", +// description = "Use Goading Potion", +// position = 10, +// section = foodAndPotionsSection +// ) +// default boolean useGoadingPotion() { +// return false; +// } + + @ConfigItem( + keyName = "Loot items", + name = "Auto loot items", + description = "Enable/disable loot items", + position = 0, + section = lootSection + ) + default boolean toggleLootItems() { + return true; + } + + @ConfigItem( + name = "Loot Style", + keyName = "lootStyle", + position = 1, + description = "Choose Looting Style", + section = lootSection + ) + default DefaultLooterStyle looterStyle() { + return DefaultLooterStyle.MIXED; + } + + @ConfigItem( + name = "List of Items", + keyName = "listOfItemsToLoot", + position = 2, + description = "List of items to loot", + section = lootSection + ) + default String listOfItemsToLoot() { + return "bones,ashes"; + } + + @ConfigItem( + keyName = "Min Price of items to loot", + name = "Min. Price of items to loot", + description = "Min. Price of items to loot", + position = 10, + section = lootSection + ) + default int minPriceOfItemsToLoot() { + return 5000; + } + + @ConfigItem( + keyName = "Max Price of items to loot", + name = "Max. Price of items to loot", + description = "Max. Price of items to loot default is set to 10M", + position = 11, + section = lootSection + ) + default int maxPriceOfItemsToLoot() { + return 10000000; + } + // toggle scatter + + @ConfigItem( + keyName = "Loot arrows", + name = "Auto loot arrows", + description = "Enable/disable loot arrows", + position = 20, + section = lootSection + ) + default boolean toggleLootArrows() { + return false; + } + + // toggle loot runes + @ConfigItem( + keyName = "Loot runes", + name = "Loot runes", + description = "Enable/disable loot runes", + position = 30, + section = lootSection + ) + default boolean toggleLootRunes() { + return false; + } + + // toggle loot coins + @ConfigItem( + keyName = "Loot coins", + name = "Loot coins", + description = "Enable/disable loot coins", + position = 40, + section = lootSection + ) + default boolean toggleLootCoins() { + return false; + } + + // toggle loot untreadables + @ConfigItem( + keyName = "Loot untradables", + name = "Loot untradables", + description = "Enable/disable loot untradables", + position = 50, + section = lootSection + ) + default boolean toggleLootUntradables() { + return false; + } + + @ConfigItem( + keyName = "Bury Bones", + name = "Bury Bones", + description = "Picks up and Bury Bones", + position = 96, + section = lootSection + ) + default boolean toggleBuryBones() { + return false; + } + + @ConfigItem( + keyName = "Scatter", + name = "Scatter", + description = "Picks up and Scatter ashes", + position = 97, + section = lootSection + ) + default boolean toggleScatter() { + return false; + } + + // delayed looting + @ConfigItem( + keyName = "delayedLooting", + name = "Delayed Looting", + description = "Lets the loot stay on the ground for a while before picking it up", + position = 98, + section = lootSection + ) + default boolean toggleDelayedLooting() { + return false; + } + + // only loot my items + @ConfigItem( + keyName = "onlyLootMyItems", + name = "Only Loot My Items", + description = "Only loot items that are dropped for/by you", + position = 99, + section = lootSection + ) + default boolean toggleOnlyLootMyItems() { + return false; + } + + //Force loot regardless if we are in combat or not + @ConfigItem( + keyName = "forceLoot", + name = "Force Loot", + description = "Force loot regardless if we are in combat or not", + position = 100, + section = lootSection + ) + default boolean toggleForceLoot() { + return false; + } + + //toggle High Alch profitable items + @ConfigItem( + keyName = "highAlchProfitable", + name = "High Alch Profitable", + description = "High Alch Profitable items", + position = 101, + section = lootSection + ) + default boolean toggleHighAlchProfitable() { + return false; + } + + @ConfigItem( + keyName = "eatFoodForSpace", + name = "Eat food for space", + description = "Eats food before looting if low on space", + position = 102, + section = lootSection + ) + default boolean eatFoodForSpace() { return false; } + + @ConfigItem( + keyName = "waitForLoot", + name = "Wait for Loot", + description = "Wait for loot to appear before attacking next NPC (6 second timeout)", + position = 103, + section = lootSection + ) + default boolean toggleWaitForLoot() { + return false; + } + + //set center tile manually + @ConfigItem( + keyName = "Center Tile", + name = "Manual Center Tile", + description = "Shift Right-click the ground to select the center tile", + position = 6, + section = combatSection + ) + default boolean toggleCenterTile() { + return false; + } + + //Use quick prayer + @ConfigItem( + keyName = "Use prayer", + name = "Use prayer", + description = "Use prayer", + position = 0, + section = prayerSection + ) + default boolean togglePrayer() { + return false; + } + + //Flick quick prayer + @ConfigItem( + keyName = "quickPrayer", + name = "Quick prayer", + description = "Use quick prayer", + position = 1, + section = prayerSection + ) + default boolean toggleQuickPray() { + return false; + } + + //Lazy flick + @ConfigItem( + keyName = "prayerStyle", + name = "Prayer Style", + description = "Select type of prayer style to use", + position = 2, + section = prayerSection + ) + default PrayerStyle prayerStyle() { + return PrayerStyle.LAZY_FLICK; + } + + //Prayer style guide + @ConfigItem( + keyName = "prayerStyleGuide", + name = "Prayer Style Guide", + description = "Prayer Style Guide", + position = 3, + section = prayerSection + ) + default String prayerStyleGuide() { + return "Lazy Flick: Flicks tick before hit\n" + + "Perfect Lazy Flick: Flicks on hit\n" + + "Continuous: Quick prayer is on when in combat\n" + + "Always On: Quick prayer is always on"; + } + + // Use Magic + @ConfigItem( + keyName = "useMagic", + name = "Use Magic", + description = "Use Magic", + position = 1, + section = skillingSection + ) + default boolean useMagic() { + return false; + } + // Magic spell + @ConfigItem( + keyName = "magicSpell", + name = "Auto Cast Spell", + description = "Magic Auto Cast Spell", + position = 2, + section = skillingSection + ) + default Rs2CombatSpells magicSpell() { + return Rs2CombatSpells.WIND_STRIKE; + } + + //Balance combat skills + @ConfigItem( + keyName = "balanceCombatSkills", + name = "Balance combat skills", + description = "Balance combat skills", + position = 10, + section = skillingSection + ) + default boolean toggleBalanceCombatSkills() { + return false; + } + + //Avoid Controlled attack style + @ConfigItem( + keyName = "avoidControlled", + name = "No Controlled Attack", + description = "Avoid Controlled attack style so you won't accidentally train unwanted combat skills", + position = 11, + section = skillingSection + ) + default boolean toggleAvoidControlled() { + return true; + } + + + //Attack style change delay (Seconds) + @ConfigItem( + keyName = "attackStyleChangeDelay", + name = "Change Delay", + description = "Attack Style Change Delay In Seconds", + position = 20, + section = skillingSection + ) + default int attackStyleChangeDelay() { + return 60 * 15; + } + // Disable on Max combat + @ConfigItem( + keyName = "disableOnMaxCombat", + name = "Disable on Max Combat", + description = "Disable on Max Combat", + position = 30, + section = skillingSection + ) + default boolean toggleDisableOnMaxCombat() { + return true; + } + //Attack skill target + @ConfigItem( + keyName = "attackSkillTarget", + name = "Attack Level Target", + description = "Attack level target", + position = 97, + section = skillingSection + ) + default int attackSkillTarget() { + return 99; + } + + //Strength skill target + @ConfigItem( + keyName = "strengthSkillTarget", + name = "Strength Level Target", + description = "Strength level target", + position = 98, + section = skillingSection + ) + default int strengthSkillTarget() { + return 99; + } + + //Defence skill target + @ConfigItem( + keyName = "defenceSkillTarget", + name = "Defence Level Target", + description = "Defence level target", + position = 99, + section = skillingSection + ) + default int defenceSkillTarget() { + return 99; + } + + + // Use Inventory Setup + @ConfigItem( + keyName = "useInventorySetup", + name = "Use Inventory Setup", + description = "Use Inventory Setup, make sure to select consumables used in the bank section", + position = 1, + section = gearSection + ) + default boolean useInventorySetup() { + return false; + } + + // Inventory setup selection TODO: Add inventory setup selection + @ConfigItem( + keyName = "InventorySetupName", + name = "Inventory setup name", + description = "Create an inventory setup in the inventory setup plugin and enter the name here", + position = 99, + section = gearSection + ) + default InventorySetup inventorySetup() { + return null; + } + + @ConfigItem( + keyName = "bank", + name = "Bank", + description = "If enabled, will bank items when inventory is full. If disabled, will just stop looting", + position = 0, + section = banking + ) + default boolean bank() { + return false; + } + + //Minimum free inventory slots to bank + @Range(max = 28) + @ConfigItem( + keyName = "minFreeSlots", + name = "Min. free slots", + description = "Minimum free inventory slots to bank, if less than this, will bank items", + position = 1, + section = banking + ) + default int minFreeSlots() { + return 5; + } + + // checkbox to use stamina potions when banking + @ConfigItem( + keyName = "useStamina", + name = "Use stamina potions", + description = "Use stamina potions when banking", + position = 2, + section = banking + ) + default boolean useStamina() { + return false; + } + + @ConfigItem( + keyName = "staminaValue", + name = "Stamina Potions", + description = "Amount of stamina potions to withdraw", + position = 2, + section = banking + ) + default int staminaValue() { + return 0; + } + + // checkbox to use food when banking + @ConfigItem( + keyName = "useFood", + name = "Use food", + description = "Use food when banking", + position = 3, + section = banking + ) + default boolean useFood() { + return false; + } + + @ConfigItem( + keyName = "foodValue", + name = "Food", + description = "Amount of food to withdraw", + position = 3, + section = banking + ) + default int foodValue() { + return 0; + } + + // checkbox to use restore potions when banking + @ConfigItem( + keyName = "useRestore", + name = "Use restore potions", + description = "Use restore potions when banking", + position = 4, + section = banking + ) + default boolean useRestore() { + return false; + } + + @ConfigItem( + keyName = "restoreValue", + name = "Restore Potions", + description = "Amount of restore potions to withdraw", + position = 4, + section = banking + ) + default int restoreValue() { + return 0; + } + + // checkbox to use prayer potions when banking + @ConfigItem( + keyName = "usePrayer", + name = "Use prayer potions", + description = "Use prayer potions when banking", + position = 5, + section = banking + ) + default boolean usePrayer() { + return false; + } + + @ConfigItem( + keyName = "prayerValue", + name = "Prayer Potions", + description = "Amount of prayer potions to withdraw", + position = 5, + section = banking + ) + default int prayerValue() { + return 0; + } + + // checkbox to use antipoison potions when banking + @ConfigItem( + keyName = "useAntipoison", + name = "Use antipoison potions", + description = "Use antipoison potions when banking", + position = 6, + section = banking + ) + default boolean useAntipoison() { + return false; + } + + @ConfigItem( + keyName = "antipoisonValue", + name = "Antipoison Potions", + description = "Amount of antipoison potions to withdraw", + position = 6, + section = banking + ) + default int antipoisonValue() { + return 0; + } + + // checkbox to use antifire potions when banking + @ConfigItem( + keyName = "useAntifire", + name = "Use antifire potions", + description = "Use antifire potions when banking", + position = 7, + section = banking + ) + default boolean useAntifire() { + return false; + } + + @ConfigItem( + keyName = "antifireValue", + name = "Antifire Potions", + description = "Amount of antifire potions to withdraw", + position = 7, + section = banking + ) + default int antifireValue() { + return 0; + } + + // checkbox to use combat potions when banking + @ConfigItem( + keyName = "useCombat", + name = "Use combat potions", + description = "Use combat potions when banking", + position = 8, + section = banking + ) + default boolean useCombat() { + return false; + } + + @ConfigItem( + keyName = "combatValue", + name = "Combat Potions", + description = "Amount of combat potions to withdraw", + position = 8, + section = banking + ) + default int combatValue() { + return 0; + } + + + // checkbox to use teleportation items when banking + @ConfigItem( + keyName = "ignoreTeleport", + name = "Ignore Teleport Items", + description = "ignore teleport items when banking", + position = 9, + section = banking + ) + default boolean ignoreTeleport() { + return true; + } + + // Safety section + @ConfigItem( + keyName = "useSafety", + name = "Use Safety", + description = "Use Safety", + position = 0, + section = safetySection + ) + default boolean useSafety() { + return false; + } + + // Missing runes + @ConfigItem( + keyName = "missingRunes", + name = "Missing Runes", + description = "Go to bank and logout if missing runes", + position = 1, + section = safetySection + ) + default boolean missingRunes() { + return true; + } + // Missing arrows + @ConfigItem( + keyName = "missingArrows", + name = "Missing Arrows", + description = "Go to bank and logout if missing arrows", + position = 2, + section = safetySection + ) + default boolean missingArrows() { + return true; + } + // Missing food + @ConfigItem( + keyName = "missingFood", + name = "Missing Food", + description = "Go to bank and logout if missing food and banking isn't enabled", + position = 3, + section = safetySection + ) + default boolean missingFood() { + return true; + } + + // Low health + @ConfigItem( + keyName = "lowHealth", + name = "Low Health", + description = "Go to bank and logout if low health", + position = 4, + section = safetySection + ) + default boolean lowHealth() { + return true; + } + // Health safety value + @ConfigItem( + keyName = "healthSafetyValue", + name = "Health Safety %", + description = "Health Safety %", + position = 5, + section = safetySection + ) + default int healthSafetyValue() { + return 25; + } + + // Slayer mode + @ConfigItem( + keyName = "slayerMode", + name = "Slayer Mode", + description = "Slayer Mode", + position = 0, + section = slayerSection, + hidden = true + ) + default boolean slayerMode() { + return false; + } + + // Slayer master + @ConfigItem( + keyName = "slayerMaster", + name = "Slayer Master", + description = "Slayer Master", + position = 1, + section = slayerSection, + hidden = true + ) + default SlayerMaster slayerMaster() { + return SlayerMaster.VANNAKA; + } + + + //hidden config item for state + @ConfigItem( + keyName = "state", + name = "State", + description = "State", + hidden = true + ) + default State state() { + return State.IDLE; + } + + // Hidden config item for inventory setup + @ConfigItem( + keyName = "inventorySetupHidden", + name = "inventorySetupHidden", + description = "inventorySetupHidden", + hidden = true + ) + default InventorySetup inventorySetupHidden() { + return null; + } + + //hidden config item for center location + @ConfigItem( + keyName = "centerLocation", + name = "Center Location", + description = "Center Location", + hidden = true + ) + default WorldPoint centerLocation() { + return new WorldPoint(0, 0, 0); + } + + //hidden config item for safe spot location + @ConfigItem( + keyName = "safeSpotLocation", + name = "Safe Spot Location", + description = "Safe Spot Location", + hidden = true + ) + default WorldPoint safeSpot() { + return new WorldPoint(0, 0, 0); + } + +} + + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java index 0f6a3df13a6..8bec68ae9af 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java @@ -1,439 +1,469 @@ -package net.runelite.client.plugins.microbot.aiofighter; - -import com.google.inject.Provides; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Point; -import net.runelite.api.*; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.events.*; -import net.runelite.api.widgets.ComponentID; -import net.runelite.api.widgets.Widget; -import net.runelite.api.worldmap.WorldMap; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.events.ConfigChanged; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.aiofighter.bank.BankerScript; -import net.runelite.client.plugins.microbot.aiofighter.cannon.CannonScript; -import net.runelite.client.plugins.microbot.aiofighter.combat.*; -import net.runelite.client.plugins.microbot.aiofighter.enums.PrayerStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.State; -import net.runelite.client.plugins.microbot.aiofighter.loot.LootScript; -import net.runelite.client.plugins.microbot.aiofighter.safety.SafetyScript; -import net.runelite.client.plugins.microbot.aiofighter.skill.AttackStyleScript; -import net.runelite.client.plugins.microbot.inventorysetups.InventorySetup; -import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; -import net.runelite.client.plugins.microbot.util.prayer.Rs2Prayer; -import net.runelite.client.ui.JagexColors; -import net.runelite.client.ui.overlay.OverlayManager; -import net.runelite.client.util.ColorUtil; -import net.runelite.client.util.Text; - -import javax.inject.Inject; -import java.awt.*; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -@PluginDescriptor( - name = PluginDescriptor.Mocrosoft + "AIO Fighter", - description = "Microbot Fighter plugin", - tags = {"fight", "microbot", "misc", "combat", "playerassistant"}, - enabledByDefault = false -) -@Slf4j -public class AIOFighterPlugin extends Plugin { - public static final String version = "1.3.1"; - private static final String SET = "Set"; - private static final String CENTER_TILE = ColorUtil.wrapWithColorTag("Center Tile", JagexColors.MENU_TARGET); - // SAFE_SPOT = "Safe Spot"; - private static final String SAFE_SPOT = ColorUtil.wrapWithColorTag("Safe Spot", JagexColors.CHAT_PRIVATE_MESSAGE_TEXT_TRANSPARENT_BACKGROUND); - private static final String ADD_TO = "Start Fighting:"; - private static final String REMOVE_FROM = "Stop Fighting:"; - private static final String WALK_HERE = "Walk here"; - private static final String ATTACK = "Attack"; - @Getter - @Setter - public static int cooldown = 0; - private final CannonScript cannonScript = new CannonScript(); - private final AttackNpcScript attackNpc = new AttackNpcScript(); - - private final FoodScript foodScript = new FoodScript(); - private final LootScript lootScript = new LootScript(); - private final SafeSpot safeSpotScript = new SafeSpot(); - private final FlickerScript flickerScript = new FlickerScript(); - private final UseSpecialAttackScript useSpecialAttackScript = new UseSpecialAttackScript(); - private final BuryScatterScript buryScatterScript = new BuryScatterScript(); - private final AttackStyleScript attackStyleScript = new AttackStyleScript(); - private final BankerScript bankerScript = new BankerScript(); - private final PrayerScript prayerScript = new PrayerScript(); - private final HighAlchScript highAlchScript = new HighAlchScript(); - private final PotionManagerScript potionManagerScript = new PotionManagerScript(); - private final SafetyScript safetyScript = new SafetyScript(); - //private final SlayerScript slayerScript = new SlayerScript(); - @Inject - private AIOFighterConfig config; - @Inject - private ConfigManager configManager; - @Inject - private OverlayManager overlayManager; - @Inject - private AIOFighterOverlay playerAssistOverlay; - @Inject - private AIOFighterInfoOverlay playerAssistInfoOverlay; - private MenuEntry lastClick; - private Point lastMenuOpenedPoint; - private WorldPoint trueTile; - - protected ScheduledExecutorService initializerExecutor = Executors.newSingleThreadScheduledExecutor(); - - @Provides - AIOFighterConfig provideConfig(ConfigManager configManager) { - return configManager.getConfig(AIOFighterConfig.class); - } - - @Override - protected void startUp() throws AWTException { - Microbot.pauseAllScripts.compareAndSet(true, false); - cooldown = 0; - //initialize any data on startup - ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); - AtomicReference> futureRef = new AtomicReference<>(); - - ScheduledFuture future = executor.scheduleWithFixedDelay(() -> { - if (Microbot.getConfigManager() == null) { - return; - } - setState(State.IDLE); - // Get the future from the reference and cancel it - ScheduledFuture scheduledFuture = futureRef.get(); - if (scheduledFuture != null) { - scheduledFuture.cancel(false); - } - // now that no other tasks run, you can shut down: - executor.shutdown(); - }, 0, 1, TimeUnit.SECONDS); - - if (overlayManager != null) { - overlayManager.add(playerAssistOverlay); - overlayManager.add(playerAssistInfoOverlay); - } - if (!config.toggleCenterTile() && Microbot.isLoggedIn()) - setCenter(Rs2Player.getWorldLocation()); - lootScript.run(config); - cannonScript.run(config); - attackNpc.run(config); - foodScript.run(config); - safeSpotScript.run(config); - flickerScript.run(config); - useSpecialAttackScript.run(config); - buryScatterScript.run(config); - attackStyleScript.run(config); - bankerScript.run(config); - prayerScript.run(config); - highAlchScript.run(config); - potionManagerScript.run(config); - safetyScript.run(config); - //slayerScript.run(config); - Microbot.getSpecialAttackConfigs() - .setSpecialAttack(true); - } - - protected void shutDown() { - lootScript.shutdown(); - cannonScript.shutdown(); - attackNpc.shutdown(); - foodScript.shutdown(); - safeSpotScript.shutdown(); - flickerScript.shutdown(); - useSpecialAttackScript.shutdown(); - buryScatterScript.shutdown(); - attackStyleScript.shutdown(); - bankerScript.shutdown(); - prayerScript.shutdown(); - highAlchScript.shutdown(); - potionManagerScript.shutdown(); - safetyScript.shutdown(); - //slayerScript.shutdown(); - resetLocation(); - overlayManager.remove(playerAssistOverlay); - overlayManager.remove(playerAssistInfoOverlay); - } - - public static void resetLocation() { - setCenter(new WorldPoint(0, 0, 0)); - setSafeSpot(new WorldPoint(0, 0, 0)); - } - - public static void setCenter(WorldPoint worldPoint) - { - Microbot.getConfigManager().setConfiguration( - "PlayerAssistant", - "centerLocation", - worldPoint - ); - } - // set safe spot - public static void setSafeSpot(WorldPoint worldPoint) - { - Microbot.getConfigManager().setConfiguration( - "PlayerAssistant", - "safeSpotLocation", - worldPoint - ); - - - } - //set Inventory Setup - private void setInventorySetup(InventorySetup inventorySetup) { - configManager.setConfiguration( - "PlayerAssistant", - "inventorySetupHidden", - inventorySetup - ); - } - - public static State getState() { - return Microbot.getConfigManager().getConfiguration( - "PlayerAssistant", - "state", - State.class - ); - } - - public static void setState(State state) { - Microbot.getConfigManager().setConfiguration( - "PlayerAssistant", - "state", - state - ); - } - - private void addNpcToList(String npcName) { - configManager.setConfiguration( - "PlayerAssistant", - "monster", - config.attackableNpcs() + npcName + "," - ); - - } - private void removeNpcFromList(String npcName) { - configManager.setConfiguration( - "PlayerAssistant", - "monster", - Arrays.stream(config.attackableNpcs().split(",")) - .filter(n -> !n.equalsIgnoreCase(npcName)) - .collect(Collectors.joining(",")) - ); - } - - // set attackable npcs - public static void setAttackableNpcs(String npcNames) { - Microbot.getConfigManager().setConfiguration( - "PlayerAssistant", - "monster", - npcNames - ); - } - - private String getNpcNameFromMenuEntry(String menuTarget) { - return menuTarget.replaceAll("<[^>]*>|\\(.*\\)", "").trim(); - } - - @Subscribe - public void onChatMessage(ChatMessage event) { - if (event.getMessage().contains("reach that")) { - AttackNpcScript.skipNpc(); - } - } - // on setting change - @Subscribe - public void onConfigChanged(ConfigChanged event) { - - - if (event.getKey().equals("Safe Spot")) { - - if (!config.toggleSafeSpot()) { - // reset safe spot to default - setSafeSpot(new WorldPoint(0, 0, 0)); - } - } - if(event.getKey().equals("Combat")) { - if (!config.toggleCombat() && config.toggleCenterTile()) { - setCenter(new WorldPoint(0, 0, 0)); - } - if (config.toggleCombat() && !config.toggleCenterTile()) { - setCenter(Rs2Player.getWorldLocation()); - } - - } - } - - - @Subscribe - public void onGameTick(GameTick gameTick) { - if (cooldown > 0 && !Rs2Combat.inCombat()) - cooldown--; - //execute flicker script - if(config.togglePrayer()) - flickerScript.onGameTick(); - } - - @Subscribe - public void onNpcDespawned(NpcDespawned npcDespawned) { - if(config.togglePrayer()) - flickerScript.onNpcDespawned(npcDespawned); - } - - @Subscribe - public void onHitsplatApplied(HitsplatApplied event){ - if (event.getActor() != Microbot.getClient().getLocalPlayer()) return; - final Hitsplat hitsplat = event.getHitsplat(); - - if ((hitsplat.isMine()) && event.getActor().getInteracting() instanceof NPC && config.togglePrayer() && (config.prayerStyle() == PrayerStyle.LAZY_FLICK) || (config.prayerStyle() == PrayerStyle.PERFECT_LAZY_FLICK)) { - flickerScript.resetLastAttack(true); - Rs2Prayer.disableAllPrayers(); - if (config.toggleQuickPray()) - Rs2Prayer.toggleQuickPrayer(false); - - - } - } - @Subscribe - public void onMenuOpened(MenuOpened event) { - lastMenuOpenedPoint = Microbot.getClient().getMouseCanvasPosition(); - trueTile = getSelectedWorldPoint(); - } - @Subscribe - private void onMenuEntryAdded(MenuEntryAdded event) { - if (Microbot.getClient().isKeyPressed(KeyCode.KC_SHIFT) && event.getOption().equals(WALK_HERE) && event.getTarget().isEmpty() && config.toggleCenterTile()) { - addMenuEntry(event, SET, CENTER_TILE, 1); - } - if (Microbot.getClient().isKeyPressed(KeyCode.KC_SHIFT) && event.getOption().equals(WALK_HERE) && event.getTarget().isEmpty()) { - addMenuEntry(event, SET, SAFE_SPOT, 1); - } - if (event.getOption().equals(ATTACK) && config.attackableNpcs().contains(getNpcNameFromMenuEntry(Text.removeTags(event.getTarget())))) { - addMenuEntry(event, REMOVE_FROM, event.getTarget(), 1); - } - if (event.getOption().equals(ATTACK) && !config.attackableNpcs().contains(getNpcNameFromMenuEntry(Text.removeTags(event.getTarget())))) { - addMenuEntry(event, ADD_TO, event.getTarget(), 1); - } - - } - - private WorldPoint getSelectedWorldPoint() { - if (Microbot.getClient().getWidget(ComponentID.WORLD_MAP_MAPVIEW) == null) { - if (Microbot.getClient().getSelectedSceneTile() != null) { - return Microbot.getClient().isInInstancedRegion() ? - WorldPoint.fromLocalInstance(Microbot.getClient(), Microbot.getClient().getSelectedSceneTile().getLocalLocation()) : - Microbot.getClient().getSelectedSceneTile().getWorldLocation(); - } - } else { - return calculateMapPoint(Microbot.getClient().isMenuOpen() ? lastMenuOpenedPoint : Microbot.getClient().getMouseCanvasPosition()); - } - return null; - } - public WorldPoint calculateMapPoint(Point point) { - WorldMap worldMap = Microbot.getClient().getWorldMap(); - float zoom = worldMap.getWorldMapZoom(); - final WorldPoint mapPoint = new WorldPoint(worldMap.getWorldMapPosition().getX(), worldMap.getWorldMapPosition().getY(), 0); - final Point middle = mapWorldPointToGraphicsPoint(mapPoint); - - if (point == null || middle == null) { - return null; - } - - final int dx = (int) ((point.getX() - middle.getX()) / zoom); - final int dy = (int) ((-(point.getY() - middle.getY())) / zoom); - - return mapPoint.dx(dx).dy(dy); - } - public Point mapWorldPointToGraphicsPoint(WorldPoint worldPoint) { - WorldMap worldMap = Microbot.getClient().getWorldMap(); - - float pixelsPerTile = worldMap.getWorldMapZoom(); - - Widget map = Microbot.getClient().getWidget(ComponentID.WORLD_MAP_MAPVIEW); - if (map != null) { - Rectangle worldMapRect = map.getBounds(); - - int widthInTiles = (int) Math.ceil(worldMapRect.getWidth() / pixelsPerTile); - int heightInTiles = (int) Math.ceil(worldMapRect.getHeight() / pixelsPerTile); - - Point worldMapPosition = worldMap.getWorldMapPosition(); - - int yTileMax = worldMapPosition.getY() - heightInTiles / 2; - int yTileOffset = (yTileMax - worldPoint.getY() - 1) * -1; - int xTileOffset = worldPoint.getX() + widthInTiles / 2 - worldMapPosition.getX(); - - int xGraphDiff = ((int) (xTileOffset * pixelsPerTile)); - int yGraphDiff = (int) (yTileOffset * pixelsPerTile); - - yGraphDiff -= (int) (pixelsPerTile - Math.ceil(pixelsPerTile / 2)); - xGraphDiff += (int) (pixelsPerTile - Math.ceil(pixelsPerTile / 2)); - - yGraphDiff = worldMapRect.height - yGraphDiff; - yGraphDiff += (int) worldMapRect.getY(); - xGraphDiff += (int) worldMapRect.getX(); - - return new Point(xGraphDiff, yGraphDiff); - } - return null; - } - private void onMenuOptionClicked(MenuEntry entry) { - - - - if (entry.getOption().equals(SET) && entry.getTarget().equals(CENTER_TILE)) { - setCenter(trueTile); - } - if (entry.getOption().equals(SET) && entry.getTarget().equals(SAFE_SPOT)) { - setSafeSpot(trueTile); - } - - - - if (entry.getType() != MenuAction.WALK) { - lastClick = entry; - } - } - - - @Subscribe - private void onMenuOptionClicked(MenuOptionClicked event) - { - if (event.getMenuOption().equals(ADD_TO)) { - addNpcToList(getNpcNameFromMenuEntry(event.getMenuTarget())); - } - if (event.getMenuOption().equals(REMOVE_FROM)) { - removeNpcFromList(getNpcNameFromMenuEntry(event.getMenuTarget())); - } - } - private void addMenuEntry(MenuEntryAdded event, String option, String target, int position) { - List entries = new LinkedList<>(Arrays.asList(Microbot.getClient().getMenuEntries())); - - if (entries.stream().anyMatch(e -> e.getOption().equals(option) && e.getTarget().equals(target))) { - return; - } - - Microbot.getClient().createMenuEntry(position) - .setOption(option) - .setTarget(target) - .setParam0(event.getActionParam0()) - .setParam1(event.getActionParam1()) - .setIdentifier(event.getIdentifier()) - .setType(MenuAction.RUNELITE) - .onClick(this::onMenuOptionClicked); - } -} +package net.runelite.client.plugins.microbot.aiofighter; + +import com.google.inject.Provides; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Point; +import net.runelite.api.*; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.*; +import net.runelite.api.widgets.ComponentID; +import net.runelite.api.widgets.Widget; +import net.runelite.api.worldmap.WorldMap; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.aiofighter.bank.BankerScript; +import net.runelite.client.plugins.microbot.aiofighter.cannon.CannonScript; +import net.runelite.client.plugins.microbot.aiofighter.combat.*; +import net.runelite.client.plugins.microbot.aiofighter.enums.PrayerStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.State; +import net.runelite.client.plugins.microbot.aiofighter.loot.LootScript; +import net.runelite.client.plugins.microbot.aiofighter.safety.SafetyScript; +import net.runelite.client.plugins.microbot.aiofighter.skill.AttackStyleScript; +import net.runelite.client.plugins.microbot.inventorysetups.InventorySetup; +import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; +import net.runelite.client.plugins.microbot.util.prayer.Rs2Prayer; +import net.runelite.client.ui.JagexColors; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.ColorUtil; +import net.runelite.client.util.Text; + +import javax.inject.Inject; +import java.awt.*; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +@PluginDescriptor( + name = PluginDescriptor.Mocrosoft + "AIO Fighter", + description = "Microbot Fighter plugin", + tags = {"fight", "microbot", "misc", "combat", "playerassistant"}, + enabledByDefault = false +) +@Slf4j +public class AIOFighterPlugin extends Plugin { + public static final String version = "1.3.1"; + private static final String SET = "Set"; + private static final String CENTER_TILE = ColorUtil.wrapWithColorTag("Center Tile", JagexColors.MENU_TARGET); + // SAFE_SPOT = "Safe Spot"; + private static final String SAFE_SPOT = ColorUtil.wrapWithColorTag("Safe Spot", JagexColors.CHAT_PRIVATE_MESSAGE_TEXT_TRANSPARENT_BACKGROUND); + private static final String ADD_TO = "Start Fighting:"; + private static final String REMOVE_FROM = "Stop Fighting:"; + private static final String WALK_HERE = "Walk here"; + private static final String ATTACK = "Attack"; + @Getter + @Setter + public static int cooldown = 0; + + @Getter + @Setter + private static long lastNpcKilledTime = 0; + + @Getter + @Setter + private static boolean waitingForLoot = false; + + public static final int LOOT_WAIT_TIMEOUT = 6000; // 6 seconds in milliseconds + + private final CannonScript cannonScript = new CannonScript(); + private final AttackNpcScript attackNpc = new AttackNpcScript(); + + private final FoodScript foodScript = new FoodScript(); + private final LootScript lootScript = new LootScript(); + private final SafeSpot safeSpotScript = new SafeSpot(); + private final FlickerScript flickerScript = new FlickerScript(); + private final UseSpecialAttackScript useSpecialAttackScript = new UseSpecialAttackScript(); + private final BuryScatterScript buryScatterScript = new BuryScatterScript(); + private final AttackStyleScript attackStyleScript = new AttackStyleScript(); + private final BankerScript bankerScript = new BankerScript(); + private final PrayerScript prayerScript = new PrayerScript(); + private final HighAlchScript highAlchScript = new HighAlchScript(); + private final PotionManagerScript potionManagerScript = new PotionManagerScript(); + private final SafetyScript safetyScript = new SafetyScript(); + //private final SlayerScript slayerScript = new SlayerScript(); + @Inject + private AIOFighterConfig config; + @Inject + private ConfigManager configManager; + @Inject + private OverlayManager overlayManager; + @Inject + private AIOFighterOverlay playerAssistOverlay; + @Inject + private AIOFighterInfoOverlay playerAssistInfoOverlay; + private MenuEntry lastClick; + private Point lastMenuOpenedPoint; + private WorldPoint trueTile; + + protected ScheduledExecutorService initializerExecutor = Executors.newSingleThreadScheduledExecutor(); + + @Provides + AIOFighterConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(AIOFighterConfig.class); + } + + @Override + protected void startUp() throws AWTException { + Microbot.pauseAllScripts.compareAndSet(true, false); + cooldown = 0; + lastNpcKilledTime = 0; + waitingForLoot = false; + //initialize any data on startup + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + AtomicReference> futureRef = new AtomicReference<>(); + + ScheduledFuture future = executor.scheduleWithFixedDelay(() -> { + if (Microbot.getConfigManager() == null) { + return; + } + setState(State.IDLE); + // Get the future from the reference and cancel it + ScheduledFuture scheduledFuture = futureRef.get(); + if (scheduledFuture != null) { + scheduledFuture.cancel(false); + } + // now that no other tasks run, you can shut down: + executor.shutdown(); + }, 0, 1, TimeUnit.SECONDS); + + if (overlayManager != null) { + overlayManager.add(playerAssistOverlay); + overlayManager.add(playerAssistInfoOverlay); + } + if (!config.toggleCenterTile() && Microbot.isLoggedIn()) + setCenter(Rs2Player.getWorldLocation()); + lootScript.run(config); + cannonScript.run(config); + attackNpc.run(config); + foodScript.run(config); + safeSpotScript.run(config); + flickerScript.run(config); + useSpecialAttackScript.run(config); + buryScatterScript.run(config); + attackStyleScript.run(config); + bankerScript.run(config); + prayerScript.run(config); + highAlchScript.run(config); + potionManagerScript.run(config); + safetyScript.run(config); + //slayerScript.run(config); + Microbot.getSpecialAttackConfigs() + .setSpecialAttack(true); + } + + protected void shutDown() { + lootScript.shutdown(); + cannonScript.shutdown(); + attackNpc.shutdown(); + foodScript.shutdown(); + safeSpotScript.shutdown(); + flickerScript.shutdown(); + useSpecialAttackScript.shutdown(); + buryScatterScript.shutdown(); + attackStyleScript.shutdown(); + bankerScript.shutdown(); + prayerScript.shutdown(); + highAlchScript.shutdown(); + potionManagerScript.shutdown(); + safetyScript.shutdown(); + //slayerScript.shutdown(); + resetLocation(); + overlayManager.remove(playerAssistOverlay); + overlayManager.remove(playerAssistInfoOverlay); + } + + public static void resetLocation() { + setCenter(new WorldPoint(0, 0, 0)); + setSafeSpot(new WorldPoint(0, 0, 0)); + } + + public static void setCenter(WorldPoint worldPoint) + { + Microbot.getConfigManager().setConfiguration( + "PlayerAssistant", + "centerLocation", + worldPoint + ); + } + // set safe spot + public static void setSafeSpot(WorldPoint worldPoint) + { + Microbot.getConfigManager().setConfiguration( + "PlayerAssistant", + "safeSpotLocation", + worldPoint + ); + + + } + //set Inventory Setup + private void setInventorySetup(InventorySetup inventorySetup) { + configManager.setConfiguration( + "PlayerAssistant", + "inventorySetupHidden", + inventorySetup + ); + } + + public static State getState() { + return Microbot.getConfigManager().getConfiguration( + "PlayerAssistant", + "state", + State.class + ); + } + + public static void setState(State state) { + Microbot.getConfigManager().setConfiguration( + "PlayerAssistant", + "state", + state + ); + } + + private void addNpcToList(String npcName) { + configManager.setConfiguration( + "PlayerAssistant", + "monster", + config.attackableNpcs() + npcName + "," + ); + + } + private void removeNpcFromList(String npcName) { + configManager.setConfiguration( + "PlayerAssistant", + "monster", + Arrays.stream(config.attackableNpcs().split(",")) + .filter(n -> !n.equalsIgnoreCase(npcName)) + .collect(Collectors.joining(",")) + ); + } + + // set attackable npcs + public static void setAttackableNpcs(String npcNames) { + Microbot.getConfigManager().setConfiguration( + "PlayerAssistant", + "monster", + npcNames + ); + } + + private String getNpcNameFromMenuEntry(String menuTarget) { + return menuTarget.replaceAll("<[^>]*>|\\(.*\\)", "").trim(); + } + + @Subscribe + public void onChatMessage(ChatMessage event) { + if (event.getMessage().contains("reach that")) { + AttackNpcScript.skipNpc(); + } + } + // on setting change + @Subscribe + public void onConfigChanged(ConfigChanged event) { + + + if (event.getKey().equals("Safe Spot")) { + + if (!config.toggleSafeSpot()) { + // reset safe spot to default + setSafeSpot(new WorldPoint(0, 0, 0)); + } + } + if(event.getKey().equals("Combat")) { + if (!config.toggleCombat() && config.toggleCenterTile()) { + setCenter(new WorldPoint(0, 0, 0)); + } + if (config.toggleCombat() && !config.toggleCenterTile()) { + setCenter(Rs2Player.getWorldLocation()); + } + + } + } + + + @Subscribe + public void onGameTick(GameTick gameTick) { + if (cooldown > 0 && !Rs2Combat.inCombat()) + cooldown--; + //execute flicker script + if(config.togglePrayer()) + flickerScript.onGameTick(); + } + + @Subscribe + public void onNpcDespawned(NpcDespawned npcDespawned) { + if(config.togglePrayer()) + flickerScript.onNpcDespawned(npcDespawned); + } + + @Subscribe + public void onActorDeath(ActorDeath event) { + if (!config.toggleWaitForLoot()) return; + + if (event.getActor() instanceof NPC) { + NPC npc = (NPC) event.getActor(); + + // Check if we were fighting this NPC + Player localPlayer = Microbot.getClient().getLocalPlayer(); + if (localPlayer != null && localPlayer.getInteracting() == npc) { + waitingForLoot = true; + lastNpcKilledTime = System.currentTimeMillis(); + Microbot.log("NPC died, waiting for loot..."); + } + } + } + + @Subscribe + public void onHitsplatApplied(HitsplatApplied event){ + if (event.getActor() != Microbot.getClient().getLocalPlayer()) return; + final Hitsplat hitsplat = event.getHitsplat(); + + if ((hitsplat.isMine()) && event.getActor().getInteracting() instanceof NPC && config.togglePrayer() && (config.prayerStyle() == PrayerStyle.LAZY_FLICK) || (config.prayerStyle() == PrayerStyle.PERFECT_LAZY_FLICK)) { + flickerScript.resetLastAttack(true); + Rs2Prayer.disableAllPrayers(); + if (config.toggleQuickPray()) + Rs2Prayer.toggleQuickPrayer(false); + + + } + } + @Subscribe + public void onMenuOpened(MenuOpened event) { + lastMenuOpenedPoint = Microbot.getClient().getMouseCanvasPosition(); + trueTile = getSelectedWorldPoint(); + } + @Subscribe + private void onMenuEntryAdded(MenuEntryAdded event) { + if (Microbot.getClient().isKeyPressed(KeyCode.KC_SHIFT) && event.getOption().equals(WALK_HERE) && event.getTarget().isEmpty() && config.toggleCenterTile()) { + addMenuEntry(event, SET, CENTER_TILE, 1); + } + if (Microbot.getClient().isKeyPressed(KeyCode.KC_SHIFT) && event.getOption().equals(WALK_HERE) && event.getTarget().isEmpty()) { + addMenuEntry(event, SET, SAFE_SPOT, 1); + } + if (event.getOption().equals(ATTACK) && config.attackableNpcs().contains(getNpcNameFromMenuEntry(Text.removeTags(event.getTarget())))) { + addMenuEntry(event, REMOVE_FROM, event.getTarget(), 1); + } + if (event.getOption().equals(ATTACK) && !config.attackableNpcs().contains(getNpcNameFromMenuEntry(Text.removeTags(event.getTarget())))) { + addMenuEntry(event, ADD_TO, event.getTarget(), 1); + } + + } + + private WorldPoint getSelectedWorldPoint() { + if (Microbot.getClient().getWidget(ComponentID.WORLD_MAP_MAPVIEW) == null) { + if (Microbot.getClient().getSelectedSceneTile() != null) { + return Microbot.getClient().isInInstancedRegion() ? + WorldPoint.fromLocalInstance(Microbot.getClient(), Microbot.getClient().getSelectedSceneTile().getLocalLocation()) : + Microbot.getClient().getSelectedSceneTile().getWorldLocation(); + } + } else { + return calculateMapPoint(Microbot.getClient().isMenuOpen() ? lastMenuOpenedPoint : Microbot.getClient().getMouseCanvasPosition()); + } + return null; + } + public WorldPoint calculateMapPoint(Point point) { + WorldMap worldMap = Microbot.getClient().getWorldMap(); + float zoom = worldMap.getWorldMapZoom(); + final WorldPoint mapPoint = new WorldPoint(worldMap.getWorldMapPosition().getX(), worldMap.getWorldMapPosition().getY(), 0); + final Point middle = mapWorldPointToGraphicsPoint(mapPoint); + + if (point == null || middle == null) { + return null; + } + + final int dx = (int) ((point.getX() - middle.getX()) / zoom); + final int dy = (int) ((-(point.getY() - middle.getY())) / zoom); + + return mapPoint.dx(dx).dy(dy); + } + public Point mapWorldPointToGraphicsPoint(WorldPoint worldPoint) { + WorldMap worldMap = Microbot.getClient().getWorldMap(); + + float pixelsPerTile = worldMap.getWorldMapZoom(); + + Widget map = Microbot.getClient().getWidget(ComponentID.WORLD_MAP_MAPVIEW); + if (map != null) { + Rectangle worldMapRect = map.getBounds(); + + int widthInTiles = (int) Math.ceil(worldMapRect.getWidth() / pixelsPerTile); + int heightInTiles = (int) Math.ceil(worldMapRect.getHeight() / pixelsPerTile); + + Point worldMapPosition = worldMap.getWorldMapPosition(); + + int yTileMax = worldMapPosition.getY() - heightInTiles / 2; + int yTileOffset = (yTileMax - worldPoint.getY() - 1) * -1; + int xTileOffset = worldPoint.getX() + widthInTiles / 2 - worldMapPosition.getX(); + + int xGraphDiff = ((int) (xTileOffset * pixelsPerTile)); + int yGraphDiff = (int) (yTileOffset * pixelsPerTile); + + yGraphDiff -= (int) (pixelsPerTile - Math.ceil(pixelsPerTile / 2)); + xGraphDiff += (int) (pixelsPerTile - Math.ceil(pixelsPerTile / 2)); + + yGraphDiff = worldMapRect.height - yGraphDiff; + yGraphDiff += (int) worldMapRect.getY(); + xGraphDiff += (int) worldMapRect.getX(); + + return new Point(xGraphDiff, yGraphDiff); + } + return null; + } + private void onMenuOptionClicked(MenuEntry entry) { + + + + if (entry.getOption().equals(SET) && entry.getTarget().equals(CENTER_TILE)) { + setCenter(trueTile); + } + if (entry.getOption().equals(SET) && entry.getTarget().equals(SAFE_SPOT)) { + setSafeSpot(trueTile); + } + + + + if (entry.getType() != MenuAction.WALK) { + lastClick = entry; + } + } + + + @Subscribe + private void onMenuOptionClicked(MenuOptionClicked event) + { + if (event.getMenuOption().equals(ADD_TO)) { + addNpcToList(getNpcNameFromMenuEntry(event.getMenuTarget())); + } + if (event.getMenuOption().equals(REMOVE_FROM)) { + removeNpcFromList(getNpcNameFromMenuEntry(event.getMenuTarget())); + } + } + private void addMenuEntry(MenuEntryAdded event, String option, String target, int position) { + List entries = new LinkedList<>(Arrays.asList(Microbot.getClient().getMenuEntries())); + + if (entries.stream().anyMatch(e -> e.getOption().equals(option) && e.getTarget().equals(target))) { + return; + } + + Microbot.getClient().createMenuEntry(position) + .setOption(option) + .setTarget(target) + .setParam0(event.getActionParam0()) + .setParam1(event.getActionParam1()) + .setIdentifier(event.getIdentifier()) + .setType(MenuAction.RUNELITE) + .onClick(this::onMenuOptionClicked); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java index e50fe66cb18..2265c1b27d0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java @@ -1,153 +1,170 @@ -package net.runelite.client.plugins.microbot.aiofighter.combat; - -import lombok.SneakyThrows; -import net.runelite.api.Actor; -import net.runelite.api.gameval.ItemID; -import net.runelite.api.coords.WorldPoint; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.Script; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; -import net.runelite.client.plugins.microbot.aiofighter.enums.State; -import net.runelite.client.plugins.microbot.shortestpath.ShortestPathPlugin; -import net.runelite.client.plugins.microbot.util.ActorModel; -import net.runelite.client.plugins.microbot.util.camera.Rs2Camera; -import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; -import net.runelite.client.plugins.microbot.util.coords.Rs2WorldArea; -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.Rs2NpcManager; -import net.runelite.client.plugins.microbot.util.npc.Rs2NpcModel; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; -import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; - -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -public class AttackNpcScript extends Script { - - public static Actor currentNpc = null; - public static AtomicReference> filteredAttackableNpcs = new AtomicReference<>(new ArrayList<>()); - public static Rs2WorldArea attackableArea = null; - private boolean messageShown = false; - - public static void skipNpc() { - currentNpc = null; - } - - @SneakyThrows - public void run(AIOFighterConfig config) { - try { - Rs2NpcManager.loadJson(); - } catch (Exception e) { - throw new RuntimeException(e); - } - - mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { - try { - if (!Microbot.isLoggedIn() || !super.run() || !config.toggleCombat()) - return; - - if(config.centerLocation().distanceTo(Rs2Player.getWorldLocation()) < config.attackRadius() && - !config.centerLocation().equals(new WorldPoint(0, 0, 0)) && AIOFighterPlugin.getState() != State.BANKING) { - if(ShortestPathPlugin.getPathfinder() != null) - Rs2Walker.setTarget(null); - AIOFighterPlugin.setState(State.IDLE); - } - - attackableArea = new Rs2WorldArea(config.centerLocation().toWorldArea()); - attackableArea = attackableArea.offset(config.attackRadius()); - List npcsToAttack = Arrays.stream(config.attackableNpcs().split(",")) - .map(x -> x.trim().toLowerCase()) - .collect(Collectors.toList()); - - filteredAttackableNpcs.set( - Rs2Npc.getAttackableNpcs(config.attackReachableNpcs()) - .filter(npc -> npc.getWorldLocation().distanceTo(config.centerLocation()) <= config.attackRadius()) - .filter(npc -> { - String name = npc.getName(); - if (name == null || name.isEmpty()) return false; - return !npcsToAttack.isEmpty() && npcsToAttack.stream().anyMatch(name::equalsIgnoreCase); - }) - .sorted(Comparator.comparingInt((Rs2NpcModel npc) -> Objects.equals(npc.getInteracting(), Microbot.getClient().getLocalPlayer()) ? 0 : 1) - .thenComparingInt(npc -> Rs2Player.getRs2WorldPoint().distanceToPath(npc.getWorldLocation()))) - .collect(Collectors.toList()) - ); - final List attackableNpcs = new ArrayList<>(); - - for (var attackableNpc: filteredAttackableNpcs.get()) { - if (attackableNpc == null || attackableNpc.getName() == null) continue; - for (var npcToAttack: npcsToAttack) { - if (npcToAttack.equalsIgnoreCase(attackableNpc.getName())) { - attackableNpcs.add(attackableNpc); - } - } - } - - filteredAttackableNpcs.set(attackableNpcs); - - if(config.state().equals(State.BANKING) || config.state().equals(State.WALKING)) - return; - - if (config.toggleCenterTile() && config.centerLocation().getX() == 0 - && config.centerLocation().getY() == 0) { - if (!messageShown) { - Microbot.showMessage("Please set a center location"); - messageShown = true; - } - return; - } - messageShown = false; - - if (AIOFighterPlugin.getCooldown() > 0 || Rs2Combat.inCombat()) { - AIOFighterPlugin.setState(State.COMBAT); - handleItemOnNpcToKill(); - return; - } - - if (!attackableNpcs.isEmpty()) { - Rs2NpcModel npc = attackableNpcs.stream().findFirst().orElse(null); - - if (!Rs2Camera.isTileOnScreen(npc.getLocalLocation())) - Rs2Camera.turnTo(npc); - - Rs2Npc.interact(npc, "attack"); - Microbot.status = "Attacking " + npc.getName(); - AIOFighterPlugin.setCooldown(config.playStyle().getRandomTickInterval()); - - } else { - Microbot.log("No attackable NPC found"); - } - } catch (Exception ex) { - Microbot.logStackTrace(this.getClass().getSimpleName(), ex); - } - }, 0, 600, TimeUnit.MILLISECONDS); - } - - - /** - * item on npcs that need to kill like rockslug - */ - private void handleItemOnNpcToKill() { - Rs2NpcModel npc = Rs2Npc.getNpcsForPlayer(ActorModel::isDead).findFirst().orElse(null); - List lizardVariants = new ArrayList<>(Arrays.asList("Lizard", "Desert Lizard", "Small Lizard")); - if (npc == null) return; - if (lizardVariants.contains(npc.getName()) && npc.getHealthRatio() < 5) { - Rs2Inventory.useItemOnNpc(ItemID.SLAYER_BAG_OF_SALT, npc); - Rs2Player.waitForAnimation(); - } else if (npc.getName().equalsIgnoreCase("rockslug") && npc.getHealthRatio() < 5) { - Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ICY_WATER, npc); - Rs2Player.waitForAnimation(); - } else if (npc.getName().equalsIgnoreCase("gargoyle") && npc.getHealthRatio() < 3) { - Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ROCK_HAMMER, npc); - Rs2Player.waitForAnimation(); - } - } - - @Override - public void shutdown() { - super.shutdown(); - } +package net.runelite.client.plugins.microbot.aiofighter.combat; + +import lombok.SneakyThrows; +import net.runelite.api.Actor; +import net.runelite.api.gameval.ItemID; +import net.runelite.api.coords.WorldPoint; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.Script; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; +import net.runelite.client.plugins.microbot.aiofighter.enums.State; +import net.runelite.client.plugins.microbot.shortestpath.ShortestPathPlugin; +import net.runelite.client.plugins.microbot.util.ActorModel; +import net.runelite.client.plugins.microbot.util.camera.Rs2Camera; +import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; +import net.runelite.client.plugins.microbot.util.coords.Rs2WorldArea; +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.Rs2NpcManager; +import net.runelite.client.plugins.microbot.util.npc.Rs2NpcModel; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; +import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +public class AttackNpcScript extends Script { + + public static Actor currentNpc = null; + public static AtomicReference> filteredAttackableNpcs = new AtomicReference<>(new ArrayList<>()); + public static Rs2WorldArea attackableArea = null; + private boolean messageShown = false; + + public static void skipNpc() { + currentNpc = null; + } + + @SneakyThrows + public void run(AIOFighterConfig config) { + try { + Rs2NpcManager.loadJson(); + } catch (Exception e) { + throw new RuntimeException(e); + } + + mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { + try { + if (!Microbot.isLoggedIn() || !super.run() || !config.toggleCombat()) + return; + + if(config.centerLocation().distanceTo(Rs2Player.getWorldLocation()) < config.attackRadius() && + !config.centerLocation().equals(new WorldPoint(0, 0, 0)) && AIOFighterPlugin.getState() != State.BANKING) { + if(ShortestPathPlugin.getPathfinder() != null) + Rs2Walker.setTarget(null); + AIOFighterPlugin.setState(State.IDLE); + } + + attackableArea = new Rs2WorldArea(config.centerLocation().toWorldArea()); + attackableArea = attackableArea.offset(config.attackRadius()); + List npcsToAttack = Arrays.stream(config.attackableNpcs().split(",")) + .map(x -> x.trim().toLowerCase()) + .collect(Collectors.toList()); + + filteredAttackableNpcs.set( + Rs2Npc.getAttackableNpcs(config.attackReachableNpcs()) + .filter(npc -> npc.getWorldLocation().distanceTo(config.centerLocation()) <= config.attackRadius()) + .filter(npc -> { + String name = npc.getName(); + if (name == null || name.isEmpty()) return false; + return !npcsToAttack.isEmpty() && npcsToAttack.stream().anyMatch(name::equalsIgnoreCase); + }) + .sorted(Comparator.comparingInt((Rs2NpcModel npc) -> Objects.equals(npc.getInteracting(), Microbot.getClient().getLocalPlayer()) ? 0 : 1) + .thenComparingInt(npc -> Rs2Player.getRs2WorldPoint().distanceToPath(npc.getWorldLocation()))) + .collect(Collectors.toList()) + ); + final List attackableNpcs = new ArrayList<>(); + + for (var attackableNpc: filteredAttackableNpcs.get()) { + if (attackableNpc == null || attackableNpc.getName() == null) continue; + for (var npcToAttack: npcsToAttack) { + if (npcToAttack.equalsIgnoreCase(attackableNpc.getName())) { + attackableNpcs.add(attackableNpc); + } + } + } + + filteredAttackableNpcs.set(attackableNpcs); + + if(config.state().equals(State.BANKING) || config.state().equals(State.WALKING)) + return; + + if (config.toggleCenterTile() && config.centerLocation().getX() == 0 + && config.centerLocation().getY() == 0) { + if (!messageShown) { + Microbot.showMessage("Please set a center location"); + messageShown = true; + } + return; + } + messageShown = false; + + // Check if we should wait for loot + if (config.toggleWaitForLoot() && AIOFighterPlugin.isWaitingForLoot()) { + long timeSinceKill = System.currentTimeMillis() - AIOFighterPlugin.getLastNpcKilledTime(); + + if (timeSinceKill >= AIOFighterPlugin.LOOT_WAIT_TIMEOUT) { + // Timeout reached - stop waiting and resume combat + AIOFighterPlugin.setWaitingForLoot(false); + AIOFighterPlugin.setLastNpcKilledTime(0); + Microbot.log("Loot wait timeout reached, resuming combat"); + } else { + // Still waiting - don't attack + int secondsLeft = (int)((AIOFighterPlugin.LOOT_WAIT_TIMEOUT - timeSinceKill) / 1000); + Microbot.status = "Waiting for loot (" + secondsLeft + "s)"; + return; + } + } + + if (AIOFighterPlugin.getCooldown() > 0 || Rs2Combat.inCombat()) { + AIOFighterPlugin.setState(State.COMBAT); + handleItemOnNpcToKill(); + return; + } + + if (!attackableNpcs.isEmpty()) { + Rs2NpcModel npc = attackableNpcs.stream().findFirst().orElse(null); + + if (!Rs2Camera.isTileOnScreen(npc.getLocalLocation())) + Rs2Camera.turnTo(npc); + + Rs2Npc.interact(npc, "attack"); + Microbot.status = "Attacking " + npc.getName(); + AIOFighterPlugin.setCooldown(config.playStyle().getRandomTickInterval()); + + } else { + Microbot.log("No attackable NPC found"); + } + } catch (Exception ex) { + Microbot.logStackTrace(this.getClass().getSimpleName(), ex); + } + }, 0, 600, TimeUnit.MILLISECONDS); + } + + + /** + * item on npcs that need to kill like rockslug + */ + private void handleItemOnNpcToKill() { + Rs2NpcModel npc = Rs2Npc.getNpcsForPlayer(ActorModel::isDead).findFirst().orElse(null); + List lizardVariants = new ArrayList<>(Arrays.asList("Lizard", "Desert Lizard", "Small Lizard")); + if (npc == null) return; + if (lizardVariants.contains(npc.getName()) && npc.getHealthRatio() < 5) { + Rs2Inventory.useItemOnNpc(ItemID.SLAYER_BAG_OF_SALT, npc); + Rs2Player.waitForAnimation(); + } else if (npc.getName().equalsIgnoreCase("rockslug") && npc.getHealthRatio() < 5) { + Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ICY_WATER, npc); + Rs2Player.waitForAnimation(); + } else if (npc.getName().equalsIgnoreCase("gargoyle") && npc.getHealthRatio() < 3) { + Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ROCK_HAMMER, npc); + Rs2Player.waitForAnimation(); + } + } + + @Override + public void shutdown() { + super.shutdown(); + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/enums/State.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/enums/State.java index 50cae14596f..9b5de6b1490 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/enums/State.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/enums/State.java @@ -1,11 +1,11 @@ -package net.runelite.client.plugins.microbot.aiofighter.enums; - -public enum State { - IDLE, - WALKING, - BANKING, - COMBAT, - DEATH, - MISC, - UNKNOWN -} +package net.runelite.client.plugins.microbot.aiofighter.enums; + +public enum State { + IDLE, + WALKING, + BANKING, + COMBAT, + DEATH, + MISC, + UNKNOWN +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java index cb1fba09001..b408211d723 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java @@ -1,200 +1,200 @@ -package net.runelite.client.plugins.microbot.aiofighter.loot; - -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.Script; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; -import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.State; -import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; -import net.runelite.client.plugins.microbot.util.grounditem.LootingParameters; -import net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem; -import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; - -import java.util.concurrent.TimeUnit; - -@Slf4j -public class LootScript extends Script { - int minFreeSlots = 0; - - public LootScript() { - - } - - - public boolean run(AIOFighterConfig config) { - - mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { - try { - minFreeSlots = config.bank() ? config.minFreeSlots() : 0; - if (!super.run()) return; - if (!Microbot.isLoggedIn()) return; - if (AIOFighterPlugin.getState().equals(State.BANKING) || AIOFighterPlugin.getState().equals(State.WALKING)) return; - if (Rs2Inventory.isFull() || Rs2Inventory.getEmptySlots() <= minFreeSlots || (Rs2Combat.inCombat() && !config.toggleForceLoot())) - return; - - - - if (!config.toggleLootItems()) return; - if (config.looterStyle().equals(DefaultLooterStyle.MIXED) || config.looterStyle().equals(DefaultLooterStyle.ITEM_LIST)) { - lootItemsOnName(config); - } - - if (config.looterStyle().equals(DefaultLooterStyle.GE_PRICE_RANGE) || config.looterStyle().equals(DefaultLooterStyle.MIXED)) { - lootItemsByValue(config); - } - lootBones(config); - lootAshes(config); - lootRunes(config); - lootCoins(config); - lootUntradeableItems(config); - lootArrows(config); - - } catch(Exception ex) { - Microbot.log("Looterscript: " + ex.getMessage()); - } - - }, 0, 200, TimeUnit.MILLISECONDS); - return true; - } - - private void lootArrows(AIOFighterConfig config) { - if (config.toggleLootArrows()) { - LootingParameters arrowParams = new LootingParameters( - config.attackRadius(), - 1, - 10, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - "arrow" - ); - if (Rs2GroundItem.lootItemsBasedOnNames(arrowParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - private void lootBones(AIOFighterConfig config) { - if (config.toggleBuryBones()) { - LootingParameters bonesParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - "bones" - ); - if (Rs2GroundItem.lootItemsBasedOnNames(bonesParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - private void lootAshes(AIOFighterConfig config) { - if (config.toggleScatter()) { - LootingParameters ashesParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - " ashes" - ); - if (Rs2GroundItem.lootItemsBasedOnNames(ashesParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - // loot runes - private void lootRunes(AIOFighterConfig config) { - if (config.toggleLootRunes()) { - LootingParameters runesParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - " rune" - ); - if (Rs2GroundItem.lootItemsBasedOnNames(runesParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - // loot coins - private void lootCoins(AIOFighterConfig config) { - if (config.toggleLootCoins()) { - LootingParameters coinsParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - "coins" - ); - if (Rs2GroundItem.lootCoins(coinsParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - // loot untreadable items - private void lootUntradeableItems(AIOFighterConfig config) { - if (config.toggleLootUntradables()) { - LootingParameters untradeableItemsParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - "untradeable" - ); - if (Rs2GroundItem.lootUntradables(untradeableItemsParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - private void lootItemsByValue(AIOFighterConfig config) { - LootingParameters valueParams = new LootingParameters( - config.minPriceOfItemsToLoot(), - config.maxPriceOfItemsToLoot(), - config.attackRadius(), - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems() - ); - if (Rs2GroundItem.lootItemBasedOnValue(valueParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - - private void lootItemsOnName(AIOFighterConfig config) { - LootingParameters valueParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - config.listOfItemsToLoot().trim().split(",") - ); - if (Rs2GroundItem.lootItemsBasedOnNames(valueParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - - public void shutdown() { - super.shutdown(); - } -} +package net.runelite.client.plugins.microbot.aiofighter.loot; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.Script; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; +import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.State; +import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; +import net.runelite.client.plugins.microbot.util.grounditem.LootingParameters; +import net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem; +import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; + +import java.util.concurrent.TimeUnit; + +@Slf4j +public class LootScript extends Script { + int minFreeSlots = 0; + + public LootScript() { + + } + + + public boolean run(AIOFighterConfig config) { + + mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { + try { + minFreeSlots = config.bank() ? config.minFreeSlots() : 0; + if (!super.run()) return; + if (!Microbot.isLoggedIn()) return; + if (AIOFighterPlugin.getState().equals(State.BANKING) || AIOFighterPlugin.getState().equals(State.WALKING)) return; + if (Rs2Inventory.isFull() || Rs2Inventory.getEmptySlots() <= minFreeSlots || (Rs2Combat.inCombat() && !config.toggleForceLoot())) + return; + + + + if (!config.toggleLootItems()) return; + if (config.looterStyle().equals(DefaultLooterStyle.MIXED) || config.looterStyle().equals(DefaultLooterStyle.ITEM_LIST)) { + lootItemsOnName(config); + } + + if (config.looterStyle().equals(DefaultLooterStyle.GE_PRICE_RANGE) || config.looterStyle().equals(DefaultLooterStyle.MIXED)) { + lootItemsByValue(config); + } + lootBones(config); + lootAshes(config); + lootRunes(config); + lootCoins(config); + lootUntradeableItems(config); + lootArrows(config); + + } catch(Exception ex) { + Microbot.log("Looterscript: " + ex.getMessage()); + } + + }, 0, 200, TimeUnit.MILLISECONDS); + return true; + } + + private void lootArrows(AIOFighterConfig config) { + if (config.toggleLootArrows()) { + LootingParameters arrowParams = new LootingParameters( + config.attackRadius(), + 1, + 10, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + "arrow" + ); + if (Rs2GroundItem.lootItemsBasedOnNames(arrowParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + private void lootBones(AIOFighterConfig config) { + if (config.toggleBuryBones()) { + LootingParameters bonesParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + "bones" + ); + if (Rs2GroundItem.lootItemsBasedOnNames(bonesParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + private void lootAshes(AIOFighterConfig config) { + if (config.toggleScatter()) { + LootingParameters ashesParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + " ashes" + ); + if (Rs2GroundItem.lootItemsBasedOnNames(ashesParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + // loot runes + private void lootRunes(AIOFighterConfig config) { + if (config.toggleLootRunes()) { + LootingParameters runesParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + " rune" + ); + if (Rs2GroundItem.lootItemsBasedOnNames(runesParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + // loot coins + private void lootCoins(AIOFighterConfig config) { + if (config.toggleLootCoins()) { + LootingParameters coinsParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + "coins" + ); + if (Rs2GroundItem.lootCoins(coinsParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + // loot untreadable items + private void lootUntradeableItems(AIOFighterConfig config) { + if (config.toggleLootUntradables()) { + LootingParameters untradeableItemsParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + "untradeable" + ); + if (Rs2GroundItem.lootUntradables(untradeableItemsParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + private void lootItemsByValue(AIOFighterConfig config) { + LootingParameters valueParams = new LootingParameters( + config.minPriceOfItemsToLoot(), + config.maxPriceOfItemsToLoot(), + config.attackRadius(), + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems() + ); + if (Rs2GroundItem.lootItemBasedOnValue(valueParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + + private void lootItemsOnName(AIOFighterConfig config) { + LootingParameters valueParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + config.listOfItemsToLoot().trim().split(",") + ); + if (Rs2GroundItem.lootItemsBasedOnNames(valueParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + + public void shutdown() { + super.shutdown(); + } +} From b2ab130d04f8f92bcc6850529a1aa8df615217ab Mon Sep 17 00:00:00 2001 From: Pert Date: Tue, 19 Aug 2025 14:13:59 -0400 Subject: [PATCH 02/16] fix(AIOFighter): Fix wait-for-loot interrupting looting actions - 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 --- .../microbot/aiofighter/AIOFighterConfig.java | 11 + .../microbot/aiofighter/AIOFighterPlugin.java | 22 + .../aiofighter/combat/AttackNpcScript.java | 49 ++- .../microbot/aiofighter/loot/LootScript.java | 398 +++++++++--------- 4 files changed, 278 insertions(+), 202 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java index dddbd87d77b..7192043a81b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java @@ -387,6 +387,17 @@ default boolean toggleHighAlchProfitable() { ) default boolean eatFoodForSpace() { return false; } + @ConfigItem( + keyName = "waitForLoot", + name = "Wait for Loot", + description = "Wait for loot to appear before attacking next NPC (6 second timeout)", + position = 103, + section = lootSection + ) + default boolean toggleWaitForLoot() { + return false; + } + //set center tile manually @ConfigItem( keyName = "Center Tile", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java index 08ef7e65806..68173c0f649 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java @@ -69,6 +69,15 @@ public class AIOFighterPlugin extends Plugin { @Getter @Setter public static int cooldown = 0; + + @Getter @Setter + private static long lastNpcKilledTime = 0; + + @Getter @Setter + private static boolean waitingForLoot = false; + + public static final int LOOT_WAIT_TIMEOUT = 6000; // 6 seconds + private final CannonScript cannonScript = new CannonScript(); private final AttackNpcScript attackNpc = new AttackNpcScript(); @@ -429,6 +438,19 @@ public void onNpcDespawned(NpcDespawned npcDespawned) { try { if(config.togglePrayer()) flickerScript.onNpcDespawned(npcDespawned); + + // Handle wait for loot feature + if (config.toggleWaitForLoot()) { + NPC npc = npcDespawned.getNpc(); + if (npc != null && npc.isDead()) { + Player localPlayer = Microbot.getClient().getLocalPlayer(); + if (localPlayer != null && localPlayer.getInteracting() == npc) { + waitingForLoot = true; + lastNpcKilledTime = System.currentTimeMillis(); + Microbot.log("NPC died, waiting for loot..."); + } + } + } } catch (Exception e) { log.info("AIO Fighter Plugin onNpcDespawned Error: " + e.getMessage()); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java index 8d96cfee112..e36d8b1c895 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java @@ -99,11 +99,51 @@ public void run(AIOFighterConfig config) { if(config.state().equals(State.BANKING) || config.state().equals(State.WALKING)) return; + // Check if we should pause while looting is happening + if (Microbot.pauseAllScripts.get()) { + return; // Don't attack while looting + } + + // Check if our current target just died and we should wait for loot + if (config.toggleWaitForLoot() && !AIOFighterPlugin.isWaitingForLoot()) { + // Check if we were recently in combat but no longer interacting (NPC just died) + Actor currentInteracting = Rs2Player.getInteracting(); + + // If we're not interacting but were recently, the NPC probably just died + if (currentInteracting == null && Rs2Player.isInCombat()) { + // We were in combat but lost our target - NPC likely died + AIOFighterPlugin.setWaitingForLoot(true); + AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis()); + Microbot.log("Lost target while in combat, waiting for loot..."); + return; + } + + if (currentInteracting instanceof net.runelite.api.NPC) { + net.runelite.api.NPC npc = (net.runelite.api.NPC) currentInteracting; + if (npc.isDead() || (npc.getHealthRatio() == 0 && npc.getHealthScale() > 0)) { + AIOFighterPlugin.setWaitingForLoot(true); + AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis()); + Microbot.log("NPC died, waiting for loot..."); + return; + } + } + } - - - - + // Check if we're waiting for loot + if (config.toggleWaitForLoot() && AIOFighterPlugin.isWaitingForLoot()) { + long timeSinceKill = System.currentTimeMillis() - AIOFighterPlugin.getLastNpcKilledTime(); + if (timeSinceKill >= AIOFighterPlugin.LOOT_WAIT_TIMEOUT) { + // Timeout reached, resume combat + AIOFighterPlugin.setWaitingForLoot(false); + AIOFighterPlugin.setLastNpcKilledTime(0); + Microbot.log("Loot wait timeout reached, resuming combat"); + } else { + // Still waiting for loot, don't attack + int secondsLeft = (int)((AIOFighterPlugin.LOOT_WAIT_TIMEOUT - timeSinceKill) / 1000); + Microbot.status = "Waiting for loot... " + secondsLeft + "s"; + return; + } + } if (config.toggleCenterTile() && config.centerLocation().getX() == 0 && config.centerLocation().getY() == 0) { @@ -126,6 +166,7 @@ public void run(AIOFighterConfig config) { if (!attackableNpcs.isEmpty()) { noNpcCount = 0; + Rs2NpcModel npc = attackableNpcs.stream().findFirst().orElse(null); if (!Rs2Camera.isTileOnScreen(npc.getLocalLocation())) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java index 6e4afbc481a..b408211d723 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java @@ -1,198 +1,200 @@ -package net.runelite.client.plugins.microbot.aiofighter.loot; - -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.Script; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; -import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.State; -import net.runelite.client.plugins.microbot.util.grounditem.LootingParameters; -import net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem; -import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; - -import java.util.concurrent.TimeUnit; - -@Slf4j -public class LootScript extends Script { - int minFreeSlots = 0; - - public LootScript() { - - } - - - public boolean run(AIOFighterConfig config) { - - mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { - try { - minFreeSlots = config.bank() ? config.minFreeSlots() : 0; - if (!super.run()) return; - if (!Microbot.isLoggedIn()) return; - if (AIOFighterPlugin.getState().equals(State.BANKING) || AIOFighterPlugin.getState().equals(State.WALKING)) return; - if (Rs2Inventory.isFull() || Rs2Inventory.getEmptySlots() <= minFreeSlots || (Rs2Player.isInCombat() && !config.toggleForceLoot())) - return; - - if (!config.toggleLootItems()) return; - if (config.looterStyle().equals(DefaultLooterStyle.MIXED) || config.looterStyle().equals(DefaultLooterStyle.ITEM_LIST)) { - lootItemsOnName(config); - } - - if (config.looterStyle().equals(DefaultLooterStyle.GE_PRICE_RANGE) || config.looterStyle().equals(DefaultLooterStyle.MIXED)) { - lootItemsByValue(config); - } - lootBones(config); - lootAshes(config); - lootRunes(config); - lootCoins(config); - lootUntradeableItems(config); - lootArrows(config); - - } catch(Exception ex) { - Microbot.log("Looterscript: " + ex.getMessage()); - } - - }, 0, 200, TimeUnit.MILLISECONDS); - return true; - } - - private void lootArrows(AIOFighterConfig config) { - if (config.toggleLootArrows()) { - LootingParameters arrowParams = new LootingParameters( - config.attackRadius(), - 1, - 10, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - "arrow" - ); - if (Rs2GroundItem.lootItemsBasedOnNames(arrowParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - private void lootBones(AIOFighterConfig config) { - if (config.toggleBuryBones()) { - LootingParameters bonesParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - "bones" - ); - if (Rs2GroundItem.lootItemsBasedOnNames(bonesParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - private void lootAshes(AIOFighterConfig config) { - if (config.toggleScatter()) { - LootingParameters ashesParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - " ashes" - ); - if (Rs2GroundItem.lootItemsBasedOnNames(ashesParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - // loot runes - private void lootRunes(AIOFighterConfig config) { - if (config.toggleLootRunes()) { - LootingParameters runesParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - " rune" - ); - if (Rs2GroundItem.lootItemsBasedOnNames(runesParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - // loot coins - private void lootCoins(AIOFighterConfig config) { - if (config.toggleLootCoins()) { - LootingParameters coinsParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - "coins" - ); - if (Rs2GroundItem.lootCoins(coinsParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - // loot untradeable items - private void lootUntradeableItems(AIOFighterConfig config) { - if (config.toggleLootUntradables()) { - LootingParameters untradeableItemsParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - "untradeable" - ); - if (Rs2GroundItem.lootUntradables(untradeableItemsParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - private void lootItemsByValue(AIOFighterConfig config) { - LootingParameters valueParams = new LootingParameters( - config.minPriceOfItemsToLoot(), - config.maxPriceOfItemsToLoot(), - config.attackRadius(), - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems() - ); - if (Rs2GroundItem.lootItemBasedOnValue(valueParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - - private void lootItemsOnName(AIOFighterConfig config) { - LootingParameters valueParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - config.listOfItemsToLoot().trim().split(",") - ); - if (Rs2GroundItem.lootItemsBasedOnNames(valueParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - - public void shutdown() { - super.shutdown(); - } -} +package net.runelite.client.plugins.microbot.aiofighter.loot; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.Script; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; +import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.State; +import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; +import net.runelite.client.plugins.microbot.util.grounditem.LootingParameters; +import net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem; +import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; + +import java.util.concurrent.TimeUnit; + +@Slf4j +public class LootScript extends Script { + int minFreeSlots = 0; + + public LootScript() { + + } + + + public boolean run(AIOFighterConfig config) { + + mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { + try { + minFreeSlots = config.bank() ? config.minFreeSlots() : 0; + if (!super.run()) return; + if (!Microbot.isLoggedIn()) return; + if (AIOFighterPlugin.getState().equals(State.BANKING) || AIOFighterPlugin.getState().equals(State.WALKING)) return; + if (Rs2Inventory.isFull() || Rs2Inventory.getEmptySlots() <= minFreeSlots || (Rs2Combat.inCombat() && !config.toggleForceLoot())) + return; + + + + if (!config.toggleLootItems()) return; + if (config.looterStyle().equals(DefaultLooterStyle.MIXED) || config.looterStyle().equals(DefaultLooterStyle.ITEM_LIST)) { + lootItemsOnName(config); + } + + if (config.looterStyle().equals(DefaultLooterStyle.GE_PRICE_RANGE) || config.looterStyle().equals(DefaultLooterStyle.MIXED)) { + lootItemsByValue(config); + } + lootBones(config); + lootAshes(config); + lootRunes(config); + lootCoins(config); + lootUntradeableItems(config); + lootArrows(config); + + } catch(Exception ex) { + Microbot.log("Looterscript: " + ex.getMessage()); + } + + }, 0, 200, TimeUnit.MILLISECONDS); + return true; + } + + private void lootArrows(AIOFighterConfig config) { + if (config.toggleLootArrows()) { + LootingParameters arrowParams = new LootingParameters( + config.attackRadius(), + 1, + 10, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + "arrow" + ); + if (Rs2GroundItem.lootItemsBasedOnNames(arrowParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + private void lootBones(AIOFighterConfig config) { + if (config.toggleBuryBones()) { + LootingParameters bonesParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + "bones" + ); + if (Rs2GroundItem.lootItemsBasedOnNames(bonesParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + private void lootAshes(AIOFighterConfig config) { + if (config.toggleScatter()) { + LootingParameters ashesParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + " ashes" + ); + if (Rs2GroundItem.lootItemsBasedOnNames(ashesParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + // loot runes + private void lootRunes(AIOFighterConfig config) { + if (config.toggleLootRunes()) { + LootingParameters runesParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + " rune" + ); + if (Rs2GroundItem.lootItemsBasedOnNames(runesParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + // loot coins + private void lootCoins(AIOFighterConfig config) { + if (config.toggleLootCoins()) { + LootingParameters coinsParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + "coins" + ); + if (Rs2GroundItem.lootCoins(coinsParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + // loot untreadable items + private void lootUntradeableItems(AIOFighterConfig config) { + if (config.toggleLootUntradables()) { + LootingParameters untradeableItemsParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + "untradeable" + ); + if (Rs2GroundItem.lootUntradables(untradeableItemsParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + private void lootItemsByValue(AIOFighterConfig config) { + LootingParameters valueParams = new LootingParameters( + config.minPriceOfItemsToLoot(), + config.maxPriceOfItemsToLoot(), + config.attackRadius(), + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems() + ); + if (Rs2GroundItem.lootItemBasedOnValue(valueParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + + private void lootItemsOnName(AIOFighterConfig config) { + LootingParameters valueParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + config.listOfItemsToLoot().trim().split(",") + ); + if (Rs2GroundItem.lootItemsBasedOnNames(valueParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + + public void shutdown() { + super.shutdown(); + } +} From 7f1b5f9a58b03c21347352a3cb71fc69c6259e8f Mon Sep 17 00:00:00 2001 From: Pert Date: Tue, 19 Aug 2025 15:29:10 -0400 Subject: [PATCH 03/16] fix(AIOFighter): Remove unnecessary formatting changes from LootScript --- .../microbot/aiofighter/loot/LootScript.java | 398 +++++++++--------- 1 file changed, 198 insertions(+), 200 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java index b408211d723..6e4afbc481a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java @@ -1,200 +1,198 @@ -package net.runelite.client.plugins.microbot.aiofighter.loot; - -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.Script; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; -import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.State; -import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; -import net.runelite.client.plugins.microbot.util.grounditem.LootingParameters; -import net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem; -import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; - -import java.util.concurrent.TimeUnit; - -@Slf4j -public class LootScript extends Script { - int minFreeSlots = 0; - - public LootScript() { - - } - - - public boolean run(AIOFighterConfig config) { - - mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { - try { - minFreeSlots = config.bank() ? config.minFreeSlots() : 0; - if (!super.run()) return; - if (!Microbot.isLoggedIn()) return; - if (AIOFighterPlugin.getState().equals(State.BANKING) || AIOFighterPlugin.getState().equals(State.WALKING)) return; - if (Rs2Inventory.isFull() || Rs2Inventory.getEmptySlots() <= minFreeSlots || (Rs2Combat.inCombat() && !config.toggleForceLoot())) - return; - - - - if (!config.toggleLootItems()) return; - if (config.looterStyle().equals(DefaultLooterStyle.MIXED) || config.looterStyle().equals(DefaultLooterStyle.ITEM_LIST)) { - lootItemsOnName(config); - } - - if (config.looterStyle().equals(DefaultLooterStyle.GE_PRICE_RANGE) || config.looterStyle().equals(DefaultLooterStyle.MIXED)) { - lootItemsByValue(config); - } - lootBones(config); - lootAshes(config); - lootRunes(config); - lootCoins(config); - lootUntradeableItems(config); - lootArrows(config); - - } catch(Exception ex) { - Microbot.log("Looterscript: " + ex.getMessage()); - } - - }, 0, 200, TimeUnit.MILLISECONDS); - return true; - } - - private void lootArrows(AIOFighterConfig config) { - if (config.toggleLootArrows()) { - LootingParameters arrowParams = new LootingParameters( - config.attackRadius(), - 1, - 10, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - "arrow" - ); - if (Rs2GroundItem.lootItemsBasedOnNames(arrowParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - private void lootBones(AIOFighterConfig config) { - if (config.toggleBuryBones()) { - LootingParameters bonesParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - "bones" - ); - if (Rs2GroundItem.lootItemsBasedOnNames(bonesParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - private void lootAshes(AIOFighterConfig config) { - if (config.toggleScatter()) { - LootingParameters ashesParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - " ashes" - ); - if (Rs2GroundItem.lootItemsBasedOnNames(ashesParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - // loot runes - private void lootRunes(AIOFighterConfig config) { - if (config.toggleLootRunes()) { - LootingParameters runesParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - " rune" - ); - if (Rs2GroundItem.lootItemsBasedOnNames(runesParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - // loot coins - private void lootCoins(AIOFighterConfig config) { - if (config.toggleLootCoins()) { - LootingParameters coinsParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - "coins" - ); - if (Rs2GroundItem.lootCoins(coinsParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - // loot untreadable items - private void lootUntradeableItems(AIOFighterConfig config) { - if (config.toggleLootUntradables()) { - LootingParameters untradeableItemsParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - "untradeable" - ); - if (Rs2GroundItem.lootUntradables(untradeableItemsParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - } - - private void lootItemsByValue(AIOFighterConfig config) { - LootingParameters valueParams = new LootingParameters( - config.minPriceOfItemsToLoot(), - config.maxPriceOfItemsToLoot(), - config.attackRadius(), - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems() - ); - if (Rs2GroundItem.lootItemBasedOnValue(valueParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - - private void lootItemsOnName(AIOFighterConfig config) { - LootingParameters valueParams = new LootingParameters( - config.attackRadius(), - 1, - 1, - minFreeSlots, - config.toggleDelayedLooting(), - config.toggleOnlyLootMyItems(), - config.listOfItemsToLoot().trim().split(",") - ); - if (Rs2GroundItem.lootItemsBasedOnNames(valueParams)) { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } - - public void shutdown() { - super.shutdown(); - } -} +package net.runelite.client.plugins.microbot.aiofighter.loot; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.Script; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; +import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.State; +import net.runelite.client.plugins.microbot.util.grounditem.LootingParameters; +import net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem; +import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; + +import java.util.concurrent.TimeUnit; + +@Slf4j +public class LootScript extends Script { + int minFreeSlots = 0; + + public LootScript() { + + } + + + public boolean run(AIOFighterConfig config) { + + mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { + try { + minFreeSlots = config.bank() ? config.minFreeSlots() : 0; + if (!super.run()) return; + if (!Microbot.isLoggedIn()) return; + if (AIOFighterPlugin.getState().equals(State.BANKING) || AIOFighterPlugin.getState().equals(State.WALKING)) return; + if (Rs2Inventory.isFull() || Rs2Inventory.getEmptySlots() <= minFreeSlots || (Rs2Player.isInCombat() && !config.toggleForceLoot())) + return; + + if (!config.toggleLootItems()) return; + if (config.looterStyle().equals(DefaultLooterStyle.MIXED) || config.looterStyle().equals(DefaultLooterStyle.ITEM_LIST)) { + lootItemsOnName(config); + } + + if (config.looterStyle().equals(DefaultLooterStyle.GE_PRICE_RANGE) || config.looterStyle().equals(DefaultLooterStyle.MIXED)) { + lootItemsByValue(config); + } + lootBones(config); + lootAshes(config); + lootRunes(config); + lootCoins(config); + lootUntradeableItems(config); + lootArrows(config); + + } catch(Exception ex) { + Microbot.log("Looterscript: " + ex.getMessage()); + } + + }, 0, 200, TimeUnit.MILLISECONDS); + return true; + } + + private void lootArrows(AIOFighterConfig config) { + if (config.toggleLootArrows()) { + LootingParameters arrowParams = new LootingParameters( + config.attackRadius(), + 1, + 10, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + "arrow" + ); + if (Rs2GroundItem.lootItemsBasedOnNames(arrowParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + private void lootBones(AIOFighterConfig config) { + if (config.toggleBuryBones()) { + LootingParameters bonesParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + "bones" + ); + if (Rs2GroundItem.lootItemsBasedOnNames(bonesParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + private void lootAshes(AIOFighterConfig config) { + if (config.toggleScatter()) { + LootingParameters ashesParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + " ashes" + ); + if (Rs2GroundItem.lootItemsBasedOnNames(ashesParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + // loot runes + private void lootRunes(AIOFighterConfig config) { + if (config.toggleLootRunes()) { + LootingParameters runesParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + " rune" + ); + if (Rs2GroundItem.lootItemsBasedOnNames(runesParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + // loot coins + private void lootCoins(AIOFighterConfig config) { + if (config.toggleLootCoins()) { + LootingParameters coinsParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + "coins" + ); + if (Rs2GroundItem.lootCoins(coinsParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + // loot untradeable items + private void lootUntradeableItems(AIOFighterConfig config) { + if (config.toggleLootUntradables()) { + LootingParameters untradeableItemsParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + "untradeable" + ); + if (Rs2GroundItem.lootUntradables(untradeableItemsParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + } + + private void lootItemsByValue(AIOFighterConfig config) { + LootingParameters valueParams = new LootingParameters( + config.minPriceOfItemsToLoot(), + config.maxPriceOfItemsToLoot(), + config.attackRadius(), + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems() + ); + if (Rs2GroundItem.lootItemBasedOnValue(valueParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + + private void lootItemsOnName(AIOFighterConfig config) { + LootingParameters valueParams = new LootingParameters( + config.attackRadius(), + 1, + 1, + minFreeSlots, + config.toggleDelayedLooting(), + config.toggleOnlyLootMyItems(), + config.listOfItemsToLoot().trim().split(",") + ); + if (Rs2GroundItem.lootItemsBasedOnNames(valueParams)) { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } + + public void shutdown() { + super.shutdown(); + } +} From b2e5a93e9cbc1fab73f71cc0f8d49a3dbe69104c Mon Sep 17 00:00:00 2001 From: Pert Date: Thu, 21 Aug 2025 10:49:29 -0400 Subject: [PATCH 04/16] fix(AIOFighter): Improve wait-for-loot reliability based on PR feedback - 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 --- .../microbot/aiofighter/AIOFighterConfig.java | 14 ++++++- .../microbot/aiofighter/AIOFighterPlugin.java | 15 ------- .../aiofighter/combat/AttackNpcScript.java | 41 ++++++++++--------- .../microbot/aiofighter/loot/LootScript.java | 8 ++++ 4 files changed, 42 insertions(+), 36 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java index 585c3f15978..066fc3cac80 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java @@ -401,7 +401,7 @@ default boolean toggleHighAlchProfitable() { @ConfigItem( keyName = "waitForLoot", name = "Wait for Loot", - description = "Wait for loot to appear before attacking next NPC (6 second timeout)", + description = "Wait for loot to appear before attacking next NPC", position = 103, section = lootSection ) @@ -409,6 +409,18 @@ default boolean toggleWaitForLoot() { return false; } + @Range(min = 1, max = 10) + @ConfigItem( + keyName = "lootWaitTimeout", + name = "Loot Wait Timeout", + description = "Seconds to wait for loot before resuming combat (1-10)", + position = 104, + section = lootSection + ) + default int lootWaitTimeout() { + return 6; + } + //set center tile manually @ConfigItem( keyName = "Center Tile", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java index 2ffcc9bf8cc..3aa81b8b59b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java @@ -75,8 +75,6 @@ public class AIOFighterPlugin extends Plugin { @Getter @Setter private static boolean waitingForLoot = false; - public static final int LOOT_WAIT_TIMEOUT = 6000; // 6 seconds - private final CannonScript cannonScript = new CannonScript(); private final AttackNpcScript attackNpc = new AttackNpcScript(); @@ -445,19 +443,6 @@ public void onNpcDespawned(NpcDespawned npcDespawned) { try { if(config.togglePrayer()) flickerScript.onNpcDespawned(npcDespawned); - - // Handle wait for loot feature - if (config.toggleWaitForLoot()) { - NPC npc = npcDespawned.getNpc(); - if (npc != null && npc.isDead()) { - Player localPlayer = Microbot.getClient().getLocalPlayer(); - if (localPlayer != null && localPlayer.getInteracting() == npc) { - waitingForLoot = true; - lastNpcKilledTime = System.currentTimeMillis(); - Microbot.log("NPC died, waiting for loot..."); - } - } - } } catch (Exception e) { log.info("AIO Fighter Plugin onNpcDespawned Error: " + e.getMessage()); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java index 3ea69df1f87..4af7f24e3c5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java @@ -45,6 +45,7 @@ public class AttackNpcScript extends Script { public static Actor currentNpc = null; public static AtomicReference> filteredAttackableNpcs = new AtomicReference<>(new ArrayList<>()); public static Rs2WorldArea attackableArea = null; + public static net.runelite.api.NPC cachedTargetNpc = null; private boolean messageShown = false; private int noNpcCount = 0; @@ -108,42 +109,42 @@ public void run(AIOFighterConfig config) { return; // Don't attack while looting } - // Check if our current target just died and we should wait for loot - if (config.toggleWaitForLoot() && !AIOFighterPlugin.isWaitingForLoot()) { - // Check if we were recently in combat but no longer interacting (NPC just died) + // Check if we need to update our cached target (but not while waiting for loot) + if (!AIOFighterPlugin.isWaitingForLoot()) { Actor currentInteracting = Rs2Player.getInteracting(); - - // If we're not interacting but were recently, the NPC probably just died - if (currentInteracting == null && Rs2Player.isInCombat()) { - // We were in combat but lost our target - NPC likely died - AIOFighterPlugin.setWaitingForLoot(true); - AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis()); - Microbot.log("Lost target while in combat, waiting for loot..."); - return; - } - if (currentInteracting instanceof net.runelite.api.NPC) { net.runelite.api.NPC npc = (net.runelite.api.NPC) currentInteracting; - if (npc.isDead() || (npc.getHealthRatio() == 0 && npc.getHealthScale() > 0)) { - AIOFighterPlugin.setWaitingForLoot(true); - AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis()); - Microbot.log("NPC died, waiting for loot..."); - return; + // Update our cached target to who we're fighting + if (npc.getHealthRatio() > 0 && !npc.isDead()) { + cachedTargetNpc = npc; } } } + + // Check if our cached target died + if (config.toggleWaitForLoot() && !AIOFighterPlugin.isWaitingForLoot() && cachedTargetNpc != null) { + if (cachedTargetNpc.isDead() || (cachedTargetNpc.getHealthRatio() == 0 && cachedTargetNpc.getHealthScale() > 0)) { + AIOFighterPlugin.setWaitingForLoot(true); + AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis()); + Microbot.log("NPC died, waiting for loot..."); + cachedTargetNpc = null; + return; + } + } // Check if we're waiting for loot if (config.toggleWaitForLoot() && AIOFighterPlugin.isWaitingForLoot()) { long timeSinceKill = System.currentTimeMillis() - AIOFighterPlugin.getLastNpcKilledTime(); - if (timeSinceKill >= AIOFighterPlugin.LOOT_WAIT_TIMEOUT) { + int timeoutMs = config.lootWaitTimeout() * 1000; + if (timeSinceKill >= timeoutMs) { // Timeout reached, resume combat AIOFighterPlugin.setWaitingForLoot(false); AIOFighterPlugin.setLastNpcKilledTime(0); + cachedTargetNpc = null; // Clear cached NPC on timeout Microbot.log("Loot wait timeout reached, resuming combat"); } else { // Still waiting for loot, don't attack - int secondsLeft = (int)((AIOFighterPlugin.LOOT_WAIT_TIMEOUT - timeSinceKill) / 1000); + int secondsLeft = (int)((timeoutMs - timeSinceKill) / 1000); Microbot.status = "Waiting for loot... " + secondsLeft + "s"; return; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java index 524d72d29ac..958b39f2a82 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java @@ -6,6 +6,7 @@ import net.runelite.client.plugins.microbot.Script; import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; +import net.runelite.client.plugins.microbot.aiofighter.combat.AttackNpcScript; import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; import net.runelite.client.plugins.microbot.aiofighter.enums.State; import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; @@ -60,6 +61,13 @@ public boolean run(AIOFighterConfig config) { if (config.toggleDelayedLooting()) { groundItems.sort(Comparator.comparingInt(Rs2GroundItem::calculateDespawnTime)); } + // Clear wait for loot state since we found loot + if (AIOFighterPlugin.isWaitingForLoot()) { + AIOFighterPlugin.setWaitingForLoot(false); + AIOFighterPlugin.setLastNpcKilledTime(0); + AttackNpcScript.cachedTargetNpc = null; // Clear the cached NPC + Microbot.log("Loot found, clearing wait state"); + } //Pause other scripts before looting Microbot.pauseAllScripts.getAndSet(true); for (GroundItem groundItem : groundItems) { From 83f6db37dfaebf8cb91eb1741e1fb7bcee1bd9d8 Mon Sep 17 00:00:00 2001 From: Pert Date: Thu, 21 Aug 2025 10:58:12 -0400 Subject: [PATCH 05/16] fix(AIOFighter): Allow looting during wait period even when attacked - 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 --- .../client/plugins/microbot/aiofighter/loot/LootScript.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java index 958b39f2a82..def926a67d8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java @@ -41,7 +41,7 @@ public boolean run(AIOFighterConfig config) { if (AIOFighterPlugin.getState().equals(State.BANKING) || AIOFighterPlugin.getState().equals(State.WALKING)) { return; } - if (Rs2Player.isInCombat() && !config.toggleForceLoot()) { + if (Rs2Player.isInCombat() && !config.toggleForceLoot() && !AIOFighterPlugin.isWaitingForLoot()) { return; } From f2023251cd0579bcba131cd52f0843e4419c61b1 Mon Sep 17 00:00:00 2001 From: Pert Date: Sat, 23 Aug 2025 19:02:25 -0400 Subject: [PATCH 06/16] feat(aiofighter): add looting bag support and blighted food detection - Empty looting bag automatically when banking - Add blighted food types to Rs2Food enum for proper banking triggers --- .../plugins/microbot/aiofighter/bank/BankerScript.java | 1 + .../runelite/client/plugins/microbot/util/misc/Rs2Food.java | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java index 27f30e694fe..acdc9d9b6bb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java @@ -386,6 +386,7 @@ public void withdrawUpkeepItems(AIOFighterConfig config) { inventorySetup.loadInventory(); + Rs2Bank.depositLootingBag(); Rs2Bank.emptyGemBag(); Rs2Bank.emptyHerbSack(); Rs2Bank.emptySeedBox(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2Food.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2Food.java index be01f6b66c4..552d14259b9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2Food.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2Food.java @@ -64,7 +64,10 @@ public enum Rs2Food { COOKED_DASHING_KEBBIT(29134, 23, "Cooked dashing kebbit",3), COOKED_MOONLIGHT_ANTELOPE(29143, 26, "Cooked moonlight antelope",3), PURPLE_SWEETS(10476, 3, "Purple Sweets",3), - CABBAGE(ItemID.CABBAGE, 1, "Cabbage",3); + CABBAGE(ItemID.CABBAGE, 1, "Cabbage",3), + BLIGHTED_MANTA_RAY(24589, 22, "Blighted manta ray", 3), + BLIGHTED_ANGLERFISH(24592, 22, "Blighted anglerfish", 3), + BLIGHTED_KARAMBWAN(24595, 18, "Blighted karambwan", 3); private int id; private int heal; From 91ac5118f6c9d142552da522f3f8f9a5f3d752ac Mon Sep 17 00:00:00 2001 From: Pert Date: Sun, 24 Aug 2025 16:49:18 -0400 Subject: [PATCH 07/16] fix(aiofighter): use NPC index comparison instead of object reference --- .../aiofighter/combat/AttackNpcScript.java | 15 +++++++++------ .../microbot/aiofighter/loot/LootScript.java | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java index 4af7f24e3c5..2eef8147f3c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java @@ -45,7 +45,7 @@ public class AttackNpcScript extends Script { public static Actor currentNpc = null; public static AtomicReference> filteredAttackableNpcs = new AtomicReference<>(new ArrayList<>()); public static Rs2WorldArea attackableArea = null; - public static net.runelite.api.NPC cachedTargetNpc = null; + public static int cachedTargetNpcIndex = -1; private boolean messageShown = false; private int noNpcCount = 0; @@ -116,18 +116,21 @@ public void run(AIOFighterConfig config) { 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()) { - cachedTargetNpc = npc; + cachedTargetNpcIndex = npc.getIndex(); } } } // Check if our cached target died - if (config.toggleWaitForLoot() && !AIOFighterPlugin.isWaitingForLoot() && cachedTargetNpc != null) { - if (cachedTargetNpc.isDead() || (cachedTargetNpc.getHealthRatio() == 0 && cachedTargetNpc.getHealthScale() > 0)) { + if (config.toggleWaitForLoot() && !AIOFighterPlugin.isWaitingForLoot() && cachedTargetNpcIndex != -1) { + // Find the NPC by index using Rs2 API + Rs2NpcModel cachedNpcModel = Rs2Npc.getNpcByIndex(cachedTargetNpcIndex); + + if (cachedNpcModel != null && (cachedNpcModel.isDead() || (cachedNpcModel.getHealthRatio() == 0 && cachedNpcModel.getHealthScale() > 0))) { AIOFighterPlugin.setWaitingForLoot(true); AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis()); Microbot.log("NPC died, waiting for loot..."); - cachedTargetNpc = null; + cachedTargetNpcIndex = -1; return; } } @@ -140,7 +143,7 @@ public void run(AIOFighterConfig config) { // Timeout reached, resume combat AIOFighterPlugin.setWaitingForLoot(false); AIOFighterPlugin.setLastNpcKilledTime(0); - cachedTargetNpc = null; // Clear cached NPC on timeout + cachedTargetNpcIndex = -1; // Clear cached NPC on timeout Microbot.log("Loot wait timeout reached, resuming combat"); } else { // Still waiting for loot, don't attack diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java index c13c5dab28e..4bcb1a17d10 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java @@ -63,7 +63,7 @@ public boolean run(AIOFighterConfig config) { if (AIOFighterPlugin.isWaitingForLoot()) { AIOFighterPlugin.setWaitingForLoot(false); AIOFighterPlugin.setLastNpcKilledTime(0); - AttackNpcScript.cachedTargetNpc = null; // Clear the cached NPC + AttackNpcScript.cachedTargetNpcIndex = -1; // Clear the cached NPC index Microbot.log("Loot found, clearing wait state"); } //Pause other scripts before looting From 8c75bd8c31fb8304299dcf1a3ba0b85b93e6ca10 Mon Sep 17 00:00:00 2001 From: Pert Date: Sun, 24 Aug 2025 17:22:22 -0400 Subject: [PATCH 08/16] fix(aiofighter): add thread safety for wait-for-loot state variables --- .../microbot/aiofighter/AIOFighterPlugin.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java index e6ce51c4c33..e9cae7f32e0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java @@ -70,10 +70,23 @@ public class AIOFighterPlugin extends Plugin { public static int cooldown = 0; @Getter @Setter - private static long lastNpcKilledTime = 0; + private static volatile long lastNpcKilledTime = 0; @Getter @Setter - private static boolean waitingForLoot = false; + private static volatile boolean waitingForLoot = false; + + /** + * Centralized method to clear wait-for-loot state + * @param reason Optional reason for clearing the state (for logging) + */ + public static void clearWaitForLoot(String reason) { + setWaitingForLoot(false); + setLastNpcKilledTime(0L); + AttackNpcScript.cachedTargetNpcIndex = -1; + if (reason != null) { + Microbot.log("Clearing wait-for-loot state: " + reason); + } + } private final CannonScript cannonScript = new CannonScript(); private final AttackNpcScript attackNpc = new AttackNpcScript(); @@ -124,6 +137,9 @@ protected void startUp() throws AWTException { return; } setState(State.IDLE); + // Reset wait for loot state on startup + setWaitingForLoot(false); + setLastNpcKilledTime(0L); // Get the future from the reference and cancel it ScheduledFuture scheduledFuture = futureRef.get(); if (scheduledFuture != null) { @@ -174,6 +190,10 @@ protected void startUp() throws AWTException { } protected void shutDown() { + // Reset wait for loot state on shutdown + setWaitingForLoot(false); + setLastNpcKilledTime(0L); + highAlchScript.shutdown(); lootScript.shutdown(); cannonScript.shutdown(); From feaaade554edbc3ef0ffa7304033cc9c18ff7617 Mon Sep 17 00:00:00 2001 From: Pert Date: Sun, 24 Aug 2025 17:25:19 -0400 Subject: [PATCH 09/16] fix(aiofighter): ensure pauseAllScripts is released in finally block --- .../microbot/aiofighter/loot/LootScript.java | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java index 4bcb1a17d10..785889b3c6b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java @@ -66,26 +66,29 @@ public boolean run(AIOFighterConfig config) { AttackNpcScript.cachedTargetNpcIndex = -1; // Clear the cached NPC index Microbot.log("Loot found, clearing wait state"); } - //Pause other scripts before looting + //Pause other scripts before looting and always release Microbot.pauseAllScripts.getAndSet(true); - for (GroundItem groundItem : groundItems) { - if (Rs2Inventory.emptySlotCount() <= minFreeSlots && !canStackItem(groundItem)) { - Microbot.log("Unable to pick loot: " + groundItem.getName() + " making space"); - if (!config.eatFoodForSpace()) { - continue; + try { + 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); + } } - 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)) { + return; } } - Microbot.log("Picking up loot: " + groundItem.getName()); - if (!waitForGroundItemDespawn(() -> interact(groundItem), groundItem)) { - return; - } + Microbot.log("Looting complete"); + } finally { + Microbot.pauseAllScripts.compareAndSet(true, false); } - Microbot.log("Looting complete"); - Microbot.pauseAllScripts.compareAndSet(true, false); } catch (Exception ex) { Microbot.log("Looterscript: " + ex.getMessage()); } From ff781f1fabcb25cc2187b6a89fe8d105a2971719 Mon Sep 17 00:00:00 2001 From: Pert Date: Sun, 24 Aug 2025 17:33:19 -0400 Subject: [PATCH 10/16] fix(aiofighter): correct instanceof check for Rs2NpcModel in target caching --- .../plugins/microbot/aiofighter/combat/AttackNpcScript.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java index 2eef8147f3c..e7696230f44 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java @@ -112,8 +112,8 @@ public void run(AIOFighterConfig config) { // Check if we need to update our cached target (but not while waiting for loot) if (!AIOFighterPlugin.isWaitingForLoot()) { Actor currentInteracting = Rs2Player.getInteracting(); - if (currentInteracting instanceof net.runelite.api.NPC) { - net.runelite.api.NPC npc = (net.runelite.api.NPC) currentInteracting; + 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(); From e23cae46c30b15edf0bf47c89d6046fae69799b4 Mon Sep 17 00:00:00 2001 From: Pert Date: Sun, 24 Aug 2025 17:45:00 -0400 Subject: [PATCH 11/16] fix(aiofighter): add volatile to cachedTargetNpcIndex and clamp timer display --- .../microbot/aiofighter/AIOFighterConfig.java | 2 +- .../aiofighter/combat/AttackNpcScript.java | 4 ++-- .../microbot/aiofighter/loot/LootScript.java | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java index 066fc3cac80..40393b49b51 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterConfig.java @@ -412,7 +412,7 @@ default boolean toggleWaitForLoot() { @Range(min = 1, max = 10) @ConfigItem( keyName = "lootWaitTimeout", - name = "Loot Wait Timeout", + name = "Loot Wait Timeout (s)", description = "Seconds to wait for loot before resuming combat (1-10)", position = 104, section = lootSection diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java index e7696230f44..a75fa024b39 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java @@ -45,7 +45,7 @@ public class AttackNpcScript extends Script { public static Actor currentNpc = null; public static AtomicReference> filteredAttackableNpcs = new AtomicReference<>(new ArrayList<>()); public static Rs2WorldArea attackableArea = null; - public static int cachedTargetNpcIndex = -1; + public static volatile int cachedTargetNpcIndex = -1; private boolean messageShown = false; private int noNpcCount = 0; @@ -147,7 +147,7 @@ public void run(AIOFighterConfig config) { Microbot.log("Loot wait timeout reached, resuming combat"); } else { // Still waiting for loot, don't attack - int secondsLeft = (int)((timeoutMs - timeSinceKill) / 1000); + int secondsLeft = (int) Math.max(0, TimeUnit.MILLISECONDS.toSeconds(timeoutMs - timeSinceKill)); Microbot.status = "Waiting for loot... " + secondsLeft + "s"; return; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java index 785889b3c6b..59a71c18ca4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java @@ -59,16 +59,11 @@ public boolean run(AIOFighterConfig config) { if (config.toggleDelayedLooting()) { groundItems.sort(Comparator.comparingInt(Rs2GroundItem::calculateDespawnTime)); } - // 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 and always release Microbot.pauseAllScripts.getAndSet(true); try { + boolean clearedWait = false; for (GroundItem groundItem : groundItems) { if (Rs2Inventory.emptySlotCount() <= minFreeSlots && !canStackItem(groundItem)) { Microbot.log("Unable to pick loot: " + groundItem.getName() + " making space"); @@ -84,6 +79,11 @@ public boolean run(AIOFighterConfig config) { if (!waitForGroundItemDespawn(() -> interact(groundItem), groundItem)) { return; } + // Clear wait state after first successful pickup + if (!clearedWait && AIOFighterPlugin.isWaitingForLoot()) { + AIOFighterPlugin.clearWaitForLoot("First loot item picked up"); + clearedWait = true; + } } Microbot.log("Looting complete"); } finally { From ee5d18839aa2de0892b9f07687f51896ca206b47 Mon Sep 17 00:00:00 2001 From: Pert Date: Sun, 24 Aug 2025 17:48:50 -0400 Subject: [PATCH 12/16] chore(aiofighter): remove changes accidentally introduced from another branch --- .../plugins/microbot/aiofighter/bank/BankerScript.java | 1 - .../runelite/client/plugins/microbot/util/misc/Rs2Food.java | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java index acdc9d9b6bb..27f30e694fe 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/bank/BankerScript.java @@ -386,7 +386,6 @@ public void withdrawUpkeepItems(AIOFighterConfig config) { inventorySetup.loadInventory(); - Rs2Bank.depositLootingBag(); Rs2Bank.emptyGemBag(); Rs2Bank.emptyHerbSack(); Rs2Bank.emptySeedBox(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2Food.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2Food.java index 552d14259b9..be01f6b66c4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2Food.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2Food.java @@ -64,10 +64,7 @@ public enum Rs2Food { COOKED_DASHING_KEBBIT(29134, 23, "Cooked dashing kebbit",3), COOKED_MOONLIGHT_ANTELOPE(29143, 26, "Cooked moonlight antelope",3), PURPLE_SWEETS(10476, 3, "Purple Sweets",3), - CABBAGE(ItemID.CABBAGE, 1, "Cabbage",3), - BLIGHTED_MANTA_RAY(24589, 22, "Blighted manta ray", 3), - BLIGHTED_ANGLERFISH(24592, 22, "Blighted anglerfish", 3), - BLIGHTED_KARAMBWAN(24595, 18, "Blighted karambwan", 3); + CABBAGE(ItemID.CABBAGE, 1, "Cabbage",3); private int id; private int heal; From 93ec43b43db3f61bc35bc1cfa4422d3d2d2ff95e Mon Sep 17 00:00:00 2001 From: Pert Date: Sun, 24 Aug 2025 18:09:29 -0400 Subject: [PATCH 13/16] fix(aiofighter): improve UX with immediate status, cleanup imports, and robustness fixes --- .../microbot/aiofighter/combat/AttackNpcScript.java | 7 +++---- .../plugins/microbot/aiofighter/loot/LootScript.java | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java index a75fa024b39..5981e1da9cc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java @@ -129,6 +129,7 @@ public void run(AIOFighterConfig config) { if (cachedNpcModel != null && (cachedNpcModel.isDead() || (cachedNpcModel.getHealthRatio() == 0 && cachedNpcModel.getHealthScale() > 0))) { AIOFighterPlugin.setWaitingForLoot(true); AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis()); + Microbot.status = "Waiting for loot..."; Microbot.log("NPC died, waiting for loot..."); cachedTargetNpcIndex = -1; return; @@ -141,13 +142,11 @@ public void run(AIOFighterConfig config) { int timeoutMs = config.lootWaitTimeout() * 1000; if (timeSinceKill >= timeoutMs) { // Timeout reached, resume combat - AIOFighterPlugin.setWaitingForLoot(false); - AIOFighterPlugin.setLastNpcKilledTime(0); + AIOFighterPlugin.clearWaitForLoot("Loot wait timeout reached, resuming combat"); 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; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java index 59a71c18ca4..00e04018c4b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java @@ -6,7 +6,6 @@ import net.runelite.client.plugins.microbot.Script; import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; -import net.runelite.client.plugins.microbot.aiofighter.combat.AttackNpcScript; import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; import net.runelite.client.plugins.microbot.aiofighter.enums.State; import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; @@ -77,7 +76,8 @@ public boolean run(AIOFighterConfig config) { } Microbot.log("Picking up loot: " + groundItem.getName()); if (!waitForGroundItemDespawn(() -> interact(groundItem), groundItem)) { - return; + // Skip this item and continue to the next rather than aborting the whole pass + continue; } // Clear wait state after first successful pickup if (!clearedWait && AIOFighterPlugin.isWaitingForLoot()) { From e86c27259b1fd14eb47215a02a5f8b56e2b5b3d4 Mon Sep 17 00:00:00 2001 From: Pert Date: Mon, 25 Aug 2025 12:23:20 -0400 Subject: [PATCH 14/16] fix(aiofighter): improve inventory handling with fast food and re-check --- .../microbot/aiofighter/AIOFighterPlugin.java | 1228 ++++++++--------- .../aiofighter/combat/AttackNpcScript.java | 510 +++---- .../microbot/aiofighter/loot/LootScript.java | 310 +++-- 3 files changed, 1026 insertions(+), 1022 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java index e9cae7f32e0..22d64d9be22 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java @@ -1,614 +1,614 @@ -package net.runelite.client.plugins.microbot.aiofighter; - -import com.google.inject.Provides; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.*; -import net.runelite.api.Point; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.events.*; -import net.runelite.api.widgets.ComponentID; -import net.runelite.api.widgets.Widget; -import net.runelite.api.worldmap.WorldMap; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.events.ConfigChanged; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.aiofighter.bank.BankerScript; -import net.runelite.client.plugins.microbot.aiofighter.cannon.CannonScript; -import net.runelite.client.plugins.microbot.aiofighter.combat.*; -import net.runelite.client.plugins.microbot.aiofighter.enums.PrayerStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.State; -import net.runelite.client.plugins.microbot.aiofighter.loot.LootScript; -import net.runelite.client.plugins.microbot.aiofighter.safety.SafetyScript; -import net.runelite.client.plugins.microbot.aiofighter.shop.ShopScript; -import net.runelite.client.plugins.microbot.aiofighter.skill.AttackStyleScript; -import net.runelite.client.plugins.microbot.inventorysetups.InventorySetup; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; -import net.runelite.client.plugins.microbot.util.prayer.Rs2Prayer; -import net.runelite.client.plugins.microbot.util.slayer.Rs2Slayer; -import net.runelite.client.ui.JagexColors; -import net.runelite.client.ui.overlay.OverlayManager; -import net.runelite.client.util.ColorUtil; -import net.runelite.client.util.Text; - -import javax.inject.Inject; -import java.awt.*; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -@PluginDescriptor( - name = PluginDescriptor.Mocrosoft + "AIO Fighter", - description = "Microbot Fighter plugin", - tags = {"fight", "microbot", "misc", "combat", "playerassistant"}, - enabledByDefault = false -) -@Slf4j -public class AIOFighterPlugin extends Plugin { - public static final String version = "2.0.2 BETA"; - public static boolean needShopping = false; - private static final String SET = "Set"; - private static final String CENTER_TILE = ColorUtil.wrapWithColorTag("Center Tile", JagexColors.MENU_TARGET); - // SAFE_SPOT = "Safe Spot"; - private static final String SAFE_SPOT = ColorUtil.wrapWithColorTag("Safe Spot", JagexColors.CHAT_PRIVATE_MESSAGE_TEXT_TRANSPARENT_BACKGROUND); - private static final String ADD_TO = "Start Fighting:"; - private static final String REMOVE_FROM = "Stop Fighting:"; - private static final String WALK_HERE = "Walk here"; - private static final String ATTACK = "Attack"; - @Getter - @Setter - public static int cooldown = 0; - - @Getter @Setter - private static volatile long lastNpcKilledTime = 0; - - @Getter @Setter - private static volatile boolean waitingForLoot = false; - - /** - * Centralized method to clear wait-for-loot state - * @param reason Optional reason for clearing the state (for logging) - */ - public static void clearWaitForLoot(String reason) { - setWaitingForLoot(false); - setLastNpcKilledTime(0L); - AttackNpcScript.cachedTargetNpcIndex = -1; - if (reason != null) { - Microbot.log("Clearing wait-for-loot state: " + reason); - } - } - - private final CannonScript cannonScript = new CannonScript(); - private final AttackNpcScript attackNpc = new AttackNpcScript(); - - private final FoodScript foodScript = new FoodScript(); - private final LootScript lootScript = new LootScript(); - private final SafeSpot safeSpotScript = new SafeSpot(); - private final FlickerScript flickerScript = new FlickerScript(); - private final UseSpecialAttackScript useSpecialAttackScript = new UseSpecialAttackScript(); - private final BuryScatterScript buryScatterScript = new BuryScatterScript(); - private final AttackStyleScript attackStyleScript = new AttackStyleScript(); - private final BankerScript bankerScript = new BankerScript(); - private final PrayerScript prayerScript = new PrayerScript(); - private final HighAlchScript highAlchScript = new HighAlchScript(); - private final PotionManagerScript potionManagerScript = new PotionManagerScript(); - private final SafetyScript safetyScript = new SafetyScript(); - private final SlayerScript slayerScript = new SlayerScript(); - private final ShopScript shopScript = new ShopScript(); - private final DodgeProjectileScript dodgeScript = new DodgeProjectileScript(); - @Inject - private AIOFighterConfig config; - @Inject - private OverlayManager overlayManager; - @Inject - private AIOFighterOverlay playerAssistOverlay; - @Inject - private AIOFighterInfoOverlay playerAssistInfoOverlay; - private MenuEntry lastClick; - private Point lastMenuOpenedPoint; - private WorldPoint trueTile; - - protected ScheduledExecutorService initializerExecutor = Executors.newSingleThreadScheduledExecutor(); - - @Provides - public AIOFighterConfig provideConfig(ConfigManager configManager) { - return configManager.getConfig(AIOFighterConfig.class); - } - - @Override - protected void startUp() throws AWTException { - Microbot.pauseAllScripts.compareAndSet(true, false); - //initialize any data on startup - ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); - AtomicReference> futureRef = new AtomicReference<>(); - - ScheduledFuture future = executor.scheduleWithFixedDelay(() -> { - if (Microbot.getConfigManager() == null) { - return; - } - setState(State.IDLE); - // Reset wait for loot state on startup - setWaitingForLoot(false); - setLastNpcKilledTime(0L); - // Get the future from the reference and cancel it - ScheduledFuture scheduledFuture = futureRef.get(); - if (scheduledFuture != null) { - scheduledFuture.cancel(false); - } - // now that no other tasks run, you can shut down: - executor.shutdown(); - }, 0, 1, TimeUnit.SECONDS); - - if (overlayManager != null) { - overlayManager.add(playerAssistOverlay); - overlayManager.add(playerAssistInfoOverlay); - playerAssistInfoOverlay.myButton.hookMouseListener(); - playerAssistInfoOverlay.blacklistButton.hookMouseListener(); - } - if (!config.toggleCenterTile() && Microbot.isLoggedIn() && !config.slayerMode()) - setCenter(Rs2Player.getWorldLocation()); - dodgeScript.run(config); - lootScript.run(config); - cannonScript.run(config); - attackNpc.run(config); - foodScript.run(config); - safeSpotScript.run(config); - flickerScript.run(config); - useSpecialAttackScript.run(config); - buryScatterScript.run(config); - attackStyleScript.run(config); - prayerScript.run(config); - highAlchScript.run(config); - potionManagerScript.run(config); - safetyScript.run(config); - slayerScript.run(config); - - // Configure special attack settings - if (config.useSpecialAttack() && config.specWeapon() != null) { - Microbot.getSpecialAttackConfigs() - .setSpecialAttack(true) - .setSpecialAttackWeapon(config.specWeapon()) - .setMinimumSpecEnergy(config.specWeapon().getEnergyRequired()); - } else { - Microbot.getSpecialAttackConfigs() - .setSpecialAttack(config.useSpecialAttack()); - } - - Rs2Slayer.blacklistedSlayerMonsters = getBlacklistedSlayerNpcs(); - bankerScript.run(config); - shopScript.run(config); - } - - protected void shutDown() { - // Reset wait for loot state on shutdown - setWaitingForLoot(false); - setLastNpcKilledTime(0L); - - highAlchScript.shutdown(); - lootScript.shutdown(); - cannonScript.shutdown(); - attackNpc.shutdown(); - dodgeScript.shutdown(); - foodScript.shutdown(); - safeSpotScript.shutdown(); - flickerScript.shutdown(); - useSpecialAttackScript.shutdown(); - buryScatterScript.shutdown(); - attackStyleScript.shutdown(); - bankerScript.shutdown(); - prayerScript.shutdown(); - potionManagerScript.shutdown(); - safetyScript.shutdown(); - slayerScript.shutdown(); - shopScript.shutdown(); - resetLocation(); - Microbot.getSpecialAttackConfigs().reset(); - overlayManager.remove(playerAssistOverlay); - overlayManager.remove(playerAssistInfoOverlay); - playerAssistInfoOverlay.myButton.unhookMouseListener(); - playerAssistInfoOverlay.blacklistButton.unhookMouseListener(); - } - - public static void resetLocation() { - setCenter(new WorldPoint(0, 0, 0)); - setSafeSpot(new WorldPoint(0, 0, 0)); - } - - public static void setCenter(WorldPoint worldPoint) - { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "centerLocation", - worldPoint - ); - } - // set safe spot - public static void setSafeSpot(WorldPoint worldPoint) - { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "safeSpotLocation", - worldPoint - ); - - - } - // Set remainingSlayerKills - public static void setRemainingSlayerKills(int remainingSlayerKills) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "remainingSlayerKills", - remainingSlayerKills - ); - } - // Set slayerLocation - public static void setSlayerLocationName(String slayerLocation) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "slayerLocation", - slayerLocation - ); - } - // Set slayerTask - public static void setSlayerTask(String slayerTask) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "slayerTask", - slayerTask - ); - } - // Set slayerTaskWeaknessThreshold - public static void setSlayerTaskWeaknessThreshold(int slayerTaskWeaknessThreshold) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "slayerTaskWeaknessThreshold", - slayerTaskWeaknessThreshold - ); - } - // Set slayerTaskWeaknessItem - public static void setSlayerTaskWeaknessItem(String slayerTaskWeaknessItem) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "slayerTaskWeaknessItem", - slayerTaskWeaknessItem - ); - } - // Set slayerHasTaskWeakness - public static void setSlayerHasTaskWeakness(boolean slayerHasTaskWeakness) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "slayerHasTaskWeakness", - slayerHasTaskWeakness - ); - } - // Set currentInventorySetup - public static void setCurrentSlayerInventorySetup(InventorySetup currentInventorySetup) { - Microbot.log("Setting current inventory setup to: " + currentInventorySetup.getName()); - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "currentInventorySetup", - currentInventorySetup - ); - } - // Get currentInventorySetup - public static InventorySetup getCurrentSlayerInventorySetup() { - return Microbot.getConfigManager().getConfiguration( - AIOFighterConfig.GROUP, - "currentInventorySetup", - InventorySetup.class - ); - } - // Get defaultInventorySetup - public static InventorySetup getDefaultInventorySetup() { - return Microbot.getConfigManager().getConfiguration( - AIOFighterConfig.GROUP, - "defaultInventorySetup", - InventorySetup.class - ); - } - // Add NPC to blacklist blacklistedSlayerNpcs - public static void addBlacklistedSlayerNpcs(String npcName) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "blacklistedSlayerNpcs", - Microbot.getConfigManager().getConfiguration( - AIOFighterConfig.GROUP, - "blacklistedSlayerNpcs", - String.class - ) + npcName + "," - ); - } - // Get blacklistedSlayerNpcs as a list - public static List getBlacklistedSlayerNpcs() { - return Arrays.asList(Microbot.getConfigManager().getConfiguration( - AIOFighterConfig.GROUP, - "blacklistedSlayerNpcs", - String.class - ).toString().split(",")); - } - //set Inventory Setup - private void setInventorySetup(InventorySetup inventorySetup) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "inventorySetupHidden", - inventorySetup - ); - } - - - public static State getState() { - return Microbot.getConfigManager().getConfiguration( - AIOFighterConfig.GROUP, - "state", - State.class - ); - } - - public static void setState(State state) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "state", - state - ); - } - public static String getNpcAttackList() { - return Microbot.getConfigManager().getConfiguration( - AIOFighterConfig.GROUP, - "monster" - ); - } - public static void addNpcToList(String npcName) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "monster", - getNpcAttackList() + npcName + "," - ); - - } - public static void removeNpcFromList(String npcName) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "monster", - Arrays.stream(getNpcAttackList().split(",")) - .filter(n -> !n.equalsIgnoreCase(npcName)) - .collect(Collectors.joining(",")) - ); - } - - // set attackable npcs - public static void setAttackableNpcs(String npcNames) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "monster", - npcNames - ); - } - - private String getNpcNameFromMenuEntry(String menuTarget) { - return menuTarget.replaceAll("<[^>]*>|\\(.*\\)", "").trim(); - } - - @Subscribe - public void onChatMessage(ChatMessage event) { - if (event.getMessage().contains("reach that")) { - AttackNpcScript.skipNpc(); - } - } - // on setting change - @Subscribe - public void onConfigChanged(ConfigChanged event) { - - - if (event.getKey().equals("Safe Spot")) { - - if (!config.toggleSafeSpot()) { - // reset safe spot to default - setSafeSpot(new WorldPoint(0, 0, 0)); - } - } - if(event.getKey().equals("Combat")) { - if (!config.toggleCombat() && config.toggleCenterTile()) { - setCenter(new WorldPoint(0, 0, 0)); - } - if (config.toggleCombat() && !config.toggleCenterTile()) { - setCenter(Rs2Player.getWorldLocation()); - } - - } - // Handle special attack weapon config changes - if (event.getKey().equals("Use special attack") || event.getKey().equals("Spec weapon")) { - if (config.useSpecialAttack() && config.specWeapon() != null) { - Microbot.getSpecialAttackConfigs() - .setSpecialAttack(true) - .setSpecialAttackWeapon(config.specWeapon()) - .setMinimumSpecEnergy(config.specWeapon().getEnergyRequired()); - } else { - Microbot.getSpecialAttackConfigs().reset(); - } - } - } - - @Subscribe - public void onProjectileMoved(ProjectileMoved event) { - Projectile projectile = event.getProjectile(); - if (projectile.getTargetActor() == null) { - //Projectiles that have targetActor null are targeting a WorldPoint and are dodgeable. - dodgeScript.projectiles.add(event.getProjectile()); - } - } - - @Subscribe - public void onGameTick(GameTick gameTick) { - try { - //execute flicker script - if(config.togglePrayer()) - flickerScript.onGameTick(); - } catch (Exception e) { - log.info("AIO Fighter Plugin onGameTick Error: " + e.getMessage()); - } - } - - @Subscribe - public void onNpcDespawned(NpcDespawned npcDespawned) { - try { - if(config.togglePrayer()) - flickerScript.onNpcDespawned(npcDespawned); - } catch (Exception e) { - log.info("AIO Fighter Plugin onNpcDespawned Error: " + e.getMessage()); - } - } - - @Subscribe - public void onHitsplatApplied(HitsplatApplied event){ - try { - if (event.getActor() != Microbot.getClient().getLocalPlayer()) return; - final Hitsplat hitsplat = event.getHitsplat(); - - if ((hitsplat.isMine()) && event.getActor().getInteracting() instanceof NPC && config.togglePrayer() && (config.prayerStyle() == PrayerStyle.LAZY_FLICK) || (config.prayerStyle() == PrayerStyle.PERFECT_LAZY_FLICK) || (config.prayerStyle() == PrayerStyle.MIXED_LAZY_FLICK)) { - flickerScript.resetLastAttack(true); - Rs2Prayer.disableAllPrayers(); - if (config.toggleQuickPray()) - Rs2Prayer.toggleQuickPrayer(false); - - - } - } catch (Exception e) { - log.info("AIO Fighter Plugin onHitsplatApplied Error: " + e.getMessage()); - } - } - @Subscribe - public void onMenuOpened(MenuOpened event) { - lastMenuOpenedPoint = Microbot.getClient().getMouseCanvasPosition(); - trueTile = getSelectedWorldPoint(); - } - @Subscribe - private void onMenuEntryAdded(MenuEntryAdded event) { - if (Microbot.getClient().isKeyPressed(KeyCode.KC_SHIFT) && event.getOption().equals(WALK_HERE) && event.getTarget().isEmpty() && config.toggleCenterTile()) { - addMenuEntry(event, SET, CENTER_TILE, 1); - } - if (Microbot.getClient().isKeyPressed(KeyCode.KC_SHIFT) && event.getOption().equals(WALK_HERE) && event.getTarget().isEmpty()) { - addMenuEntry(event, SET, SAFE_SPOT, 1); - } - if (event.getOption().equals(ATTACK) && config.attackableNpcs().contains(getNpcNameFromMenuEntry(Text.removeTags(event.getTarget())))) { - addMenuEntry(event, REMOVE_FROM, event.getTarget(), 1); - } - if (event.getOption().equals(ATTACK) && !config.attackableNpcs().contains(getNpcNameFromMenuEntry(Text.removeTags(event.getTarget())))) { - addMenuEntry(event, ADD_TO, event.getTarget(), 1); - } - - } - - private WorldPoint getSelectedWorldPoint() { - if (Microbot.getClient().getWidget(ComponentID.WORLD_MAP_MAPVIEW) == null) { - if (Microbot.getClient().getSelectedSceneTile() != null) { - return Microbot.getClient().isInInstancedRegion() ? - WorldPoint.fromLocalInstance(Microbot.getClient(), Microbot.getClient().getSelectedSceneTile().getLocalLocation()) : - Microbot.getClient().getSelectedSceneTile().getWorldLocation(); - } - } else { - return calculateMapPoint(Microbot.getClient().isMenuOpen() ? lastMenuOpenedPoint : Microbot.getClient().getMouseCanvasPosition()); - } - return null; - } - public WorldPoint calculateMapPoint(Point point) { - WorldMap worldMap = Microbot.getClient().getWorldMap(); - float zoom = worldMap.getWorldMapZoom(); - final WorldPoint mapPoint = new WorldPoint(worldMap.getWorldMapPosition().getX(), worldMap.getWorldMapPosition().getY(), 0); - final Point middle = mapWorldPointToGraphicsPoint(mapPoint); - - if (point == null || middle == null) { - return null; - } - - final int dx = (int) ((point.getX() - middle.getX()) / zoom); - final int dy = (int) ((-(point.getY() - middle.getY())) / zoom); - - return mapPoint.dx(dx).dy(dy); - } - public Point mapWorldPointToGraphicsPoint(WorldPoint worldPoint) { - WorldMap worldMap = Microbot.getClient().getWorldMap(); - - float pixelsPerTile = worldMap.getWorldMapZoom(); - - Widget map = Microbot.getClient().getWidget(ComponentID.WORLD_MAP_MAPVIEW); - if (map != null) { - Rectangle worldMapRect = map.getBounds(); - - int widthInTiles = (int) Math.ceil(worldMapRect.getWidth() / pixelsPerTile); - int heightInTiles = (int) Math.ceil(worldMapRect.getHeight() / pixelsPerTile); - - Point worldMapPosition = worldMap.getWorldMapPosition(); - - int yTileMax = worldMapPosition.getY() - heightInTiles / 2; - int yTileOffset = (yTileMax - worldPoint.getY() - 1) * -1; - int xTileOffset = worldPoint.getX() + widthInTiles / 2 - worldMapPosition.getX(); - - int xGraphDiff = ((int) (xTileOffset * pixelsPerTile)); - int yGraphDiff = (int) (yTileOffset * pixelsPerTile); - - yGraphDiff -= (int) (pixelsPerTile - Math.ceil(pixelsPerTile / 2)); - xGraphDiff += (int) (pixelsPerTile - Math.ceil(pixelsPerTile / 2)); - - yGraphDiff = worldMapRect.height - yGraphDiff; - yGraphDiff += (int) worldMapRect.getY(); - xGraphDiff += (int) worldMapRect.getX(); - - return new Point(xGraphDiff, yGraphDiff); - } - return null; - } - private void onMenuOptionClicked(MenuEntry entry) { - - - - if (entry.getOption().equals(SET) && entry.getTarget().equals(CENTER_TILE)) { - setCenter(trueTile); - } - if (entry.getOption().equals(SET) && entry.getTarget().equals(SAFE_SPOT)) { - setSafeSpot(trueTile); - } - - - if (entry.getType() != MenuAction.WALK) { - lastClick = entry; - } - } - - - @Subscribe - private void onMenuOptionClicked(MenuOptionClicked event) - { - if (event.getMenuOption().equals(ADD_TO)) { - addNpcToList(getNpcNameFromMenuEntry(event.getMenuTarget())); - } - if (event.getMenuOption().equals(REMOVE_FROM)) { - removeNpcFromList(getNpcNameFromMenuEntry(event.getMenuTarget())); - } - } - private void addMenuEntry(MenuEntryAdded event, String option, String target, int position) { - List entries = new LinkedList<>(Arrays.asList(Microbot.getClient().getMenuEntries())); - - if (entries.stream().anyMatch(e -> e.getOption().equals(option) && e.getTarget().equals(target))) { - return; - } - - Microbot.getClient().createMenuEntry(position) - .setOption(option) - .setTarget(target) - .setParam0(event.getActionParam0()) - .setParam1(event.getActionParam1()) - .setIdentifier(event.getIdentifier()) - .setType(MenuAction.RUNELITE) - .onClick(this::onMenuOptionClicked); - } -} +package net.runelite.client.plugins.microbot.aiofighter; + +import com.google.inject.Provides; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.*; +import net.runelite.api.Point; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.*; +import net.runelite.api.widgets.ComponentID; +import net.runelite.api.widgets.Widget; +import net.runelite.api.worldmap.WorldMap; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.aiofighter.bank.BankerScript; +import net.runelite.client.plugins.microbot.aiofighter.cannon.CannonScript; +import net.runelite.client.plugins.microbot.aiofighter.combat.*; +import net.runelite.client.plugins.microbot.aiofighter.enums.PrayerStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.State; +import net.runelite.client.plugins.microbot.aiofighter.loot.LootScript; +import net.runelite.client.plugins.microbot.aiofighter.safety.SafetyScript; +import net.runelite.client.plugins.microbot.aiofighter.shop.ShopScript; +import net.runelite.client.plugins.microbot.aiofighter.skill.AttackStyleScript; +import net.runelite.client.plugins.microbot.inventorysetups.InventorySetup; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; +import net.runelite.client.plugins.microbot.util.prayer.Rs2Prayer; +import net.runelite.client.plugins.microbot.util.slayer.Rs2Slayer; +import net.runelite.client.ui.JagexColors; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.ColorUtil; +import net.runelite.client.util.Text; + +import javax.inject.Inject; +import java.awt.*; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +@PluginDescriptor( + name = PluginDescriptor.Mocrosoft + "AIO Fighter", + description = "Microbot Fighter plugin", + tags = {"fight", "microbot", "misc", "combat", "playerassistant"}, + enabledByDefault = false +) +@Slf4j +public class AIOFighterPlugin extends Plugin { + public static final String version = "2.0.2 BETA"; + public static boolean needShopping = false; + private static final String SET = "Set"; + private static final String CENTER_TILE = ColorUtil.wrapWithColorTag("Center Tile", JagexColors.MENU_TARGET); + // SAFE_SPOT = "Safe Spot"; + private static final String SAFE_SPOT = ColorUtil.wrapWithColorTag("Safe Spot", JagexColors.CHAT_PRIVATE_MESSAGE_TEXT_TRANSPARENT_BACKGROUND); + private static final String ADD_TO = "Start Fighting:"; + private static final String REMOVE_FROM = "Stop Fighting:"; + private static final String WALK_HERE = "Walk here"; + private static final String ATTACK = "Attack"; + @Getter + @Setter + public static int cooldown = 0; + + @Getter @Setter + private static volatile long lastNpcKilledTime = 0; + + @Getter @Setter + private static volatile boolean waitingForLoot = false; + + /** + * Centralized method to clear wait-for-loot state + * @param reason Optional reason for clearing the state (for logging) + */ + public static void clearWaitForLoot(String reason) { + setWaitingForLoot(false); + setLastNpcKilledTime(0L); + AttackNpcScript.cachedTargetNpcIndex = -1; + if (reason != null) { + Microbot.log("Clearing wait-for-loot state: " + reason); + } + } + + private final CannonScript cannonScript = new CannonScript(); + private final AttackNpcScript attackNpc = new AttackNpcScript(); + + private final FoodScript foodScript = new FoodScript(); + private final LootScript lootScript = new LootScript(); + private final SafeSpot safeSpotScript = new SafeSpot(); + private final FlickerScript flickerScript = new FlickerScript(); + private final UseSpecialAttackScript useSpecialAttackScript = new UseSpecialAttackScript(); + private final BuryScatterScript buryScatterScript = new BuryScatterScript(); + private final AttackStyleScript attackStyleScript = new AttackStyleScript(); + private final BankerScript bankerScript = new BankerScript(); + private final PrayerScript prayerScript = new PrayerScript(); + private final HighAlchScript highAlchScript = new HighAlchScript(); + private final PotionManagerScript potionManagerScript = new PotionManagerScript(); + private final SafetyScript safetyScript = new SafetyScript(); + private final SlayerScript slayerScript = new SlayerScript(); + private final ShopScript shopScript = new ShopScript(); + private final DodgeProjectileScript dodgeScript = new DodgeProjectileScript(); + @Inject + private AIOFighterConfig config; + @Inject + private OverlayManager overlayManager; + @Inject + private AIOFighterOverlay playerAssistOverlay; + @Inject + private AIOFighterInfoOverlay playerAssistInfoOverlay; + private MenuEntry lastClick; + private Point lastMenuOpenedPoint; + private WorldPoint trueTile; + + protected ScheduledExecutorService initializerExecutor = Executors.newSingleThreadScheduledExecutor(); + + @Provides + public AIOFighterConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(AIOFighterConfig.class); + } + + @Override + protected void startUp() throws AWTException { + Microbot.pauseAllScripts.compareAndSet(true, false); + //initialize any data on startup + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + AtomicReference> futureRef = new AtomicReference<>(); + + ScheduledFuture future = executor.scheduleWithFixedDelay(() -> { + if (Microbot.getConfigManager() == null) { + return; + } + setState(State.IDLE); + // Reset wait for loot state on startup + setWaitingForLoot(false); + setLastNpcKilledTime(0L); + // Get the future from the reference and cancel it + ScheduledFuture scheduledFuture = futureRef.get(); + if (scheduledFuture != null) { + scheduledFuture.cancel(false); + } + // now that no other tasks run, you can shut down: + executor.shutdown(); + }, 0, 1, TimeUnit.SECONDS); + + if (overlayManager != null) { + overlayManager.add(playerAssistOverlay); + overlayManager.add(playerAssistInfoOverlay); + playerAssistInfoOverlay.myButton.hookMouseListener(); + playerAssistInfoOverlay.blacklistButton.hookMouseListener(); + } + if (!config.toggleCenterTile() && Microbot.isLoggedIn() && !config.slayerMode()) + setCenter(Rs2Player.getWorldLocation()); + dodgeScript.run(config); + lootScript.run(config); + cannonScript.run(config); + attackNpc.run(config); + foodScript.run(config); + safeSpotScript.run(config); + flickerScript.run(config); + useSpecialAttackScript.run(config); + buryScatterScript.run(config); + attackStyleScript.run(config); + prayerScript.run(config); + highAlchScript.run(config); + potionManagerScript.run(config); + safetyScript.run(config); + slayerScript.run(config); + + // Configure special attack settings + if (config.useSpecialAttack() && config.specWeapon() != null) { + Microbot.getSpecialAttackConfigs() + .setSpecialAttack(true) + .setSpecialAttackWeapon(config.specWeapon()) + .setMinimumSpecEnergy(config.specWeapon().getEnergyRequired()); + } else { + Microbot.getSpecialAttackConfigs() + .setSpecialAttack(config.useSpecialAttack()); + } + + Rs2Slayer.blacklistedSlayerMonsters = getBlacklistedSlayerNpcs(); + bankerScript.run(config); + shopScript.run(config); + } + + protected void shutDown() { + // Reset wait for loot state on shutdown + setWaitingForLoot(false); + setLastNpcKilledTime(0L); + + highAlchScript.shutdown(); + lootScript.shutdown(); + cannonScript.shutdown(); + attackNpc.shutdown(); + dodgeScript.shutdown(); + foodScript.shutdown(); + safeSpotScript.shutdown(); + flickerScript.shutdown(); + useSpecialAttackScript.shutdown(); + buryScatterScript.shutdown(); + attackStyleScript.shutdown(); + bankerScript.shutdown(); + prayerScript.shutdown(); + potionManagerScript.shutdown(); + safetyScript.shutdown(); + slayerScript.shutdown(); + shopScript.shutdown(); + resetLocation(); + Microbot.getSpecialAttackConfigs().reset(); + overlayManager.remove(playerAssistOverlay); + overlayManager.remove(playerAssistInfoOverlay); + playerAssistInfoOverlay.myButton.unhookMouseListener(); + playerAssistInfoOverlay.blacklistButton.unhookMouseListener(); + } + + public static void resetLocation() { + setCenter(new WorldPoint(0, 0, 0)); + setSafeSpot(new WorldPoint(0, 0, 0)); + } + + public static void setCenter(WorldPoint worldPoint) + { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "centerLocation", + worldPoint + ); + } + // set safe spot + public static void setSafeSpot(WorldPoint worldPoint) + { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "safeSpotLocation", + worldPoint + ); + + + } + // Set remainingSlayerKills + public static void setRemainingSlayerKills(int remainingSlayerKills) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "remainingSlayerKills", + remainingSlayerKills + ); + } + // Set slayerLocation + public static void setSlayerLocationName(String slayerLocation) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "slayerLocation", + slayerLocation + ); + } + // Set slayerTask + public static void setSlayerTask(String slayerTask) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "slayerTask", + slayerTask + ); + } + // Set slayerTaskWeaknessThreshold + public static void setSlayerTaskWeaknessThreshold(int slayerTaskWeaknessThreshold) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "slayerTaskWeaknessThreshold", + slayerTaskWeaknessThreshold + ); + } + // Set slayerTaskWeaknessItem + public static void setSlayerTaskWeaknessItem(String slayerTaskWeaknessItem) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "slayerTaskWeaknessItem", + slayerTaskWeaknessItem + ); + } + // Set slayerHasTaskWeakness + public static void setSlayerHasTaskWeakness(boolean slayerHasTaskWeakness) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "slayerHasTaskWeakness", + slayerHasTaskWeakness + ); + } + // Set currentInventorySetup + public static void setCurrentSlayerInventorySetup(InventorySetup currentInventorySetup) { + Microbot.log("Setting current inventory setup to: " + currentInventorySetup.getName()); + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "currentInventorySetup", + currentInventorySetup + ); + } + // Get currentInventorySetup + public static InventorySetup getCurrentSlayerInventorySetup() { + return Microbot.getConfigManager().getConfiguration( + AIOFighterConfig.GROUP, + "currentInventorySetup", + InventorySetup.class + ); + } + // Get defaultInventorySetup + public static InventorySetup getDefaultInventorySetup() { + return Microbot.getConfigManager().getConfiguration( + AIOFighterConfig.GROUP, + "defaultInventorySetup", + InventorySetup.class + ); + } + // Add NPC to blacklist blacklistedSlayerNpcs + public static void addBlacklistedSlayerNpcs(String npcName) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "blacklistedSlayerNpcs", + Microbot.getConfigManager().getConfiguration( + AIOFighterConfig.GROUP, + "blacklistedSlayerNpcs", + String.class + ) + npcName + "," + ); + } + // Get blacklistedSlayerNpcs as a list + public static List getBlacklistedSlayerNpcs() { + return Arrays.asList(Microbot.getConfigManager().getConfiguration( + AIOFighterConfig.GROUP, + "blacklistedSlayerNpcs", + String.class + ).toString().split(",")); + } + //set Inventory Setup + private void setInventorySetup(InventorySetup inventorySetup) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "inventorySetupHidden", + inventorySetup + ); + } + + + public static State getState() { + return Microbot.getConfigManager().getConfiguration( + AIOFighterConfig.GROUP, + "state", + State.class + ); + } + + public static void setState(State state) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "state", + state + ); + } + public static String getNpcAttackList() { + return Microbot.getConfigManager().getConfiguration( + AIOFighterConfig.GROUP, + "monster" + ); + } + public static void addNpcToList(String npcName) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "monster", + getNpcAttackList() + npcName + "," + ); + + } + public static void removeNpcFromList(String npcName) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "monster", + Arrays.stream(getNpcAttackList().split(",")) + .filter(n -> !n.equalsIgnoreCase(npcName)) + .collect(Collectors.joining(",")) + ); + } + + // set attackable npcs + public static void setAttackableNpcs(String npcNames) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "monster", + npcNames + ); + } + + private String getNpcNameFromMenuEntry(String menuTarget) { + return menuTarget.replaceAll("<[^>]*>|\\(.*\\)", "").trim(); + } + + @Subscribe + public void onChatMessage(ChatMessage event) { + if (event.getMessage().contains("reach that")) { + AttackNpcScript.skipNpc(); + } + } + // on setting change + @Subscribe + public void onConfigChanged(ConfigChanged event) { + + + if (event.getKey().equals("Safe Spot")) { + + if (!config.toggleSafeSpot()) { + // reset safe spot to default + setSafeSpot(new WorldPoint(0, 0, 0)); + } + } + if(event.getKey().equals("Combat")) { + if (!config.toggleCombat() && config.toggleCenterTile()) { + setCenter(new WorldPoint(0, 0, 0)); + } + if (config.toggleCombat() && !config.toggleCenterTile()) { + setCenter(Rs2Player.getWorldLocation()); + } + + } + // Handle special attack weapon config changes + if (event.getKey().equals("Use special attack") || event.getKey().equals("Spec weapon")) { + if (config.useSpecialAttack() && config.specWeapon() != null) { + Microbot.getSpecialAttackConfigs() + .setSpecialAttack(true) + .setSpecialAttackWeapon(config.specWeapon()) + .setMinimumSpecEnergy(config.specWeapon().getEnergyRequired()); + } else { + Microbot.getSpecialAttackConfigs().reset(); + } + } + } + + @Subscribe + public void onProjectileMoved(ProjectileMoved event) { + Projectile projectile = event.getProjectile(); + if (projectile.getTargetActor() == null) { + //Projectiles that have targetActor null are targeting a WorldPoint and are dodgeable. + dodgeScript.projectiles.add(event.getProjectile()); + } + } + + @Subscribe + public void onGameTick(GameTick gameTick) { + try { + //execute flicker script + if(config.togglePrayer()) + flickerScript.onGameTick(); + } catch (Exception e) { + log.info("AIO Fighter Plugin onGameTick Error: " + e.getMessage()); + } + } + + @Subscribe + public void onNpcDespawned(NpcDespawned npcDespawned) { + try { + if(config.togglePrayer()) + flickerScript.onNpcDespawned(npcDespawned); + } catch (Exception e) { + log.info("AIO Fighter Plugin onNpcDespawned Error: " + e.getMessage()); + } + } + + @Subscribe + public void onHitsplatApplied(HitsplatApplied event){ + try { + if (event.getActor() != Microbot.getClient().getLocalPlayer()) return; + final Hitsplat hitsplat = event.getHitsplat(); + + if ((hitsplat.isMine()) && event.getActor().getInteracting() instanceof NPC && config.togglePrayer() && (config.prayerStyle() == PrayerStyle.LAZY_FLICK) || (config.prayerStyle() == PrayerStyle.PERFECT_LAZY_FLICK) || (config.prayerStyle() == PrayerStyle.MIXED_LAZY_FLICK)) { + flickerScript.resetLastAttack(true); + Rs2Prayer.disableAllPrayers(); + if (config.toggleQuickPray()) + Rs2Prayer.toggleQuickPrayer(false); + + + } + } catch (Exception e) { + log.info("AIO Fighter Plugin onHitsplatApplied Error: " + e.getMessage()); + } + } + @Subscribe + public void onMenuOpened(MenuOpened event) { + lastMenuOpenedPoint = Microbot.getClient().getMouseCanvasPosition(); + trueTile = getSelectedWorldPoint(); + } + @Subscribe + private void onMenuEntryAdded(MenuEntryAdded event) { + if (Microbot.getClient().isKeyPressed(KeyCode.KC_SHIFT) && event.getOption().equals(WALK_HERE) && event.getTarget().isEmpty() && config.toggleCenterTile()) { + addMenuEntry(event, SET, CENTER_TILE, 1); + } + if (Microbot.getClient().isKeyPressed(KeyCode.KC_SHIFT) && event.getOption().equals(WALK_HERE) && event.getTarget().isEmpty()) { + addMenuEntry(event, SET, SAFE_SPOT, 1); + } + if (event.getOption().equals(ATTACK) && config.attackableNpcs().contains(getNpcNameFromMenuEntry(Text.removeTags(event.getTarget())))) { + addMenuEntry(event, REMOVE_FROM, event.getTarget(), 1); + } + if (event.getOption().equals(ATTACK) && !config.attackableNpcs().contains(getNpcNameFromMenuEntry(Text.removeTags(event.getTarget())))) { + addMenuEntry(event, ADD_TO, event.getTarget(), 1); + } + + } + + private WorldPoint getSelectedWorldPoint() { + if (Microbot.getClient().getWidget(ComponentID.WORLD_MAP_MAPVIEW) == null) { + if (Microbot.getClient().getSelectedSceneTile() != null) { + return Microbot.getClient().isInInstancedRegion() ? + WorldPoint.fromLocalInstance(Microbot.getClient(), Microbot.getClient().getSelectedSceneTile().getLocalLocation()) : + Microbot.getClient().getSelectedSceneTile().getWorldLocation(); + } + } else { + return calculateMapPoint(Microbot.getClient().isMenuOpen() ? lastMenuOpenedPoint : Microbot.getClient().getMouseCanvasPosition()); + } + return null; + } + public WorldPoint calculateMapPoint(Point point) { + WorldMap worldMap = Microbot.getClient().getWorldMap(); + float zoom = worldMap.getWorldMapZoom(); + final WorldPoint mapPoint = new WorldPoint(worldMap.getWorldMapPosition().getX(), worldMap.getWorldMapPosition().getY(), 0); + final Point middle = mapWorldPointToGraphicsPoint(mapPoint); + + if (point == null || middle == null) { + return null; + } + + final int dx = (int) ((point.getX() - middle.getX()) / zoom); + final int dy = (int) ((-(point.getY() - middle.getY())) / zoom); + + return mapPoint.dx(dx).dy(dy); + } + public Point mapWorldPointToGraphicsPoint(WorldPoint worldPoint) { + WorldMap worldMap = Microbot.getClient().getWorldMap(); + + float pixelsPerTile = worldMap.getWorldMapZoom(); + + Widget map = Microbot.getClient().getWidget(ComponentID.WORLD_MAP_MAPVIEW); + if (map != null) { + Rectangle worldMapRect = map.getBounds(); + + int widthInTiles = (int) Math.ceil(worldMapRect.getWidth() / pixelsPerTile); + int heightInTiles = (int) Math.ceil(worldMapRect.getHeight() / pixelsPerTile); + + Point worldMapPosition = worldMap.getWorldMapPosition(); + + int yTileMax = worldMapPosition.getY() - heightInTiles / 2; + int yTileOffset = (yTileMax - worldPoint.getY() - 1) * -1; + int xTileOffset = worldPoint.getX() + widthInTiles / 2 - worldMapPosition.getX(); + + int xGraphDiff = ((int) (xTileOffset * pixelsPerTile)); + int yGraphDiff = (int) (yTileOffset * pixelsPerTile); + + yGraphDiff -= (int) (pixelsPerTile - Math.ceil(pixelsPerTile / 2)); + xGraphDiff += (int) (pixelsPerTile - Math.ceil(pixelsPerTile / 2)); + + yGraphDiff = worldMapRect.height - yGraphDiff; + yGraphDiff += (int) worldMapRect.getY(); + xGraphDiff += (int) worldMapRect.getX(); + + return new Point(xGraphDiff, yGraphDiff); + } + return null; + } + private void onMenuOptionClicked(MenuEntry entry) { + + + + if (entry.getOption().equals(SET) && entry.getTarget().equals(CENTER_TILE)) { + setCenter(trueTile); + } + if (entry.getOption().equals(SET) && entry.getTarget().equals(SAFE_SPOT)) { + setSafeSpot(trueTile); + } + + + if (entry.getType() != MenuAction.WALK) { + lastClick = entry; + } + } + + + @Subscribe + private void onMenuOptionClicked(MenuOptionClicked event) + { + if (event.getMenuOption().equals(ADD_TO)) { + addNpcToList(getNpcNameFromMenuEntry(event.getMenuTarget())); + } + if (event.getMenuOption().equals(REMOVE_FROM)) { + removeNpcFromList(getNpcNameFromMenuEntry(event.getMenuTarget())); + } + } + private void addMenuEntry(MenuEntryAdded event, String option, String target, int position) { + List entries = new LinkedList<>(Arrays.asList(Microbot.getClient().getMenuEntries())); + + if (entries.stream().anyMatch(e -> e.getOption().equals(option) && e.getTarget().equals(target))) { + return; + } + + Microbot.getClient().createMenuEntry(position) + .setOption(option) + .setTarget(target) + .setParam0(event.getActionParam0()) + .setParam1(event.getActionParam1()) + .setIdentifier(event.getIdentifier()) + .setType(MenuAction.RUNELITE) + .onClick(this::onMenuOptionClicked); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java index 5981e1da9cc..78c8601b571 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java @@ -1,255 +1,255 @@ -package net.runelite.client.plugins.microbot.aiofighter.combat; - -import lombok.SneakyThrows; -import net.runelite.api.Actor; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.gameval.ItemID; -import net.runelite.api.gameval.VarPlayerID; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.Script; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; -import net.runelite.client.plugins.microbot.aiofighter.enums.AttackStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.AttackStyleMapper; -import net.runelite.client.plugins.microbot.aiofighter.enums.State; -import net.runelite.client.plugins.microbot.shortestpath.ShortestPathPlugin; -import net.runelite.client.plugins.microbot.util.ActorModel; -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.ActivityIntensity; -import net.runelite.client.plugins.microbot.util.camera.Rs2Camera; -import net.runelite.client.plugins.microbot.util.coords.Rs2WorldArea; -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.Rs2NpcManager; -import net.runelite.client.plugins.microbot.util.npc.Rs2NpcModel; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; -import net.runelite.client.plugins.microbot.util.prayer.Rs2Prayer; -import net.runelite.client.plugins.microbot.util.prayer.Rs2PrayerEnum; -import net.runelite.client.plugins.microbot.util.slayer.Rs2Slayer; -import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; -import org.slf4j.event.Level; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -import static net.runelite.api.gameval.VarbitID.*; - -public class AttackNpcScript extends Script { - - public static Actor currentNpc = null; - public static AtomicReference> filteredAttackableNpcs = new AtomicReference<>(new ArrayList<>()); - public static Rs2WorldArea attackableArea = null; - public static volatile int cachedTargetNpcIndex = -1; - private boolean messageShown = false; - private int noNpcCount = 0; - - public static void skipNpc() { - currentNpc = null; - } - - @SneakyThrows - public void run(AIOFighterConfig config) { - try { - Rs2NpcManager.loadJson(); - Rs2Antiban.resetAntibanSettings(); - Rs2Antiban.antibanSetupTemplates.applyCombatSetup(); - Rs2Antiban.setActivityIntensity(ActivityIntensity.EXTREME); - } catch (Exception e) { - throw new RuntimeException(e); - } - - mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { - try { - if (!Microbot.isLoggedIn() || !super.run() || !config.toggleCombat()) - return; - - if (config.centerLocation().distanceTo(Rs2Player.getWorldLocation()) < config.attackRadius() && - !config.centerLocation().equals(new WorldPoint(0, 0, 0)) && AIOFighterPlugin.getState() != State.BANKING) { - if (ShortestPathPlugin.getPathfinder() != null) - Rs2Walker.setTarget(null); - AIOFighterPlugin.setState(State.IDLE); - } - - attackableArea = new Rs2WorldArea(config.centerLocation().toWorldArea()); - attackableArea = attackableArea.offset(config.attackRadius()); - List npcsToAttack = Arrays.stream(config.attackableNpcs().split(",")) - .map(x -> x.trim().toLowerCase()) - .collect(Collectors.toList()); - filteredAttackableNpcs.set( - Rs2Npc.getAttackableNpcs(config.attackReachableNpcs()) - .filter(npc -> npc.getWorldLocation().distanceTo(config.centerLocation()) <= config.attackRadius()) - .filter(npc -> npc.getName() != null && !npcsToAttack.isEmpty() && npcsToAttack.stream().anyMatch(npc.getName()::equalsIgnoreCase)) - .sorted(Comparator.comparingInt((Rs2NpcModel npc) -> npc.getInteracting() == Microbot.getClient().getLocalPlayer() ? 0 : 1) - .thenComparingInt(npc -> Rs2Player.getRs2WorldPoint().distanceToPath(npc.getWorldLocation()))) - .collect(Collectors.toList()) - ); - final List attackableNpcs = new ArrayList<>(); - - for (var attackableNpc : filteredAttackableNpcs.get()) { - if (attackableNpc == null || attackableNpc.getName() == null) continue; - for (var npcToAttack : npcsToAttack) { - if (npcToAttack.equalsIgnoreCase(attackableNpc.getName())) { - attackableNpcs.add(attackableNpc); - } - } - } - filteredAttackableNpcs.set(attackableNpcs); - - if (config.state().equals(State.BANKING) || config.state().equals(State.WALKING)) - return; - - // Check if we should pause while looting is happening - if (Microbot.pauseAllScripts.get()) { - return; // Don't attack while looting - } - - // Check if we need to update our cached target (but not while waiting for loot) - if (!AIOFighterPlugin.isWaitingForLoot()) { - Actor 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(); - } - } - } - - // Check if our cached target died - if (config.toggleWaitForLoot() && !AIOFighterPlugin.isWaitingForLoot() && cachedTargetNpcIndex != -1) { - // Find the NPC by index using Rs2 API - Rs2NpcModel cachedNpcModel = Rs2Npc.getNpcByIndex(cachedTargetNpcIndex); - - if (cachedNpcModel != null && (cachedNpcModel.isDead() || (cachedNpcModel.getHealthRatio() == 0 && cachedNpcModel.getHealthScale() > 0))) { - AIOFighterPlugin.setWaitingForLoot(true); - AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis()); - Microbot.status = "Waiting for loot..."; - Microbot.log("NPC died, waiting for loot..."); - cachedTargetNpcIndex = -1; - return; - } - } - - // Check if we're waiting for loot - if (config.toggleWaitForLoot() && AIOFighterPlugin.isWaitingForLoot()) { - long timeSinceKill = System.currentTimeMillis() - AIOFighterPlugin.getLastNpcKilledTime(); - int timeoutMs = config.lootWaitTimeout() * 1000; - if (timeSinceKill >= timeoutMs) { - // Timeout reached, resume combat - AIOFighterPlugin.clearWaitForLoot("Loot wait timeout reached, resuming combat"); - cachedTargetNpcIndex = -1; // Clear cached NPC on timeout - } else { - // Still waiting for loot, don't attack - int secondsLeft = (int) Math.max(1, TimeUnit.MILLISECONDS.toSeconds(timeoutMs - timeSinceKill)); - Microbot.status = "Waiting for loot... " + secondsLeft + "s"; - return; - } - } - - if (config.toggleCenterTile() && config.centerLocation().getX() == 0 - && config.centerLocation().getY() == 0) { - if (!messageShown) { - Microbot.showMessage("Please set a center location"); - messageShown = true; - } - return; - } - messageShown = false; - - - if (Rs2AntibanSettings.actionCooldownActive) { - AIOFighterPlugin.setState(State.COMBAT); - handleItemOnNpcToKill(config); - return; - } - - if (!attackableNpcs.isEmpty()) { - noNpcCount = 0; - - Rs2NpcModel npc = attackableNpcs.stream().findFirst().orElse(null); - - if (!Rs2Camera.isTileOnScreen(npc.getLocalLocation())) - Rs2Camera.turnTo(npc); - - Rs2Npc.interact(npc, "attack"); - Microbot.status = "Attacking " + npc.getName(); - Rs2Antiban.actionCooldown(); - //sleepUntil(Rs2Player::isInteracting, 1000); - - if (config.togglePrayer()) { - if (!config.toggleQuickPray()) { - AttackStyle attackStyle = AttackStyleMapper - .mapToAttackStyle(Rs2NpcManager.getAttackStyle(npc.getId())); - if (attackStyle != null) { - switch (attackStyle) { - case MAGE: - Rs2Prayer.toggle(Rs2PrayerEnum.PROTECT_MAGIC, true); - break; - case MELEE: - Rs2Prayer.toggle(Rs2PrayerEnum.PROTECT_MELEE, true); - break; - case RANGED: - Rs2Prayer.toggle(Rs2PrayerEnum.PROTECT_RANGE, true); - break; - } - } - } else { - Rs2Prayer.toggleQuickPrayer(true); - } - } - - - } else { - if (Rs2Player.getWorldLocation().isInArea(attackableArea)) { - Microbot.log(Level.INFO, "No attackable NPC found"); - noNpcCount++; - if (noNpcCount > 60 && config.slayerMode()) { - Microbot.log(Level.INFO, "No attackable NPC found for 60 ticks, resetting slayer task"); - AIOFighterPlugin.addBlacklistedSlayerNpcs(Rs2Slayer.slayerTaskMonsterTarget); - noNpcCount = 0; - SlayerScript.reset(); - } - } else { - Rs2Walker.walkTo(config.centerLocation(), 0); - AIOFighterPlugin.setState(State.WALKING); - } - - } - } catch (Exception ex) { - Microbot.logStackTrace(this.getClass().getSimpleName(), ex); - } - }, 0, 600, TimeUnit.MILLISECONDS); - } - - - /** - * item on npcs that need to kill like rockslug - */ - private void handleItemOnNpcToKill(AIOFighterConfig config) { - Rs2NpcModel npc = Rs2Npc.getNpcsForPlayer(ActorModel::isDead).findFirst().orElse(null); - List lizardVariants = new ArrayList<>(Arrays.asList("Lizard", "Desert Lizard", "Small Lizard")); - if (npc == null) return; - if (Microbot.getVarbitValue(SLAYER_AUTOKILL_DESERTLIZARDS) == 0 && lizardVariants.contains(npc.getName()) && npc.getHealthRatio() < 5) { - Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ICY_WATER, npc); - Rs2Player.waitForAnimation(); - } else if (Microbot.getVarbitValue(SLAYER_AUTOKILL_ROCKSLUGS) == 0 && npc.getName().equalsIgnoreCase("rockslug") && npc.getHealthRatio() < 5) { - Rs2Inventory.useItemOnNpc(ItemID.SLAYER_BAG_OF_SALT, npc); - Rs2Player.waitForAnimation(); - } else if (Microbot.getVarbitValue(SLAYER_AUTOKILL_GARGOYLES) == 0 && npc.getName().equalsIgnoreCase("gargoyle") && npc.getHealthRatio() < 3) { - Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ROCK_HAMMER, npc); - Rs2Player.waitForAnimation(); - } - } - - - @Override - public void shutdown() { - super.shutdown(); - } -} +package net.runelite.client.plugins.microbot.aiofighter.combat; + +import lombok.SneakyThrows; +import net.runelite.api.Actor; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.gameval.ItemID; +import net.runelite.api.gameval.VarPlayerID; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.Script; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; +import net.runelite.client.plugins.microbot.aiofighter.enums.AttackStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.AttackStyleMapper; +import net.runelite.client.plugins.microbot.aiofighter.enums.State; +import net.runelite.client.plugins.microbot.shortestpath.ShortestPathPlugin; +import net.runelite.client.plugins.microbot.util.ActorModel; +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.ActivityIntensity; +import net.runelite.client.plugins.microbot.util.camera.Rs2Camera; +import net.runelite.client.plugins.microbot.util.coords.Rs2WorldArea; +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.Rs2NpcManager; +import net.runelite.client.plugins.microbot.util.npc.Rs2NpcModel; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; +import net.runelite.client.plugins.microbot.util.prayer.Rs2Prayer; +import net.runelite.client.plugins.microbot.util.prayer.Rs2PrayerEnum; +import net.runelite.client.plugins.microbot.util.slayer.Rs2Slayer; +import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; +import org.slf4j.event.Level; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import static net.runelite.api.gameval.VarbitID.*; + +public class AttackNpcScript extends Script { + + public static Actor currentNpc = null; + public static AtomicReference> filteredAttackableNpcs = new AtomicReference<>(new ArrayList<>()); + public static Rs2WorldArea attackableArea = null; + public static volatile int cachedTargetNpcIndex = -1; + private boolean messageShown = false; + private int noNpcCount = 0; + + public static void skipNpc() { + currentNpc = null; + } + + @SneakyThrows + public void run(AIOFighterConfig config) { + try { + Rs2NpcManager.loadJson(); + Rs2Antiban.resetAntibanSettings(); + Rs2Antiban.antibanSetupTemplates.applyCombatSetup(); + Rs2Antiban.setActivityIntensity(ActivityIntensity.EXTREME); + } catch (Exception e) { + throw new RuntimeException(e); + } + + mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { + try { + if (!Microbot.isLoggedIn() || !super.run() || !config.toggleCombat()) + return; + + if (config.centerLocation().distanceTo(Rs2Player.getWorldLocation()) < config.attackRadius() && + !config.centerLocation().equals(new WorldPoint(0, 0, 0)) && AIOFighterPlugin.getState() != State.BANKING) { + if (ShortestPathPlugin.getPathfinder() != null) + Rs2Walker.setTarget(null); + AIOFighterPlugin.setState(State.IDLE); + } + + attackableArea = new Rs2WorldArea(config.centerLocation().toWorldArea()); + attackableArea = attackableArea.offset(config.attackRadius()); + List npcsToAttack = Arrays.stream(config.attackableNpcs().split(",")) + .map(x -> x.trim().toLowerCase()) + .collect(Collectors.toList()); + filteredAttackableNpcs.set( + Rs2Npc.getAttackableNpcs(config.attackReachableNpcs()) + .filter(npc -> npc.getWorldLocation().distanceTo(config.centerLocation()) <= config.attackRadius()) + .filter(npc -> npc.getName() != null && !npcsToAttack.isEmpty() && npcsToAttack.stream().anyMatch(npc.getName()::equalsIgnoreCase)) + .sorted(Comparator.comparingInt((Rs2NpcModel npc) -> npc.getInteracting() == Microbot.getClient().getLocalPlayer() ? 0 : 1) + .thenComparingInt(npc -> Rs2Player.getRs2WorldPoint().distanceToPath(npc.getWorldLocation()))) + .collect(Collectors.toList()) + ); + final List attackableNpcs = new ArrayList<>(); + + for (var attackableNpc : filteredAttackableNpcs.get()) { + if (attackableNpc == null || attackableNpc.getName() == null) continue; + for (var npcToAttack : npcsToAttack) { + if (npcToAttack.equalsIgnoreCase(attackableNpc.getName())) { + attackableNpcs.add(attackableNpc); + } + } + } + filteredAttackableNpcs.set(attackableNpcs); + + if (config.state().equals(State.BANKING) || config.state().equals(State.WALKING)) + return; + + // Check if we should pause while looting is happening + if (Microbot.pauseAllScripts.get()) { + return; // Don't attack while looting + } + + // Check if we need to update our cached target (but not while waiting for loot) + if (!AIOFighterPlugin.isWaitingForLoot()) { + Actor 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(); + } + } + } + + // Check if our cached target died + if (config.toggleWaitForLoot() && !AIOFighterPlugin.isWaitingForLoot() && cachedTargetNpcIndex != -1) { + // Find the NPC by index using Rs2 API + Rs2NpcModel cachedNpcModel = Rs2Npc.getNpcByIndex(cachedTargetNpcIndex); + + if (cachedNpcModel != null && (cachedNpcModel.isDead() || (cachedNpcModel.getHealthRatio() == 0 && cachedNpcModel.getHealthScale() > 0))) { + AIOFighterPlugin.setWaitingForLoot(true); + AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis()); + Microbot.status = "Waiting for loot..."; + Microbot.log("NPC died, waiting for loot..."); + cachedTargetNpcIndex = -1; + return; + } + } + + // Check if we're waiting for loot + if (config.toggleWaitForLoot() && AIOFighterPlugin.isWaitingForLoot()) { + long timeSinceKill = System.currentTimeMillis() - AIOFighterPlugin.getLastNpcKilledTime(); + int timeoutMs = config.lootWaitTimeout() * 1000; + if (timeSinceKill >= timeoutMs) { + // Timeout reached, resume combat + AIOFighterPlugin.clearWaitForLoot("Loot wait timeout reached, resuming combat"); + cachedTargetNpcIndex = -1; // Clear cached NPC on timeout + } else { + // Still waiting for loot, don't attack + int secondsLeft = (int) Math.max(1, TimeUnit.MILLISECONDS.toSeconds(timeoutMs - timeSinceKill)); + Microbot.status = "Waiting for loot... " + secondsLeft + "s"; + return; + } + } + + if (config.toggleCenterTile() && config.centerLocation().getX() == 0 + && config.centerLocation().getY() == 0) { + if (!messageShown) { + Microbot.showMessage("Please set a center location"); + messageShown = true; + } + return; + } + messageShown = false; + + + if (Rs2AntibanSettings.actionCooldownActive) { + AIOFighterPlugin.setState(State.COMBAT); + handleItemOnNpcToKill(config); + return; + } + + if (!attackableNpcs.isEmpty()) { + noNpcCount = 0; + + Rs2NpcModel npc = attackableNpcs.stream().findFirst().orElse(null); + + if (!Rs2Camera.isTileOnScreen(npc.getLocalLocation())) + Rs2Camera.turnTo(npc); + + Rs2Npc.interact(npc, "attack"); + Microbot.status = "Attacking " + npc.getName(); + Rs2Antiban.actionCooldown(); + //sleepUntil(Rs2Player::isInteracting, 1000); + + if (config.togglePrayer()) { + if (!config.toggleQuickPray()) { + AttackStyle attackStyle = AttackStyleMapper + .mapToAttackStyle(Rs2NpcManager.getAttackStyle(npc.getId())); + if (attackStyle != null) { + switch (attackStyle) { + case MAGE: + Rs2Prayer.toggle(Rs2PrayerEnum.PROTECT_MAGIC, true); + break; + case MELEE: + Rs2Prayer.toggle(Rs2PrayerEnum.PROTECT_MELEE, true); + break; + case RANGED: + Rs2Prayer.toggle(Rs2PrayerEnum.PROTECT_RANGE, true); + break; + } + } + } else { + Rs2Prayer.toggleQuickPrayer(true); + } + } + + + } else { + if (Rs2Player.getWorldLocation().isInArea(attackableArea)) { + Microbot.log(Level.INFO, "No attackable NPC found"); + noNpcCount++; + if (noNpcCount > 60 && config.slayerMode()) { + Microbot.log(Level.INFO, "No attackable NPC found for 60 ticks, resetting slayer task"); + AIOFighterPlugin.addBlacklistedSlayerNpcs(Rs2Slayer.slayerTaskMonsterTarget); + noNpcCount = 0; + SlayerScript.reset(); + } + } else { + Rs2Walker.walkTo(config.centerLocation(), 0); + AIOFighterPlugin.setState(State.WALKING); + } + + } + } catch (Exception ex) { + Microbot.logStackTrace(this.getClass().getSimpleName(), ex); + } + }, 0, 600, TimeUnit.MILLISECONDS); + } + + + /** + * item on npcs that need to kill like rockslug + */ + private void handleItemOnNpcToKill(AIOFighterConfig config) { + Rs2NpcModel npc = Rs2Npc.getNpcsForPlayer(ActorModel::isDead).findFirst().orElse(null); + List lizardVariants = new ArrayList<>(Arrays.asList("Lizard", "Desert Lizard", "Small Lizard")); + if (npc == null) return; + if (Microbot.getVarbitValue(SLAYER_AUTOKILL_DESERTLIZARDS) == 0 && lizardVariants.contains(npc.getName()) && npc.getHealthRatio() < 5) { + Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ICY_WATER, npc); + Rs2Player.waitForAnimation(); + } else if (Microbot.getVarbitValue(SLAYER_AUTOKILL_ROCKSLUGS) == 0 && npc.getName().equalsIgnoreCase("rockslug") && npc.getHealthRatio() < 5) { + Rs2Inventory.useItemOnNpc(ItemID.SLAYER_BAG_OF_SALT, npc); + Rs2Player.waitForAnimation(); + } else if (Microbot.getVarbitValue(SLAYER_AUTOKILL_GARGOYLES) == 0 && npc.getName().equalsIgnoreCase("gargoyle") && npc.getHealthRatio() < 3) { + Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ROCK_HAMMER, npc); + Rs2Player.waitForAnimation(); + } + } + + + @Override + public void shutdown() { + super.shutdown(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java index 00e04018c4b..50c5dc67342 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java @@ -1,153 +1,157 @@ -package net.runelite.client.plugins.microbot.aiofighter.loot; - -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.plugins.grounditems.GroundItem; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.Script; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; -import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.State; -import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; -import net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem; -import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; -import net.runelite.client.plugins.microbot.util.inventory.Rs2RunePouch; -import net.runelite.client.plugins.microbot.util.magic.Runes; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; - -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import static net.runelite.api.TileItem.OWNERSHIP_SELF; -import static net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem.*; - -@Slf4j -public class LootScript extends Script { - int minFreeSlots = 0; - - public boolean run(AIOFighterConfig config) { - mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { - try { - minFreeSlots = config.bank() ? config.minFreeSlots() : 0; - if (!super.run()) return; - if (!Microbot.isLoggedIn()) return; - if (!config.toggleLootItems()) return; - if (Rs2AntibanSettings.actionCooldownActive) return; - if (AIOFighterPlugin.getState().equals(State.BANKING) || AIOFighterPlugin.getState().equals(State.WALKING)) { - return; - } - if (Rs2Player.isInCombat() && !config.toggleForceLoot() && !AIOFighterPlugin.isWaitingForLoot()) { - return; - } - - - String[] itemNamesToLoot = lootItemNames(config); - final Predicate filter = groundItem -> - groundItem.getLocation().distanceTo(Microbot.getClient().getLocalPlayer().getWorldLocation()) < config.attackRadius() && - (!config.toggleOnlyLootMyItems() || groundItem.getOwnership() == OWNERSHIP_SELF) && - (shouldLootBasedOnName(groundItem, itemNamesToLoot) || shouldLootBasedOnValue(groundItem, config)); - List groundItems = getGroundItems().values().stream() - .filter(filter) - .collect(Collectors.toList()); - - if (groundItems.isEmpty()) { - return; - } - if (config.toggleDelayedLooting()) { - groundItems.sort(Comparator.comparingInt(Rs2GroundItem::calculateDespawnTime)); - } - // Defer clearing wait-for-loot until we successfully pick at least one item - //Pause other scripts before looting and always release - Microbot.pauseAllScripts.getAndSet(true); - try { - 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)) { - // Skip this item and continue to the next rather than aborting the whole pass - continue; - } - // Clear wait state after first successful pickup - if (!clearedWait && AIOFighterPlugin.isWaitingForLoot()) { - AIOFighterPlugin.clearWaitForLoot("First loot item picked up"); - clearedWait = true; - } - } - Microbot.log("Looting complete"); - } finally { - Microbot.pauseAllScripts.compareAndSet(true, false); - } - } catch (Exception ex) { - Microbot.log("Looterscript: " + ex.getMessage()); - } - - }, 0, 200, TimeUnit.MILLISECONDS); - return true; - } - - private boolean canStackItem(GroundItem groundItem) { - if (!groundItem.isStackable()) { - return false; - } - int runePouchRunes = Rs2RunePouch.getQuantity(groundItem.getItemId()); - if (runePouchRunes > 0 && runePouchRunes <= 16000 - groundItem.getQuantity()) { - return true; - } - //TODO("Coal bag, Herb Sack, Seed pack") - return Rs2Inventory.contains(groundItem.getItemId()); - } - - private boolean shouldLootBasedOnValue(GroundItem groundItem, AIOFighterConfig config) { - if (config.looterStyle() != DefaultLooterStyle.GE_PRICE_RANGE && config.looterStyle() != DefaultLooterStyle.MIXED) - return false; - int price = groundItem.getGePrice(); - return config.minPriceOfItemsToLoot() <= price && price / groundItem.getQuantity() <= config.maxPriceOfItemsToLoot(); - } - - private boolean shouldLootBasedOnName(GroundItem groundItem, String[] itemNamesToLoot) { - return Arrays.stream(itemNamesToLoot).anyMatch(name -> groundItem.getName().trim().toLowerCase().contains(name.trim().toLowerCase())); - } - - private String[] lootItemNames(AIOFighterConfig config) { - ArrayList itemNamesToLoot = new ArrayList<>(); - if (config.toggleLootArrows()) { - itemNamesToLoot.add("arrow"); - } - if (config.toggleBuryBones()) { - itemNamesToLoot.add("bones"); - } - if (config.toggleScatter()) { - itemNamesToLoot.add(" ashes"); - } - if (config.toggleLootRunes()) { - itemNamesToLoot.add(" rune"); - } - if (config.toggleLootCoins()) { - itemNamesToLoot.add("coins"); - } - if (config.toggleLootUntradables()) { - itemNamesToLoot.add("untradeable"); - itemNamesToLoot.add("scroll box"); - } - if (config.looterStyle().equals(DefaultLooterStyle.MIXED) || config.looterStyle().equals(DefaultLooterStyle.ITEM_LIST)) { - itemNamesToLoot.addAll(Arrays.asList(config.listOfItemsToLoot().trim().split(","))); - } - return itemNamesToLoot.toArray(new String[0]); - } - - public void shutdown() { - super.shutdown(); - } -} +package net.runelite.client.plugins.microbot.aiofighter.loot; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.grounditems.GroundItem; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.Script; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; +import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.State; +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem; +import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; +import net.runelite.client.plugins.microbot.util.inventory.Rs2RunePouch; +import net.runelite.client.plugins.microbot.util.magic.Runes; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static net.runelite.api.TileItem.OWNERSHIP_SELF; +import static net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem.*; + +@Slf4j +public class LootScript extends Script { + int minFreeSlots = 0; + + public boolean run(AIOFighterConfig config) { + mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { + try { + minFreeSlots = config.bank() ? config.minFreeSlots() : 0; + if (!super.run()) return; + if (!Microbot.isLoggedIn()) return; + if (!config.toggleLootItems()) return; + if (Rs2AntibanSettings.actionCooldownActive) return; + if (AIOFighterPlugin.getState().equals(State.BANKING) || AIOFighterPlugin.getState().equals(State.WALKING)) { + return; + } + if (Rs2Player.isInCombat() && !config.toggleForceLoot() && !AIOFighterPlugin.isWaitingForLoot()) { + return; + } + + + String[] itemNamesToLoot = lootItemNames(config); + final Predicate filter = groundItem -> + groundItem.getLocation().distanceTo(Microbot.getClient().getLocalPlayer().getWorldLocation()) < config.attackRadius() && + (!config.toggleOnlyLootMyItems() || groundItem.getOwnership() == OWNERSHIP_SELF) && + (shouldLootBasedOnName(groundItem, itemNamesToLoot) || shouldLootBasedOnValue(groundItem, config)); + List groundItems = getGroundItems().values().stream() + .filter(filter) + .collect(Collectors.toList()); + + if (groundItems.isEmpty()) { + return; + } + if (config.toggleDelayedLooting()) { + groundItems.sort(Comparator.comparingInt(Rs2GroundItem::calculateDespawnTime)); + } + // Defer clearing wait-for-loot until we successfully pick at least one item + //Pause other scripts before looting and always release + Microbot.pauseAllScripts.getAndSet(true); + try { + 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, 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; + } + } + Microbot.log("Picking up loot: " + groundItem.getName()); + if (!waitForGroundItemDespawn(() -> interact(groundItem), groundItem)) { + // Skip this item and continue to the next rather than aborting the whole pass + continue; + } + // Clear wait state after first successful pickup + if (!clearedWait && AIOFighterPlugin.isWaitingForLoot()) { + AIOFighterPlugin.clearWaitForLoot("First loot item picked up"); + clearedWait = true; + } + } + Microbot.log("Looting complete"); + } finally { + Microbot.pauseAllScripts.compareAndSet(true, false); + } + } catch (Exception ex) { + Microbot.log("Looterscript: " + ex.getMessage()); + } + + }, 0, 200, TimeUnit.MILLISECONDS); + return true; + } + + private boolean canStackItem(GroundItem groundItem) { + if (!groundItem.isStackable()) { + return false; + } + int runePouchRunes = Rs2RunePouch.getQuantity(groundItem.getItemId()); + if (runePouchRunes > 0 && runePouchRunes <= 16000 - groundItem.getQuantity()) { + return true; + } + //TODO("Coal bag, Herb Sack, Seed pack") + return Rs2Inventory.contains(groundItem.getItemId()); + } + + private boolean shouldLootBasedOnValue(GroundItem groundItem, AIOFighterConfig config) { + if (config.looterStyle() != DefaultLooterStyle.GE_PRICE_RANGE && config.looterStyle() != DefaultLooterStyle.MIXED) + return false; + int price = groundItem.getGePrice(); + return config.minPriceOfItemsToLoot() <= price && price / groundItem.getQuantity() <= config.maxPriceOfItemsToLoot(); + } + + private boolean shouldLootBasedOnName(GroundItem groundItem, String[] itemNamesToLoot) { + return Arrays.stream(itemNamesToLoot).anyMatch(name -> groundItem.getName().trim().toLowerCase().contains(name.trim().toLowerCase())); + } + + private String[] lootItemNames(AIOFighterConfig config) { + ArrayList itemNamesToLoot = new ArrayList<>(); + if (config.toggleLootArrows()) { + itemNamesToLoot.add("arrow"); + } + if (config.toggleBuryBones()) { + itemNamesToLoot.add("bones"); + } + if (config.toggleScatter()) { + itemNamesToLoot.add(" ashes"); + } + if (config.toggleLootRunes()) { + itemNamesToLoot.add(" rune"); + } + if (config.toggleLootCoins()) { + itemNamesToLoot.add("coins"); + } + if (config.toggleLootUntradables()) { + itemNamesToLoot.add("untradeable"); + itemNamesToLoot.add("scroll box"); + } + if (config.looterStyle().equals(DefaultLooterStyle.MIXED) || config.looterStyle().equals(DefaultLooterStyle.ITEM_LIST)) { + itemNamesToLoot.addAll(Arrays.asList(config.listOfItemsToLoot().trim().split(","))); + } + return itemNamesToLoot.toArray(new String[0]); + } + + public void shutdown() { + super.shutdown(); + } +} From 285c006f295940492e170a2d35f3a76c33144ea3 Mon Sep 17 00:00:00 2001 From: Pert Date: Mon, 25 Aug 2025 12:27:37 -0400 Subject: [PATCH 15/16] fix(aiofighter): preserve concurrent pause state in LootScript --- .../client/plugins/microbot/aiofighter/loot/LootScript.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java index 50c5dc67342..d094f22e62f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java @@ -60,7 +60,7 @@ public boolean run(AIOFighterConfig config) { } // Defer clearing wait-for-loot until we successfully pick at least one item //Pause other scripts before looting and always release - Microbot.pauseAllScripts.getAndSet(true); + boolean previousPauseState = Microbot.pauseAllScripts.getAndSet(true); try { boolean clearedWait = false; for (GroundItem groundItem : groundItems) { @@ -91,7 +91,7 @@ public boolean run(AIOFighterConfig config) { } Microbot.log("Looting complete"); } finally { - Microbot.pauseAllScripts.compareAndSet(true, false); + Microbot.pauseAllScripts.set(previousPauseState); } } catch (Exception ex) { Microbot.log("Looterscript: " + ex.getMessage()); From 750385adcfad6744c859cfef9c1842ef2a053312 Mon Sep 17 00:00:00 2001 From: Pert Date: Mon, 25 Aug 2025 22:55:51 -0400 Subject: [PATCH 16/16] fix(AIOfighter): fix line endings --- .../microbot/aiofighter/AIOFighterPlugin.java | 1228 ++++++++--------- .../aiofighter/combat/AttackNpcScript.java | 510 +++---- .../microbot/aiofighter/loot/LootScript.java | 314 ++--- 3 files changed, 1026 insertions(+), 1026 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java index 22d64d9be22..e9cae7f32e0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/AIOFighterPlugin.java @@ -1,614 +1,614 @@ -package net.runelite.client.plugins.microbot.aiofighter; - -import com.google.inject.Provides; -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.*; -import net.runelite.api.Point; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.events.*; -import net.runelite.api.widgets.ComponentID; -import net.runelite.api.widgets.Widget; -import net.runelite.api.worldmap.WorldMap; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.events.ConfigChanged; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.aiofighter.bank.BankerScript; -import net.runelite.client.plugins.microbot.aiofighter.cannon.CannonScript; -import net.runelite.client.plugins.microbot.aiofighter.combat.*; -import net.runelite.client.plugins.microbot.aiofighter.enums.PrayerStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.State; -import net.runelite.client.plugins.microbot.aiofighter.loot.LootScript; -import net.runelite.client.plugins.microbot.aiofighter.safety.SafetyScript; -import net.runelite.client.plugins.microbot.aiofighter.shop.ShopScript; -import net.runelite.client.plugins.microbot.aiofighter.skill.AttackStyleScript; -import net.runelite.client.plugins.microbot.inventorysetups.InventorySetup; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; -import net.runelite.client.plugins.microbot.util.prayer.Rs2Prayer; -import net.runelite.client.plugins.microbot.util.slayer.Rs2Slayer; -import net.runelite.client.ui.JagexColors; -import net.runelite.client.ui.overlay.OverlayManager; -import net.runelite.client.util.ColorUtil; -import net.runelite.client.util.Text; - -import javax.inject.Inject; -import java.awt.*; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -@PluginDescriptor( - name = PluginDescriptor.Mocrosoft + "AIO Fighter", - description = "Microbot Fighter plugin", - tags = {"fight", "microbot", "misc", "combat", "playerassistant"}, - enabledByDefault = false -) -@Slf4j -public class AIOFighterPlugin extends Plugin { - public static final String version = "2.0.2 BETA"; - public static boolean needShopping = false; - private static final String SET = "Set"; - private static final String CENTER_TILE = ColorUtil.wrapWithColorTag("Center Tile", JagexColors.MENU_TARGET); - // SAFE_SPOT = "Safe Spot"; - private static final String SAFE_SPOT = ColorUtil.wrapWithColorTag("Safe Spot", JagexColors.CHAT_PRIVATE_MESSAGE_TEXT_TRANSPARENT_BACKGROUND); - private static final String ADD_TO = "Start Fighting:"; - private static final String REMOVE_FROM = "Stop Fighting:"; - private static final String WALK_HERE = "Walk here"; - private static final String ATTACK = "Attack"; - @Getter - @Setter - public static int cooldown = 0; - - @Getter @Setter - private static volatile long lastNpcKilledTime = 0; - - @Getter @Setter - private static volatile boolean waitingForLoot = false; - - /** - * Centralized method to clear wait-for-loot state - * @param reason Optional reason for clearing the state (for logging) - */ - public static void clearWaitForLoot(String reason) { - setWaitingForLoot(false); - setLastNpcKilledTime(0L); - AttackNpcScript.cachedTargetNpcIndex = -1; - if (reason != null) { - Microbot.log("Clearing wait-for-loot state: " + reason); - } - } - - private final CannonScript cannonScript = new CannonScript(); - private final AttackNpcScript attackNpc = new AttackNpcScript(); - - private final FoodScript foodScript = new FoodScript(); - private final LootScript lootScript = new LootScript(); - private final SafeSpot safeSpotScript = new SafeSpot(); - private final FlickerScript flickerScript = new FlickerScript(); - private final UseSpecialAttackScript useSpecialAttackScript = new UseSpecialAttackScript(); - private final BuryScatterScript buryScatterScript = new BuryScatterScript(); - private final AttackStyleScript attackStyleScript = new AttackStyleScript(); - private final BankerScript bankerScript = new BankerScript(); - private final PrayerScript prayerScript = new PrayerScript(); - private final HighAlchScript highAlchScript = new HighAlchScript(); - private final PotionManagerScript potionManagerScript = new PotionManagerScript(); - private final SafetyScript safetyScript = new SafetyScript(); - private final SlayerScript slayerScript = new SlayerScript(); - private final ShopScript shopScript = new ShopScript(); - private final DodgeProjectileScript dodgeScript = new DodgeProjectileScript(); - @Inject - private AIOFighterConfig config; - @Inject - private OverlayManager overlayManager; - @Inject - private AIOFighterOverlay playerAssistOverlay; - @Inject - private AIOFighterInfoOverlay playerAssistInfoOverlay; - private MenuEntry lastClick; - private Point lastMenuOpenedPoint; - private WorldPoint trueTile; - - protected ScheduledExecutorService initializerExecutor = Executors.newSingleThreadScheduledExecutor(); - - @Provides - public AIOFighterConfig provideConfig(ConfigManager configManager) { - return configManager.getConfig(AIOFighterConfig.class); - } - - @Override - protected void startUp() throws AWTException { - Microbot.pauseAllScripts.compareAndSet(true, false); - //initialize any data on startup - ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); - AtomicReference> futureRef = new AtomicReference<>(); - - ScheduledFuture future = executor.scheduleWithFixedDelay(() -> { - if (Microbot.getConfigManager() == null) { - return; - } - setState(State.IDLE); - // Reset wait for loot state on startup - setWaitingForLoot(false); - setLastNpcKilledTime(0L); - // Get the future from the reference and cancel it - ScheduledFuture scheduledFuture = futureRef.get(); - if (scheduledFuture != null) { - scheduledFuture.cancel(false); - } - // now that no other tasks run, you can shut down: - executor.shutdown(); - }, 0, 1, TimeUnit.SECONDS); - - if (overlayManager != null) { - overlayManager.add(playerAssistOverlay); - overlayManager.add(playerAssistInfoOverlay); - playerAssistInfoOverlay.myButton.hookMouseListener(); - playerAssistInfoOverlay.blacklistButton.hookMouseListener(); - } - if (!config.toggleCenterTile() && Microbot.isLoggedIn() && !config.slayerMode()) - setCenter(Rs2Player.getWorldLocation()); - dodgeScript.run(config); - lootScript.run(config); - cannonScript.run(config); - attackNpc.run(config); - foodScript.run(config); - safeSpotScript.run(config); - flickerScript.run(config); - useSpecialAttackScript.run(config); - buryScatterScript.run(config); - attackStyleScript.run(config); - prayerScript.run(config); - highAlchScript.run(config); - potionManagerScript.run(config); - safetyScript.run(config); - slayerScript.run(config); - - // Configure special attack settings - if (config.useSpecialAttack() && config.specWeapon() != null) { - Microbot.getSpecialAttackConfigs() - .setSpecialAttack(true) - .setSpecialAttackWeapon(config.specWeapon()) - .setMinimumSpecEnergy(config.specWeapon().getEnergyRequired()); - } else { - Microbot.getSpecialAttackConfigs() - .setSpecialAttack(config.useSpecialAttack()); - } - - Rs2Slayer.blacklistedSlayerMonsters = getBlacklistedSlayerNpcs(); - bankerScript.run(config); - shopScript.run(config); - } - - protected void shutDown() { - // Reset wait for loot state on shutdown - setWaitingForLoot(false); - setLastNpcKilledTime(0L); - - highAlchScript.shutdown(); - lootScript.shutdown(); - cannonScript.shutdown(); - attackNpc.shutdown(); - dodgeScript.shutdown(); - foodScript.shutdown(); - safeSpotScript.shutdown(); - flickerScript.shutdown(); - useSpecialAttackScript.shutdown(); - buryScatterScript.shutdown(); - attackStyleScript.shutdown(); - bankerScript.shutdown(); - prayerScript.shutdown(); - potionManagerScript.shutdown(); - safetyScript.shutdown(); - slayerScript.shutdown(); - shopScript.shutdown(); - resetLocation(); - Microbot.getSpecialAttackConfigs().reset(); - overlayManager.remove(playerAssistOverlay); - overlayManager.remove(playerAssistInfoOverlay); - playerAssistInfoOverlay.myButton.unhookMouseListener(); - playerAssistInfoOverlay.blacklistButton.unhookMouseListener(); - } - - public static void resetLocation() { - setCenter(new WorldPoint(0, 0, 0)); - setSafeSpot(new WorldPoint(0, 0, 0)); - } - - public static void setCenter(WorldPoint worldPoint) - { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "centerLocation", - worldPoint - ); - } - // set safe spot - public static void setSafeSpot(WorldPoint worldPoint) - { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "safeSpotLocation", - worldPoint - ); - - - } - // Set remainingSlayerKills - public static void setRemainingSlayerKills(int remainingSlayerKills) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "remainingSlayerKills", - remainingSlayerKills - ); - } - // Set slayerLocation - public static void setSlayerLocationName(String slayerLocation) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "slayerLocation", - slayerLocation - ); - } - // Set slayerTask - public static void setSlayerTask(String slayerTask) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "slayerTask", - slayerTask - ); - } - // Set slayerTaskWeaknessThreshold - public static void setSlayerTaskWeaknessThreshold(int slayerTaskWeaknessThreshold) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "slayerTaskWeaknessThreshold", - slayerTaskWeaknessThreshold - ); - } - // Set slayerTaskWeaknessItem - public static void setSlayerTaskWeaknessItem(String slayerTaskWeaknessItem) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "slayerTaskWeaknessItem", - slayerTaskWeaknessItem - ); - } - // Set slayerHasTaskWeakness - public static void setSlayerHasTaskWeakness(boolean slayerHasTaskWeakness) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "slayerHasTaskWeakness", - slayerHasTaskWeakness - ); - } - // Set currentInventorySetup - public static void setCurrentSlayerInventorySetup(InventorySetup currentInventorySetup) { - Microbot.log("Setting current inventory setup to: " + currentInventorySetup.getName()); - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "currentInventorySetup", - currentInventorySetup - ); - } - // Get currentInventorySetup - public static InventorySetup getCurrentSlayerInventorySetup() { - return Microbot.getConfigManager().getConfiguration( - AIOFighterConfig.GROUP, - "currentInventorySetup", - InventorySetup.class - ); - } - // Get defaultInventorySetup - public static InventorySetup getDefaultInventorySetup() { - return Microbot.getConfigManager().getConfiguration( - AIOFighterConfig.GROUP, - "defaultInventorySetup", - InventorySetup.class - ); - } - // Add NPC to blacklist blacklistedSlayerNpcs - public static void addBlacklistedSlayerNpcs(String npcName) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "blacklistedSlayerNpcs", - Microbot.getConfigManager().getConfiguration( - AIOFighterConfig.GROUP, - "blacklistedSlayerNpcs", - String.class - ) + npcName + "," - ); - } - // Get blacklistedSlayerNpcs as a list - public static List getBlacklistedSlayerNpcs() { - return Arrays.asList(Microbot.getConfigManager().getConfiguration( - AIOFighterConfig.GROUP, - "blacklistedSlayerNpcs", - String.class - ).toString().split(",")); - } - //set Inventory Setup - private void setInventorySetup(InventorySetup inventorySetup) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "inventorySetupHidden", - inventorySetup - ); - } - - - public static State getState() { - return Microbot.getConfigManager().getConfiguration( - AIOFighterConfig.GROUP, - "state", - State.class - ); - } - - public static void setState(State state) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "state", - state - ); - } - public static String getNpcAttackList() { - return Microbot.getConfigManager().getConfiguration( - AIOFighterConfig.GROUP, - "monster" - ); - } - public static void addNpcToList(String npcName) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "monster", - getNpcAttackList() + npcName + "," - ); - - } - public static void removeNpcFromList(String npcName) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "monster", - Arrays.stream(getNpcAttackList().split(",")) - .filter(n -> !n.equalsIgnoreCase(npcName)) - .collect(Collectors.joining(",")) - ); - } - - // set attackable npcs - public static void setAttackableNpcs(String npcNames) { - Microbot.getConfigManager().setConfiguration( - AIOFighterConfig.GROUP, - "monster", - npcNames - ); - } - - private String getNpcNameFromMenuEntry(String menuTarget) { - return menuTarget.replaceAll("<[^>]*>|\\(.*\\)", "").trim(); - } - - @Subscribe - public void onChatMessage(ChatMessage event) { - if (event.getMessage().contains("reach that")) { - AttackNpcScript.skipNpc(); - } - } - // on setting change - @Subscribe - public void onConfigChanged(ConfigChanged event) { - - - if (event.getKey().equals("Safe Spot")) { - - if (!config.toggleSafeSpot()) { - // reset safe spot to default - setSafeSpot(new WorldPoint(0, 0, 0)); - } - } - if(event.getKey().equals("Combat")) { - if (!config.toggleCombat() && config.toggleCenterTile()) { - setCenter(new WorldPoint(0, 0, 0)); - } - if (config.toggleCombat() && !config.toggleCenterTile()) { - setCenter(Rs2Player.getWorldLocation()); - } - - } - // Handle special attack weapon config changes - if (event.getKey().equals("Use special attack") || event.getKey().equals("Spec weapon")) { - if (config.useSpecialAttack() && config.specWeapon() != null) { - Microbot.getSpecialAttackConfigs() - .setSpecialAttack(true) - .setSpecialAttackWeapon(config.specWeapon()) - .setMinimumSpecEnergy(config.specWeapon().getEnergyRequired()); - } else { - Microbot.getSpecialAttackConfigs().reset(); - } - } - } - - @Subscribe - public void onProjectileMoved(ProjectileMoved event) { - Projectile projectile = event.getProjectile(); - if (projectile.getTargetActor() == null) { - //Projectiles that have targetActor null are targeting a WorldPoint and are dodgeable. - dodgeScript.projectiles.add(event.getProjectile()); - } - } - - @Subscribe - public void onGameTick(GameTick gameTick) { - try { - //execute flicker script - if(config.togglePrayer()) - flickerScript.onGameTick(); - } catch (Exception e) { - log.info("AIO Fighter Plugin onGameTick Error: " + e.getMessage()); - } - } - - @Subscribe - public void onNpcDespawned(NpcDespawned npcDespawned) { - try { - if(config.togglePrayer()) - flickerScript.onNpcDespawned(npcDespawned); - } catch (Exception e) { - log.info("AIO Fighter Plugin onNpcDespawned Error: " + e.getMessage()); - } - } - - @Subscribe - public void onHitsplatApplied(HitsplatApplied event){ - try { - if (event.getActor() != Microbot.getClient().getLocalPlayer()) return; - final Hitsplat hitsplat = event.getHitsplat(); - - if ((hitsplat.isMine()) && event.getActor().getInteracting() instanceof NPC && config.togglePrayer() && (config.prayerStyle() == PrayerStyle.LAZY_FLICK) || (config.prayerStyle() == PrayerStyle.PERFECT_LAZY_FLICK) || (config.prayerStyle() == PrayerStyle.MIXED_LAZY_FLICK)) { - flickerScript.resetLastAttack(true); - Rs2Prayer.disableAllPrayers(); - if (config.toggleQuickPray()) - Rs2Prayer.toggleQuickPrayer(false); - - - } - } catch (Exception e) { - log.info("AIO Fighter Plugin onHitsplatApplied Error: " + e.getMessage()); - } - } - @Subscribe - public void onMenuOpened(MenuOpened event) { - lastMenuOpenedPoint = Microbot.getClient().getMouseCanvasPosition(); - trueTile = getSelectedWorldPoint(); - } - @Subscribe - private void onMenuEntryAdded(MenuEntryAdded event) { - if (Microbot.getClient().isKeyPressed(KeyCode.KC_SHIFT) && event.getOption().equals(WALK_HERE) && event.getTarget().isEmpty() && config.toggleCenterTile()) { - addMenuEntry(event, SET, CENTER_TILE, 1); - } - if (Microbot.getClient().isKeyPressed(KeyCode.KC_SHIFT) && event.getOption().equals(WALK_HERE) && event.getTarget().isEmpty()) { - addMenuEntry(event, SET, SAFE_SPOT, 1); - } - if (event.getOption().equals(ATTACK) && config.attackableNpcs().contains(getNpcNameFromMenuEntry(Text.removeTags(event.getTarget())))) { - addMenuEntry(event, REMOVE_FROM, event.getTarget(), 1); - } - if (event.getOption().equals(ATTACK) && !config.attackableNpcs().contains(getNpcNameFromMenuEntry(Text.removeTags(event.getTarget())))) { - addMenuEntry(event, ADD_TO, event.getTarget(), 1); - } - - } - - private WorldPoint getSelectedWorldPoint() { - if (Microbot.getClient().getWidget(ComponentID.WORLD_MAP_MAPVIEW) == null) { - if (Microbot.getClient().getSelectedSceneTile() != null) { - return Microbot.getClient().isInInstancedRegion() ? - WorldPoint.fromLocalInstance(Microbot.getClient(), Microbot.getClient().getSelectedSceneTile().getLocalLocation()) : - Microbot.getClient().getSelectedSceneTile().getWorldLocation(); - } - } else { - return calculateMapPoint(Microbot.getClient().isMenuOpen() ? lastMenuOpenedPoint : Microbot.getClient().getMouseCanvasPosition()); - } - return null; - } - public WorldPoint calculateMapPoint(Point point) { - WorldMap worldMap = Microbot.getClient().getWorldMap(); - float zoom = worldMap.getWorldMapZoom(); - final WorldPoint mapPoint = new WorldPoint(worldMap.getWorldMapPosition().getX(), worldMap.getWorldMapPosition().getY(), 0); - final Point middle = mapWorldPointToGraphicsPoint(mapPoint); - - if (point == null || middle == null) { - return null; - } - - final int dx = (int) ((point.getX() - middle.getX()) / zoom); - final int dy = (int) ((-(point.getY() - middle.getY())) / zoom); - - return mapPoint.dx(dx).dy(dy); - } - public Point mapWorldPointToGraphicsPoint(WorldPoint worldPoint) { - WorldMap worldMap = Microbot.getClient().getWorldMap(); - - float pixelsPerTile = worldMap.getWorldMapZoom(); - - Widget map = Microbot.getClient().getWidget(ComponentID.WORLD_MAP_MAPVIEW); - if (map != null) { - Rectangle worldMapRect = map.getBounds(); - - int widthInTiles = (int) Math.ceil(worldMapRect.getWidth() / pixelsPerTile); - int heightInTiles = (int) Math.ceil(worldMapRect.getHeight() / pixelsPerTile); - - Point worldMapPosition = worldMap.getWorldMapPosition(); - - int yTileMax = worldMapPosition.getY() - heightInTiles / 2; - int yTileOffset = (yTileMax - worldPoint.getY() - 1) * -1; - int xTileOffset = worldPoint.getX() + widthInTiles / 2 - worldMapPosition.getX(); - - int xGraphDiff = ((int) (xTileOffset * pixelsPerTile)); - int yGraphDiff = (int) (yTileOffset * pixelsPerTile); - - yGraphDiff -= (int) (pixelsPerTile - Math.ceil(pixelsPerTile / 2)); - xGraphDiff += (int) (pixelsPerTile - Math.ceil(pixelsPerTile / 2)); - - yGraphDiff = worldMapRect.height - yGraphDiff; - yGraphDiff += (int) worldMapRect.getY(); - xGraphDiff += (int) worldMapRect.getX(); - - return new Point(xGraphDiff, yGraphDiff); - } - return null; - } - private void onMenuOptionClicked(MenuEntry entry) { - - - - if (entry.getOption().equals(SET) && entry.getTarget().equals(CENTER_TILE)) { - setCenter(trueTile); - } - if (entry.getOption().equals(SET) && entry.getTarget().equals(SAFE_SPOT)) { - setSafeSpot(trueTile); - } - - - if (entry.getType() != MenuAction.WALK) { - lastClick = entry; - } - } - - - @Subscribe - private void onMenuOptionClicked(MenuOptionClicked event) - { - if (event.getMenuOption().equals(ADD_TO)) { - addNpcToList(getNpcNameFromMenuEntry(event.getMenuTarget())); - } - if (event.getMenuOption().equals(REMOVE_FROM)) { - removeNpcFromList(getNpcNameFromMenuEntry(event.getMenuTarget())); - } - } - private void addMenuEntry(MenuEntryAdded event, String option, String target, int position) { - List entries = new LinkedList<>(Arrays.asList(Microbot.getClient().getMenuEntries())); - - if (entries.stream().anyMatch(e -> e.getOption().equals(option) && e.getTarget().equals(target))) { - return; - } - - Microbot.getClient().createMenuEntry(position) - .setOption(option) - .setTarget(target) - .setParam0(event.getActionParam0()) - .setParam1(event.getActionParam1()) - .setIdentifier(event.getIdentifier()) - .setType(MenuAction.RUNELITE) - .onClick(this::onMenuOptionClicked); - } -} +package net.runelite.client.plugins.microbot.aiofighter; + +import com.google.inject.Provides; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.*; +import net.runelite.api.Point; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.*; +import net.runelite.api.widgets.ComponentID; +import net.runelite.api.widgets.Widget; +import net.runelite.api.worldmap.WorldMap; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.aiofighter.bank.BankerScript; +import net.runelite.client.plugins.microbot.aiofighter.cannon.CannonScript; +import net.runelite.client.plugins.microbot.aiofighter.combat.*; +import net.runelite.client.plugins.microbot.aiofighter.enums.PrayerStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.State; +import net.runelite.client.plugins.microbot.aiofighter.loot.LootScript; +import net.runelite.client.plugins.microbot.aiofighter.safety.SafetyScript; +import net.runelite.client.plugins.microbot.aiofighter.shop.ShopScript; +import net.runelite.client.plugins.microbot.aiofighter.skill.AttackStyleScript; +import net.runelite.client.plugins.microbot.inventorysetups.InventorySetup; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; +import net.runelite.client.plugins.microbot.util.prayer.Rs2Prayer; +import net.runelite.client.plugins.microbot.util.slayer.Rs2Slayer; +import net.runelite.client.ui.JagexColors; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.ColorUtil; +import net.runelite.client.util.Text; + +import javax.inject.Inject; +import java.awt.*; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +@PluginDescriptor( + name = PluginDescriptor.Mocrosoft + "AIO Fighter", + description = "Microbot Fighter plugin", + tags = {"fight", "microbot", "misc", "combat", "playerassistant"}, + enabledByDefault = false +) +@Slf4j +public class AIOFighterPlugin extends Plugin { + public static final String version = "2.0.2 BETA"; + public static boolean needShopping = false; + private static final String SET = "Set"; + private static final String CENTER_TILE = ColorUtil.wrapWithColorTag("Center Tile", JagexColors.MENU_TARGET); + // SAFE_SPOT = "Safe Spot"; + private static final String SAFE_SPOT = ColorUtil.wrapWithColorTag("Safe Spot", JagexColors.CHAT_PRIVATE_MESSAGE_TEXT_TRANSPARENT_BACKGROUND); + private static final String ADD_TO = "Start Fighting:"; + private static final String REMOVE_FROM = "Stop Fighting:"; + private static final String WALK_HERE = "Walk here"; + private static final String ATTACK = "Attack"; + @Getter + @Setter + public static int cooldown = 0; + + @Getter @Setter + private static volatile long lastNpcKilledTime = 0; + + @Getter @Setter + private static volatile boolean waitingForLoot = false; + + /** + * Centralized method to clear wait-for-loot state + * @param reason Optional reason for clearing the state (for logging) + */ + public static void clearWaitForLoot(String reason) { + setWaitingForLoot(false); + setLastNpcKilledTime(0L); + AttackNpcScript.cachedTargetNpcIndex = -1; + if (reason != null) { + Microbot.log("Clearing wait-for-loot state: " + reason); + } + } + + private final CannonScript cannonScript = new CannonScript(); + private final AttackNpcScript attackNpc = new AttackNpcScript(); + + private final FoodScript foodScript = new FoodScript(); + private final LootScript lootScript = new LootScript(); + private final SafeSpot safeSpotScript = new SafeSpot(); + private final FlickerScript flickerScript = new FlickerScript(); + private final UseSpecialAttackScript useSpecialAttackScript = new UseSpecialAttackScript(); + private final BuryScatterScript buryScatterScript = new BuryScatterScript(); + private final AttackStyleScript attackStyleScript = new AttackStyleScript(); + private final BankerScript bankerScript = new BankerScript(); + private final PrayerScript prayerScript = new PrayerScript(); + private final HighAlchScript highAlchScript = new HighAlchScript(); + private final PotionManagerScript potionManagerScript = new PotionManagerScript(); + private final SafetyScript safetyScript = new SafetyScript(); + private final SlayerScript slayerScript = new SlayerScript(); + private final ShopScript shopScript = new ShopScript(); + private final DodgeProjectileScript dodgeScript = new DodgeProjectileScript(); + @Inject + private AIOFighterConfig config; + @Inject + private OverlayManager overlayManager; + @Inject + private AIOFighterOverlay playerAssistOverlay; + @Inject + private AIOFighterInfoOverlay playerAssistInfoOverlay; + private MenuEntry lastClick; + private Point lastMenuOpenedPoint; + private WorldPoint trueTile; + + protected ScheduledExecutorService initializerExecutor = Executors.newSingleThreadScheduledExecutor(); + + @Provides + public AIOFighterConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(AIOFighterConfig.class); + } + + @Override + protected void startUp() throws AWTException { + Microbot.pauseAllScripts.compareAndSet(true, false); + //initialize any data on startup + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + AtomicReference> futureRef = new AtomicReference<>(); + + ScheduledFuture future = executor.scheduleWithFixedDelay(() -> { + if (Microbot.getConfigManager() == null) { + return; + } + setState(State.IDLE); + // Reset wait for loot state on startup + setWaitingForLoot(false); + setLastNpcKilledTime(0L); + // Get the future from the reference and cancel it + ScheduledFuture scheduledFuture = futureRef.get(); + if (scheduledFuture != null) { + scheduledFuture.cancel(false); + } + // now that no other tasks run, you can shut down: + executor.shutdown(); + }, 0, 1, TimeUnit.SECONDS); + + if (overlayManager != null) { + overlayManager.add(playerAssistOverlay); + overlayManager.add(playerAssistInfoOverlay); + playerAssistInfoOverlay.myButton.hookMouseListener(); + playerAssistInfoOverlay.blacklistButton.hookMouseListener(); + } + if (!config.toggleCenterTile() && Microbot.isLoggedIn() && !config.slayerMode()) + setCenter(Rs2Player.getWorldLocation()); + dodgeScript.run(config); + lootScript.run(config); + cannonScript.run(config); + attackNpc.run(config); + foodScript.run(config); + safeSpotScript.run(config); + flickerScript.run(config); + useSpecialAttackScript.run(config); + buryScatterScript.run(config); + attackStyleScript.run(config); + prayerScript.run(config); + highAlchScript.run(config); + potionManagerScript.run(config); + safetyScript.run(config); + slayerScript.run(config); + + // Configure special attack settings + if (config.useSpecialAttack() && config.specWeapon() != null) { + Microbot.getSpecialAttackConfigs() + .setSpecialAttack(true) + .setSpecialAttackWeapon(config.specWeapon()) + .setMinimumSpecEnergy(config.specWeapon().getEnergyRequired()); + } else { + Microbot.getSpecialAttackConfigs() + .setSpecialAttack(config.useSpecialAttack()); + } + + Rs2Slayer.blacklistedSlayerMonsters = getBlacklistedSlayerNpcs(); + bankerScript.run(config); + shopScript.run(config); + } + + protected void shutDown() { + // Reset wait for loot state on shutdown + setWaitingForLoot(false); + setLastNpcKilledTime(0L); + + highAlchScript.shutdown(); + lootScript.shutdown(); + cannonScript.shutdown(); + attackNpc.shutdown(); + dodgeScript.shutdown(); + foodScript.shutdown(); + safeSpotScript.shutdown(); + flickerScript.shutdown(); + useSpecialAttackScript.shutdown(); + buryScatterScript.shutdown(); + attackStyleScript.shutdown(); + bankerScript.shutdown(); + prayerScript.shutdown(); + potionManagerScript.shutdown(); + safetyScript.shutdown(); + slayerScript.shutdown(); + shopScript.shutdown(); + resetLocation(); + Microbot.getSpecialAttackConfigs().reset(); + overlayManager.remove(playerAssistOverlay); + overlayManager.remove(playerAssistInfoOverlay); + playerAssistInfoOverlay.myButton.unhookMouseListener(); + playerAssistInfoOverlay.blacklistButton.unhookMouseListener(); + } + + public static void resetLocation() { + setCenter(new WorldPoint(0, 0, 0)); + setSafeSpot(new WorldPoint(0, 0, 0)); + } + + public static void setCenter(WorldPoint worldPoint) + { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "centerLocation", + worldPoint + ); + } + // set safe spot + public static void setSafeSpot(WorldPoint worldPoint) + { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "safeSpotLocation", + worldPoint + ); + + + } + // Set remainingSlayerKills + public static void setRemainingSlayerKills(int remainingSlayerKills) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "remainingSlayerKills", + remainingSlayerKills + ); + } + // Set slayerLocation + public static void setSlayerLocationName(String slayerLocation) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "slayerLocation", + slayerLocation + ); + } + // Set slayerTask + public static void setSlayerTask(String slayerTask) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "slayerTask", + slayerTask + ); + } + // Set slayerTaskWeaknessThreshold + public static void setSlayerTaskWeaknessThreshold(int slayerTaskWeaknessThreshold) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "slayerTaskWeaknessThreshold", + slayerTaskWeaknessThreshold + ); + } + // Set slayerTaskWeaknessItem + public static void setSlayerTaskWeaknessItem(String slayerTaskWeaknessItem) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "slayerTaskWeaknessItem", + slayerTaskWeaknessItem + ); + } + // Set slayerHasTaskWeakness + public static void setSlayerHasTaskWeakness(boolean slayerHasTaskWeakness) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "slayerHasTaskWeakness", + slayerHasTaskWeakness + ); + } + // Set currentInventorySetup + public static void setCurrentSlayerInventorySetup(InventorySetup currentInventorySetup) { + Microbot.log("Setting current inventory setup to: " + currentInventorySetup.getName()); + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "currentInventorySetup", + currentInventorySetup + ); + } + // Get currentInventorySetup + public static InventorySetup getCurrentSlayerInventorySetup() { + return Microbot.getConfigManager().getConfiguration( + AIOFighterConfig.GROUP, + "currentInventorySetup", + InventorySetup.class + ); + } + // Get defaultInventorySetup + public static InventorySetup getDefaultInventorySetup() { + return Microbot.getConfigManager().getConfiguration( + AIOFighterConfig.GROUP, + "defaultInventorySetup", + InventorySetup.class + ); + } + // Add NPC to blacklist blacklistedSlayerNpcs + public static void addBlacklistedSlayerNpcs(String npcName) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "blacklistedSlayerNpcs", + Microbot.getConfigManager().getConfiguration( + AIOFighterConfig.GROUP, + "blacklistedSlayerNpcs", + String.class + ) + npcName + "," + ); + } + // Get blacklistedSlayerNpcs as a list + public static List getBlacklistedSlayerNpcs() { + return Arrays.asList(Microbot.getConfigManager().getConfiguration( + AIOFighterConfig.GROUP, + "blacklistedSlayerNpcs", + String.class + ).toString().split(",")); + } + //set Inventory Setup + private void setInventorySetup(InventorySetup inventorySetup) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "inventorySetupHidden", + inventorySetup + ); + } + + + public static State getState() { + return Microbot.getConfigManager().getConfiguration( + AIOFighterConfig.GROUP, + "state", + State.class + ); + } + + public static void setState(State state) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "state", + state + ); + } + public static String getNpcAttackList() { + return Microbot.getConfigManager().getConfiguration( + AIOFighterConfig.GROUP, + "monster" + ); + } + public static void addNpcToList(String npcName) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "monster", + getNpcAttackList() + npcName + "," + ); + + } + public static void removeNpcFromList(String npcName) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "monster", + Arrays.stream(getNpcAttackList().split(",")) + .filter(n -> !n.equalsIgnoreCase(npcName)) + .collect(Collectors.joining(",")) + ); + } + + // set attackable npcs + public static void setAttackableNpcs(String npcNames) { + Microbot.getConfigManager().setConfiguration( + AIOFighterConfig.GROUP, + "monster", + npcNames + ); + } + + private String getNpcNameFromMenuEntry(String menuTarget) { + return menuTarget.replaceAll("<[^>]*>|\\(.*\\)", "").trim(); + } + + @Subscribe + public void onChatMessage(ChatMessage event) { + if (event.getMessage().contains("reach that")) { + AttackNpcScript.skipNpc(); + } + } + // on setting change + @Subscribe + public void onConfigChanged(ConfigChanged event) { + + + if (event.getKey().equals("Safe Spot")) { + + if (!config.toggleSafeSpot()) { + // reset safe spot to default + setSafeSpot(new WorldPoint(0, 0, 0)); + } + } + if(event.getKey().equals("Combat")) { + if (!config.toggleCombat() && config.toggleCenterTile()) { + setCenter(new WorldPoint(0, 0, 0)); + } + if (config.toggleCombat() && !config.toggleCenterTile()) { + setCenter(Rs2Player.getWorldLocation()); + } + + } + // Handle special attack weapon config changes + if (event.getKey().equals("Use special attack") || event.getKey().equals("Spec weapon")) { + if (config.useSpecialAttack() && config.specWeapon() != null) { + Microbot.getSpecialAttackConfigs() + .setSpecialAttack(true) + .setSpecialAttackWeapon(config.specWeapon()) + .setMinimumSpecEnergy(config.specWeapon().getEnergyRequired()); + } else { + Microbot.getSpecialAttackConfigs().reset(); + } + } + } + + @Subscribe + public void onProjectileMoved(ProjectileMoved event) { + Projectile projectile = event.getProjectile(); + if (projectile.getTargetActor() == null) { + //Projectiles that have targetActor null are targeting a WorldPoint and are dodgeable. + dodgeScript.projectiles.add(event.getProjectile()); + } + } + + @Subscribe + public void onGameTick(GameTick gameTick) { + try { + //execute flicker script + if(config.togglePrayer()) + flickerScript.onGameTick(); + } catch (Exception e) { + log.info("AIO Fighter Plugin onGameTick Error: " + e.getMessage()); + } + } + + @Subscribe + public void onNpcDespawned(NpcDespawned npcDespawned) { + try { + if(config.togglePrayer()) + flickerScript.onNpcDespawned(npcDespawned); + } catch (Exception e) { + log.info("AIO Fighter Plugin onNpcDespawned Error: " + e.getMessage()); + } + } + + @Subscribe + public void onHitsplatApplied(HitsplatApplied event){ + try { + if (event.getActor() != Microbot.getClient().getLocalPlayer()) return; + final Hitsplat hitsplat = event.getHitsplat(); + + if ((hitsplat.isMine()) && event.getActor().getInteracting() instanceof NPC && config.togglePrayer() && (config.prayerStyle() == PrayerStyle.LAZY_FLICK) || (config.prayerStyle() == PrayerStyle.PERFECT_LAZY_FLICK) || (config.prayerStyle() == PrayerStyle.MIXED_LAZY_FLICK)) { + flickerScript.resetLastAttack(true); + Rs2Prayer.disableAllPrayers(); + if (config.toggleQuickPray()) + Rs2Prayer.toggleQuickPrayer(false); + + + } + } catch (Exception e) { + log.info("AIO Fighter Plugin onHitsplatApplied Error: " + e.getMessage()); + } + } + @Subscribe + public void onMenuOpened(MenuOpened event) { + lastMenuOpenedPoint = Microbot.getClient().getMouseCanvasPosition(); + trueTile = getSelectedWorldPoint(); + } + @Subscribe + private void onMenuEntryAdded(MenuEntryAdded event) { + if (Microbot.getClient().isKeyPressed(KeyCode.KC_SHIFT) && event.getOption().equals(WALK_HERE) && event.getTarget().isEmpty() && config.toggleCenterTile()) { + addMenuEntry(event, SET, CENTER_TILE, 1); + } + if (Microbot.getClient().isKeyPressed(KeyCode.KC_SHIFT) && event.getOption().equals(WALK_HERE) && event.getTarget().isEmpty()) { + addMenuEntry(event, SET, SAFE_SPOT, 1); + } + if (event.getOption().equals(ATTACK) && config.attackableNpcs().contains(getNpcNameFromMenuEntry(Text.removeTags(event.getTarget())))) { + addMenuEntry(event, REMOVE_FROM, event.getTarget(), 1); + } + if (event.getOption().equals(ATTACK) && !config.attackableNpcs().contains(getNpcNameFromMenuEntry(Text.removeTags(event.getTarget())))) { + addMenuEntry(event, ADD_TO, event.getTarget(), 1); + } + + } + + private WorldPoint getSelectedWorldPoint() { + if (Microbot.getClient().getWidget(ComponentID.WORLD_MAP_MAPVIEW) == null) { + if (Microbot.getClient().getSelectedSceneTile() != null) { + return Microbot.getClient().isInInstancedRegion() ? + WorldPoint.fromLocalInstance(Microbot.getClient(), Microbot.getClient().getSelectedSceneTile().getLocalLocation()) : + Microbot.getClient().getSelectedSceneTile().getWorldLocation(); + } + } else { + return calculateMapPoint(Microbot.getClient().isMenuOpen() ? lastMenuOpenedPoint : Microbot.getClient().getMouseCanvasPosition()); + } + return null; + } + public WorldPoint calculateMapPoint(Point point) { + WorldMap worldMap = Microbot.getClient().getWorldMap(); + float zoom = worldMap.getWorldMapZoom(); + final WorldPoint mapPoint = new WorldPoint(worldMap.getWorldMapPosition().getX(), worldMap.getWorldMapPosition().getY(), 0); + final Point middle = mapWorldPointToGraphicsPoint(mapPoint); + + if (point == null || middle == null) { + return null; + } + + final int dx = (int) ((point.getX() - middle.getX()) / zoom); + final int dy = (int) ((-(point.getY() - middle.getY())) / zoom); + + return mapPoint.dx(dx).dy(dy); + } + public Point mapWorldPointToGraphicsPoint(WorldPoint worldPoint) { + WorldMap worldMap = Microbot.getClient().getWorldMap(); + + float pixelsPerTile = worldMap.getWorldMapZoom(); + + Widget map = Microbot.getClient().getWidget(ComponentID.WORLD_MAP_MAPVIEW); + if (map != null) { + Rectangle worldMapRect = map.getBounds(); + + int widthInTiles = (int) Math.ceil(worldMapRect.getWidth() / pixelsPerTile); + int heightInTiles = (int) Math.ceil(worldMapRect.getHeight() / pixelsPerTile); + + Point worldMapPosition = worldMap.getWorldMapPosition(); + + int yTileMax = worldMapPosition.getY() - heightInTiles / 2; + int yTileOffset = (yTileMax - worldPoint.getY() - 1) * -1; + int xTileOffset = worldPoint.getX() + widthInTiles / 2 - worldMapPosition.getX(); + + int xGraphDiff = ((int) (xTileOffset * pixelsPerTile)); + int yGraphDiff = (int) (yTileOffset * pixelsPerTile); + + yGraphDiff -= (int) (pixelsPerTile - Math.ceil(pixelsPerTile / 2)); + xGraphDiff += (int) (pixelsPerTile - Math.ceil(pixelsPerTile / 2)); + + yGraphDiff = worldMapRect.height - yGraphDiff; + yGraphDiff += (int) worldMapRect.getY(); + xGraphDiff += (int) worldMapRect.getX(); + + return new Point(xGraphDiff, yGraphDiff); + } + return null; + } + private void onMenuOptionClicked(MenuEntry entry) { + + + + if (entry.getOption().equals(SET) && entry.getTarget().equals(CENTER_TILE)) { + setCenter(trueTile); + } + if (entry.getOption().equals(SET) && entry.getTarget().equals(SAFE_SPOT)) { + setSafeSpot(trueTile); + } + + + if (entry.getType() != MenuAction.WALK) { + lastClick = entry; + } + } + + + @Subscribe + private void onMenuOptionClicked(MenuOptionClicked event) + { + if (event.getMenuOption().equals(ADD_TO)) { + addNpcToList(getNpcNameFromMenuEntry(event.getMenuTarget())); + } + if (event.getMenuOption().equals(REMOVE_FROM)) { + removeNpcFromList(getNpcNameFromMenuEntry(event.getMenuTarget())); + } + } + private void addMenuEntry(MenuEntryAdded event, String option, String target, int position) { + List entries = new LinkedList<>(Arrays.asList(Microbot.getClient().getMenuEntries())); + + if (entries.stream().anyMatch(e -> e.getOption().equals(option) && e.getTarget().equals(target))) { + return; + } + + Microbot.getClient().createMenuEntry(position) + .setOption(option) + .setTarget(target) + .setParam0(event.getActionParam0()) + .setParam1(event.getActionParam1()) + .setIdentifier(event.getIdentifier()) + .setType(MenuAction.RUNELITE) + .onClick(this::onMenuOptionClicked); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java index 78c8601b571..5981e1da9cc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/combat/AttackNpcScript.java @@ -1,255 +1,255 @@ -package net.runelite.client.plugins.microbot.aiofighter.combat; - -import lombok.SneakyThrows; -import net.runelite.api.Actor; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.gameval.ItemID; -import net.runelite.api.gameval.VarPlayerID; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.Script; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; -import net.runelite.client.plugins.microbot.aiofighter.enums.AttackStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.AttackStyleMapper; -import net.runelite.client.plugins.microbot.aiofighter.enums.State; -import net.runelite.client.plugins.microbot.shortestpath.ShortestPathPlugin; -import net.runelite.client.plugins.microbot.util.ActorModel; -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.ActivityIntensity; -import net.runelite.client.plugins.microbot.util.camera.Rs2Camera; -import net.runelite.client.plugins.microbot.util.coords.Rs2WorldArea; -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.Rs2NpcManager; -import net.runelite.client.plugins.microbot.util.npc.Rs2NpcModel; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; -import net.runelite.client.plugins.microbot.util.prayer.Rs2Prayer; -import net.runelite.client.plugins.microbot.util.prayer.Rs2PrayerEnum; -import net.runelite.client.plugins.microbot.util.slayer.Rs2Slayer; -import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; -import org.slf4j.event.Level; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -import static net.runelite.api.gameval.VarbitID.*; - -public class AttackNpcScript extends Script { - - public static Actor currentNpc = null; - public static AtomicReference> filteredAttackableNpcs = new AtomicReference<>(new ArrayList<>()); - public static Rs2WorldArea attackableArea = null; - public static volatile int cachedTargetNpcIndex = -1; - private boolean messageShown = false; - private int noNpcCount = 0; - - public static void skipNpc() { - currentNpc = null; - } - - @SneakyThrows - public void run(AIOFighterConfig config) { - try { - Rs2NpcManager.loadJson(); - Rs2Antiban.resetAntibanSettings(); - Rs2Antiban.antibanSetupTemplates.applyCombatSetup(); - Rs2Antiban.setActivityIntensity(ActivityIntensity.EXTREME); - } catch (Exception e) { - throw new RuntimeException(e); - } - - mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { - try { - if (!Microbot.isLoggedIn() || !super.run() || !config.toggleCombat()) - return; - - if (config.centerLocation().distanceTo(Rs2Player.getWorldLocation()) < config.attackRadius() && - !config.centerLocation().equals(new WorldPoint(0, 0, 0)) && AIOFighterPlugin.getState() != State.BANKING) { - if (ShortestPathPlugin.getPathfinder() != null) - Rs2Walker.setTarget(null); - AIOFighterPlugin.setState(State.IDLE); - } - - attackableArea = new Rs2WorldArea(config.centerLocation().toWorldArea()); - attackableArea = attackableArea.offset(config.attackRadius()); - List npcsToAttack = Arrays.stream(config.attackableNpcs().split(",")) - .map(x -> x.trim().toLowerCase()) - .collect(Collectors.toList()); - filteredAttackableNpcs.set( - Rs2Npc.getAttackableNpcs(config.attackReachableNpcs()) - .filter(npc -> npc.getWorldLocation().distanceTo(config.centerLocation()) <= config.attackRadius()) - .filter(npc -> npc.getName() != null && !npcsToAttack.isEmpty() && npcsToAttack.stream().anyMatch(npc.getName()::equalsIgnoreCase)) - .sorted(Comparator.comparingInt((Rs2NpcModel npc) -> npc.getInteracting() == Microbot.getClient().getLocalPlayer() ? 0 : 1) - .thenComparingInt(npc -> Rs2Player.getRs2WorldPoint().distanceToPath(npc.getWorldLocation()))) - .collect(Collectors.toList()) - ); - final List attackableNpcs = new ArrayList<>(); - - for (var attackableNpc : filteredAttackableNpcs.get()) { - if (attackableNpc == null || attackableNpc.getName() == null) continue; - for (var npcToAttack : npcsToAttack) { - if (npcToAttack.equalsIgnoreCase(attackableNpc.getName())) { - attackableNpcs.add(attackableNpc); - } - } - } - filteredAttackableNpcs.set(attackableNpcs); - - if (config.state().equals(State.BANKING) || config.state().equals(State.WALKING)) - return; - - // Check if we should pause while looting is happening - if (Microbot.pauseAllScripts.get()) { - return; // Don't attack while looting - } - - // Check if we need to update our cached target (but not while waiting for loot) - if (!AIOFighterPlugin.isWaitingForLoot()) { - Actor 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(); - } - } - } - - // Check if our cached target died - if (config.toggleWaitForLoot() && !AIOFighterPlugin.isWaitingForLoot() && cachedTargetNpcIndex != -1) { - // Find the NPC by index using Rs2 API - Rs2NpcModel cachedNpcModel = Rs2Npc.getNpcByIndex(cachedTargetNpcIndex); - - if (cachedNpcModel != null && (cachedNpcModel.isDead() || (cachedNpcModel.getHealthRatio() == 0 && cachedNpcModel.getHealthScale() > 0))) { - AIOFighterPlugin.setWaitingForLoot(true); - AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis()); - Microbot.status = "Waiting for loot..."; - Microbot.log("NPC died, waiting for loot..."); - cachedTargetNpcIndex = -1; - return; - } - } - - // Check if we're waiting for loot - if (config.toggleWaitForLoot() && AIOFighterPlugin.isWaitingForLoot()) { - long timeSinceKill = System.currentTimeMillis() - AIOFighterPlugin.getLastNpcKilledTime(); - int timeoutMs = config.lootWaitTimeout() * 1000; - if (timeSinceKill >= timeoutMs) { - // Timeout reached, resume combat - AIOFighterPlugin.clearWaitForLoot("Loot wait timeout reached, resuming combat"); - cachedTargetNpcIndex = -1; // Clear cached NPC on timeout - } else { - // Still waiting for loot, don't attack - int secondsLeft = (int) Math.max(1, TimeUnit.MILLISECONDS.toSeconds(timeoutMs - timeSinceKill)); - Microbot.status = "Waiting for loot... " + secondsLeft + "s"; - return; - } - } - - if (config.toggleCenterTile() && config.centerLocation().getX() == 0 - && config.centerLocation().getY() == 0) { - if (!messageShown) { - Microbot.showMessage("Please set a center location"); - messageShown = true; - } - return; - } - messageShown = false; - - - if (Rs2AntibanSettings.actionCooldownActive) { - AIOFighterPlugin.setState(State.COMBAT); - handleItemOnNpcToKill(config); - return; - } - - if (!attackableNpcs.isEmpty()) { - noNpcCount = 0; - - Rs2NpcModel npc = attackableNpcs.stream().findFirst().orElse(null); - - if (!Rs2Camera.isTileOnScreen(npc.getLocalLocation())) - Rs2Camera.turnTo(npc); - - Rs2Npc.interact(npc, "attack"); - Microbot.status = "Attacking " + npc.getName(); - Rs2Antiban.actionCooldown(); - //sleepUntil(Rs2Player::isInteracting, 1000); - - if (config.togglePrayer()) { - if (!config.toggleQuickPray()) { - AttackStyle attackStyle = AttackStyleMapper - .mapToAttackStyle(Rs2NpcManager.getAttackStyle(npc.getId())); - if (attackStyle != null) { - switch (attackStyle) { - case MAGE: - Rs2Prayer.toggle(Rs2PrayerEnum.PROTECT_MAGIC, true); - break; - case MELEE: - Rs2Prayer.toggle(Rs2PrayerEnum.PROTECT_MELEE, true); - break; - case RANGED: - Rs2Prayer.toggle(Rs2PrayerEnum.PROTECT_RANGE, true); - break; - } - } - } else { - Rs2Prayer.toggleQuickPrayer(true); - } - } - - - } else { - if (Rs2Player.getWorldLocation().isInArea(attackableArea)) { - Microbot.log(Level.INFO, "No attackable NPC found"); - noNpcCount++; - if (noNpcCount > 60 && config.slayerMode()) { - Microbot.log(Level.INFO, "No attackable NPC found for 60 ticks, resetting slayer task"); - AIOFighterPlugin.addBlacklistedSlayerNpcs(Rs2Slayer.slayerTaskMonsterTarget); - noNpcCount = 0; - SlayerScript.reset(); - } - } else { - Rs2Walker.walkTo(config.centerLocation(), 0); - AIOFighterPlugin.setState(State.WALKING); - } - - } - } catch (Exception ex) { - Microbot.logStackTrace(this.getClass().getSimpleName(), ex); - } - }, 0, 600, TimeUnit.MILLISECONDS); - } - - - /** - * item on npcs that need to kill like rockslug - */ - private void handleItemOnNpcToKill(AIOFighterConfig config) { - Rs2NpcModel npc = Rs2Npc.getNpcsForPlayer(ActorModel::isDead).findFirst().orElse(null); - List lizardVariants = new ArrayList<>(Arrays.asList("Lizard", "Desert Lizard", "Small Lizard")); - if (npc == null) return; - if (Microbot.getVarbitValue(SLAYER_AUTOKILL_DESERTLIZARDS) == 0 && lizardVariants.contains(npc.getName()) && npc.getHealthRatio() < 5) { - Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ICY_WATER, npc); - Rs2Player.waitForAnimation(); - } else if (Microbot.getVarbitValue(SLAYER_AUTOKILL_ROCKSLUGS) == 0 && npc.getName().equalsIgnoreCase("rockslug") && npc.getHealthRatio() < 5) { - Rs2Inventory.useItemOnNpc(ItemID.SLAYER_BAG_OF_SALT, npc); - Rs2Player.waitForAnimation(); - } else if (Microbot.getVarbitValue(SLAYER_AUTOKILL_GARGOYLES) == 0 && npc.getName().equalsIgnoreCase("gargoyle") && npc.getHealthRatio() < 3) { - Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ROCK_HAMMER, npc); - Rs2Player.waitForAnimation(); - } - } - - - @Override - public void shutdown() { - super.shutdown(); - } -} +package net.runelite.client.plugins.microbot.aiofighter.combat; + +import lombok.SneakyThrows; +import net.runelite.api.Actor; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.gameval.ItemID; +import net.runelite.api.gameval.VarPlayerID; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.Script; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; +import net.runelite.client.plugins.microbot.aiofighter.enums.AttackStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.AttackStyleMapper; +import net.runelite.client.plugins.microbot.aiofighter.enums.State; +import net.runelite.client.plugins.microbot.shortestpath.ShortestPathPlugin; +import net.runelite.client.plugins.microbot.util.ActorModel; +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.ActivityIntensity; +import net.runelite.client.plugins.microbot.util.camera.Rs2Camera; +import net.runelite.client.plugins.microbot.util.coords.Rs2WorldArea; +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.Rs2NpcManager; +import net.runelite.client.plugins.microbot.util.npc.Rs2NpcModel; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; +import net.runelite.client.plugins.microbot.util.prayer.Rs2Prayer; +import net.runelite.client.plugins.microbot.util.prayer.Rs2PrayerEnum; +import net.runelite.client.plugins.microbot.util.slayer.Rs2Slayer; +import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; +import org.slf4j.event.Level; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +import static net.runelite.api.gameval.VarbitID.*; + +public class AttackNpcScript extends Script { + + public static Actor currentNpc = null; + public static AtomicReference> filteredAttackableNpcs = new AtomicReference<>(new ArrayList<>()); + public static Rs2WorldArea attackableArea = null; + public static volatile int cachedTargetNpcIndex = -1; + private boolean messageShown = false; + private int noNpcCount = 0; + + public static void skipNpc() { + currentNpc = null; + } + + @SneakyThrows + public void run(AIOFighterConfig config) { + try { + Rs2NpcManager.loadJson(); + Rs2Antiban.resetAntibanSettings(); + Rs2Antiban.antibanSetupTemplates.applyCombatSetup(); + Rs2Antiban.setActivityIntensity(ActivityIntensity.EXTREME); + } catch (Exception e) { + throw new RuntimeException(e); + } + + mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { + try { + if (!Microbot.isLoggedIn() || !super.run() || !config.toggleCombat()) + return; + + if (config.centerLocation().distanceTo(Rs2Player.getWorldLocation()) < config.attackRadius() && + !config.centerLocation().equals(new WorldPoint(0, 0, 0)) && AIOFighterPlugin.getState() != State.BANKING) { + if (ShortestPathPlugin.getPathfinder() != null) + Rs2Walker.setTarget(null); + AIOFighterPlugin.setState(State.IDLE); + } + + attackableArea = new Rs2WorldArea(config.centerLocation().toWorldArea()); + attackableArea = attackableArea.offset(config.attackRadius()); + List npcsToAttack = Arrays.stream(config.attackableNpcs().split(",")) + .map(x -> x.trim().toLowerCase()) + .collect(Collectors.toList()); + filteredAttackableNpcs.set( + Rs2Npc.getAttackableNpcs(config.attackReachableNpcs()) + .filter(npc -> npc.getWorldLocation().distanceTo(config.centerLocation()) <= config.attackRadius()) + .filter(npc -> npc.getName() != null && !npcsToAttack.isEmpty() && npcsToAttack.stream().anyMatch(npc.getName()::equalsIgnoreCase)) + .sorted(Comparator.comparingInt((Rs2NpcModel npc) -> npc.getInteracting() == Microbot.getClient().getLocalPlayer() ? 0 : 1) + .thenComparingInt(npc -> Rs2Player.getRs2WorldPoint().distanceToPath(npc.getWorldLocation()))) + .collect(Collectors.toList()) + ); + final List attackableNpcs = new ArrayList<>(); + + for (var attackableNpc : filteredAttackableNpcs.get()) { + if (attackableNpc == null || attackableNpc.getName() == null) continue; + for (var npcToAttack : npcsToAttack) { + if (npcToAttack.equalsIgnoreCase(attackableNpc.getName())) { + attackableNpcs.add(attackableNpc); + } + } + } + filteredAttackableNpcs.set(attackableNpcs); + + if (config.state().equals(State.BANKING) || config.state().equals(State.WALKING)) + return; + + // Check if we should pause while looting is happening + if (Microbot.pauseAllScripts.get()) { + return; // Don't attack while looting + } + + // Check if we need to update our cached target (but not while waiting for loot) + if (!AIOFighterPlugin.isWaitingForLoot()) { + Actor 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(); + } + } + } + + // Check if our cached target died + if (config.toggleWaitForLoot() && !AIOFighterPlugin.isWaitingForLoot() && cachedTargetNpcIndex != -1) { + // Find the NPC by index using Rs2 API + Rs2NpcModel cachedNpcModel = Rs2Npc.getNpcByIndex(cachedTargetNpcIndex); + + if (cachedNpcModel != null && (cachedNpcModel.isDead() || (cachedNpcModel.getHealthRatio() == 0 && cachedNpcModel.getHealthScale() > 0))) { + AIOFighterPlugin.setWaitingForLoot(true); + AIOFighterPlugin.setLastNpcKilledTime(System.currentTimeMillis()); + Microbot.status = "Waiting for loot..."; + Microbot.log("NPC died, waiting for loot..."); + cachedTargetNpcIndex = -1; + return; + } + } + + // Check if we're waiting for loot + if (config.toggleWaitForLoot() && AIOFighterPlugin.isWaitingForLoot()) { + long timeSinceKill = System.currentTimeMillis() - AIOFighterPlugin.getLastNpcKilledTime(); + int timeoutMs = config.lootWaitTimeout() * 1000; + if (timeSinceKill >= timeoutMs) { + // Timeout reached, resume combat + AIOFighterPlugin.clearWaitForLoot("Loot wait timeout reached, resuming combat"); + cachedTargetNpcIndex = -1; // Clear cached NPC on timeout + } else { + // Still waiting for loot, don't attack + int secondsLeft = (int) Math.max(1, TimeUnit.MILLISECONDS.toSeconds(timeoutMs - timeSinceKill)); + Microbot.status = "Waiting for loot... " + secondsLeft + "s"; + return; + } + } + + if (config.toggleCenterTile() && config.centerLocation().getX() == 0 + && config.centerLocation().getY() == 0) { + if (!messageShown) { + Microbot.showMessage("Please set a center location"); + messageShown = true; + } + return; + } + messageShown = false; + + + if (Rs2AntibanSettings.actionCooldownActive) { + AIOFighterPlugin.setState(State.COMBAT); + handleItemOnNpcToKill(config); + return; + } + + if (!attackableNpcs.isEmpty()) { + noNpcCount = 0; + + Rs2NpcModel npc = attackableNpcs.stream().findFirst().orElse(null); + + if (!Rs2Camera.isTileOnScreen(npc.getLocalLocation())) + Rs2Camera.turnTo(npc); + + Rs2Npc.interact(npc, "attack"); + Microbot.status = "Attacking " + npc.getName(); + Rs2Antiban.actionCooldown(); + //sleepUntil(Rs2Player::isInteracting, 1000); + + if (config.togglePrayer()) { + if (!config.toggleQuickPray()) { + AttackStyle attackStyle = AttackStyleMapper + .mapToAttackStyle(Rs2NpcManager.getAttackStyle(npc.getId())); + if (attackStyle != null) { + switch (attackStyle) { + case MAGE: + Rs2Prayer.toggle(Rs2PrayerEnum.PROTECT_MAGIC, true); + break; + case MELEE: + Rs2Prayer.toggle(Rs2PrayerEnum.PROTECT_MELEE, true); + break; + case RANGED: + Rs2Prayer.toggle(Rs2PrayerEnum.PROTECT_RANGE, true); + break; + } + } + } else { + Rs2Prayer.toggleQuickPrayer(true); + } + } + + + } else { + if (Rs2Player.getWorldLocation().isInArea(attackableArea)) { + Microbot.log(Level.INFO, "No attackable NPC found"); + noNpcCount++; + if (noNpcCount > 60 && config.slayerMode()) { + Microbot.log(Level.INFO, "No attackable NPC found for 60 ticks, resetting slayer task"); + AIOFighterPlugin.addBlacklistedSlayerNpcs(Rs2Slayer.slayerTaskMonsterTarget); + noNpcCount = 0; + SlayerScript.reset(); + } + } else { + Rs2Walker.walkTo(config.centerLocation(), 0); + AIOFighterPlugin.setState(State.WALKING); + } + + } + } catch (Exception ex) { + Microbot.logStackTrace(this.getClass().getSimpleName(), ex); + } + }, 0, 600, TimeUnit.MILLISECONDS); + } + + + /** + * item on npcs that need to kill like rockslug + */ + private void handleItemOnNpcToKill(AIOFighterConfig config) { + Rs2NpcModel npc = Rs2Npc.getNpcsForPlayer(ActorModel::isDead).findFirst().orElse(null); + List lizardVariants = new ArrayList<>(Arrays.asList("Lizard", "Desert Lizard", "Small Lizard")); + if (npc == null) return; + if (Microbot.getVarbitValue(SLAYER_AUTOKILL_DESERTLIZARDS) == 0 && lizardVariants.contains(npc.getName()) && npc.getHealthRatio() < 5) { + Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ICY_WATER, npc); + Rs2Player.waitForAnimation(); + } else if (Microbot.getVarbitValue(SLAYER_AUTOKILL_ROCKSLUGS) == 0 && npc.getName().equalsIgnoreCase("rockslug") && npc.getHealthRatio() < 5) { + Rs2Inventory.useItemOnNpc(ItemID.SLAYER_BAG_OF_SALT, npc); + Rs2Player.waitForAnimation(); + } else if (Microbot.getVarbitValue(SLAYER_AUTOKILL_GARGOYLES) == 0 && npc.getName().equalsIgnoreCase("gargoyle") && npc.getHealthRatio() < 3) { + Rs2Inventory.useItemOnNpc(ItemID.SLAYER_ROCK_HAMMER, npc); + Rs2Player.waitForAnimation(); + } + } + + + @Override + public void shutdown() { + super.shutdown(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java index d094f22e62f..5d5c00d8e09 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/aiofighter/loot/LootScript.java @@ -1,157 +1,157 @@ -package net.runelite.client.plugins.microbot.aiofighter.loot; - -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.plugins.grounditems.GroundItem; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.Script; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; -import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; -import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; -import net.runelite.client.plugins.microbot.aiofighter.enums.State; -import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; -import net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem; -import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; -import net.runelite.client.plugins.microbot.util.inventory.Rs2RunePouch; -import net.runelite.client.plugins.microbot.util.magic.Runes; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; - -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -import static net.runelite.api.TileItem.OWNERSHIP_SELF; -import static net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem.*; - -@Slf4j -public class LootScript extends Script { - int minFreeSlots = 0; - - public boolean run(AIOFighterConfig config) { - mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { - try { - minFreeSlots = config.bank() ? config.minFreeSlots() : 0; - if (!super.run()) return; - if (!Microbot.isLoggedIn()) return; - if (!config.toggleLootItems()) return; - if (Rs2AntibanSettings.actionCooldownActive) return; - if (AIOFighterPlugin.getState().equals(State.BANKING) || AIOFighterPlugin.getState().equals(State.WALKING)) { - return; - } - if (Rs2Player.isInCombat() && !config.toggleForceLoot() && !AIOFighterPlugin.isWaitingForLoot()) { - return; - } - - - String[] itemNamesToLoot = lootItemNames(config); - final Predicate filter = groundItem -> - groundItem.getLocation().distanceTo(Microbot.getClient().getLocalPlayer().getWorldLocation()) < config.attackRadius() && - (!config.toggleOnlyLootMyItems() || groundItem.getOwnership() == OWNERSHIP_SELF) && - (shouldLootBasedOnName(groundItem, itemNamesToLoot) || shouldLootBasedOnValue(groundItem, config)); - List groundItems = getGroundItems().values().stream() - .filter(filter) - .collect(Collectors.toList()); - - if (groundItems.isEmpty()) { - return; - } - if (config.toggleDelayedLooting()) { - groundItems.sort(Comparator.comparingInt(Rs2GroundItem::calculateDespawnTime)); - } - // Defer clearing wait-for-loot until we successfully pick at least one item - //Pause other scripts before looting and always release - boolean previousPauseState = Microbot.pauseAllScripts.getAndSet(true); - try { - 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, 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; - } - } - Microbot.log("Picking up loot: " + groundItem.getName()); - if (!waitForGroundItemDespawn(() -> interact(groundItem), groundItem)) { - // Skip this item and continue to the next rather than aborting the whole pass - continue; - } - // Clear wait state after first successful pickup - if (!clearedWait && AIOFighterPlugin.isWaitingForLoot()) { - AIOFighterPlugin.clearWaitForLoot("First loot item picked up"); - clearedWait = true; - } - } - Microbot.log("Looting complete"); - } finally { - Microbot.pauseAllScripts.set(previousPauseState); - } - } catch (Exception ex) { - Microbot.log("Looterscript: " + ex.getMessage()); - } - - }, 0, 200, TimeUnit.MILLISECONDS); - return true; - } - - private boolean canStackItem(GroundItem groundItem) { - if (!groundItem.isStackable()) { - return false; - } - int runePouchRunes = Rs2RunePouch.getQuantity(groundItem.getItemId()); - if (runePouchRunes > 0 && runePouchRunes <= 16000 - groundItem.getQuantity()) { - return true; - } - //TODO("Coal bag, Herb Sack, Seed pack") - return Rs2Inventory.contains(groundItem.getItemId()); - } - - private boolean shouldLootBasedOnValue(GroundItem groundItem, AIOFighterConfig config) { - if (config.looterStyle() != DefaultLooterStyle.GE_PRICE_RANGE && config.looterStyle() != DefaultLooterStyle.MIXED) - return false; - int price = groundItem.getGePrice(); - return config.minPriceOfItemsToLoot() <= price && price / groundItem.getQuantity() <= config.maxPriceOfItemsToLoot(); - } - - private boolean shouldLootBasedOnName(GroundItem groundItem, String[] itemNamesToLoot) { - return Arrays.stream(itemNamesToLoot).anyMatch(name -> groundItem.getName().trim().toLowerCase().contains(name.trim().toLowerCase())); - } - - private String[] lootItemNames(AIOFighterConfig config) { - ArrayList itemNamesToLoot = new ArrayList<>(); - if (config.toggleLootArrows()) { - itemNamesToLoot.add("arrow"); - } - if (config.toggleBuryBones()) { - itemNamesToLoot.add("bones"); - } - if (config.toggleScatter()) { - itemNamesToLoot.add(" ashes"); - } - if (config.toggleLootRunes()) { - itemNamesToLoot.add(" rune"); - } - if (config.toggleLootCoins()) { - itemNamesToLoot.add("coins"); - } - if (config.toggleLootUntradables()) { - itemNamesToLoot.add("untradeable"); - itemNamesToLoot.add("scroll box"); - } - if (config.looterStyle().equals(DefaultLooterStyle.MIXED) || config.looterStyle().equals(DefaultLooterStyle.ITEM_LIST)) { - itemNamesToLoot.addAll(Arrays.asList(config.listOfItemsToLoot().trim().split(","))); - } - return itemNamesToLoot.toArray(new String[0]); - } - - public void shutdown() { - super.shutdown(); - } -} +package net.runelite.client.plugins.microbot.aiofighter.loot; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.grounditems.GroundItem; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.Script; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterConfig; +import net.runelite.client.plugins.microbot.aiofighter.AIOFighterPlugin; +import net.runelite.client.plugins.microbot.aiofighter.enums.DefaultLooterStyle; +import net.runelite.client.plugins.microbot.aiofighter.enums.State; +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem; +import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; +import net.runelite.client.plugins.microbot.util.inventory.Rs2RunePouch; +import net.runelite.client.plugins.microbot.util.magic.Runes; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; + +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static net.runelite.api.TileItem.OWNERSHIP_SELF; +import static net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem.*; + +@Slf4j +public class LootScript extends Script { + int minFreeSlots = 0; + + public boolean run(AIOFighterConfig config) { + mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { + try { + minFreeSlots = config.bank() ? config.minFreeSlots() : 0; + if (!super.run()) return; + if (!Microbot.isLoggedIn()) return; + if (!config.toggleLootItems()) return; + if (Rs2AntibanSettings.actionCooldownActive) return; + if (AIOFighterPlugin.getState().equals(State.BANKING) || AIOFighterPlugin.getState().equals(State.WALKING)) { + return; + } + if (Rs2Player.isInCombat() && !config.toggleForceLoot() && !AIOFighterPlugin.isWaitingForLoot()) { + return; + } + + + String[] itemNamesToLoot = lootItemNames(config); + final Predicate filter = groundItem -> + groundItem.getLocation().distanceTo(Microbot.getClient().getLocalPlayer().getWorldLocation()) < config.attackRadius() && + (!config.toggleOnlyLootMyItems() || groundItem.getOwnership() == OWNERSHIP_SELF) && + (shouldLootBasedOnName(groundItem, itemNamesToLoot) || shouldLootBasedOnValue(groundItem, config)); + List groundItems = getGroundItems().values().stream() + .filter(filter) + .collect(Collectors.toList()); + + if (groundItems.isEmpty()) { + return; + } + if (config.toggleDelayedLooting()) { + groundItems.sort(Comparator.comparingInt(Rs2GroundItem::calculateDespawnTime)); + } + // Defer clearing wait-for-loot until we successfully pick at least one item + //Pause other scripts before looting and always release + boolean previousPauseState = Microbot.pauseAllScripts.getAndSet(true); + try { + 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, 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; + } + } + Microbot.log("Picking up loot: " + groundItem.getName()); + if (!waitForGroundItemDespawn(() -> interact(groundItem), groundItem)) { + // Skip this item and continue to the next rather than aborting the whole pass + continue; + } + // Clear wait state after first successful pickup + if (!clearedWait && AIOFighterPlugin.isWaitingForLoot()) { + AIOFighterPlugin.clearWaitForLoot("First loot item picked up"); + clearedWait = true; + } + } + Microbot.log("Looting complete"); + } finally { + Microbot.pauseAllScripts.set(previousPauseState); + } + } catch (Exception ex) { + Microbot.log("Looterscript: " + ex.getMessage()); + } + + }, 0, 200, TimeUnit.MILLISECONDS); + return true; + } + + private boolean canStackItem(GroundItem groundItem) { + if (!groundItem.isStackable()) { + return false; + } + int runePouchRunes = Rs2RunePouch.getQuantity(groundItem.getItemId()); + if (runePouchRunes > 0 && runePouchRunes <= 16000 - groundItem.getQuantity()) { + return true; + } + //TODO("Coal bag, Herb Sack, Seed pack") + return Rs2Inventory.contains(groundItem.getItemId()); + } + + private boolean shouldLootBasedOnValue(GroundItem groundItem, AIOFighterConfig config) { + if (config.looterStyle() != DefaultLooterStyle.GE_PRICE_RANGE && config.looterStyle() != DefaultLooterStyle.MIXED) + return false; + int price = groundItem.getGePrice(); + return config.minPriceOfItemsToLoot() <= price && price / groundItem.getQuantity() <= config.maxPriceOfItemsToLoot(); + } + + private boolean shouldLootBasedOnName(GroundItem groundItem, String[] itemNamesToLoot) { + return Arrays.stream(itemNamesToLoot).anyMatch(name -> groundItem.getName().trim().toLowerCase().contains(name.trim().toLowerCase())); + } + + private String[] lootItemNames(AIOFighterConfig config) { + ArrayList itemNamesToLoot = new ArrayList<>(); + if (config.toggleLootArrows()) { + itemNamesToLoot.add("arrow"); + } + if (config.toggleBuryBones()) { + itemNamesToLoot.add("bones"); + } + if (config.toggleScatter()) { + itemNamesToLoot.add(" ashes"); + } + if (config.toggleLootRunes()) { + itemNamesToLoot.add(" rune"); + } + if (config.toggleLootCoins()) { + itemNamesToLoot.add("coins"); + } + if (config.toggleLootUntradables()) { + itemNamesToLoot.add("untradeable"); + itemNamesToLoot.add("scroll box"); + } + if (config.looterStyle().equals(DefaultLooterStyle.MIXED) || config.looterStyle().equals(DefaultLooterStyle.ITEM_LIST)) { + itemNamesToLoot.addAll(Arrays.asList(config.listOfItemsToLoot().trim().split(","))); + } + return itemNamesToLoot.toArray(new String[0]); + } + + public void shutdown() { + super.shutdown(); + } +}