Added sorting on the item overlay, items now appear in the order that they'll be picked up (#320)

Changed item menu sorting:
  Hidden items now appear at the bottom of the menu, so that hidden items don't get picked up over lower value non-hidden items

  Added a config option to put hidden items below "Walk here"
    this option makes it so that you can't accidentally pick up hidden items

  Added a config option to sort by GE value
    this option means that you'll always pick up highest GE value items first with left click
    this option also sorts the overlay by GE value
This commit is contained in:
James
2019-05-20 13:31:20 -07:00
committed by Kyleeld
parent a327cc2a42
commit 68fa0ef253
3 changed files with 1793 additions and 1673 deletions

View File

@@ -1,439 +1,461 @@
/*
* Copyright (c) 2017, Aria <aria@ar1as.space>
* 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.grounditems;
import java.awt.Color;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.plugins.grounditems.config.ItemHighlightMode;
import net.runelite.client.plugins.grounditems.config.MenuHighlightMode;
import net.runelite.client.plugins.grounditems.config.PriceDisplayMode;
import net.runelite.client.plugins.grounditems.config.TimerDisplayMode;
import net.runelite.client.plugins.grounditems.config.ValueCalculationMode;
@ConfigGroup("grounditems")
public interface GroundItemsConfig extends Config
{
@ConfigItem(
keyName = "highlightedItems",
name = "Highlighted Items",
description = "Configures specifically highlighted ground items. Format: (item), (item)",
position = 0
)
default String getHighlightItems()
{
return "";
}
@ConfigItem(
keyName = "highlightedItems",
name = "",
description = ""
)
void setHighlightedItem(String key);
@ConfigItem(
keyName = "hiddenItems",
name = "Hidden Items",
description = "Configures hidden ground items. Format: (item), (item)",
position = 1
)
default String getHiddenItems()
{
return "Vial, Ashes, Coins, Bones, Bucket, Jug, Seaweed";
}
@ConfigItem(
keyName = "hiddenItems",
name = "",
description = ""
)
void setHiddenItems(String key);
@ConfigItem(
keyName = "showHighlightedOnly",
name = "Show Highlighted items only",
description = "Configures whether or not to draw items only on your highlighted list",
position = 2
)
default boolean showHighlightedOnly()
{
return false;
}
@ConfigItem(
keyName = "dontHideUntradeables",
name = "Do not hide untradeables",
description = "Configures whether or not untradeable items ignore hiding under settings",
position = 3
)
default boolean dontHideUntradeables()
{
return true;
}
@ConfigItem(
keyName = "showMenuItemQuantities",
name = "Show Menu Item Quantities",
description = "Configures whether or not to show the item quantities in the menu",
position = 4
)
default boolean showMenuItemQuantities()
{
return true;
}
@ConfigItem(
keyName = "recolorMenuHiddenItems",
name = "Recolor Menu Hidden Items",
description = "Configures whether or not hidden items in right click menu will be recolored",
position = 5
)
default boolean recolorMenuHiddenItems()
{
return false;
}
@ConfigItem(
keyName = "highlightTiles",
name = "Highlight Tiles",
description = "Configures whether or not to highlight tiles containing ground items",
position = 6
)
default boolean highlightTiles()
{
return false;
}
@ConfigItem(
keyName = "notifyHighlightedDrops",
name = "Notify for Highlighted drops",
description = "Configures whether or not to notify for drops on your highlighted list",
position = 7
)
default boolean notifyHighlightedDrops()
{
return false;
}
@ConfigItem(
keyName = "priceDisplayMode",
name = "Price Display Mode",
description = "Configures what price types are shown alongside of ground item name",
position = 8
)
default PriceDisplayMode priceDisplayMode()
{
return PriceDisplayMode.BOTH;
}
@ConfigItem(
keyName = "itemHighlightMode",
name = "Item Highlight Mode",
description = "Configures how ground items will be highlighted",
position = 9
)
default ItemHighlightMode itemHighlightMode()
{
return ItemHighlightMode.BOTH;
}
@ConfigItem(
keyName = "menuHighlightMode",
name = "Menu Highlight Mode",
description = "Configures what to highlight in right-click menu",
position = 10
)
default MenuHighlightMode menuHighlightMode()
{
return MenuHighlightMode.NAME;
}
@ConfigItem(
keyName = "highlightValueCalculation",
name = "Highlight Value Calculation",
description = "Configures which coin value is used to determine highlight color",
position = 11
)
default ValueCalculationMode valueCalculationMode()
{
return ValueCalculationMode.HIGHEST;
}
@ConfigItem(
keyName = "highlightOverValue2",
name = "Highlight > Value",
description = "Configures highlighted ground items over either GE or HA value",
position = 12
)
default int getHighlightOverValue()
{
return 0;
}
@ConfigItem(
keyName = "hideUnderValue",
name = "Hide < Value",
description = "Configures hidden ground items under both GE and HA value",
position = 13
)
default int getHideUnderValue()
{
return 0;
}
@ConfigItem(
keyName = "defaultColor",
name = "Default items color",
description = "Configures the color for default, non-highlighted items",
position = 14
)
default Color defaultColor()
{
return Color.WHITE;
}
@ConfigItem(
keyName = "highlightedColor",
name = "Highlighted items color",
description = "Configures the color for highlighted items",
position = 15
)
default Color highlightedColor()
{
return Color.decode("#AA00FF");
}
@ConfigItem(
keyName = "hiddenColor",
name = "Hidden items color",
description = "Configures the color for hidden items in right-click menu and when holding ALT",
position = 16
)
default Color hiddenColor()
{
return Color.GRAY;
}
@ConfigItem(
keyName = "lowValueColor",
name = "Low value items color",
description = "Configures the color for low value items",
position = 17
)
default Color lowValueColor()
{
return Color.decode("#66B2FF");
}
@ConfigItem(
keyName = "lowValuePrice",
name = "Low value price",
description = "Configures the start price for low value items",
position = 18
)
default int lowValuePrice()
{
return 20000;
}
@ConfigItem(
keyName = "notifyLowValueDrops",
name = "Notify for low value drops",
description = "Configures whether or not to notify for drops of low value",
position = 19
)
default boolean notifyLowValueDrops()
{
return false;
}
@ConfigItem(
keyName = "mediumValueColor",
name = "Medium value items color",
description = "Configures the color for medium value items",
position = 20
)
default Color mediumValueColor()
{
return Color.decode("#99FF99");
}
@ConfigItem(
keyName = "mediumValuePrice",
name = "Medium value price",
description = "Configures the start price for medium value items",
position = 21
)
default int mediumValuePrice()
{
return 100000;
}
@ConfigItem(
keyName = "notifyMediumValueDrops",
name = "Notify for medium value drops",
description = "Configures whether or not to notify for drops of medium value",
position = 22
)
default boolean notifyMediumValueDrops()
{
return false;
}
@ConfigItem(
keyName = "highValueColor",
name = "High value items color",
description = "Configures the color for high value items",
position = 23
)
default Color highValueColor()
{
return Color.decode("#FF9600");
}
@ConfigItem(
keyName = "highValuePrice",
name = "High value price",
description = "Configures the start price for high value items",
position = 24
)
default int highValuePrice()
{
return 1000000;
}
@ConfigItem(
keyName = "notifyHighValueDrops",
name = "Notify for high value drops",
description = "Configures whether or not to notify for drops of high value",
position = 25
)
default boolean notifyHighValueDrops()
{
return false;
}
@ConfigItem(
keyName = "insaneValueColor",
name = "Insane value items color",
description = "Configures the color for insane value items",
position = 26
)
default Color insaneValueColor()
{
return Color.decode("#FF66B2");
}
@ConfigItem(
keyName = "insaneValuePrice",
name = "Insane value price",
description = "Configures the start price for insane value items",
position = 27
)
default int insaneValuePrice()
{
return 10000000;
}
@ConfigItem(
keyName = "notifyInsaneValueDrops",
name = "Notify for insane value drops",
description = "Configures whether or not to notify for drops of insane value",
position = 28
)
default boolean notifyInsaneValueDrops()
{
return false;
}
@ConfigItem(
keyName = "onlyShowLoot",
name = "Only show loot",
description = "Only shows drops from NPCs and players",
position = 29
)
default boolean onlyShowLoot()
{
return false;
}
@ConfigItem(
keyName = "doubleTapDelay",
name = "Delay for double-tap ALT to hide",
description = "Decrease this number if you accidentally hide ground items often. (0 = Disabled)",
position = 30
)
default int doubleTapDelay()
{
return 250;
}
@ConfigItem(
keyName = "collapseEntries",
name = "Collapse ground item menu entries",
description = "Collapses ground item menu entries together and appends count",
position = 31
)
default boolean collapseEntries()
{
return false;
}
@ConfigItem(
keyName = "removeIgnored",
name = "Hide Ignored",
description = "Remove take option for items that are on the hidden items list.",
position = 32
)
default boolean removeIgnored()
{
return false;
}
@ConfigItem(
keyName = "toggleOutline",
name = "Text Outline",
description = "Use an outline around text instead of a text shadow",
position = 33
)
default boolean toggleOutline()
{
return false;
}
@ConfigItem(
keyName = "showGroundItemDuration",
name = "Show time remaining",
description = "Turn on a countdown timer to show how long an item will remain on the ground",
position = 34
)
default TimerDisplayMode showGroundItemDuration()
{
return TimerDisplayMode.HOTKEY_PRESSED;
}
}
/*
* Copyright (c) 2017, Aria <aria@ar1as.space>
* 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.grounditems;
import java.awt.Color;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.plugins.grounditems.config.ItemHighlightMode;
import net.runelite.client.plugins.grounditems.config.MenuHighlightMode;
import net.runelite.client.plugins.grounditems.config.PriceDisplayMode;
import net.runelite.client.plugins.grounditems.config.TimerDisplayMode;
import net.runelite.client.plugins.grounditems.config.ValueCalculationMode;
@ConfigGroup("grounditems")
public interface GroundItemsConfig extends Config
{
@ConfigItem(
keyName = "highlightedItems",
name = "Highlighted Items",
description = "Configures specifically highlighted ground items. Format: (item), (item)",
position = 0
)
default String getHighlightItems()
{
return "";
}
@ConfigItem(
keyName = "highlightedItems",
name = "",
description = ""
)
void setHighlightedItem(String key);
@ConfigItem(
keyName = "hiddenItems",
name = "Hidden Items",
description = "Configures hidden ground items. Format: (item), (item)",
position = 1
)
default String getHiddenItems()
{
return "Vial, Ashes, Coins, Bones, Bucket, Jug, Seaweed";
}
@ConfigItem(
keyName = "hiddenItems",
name = "",
description = ""
)
void setHiddenItems(String key);
@ConfigItem(
keyName = "showHighlightedOnly",
name = "Show Highlighted items only",
description = "Configures whether or not to draw items only on your highlighted list",
position = 2
)
default boolean showHighlightedOnly()
{
return false;
}
@ConfigItem(
keyName = "dontHideUntradeables",
name = "Do not hide untradeables",
description = "Configures whether or not untradeable items ignore hiding under settings",
position = 3
)
default boolean dontHideUntradeables()
{
return true;
}
@ConfigItem(
keyName = "showMenuItemQuantities",
name = "Show Menu Item Quantities",
description = "Configures whether or not to show the item quantities in the menu",
position = 4
)
default boolean showMenuItemQuantities()
{
return true;
}
@ConfigItem(
keyName = "recolorMenuHiddenItems",
name = "Recolor Menu Hidden Items",
description = "Configures whether or not hidden items in right click menu will be recolored",
position = 5
)
default boolean recolorMenuHiddenItems()
{
return false;
}
@ConfigItem(
keyName = "highlightTiles",
name = "Highlight Tiles",
description = "Configures whether or not to highlight tiles containing ground items",
position = 6
)
default boolean highlightTiles()
{
return false;
}
@ConfigItem(
keyName = "notifyHighlightedDrops",
name = "Notify for Highlighted drops",
description = "Configures whether or not to notify for drops on your highlighted list",
position = 7
)
default boolean notifyHighlightedDrops()
{
return false;
}
@ConfigItem(
keyName = "priceDisplayMode",
name = "Price Display Mode",
description = "Configures what price types are shown alongside of ground item name",
position = 8
)
default PriceDisplayMode priceDisplayMode()
{
return PriceDisplayMode.BOTH;
}
@ConfigItem(
keyName = "itemHighlightMode",
name = "Item Highlight Mode",
description = "Configures how ground items will be highlighted",
position = 9
)
default ItemHighlightMode itemHighlightMode()
{
return ItemHighlightMode.BOTH;
}
@ConfigItem(
keyName = "menuHighlightMode",
name = "Menu Highlight Mode",
description = "Configures what to highlight in right-click menu",
position = 10
)
default MenuHighlightMode menuHighlightMode()
{
return MenuHighlightMode.NAME;
}
@ConfigItem(
keyName = "highlightValueCalculation",
name = "Highlight Value Calculation",
description = "Configures which coin value is used to determine highlight color",
position = 11
)
default ValueCalculationMode valueCalculationMode()
{
return ValueCalculationMode.HIGHEST;
}
@ConfigItem(
keyName = "highlightOverValue2",
name = "Highlight > Value",
description = "Configures highlighted ground items over either GE or HA value",
position = 12
)
default int getHighlightOverValue()
{
return 0;
}
@ConfigItem(
keyName = "hideUnderValue",
name = "Hide < Value",
description = "Configures hidden ground items under both GE and HA value",
position = 13
)
default int getHideUnderValue()
{
return 0;
}
@ConfigItem(
keyName = "defaultColor",
name = "Default items color",
description = "Configures the color for default, non-highlighted items",
position = 14
)
default Color defaultColor()
{
return Color.WHITE;
}
@ConfigItem(
keyName = "highlightedColor",
name = "Highlighted items color",
description = "Configures the color for highlighted items",
position = 15
)
default Color highlightedColor()
{
return Color.decode("#AA00FF");
}
@ConfigItem(
keyName = "hiddenColor",
name = "Hidden items color",
description = "Configures the color for hidden items in right-click menu and when holding ALT",
position = 16
)
default Color hiddenColor()
{
return Color.GRAY;
}
@ConfigItem(
keyName = "lowValueColor",
name = "Low value items color",
description = "Configures the color for low value items",
position = 17
)
default Color lowValueColor()
{
return Color.decode("#66B2FF");
}
@ConfigItem(
keyName = "lowValuePrice",
name = "Low value price",
description = "Configures the start price for low value items",
position = 18
)
default int lowValuePrice()
{
return 20000;
}
@ConfigItem(
keyName = "notifyLowValueDrops",
name = "Notify for low value drops",
description = "Configures whether or not to notify for drops of low value",
position = 19
)
default boolean notifyLowValueDrops()
{
return false;
}
@ConfigItem(
keyName = "mediumValueColor",
name = "Medium value items color",
description = "Configures the color for medium value items",
position = 20
)
default Color mediumValueColor()
{
return Color.decode("#99FF99");
}
@ConfigItem(
keyName = "mediumValuePrice",
name = "Medium value price",
description = "Configures the start price for medium value items",
position = 21
)
default int mediumValuePrice()
{
return 100000;
}
@ConfigItem(
keyName = "notifyMediumValueDrops",
name = "Notify for medium value drops",
description = "Configures whether or not to notify for drops of medium value",
position = 22
)
default boolean notifyMediumValueDrops()
{
return false;
}
@ConfigItem(
keyName = "highValueColor",
name = "High value items color",
description = "Configures the color for high value items",
position = 23
)
default Color highValueColor()
{
return Color.decode("#FF9600");
}
@ConfigItem(
keyName = "highValuePrice",
name = "High value price",
description = "Configures the start price for high value items",
position = 24
)
default int highValuePrice()
{
return 1000000;
}
@ConfigItem(
keyName = "notifyHighValueDrops",
name = "Notify for high value drops",
description = "Configures whether or not to notify for drops of high value",
position = 25
)
default boolean notifyHighValueDrops()
{
return false;
}
@ConfigItem(
keyName = "insaneValueColor",
name = "Insane value items color",
description = "Configures the color for insane value items",
position = 26
)
default Color insaneValueColor()
{
return Color.decode("#FF66B2");
}
@ConfigItem(
keyName = "insaneValuePrice",
name = "Insane value price",
description = "Configures the start price for insane value items",
position = 27
)
default int insaneValuePrice()
{
return 10000000;
}
@ConfigItem(
keyName = "notifyInsaneValueDrops",
name = "Notify for insane value drops",
description = "Configures whether or not to notify for drops of insane value",
position = 28
)
default boolean notifyInsaneValueDrops()
{
return false;
}
@ConfigItem(
keyName = "onlyShowLoot",
name = "Only show loot",
description = "Only shows drops from NPCs and players",
position = 29
)
default boolean onlyShowLoot()
{
return false;
}
@ConfigItem(
keyName = "doubleTapDelay",
name = "Delay for double-tap ALT to hide",
description = "Decrease this number if you accidentally hide ground items often. (0 = Disabled)",
position = 30
)
default int doubleTapDelay()
{
return 250;
}
@ConfigItem(
keyName = "collapseEntries",
name = "Collapse ground item menu entries",
description = "Collapses ground item menu entries together and appends count",
position = 31
)
default boolean collapseEntries()
{
return false;
}
@ConfigItem(
keyName = "removeIgnored",
name = "Hide Ignored",
description = "Remove take option for items that are on the hidden items list.",
position = 32
)
default boolean removeIgnored()
{
return false;
}
@ConfigItem(
keyName = "toggleOutline",
name = "Text Outline",
description = "Use an outline around text instead of a text shadow",
position = 33
)
default boolean toggleOutline()
{
return false;
}
@ConfigItem(
keyName = "showGroundItemDuration",
name = "Show time remaining",
description = "Turn on a countdown timer to show how long an item will remain on the ground",
position = 34
)
default TimerDisplayMode showGroundItemDuration()
{
return TimerDisplayMode.HOTKEY_PRESSED;
}
@ConfigItem(
keyName = "sortByGEPrice",
name = "Sort by GE price",
description = "Sorts ground items by GE price, instead of alch value",
position = 35
)
default boolean sortByGEPrice()
{
return false;
}
@ConfigItem(
keyName = "rightClickHidden",
name = "Right click hidden items",
description = "Places hidden items below the 'Walk here' option, making it so that you need to right click to pick them up",
position = 36
)
default boolean rightClickHidden()
{
return false;
}
}

View File

@@ -1,475 +1,483 @@
/*
* Copyright (c) 2017, Aria <aria@ar1as.space>
* Copyright (c) 2018, 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.plugins.grounditems;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.time.Duration;
import java.time.Instant;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.Perspective;
import net.runelite.api.Player;
import net.runelite.api.Point;
import net.runelite.api.Tile;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import static net.runelite.client.plugins.grounditems.config.ItemHighlightMode.MENU;
import net.runelite.client.plugins.grounditems.config.PriceDisplayMode;
import net.runelite.client.plugins.grounditems.config.TimerDisplayMode;
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.OverlayUtil;
import net.runelite.client.ui.overlay.components.BackgroundComponent;
import net.runelite.client.ui.overlay.components.ProgressPieComponent;
import net.runelite.client.ui.overlay.components.TextComponent;
import net.runelite.client.util.StackFormatter;
public class GroundItemsOverlay extends Overlay
{
private static final int MAX_DISTANCE = 2500;
// We must offset the text on the z-axis such that
// it doesn't obscure the ground items below it.
private static final int OFFSET_Z = 20;
// The game won't send anything higher than this value to the plugin -
// so we replace any item quantity higher with "Lots" instead.
private static final int MAX_QUANTITY = 65535;
// The 15 pixel gap between each drawn ground item.
private static final int STRING_GAP = 15;
// Size of the hidden/highlight boxes
private static final int RECTANGLE_SIZE = 8;
private static final int TIMER_OVERLAY_DIAMETER = 10;
private static final int PUBLIC_ITEM_DURATION_MILLIS = 60000;
private static final float WARNING_THRESHOLD = 0.25f;
private static final Color PUBLIC_TIMER_COLOR = Color.YELLOW;
private static final Color PRIVATE_TIMER_COLOR = Color.GREEN;
private static final Color PUBLIC_WARNING_TIMER_COLOR = Color.RED;
private final Client client;
private final GroundItemsPlugin plugin;
private final GroundItemsConfig config;
private final StringBuilder itemStringBuilder = new StringBuilder();
private final BackgroundComponent backgroundComponent = new BackgroundComponent();
private final TextComponent textComponent = new TextComponent();
private final ProgressPieComponent progressPieComponent = new ProgressPieComponent();
private final Map<WorldPoint, Integer> offsetMap = new HashMap<>();
@Inject
private GroundItemsOverlay(Client client, GroundItemsPlugin plugin, GroundItemsConfig config)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
this.client = client;
this.plugin = plugin;
this.config = config;
}
@Override
public Dimension render(Graphics2D graphics)
{
final boolean dontShowOverlay = (config.itemHighlightMode() == MENU || plugin.isHideAll()) && !plugin.isHotKeyPressed();
if (dontShowOverlay && !config.highlightTiles())
{
return null;
}
final FontMetrics fm = graphics.getFontMetrics();
final Player player = client.getLocalPlayer();
if (player == null || client.getViewportWidget() == null)
{
return null;
}
offsetMap.clear();
final LocalPoint localLocation = player.getLocalLocation();
final Point mousePos = client.getMouseCanvasPosition();
Collection<GroundItem> groundItemList = GroundItemsPlugin.getCollectedGroundItems().values();
GroundItem topGroundItem = null;
if (plugin.isHotKeyPressed())
{
// Make copy of ground items because we are going to modify them here, and the array list supports our
// desired behaviour here
groundItemList = new ArrayList<>(groundItemList);
final java.awt.Point awtMousePos = new java.awt.Point(mousePos.getX(), mousePos.getY());
GroundItem groundItem = null;
for (GroundItem item : groundItemList)
{
item.setOffset(offsetMap.compute(item.getLocation(), (k, v) -> v != null ? v + 1 : 0));
if (groundItem != null)
{
continue;
}
if (plugin.getTextBoxBounds() != null
&& item.equals(plugin.getTextBoxBounds().getValue())
&& plugin.getTextBoxBounds().getKey().contains(awtMousePos))
{
groundItem = item;
continue;
}
if (plugin.getHiddenBoxBounds() != null
&& item.equals(plugin.getHiddenBoxBounds().getValue())
&& plugin.getHiddenBoxBounds().getKey().contains(awtMousePos))
{
groundItem = item;
continue;
}
if (plugin.getHighlightBoxBounds() != null
&& item.equals(plugin.getHighlightBoxBounds().getValue())
&& plugin.getHighlightBoxBounds().getKey().contains(awtMousePos))
{
groundItem = item;
}
}
if (groundItem != null)
{
groundItemList.remove(groundItem);
groundItemList.add(groundItem);
topGroundItem = groundItem;
}
}
plugin.setTextBoxBounds(null);
plugin.setHiddenBoxBounds(null);
plugin.setHighlightBoxBounds(null);
final boolean onlyShowLoot = config.onlyShowLoot();
for (GroundItem item : groundItemList)
{
final LocalPoint groundPoint = LocalPoint.fromWorld(client, item.getLocation());
if (groundPoint == null || localLocation.distanceTo(groundPoint) > MAX_DISTANCE
|| (onlyShowLoot && !item.isMine()))
{
continue;
}
final Color highlighted = plugin.getHighlighted(item.getName(), item.getGePrice(), item.getHaPrice());
final Color hidden = plugin.getHidden(item.getName(), item.getGePrice(), item.getHaPrice(), item.isTradeable());
if (highlighted == null && !plugin.isHotKeyPressed())
{
// Do not display hidden items
if (hidden != null)
{
continue;
}
// Do not display non-highlighted items
if (config.showHighlightedOnly())
{
continue;
}
}
final Color color = plugin.getItemColor(highlighted, hidden);
if (config.highlightTiles())
{
final Polygon poly = Perspective.getCanvasTilePoly(client, groundPoint);
if (poly != null)
{
OverlayUtil.renderPolygon(graphics, poly, color);
}
}
if (dontShowOverlay)
{
continue;
}
itemStringBuilder.append(item.getName());
if (item.getQuantity() > 1)
{
if (item.getQuantity() >= MAX_QUANTITY)
{
itemStringBuilder.append(" (Lots!)");
}
else
{
itemStringBuilder.append(" (")
.append(StackFormatter.quantityToStackSize(item.getQuantity()))
.append(")");
}
}
if (config.priceDisplayMode() == PriceDisplayMode.BOTH)
{
if (item.getGePrice() > 0)
{
itemStringBuilder.append(" (EX: ")
.append(StackFormatter.quantityToStackSize(item.getGePrice()))
.append(" gp)");
}
if (item.getHaPrice() > 0)
{
itemStringBuilder.append(" (HA: ")
.append(StackFormatter.quantityToStackSize(item.getHaPrice()))
.append(" gp)");
}
}
else if (config.priceDisplayMode() != PriceDisplayMode.OFF)
{
final int price = config.priceDisplayMode() == PriceDisplayMode.GE
? item.getGePrice()
: item.getHaPrice();
if (price > 0)
{
itemStringBuilder
.append(" (")
.append(StackFormatter.quantityToStackSize(price))
.append(" gp)");
}
}
final String itemString = itemStringBuilder.toString();
itemStringBuilder.setLength(0);
if (item.getHeight() == -1)
{
final Tile[][][] sceneTiles = client.getScene().getTiles();
final Tile itemTile = sceneTiles[client.getPlane()][groundPoint.getSceneX()][groundPoint.getSceneY()];
item.setHeight(itemTile.getItemLayer().getHeight());
}
final Point textPoint = Perspective.getCanvasTextLocation(client,
graphics,
groundPoint,
itemString,
item.getHeight() + OFFSET_Z);
if (textPoint == null)
{
continue;
}
final int offset = plugin.isHotKeyPressed()
? item.getOffset()
: offsetMap.compute(item.getLocation(), (k, v) -> v != null ? v + 1 : 0);
final int textX = textPoint.getX();
final int textY = textPoint.getY() - (STRING_GAP * offset);
if (plugin.isHotKeyPressed())
{
final int stringWidth = fm.stringWidth(itemString);
final int stringHeight = fm.getHeight();
// Item bounds
int x = textX - 2;
int y = textY - stringHeight - 2;
int width = stringWidth + 4;
int height = stringHeight + 4;
final Rectangle itemBounds = new Rectangle(x, y, width, height);
// Hidden box
x += width + 2;
y = textY - (RECTANGLE_SIZE + stringHeight) / 2;
width = height = RECTANGLE_SIZE;
final Rectangle itemHiddenBox = new Rectangle(x, y, width, height);
// Highlight box
x += width + 2;
final Rectangle itemHighlightBox = new Rectangle(x, y, width, height);
boolean mouseInBox = itemBounds.contains(mousePos.getX(), mousePos.getY());
boolean mouseInHiddenBox = itemHiddenBox.contains(mousePos.getX(), mousePos.getY());
boolean mouseInHighlightBox = itemHighlightBox.contains(mousePos.getX(), mousePos.getY());
if (mouseInBox)
{
plugin.setTextBoxBounds(new SimpleEntry<>(itemBounds, item));
}
else if (mouseInHiddenBox)
{
plugin.setHiddenBoxBounds(new SimpleEntry<>(itemHiddenBox, item));
}
else if (mouseInHighlightBox)
{
plugin.setHighlightBoxBounds(new SimpleEntry<>(itemHighlightBox, item));
}
boolean topItem = topGroundItem == item;
// Draw background if hovering
if (topItem && (mouseInBox || mouseInHiddenBox || mouseInHighlightBox))
{
backgroundComponent.setRectangle(itemBounds);
backgroundComponent.render(graphics);
}
// Draw hidden box
drawRectangle(graphics, itemHiddenBox, topItem && mouseInHiddenBox ? Color.RED : color, hidden != null, true);
// Draw highlight box
drawRectangle(graphics, itemHighlightBox, topItem && mouseInHighlightBox ? Color.GREEN : color, highlighted != null, false);
}
if (config.showGroundItemDuration() == TimerDisplayMode.ALWAYS
|| (config.showGroundItemDuration() == TimerDisplayMode.HOTKEY_PRESSED && plugin.isHotKeyPressed()))
{
drawTimerOverlay(graphics, new java.awt.Point(textX, textY), item);
}
if (config.toggleOutline())
{
graphics.setColor(Color.BLACK);
graphics.drawString(itemString, textX + 1, textY + 1);
graphics.drawString(itemString, textX - 1, textY - 1);
graphics.drawString(itemString, textX - 1, textY + 1);
graphics.drawString(itemString, textX + 1, textY - 1);
}
textComponent.setText(itemString);
textComponent.setColor(color);
textComponent.setPosition(new java.awt.Point(textX, textY));
textComponent.render(graphics);
}
return null;
}
private void drawRectangle(Graphics2D graphics, Rectangle rect, Color color, boolean inList, boolean hiddenBox)
{
graphics.setColor(Color.BLACK);
graphics.drawRect(rect.x + 1, rect.y + 1, rect.width, rect.height);
graphics.setColor(color);
graphics.draw(rect);
if (inList)
{
graphics.fill(rect);
}
graphics.setColor(Color.WHITE);
// Minus symbol
graphics.drawLine
(
rect.x + 2,
rect.y + (rect.height / 2),
rect.x + rect.width - 2,
rect.y + (rect.height / 2)
);
if (!hiddenBox)
{
// Plus symbol
graphics.drawLine
(
rect.x + (rect.width / 2),
rect.y + 2,
rect.x + (rect.width / 2),
rect.y + rect.height - 2
);
}
}
private void drawTimerOverlay(Graphics2D graphics, java.awt.Point location, GroundItem item)
{
progressPieComponent.setDiameter(TIMER_OVERLAY_DIAMETER);
int x = (int) location.getX() - TIMER_OVERLAY_DIAMETER;
int y = (int) location.getY() - TIMER_OVERLAY_DIAMETER / 2;
progressPieComponent.setPosition(new Point(x, y));
double millisOnGround = Duration.between(item.getDroppedInstant(), Instant.now()).toMillis();
boolean isPubliclyVisible = !item.isAlwaysPrivate() && millisOnGround > item.getDurationMillis();
double timeLeftRelative;
Color fillColor;
if (isPubliclyVisible || !item.isOwnedByPlayer())
{
if (item.isOwnedByPlayer())
{
timeLeftRelative = getTimeLeftRelative(millisOnGround - PUBLIC_ITEM_DURATION_MILLIS, PUBLIC_ITEM_DURATION_MILLIS);
}
else
{
timeLeftRelative = getTimeLeftRelative(millisOnGround, PUBLIC_ITEM_DURATION_MILLIS);
}
if (timeLeftRelative < WARNING_THRESHOLD)
{
fillColor = PUBLIC_WARNING_TIMER_COLOR;
}
else
{
fillColor = PUBLIC_TIMER_COLOR;
}
}
else
{
timeLeftRelative = getTimeLeftRelative(millisOnGround, item.getDurationMillis());
fillColor = PRIVATE_TIMER_COLOR;
}
// don't draw timer for any permanently spawned items or broken edge cases
if (timeLeftRelative > 1 || timeLeftRelative < 0)
{
return;
}
progressPieComponent.setFill(fillColor);
progressPieComponent.setBorderColor(fillColor);
progressPieComponent.setProgress(timeLeftRelative);
progressPieComponent.render(graphics);
}
private double getTimeLeftRelative(double millisOnGround, int duration)
{
return (duration - millisOnGround) / duration;
}
}
/*
* Copyright (c) 2017, Aria <aria@ar1as.space>
* Copyright (c) 2018, 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.plugins.grounditems;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.time.Duration;
import java.time.Instant;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.Comparator;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.Perspective;
import net.runelite.api.Player;
import net.runelite.api.Point;
import net.runelite.api.Tile;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import static net.runelite.client.plugins.grounditems.config.ItemHighlightMode.MENU;
import net.runelite.client.plugins.grounditems.config.PriceDisplayMode;
import net.runelite.client.plugins.grounditems.config.TimerDisplayMode;
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.OverlayUtil;
import net.runelite.client.ui.overlay.components.BackgroundComponent;
import net.runelite.client.ui.overlay.components.ProgressPieComponent;
import net.runelite.client.ui.overlay.components.TextComponent;
import net.runelite.client.util.StackFormatter;
public class GroundItemsOverlay extends Overlay
{
private static final int MAX_DISTANCE = 2500;
// We must offset the text on the z-axis such that
// it doesn't obscure the ground items below it.
private static final int OFFSET_Z = 20;
// The game won't send anything higher than this value to the plugin -
// so we replace any item quantity higher with "Lots" instead.
private static final int MAX_QUANTITY = 65535;
// The 15 pixel gap between each drawn ground item.
private static final int STRING_GAP = 15;
// Size of the hidden/highlight boxes
private static final int RECTANGLE_SIZE = 8;
private static final int TIMER_OVERLAY_DIAMETER = 10;
private static final int PUBLIC_ITEM_DURATION_MILLIS = 60000;
private static final float WARNING_THRESHOLD = 0.25f;
private static final Color PUBLIC_TIMER_COLOR = Color.YELLOW;
private static final Color PRIVATE_TIMER_COLOR = Color.GREEN;
private static final Color PUBLIC_WARNING_TIMER_COLOR = Color.RED;
private final Client client;
private final GroundItemsPlugin plugin;
private final GroundItemsConfig config;
private final StringBuilder itemStringBuilder = new StringBuilder();
private final BackgroundComponent backgroundComponent = new BackgroundComponent();
private final TextComponent textComponent = new TextComponent();
private final ProgressPieComponent progressPieComponent = new ProgressPieComponent();
private final Map<WorldPoint, Integer> offsetMap = new HashMap<>();
@Inject
private GroundItemsOverlay(Client client, GroundItemsPlugin plugin, GroundItemsConfig config)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
this.client = client;
this.plugin = plugin;
this.config = config;
}
@Override
public Dimension render(Graphics2D graphics)
{
final boolean dontShowOverlay = (config.itemHighlightMode() == MENU || plugin.isHideAll()) && !plugin.isHotKeyPressed();
if (dontShowOverlay && !config.highlightTiles())
{
return null;
}
final FontMetrics fm = graphics.getFontMetrics();
final Player player = client.getLocalPlayer();
if (player == null || client.getViewportWidget() == null)
{
return null;
}
offsetMap.clear();
final LocalPoint localLocation = player.getLocalLocation();
final Point mousePos = client.getMouseCanvasPosition();
Collection<GroundItem> groundItemList = GroundItemsPlugin.getCollectedGroundItems().values();
GroundItem topGroundItem = null;
if (plugin.isHotKeyPressed())
{
// Make copy of ground items because we are going to modify them here, and the array list supports our
// desired behaviour here
groundItemList = new ArrayList<>(groundItemList);
final java.awt.Point awtMousePos = new java.awt.Point(mousePos.getX(), mousePos.getY());
GroundItem groundItem = null;
for (GroundItem item : groundItemList)
{
item.setOffset(offsetMap.compute(item.getLocation(), (k, v) -> v != null ? v + 1 : 0));
if (groundItem != null)
{
continue;
}
if (plugin.getTextBoxBounds() != null
&& item.equals(plugin.getTextBoxBounds().getValue())
&& plugin.getTextBoxBounds().getKey().contains(awtMousePos))
{
groundItem = item;
continue;
}
if (plugin.getHiddenBoxBounds() != null
&& item.equals(plugin.getHiddenBoxBounds().getValue())
&& plugin.getHiddenBoxBounds().getKey().contains(awtMousePos))
{
groundItem = item;
continue;
}
if (plugin.getHighlightBoxBounds() != null
&& item.equals(plugin.getHighlightBoxBounds().getValue())
&& plugin.getHighlightBoxBounds().getKey().contains(awtMousePos))
{
groundItem = item;
}
}
if (groundItem != null)
{
groundItemList.remove(groundItem);
groundItemList.add(groundItem);
topGroundItem = groundItem;
}
}
plugin.setTextBoxBounds(null);
plugin.setHiddenBoxBounds(null);
plugin.setHighlightBoxBounds(null);
final boolean onlyShowLoot = config.onlyShowLoot();
List<GroundItem> groundItemListAsList = new ArrayList<>(groundItemList); // make a copy so we can non-destructively modify the list
Comparator<GroundItem> compareByHaPrice = Comparator.comparingInt(GroundItem::getHaPrice);
Comparator<GroundItem> compareByGePrice = Comparator.comparingInt(GroundItem::getGePrice);
groundItemListAsList.sort(config.sortByGEPrice() ? compareByGePrice : compareByHaPrice);
for (GroundItem item : groundItemListAsList)
{
final LocalPoint groundPoint = LocalPoint.fromWorld(client, item.getLocation());
if (groundPoint == null || localLocation.distanceTo(groundPoint) > MAX_DISTANCE
|| (onlyShowLoot && !item.isMine()))
{
continue;
}
final Color highlighted = plugin.getHighlighted(item.getName(), item.getGePrice(), item.getHaPrice());
final Color hidden = plugin.getHidden(item.getName(), item.getGePrice(), item.getHaPrice(), item.isTradeable());
if (highlighted == null && !plugin.isHotKeyPressed())
{
// Do not display hidden items
if (hidden != null)
{
continue;
}
// Do not display non-highlighted items
if (config.showHighlightedOnly())
{
continue;
}
}
final Color color = plugin.getItemColor(highlighted, hidden);
if (config.highlightTiles())
{
final Polygon poly = Perspective.getCanvasTilePoly(client, groundPoint);
if (poly != null)
{
OverlayUtil.renderPolygon(graphics, poly, color);
}
}
if (dontShowOverlay)
{
continue;
}
itemStringBuilder.append(item.getName());
if (item.getQuantity() > 1)
{
if (item.getQuantity() >= MAX_QUANTITY)
{
itemStringBuilder.append(" (Lots!)");
}
else
{
itemStringBuilder.append(" (")
.append(StackFormatter.quantityToStackSize(item.getQuantity()))
.append(")");
}
}
if (config.priceDisplayMode() == PriceDisplayMode.BOTH)
{
if (item.getGePrice() > 0)
{
itemStringBuilder.append(" (EX: ")
.append(StackFormatter.quantityToStackSize(item.getGePrice()))
.append(" gp)");
}
if (item.getHaPrice() > 0)
{
itemStringBuilder.append(" (HA: ")
.append(StackFormatter.quantityToStackSize(item.getHaPrice()))
.append(" gp)");
}
}
else if (config.priceDisplayMode() != PriceDisplayMode.OFF)
{
final int price = config.priceDisplayMode() == PriceDisplayMode.GE
? item.getGePrice()
: item.getHaPrice();
if (price > 0)
{
itemStringBuilder
.append(" (")
.append(StackFormatter.quantityToStackSize(price))
.append(" gp)");
}
}
final String itemString = itemStringBuilder.toString();
itemStringBuilder.setLength(0);
if (item.getHeight() == -1)
{
final Tile[][][] sceneTiles = client.getScene().getTiles();
final Tile itemTile = sceneTiles[client.getPlane()][groundPoint.getSceneX()][groundPoint.getSceneY()];
item.setHeight(itemTile.getItemLayer().getHeight());
}
final Point textPoint = Perspective.getCanvasTextLocation(client,
graphics,
groundPoint,
itemString,
item.getHeight() + OFFSET_Z);
if (textPoint == null)
{
continue;
}
final int offset = plugin.isHotKeyPressed()
? item.getOffset()
: offsetMap.compute(item.getLocation(), (k, v) -> v != null ? v + 1 : 0);
final int textX = textPoint.getX();
final int textY = textPoint.getY() - (STRING_GAP * offset);
if (plugin.isHotKeyPressed())
{
final int stringWidth = fm.stringWidth(itemString);
final int stringHeight = fm.getHeight();
// Item bounds
int x = textX - 2;
int y = textY - stringHeight - 2;
int width = stringWidth + 4;
int height = stringHeight + 4;
final Rectangle itemBounds = new Rectangle(x, y, width, height);
// Hidden box
x += width + 2;
y = textY - (RECTANGLE_SIZE + stringHeight) / 2;
width = height = RECTANGLE_SIZE;
final Rectangle itemHiddenBox = new Rectangle(x, y, width, height);
// Highlight box
x += width + 2;
final Rectangle itemHighlightBox = new Rectangle(x, y, width, height);
boolean mouseInBox = itemBounds.contains(mousePos.getX(), mousePos.getY());
boolean mouseInHiddenBox = itemHiddenBox.contains(mousePos.getX(), mousePos.getY());
boolean mouseInHighlightBox = itemHighlightBox.contains(mousePos.getX(), mousePos.getY());
if (mouseInBox)
{
plugin.setTextBoxBounds(new SimpleEntry<>(itemBounds, item));
}
else if (mouseInHiddenBox)
{
plugin.setHiddenBoxBounds(new SimpleEntry<>(itemHiddenBox, item));
}
else if (mouseInHighlightBox)
{
plugin.setHighlightBoxBounds(new SimpleEntry<>(itemHighlightBox, item));
}
boolean topItem = topGroundItem == item;
// Draw background if hovering
if (topItem && (mouseInBox || mouseInHiddenBox || mouseInHighlightBox))
{
backgroundComponent.setRectangle(itemBounds);
backgroundComponent.render(graphics);
}
// Draw hidden box
drawRectangle(graphics, itemHiddenBox, topItem && mouseInHiddenBox ? Color.RED : color, hidden != null, true);
// Draw highlight box
drawRectangle(graphics, itemHighlightBox, topItem && mouseInHighlightBox ? Color.GREEN : color, highlighted != null, false);
}
if (config.showGroundItemDuration() == TimerDisplayMode.ALWAYS
|| (config.showGroundItemDuration() == TimerDisplayMode.HOTKEY_PRESSED && plugin.isHotKeyPressed()))
{
drawTimerOverlay(graphics, new java.awt.Point(textX, textY), item);
}
if (config.toggleOutline())
{
graphics.setColor(Color.BLACK);
graphics.drawString(itemString, textX + 1, textY + 1);
graphics.drawString(itemString, textX - 1, textY - 1);
graphics.drawString(itemString, textX - 1, textY + 1);
graphics.drawString(itemString, textX + 1, textY - 1);
}
textComponent.setText(itemString);
textComponent.setColor(color);
textComponent.setPosition(new java.awt.Point(textX, textY));
textComponent.render(graphics);
}
return null;
}
private void drawRectangle(Graphics2D graphics, Rectangle rect, Color color, boolean inList, boolean hiddenBox)
{
graphics.setColor(Color.BLACK);
graphics.drawRect(rect.x + 1, rect.y + 1, rect.width, rect.height);
graphics.setColor(color);
graphics.draw(rect);
if (inList)
{
graphics.fill(rect);
}
graphics.setColor(Color.WHITE);
// Minus symbol
graphics.drawLine
(
rect.x + 2,
rect.y + (rect.height / 2),
rect.x + rect.width - 2,
rect.y + (rect.height / 2)
);
if (!hiddenBox)
{
// Plus symbol
graphics.drawLine
(
rect.x + (rect.width / 2),
rect.y + 2,
rect.x + (rect.width / 2),
rect.y + rect.height - 2
);
}
}
private void drawTimerOverlay(Graphics2D graphics, java.awt.Point location, GroundItem item)
{
progressPieComponent.setDiameter(TIMER_OVERLAY_DIAMETER);
int x = (int) location.getX() - TIMER_OVERLAY_DIAMETER;
int y = (int) location.getY() - TIMER_OVERLAY_DIAMETER / 2;
progressPieComponent.setPosition(new Point(x, y));
double millisOnGround = Duration.between(item.getDroppedInstant(), Instant.now()).toMillis();
boolean isPubliclyVisible = !item.isAlwaysPrivate() && millisOnGround > item.getDurationMillis();
double timeLeftRelative;
Color fillColor;
if (isPubliclyVisible || !item.isOwnedByPlayer())
{
if (item.isOwnedByPlayer())
{
timeLeftRelative = getTimeLeftRelative(millisOnGround - PUBLIC_ITEM_DURATION_MILLIS, PUBLIC_ITEM_DURATION_MILLIS);
}
else
{
timeLeftRelative = getTimeLeftRelative(millisOnGround, PUBLIC_ITEM_DURATION_MILLIS);
}
if (timeLeftRelative < WARNING_THRESHOLD)
{
fillColor = PUBLIC_WARNING_TIMER_COLOR;
}
else
{
fillColor = PUBLIC_TIMER_COLOR;
}
}
else
{
timeLeftRelative = getTimeLeftRelative(millisOnGround, item.getDurationMillis());
fillColor = PRIVATE_TIMER_COLOR;
}
// don't draw timer for any permanently spawned items or broken edge cases
if (timeLeftRelative > 1 || timeLeftRelative < 0)
{
return;
}
progressPieComponent.setFill(fillColor);
progressPieComponent.setBorderColor(fillColor);
progressPieComponent.setProgress(timeLeftRelative);
progressPieComponent.render(graphics);
}
private double getTimeLeftRelative(double millisOnGround, int duration)
{
return (duration - millisOnGround) / duration;
}
}