diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/lootassist/LootAssistOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/lootassist/LootAssistOverlay.java new file mode 100644 index 0000000000..887aff4f4d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/lootassist/LootAssistOverlay.java @@ -0,0 +1,109 @@ +package net.runelite.client.plugins.lootassist; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.text.DecimalFormat; +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.Point; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; +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.ui.overlay.OverlayUtil; + +public class LootAssistOverlay extends Overlay +{ + + + private LootAssistPlugin plugin; + private Client client; + private DecimalFormat d = new DecimalFormat("##.#"); + + @Inject + public LootAssistOverlay(Client client, LootAssistPlugin plugin) + { + this.plugin = plugin; + this.client = client; + setLayer(OverlayLayer.ABOVE_SCENE); + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) + { + for (Map.Entry entry : LootAssistPlugin.lootPiles.entrySet()) + { + WorldPoint localPoint = entry.getKey(); + LootPile pile = entry.getValue(); + int x; + int y; + try + { + + x = LocalPoint.fromWorld(client, pile.getLocation()).getSceneX(); + + y = LocalPoint.fromWorld(client, pile.getLocation()).getSceneY(); + } + catch (NullPointerException e) + { + continue; + } + if (!localPoint.isInScene(client)) + { + continue; + } + + int timeRemaining = (int) ((pile.getTimeAppearing() - System.currentTimeMillis()) / 1000); + + if (timeRemaining < 0) + { + LootAssistPlugin.lootPiles.remove(localPoint); + client.clearHintArrow(); + } + else + { + String nameOverlay = pile.getPlayerName(); + String timeOverlay = d.format((pile.getTimeAppearing() - System.currentTimeMillis()) / 1000f); + final Polygon poly = Perspective.getCanvasTilePoly(client, + client.getScene().getTiles()[client.getPlane()][x][y].getLocalLocation()); + if (poly != null) + { + Point textLoc = Perspective.getCanvasTextLocation(client, graphics, + LocalPoint.fromWorld(client, pile.getLocation()), + nameOverlay, graphics.getFontMetrics().getHeight() * 7); + Point timeLoc = Perspective.getCanvasTextLocation(client, graphics, + LocalPoint.fromWorld(client, pile.getLocation()), + timeOverlay, graphics.getFontMetrics().getHeight()); + OverlayUtil.renderPolygon(graphics, poly, Color.WHITE); + if (timeRemaining < 5) + { + OverlayUtil.renderTextLocation(graphics, timeLoc, timeOverlay, Color.RED); + OverlayUtil.renderTextLocation(graphics, textLoc, nameOverlay, Color.RED); + } + if (timeRemaining < 2) + { + client.setHintArrow(WorldPoint.fromLocal(client, + LocalPoint.fromWorld(client, pile.getLocation()))); + } + else + { + OverlayUtil.renderTextLocation(graphics, timeLoc, timeOverlay, Color.WHITE); + OverlayUtil.renderTextLocation(graphics, textLoc, nameOverlay, Color.WHITE); + } + + + } + } + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/lootassist/LootAssistPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/lootassist/LootAssistPlugin.java new file mode 100644 index 0000000000..066a24c87e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/lootassist/LootAssistPlugin.java @@ -0,0 +1,84 @@ +package net.runelite.client.plugins.lootassist; + +import com.google.inject.Provides; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import javax.inject.Inject; +import net.runelite.api.Actor; +import net.runelite.api.AnimationID; +import net.runelite.api.Client; +import net.runelite.api.Player; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.AnimationChanged; +import net.runelite.api.events.GameStateChanged; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.PluginType; +import net.runelite.client.ui.overlay.OverlayManager; + +@PluginDescriptor( + name = "Loot Assist Plugin", + description = "Creates a tile overlay with a timer that counts down to when the loot will appear to you", + tags = {"pklite", "loot", "looting", "loot assist", "assist", "loot assist"}, + type = PluginType.PVP, + enabledByDefault = false +) +public class LootAssistPlugin extends Plugin +{ + + @Inject + private Client client; + + @Inject + OverlayManager overlayManager; + + @Inject + LootAssistOverlay lootAssistOverlay; + + @Inject + private LootAssitConfig config; + + + public static ConcurrentHashMap lootPiles = new ConcurrentHashMap<>(); + + @Provides + LootAssitConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(LootAssitConfig.class); + } + + @Override + protected void startUp() throws Exception + { + overlayManager.add(lootAssistOverlay); + } + + @Override + protected void shutDown() throws Exception + { + lootPiles.clear(); + overlayManager.remove(lootAssistOverlay); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) + { + lootPiles.clear(); + } + + @Subscribe + public void onAnimationChanged(AnimationChanged event) + { + final Actor actor = event.getActor(); + if (actor.getAnimation() == AnimationID.DEATH && actor instanceof Player) + { + LootPile pile = new LootPile(actor.getWorldLocation(), actor.getName()); + lootPiles.put(pile.getLocation(), pile); + } + } + + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/lootassist/LootAssitConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/lootassist/LootAssitConfig.java new file mode 100644 index 0000000000..8c4d5b1a10 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/lootassist/LootAssitConfig.java @@ -0,0 +1,20 @@ +package net.runelite.client.plugins.lootassist; + +import java.awt.Color; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("lootassist") +public interface LootAssitConfig extends Config +{ + @ConfigItem( + keyName = "color", + name = "Color", + description = "The Color of the tile and name overlay that indicates where loot will appear" + ) + default Color color() + { + return Color.WHITE; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/lootassist/LootPile.java b/runelite-client/src/main/java/net/runelite/client/plugins/lootassist/LootPile.java new file mode 100644 index 0000000000..871e6066d4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/lootassist/LootPile.java @@ -0,0 +1,28 @@ +package net.runelite.client.plugins.lootassist; + +import lombok.Getter; +import lombok.Setter; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; + +public class LootPile +{ + private static final long TIME_UNTIL_VISIBLE = 60000; + private final long timeCreated; + @Getter + private final long timeAppearing; + @Getter + private final WorldPoint location; + @Getter + private final String playerName; + @Getter @Setter + private boolean beingRendered = false; + + public LootPile(WorldPoint location, String playerName) + { + this.timeCreated = System.currentTimeMillis(); + this.location = location; + this.playerName = playerName; + this.timeAppearing = this.timeCreated + TIME_UNTIL_VISIBLE; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java b/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java index 3a842c227f..c306d45125 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java @@ -1,61 +1,111 @@ +/* + * Copyright (c) 2019. PKLite - All Rights Reserved + * Unauthorized modification, distribution, or possession of this source file, via any medium is strictly prohibited. + * Proprietary and confidential. Refer to PKLite License file for more information on + * full terms of this copyright and to determine what constitutes authorized use. + * Written by PKLite(ST0NEWALL, others) , 2019 + * + */ + package net.runelite.client.util; -import net.runelite.api.ItemComposition; -import java.util.TreeMap; import java.util.Comparator; -import org.apache.commons.lang3.ArrayUtils; import java.util.Objects; -import net.runelite.api.ItemContainer; -import net.runelite.api.Item; -import net.runelite.api.InventoryID; -import net.runelite.client.game.ItemManager; -import net.runelite.api.Player; +import java.util.TreeMap; import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemComposition; +import net.runelite.api.Player; +import net.runelite.api.Varbits; +import net.runelite.api.WorldType; import net.runelite.api.coords.WorldPoint; +import net.runelite.client.game.ItemManager; +import static net.runelite.client.util.StackFormatter.quantityToRSDecimalStack; +import org.apache.commons.lang3.ArrayUtils; +/** + * + */ public class PvPUtil { - public PvPUtil() { - super(); + + /** + * Gets the wilderness level based on a world point + * @param point the point in the world to get the wilderness level for + * @return the int representing the wilderness level + */ + public static int getWildernessLevelFrom(WorldPoint point) + { + int x = point.getX(); + int y = point.getY(); + + int underLevel = ((y - 9920) / 8) + 1; + int upperLevel = ((y - 3520) / 8) + 1; + + return y > 6400 ? underLevel : upperLevel; } - - public static int getWildernessLevelFrom(final WorldPoint point) { - final int x = point.getX(); - final int y = point.getY(); - final int underLevel = (y - 9920) / 8 + 1; - final int upperLevel = (y - 3520) / 8 + 1; - return (y > 6400) ? underLevel : upperLevel; - } - - public static boolean isAttackable(final Client c, final Player p) { - return Math.abs(c.getLocalPlayer().getCombatLevel() - p.getCombatLevel()) < getWildernessLevelFrom(c.getLocalPlayer().getWorldLocation()); - } - - public static int calculateRisk(final Client client, final ItemManager itemManager) { - if (client.getItemContainer(InventoryID.EQUIPMENT) == null) { - return 0; + + /** + * Determines if another player is attackable based off of wilderness level and combat levels + * @param client The client of the local player + * @param player the player to determine attackability + * @return returns true if the player is attackable, false otherwise + */ + public static boolean isAttackable(Client client, Player player) + { + int wildernessLevel = 0; + if (!(client.getVar(Varbits.IN_WILDERNESS) == 1 || WorldType.isPvpWorld(client.getWorldType()))) + { + return false; } - if (client.getItemContainer(InventoryID.INVENTORY).getItems() == null) { - return 0; - } - final Item[] items = (Item[])ArrayUtils.addAll(((ItemContainer)Objects.requireNonNull(client.getItemContainer(InventoryID.EQUIPMENT))).getItems(), ((ItemContainer)Objects.requireNonNull(client.getItemContainer(InventoryID.INVENTORY))).getItems()); - final TreeMap priceMap = new TreeMap(Comparator.comparingInt(Integer::intValue)); - int wealth = 0; - for (final Item i : items) { - int value = itemManager.getItemPrice(i.getId()) * i.getQuantity(); - final ItemComposition itemComposition = itemManager.getItemComposition(i.getId()); - if (!itemComposition.isTradeable() && value == 0) { - value = itemComposition.getPrice() * i.getQuantity(); - priceMap.put(Integer.valueOf(value), i); + if (WorldType.isPvpWorld(client.getWorldType())) + { + if (client.getVar(Varbits.IN_WILDERNESS) != 1) + { + return Math.abs(client.getLocalPlayer().getCombatLevel() - player.getCombatLevel()) <= 15; } - else { + wildernessLevel = 15; + } + return Math.abs(client.getLocalPlayer().getCombatLevel() - player.getCombatLevel()) + < (getWildernessLevelFrom(client.getLocalPlayer().getWorldLocation())+ wildernessLevel); + } + + public static int calculateRisk(Client client, ItemManager itemManager) + { + if (client.getItemContainer(InventoryID.EQUIPMENT) == null) + { + return 0; + } + if (client.getItemContainer(InventoryID.INVENTORY).getItems() == null) + { + return 0; + } + Item[] items = ArrayUtils.addAll(Objects.requireNonNull(client.getItemContainer(InventoryID.EQUIPMENT)).getItems(), + Objects.requireNonNull(client.getItemContainer(InventoryID.INVENTORY)).getItems()); + TreeMap priceMap = new TreeMap<>(Comparator.comparingInt(Integer::intValue)); + int wealth = 0; + for (Item i : items) + { + int value = (itemManager.getItemPrice(i.getId()) * i.getQuantity()); + + final ItemComposition itemComposition = itemManager.getItemComposition(i.getId()); + if (!itemComposition.isTradeable() && value == 0) + { + value = itemComposition.getPrice() * i.getQuantity(); + priceMap.put(value, i); + } + else + { value = itemManager.getItemPrice(i.getId()) * i.getQuantity(); - if (i.getId() > 0 && value > 0) { - priceMap.put(Integer.valueOf(value), i); + if (i.getId() > 0 && value > 0) + { + priceMap.put(value, i); } } wealth += value; } - return Integer.parseInt(StackFormatter.quantityToRSDecimalStack(priceMap.keySet().stream().mapToInt(Integer::intValue).sum())); + return Integer.parseInt(quantityToRSDecimalStack(priceMap.keySet().stream().mapToInt(Integer::intValue).sum())); + } }