Add inventory tagging plugin

Add plugin that will tag items in inventory with 4 different categories
and draw outlines for tagged items.

Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
This commit is contained in:
Tomas Slusny
2018-05-20 00:31:19 +02:00
committed by Adam
parent af7e76502b
commit 49c3f538ef
3 changed files with 516 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
/*
* Copyright (c) 2018 kulers
* 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.plugins.inventorytags;
import java.awt.Color;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@ConfigGroup(
keyName = "inventorytags",
name = "Inventory Tags",
description = "Configuration for the Inventory Item Tagging plugin"
)
public interface InventoryTagsConfig extends Config
{
String GROUP = "inventorytags";
@ConfigItem(
position = 0,
keyName = "groupColor1",
name = "Group 1 Color",
description = "Color of the Tag"
)
default Color getGroup1Color()
{
return new Color(255, 0, 0);
}
@ConfigItem(
position = 1,
keyName = "groupColor2",
name = "Group 2 Color",
description = "Color of the Tag"
)
default Color getGroup2Color()
{
return new Color(0, 255, 0);
}
@ConfigItem(
position = 2,
keyName = "groupColor3",
name = "Group 3 Color",
description = "Color of the Tag"
)
default Color getGroup3Color()
{
return new Color(0, 0, 255);
}
@ConfigItem(
position = 3,
keyName = "groupColor4",
name = "Group 4 Color",
description = "Color of the Tag"
)
default Color getGroup4Color()
{
return new Color(255, 0, 255);
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright (c) 2018 kulers
* 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.plugins.inventorytags;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.Query;
import net.runelite.api.SpritePixels;
import net.runelite.api.queries.InventoryWidgetItemQuery;
import net.runelite.api.widgets.WidgetItem;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
import net.runelite.client.util.QueryRunner;
public class InventoryTagsOverlay extends Overlay
{
private final Map<String, BufferedImage> storedOutlines = new HashMap<>();
private final QueryRunner queryRunner;
private final Client client;
private final InventoryTagsPlugin plugin;
@Inject
private InventoryTagsOverlay(QueryRunner queryRunner, Client client, InventoryTagsPlugin plugin)
{
setPosition(OverlayPosition.DYNAMIC);
setPriority(OverlayPriority.LOW);
setLayer(OverlayLayer.ABOVE_WIDGETS);
this.queryRunner = queryRunner;
this.client = client;
this.plugin = plugin;
}
@Override
public Dimension render(Graphics2D graphics)
{
if (!plugin.isHasTaggedItems())
{
return null;
}
// Now query the inventory for the tagged item ids
final Query query = new InventoryWidgetItemQuery();
final WidgetItem[] widgetItems = queryRunner.runQuery(query);
// Iterate through all found items and draw the outlines
for (final WidgetItem item : widgetItems)
{
final String group = plugin.getTag(item.getId());
if (group != null)
{
final Color color = plugin.getGroupNameColor(group);
if (color != null)
{
final BufferedImage outline = getOutline(item.getId(), color);
graphics.drawImage(outline, item.getCanvasLocation().getX() + 1, item.getCanvasLocation().getY() + 1, null);
}
}
}
return null;
}
void clearStoredOutlines()
{
storedOutlines.clear();
}
private BufferedImage getOutline(final int id, final Color color)
{
final String key = getStringGeneratedId(id, color);
BufferedImage stored = storedOutlines.get(key);
if (stored != null)
{
return stored;
}
final SpritePixels itemSprite = client.createItemSprite(id, 1, 1, 0, 0, true, 710);
final BufferedImage generatedPicture = itemSprite.toBufferedOutline(color);
storedOutlines.put(key, generatedPicture);
return generatedPicture;
}
private String getStringGeneratedId(final int id, final Color color)
{
return id + "." + color.getRGB();
}
}

View File

@@ -0,0 +1,314 @@
/*
* Copyright (c) 2018 kulers
* 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.plugins.inventorytags;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Provides;
import java.awt.Color;
import java.util.List;
import javax.inject.Inject;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.InventoryID;
import net.runelite.api.Item;
import net.runelite.api.ItemContainer;
import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.ItemContainerChanged;
import net.runelite.api.events.MenuOpened;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.util.Text;
import org.apache.commons.lang3.ArrayUtils;
@PluginDescriptor(
name = "Inventory Tags",
enabledByDefault = false
)
public class InventoryTagsPlugin extends Plugin
{
private static final String ITEM_KEY_PREFIX = "item_";
private static final String SETNAME_GROUP_1 = "Group 1";
private static final String SETNAME_GROUP_2 = "Group 2";
private static final String SETNAME_GROUP_3 = "Group 3";
private static final String SETNAME_GROUP_4 = "Group 4";
private static final String CONFIGURE = "Configure";
private static final String SAVE = "Save";
private static final String MENU_TARGET = "<col=ff9040>Inventory Tags";
private static final String MENU_SET = "Mark";
private static final String MENU_REMOVE = "Remove";
private static final List<String> GROUPS = ImmutableList.of(SETNAME_GROUP_4, SETNAME_GROUP_3, SETNAME_GROUP_2, SETNAME_GROUP_1);
@Inject
private Client client;
@Inject
private ConfigManager configManager;
@Inject
private InventoryTagsConfig config;
@Inject
private InventoryTagsOverlay overlay;
@Inject
private OverlayManager overlayManager;
@Getter(AccessLevel.PACKAGE)
private boolean hasTaggedItems;
private boolean editorMode = false;
@Provides
InventoryTagsConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(InventoryTagsConfig.class);
}
String getTag(int itemId)
{
String tag = configManager.getConfiguration(InventoryTagsConfig.GROUP, ITEM_KEY_PREFIX + itemId);
if (tag == null || tag.isEmpty())
{
return null;
}
return tag;
}
void setTag(int itemId, String tag)
{
configManager.setConfiguration(InventoryTagsConfig.GROUP, ITEM_KEY_PREFIX + itemId, tag);
}
void unsetTag(int itemId)
{
configManager.unsetConfiguration(InventoryTagsConfig.GROUP, ITEM_KEY_PREFIX + itemId);
}
@Override
protected void startUp() throws Exception
{
overlayManager.add(overlay);
}
@Override
protected void shutDown() throws Exception
{
overlayManager.remove(overlay);
hasTaggedItems = editorMode = false;
}
@Subscribe
public void onConfigChanged(ConfigChanged configChanged)
{
if (!configChanged.getGroup().equals("inventorytags"))
{
return;
}
overlay.clearStoredOutlines();
}
@Subscribe
public void onClickMenu(final MenuOptionClicked event)
{
if (event.getMenuAction() != MenuAction.RUNELITE)
{
return;
}
if (MENU_TARGET.equals(event.getMenuTarget()))
{
switch (event.getMenuOption())
{
case CONFIGURE:
editorMode = true;
break;
case SAVE:
editorMode = false;
break;
}
return;
}
final String selectedMenu = Text.removeTags(event.getMenuTarget());
if (event.getMenuOption().equals(MENU_SET))
{
setTag(event.getId(), selectedMenu);
hasTaggedItems = true;
}
else if (event.getMenuOption().equals(MENU_REMOVE))
{
unsetTag(event.getId());
checkForTags(client.getItemContainer(InventoryID.INVENTORY));
}
}
@Subscribe
public void onMenuOpened(final MenuOpened event)
{
final MenuEntry firstEntry = event.getFirstEntry();
if (firstEntry == null)
{
return;
}
final int widgetId = firstEntry.getParam1();
// Inventory button menu
if (widgetId == WidgetInfo.FIXED_VIEWPORT_INVENTORY_TAB.getId() ||
widgetId == WidgetInfo.RESIZABLE_VIEWPORT_INVENTORY_TAB.getId() ||
widgetId == WidgetInfo.RESIZABLE_VIEWPORT_BOTTOM_LINE_INVENTORY_TAB.getId())
{
final int itemId = firstEntry.getIdentifier();
if (itemId == -1)
{
return;
}
MenuEntry[] entries = event.getMenuEntries();
final MenuEntry configureOption = new MenuEntry();
configureOption.setOption(editorMode ? SAVE : CONFIGURE);
configureOption.setTarget(MENU_TARGET);
configureOption.setIdentifier(itemId);
configureOption.setParam1(widgetId);
configureOption.setType(MenuAction.RUNELITE.getId());
entries = ArrayUtils.addAll(entries, configureOption);
client.setMenuEntries(entries);
}
// Inventory item menu
if (widgetId == WidgetInfo.INVENTORY.getId() && editorMode)
{
int itemId = firstEntry.getIdentifier();
if (itemId == -1)
{
return;
}
MenuEntry[] menuList = new MenuEntry[GROUPS.size()];
int num = 0;
for (final String groupName : GROUPS)
{
final String group = getTag(itemId);
final MenuEntry newMenu = new MenuEntry();
final Color color = getGroupNameColor(groupName);
newMenu.setOption(group != null && groupName.equals(group) ? MENU_REMOVE : MENU_SET);
newMenu.setTarget("<col=" + getHexColor(MoreObjects.firstNonNull(color, Color.WHITE)) + ">" + groupName);
newMenu.setIdentifier(itemId);
newMenu.setParam1(widgetId);
newMenu.setType(MenuAction.RUNELITE.getId());
menuList[num++] = newMenu;
}
client.setMenuEntries(menuList);
}
}
@Subscribe
public void onItemContainerChanged(ItemContainerChanged itemContainerChanged)
{
ItemContainer itemContainer = itemContainerChanged.getItemContainer();
if (itemContainer == client.getItemContainer(InventoryID.INVENTORY))
{
checkForTags(itemContainer);
}
}
private void checkForTags(ItemContainer itemContainer)
{
hasTaggedItems = false;
if (itemContainer == null)
{
return;
}
Item[] items = itemContainer.getItems();
if (items != null)
{
for (Item item : items)
{
if (item == null)
{
continue;
}
String tag = getTag(item.getId());
if (tag == null)
{
continue;
}
hasTaggedItems = true;
return;
}
}
}
private String getHexColor(final Color color)
{
return String.format("%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue());
}
Color getGroupNameColor(final String name)
{
switch (name)
{
case SETNAME_GROUP_1:
return config.getGroup1Color();
case SETNAME_GROUP_2:
return config.getGroup2Color();
case SETNAME_GROUP_3:
return config.getGroup3Color();
case SETNAME_GROUP_4:
return config.getGroup4Color();
}
return null;
}
}