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