From c0bc66fb9c2dc7deaee54ab4da51e40b6cdf3e4c Mon Sep 17 00:00:00 2001 From: James <38226001+james-munson@users.noreply.github.com> Date: Sat, 20 Apr 2019 12:06:29 -0700 Subject: [PATCH] Changes (#15) * Add config option to hide inventory viewer when player inventory is open * Fix style error * Adds world history to world hopper plugin.\n- Adds support for converting strings to Maps\n- Ability to clear history * Fixed code style issues * Fixed code style issues in another file * Adds ability to not show tabs if you don't want * Tabs are not shown by default * Fix indentation in InventoryViewerConfig * mixes checkstyle * mixes checkstyle * Adds ability to see your xp drops as damage, overlayed above your opponent * Adds building to jar capabilities and fixes 0s * added output * Add grouping option to Inventory Viewer * Add option to show free slots Works on both the Grouped and Full modes * skybox: calculate brightness increase in HSB format * mixins: renderWidgetLayer: skip hidden widgets * World Map: Identify Both Shield of Arrav Quest Start Points (#8442) Closes #8437 * Add Ammo plugin Shows the contents of the Ammo slot as an Infobox * Add support for weapon slot items, such as darts * Add stack formatting * Improve Ammo's Documentation * widgetitem: associate Widget with WidgetItem * widgetitem overlay: allow configuring which interfaces to overlay Update overlays to behave consistent with how they behaved before removal of query api, with the exception of adding the rune pouch overlay to the bank. * Displays teammate health bars for BA Healer overlay Parses teammate hp and outputs to System.out Removed useless class Displays offset inverted health bars Set accurate values for location and size of health bars Checkstyle Checkstyle * Nothing * Removes max draw distance of ground markers * Added vanguard hp tracker * Vanguard Disabled by default * Add player indicator config to show offline friends * Better player indicator plugin, removed pk vison. added InteractChanged api * Add a toggle to only show player indicators in the wilderness. * Dont limit level by default in playerindicators * Added type to plugins --- .../java/net/runelite/api/VarClientInt.java | 5 + .../main/java/net/runelite/api/Varbits.java | 5 + .../net/runelite/api/coords/WorldPoint.java | 21 + .../runelite/api/events/InteractChanged.java | 34 + .../net/runelite/api/widgets/WidgetID.java | 10 + .../net/runelite/api/widgets/WidgetInfo.java | 23 +- .../runelite/client/config/ConfigManager.java | 16 + .../client/plugins/ammo/AmmoCounter.java | 59 + .../client/plugins/ammo/AmmoPlugin.java | 142 +++ .../plugins/attackstyles/AttackStyle.java | 2 +- .../plugins/attackstyles/WeaponType.java | 2 +- .../BarbarianAssaultOverlay.java | 47 +- .../plugins/barbarianassault/HealerTeam.java | 44 + .../plugins/experiencedrop/XpDropConfig.java | 22 + .../plugins/experiencedrop/XpDropOverlay.java | 73 ++ .../plugins/experiencedrop/XpDropPlugin.java | 214 +++- .../groundmarkers/GroundMarkerOverlay.java | 7 - .../InventoryViewerConfig.java | 63 + .../inventoryviewer/InventoryViewerMode.java | 44 + .../InventoryViewerOverlay.java | 101 +- .../InventoryViewerPlugin.java | 10 + .../PlayerIndicatorsConfig.java | 175 ++- .../PlayerIndicatorsOverlay.java | 95 +- .../PlayerIndicatorsPlugin.java | 162 ++- .../PlayerIndicatorsService.java | 92 +- .../client/plugins/skybox/Skybox.java | 9 +- .../plugins/vanguards/VanguardOverlay.java | 119 ++ .../plugins/vanguards/VanguardPlugin.java | 45 + .../worldhopper/WorldHopperConfig.java | 11 + .../worldhopper/WorldHopperPlugin.java | 47 + .../worldhopper/WorldSwitcherPanel.java | 1036 ++++++++++------- .../plugins/worldhopper/WorldTableHeader.java | 9 +- .../plugins/worldhopper/WorldTableRow.java | 1 + .../plugins/worldmap/QuestStartLocation.java | 2 - .../net/runelite/mixins/RSActorMixin.java | 6 +- 35 files changed, 2208 insertions(+), 545 deletions(-) create mode 100644 runelite-api/src/main/java/net/runelite/api/events/InteractChanged.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/ammo/AmmoCounter.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/ammo/AmmoPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/HealerTeam.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerMode.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/vanguards/VanguardOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/vanguards/VanguardPlugin.java diff --git a/runelite-api/src/main/java/net/runelite/api/VarClientInt.java b/runelite-api/src/main/java/net/runelite/api/VarClientInt.java index 0652f3b0e0..b58e879437 100644 --- a/runelite-api/src/main/java/net/runelite/api/VarClientInt.java +++ b/runelite-api/src/main/java/net/runelite/api/VarClientInt.java @@ -45,6 +45,11 @@ public enum VarClientInt INPUT_TYPE(5), MEMBERSHIP_STATUS(103), + /** + * -1 = player inventory closed + * 3 = player inventory opened + */ + PLAYER_INVENTORY_OPENED(171), INVENTORY_TAB(171), diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index 3c46a0b71e..8c4c18bf91 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -349,6 +349,11 @@ public enum Varbits */ MULTICOMBAT_AREA(4605), + /** + * In the Wilderness + */ + IN_THE_WILDERNESS(5963), + /** * Kingdom Management */ diff --git a/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java b/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java index bbacd1914a..ee7c67b20b 100644 --- a/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java +++ b/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java @@ -327,6 +327,27 @@ public class WorldPoint return ((x >> 6) << 8) | (y >> 6); } + /** + * Checks if user in within certain zone specified by upper and lower bound + * @param lowerBound + * @param upperBound + * @param userLocation + * @return + */ + public static boolean isInZone(WorldPoint lowerBound, WorldPoint upperBound, WorldPoint userLocation) + { + if (userLocation.getX() < lowerBound.getX() + || userLocation.getX() > upperBound.getX() + || userLocation.getY() < lowerBound.getY() + || userLocation.getY() > upperBound.getY() + || userLocation.getPlane() < lowerBound.getPlane() + || userLocation.getPlane() > upperBound.getPlane()) + { + return false; + } + return true; + } + /** * Converts the passed region ID and coordinates to a world coordinate */ diff --git a/runelite-api/src/main/java/net/runelite/api/events/InteractChanged.java b/runelite-api/src/main/java/net/runelite/api/events/InteractChanged.java new file mode 100644 index 0000000000..70f5452387 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/InteractChanged.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018, Seth + * 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.events; + +import lombok.Data; +import net.runelite.api.Actor; + +@Data +public class InteractChanged +{ + private Actor actor; +} \ No newline at end of file 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 fb3825b8e1..471c0acbe4 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 @@ -552,6 +552,13 @@ public class WidgetID static final int ROLE_SPRITE = 11; static final int ROLE = 12; } + static class HLR + { + static final int TEAMMATE1 = 18; + static final int TEAMMATE2 = 22; + static final int TEAMMATE3 = 26; + static final int TEAMMATE4 = 30; + } static final int CORRECT_STYLE = 3; static final int CURRENT_WAVE_WIDGET = 4; static final int CURRENT_WAVE = 5; @@ -857,6 +864,9 @@ static final int WIND_STRIKE = 5; static class Pvp { + static final int PVP_WIDGET_CONTAINER = 54; // OUTDATED? + static final int SKULL = 56; // OUTDATED? + static final int ATTACK_RANGE = 59; // OUTDATED? static final int BOUNTY_HUNTER_INFO = 19; static final int KILLDEATH_RATIO = 15; static final int SKULL_CONTAINER = 62; 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 c6a818fc63..d345b27aa8 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 @@ -348,6 +348,11 @@ public enum WidgetInfo BA_HEAL_ROLE_TEXT(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.ROLE), BA_HEAL_ROLE_SPRITE(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.ROLE_SPRITE), + BA_HEAL_TEAMMATE1(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.HLR.TEAMMATE1), + BA_HEAL_TEAMMATE2(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.HLR.TEAMMATE2), + BA_HEAL_TEAMMATE3(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.HLR.TEAMMATE3), + BA_HEAL_TEAMMATE4(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.HLR.TEAMMATE4), + BA_COLL_WAVE_TEXT(WidgetID.BA_COLLECTOR_GROUP_ID, WidgetID.BarbarianAssault.CURRENT_WAVE), BA_COLL_CALL_TEXT(WidgetID.BA_COLLECTOR_GROUP_ID, WidgetID.BarbarianAssault.TO_CALL), BA_COLL_LISTEN_TEXT(WidgetID.BA_COLLECTOR_GROUP_ID, WidgetID.BarbarianAssault.CORRECT_STYLE), @@ -451,6 +456,17 @@ public enum WidgetInfo MINIGAME_TELEPORT_BUTTON(WidgetID.MINIGAME_TAB_ID, WidgetID.Minigames.TELEPORT_BUTTON), + PVP_CONTAINER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.PVP_WIDGET_CONTAINER), + PVP_SKULL_CONTAINER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SKULL_CONTAINER), + PVP_SKULL(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SKULL), + PVP_ATTACK_RANGE(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.ATTACK_RANGE), + PVP_WORLD_SAFE_ZONE(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SAFE_ZONE), + + PVP_WILDERNESS_LEVEL(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.WILDERNESS_LEVEL), + + PVP_BOUNTY_HUNTER_INFO(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.BOUNTY_HUNTER_INFO), + PVP_KILLDEATH_COUNTER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.KILLDEATH_RATIO), + /* STANDARD SPELL BOOK WIDGETS*/ SPELL_LUMBRIDGE_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.LUMBRIDGE_HOME_TELEPORT), SPELL_BIND(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.BIND), @@ -502,13 +518,6 @@ public enum WidgetInfo /* END OF ANCIENT SPELL BOOK WIDGETS*/ - PVP_SKULL_CONTAINER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SKULL_CONTAINER), - PVP_WORLD_SAFE_ZONE(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SAFE_ZONE), - - PVP_WILDERNESS_LEVEL(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.WILDERNESS_LEVEL), - PVP_BOUNTY_HUNTER_INFO(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.BOUNTY_HUNTER_INFO), - PVP_KILLDEATH_COUNTER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.KILLDEATH_RATIO), - KOUREND_FAVOUR_OVERLAY(WidgetID.KOUREND_FAVOUR_GROUP_ID, WidgetID.KourendFavour.KOUREND_FAVOUR_OVERLAY), ZEAH_MESS_HALL_COOKING_DISPLAY(WidgetID.ZEAH_MESS_HALL_GROUP_ID, WidgetID.Zeah.MESS_HALL_COOKING_DISPLAY), diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java index 87f63d3826..7306ab0a51 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java @@ -602,6 +602,22 @@ public class ConfigManager { return Duration.ofMillis(Long.parseLong(str)); } + if (type == Map.class) + { + Map output = new HashMap<>(); + str = str.substring(1, str.length() - 1); + String[] splitStr = str.split(", "); + for (String s : splitStr) + { + String[] keyVal = s.split("="); + if (keyVal.length > 1) + { + output.put(keyVal[0], keyVal[1]); + } + } + + return output; + } return str; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/ammo/AmmoCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/ammo/AmmoCounter.java new file mode 100644 index 0000000000..1e7b7cb3d4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/ammo/AmmoCounter.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 Hydrox6 + * 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.ammo; + +import java.awt.image.BufferedImage; +import lombok.Getter; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.overlay.infobox.Counter; +import net.runelite.client.util.StackFormatter; + +public class AmmoCounter extends Counter +{ + @Getter + private int itemID; + + @Getter + private String name; + + public AmmoCounter(Plugin plugin, int itemID, int count, String name, BufferedImage image) + { + super(image, plugin, count); + this.itemID = itemID; + this.name = name; + } + + @Override + public String getText() + { + return StackFormatter.quantityToRSDecimalStack(getCount()); + } + + @Override + public String getTooltip() + { + return name; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/ammo/AmmoPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/ammo/AmmoPlugin.java new file mode 100644 index 0000000000..b2466994a1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/ammo/AmmoPlugin.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2019 Hydrox6 + * 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.ammo; + +import java.awt.image.BufferedImage; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.EquipmentInventorySlot; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemComposition; +import net.runelite.api.ItemContainer; +import net.runelite.api.events.ItemContainerChanged; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; + +@PluginDescriptor( + name = "Ammo", + description = "Shows the current ammo the player has equipped", + tags = {"bolts", "darts", "chinchompa"}, + type = "utility" +) +public class AmmoPlugin extends Plugin +{ + @Inject + private Client client; + + @Inject + private ClientThread clientThread; + + @Inject + private InfoBoxManager infoBoxManager; + + @Inject + private ItemManager itemManager; + + private AmmoCounter counterBox; + + @Override + public void startUp() throws Exception + { + clientThread.invokeLater(() -> + { + ItemContainer container = client.getItemContainer(InventoryID.EQUIPMENT); + if (container != null) + { + parseInventory(container.getItems()); + } + }); + } + + @Override + public void shutDown() throws Exception + { + infoBoxManager.removeInfoBox(counterBox); + counterBox = null; + } + + @Subscribe + public void onItemContainerChanged(ItemContainerChanged event) + { + if (event.getItemContainer() != client.getItemContainer(InventoryID.EQUIPMENT)) + { + return; + } + + parseInventory(event.getItemContainer().getItems()); + } + + private void parseInventory(Item[] items) + { + // Check for weapon slot items. This overrides the ammo slot, + // as the player will use the thrown weapon (eg. chinchompas, knives, darts) + if (items.length >= EquipmentInventorySlot.WEAPON.getSlotIdx() - 1) + { + final Item weapon = items[EquipmentInventorySlot.WEAPON.getSlotIdx()]; + final ItemComposition weaponComp = itemManager.getItemComposition(weapon.getId()); + if (weaponComp.isStackable()) + { + updateInfobox(weapon, weaponComp); + return; + } + } + + if (items.length <= EquipmentInventorySlot.AMMO.getSlotIdx()) + { + return; + } + + final Item ammo = items[EquipmentInventorySlot.AMMO.getSlotIdx()]; + final ItemComposition comp = itemManager.getItemComposition(ammo.getId()); + + if (!comp.isStackable()) + { + infoBoxManager.removeInfoBox(counterBox); + counterBox = null; + return; + } + + updateInfobox(ammo, comp); + } + + private void updateInfobox(Item item, ItemComposition comp) + { + if (counterBox != null && counterBox.getItemID() == item.getId()) + { + counterBox.setCount(item.getQuantity()); + return; + } + + infoBoxManager.removeInfoBox(counterBox); + final BufferedImage image = itemManager.getImage(item.getId(), 5, false); + counterBox = new AmmoCounter(this, item.getId(), item.getQuantity(), comp.getName(), image); + infoBoxManager.addInfoBox(counterBox); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/attackstyles/AttackStyle.java b/runelite-client/src/main/java/net/runelite/client/plugins/attackstyles/AttackStyle.java index 047363e1e4..c32ba11af3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/attackstyles/AttackStyle.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/attackstyles/AttackStyle.java @@ -26,7 +26,7 @@ package net.runelite.client.plugins.attackstyles; import net.runelite.api.Skill; -enum AttackStyle +public enum AttackStyle { ACCURATE("Accurate", Skill.ATTACK), AGGRESSIVE("Aggressive", Skill.STRENGTH), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/attackstyles/WeaponType.java b/runelite-client/src/main/java/net/runelite/client/plugins/attackstyles/WeaponType.java index ef9ba0a188..d74742fca8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/attackstyles/WeaponType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/attackstyles/WeaponType.java @@ -28,7 +28,7 @@ import java.util.HashMap; import java.util.Map; import static net.runelite.client.plugins.attackstyles.AttackStyle.*; -enum WeaponType +public enum WeaponType { TYPE_0(ACCURATE, AGGRESSIVE, null, DEFENSIVE), TYPE_1(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultOverlay.java index 5bc5959622..2cac64080d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultOverlay.java @@ -55,6 +55,11 @@ class BarbarianAssaultOverlay extends Overlay { private static final int MAX_EGG_DISTANCE = 2500; + + private final int HEALTH_BAR_HEIGHT = 20; + private final Color HEALTH_BAR_COLOR = new Color(225, 35, 0, 125); + private static final Color BACKGROUND = new Color(0, 0, 0, 150); + private final Client client; private final BarbarianAssaultPlugin plugin; private final BarbarianAssaultConfig config; @@ -156,7 +161,35 @@ class BarbarianAssaultOverlay extends Overlay } } - return null; + if (role == Role.HEALER) + { + for (HealerTeam teammate : HealerTeam.values()) + { + Widget widget = client.getWidget(teammate.getTeammate()); + if (widget == null) + { + continue; + } + + String[] teammateHealth = widget.getText().split(" / "); + final int curHealth = Integer.parseInt(teammateHealth[0]); + final int maxHealth = Integer.parseInt(teammateHealth[1]); + + int width = teammate.getWidth(); + final int filledWidth = getBarWidth(maxHealth, curHealth, width); + + int offsetX = teammate.getOffset().getX(); + int offsetY = teammate.getOffset().getY(); + int x = widget.getCanvasLocation().getX() - offsetX; + int y = widget.getCanvasLocation().getY() - offsetY; + + graphics.setColor(HEALTH_BAR_COLOR); + graphics.fillRect(x, y, filledWidth, HEALTH_BAR_HEIGHT); + } + } + + + return null; } private void renderEggLocation(Graphics2D graphics, WorldPoint location, int quantity, Color color) @@ -186,4 +219,16 @@ class BarbarianAssaultOverlay extends Overlay Point textPoint = Perspective.getCanvasTextLocation(client, graphics, groundPoint, quantityText, 0); OverlayUtil.renderTextLocation(graphics, textPoint, quantityText, Color.WHITE); } + + private static int getBarWidth(int base, int current, int size) + { + final double ratio = (double) current / base; + + if (ratio >= 1) + { + return size; + } + + return (int) Math.round(ratio * size); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/HealerTeam.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/HealerTeam.java new file mode 100644 index 0000000000..5521d620f7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/HealerTeam.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018, whartd + * 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.barbarianassault; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.Point; +import net.runelite.api.widgets.WidgetInfo; + +@Getter +@AllArgsConstructor +enum HealerTeam +{ + TEAMMATE1(WidgetInfo.BA_HEAL_TEAMMATE1, new Point(28, 2), 115), + TEAMMATE2(WidgetInfo.BA_HEAL_TEAMMATE2, new Point(26, 2), 115), + TEAMMATE3(WidgetInfo.BA_HEAL_TEAMMATE3, new Point(26, 2), 115), + TEAMMATE4(WidgetInfo.BA_HEAL_TEAMMATE4, new Point(25, 2), 115); + + private WidgetInfo teammate; + private Point offset; + private int width; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropConfig.java index ec1f1acc45..0d1beab3e7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropConfig.java @@ -87,4 +87,26 @@ public interface XpDropConfig extends Config return 0; } + @ConfigItem( + keyName = "showDamage", + name = "Show Damage on XP Drop", + description = "Show what you hit next to the XP drop", + position = 5 + ) + default boolean showDamage() + { + return false; + } + + @ConfigItem( + keyName = "damageColor", + name = "Damage Color", + description = "The color you want the text to be for damage", + position = 6 + ) + default Color getDamageColor() + { + return Color.RED; + } + } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropOverlay.java new file mode 100644 index 0000000000..119da63597 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropOverlay.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017, honeyhoney + * 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.experiencedrop; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; + +import net.runelite.api.Actor; +import net.runelite.api.Point; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; + +class XpDropOverlay extends Overlay +{ + private final XpDropPlugin plugin; + private final XpDropConfig config; + + @Inject + private XpDropOverlay(XpDropPlugin plugin, XpDropConfig config) + { + this.plugin = plugin; + this.config = config; + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (config.showDamage()) + { + final Actor opponent = plugin.getLastOpponent(); + if (opponent != null) + { + int offset = opponent.getLogicalHeight() + 50; + String damageStr = String.valueOf(this.plugin.getDamage()); + Point textLocation = opponent.getCanvasTextLocation(graphics, damageStr, offset); + + if (textLocation != null && this.plugin.getDamage() != 0) + { + OverlayUtil.renderTextLocation(graphics, textLocation, damageStr, config.getDamageColor()); + } + } + } + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropPlugin.java index aca9477123..cb7c698195 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/experiencedrop/XpDropPlugin.java @@ -25,26 +25,38 @@ package net.runelite.client.plugins.experiencedrop; import com.google.inject.Provides; + +import java.time.Duration; +import java.time.Instant; import java.util.Arrays; import java.util.EnumMap; import java.util.Map; import java.util.stream.IntStream; import javax.inject.Inject; -import net.runelite.api.Client; + +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.*; + import static net.runelite.api.ScriptID.XPDROP_DISABLED; -import net.runelite.api.Skill; -import net.runelite.api.SpriteID; -import net.runelite.api.Varbits; -import net.runelite.api.events.ExperienceChanged; -import net.runelite.api.events.GameTick; -import net.runelite.api.events.WidgetHiddenChanged; +import static net.runelite.client.plugins.attackstyles.AttackStyle.*; + +import net.runelite.api.events.*; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetID; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.HiscoreManager; +import net.runelite.client.game.NPCManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.attackstyles.AttackStyle; +import net.runelite.client.plugins.attackstyles.WeaponType; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.Text; +import net.runelite.http.api.hiscore.HiscoreEndpoint; +import net.runelite.http.api.hiscore.HiscoreResult; @PluginDescriptor( name = "XP Drop", @@ -54,6 +66,7 @@ import net.runelite.client.plugins.PluginDescriptor; public class XpDropPlugin extends Plugin { private static final int XPDROP_PADDING = 2; // space between xp drop icons + private static final Duration WAIT = Duration.ofSeconds(5); @Inject private Client client; @@ -63,11 +76,39 @@ public class XpDropPlugin extends Plugin private int tickCounter = 0; private int previousExpGained; + private boolean hasHit = false; private boolean hasDropped = false; private boolean correctPrayer; private Skill lastSkill = null; private Map previousSkillExpTable = new EnumMap<>(Skill.class); private PrayerType currentTickPrayer; + private AttackStyle attackStyle; + private int attackStyleVarbit = -1; + private int equippedWeaponTypeVarbit = -1; + private int castingModeVarbit = -1; + private int opponentHealth = -1; + private int xpGains = 0; + private AttackStyle[] offensiveStyles = {ACCURATE, AGGRESSIVE, DEFENSIVE, CONTROLLED, RANGING, LONGRANGE, CASTING, DEFENSIVE_CASTING}; + + @Getter(AccessLevel.PACKAGE) + private int damage = 0; + + @Getter(AccessLevel.PACKAGE) + private Actor lastOpponent; + + private Instant lastTime; + + @Inject + private OverlayManager overlayManager; + + @Inject + private XpDropOverlay overlay; + + @Inject + private NPCManager npcManager; + + @Inject + private HiscoreManager hiscoreManager; @Provides XpDropConfig provideConfig(ConfigManager configManager) @@ -75,6 +116,23 @@ public class XpDropPlugin extends Plugin return configManager.getConfig(XpDropConfig.class); } + @Override + protected void startUp() throws Exception + { + lastOpponent = null; + overlayManager.add(overlay); + if (client.getGameState() == GameState.LOGGED_IN) + { + attackStyleVarbit = client.getVar(VarPlayer.ATTACK_STYLE); + equippedWeaponTypeVarbit = client.getVar(Varbits.EQUIPPED_WEAPON_TYPE); + castingModeVarbit = client.getVar(Varbits.DEFENSIVE_CASTING_MODE); + updateAttackStyle( + equippedWeaponTypeVarbit, + attackStyleVarbit, + castingModeVarbit); + } + } + @Subscribe public void onWidgetHiddenChanged(WidgetHiddenChanged event) { @@ -203,6 +261,42 @@ public class XpDropPlugin extends Plugin @Subscribe public void onGameTick(GameTick tick) { + // Detect hitting a 0 + if (lastOpponent != null) + { + int health = calculateHealth(lastOpponent); + if (health != -1 && opponentHealth != -1 && health == opponentHealth && hasHit) + { + damage = 0; + hasHit = false; + } + } + + // Handle getting XP gains + if (hasDropped) + { + if (xpGains != 0 && attackStyle.getSkills().length > 1 && attackStyle != LONGRANGE) + { + damage = (int) (xpGains / (attackStyle.getSkills().length * 1.3)); + } + else if (xpGains != 0) + { + damage = xpGains / 4; + } + + xpGains = 0; + hasDropped = false; + } + + // Clear opponent + if (lastOpponent != null && lastTime != null && client.getLocalPlayer().getInteracting() == null) + { + if (Duration.between(lastTime, Instant.now()).compareTo(WAIT) > 0) + { + lastOpponent = null; + } + } + currentTickPrayer = getActivePrayerType(); correctPrayer = false; @@ -240,9 +334,115 @@ public class XpDropPlugin extends Plugin Integer previous = previousSkillExpTable.put(skill, xp); if (previous != null) { + opponentHealth = calculateHealth(lastOpponent); previousExpGained = xp - previous; + if (skill != Skill.HITPOINTS && Arrays.stream(offensiveStyles).anyMatch(attackStyle::equals)) + { + xpGains += previousExpGained; + } + hasDropped = true; + hasHit = true; } } + private void updateAttackStyle(int equippedWeaponType, int attackStyleIndex, int castingMode) + { + AttackStyle[] attackStyles = WeaponType.getWeaponType(equippedWeaponType).getAttackStyles(); + if (attackStyleIndex < attackStyles.length) + { + attackStyle = attackStyles[attackStyleIndex]; + if (attackStyle == null) + { + attackStyle = OTHER; + } + else if ((attackStyle == CASTING) && (castingMode == 1)) + { + attackStyle = DEFENSIVE_CASTING; + } + } + } + + @Subscribe + public void onInteractingChanged(InteractingChanged event) + { + if (event.getSource() != client.getLocalPlayer()) + { + return; + } + + Actor opponent = event.getTarget(); + + if (opponent == null) + { + lastTime = Instant.now(); + return; + } + + damage = 0; + lastOpponent = opponent; + opponentHealth = calculateHealth(opponent); + } + + private int calculateHealth(Actor target) + { + if (target == null || target.getName() == null) + { + return -1; + } + + final int healthScale = target.getHealth(); + final int healthRatio = target.getHealthRatio(); + final String targetName = Text.removeTags(target.getName()); + + Integer maxHealth = -1; + if (target instanceof NPC) + { + maxHealth = npcManager.getHealth(targetName, target.getCombatLevel()); + } + else if (target instanceof Player) + { + final HiscoreResult hiscoreResult = hiscoreManager.lookupAsync(targetName, HiscoreEndpoint.NORMAL); + if (hiscoreResult != null) + { + final int hp = hiscoreResult.getHitpoints().getLevel(); + if (hp > 0) + { + maxHealth = hp; + } + } + } + + if (healthRatio < 0 || healthScale <= 0 || maxHealth == null) + { + return -1; + } + + return (int)((maxHealth * healthRatio / healthScale) + 0.5f); + } + + @Subscribe + public void onVarbitChanged(VarbitChanged event) + { + if (attackStyleVarbit == -1 || attackStyleVarbit != client.getVar(VarPlayer.ATTACK_STYLE)) + { + attackStyleVarbit = client.getVar(VarPlayer.ATTACK_STYLE); + updateAttackStyle(client.getVar(Varbits.EQUIPPED_WEAPON_TYPE), attackStyleVarbit, + client.getVar(Varbits.DEFENSIVE_CASTING_MODE)); + } + + if (equippedWeaponTypeVarbit == -1 || equippedWeaponTypeVarbit != client.getVar(Varbits.EQUIPPED_WEAPON_TYPE)) + { + equippedWeaponTypeVarbit = client.getVar(Varbits.EQUIPPED_WEAPON_TYPE); + updateAttackStyle(equippedWeaponTypeVarbit, client.getVar(VarPlayer.ATTACK_STYLE), + client.getVar(Varbits.DEFENSIVE_CASTING_MODE)); + } + + if (castingModeVarbit == -1 || castingModeVarbit != client.getVar(Varbits.DEFENSIVE_CASTING_MODE)) + { + castingModeVarbit = client.getVar(Varbits.DEFENSIVE_CASTING_MODE); + updateAttackStyle(client.getVar(Varbits.EQUIPPED_WEAPON_TYPE), client.getVar(VarPlayer.ATTACK_STYLE), + castingModeVarbit); + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java index 768d90b51b..ec1814bde4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java @@ -43,7 +43,6 @@ import net.runelite.client.ui.overlay.OverlayUtil; public class GroundMarkerOverlay extends Overlay { - private static final int MAX_DRAW_DISTANCE = 32; private final Client client; private final GroundMarkerConfig config; @@ -87,12 +86,6 @@ public class GroundMarkerOverlay extends Overlay private void drawTile(Graphics2D graphics, WorldPoint point, Color color) { - WorldPoint playerLocation = client.getLocalPlayer().getWorldLocation(); - - if (point.distanceTo(playerLocation) >= MAX_DRAW_DISTANCE) - { - return; - } LocalPoint lp = LocalPoint.fromWorld(client, point); if (lp == null) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerConfig.java new file mode 100644 index 0000000000..38ff6d6e3d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerConfig.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 Hydrox6 + * 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.inventoryviewer; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup(InventoryViewerPlugin.CONFIG_GROUP_KEY) +public interface InventoryViewerConfig extends Config +{ + @ConfigItem( + keyName = "viewerMode", + name = "Mode", + description = "The mode to display the inventory viewer with" + ) + default InventoryViewerMode viewerMode() + { + return InventoryViewerMode.FULL; + } + + @ConfigItem( + keyName = "showFreeSlots", + name = "Show Free Slots", + description = "Whether to show a label with the free slots in the inventory" + ) + default boolean showFreeSlots() + { + return false; + } + + @ConfigItem( + keyName = "hideWhenInvOpen", + name = "Hide when inventory is open", + description = "Hide the inventory viewer when the player's inventory is open" + ) + default boolean hideWhenInvOpen() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerMode.java new file mode 100644 index 0000000000..562ae8c972 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerMode.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 Hydrox6 + * 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.inventoryviewer; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum InventoryViewerMode +{ + FULL("Full"), + GROUPED("Grouped"); + + private final String name; + + @Override + public String toString() + { + return name; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerOverlay.java index 71da3492aa..d66718f4c0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerOverlay.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 AWPH-I + * Copyright (c) 2019 Hydrox6 * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,9 +25,12 @@ */ package net.runelite.client.plugins.inventoryviewer; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Point; +import java.awt.Rectangle; import java.awt.image.BufferedImage; import javax.inject.Inject; import net.runelite.api.Client; @@ -34,11 +38,14 @@ import net.runelite.api.InventoryID; import net.runelite.api.Item; import net.runelite.api.ItemComposition; import net.runelite.api.ItemContainer; +import net.runelite.api.VarClientInt; import net.runelite.client.game.ItemManager; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.ComponentConstants; import net.runelite.client.ui.overlay.components.ImageComponent; import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; class InventoryViewerOverlay extends Overlay { @@ -49,23 +56,49 @@ class InventoryViewerOverlay extends Overlay private final Client client; private final ItemManager itemManager; + private final InventoryViewerConfig config; - private final PanelComponent panelComponent = new PanelComponent(); + private final PanelComponent wrapperComponent = new PanelComponent(); + private final PanelComponent inventoryComponent = new PanelComponent(); + private final TitleComponent freeSlotsComponent = TitleComponent.builder().build(); @Inject - private InventoryViewerOverlay(Client client, ItemManager itemManager) + private InventoryViewerOverlay(Client client, ItemManager itemManager, InventoryViewerConfig config) { setPosition(OverlayPosition.BOTTOM_RIGHT); - panelComponent.setWrapping(4); - panelComponent.setGap(new Point(6, 4)); - panelComponent.setOrientation(PanelComponent.Orientation.HORIZONTAL); + + inventoryComponent.setWrapping(4); + inventoryComponent.setGap(new Point(6, 4)); + inventoryComponent.setOrientation(PanelComponent.Orientation.HORIZONTAL); + inventoryComponent.setBackgroundColor(null); + inventoryComponent.setBorder(new Rectangle( + 0, + ComponentConstants.STANDARD_BORDER, + 0, + ComponentConstants.STANDARD_BORDER)); + + wrapperComponent.setOrientation(PanelComponent.Orientation.VERTICAL); + wrapperComponent.setWrapping(2); + wrapperComponent.setBorder(new Rectangle( + ComponentConstants.STANDARD_BORDER * 2, + ComponentConstants.STANDARD_BORDER, + ComponentConstants.STANDARD_BORDER * 2, + ComponentConstants.STANDARD_BORDER)); + this.itemManager = itemManager; this.client = client; + this.config = config; } @Override public Dimension render(Graphics2D graphics) { + if (config.hideWhenInvOpen() + && client.getVar(VarClientInt.PLAYER_INVENTORY_OPENED) == 3) + { + return null; + } + final ItemContainer itemContainer = client.getItemContainer(InventoryID.INVENTORY); if (itemContainer == null) @@ -73,10 +106,50 @@ class InventoryViewerOverlay extends Overlay return null; } - panelComponent.getChildren().clear(); + inventoryComponent.getChildren().clear(); + wrapperComponent.getChildren().clear(); final Item[] items = itemContainer.getItems(); + if (config.viewerMode() == InventoryViewerMode.GROUPED) + { + Multiset totals = HashMultiset.create(); + for (Item item : items) + { + if (item.getId() != -1) + { + totals.add(item.getId(), item.getQuantity()); + } + } + + final long remaining = INVENTORY_SIZE - totals.size(); + if (remaining == INVENTORY_SIZE) + { + return null; + } + + for (Multiset.Entry cursor : totals.entrySet()) + { + final BufferedImage image = itemManager.getImage(cursor.getElement(), cursor.getCount(), true); + if (image != null) + { + inventoryComponent.getChildren().add(new ImageComponent(image)); + } + } + wrapperComponent.getChildren().add(inventoryComponent); + + if (config.showFreeSlots()) + { + freeSlotsComponent.setText(remaining + " free"); + wrapperComponent.setPreferredSize(new Dimension(Math.min(totals.elementSet().size(), 4) * (PLACEHOLDER_WIDTH + 6) + ComponentConstants.STANDARD_BORDER * 2, 0)); + wrapperComponent.getChildren().add(freeSlotsComponent); + } + + return wrapperComponent.render(graphics); + } + + int remaining = 28; + for (int i = 0; i < INVENTORY_SIZE; i++) { if (i < items.length) @@ -84,20 +157,30 @@ class InventoryViewerOverlay extends Overlay final Item item = items[i]; if (item.getQuantity() > 0) { + remaining -= 1; final BufferedImage image = getImage(item); if (image != null) { - panelComponent.getChildren().add(new ImageComponent(image)); + inventoryComponent.getChildren().add(new ImageComponent(image)); continue; } } } // put a placeholder image so each item is aligned properly and the panel is not resized - panelComponent.getChildren().add(PLACEHOLDER_IMAGE); + inventoryComponent.getChildren().add(PLACEHOLDER_IMAGE); } - return panelComponent.render(graphics); + wrapperComponent.getChildren().add(inventoryComponent); + + if (config.showFreeSlots()) + { + freeSlotsComponent.setText(remaining + " free"); + wrapperComponent.setPreferredSize(new Dimension(4 * (PLACEHOLDER_WIDTH + 6) + ComponentConstants.STANDARD_BORDER * 2, 0)); + wrapperComponent.getChildren().add(freeSlotsComponent); + } + + return wrapperComponent.render(graphics); } private BufferedImage getImage(Item item) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerPlugin.java index a9aa9f6453..71032a10c7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerPlugin.java @@ -24,7 +24,9 @@ */ package net.runelite.client.plugins.inventoryviewer; +import com.google.inject.Provides; import javax.inject.Inject; +import net.runelite.client.config.ConfigManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.ui.overlay.OverlayManager; @@ -37,12 +39,20 @@ import net.runelite.client.ui.overlay.OverlayManager; ) public class InventoryViewerPlugin extends Plugin { + static final String CONFIG_GROUP_KEY = "inventoryviewer"; + @Inject private InventoryViewerOverlay overlay; @Inject private OverlayManager overlayManager; + @Provides + InventoryViewerConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(InventoryViewerConfig.class); + } + @Override public void startUp() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsConfig.java index 6b032bc8ee..f218bf5cbf 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsConfig.java @@ -137,13 +137,48 @@ public interface PlayerIndicatorsConfig extends Config name = "Non-clan member color", description = "Color of non-clan member names" ) - default Color getNonClanMemberColor() + default Color getNonClanMemberColor() { return Color.RED; } + + @ConfigItem( + position = 10, + keyName = "drawAttackerNames", + name = "Highlight attacker players", + description = "Configures whether or not attacker players should be highlighted" + ) + default boolean highlightAttackerPlayers() { - return Color.RED; + return false; } @ConfigItem( - position = 10, + position = 11, + keyName = "attackerColor", + name = "Attacker player color", + description = "Color of attacking player names" + ) + default Color getAttackerPlayerColor() { return new Color(241, 0, 108); } + + @ConfigItem( + position = 12, + keyName = "drawAttackableNames", + name = "Highlight attackable players", + description = "Configures whether or not attackable players should be highlighted" + ) + default boolean highlightAttackablePlayers() + { + return false; + } + + @ConfigItem( + position = 13, + keyName = "attackableColor", + name = "Attackable player color", + description = "Color of attackable player names" + ) + default Color getAttackablePlayerColor() { return new Color(231, 122,- 0); } + + @ConfigItem( + position = 14, keyName = "drawPlayerTiles", name = "Draw tiles under players", description = "Configures whether or not tiles under highlighted players should be drawn" @@ -154,18 +189,29 @@ public interface PlayerIndicatorsConfig extends Config } @ConfigItem( - position = 11, - keyName = "playerNamePosition", - name = "Name position", - description = "Configures the position of drawn player names, or if they should be disabled" + position = 15, + keyName = "drawOverheadPlayerNames", + name = "Draw names above players", + description = "Configures whether or not player names should be drawn above players" ) - default PlayerNameLocation playerNamePosition() + default boolean drawOverheadPlayerNames() { - return PlayerNameLocation.ABOVE_HEAD; + return true; } @ConfigItem( - position = 12, + position = 16, + keyName = "drawOverheadLevels", + name = "Draw combat levels above players", + description = "Configures whether or not combat levels should be drawn above players" + ) + default boolean drawOverheadLevels() + { + return false; + } + + @ConfigItem( + position = 17, keyName = "drawMinimapNames", name = "Draw names on minimap", description = "Configures whether or not minimap names for players with rendered names should be drawn" @@ -176,7 +222,7 @@ public interface PlayerIndicatorsConfig extends Config } @ConfigItem( - position = 13, + position = 18, keyName = "colorPlayerMenu", name = "Colorize player menu", description = "Color right click menu for players" @@ -187,7 +233,7 @@ public interface PlayerIndicatorsConfig extends Config } @ConfigItem( - position = 14, + position = 19, keyName = "clanMenuIcons", name = "Show clan ranks", description = "Add clan rank to right click menu and next to player names" @@ -196,4 +242,109 @@ public interface PlayerIndicatorsConfig extends Config { return true; } + + @ConfigItem( + position = 20, + keyName = "showOfflineFriends", + name = "Show offline friends", + description = "Draw friends names even if they're offline" + ) + default boolean showOfflineFriends() + { + return true; + } + + @ConfigItem( + position = 21, + keyName = "drawHighlightedNames", + name = "Draw highlighted player names", + description = "Configures whether or not highlighted player names should be drawn" + ) + default boolean drawHighlightedNames() + { + return false; + } + + @ConfigItem( + keyName = "highlightedNames", + name = "Highlighted names", + description = "Clan caller names separated by a comma" + ) + default String getHighlightedNames() + { + return ""; + } + + @ConfigItem( + keyName = "highlightedNamesColor", + name = "Highlighted names color", + description = "Color of highlighted names" + ) + default Color getHighlightedNamesColor() + { + return Color.ORANGE; + } + + @ConfigItem( + position = 22, + keyName = "drawHighlightedTargetNames", + name = "Draw highlighted target names", + description = "Configures whether or not highlighted target names should be drawn" + ) + default boolean drawHighlightedTargetNames() + { + return false; + } + + @ConfigItem( + position = 23, + keyName = "highlightedTargetColor", + name = "Highlighted target color", + description = "Color of highlighted target names" + ) + default Color getHighlightedTargetColor() + { + return new Color(255, 100, 183); + } + + @ConfigItem( + position = 24, + keyName = "limitLevel", + name = "Limit Level", + description = "Limit the players to show +-x your level. Useful for BH" + ) + default boolean limitLevel() + { + return false; + } + + @ConfigItem( + position = 25, + keyName = "level", + name = "Level", + description = "The level to limit players shown +-x" + ) + default int intLevel() + { + return 5; + } + + @ConfigItem( + position = 26, + keyName = "wildernessOnly", + name = "Show only in wilderness", + description = "Toggle whether or not to only show player indicators in the wilderness" + ) + default boolean showInWildernessOnly() + { + return false; + } + + @ConfigItem( + position = 27, + keyName="rightClickOverhead", + name="Add Overheads to Right Click Menu", + description="Feature shows a player's overhead prayer in the right click menu. Useful for DDs, or extremely crowded areas.") + + default boolean rightClickOverhead() { return false; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsOverlay.java index 5373fc1eb4..e582694a9f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsOverlay.java @@ -1,6 +1,5 @@ /* * Copyright (c) 2018, Tomas Slusny - * Copyright (c) 2019, Jordan Atwood * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,6 +31,7 @@ import java.awt.image.BufferedImage; import javax.inject.Inject; import javax.inject.Singleton; import net.runelite.api.ClanMemberRank; +import net.runelite.api.Client; import net.runelite.api.Player; import net.runelite.api.Point; import net.runelite.client.game.ClanManager; @@ -39,18 +39,17 @@ import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayPosition; import net.runelite.client.ui.overlay.OverlayPriority; import net.runelite.client.ui.overlay.OverlayUtil; -import net.runelite.client.util.Text; @Singleton public class PlayerIndicatorsOverlay extends Overlay { - private static final int ACTOR_OVERHEAD_TEXT_MARGIN = 40; - private static final int ACTOR_HORIZONTAL_TEXT_MARGIN = 10; - private final PlayerIndicatorsService playerIndicatorsService; private final PlayerIndicatorsConfig config; private final ClanManager clanManager; + @Inject + private Client client; + @Inject private PlayerIndicatorsOverlay(PlayerIndicatorsConfig config, PlayerIndicatorsService playerIndicatorsService, ClanManager clanManager) @@ -71,78 +70,68 @@ public class PlayerIndicatorsOverlay extends Overlay private void renderPlayerOverlay(Graphics2D graphics, Player actor, Color color) { - final PlayerNameLocation drawPlayerNamesConfig = config.playerNamePosition(); - if (drawPlayerNamesConfig == PlayerNameLocation.DISABLED) + if (!config.drawOverheadPlayerNames() && !config.drawOverheadLevels()) { return; } - final int zOffset; - switch (drawPlayerNamesConfig) + String namee = actor.getName().replace('\u00A0', ' '); + String combatLevel = Integer.toString(actor.getCombatLevel()); + String playerInfo = ""; + + if (config.drawOverheadPlayerNames()) { - case MODEL_CENTER: - case MODEL_RIGHT: - zOffset = actor.getLogicalHeight() / 2; - break; - default: - zOffset = actor.getLogicalHeight() + ACTOR_OVERHEAD_TEXT_MARGIN; + playerInfo = namee; } - final String name = Text.sanitize(actor.getName()); - Point textLocation = actor.getCanvasTextLocation(graphics, name, zOffset); - - if (drawPlayerNamesConfig == PlayerNameLocation.MODEL_RIGHT) + if (config.drawOverheadLevels()) { - textLocation = actor.getCanvasTextLocation(graphics, "", zOffset); + if (!playerInfo.isEmpty()) + { + playerInfo = playerInfo.concat("(" + combatLevel + ")"); + } + else + { + playerInfo = combatLevel; + } + } - if (textLocation == null) + if (config.limitLevel()) + { + if (!(client.getLocalPlayer().getCombatLevel() >= actor.getCombatLevel() - config.intLevel() && client.getLocalPlayer().getCombatLevel() <= actor.getCombatLevel() + config.intLevel())) { return; } - - textLocation = new Point(textLocation.getX() + ACTOR_HORIZONTAL_TEXT_MARGIN, textLocation.getY()); } - if (textLocation == null) - { - return; - } + String name = actor.getName().replace('\u00A0', ' ') + (config.limitLevel() ? " Lvl: " + actor.getCombatLevel() : ""); + int offset = actor.getLogicalHeight() + 40; + Point textLocation = actor.getCanvasTextLocation(graphics, playerInfo, offset); - if (config.showClanRanks() && actor.isClanMember()) + if (textLocation != null) { - final ClanMemberRank rank = clanManager.getRank(name); - - if (rank != ClanMemberRank.UNRANKED) + if (config.showClanRanks() && actor.isClanMember()) { - final BufferedImage clanchatImage = clanManager.getClanImage(rank); + ClanMemberRank rank = clanManager.getRank(name); - if (clanchatImage != null) + if (rank != ClanMemberRank.UNRANKED) { - final int clanImageWidth = clanchatImage.getWidth(); - final int clanImageTextMargin; - final int clanImageNegativeMargin; + BufferedImage clanchatImage = clanManager.getClanImage(rank); - if (drawPlayerNamesConfig == PlayerNameLocation.MODEL_RIGHT) + if (clanchatImage != null) { - clanImageTextMargin = clanImageWidth; - clanImageNegativeMargin = 0; - } - else - { - clanImageTextMargin = clanImageWidth / 2; - clanImageNegativeMargin = clanImageWidth / 2; - } + int width = clanchatImage.getWidth(); + int textHeight = graphics.getFontMetrics().getHeight() - graphics.getFontMetrics().getMaxDescent(); + Point imageLocation = new Point(textLocation.getX() - width / 2 - 1, textLocation.getY() - textHeight / 2 - clanchatImage.getHeight() / 2); + OverlayUtil.renderImageLocation(graphics, imageLocation, clanchatImage); - final int textHeight = graphics.getFontMetrics().getHeight() - graphics.getFontMetrics().getMaxDescent(); - final Point imageLocation = new Point(textLocation.getX() - clanImageNegativeMargin - 1, textLocation.getY() - textHeight / 2 - clanchatImage.getHeight() / 2); - OverlayUtil.renderImageLocation(graphics, imageLocation, clanchatImage); - - // move text - textLocation = new Point(textLocation.getX() + clanImageTextMargin, textLocation.getY()); + // move text + textLocation = new Point(textLocation.getX() + width / 2, textLocation.getY()); + } } } - } - OverlayUtil.renderTextLocation(graphics, textLocation, name, color); + OverlayUtil.renderTextLocation(graphics, textLocation, playerInfo, color); + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsPlugin.java index e545f92dff..fd56c88fea 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsPlugin.java @@ -27,13 +27,16 @@ package net.runelite.client.plugins.playerindicators; import com.google.inject.Provides; import java.awt.Color; import javax.inject.Inject; -import net.runelite.api.ClanMemberRank; + +import net.runelite.api.*; + import static net.runelite.api.ClanMemberRank.UNRANKED; -import net.runelite.api.Client; import static net.runelite.api.MenuAction.*; -import net.runelite.api.MenuEntry; -import net.runelite.api.Player; + +import net.runelite.api.coords.WorldPoint; import net.runelite.api.events.MenuEntryAdded; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.game.ClanManager; @@ -41,14 +44,25 @@ import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.util.ColorUtil; +import com.google.common.base.Splitter; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; + +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.InteractChanged; +import net.runelite.client.util.WildcardMatcher; + @PluginDescriptor( name = "Player Indicators", description = "Highlight players on-screen and/or on the minimap", - tags = {"highlight", "minimap", "overlay", "players"} + tags = {"highlight", "minimap", "overlay", "players"}, + type = "utility" ) public class PlayerIndicatorsPlugin extends Plugin { + private static final Splitter COMMA_SPLITTER = Splitter.on(Pattern.compile("\\s*,\\s*")); @Inject private OverlayManager overlayManager; @@ -70,6 +84,8 @@ public class PlayerIndicatorsPlugin extends Plugin @Inject private ClanManager clanManager; + private Map highlightedPlayers = new HashMap<>(); + @Provides PlayerIndicatorsConfig provideConfig(ConfigManager configManager) { @@ -82,6 +98,7 @@ public class PlayerIndicatorsPlugin extends Plugin overlayManager.add(playerIndicatorsOverlay); overlayManager.add(playerIndicatorsTileOverlay); overlayManager.add(playerIndicatorsMinimapOverlay); + updateHighlightList(); } @Override @@ -92,9 +109,60 @@ public class PlayerIndicatorsPlugin extends Plugin overlayManager.remove(playerIndicatorsMinimapOverlay); } + @Subscribe + public void onInteractChanged(InteractChanged event) + { + Actor actor = event.getActor(); + if (actor != null + && actor.getName() != null + && isHighlighted(actor)) + { + highlightedPlayers.put(actor.getName().toLowerCase(), actor.getInteracting()); + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (event.getGroup().equals("playerindicators") && event.getKey().equals("highlightedNames")) + { + updateHighlightList(); + } + } + + private void updateHighlightList() + { + highlightedPlayers.clear(); + for (String player : COMMA_SPLITTER.splitToList(config.getHighlightedNames().toLowerCase().trim())) + { + highlightedPlayers.put(player, null); + } + } + + boolean isHighlighted(Actor player) + { + for (Map.Entry map : highlightedPlayers.entrySet()) + { + if (WildcardMatcher.matches(map.getKey(), player.getName())) + { + return true; + } + } + return false; + } + + boolean isHighlightedTarget(Player player) + { + return highlightedPlayers.containsValue(player); + } + @Subscribe public void onMenuEntryAdded(MenuEntryAdded menuEntryAdded) { + if (config.showInWildernessOnly() && client.getVar(Varbits.IN_THE_WILDERNESS) != 1) + { + return; + } int type = menuEntryAdded.getType(); if (type >= 2000) @@ -154,6 +222,37 @@ public class PlayerIndicatorsPlugin extends Plugin { color = config.getNonClanMemberColor(); } + else if (config.drawHighlightedNames() && isHighlighted(player)) + { + color = config.getHighlightedNamesColor(); + } + else if (config.drawHighlightedTargetNames() && isHighlightedTarget(player)) + { + color = config.getHighlightedTargetColor(); + } + else if (config.highlightAttackerPlayers() && player.getInteracting() == localPlayer) + { + color = config.getAttackerPlayerColor(); + } + else if (config.highlightAttackablePlayers() && isWithinLevelRange(player.getCombatLevel())) + { + color = config.getAttackablePlayerColor(); + } + if (this.config.rightClickOverhead() && !player.isClanMember() && player.getOverheadIcon() != null) { // NEEDS TESTING + if (player.getOverheadIcon().equals((Object)HeadIcon.MAGIC)) { + image = 29; + } else if (player.getOverheadIcon().equals((Object)HeadIcon.RANGED)) { + image = 30; + } else if (player.getOverheadIcon().equals((Object)HeadIcon.MELEE)) { + image = 31; + } else if (player.getOverheadIcon().equals((Object)HeadIcon.REDEMPTION)) { + image = 32; + } else if (player.getOverheadIcon().equals((Object)HeadIcon.RETRIBUTION)) { + image = 33; + } else if (player.getOverheadIcon().equals((Object)HeadIcon.SMITE)) { + image = 34; + } + } if (image != -1 || color != null) { @@ -182,4 +281,57 @@ public class PlayerIndicatorsPlugin extends Plugin } } } + + public boolean isWithinLevelRange(int playerCombatLevel) + { + Widget levelRangeWidget = client.getWidget(WidgetInfo.PVP_ATTACK_RANGE); + Widget wildernessLevelWidget = client.getWidget(WidgetInfo.PVP_WILDERNESS_LEVEL); + + int localPlayerLevel = client.getLocalPlayer().getCombatLevel(); + int lowerLevelBound = localPlayerLevel - 15; + int upperLevelBound = localPlayerLevel + 15; + + if (levelRangeWidget == null && wildernessLevelWidget == null) + { + return false; + } + + if (!levelRangeWidget.isHidden() && !wildernessLevelWidget.isHidden()) + { + int wildernessLevel = calculateWildernessLevel(client.getLocalPlayer().getWorldLocation()); + lowerLevelBound = localPlayerLevel - wildernessLevel - 15; + upperLevelBound = localPlayerLevel + wildernessLevel + 15; + return (playerCombatLevel >= lowerLevelBound && playerCombatLevel <= upperLevelBound); + } + else if (levelRangeWidget.isHidden() && !wildernessLevelWidget.isHidden()) + { + int wildernessLevel = calculateWildernessLevel(client.getLocalPlayer().getWorldLocation()); + lowerLevelBound = localPlayerLevel - wildernessLevel; + upperLevelBound = localPlayerLevel + wildernessLevel; + return (playerCombatLevel >= lowerLevelBound && playerCombatLevel <= upperLevelBound); + } + else + { + return (playerCombatLevel >= lowerLevelBound && playerCombatLevel <= upperLevelBound); + } + } + + public static int calculateWildernessLevel(WorldPoint userLocation) + { + int wildernessLevel = 0; + if (WorldPoint.isInZone(new WorldPoint(2944, 3520, 0), new WorldPoint(3391, 4351, 3), userLocation)) + { + wildernessLevel = ((userLocation.getY() - (55 * 64)) / 8) + 1; + } + else if (WorldPoint.isInZone(new WorldPoint(3008, 10112, 0), new WorldPoint(3071, 10175, 3), userLocation)) + { + wildernessLevel = ((userLocation.getY() - (155 * 64)) / 8) - 1; + } + else if (WorldPoint.isInZone(new WorldPoint(2944, 9920, 0), new WorldPoint(3391, 10879, 3), userLocation)) + { + wildernessLevel = ((userLocation.getY() - (155 * 64)) / 8) + 1; + } + return wildernessLevel; + } + } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsService.java b/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsService.java index 769c7e3aed..74b1c5a58b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsService.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsService.java @@ -30,62 +30,96 @@ import javax.inject.Inject; import javax.inject.Singleton; import net.runelite.api.Client; import net.runelite.api.Player; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.Varbits; + + +import static net.runelite.client.plugins.playerindicators.PlayerIndicatorsPlugin.calculateWildernessLevel; @Singleton -public class PlayerIndicatorsService -{ +public class PlayerIndicatorsService { private final Client client; private final PlayerIndicatorsConfig config; @Inject - private PlayerIndicatorsService(Client client, PlayerIndicatorsConfig config) - { + private PlayerIndicatorsService(Client client, PlayerIndicatorsConfig config) { this.config = config; this.client = client; } - public void forEachPlayer(final BiConsumer consumer) - { - if (!config.highlightOwnPlayer() && !config.drawClanMemberNames() - && !config.highlightFriends() && !config.highlightNonClanMembers()) + public void forEachPlayer(final BiConsumer consumer) { + + if (config.showInWildernessOnly() && client.getVar(Varbits.IN_THE_WILDERNESS) != 1) { return; } + if (!config.highlightOwnPlayer() && !config.drawClanMemberNames() + && !config.highlightFriends() && !config.highlightNonClanMembers() + && !config.highlightAttackablePlayers() && !config.highlightAttackerPlayers()) { + return; + } + final Player localPlayer = client.getLocalPlayer(); - for (Player player : client.getPlayers()) - { - if (player == null || player.getName() == null) - { + for (Player player : client.getPlayers()) { + if (player == null || player.getName() == null) { continue; } boolean isClanMember = player.isClanMember(); - if (player == localPlayer) - { - if (config.highlightOwnPlayer()) - { + if (player == localPlayer) { + if (config.highlightOwnPlayer()) { consumer.accept(player, config.getOwnPlayerColor()); } - } - else if (config.highlightFriends() && player.isFriend()) - { + } else if (config.highlightFriends() && player.isFriend()) { consumer.accept(player, config.getFriendColor()); - } - else if (config.drawClanMemberNames() && isClanMember) - { + } else if (config.drawClanMemberNames() && isClanMember) { consumer.accept(player, config.getClanMemberColor()); - } - else if (config.highlightTeamMembers() && localPlayer.getTeam() > 0 && localPlayer.getTeam() == player.getTeam()) - { + } else if (config.highlightTeamMembers() && localPlayer.getTeam() > 0 && localPlayer.getTeam() == player.getTeam()) { consumer.accept(player, config.getTeamMemberColor()); - } - else if (config.highlightNonClanMembers() && !isClanMember) - { + } else if (config.highlightNonClanMembers() && !isClanMember) { consumer.accept(player, config.getNonClanMemberColor()); - } + } else if (config.highlightAttackerPlayers() && player.getInteracting() == localPlayer) { + consumer.accept(player, config.getAttackerPlayerColor()); + } else if (config.highlightAttackablePlayers() && isWithinLevelRange(player.getCombatLevel())) { + consumer.accept(player, config.getAttackablePlayerColor()); + } + } + } + + public boolean isWithinLevelRange(int playerCombatLevel) + { + Widget levelRangeWidget = client.getWidget(WidgetInfo.PVP_ATTACK_RANGE); + Widget wildernessLevelWidget = client.getWidget(WidgetInfo.PVP_WILDERNESS_LEVEL); + + int localPlayerLevel = client.getLocalPlayer().getCombatLevel(); + int lowerLevelBound = localPlayerLevel - 15; + int upperLevelBound = localPlayerLevel + 15; + + if (levelRangeWidget == null && wildernessLevelWidget == null) + { + return false; + } + + if (!levelRangeWidget.isHidden() && !wildernessLevelWidget.isHidden()) + { + lowerLevelBound = Integer.parseInt(levelRangeWidget.getText().split("-")[0]); + upperLevelBound = Integer.parseInt(levelRangeWidget.getText().split("-")[1]); + return (playerCombatLevel >= lowerLevelBound && playerCombatLevel <= upperLevelBound); + } + else if (levelRangeWidget.isHidden() && !wildernessLevelWidget.isHidden()) + { + int wildernessLevel = calculateWildernessLevel(client.getLocalPlayer().getWorldLocation()); + lowerLevelBound = localPlayerLevel - wildernessLevel; + upperLevelBound = localPlayerLevel + wildernessLevel; + return (playerCombatLevel >= lowerLevelBound && playerCombatLevel <= upperLevelBound); + } + else + { + return (playerCombatLevel >= lowerLevelBound && playerCombatLevel <= upperLevelBound); } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skybox/Skybox.java b/runelite-client/src/main/java/net/runelite/client/plugins/skybox/Skybox.java index e513166143..aa770f37f8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skybox/Skybox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skybox/Skybox.java @@ -24,6 +24,7 @@ */ package net.runelite.client.plugins.skybox; +import java.awt.Color; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.IOException; @@ -447,7 +448,7 @@ class Skybox } // Convert back to int range values, and bounds check while we are at it - byte ay = (byte) Math.min(Math.max(Math.round(Math.pow(ty / t, brightness) * 255.d), 0), 255); + byte ay = (byte) Math.min(Math.max(Math.round(ty / t * 255.d), 0), 255); byte aco = (byte) Math.min(Math.max(Math.round(tco * 128.d / t), -128), 127); byte acg = (byte) Math.min(Math.max(Math.round(tcg * 128.d / t), -128), 127); @@ -457,7 +458,11 @@ class Skybox int r = (tmp - (aco >> 1)) & 0xFF; int b = (r + aco) & 0xFF; - return r << 16 | g << 8 | b; + // increase brightness with HSB + float[] hsb = Color.RGBtoHSB(r, g, b, null); + hsb[2] = (float) Math.pow(hsb[2], brightness); + + return 0xFFFFFF & Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]); } /** diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/vanguards/VanguardOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/vanguards/VanguardOverlay.java new file mode 100644 index 0000000000..a4a74ad8a6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/vanguards/VanguardOverlay.java @@ -0,0 +1,119 @@ + +package net.runelite.client.plugins.vanguards; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; + +import net.runelite.api.Actor; +import net.runelite.api.NPC; +import net.runelite.api.Player; +import net.runelite.api.Client; +import net.runelite.client.game.NPCManager; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.LineComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; + +import net.runelite.client.plugins.opponentinfo.OpponentInfoPlugin; +//import net.runelite.client.plugins.opponentinfo.OpponentInfoOverlay; + +import javax.inject.Inject; + +public class VanguardOverlay extends Overlay { + + private VanguardPlugin plugin; + private final PanelComponent panelComponent = new PanelComponent(); + + private static final int MAGE_VANGUARD_ID = 7529; + private static final int RANGE_VANGUARD_ID = 7528; + private static final int MELEE_VANGUARD_ID = 7527; + //private final NPCManager npcManager; + + private int mageHp = -1; + private float magePercent = 0; + + private int rangeHp = -1; + private float rangePercent = 0; + + private int meleeHp = -1; + private float meleePercent = 0; + + public String right_mage_str, right_range_str, right_melee_str = ""; + + @Inject + private Client client; + + + @Inject + public VanguardOverlay(VanguardPlugin plugin) { + super(plugin);//? + this.plugin = plugin; + + setPosition(OverlayPosition.ABOVE_CHATBOX_RIGHT); + //this.opponentInfoPlugin = opponentInfoPlugin; + } + + @Override + public Dimension render(Graphics2D graphics) + { + Player p = client.getLocalPlayer(); //local player aka me + Actor opponent = p.getInteracting(); //get entity i am interacting with + //how to find its Id since it's an Actor not NPC specifically + //if(opponent.getName().equals("Vanguard") && opponent.getHealth() > 0)//might wana double check the name + //{ + if(opponent instanceof NPC) + { + int id = ((NPC) opponent).getId(); + String name = opponent.getName(); + + if(id == MAGE_VANGUARD_ID) //maybe check name.equals("Vanguard") + { + magePercent = (float)opponent.getHealthRatio() / opponent.getHealth() * 100; + mageHp = (int)magePercent; + right_mage_str = Integer.toString(mageHp); + System.out.println("mager"); + } + else if (id == RANGE_VANGUARD_ID) + { + rangePercent = (float)opponent.getHealthRatio() / opponent.getHealth() * 100; + rangeHp = (int)rangePercent; + right_range_str = Integer.toString(rangeHp); + + System.out.println("ranger"); + } + else if (id == MELEE_VANGUARD_ID) + { + meleePercent = (float)opponent.getHealthRatio()/opponent.getHealth() * 100; + meleeHp = (int)meleePercent; + right_melee_str = Integer.toString(meleeHp); + + + System.out.println("meleer"); + } + } + //} + + + //if (opponent == null) { + //} + + + panelComponent.getChildren().clear(); + String overlayTitle = "Vanguard HP"; + //title + panelComponent.getChildren().add(TitleComponent.builder().text(overlayTitle).color(Color.RED).build()); + + //size (width) + panelComponent.setPreferredSize(new Dimension(graphics.getFontMetrics().stringWidth(overlayTitle) + 30, 0)); + + panelComponent.getChildren().add(LineComponent.builder().left("Mage:").right(right_mage_str).build()); + + panelComponent.getChildren().add(LineComponent.builder().left("Range:").right(right_range_str).build()); + + panelComponent.getChildren().add(LineComponent.builder().left("Melee:").right(right_melee_str).build()); + + return panelComponent.render(graphics); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/vanguards/VanguardPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/vanguards/VanguardPlugin.java new file mode 100644 index 0000000000..88cb075867 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/vanguards/VanguardPlugin.java @@ -0,0 +1,45 @@ + +package net.runelite.client.plugins.vanguards; + +import javax.inject.Inject; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +@PluginDescriptor( + name= "Vanguard HP Overlay", + description= "tracks HP of all three vanguards", + tags= {"overlay", "vangs", "cox"}, + enabledByDefault = false, + type = "PVM" +) +public class VanguardPlugin extends Plugin { + private static final int MAGE_VANGUARD_ID = 7526; //i think + private static final int RANGE_VANGUARD_ID = 7527; + private static final int MELEE_VANGUARD_ID = 7528; + + + @Inject + private OverlayManager overlayManager; + + @Inject + private VanguardOverlay overlay; + + @Override + protected void startUp() throws Exception + { + overlayManager.add(overlay); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(overlay); + overlay.right_mage_str = "-"; + overlay.right_range_str = "-"; + overlay.right_melee_str = "-"; + } + + +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperConfig.java index f7a75781fd..a88c9510b6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperConfig.java @@ -112,4 +112,15 @@ public interface WorldHopperConfig extends Config { return SubscriptionFilterMode.BOTH; } + + @ConfigItem( + keyName = "showHistory", + name = "Show history tab", + description = "Shows the history tab", + position = 5 + ) + default boolean showHistory() + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java index a6d52f7025..88b6089f4a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java @@ -258,10 +258,49 @@ public class WorldHopperPlugin extends Plugin panel.setFilterMode(config.subscriptionFilter()); updateList(); break; + case "showHistory": + panel.updateLayout(); + break; } } } + boolean showHistory() + { + return config.showHistory(); + } + + Map getHistory() + { + Map history = configManager.getConfiguration(WorldHopperConfig.GROUP, "history", Map.class); + if (history == null) + { + history = new HashMap(); + } + + return history; + } + + void clearHistory() + { + Map history = getHistory(); + history.clear(); + configManager.setConfiguration(WorldHopperConfig.GROUP, "history", history); + } + + void addToHistory() + { + addToHistory(client.getWorld()); + } + + void addToHistory(int world) + { + long unixTime = System.currentTimeMillis() / 1000L; + Map history = getHistory(); + history.put(String.valueOf(world), String.valueOf(unixTime)); + configManager.setConfiguration(WorldHopperConfig.GROUP, "history", history); + } + private void setFavoriteConfig(int world) { configManager.setConfiguration(WorldHopperConfig.GROUP, "favorite_" + world, true); @@ -408,6 +447,12 @@ public class WorldHopperPlugin extends Plugin lastWorld = newWorld; } } + + if (gameStateChanged.getGameState() == GameState.LOGGED_IN) + { + addToHistory(client.getWorld()); + panel.updateList(); + } } @Subscribe @@ -644,6 +689,8 @@ public class WorldHopperPlugin extends Plugin quickHopTargetWorld = rsWorld; displaySwitcherAttempts = 0; + + addToHistory(worldId); } @Subscribe diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldSwitcherPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldSwitcherPanel.java index b27f356dac..fbe8ecdbaa 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldSwitcherPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldSwitcherPanel.java @@ -1,399 +1,637 @@ -/* - * Copyright (c) 2018, Psikoi - * 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.worldhopper; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.GridLayout; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import javax.swing.JPanel; -import javax.swing.SwingUtilities; -import lombok.AccessLevel; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.DynamicGridLayout; -import net.runelite.client.ui.PluginPanel; -import net.runelite.http.api.worlds.World; -import net.runelite.http.api.worlds.WorldType; - -@Slf4j -class WorldSwitcherPanel extends PluginPanel -{ - private static final Color ODD_ROW = new Color(44, 44, 44); - - private static final int WORLD_COLUMN_WIDTH = 60; - private static final int PLAYERS_COLUMN_WIDTH = 40; - private static final int PING_COLUMN_WIDTH = 47; - - private final JPanel listContainer = new JPanel(); - - private WorldTableHeader worldHeader; - private WorldTableHeader playersHeader; - private WorldTableHeader activityHeader; - private WorldTableHeader pingHeader; - - private WorldOrder orderIndex = WorldOrder.WORLD; - private boolean ascendingOrder = true; - - private ArrayList rows = new ArrayList<>(); - private WorldHopperPlugin plugin; - @Setter(AccessLevel.PACKAGE) - private SubscriptionFilterMode filterMode; - - WorldSwitcherPanel(WorldHopperPlugin plugin) - { - this.plugin = plugin; - - setBorder(null); - setLayout(new DynamicGridLayout(0, 1)); - - JPanel headerContainer = buildHeader(); - - listContainer.setLayout(new GridLayout(0, 1)); - - add(headerContainer); - add(listContainer); - } - - void switchCurrentHighlight(int newWorld, int lastWorld) - { - for (WorldTableRow row : rows) - { - if (row.getWorld().getId() == newWorld) - { - row.recolour(true); - } - else if (row.getWorld().getId() == lastWorld) - { - row.recolour(false); - } - } - } - - void updateListData(Map worldData) - { - for (WorldTableRow worldTableRow : rows) - { - World world = worldTableRow.getWorld(); - Integer playerCount = worldData.get(world.getId()); - if (playerCount != null) - { - worldTableRow.updatePlayerCount(playerCount); - } - } - - // If the list is being ordered by player count, then it has to be re-painted - // to properly display the new data - if (orderIndex == WorldOrder.PLAYERS) - { - updateList(); - } - } - - void updatePing(int world, int ping) - { - for (WorldTableRow worldTableRow : rows) - { - if (worldTableRow.getWorld().getId() == world) - { - worldTableRow.setPing(ping); - - // If the panel is sorted by ping, re-sort it - if (orderIndex == WorldOrder.PING) - { - updateList(); - } - break; - } - } - } - - void hidePing() - { - for (WorldTableRow worldTableRow : rows) - { - worldTableRow.hidePing(); - } - } - - void showPing() - { - for (WorldTableRow worldTableRow : rows) - { - worldTableRow.showPing(); - } - } - - void updateList() - { - rows.sort((r1, r2) -> - { - switch (orderIndex) - { - case PING: - return Integer.compare(r1.getPing(), r2.getPing()) * (ascendingOrder ? 1 : -1); - case WORLD: - return Integer.compare(r1.getWorld().getId(), r2.getWorld().getId()) * (ascendingOrder ? 1 : -1); - case PLAYERS: - return Integer.compare(r1.getUpdatedPlayerCount(), r2.getUpdatedPlayerCount()) * (ascendingOrder ? 1 : -1); - case ACTIVITY: - return r1.getWorld().getActivity().compareTo(r2.getWorld().getActivity()) * -1 * (ascendingOrder ? 1 : -1); - default: - return 0; - } - }); - - // Leave empty activity worlds on the bottom of the list - if (orderIndex == WorldOrder.ACTIVITY) - { - rows.sort((r1, r2) -> r1.getWorld().getActivity().equals("-") ? 1 : -1); - } - - rows.sort((r1, r2) -> - { - boolean b1 = plugin.isFavorite(r1.getWorld()); - boolean b2 = plugin.isFavorite(r2.getWorld()); - return Boolean.compare(b2, b1); - }); - - listContainer.removeAll(); - - for (int i = 0; i < rows.size(); i++) - { - WorldTableRow row = rows.get(i); - row.setBackground(i % 2 == 0 ? ODD_ROW : ColorScheme.DARK_GRAY_COLOR); - listContainer.add(row); - } - - listContainer.revalidate(); - listContainer.repaint(); - } - - void updateFavoriteMenu(int world, boolean favorite) - { - for (WorldTableRow row : rows) - { - if (row.getWorld().getId() == world) - { - row.setFavoriteMenu(favorite); - } - } - } - - void resetAllFavoriteMenus() - { - for (WorldTableRow row : rows) - { - row.setFavoriteMenu(false); - } - - } - - void populate(List worlds) - { - rows.clear(); - - for (int i = 0; i < worlds.size(); i++) - { - World world = worlds.get(i); - - switch (filterMode) - { - case FREE: - if (world.getTypes().contains(WorldType.MEMBERS)) - { - continue; - } - break; - case MEMBERS: - if (!world.getTypes().contains(WorldType.MEMBERS)) - { - continue; - } - break; - } - - rows.add(buildRow(world, i % 2 == 0, world.getId() == plugin.getCurrentWorld() && plugin.getLastWorld() != 0, plugin.isFavorite(world))); - } - - updateList(); - } - - private void orderBy(WorldOrder order) - { - pingHeader.highlight(false, ascendingOrder); - worldHeader.highlight(false, ascendingOrder); - playersHeader.highlight(false, ascendingOrder); - activityHeader.highlight(false, ascendingOrder); - - switch (order) - { - case PING: - pingHeader.highlight(true, ascendingOrder); - break; - case WORLD: - worldHeader.highlight(true, ascendingOrder); - break; - case PLAYERS: - playersHeader.highlight(true, ascendingOrder); - break; - case ACTIVITY: - activityHeader.highlight(true, ascendingOrder); - break; - } - - orderIndex = order; - updateList(); - } - - /** - * Builds the entire table header. - */ - private JPanel buildHeader() - { - JPanel header = new JPanel(new BorderLayout()); - JPanel leftSide = new JPanel(new BorderLayout()); - JPanel rightSide = new JPanel(new BorderLayout()); - - pingHeader = new WorldTableHeader("Ping", orderIndex == WorldOrder.PING, ascendingOrder, plugin::refresh); - pingHeader.setPreferredSize(new Dimension(PING_COLUMN_WIDTH, 0)); - pingHeader.addMouseListener(new MouseAdapter() - { - @Override - public void mousePressed(MouseEvent mouseEvent) - { - if (SwingUtilities.isRightMouseButton(mouseEvent)) - { - return; - } - ascendingOrder = orderIndex != WorldOrder.PING || !ascendingOrder; - orderBy(WorldOrder.PING); - } - }); - - worldHeader = new WorldTableHeader("World", orderIndex == WorldOrder.WORLD, ascendingOrder, plugin::refresh); - worldHeader.setPreferredSize(new Dimension(WORLD_COLUMN_WIDTH, 0)); - worldHeader.addMouseListener(new MouseAdapter() - { - @Override - public void mousePressed(MouseEvent mouseEvent) - { - if (SwingUtilities.isRightMouseButton(mouseEvent)) - { - return; - } - ascendingOrder = orderIndex != WorldOrder.WORLD || !ascendingOrder; - orderBy(WorldOrder.WORLD); - } - }); - - playersHeader = new WorldTableHeader("#", orderIndex == WorldOrder.PLAYERS, ascendingOrder, plugin::refresh); - playersHeader.setPreferredSize(new Dimension(PLAYERS_COLUMN_WIDTH, 0)); - playersHeader.addMouseListener(new MouseAdapter() - { - @Override - public void mousePressed(MouseEvent mouseEvent) - { - if (SwingUtilities.isRightMouseButton(mouseEvent)) - { - return; - } - ascendingOrder = orderIndex != WorldOrder.PLAYERS || !ascendingOrder; - orderBy(WorldOrder.PLAYERS); - } - }); - - activityHeader = new WorldTableHeader("Activity", orderIndex == WorldOrder.ACTIVITY, ascendingOrder, plugin::refresh); - activityHeader.addMouseListener(new MouseAdapter() - { - @Override - public void mousePressed(MouseEvent mouseEvent) - { - if (SwingUtilities.isRightMouseButton(mouseEvent)) - { - return; - } - ascendingOrder = orderIndex != WorldOrder.ACTIVITY || !ascendingOrder; - orderBy(WorldOrder.ACTIVITY); - } - }); - - leftSide.add(worldHeader, BorderLayout.WEST); - leftSide.add(playersHeader, BorderLayout.CENTER); - - rightSide.add(activityHeader, BorderLayout.CENTER); - rightSide.add(pingHeader, BorderLayout.EAST); - - header.add(leftSide, BorderLayout.WEST); - header.add(rightSide, BorderLayout.CENTER); - - return header; - } - - /** - * Builds a table row, that displays the world's information. - */ - private WorldTableRow buildRow(World world, boolean stripe, boolean current, boolean favorite) - { - WorldTableRow row = new WorldTableRow(world, current, favorite, - world1 -> - { - plugin.hopTo(world1); - }, - (world12, add) -> - { - if (add) - { - plugin.addToFavorites(world12); - } - else - { - plugin.removeFromFavorites(world12); - } - - updateList(); - } - ); - row.setBackground(stripe ? ODD_ROW : ColorScheme.DARK_GRAY_COLOR); - return row; - } - - /** - * Enumerates the multiple ordering options for the world list. - */ - private enum WorldOrder - { - WORLD, - PLAYERS, - ACTIVITY, - PING - } -} +/* + * Copyright (c) 2018, Psikoi + * 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.worldhopper; + +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridLayout; +import java.awt.GridBagLayout; +import java.awt.Color; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.HashMap; +import java.util.stream.Collectors; +import javax.swing.JPanel; +import javax.swing.JButton; +import javax.swing.JTabbedPane; +import javax.swing.border.Border; +import javax.swing.BorderFactory; +import javax.swing.SwingUtilities; +import lombok.AccessLevel; +import lombok.Setter; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.DynamicGridLayout; +import net.runelite.client.ui.PluginPanel; +import net.runelite.http.api.worlds.World; +import net.runelite.http.api.worlds.WorldType; + +@Slf4j +class WorldSwitcherPanel extends PluginPanel +{ + private static final Color ODD_ROW = new Color(44, 44, 44); + + private static final int WORLD_COLUMN_WIDTH = 60; + private static final int PLAYERS_COLUMN_WIDTH = 40; + private static final int PING_COLUMN_WIDTH = 47; + + private final JPanel headerContainer; + private final JPanel headerHistContainer; + private final JPanel listContainer = new JPanel(); + private final JPanel histContainer = new JPanel(); + + private WorldTableHeader worldHeader; + private WorldTableHeader playersHeader; + private WorldTableHeader activityHeader; + private WorldTableHeader pingHeader; + + private WorldOrder orderIndex = WorldOrder.WORLD; + private boolean ascendingOrder = true; + + private ArrayList rows = new ArrayList<>(); + private ArrayList histRows = new ArrayList<>(); + private WorldHopperPlugin plugin; + @Setter(AccessLevel.PACKAGE) + private SubscriptionFilterMode filterMode; + + WorldSwitcherPanel(WorldHopperPlugin plugin) + { + this.plugin = plugin; + + setBorder(null); + setLayout(new DynamicGridLayout(0, 1)); + + headerContainer = buildHeader(); + headerHistContainer = buildHistoryHeader(); + + listContainer.setLayout(new GridLayout(0, 1)); + histContainer.setLayout(new GridLayout(0, 1)); + + updateLayout(); + } + + void updateLayout() + { + if (this.getComponentCount() > 0) + { + for (Component c : this.getComponents()) + { + this.remove(c); + } + } + + if (plugin.showHistory()) + { + Component tabs = createTabs(); + add(tabs); + } + else + { + add(headerContainer); + add(listContainer); + } + } + + void switchCurrentHighlight(int newWorld, int lastWorld) + { + for (WorldTableRow row : rows) + { + if (row.getWorld().getId() == newWorld) + { + row.recolour(true); + } + else if (row.getWorld().getId() == lastWorld) + { + row.recolour(false); + } + } + + for (WorldTableRow row : histRows) + { + if (row.getWorld().getId() == newWorld) + { + row.recolour(true); + } + else if (row.getWorld().getId() == lastWorld) + { + row.recolour(false); + } + } + } + + void updateListData(Map worldData) + { + for (WorldTableRow worldTableRow : rows) + { + World world = worldTableRow.getWorld(); + Integer playerCount = worldData.get(world.getId()); + if (playerCount != null) + { + worldTableRow.updatePlayerCount(playerCount); + } + } + + for (WorldTableRow worldTableRow : histRows) + { + World world = worldTableRow.getWorld(); + Integer playerCount = worldData.get(world.getId()); + if (playerCount != null) + { + worldTableRow.updatePlayerCount(playerCount); + } + } + + // If the list is being ordered by player count, then it has to be re-painted + // to properly display the new data + if (orderIndex == WorldOrder.PLAYERS) + { + updateList(); + } + } + + void updatePing(int world, int ping) + { + for (WorldTableRow worldTableRow : rows) + { + if (worldTableRow.getWorld().getId() == world) + { + worldTableRow.setPing(ping); + + // If the panel is sorted by ping, re-sort it + if (orderIndex == WorldOrder.PING) + { + updateList(); + } + break; + } + } + + for (WorldTableRow worldTableRow : histRows) + { + if (worldTableRow.getWorld().getId() == world) + { + worldTableRow.setPing(ping); + + // If the panel is sorted by ping, re-sort it + if (orderIndex == WorldOrder.PING) + { + updateList(); + } + break; + } + } + } + + void hidePing() + { + for (WorldTableRow worldTableRow : rows) + { + worldTableRow.hidePing(); + } + + for (WorldTableRow worldTableRow : histRows) + { + worldTableRow.hidePing(); + } + } + + void showPing() + { + for (WorldTableRow worldTableRow : rows) + { + worldTableRow.showPing(); + } + + for (WorldTableRow worldTableRow : histRows) + { + worldTableRow.showPing(); + } + } + + void updateList() + { + rows.sort((r1, r2) -> + { + switch (orderIndex) + { + case PING: + return Integer.compare(r1.getPing(), r2.getPing()) * (ascendingOrder ? 1 : -1); + case WORLD: + return Integer.compare(r1.getWorld().getId(), r2.getWorld().getId()) * (ascendingOrder ? 1 : -1); + case PLAYERS: + return Integer.compare(r1.getUpdatedPlayerCount(), r2.getUpdatedPlayerCount()) * (ascendingOrder ? 1 : -1); + case ACTIVITY: + return r1.getWorld().getActivity().compareTo(r2.getWorld().getActivity()) * -1 * (ascendingOrder ? 1 : -1); + default: + return 0; + } + }); + + // Leave empty activity worlds on the bottom of the list + if (orderIndex == WorldOrder.ACTIVITY) + { + rows.sort((r1, r2) -> r1.getWorld().getActivity().equals("-") ? 1 : -1); + } + + rows.sort((r1, r2) -> + { + boolean b1 = plugin.isFavorite(r1.getWorld()); + boolean b2 = plugin.isFavorite(r2.getWorld()); + return Boolean.compare(b2, b1); + }); + + listContainer.removeAll(); + histContainer.removeAll(); + + Map history = plugin.getHistory(); + Map matchedHist = new HashMap<>(); + for (int i = 0; i < rows.size(); i++) + { + WorldTableRow row = rows.get(i); + row.setBackground(i % 2 == 0 ? ODD_ROW : ColorScheme.DARK_GRAY_COLOR); + listContainer.add(row); + + String worldNum = String.valueOf(row.getWorld().getId()); + if (history.containsKey(worldNum)) + { + // Add toa list that we can sort later + matchedHist.put(worldNum, history.get(worldNum)); + } + } + + // Sort by ascending + matchedHist = matchedHist.entrySet().stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, + (e1, e2) -> e1, LinkedHashMap::new)); + + // Add matched rows to history list + Iterator it = matchedHist.entrySet().iterator(); + int histRowCount = 0; + while (it.hasNext()) + { + Map.Entry pair = (Map.Entry)it.next(); + for (WorldTableRow r : rows) + { + WorldTableRow histRow = r; + histRow.setBackground(histRowCount % 2 == 0 ? ODD_ROW : ColorScheme.DARK_GRAY_COLOR); + if (String.valueOf(r.getWorld().getId()).equals(pair.getKey())) + { + histContainer.add(r); + histRowCount++; + break; + } + } + it.remove(); + } + + listContainer.revalidate(); + listContainer.repaint(); + histContainer.revalidate(); + histContainer.repaint(); + } + + Component createTabs() + { + // Constraints for GB Layout + GridBagConstraints listConst = new GridBagConstraints(); + listConst.gridx = 0; + listConst.gridy = 1; + listConst.fill = GridBagConstraints.HORIZONTAL; + GridBagConstraints headConst = new GridBagConstraints(); + headConst.gridx = 0; + headConst.gridy = 0; + headConst.fill = GridBagConstraints.HORIZONTAL; + GridBagConstraints resetConst = new GridBagConstraints(); + resetConst.gridx = 0; + resetConst.gridy = 2; + resetConst.fill = GridBagConstraints.HORIZONTAL; + + // Border so that the scrollbar doesn't go over ping + Border paddingBorder = BorderFactory.createEmptyBorder(0, 0, 0, 5); + + // Clear history button + JButton resetBtn = new JButton("Clear History"); + resetBtn.addActionListener(e -> + { + plugin.clearHistory(); + plugin.addToHistory(); + updateList(); + }); + + // World Selector page + JPanel worldPanel = new JPanel(); + worldPanel.setBorder(paddingBorder); + worldPanel.setLayout(new GridBagLayout()); + worldPanel.add(headerContainer, headConst); + worldPanel.add(listContainer, listConst); + + // History page + JPanel histPanel = new JPanel(); + histPanel.setBorder(paddingBorder); + histPanel.setLayout(new GridBagLayout()); + histPanel.add(headerHistContainer, headConst); + histPanel.add(histContainer, listConst); + histPanel.add(resetBtn, resetConst); + + JTabbedPane worldTabs = new JTabbedPane(); + worldTabs.setName("tabs"); + worldTabs.addTab("Worlds", worldPanel); + worldTabs.addTab("History", histPanel); + + // This is a fix for preventing stretching of WorldTableRows + worldTabs.addChangeListener(e -> + { + switch (worldTabs.getSelectedIndex()) + { + case 0: + histPanel.remove(histContainer); + if (worldPanel.getComponentCount() < 2) + { + worldPanel.add(listContainer, listConst); + } + break; + case 1: + worldPanel.remove(listContainer); + if (histPanel.getComponentCount() < 3) + { + histPanel.add(histContainer, listConst); + } + break; + } + }); + + return worldTabs; + } + + void updateFavoriteMenu(int world, boolean favorite) + { + for (WorldTableRow row : rows) + { + if (row.getWorld().getId() == world) + { + row.setFavoriteMenu(favorite); + } + } + + for (WorldTableRow row : histRows) + { + if (row.getWorld().getId() == world) + { + row.setFavoriteMenu(favorite); + } + } + } + + void resetAllFavoriteMenus() + { + for (WorldTableRow row : rows) + { + row.setFavoriteMenu(false); + } + + for (WorldTableRow row : histRows) + { + row.setFavoriteMenu(false); + } + } + + void populate(List worlds) + { + rows.clear(); + + for (int i = 0; i < worlds.size(); i++) + { + World world = worlds.get(i); + + switch (filterMode) + { + case FREE: + if (world.getTypes().contains(WorldType.MEMBERS)) + { + continue; + } + break; + case MEMBERS: + if (!world.getTypes().contains(WorldType.MEMBERS)) + { + continue; + } + break; + } + + rows.add(buildRow(world, i % 2 == 0, world.getId() == plugin.getCurrentWorld() && plugin.getLastWorld() != 0, plugin.isFavorite(world))); + } + + updateList(); + } + + private void orderBy(WorldOrder order) + { + pingHeader.highlight(false, ascendingOrder); + worldHeader.highlight(false, ascendingOrder); + playersHeader.highlight(false, ascendingOrder); + activityHeader.highlight(false, ascendingOrder); + + switch (order) + { + case PING: + pingHeader.highlight(true, ascendingOrder); + break; + case WORLD: + worldHeader.highlight(true, ascendingOrder); + break; + case PLAYERS: + playersHeader.highlight(true, ascendingOrder); + break; + case ACTIVITY: + activityHeader.highlight(true, ascendingOrder); + break; + } + + orderIndex = order; + updateList(); + } + + /** + * Builds the entire table header. + */ + private JPanel buildHistoryHeader() + { + JPanel header = new JPanel(new BorderLayout()); + JPanel leftSide = new JPanel(new BorderLayout()); + JPanel rightSide = new JPanel(new BorderLayout()); + + WorldTableHeader pingHeader = new WorldTableHeader("Ping", false, ascendingOrder, plugin::refresh); + pingHeader.setPreferredSize(new Dimension(PING_COLUMN_WIDTH, 0)); + + WorldTableHeader worldHeader = new WorldTableHeader("World", false, ascendingOrder, plugin::refresh); + worldHeader.setPreferredSize(new Dimension(WORLD_COLUMN_WIDTH, 0)); + + WorldTableHeader playersHeader = new WorldTableHeader("#", false, ascendingOrder, plugin::refresh); + playersHeader.setPreferredSize(new Dimension(PLAYERS_COLUMN_WIDTH, 0)); + + WorldTableHeader activityHeader = new WorldTableHeader("Activity", false, ascendingOrder, plugin::refresh); + + leftSide.add(worldHeader, BorderLayout.WEST); + leftSide.add(playersHeader, BorderLayout.CENTER); + + rightSide.add(activityHeader, BorderLayout.CENTER); + rightSide.add(pingHeader, BorderLayout.EAST); + + header.add(leftSide, BorderLayout.WEST); + header.add(rightSide, BorderLayout.CENTER); + + return header; + } + + private JPanel buildHeader() + { + JPanel header = new JPanel(new BorderLayout()); + JPanel leftSide = new JPanel(new BorderLayout()); + JPanel rightSide = new JPanel(new BorderLayout()); + + pingHeader = new WorldTableHeader("Ping", orderIndex == WorldOrder.PING, ascendingOrder, plugin::refresh); + pingHeader.setPreferredSize(new Dimension(PING_COLUMN_WIDTH, 0)); + pingHeader.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + if (SwingUtilities.isRightMouseButton(mouseEvent)) + { + return; + } + ascendingOrder = orderIndex != WorldOrder.PING || !ascendingOrder; + orderBy(WorldOrder.PING); + } + }); + + worldHeader = new WorldTableHeader("World", orderIndex == WorldOrder.WORLD, ascendingOrder, plugin::refresh); + worldHeader.setPreferredSize(new Dimension(WORLD_COLUMN_WIDTH, 0)); + worldHeader.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + if (SwingUtilities.isRightMouseButton(mouseEvent)) + { + return; + } + ascendingOrder = orderIndex != WorldOrder.WORLD || !ascendingOrder; + orderBy(WorldOrder.WORLD); + } + }); + + playersHeader = new WorldTableHeader("#", orderIndex == WorldOrder.PLAYERS, ascendingOrder, plugin::refresh); + playersHeader.setPreferredSize(new Dimension(PLAYERS_COLUMN_WIDTH, 0)); + playersHeader.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + if (SwingUtilities.isRightMouseButton(mouseEvent)) + { + return; + } + ascendingOrder = orderIndex != WorldOrder.PLAYERS || !ascendingOrder; + orderBy(WorldOrder.PLAYERS); + } + }); + + activityHeader = new WorldTableHeader("Activity", orderIndex == WorldOrder.ACTIVITY, ascendingOrder, plugin::refresh); + activityHeader.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + if (SwingUtilities.isRightMouseButton(mouseEvent)) + { + return; + } + ascendingOrder = orderIndex != WorldOrder.ACTIVITY || !ascendingOrder; + orderBy(WorldOrder.ACTIVITY); + } + }); + + leftSide.add(worldHeader, BorderLayout.WEST); + leftSide.add(playersHeader, BorderLayout.CENTER); + + rightSide.add(activityHeader, BorderLayout.CENTER); + rightSide.add(pingHeader, BorderLayout.EAST); + + header.add(leftSide, BorderLayout.WEST); + header.add(rightSide, BorderLayout.CENTER); + + return header; + } + + /** + * Builds a table row, that displays the world's information. + */ + private WorldTableRow buildRow(World world, boolean stripe, boolean current, boolean favorite) + { + WorldTableRow row = new WorldTableRow(world, current, favorite, + world1 -> + { + plugin.hopTo(world1); + }, + (world12, add) -> + { + if (add) + { + plugin.addToFavorites(world12); + } + else + { + plugin.removeFromFavorites(world12); + } + + updateList(); + } + ); + row.setBackground(stripe ? ODD_ROW : ColorScheme.DARK_GRAY_COLOR); + return row; + } + + /** + * Enumerates the multiple ordering options for the world list. + */ + private enum WorldOrder + { + WORLD, + PLAYERS, + ACTIVITY, + PING + } +} 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..2b62fa4d88 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 @@ -31,12 +31,7 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.image.BufferedImage; import javax.annotation.Nonnull; -import javax.swing.BorderFactory; -import javax.swing.ImageIcon; -import javax.swing.JLabel; -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; +import javax.swing.*; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import net.runelite.client.ui.ColorScheme; @@ -121,7 +116,7 @@ class WorldTableHeader extends JPanel add(textLabel, BorderLayout.WEST); add(arrowLabel, BorderLayout.EAST); - } +} /** * The labels inherit the parent's mouse listeners. diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java index c23face9ba..a497da60e7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java @@ -96,6 +96,7 @@ class WorldTableRow extends JPanel this.world = world; this.onFavorite = onFavorite; this.updatedPlayerCount = world.getPlayers(); + this. setLayout(new BorderLayout()); setBorder(new EmptyBorder(2, 0, 2, 0)); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartLocation.java index afd2bba6a4..77157bb490 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/QuestStartLocation.java @@ -46,10 +46,8 @@ enum QuestStartLocation THE_RESTLESS_GHOST("The Restless Ghost", new WorldPoint(3240, 3210, 0)), RUNE_MYSTERIES("Rune Mysteries", new WorldPoint(3210, 3220, 0)), SHEEP_SHEARER("Sheep Shearer", new WorldPoint(3190, 3272, 0)), - SHIELD_OF_ARRAV_PHOENIX_GANG("Shield of Arrav (Phoenix Gang)", new WorldPoint(3208, 3495, 0)), SHIELD_OF_ARRAV_BLACK_ARM_GANG("Shield of Arrav (Black Arm Gang)", new WorldPoint(3208, 3392, 0)), - VAMPIRE_SLAYER("Vampire Slayer", new WorldPoint(3096, 3266, 0)), WITCHS_POTION("Witch's Potion", new WorldPoint(2967, 3203, 0)), X_MARKS_THE_SPOT("X Marks the Spot", new WorldPoint(3227, 3242, 0)), diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java index 7c2d19323f..714b1dcd17 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java @@ -135,9 +135,9 @@ public abstract class RSActorMixin implements RSActor public WorldPoint getWorldLocation() { return WorldPoint.fromLocal(client, - this.getPathX()[0] * Perspective.LOCAL_TILE_SIZE + Perspective.LOCAL_TILE_SIZE / 2, - this.getPathY()[0] * Perspective.LOCAL_TILE_SIZE + Perspective.LOCAL_TILE_SIZE / 2, - client.getPlane()); + this.getPathX()[0] * Perspective.LOCAL_TILE_SIZE + Perspective.LOCAL_TILE_SIZE / 2, + this.getPathY()[0] * Perspective.LOCAL_TILE_SIZE + Perspective.LOCAL_TILE_SIZE / 2, + client.getPlane()); } @Inject