diff --git a/runelite-client/src/main/java/net/runelite/client/menus/AbstractMenuEntry.java b/runelite-client/src/main/java/net/runelite/client/menus/AbstractMenuEntry.java new file mode 100644 index 0000000000..14577265d4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/menus/AbstractMenuEntry.java @@ -0,0 +1,100 @@ +package net.runelite.client.menus; + +import joptsimple.internal.Strings; +import lombok.Getter; +import net.runelite.api.MenuEntry; +import static net.runelite.client.menus.MenuManager.LEVEL_PATTERN; +import net.runelite.client.util.Text; + +class AbstractMenuEntry +{ + @Getter + private String option; + + @Getter + private String target; + + @Getter + private int id; + + @Getter + private int type; + + @Getter + private boolean strictOption; + + @Getter + private boolean strictTarget; + + AbstractMenuEntry(String option, String target) + { + this(option, target, -1, -1, true, true); + } + + AbstractMenuEntry(String option, String target, boolean strictTarget) + { + this(option, target, -1, -1, true, strictTarget); + } + + AbstractMenuEntry(String option, String target, int id, int type, boolean strictOption, boolean strictTarget) + { + this.option = option; + this.target = target; + this.id = id; + this.type = type; + this.strictOption = strictOption; + this.strictTarget = strictTarget; + } + + boolean matches(MenuEntry entry) + { + String opt = Text.standardize(entry.getOption()); + + if (strictOption && !opt.equals(option) || !strictOption && !opt.contains(option)) + { + return false; + } + + if (strictTarget || !Strings.isNullOrEmpty(target)) + { + String tgt = Text.standardize(LEVEL_PATTERN.matcher(entry.getTarget()).replaceAll("")); + + if (strictTarget && !tgt.equals(target) || !strictTarget && !tgt.contains(target)) + { + return false; + } + } + + if (id != -1) + { + int id = entry.getIdentifier(); + + if (this.id != id) + { + return false; + } + } + + if (type != -1) + { + int type = entry.getType(); + + if (this.type != type) + { + return false; + } + } + + return true; + } + + boolean equals(AbstractMenuEntry other) + { + return target.equals(other.getTarget()) + && option.equals(other.getOption()) + && id == other.getId() + && type == other.getType() + && strictOption == other.isStrictOption() + && strictTarget == other.isStrictTarget(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/menus/MenuManager.java b/runelite-client/src/main/java/net/runelite/client/menus/MenuManager.java index 476cfcd148..4fba85094b 100644 --- a/runelite-client/src/main/java/net/runelite/client/menus/MenuManager.java +++ b/runelite-client/src/main/java/net/runelite/client/menus/MenuManager.java @@ -27,6 +27,7 @@ package net.runelite.client.menus; import com.google.common.base.Preconditions; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -35,6 +36,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; @@ -62,7 +64,20 @@ public class MenuManager */ private static final int IDX_LOWER = 4; private static final int IDX_UPPER = 8; - private static final Pattern LEVEL_PATTERN = Pattern.compile("\\(level-[0-9]*\\)"); + static final Pattern LEVEL_PATTERN = Pattern.compile("\\(level-[0-9]*\\)"); + + private static MenuEntry CANCEL() + { + MenuEntry cancel = new MenuEntry(); + cancel.setOption("Cancel"); + cancel.setTarget(""); + cancel.setIdentifier(0); + cancel.setType(MenuAction.CANCEL.getId()); + cancel.setParam0(0); + cancel.setParam1(0); + + return cancel; + } private final Client client; private final EventBus eventBus; @@ -72,7 +87,11 @@ public class MenuManager //Used to manage custom non-player menu options private final Multimap managedMenuOptions = HashMultimap.create(); private final Set npcMenuOptions = new HashSet<>(); - private final Map> priorityEntries = new HashMap<>(); + + private final Set priorityEntries = new HashSet<>(); + private final Set currentPriorityEntries = new HashSet<>(); + + private final Map swaps = new HashMap<>(); @Inject private MenuManager(Client client, EventBus eventBus) @@ -125,6 +144,12 @@ public class MenuManager Collection options = managedMenuOptions.get(widgetId); MenuEntry[] menuEntries = client.getMenuEntries(); + if (menuEntries.length == 1) + { + // Menu entries reset, so priority entries should reset as well + currentPriorityEntries.clear(); + } + for (WidgetMenuOption currentMenu : options) { if (!menuContainsCustomMenu(currentMenu))//Don't add if we have already added it to this widget @@ -141,48 +166,49 @@ public class MenuManager } } - List menuEntryList = Arrays.asList(menuEntries); - boolean shouldPurge = menuEntryList.stream().anyMatch(m -> - { - String opt = Text.standardize(m.getOption()); - String tgt = Text.standardize(m.getTarget()); - tgt = LEVEL_PATTERN.matcher(tgt).replaceAll("").trim(); + MenuEntry newestEntry = menuEntries[menuEntries.length - 1]; - if (!priorityEntries.containsKey(opt)) - { - return false; - } + boolean isPrio = priorityEntries.stream().anyMatch(p -> p.matches(newestEntry)); - return priorityEntries.get(opt).contains(tgt); - }); - - if (shouldPurge) + // If the last entry was a priority entry, keep track of it + if (isPrio) { - client.setMenuEntries(menuEntryList.stream().filter(m -> - { - String opt = Text.standardize(m.getOption()); - String tgt = Text.standardize(m.getTarget()); - tgt = LEVEL_PATTERN.matcher(tgt).replaceAll("").trim(); - - if (opt.equals("cancel")) - { - return true; - } - - if (!priorityEntries.containsKey(opt)) - { - return false; - } - - // Gets overridden by actor names - if (opt.equals("walk here")) - { - return true; - } - - return priorityEntries.get(opt).contains(tgt); - }).toArray(MenuEntry[]::new)); + currentPriorityEntries.add(newestEntry); } + + // Make a copy of the menu entries, cause you can't remove from Arrays.asList() + List copy = new ArrayList<>(Arrays.asList(menuEntries)); + + // If there are entries we want to prioritize, we have to remove the rest + if (!currentPriorityEntries.isEmpty()) + { + copy.retainAll(currentPriorityEntries); + + copy.add(CANCEL()); + } + + /*// Find the current entry in the swaps map + Optional swapEntry = swaps.keySet().stream().filter(e -> e.matches(newestEntry)).findFirst(); + + if (swapEntry.isPresent()) + { + AbstractMenuEntry swap = swapEntry.get(); + AbstractMenuEntry swapTarget = swaps.get(swap); + + // Find the target for the swap in current menu entries + Optional foundSwap = Lists.reverse(copy).stream().filter(swapTarget::matches).findFirst(); + + if (foundSwap.isPresent()) + { + // Swap + int index = copy.indexOf(foundSwap.get()); + + copy.set(index, newestEntry); + copy.set(copy.size() - 1, foundSwap.get()); + } + }*/ + + client.setMenuEntries(copy.toArray(new MenuEntry[0])); } public void addPlayerMenuItem(String menuText) @@ -356,18 +382,16 @@ public class MenuManager } /** - * Adds to the map of menu entries which when present, will remove all entries except for this one + * Adds to the set of menu entries which when present, will remove all entries except for this one */ public void addPriorityEntry(String option, String target) { option = Text.standardize(option); target = Text.standardize(target); - Set targets = priorityEntries.getOrDefault(option, new HashSet<>()); + AbstractMenuEntry entry = new AbstractMenuEntry(option, target); - targets.add(target); - - priorityEntries.put(option, targets); + priorityEntries.add(entry); } public void removePriorityEntry(String option, String target) @@ -375,17 +399,10 @@ public class MenuManager option = Text.standardize(option); target = Text.standardize(target); - Set targets = priorityEntries.getOrDefault(option, new HashSet<>()); + AbstractMenuEntry entry = new AbstractMenuEntry(option, target); - targets.remove(target); + Set toRemove = priorityEntries.stream().filter(entry::equals).collect(Collectors.toSet()); - if (targets.isEmpty()) - { - priorityEntries.remove(option); - } - else - { - priorityEntries.put(option, targets); - } + priorityEntries.removeAll(toRemove); } }