menu manager: add custom widget menu capability

This commit is contained in:
Toocanzs
2017-11-16 20:11:19 -05:00
committed by Adam
parent ab297e969c
commit c7a0699a41
7 changed files with 368 additions and 2 deletions

View File

@@ -28,6 +28,8 @@ public enum WidgetInfo
{
INVENTORY(WidgetID.INVENTORY_GROUP_ID, 0),
WORLD_MAP(WidgetID.WORLD_MAP_MENU_GROUP_ID, WidgetID.WorldMap.OPTION),
CLUE_SCROLL_TEXT(WidgetID.CLUE_SCROLL_GROUP_ID, WidgetID.Cluescroll.CLUE_TEXT),
EQUIPMENT(WidgetID.EQUIPMENT_GROUP_ID, 0),
@@ -106,6 +108,11 @@ public enum WidgetInfo
this.childId = childId;
}
public int getId()
{
return groupId << 16 | childId;
}
public int getGroupId()
{
return groupId;

View File

@@ -176,7 +176,7 @@ public class Hooks
}
}
public static void menuActionHook(int var0, int var1, int menuAction, int id, String menuOption, String menuTarget, int var6, int var7)
public static void menuActionHook(int var0, int widgetId, int menuAction, int id, String menuOption, String menuTarget, int var6, int var7)
{
/* Along the way, the RuneScape client may change a menuAction by incrementing it with 2000.
* I have no idea why, but it does. Their code contains the same conditional statement.
@@ -186,17 +186,31 @@ public class Hooks
menuAction -= 2000;
}
logger.debug("Menu action clicked: {} ({}) on {} ({})", menuOption, menuAction, menuTarget.isEmpty() ? "<nothing>" : menuTarget, id);
logger.debug("Menu action clicked: {} ({}) on {} ({} widget: {})",
menuOption, menuAction, menuTarget.isEmpty() ? "<nothing>" : menuTarget, id, var0, widgetId);
MenuOptionClicked menuOptionClicked = new MenuOptionClicked();
menuOptionClicked.setMenuOption(menuOption);
menuOptionClicked.setMenuTarget(menuTarget);
menuOptionClicked.setMenuAction(MenuAction.of(menuAction));
menuOptionClicked.setId(id);
menuOptionClicked.setWidgetId(widgetId);
eventBus.post(menuOptionClicked);
}
public static void addMenuEntry(String option, String target, int type, int identifier, int param0, int param1)
{
if (logger.isTraceEnabled())
{
logger.trace("Menu entry added {} {}", option, target);
}
MenuEntryAdded menuEntry = new MenuEntryAdded(option, target, type, identifier, param0, param1);
eventBus.post(menuEntry);
}
public static void addChatMessage(int type, String sender, String message, String clan)
{
if (logger.isDebugEnabled())

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.events;
public class MenuEntryAdded
{
private String option;
private String target;
private int type;
private int identifier;
private int actionParam0;
private int actionParam1;
public MenuEntryAdded(String option, String target, int type, int identifier, int actionParam0, int param1)
{
this.option = option;
this.target = target;
this.type = type;
this.identifier = identifier;
this.actionParam0 = actionParam0;
this.actionParam1 = param1;
}
public String getOption()
{
return option;
}
public String getTarget()
{
return target;
}
public int getType()
{
return type;
}
public int getIdentifier()
{
return identifier;
}
public int getActionParam0()
{
return actionParam0;
}
public int getActionParam1()
{
return actionParam1;
}
}

View File

@@ -36,6 +36,7 @@ public class MenuOptionClicked
private String menuTarget;
private MenuAction menuAction;
private int id;
private int widgetId;
public String getMenuOption()
{
@@ -77,4 +78,14 @@ public class MenuOptionClicked
this.id = id;
}
public int getWidgetId()
{
return widgetId;
}
public void setWidgetId(int widgetId)
{
this.widgetId = widgetId;
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.events;
import net.runelite.api.widgets.WidgetInfo;
public class WidgetMenuOptionClicked
{
private String menuOption;
private String menuTarget;
private WidgetInfo widget;
public String getMenuOption()
{
return menuOption;
}
public void setMenuOption(String menuOption)
{
this.menuOption = menuOption;
}
public String getMenuTarget()
{
return menuTarget;
}
public void setMenuTarget(String menuTarget)
{
this.menuTarget = menuTarget;
}
public WidgetInfo getWidget()
{
return widget;
}
public void setWidget(WidgetInfo widget)
{
this.widget = widget;
}
}

View File

@@ -25,8 +25,12 @@
package net.runelite.client.menus;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
@@ -34,6 +38,10 @@ import javax.inject.Provider;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.events.WidgetMenuOptionClicked;
import net.runelite.client.events.MenuEntryAdded;
import net.runelite.client.events.MenuOptionClicked;
import net.runelite.client.events.PlayerMenuOptionClicked;
import net.runelite.client.events.PlayerMenuOptionsChanged;
@@ -56,6 +64,8 @@ public class MenuManager
//Maps the indexes that are being used to the menu option.
private final Map<Integer, String> playerMenuIndexMap = new HashMap<>();
//Used to manage custom non-player menu options
private final Multimap<Integer, WidgetMenuOption> managedMenuOptions = HashMultimap.create();
@Inject
public MenuManager(Provider<Client> clientProvider, EventBus eventBus)
@@ -64,6 +74,69 @@ public class MenuManager
this.eventBus = eventBus;
}
/**
* Adds a CustomMenuOption to the list of managed menu options.
*
* @param customMenuOption The custom menu to add
*/
public void addManagedCustomMenu(WidgetMenuOption customMenuOption)
{
WidgetInfo widget = customMenuOption.getWidget();
managedMenuOptions.put(widget.getId(), customMenuOption);
}
/**
* Removes a CustomMenuOption from the list of managed menu options.
*
* @param customMenuOption The custom menu to add
*/
public void removeManagedCustomMenu(WidgetMenuOption customMenuOption)
{
WidgetInfo widget = customMenuOption.getWidget();
managedMenuOptions.remove(widget.getId(), customMenuOption);
}
private boolean menuContainsCustomMenu(WidgetMenuOption customMenuOption)
{
Client client = clientProvider.get();
for (MenuEntry menuEntry : client.getMenuEntries())
{
String option = menuEntry.getOption();
String target = menuEntry.getTarget();
if (option.equals(customMenuOption.getMenuOption()) && target.equals(customMenuOption.getMenuTarget()))
{
return true;
}
}
return false;
}
@Subscribe
public void onMenuEntryAdded(MenuEntryAdded event)
{
int widgetId = event.getActionParam1();
Collection<WidgetMenuOption> options = managedMenuOptions.get(widgetId);
for (WidgetMenuOption currentMenu : options)
{
Client client = clientProvider.get();
if (!menuContainsCustomMenu(currentMenu))//Don't add if we have already added it to this widget
{
MenuEntry[] menuEntries = client.getMenuEntries();
menuEntries = Arrays.copyOf(menuEntries, menuEntries.length + 1);
MenuEntry menuEntry = menuEntries[menuEntries.length - 1] = new MenuEntry();
menuEntry.setOption(currentMenu.getMenuOption());
menuEntry.setParam1(widgetId);
menuEntry.setTarget(currentMenu.getMenuTarget());
menuEntry.setType(MenuAction.RUNELITE);
client.setMenuEntries(menuEntries);
}
}
}
public void addPlayerMenuItem(String menuText)
{
Preconditions.checkNotNull(menuText);
@@ -110,6 +183,23 @@ public class MenuManager
return; // not a player menu
}
int widgetId = event.getWidgetId();
Collection<WidgetMenuOption> options = managedMenuOptions.get(widgetId);
for (WidgetMenuOption curMenuOption : options)
{
if (curMenuOption.getMenuTarget().equals(event.getMenuTarget())
&& curMenuOption.getMenuOption().equals(event.getMenuOption()))
{
WidgetMenuOptionClicked customMenu = new WidgetMenuOptionClicked();
customMenu.setMenuOption(event.getMenuOption());
customMenu.setMenuTarget(event.getMenuTarget());
customMenu.setWidget(curMenuOption.getWidget());
eventBus.post(customMenu);
return; // don't continue because it's not a player option
}
}
String target = event.getMenuTarget();
String username = target.split("[<>]")[2]; // <col=ffffff>username<col=40ff00> (level-42)

View File

@@ -0,0 +1,105 @@
/*
* Copyright (c) 2017, Robin <robin.weymans@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.menus;
import net.runelite.api.widgets.WidgetInfo;
import java.awt.Color;
public final class WidgetMenuOption
{
/**
* The left hand text to be displayed on the menu option. Ex. the menuOption of "Drop Bones" is "Drop"
*/
private String menuOption;
/**
* The right hand text to be displayed on the menu option Ex. the menuTarget of "Drop Bones" is "Bones"
*/
private String menuTarget;
/**
* The color that the menuTarget should be. Defaults to the brownish color that most menu options have.
*/
private Color color = Color.decode("#ff9040");
/**
* The widget to add the option to
*/
private final WidgetInfo widget;
/**
* Creates a menu to be added to right click menus. The menu will only be added if match is found within the menu options
*
* @param menuOption Option text of this right click option
* @param menuTarget Target text of this right click option
* @param widget The widget to attach this option to
*/
public WidgetMenuOption(String menuOption, String menuTarget, WidgetInfo widget)
{
this.menuOption = menuOption;
setMenuTarget(menuTarget);
this.widget = widget;
}
public void setMenuOption(String option)
{
menuOption = option;
}
/**
* Sets the target of the menu option. Color code will be added on to target
*
* @param target The target text without color code.
*/
public void setMenuTarget(String target)
{
menuTarget = String.format("<col=%02x%02x%02x>%s</col>",
color.getRed(), color.getGreen(), color.getBlue(), target);
}
public String getMenuOption()
{
return menuOption;
}
public String getMenuTarget()
{
return menuTarget;
}
public WidgetInfo getWidget()
{
return widget;
}
public Color getColor()
{
return color;
}
public void setColor(Color col)
{
color = col;
}
}