ground items plugin: add lootbeams

This commit is contained in:
Trevor
2021-07-03 22:56:58 -04:00
committed by Adam
parent 8d73d50520
commit 48c6434e6d
8 changed files with 238 additions and 25 deletions

View File

@@ -204,6 +204,7 @@ public final class AnimationID
public static final int LEAGUE_HOME_TELEPORT_4 = 8803;
public static final int LEAGUE_HOME_TELEPORT_5 = 8805;
public static final int LEAGUE_HOME_TELEPORT_6 = 8807;
public static final int RAID_LIGHT_ANIMATION = 3101;
public static final int CONSTRUCTION = 3676;
public static final int CONSTRUCTION_IMCANDO = 8912;

View File

@@ -29,7 +29,6 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import lombok.Builder;
import lombok.Data;
import lombok.Value;
import net.runelite.api.coords.WorldPoint;
@Data
@@ -66,11 +65,4 @@ class GroundItem
{
return lootType != LootType.UNKNOWN;
}
@Value
static class GroundItemKey
{
private int itemId;
private WorldPoint location;
}
}

View File

@@ -403,4 +403,26 @@ public interface GroundItemsConfig extends Config
{
return false;
}
@ConfigItem(
keyName = "showLootbeamForHighlighted",
name = "Highlighted item lootbeams",
description = "Configures lootbeams to show for all highlighted items.",
position = 30
)
default boolean showLootbeamForHighlighted()
{
return false;
}
@ConfigItem(
keyName = "showLootbeamTier",
name = "Lootbeam tier",
description = "Configures which price tiers will trigger a lootbeam",
position = 31
)
default HighlightTier showLootbeamTier()
{
return HighlightTier.HIGH;
}
}

View File

@@ -49,6 +49,7 @@ import net.runelite.api.coords.WorldPoint;
import static net.runelite.client.plugins.grounditems.GroundItemsPlugin.MAX_QUANTITY;
import net.runelite.client.plugins.grounditems.config.DespawnTimerMode;
import static net.runelite.client.plugins.grounditems.config.ItemHighlightMode.MENU;
import static net.runelite.client.plugins.grounditems.config.ItemHighlightMode.NONE;
import net.runelite.client.plugins.grounditems.config.PriceDisplayMode;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
@@ -107,7 +108,8 @@ public class GroundItemsOverlay extends Overlay
@Override
public Dimension render(Graphics2D graphics)
{
final boolean dontShowOverlay = (config.itemHighlightMode() == MENU || plugin.isHideAll()) && !plugin.isHotKeyPressed();
final boolean dontShowOverlay = (config.itemHighlightMode() == MENU || config.itemHighlightMode() == NONE
|| plugin.isHideAll()) && !plugin.isHotKeyPressed();
if (dontShowOverlay && !config.highlightTiles())
{

View File

@@ -28,7 +28,9 @@ package net.runelite.client.plugins.grounditems;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.EvictingQueue;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Table;
import com.google.inject.Provides;
import java.awt.Color;
import java.awt.Rectangle;
@@ -38,7 +40,7 @@ import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
@@ -71,6 +73,7 @@ import net.runelite.api.events.ItemSpawned;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.client.Notifier;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
@@ -83,7 +86,7 @@ import net.runelite.client.input.MouseManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.grounditems.config.HighlightTier;
import static net.runelite.client.plugins.grounditems.config.ItemHighlightMode.OVERLAY;
import net.runelite.client.plugins.grounditems.config.ItemHighlightMode;
import net.runelite.client.plugins.grounditems.config.MenuHighlightMode;
import static net.runelite.client.plugins.grounditems.config.MenuHighlightMode.BOTH;
import static net.runelite.client.plugins.grounditems.config.MenuHighlightMode.NAME;
@@ -158,6 +161,9 @@ public class GroundItemsPlugin extends Plugin
@Inject
private Client client;
@Inject
private ClientThread clientThread;
@Inject
private ItemManager itemManager;
@@ -177,12 +183,13 @@ public class GroundItemsPlugin extends Plugin
private ScheduledExecutorService executor;
@Getter
private final Map<GroundItem.GroundItemKey, GroundItem> collectedGroundItems = new LinkedHashMap<>();
private final Table<WorldPoint, Integer, GroundItem> collectedGroundItems = HashBasedTable.create();
private List<PriceHighlight> priceChecks = ImmutableList.of();
private LoadingCache<NamedQuantity, Boolean> highlightedItems;
private LoadingCache<NamedQuantity, Boolean> hiddenItems;
private final Queue<Integer> droppedItemQueue = EvictingQueue.create(16); // recently dropped items
private int lastUsedItem;
private final Map<WorldPoint, Lootbeam> lootbeams = new HashMap<>();
@Provides
GroundItemsConfig provideConfig(ConfigManager configManager)
@@ -213,6 +220,7 @@ public class GroundItemsPlugin extends Plugin
hiddenItemList = null;
highlightedItemsList = null;
collectedGroundItems.clear();
clientThread.invokeLater(this::removeAllLootbeams);
}
@Subscribe
@@ -230,6 +238,7 @@ public class GroundItemsPlugin extends Plugin
if (event.getGameState() == GameState.LOADING)
{
collectedGroundItems.clear();
lootbeams.clear();
}
}
@@ -240,19 +249,23 @@ public class GroundItemsPlugin extends Plugin
Tile tile = itemSpawned.getTile();
GroundItem groundItem = buildGroundItem(tile, item);
GroundItem.GroundItemKey groundItemKey = new GroundItem.GroundItemKey(item.getId(), tile.getWorldLocation());
GroundItem existing = collectedGroundItems.putIfAbsent(groundItemKey, groundItem);
GroundItem existing = collectedGroundItems.get(tile.getWorldLocation(), item.getId());
if (existing != null)
{
existing.setQuantity(existing.getQuantity() + groundItem.getQuantity());
// The spawn time remains set at the oldest spawn
}
else
{
collectedGroundItems.put(tile.getWorldLocation(), item.getId(), groundItem);
}
if (!config.onlyShowLoot())
{
notifyHighlightedItem(groundItem);
}
handleLootbeam(tile.getWorldLocation());
}
@Subscribe
@@ -261,8 +274,7 @@ public class GroundItemsPlugin extends Plugin
TileItem item = itemDespawned.getItem();
Tile tile = itemDespawned.getTile();
GroundItem.GroundItemKey groundItemKey = new GroundItem.GroundItemKey(item.getId(), tile.getWorldLocation());
GroundItem groundItem = collectedGroundItems.get(groundItemKey);
GroundItem groundItem = collectedGroundItems.get(tile.getWorldLocation(), item.getId());
if (groundItem == null)
{
return;
@@ -270,7 +282,7 @@ public class GroundItemsPlugin extends Plugin
if (groundItem.getQuantity() <= item.getQuantity())
{
collectedGroundItems.remove(groundItemKey);
collectedGroundItems.remove(tile.getWorldLocation(), item.getId());
}
else
{
@@ -280,6 +292,8 @@ public class GroundItemsPlugin extends Plugin
// time
groundItem.setSpawnTime(null);
}
handleLootbeam(tile.getWorldLocation());
}
@Subscribe
@@ -291,12 +305,13 @@ public class GroundItemsPlugin extends Plugin
int newQuantity = itemQuantityChanged.getNewQuantity();
int diff = newQuantity - oldQuantity;
GroundItem.GroundItemKey groundItemKey = new GroundItem.GroundItemKey(item.getId(), tile.getWorldLocation());
GroundItem groundItem = collectedGroundItems.get(groundItemKey);
GroundItem groundItem = collectedGroundItems.get(tile.getWorldLocation(), item.getId());
if (groundItem != null)
{
groundItem.setQuantity(groundItem.getQuantity() + diff);
}
handleLootbeam(tile.getWorldLocation());
}
@Subscribe
@@ -366,8 +381,7 @@ public class GroundItemsPlugin extends Plugin
for (ItemStack itemStack : items)
{
WorldPoint location = WorldPoint.fromLocal(client, itemStack.getLocation());
GroundItem.GroundItemKey groundItemKey = new GroundItem.GroundItemKey(itemStack.getId(), location);
GroundItem groundItem = collectedGroundItems.get(groundItemKey);
GroundItem groundItem = collectedGroundItems.get(location, itemStack.getId());
if (groundItem != null)
{
groundItem.setLootType(lootType);
@@ -460,12 +474,14 @@ public class GroundItemsPlugin extends Plugin
}
priceChecks = priceCheckBuilder.build();
clientThread.invokeLater(this::handleLootbeams);
}
@Subscribe
public void onMenuEntryAdded(MenuEntryAdded event)
{
if (config.itemHighlightMode() != OVERLAY)
if (config.itemHighlightMode() == ItemHighlightMode.MENU || config.itemHighlightMode() == ItemHighlightMode.BOTH)
{
final boolean telegrabEntry = event.getOption().equals("Cast") && event.getTarget().startsWith(TELEGRAB_TEXT) && event.getType() == CAST_ON_ITEM;
if (!(event.getOption().equals("Take") && event.getType() == THIRD_OPTION) && !telegrabEntry)
@@ -481,8 +497,7 @@ public class GroundItemsPlugin extends Plugin
MenuEntry lastEntry = menuEntries[menuEntries.length - 1];
final WorldPoint worldPoint = WorldPoint.fromScene(client, sceneX, sceneY, client.getPlane());
GroundItem.GroundItemKey groundItemKey = new GroundItem.GroundItemKey(itemId, worldPoint);
GroundItem groundItem = collectedGroundItems.get(groundItemKey);
GroundItem groundItem = collectedGroundItems.get(worldPoint, itemId);
int quantity = groundItem.getQuantity();
final int gePrice = groundItem.getGePrice();
@@ -705,4 +720,103 @@ public class GroundItemsPlugin extends Plugin
lastUsedItem = clickedItem.getId();
}
}
private void handleLootbeam(WorldPoint worldPoint)
{
/*
* Return and remove the lootbeam from this location if lootbeam are disabled
* Lootbeam can be at this location if the config was just changed
*/
if (!(config.showLootbeamForHighlighted() || config.showLootbeamTier() != HighlightTier.OFF))
{
removeLootbeam(worldPoint);
return;
}
int price = -1;
Collection<GroundItem> groundItems = collectedGroundItems.row(worldPoint).values();
for (GroundItem groundItem : groundItems)
{
if ((config.onlyShowLoot() && !groundItem.isMine()))
{
continue;
}
/*
* highlighted items have the highest priority so if an item is highlighted at this location
* we can early return
*/
NamedQuantity item = new NamedQuantity(groundItem);
if (config.showLootbeamForHighlighted()
&& TRUE.equals(highlightedItems.getUnchecked(item)))
{
addLootbeam(worldPoint, config.highlightedColor());
return;
}
// Explicit hide takes priority over implicit highlight
if (TRUE.equals(hiddenItems.getUnchecked(item)))
{
continue;
}
int itemPrice = getValueByMode(groundItem.getGePrice(), groundItem.getHaPrice());
price = Math.max(itemPrice, price);
}
if (config.showLootbeamTier() != HighlightTier.OFF)
{
for (PriceHighlight highlight : priceChecks)
{
if (price > highlight.getPrice() && price > config.showLootbeamTier().getValueFromTier(config))
{
addLootbeam(worldPoint, highlight.color);
return;
}
}
}
removeLootbeam(worldPoint);
}
private void handleLootbeams()
{
for (WorldPoint worldPoint : collectedGroundItems.rowKeySet())
{
handleLootbeam(worldPoint);
}
}
private void removeAllLootbeams()
{
for (Lootbeam lootbeam : lootbeams.values())
{
lootbeam.remove();
}
lootbeams.clear();
}
private void addLootbeam(WorldPoint worldPoint, Color color)
{
Lootbeam lootbeam = lootbeams.get(worldPoint);
if (lootbeam == null)
{
lootbeam = new Lootbeam(client, worldPoint, color);
lootbeams.put(worldPoint, lootbeam);
}
else
{
lootbeam.setColor(color);
}
}
private void removeLootbeam(WorldPoint worldPoint)
{
Lootbeam lootbeam = lootbeams.remove(worldPoint);
if (lootbeam != null)
{
lootbeam.remove();
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2021, Trevor <https://github.com/Trevor159>
* 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 net.runelite.api.AnimationID;
import net.runelite.api.Client;
import net.runelite.api.JagexColor;
import net.runelite.api.RuneLiteObject;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import java.awt.Color;
class Lootbeam
{
private static final int RAID_LIGHT_MODEL = 5809;
private static final short RAID_LIGHT_FIND_COLOR = 6371;
private final RuneLiteObject runeLiteObject;
private final Client client;
private Color color;
public Lootbeam(Client client, WorldPoint worldPoint, Color color)
{
this.client = client;
runeLiteObject = client.createRuneLiteObject();
setColor(color);
runeLiteObject.setAnimation(client.loadAnimation(AnimationID.RAID_LIGHT_ANIMATION));
runeLiteObject.setShouldLoop(true);
LocalPoint lp = LocalPoint.fromWorld(client, worldPoint);
runeLiteObject.setLocation(lp, client.getPlane());
runeLiteObject.setActive(true);
}
public void setColor(Color color)
{
if (this.color != null && this.color.equals(color))
{
return;
}
this.color = color;
runeLiteObject.setModel(client.loadModel(
RAID_LIGHT_MODEL,
new short[]{RAID_LIGHT_FIND_COLOR},
new short[]{JagexColor.rgbToHSL(color.getRGB(), 1.0d)}
));
}
public void remove()
{
runeLiteObject.setActive(false);
}
}

View File

@@ -31,6 +31,7 @@ import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum ItemHighlightMode
{
NONE("None"),
OVERLAY("Overlay"),
MENU("Right-click menu"),
BOTH("Both");

View File

@@ -111,6 +111,8 @@ public class GroundItemsPluginTest
when(client.getLocalPlayer()).thenReturn(mock(Player.class));
when(config.getHiddenItems()).thenReturn("");
when(config.showLootbeamForHighlighted()).thenReturn(false);
when(config.showLootbeamTier()).thenReturn(HighlightTier.OFF);
}
@Test