From 4148b5e4a849b03616364b766dbfda181a015f1e Mon Sep 17 00:00:00 2001 From: Max Weber Date: Fri, 22 Nov 2019 09:14:12 -0700 Subject: [PATCH 01/64] config: Refactor config panel into separate panels for each logical view --- .../client/plugins/config/ConfigPanel.java | 388 ++++-------------- .../client/plugins/config/ConfigPlugin.java | 39 +- .../plugins/config/FixedWidthPanel.java | 39 ++ .../client/plugins/config/HotkeyButton.java | 2 +- .../config/PluginConfigurationDescriptor.java | 69 ++++ .../client/plugins/config/PluginListItem.java | 213 +++------- .../plugins/config/PluginListPanel.java | 356 ++++++++++++++++ .../plugins/config/PluginToggleButton.java | 62 +++ .../client/ui/MultiplexingPluginPanel.java | 134 ++++++ .../net/runelite/client/ui/PluginPanel.java | 6 +- .../net/runelite/client/util/SwingUtil.java | 16 + 11 files changed, 844 insertions(+), 480 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/config/FixedWidthPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/MultiplexingPluginPanel.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java index bd12d73470..4ed399b78a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java @@ -36,12 +36,7 @@ import java.awt.event.ItemEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.concurrent.ScheduledExecutorService; -import java.util.stream.Collectors; +import javax.inject.Inject; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -61,66 +56,53 @@ import javax.swing.SpinnerNumberModel; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeListener; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; import javax.swing.text.JTextComponent; import lombok.extern.slf4j.Slf4j; -import net.runelite.client.config.ChatColorConfig; -import net.runelite.client.config.Config; import net.runelite.client.config.ConfigDescriptor; -import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigItemDescriptor; import net.runelite.client.config.ConfigManager; import net.runelite.client.config.Keybind; import net.runelite.client.config.ModifierlessKeybind; import net.runelite.client.config.Range; -import net.runelite.client.config.RuneLiteConfig; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.PluginInstantiationException; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.PluginChanged; import net.runelite.client.plugins.PluginManager; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.DynamicGridLayout; import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.components.ComboBoxListRenderer; -import net.runelite.client.ui.components.IconButton; -import net.runelite.client.ui.components.IconTextField; import net.runelite.client.ui.components.colorpicker.ColorPickerManager; import net.runelite.client.ui.components.colorpicker.RuneliteColorPicker; import net.runelite.client.util.ColorUtil; import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.SwingUtil; import net.runelite.client.util.Text; @Slf4j -public class ConfigPanel extends PluginPanel +class ConfigPanel extends PluginPanel { private static final int SPINNER_FIELD_WIDTH = 6; - private static final int SCROLLBAR_WIDTH = 17; - private static final int OFFSET = 6; private static final ImageIcon BACK_ICON; private static final ImageIcon BACK_ICON_HOVER; - private static final String RUNELITE_GROUP_NAME = RuneLiteConfig.class.getAnnotation(ConfigGroup.class).value(); - private static final String PINNED_PLUGINS_CONFIG_KEY = "pinnedPlugins"; - private static final String RUNELITE_PLUGIN = "RuneLite"; - private static final String CHAT_COLOR_PLUGIN = "Chat Color"; + private final FixedWidthPanel mainPanel; + private final JLabel title; + private final PluginToggleButton pluginToggle; - private final PluginManager pluginManager; - private final ConfigManager configManager; - private final ScheduledExecutorService executorService; - private final RuneLiteConfig runeLiteConfig; - private final ChatColorConfig chatColorConfig; - private final ColorPickerManager colorPickerManager; - private final List pluginList = new ArrayList<>(); + @Inject + private PluginListPanel pluginList; - private final IconTextField searchBar = new IconTextField(); - private final JPanel topPanel; - private final JPanel mainPanel; - private final JScrollPane scrollPane; + @Inject + private ConfigManager configManager; - private boolean showingPluginList = true; - private int scrollBarPosition = 0; + @Inject + private PluginManager pluginManager; + + @Inject + private ColorPickerManager colorPickerManager; + + private PluginConfigurationDescriptor pluginConfig = null; static { @@ -129,48 +111,16 @@ public class ConfigPanel extends PluginPanel BACK_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(backIcon, -100)); } - ConfigPanel(PluginManager pluginManager, ConfigManager configManager, ScheduledExecutorService executorService, - RuneLiteConfig runeLiteConfig, ChatColorConfig chatColorConfig, ColorPickerManager colorPickerManager) + public ConfigPanel() { super(false); - this.pluginManager = pluginManager; - this.configManager = configManager; - this.executorService = executorService; - this.runeLiteConfig = runeLiteConfig; - this.chatColorConfig = chatColorConfig; - this.colorPickerManager = colorPickerManager; - - searchBar.setIcon(IconTextField.Icon.SEARCH); - searchBar.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH - 20, 30)); - searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR); - searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR); - searchBar.getDocument().addDocumentListener(new DocumentListener() - { - @Override - public void insertUpdate(DocumentEvent e) - { - onSearchBarChanged(); - } - - @Override - public void removeUpdate(DocumentEvent e) - { - onSearchBarChanged(); - } - - @Override - public void changedUpdate(DocumentEvent e) - { - onSearchBarChanged(); - } - }); setLayout(new BorderLayout()); setBackground(ColorScheme.DARK_GRAY_COLOR); - topPanel = new JPanel(); + JPanel topPanel = new JPanel(); topPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); - topPanel.setLayout(new BorderLayout(0, OFFSET)); + topPanel.setLayout(new BorderLayout(0, BORDER_OFFSET)); add(topPanel, BorderLayout.NORTH); mainPanel = new FixedWidthPanel(); @@ -182,139 +132,66 @@ public class ConfigPanel extends PluginPanel northPanel.setLayout(new BorderLayout()); northPanel.add(mainPanel, BorderLayout.NORTH); - scrollPane = new JScrollPane(northPanel); + JScrollPane scrollPane = new JScrollPane(northPanel); scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); add(scrollPane, BorderLayout.CENTER); - initializePluginList(); - refreshPluginList(); - } - - private void initializePluginList() - { - final List pinnedPlugins = getPinnedPluginNames(); - - // populate pluginList with all non-hidden plugins - pluginManager.getPlugins().stream() - .filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden()) - .forEach(plugin -> - { - final PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class); - final Config config = pluginManager.getPluginConfigProxy(plugin); - final ConfigDescriptor configDescriptor = config == null ? null : configManager.getConfigDescriptor(config); - - final PluginListItem listItem = new PluginListItem(this, plugin, descriptor, config, configDescriptor); - listItem.setPinned(pinnedPlugins.contains(listItem.getName())); - pluginList.add(listItem); - }); - - // add special entries for core client configurations - final PluginListItem runeLite = new PluginListItem(this, runeLiteConfig, - configManager.getConfigDescriptor(runeLiteConfig), - RUNELITE_PLUGIN, "RuneLite client settings", "client"); - runeLite.setPinned(pinnedPlugins.contains(RUNELITE_PLUGIN)); - pluginList.add(runeLite); - - final PluginListItem chatColor = new PluginListItem(this, chatColorConfig, - configManager.getConfigDescriptor(chatColorConfig), - CHAT_COLOR_PLUGIN, "Recolor chat text", "colour", "messages"); - chatColor.setPinned(pinnedPlugins.contains(CHAT_COLOR_PLUGIN)); - pluginList.add(chatColor); - - pluginList.sort(Comparator.comparing(PluginListItem::getName)); - } - - void refreshPluginList() - { - // update enabled / disabled status of all items - pluginList.forEach(listItem -> - { - final Plugin plugin = listItem.getPlugin(); - if (plugin != null) - { - listItem.setPluginEnabled(pluginManager.isPluginEnabled(plugin)); - } - }); - - if (showingPluginList) - { - openConfigList(); - } - } - - void openConfigList() - { - if (showingPluginList) - { - scrollBarPosition = scrollPane.getVerticalScrollBar().getValue(); - } - - showingPluginList = true; - - topPanel.removeAll(); - mainPanel.removeAll(); - topPanel.add(searchBar, BorderLayout.CENTER); - - onSearchBarChanged(); - searchBar.requestFocusInWindow(); - validate(); - scrollPane.getVerticalScrollBar().setValue(scrollBarPosition); - } - - private void onSearchBarChanged() - { - final String text = searchBar.getText(); - - pluginList.forEach(mainPanel::remove); - - showMatchingPlugins(true, text); - showMatchingPlugins(false, text); - - revalidate(); - } - - private void showMatchingPlugins(boolean pinned, String text) - { - if (text.isEmpty()) - { - pluginList.stream().filter(item -> pinned == item.isPinned()).forEach(mainPanel::add); - return; - } - - final String[] searchTerms = text.toLowerCase().split(" "); - pluginList.forEach(listItem -> - { - if (pinned == listItem.isPinned() && listItem.matchesSearchTerms(searchTerms)) - { - mainPanel.add(listItem); - } - }); - } - - void openGroupConfigPanel(PluginListItem listItem, Config config, ConfigDescriptor cd) - { - showingPluginList = false; - - scrollBarPosition = scrollPane.getVerticalScrollBar().getValue(); - topPanel.removeAll(); - mainPanel.removeAll(); - - final IconButton topPanelBackButton = new IconButton(BACK_ICON, BACK_ICON_HOVER); + JButton topPanelBackButton = new JButton(BACK_ICON); + topPanelBackButton.setRolloverIcon(BACK_ICON_HOVER); + SwingUtil.removeButtonDecorations(topPanelBackButton); topPanelBackButton.setPreferredSize(new Dimension(22, 0)); topPanelBackButton.setBorder(new EmptyBorder(0, 0, 0, 5)); - topPanelBackButton.addActionListener(e -> openConfigList()); + topPanelBackButton.addActionListener(e -> pluginList.getMuxer().popState()); topPanelBackButton.setToolTipText("Back"); topPanel.add(topPanelBackButton, BorderLayout.WEST); - topPanel.add(listItem.getConfigToggleButton(), BorderLayout.EAST); - - String name = listItem.getName(); - JLabel title = new JLabel(name); + pluginToggle = new PluginToggleButton(); + topPanel.add(pluginToggle, BorderLayout.EAST); + title = new JLabel(); title.setForeground(Color.WHITE); - title.setToolTipText("" + name + ":
" + listItem.getDescription() + ""); - PluginListItem.addLabelPopupMenu(title, PluginListItem.wikiLinkMenuItem(listItem.getName())); - topPanel.add(title); + topPanel.add(title); + } + + void init(PluginConfigurationDescriptor pluginConfig) + { + assert this.pluginConfig == null; + this.pluginConfig = pluginConfig; + + String name = pluginConfig.getName(); + title.setText(name); + title.setForeground(Color.WHITE); + title.setToolTipText("" + name + ":
" + pluginConfig.getDescription() + ""); + PluginListItem.addLabelPopupMenu(title, pluginConfig.createSupportMenuItem()); + + if (pluginConfig.getPlugin() != null) + { + pluginToggle.setSelected(pluginManager.isPluginEnabled(pluginConfig.getPlugin())); + pluginToggle.addItemListener(i -> + { + if (pluginToggle.isSelected()) + { + pluginList.startPlugin(pluginConfig.getPlugin()); + } + else + { + pluginList.stopPlugin(pluginConfig.getPlugin()); + } + }); + } + else + { + pluginToggle.setVisible(false); + } + + rebuild(); + } + + private void rebuild() + { + mainPanel.removeAll(); + + ConfigDescriptor cd = pluginConfig.getConfigDescriptor(); for (ConfigItemDescriptor cid : cd.getItems()) { if (cid.getItem().hidden()) @@ -325,7 +202,7 @@ public class ConfigPanel extends PluginPanel JPanel item = new JPanel(); item.setLayout(new BorderLayout()); item.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); - name = cid.getItem().name(); + String name = cid.getItem().name(); JLabel configEntryName = new JLabel(name); configEntryName.setForeground(Color.WHITE); configEntryName.setToolTipText("" + name + ":
" + cid.getItem().description() + ""); @@ -336,7 +213,7 @@ public class ConfigPanel extends PluginPanel JCheckBox checkbox = new JCheckBox(); checkbox.setBackground(ColorScheme.LIGHT_GRAY_COLOR); checkbox.setSelected(Boolean.parseBoolean(configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName()))); - checkbox.addActionListener(ae -> changeConfiguration(listItem, config, checkbox, cd, cid)); + checkbox.addActionListener(ae -> changeConfiguration(checkbox, cd, cid)); item.add(checkbox, BorderLayout.EAST); } @@ -361,7 +238,7 @@ public class ConfigPanel extends PluginPanel Component editor = spinner.getEditor(); JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField(); spinnerTextField.setColumns(SPINNER_FIELD_WIDTH); - spinner.addChangeListener(ce -> changeConfiguration(listItem, config, spinner, cd, cid)); + spinner.addChangeListener(ce -> changeConfiguration(spinner, cd, cid)); item.add(spinner, BorderLayout.EAST); } @@ -390,7 +267,7 @@ public class ConfigPanel extends PluginPanel @Override public void focusLost(FocusEvent e) { - changeConfiguration(listItem, config, textField, cd, cid); + changeConfiguration(textField, cd, cid); } }); @@ -433,7 +310,7 @@ public class ConfigPanel extends PluginPanel colorPickerBtn.setBackground(c); colorPickerBtn.setText(ColorUtil.toHexColor(c).toUpperCase()); }); - colorPicker.setOnClose(c -> changeConfiguration(listItem, config, colorPicker, cd, cid)); + colorPicker.setOnClose(c -> changeConfiguration(colorPicker, cd, cid)); colorPicker.setVisible(true); } }); @@ -499,7 +376,7 @@ public class ConfigPanel extends PluginPanel { if (e.getStateChange() == ItemEvent.SELECTED) { - changeConfiguration(listItem, config, box, cd, cid); + changeConfiguration(box, cd, cid); box.setToolTipText(Text.titleCase((Enum) box.getSelectedItem())); } }); @@ -519,7 +396,7 @@ public class ConfigPanel extends PluginPanel @Override public void focusLost(FocusEvent e) { - changeConfiguration(listItem, config, button, cd, cid); + changeConfiguration(button, cd, cid); } }); @@ -538,23 +415,21 @@ public class ConfigPanel extends PluginPanel if (result == JOptionPane.YES_OPTION) { - configManager.setDefaultConfiguration(config, true); + configManager.setDefaultConfiguration(pluginConfig.getConfig(), true); - // Reload configuration panel - openGroupConfigPanel(listItem, config, cd); + rebuild(); } }); mainPanel.add(resetButton); JButton backButton = new JButton("Back"); - backButton.addActionListener(e -> openConfigList()); + backButton.addActionListener(e -> pluginList.getMuxer().popState()); mainPanel.add(backButton); revalidate(); - scrollPane.getVerticalScrollBar().setValue(0); } - private void changeConfiguration(PluginListItem listItem, Config config, Component component, ConfigDescriptor cd, ConfigItemDescriptor cid) + private void changeConfiguration(Component component, ConfigDescriptor cd, ConfigItemDescriptor cid) { final ConfigItem configItem = cid.getItem(); @@ -566,7 +441,7 @@ public class ConfigPanel extends PluginPanel if (result != JOptionPane.YES_OPTION) { - openGroupConfigPanel(listItem, config, cd); + rebuild(); return; } } @@ -603,102 +478,21 @@ public class ConfigPanel extends PluginPanel } } - void startPlugin(Plugin plugin, PluginListItem listItem) - { - executorService.submit(() -> - { - pluginManager.setPluginEnabled(plugin, true); - - try - { - pluginManager.startPlugin(plugin); - } - catch (PluginInstantiationException ex) - { - log.warn("Error when starting plugin {}", plugin.getClass().getSimpleName(), ex); - } - - listItem.setPluginEnabled(true); - }); - } - - void stopPlugin(Plugin plugin, PluginListItem listItem) - { - executorService.submit(() -> - { - pluginManager.setPluginEnabled(plugin, false); - - try - { - pluginManager.stopPlugin(plugin); - } - catch (PluginInstantiationException ex) - { - log.warn("Error when stopping plugin {}", plugin.getClass().getSimpleName(), ex); - } - - listItem.setPluginEnabled(false); - }); - } - - private List getPinnedPluginNames() - { - final String config = configManager.getConfiguration(RUNELITE_GROUP_NAME, PINNED_PLUGINS_CONFIG_KEY); - - if (config == null) - { - return Collections.emptyList(); - } - - return Text.fromCSV(config); - } - - void savePinnedPlugins() - { - final String value = pluginList.stream() - .filter(PluginListItem::isPinned) - .map(PluginListItem::getName) - .collect(Collectors.joining(",")); - - configManager.setConfiguration(RUNELITE_GROUP_NAME, PINNED_PLUGINS_CONFIG_KEY, value); - } - - void openConfigurationPanel(String configGroup) - { - for (PluginListItem pluginListItem : pluginList) - { - if (pluginListItem.getName().equals(configGroup)) - { - openGroupConfigPanel(pluginListItem, pluginListItem.getConfig(), pluginListItem.getConfigDescriptor()); - break; - } - } - } - - @Override - public void onActivate() - { - super.onActivate(); - - if (searchBar.getParent() != null) - { - searchBar.requestFocusInWindow(); - } - } - @Override public Dimension getPreferredSize() { return new Dimension(PANEL_WIDTH + SCROLLBAR_WIDTH, super.getPreferredSize().height); } - private class FixedWidthPanel extends JPanel + @Subscribe + public void onPluginChanged(PluginChanged event) { - @Override - public Dimension getPreferredSize() + if (event.getPlugin() == this.pluginConfig.getPlugin()) { - return new Dimension(PANEL_WIDTH, super.getPreferredSize().height); + SwingUtilities.invokeLater(() -> + { + pluginToggle.setSelected(event.isLoaded()); + }); } - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java index f17542447b..3024dfc786 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPlugin.java @@ -25,8 +25,8 @@ package net.runelite.client.plugins.config; import java.awt.image.BufferedImage; -import java.util.concurrent.ScheduledExecutorService; import javax.inject.Inject; +import javax.inject.Provider; import javax.swing.SwingUtilities; import net.runelite.api.MenuAction; import net.runelite.client.config.ChatColorConfig; @@ -34,13 +34,10 @@ import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.OverlayMenuClicked; -import net.runelite.client.events.PluginChanged; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.PluginManager; import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.NavigationButton; -import net.runelite.client.ui.components.colorpicker.ColorPickerManager; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayMenuEntry; import net.runelite.client.util.ImageUtil; @@ -55,31 +52,35 @@ public class ConfigPlugin extends Plugin @Inject private ClientToolbar clientToolbar; + @Inject + private Provider pluginListPanelProvider; + @Inject private ConfigManager configManager; - @Inject - private PluginManager pluginManager; - - @Inject - private ScheduledExecutorService executorService; - @Inject private RuneLiteConfig runeLiteConfig; @Inject private ChatColorConfig chatColorConfig; - @Inject - private ColorPickerManager colorPickerManager; + private PluginListPanel pluginListPanel; - private ConfigPanel configPanel; private NavigationButton navButton; @Override protected void startUp() throws Exception { - configPanel = new ConfigPanel(pluginManager, configManager, executorService, runeLiteConfig, chatColorConfig, colorPickerManager); + pluginListPanel = pluginListPanelProvider.get(); + pluginListPanel.addFakePlugin(new PluginConfigurationDescriptor( + "RuneLite", "RuneLite client settings", new String[]{"client"}, + null, runeLiteConfig, configManager.getConfigDescriptor(runeLiteConfig) + ), + new PluginConfigurationDescriptor( + "Chat Color", "Recolor chat text", new String[]{"colour", "messages"}, + null, chatColorConfig, configManager.getConfigDescriptor(chatColorConfig) + )); + pluginListPanel.rebuildPluginList(); final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "config_icon.png"); @@ -87,7 +88,7 @@ public class ConfigPlugin extends Plugin .tooltip("Configuration") .icon(icon) .priority(0) - .panel(configPanel) + .panel(pluginListPanel.getMuxer()) .build(); clientToolbar.addNavigation(navButton); @@ -99,12 +100,6 @@ public class ConfigPlugin extends Plugin clientToolbar.removeNavigation(navButton); } - @Subscribe - public void onPluginChanged(PluginChanged event) - { - SwingUtilities.invokeLater(configPanel::refreshPluginList); - } - @Subscribe public void onOverlayMenuClicked(OverlayMenuClicked overlayMenuClicked) { @@ -126,7 +121,7 @@ public class ConfigPlugin extends Plugin { navButton.getOnSelect().run(); } - configPanel.openConfigurationPanel(descriptor.name()); + pluginListPanel.openConfigurationPanel(descriptor.name()); }); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/FixedWidthPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/FixedWidthPanel.java new file mode 100644 index 0000000000..0a6373a201 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/FixedWidthPanel.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017, Adam + * 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.config; + +import java.awt.Dimension; +import javax.swing.JPanel; +import net.runelite.client.ui.PluginPanel; + +class FixedWidthPanel extends JPanel +{ + @Override + public Dimension getPreferredSize() + { + return new Dimension(PluginPanel.PANEL_WIDTH, super.getPreferredSize().height); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/HotkeyButton.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/HotkeyButton.java index 585ade5ea6..17b23a5785 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/HotkeyButton.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/HotkeyButton.java @@ -31,7 +31,7 @@ import lombok.Getter; import net.runelite.client.config.Keybind; import net.runelite.client.config.ModifierlessKeybind; -public class HotkeyButton extends JButton +class HotkeyButton extends JButton { @Getter private Keybind value; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java new file mode 100644 index 0000000000..69932e1d61 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019 Abex + * 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.config; + +import javax.annotation.Nullable; +import javax.swing.JMenuItem; +import lombok.Value; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigDescriptor; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.util.LinkBrowser; + +@Value +class PluginConfigurationDescriptor +{ + private final String name; + private final String description; + private final String[] tags; + + // Can be null if its not an actual plugin (RuneLite / ChatColors) + @Nullable + private final Plugin plugin; + + // Can be null if it has no more configuration than the on/off toggle + @Nullable + private final Config config; + + @Nullable + private final ConfigDescriptor configDescriptor; + + boolean hasConfigurables() + { + return configDescriptor != null && !configDescriptor.getItems().stream().allMatch(item -> item.getItem().hidden()); + } + + /** + * Creates a menu item for linking to a support page for the plugin + * + * @return A {@link JMenuItem} which opens the plugin's wiki page URL in the browser when clicked + */ + JMenuItem createSupportMenuItem() + { + final JMenuItem menuItem = new JMenuItem("Wiki"); + menuItem.addActionListener(e -> LinkBrowser.browse("https://github.com/runelite/runelite/wiki/" + name.replace(' ', '-'))); + return menuItem; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java index 69dec09365..340e0982ba 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java @@ -38,91 +38,49 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import javax.annotation.Nullable; import javax.swing.ImageIcon; +import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import javax.swing.JToggleButton; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; -import lombok.AccessLevel; import lombok.Getter; -import net.runelite.client.config.Config; -import net.runelite.client.config.ConfigDescriptor; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; -import net.runelite.client.ui.components.IconButton; import net.runelite.client.util.ImageUtil; -import net.runelite.client.util.LinkBrowser; +import net.runelite.client.util.SwingUtil; import org.apache.commons.text.similarity.JaroWinklerDistance; class PluginListItem extends JPanel { private static final JaroWinklerDistance DISTANCE = new JaroWinklerDistance(); - private static final String RUNELITE_WIKI_FORMAT = "https://github.com/runelite/runelite/wiki/%s"; private static final ImageIcon CONFIG_ICON; private static final ImageIcon CONFIG_ICON_HOVER; - private static final ImageIcon ON_SWITCHER; - private static final ImageIcon OFF_SWITCHER; private static final ImageIcon ON_STAR; private static final ImageIcon OFF_STAR; - private final ConfigPanel configPanel; + private final PluginListPanel pluginListPanel; @Getter - @Nullable - private final Plugin plugin; - - @Nullable - @Getter(AccessLevel.PACKAGE) - private final Config config; - - @Nullable - @Getter(AccessLevel.PACKAGE) - private final ConfigDescriptor configDescriptor; - - @Getter - private final String name; - - @Getter - private final String description; - - @Getter - private final IconButton configToggleButton; + private final PluginConfigurationDescriptor pluginConfig; private final List keywords = new ArrayList<>(); - private final IconButton pinButton = new IconButton(OFF_STAR); - private final IconButton configButton = new IconButton(CONFIG_ICON, CONFIG_ICON_HOVER); - private final IconButton toggleButton; - - private boolean isPluginEnabled = false; - - @Getter - private boolean isPinned = false; + private final JToggleButton pinButton; + private final JToggleButton onOffToggle; static { BufferedImage configIcon = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "config_edit_icon.png"); - BufferedImage onSwitcher = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "switcher_on.png"); BufferedImage onStar = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "star_on.png"); CONFIG_ICON = new ImageIcon(configIcon); - ON_SWITCHER = new ImageIcon(onSwitcher); ON_STAR = new ImageIcon(onStar); CONFIG_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(configIcon, -100)); - BufferedImage offSwitcherImage = ImageUtil.flipImage( - ImageUtil.grayscaleOffset( - ImageUtil.grayscaleImage(onSwitcher), - 0.61f - ), - true, - false - ); - OFF_SWITCHER = new ImageIcon(offSwitcherImage); + BufferedImage offStar = ImageUtil.grayscaleOffset( ImageUtil.grayscaleImage(onStar), 0.77f @@ -130,76 +88,54 @@ class PluginListItem extends JPanel OFF_STAR = new ImageIcon(offStar); } - /** - * Creates a new {@code PluginListItem} for a plugin. - *

- * Note that {@code config} and {@code configDescriptor} can be {@code null} - * if there is no configuration associated with the plugin. - */ - PluginListItem(ConfigPanel configPanel, Plugin plugin, PluginDescriptor descriptor, - @Nullable Config config, @Nullable ConfigDescriptor configDescriptor) + PluginListItem(PluginListPanel pluginListPanel, PluginConfigurationDescriptor pluginConfig) { - this(configPanel, plugin, config, configDescriptor, - descriptor.name(), descriptor.description(), descriptor.tags()); - } + this.pluginListPanel = pluginListPanel; + this.pluginConfig = pluginConfig; - /** - * Creates a new {@code PluginListItem} for a core configuration. - */ - PluginListItem(ConfigPanel configPanel, Config config, ConfigDescriptor configDescriptor, - String name, String description, String... tags) - { - this(configPanel, null, config, configDescriptor, name, description, tags); - } - - private PluginListItem(ConfigPanel configPanel, @Nullable Plugin plugin, @Nullable Config config, - @Nullable ConfigDescriptor configDescriptor, String name, String description, String... tags) - { - this.configPanel = configPanel; - this.plugin = plugin; - this.config = config; - this.configDescriptor = configDescriptor; - this.name = name; - this.description = description; - Collections.addAll(keywords, name.toLowerCase().split(" ")); - Collections.addAll(keywords, description.toLowerCase().split(" ")); - Collections.addAll(keywords, tags); + Collections.addAll(keywords, pluginConfig.getName().toLowerCase().split(" ")); + Collections.addAll(keywords, pluginConfig.getDescription().toLowerCase().split(" ")); + Collections.addAll(keywords, pluginConfig.getTags()); final List popupMenuItems = new ArrayList<>(); setLayout(new BorderLayout(3, 0)); setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH, 20)); - JLabel nameLabel = new JLabel(name); + JLabel nameLabel = new JLabel(pluginConfig.getName()); nameLabel.setForeground(Color.WHITE); - if (!description.isEmpty()) + if (!pluginConfig.getDescription().isEmpty()) { - nameLabel.setToolTipText("" + name + ":
" + description + ""); + nameLabel.setToolTipText("" + pluginConfig.getName() + ":
" + pluginConfig.getDescription() + ""); } - + pinButton = new JToggleButton(OFF_STAR); + pinButton.setSelectedIcon(ON_STAR); + SwingUtil.removeButtonDecorations(pinButton); + SwingUtil.addModalTooltip(pinButton, "Unpin plugin", "Pin plugin"); pinButton.setPreferredSize(new Dimension(21, 0)); add(pinButton, BorderLayout.LINE_START); pinButton.addActionListener(e -> { - setPinned(!isPinned); - configPanel.savePinnedPlugins(); - configPanel.openConfigList(); + pluginListPanel.savePinnedPlugins(); + pluginListPanel.refresh(); }); final JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new GridLayout(1, 2)); add(buttonPanel, BorderLayout.LINE_END); - configButton.setPreferredSize(new Dimension(25, 0)); - configButton.setVisible(false); - buttonPanel.add(configButton); - - // add a listener to configButton only if there are config items to show - if (config != null && !configDescriptor.getItems().stream().allMatch(item -> item.getItem().hidden())) + if (pluginConfig.hasConfigurables()) { + JButton configButton = new JButton(CONFIG_ICON); + configButton.setRolloverIcon(CONFIG_ICON_HOVER); + SwingUtil.removeButtonDecorations(configButton); + configButton.setPreferredSize(new Dimension(25, 0)); + configButton.setVisible(false); + buttonPanel.add(configButton); + configButton.addActionListener(e -> { configButton.setIcon(CONFIG_ICON); @@ -214,72 +150,50 @@ class PluginListItem extends JPanel popupMenuItems.add(configMenuItem); } - popupMenuItems.add(wikiLinkMenuItem(name)); + popupMenuItems.add(pluginConfig.createSupportMenuItem()); addLabelPopupMenu(nameLabel, popupMenuItems); add(nameLabel, BorderLayout.CENTER); - toggleButton = createToggleButton(); - buttonPanel.add(toggleButton); - - configToggleButton = createToggleButton(); - } - - private void attachToggleButtonListener(IconButton button) - { - // no need for a listener if there is no plugin to enable / disable - if (plugin == null) + onOffToggle = new PluginToggleButton(); + buttonPanel.add(onOffToggle); + if (pluginConfig.getPlugin() != null) { - button.setVisible(false); - return; + onOffToggle.addItemListener(i -> + { + if (onOffToggle.isSelected()) + { + pluginListPanel.startPlugin(pluginConfig.getPlugin()); + } + else + { + pluginListPanel.stopPlugin(pluginConfig.getPlugin()); + } + }); } - - button.addActionListener(e -> + else { - if (isPluginEnabled) - { - configPanel.stopPlugin(plugin, PluginListItem.this); - } - else - { - configPanel.startPlugin(plugin, PluginListItem.this); - } - - setPluginEnabled(!isPluginEnabled); - updateToggleButton(button); - }); + onOffToggle.setVisible(false); + } } - private IconButton createToggleButton() + boolean isPinned() { - final IconButton button = new IconButton(OFF_SWITCHER); - button.setPreferredSize(new Dimension(25, 0)); - updateToggleButton(button); - attachToggleButtonListener(button); - return button; - } - - void setPluginEnabled(boolean enabled) - { - isPluginEnabled = enabled; - updateToggleButton(toggleButton); - updateToggleButton(configToggleButton); + return pinButton.isSelected(); } void setPinned(boolean pinned) { - isPinned = pinned; - pinButton.setIcon(pinned ? ON_STAR : OFF_STAR); - pinButton.setToolTipText(pinned ? "Unpin plugin" : "Pin plugin"); + pinButton.setSelected(pinned); } - private void updateToggleButton(IconButton button) + void setPluginEnabled(boolean enabled) { - button.setIcon(isPluginEnabled ? ON_SWITCHER : OFF_SWITCHER); - button.setToolTipText(isPluginEnabled ? "Disable plugin" : "Enable plugin"); + onOffToggle.setSelected(enabled); } /** * Checks if all the search terms in the given list matches at least one keyword. + * * @return true if all search terms matches at least one keyword, or false if otherwise. */ boolean matchesSearchTerms(String[] searchTerms) @@ -297,7 +211,7 @@ class PluginListItem extends JPanel private void openGroupConfigPanel() { - configPanel.openGroupConfigPanel(PluginListItem.this, config, configDescriptor); + pluginListPanel.openConfigurationPanel(pluginConfig); } /** @@ -360,19 +274,4 @@ class PluginListItem extends JPanel } }); } - - /** - * Creates a menu item for linking to a wiki page which, when clicked, opens a link to the plugin's wiki page for - * the passed plugin name. - * - * @param pluginName The name of the plugin which should be linked to - * @return A {@link JMenuItem} which opens the plugin's wiki page URL in the browser when clicked - */ - static JMenuItem wikiLinkMenuItem(final String pluginName) - { - final JMenuItem menuItem = new JMenuItem("Wiki"); - final String sanitizedName = pluginName.replace(' ', '-'); - menuItem.addActionListener(e -> LinkBrowser.browse(String.format(RUNELITE_WIKI_FORMAT, sanitizedName))); - return menuItem; - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java new file mode 100644 index 0000000000..abf9ab3bdb --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2017, Adam + * 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.config; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.ScheduledExecutorService; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigDescriptor; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.RuneLiteConfig; +import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.PluginChanged; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.PluginInstantiationException; +import net.runelite.client.plugins.PluginManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.DynamicGridLayout; +import net.runelite.client.ui.MultiplexingPluginPanel; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.IconTextField; +import net.runelite.client.util.Text; + +@Slf4j +@Singleton +class PluginListPanel extends PluginPanel +{ + private static final String RUNELITE_GROUP_NAME = RuneLiteConfig.class.getAnnotation(ConfigGroup.class).value(); + private static final String PINNED_PLUGINS_CONFIG_KEY = "pinnedPlugins"; + + private final ConfigManager configManager; + private final PluginManager pluginManager; + private final ScheduledExecutorService executorService; + private final Provider configPanelProvider; + private final List fakePlugins = new ArrayList<>(); + + @Getter + private final MultiplexingPluginPanel muxer; + private final IconTextField searchBar; + private final JScrollPane scrollPane; + private final FixedWidthPanel mainPanel; + private List pluginList; + + @Inject + public PluginListPanel( + ConfigManager configManager, + PluginManager pluginManager, + ScheduledExecutorService executorService, + EventBus eventBus, + Provider configPanelProvider) + { + super(false); + + this.configManager = configManager; + this.pluginManager = pluginManager; + this.executorService = executorService; + this.configPanelProvider = configPanelProvider; + + muxer = new MultiplexingPluginPanel(this) + { + @Override + protected void onAdd(PluginPanel p) + { + eventBus.register(p); + } + + @Override + protected void onRemove(PluginPanel p) + { + eventBus.unregister(p); + } + }; + + searchBar = new IconTextField(); + searchBar.setIcon(IconTextField.Icon.SEARCH); + searchBar.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH - 20, 30)); + searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR); + searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR); + searchBar.getDocument().addDocumentListener(new DocumentListener() + { + @Override + public void insertUpdate(DocumentEvent e) + { + onSearchBarChanged(); + } + + @Override + public void removeUpdate(DocumentEvent e) + { + onSearchBarChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) + { + onSearchBarChanged(); + } + }); + + setLayout(new BorderLayout()); + setBackground(ColorScheme.DARK_GRAY_COLOR); + + JPanel topPanel = new JPanel(); + topPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + topPanel.setLayout(new BorderLayout(0, BORDER_OFFSET)); + topPanel.add(searchBar, BorderLayout.CENTER); + add(topPanel, BorderLayout.NORTH); + + mainPanel = new FixedWidthPanel(); + mainPanel.setBorder(new EmptyBorder(8, 10, 10, 10)); + mainPanel.setLayout(new DynamicGridLayout(0, 1, 0, 5)); + mainPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + + JPanel northPanel = new FixedWidthPanel(); + northPanel.setLayout(new BorderLayout()); + northPanel.add(mainPanel, BorderLayout.NORTH); + + scrollPane = new JScrollPane(northPanel); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + add(scrollPane, BorderLayout.CENTER); + } + + void rebuildPluginList() + { + final List pinnedPlugins = getPinnedPluginNames(); + + // populate pluginList with all non-hidden plugins + pluginList = Stream.concat( + fakePlugins.stream(), + pluginManager.getPlugins().stream() + .filter(plugin -> !plugin.getClass().getAnnotation(PluginDescriptor.class).hidden()) + .map(plugin -> + { + PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class); + Config config = pluginManager.getPluginConfigProxy(plugin); + ConfigDescriptor configDescriptor = config == null ? null : configManager.getConfigDescriptor(config); + + return new PluginConfigurationDescriptor( + descriptor.name(), + descriptor.description(), + descriptor.tags(), + plugin, + config, + configDescriptor); + }) + ).map(desc -> + { + PluginListItem listItem = new PluginListItem(this, desc); + listItem.setPinned(pinnedPlugins.contains(desc.getName())); + return listItem; + }).collect(Collectors.toList()); + + pluginList.sort(Comparator.comparing(p -> p.getPluginConfig().getName())); + mainPanel.removeAll(); + refresh(); + } + + void addFakePlugin(PluginConfigurationDescriptor... descriptor) + { + Collections.addAll(fakePlugins, descriptor); + } + + void refresh() + { + // update enabled / disabled status of all items + pluginList.forEach(listItem -> + { + final Plugin plugin = listItem.getPluginConfig().getPlugin(); + if (plugin != null) + { + listItem.setPluginEnabled(pluginManager.isPluginEnabled(plugin)); + } + }); + + int scrollBarPosition = scrollPane.getVerticalScrollBar().getValue(); + + onSearchBarChanged(); + searchBar.requestFocusInWindow(); + validate(); + + scrollPane.getVerticalScrollBar().setValue(scrollBarPosition); + } + + private void onSearchBarChanged() + { + final String text = searchBar.getText(); + + pluginList.forEach(mainPanel::remove); + + showMatchingPlugins(true, text); + showMatchingPlugins(false, text); + + revalidate(); + } + + private void showMatchingPlugins(boolean pinned, String text) + { + if (text.isEmpty()) + { + pluginList.stream().filter(item -> pinned == item.isPinned()).forEach(mainPanel::add); + return; + } + + final String[] searchTerms = text.toLowerCase().split(" "); + pluginList.forEach(listItem -> + { + if (pinned == listItem.isPinned() && listItem.matchesSearchTerms(searchTerms)) + { + mainPanel.add(listItem); + } + }); + } + + void openConfigurationPanel(String configGroup) + { + for (PluginListItem pluginListItem : pluginList) + { + if (pluginListItem.getPluginConfig().getName().equals(configGroup)) + { + openConfigurationPanel(pluginListItem.getPluginConfig()); + break; + } + } + } + + void openConfigurationPanel(PluginConfigurationDescriptor plugin) + { + ConfigPanel panel = configPanelProvider.get(); + panel.init(plugin); + muxer.pushState(panel); + } + + void startPlugin(Plugin plugin) + { + executorService.submit(() -> + { + pluginManager.setPluginEnabled(plugin, true); + + try + { + pluginManager.startPlugin(plugin); + } + catch (PluginInstantiationException ex) + { + log.warn("Error when starting plugin {}", plugin.getClass().getSimpleName(), ex); + } + }); + } + + void stopPlugin(Plugin plugin) + { + executorService.submit(() -> + { + pluginManager.setPluginEnabled(plugin, false); + + try + { + pluginManager.stopPlugin(plugin); + } + catch (PluginInstantiationException ex) + { + log.warn("Error when stopping plugin {}", plugin.getClass().getSimpleName(), ex); + } + }); + } + + private List getPinnedPluginNames() + { + final String config = configManager.getConfiguration(RUNELITE_GROUP_NAME, PINNED_PLUGINS_CONFIG_KEY); + + if (config == null) + { + return Collections.emptyList(); + } + + return Text.fromCSV(config); + } + + void savePinnedPlugins() + { + final String value = pluginList.stream() + .filter(PluginListItem::isPinned) + .map(p -> p.getPluginConfig().getName()) + .collect(Collectors.joining(",")); + + configManager.setConfiguration(RUNELITE_GROUP_NAME, PINNED_PLUGINS_CONFIG_KEY, value); + } + + @Subscribe + public void onPluginChanged(PluginChanged event) + { + SwingUtilities.invokeLater(this::refresh); + } + + @Override + public Dimension getPreferredSize() + { + return new Dimension(PANEL_WIDTH + SCROLLBAR_WIDTH, super.getPreferredSize().height); + } + + @Override + public void onActivate() + { + super.onActivate(); + + if (searchBar.getParent() != null) + { + searchBar.requestFocusInWindow(); + } + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java new file mode 100644 index 0000000000..0fb22dfbe0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 Abex + * 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.config; + +import java.awt.Dimension; +import java.awt.image.BufferedImage; +import javax.swing.ImageIcon; +import javax.swing.JToggleButton; +import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.SwingUtil; + +class PluginToggleButton extends JToggleButton +{ + private static final ImageIcon ON_SWITCHER; + private static final ImageIcon OFF_SWITCHER; + + static + { + BufferedImage onSwitcher = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "switcher_on.png"); + ON_SWITCHER = new ImageIcon(onSwitcher); + OFF_SWITCHER = new ImageIcon(ImageUtil.flipImage( + ImageUtil.grayscaleOffset( + ImageUtil.grayscaleImage(onSwitcher), + 0.61f + ), + true, + false + )); + } + + public PluginToggleButton() + { + super(OFF_SWITCHER); + setSelectedIcon(ON_SWITCHER); + SwingUtil.removeButtonDecorations(this); + setPreferredSize(new Dimension(25, 0)); + SwingUtil.addModalTooltip(this, "Disable plugin", "Enable plugin"); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/MultiplexingPluginPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/MultiplexingPluginPanel.java new file mode 100644 index 0000000000..6454058a4c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/MultiplexingPluginPanel.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2019 Abex + * 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.ui; + +import java.awt.CardLayout; + +public class MultiplexingPluginPanel extends PluginPanel +{ + private final CardLayout layout; + private boolean active = false; + private PluginPanel current; + + public MultiplexingPluginPanel(PluginPanel root) + { + super(false); + + layout = new CardLayout(); + setLayout(layout); + pushState(root); + } + + public void destroy() + { + for (int i = getComponentCount() - 1; i > 0; i--) + { + onRemove((PluginPanel) getComponent(i)); + remove(i); + } + } + + public void pushState(PluginPanel subpanel) + { + int index = -1; + for (int i = getComponentCount() - 1; i >= 0; i--) + { + if (getComponent(i) == subpanel) + { + index = i; + break; + } + } + + if (active) + { + current.onDeactivate(); + subpanel.onActivate(); + } + current = subpanel; + + String name = System.identityHashCode(subpanel) + ""; + + if (index != -1) + { + for (int i = getComponentCount() - 1; i > index; i--) + { + popState(); + } + } + else + { + add(subpanel, name); + onAdd(subpanel); + } + + layout.show(this, name); + revalidate(); + } + + public void popState() + { + int count = getComponentCount(); + if (count <= 1) + { + assert false : "Cannot pop last component"; + return; + } + + PluginPanel subpanel = (PluginPanel) getComponent(count - 2); + if (active) + { + current.onDeactivate(); + subpanel.onActivate(); + current = subpanel; + } + layout.show(this, System.identityHashCode(subpanel) + ""); + onRemove((PluginPanel) getComponent(count - 1)); + remove(count - 1); + revalidate(); + } + + protected void onAdd(PluginPanel p) + { + } + + protected void onRemove(PluginPanel p) + { + } + + @Override + public void onActivate() + { + active = true; + current.onActivate(); + } + + @Override + public void onDeactivate() + { + active = false; + current.onDeactivate(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java index 73e9a92f8b..f5678f9f8d 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java @@ -36,9 +36,9 @@ import lombok.Getter; public abstract class PluginPanel extends JPanel { public static final int PANEL_WIDTH = 225; - private static final int SCROLLBAR_WIDTH = 17; - private static final int OFFSET = 6; - private static final EmptyBorder BORDER_PADDING = new EmptyBorder(OFFSET, OFFSET, OFFSET, OFFSET); + public static final int SCROLLBAR_WIDTH = 17; + public static final int BORDER_OFFSET = 6; + private static final EmptyBorder BORDER_PADDING = new EmptyBorder(BORDER_OFFSET, BORDER_OFFSET, BORDER_OFFSET, BORDER_OFFSET); private static final Dimension OUTER_PREFERRED_SIZE = new Dimension(PluginPanel.PANEL_WIDTH + SCROLLBAR_WIDTH, 0); @Getter(AccessLevel.PROTECTED) diff --git a/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java index 39fc738f1f..f4e12c9020 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java @@ -29,6 +29,7 @@ import java.awt.Color; import java.awt.Font; import java.awt.Frame; import java.awt.Image; +import java.awt.Insets; import java.awt.SystemTray; import java.awt.TrayIcon; import java.awt.event.MouseAdapter; @@ -41,6 +42,7 @@ import java.util.concurrent.Callable; import java.util.function.BiConsumer; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.swing.AbstractButton; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; @@ -277,4 +279,18 @@ public class SwingUtil navigationButton.setOnSelect(button::doClick); return button; } + + public static void removeButtonDecorations(AbstractButton button) + { + button.setBorderPainted(false); + button.setContentAreaFilled(false); + button.setFocusPainted(false); + button.setMargin(new Insets(0, 0, 0, 0)); + button.setOpaque(false); + } + + public static void addModalTooltip(AbstractButton button, String on, String off) + { + button.addItemListener(l -> button.setToolTipText(button.isSelected() ? on : off)); + } } From 808e85fdf828e23744dd4d246483d2f691c90971 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Tue, 26 Nov 2019 21:10:20 -0700 Subject: [PATCH 02/64] runelite-client: Remove IconButton Most of this class is defaults, which can just be a normal method and a listener for hover support, which is part of the base class anyway. --- .../kourendlibrary/KourendLibraryPanel.java | 24 ++----- .../timetracking/clocks/ClockPanel.java | 37 ++++------ .../timetracking/clocks/ClockTabPanel.java | 7 +- .../timetracking/clocks/StopwatchPanel.java | 11 ++- .../timetracking/clocks/TimerPanel.java | 7 +- .../client/ui/components/IconButton.java | 71 ------------------- 6 files changed, 38 insertions(+), 119 deletions(-) delete mode 100644 runelite-client/src/main/java/net/runelite/client/ui/components/IconButton.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java index f65f0c7a63..8480328c47 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java @@ -29,8 +29,6 @@ import com.google.inject.Inject; import java.awt.BorderLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.util.Comparator; import java.util.HashMap; @@ -52,7 +50,7 @@ import net.runelite.client.util.ImageUtil; class KourendLibraryPanel extends PluginPanel { private static final ImageIcon RESET_ICON; - private static final ImageIcon RESET_CLICK_ICON; + private static final ImageIcon RESET_HOVER_ICON; private final KourendLibraryConfig config; private final Library library; @@ -63,7 +61,7 @@ class KourendLibraryPanel extends PluginPanel { final BufferedImage resetIcon = ImageUtil.getResourceStreamFromClass(KourendLibraryPanel.class, "/util/reset.png"); RESET_ICON = new ImageIcon(resetIcon); - RESET_CLICK_ICON = new ImageIcon(ImageUtil.alphaOffset(resetIcon, -100)); + RESET_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(resetIcon, -100)); } @Inject @@ -100,21 +98,11 @@ class KourendLibraryPanel extends PluginPanel }); JButton reset = new JButton("Reset", RESET_ICON); - reset.addMouseListener(new MouseAdapter() + reset.setRolloverIcon(RESET_HOVER_ICON); + reset.addActionListener(ev -> { - @Override - public void mousePressed(MouseEvent mouseEvent) - { - reset.setIcon(RESET_CLICK_ICON); - library.reset(); - update(); - } - - @Override - public void mouseReleased(MouseEvent mouseEvent) - { - reset.setIcon(RESET_ICON); - } + library.reset(); + update(); }); add(reset, BorderLayout.NORTH); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockPanel.java index 98db274ee2..612a5a0918 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockPanel.java @@ -30,12 +30,12 @@ import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.time.Duration; import java.time.format.DateTimeParseException; import javax.swing.BorderFactory; +import javax.swing.JButton; import javax.swing.JPanel; +import javax.swing.JToggleButton; import javax.swing.SwingConstants; import javax.swing.border.Border; import javax.swing.border.CompoundBorder; @@ -43,7 +43,7 @@ import javax.swing.border.EmptyBorder; import lombok.Getter; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.components.FlatTextField; -import net.runelite.client.ui.components.IconButton; +import net.runelite.client.util.SwingUtil; abstract class ClockPanel extends JPanel { @@ -63,7 +63,7 @@ abstract class ClockPanel extends JPanel final JPanel rightActions; private final FlatTextField nameInput; - private final IconButton startPauseButton; + private final JToggleButton startPauseButton; private final FlatTextField displayInput; @Getter @@ -167,28 +167,17 @@ abstract class ClockPanel extends JPanel leftActions = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 0)); leftActions.setBackground(ColorScheme.DARKER_GRAY_COLOR); - startPauseButton = new IconButton(ClockTabPanel.START_ICON); + startPauseButton = new JToggleButton(ClockTabPanel.START_ICON); + startPauseButton.setRolloverIcon(ClockTabPanel.START_ICON_HOVER); + startPauseButton.setSelectedIcon(ClockTabPanel.PAUSE_ICON); + startPauseButton.setRolloverSelectedIcon(ClockTabPanel.PAUSE_ICON_HOVER); + SwingUtil.removeButtonDecorations(startPauseButton); startPauseButton.setPreferredSize(new Dimension(16, 14)); updateActivityStatus(); - startPauseButton.addMouseListener(new MouseAdapter() - { - @Override - public void mouseEntered(MouseEvent e) - { - startPauseButton.setIcon(clock.isActive() ? ClockTabPanel.PAUSE_ICON_HOVER : ClockTabPanel.START_ICON_HOVER); - } - - @Override - public void mouseExited(MouseEvent e) - { - startPauseButton.setIcon(clock.isActive() ? ClockTabPanel.PAUSE_ICON : ClockTabPanel.START_ICON); - } - }); - startPauseButton.addActionListener(e -> { - if (clock.isActive()) + if (!startPauseButton.isSelected()) { clock.pause(); } @@ -201,7 +190,9 @@ abstract class ClockPanel extends JPanel clockManager.saveToConfig(); }); - IconButton resetButton = new IconButton(ClockTabPanel.RESET_ICON, ClockTabPanel.RESET_ICON_HOVER); + JButton resetButton = new JButton(ClockTabPanel.RESET_ICON); + resetButton.setRolloverIcon(ClockTabPanel.RESET_ICON_HOVER); + SwingUtil.removeButtonDecorations(resetButton); resetButton.setPreferredSize(new Dimension(16, 14)); resetButton.setToolTipText("Reset " + clockType); @@ -249,7 +240,7 @@ abstract class ClockPanel extends JPanel displayInput.setEditable(editable && !isActive); displayInput.getTextField().setForeground(isActive ? ACTIVE_CLOCK_COLOR : INACTIVE_CLOCK_COLOR); startPauseButton.setToolTipText(isActive ? "Pause " + clockType : "Start " + clockType); - startPauseButton.setIcon(isActive ? ClockTabPanel.PAUSE_ICON : ClockTabPanel.START_ICON); + startPauseButton.setSelected(isActive); if (editable && clock.getDisplayTime() == 0 && !isActive) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockTabPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockTabPanel.java index 79578a3b79..7eb2dfb9ca 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockTabPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockTabPanel.java @@ -32,6 +32,7 @@ import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; import javax.swing.ImageIcon; +import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; @@ -40,9 +41,9 @@ import net.runelite.client.plugins.timetracking.TimeTrackingPlugin; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.DynamicGridLayout; import net.runelite.client.ui.FontManager; -import net.runelite.client.ui.components.IconButton; import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.SwingUtil; public class ClockTabPanel extends TabContentPanel { @@ -150,7 +151,9 @@ public class ClockTabPanel extends TabContentPanel headerLabel.setFont(FontManager.getRunescapeSmallFont()); panel.add(headerLabel, BorderLayout.CENTER); - IconButton addButton = new IconButton(ADD_ICON, ADD_ICON_HOVER); + JButton addButton = new JButton(ADD_ICON); + addButton.setRolloverIcon(ADD_ICON_HOVER); + SwingUtil.removeButtonDecorations(addButton); addButton.setPreferredSize(new Dimension(14, 14)); addButton.setToolTipText("Add a " + type); addButton.addActionListener(actionListener); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/StopwatchPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/StopwatchPanel.java index 99bb8def81..0603907d54 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/StopwatchPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/StopwatchPanel.java @@ -30,13 +30,14 @@ import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.util.List; +import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingConstants; import javax.swing.border.EmptyBorder; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; -import net.runelite.client.ui.components.IconButton; +import net.runelite.client.util.SwingUtil; class StopwatchPanel extends ClockPanel { @@ -57,7 +58,9 @@ class StopwatchPanel extends ClockPanel contentContainer.add(lapsContainer); - IconButton lapButton = new IconButton(ClockTabPanel.LAP_ICON, ClockTabPanel.LAP_ICON_HOVER); + JButton lapButton = new JButton(ClockTabPanel.LAP_ICON); + lapButton.setRolloverIcon(ClockTabPanel.LAP_ICON_HOVER); + SwingUtil.removeButtonDecorations(lapButton); lapButton.setPreferredSize(new Dimension(16, 14)); lapButton.setToolTipText("Add lap time"); @@ -70,7 +73,9 @@ class StopwatchPanel extends ClockPanel leftActions.add(lapButton); - IconButton deleteButton = new IconButton(ClockTabPanel.DELETE_ICON, ClockTabPanel.DELETE_ICON_HOVER); + JButton deleteButton = new JButton(ClockTabPanel.DELETE_ICON); + deleteButton.setRolloverIcon(ClockTabPanel.DELETE_ICON_HOVER); + SwingUtil.removeButtonDecorations(deleteButton); deleteButton.setPreferredSize(new Dimension(16, 14)); deleteButton.setToolTipText("Delete stopwatch"); deleteButton.addActionListener(e -> clockManager.removeStopwatch(stopwatch)); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/TimerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/TimerPanel.java index 9f2bf047f8..c1fc578dff 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/TimerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/TimerPanel.java @@ -25,7 +25,8 @@ package net.runelite.client.plugins.timetracking.clocks; import java.awt.Dimension; -import net.runelite.client.ui.components.IconButton; +import javax.swing.JButton; +import net.runelite.client.util.SwingUtil; class TimerPanel extends ClockPanel { @@ -33,7 +34,9 @@ class TimerPanel extends ClockPanel { super(clockManager, timer, "timer", true); - IconButton deleteButton = new IconButton(ClockTabPanel.DELETE_ICON, ClockTabPanel.DELETE_ICON_HOVER); + JButton deleteButton = new JButton(ClockTabPanel.DELETE_ICON); + SwingUtil.removeButtonDecorations(deleteButton); + deleteButton.setRolloverIcon(ClockTabPanel.DELETE_ICON_HOVER); deleteButton.setPreferredSize(new Dimension(16, 14)); deleteButton.setToolTipText("Delete timer"); deleteButton.addActionListener(e -> clockManager.removeTimer(timer)); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/IconButton.java b/runelite-client/src/main/java/net/runelite/client/ui/components/IconButton.java deleted file mode 100644 index 1d54bf601c..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/IconButton.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2018, Daniel Teo - * 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.ui.components; - -import java.awt.Insets; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import javax.swing.ImageIcon; -import javax.swing.JButton; - -/** - * A button that consists of an icon, without any background, borders, or margins. - */ -public class IconButton extends JButton -{ - public IconButton(ImageIcon icon) - { - this(icon, null); - } - - public IconButton(ImageIcon icon, ImageIcon hoverIcon) - { - setIcon(icon); - setBorderPainted(false); - setContentAreaFilled(false); - setFocusPainted(false); - setMargin(new Insets(0, 0, 0, 0)); - setOpaque(false); - setRolloverEnabled(false); - - if (hoverIcon != null) - { - addMouseListener(new MouseAdapter() - { - @Override - public void mouseEntered(MouseEvent e) - { - setIcon(hoverIcon); - } - - @Override - public void mouseExited(MouseEvent e) - { - setIcon(icon); - } - }); - } - } -} From dc2f4b6f590e65183cc3d11d6880bd0e6ce5bf17 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Tue, 26 Nov 2019 22:27:45 -0700 Subject: [PATCH 03/64] ImageUtil: Rename methods to luminance, and work with non ARGB images --- .../client/plugins/config/PluginListItem.java | 4 +- .../plugins/config/PluginToggleButton.java | 2 +- .../screenmarkers/ui/ScreenMarkerPanel.java | 6 +-- .../timetracking/clocks/ClockTabPanel.java | 10 ++--- .../plugins/worldhopper/WorldTableHeader.java | 2 +- .../net/runelite/client/util/ImageUtil.java | 41 +++++++++++++------ .../runelite/client/util/ImageUtilTest.java | 34 +++++++-------- 7 files changed, 58 insertions(+), 41 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java index 340e0982ba..d0a61f0b7d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java @@ -79,9 +79,9 @@ class PluginListItem extends JPanel BufferedImage onStar = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "star_on.png"); CONFIG_ICON = new ImageIcon(configIcon); ON_STAR = new ImageIcon(onStar); - CONFIG_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(configIcon, -100)); + CONFIG_ICON_HOVER = new ImageIcon(ImageUtil.luminanceOffset(configIcon, -100)); - BufferedImage offStar = ImageUtil.grayscaleOffset( + BufferedImage offStar = ImageUtil.luminanceScale( ImageUtil.grayscaleImage(onStar), 0.77f ); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java index 0fb22dfbe0..a2eb72719f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java @@ -42,7 +42,7 @@ class PluginToggleButton extends JToggleButton BufferedImage onSwitcher = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "switcher_on.png"); ON_SWITCHER = new ImageIcon(onSwitcher); OFF_SWITCHER = new ImageIcon(ImageUtil.flipImage( - ImageUtil.grayscaleOffset( + ImageUtil.luminanceScale( ImageUtil.grayscaleImage(onSwitcher), 0.61f ), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java index e9d827202b..782697f396 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ui/ScreenMarkerPanel.java @@ -108,7 +108,7 @@ class ScreenMarkerPanel extends JPanel static { final BufferedImage borderImg = ImageUtil.getResourceStreamFromClass(ScreenMarkerPlugin.class, "border_color_icon.png"); - final BufferedImage borderImgHover = ImageUtil.grayscaleOffset(borderImg, -150); + final BufferedImage borderImgHover = ImageUtil.luminanceOffset(borderImg, -150); BORDER_COLOR_ICON = new ImageIcon(borderImg); BORDER_COLOR_HOVER_ICON = new ImageIcon(borderImgHover); @@ -116,7 +116,7 @@ class ScreenMarkerPanel extends JPanel NO_BORDER_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(borderImgHover, -100)); final BufferedImage fillImg = ImageUtil.getResourceStreamFromClass(ScreenMarkerPlugin.class, "fill_color_icon.png"); - final BufferedImage fillImgHover = ImageUtil.grayscaleOffset(fillImg, -150); + final BufferedImage fillImgHover = ImageUtil.luminanceOffset(fillImg, -150); FILL_COLOR_ICON = new ImageIcon(fillImg); FILL_COLOR_HOVER_ICON = new ImageIcon(fillImgHover); @@ -124,7 +124,7 @@ class ScreenMarkerPanel extends JPanel NO_FILL_COLOR_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(fillImgHover, -100)); final BufferedImage opacityImg = ImageUtil.getResourceStreamFromClass(ScreenMarkerPlugin.class, "opacity_icon.png"); - final BufferedImage opacityImgHover = ImageUtil.grayscaleOffset(opacityImg, -150); + final BufferedImage opacityImgHover = ImageUtil.luminanceOffset(opacityImg, -150); FULL_OPACITY_ICON = new ImageIcon(opacityImg); FULL_OPACITY_HOVER_ICON = new ImageIcon(opacityImgHover); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockTabPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockTabPanel.java index 7eb2dfb9ca..cd1b1a5a2e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockTabPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockTabPanel.java @@ -75,15 +75,15 @@ public class ClockTabPanel extends TabContentPanel BufferedImage addIcon = ImageUtil.getResourceStreamFromClass(TimeTrackingPlugin.class, "add_icon.png"); DELETE_ICON = new ImageIcon(deleteIcon); - DELETE_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(deleteIcon, -80)); + DELETE_ICON_HOVER = new ImageIcon(ImageUtil.luminanceOffset(deleteIcon, -80)); LAP_ICON = new ImageIcon(lapIcon); - LAP_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(lapIcon, -80)); + LAP_ICON_HOVER = new ImageIcon(ImageUtil.luminanceOffset(lapIcon, -80)); PAUSE_ICON = new ImageIcon(pauseIcon); - PAUSE_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(pauseIcon, -80)); + PAUSE_ICON_HOVER = new ImageIcon(ImageUtil.luminanceOffset(pauseIcon, -80)); RESET_ICON = new ImageIcon(resetIcon); - RESET_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(resetIcon, -80)); + RESET_ICON_HOVER = new ImageIcon(ImageUtil.luminanceOffset(resetIcon, -80)); START_ICON = new ImageIcon(startIcon); - START_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(startIcon, -80)); + START_ICON_HOVER = new ImageIcon(ImageUtil.luminanceOffset(startIcon, -80)); ADD_ICON = new ImageIcon(addIcon); ADD_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(addIcon, 0.53f)); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableHeader.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableHeader.java index 2d7ef01406..6e3c094ef0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableHeader.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableHeader.java @@ -56,7 +56,7 @@ class WorldTableHeader extends JPanel { final BufferedImage arrowDown = ImageUtil.getResourceStreamFromClass(WorldHopperPlugin.class, "arrow_down.png"); final BufferedImage arrowUp = ImageUtil.rotateImage(arrowDown, Math.PI); - final BufferedImage arrowUpFaded = ImageUtil.grayscaleOffset(arrowUp, -80); + final BufferedImage arrowUpFaded = ImageUtil.luminanceOffset(arrowUp, -80); ARROW_UP = new ImageIcon(arrowUpFaded); final BufferedImage highlightArrowDown = ImageUtil.fillImage(arrowDown, HIGHLIGHT_COLOR); diff --git a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java index 47df1a4349..afe7054d43 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java @@ -70,23 +70,37 @@ public class ImageUtil return (BufferedImage) image; } - final BufferedImage out = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB); - final Graphics2D g2d = out.createGraphics(); + return toARGB(image); + } + + /** + * Creates an ARGB {@link BufferedImage} from an {@link Image}. + */ + public static BufferedImage toARGB(final Image image) + { + if (image instanceof BufferedImage && ((BufferedImage) image).getType() == BufferedImage.TYPE_INT_ARGB) + { + return (BufferedImage) image; + } + + BufferedImage out = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = out.createGraphics(); g2d.drawImage(image, 0, 0, null); g2d.dispose(); return out; } /** - * Offsets an image in the grayscale (darkens/brightens) by a given offset. + * Offsets an image's luminance by a given value. * - * @param image The image to be darkened or brightened. + * @param rawImg The image to be darkened or brightened. * @param offset A signed 8-bit integer value to brighten or darken the image with. * Values above 0 will brighten, and values below 0 will darken. * @return The given image with its brightness adjusted by the given offset. */ - public static BufferedImage grayscaleOffset(final BufferedImage image, final int offset) + public static BufferedImage luminanceOffset(final Image rawImg, final int offset) { + BufferedImage image = toARGB(rawImg); final float offsetFloat = (float) offset; final int numComponents = image.getColorModel().getNumComponents(); final float[] scales = new float[numComponents]; @@ -104,15 +118,16 @@ public class ImageUtil } /** - * Offsets an image in the grayscale (darkens/brightens) by a given percentage. + * Changes an images luminance by a scaling factor * - * @param image The image to be darkened or brightened. + * @param rawImg The image to be darkened or brightened. * @param percentage The ratio to darken or brighten the given image. * Values above 1 will brighten, and values below 1 will darken. * @return The given image with its brightness scaled by the given percentage. */ - public static BufferedImage grayscaleOffset(final BufferedImage image, final float percentage) + public static BufferedImage luminanceScale(final Image rawImg, final float percentage) { + BufferedImage image = toARGB(rawImg); final int numComponents = image.getColorModel().getNumComponents(); final float[] scales = new float[numComponents]; final float[] offsets = new float[numComponents]; @@ -131,14 +146,15 @@ public class ImageUtil /** * Offsets an image's alpha component by a given offset. * - * @param image The image to be made more or less transparent. + * @param rawImg The image to be made more or less transparent. * @param offset A signed 8-bit integer value to modify the image's alpha component with. * Values above 0 will increase transparency, and values below 0 will decrease * transparency. * @return The given image with its alpha component adjusted by the given offset. */ - public static BufferedImage alphaOffset(final BufferedImage image, final int offset) + public static BufferedImage alphaOffset(final Image rawImg, final int offset) { + BufferedImage image = toARGB(rawImg); final float offsetFloat = (float) offset; final int numComponents = image.getColorModel().getNumComponents(); final float[] scales = new float[numComponents]; @@ -153,14 +169,15 @@ public class ImageUtil /** * Offsets an image's alpha component by a given percentage. * - * @param image The image to be made more or less transparent. + * @param rawImg The image to be made more or less transparent. * @param percentage The ratio to modify the image's alpha component with. * Values above 1 will increase transparency, and values below 1 will decrease * transparency. * @return The given image with its alpha component scaled by the given percentage. */ - public static BufferedImage alphaOffset(final BufferedImage image, final float percentage) + public static BufferedImage alphaOffset(final Image rawImg, final float percentage) { + BufferedImage image = toARGB(rawImg); final int numComponents = image.getColorModel().getNumComponents(); final float[] scales = new float[numComponents]; final float[] offsets = new float[numComponents]; diff --git a/runelite-client/src/test/java/net/runelite/client/util/ImageUtilTest.java b/runelite-client/src/test/java/net/runelite/client/util/ImageUtilTest.java index 829e90368b..44dc88d778 100644 --- a/runelite-client/src/test/java/net/runelite/client/util/ImageUtilTest.java +++ b/runelite-client/src/test/java/net/runelite/client/util/ImageUtilTest.java @@ -81,25 +81,25 @@ public class ImageUtilTest public void grayscaleOffset() { // grayscaleOffset(BufferedImage image, int offset) - assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(BLACK), -255))); - assertTrue(bufferedImagesEqual(oneByOne(new Color(50, 50, 50)), ImageUtil.grayscaleOffset(oneByOne(BLACK), 50))); - assertTrue(bufferedImagesEqual(oneByOne(GRAY), ImageUtil.grayscaleOffset(oneByOne(BLACK), 128))); - assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(GRAY), -255))); - assertTrue(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.grayscaleOffset(oneByOne(BLACK), 255))); - assertTrue(bufferedImagesEqual(oneByOne(new Color(200, 200, 200)), ImageUtil.grayscaleOffset(oneByOne(WHITE), -55))); - assertTrue(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.grayscaleOffset(oneByOne(WHITE), 55))); + assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.luminanceOffset(oneByOne(BLACK), -255))); + assertTrue(bufferedImagesEqual(oneByOne(new Color(50, 50, 50)), ImageUtil.luminanceOffset(oneByOne(BLACK), 50))); + assertTrue(bufferedImagesEqual(oneByOne(GRAY), ImageUtil.luminanceOffset(oneByOne(BLACK), 128))); + assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.luminanceOffset(oneByOne(GRAY), -255))); + assertTrue(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.luminanceOffset(oneByOne(BLACK), 255))); + assertTrue(bufferedImagesEqual(oneByOne(new Color(200, 200, 200)), ImageUtil.luminanceOffset(oneByOne(WHITE), -55))); + assertTrue(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.luminanceOffset(oneByOne(WHITE), 55))); // grayscaleOffset(BufferedImage image, float percentage) - assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(BLACK), 0f))); - assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(BLACK), 1f))); - assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(BLACK), 2f))); - assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(GRAY), 0f))); - assertTrue(bufferedImagesEqual(oneByOne(GRAY), ImageUtil.grayscaleOffset(oneByOne(GRAY), 1f))); - assertTrue(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.grayscaleOffset(oneByOne(GRAY), 2f))); - assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.grayscaleOffset(oneByOne(WHITE), 0f))); - assertTrue(bufferedImagesEqual(oneByOne(GRAY), ImageUtil.grayscaleOffset(oneByOne(WHITE), 0.503f))); // grayscaleOffset does Math.floor - assertTrue(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.grayscaleOffset(oneByOne(WHITE), 1f))); - assertTrue(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.grayscaleOffset(oneByOne(WHITE), 2f))); + assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.luminanceScale(oneByOne(BLACK), 0f))); + assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.luminanceScale(oneByOne(BLACK), 1f))); + assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.luminanceScale(oneByOne(BLACK), 2f))); + assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.luminanceScale(oneByOne(GRAY), 0f))); + assertTrue(bufferedImagesEqual(oneByOne(GRAY), ImageUtil.luminanceScale(oneByOne(GRAY), 1f))); + assertTrue(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.luminanceScale(oneByOne(GRAY), 2f))); + assertTrue(bufferedImagesEqual(oneByOne(BLACK), ImageUtil.luminanceScale(oneByOne(WHITE), 0f))); + assertTrue(bufferedImagesEqual(oneByOne(GRAY), ImageUtil.luminanceScale(oneByOne(WHITE), 0.503f))); // grayscaleOffset does Math.floor + assertTrue(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.luminanceScale(oneByOne(WHITE), 1f))); + assertTrue(bufferedImagesEqual(oneByOne(WHITE), ImageUtil.luminanceScale(oneByOne(WHITE), 2f))); } @Test From 754ea00789ff0f4891063f3be3730d4aff4c3008 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Wed, 27 Nov 2019 00:24:55 -0700 Subject: [PATCH 04/64] DynamicGridLayout: Take the container's insets into account --- .../java/net/runelite/client/ui/DynamicGridLayout.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/DynamicGridLayout.java b/runelite-client/src/main/java/net/runelite/client/ui/DynamicGridLayout.java index 8be06a706c..120decaf13 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/DynamicGridLayout.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/DynamicGridLayout.java @@ -100,8 +100,11 @@ public class DynamicGridLayout extends GridLayout // scaling factors final Dimension pd = preferredLayoutSize(parent); - final double sw = (1.0 * parent.getWidth()) / pd.width; - final double sh = (1.0 * parent.getHeight()) / pd.height; + final Insets parentInsets = parent.getInsets(); + int wborder = parentInsets.left + parentInsets.right; + int hborder = parentInsets.top + parentInsets.bottom; + final double sw = (1.0 * parent.getWidth() - wborder) / (pd.width - wborder); + final double sh = (1.0 * parent.getHeight() - hborder) / (pd.height - hborder); final int[] w = new int[ncols]; final int[] h = new int[nrows]; From 7ff327c57ef11dccd76cde8cb3cdbf1f020ffcb7 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sat, 30 Nov 2019 22:21:46 -0700 Subject: [PATCH 05/64] SplashScreen: Ceiling download total --- .../src/main/java/net/runelite/client/ui/SplashScreen.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java b/runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java index 6e945cb2a4..93313665e0 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java @@ -206,8 +206,9 @@ public class SplashScreen extends JFrame implements ActionListener String progress; if (mib) { - final double MiB = 1024 * 1042; - progress = String.format("%.1f / %.1f MiB", done / MiB, total / MiB); + final double MiB = 1024 * 1024; + final double CEIL = 1.d / 10.d; + progress = String.format("%.1f / %.1f MiB", done / MiB, (total / MiB) + CEIL); } else { From 5a703157772ce3069bd6d20fe5e539f0fe5ec11b Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sat, 30 Nov 2019 22:23:44 -0700 Subject: [PATCH 06/64] SplashScreen: Allow use after our L&F has been installed --- .../net/runelite/client/ui/SplashScreen.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java b/runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java index 93313665e0..59f0b13610 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/SplashScreen.java @@ -45,6 +45,8 @@ import javax.swing.UIManager; import javax.swing.border.EmptyBorder; import javax.swing.plaf.basic.BasicProgressBarUI; import lombok.extern.slf4j.Slf4j; +import net.runelite.client.ui.skin.SubstanceRuneLiteLookAndFeel; +import org.pushingpixels.substance.internal.SubstanceSynapse; @Slf4j public class SplashScreen extends JFrame implements ActionListener @@ -151,6 +153,11 @@ public class SplashScreen extends JFrame implements ActionListener } } + public static boolean isOpen() + { + return INSTANCE != null; + } + public static void init() { try @@ -164,8 +171,16 @@ public class SplashScreen extends JFrame implements ActionListener try { - UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + boolean hasLAF = UIManager.getLookAndFeel() instanceof SubstanceRuneLiteLookAndFeel; + if (!hasLAF) + { + UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + } INSTANCE = new SplashScreen(); + if (hasLAF) + { + INSTANCE.getRootPane().putClientProperty(SubstanceSynapse.COLORIZATION_FACTOR, 1.0); + } } catch (Exception e) { From 43390c0c2892df22e01677fdaa51470110b252f5 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sat, 30 Nov 2019 22:25:37 -0700 Subject: [PATCH 07/64] runelite-client: Add External Plugin support --- .../java/net/runelite/client/RuneLite.java | 11 +- .../runelite/client/RuneLiteProperties.java | 9 + .../client/config/RuneLiteConfig.java | 4 +- .../client/events/ExternalPluginsChanged.java | 38 ++ .../ExternalPluginClassLoader.java | 41 ++ .../externalplugins/ExternalPluginClient.java | 137 +++++ .../ExternalPluginManager.java | 366 ++++++++++++ .../ExternalPluginManifest.java | 84 +++ .../client/plugins/PluginManager.java | 94 +-- .../client/plugins/config/ConfigPanel.java | 33 +- .../config/PluginConfigurationDescriptor.java | 29 +- .../client/plugins/config/PluginHubPanel.java | 559 ++++++++++++++++++ .../client/plugins/config/PluginListItem.java | 40 +- .../plugins/config/PluginListPanel.java | 42 +- .../net/runelite/client/rs/ClientLoader.java | 2 + .../runelite/client/ui/FatalErrorDialog.java | 2 +- .../{rs => util}/CountingInputStream.java | 6 +- .../{rs => util}/VerificationException.java | 2 +- .../externalplugins/externalplugins.crt | 19 + .../plugins/config/pluginhub_configure.png | Bin 0 -> 410 bytes .../client/plugins/config/pluginhub_help.png | Bin 0 -> 477 bytes .../plugins/config/pluginhub_missingicon.png | Bin 0 -> 764 bytes .../net/runelite/client/runelite.properties | 2 + 23 files changed, 1447 insertions(+), 73 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/events/ExternalPluginsChanged.java create mode 100644 runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClassLoader.java create mode 100644 runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java create mode 100644 runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManager.java create mode 100644 runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManifest.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java rename runelite-client/src/main/java/net/runelite/client/{rs => util}/CountingInputStream.java (93%) rename runelite-client/src/main/java/net/runelite/client/{rs => util}/VerificationException.java (97%) create mode 100644 runelite-client/src/main/resources/net/runelite/client/externalplugins/externalplugins.crt create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/config/pluginhub_configure.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/config/pluginhub_help.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/config/pluginhub_missingicon.png diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index f70ba2dd63..cb1ffd5ee2 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -57,6 +57,7 @@ import net.runelite.client.game.ItemManager; import net.runelite.client.game.LootManager; import net.runelite.client.game.chatbox.ChatboxPanelManager; import net.runelite.client.menus.MenuManager; +import net.runelite.client.externalplugins.ExternalPluginManager; import net.runelite.client.plugins.PluginManager; import net.runelite.client.rs.ClientLoader; import net.runelite.client.rs.ClientUpdateCheckMode; @@ -80,6 +81,7 @@ public class RuneLite { public static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".runelite"); public static final File CACHE_DIR = new File(RUNELITE_DIR, "cache"); + public static final File PLUGINS_DIR = new File(RUNELITE_DIR, "plugins"); public static final File PROFILES_DIR = new File(RUNELITE_DIR, "profiles"); public static final File SCREENSHOT_DIR = new File(RUNELITE_DIR, "screenshots"); public static final File LOGS_DIR = new File(RUNELITE_DIR, "logs"); @@ -90,6 +92,9 @@ public class RuneLite @Inject private PluginManager pluginManager; + @Inject + private ExternalPluginManager externalPluginManager; + @Inject private EventBus eventBus; @@ -288,12 +293,13 @@ public class RuneLite // Load the plugins, but does not start them yet. // This will initialize configuration pluginManager.loadCorePlugins(); + externalPluginManager.loadExternalPlugins(); SplashScreen.stage(.70, null, "Finalizing configuration"); // Plugins have provided their config, so set default config // to main settings - pluginManager.loadDefaultPluginConfiguration(); + pluginManager.loadDefaultPluginConfiguration(null); // Start client session clientSessionManager.start(); @@ -309,6 +315,7 @@ public class RuneLite // Register event listeners eventBus.register(clientUI); eventBus.register(pluginManager); + eventBus.register(externalPluginManager); eventBus.register(overlayManager); eventBus.register(drawManager); eventBus.register(infoBoxManager); @@ -337,7 +344,7 @@ public class RuneLite } // Start plugins - pluginManager.startCorePlugins(); + pluginManager.startPlugins(); SplashScreen.stop(); diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java index 60355dee62..6408be3522 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Properties; import javax.annotation.Nullable; +import okhttp3.HttpUrl; public class RuneLiteProperties { @@ -45,6 +46,8 @@ public class RuneLiteProperties private static final String DNS_CHANGE_LINK = "runelite.dnschange.link"; private static final String JAV_CONFIG = "runelite.jav_config"; private static final String JAV_CONFIG_BACKUP = "runelite.jav_config_backup"; + private static final String PLUGINHUB_BASE = "runelite.pluginhub.url"; + private static final String PLUGINHUB_VERSION = "runelite.pluginhub.version"; private static final Properties properties = new Properties(); @@ -130,4 +133,10 @@ public class RuneLiteProperties { return properties.getProperty(JAV_CONFIG_BACKUP); } + + public static HttpUrl getPluginHubBase() + { + String version = System.getProperty(PLUGINHUB_VERSION, properties.getProperty(PLUGINHUB_VERSION)); + return HttpUrl.parse(properties.get(PLUGINHUB_BASE) + "/" + version); + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java index 2c4cf3597a..e28bcdcbf1 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/config/RuneLiteConfig.java @@ -28,9 +28,11 @@ import java.awt.Dimension; import net.runelite.api.Constants; import net.runelite.client.ui.ContainableFrame; -@ConfigGroup("runelite") +@ConfigGroup(RuneLiteConfig.GROUP_NAME) public interface RuneLiteConfig extends Config { + String GROUP_NAME = "runelite"; + @ConfigItem( keyName = "gameSize", name = "Game size", diff --git a/runelite-client/src/main/java/net/runelite/client/events/ExternalPluginsChanged.java b/runelite-client/src/main/java/net/runelite/client/events/ExternalPluginsChanged.java new file mode 100644 index 0000000000..f38490121e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/ExternalPluginsChanged.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Abex + * 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.events; + +import java.util.List; +import lombok.Value; +import net.runelite.client.externalplugins.ExternalPluginManifest; + +/** + * Posted when an external plugin has been added, removed, or updated + */ +@Value +public class ExternalPluginsChanged +{ + private final List loadedManifest; +} diff --git a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClassLoader.java b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClassLoader.java new file mode 100644 index 0000000000..054a7779b5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClassLoader.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019 Abex + * 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.externalplugins; + +import java.net.URL; +import java.net.URLClassLoader; +import lombok.Getter; + +class ExternalPluginClassLoader extends URLClassLoader +{ + @Getter + private final ExternalPluginManifest manifest; + + ExternalPluginClassLoader(ExternalPluginManifest manifest, URL[] urls) + { + super(urls, ExternalPluginClassLoader.class.getClassLoader()); + this.manifest = manifest; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java new file mode 100644 index 0000000000..cd023c8c3a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019 Abex + * 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.externalplugins; + +import com.google.common.reflect.TypeToken; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.List; +import javax.imageio.ImageIO; +import javax.inject.Inject; +import net.runelite.client.RuneLiteProperties; +import net.runelite.http.api.RuneLiteAPI; +import net.runelite.client.util.VerificationException; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okio.BufferedSource; + +public class ExternalPluginClient +{ + private final OkHttpClient cachingClient; + + @Inject + public ExternalPluginClient(OkHttpClient cachingClient) + { + this.cachingClient = cachingClient; + } + + public List downloadManifest() throws IOException, VerificationException + { + HttpUrl manifest = RuneLiteProperties.getPluginHubBase() + .newBuilder() + .addPathSegments("manifest.js") + .build(); + try (Response res = cachingClient.newCall(new Request.Builder().url(manifest).build()).execute()) + { + if (res.code() != 200) + { + throw new IOException("Non-OK response code: " + res.code()); + } + + BufferedSource src = res.body().source(); + + byte[] signature = new byte[src.readInt()]; + src.readFully(signature); + + byte[] data = src.readByteArray(); + Signature s = Signature.getInstance("SHA256withRSA"); + s.initVerify(loadCertificate()); + s.update(data); + + if (!s.verify(signature)) + { + throw new VerificationException("Unable to verify external plugin manifest"); + } + + return RuneLiteAPI.GSON.fromJson(new String(data, StandardCharsets.UTF_8), + new TypeToken>() + { + }.getType()); + } + catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) + { + throw new RuntimeException(e); + } + } + + public BufferedImage downloadIcon(ExternalPluginManifest plugin) throws IOException + { + if (!plugin.hasIcon()) + { + return null; + } + + HttpUrl url = RuneLiteProperties.getPluginHubBase() + .newBuilder() + .addPathSegment(plugin.getInternalName()) + .addPathSegment(plugin.getCommit() + ".png") + .build(); + + try (Response res = cachingClient.newCall(new Request.Builder().url(url).build()).execute()) + { + byte[] bytes = res.body().bytes(); + // We don't stream so the lock doesn't block the edt trying to load something at the same time + synchronized (ImageIO.class) + { + return ImageIO.read(new ByteArrayInputStream(bytes)); + } + } + } + + private static Certificate loadCertificate() + { + try + { + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + Certificate certificate = certFactory.generateCertificate(ExternalPluginClient.class.getResourceAsStream("externalplugins.crt")); + return certificate; + } + catch (CertificateException e) + { + throw new RuntimeException(e); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManager.java b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManager.java new file mode 100644 index 0000000000..531f443cf9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManager.java @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2019 Abex + * 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.externalplugins; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; +import com.google.common.hash.Hashing; +import com.google.common.hash.HashingInputStream; +import com.google.common.io.Files; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Function; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.RuneLite; +import net.runelite.client.RuneLiteProperties; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.RuneLiteConfig; +import net.runelite.client.eventbus.EventBus; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ExternalPluginsChanged; +import net.runelite.client.events.SessionClose; +import net.runelite.client.events.SessionOpen; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginInstantiationException; +import net.runelite.client.plugins.PluginManager; +import net.runelite.client.ui.SplashScreen; +import net.runelite.client.util.CountingInputStream; +import net.runelite.client.util.Text; +import net.runelite.client.util.VerificationException; +import net.runelite.http.api.RuneLiteAPI; +import okhttp3.HttpUrl; +import okhttp3.Request; +import okhttp3.Response; + +@Singleton +@Slf4j +public class ExternalPluginManager +{ + private static final String PLUGIN_LIST_KEY = "externalPlugins"; + private static Class[] builtinExternals = null; + + @Inject + private ConfigManager configManager; + + @Inject + private ExternalPluginClient externalPluginClient; + + @Inject + private PluginManager pluginManager; + + @Inject + private ScheduledExecutorService executor; + + @Inject + private EventBus eventBus; + + public void loadExternalPlugins() throws PluginInstantiationException + { + refreshPlugins(); + + if (builtinExternals != null) + { + // builtin external's don't actually have a manifest or a separate classloader... + pluginManager.loadPlugins(Lists.newArrayList(builtinExternals), null); + } + } + + @Subscribe + public void onSessionOpen(SessionOpen event) + { + executor.submit(this::refreshPlugins); + } + + @Subscribe + public void onSessionClose(SessionClose event) + { + executor.submit(this::refreshPlugins); + } + + private void refreshPlugins() + { + Multimap loadedExternalPlugins = HashMultimap.create(); + for (Plugin p : pluginManager.getPlugins()) + { + ExternalPluginManifest m = getExternalPluginManifest(p.getClass()); + if (m != null) + { + loadedExternalPlugins.put(m, p); + } + } + + List installedIDs = getInstalledExternalPlugins(); + if (installedIDs.isEmpty() && loadedExternalPlugins.isEmpty()) + { + return; + } + + boolean startup = SplashScreen.isOpen(); + try + { + double splashStart = startup ? .60 : 0; + double splashLength = startup ? .10 : 1; + if (!startup) + { + SplashScreen.init(); + } + + SplashScreen.stage(splashStart, null, "Downloading external plugins"); + Set externalPlugins = new HashSet<>(); + + RuneLite.PLUGINS_DIR.mkdirs(); + + List manifestList; + try + { + manifestList = externalPluginClient.downloadManifest(); + Map manifests = manifestList + .stream().collect(ImmutableMap.toImmutableMap(ExternalPluginManifest::getInternalName, Function.identity())); + + Set needsDownload = new HashSet<>(); + Set keep = new HashSet<>(); + + for (String name : installedIDs) + { + ExternalPluginManifest manifest = manifests.get(name); + if (manifest != null) + { + externalPlugins.add(manifest); + + if (!manifest.isValid()) + { + needsDownload.add(manifest); + } + else + { + keep.add(manifest.getJarFile()); + } + } + } + + // delete old plugins + File[] files = RuneLite.PLUGINS_DIR.listFiles(); + if (files != null) + { + for (File fi : files) + { + if (!keep.contains(fi)) + { + fi.delete(); + } + } + } + + int toDownload = needsDownload.stream().mapToInt(ExternalPluginManifest::getSize).sum(); + int downloaded = 0; + + for (ExternalPluginManifest manifest : needsDownload) + { + HttpUrl url = RuneLiteProperties.getPluginHubBase().newBuilder() + .addPathSegment(manifest.getInternalName()) + .addPathSegment(manifest.getCommit() + ".jar") + .build(); + + try (Response res = RuneLiteAPI.CLIENT.newCall(new Request.Builder().url(url).build()).execute()) + { + int fdownloaded = downloaded; + downloaded += manifest.getSize(); + HashingInputStream his = new HashingInputStream(Hashing.sha256(), + new CountingInputStream(res.body().byteStream(), i -> + SplashScreen.stage(splashStart + (splashLength * .2), splashStart + (splashLength * .8), + null, "Downloading " + manifest.getDisplayName(), + i + fdownloaded, toDownload, true))); + Files.asByteSink(manifest.getJarFile()).writeFrom(his); + if (!his.hash().toString().equals(manifest.getHash())) + { + throw new VerificationException("Plugin " + manifest.getInternalName() + " didn't match its hash"); + } + } + catch (IOException | VerificationException e) + { + externalPlugins.remove(manifest); + log.error("Unable to download external plugin \"{}\"", manifest.getInternalName(), e); + } + } + } + catch (IOException | VerificationException e) + { + log.error("Unable to download external plugins", e); + return; + } + + SplashScreen.stage(splashStart + (splashLength * .8), null, "Starting external plugins"); + + // TODO(abex): make sure the plugins get fully removed from the scheduler/eventbus/other managers (iterate and check classloader) + Set add = new HashSet<>(); + for (ExternalPluginManifest ex : externalPlugins) + { + if (loadedExternalPlugins.removeAll(ex).size() <= 0) + { + add.add(ex); + } + } + // list of loaded external plugins that aren't in the manifest + Collection remove = loadedExternalPlugins.values(); + + for (Plugin p : remove) + { + log.info("Stopping external plugin \"{}\"", p.getClass()); + try + { + pluginManager.stopPlugin(p); + } + catch (PluginInstantiationException e) + { + log.warn("Unable to stop external plugin \"{}\"", p.getClass().getName(), e); + } + pluginManager.remove(p); + } + + for (ExternalPluginManifest manifest : add) + { + // I think this can't happen, but just in case + if (!manifest.isValid()) + { + log.warn("Invalid plugin for validated manifest: {}", manifest); + continue; + } + + log.info("Loading external plugin \"{}\" version \"{}\" commit \"{}\"", manifest.getInternalName(), manifest.getVersion(), manifest.getCommit()); + + List newPlugins = null; + try + { + ClassLoader cl = new ExternalPluginClassLoader(manifest, new URL[]{manifest.getJarFile().toURI().toURL()}); + List> clazzes = new ArrayList<>(); + for (String className : manifest.getPlugins()) + { + clazzes.add(cl.loadClass(className)); + } + + newPlugins = pluginManager.loadPlugins(clazzes, null); + if (!startup) + { + pluginManager.loadDefaultPluginConfiguration(newPlugins); + + for (Plugin p : newPlugins) + { + pluginManager.startPlugin(p); + } + } + } + catch (Exception e) + { + log.warn("Unable to start or load external plugin \"{}\"", manifest.getInternalName(), e); + if (newPlugins != null) + { + for (Plugin p : newPlugins) + { + try + { + pluginManager.stopPlugin(p); + } + catch (Exception inner) + { + } + pluginManager.remove(p); + } + } + } + } + + if (!startup) + { + eventBus.post(new ExternalPluginsChanged(manifestList)); + } + } + finally + { + if (!startup) + { + SplashScreen.stop(); + } + } + } + + public List getInstalledExternalPlugins() + { + String externalPluginsStr = configManager.getConfiguration(RuneLiteConfig.GROUP_NAME, PLUGIN_LIST_KEY); + return Text.fromCSV(externalPluginsStr == null ? "" : externalPluginsStr); + } + + public void install(String key) + { + Set plugins = new HashSet<>(getInstalledExternalPlugins()); + if (plugins.add(key)) + { + configManager.setConfiguration(RuneLiteConfig.GROUP_NAME, PLUGIN_LIST_KEY, Text.toCSV(plugins)); + executor.submit(this::refreshPlugins); + } + } + + public void remove(String key) + { + Set plugins = new HashSet<>(getInstalledExternalPlugins()); + if (plugins.remove(key)) + { + configManager.setConfiguration(RuneLiteConfig.GROUP_NAME, PLUGIN_LIST_KEY, Text.toCSV(plugins)); + executor.submit(this::refreshPlugins); + } + } + + public void update() + { + executor.submit(this::refreshPlugins); + } + + public static ExternalPluginManifest getExternalPluginManifest(Class plugin) + { + ClassLoader cl = plugin.getClassLoader(); + if (cl instanceof ExternalPluginClassLoader) + { + ExternalPluginClassLoader ecl = (ExternalPluginClassLoader) cl; + return ecl.getManifest(); + } + return null; + } + + public static void loadBuiltin(Class... plugins) + { + builtinExternals = plugins; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManifest.java b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManifest.java new file mode 100644 index 0000000000..95b5fe5637 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginManifest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019 Abex + * 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.externalplugins; + +import com.google.common.hash.Hashing; +import com.google.common.io.Files; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import lombok.Data; +import lombok.EqualsAndHashCode; +import net.runelite.client.RuneLite; + +@Data +public class ExternalPluginManifest +{ + private String internalName; + private String commit; + private String hash; + private int size; + private String[] plugins; + + private String displayName; + private String version; + private String author; + private String description; + private String[] tags; + @EqualsAndHashCode.Exclude + private URL support; + private boolean hasIcon; + + public boolean hasIcon() + { + return hasIcon; + } + + File getJarFile() + { + return new File(RuneLite.PLUGINS_DIR, internalName + commit + ".jar"); + } + + boolean isValid() + { + File file = getJarFile(); + + try + { + if (file.exists()) + { + String hash = Files.asByteSource(file).hash(Hashing.sha256()).toString(); + if (this.hash.equals(hash)) + { + return true; + } + } + } + catch (IOException e) + { + } + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index 25d379c998..fadd54ebc9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -25,7 +25,6 @@ package net.runelite.client.plugins; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.graph.Graph; import com.google.common.graph.GraphBuilder; @@ -49,6 +48,7 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledExecutorService; +import java.util.function.BiConsumer; import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Named; @@ -61,7 +61,6 @@ import net.runelite.client.events.SessionClose; import net.runelite.client.events.SessionOpen; import net.runelite.client.RuneLite; import net.runelite.client.config.Config; -import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; @@ -90,8 +89,6 @@ public class PluginManager private final Provider sceneTileManager; private final List plugins = new CopyOnWriteArrayList<>(); private final List activePlugins = new CopyOnWriteArrayList<>(); - private final String runeliteGroupName = RuneLiteConfig.class - .getAnnotation(ConfigGroup.class).value(); @Setter boolean isOutdated; @@ -128,15 +125,22 @@ public class PluginManager private void refreshPlugins() { - loadDefaultPluginConfiguration(); + loadDefaultPluginConfiguration(null); getPlugins() .forEach(plugin -> executor.submit(() -> { try { - if (!startPlugin(plugin)) + if (isPluginEnabled(plugin) != activePlugins.contains(plugin)) { - stopPlugin(plugin); + if (activePlugins.contains(plugin)) + { + stopPlugin(plugin); + } + else + { + startPlugin(plugin); + } } } catch (PluginInstantiationException e) @@ -162,11 +166,15 @@ public class PluginManager return null; } - public List getPluginConfigProxies() + public List getPluginConfigProxies(Collection plugins) { List injectors = new ArrayList<>(); - injectors.add(RuneLite.getInjector()); - getPlugins().forEach(pl -> injectors.add(pl.getInjector())); + if (plugins == null) + { + injectors.add(RuneLite.getInjector()); + plugins = getPlugins(); + } + plugins.forEach(pl -> injectors.add(pl.getInjector())); List list = new ArrayList<>(); for (Injector injector : injectors) @@ -185,20 +193,15 @@ public class PluginManager return list; } - public void loadDefaultPluginConfiguration() + public void loadDefaultPluginConfiguration(Collection plugins) { - for (Object config : getPluginConfigProxies()) + for (Object config : getPluginConfigProxies(plugins)) { configManager.setDefaultConfiguration(config, false); } } - public void loadCorePlugins() throws IOException - { - plugins.addAll(scanAndInstantiate(getClass().getClassLoader(), PLUGIN_PACKAGE)); - } - - public void startCorePlugins() + public void startPlugins() { List scannedPlugins = new ArrayList<>(plugins); int loaded = 0; @@ -219,37 +222,41 @@ public class PluginManager } } - List scanAndInstantiate(ClassLoader classLoader, String packageName) throws IOException + public void loadCorePlugins() throws IOException, PluginInstantiationException { SplashScreen.stage(.59, null, "Loading Plugins"); + ClassPath classPath = ClassPath.from(getClass().getClassLoader()); + + List> plugins = classPath.getTopLevelClassesRecursive(PLUGIN_PACKAGE).stream() + .map(ClassInfo::load) + .collect(Collectors.toList()); + + loadPlugins(plugins, (loaded, total) -> + SplashScreen.stage(.60, .70, null, "Loading Plugins", loaded, total, false)); + } + + public List loadPlugins(List> plugins, BiConsumer onPluginLoaded) throws PluginInstantiationException + { MutableGraph> graph = GraphBuilder .directed() .build(); - List scannedPlugins = new ArrayList<>(); - ClassPath classPath = ClassPath.from(classLoader); - - ImmutableSet classes = packageName == null ? classPath.getAllClasses() - : classPath.getTopLevelClassesRecursive(packageName); - for (ClassInfo classInfo : classes) + for (Class clazz : plugins) { - Class clazz = classInfo.load(); PluginDescriptor pluginDescriptor = clazz.getAnnotation(PluginDescriptor.class); if (pluginDescriptor == null) { if (clazz.getSuperclass() == Plugin.class) { - log.warn("Class {} is a plugin, but has no plugin descriptor", - clazz); + log.warn("Class {} is a plugin, but has no plugin descriptor", clazz); } continue; } if (clazz.getSuperclass() != Plugin.class) { - log.warn("Class {} has plugin descriptor, but is not a plugin", - clazz); + log.warn("Class {} has plugin descriptor, but is not a plugin", clazz); continue; } @@ -280,20 +287,22 @@ public class PluginManager if (Graphs.hasCycle(graph)) { - throw new RuntimeException("Plugin dependency graph contains a cycle!"); + throw new PluginInstantiationException("Plugin dependency graph contains a cycle!"); } List> sortedPlugins = topologicalSort(graph); sortedPlugins = Lists.reverse(sortedPlugins); int loaded = 0; + List newPlugins = new ArrayList<>(); for (Class pluginClazz : sortedPlugins) { Plugin plugin; try { - plugin = instantiate(scannedPlugins, (Class) pluginClazz); - scannedPlugins.add(plugin); + plugin = instantiate(this.plugins, (Class) pluginClazz); + newPlugins.add(plugin); + this.plugins.add(plugin); } catch (PluginInstantiationException ex) { @@ -301,10 +310,13 @@ public class PluginManager } loaded++; - SplashScreen.stage(.60, .70, null, "Loading Plugins", loaded, sortedPlugins.size(), false); + if (onPluginLoaded != null) + { + onPluginLoaded.accept(loaded, sortedPlugins.size()); + } } - return scannedPlugins; + return newPlugins; } public synchronized boolean startPlugin(Plugin plugin) throws PluginInstantiationException @@ -355,13 +367,11 @@ public class PluginManager public synchronized boolean stopPlugin(Plugin plugin) throws PluginInstantiationException { - if (!activePlugins.contains(plugin) || isPluginEnabled(plugin)) + if (!activePlugins.remove(plugin)) { return false; } - activePlugins.remove(plugin); - try { unschedule(plugin); @@ -395,13 +405,13 @@ public class PluginManager public void setPluginEnabled(Plugin plugin, boolean enabled) { final String keyName = plugin.getClass().getSimpleName().toLowerCase(); - configManager.setConfiguration(runeliteGroupName, keyName, String.valueOf(enabled)); + configManager.setConfiguration(RuneLiteConfig.GROUP_NAME, keyName, String.valueOf(enabled)); } public boolean isPluginEnabled(Plugin plugin) { final String keyName = plugin.getClass().getSimpleName().toLowerCase(); - final String value = configManager.getConfiguration(runeliteGroupName, keyName); + final String value = configManager.getConfiguration(RuneLiteConfig.GROUP_NAME, keyName); if (value != null) { @@ -465,12 +475,12 @@ public class PluginManager return plugin; } - void add(Plugin plugin) + public void add(Plugin plugin) { plugins.add(plugin); } - void remove(Plugin plugin) + public void remove(Plugin plugin) { plugins.remove(plugin); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java index 4ed399b78a..01989d39d9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java @@ -44,6 +44,7 @@ import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFormattedTextField; import javax.swing.JLabel; +import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; @@ -66,7 +67,10 @@ import net.runelite.client.config.Keybind; import net.runelite.client.config.ModifierlessKeybind; import net.runelite.client.config.Range; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ExternalPluginsChanged; import net.runelite.client.events.PluginChanged; +import net.runelite.client.externalplugins.ExternalPluginManager; +import net.runelite.client.externalplugins.ExternalPluginManifest; import net.runelite.client.plugins.PluginManager; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.DynamicGridLayout; @@ -83,8 +87,8 @@ import net.runelite.client.util.Text; class ConfigPanel extends PluginPanel { private static final int SPINNER_FIELD_WIDTH = 6; - private static final ImageIcon BACK_ICON; - private static final ImageIcon BACK_ICON_HOVER; + static final ImageIcon BACK_ICON; + static final ImageIcon BACK_ICON_HOVER; private final FixedWidthPanel mainPanel; private final JLabel title; @@ -99,6 +103,9 @@ class ConfigPanel extends PluginPanel @Inject private PluginManager pluginManager; + @Inject + private ExternalPluginManager externalPluginManager; + @Inject private ColorPickerManager colorPickerManager; @@ -162,7 +169,16 @@ class ConfigPanel extends PluginPanel title.setText(name); title.setForeground(Color.WHITE); title.setToolTipText("" + name + ":
" + pluginConfig.getDescription() + ""); - PluginListItem.addLabelPopupMenu(title, pluginConfig.createSupportMenuItem()); + + ExternalPluginManifest mf = pluginConfig.getExternalPluginManifest(); + JMenuItem uninstallItem = null; + if (mf != null) + { + uninstallItem = new JMenuItem("Uninstall"); + uninstallItem.addActionListener(ev -> externalPluginManager.remove(mf.getInternalName())); + } + + PluginListItem.addLabelPopupMenu(title, pluginConfig.createSupportMenuItem(), uninstallItem); if (pluginConfig.getPlugin() != null) { @@ -495,4 +511,15 @@ class ConfigPanel extends PluginPanel }); } } + + @Subscribe + private void onExternalPluginsChanged(ExternalPluginsChanged ev) + { + if (pluginManager.getPlugins().stream() + .noneMatch(p -> p == this.pluginConfig.getPlugin())) + { + pluginList.getMuxer().popState(); + } + SwingUtilities.invokeLater(this::rebuild); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java index 69932e1d61..279991656b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java @@ -29,6 +29,8 @@ import javax.swing.JMenuItem; import lombok.Value; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigDescriptor; +import net.runelite.client.externalplugins.ExternalPluginManager; +import net.runelite.client.externalplugins.ExternalPluginManifest; import net.runelite.client.plugins.Plugin; import net.runelite.client.util.LinkBrowser; @@ -60,10 +62,35 @@ class PluginConfigurationDescriptor * * @return A {@link JMenuItem} which opens the plugin's wiki page URL in the browser when clicked */ + @Nullable JMenuItem createSupportMenuItem() { - final JMenuItem menuItem = new JMenuItem("Wiki"); + ExternalPluginManifest mf = getExternalPluginManifest(); + if (mf != null) + { + if (mf.getSupport() == null) + { + return null; + } + + JMenuItem menuItem = new JMenuItem("Support"); + menuItem.addActionListener(e -> LinkBrowser.browse(mf.getSupport().toString())); + return menuItem; + } + + JMenuItem menuItem = new JMenuItem("Wiki"); menuItem.addActionListener(e -> LinkBrowser.browse("https://github.com/runelite/runelite/wiki/" + name.replace(' ', '-'))); return menuItem; } + + @Nullable + ExternalPluginManifest getExternalPluginManifest() + { + if (plugin == null) + { + return null; + } + + return ExternalPluginManager.getExternalPluginManifest(plugin.getClass()); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java new file mode 100644 index 0000000000..09907493ff --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2019 Abex + * 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.config; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Function; +import java.util.function.ToDoubleFunction; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.swing.AbstractAction; +import javax.swing.BorderFactory; +import javax.swing.GroupLayout; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.KeyStroke; +import javax.swing.LayoutStyle; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.config.Config; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ExternalPluginsChanged; +import net.runelite.client.externalplugins.ExternalPluginClient; +import net.runelite.client.externalplugins.ExternalPluginManager; +import net.runelite.client.externalplugins.ExternalPluginManifest; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.DynamicGridLayout; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.IconTextField; +import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.LinkBrowser; +import net.runelite.client.util.SwingUtil; +import net.runelite.client.util.VerificationException; +import org.apache.commons.text.similarity.JaroWinklerDistance; + +@Slf4j +@Singleton +class PluginHubPanel extends PluginPanel +{ + private static final ImageIcon MISSING_ICON; + private static final ImageIcon HELP_ICON; + private static final ImageIcon HELP_ICON_HOVER; + private static final ImageIcon CONFIGURE_ICON; + private static final ImageIcon CONFIGURE_ICON_HOVER; + private static final Pattern SPACES = Pattern.compile(" +"); + private static final JaroWinklerDistance DISTANCE = new JaroWinklerDistance(); + + static + { + BufferedImage missingIcon = ImageUtil.getResourceStreamFromClass(PluginHubPanel.class, "pluginhub_missingicon.png"); + MISSING_ICON = new ImageIcon(missingIcon); + + BufferedImage helpIcon = ImageUtil.getResourceStreamFromClass(PluginHubPanel.class, "pluginhub_help.png"); + HELP_ICON = new ImageIcon(helpIcon); + HELP_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(helpIcon, -100)); + + BufferedImage configureIcon = ImageUtil.getResourceStreamFromClass(PluginHubPanel.class, "pluginhub_configure.png"); + CONFIGURE_ICON = new ImageIcon(configureIcon); + CONFIGURE_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(configureIcon, -100)); + } + + private class PluginItem extends JPanel + { + private static final int HEIGHT = 70; + private static final int ICON_WIDTH = 48; + private static final int BOTTOM_LINE_HEIGHT = 16; + static final float MIN_FILTER_SCORE = .8f; + + private final ExternalPluginManifest manifest; + + @Getter + private final boolean installed; + + @Getter + private float filter; + + PluginItem(ExternalPluginManifest newManifest, Collection loadedPlugins, boolean installed) + { + ExternalPluginManifest loaded = null; + if (!loadedPlugins.isEmpty()) + { + loaded = ExternalPluginManager.getExternalPluginManifest(loadedPlugins.iterator().next().getClass()); + } + + manifest = newManifest == null ? loaded : newManifest; + this.installed = installed; + + setBackground(ColorScheme.DARKER_GRAY_COLOR); + setOpaque(true); + + GroupLayout layout = new GroupLayout(this); + setLayout(layout); + + JLabel pluginName = new JLabel(manifest.getDisplayName()); + pluginName.setFont(FontManager.getRunescapeBoldFont()); + pluginName.setToolTipText(manifest.getDisplayName()); + + JLabel author = new JLabel(manifest.getAuthor()); + author.setFont(FontManager.getRunescapeSmallFont()); + author.setToolTipText(manifest.getAuthor()); + + JLabel version = new JLabel(manifest.getVersion()); + version.setFont(FontManager.getRunescapeSmallFont()); + version.setToolTipText(manifest.getVersion()); + + JLabel description = new JLabel(manifest.getDescription()); + description.setToolTipText(manifest.getDescription()); + + JLabel icon = new JLabel(); + icon.setHorizontalAlignment(JLabel.CENTER); + icon.setIcon(MISSING_ICON); + if (manifest.hasIcon()) + { + executor.submit(() -> + { + try + { + BufferedImage img = externalPluginClient.downloadIcon(manifest); + + SwingUtilities.invokeLater(() -> + { + icon.setIcon(new ImageIcon(img)); + }); + } + catch (IOException e) + { + log.info("Cannot download icon for plugin \"{}\"", manifest.getInternalName(), e); + } + }); + } + + JButton help = new JButton(HELP_ICON); + help.setRolloverIcon(HELP_ICON_HOVER); + SwingUtil.removeButtonDecorations(help); + help.setToolTipText("Help"); + help.setBorder(null); + if (manifest.getSupport() == null) + { + help.setVisible(false); + } + else + { + help.addActionListener(ev -> LinkBrowser.browse(manifest.getSupport().toString())); + } + + JButton configure = new JButton(CONFIGURE_ICON); + configure.setRolloverIcon(CONFIGURE_ICON_HOVER); + SwingUtil.removeButtonDecorations(configure); + configure.setToolTipText("Configure"); + help.setBorder(null); + if (loaded != null) + { + String search = null; + if (loadedPlugins.size() > 1) + { + search = loaded.getInternalName(); + } + else + { + Plugin plugin = loadedPlugins.iterator().next(); + Config cfg = pluginManager.getPluginConfigProxy(plugin); + if (cfg == null) + { + search = loaded.getInternalName(); + } + else + { + configure.addActionListener(l -> pluginListPanel.openConfigurationPanel(plugin)); + } + } + + if (search != null) + { + final String javaIsABadLanguage = search; + configure.addActionListener(l -> pluginListPanel.openWithFilter(javaIsABadLanguage)); + } + } + else + { + configure.setVisible(false); + } + + boolean install = !installed; + boolean update = loaded != null && newManifest != null && !newManifest.equals(loaded); + boolean remove = !install && !update; + JButton addrm = new JButton(); + if (install) + { + addrm.setText("Install"); + addrm.setBackground(new Color(0x28BE28)); + addrm.addActionListener(l -> externalPluginManager.install(manifest.getInternalName())); + } + else if (remove) + { + addrm.setText("Remove"); + addrm.setBackground(new Color(0xBE2828)); + addrm.addActionListener(l -> externalPluginManager.remove(manifest.getInternalName())); + } + else + { + assert update; + addrm.setText("Update"); + addrm.setBackground(new Color(0x1F621F)); + addrm.addActionListener(l -> externalPluginManager.update()); + } + addrm.setBorder(new LineBorder(addrm.getBackground().darker())); + addrm.setFocusPainted(false); + + layout.setHorizontalGroup(layout.createSequentialGroup() + .addComponent(icon, ICON_WIDTH, ICON_WIDTH, ICON_WIDTH) + .addGap(5) + .addGroup(layout.createParallelGroup() + .addGroup(layout.createSequentialGroup() + .addComponent(pluginName, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addComponent(author, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE)) + .addComponent(description, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(version, 0, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, 100) + .addComponent(help, 0, 24, 24) + .addComponent(configure, 0, 24, 24) + .addComponent(addrm, 0, 50, GroupLayout.PREFERRED_SIZE) + .addGap(5)))); + + layout.setVerticalGroup(layout.createParallelGroup() + .addComponent(icon, HEIGHT, HEIGHT, HEIGHT) + .addGroup(layout.createSequentialGroup() + .addGap(5) + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(pluginName) + .addComponent(author)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addComponent(description) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.PREFERRED_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(version, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT) + .addComponent(help, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT) + .addComponent(configure, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT) + .addComponent(addrm, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT, BOTTOM_LINE_HEIGHT)) + .addGap(5))); + } + + float setFilter(String[] filter) + { + ToDoubleFunction match = r -> Stream.of(filter) + .mapToDouble(l -> Math.pow(DISTANCE.apply(l, r), 2)) + .max() + .orElse(0.D); + + double sim = SPACES.splitAsStream(manifest.getDisplayName()).collect(Collectors.averagingDouble(match)) * 2; + + if (manifest.getTags() != null) + { + sim += Stream.of(manifest.getTags()).mapToDouble(match).sum(); + } + + return this.filter = (float) sim; + } + } + + private final PluginListPanel pluginListPanel; + private final ExternalPluginManager externalPluginManager; + private final PluginManager pluginManager; + private final ExternalPluginClient externalPluginClient; + private final ScheduledExecutorService executor; + + private final IconTextField searchBar; + private final JLabel refreshing; + private final JPanel mainPanel; + private List plugins = null; + + @Inject + PluginHubPanel( + PluginListPanel pluginListPanel, + ExternalPluginManager externalPluginManager, + PluginManager pluginManager, + ExternalPluginClient externalPluginClient, + ScheduledExecutorService executor) + { + super(false); + this.pluginListPanel = pluginListPanel; + this.externalPluginManager = externalPluginManager; + this.pluginManager = pluginManager; + this.externalPluginClient = externalPluginClient; + this.executor = executor; + + { + Object refresh = "this could just be a lambda, but no, it has to be abstracted"; + getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0), refresh); + getActionMap().put(refresh, new AbstractAction() + { + @Override + public void actionPerformed(ActionEvent e) + { + reloadPluginList(); + } + }); + } + + GroupLayout layout = new GroupLayout(this); + setLayout(layout); + setBackground(ColorScheme.DARK_GRAY_COLOR); + + searchBar = new IconTextField(); + searchBar.setIcon(IconTextField.Icon.SEARCH); + searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR); + searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR); + searchBar.getDocument().addDocumentListener(new DocumentListener() + { + @Override + public void insertUpdate(DocumentEvent e) + { + filter(); + } + + @Override + public void removeUpdate(DocumentEvent e) + { + filter(); + } + + @Override + public void changedUpdate(DocumentEvent e) + { + filter(); + } + }); + + JLabel externalPluginWarning = new JLabel("External plugins are not supported by the RuneLite Developers." + + "They may cause bugs or instability."); + externalPluginWarning.setBackground(new Color(0xFFBB33)); + externalPluginWarning.setForeground(Color.BLACK); + externalPluginWarning.setBorder(new EmptyBorder(5, 5, 5, 2)); + externalPluginWarning.setOpaque(true); + + JLabel externalPluginWarning2 = new JLabel("Use at your own risk!"); + externalPluginWarning2.setHorizontalAlignment(JLabel.CENTER); + externalPluginWarning2.setFont(FontManager.getRunescapeBoldFont()); + externalPluginWarning2.setBackground(externalPluginWarning.getBackground()); + externalPluginWarning2.setForeground(externalPluginWarning.getForeground()); + externalPluginWarning2.setBorder(new EmptyBorder(0, 5, 5, 5)); + externalPluginWarning2.setOpaque(true); + + JButton backButton = new JButton(ConfigPanel.BACK_ICON); + backButton.setRolloverIcon(ConfigPanel.BACK_ICON_HOVER); + SwingUtil.removeButtonDecorations(backButton); + backButton.setToolTipText("Back"); + backButton.addActionListener(l -> pluginListPanel.getMuxer().popState()); + + mainPanel = new JPanel(); + mainPanel.setBorder(BorderFactory.createEmptyBorder(0, 7, 7, 7)); + mainPanel.setLayout(new DynamicGridLayout(0, 1, 0, 5)); + mainPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + + refreshing = new JLabel("Loading..."); + refreshing.setHorizontalAlignment(JLabel.CENTER); + + JPanel mainPanelWrapper = new JPanel(); + mainPanelWrapper.setLayout(new BorderLayout()); + mainPanelWrapper.add(mainPanel, BorderLayout.NORTH); + mainPanelWrapper.add(refreshing, BorderLayout.CENTER); + + JScrollPane scrollPane = new JScrollPane(); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.setPreferredSize(new Dimension(Short.MAX_VALUE, Short.MAX_VALUE)); + scrollPane.getViewport().setLayout(new BorderLayout()); + scrollPane.getViewport().add(mainPanelWrapper, BorderLayout.CENTER); + + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(externalPluginWarning) + .addComponent(externalPluginWarning2) + .addGap(10) + .addGroup(layout.createParallelGroup() + .addComponent(backButton) + .addComponent(searchBar)) + .addGap(10) + .addComponent(scrollPane)); + + layout.setHorizontalGroup(layout.createParallelGroup() + .addComponent(externalPluginWarning, 0, Short.MAX_VALUE, Short.MAX_VALUE) + .addComponent(externalPluginWarning2, 0, Short.MAX_VALUE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(backButton) + .addComponent(searchBar) + .addGap(10)) + .addComponent(scrollPane)); + + revalidate(); + + refreshing.setVisible(false); + reloadPluginList(); + } + + private void reloadPluginList() + { + if (refreshing.isVisible()) + { + return; + } + + refreshing.setVisible(true); + mainPanel.removeAll(); + + executor.submit(() -> + { + List manifest; + try + { + manifest = externalPluginClient.downloadManifest(); + } + catch (IOException | VerificationException e) + { + log.error("", e); + SwingUtilities.invokeLater(() -> + { + refreshing.setVisible(false); + mainPanel.add(new JLabel("Downloading the plugin manifest failed")); + + JButton retry = new JButton("Retry"); + retry.addActionListener(l -> reloadPluginList()); + mainPanel.add(retry); + }); + return; + } + + reloadPluginList(manifest); + }); + } + + private void reloadPluginList(List manifest) + { + Map manifests = manifest.stream() + .collect(ImmutableMap.toImmutableMap(ExternalPluginManifest::getInternalName, Function.identity())); + + Multimap loadedPlugins = HashMultimap.create(); + for (Plugin p : pluginManager.getPlugins()) + { + Class clazz = p.getClass(); + ExternalPluginManifest mf = ExternalPluginManager.getExternalPluginManifest(clazz); + if (mf != null) + { + loadedPlugins.put(mf.getInternalName(), p); + } + } + + Set installed = new HashSet<>(externalPluginManager.getInstalledExternalPlugins()); + + SwingUtilities.invokeLater(() -> + { + plugins = Sets.union(manifests.keySet(), loadedPlugins.keySet()) + .stream() + .map(id -> new PluginItem(manifests.get(id), loadedPlugins.get(id), installed.contains(id))) + .collect(Collectors.toList()); + + refreshing.setVisible(false); + filter(); + }); + } + + void filter() + { + if (refreshing.isVisible()) + { + return; + } + + mainPanel.removeAll(); + + Stream stream = plugins.stream(); + + String search = searchBar.getText(); + boolean isSearching = search != null && !search.trim().isEmpty(); + if (isSearching) + { + String[] searchArray = SPACES.split(search.toLowerCase()); + stream = stream + .filter(p -> p.setFilter(searchArray) > PluginItem.MIN_FILTER_SCORE) + .sorted(Comparator.comparing(PluginItem::getFilter)); + } + else + { + stream = stream + .sorted(Comparator.comparing(PluginItem::isInstalled)); + } + + stream.forEach(mainPanel::add); + mainPanel.revalidate(); + } + + @Override + public void onActivate() + { + revalidate(); + searchBar.setText(""); + reloadPluginList(); + searchBar.requestFocusInWindow(); + } + + @Subscribe + private void onExternalPluginsChanged(ExternalPluginsChanged ev) + { + SwingUtilities.invokeLater(() -> reloadPluginList(ev.getLoadedManifest())); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java index d0a61f0b7d..9b1575acc3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java @@ -35,7 +35,6 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import javax.swing.ImageIcon; @@ -48,6 +47,7 @@ import javax.swing.JToggleButton; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import lombok.Getter; +import net.runelite.client.externalplugins.ExternalPluginManifest; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; import net.runelite.client.util.ImageUtil; @@ -96,6 +96,11 @@ class PluginListItem extends JPanel Collections.addAll(keywords, pluginConfig.getName().toLowerCase().split(" ")); Collections.addAll(keywords, pluginConfig.getDescription().toLowerCase().split(" ")); Collections.addAll(keywords, pluginConfig.getTags()); + ExternalPluginManifest mf = pluginConfig.getExternalPluginManifest(); + if (mf != null) + { + keywords.add(mf.getInternalName()); + } final List popupMenuItems = new ArrayList<>(); @@ -127,6 +132,7 @@ class PluginListItem extends JPanel buttonPanel.setLayout(new GridLayout(1, 2)); add(buttonPanel, BorderLayout.LINE_END); + JMenuItem configMenuItem = null; if (pluginConfig.hasConfigurables()) { JButton configButton = new JButton(CONFIG_ICON); @@ -145,13 +151,18 @@ class PluginListItem extends JPanel configButton.setVisible(true); configButton.setToolTipText("Edit plugin configuration"); - final JMenuItem configMenuItem = new JMenuItem("Configure"); + configMenuItem = new JMenuItem("Configure"); configMenuItem.addActionListener(e -> openGroupConfigPanel()); - popupMenuItems.add(configMenuItem); } - popupMenuItems.add(pluginConfig.createSupportMenuItem()); - addLabelPopupMenu(nameLabel, popupMenuItems); + JMenuItem uninstallItem = null; + if (mf != null) + { + uninstallItem = new JMenuItem("Uninstall"); + uninstallItem.addActionListener(ev -> pluginListPanel.getExternalPluginManager().remove(mf.getInternalName())); + } + + addLabelPopupMenu(nameLabel, configMenuItem, pluginConfig.createSupportMenuItem(), uninstallItem); add(nameLabel, BorderLayout.CENTER); onOffToggle = new PluginToggleButton(); @@ -214,18 +225,6 @@ class PluginListItem extends JPanel pluginListPanel.openConfigurationPanel(pluginConfig); } - /** - * Adds a mouseover effect to change the text of the passed label to {@link ColorScheme#BRAND_ORANGE} color, and - * adds the passed menu item to a popup menu shown when the label is clicked. - * - * @param label The label to attach the mouseover and click effects to - * @param menuItem The menu item to be shown when the label is clicked - */ - static void addLabelPopupMenu(final JLabel label, final JMenuItem menuItem) - { - addLabelPopupMenu(label, Collections.singletonList(menuItem)); - } - /** * Adds a mouseover effect to change the text of the passed label to {@link ColorScheme#BRAND_ORANGE} color, and * adds the passed menu items to a popup menu shown when the label is clicked. @@ -233,7 +232,7 @@ class PluginListItem extends JPanel * @param label The label to attach the mouseover and click effects to * @param menuItems The menu items to be shown when the label is clicked */ - static void addLabelPopupMenu(final JLabel label, final Collection menuItems) + static void addLabelPopupMenu(JLabel label, JMenuItem... menuItems) { final JPopupMenu menu = new JPopupMenu(); final Color labelForeground = label.getForeground(); @@ -241,6 +240,11 @@ class PluginListItem extends JPanel for (final JMenuItem menuItem : menuItems) { + if (menuItem == null) + { + continue; + } + // Some machines register mouseEntered through a popup menu, and do not register mouseExited when a popup // menu item is clicked, so reset the label's color when we click one of these options. menuItem.addActionListener(e -> label.setForeground(labelForeground)); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java index abf9ab3bdb..b7b08b780b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListPanel.java @@ -37,6 +37,7 @@ import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Provider; import javax.inject.Singleton; +import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ScrollPaneConstants; @@ -53,7 +54,9 @@ import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ExternalPluginsChanged; import net.runelite.client.events.PluginChanged; +import net.runelite.client.externalplugins.ExternalPluginManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginInstantiationException; @@ -78,8 +81,12 @@ class PluginListPanel extends PluginPanel private final Provider configPanelProvider; private final List fakePlugins = new ArrayList<>(); + @Getter + private final ExternalPluginManager externalPluginManager; + @Getter private final MultiplexingPluginPanel muxer; + private final IconTextField searchBar; private final JScrollPane scrollPane; private final FixedWidthPanel mainPanel; @@ -89,14 +96,17 @@ class PluginListPanel extends PluginPanel public PluginListPanel( ConfigManager configManager, PluginManager pluginManager, + ExternalPluginManager externalPluginManager, ScheduledExecutorService executorService, EventBus eventBus, - Provider configPanelProvider) + Provider configPanelProvider, + Provider pluginHubPanelProvider) { super(false); this.configManager = configManager; this.pluginManager = pluginManager; + this.externalPluginManager = externalPluginManager; this.executorService = executorService; this.configPanelProvider = configPanelProvider; @@ -155,9 +165,15 @@ class PluginListPanel extends PluginPanel mainPanel.setLayout(new DynamicGridLayout(0, 1, 0, 5)); mainPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + JButton externalPluginButton = new JButton("Plugin Hub"); + externalPluginButton.setBorder(new EmptyBorder(5, 5, 5, 5)); + externalPluginButton.setLayout(new BorderLayout(0, BORDER_OFFSET)); + externalPluginButton.addActionListener(l -> muxer.pushState(pluginHubPanelProvider.get())); + JPanel northPanel = new FixedWidthPanel(); northPanel.setLayout(new BorderLayout()); northPanel.add(mainPanel, BorderLayout.NORTH); + northPanel.add(externalPluginButton, BorderLayout.SOUTH); scrollPane = new JScrollPane(northPanel); scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); @@ -225,6 +241,13 @@ class PluginListPanel extends PluginPanel scrollPane.getVerticalScrollBar().setValue(scrollBarPosition); } + void openWithFilter(String filter) + { + searchBar.setText(filter); + onSearchBarChanged(); + muxer.pushState(this); + } + private void onSearchBarChanged() { final String text = searchBar.getText(); @@ -267,6 +290,18 @@ class PluginListPanel extends PluginPanel } } + void openConfigurationPanel(Plugin plugin) + { + for (PluginListItem pluginListItem : pluginList) + { + if (pluginListItem.getPluginConfig().getPlugin() == plugin) + { + openConfigurationPanel(pluginListItem.getPluginConfig()); + break; + } + } + } + void openConfigurationPanel(PluginConfigurationDescriptor plugin) { ConfigPanel panel = configPanelProvider.get(); @@ -353,4 +388,9 @@ class PluginListPanel extends PluginPanel } } + @Subscribe + private void onExternalPluginsChanged(ExternalPluginsChanged ev) + { + SwingUtilities.invokeLater(this::rebuildPluginList); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java b/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java index 10c9f76bc8..58993f63b7 100644 --- a/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java +++ b/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java @@ -65,8 +65,10 @@ import static net.runelite.client.rs.ClientUpdateCheckMode.NONE; import static net.runelite.client.rs.ClientUpdateCheckMode.VANILLA; import net.runelite.client.ui.FatalErrorDialog; import net.runelite.client.ui.SplashScreen; +import net.runelite.client.util.CountingInputStream; import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.worlds.World; +import net.runelite.client.util.VerificationException; import okhttp3.HttpUrl; import okhttp3.Request; import okhttp3.Response; diff --git a/runelite-client/src/main/java/net/runelite/client/ui/FatalErrorDialog.java b/runelite-client/src/main/java/net/runelite/client/ui/FatalErrorDialog.java index 34517868d8..131bbb7830 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/FatalErrorDialog.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/FatalErrorDialog.java @@ -53,7 +53,7 @@ import javax.swing.border.EmptyBorder; import lombok.extern.slf4j.Slf4j; import net.runelite.client.RuneLite; import net.runelite.client.RuneLiteProperties; -import net.runelite.client.rs.VerificationException; +import net.runelite.client.util.VerificationException; import net.runelite.client.util.LinkBrowser; @Slf4j diff --git a/runelite-client/src/main/java/net/runelite/client/rs/CountingInputStream.java b/runelite-client/src/main/java/net/runelite/client/util/CountingInputStream.java similarity index 93% rename from runelite-client/src/main/java/net/runelite/client/rs/CountingInputStream.java rename to runelite-client/src/main/java/net/runelite/client/util/CountingInputStream.java index 5f44362b1a..8543d65295 100644 --- a/runelite-client/src/main/java/net/runelite/client/rs/CountingInputStream.java +++ b/runelite-client/src/main/java/net/runelite/client/util/CountingInputStream.java @@ -22,18 +22,18 @@ * (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.rs; +package net.runelite.client.util; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.util.function.IntConsumer; -class CountingInputStream extends FilterInputStream +public class CountingInputStream extends FilterInputStream { private final IntConsumer changed; - CountingInputStream(InputStream in, IntConsumer changed) + public CountingInputStream(InputStream in, IntConsumer changed) { super(in); this.changed = changed; diff --git a/runelite-client/src/main/java/net/runelite/client/rs/VerificationException.java b/runelite-client/src/main/java/net/runelite/client/util/VerificationException.java similarity index 97% rename from runelite-client/src/main/java/net/runelite/client/rs/VerificationException.java rename to runelite-client/src/main/java/net/runelite/client/util/VerificationException.java index 4138a12fd3..2f6f1f5dee 100644 --- a/runelite-client/src/main/java/net/runelite/client/rs/VerificationException.java +++ b/runelite-client/src/main/java/net/runelite/client/util/VerificationException.java @@ -22,7 +22,7 @@ * (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.rs; +package net.runelite.client.util; public class VerificationException extends Exception { diff --git a/runelite-client/src/main/resources/net/runelite/client/externalplugins/externalplugins.crt b/runelite-client/src/main/resources/net/runelite/client/externalplugins/externalplugins.crt new file mode 100644 index 0000000000..2ba1550b51 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/externalplugins/externalplugins.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIJAK8uBanmNQZaMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV +BAMMEHJ1bmVsaXRlLXBsdWdpbnMwHhcNMTkxMjEyMjEwNzUxWhcNMjUxMjEwMjEw +NzUxWjAbMRkwFwYDVQQDDBBydW5lbGl0ZS1wbHVnaW5zMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEApu11OVANSU+pHaXRxB7fIZapucJ6BT46neicEixs +NVPuK/QRVjO/G8F++MXFD/tlZUOEDllDN8uaHBIVwxilqEVYL7oX65Esl7qqC1TZ +WGdjiMyYoK3CXWEWB4w+CdB31T7JG2HqH45ZsVs+U9OVWBkNkL5nNQNPOmZFd+3A +yCb9nGlO7SxduiHpwh3CV19jY47y8tevyo5qpaBuQeWtu3vbpeer0kbDarwD3xoF +yUMPRK518gxRUSmOpsSG5viQ731mKVCUUfIXz91d3s+kJYAjORHS4zJe9s+1dljp +oLYNLkaP6m3CmNtC84OxkmognvZTNMbiQ3GQm/BK4sdjPQIDAQABo1MwUTAdBgNV +HQ4EFgQUxrkiRXNd0OHPMkqgl9UgV1//OuQwHwYDVR0jBBgwFoAUxrkiRXNd0OHP +Mkqgl9UgV1//OuQwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA +StPyblz3aqOM5z2KqHX1B7Z3Q8B58g55YSefpcfwWEc6LT4HCztszcZDteWpV3W2 +ERfemkGKgsDhQ0qkzIt7tS5eNN3PPj7RZZm7vl5HquQ1vC/33ri/Z3CEKzbW7knt +i1iEpx8E9DKb9J9DjdKwNxSomOyCOFUt9YoQJs80xc1mwPDd6aWR3xwvnEUimkm+ +Dbj7HMOXLeyN810wkeWcT8nC5GhxH3ZAmVExBHsaIOB876RntzshBehjY8s8JQhw +R+fT1e8EhYMM9ylYDk1KIWFWrAujjU04lS9tXZ5C2e7fr9R953XN6Y0PNM/taNTU +GzwGroJZI02V+1ADO14rRA== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/config/pluginhub_configure.png b/runelite-client/src/main/resources/net/runelite/client/plugins/config/pluginhub_configure.png new file mode 100644 index 0000000000000000000000000000000000000000..5cf5a192e56e9ff7870ea5e09fbd9ed83e127f2a GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf&@~GAIf1 z3ua(sVrF4w=j7uT5R{No*U&fji;hpq?&zO1b@kd!yY?M8c=*)CtGDjld-3}1`>#L$ z8v6dM1L|$_ba4!kkn}ysF4Sbe;CfI#+2QED>dvkk_y78*8}+zopr0As?#R{#J2 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/config/pluginhub_help.png b/runelite-client/src/main/resources/net/runelite/client/plugins/config/pluginhub_help.png new file mode 100644 index 0000000000000000000000000000000000000000..ffe3b6084021e9b5702f245a1524caa1152db101 GIT binary patch literal 477 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf_5hy{S0KG~2!dBDI_3eLZc`HE z7tFxO#=*nOCoUnQqNc8A>g(qp7#0~9pH^C4-_Y6JH*wOG*>mSDTd{imhV47|>^pw` z>h+tq?%aF!;{ErZf4NU?V*#2o)6>ND!zC z_2T7i?&%)04nKYVJNM!OF~7B;?kW#G_sY#%b4Byw(tS@{?&ixnMBh~}i7HqSdTi&e zm-l{fIqF9IPM_!;Joj_>r{nD<{K|{9nmYse+1!ph yRD`WR^~rJW#1`Z1(35_4yZ-;$VV(0~zHU;md{@Me*CjwlGI+ZBxvX zWm9uU|HLV?W-nd4e*3O{M~|O4cmCq#tJiNmd-3x1$L~LX|9#LmPnUs#F~ZZuF~sBe z+AA-E4jG8BKIrC(aJ}Pjr1l`Ixrlg($Q{SriQB&aoqtqhw_5Hj>&HJQ&$PZjv+U9m zhEHo={s?9KYI4ET@#g$qo48fKF5CC#y4F9cx)c2Q_`G`g;7{)J{-5xQJs11BYhPl< z;;$Dz@11i;nQzXq#$D;Xnm3kLT+681C;#o%+yP)^Ykkt+BJ^DM?EI}im~O{joc`zLj^57RuJ_{2?dz1(Ol$Jn zzJHOKEcH=t*Nh7h+mdY~B<|n%`nTqbM55Q3oh!~S`SDPvJ|K5dyZB1|uJDIZVK=&V zT1#&_Hhm0Wt2blX!$C^zrXCOCcpT~bk^SAYfoTS(Vukb zlzFvsZ?(QSzrL<^$;NwG)c)Gnd&~69mNSafM6Hm6!Fdr@EdROP`&?s%w>*gh#vy~J LtDnm{r-UW| Date: Tue, 17 Dec 2019 02:18:43 -0500 Subject: [PATCH 08/64] Add HP for level 19 and 22 Guards. --- runelite-client/src/main/resources/npc_health.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runelite-client/src/main/resources/npc_health.json b/runelite-client/src/main/resources/npc_health.json index 123f28c6a9..cd1a1637a4 100644 --- a/runelite-client/src/main/resources/npc_health.json +++ b/runelite-client/src/main/resources/npc_health.json @@ -251,8 +251,10 @@ "Skeleton heavy_132": 124, "Skeleton thug_132": 124, "Black knight_33": 42, + "Guard_19": 22, "Guard_20": 22, "Guard_21": 22, + "Guard_22": 22, "Fire wizard_13": 25, "Water wizard_13": 25, "Earth wizard_13": 25, From eee9a51474561a07378dbfa6ebe16a30e55085f4 Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Tue, 17 Dec 2019 15:02:03 +0000 Subject: [PATCH 09/64] kourendlibrary: fix layout resetting unintentionally --- .../client/plugins/kourendlibrary/Library.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java index ea74a84b39..65a7e363bb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java @@ -130,14 +130,12 @@ class Library } else if (state != SolvedState.NO_DATA) { - // We know all of the possible things in this shelf. - if (book != null || bookcase.getPossibleBooks().stream().noneMatch(Book::isDarkManuscript)) + // Reset if the book we found isn't what we expected + // Reset if we found nothing when we expected something that wasn't a Dark Manuscript, since the layout has changed + if ((book != null && !bookcase.getPossibleBooks().contains(book)) || + (book == null && !bookcase.getPossibleBooks().isEmpty() && bookcase.getPossibleBooks().stream().noneMatch(Book::isDarkManuscript))) { - // Check to see if our guess is wrong - if (!bookcase.getPossibleBooks().contains(book)) - { - reset(); - } + reset(); } } From 6f0cbe973581d886a03059d6cf6a3416c1f1acdb Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Tue, 17 Dec 2019 17:12:49 +0100 Subject: [PATCH 10/64] FPS: Add range to the fps target config items --- .../java/net/runelite/client/plugins/fps/FpsConfig.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fps/FpsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/fps/FpsConfig.java index e47449ee71..feba6256cb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fps/FpsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fps/FpsConfig.java @@ -27,6 +27,7 @@ package net.runelite.client.plugins.fps; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Range; @ConfigGroup(FpsPlugin.CONFIG_GROUP_KEY) public interface FpsConfig extends Config @@ -43,6 +44,10 @@ public interface FpsConfig extends Config return false; } + @Range( + min = 1, + max = 50 + ) @ConfigItem( keyName = "maxFps", name = "Global FPS target", @@ -65,6 +70,10 @@ public interface FpsConfig extends Config return false; } + @Range( + min = 1, + max = 50 + ) @ConfigItem( keyName = "maxFpsUnfocused", name = "Unfocused FPS target", From 75687745f884d906967131bb95ac523d0a593d8a Mon Sep 17 00:00:00 2001 From: Max Weber Date: Tue, 17 Dec 2019 13:06:37 -0700 Subject: [PATCH 11/64] objectindicators: Correctly match template plane poh and cox instances use a different plane in their source chunks than the destination chunks, which made this unable to remove the objectPoint in most poh wallkits. This also makes it so if you add a dungeon to your poh it won't destroy your markers --- .../plugins/objectindicators/ObjectIndicatorsPlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsPlugin.java index 79bc678f3e..397b24b29c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsPlugin.java @@ -326,7 +326,7 @@ public class ObjectIndicatorsPlugin extends Plugin implements KeyListener { if (worldPoint.getRegionX() == objectPoint.getRegionX() && worldPoint.getRegionY() == objectPoint.getRegionY() - && object.getPlane() == objectPoint.getZ()) + && worldPoint.getPlane() == objectPoint.getZ()) { // Transform object to get the name which matches against what we've stored if (objectPoint.getName().equals(getObjectComposition(object.getId()).getName())) @@ -423,7 +423,7 @@ public class ObjectIndicatorsPlugin extends Plugin implements KeyListener regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), - client.getPlane()); + worldPoint.getPlane()); Set objectPoints = points.computeIfAbsent(regionId, k -> new HashSet<>()); From 98da4aa8108fadf493eeb90f8bcef8baaa03d3d0 Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Wed, 18 Dec 2019 03:02:25 +0000 Subject: [PATCH 12/64] interfacestyles: fix 2005 fixed mode minimap's right side The right side of the inside was missing a single pixel for some reason, which caused a "crack" where the minimap wasn't rendered --- .../plugins/interfacestyles/2005/1182.png | Bin 6968 -> 6973 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/interfacestyles/2005/1182.png b/runelite-client/src/main/resources/net/runelite/client/plugins/interfacestyles/2005/1182.png index e1ce17eff35824ad11ed058724f5611ee2719f41..c710825a8286251ea98d0f50ee6192788763da24 100644 GIT binary patch literal 6973 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 P00000NkvXXu0mjfIS6=% literal 6968 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 Date: Wed, 18 Dec 2019 17:41:59 -0500 Subject: [PATCH 13/64] api: modify RemoveFriend to accept a nameable Also update javadoc to reflect that it now also includes ignores --- .../main/java/net/runelite/api/events/RemovedFriend.java | 7 ++++--- .../client/plugins/friendnotes/FriendNotesPlugin.java | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/events/RemovedFriend.java b/runelite-api/src/main/java/net/runelite/api/events/RemovedFriend.java index c60cd13c33..a4bed3e95f 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/RemovedFriend.java +++ b/runelite-api/src/main/java/net/runelite/api/events/RemovedFriend.java @@ -25,15 +25,16 @@ package net.runelite.api.events; import lombok.Value; +import net.runelite.api.Nameable; /** - * An event where a request to remove a friend is sent to the server. + * An event trigger when a player is removed from the friend or ignore list. */ @Value public class RemovedFriend { /** - * The name of the removed friend. + * The removed friend or ignore entry */ - private final String name; + private final Nameable nameable; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/friendnotes/FriendNotesPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/friendnotes/FriendNotesPlugin.java index 75cc26163c..33371ec41f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/friendnotes/FriendNotesPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/friendnotes/FriendNotesPlugin.java @@ -251,7 +251,7 @@ public class FriendNotesPlugin extends Plugin public void onRemovedFriend(RemovedFriend event) { // Delete a friend's note if they are removed - final String displayName = Text.toJagexName(event.getName()); + final String displayName = Text.toJagexName(event.getNameable().getName()); log.debug("Remove friend: '{}'", displayName); setFriendNote(displayName, null); } From 1d19c8cbf3be129db41494f725ec24dee6189358 Mon Sep 17 00:00:00 2001 From: Rami <44623786+Rami-J@users.noreply.github.com> Date: Wed, 18 Dec 2019 19:25:15 -0500 Subject: [PATCH 14/64] friend notes: support notes on ignore list players --- .../java/net/runelite/api/widgets/WidgetInfo.java | 1 + .../plugins/friendnotes/FriendNotesPlugin.java | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 1b487ba6dd..4f2bcca5ce 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -45,6 +45,7 @@ public enum WidgetInfo INVENTORY(WidgetID.INVENTORY_GROUP_ID, 0), FRIENDS_LIST(WidgetID.FRIENDS_LIST_GROUP_ID, 0), + IGNORE_LIST(WidgetID.IGNORE_LIST_GROUP_ID, 0), CLAN_CHAT(WidgetID.CLAN_CHAT_GROUP_ID, 0), RAIDING_PARTY(WidgetID.RAIDING_PARTY_GROUP_ID, 0), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/friendnotes/FriendNotesPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/friendnotes/FriendNotesPlugin.java index 33371ec41f..ba0d85cff7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/friendnotes/FriendNotesPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/friendnotes/FriendNotesPlugin.java @@ -36,6 +36,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.Friend; +import net.runelite.api.Ignore; import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; import net.runelite.api.Nameable; @@ -164,7 +165,8 @@ public class FriendNotesPlugin extends Plugin final int groupId = WidgetInfo.TO_GROUP(event.getActionParam1()); // Look for "Message" on friends list - if (groupId == WidgetInfo.FRIENDS_LIST.getGroupId() && event.getOption().equals("Message")) + if ((groupId == WidgetInfo.FRIENDS_LIST.getGroupId() && event.getOption().equals("Message")) || + (groupId == WidgetInfo.IGNORE_LIST.getGroupId() && event.getOption().equals("Delete"))) { // Friends have color tags setHoveredFriend(Text.toJagexName(Text.removeTags(event.getTarget()))); @@ -190,7 +192,9 @@ public class FriendNotesPlugin extends Plugin @Subscribe public void onMenuOptionClicked(MenuOptionClicked event) { - if (WidgetInfo.TO_GROUP(event.getWidgetId()) == WidgetInfo.FRIENDS_LIST.getGroupId()) + final int groupId = WidgetInfo.TO_GROUP(event.getWidgetId()); + + if (groupId == WidgetInfo.FRIENDS_LIST.getGroupId() || groupId == WidgetInfo.IGNORE_LIST.getGroupId()) { if (Strings.isNullOrEmpty(event.getMenuTarget())) { @@ -230,12 +234,11 @@ public class FriendNotesPlugin extends Plugin { final Nameable nameable = event.getNameable(); - if (nameable instanceof Friend) + if (nameable instanceof Friend || nameable instanceof Ignore) { // Migrate a friend's note to their new display name - final Friend friend = (Friend) nameable; - String name = friend.getName(); - String prevName = friend.getPrevName(); + String name = nameable.getName(); + String prevName = nameable.getPrevName(); if (prevName != null) { From ac0d3664263679414b1548bf74f805500ca17ab4 Mon Sep 17 00:00:00 2001 From: Zachary Waller Date: Wed, 18 Dec 2019 18:14:45 -0800 Subject: [PATCH 15/64] examine plugin: fix overflow on item stack prices --- .../net/runelite/client/plugins/examine/ExaminePlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/examine/ExaminePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/examine/ExaminePlugin.java index 53602a3195..081c464400 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/examine/ExaminePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/examine/ExaminePlugin.java @@ -328,8 +328,8 @@ public class ExaminePlugin extends Plugin // quantity is at least 1 quantity = Math.max(1, quantity); int itemCompositionPrice = itemComposition.getPrice(); - final int gePrice = itemManager.getItemPrice(id); - final int alchPrice = itemCompositionPrice <= 0 ? 0 : Math.round(itemCompositionPrice * Constants.HIGH_ALCHEMY_MULTIPLIER); + final long gePrice = itemManager.getItemPrice(id); + final long alchPrice = itemCompositionPrice <= 0 ? 0 : Math.round(itemCompositionPrice * Constants.HIGH_ALCHEMY_MULTIPLIER); if (gePrice > 0 || alchPrice > 0) { From 678cc03f15be0c657127a865c53bc7d4a10e13f9 Mon Sep 17 00:00:00 2001 From: Zachary Waller Date: Wed, 18 Dec 2019 18:26:54 -0800 Subject: [PATCH 16/64] itemprices: fix overflow of item stack prices --- .../client/plugins/itemprices/ItemPricesOverlay.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesOverlay.java index bcb06fe6f1..d9fa163675 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesOverlay.java @@ -234,7 +234,7 @@ class ItemPricesOverlay extends Overlay if (gePrice > 0) { itemStringBuilder.append("EX: ") - .append(QuantityFormatter.quantityToStackSize(gePrice * qty)) + .append(QuantityFormatter.quantityToStackSize((long) gePrice * qty)) .append(" gp"); if (config.showEA() && qty > 1) { @@ -251,7 +251,7 @@ class ItemPricesOverlay extends Overlay } itemStringBuilder.append("HA: ") - .append(QuantityFormatter.quantityToStackSize(haValue * qty)) + .append(QuantityFormatter.quantityToStackSize((long) haValue * qty)) .append(" gp"); if (config.showEA() && qty > 1) { @@ -267,7 +267,7 @@ class ItemPricesOverlay extends Overlay itemStringBuilder.append("
"); itemStringBuilder.append("HA Profit: ") - .append(ColorUtil.wrapWithColorTag(String.valueOf(haProfit * qty), haColor)) + .append(ColorUtil.wrapWithColorTag(String.valueOf((long) haProfit * qty), haColor)) .append(" gp"); if (config.showEA() && qty > 1) { From 85288c02612272ec0ecb49d72df4156eb72e7d2a Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 19 Dec 2019 08:40:40 -0500 Subject: [PATCH 17/64] Bump to 1.6.0-SNAPSHOT --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 2 +- protocol-api/pom.xml | 2 +- protocol/pom.xml | 2 +- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 670082196a..abdcd13a53 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.5.44-SNAPSHOT + 1.6.0-SNAPSHOT cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index 4488998eac..a05e74df74 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.5.44-SNAPSHOT + 1.6.0-SNAPSHOT Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index beeb60e664..979b919140 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.5.44-SNAPSHOT + 1.6.0-SNAPSHOT cache diff --git a/http-api/pom.xml b/http-api/pom.xml index 12796de388..036b443e60 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.5.44-SNAPSHOT + 1.6.0-SNAPSHOT Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index f413c98571..8e78e4caa5 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.5.44-SNAPSHOT + 1.6.0-SNAPSHOT Web Service diff --git a/pom.xml b/pom.xml index bbfbfa1eac..20b332677d 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.5.44-SNAPSHOT + 1.6.0-SNAPSHOT pom RuneLite diff --git a/protocol-api/pom.xml b/protocol-api/pom.xml index b2413be1b4..165fe5b247 100644 --- a/protocol-api/pom.xml +++ b/protocol-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.5.44-SNAPSHOT + 1.6.0-SNAPSHOT protocol-api diff --git a/protocol/pom.xml b/protocol/pom.xml index 24ed77c947..5baee3bf6e 100644 --- a/protocol/pom.xml +++ b/protocol/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.5.44-SNAPSHOT + 1.6.0-SNAPSHOT protocol diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index c3ce772241..38791b4578 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.5.44-SNAPSHOT + 1.6.0-SNAPSHOT runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index d8876570ee..9b4713ad1b 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.5.44-SNAPSHOT + 1.6.0-SNAPSHOT client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index d6ece9fce8..5d336cc020 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.5.44-SNAPSHOT + 1.6.0-SNAPSHOT script-assembler-plugin From 4882b4e7a16e96dae670dde64b789e7670d455fd Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Thu, 19 Dec 2019 21:50:57 +0000 Subject: [PATCH 18/64] Release 1.6.0 --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 4 ++-- protocol-api/pom.xml | 2 +- protocol/pom.xml | 2 +- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index abdcd13a53..5dd862bd9e 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0-SNAPSHOT + 1.6.0 cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index a05e74df74..e7208309b3 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.0-SNAPSHOT + 1.6.0 Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index 979b919140..e979319dc6 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0-SNAPSHOT + 1.6.0 cache diff --git a/http-api/pom.xml b/http-api/pom.xml index 036b443e60..0b8b52e387 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.0-SNAPSHOT + 1.6.0 Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index 8e78e4caa5..775f91ce60 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.0-SNAPSHOT + 1.6.0 Web Service diff --git a/pom.xml b/pom.xml index 20b332677d..72c44b1573 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.0-SNAPSHOT + 1.6.0 pom RuneLite @@ -59,7 +59,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - HEAD + runelite-parent-1.6.0 diff --git a/protocol-api/pom.xml b/protocol-api/pom.xml index 165fe5b247..9fc95dacae 100644 --- a/protocol-api/pom.xml +++ b/protocol-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0-SNAPSHOT + 1.6.0 protocol-api diff --git a/protocol/pom.xml b/protocol/pom.xml index 5baee3bf6e..357019892e 100644 --- a/protocol/pom.xml +++ b/protocol/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0-SNAPSHOT + 1.6.0 protocol diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index 38791b4578..9f8f7c0b8c 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0-SNAPSHOT + 1.6.0 runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 9b4713ad1b..3dd59c04ef 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0-SNAPSHOT + 1.6.0 client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 5d336cc020..28cf851b46 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0-SNAPSHOT + 1.6.0 script-assembler-plugin From d678e67ab3fccae925a289a79a6191514dc45d7a Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Thu, 19 Dec 2019 21:51:07 +0000 Subject: [PATCH 19/64] Bump for 1.6.1-SNAPSHOT --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 4 ++-- protocol-api/pom.xml | 2 +- protocol/pom.xml | 2 +- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 5dd862bd9e..ccdbae13c0 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0 + 1.6.1-SNAPSHOT cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index e7208309b3..4f0c559724 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.0 + 1.6.1-SNAPSHOT Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index e979319dc6..55ea9da04f 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0 + 1.6.1-SNAPSHOT cache diff --git a/http-api/pom.xml b/http-api/pom.xml index 0b8b52e387..48f3a8e20e 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.0 + 1.6.1-SNAPSHOT Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index 775f91ce60..c54e4496ba 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.0 + 1.6.1-SNAPSHOT Web Service diff --git a/pom.xml b/pom.xml index 72c44b1573..16003b7af5 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.0 + 1.6.1-SNAPSHOT pom RuneLite @@ -59,7 +59,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - runelite-parent-1.6.0 + HEAD diff --git a/protocol-api/pom.xml b/protocol-api/pom.xml index 9fc95dacae..a5958cb2f6 100644 --- a/protocol-api/pom.xml +++ b/protocol-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0 + 1.6.1-SNAPSHOT protocol-api diff --git a/protocol/pom.xml b/protocol/pom.xml index 357019892e..bbf06de65a 100644 --- a/protocol/pom.xml +++ b/protocol/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0 + 1.6.1-SNAPSHOT protocol diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index 9f8f7c0b8c..aaa8fd7f59 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0 + 1.6.1-SNAPSHOT runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 3dd59c04ef..a4d74de1be 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0 + 1.6.1-SNAPSHOT client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 28cf851b46..8a80a4443f 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.0 + 1.6.1-SNAPSHOT script-assembler-plugin From 37504f2f717d28117a762be82976b858120efa28 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Thu, 19 Dec 2019 15:16:10 -0700 Subject: [PATCH 20/64] config: Fix PluginHub scrollbar --- .../net/runelite/client/plugins/config/PluginHubPanel.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java index 09907493ff..7fcba81410 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java @@ -410,7 +410,7 @@ class PluginHubPanel extends PluginPanel refreshing = new JLabel("Loading..."); refreshing.setHorizontalAlignment(JLabel.CENTER); - JPanel mainPanelWrapper = new JPanel(); + JPanel mainPanelWrapper = new FixedWidthPanel(); mainPanelWrapper.setLayout(new BorderLayout()); mainPanelWrapper.add(mainPanel, BorderLayout.NORTH); mainPanelWrapper.add(refreshing, BorderLayout.CENTER); @@ -418,8 +418,7 @@ class PluginHubPanel extends PluginPanel JScrollPane scrollPane = new JScrollPane(); scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setPreferredSize(new Dimension(Short.MAX_VALUE, Short.MAX_VALUE)); - scrollPane.getViewport().setLayout(new BorderLayout()); - scrollPane.getViewport().add(mainPanelWrapper, BorderLayout.CENTER); + scrollPane.setViewportView(mainPanelWrapper); layout.setVerticalGroup(layout.createSequentialGroup() .addComponent(externalPluginWarning) From e197b1763706f0940b93aca14cb7bccc5fd97b45 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Thu, 19 Dec 2019 15:46:13 -0700 Subject: [PATCH 21/64] config: Work around JDK-8079640 --- .../net/runelite/client/plugins/config/PluginHubPanel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java index 7fcba81410..b809c079c0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java @@ -417,7 +417,8 @@ class PluginHubPanel extends PluginPanel JScrollPane scrollPane = new JScrollPane(); scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - scrollPane.setPreferredSize(new Dimension(Short.MAX_VALUE, Short.MAX_VALUE)); + // Can't use Short.MAX_VALUE like the docs say because of JDK-8079640 + scrollPane.setPreferredSize(new Dimension(0x7000, 0x7000)); scrollPane.setViewportView(mainPanelWrapper); layout.setVerticalGroup(layout.createSequentialGroup() From 0bcab39e4972022528af77c22bf38851692c4dc9 Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Sat, 21 Dec 2019 16:23:47 -0800 Subject: [PATCH 22/64] loottracker plugin: Add Loot Received event --- .../plugins/loottracker/LootReceived.java | 44 ++++++++++++ .../loottracker/LootTrackerPlugin.java | 69 +++++++------------ 2 files changed, 70 insertions(+), 43 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootReceived.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootReceived.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootReceived.java new file mode 100644 index 0000000000..bba5363988 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootReceived.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019, Adam + * 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.loottracker; + +import java.util.Collection; +import lombok.AllArgsConstructor; +import lombok.Data; +import net.runelite.client.game.ItemStack; +import net.runelite.http.api.loottracker.LootRecordType; + +/** + * Event published by the loottracker plugin when new loot is received + */ +@Data +@AllArgsConstructor +public class LootReceived +{ + private String name; + private int combatLevel; + private LootRecordType type; + private Collection items; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index f6f1083d1a..8a15bad789 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -64,7 +64,6 @@ import net.runelite.api.Player; import net.runelite.api.SpriteID; import net.runelite.api.coords.WorldPoint; import net.runelite.api.events.ChatMessage; -import net.runelite.client.events.ConfigChanged; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.ItemContainerChanged; import net.runelite.api.events.WidgetLoaded; @@ -73,7 +72,9 @@ import net.runelite.client.account.AccountSession; import net.runelite.client.account.SessionManager; import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; import net.runelite.client.events.NpcLootReceived; import net.runelite.client.events.PlayerLootReceived; import net.runelite.client.events.SessionClose; @@ -159,6 +160,9 @@ public class LootTrackerPlugin extends Plugin @Inject private ScheduledExecutorService executor; + @Inject + private EventBus eventBus; + private LootTrackerPanel panel; private NavigationButton navButton; private String eventType; @@ -320,6 +324,23 @@ public class LootTrackerPlugin extends Plugin } } + void addLoot(String name, int combatLevel, LootRecordType type, Collection items) + { + final LootTrackerItem[] entries = buildEntries(stack(items)); + SwingUtilities.invokeLater(() -> panel.add(name, combatLevel, entries)); + + if (config.saveLoot()) + { + LootRecord lootRecord = new LootRecord(name, type, toGameItems(items), Instant.now()); + synchronized (queuedLoots) + { + queuedLoots.add(lootRecord); + } + } + + eventBus.post(new LootReceived(name, combatLevel, type, items)); + } + @Subscribe public void onNpcLootReceived(final NpcLootReceived npcLootReceived) { @@ -327,17 +348,8 @@ public class LootTrackerPlugin extends Plugin final Collection items = npcLootReceived.getItems(); final String name = npc.getName(); final int combat = npc.getCombatLevel(); - final LootTrackerItem[] entries = buildEntries(stack(items)); - SwingUtilities.invokeLater(() -> panel.add(name, combat, entries)); - if (config.saveLoot()) - { - LootRecord lootRecord = new LootRecord(name, LootRecordType.NPC, toGameItems(items), Instant.now()); - synchronized (queuedLoots) - { - queuedLoots.add(lootRecord); - } - } + addLoot(name, combat, LootRecordType.NPC, items); } @Subscribe @@ -353,17 +365,8 @@ public class LootTrackerPlugin extends Plugin final Collection items = playerLootReceived.getItems(); final String name = player.getName(); final int combat = player.getCombatLevel(); - final LootTrackerItem[] entries = buildEntries(stack(items)); - SwingUtilities.invokeLater(() -> panel.add(name, combat, entries)); - if (config.saveLoot()) - { - LootRecord lootRecord = new LootRecord(name, LootRecordType.PLAYER, toGameItems(items), Instant.now()); - synchronized (queuedLoots) - { - queuedLoots.add(lootRecord); - } - } + addLoot(name, combat, LootRecordType.PLAYER, items); } @Subscribe @@ -433,17 +436,7 @@ public class LootTrackerPlugin extends Plugin return; } - final LootTrackerItem[] entries = buildEntries(stack(items)); - SwingUtilities.invokeLater(() -> panel.add(eventType, -1, entries)); - - if (config.saveLoot()) - { - LootRecord lootRecord = new LootRecord(eventType, LootRecordType.EVENT, toGameItems(items), Instant.now()); - synchronized (queuedLoots) - { - queuedLoots.add(lootRecord); - } - } + addLoot(eventType, -1, LootRecordType.EVENT, items); } @Subscribe @@ -599,17 +592,7 @@ public class LootTrackerPlugin extends Plugin .map(e -> new ItemStack(e.getElement(), e.getCount(), client.getLocalPlayer().getLocalLocation())) .collect(Collectors.toList()); - final LootTrackerItem[] entries = buildEntries(stack(items)); - SwingUtilities.invokeLater(() -> panel.add(chestType, -1, entries)); - - if (config.saveLoot()) - { - LootRecord lootRecord = new LootRecord(chestType, LootRecordType.EVENT, toGameItems(items), Instant.now()); - synchronized (queuedLoots) - { - queuedLoots.add(lootRecord); - } - } + addLoot(chestType, -1, LootRecordType.EVENT, items); inventorySnapshot = null; } From a8d2dd08d4916afa20750533a05c9e45355f6f8e Mon Sep 17 00:00:00 2001 From: RuneLite Cache-Code Autoupdater Date: Mon, 16 Dec 2019 17:03:23 +0000 Subject: [PATCH 23/64] Update Item IDs to 2019-12-16-rev182 --- .../src/main/java/net/runelite/api/ItemID.java | 15 +++++++++++++++ .../main/java/net/runelite/api/NullItemID.java | 1 + 2 files changed, 16 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/ItemID.java b/runelite-api/src/main/java/net/runelite/api/ItemID.java index 0750496ad8..aeef0e83b7 100644 --- a/runelite-api/src/main/java/net/runelite/api/ItemID.java +++ b/runelite-api/src/main/java/net/runelite/api/ItemID.java @@ -11365,5 +11365,20 @@ public final class ItemID public static final int VOLATILE_NIGHTMARE_STAFF = 24424; public static final int ELDRITCH_NIGHTMARE_STAFF = 24425; public static final int CABBAGE_24426 = 24426; + public static final int GREEN_GINGERBREAD_SHIELD = 24428; + public static final int RED_GINGERBREAD_SHIELD = 24430; + public static final int BLUE_GINGERBREAD_SHIELD = 24431; + public static final int FESTIVE_CINNAMON_STICK = 24432; + public static final int FESTIVE_GINGER_POWDER = 24433; + public static final int FESTIVE_EGG = 24434; + public static final int FESTIVE_POT = 24435; + public static final int FESTIVE_FLOUR = 24436; + public static final int GINGERBREAD_SHIELD = 24437; + public static final int ICED_GINGERBREAD_SHIELD = 24438; + public static final int ICED_GINGERBREAD_SHIELD_24439 = 24439; + public static final int ICED_GINGERBREAD_SHIELD_24440 = 24440; + public static final int SCAPERUNE_TELEPORT = 24441; + public static final int BAKERY_STORAGE_KEY = 24442; + public static final int GINGERBREAD_GNOME = 24443; /* This file is automatically generated. Do not edit. */ } diff --git a/runelite-api/src/main/java/net/runelite/api/NullItemID.java b/runelite-api/src/main/java/net/runelite/api/NullItemID.java index 49dbc67c58..ebdbdc27a7 100644 --- a/runelite-api/src/main/java/net/runelite/api/NullItemID.java +++ b/runelite-api/src/main/java/net/runelite/api/NullItemID.java @@ -12852,5 +12852,6 @@ public final class NullItemID public static final int NULL_24414 = 24414; public static final int NULL_24415 = 24415; public static final int NULL_24427 = 24427; + public static final int NULL_24429 = 24429; /* This file is automatically generated. Do not edit. */ } From b808676797d4feca8cdc129c4bd9294f06ce4bb3 Mon Sep 17 00:00:00 2001 From: RuneLite Cache-Code Autoupdater Date: Mon, 16 Dec 2019 17:03:24 +0000 Subject: [PATCH 24/64] Update Item variations to 2019-12-16-rev182 --- runelite-client/src/main/resources/item_variations.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runelite-client/src/main/resources/item_variations.json b/runelite-client/src/main/resources/item_variations.json index a7c2b1d79a..a1ccb04d2e 100644 --- a/runelite-client/src/main/resources/item_variations.json +++ b/runelite-client/src/main/resources/item_variations.json @@ -9670,5 +9670,10 @@ 24393, 24403, 24411 + ], + "iced gingerbread shield": [ + 24438, + 24439, + 24440 ] } \ No newline at end of file From b7f01df174e1bb97caa4ef92570fc6fe672464e1 Mon Sep 17 00:00:00 2001 From: RuneLite Cache-Code Autoupdater Date: Mon, 16 Dec 2019 17:03:24 +0000 Subject: [PATCH 25/64] Update Object IDs to 2019-12-16-rev182 --- .../java/net/runelite/api/NullObjectID.java | 24 ++++++++++++++++ .../main/java/net/runelite/api/ObjectID.java | 28 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/NullObjectID.java b/runelite-api/src/main/java/net/runelite/api/NullObjectID.java index 9a0be1f67a..e9f06cb9ad 100644 --- a/runelite-api/src/main/java/net/runelite/api/NullObjectID.java +++ b/runelite-api/src/main/java/net/runelite/api/NullObjectID.java @@ -18074,5 +18074,29 @@ public final class NullObjectID public static final int NULL_37481 = 37481; public static final int NULL_37490 = 37490; public static final int NULL_37491 = 37491; + public static final int NULL_37561 = 37561; + public static final int NULL_37562 = 37562; + public static final int NULL_37563 = 37563; + public static final int NULL_37564 = 37564; + public static final int NULL_37565 = 37565; + public static final int NULL_37566 = 37566; + public static final int NULL_37567 = 37567; + public static final int NULL_37568 = 37568; + public static final int NULL_37569 = 37569; + public static final int NULL_37573 = 37573; + public static final int NULL_37574 = 37574; + public static final int NULL_37575 = 37575; + public static final int NULL_37576 = 37576; + public static final int NULL_37577 = 37577; + public static final int NULL_37578 = 37578; + public static final int NULL_37579 = 37579; + public static final int NULL_37580 = 37580; + public static final int NULL_37581 = 37581; + public static final int NULL_37582 = 37582; + public static final int NULL_37583 = 37583; + public static final int NULL_37584 = 37584; + public static final int NULL_37585 = 37585; + public static final int NULL_37597 = 37597; + public static final int NULL_37598 = 37598; /* This file is automatically generated. Do not edit. */ } diff --git a/runelite-api/src/main/java/net/runelite/api/ObjectID.java b/runelite-api/src/main/java/net/runelite/api/ObjectID.java index 13a71b0f9f..e955bdcf12 100644 --- a/runelite-api/src/main/java/net/runelite/api/ObjectID.java +++ b/runelite-api/src/main/java/net/runelite/api/ObjectID.java @@ -19460,5 +19460,33 @@ public final class ObjectID public static final int ORNATE_JEWELLERY_BOX_37544 = 37544; public static final int ORNATE_JEWELLERY_BOX_37545 = 37545; public static final int ORNATE_JEWELLERY_BOX_37546 = 37546; + public static final int CELL_DOOR_37547 = 37547; + public static final int TRAPDOOR_37548 = 37548; + public static final int TRAPDOOR_37549 = 37549; + public static final int BARREL_OF_FESTIVE_BRANCHES = 37550; + public static final int SACK_OF_FLOUR = 37551; + public static final int SACK_OF_GINGER = 37552; + public static final int BUCKET_OF_EGGS = 37553; + public static final int LADDER_37554 = 37554; + public static final int DOOR_37555 = 37555; + public static final int DOOR_37556 = 37556; + public static final int FIREPLACE_37557 = 37557; + public static final int LOGS_37558 = 37558; + public static final int SHELVES_37559 = 37559; + public static final int SANTAS_SACK = 37560; + public static final int TREE_37570 = 37570; + public static final int GIFTS = 37571; + public static final int GIFTS_37572 = 37572; + public static final int TREE_37586 = 37586; + public static final int TREE_STUMP_37587 = 37587; + public static final int LEVER_37588 = 37588; + public static final int LEVER_37589 = 37589; + public static final int TABLE_37590 = 37590; + public static final int RECEIVER = 37591; + public static final int MIXER = 37592; + public static final int CONVEYOR_BELT_37593 = 37593; + public static final int HOPPER_37594 = 37594; + public static final int SUPPORT_37595 = 37595; + public static final int OVEN_37596 = 37596; /* This file is automatically generated. Do not edit. */ } From cf981d421e0eaabd233fca31b9feefda4a727c06 Mon Sep 17 00:00:00 2001 From: RuneLite Cache-Code Autoupdater Date: Mon, 16 Dec 2019 17:03:25 +0000 Subject: [PATCH 26/64] Update NPC IDs to 2019-12-16-rev182 --- .../src/main/java/net/runelite/api/NpcID.java | 339 +++++++++++------- 1 file changed, 216 insertions(+), 123 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/NpcID.java b/runelite-api/src/main/java/net/runelite/api/NpcID.java index c40eac4e49..02ea92bb3d 100644 --- a/runelite-api/src/main/java/net/runelite/api/NpcID.java +++ b/runelite-api/src/main/java/net/runelite/api/NpcID.java @@ -98,7 +98,8 @@ public final class NpcID public static final int GHOST_95 = 95; public static final int GHOST_96 = 96; public static final int GHOST_97 = 97; - public static final int DEATH_WING = 99; + public static final int GHOST_98 = 98; + public static final int GHOST_99 = 99; public static final int ROCK_CRAB = 100; public static final int ROCKS = 101; public static final int ROCK_CRAB_102 = 102; @@ -459,9 +460,9 @@ public final class NpcID public static final int SKELETAL_WYVERN_468 = 468; public static final int KILLERWATT = 469; public static final int KILLERWATT_470 = 470; - public static final int DARK_WIZARD = 472; - public static final int INVRIGAR_THE_NECROMANCER = 473; - public static final int DARK_WIZARD_474 = 474; + public static final int GHOST_472 = 472; + public static final int GHOST_473 = 473; + public static final int GHOST_474 = 474; public static final int HOLE_IN_THE_WALL = 475; public static final int WALL_BEAST = 476; public static final int GIANT_FROG = 477; @@ -491,32 +492,34 @@ public final class NpcID public static final int VANESSA = 502; public static final int RICHARD = 503; public static final int ALICE = 504; - public static final int MUGGER = 505; - public static final int WITCH = 506; - public static final int WITCH_507 = 507; - public static final int BLACK_KNIGHT = 508; - public static final int BLACK_KNIGHT_509 = 509; - public static final int HIGHWAYMAN = 510; - public static final int HIGHWAYMAN_511 = 511; - public static final int CHAOS_DRUID = 512; - public static final int PIRATE = 513; - public static final int PIRATE_514 = 514; - public static final int PIRATE_515 = 515; - public static final int PIRATE_516 = 516; - public static final int THUG = 517; - public static final int ROGUE = 518; - public static final int MONK_OF_ZAMORAK = 519; - public static final int MONK_OF_ZAMORAK_520 = 520; - public static final int MONK_OF_ZAMORAK_521 = 521; - public static final int TRIBESMAN = 522; - public static final int DARK_WARRIOR = 523; - public static final int CHAOS_DRUID_WARRIOR = 524; - public static final int NECROMANCER = 525; - public static final int BANDIT = 526; - public static final int GUARD_BANDIT = 527; - public static final int BARBARIAN_GUARD = 528; - public static final int PORTAL = 530; - public static final int PORTAL_532 = 532; + public static final int GHOST_505 = 505; + public static final int GHOST_506 = 506; + public static final int GHOST_507 = 507; + public static final int SOULLESS = 508; + public static final int DEATH_WING = 509; + public static final int DARK_WIZARD = 510; + public static final int INVRIGAR_THE_NECROMANCER = 511; + public static final int DARK_WIZARD_512 = 512; + public static final int MUGGER = 513; + public static final int WITCH = 514; + public static final int WITCH_515 = 515; + public static final int BLACK_KNIGHT = 516; + public static final int BLACK_KNIGHT_517 = 517; + public static final int HIGHWAYMAN = 518; + public static final int HIGHWAYMAN_519 = 519; + public static final int CHAOS_DRUID = 520; + public static final int PIRATE = 521; + public static final int PIRATE_522 = 522; + public static final int PIRATE_523 = 523; + public static final int PIRATE_524 = 524; + public static final int THUG = 525; + public static final int ROGUE = 526; + public static final int MONK_OF_ZAMORAK = 527; + public static final int MONK_OF_ZAMORAK_528 = 528; + public static final int MONK_OF_ZAMORAK_529 = 529; + public static final int TRIBESMAN = 530; + public static final int DARK_WARRIOR = 531; + public static final int CHAOS_DRUID_WARRIOR = 532; public static final int FUNGI = 533; public static final int THESSALIA = 534; public static final int FUNGI_535 = 535; @@ -672,7 +675,7 @@ public final class NpcID public static final int BARTENDER = 687; public static final int EBLIS = 688; public static final int EBLIS_689 = 689; - public static final int BANDIT_690 = 690; + public static final int BANDIT = 690; public static final int BANDIT_691 = 691; public static final int BANDIT_692 = 692; public static final int BANDIT_693 = 693; @@ -986,16 +989,18 @@ public final class NpcID public static final int RAT_1021 = 1021; public static final int RAT_1022 = 1022; public static final int ZYGOMITE_1024 = 1024; - public static final int BANKER_1027 = 1027; - public static final int BANKER_1028 = 1028; - public static final int BANKER_1029 = 1029; - public static final int BANKER_1030 = 1030; - public static final int BANKER_1031 = 1031; - public static final int BANKER_1032 = 1032; - public static final int BANKER_1033 = 1033; - public static final int BANKER_1034 = 1034; - public static final int BANKER_1035 = 1035; - public static final int BANKER_1036 = 1036; + public static final int NECROMANCER = 1025; + public static final int BANDIT_1026 = 1026; + public static final int GUARD_BANDIT = 1027; + public static final int BARBARIAN_GUARD = 1028; + public static final int JACK_FROST = 1029; + public static final int SANTA = 1030; + public static final int ANTISANTA = 1031; + public static final int ANTISANTA_1032 = 1032; + public static final int ICELORD_1033 = 1033; + public static final int WORRIED_BAKER = 1034; + public static final int PAUL = 1035; + public static final int MARY = 1036; public static final int SNAKE = 1037; public static final int MONKEY_1038 = 1038; public static final int ALBINO_BAT = 1039; @@ -1258,13 +1263,6 @@ public final class NpcID public static final int HARI = 1325; public static final int BARFY_BILL = 1326; public static final int TYRAS_GUARD_1327 = 1327; - public static final int TRADER_STAN = 1328; - public static final int TRADER_CREWMEMBER = 1329; - public static final int TRADER_CREWMEMBER_1330 = 1330; - public static final int TRADER_CREWMEMBER_1331 = 1331; - public static final int TRADER_CREWMEMBER_1332 = 1332; - public static final int TRADER_CREWMEMBER_1333 = 1333; - public static final int TRADER_CREWMEMBER_1334 = 1334; public static final int JACK_SEAGULL = 1335; public static final int LONGBOW_BEN = 1336; public static final int AHAB = 1337; @@ -1525,10 +1523,12 @@ public final class NpcID public static final int BATTLE_MAGE = 1610; public static final int BATTLE_MAGE_1611 = 1611; public static final int BATTLE_MAGE_1612 = 1612; + public static final int BANKER_1613 = 1613; public static final int PHIALS = 1614; public static final int BANKNOTE_EXCHANGE_MERCHANT = 1615; public static final int HIGH_PRIESTESS_ZULHARCINQA = 1616; public static final int PRIESTESS_ZULGWENWYNIG = 1617; + public static final int BANKER_1618 = 1618; public static final int CAT_1619 = 1619; public static final int CAT_1620 = 1620; public static final int CAT_1621 = 1621; @@ -1543,6 +1543,8 @@ public final class NpcID public static final int LAZY_CAT_1630 = 1630; public static final int LAZY_CAT_1631 = 1631; public static final int LAZY_HELLCAT = 1632; + public static final int BANKER_1633 = 1633; + public static final int BANKER_1634 = 1634; public static final int BABY_IMPLING = 1635; public static final int YOUNG_IMPLING = 1636; public static final int GOURMET_IMPLING = 1637; @@ -1647,7 +1649,7 @@ public final class NpcID public static final int BRAWLER_1736 = 1736; public static final int BRAWLER_1737 = 1737; public static final int BRAWLER_1738 = 1738; - public static final int PORTAL_1739 = 1739; + public static final int PORTAL = 1739; public static final int PORTAL_1740 = 1740; public static final int PORTAL_1741 = 1741; public static final int PORTAL_1742 = 1742; @@ -2882,44 +2884,41 @@ public final class NpcID public static final int GOBLIN_3074 = 3074; public static final int GOBLIN_3075 = 3075; public static final int GOBLIN_3076 = 3076; - public static final int HANS = 3077; - public static final int MAN_3078 = 3078; - public static final int MAN_3079 = 3079; - public static final int MAN_3080 = 3080; - public static final int MAN_3081 = 3081; - public static final int MAN_3082 = 3082; - public static final int WOMAN_3083 = 3083; - public static final int WOMAN_3084 = 3084; - public static final int WOMAN_3085 = 3085; - public static final int FARMER = 3086; - public static final int FARMER_3087 = 3087; - public static final int FARMER_3088 = 3088; - public static final int FARMER_3089 = 3089; - public static final int FARMER_3090 = 3090; - public static final int FARMER_3091 = 3091; - public static final int THIEF_3092 = 3092; - public static final int THIEF_3093 = 3093; - public static final int GUARD_3094 = 3094; - public static final int TRAMP_3095 = 3095; - public static final int BARBARIAN_3096 = 3096; - public static final int WIZARD_3097 = 3097; - public static final int DRUID = 3098; + public static final int GEORGIE = 3077; + public static final int GEORGIE_3078 = 3078; + public static final int BEVERLY = 3079; + public static final int BEVERLY_3080 = 3080; + public static final int BILL = 3081; + public static final int BILL_3082 = 3082; + public static final int JESS = 3083; + public static final int JESS_3084 = 3084; + public static final int PORTAL_3086 = 3086; + public static final int PORTAL_3088 = 3088; + public static final int BANKER_3089 = 3089; + public static final int BANKER_3090 = 3090; + public static final int BANKER_3091 = 3091; + public static final int BANKER_3092 = 3092; + public static final int BANKER_3093 = 3093; + public static final int BANKER_3094 = 3094; + public static final int CHIEF_SERVANT = 3095; + public static final int TAXIDERMIST = 3096; + public static final int ESTATE_AGENT = 3097; + public static final int STONEMASON = 3098; public static final int HELLPUPPY_3099 = 3099; - public static final int WARRIOR_WOMAN = 3100; - public static final int MAN_3101 = 3101; - public static final int BARBARIAN_3102 = 3102; - public static final int ALKHARID_WARRIOR = 3103; - public static final int PALADIN_3104 = 3104; - public static final int PALADIN_3105 = 3105; - public static final int HERO = 3106; - public static final int FORESTER = 3107; - public static final int KNIGHT_OF_ARDOUGNE = 3108; + public static final int SIR_RENITEE = 3100; + public static final int SAWMILL_OPERATOR = 3101; + public static final int GARDEN_SUPPLIER = 3102; + public static final int GARDEN_SUPPLIER_3103 = 3103; + public static final int HANS = 3105; + public static final int MAN_3106 = 3106; + public static final int MAN_3107 = 3107; + public static final int MAN_3108 = 3108; public static final int MAN_3109 = 3109; - public static final int WOMAN_3110 = 3110; - public static final int KNIGHT_OF_ARDOUGNE_3111 = 3111; - public static final int ARCHER_3112 = 3112; - public static final int ZOO_KEEPER = 3113; - public static final int CHUCK = 3114; + public static final int MAN_3110 = 3110; + public static final int WOMAN_3111 = 3111; + public static final int WOMAN_3112 = 3112; + public static final int WOMAN_3113 = 3113; + public static final int FARMER = 3114; public static final int FARID_MORRISANE_ORES_AND_BARS = 3115; public static final int TZKIH_3116 = 3116; public static final int TZKIH_3117 = 3117; @@ -3039,26 +3038,26 @@ public final class NpcID public static final int TREE = 3240; public static final int BLANDEBIR = 3241; public static final int METARIALUS = 3242; - public static final int BARMAN_3243 = 3243; - public static final int PRIEST_3244 = 3244; - public static final int GUARD_3245 = 3245; + public static final int FARMER_3243 = 3243; + public static final int FARMER_3244 = 3244; + public static final int FARMER_3245 = 3245; public static final int WIZARD_FRUMSCONE = 3246; public static final int WIZARD_AKUTHA = 3247; public static final int WIZARD_DISTENTOR = 3248; public static final int WIZARD_SININA = 3249; - public static final int DOOR_MAN = 3250; - public static final int WATCHMAN = 3251; - public static final int SOLDIER = 3252; - public static final int WYSON_THE_GARDENER = 3253; - public static final int SIGBERT_THE_ADVENTURER = 3254; - public static final int SHIPYARD_WORKER_3255 = 3255; - public static final int SHIPYARD_WORKER_3256 = 3256; - public static final int MASTER_FARMER = 3257; - public static final int MASTER_FARMER_3258 = 3258; - public static final int MARKET_GUARD_3259 = 3259; - public static final int MAN_3260 = 3260; - public static final int GEE = 3261; - public static final int DONIE = 3262; + public static final int FARMER_3250 = 3250; + public static final int FARMER_3251 = 3251; + public static final int THIEF_3252 = 3252; + public static final int THIEF_3253 = 3253; + public static final int GUARD_3254 = 3254; + public static final int TRAMP_3255 = 3255; + public static final int BARBARIAN_3256 = 3256; + public static final int WIZARD_3257 = 3257; + public static final int DRUID = 3258; + public static final int DRUID_3259 = 3259; + public static final int WARRIOR_WOMAN = 3260; + public static final int MAN_3261 = 3261; + public static final int BARBARIAN_3262 = 3262; public static final int DRUNKEN_MAN = 3263; public static final int MAN_3264 = 3264; public static final int MAN_3265 = 3265; @@ -3088,6 +3087,19 @@ public final class NpcID public static final int HOBGOBLIN_3289 = 3289; public static final int FROG_3290 = 3290; public static final int POSTIE_PETE = 3291; + public static final int ALKHARID_WARRIOR = 3292; + public static final int PALADIN_3293 = 3293; + public static final int PALADIN_3294 = 3294; + public static final int HERO = 3295; + public static final int FORESTER = 3296; + public static final int KNIGHT_OF_ARDOUGNE = 3297; + public static final int MAN_3298 = 3298; + public static final int WOMAN_3299 = 3299; + public static final int KNIGHT_OF_ARDOUGNE_3300 = 3300; + public static final int ARCHER_3301 = 3301; + public static final int ZOO_KEEPER = 3302; + public static final int CHUCK = 3303; + public static final int BARMAN_3304 = 3304; public static final int MASTER_CHEF = 3305; public static final int HENJA = 3306; public static final int COMBAT_INSTRUCTOR = 3307; @@ -3837,7 +3849,7 @@ public final class NpcID public static final int DENULTH = 4083; public static final int SERGEANT = 4084; public static final int SERGEANT_4085 = 4085; - public static final int SOLDIER_4086 = 4086; + public static final int SOLDIER = 4086; public static final int SOLDIER_4087 = 4087; public static final int SOLDIER_4088 = 4088; public static final int SOLDIER_4089 = 4089; @@ -3965,7 +3977,7 @@ public final class NpcID public static final int PIERRE = 4213; public static final int HOBBES = 4214; public static final int LOUISA = 4215; - public static final int MARY = 4216; + public static final int MARY_4216 = 4216; public static final int STANFORD = 4217; public static final int GUARD_4218 = 4218; public static final int GOSSIP = 4219; @@ -5067,13 +5079,13 @@ public final class NpcID public static final int COMBAT_STONE_5414 = 5414; public static final int COMBAT_STONE_5415 = 5415; public static final int COMBAT_STONE_5416 = 5416; - public static final int CHIEF_SERVANT = 5417; - public static final int TAXIDERMIST = 5418; - public static final int ESTATE_AGENT = 5419; - public static final int STONEMASON = 5420; - public static final int SIR_RENITEE = 5421; - public static final int SAWMILL_OPERATOR = 5422; - public static final int GARDEN_SUPPLIER = 5423; + public static final int PRIEST_5417 = 5417; + public static final int GUARD_5418 = 5418; + public static final int DOOR_MAN = 5419; + public static final int WATCHMAN = 5420; + public static final int SOLDIER_5421 = 5421; + public static final int WYSON_THE_GARDENER = 5422; + public static final int SIGBERT_THE_ADVENTURER = 5423; public static final int CAPT_ARNAV = 5426; public static final int FLIPPA = 5427; public static final int TILT = 5428; @@ -5104,7 +5116,7 @@ public final class NpcID public static final int THUMPY = 5454; public static final int THOMDRIL = 5455; public static final int KENDALL = 5456; - public static final int DRUID_5457 = 5457; + public static final int SHIPYARD_WORKER_5457 = 5457; public static final int SUSPECT_5458 = 5458; public static final int SUSPECT_5459 = 5459; public static final int SUSPECT_5460 = 5460; @@ -5365,6 +5377,10 @@ public final class NpcID public static final int SHEEP_5726 = 5726; public static final int RABBIT_5727 = 5727; public static final int IMP_5728 = 5728; + public static final int SHIPYARD_WORKER_5729 = 5729; + public static final int MASTER_FARMER = 5730; + public static final int MASTER_FARMER_5731 = 5731; + public static final int MARKET_GUARD_5732 = 5732; public static final int ELNOCK_INQUISITOR = 5734; public static final int IMPLING = 5735; public static final int FAIRY_AERYKA = 5736; @@ -6160,14 +6176,10 @@ public final class NpcID public static final int AWOWOGEI_6812 = 6812; public static final int MONKEY_ARCHER_6813 = 6813; public static final int LAMMY_LANGLE = 6814; - public static final int GHOST_6815 = 6815; - public static final int GHOST_6816 = 6816; - public static final int GHOST_6817 = 6817; - public static final int GHOST_6818 = 6818; - public static final int GHOST_6819 = 6819; - public static final int GHOST_6820 = 6820; - public static final int GHOST_6821 = 6821; - public static final int GHOST_6822 = 6822; + public static final int MAN_6815 = 6815; + public static final int GEE = 6816; + public static final int DONIE = 6817; + public static final int MAN_6818 = 6818; public static final int TOWN_CRIER_6823 = 6823; public static final int GIANT_BAT_6824 = 6824; public static final int ROD_FISHING_SPOT_6825 = 6825; @@ -6818,7 +6830,6 @@ public final class NpcID public static final int TEKTON_ENRAGED_7544 = 7544; public static final int TEKTON_7545 = 7545; public static final int BARTENDER_7546 = 7546; - public static final int MAN_7547 = 7547; public static final int SCAVENGER_BEAST = 7548; public static final int SCAVENGER_BEAST_7549 = 7549; public static final int GREAT_OLM_RIGHT_CLAW = 7550; @@ -7161,7 +7172,7 @@ public final class NpcID public static final int REVENANT_DARK_BEAST = 7938; public static final int REVENANT_KNIGHT = 7939; public static final int REVENANT_DRAGON = 7940; - public static final int PAUL = 7941; + public static final int PAUL_7941 = 7941; public static final int EMBLEM_TRADER_7943 = 7943; public static final int FISHING_SPOT_7946 = 7946; public static final int FISHING_SPOT_7947 = 7947; @@ -7650,7 +7661,6 @@ public final class NpcID public static final int TORMENTED_SOUL_8513 = 8513; public static final int TRAPPED_SOUL = 8514; public static final int ALYSSA = 8515; - public static final int GARDEN_SUPPLIER_8516 = 8516; public static final int IKKLE_HYDRA_8517 = 8517; public static final int IKKLE_HYDRA_8518 = 8518; public static final int IKKLE_HYDRA_8519 = 8519; @@ -7822,7 +7832,6 @@ public final class NpcID public static final int SMOLCANO = 8731; public static final int MUGGER_8732 = 8732; public static final int CRAB_8733 = 8733; - public static final int SOULLESS = 8734; public static final int MOSS_GIANT_8736 = 8736; public static final int YOUNGLLEF_8737 = 8737; public static final int CORRUPTED_YOUNGLLEF_8738 = 8738; @@ -8318,5 +8327,89 @@ public final class NpcID public static final int TYPHOR_9296 = 9296; public static final int VRITRA = 9297; public static final int MAZ = 9298; + public static final int TRADER_STAN = 9299; + public static final int TRADER_STAN_9300 = 9300; + public static final int TRADER_STAN_9301 = 9301; + public static final int TRADER_STAN_9302 = 9302; + public static final int TRADER_STAN_9303 = 9303; + public static final int TRADER_STAN_9304 = 9304; + public static final int TRADER_STAN_9305 = 9305; + public static final int TRADER_STAN_9307 = 9307; + public static final int TRADER_STAN_9308 = 9308; + public static final int TRADER_STAN_9309 = 9309; + public static final int TRADER_STAN_9310 = 9310; + public static final int TRADER_STAN_9311 = 9311; + public static final int TRADER_CREWMEMBER = 9312; + public static final int TRADER_CREWMEMBER_9313 = 9313; + public static final int TRADER_CREWMEMBER_9314 = 9314; + public static final int TRADER_CREWMEMBER_9315 = 9315; + public static final int TRADER_CREWMEMBER_9316 = 9316; + public static final int TRADER_CREWMEMBER_9317 = 9317; + public static final int TRADER_CREWMEMBER_9318 = 9318; + public static final int TRADER_CREWMEMBER_9319 = 9319; + public static final int TRADER_CREWMEMBER_9320 = 9320; + public static final int TRADER_CREWMEMBER_9321 = 9321; + public static final int TRADER_CREWMEMBER_9322 = 9322; + public static final int TRADER_CREWMEMBER_9323 = 9323; + public static final int TRADER_CREWMEMBER_9324 = 9324; + public static final int TRADER_CREWMEMBER_9325 = 9325; + public static final int TRADER_CREWMEMBER_9326 = 9326; + public static final int TRADER_CREWMEMBER_9327 = 9327; + public static final int TRADER_CREWMEMBER_9328 = 9328; + public static final int TRADER_CREWMEMBER_9329 = 9329; + public static final int TRADER_CREWMEMBER_9330 = 9330; + public static final int TRADER_CREWMEMBER_9331 = 9331; + public static final int TRADER_CREWMEMBER_9332 = 9332; + public static final int TRADER_CREWMEMBER_9333 = 9333; + public static final int TRADER_CREWMEMBER_9334 = 9334; + public static final int TRADER_CREWMEMBER_9335 = 9335; + public static final int TRADER_CREWMEMBER_9336 = 9336; + public static final int TRADER_CREWMEMBER_9337 = 9337; + public static final int TRADER_CREWMEMBER_9338 = 9338; + public static final int TRADER_CREWMEMBER_9339 = 9339; + public static final int TRADER_CREWMEMBER_9340 = 9340; + public static final int TRADER_CREWMEMBER_9341 = 9341; + public static final int TRADER_CREWMEMBER_9342 = 9342; + public static final int TRADER_CREWMEMBER_9343 = 9343; + public static final int TRADER_CREWMEMBER_9344 = 9344; + public static final int TRADER_CREWMEMBER_9345 = 9345; + public static final int TRADER_CREWMEMBER_9346 = 9346; + public static final int TRADER_CREWMEMBER_9347 = 9347; + public static final int TRADER_CREWMEMBER_9348 = 9348; + public static final int TRADER_CREWMEMBER_9349 = 9349; + public static final int TRADER_CREWMEMBER_9350 = 9350; + public static final int TRADER_CREWMEMBER_9351 = 9351; + public static final int TRADER_CREWMEMBER_9352 = 9352; + public static final int TRADER_CREWMEMBER_9353 = 9353; + public static final int TRADER_CREWMEMBER_9354 = 9354; + public static final int TRADER_CREWMEMBER_9355 = 9355; + public static final int TRADER_CREWMEMBER_9356 = 9356; + public static final int TRADER_CREWMEMBER_9357 = 9357; + public static final int TRADER_CREWMEMBER_9358 = 9358; + public static final int TRADER_CREWMEMBER_9359 = 9359; + public static final int TRADER_CREWMEMBER_9360 = 9360; + public static final int TRADER_CREWMEMBER_9361 = 9361; + public static final int TRADER_CREWMEMBER_9362 = 9362; + public static final int TRADER_CREWMEMBER_9363 = 9363; + public static final int TRADER_CREWMEMBER_9364 = 9364; + public static final int TRADER_CREWMEMBER_9365 = 9365; + public static final int TRADER_CREWMEMBER_9366 = 9366; + public static final int TRADER_CREWMEMBER_9367 = 9367; + public static final int TRADER_CREWMEMBER_9368 = 9368; + public static final int TRADER_CREWMEMBER_9369 = 9369; + public static final int TRADER_CREWMEMBER_9370 = 9370; + public static final int TRADER_CREWMEMBER_9371 = 9371; + public static final int TRADER_CREWMEMBER_9372 = 9372; + public static final int TRADER_CREWMEMBER_9373 = 9373; + public static final int TRADER_CREWMEMBER_9374 = 9374; + public static final int TRADER_CREWMEMBER_9375 = 9375; + public static final int TRADER_CREWMEMBER_9376 = 9376; + public static final int TRADER_CREWMEMBER_9377 = 9377; + public static final int TRADER_CREWMEMBER_9378 = 9378; + public static final int TRADER_CREWMEMBER_9379 = 9379; + public static final int TRADER_CREWMEMBER_9380 = 9380; + public static final int TRADER_CREWMEMBER_9381 = 9381; + public static final int TRADER_CREWMEMBER_9382 = 9382; + public static final int TRADER_CREWMEMBER_9383 = 9383; /* This file is automatically generated. Do not edit. */ } From a1ec2d8e20cc8e6c3c52fde2f0a33a97b22aeb14 Mon Sep 17 00:00:00 2001 From: Blackberry0Pie Date: Mon, 23 Dec 2019 18:40:42 -0500 Subject: [PATCH 27/64] slayer plugin: add Forthos Dungeon This adds icons for temple spiders and undead druids, and the Forthos Dungeon slayer area --- .../src/main/java/net/runelite/client/plugins/slayer/Task.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java index 161023b8fa..1667c71188 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java @@ -158,11 +158,13 @@ enum Task STEEL_DRAGONS("Steel dragons", ItemID.STEEL_DRAGON), SULPHUR_LIZARDS("Sulphur Lizards", ItemID.SULPHUR_LIZARD), SUQAHS("Suqahs", ItemID.SUQAH_TOOTH), + TEMPLE_SPIDERS("Temple Spiders", ItemID.RED_SPIDERS_EGGS), TERROR_DOGS("Terror dogs", ItemID.TERROR_DOG), THERMONUCLEAR_SMOKE_DEVIL("Thermonuclear Smoke Devil", ItemID.PET_SMOKE_DEVIL), TROLLS("Trolls", ItemID.TROLL_GUARD), TUROTH("Turoth", ItemID.TUROTH), TZHAAR("Tzhaar", ItemID.ENSOULED_TZHAAR_HEAD), + UNDEAD_DRUIDS("Undead Druids", ItemID.MASK_OF_RANUL), VAMPYRES("Vampyres", ItemID.STAKE, "Vyrewatch", "Vampire"), VENENATIS("Venenatis", ItemID.VENENATIS_SPIDERLING), VETION("Vet'ion", ItemID.VETION_JR), @@ -192,6 +194,7 @@ enum Task "Death Plateau", "Evil Chicken's Lair", "Fossil Island", + "Forthos Dungeon", "Fremennik Slayer Dungeon", "God Wars Dungeon", "Iorwerth Dungeon", From 6d9aaaf8385ba34ccc6697ac9b2d8009c5d4c9e2 Mon Sep 17 00:00:00 2001 From: Zachary Waller Date: Mon, 23 Dec 2019 15:46:11 -0800 Subject: [PATCH 28/64] item identification: add potions --- .../ItemIdentification.java | 75 ++++++++++++++++++- .../ItemIdentificationConfig.java | 10 +++ .../ItemIdentificationOverlay.java | 6 ++ 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java index 6e8fc3a409..ad62f6f8e6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java @@ -120,7 +120,77 @@ enum ItemIdentification RED_TOPAZ(Type.GEM, "Topaz", "T", ItemID.UNCUT_RED_TOPAZ, ItemID.RED_TOPAZ), DRAGONSTONE(Type.GEM, "Dragon", "DR", ItemID.UNCUT_DRAGONSTONE, ItemID.DRAGONSTONE), ONYX(Type.GEM, "Onyx", "ON", ItemID.UNCUT_ONYX, ItemID.ONYX), - ZENYTE(Type.GEM, "Zenyte", "Z", ItemID.UNCUT_ZENYTE, ItemID.ZENYTE); + ZENYTE(Type.GEM, "Zenyte", "Z", ItemID.UNCUT_ZENYTE, ItemID.ZENYTE), + + // Potions + ATTACK(Type.POTION, "Att", "A", ItemID.ATTACK_POTION4, ItemID.ATTACK_POTION3, ItemID.ATTACK_POTION2, ItemID.ATTACK_POTION1), + STRENGTH(Type.POTION, "Str", "S", ItemID.STRENGTH_POTION4, ItemID.STRENGTH_POTION3, ItemID.STRENGTH_POTION2, ItemID.STRENGTH_POTION1), + DEFENCE(Type.POTION, "Def", "D", ItemID.DEFENCE_POTION4, ItemID.DEFENCE_POTION3, ItemID.DEFENCE_POTION2, ItemID.DEFENCE_POTION1), + COMBAT(Type.POTION, "Com", "D", ItemID.COMBAT_POTION4, ItemID.COMBAT_POTION3, ItemID.COMBAT_POTION2, ItemID.COMBAT_POTION1), + MAGIC(Type.POTION, "Magic", "M", ItemID.MAGIC_POTION4, ItemID.MAGIC_POTION3, ItemID.MAGIC_POTION2, ItemID.MAGIC_POTION1), + RANGING(Type.POTION, "Range", "R", ItemID.RANGING_POTION4, ItemID.RANGING_POTION3, ItemID.RANGING_POTION2, ItemID.RANGING_POTION1), + BASTION(Type.POTION, "Bastion", "B", ItemID.BASTION_POTION4, ItemID.BASTION_POTION3, ItemID.BASTION_POTION2, ItemID.BASTION_POTION1), + BATTLEMAGE(Type.POTION, "BatMage", "B.M", ItemID.BATTLEMAGE_POTION4, ItemID.BATTLEMAGE_POTION3, ItemID.BATTLEMAGE_POTION2, ItemID.BATTLEMAGE_POTION1), + + SUPER_ATTACK(Type.POTION, "S.Att", "S.A", ItemID.SUPER_ATTACK4, ItemID.SUPER_ATTACK3, ItemID.SUPER_ATTACK2, ItemID.SUPER_ATTACK1), + SUPER_STRENGTH(Type.POTION, "S.Str", "S.S", ItemID.SUPER_STRENGTH4, ItemID.SUPER_STRENGTH3, ItemID.SUPER_STRENGTH2, ItemID.SUPER_STRENGTH1), + SUPER_DEFENCE(Type.POTION, "S.Def", "S.D", ItemID.SUPER_DEFENCE4, ItemID.SUPER_DEFENCE3, ItemID.SUPER_DEFENCE2, ItemID.SUPER_DEFENCE1), + SUPER_COMBAT(Type.POTION, "S.Com", "S.C", ItemID.SUPER_COMBAT_POTION4, ItemID.SUPER_COMBAT_POTION3, ItemID.SUPER_COMBAT_POTION2, ItemID.SUPER_COMBAT_POTION1), + SUPER_RANGING(Type.POTION, "S.Range", "S.Ra", ItemID.SUPER_RANGING_4, ItemID.SUPER_RANGING_3, ItemID.SUPER_RANGING_2, ItemID.SUPER_RANGING_1), + SUPER_MAGIC(Type.POTION, "S.Magic", "S.M", ItemID.SUPER_MAGIC_POTION_4, ItemID.SUPER_MAGIC_POTION_3, ItemID.SUPER_MAGIC_POTION_2, ItemID.SUPER_MAGIC_POTION_1), + + DIVINE_SUPER_ATTACK(Type.POTION, "S.Att", "S.A", ItemID.DIVINE_SUPER_ATTACK_POTION4, ItemID.DIVINE_SUPER_ATTACK_POTION3, ItemID.DIVINE_SUPER_ATTACK_POTION2, ItemID.DIVINE_SUPER_ATTACK_POTION1), + DIVINE_SUPER_DEFENCE(Type.POTION, "S.Def", "S.D", ItemID.DIVINE_SUPER_DEFENCE_POTION4, ItemID.DIVINE_SUPER_DEFENCE_POTION3, ItemID.DIVINE_SUPER_DEFENCE_POTION2, ItemID.DIVINE_SUPER_DEFENCE_POTION1), + DIVINE_SUPER_STRENGTH(Type.POTION, "S.Str", "S.S", ItemID.DIVINE_SUPER_STRENGTH_POTION4, ItemID.DIVINE_SUPER_STRENGTH_POTION3, ItemID.DIVINE_SUPER_STRENGTH_POTION2, ItemID.DIVINE_SUPER_STRENGTH_POTION1), + DIVINE_SUPER_COMBAT(Type.POTION, "S.Com", "S.C", ItemID.DIVINE_SUPER_COMBAT_POTION4, ItemID.DIVINE_SUPER_COMBAT_POTION3, ItemID.DIVINE_SUPER_COMBAT_POTION2, ItemID.DIVINE_SUPER_COMBAT_POTION1), + DIVINE_RANGING(Type.POTION, "Range", "R", ItemID.DIVINE_RANGING_POTION4, ItemID.DIVINE_RANGING_POTION3, ItemID.DIVINE_RANGING_POTION2, ItemID.DIVINE_RANGING_POTION1), + DIVINE_MAGIC(Type.POTION, "Magic", "M", ItemID.DIVINE_MAGIC_POTION4, ItemID.DIVINE_MAGIC_POTION3, ItemID.DIVINE_MAGIC_POTION2, ItemID.DIVINE_MAGIC_POTION1), + + RESTORE(Type.POTION, "Restore", "Re", ItemID.RESTORE_POTION4, ItemID.RESTORE_POTION3, ItemID.RESTORE_POTION2, ItemID.RESTORE_POTION1), + SUPER_RESTORE(Type.POTION, "S.Rest", "S.Re", ItemID.SUPER_RESTORE4, ItemID.SUPER_RESTORE3, ItemID.SUPER_RESTORE2, ItemID.SUPER_RESTORE1), + PRAYER(Type.POTION, "Prayer", "P", ItemID.PRAYER_POTION4, ItemID.PRAYER_POTION3, ItemID.PRAYER_POTION2, ItemID.PRAYER_POTION1), + ENERGY(Type.POTION, "Energy", "En", ItemID.ENERGY_POTION4, ItemID.ENERGY_POTION3, ItemID.ENERGY_POTION2, ItemID.ENERGY_POTION1), + SUPER_ENERGY(Type.POTION, "S.Energ", "S.En", ItemID.SUPER_ENERGY4, ItemID.SUPER_ENERGY3, ItemID.SUPER_ENERGY2, ItemID.SUPER_ENERGY1), + STAMINA(Type.POTION, "Stamina", "St", ItemID.STAMINA_POTION4, ItemID.STAMINA_POTION3, ItemID.STAMINA_POTION2, ItemID.STAMINA_POTION1), + OVERLOAD(Type.POTION, "Overloa", "OL", ItemID.OVERLOAD_4, ItemID.OVERLOAD_3, ItemID.OVERLOAD_2, ItemID.OVERLOAD_1), + ABSORPTION(Type.POTION, "Absorb", "Ab", ItemID.ABSORPTION_4, ItemID.ABSORPTION_3, ItemID.ABSORPTION_2, ItemID.ABSORPTION_1), + + ZAMORAK_BREW(Type.POTION, "ZammyBr", "Za", ItemID.ZAMORAK_BREW4, ItemID.ZAMORAK_BREW3, ItemID.ZAMORAK_BREW2, ItemID.ZAMORAK_BREW1), + SARADOMIN_BREW(Type.POTION, "SaraBr", "Sa", ItemID.SARADOMIN_BREW4, ItemID.SARADOMIN_BREW3, ItemID.SARADOMIN_BREW2, ItemID.SARADOMIN_BREW1), + + ANTIPOISON(Type.POTION, "AntiP", "AP", ItemID.ANTIPOISON4, ItemID.ANTIPOISON3, ItemID.ANTIPOISON2, ItemID.ANTIPOISON1), + SUPERANTIPOISON(Type.POTION, "S.AntiP", "S.AP", ItemID.SUPERANTIPOISON4, ItemID.SUPERANTIPOISON3, ItemID.SUPERANTIPOISON2, ItemID.SUPERANTIPOISON1), + ANTIDOTE_P(Type.POTION, "Antid+", "A+", ItemID.ANTIDOTE4, ItemID.ANTIDOTE3, ItemID.ANTIDOTE2, ItemID.ANTIDOTE1), + ANTIDOTE_PP(Type.POTION, "Antid++", "A++", ItemID.ANTIDOTE4_5952, ItemID.ANTIDOTE3_5954, ItemID.ANTIDOTE2_5956, ItemID.ANTIDOTE1_5958), + ANTIVENOM(Type.POTION, "Anti-V", "AV", ItemID.ANTIVENOM4, ItemID.ANTIVENOM3, ItemID.ANTIVENOM2, ItemID.ANTIVENOM1), + ANTIVENOM_P(Type.POTION, "Anti-V+", "AV+", ItemID.ANTIVENOM4_12913, ItemID.ANTIVENOM3_12915, ItemID.ANTIVENOM2_12917, ItemID.ANTIVENOM1_12919), + + RELICYMS_BALM(Type.POTION, "Relicym", "R.B", ItemID.RELICYMS_BALM4, ItemID.RELICYMS_BALM3, ItemID.RELICYMS_BALM2, ItemID.RELICYMS_BALM1), + SANFEW_SERUM(Type.POTION, "Sanfew", "Sf", ItemID.SANFEW_SERUM4, ItemID.SANFEW_SERUM3, ItemID.SANFEW_SERUM2, ItemID.SANFEW_SERUM1), + ANTIFIRE(Type.POTION, "Antif", "Af", ItemID.ANTIFIRE_POTION4, ItemID.ANTIFIRE_POTION3, ItemID.ANTIFIRE_POTION2, ItemID.ANTIFIRE_POTION1), + EXTENDED_ANTIFIRE(Type.POTION, "E.Antif", "E.Af", ItemID.EXTENDED_ANTIFIRE4, ItemID.EXTENDED_ANTIFIRE3, ItemID.EXTENDED_ANTIFIRE2, ItemID.EXTENDED_ANTIFIRE1), + SUPER_ANTIFIRE(Type.POTION, "S.Antif", "S.Af", ItemID.SUPER_ANTIFIRE_POTION4, ItemID.SUPER_ANTIFIRE_POTION3, ItemID.SUPER_ANTIFIRE_POTION2, ItemID.SUPER_ANTIFIRE_POTION1), + EXTENDED_SUPER_ANTIFIRE(Type.POTION, "ES.Antif", "ES.Af", ItemID.EXTENDED_SUPER_ANTIFIRE4, ItemID.EXTENDED_SUPER_ANTIFIRE3, ItemID.EXTENDED_SUPER_ANTIFIRE2, ItemID.EXTENDED_SUPER_ANTIFIRE1), + + SERUM_207(Type.POTION, "Ser207", "S7", ItemID.SERUM_207_4, ItemID.SERUM_207_3, ItemID.SERUM_207_2, ItemID.SERUM_207_1), + SERUM_208(Type.POTION, "Ser208", "S8", ItemID.SERUM_208_4, ItemID.SERUM_208_3, ItemID.SERUM_208_2, ItemID.SERUM_208_1), + COMPOST(Type.POTION, "Compost", "Cp", ItemID.COMPOST_POTION4, ItemID.COMPOST_POTION3, ItemID.COMPOST_POTION2, ItemID.COMPOST_POTION1), + + // Unfinished Potions + GUAM_POTION(Type.POTION, "Guam", "G", ItemID.GUAM_POTION_UNF), + MARRENTILL_POTION(Type.POTION, "Marren", "M", ItemID.MARRENTILL_POTION_UNF), + TARROMIN_POTION(Type.POTION, "Tarro", "TAR", ItemID.TARROMIN_POTION_UNF), + HARRALANDER_POTION(Type.POTION, "Harra", "H", ItemID.HARRALANDER_POTION_UNF), + RANARR_POTION(Type.POTION, "Ranarr", "R", ItemID.RANARR_POTION_UNF), + TOADFLAX_POTION(Type.POTION, "Toad", "TOA", ItemID.TOADFLAX_POTION_UNF), + IRIT_POTION(Type.POTION, "Irit", "I", ItemID.IRIT_POTION_UNF), + AVANTOE_POTION(Type.POTION, "Avan", "A", ItemID.AVANTOE_POTION_UNF), + KWUARM_POTION(Type.POTION, "Kwuarm", "K", ItemID.KWUARM_POTION_UNF), + SNAPDRAGON_POTION(Type.POTION, "Snap", "S", ItemID.SNAPDRAGON_POTION_UNF), + CADANTINE_POTION(Type.POTION, "Cadan", "C", ItemID.CADANTINE_POTION_UNF), + LANTADYME_POTION(Type.POTION, "Lanta", "L", ItemID.LANTADYME_POTION_UNF), + DWARF_WEED_POTION(Type.POTION, "Dwarf", "D", ItemID.DWARF_WEED_POTION_UNF), + TORSTOL_POTION(Type.POTION, "Torstol", "TOR", ItemID.TORSTOL_POTION_UNF); final Type type; final String medName; @@ -163,6 +233,7 @@ enum ItemIdentification HERB, SAPLING, ORE, - GEM + GEM, + POTION } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentificationConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentificationConfig.java index f8e524b35a..ca05bfdcba 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentificationConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentificationConfig.java @@ -103,4 +103,14 @@ public interface ItemIdentificationConfig extends Config { return false; } + + @ConfigItem( + keyName = "showPotions", + name = "Potions", + description = "Show identification on Potions" + ) + default boolean showPotions() + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentificationOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentificationOverlay.java index 341cd1c306..b09ff5ab89 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentificationOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentificationOverlay.java @@ -95,6 +95,12 @@ class ItemIdentificationOverlay extends WidgetItemOverlay return; } break; + case POTION: + if (!config.showPotions()) + { + return; + } + break; } graphics.setFont(FontManager.getRunescapeSmallFont()); From f3b8bace727ffd8ba2f1cc6898496e30d981689a Mon Sep 17 00:00:00 2001 From: Rami <44623786+Rami-J@users.noreply.github.com> Date: Mon, 23 Dec 2019 19:34:20 -0500 Subject: [PATCH 29/64] hiscore plugin: add Lookup to ignore list --- .../runelite/client/plugins/hiscore/HiscorePlugin.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java index dbf1eb1e75..d2320484db 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePlugin.java @@ -66,7 +66,7 @@ public class HiscorePlugin extends Plugin { private static final String LOOKUP = "Lookup"; private static final String KICK_OPTION = "Kick"; - private static final ImmutableList AFTER_OPTIONS = ImmutableList.of("Message", "Add ignore", "Remove friend", KICK_OPTION); + private static final ImmutableList AFTER_OPTIONS = ImmutableList.of("Message", "Add ignore", "Remove friend", "Delete", KICK_OPTION); private static final Pattern BOUNTY_PATTERN = Pattern.compile("You've been assigned a target: (.*)"); @Inject @@ -177,11 +177,10 @@ public class HiscorePlugin extends Plugin if (groupId == WidgetInfo.FRIENDS_LIST.getGroupId() || groupId == WidgetInfo.CLAN_CHAT.getGroupId() || groupId == WidgetInfo.CHATBOX.getGroupId() && !KICK_OPTION.equals(option) || //prevent from adding for Kick option (interferes with the raiding party one) - groupId == WidgetInfo.RAIDING_PARTY.getGroupId() || groupId == WidgetInfo.PRIVATE_CHAT_MESSAGE.getGroupId()) + groupId == WidgetInfo.RAIDING_PARTY.getGroupId() || groupId == WidgetInfo.PRIVATE_CHAT_MESSAGE.getGroupId() || + groupId == WidgetInfo.IGNORE_LIST.getGroupId()) { - boolean after; - - if (!AFTER_OPTIONS.contains(option)) + if (!AFTER_OPTIONS.contains(option) || (option.equals("Delete") && groupId != WidgetInfo.IGNORE_LIST.getGroupId())) { return; } From 408c2c5e5022d755d8b0e43fab71403ee423808f Mon Sep 17 00:00:00 2001 From: Jason O'Neill Date: Mon, 23 Dec 2019 16:45:46 -0800 Subject: [PATCH 30/64] item stats: fix White Tree Fruit stats --- .../net/runelite/client/plugins/itemstats/ItemStatChanges.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java index 5644414c70..bfafe09c25 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java @@ -185,7 +185,7 @@ public class ItemStatChanges add(combo(2, boost(HITPOINTS, 5), heal(RUN_ENERGY, 5)), GUTHIX_REST1, GUTHIX_REST2, GUTHIX_REST3, GUTHIX_REST4); // Misc/run energy - add(heal(RUN_ENERGY, 10), WHITE_TREE_FRUIT); + add(combo(food(3), range(heal(RUN_ENERGY, 5), heal(RUN_ENERGY, 10))), WHITE_TREE_FRUIT); add(heal(RUN_ENERGY, 30), STRANGE_FRUIT); add(heal(RUN_ENERGY, 50), MINT_CAKE); add(combo(food(12), heal(RUN_ENERGY, 50)), GOUT_TUBER); From 6782d888d7f3f759af5f29f4579c7da7f1d415d3 Mon Sep 17 00:00:00 2001 From: Caleb Waters Date: Mon, 23 Dec 2019 18:53:24 -0600 Subject: [PATCH 31/64] skill calc: change arrow shaft xp to be per shaft --- .../client/plugins/skillcalculator/skill_fletching.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_fletching.json b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_fletching.json index e64e32c28d..13c04e5a1c 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_fletching.json +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_fletching.json @@ -4,7 +4,7 @@ "level": 1, "icon": 52, "name": "Arrow Shaft", - "xp": 5 + "xp": 0.33 }, { "level": 1, From cb0d401ecf57ab322d2b9864776b917551ec7de0 Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Tue, 24 Dec 2019 09:15:14 +0000 Subject: [PATCH 32/64] kourendlibrary: clear tracked NPCs when changing worlds --- .../kourendlibrary/KourendLibraryPlugin.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java index c9770baab7..781ad13308 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java @@ -39,6 +39,7 @@ import lombok.extern.slf4j.Slf4j; import net.runelite.api.AnimationID; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; +import net.runelite.api.GameState; import net.runelite.api.InventoryID; import net.runelite.api.Item; import net.runelite.api.ItemContainer; @@ -49,6 +50,7 @@ import net.runelite.api.Player; import net.runelite.api.coords.WorldPoint; import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.NpcDespawned; import net.runelite.api.events.NpcSpawned; import net.runelite.client.events.ConfigChanged; @@ -159,6 +161,7 @@ public class KourendLibraryPlugin extends Plugin lastBookcaseClick = null; lastBookcaseAnimatedOn = null; playerBooks = null; + npcsToMark.clear(); } @Subscribe @@ -228,6 +231,16 @@ public class KourendLibraryPlugin extends Plugin } } + @Subscribe + public void onGameStateChanged(GameStateChanged event) + { + if (event.getGameState() == GameState.LOGIN_SCREEN || + event.getGameState() == GameState.HOPPING) + { + npcsToMark.clear(); + } + } + @Subscribe public void onGameTick(GameTick tick) { From cb131029ec91a7da22b0b00d6ceaf7536670f6dd Mon Sep 17 00:00:00 2001 From: Alexsuperfly Date: Mon, 16 Dec 2019 14:55:31 -0500 Subject: [PATCH 33/64] woodcutting: account for plane change with despawn events the server only sends a despawn event when the actual object does despawn or when you change to that objects level and the server sends it to sync you with that planes objects. By setting the current player plane on gametick and checking that against the despawned objects plane, and only processing events where they match we can gurantee that the actual object has just despawned instead of the server sending the event just to resync the player with the current status of the location they just entered --- .../client/plugins/woodcutting/WoodcuttingPlugin.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java index 1078381bf4..b1d6544899 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java @@ -103,6 +103,7 @@ public class WoodcuttingPlugin extends Plugin @Getter(AccessLevel.PACKAGE) private final List respawns = new ArrayList<>(); private boolean recentlyLoggedIn; + private int currentPlane; @Provides WoodcuttingConfig getConfig(ConfigManager configManager) @@ -144,6 +145,7 @@ public class WoodcuttingPlugin extends Plugin public void onGameTick(GameTick gameTick) { recentlyLoggedIn = false; + currentPlane = client.getPlane(); respawns.removeIf(TreeRespawn::isExpired); @@ -204,7 +206,7 @@ public class WoodcuttingPlugin extends Plugin Tree tree = Tree.findTree(object.getId()); if (tree != null) { - if (tree.getRespawnTime() != null && !recentlyLoggedIn) + if (tree.getRespawnTime() != null && !recentlyLoggedIn && currentPlane == object.getPlane()) { Point max = object.getSceneMaxLocation(); Point min = object.getSceneMinLocation(); From 7810d5cd6da660d10c45512856a5a2116d13ba86 Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Tue, 24 Dec 2019 15:42:35 +0000 Subject: [PATCH 34/64] chat message manager: make sender recoloring use messageNode --- .../main/java/net/runelite/client/chat/ChatMessageManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java b/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java index ec4fc4aa91..34853d6f04 100644 --- a/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java +++ b/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java @@ -150,7 +150,7 @@ public class ChatMessageManager messageNode.setName(ColorUtil.wrapWithColorTag(messageNode.getName(), usernameColor)); } - String sender = chatMessage.getSender(); + String sender = messageNode.getSender(); if (senderColor != null && !Strings.isNullOrEmpty(sender)) { messageNode.setSender(ColorUtil.wrapWithColorTag(sender, senderColor)); From 7bb91ba88305ad3fb185f0fe6854df068b4bc697 Mon Sep 17 00:00:00 2001 From: Alexsuperfly Date: Tue, 24 Dec 2019 11:20:33 -0500 Subject: [PATCH 35/64] raids: allow 'unknown' to evaluate to both unknown types --- .../runelite/client/plugins/raids/RaidsPlugin.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java index cdeabce0a9..81881bd28b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java @@ -421,7 +421,18 @@ public class RaidsPlugin extends Plugin private void updateList(Collection list, String input) { list.clear(); - list.addAll(Text.fromCSV(input.toLowerCase())); + for (String s : Text.fromCSV(input.toLowerCase())) + { + if (s.equals("unknown")) + { + list.add("unknown (combat)"); + list.add("unknown (puzzle)"); + } + else + { + list.add(s); + } + } } boolean getRotationMatches() From 40aedfb10e39106f61335189c2669a66eb1ada3f Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Thu, 26 Dec 2019 01:28:08 +0000 Subject: [PATCH 36/64] kourendlibrary: only reset if layout changes when complete --- .../client/plugins/kourendlibrary/Library.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java index 65a7e363bb..214c780ea1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java @@ -131,18 +131,25 @@ class Library else if (state != SolvedState.NO_DATA) { // Reset if the book we found isn't what we expected - // Reset if we found nothing when we expected something that wasn't a Dark Manuscript, since the layout has changed - if ((book != null && !bookcase.getPossibleBooks().contains(book)) || - (book == null && !bookcase.getPossibleBooks().isEmpty() && bookcase.getPossibleBooks().stream().noneMatch(Book::isDarkManuscript))) + + if (book != null && !bookcase.getPossibleBooks().contains(book)) { reset(); } } - // Everything is known, nothing to do if (state == SolvedState.COMPLETE) { - return; + // Reset if we found nothing when we expected something that wasn't a Dark Manuscript, since the layout has changed + if (book == null && !bookcase.getPossibleBooks().isEmpty() && bookcase.getPossibleBooks().stream().noneMatch(Book::isDarkManuscript)) + { + reset(); + } + else + { + // Everything is known, nothing to do + return; + } } log.info("Setting bookcase {} to {}", bookcase.getIndex(), book); From c6d9b20ecb432de38fa2b9535561031cfd969f45 Mon Sep 17 00:00:00 2001 From: dekvall Date: Thu, 26 Dec 2019 07:32:23 +0100 Subject: [PATCH 37/64] xp tracker: don't unpause on failed login If the wrong password is entered, the xp tracker unpauses for a brief moment since the GameState is no longer LOGIN_SCREEN. --- .../client/plugins/xptracker/XpTrackerPlugin.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java index d4dd39696c..40cc7cd66e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java @@ -670,7 +670,19 @@ public class XpTrackerPlugin extends Plugin xpPauseState.tickXp(skill, skillExperience, xpTrackerConfig.pauseSkillAfter()); } - xpPauseState.tickLogout(xpTrackerConfig.pauseOnLogout(), !GameState.LOGIN_SCREEN.equals(client.getGameState())); + final boolean loggedIn; + switch (client.getGameState()) + { + case LOGIN_SCREEN: + case LOGGING_IN: + case LOGIN_SCREEN_AUTHENTICATOR: + loggedIn = false; + break; + default: + loggedIn = true; + break; + } + xpPauseState.tickLogout(xpTrackerConfig.pauseOnLogout(), loggedIn); if (lastTickMillis == 0) { From e2815526cc03f4367b8f1be5a99f4f8e3ecd249f Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Fri, 27 Dec 2019 22:33:52 +0000 Subject: [PATCH 38/64] kourendlibrary: fix panel reloading not being done on the swing thread --- .../kourendlibrary/KourendLibraryPlugin.java | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java index c9770baab7..a0ec3bc410 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java @@ -171,29 +171,31 @@ public class KourendLibraryPlugin extends Plugin if (ev.getKey().equals("hideVarlamoreEnvoy")) { - panel.reload(); + SwingUtilities.invokeLater(panel::reload); } - - SwingUtilities.invokeLater(() -> + else if (ev.getKey().equals("hideButton")) { - if (!config.hideButton()) + SwingUtilities.invokeLater(() -> { - clientToolbar.addNavigation(navButton); - } - else - { - Player lp = client.getLocalPlayer(); - boolean inRegion = lp != null && lp.getWorldLocation().getRegionID() == REGION; - if (inRegion) + if (!config.hideButton()) { clientToolbar.addNavigation(navButton); } else { - clientToolbar.removeNavigation(navButton); + Player lp = client.getLocalPlayer(); + boolean inRegion = lp != null && lp.getWorldLocation().getRegionID() == REGION; + if (inRegion) + { + clientToolbar.addNavigation(navButton); + } + else + { + clientToolbar.removeNavigation(navButton); + } } - } - }); + }); + } } @Subscribe From 9cc321e14382a2e0d45fae811ff8e1d92bfb8d80 Mon Sep 17 00:00:00 2001 From: Rami-J Date: Sun, 15 Dec 2019 20:29:04 -0500 Subject: [PATCH 39/64] menu swapper: add support for ge offer withdraw Co-authored-by: Adam --- .../menuentryswapper/GEItemCollectMode.java | 46 +++++++++++++++++++ .../MenuEntrySwapperConfig.java | 10 ++++ .../MenuEntrySwapperPlugin.java | 21 ++++++++- 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/GEItemCollectMode.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/GEItemCollectMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/GEItemCollectMode.java new file mode 100644 index 0000000000..e15277e320 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/GEItemCollectMode.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019, Rami + * 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.menuentryswapper; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum GEItemCollectMode +{ + DEFAULT("Default"), + ITEMS("Collect-items"), + NOTES("Collect-notes"), + BANK("Bank"); + + private final String name; + + @Override + public String toString() + { + return name; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java index 6d14f0188d..cbd75154c5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java @@ -361,4 +361,14 @@ public interface MenuEntrySwapperConfig extends Config { return false; } + + @ConfigItem( + keyName = "swapGEItemCollect", + name = "GE Item Collect", + description = "Swap Collect-notes, Collect-items, or Bank options from GE offer" + ) + default GEItemCollectMode swapGEItemCollect() + { + return GEItemCollectMode.DEFAULT; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java index 28802618d0..ccc61e2049 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2018, Adam * Copyright (c) 2018, Kamiel + * Copyright (c) 2019, Rami * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,7 +41,6 @@ import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; import net.runelite.api.NPC; import net.runelite.api.events.ClientTick; -import net.runelite.client.events.ConfigChanged; import net.runelite.api.events.FocusChanged; import net.runelite.api.events.MenuOpened; import net.runelite.api.events.MenuOptionClicked; @@ -50,6 +50,7 @@ import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemVariationMapping; import net.runelite.client.input.KeyManager; @@ -647,6 +648,24 @@ public class MenuEntrySwapperPlugin extends Plugin { swap("use", option, target, index); } + else if (option.equals("collect to inventory") || option.startsWith("collect-note") || option.startsWith("collect-item")) + { + switch (config.swapGEItemCollect()) + { + case ITEMS: + swap("collect-items", option, target, index); + swap("collect-item", option, target, index); + break; + case NOTES: + swap("collect-notes", option, target, index); + swap("collect-note", option, target, index); + break; + case BANK: + swap("collect to bank", option, target, index); + swap("bank", option, target, index); + break; + } + } if (shiftModifier && config.swapTeleportSpell()) { From 39d87a553d867aa4c2dbf395957eaf247c2d7581 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 29 Dec 2019 11:45:04 -0500 Subject: [PATCH 40/64] cannon plugin: make ammo notification threshold configurable Co-authored-by: Lucas Childers Co-authored-by: redrumyliad --- .../client/plugins/cannon/CannonConfig.java | 35 +++++++++++++++---- .../client/plugins/cannon/CannonPlugin.java | 14 ++++++-- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonConfig.java index 29a1496f85..05ba93da2e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonConfig.java @@ -29,24 +29,42 @@ import net.runelite.client.config.Alpha; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Range; +import static net.runelite.client.plugins.cannon.CannonPlugin.MAX_CBALLS; @ConfigGroup("cannon") public interface CannonConfig extends Config { @ConfigItem( keyName = "showEmptyCannonNotification", - name = "Empty cannon notification", - description = "Configures whether to notify you that the cannon is empty" + name = "Enable cannon notifications", + description = "Configures whether to notify you when your cannon is low on cannonballs", + position = 1 ) - default boolean showEmptyCannonNotification() + default boolean showCannonNotifications() { return true; } + @Range( + max = MAX_CBALLS + ) + @ConfigItem( + keyName = "lowWarningThreshold", + name = "Low Warning Threshold", + description = "Configures the number of cannonballs remaining before a notification is sent.
Regardless of this value, a notification will still be sent when your cannon is empty.", + position = 2 + ) + default int lowWarningThreshold() + { + return 0; + } + @ConfigItem( keyName = "showInfobox", name = "Show Cannonball infobox", - description = "Configures whether to show the cannonballs in an infobox" + description = "Configures whether to show the cannonballs in an infobox", + position = 3 ) default boolean showInfobox() { @@ -56,7 +74,8 @@ public interface CannonConfig extends Config @ConfigItem( keyName = "showDoubleHitSpot", name = "Show double hit spots", - description = "Configures whether to show the NPC double hit spot" + description = "Configures whether to show the NPC double hit spot", + position = 4 ) default boolean showDoubleHitSpot() { @@ -67,7 +86,8 @@ public interface CannonConfig extends Config @ConfigItem( keyName = "highlightDoubleHitColor", name = "Color of double hit spots", - description = "Configures the highlight color of double hit spots" + description = "Configures the highlight color of double hit spots", + position = 5 ) default Color highlightDoubleHitColor() { @@ -77,7 +97,8 @@ public interface CannonConfig extends Config @ConfigItem( keyName = "showCannonSpots", name = "Show common cannon spots", - description = "Configures whether to show common cannon spots or not" + description = "Configures whether to show common cannon spots or not", + position = 6 ) default boolean showCannonSpots() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java index 6462b89e7f..39bcc3b01c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java @@ -71,10 +71,11 @@ import net.runelite.client.ui.overlay.infobox.InfoBoxManager; public class CannonPlugin extends Plugin { private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)"); - private static final int MAX_CBALLS = 30; + static final int MAX_CBALLS = 30; private CannonCounter counter; private boolean skipProjectileCheckThisTick; + private boolean cannonBallNotificationSent; @Getter private int cballsLeft; @@ -139,6 +140,7 @@ public class CannonPlugin extends Plugin overlayManager.remove(cannonSpotOverlay); cannonPlaced = false; cannonPosition = null; + cannonBallNotificationSent = false; cballsLeft = 0; removeCounter(); skipProjectileCheckThisTick = false; @@ -275,6 +277,12 @@ public class CannonPlugin extends Plugin if (!skipProjectileCheckThisTick) { cballsLeft--; + + if (config.showCannonNotifications() && !cannonBallNotificationSent && cballsLeft > 0 && config.lowWarningThreshold() >= cballsLeft) + { + notifier.notify(String.format("Your cannon has %d cannon balls remaining!", cballsLeft)); + cannonBallNotificationSent = true; + } } } } @@ -338,6 +346,8 @@ public class CannonPlugin extends Plugin cballsLeft++; } } + + cannonBallNotificationSent = false; } if (event.getMessage().contains("Your cannon is out of ammo!")) @@ -349,7 +359,7 @@ public class CannonPlugin extends Plugin // extra check is a good idea. cballsLeft = 0; - if (config.showEmptyCannonNotification()) + if (config.showCannonNotifications()) { notifier.notify("Your cannon is out of ammo!"); } From 5ad94eae2e1fcdad9e2d3f63795ef882251aec66 Mon Sep 17 00:00:00 2001 From: dekvall Date: Thu, 26 Dec 2019 07:08:22 +0100 Subject: [PATCH 41/64] chatcommands: support keybinds in pms --- .../chatcommands/ChatKeyboardListener.java | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatKeyboardListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatKeyboardListener.java index 8472030fcb..991d4a644f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatKeyboardListener.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatKeyboardListener.java @@ -29,7 +29,9 @@ import javax.inject.Inject; import javax.inject.Singleton; import net.runelite.api.Client; import net.runelite.api.ScriptID; +import net.runelite.api.VarClientInt; import net.runelite.api.VarClientStr; +import net.runelite.api.vars.InputType; import net.runelite.client.callback.ClientThread; import net.runelite.client.input.KeyListener; @@ -56,7 +58,11 @@ public class ChatKeyboardListener implements KeyListener { if (chatCommandsConfig.clearSingleWord().matches(e)) { - String input = client.getVar(VarClientStr.CHATBOX_TYPED_TEXT); + int inputTye = client.getVar(VarClientInt.INPUT_TYPE); + String input = inputTye == InputType.NONE.getType() + ? client.getVar(VarClientStr.CHATBOX_TYPED_TEXT) + : client.getVar(VarClientStr.INPUT_TEXT); + if (input != null) { // remove trailing space @@ -77,20 +83,27 @@ public class ChatKeyboardListener implements KeyListener replacement = ""; } - clientThread.invoke(() -> - { - client.setVar(VarClientStr.CHATBOX_TYPED_TEXT, replacement); - client.runScript(ScriptID.CHAT_PROMPT_INIT); - }); + clientThread.invoke(() -> applyText(inputTye, replacement)); } } else if (chatCommandsConfig.clearChatBox().matches(e)) { - clientThread.invoke(() -> - { - client.setVar(VarClientStr.CHATBOX_TYPED_TEXT, ""); - client.runScript(ScriptID.CHAT_PROMPT_INIT); - }); + int inputTye = client.getVar(VarClientInt.INPUT_TYPE); + clientThread.invoke(() -> applyText(inputTye, "")); + } + } + + private void applyText(int inputType, String replacement) + { + if (inputType == InputType.NONE.getType()) + { + client.setVar(VarClientStr.CHATBOX_TYPED_TEXT, replacement); + client.runScript(ScriptID.CHAT_PROMPT_INIT); + } + else if (inputType == InputType.PRIVATE_MESSAGE.getType()) + { + client.setVar(VarClientStr.INPUT_TEXT, replacement); + client.runScript(ScriptID.CHAT_TEXT_INPUT_REBUILD, ""); } } From 9f86c9f128066f0a34616f729f91dfc16238644b Mon Sep 17 00:00:00 2001 From: Alexsuperfly Date: Sun, 29 Dec 2019 14:06:51 -0500 Subject: [PATCH 42/64] woodcutting: create maple timer for miscellania region --- .../runelite/client/plugins/woodcutting/Tree.java | 15 ++++++++++++++- .../plugins/woodcutting/WoodcuttingPlugin.java | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/Tree.java b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/Tree.java index 6f11a1eb4a..94105988b5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/Tree.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/Tree.java @@ -60,7 +60,14 @@ enum Tree REGULAR_TREE(null, TREE, TREE_1277, TREE_1278, TREE_1279, TREE_1280), OAK_TREE(Duration.ofMillis(8500), ObjectID.OAK_TREE, OAK_TREE_4540, OAK_10820), WILLOW_TREE(Duration.ofMillis(8500), WILLOW, WILLOW_10829, WILLOW_10831, WILLOW_10833), - MAPLE_TREE(Duration.ofSeconds(35), ObjectID.MAPLE_TREE, MAPLE_TREE_10832, MAPLE_TREE_36681), + MAPLE_TREE(Duration.ofSeconds(35), ObjectID.MAPLE_TREE, MAPLE_TREE_10832, MAPLE_TREE_36681) + { + @Override + Duration getRespawnTime(int region) + { + return region == MISCELLANIA_REGION ? Duration.ofMillis(8500) : super.respawnTime; + } + }, TEAK_TREE(Duration.ofMillis(8500), TEAK, TEAK_36686), MAHOGANY_TREE(Duration.ofMillis(8500), MAHOGANY, MAHOGANY_36688), YEW_TREE(Duration.ofMinutes(1), YEW, NULL_10823, YEW_36683), @@ -77,6 +84,7 @@ enum Tree this.treeIds = treeIds; } + private static final int MISCELLANIA_REGION = 10044; private static final Map TREES; static @@ -94,6 +102,11 @@ enum Tree TREES = builder.build(); } + Duration getRespawnTime(int region) + { + return respawnTime; + } + static Tree findTree(int objectId) { return TREES.get(objectId); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java index b1d6544899..8b42ed7d6c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java @@ -213,7 +213,9 @@ public class WoodcuttingPlugin extends Plugin int lenX = max.getX() - min.getX(); int lenY = max.getY() - min.getY(); log.debug("Adding respawn timer for {} tree at {}", tree, object.getLocalLocation()); - TreeRespawn treeRespawn = new TreeRespawn(tree, lenX, lenY, WorldPoint.fromScene(client, min.getX(), min.getY(), client.getPlane()), Instant.now(), (int) tree.getRespawnTime().toMillis()); + + final int region = client.getLocalPlayer().getWorldLocation().getRegionID(); + TreeRespawn treeRespawn = new TreeRespawn(tree, lenX, lenY, WorldPoint.fromScene(client, min.getX(), min.getY(), client.getPlane()), Instant.now(), (int) tree.getRespawnTime(region).toMillis()); respawns.add(treeRespawn); } From 61a72d5a54798b606e12bb76deaa9ef2c12fb981 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 29 Dec 2019 14:36:56 -0500 Subject: [PATCH 43/64] menu swapper: add shift click abort ge offer Co-authored-by: Adelaidian --- .../menuentryswapper/MenuEntrySwapperConfig.java | 10 ++++++++++ .../menuentryswapper/MenuEntrySwapperPlugin.java | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java index cbd75154c5..b340e41bca 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java @@ -371,4 +371,14 @@ public interface MenuEntrySwapperConfig extends Config { return GEItemCollectMode.DEFAULT; } + + @ConfigItem( + keyName = "swapGEAbort", + name = "GE Abort", + description = "Swap abort offer on Grand Exchange offers when shift-clicking" + ) + default boolean swapGEAbort() + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java index ccc61e2049..865bb441b9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java @@ -622,6 +622,10 @@ public class MenuEntrySwapperPlugin extends Plugin { swap("pick-lots", option, target, index); } + else if (shiftModifier && option.equals("view offer") && config.swapGEAbort()) + { + swap("abort offer", option, target, index); + } else if (config.shiftClickCustomization() && shiftModifier && !option.equals("use")) { Integer customOption = getSwapConfig(eventId); From 618050ad071454bbff26428fce1cfa26552409c8 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 30 Dec 2019 15:44:02 -0500 Subject: [PATCH 44/64] clientloader: throw an exception when unable to peek first entry --- .../main/java/net/runelite/client/rs/ClientLoader.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java b/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java index 58993f63b7..10fa8e9fd4 100644 --- a/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java +++ b/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java @@ -279,6 +279,11 @@ public class ClientLoader implements Supplier // Its important to not close the response manually - this should be the only close or // try-with-resources on this stream or it's children + if (!response.isSuccessful()) + { + throw new IOException("unsuccessful response fetching gamepack: " + response.message()); + } + int length = (int) response.body().contentLength(); if (length < 0) { @@ -307,6 +312,11 @@ public class ClientLoader implements Supplier // Get the mtime from the first entry so check it against the cache { JarEntry je = networkJIS.getNextJarEntry(); + if (je == null) + { + throw new IOException("unable to peek first jar entry"); + } + networkJIS.skip(Long.MAX_VALUE); verifyJarEntry(je, jagexCertificateChain); long vanillaClientMTime = je.getLastModifiedTime().toMillis(); From bc6a22423721e3c4ecd30244a3e1a2395c1aa2d6 Mon Sep 17 00:00:00 2001 From: 15987632 Date: Mon, 30 Dec 2019 19:55:01 -0500 Subject: [PATCH 45/64] client: add VisibleForExternalPlugins annotation --- .../VisibleForExternalPlugins.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 runelite-api/src/main/java/net/runelite/api/annotations/VisibleForExternalPlugins.java diff --git a/runelite-api/src/main/java/net/runelite/api/annotations/VisibleForExternalPlugins.java b/runelite-api/src/main/java/net/runelite/api/annotations/VisibleForExternalPlugins.java new file mode 100644 index 0000000000..8046e92bde --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/annotations/VisibleForExternalPlugins.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019, Trevor + * 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.api.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Used to indicate a method is only visible for external plugins + */ +@Documented +@Retention(RetentionPolicy.SOURCE) +public @interface VisibleForExternalPlugins +{ +} From 1af81d40bec964245181262757222c38d23790ac Mon Sep 17 00:00:00 2001 From: 15987632 Date: Mon, 30 Dec 2019 19:55:25 -0500 Subject: [PATCH 46/64] client: add getVar methods for external plugins --- .../main/java/net/runelite/api/Client.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index ea20abdccf..063e833f89 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -32,6 +32,7 @@ import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; import net.runelite.api.annotations.VisibleForDevtools; +import net.runelite.api.annotations.VisibleForExternalPlugins; import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldPoint; import net.runelite.api.hooks.Callbacks; @@ -686,6 +687,42 @@ public interface Client extends GameEngine */ String getVar(VarClientStr varClientStr); + /** + * Gets the value of a given VarPlayer. + * + * @param varpId the VarPlayer id + * @return the value + */ + @VisibleForExternalPlugins + int getVarpValue(int varpId); + + /** + * Gets the value of a given Varbit. + * + * @param varbitId the varbit id + * @return the value + */ + @VisibleForExternalPlugins + int getVarbitValue(int varbitId); + + /** + * Gets the value of a given VarClientInt + * + * @param varcIntId the VarClientInt id + * @return the value + */ + @VisibleForExternalPlugins + int getVarcIntValue(int varcIntId); + + /** + * Gets the value of a given VarClientStr + * + * @param varcStrId the VarClientStr id + * @return the value + */ + @VisibleForExternalPlugins + String getVarcStrValue(int varcStrId); + /** * Sets a VarClientString to the passed value */ From c549114734a60759a0714b540c3283ea7fb85947 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 1 Jan 2020 09:53:41 -0500 Subject: [PATCH 47/64] cache: add param config --- .../java/net/runelite/cache/ConfigType.java | 1 + .../cache/definitions/ParamDefinition.java | 37 ++++++++ .../definitions/loaders/ParamLoader.java | 64 ++++++++++++++ .../java/net/runelite/cache/ParamDumper.java | 88 +++++++++++++++++++ 4 files changed, 190 insertions(+) create mode 100644 cache/src/main/java/net/runelite/cache/definitions/ParamDefinition.java create mode 100644 cache/src/main/java/net/runelite/cache/definitions/loaders/ParamLoader.java create mode 100644 cache/src/test/java/net/runelite/cache/ParamDumper.java diff --git a/cache/src/main/java/net/runelite/cache/ConfigType.java b/cache/src/main/java/net/runelite/cache/ConfigType.java index 48d8e229c0..13056e2e33 100644 --- a/cache/src/main/java/net/runelite/cache/ConfigType.java +++ b/cache/src/main/java/net/runelite/cache/ConfigType.java @@ -36,6 +36,7 @@ public enum ConfigType ENUM(8), NPC(9), ITEM(10), + PARAMS(11), SEQUENCE(12), SPOTANIM(13), VARBIT(14), diff --git a/cache/src/main/java/net/runelite/cache/definitions/ParamDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/ParamDefinition.java new file mode 100644 index 0000000000..c3a25a1f1e --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/ParamDefinition.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020, Adam + * 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.cache.definitions; + +import lombok.Data; +import net.runelite.cache.util.ScriptVarType; + +@Data +public class ParamDefinition +{ + private ScriptVarType type; + private boolean isMembers = true; + private int defaultInt; + private String defaultString; +} diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/ParamLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/ParamLoader.java new file mode 100644 index 0000000000..98aec2b95f --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/ParamLoader.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020, Adam + * 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.cache.definitions.loaders; + +import net.runelite.cache.definitions.ParamDefinition; +import net.runelite.cache.io.InputStream; +import net.runelite.cache.util.ScriptVarType; + +public class ParamLoader +{ + public ParamDefinition load(byte[] data) + { + ParamDefinition def = new ParamDefinition(); + InputStream b = new InputStream(data); + + for (; ; ) + { + int opcode = b.readUnsignedByte(); + + switch (opcode) + { + case 0: + return def; + case 1: + { + int idx = b.readByte(); + def.setType(ScriptVarType.forCharKey((char) idx)); + break; + } + case 2: + def.setDefaultInt(b.readInt()); + break; + case 4: + def.setMembers(false); + break; + case 5: + def.setDefaultString(b.readString()); + break; + } + } + } +} diff --git a/cache/src/test/java/net/runelite/cache/ParamDumper.java b/cache/src/test/java/net/runelite/cache/ParamDumper.java new file mode 100644 index 0000000000..60c8e8b87c --- /dev/null +++ b/cache/src/test/java/net/runelite/cache/ParamDumper.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020, Adam + * 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.cache; + +import com.google.common.io.Files; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import lombok.extern.slf4j.Slf4j; +import net.runelite.cache.definitions.ParamDefinition; +import net.runelite.cache.definitions.loaders.ParamLoader; +import net.runelite.cache.fs.Archive; +import net.runelite.cache.fs.ArchiveFiles; +import net.runelite.cache.fs.FSFile; +import net.runelite.cache.fs.Index; +import net.runelite.cache.fs.Storage; +import net.runelite.cache.fs.Store; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +@Slf4j +public class ParamDumper +{ + private Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + @Rule + public TemporaryFolder folder = StoreLocation.getTemporaryFolder(); + + @Test + @Ignore + public void test() throws IOException + { + File dumpDir = folder.newFolder(); + int count = 0; + + try (Store store = new Store(StoreLocation.LOCATION)) + { + store.load(); + + Storage storage = store.getStorage(); + Index index = store.getIndex(IndexType.CONFIGS); + Archive archive = index.getArchive(ConfigType.PARAMS.getId()); + + ParamLoader loader = new ParamLoader(); + + byte[] archiveData = storage.loadArchive(archive); + ArchiveFiles files = archive.getFiles(archiveData); + + for (FSFile file : files.getFiles()) + { + byte[] b = file.getContents(); + + ParamDefinition def = loader.load(b); + + Files.asCharSink(new File(dumpDir, file.getFileId() + ".json"), Charset.defaultCharset()).write(gson.toJson(def)); + ++count; + } + } + + log.info("Dumped {} params to {}", count, dumpDir); + } +} From 49a7c18ad66eb7c4f75e78d722a820d7ad23e916 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 3 Jan 2020 11:01:34 -0500 Subject: [PATCH 48/64] camera plugin: reposition startUp/shutdown members --- .../client/plugins/camera/CameraPlugin.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java index 044798c07a..68875487b4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java @@ -91,6 +91,26 @@ public class CameraPlugin extends Plugin implements KeyListener, MouseListener return configManager.getConfig(CameraConfig.class); } + @Override + protected void startUp() + { + rightClick = false; + middleClick = false; + menuHasEntries = false; + client.setCameraPitchRelaxerEnabled(config.relaxCameraPitch()); + keyManager.registerKeyListener(this); + mouseManager.registerMouseListener(this); + } + + @Override + protected void shutDown() + { + client.setCameraPitchRelaxerEnabled(false); + keyManager.unregisterKeyListener(this); + mouseManager.unregisterMouseListener(this); + controlDown = false; + } + @Subscribe public void onScriptCallbackEvent(ScriptCallbackEvent event) { @@ -164,26 +184,6 @@ public class CameraPlugin extends Plugin implements KeyListener, MouseListener } } - @Override - protected void startUp() - { - rightClick = false; - middleClick = false; - menuHasEntries = false; - client.setCameraPitchRelaxerEnabled(config.relaxCameraPitch()); - keyManager.registerKeyListener(this); - mouseManager.registerMouseListener(this); - } - - @Override - protected void shutDown() - { - client.setCameraPitchRelaxerEnabled(false); - keyManager.unregisterKeyListener(this); - mouseManager.unregisterMouseListener(this); - controlDown = false; - } - @Subscribe public void onConfigChanged(ConfigChanged ev) { From da0f507ffbb9d57332a82fe25e6266fb4ecd3e13 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 3 Jan 2020 09:52:20 -0500 Subject: [PATCH 49/64] camera plugin: add Look South/East/West option to compass --- .../client/plugins/camera/CameraConfig.java | 11 ++++ .../client/plugins/camera/CameraPlugin.java | 39 +++++++++++++ .../src/main/scripts/ToplevelCompassOp.hash | 1 + .../src/main/scripts/ToplevelCompassOp.rs2asm | 55 +++++++++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 runelite-client/src/main/scripts/ToplevelCompassOp.hash create mode 100644 runelite-client/src/main/scripts/ToplevelCompassOp.rs2asm diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraConfig.java index c7856d938b..32c2e3ba0d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraConfig.java @@ -147,4 +147,15 @@ public interface CameraConfig extends Config { return false; } + + @ConfigItem( + keyName = "compassLook", + name = "Compass options", + description = "Adds Look South, East, and West options to the compass", + position = 10 + ) + default boolean compassLook() + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java index 68875487b4..c67157d8db 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java @@ -30,6 +30,7 @@ import com.google.common.primitives.Ints; import com.google.inject.Provides; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; +import java.util.Arrays; import javax.inject.Inject; import javax.swing.SwingUtilities; import net.runelite.api.Client; @@ -39,6 +40,7 @@ import net.runelite.api.ScriptID; import net.runelite.api.VarPlayer; import net.runelite.api.events.ClientTick; import net.runelite.api.events.FocusChanged; +import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ConfigManager; @@ -60,6 +62,10 @@ import net.runelite.client.plugins.PluginDescriptor; public class CameraPlugin extends Plugin implements KeyListener, MouseListener { private static final int DEFAULT_ZOOM_INCREMENT = 25; + private static final String LOOK_NORTH = "Look North"; + private static final String LOOK_SOUTH = "Look South"; + private static final String LOOK_EAST = "Look East"; + private static final String LOOK_WEST = "Look West"; private boolean controlDown; // flags used to store the mousedown states @@ -111,6 +117,39 @@ public class CameraPlugin extends Plugin implements KeyListener, MouseListener controlDown = false; } + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded menuEntryAdded) + { + if (menuEntryAdded.getType() == MenuAction.WIDGET_DEFAULT.getId() && menuEntryAdded.getOption().equals(LOOK_NORTH) && config.compassLook()) + { + MenuEntry[] menuEntries = client.getMenuEntries(); + int len = menuEntries.length; + MenuEntry north = menuEntries[len - 1]; + + menuEntries = Arrays.copyOf(menuEntries, len + 3); + + // The handling for these entries is done in ToplevelCompassOp.rs2asm + menuEntries[--len] = createCameraLookEntry(menuEntryAdded, 4, LOOK_WEST); + menuEntries[++len] = createCameraLookEntry(menuEntryAdded, 3, LOOK_EAST); + menuEntries[++len] = createCameraLookEntry(menuEntryAdded, 2, LOOK_SOUTH); + menuEntries[++len] = north; + + client.setMenuEntries(menuEntries); + } + } + + private MenuEntry createCameraLookEntry(MenuEntryAdded lookNorth, int identifier, String option) + { + MenuEntry m = new MenuEntry(); + m.setOption(option); + m.setTarget(lookNorth.getTarget()); + m.setIdentifier(identifier); + m.setType(MenuAction.WIDGET_DEFAULT.getId()); + m.setParam0(lookNorth.getActionParam0()); + m.setParam1(lookNorth.getActionParam1()); + return m; + } + @Subscribe public void onScriptCallbackEvent(ScriptCallbackEvent event) { diff --git a/runelite-client/src/main/scripts/ToplevelCompassOp.hash b/runelite-client/src/main/scripts/ToplevelCompassOp.hash new file mode 100644 index 0000000000..605080e357 --- /dev/null +++ b/runelite-client/src/main/scripts/ToplevelCompassOp.hash @@ -0,0 +1 @@ +6A53DA1D918405E3F314E4350A9CF4002988E5C45E06D37A00AA725003FAD064 \ No newline at end of file diff --git a/runelite-client/src/main/scripts/ToplevelCompassOp.rs2asm b/runelite-client/src/main/scripts/ToplevelCompassOp.rs2asm new file mode 100644 index 0000000000..6884cdf1f0 --- /dev/null +++ b/runelite-client/src/main/scripts/ToplevelCompassOp.rs2asm @@ -0,0 +1,55 @@ +.id 1050 +.int_stack_count 1 +.string_stack_count 0 +.int_var_count 2 ; +1 for saving target angle +.string_var_count 0 +; Remove check of op index +; iload 0 +; iconst 1 +; if_icmpne LABEL10 + get_varbit 542 + iconst 1 + if_icmpeq LABEL10 + get_varbit 4606 + iconst 0 + if_icmpne LABEL10 + jump LABEL11 +LABEL10: + return +LABEL11: + ; switch on op index + iload 0 + switch + 1: LOOK_NORTH + 2: LOOK_SOUTH + 3: LOOK_EAST + 4: LOOK_WEST + jump LABEL10 +LOOK_NORTH: + iconst 0 + istore 1 + jump LOOK +LOOK_SOUTH: + iconst 1024 + istore 1 + jump LOOK +LOOK_EAST: + iconst 1536 + istore 1 + jump LOOK +LOOK_WEST: + iconst 512 + istore 1 + jump LOOK +LOOK: + iconst 2266 + iconst 1 + iconst 0 + sound_synth + iconst 225 + iconst 5 + randominc + add + iload 1 ; load target angle + cam_forceangle + return From 10744a0edc3f52329116cece7bdf47566ca349ee Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Fri, 3 Jan 2020 23:23:36 +0000 Subject: [PATCH 50/64] clues: add Lava battlestaff (or) to the Trollheim emote clue --- .../runelite/client/plugins/cluescrolls/clues/EmoteClue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java index dff0c2f105..4a2d00095c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java @@ -168,7 +168,7 @@ public class EmoteClue extends ClueScroll implements TextClueScroll, LocationClu new EmoteClue("Yawn in Draynor Marketplace. Equip studded leather chaps, an iron kiteshield and a steel longsword.", "Draynor", DRAYNOR_VILLAGE_MARKET, new WorldPoint(3083, 3253, 0), YAWN, item(STUDDED_CHAPS), item(IRON_KITESHIELD), item(STEEL_LONGSWORD)), new EmoteClue("Yawn in the Castle Wars lobby. Shrug before you talk to me. Equip a ruby amulet, a mithril scimitar and a Wilderness cape.", "Castle Wars", CASTLE_WARS_BANK, new WorldPoint(2440, 3092, 0), YAWN, SHRUG, item(RUBY_AMULET), item(MITHRIL_SCIMITAR), range("Any team cape", TEAM1_CAPE, TEAM50_CAPE)), new EmoteClue("Yawn in the rogues' general store. Beware of double agents! Equip an adamant square shield, blue dragon vambraces and a rune pickaxe.", "Rogues general store", NOTERAZZOS_SHOP_IN_THE_WILDERNESS, new WorldPoint(3026, 3701, 0), YAWN, item(ADAMANT_SQ_SHIELD), item(BLUE_DHIDE_VAMB), item(RUNE_PICKAXE)), - new EmoteClue("Yawn at the top of Trollheim. Equip a lava battlestaff, black dragonhide vambraces and a mind shield.", "Trollheim Mountain", ON_TOP_OF_TROLLHEIM_MOUNTAIN, new WorldPoint(2887, 3676, 0), YAWN, item(LAVA_BATTLESTAFF), item(BLACK_DHIDE_VAMB), item(MIND_SHIELD)), + new EmoteClue("Yawn at the top of Trollheim. Equip a lava battlestaff, black dragonhide vambraces and a mind shield.", "Trollheim Mountain", ON_TOP_OF_TROLLHEIM_MOUNTAIN, new WorldPoint(2887, 3676, 0), YAWN, any("Lava battlestaff", item(LAVA_BATTLESTAFF), item(LAVA_BATTLESTAFF_21198)), item(BLACK_DHIDE_VAMB), item(MIND_SHIELD)), new EmoteClue("Yawn in the centre of Arceuus library. Nod your head before you talk to me. Equip blue dragonhide vambraces, adamant boots and an adamant dagger.", "Arceuus library", ENTRANCE_OF_THE_ARCEUUS_LIBRARY, new WorldPoint(1632, 3807, 0), YAWN, YES, item(BLUE_DHIDE_VAMB), item(ADAMANT_BOOTS), item(ADAMANT_DAGGER)), new EmoteClue("Swing a bullroarer at the top of the watchtower. Beware of double agents! Equip a dragon plateskirt, climbing boots and a dragon chainbody.", "Yanille watchtower", TOP_FLOOR_OF_THE_YANILLE_WATCHTOWER, new WorldPoint(2932, 4712, 0), BULL_ROARER, item(DRAGON_PLATESKIRT), item(CLIMBING_BOOTS), item(DRAGON_CHAINBODY_3140), item(ItemID.BULL_ROARER)), new EmoteClue("Blow a raspberry at Gypsy Aris in her tent. Equip a gold ring and a gold necklace.", "Varrock", GYPSY_TENT_ENTRANCE, new WorldPoint(3203, 3424, 0), RASPBERRY, item(GOLD_RING), item(GOLD_NECKLACE)), From 062d1b99b94a0515702b274e04dfdd65b6028cda Mon Sep 17 00:00:00 2001 From: Gamma91 Date: Sat, 4 Jan 2020 14:37:00 -0500 Subject: [PATCH 51/64] hiscore skill: fix spelling of commander zilyana --- .../main/java/net/runelite/http/api/hiscore/HiscoreResult.java | 2 +- .../main/java/net/runelite/http/api/hiscore/HiscoreSkill.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreResult.java b/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreResult.java index 3edb616233..576b20a39f 100644 --- a/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreResult.java +++ b/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreResult.java @@ -203,7 +203,7 @@ public class HiscoreResult return chaosElemental; case CHAOS_FANATIC: return chaosFanatic; - case COMMMANDER_ZILYANA: + case COMMANDER_ZILYANA: return commanderZilyana; case CORPOREAL_BEAST: return corporealBeast; diff --git a/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreSkill.java b/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreSkill.java index bcbaf96860..c730917863 100644 --- a/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreSkill.java +++ b/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreSkill.java @@ -76,7 +76,7 @@ public enum HiscoreSkill CHAMBERS_OF_XERIC_CHALLENGE_MODE("Chambers of Xeric: Challenge Mode"), CHAOS_ELEMENTAL("Chaos Elemental"), CHAOS_FANATIC("Chaos Fanatic"), - COMMMANDER_ZILYANA("Commander Zilyana"), + COMMANDER_ZILYANA("Commander Zilyana"), CORPOREAL_BEAST("Corporeal Beast"), CRAZY_ARCHAEOLOGIST("Crazy Archaeologist"), DAGANNOTH_PRIME("Dagannoth Prime"), From 3cf777d274c7e654583baac247ff415e8fa07dda Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 4 Jan 2020 18:26:56 -0500 Subject: [PATCH 52/64] hiscore plugin: add boss hiscores Co-authored-by: Abex Co-authored-by: Gamma91 --- .../http/api/hiscore/HiscoreSkill.java | 160 +++++------ .../http/api/hiscore/HiscoreSkillType.java | 33 +++ .../client/plugins/hiscore/HiscorePanel.java | 248 ++++++++++-------- .../net/runelite/client/util/ImageUtil.java | 6 +- .../plugins/hiscore/bosses/abyssal_sire.png | Bin 0 -> 958 bytes .../hiscore/bosses/alchemical_hydra.png | Bin 0 -> 969 bytes .../plugins/hiscore/bosses/barrows_chests.png | Bin 0 -> 648 bytes .../plugins/hiscore/bosses/bryophyta.png | Bin 0 -> 697 bytes .../plugins/hiscore/bosses/callisto.png | Bin 0 -> 1024 bytes .../plugins/hiscore/bosses/cerberus.png | Bin 0 -> 745 bytes .../hiscore/bosses/chambers_of_xeric.png | Bin 0 -> 952 bytes .../chambers_of_xeric_challenge_mode.png | Bin 0 -> 985 bytes .../hiscore/bosses/chaos_elemental.png | Bin 0 -> 910 bytes .../plugins/hiscore/bosses/chaos_fanatic.png | Bin 0 -> 593 bytes .../hiscore/bosses/commander_zilyana.png | Bin 0 -> 998 bytes .../hiscore/bosses/corporeal_beast.png | Bin 0 -> 977 bytes .../hiscore/bosses/crazy_archaeologist.png | Bin 0 -> 881 bytes .../hiscore/bosses/dagannoth_prime.png | Bin 0 -> 697 bytes .../plugins/hiscore/bosses/dagannoth_rex.png | Bin 0 -> 688 bytes .../hiscore/bosses/dagannoth_supreme.png | Bin 0 -> 697 bytes .../hiscore/bosses/deranged_archaeologist.png | Bin 0 -> 1162 bytes .../hiscore/bosses/general_graardor.png | Bin 0 -> 890 bytes .../plugins/hiscore/bosses/giant_mole.png | Bin 0 -> 904 bytes .../hiscore/bosses/grotesque_guardians.png | Bin 0 -> 884 bytes .../client/plugins/hiscore/bosses/hespori.png | Bin 0 -> 766 bytes .../plugins/hiscore/bosses/kalphite_queen.png | Bin 0 -> 807 bytes .../hiscore/bosses/king_black_dragon.png | Bin 0 -> 848 bytes .../client/plugins/hiscore/bosses/kraken.png | Bin 0 -> 720 bytes .../plugins/hiscore/bosses/kreearra.png | Bin 0 -> 800 bytes .../hiscore/bosses/kril_tsutsaroth.png | Bin 0 -> 1075 bytes .../client/plugins/hiscore/bosses/mimic.png | Bin 0 -> 1139 bytes .../client/plugins/hiscore/bosses/obor.png | Bin 0 -> 648 bytes .../plugins/hiscore/bosses/sarachnis.png | Bin 0 -> 884 bytes .../client/plugins/hiscore/bosses/scorpia.png | Bin 0 -> 706 bytes .../client/plugins/hiscore/bosses/skotizo.png | Bin 0 -> 994 bytes .../hiscore/bosses/the_corrupted_gauntlet.png | Bin 0 -> 823 bytes .../plugins/hiscore/bosses/the_gauntlet.png | Bin 0 -> 909 bytes .../hiscore/bosses/theatre_of_blood.png | Bin 0 -> 788 bytes .../bosses/thermonuclear_smoke_devil.png | Bin 0 -> 700 bytes .../plugins/hiscore/bosses/tzkal_zuk.png | Bin 0 -> 868 bytes .../plugins/hiscore/bosses/tztok_jad.png | Bin 0 -> 764 bytes .../plugins/hiscore/bosses/venenatis.png | Bin 0 -> 882 bytes .../client/plugins/hiscore/bosses/vetion.png | Bin 0 -> 728 bytes .../client/plugins/hiscore/bosses/vorkath.png | Bin 0 -> 1041 bytes .../plugins/hiscore/bosses/wintertodt.png | Bin 0 -> 957 bytes .../client/plugins/hiscore/bosses/zalcano.png | Bin 0 -> 929 bytes .../client/plugins/hiscore/bosses/zulrah.png | Bin 0 -> 489 bytes .../client/plugins/hiscore/overall.png | Bin 0 -> 802 bytes ... - hunter.png => bounty_hunter_hunter.png} | Bin ...er - rogue.png => bounty_hunter_rogue.png} | Bin ... scrolls (all).png => clue_scroll_all.png} | Bin ...man standing.png => last_man_standing.png} | Bin .../{league points.png => league_points.png} | Bin .../plugins/hiscore/HiscorePanelTest.java | 12 + 54 files changed, 267 insertions(+), 192 deletions(-) create mode 100644 http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreSkillType.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/abyssal_sire.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/alchemical_hydra.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/barrows_chests.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/bryophyta.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/callisto.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/cerberus.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/chambers_of_xeric.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/chambers_of_xeric_challenge_mode.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/chaos_elemental.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/chaos_fanatic.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/commander_zilyana.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/corporeal_beast.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/crazy_archaeologist.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_prime.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_rex.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_supreme.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/deranged_archaeologist.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/general_graardor.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/giant_mole.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/grotesque_guardians.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/hespori.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/kalphite_queen.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/king_black_dragon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/kraken.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/kreearra.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/kril_tsutsaroth.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/mimic.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/obor.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/sarachnis.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/scorpia.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/skotizo.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/the_corrupted_gauntlet.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/the_gauntlet.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/theatre_of_blood.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/thermonuclear_smoke_devil.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/tzkal_zuk.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/tztok_jad.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/venenatis.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/vetion.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/vorkath.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/wintertodt.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/zalcano.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/zulrah.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/overall.png rename runelite-client/src/main/resources/skill_icons_small/{bounty hunter - hunter.png => bounty_hunter_hunter.png} (100%) rename runelite-client/src/main/resources/skill_icons_small/{bounty hunter - rogue.png => bounty_hunter_rogue.png} (100%) rename runelite-client/src/main/resources/skill_icons_small/{clue scrolls (all).png => clue_scroll_all.png} (100%) rename runelite-client/src/main/resources/skill_icons_small/{last man standing.png => last_man_standing.png} (100%) rename runelite-client/src/main/resources/skill_icons_small/{league points.png => league_points.png} (100%) diff --git a/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreSkill.java b/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreSkill.java index c730917863..ac57522a5b 100644 --- a/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreSkill.java +++ b/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreSkill.java @@ -26,89 +26,93 @@ package net.runelite.http.api.hiscore; import lombok.AllArgsConstructor; import lombok.Getter; +import static net.runelite.http.api.hiscore.HiscoreSkillType.SKILL; +import static net.runelite.http.api.hiscore.HiscoreSkillType.ACTIVITY; +import static net.runelite.http.api.hiscore.HiscoreSkillType.BOSS; @AllArgsConstructor @Getter public enum HiscoreSkill { - OVERALL("Overall"), - ATTACK("Attack"), - DEFENCE("Defence"), - STRENGTH("Strength"), - HITPOINTS("Hitpoints"), - RANGED("Ranged"), - PRAYER("Prayer"), - MAGIC("Magic"), - COOKING("Cooking"), - WOODCUTTING("Woodcutting"), - FLETCHING("Fletching"), - FISHING("Fishing"), - FIREMAKING("Firemaking"), - CRAFTING("Crafting"), - SMITHING("Smithing"), - MINING("Mining"), - HERBLORE("Herblore"), - AGILITY("Agility"), - THIEVING("Thieving"), - SLAYER("Slayer"), - FARMING("Farming"), - RUNECRAFT("Runecraft"), - HUNTER("Hunter"), - CONSTRUCTION("Construction"), - LEAGUE_POINTS("League Points"), - BOUNTY_HUNTER_HUNTER("Bounty Hunter - Hunter"), - BOUNTY_HUNTER_ROGUE("Bounty Hunter - Rogue"), - CLUE_SCROLL_ALL("Clue Scrolls (all)"), - CLUE_SCROLL_BEGINNER("Clue Scrolls (beginner)"), - CLUE_SCROLL_EASY("Clue Scrolls (easy)"), - CLUE_SCROLL_MEDIUM("Clue Scrolls (medium)"), - CLUE_SCROLL_HARD("Clue Scrolls (hard)"), - CLUE_SCROLL_ELITE("Clue Scrolls (elite)"), - CLUE_SCROLL_MASTER("Clue Scrolls (master)"), - LAST_MAN_STANDING("Last Man Standing"), - ABYSSAL_SIRE("Abyssal Sire"), - ALCHEMICAL_HYDRA("Alchemical Hydra"), - BARROWS_CHESTS("Barrows Chests"), - BRYOPHYTA("Bryophyta"), - CALLISTO("Callisto"), - CERBERUS("Cerberus"), - CHAMBERS_OF_XERIC("Chambers of Xeric"), - CHAMBERS_OF_XERIC_CHALLENGE_MODE("Chambers of Xeric: Challenge Mode"), - CHAOS_ELEMENTAL("Chaos Elemental"), - CHAOS_FANATIC("Chaos Fanatic"), - COMMANDER_ZILYANA("Commander Zilyana"), - CORPOREAL_BEAST("Corporeal Beast"), - CRAZY_ARCHAEOLOGIST("Crazy Archaeologist"), - DAGANNOTH_PRIME("Dagannoth Prime"), - DAGANNOTH_REX("Dagannoth Rex"), - DAGANNOTH_SUPREME("Dagannoth Supreme"), - DERANGED_ARCHAEOLOGIST("Deranged Archaeologist"), - GENERAL_GRAARDOR("General Graardor"), - GIANT_MOLE("Giant Mole"), - GROTESQUE_GUARDIANS("Grotesque Guardians"), - HESPORI("Hespori"), - KALPHITE_QUEEN("Kalphite Queen"), - KING_BLACK_DRAGON("King Black Dragon"), - KRAKEN("Kraken"), - KREEARRA("Kree'Arra"), - KRIL_TSUTSAROTH("K'ril Tsutsaroth"), - MIMIC("Mimic"), - OBOR("Obor"), - SARACHNIS("Sarachnis"), - SCORPIA("Scorpia"), - SKOTIZO("Skotizo"), - THE_GAUNTLET("The Gauntlet"), - THE_CORRUPTED_GAUNTLET("The Corrupted Gauntlet"), - THEATRE_OF_BLOOD("Theatre of Blood"), - THERMONUCLEAR_SMOKE_DEVIL("Thermonuclear Smoke Devil"), - TZKAL_ZUK("TzKal-Zuk"), - TZTOK_JAD("TzTok-Jad"), - VENENATIS("Venenatis"), - VETION("Vet'ion"), - VORKATH("Vorkath"), - WINTERTODT("Wintertodt"), - ZALCANO("Zalcano"), - ZULRAH("Zulrah"); + OVERALL("Overall", HiscoreSkillType.OVERALL), + ATTACK("Attack", SKILL), + DEFENCE("Defence", SKILL), + STRENGTH("Strength", SKILL), + HITPOINTS("Hitpoints", SKILL), + RANGED("Ranged", SKILL), + PRAYER("Prayer", SKILL), + MAGIC("Magic", SKILL), + COOKING("Cooking", SKILL), + WOODCUTTING("Woodcutting", SKILL), + FLETCHING("Fletching", SKILL), + FISHING("Fishing", SKILL), + FIREMAKING("Firemaking", SKILL), + CRAFTING("Crafting", SKILL), + SMITHING("Smithing", SKILL), + MINING("Mining", SKILL), + HERBLORE("Herblore", SKILL), + AGILITY("Agility", SKILL), + THIEVING("Thieving", SKILL), + SLAYER("Slayer", SKILL), + FARMING("Farming", SKILL), + RUNECRAFT("Runecraft", SKILL), + HUNTER("Hunter", SKILL), + CONSTRUCTION("Construction", SKILL), + LEAGUE_POINTS("League Points", ACTIVITY), + BOUNTY_HUNTER_HUNTER("Bounty Hunter - Hunter", ACTIVITY), + BOUNTY_HUNTER_ROGUE("Bounty Hunter - Rogue", ACTIVITY), + CLUE_SCROLL_ALL("Clue Scrolls (all)", ACTIVITY), + CLUE_SCROLL_BEGINNER("Clue Scrolls (beginner)", ACTIVITY), + CLUE_SCROLL_EASY("Clue Scrolls (easy)", ACTIVITY), + CLUE_SCROLL_MEDIUM("Clue Scrolls (medium)", ACTIVITY), + CLUE_SCROLL_HARD("Clue Scrolls (hard)", ACTIVITY), + CLUE_SCROLL_ELITE("Clue Scrolls (elite)", ACTIVITY), + CLUE_SCROLL_MASTER("Clue Scrolls (master)", ACTIVITY), + LAST_MAN_STANDING("Last Man Standing", ACTIVITY), + ABYSSAL_SIRE("Abyssal Sire", BOSS), + ALCHEMICAL_HYDRA("Alchemical Hydra", BOSS), + BARROWS_CHESTS("Barrows Chests", BOSS), + BRYOPHYTA("Bryophyta", BOSS), + CALLISTO("Callisto", BOSS), + CERBERUS("Cerberus", BOSS), + CHAMBERS_OF_XERIC("Chambers of Xeric", BOSS), + CHAMBERS_OF_XERIC_CHALLENGE_MODE("Chambers of Xeric: Challenge Mode", BOSS), + CHAOS_ELEMENTAL("Chaos Elemental", BOSS), + CHAOS_FANATIC("Chaos Fanatic", BOSS), + COMMANDER_ZILYANA("Commander Zilyana", BOSS), + CORPOREAL_BEAST("Corporeal Beast", BOSS), + CRAZY_ARCHAEOLOGIST("Crazy Archaeologist", BOSS), + DAGANNOTH_PRIME("Dagannoth Prime", BOSS), + DAGANNOTH_REX("Dagannoth Rex", BOSS), + DAGANNOTH_SUPREME("Dagannoth Supreme", BOSS), + DERANGED_ARCHAEOLOGIST("Deranged Archaeologist", BOSS), + GENERAL_GRAARDOR("General Graardor", BOSS), + GIANT_MOLE("Giant Mole", BOSS), + GROTESQUE_GUARDIANS("Grotesque Guardians", BOSS), + HESPORI("Hespori", BOSS), + KALPHITE_QUEEN("Kalphite Queen", BOSS), + KING_BLACK_DRAGON("King Black Dragon", BOSS), + KRAKEN("Kraken", BOSS), + KREEARRA("Kree'Arra", BOSS), + KRIL_TSUTSAROTH("K'ril Tsutsaroth", BOSS), + MIMIC("Mimic", BOSS), + OBOR("Obor", BOSS), + SARACHNIS("Sarachnis", BOSS), + SCORPIA("Scorpia", BOSS), + SKOTIZO("Skotizo", BOSS), + THE_GAUNTLET("The Gauntlet", BOSS), + THE_CORRUPTED_GAUNTLET("The Corrupted Gauntlet", BOSS), + THEATRE_OF_BLOOD("Theatre of Blood", BOSS), + THERMONUCLEAR_SMOKE_DEVIL("Thermonuclear Smoke Devil", BOSS), + TZKAL_ZUK("TzKal-Zuk", BOSS), + TZTOK_JAD("TzTok-Jad", BOSS), + VENENATIS("Venenatis", BOSS), + VETION("Vet'ion", BOSS), + VORKATH("Vorkath", BOSS), + WINTERTODT("Wintertodt", BOSS), + ZALCANO("Zalcano", BOSS), + ZULRAH("Zulrah", BOSS); private final String name; + private final HiscoreSkillType type; } diff --git a/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreSkillType.java b/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreSkillType.java new file mode 100644 index 0000000000..67e695364b --- /dev/null +++ b/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreSkillType.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2019, Bram91 + * 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.http.api.hiscore; + +public enum HiscoreSkillType +{ + OVERALL, + SKILL, + ACTIVITY, + BOSS +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePanel.java index 2a11a59d2c..23ff6521d2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hiscore/HiscorePanel.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2017, Adam * Copyright (c) 2018, Psikoi + * Copyright (c) 2019, Bram91 * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,6 +26,7 @@ */ package net.runelite.client.plugins.hiscore; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import java.awt.Dimension; @@ -37,9 +39,10 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.io.IOException; -import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nullable; import javax.inject.Inject; @@ -64,36 +67,10 @@ import net.runelite.http.api.hiscore.HiscoreClient; import net.runelite.http.api.hiscore.HiscoreEndpoint; import net.runelite.http.api.hiscore.HiscoreResult; import net.runelite.http.api.hiscore.HiscoreSkill; -import static net.runelite.http.api.hiscore.HiscoreSkill.AGILITY; -import static net.runelite.http.api.hiscore.HiscoreSkill.ATTACK; -import static net.runelite.http.api.hiscore.HiscoreSkill.BOUNTY_HUNTER_HUNTER; -import static net.runelite.http.api.hiscore.HiscoreSkill.BOUNTY_HUNTER_ROGUE; -import static net.runelite.http.api.hiscore.HiscoreSkill.CLUE_SCROLL_ALL; -import static net.runelite.http.api.hiscore.HiscoreSkill.CONSTRUCTION; -import static net.runelite.http.api.hiscore.HiscoreSkill.COOKING; -import static net.runelite.http.api.hiscore.HiscoreSkill.CRAFTING; -import static net.runelite.http.api.hiscore.HiscoreSkill.DEFENCE; -import static net.runelite.http.api.hiscore.HiscoreSkill.FARMING; -import static net.runelite.http.api.hiscore.HiscoreSkill.FIREMAKING; -import static net.runelite.http.api.hiscore.HiscoreSkill.FISHING; -import static net.runelite.http.api.hiscore.HiscoreSkill.FLETCHING; -import static net.runelite.http.api.hiscore.HiscoreSkill.HERBLORE; -import static net.runelite.http.api.hiscore.HiscoreSkill.HITPOINTS; -import static net.runelite.http.api.hiscore.HiscoreSkill.HUNTER; -import static net.runelite.http.api.hiscore.HiscoreSkill.LAST_MAN_STANDING; -import static net.runelite.http.api.hiscore.HiscoreSkill.LEAGUE_POINTS; -import static net.runelite.http.api.hiscore.HiscoreSkill.MAGIC; -import static net.runelite.http.api.hiscore.HiscoreSkill.MINING; -import static net.runelite.http.api.hiscore.HiscoreSkill.OVERALL; -import static net.runelite.http.api.hiscore.HiscoreSkill.PRAYER; -import static net.runelite.http.api.hiscore.HiscoreSkill.RANGED; -import static net.runelite.http.api.hiscore.HiscoreSkill.RUNECRAFT; -import static net.runelite.http.api.hiscore.HiscoreSkill.SLAYER; -import static net.runelite.http.api.hiscore.HiscoreSkill.SMITHING; -import static net.runelite.http.api.hiscore.HiscoreSkill.STRENGTH; -import static net.runelite.http.api.hiscore.HiscoreSkill.THIEVING; -import static net.runelite.http.api.hiscore.HiscoreSkill.WOODCUTTING; +import static net.runelite.http.api.hiscore.HiscoreSkill.*; +import net.runelite.http.api.hiscore.HiscoreSkillType; import net.runelite.http.api.hiscore.Skill; +import org.apache.commons.lang3.StringUtils; @Slf4j public class HiscorePanel extends PluginPanel @@ -115,6 +92,27 @@ public class HiscorePanel extends PluginPanel CONSTRUCTION, HUNTER ); + /** + * Bosses, ordered in the way they should be displayed in the panel. + */ + private static final List BOSSES = ImmutableList.of( + ABYSSAL_SIRE, ALCHEMICAL_HYDRA, BARROWS_CHESTS, + BRYOPHYTA, CALLISTO, CERBERUS, + CHAMBERS_OF_XERIC, CHAMBERS_OF_XERIC_CHALLENGE_MODE, CHAOS_ELEMENTAL, + CHAOS_FANATIC, COMMANDER_ZILYANA, CORPOREAL_BEAST, + DAGANNOTH_PRIME, DAGANNOTH_REX, DAGANNOTH_SUPREME, + CRAZY_ARCHAEOLOGIST, DERANGED_ARCHAEOLOGIST, GENERAL_GRAARDOR, + GIANT_MOLE, GROTESQUE_GUARDIANS, HESPORI, + KALPHITE_QUEEN, KING_BLACK_DRAGON, KRAKEN, + KREEARRA, KRIL_TSUTSAROTH, MIMIC, + OBOR, SARACHNIS, SCORPIA, + SKOTIZO, THE_GAUNTLET, THE_CORRUPTED_GAUNTLET, + THEATRE_OF_BLOOD, THERMONUCLEAR_SMOKE_DEVIL, TZKAL_ZUK, + TZTOK_JAD, VENENATIS, VETION, + VORKATH, WINTERTODT, ZALCANO, + ZULRAH + ); + @Inject ScheduledExecutorService executor; @@ -126,9 +124,8 @@ public class HiscorePanel extends PluginPanel private final IconTextField searchBar; - private final List skillLabels = new ArrayList<>(); - - private final JPanel statsPanel = new JPanel(); + // Not an enummap because we need null keys for combat + private final Map skillLabels = new HashMap<>(); /* Container of all the selectable endpoints (ironman, deadman, etc) */ private final MaterialTabGroup tabGroup; @@ -245,15 +242,15 @@ public class HiscorePanel extends PluginPanel c.gridy++; // Panel that holds skill icons - GridLayout stats = new GridLayout(8, 3); - statsPanel.setLayout(stats); + JPanel statsPanel = new JPanel(); + statsPanel.setLayout(new GridLayout(8, 3)); statsPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); statsPanel.setBorder(new EmptyBorder(5, 0, 5, 0)); // For each skill on the ingame skill panel, create a Label and add it to the UI for (HiscoreSkill skill : SKILLS) { - JPanel panel = makeSkillPanel(skill); + JPanel panel = makeHiscorePanel(skill); statsPanel.add(panel); } @@ -261,11 +258,11 @@ public class HiscorePanel extends PluginPanel c.gridy++; JPanel totalPanel = new JPanel(); - totalPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); totalPanel.setLayout(new GridLayout(1, 2)); + totalPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); - totalPanel.add(makeSkillPanel(null)); //combat has no hiscore skill, refered to as null - totalPanel.add(makeSkillPanel(OVERALL)); + totalPanel.add(makeHiscorePanel(null)); //combat has no hiscore skill, referred to as null + totalPanel.add(makeHiscorePanel(OVERALL)); add(totalPanel, c); c.gridy++; @@ -276,14 +273,28 @@ public class HiscorePanel extends PluginPanel minigamePanel.setLayout(new GridLayout(2, 3)); minigamePanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); - minigamePanel.add(makeSkillPanel(CLUE_SCROLL_ALL)); - minigamePanel.add(makeSkillPanel(LEAGUE_POINTS)); - minigamePanel.add(makeSkillPanel(LAST_MAN_STANDING)); - minigamePanel.add(makeSkillPanel(BOUNTY_HUNTER_ROGUE)); - minigamePanel.add(makeSkillPanel(BOUNTY_HUNTER_HUNTER)); + minigamePanel.add(makeHiscorePanel(CLUE_SCROLL_ALL)); + minigamePanel.add(makeHiscorePanel(LEAGUE_POINTS)); + minigamePanel.add(makeHiscorePanel(LAST_MAN_STANDING)); + minigamePanel.add(makeHiscorePanel(BOUNTY_HUNTER_ROGUE)); + minigamePanel.add(makeHiscorePanel(BOUNTY_HUNTER_HUNTER)); add(minigamePanel, c); c.gridy++; + + JPanel bossPanel = new JPanel(); + bossPanel.setLayout(new GridLayout(0, 3)); + bossPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + // For each boss on the hi-scores, create a Label and add it to the UI + for (HiscoreSkill skill : BOSSES) + { + JPanel panel = makeHiscorePanel(skill); + bossPanel.add(panel); + } + + add(bossPanel, c); + c.gridy++; } @Override @@ -294,24 +305,29 @@ public class HiscorePanel extends PluginPanel } /* Builds a JPanel displaying an icon and level/number associated with it */ - private JPanel makeSkillPanel(HiscoreSkill skill) + private JPanel makeHiscorePanel(HiscoreSkill skill) { + HiscoreSkillType skillType = skill == null ? HiscoreSkillType.SKILL : skill.getType(); + JLabel label = new JLabel(); label.setFont(FontManager.getRunescapeSmallFont()); - label.setText("--"); + label.setText(pad("--", skillType)); - String skillName = (skill == null ? "combat" : skill.getName().toLowerCase()); - String directory = "/skill_icons"; - if (skillName.equals("combat") || skillName.equals("overall")) + String directory; + if (skill == null || skill == OVERALL) { - // Cannot use SpriteManager as HiscorePlugin loads before a Client is available - directory += "/"; + directory = "/skill_icons/"; + } + else if (skill.getType() == HiscoreSkillType.BOSS) + { + directory = "bosses/"; } else { - directory += "_small/"; + directory = "/skill_icons_small/"; } + String skillName = (skill == null ? "combat" : skill.name().toLowerCase()); String skillIcon = directory + skillName + ".png"; log.debug("Loading skill icon from {}", skillIcon); @@ -323,8 +339,8 @@ public class HiscorePanel extends PluginPanel JPanel skillPanel = new JPanel(); skillPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); skillPanel.setBorder(new EmptyBorder(2, 0, 2, 0)); - skillLabels.add(label); - skillPanel.add(skillLabels.get(skillLabels.size() - 1)); + skillLabels.put(skill, label); + skillPanel.add(label); return skillPanel; } @@ -359,9 +375,13 @@ public class HiscorePanel extends PluginPanel searchBar.setIcon(IconTextField.Icon.LOADING_DARKER); loading = true; - for (JLabel label : skillLabels) + for (Map.Entry entry : skillLabels.entrySet()) { - label.setText("--"); + HiscoreSkill skill = entry.getKey(); + JLabel label = entry.getValue(); + HiscoreSkillType skillType = skill == null ? HiscoreSkillType.SKILL : skill.getType(); + + label.setText(pad("--", skillType)); label.setToolTipText(null); } @@ -398,10 +418,10 @@ public class HiscorePanel extends PluginPanel searchBar.setEditable(true); loading = false; - int index = 0; - for (JLabel label : skillLabels) + for (Map.Entry entry : skillLabels.entrySet()) { - HiscoreSkill skill = find(index); + HiscoreSkill skill = entry.getKey(); + JLabel label = entry.getValue(); Skill s; if (skill == null) @@ -423,7 +443,7 @@ public class HiscorePanel extends PluginPanel else if ((s = result.getSkill(skill)) != null) { final long exp = s.getExperience(); - final boolean isSkill = SKILLS.contains(skill); + final boolean isSkill = skill.getType() == HiscoreSkillType.SKILL; int level = -1; if (config.virtualLevels() && isSkill && exp > -1L) { @@ -438,12 +458,11 @@ public class HiscorePanel extends PluginPanel if (level != -1) { - label.setText(Integer.toString(level)); + label.setText(pad(formatLevel(level), skill.getType())); } } label.setToolTipText(detailsHtml(result, skill)); - index++; } } @@ -457,37 +476,6 @@ public class HiscorePanel extends PluginPanel this.searchBar.removeKeyListener(l); } - /* - Returns a hiscore skill based on it's display order. - */ - private HiscoreSkill find(int index) - { - if (index < SKILLS.size()) - { - return SKILLS.get(index); - } - - switch (index - SKILLS.size()) - { - case 0: - return null; - case 1: - return OVERALL; - case 2: - return CLUE_SCROLL_ALL; - case 3: - return LEAGUE_POINTS; - case 4: - return LAST_MAN_STANDING; - case 5: - return BOUNTY_HUNTER_ROGUE; - case 6: - return BOUNTY_HUNTER_HUNTER; - } - - return null; - } - /* Builds a html string to display on tooltip (when hovering a skill). */ @@ -584,36 +572,50 @@ public class HiscorePanel extends PluginPanel } default: { - Skill requestedSkill = result.getSkill(skill); - final long experience = requestedSkill.getExperience(); - - String rank = (requestedSkill.getRank() == -1) ? "Unranked" : QuantityFormatter.formatNumber(requestedSkill.getRank()); - String exp = (experience == -1L) ? "Unranked" : QuantityFormatter.formatNumber(experience); - String remainingXp; - if (experience == -1L) + if (skill.getType() == HiscoreSkillType.BOSS) { - remainingXp = "Unranked"; + Skill requestedSkill = result.getSkill(skill); + String rank = "Unranked"; + String lvl = "0"; + if (requestedSkill != null) + { + rank = (requestedSkill.getRank() == -1) ? "Unranked" : QuantityFormatter.formatNumber(requestedSkill.getRank()); + lvl = (requestedSkill.getLevel() == -1 ? "0" : QuantityFormatter.formatNumber(requestedSkill.getLevel())); + } + content += "

Boss: " + skill.getName() + "

"; + content += "

Rank: " + rank + "

"; + content += "

KC: " + lvl + "

"; } else { - int currentLevel = Experience.getLevelForXp((int) experience); - remainingXp = (currentLevel + 1 <= Experience.MAX_VIRT_LEVEL) ? QuantityFormatter.formatNumber(Experience.getXpForLevel(currentLevel + 1) - experience) : "0"; + Skill requestedSkill = result.getSkill(skill); + final long experience = requestedSkill.getExperience(); + + String rank = (requestedSkill.getRank() == -1) ? "Unranked" : QuantityFormatter.formatNumber(requestedSkill.getRank()); + String exp = (experience == -1L) ? "Unranked" : QuantityFormatter.formatNumber(experience); + String remainingXp; + if (experience == -1L) + { + remainingXp = "Unranked"; + } + else + { + int currentLevel = Experience.getLevelForXp((int) experience); + remainingXp = (currentLevel + 1 <= Experience.MAX_VIRT_LEVEL) ? QuantityFormatter.formatNumber(Experience.getXpForLevel(currentLevel + 1) - experience) : "0"; + } + + content += "

Skill: " + skill.getName() + "

"; + content += "

Rank: " + rank + "

"; + content += "

Experience: " + exp + "

"; + content += "

Remaining XP: " + remainingXp + "

"; } - - content += "

Skill: " + skill.getName() + "

"; - content += "

Rank: " + rank + "

"; - content += "

Experience: " + exp + "

"; - content += "

Remaining XP: " + remainingXp + "

"; - break; } } } - /** - * Adds a html progress bar to the hover information - */ - if (SKILLS.contains(skill)) + // Add a html progress bar to the hover information + if (skill != null && skill.getType() == HiscoreSkillType.SKILL) { long experience = result.getSkill(skill).getExperience(); if (experience >= 0) @@ -668,4 +670,24 @@ public class HiscorePanel extends PluginPanel } return HiscoreEndpoint.NORMAL; } + + @VisibleForTesting + static String formatLevel(int level) + { + if (level < 10000) + { + return Integer.toString(level); + } + else + { + return (level / 1000) + "k"; + } + } + + private static String pad(String str, HiscoreSkillType type) + { + // Left pad label text to keep labels aligned + int pad = type == HiscoreSkillType.BOSS ? 4 : 2; + return StringUtils.leftPad(str, pad); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java index afe7054d43..2d4246a041 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ImageUtil.java @@ -380,9 +380,13 @@ public class ImageUtil return ImageIO.read(c.getResourceAsStream(path)); } } + catch (IllegalArgumentException e) + { + throw new IllegalArgumentException(path, e); + } catch (IOException e) { - throw new RuntimeException(e); + throw new RuntimeException(path, e); } } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/abyssal_sire.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/abyssal_sire.png new file mode 100644 index 0000000000000000000000000000000000000000..3e410775614442cc3208632038b0d062aa425fb9 GIT binary patch literal 958 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 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/callisto.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/callisto.png new file mode 100644 index 0000000000000000000000000000000000000000..6f307a26d5d6b5b0c829093ebc76dfeab22c4b8a GIT binary patch literal 1024 zcmV+b1poVqP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZmN}9%}vBz_s%|+tMO)a-V>Xt?tX|47Dv;JA@ z-$HB6i#(DSe4d*?2ytBqp(i{GQ|q23kr1K+bf)EKt;KT_2qBh$KPDw#T3w~xZj)vi zQV7=8*NNkJ6kgU^-^gQ51y3Aw^MOSr%xGWtvY--jf7rN}QzZ?e8-PHjqlu?RJq; zqP52JJOJ`M$8j9uIL7llwAQ3)$}k9~Z}RuIZqa?PLs1mW%(x5(0pW0n)|&UvtzavK zYulKniQnrnSF7R8&Z3k;N=Z=^(>riYlecPBu5CYH>CHDdeR`2rtIhJ#8S3?uY~0(# zP?BL3kt7NAdYwVPkCO74Ui3M@R+RpM48^zK{lx3@lKb}>JluPTW7|j} zDDs@x|6GgH uE*v?QQ~@pmTVwGp;1ghZ3jVYh{|x}o7^k!+^{BxB0000f+ literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/cerberus.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/cerberus.png new file mode 100644 index 0000000000000000000000000000000000000000..4f7ef937cdcfc5be249f1608d2b852d935084fcd GIT binary patch literal 745 zcmV(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;Z%IT!RCwC7l}k?)K@^3*+dVbYGtCV1fB>R^vXDd#n2;DWCahc;VsIt#2k6eY zFmWZWToL^b`afjrN;GjvBu+FKMIOTd17VH}Xl>;+qfp6B-&EJVr|O<_t4tCnIXq5y z>D9}20UAi5GoW5j5~uFY;x9gq;>7-LG&Ac1K01Sfa&&Y! zs!Ln+Tt3B@@;BC(Hz==G2{S3GYqcostfaX{OSA;AC0I_Zk!>$>{(3*3XBSvms!?67 za=#;FJn(SNMKLo?$uut23$ar4fcikWQkw>f1@&h`xr876pKPc^aisv<>I-=MsKAHW zO{S(6r~npQR|DpMZ`StYIRxMV@H}lWmQ1rKs3bNhuZhk8RySd_4hi5#HqA<9Jqj&I zS~^mul3oC;3({FJTyoS6zG9S8(CVOnSFtOVbuyV$6q;{{gE$YDd=!{z093>jV(JHV zfdNpIbFMi)NVWW*US{_(Dpla`c>D^O~0=*p2 bY(ED8eE`&hj||qd00000NkvXXu0mjfu*5>B literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/chambers_of_xeric.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/chambers_of_xeric.png new file mode 100644 index 0000000000000000000000000000000000000000..a7240d11f04bd8a755107d8b9ef134d6fcf7c5d0 GIT binary patch literal 952 zcmV;p14sOcP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZibI`PWnDKrdb_r}_qH3y7p|G* zYG)?D?_Sn-A+S=P` z*u2@IX`B@l>n;IC0a@d6N%!Hy($m!`x@pSKiG*DG`WyLpXh@t@RRUNNu=67#_8Evs zD?qVS06zV@WW4&lol7+6x~QD5X3Z1re{(d zd841#`}-}kVWm{-azD)vr-(tc1@;IDVa^rNjj5}#LX#b+p}l+Us_k| z@cxMpWG<5t-7q9yD9Fv}Y5VBPGO_}wSmc#1nsR-+B~(6WL7?Yf{C`q*Z@#17{7)P1 a&jA1)<>Je)g5qER0000(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZQIhUv7}cE(O^TG3iMK-CTnG9+k4V<@I# zBqkOX#+|TosWEQ-vvLbDQ5P<#J4p;8NF))OV$ucB(#C&nS}091I-Ni7x@cdW5^BqW zl{fh|_q^}>?s@mzbHo_KgItOSy#6N!B9al2vm#V7-RkuKn5^(A-f(gfLiC| z2Z0HI`g9}VRAUettokM%;Jz~mPTbU*HXnTXk=eEH$smJ~<@m@wvK!n`1|rfA;JOZP zyz?erG)i_)j@m>Fpi4xCfG({yT5Ghe1L}$DY}6X!dEgX4XKp*O_$Df)G7q&hv#lpf z#B%`-gj|Qi$B*#-=O6Ok7a!Bqk_seFu4y15p8($hbab_|@6{LaeVP>4k*gqMip77FB^&y#y$FJFE78M$hQ~4xm~8Nwg&ItI~c4s zco)Do6^g|&^RqXZo0_IlC?HB9mcr5+r7bK`lq^jl>_Ie2Y2gl&XU@^EIl^xXCEV0X zC7l4!)fM#Yz<$!r4SfIY4HhRx@pkUQ@;r3NMl6N#mu83Sd0f3X%@mzePuHHy17qBjZ=O<%CE!r!feKV(#}T#?GB*_?sUn{#E4R zrdmRst(+d6CQ|;J@a)X$vR|$u6TI3IibF@9;r!4g>gzTUt%>l{^%=vT!Q{26pbbgTbB^7kr>tN{@1v&gP_GHaqSgCDKo&T= z25-n1!+Ndx_!{K?yLkOojc)|Vuf#Sm4~D9QPi}M$1o&qFG^5J^m1fqC00000NkvXX Hu0mjf(kR3Y literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/chaos_elemental.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/chaos_elemental.png new file mode 100644 index 0000000000000000000000000000000000000000..cc36114993d01af961150d0a5c0c28679e4a3755 GIT binary patch literal 910 zcmV;919AL`P)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ<6iGxuRCwC7l-o~SRTPK6HOve{hq*9iW&{k-qEIUeLQxY*7?XO5#M_H~Y-7^s zvwuO;|G>+O@kJkf!}!odLIJBu)0n2#(0Hc>Y@rNu=FB-~&Si$PeUM`e)Xq4uJ6R7q z`K|Tswbouslu|s%6XF4%rU4?N{$Wc^`!!P{5lI8u{WMA`X(5Dh=>B18tCWf;SBLdQ>42R%r9SOI(M9pzh1ysHGm^Zshj^P0};6fa3tH!`B!K8 z{Hjj6D*?dF@Gw(@BZNZjf&5%6B^m?x=#En|&*HqF_WvS=G?EdE;Lm6H>M!)*Ysi^!)9o4nVg#o(Suqibc&SJ>8fM!xxU%{?x$YzrgR_DBbi+9aUj6K3fu|>DaBLf|W>YaL4D=0Q zS~^D~4i~@MWMO5Fp42fcSEFp-2KY=Vb*3r6d+(gbt$DbvP1QC@B{Nu-j#40g4c)LY zsv7Y_oqV$y}k?Z{Qf@Nv&nahcknz1(=srt1~tzm6bjSXnZj?2Qu96fA08r= z?xyhEi=+~Nf78YNv#>vdYrvQY%)W4nNH|L3Py*Aj=*{(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ-*GWV{RCwC7m9cIUQ4mGXynWca!U;A_WMLskA}#`2D~SvdG@&3;48o}>@)h9^ z!XNk(@(HOZLPMQ~G$}x7Ks1CVLZ$$*z7%9FN38MMCL@iuc$#yixihmSiK`69701K^ zW>z<|T{G*NnJ)5pWPzky3G4&yFLjK5%pw|?@^DR{U4Nr3P;HiJ@rWX$#fZv!L~^H0I-+BmX{iObib9km9q#4Q0!yv1Ustu!KUuc0w0{~^jc;_Mbi2I_+>uM fc>6dj9N_l=38UqZt1Yzf00000NkvXXu0mjf6w&|8 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/commander_zilyana.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/commander_zilyana.png new file mode 100644 index 0000000000000000000000000000000000000000..e207d7e79166acad1472ebd1ca753d4155cb9555 GIT binary patch literal 998 zcmV(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZQ%lF{$}z)5bP!v+BskY*Asbxeu-T5T^_&!<${S z13_Voxr;H_WXx?aM=$0&$G|HuhP#l77j9n2=qe0pFB+k%RB>yRlByveCT-5i$;o+l zlZ+U*HgovGb93JF{C_+j@AC*LC6DqbJmS+dKnQV82qCMsPYB^}%27(WssZN!*=SAq zHX(irkgBT4kGDT3Uwgby2Hb0!hG?s#jskx$)+--tss61 z7zX(0g=gsv`q^}FKlwWea*=gR9zXr?fNBAU6(KnC;x-04LLenexeRmHZxRT!{O>N> z3=sBucqYQlUw4u(-+9*b!@v*bG<30j5Y!e178gcY{BOb66%QX{{AO$ zdxKPL8!17-BxUq+{!)S6{WC1WqoYS_(?K-0X%rw9dy+$k-ezGj zg=x*AnL3)Q1+cL#n_RkpqG~uCE)=7LstBZT&=DJC{_@0%HAn#bJ`+dPG};0_W^++= z!-j=jO%;J^|SI(k?1i1;Z$j zx|?Jo-A*77#^rM2UZb(GXNsw--4vajwD(ygrcFrYP=rEvcQ0+BFv;Wsz^}DduSMVk z*sCjT_ha>HJyF{O~z**OJU9%2*Xp9WAVNuOpimsEUfmZE#}j2!>%$E?d~P zTzM85Ob(DRsRjb@aZ416=usg>vQJ7EKctePH1(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ4}gsG^+m?b{@ zpz&?VqCOZCHC|%WWEqK1ChDRw8rhait8*s62puVtZm_mu2X;`}($aBhe?CZ;Mr&6w zJIQ(YCEs(-?|jcqlv3PG(YVP{Pyi8ei-`K0`U{Gnl*+4sA5j0I{u|lLjcOnw3gEfE zM>y2~7!#8|PX9E_w!#rDdpc3MI!PlfF^>b36XdTV@WZ#>T=2~<_yeNa1BJn zEFzy|cm`epc%o+?_S$M3j-8wt8fRr?m8TCLV7qx+#x7E528+O<%)?e?;lN%e&5aIT zdwYQC$q8cfVFq2pj8BZyz5js>v~=TBk%A~X_TCX3b}M~79YkXhUVnE0P1E@9xt5y4hv$qwYs_NC7iAiliJW&afhPea%KySQ(66QyPw$!LPP9cJQs z9eeI=Cm0N1x}}8RT$m@j_i_B2?*Z=2m%;l0pB_KK;O8Im)WJs?8VxXdKFHByU$SSf zlm2IWu-8@N^ZE$=k)Wlyfurvn#_hS7yHT>+R7Bcx`J!Z+r{8w#_dqMVvaAsREXv)sbmv=>WV17l<77@3+O z8jJmtsvHOA#XJY5AkFrds|K$Bd4^xge*8HAXt1VM=dIdh00000NkvXXu0mjf3J<|K literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/crazy_archaeologist.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/crazy_archaeologist.png new file mode 100644 index 0000000000000000000000000000000000000000..53d758bf8efc796a336498d1328965e271a3fca9 GIT binary patch literal 881 zcmV-%1CIQOP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;_en%SRCwB~mCbGwR}{y8cgADaO^6*kAx&LADiBCTRIp${rK->cYFBL+RhR7} z^bMeIK;MA8L|3!|foP>jR?`YdASwc zbM6Ns!e9bLzz1YR)__~x=bJ$BUtX^O)KZua%|S2KH9QbXDFOKUvolPU-p2D>LXjac0syo&SgWw6O%OB)qZng7+{_sH+yvGNYmIg~fKuuPa2=pje2Z4FiN9^hdb_x; zMcX{eoy2uBSb;?EwJnVMHb0E2;5xu`d7N{nkKnfwl$*n<0>);rB1j^58K{&?%oHa0 zbNM-#ou2+esuo@Q_$=%7I!Wx}I6`G=ic{~-GCO^gP8{MPoT$9VA4|&^BZQIeNxw)1 z*lcVNcM@jHC8j6GxpwJ8-amd4Z)}pk8iwN&5%sM$Ge;{dt^UU1v(27#ZqUma1~67g zwBe=iQy3fLov9LTHjh}tvHUjZgq`L(D%mB9`^)?*5&13^APhqs*FmgcMYug*C5a=% zXrd(Hr~CJKv{K_?Z53-eJ=ra#R54YHlv22k!cmGwv&GXFTh!|7eE!j8?pCY#L4f0A zY3UrTounr#04IUp4|wh0$-NzX>4P$14Cl|C=7R=W_Ub00000NkvXX Hu0mjf)JTjP literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_prime.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_prime.png new file mode 100644 index 0000000000000000000000000000000000000000..6b5543b5e10d85211c421f8ffff2723cfc37408b GIT binary patch literal 697 zcmV;q0!ICbP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;KS@MERCwC7l}%3*Q51%sGo6-x&`!a$pb|RJA{7^6FzL=TCa!g(8~+Ua0~Y=c zqZ>CQiXREcLitEUBZ0Ppfl{18Xoq&lTo6cN>gc@Z(&#&sf*h(rPFGMiFLTE*%eB+mf}fcQ|H?4vB!AB%-I3%yGuA__=& zJRV|0F)|MyFrNRx)b)7BJx$$ zFadvny~92HK|i7X5XOK((*#I}NK`3R{@)5#0k(f`b9U}fcNMndp!$659~8L}31LKy zhOgE2unPqJ#Ox{~JKsoctZ=)y%b*cvZ7WA=GSyR)X8|VCx7c%qwcsGdOqLf*>v;Ws z`Xl{(UHQ_s(X9yX0;KPxsoGWY+Xa++q5I~>Cd(g|a4-6hS4x>(OQdNUyZK#`W(>!1 z7>*efas}#j2jF$9$>*J=hee;AhnP;IdKuh?d^s$j0o f+2%WWXnzL)mIn$vEZ??a00000NkvXXu0mjf_lz*} literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_rex.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_rex.png new file mode 100644 index 0000000000000000000000000000000000000000..fc2bcbc3a0f19f0406e8b195fbad4b84c42da019 GIT binary patch literal 688 zcmV;h0#E&kP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;Hc3Q5RCwC7luJ)jQ4q&}=hEBvlh#`7lPZxE)P*E2h|w5VF4Pa;%1TC?V(@stML{(d*s=YyL7P&Tmx$AdlSb!41uUvtq zrlaeUa?!@~@YKMr*oftRPxUgLB*M+Qg(>{lDKn;q)) zCT-0?Q3OJOlt$MtGc3SMfR$Hk6!yxPOe(6$L@XNNLh2l}CsWZluvFIu9Q`#%xycxE#EF_o45gdjVVm zxHvOKt={14)^`l!;PSrzxXFvvHH?ExoRLx{hNehS6t*%uB;!F^trnqE9=@f2pzCdb zrC!Mg0w|UD3HZHaw(}t1XRh2eEl4Tv_8o$h@>m6AA6@@j>{0F2!9t(f76)0h-va>f WJ@sLQ6@)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;KS@MERCwC7l}&3CQ51%sJDF*kuO>7JO$}`ZWBjOyZi=`N3xX@5Zrti$Y5zfg zhzms!L@KqoXjDozQ7kn{^HH@4os=Y3iocEsh+;gswQgV?? z;eyw|1VRX12qDj4MF^qy^&3PYAw&o;&O4J*ieA3z0Kp4D0w5ZjWZ}_0>PCZiuh&PH zNC+W;gv+H6jZJd@;axuG%Y>)H1J^+95q#lf|Mb~oG`B`BUuJG9!eQ%hbOK-r;LiLU zwr%r1vyG-G*fj&o>N4(e(>9L~LcEkxJ{g8U2S_Am_))I1IPW6h>C$Z4w2pLkq7go> zegaq=wgz>8T6LdhvyIs@sqC7pzWK@CUWdAIFmjP~fLT3Gd}b2UN^#J>jN|xOf1AeV z_l-zR7;Ij;gs(Rh7u>B~rJNjJd}~ z&Y%tu2>J2&Jk*T_wr%4J_?V7Q5tulYDDk(`|C=BKko#W5wk)J0kDsS|jJ>Ks%;zN- z3Y^#)Lk~NTcbtP(n-5!gUTkLhw)>l4z)NPkMDluKB#D*)uH4X3rR1u|&+`ZK#{ppLYmSYzFF41Oct=X94=s_R2ug)rlCx7- z9gA2rNHJHT)9C`d>J@w-f@Y&dI2xi*sey!F)zc1M?lptXei-KhkbRK(m+x8h%3!(A f-Bt!^v_As?zd;F;#K1vc00000NkvXXu0mjfIIc2@ literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/deranged_archaeologist.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/deranged_archaeologist.png new file mode 100644 index 0000000000000000000000000000000000000000..8dfae8c2fb8f4fb5433263f5046cc1a2d262615c GIT binary patch literal 1162 zcmV;51a(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ=5J^NqRCwBqm0NFARTRg6`*zOUI~`hDrllRISVK_Y!RUh$eK5T8?2G*d@)6*Z zfS*8lGZ+bp27;niz(j@877%(FI@7sz=A5(7_F*m#1437_l9RpG`Ty76>jwK1H=@;n z!8w4X5&Jj5DWLQ^&~vW<9@bBQJaGMWeqsnP-_vSOmw}T2#p!WQy}iinOrC2$-s8dJ zCoe=YmQ8b{ILpFJ5kF_SdHZixpKJi2hx|&wZ@uqzV18;c&$~w#@e`V)@z~i4c&AiC zDnX;&r2M?bcrM53#YM8IG~jr&x zDN0jCjIWSR;TeO`3MmDhFhXfXv)$^$omNBG8^8q{#~eRe;g~ zmJ;h6T55dH;2DEb5+M{}6cRg&_8ptsy8uZ!a)D0))}B7&+2bb9w*ow^k@CM(DJ4ny zJ_+B$_dJx+Xr&PXtc`KbacFX~@BYOo!FAwDtKFf}sL`s&7_ITN0nsH9Lf{#L?|EpY z@U%iH1vs3GX|+0pd+wziECHALhnaMOg@s94Re^IcMjM=qdse}b;yCGqM`Rri5D37s zUD1TrVr~2q!TJ8d)I^>$ADkeQ@rVOUy9IF^<6JlTLM};ZI*(G_#40Iq&e3dlh-`?B z%NQeGAUFq{EKZGc_M`XN-3f`Bj>iw1IOj+ueBxd+Yb98iDd2ks-xzEhW8)Zvz&D1m zbP}Tka;S7P@HN2f^aOkD04KUvcGlv6b`YSILQ2WpRGwVg$2W#rE2O#Cp;B*g_+XBw zL7wOSu`Nz#q;BEa+NE;sJn;fLRssWloXDXF(Y%GEt)4(8BG zcEN#WtxCoO^wZgB)VX?VnXR1)VPyN`X7EH8hKb6RD%Wn`X8Hbob{h>Q@=3y|TavMJ z#Ifs*YG?-`N#CPVZ?L|x-H-eTaLPGX9c{Ee(N~eR{I#~ucej4x&dMrL6moB+Ouf|^ z)Vc#&od9cNzW@0ywZ>k5N31#Ls;?Y&Lw#|%SHfRjz4bbkdMb z#Q3(w>dGb$o@~A_ZU)7(k8QRDTzc==JfENan9WMSUOU3|_FQBw-`>2zKU>=XyUw}% z@XvA|1m^*c9X^DoHKpk+*_0uSB7!L7{-Z~IaNe{c|4*uKSe<=6@0?>)_am40L+~na c=N$hI0P8jDz1=R*ZvX%Q07*qoM6N<$g5QK9m;e9( literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/general_graardor.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/general_graardor.png new file mode 100644 index 0000000000000000000000000000000000000000..1d8e4334fa01f36d553cfc3951aedfa63c9e50b9 GIT binary patch literal 890 zcmV-=1BLvFP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ<07*naRCwC#l}m3MRTRg6cb*=P9eY~Gkf>?gmQo;)5-J1>N+K*IHoO){6$@yK zR6Yd~e1l4S09Nd~Kw^W0KmsHbB&P*Ro3=>`P5es7vFjPncxEmOVpO|z>4HcsIMS7_ z&eb{p^FNP!gp`uK6ooyO{{jBP078fhLI^qLd@O`m5<=wv2|(PhO?M65hYZq@QntoD zYGd?y0NvC$`PR#9ZMAvnDVt+c9-n@`i3me#jXU^VAHWqt+-0K z8JmpKBl7dCRF(-7xN+ron(iI?{b*TkK+D3iPWfdbX1}9^*5I= zrDXp2Jm)@mhw{<_XWx6B$$~SKfqNM2Mt&Z6f1o=&TLj?MQ?IdIclqVVuXy~iC3H%{+a9Z{Yg}8a4y9?d1{Z)u0MpR06^#jXKcN}Ya@$BnaPG{z+*pqw0e7bd zDS<+dt7}c3f9~l4!0~&EEQB}>EM_tmvg993v+r?#(*k ztq!Py`MD!}^3jJRNj&g%W{AOHU^Go-=7|zP6w~YacyU5nRXBPehdF(eZ}`{8@Kf3+<@Wt2e)AZ3~43l2TMN8^tM(^;K^K@MxjAfwtG2vQU@ z1IJU*gak#RDJE_+!gT}g>X+6izvF*vozJ_Tcj803_!%7j6xA Qi2wiq07*qoM6N<$f_JQq>;M1& literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/giant_mole.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/giant_mole.png new file mode 100644 index 0000000000000000000000000000000000000000..f814bbd2d5a19b3489669e04b3c62966bd1360a6 GIT binary patch literal 904 zcmV;319$w1P)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ<4oO5oRCwCNl|649RTPGwbMMTqcV_m}tfOFzgo#50M1%;DCQ?EH6hJ~jm5PRz z1n~#p7Z94HL!v^6#t4L>`8X-O6KyuLAqYh|(vhz2 z$NS!U&U?=hRpnlq;U3%lB@mI5BC;kTI>lcXk=h*@R5jOuWkB!7r(vin&Xk*<2TogS zs01Zu>t#Stl;JQ2gidFNo&JC{RYW9H%5$o^dJER1ibmjP0Tv&gqh1YA1=SQ3QdOcT zB1)8Yr%&s8mpD$wBO+4?L}VE_4e0HIljomW z;Oz^a=Mp$$;O}H%dE&qV)ryZ}1nZ2zW5ux!Q4)9`zEshbAR0#(WQ4}+XWBUC*ak?iEYlOe9wdw5a0=%!P=k6%exki;j!NGMMtPvD2 zMsOU1v1UZ1!YV+BuV#VCV;huBr#YK3__{s;#ffd$1Sq; z_jY!CMMUbV+MY^KD3owb5z>g=FuXbMx)uc-$1;c#jM;m9Q4Ebo7g-B`rPtpDXo<+v zs=AgNqIchTmDm;uYO~A~-0ag~jlr8KVysXKDwL~rtXrZ`C@{aj$P&+nSaf}V<%p_ zIli{tp&P~=KXwF-2PE+TLB!x8L*PORq4uuL*oSRi-C_m9e$AfcJcF zhEp$`z;O(nZl6D|Y;yGQL6Q{WBw_vXCYP>UW@EF(a2Vb8qs`dFs;bJEp9kpmLn>vD z!1vg=y2bHhi+uRW7l;@F&*!(_FY(9vRxWsMi1KWwyUTVj)tONM literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/grotesque_guardians.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/grotesque_guardians.png new file mode 100644 index 0000000000000000000000000000000000000000..98606aed1143fa45c92611a41e0fc78bcfbcc3ec GIT binary patch literal 884 zcmV-)1B?8LP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;`bk7VRCwC7l}l?|R}ja4BkSt=TGp%B@-y`VH`GqtVh7xX7q$sb7D*sog+QR~ zqR?gOCuqMxKSB!aqG`dKLKZ1~(&Ds&CJKpNyAYhvT8f1AvLs*m-aFmMMm4gfKz(31 zFz3wwe;)st5mn_hhmTVpFGV0C*F{9@_HJ82s@j$T9|C&B@-5?z0sFKGm;xFKhjes& z^r-)K+XN?p_Yd~#M;~0(@re;#Twc&fG;+9h9Dy8Fk%%b3{L(zGPb8VCLN9)YZOmO$7hrBj1 z%iQb)Uw!d4ix(FULLwq^{X_y0$pZv~7LMbvTi#`_vPXO%PB0YY-u*2`&&(oTfSqaw zQ`3{g2jWbgpFDtA5s}6JU>-b?i2ML7h``+ZTV&H&JkP_jEX*#GXf#TPzk^D-f@zvK zj>FLK5CJoQ<2WQ%6Kt$)94J$o-CzOW?9}U&OJzLu@cDfBI(&!-uIsW}+$9taVRo9- z>>9RhV|JR@woTt)Uweu6=i_w@!+^TN+aJ-AZw|5Y~NoqU8{{B9|d#ZZ7DM6hi3XB4O96in0NFR~j0Rm=# z=YMUooz68obWBzA$ErwG^Q!uwuI0CZ&+0Qzx6(L{L+q6p#nPc_mVi%yB~_J1@Ru#u z9|I~PdimPrmPPK^V%MVKXp?>pAu`mt5azXJeDZS{DlPXE;a0000< KMNUMnLSTXdIhWx8 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/hespori.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/hespori.png new file mode 100644 index 0000000000000000000000000000000000000000..dffc71404c749bd8dfbba01b1b1fbe53533e555f GIT binary patch literal 766 zcmV(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;gh@m}RCwC7mCZ|3Q5c7xbI!eY#u@WE8v7V5KT>DZnYs7QJuOm+$WbRea8~Di--qAtJ?A|_ zYt2?J$ri5-0fZ3xuU)%QfLPBWA;doewASLk6$l~50jk4cdb+ykXl*4N3jL{SpklzX z4Lg)l9B6B!SeT}yAHSfnVYxbpV53cr~ z;AHO}s)7~)%Oqe~2mxMciNRzVPlP#jyu0!yd!-V&LWyADHvm9NNppM~RRKwA^b`4l zQx;&9JIv>c*wvEkM399E6R%|Am0U!vLo^yDU+|fl^($9Ua6Hmu@A#Q;|JijwbF4w#%?Wbf_<0s)g) zT@8j(C?%0nAOxg7WEp<(t#XUv^zkj)h;2P`bQ*flV zv=W}&j76UT?miqL`Fsi~G-w|Ln3loJbdJ==IUWp+lYEw1eV3b=MQ4Hg0M{;^(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;tw}^dRCwC7l)F!pQ5eR5r#A{M?e~>(E0i|aBB<3SNP-xZp@SP5FPp)|!MHFx z>0jXDu5oc9fx+lPLNvx`Oca7eQ=tUPz2(+Ixs~I>M@j{uHN43gzLV$V_nz}S?X_YH&)Hx3gDA*2wZKVN{z_CV|Ya=ndax1G1cvnUobL$MU; zt)1+115(Oh!GL==+-w?kxaN#Zq^qcF*&sZrWAN*5recY_p6o!AWo-@8S%j)&@K_?u ze27vuJI+)rfnu|0&GZxun2M&@-Yg>OSJ-SRr$MzdXx>I~STP#)==C~)j$L28`8-h< z@C-mLFXmFnOL-;O6gy|UeqMKf#GXtu9A9N(B*jL0=SV+9?w|=FUILxkch}=vRJ&AC z$u&mb#4+zIFyl=SpUv>OZ~RC<#9@=H87_n%a786By+)14K_XpF%4s0HFv{wTfzil3 zz_OH5$!n6euB4PSoN})iejq??>Ut@znSH; z35pEMEX^fxxz%i0R0t6)2rxehnT(!Nn}z6Dn%g%YaG}kH$pnuchOkp0*G`E+l*X lr7IM$3u(^*^yg~+3;?#GtGg9I^{oH^002ovPDHLkV1i;GZ%hCH literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/king_black_dragon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/king_black_dragon.png new file mode 100644 index 0000000000000000000000000000000000000000..3bc8a77466a84e7800e7751590ca3ecfd1a8be83 GIT binary patch literal 848 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 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/mimic.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/mimic.png new file mode 100644 index 0000000000000000000000000000000000000000..3d2c0a4606d854ea10cc4e207f6b5e0828cfa6b3 GIT binary patch literal 1139 zcmV-(1dRKMP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ<`AI}URCwC7m0M^WRUF2DXJ&SGb~DYUZ8lb#Cbh8%u_>liH1w(n7TO0}Y6Typ zBKROu5c=YizNpk2h_9lQ;*0c2)Q7fUE!GOHSbG<2Lqlx3Z8n$Po!z;eGslO`E^$q; zcscNgIm5^Q@}2KHM`*42KbO(^LkKY|gwQRq`yW@(S~mlj1@t2EQ46+X0nBUjF@{Oo&&h9OX=E&cnK_Z;|0f=Kf!*0Cx;zWqL5M|l_qOj zgc01o+~CzcS*FW%a5|r->Uqcn{7{o~9l&2$MRT0enX@nrjf`WYlEjH1jABd^lmf2? zT0<0rk_xFL0M$SfE9huzva!2Wra6G&K7;DqdCFKKC0M3~A406akLt<8C`QVdYNbRF zfL9HPr9sznr;+#Yf}UnyxaFj!EU)$!4=eQH0|-969nyvw$)8@e9CYu`ZF0 zzO}5}a34!5RfZqB!_ba{M3G6`s@>$wE8PC>5<^>Tip5ESu_+$gI6{B_AjMlhbK>|{ zi^>#$eDBkQVZheSz0@{D6biGY0n+&_-+cZn1KmBW-`h?lw2+?9 z`3u*W@sg&an`bcpn%Mu`6RcUc3&SwTxkXMNy@+FHP*M`97(WWw^Wr-0PI?&bBPc1E zxcxhBo6Dg?Z#Jn}bONh#hC*QjsgzBUbP`EP%BUyze2=nUrBGhObhXIME2l|h6@TIc zvAgweom{UTfNiIl8av6I-#jd%7pWCN6d+B(re}6BK0b<+3F(;{hy9wlX2IJSdj*`!i7ZP_`ag~`29RGtwyT9fDm(}1PRzGb;IIQ))*3KUkxB8jKa zbOBHcK+1%R*TX-lr+G`as_5UY`3h>KsPrG?|4WWP1_04*{!dOKgCPI_002ovPDHLk FV1oP05$FH_ literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/obor.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/obor.png new file mode 100644 index 0000000000000000000000000000000000000000..8c9ea13036c8b75907c2220921238496f39655c6 GIT binary patch literal 648 zcmV;30(bq1P)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;4oO5oRCwC7l}&3CQ51%sn{U&`q^TwZ#iVG(g`m=nJ68qKja^C?+F#(ux`Mwz z{KCCUL8J(Zt_vv$)e4HXme$xBlbB>`%uJF=W{wLTD1y!4qz4XMIOm?{-uoWTRU*P} znvY#JR{*6{LMbKN{DNzOi1c+}4v_8cW}k@Aukcl;U_2I;2RAOsp-j?&ECGpr0JW3I zN+|)DIDUkiSEe!b8efawxc_LLl|scy8xxVw|E%E#@bURAP(FI?2L4c#VyVpR-N!7v zT?Z(LNY0bU_Z~i>T3V-TwrMx(42Nv4PoHtZSub5Q1@LlVg{IM=^u5N~Mv2vp3YIP2 z+f+p609LI25F|rSopBj8e)KAh5&xp9C%gjAv7{+9rCdgWl@Uw7}?E-T=TG zXL0fTY4RU7scQ|&TRO2&kVs^YX8-`_CMRihd>Fkhwl9o7U~w=tj4d*lQSs{aBAQkM zCDb&HQ)44ULP6T5=^0P}sMhM_7neyz6`9cl$FgbSLt&E9pf`Ysh4Kfo`n)Vqd%8LeLe?*)D iMu3^W;xqjY@M{1l@ICGKv%xq300007 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/sarachnis.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/sarachnis.png new file mode 100644 index 0000000000000000000000000000000000000000..e74c398976d28b3ae566d7ead7fc69a8ff5d25c0 GIT binary patch literal 884 zcmV-)1B?8LP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;`bk7VRCwC7mCJ8iRS?F1<9n}D?AX`FeM+P_Dx#uPC9o?MAn2kiq%PPuE7svZ z>3;z1Snw!9Vu`|v6|9O71Q8)6@gsIp+qv(1&al9a+{CrhMUOO#BhAb=`sVS8s`4UV zffxMF1Rx?CBBCd3Q$(5zgH?5|0bc;Rz#cs-UKaI0L=@oOD_3}}S|PNSK!jg*TC}2w zJU0{9FI9E@WW0M;F5f=?GC`IjB3!Oi`S^O7APm_ZJmIIkeXQyO5#c0AVL|ol0EL2} zD1uU}R8eCfNlBzYX}QWf^%?~cfCuCLG|$@P&jD^OIb_x%C@*;~`O%2BlR*XhxbF(#%j^UgmMDH5ne6*#i-| z4}3TIF9W4UgRI@c2<&=G1Zl?J*58EV;(Qm0!gz{4hyLY)z z_xbVbZ>e0U(H%w{CJ}LxP3NtjqG&?TC#x%5t5tDo4U#xw=irb(h9f-BWy$gI9GB5S zmnFgE0D3`4oOoQJ#?)OF~9! z?D%j#^#=2j&jk3n)5lnggCHUVX$lIN+KJ0&sw!ivEjp6Z6s-c=kN0~#$#OPD`1IBd zDgwX7If*?6c=TM7c0yb>76{A6B7r(3SThu?`3#`7=S{Av{5Jq_AN%jm*Ve`W0000< KMNUMnLSTZ)eUt3~ literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/scorpia.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/scorpia.png new file mode 100644 index 0000000000000000000000000000000000000000..eeb63104545ee3a399ea58c66844379542d67442 GIT binary patch literal 706 zcmV;z0zLhSP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;NJ&INRCwC7l+8|4VGxF&Y0qEjX(=r#6x$x42qkKoAi5wVHEu95v6tY|wO8UL za0y<5EL`Zy#AxDAf&^muO-{>(R?N8Y9Mc}!N&vs)O-|;UGxN>N`;Mq8|MSH7&*#z! zM5Hbv8sOy_2UWc=fI6Vvi2BH7W*{QL&l1E3Rh27gk%;&JBZ&mJD+{4{>q=S#WB?Tr zEfoqHRH8ubjPC`4*TR0MTvrp06$W4L>pvur7a3qJuq&XsY*vl95#D_t7-vX=*HMOq zaFYqWIW?v3h!fhpb0_G2xnr9;<9S-h=QZUf6i~y6t5+(iWf}vi*$tx)LFxc0*JW;Y zmXUAs_OT!Evkc%+jkZh+1uY?|FFfq zYL#}o%}!&NTk?~Sm`r9IOf$}>FFR3I4%R`)?a_3arr*RX-Tz zb(OE14YFfX%+1UoB7^l3RuH~CmmR|^6me`D5kY}wvx(z4oE{yKpP%RRw+}pBTjt=X z#hbTVY;JE6HX>R9-PE&8Q}0$P>L!zF+hz}QVzHQJGZ}rbw4}41r`K{heewK><|ih! zwz4vC;5`Wz%4JQbQfe4+9Yj$;%hS`kT&@23E|rEZyPg?1jzjDCnA6i=02`_*A%L;a zud%m_Wm$}-(roWGxb|RkB^^A oXct*_+HXf)WrFo#LyhA10RGnGHXJ=`sQ>@~07*qoM6N<$g0;~&=l}o! literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/skotizo.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/skotizo.png new file mode 100644 index 0000000000000000000000000000000000000000..cd96bbcca01af36999b59bc2ef0ff99afd00678b GIT binary patch literal 994 zcmV<810DQ{P)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ$0%e1zXB=Iv-AFdN^LVH3;Ry#C?;Kob$|_=Q-zj-@HdtO7S8? z@Pgx70Zr3-nx-jD)6{M7|0JMk+I^sSZmq7vuMDNs*a0X&-QCzwqxpIKPUTZ2gf{y2 z#Z^4w!l}PGfAxucmbuwkbY1-Gv=jL(iQ6Za|azVjx&-$fHTlZ6s8 zJ!43Eqd@O@U7?#g{$qpLnJV8uc)-L&j(eMLGh1C|_0@Hpykh&OU6vOYke-E-CTH64 zMuBeu5;MR{BG&IMU>GL9JbHxN^GMQ^X0t}BI{f_WZyX>0fs~S7+G1tBLZssB1(Z_z z07;T^=aYFlhbCrNqF5~O!`2qn=?cxo1{q5bgdxjIi$v}KD|!jrve3}47kFN#=(->o zqzsY>VQBP^4DN1h@NjpRC;R)fj*f^21A4xP6Hd|Pgp6g-XinZ}(G!4Tp-8zjNxO53 z)-Lh><{F)Dmmmm<;+P-|dAzqrtun<7ud%`#q`?%HZJ_J=rS_qX+Vu$h?BkDcvJR$c zaHb?#%ORI@ICy%9>v@D>$V|10Fbv`(!m)D%fsfnk(>gk0@8Do$xi+>;bB=>jia3tx z_d|wqNZ4Jj51D5c0b4xaCi+Qt2`76nm6G8o``E^DvPvG-j_?=-}=ErwE( z&1Mn0pmPw>?sRcom&4y%092-?F25@mll*_hxFo+d=0EQiT%|GCKU{we06B@KvWeyY QFaQ7m07*qoM6N<$f?p@fM*si- literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/the_corrupted_gauntlet.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/the_corrupted_gauntlet.png new file mode 100644 index 0000000000000000000000000000000000000000..9553dde2c1241fdcb4ae2e3e9729aa96e5cc16d1 GIT binary patch literal 823 zcmV-71IYY|P)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;y-7qtRCwCFm0d`aVHC%I@5kPKyjyeA>2eNrW|D|$Mi*&f5kZh3ME6$Ebyv}S z*U@!X5!F=}S{6bOWZIJwe zfW;;bRRE|0ajmmEzdr}aU-B{Cg;M@+C``Zwum%uMqbR>aN|GeDRFSvJfebk8*4)t5 ztd}~?EHD5>0QNa`zAuM4ifQLEH4N~`R-Egx+|aG8Q=^!as=J1t zi_2J?5?tHIRBqpU zfF>mBy>|4JPAC~+Gnqsx6R@l9)Ydo!AVyDJ7Ek==iv@9;xD~rv;^OoAv$PULGpWq2 zMoF!$F|~727c1#>qkmJ=QkfTbw7qt%u5tmt1_0J*YYMrLZ&?5U002ovPDHLkV1h=S BYtjG! literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/the_gauntlet.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/the_gauntlet.png new file mode 100644 index 0000000000000000000000000000000000000000..e34fbe3d152e1835582375364946d8ff7a9de02c GIT binary patch literal 909 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 IM6N<$g2D-oqyPW_ literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/vetion.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/vetion.png new file mode 100644 index 0000000000000000000000000000000000000000..383d30a11991846365ce100bed007026e1d29df3 GIT binary patch literal 728 zcmV;}0w?{6P)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;UP(kjRCwC7mETK~VHC$d=h^1wez|FE%5?9xD9|!Ahy{x&UC1?&>e- zKgs`~=-Lv6>dKM`y+}}~L~<_2{4s6wbhCHpT65DnaNy;6&i8zI&pF@ch^q2G&G4VC zB?1w7CL&t#@uonk+LQw?0j=?gV{t7ID4>sTJ<@?#f0g7ooN2N&;{YDFi|yz-j=baF z^*}@>fENJ0p*XwCC4%k%Zrt|$WThvQb@9iIsfZ{*CYj+vVSwCLj;{SK-k*QN!qPIO zQu%N{rKLX*ks*M9za7(A6NNG8f#^A2x5Ou1atd!AKB)wu6)=@s@lX$blU{-H<>;5+br`&pLq9VYqkwE3zwEb?7> ztaC}MkVQI~Mx>f!<*z-kliJqiauVsj59L=}9!#>o7@~L0Vx}~ME6@gRhzH}$FU=F~ z2(y@9{G%6jOB6nnA>i*prHyZ2pQ8s#p-{x}%S|r2fhW+;LVkfhPam^Ovt(txn!KTs zffZXoE?4^z)f$Z6iQU$V{sB#QUeRmmeyyYIO(4-w$(5iZO0~aJiMYnPJJsPZ>hpRI zCr&i2WK|6TVPF_Y9=^M@I>hc8Jh}Rm?VVz^-m1FNl9E+*Q&s0{fjgxlIdbIYHt>qK z?$n&*si-~yTZ378A|dAXKQPR&eQcd}A3OjU8@s`ta|dG#qYwM|()$Txq^h42?}OS& zNm)^KI*i^NO{>f0w9iLQybpkhI^$UDiBm6}3R$R5@{vZE-va=iM-p~g0g`3_0000< KMNUMnLSTZL7*c!y literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/vorkath.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/vorkath.png new file mode 100644 index 0000000000000000000000000000000000000000..03bcc75ed4c073de3fa989e659208d3211c320fa GIT binary patch literal 1041 zcmV+s1n&EZP)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZR1`&)qIb0filW3TaKkGSmhRw9Kq^B- zSWVoH1NJOiuS|NScdtaE5j%09f@GkAU=r8_d|=P(x-Pq)d9qR@P*vdg$$r^?V4rB3 zQ>yH&YJ}=*1(3e}FC-KS+Vh)ifn}HM_4wGfV;lZ}pQeVu-*pB$cRfYNLyu9TyNQJT zgd-ukw`?SnD{|uWF*K(}xFKvq_SheeN&%NkCl-qlZHSVd$?*8@T`bPcaU4OZtCOZ!j9-UF@Ok~rq-TgkBU~7}2=ESYaD_m*Y!MDe*th>hempV2gWVhG z-n@|uiL0EKFjlcZ-P-l+Y*di4;IFIY{SV*6vMf@m8)P%t6-~5EV03hpt(&*-a8D0C zTestO>tu{PJ9jkm=j0+avqhZ267_nTYnc-9)(+w!57X1v$z`%!NhEBlL$}H+A}ER? z04>ciUO)5^N!5qLr7>TI?UEwuuAy&ALyQdhY>qA~$nqCymVOuk2)3=`>9^m-p=pR!rp6xthZCigXL>ThY%Wd7oI_Jp$_plDp+I72kf}@K zHs|vqa>!&fZvhgeKs9LH(i zs{MgV@?};bB9p+W;!TsW<3|auT~DB~8F4r%%uFGQ;8iW0ULTS64rb$Rc)UJLU^;Es z9H0D~**yaA@u!D*w6?&+#3f#66fkFbtusWeuF#a9VDhIQP^<-fEphff`^4S5>6f1$ zA#Iqv)E*{v`6|l5w~UOP$DwL8Hnh^BySUG}Kzu$$CUyO;1l|G|96ZOFlLJiU=17hw zQC)7nIdY7GQNW3d_?kAxZe&R&uQ7P`oQ*wI)om+ZG(}MYKo-E|tL5dxpK-sDX6XCx zFp33&^+8P2B%Mi<)O4==@-t>WW5c^eWcZFJ5RshS!*j&~>glhUX(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZoDVR4 z5Vw$M{1bx@CXg63YBa`Zh&(XjlNi&v7ukbb41cB>T8Ci>5gQy6b%Sn^u3guAd|8{j zt~9X3_)Si7Z_Y30`}>}Ae&;A6!heY>{%14;VzEUKk&0Fc$&%m##O{`$G(`zZu#nBB zqN*yot{42_N;gszMM}(Vv-sn{#==iz?ZX;sh$ z&=_n08c}RIu?RHPPUCdg3uj{1;H6kuXK?bJXLx8sb6I~J5Vy-Ar;bM?5D3UCkK1L} zgPQD%Iv3PpWrJJZ-P7SAgQTa7tWq`0(lua(UrEIK|UnjdJ*@RYdOzv32vk%(~X&_dm+mvL6_^zn6*6i=9<0 zOyQ$gIG?U?ba#N+e4cQqiRG(rAg0OLUnUv5-aydrq{hDnk`y@>VQQ=gV)SWHWe{3)zd;&7j|N%rx^b`%dg`pbiIb|Bj5AW34^TZVOedI)U{#OJa?MZ zbQ<8`e0rPbvoI(k$wjXnke-gmq`hUGYzo$i(_UR{wcrY`K(maV`a=F0?ve2;!wY89 zoy?+mnVGY|YBW`j9qN`n9lPYnq5b0ZILe(1R~mL!lFoU_3lw&>Z!q`8 zt9T7(DM6wffz9&BDq?OfN8U6^-O6))Vw!{9?Iel1x%^sk>h|evgZ6HbbPf zm02ajOYa?HC~>h6oO$I-f&ijc+vWL=C&XpAMAOvL^z2y5!dXO8VsRh^ytvejX2l03 f!QFSd(f%C(CE^4kfX!(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZRCwC7l}~RQRTRa4cRVv0XFSO`iQP11+2;j zv0=fkQNIEUmhD#n!3GH-byJX7psJ`v3KXyCqA0E$CvDRvj_t8M9(x`OVz7wqno7OW zlis}h=V;D7?_E(!@ghy*1=~OX5xFKJs%NeB_}2d8QzDY-Tc;mIBGQ*PrIZZw1N%xT zcis(Z zc9tujzDKFJiD?)tEH3utOz(H-I1WK8K-YD?{`M9ifa1)lQ=k;3?QOpN<|Y6a-hGEK z3=xsh0d!qQL|9sWmE~*}!?JjE_ctPTj4L}Ee3DM{QEr7N7z6BEp&T=Q*BA zp_IakMxnfqdh!SW&$Os&I!(Vp5Cm)&ixf6C4$1itMM|lYB2rdLP2T?=Bp z4(pqWxn!H{7a5Y_9@)htTbr8z50p}wk(vx#1FjZ|J1k_TF@qLA{5+0#M`yj(q$3({ z1Vy&232xrL*Uc&XpSS2LfIE>S3`1tTD%a}?Ui)y2rJTk0kA3{(FJW00v&m#P=iJCL z?OJAjo|@;OM(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ-Z%IT!RCwC7ls`y9Q543%YleRaCA5edY7q&7B*|)%rf6zxi)w9ZaSei&7N^EA zXb2i|wHktGipZ^{p(GG2Qy)GbTJg1X<@FMuR`1c0H$Th#pe%X4s{qs!rh<4=FSGP z=P$TEH842Z`{!Tu2mq;rD#9^0)>oY<0 z+T+w5q%{OkZg-!gEnp15g%nIAR_OJ(qW?1NHCL(Qnmv+Tcjax@xM zJ1QFWx>~^7yJEmouB;ZI8?R~sq0qQmKt5kk3#jRuT0lkX{uiN>b~nu)8C#v)vzRo2B=I=_OgnuXK0|`h00000NkvXXu0mjf^mfC= literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/overall.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/overall.png new file mode 100644 index 0000000000000000000000000000000000000000..50e4458e8ebf0a7d52189cf1113ed9558bd59c46 GIT binary patch literal 802 zcmV+-1Ks?IP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGf6951U69E94oEQKA0=G#-K~y+TeN;Vf z6G0Gtd!Kgh96NFF#ZD1L5hPUsaYvKRQ5?3UvGv#Pt3bq?&VaDtb7IJ30GdA8C>lE9XNY%vCJwQKMSjcB<8v zk{V-ofefUUUKyew0OuS54+^(&Xk;iBE0vW^$Jx4Q+m4)keQzy11UArXx*8eeR$JxoTbT0ELs*ORTW8`LU55YL`*?^1nz$VHO!JhYNpHNED?qg zf-sZ^8$O;M!sY&1o*1=&&vtF>d8QyUr=0>2KAavReyplJhTJc*&k5tWs)#1bgUAdq3s+^;onGh0_A%cX%rz6D?N92b&yOEps zkcfTsPru~!-0!3$%JV5P7D%KC;%uagsTy9H0&9O?4?#KO)FSfO66anKS-mt(ixG`g zIxaigdjJ_A2}t45HzY#5U?oM(=1POhPyZB1_roYeV5?B{!9JR%Ar3iGPkvA2bByVf gP$1?#3YuL14Zb;SDz!i2Z2$lO07*qoM6N<$g3vTwBLDyZ literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/skill_icons_small/bounty hunter - hunter.png b/runelite-client/src/main/resources/skill_icons_small/bounty_hunter_hunter.png similarity index 100% rename from runelite-client/src/main/resources/skill_icons_small/bounty hunter - hunter.png rename to runelite-client/src/main/resources/skill_icons_small/bounty_hunter_hunter.png diff --git a/runelite-client/src/main/resources/skill_icons_small/bounty hunter - rogue.png b/runelite-client/src/main/resources/skill_icons_small/bounty_hunter_rogue.png similarity index 100% rename from runelite-client/src/main/resources/skill_icons_small/bounty hunter - rogue.png rename to runelite-client/src/main/resources/skill_icons_small/bounty_hunter_rogue.png diff --git a/runelite-client/src/main/resources/skill_icons_small/clue scrolls (all).png b/runelite-client/src/main/resources/skill_icons_small/clue_scroll_all.png similarity index 100% rename from runelite-client/src/main/resources/skill_icons_small/clue scrolls (all).png rename to runelite-client/src/main/resources/skill_icons_small/clue_scroll_all.png diff --git a/runelite-client/src/main/resources/skill_icons_small/last man standing.png b/runelite-client/src/main/resources/skill_icons_small/last_man_standing.png similarity index 100% rename from runelite-client/src/main/resources/skill_icons_small/last man standing.png rename to runelite-client/src/main/resources/skill_icons_small/last_man_standing.png diff --git a/runelite-client/src/main/resources/skill_icons_small/league points.png b/runelite-client/src/main/resources/skill_icons_small/league_points.png similarity index 100% rename from runelite-client/src/main/resources/skill_icons_small/league points.png rename to runelite-client/src/main/resources/skill_icons_small/league_points.png diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/hiscore/HiscorePanelTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/hiscore/HiscorePanelTest.java index c814cbd6a7..3d5255f088 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/hiscore/HiscorePanelTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/hiscore/HiscorePanelTest.java @@ -24,6 +24,8 @@ */ package net.runelite.client.plugins.hiscore; +import static net.runelite.client.plugins.hiscore.HiscorePanel.formatLevel; +import static org.junit.Assert.assertEquals; import org.junit.Test; public class HiscorePanelTest @@ -35,4 +37,14 @@ public class HiscorePanelTest { }); } + + @Test + public void testFormatLevel() + { + assertEquals("398", formatLevel(398)); + assertEquals("5000", formatLevel(5000)); + assertEquals("7682", formatLevel(7682)); + assertEquals("12k", formatLevel(12398)); + assertEquals("219k", formatLevel(219824)); + } } From af4fd1f6fe58adcbd8d00f7a12b22963de069768 Mon Sep 17 00:00:00 2001 From: Sam Edelsten Date: Sun, 5 Jan 2020 17:30:48 +0000 Subject: [PATCH 53/64] add foods to skill calculator add missing foods to skill_cooking.json --- .../skillcalculator/skill_cooking.json | 218 ++++++++++++++++-- 1 file changed, 205 insertions(+), 13 deletions(-) diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_cooking.json b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_cooking.json index ac87de4298..6ddb07cbaf 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_cooking.json +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_cooking.json @@ -1,5 +1,11 @@ { "actions": [ + { + "level": 1, + "icon": 9436, + "name": "Sinew", + "xp": 3 + }, { "level": 1, "icon": 315, @@ -24,24 +30,36 @@ "name": "Cooked Rabbit", "xp": 30 }, + { + "level": 1, + "icon": 319, + "name": "Anchovies", + "xp": 30 + }, { "level": 1, "icon": 325, "name": "Sardine", "xp": 40 }, + { + "level": 1, + "icon": 3146, + "name": "Poison Karambwan", + "xp": 80 + }, + { + "level": 1, + "icon": 1861, + "name": "Ugthanki Meat", + "xp": 40 + }, { "level": 1, "icon": 2309, "name": "Bread", "xp": 40 }, - { - "level": 3, - "icon": 9436, - "name": "Sinew", - "xp": 3 - }, { "level": 5, "icon": 347, @@ -70,7 +88,7 @@ "level": 9, "icon": 7072, "name": "Spicy Sauce", - "xp": 24 + "xp": 25 }, { "level": 10, @@ -108,6 +126,12 @@ "name": "Thin Snail Meat", "xp": 70 }, + { + "level": 12, + "icon": 2213, + "name": "Spicy Crunchies", + "xp": 100 + }, { "level": 13, "icon": 7078, @@ -126,24 +150,36 @@ "name": "Worm Crunchies", "xp": 104 }, - { - "level": 15, - "icon": 319, - "name": "Anchovies", - "xp": 30 - }, { "level": 15, "icon": 333, "name": "Trout", "xp": 70 }, + { + "level": 16, + "icon": 6293, + "name": "Spider on stick", + "xp": 80 + }, + { + "level": 16, + "icon": 6295, + "name": "Spider on shaft", + "xp": 80 + }, { "level": 16, "icon": 7223, "name": "Roast Rabbit", "xp": 72.5 }, + { + "level": 16, + "icon": 2209, + "name": "Chocchip crunchies", + "xp": 100 + }, { "level": 17, "icon": 3371, @@ -186,12 +222,30 @@ "name": "Pike", "xp": 80 }, + { + "level": 20, + "icon": 712, + "name": "Cup of tea", + "xp": 52 + }, { "level": 21, "icon": 9988, "name": "Roast Beast Meat", "xp": 82.5 }, + { + "level": 21, + "icon": 7521, + "name": "Cooked Crab Meat", + "xp": 100 + }, + { + "level": 21, + "icon": 2130, + "name": "Pot of cream", + "xp": 18 + }, { "level": 22, "icon": 3373, @@ -264,6 +318,18 @@ "name": "Mud Pie", "xp": 128 }, + { + "level": 29, + "icon": 1909, + "name": "Greenman's ale", + "xp": 281 + }, + { + "level": 29, + "icon": 2259, + "name": "Cheese and Tomato Batta", + "xp": 158 + }, { "level": 30, "icon": 361, @@ -288,18 +354,42 @@ "name": "Cooked Karambwan", "xp": 190 }, + { + "level": 30, + "icon": 2878, + "name": "Roasted Chompy", + "xp": 100 + }, + { + "level": 31, + "icon": 7530, + "name": "Fishcake", + "xp": 100 + }, { "level": 32, "icon": 2092, "name": "Drunk Dragon", "xp": 160 }, + { + "level": 33, + "icon": 2074, + "name": "Choc Saturday", + "xp": 170 + }, { "level": 34, "icon": 7178, "name": "Garden Pie", "xp": 138 }, + { + "level": 34, + "icon": 1907, + "name": "Wizard's mind bomb", + "xp": 314 + }, { "level": 35, "icon": 1993, @@ -318,6 +408,12 @@ "name": "Rainbow Fish", "xp": 110 }, + { + "level": 35, + "icon": 2195, + "name": "Veg ball", + "xp": 175 + }, { "level": 37, "icon": 2064, @@ -330,12 +426,24 @@ "name": "Cave Eel", "xp": 115 }, + { + "level": 38, + "icon": 6697, + "name": "Pat of butter", + "xp": 40.5 + }, { "level": 39, "icon": 1911, "name": "Dragon Bitter", "xp": 347 }, + { + "level": 39, + "icon": 6703, + "name": "Potato with butter", + "xp": 40 + }, { "level": 40, "icon": 379, @@ -348,18 +456,36 @@ "name": "Cake", "xp": 180 }, + { + "level": 40, + "icon": 2187, + "name": "Tangled toad's legs", + "xp": 185 + }, { "level": 41, "icon": 7054, "name": "Chilli Potato", "xp": 165.5 }, + { + "level": 41, + "icon": 7568, + "name": "Cooked Jubbly", + "xp": 160 + }, { "level": 42, "icon": 2185, "name": "Chocolate Bomb", "xp": 190 }, + { + "level": 42, + "icon": 7084, + "name": "Fried Onions", + "xp": 60 + }, { "level": 43, "icon": 365, @@ -384,12 +510,36 @@ "name": "Meat Pizza", "xp": 169 }, + { + "level": 46, + "icon": 7082, + "name": "Fried Mushrooms", + "xp": 60 + }, { "level": 47, "icon": 7188, "name": "Fish Pie", "xp": 164 }, + { + "level": 47, + "icon": 6705, + "name": "Potato with cheese", + "xp": 40 + }, + { + "level": 48, + "icon": 1985, + "name": "Cheese", + "xp": 64 + }, + { + "level": 49, + "icon": 5751, + "name": "Axeman's folly", + "xp": 413 + }, { "level": 50, "icon": 2343, @@ -414,18 +564,48 @@ "name": "Botanical Pie", "xp": 180 }, + { + "level": 53, + "icon": 2149, + "name": "Lava Eel", + "xp": 30 + }, + { + "level": 54, + "icon": 5755, + "name": "Chef's Delight", + "xp": 446 + }, { "level": 55, "icon": 2297, "name": "Anchovy Pizza", "xp": 182 }, + { + "level": 57, + "icon": 7066, + "name": "Mushroom & onion", + "xp": 120 + }, { "level": 58, "icon": 1883, "name": "Ugthanki Kebab (Fresh)", "xp": 80 }, + { + "level": 58, + "icon": 1865, + "name": "Pitta Bread", + "xp": 40 + }, + { + "level": 59, + "icon": 5759, + "name": "Slayer's respite", + "xp": 479 + }, { "level": 60, "icon": 2011, @@ -456,6 +636,12 @@ "name": "Pineapple Pizza", "xp": 188 }, + { + "level": 65, + "icon": 245, + "name": "Wine of Zamorak", + "xp": 200 + }, { "level": 67, "icon": 7068, @@ -474,6 +660,12 @@ "name": "Admiral Pie", "xp": 210 }, + { + "level": 72, + "icon": 13339, + "name": "Sacred Eel", + "xp": 109 + }, { "level": 73, "icon": 22795, From 456f5dc21666ea5df91292f6de9d50c56e7eaeff Mon Sep 17 00:00:00 2001 From: Sam Edelsten Date: Sun, 5 Jan 2020 17:31:14 +0000 Subject: [PATCH 54/64] add vscode files to gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a4f1ba7d02..47811495bb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ project.properties .idea/ .project .settings/ -.classpath \ No newline at end of file +.classpath +.vscode +.factorypath \ No newline at end of file From 90ce907ade4342029691a35bc6fc3722b3e7a91d Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Mon, 6 Jan 2020 13:38:30 +0000 Subject: [PATCH 55/64] hiscore api: add Callisto and Cerberus --- .../net/runelite/http/api/hiscore/HiscoreResultBuilder.java | 4 ++-- .../net/runelite/http/service/hiscore/HiscoreServiceTest.java | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreResultBuilder.java b/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreResultBuilder.java index b9efab4e20..3ea03502bd 100644 --- a/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreResultBuilder.java +++ b/http-api/src/main/java/net/runelite/http/api/hiscore/HiscoreResultBuilder.java @@ -94,8 +94,8 @@ class HiscoreResultBuilder hiscoreResult.setAlchemicalHydra(skills.get(index++)); hiscoreResult.setBarrowsChests(skills.get(index++)); hiscoreResult.setBryophyta(skills.get(index++)); -// hiscoreResult.setCallisto(skills.get(index++)); -// hiscoreResult.setCerberus(skills.get(index++)); + hiscoreResult.setCallisto(skills.get(index++)); + hiscoreResult.setCerberus(skills.get(index++)); hiscoreResult.setChambersOfXeric(skills.get(index++)); hiscoreResult.setChambersOfXericChallengeMode(skills.get(index++)); hiscoreResult.setChaosElemental(skills.get(index++)); diff --git a/http-service/src/test/java/net/runelite/http/service/hiscore/HiscoreServiceTest.java b/http-service/src/test/java/net/runelite/http/service/hiscore/HiscoreServiceTest.java index 5f96f300b5..ee91740e17 100644 --- a/http-service/src/test/java/net/runelite/http/service/hiscore/HiscoreServiceTest.java +++ b/http-service/src/test/java/net/runelite/http/service/hiscore/HiscoreServiceTest.java @@ -75,6 +75,8 @@ public class HiscoreServiceTest + "15020,388\n" + "50463,147\n" + "-1,-1\n" + + "92357,1\n" + + "22758,637\n" + "22744,107\n" + "-1,-1\n" + "20150,17\n" @@ -149,6 +151,7 @@ public class HiscoreServiceTest Assert.assertEquals(-1, result.getLastManStanding().getLevel()); Assert.assertEquals(2460, result.getLeaguePoints().getLevel()); Assert.assertEquals(37, result.getAbyssalSire().getLevel()); + Assert.assertEquals(92357, result.getCallisto().getRank()); Assert.assertEquals(5847, result.getZulrah().getLevel()); } From bf0ff69e07eaebd12ada127ae55113456c19209d Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 7 Jan 2020 11:15:24 -0500 Subject: [PATCH 56/64] raids plugin: add option to show loot value Co-authored-by: melkypie --- .../client/plugins/raids/RaidsConfig.java | 11 ++++ .../client/plugins/raids/RaidsPlugin.java | 51 +++++++++++++++++++ .../client/plugins/raids/RaidsPluginTest.java | 46 +++++++++++++++++ 3 files changed, 108 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsConfig.java index 10612e483d..9d6fd5d8c3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsConfig.java @@ -162,4 +162,15 @@ public interface RaidsConfig extends Config { return false; } + + @ConfigItem( + position = 12, + keyName = "showLootValue", + name = "Show Loot Value", + description = "Shows the value of your loot at the end of a raid" + ) + default boolean showLootValue() + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java index 81881bd28b..1c581a3f26 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java @@ -47,6 +47,8 @@ import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.InstanceTemplates; +import net.runelite.api.InventoryID; +import net.runelite.api.ItemContainer; import net.runelite.api.MenuAction; import net.runelite.api.MessageNode; import net.runelite.api.NullObjectID; @@ -58,6 +60,8 @@ import net.runelite.api.VarPlayer; import net.runelite.api.Varbits; import net.runelite.api.events.ChatMessage; import net.runelite.api.events.VarbitChanged; +import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.widgets.WidgetID; import net.runelite.client.callback.ClientThread; import net.runelite.client.chat.ChatColorType; import net.runelite.client.chat.ChatCommandManager; @@ -69,6 +73,7 @@ import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ChatInput; import net.runelite.client.events.ConfigChanged; import net.runelite.client.events.OverlayMenuClicked; +import net.runelite.client.game.ItemManager; import net.runelite.client.game.SpriteManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; @@ -76,6 +81,7 @@ import net.runelite.client.plugins.raids.solver.Layout; import net.runelite.client.plugins.raids.solver.LayoutSolver; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import net.runelite.client.util.QuantityFormatter; import net.runelite.client.util.Text; import static net.runelite.client.util.Text.sanitize; import net.runelite.client.ws.PartyMember; @@ -144,6 +150,9 @@ public class RaidsPlugin extends Plugin @Inject private ScheduledExecutorService scheduledExecutorService; + @Inject + private ItemManager itemManager; + @Getter private final Set roomWhitelist = new HashSet(); @@ -163,6 +172,8 @@ public class RaidsPlugin extends Plugin @Getter private boolean inRaidChambers; + private boolean chestOpened; + private RaidsTimer timer; @Provides @@ -195,6 +206,7 @@ public class RaidsPlugin extends Plugin inRaidChambers = false; raid = null; timer = null; + chestOpened = false; } @Subscribe @@ -215,6 +227,44 @@ public class RaidsPlugin extends Plugin clientThread.invokeLater(() -> checkRaidPresence(true)); } + @Subscribe + public void onWidgetLoaded(WidgetLoaded event) + { + if (event.getGroupId() != WidgetID.CHAMBERS_OF_XERIC_REWARD_GROUP_ID || + !config.showLootValue() || + chestOpened) + { + return; + } + + chestOpened = true; + + ItemContainer rewardItemContainer = client.getItemContainer(InventoryID.CHAMBERS_OF_XERIC_CHEST); + if (rewardItemContainer == null) + { + return; + } + + long totalValue = Arrays.stream(rewardItemContainer.getItems()) + .filter(item -> item.getId() > -1) + .mapToLong(item -> (long) itemManager.getItemPrice(item.getId()) * item.getQuantity()) + .sum(); + + String chatMessage = new ChatMessageBuilder() + .append(ChatColorType.NORMAL) + .append("Your loot is worth around ") + .append(ChatColorType.HIGHLIGHT) + .append(QuantityFormatter.formatNumber(totalValue)) + .append(ChatColorType.NORMAL) + .append(" coins.") + .build(); + + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.FRIENDSCHATNOTIFICATION) + .runeLiteFormattedMessage(chatMessage) + .build()); + } + @Subscribe public void onVarbitChanged(VarbitChanged event) { @@ -309,6 +359,7 @@ public class RaidsPlugin extends Plugin if (inRaidChambers) { raid = buildRaid(); + chestOpened = false; if (raid == null) { diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/raids/RaidsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/raids/RaidsPluginTest.java index 09c432f7bd..23c71795c0 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/raids/RaidsPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/raids/RaidsPluginTest.java @@ -30,15 +30,27 @@ import com.google.inject.testing.fieldbinder.Bind; import com.google.inject.testing.fieldbinder.BoundFieldModule; import java.util.concurrent.ScheduledExecutorService; import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import net.runelite.api.ItemID; +import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.widgets.WidgetID; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; import net.runelite.client.config.ChatColorConfig; import net.runelite.client.config.RuneLiteConfig; +import net.runelite.client.game.ItemManager; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; @@ -61,6 +73,14 @@ public class RaidsPluginTest @Bind RuneLiteConfig runeliteConfig; + @Mock + @Bind + ItemManager itemManager; + + @Mock + @Bind + ChatMessageManager chatMessageManager; + @Mock @Bind RaidsConfig raidsConfig; @@ -119,4 +139,30 @@ public class RaidsPluginTest assertFalse(raidsPlugin.getRotationMatches()); } + + @Test + public void testLootValue() + { + when(raidsConfig.showLootValue()).thenReturn(true); + + ItemContainer itemContainer = mock(ItemContainer.class); + when(itemContainer.getItems()).thenReturn(new Item[]{ + new Item(ItemID.TWISTED_BOW, 1), + new Item(ItemID.PURE_ESSENCE, 42) + }); + when(client.getItemContainer(InventoryID.CHAMBERS_OF_XERIC_CHEST)).thenReturn(itemContainer); + + when(itemManager.getItemPrice(ItemID.TWISTED_BOW)).thenReturn(1_100_000_000); + when(itemManager.getItemPrice(ItemID.PURE_ESSENCE)).thenReturn(6); + + WidgetLoaded widgetLoaded = new WidgetLoaded(); + widgetLoaded.setGroupId(WidgetID.CHAMBERS_OF_XERIC_REWARD_GROUP_ID); + raidsPlugin.onWidgetLoaded(widgetLoaded); + + ArgumentCaptor captor = ArgumentCaptor.forClass(QueuedMessage.class); + verify(chatMessageManager).queue(captor.capture()); + + QueuedMessage queuedMessage = captor.getValue(); + assertEquals("Your loot is worth around 1,100,000,252 coins.", queuedMessage.getRuneLiteFormattedMessage()); + } } From 358703f30719b6785078dc827db42b937dbc7902 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Wed, 8 Jan 2020 11:41:50 -0700 Subject: [PATCH 57/64] banktags: move new tagtab button to the bottom of the list To make way for the equipment button --- .../plugins/banktags/tabs/TabInterface.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabInterface.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabInterface.java index 61b992036e..4126e3805f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabInterface.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/TabInterface.java @@ -859,19 +859,21 @@ public class TabInterface --maxTabs; } - if (currentTabIndex + direction >= tabManager.size() || currentTabIndex + direction < 0) + int proposedIndex = currentTabIndex + direction; + int numTabs = tabManager.size() + 1; + + if (proposedIndex >= numTabs || proposedIndex < 0) { currentTabIndex = 0; } - - if ((tabManager.size() - (currentTabIndex + direction) >= maxTabs) && (currentTabIndex + direction > -1)) + else if (numTabs - proposedIndex >= maxTabs) { - currentTabIndex += direction; + currentTabIndex = proposedIndex; } - else if (maxTabs < tabManager.size() && tabManager.size() - (currentTabIndex + direction) < maxTabs) + else if (maxTabs < numTabs && numTabs - proposedIndex < maxTabs) { // Edge case when only 1 tab displays instead of up to maxTabs when one is deleted at the end of the list - currentTabIndex += direction; + currentTabIndex = proposedIndex; scrollTab(-1); } @@ -952,7 +954,7 @@ public class TabInterface { int y = bounds.y + MARGIN + BUTTON_HEIGHT; - if (maxTabs >= tabManager.size()) + if (maxTabs > tabManager.size()) { currentTabIndex = 0; } @@ -978,6 +980,8 @@ public class TabInterface y += TAB_HEIGHT + MARGIN; } + updateWidget(newTab, y); + boolean hidden = !(tabManager.size() > 0); upButton.setHidden(hidden); From dc7d1dfb26975d701553f7497e07167f28ad879e Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Thu, 9 Jan 2020 11:13:13 +0000 Subject: [PATCH 58/64] Revert "client: Add Music Track Indicator plugin" This reverts commit f6e5ef1f3d13d40713ca28ade53d3b9781fb8bbf --- .../musicindicator/MusicIndicatorPlugin.java | 204 ------------------ 1 file changed, 204 deletions(-) delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/musicindicator/MusicIndicatorPlugin.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/musicindicator/MusicIndicatorPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/musicindicator/MusicIndicatorPlugin.java deleted file mode 100644 index 25584f3a88..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/musicindicator/MusicIndicatorPlugin.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2019, Shaun Dreclin - * 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.musicindicator; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import javax.inject.Inject; -import net.runelite.api.ChatMessageType; -import net.runelite.api.Client; -import net.runelite.api.EnumComposition; -import net.runelite.api.EnumID; -import net.runelite.api.VarPlayer; -import net.runelite.api.events.GameStateChanged; -import net.runelite.api.events.GameTick; -import net.runelite.api.events.VarbitChanged; -import net.runelite.client.chat.ChatColorType; -import net.runelite.client.chat.ChatMessageBuilder; -import net.runelite.client.chat.ChatMessageManager; -import net.runelite.client.chat.QueuedMessage; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; - -@PluginDescriptor( - name = "Music Track Indicator", - description = "Show chat notifications when unlocking music tracks" -) -public class MusicIndicatorPlugin extends Plugin -{ - private static final List MUSIC_TRACK_VARPS = ImmutableList.of( - VarPlayer.MUSIC_TRACKS_UNLOCKED_1, VarPlayer.MUSIC_TRACKS_UNLOCKED_2, VarPlayer.MUSIC_TRACKS_UNLOCKED_3, - VarPlayer.MUSIC_TRACKS_UNLOCKED_4, VarPlayer.MUSIC_TRACKS_UNLOCKED_5, VarPlayer.MUSIC_TRACKS_UNLOCKED_6, - VarPlayer.MUSIC_TRACKS_UNLOCKED_7, VarPlayer.MUSIC_TRACKS_UNLOCKED_8, VarPlayer.MUSIC_TRACKS_UNLOCKED_9, - VarPlayer.MUSIC_TRACKS_UNLOCKED_10, VarPlayer.MUSIC_TRACKS_UNLOCKED_11, VarPlayer.MUSIC_TRACKS_UNLOCKED_12, - VarPlayer.MUSIC_TRACKS_UNLOCKED_13, VarPlayer.MUSIC_TRACKS_UNLOCKED_14, VarPlayer.MUSIC_TRACKS_UNLOCKED_15, - VarPlayer.MUSIC_TRACKS_UNLOCKED_16, VarPlayer.MUSIC_TRACKS_UNLOCKED_17, VarPlayer.MUSIC_TRACKS_UNLOCKED_18, - VarPlayer.MUSIC_TRACKS_UNLOCKED_19 - ); - - private static final Map VARP_INDEX_TO_VARPLAYER = MUSIC_TRACK_VARPS.stream() - .collect(Collectors.collectingAndThen(Collectors.toMap(VarPlayer::getId, Function.identity()), - ImmutableMap::copyOf)); - - @Inject - private Client client; - - @Inject - private ChatMessageManager chatMessageManager; - - // Mapping of relevant varps to their values, used to compare against new values - private final Map musicTrackVarpValues = new HashMap<>(); - - private boolean loggingIn; - - @Override - public void startUp() - { - loggingIn = true; - } - - @Override - public void shutDown() - { - musicTrackVarpValues.clear(); - } - - @Subscribe - public void onGameStateChanged(GameStateChanged event) - { - switch (event.getGameState()) - { - case LOGGING_IN: - case CONNECTION_LOST: - case HOPPING: - musicTrackVarpValues.clear(); - loggingIn = true; - } - } - - @Subscribe - public void onGameTick(GameTick event) - { - if (!loggingIn) - { - return; - } - - loggingIn = false; - - for (VarPlayer musicTrackVarp : MUSIC_TRACK_VARPS) - { - int value = client.getVar(musicTrackVarp); - musicTrackVarpValues.put(musicTrackVarp, value); - } - } - - @Subscribe - public void onVarbitChanged(VarbitChanged event) - { - int idx = event.getIndex(); - - VarPlayer varPlayer = VARP_INDEX_TO_VARPLAYER.get(idx); - if (varPlayer == null) - { - return; - } - - // Old varplayer values have not been initialized yet - if (musicTrackVarpValues.isEmpty()) - { - return; - } - - assert musicTrackVarpValues.containsKey(varPlayer); - - int newValue = client.getVar(varPlayer); - int oldValue = musicTrackVarpValues.put(varPlayer, newValue); - int musicTracksUnlocked = ~oldValue & newValue; - - if (musicTracksUnlocked == 0) - { - return; - } - - final EnumComposition names = client.getEnum(EnumID.MUSIC_TRACK_NAMES); - final int varpId = MUSIC_TRACK_VARPS.indexOf(varPlayer) + 1; - - for (int bit = 0; bit < Integer.SIZE; ++bit) - { - if ((musicTracksUnlocked & (1 << bit)) == 0) - { - continue; - } - - int musicTrackId = getTrackId(varpId, bit); - String musicTrackName = names.getStringValue(musicTrackId); - - sendChatMessage("You have unlocked a new music track: " + musicTrackName + "."); - } - } - - /** - * Get the id for a track identified by the given varp and a bit index - * @param variableId - * @param bit - * @return - */ - private int getTrackId(int variableId, int bit) - { - // values are packed into a coordgrid - int packed = (variableId << 14) | bit; - EnumComposition ids = client.getEnum(EnumID.MUSIC_TRACK_IDS); - for (int key : ids.getKeys()) - { - int value = ids.getIntValue(key); - if (value == packed) - { - return key; - } - } - return -1; - } - - private void sendChatMessage(String chatMessage) - { - final String message = new ChatMessageBuilder() - .append(ChatColorType.HIGHLIGHT) - .append(chatMessage) - .build(); - - chatMessageManager.queue( - QueuedMessage.builder() - .type(ChatMessageType.CONSOLE) - .runeLiteFormattedMessage(message) - .build()); - } -} From d1f588b76263949670d5331dd586035c364e2ba3 Mon Sep 17 00:00:00 2001 From: RuneLite Cache-Code Autoupdater Date: Thu, 9 Jan 2020 11:33:00 +0000 Subject: [PATCH 59/64] Update Object IDs to 2020-01-09-rev182 --- .../java/net/runelite/api/NullObjectID.java | 25 ---------------- .../main/java/net/runelite/api/ObjectID.java | 29 ------------------- 2 files changed, 54 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/NullObjectID.java b/runelite-api/src/main/java/net/runelite/api/NullObjectID.java index e9f06cb9ad..0e2fb3622a 100644 --- a/runelite-api/src/main/java/net/runelite/api/NullObjectID.java +++ b/runelite-api/src/main/java/net/runelite/api/NullObjectID.java @@ -12856,7 +12856,6 @@ public final class NullObjectID public static final int NULL_27103 = 27103; public static final int NULL_27104 = 27104; public static final int NULL_27105 = 27105; - public static final int NULL_27106 = 27106; public static final int NULL_27111 = 27111; public static final int NULL_27112 = 27112; public static final int NULL_27113 = 27113; @@ -18074,29 +18073,5 @@ public final class NullObjectID public static final int NULL_37481 = 37481; public static final int NULL_37490 = 37490; public static final int NULL_37491 = 37491; - public static final int NULL_37561 = 37561; - public static final int NULL_37562 = 37562; - public static final int NULL_37563 = 37563; - public static final int NULL_37564 = 37564; - public static final int NULL_37565 = 37565; - public static final int NULL_37566 = 37566; - public static final int NULL_37567 = 37567; - public static final int NULL_37568 = 37568; - public static final int NULL_37569 = 37569; - public static final int NULL_37573 = 37573; - public static final int NULL_37574 = 37574; - public static final int NULL_37575 = 37575; - public static final int NULL_37576 = 37576; - public static final int NULL_37577 = 37577; - public static final int NULL_37578 = 37578; - public static final int NULL_37579 = 37579; - public static final int NULL_37580 = 37580; - public static final int NULL_37581 = 37581; - public static final int NULL_37582 = 37582; - public static final int NULL_37583 = 37583; - public static final int NULL_37584 = 37584; - public static final int NULL_37585 = 37585; - public static final int NULL_37597 = 37597; - public static final int NULL_37598 = 37598; /* This file is automatically generated. Do not edit. */ } diff --git a/runelite-api/src/main/java/net/runelite/api/ObjectID.java b/runelite-api/src/main/java/net/runelite/api/ObjectID.java index e955bdcf12..31dcfd0a06 100644 --- a/runelite-api/src/main/java/net/runelite/api/ObjectID.java +++ b/runelite-api/src/main/java/net/runelite/api/ObjectID.java @@ -14234,7 +14234,6 @@ public final class ObjectID public static final int LIGHT_27093 = 27093; public static final int CLAN_CUP_PORTAL = 27095; public static final int EXIT_PORTAL_27096 = 27096; - public static final int ANTISANTA = 27097; public static final int SOFA = 27098; public static final int CRATE_27100 = 27100; public static final int CRATES_27101 = 27101; @@ -19460,33 +19459,5 @@ public final class ObjectID public static final int ORNATE_JEWELLERY_BOX_37544 = 37544; public static final int ORNATE_JEWELLERY_BOX_37545 = 37545; public static final int ORNATE_JEWELLERY_BOX_37546 = 37546; - public static final int CELL_DOOR_37547 = 37547; - public static final int TRAPDOOR_37548 = 37548; - public static final int TRAPDOOR_37549 = 37549; - public static final int BARREL_OF_FESTIVE_BRANCHES = 37550; - public static final int SACK_OF_FLOUR = 37551; - public static final int SACK_OF_GINGER = 37552; - public static final int BUCKET_OF_EGGS = 37553; - public static final int LADDER_37554 = 37554; - public static final int DOOR_37555 = 37555; - public static final int DOOR_37556 = 37556; - public static final int FIREPLACE_37557 = 37557; - public static final int LOGS_37558 = 37558; - public static final int SHELVES_37559 = 37559; - public static final int SANTAS_SACK = 37560; - public static final int TREE_37570 = 37570; - public static final int GIFTS = 37571; - public static final int GIFTS_37572 = 37572; - public static final int TREE_37586 = 37586; - public static final int TREE_STUMP_37587 = 37587; - public static final int LEVER_37588 = 37588; - public static final int LEVER_37589 = 37589; - public static final int TABLE_37590 = 37590; - public static final int RECEIVER = 37591; - public static final int MIXER = 37592; - public static final int CONVEYOR_BELT_37593 = 37593; - public static final int HOPPER_37594 = 37594; - public static final int SUPPORT_37595 = 37595; - public static final int OVEN_37596 = 37596; /* This file is automatically generated. Do not edit. */ } From 75815a218804224240776e87e0fc84b0c81bc329 Mon Sep 17 00:00:00 2001 From: RuneLite Cache-Code Autoupdater Date: Thu, 9 Jan 2020 11:33:01 +0000 Subject: [PATCH 60/64] Update NPC IDs to 2020-01-09-rev182 --- .../src/main/java/net/runelite/api/NpcID.java | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/NpcID.java b/runelite-api/src/main/java/net/runelite/api/NpcID.java index 02ea92bb3d..bdadb5b768 100644 --- a/runelite-api/src/main/java/net/runelite/api/NpcID.java +++ b/runelite-api/src/main/java/net/runelite/api/NpcID.java @@ -993,14 +993,6 @@ public final class NpcID public static final int BANDIT_1026 = 1026; public static final int GUARD_BANDIT = 1027; public static final int BARBARIAN_GUARD = 1028; - public static final int JACK_FROST = 1029; - public static final int SANTA = 1030; - public static final int ANTISANTA = 1031; - public static final int ANTISANTA_1032 = 1032; - public static final int ICELORD_1033 = 1033; - public static final int WORRIED_BAKER = 1034; - public static final int PAUL = 1035; - public static final int MARY = 1036; public static final int SNAKE = 1037; public static final int MONKEY_1038 = 1038; public static final int ALBINO_BAT = 1039; @@ -2884,14 +2876,6 @@ public final class NpcID public static final int GOBLIN_3074 = 3074; public static final int GOBLIN_3075 = 3075; public static final int GOBLIN_3076 = 3076; - public static final int GEORGIE = 3077; - public static final int GEORGIE_3078 = 3078; - public static final int BEVERLY = 3079; - public static final int BEVERLY_3080 = 3080; - public static final int BILL = 3081; - public static final int BILL_3082 = 3082; - public static final int JESS = 3083; - public static final int JESS_3084 = 3084; public static final int PORTAL_3086 = 3086; public static final int PORTAL_3088 = 3088; public static final int BANKER_3089 = 3089; @@ -3977,7 +3961,7 @@ public final class NpcID public static final int PIERRE = 4213; public static final int HOBBES = 4214; public static final int LOUISA = 4215; - public static final int MARY_4216 = 4216; + public static final int MARY = 4216; public static final int STANFORD = 4217; public static final int GUARD_4218 = 4218; public static final int GOSSIP = 4219; @@ -7172,7 +7156,7 @@ public final class NpcID public static final int REVENANT_DARK_BEAST = 7938; public static final int REVENANT_KNIGHT = 7939; public static final int REVENANT_DRAGON = 7940; - public static final int PAUL_7941 = 7941; + public static final int PAUL = 7941; public static final int EMBLEM_TRADER_7943 = 7943; public static final int FISHING_SPOT_7946 = 7946; public static final int FISHING_SPOT_7947 = 7947; From a3d1c9b348782c54dfe7ccb892c313a07780e504 Mon Sep 17 00:00:00 2001 From: RuneLite Cache-Code Autoupdater Date: Thu, 9 Jan 2020 11:33:04 +0000 Subject: [PATCH 61/64] Update Widget IDs to 2020-01-09-rev182 Lost widget 387.16 Lost widget 387.7 Lost widget 387.8 Lost widget 387.9 Lost widget 387.10 Lost widget 387.11 Lost widget 387.12 Lost widget 387.13 Lost widget 387.14 Lost widget 387.15 Lost widget 12.10 --- .../net/runelite/api/widgets/WidgetID.java | 27 ++++++------------- .../net/runelite/api/widgets/WidgetInfo.java | 12 --------- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index d671304d99..438e6ae2e5 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -228,14 +228,14 @@ public class WidgetID static final int BANK_CONTAINER = 1; static final int INVENTORY_ITEM_CONTAINER = 3; static final int BANK_TITLE_BAR = 4; - static final int CONTENT_CONTAINER = 10; - static final int TAB_CONTAINER = 11; - static final int ITEM_CONTAINER = 13; - static final int SEARCH_BUTTON_BACKGROUND = 40; - static final int DEPOSIT_INVENTORY = 42; - static final int DEPOSIT_EQUIPMENT = 44; - static final int INCINERATOR = 46; - static final int INCINERATOR_CONFIRM = 47; + static final int CONTENT_CONTAINER = 9; + static final int TAB_CONTAINER = 10; + static final int ITEM_CONTAINER = 12; + static final int SEARCH_BUTTON_BACKGROUND = 39; + static final int DEPOSIT_INVENTORY = 41; + static final int DEPOSIT_EQUIPMENT = 43; + static final int INCINERATOR = 45; + static final int INCINERATOR_CONFIRM = 46; } static class GrandExchange @@ -279,17 +279,6 @@ public class WidgetID static class Equipment { - static final int HELMET = 6; - static final int CAPE = 7; - static final int AMULET = 8; - static final int WEAPON = 9; - static final int BODY = 10; - static final int SHIELD = 11; - static final int LEGS = 12; - static final int GLOVES = 13; - static final int BOOTS = 14; - static final int RING = 15; - static final int AMMO = 16; static final int INVENTORY_ITEM_CONTAINER = 0; } diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 4f2bcca5ce..a1354618f8 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -62,18 +62,6 @@ public enum WidgetInfo EQUIPMENT(WidgetID.EQUIPMENT_GROUP_ID, 0), EQUIPMENT_INVENTORY_ITEMS_CONTAINER(WidgetID.EQUIPMENT_INVENTORY_GROUP_ID, WidgetID.Equipment.INVENTORY_ITEM_CONTAINER), - EQUIPMENT_HELMET(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.HELMET), - EQUIPMENT_CAPE(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.CAPE), - EQUIPMENT_AMULET(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.AMULET), - EQUIPMENT_WEAPON(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.WEAPON), - EQUIPMENT_BODY(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.BODY), - EQUIPMENT_SHIELD(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.SHIELD), - EQUIPMENT_LEGS(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.LEGS), - EQUIPMENT_GLOVES(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.GLOVES), - EQUIPMENT_BOOTS(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.BOOTS), - EQUIPMENT_RING(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.RING), - EQUIPMENT_AMMO(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.AMMO), - EMOTE_WINDOW(WidgetID.EMOTES_GROUP_ID, WidgetID.Emotes.EMOTE_WINDOW), EMOTE_CONTAINER(WidgetID.EMOTES_GROUP_ID, WidgetID.Emotes.EMOTE_CONTAINER), EMOTE_SCROLLBAR(WidgetID.EMOTES_GROUP_ID, WidgetID.Emotes.EMOTE_SCROLLBAR), From 3ac681ea0859cc3bbdd9682141f1d68d790ad4b7 Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Thu, 9 Jan 2020 12:13:50 +0000 Subject: [PATCH 62/64] Release 1.6.1 --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 4 ++-- protocol-api/pom.xml | 2 +- protocol/pom.xml | 2 +- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index ccdbae13c0..ba4de9a121 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1-SNAPSHOT + 1.6.1 cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index 4f0c559724..6ee6f82127 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.1-SNAPSHOT + 1.6.1 Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index 55ea9da04f..811cac4493 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1-SNAPSHOT + 1.6.1 cache diff --git a/http-api/pom.xml b/http-api/pom.xml index 48f3a8e20e..7df7524d0a 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.1-SNAPSHOT + 1.6.1 Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index c54e4496ba..f4828cca04 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.1-SNAPSHOT + 1.6.1 Web Service diff --git a/pom.xml b/pom.xml index 16003b7af5..d6357248cb 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.1-SNAPSHOT + 1.6.1 pom RuneLite @@ -59,7 +59,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - HEAD + runelite-parent-1.6.1 diff --git a/protocol-api/pom.xml b/protocol-api/pom.xml index a5958cb2f6..385d9a3739 100644 --- a/protocol-api/pom.xml +++ b/protocol-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1-SNAPSHOT + 1.6.1 protocol-api diff --git a/protocol/pom.xml b/protocol/pom.xml index bbf06de65a..16ad84e074 100644 --- a/protocol/pom.xml +++ b/protocol/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1-SNAPSHOT + 1.6.1 protocol diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index aaa8fd7f59..142a6fef4b 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1-SNAPSHOT + 1.6.1 runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index a4d74de1be..803d0f4362 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1-SNAPSHOT + 1.6.1 client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 8a80a4443f..21e57e06b2 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1-SNAPSHOT + 1.6.1 script-assembler-plugin From 8eefd98482e1f523a99e06575e60d52cea4d047d Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Thu, 9 Jan 2020 12:13:59 +0000 Subject: [PATCH 63/64] Bump for 1.6.2-SNAPSHOT --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 4 ++-- protocol-api/pom.xml | 2 +- protocol/pom.xml | 2 +- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index ba4de9a121..46be5426a0 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1 + 1.6.2-SNAPSHOT cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index 6ee6f82127..df74131907 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.1 + 1.6.2-SNAPSHOT Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index 811cac4493..b183e75948 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1 + 1.6.2-SNAPSHOT cache diff --git a/http-api/pom.xml b/http-api/pom.xml index 7df7524d0a..b7a8349fb9 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.1 + 1.6.2-SNAPSHOT Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index f4828cca04..455b89cf47 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.1 + 1.6.2-SNAPSHOT Web Service diff --git a/pom.xml b/pom.xml index d6357248cb..bab70681e3 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.1 + 1.6.2-SNAPSHOT pom RuneLite @@ -59,7 +59,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - runelite-parent-1.6.1 + HEAD diff --git a/protocol-api/pom.xml b/protocol-api/pom.xml index 385d9a3739..b12d5134e5 100644 --- a/protocol-api/pom.xml +++ b/protocol-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1 + 1.6.2-SNAPSHOT protocol-api diff --git a/protocol/pom.xml b/protocol/pom.xml index 16ad84e074..861302c8ab 100644 --- a/protocol/pom.xml +++ b/protocol/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1 + 1.6.2-SNAPSHOT protocol diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index 142a6fef4b..507d401205 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1 + 1.6.2-SNAPSHOT runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 803d0f4362..660fbcd36a 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1 + 1.6.2-SNAPSHOT client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 21e57e06b2..c66cd0b11c 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.1 + 1.6.2-SNAPSHOT script-assembler-plugin From faacbc41601ea6c24e235046420c7295b250bfc7 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 9 Jan 2020 10:31:24 -0500 Subject: [PATCH 64/64] feed controller: catch any exception when fetching newes sources --- .../runelite/http/service/feed/FeedController.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/http-service/src/main/java/net/runelite/http/service/feed/FeedController.java b/http-service/src/main/java/net/runelite/http/service/feed/FeedController.java index 480478c9d0..66bd8582ce 100644 --- a/http-service/src/main/java/net/runelite/http/service/feed/FeedController.java +++ b/http-service/src/main/java/net/runelite/http/service/feed/FeedController.java @@ -27,7 +27,6 @@ package net.runelite.http.service.feed; import com.google.common.hash.HashCode; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -92,27 +91,27 @@ public class FeedController { items.addAll(blogService.getBlogPosts()); } - catch (IOException e) + catch (Exception e) { - log.warn(e.getMessage()); + log.warn("unable to fetch blogs", e); } try { items.addAll(twitterService.getTweets()); } - catch (IOException e) + catch (Exception e) { - log.warn(e.getMessage()); + log.warn("unable to fetch tweets", e); } try { items.addAll(osrsNewsService.getNews()); } - catch (IOException e) + catch (Exception e) { - log.warn(e.getMessage()); + log.warn("unable to fetch news", e); } memoizedFeed = new MemoizedFeed(new FeedResult(items));