diff --git a/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/DismissNpcEvent.java b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/DismissNpcEvent.java index 6ec3500d77..5e742abde7 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/DismissNpcEvent.java +++ b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/DismissNpcEvent.java @@ -1,16 +1,27 @@ package net.runelite.client.plugins.microbot.eventdismiss; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ItemID; import net.runelite.client.plugins.microbot.BlockingEvent; import net.runelite.client.plugins.microbot.BlockingEventPriority; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.util.Global; import net.runelite.client.plugins.microbot.util.dialogues.Rs2Dialogue; +import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; +import net.runelite.client.plugins.microbot.util.math.Rs2Random; import net.runelite.client.plugins.microbot.util.npc.Rs2Npc; import net.runelite.client.plugins.microbot.api.npc.models.Rs2NpcModel; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +@Slf4j public class DismissNpcEvent implements BlockingEvent { private final EventDismissConfig config; + private final AtomicBoolean waitingForLamp = new AtomicBoolean(false); + private final AtomicInteger lampWaitCounter = new AtomicInteger(0); + private static final int MAX_LAMP_WAIT_TICKS = 50; public DismissNpcEvent(EventDismissConfig config) { this.config = config; @@ -24,47 +35,124 @@ private Rs2NpcModel getRandomEventNpc() { @Override public boolean validate() { - Rs2NpcModel randomEventNPC = getRandomEventNpc(); - if (randomEventNPC == null) { - return false; + if (waitingForLamp.get()) { + return true; } - return randomEventNPC.hasLineOfSight(); + Rs2NpcModel npc = getRandomEventNpc(); + return npc != null && npc.hasLineOfSight(); } @Override public boolean execute() { + if (waitingForLamp.get()) { + return handleLampWait(); + } + Rs2NpcModel npc = getRandomEventNpc(); if (npc == null) return true; String name = npc.getName(); - if (name == null) return true; - if (("Count Check".equals(name) && !config.dismissCountCheck()) || - ("Genie".equals(name) && !config.dismissGenie())) { - talkTo(npc); - return !validate(); + Global.sleep(Rs2Random.between(1200, 3000)); + + if (shouldAcceptLamp(name)) { + if (Rs2Inventory.isFull()) { + log.info("Inventory full — waiting for space to accept lamp from {}", name); + return false; + } + return acceptLamp(npc); } dismiss(npc); return !validate(); } - @Override - public BlockingEventPriority priority() { - return BlockingEventPriority.LOWEST; + private boolean shouldAcceptLamp(String npcName) { + if ("Genie".equals(npcName)) { + return config.genieAction() == EventAction.ACCEPT; + } + if ("Count Check".equals(npcName)) { + return config.countCheckAction() == EventAction.ACCEPT; + } + return false; } - private void talkTo(Rs2NpcModel npc) { + private boolean acceptLamp(Rs2NpcModel npc) { npc.click("Talk-to"); - Rs2Dialogue.sleepUntilHasContinue(); - Rs2Dialogue.clickContinue(); + continueDialogueUntilClosed(); + + if (Rs2Inventory.contains(ItemID.LAMP)) { + log.info("Lamp received — using on {}", config.lampSkill()); + Global.sleep(600, 1200); + if (LampUtility.useLamp(config.lampSkill())) { + return true; + } + } + + log.info("Waiting for lamp to appear in inventory"); + waitingForLamp.set(true); + lampWaitCounter.set(0); + + Global.sleep(600, 1200); + + if (Rs2Inventory.contains(ItemID.LAMP)) { + if (LampUtility.useLamp(config.lampSkill())) { + resetLampWaitState(); + return true; + } + } + + return false; + } + + private boolean handleLampWait() { + if (lampWaitCounter.incrementAndGet() > MAX_LAMP_WAIT_TICKS) { + log.warn("Lamp wait timeout"); + resetLampWaitState(); + return true; + } + + if (Rs2Inventory.contains(ItemID.LAMP)) { + log.info("Lamp appeared — using on {}", config.lampSkill()); + if (LampUtility.useLamp(config.lampSkill())) { + resetLampWaitState(); + return true; + } + } + + return false; + } + + private void resetLampWaitState() { + waitingForLamp.set(false); + lampWaitCounter.set(0); + } + + private void continueDialogueUntilClosed() { + Rs2Dialogue.sleepUntilInDialogue(); + + while (Rs2Dialogue.isInDialogue()) { + if (Rs2Dialogue.hasContinue()) { + Rs2Dialogue.clickContinue(); + Global.sleep(600, 1200); + } else { + Global.sleep(300, 600); + } + } + + Rs2Dialogue.sleepUntilNotInDialogue(); } private void dismiss(Rs2NpcModel npc) { npc.click("Dismiss"); Global.sleepUntil(() -> getRandomEventNpc() == null); } + + @Override + public BlockingEventPriority priority() { + return BlockingEventPriority.LOWEST; + } } diff --git a/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventAction.java b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventAction.java new file mode 100644 index 0000000000..ff7c4278f4 --- /dev/null +++ b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventAction.java @@ -0,0 +1,17 @@ +package net.runelite.client.plugins.microbot.eventdismiss; + +public enum EventAction { + ACCEPT("Accept"), + DISMISS("Dismiss"); + + private final String name; + + EventAction(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } +} \ No newline at end of file diff --git a/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventDismissConfig.java b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventDismissConfig.java index c9307ece46..dc9bfd4912 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventDismissConfig.java +++ b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventDismissConfig.java @@ -1,29 +1,62 @@ package net.runelite.client.plugins.microbot.eventdismiss; +import net.runelite.api.Skill; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.ConfigSection; @ConfigGroup("EventDismiss") public interface EventDismissConfig extends Config { + @ConfigSection( + name = "Lamp Events", + description = "Settings for lamp-giving random events", + position = 0 + ) + String lampSection = "lampEvents"; + @ConfigItem( - name = "Count Check Dismiss", - keyName = "dismissCountCheck", - position = 3, - description = "Dismiss Count Check random event" + name = "Genie", + keyName = "genieAction", + position = 0, + section = lampSection, + description = "Accept lamp or dismiss Genie random event" ) - default boolean dismissCountCheck() { - return false; + default EventAction genieAction() { + return EventAction.ACCEPT; } @ConfigItem( - name = "Genie Dismiss", - keyName = "dismissGenie", - position = 9, - description = "Dismiss Genie random event" + name = "Count Check", + keyName = "countCheckAction", + position = 1, + section = lampSection, + description = "Accept lamp or dismiss Count Check random event" + ) + default EventAction countCheckAction() { + return EventAction.ACCEPT; + } + + @ConfigItem( + name = "Lamp Skill", + keyName = "lampSkill", + position = 2, + section = lampSection, + description = "Skill to use experience lamps on" + ) + default Skill lampSkill() { + return Skill.HERBLORE; + } + + @ConfigItem( + name = "Use Stray Lamps", + keyName = "checkForLamps", + position = 3, + section = lampSection, + description = "Automatically use lamps found in inventory from any source" ) - default boolean dismissGenie() { + default boolean checkForLamps() { return false; } } diff --git a/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventDismissOverlay.java b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventDismissOverlay.java new file mode 100644 index 0000000000..50480988d4 --- /dev/null +++ b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventDismissOverlay.java @@ -0,0 +1,70 @@ +package net.runelite.client.plugins.microbot.eventdismiss; + +import net.runelite.client.ui.overlay.OverlayPanel; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.LineComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; + +import javax.inject.Inject; +import java.awt.*; + +public class EventDismissOverlay extends OverlayPanel { + + private final EventDismissConfig config; + + @Inject + EventDismissOverlay(EventDismissPlugin plugin, EventDismissConfig config) { + super(plugin); + this.config = config; + setPosition(OverlayPosition.TOP_LEFT); + setNaughty(); + } + + @Override + public Dimension render(Graphics2D graphics) { + boolean lampFeaturesActive = config.genieAction() == EventAction.ACCEPT + || config.countCheckAction() == EventAction.ACCEPT + || config.checkForLamps(); + + if (!lampFeaturesActive) { + return null; + } + + panelComponent.setPreferredSize(new Dimension(200, 0)); + + panelComponent.getChildren().add(TitleComponent.builder() + .text("Event Dismiss") + .color(Color.CYAN) + .build()); + + panelComponent.getChildren().add(LineComponent.builder() + .left("Lamp Skill:") + .right(config.lampSkill().getName()) + .rightColor(Color.YELLOW) + .build()); + + panelComponent.getChildren().add(LineComponent.builder() + .left("Lamps Used:") + .right(String.valueOf(LampUtility.getLampsUsed())) + .rightColor(Color.GREEN) + .build()); + + if (LampUtility.getLampsUsed() > 0 && LampUtility.getLastLampTime() > 0) { + long secondsAgo = (System.currentTimeMillis() - LampUtility.getLastLampTime()) / 1000; + panelComponent.getChildren().add(LineComponent.builder() + .left("Last Lamp:") + .right(formatDuration(secondsAgo)) + .rightColor(Color.LIGHT_GRAY) + .build()); + } + + return super.render(graphics); + } + + private static String formatDuration(long seconds) { + if (seconds < 60) { + return seconds + "s ago"; + } + return (seconds / 60) + "m ago"; + } +} diff --git a/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventDismissPlugin.java b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventDismissPlugin.java index 5fa68b59ee..a1a6aeb906 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventDismissPlugin.java +++ b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/EventDismissPlugin.java @@ -7,14 +7,15 @@ import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.PluginConstants; +import net.runelite.client.ui.overlay.OverlayManager; import javax.inject.Inject; import java.awt.*; @PluginDescriptor( name = PluginDescriptor.Default + "Event Dismiss", - description = "Random Event Dismisser", - tags = {"random", "events", "microbot"}, + description = "Dismisses random events and optionally accepts lamps from Genie/Count Check", + tags = {"random", "events", "microbot", "lamp", "genie"}, authors = {"Unknown"}, version = EventDismissPlugin.version, minClientVersion = "2.0.7", @@ -25,13 +26,17 @@ ) @Slf4j public class EventDismissPlugin extends Plugin { - public static final String version = "1.0.4"; - @Inject - private ConfigManager configManager; + public static final String version = "2.0.0"; + @Inject private EventDismissConfig config; + @Inject + private OverlayManager overlayManager; + @Inject + private EventDismissOverlay overlay; private DismissNpcEvent dismissNpcEvent; + private UseLampEvent useLampEvent; @Provides EventDismissConfig provideConfig(ConfigManager configManager) { @@ -41,11 +46,23 @@ EventDismissConfig provideConfig(ConfigManager configManager) { @Override protected void startUp() throws AWTException { dismissNpcEvent = new DismissNpcEvent(config); + useLampEvent = new UseLampEvent(config); Microbot.getBlockingEventManager().add(dismissNpcEvent); + Microbot.getBlockingEventManager().add(useLampEvent); + if (overlayManager != null) { + overlayManager.add(overlay); + } + LampUtility.reset(); } + @Override protected void shutDown() { Microbot.getBlockingEventManager().remove(dismissNpcEvent); + Microbot.getBlockingEventManager().remove(useLampEvent); + if (overlayManager != null) { + overlayManager.remove(overlay); + } dismissNpcEvent = null; + useLampEvent = null; } } diff --git a/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/LampUtility.java b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/LampUtility.java new file mode 100644 index 0000000000..ce655080b8 --- /dev/null +++ b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/LampUtility.java @@ -0,0 +1,105 @@ +package net.runelite.client.plugins.microbot.eventdismiss; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ItemID; +import net.runelite.api.Skill; +import net.runelite.client.plugins.microbot.util.Global; +import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; +import net.runelite.client.plugins.microbot.util.widget.Rs2Widget; + +import java.util.EnumMap; +import java.util.Map; + +import static net.runelite.client.plugins.microbot.eventdismiss.LampWidgetConstants.*; + +@Slf4j +public class LampUtility { + + private static final Map SKILL_WIDGET_MAP = new EnumMap<>(Skill.class); + + @Getter + private static int lampsUsed = 0; + @Getter + private static long lastLampTime = 0; + + static { + SKILL_WIDGET_MAP.put(Skill.ATTACK, WIDGET_ATTACK); + SKILL_WIDGET_MAP.put(Skill.STRENGTH, WIDGET_STRENGTH); + SKILL_WIDGET_MAP.put(Skill.RANGED, WIDGET_RANGED); + SKILL_WIDGET_MAP.put(Skill.MAGIC, WIDGET_MAGIC); + SKILL_WIDGET_MAP.put(Skill.DEFENCE, WIDGET_DEFENCE); + SKILL_WIDGET_MAP.put(Skill.SAILING, WIDGET_SAILING); + SKILL_WIDGET_MAP.put(Skill.HITPOINTS, WIDGET_HITPOINTS); + SKILL_WIDGET_MAP.put(Skill.PRAYER, WIDGET_PRAYER); + SKILL_WIDGET_MAP.put(Skill.AGILITY, WIDGET_AGILITY); + SKILL_WIDGET_MAP.put(Skill.HERBLORE, WIDGET_HERBLORE); + SKILL_WIDGET_MAP.put(Skill.THIEVING, WIDGET_THIEVING); + SKILL_WIDGET_MAP.put(Skill.CRAFTING, WIDGET_CRAFTING); + SKILL_WIDGET_MAP.put(Skill.RUNECRAFT, WIDGET_RUNECRAFT); + SKILL_WIDGET_MAP.put(Skill.SLAYER, WIDGET_SLAYER); + SKILL_WIDGET_MAP.put(Skill.FARMING, WIDGET_FARMING); + SKILL_WIDGET_MAP.put(Skill.MINING, WIDGET_MINING); + SKILL_WIDGET_MAP.put(Skill.SMITHING, WIDGET_SMITHING); + SKILL_WIDGET_MAP.put(Skill.FISHING, WIDGET_FISHING); + SKILL_WIDGET_MAP.put(Skill.COOKING, WIDGET_COOKING); + SKILL_WIDGET_MAP.put(Skill.FIREMAKING, WIDGET_FIREMAKING); + SKILL_WIDGET_MAP.put(Skill.WOODCUTTING, WIDGET_WOODCUTTING); + SKILL_WIDGET_MAP.put(Skill.FLETCHING, WIDGET_FLETCHING); + SKILL_WIDGET_MAP.put(Skill.CONSTRUCTION, WIDGET_CONSTRUCTION); + SKILL_WIDGET_MAP.put(Skill.HUNTER, WIDGET_HUNTER); + } + + public static int getSkillWidgetId(Skill skill) { + if (skill == null) { + return -1; + } + return SKILL_WIDGET_MAP.getOrDefault(skill, -1); + } + + public static boolean useLamp(Skill skill) { + if (skill == null || !Rs2Inventory.contains(ItemID.LAMP)) { + return false; + } + + if (!Rs2Inventory.interact(ItemID.LAMP, "Rub")) { + log.warn("Failed to rub lamp"); + return false; + } + + if (!Global.sleepUntil(() -> Rs2Widget.isWidgetVisible(LAMP_WIDGET_GROUP, LAMP_WIDGET_ROOT), 3000)) { + log.warn("Lamp interface did not open"); + return false; + } + + int skillWidgetId = getSkillWidgetId(skill); + if (skillWidgetId == -1) { + log.warn("Unsupported lamp skill: {}", skill); + return false; + } + + Rs2Widget.clickWidget(LAMP_WIDGET_GROUP, skillWidgetId); + Global.sleep(600, 1200); + + Rs2Widget.clickWidget(LAMP_WIDGET_GROUP, LAMP_CONFIRM_BUTTON); + + if (!Global.sleepUntil(() -> !Rs2Inventory.contains(ItemID.LAMP), 3000)) { + log.warn("Lamp was not consumed after confirm"); + return false; + } + + lampsUsed++; + lastLampTime = System.currentTimeMillis(); + log.info("Used lamp on {}", skill); + return true; + } + + public static boolean hasLampInInventory() { + return Rs2Inventory.contains(ItemID.LAMP); + } + + public static void reset() { + lampsUsed = 0; + lastLampTime = 0; + } +} diff --git a/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/LampWidgetConstants.java b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/LampWidgetConstants.java new file mode 100644 index 0000000000..8bc454373c --- /dev/null +++ b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/LampWidgetConstants.java @@ -0,0 +1,37 @@ +package net.runelite.client.plugins.microbot.eventdismiss; + +public final class LampWidgetConstants { + + private LampWidgetConstants() { + } + + public static final int LAMP_WIDGET_GROUP = 240; + public static final int LAMP_WIDGET_ROOT = 0; + public static final int LAMP_CONFIRM_BUTTON = 27; + + // Verified live 2026-05-24 against widget group 240 + public static final int WIDGET_ATTACK = 2; + public static final int WIDGET_STRENGTH = 3; + public static final int WIDGET_RANGED = 4; + public static final int WIDGET_MAGIC = 5; + public static final int WIDGET_DEFENCE = 6; + public static final int WIDGET_SAILING = 7; + public static final int WIDGET_HITPOINTS = 8; + public static final int WIDGET_PRAYER = 9; + public static final int WIDGET_AGILITY = 10; + public static final int WIDGET_HERBLORE = 11; + public static final int WIDGET_THIEVING = 12; + public static final int WIDGET_CRAFTING = 13; + public static final int WIDGET_RUNECRAFT = 14; + public static final int WIDGET_SLAYER = 15; + public static final int WIDGET_FARMING = 16; + public static final int WIDGET_MINING = 17; + public static final int WIDGET_SMITHING = 18; + public static final int WIDGET_FISHING = 19; + public static final int WIDGET_COOKING = 20; + public static final int WIDGET_FIREMAKING = 21; + public static final int WIDGET_WOODCUTTING = 22; + public static final int WIDGET_FLETCHING = 23; + public static final int WIDGET_CONSTRUCTION = 24; + public static final int WIDGET_HUNTER = 25; +} diff --git a/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/UseLampEvent.java b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/UseLampEvent.java new file mode 100644 index 0000000000..3a4433839d --- /dev/null +++ b/src/main/java/net/runelite/client/plugins/microbot/eventdismiss/UseLampEvent.java @@ -0,0 +1,33 @@ +package net.runelite.client.plugins.microbot.eventdismiss; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ItemID; +import net.runelite.client.plugins.microbot.BlockingEvent; +import net.runelite.client.plugins.microbot.BlockingEventPriority; +import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; + +@Slf4j +public class UseLampEvent implements BlockingEvent { + + private final EventDismissConfig config; + + public UseLampEvent(EventDismissConfig config) { + this.config = config; + } + + @Override + public boolean validate() { + return config.checkForLamps() && Rs2Inventory.contains(ItemID.LAMP); + } + + @Override + public boolean execute() { + log.debug("Using stray lamp on {}", config.lampSkill()); + return LampUtility.useLamp(config.lampSkill()); + } + + @Override + public BlockingEventPriority priority() { + return BlockingEventPriority.NORMAL; + } +}