additional plugins

This commit is contained in:
Kyleeld
2019-04-20 18:59:13 +01:00
parent 22ca3f43e4
commit 447aaf6a25
31 changed files with 3029 additions and 0 deletions

View File

@@ -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

View File

@@ -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
)

View File

@@ -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<InventorySetupItem> inventory;
@Getter
private ArrayList<InventorySetupItem> equipment;
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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<String, InventorySetup> 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<InventorySetupItem> inv = getNormalizedContainer(InventoryID.INVENTORY);
ArrayList<InventorySetupItem> 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<HashMap<String, InventorySetup>>()
{
}.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<InventorySetupItem> normContainer = getNormalizedContainer(InventoryID.INVENTORY);
final InventorySetup setup = inventorySetups.get(selectedInventorySetup);
panel.highlightDifferences(normContainer, setup, InventoryID.INVENTORY);
}
else if (container == client.getItemContainer(InventoryID.EQUIPMENT))
{
ArrayList<InventorySetupItem> 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<InventorySetupItem> getNormalizedContainer(final InventoryID id)
{
assert id == InventoryID.INVENTORY || id == InventoryID.EQUIPMENT : "invalid inventory ID";
final ItemContainer container = client.getItemContainer(id);
ArrayList<InventorySetupItem> 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<InventorySetupItem> items = new ArrayList<>();
items.addAll(setup.getEquipment());
items.addAll(setup.getInventory());
ArrayList<Integer> 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();
}
}

View File

@@ -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<InventorySetupItem> 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);
}

View File

@@ -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<EquipmentInventorySlot, InventorySetupSlot> 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<InventorySetupItem> 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<InventorySetupItem> currEquipment, final InventorySetup inventorySetup)
{
final ArrayList<InventorySetupItem> 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);
}
}
}

View File

@@ -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<InventorySetupSlot> 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<InventorySetupItem> 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<InventorySetupItem> currInventory, final InventorySetup inventorySetup)
{
final ArrayList<InventorySetupItem> 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);
}
}
}

View File

@@ -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<String> 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<InventorySetupItem> normInv = plugin.getNormalizedContainer(InventoryID.INVENTORY);
final ArrayList<InventorySetupItem> 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<InventorySetupItem> 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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,193 @@
/*
* Copyright (c) 2018, Tim Lehner <Timothy.Lehner.2011@live.rhul.ac.uk>
* 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<Integer, Integer> numberOfSolutionsWithPoison;
private final SolutionSet solution;
private final HashSet<Integer> 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<Integer> matchSolutions()
{
TreeSet<Integer> 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<SolutionSet> getPosssibleSolutions()
{
List<SolutionSet> 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<Integer, Integer> 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();
}
}

View File

@@ -0,0 +1,261 @@
/*
* Copyright (c) 2018, Tim Lehner <Timothy.Lehner.2011@live.rhul.ac.uk>
* 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<InstancePoint, Integer> chestIds;
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright (c) 2018, Tim Lehner <Timothy.Lehner.2011@live.rhul.ac.uk>
* 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<Integer> emptyChests;
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2018, Tim Lehner <Timothy.Lehner.2011@live.rhul.ac.uk>
* 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;
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright (c) 2018, Tim Lehner <Timothy.Lehner.2011@live.rhul.ac.uk>
* 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<WorldPoint, ThievingChest> 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<Integer> 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;
}
}

View File

@@ -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;
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2017, Tim Lehner <Timothy.Lehner.2011@live.rhul.ac.uk>
* 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;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2017, Tim Lehner <Timothy.Lehner.2011@live.rhul.ac.uk>
* 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};
}

View File

@@ -0,0 +1,270 @@
/*
* Copyright (c) 2017, Tim Lehner <Timothy.Lehner.2011@live.rhul.ac.uk>
* 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 = "<font color=\"#4863A0\">!Raids Bat Finder</font>",
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<WorldPoint, ThievingChest> 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();
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2019, Tim Lehner <Timothy.Lehner.2011@live.rhul.ac.uk>
* 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;
}
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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<Tile> 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<WorldPoint> worldPoints = worldArea.toWorldPointList();
safeSpotList = getSafeSpotList(enemy, worldPoints);
}
}
private ArrayList<Tile> getSafeSpotList(Actor actor, List<WorldPoint> worldPoints)
{
ArrayList<Tile> 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<Tile> getSafeSpotList() {
return safeSpotList;
}
public boolean isSafeSpotsRenderable() {
return safeSpotsRenderable;
}
}

View File

@@ -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 {

View File

@@ -57,6 +57,7 @@ import net.runelite.client.ui.overlay.OverlayManager;
name = "<font color=\"aqua\">!War</font>",
description = "War War War.",
tags = {"skill", "total", "max", "PVP"},
type = "PVP",
enabledByDefault = false
)
public class WarIndicatorPlugin extends Plugin

View File

@@ -55,6 +55,7 @@ import net.runelite.client.ui.overlay.OverlayManager;
name = "<font color=\"aqua\">!MultiLines</font>",
description = "Show borders of multicombat and PvP safezones",
tags = {"multicombat", "lines", "pvp", "deadman", "safezones", "bogla"},
type = "utility",
enabledByDefault = false
)
public class ZoneIndicatorsPlugin extends Plugin