From 447aaf6a2503e36a33f3439f419c90f9fe7d6e6f Mon Sep 17 00:00:00 2001 From: Kyleeld <48519776+Kyleeld@users.noreply.github.com> Date: Sat, 20 Apr 2019 18:59:13 +0100 Subject: [PATCH] additional plugins --- .../fkeyremapping/fKeyRemappingPlugin.java | 1 + .../groupitemlist/GroupItemListPlugin.java | 1 + .../inventorysetups/InventorySetup.java | 15 + .../InventorySetupBankOverlay.java | 113 +++++ .../inventorysetups/InventorySetupConfig.java | 84 ++++ .../inventorysetups/InventorySetupItem.java | 15 + .../inventorysetups/InventorySetupPlugin.java | 403 ++++++++++++++++++ .../ui/InventorySetupContainerPanel.java | 109 +++++ .../ui/InventorySetupEquipmentPanel.java | 91 ++++ .../ui/InventorySetupInventoryPanel.java | 78 ++++ .../ui/InventorySetupPluginPanel.java | 287 +++++++++++++ .../ui/InventorySetupSlot.java | 45 ++ .../LootingBagViewerOverlay.java | 123 ++++++ .../LootingBagViewerPlugin.java | 60 +++ .../raidsthieving/BatSolver/BatSolver.java | 193 +++++++++ .../BatSolver/ChestIdentifier.java | 261 ++++++++++++ .../raidsthieving/BatSolver/SolutionSet.java | 165 +++++++ .../BatSolver/ThievingRoomType.java | 61 +++ .../plugins/raidsthieving/ChestOverlay.java | 172 ++++++++ .../plugins/raidsthieving/InstancePoint.java | 98 +++++ .../raidsthieving/RaidsThievingConfig.java | 67 +++ .../raidsthieving/RaidsThievingConstants.java | 35 ++ .../raidsthieving/RaidsThievingPlugin.java | 270 ++++++++++++ .../plugins/raidsthieving/ThievingChest.java | 78 ++++ .../rememberclan/RememberClanPlugin.java | 1 + .../plugins/safespot/SafeSpotConfig.java | 25 ++ .../plugins/safespot/SafeSpotOverlay.java | 40 ++ .../plugins/safespot/SafeSpotPlugin.java | 135 ++++++ .../plugins/stronghold/StrongholdPlugin.java | 1 + .../warindicators/WarIndicatorPlugin.java | 1 + .../zoneIndicators/ZoneIndicatorsPlugin.java | 1 + 31 files changed, 3029 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetup.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupBankOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupItem.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupContainerPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupEquipmentPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupInventoryPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupPluginPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupSlot.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/lootingbagviewer/LootingBagViewerOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/lootingbagviewer/LootingBagViewerPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/BatSolver.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/ChestIdentifier.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/SolutionSet.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/ThievingRoomType.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/ChestOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/InstancePoint.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/RaidsThievingConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/RaidsThievingConstants.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/RaidsThievingPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/ThievingChest.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/safespot/SafeSpotConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/safespot/SafeSpotOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/safespot/SafeSpotPlugin.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fkeyremapping/fKeyRemappingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/fkeyremapping/fKeyRemappingPlugin.java index 78199bfcc5..050d452811 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fkeyremapping/fKeyRemappingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fkeyremapping/fKeyRemappingPlugin.java @@ -16,6 +16,7 @@ import net.runelite.client.plugins.PluginDescriptor; name = "fKeyRemapping", description = "Used for interface hotkeys", tags = {"hotkey", "remapping"}, + type = "utility", enabledByDefault = true ) public class fKeyRemappingPlugin extends Plugin diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groupitemlist/GroupItemListPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/groupitemlist/GroupItemListPlugin.java index a01716d388..99d9b14831 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groupitemlist/GroupItemListPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groupitemlist/GroupItemListPlugin.java @@ -17,6 +17,7 @@ import java.util.LinkedHashMap; name = "!Group Item List", description = "Group the right click menu of a pile of items.", tags = {"ground", "compress", "pile", "group"}, + type = "utility", enabledByDefault = false ) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetup.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetup.java new file mode 100644 index 0000000000..043d22a30c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetup.java @@ -0,0 +1,15 @@ +package net.runelite.client.plugins.inventorysetups; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.ArrayList; + +@AllArgsConstructor +public class InventorySetup +{ + @Getter + private ArrayList inventory; + @Getter + private ArrayList equipment; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupBankOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupBankOverlay.java new file mode 100644 index 0000000000..34ba341fc7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupBankOverlay.java @@ -0,0 +1,113 @@ +package net.runelite.client.plugins.inventorysetups; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.Point; +import net.runelite.api.Query; +import net.runelite.api.SpritePixels; +import net.runelite.api.queries.BankItemQuery; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.widgets.WidgetItem; +import net.runelite.client.ui.FontManager; +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; + +import javax.inject.Inject; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.util.Arrays; +import java.util.Objects; + +@Slf4j +public class InventorySetupBankOverlay extends Overlay +{ + private final Client client; + private final QueryRunner queryRunner; + private final InventorySetupPlugin plugin; + private final InventorySetupConfig config; + + @Inject + public InventorySetupBankOverlay(Client client, QueryRunner queryRunner, InventorySetupPlugin plugin, InventorySetupConfig config) + { + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.LOW); + setLayer(OverlayLayer.ABOVE_WIDGETS); + this.client = client; + this.queryRunner = queryRunner; + this.plugin = plugin; + this.config = config; + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (config.getBankHighlight()) + { + int[] ids = plugin.getCurrentInventorySetupIds(); + if (ids == null) + { + return null; + } + ids = Arrays.stream(ids) + .filter(Objects::nonNull) + .filter(id -> id != -1) + .toArray(); + final Query query = new BankItemQuery().idEquals(ids); + final WidgetItem[] widgetItems = queryRunner.runQuery(query); + final Widget bankContainer = client.getWidget(WidgetInfo.BANK_CONTAINER); + for (final WidgetItem item : widgetItems) + { + Point canvasLocation = item.getCanvasLocation(); + Rectangle canvasBounds = item.getCanvasBounds(); + Point windowLocation = bankContainer.getCanvasLocation(); + + if (canvasLocation == null || windowLocation == null) + { + return null; + } + + if (!(canvasLocation.getY() + 60 >= windowLocation.getY() + bankContainer.getHeight()) && !(canvasLocation.getY() + canvasBounds.getHeight() <= windowLocation.getY() + 90)) + { + final Color color = config.getBankHighlightColor(); + + if (color != null) + { + final BufferedImage outline = loadItemOutline(item.getId(), item.getQuantity(), color); + graphics.drawImage(outline, item.getCanvasLocation().getX() + 1, item.getCanvasLocation().getY() + 1, null); + if (item.getQuantity() > 1) + { + drawQuantity(graphics, item, Color.YELLOW); + } + else if (item.getQuantity() == 0) + { + drawQuantity(graphics, item, Color.YELLOW.darker()); + } + } + } + } + } + return null; + } + + private void drawQuantity(Graphics2D graphics, WidgetItem item, Color darker) + { + graphics.setColor(Color.BLACK); + graphics.drawString(String.valueOf(item.getQuantity()), item.getCanvasLocation().getX() + 2, item.getCanvasLocation().getY() + 11); + graphics.setColor(darker); + graphics.setFont(FontManager.getRunescapeSmallFont()); + graphics.drawString(String.valueOf(item.getQuantity()), item.getCanvasLocation().getX() + 1, item.getCanvasLocation().getY() + 10); + } + + private BufferedImage loadItemOutline(final int itemId, final int itemQuantity, final Color outlineColor) + { + final SpritePixels itemSprite = client.createItemSprite(itemId, itemQuantity, 2, 0, 0, true, 710); + return itemSprite.toBufferedOutline(outlineColor); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupConfig.java new file mode 100644 index 0000000000..edcc47cd9c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupConfig.java @@ -0,0 +1,84 @@ +package net.runelite.client.plugins.inventorysetups; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +import java.awt.Color; + +@ConfigGroup("inventorysetups") +public interface InventorySetupConfig extends Config +{ + @ConfigItem( + keyName = "highlightDifferences", + name = "Highlight Differences", + description = "Highlight slots that don't match the selected setup", + position = 0 + ) + + default boolean getHighlightDifferences() + { + return false; + } + + @ConfigItem( + keyName = "highlightDifferenceColor", + name = "Highlight Color", + description = "The color used to highlight differences between setups", + position = 1 + ) + + default Color getHighlightColor() + { + return Color.RED; + } + + @ConfigItem( + keyName = "stackDifference", + name = "Stack Difference", + description = "Differences between setups will be highlighted if the stack size is different", + position = 2 + ) + + default boolean getStackDifference() + { + return false; + } + + @ConfigItem( + keyName = "variationDifference", + name = "Variation Difference", + description = "Variations of items (E.g., charged jewellery) will be counted as different", + position = 2 + ) + + default boolean getVariationDifference() + { + return false; + } + + @ConfigItem( + keyName = "bankHighlight", + name = "Bank Highlight", + description = "Highlight setup items in bank", + position = 4 + ) + + default boolean getBankHighlight() + { + return false; + } + + @ConfigItem( + keyName = "bankHighlightColor", + name = "Bank Highlight Color", + description = "The color used to highlight setup items in bank", + position = 5 + ) + + default Color getBankHighlightColor() + { + return Color.RED; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupItem.java new file mode 100644 index 0000000000..c1af4e68fd --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupItem.java @@ -0,0 +1,15 @@ +package net.runelite.client.plugins.inventorysetups; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +public class InventorySetupItem +{ + @Getter + private final int id; + @Getter + private final String name; + @Getter + private final int quantity; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupPlugin.java new file mode 100644 index 0000000000..8b9c632498 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/InventorySetupPlugin.java @@ -0,0 +1,403 @@ +package net.runelite.client.plugins.inventorysetups; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.google.inject.Provides; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemComposition; +import net.runelite.api.ItemContainer; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.ItemContainerChanged; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.ItemManager; +import net.runelite.client.game.ItemVariationMapping; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.inventorysetups.ui.InventorySetupPluginPanel; +import net.runelite.client.ui.ClientToolbar; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.ImageUtil; + +import javax.inject.Inject; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; +import java.awt.image.BufferedImage; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; + +@PluginDescriptor( + name = "Inventory Setups", + description = "Save inventory setups", + tags = { "items", "inventory", "setups"}, + type = "utility", + enabledByDefault = false +) + +@Slf4j +public class InventorySetupPlugin extends Plugin +{ + + private static final String CONFIG_GROUP = "inventorysetups"; + private static final String CONFIG_KEY = "setups"; + private static final int NUM_INVENTORY_ITEMS = 28; + private static final int NUM_EQUIPMENT_ITEMS = 14; + + @Inject + private Client client; + + @Inject + private ItemManager itemManager; + + @Inject + private InventorySetupBankOverlay overlay; + + @Inject + private ClientToolbar clientToolbar; + + @Inject + private InventorySetupConfig config; + + @Inject + private OverlayManager overlayManager; + + @Inject + private ClientThread clientThread; + + @Inject + private ConfigManager configManager; + + private InventorySetupPluginPanel panel; + + private HashMap inventorySetups; + + private NavigationButton navButton; + + private boolean highlightDifference; + + @Override + public void startUp() + { + overlayManager.add(overlay); + + panel = new InventorySetupPluginPanel(this, itemManager); + + final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "inventorysetups_icon.png"); + + navButton = NavigationButton.builder() + .tooltip("Inventory Setups") + .icon(icon) + .priority(9) + .panel(panel) + .build(); + + clientToolbar.addNavigation(navButton); + + // load all the inventory setups from the config file + clientThread.invokeLater(() -> + { + if (client.getGameState() != GameState.LOGIN_SCREEN) + { + return false; + } + + loadConfig(); + panel.showNoSetupsPanel(); + return true; + }); + + } + + public void addInventorySetup() + { + final String name = JOptionPane.showInputDialog(panel, + "Enter the name of this setup.", + "Add New Setup", + JOptionPane.PLAIN_MESSAGE); + + // cancel button was clicked + if (name == null) + { + return; + } + + if (name.isEmpty()) + { + JOptionPane.showMessageDialog(panel, + "Invalid Setup Name", + "Names must not be empty.", + JOptionPane.PLAIN_MESSAGE); + return; + } + + if (inventorySetups.containsKey(name)) + { + String builder = "The setup " + name + " already exists. " + + "Would you like to replace it with the current setup?"; + int confirm = JOptionPane.showConfirmDialog(panel, + builder, + "Warning", + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.PLAIN_MESSAGE); + + if (confirm == JOptionPane.CANCEL_OPTION) + { + return; + } + + // delete the old setup, no need to ask for confirmation + // because the user confirmed above + removeInventorySetup(name, false); + } + + clientThread.invoke(() -> + { + ArrayList inv = getNormalizedContainer(InventoryID.INVENTORY); + ArrayList eqp = getNormalizedContainer(InventoryID.EQUIPMENT); + + final InventorySetup invSetup = new InventorySetup(inv, eqp); + SwingUtilities.invokeLater(() -> + { + inventorySetups.put(name, invSetup); + panel.addInventorySetup(name); + panel.setCurrentInventorySetup(name); + + updateConfig(); + }); + }); + } + + public void removeInventorySetup(final String name, boolean askForConfirmation) + { + if (inventorySetups.containsKey(name)) + { + int confirm = JOptionPane.YES_OPTION; + + if (askForConfirmation) + { + confirm = JOptionPane.showConfirmDialog(panel, + "Are you sure you want to remove this setup?", + "Warning", + JOptionPane.YES_NO_OPTION, + JOptionPane.PLAIN_MESSAGE); + } + + if (confirm == JOptionPane.YES_OPTION) + { + inventorySetups.remove(name); + panel.removeInventorySetup(name); + } + + updateConfig(); + } + } + + public final InventorySetup getInventorySetup(final String name) + { + return inventorySetups.get(name); + } + + @Provides + InventorySetupConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(InventorySetupConfig.class); + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (event.getGroup().equals(CONFIG_GROUP)) + { + // only allow highlighting if the config is enabled and the player is logged in + highlightDifference = config.getHighlightDifferences() && client.getGameState() == GameState.LOGGED_IN; + final String setupName = panel.getSelectedInventorySetup(); + if (highlightDifference && !setupName.isEmpty()) + { + panel.setCurrentInventorySetup(setupName); + } + } + } + + private void updateConfig() + { + if (inventorySetups.isEmpty()) + { + configManager.unsetConfiguration(CONFIG_GROUP, CONFIG_KEY); + return; + } + + final Gson gson = new Gson(); + final String json = gson.toJson(inventorySetups); + configManager.setConfiguration(CONFIG_GROUP, CONFIG_KEY, json); + } + + private void loadConfig() + { + // serialize the internal data structure from the json in the configuration + final String json = configManager.getConfiguration(CONFIG_GROUP, CONFIG_KEY); + if (json == null || json.isEmpty()) + { + inventorySetups = new HashMap<>(); + } + else + { + // TODO add last resort?, serialize exception just make empty map + final Gson gson = new Gson(); + Type type = new TypeToken>() + { + + }.getType(); + inventorySetups = gson.fromJson(json, type); + } + + for (final String key : inventorySetups.keySet()) + { + panel.addInventorySetup(key); + } + + highlightDifference = false; + } + + @Subscribe + public void onItemContainerChanged(ItemContainerChanged event) + { + + if (!highlightDifference || client.getGameState() != GameState.LOGGED_IN) + { + return; + } + + // empty entry, no need to compare anything + final String selectedInventorySetup = panel.getSelectedInventorySetup(); + if (selectedInventorySetup.isEmpty()) + { + return; + } + + // check to see that the container is the equipment or inventory + ItemContainer container = event.getItemContainer(); + + if (container == client.getItemContainer(InventoryID.INVENTORY)) + { + ArrayList normContainer = getNormalizedContainer(InventoryID.INVENTORY); + final InventorySetup setup = inventorySetups.get(selectedInventorySetup); + panel.highlightDifferences(normContainer, setup, InventoryID.INVENTORY); + } + else if (container == client.getItemContainer(InventoryID.EQUIPMENT)) + { + ArrayList normContainer = getNormalizedContainer(InventoryID.EQUIPMENT); + final InventorySetup setup = inventorySetups.get(selectedInventorySetup); + panel.highlightDifferences(normContainer, setup, InventoryID.EQUIPMENT); + } + + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) + { + switch (event.getGameState()) + { + // set the highlighting off if login screen shows up + case LOGIN_SCREEN: + highlightDifference = false; + final String setupName = panel.getSelectedInventorySetup(); + if (!setupName.isEmpty()) + { + panel.setCurrentInventorySetup(setupName); + } + break; + + // set highlighting + case LOGGED_IN: + highlightDifference = config.getHighlightDifferences(); + break; + } + } + + public ArrayList getNormalizedContainer(final InventoryID id) + { + assert id == InventoryID.INVENTORY || id == InventoryID.EQUIPMENT : "invalid inventory ID"; + + final ItemContainer container = client.getItemContainer(id); + + ArrayList newContainer = new ArrayList<>(); + + Item[] items = null; + if (container != null) + { + items = container.getItems(); + } + + int size = id == InventoryID.INVENTORY ? NUM_INVENTORY_ITEMS : NUM_EQUIPMENT_ITEMS; + + for (int i = 0; i < size; i++) + { + if (items == null || i >= items.length) + { + newContainer.add(new InventorySetupItem(-1, "", 0)); + } + else + { + final Item item = items[i]; + String itemName = ""; + if (client.isClientThread()) + { + itemName = itemManager.getItemComposition(item.getId()).getName(); + } + newContainer.add(new InventorySetupItem(item.getId(), itemName, item.getQuantity())); + } + } + + return newContainer; + } + + public final InventorySetupConfig getConfig() + { + return config; + } + + public boolean getHighlightDifference() + { + return highlightDifference; + } + + @Override + public void shutDown() + { + overlayManager.remove(overlay); + clientToolbar.removeNavigation(navButton); + } + + final int[] getCurrentInventorySetupIds() + { + InventorySetup setup = inventorySetups.get(panel.getSelectedInventorySetup()); + if (setup == null) + { + return null; + } + ArrayList items = new ArrayList<>(); + items.addAll(setup.getEquipment()); + items.addAll(setup.getInventory()); + ArrayList itemIds = new ArrayList<>(); + for (InventorySetupItem item : items) + { + int id = item.getId(); + ItemComposition itemComposition = itemManager.getItemComposition(id); + if (id > 0) + { + itemIds.add(ItemVariationMapping.map(id)); + itemIds.add(itemComposition.getPlaceholderId()); + } + + } + return itemIds.stream().mapToInt(i -> i).toArray(); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupContainerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupContainerPanel.java new file mode 100644 index 0000000000..d5eda3697f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupContainerPanel.java @@ -0,0 +1,109 @@ +package net.runelite.client.plugins.inventorysetups.ui; + +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.game.ItemManager; +import net.runelite.client.game.ItemVariationMapping; +import net.runelite.client.plugins.inventorysetups.InventorySetupConfig; +import net.runelite.client.plugins.inventorysetups.InventorySetupItem; +import net.runelite.client.plugins.inventorysetups.InventorySetupPlugin; +import net.runelite.client.ui.ColorScheme; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Color; +import java.util.ArrayList; + +public abstract class InventorySetupContainerPanel extends JPanel +{ + + protected ItemManager itemManager; + + private final InventorySetupPlugin plugin; + + InventorySetupContainerPanel(final ItemManager itemManager, final InventorySetupPlugin plugin, String captionText) + { + this.itemManager = itemManager; + this.plugin = plugin; + JPanel containerPanel = new JPanel(); + + final JPanel containerSlotsPanel = new JPanel(); + + setupContainerPanel(containerSlotsPanel); + + // caption + final JLabel caption = new JLabel(captionText); + caption.setHorizontalAlignment(JLabel.CENTER); + caption.setVerticalAlignment(JLabel.CENTER); + + // panel that holds the caption and any other graphics + final JPanel captionPanel = new JPanel(); + captionPanel.add(caption); + + containerPanel.setLayout(new BorderLayout()); + containerPanel.add(captionPanel, BorderLayout.NORTH); + containerPanel.add(containerSlotsPanel, BorderLayout.CENTER); + + add(containerPanel); + } + + void setContainerSlot(int index, + final InventorySetupSlot containerSlot, + final ArrayList items) + { + if (index >= items.size() || items.get(index).getId() == -1) + { + containerSlot.setImageLabel(null, null); + return; + } + + int itemId = items.get(index).getId(); + int quantity = items.get(index).getQuantity(); + final String itemName = items.get(index).getName(); + AsyncBufferedImage itemImg = itemManager.getImage(itemId, quantity, quantity > 1); + String toolTip = itemName; + if (quantity > 1) + { + toolTip += " (" + quantity + ")"; + } + containerSlot.setImageLabel(toolTip, itemImg); + } + + void highlightDifferentSlotColor(InventorySetupItem savedItem, + InventorySetupItem currItem, + final InventorySetupSlot containerSlot) + { + // important note: do not use item names for comparisons + // they are all empty to avoid clientThread usage when highlighting + + final InventorySetupConfig config = plugin.getConfig(); + final Color highlightColor = config.getHighlightColor(); + + if (config.getStackDifference() && currItem.getQuantity() != savedItem.getQuantity()) + { + containerSlot.setBackground(highlightColor); + return; + } + + int currId = currItem.getId(); + int checkId = savedItem.getId(); + + if (!config.getVariationDifference()) + { + currId = ItemVariationMapping.map(currId); + checkId = ItemVariationMapping.map(checkId); + } + + if (currId != checkId) + { + containerSlot.setBackground(highlightColor); + return; + } + + // set the color back to the original, because they match + containerSlot.setBackground(ColorScheme.DARKER_GRAY_COLOR); + } + + abstract public void setupContainerPanel(final JPanel containerSlotsPanel); + +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupEquipmentPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupEquipmentPanel.java new file mode 100644 index 0000000000..7e0fecfa2b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupEquipmentPanel.java @@ -0,0 +1,91 @@ +package net.runelite.client.plugins.inventorysetups.ui; + +import net.runelite.api.EquipmentInventorySlot; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.inventorysetups.InventorySetup; +import net.runelite.client.plugins.inventorysetups.InventorySetupItem; +import net.runelite.client.plugins.inventorysetups.InventorySetupPlugin; +import net.runelite.client.ui.ColorScheme; + +import javax.swing.JPanel; +import java.awt.GridLayout; +import java.util.ArrayList; +import java.util.HashMap; + +public class InventorySetupEquipmentPanel extends InventorySetupContainerPanel +{ + private HashMap equipmentSlots; + + InventorySetupEquipmentPanel(final ItemManager itemManager, final InventorySetupPlugin plugin) + { + super(itemManager, plugin, "Equipment"); + } + + @Override + public void setupContainerPanel(final JPanel containerSlotsPanel) + { + this.equipmentSlots = new HashMap<>(); + for (EquipmentInventorySlot slot : EquipmentInventorySlot.values()) + { + equipmentSlots.put(slot, new InventorySetupSlot(ColorScheme.DARKER_GRAY_COLOR)); + } + + final GridLayout gridLayout = new GridLayout(5, 3, 1, 1); + containerSlotsPanel.setLayout(gridLayout); + + // add the grid layouts, including invisible ones + containerSlotsPanel.add(new InventorySetupSlot(ColorScheme.DARK_GRAY_COLOR)); + containerSlotsPanel.add(equipmentSlots.get(EquipmentInventorySlot.HEAD)); + containerSlotsPanel.add(new InventorySetupSlot(ColorScheme.DARK_GRAY_COLOR)); + containerSlotsPanel.add(equipmentSlots.get(EquipmentInventorySlot.CAPE)); + containerSlotsPanel.add(equipmentSlots.get(EquipmentInventorySlot.AMULET)); + containerSlotsPanel.add(equipmentSlots.get(EquipmentInventorySlot.AMMO)); + containerSlotsPanel.add(equipmentSlots.get(EquipmentInventorySlot.WEAPON)); + containerSlotsPanel.add(equipmentSlots.get(EquipmentInventorySlot.BODY)); + containerSlotsPanel.add(equipmentSlots.get(EquipmentInventorySlot.SHIELD)); + containerSlotsPanel.add(new InventorySetupSlot(ColorScheme.DARK_GRAY_COLOR)); + containerSlotsPanel.add(equipmentSlots.get(EquipmentInventorySlot.LEGS)); + containerSlotsPanel.add(new InventorySetupSlot(ColorScheme.DARK_GRAY_COLOR)); + containerSlotsPanel.add(equipmentSlots.get(EquipmentInventorySlot.GLOVES)); + containerSlotsPanel.add(equipmentSlots.get(EquipmentInventorySlot.BOOTS)); + containerSlotsPanel.add(equipmentSlots.get(EquipmentInventorySlot.RING)); + + } + + void setEquipmentSetupSlots(final InventorySetup setup) + { + final ArrayList equipment = setup.getEquipment(); + + for (final EquipmentInventorySlot slot : EquipmentInventorySlot.values()) + { + int i = slot.getSlotIdx(); + super.setContainerSlot(i, equipmentSlots.get(slot), equipment); + } + + validate(); + repaint(); + + } + + void highlightDifferences(final ArrayList currEquipment, final InventorySetup inventorySetup) + { + final ArrayList equipToCheck = inventorySetup.getEquipment(); + + assert currEquipment.size() == equipToCheck.size() : "size mismatch"; + + for (final EquipmentInventorySlot slot : EquipmentInventorySlot.values()) + { + + int slotIdx = slot.getSlotIdx(); + super.highlightDifferentSlotColor(equipToCheck.get(slotIdx), currEquipment.get(slotIdx), equipmentSlots.get(slot)); + } + } + + void resetEquipmentSlotsColor() + { + for (final EquipmentInventorySlot slot : EquipmentInventorySlot.values()) + { + equipmentSlots.get(slot).setBackground(ColorScheme.DARKER_GRAY_COLOR); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupInventoryPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupInventoryPanel.java new file mode 100644 index 0000000000..136f4603ea --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupInventoryPanel.java @@ -0,0 +1,78 @@ +package net.runelite.client.plugins.inventorysetups.ui; + +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.inventorysetups.InventorySetup; +import net.runelite.client.plugins.inventorysetups.InventorySetupItem; +import net.runelite.client.plugins.inventorysetups.InventorySetupPlugin; +import net.runelite.client.ui.ColorScheme; + +import javax.swing.JPanel; +import java.awt.GridLayout; +import java.util.ArrayList; + +public class InventorySetupInventoryPanel extends InventorySetupContainerPanel +{ + + private static final int ITEMS_PER_ROW = 4; + private static final int NUM_INVENTORY_ITEMS = 28; + + private ArrayList inventorySlots; + + InventorySetupInventoryPanel(final ItemManager itemManager, final InventorySetupPlugin plugin) + { + super(itemManager, plugin, "Inventory"); + } + + + @Override + public void setupContainerPanel(final JPanel containerSlotsPanel) + { + this.inventorySlots = new ArrayList<>(); + for (int i = 0; i < NUM_INVENTORY_ITEMS; i++) + { + inventorySlots.add(new InventorySetupSlot(ColorScheme.DARKER_GRAY_COLOR)); + } + + int numRows = (NUM_INVENTORY_ITEMS + ITEMS_PER_ROW - 1) / ITEMS_PER_ROW; + containerSlotsPanel.setLayout(new GridLayout(numRows, ITEMS_PER_ROW, 1, 1)); + for (int i = 0; i < NUM_INVENTORY_ITEMS; i++) + { + containerSlotsPanel.add(inventorySlots.get(i)); + } + } + + void setInventorySetupSlots(final InventorySetup setup) + { + ArrayList inventory = setup.getInventory(); + + for (int i = 0; i < NUM_INVENTORY_ITEMS; i++) + { + super.setContainerSlot(i, inventorySlots.get(i), inventory); + } + + validate(); + repaint(); + + } + + void highlightDifferentSlots(final ArrayList currInventory, final InventorySetup inventorySetup) + { + + final ArrayList inventoryToCheck = inventorySetup.getInventory(); + + assert currInventory.size() == inventoryToCheck.size() : "size mismatch"; + + for (int i = 0; i < NUM_INVENTORY_ITEMS; i++) + { + super.highlightDifferentSlotColor(inventoryToCheck.get(i), currInventory.get(i), inventorySlots.get(i)); + } + } + + void resetInventorySlotsColor() + { + for (InventorySetupSlot inventorySlot : inventorySlots) + { + inventorySlot.setBackground(ColorScheme.DARKER_GRAY_COLOR); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupPluginPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupPluginPanel.java new file mode 100644 index 0000000000..b1b88a02c7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupPluginPanel.java @@ -0,0 +1,287 @@ +package net.runelite.client.plugins.inventorysetups.ui; + +import net.runelite.api.InventoryID; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.inventorysetups.InventorySetup; +import net.runelite.client.plugins.inventorysetups.InventorySetupItem; +import net.runelite.client.plugins.inventorysetups.InventorySetupPlugin; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.PluginErrorPanel; +import net.runelite.client.util.ImageUtil; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.border.EmptyBorder; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ItemEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.util.ArrayList; + +public class InventorySetupPluginPanel extends PluginPanel +{ + + private static ImageIcon ADD_ICON; + private static ImageIcon ADD_HOVER_ICON; + private static ImageIcon REMOVE_ICON; + private static ImageIcon REMOVE_HOVER_ICON; + + private final JPanel noSetupsPanel; + private final JPanel invEqPanel; + + private final InventorySetupInventoryPanel invPanel; + private final InventorySetupEquipmentPanel eqpPanel; + + private final JComboBox setupComboBox; + + private final JLabel removeMarker; + + private final InventorySetupPlugin plugin; + + static + { + final BufferedImage addIcon = ImageUtil.getResourceStreamFromClass(InventorySetupPlugin.class, "add_icon.png"); + ADD_ICON = new ImageIcon(addIcon); + ADD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(addIcon, 0.53f)); + + final BufferedImage removeIcon = ImageUtil.getResourceStreamFromClass(InventorySetupPlugin.class, "remove_icon.png"); + REMOVE_ICON = new ImageIcon(removeIcon); + REMOVE_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(removeIcon, 0.53f)); + } + + public InventorySetupPluginPanel(final InventorySetupPlugin plugin, final ItemManager itemManager) + { + super(false); + this.plugin = plugin; + this.removeMarker = new JLabel(REMOVE_ICON); + this.invPanel = new InventorySetupInventoryPanel(itemManager, plugin); + this.eqpPanel = new InventorySetupEquipmentPanel(itemManager, plugin); + this.noSetupsPanel = new JPanel(); + this.invEqPanel = new JPanel(); + this.setupComboBox = new JComboBox<>(); + + // setup the title + final JLabel addMarker = new JLabel(ADD_ICON); + final JLabel title = new JLabel(); + title.setText("Inventory Setups"); + title.setForeground(Color.WHITE); + + // setup the add marker (+ sign in the top right) + addMarker.setToolTipText("Add a new inventory setup"); + addMarker.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + plugin.addInventorySetup(); + } + + @Override + public void mouseEntered(MouseEvent e) + { + addMarker.setIcon(ADD_HOVER_ICON); + } + + @Override + public void mouseExited(MouseEvent e) + { + addMarker.setIcon(ADD_ICON); + } + }); + + // setup the remove marker (X sign in the top right) + removeMarker.setToolTipText("Remove the current inventory setup"); + removeMarker.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + final String name = (String)setupComboBox.getSelectedItem(); + plugin.removeInventorySetup(name, true); + } + + @Override + public void mouseEntered(MouseEvent e) + { + if (removeMarker.isEnabled()) + { + removeMarker.setIcon(REMOVE_HOVER_ICON); + } + } + + @Override + public void mouseExited(MouseEvent e) + { + removeMarker.setIcon(REMOVE_ICON); + } + }); + + // setup the combo box for selection switching + // add empty to indicate the empty position + setupComboBox.addItem(""); + setupComboBox.setSelectedIndex(0); + setupComboBox.addItemListener(e -> + { + if (e.getStateChange() == ItemEvent.SELECTED) + { + String selection = (String)e.getItem(); + setCurrentInventorySetup(selection); + } + }); + + // the panel on the top right that holds the add and delete buttons + final JPanel markersPanel = new JPanel(); + markersPanel.setLayout(new FlowLayout(FlowLayout.RIGHT, 10, 0)); + markersPanel.add(removeMarker); + markersPanel.add(addMarker); + + // the top panel that has the title and the buttons + final JPanel titleAndMarkersPanel = new JPanel(); + titleAndMarkersPanel.setLayout(new BorderLayout()); + titleAndMarkersPanel.add(title, BorderLayout.WEST); + titleAndMarkersPanel.add(markersPanel, BorderLayout.EAST); + + // the panel that stays at the top and doesn't scroll + // contains the title, buttons, and the combo box + final JPanel northAnchoredPanel = new JPanel(); + northAnchoredPanel.setLayout(new BoxLayout(northAnchoredPanel, BoxLayout.Y_AXIS)); + northAnchoredPanel.setBorder(new EmptyBorder(0, 0, 10, 0)); + northAnchoredPanel.add(titleAndMarkersPanel); + northAnchoredPanel.add(Box.createRigidArea(new Dimension(0, 10))); + northAnchoredPanel.add(setupComboBox); + + // the panel that holds the inventory and equipment panels + final BoxLayout invEqLayout = new BoxLayout(invEqPanel, BoxLayout.Y_AXIS); + invEqPanel.setLayout(invEqLayout); + invEqPanel.add(invPanel); + invEqPanel.add(Box.createRigidArea(new Dimension(0, 10))); + invEqPanel.add(eqpPanel); + + // setup the error panel. It's wrapped around a normal panel + // so it doesn't stretch to fill the parent panel + final PluginErrorPanel errorPanel = new PluginErrorPanel(); + errorPanel.setContent("Inventory Setups", "Select or create an inventory setup."); + noSetupsPanel.add(errorPanel); + + // the panel that holds the inventory panels, and the error panel + final JPanel contentPanel = new JPanel(); + final BoxLayout contentLayout = new BoxLayout(contentPanel, BoxLayout.Y_AXIS); + contentPanel.setLayout(contentLayout); + contentPanel.add(invEqPanel); + contentPanel.add(noSetupsPanel); + + // wrapper for the main content panel to keep it from stretching + final JPanel contentWrapper = new JPanel(new BorderLayout()); + contentWrapper.add(Box.createGlue(), BorderLayout.CENTER); + contentWrapper.add(contentPanel, BorderLayout.NORTH); + final JScrollPane contentWrapperPane = new JScrollPane(contentWrapper); + contentWrapperPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + setLayout(new BorderLayout()); + setBorder(new EmptyBorder(10, 10, 10, 10)); + add(northAnchoredPanel, BorderLayout.NORTH); + add(contentWrapperPane, BorderLayout.CENTER); + + // show the no setups panel on startup + showNoSetupsPanel(); + + } + + public void showNoSetupsPanel() + { + setupComboBox.setSelectedIndex(0); + removeMarker.setEnabled(false); + noSetupsPanel.setVisible(true); + invEqPanel.setVisible(false); + } + + private void showHasSetupPanel(final String name) + { + setupComboBox.setSelectedItem(name); + removeMarker.setEnabled(true); + noSetupsPanel.setVisible(false); + invEqPanel.setVisible(true); + } + + public void setCurrentInventorySetup(final String name) + { + if (name.isEmpty()) + { + showNoSetupsPanel(); + return; + } + + showHasSetupPanel(name); + + final InventorySetup inventorySetup = plugin.getInventorySetup(name); + + invPanel.setInventorySetupSlots(inventorySetup); + eqpPanel.setEquipmentSetupSlots(inventorySetup); + + if (plugin.getHighlightDifference()) + { + final ArrayList normInv = plugin.getNormalizedContainer(InventoryID.INVENTORY); + final ArrayList normEqp = plugin.getNormalizedContainer(InventoryID.EQUIPMENT); + + highlightDifferences(normInv, inventorySetup, InventoryID.INVENTORY); + highlightDifferences(normEqp, inventorySetup, InventoryID.EQUIPMENT); + } + else + { + invPanel.resetInventorySlotsColor(); + eqpPanel.resetEquipmentSlotsColor(); + } + + validate(); + repaint(); + } + + public void addInventorySetup(final String name) + { + setupComboBox.addItem(name); + } + + public void removeInventorySetup(final String name) + { + setupComboBox.removeItem(name); + showNoSetupsPanel(); + + invPanel.resetInventorySlotsColor(); + eqpPanel.resetEquipmentSlotsColor(); + + validate(); + repaint(); + } + + public void highlightDifferences(final ArrayList container, + final InventorySetup setupToCheck, + final InventoryID type) + { + switch (type) + { + case INVENTORY: + invPanel.highlightDifferentSlots(container, setupToCheck); + break; + + case EQUIPMENT: + eqpPanel.highlightDifferences(container, setupToCheck); + break; + } + } + + public final String getSelectedInventorySetup() + { + return (String)setupComboBox.getSelectedItem(); + } + + +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupSlot.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupSlot.java new file mode 100644 index 0000000000..13bbbaef14 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventorysetups/ui/InventorySetupSlot.java @@ -0,0 +1,45 @@ +package net.runelite.client.plugins.inventorysetups.ui; + +import net.runelite.client.game.AsyncBufferedImage; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import java.awt.Color; +import java.awt.Dimension; + +public class InventorySetupSlot extends JPanel +{ + + private final JLabel imageLabel; + + public InventorySetupSlot(Color color) + { + imageLabel = new JLabel(); + imageLabel.setVerticalAlignment(SwingConstants.CENTER); + setPreferredSize(new Dimension(46, 42)); + setBackground(color); + add(imageLabel); + + } + + public void setImageLabel(String toolTip, AsyncBufferedImage itemImage) + { + if (itemImage == null || toolTip == null) + { + imageLabel.setToolTipText(""); + imageLabel.setIcon(null); + imageLabel.revalidate(); + return; + } + + imageLabel.setToolTipText(toolTip); + itemImage.addTo(imageLabel); + + validate(); + repaint(); + } + + + +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/lootingbagviewer/LootingBagViewerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/lootingbagviewer/LootingBagViewerOverlay.java new file mode 100644 index 0000000000..85a07780de --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/lootingbagviewer/LootingBagViewerOverlay.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018 AWPH-I + * 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.lootingbagviewer; + +import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import net.runelite.client.game.ItemManager; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.ImageComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; + +import javax.inject.Inject; +import java.awt.*; +import java.awt.image.BufferedImage; + +class LootingBagViewerOverlay extends Overlay +{ + private static final int INVENTORY_SIZE = 28; + private static final int PLACEHOLDER_WIDTH = 36; + private static final int PLACEHOLDER_HEIGHT = 32; + private static final ImageComponent PLACEHOLDER_IMAGE = new ImageComponent(new BufferedImage(PLACEHOLDER_WIDTH, PLACEHOLDER_HEIGHT, BufferedImage.TYPE_4BYTE_ABGR)); + + private final Client client; + private final ItemManager itemManager; + + private final PanelComponent panelComponent = new PanelComponent(); + + private ItemContainer itemContainer; + private Item[] items; + + @Inject + private LootingBagViewerOverlay(Client client, ItemManager itemManager) + { + setPosition(OverlayPosition.BOTTOM_RIGHT); + panelComponent.setWrapping(4); + panelComponent.setGap(new Point(6, 4)); + panelComponent.setOrientation(PanelComponent.Orientation.HORIZONTAL); + this.itemManager = itemManager; + this.client = client; + + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (itemContainer == null) + { + if(client.getItemContainer(InventoryID.LOOTING_BAG) != null) { + itemContainer = client.getItemContainer(InventoryID.LOOTING_BAG); + items = itemContainer.getItems(); + } + return null; + } + else if(itemContainer != null && client.getItemContainer(InventoryID.LOOTING_BAG) != null) + { + itemContainer = client.getItemContainer(InventoryID.LOOTING_BAG); + Item[] tempItems = itemContainer.getItems(); + + for(int i = 0; i < items.length; i++) + { + if(!items[i].equals(tempItems[i])) + { + items = tempItems; + } + } + } + + panelComponent.getChildren().clear(); + + for (int i = 0; i < INVENTORY_SIZE; i++) + { + if (i < items.length) + { + final Item item = items[i]; + if (item.getQuantity() > 0) + { + final BufferedImage image = getImage(item); + if (image != null) + { + panelComponent.getChildren().add(new ImageComponent(image)); + continue; + } + } + } + + // put a placeholder image so each item is aligned properly and the panel is not resized + panelComponent.getChildren().add(PLACEHOLDER_IMAGE); + } + + return panelComponent.render(graphics); + } + + private BufferedImage getImage(Item item) + { + return itemManager.getImage(item.getId(), item.getQuantity(), item.getQuantity() > 1); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/lootingbagviewer/LootingBagViewerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/lootingbagviewer/LootingBagViewerPlugin.java new file mode 100644 index 0000000000..5729c69993 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/lootingbagviewer/LootingBagViewerPlugin.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018 AWPH-I + * 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.lootingbagviewer; + +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +import javax.inject.Inject; + +@PluginDescriptor( + name = "PvP Looting Bag Viewer", + description = "Add an overlay showing the contents of your looting bag", + tags = {"alternate", "items", "overlay", "second"}, + type = "utility", + enabledByDefault = false +) +public class LootingBagViewerPlugin extends Plugin +{ + @Inject + private net.runelite.client.plugins.lootingbagviewer.LootingBagViewerOverlay overlay; + + @Inject + private OverlayManager overlayManager; + + @Override + public void startUp() + { + overlayManager.add(overlay); + } + + @Override + public void shutDown() + { + overlayManager.remove(overlay); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/BatSolver.java b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/BatSolver.java new file mode 100644 index 0000000000..ebbad81a54 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/BatSolver.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2018, Tim Lehner + * 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.raidsthieving.BatSolver; + +import java.util.Map; +import java.util.HashSet; +import java.util.HashMap; +import java.util.TreeSet; +import java.util.List; +import java.util.ArrayList; +import static net.runelite.client.plugins.raidsthieving.BatSolver.SolutionSet.SOLUTION_SETS; + +public class BatSolver +{ + private Map numberOfSolutionsWithPoison; + private final SolutionSet solution; + + private final HashSet grubsChests; + + public BatSolver(ThievingRoomType roomType) + { + solution = new SolutionSet(roomType); + grubsChests = new HashSet<>(); + } + + public void addEmptyChest(int chestId) + { + // When a new empty chest is found, add it to the current solution set + solution.addEmptyChest(chestId); + calculateChanceOfPoison(); + } + + public void addGrubsChest(int chestId) + { + // When a chest with grubs is found, keep track of it to invalidate solutions + grubsChests.add(chestId); + calculateChanceOfPoison(); + } + + public TreeSet matchSolutions() + { + TreeSet possibleEmptyChests = new TreeSet<>(); + for (SolutionSet knownSolution : SolutionSet.SOLUTION_SETS) + { + if (knownSolution.getType() == solution.getType() && matchSolution(knownSolution)) + { + possibleEmptyChests.addAll(knownSolution.getEmptyChests()); + } + } + + return possibleEmptyChests; + } + + private boolean matchSolution(SolutionSet testSolution) + { + for (Integer grubsChest : grubsChests) + { + if (testSolution.containsChest(grubsChest)) + { + // If one of the chests is known to have grubs, it cannot be a solution + return false; + } + } + + boolean matchesAll = true; + boolean everMatched = false; + for (int i : solution.getEmptyChests()) + { + if (!testSolution.containsChest(i)) + { + matchesAll = false; + } + else + { + everMatched = true; + } + } + return matchesAll && everMatched; + } + + public ThievingRoomType getType() + { + return solution.getType(); + } + + + public void calculateChanceOfPoison() + { + if (getType() == null) + { + numberOfSolutionsWithPoison = null; + return; + } + + numberOfSolutionsWithPoison = new HashMap<>(); + for (SolutionSet sol : getPosssibleSolutions()) + { + if (getType() == sol.getType() && (solution.getEmptyChests().size() == 0 || matchSolution(sol))) + { + for (Integer i : sol.getEmptyChests()) + { + if (numberOfSolutionsWithPoison.containsKey(i)) + { + numberOfSolutionsWithPoison.put(i, numberOfSolutionsWithPoison.get(i) + 1); + } + else + { + numberOfSolutionsWithPoison.put(i, 1); + } + } + } + } + } + + private List getPosssibleSolutions() + { + List possibleSolutions = new ArrayList<>(); + for (SolutionSet soln : SOLUTION_SETS) + { + // Check if we've found grubs in one of the chests, invalidating it as an solution + boolean foundMatch = false; + for (int i : grubsChests) + { + if (soln.containsChest(i)) + { + foundMatch = true; + } + } + if (!foundMatch) + { + possibleSolutions.add(soln); + } + } + return possibleSolutions; + } + + public double relativeLikelihoodPoison(int chestId) + { + // Returns a double between 0 and 1 of how likely the chest has poison based on the number of possible solutions + // Uses a Sigmoid like function to give good contrast in drawn opacity, + // perhaps could be changed to something more accurate quantitavely. + if (numberOfSolutionsWithPoison == null) + { + calculateChanceOfPoison(); + } + if (numberOfSolutionsWithPoison == null) + { + return 1.0; + } + int mostFrequentPoison = 0; + for (Map.Entry entry : numberOfSolutionsWithPoison.entrySet()) + { + if (entry.getValue() > mostFrequentPoison) + { + mostFrequentPoison = entry.getValue(); + } + } + int timesFound = 0; + if (numberOfSolutionsWithPoison.containsKey(chestId)) + { + timesFound = numberOfSolutionsWithPoison.get(chestId); + } + double chestChance = (double) (timesFound) / (double) (mostFrequentPoison); + return 1. / (1 + Math.exp(5 - 10 * chestChance)); + } + + public int getNumberOfEmptyChests() + { + return solution.getEmptyChests().size(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/ChestIdentifier.java b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/ChestIdentifier.java new file mode 100644 index 0000000000..7346e4ecf6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/ChestIdentifier.java @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2018, Tim Lehner + * 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.raidsthieving.BatSolver; + +import net.runelite.client.plugins.raidsthieving.InstancePoint; +import net.runelite.client.plugins.raidsthieving.ThievingChest; +import java.util.HashMap; +import java.util.Map; + +public class ChestIdentifier +{ + public ChestIdentifier(ThievingRoomType roomType) + { + chestIds = new HashMap<>(); + switch (roomType) + { + case LEFT_TURN: + chestIds.put(new InstancePoint(3283, 5379), 1); + chestIds.put(new InstancePoint(3285, 5380), 2); + chestIds.put(new InstancePoint(3279, 5381), 3); + chestIds.put(new InstancePoint(3287, 5382), 4); + chestIds.put(new InstancePoint(3281, 5382), 5); + chestIds.put(new InstancePoint(3284, 5383), 6); + chestIds.put(new InstancePoint(3283, 5384), 7); + chestIds.put(new InstancePoint(3286, 5384), 8); + chestIds.put(new InstancePoint(3288, 5384), 9); + chestIds.put(new InstancePoint(3277, 5385), 10); + chestIds.put(new InstancePoint(3280, 5385), 11); + chestIds.put(new InstancePoint(3285, 5386), 12); + chestIds.put(new InstancePoint(3290, 5386), 13); + chestIds.put(new InstancePoint(3275, 5387), 14); + chestIds.put(new InstancePoint(3287, 5387), 15); + chestIds.put(new InstancePoint(3288, 5387), 16); + chestIds.put(new InstancePoint(3281, 5388), 17); + chestIds.put(new InstancePoint(3291, 5388), 18); + chestIds.put(new InstancePoint(3280, 5389), 19); + chestIds.put(new InstancePoint(3285, 5389), 20); + chestIds.put(new InstancePoint(3289, 5389), 21); + chestIds.put(new InstancePoint(3283, 5390), 22); + chestIds.put(new InstancePoint(3285, 5390), 23); + chestIds.put(new InstancePoint(3288, 5390), 24); + chestIds.put(new InstancePoint(3290, 5390), 25); + chestIds.put(new InstancePoint(3282, 5391), 26); + chestIds.put(new InstancePoint(3289, 5391), 27); + chestIds.put(new InstancePoint(3292, 5391), 28); + chestIds.put(new InstancePoint(3279, 5392), 29); + chestIds.put(new InstancePoint(3276, 5393), 30); + chestIds.put(new InstancePoint(3279, 5393), 31); + chestIds.put(new InstancePoint(3284, 5393), 32); + chestIds.put(new InstancePoint(3285, 5393), 33); + chestIds.put(new InstancePoint(3291, 5393), 34); + chestIds.put(new InstancePoint(3275, 5394), 35); + chestIds.put(new InstancePoint(3277, 5394), 36); + chestIds.put(new InstancePoint(3288, 5394), 37); + chestIds.put(new InstancePoint(3276, 5395), 38); + chestIds.put(new InstancePoint(3281, 5395), 39); + chestIds.put(new InstancePoint(3285, 5395), 40); + chestIds.put(new InstancePoint(3287, 5395), 41); + chestIds.put(new InstancePoint(3289, 5395), 42); + chestIds.put(new InstancePoint(3274, 5396), 43); + chestIds.put(new InstancePoint(3283, 5396), 44); + chestIds.put(new InstancePoint(3285, 5396), 45); + chestIds.put(new InstancePoint(3288, 5396), 46); + chestIds.put(new InstancePoint(3272, 5397), 47); + chestIds.put(new InstancePoint(3280, 5397), 48); + chestIds.put(new InstancePoint(3277, 5398), 49); + chestIds.put(new InstancePoint(3281, 5398), 50); + chestIds.put(new InstancePoint(3284, 5398), 51); + chestIds.put(new InstancePoint(3276, 5399), 52); + chestIds.put(new InstancePoint(3278, 5399), 53); + chestIds.put(new InstancePoint(3283, 5399), 54); + chestIds.put(new InstancePoint(3285, 5399), 55); + chestIds.put(new InstancePoint(3277, 5400), 56); + chestIds.put(new InstancePoint(3284, 5400), 57); + chestIds.put(new InstancePoint(3288, 5400), 58); + chestIds.put(new InstancePoint(3281, 5401), 59); + chestIds.put(new InstancePoint(3286, 5401), 60); + chestIds.put(new InstancePoint(3279, 5402), 61); + chestIds.put(new InstancePoint(3285, 5402), 62); + chestIds.put(new InstancePoint(3280, 5403), 63); + chestIds.put(new InstancePoint(3283, 5403), 64); + break; + case RIGHT_TURN: + chestIds.put(new InstancePoint(3338, 5405), 1); + chestIds.put(new InstancePoint(3334, 5405), 2); + chestIds.put(new InstancePoint(3342, 5404), 3); + chestIds.put(new InstancePoint(3340, 5404), 4); + chestIds.put(new InstancePoint(3345, 5403), 5); + chestIds.put(new InstancePoint(3334, 5403), 6); + chestIds.put(new InstancePoint(3330, 5403), 7); + chestIds.put(new InstancePoint(3343, 5402), 8); + chestIds.put(new InstancePoint(3342, 5402), 9); + chestIds.put(new InstancePoint(3339, 5402), 10); + chestIds.put(new InstancePoint(3338, 5402), 11); + chestIds.put(new InstancePoint(3336, 5402), 12); + chestIds.put(new InstancePoint(3347, 5401), 13); + chestIds.put(new InstancePoint(3330, 5401), 14); + chestIds.put(new InstancePoint(3345, 5400), 15); + chestIds.put(new InstancePoint(3341, 5400), 16); + chestIds.put(new InstancePoint(3337, 5400), 17); + chestIds.put(new InstancePoint(3334, 5400), 18); + chestIds.put(new InstancePoint(3345, 5399), 19); + chestIds.put(new InstancePoint(3343, 5399), 20); + chestIds.put(new InstancePoint(3340, 5399), 21); + chestIds.put(new InstancePoint(3335, 5399), 22); + chestIds.put(new InstancePoint(3331, 5399), 23); + chestIds.put(new InstancePoint(3338, 5398), 24); + chestIds.put(new InstancePoint(3337, 5398), 25); + chestIds.put(new InstancePoint(3345, 5397), 26); + chestIds.put(new InstancePoint(3341, 5397), 27); + chestIds.put(new InstancePoint(3334, 5397), 28); + chestIds.put(new InstancePoint(3331, 5397), 29); + chestIds.put(new InstancePoint(3346, 5396), 30); + chestIds.put(new InstancePoint(3343, 5396), 31); + chestIds.put(new InstancePoint(3339, 5396), 32); + chestIds.put(new InstancePoint(3335, 5396), 33); + chestIds.put(new InstancePoint(3333, 5396), 34); + chestIds.put(new InstancePoint(3340, 5395), 35); + chestIds.put(new InstancePoint(3337, 5395), 36); + chestIds.put(new InstancePoint(3334, 5395), 37); + chestIds.put(new InstancePoint(3345, 5394), 38); + chestIds.put(new InstancePoint(3342, 5394), 39); + chestIds.put(new InstancePoint(3332, 5394), 40); + chestIds.put(new InstancePoint(3343, 5393), 41); + chestIds.put(new InstancePoint(3341, 5393), 42); + chestIds.put(new InstancePoint(3338, 5393), 43); + chestIds.put(new InstancePoint(3335, 5393), 44); + chestIds.put(new InstancePoint(3334, 5393), 45); + chestIds.put(new InstancePoint(3346, 5392), 46); + chestIds.put(new InstancePoint(3342, 5392), 47); + chestIds.put(new InstancePoint(3332, 5392), 48); + chestIds.put(new InstancePoint(3350, 5391), 49); + chestIds.put(new InstancePoint(3346, 5391), 50); + chestIds.put(new InstancePoint(3340, 5391), 51); + chestIds.put(new InstancePoint(3339, 5391), 52); + chestIds.put(new InstancePoint(3336, 5391), 53); + chestIds.put(new InstancePoint(3333, 5391), 54); + chestIds.put(new InstancePoint(3349, 5390), 55); + chestIds.put(new InstancePoint(3343, 5390), 56); + chestIds.put(new InstancePoint(3337, 5390), 57); + chestIds.put(new InstancePoint(3335, 5390), 58); + chestIds.put(new InstancePoint(3344, 5389), 59); + chestIds.put(new InstancePoint(3340, 5389), 60); + chestIds.put(new InstancePoint(3336, 5389), 61); + chestIds.put(new InstancePoint(3333, 5389), 62); + chestIds.put(new InstancePoint(3346, 5388), 63); + chestIds.put(new InstancePoint(3340, 5387), 64); + chestIds.put(new InstancePoint(3337, 5386), 65); + chestIds.put(new InstancePoint(3333, 5386), 66); + chestIds.put(new InstancePoint(3338, 5385), 67); + chestIds.put(new InstancePoint(3336, 5385), 68); + chestIds.put(new InstancePoint(3337, 5384), 69); + chestIds.put(new InstancePoint(3340, 5382), 70); + chestIds.put(new InstancePoint(3334, 5383), 71); + chestIds.put(new InstancePoint(3340, 5379), 72); + chestIds.put(new InstancePoint(3338, 5380), 73); + chestIds.put(new InstancePoint(3336, 5381), 74); + break; + case STRAIGHT: + chestIds.put(new InstancePoint(3308, 5378), 1); + chestIds.put(new InstancePoint(3305, 5379), 2); + chestIds.put(new InstancePoint(3307, 5379), 3); + chestIds.put(new InstancePoint(3304, 5381), 4); + chestIds.put(new InstancePoint(3310, 5381), 5); + chestIds.put(new InstancePoint(3302, 5382), 6); + chestIds.put(new InstancePoint(3307, 5382), 7); + chestIds.put(new InstancePoint(3312, 5382), 8); + chestIds.put(new InstancePoint(3317, 5382), 9); + chestIds.put(new InstancePoint(3319, 5382), 10); + chestIds.put(new InstancePoint(3304, 5383), 11); + chestIds.put(new InstancePoint(3305, 5383), 12); + chestIds.put(new InstancePoint(3307, 5383), 13); + chestIds.put(new InstancePoint(3310, 5383), 14); + chestIds.put(new InstancePoint(3315, 5383), 15); + chestIds.put(new InstancePoint(3320, 5383), 16); + chestIds.put(new InstancePoint(3300, 5384), 17); + chestIds.put(new InstancePoint(3309, 5384), 18); + chestIds.put(new InstancePoint(3311, 5384), 19); + chestIds.put(new InstancePoint(3313, 5384), 20); + chestIds.put(new InstancePoint(3317, 5384), 21); + chestIds.put(new InstancePoint(3318, 5384), 22); + chestIds.put(new InstancePoint(3302, 5385), 23); + chestIds.put(new InstancePoint(3306, 5385), 24); + chestIds.put(new InstancePoint(3310, 5385), 25); + chestIds.put(new InstancePoint(3313, 5385), 26); + chestIds.put(new InstancePoint(3320, 5385), 27); + chestIds.put(new InstancePoint(3302, 5386), 28); + chestIds.put(new InstancePoint(3305, 5386), 29); + chestIds.put(new InstancePoint(3316, 5386), 30); + chestIds.put(new InstancePoint(3321, 5386), 31); + chestIds.put(new InstancePoint(3300, 5387), 32); + chestIds.put(new InstancePoint(3308, 5387), 33); + chestIds.put(new InstancePoint(3314, 5387), 34); + chestIds.put(new InstancePoint(3317, 5387), 35); + chestIds.put(new InstancePoint(3301, 5388), 36); + chestIds.put(new InstancePoint(3306, 5388), 37); + chestIds.put(new InstancePoint(3312, 5388), 38); + chestIds.put(new InstancePoint(3322, 5388), 39); + chestIds.put(new InstancePoint(3309, 5389), 40); + chestIds.put(new InstancePoint(3311, 5389), 41); + chestIds.put(new InstancePoint(3313, 5389), 42); + chestIds.put(new InstancePoint(3316, 5389), 43); + chestIds.put(new InstancePoint(3320, 5389), 44); + chestIds.put(new InstancePoint(3300, 5390), 45); + chestIds.put(new InstancePoint(3303, 5390), 46); + chestIds.put(new InstancePoint(3304, 5390), 47); + chestIds.put(new InstancePoint(3312, 5390), 48); + chestIds.put(new InstancePoint(3320, 5390), 49); + chestIds.put(new InstancePoint(3307, 5391), 50); + chestIds.put(new InstancePoint(3310, 5391), 51); + chestIds.put(new InstancePoint(3317, 5391), 52); + chestIds.put(new InstancePoint(3318, 5391), 53); + chestIds.put(new InstancePoint(3323, 5391), 54); + chestIds.put(new InstancePoint(3301, 5392), 55); + chestIds.put(new InstancePoint(3303, 5392), 56); + chestIds.put(new InstancePoint(3309, 5392), 57); + chestIds.put(new InstancePoint(3314, 5392), 58); + chestIds.put(new InstancePoint(3322, 5392), 59); + chestIds.put(new InstancePoint(3305, 5393), 60); + chestIds.put(new InstancePoint(3307, 5393), 61); + chestIds.put(new InstancePoint(3316, 5393), 62); + chestIds.put(new InstancePoint(3309, 5394), 63); + chestIds.put(new InstancePoint(3312, 5394), 64); + chestIds.put(new InstancePoint(3322, 5394), 65); + chestIds.put(new InstancePoint(3310, 5379), 66); + break; + } + + } + + public int indentifyChest(ThievingChest chest) + { + int id = chestIds.get(chest.getInstancePoint()); + chest.setChestId(id); + return id; + } + + private Map chestIds; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/SolutionSet.java b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/SolutionSet.java new file mode 100644 index 0000000000..dc1c3c3dc2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/SolutionSet.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2018, Tim Lehner + * 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.raidsthieving.BatSolver; + +import lombok.Getter; +import java.util.HashSet; +import java.util.Arrays; +import java.util.Set; + +// Each Thieving room has 4 empty chests +// User-reported data shows these 4 come in groups, +// +// e.g. if there is an empty chest in L room chest 1, the other empty chests could be 16, 17, 38, 54, 55 +// See https://dikkenoob.github.io/ for more information + +public class SolutionSet +{ + public static final SolutionSet[] SOLUTION_SETS = + { + new SolutionSet(ThievingRoomType.LEFT_TURN, 1, 16, 17, 55), + new SolutionSet(ThievingRoomType.LEFT_TURN, 1, 17, 38, 54), + new SolutionSet(ThievingRoomType.LEFT_TURN, 2, 7, 21, 37), + new SolutionSet(ThievingRoomType.LEFT_TURN, 3, 5, 19, 30), + new SolutionSet(ThievingRoomType.LEFT_TURN, 3, 11, 15, 40), + new SolutionSet(ThievingRoomType.LEFT_TURN, 4, 22, 27, 46), + new SolutionSet(ThievingRoomType.LEFT_TURN, 5, 9, 19, 45), + new SolutionSet(ThievingRoomType.LEFT_TURN, 6, 24, 26, 41), + new SolutionSet(ThievingRoomType.LEFT_TURN, 6, 26, 32, 52), + new SolutionSet(ThievingRoomType.LEFT_TURN, 7, 13, 44, 59), + new SolutionSet(ThievingRoomType.LEFT_TURN, 8, 14, 41, 43), + new SolutionSet(ThievingRoomType.LEFT_TURN, 8, 10, 28, 33), + new SolutionSet(ThievingRoomType.LEFT_TURN, 8, 31, 47, 50), + new SolutionSet(ThievingRoomType.LEFT_TURN, 10, 35, 54, 63), + new SolutionSet(ThievingRoomType.LEFT_TURN, 10, 30, 32, 59), + new SolutionSet(ThievingRoomType.LEFT_TURN, 12, 40, 53, 56), + new SolutionSet(ThievingRoomType.LEFT_TURN, 12, 13, 42, 54), + new SolutionSet(ThievingRoomType.LEFT_TURN, 13, 22, 27, 46), + new SolutionSet(ThievingRoomType.LEFT_TURN, 14, 18, 23, 51), + new SolutionSet(ThievingRoomType.LEFT_TURN, 15, 43, 44, 58), + new SolutionSet(ThievingRoomType.LEFT_TURN, 15, 16, 42, 45), + new SolutionSet(ThievingRoomType.LEFT_TURN, 20, 29, 45, 51), + new SolutionSet(ThievingRoomType.LEFT_TURN, 20, 25, 32, 34), + new SolutionSet(ThievingRoomType.LEFT_TURN, 20, 28, 51, 62), + new SolutionSet(ThievingRoomType.LEFT_TURN, 21, 39, 41, 58), + new SolutionSet(ThievingRoomType.LEFT_TURN, 22, 25, 54, 64), + new SolutionSet(ThievingRoomType.LEFT_TURN, 23, 31, 47, 55), + new SolutionSet(ThievingRoomType.LEFT_TURN, 23, 33, 37, 60), + new SolutionSet(ThievingRoomType.LEFT_TURN, 24, 34, 55), + new SolutionSet(ThievingRoomType.LEFT_TURN, 26, 50, 63, 27), + new SolutionSet(ThievingRoomType.LEFT_TURN, 29, 39, 41, 61), + new SolutionSet(ThievingRoomType.LEFT_TURN, 33, 46, 52, 57), + new SolutionSet(ThievingRoomType.LEFT_TURN, 34, 45, 49, 60), + new SolutionSet(ThievingRoomType.LEFT_TURN, 36, 40, 42, 62), + new SolutionSet(ThievingRoomType.LEFT_TURN, 37, 38, 51, 64), + new SolutionSet(ThievingRoomType.LEFT_TURN, 48, 53, 55, 56), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 1, 6, 28, 41), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 1, 42, 55, 60), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 2, 10, 31, 44), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 2, 33, 51, 68), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 3, 31, 43, 46), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 3, 5, 21, 48), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 4, 20, 24, 33), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 4, 38, 47), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 5, 21, 48), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 5, 17, 35, 63), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 7, 17, 45, 47), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 7, 37, 41, 52), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 8, 13, 40, 42), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 8, 20, 24, 30), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 9, 15, 23, 35), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 11, 13, 21, 50), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 11, 18, 37, 39), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 12, 14, 27, 34), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 14, 45, 67, 71), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 16, 22, 29, 32), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 18, 28, 31, 64), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 19, 21, 63, 69), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 20, 51, 68, 72), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 22, 29, 56, 61), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 23, 53, 66, 74), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 26, 35, 53, 59), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 27, 30, 55, 57), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 31, 58, 60, 73), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 34, 57, 58, 70), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 38, 56, 61, 70), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 40, 54, 65, 72), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 42, 46, 65), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 47, 49, 66, 67), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 48, 62, 69), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 9, 19, 32, 41), + new SolutionSet(ThievingRoomType.RIGHT_TURN, 16, 26, 36, 39), + new SolutionSet(ThievingRoomType.STRAIGHT, 1, 39, 43, 51), + new SolutionSet(ThievingRoomType.STRAIGHT, 2, 15, 20, 53), + new SolutionSet(ThievingRoomType.STRAIGHT, 3, 10, 42, 44), + new SolutionSet(ThievingRoomType.STRAIGHT, 4, 14, 38, 52), + new SolutionSet(ThievingRoomType.STRAIGHT, 5, 6, 35, 41), + new SolutionSet(ThievingRoomType.STRAIGHT, 7, 16, 34, 49), + new SolutionSet(ThievingRoomType.STRAIGHT, 9, 12, 26, 27), + new SolutionSet(ThievingRoomType.STRAIGHT, 13, 25, 30, 31), + new SolutionSet(ThievingRoomType.STRAIGHT, 15, 20, 53), + new SolutionSet(ThievingRoomType.STRAIGHT, 17, 24, 34, 58), + new SolutionSet(ThievingRoomType.STRAIGHT, 18, 23, 35, 57), + new SolutionSet(ThievingRoomType.STRAIGHT, 19, 26, 47, 65), + new SolutionSet(ThievingRoomType.STRAIGHT, 21, 33, 36, 61), + new SolutionSet(ThievingRoomType.STRAIGHT, 21, 54, 66), + new SolutionSet(ThievingRoomType.STRAIGHT, 22, 25, 46, 55), + new SolutionSet(ThievingRoomType.STRAIGHT, 24, 34, 58), + new SolutionSet(ThievingRoomType.STRAIGHT, 28, 40, 52, 62), + new SolutionSet(ThievingRoomType.STRAIGHT, 29, 41, 42, 63), + new SolutionSet(ThievingRoomType.STRAIGHT, 30, 32, 37, 64), + new SolutionSet(ThievingRoomType.STRAIGHT, 39, 43, 51), + new SolutionSet(ThievingRoomType.STRAIGHT, 43, 45, 50, 60), + new SolutionSet(ThievingRoomType.STRAIGHT, 51, 53, 56, 59) + }; + + SolutionSet(ThievingRoomType type) + { + this.type = type; + emptyChests = new HashSet<>(); + } + + private SolutionSet(ThievingRoomType type, Integer... emptyChests) + { + this.type = type; + this.emptyChests = new HashSet<>(Arrays.asList(emptyChests)); + } + + public void addEmptyChest(int chestId) + { + emptyChests.add(chestId); + } + + public boolean containsChest(int chestId) + { + return emptyChests.contains(chestId); + } + + @Getter + private ThievingRoomType type; + + @Getter + private Set emptyChests; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/ThievingRoomType.java b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/ThievingRoomType.java new file mode 100644 index 0000000000..f709b2d435 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/BatSolver/ThievingRoomType.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Tim Lehner + * 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.raidsthieving.BatSolver; + +// There are three distinct Thieving rooms, distinguished by the position of the entrance relative to the exit +// e.g. If you enter the room and must turn left to get to the exit and trough, this is a LEFT_TURN + +import net.runelite.client.plugins.raidsthieving.InstancePoint; + +public enum ThievingRoomType +{ + LEFT_TURN(3271, 5389), + RIGHT_TURN(3350, 5399), + STRAIGHT(3317, 5397); + + private final int x; + private final int y; + + ThievingRoomType(int x, int y) + { + this.x = x; + this.y = y; + } + + public static ThievingRoomType IdentifyByInstancePoint(InstancePoint point) + { + for (ThievingRoomType type : ThievingRoomType.values()) + { + if (Math.abs(type.x - point.getX()) <= 1 && + Math.abs(type.y - point.getY()) <= 1) + { + return type; + } + } + + return null; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/ChestOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/ChestOverlay.java new file mode 100644 index 0000000000..5e09486f89 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/ChestOverlay.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2018, Tim Lehner + * 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.raidsthieving; + +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.plugins.raidsthieving.BatSolver.BatSolver; +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.components.ProgressPieComponent; +import javax.inject.Inject; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.util.Map; +import java.util.TreeSet; + +/** + * Represents the overlay that shows timers on traps that are placed by the + * player. + */ +public class ChestOverlay extends Overlay +{ + + private final Client client; + private final RaidsThievingPlugin plugin; + private final RaidsThievingConfig config; + + @Inject + ChestOverlay(Client client, RaidsThievingPlugin plugin, RaidsThievingConfig config) + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + this.plugin = plugin; + this.config = config; + this.client = client; + } + + @Override + public Dimension render(Graphics2D graphics) + { + drawChests(graphics); + return null; + } + + /** + * Updates the timer colors. + */ + public void updateConfig() + { + } + + /** + * Iterates over all the traps that were placed by the local player, and + * draws a circle or a timer on the trap, depending on the trap state. + * + * @param graphics + */ + private void drawChests(Graphics2D graphics) + { + + for (Map.Entry entry : plugin.getChests().entrySet()) + { + ThievingChest chest = entry.getValue(); + WorldPoint pos = entry.getKey(); + + + if (chest != null) + { + if (!plugin.isBatsFound() && !chest.isEverOpened()) + { + if (shouldDrawChest(pos)) + { + Color drawColor = new Color(config.getPotentialBatColor().getRed(), + config.getPotentialBatColor().getGreen(), + config.getPotentialBatColor().getBlue(), + getChestOpacity(pos)); + drawCircleOnTrap(graphics, chest, drawColor); + } + } + if (chest.isPoison()) + { + drawCircleOnTrap(graphics, chest, config.getPoisonTrapColor()); + } + } + } + } + + private boolean shouldDrawChest(WorldPoint chestPos) + { + if (plugin.numberOfEmptyChestsFound() == 0) + { + return true; + } + int chestId = plugin.getChestId(chestPos); + BatSolver solver = plugin.getSolver(); + if (solver != null && chestId != -1) + { + TreeSet matches = solver.matchSolutions(); + return matches.contains(chestId) || matches.size() == 0; + } + return true; + } + + /** + * Draws a timer on a given trap. + * + * @param graphics + * @param chest The chest on which the circle needs to be drawn + * @param fill The fill color of the timer + */ + private void drawCircleOnTrap(Graphics2D graphics, ThievingChest chest, Color fill) + { + if (chest.getLocalPoint().getPlane() != client.getPlane()) + { + return; + } + LocalPoint localLoc = LocalPoint.fromWorld(client, chest.getLocalPoint()); + if (localLoc == null) + { + return; + } + Point loc = Perspective.localToCanvas(client, localLoc, chest.getLocalPoint().getPlane()); + + ProgressPieComponent pie = new ProgressPieComponent(); + pie.setFill(fill); + pie.setBorderColor(Color.BLACK); + pie.setPosition(loc); + pie.setProgress(1); + if (graphics != null && loc != null) + { + pie.render(graphics); + } + } + + private int getChestOpacity(WorldPoint chestPos) + { + int chestId = plugin.getChestId(chestPos); + BatSolver solver = plugin.getSolver(); + if (solver != null && chestId != -1) + { + return (int) (255 * solver.relativeLikelihoodPoison(chestId)); + } + return 255; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/InstancePoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/InstancePoint.java new file mode 100644 index 0000000000..0df80aeb65 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/InstancePoint.java @@ -0,0 +1,98 @@ +package net.runelite.client.plugins.raidsthieving; + +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.api.Point; +import net.runelite.api.coords.WorldPoint; + +import java.util.Objects; + +/** + * Represents a point in the instance chunk, invariant of rotation. + */ +@Getter +public class InstancePoint +{ + private static final int CHUNK_SIZE = 8; + private static final double CHUNK_OFFSET = 3.5; + + public InstancePoint(int x, int y, int rot) + { + this.x = x; + this.y = y; + this.rot = rot; + } + + public InstancePoint(int x, int y) + { + this.x = x; + this.y = y; + this.rot = 0; + } + + public static InstancePoint buildFromPoint(WorldPoint worldPoint, Client client) + { + Point point = new Point(worldPoint.getX(), worldPoint.getY()); + Point base = new Point(client.getBaseX(), client.getBaseY()); + int plane = worldPoint.getPlane(); + + int deltaX = point.getX() - base.getX(); + int deltaY = point.getY() - base.getY(); + int chunkIndexX = deltaX / CHUNK_SIZE; + int chunkIndexY = deltaY / CHUNK_SIZE; + + int chunkData = client.getInstanceTemplateChunks()[plane][chunkIndexX][chunkIndexY]; + int rotation = chunkData >> 1 & 0x3; + int y = (chunkData >> 3 & 0x7FF) * 8; + int x = (chunkData >> 14 & 0x3FF) * 8; + + return buildFromTile(base, point, rotation, new Point(x, y)); + } + + public static InstancePoint buildFromTile(Point base, Point tile, int rot, Point chunkOrigin) + { + int deltaX = tile.getX() - base.getX(); + int deltaY = tile.getY() - base.getY(); + + double chunkOffsetX = (deltaX % CHUNK_SIZE) - CHUNK_OFFSET; + double chunkOffsetY = (deltaY % CHUNK_SIZE) - CHUNK_OFFSET; + + for (int i = 0; i < rot; i++) + { + double temp = chunkOffsetX; + chunkOffsetX = -chunkOffsetY; + chunkOffsetY = temp; + } + + chunkOffsetX += CHUNK_OFFSET; + chunkOffsetY += CHUNK_OFFSET; + + int invariantChunkOffsetX = (int) chunkOffsetX; + int invariantChunkOffsetY = (int) chunkOffsetY; + + return new InstancePoint( + chunkOrigin.getX() + invariantChunkOffsetX, + chunkOrigin.getY() + invariantChunkOffsetY, + rot); + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InstancePoint that = (InstancePoint) o; + return x == that.x && + y == that.y; + } + + @Override + public int hashCode() + { + return Objects.hash(x, y); + } + + private int x; + private int y; + private int rot; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/RaidsThievingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/RaidsThievingConfig.java new file mode 100644 index 0000000000..bb055edf4e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/RaidsThievingConfig.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2017, Tim Lehner + * 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.raidsthieving; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import java.awt.Color; + +@ConfigGroup("raidsthievingplugin") +public interface RaidsThievingConfig extends Config +{ + @ConfigItem( + position = 1, + keyName = "hexColorPotentialBat", + name = "Potential Bat", + description = "Color of marker for chests which could have bat" + ) + default Color getPotentialBatColor() + { + return Color.YELLOW; + } + + @ConfigItem( + position = 2, + keyName = "hexColorPoison", + name = "Poison trap", + description = "Color of chest with poison" + ) + default Color getPoisonTrapColor() + { + return Color.GREEN; + } + + @ConfigItem( + position = 5, + keyName = "batNotify", + name = "Notify when found", + description = "Send notification if you see bats being found." + ) + default boolean batFoundNotify() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/RaidsThievingConstants.java b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/RaidsThievingConstants.java new file mode 100644 index 0000000000..965934a01b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/RaidsThievingConstants.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017, Tim Lehner + * 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.raidsthieving; + +public class RaidsThievingConstants +{ + public static final int CLOSED_CHEST_ID = 29742; + public static final int OPEN_EMPTY_CHEST = 29743; + public static final int OPEN_FULL_CHEST_1 = 29744; + public static final int OPEN_FULL_CHEST_2 = 29745; + public static final int EMPTY_TROUGH = 29746; + public static final int[] STORAGE = {29769, 29770, 29771, 29772}; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/RaidsThievingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/RaidsThievingPlugin.java new file mode 100644 index 0000000000..d9f7de646c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/RaidsThievingPlugin.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2017, Tim Lehner + * 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.raidsthieving; + +import com.google.inject.Provides; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.GameObject; +import net.runelite.api.GraphicsObject; +import net.runelite.api.Varbits; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.GameObjectSpawned; +import net.runelite.api.events.GraphicsObjectCreated; +import net.runelite.api.events.VarbitChanged; +import net.runelite.client.Notifier; +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.raidsthieving.BatSolver.BatSolver; +import net.runelite.client.plugins.raidsthieving.BatSolver.ChestIdentifier; +import net.runelite.client.plugins.raidsthieving.BatSolver.ThievingRoomType; +import net.runelite.client.ui.overlay.OverlayManager; +import javax.inject.Inject; +import java.text.MessageFormat; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@PluginDescriptor( + name = "!Raids Bat Finder", + description = "Tracks which chests need to be searched for bats and which poison", + tags = {"overlay", "skilling", "raid"} +) +public class RaidsThievingPlugin extends Plugin +{ + @Inject + private Client client; + + @Inject + private OverlayManager overlayManager; + + @Inject + private ChestOverlay overlay; + + @Inject + private Notifier notifier; + + @Inject + private RaidsThievingConfig config; + + @Getter + private final Map chests = new HashMap<>(); + + @Getter + private Instant lastActionTime = Instant.ofEpochMilli(0); + + private boolean inRaidChambers; + + @Getter + private boolean batsFound; + + @Getter + private BatSolver solver; + + @Getter + private ChestIdentifier mapper; + + + @Provides + RaidsThievingConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(RaidsThievingConfig.class); + } + + @Override + protected void startUp() + { + overlayManager.add(overlay); + overlay.updateConfig(); + reset(); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(overlay); + lastActionTime = Instant.ofEpochMilli(0); + chests.clear(); + } + + + @Subscribe + public void onGameObjectSpawned(GameObjectSpawned event) + { + GameObject obj = event.getGameObject(); + WorldPoint loc = obj.getWorldLocation(); + InstancePoint absLoc = InstancePoint.buildFromPoint(loc, client); + + if (obj.getId() == RaidsThievingConstants.EMPTY_TROUGH) + { + ThievingRoomType type = ThievingRoomType.IdentifyByInstancePoint(absLoc); + + if (type != null) + { + solver = new BatSolver(type); + mapper = new ChestIdentifier(type); + for (ThievingChest chest : chests.values()) + { + mapper.indentifyChest(chest); + } + } + else + { + log.error(MessageFormat.format("Unable to identify room type with: {0} {1} {2} {3} {4}.", + loc.getX(), loc.getY(), absLoc.getX(), absLoc.getY(), absLoc.getRot())); + log.error("Please report this @https://github.com/runelite/runelite/pull/4914!"); + } + } + if (obj.getId() == RaidsThievingConstants.CLOSED_CHEST_ID) + { + if (!chests.containsKey(loc)) + { + ThievingChest chest = new ThievingChest(obj, absLoc); + + if (mapper != null) + { + mapper.indentifyChest(chest); + } + + chests.put(loc, chest); + } + else + { + checkForBats(); + } + } + + if (obj.getId() == RaidsThievingConstants.OPEN_FULL_CHEST_1 || + obj.getId() == RaidsThievingConstants.OPEN_FULL_CHEST_2) + { + ThievingChest chest = chests.get(obj.getWorldLocation()); + // We found a chest that has grubs + log.info(MessageFormat.format("Found grubs at {0}, {1} chestId: {2}", loc.getX(), loc.getY(), chest.getChestId())); + if (solver != null && chest.getChestId() != -1) + { + chest.setEverOpened(true); + solver.addGrubsChest(chest.getChestId()); + } + checkForBats(); + } + + if (obj.getId() == RaidsThievingConstants.OPEN_EMPTY_CHEST) + { + ThievingChest chest = chests.get(obj.getWorldLocation()); + // We found a chest that could have poison + if (solver != null && chest.getChestId() != -1) + { + chest.setEmpty(true); + chest.setEverOpened(true); + solver.addEmptyChest(chest.getChestId()); + } + } + } + + + @Subscribe + public void onGraphicsObjectCreated(GraphicsObjectCreated event) + { + GraphicsObject obj = event.getGraphicsObject(); + if (obj.getId() == 184) + { + log.debug("Found poison splat"); + WorldPoint loc = WorldPoint.fromLocal(client, obj.getLocation()); + chests.get(loc).setPoison(true); + } + } + + @Subscribe + public void onVarbitChanged(VarbitChanged event) + { + boolean setting = client.getVar(Varbits.IN_RAID) == 1; + + if (inRaidChambers != setting) + { + inRaidChambers = setting; + reset(); + } + + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (event.getGroup().equals("raidsthievingplugin")) + { + overlay.updateConfig(); + } + } + + private void reset() + { + chests.clear(); + batsFound = false; + solver = null; + mapper = null; + } + + public int numberOfEmptyChestsFound() + { + int total = 0; + for (ThievingChest chest : chests.values()) + { + if (chest.isEmpty()) + { + total++; + } + } + return total; + } + + + private boolean checkForBats() + { + for (ThievingChest chest : chests.values()) + { + if (chest.isEmpty() && !chest.isPoison()) + { + batsFound = true; + if (config.batFoundNotify()) + { + notifier.notify("Bats have been found!"); + } + return true; + } + } + return false; + } + + public int getChestId(WorldPoint worldPoint) + { + return chests.get(worldPoint).getChestId(); + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/ThievingChest.java b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/ThievingChest.java new file mode 100644 index 0000000000..05a58d554e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raidsthieving/ThievingChest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2019, Tim Lehner + * 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.raidsthieving; + +import lombok.Getter; +import lombok.Setter; +import net.runelite.api.GameObject; +import net.runelite.api.coords.WorldPoint; + +/** + * Wrapper class for a GameObject that represents a chest in the thieving room of Chambers of Xeric. + */ +@Getter +public class ThievingChest +{ + /** + * If the chest has never been opened, it could have bats. + */ + @Setter + private boolean everOpened; + + /** + * If the chest is empty, it could have bats. + */ + @Setter + private boolean empty; + + /** + * If the chest contains a poison trap instead. + */ + @Setter + private boolean poison; + + + @Setter + private int chestId; + + private final WorldPoint localPoint; + private final InstancePoint instancePoint; + + /** + * Constructor for a ThievingChest object + * + * @param gameObject The gameobject thats corresponds with this trap. + */ + ThievingChest(GameObject gameObject, InstancePoint instancePoint) + { + this.everOpened = false; + this.poison = false; + this.empty = false; + localPoint = gameObject.getWorldLocation(); + this.instancePoint = instancePoint; + this.chestId = -1; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/rememberclan/RememberClanPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/rememberclan/RememberClanPlugin.java index be4d85e9fe..33f702e072 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/rememberclan/RememberClanPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/rememberclan/RememberClanPlugin.java @@ -45,6 +45,7 @@ import net.runelite.client.plugins.PluginDescriptor; @PluginDescriptor( name = "!Remember Clan", description = "Remember a specific clan!", + type = "utility", enabledByDefault = false ) public class RememberClanPlugin extends Plugin diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/safespot/SafeSpotConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/safespot/SafeSpotConfig.java new file mode 100644 index 0000000000..ec2e3cc105 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/safespot/SafeSpotConfig.java @@ -0,0 +1,25 @@ +package net.runelite.client.plugins.safespot; + +import java.awt.Color; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.Config; + +@ConfigGroup("safespot") +public interface SafeSpotConfig extends Config +{ + @ConfigItem(position = 1, keyName = "playerSafeSpots", name = "Render for Players", description = "Renders 1 way safe spots vs other players") + default boolean playerSafeSpots() { + return true; + } + + @ConfigItem(position = 2, keyName = "npcSafeSpots", name = "Render for NPCs", description = "Renders 1 way safe spots vs NPCs") + default boolean npcSafeSpots() { + return false; + } + + @ConfigItem(position = 3, keyName = "tileColor", name = "Tile Color", description = "Color of safe spot tile") + default Color tileColor() { + return Color.MAGENTA; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/safespot/SafeSpotOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/safespot/SafeSpotOverlay.java new file mode 100644 index 0000000000..7259957f15 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/safespot/SafeSpotOverlay.java @@ -0,0 +1,40 @@ +package net.runelite.client.plugins.safespot; + +import javax.inject.*; +import net.runelite.client.ui.overlay.*; +import java.awt.*; +import net.runelite.api.*; + +public class SafeSpotOverlay extends Overlay +{ + private final Client client; + private final SafeSpotPlugin safeSpotPlugin; + private final SafeSpotConfig config; + + @Inject + public SafeSpotOverlay( Client client, SafeSpotPlugin safeSpotPlugin, SafeSpotConfig config) { + this.client = client; + this.safeSpotPlugin = safeSpotPlugin; + this.config = config; + this.setPosition(OverlayPosition.DYNAMIC); + this.setPriority(OverlayPriority.LOW); + this.setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D graphics) { + if (safeSpotPlugin.isSafeSpotsRenderable() && safeSpotPlugin.getSafeSpotList() != null && safeSpotPlugin.getSafeSpotList().size() > 0) { + safeSpotPlugin.getSafeSpotList().forEach(tile -> { + Polygon poly; + if (tile != null && tile.getLocalLocation() != null) { + poly = Perspective.getCanvasTilePoly(client, tile.getLocalLocation()); + if (poly != null) { + OverlayUtil.renderPolygon(graphics, poly, config.tileColor()); + } + } + return; + }); + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/safespot/SafeSpotPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/safespot/SafeSpotPlugin.java new file mode 100644 index 0000000000..9955332d11 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/safespot/SafeSpotPlugin.java @@ -0,0 +1,135 @@ +package net.runelite.client.plugins.safespot; + + +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldPoint; +import java.util.List; +import net.runelite.api.Actor; +import net.runelite.api.coords.WorldArea; +import net.runelite.api.NPC; +import net.runelite.api.Player; +import net.runelite.api.events.GameTick; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.api.events.InteractingChanged; +import com.google.inject.Provides; +import net.runelite.client.config.ConfigManager; +import net.runelite.api.Tile; +import java.util.ArrayList; +import net.runelite.client.ui.overlay.OverlayManager; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.Plugin; + +@PluginDescriptor( + name = "1 Way Safe Spots", + description = "Renders tile overlays for one way safe spots", + tags = { "safe spot", "pvp", "safespots", "pklite" }, + type = "PVP", + enabledByDefault = false +) + +// TODO : filter tiles you cant stand on + +public class SafeSpotPlugin extends Plugin +{ + @Inject + private Client client; + @Inject + OverlayManager overlayManager; + @Inject + private SafeSpotConfig config; + private ArrayList safeSpotList; + private boolean safeSpotsRenderable; + private SafeSpotOverlay safeSpotOverlay; + private int tickCount; + + public SafeSpotPlugin() { + this.safeSpotsRenderable = false; + this.tickCount = 0; + } + + @Provides + SafeSpotConfig config(ConfigManager configManager) { + return configManager.getConfig(SafeSpotConfig.class); + } + + @Override + protected void startUp() throws Exception { + this.safeSpotOverlay = new SafeSpotOverlay(this.client, this, this.config); + this.overlayManager.add(safeSpotOverlay); + } + + @Override + protected void shutDown() throws Exception { + this.overlayManager.remove(safeSpotOverlay); + } + + @Subscribe + private void onInteractingChanged(InteractingChanged event) { + if (event.getSource() != client.getLocalPlayer()) { + return; + } + if (event.getTarget() == null && (config.npcSafeSpots() || config.playerSafeSpots())) { + tickCount = 10; + } + } + + @Subscribe + public void onGameTick(GameTick event) { + if (client.getLocalPlayer().getInteracting() != null) { + if (client.getLocalPlayer().getInteracting() instanceof Player && config.playerSafeSpots()) { + safeSpotsRenderable = true; + updateSafeSpots(); + } + if (client.getLocalPlayer().getInteracting() instanceof NPC && config.npcSafeSpots()) { + safeSpotsRenderable = true; + updateSafeSpots(); + } + } + else { + safeSpotsRenderable = false; + } + if (tickCount > 0) { + --tickCount; + safeSpotsRenderable = true; + } + } + + private void updateSafeSpots() + { + if (client.getLocalPlayer().getInteracting() != null) + { + Actor enemy = client.getLocalPlayer().getInteracting(); + + WorldArea worldArea = new WorldArea(enemy.getWorldLocation().getX() - 12, enemy.getWorldLocation().getY() - 12, 24, 24, client.getPlane()); + List worldPoints = worldArea.toWorldPointList(); + safeSpotList = getSafeSpotList(enemy, worldPoints); + } + } + + private ArrayList getSafeSpotList(Actor actor, List worldPoints) + { + ArrayList safeSpotList = new ArrayList(); + Tile[][][] tiles = client.getScene().getTiles(); + for (WorldPoint w : worldPoints) + { + LocalPoint toPoint = LocalPoint.fromWorld(client, w); + Tile fromTile = tiles[client.getPlane()][actor.getLocalLocation().getSceneX()][actor.getLocalLocation().getSceneY()]; + Tile toTile = tiles[client.getPlane()][toPoint.getSceneX()][toPoint.getSceneY()]; + if ((toTile.hasLineOfSightTo(fromTile)) && (!fromTile.hasLineOfSightTo(toTile))) + { + safeSpotList.add(toTile); + } + } + return safeSpotList; + } + + public ArrayList getSafeSpotList() { + return safeSpotList; + } + + public boolean isSafeSpotsRenderable() { + return safeSpotsRenderable; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/stronghold/StrongholdPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/stronghold/StrongholdPlugin.java index d1de9d419b..37e3d0507b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/stronghold/StrongholdPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/stronghold/StrongholdPlugin.java @@ -43,6 +43,7 @@ import java.awt.Color; name = "!Stronghold", description = "Highlights the correct answer to Stronghold of Security questions", tags = {"stronghold", "security", "overlay", "answer", "highlight"} + type = "utility", ) @Slf4j public class StrongholdPlugin extends Plugin { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/warindicators/WarIndicatorPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/warindicators/WarIndicatorPlugin.java index af7cf94ab7..a05322590d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/warindicators/WarIndicatorPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/warindicators/WarIndicatorPlugin.java @@ -57,6 +57,7 @@ import net.runelite.client.ui.overlay.OverlayManager; name = "!War", description = "War War War.", tags = {"skill", "total", "max", "PVP"}, + type = "PVP", enabledByDefault = false ) public class WarIndicatorPlugin extends Plugin diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsPlugin.java index 5f3397e900..df3a773aaa 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zoneIndicators/ZoneIndicatorsPlugin.java @@ -55,6 +55,7 @@ import net.runelite.client.ui.overlay.OverlayManager; name = "!MultiLines", description = "Show borders of multicombat and PvP safezones", tags = {"multicombat", "lines", "pvp", "deadman", "safezones", "bogla"}, + type = "utility", enabledByDefault = false ) public class ZoneIndicatorsPlugin extends Plugin